From 4f0b523fb00e3569c9bf169cc3aec5aea3a34e7e Mon Sep 17 00:00:00 2001 From: ZhaoChen Date: Thu, 4 Nov 2021 19:41:37 +0800 Subject: [PATCH] feat(DateRangePicker): add new DateRangePicker --- src/date-picker/Picker.tsx | 17 ++- src/date-range-picker/Picker.tsx | 101 ++++++++++++++++++ .../demos/picker.stories.tsx | 39 +++++++ src/date-range-picker/index.tsx | 5 + src/date-range-picker/interfaces.ts | 35 ++++++ src/date-range-picker/style/index.less | 12 +++ src/date-range-picker/style/index.ts | 1 + src/locales/zh-CN.ts | 4 +- src/static-date-picker/DatePicker.tsx | 4 +- ...gePicker.mdx => StaticDateRangePicker.mdx} | 10 +- ....tsx => StaticDateRangePicker.stories.tsx} | 12 ++- ...gePicker.tsx => StaticDateRangePicker.tsx} | 12 +-- .../__test__/DateRangePicker.test.tsx | 4 +- src/static-date-range-picker/index.ts | 8 +- src/static-date-range-picker/interfaces.ts | 6 +- 15 files changed, 236 insertions(+), 34 deletions(-) create mode 100644 src/date-range-picker/Picker.tsx create mode 100644 src/date-range-picker/demos/picker.stories.tsx create mode 100644 src/date-range-picker/index.tsx create mode 100644 src/date-range-picker/interfaces.ts create mode 100644 src/date-range-picker/style/index.less create mode 100644 src/date-range-picker/style/index.ts rename src/static-date-range-picker/{DateRangePicker.mdx => StaticDateRangePicker.mdx} (68%) rename src/static-date-range-picker/{DateRangePicker.stories.tsx => StaticDateRangePicker.stories.tsx} (70%) rename src/static-date-range-picker/{DateRangePicker.tsx => StaticDateRangePicker.tsx} (92%) diff --git a/src/date-picker/Picker.tsx b/src/date-picker/Picker.tsx index afdda50085..6f21977d31 100644 --- a/src/date-picker/Picker.tsx +++ b/src/date-picker/Picker.tsx @@ -10,9 +10,9 @@ import { DatePickerProps } from './interfaces'; export const DatePicker: React.FC = (props: DatePickerProps) => { const { - onVisibleChange: onDropdownVisibleChange, + onVisibleChange: onPopoverVisibleChange, overlayClassName, - visible: dropdownVisible, + visible: popoverVisible, trigger, disabled, value, @@ -22,13 +22,17 @@ export const DatePicker: React.FC = (props: DatePickerProps) => placeholder, allowClear, format: formatString, + prefix, + suffix, + hidePrefix, + size, ...restProps } = props; const prefixCls = usePrefixCls('date-picker-new'); const overlayCls = classnames(`${prefixCls}-overlay`, overlayClassName); - const [visible, setVisible] = useControlledState(dropdownVisible, false); + const [visible, setVisible] = useControlledState(popoverVisible, false); const [controlledValue, setControlledValue] = useControlledState(value, defaultValue); @@ -36,7 +40,7 @@ export const DatePicker: React.FC = (props: DatePickerProps) => const handleVisibleChange = (current: boolean) => { setVisible(current); - onDropdownVisibleChange?.(current); + onPopoverVisibleChange?.(current); }; const handleOnSelect = (currentValue: Date) => { @@ -53,11 +57,14 @@ export const DatePicker: React.FC = (props: DatePickerProps) => } return ( } + prefix={prefix || } placeholder={placeholder} disabled={disabled} allowClear={allowClear} value={controlledValue && formatDate(controlledValue)} + size={size} + suffix={suffix} + hidePrefix={hidePrefix} /> ); } diff --git a/src/date-range-picker/Picker.tsx b/src/date-range-picker/Picker.tsx new file mode 100644 index 0000000000..d70f91325c --- /dev/null +++ b/src/date-range-picker/Picker.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import classnames from 'classnames'; +import { usePrefixCls, useControlledState } from '@gio-design/utils'; +import format from 'date-fns/format'; +import { CalendarOutlined } from '@gio-design/icons'; +import Popover from '../popover'; +import { InputButton } from '../input'; +import StaticDateRangePicker from '../static-date-range-picker'; +import { DateRangePickerProps, NullableDate, NullableString } from './interfaces'; + +export const DateRangePicker: React.FC = (props: DateRangePickerProps) => { + const { + onVisibleChange: onPopoverVisibleChange, + overlayClassName, + visible: popoverVisible, + trigger, + disabled, + value, + defaultValue, + onSelect, + disabledDate, + placeholder, + allowClear, + format: formatString, + prefix, + suffix, + hidePrefix, + size, + ...restProps + } = props; + + const prefixCls = usePrefixCls('date-range-picker-new'); + const overlayCls = classnames(`${prefixCls}-overlay`, overlayClassName); + + const formatDates = (dates: [NullableDate, NullableDate]): NullableString => { + const strongFormat = (date: NullableDate) => (date ? format(date, formatString ?? 'yyyy/MM/dd') : undefined); + return `${strongFormat(dates[0]) || ''} - ${strongFormat(dates[1]) || ''}`; + }; + + const [visible, setVisible] = useControlledState(popoverVisible, false); + + const [controlledValue, setControlledValue] = useControlledState<[NullableDate, NullableDate] | undefined>( + value, + defaultValue + ); + + const handleVisibleChange = (current: boolean) => { + setVisible(current); + onPopoverVisibleChange?.(current); + }; + + const handleOnSelect = (currentValue: [Date, Date], index: number) => { + setControlledValue(currentValue); + if (index) { + setVisible(false); + onSelect?.(currentValue, formatDates(currentValue)); + } + }; + + const content = ( + + ); + + function renderTrigger() { + if (trigger) { + return
{trigger}
; + } + return ( + } + placeholder={placeholder} + disabled={disabled} + allowClear={allowClear} + value={controlledValue && formatDates(controlledValue)} + size={size} + suffix={suffix} + hidePrefix={hidePrefix} + /> + ); + } + + return ( + + {renderTrigger()} + + ); +}; + +export default DateRangePicker; diff --git a/src/date-range-picker/demos/picker.stories.tsx b/src/date-range-picker/demos/picker.stories.tsx new file mode 100644 index 0000000000..b19569affa --- /dev/null +++ b/src/date-range-picker/demos/picker.stories.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Meta, Story } from '@storybook/react/types-6-0'; +import { action } from '@storybook/addon-actions'; + +import DateRangePicker from '../index'; + +import { DateRangePickerProps } from '../interfaces'; + +import '../style'; + +export default { + title: 'Upgraded/DateRangePicker', + component: DateRangePicker, + parameters: { + docs: {}, + }, +} as Meta; + +const defaultPlaceholder = '选择日期范围'; + +const Template: Story = (args) => ( +
+ +
+); + +export const Basic = Template.bind({}); +Basic.args = { + placeholder: defaultPlaceholder, + onSelect: action('selected:'), + onClear: action('onClear:'), +}; + +export const DisbaledDate = Template.bind({}); +DisbaledDate.args = { + placeholder: defaultPlaceholder, + onSelect: action('selected:'), + disabledDate: (current: Date) => current.getTime() > new Date().getTime(), +}; diff --git a/src/date-range-picker/index.tsx b/src/date-range-picker/index.tsx new file mode 100644 index 0000000000..7e1dc160ce --- /dev/null +++ b/src/date-range-picker/index.tsx @@ -0,0 +1,5 @@ +import DateRangePicker from './Picker'; + +export type { DateRangePickerProps } from './interfaces'; + +export default DateRangePicker; diff --git a/src/date-range-picker/interfaces.ts b/src/date-range-picker/interfaces.ts new file mode 100644 index 0000000000..4e4c6ce714 --- /dev/null +++ b/src/date-range-picker/interfaces.ts @@ -0,0 +1,35 @@ +import React from 'react'; +import { CommonProps } from '@gio-design/utils'; +import { PopoverProps } from '../popover'; +import { StaticDateRangePickerProps } from '../static-date-range-picker'; +import { InputButtonProps } from '../input'; + +export type NullableDate = Date | undefined; +export type NullableString = string | undefined; + +export interface DateRangePickerProps + extends CommonProps, + Omit, + Omit, + Omit { + /** + * 自定义的触发器 + */ + trigger?: React.ReactNode; + /** + * 日期展示格式 + */ + format?: string; + /** + * 选择日期时的回调 + * + * @param dates - 选择的日期 `[Date, Date]` + * @param dateStrings - 格式化后的日期 `[string, string]` + */ + onSelect?: (dates: [NullableDate, NullableDate], dateStrings: NullableString) => void; + /** + * 选择的日期 + */ + value?: [NullableDate, NullableDate]; + defaultValue?: [NullableDate, NullableDate]; +} diff --git a/src/date-range-picker/style/index.less b/src/date-range-picker/style/index.less new file mode 100644 index 0000000000..eb1cbf4f43 --- /dev/null +++ b/src/date-range-picker/style/index.less @@ -0,0 +1,12 @@ +@import '../../stylesheet/index.less'; +@import '../../stylesheet/mixin/index.less'; +@import '../../stylesheet/variables/index.less'; + +@picker-prefix-cls: ~'@{component-prefix}-date-range-picker-new'; + +.@{picker-prefix-cls}-overlay { + height: fit-content; + background: @gray-0; + border-radius: 4px; + .elevation(2); +} diff --git a/src/date-range-picker/style/index.ts b/src/date-range-picker/style/index.ts new file mode 100644 index 0000000000..d74e52ee9f --- /dev/null +++ b/src/date-range-picker/style/index.ts @@ -0,0 +1 @@ +import './index.less'; diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 0cdda45a84..2ba016a651 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -1,6 +1,6 @@ import type { Locale } from '@gio-design/utils'; -import datePickerLocale from '../date-picker/locales/zh-CN'; -import dateRangePickerLocale from '../date-range-picker/locales/zh-CN'; +import datePickerLocale from '../static-date-picker/locales/zh-CN'; +import dateRangePickerLocale from '../static-date-range-picker/locales/zh-CN'; import dateRangeSelectorLocale from '../date-range-selector/locales/zh-CN'; import dateSelectorLocale from '../date-selector/locales/zh-CN'; import emptyLocale from '../empty/locales/zh-CN'; diff --git a/src/static-date-picker/DatePicker.tsx b/src/static-date-picker/DatePicker.tsx index 7ed73b7102..bbf670be33 100644 --- a/src/static-date-picker/DatePicker.tsx +++ b/src/static-date-picker/DatePicker.tsx @@ -5,9 +5,9 @@ import { LeftDoubleOutlined, LeftOutlined, RightOutlined, RightDoubleOutlined } import PickerPanel from 'rc-picker/lib/PickerPanel'; import generateDateFns from 'rc-picker/lib/generate/dateFns'; import defaultLocale from './locales/zh-CN'; -import { DatePickerProps } from './interfaces'; +import { StaticDatePickerProps } from './interfaces'; -function DatePicker({ viewDate, ...restProps }: DatePickerProps) { +function DatePicker({ viewDate, ...restProps }: StaticDatePickerProps) { const locale = useLocale('DatePicker') || defaultLocale; return ( diff --git a/src/static-date-range-picker/DateRangePicker.mdx b/src/static-date-range-picker/StaticDateRangePicker.mdx similarity index 68% rename from src/static-date-range-picker/DateRangePicker.mdx rename to src/static-date-range-picker/StaticDateRangePicker.mdx index 2ebf2d894b..b3c33edac1 100644 --- a/src/static-date-range-picker/DateRangePicker.mdx +++ b/src/static-date-range-picker/StaticDateRangePicker.mdx @@ -1,7 +1,7 @@ import { Subtitle, ArgsTable, Story, Canvas } from '@storybook/addon-docs'; -import DateRangePicker from './index'; +import StaticDateRangePicker from './index'; -# DateRangePicker 日期范围选择器 +# StaticDateRangePicker 日期范围选择器 当用户需要一个时间段,可以在面板中进行选择。 @@ -12,7 +12,7 @@ import DateRangePicker from './index'; 最简单的用法,在面板中可以选择日期范围。 - + ### 禁止选择部分日期 @@ -20,7 +20,7 @@ import DateRangePicker from './index'; 可用 `disabledDate` 禁止选择部分日期。 - + ### 面板默认日期 @@ -33,4 +33,4 @@ import DateRangePicker from './index'; ## 参数说明 - + diff --git a/src/static-date-range-picker/DateRangePicker.stories.tsx b/src/static-date-range-picker/StaticDateRangePicker.stories.tsx similarity index 70% rename from src/static-date-range-picker/DateRangePicker.stories.tsx rename to src/static-date-range-picker/StaticDateRangePicker.stories.tsx index c819d3a8c1..7d8df4a525 100644 --- a/src/static-date-range-picker/DateRangePicker.stories.tsx +++ b/src/static-date-range-picker/StaticDateRangePicker.stories.tsx @@ -2,13 +2,13 @@ import React from 'react'; import { Meta, Story } from '@storybook/react/types-6-0'; import { action } from '@storybook/addon-actions'; import { isBefore, startOfToday, subMonths } from 'date-fns'; -import Docs from './DateRangePicker.mdx'; -import { DateRangePicker, DateRangePickerProps } from './index'; +import Docs from './StaticDateRangePicker.mdx'; +import { StaticDateRangePicker, StaticDateRangePickerProps } from './index'; import './style'; export default { - component: DateRangePicker, + component: StaticDateRangePicker, parameters: { design: { type: 'figma', @@ -19,10 +19,12 @@ export default { page: Docs, }, }, - title: 'Upgraded/StaticDateRangePicker', + title: 'Upgraded/StaticStaticDateRangePicker', } as Meta; -const Template: Story = (args) => ; +const Template: Story = (args) => ( + +); export const Basic = Template.bind({}); Basic.args = {}; diff --git a/src/static-date-range-picker/DateRangePicker.tsx b/src/static-date-range-picker/StaticDateRangePicker.tsx similarity index 92% rename from src/static-date-range-picker/DateRangePicker.tsx rename to src/static-date-range-picker/StaticDateRangePicker.tsx index 881f6fa9e1..19ecc8adf8 100644 --- a/src/static-date-range-picker/DateRangePicker.tsx +++ b/src/static-date-range-picker/StaticDateRangePicker.tsx @@ -6,10 +6,10 @@ import { RangeValue } from 'rc-picker/lib/interface'; import { useControlledState, usePrefixCls } from '@gio-design/utils'; import isBefore from 'date-fns/isBefore'; import StaticDatePicker, { StaticDatePickerContext } from '../static-date-picker'; -import { DateRangePickerProps } from './interfaces'; +import { StaticDateRangePickerProps } from './interfaces'; import { getDefaultViewDates, calcClosingViewDate, mergeDates } from './utils'; -function DateRangePicker({ +function StaticDateRangePicker({ className, defaultValue, defaultViewDates, @@ -20,7 +20,7 @@ function DateRangePicker({ style, value, locale, -}: DateRangePickerProps) { +}: StaticDateRangePickerProps) { const [viewDates, setViewDates] = React.useState<[Date, Date]>(defaultViewDates ?? getDefaultViewDates()); const [hoveredDates, setHoveredDates] = React.useState>(); const [dateIndex, setDateIndex] = React.useState(0); @@ -41,13 +41,13 @@ function DateRangePicker({ > { + disabledDate={(currentDate: Date) => { const isBeforeStartDate = selectedValue && selectedValue[0] && !selectedValue[1] ? isBefore(currentDate, selectedValue[0]) : false; const isDisabledDate = disabledDate ? disabledDate(currentDate) : false; return isBeforeStartDate || isDisabledDate; }} - onPanelChange={(currentValue) => { + onPanelChange={(currentValue: Date) => { if (index) { setViewDates([calcClosingViewDate(currentValue, -1), currentValue]); } else { @@ -94,4 +94,4 @@ function DateRangePicker({ ); } -export default DateRangePicker; +export default StaticDateRangePicker; diff --git a/src/static-date-range-picker/__test__/DateRangePicker.test.tsx b/src/static-date-range-picker/__test__/DateRangePicker.test.tsx index 56074df887..ac802a6c81 100644 --- a/src/static-date-range-picker/__test__/DateRangePicker.test.tsx +++ b/src/static-date-range-picker/__test__/DateRangePicker.test.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { add, startOfMonth, startOfToday, subMonths } from 'date-fns'; -import { Basic, DefaultViewDates, DisabledDate } from '../DateRangePicker.stories'; +import { Basic, DefaultViewDates, DisabledDate } from '../StaticDateRangePicker.stories'; -describe('DateRangePicker', () => { +describe('StaticDateRangePicker', () => { beforeAll(() => { // mock now is 2021/05/20 00:00:00.000 jest.useFakeTimers('modern'); diff --git a/src/static-date-range-picker/index.ts b/src/static-date-range-picker/index.ts index 7074496d06..cc077340c7 100644 --- a/src/static-date-range-picker/index.ts +++ b/src/static-date-range-picker/index.ts @@ -1,6 +1,6 @@ -import DateRangePicker from './DateRangePicker'; -import { DateRangePickerProps } from './interfaces'; +import StaticDateRangePicker from './StaticDateRangePicker'; +import { StaticDateRangePickerProps } from './interfaces'; -export { DateRangePicker, DateRangePickerProps }; +export { StaticDateRangePicker, StaticDateRangePickerProps }; -export default DateRangePicker; +export default StaticDateRangePicker; diff --git a/src/static-date-range-picker/interfaces.ts b/src/static-date-range-picker/interfaces.ts index bce404c081..1dc9d9eca5 100644 --- a/src/static-date-range-picker/interfaces.ts +++ b/src/static-date-range-picker/interfaces.ts @@ -2,9 +2,9 @@ import { CommonProps } from '@gio-design/utils'; import { Locale } from 'rc-picker/lib/interface'; import { DatePickerProps } from '../date-picker'; -export type DateRangePickerLocale = Omit; +export type StaticDateRangePickerLocale = Omit; -export interface DateRangePickerProps extends CommonProps, Pick { +export interface StaticDateRangePickerProps extends CommonProps, Pick { /** * 默认选择的日期 */ @@ -16,7 +16,7 @@ export interface DateRangePickerProps extends CommonProps, Pick