Skip to content

Commit 4548379

Browse files
authored
feat: range editable support minCount & maxCount (#1011)
* feat: support min & max count * test: update test case
1 parent 77bc971 commit 4548379

File tree

6 files changed

+80
-16
lines changed

6 files changed

+80
-16
lines changed

assets/index.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
cursor: -webkit-grab;
6969
cursor: grab;
7070
opacity: 0.8;
71+
user-select: none;
7172
touch-action: pan-x;
7273

7374
&-dragging&-dragging&-dragging {

docs/examples/editable.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export default () => {
1818
// range
1919
range={{
2020
editable: true,
21+
minCount: 1,
22+
maxCount: 4,
2123
}}
2224
// track={false}
2325
min={0}

src/Slider.tsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ import type {
4040
export type RangeConfig = {
4141
editable?: boolean;
4242
draggableTrack?: boolean;
43+
/** Set min count when `editable` */
44+
minCount?: number;
45+
/** Set max count when `editable` */
46+
maxCount?: number;
4347
};
4448

4549
export interface SliderProps<ValueType = number | number[]> {
@@ -59,6 +63,7 @@ export interface SliderProps<ValueType = number | number[]> {
5963

6064
// Value
6165
range?: boolean | RangeConfig;
66+
/** @deprecated Use `range.minCount` or `range.maxCount` to handle this */
6267
count?: number;
6368
min?: number;
6469
max?: number;
@@ -185,7 +190,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
185190
}, [reverse, vertical]);
186191

187192
// ============================ Range =============================
188-
const [rangeEnabled, rangeEditable, rangeDraggableTrack] = useRange(range);
193+
const [rangeEnabled, rangeEditable, rangeDraggableTrack, minCount, maxCount] = useRange(range);
189194

190195
const mergedMin = React.useMemo(() => (isFinite(min) ? min : 0), [min]);
191196
const mergedMax = React.useMemo(() => (isFinite(max) ? max : 100), [max]);
@@ -312,17 +317,19 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
312317
});
313318

314319
const onDelete = (index: number) => {
315-
if (!disabled && rangeEditable) {
316-
const cloneNextValues = [...rawValues];
317-
cloneNextValues.splice(index, 1);
320+
if (disabled || !rangeEditable || rawValues.length <= minCount) {
321+
return;
322+
}
318323

319-
onBeforeChange?.(getTriggerValue(cloneNextValues));
320-
triggerChange(cloneNextValues);
324+
const cloneNextValues = [...rawValues];
325+
cloneNextValues.splice(index, 1);
321326

322-
const nextFocusIndex = Math.max(0, index - 1);
323-
handlesRef.current.hideHelp();
324-
handlesRef.current.focus(nextFocusIndex);
325-
}
327+
onBeforeChange?.(getTriggerValue(cloneNextValues));
328+
triggerChange(cloneNextValues);
329+
330+
const nextFocusIndex = Math.max(0, index - 1);
331+
handlesRef.current.hideHelp();
332+
handlesRef.current.focus(nextFocusIndex);
326333
};
327334

328335
const [draggingIndex, draggingValue, draggingDelete, cacheValues, onStartDrag] = useDrag(
@@ -336,6 +343,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
336343
finishChange,
337344
offsetValues,
338345
rangeEditable,
346+
minCount,
339347
);
340348

341349
/**
@@ -365,7 +373,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
365373

366374
let focusIndex = valueIndex;
367375

368-
if (rangeEditable && valueDist !== 0) {
376+
if (rangeEditable && valueDist !== 0 && (!maxCount || rawValues.length < maxCount)) {
369377
cloneNextValues.splice(valueBeforeIndex + 1, 0, newValue);
370378
focusIndex = valueBeforeIndex + 1;
371379
} else {

src/hooks/useDrag.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function useDrag(
2323
finishChange: (draggingDelete: boolean) => void,
2424
offsetValues: OffsetValues,
2525
editable: boolean,
26+
minCount: number,
2627
): [
2728
draggingIndex: number,
2829
draggingValue: number,
@@ -159,7 +160,9 @@ function useDrag(
159160
}
160161

161162
// Check if need mark remove
162-
deleteMark = editable ? Math.abs(removeDist) > REMOVE_DIST : false;
163+
deleteMark = editable
164+
? Math.abs(removeDist) > REMOVE_DIST && minCount < cacheValues.length
165+
: false;
163166
setDraggingDelete(deleteMark);
164167

165168
updateCacheValue(valueIndex, offSetPercent, deleteMark);

src/hooks/useRange.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@ import type { SliderProps } from '../Slider';
44

55
export default function useRange(
66
range?: SliderProps['range'],
7-
): [range: boolean, rangeEditable: boolean, rangeDraggableTrack: boolean] {
7+
): [
8+
range: boolean,
9+
rangeEditable: boolean,
10+
rangeDraggableTrack: boolean,
11+
minCount: number,
12+
maxCount?: number,
13+
] {
814
return useMemo(() => {
915
if (range === true || !range) {
10-
return [!!range, false, false];
16+
return [!!range, false, false, 0];
1117
}
1218

13-
const { editable, draggableTrack } = range;
19+
const { editable, draggableTrack, minCount, maxCount } = range;
1420

1521
if (process.env.NODE_ENV !== 'production') {
1622
warning(!editable || !draggableTrack, '`editable` can not work with `draggableTrack`.');
1723
}
1824

19-
return [true, editable, !editable && draggableTrack];
25+
return [true, editable, !editable && draggableTrack, minCount || 0, maxCount];
2026
}, [range]);
2127
}

tests/Range.test.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,5 +751,49 @@ describe('Range', () => {
751751
// 2 handle
752752
expect(container.querySelectorAll('.rc-slider-handle')).toHaveLength(0);
753753
});
754+
755+
it('not remove when minCount', () => {
756+
const onChange = jest.fn();
757+
758+
const { container } = render(
759+
<Slider
760+
onChange={onChange}
761+
min={0}
762+
max={100}
763+
defaultValue={[0]}
764+
range={{ editable: true, minCount: 1 }}
765+
activeHandleRender={(ori) => ori}
766+
/>,
767+
);
768+
769+
const handle = container.querySelector('.rc-slider-handle');
770+
771+
// Key
772+
fireEvent.mouseEnter(handle);
773+
fireEvent.keyDown(handle, {
774+
keyCode: keyCode.DELETE,
775+
});
776+
expect(onChange).not.toHaveBeenCalled();
777+
778+
// Mouse
779+
doMouseMove(container, 0, 1000);
780+
expect(onChange).toHaveBeenCalledWith([100]);
781+
});
782+
783+
it('maxCount not add', () => {
784+
const onChange = jest.fn();
785+
const { container } = render(
786+
<Slider
787+
onChange={onChange}
788+
min={0}
789+
max={100}
790+
value={[0, 100]}
791+
range={{ editable: true, maxCount: 2 }}
792+
/>,
793+
);
794+
795+
doMouseDown(container, 50, 'rc-slider', true);
796+
expect(onChange).toHaveBeenCalledWith([0, 50]);
797+
});
754798
});
755799
});

0 commit comments

Comments
 (0)