UNPKG

@wordpress/components

Version:
343 lines (334 loc) 12.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.RangeControl = void 0; var _clsx = _interopRequireDefault(require("clsx")); var _i18n = require("@wordpress/i18n"); var _element = require("@wordpress/element"); var _compose = require("@wordpress/compose"); var _baseControl = _interopRequireDefault(require("../base-control")); var _button = _interopRequireDefault(require("../button")); var _icon = _interopRequireDefault(require("../icon")); var _utils = require("../utils"); var _utils2 = require("./utils"); var _math = require("../utils/math"); var _inputRange = _interopRequireDefault(require("./input-range")); var _rail = _interopRequireDefault(require("./rail")); var _tooltip = _interopRequireDefault(require("./tooltip")); var _rangeControlStyles = require("./styles/range-control-styles"); var _space = require("../utils/space"); var _deprecated36pxSize = require("../utils/deprecated-36px-size"); var _jsxRuntime = require("react/jsx-runtime"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ const noop = () => {}; /** * Computes the value that `RangeControl` should reset to when pressing * the reset button. */ function computeResetValue({ resetFallbackValue, initialPosition }) { if (resetFallbackValue !== undefined) { return !Number.isNaN(resetFallbackValue) ? resetFallbackValue : null; } if (initialPosition !== undefined) { return !Number.isNaN(initialPosition) ? initialPosition : null; } return null; } function UnforwardedRangeControl(props, forwardedRef) { const { __nextHasNoMarginBottom = false, afterIcon, allowReset = false, beforeIcon, className, color: colorProp = _utils.COLORS.theme.accent, currentInput, disabled = false, help, hideLabelFromVision = false, initialPosition, isShiftStepEnabled = true, label, marks = false, max = 100, min = 0, onBlur = noop, onChange = noop, onFocus = noop, onMouseLeave = noop, onMouseMove = noop, railColor, renderTooltipContent = v => v, resetFallbackValue, __next40pxDefaultSize = false, shiftStep = 10, showTooltip: showTooltipProp, step = 1, trackColor, value: valueProp, withInputField = true, __shouldNotWarnDeprecated36pxSize, ...otherProps } = props; const [value, setValue] = (0, _utils2.useControlledRangeValue)({ min, max, value: valueProp !== null && valueProp !== void 0 ? valueProp : null, initial: initialPosition }); const isResetPendent = (0, _element.useRef)(false); let hasTooltip = showTooltipProp; let hasInputField = withInputField; if (step === 'any') { // The tooltip and number input field are hidden when the step is "any" // because the decimals get too lengthy to fit well. hasTooltip = false; hasInputField = false; } const [showTooltip, setShowTooltip] = (0, _element.useState)(hasTooltip); const [isFocused, setIsFocused] = (0, _element.useState)(false); const inputRef = (0, _element.useRef)(); const isCurrentlyFocused = inputRef.current?.matches(':focus'); const isThumbFocused = !disabled && isFocused; const isValueReset = value === null; const currentValue = value !== undefined ? value : currentInput; const inputSliderValue = isValueReset ? '' : currentValue; const rangeFillValue = isValueReset ? (max - min) / 2 + min : value; const fillValue = isValueReset ? 50 : (value - min) / (max - min) * 100; const fillValueOffset = `${(0, _math.clamp)(fillValue, 0, 100)}%`; const classes = (0, _clsx.default)('components-range-control', className); const wrapperClasses = (0, _clsx.default)('components-range-control__wrapper', !!marks && 'is-marked'); const id = (0, _compose.useInstanceId)(UnforwardedRangeControl, 'inspector-range-control'); const describedBy = !!help ? `${id}__help` : undefined; const enableTooltip = hasTooltip !== false && Number.isFinite(value); const handleOnRangeChange = event => { const nextValue = parseFloat(event.target.value); setValue(nextValue); onChange(nextValue); }; const handleOnChange = next => { // @ts-expect-error TODO: Investigate if it's problematic for setValue() to // potentially receive a NaN when next is undefined. let nextValue = parseFloat(next); setValue(nextValue); /* * Calls onChange only when nextValue is numeric * otherwise may queue a reset for the blur event. */ if (!isNaN(nextValue)) { if (nextValue < min || nextValue > max) { nextValue = (0, _utils2.floatClamp)(nextValue, min, max); } onChange(nextValue); isResetPendent.current = false; } else if (allowReset) { isResetPendent.current = true; } }; const handleOnInputNumberBlur = () => { if (isResetPendent.current) { handleOnReset(); isResetPendent.current = false; } }; const handleOnReset = () => { // Reset to `resetFallbackValue` if defined, otherwise set internal value // to `null` — which, if propagated to the `value` prop, will cause // the value to be reset to the `initialPosition` prop if defined. const resetValue = Number.isNaN(resetFallbackValue) ? null : resetFallbackValue !== null && resetFallbackValue !== void 0 ? resetFallbackValue : null; setValue(resetValue); /** * Previously, this callback would always receive undefined as * an argument. This behavior is unexpected, specifically * when resetFallbackValue is defined. * * The value of undefined is not ideal. Passing it through * to internal <input /> elements would change it from a * controlled component to an uncontrolled component. * * For now, to minimize unexpected regressions, we're going to * preserve the undefined callback argument, except when a * resetFallbackValue is defined. */ onChange(resetValue !== null && resetValue !== void 0 ? resetValue : undefined); }; const handleShowTooltip = () => setShowTooltip(true); const handleHideTooltip = () => setShowTooltip(false); const handleOnBlur = event => { onBlur(event); setIsFocused(false); handleHideTooltip(); }; const handleOnFocus = event => { onFocus(event); setIsFocused(true); handleShowTooltip(); }; const offsetStyle = { [(0, _i18n.isRTL)() ? 'right' : 'left']: fillValueOffset }; // Add default size deprecation warning. (0, _deprecated36pxSize.maybeWarnDeprecated36pxSize)({ componentName: 'RangeControl', __next40pxDefaultSize, size: undefined, __shouldNotWarnDeprecated36pxSize }); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_baseControl.default, { __nextHasNoMarginBottom: __nextHasNoMarginBottom, __associatedWPComponentName: "RangeControl", className: classes, label: label, hideLabelFromVision: hideLabelFromVision, id: `${id}`, help: help, children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_rangeControlStyles.Root, { className: "components-range-control__root", __next40pxDefaultSize: __next40pxDefaultSize, children: [beforeIcon && /*#__PURE__*/(0, _jsxRuntime.jsx)(_rangeControlStyles.BeforeIconWrapper, { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icon.default, { icon: beforeIcon }) }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_rangeControlStyles.Wrapper, { __nextHasNoMarginBottom: __nextHasNoMarginBottom, className: wrapperClasses, color: colorProp, marks: !!marks, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_inputRange.default, { ...otherProps, className: "components-range-control__slider", describedBy: describedBy, disabled: disabled, id: `${id}`, label: label, max: max, min: min, onBlur: handleOnBlur, onChange: handleOnRangeChange, onFocus: handleOnFocus, onMouseMove: onMouseMove, onMouseLeave: onMouseLeave, ref: (0, _compose.useMergeRefs)([inputRef, forwardedRef]), step: step, value: inputSliderValue !== null && inputSliderValue !== void 0 ? inputSliderValue : undefined }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_rail.default, { "aria-hidden": true, disabled: disabled, marks: marks, max: max, min: min, railColor: railColor, step: step, value: rangeFillValue }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_rangeControlStyles.Track, { "aria-hidden": true, className: "components-range-control__track", disabled: disabled, style: { width: fillValueOffset }, trackColor: trackColor }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_rangeControlStyles.ThumbWrapper, { className: "components-range-control__thumb-wrapper", style: offsetStyle, disabled: disabled, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_rangeControlStyles.Thumb, { "aria-hidden": true, isFocused: isThumbFocused, disabled: disabled }) }), enableTooltip && /*#__PURE__*/(0, _jsxRuntime.jsx)(_tooltip.default, { className: "components-range-control__tooltip", inputRef: inputRef, tooltipPosition: "bottom", renderTooltipContent: renderTooltipContent, show: isCurrentlyFocused || showTooltip, style: offsetStyle, value: value })] }), afterIcon && /*#__PURE__*/(0, _jsxRuntime.jsx)(_rangeControlStyles.AfterIconWrapper, { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icon.default, { icon: afterIcon }) }), hasInputField && /*#__PURE__*/(0, _jsxRuntime.jsx)(_rangeControlStyles.InputNumber, { "aria-label": label, className: "components-range-control__number", disabled: disabled, inputMode: "decimal", isShiftStepEnabled: isShiftStepEnabled, max: max, min: min, onBlur: handleOnInputNumberBlur, onChange: handleOnChange, shiftStep: shiftStep, size: __next40pxDefaultSize ? '__unstable-large' : 'default', __unstableInputWidth: __next40pxDefaultSize ? (0, _space.space)(20) : (0, _space.space)(16), step: step // @ts-expect-error TODO: Investigate if the `null` value is necessary , value: inputSliderValue, __shouldNotWarnDeprecated36pxSize: true }), allowReset && /*#__PURE__*/(0, _jsxRuntime.jsx)(_rangeControlStyles.ActionRightWrapper, { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.default, { className: "components-range-control__reset" // If the RangeControl itself is disabled, the reset button shouldn't be in the tab sequence. , accessibleWhenDisabled: !disabled // The reset button should be disabled if RangeControl itself is disabled, // or if the current `value` is equal to the value that would be currently // assigned when clicking the button. , disabled: disabled || value === computeResetValue({ resetFallbackValue, initialPosition }), variant: "secondary", size: "small", onClick: handleOnReset, children: (0, _i18n.__)('Reset') }) })] }) }); } /** * RangeControls are used to make selections from a range of incremental values. * * ```jsx * import { RangeControl } from '@wordpress/components'; * import { useState } from '@wordpress/element'; * * const MyRangeControl = () => { * const [ value, setValue ] = useState(); * return ( * <RangeControl * __nextHasNoMarginBottom * __next40pxDefaultSize * help="Please select how transparent you would like this." * initialPosition={ 50 } * label="Opacity" * max={ 100 } * min={ 0 } * value={ value } * onChange={ setValue } * /> * ); * }; * ``` */ const RangeControl = exports.RangeControl = (0, _element.forwardRef)(UnforwardedRangeControl); var _default = exports.default = RangeControl; //# sourceMappingURL=index.js.map