Skip to content

Commit

Permalink
chore: optimize disabledDate logic (ant-design#191)
Browse files Browse the repository at this point in the history
co-author <ayn1914420862@gmail.com>
  • Loading branch information
kerm1it authored Dec 30, 2020
1 parent 2db525e commit 42f853b
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 21 deletions.
64 changes: 64 additions & 0 deletions examples/disabledDate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import type { Moment } from 'moment';
import moment from 'moment';
import Picker from '../src/Picker';
import momentGenerateConfig from '../src/generate/moment';
import enUS from '../src/locale/en_US';
import '../assets/index.less';

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

const onSelect = (newValue: Moment) => {
console.log('Select:', newValue);
};

const onChange = (newValue: Moment | null, formatString?: string) => {
console.log('Change:', newValue, formatString);
setValue(newValue);
};

function disabledDateBeforeToday(current: Moment) {
return current <= moment().endOf('day');
}

function disabledDateAfterToday(current: Moment) {
return current >= moment().endOf('day');
}

function disabledDateAfterTodayAndBeforeLastYear(current) {
return current >= moment().startOf('day') || current < moment().subtract(1, 'years');
}

const sharedProps = {
generateConfig: momentGenerateConfig,
value,
onSelect,
onChange,
};

return (
<div style={{ paddingBottom: '20px' }}>
<h1>Value: {value ? value.format('YYYY-MM-DD HH:mm:ss') : 'null'}</h1>
<h2>Date Mode</h2>
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
<div style={{ margin: '0 8px' }}>
<h3>Before Today</h3>
<Picker<Moment> {...sharedProps} disabledDate={disabledDateBeforeToday} locale={enUS} />
</div>
<div style={{ margin: '0 8px' }}>
<h3>After Today</h3>
<Picker<Moment> {...sharedProps} disabledDate={disabledDateAfterToday} locale={enUS} />
</div>
<div style={{ margin: '0 8px' }}>
<h3>After Today or Before last year</h3>
<Picker<Moment>
{...sharedProps}
disabledDate={disabledDateAfterTodayAndBeforeLastYear}
locale={enUS}
/>
</div>
</div>
</div>
);
};
3 changes: 2 additions & 1 deletion src/PanelContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import type { OnSelect } from './interface';
import type { OnSelect, PanelMode } from './interface';

export type ContextOperationRefProps = {
onKeyDown?: (e: React.KeyboardEvent<HTMLElement>) => boolean;
Expand All @@ -18,6 +18,7 @@ export type PanelContextProps = {
onSelect?: OnSelect<any>;
hideRanges?: boolean;
open?: boolean;
mode?: PanelMode;

/** Only used for TimePicker and this is a deprecated prop */
defaultOpenValue?: any;
Expand Down
17 changes: 11 additions & 6 deletions src/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,12 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
};
}

const [hoverValue, onEnter, onLeave] = useHoverValue(text, {
formatList,
generateConfig,
locale,
});

// ============================= Panel =============================
const panelProps = {
// Remove `picker` & `format` here since TimePicker is little different with other panel
Expand All @@ -384,6 +390,11 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
setSelectedValue(date);
}}
direction={direction}
onPanelChange={(viewDate, mode) => {
const { onPanelChange } = props;
onLeave(true);
onPanelChange?.(viewDate, mode);
}}
/>
);

Expand Down Expand Up @@ -446,12 +457,6 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
};
const popupPlacement = direction === 'rtl' ? 'bottomRight' : 'bottomLeft';

const [hoverValue, onEnter, onLeave] = useHoverValue(text, {
formatList,
generateConfig,
locale,
});

