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.

213 lines
8.2 KiB

import * as React from 'react';
import classNames from 'classnames';
import CalendarOutlined from '@ant-design/icons/CalendarOutlined';
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import RCPicker from 'rc-picker';
import type { PickerMode } from 'rc-picker/lib/interface';
import type { GenerateConfig } from 'rc-picker/lib/generate/index';
import { forwardRef, useContext } from 'react';
import enUS from '../locale/en_US';
import { getPlaceholder, transPlacement2DropdownAlign } from '../util';
import devWarning from '../../_util/devWarning';
import type { ConfigConsumerProps } from '../../config-provider';
import { ConfigContext } from '../../config-provider';
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
import SizeContext from '../../config-provider/SizeContext';
import DisabledContext from '../../config-provider/DisabledContext';
import type { PickerProps, PickerLocale, PickerDateProps, PickerTimeProps } from '.';
import { getTimeProps, Components } from '.';
import { FormItemInputContext } from '../../form/context';
import type { InputStatus } from '../../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
import type { DatePickRef, PickerComponentClass } from './interface';
export default function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
type DatePickerProps = PickerProps<DateType> & {
status?: InputStatus;
};
function getPicker<InnerPickerProps extends DatePickerProps>(
picker?: PickerMode,
displayName?: string,
) {
class Picker extends React.Component<InnerPickerProps> {
static contextType = ConfigContext;
static displayName: string;
context: ConfigConsumerProps;
pickerRef = React.createRef<RCPicker<DateType>>();
constructor(props: InnerPickerProps) {
super(props);
devWarning(
picker !== 'quarter',
displayName!,
`DatePicker.${displayName} is legacy usage. Please use DatePicker[picker='${picker}'] directly.`,
);
}
focus = () => {
if (this.pickerRef.current) {
this.pickerRef.current.focus();
}
};
blur = () => {
if (this.pickerRef.current) {
this.pickerRef.current.blur();
}
};
renderPicker = (contextLocale: PickerLocale) => {
const locale = { ...contextLocale, ...this.props.locale };
const { getPrefixCls, direction, getPopupContainer } = this.context;
const {
prefixCls,
getPopupContainer: customizeGetPopupContainer,
className,
size: customizeSize,
disabled: customDisabled,
bordered = true,
placement,
placeholder,
status: customStatus,
...restProps
} = this.props;
const { format, showTime } = this.props as any;
const additionalProps = {
showToday: true,
};
let additionalOverrideProps: any = {};
if (picker) {
additionalOverrideProps.picker = picker;
}
const mergedPicker = picker || this.props.picker;
additionalOverrideProps = {
...additionalOverrideProps,
...(showTime ? getTimeProps({ format, picker: mergedPicker, ...showTime }) : {}),
...(mergedPicker === 'time'
? getTimeProps({ format, ...this.props, picker: mergedPicker })
: {}),
};
const rootPrefixCls = getPrefixCls();
return (
<DisabledContext.Consumer>
{disabled => {
const mergedDisabled = customDisabled || disabled;
return (
<SizeContext.Consumer>
{size => {
const mergedSize = customizeSize || size;
return (
<FormItemInputContext.Consumer>
{({ hasFeedback, status: contextStatus, feedbackIcon }) => {
const suffixNode = (
<>
{mergedPicker === 'time' ? (
<ClockCircleOutlined />
) : (
<CalendarOutlined />
)}
{hasFeedback && feedbackIcon}
</>
);
return (
<RCPicker<DateType>
ref={this.pickerRef}
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
suffixIcon={suffixNode}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
{...additionalProps}
{...restProps}
{...additionalOverrideProps}
locale={locale!.lang}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(
prefixCls as string,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
className,
)}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
disabled={mergedDisabled}
/>
);
}}
</FormItemInputContext.Consumer>
);
}}
</SizeContext.Consumer>
);
}}
</DisabledContext.Consumer>
);
};
render() {
return (
<LocaleReceiver componentName="DatePicker" defaultLocale={enUS}>
{this.renderPicker}
</LocaleReceiver>
);
}
}
const PickerWrapper = forwardRef<DatePickRef<DateType>, InnerPickerProps>((props, ref) => {
const { prefixCls: customizePrefixCls } = props;
const { getPrefixCls } = useContext(ConfigContext);
const prefixCls = getPrefixCls('picker', customizePrefixCls);
const pickerProps: InnerPickerProps = {
...props,
prefixCls,
ref,
};
return <Picker {...pickerProps} />;
});
if (displayName) {
PickerWrapper.displayName = displayName;
}
return PickerWrapper as unknown as PickerComponentClass<InnerPickerProps>;
}
const DatePicker = getPicker<DatePickerProps>();
const WeekPicker = getPicker<Omit<PickerDateProps<DateType>, 'picker'>>('week', 'WeekPicker');
const MonthPicker = getPicker<Omit<PickerDateProps<DateType>, 'picker'>>('month', 'MonthPicker');
const YearPicker = getPicker<Omit<PickerDateProps<DateType>, 'picker'>>('year', 'YearPicker');
const TimePicker = getPicker<Omit<PickerTimeProps<DateType>, 'picker'>>('time', 'TimePicker');
const QuarterPicker = getPicker<Omit<PickerTimeProps<DateType>, 'picker'>>(
'quarter',
'QuarterPicker',
);
return { DatePicker, WeekPicker, MonthPicker, YearPicker, TimePicker, QuarterPicker };
}