diff --git a/package.json b/package.json index c182b0cff..32048c1ca 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "13.8.5", + "version": "13.9.5", "license": "MIT", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", diff --git a/src/components/data-grid/DataGrid.stories.tsx b/src/components/data-grid/DataGrid.stories.tsx index 29153e867..692f0c106 100755 --- a/src/components/data-grid/DataGrid.stories.tsx +++ b/src/components/data-grid/DataGrid.stories.tsx @@ -28,6 +28,7 @@ export default { } as Meta; const Template: Story = (args) => { + const [dates, setDates] = useState([new Date()]); const [views, setViews] = useState([ { id: '1', @@ -119,6 +120,8 @@ const Template: Story = (args) => { onCreateView={(input: CreateViewInput) => handleCreateView(input)} onSaveViewState={(id: string, viewState: string) => handleSaveView(id, viewState)} onReady={(data) => console.log(data)} + dates={dates} + setDates={setDates} /> ); }; diff --git a/src/components/data-grid/defaultFormatSettings.ts b/src/components/data-grid/defaultFormatSettings.ts index 15a9e6ea0..cac5a7fcc 100755 --- a/src/components/data-grid/defaultFormatSettings.ts +++ b/src/components/data-grid/defaultFormatSettings.ts @@ -4,7 +4,7 @@ import { FormatSettings } from './types'; export const defaultFormatSettings: FormatSettings = { timeFormat: 'HH:mm', durationFormat: 'HH:mm', - dateFormat: 'DD/MM/YYYY', + dateFormat: 'dd/MM/yyyy', currency: 'USD', numberFormat: NumberFormat.Dot, language: 'en', diff --git a/src/components/data-grid/filters/ColumnFilters.tsx b/src/components/data-grid/filters/ColumnFilters.tsx index 983132d27..3718d148b 100755 --- a/src/components/data-grid/filters/ColumnFilters.tsx +++ b/src/components/data-grid/filters/ColumnFilters.tsx @@ -6,13 +6,13 @@ import { Dropdown, DropdownItem } from '../../dropdown'; import { MinusIcon } from '../../icons/minus'; import { AddLineIcon } from '../../icons/add-line'; import { useTheme } from '../../../providers'; -import { DatepickerPopover } from '../../datepicker-popover'; import { FilterButton } from './FilterButton'; import { Button } from '../../button'; import { ButtonKind } from '../../../models'; import { ParagraphSmall } from 'baseui/typography'; import { FixedSizeSelect } from '../../fixed-size-select'; import { MultiFilter } from './MultiFilter'; +import { CustomDatepicker } from 'components/datepicker'; const LESS_FILTERS_BUTTON_TEST_ID = 'less-filters-button'; const MORE_FILTERS_BUTTON_TEST_ID = 'more-filters-button'; @@ -38,7 +38,6 @@ export const ColumnFilters = ({ onShowLessFiltersChange, }: ColumnFiltersProps) => { const [showLessFilters, setShowLessFilters] = useState(initialShowLessFilters ?? true); - const [datepickerIsOpen, setDatepickerIsOpen] = useState(false); const [internalDates, setInternalDates] = useState([]); const { @@ -107,12 +106,8 @@ export const ColumnFilters = ({ [filterOnValue, isSelectValueActive], ); - const dateFilterIsActive = () => dates?.length === 2; - const isSetFilterActive = (columnField: string) => !!selectedFilterIds[columnField]?.length; - const getDateIconColor = () => (dateFilterIsActive() ? primary : contentSecondary); - const getSetIconColor = (columnField: string) => (isSetFilterActive(columnField) ? primary : contentSecondary); const getSelectActiveItem = (columnField: string, values?: string[] | FilterValue[]) => { @@ -135,11 +130,6 @@ export const ColumnFilters = ({ }; }; - const getDateTitleFormat = (date: Date) => new TcDate(date).format(dateFormat); - - const getDateTitle = (title: string) => - dates && dateFilterIsActive() ? `${getDateTitleFormat(dates[0])} - ${getDateTitleFormat(dates[1])}` : title; - const getSetTitle = (columnField: string, title: string) => { if (!isSetFilterActive(columnField)) { return title; @@ -148,14 +138,6 @@ export const ColumnFilters = ({ return `${length} ${title}`; }; - const toggleDatePicker = () => { - if (datepickerIsOpen) { - setDatepickerIsOpen(false); - return setInternalDates([]); - } - return setDatepickerIsOpen(true); - }; - const onDateSelect = ({ date: selectedDates, columnField }: { date: Date | Date[]; columnField: string }) => { if (!setDates) { return; @@ -169,7 +151,7 @@ export const ColumnFilters = ({ if (selectedDates.length > 1) { setDates(selectedDates); - toggleDatePicker(); + // toggleDatePicker(); filterOnDate(columnField, selectedDates); } }; @@ -240,23 +222,13 @@ export const ColumnFilters = ({ const filterTypes = { [FilterType.date]: ( <> - setDatepickerIsOpen(!datepickerIsOpen)} - startEnhancer={Icon && } - size={SIZE.compact} - title={getDateTitle(title)} - arrows - /> - onDateSelect({ date, columnField })} date={internalDates.length ? internalDates : dates} - isOpen={datepickerIsOpen} - setIsOpen={toggleDatePicker} - monthsShown={2} - range - quickSelect locale={locale} + range translations={datepickerTranslations} + dateFormat={dateFormat} /> ), diff --git a/src/components/datepicker/CustomDatepicker.stories.tsx b/src/components/datepicker/CustomDatepicker.stories.tsx new file mode 100644 index 000000000..78c65c778 --- /dev/null +++ b/src/components/datepicker/CustomDatepicker.stories.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Story, Meta } from '@storybook/react/types-6-0'; + +import { TcDate } from '@timechimp/timechimp-typescript-helpers'; +import { CustomDatepicker } from './CustomDatepicker'; +import { CustomDatepickerProps } from './types'; + +export default { + title: 'Components/CustomDatepicker', + component: CustomDatepicker, +} as Meta; + +let isOpen = true; + +const Template: Story = (args) => { + const [date, setDate] = React.useState<[Date, Date]>([ + new TcDate().startOf('month').toDate(), + new TcDate().endOf('month').toDate(), + ]); + + return setDate(date as [Date, Date])} />; +}; + +export const Default = Template.bind({}); +Default.args = { + placement: 'bottomLeft', + date: [new TcDate().startOf('month').toDate(), new TcDate().endOf('month').toDate()], + isOpen: true, + setIsOpen: () => (isOpen = !isOpen), + quickSelect: true, +}; diff --git a/src/components/datepicker/CustomDatepicker.tsx b/src/components/datepicker/CustomDatepicker.tsx new file mode 100644 index 000000000..40687191b --- /dev/null +++ b/src/components/datepicker/CustomDatepicker.tsx @@ -0,0 +1,202 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { useTheme } from '../../providers'; +import { TcDate } from '@timechimp/timechimp-typescript-helpers'; +import { DatepickerOption } from '../datepicker-popover/types'; +import { SingleSelect } from 'components/select'; +import { Datepicker } from 'components/datepicker/Datepicker'; +import { FlexItem } from 'components/flex-item'; +import { Block } from 'baseui/block'; +import { CustomDatepickerProps } from './types'; + +export const CustomDatepicker = ({ date, translations, onChange, dateFormat, ...rest }: CustomDatepickerProps) => { + const [isDatepickerOpen, setIsDatepickerOpen] = useState(false); + const [value, setValue] = useState([new Date()]); + + const { + theme: { + current: { + sizing: { scale200, scale300, scale4800 }, + customSizing: { scale6000 }, + }, + }, + } = useTheme(); + + const quickSelectOptions: DatepickerOption[] = [ + { + id: translations?.allPeriods ?? 'All periods', + beginDate: new TcDate().subtract(1, 'year').startOf('year').toDate(), + endDate: new TcDate().endOf('year').toDate(), + }, + { + id: translations?.today ?? 'Today', + beginDate: new Date(), + endDate: new Date(), + }, + { + id: translations?.yesterday ?? 'Yesterday', + beginDate: new TcDate().subtract(1, 'day').toDate(), + endDate: new TcDate().subtract(1, 'day').toDate(), + }, + { + id: translations?.thisWeek ?? 'This week', + beginDate: new TcDate().startOf('week', 1).getDateWithoutTimeAsUTC(), + endDate: new TcDate().endOf('week', 1).getDateWithoutTimeAsUTC(), + }, + { + id: translations?.thisMonth ?? 'This month', + beginDate: new TcDate().startOf('month').toDate(), + endDate: new TcDate().endOf('month').toDate(), + }, + { + id: translations?.thisQuarter ?? 'This quarter', + beginDate: new TcDate().startOf('quarter').toDate(), + endDate: new TcDate().endOf('quarter').toDate(), + }, + { + id: translations?.thisYear ?? 'This year', + beginDate: new TcDate().startOf('year').toDate(), + endDate: new TcDate().endOf('year').toDate(), + }, + { + id: translations?.previousWeek ?? 'Previous week', + beginDate: new TcDate().subtract(1, 'week').startOf('week', 1).toDate(), + endDate: new TcDate().subtract(1, 'week').endOf('week', 1).toDate(), + }, + { + id: translations?.previousMonth ?? 'Previous month', + beginDate: new TcDate().subtract(1, 'month').startOf('month').toDate(), + endDate: new TcDate().subtract(1, 'month').endOf('month').toDate(), + }, + { + id: translations?.previousQuarter ?? 'Previous quarter', + beginDate: new TcDate().subtract(1, 'quarter').startOf('quarter').toDate(), + endDate: new TcDate().subtract(1, 'quarter').endOf('quarter').toDate(), + }, + { + id: translations?.previousYear ?? 'Previous year', + beginDate: new TcDate().subtract(1, 'year').startOf('year').toDate(), + endDate: new TcDate().subtract(1, 'year').endOf('year').toDate(), + }, + { id: translations?.custom ?? 'Custom' }, + ]; + + const isSameDates = (option: DatepickerOption, _date: Date[]) => { + const selectedBeginDate = _date[0]; + const selectedEndDate = _date[1]; + const optionBeginDate = new TcDate(option.beginDate); + const optionEndDate = new TcDate(option.endDate); + + return optionBeginDate.isSame(selectedBeginDate, 'day') && optionEndDate.isSame(selectedEndDate, 'day'); + }; + + const isSameSingleDate = (option: DatepickerOption, _date: Date) => { + const selectedDate = _date; + const optionBeginDate = new TcDate(option.beginDate); + + return optionBeginDate.isSame(selectedDate, 'day'); + }; + + useEffect(() => { + if (!date) { + return; + } + + let quickSelectOption; + + if (!Array.isArray(date)) { + quickSelectOption = quickSelectOptions.find((option) => isSameSingleDate(option, date)); + } else { + quickSelectOption = quickSelectOptions.find((option) => isSameDates(option, date)); + } + + if (quickSelectOption) { + return; + } + + setIsDatepickerOpen(true); + + if (Array.isArray(date)) { + setValue(date); + return; + } + + setValue([date]); + }, [date]); + + const isDateInQuickSelectOptions = useMemo(() => { + if (!date) { + return false; + } + + if (!Array.isArray(date)) { + return quickSelectOptions.some((option) => isSameSingleDate(option, date)); + } + return quickSelectOptions.some((option) => isSameDates(option, date)); + }, [date, quickSelectOptions]); + + const quickSelectValue = useMemo(() => { + if (!date) { + return undefined; + } + + if (!isDateInQuickSelectOptions) { + return quickSelectOptions.find((option) => option.id === 'Custom'); + } + + if (!Array.isArray(date)) { + const quickSelectOption = quickSelectOptions.find((option) => isSameSingleDate(option, date)); + return quickSelectOption; + } + + const quickSelectOption = quickSelectOptions.find((option) => isSameDates(option, date)); + return quickSelectOption; + }, [date, quickSelectOptions, isDateInQuickSelectOptions]); + + const handleQuickOptionsClick = (value: DatepickerOption | null) => { + if (!value) { + return; + } + + if (value.id === 'Custom') { + setIsDatepickerOpen(true); + } else { + setIsDatepickerOpen(false); + onChange([value.beginDate, value.endDate]); + } + }; + + const handleDateClick = (date: Date[]) => { + if (date.length > 1) { + onChange(date); + return; + } + + setValue(date); + }; + + return ( + + + + + {isDatepickerOpen && ( + + handleDateClick(date)} + formatString={dateFormat} + /> + + )} + + ); +}; diff --git a/src/components/datepicker/index.ts b/src/components/datepicker/index.ts index 76c19c501..261a9fee8 100755 --- a/src/components/datepicker/index.ts +++ b/src/components/datepicker/index.ts @@ -1,3 +1,4 @@ export * from './Datepicker'; export * from './DatePickerTemplate'; export * from './types'; +export * from './CustomDatepicker'; diff --git a/src/components/datepicker/types.ts b/src/components/datepicker/types.ts index 4588192e6..ec30684d9 100644 --- a/src/components/datepicker/types.ts +++ b/src/components/datepicker/types.ts @@ -1,5 +1,5 @@ import { SupportedLocale } from '@timechimp/timechimp-typescript-helpers'; -import { DatepickerProps as BaseDatepickerProps } from 'baseui/datepicker'; +import { DatepickerProps as BaseDatepickerProps, CalendarProps } from 'baseui/datepicker'; import { TetherPlacement } from 'baseui/layer'; export interface DatepickerRangeTranslations { @@ -14,6 +14,8 @@ export interface DatepickerRangeTranslations { previousMonth?: string; previousQuarter?: string; previousYear?: string; + custom?: string; + allPeriods?: string; } export interface DatepickerProps extends BaseDatepickerProps { @@ -28,3 +30,13 @@ export interface DatepickerProps extends BaseDatepickerProps { translations?: DatepickerRangeTranslations; showSkeleton?: boolean; } + +export interface CustomDatepickerProps extends Omit { + dateFormat: string; + date?: Date | Date[]; + placement?: TetherPlacement; + locale?: SupportedLocale; + weekStartDay?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined; + translations?: DatepickerRangeTranslations; + onChange: (date: Date | Date[]) => void; +}