@react-stately/numberfield 3.4.2 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/import.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {useControlledState as $vhjCi$useControlledState, clamp as $vhjCi$clamp, snapValueToStep as $vhjCi$snapValueToStep} from "@react-stately/utils";
2
2
  import {NumberFormatter as $vhjCi$NumberFormatter, NumberParser as $vhjCi$NumberParser} from "@internationalized/number";
3
- import {useState as $vhjCi$useState, useMemo as $vhjCi$useMemo, useCallback as $vhjCi$useCallback, useRef as $vhjCi$useRef} from "react";
3
+ import {useState as $vhjCi$useState, useMemo as $vhjCi$useMemo, useCallback as $vhjCi$useCallback} from "react";
4
4
 
5
5
  /*
6
6
  * Copyright 2020 Adobe. All rights reserved.
@@ -56,22 +56,19 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
56
56
  // Update the input value when the number value or format options change. This is done
57
57
  // in a useEffect so that the controlled behavior is correct and we only update the
58
58
  // textfield after prop changes.
59
- let prevValue = (0, $vhjCi$useRef)(numberValue);
60
- let prevLocale = (0, $vhjCi$useRef)(locale);
61
- let prevFormatOptions = (0, $vhjCi$useRef)(formatOptions);
62
- if (!Object.is(numberValue, prevValue.current) || locale !== prevLocale.current || formatOptions !== prevFormatOptions.current) {
59
+ let [prevValue, setPrevValue] = (0, $vhjCi$useState)(numberValue);
60
+ let [prevLocale, setPrevLocale] = (0, $vhjCi$useState)(locale);
61
+ let [prevFormatOptions, setPrevFormatOptions] = (0, $vhjCi$useState)(formatOptions);
62
+ if (!Object.is(numberValue, prevValue) || locale !== prevLocale || formatOptions !== prevFormatOptions) {
63
63
  setInputValue(format(numberValue));
64
- prevValue.current = numberValue;
65
- prevLocale.current = locale;
66
- prevFormatOptions.current = formatOptions;
64
+ setPrevValue(numberValue);
65
+ setPrevLocale(locale);
66
+ setPrevFormatOptions(formatOptions);
67
67
  }
68
- // Store last parsed value in a ref so it can be used by increment/decrement below
69
68
  let parsedValue = (0, $vhjCi$useMemo)(()=>numberParser.parse(inputValue), [
70
69
  numberParser,
71
70
  inputValue
72
71
  ]);
73
- let parsed = (0, $vhjCi$useRef)(0);
74
- parsed.current = parsedValue;
75
72
  let commit = ()=>{
76
73
  // Set to empty state if input value is empty
77
74
  if (!inputValue.length) {
@@ -80,21 +77,21 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
80
77
  return;
81
78
  }
82
79
  // if it failed to parse, then reset input to formatted version of current number
83
- if (isNaN(parsed.current)) {
80
+ if (isNaN(parsedValue)) {
84
81
  setInputValue(format(numberValue));
85
82
  return;
86
83
  }
87
84
  // Clamp to min and max, round to the nearest step, and round to specified number of digits
88
85
  let clampedValue;
89
- if (isNaN(step)) clampedValue = (0, $vhjCi$clamp)(parsed.current, minValue, maxValue);
90
- else clampedValue = (0, $vhjCi$snapValueToStep)(parsed.current, minValue, maxValue, step);
86
+ if (isNaN(step)) clampedValue = (0, $vhjCi$clamp)(parsedValue, minValue, maxValue);
87
+ else clampedValue = (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, step);
91
88
  clampedValue = numberParser.parse(format(clampedValue));
92
89
  setNumberValue(clampedValue);
93
90
  // in a controlled state, the numberValue won't change, so we won't go back to our old input without help
94
91
  setInputValue(format(value === undefined ? clampedValue : numberValue));
95
92
  };
96
93
  let safeNextStep = (operation, minMax)=>{
97
- let prev = parsed.current;
94
+ let prev = parsedValue;
98
95
  if (isNaN(prev)) {
99
96
  // if the input is empty, start from the min/max value when incrementing/decrementing,
100
97
  // or zero if there is no min/max value defined.
@@ -103,8 +100,8 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
103
100
  } else {
104
101
  // otherwise, first snap the current value to the nearest step. if it moves in the direction
105
102
  // we're going, use that value, otherwise add the step and snap that value.
106
- let newValue1 = (0, $vhjCi$snapValueToStep)(prev, minValue, maxValue, clampStep);
107
- if (operation === "+" && newValue1 > prev || operation === "-" && newValue1 < prev) return newValue1;
103
+ let newValue = (0, $vhjCi$snapValueToStep)(prev, minValue, maxValue, clampStep);
104
+ if (operation === "+" && newValue > prev || operation === "-" && newValue < prev) return newValue;
108
105
  return (0, $vhjCi$snapValueToStep)($de67e98908f0c6ee$var$handleDecimalOperation(operation, prev, clampStep), minValue, maxValue, clampStep);
109
106
  }
110
107
  };
@@ -156,6 +153,7 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
156
153
  minValue: minValue,
157
154
  maxValue: maxValue,
158
155
  numberValue: parsedValue,
156
+ setNumberValue: setNumberValue,
159
157
  setInputValue: setInputValue,
160
158
  inputValue: inputValue,
161
159
  commit: commit
package/dist/main.js CHANGED
@@ -61,22 +61,19 @@ function $e18a693eeaf9c060$export$7f629e9dc1ecf37c(props) {
61
61
  // Update the input value when the number value or format options change. This is done
62
62
  // in a useEffect so that the controlled behavior is correct and we only update the
63
63
  // textfield after prop changes.
64
- let prevValue = (0, $cmJn2$react.useRef)(numberValue);
65
- let prevLocale = (0, $cmJn2$react.useRef)(locale);
66
- let prevFormatOptions = (0, $cmJn2$react.useRef)(formatOptions);
67
- if (!Object.is(numberValue, prevValue.current) || locale !== prevLocale.current || formatOptions !== prevFormatOptions.current) {
64
+ let [prevValue, setPrevValue] = (0, $cmJn2$react.useState)(numberValue);
65
+ let [prevLocale, setPrevLocale] = (0, $cmJn2$react.useState)(locale);
66
+ let [prevFormatOptions, setPrevFormatOptions] = (0, $cmJn2$react.useState)(formatOptions);
67
+ if (!Object.is(numberValue, prevValue) || locale !== prevLocale || formatOptions !== prevFormatOptions) {
68
68
  setInputValue(format(numberValue));
69
- prevValue.current = numberValue;
70
- prevLocale.current = locale;
71
- prevFormatOptions.current = formatOptions;
69
+ setPrevValue(numberValue);
70
+ setPrevLocale(locale);
71
+ setPrevFormatOptions(formatOptions);
72
72
  }
73
- // Store last parsed value in a ref so it can be used by increment/decrement below
74
73
  let parsedValue = (0, $cmJn2$react.useMemo)(()=>numberParser.parse(inputValue), [
75
74
  numberParser,
76
75
  inputValue
77
76
  ]);
78
- let parsed = (0, $cmJn2$react.useRef)(0);
79
- parsed.current = parsedValue;
80
77
  let commit = ()=>{
81
78
  // Set to empty state if input value is empty
82
79
  if (!inputValue.length) {
@@ -85,21 +82,21 @@ function $e18a693eeaf9c060$export$7f629e9dc1ecf37c(props) {
85
82
  return;
86
83
  }
87
84
  // if it failed to parse, then reset input to formatted version of current number
88
- if (isNaN(parsed.current)) {
85
+ if (isNaN(parsedValue)) {
89
86
  setInputValue(format(numberValue));
90
87
  return;
91
88
  }
92
89
  // Clamp to min and max, round to the nearest step, and round to specified number of digits
93
90
  let clampedValue;
94
- if (isNaN(step)) clampedValue = (0, $cmJn2$reactstatelyutils.clamp)(parsed.current, minValue, maxValue);
95
- else clampedValue = (0, $cmJn2$reactstatelyutils.snapValueToStep)(parsed.current, minValue, maxValue, step);
91
+ if (isNaN(step)) clampedValue = (0, $cmJn2$reactstatelyutils.clamp)(parsedValue, minValue, maxValue);
92
+ else clampedValue = (0, $cmJn2$reactstatelyutils.snapValueToStep)(parsedValue, minValue, maxValue, step);
96
93
  clampedValue = numberParser.parse(format(clampedValue));
97
94
  setNumberValue(clampedValue);
98
95
  // in a controlled state, the numberValue won't change, so we won't go back to our old input without help
99
96
  setInputValue(format(value === undefined ? clampedValue : numberValue));
100
97
  };
101
98
  let safeNextStep = (operation, minMax)=>{
102
- let prev = parsed.current;
99
+ let prev = parsedValue;
103
100
  if (isNaN(prev)) {
104
101
  // if the input is empty, start from the min/max value when incrementing/decrementing,
105
102
  // or zero if there is no min/max value defined.
@@ -108,8 +105,8 @@ function $e18a693eeaf9c060$export$7f629e9dc1ecf37c(props) {
108
105
  } else {
109
106
  // otherwise, first snap the current value to the nearest step. if it moves in the direction
110
107
  // we're going, use that value, otherwise add the step and snap that value.
111
- let newValue1 = (0, $cmJn2$reactstatelyutils.snapValueToStep)(prev, minValue, maxValue, clampStep);
112
- if (operation === "+" && newValue1 > prev || operation === "-" && newValue1 < prev) return newValue1;
108
+ let newValue = (0, $cmJn2$reactstatelyutils.snapValueToStep)(prev, minValue, maxValue, clampStep);
109
+ if (operation === "+" && newValue > prev || operation === "-" && newValue < prev) return newValue;
113
110
  return (0, $cmJn2$reactstatelyutils.snapValueToStep)($e18a693eeaf9c060$var$handleDecimalOperation(operation, prev, clampStep), minValue, maxValue, clampStep);
114
111
  }
115
112
  };
@@ -161,6 +158,7 @@ function $e18a693eeaf9c060$export$7f629e9dc1ecf37c(props) {
161
158
  minValue: minValue,
162
159
  maxValue: maxValue,
163
160
  numberValue: parsedValue,
161
+ setNumberValue: setNumberValue,
164
162
  setInputValue: setInputValue,
165
163
  inputValue: inputValue,
166
164
  commit: commit
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/@react-stately/numberfield/src/index.ts","packages/@react-stately/numberfield/src/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\nexport {useNumberFieldState} from './useNumberFieldState';\n\nexport type {NumberFieldStateOptions} from './useNumberFieldState';\nexport type {NumberFieldState} from './useNumberFieldState';\n","/*\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, useControlledState} from '@react-stately/utils';\nimport {NumberFieldProps} from '@react-types/numberfield';\nimport {NumberFormatter, NumberParser} from '@internationalized/number';\nimport {useCallback, useMemo, useRef, useState} from 'react';\n\nexport interface NumberFieldState {\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 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 /**\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 */\n commit(): 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,\n onChange,\n locale,\n isDisabled,\n isReadOnly\n } = props;\n\n let [numberValue, setNumberValue] = useControlledState<number>(value, isNaN(defaultValue) ? NaN : defaultValue, onChange);\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 clampStep = !isNaN(step) ? step : 1;\n if (intlOptions.style === 'percent' && 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 = useRef(numberValue);\n let prevLocale = useRef(locale);\n let prevFormatOptions = useRef(formatOptions);\n if (!Object.is(numberValue, prevValue.current) || locale !== prevLocale.current || formatOptions !== prevFormatOptions.current) {\n setInputValue(format(numberValue));\n prevValue.current = numberValue;\n prevLocale.current = locale;\n prevFormatOptions.current = formatOptions;\n }\n\n // Store last parsed value in a ref so it can be used by increment/decrement below\n let parsedValue = useMemo(() => numberParser.parse(inputValue), [numberParser, inputValue]);\n let parsed = useRef(0);\n parsed.current = parsedValue;\n\n let commit = () => {\n // Set to empty state if input value is empty\n if (!inputValue.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(parsed.current)) {\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: number;\n if (isNaN(step)) {\n clampedValue = clamp(parsed.current, minValue, maxValue);\n } else {\n clampedValue = snapValueToStep(parsed.current, minValue, maxValue, step);\n }\n\n clampedValue = numberParser.parse(format(clampedValue));\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 };\n\n let safeNextStep = (operation: '+' | '-', minMax: number) => {\n let prev = parsed.current;\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 };\n\n let decrement = () => {\n let newValue = safeNextStep('-', maxValue);\n\n if (newValue === numberValue) {\n setInputValue(format(newValue));\n }\n\n setNumberValue(newValue);\n };\n\n let incrementToMax = () => {\n if (maxValue != null) {\n setNumberValue(snapValueToStep(maxValue, minValue, maxValue, clampStep));\n }\n };\n\n let decrementToMin = () => {\n if (minValue != null) {\n setNumberValue(minValue);\n }\n };\n\n let canIncrement = useMemo(() => (\n !isDisabled &&\n !isReadOnly &&\n (\n isNaN(parsedValue) ||\n 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 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 validate,\n increment,\n incrementToMax,\n decrement,\n decrementToMin,\n canIncrement,\n canDecrement,\n minValue,\n maxValue,\n numberValue: parsedValue,\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":"main.js.map"}
1
+ {"mappings":"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","sources":["packages/@react-stately/numberfield/src/index.ts","packages/@react-stately/numberfield/src/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\nexport {useNumberFieldState} from './useNumberFieldState';\n\nexport type {NumberFieldStateOptions} from './useNumberFieldState';\nexport type {NumberFieldState} from './useNumberFieldState';\n","/*\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, useControlledState} from '@react-stately/utils';\nimport {NumberFieldProps} from '@react-types/numberfield';\nimport {NumberFormatter, NumberParser} from '@internationalized/number';\nimport {useCallback, useMemo, useState} from 'react';\n\nexport interface NumberFieldState {\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 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 */\n commit(): 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,\n onChange,\n locale,\n isDisabled,\n isReadOnly\n } = props;\n\n let [numberValue, setNumberValue] = useControlledState<number>(value, isNaN(defaultValue) ? NaN : defaultValue, onChange);\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 clampStep = !isNaN(step) ? step : 1;\n if (intlOptions.style === 'percent' && 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 = () => {\n // Set to empty state if input value is empty\n if (!inputValue.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(parsedValue)) {\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: number;\n if (isNaN(step)) {\n clampedValue = clamp(parsedValue, minValue, maxValue);\n } else {\n clampedValue = snapValueToStep(parsedValue, minValue, maxValue, step);\n }\n\n clampedValue = numberParser.parse(format(clampedValue));\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 };\n\n let safeNextStep = (operation: '+' | '-', minMax: number) => {\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 };\n\n let decrement = () => {\n let newValue = safeNextStep('-', maxValue);\n\n if (newValue === numberValue) {\n setInputValue(format(newValue));\n }\n\n setNumberValue(newValue);\n };\n\n let incrementToMax = () => {\n if (maxValue != null) {\n setNumberValue(snapValueToStep(maxValue, minValue, maxValue, clampStep));\n }\n };\n\n let decrementToMin = () => {\n if (minValue != null) {\n setNumberValue(minValue);\n }\n };\n\n let canIncrement = useMemo(() => (\n !isDisabled &&\n !isReadOnly &&\n (\n isNaN(parsedValue) ||\n 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 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 validate,\n increment,\n incrementToMax,\n decrement,\n decrementToMin,\n canIncrement,\n canDecrement,\n minValue,\n maxValue,\n numberValue: parsedValue,\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":"main.js.map"}
package/dist/module.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {useControlledState as $vhjCi$useControlledState, clamp as $vhjCi$clamp, snapValueToStep as $vhjCi$snapValueToStep} from "@react-stately/utils";
2
2
  import {NumberFormatter as $vhjCi$NumberFormatter, NumberParser as $vhjCi$NumberParser} from "@internationalized/number";
3
- import {useState as $vhjCi$useState, useMemo as $vhjCi$useMemo, useCallback as $vhjCi$useCallback, useRef as $vhjCi$useRef} from "react";
3
+ import {useState as $vhjCi$useState, useMemo as $vhjCi$useMemo, useCallback as $vhjCi$useCallback} from "react";
4
4
 
5
5
  /*
6
6
  * Copyright 2020 Adobe. All rights reserved.
@@ -56,22 +56,19 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
56
56
  // Update the input value when the number value or format options change. This is done
57
57
  // in a useEffect so that the controlled behavior is correct and we only update the
58
58
  // textfield after prop changes.
59
- let prevValue = (0, $vhjCi$useRef)(numberValue);
60
- let prevLocale = (0, $vhjCi$useRef)(locale);
61
- let prevFormatOptions = (0, $vhjCi$useRef)(formatOptions);
62
- if (!Object.is(numberValue, prevValue.current) || locale !== prevLocale.current || formatOptions !== prevFormatOptions.current) {
59
+ let [prevValue, setPrevValue] = (0, $vhjCi$useState)(numberValue);
60
+ let [prevLocale, setPrevLocale] = (0, $vhjCi$useState)(locale);
61
+ let [prevFormatOptions, setPrevFormatOptions] = (0, $vhjCi$useState)(formatOptions);
62
+ if (!Object.is(numberValue, prevValue) || locale !== prevLocale || formatOptions !== prevFormatOptions) {
63
63
  setInputValue(format(numberValue));
64
- prevValue.current = numberValue;
65
- prevLocale.current = locale;
66
- prevFormatOptions.current = formatOptions;
64
+ setPrevValue(numberValue);
65
+ setPrevLocale(locale);
66
+ setPrevFormatOptions(formatOptions);
67
67
  }
68
- // Store last parsed value in a ref so it can be used by increment/decrement below
69
68
  let parsedValue = (0, $vhjCi$useMemo)(()=>numberParser.parse(inputValue), [
70
69
  numberParser,
71
70
  inputValue
72
71
  ]);
73
- let parsed = (0, $vhjCi$useRef)(0);
74
- parsed.current = parsedValue;
75
72
  let commit = ()=>{
76
73
  // Set to empty state if input value is empty
77
74
  if (!inputValue.length) {
@@ -80,21 +77,21 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
80
77
  return;
81
78
  }
82
79
  // if it failed to parse, then reset input to formatted version of current number
83
- if (isNaN(parsed.current)) {
80
+ if (isNaN(parsedValue)) {
84
81
  setInputValue(format(numberValue));
85
82
  return;
86
83
  }
87
84
  // Clamp to min and max, round to the nearest step, and round to specified number of digits
88
85
  let clampedValue;
89
- if (isNaN(step)) clampedValue = (0, $vhjCi$clamp)(parsed.current, minValue, maxValue);
90
- else clampedValue = (0, $vhjCi$snapValueToStep)(parsed.current, minValue, maxValue, step);
86
+ if (isNaN(step)) clampedValue = (0, $vhjCi$clamp)(parsedValue, minValue, maxValue);
87
+ else clampedValue = (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, step);
91
88
  clampedValue = numberParser.parse(format(clampedValue));
92
89
  setNumberValue(clampedValue);
93
90
  // in a controlled state, the numberValue won't change, so we won't go back to our old input without help
94
91
  setInputValue(format(value === undefined ? clampedValue : numberValue));
95
92
  };
96
93
  let safeNextStep = (operation, minMax)=>{
97
- let prev = parsed.current;
94
+ let prev = parsedValue;
98
95
  if (isNaN(prev)) {
99
96
  // if the input is empty, start from the min/max value when incrementing/decrementing,
100
97
  // or zero if there is no min/max value defined.
@@ -103,8 +100,8 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
103
100
  } else {
104
101
  // otherwise, first snap the current value to the nearest step. if it moves in the direction
105
102
  // we're going, use that value, otherwise add the step and snap that value.
106
- let newValue1 = (0, $vhjCi$snapValueToStep)(prev, minValue, maxValue, clampStep);
107
- if (operation === "+" && newValue1 > prev || operation === "-" && newValue1 < prev) return newValue1;
103
+ let newValue = (0, $vhjCi$snapValueToStep)(prev, minValue, maxValue, clampStep);
104
+ if (operation === "+" && newValue > prev || operation === "-" && newValue < prev) return newValue;
108
105
  return (0, $vhjCi$snapValueToStep)($de67e98908f0c6ee$var$handleDecimalOperation(operation, prev, clampStep), minValue, maxValue, clampStep);
109
106
  }
110
107
  };
@@ -156,6 +153,7 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
156
153
  minValue: minValue,
157
154
  maxValue: maxValue,
158
155
  numberValue: parsedValue,
156
+ setNumberValue: setNumberValue,
159
157
  setInputValue: setInputValue,
160
158
  inputValue: inputValue,
161
159
  commit: commit
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/@react-stately/numberfield/src/index.ts","packages/@react-stately/numberfield/src/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\nexport {useNumberFieldState} from './useNumberFieldState';\n\nexport type {NumberFieldStateOptions} from './useNumberFieldState';\nexport type {NumberFieldState} from './useNumberFieldState';\n","/*\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, useControlledState} from '@react-stately/utils';\nimport {NumberFieldProps} from '@react-types/numberfield';\nimport {NumberFormatter, NumberParser} from '@internationalized/number';\nimport {useCallback, useMemo, useRef, useState} from 'react';\n\nexport interface NumberFieldState {\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 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 /**\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 */\n commit(): 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,\n onChange,\n locale,\n isDisabled,\n isReadOnly\n } = props;\n\n let [numberValue, setNumberValue] = useControlledState<number>(value, isNaN(defaultValue) ? NaN : defaultValue, onChange);\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 clampStep = !isNaN(step) ? step : 1;\n if (intlOptions.style === 'percent' && 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 = useRef(numberValue);\n let prevLocale = useRef(locale);\n let prevFormatOptions = useRef(formatOptions);\n if (!Object.is(numberValue, prevValue.current) || locale !== prevLocale.current || formatOptions !== prevFormatOptions.current) {\n setInputValue(format(numberValue));\n prevValue.current = numberValue;\n prevLocale.current = locale;\n prevFormatOptions.current = formatOptions;\n }\n\n // Store last parsed value in a ref so it can be used by increment/decrement below\n let parsedValue = useMemo(() => numberParser.parse(inputValue), [numberParser, inputValue]);\n let parsed = useRef(0);\n parsed.current = parsedValue;\n\n let commit = () => {\n // Set to empty state if input value is empty\n if (!inputValue.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(parsed.current)) {\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: number;\n if (isNaN(step)) {\n clampedValue = clamp(parsed.current, minValue, maxValue);\n } else {\n clampedValue = snapValueToStep(parsed.current, minValue, maxValue, step);\n }\n\n clampedValue = numberParser.parse(format(clampedValue));\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 };\n\n let safeNextStep = (operation: '+' | '-', minMax: number) => {\n let prev = parsed.current;\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 };\n\n let decrement = () => {\n let newValue = safeNextStep('-', maxValue);\n\n if (newValue === numberValue) {\n setInputValue(format(newValue));\n }\n\n setNumberValue(newValue);\n };\n\n let incrementToMax = () => {\n if (maxValue != null) {\n setNumberValue(snapValueToStep(maxValue, minValue, maxValue, clampStep));\n }\n };\n\n let decrementToMin = () => {\n if (minValue != null) {\n setNumberValue(minValue);\n }\n };\n\n let canIncrement = useMemo(() => (\n !isDisabled &&\n !isReadOnly &&\n (\n isNaN(parsedValue) ||\n 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 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 validate,\n increment,\n incrementToMax,\n decrement,\n decrementToMin,\n canIncrement,\n canDecrement,\n minValue,\n maxValue,\n numberValue: parsedValue,\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":"module.js.map"}
1
+ {"mappings":"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","sources":["packages/@react-stately/numberfield/src/index.ts","packages/@react-stately/numberfield/src/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\nexport {useNumberFieldState} from './useNumberFieldState';\n\nexport type {NumberFieldStateOptions} from './useNumberFieldState';\nexport type {NumberFieldState} from './useNumberFieldState';\n","/*\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, useControlledState} from '@react-stately/utils';\nimport {NumberFieldProps} from '@react-types/numberfield';\nimport {NumberFormatter, NumberParser} from '@internationalized/number';\nimport {useCallback, useMemo, useState} from 'react';\n\nexport interface NumberFieldState {\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 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 */\n commit(): 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,\n onChange,\n locale,\n isDisabled,\n isReadOnly\n } = props;\n\n let [numberValue, setNumberValue] = useControlledState<number>(value, isNaN(defaultValue) ? NaN : defaultValue, onChange);\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 clampStep = !isNaN(step) ? step : 1;\n if (intlOptions.style === 'percent' && 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 = () => {\n // Set to empty state if input value is empty\n if (!inputValue.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(parsedValue)) {\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: number;\n if (isNaN(step)) {\n clampedValue = clamp(parsedValue, minValue, maxValue);\n } else {\n clampedValue = snapValueToStep(parsedValue, minValue, maxValue, step);\n }\n\n clampedValue = numberParser.parse(format(clampedValue));\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 };\n\n let safeNextStep = (operation: '+' | '-', minMax: number) => {\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 };\n\n let decrement = () => {\n let newValue = safeNextStep('-', maxValue);\n\n if (newValue === numberValue) {\n setInputValue(format(newValue));\n }\n\n setNumberValue(newValue);\n };\n\n let incrementToMax = () => {\n if (maxValue != null) {\n setNumberValue(snapValueToStep(maxValue, minValue, maxValue, clampStep));\n }\n };\n\n let decrementToMin = () => {\n if (minValue != null) {\n setNumberValue(minValue);\n }\n };\n\n let canIncrement = useMemo(() => (\n !isDisabled &&\n !isReadOnly &&\n (\n isNaN(parsedValue) ||\n 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 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 validate,\n increment,\n incrementToMax,\n decrement,\n decrementToMin,\n canIncrement,\n canDecrement,\n minValue,\n maxValue,\n numberValue: parsedValue,\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":"module.js.map"}
package/dist/types.d.ts CHANGED
@@ -26,6 +26,8 @@ export interface NumberFieldState {
26
26
  validate(value: string): boolean;
27
27
  /** Sets the current text value of the input. */
28
28
  setInputValue(val: string): void;
29
+ /** Sets the number value. */
30
+ setNumberValue(val: number): void;
29
31
  /**
30
32
  * Commits the current input value. The value is parsed to a number, clamped according
31
33
  * to the minimum and maximum values of the field, and snapped to the nearest step value.
@@ -1 +1 @@
1
- {"mappings":";AAiBA;IACE;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,YAAY,EAAE,OAAO,CAAC;IACtB,4FAA4F;IAC5F,YAAY,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,gDAAgD;IAChD,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;;OAKG;IACH,MAAM,IAAI,IAAI,CAAC;IACf,0FAA0F;IAC1F,SAAS,IAAI,IAAI,CAAC;IAClB,0FAA0F;IAC1F,SAAS,IAAI,IAAI,CAAC;IAClB,6EAA6E;IAC7E,cAAc,IAAI,IAAI,CAAC;IACvB,6EAA6E;IAC7E,cAAc,IAAI,IAAI,CAAA;CACvB;AAED,wCAAyC,SAAQ,gBAAgB;IAC/D;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,oCACE,KAAK,EAAE,uBAAuB,GAC7B,gBAAgB,CA+KlB","sources":["packages/@react-stately/numberfield/src/packages/@react-stately/numberfield/src/useNumberFieldState.ts","packages/@react-stately/numberfield/src/packages/@react-stately/numberfield/src/index.ts","packages/@react-stately/numberfield/src/index.ts"],"sourcesContent":[null,null,"/*\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\nexport {useNumberFieldState} from './useNumberFieldState';\n\nexport type {NumberFieldStateOptions} from './useNumberFieldState';\nexport type {NumberFieldState} from './useNumberFieldState';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
1
+ {"mappings":";AAiBA;IACE;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,YAAY,EAAE,OAAO,CAAC;IACtB,4FAA4F;IAC5F,YAAY,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,gDAAgD;IAChD,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,6BAA6B;IAC7B,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;;OAKG;IACH,MAAM,IAAI,IAAI,CAAC;IACf,0FAA0F;IAC1F,SAAS,IAAI,IAAI,CAAC;IAClB,0FAA0F;IAC1F,SAAS,IAAI,IAAI,CAAC;IAClB,6EAA6E;IAC7E,cAAc,IAAI,IAAI,CAAC;IACvB,6EAA6E;IAC7E,cAAc,IAAI,IAAI,CAAA;CACvB;AAED,wCAAyC,SAAQ,gBAAgB;IAC/D;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,oCACE,KAAK,EAAE,uBAAuB,GAC7B,gBAAgB,CA4KlB","sources":["packages/@react-stately/numberfield/src/packages/@react-stately/numberfield/src/useNumberFieldState.ts","packages/@react-stately/numberfield/src/packages/@react-stately/numberfield/src/index.ts","packages/@react-stately/numberfield/src/index.ts"],"sourcesContent":[null,null,"/*\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\nexport {useNumberFieldState} from './useNumberFieldState';\n\nexport type {NumberFieldStateOptions} from './useNumberFieldState';\nexport type {NumberFieldState} from './useNumberFieldState';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-stately/numberfield",
3
- "version": "3.4.2",
3
+ "version": "3.6.0",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -22,11 +22,11 @@
22
22
  "url": "https://github.com/adobe/react-spectrum"
23
23
  },
24
24
  "dependencies": {
25
- "@internationalized/number": "^3.2.0",
26
- "@react-stately/utils": "^3.6.0",
27
- "@react-types/numberfield": "^3.4.2",
28
- "@react-types/shared": "^3.18.1",
29
- "@swc/helpers": "^0.4.14"
25
+ "@internationalized/number": "^3.2.1",
26
+ "@react-stately/utils": "^3.7.0",
27
+ "@react-types/numberfield": "^3.5.0",
28
+ "@react-types/shared": "^3.19.0",
29
+ "@swc/helpers": "^0.5.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
@@ -34,5 +34,5 @@
34
34
  "publishConfig": {
35
35
  "access": "public"
36
36
  },
37
- "gitHead": "5911ed21de4b76d66f6254c02302519e02d50e16"
37
+ "gitHead": "d4dfe4bb842a914f10045ee63fc6b92f034c5b30"
38
38
  }
@@ -13,7 +13,7 @@
13
13
  import {clamp, snapValueToStep, useControlledState} from '@react-stately/utils';
14
14
  import {NumberFieldProps} from '@react-types/numberfield';
15
15
  import {NumberFormatter, NumberParser} from '@internationalized/number';
16
- import {useCallback, useMemo, useRef, useState} from 'react';
16
+ import {useCallback, useMemo, useState} from 'react';
17
17
 
18
18
  export interface NumberFieldState {
19
19
  /**
@@ -42,6 +42,8 @@ export interface NumberFieldState {
42
42
  validate(value: string): boolean,
43
43
  /** Sets the current text value of the input. */
44
44
  setInputValue(val: string): void,
45
+ /** Sets the number value. */
46
+ setNumberValue(val: number): void,
45
47
  /**
46
48
  * Commits the current input value. The value is parsed to a number, clamped according
47
49
  * to the minimum and maximum values of the field, and snapped to the nearest step value.
@@ -104,21 +106,17 @@ export function useNumberFieldState(
104
106
  // Update the input value when the number value or format options change. This is done
105
107
  // in a useEffect so that the controlled behavior is correct and we only update the
106
108
  // textfield after prop changes.
107
- let prevValue = useRef(numberValue);
108
- let prevLocale = useRef(locale);
109
- let prevFormatOptions = useRef(formatOptions);
110
- if (!Object.is(numberValue, prevValue.current) || locale !== prevLocale.current || formatOptions !== prevFormatOptions.current) {
109
+ let [prevValue, setPrevValue] = useState(numberValue);
110
+ let [prevLocale, setPrevLocale] = useState(locale);
111
+ let [prevFormatOptions, setPrevFormatOptions] = useState(formatOptions);
112
+ if (!Object.is(numberValue, prevValue) || locale !== prevLocale || formatOptions !== prevFormatOptions) {
111
113
  setInputValue(format(numberValue));
112
- prevValue.current = numberValue;
113
- prevLocale.current = locale;
114
- prevFormatOptions.current = formatOptions;
114
+ setPrevValue(numberValue);
115
+ setPrevLocale(locale);
116
+ setPrevFormatOptions(formatOptions);
115
117
  }
116
118
 
117
- // Store last parsed value in a ref so it can be used by increment/decrement below
118
119
  let parsedValue = useMemo(() => numberParser.parse(inputValue), [numberParser, inputValue]);
119
- let parsed = useRef(0);
120
- parsed.current = parsedValue;
121
-
122
120
  let commit = () => {
123
121
  // Set to empty state if input value is empty
124
122
  if (!inputValue.length) {
@@ -128,7 +126,7 @@ export function useNumberFieldState(
128
126
  }
129
127
 
130
128
  // if it failed to parse, then reset input to formatted version of current number
131
- if (isNaN(parsed.current)) {
129
+ if (isNaN(parsedValue)) {
132
130
  setInputValue(format(numberValue));
133
131
  return;
134
132
  }
@@ -136,9 +134,9 @@ export function useNumberFieldState(
136
134
  // Clamp to min and max, round to the nearest step, and round to specified number of digits
137
135
  let clampedValue: number;
138
136
  if (isNaN(step)) {
139
- clampedValue = clamp(parsed.current, minValue, maxValue);
137
+ clampedValue = clamp(parsedValue, minValue, maxValue);
140
138
  } else {
141
- clampedValue = snapValueToStep(parsed.current, minValue, maxValue, step);
139
+ clampedValue = snapValueToStep(parsedValue, minValue, maxValue, step);
142
140
  }
143
141
 
144
142
  clampedValue = numberParser.parse(format(clampedValue));
@@ -149,7 +147,7 @@ export function useNumberFieldState(
149
147
  };
150
148
 
151
149
  let safeNextStep = (operation: '+' | '-', minMax: number) => {
152
- let prev = parsed.current;
150
+ let prev = parsedValue;
153
151
 
154
152
  if (isNaN(prev)) {
155
153
  // if the input is empty, start from the min/max value when incrementing/decrementing,
@@ -244,6 +242,7 @@ export function useNumberFieldState(
244
242
  minValue,
245
243
  maxValue,
246
244
  numberValue: parsedValue,
245
+ setNumberValue,
247
246
  setInputValue,
248
247
  inputValue,
249
248
  commit