"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
      }), 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 [ isChecked, setChecked ] = useState( true );
 *   return (
 *     <RangeControl
 *       __nextHasNoMarginBottom
 *       __next40pxDefaultSize
 *       help="Please select how transparent you would like this."
 *       initialPosition={50}
 *       label="Opacity"
 *       max={100}
 *       min={0}
 *       onChange={() => {}}
 *     />
 *   );
 * };
 * ```
 */
const RangeControl = exports.RangeControl = (0, _element.forwardRef)(UnforwardedRangeControl);
var _default = exports.default = RangeControl;
//# sourceMappingURL=index.js.map