{"version":3,"sources":["../src/use-counter.ts"],"sourcesContent":["import { useCallbackRef } from \"@chakra-ui/react-use-callback-ref\"\nimport {\n  clampValue,\n  countDecimalPlaces,\n  toPrecision,\n} from \"@chakra-ui/number-utils\"\nimport { useCallback, useState } from \"react\"\n\nexport interface UseCounterProps {\n  /**\n   * The callback fired when the value changes\n   */\n  onChange?(valueAsString: string, valueAsNumber: number): void\n  /**\n   * The number of decimal points used to round the value\n   */\n  precision?: number\n  /**\n   * The initial value of the counter. Should be less than `max` and greater than `min`\n   */\n  defaultValue?: string | number\n  /**\n   * The value of the counter. Should be less than `max` and greater than `min`\n   */\n  value?: string | number\n  /**\n   * The step used to increment or decrement the value\n   * @default 1\n   */\n  step?: number\n  /**\n   * The minimum value of the counter\n   * @default Number.MIN_SAFE_INTEGER\n   */\n  min?: number\n  /**\n   * The maximum value of the counter\n   * @default Number.MAX_SAFE_INTEGER\n   */\n  max?: number\n  /**\n   * This controls the value update behavior in general.\n   *\n   * - If `true` and you use the stepper or up/down arrow keys,\n   *  the value will not exceed the `max` or go lower than `min`\n   *\n   * - If `false`, the value will be allowed to go out of range.\n   *\n   * @default true\n   */\n  keepWithinRange?: boolean\n}\n\nexport function useCounter(props: UseCounterProps = {}) {\n  const {\n    onChange,\n    precision: precisionProp,\n    defaultValue,\n    value: valueProp,\n    step: stepProp = 1,\n    min = Number.MIN_SAFE_INTEGER,\n    max = Number.MAX_SAFE_INTEGER,\n    keepWithinRange = true,\n  } = props\n\n  const onChangeProp = useCallbackRef(onChange)\n\n  const [valueState, setValue] = useState<string | number>(() => {\n    if (defaultValue == null) return \"\"\n    return cast(defaultValue, stepProp, precisionProp) ?? \"\"\n  })\n\n  /**\n   * Because the component that consumes this hook can be controlled or uncontrolled\n   * we'll keep track of that\n   */\n  const isControlled = typeof valueProp !== \"undefined\"\n  const value = isControlled ? valueProp : valueState\n\n  const decimalPlaces = getDecimalPlaces(parse(value), stepProp)\n\n  const precision = precisionProp ?? decimalPlaces\n\n  const update = useCallback(\n    (next: string | number) => {\n      if (next === value) return\n      if (!isControlled) {\n        setValue(next.toString())\n      }\n      onChangeProp?.(next.toString(), parse(next))\n    },\n    [onChangeProp, isControlled, value],\n  )\n\n  // Function to clamp the value and round it to the precision\n  const clamp = useCallback(\n    (value: number) => {\n      let nextValue = value\n\n      if (keepWithinRange) {\n        nextValue = clampValue(nextValue, min, max)\n      }\n\n      return toPrecision(nextValue, precision)\n    },\n    [precision, keepWithinRange, max, min],\n  )\n\n  const increment = useCallback(\n    (step = stepProp) => {\n      let next: string | number\n\n      /**\n       * Let's follow the native browser behavior for\n       * scenarios where the input starts empty (\"\")\n       */\n      if (value === \"\") {\n        /**\n         * If `min` is set, native input, starts at the `min`.\n         * Else, it starts at `step`\n         */\n        next = parse(step)\n      } else {\n        next = parse(value) + step\n      }\n\n      next = clamp(next as number)\n      update(next)\n    },\n    [clamp, stepProp, update, value],\n  )\n\n  const decrement = useCallback(\n    (step = stepProp) => {\n      let next: string | number\n\n      // Same thing here. We'll follow native implementation\n      if (value === \"\") {\n        next = parse(-step)\n      } else {\n        next = parse(value) - step\n      }\n\n      next = clamp(next as number)\n      update(next)\n    },\n    [clamp, stepProp, update, value],\n  )\n\n  const reset = useCallback(() => {\n    let next: string | number\n    if (defaultValue == null) {\n      next = \"\"\n    } else {\n      next = cast(defaultValue, stepProp, precisionProp) ?? min\n    }\n    update(next)\n  }, [defaultValue, precisionProp, stepProp, update, min])\n\n  const castValue = useCallback(\n    (value: string | number) => {\n      const nextValue = cast(value, stepProp, precision) ?? min\n      update(nextValue)\n    },\n    [precision, stepProp, update, min],\n  )\n\n  const valueAsNumber = parse(value)\n\n  /**\n   * Common range checks\n   */\n  const isOutOfRange = valueAsNumber > max || valueAsNumber < min\n  const isAtMax = valueAsNumber === max\n  const isAtMin = valueAsNumber === min\n\n  return {\n    isOutOfRange,\n    isAtMax,\n    isAtMin,\n    precision,\n    value,\n    valueAsNumber,\n    update,\n    reset,\n    increment,\n    decrement,\n    clamp,\n    cast: castValue,\n    setValue,\n  }\n}\n\nexport type UseCounterReturn = ReturnType<typeof useCounter>\n\nfunction parse(value: string | number) {\n  return parseFloat(value.toString().replace(/[^\\w.-]+/g, \"\"))\n}\n\nfunction getDecimalPlaces(value: number, step: number) {\n  return Math.max(countDecimalPlaces(step), countDecimalPlaces(value))\n}\n\nfunction cast(value: string | number, step: number, precision?: number) {\n  const parsedValue = parse(value)\n  if (Number.isNaN(parsedValue)) return undefined\n  const decimalPlaces = getDecimalPlaces(parsedValue, step)\n  return toPrecision(parsedValue, precision ?? decimalPlaces)\n}\n"],"mappings":"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","names":["value"]}