Skip to content
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

[Slider] Replace internal map with Composite metadata #1082

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/react/src/slider/control/SliderControl.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const testRootContext: SliderRootContext = {
}),
handleValueChange: NOOP,
largeStep: 10,
inputIdMap: new Map(),
thumbMap: new Map(),
max: 100,
min: 0,
minStepsBetweenValues: 0,
Expand All @@ -38,12 +38,10 @@ const testRootContext: SliderRootContext = {
touched: false,
},
percentageValues: [0],
registerInputId: () => ({
deregister: NOOP,
}),
registerSliderControl: NOOP,
setActive: NOOP,
setDragging: NOOP,
setThumbMap: NOOP,
setValueState: NOOP,
step: 1,
thumbRefs: { current: [] },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const testRootContext: SliderRootContext = {
}),
handleValueChange: NOOP,
largeStep: 10,
inputIdMap: new Map(),
thumbMap: new Map(),
max: 100,
min: 0,
minStepsBetweenValues: 0,
Expand All @@ -38,12 +38,10 @@ const testRootContext: SliderRootContext = {
touched: false,
},
percentageValues: [0],
registerInputId: () => ({
deregister: NOOP,
}),
registerSliderControl: NOOP,
setActive: NOOP,
setDragging: NOOP,
setThumbMap: NOOP,
setValueState: NOOP,
step: 1,
thumbRefs: { current: [] },
Expand Down
47 changes: 12 additions & 35 deletions packages/react/src/slider/root/useSliderRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useEnhancedEffect } from '../../utils/useEnhancedEffect';
import { useForkRef } from '../../utils/useForkRef';
import { useBaseUiId } from '../../utils/useBaseUiId';
import { valueToPercent } from '../../utils/valueToPercent';
import type { CompositeMetadata } from '../../composite/list/CompositeList';
import type { TextDirection } from '../../direction-provider/DirectionContext';
import { useField } from '../../field/useField';
import { useFieldRootContext } from '../../field/root/FieldRootContext';
Expand All @@ -18,6 +19,7 @@ import { percentToValue, roundValueToStep } from '../utils';
import { asc } from '../utils/asc';
import { setValueIndex } from '../utils/setValueIndex';
import { getSliderValue } from '../utils/getSliderValue';
import { ThumbMetadata } from '../thumb/useSliderThumb';

