Skip to content

Commit

Permalink
feat: range editable support minCount & maxCount (#1011)
Browse files Browse the repository at this point in the history
* feat: support min & max count

* test: update test case
  • Loading branch information
zombieJ authored Jul 20, 2024
1 parent 77bc971 commit 4548379
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 16 deletions.
1 change: 1 addition & 0 deletions assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
cursor: -webkit-grab;
cursor: grab;
opacity: 0.8;
user-select: none;
touch-action: pan-x;

&-dragging&-dragging&-dragging {
Expand Down
2 changes: 2 additions & 0 deletions docs/examples/editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export default () => {
// range
range={{
editable: true,
minCount: 1,
maxCount: 4,
}}
// track={false}
min={0}
Expand Down
30 changes: 19 additions & 11 deletions src/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ import type {
export type RangeConfig = {
editable?: boolean;
draggableTrack?: boolean;
/** Set min count when `editable` */
minCount?: number;
/** Set max count when `editable` */
maxCount?: number;
};

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

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

// ============================ Range =============================
const [rangeEnabled, rangeEditable, rangeDraggableTrack] = useRange(range);
const [rangeEnabled, rangeEditable, rangeDraggableTrack, minCount, maxCount] = useRange(range);

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

const onDelete = (index: number) => {
if (!disabled && rangeEditable) {
const cloneNextValues = [...rawValues];
cloneNextValues.splice(index, 1);
if (disabled || !rangeEditable || rawValues.length <= minCount) {
return;
}

onBeforeChange?.(getTriggerValue(cloneNextValues));
triggerChange(cloneNextValues);
const cloneNextValues = [...rawValues];
cloneNextValues.splice(index, 1);

const nextFocusIndex = Math.max(0, index - 1);
handlesRef.current.hideHelp();
handlesRef.current.focus(nextFocusIndex);
}
onBeforeChange?.(getTriggerValue(cloneNextValues));
triggerChange(cloneNextValues);

const nextFocusIndex = Math.max(0, index - 1);
handlesRef.current.hideHelp();
handlesRef.current.focus(nextFocusIndex);
};

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

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

let focusIndex = valueIndex;

if (rangeEditable && valueDist !== 0) {
if (rangeEditable && valueDist !== 0 && (!maxCount || rawValues.length < maxCount)) {
cloneNextValues.splice(valueBeforeIndex + 1, 0, newValue);
focusIndex = valueBeforeIndex + 1;
} else {
Expand Down
5 changes: 4 additions & 1 deletion src/hooks/useDrag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function useDrag(
finishChange: (draggingDelete: boolean) => void,
offsetValues: OffsetValues,
editable: boolean,
minCount: number,
): [
draggingIndex: number,
draggingValue: number,
Expand Down Expand Up @@ -159,7 +160,9 @@ function useDrag(
}

// Check if need mark remove
deleteMark = editable ? Math.abs(removeDist) > REMOVE_DIST : false;
deleteMark = editable
? Math.abs(removeDist) > REMOVE_DIST && minCount < cacheValues.length
: false;
setDraggingDelete(deleteMark);

updateCacheValue(valueIndex, offSetPercent, deleteMark);
Expand Down
14 changes: 10 additions & 4 deletions src/hooks/useRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ import type { SliderProps } from '../Slider';

export default function useRange(
range?: SliderProps['range'],
): [range: boolean, rangeEditable: boolean, rangeDraggableTrack: boolean] {
): [
range: boolean,
rangeEditable: boolean,
rangeDraggableTrack: boolean,
minCount: number,
maxCount?: number,
] {
return useMemo(() => {
if (range === true || !range) {
return [!!range, false, false];
return [!!range, false, false, 0];
}

const { editable, draggableTrack } = range;
const { editable, draggableTrack, minCount, maxCount } = range;

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

return [true, editable, !editable && draggableTrack];
return [true, editable, !editable && draggableTrack, minCount || 0, maxCount];
}, [range]);
}
44 changes: 44 additions & 0 deletions tests/Range.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -751,5 +751,49 @@ describe('Range', () => {
// 2 handle
expect(container.querySelectorAll('.rc-slider-handle')).toHaveLength(0);
});

it('not remove when minCount', () => {
const onChange = jest.fn();

const { container } = render(
<Slider
onChange={onChange}
min={0}
max={100}
defaultValue={[0]}
range={{ editable: true, minCount: 1 }}
activeHandleRender={(ori) => ori}
/>,
);

const handle = container.querySelector('.rc-slider-handle');

// Key
fireEvent.mouseEnter(handle);
fireEvent.keyDown(handle, {
keyCode: keyCode.DELETE,
});
expect(onChange).not.toHaveBeenCalled();

// Mouse
doMouseMove(container, 0, 1000);
expect(onChange).toHaveBeenCalledWith([100]);
});

it('maxCount not add', () => {
const onChange = jest.fn();
const { container } = render(
<Slider
onChange={onChange}
min={0}
max={100}
value={[0, 100]}
range={{ editable: true, maxCount: 2 }}
/>,
);

doMouseDown(container, 50, 'rc-slider', true);
expect(onChange).toHaveBeenCalledWith([0, 50]);
});
});
});

0 comments on commit 4548379

Please sign in to comment.