From 04bb47278e2732b19fab80db9b3d92c8ec0d0f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 3 Sep 2026 12:00:10 +0800 Subject: [PATCH] fix: preserve pending range selection on hover --- src/PickerInput/Popup/PopupPanel.tsx | 3 +++ src/PickerInput/RangePicker.tsx | 8 +++++++- src/PickerInput/hooks/useRangeValueChange.ts | 5 +++++ tests/range.spec.tsx | 16 ++++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/PickerInput/Popup/PopupPanel.tsx b/src/PickerInput/Popup/PopupPanel.tsx index e59b4fba3..76d797b29 100644 --- a/src/PickerInput/Popup/PopupPanel.tsx +++ b/src/PickerInput/Popup/PopupPanel.tsx @@ -14,6 +14,7 @@ export type PopupPanelProps = MustProp FooterProps & { multiplePanel?: boolean; range?: boolean; + cellHoverValue?: DateType[]; onPickerValueChange: (date: DateType) => void; }; @@ -30,6 +31,7 @@ export default function PopupPanel( onSubmit, range, hoverValue, + cellHoverValue, } = props; const { prefixCls, generateConfig } = React.useContext(PickerContext); @@ -72,6 +74,7 @@ export default function PopupPanel( if (range) { pickerProps.hoverRangeValue = hoverValue; + pickerProps.hoverValue = cellHoverValue; } else { pickerProps.hoverValue = hoverValue; } diff --git a/src/PickerInput/RangePicker.tsx b/src/PickerInput/RangePicker.tsx index 36c2dda2e..f70eec948 100644 --- a/src/PickerInput/RangePicker.tsx +++ b/src/PickerInput/RangePicker.tsx @@ -331,6 +331,7 @@ function RangePicker( triggeredFields, triggerRangeValueChange, resetRangeValueChange, + currentFieldModified, ] = useRangeValueChange( enabledFieldCount, needConfirm, @@ -502,6 +503,10 @@ function RangePicker( return internalHoverValues || calendarValue; }, [calendarValue, internalHoverValues]); + const keepCurrentSelection = needConfirm && currentFieldModified && hoverSource === 'cell'; + const panelHoverValues = keepCurrentSelection ? calendarValue : hoverValues; + const activeHoverValue = internalHoverValues?.[activeIndex]; + // Clean up `internalHoverValues` when closed React.useEffect(() => { if (!mergedOpen) { @@ -640,7 +645,8 @@ function RangePicker( defaultOpenValue={toArray(showTime?.defaultOpenValue)[activeIndex]} onPickerValueChange={setCurrentPickerValue} // Hover - hoverValue={hoverValues} + hoverValue={panelHoverValues} + cellHoverValue={keepCurrentSelection && activeHoverValue ? [activeHoverValue] : null} onHover={onPanelHover} // Submit needConfirm={needConfirm} diff --git a/src/PickerInput/hooks/useRangeValueChange.ts b/src/PickerInput/hooks/useRangeValueChange.ts index 6fad8d25a..dfdccfa7c 100644 --- a/src/PickerInput/hooks/useRangeValueChange.ts +++ b/src/PickerInput/hooks/useRangeValueChange.ts @@ -59,6 +59,7 @@ export type UseRangeValueChangeReturn = [ triggeredFields: number[], triggerChange: TriggerChange, reset: VoidFunction, + currentFieldModified: boolean, ]; interface TriggeredField { @@ -509,6 +510,9 @@ export default function useRangeValueChange( lastValidIndexRef.current = currentIndex ?? lastValidIndexRef.current ?? 0; const triggeredFields = triggeredFieldsRef.current.map((field) => field.index); + const currentFieldModified = triggeredFieldsRef.current.some( + (field) => field.index === currentIndex && field.modified, + ); return [ currentIndex, @@ -517,5 +521,6 @@ export default function useRangeValueChange( triggeredFields, triggerChange, reset, + currentFieldModified, ]; } diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index 43ebad195..639fcf49b 100644 --- a/tests/range.spec.tsx +++ b/tests/range.spec.tsx @@ -768,6 +768,22 @@ describe('Picker.Range', () => { expect(findCell(end)).not.toHaveClass('rc-picker-cell-range-end'); }); }); + + it('should keep the pending selection when hovering with confirmation', async () => { + const { container } = render(); + openPicker(container); + selectCell(11); + + expect(findCell(11)).toHaveClass('rc-picker-cell-range-start'); + + fireEvent.mouseEnter(findCell(22)); + await waitFakeTimer(); + + expect(container.querySelectorAll('input')[0]).toHaveValue('1990-09-22 00:00:00'); + expect(findCell(11)).toHaveClass('rc-picker-cell-range-start'); + expect(findCell(22)).toHaveClass('rc-picker-cell-hover'); + expect(findCell(22)).not.toHaveClass('rc-picker-cell-range-start'); + }); }); it('should close when user focus out', () => {