function findClosest(values: number[], currentValue: number) {
const { index: closestIndex } =
Expand Down Expand Up @@ -153,6 +155,10 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo

const id = useBaseUiId(idProp);

const [thumbMap, setThumbMap] = React.useState(
() => new Map<Node, CompositeMetadata<ThumbMetadata> | null>(),
);

useEnhancedEffect(() => {
setControlId(id);
return () => {
Expand Down Expand Up @@ -184,30 +190,6 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
[inputValidationRef],
);

// Map with index (DOM position) as the key and the id attribute of each thumb <input> element as the value
const [inputIdMap, setInputMap] = React.useState(() => new Map<number, string>());

const deregisterInputId = React.useCallback((index: number) => {
setInputMap((prevMap) => {
const nextMap = new Map(prevMap);
nextMap.delete(index);
return nextMap;
});
}, []);

const registerInputId = React.useCallback(
(index: number, inputId: string | undefined) => {
if (index > -1 && inputId !== undefined) {
setInputMap((prevMap) => new Map(prevMap).set(index, inputId));
}

return {
deregister: deregisterInputId,
};
},
[deregisterInputId],
);

const handleValueChange = React.useCallback(
(value: number | number[], thumbIndex: number, event: Event | React.SyntheticEvent) => {
if (!onValueChange) {
Expand Down Expand Up @@ -423,7 +405,6 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
dragging,
getFingerNewValue,
handleValueChange,
inputIdMap,
largeStep,
max,
min,
Expand All @@ -433,13 +414,14 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
orientation,
percentageValues: values.map((v) => valueToPercent(v, min, max)),
range,
registerInputId,
registerSliderControl,
setActive,
setDragging,
setThumbMap,
setValueState,
step,
tabIndex,
thumbMap,
thumbRefs,
values,
}),
Expand All @@ -454,7 +436,6 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
dragging,
getFingerNewValue,
handleValueChange,
inputIdMap,
largeStep,
max,
min,
Expand All @@ -463,13 +444,14 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
onValueCommitted,
orientation,
range,
registerInputId,
registerSliderControl,
setActive,
setDragging,
setThumbMap,
setValueState,
step,
tabIndex,
thumbMap,
thumbRefs,
values,
],
Expand Down Expand Up @@ -605,7 +587,6 @@ export namespace useSliderRoot {
activeThumb: number,
event: React.SyntheticEvent | Event,
) => void;
inputIdMap: Map<number, string>;
/**
* The large step value of the slider when incrementing or decrementing while the shift key is held,
* or when using Page-Up or Page-Down keys. Snaps to multiples of this value.
Expand All @@ -631,26 +612,22 @@ export namespace useSliderRoot {
* @default 'horizontal'
*/
orientation: Orientation;
registerInputId: (
index: number,
id: string | undefined,
) => {
deregister: (index: number) => void;
};
registerSliderControl: (element: HTMLElement | null) => void;
/**
* The value(s) of the slider as percentages
*/
percentageValues: readonly number[];
setActive: (activeIndex: number) => void;
setDragging: (isDragging: boolean) => void;
setThumbMap: (map: Map<Node, CompositeMetadata<ThumbMetadata> | null>) => void;
setValueState: (newValue: number | number[]) => void;
/**
* The step increment of the slider when incrementing or decrementing. It will snap
* to multiples of this value. Decimal values are supported.
* @default 1
*/
step: number;
thumbMap: Map<Node, CompositeMetadata<ThumbMetadata> | null>;
thumbRefs: React.MutableRefObject<(HTMLElement | null)[]>;
tabIndex?: number;
/**
Expand Down
6 changes: 2 additions & 4 deletions packages/react/src/slider/thumb/SliderThumb.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const testRootContext: SliderRootContext = {
}),
handleValueChange: NOOP,
largeStep: 10,
inputIdMap: new Map(),
thumbMap: new Map(),
max: 100,
min: 0,
minStepsBetweenValues: 0,
Expand All @@ -38,12 +38,10 @@ const testRootContext: SliderRootContext = {
touched: false,
},
percentageValues: [0],
registerInputId: () => ({
deregister: NOOP,
}),
registerSliderControl: NOOP,
setActive: NOOP,
setDragging: NOOP,
setThumbMap: NOOP,
setValueState: NOOP,
step: 1,
thumbRefs: { current: [] },
Expand Down
2 changes: 0 additions & 2 deletions packages/react/src/slider/thumb/SliderThumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ const SliderThumb = React.forwardRef(function SliderThumb(
orientation,
state,
percentageValues,
registerInputId,
step,
tabIndex,
values,
Expand Down Expand Up @@ -97,7 +96,6 @@ const SliderThumb = React.forwardRef(function SliderThumb(
name,
orientation,
percentageValues,
registerInputId,
rootRef: mergedRef,
step,
tabIndex,
Expand Down
30 changes: 16 additions & 14 deletions packages/react/src/slider/thumb/useSliderThumb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as React from 'react';
import { formatNumber } from '../../utils/formatNumber';
import { mergeReactProps } from '../../utils/mergeReactProps';
import { GenericHTMLProps } from '../../utils/types';
import { useEnhancedEffect } from '../../utils/useEnhancedEffect';
import { useForkRef } from '../../utils/useForkRef';
import { useBaseUiId } from '../../utils/useBaseUiId';
import { visuallyHidden } from '../../utils/visuallyHidden';
Expand All @@ -13,6 +12,10 @@ import { useFieldRootContext } from '../../field/root/FieldRootContext';
import { getSliderValue } from '../utils/getSliderValue';
import type { useSliderRoot } from '../root/useSliderRoot';

export interface ThumbMetadata {
inputId: string | undefined;
}

function getNewValue(
thumbValue: number,
step: number,
Expand Down Expand Up @@ -64,7 +67,6 @@ export function useSliderThumb(parameters: useSliderThumb.Parameters): useSlider
name,
orientation,
percentageValues,
registerInputId,
rootRef: externalRef,
step,
tabIndex,
Expand All @@ -79,24 +81,25 @@ export function useSliderThumb(parameters: useSliderThumb.Parameters): useSlider
} = useFieldControlValidation();

const thumbId = useBaseUiId(idParam);

const thumbRef = React.useRef<HTMLElement>(null);
const inputRef = React.useRef<HTMLInputElement>(null);

const mergedInputRef = useForkRef(inputRef, inputValidationRef);

const { ref: listItemRef, index } = useCompositeListItem();

const mergedThumbRef = useForkRef(externalRef, listItemRef, thumbRef);

const inputId = useBaseUiId(inputIdParam);

useEnhancedEffect(() => {
const { deregister } = registerInputId(index, inputId);
const thumbMetadata = React.useMemo(
() => ({
inputId,
}),
[inputId],
);

return () => {
deregister(index);
};
}, [index, inputId, registerInputId]);
const { ref: listItemRef, index } = useCompositeListItem<ThumbMetadata>({
metadata: thumbMetadata,
});

const mergedThumbRef = useForkRef(externalRef, listItemRef, thumbRef);

const thumbValue = sliderValues[index];

Expand Down Expand Up @@ -328,7 +331,6 @@ export namespace useSliderThumb {
| 'name'
| 'orientation'
| 'percentageValues'
| 'registerInputId'
| 'step'
| 'tabIndex'
| 'values'
Expand Down
6 changes: 2 additions & 4 deletions packages/react/src/slider/track/SliderTrack.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const testRootContext: SliderRootContext = {
}),
handleValueChange: NOOP,
largeStep: 10,
inputIdMap: new Map(),
thumbMap: new Map(),
max: 100,
min: 0,
minStepsBetweenValues: 0,
Expand All @@ -38,12 +38,10 @@ const testRootContext: SliderRootContext = {
touched: false,
},
percentageValues: [0],
registerInputId: () => ({
deregister: NOOP,
}),
registerSliderControl: NOOP,
setActive: NOOP,
setDragging: NOOP,
setThumbMap: NOOP,
setValueState: NOOP,
step: 1,
thumbRefs: { current: [] },
Expand Down
6 changes: 2 additions & 4 deletions packages/react/src/slider/value/SliderValue.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const testRootContext: SliderRootContext = {
}),
handleValueChange: NOOP,
largeStep: 10,
inputIdMap: new Map(),
thumbMap: new Map(),
max: 100,
min: 0,
minStepsBetweenValues: 0,
Expand All @@ -40,12 +40,10 @@ const testRootContext: SliderRootContext = {
touched: false,
},
percentageValues: [0],
registerInputId: () => ({
deregister: NOOP,
}),
registerSliderControl: NOOP,
setActive: NOOP,
setDragging: NOOP,
setThumbMap: NOOP,
setValueState: NOOP,
step: 1,
thumbRefs: { current: [] },
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/slider/value/SliderValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ const SliderValue = React.forwardRef(function SliderValue(
) {
const { render, className, children, ...otherProps } = props;

const { inputIdMap, state, values, format } = useSliderRootContext();
const { thumbMap, state, values, format } = useSliderRootContext();

const { getRootProps, formattedValues } = useSliderValue({
format,
inputIdMap,
thumbMap,
values,
});

Expand Down
15 changes: 6 additions & 9 deletions packages/react/src/slider/value/useSliderValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,17 @@ import { mergeReactProps } from '../../utils/mergeReactProps';
import type { useSliderRoot } from '../root/useSliderRoot';

export function useSliderValue(parameters: useSliderValue.Parameters): useSliderValue.ReturnValue {
const { 'aria-live': ariaLive = 'off', format: formatParam, inputIdMap, values } = parameters;
const { 'aria-live': ariaLive = 'off', format: formatParam, thumbMap, values } = parameters;

const outputFor = React.useMemo(() => {
const size = inputIdMap.size;
let htmlFor = '';
for (let i = 0; i < size; i += 1) {
const inputId = inputIdMap.get(i);
if (!inputId) {
break;
for (const thumbMetadata of thumbMap.values()) {
if (thumbMetadata?.inputId) {
htmlFor += `${thumbMetadata.inputId} `;
}
htmlFor += `${inputId} `;
}
return htmlFor.trim() === '' ? undefined : htmlFor.trim();
}, [inputIdMap]);
}, [thumbMap]);

const formattedValues = React.useMemo(() => {
const arr = [];
Expand Down Expand Up @@ -52,7 +49,7 @@ export function useSliderValue(parameters: useSliderValue.Parameters): useSlider
}

export namespace useSliderValue {
export interface Parameters extends Pick<useSliderRoot.ReturnValue, 'inputIdMap' | 'values'> {
export interface Parameters extends Pick<useSliderRoot.ReturnValue, 'thumbMap' | 'values'> {
'aria-live'?: React.AriaAttributes['aria-live'];
/**
* Options to format the input value.
Expand Down
Loading