From 8174c4f4b71d62d6614fc7ba08abc530bc844f9b Mon Sep 17 00:00:00 2001 From: saller Date: Thu, 29 Feb 2024 16:54:08 +0800 Subject: [PATCH] fix(comp:date-picker): range start and and date cover full range (#1851) --- .../date-picker/src/DateRangePicker.tsx | 12 ++- .../src/composables/useRangeControl.ts | 34 +++++++-- .../src/composables/useRangePanelState.ts | 9 +-- .../date-picker/src/panel/Panel.tsx | 4 +- .../date-picker/src/panel/RangePanel.tsx | 13 +--- packages/components/date-picker/src/utils.ts | 74 +++++++++++++++++++ 6 files changed, 121 insertions(+), 25 deletions(-) diff --git a/packages/components/date-picker/src/DateRangePicker.tsx b/packages/components/date-picker/src/DateRangePicker.tsx index 57d8769c7..207e26efc 100644 --- a/packages/components/date-picker/src/DateRangePicker.tsx +++ b/packages/components/date-picker/src/DateRangePicker.tsx @@ -54,7 +54,13 @@ export default defineComponent({ const { accessor, handleFocus: _handleFocus, handleBlur: _handleBlur, handleChange } = pickerStateContext - const rangeControlContext = useRangeControl(dateConfig, formatContext, inputEnableStatus, toRef(accessor, 'value')) + const rangeControlContext = useRangeControl( + dateConfig, + formatContext, + inputEnableStatus, + toRef(accessor, 'value'), + toRef(props, 'type'), + ) const handleKeyDown = useRangeKeyboardEvents(rangeControlContext, overlayOpened, setOverlayOpened, handleChange) const { focused, handleFocus, handleBlur, bindOverlayMonitor } = useOverlayFocusMonitor(_handleFocus, _handleBlur) @@ -93,7 +99,9 @@ export default defineComponent({ watch(overlayOpened, opened => { if (opened) { setTimeout(() => { - inputRef.value?.focus() + if (!focused.value) { + inputRef.value?.focus() + } }) } else { rangeControlContext.init(true) diff --git a/packages/components/date-picker/src/composables/useRangeControl.ts b/packages/components/date-picker/src/composables/useRangeControl.ts index 0150549d6..be2c95963 100644 --- a/packages/components/date-picker/src/composables/useRangeControl.ts +++ b/packages/components/date-picker/src/composables/useRangeControl.ts @@ -5,15 +5,23 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ +import type { DatePickerType } from '../types' +import type { DateConfig, DateConfigType, TimeConfigType } from '@idux/components/config' + import { type ComputedRef, type Ref, computed, watch } from 'vue' import { convertArray, useState } from '@idux/cdk/utils' -import { type DateConfig } from '@idux/components/config' import { type PickerControlContext, useControl } from './useControl' import { type FormatContext } from './useFormat' import { type InputEnableStatus } from './useInputEnableStatus' -import { compareDateTime, convertToDate, sortRangeValue } from '../utils' +import { + adjustRangeValue, + compareDateTime, + convertPickerTypeToConfigType, + convertToDate, + sortRangeValue, +} from '../utils' export interface PickerRangeControlContext { buffer: ComputedRef<(Date | undefined)[] | undefined> @@ -34,11 +42,13 @@ export function useRangeControl( formatContext: FormatContext, inputEnableStatus: ComputedRef, valueRef: Ref<(string | number | Date)[] | undefined>, + typeRef: Ref, ): PickerRangeControlContext { - const { formatRef } = formatContext - const [buffer, setBuffer] = useState<(Date | undefined)[] | undefined>( + const { formatRef, hourEnabled, secondEnabled, minuteEnabled } = formatContext + const convertedValue = computed(() => convertArray(valueRef.value).map(v => convertToDate(dateConfig, v, formatRef.value)), ) + const [buffer, setBuffer] = useState<(Date | undefined)[] | undefined>(convertedValue.value) const [bufferUpdated, setBufferUpdated] = useState(false) const handleBufferUpdate = (values: (string | number | Date | undefined)[] | undefined) => { setBuffer(sortRangeValue(dateConfig, getRangeValue(dateConfig, values, formatRef.value), 'date')) @@ -73,12 +83,24 @@ export function useRangeControl( : [buffer.value?.[0], value] } + const getAdjustedBufferValue = (value: Date | undefined, isFrom: boolean) => { + const adjustType: DateConfigType | TimeConfigType = secondEnabled.value + ? 'second' + : minuteEnabled.value + ? 'minute' + : hourEnabled.value + ? 'hour' + : convertPickerTypeToConfigType(typeRef.value) + + return adjustRangeValue(dateConfig, getValidBufferValue(value, isFrom), convertedValue.value, adjustType) + } + const fromControl = useControl(dateConfig, formatContext, inputEnableStatus, fromDateRef, value => { - setBuffer(getValidBufferValue(value, true)) + setBuffer(getAdjustedBufferValue(value, true)) setBufferUpdated(true) }) const toControl = useControl(dateConfig, formatContext, inputEnableStatus, toDateRef, value => { - setBuffer(getValidBufferValue(value, false)) + setBuffer(getAdjustedBufferValue(value, false)) setBufferUpdated(true) }) diff --git a/packages/components/date-picker/src/composables/useRangePanelState.ts b/packages/components/date-picker/src/composables/useRangePanelState.ts index fc78f61df..2d21a5bd9 100644 --- a/packages/components/date-picker/src/composables/useRangePanelState.ts +++ b/packages/components/date-picker/src/composables/useRangePanelState.ts @@ -12,7 +12,7 @@ import { type ComputedRef, computed, watch } from 'vue' import { callEmit, convertArray, useState } from '@idux/cdk/utils' -import { applyDateTime, sortRangeValue } from '../utils' +import { adjustRangeValue, convertPickerTypeToConfigType, sortRangeValue } from '../utils' export interface RangePanelStateContext { panelValue: ComputedRef<(Date | undefined)[] | undefined> @@ -53,12 +53,9 @@ export function useRangePanelState(props: DateRangePanelProps, dateConfig: DateC callEmit(props.onSelect, [value, undefined]) } else { const propsValue = convertArray(props.value) + const sortedValue = sortRangeValue(dateConfig, [selectingDates.value![0], value], 'date') as Date[] handleChange( - sortRangeValue(dateConfig, [selectingDates.value![0], value], 'date').map((dateValue, index) => - propsValue[index] - ? applyDateTime(dateConfig, propsValue[index], dateValue!, ['hour', 'minute', 'second']) - : dateValue, - ) as Date[], + adjustRangeValue(dateConfig, sortedValue, propsValue, convertPickerTypeToConfigType(props.type)) as Date[], ) setIsSelecting(false) } diff --git a/packages/components/date-picker/src/panel/Panel.tsx b/packages/components/date-picker/src/panel/Panel.tsx index 220350bc6..0070101dd 100644 --- a/packages/components/date-picker/src/panel/Panel.tsx +++ b/packages/components/date-picker/src/panel/Panel.tsx @@ -17,7 +17,7 @@ import { getTimePickerThemeTokens } from '@idux/components/time-picker' import { getThemeTokens } from '../../theme' import { useActiveValue } from '../composables/useActiveValue' import { datePanelProps } from '../types' -import { applyDateTime } from '../utils' +import { applyDateTime, convertPickerTypeToConfigType } from '../utils' export default defineComponent({ name: 'IxDatePanel', @@ -55,7 +55,7 @@ export default defineComponent({ } return () => { - const datePanelType = props.type === 'datetime' ? 'date' : props.type + const datePanelType = convertPickerTypeToConfigType(props.type) const datePanelProps = { cellTooltip: props.cellTooltip, diff --git a/packages/components/date-picker/src/panel/RangePanel.tsx b/packages/components/date-picker/src/panel/RangePanel.tsx index 5a01e024c..e04c922b9 100644 --- a/packages/components/date-picker/src/panel/RangePanel.tsx +++ b/packages/components/date-picker/src/panel/RangePanel.tsx @@ -19,7 +19,7 @@ import { getThemeTokens } from '../../theme' import { useRangeActiveValue } from '../composables/useActiveValue' import { useRangePanelState } from '../composables/useRangePanelState' import { dateRangePanelProps } from '../types' -import { sortRangeValue } from '../utils' +import { convertPickerTypeToConfigType } from '../utils' export default defineComponent({ name: 'IxDateRangePanel', @@ -51,22 +51,17 @@ export default defineComponent({ const renderSide = (isFrom: boolean) => { const timeValue = panelValue.value?.[isFrom ? 0 : 1] + const datePanelType = convertPickerTypeToConfigType(props.type) const activeValue = isFrom ? fromActiveValue.value : toActiveValue.value const handleTimePanelChange = (value: Date) => { - handleChange( - sortRangeValue( - dateConfig, - isFrom ? [value, panelValue.value?.[1]] : [panelValue.value?.[0], value], - 'date', - ) as Date[], - ) + handleChange((isFrom ? [value, panelValue.value?.[1]] : [panelValue.value?.[0], value]) as Date[]) } const datePanelProps = { cellTooltip: props.cellTooltip, disabledDate: props.disabledDate, - type: props.type === 'datetime' ? 'date' : props.type, + type: datePanelType, value: panelValue.value, visible: props.type === 'datetime' ? props.visible === 'datePanel' : !!props.visible, activeDate: activeValue, diff --git a/packages/components/date-picker/src/utils.ts b/packages/components/date-picker/src/utils.ts index 3331c88f2..c32e8040f 100644 --- a/packages/components/date-picker/src/utils.ts +++ b/packages/components/date-picker/src/utils.ts @@ -5,10 +5,15 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ +import type { DatePickerType } from './types' import type { DateConfig, DateConfigType, TimeConfigType } from '@idux/components/config' import { convertArray } from '@idux/cdk/utils' +export function convertPickerTypeToConfigType(type: DatePickerType): Exclude { + return type === 'datetime' ? 'date' : type +} + export function convertToDate( dateConfig: DateConfig, value: string | number | Date | undefined, @@ -71,3 +76,72 @@ export function sortRangeValue( ): (Date | undefined)[] { return values.sort((v1, v2) => compareDateTime(dateConfig, v1, v2, type)) } + +const typeApplySequence: (DateConfigType | TimeConfigType)[] = [ + 'year', + 'month', + 'date', + 'hour', + 'minute', + 'second', + 'millisecond', +] +function getTypeForSequenceSearch(type: DateConfigType | TimeConfigType): DateConfigType | TimeConfigType { + switch (type) { + case 'week': + return 'date' + case 'day': + return 'date' + case 'quarter': + return 'month' + default: + return type + } +} +function applyDateOfPropValue( + dateConfig: DateConfig, + value: Date, + propValue: Date, + type: DateConfigType | TimeConfigType, +): Date { + const typeForSequenceSearch = getTypeForSequenceSearch(type) + const typesToApplay = typeApplySequence.slice(typeApplySequence.indexOf(typeForSequenceSearch) + 1) + + return applyDateTime(dateConfig, propValue, value, typesToApplay) +} + +function adjustRangeBoundary( + dateConfig: DateConfig, + value: Date | undefined, + propValue: Date | undefined, + type: DateConfigType | TimeConfigType, + isStart: boolean, +): Date | undefined { + if (!value) { + return + } + + if (propValue) { + return applyDateOfPropValue(dateConfig, value, propValue, type) + } + + return isStart ? dateConfig.startOf(value, type) : dateConfig.endOf(value, type) +} + +export function adjustRangeValue( + dateConfig: DateConfig, + values: (Date | undefined)[] | undefined, + propValues: undefined | (Date | undefined)[], + type: DateConfigType | TimeConfigType, +): (Date | undefined)[] | undefined { + if (!values) { + return + } + + const [start, end] = values + + return [ + adjustRangeBoundary(dateConfig, start, propValues?.[0], type, true), + adjustRangeBoundary(dateConfig, end, propValues?.[1], type, false), + ] +}