{"mappings":"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","sources":["packages/react-stately/src/numberfield/useNumberFieldState.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {clamp, snapValueToStep} from '../utils/number';\n\nimport {FocusableProps, HelpTextProps, InputBase, LabelableProps, RangeInputBase, TextInputBase, Validation, ValueBase} from '@react-types/shared';\nimport {FormValidationState, useFormValidationState} from '../form/useFormValidationState';\nimport {NumberFormatter, NumberParser} from '@internationalized/number';\nimport {useCallback, useMemo, useState} from 'react';\nimport {useControlledState} from '../utils/useControlledState';\n\nexport interface NumberFieldProps extends InputBase, Validation<number>, FocusableProps, TextInputBase, ValueBase<number>, RangeInputBase<number>, LabelableProps, HelpTextProps {\n  /**\n   * Formatting options for the value displayed in the number field.\n   * This also affects what characters are allowed to be typed by the user.\n   */\n  formatOptions?: Intl.NumberFormatOptions,\n  /**\n   * Controls the behavior of the number field when the user blurs the field after editing.\n   * 'snap' will clamp the value to the min/max values, and snap to the nearest step value.\n   * 'validate' will not clamp the value, and will validate that the value is within the min/max range and on a valid step.\n   * @default 'snap'\n   */\n  commitBehavior?: 'snap' | 'validate'\n}\n\nexport interface NumberFieldState extends FormValidationState {\n  /**\n   * The current text value of the input. Updated as the user types,\n   * and formatted according to `formatOptions` on blur.\n   */\n  inputValue: string,\n  /**\n   * The currently parsed number value, or NaN if a valid number could not be parsed.\n   * Updated based on the `inputValue` as the user types.\n   */\n  numberValue: number,\n  /** The default value of the input. */\n  defaultNumberValue: number,\n  /** The minimum value of the number field. */\n  minValue?: number,\n  /** The maximum value of the number field. */\n  maxValue?: number,\n  /** Whether the current value can be incremented according to the maximum value and step. */\n  canIncrement: boolean,\n  /** Whether the current value can be decremented according to the minimum value and step. */\n  canDecrement: boolean,\n  /**\n   * Validates a user input string according to the current locale and format options.\n   * Values can be partially entered, and may be valid even if they cannot currently be parsed to a number.\n   * Can be used to implement validation as a user types.\n   */\n  validate(value: string): boolean,\n  /** Sets the current text value of the input. */\n  setInputValue(val: string): void,\n  /** Sets the number value. */\n  setNumberValue(val: number): void,\n  /**\n   * Commits the current input value. The value is parsed to a number, clamped according\n   * to the minimum and maximum values of the field, and snapped to the nearest step value.\n   * This will fire the `onChange` prop with the new value, and if uncontrolled, update the `numberValue`.\n   * Typically this is called when the field is blurred.\n   * @param value - The value to commit. If not provided, the current input value is used.\n   */\n  commit(value?: string): void,\n  /** Increments the current input value to the next step boundary, and fires `onChange`. */\n  increment(): void,\n  /** Decrements the current input value to the next step boundary, and fires `onChange`. */\n  decrement(): void,\n  /** Sets the current value to the `maxValue` if any, and fires `onChange`. */\n  incrementToMax(): void,\n  /** Sets the current value to the `minValue` if any, and fires `onChange`. */\n  decrementToMin(): void\n}\n\nexport interface NumberFieldStateOptions extends NumberFieldProps {\n  /**\n   * The locale that should be used for parsing.\n   * @default 'en-US'\n   */\n  locale: string\n}\n\n/**\n * Provides state management for a number field component. Number fields allow users to enter a number,\n * and increment or decrement the value using stepper buttons.\n */\nexport function useNumberFieldState(\n  props: NumberFieldStateOptions\n): NumberFieldState {\n  let {\n    minValue,\n    maxValue,\n    step,\n    formatOptions,\n    value,\n    defaultValue = NaN,\n    onChange,\n    locale,\n    isDisabled,\n    isReadOnly,\n    commitBehavior = 'snap'\n  } = props;\n\n  if (value === null) {\n    value = NaN;\n  }\n\n  let snapValue = useCallback(value => {\n    return step === undefined || isNaN(step)\n      ? clamp(value, minValue, maxValue)\n      : snapValueToStep(value, minValue, maxValue, step);\n  }, [step, minValue, maxValue]);\n\n  if (value !== undefined && !isNaN(value) && commitBehavior === 'snap') {\n    value = snapValue(value);\n  }\n\n  if (!isNaN(defaultValue) && commitBehavior === 'snap') {\n    defaultValue = snapValue(defaultValue);\n  }\n\n  let [numberValue, setNumberValue] = useControlledState<number>(value, isNaN(defaultValue) ? NaN : defaultValue, onChange);\n  let [initialValue] = useState(numberValue);\n  let [inputValue, setInputValue] = useState(() => isNaN(numberValue) ? '' : new NumberFormatter(locale, formatOptions).format(numberValue));\n\n  let numberParser = useMemo(() => new NumberParser(locale, formatOptions), [locale, formatOptions]);\n  let numberingSystem = useMemo(() => numberParser.getNumberingSystem(inputValue), [numberParser, inputValue]);\n  let formatter = useMemo(() => new NumberFormatter(locale, {...formatOptions, numberingSystem}), [locale, formatOptions, numberingSystem]);\n  let intlOptions = useMemo(() => formatter.resolvedOptions(), [formatter]);\n  let format = useCallback((value: number) => (isNaN(value) || value === null) ? '' : formatter.format(value), [formatter]);\n\n  let validation = useFormValidationState({\n    ...props,\n    value: numberValue\n  });\n\n  let clampStep = (step !== undefined && !isNaN(step)) ? step : 1;\n  if (intlOptions.style === 'percent' && (step === undefined || isNaN(step))) {\n    clampStep = 0.01;\n  }\n\n  // Update the input value when the number value or format options change. This is done\n  // in a useEffect so that the controlled behavior is correct and we only update the\n  // textfield after prop changes.\n  let [prevValue, setPrevValue] = useState(numberValue);\n  let [prevLocale, setPrevLocale] = useState(locale);\n  let [prevFormatOptions, setPrevFormatOptions] = useState(formatOptions);\n  if (!Object.is(numberValue, prevValue) || locale !== prevLocale || formatOptions !== prevFormatOptions) {\n    setInputValue(format(numberValue));\n    setPrevValue(numberValue);\n    setPrevLocale(locale);\n    setPrevFormatOptions(formatOptions);\n  }\n\n  let parsedValue = useMemo(() => numberParser.parse(inputValue), [numberParser, inputValue]);\n  let commit = (overrideValue?: string) => {\n    let newInputValue = overrideValue === undefined ? inputValue : overrideValue;\n    let newParsedValue = parsedValue;\n    if (overrideValue !== undefined) {\n      newParsedValue = numberParser.parse(newInputValue);\n    }\n    // Set to empty state if input value is empty\n    if (!newInputValue.length) {\n      setNumberValue(NaN);\n      setInputValue(value === undefined ? '' : format(numberValue));\n      return;\n    }\n\n    // if it failed to parse, then reset input to formatted version of current number\n    if (isNaN(newParsedValue)) {\n      setInputValue(format(numberValue));\n      return;\n    }\n\n    // Clamp to min and max, round to the nearest step, and round to specified number of digits\n    let clampedValue = commitBehavior === 'snap' ? snapValue(newParsedValue) : newParsedValue;\n    clampedValue = numberParser.parse(format(clampedValue));\n    let shouldValidate = clampedValue !== numberValue;\n    setNumberValue(clampedValue);\n\n    // in a controlled state, the numberValue won't change, so we won't go back to our old input without help\n    setInputValue(format(value === undefined ? clampedValue : numberValue));\n    if (shouldValidate) {\n      validation.commitValidation();\n    }\n  };\n\n  let safeNextStep = (operation: '+' | '-', minMax: number = 0) => {\n    let prev = parsedValue;\n\n    if (isNaN(prev)) {\n      // if the input is empty, start from the min/max value when incrementing/decrementing,\n      // or zero if there is no min/max value defined.\n      let newValue = isNaN(minMax) ? 0 : minMax;\n      return snapValueToStep(newValue, minValue, maxValue, clampStep);\n    } else {\n      // otherwise, first snap the current value to the nearest step. if it moves in the direction\n      // we're going, use that value, otherwise add the step and snap that value.\n      let newValue = snapValueToStep(prev, minValue, maxValue, clampStep);\n      if ((operation === '+' && newValue > prev) || (operation === '-' && newValue < prev)) {\n        return newValue;\n      }\n\n      return snapValueToStep(\n        handleDecimalOperation(operation, prev, clampStep),\n        minValue,\n        maxValue,\n        clampStep\n      );\n    }\n  };\n\n  let increment = () => {\n    let newValue = safeNextStep('+', minValue);\n\n    // if we've arrived at the same value that was previously in the state, the\n    // input value should be updated to match\n    // ex type 4, press increment, highlight the number in the input, type 4 again, press increment\n    // you'd be at 5, then incrementing to 5 again, so no re-render would happen and 4 would be left in the input\n    if (newValue === numberValue) {\n      setInputValue(format(newValue));\n    }\n\n    setNumberValue(newValue);\n    validation.commitValidation();\n  };\n\n  let decrement = () => {\n    let newValue = safeNextStep('-', maxValue);\n\n    if (newValue === numberValue) {\n      setInputValue(format(newValue));\n    }\n\n    setNumberValue(newValue);\n    validation.commitValidation();\n  };\n\n  let incrementToMax = () => {\n    if (maxValue != null) {\n      setNumberValue(snapValueToStep(maxValue, minValue, maxValue, clampStep));\n      validation.commitValidation();\n    }\n  };\n\n  let decrementToMin = () => {\n    if (minValue != null) {\n      setNumberValue(minValue);\n      validation.commitValidation();\n    }\n  };\n\n  let canIncrement = useMemo(() => (\n    !isDisabled &&\n    !isReadOnly &&\n    (\n      isNaN(parsedValue) ||\n      (maxValue === undefined || isNaN(maxValue)) ||\n      snapValueToStep(parsedValue, minValue, maxValue, clampStep) > parsedValue ||\n      handleDecimalOperation('+', parsedValue, clampStep) <= maxValue\n    )\n  ), [isDisabled, isReadOnly, minValue, maxValue, clampStep, parsedValue]);\n\n  let canDecrement = useMemo(() => (\n    !isDisabled &&\n    !isReadOnly &&\n    (\n      isNaN(parsedValue) ||\n      (minValue === undefined || isNaN(minValue)) ||\n      snapValueToStep(parsedValue, minValue, maxValue, clampStep) < parsedValue ||\n      handleDecimalOperation('-', parsedValue, clampStep) >= minValue\n    )\n  ), [isDisabled, isReadOnly, minValue, maxValue, clampStep, parsedValue]);\n\n  let validate = (value: string) => numberParser.isValidPartialNumber(value, minValue, maxValue);\n\n  return {\n    ...validation,\n    validate,\n    increment,\n    incrementToMax,\n    decrement,\n    decrementToMin,\n    canIncrement,\n    canDecrement,\n    minValue,\n    maxValue,\n    numberValue: parsedValue,\n    defaultNumberValue: isNaN(defaultValue) ? initialValue : defaultValue,\n    setNumberValue,\n    setInputValue,\n    inputValue,\n    commit\n  };\n}\n\nfunction handleDecimalOperation(operator: '-' | '+', value1: number, value2: number): number {\n  let result = operator === '+' ? value1 + value2 : value1 - value2;\n\n  // Check if we have decimals\n  if (value1 % 1 !== 0 || value2 % 1 !== 0) {\n    const value1Decimal = value1.toString().split('.');\n    const value2Decimal = value2.toString().split('.');\n    const value1DecimalLength = (value1Decimal[1] && value1Decimal[1].length) || 0;\n    const value2DecimalLength = (value2Decimal[1] && value2Decimal[1].length) || 0;\n    const multiplier = Math.pow(10, Math.max(value1DecimalLength, value2DecimalLength));\n\n    // Transform the decimals to integers based on the precision\n    value1 = Math.round(value1 * multiplier);\n    value2 = Math.round(value2 * multiplier);\n\n    // Perform the operation on integers values to make sure we don't get a fancy decimal value\n    result = operator === '+' ? value1 + value2 : value1 - value2;\n\n    // Transform the integer result back to decimal\n    result /= multiplier;\n  }\n\n  return result;\n}\n"],"names":[],"version":3,"file":"useNumberFieldState.cjs.map"}