-
-
Notifications
You must be signed in to change notification settings - Fork 775
feat: Range 组件支持 disabled 数组形式 #1068
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
bedc645
a7db356
4f28cc4
218f061
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| title: Disabled Handle | ||
| title.zh-CN: 禁用特定滑块 | ||
| nav: | ||
| title: Demo | ||
| path: /demo | ||
| --- | ||
|
|
||
| <code src="../examples/disabled-handle.tsx"></code> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| /* eslint react/no-multi-comp: 0, no-console: 0 */ | ||
| import Slider from '@rc-component/slider'; | ||
| import React, { useState } from 'react'; | ||
| import '../../assets/index.less'; | ||
|
|
||
| const style: React.CSSProperties = { | ||
| width: 400, | ||
| margin: 50, | ||
| }; | ||
|
|
||
| const BasicDisabledHandle = () => { | ||
| const [value, setValue] = useState([0, 30, 60, 100]); | ||
| const [disabled, setDisabled] = useState([true, false, false, true]); | ||
|
|
||
| return ( | ||
| <div> | ||
| <Slider range value={value} onChange={setValue} disabled={disabled} /> | ||
| <div style={{ marginTop: 16 }}> | ||
| {value.map((val, index) => ( | ||
| <label key={index} style={{ marginRight: 16 }}> | ||
| <input | ||
| type="checkbox" | ||
| checked={disabled[index]} | ||
| onChange={() => { | ||
| const newDisabled = [...disabled]; | ||
| newDisabled[index] = !newDisabled[index]; | ||
| setDisabled(newDisabled); | ||
| }} | ||
| /> | ||
| Handle {index + 1} ({val}) {disabled[index] ? 'Disabled' : 'Enabled'} | ||
| </label> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| const DisabledHandleAsBoundary = () => { | ||
| const [value, setValue] = useState([10, 50, 90]); | ||
|
|
||
| return ( | ||
| <div> | ||
| <Slider range value={value} onChange={setValue} disabled={[false, true, false]} /> | ||
| <p style={{ marginTop: 8, color: '#999' }}> | ||
| Middle handle (50) is disabled and acts as a boundary. | ||
| First handle cannot go beyond 50, third handle cannot go below 50. | ||
| Disabled handle has gray border and not-allowed cursor. | ||
| </p> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| const MultipleDisabledBoundaries = () => { | ||
| const [value, setValue] = useState([10, 30, 50, 70, 90]); | ||
|
|
||
| return ( | ||
| <div> | ||
| <Slider range value={value} onChange={setValue} disabled={[true, false, true, false, true]} /> | ||
| <p style={{ marginTop: 8, color: '#999' }}> | ||
| Handles at 10, 50, 90 are disabled. | ||
| Handle at 30 can only move between 10-50, handle at 70 can only move between 50-90. | ||
| </p> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default () => ( | ||
| <div> | ||
| <div style={style}> | ||
| <h3>Basic Disabled Handle</h3> | ||
| <p>Toggle checkboxes to disable/enable specific handles</p> | ||
| <BasicDisabledHandle /> | ||
| </div> | ||
|
|
||
| <div style={style}> | ||
| <h3>Disabled Handle as Boundary</h3> | ||
| <DisabledHandleAsBoundary /> | ||
| </div> | ||
|
|
||
| <div style={style}> | ||
| <h3>Multiple Disabled Boundaries</h3> | ||
| <MultipleDisabledBoundaries /> | ||
| </div> | ||
| </div> | ||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,7 +57,7 @@ export interface SliderProps<ValueType = number | number[]> { | |
| id?: string; | ||
|
|
||
| // Status | ||
| disabled?: boolean; | ||
| disabled?: boolean | boolean[]; | ||
| keyboard?: boolean; | ||
| autoFocus?: boolean; | ||
| onFocus?: (e: React.FocusEvent<HTMLDivElement>) => void; | ||
|
|
@@ -131,8 +131,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop | |
|
|
||
| id, | ||
|
|
||
| // Status | ||
| disabled = false, | ||
| disabled: rawDisabled = false, | ||
| keyboard = true, | ||
| autoFocus, | ||
| onFocus, | ||
|
|
@@ -188,6 +187,24 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop | |
| const handlesRef = React.useRef<HandlesRef>(null); | ||
| const containerRef = React.useRef<HTMLDivElement>(null); | ||
|
|
||
| // ============================ Disabled ============================ | ||
| const disabled = React.useMemo(() => { | ||
| if (typeof rawDisabled === 'boolean') { | ||
| return rawDisabled; | ||
| } | ||
| return rawDisabled.every((d) => d); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里如果给的是 disabled={[true]} ,但是有 2 个节点也会被当成全部 disabled。 |
||
| }, [rawDisabled]); | ||
|
zombieJ marked this conversation as resolved.
Outdated
|
||
|
|
||
| const isHandleDisabled = React.useCallback( | ||
| (index: number) => { | ||
| if (typeof rawDisabled === 'boolean') { | ||
| return rawDisabled; | ||
| } | ||
| return rawDisabled[index] || false; | ||
| }, | ||
| [rawDisabled], | ||
| ); | ||
|
coderabbitai[bot] marked this conversation as resolved.
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| const direction = React.useMemo<Direction>(() => { | ||
| if (vertical) { | ||
| return reverse ? 'ttb' : 'btt'; | ||
|
|
@@ -247,6 +264,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop | |
| markList, | ||
| allowCross, | ||
| mergedPush, | ||
| isHandleDisabled, | ||
| ); | ||
|
|
||
| // ============================ Values ============================ | ||
|
|
@@ -321,7 +339,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop | |
| }); | ||
|
|
||
| const onDelete = (index: number) => { | ||
| if (disabled || !rangeEditable || rawValues.length <= minCount) { | ||
| if (disabled || !rangeEditable || rawValues.length <= minCount || isHandleDisabled(index)) { | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -348,6 +366,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop | |
| offsetValues, | ||
| rangeEditable, | ||
| minCount, | ||
| isHandleDisabled, | ||
| ); | ||
|
|
||
| /** | ||
|
|
@@ -378,10 +397,39 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop | |
| let focusIndex = valueIndex; | ||
|
|
||
| if (rangeEditable && valueDist !== 0 && (!maxCount || rawValues.length < maxCount)) { | ||
| const leftDisabled = isHandleDisabled(valueBeforeIndex); | ||
| const rightDisabled = isHandleDisabled(valueBeforeIndex + 1); | ||
|
|
||
| if (leftDisabled && rightDisabled) { | ||
| return; | ||
| } | ||
|
|
||
| cloneNextValues.splice(valueBeforeIndex + 1, 0, newValue); | ||
| focusIndex = valueBeforeIndex + 1; | ||
| } else { | ||
| if (isHandleDisabled(valueIndex)) { | ||
| let nearestIndex = -1; | ||
| let nearestDist = mergedMax - mergedMin; | ||
|
|
||
| rawValues.forEach((val, index) => { | ||
| if (!isHandleDisabled(index)) { | ||
| const dist = Math.abs(newValue - val); | ||
| if (dist < nearestDist) { | ||
| nearestDist = dist; | ||
| nearestIndex = index; | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| // If all handles are disabled, do nothing | ||
| if (nearestIndex === -1) { | ||
| return; | ||
| } | ||
|
|
||
| valueIndex = nearestIndex; | ||
| } | ||
| cloneNextValues[valueIndex] = newValue; | ||
| focusIndex = valueIndex; | ||
|
EmilyyyLiu marked this conversation as resolved.
Comment on lines
+411
to
+433
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 轨道 / Mark 点击的回退选择会让 handle 穿过禁用边界。 Line 415-423 在最近的 handle 是禁用时,会从整个 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| // Fill value to match default 2 (only when `rawValues` is empty) | ||
|
|
@@ -443,7 +491,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop | |
| const [keyboardValue, setKeyboardValue] = React.useState<number>(null); | ||
|
|
||
| const onHandleOffsetChange = (offset: number | 'min' | 'max', valueIndex: number) => { | ||
| if (!disabled) { | ||
| if (!disabled && !isHandleDisabled(valueIndex)) { | ||
| const next = offsetValues(rawValues, offset, valueIndex); | ||
|
|
||
| onBeforeChange?.(getTriggerValue(rawValues)); | ||
|
|
@@ -546,6 +594,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop | |
| ariaValueTextFormatterForHandle, | ||
| styles: styles || {}, | ||
| classNames: classNames || {}, | ||
| isHandleDisabled, | ||
| }), | ||
| [ | ||
| mergedMin, | ||
|
|
@@ -565,6 +614,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop | |
| ariaValueTextFormatterForHandle, | ||
| styles, | ||
| classNames, | ||
| isHandleDisabled, | ||
| ], | ||
| ); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.