UNPKG

@base-ui-components/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

223 lines (221 loc) 8.93 kB
"use strict"; 'use client'; Object.defineProperty(exports, "__esModule", { value: true }); exports.useSliderThumb = useSliderThumb; var React = _interopRequireWildcard(require("react")); var _formatNumber = require("../../utils/formatNumber"); var _mergeReactProps = require("../../utils/mergeReactProps"); var _useEnhancedEffect = require("../../utils/useEnhancedEffect"); var _useForkRef = require("../../utils/useForkRef"); var _useBaseUiId = require("../../utils/useBaseUiId"); var _visuallyHidden = require("../../utils/visuallyHidden"); var _useCompositeListItem = require("../../composite/list/useCompositeListItem"); var _useFieldControlValidation = require("../../field/control/useFieldControlValidation"); var _FieldRootContext = require("../../field/root/FieldRootContext"); var _getSliderValue = require("../utils/getSliderValue"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function getNewValue(thumbValue, step, direction, min, max) { return direction === 1 ? Math.min(thumbValue + step, max) : Math.max(thumbValue - step, min); } function getDefaultAriaValueText(values, index, format) { if (index < 0) { return undefined; } if (values.length === 2) { if (index === 0) { return `${(0, _formatNumber.formatNumber)(values[index], [], format)} start range`; } return `${(0, _formatNumber.formatNumber)(values[index], [], format)} end range`; } return format ? (0, _formatNumber.formatNumber)(values[index], [], format) : undefined; } function useSliderThumb(parameters) { const { active: activeIndex, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, 'aria-valuetext': ariaValuetext, changeValue, direction, disabled, format, getAriaLabel, getAriaValueText, id: idParam, inputId: inputIdParam, largeStep, max, min, minStepsBetweenValues, name, orientation, percentageValues, registerInputId, rootRef: externalRef, step, tabIndex, values: sliderValues } = parameters; const { setTouched } = (0, _FieldRootContext.useFieldRootContext)(); const { getInputValidationProps, inputRef: inputValidationRef, commitValidation } = (0, _useFieldControlValidation.useFieldControlValidation)(); const thumbId = (0, _useBaseUiId.useBaseUiId)(idParam); const thumbRef = React.useRef(null); const inputRef = React.useRef(null); const mergedInputRef = (0, _useForkRef.useForkRef)(inputRef, inputValidationRef); const { ref: listItemRef, index } = (0, _useCompositeListItem.useCompositeListItem)(); const mergedThumbRef = (0, _useForkRef.useForkRef)(externalRef, listItemRef, thumbRef); const inputId = (0, _useBaseUiId.useBaseUiId)(inputIdParam); (0, _useEnhancedEffect.useEnhancedEffect)(() => { const { deregister } = registerInputId(index, inputId); return () => { deregister(index); }; }, [index, inputId, registerInputId]); const thumbValue = sliderValues[index]; // for SSR, don't wait for the index if there's only one thumb const percent = percentageValues.length === 1 ? percentageValues[0] : percentageValues[index]; const isRtl = direction === 'rtl'; const getThumbStyle = React.useCallback(() => { const isVertical = orientation === 'vertical'; if (!Number.isFinite(percent)) { return _visuallyHidden.visuallyHidden; } return { position: 'absolute', [{ horizontal: 'insetInlineStart', vertical: 'bottom' }[orientation]]: `${percent}%`, [isVertical ? 'left' : 'top']: '50%', transform: `translate(${(isVertical || !isRtl ? -1 : 1) * 50}%, ${(isVertical ? 1 : -1) * 50}%)`, // So the non active thumb doesn't show its label on hover. pointerEvents: activeIndex !== -1 && activeIndex !== index ? 'none' : undefined, zIndex: activeIndex === index ? 1 : undefined }; }, [activeIndex, isRtl, orientation, percent, index]); const getRootProps = React.useCallback((externalProps = {}) => { return (0, _mergeReactProps.mergeReactProps)(externalProps, { 'data-index': index, id: thumbId, onBlur() { if (!thumbRef.current) { return; } setTouched(true); commitValidation((0, _getSliderValue.getSliderValue)({ valueInput: thumbValue, min, max, index, range: sliderValues.length > 1, values: sliderValues })); }, onKeyDown(event) { let newValue = null; const isRange = sliderValues.length > 1; switch (event.key) { case 'ArrowUp': newValue = getNewValue(thumbValue, event.shiftKey ? largeStep : step, 1, min, max); break; case 'ArrowRight': newValue = getNewValue(thumbValue, event.shiftKey ? largeStep : step, isRtl ? -1 : 1, min, max); break; case 'ArrowDown': newValue = getNewValue(thumbValue, event.shiftKey ? largeStep : step, -1, min, max); break; case 'ArrowLeft': newValue = getNewValue(thumbValue, event.shiftKey ? largeStep : step, isRtl ? 1 : -1, min, max); break; case 'PageUp': newValue = getNewValue(thumbValue, largeStep, 1, min, max); break; case 'PageDown': newValue = getNewValue(thumbValue, largeStep, -1, min, max); break; case 'End': newValue = max; if (isRange) { newValue = Number.isFinite(sliderValues[index + 1]) ? sliderValues[index + 1] - step * minStepsBetweenValues : max; } break; case 'Home': newValue = min; if (isRange) { newValue = Number.isFinite(sliderValues[index - 1]) ? sliderValues[index - 1] + step * minStepsBetweenValues : min; } break; default: break; } if (newValue !== null) { changeValue(newValue, index, event); event.preventDefault(); } }, ref: mergedThumbRef, style: { ...getThumbStyle() }, tabIndex: tabIndex ?? (disabled ? undefined : 0) }); }, [changeValue, commitValidation, disabled, getThumbStyle, index, isRtl, largeStep, max, mergedThumbRef, min, minStepsBetweenValues, setTouched, sliderValues, step, tabIndex, thumbId, thumbValue]); const getThumbInputProps = React.useCallback((externalProps = {}) => { let cssWritingMode; if (orientation === 'vertical') { cssWritingMode = isRtl ? 'vertical-rl' : 'vertical-lr'; } return (0, _mergeReactProps.mergeReactProps)(getInputValidationProps(externalProps), { 'aria-label': getAriaLabel ? getAriaLabel(index) : ariaLabel, 'aria-labelledby': ariaLabelledby, 'aria-orientation': orientation, 'aria-valuemax': max, 'aria-valuemin': min, 'aria-valuenow': thumbValue, 'aria-valuetext': getAriaValueText ? getAriaValueText((0, _formatNumber.formatNumber)(thumbValue, [], format), thumbValue, index) : ariaValuetext ?? getDefaultAriaValueText(sliderValues, index, format), 'data-index': index, disabled, id: inputId, max, min, name, onChange(event) { // @ts-ignore changeValue(event.target.valueAsNumber, index, event); }, ref: mergedInputRef, step, style: { ..._visuallyHidden.visuallyHidden, direction: isRtl ? 'rtl' : 'ltr', // So that VoiceOver's focus indicator matches the thumb's dimensions width: '100%', height: '100%', writingMode: cssWritingMode }, tabIndex: -1, type: 'range', value: thumbValue ?? '' }); }, [ariaLabel, ariaLabelledby, ariaValuetext, changeValue, disabled, format, getAriaLabel, getAriaValueText, getInputValidationProps, index, isRtl, max, mergedInputRef, min, name, orientation, sliderValues, step, inputId, thumbValue]); return React.useMemo(() => ({ getRootProps, getThumbInputProps, disabled, index }), [getRootProps, getThumbInputProps, disabled, index]); }