You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
5.9 KiB
206 lines
5.9 KiB
import Select from '../select';
|
|
import { Group, Button } from '../radio';
|
|
import type { CalendarMode, SelectInfo } from './generateCalendar';
|
|
import type { Ref } from 'vue';
|
|
import { defineComponent, ref } from 'vue';
|
|
import type { Locale } from '../vc-picker/interface';
|
|
import type { GenerateConfig } from '../vc-picker/generate';
|
|
import { FormItemInputContext } from '../form/FormItemContext';
|
|
|
|
const YearSelectOffset = 10;
|
|
const YearSelectTotal = 20;
|
|
|
|
interface SharedProps<DateType> {
|
|
prefixCls: string;
|
|
value: DateType;
|
|
validRange?: [DateType, DateType];
|
|
generateConfig: GenerateConfig<DateType>;
|
|
locale: Locale;
|
|
fullscreen: boolean;
|
|
divRef: Ref<HTMLDivElement>;
|
|
onChange: (year: DateType) => void;
|
|
}
|
|
|
|
function YearSelect<DateType>(props: SharedProps<DateType>) {
|
|
const { fullscreen, validRange, generateConfig, locale, prefixCls, value, onChange, divRef } =
|
|
props;
|
|
|
|
const year = generateConfig.getYear(value || generateConfig.getNow());
|
|
|
|
let start = year - YearSelectOffset;
|
|
let end = start + YearSelectTotal;
|
|
|
|
if (validRange) {
|
|
start = generateConfig.getYear(validRange[0]);
|
|
end = generateConfig.getYear(validRange[1]) + 1;
|
|
}
|
|
|
|
const suffix = locale && locale.year === '年' ? '年' : '';
|
|
const options: { label: string; value: number }[] = [];
|
|
for (let index = start; index < end; index++) {
|
|
options.push({ label: `${index}${suffix}`, value: index });
|
|
}
|
|
return (
|
|
<Select
|
|
size={fullscreen ? undefined : 'small'}
|
|
options={options}
|
|
value={year}
|
|
class={`${prefixCls}-year-select`}
|
|
onChange={(numYear: number) => {
|
|
let newDate = generateConfig.setYear(value, numYear);
|
|
|
|
if (validRange) {
|
|
const [startDate, endDate] = validRange;
|
|
const newYear = generateConfig.getYear(newDate);
|
|
const newMonth = generateConfig.getMonth(newDate);
|
|
if (
|
|
newYear === generateConfig.getYear(endDate) &&
|
|
newMonth > generateConfig.getMonth(endDate)
|
|
) {
|
|
newDate = generateConfig.setMonth(newDate, generateConfig.getMonth(endDate));
|
|
}
|
|
if (
|
|
newYear === generateConfig.getYear(startDate) &&
|
|
newMonth < generateConfig.getMonth(startDate)
|
|
) {
|
|
newDate = generateConfig.setMonth(newDate, generateConfig.getMonth(startDate));
|
|
}
|
|
}
|
|
|
|
onChange(newDate);
|
|
}}
|
|
getPopupContainer={() => divRef!.value!}
|
|
/>
|
|
);
|
|
}
|
|
YearSelect.inheritAttrs = false;
|
|
|
|
function MonthSelect<DateType>(props: SharedProps<DateType>) {
|
|
const { prefixCls, fullscreen, validRange, value, generateConfig, locale, onChange, divRef } =
|
|
props;
|
|
const month = generateConfig.getMonth(value || generateConfig.getNow());
|
|
|
|
let start = 0;
|
|
let end = 11;
|
|
|
|
if (validRange) {
|
|
const [rangeStart, rangeEnd] = validRange;
|
|
const currentYear = generateConfig.getYear(value);
|
|
if (generateConfig.getYear(rangeEnd) === currentYear) {
|
|
end = generateConfig.getMonth(rangeEnd);
|
|
}
|
|
if (generateConfig.getYear(rangeStart) === currentYear) {
|
|
start = generateConfig.getMonth(rangeStart);
|
|
}
|
|
}
|
|
|
|
const months = locale.shortMonths || generateConfig.locale.getShortMonths!(locale.locale);
|
|
const options: { label: string; value: number }[] = [];
|
|
for (let index = start; index <= end; index += 1) {
|
|
options.push({
|
|
label: months[index],
|
|
value: index,
|
|
});
|
|
}
|
|
|
|
return (
|
|
<Select
|
|
size={fullscreen ? undefined : 'small'}
|
|
class={`${prefixCls}-month-select`}
|
|
value={month}
|
|
options={options}
|
|
onChange={(newMonth: number) => {
|
|
onChange(generateConfig.setMonth(value, newMonth));
|
|
}}
|
|
getPopupContainer={() => divRef!.value!}
|
|
/>
|
|
);
|
|
}
|
|
|
|
MonthSelect.inheritAttrs = false;
|
|
|
|
interface ModeSwitchProps<DateType> extends Omit<SharedProps<DateType>, 'onChange'> {
|
|
mode: CalendarMode;
|
|
onModeChange: (type: CalendarMode) => void;
|
|
}
|
|
|
|
function ModeSwitch<DateType>(props: ModeSwitchProps<DateType>) {
|
|
const { prefixCls, locale, mode, fullscreen, onModeChange } = props;
|
|
return (
|
|
<Group
|
|
onChange={({ target: { value } }) => {
|
|
onModeChange(value);
|
|
}}
|
|
value={mode}
|
|
size={fullscreen ? undefined : 'small'}
|
|
class={`${prefixCls}-mode-switch`}
|
|
>
|
|
<Button value="month">{locale.month}</Button>
|
|
<Button value="year">{locale.year}</Button>
|
|
</Group>
|
|
);
|
|
}
|
|
ModeSwitch.inheritAttrs = false;
|
|
|
|
export interface CalendarHeaderProps<DateType> {
|
|
prefixCls: string;
|
|
value: DateType;
|
|
validRange?: [DateType, DateType];
|
|
generateConfig: GenerateConfig<DateType>;
|
|
locale: Locale;
|
|
mode: CalendarMode;
|
|
fullscreen: boolean;
|
|
onChange: (date: DateType, source: SelectInfo['source']) => void;
|
|
onModeChange: (mode: CalendarMode) => void;
|
|
}
|
|
|
|
export default defineComponent<CalendarHeaderProps<any>>({
|
|
name: 'CalendarHeader',
|
|
inheritAttrs: false,
|
|
props: [
|
|
'mode',
|
|
'prefixCls',
|
|
'value',
|
|
'validRange',
|
|
'generateConfig',
|
|
'locale',
|
|
'mode',
|
|
'fullscreen',
|
|
] as any,
|
|
setup(_props, { attrs }) {
|
|
const divRef = ref<HTMLDivElement>(null);
|
|
const formItemInputContext = FormItemInputContext.useInject();
|
|
FormItemInputContext.useProvide(formItemInputContext, { isFormItemInput: false });
|
|
|
|
return () => {
|
|
const props = { ..._props, ...attrs };
|
|
const { prefixCls, fullscreen, mode, onChange, onModeChange } = props;
|
|
const sharedProps = {
|
|
...props,
|
|
fullscreen,
|
|
divRef,
|
|
} as any;
|
|
|
|
return (
|
|
<div class={`${prefixCls}-header`} ref={divRef}>
|
|
<YearSelect
|
|
{...sharedProps}
|
|
onChange={v => {
|
|
onChange(v, 'year');
|
|
}}
|
|
/>
|
|
{mode === 'month' && (
|
|
<MonthSelect
|
|
{...sharedProps}
|
|
onChange={v => {
|
|
onChange(v, 'month');
|
|
}}
|
|
/>
|
|
)}
|
|
<ModeSwitch {...sharedProps} onModeChange={onModeChange} />
|
|
</div>
|
|
);
|
|
};
|
|
},
|
|
});
|