return (
<PanelContext.Provider
value={{
Expand Down
3 changes: 2 additions & 1 deletion src/PickerPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
onViewDateChange: setViewDate,
sourceMode,
onPanelChange: onInternalPanelChange,
disabledDate: mergedMode !== 'decade' ? disabledDate : undefined,
disabledDate,
};
delete pickerProps.onChange;
delete pickerProps.onSelect;
Expand Down Expand Up @@ -518,6 +518,7 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
<PanelContext.Provider
value={{
...panelContext,
mode: mergedMode,
hideHeader: 'hideHeader' in props ? hideHeader : panelContext.hideHeader,
hidePrevBtn: inRange && panelPosition === 'right',
hideNextBtn: inRange && panelPosition === 'left',
Expand Down
7 changes: 7 additions & 0 deletions src/RangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,13 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
locale={locale}
tabIndex={-1}
onPanelChange={(date, newMode) => {
// clear hover value when panel change
if (mergedActivePickerIndex === 0) {
onStartLeave(true);
}
if (mergedActivePickerIndex === 1) {
onEndLeave(true);
}
triggerModesChange(
updateValues(mergedModes, newMode, mergedActivePickerIndex),
updateValues(selectedValue, date, mergedActivePickerIndex),
Expand Down
5 changes: 1 addition & 4 deletions src/panels/DecadePanel/DecadeBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type YearBodyProps<DateType> = {

function DecadeBody<DateType>(props: YearBodyProps<DateType>) {
const DECADE_UNIT_DIFF_DES = DECADE_UNIT_DIFF - 1;
const { prefixCls, viewDate, generateConfig, disabledDate } = props;
const { prefixCls, viewDate, generateConfig } = props;

const cellPrefixCls = `${prefixCls}-cell`;

Expand All @@ -35,13 +35,10 @@ function DecadeBody<DateType>(props: YearBodyProps<DateType>) {
);

const getCellClassName = (date: DateType) => {
const disabled = disabledDate && disabledDate(date);

const startDecadeNumber = generateConfig.getYear(date);
const endDecadeNumber = startDecadeNumber + DECADE_UNIT_DIFF_DES;

return {
[`${cellPrefixCls}-disabled`]: disabled,
[`${cellPrefixCls}-in-view`]:
startDecadeYear <= startDecadeNumber && endDecadeNumber <= endDecadeYear,
[`${cellPrefixCls}-selected`]: startDecadeNumber === decadeYearNumber,
Expand Down
17 changes: 13 additions & 4 deletions src/panels/PanelBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PanelContext from '../PanelContext';
import type { GenerateConfig } from '../generate';
import { getLastDay } from '../utils/timeUtil';
import type { PanelMode } from '../interface';
import { getCellDateDisabled } from '../utils/dateUtil';

export type PanelBodyProps<DateType> = {
prefixCls: string;
Expand Down Expand Up @@ -46,7 +47,7 @@ export default function PanelBody<DateType>({
titleCell,
headerCells,
}: PanelBodyProps<DateType>) {
const { onDateMouseEnter, onDateMouseLeave } = React.useContext(PanelContext);
const { onDateMouseEnter, onDateMouseLeave, mode } = React.useContext(PanelContext);

const cellPrefixCls = `${prefixCls}-cell`;

Expand All @@ -60,7 +61,12 @@ export default function PanelBody<DateType>({
for (let j = 0; j < colNum; j += 1) {
const offset = i * colNum + j;
const currentDate = getCellDate(baseDate, offset);
const disabled = disabledDate && disabledDate(currentDate);
const disabled = getCellDateDisabled({
cellDate: currentDate,
mode,
disabledDate,
generateConfig,
});

if (j === 0) {
rowStartDate = currentDate;
Expand All @@ -78,8 +84,11 @@ export default function PanelBody<DateType>({
title={title}
className={classNames(cellPrefixCls, {
[`${cellPrefixCls}-disabled`]: disabled,
[`${cellPrefixCls}-start`]: getCellText(currentDate) === 1 || picker === 'year' && Number(title) % 10 === 0,
[`${cellPrefixCls}-end`]: title === getLastDay(generateConfig, currentDate) || picker === 'year' && Number(title) % 10 === 9,
[`${cellPrefixCls}-start`]:
getCellText(currentDate) === 1 || (picker === 'year' && Number(title) % 10 === 0),
[`${cellPrefixCls}-end`]:
title === getLastDay(generateConfig, currentDate) ||
(picker === 'year' && Number(title) % 10 === 9),
...getCellClassName(currentDate),
})}
onClick={() => {
Expand Down
93 changes: 92 additions & 1 deletion src/utils/dateUtil.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DECADE_UNIT_DIFF } from '../panels/DecadePanel/index';
import type { PanelMode, NullableDateType, PickerMode, Locale, CustomFormat } from '../interface';
import type { GenerateConfig } from '../generate';
import type { NullableDateType, PickerMode, Locale, CustomFormat } from '../interface';

export const WEEK_DAY_COUNT = 7;

Expand Down Expand Up @@ -228,3 +229,93 @@ export function parseValue<DateType>(

return generateConfig.locale.parse(locale.locale, value, formatList as string[]);
}

// eslint-disable-next-line consistent-return
export function getCellDateDisabled<DateType>({
cellDate,
mode,
disabledDate,
generateConfig,
}: {
cellDate: DateType;
mode: Omit<PanelMode, 'time'>;
generateConfig: GenerateConfig<DateType>;
disabledDate?: (date: DateType) => boolean;
}): boolean {
if (!disabledDate) return false;
// Whether cellDate is disabled in range
const getDisabledFromRange = (
currentMode: 'date' | 'month' | 'year',
start: number,
end: number,
) => {
let current = start;
while (current <= end) {
let date: DateType;
switch (currentMode) {
case 'date': {
date = generateConfig.setDate(cellDate, current);
if (!disabledDate(date)) {
return false;
}
break;
}
case 'month': {
date = generateConfig.setMonth(cellDate, current);
if (
!getCellDateDisabled({
cellDate: date,
mode: 'month',
generateConfig,
disabledDate,
})
) {
return false;
}
break;
}
case 'year': {
date = generateConfig.setYear(cellDate, current);
if (
!getCellDateDisabled({
cellDate: date,
mode: 'year',
generateConfig,
disabledDate,
})
) {
return false;
}
break;
}
}
current += 1;
}
return true;
};
switch (mode) {
case 'date':
case 'week': {
return disabledDate(cellDate);
}
case 'month': {
const startDate = 1;
const endDate = generateConfig.getDate(generateConfig.getEndDate(cellDate));
return getDisabledFromRange('date', startDate, endDate);
}
case 'quarter': {
const startMonth = Math.floor(generateConfig.getMonth(cellDate) / 3) * 3;
const endMonth = startMonth + 2;
return getDisabledFromRange('month', startMonth, endMonth);
}
case 'year': {
return getDisabledFromRange('month', 0, 11);
}
case 'decade': {
const year = generateConfig.getYear(cellDate);
const startYear = Math.floor(year / DECADE_UNIT_DIFF) * DECADE_UNIT_DIFF;
const endYear = startYear + DECADE_UNIT_DIFF - 1;
return getDisabledFromRange('year', startYear, endYear);
}
}
}
Loading

0 comments on commit 42f853b

Please sign in to comment.