From 25fbecb0c9241861772527d692dc5cacf215e279 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: Tue, 16 Jul 2024 16:19:27 +0800 Subject: [PATCH 01/14] refactor: update logic --- docs/examples/multiple.tsx | 8 +++--- package.json | 1 + src/Slider.tsx | 54 ++++++++++++++++++++++---------------- src/hooks/useRange.ts | 14 ++++++++++ 4 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 src/hooks/useRange.ts diff --git a/docs/examples/multiple.tsx b/docs/examples/multiple.tsx index 55bc91d25..25a35a8cb 100644 --- a/docs/examples/multiple.tsx +++ b/docs/examples/multiple.tsx @@ -17,17 +17,19 @@ const NodeWrapper = ({ children }: { children: React.ReactElement }) => { }; export default () => { - const [value, setValue] = React.useState([0, 5, 8]); + const [value, setValue] = React.useState([0, 50, 80]); return (
{ // console.log('>>>', nextValue); diff --git a/package.json b/package.json index 08efda1f8..a33a27dcc 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "@testing-library/react": "^12.1.3", "@types/classnames": "^2.2.9", "@types/jest": "^29.5.1", + "@types/node": "^20.14.10", "@types/react": "^18.2.42", "@types/react-dom": "^18.0.11", "@umijs/fabric": "^4.0.1", diff --git a/src/Slider.tsx b/src/Slider.tsx index 1168e4b38..ac666635f 100644 --- a/src/Slider.tsx +++ b/src/Slider.tsx @@ -13,6 +13,7 @@ import type { SliderContextProps } from './context'; import SliderContext from './context'; import useDrag from './hooks/useDrag'; import useOffset from './hooks/useOffset'; +import useRange from './hooks/useRange'; import type { AriaValueFormat, Direction, @@ -35,6 +36,11 @@ import type { * - keyboard support pushable */ +export type RangeConfig = { + editable?: boolean; + draggableTrack?: boolean; +}; + export interface SliderProps { prefixCls?: string; className?: string; @@ -51,7 +57,7 @@ export interface SliderProps { onBlur?: (e: React.FocusEvent) => void; // Value - range?: boolean; + range?: boolean | RangeConfig; count?: number; min?: number; max?: number; @@ -68,8 +74,6 @@ export interface SliderProps { // Cross allowCross?: boolean; pushable?: boolean | number; - /** range only */ - draggableTrack?: boolean; // Direction reverse?: boolean; @@ -94,6 +98,7 @@ export interface SliderProps { // Components handleRender?: HandlesProps['handleRender']; activeHandleRender?: HandlesProps['handleRender']; + track?: boolean; // Accessibility tabIndex?: number | number[]; @@ -138,7 +143,6 @@ const Slider = React.forwardRef>((prop // Cross allowCross = true, pushable = false, - draggableTrack, // Direction reverse, @@ -160,6 +164,7 @@ const Slider = React.forwardRef>((prop // Components handleRender, activeHandleRender, + track, // Accessibility tabIndex = 0, @@ -179,6 +184,8 @@ const Slider = React.forwardRef>((prop }, [reverse, vertical]); // ============================ Range ============================= + const [rangeEnabled, rangeEditable, rangeDraggableTrack] = useRange(range); + const mergedMin = React.useMemo(() => (isFinite(min) ? min : 0), [min]); const mergedMax = React.useMemo(() => (isFinite(max) ? max : 100), [max]); @@ -247,7 +254,7 @@ const Slider = React.forwardRef>((prop let returnValues = mergedValue === null ? [] : [val0]; // Format as range - if (range) { + if (rangeEnabled) { returnValues = [...valueList]; // When count provided or value is `undefined`, we fill values @@ -269,13 +276,14 @@ const Slider = React.forwardRef>((prop }); return returnValues; - }, [mergedValue, range, mergedMin, count, formatValue]); + }, [mergedValue, rangeEnabled, mergedMin, count, formatValue]); // =========================== onChange =========================== const rawValuesRef = React.useRef(rawValues); rawValuesRef.current = rawValues; - const getTriggerValue = (triggerValues: number[]) => (range ? triggerValues : triggerValues[0]); + const getTriggerValue = (triggerValues: number[]) => + rangeEnabled ? triggerValues : triggerValues[0]; const triggerChange = (nextValues: number[]) => { // Order first @@ -331,7 +339,7 @@ const Slider = React.forwardRef>((prop cloneNextValues[valueIndex] = newValue; // Fill value to match default 2 - if (range && !rawValues.length && count === undefined) { + if (rangeEnabled && !rawValues.length && count === undefined) { cloneNextValues.push(newValue); } @@ -402,14 +410,14 @@ const Slider = React.forwardRef>((prop // ============================= Drag ============================= const mergedDraggableTrack = React.useMemo(() => { - if (draggableTrack && mergedStep === null) { + if (rangeDraggableTrack && mergedStep === null) { if (process.env.NODE_ENV !== 'production') { warning(false, '`draggableTrack` is not supported when `step` is `null`.'); } return false; } - return draggableTrack; - }, [draggableTrack, mergedStep]); + return rangeDraggableTrack; + }, [rangeDraggableTrack, mergedStep]); const onStartMove: OnStartMove = (e, valueIndex) => { onStartDrag(e, valueIndex); @@ -435,12 +443,12 @@ const Slider = React.forwardRef>((prop // Provide a range values with included [min, max] // Used for Track, Mark & Dot const [includedStart, includedEnd] = React.useMemo(() => { - if (!range) { + if (!rangeEnabled) { return [mergedMin, sortedCacheValues[0]]; } return [sortedCacheValues[0], sortedCacheValues[sortedCacheValues.length - 1]]; - }, [sortedCacheValues, range, mergedMin]); + }, [sortedCacheValues, rangeEnabled, mergedMin]); // ============================= Refs ============================= React.useImperativeHandle(ref, () => ({ @@ -474,7 +482,7 @@ const Slider = React.forwardRef>((prop included, includedStart, includedEnd, - range, + range: rangeEnabled, tabIndex, ariaLabelForHandle, ariaLabelledByForHandle, @@ -492,7 +500,7 @@ const Slider = React.forwardRef>((prop included, includedStart, includedEnd, - range, + rangeEnabled, tabIndex, ariaLabelForHandle, ariaLabelledByForHandle, @@ -521,13 +529,15 @@ const Slider = React.forwardRef>((prop style={{ ...railStyle, ...styles?.rail }} /> - + {track !== false && ( + + )} { + if (range === true || !range) { + return [!!range, false, false]; + } + + return [true, range.editable, range.draggableTrack]; + }, [range]); +} From 2332e705e9c4d62f9224a9d59b1d0a3d687d3c8e 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: Tue, 16 Jul 2024 17:02:53 +0800 Subject: [PATCH 02/14] feat: click to add --- docs/demo/editable.md | 8 ++++++++ docs/examples/editable.tsx | 38 ++++++++++++++++++++++++++++++++++++++ docs/examples/multiple.tsx | 4 +--- src/Slider.tsx | 28 ++++++++++++++++++++++------ 4 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 docs/demo/editable.md create mode 100644 docs/examples/editable.tsx diff --git a/docs/demo/editable.md b/docs/demo/editable.md new file mode 100644 index 000000000..3c31ad9b4 --- /dev/null +++ b/docs/demo/editable.md @@ -0,0 +1,8 @@ +--- +title: Multiple +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/editable.tsx b/docs/examples/editable.tsx new file mode 100644 index 000000000..006d3e7b8 --- /dev/null +++ b/docs/examples/editable.tsx @@ -0,0 +1,38 @@ +/* eslint react/no-multi-comp: 0, no-console: 0 */ +import Slider from 'rc-slider'; +import React from 'react'; +import '../../assets/index.less'; + +const style: React.CSSProperties = { + width: 400, + margin: 50, +}; + +export default () => { + const [value, setValue] = React.useState([0, 50, 80]); + + return ( +
+
+ { + console.error('Next', nextValue); + setValue(nextValue as any); + }} + styles={{ + rail: { + background: `linear-gradient(to right, blue, red)`, + }, + }} + /> +
+
+ ); +}; diff --git a/docs/examples/multiple.tsx b/docs/examples/multiple.tsx index 25a35a8cb..5f5e96301 100644 --- a/docs/examples/multiple.tsx +++ b/docs/examples/multiple.tsx @@ -23,9 +23,7 @@ export default () => {
>((prop offsetValues, ); + /** + * When `rangeEditable` will insert a new value in the values array. + * Else it will replace the value in the values array. + */ const changeToCloseValue = (newValue: number, e?: React.MouseEvent) => { if (!disabled) { + // Create new values + const cloneNextValues = [...rawValues]; + let valueIndex = 0; + let valueBeforeIndex = 0; // Record the index which value < newValue let valueDist = mergedMax - mergedMin; rawValues.forEach((val, index) => { @@ -331,15 +339,23 @@ const Slider = React.forwardRef>((prop valueDist = dist; valueIndex = index; } + + if (val < newValue) { + valueBeforeIndex = index; + } }); - // Create new values - const cloneNextValues = [...rawValues]; + let focusIndex = valueIndex; - cloneNextValues[valueIndex] = newValue; + if (rangeEditable && valueDist !== 0) { + cloneNextValues.splice(valueBeforeIndex + 1, 0, newValue); + focusIndex = valueBeforeIndex + 1; + } else { + cloneNextValues[valueIndex] = newValue; + } // Fill value to match default 2 - if (rangeEnabled && !rawValues.length && count === undefined) { + if (rangeEnabled && !cloneNextValues.length && count === undefined) { cloneNextValues.push(newValue); } @@ -347,8 +363,8 @@ const Slider = React.forwardRef>((prop triggerChange(cloneNextValues); if (e) { (document.activeElement as HTMLElement)?.blur?.(); - handlesRef.current.focus(valueIndex); - onStartDrag(e, valueIndex, cloneNextValues); + handlesRef.current.focus(focusIndex); + onStartDrag(e, focusIndex, cloneNextValues); } } }; From 1b9468db7fe96384531946df26866cb935665cca 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: Tue, 16 Jul 2024 17:04:48 +0800 Subject: [PATCH 03/14] chore: click to move --- src/hooks/useDrag.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index 49fb8fc80..3c47fe954 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -105,6 +105,7 @@ function useDrag( setDraggingIndex(valueIndex); setDraggingValue(originValue); setOriginValues(initialValues); + setCacheValues(initialValues); const { pageX: startX, pageY: startY } = getPosition(e); From e0b6fe41b91a298e63c92b6e7b6d1926295dd59a 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: Tue, 16 Jul 2024 17:14:05 +0800 Subject: [PATCH 04/14] feat: key to delete --- src/Handles/Handle.tsx | 7 +++++++ src/Handles/index.tsx | 1 + src/Slider.tsx | 14 ++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/src/Handles/Handle.tsx b/src/Handles/Handle.tsx index bf802145e..ae0935195 100644 --- a/src/Handles/Handle.tsx +++ b/src/Handles/Handle.tsx @@ -20,6 +20,7 @@ export interface HandleProps valueIndex: number; dragging: boolean; onStartMove: OnStartMove; + onDelete: (index: number) => void; onOffsetChange: (value: number | 'min' | 'max', valueIndex: number) => void; onFocus: (e: React.FocusEvent, index: number) => void; onMouseEnter: (e: React.MouseEvent, index: number) => void; @@ -37,6 +38,7 @@ const Handle = React.forwardRef((props, ref) => { value, valueIndex, onStartMove, + onDelete, style, render, dragging, @@ -118,6 +120,11 @@ const Handle = React.forwardRef((props, ref) => { case KeyCode.PAGE_DOWN: offset = -2; break; + + case KeyCode.BACKSPACE: + case KeyCode.DELETE: + onDelete(valueIndex); + break; } if (offset !== null) { diff --git a/src/Handles/index.tsx b/src/Handles/index.tsx index c412c9c96..e2b952136 100644 --- a/src/Handles/index.tsx +++ b/src/Handles/index.tsx @@ -12,6 +12,7 @@ export interface HandlesProps { onOffsetChange: (value: number | 'min' | 'max', valueIndex: number) => void; onFocus?: (e: React.FocusEvent) => void; onBlur?: (e: React.FocusEvent) => void; + onDelete: (index: number) => void; handleRender?: HandleProps['render']; /** * When config `activeHandleRender`, diff --git a/src/Slider.tsx b/src/Slider.tsx index 1104d88fd..b7cc04938 100644 --- a/src/Slider.tsx +++ b/src/Slider.tsx @@ -369,6 +369,19 @@ const Slider = React.forwardRef>((prop } }; + const onDelete = (index: number) => { + if (!disabled && rangeEditable) { + const cloneNextValues = [...rawValues]; + cloneNextValues.splice(index, 1); + + onBeforeChange?.(getTriggerValue(cloneNextValues)); + triggerChange(cloneNextValues); + + const nextFocusIndex = Math.max(0, index - 1); + handlesRef.current.focus(nextFocusIndex); + } + }; + // ============================ Click ============================= const onSliderMouseDown: React.MouseEventHandler = (e) => { e.preventDefault(); @@ -576,6 +589,7 @@ const Slider = React.forwardRef>((prop handleRender={handleRender} activeHandleRender={activeHandleRender} onChangeComplete={finishChange} + onDelete={rangeEditable ? onDelete : undefined} /> From 58b7761581b6e5857c9da7af215e2f31e7790799 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: Tue, 16 Jul 2024 19:20:38 +0800 Subject: [PATCH 05/14] chore: support drag to remove --- assets/index.less | 10 ++++++--- docs/examples/editable.tsx | 5 ++++- src/Handles/Handle.tsx | 5 +++++ src/Handles/index.tsx | 43 +++++++++++++++++++++++--------------- src/Slider.tsx | 30 +++++++++++++------------- src/hooks/useDrag.ts | 27 +++++++++++++++++++++++- 6 files changed, 84 insertions(+), 36 deletions(-) diff --git a/assets/index.less b/assets/index.less index aa10b3d3a..e2cb4e411 100644 --- a/assets/index.less +++ b/assets/index.less @@ -73,6 +73,10 @@ &-dragging&-dragging&-dragging { border-color: tint(@primary-color, 20%); box-shadow: 0 0 0 5px tint(@primary-color, 50%); + + &-delete { + opacity: 0; + } } &:focus { @@ -186,11 +190,11 @@ left: 5px; width: 4px; } - + &-track-draggable { - border-top:0; - border-bottom: 0; + border-top: 0; border-right: 5px solid rgba(0, 0, 0, 0); + border-bottom: 0; border-left: 5px solid rgba(0, 0, 0, 0); transform: translateX(-5px); } diff --git a/docs/examples/editable.tsx b/docs/examples/editable.tsx index 006d3e7b8..06c866dce 100644 --- a/docs/examples/editable.tsx +++ b/docs/examples/editable.tsx @@ -23,9 +23,12 @@ export default () => { max={100} value={value} onChange={(nextValue) => { - console.error('Next', nextValue); + // console.error('Next', nextValue); setValue(nextValue as any); }} + onChangeComplete={(nextValue) => { + console.log('Complete', nextValue); + }} styles={{ rail: { background: `linear-gradient(to right, blue, red)`, diff --git a/src/Handles/Handle.tsx b/src/Handles/Handle.tsx index ae0935195..1a54f6ccd 100644 --- a/src/Handles/Handle.tsx +++ b/src/Handles/Handle.tsx @@ -10,6 +10,7 @@ interface RenderProps { prefixCls: string; value: number; dragging: boolean; + draggingDelete: boolean; } export interface HandleProps @@ -19,6 +20,7 @@ export interface HandleProps value: number; valueIndex: number; dragging: boolean; + draggingDelete: boolean; onStartMove: OnStartMove; onDelete: (index: number) => void; onOffsetChange: (value: number | 'min' | 'max', valueIndex: number) => void; @@ -42,6 +44,7 @@ const Handle = React.forwardRef((props, ref) => { style, render, dragging, + draggingDelete, onOffsetChange, onChangeComplete, onFocus, @@ -184,6 +187,7 @@ const Handle = React.forwardRef((props, ref) => { { [`${handlePrefixCls}-${valueIndex + 1}`]: valueIndex !== null && range, [`${handlePrefixCls}-dragging`]: dragging, + [`${handlePrefixCls}-dragging-delete`]: draggingDelete, }, classNames.handle, )} @@ -204,6 +208,7 @@ const Handle = React.forwardRef((props, ref) => { prefixCls, value, dragging, + draggingDelete, }); } diff --git a/src/Handles/index.tsx b/src/Handles/index.tsx index e2b952136..c7a4f97bd 100644 --- a/src/Handles/index.tsx +++ b/src/Handles/index.tsx @@ -21,6 +21,7 @@ export interface HandlesProps { */ activeHandleRender?: HandleProps['render']; draggingIndex: number; + draggingDelete: boolean; onChangeComplete?: () => void; } @@ -38,6 +39,7 @@ const Handles = React.forwardRef((props, ref) => { handleRender, activeHandleRender, draggingIndex, + draggingDelete, onFocus, ...restProps } = props; @@ -75,24 +77,30 @@ const Handles = React.forwardRef((props, ref) => { return ( <> - {values.map((value, index) => ( - { - if (!node) { - delete handlesRef.current[index]; - } else { - handlesRef.current[index] = node; - } - }} - dragging={draggingIndex === index} - style={getIndex(style, index)} - key={index} - value={value} - valueIndex={index} - {...handleProps} - /> - ))} + {values.map((value, index) => { + const dragging = draggingIndex === index; + + return ( + { + if (!node) { + delete handlesRef.current[index]; + } else { + handlesRef.current[index] = node; + } + }} + dragging={dragging} + draggingDelete={dragging && draggingDelete} + style={getIndex(style, index)} + key={index} + value={value} + valueIndex={index} + {...handleProps} + /> + ); + })} + {/* Used for render tooltip, this is not a real handle */} {activeHandleRender && ( ((props, ref) => { value={values[activeIndex]} valueIndex={null} dragging={draggingIndex !== -1} + draggingDelete={draggingDelete} render={activeHandleRender} style={{ pointerEvents: 'none' }} tabIndex={null} diff --git a/src/Slider.tsx b/src/Slider.tsx index b7cc04938..f9c274dfc 100644 --- a/src/Slider.tsx +++ b/src/Slider.tsx @@ -308,7 +308,20 @@ const Slider = React.forwardRef>((prop onChangeComplete?.(finishValue); }; - const [draggingIndex, draggingValue, cacheValues, onStartDrag] = useDrag( + const onDelete = (index: number) => { + if (!disabled && rangeEditable) { + const cloneNextValues = [...rawValues]; + cloneNextValues.splice(index, 1); + + onBeforeChange?.(getTriggerValue(cloneNextValues)); + triggerChange(cloneNextValues); + + const nextFocusIndex = Math.max(0, index - 1); + handlesRef.current.focus(nextFocusIndex); + } + }; + + const [draggingIndex, draggingValue, draggingDelete, cacheValues, onStartDrag] = useDrag( containerRef, direction, rawValues, @@ -318,6 +331,7 @@ const Slider = React.forwardRef>((prop triggerChange, finishChange, offsetValues, + onDelete, ); /** @@ -369,19 +383,6 @@ const Slider = React.forwardRef>((prop } }; - const onDelete = (index: number) => { - if (!disabled && rangeEditable) { - const cloneNextValues = [...rawValues]; - cloneNextValues.splice(index, 1); - - onBeforeChange?.(getTriggerValue(cloneNextValues)); - triggerChange(cloneNextValues); - - const nextFocusIndex = Math.max(0, index - 1); - handlesRef.current.focus(nextFocusIndex); - } - }; - // ============================ Click ============================= const onSliderMouseDown: React.MouseEventHandler = (e) => { e.preventDefault(); @@ -582,6 +583,7 @@ const Slider = React.forwardRef>((prop style={handleStyle} values={cacheValues} draggingIndex={draggingIndex} + draggingDelete={draggingDelete} onStartMove={onStartMove} onOffsetChange={onHandleOffsetChange} onFocus={onFocus} diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index 3c47fe954..82f79e9d6 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -3,6 +3,9 @@ import * as React from 'react'; import type { Direction, OnStartMove } from '../interface'; import type { OffsetValues } from './useOffset'; +/** Drag to delete offset. It's a user experience number for dragging out */ +const REMOVE_DIST = 125; + function getPosition(e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) { const obj = 'touches' in e ? e.touches[0] : e; @@ -19,14 +22,17 @@ function useDrag( triggerChange: (values: number[]) => void, finishChange: () => void, offsetValues: OffsetValues, + onDelete: (index: number) => void, ): [ draggingIndex: number, draggingValue: number, + draggingDelete: boolean, returnValues: number[], onStartMove: OnStartMove, ] { const [draggingValue, setDraggingValue] = React.useState(null); const [draggingIndex, setDraggingIndex] = React.useState(-1); + const [draggingDelete, setDraggingDelete] = React.useState(false); const [cacheValues, setCacheValues] = React.useState(rawValues); const [originValues, setOriginValues] = React.useState(rawValues); @@ -95,6 +101,12 @@ function useDrag( } }); + const deleteIfNeed = useEvent(() => { + if (draggingDelete) { + onDelete(draggingIndex); + } + }); + const onStartMove: OnStartMove = (e, valueIndex, startValues?: number[]) => { e.stopPropagation(); @@ -106,6 +118,7 @@ function useDrag( setDraggingValue(originValue); setOriginValues(initialValues); setCacheValues(initialValues); + setDraggingDelete(false); const { pageX: startX, pageY: startY } = getPosition(e); @@ -120,23 +133,33 @@ function useDrag( const { width, height } = containerRef.current.getBoundingClientRect(); let offSetPercent: number; + let removeDist: number; + switch (direction) { case 'btt': offSetPercent = -offsetY / height; + removeDist = offsetX; break; case 'ttb': offSetPercent = offsetY / height; + removeDist = offsetX; break; case 'rtl': offSetPercent = -offsetX / width; + removeDist = offsetY; break; default: offSetPercent = offsetX / width; + removeDist = offsetY; } + updateCacheValue(valueIndex, offSetPercent); + + // Check if need mark remove + setDraggingDelete(Math.abs(removeDist) > REMOVE_DIST); }; // End @@ -150,6 +173,8 @@ function useDrag( mouseMoveEventRef.current = null; mouseUpEventRef.current = null; + deleteIfNeed(); + setDraggingIndex(-1); finishChange(); }; @@ -172,7 +197,7 @@ function useDrag( : rawValues; }, [rawValues, cacheValues]); - return [draggingIndex, draggingValue, returnValues, onStartMove]; + return [draggingIndex, draggingValue, draggingDelete, returnValues, onStartMove]; } export default useDrag; From 977d4c5b6f3049125f6df6d3290d31fa21d83d2b 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: Wed, 17 Jul 2024 11:10:57 +0800 Subject: [PATCH 06/14] chore: drag to delete --- docs/examples/editable.tsx | 2 +- src/hooks/useDrag.ts | 90 +++++++++++++++++++++----------------- src/hooks/useRange.ts | 9 +++- 3 files changed, 59 insertions(+), 42 deletions(-) diff --git a/docs/examples/editable.tsx b/docs/examples/editable.tsx index 06c866dce..fa2e56111 100644 --- a/docs/examples/editable.tsx +++ b/docs/examples/editable.tsx @@ -23,7 +23,7 @@ export default () => { max={100} value={value} onChange={(nextValue) => { - // console.error('Next', nextValue); + // console.log('Change:', nextValue); setValue(nextValue as any); }} onChangeComplete={(nextValue) => { diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index 82f79e9d6..f5762ac47 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -56,50 +56,56 @@ function useDrag( [], ); - const flushValues = (nextValues: number[], nextValue?: number) => { + const flushValues = (nextValues: number[], nextValue?: number, deleteMark?: boolean) => { // Perf: Only update state when value changed if (cacheValues.some((val, i) => val !== nextValues[i])) { if (nextValue !== undefined) { setDraggingValue(nextValue); } setCacheValues(nextValues); - triggerChange(nextValues); + + let changeValues = nextValues; + if (deleteMark) { + changeValues = nextValues.filter((_, i) => i !== draggingIndex); + } + console.log('flushValues', changeValues, nextValues, draggingIndex, deleteMark); + triggerChange(changeValues); } }; - const updateCacheValue = useEvent((valueIndex: number, offsetPercent: number) => { - // Basic point offset - - if (valueIndex === -1) { - // >>>> Dragging on the track - const startValue = originValues[0]; - const endValue = originValues[originValues.length - 1]; - const maxStartOffset = min - startValue; - const maxEndOffset = max - endValue; - - // Get valid offset - let offset = offsetPercent * (max - min); - offset = Math.max(offset, maxStartOffset); - offset = Math.min(offset, maxEndOffset); - - // Use first value to revert back of valid offset (like steps marks) - const formatStartValue = formatValue(startValue + offset); - offset = formatStartValue - startValue; - const cloneCacheValues = originValues.map((val) => val + offset); - flushValues(cloneCacheValues); - } else { - // >>>> Dragging on the handle - const offsetDist = (max - min) * offsetPercent; - - // Always start with the valueIndex origin value - const cloneValues = [...cacheValues]; - cloneValues[valueIndex] = originValues[valueIndex]; - - const next = offsetValues(cloneValues, offsetDist, valueIndex, 'dist'); - - flushValues(next.values, next.value); - } - }); + const updateCacheValue = useEvent( + (valueIndex: number, offsetPercent: number, deleteMark: boolean) => { + if (valueIndex === -1) { + // >>>> Dragging on the track + const startValue = originValues[0]; + const endValue = originValues[originValues.length - 1]; + const maxStartOffset = min - startValue; + const maxEndOffset = max - endValue; + + // Get valid offset + let offset = offsetPercent * (max - min); + offset = Math.max(offset, maxStartOffset); + offset = Math.min(offset, maxEndOffset); + + // Use first value to revert back of valid offset (like steps marks) + const formatStartValue = formatValue(startValue + offset); + offset = formatStartValue - startValue; + const cloneCacheValues = originValues.map((val) => val + offset); + flushValues(cloneCacheValues); + } else { + // >>>> Dragging on the handle + const offsetDist = (max - min) * offsetPercent; + + // Always start with the valueIndex origin value + const cloneValues = [...cacheValues]; + cloneValues[valueIndex] = originValues[valueIndex]; + + const next = offsetValues(cloneValues, offsetDist, valueIndex, 'dist'); + + flushValues(next.values, next.value, deleteMark); + } + }, + ); const deleteIfNeed = useEvent(() => { if (draggingDelete) { @@ -156,10 +162,11 @@ function useDrag( removeDist = offsetY; } - updateCacheValue(valueIndex, offSetPercent); - // Check if need mark remove - setDraggingDelete(Math.abs(removeDist) > REMOVE_DIST); + const deleteMark = Math.abs(removeDist) > REMOVE_DIST; + setDraggingDelete(deleteMark); + + updateCacheValue(valueIndex, offSetPercent, deleteMark); }; // End @@ -190,12 +197,15 @@ function useDrag( // Only return cache value when it mapping with rawValues const returnValues = React.useMemo(() => { const sourceValues = [...rawValues].sort((a, b) => a - b); - const targetValues = [...cacheValues].sort((a, b) => a - b); + + const targetValues = ( + draggingDelete ? cacheValues.filter((_, i) => i !== draggingIndex) : [...cacheValues] + ).sort((a, b) => a - b); return sourceValues.every((val, index) => val === targetValues[index]) ? cacheValues : rawValues; - }, [rawValues, cacheValues]); + }, [rawValues, cacheValues, draggingDelete, draggingIndex]); return [draggingIndex, draggingValue, draggingDelete, returnValues, onStartMove]; } diff --git a/src/hooks/useRange.ts b/src/hooks/useRange.ts index 50e745cfb..ff109f239 100644 --- a/src/hooks/useRange.ts +++ b/src/hooks/useRange.ts @@ -1,3 +1,4 @@ +import { warning } from 'rc-util/lib/warning'; import { useMemo } from 'react'; import type { SliderProps } from '../Slider'; @@ -9,6 +10,12 @@ export default function useRange( return [!!range, false, false]; } - return [true, range.editable, range.draggableTrack]; + const { editable, draggableTrack } = range; + + if (process.env.NODE_ENV !== 'production') { + warning(!editable || !draggableTrack, '`editable` can not work with `draggableTrack`.'); + } + + return [true, editable, !editable && draggableTrack]; }, [range]); } From c6da4793e31e9ab762ea602fdd7e55501d16451b 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: Wed, 17 Jul 2024 11:17:14 +0800 Subject: [PATCH 07/14] chore: dragable --- docs/examples/editable.tsx | 2 +- src/Slider.tsx | 23 ++++++++++------------- src/hooks/useDrag.ts | 10 +--------- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/docs/examples/editable.tsx b/docs/examples/editable.tsx index fa2e56111..4cd785a60 100644 --- a/docs/examples/editable.tsx +++ b/docs/examples/editable.tsx @@ -23,7 +23,7 @@ export default () => { max={100} value={value} onChange={(nextValue) => { - // console.log('Change:', nextValue); + console.log('Change:', nextValue); setValue(nextValue as any); }} onChangeComplete={(nextValue) => { diff --git a/src/Slider.tsx b/src/Slider.tsx index f9c274dfc..31de66ab2 100644 --- a/src/Slider.tsx +++ b/src/Slider.tsx @@ -1,4 +1,5 @@ import cls from 'classnames'; +import { useEvent } from 'rc-util'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import isEqual from 'rc-util/lib/isEqual'; import warning from 'rc-util/lib/warning'; @@ -279,34 +280,31 @@ const Slider = React.forwardRef>((prop }, [mergedValue, rangeEnabled, mergedMin, count, formatValue]); // =========================== onChange =========================== - const rawValuesRef = React.useRef(rawValues); - rawValuesRef.current = rawValues; - const getTriggerValue = (triggerValues: number[]) => rangeEnabled ? triggerValues : triggerValues[0]; - const triggerChange = (nextValues: number[]) => { + const triggerChange = useEvent((nextValues: number[]) => { // Order first const cloneNextValues = [...nextValues].sort((a, b) => a - b); // Trigger event if needed - if (onChange && !isEqual(cloneNextValues, rawValuesRef.current, true)) { + if (onChange && !isEqual(cloneNextValues, rawValues, true)) { onChange(getTriggerValue(cloneNextValues)); } // We set this later since it will re-render component immediately setValue(cloneNextValues); - }; + }); - const finishChange = () => { - const finishValue = getTriggerValue(rawValuesRef.current); + const finishChange = useEvent(() => { + const finishValue = getTriggerValue(rawValues); onAfterChange?.(finishValue); warning( !onAfterChange, '[rc-slider] `onAfterChange` is deprecated. Please use `onChangeComplete` instead.', ); onChangeComplete?.(finishValue); - }; + }); const onDelete = (index: number) => { if (!disabled && rangeEditable) { @@ -331,7 +329,6 @@ const Slider = React.forwardRef>((prop triggerChange, finishChange, offsetValues, - onDelete, ); /** @@ -449,11 +446,11 @@ const Slider = React.forwardRef>((prop return rangeDraggableTrack; }, [rangeDraggableTrack, mergedStep]); - const onStartMove: OnStartMove = (e, valueIndex) => { + const onStartMove: OnStartMove = useEvent((e, valueIndex) => { onStartDrag(e, valueIndex); - onBeforeChange?.(getTriggerValue(rawValuesRef.current)); - }; + onBeforeChange?.(getTriggerValue(rawValues)); + }); // Auto focus for updated handle const dragging = draggingIndex !== -1; diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index f5762ac47..b849beda8 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -22,7 +22,6 @@ function useDrag( triggerChange: (values: number[]) => void, finishChange: () => void, offsetValues: OffsetValues, - onDelete: (index: number) => void, ): [ draggingIndex: number, draggingValue: number, @@ -68,7 +67,6 @@ function useDrag( if (deleteMark) { changeValues = nextValues.filter((_, i) => i !== draggingIndex); } - console.log('flushValues', changeValues, nextValues, draggingIndex, deleteMark); triggerChange(changeValues); } }; @@ -107,12 +105,6 @@ function useDrag( }, ); - const deleteIfNeed = useEvent(() => { - if (draggingDelete) { - onDelete(draggingIndex); - } - }); - const onStartMove: OnStartMove = (e, valueIndex, startValues?: number[]) => { e.stopPropagation(); @@ -180,7 +172,7 @@ function useDrag( mouseMoveEventRef.current = null; mouseUpEventRef.current = null; - deleteIfNeed(); + // deleteIfNeed(); setDraggingIndex(-1); finishChange(); From da0c477241a5cd98f8960ee8d44053ed40c63eaf 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: Wed, 17 Jul 2024 11:19:21 +0800 Subject: [PATCH 08/14] chore: editable only --- src/Slider.tsx | 1 + src/hooks/useDrag.ts | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Slider.tsx b/src/Slider.tsx index 31de66ab2..721f4e39e 100644 --- a/src/Slider.tsx +++ b/src/Slider.tsx @@ -329,6 +329,7 @@ const Slider = React.forwardRef>((prop triggerChange, finishChange, offsetValues, + rangeEditable, ); /** diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index b849beda8..5b5a37ea1 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -22,6 +22,7 @@ function useDrag( triggerChange: (values: number[]) => void, finishChange: () => void, offsetValues: OffsetValues, + editable: boolean, ): [ draggingIndex: number, draggingValue: number, @@ -155,7 +156,7 @@ function useDrag( } // Check if need mark remove - const deleteMark = Math.abs(removeDist) > REMOVE_DIST; + const deleteMark = editable ? Math.abs(removeDist) > REMOVE_DIST : false; setDraggingDelete(deleteMark); updateCacheValue(valueIndex, offSetPercent, deleteMark); @@ -172,8 +173,6 @@ function useDrag( mouseMoveEventRef.current = null; mouseUpEventRef.current = null; - // deleteIfNeed(); - setDraggingIndex(-1); finishChange(); }; From 8b2efd54ddc82aea1d5c18a5b3529231d9f30259 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: Wed, 17 Jul 2024 11:35:32 +0800 Subject: [PATCH 09/14] chore: good for dragging remove check --- src/hooks/useDrag.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index 5b5a37ea1..cc9e096a0 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -188,15 +188,21 @@ function useDrag( // Only return cache value when it mapping with rawValues const returnValues = React.useMemo(() => { const sourceValues = [...rawValues].sort((a, b) => a - b); + const targetValues = [...cacheValues].sort((a, b) => a - b); - const targetValues = ( - draggingDelete ? cacheValues.filter((_, i) => i !== draggingIndex) : [...cacheValues] - ).sort((a, b) => a - b); + const counts: Record = {}; + targetValues.forEach((val) => { + counts[val] = (counts[val] || 0) + 1; + }); + sourceValues.forEach((val) => { + counts[val] = (counts[val] || 0) - 1; + }); - return sourceValues.every((val, index) => val === targetValues[index]) - ? cacheValues - : rawValues; - }, [rawValues, cacheValues, draggingDelete, draggingIndex]); + const maxDiffCount = editable ? 1 : 0; + const diffCount: number = Object.values(counts).reduce((prev, next) => prev + next, 0); + + return diffCount <= maxDiffCount ? cacheValues : rawValues; + }, [rawValues, cacheValues, editable]); return [draggingIndex, draggingValue, draggingDelete, returnValues, onStartMove]; } From 170def654b2c7639dc497bf5ab254e8af44afff1 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: Wed, 17 Jul 2024 11:49:46 +0800 Subject: [PATCH 10/14] chore: fix save --- docs/examples/editable.tsx | 12 +++++++----- src/Slider.tsx | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/examples/editable.tsx b/docs/examples/editable.tsx index 4cd785a60..8185263ed 100644 --- a/docs/examples/editable.tsx +++ b/docs/examples/editable.tsx @@ -15,15 +15,17 @@ export default () => {
{ - console.log('Change:', nextValue); + // console.error('Change:', nextValue); setValue(nextValue as any); }} onChangeComplete={(nextValue) => { diff --git a/src/Slider.tsx b/src/Slider.tsx index 721f4e39e..78af41525 100644 --- a/src/Slider.tsx +++ b/src/Slider.tsx @@ -366,8 +366,8 @@ const Slider = React.forwardRef>((prop cloneNextValues[valueIndex] = newValue; } - // Fill value to match default 2 - if (rangeEnabled && !cloneNextValues.length && count === undefined) { + // Fill value to match default 2 (only when `rawValues` is empty) + if (rangeEnabled && !rawValues.length && count === undefined) { cloneNextValues.push(newValue); } From 97fb61d55015eb23d0a135a5a1180b75275648ca 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: Wed, 17 Jul 2024 11:57:23 +0800 Subject: [PATCH 11/14] test: fix test case --- tests/{Range.test.js => Range.test.tsx} | 42 +++++++------------ ...Range.test.js.snap => Range.test.tsx.snap} | 0 2 files changed, 15 insertions(+), 27 deletions(-) rename tests/{Range.test.js => Range.test.tsx} (95%) rename tests/__snapshots__/{Range.test.js.snap => Range.test.tsx.snap} (100%) diff --git a/tests/Range.test.js b/tests/Range.test.tsx similarity index 95% rename from tests/Range.test.js rename to tests/Range.test.tsx index 638e22d93..79e1bb66b 100644 --- a/tests/Range.test.js +++ b/tests/Range.test.tsx @@ -5,11 +5,9 @@ import keyCode from 'rc-util/lib/KeyCode'; import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; import { resetWarned } from 'rc-util/lib/warning'; import React from 'react'; -import Slider from '../src/'; +import Slider from '../src'; describe('Range', () => { - let container; - beforeAll(() => { spyElementPrototypes(HTMLElement, { getBoundingClientRect: () => ({ @@ -19,25 +17,16 @@ describe('Range', () => { }); }); - beforeEach(() => { - container = document.createElement('div'); - document.body.appendChild(container); - }); - - afterEach(() => { - document.body.removeChild(container); - }); - function doMouseMove(container, start, end, element = 'rc-slider-handle') { const mouseDown = createEvent.mouseDown(container.getElementsByClassName(element)[0]); - mouseDown.pageX = start; - mouseDown.pageY = start; + (mouseDown as any).pageX = start; + (mouseDown as any).pageY = start; fireEvent(container.getElementsByClassName(element)[0], mouseDown); // Drag const mouseMove = createEvent.mouseMove(document); - mouseMove.pageX = end; - mouseMove.pageY = end; + (mouseMove as any).pageX = end; + (mouseMove as any).pageY = end; fireEvent(document, mouseMove); } @@ -45,14 +34,14 @@ describe('Range', () => { const touchStart = createEvent.touchStart(container.getElementsByClassName(element)[0], { touches: [{}], }); - touchStart.touches[0].pageX = start; + (touchStart as any).touches[0].pageX = start; fireEvent(container.getElementsByClassName(element)[0], touchStart); // Drag const touchMove = createEvent.touchMove(document, { touches: [{}], }); - touchMove.touches[0].pageX = end; + (touchMove as any).touches[0].pageX = end; fireEvent(document, touchMove); } @@ -344,7 +333,7 @@ describe('Range', () => { return ( { + onChange={(values: number[]) => { setValue(values); onChange(values); }} @@ -377,7 +366,7 @@ describe('Range', () => { const onChange = jest.fn(); const { container, unmount } = render( - , + , ); // Do move @@ -507,15 +496,15 @@ describe('Range', () => { it('focus()', () => { const handleFocus = jest.fn(); const { container } = render(); - container.getElementsByClassName('rc-slider-handle')[0].focus(); + container.querySelector('.rc-slider-handle').focus(); expect(handleFocus).toBeCalled(); }); it('blur()', () => { const handleBlur = jest.fn(); const { container } = render(); - container.getElementsByClassName('rc-slider-handle')[0].focus(); - container.getElementsByClassName('rc-slider-handle')[0].blur(); + container.querySelector('.rc-slider-handle').focus(); + container.querySelector('.rc-slider-handle').blur(); expect(handleBlur).toHaveBeenCalled(); }); }); @@ -524,7 +513,7 @@ describe('Range', () => { const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); resetWarned(); - render(); + render(); expect(errorSpy).toHaveBeenCalledWith( 'Warning: `draggableTrack` is not supported when `step` is `null`.', @@ -534,16 +523,15 @@ describe('Range', () => { it('Track should have the correct thickness', () => { const { container } = render( - , + , ); const { container: containerVertical } = render( , diff --git a/tests/__snapshots__/Range.test.js.snap b/tests/__snapshots__/Range.test.tsx.snap similarity index 100% rename from tests/__snapshots__/Range.test.js.snap rename to tests/__snapshots__/Range.test.tsx.snap From d435d12248eaae27d9c8d351cdc38ac77a87ab6e 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: Wed, 17 Jul 2024 13:58:19 +0800 Subject: [PATCH 12/14] chore: adjust range --- docs/examples/editable.tsx | 12 ++++++------ src/hooks/useDrag.ts | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/examples/editable.tsx b/docs/examples/editable.tsx index 8185263ed..cba0c7e5c 100644 --- a/docs/examples/editable.tsx +++ b/docs/examples/editable.tsx @@ -15,15 +15,15 @@ export default () => {
{ // console.error('Change:', nextValue); setValue(nextValue as any); diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index cc9e096a0..769013890 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -4,7 +4,7 @@ import type { Direction, OnStartMove } from '../interface'; import type { OffsetValues } from './useOffset'; /** Drag to delete offset. It's a user experience number for dragging out */ -const REMOVE_DIST = 125; +const REMOVE_DIST = 130; function getPosition(e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) { const obj = 'touches' in e ? e.touches[0] : e; From e6957fa175f70b10f57eb1b961f1c53033345c94 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: Wed, 17 Jul 2024 14:33:06 +0800 Subject: [PATCH 13/14] test: add test case --- docs/examples/editable.tsx | 2 +- tests/Range.test.tsx | 89 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/docs/examples/editable.tsx b/docs/examples/editable.tsx index cba0c7e5c..051ef4e8f 100644 --- a/docs/examples/editable.tsx +++ b/docs/examples/editable.tsx @@ -25,7 +25,7 @@ export default () => { value={value} // defaultValue={null} onChange={(nextValue) => { - // console.error('Change:', nextValue); + console.error('Change:', nextValue); setValue(nextValue as any); }} onChangeComplete={(nextValue) => { diff --git a/tests/Range.test.tsx b/tests/Range.test.tsx index 79e1bb66b..3b7664556 100644 --- a/tests/Range.test.tsx +++ b/tests/Range.test.tsx @@ -13,15 +13,37 @@ describe('Range', () => { getBoundingClientRect: () => ({ width: 100, height: 100, + left: 0, + top: 0, + bottom: 100, + right: 100, }), }); }); - function doMouseMove(container, start, end, element = 'rc-slider-handle') { + beforeEach(() => { + resetWarned(); + }); + + function doMouseDown(container: HTMLElement, start: number, element = 'rc-slider-handle') { const mouseDown = createEvent.mouseDown(container.getElementsByClassName(element)[0]); (mouseDown as any).pageX = start; (mouseDown as any).pageY = start; + Object.defineProperties(mouseDown, { + clientX: { get: () => start }, + clientY: { get: () => start }, + }); + fireEvent(container.getElementsByClassName(element)[0], mouseDown); + } + + function doMouseMove( + container: HTMLElement, + start: number, + end: number, + element = 'rc-slider-handle', + ) { + doMouseDown(container, start, element); // Drag const mouseMove = createEvent.mouseMove(document); @@ -30,7 +52,12 @@ describe('Range', () => { fireEvent(document, mouseMove); } - function doTouchMove(container, start, end, element = 'rc-slider-handle') { + function doTouchMove( + container: HTMLElement, + start: number, + end: number, + element = 'rc-slider-handle', + ) { const touchStart = createEvent.touchStart(container.getElementsByClassName(element)[0], { touches: [{}], }); @@ -512,7 +539,6 @@ describe('Range', () => { it('warning for `draggableTrack` and `mergedStep=null`', () => { const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - resetWarned(); render(); expect(errorSpy).toHaveBeenCalledWith( @@ -587,4 +613,61 @@ describe('Range', () => { expect(container.querySelector('.rc-slider-handle')).toHaveClass('my-handle'); expect(container.querySelector('.rc-slider-rail')).toHaveClass('my-rail'); }); + + describe('editable', () => { + it('click to create', () => { + const onChange = jest.fn(); + const { container } = render( + , + ); + + doMouseDown(container, 50, 'rc-slider'); + + expect(onChange).toHaveBeenCalledWith([0, 50, 100]); + }); + + it('can not editable with draggableTrack at same time', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + render(); + + expect(errorSpy).toHaveBeenCalledWith( + 'Warning: `editable` can not work with `draggableTrack`.', + ); + errorSpy.mockRestore(); + }); + + it('drag out to remove', () => { + const onChange = jest.fn(); + const onChangeComplete = jest.fn(); + const { container } = render( + , + ); + + doMouseMove(container, 0, 1000); + expect(onChange).toHaveBeenCalledWith([50, 100]); + + // Fire mouse up + fireEvent.mouseUp(container.querySelector('.rc-slider-handle')); + expect(onChangeComplete).toHaveBeenCalledWith([50, 100]); + }); + + // it('key to delete', () => { + // fireEvent.keyDown(container.getElementsByClassName('rc-slider-handle')[1], { + // keyCode: keyCode.RIGHT, + // }); + // }); + }); }); From b86d1695bbe045be127c2cc806add269e048fe56 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: Wed, 17 Jul 2024 15:00:38 +0800 Subject: [PATCH 14/14] test: coverage --- src/hooks/useDrag.ts | 2 +- tests/Range.test.tsx | 77 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index 769013890..37de73a42 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -58,7 +58,7 @@ function useDrag( const flushValues = (nextValues: number[], nextValue?: number, deleteMark?: boolean) => { // Perf: Only update state when value changed - if (cacheValues.some((val, i) => val !== nextValues[i])) { + if (cacheValues.some((val, i) => val !== nextValues[i]) || deleteMark) { if (nextValue !== undefined) { setDraggingValue(nextValue); } diff --git a/tests/Range.test.tsx b/tests/Range.test.tsx index 3b7664556..63010ee4d 100644 --- a/tests/Range.test.tsx +++ b/tests/Range.test.tsx @@ -642,32 +642,79 @@ describe('Range', () => { errorSpy.mockRestore(); }); - it('drag out to remove', () => { + describe('drag out to remove', () => { + it('uncontrolled', () => { + const onChange = jest.fn(); + const onChangeComplete = jest.fn(); + const { container } = render( + , + ); + + doMouseMove(container, 0, 1000); + expect(onChange).toHaveBeenCalledWith([50, 100]); + + // Fire mouse up + fireEvent.mouseUp(container.querySelector('.rc-slider-handle')); + expect(onChangeComplete).toHaveBeenCalledWith([50, 100]); + }); + + it('controlled', () => { + const onChange = jest.fn(); + const onChangeComplete = jest.fn(); + + const Demo = () => { + const [value, setValue] = React.useState([0, 50, 100]); + return ( + { + onChange(nextValue); + setValue(nextValue); + }} + onChangeComplete={onChangeComplete} + min={0} + max={100} + value={value} + range={{ editable: true }} + /> + ); + }; + + const { container } = render(); + + doMouseMove(container, 0, 1000); + expect(onChange).toHaveBeenCalledWith([50, 100]); + + // Fire mouse up + fireEvent.mouseUp(container.querySelector('.rc-slider-handle')); + expect(onChangeComplete).toHaveBeenCalledWith([50, 100]); + }); + }); + + it('key to delete', () => { const onChange = jest.fn(); - const onChangeComplete = jest.fn(); + const { container } = render( , ); - doMouseMove(container, 0, 1000); - expect(onChange).toHaveBeenCalledWith([50, 100]); + fireEvent.keyDown(container.querySelectorAll('.rc-slider-handle')[1], { + keyCode: keyCode.DELETE, + }); - // Fire mouse up - fireEvent.mouseUp(container.querySelector('.rc-slider-handle')); - expect(onChangeComplete).toHaveBeenCalledWith([50, 100]); + expect(onChange).toHaveBeenCalledWith([0, 100]); }); - - // it('key to delete', () => { - // fireEvent.keyDown(container.getElementsByClassName('rc-slider-handle')[1], { - // keyCode: keyCode.RIGHT, - // }); - // }); }); });