Skip to content

Commit

Permalink
Use composite metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
mj12albert committed Dec 13, 2024
1 parent d477add commit 381870b
Show file tree
Hide file tree
Showing 10 changed files with 43 additions and 82 deletions.
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
6 changes: 2 additions & 4 deletions packages/react/src/slider/indicator/SliderIndicator.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
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
27 changes: 13 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,22 @@ 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 = {
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 +328,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

0 comments on commit 381870b

Please sign in to comment.