Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ render(<Picker />, mountNode);
| onCalendarChange | Function(value:[moment], formatString: [string, string]) | | a callback function, can be executed when the start time or the end time of the range is changing |
| direction | String: ltr or rtl | | Layout direction of picker component, it supports RTL direction too. |
| order | Boolean | true | (TimeRangePicker only) `false` to disable auto order |
| order | Boolean | true | (TimeRangePicker only) `false` to disable auto order |
| disabledPickerStartDate | Function(startDate: moment):boolean | | disable start date |
| disabledPickerEndDate | Function(endDate: moment):boolean | | disable end date |

### showTime-options

Expand Down
48 changes: 25 additions & 23 deletions examples/range.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ function formatDate(date: Moment | null) {
}

export default () => {
const [value, setValue] = React.useState<
[Moment | null, Moment | null] | null
>([defaultStartValue, defaultEndValue]);
const [value, setValue] = React.useState<[Moment | null, Moment | null] | null>([
defaultStartValue,
defaultEndValue,
]);

const onChange = (
newValue: [Moment | null, Moment | null] | null,
formatStrings?: string[],
) => {
const onChange = (newValue: [Moment | null, Moment | null] | null, formatStrings?: string[]) => {
console.log('Change:', newValue, formatStrings);
setValue(newValue);
};
Expand All @@ -33,6 +31,12 @@ export default () => {
console.log('Calendar Change:', newValue, formatStrings);
};

const onChangeWithAllDatesEnabled = (newValue: [Moment | null, Moment | null] | null) => {
if (newValue[0] && newValue[1] && newValue[1].isBefore(newValue[0])) {
setValue([newValue[1], newValue[0]]);
}
};

const sharedProps = {
generateConfig: momentGenerateConfig,
value,
Expand All @@ -44,10 +48,7 @@ export default () => {

return (
<div>
<h2>
Value:{' '}
{value ? `${formatDate(value[0])} ~ ${formatDate(value[1])}` : 'null'}
</h2>
<h2>Value: {value ? `${formatDate(value[0])} ~ ${formatDate(value[1])}` : 'null'}</h2>

<div style={{ display: 'flex', flexWrap: 'wrap' }}>
<div style={{ margin: '0 8px' }}>
Expand Down Expand Up @@ -132,21 +133,11 @@ export default () => {

<div style={{ margin: '0 8px' }}>
<h3>Start disabled</h3>
<RangePicker<Moment>
{...sharedProps}
locale={zhCN}
allowClear
disabled={[true, false]}
/>
<RangePicker<Moment> {...sharedProps} locale={zhCN} allowClear disabled={[true, false]} />
</div>
<div style={{ margin: '0 8px' }}>
<h3>End disabled</h3>
<RangePicker<Moment>
{...sharedProps}
locale={zhCN}
allowClear
disabled={[false, true]}
/>
<RangePicker<Moment> {...sharedProps} locale={zhCN} allowClear disabled={[false, true]} />
</div>

<div style={{ margin: '0 8px' }}>
Expand All @@ -161,6 +152,17 @@ export default () => {
renderExtraFooter={() => <div>extra footer</div>}
/>
</div>
<div style={{ margin: '0 8px' }}>
<h3>All dates enabled</h3>
<RangePicker<Moment>
{...sharedProps}
onChange={onChangeWithAllDatesEnabled}
value={undefined}
locale={zhCN}
disabledPickerEndDate={() => false}
disabledPickerStartDate={() => false}
/>
</div>
</div>
</div>
);
Expand Down
14 changes: 10 additions & 4 deletions src/RangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ export interface RangePickerSharedProps<DateType> {
direction?: 'ltr' | 'rtl';
/** @private Internal control of active picker. Do not use since it's private usage */
activePickerIndex?: 0 | 1;
disabledPickerStartDate?: (startDate: DateType) => boolean;
disabledPickerEndDate?: (endDate: DateType) => boolean;
}

type OmitPickerProps<Props> = Omit<
Expand Down Expand Up @@ -182,6 +184,8 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
order,
direction,
activePickerIndex,
disabledPickerStartDate,
disabledPickerEndDate,
} = props as MergedRangePickerProps<DateType>;

const needConfirmButton: boolean = (picker === 'date' && !!showTime) || picker === 'time';
Expand Down Expand Up @@ -288,6 +292,8 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
disabled: mergedDisabled,
disabledDate,
generateConfig,
disabledPickerStartDate,
disabledPickerEndDate,
});

// ============================= Open ==============================
Expand Down Expand Up @@ -332,13 +338,13 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {

let values = newValue;
const startValue = getValue(values, 0);
let endValue = getValue(values, 1);
const endValue = getValue(values, 1);

if (startValue && endValue && generateConfig.isAfter(startValue, endValue)) {
if (!isSameDate(generateConfig, startValue, endValue)) {
// Clean up end date when start date is after end date
values = [startValue, null];
endValue = null;
// if user selects start date while end date picker is opened
// then set the start date at proper position
values = [startValue, endValue];
} else if (picker !== 'time' || order !== false) {
// Reorder when in same date
values = reorderValues(values, generateConfig);
Expand Down
19 changes: 13 additions & 6 deletions src/hooks/useRangeDisabled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,44 @@ export default function useRangeDisabled<DateType>({
disabledDate,
disabled,
generateConfig,
disabledPickerStartDate,
disabledPickerEndDate,
}: {
picker: PickerMode;
selectedValue: RangeValue<DateType>;
disabledDate?: (date: DateType) => boolean;
disabled: [boolean, boolean];
locale: Locale;
generateConfig: GenerateConfig<DateType>;
disabledPickerStartDate?: (startDateToDisable: DateType) => boolean;
disabledPickerEndDate?: (endDateToDisable: DateType) => boolean;
}) {
const startDate = getValue(selectedValue, 0);
const endDate = getValue(selectedValue, 1);

const disabledStartDate = React.useCallback(
(date: DateType) => {
if (disabledPickerStartDate) {
return disabledPickerStartDate(date);
}
if (disabledDate && disabledDate(date)) {
return true;
}

if (disabled[1] && endDate) {
return (
!isSameDate(generateConfig, date, endDate) &&
generateConfig.isAfter(date, endDate)
);
return !isSameDate(generateConfig, date, endDate) && generateConfig.isAfter(date, endDate);
}

return false;
},
[disabledDate, disabled[1], endDate],
[disabledDate, disabled[1], endDate, disabledPickerStartDate],
);

const disableEndDate = React.useCallback(
(date: DateType) => {
if (disabledPickerEndDate) {
return disabledPickerEndDate(date);
}
if (disabledDate && disabledDate(date)) {
return true;
}
Expand All @@ -59,7 +66,7 @@ export default function useRangeDisabled<DateType>({

return false;
},
[disabledDate, startDate, picker],
[disabledDate, startDate, picker, disabledPickerEndDate],
);

// Handle week date disabled
Expand Down
34 changes: 31 additions & 3 deletions tests/range.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ describe('Picker.Range', () => {
expect(wrapper.findCell(11).hasClass('rc-picker-cell-disabled')).toBeFalsy();
});

it('Reset when startDate is after endDate', () => {
it('Set correct start and end date', () => {
const onChange = jest.fn();
const wrapper = mount(<MomentRangePicker onChange={onChange} />);

Expand All @@ -182,8 +182,8 @@ describe('Picker.Range', () => {

wrapper.openPicker(0);
wrapper.selectCell(23);
expect(onChange).not.toHaveBeenCalled();
matchValues(wrapper, '1990-09-23', '');
expect(onChange).toHaveBeenCalled();
matchValues(wrapper, '1990-09-07', '1990-09-23');
});

it('allowEmpty', () => {
Expand Down Expand Up @@ -1063,4 +1063,32 @@ describe('Picker.Range', () => {
.props().id,
).toEqual('bamboo');
});

it('all dates inside endDate picker should be clickable', () => {
const onChange = jest.fn();

const wrapper = mount(
<MomentRangePicker
onChange={onChange}
disabledPickerEndDate={() => false}
disabledPickerStartDate={() => false}
/>,
);

let cellNode: Wrapper;

// Start date
wrapper.openPicker();
wrapper.selectCell(23);

// End date
cellNode = wrapper.selectCell(11);
expect(cellNode.hasClass('rc-picker-cell-disabled')).toBeFalsy();
expect(onChange).toHaveBeenCalled();

// Click origin disabled date
cellNode = wrapper.selectCell(28);
expect(cellNode.hasClass('rc-picker-cell-disabled')).toBeFalsy();
expect(onChange).toHaveBeenCalled();
});
});