mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-07 09:26:06 +08:00
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
This commit is contained in:
parent
50b82ca509
commit
407a41a142
@ -37,6 +37,7 @@ const eslintrc = {
|
||||
'react/forbid-prop-types': 0,
|
||||
'react/jsx-indent': 0,
|
||||
'react/jsx-wrap-multilines': ['error', { declaration: false, assignment: false }],
|
||||
'import/extensions': 0,
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
@ -86,7 +87,6 @@ const eslintrc = {
|
||||
'react/static-property-placement': 0,
|
||||
'jest/no-test-callback': 0,
|
||||
'jest/expect-expect': 0,
|
||||
'import/extensions': 0,
|
||||
},
|
||||
globals: {
|
||||
gtag: true,
|
||||
|
@ -1,223 +1,188 @@
|
||||
import * as React from 'react';
|
||||
import * as moment from 'moment';
|
||||
import { GenerateConfig } from 'rc-picker/lib/generate';
|
||||
import { Locale } from 'rc-picker/lib/interface';
|
||||
import Select from '../select';
|
||||
import { Group, Button, RadioChangeEvent } from '../radio';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { Group, Button } from '../radio';
|
||||
import { CalendarMode } from './generateCalendar';
|
||||
|
||||
const { Option } = Select;
|
||||
const YearSelectOffset = 10;
|
||||
const YearSelectTotal = 20;
|
||||
|
||||
function getMonthsLocale(value: moment.Moment) {
|
||||
const current = value.clone();
|
||||
const localeData = value.localeData();
|
||||
const months: any[] = [];
|
||||
for (let i = 0; i < 12; i++) {
|
||||
current.month(i);
|
||||
months.push(localeData.monthsShort(current));
|
||||
}
|
||||
return months;
|
||||
interface SharedProps<DateType> {
|
||||
prefixCls: string;
|
||||
value: DateType;
|
||||
validRange?: [DateType, DateType];
|
||||
generateConfig: GenerateConfig<DateType>;
|
||||
locale: Locale;
|
||||
fullscreen: boolean;
|
||||
divRef: React.RefObject<HTMLDivElement>;
|
||||
onChange: (year: DateType) => void;
|
||||
}
|
||||
|
||||
export interface RenderHeader {
|
||||
value: moment.Moment;
|
||||
onChange?: (value: moment.Moment) => void;
|
||||
type: string;
|
||||
onTypeChange: (type: string) => void;
|
||||
}
|
||||
function YearSelect<DateType>(props: SharedProps<DateType>) {
|
||||
const {
|
||||
fullscreen,
|
||||
validRange,
|
||||
generateConfig,
|
||||
locale,
|
||||
prefixCls,
|
||||
value,
|
||||
onChange,
|
||||
divRef,
|
||||
} = props;
|
||||
|
||||
export type HeaderRender = (headerRender: RenderHeader) => React.ReactNode;
|
||||
const year = generateConfig.getYear(value);
|
||||
|
||||
export interface HeaderProps {
|
||||
prefixCls?: string;
|
||||
locale?: any;
|
||||
fullscreen?: boolean;
|
||||
yearSelectOffset?: number;
|
||||
yearSelectTotal?: number;
|
||||
type?: string;
|
||||
onValueChange?: (value: moment.Moment) => void;
|
||||
onTypeChange?: (type: string) => void;
|
||||
value: moment.Moment;
|
||||
validRange?: [moment.Moment, moment.Moment];
|
||||
headerRender?: HeaderRender;
|
||||
}
|
||||
let start = year - YearSelectOffset;
|
||||
let end = start + YearSelectTotal;
|
||||
|
||||
export default class Header extends React.Component<HeaderProps, any> {
|
||||
static defaultProps = {
|
||||
yearSelectOffset: 10,
|
||||
yearSelectTotal: 20,
|
||||
};
|
||||
|
||||
private calenderHeaderNode: HTMLDivElement;
|
||||
|
||||
getYearSelectElement(prefixCls: string, year: number) {
|
||||
const { yearSelectOffset, yearSelectTotal, locale = {}, fullscreen, validRange } = this.props;
|
||||
let start = year - (yearSelectOffset as number);
|
||||
let end = start + (yearSelectTotal as number);
|
||||
if (validRange) {
|
||||
start = validRange[0].get('year');
|
||||
end = validRange[1].get('year') + 1;
|
||||
}
|
||||
const suffix = locale.year === '年' ? '年' : '';
|
||||
const options: React.ReactElement<any>[] = [];
|
||||
for (let index = start; index < end; index++) {
|
||||
const optionValue = `${index}`;
|
||||
options.push(
|
||||
<Option key={optionValue} value={optionValue}>
|
||||
{index + suffix}
|
||||
</Option>,
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Select
|
||||
size={fullscreen ? 'default' : 'small'}
|
||||
dropdownMatchSelectWidth={100}
|
||||
className={`${prefixCls}-year-select`}
|
||||
onChange={this.onYearChange}
|
||||
value={String(year)}
|
||||
getPopupContainer={() => this.calenderHeaderNode}
|
||||
>
|
||||
{options}
|
||||
</Select>
|
||||
);
|
||||
if (validRange) {
|
||||
start = generateConfig.getYear(validRange[0]);
|
||||
end = generateConfig.getYear(validRange[1]) + 1;
|
||||
}
|
||||
|
||||
getMonthSelectElement(prefixCls: string, month: number, months: number[]) {
|
||||
const { fullscreen, validRange, value } = this.props;
|
||||
const options: React.ReactElement<any>[] = [];
|
||||
let start = 0;
|
||||
let end = 12;
|
||||
if (validRange) {
|
||||
const [rangeStart, rangeEnd] = validRange;
|
||||
const currentYear = value.get('year');
|
||||
if (rangeEnd.get('year') === currentYear) {
|
||||
end = rangeEnd.get('month') + 1;
|
||||
}
|
||||
if (rangeStart.get('year') === currentYear) {
|
||||
start = rangeStart.get('month');
|
||||
}
|
||||
}
|
||||
|
||||
for (let index = start; index < end; index++) {
|
||||
const optionValue = `${index}`;
|
||||
options.push(
|
||||
<Option key={optionValue} value={optionValue}>
|
||||
{months[index]}
|
||||
</Option>,
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Select
|
||||
size={fullscreen ? 'default' : 'small'}
|
||||
dropdownMatchSelectWidth={100}
|
||||
className={`${prefixCls}-month-select`}
|
||||
value={String(month)}
|
||||
onChange={this.onMonthChange}
|
||||
getPopupContainer={() => this.calenderHeaderNode}
|
||||
>
|
||||
{options}
|
||||
</Select>
|
||||
);
|
||||
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 });
|
||||
}
|
||||
|
||||
onYearChange = (year: string) => {
|
||||
const { value, validRange } = this.props;
|
||||
const newValue = value.clone();
|
||||
newValue.year(parseInt(year, 10));
|
||||
// switch the month so that it remains within range when year changes
|
||||
if (validRange) {
|
||||
const [start, end] = validRange;
|
||||
const newYear = newValue.get('year');
|
||||
const newMonth = newValue.get('month');
|
||||
if (newYear === end.get('year') && newMonth > end.get('month')) {
|
||||
newValue.month(end.get('month'));
|
||||
}
|
||||
if (newYear === start.get('year') && newMonth < start.get('month')) {
|
||||
newValue.month(start.get('month'));
|
||||
}
|
||||
return (
|
||||
<Select
|
||||
size={fullscreen ? 'default' : 'small'}
|
||||
options={options}
|
||||
value={year}
|
||||
className={`${prefixCls}-year-select`}
|
||||
onChange={numYear => {
|
||||
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!.current!}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function MonthSelect<DateType>(props: SharedProps<DateType>) {
|
||||
const {
|
||||
prefixCls,
|
||||
fullscreen,
|
||||
validRange,
|
||||
value,
|
||||
generateConfig,
|
||||
locale,
|
||||
onChange,
|
||||
divRef,
|
||||
} = props;
|
||||
const month = generateConfig.getMonth(value);
|
||||
|
||||
let start = 0;
|
||||
let end = 12;
|
||||
|
||||
if (validRange) {
|
||||
const [rangeStart, rangeEnd] = validRange;
|
||||
const currentYear = generateConfig.getYear(value);
|
||||
if (generateConfig.getYear(rangeEnd) === currentYear) {
|
||||
end = generateConfig.getMonth(rangeEnd);
|
||||
}
|
||||
|
||||
const { onValueChange } = this.props;
|
||||
if (onValueChange) {
|
||||
onValueChange(newValue);
|
||||
if (generateConfig.getYear(rangeStart) === currentYear) {
|
||||
start = generateConfig.getMonth(rangeStart);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onMonthChange = (month: string) => {
|
||||
const newValue = this.props.value.clone();
|
||||
newValue.month(parseInt(month, 10));
|
||||
const { onValueChange } = this.props;
|
||||
if (onValueChange) {
|
||||
onValueChange(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
onInternalTypeChange = (e: RadioChangeEvent) => {
|
||||
this.onTypeChange(e.target.value as string);
|
||||
};
|
||||
|
||||
onTypeChange = (type: string) => {
|
||||
const { onTypeChange } = this.props;
|
||||
if (onTypeChange) {
|
||||
onTypeChange(type);
|
||||
}
|
||||
};
|
||||
|
||||
getCalenderHeaderNode = (node: HTMLDivElement) => {
|
||||
this.calenderHeaderNode = node;
|
||||
};
|
||||
|
||||
getMonthYearSelections = (getPrefixCls: ConfigConsumerProps['getPrefixCls']) => {
|
||||
const { prefixCls: customizePrefixCls, type, value } = this.props;
|
||||
|
||||
const prefixCls = getPrefixCls('fullcalendar', customizePrefixCls);
|
||||
const yearReactNode = this.getYearSelectElement(prefixCls, value.year());
|
||||
const monthReactNode =
|
||||
type === 'month'
|
||||
? this.getMonthSelectElement(prefixCls, value.month(), getMonthsLocale(value))
|
||||
: null;
|
||||
return {
|
||||
yearReactNode,
|
||||
monthReactNode,
|
||||
};
|
||||
};
|
||||
|
||||
getTypeSwitch = () => {
|
||||
const { locale = {}, type, fullscreen } = this.props;
|
||||
const size = fullscreen ? 'default' : 'small';
|
||||
return (
|
||||
<Group onChange={this.onInternalTypeChange} value={type} size={size}>
|
||||
<Button value="month">{locale.month}</Button>
|
||||
<Button value="year">{locale.year}</Button>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
headerRenderCustom = (headerRender: HeaderRender): React.ReactNode => {
|
||||
const { type, onValueChange, value } = this.props;
|
||||
|
||||
return headerRender({
|
||||
value,
|
||||
type: type || 'month',
|
||||
onChange: onValueChange,
|
||||
onTypeChange: this.onTypeChange,
|
||||
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,
|
||||
});
|
||||
};
|
||||
|
||||
renderHeader = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const { prefixCls, headerRender } = this.props;
|
||||
const typeSwitch = this.getTypeSwitch();
|
||||
const { yearReactNode, monthReactNode } = this.getMonthYearSelections(getPrefixCls);
|
||||
return headerRender ? (
|
||||
this.headerRenderCustom(headerRender)
|
||||
) : (
|
||||
<div className={`${prefixCls}-header`} ref={this.getCalenderHeaderNode}>
|
||||
{yearReactNode}
|
||||
{monthReactNode}
|
||||
{typeSwitch}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderHeader}</ConfigConsumer>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Select
|
||||
size={fullscreen ? 'default' : 'small'}
|
||||
dropdownMatchSelectWidth={100}
|
||||
className={`${prefixCls}-month-select`}
|
||||
value={month}
|
||||
options={options}
|
||||
onChange={newMonth => {
|
||||
onChange(generateConfig.setMonth(value, newMonth));
|
||||
}}
|
||||
getPopupContainer={() => divRef!.current!}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
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 ? 'default' : 'small'}
|
||||
className={`${prefixCls}-mode-switch`}
|
||||
>
|
||||
<Button value="month">{locale.month}</Button>
|
||||
<Button value="year">{locale.year}</Button>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
export interface CalendarHeaderProps<DateType> {
|
||||
prefixCls: string;
|
||||
value: DateType;
|
||||
validRange?: [DateType, DateType];
|
||||
generateConfig: GenerateConfig<DateType>;
|
||||
locale: Locale;
|
||||
mode: CalendarMode;
|
||||
fullscreen: boolean;
|
||||
onChange: (date: DateType) => void;
|
||||
onModeChange: (mode: CalendarMode) => void;
|
||||
}
|
||||
function CalendarHeader<DateType>(props: CalendarHeaderProps<DateType>) {
|
||||
const { prefixCls, fullscreen, mode, onChange, onModeChange } = props;
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const sharedProps = {
|
||||
...props,
|
||||
onChange,
|
||||
fullscreen,
|
||||
divRef,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${prefixCls}-header`} ref={divRef}>
|
||||
<YearSelect {...sharedProps} />
|
||||
{mode === 'month' && <MonthSelect {...sharedProps} />}
|
||||
<ModeSwitch {...sharedProps} onModeChange={onModeChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CalendarHeader;
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import Moment from 'moment';
|
||||
import momentGenerateConfig from 'rc-picker/lib/generate/moment';
|
||||
import { mount } from 'enzyme';
|
||||
import MockDate from 'mockdate';
|
||||
import Calendar from '..';
|
||||
@ -11,7 +12,6 @@ import mountTest from '../../../tests/shared/mountTest';
|
||||
|
||||
describe('Calendar', () => {
|
||||
mountTest(Calendar);
|
||||
mountTest(() => <Header value={Moment()} />);
|
||||
|
||||
function openSelect(wrapper, className) {
|
||||
wrapper
|
||||
@ -34,7 +34,7 @@ describe('Calendar', () => {
|
||||
const onSelect = jest.fn();
|
||||
const wrapper = mount(<Calendar onSelect={onSelect} />);
|
||||
wrapper
|
||||
.find('.ant-fullcalendar-cell')
|
||||
.find('.ant-picker-cell')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything());
|
||||
@ -49,11 +49,11 @@ describe('Calendar', () => {
|
||||
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
|
||||
);
|
||||
wrapper
|
||||
.find('[title="February 1, 2018"]')
|
||||
.find('[title="2018-02-01"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('[title="February 2, 2018"]')
|
||||
.find('[title="2018-02-02"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
expect(onSelect.mock.calls.length).toBe(1);
|
||||
@ -66,12 +66,10 @@ describe('Calendar', () => {
|
||||
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
|
||||
);
|
||||
wrapper
|
||||
.find('[title="February 20, 2018"]')
|
||||
.find('[title="2018-02-20"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
const elem = wrapper
|
||||
.find('[title="February 20, 2018"]')
|
||||
.hasClass('ant-fullcalendar-disabled-cell');
|
||||
const elem = wrapper.find('[title="2018-02-20"]').hasClass('ant-picker-cell-disabled');
|
||||
expect(elem).toEqual(true);
|
||||
expect(onSelect.mock.calls.length).toBe(0);
|
||||
});
|
||||
@ -89,28 +87,28 @@ describe('Calendar', () => {
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('[title="Jan"]')
|
||||
.find('[title="2018-01"]')
|
||||
.at(0)
|
||||
.hasClass('ant-fullcalendar-month-panel-cell-disabled'),
|
||||
.hasClass('ant-picker-cell-disabled'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
wrapper
|
||||
.find('[title="Feb"]')
|
||||
.find('[title="2018-02"]')
|
||||
.at(0)
|
||||
.hasClass('ant-fullcalendar-month-panel-cell-disabled'),
|
||||
.hasClass('ant-picker-cell-disabled'),
|
||||
).toBe(false);
|
||||
expect(
|
||||
wrapper
|
||||
.find('[title="Jun"]')
|
||||
.find('[title="2018-06"]')
|
||||
.at(0)
|
||||
.hasClass('ant-fullcalendar-month-panel-cell-disabled'),
|
||||
.hasClass('ant-picker-cell-disabled'),
|
||||
).toBe(true);
|
||||
wrapper
|
||||
.find('[title="Jan"]')
|
||||
.find('[title="2018-01"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('[title="Mar"]')
|
||||
.find('[title="2018-03"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
expect(onSelect.mock.calls.length).toBe(1);
|
||||
@ -119,9 +117,9 @@ describe('Calendar', () => {
|
||||
it('months other than in valid range should not be shown in header', () => {
|
||||
const validRange = [Moment('2017-02-02'), Moment('2018-05-18')];
|
||||
const wrapper = mount(<Calendar validRange={validRange} />);
|
||||
openSelect(wrapper, '.ant-fullcalendar-year-select');
|
||||
openSelect(wrapper, '.ant-picker-calendar-year-select');
|
||||
clickSelectItem(wrapper);
|
||||
openSelect(wrapper, '.ant-fullcalendar-month-select');
|
||||
openSelect(wrapper, '.ant-picker-calendar-month-select');
|
||||
// 2 years and 11 months
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(13);
|
||||
});
|
||||
@ -129,8 +127,7 @@ describe('Calendar', () => {
|
||||
it('getDateRange should returns a disabledDate function', () => {
|
||||
const validRange = [Moment('2018-02-02'), Moment('2018-05-18')];
|
||||
const wrapper = mount(<Calendar validRange={validRange} defaultValue={Moment('2018-02-02')} />);
|
||||
const instance = wrapper.instance();
|
||||
const disabledDate = instance.getDateRange(validRange);
|
||||
const { disabledDate } = wrapper.find('PickerPanel').props();
|
||||
expect(disabledDate(Moment('2018-06-02'))).toBe(true);
|
||||
expect(disabledDate(Moment('2018-04-02'))).toBe(false);
|
||||
});
|
||||
@ -139,9 +136,9 @@ describe('Calendar', () => {
|
||||
const monthMode = 'month';
|
||||
const yearMode = 'year';
|
||||
const wrapper = mount(<Calendar />);
|
||||
expect(wrapper.state().mode).toEqual(monthMode);
|
||||
wrapper.setProps({ mode: 'year' });
|
||||
expect(wrapper.state().mode).toEqual(yearMode);
|
||||
expect(wrapper.find('CalendarHeader').props().mode).toEqual(monthMode);
|
||||
wrapper.setProps({ mode: yearMode });
|
||||
expect(wrapper.find('CalendarHeader').props().mode).toEqual(yearMode);
|
||||
});
|
||||
|
||||
it('Calendar should switch mode', () => {
|
||||
@ -149,9 +146,9 @@ describe('Calendar', () => {
|
||||
const yearMode = 'year';
|
||||
const onPanelChangeStub = jest.fn();
|
||||
const wrapper = mount(<Calendar mode={yearMode} onPanelChange={onPanelChangeStub} />);
|
||||
expect(wrapper.state().mode).toEqual(yearMode);
|
||||
expect(wrapper.find('CalendarHeader').props().mode).toEqual(yearMode);
|
||||
wrapper.setProps({ mode: monthMode });
|
||||
expect(wrapper.state().mode).toEqual(monthMode);
|
||||
expect(wrapper.find('CalendarHeader').props().mode).toEqual(monthMode);
|
||||
expect(onPanelChangeStub).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
@ -170,7 +167,7 @@ describe('Calendar', () => {
|
||||
const wrapper = mount(<Calendar onPanelChange={onPanelChange} value={date} />);
|
||||
|
||||
wrapper
|
||||
.find('.ant-fullcalendar-cell')
|
||||
.find('.ant-picker-cell')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
|
||||
@ -182,12 +179,14 @@ describe('Calendar', () => {
|
||||
const onPanelChange = jest.fn();
|
||||
const date = new Moment(new Date(Date.UTC(2017, 7, 9, 8)));
|
||||
const wrapper = mount(<Calendar onPanelChange={onPanelChange} value={date} />);
|
||||
expect(wrapper.state().mode).toBe('month');
|
||||
expect(wrapper.find('.ant-fullcalendar-table').length).toBe(1);
|
||||
expect(wrapper.find('.ant-fullcalendar-month-panel-table').length).toBe(0);
|
||||
|
||||
expect(wrapper.find('CalendarHeader').props().mode).toBe('month');
|
||||
expect(wrapper.find('.ant-picker-date-panel').length).toBe(1);
|
||||
expect(wrapper.find('.ant-picker-month-panel').length).toBe(0);
|
||||
|
||||
wrapper.find('.ant-radio-button-input[value="year"]').simulate('change');
|
||||
expect(wrapper.find('.ant-fullcalendar-table').length).toBe(0);
|
||||
expect(wrapper.find('.ant-fullcalendar-month-panel-table').length).toBe(1);
|
||||
expect(wrapper.find('.ant-picker-date-panel').length).toBe(0);
|
||||
expect(wrapper.find('.ant-picker-month-panel').length).toBe(1);
|
||||
expect(onPanelChange).toHaveBeenCalled();
|
||||
expect(onPanelChange.mock.calls[0][1]).toEqual('year');
|
||||
});
|
||||
@ -195,13 +194,15 @@ describe('Calendar', () => {
|
||||
const createWrapper = (start, end, value, onValueChange) => {
|
||||
const wrapper = mount(
|
||||
<Header
|
||||
onValueChange={onValueChange}
|
||||
prefixCls="ant-picker-calendar"
|
||||
generateConfig={momentGenerateConfig}
|
||||
onChange={onValueChange}
|
||||
value={value}
|
||||
validRange={[start, end]}
|
||||
locale={{ year: '年' }}
|
||||
/>,
|
||||
);
|
||||
openSelect(wrapper, '.ant-fullcalendar-year-select');
|
||||
openSelect(wrapper, '.ant-picker-calendar-year-select');
|
||||
clickSelectItem(wrapper);
|
||||
};
|
||||
|
||||
@ -223,6 +224,29 @@ describe('Calendar', () => {
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.year('2019').month('10'));
|
||||
});
|
||||
|
||||
it('if change year and new month > end month, set value.month to end.month ', () => {
|
||||
const value = new Moment('2018-11-03');
|
||||
const start = new Moment('2000-01-01');
|
||||
const end = new Moment('2019-03-01');
|
||||
const onValueChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Header
|
||||
prefixCls="ant-picker-calendar"
|
||||
generateConfig={momentGenerateConfig}
|
||||
onChange={onValueChange}
|
||||
value={value}
|
||||
validRange={[start, end]}
|
||||
locale={{ year: '年' }}
|
||||
/>,
|
||||
);
|
||||
openSelect(wrapper, '.ant-picker-calendar-year-select');
|
||||
wrapper
|
||||
.find('.ant-select-item-option')
|
||||
.last()
|
||||
.simulate('click');
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.year('2019').month('2'));
|
||||
});
|
||||
|
||||
it('onMonthChange should work correctly', () => {
|
||||
const start = new Moment('2018-11-01');
|
||||
const end = new Moment('2019-03-01');
|
||||
@ -230,14 +254,16 @@ describe('Calendar', () => {
|
||||
const onValueChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Header
|
||||
onValueChange={onValueChange}
|
||||
prefixCls="ant-picker-calendar"
|
||||
generateConfig={momentGenerateConfig}
|
||||
onChange={onValueChange}
|
||||
value={value}
|
||||
validRange={[start, end]}
|
||||
locale={{ year: '年' }}
|
||||
type="month"
|
||||
locale={{ year: '年', locale: 'zh_CN' }}
|
||||
mode="month"
|
||||
/>,
|
||||
);
|
||||
openSelect(wrapper, '.ant-fullcalendar-month-select');
|
||||
openSelect(wrapper, '.ant-picker-calendar-month-select');
|
||||
clickSelectItem(wrapper);
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.month(10));
|
||||
});
|
||||
@ -247,8 +273,10 @@ describe('Calendar', () => {
|
||||
const value = new Moment('2018-12-03');
|
||||
const wrapper = mount(
|
||||
<Header
|
||||
onTypeChange={onTypeChange}
|
||||
locale={{ year: '年', month: '月' }}
|
||||
prefixCls="ant-picker-calendar"
|
||||
generateConfig={momentGenerateConfig}
|
||||
onModeChange={onTypeChange}
|
||||
locale={{ year: '年', month: '月', locale: 'zh_CN' }}
|
||||
value={value}
|
||||
type="date"
|
||||
/>,
|
||||
@ -264,6 +292,8 @@ describe('Calendar', () => {
|
||||
const onMonthChange = jest.fn();
|
||||
const onYearChange = jest.fn();
|
||||
const onTypeChange = jest.fn();
|
||||
|
||||
// Year
|
||||
const headerRender = jest.fn(({ value }) => {
|
||||
const year = value.year();
|
||||
const options = [];
|
||||
@ -297,6 +327,8 @@ describe('Calendar', () => {
|
||||
.simulate('click');
|
||||
|
||||
expect(onYearChange).toHaveBeenCalled();
|
||||
|
||||
// Month
|
||||
const headerRenderWithMonth = jest.fn(({ value }) => {
|
||||
const start = 0;
|
||||
const end = 12;
|
||||
@ -316,14 +348,15 @@ describe('Calendar', () => {
|
||||
</Select.Option>,
|
||||
);
|
||||
}
|
||||
|
||||
const month = value.month();
|
||||
return (
|
||||
<Select
|
||||
size="small"
|
||||
dropdownMatchSelectWidth={false}
|
||||
value={String(month)}
|
||||
className="my-mont-select"
|
||||
className="my-month-select"
|
||||
onChange={onMonthChange}
|
||||
value={String(month)}
|
||||
>
|
||||
{monthOptions}
|
||||
</Select>
|
||||
@ -339,8 +372,10 @@ describe('Calendar', () => {
|
||||
findSelectItem(wrapperWithMonth)
|
||||
.last()
|
||||
.simulate('click');
|
||||
|
||||
expect(onMonthChange).toHaveBeenCalled();
|
||||
|
||||
// Type
|
||||
const headerRenderWithTypeChange = jest.fn(({ type }) => {
|
||||
return (
|
||||
<Group size="small" onChange={onTypeChange} value={type}>
|
||||
@ -360,4 +395,28 @@ describe('Calendar', () => {
|
||||
.simulate('change');
|
||||
expect(onTypeChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('dateFullCellRender', () => {
|
||||
const wrapper = mount(
|
||||
<Calendar dateFullCellRender={() => <div className="light">Bamboo</div>} />,
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.light')
|
||||
.first()
|
||||
.text(),
|
||||
).toEqual('Bamboo');
|
||||
});
|
||||
|
||||
it('monthFullCellRender', () => {
|
||||
const wrapper = mount(
|
||||
<Calendar mode="year" monthFullCellRender={() => <div className="bamboo">Light</div>} />,
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.bamboo')
|
||||
.first()
|
||||
.text(),
|
||||
).toEqual('Light');
|
||||
});
|
||||
});
|
||||
|
@ -17,7 +17,7 @@ A basic calendar component with Year/Month switch.
|
||||
import { Calendar } from 'antd';
|
||||
|
||||
function onPanelChange(value, mode) {
|
||||
console.log(value, mode);
|
||||
console.log(value.format('YYYY-MM-DD'), mode);
|
||||
}
|
||||
|
||||
ReactDOM.render(<Calendar onPanelChange={onPanelChange} />, mountNode);
|
||||
|
@ -21,7 +21,7 @@ function onPanelChange(value, mode) {
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<div style={{ width: 300, border: '1px solid #d9d9d9', borderRadius: 4 }}>
|
||||
<div style={{ width: 300, border: '1px solid #f0f0f0', borderRadius: 4 }}>
|
||||
<Calendar fullscreen={false} onPanelChange={onPanelChange} />
|
||||
</div>,
|
||||
mountNode,
|
||||
|
287
components/calendar/generateCalendar.tsx
Normal file
287
components/calendar/generateCalendar.tsx
Normal file
@ -0,0 +1,287 @@
|
||||
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;
|
@ -1,281 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import * as moment from 'moment';
|
||||
import FullCalendar from 'rc-calendar/lib/FullCalendar';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import Header, { HeaderRender } from './Header';
|
||||
import enUS from './locale/en_US';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import interopDefault from '../_util/interopDefault';
|
||||
import { Moment } from 'moment';
|
||||
import momentGenerateConfig from 'rc-picker/lib/generate/moment';
|
||||
import generateCalendar from './generateCalendar';
|
||||
|
||||
export { HeaderProps } from './Header';
|
||||
|
||||
function noop() {
|
||||
return null;
|
||||
}
|
||||
|
||||
function zerofixed(v: number) {
|
||||
if (v < 10) {
|
||||
return `0${v}`;
|
||||
}
|
||||
return `${v}`;
|
||||
}
|
||||
|
||||
export type CalendarMode = 'month' | 'year';
|
||||
export interface CalendarProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
value?: moment.Moment;
|
||||
defaultValue?: moment.Moment;
|
||||
mode?: CalendarMode;
|
||||
fullscreen?: boolean;
|
||||
dateCellRender?: (date: moment.Moment) => React.ReactNode;
|
||||
monthCellRender?: (date: moment.Moment) => React.ReactNode;
|
||||
dateFullCellRender?: (date: moment.Moment) => React.ReactNode;
|
||||
monthFullCellRender?: (date: moment.Moment) => React.ReactNode;
|
||||
locale?: any;
|
||||
style?: React.CSSProperties;
|
||||
onPanelChange?: (date?: moment.Moment, mode?: CalendarMode) => void;
|
||||
onSelect?: (date?: moment.Moment) => void;
|
||||
onChange?: (date?: moment.Moment) => void;
|
||||
disabledDate?: (current: moment.Moment) => boolean;
|
||||
validRange?: [moment.Moment, moment.Moment];
|
||||
headerRender?: HeaderRender;
|
||||
}
|
||||
|
||||
export interface CalendarState {
|
||||
value: moment.Moment;
|
||||
mode?: CalendarMode;
|
||||
}
|
||||
|
||||
class Calendar extends React.Component<CalendarProps, CalendarState> {
|
||||
static defaultProps = {
|
||||
locale: {},
|
||||
fullscreen: true,
|
||||
onSelect: noop,
|
||||
onPanelChange: noop,
|
||||
onChange: noop,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
monthCellRender: PropTypes.func,
|
||||
dateCellRender: PropTypes.func,
|
||||
monthFullCellRender: PropTypes.func,
|
||||
dateFullCellRender: PropTypes.func,
|
||||
fullscreen: PropTypes.bool,
|
||||
locale: PropTypes.object,
|
||||
prefixCls: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
onPanelChange: PropTypes.func,
|
||||
value: PropTypes.object as PropTypes.Requireable<moment.Moment>,
|
||||
onSelect: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
headerRender: PropTypes.func,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps: CalendarProps) {
|
||||
const newState = {} as CalendarState;
|
||||
if ('value' in nextProps) {
|
||||
newState.value = nextProps.value!;
|
||||
}
|
||||
if ('mode' in nextProps) {
|
||||
newState.mode = nextProps.mode;
|
||||
}
|
||||
return Object.keys(newState).length > 0 ? newState : null;
|
||||
}
|
||||
|
||||
prefixCls?: string;
|
||||
|
||||
constructor(props: CalendarProps) {
|
||||
super(props);
|
||||
|
||||
const value = props.value || props.defaultValue || interopDefault(moment)();
|
||||
if (!interopDefault(moment).isMoment(value)) {
|
||||
throw new Error(
|
||||
'The value/defaultValue of Calendar must be a moment object after `antd@2.0`, ' +
|
||||
'see: https://u.ant.design/calendar-value',
|
||||
);
|
||||
}
|
||||
this.state = {
|
||||
value,
|
||||
mode: props.mode || 'month',
|
||||
};
|
||||
}
|
||||
|
||||
onHeaderValueChange = (value: moment.Moment) => {
|
||||
this.setValue(value, 'changePanel');
|
||||
};
|
||||
|
||||
onHeaderTypeChange = (mode: CalendarMode) => {
|
||||
this.setState({ mode });
|
||||
this.onPanelChange(this.state.value, mode);
|
||||
};
|
||||
|
||||
onPanelChange(value: moment.Moment, mode: CalendarMode | undefined) {
|
||||
const { onPanelChange, onChange } = this.props;
|
||||
if (onPanelChange) {
|
||||
onPanelChange(value, mode);
|
||||
}
|
||||
if (onChange && value !== this.state.value) {
|
||||
onChange(value);
|
||||
}
|
||||
}
|
||||
|
||||
onSelect = (value: moment.Moment) => {
|
||||
this.setValue(value, 'select');
|
||||
};
|
||||
|
||||
setValue = (value: moment.Moment, way: 'select' | 'changePanel') => {
|
||||
const prevValue = this.props.value || this.state.value;
|
||||
const { mode } = this.state;
|
||||
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ value });
|
||||
}
|
||||
if (way === 'select') {
|
||||
if (prevValue && prevValue.month() !== value.month()) {
|
||||
this.onPanelChange(value, mode);
|
||||
}
|
||||
if (this.props.onSelect) {
|
||||
this.props.onSelect(value);
|
||||
}
|
||||
} else if (way === 'changePanel') {
|
||||
this.onPanelChange(value, mode);
|
||||
}
|
||||
};
|
||||
|
||||
getDateRange = (
|
||||
validRange: [moment.Moment, moment.Moment],
|
||||
disabledDate?: (current: moment.Moment) => boolean,
|
||||
) => (current: moment.Moment) => {
|
||||
if (!current) {
|
||||
return false;
|
||||
}
|
||||
const [startDate, endDate] = validRange;
|
||||
const inRange = !current.isBetween(startDate, endDate, 'days', '[]');
|
||||
if (disabledDate) {
|
||||
return disabledDate(current) || inRange;
|
||||
}
|
||||
return inRange;
|
||||
};
|
||||
|
||||
getDefaultLocale = () => {
|
||||
const result = {
|
||||
...enUS,
|
||||
...this.props.locale,
|
||||
};
|
||||
result.lang = {
|
||||
...result.lang,
|
||||
...(this.props.locale || {}).lang,
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
monthCellRender = (value: moment.Moment) => {
|
||||
const { monthCellRender = noop as Function } = this.props;
|
||||
const { prefixCls } = this;
|
||||
return (
|
||||
<div className={`${prefixCls}-month`}>
|
||||
<div className={`${prefixCls}-value`}>{value.localeData().monthsShort(value)}</div>
|
||||
<div className={`${prefixCls}-content`}>{monthCellRender(value)}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
dateCellRender = (value: moment.Moment) => {
|
||||
const { dateCellRender = noop as Function } = this.props;
|
||||
const { prefixCls } = this;
|
||||
return (
|
||||
<div className={`${prefixCls}-date`}>
|
||||
<div className={`${prefixCls}-value`}>{zerofixed(value.date())}</div>
|
||||
<div className={`${prefixCls}-content`}>{dateCellRender(value)}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
renderCalendar = (locale: any, localeCode: string) => {
|
||||
const { state, props } = this;
|
||||
const { value, mode } = state;
|
||||
if (value && localeCode) {
|
||||
value.locale(localeCode);
|
||||
}
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
style,
|
||||
className,
|
||||
fullscreen,
|
||||
headerRender,
|
||||
dateFullCellRender,
|
||||
monthFullCellRender,
|
||||
} = props;
|
||||
const monthCellRender = monthFullCellRender || this.monthCellRender;
|
||||
const dateCellRender = dateFullCellRender || this.dateCellRender;
|
||||
|
||||
let { disabledDate } = props;
|
||||
|
||||
if (props.validRange) {
|
||||
disabledDate = this.getDateRange(props.validRange, disabledDate);
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfigConsumer>
|
||||
{({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const prefixCls = getPrefixCls('fullcalendar', customizePrefixCls);
|
||||
|
||||
// To support old version react.
|
||||
// Have to add prefixCls on the instance.
|
||||
// https://github.com/facebook/react/issues/12397
|
||||
this.prefixCls = prefixCls;
|
||||
|
||||
let cls = className || '';
|
||||
if (fullscreen) {
|
||||
cls += ` ${prefixCls}-fullscreen`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cls} style={style}>
|
||||
<Header
|
||||
fullscreen={fullscreen}
|
||||
type={mode}
|
||||
headerRender={headerRender}
|
||||
value={value}
|
||||
locale={locale.lang}
|
||||
prefixCls={prefixCls}
|
||||
onTypeChange={this.onHeaderTypeChange}
|
||||
onValueChange={this.onHeaderValueChange}
|
||||
validRange={props.validRange}
|
||||
/>
|
||||
<FullCalendar
|
||||
{...props}
|
||||
disabledDate={disabledDate}
|
||||
Select={noop}
|
||||
locale={locale.lang}
|
||||
type={mode === 'year' ? 'month' : 'date'}
|
||||
prefixCls={prefixCls}
|
||||
showHeader={false}
|
||||
value={value}
|
||||
monthCellRender={monthCellRender}
|
||||
dateCellRender={dateCellRender}
|
||||
onSelect={this.onSelect}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</ConfigConsumer>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<LocaleReceiver componentName="Calendar" defaultLocale={this.getDefaultLocale}>
|
||||
{this.renderCalendar}
|
||||
</LocaleReceiver>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
polyfill(Calendar);
|
||||
const Calendar = generateCalendar<Moment>(momentGenerateConfig);
|
||||
|
||||
export default Calendar;
|
||||
|
@ -1,279 +1,165 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@full-calendar-prefix-cls: ~'@{ant-prefix}-fullcalendar';
|
||||
@calendar-prefix-cls: ~'@{ant-prefix}-picker-calendar';
|
||||
@calendar-picker-prefix-cls: ~'@{ant-prefix}-picker';
|
||||
|
||||
.@{full-calendar-prefix-cls} {
|
||||
.@{calendar-prefix-cls} {
|
||||
.reset-component;
|
||||
background: @calendar-bg;
|
||||
|
||||
border-top: @border-width-base @border-style-base @border-color-base;
|
||||
outline: none;
|
||||
|
||||
.@{ant-prefix}-select&-year-select {
|
||||
width: 90px;
|
||||
|
||||
&.@{ant-prefix}-select-sm {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{ant-prefix}-select&-month-select {
|
||||
width: 80px;
|
||||
margin-left: 8px;
|
||||
text-align: left;
|
||||
|
||||
&.@{ant-prefix}-select-sm {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
// ========================= Header =========================
|
||||
&-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 11px 16px 11px 0;
|
||||
text-align: right;
|
||||
|
||||
.@{ant-prefix}-select-dropdown {
|
||||
text-align: left;
|
||||
.@{calendar-prefix-cls}-year-select {
|
||||
width: 85px;
|
||||
}
|
||||
|
||||
.@{ant-prefix}-radio-group {
|
||||
margin-left: 8px;
|
||||
text-align: left;
|
||||
.@{calendar-prefix-cls}-month-select {
|
||||
width: 70px;
|
||||
margin-left: @padding-xs;
|
||||
}
|
||||
|
||||
label.@{ant-prefix}-radio-button {
|
||||
height: 22px;
|
||||
padding: 0 10px;
|
||||
line-height: 20px;
|
||||
.@{calendar-prefix-cls}-mode-switch {
|
||||
margin-left: @padding-xs;
|
||||
}
|
||||
}
|
||||
|
||||
&-date-panel {
|
||||
position: relative;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&-calendar-body {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: 256px;
|
||||
background-color: transparent;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
.@{calendar-picker-prefix-cls}-panel {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
td {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-calendar-table {
|
||||
margin-bottom: 0;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
&-column-header {
|
||||
width: 33px;
|
||||
padding: 0;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
.@{full-calendar-prefix-cls}-column-header-inner {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
&-week-number-header {
|
||||
.@{full-calendar-prefix-cls}-column-header-inner {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-month,
|
||||
&-date {
|
||||
text-align: center;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
&-value {
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
color: @text-color;
|
||||
line-height: 24px;
|
||||
background: transparent;
|
||||
border-radius: @border-radius-base;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: @item-hover-bg;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: @text-color-inverse;
|
||||
background: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-month-panel-cell &-value {
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
&-today &-value,
|
||||
&-month-panel-current-cell &-value {
|
||||
box-shadow: 0 0 0 1px @primary-color inset;
|
||||
}
|
||||
|
||||
&-selected-day &-value,
|
||||
&-month-panel-selected-cell &-value {
|
||||
color: @text-color-inverse;
|
||||
background: @primary-color;
|
||||
}
|
||||
|
||||
&-disabled-cell-first-of-row &-value {
|
||||
border-top-left-radius: @border-radius-base;
|
||||
border-bottom-left-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
&-disabled-cell-last-of-row &-value {
|
||||
border-top-right-radius: @border-radius-base;
|
||||
border-bottom-right-radius: @border-radius-base;
|
||||
}
|
||||
|
||||
&-last-month-cell &-value,
|
||||
&-next-month-btn-day &-value {
|
||||
color: @disabled-color;
|
||||
}
|
||||
|
||||
&-month-panel-table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
border-collapse: separate;
|
||||
}
|
||||
|
||||
&-content {
|
||||
position: absolute;
|
||||
bottom: -9px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-fullscreen {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
&-fullscreen &-table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
&-fullscreen &-header {
|
||||
.@{ant-prefix}-radio-group {
|
||||
margin-left: 16px;
|
||||
}
|
||||
label.@{ant-prefix}-radio-button {
|
||||
height: @input-height-base;
|
||||
line-height: @input-height-base - 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&-fullscreen &-month,
|
||||
&-fullscreen &-date {
|
||||
display: block;
|
||||
height: 116px;
|
||||
margin: 0 4px;
|
||||
padding: 4px 8px;
|
||||
color: @text-color;
|
||||
text-align: left;
|
||||
border-top: 2px solid @border-color-split;
|
||||
transition: background 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: @item-hover-bg;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: @primary-2;
|
||||
}
|
||||
}
|
||||
|
||||
&-fullscreen &-column-header {
|
||||
padding-right: 12px;
|
||||
padding-bottom: 5px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&-fullscreen &-value {
|
||||
width: auto;
|
||||
text-align: right;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&-fullscreen &-today &-value {
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
&-fullscreen &-month-panel-current-cell &-month,
|
||||
&-fullscreen &-today &-date {
|
||||
background: transparent;
|
||||
border-top-color: @primary-color;
|
||||
}
|
||||
|
||||
&-fullscreen &-month-panel-current-cell &-value,
|
||||
&-fullscreen &-today &-value {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&-fullscreen &-month-panel-selected-cell &-month,
|
||||
&-fullscreen &-selected-day &-date {
|
||||
background: @primary-1;
|
||||
}
|
||||
|
||||
&-fullscreen &-month-panel-selected-cell &-value,
|
||||
&-fullscreen &-selected-day &-value {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
&-fullscreen &-last-month-cell &-date,
|
||||
&-fullscreen &-next-month-btn-day &-date {
|
||||
color: @disabled-color;
|
||||
}
|
||||
|
||||
&-fullscreen &-content {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: 88px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&-disabled-cell &-date {
|
||||
&,
|
||||
&:hover {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
&-disabled-cell:not(&-today) &-date {
|
||||
&,
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&-disabled-cell &-value {
|
||||
width: auto;
|
||||
color: @disabled-color;
|
||||
border-top: @border-width-base @border-style-base @border-color-split;
|
||||
border-radius: 0;
|
||||
cursor: not-allowed;
|
||||
|
||||
.@{calendar-picker-prefix-cls}-month-panel,
|
||||
.@{calendar-picker-prefix-cls}-date-panel {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.@{calendar-picker-prefix-cls}-body {
|
||||
padding: @padding-xs @padding-sm;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-picker-prefix-cls}-panel {
|
||||
.@{calendar-picker-prefix-cls}-content {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// ========================== Mini ==========================
|
||||
&-mini {
|
||||
.@{calendar-picker-prefix-cls}-content {
|
||||
height: 256px;
|
||||
|
||||
th {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========================== Full ==========================
|
||||
&-full {
|
||||
.@{calendar-prefix-cls}-header {
|
||||
.@{calendar-prefix-cls}-year-select {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-month-select {
|
||||
width: 80px;
|
||||
margin-left: @padding-xs;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-mode-switch {
|
||||
margin-left: @padding-md;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-picker-prefix-cls}-panel {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
border: 0;
|
||||
|
||||
.@{calendar-picker-prefix-cls}-body {
|
||||
th,
|
||||
td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
th {
|
||||
height: auto;
|
||||
padding: 0 12px 5px 0;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
// Cell
|
||||
.@{calendar-picker-prefix-cls}-cell {
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.@{calendar-prefix-cls}-date {
|
||||
background: @item-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-date-today::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-selected,
|
||||
&-selected:hover {
|
||||
.@{calendar-prefix-cls}-date,
|
||||
.@{calendar-prefix-cls}-date-today {
|
||||
background: @calendar-item-active-bg;
|
||||
|
||||
.@{calendar-prefix-cls}-date-value {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cell date
|
||||
.@{calendar-prefix-cls}-date {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin: 0 @padding-xs / 2;
|
||||
padding: @padding-xs / 2 @padding-xs 0;
|
||||
border: 0;
|
||||
border-top: 2px solid @border-color-split;
|
||||
border-radius: 0;
|
||||
transition: background 0.3s;
|
||||
|
||||
&-value {
|
||||
line-height: 24px;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
&-content {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: 86px;
|
||||
overflow-y: auto;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
|
||||
&-today {
|
||||
border-color: @primary-color;
|
||||
|
||||
.@{calendar-prefix-cls}-date-value {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,20 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { CalendarOutlined } from '@ant-design/icons';
|
||||
|
||||
export default function InputIcon(props: { suffixIcon: React.ReactNode; prefixCls: string }) {
|
||||
const { suffixIcon, prefixCls } = props;
|
||||
return (
|
||||
(suffixIcon &&
|
||||
(React.isValidElement<{ className?: string }>(suffixIcon) ? (
|
||||
React.cloneElement(suffixIcon, {
|
||||
className: classNames({
|
||||
[suffixIcon.props.className!]: suffixIcon.props.className,
|
||||
[`${prefixCls}-picker-icon`]: true,
|
||||
}),
|
||||
})
|
||||
) : (
|
||||
<span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>
|
||||
))) || <CalendarOutlined className={`${prefixCls}-picker-icon`} />
|
||||
);
|
||||
}
|
6
components/date-picker/PickerButton.tsx
Normal file
6
components/date-picker/PickerButton.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import Button, { ButtonProps } from '../button';
|
||||
|
||||
export default function PickerButton(props: ButtonProps) {
|
||||
return <Button size="small" type="primary" {...props} />;
|
||||
}
|
6
components/date-picker/PickerTag.tsx
Normal file
6
components/date-picker/PickerTag.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import Tag, { TagProps } from '../tag';
|
||||
|
||||
export default function PickerButton(props: TagProps) {
|
||||
return <Tag color="blue" {...props} />;
|
||||
}
|
@ -1,444 +0,0 @@
|
||||
/* tslint:disable jsx-no-multiline-js */
|
||||
import * as React from 'react';
|
||||
import * as moment from 'moment';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import RangeCalendar from 'rc-calendar/lib/RangeCalendar';
|
||||
import RcDatePicker from 'rc-calendar/lib/Picker';
|
||||
import classNames from 'classnames';
|
||||
import shallowequal from 'shallowequal';
|
||||
import { CloseCircleFilled } from '@ant-design/icons';
|
||||
|
||||
import Tag from '../tag';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
import interopDefault from '../_util/interopDefault';
|
||||
import { RangePickerValue, RangePickerPresetRange, RangePickerProps } from './interface';
|
||||
import { formatDate } from './utils';
|
||||
import InputIcon from './InputIcon';
|
||||
|
||||
export interface RangePickerState {
|
||||
value?: RangePickerValue;
|
||||
showDate?: RangePickerValue;
|
||||
open?: boolean;
|
||||
hoverValue?: RangePickerValue;
|
||||
}
|
||||
|
||||
function getShowDateFromValue(value: RangePickerValue, mode?: string | string[]) {
|
||||
const [start, end] = value;
|
||||
// value could be an empty array, then we should not reset showDate
|
||||
if (!start && !end) {
|
||||
return;
|
||||
}
|
||||
if (mode && mode[0] === 'month') {
|
||||
return [start, end] as RangePickerValue;
|
||||
}
|
||||
const newEnd = end && end.isSame(start, 'month') ? end.clone().add(1, 'month') : end;
|
||||
return [start, newEnd] as RangePickerValue;
|
||||
}
|
||||
|
||||
function pickerValueAdapter(
|
||||
value?: moment.Moment | RangePickerValue,
|
||||
): RangePickerValue | undefined {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
return [value, value.clone().add(1, 'month')];
|
||||
}
|
||||
|
||||
function isEmptyArray(arr: any) {
|
||||
if (Array.isArray(arr)) {
|
||||
return arr.length === 0 || arr.every(i => !i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function fixLocale(value: RangePickerValue | undefined, localeCode: string | undefined) {
|
||||
if (!localeCode) {
|
||||
return;
|
||||
}
|
||||
if (!value || value.length === 0) {
|
||||
return;
|
||||
}
|
||||
const [start, end] = value;
|
||||
if (start) {
|
||||
start!.locale(localeCode);
|
||||
}
|
||||
if (end) {
|
||||
end!.locale(localeCode);
|
||||
}
|
||||
}
|
||||
|
||||
class RangePicker extends React.Component<RangePickerProps, RangePickerState> {
|
||||
static defaultProps = {
|
||||
allowClear: true,
|
||||
showToday: false,
|
||||
separator: '~',
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps: RangePickerProps, prevState: RangePickerState) {
|
||||
let state = null;
|
||||
if ('value' in nextProps) {
|
||||
const value = nextProps.value || [];
|
||||
state = {
|
||||
value,
|
||||
};
|
||||
if (!shallowequal(nextProps.value, prevState.value)) {
|
||||
state = {
|
||||
...state,
|
||||
showDate: getShowDateFromValue(value, nextProps.mode) || prevState.showDate,
|
||||
};
|
||||
}
|
||||
}
|
||||
if ('open' in nextProps && prevState.open !== nextProps.open) {
|
||||
state = {
|
||||
...state,
|
||||
open: nextProps.open,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
private picker: HTMLSpanElement;
|
||||
|
||||
private prefixCls?: string;
|
||||
|
||||
private tagPrefixCls?: string;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
const value = props.value || props.defaultValue || [];
|
||||
const [start, end] = value;
|
||||
if (
|
||||
(start && !interopDefault(moment).isMoment(start)) ||
|
||||
(end && !interopDefault(moment).isMoment(end))
|
||||
) {
|
||||
throw new Error(
|
||||
'The value/defaultValue of RangePicker must be a moment object array after `antd@2.0`, ' +
|
||||
'see: https://u.ant.design/date-picker-value',
|
||||
);
|
||||
}
|
||||
const pickerValue = !value || isEmptyArray(value) ? props.defaultPickerValue : value;
|
||||
this.state = {
|
||||
value,
|
||||
showDate: pickerValueAdapter(pickerValue || interopDefault(moment)()),
|
||||
open: props.open,
|
||||
hoverValue: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(_: any, prevState: RangePickerState) {
|
||||
if (!('open' in this.props) && prevState.open && !this.state.open) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
|
||||
setValue(value: RangePickerValue, hidePanel?: boolean) {
|
||||
this.handleChange(value);
|
||||
if ((hidePanel || !this.props.showTime) && !('open' in this.props)) {
|
||||
this.setState({ open: false });
|
||||
}
|
||||
}
|
||||
|
||||
savePicker = (node: HTMLSpanElement) => {
|
||||
this.picker = node;
|
||||
};
|
||||
|
||||
clearSelection = (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.setState({ value: [] });
|
||||
this.handleChange([]);
|
||||
};
|
||||
|
||||
clearHoverValue = () => this.setState({ hoverValue: [] });
|
||||
|
||||
handleChange = (value: RangePickerValue) => {
|
||||
const { props } = this;
|
||||
if (!('value' in props)) {
|
||||
this.setState(({ showDate }) => ({
|
||||
value,
|
||||
showDate: getShowDateFromValue(value) || showDate,
|
||||
}));
|
||||
}
|
||||
if (value[0] && value[0].diff(value[1]) > 0) {
|
||||
value[1] = undefined;
|
||||
}
|
||||
const [start, end] = value;
|
||||
if (typeof props.onChange === 'function') {
|
||||
props.onChange(value, [formatDate(start, props.format), formatDate(end, props.format)]);
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenChange = (open: boolean) => {
|
||||
if (!('open' in this.props)) {
|
||||
this.setState({ open });
|
||||
}
|
||||
|
||||
if (open === false) {
|
||||
this.clearHoverValue();
|
||||
}
|
||||
|
||||
const { onOpenChange } = this.props;
|
||||
if (onOpenChange) {
|
||||
onOpenChange(open);
|
||||
}
|
||||
};
|
||||
|
||||
handleShowDateChange = (showDate: RangePickerValue) => this.setState({ showDate });
|
||||
|
||||
handleHoverChange = (hoverValue: any) => this.setState({ hoverValue });
|
||||
|
||||
handleRangeMouseLeave = () => {
|
||||
if (this.state.open) {
|
||||
this.clearHoverValue();
|
||||
}
|
||||
};
|
||||
|
||||
handleCalendarInputSelect = (value: RangePickerValue) => {
|
||||
const [start] = value;
|
||||
if (!start) {
|
||||
return;
|
||||
}
|
||||
this.setState(({ showDate }) => ({
|
||||
value,
|
||||
showDate: getShowDateFromValue(value) || showDate,
|
||||
}));
|
||||
};
|
||||
|
||||
handleRangeClick = (value: RangePickerPresetRange) => {
|
||||
if (typeof value === 'function') {
|
||||
value = value();
|
||||
}
|
||||
|
||||
this.setValue(value, true);
|
||||
|
||||
const { onOk, onOpenChange } = this.props;
|
||||
if (onOk) {
|
||||
onOk(value);
|
||||
}
|
||||
|
||||
if (onOpenChange) {
|
||||
onOpenChange(false);
|
||||
}
|
||||
};
|
||||
|
||||
focus() {
|
||||
this.picker.focus();
|
||||
}
|
||||
|
||||
blur() {
|
||||
this.picker.blur();
|
||||
}
|
||||
|
||||
renderFooter = () => {
|
||||
const { ranges, renderExtraFooter } = this.props;
|
||||
const { prefixCls, tagPrefixCls } = this;
|
||||
if (!ranges && !renderExtraFooter) {
|
||||
return null;
|
||||
}
|
||||
const customFooter = renderExtraFooter ? (
|
||||
<div className={`${prefixCls}-footer-extra`} key="extra">
|
||||
{renderExtraFooter()}
|
||||
</div>
|
||||
) : null;
|
||||
const operations =
|
||||
ranges &&
|
||||
Object.keys(ranges).map(range => {
|
||||
const value = ranges[range];
|
||||
const hoverValue = typeof value === 'function' ? value.call(this) : value;
|
||||
return (
|
||||
<Tag
|
||||
key={range}
|
||||
prefixCls={tagPrefixCls}
|
||||
color="blue"
|
||||
onClick={() => this.handleRangeClick(value)}
|
||||
onMouseEnter={() => this.setState({ hoverValue })}
|
||||
onMouseLeave={this.handleRangeMouseLeave}
|
||||
>
|
||||
{range}
|
||||
</Tag>
|
||||
);
|
||||
});
|
||||
const rangeNode =
|
||||
operations && operations.length > 0 ? (
|
||||
<div className={`${prefixCls}-footer-extra ${prefixCls}-range-quick-selector`} key="range">
|
||||
{operations}
|
||||
</div>
|
||||
) : null;
|
||||
return [rangeNode, customFooter];
|
||||
};
|
||||
|
||||
renderRangePicker = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const { state, props } = this;
|
||||
const { value, showDate, hoverValue, open } = state;
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
tagPrefixCls: customizeTagPrefixCls,
|
||||
popupStyle,
|
||||
style,
|
||||
disabledDate,
|
||||
disabledTime,
|
||||
showTime,
|
||||
showToday,
|
||||
ranges,
|
||||
onOk,
|
||||
locale,
|
||||
// @ts-ignore
|
||||
localeCode,
|
||||
format,
|
||||
dateRender,
|
||||
onCalendarChange,
|
||||
suffixIcon,
|
||||
separator,
|
||||
} = props;
|
||||
|
||||
const prefixCls = getPrefixCls('calendar', customizePrefixCls);
|
||||
const tagPrefixCls = getPrefixCls('tag', customizeTagPrefixCls);
|
||||
// To support old version react.
|
||||
// Have to add prefixCls on the instance.
|
||||
// https://github.com/facebook/react/issues/12397
|
||||
this.prefixCls = prefixCls;
|
||||
this.tagPrefixCls = tagPrefixCls;
|
||||
|
||||
fixLocale(value, localeCode);
|
||||
fixLocale(showDate, localeCode);
|
||||
|
||||
warning(
|
||||
!('onOK' in props),
|
||||
'RangePicker',
|
||||
'It should be `RangePicker[onOk]`, instead of `onOK`!',
|
||||
);
|
||||
|
||||
const calendarClassName = classNames({
|
||||
[`${prefixCls}-time`]: showTime,
|
||||
[`${prefixCls}-range-with-ranges`]: ranges,
|
||||
});
|
||||
|
||||
// 需要选择时间时,点击 ok 时才触发 onChange
|
||||
const pickerChangeHandler = {
|
||||
onChange: this.handleChange,
|
||||
};
|
||||
let calendarProps: any = {
|
||||
onOk: this.handleChange,
|
||||
};
|
||||
if (props.timePicker) {
|
||||
pickerChangeHandler.onChange = changedValue => this.handleChange(changedValue);
|
||||
} else {
|
||||
calendarProps = {};
|
||||
}
|
||||
if ('mode' in props) {
|
||||
calendarProps.mode = props.mode;
|
||||
}
|
||||
|
||||
const startPlaceholder = Array.isArray(props.placeholder)
|
||||
? props.placeholder[0]
|
||||
: locale.lang.rangePlaceholder[0];
|
||||
const endPlaceholder = Array.isArray(props.placeholder)
|
||||
? props.placeholder[1]
|
||||
: locale.lang.rangePlaceholder[1];
|
||||
|
||||
const calendar = (
|
||||
<RangeCalendar
|
||||
{...calendarProps}
|
||||
seperator={separator}
|
||||
onChange={onCalendarChange}
|
||||
format={format}
|
||||
prefixCls={prefixCls}
|
||||
className={calendarClassName}
|
||||
renderFooter={this.renderFooter}
|
||||
timePicker={props.timePicker}
|
||||
disabledDate={disabledDate}
|
||||
disabledTime={disabledTime}
|
||||
dateInputPlaceholder={[startPlaceholder, endPlaceholder]}
|
||||
locale={locale.lang}
|
||||
onOk={onOk}
|
||||
dateRender={dateRender}
|
||||
value={showDate}
|
||||
onValueChange={this.handleShowDateChange}
|
||||
hoverValue={hoverValue}
|
||||
onHoverChange={this.handleHoverChange}
|
||||
onPanelChange={props.onPanelChange}
|
||||
showToday={showToday}
|
||||
onInputSelect={this.handleCalendarInputSelect}
|
||||
/>
|
||||
);
|
||||
|
||||
// default width for showTime
|
||||
const pickerStyle = {} as any;
|
||||
if (props.showTime) {
|
||||
pickerStyle.width = (style && style.width) || 350;
|
||||
}
|
||||
const [startValue, endValue] = value as RangePickerValue;
|
||||
const clearIcon =
|
||||
!props.disabled && props.allowClear && value && (startValue || endValue) ? (
|
||||
<CloseCircleFilled className={`${prefixCls}-picker-clear`} onClick={this.clearSelection} />
|
||||
) : null;
|
||||
|
||||
const inputIcon = <InputIcon suffixIcon={suffixIcon} prefixCls={prefixCls} />;
|
||||
|
||||
const input = ({ value: inputValue }: { value: any }) => {
|
||||
const [start, end] = inputValue;
|
||||
return (
|
||||
<span className={props.pickerInputClass}>
|
||||
<input
|
||||
disabled={props.disabled}
|
||||
readOnly
|
||||
value={formatDate(start, props.format)}
|
||||
placeholder={startPlaceholder}
|
||||
className={`${prefixCls}-range-picker-input`}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<span className={`${prefixCls}-range-picker-separator`}> {separator} </span>
|
||||
<input
|
||||
disabled={props.disabled}
|
||||
readOnly
|
||||
value={formatDate(end, props.format)}
|
||||
placeholder={endPlaceholder}
|
||||
className={`${prefixCls}-range-picker-input`}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
{clearIcon}
|
||||
{inputIcon}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<span
|
||||
ref={this.savePicker}
|
||||
id={typeof props.id === 'number' ? props.id.toString() : props.id}
|
||||
className={classNames(props.className, props.pickerClass)}
|
||||
style={{ ...style, ...pickerStyle }}
|
||||
tabIndex={props.disabled ? -1 : 0}
|
||||
onFocus={props.onFocus}
|
||||
onBlur={props.onBlur}
|
||||
onMouseEnter={props.onMouseEnter}
|
||||
onMouseLeave={props.onMouseLeave}
|
||||
>
|
||||
<RcDatePicker
|
||||
{...props}
|
||||
{...pickerChangeHandler}
|
||||
calendar={calendar}
|
||||
value={value}
|
||||
open={open}
|
||||
onOpenChange={this.handleOpenChange}
|
||||
prefixCls={`${prefixCls}-picker-container`}
|
||||
style={popupStyle}
|
||||
>
|
||||
{input}
|
||||
</RcDatePicker>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderRangePicker}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
|
||||
polyfill(RangePicker);
|
||||
|
||||
export default RangePicker;
|
@ -1,226 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import * as moment from 'moment';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import Calendar from 'rc-calendar';
|
||||
import RcDatePicker from 'rc-calendar/lib/Picker';
|
||||
import classNames from 'classnames';
|
||||
import { CloseCircleFilled } from '@ant-design/icons';
|
||||
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import interopDefault from '../_util/interopDefault';
|
||||
import InputIcon from './InputIcon';
|
||||
|
||||
function formatValue(value: moment.Moment | null, format: string): string {
|
||||
return (value && value.format(format)) || '';
|
||||
}
|
||||
|
||||
interface WeekPickerState {
|
||||
open: boolean;
|
||||
value: moment.Moment | null;
|
||||
}
|
||||
|
||||
class WeekPicker extends React.Component<any, WeekPickerState> {
|
||||
static defaultProps = {
|
||||
format: 'gggg-wo',
|
||||
allowClear: true,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps: any) {
|
||||
if ('value' in nextProps || 'open' in nextProps) {
|
||||
const state = {} as WeekPickerState;
|
||||
if ('value' in nextProps) {
|
||||
state.value = nextProps.value;
|
||||
}
|
||||
if ('open' in nextProps) {
|
||||
state.open = nextProps.open;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private input: any;
|
||||
|
||||
private prefixCls?: string;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
const value = props.value || props.defaultValue;
|
||||
if (value && !interopDefault(moment).isMoment(value)) {
|
||||
throw new Error(
|
||||
'The value/defaultValue of WeekPicker must be ' +
|
||||
'a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value',
|
||||
);
|
||||
}
|
||||
this.state = {
|
||||
value,
|
||||
open: props.open,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(_: any, prevState: WeekPickerState) {
|
||||
if (!('open' in this.props) && prevState.open && !this.state.open) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
|
||||
saveInput = (node: any) => {
|
||||
this.input = node;
|
||||
};
|
||||
|
||||
weekDateRender = (current: any) => {
|
||||
const selectedValue = this.state.value;
|
||||
const { prefixCls } = this;
|
||||
const { dateRender } = this.props;
|
||||
const dateNode = dateRender ? dateRender(current) : current.date();
|
||||
if (
|
||||
selectedValue &&
|
||||
current.year() === selectedValue.year() &&
|
||||
current.week() === selectedValue.week()
|
||||
) {
|
||||
return (
|
||||
<div className={`${prefixCls}-selected-day`}>
|
||||
<div className={`${prefixCls}-date`}>{dateNode}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <div className={`${prefixCls}-date`}>{dateNode}</div>;
|
||||
};
|
||||
|
||||
handleChange = (value: moment.Moment | null) => {
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ value });
|
||||
}
|
||||
this.props.onChange(value, formatValue(value, this.props.format));
|
||||
};
|
||||
|
||||
handleOpenChange = (open: boolean) => {
|
||||
const { onOpenChange } = this.props;
|
||||
if (!('open' in this.props)) {
|
||||
this.setState({ open });
|
||||
}
|
||||
|
||||
if (onOpenChange) {
|
||||
onOpenChange(open);
|
||||
}
|
||||
};
|
||||
|
||||
clearSelection = (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.handleChange(null);
|
||||
};
|
||||
|
||||
focus() {
|
||||
this.input.focus();
|
||||
}
|
||||
|
||||
blur() {
|
||||
this.input.blur();
|
||||
}
|
||||
|
||||
renderFooter = (...args: any[]) => {
|
||||
const { prefixCls, renderExtraFooter } = this.props;
|
||||
return renderExtraFooter ? (
|
||||
<div className={`${prefixCls}-footer-extra`}>{renderExtraFooter(...args)}</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
renderWeekPicker = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
disabled,
|
||||
pickerClass,
|
||||
popupStyle,
|
||||
pickerInputClass,
|
||||
format,
|
||||
allowClear,
|
||||
locale,
|
||||
localeCode,
|
||||
disabledDate,
|
||||
style,
|
||||
onFocus,
|
||||
onBlur,
|
||||
id,
|
||||
suffixIcon,
|
||||
defaultPickerValue,
|
||||
} = this.props;
|
||||
|
||||
const prefixCls = getPrefixCls('calendar', customizePrefixCls);
|
||||
// To support old version react.
|
||||
// Have to add prefixCls on the instance.
|
||||
// https://github.com/facebook/react/issues/12397
|
||||
this.prefixCls = prefixCls;
|
||||
|
||||
const { open, value: pickerValue } = this.state;
|
||||
if (pickerValue && localeCode) {
|
||||
pickerValue.locale(localeCode);
|
||||
}
|
||||
|
||||
const placeholder =
|
||||
'placeholder' in this.props ? this.props.placeholder : locale.lang.placeholder;
|
||||
|
||||
const calendar = (
|
||||
<Calendar
|
||||
showWeekNumber
|
||||
dateRender={this.weekDateRender}
|
||||
prefixCls={prefixCls}
|
||||
format={format}
|
||||
locale={locale.lang}
|
||||
showDateInput={false}
|
||||
showToday={false}
|
||||
disabledDate={disabledDate}
|
||||
renderFooter={this.renderFooter}
|
||||
defaultValue={defaultPickerValue}
|
||||
/>
|
||||
);
|
||||
const clearIcon =
|
||||
!disabled && allowClear && this.state.value ? (
|
||||
<CloseCircleFilled className={`${prefixCls}-picker-clear`} onClick={this.clearSelection} />
|
||||
) : null;
|
||||
|
||||
const inputIcon = <InputIcon suffixIcon={suffixIcon} prefixCls={prefixCls} />;
|
||||
|
||||
const input = ({ value }: { value: moment.Moment | undefined }) => (
|
||||
<span style={{ display: 'inline-block', width: '100%' }}>
|
||||
<input
|
||||
ref={this.saveInput}
|
||||
disabled={disabled}
|
||||
readOnly
|
||||
value={(value && value.format(format)) || ''}
|
||||
placeholder={placeholder}
|
||||
className={pickerInputClass}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
{clearIcon}
|
||||
{inputIcon}
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<span className={classNames(className, pickerClass)} style={style} id={id}>
|
||||
<RcDatePicker
|
||||
{...this.props}
|
||||
calendar={calendar}
|
||||
prefixCls={`${prefixCls}-picker-container`}
|
||||
value={pickerValue}
|
||||
onChange={this.handleChange}
|
||||
open={open}
|
||||
onOpenChange={this.handleOpenChange}
|
||||
style={popupStyle}
|
||||
>
|
||||
{input}
|
||||
</RcDatePicker>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderWeekPicker}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
|
||||
polyfill(WeekPicker);
|
||||
|
||||
export default WeekPicker;
|
@ -3,13 +3,12 @@ import { mount } from 'enzyme';
|
||||
import moment from 'moment';
|
||||
import MockDate from 'mockdate';
|
||||
import DatePicker from '..';
|
||||
import { selectDate, openPanel, clearInput, nextYear, nextMonth, hasSelected } from './utils';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
|
||||
describe('DatePicker', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
focusTest(DatePicker);
|
||||
focusTest(DatePicker, true);
|
||||
|
||||
beforeEach(() => {
|
||||
MockDate.set(moment('2016-11-22'));
|
||||
@ -24,14 +23,10 @@ describe('DatePicker', () => {
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('support name prop', () => {
|
||||
const wrapper = mount(<DatePicker name="bamboo" />);
|
||||
expect(wrapper.find('input').props().name).toBe('bamboo');
|
||||
});
|
||||
|
||||
it('prop locale should works', () => {
|
||||
const locale = {
|
||||
lang: {
|
||||
locale: 'mk',
|
||||
placeholder: 'Избери дата',
|
||||
rangePlaceholder: ['Начална дата', 'Крайна дата'],
|
||||
today: 'Днес',
|
||||
@ -69,172 +64,13 @@ describe('DatePicker', () => {
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// Fix https://github.com/ant-design/ant-design/issues/8885
|
||||
it('control value after panel closed', () => {
|
||||
class Test extends React.Component {
|
||||
state = {
|
||||
cleared: false,
|
||||
value: moment(),
|
||||
};
|
||||
|
||||
onChange = value => {
|
||||
let { cleared } = this.state;
|
||||
|
||||
let newValue = value;
|
||||
if (cleared) {
|
||||
newValue = moment(moment(value).format('YYYY-MM-DD 12:12:12'));
|
||||
cleared = false;
|
||||
}
|
||||
|
||||
if (!newValue) {
|
||||
cleared = true;
|
||||
}
|
||||
|
||||
this.setState({ value: newValue, cleared });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value } = this.state;
|
||||
return (
|
||||
<DatePicker
|
||||
showTime
|
||||
value={value}
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const wrapper = mount(<Test />);
|
||||
// clear input
|
||||
clearInput(wrapper);
|
||||
openPanel(wrapper);
|
||||
selectDate(wrapper, moment('2016-11-13'));
|
||||
expect(wrapper.find('.ant-calendar-input').getDOMNode().value).toBe('2016-11-13 12:12:12');
|
||||
selectDate(wrapper, moment('2016-11-14'));
|
||||
expect(wrapper.find('.ant-calendar-input').getDOMNode().value).toBe('2016-11-14 12:12:12');
|
||||
});
|
||||
|
||||
it('triggers onChange only when date was selected', () => {
|
||||
const handleChange = jest.fn();
|
||||
const wrapper = mount(<DatePicker onChange={handleChange} />);
|
||||
openPanel(wrapper);
|
||||
nextYear(wrapper);
|
||||
expect(handleChange).not.toHaveBeenCalled();
|
||||
nextMonth(wrapper);
|
||||
expect(handleChange).not.toHaveBeenCalled();
|
||||
selectDate(wrapper, moment('2017-12-22'));
|
||||
expect(handleChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('clear input', () => {
|
||||
const wrapper = mount(<DatePicker />);
|
||||
openPanel(wrapper);
|
||||
selectDate(wrapper, moment('2016-11-23'));
|
||||
clearInput(wrapper);
|
||||
openPanel(wrapper);
|
||||
expect(hasSelected(wrapper, moment('2016-11-22'))).toBe(true);
|
||||
});
|
||||
|
||||
it('sets data attributes on input', () => {
|
||||
const wrapper = mount(<DatePicker data-test="test-id" data-id="12345" />);
|
||||
const input = wrapper.find('.ant-calendar-picker-input').getDOMNode();
|
||||
expect(input.getAttribute('data-test')).toBe('test-id');
|
||||
expect(input.getAttribute('data-id')).toBe('12345');
|
||||
});
|
||||
|
||||
it('sets aria attributes on input', () => {
|
||||
const wrapper = mount(<DatePicker aria-label="some-label" aria-labelledby="label-id" />);
|
||||
const input = wrapper.find('.ant-calendar-picker-input').getDOMNode();
|
||||
expect(input.getAttribute('aria-label')).toBe('some-label');
|
||||
expect(input.getAttribute('aria-labelledby')).toBe('label-id');
|
||||
});
|
||||
|
||||
it('sets role attribute on input', () => {
|
||||
const wrapper = mount(<DatePicker role="search" />);
|
||||
const input = wrapper.find('.ant-calendar-picker-input').getDOMNode();
|
||||
expect(input.getAttribute('role')).toBe('search');
|
||||
});
|
||||
|
||||
it('changes year/month when under control', () => {
|
||||
const wrapper = mount(<DatePicker value={moment('2018-07-01')} />);
|
||||
openPanel(wrapper);
|
||||
expect(wrapper.find('.ant-calendar-my-select').text()).toBe('Jul2018');
|
||||
wrapper.find('.ant-calendar-prev-year-btn').simulate('click');
|
||||
wrapper.find('.ant-calendar-prev-month-btn').simulate('click');
|
||||
expect(wrapper.find('.ant-calendar-my-select').text()).toBe('Jun2017');
|
||||
});
|
||||
|
||||
it('disabled date', () => {
|
||||
function disabledDate(current) {
|
||||
return current && current < moment().endOf('day');
|
||||
}
|
||||
|
||||
const wrapper = mount(<DatePicker disabledDate={disabledDate} />);
|
||||
const wrapper = mount(<DatePicker disabledDate={disabledDate} open />);
|
||||
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('extra footer works', () => {
|
||||
const wrapper = mount(
|
||||
<DatePicker renderExtraFooter={mode => <span className="extra-node">{mode}</span>} />,
|
||||
);
|
||||
openPanel(wrapper);
|
||||
|
||||
let extraNode = wrapper.find('.extra-node');
|
||||
expect(extraNode.length).toBe(1);
|
||||
expect(extraNode.text()).toBe('date');
|
||||
|
||||
wrapper
|
||||
.find('.ant-calendar-month-select')
|
||||
.hostNodes()
|
||||
.simulate('click');
|
||||
extraNode = wrapper.find('.ant-calendar-month-panel .extra-node');
|
||||
expect(extraNode.length).toBe(1);
|
||||
expect(extraNode.text()).toBe('month');
|
||||
|
||||
wrapper
|
||||
.find('.ant-calendar-year-select')
|
||||
.hostNodes()
|
||||
.simulate('click');
|
||||
extraNode = wrapper.find('.ant-calendar-year-panel .extra-node');
|
||||
expect(extraNode.length).toBe(1);
|
||||
expect(extraNode.text()).toBe('year');
|
||||
|
||||
wrapper
|
||||
.find('.ant-calendar-year-panel-decade-select')
|
||||
.hostNodes()
|
||||
.simulate('click');
|
||||
extraNode = wrapper.find('.ant-calendar-decade-panel .extra-node');
|
||||
expect(extraNode.length).toBe(1);
|
||||
expect(extraNode.text()).toBe('decade');
|
||||
});
|
||||
|
||||
it('supports multiple formats', () => {
|
||||
const wrapper = mount(<DatePicker format={['DD/MM/YYYY', 'DD/MM/YY']} />);
|
||||
openPanel(wrapper);
|
||||
wrapper.find('.ant-calendar-input').simulate('change', { target: { value: '02/07/18' } });
|
||||
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode().value).toBe('02/07/2018');
|
||||
});
|
||||
|
||||
describe('warning use if use invalidate moment', () => {
|
||||
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const invalidateTime = moment('I AM INVALIDATE');
|
||||
warnSpy.mockReset();
|
||||
|
||||
it('defaultValue', () => {
|
||||
mount(<DatePicker defaultValue={invalidateTime} />);
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: DatePicker] `defaultValue` provides invalidate moment time. If you want to set empty value, use `null` instead.',
|
||||
);
|
||||
});
|
||||
|
||||
it('value', () => {
|
||||
mount(<DatePicker value={invalidateTime} />);
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: DatePicker] `value` provides invalidate moment time. If you want to set empty value, use `null` instead.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import moment from 'moment';
|
||||
import DatePicker from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import { openPanel } from './utils';
|
||||
|
||||
const { MonthPicker } = DatePicker;
|
||||
|
||||
describe('MonthPicker', () => {
|
||||
focusTest(MonthPicker);
|
||||
|
||||
it('reset select item when popup close', () => {
|
||||
const wrapper = mount(<MonthPicker value={moment('2018-07-01')} />);
|
||||
openPanel(wrapper);
|
||||
wrapper
|
||||
.find('.ant-calendar-month-panel-month')
|
||||
.first()
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('.ant-calendar-month-panel-cell')
|
||||
.at(6)
|
||||
.hasClass('ant-calendar-month-panel-selected-cell');
|
||||
});
|
||||
});
|
@ -1,15 +1,15 @@
|
||||
import React from 'react';
|
||||
import { mount, render } from 'enzyme';
|
||||
import { mount } from 'enzyme';
|
||||
import moment from 'moment';
|
||||
import DatePicker from '..';
|
||||
import { setMockDate, resetMockDate } from '../../../tests/utils';
|
||||
import { selectDate, openPanel } from './utils';
|
||||
import { openPicker, selectCell, closePicker } from './utils';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
describe('RangePicker', () => {
|
||||
focusTest(RangePicker);
|
||||
focusTest(RangePicker, true);
|
||||
|
||||
beforeEach(() => {
|
||||
setMockDate();
|
||||
@ -19,300 +19,21 @@ describe('RangePicker', () => {
|
||||
resetMockDate();
|
||||
});
|
||||
|
||||
it('show month panel according to value', () => {
|
||||
const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn');
|
||||
const wrapper = mount(
|
||||
<RangePicker getCalendarContainer={trigger => trigger} format="YYYY/MM/DD" showTime open />,
|
||||
);
|
||||
|
||||
wrapper.setProps({ value: [birthday, birthday] });
|
||||
expect(
|
||||
render(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('switch to corresponding month panel when click presetted ranges', () => {
|
||||
const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn');
|
||||
const wrapper = mount(
|
||||
<RangePicker
|
||||
ranges={{
|
||||
'My Birthday': [birthday, birthday],
|
||||
}}
|
||||
getCalendarContainer={trigger => trigger}
|
||||
format="YYYY/MM/DD"
|
||||
showTime
|
||||
open
|
||||
/>,
|
||||
);
|
||||
|
||||
const rangeCalendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
|
||||
expect(
|
||||
render(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('highlight range when hover presetted range', () => {
|
||||
const wrapper = mount(
|
||||
<RangePicker
|
||||
ranges={{
|
||||
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
||||
}}
|
||||
getCalendarContainer={trigger => trigger}
|
||||
format="YYYY/MM/DD"
|
||||
open
|
||||
/>,
|
||||
);
|
||||
|
||||
let rangeCalendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector Tag').simulate('mouseEnter');
|
||||
rangeCalendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
expect(rangeCalendarWrapper.find('.ant-calendar-selected-day').length).toBe(2);
|
||||
});
|
||||
|
||||
it('should trigger onCalendarChange when change value', () => {
|
||||
const onCalendarChangeFn = jest.fn();
|
||||
const wrapper = mount(
|
||||
<RangePicker
|
||||
getCalendarContainer={trigger => trigger}
|
||||
onCalendarChange={onCalendarChangeFn}
|
||||
open
|
||||
/>,
|
||||
);
|
||||
const rangeCalendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
rangeCalendarWrapper
|
||||
.find('.ant-calendar-cell')
|
||||
.at(15)
|
||||
.simulate('click');
|
||||
expect(onCalendarChangeFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// issue: https://github.com/ant-design/ant-design/issues/5872
|
||||
it('should not throw error when value is reset to `[]`', () => {
|
||||
const birthday = moment('2000-01-01', 'YYYY-MM-DD');
|
||||
const wrapper = mount(
|
||||
<RangePicker getCalendarContainer={trigger => trigger} value={[birthday, birthday]} open />,
|
||||
);
|
||||
const wrapper = mount(<RangePicker value={[birthday, birthday]} open />);
|
||||
wrapper.setProps({ value: [] });
|
||||
const rangeCalendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
expect(() =>
|
||||
rangeCalendarWrapper
|
||||
.find('.ant-calendar-cell')
|
||||
.at(15)
|
||||
.simulate('click')
|
||||
.simulate('click'),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
// issue: https://github.com/ant-design/ant-design/issues/7077
|
||||
it('should not throw error when select after clear', () => {
|
||||
const wrapper = mount(<RangePicker getCalendarContainer={trigger => trigger} open />);
|
||||
let rangeCalendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
rangeCalendarWrapper
|
||||
.find('.ant-calendar-cell')
|
||||
.at(15)
|
||||
.simulate('click')
|
||||
.simulate('click');
|
||||
wrapper.update();
|
||||
wrapper
|
||||
.find('.ant-calendar-picker-clear')
|
||||
.hostNodes()
|
||||
.simulate('click');
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
rangeCalendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
expect(() =>
|
||||
rangeCalendarWrapper
|
||||
.find('.ant-calendar-cell')
|
||||
.at(15)
|
||||
.simulate('click')
|
||||
.simulate('click'),
|
||||
).not.toThrow();
|
||||
});
|
||||
expect(() => {
|
||||
openPicker(wrapper);
|
||||
selectCell(wrapper, 3);
|
||||
closePicker(wrapper);
|
||||
|
||||
it('clear hover value after panel close', () => {
|
||||
jest.useFakeTimers();
|
||||
const wrapper = mount(
|
||||
<div>
|
||||
<RangePicker value={[moment(), moment().add(2, 'day')]} />
|
||||
</div>,
|
||||
);
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
wrapper
|
||||
.find('.ant-calendar-cell')
|
||||
.at(25)
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('.ant-calendar-cell')
|
||||
.at(27)
|
||||
.simulate('mouseEnter');
|
||||
document.dispatchEvent(new MouseEvent('mousedown'));
|
||||
jest.runAllTimers();
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-cell')
|
||||
.at(23)
|
||||
.hasClass('ant-calendar-in-range-cell'),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
describe('preset range', () => {
|
||||
it('static range', () => {
|
||||
const range = [moment().subtract(2, 'd'), moment()];
|
||||
const format = 'YYYY-MM-DD HH:mm:ss';
|
||||
const wrapper = mount(<RangePicker ranges={{ 'recent two days': range }} format={format} />);
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-picker-input')
|
||||
.first()
|
||||
.getDOMNode().value,
|
||||
).toBe(range[0].format(format));
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-picker-input')
|
||||
.last()
|
||||
.getDOMNode().value,
|
||||
).toBe(range[1].format(format));
|
||||
});
|
||||
|
||||
it('function range', () => {
|
||||
const range = [moment().subtract(2, 'd'), moment()];
|
||||
const format = 'YYYY-MM-DD HH:mm:ss';
|
||||
const wrapper = mount(
|
||||
<RangePicker ranges={{ 'recent two days': () => range }} format={format} />,
|
||||
);
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-picker-input')
|
||||
.first()
|
||||
.getDOMNode().value,
|
||||
).toBe(range[0].format(format));
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-picker-input')
|
||||
.last()
|
||||
.getDOMNode().value,
|
||||
).toBe(range[1].format(format));
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/6999
|
||||
it('input date manually', () => {
|
||||
const wrapper = mount(<RangePicker open />);
|
||||
const dateString = '2008-12-31';
|
||||
const input = wrapper.find('.ant-calendar-input').first();
|
||||
input.simulate('change', { target: { value: dateString } });
|
||||
expect(input.getDOMNode().value).toBe(dateString);
|
||||
});
|
||||
|
||||
it('triggers onOk when click on preset range', () => {
|
||||
const handleOk = jest.fn();
|
||||
const range = [moment().subtract(2, 'd'), moment()];
|
||||
const wrapper = mount(<RangePicker ranges={{ 'recent two days': range }} onOk={handleOk} />);
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
|
||||
expect(handleOk).toHaveBeenCalledWith(range);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/9267
|
||||
it('invali end date not throw error', () => {
|
||||
const wrapper = mount(<RangePicker />);
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
selectDate(wrapper, moment('2017-09-18'), 0);
|
||||
selectDate(wrapper, moment('2017-10-18'), 1);
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
expect(() =>
|
||||
wrapper
|
||||
.find('.ant-calendar-input')
|
||||
.at(1)
|
||||
.simulate('change', { target: { value: '2016-01-01' } }),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('changes year/month when under control', () => {
|
||||
const wrapper = mount(<RangePicker value={[moment('2018-07-01'), moment('2018-07-02')]} />);
|
||||
openPanel(wrapper);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-my-select')
|
||||
.first()
|
||||
.text(),
|
||||
).toBe('Jul2018');
|
||||
wrapper
|
||||
.find('.ant-calendar-prev-year-btn')
|
||||
.first()
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('.ant-calendar-prev-month-btn')
|
||||
.first()
|
||||
.simulate('click');
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-my-select')
|
||||
.first()
|
||||
.text(),
|
||||
).toBe('Jun2017');
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/11631
|
||||
it('triggers onOpenChange when click on preset range', () => {
|
||||
const handleOpenChange = jest.fn();
|
||||
const range = [moment().subtract(2, 'd'), moment()];
|
||||
const wrapper = mount(
|
||||
<RangePicker onOpenChange={handleOpenChange} ranges={{ 'recent two days': range }} />,
|
||||
);
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
|
||||
expect(handleOpenChange).toHaveBeenCalledWith(false);
|
||||
openPicker(wrapper, 1);
|
||||
selectCell(wrapper, 5, 1);
|
||||
closePicker(wrapper, 1);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('customize separator', () => {
|
||||
@ -322,90 +43,36 @@ describe('RangePicker', () => {
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/13302
|
||||
describe('in "month" mode, when the left and right panels select the same month', () => {
|
||||
it('left panel and right panel could be the same month', () => {
|
||||
const wrapper = mount(<RangePicker mode={['month', 'month']} />);
|
||||
wrapper.setProps({ value: [moment(), moment()] });
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-picker-input')
|
||||
.first()
|
||||
.getDOMNode().value,
|
||||
).toBe(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-picker-input')
|
||||
.last()
|
||||
.getDOMNode().value,
|
||||
);
|
||||
});
|
||||
|
||||
it('the cell status is correct', () => {
|
||||
const wrapper = mount(<RangePicker mode={['month', 'month']} />);
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
wrapper
|
||||
.find('.ant-calendar-range-left .ant-calendar-month-panel-cell')
|
||||
.at(3)
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('.ant-calendar-range-right .ant-calendar-month-panel-cell')
|
||||
.at(3)
|
||||
.simulate('click');
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-left .ant-calendar-month-panel-cell')
|
||||
.at(3)
|
||||
.hasClass('ant-calendar-month-panel-selected-cell'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-left .ant-calendar-month-panel-cell')
|
||||
.at(3)
|
||||
.hasClass('ant-calendar-month-panel-selected-cell'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-left .ant-calendar-month-panel-cell')
|
||||
.at(2)
|
||||
.hasClass('ant-calendar-month-panel-cell-disabled'),
|
||||
).toBe(false);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-left .ant-calendar-month-panel-cell')
|
||||
.at(4)
|
||||
.hasClass('ant-calendar-month-panel-cell-disabled'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-right .ant-calendar-month-panel-cell')
|
||||
.at(2)
|
||||
.hasClass('ant-calendar-month-panel-cell-disabled'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-calendar-range-right .ant-calendar-month-panel-cell')
|
||||
.at(4)
|
||||
.hasClass('ant-calendar-month-panel-cell-disabled'),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
class Test extends React.Component {
|
||||
state = {
|
||||
value: null,
|
||||
};
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/17135
|
||||
it('the end time should be less than the start time', () => {
|
||||
const wrapper = mount(<RangePicker defaultValue={[moment(), moment()]} />);
|
||||
wrapper.find('.ant-calendar-picker-input').simulate('click');
|
||||
const firstInput = wrapper.find('.ant-calendar-input').first();
|
||||
const secondInput = wrapper.find('.ant-calendar-input').last();
|
||||
firstInput.simulate('change', {
|
||||
target: {
|
||||
value: moment()
|
||||
.add(1, 'day')
|
||||
.format('YYYY-MM-DD'),
|
||||
},
|
||||
onPanelChange = value => {
|
||||
this.setState({ value });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RangePicker
|
||||
value={this.state.value}
|
||||
mode={['month', 'month']}
|
||||
onPanelChange={this.onPanelChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
const wrapper = mount(<Test />);
|
||||
openPicker(wrapper);
|
||||
selectCell(wrapper, 'Feb');
|
||||
openPicker(wrapper, 1);
|
||||
selectCell(wrapper, 'Feb');
|
||||
closePicker(wrapper, 1);
|
||||
|
||||
const { value } = wrapper.state();
|
||||
|
||||
expect(value[0].isSame(value[1], 'date')).toBeTruthy();
|
||||
});
|
||||
expect(firstInput.getDOMNode().value).toBe(
|
||||
moment()
|
||||
.add(1, 'day')
|
||||
.format('YYYY-MM-DD'),
|
||||
);
|
||||
expect(secondInput.getDOMNode().value).toBe('');
|
||||
});
|
||||
});
|
||||
|
@ -1,10 +1,8 @@
|
||||
import React from 'react';
|
||||
import { mount, render } from 'enzyme';
|
||||
import moment from 'moment';
|
||||
import { mount } from 'enzyme';
|
||||
import { setMockDate, resetMockDate } from '../../../tests/utils';
|
||||
import DatePicker from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import { openPanel } from './utils';
|
||||
|
||||
const { WeekPicker } = DatePicker;
|
||||
|
||||
@ -17,71 +15,10 @@ describe('WeekPicker', () => {
|
||||
resetMockDate();
|
||||
});
|
||||
|
||||
focusTest(WeekPicker);
|
||||
focusTest(WeekPicker, true);
|
||||
|
||||
it('should support style prop', () => {
|
||||
const wrapper = mount(<WeekPicker style={{ width: 400 }} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('extra footer works', () => {
|
||||
const wrapper = mount(
|
||||
<WeekPicker renderExtraFooter={mode => <span className="extra-node">{mode}</span>} />,
|
||||
);
|
||||
openPanel(wrapper);
|
||||
|
||||
let extraNode = wrapper.find('.extra-node');
|
||||
expect(extraNode.length).toBe(1);
|
||||
expect(extraNode.text()).toBe('date');
|
||||
|
||||
wrapper
|
||||
.find('.ant-calendar-month-select')
|
||||
.hostNodes()
|
||||
.simulate('click');
|
||||
extraNode = wrapper.find('.ant-calendar-month-panel .extra-node');
|
||||
expect(extraNode.length).toBe(1);
|
||||
expect(extraNode.text()).toBe('month');
|
||||
|
||||
wrapper
|
||||
.find('.ant-calendar-year-select')
|
||||
.hostNodes()
|
||||
.simulate('click');
|
||||
extraNode = wrapper.find('.ant-calendar-year-panel .extra-node');
|
||||
expect(extraNode.length).toBe(1);
|
||||
expect(extraNode.text()).toBe('year');
|
||||
|
||||
wrapper
|
||||
.find('.ant-calendar-year-panel-decade-select')
|
||||
.hostNodes()
|
||||
.simulate('click');
|
||||
extraNode = wrapper.find('.ant-calendar-decade-panel .extra-node');
|
||||
expect(extraNode.length).toBe(1);
|
||||
expect(extraNode.text()).toBe('decade');
|
||||
});
|
||||
|
||||
it('should support dateRender', () => {
|
||||
const wrapper = mount(
|
||||
<WeekPicker open dateRender={current => <span>{current.format('YYYY-MM-DD')}</span>} />,
|
||||
);
|
||||
expect(
|
||||
render(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support allowClear', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<WeekPicker defaultValue={moment()} onChange={onChange} />,
|
||||
);
|
||||
wrapper
|
||||
.find('.ant-calendar-picker-clear')
|
||||
.hostNodes()
|
||||
.simulate('click');
|
||||
expect(onChange).toHaveBeenCalledWith(null, '');
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,826 +1,43 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`WeekPicker should support dateRender 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="ant-calendar-picker-container"
|
||||
style="opacity:0"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar ant-calendar-week-number"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-panel"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date-panel"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-header"
|
||||
>
|
||||
<div
|
||||
style="position:relative"
|
||||
>
|
||||
<a
|
||||
class="ant-calendar-prev-year-btn"
|
||||
role="button"
|
||||
title="Last year (Control + left)"
|
||||
/>
|
||||
<a
|
||||
class="ant-calendar-prev-month-btn"
|
||||
role="button"
|
||||
title="Previous month (PageUp)"
|
||||
/>
|
||||
<span
|
||||
class="ant-calendar-my-select"
|
||||
>
|
||||
<a
|
||||
class="ant-calendar-month-select"
|
||||
role="button"
|
||||
title="Choose a month"
|
||||
>
|
||||
Sep
|
||||
</a>
|
||||
<a
|
||||
class="ant-calendar-year-select"
|
||||
role="button"
|
||||
title="Choose a year"
|
||||
>
|
||||
2017
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
class="ant-calendar-next-month-btn"
|
||||
title="Next month (PageDown)"
|
||||
/>
|
||||
<a
|
||||
class="ant-calendar-next-year-btn"
|
||||
title="Next year (Control + right)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-calendar-body"
|
||||
>
|
||||
<table
|
||||
cellspacing="0"
|
||||
class="ant-calendar-table"
|
||||
role="grid"
|
||||
>
|
||||
<thead>
|
||||
<tr
|
||||
role="row"
|
||||
>
|
||||
<th
|
||||
class="ant-calendar-column-header ant-calendar-week-number-header"
|
||||
role="columnheader"
|
||||
>
|
||||
<span
|
||||
class="ant-calendar-column-header-inner"
|
||||
>
|
||||
x
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="ant-calendar-column-header"
|
||||
role="columnheader"
|
||||
title="Sun"
|
||||
>
|
||||
<span
|
||||
class="ant-calendar-column-header-inner"
|
||||
>
|
||||
Su
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="ant-calendar-column-header"
|
||||
role="columnheader"
|
||||
title="Mon"
|
||||
>
|
||||
<span
|
||||
class="ant-calendar-column-header-inner"
|
||||
>
|
||||
Mo
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="ant-calendar-column-header"
|
||||
role="columnheader"
|
||||
title="Tue"
|
||||
>
|
||||
<span
|
||||
class="ant-calendar-column-header-inner"
|
||||
>
|
||||
Tu
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="ant-calendar-column-header"
|
||||
role="columnheader"
|
||||
title="Wed"
|
||||
>
|
||||
<span
|
||||
class="ant-calendar-column-header-inner"
|
||||
>
|
||||
We
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="ant-calendar-column-header"
|
||||
role="columnheader"
|
||||
title="Thu"
|
||||
>
|
||||
<span
|
||||
class="ant-calendar-column-header-inner"
|
||||
>
|
||||
Th
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="ant-calendar-column-header"
|
||||
role="columnheader"
|
||||
title="Fri"
|
||||
>
|
||||
<span
|
||||
class="ant-calendar-column-header-inner"
|
||||
>
|
||||
Fr
|
||||
</span>
|
||||
</th>
|
||||
<th
|
||||
class="ant-calendar-column-header"
|
||||
role="columnheader"
|
||||
title="Sat"
|
||||
>
|
||||
<span
|
||||
class="ant-calendar-column-header-inner"
|
||||
>
|
||||
Sa
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
class="ant-calendar-tbody"
|
||||
>
|
||||
<tr
|
||||
class=""
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-week-number-cell"
|
||||
role="gridcell"
|
||||
>
|
||||
35
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
role="gridcell"
|
||||
title="August 27, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-08-27
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
role="gridcell"
|
||||
title="August 28, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-08-28
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
role="gridcell"
|
||||
title="August 29, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-08-29
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell"
|
||||
role="gridcell"
|
||||
title="August 30, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-08-30
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-month-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="August 31, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-08-31
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 1, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-01
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 2, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-02
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class=""
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-week-number-cell"
|
||||
role="gridcell"
|
||||
>
|
||||
36
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 3, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-03
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 4, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-04
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 5, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-05
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 6, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-06
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 7, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-07
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 8, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-08
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 9, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-09
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class=""
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-week-number-cell"
|
||||
role="gridcell"
|
||||
>
|
||||
37
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 10, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-10
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 11, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-11
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 12, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-12
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 13, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-13
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 14, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-14
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 15, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-15
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 16, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-16
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class="ant-calendar-current-week ant-calendar-active-week"
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-week-number-cell"
|
||||
role="gridcell"
|
||||
>
|
||||
38
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 17, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-17
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-today ant-calendar-selected-day"
|
||||
role="gridcell"
|
||||
title="September 18, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-18
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 19, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-19
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 20, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-20
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 21, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-21
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 22, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-22
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 23, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-23
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class=""
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-week-number-cell"
|
||||
role="gridcell"
|
||||
>
|
||||
39
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 24, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-24
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 25, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-25
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 26, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-26
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 27, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-27
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 28, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-28
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell"
|
||||
role="gridcell"
|
||||
title="September 29, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-29
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-last-day-of-month"
|
||||
role="gridcell"
|
||||
title="September 30, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-09-30
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
class=""
|
||||
role="row"
|
||||
>
|
||||
<td
|
||||
class="ant-calendar-week-number-cell"
|
||||
role="gridcell"
|
||||
>
|
||||
40
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-next-month-btn-day"
|
||||
role="gridcell"
|
||||
title="October 1, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-10-01
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-next-month-btn-day"
|
||||
role="gridcell"
|
||||
title="October 2, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-10-02
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-next-month-btn-day"
|
||||
role="gridcell"
|
||||
title="October 3, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-10-03
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-next-month-btn-day"
|
||||
role="gridcell"
|
||||
title="October 4, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-10-04
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-next-month-btn-day"
|
||||
role="gridcell"
|
||||
title="October 5, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-10-05
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-next-month-btn-day"
|
||||
role="gridcell"
|
||||
title="October 6, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-10-06
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-calendar-cell ant-calendar-next-month-btn-day"
|
||||
role="gridcell"
|
||||
title="October 7, 2017"
|
||||
>
|
||||
<div
|
||||
class="ant-calendar-date"
|
||||
>
|
||||
<span>
|
||||
2017-10-07
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`WeekPicker should support style prop 1`] = `
|
||||
<span
|
||||
class="ant-calendar-picker"
|
||||
<div
|
||||
class="ant-picker"
|
||||
style="width: 400px;"
|
||||
>
|
||||
<span
|
||||
style="display: inline-block; width: 100%;"
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
class="ant-calendar-picker-input ant-input"
|
||||
placeholder="Select date"
|
||||
placeholder="Select week"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar ant-calendar-picker-icon"
|
||||
role="img"
|
||||
class="ant-picker-suffix"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar"
|
||||
role="img"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,89 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import moment from 'moment';
|
||||
import MockDate from 'mockdate';
|
||||
import DatePicker from '..';
|
||||
import Input from '../../input';
|
||||
import { selectDate, openPanel } from './utils';
|
||||
|
||||
const { MonthPicker, WeekPicker, RangePicker } = DatePicker;
|
||||
|
||||
describe('DatePicker', () => {
|
||||
beforeEach(() => {
|
||||
MockDate.set(moment('2016-11-22'));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
MockDate.reset();
|
||||
});
|
||||
|
||||
it('should focus trigger input after select date in DatePicker', () => {
|
||||
const wrapper = mount(<DatePicker />);
|
||||
openPanel(wrapper);
|
||||
selectDate(wrapper, moment('2016-11-23'));
|
||||
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement);
|
||||
});
|
||||
|
||||
it('should focus trigger input after select date in RangePicker', () => {
|
||||
const wrapper = mount(<RangePicker />);
|
||||
openPanel(wrapper);
|
||||
selectDate(wrapper, moment('2016-11-23'), 0);
|
||||
selectDate(wrapper, moment('2016-11-28'), 1);
|
||||
expect(wrapper.find('.ant-calendar-picker').getDOMNode()).toBe(document.activeElement);
|
||||
});
|
||||
|
||||
it('should focus trigger input after select date in MonthPicker', () => {
|
||||
const wrapper = mount(<MonthPicker />);
|
||||
openPanel(wrapper);
|
||||
wrapper
|
||||
.find('.ant-calendar-month-panel-month')
|
||||
.first()
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('.ant-calendar-month-panel-cell')
|
||||
.at(6)
|
||||
.hasClass('ant-calendar-month-panel-selected-cell');
|
||||
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement);
|
||||
});
|
||||
|
||||
it('should focus trigger input after select date in WeekPicker', () => {
|
||||
const wrapper = mount(<WeekPicker />);
|
||||
openPanel(wrapper);
|
||||
selectDate(wrapper, moment('2016-11-23'));
|
||||
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement);
|
||||
});
|
||||
|
||||
it('should not auto focus trigger input when open prop is true in DatePicker', () => {
|
||||
const wrapper = mount(<DatePicker open />);
|
||||
const wrapperInput = mount(<Input />);
|
||||
wrapperInput.instance().select();
|
||||
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).not.toBe(
|
||||
document.activeElement,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not auto focus trigger input when open prop is true in RangePicker', () => {
|
||||
const wrapper = mount(<RangePicker open />);
|
||||
const wrapperInput = mount(<Input />);
|
||||
wrapperInput.instance().select();
|
||||
expect(wrapper.find('.ant-calendar-picker').getDOMNode()).not.toBe(document.activeElement);
|
||||
});
|
||||
|
||||
it('should not auto focus trigger input when open prop is true in WeekPicker', () => {
|
||||
const wrapper = mount(<WeekPicker open />);
|
||||
const wrapperInput = mount(<Input />);
|
||||
wrapperInput.instance().select();
|
||||
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).not.toBe(
|
||||
document.activeElement,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not auto focus trigger input when open prop is true in MonthPicker', () => {
|
||||
const wrapper = mount(<MonthPicker open />);
|
||||
const wrapperInput = mount(<Input />);
|
||||
wrapperInput.instance().select();
|
||||
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).not.toBe(
|
||||
document.activeElement,
|
||||
);
|
||||
});
|
||||
});
|
@ -1,52 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import DatePicker from '..';
|
||||
|
||||
const { MonthPicker, WeekPicker, RangePicker } = DatePicker;
|
||||
|
||||
describe('invalid value or defaultValue', () => {
|
||||
beforeAll(() => {
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error.mockRestore();
|
||||
});
|
||||
|
||||
it('DatePicker should throw error when value or defaultValue is not moment object', () => {
|
||||
expect(() => {
|
||||
mount(<DatePicker value={{}} />);
|
||||
}).toThrow('The value/defaultValue of DatePicker or MonthPicker must be a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value');
|
||||
expect(() => {
|
||||
mount(<DatePicker defaultValue={{}} />);
|
||||
}).toThrow('The value/defaultValue of DatePicker or MonthPicker must be a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value')
|
||||
});
|
||||
|
||||
it('WeekPicker should throw error when value or defaultValue is not moment object', () => {
|
||||
expect(() => {
|
||||
mount(<WeekPicker value={{}} />);
|
||||
}).toThrow('The value/defaultValue of WeekPicker must be a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value');
|
||||
expect(() => {
|
||||
mount(<WeekPicker defaultValue={{}} />);
|
||||
}).toThrow('The value/defaultValue of WeekPicker must be a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value');
|
||||
});
|
||||
|
||||
it('RangePicker should throw error when value or defaultValue is not moment object', () => {
|
||||
expect(() => {
|
||||
mount(<RangePicker value={[{}, {}]} />);
|
||||
}).toThrow('The value/defaultValue of RangePicker must be a moment object array after `antd@2.0`, see: https://u.ant.design/date-picker-value');
|
||||
expect(() => {
|
||||
mount(<RangePicker defaultValue={[{}, {}]} />);
|
||||
}).toThrow('The value/defaultValue of RangePicker must be a moment object array after `antd@2.0`, see: https://u.ant.design/date-picker-value')
|
||||
});
|
||||
|
||||
it('MonthPicker should throw error when value or defaultValue is not moment object', () => {
|
||||
expect(() => {
|
||||
mount(<MonthPicker value={{}} />);
|
||||
}).toThrow('The value/defaultValue of DatePicker or MonthPicker must be a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value');
|
||||
expect(() => {
|
||||
mount(<MonthPicker defaultValue={{}} />);
|
||||
}).toThrow('The value/defaultValue of DatePicker or MonthPicker must be a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value')
|
||||
});
|
||||
});
|
@ -1,155 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import moment from 'moment';
|
||||
import DatePicker from '..';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
describe('DatePicker with showTime', () => {
|
||||
it('should trigger onChange when select value', () => {
|
||||
const onChangeFn = jest.fn();
|
||||
const onOpenChangeFn = jest.fn();
|
||||
const wrapper = mount(
|
||||
<DatePicker showTime open onChange={onChangeFn} onOpenChange={onOpenChangeFn} />,
|
||||
);
|
||||
|
||||
const calendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
calendarWrapper
|
||||
.find('.ant-calendar-date')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
expect(onChangeFn).toHaveBeenCalled();
|
||||
expect(onOpenChangeFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should trigger onOk when press ok button', () => {
|
||||
const onOkFn = jest.fn();
|
||||
const onOpenChangeFn = jest.fn();
|
||||
const onChangeFn = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
<DatePicker
|
||||
showTime
|
||||
open
|
||||
onChange={onChangeFn}
|
||||
onOk={onOkFn}
|
||||
onOpenChange={onOpenChangeFn}
|
||||
defaultValue={moment()}
|
||||
/>,
|
||||
);
|
||||
|
||||
const calendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
calendarWrapper.find('.ant-calendar-ok-btn').simulate('click');
|
||||
expect(onOkFn).toHaveBeenCalled();
|
||||
expect(onOpenChangeFn).toHaveBeenCalledWith(false);
|
||||
expect(onChangeFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should trigger onChange when click Now link', () => {
|
||||
const onOpenChangeFn = jest.fn();
|
||||
const onChangeFn = jest.fn();
|
||||
|
||||
const wrapper = mount(
|
||||
<DatePicker showTime open onChange={onChangeFn} onOpenChange={onOpenChangeFn} />,
|
||||
);
|
||||
|
||||
const calendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
calendarWrapper.find('.ant-calendar-today-btn').simulate('click');
|
||||
expect(onOpenChangeFn).toHaveBeenCalledWith(false);
|
||||
expect(onChangeFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have correct className when use12Hours is true', () => {
|
||||
const wrapper = mount(<DatePicker showTime={{ use12Hours: true }} open />);
|
||||
const calendarWrapper = mount(
|
||||
wrapper
|
||||
.find('Trigger')
|
||||
.instance()
|
||||
.getComponent(),
|
||||
);
|
||||
expect(calendarWrapper.find('.ant-calendar-time-picker-column-4').length).toBe(0);
|
||||
calendarWrapper
|
||||
.find('.ant-calendar-time-picker-btn')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
expect(calendarWrapper.find('.ant-calendar-time-picker-column-4').hostNodes().length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RangePicker with showTime', () => {
|
||||
it('should trigger onChange when select value', () => {
|
||||
const onChangeFn = jest.fn();
|
||||
const onOpenChangeFn = jest.fn();
|
||||
const wrapper = mount(
|
||||
<RangePicker showTime open onChange={onChangeFn} onOpenChange={onOpenChangeFn} />,
|
||||
);
|
||||
|
||||
function findNode(selector) {
|
||||
return wrapper.find('Trigger').find(selector);
|
||||
}
|
||||
|
||||
expect(
|
||||
findNode('.ant-calendar-time-picker-btn').hasClass('ant-calendar-time-picker-btn-disabled'),
|
||||
).toBe(true);
|
||||
expect(findNode('.ant-calendar-ok-btn').hasClass('ant-calendar-ok-btn-disabled')).toBe(true);
|
||||
findNode('.ant-calendar-date')
|
||||
.at(10)
|
||||
.simulate('click');
|
||||
findNode('.ant-calendar-date')
|
||||
.at(11)
|
||||
.simulate('click');
|
||||
|
||||
expect(
|
||||
findNode('.ant-calendar-time-picker-btn').hasClass('ant-calendar-time-picker-btn-disabled'),
|
||||
).toBe(false);
|
||||
expect(findNode('.ant-calendar-ok-btn').hasClass('ant-calendar-ok-btn-disabled')).toBe(false);
|
||||
expect(onChangeFn).toHaveBeenCalled();
|
||||
expect(onOpenChangeFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should trigger onOk when press ok button', () => {
|
||||
const onOkFn = jest.fn();
|
||||
const onChangeFn = jest.fn();
|
||||
const onOpenChangeFn = jest.fn();
|
||||
const wrapper = mount(
|
||||
<RangePicker
|
||||
showTime
|
||||
open
|
||||
onOk={onOkFn}
|
||||
onChange={onChangeFn}
|
||||
onOpenChange={onOpenChangeFn}
|
||||
/>,
|
||||
);
|
||||
|
||||
function findNode(selector) {
|
||||
return wrapper.find('Trigger').find(selector);
|
||||
}
|
||||
|
||||
findNode('.ant-calendar-date')
|
||||
.at(10)
|
||||
.simulate('click');
|
||||
findNode('.ant-calendar-date')
|
||||
.at(11)
|
||||
.simulate('click');
|
||||
onChangeFn.mockClear();
|
||||
findNode('.ant-calendar-ok-btn').simulate('click');
|
||||
expect(onOkFn).toHaveBeenCalled();
|
||||
expect(onOpenChangeFn).toHaveBeenCalledWith(false);
|
||||
expect(onChangeFn).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -31,3 +31,38 @@ export function nextYear(wrapper) {
|
||||
export function nextMonth(wrapper) {
|
||||
wrapper.find('.ant-calendar-next-month-btn').simulate('click');
|
||||
}
|
||||
|
||||
export function openPicker(wrapper, index = 0) {
|
||||
wrapper
|
||||
.find('input')
|
||||
.at(index)
|
||||
.simulate('mousedown')
|
||||
.simulate('focus');
|
||||
}
|
||||
export function closePicker(wrapper, index = 0) {
|
||||
wrapper
|
||||
.find('input')
|
||||
.at(index)
|
||||
.simulate('blur');
|
||||
}
|
||||
|
||||
export function selectCell(wrapper, text, index = 0) {
|
||||
let matchCell;
|
||||
|
||||
wrapper
|
||||
.find('table')
|
||||
.at(index)
|
||||
.find('td')
|
||||
.forEach(td => {
|
||||
if (td.text() === String(text) && td.props().className.includes('-in-view')) {
|
||||
matchCell = td;
|
||||
td.simulate('click');
|
||||
}
|
||||
});
|
||||
|
||||
if (!matchCell) {
|
||||
throw new Error('Cell not match in picker panel.');
|
||||
}
|
||||
|
||||
return matchCell;
|
||||
}
|
||||
|
@ -1,275 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import * as moment from 'moment';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import MonthCalendar from 'rc-calendar/lib/MonthCalendar';
|
||||
import RcDatePicker from 'rc-calendar/lib/Picker';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import { CloseCircleFilled, CalendarOutlined } from '@ant-design/icons';
|
||||
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
import interopDefault from '../_util/interopDefault';
|
||||
import getDataOrAriaProps from '../_util/getDataOrAriaProps';
|
||||
import { formatDate } from './utils';
|
||||
|
||||
export interface PickerProps {
|
||||
value?: moment.Moment;
|
||||
open?: boolean;
|
||||
prefixCls: string;
|
||||
}
|
||||
|
||||
export interface PickerState {
|
||||
open: boolean;
|
||||
value: moment.Moment | null;
|
||||
showDate: moment.Moment | null;
|
||||
}
|
||||
|
||||
export default function createPicker(TheCalendar: React.ComponentClass): any {
|
||||
class CalenderWrapper extends React.Component<any, PickerState> {
|
||||
static defaultProps = {
|
||||
allowClear: true,
|
||||
showToday: true,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps: PickerProps, prevState: PickerState) {
|
||||
const state: Partial<PickerState> = {};
|
||||
let { open } = prevState;
|
||||
|
||||
if ('open' in nextProps) {
|
||||
state.open = nextProps.open;
|
||||
open = nextProps.open || false;
|
||||
}
|
||||
if ('value' in nextProps) {
|
||||
state.value = nextProps.value;
|
||||
if (
|
||||
nextProps.value !== prevState.value ||
|
||||
(!open && nextProps.value !== prevState.showDate)
|
||||
) {
|
||||
state.showDate = nextProps.value;
|
||||
}
|
||||
}
|
||||
return Object.keys(state).length > 0 ? state : null;
|
||||
}
|
||||
|
||||
private input: any;
|
||||
|
||||
private prefixCls?: string;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
const value = props.value || props.defaultValue;
|
||||
if (value && !interopDefault(moment).isMoment(value)) {
|
||||
throw new Error(
|
||||
'The value/defaultValue of DatePicker or MonthPicker must be ' +
|
||||
'a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value',
|
||||
);
|
||||
}
|
||||
this.state = {
|
||||
value,
|
||||
showDate: value,
|
||||
open: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(_: PickerProps, prevState: PickerState) {
|
||||
if (!('open' in this.props) && prevState.open && !this.state.open) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
|
||||
saveInput = (node: any) => {
|
||||
this.input = node;
|
||||
};
|
||||
|
||||
clearSelection = (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.handleChange(null);
|
||||
};
|
||||
|
||||
handleChange = (value: moment.Moment | null) => {
|
||||
const { props } = this;
|
||||
if (!('value' in props)) {
|
||||
this.setState({
|
||||
value,
|
||||
showDate: value,
|
||||
});
|
||||
}
|
||||
props.onChange(value, formatDate(value, props.format));
|
||||
};
|
||||
|
||||
handleCalendarChange = (value: moment.Moment) => {
|
||||
this.setState({ showDate: value });
|
||||
};
|
||||
|
||||
handleOpenChange = (open: boolean) => {
|
||||
const { onOpenChange } = this.props;
|
||||
if (!('open' in this.props)) {
|
||||
this.setState({ open });
|
||||
}
|
||||
|
||||
if (onOpenChange) {
|
||||
onOpenChange(open);
|
||||
}
|
||||
};
|
||||
|
||||
focus() {
|
||||
this.input.focus();
|
||||
}
|
||||
|
||||
blur() {
|
||||
this.input.blur();
|
||||
}
|
||||
|
||||
renderFooter = (...args: any[]) => {
|
||||
const { renderExtraFooter } = this.props;
|
||||
const { prefixCls } = this;
|
||||
return renderExtraFooter ? (
|
||||
<div className={`${prefixCls}-footer-extra`}>{renderExtraFooter(...args)}</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
renderPicker = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const { value, showDate, open } = this.state;
|
||||
const props = omit(this.props, ['onChange']);
|
||||
const { prefixCls: customizePrefixCls, locale, localeCode, suffixIcon } = props;
|
||||
|
||||
const prefixCls = getPrefixCls('calendar', customizePrefixCls);
|
||||
// To support old version react.
|
||||
// Have to add prefixCls on the instance.
|
||||
// https://github.com/facebook/react/issues/12397
|
||||
this.prefixCls = prefixCls;
|
||||
|
||||
const placeholder = 'placeholder' in props ? props.placeholder : locale.lang.placeholder;
|
||||
|
||||
const disabledTime = props.showTime ? props.disabledTime : null;
|
||||
|
||||
const calendarClassName = classNames({
|
||||
[`${prefixCls}-time`]: props.showTime,
|
||||
[`${prefixCls}-month`]: MonthCalendar === TheCalendar,
|
||||
});
|
||||
|
||||
if (value && localeCode) {
|
||||
value.locale(localeCode);
|
||||
}
|
||||
|
||||
let pickerProps: Object = {};
|
||||
let calendarProps: any = {};
|
||||
const pickerStyle: { minWidth?: number } = {};
|
||||
if (props.showTime) {
|
||||
calendarProps = {
|
||||
// fix https://github.com/ant-design/ant-design/issues/1902
|
||||
onSelect: this.handleChange,
|
||||
};
|
||||
pickerStyle.minWidth = 195;
|
||||
} else {
|
||||
pickerProps = {
|
||||
onChange: this.handleChange,
|
||||
};
|
||||
}
|
||||
if ('mode' in props) {
|
||||
calendarProps.mode = props.mode;
|
||||
}
|
||||
|
||||
warning(
|
||||
!('onOK' in props),
|
||||
'DatePicker',
|
||||
'It should be `DatePicker[onOk]` or `MonthPicker[onOk]`, instead of `onOK`!',
|
||||
);
|
||||
const calendar = (
|
||||
<TheCalendar
|
||||
{...calendarProps}
|
||||
disabledDate={props.disabledDate}
|
||||
disabledTime={disabledTime}
|
||||
locale={locale.lang}
|
||||
timePicker={props.timePicker}
|
||||
defaultValue={props.defaultPickerValue || interopDefault(moment)()}
|
||||
dateInputPlaceholder={placeholder}
|
||||
prefixCls={prefixCls}
|
||||
className={calendarClassName}
|
||||
onOk={props.onOk}
|
||||
dateRender={props.dateRender}
|
||||
format={props.format}
|
||||
showToday={props.showToday}
|
||||
monthCellContentRender={props.monthCellContentRender}
|
||||
renderFooter={this.renderFooter}
|
||||
onPanelChange={props.onPanelChange}
|
||||
onChange={this.handleCalendarChange}
|
||||
value={showDate}
|
||||
/>
|
||||
);
|
||||
|
||||
const clearIcon =
|
||||
!props.disabled && props.allowClear && value ? (
|
||||
<CloseCircleFilled
|
||||
className={`${prefixCls}-picker-clear`}
|
||||
onClick={this.clearSelection}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const inputIcon = (suffixIcon &&
|
||||
(React.isValidElement<{ className?: string }>(suffixIcon) ? (
|
||||
React.cloneElement(suffixIcon, {
|
||||
className: classNames({
|
||||
[suffixIcon.props.className!]: suffixIcon.props.className,
|
||||
[`${prefixCls}-picker-icon`]: true,
|
||||
}),
|
||||
})
|
||||
) : (
|
||||
<span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>
|
||||
))) || <CalendarOutlined className={`${prefixCls}-picker-icon`} />;
|
||||
|
||||
const dataOrAriaProps = getDataOrAriaProps(props);
|
||||
const input = ({ value: inputValue }: { value: moment.Moment | null }) => (
|
||||
<div>
|
||||
<input
|
||||
ref={this.saveInput}
|
||||
disabled={props.disabled}
|
||||
readOnly
|
||||
value={formatDate(inputValue, props.format)}
|
||||
placeholder={placeholder}
|
||||
className={props.pickerInputClass}
|
||||
tabIndex={props.tabIndex}
|
||||
name={props.name}
|
||||
{...dataOrAriaProps}
|
||||
/>
|
||||
{clearIcon}
|
||||
{inputIcon}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<span
|
||||
id={props.id}
|
||||
className={classNames(props.className, props.pickerClass)}
|
||||
style={{ ...pickerStyle, ...props.style }}
|
||||
onFocus={props.onFocus}
|
||||
onBlur={props.onBlur}
|
||||
onMouseEnter={props.onMouseEnter}
|
||||
onMouseLeave={props.onMouseLeave}
|
||||
>
|
||||
<RcDatePicker
|
||||
{...props}
|
||||
{...pickerProps}
|
||||
calendar={calendar}
|
||||
value={value}
|
||||
prefixCls={`${prefixCls}-picker-container`}
|
||||
style={props.popupStyle}
|
||||
open={open}
|
||||
onOpenChange={this.handleOpenChange}
|
||||
>
|
||||
{input}
|
||||
</RcDatePicker>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderPicker}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
polyfill(CalenderWrapper);
|
||||
|
||||
return CalenderWrapper;
|
||||
}
|
@ -16,8 +16,6 @@ Basic use case. Users can select or input a date in panel.
|
||||
```jsx
|
||||
import { DatePicker } from 'antd';
|
||||
|
||||
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
|
||||
|
||||
function onChange(date, dateString) {
|
||||
console.log(date, dateString);
|
||||
}
|
||||
@ -26,11 +24,11 @@ ReactDOM.render(
|
||||
<div>
|
||||
<DatePicker onChange={onChange} />
|
||||
<br />
|
||||
<MonthPicker onChange={onChange} placeholder="Select month" />
|
||||
<DatePicker onChange={onChange} picker="week" />
|
||||
<br />
|
||||
<RangePicker onChange={onChange} />
|
||||
<DatePicker onChange={onChange} picker="month" />
|
||||
<br />
|
||||
<WeekPicker onChange={onChange} placeholder="Select week" />
|
||||
<DatePicker onChange={onChange} picker="year" />
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -17,7 +17,7 @@ Disabled part of dates and time by `disabledDate` and `disabledTime` respectivel
|
||||
import moment from 'moment';
|
||||
import { DatePicker } from 'antd';
|
||||
|
||||
const { MonthPicker, RangePicker } = DatePicker;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
function range(start, end) {
|
||||
const result = [];
|
||||
@ -64,7 +64,7 @@ ReactDOM.render(
|
||||
showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }}
|
||||
/>
|
||||
<br />
|
||||
<MonthPicker disabledDate={disabledDate} placeholder="Select month" />
|
||||
<DatePicker picker="month" disabledDate={disabledDate} />
|
||||
<br />
|
||||
<RangePicker
|
||||
disabledDate={disabledDate}
|
||||
|
@ -7,11 +7,11 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
选择框的不可用状态。
|
||||
选择框的不可用状态。你也可以通过数组单独禁用 RangePicker 的其中一项。
|
||||
|
||||
## en-US
|
||||
|
||||
A disabled state of the `DatePicker`.
|
||||
A disabled state of the `DatePicker`. You can also set as array to disable one of input.
|
||||
|
||||
```jsx
|
||||
import { DatePicker } from 'antd';
|
||||
@ -30,6 +30,11 @@ ReactDOM.render(
|
||||
defaultValue={[moment('2015-06-06', dateFormat), moment('2015-06-06', dateFormat)]}
|
||||
disabled
|
||||
/>
|
||||
<br />
|
||||
<RangePicker
|
||||
defaultValue={[moment('2019-09-03', dateFormat), moment('2019-11-22', dateFormat)]}
|
||||
disabled={[false, true]}
|
||||
/>
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -16,15 +16,19 @@ Render extra footer in panel for customized requirements.
|
||||
```jsx
|
||||
import { DatePicker } from 'antd';
|
||||
|
||||
const { RangePicker, MonthPicker } = DatePicker;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<DatePicker renderExtraFooter={() => 'extra footer'} />
|
||||
<br />
|
||||
<DatePicker renderExtraFooter={() => 'extra footer'} showTime />
|
||||
<br />
|
||||
<RangePicker renderExtraFooter={() => 'extra footer'} />
|
||||
<br />
|
||||
<RangePicker renderExtraFooter={() => 'extra footer'} showTime />
|
||||
<MonthPicker renderExtraFooter={() => 'extra footer'} placeholder="Select month" />
|
||||
<br />
|
||||
<DatePicker renderExtraFooter={() => 'extra footer'} picker="month" />
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 1
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 日期格式
|
||||
en-US: Date Format
|
||||
@ -17,7 +17,7 @@ We can set the date format by `format`.
|
||||
import { DatePicker } from 'antd';
|
||||
import moment from 'moment';
|
||||
|
||||
const { MonthPicker, RangePicker } = DatePicker;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const dateFormat = 'YYYY/MM/DD';
|
||||
const monthFormat = 'YYYY/MM';
|
||||
@ -30,7 +30,7 @@ ReactDOM.render(
|
||||
<br />
|
||||
<DatePicker defaultValue={moment('01/01/2015', dateFormatList[0])} format={dateFormatList} />
|
||||
<br />
|
||||
<MonthPicker defaultValue={moment('2015/01', monthFormat)} format={monthFormat} />
|
||||
<DatePicker defaultValue={moment('2015/01', monthFormat)} format={monthFormat} picker="month" />
|
||||
<br />
|
||||
<RangePicker
|
||||
defaultValue={[moment('2015/01/01', dateFormat), moment('2015/01/01', dateFormat)]}
|
||||
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
order: 11
|
||||
order: 99
|
||||
title:
|
||||
zh-CN: 受控面板
|
||||
en-US: Controlled Panels
|
||||
debug: true
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
35
components/date-picker/demo/range-picker.md
Normal file
35
components/date-picker/demo/range-picker.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: 范围选择器
|
||||
en-US: Range Picker
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
通过设置 `picker` 属性,指定范围选择器类型。
|
||||
|
||||
## en-US
|
||||
|
||||
Set range picker type by `picker` prop.
|
||||
|
||||
```jsx
|
||||
import { DatePicker } from 'antd';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<RangePicker />
|
||||
<br />
|
||||
<RangePicker showTime />
|
||||
<br />
|
||||
<RangePicker picker="week" />
|
||||
<br />
|
||||
<RangePicker picker="month" />
|
||||
<br />
|
||||
<RangePicker picker="year" />
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 2
|
||||
order: 11
|
||||
title:
|
||||
zh-CN: 三种大小
|
||||
en-US: Three Sizes
|
||||
@ -16,7 +16,7 @@ The input box comes in three sizes. `default` will be used if `size` is omitted.
|
||||
```jsx
|
||||
import { DatePicker, Radio } from 'antd';
|
||||
|
||||
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
class PickerSizesDemo extends React.Component {
|
||||
state = {
|
||||
@ -40,11 +40,11 @@ class PickerSizesDemo extends React.Component {
|
||||
<br />
|
||||
<DatePicker size={size} />
|
||||
<br />
|
||||
<MonthPicker size={size} placeholder="Select Month" />
|
||||
<DatePicker size={size} picker="month" />
|
||||
<br />
|
||||
<RangePicker size={size} />
|
||||
<br />
|
||||
<WeekPicker size={size} placeholder="Select Week" />
|
||||
<DatePicker size={size} picker="week" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
order: 6
|
||||
order: 99
|
||||
title:
|
||||
zh-CN: 自定义日期范围选择
|
||||
en-US: Customized Range Picker
|
||||
debug: true
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 13
|
||||
order: 99
|
||||
debug: true
|
||||
title:
|
||||
zh-CN: 后缀图标
|
||||
@ -19,7 +19,7 @@ import { DatePicker } from 'antd';
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
|
||||
const smileIcon = <SmileOutlined />;
|
||||
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
function onChange(date, dateString) {
|
||||
console.log(date, dateString);
|
||||
@ -29,19 +29,19 @@ ReactDOM.render(
|
||||
<div>
|
||||
<DatePicker suffixIcon={smileIcon} onChange={onChange} />
|
||||
<br />
|
||||
<MonthPicker suffixIcon={smileIcon} onChange={onChange} placeholder="Select month" />
|
||||
<DatePicker suffixIcon={smileIcon} onChange={onChange} picker="month" />
|
||||
<br />
|
||||
<RangePicker suffixIcon={smileIcon} onChange={onChange} />
|
||||
<br />
|
||||
<WeekPicker suffixIcon={smileIcon} onChange={onChange} placeholder="Select week" />
|
||||
<DatePicker suffixIcon={smileIcon} onChange={onChange} picker="week" />
|
||||
<br />
|
||||
<DatePicker suffixIcon="ab" onChange={onChange} />
|
||||
<br />
|
||||
<MonthPicker suffixIcon="ab" onChange={onChange} placeholder="Select month" />
|
||||
<DatePicker suffixIcon="ab" onChange={onChange} picker="month" />
|
||||
<br />
|
||||
<RangePicker suffixIcon="ab" onChange={onChange} />
|
||||
<br />
|
||||
<WeekPicker suffixIcon="ab" onChange={onChange} placeholder="Select week" />
|
||||
<DatePicker suffixIcon="ab" onChange={onChange} picker="week" />
|
||||
</div>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -29,12 +29,11 @@ function onOk(value) {
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<DatePicker showTime placeholder="Select Time" onChange={onChange} onOk={onOk} />
|
||||
<DatePicker showTime onChange={onChange} onOk={onOk} />
|
||||
<br />
|
||||
<RangePicker
|
||||
showTime={{ format: 'HH:mm' }}
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
placeholder={['Start Time', 'End Time']}
|
||||
onChange={onChange}
|
||||
onOk={onOk}
|
||||
/>
|
||||
|
317
components/date-picker/generatePicker.tsx
Normal file
317
components/date-picker/generatePicker.tsx
Normal file
@ -0,0 +1,317 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import RCPicker, { RangePicker as RCRangePicker } from 'rc-picker';
|
||||
import { GenerateConfig } from 'rc-picker/lib/generate/index';
|
||||
import {
|
||||
PickerBaseProps as RCPickerBaseProps,
|
||||
PickerDateProps as RCPickerDateProps,
|
||||
PickerTimeProps as RCPickerTimeProps,
|
||||
} from 'rc-picker/lib/Picker';
|
||||
import { SharedTimeProps } from 'rc-picker/lib/panels/TimePanel';
|
||||
import {
|
||||
RangePickerBaseProps as RCRangePickerBaseProps,
|
||||
RangePickerDateProps as RCRangePickerDateProps,
|
||||
RangePickerTimeProps as RCRangePickerTimeProps,
|
||||
} from 'rc-picker/lib/RangePicker';
|
||||
import { PickerMode } from 'rc-picker/lib/interface';
|
||||
import { CalendarOutlined, ClockCircleOutlined, CloseCircleFilled } from '@ant-design/icons';
|
||||
import { ConfigContext, ConfigConsumerProps } from '../config-provider';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import enUS from './locale/en_US';
|
||||
import { getPlaceholder, getRangePlaceholder } from './util';
|
||||
import PickerButton from './PickerButton';
|
||||
import PickerTag from './PickerTag';
|
||||
|
||||
const Components = { button: PickerButton, rangeItem: PickerTag };
|
||||
|
||||
function toArray<T>(list: T | T[]): T[] {
|
||||
if (!list) {
|
||||
return [];
|
||||
}
|
||||
return Array.isArray(list) ? list : [list];
|
||||
}
|
||||
|
||||
function getTimeProps<DateType>(
|
||||
props: { format?: string; picker?: PickerMode } & SharedTimeProps<DateType>,
|
||||
) {
|
||||
const { format, picker, showHour, showMinute, showSecond, use12Hours } = props;
|
||||
|
||||
const firstFormat = toArray(format)[0];
|
||||
const showTimeObj: SharedTimeProps<DateType> = { ...props };
|
||||
|
||||
if (firstFormat) {
|
||||
if (!firstFormat.includes('s') && showSecond === undefined) {
|
||||
showTimeObj.showSecond = false;
|
||||
}
|
||||
if (!firstFormat.includes('m') && showMinute === undefined) {
|
||||
showTimeObj.showMinute = false;
|
||||
}
|
||||
if (!firstFormat.includes('H') && !firstFormat.includes('h') && showHour === undefined) {
|
||||
showTimeObj.showHour = false;
|
||||
}
|
||||
|
||||
if ((firstFormat.includes('a') || firstFormat.includes('A')) && use12Hours === undefined) {
|
||||
showTimeObj.use12Hours = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (picker === 'time') {
|
||||
return showTimeObj;
|
||||
}
|
||||
|
||||
return {
|
||||
showTime: showTimeObj,
|
||||
};
|
||||
}
|
||||
|
||||
type InjectDefaultProps<Props> = Omit<
|
||||
Props,
|
||||
| 'locale'
|
||||
| 'generateConfig'
|
||||
| 'prevIcon'
|
||||
| 'nextIcon'
|
||||
| 'superPrevIcon'
|
||||
| 'superNextIcon'
|
||||
| 'hideHeader'
|
||||
| 'components'
|
||||
> & {
|
||||
locale?: typeof enUS;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
};
|
||||
|
||||
// Picker Props
|
||||
export type PickerBaseProps<DateType> = InjectDefaultProps<RCPickerBaseProps<DateType>>;
|
||||
export type PickerDateProps<DateType> = InjectDefaultProps<RCPickerDateProps<DateType>>;
|
||||
export type PickerTimeProps<DateType> = InjectDefaultProps<RCPickerTimeProps<DateType>>;
|
||||
|
||||
export type PickerProps<DateType> =
|
||||
| PickerBaseProps<DateType>
|
||||
| PickerDateProps<DateType>
|
||||
| PickerTimeProps<DateType>;
|
||||
|
||||
// Range Picker Props
|
||||
export type RangePickerBaseProps<DateType> = InjectDefaultProps<RCRangePickerBaseProps<DateType>>;
|
||||
export type RangePickerDateProps<DateType> = InjectDefaultProps<RCRangePickerDateProps<DateType>>;
|
||||
export type RangePickerTimeProps<DateType> = InjectDefaultProps<RCRangePickerTimeProps<DateType>>;
|
||||
|
||||
export type RangePickerProps<DateType> =
|
||||
| RangePickerBaseProps<DateType>
|
||||
| RangePickerDateProps<DateType>
|
||||
| RangePickerTimeProps<DateType>;
|
||||
|
||||
function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
// =========================== Picker ===========================
|
||||
type DatePickerProps = PickerProps<DateType>;
|
||||
|
||||
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>>();
|
||||
|
||||
focus = () => {
|
||||
if (this.pickerRef.current) {
|
||||
this.pickerRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
blur = () => {
|
||||
if (this.pickerRef.current) {
|
||||
this.pickerRef.current.blur();
|
||||
}
|
||||
};
|
||||
|
||||
getDefaultLocale = () => {
|
||||
const { locale } = this.props;
|
||||
const result = {
|
||||
...enUS,
|
||||
...locale,
|
||||
};
|
||||
result.lang = {
|
||||
...result.lang,
|
||||
...((locale || {}) as any).lang,
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
renderPicker = (locale: any) => {
|
||||
const { getPrefixCls } = this.context;
|
||||
const { prefixCls: customizePrefixCls, className, size, ...restProps } = this.props;
|
||||
const { format, showTime } = this.props as any;
|
||||
const prefixCls = getPrefixCls('picker', customizePrefixCls);
|
||||
|
||||
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 })
|
||||
: {}),
|
||||
};
|
||||
|
||||
return (
|
||||
<RCPicker<DateType>
|
||||
ref={this.pickerRef}
|
||||
placeholder={getPlaceholder(mergedPicker, locale)}
|
||||
suffixIcon={mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
|
||||
clearIcon={<CloseCircleFilled />}
|
||||
allowClear
|
||||
transitionName="slide-up"
|
||||
{...additionalProps}
|
||||
{...restProps}
|
||||
{...additionalOverrideProps}
|
||||
locale={locale!.lang}
|
||||
className={classNames(className, {
|
||||
[`${prefixCls}-${size}`]: size,
|
||||
})}
|
||||
prefixCls={prefixCls}
|
||||
generateConfig={generateConfig}
|
||||
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`} />}
|
||||
components={Components}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<LocaleReceiver componentName="DatePicker" defaultLocale={this.getDefaultLocale}>
|
||||
{this.renderPicker}
|
||||
</LocaleReceiver>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (displayName) {
|
||||
Picker.displayName = displayName;
|
||||
}
|
||||
|
||||
return Picker as React.ComponentClass<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');
|
||||
|
||||
// ======================== Range Picker ========================
|
||||
class RangePicker extends React.Component<RangePickerProps<DateType>> {
|
||||
static contextType = ConfigContext;
|
||||
|
||||
context: ConfigConsumerProps;
|
||||
|
||||
pickerRef = React.createRef<RCRangePicker<DateType>>();
|
||||
|
||||
focus = () => {
|
||||
if (this.pickerRef.current) {
|
||||
this.pickerRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
blur = () => {
|
||||
if (this.pickerRef.current) {
|
||||
this.pickerRef.current.blur();
|
||||
}
|
||||
};
|
||||
|
||||
getDefaultLocale = () => {
|
||||
const { locale } = this.props;
|
||||
const result = {
|
||||
...enUS,
|
||||
...locale,
|
||||
};
|
||||
result.lang = {
|
||||
...result.lang,
|
||||
...((locale || {}) as any).lang,
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
renderPicker = (locale: any) => {
|
||||
const { getPrefixCls } = this.context;
|
||||
const { prefixCls: customizePrefixCls, className, size, ...restProps } = this.props;
|
||||
const { format, showTime, picker } = this.props as any;
|
||||
const prefixCls = getPrefixCls('picker', customizePrefixCls);
|
||||
|
||||
let additionalOverrideProps: any = {};
|
||||
|
||||
additionalOverrideProps = {
|
||||
...additionalOverrideProps,
|
||||
...(showTime ? getTimeProps({ format, picker, ...showTime }) : {}),
|
||||
...(picker === 'time' ? getTimeProps({ format, ...this.props, picker }) : {}),
|
||||
};
|
||||
|
||||
return (
|
||||
<RCRangePicker<DateType>
|
||||
separator={<span className={`${prefixCls}-separator`}>→</span>}
|
||||
ref={this.pickerRef}
|
||||
placeholder={getRangePlaceholder(picker, locale)}
|
||||
suffixIcon={picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
|
||||
clearIcon={<CloseCircleFilled />}
|
||||
allowClear
|
||||
transitionName="slide-up"
|
||||
{...restProps}
|
||||
className={classNames(className, {
|
||||
[`${prefixCls}-${size}`]: size,
|
||||
})}
|
||||
{...additionalOverrideProps}
|
||||
locale={locale!.lang}
|
||||
prefixCls={prefixCls}
|
||||
generateConfig={generateConfig}
|
||||
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`} />}
|
||||
components={Components}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<LocaleReceiver componentName="DatePicker" defaultLocale={this.getDefaultLocale}>
|
||||
{this.renderPicker}
|
||||
</LocaleReceiver>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================== Export ===========================
|
||||
type MergedDatePicker = typeof DatePicker & {
|
||||
WeekPicker: typeof WeekPicker;
|
||||
MonthPicker: typeof MonthPicker;
|
||||
YearPicker: typeof YearPicker;
|
||||
RangePicker: React.ComponentClass<RangePickerProps<DateType>>;
|
||||
TimePicker: typeof TimePicker;
|
||||
};
|
||||
|
||||
const MergedDatePicker = DatePicker as MergedDatePicker;
|
||||
MergedDatePicker.WeekPicker = WeekPicker;
|
||||
MergedDatePicker.MonthPicker = MonthPicker;
|
||||
MergedDatePicker.YearPicker = YearPicker;
|
||||
MergedDatePicker.RangePicker = RangePicker;
|
||||
MergedDatePicker.TimePicker = TimePicker;
|
||||
|
||||
return MergedDatePicker;
|
||||
}
|
||||
|
||||
export default generatePicker;
|
@ -12,12 +12,13 @@ By clicking the input box, you can select a date from a popup calendar.
|
||||
|
||||
## API
|
||||
|
||||
There are four kinds of picker:
|
||||
There are five kinds of picker:
|
||||
|
||||
- DatePicker
|
||||
- MonthPicker
|
||||
- RangePicker
|
||||
- WeekPicker
|
||||
- YearPicker
|
||||
|
||||
### Localization
|
||||
|
||||
@ -31,19 +32,16 @@ import locale from 'antd/es/date-picker/locale/zh_CN';
|
||||
<DatePicker locale={locale} />;
|
||||
```
|
||||
|
||||
**Note:** Part of locale of DatePicker, MonthPicker, RangePicker, WeekPicker is read from value. So, please set the locale of moment correctly.
|
||||
|
||||
```jsx
|
||||
// The default locale is en-US, if you want to use other locale, just set locale in entry file globally.
|
||||
import moment from 'moment';
|
||||
import 'moment/locale/zh-cn';
|
||||
|
||||
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />;
|
||||
```
|
||||
|
||||
### Common API
|
||||
|
||||
The following APIs are shared by DatePicker, MonthPicker, RangePicker, WeekPicker.
|
||||
The following APIs are shared by DatePicker, YearPicker, MonthPicker, RangePicker, WeekPicker.
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
@ -54,10 +52,11 @@ The following APIs are shared by DatePicker, MonthPicker, RangePicker, WeekPicke
|
||||
| disabled | determine whether the DatePicker is disabled | boolean | false | |
|
||||
| disabledDate | specify the date that cannot be selected | (currentDate: moment) => boolean | - | |
|
||||
| dropdownClassName | to customize the className of the popup calendar | string | - | |
|
||||
| getCalendarContainer | to set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - | |
|
||||
| getPopupContainer | to set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - | |
|
||||
| locale | localization configuration | object | [default](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | |
|
||||
| mode | picker panel mode([Cannot select year or month anymore?](/docs/react/faq#When-set-mode-to-DatePicker/RangePicker,-cannot-select-year-or-month-anymore?) | `time|date|month|year|decade` | 'date' | |
|
||||
| mode | picker panel mode([Cannot select year or month anymore?](/docs/react/faq#When-set-mode-to-DatePicker/RangePicker,-cannot-select-year-or-month-anymore?) | `time|date|month|year|decade` | - | |
|
||||
| open | open state of picker | boolean | - | |
|
||||
| picker | Set picker type | `date`, `week`, `month`, `year` | `date` | |
|
||||
| placeholder | placeholder of date input | string\|RangePicker\[] | - | |
|
||||
| popupStyle | to customize the style of the popup calendar | object | {} | |
|
||||
| size | determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | string | - | |
|
||||
@ -90,6 +89,17 @@ The following APIs are shared by DatePicker, MonthPicker, RangePicker, WeekPicke
|
||||
| onOk | callback when click ok button | function() | - | |
|
||||
| onPanelChange | Callback function for panel changing | function(value, mode) | - | |
|
||||
|
||||
### YearPicker
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| defaultValue | to set default date | [moment](http://momentjs.com/) | - | |
|
||||
| defaultPickerValue | to set default picker date | [moment](http://momentjs.com/) | - | |
|
||||
| format | to set the date format, refer to [moment.js](http://momentjs.com/) | string | "YYYY" | |
|
||||
| renderExtraFooter | render extra footer in panel | () => React.ReactNode | - | |
|
||||
| value | to set date | [moment](http://momentjs.com/) | - | |
|
||||
| onChange | a callback function, can be executed when the selected time is changing | function(date: moment, dateString: string) | - | |
|
||||
|
||||
### MonthPicker
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
@ -117,8 +127,10 @@ The following APIs are shared by DatePicker, MonthPicker, RangePicker, WeekPicke
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| allowEmpty | Allow start or end input leave empty | \[boolean, boolean] | \[false, false] | |
|
||||
| defaultValue | to set default date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - | |
|
||||
| defaultPickerValue | to set default picker date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)\] | - | |
|
||||
| disabled | disable start or end | [boolean, boolean] | - | |
|
||||
| disabledTime | to specify the time that cannot be selected | function(dates: \[moment, moment], partial: `'start'|'end'`) | - | |
|
||||
| format | to set the date format, refer to [moment.js](http://momentjs.com/). When an array is provided, all values are used for parsing and first value is used for formatting. | string \| string[] | "YYYY-MM-DD HH:mm:ss" | |
|
||||
| ranges | preseted ranges for quick selection | { \[range: string]: [moment](http://momentjs.com/)\[] } \| { \[range: string]: () => [moment](http://momentjs.com/)\[] } | - | |
|
||||
@ -129,10 +141,9 @@ The following APIs are shared by DatePicker, MonthPicker, RangePicker, WeekPicke
|
||||
| value | to set date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - | |
|
||||
| onCalendarChange | a callback function, can be executed when the start time or the end time of the range is changing | function(dates: \[moment, moment], dateStrings: \[string, string]) | - | |
|
||||
| onChange | a callback function, can be executed when the selected time is changing | function(dates: \[moment, moment], dateStrings: \[string, string]) | - | |
|
||||
| onOk | callback when click ok button | function(dates: [moment](http://momentjs.com/)\[]) | - | |
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-calendar-picker {
|
||||
.code-box-demo .ant-picker {
|
||||
margin: 0 8px 12px 0;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,22 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import RcCalendar from 'rc-calendar';
|
||||
import MonthCalendar from 'rc-calendar/lib/MonthCalendar';
|
||||
import createPicker from './createPicker';
|
||||
import wrapPicker from './wrapPicker';
|
||||
import RangePicker from './RangePicker';
|
||||
import WeekPicker from './WeekPicker';
|
||||
import { DatePickerProps, DatePickerDecorator } from './interface';
|
||||
import { Moment } from 'moment';
|
||||
import momentGenerateConfig from 'rc-picker/lib/generate/moment';
|
||||
import generatePicker from './generatePicker';
|
||||
|
||||
const DatePicker = wrapPicker(createPicker(RcCalendar), 'date') as React.ClassicComponentClass<
|
||||
DatePickerProps
|
||||
>;
|
||||
const DatePicker = generatePicker<Moment>(momentGenerateConfig);
|
||||
|
||||
const MonthPicker = wrapPicker(createPicker(MonthCalendar), 'month');
|
||||
|
||||
Object.assign(DatePicker, {
|
||||
RangePicker: wrapPicker(RangePicker, 'date'),
|
||||
MonthPicker,
|
||||
WeekPicker: wrapPicker(WeekPicker, 'week'),
|
||||
});
|
||||
|
||||
export default DatePicker as DatePickerDecorator;
|
||||
export default DatePicker;
|
||||
|
@ -13,12 +13,13 @@ subtitle: 日期选择框
|
||||
|
||||
## API
|
||||
|
||||
日期类组件包括以下四种形式。
|
||||
日期类组件包括以下五种形式。
|
||||
|
||||
- DatePicker
|
||||
- MonthPicker
|
||||
- RangePicker
|
||||
- WeekPicker
|
||||
- YearPicker
|
||||
|
||||
### 国际化配置
|
||||
|
||||
@ -32,20 +33,17 @@ import locale from 'antd/es/date-picker/locale/zh_CN';
|
||||
<DatePicker locale={locale} />;
|
||||
```
|
||||
|
||||
**注意:**DatePicker、MonthPicker、RangePicker、WeekPicker 部分 locale 是从 value 中读取,所以请先正确设置 moment 的 locale。
|
||||
|
||||
```jsx
|
||||
// 默认语言为 en-US,如果你需要设置其他语言,推荐在入口文件全局设置 locale
|
||||
import moment from 'moment';
|
||||
import 'moment/locale/zh-cn';
|
||||
moment.locale('zh-cn');
|
||||
|
||||
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />;
|
||||
```
|
||||
|
||||
### 共同的 API
|
||||
|
||||
以下 API 为 DatePicker、MonthPicker、RangePicker, WeekPicker 共享的 API。
|
||||
以下 API 为 DatePicker、YearPicker、MonthPicker、RangePicker, WeekPicker 共享的 API。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
@ -56,10 +54,11 @@ moment.locale('zh-cn');
|
||||
| disabled | 禁用 | boolean | false | |
|
||||
| disabledDate | 不可选择的日期 | (currentDate: moment) => boolean | 无 | |
|
||||
| dropdownClassName | 额外的弹出日历 className | string | - | |
|
||||
| getCalendarContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 | |
|
||||
| getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 | |
|
||||
| locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | |
|
||||
| mode | 日期面板的状态([设置后无法选择年份/月份?](/docs/react/faq#当我指定了-DatePicker/RangePicker-的-mode-属性后,点击后无法选择年份/月份?)) | `time|date|month|year|decade` | 'date' | |
|
||||
| mode | 日期面板的状态([设置后无法选择年份/月份?](/docs/react/faq#当我指定了-DatePicker/RangePicker-的-mode-属性后,点击后无法选择年份/月份?)) | `time|date|month|year|decade` | - | |
|
||||
| open | 控制弹层是否展开 | boolean | - | |
|
||||
| picker | 设置选择器类型 | `date`, `week`, `month`, `year` | `date` | |
|
||||
| placeholder | 输入框提示文字 | string\|RangePicker\[] | - | |
|
||||
| popupStyle | 额外的弹出日历样式 | object | {} | |
|
||||
| size | 输入框大小,`large` 高度为 40px,`small` 为 24px,默认是 32px | string | 无 | |
|
||||
@ -92,6 +91,17 @@ moment.locale('zh-cn');
|
||||
| onOk | 点击确定按钮的回调 | function() | - | |
|
||||
| onPanelChange | 日期面板变化时的回调 | function(value, mode) | - | |
|
||||
|
||||
### YearPicker
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| defaultValue | 默认日期 | [moment](http://momentjs.com/) | 无 | |
|
||||
| defaultPickerValue | 默认面板日期 | [moment](http://momentjs.com/) | 无 | |
|
||||
| format | 展示的日期格式,配置参考 [moment.js](http://momentjs.com/) | string | "YYYY" | |
|
||||
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
|
||||
| value | 日期 | [moment](http://momentjs.com/) | 无 | |
|
||||
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: moment, dateString: string) | - | |
|
||||
|
||||
### MonthPicker
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
@ -119,8 +129,10 @@ moment.locale('zh-cn');
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| allowEmpty | 允许起始项部分为空 | \[boolean, boolean] | \[false, false] | |
|
||||
| defaultValue | 默认日期 | [moment](http://momentjs.com/)\[] | 无 | |
|
||||
| defaultPickerValue | 默认面板日期 | [moment](http://momentjs.com/)\[] | 无 | |
|
||||
| disabled | 禁用起始项 | [boolean, boolean] | 无 | |
|
||||
| disabledTime | 不可选择的时间 | function(dates: \[moment, moment\], partial: `'start'|'end'`) | 无 | |
|
||||
| format | 展示的日期格式 | string | "YYYY-MM-DD HH:mm:ss" | |
|
||||
| ranges | 预设时间范围快捷选择 | { \[range: string]: [moment](http://momentjs.com/)\[] } \| { \[range: string]: () => [moment](http://momentjs.com/)\[] } | 无 | |
|
||||
@ -131,10 +143,9 @@ moment.locale('zh-cn');
|
||||
| value | 日期 | [moment](http://momentjs.com/)\[] | 无 | |
|
||||
| onCalendarChange | 待选日期发生变化的回调 | function(dates: \[moment, moment\], dateStrings: \[string, string\]) | 无 | |
|
||||
| onChange | 日期范围发生变化的回调 | function(dates: \[moment, moment\], dateStrings: \[string, string\]) | 无 | |
|
||||
| onOk | 点击确定按钮的回调 | function(dates: [moment](http://momentjs.com/)\[]) | - | |
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-calendar-picker {
|
||||
.code-box-demo .ant-picker {
|
||||
margin: 0 8px 12px 0;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,114 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import * as moment from 'moment';
|
||||
import { TimePickerProps } from '../time-picker';
|
||||
import { tuple } from '../_util/type';
|
||||
|
||||
export interface PickerProps {
|
||||
id?: number | string;
|
||||
name?: string;
|
||||
prefixCls?: string;
|
||||
inputPrefixCls?: string;
|
||||
format?: string | string[];
|
||||
disabled?: boolean;
|
||||
allowClear?: boolean;
|
||||
className?: string;
|
||||
pickerClass?: string;
|
||||
pickerInputClass?: string;
|
||||
suffixIcon?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
popupStyle?: React.CSSProperties;
|
||||
dropdownClassName?: string;
|
||||
locale?: any;
|
||||
size?: 'large' | 'small' | 'default';
|
||||
getCalendarContainer?: (triggerNode: Element) => HTMLElement;
|
||||
open?: boolean;
|
||||
onOpenChange?: (status: boolean) => void;
|
||||
disabledDate?: (current: moment.Moment | undefined) => boolean;
|
||||
dateRender?: (current: moment.Moment, today: moment.Moment) => React.ReactNode;
|
||||
autoFocus?: boolean;
|
||||
onFocus?: React.FocusEventHandler;
|
||||
onBlur?: (e: React.SyntheticEvent) => void;
|
||||
}
|
||||
|
||||
export interface SinglePickerProps {
|
||||
value?: moment.Moment;
|
||||
defaultValue?: moment.Moment;
|
||||
defaultPickerValue?: moment.Moment;
|
||||
placeholder?: string;
|
||||
renderExtraFooter?: (mode: DatePickerMode) => React.ReactNode;
|
||||
onChange?: (date: moment.Moment | null, dateString: string) => void;
|
||||
}
|
||||
|
||||
const DatePickerModes = tuple('time', 'date', 'month', 'year', 'decade');
|
||||
export type DatePickerMode = typeof DatePickerModes[number];
|
||||
|
||||
export interface DatePickerProps extends PickerProps, SinglePickerProps {
|
||||
showTime?: TimePickerProps | boolean;
|
||||
showToday?: boolean;
|
||||
open?: boolean;
|
||||
disabledTime?: (
|
||||
current: moment.Moment | undefined,
|
||||
) => {
|
||||
disabledHours?: () => number[];
|
||||
disabledMinutes?: () => number[];
|
||||
disabledSeconds?: () => number[];
|
||||
};
|
||||
onOpenChange?: (status: boolean) => void;
|
||||
onPanelChange?: (value: moment.Moment | undefined, mode: DatePickerMode) => void;
|
||||
onOk?: (selectedTime: moment.Moment) => void;
|
||||
mode?: DatePickerMode;
|
||||
}
|
||||
|
||||
export interface MonthPickerProps extends PickerProps, SinglePickerProps {
|
||||
monthCellContentRender?: (date: moment.Moment, locale: any) => React.ReactNode;
|
||||
}
|
||||
|
||||
export type RangePickerValue =
|
||||
| undefined[]
|
||||
| [moment.Moment]
|
||||
| [undefined, moment.Moment]
|
||||
| [moment.Moment, undefined]
|
||||
| [moment.Moment, moment.Moment];
|
||||
export type RangePickerPresetRange = RangePickerValue | (() => RangePickerValue);
|
||||
|
||||
export interface RangePickerProps extends PickerProps {
|
||||
className?: string;
|
||||
tagPrefixCls?: string;
|
||||
value?: RangePickerValue;
|
||||
defaultValue?: RangePickerValue;
|
||||
defaultPickerValue?: RangePickerValue;
|
||||
timePicker?: React.ReactNode;
|
||||
onChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
|
||||
onCalendarChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
|
||||
onOk?: (selectedTime: RangePickerPresetRange) => void;
|
||||
showTime?: TimePickerProps | boolean;
|
||||
showToday?: boolean;
|
||||
ranges?: {
|
||||
[range: string]: RangePickerPresetRange;
|
||||
};
|
||||
placeholder?: [string, string];
|
||||
mode?: string | string[];
|
||||
separator?: React.ReactNode;
|
||||
disabledTime?: (
|
||||
current: moment.Moment | undefined,
|
||||
type: string,
|
||||
) => {
|
||||
disabledHours?: () => number[];
|
||||
disabledMinutes?: () => number[];
|
||||
disabledSeconds?: () => number[];
|
||||
};
|
||||
onPanelChange?: (value?: RangePickerValue, mode?: string | string[]) => void;
|
||||
renderExtraFooter?: () => React.ReactNode;
|
||||
onMouseEnter?: (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => void;
|
||||
onMouseLeave?: (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => void;
|
||||
}
|
||||
|
||||
export interface WeekPickerProps extends PickerProps, SinglePickerProps {
|
||||
// - currently no own props -
|
||||
}
|
||||
|
||||
export interface DatePickerDecorator extends React.ClassicComponentClass<DatePickerProps> {
|
||||
RangePicker: React.ClassicComponentClass<RangePickerProps>;
|
||||
MonthPicker: React.ClassicComponentClass<MonthPickerProps>;
|
||||
WeekPicker: React.ClassicComponentClass<WeekPickerProps>;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/ar_EG';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/ar_EG';
|
||||
import TimePickerLocale from '../../time-picker/locale/ar_EG';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/bg_BG';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/bg_BG';
|
||||
import TimePickerLocale from '../../time-picker/locale/bg_BG';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/ca_ES';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/ca_ES';
|
||||
import TimePickerLocale from '../../time-picker/locale/ca_ES';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/cs_CZ';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/cs_CZ';
|
||||
import TimePickerLocale from '../../time-picker/locale/cs_CZ';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/da_DK';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/da_DK';
|
||||
import TimePickerLocale from '../../time-picker/locale/da_DK';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/de_DE';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/de_DE';
|
||||
import TimePickerLocale from '../../time-picker/locale/de_DE';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/el_GR';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/el_GR';
|
||||
import TimePickerLocale from '../../time-picker/locale/el_GR';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/en_GB';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/en_GB';
|
||||
import TimePickerLocale from '../../time-picker/locale/en_GB';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,11 +1,17 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/en_US';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/en_US';
|
||||
import TimePickerLocale from '../../time-picker/locale/en_US';
|
||||
|
||||
// Merge into a locale object
|
||||
const locale = {
|
||||
lang: {
|
||||
placeholder: 'Select date',
|
||||
yearPlaceholder: 'Select year',
|
||||
monthPlaceholder: 'Select month',
|
||||
weekPlaceholder: 'Select week',
|
||||
rangePlaceholder: ['Start date', 'End date'],
|
||||
rangeYearPlaceholder: ['Start year', 'End year'],
|
||||
rangeMonthPlaceholder: ['Start month', 'End month'],
|
||||
rangeWeekPlaceholder: ['Start week', 'End week'],
|
||||
...CalendarLocale,
|
||||
},
|
||||
timePickerLocale: {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/es_ES';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/es_ES';
|
||||
import TimePickerLocale from '../../time-picker/locale/es_ES';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/et_EE';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/et_EE';
|
||||
import TimePickerLocale from '../../time-picker/locale/et_EE';
|
||||
|
||||
// 统一合并为完整的 Locale
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"lang": {
|
||||
"locale": "en_US",
|
||||
"placeholder": "Select date",
|
||||
"rangePlaceholder": ["Start date", "End date"],
|
||||
"today": "Today",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/fa_IR';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/fa_IR';
|
||||
import TimePickerLocale from '../../time-picker/locale/fa_IR';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/fi_FI';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/fi_FI';
|
||||
import TimePickerLocale from '../../time-picker/locale/fi_FI';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/fr_BE';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/fr_BE';
|
||||
import TimePickerLocale from '../../time-picker/locale/fr_BE';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/fr_FR';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/fr_FR';
|
||||
import TimePickerLocale from '../../time-picker/locale/fr_FR';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/he_IL';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/he_IL';
|
||||
import TimePickerLocale from '../../time-picker/locale/he_IL';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/hi_IN';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/hi_IN';
|
||||
import TimePickerLocale from '../../time-picker/locale/hi_IN';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/hr_HR';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/hr_HR';
|
||||
import TimePickerLocale from '../../time-picker/locale/hr_HR';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/hu_HU';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/hu_HU';
|
||||
import TimePickerLocale from '../../time-picker/locale/hu_HU';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/id_ID';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/id_ID';
|
||||
import TimePickerLocale from '../../time-picker/locale/id_ID';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/is_IS';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/is_IS';
|
||||
import TimePickerLocale from '../../time-picker/locale/is_IS';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/it_IT';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/it_IT';
|
||||
import TimePickerLocale from '../../time-picker/locale/it_IT';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/ja_JP';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/ja_JP';
|
||||
import TimePickerLocale from '../../time-picker/locale/ja_JP';
|
||||
|
||||
const locale = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/kn_IN';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/kn_IN';
|
||||
import TimePickerLocale from '../../time-picker/locale/kn_IN';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/ko_KR';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/ko_KR';
|
||||
import TimePickerLocale from '../../time-picker/locale/ko_KR';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/ku_IQ';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/ku_IQ';
|
||||
import TimePickerLocale from '../../time-picker/locale/ku_IQ';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/lv_LV';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/lv_LV';
|
||||
import TimePickerLocale from '../../time-picker/locale/lv_LV';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/mk_MK';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/mk_MK';
|
||||
import TimePickerLocale from '../../time-picker/locale/mk_MK';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/mn_MN';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/mn_MN';
|
||||
import TimePickerLocale from '../../time-picker/locale/mn_MN';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/ms_MY';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/ms_MY';
|
||||
import TimePickerLocale from '../../time-picker/locale/ms_MY';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/nb_NO';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/nb_NO';
|
||||
import TimePickerLocale from '../../time-picker/locale/nb_NO';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/nl_BE';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/nl_BE';
|
||||
import TimePickerLocale from '../../time-picker/locale/nl_BE';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/nl_NL';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/nl_NL';
|
||||
import TimePickerLocale from '../../time-picker/locale/nl_NL';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/pl_PL';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/pl_PL';
|
||||
import TimePickerLocale from '../../time-picker/locale/pl_PL';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/pt_BR';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/pt_BR';
|
||||
import TimePickerLocale from '../../time-picker/locale/pt_BR';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/pt_PT';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/pt_PT';
|
||||
import TimePickerLocale from '../../time-picker/locale/pt_PT';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/ro_RO';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/ro_RO';
|
||||
import TimePickerLocale from '../../time-picker/locale/ro_RO';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Created by Andrey Gayvoronsky on 13/04/16.
|
||||
*/
|
||||
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/ru_RU';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/ru_RU';
|
||||
import TimePickerLocale from '../../time-picker/locale/ru_RU';
|
||||
|
||||
const locale = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/sk_SK';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/sk_SK';
|
||||
import TimePickerLocale from '../../time-picker/locale/sk_SK';
|
||||
|
||||
// 统一合并为完整的 Locale
|
||||
|
@ -3,6 +3,7 @@ import TimePickerLocale from '../../time-picker/locale/sl_SI';
|
||||
// Merge into a locale object
|
||||
const locale = {
|
||||
lang: {
|
||||
locale: 'sl',
|
||||
placeholder: 'Izberite datum',
|
||||
rangePlaceholder: ['Začetni datum', 'Končni datum'],
|
||||
today: 'Danes',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/sr_RS';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/sr_RS';
|
||||
import TimePickerLocale from '../../time-picker/locale/sr_RS';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/sv_SE';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/sv_SE';
|
||||
import TimePickerLocale from '../../time-picker/locale/sv_SE';
|
||||
|
||||
const locale = {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Tamil Locale added to rc-calendar
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/ta_IN';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/ta_IN';
|
||||
import TimePickerLocale from '../../time-picker/locale/ta_IN';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/th_TH';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/th_TH';
|
||||
import TimePickerLocale from '../../time-picker/locale/th_TH';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/tr_TR';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/tr_TR';
|
||||
import TimePickerLocale from '../../time-picker/locale/tr_TR';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/uk_UA';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/uk_UA';
|
||||
import TimePickerLocale from '../../time-picker/locale/uk_UA';
|
||||
|
||||
const locale = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/vi_VN';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/vi_VN';
|
||||
import TimePickerLocale from '../../time-picker/locale/vi_VN';
|
||||
|
||||
// Merge into a locale object
|
||||
|
@ -1,10 +1,16 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/zh_CN';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/zh_CN';
|
||||
import TimePickerLocale from '../../time-picker/locale/zh_CN';
|
||||
|
||||
const locale = {
|
||||
lang: {
|
||||
placeholder: '请选择日期',
|
||||
yearPlaceholder: '请选择年份',
|
||||
monthPlaceholder: '请选择月份',
|
||||
weekPlaceholder: '请选择周',
|
||||
rangePlaceholder: ['开始日期', '结束日期'],
|
||||
rangeYearPlaceholder: ['开始年份', '结束年份'],
|
||||
rangeMonthPlaceholder: ['开始月份', '结束月份'],
|
||||
rangeWeekPlaceholder: ['开始周', '结束周'],
|
||||
...CalendarLocale,
|
||||
},
|
||||
timePickerLocale: {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import CalendarLocale from 'rc-calendar/lib/locale/zh_TW';
|
||||
import CalendarLocale from 'rc-picker/lib/locale/zh_TW';
|
||||
import TimePickerLocale from '../../time-picker/locale/zh_TW';
|
||||
|
||||
const locale = {
|
||||
|
@ -1,402 +0,0 @@
|
||||
.calendarLeftArrow() {
|
||||
height: 100%;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
vertical-align: middle;
|
||||
border: 0 solid #aaa;
|
||||
border-width: 1.5px 0 0 1.5px;
|
||||
border-radius: 1px;
|
||||
transform: rotate(-45deg) scale(0.8);
|
||||
transition: all 0.3s;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:hover::before,
|
||||
&:hover::after {
|
||||
border-color: @text-color;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.calendarLeftDoubleArrow() {
|
||||
.calendarLeftArrow;
|
||||
|
||||
&::after {
|
||||
position: relative;
|
||||
left: -3px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.calendarRightArrow() {
|
||||
.calendarLeftArrow;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
transform: rotate(135deg) scale(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.calendarRightDoubleArrow() {
|
||||
.calendarRightArrow;
|
||||
|
||||
&::before {
|
||||
position: relative;
|
||||
left: 3px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.calendarPanelHeader(@calendar-prefix-cls) {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
border-bottom: @border-width-base @border-style-base @border-color-split;
|
||||
user-select: none;
|
||||
|
||||
a:hover {
|
||||
color: @link-hover-color;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-century-select,
|
||||
.@{calendar-prefix-cls}-decade-select,
|
||||
.@{calendar-prefix-cls}-year-select,
|
||||
.@{calendar-prefix-cls}-month-select {
|
||||
display: inline-block;
|
||||
padding: 0 2px;
|
||||
color: @heading-color;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-century-select-arrow,
|
||||
.@{calendar-prefix-cls}-decade-select-arrow,
|
||||
.@{calendar-prefix-cls}-year-select-arrow,
|
||||
.@{calendar-prefix-cls}-month-select-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-prev-century-btn,
|
||||
.@{calendar-prefix-cls}-next-century-btn,
|
||||
.@{calendar-prefix-cls}-prev-decade-btn,
|
||||
.@{calendar-prefix-cls}-next-decade-btn,
|
||||
.@{calendar-prefix-cls}-prev-month-btn,
|
||||
.@{calendar-prefix-cls}-next-month-btn,
|
||||
.@{calendar-prefix-cls}-prev-year-btn,
|
||||
.@{calendar-prefix-cls}-next-year-btn {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
color: @text-color-secondary;
|
||||
font-size: 16px;
|
||||
font-family: Arial, 'Hiragino Sans GB', 'Microsoft Yahei', 'Microsoft Sans Serif', sans-serif;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-prev-century-btn,
|
||||
.@{calendar-prefix-cls}-prev-decade-btn,
|
||||
.@{calendar-prefix-cls}-prev-year-btn {
|
||||
left: 7px;
|
||||
.calendarLeftDoubleArrow;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-next-century-btn,
|
||||
.@{calendar-prefix-cls}-next-decade-btn,
|
||||
.@{calendar-prefix-cls}-next-year-btn {
|
||||
right: 7px;
|
||||
.calendarRightDoubleArrow;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-prev-month-btn {
|
||||
left: 29px;
|
||||
.calendarLeftArrow;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-next-month-btn {
|
||||
right: 29px;
|
||||
.calendarRightArrow;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-selected-cell() {
|
||||
.@{calendar-prefix-cls}-date {
|
||||
color: @text-color-inverse;
|
||||
background: @primary-color;
|
||||
border: @border-width-base @border-style-base transparent;
|
||||
|
||||
&:hover {
|
||||
background: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls} {
|
||||
position: relative;
|
||||
width: 280px;
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-base;
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
background-color: @calendar-bg;
|
||||
background-clip: padding-box;
|
||||
border: @border-width-base @border-style-base @calendar-border-color;
|
||||
border-radius: @border-radius-base;
|
||||
outline: none;
|
||||
box-shadow: @box-shadow-base;
|
||||
|
||||
&-input-wrap {
|
||||
height: 34px;
|
||||
padding: 6px @control-padding-horizontal - 2px;
|
||||
border-bottom: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
|
||||
&-input {
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
color: @input-color;
|
||||
background: @calendar-input-bg;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
cursor: auto;
|
||||
.placeholder;
|
||||
}
|
||||
|
||||
&-week-number {
|
||||
width: 286px;
|
||||
|
||||
&-cell {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
.calendarPanelHeader(@calendar-prefix-cls);
|
||||
}
|
||||
|
||||
&-body {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
background-color: transparent;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
text-align: center;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&-calendar-table {
|
||||
margin-bottom: 0;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
&-column-header {
|
||||
width: 33px;
|
||||
padding: 6px 0;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
.@{calendar-prefix-cls}-column-header-inner {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
&-week-number-header {
|
||||
.@{calendar-prefix-cls}-column-header-inner {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-cell {
|
||||
height: 30px;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
&-date {
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
color: @text-color;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
background: transparent;
|
||||
border: @border-width-base @border-style-base transparent;
|
||||
border-radius: @border-radius-base;
|
||||
transition: background 0.3s ease;
|
||||
|
||||
&-panel {
|
||||
position: relative;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: @item-hover-bg;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: @text-color-inverse;
|
||||
background: @primary-5;
|
||||
}
|
||||
}
|
||||
|
||||
&-today &-date {
|
||||
color: @primary-color;
|
||||
font-weight: bold;
|
||||
border-color: @primary-color;
|
||||
}
|
||||
|
||||
&-selected-day &-date {
|
||||
background: @primary-2;
|
||||
}
|
||||
|
||||
&-last-month-cell &-date,
|
||||
&-next-month-btn-day &-date {
|
||||
&,
|
||||
&:hover {
|
||||
color: @disabled-color;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&-disabled-cell &-date {
|
||||
position: relative;
|
||||
width: auto;
|
||||
color: @disabled-color;
|
||||
background: @disabled-bg;
|
||||
border: @border-width-base @border-style-base transparent;
|
||||
border-radius: 0;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background: @disabled-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&-disabled-cell&-selected-day &-date::before {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: 5px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: @border-radius-base;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&-disabled-cell&-today &-date {
|
||||
position: relative;
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: 5px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: @border-width-base @border-style-base @disabled-color;
|
||||
border-radius: @border-radius-base;
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
|
||||
&-disabled-cell-first-of-row &-date {
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
&-disabled-cell-last-of-row &-date {
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
padding: 0 12px;
|
||||
line-height: 38px;
|
||||
border-top: @border-width-base @border-style-base @border-color-split;
|
||||
&:empty {
|
||||
border-top: 0;
|
||||
}
|
||||
&-btn {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
&-extra {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-today-btn,
|
||||
.@{calendar-prefix-cls}-clear-btn {
|
||||
display: inline-block;
|
||||
margin: 0 0 0 8px;
|
||||
text-align: center;
|
||||
&-disabled {
|
||||
color: @disabled-color;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
&:only-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-clear-btn {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 5px;
|
||||
display: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
text-indent: -76px;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-clear-btn::after {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
color: @disabled-color;
|
||||
font-size: @font-size-base;
|
||||
line-height: 1;
|
||||
text-indent: 43px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-clear-btn:hover::after {
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-ok-btn {
|
||||
.btn;
|
||||
.btn-primary;
|
||||
.button-size(@btn-height-sm; @btn-padding-sm; @font-size-base; @border-radius-base);
|
||||
|
||||
line-height: @btn-height-sm - 2px;
|
||||
|
||||
.button-disabled();
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
.@{calendar-prefix-cls}-month {
|
||||
.@{calendar-prefix-cls}-month-header-wrap {
|
||||
position: relative;
|
||||
height: 288px;
|
||||
}
|
||||
.@{calendar-prefix-cls}-month-panel,
|
||||
.@{calendar-prefix-cls}-year-panel {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
@import '../../button/style/mixin';
|
||||
|
||||
.@{calendar-prefix-cls}-picker-container {
|
||||
.reset-component;
|
||||
|
||||
position: absolute;
|
||||
z-index: @zindex-picker;
|
||||
font-family: @font-family;
|
||||
|
||||
&.slide-up-enter.slide-up-enter-active&-placement-topLeft,
|
||||
&.slide-up-enter.slide-up-enter-active&-placement-topRight,
|
||||
&.slide-up-appear.slide-up-appear-active&-placement-topLeft,
|
||||
&.slide-up-appear.slide-up-appear-active&-placement-topRight {
|
||||
animation-name: antSlideDownIn;
|
||||
}
|
||||
|
||||
&.slide-up-enter.slide-up-enter-active&-placement-bottomLeft,
|
||||
&.slide-up-enter.slide-up-enter-active&-placement-bottomRight,
|
||||
&.slide-up-appear.slide-up-appear-active&-placement-bottomLeft,
|
||||
&.slide-up-appear.slide-up-appear-active&-placement-bottomRight {
|
||||
animation-name: antSlideUpIn;
|
||||
}
|
||||
|
||||
&.slide-up-leave.slide-up-leave-active&-placement-topLeft,
|
||||
&.slide-up-leave.slide-up-leave-active&-placement-topRight {
|
||||
animation-name: antSlideDownOut;
|
||||
}
|
||||
|
||||
&.slide-up-leave.slide-up-leave-active&-placement-bottomLeft,
|
||||
&.slide-up-leave.slide-up-leave-active&-placement-bottomRight {
|
||||
animation-name: antSlideUpOut;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-picker {
|
||||
.reset-component;
|
||||
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
outline: none;
|
||||
cursor: text;
|
||||
transition: opacity 0.3s;
|
||||
|
||||
&-input {
|
||||
outline: none;
|
||||
|
||||
&.@{ant-prefix}-input {
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
}
|
||||
|
||||
&-input.@{ant-prefix}-input-sm {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&:hover &-input:not(.@{ant-prefix}-input-disabled) {
|
||||
border-color: @input-hover-border-color;
|
||||
}
|
||||
|
||||
&:focus &-input:not(.@{ant-prefix}-input-disabled) {
|
||||
.active();
|
||||
}
|
||||
|
||||
&-clear,
|
||||
&-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: @control-padding-horizontal;
|
||||
z-index: 1;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-top: -7px;
|
||||
font-size: @font-size-sm;
|
||||
line-height: 14px;
|
||||
transition: all 0.3s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
&-clear {
|
||||
z-index: 2;
|
||||
color: @disabled-color;
|
||||
font-size: @font-size-base;
|
||||
background: @input-bg;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
&:hover {
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover &-clear {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
display: inline-block;
|
||||
color: @disabled-color;
|
||||
font-size: @font-size-base;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&-small &-clear,
|
||||
&-small &-icon {
|
||||
right: @control-padding-horizontal-sm;
|
||||
}
|
||||
}
|
@ -1,248 +0,0 @@
|
||||
@input-box-height: 34px;
|
||||
|
||||
.@{calendar-prefix-cls}-range-picker-input {
|
||||
width: 44%;
|
||||
height: 99%;
|
||||
text-align: center;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
.placeholder();
|
||||
|
||||
&[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-range-picker-separator {
|
||||
display: inline-block;
|
||||
min-width: 10px;
|
||||
height: 100%;
|
||||
color: @text-color-secondary;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-range {
|
||||
width: 552px;
|
||||
overflow: hidden;
|
||||
|
||||
.@{calendar-prefix-cls}-date-panel {
|
||||
&::after {
|
||||
display: block;
|
||||
clear: both;
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
content: '.';
|
||||
}
|
||||
}
|
||||
&-part {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
&-left {
|
||||
float: left;
|
||||
.@{calendar-prefix-cls} {
|
||||
&-time-picker-inner {
|
||||
border-right: 1px solid @border-color-split;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-right {
|
||||
float: right;
|
||||
.@{calendar-prefix-cls} {
|
||||
&-time-picker-inner {
|
||||
border-left: 1px solid @border-color-split;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-middle {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
z-index: 1;
|
||||
height: @input-box-height;
|
||||
margin: 1px 0 0 0;
|
||||
padding: 0 200px 0 0;
|
||||
color: @text-color-secondary;
|
||||
line-height: @input-box-height;
|
||||
text-align: center;
|
||||
transform: translateX(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-right .@{calendar-prefix-cls}-date-input-wrap {
|
||||
margin-left: -90px;
|
||||
}
|
||||
|
||||
&.@{calendar-prefix-cls}-time &-middle {
|
||||
padding: 0 10px 0 0;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-today
|
||||
:not(.@{calendar-prefix-cls}-disabled-cell)
|
||||
:not(.@{calendar-prefix-cls}-last-month-cell)
|
||||
:not(.@{calendar-prefix-cls}-next-month-btn-day) {
|
||||
.@{calendar-prefix-cls}-date {
|
||||
color: @primary-color;
|
||||
background: @primary-2;
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-selected-start-date,
|
||||
.@{calendar-prefix-cls}-selected-end-date {
|
||||
.calendar-selected-cell;
|
||||
}
|
||||
|
||||
&.@{calendar-prefix-cls}-time &-right .@{calendar-prefix-cls}-date-input-wrap {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-input-wrap {
|
||||
position: relative;
|
||||
height: @input-box-height;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-input,
|
||||
.@{calendar-timepicker-prefix-cls}-input {
|
||||
.input;
|
||||
height: @input-height-sm;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
line-height: @input-height-sm;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-timepicker-prefix-cls}-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.@{calendar-prefix-cls}-week-number {
|
||||
width: 574px;
|
||||
|
||||
.@{calendar-prefix-cls}-range-part {
|
||||
width: 286px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-year-panel,
|
||||
.@{calendar-prefix-cls}-month-panel,
|
||||
.@{calendar-prefix-cls}-decade-panel {
|
||||
top: @input-box-height;
|
||||
}
|
||||
.@{calendar-prefix-cls}-month-panel .@{calendar-prefix-cls}-year-panel {
|
||||
top: 0;
|
||||
}
|
||||
.@{calendar-prefix-cls}-decade-panel-table,
|
||||
.@{calendar-prefix-cls}-year-panel-table,
|
||||
.@{calendar-prefix-cls}-month-panel-table {
|
||||
height: 208px;
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-in-range-cell {
|
||||
position: relative;
|
||||
border-radius: 0;
|
||||
> div {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
bottom: 4px;
|
||||
left: 0;
|
||||
display: block;
|
||||
background: @calendar-item-active-bg;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-footer-extra {
|
||||
float: left;
|
||||
}
|
||||
|
||||
// `div` for selector specificity
|
||||
div&-quick-selector {
|
||||
text-align: left;
|
||||
|
||||
> a {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls},
|
||||
.@{calendar-prefix-cls}-month-panel,
|
||||
.@{calendar-prefix-cls}-year-panel,
|
||||
.@{calendar-prefix-cls}-decade-panel {
|
||||
&-header {
|
||||
border-bottom: 0;
|
||||
}
|
||||
&-body {
|
||||
border-top: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
}
|
||||
|
||||
&.@{calendar-prefix-cls}-time {
|
||||
.@{calendar-timepicker-prefix-cls} {
|
||||
top: 68px;
|
||||
z-index: 2; // cover .ant-calendar-range .ant-calendar-in-range-cell > div (z-index: 1)
|
||||
width: 100%;
|
||||
height: 207px;
|
||||
&-panel {
|
||||
height: 267px;
|
||||
margin-top: -34px;
|
||||
}
|
||||
|
||||
&-inner {
|
||||
height: 100%;
|
||||
padding-top: 40px;
|
||||
background: none;
|
||||
}
|
||||
|
||||
&-combobox {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
background-color: @component-background;
|
||||
border-top: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
&-select {
|
||||
height: 100%;
|
||||
ul {
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.@{calendar-prefix-cls}-footer .@{calendar-prefix-cls}-time-picker-btn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.@{calendar-prefix-cls}-today-btn {
|
||||
height: 22px;
|
||||
margin: 8px 12px;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
&-with-ranges.@{calendar-prefix-cls}-time .@{calendar-timepicker-prefix-cls} {
|
||||
height: 233px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{calendar-prefix-cls}-range.@{calendar-prefix-cls}-show-time-picker {
|
||||
.@{calendar-prefix-cls}-body {
|
||||
border-top-color: transparent;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user