ant-design/components/calendar/generateCalendar.tsx
二货机器人 407a41a142
feat: New Picker (#20023)
* init generate

* basic style

* basic panel style

* update mode panel style

* update style

* generate More picker

* default clear icon

* chore: Update separator type

* feat: Add ranged start & end className

* update range style

* Add transition effect

* support size config

* adjust range style

* chore: Auto fill time by showTime

* auto set time by format

* update disabled style

* update seperator style

* ranges style

* support extra footer style

* remove useless test case
part is not usable anymore
part is already tested in rc-picker

* init calendar

* all demos

* fix calendar basic test

* fix time-picker test case

* update snapshot

* fix tooltip test case & lint

* fix locale & style lint

* fix compile

* fix style

* fix style lint

* fix calendar style

* update rc-picker version

* adjust style

* move picker placeholder into locale file

* update snapshot

* add hover style

* update picker version

* fix icon position & style

* update picker version

* update deps for pading

* fix: align of suffix

* feat: Year & Month support range effect

* adjust range style to support up-down placement

* update rc-picker

* update range picker style

* adjust extra footer line style

* update snapshot

* fix: Locale error

* fix: style lint

* fix: add missing button style deps

* update test case

* fix firefox additional white line style issue

* rollback demo

* fix ff additional blue color

* docs: Remove placeholder in demo

* rangepicker ranges is tag now

* connect start / end background color with picker range

* update deps

* update deps for fixing blur text issue

* hide start-end demo

* range hover style update

* hover range with ranged value

* black magic of inner hover style

* hover style of range adjust

* fix css select miss hit on DatePicker

* remove one eslint rule

* fade range hovered color

* week should alway not show the cell selection

* update style of selection

* update snapshot

* fix style

* add margin back

* update rc-picker deps

* update date & time picker & form style

* fix disabled demo & update form style

* update docs about allowEmpty

* hide arrow in time range picker

* add hover & focused style

* fix lint

* fix style & update snapshot

* raise disabled selector proirity

* fix disabled today border color

* extra footer provides an bottom line

* time picker hover support transition background

* add padding style

* fix Firefox not correct calculate inline-flex

* fix style

* fix week picker missing today border color

* rm useless padding

* Force padding to 0

* test coverage

* dedup eslint rule

* adjust logic to imporve coverage

* fix render cell logic
2019-12-11 23:32:19 +08:00

288 lines
8.8 KiB
TypeScript

import * as React from 'react';
import classNames from 'classnames';
import padStart from 'lodash/padStart';
import { PickerPanel as RCPickerPanel } from 'rc-picker';
import { Locale } from 'rc-picker/lib/interface';
import { GenerateConfig } from 'rc-picker/lib/generate';
import {
PickerPanelBaseProps as RCPickerPanelBaseProps,
PickerPanelDateProps as RCPickerPanelDateProps,
PickerPanelTimeProps as RCPickerPanelTimeProps,
} from 'rc-picker/lib/PickerPanel';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import enUS from './locale/en_US';
import { ConfigContext } from '../config-provider';
import CalendarHeader from './Header';
type InjectDefaultProps<Props> = Omit<
Props,
'locale' | 'generateConfig' | 'prevIcon' | 'nextIcon' | 'superPrevIcon' | 'superNextIcon'
> & {
locale?: typeof enUS;
size?: 'large' | 'default' | 'small';
};
// Picker Props
export type PickerPanelBaseProps<DateType> = InjectDefaultProps<RCPickerPanelBaseProps<DateType>>;
export type PickerPanelDateProps<DateType> = InjectDefaultProps<RCPickerPanelDateProps<DateType>>;
export type PickerPanelTimeProps<DateType> = InjectDefaultProps<RCPickerPanelTimeProps<DateType>>;
export type PickerProps<DateType> =
| PickerPanelBaseProps<DateType>
| PickerPanelDateProps<DateType>
| PickerPanelTimeProps<DateType>;
export type CalendarMode = 'year' | 'month';
export type HeaderRender<DateType> = (config: {
value: DateType;
type: CalendarMode;
onChange: (date: DateType) => void;
onTypeChange: (type: CalendarMode) => void;
}) => React.ReactNode;
export interface CalendarProps<DateType> {
prefixCls?: string;
className?: string;
style?: React.CSSProperties;
locale?: typeof enUS;
validRange?: [DateType, DateType];
disabledDate?: (date: DateType) => boolean;
dateFullCellRender?: (date: DateType) => React.ReactNode;
dateCellRender?: (date: DateType) => React.ReactNode;
monthFullCellRender?: (date: DateType) => React.ReactNode;
monthCellRender?: (date: DateType) => React.ReactNode;
headerRender?: HeaderRender<DateType>;
value?: DateType;
defaultValue?: DateType;
mode?: CalendarMode;
fullscreen?: boolean;
onChange?: (date: DateType) => void;
onPanelChange?: (date: DateType, mode: CalendarMode) => void;
onSelect?: (date: DateType) => void;
}
function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
function isSameMonth(date1: DateType, date2: DateType) {
return (
date1 === date2 ||
(date1 &&
date2 &&
generateConfig.getYear(date1) === generateConfig.getYear(date2) &&
generateConfig.getMonth(date1) === generateConfig.getMonth(date2))
);
}
function isSameDate(date1: DateType, date2: DateType) {
return (
isSameMonth(date1, date2) && generateConfig.getDate(date1) === generateConfig.getDate(date2)
);
}
const Calendar = (props: CalendarProps<DateType>) => {
const {
prefixCls: customizePrefixCls,
className,
dateFullCellRender,
dateCellRender,
monthFullCellRender,
monthCellRender,
headerRender,
value,
defaultValue,
disabledDate,
mode,
validRange,
fullscreen = true,
onChange,
onPanelChange,
onSelect,
} = props;
const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('picker', customizePrefixCls);
const calendarPrefixCls = `${prefixCls}-calendar`;
const today = generateConfig.getNow();
// ====================== State =======================
// Value
const [innerValue, setInnerValue] = React.useState(
() => value || defaultValue || generateConfig.getNow(),
);
const mergedValue = value || innerValue;
// Mode
const [innerMode, setInnerMode] = React.useState(() => mode || 'month');
const mergedMode = mode || innerMode;
const panelMode = React.useMemo<'month' | 'date'>(
() => (mergedMode === 'year' ? 'month' : 'date'),
[mergedMode],
);
// Disabled Date
const mergedDisabledDate = React.useMemo(() => {
if (validRange) {
return (date: DateType) => {
return (
generateConfig.isAfter(validRange[0], date) ||
generateConfig.isAfter(date, validRange[1])
);
};
}
return disabledDate;
}, [disabledDate, validRange]);
// ====================== Events ======================
const triggerPanelChange = (date: DateType, newMode: CalendarMode) => {
if (onPanelChange) {
onPanelChange(date, newMode);
}
};
const triggerChange = (date: DateType) => {
setInnerValue(date);
if (!isSameDate(date, mergedValue)) {
triggerPanelChange(date, mergedMode);
if (onChange) {
onChange(date);
}
}
};
const triggerModeChange = (newMode: CalendarMode) => {
setInnerMode(newMode);
triggerPanelChange(mergedValue, newMode);
};
const onInternalSelect = (date: DateType) => {
triggerChange(date);
if (onSelect) {
onSelect(date);
}
};
// ====================== Locale ======================
const getDefaultLocale = () => {
const { locale } = props;
const result = {
...enUS,
...locale,
};
result.lang = {
...result.lang,
...((locale || {}) as any).lang,
};
return result;
};
// ====================== Render ======================
const dateRender = React.useCallback(
(date: DateType): React.ReactNode => {
if (dateFullCellRender) {
return dateFullCellRender(date);
}
return (
<div
className={classNames(`${prefixCls}-cell-inner`, `${calendarPrefixCls}-date`, {
[`${calendarPrefixCls}-date-today`]: isSameDate(today, date),
})}
>
<div className={`${calendarPrefixCls}-date-value`}>
{padStart(String(generateConfig.getDate(date)), 2, '0')}
</div>
<div className={`${calendarPrefixCls}-date-content`}>
{dateCellRender && dateCellRender(date)}
</div>
</div>
);
},
[dateFullCellRender, dateCellRender],
);
const monthRender = React.useCallback(
(date: DateType, locale: Locale): React.ReactNode => {
if (monthFullCellRender) {
return monthFullCellRender(date);
}
const months = locale.shortMonths || generateConfig.locale.getShortMonths!(locale.locale);
return (
<div
className={classNames(`${prefixCls}-cell-inner`, `${calendarPrefixCls}-date`, {
[`${calendarPrefixCls}-date-today`]: isSameMonth(today, date),
})}
>
<div className={`${calendarPrefixCls}-date-value`}>
{months[generateConfig.getMonth(date)]}
</div>
<div className={`${calendarPrefixCls}-date-content`}>
{monthCellRender && monthCellRender(date)}
</div>
</div>
);
},
[monthFullCellRender, monthCellRender],
);
return (
<LocaleReceiver componentName="Calendar" defaultLocale={getDefaultLocale}>
{(mergedLocale: any) => {
return (
<div
className={classNames(calendarPrefixCls, className, {
[`${calendarPrefixCls}-full`]: fullscreen,
[`${calendarPrefixCls}-mini`]: !fullscreen,
})}
>
{headerRender ? (
headerRender({
value: mergedValue,
type: mergedMode,
onChange: onInternalSelect,
onTypeChange: triggerModeChange,
})
) : (
<CalendarHeader
prefixCls={calendarPrefixCls}
value={mergedValue}
generateConfig={generateConfig}
mode={mergedMode}
fullscreen={fullscreen}
locale={mergedLocale.lang}
validRange={validRange}
onChange={onInternalSelect}
onModeChange={triggerModeChange}
/>
)}
<RCPickerPanel
value={mergedValue}
prefixCls={prefixCls}
locale={mergedLocale.lang}
generateConfig={generateConfig}
dateRender={dateRender}
monthCellRender={date => monthRender(date, mergedLocale.lang)}
onSelect={onInternalSelect}
mode={panelMode}
picker={panelMode as any}
disabledDate={mergedDisabledDate}
hideHeader
/>
</div>
);
}}
</LocaleReceiver>
);
};
return Calendar;
}
export default generateCalendar;