@react-stately/numberfield 3.7.0 → 3.9.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,4 +1,4 @@
1
- import {useControlledState as $vhjCi$useControlledState, clamp as $vhjCi$clamp, snapValueToStep as $vhjCi$snapValueToStep} from "@react-stately/utils";
1
+ import {snapValueToStep as $vhjCi$snapValueToStep, clamp as $vhjCi$clamp, useControlledState as $vhjCi$useControlledState} from "@react-stately/utils";
2
2
  import {useFormValidationState as $vhjCi$useFormValidationState} from "@react-stately/form";
3
3
  import {NumberFormatter as $vhjCi$NumberFormatter, NumberParser as $vhjCi$NumberParser} from "@internationalized/number";
4
4
  import {useState as $vhjCi$useState, useMemo as $vhjCi$useMemo, useCallback as $vhjCi$useCallback} from "react";
@@ -28,7 +28,16 @@ import {useState as $vhjCi$useState, useMemo as $vhjCi$useMemo, useCallback as $
28
28
 
29
29
 
30
30
  function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
31
- let { minValue: minValue, maxValue: maxValue, step: step, formatOptions: formatOptions, value: value, defaultValue: defaultValue, onChange: onChange, locale: locale, isDisabled: isDisabled, isReadOnly: isReadOnly } = props;
31
+ let { minValue: minValue, maxValue: maxValue, step: step, formatOptions: formatOptions, value: value, defaultValue: defaultValue = NaN, onChange: onChange, locale: locale, isDisabled: isDisabled, isReadOnly: isReadOnly } = props;
32
+ if (value === null) value = NaN;
33
+ if (value !== undefined && !isNaN(value)) {
34
+ if (step !== undefined && !isNaN(step)) value = (0, $vhjCi$snapValueToStep)(value, minValue, maxValue, step);
35
+ else value = (0, $vhjCi$clamp)(value, minValue, maxValue);
36
+ }
37
+ if (!isNaN(defaultValue)) {
38
+ if (step !== undefined && !isNaN(step)) defaultValue = (0, $vhjCi$snapValueToStep)(defaultValue, minValue, maxValue, step);
39
+ else defaultValue = (0, $vhjCi$clamp)(defaultValue, minValue, maxValue);
40
+ }
32
41
  let [numberValue, setNumberValue] = (0, $vhjCi$useControlledState)(value, isNaN(defaultValue) ? NaN : defaultValue, onChange);
33
42
  let [inputValue, setInputValue] = (0, $vhjCi$useState)(()=>isNaN(numberValue) ? "" : new (0, $vhjCi$NumberFormatter)(locale, formatOptions).format(numberValue));
34
43
  let numberParser = (0, $vhjCi$useMemo)(()=>new (0, $vhjCi$NumberParser)(locale, formatOptions), [
@@ -57,8 +66,8 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
57
66
  ...props,
58
67
  value: numberValue
59
68
  });
60
- let clampStep = !isNaN(step) ? step : 1;
61
- if (intlOptions.style === "percent" && isNaN(step)) clampStep = 0.01;
69
+ let clampStep = step !== undefined && !isNaN(step) ? step : 1;
70
+ if (intlOptions.style === "percent" && (step === undefined || isNaN(step))) clampStep = 0.01;
62
71
  // Update the input value when the number value or format options change. This is done
63
72
  // in a useEffect so that the controlled behavior is correct and we only update the
64
73
  // textfield after prop changes.
@@ -89,14 +98,14 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
89
98
  }
90
99
  // Clamp to min and max, round to the nearest step, and round to specified number of digits
91
100
  let clampedValue;
92
- if (isNaN(step)) clampedValue = (0, $vhjCi$clamp)(parsedValue, minValue, maxValue);
101
+ if (step === undefined || isNaN(step)) clampedValue = (0, $vhjCi$clamp)(parsedValue, minValue, maxValue);
93
102
  else clampedValue = (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, step);
94
103
  clampedValue = numberParser.parse(format(clampedValue));
95
104
  setNumberValue(clampedValue);
96
105
  // in a controlled state, the numberValue won't change, so we won't go back to our old input without help
97
106
  setInputValue(format(value === undefined ? clampedValue : numberValue));
98
107
  };
99
- let safeNextStep = (operation, minMax)=>{
108
+ let safeNextStep = (operation, minMax = 0)=>{
100
109
  let prev = parsedValue;
101
110
  if (isNaN(prev)) {
102
111
  // if the input is empty, start from the min/max value when incrementing/decrementing,
@@ -139,7 +148,7 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
139
148
  validation.commitValidation();
140
149
  }
141
150
  };
142
- let canIncrement = (0, $vhjCi$useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || isNaN(maxValue) || (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, clampStep) > parsedValue || $de67e98908f0c6ee$var$handleDecimalOperation("+", parsedValue, clampStep) <= maxValue), [
151
+ let canIncrement = (0, $vhjCi$useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || maxValue === undefined || isNaN(maxValue) || (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, clampStep) > parsedValue || $de67e98908f0c6ee$var$handleDecimalOperation("+", parsedValue, clampStep) <= maxValue), [
143
152
  isDisabled,
144
153
  isReadOnly,
145
154
  minValue,
@@ -147,7 +156,7 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
147
156
  clampStep,
148
157
  parsedValue
149
158
  ]);
150
- let canDecrement = (0, $vhjCi$useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || isNaN(minValue) || (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, clampStep) < parsedValue || $de67e98908f0c6ee$var$handleDecimalOperation("-", parsedValue, clampStep) >= minValue), [
159
+ let canDecrement = (0, $vhjCi$useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || minValue === undefined || isNaN(minValue) || (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, clampStep) < parsedValue || $de67e98908f0c6ee$var$handleDecimalOperation("-", parsedValue, clampStep) >= minValue), [
151
160
  isDisabled,
152
161
  isReadOnly,
153
162
  minValue,
package/dist/main.js CHANGED
@@ -34,7 +34,16 @@ $parcel$export(module.exports, "useNumberFieldState", () => $e18a693eeaf9c060$ex
34
34
 
35
35
 
36
36
  function $e18a693eeaf9c060$export$7f629e9dc1ecf37c(props) {
37
- let { minValue: minValue, maxValue: maxValue, step: step, formatOptions: formatOptions, value: value, defaultValue: defaultValue, onChange: onChange, locale: locale, isDisabled: isDisabled, isReadOnly: isReadOnly } = props;
37
+ let { minValue: minValue, maxValue: maxValue, step: step, formatOptions: formatOptions, value: value, defaultValue: defaultValue = NaN, onChange: onChange, locale: locale, isDisabled: isDisabled, isReadOnly: isReadOnly } = props;
38
+ if (value === null) value = NaN;
39
+ if (value !== undefined && !isNaN(value)) {
40
+ if (step !== undefined && !isNaN(step)) value = (0, $cmJn2$reactstatelyutils.snapValueToStep)(value, minValue, maxValue, step);
41
+ else value = (0, $cmJn2$reactstatelyutils.clamp)(value, minValue, maxValue);
42
+ }
43
+ if (!isNaN(defaultValue)) {
44
+ if (step !== undefined && !isNaN(step)) defaultValue = (0, $cmJn2$reactstatelyutils.snapValueToStep)(defaultValue, minValue, maxValue, step);
45
+ else defaultValue = (0, $cmJn2$reactstatelyutils.clamp)(defaultValue, minValue, maxValue);
46
+ }
38
47
  let [numberValue, setNumberValue] = (0, $cmJn2$reactstatelyutils.useControlledState)(value, isNaN(defaultValue) ? NaN : defaultValue, onChange);
39
48
  let [inputValue, setInputValue] = (0, $cmJn2$react.useState)(()=>isNaN(numberValue) ? "" : new (0, $cmJn2$internationalizednumber.NumberFormatter)(locale, formatOptions).format(numberValue));
40
49
  let numberParser = (0, $cmJn2$react.useMemo)(()=>new (0, $cmJn2$internationalizednumber.NumberParser)(locale, formatOptions), [
@@ -63,8 +72,8 @@ function $e18a693eeaf9c060$export$7f629e9dc1ecf37c(props) {
63
72
  ...props,
64
73
  value: numberValue
65
74
  });
66
- let clampStep = !isNaN(step) ? step : 1;
67
- if (intlOptions.style === "percent" && isNaN(step)) clampStep = 0.01;
75
+ let clampStep = step !== undefined && !isNaN(step) ? step : 1;
76
+ if (intlOptions.style === "percent" && (step === undefined || isNaN(step))) clampStep = 0.01;
68
77
  // Update the input value when the number value or format options change. This is done
69
78
  // in a useEffect so that the controlled behavior is correct and we only update the
70
79
  // textfield after prop changes.
@@ -95,14 +104,14 @@ function $e18a693eeaf9c060$export$7f629e9dc1ecf37c(props) {
95
104
  }
96
105
  // Clamp to min and max, round to the nearest step, and round to specified number of digits
97
106
  let clampedValue;
98
- if (isNaN(step)) clampedValue = (0, $cmJn2$reactstatelyutils.clamp)(parsedValue, minValue, maxValue);
107
+ if (step === undefined || isNaN(step)) clampedValue = (0, $cmJn2$reactstatelyutils.clamp)(parsedValue, minValue, maxValue);
99
108
  else clampedValue = (0, $cmJn2$reactstatelyutils.snapValueToStep)(parsedValue, minValue, maxValue, step);
100
109
  clampedValue = numberParser.parse(format(clampedValue));
101
110
  setNumberValue(clampedValue);
102
111
  // in a controlled state, the numberValue won't change, so we won't go back to our old input without help
103
112
  setInputValue(format(value === undefined ? clampedValue : numberValue));
104
113
  };
105
- let safeNextStep = (operation, minMax)=>{
114
+ let safeNextStep = (operation, minMax = 0)=>{
106
115
  let prev = parsedValue;
107
116
  if (isNaN(prev)) {
108
117
  // if the input is empty, start from the min/max value when incrementing/decrementing,
@@ -145,7 +154,7 @@ function $e18a693eeaf9c060$export$7f629e9dc1ecf37c(props) {
145
154
  validation.commitValidation();
146
155
  }
147
156
  };
148
- let canIncrement = (0, $cmJn2$react.useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || isNaN(maxValue) || (0, $cmJn2$reactstatelyutils.snapValueToStep)(parsedValue, minValue, maxValue, clampStep) > parsedValue || $e18a693eeaf9c060$var$handleDecimalOperation("+", parsedValue, clampStep) <= maxValue), [
157
+ let canIncrement = (0, $cmJn2$react.useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || maxValue === undefined || isNaN(maxValue) || (0, $cmJn2$reactstatelyutils.snapValueToStep)(parsedValue, minValue, maxValue, clampStep) > parsedValue || $e18a693eeaf9c060$var$handleDecimalOperation("+", parsedValue, clampStep) <= maxValue), [
149
158
  isDisabled,
150
159
  isReadOnly,
151
160
  minValue,
@@ -153,7 +162,7 @@ function $e18a693eeaf9c060$export$7f629e9dc1ecf37c(props) {
153
162
  clampStep,
154
163
  parsedValue
155
164
  ]);
156
- let canDecrement = (0, $cmJn2$react.useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || isNaN(minValue) || (0, $cmJn2$reactstatelyutils.snapValueToStep)(parsedValue, minValue, maxValue, clampStep) < parsedValue || $e18a693eeaf9c060$var$handleDecimalOperation("-", parsedValue, clampStep) >= minValue), [
165
+ let canDecrement = (0, $cmJn2$react.useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || minValue === undefined || isNaN(minValue) || (0, $cmJn2$reactstatelyutils.snapValueToStep)(parsedValue, minValue, maxValue, clampStep) < parsedValue || $e18a693eeaf9c060$var$handleDecimalOperation("-", parsedValue, clampStep) >= minValue), [
157
166
  isDisabled,
158
167
  isReadOnly,
159
168
  minValue,
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 {FormValidationState, useFormValidationState} from '@react-stately/form';\nimport {NumberFieldProps} from '@react-types/numberfield';\nimport {NumberFormatter, NumberParser} from '@internationalized/number';\nimport {useCallback, useMemo, useState} from 'react';\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 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 validation = useFormValidationState({\n ...props,\n value: numberValue\n });\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 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 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 ...validation,\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"}
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 {FormValidationState, useFormValidationState} from '@react-stately/form';\nimport {NumberFieldProps} from '@react-types/numberfield';\nimport {NumberFormatter, NumberParser} from '@internationalized/number';\nimport {useCallback, useMemo, useState} from 'react';\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 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 = NaN,\n onChange,\n locale,\n isDisabled,\n isReadOnly\n } = props;\n\n if (value === null) {\n value = NaN;\n }\n\n if (value !== undefined && !isNaN(value)) {\n if (step !== undefined && !isNaN(step)) {\n value = snapValueToStep(value, minValue, maxValue, step);\n } else {\n value = clamp(value, minValue, maxValue);\n }\n }\n\n if (!isNaN(defaultValue)) {\n if (step !== undefined && !isNaN(step)) {\n defaultValue = snapValueToStep(defaultValue, minValue, maxValue, step);\n } else {\n defaultValue = clamp(defaultValue, minValue, maxValue);\n }\n }\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 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 = () => {\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 (step === undefined || 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 = 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 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,4 +1,4 @@
1
- import {useControlledState as $vhjCi$useControlledState, clamp as $vhjCi$clamp, snapValueToStep as $vhjCi$snapValueToStep} from "@react-stately/utils";
1
+ import {snapValueToStep as $vhjCi$snapValueToStep, clamp as $vhjCi$clamp, useControlledState as $vhjCi$useControlledState} from "@react-stately/utils";
2
2
  import {useFormValidationState as $vhjCi$useFormValidationState} from "@react-stately/form";
3
3
  import {NumberFormatter as $vhjCi$NumberFormatter, NumberParser as $vhjCi$NumberParser} from "@internationalized/number";
4
4
  import {useState as $vhjCi$useState, useMemo as $vhjCi$useMemo, useCallback as $vhjCi$useCallback} from "react";
@@ -28,7 +28,16 @@ import {useState as $vhjCi$useState, useMemo as $vhjCi$useMemo, useCallback as $
28
28
 
29
29
 
30
30
  function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
31
- let { minValue: minValue, maxValue: maxValue, step: step, formatOptions: formatOptions, value: value, defaultValue: defaultValue, onChange: onChange, locale: locale, isDisabled: isDisabled, isReadOnly: isReadOnly } = props;
31
+ let { minValue: minValue, maxValue: maxValue, step: step, formatOptions: formatOptions, value: value, defaultValue: defaultValue = NaN, onChange: onChange, locale: locale, isDisabled: isDisabled, isReadOnly: isReadOnly } = props;
32
+ if (value === null) value = NaN;
33
+ if (value !== undefined && !isNaN(value)) {
34
+ if (step !== undefined && !isNaN(step)) value = (0, $vhjCi$snapValueToStep)(value, minValue, maxValue, step);
35
+ else value = (0, $vhjCi$clamp)(value, minValue, maxValue);
36
+ }
37
+ if (!isNaN(defaultValue)) {
38
+ if (step !== undefined && !isNaN(step)) defaultValue = (0, $vhjCi$snapValueToStep)(defaultValue, minValue, maxValue, step);
39
+ else defaultValue = (0, $vhjCi$clamp)(defaultValue, minValue, maxValue);
40
+ }
32
41
  let [numberValue, setNumberValue] = (0, $vhjCi$useControlledState)(value, isNaN(defaultValue) ? NaN : defaultValue, onChange);
33
42
  let [inputValue, setInputValue] = (0, $vhjCi$useState)(()=>isNaN(numberValue) ? "" : new (0, $vhjCi$NumberFormatter)(locale, formatOptions).format(numberValue));
34
43
  let numberParser = (0, $vhjCi$useMemo)(()=>new (0, $vhjCi$NumberParser)(locale, formatOptions), [
@@ -57,8 +66,8 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
57
66
  ...props,
58
67
  value: numberValue
59
68
  });
60
- let clampStep = !isNaN(step) ? step : 1;
61
- if (intlOptions.style === "percent" && isNaN(step)) clampStep = 0.01;
69
+ let clampStep = step !== undefined && !isNaN(step) ? step : 1;
70
+ if (intlOptions.style === "percent" && (step === undefined || isNaN(step))) clampStep = 0.01;
62
71
  // Update the input value when the number value or format options change. This is done
63
72
  // in a useEffect so that the controlled behavior is correct and we only update the
64
73
  // textfield after prop changes.
@@ -89,14 +98,14 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
89
98
  }
90
99
  // Clamp to min and max, round to the nearest step, and round to specified number of digits
91
100
  let clampedValue;
92
- if (isNaN(step)) clampedValue = (0, $vhjCi$clamp)(parsedValue, minValue, maxValue);
101
+ if (step === undefined || isNaN(step)) clampedValue = (0, $vhjCi$clamp)(parsedValue, minValue, maxValue);
93
102
  else clampedValue = (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, step);
94
103
  clampedValue = numberParser.parse(format(clampedValue));
95
104
  setNumberValue(clampedValue);
96
105
  // in a controlled state, the numberValue won't change, so we won't go back to our old input without help
97
106
  setInputValue(format(value === undefined ? clampedValue : numberValue));
98
107
  };
99
- let safeNextStep = (operation, minMax)=>{
108
+ let safeNextStep = (operation, minMax = 0)=>{
100
109
  let prev = parsedValue;
101
110
  if (isNaN(prev)) {
102
111
  // if the input is empty, start from the min/max value when incrementing/decrementing,
@@ -139,7 +148,7 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
139
148
  validation.commitValidation();
140
149
  }
141
150
  };
142
- let canIncrement = (0, $vhjCi$useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || isNaN(maxValue) || (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, clampStep) > parsedValue || $de67e98908f0c6ee$var$handleDecimalOperation("+", parsedValue, clampStep) <= maxValue), [
151
+ let canIncrement = (0, $vhjCi$useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || maxValue === undefined || isNaN(maxValue) || (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, clampStep) > parsedValue || $de67e98908f0c6ee$var$handleDecimalOperation("+", parsedValue, clampStep) <= maxValue), [
143
152
  isDisabled,
144
153
  isReadOnly,
145
154
  minValue,
@@ -147,7 +156,7 @@ function $de67e98908f0c6ee$export$7f629e9dc1ecf37c(props) {
147
156
  clampStep,
148
157
  parsedValue
149
158
  ]);
150
- let canDecrement = (0, $vhjCi$useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || isNaN(minValue) || (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, clampStep) < parsedValue || $de67e98908f0c6ee$var$handleDecimalOperation("-", parsedValue, clampStep) >= minValue), [
159
+ let canDecrement = (0, $vhjCi$useMemo)(()=>!isDisabled && !isReadOnly && (isNaN(parsedValue) || minValue === undefined || isNaN(minValue) || (0, $vhjCi$snapValueToStep)(parsedValue, minValue, maxValue, clampStep) < parsedValue || $de67e98908f0c6ee$var$handleDecimalOperation("-", parsedValue, clampStep) >= minValue), [
151
160
  isDisabled,
152
161
  isReadOnly,
153
162
  minValue,
@@ -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 {FormValidationState, useFormValidationState} from '@react-stately/form';\nimport {NumberFieldProps} from '@react-types/numberfield';\nimport {NumberFormatter, NumberParser} from '@internationalized/number';\nimport {useCallback, useMemo, useState} from 'react';\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 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 validation = useFormValidationState({\n ...props,\n value: numberValue\n });\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 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 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 ...validation,\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"}
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 {FormValidationState, useFormValidationState} from '@react-stately/form';\nimport {NumberFieldProps} from '@react-types/numberfield';\nimport {NumberFormatter, NumberParser} from '@internationalized/number';\nimport {useCallback, useMemo, useState} from 'react';\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 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 = NaN,\n onChange,\n locale,\n isDisabled,\n isReadOnly\n } = props;\n\n if (value === null) {\n value = NaN;\n }\n\n if (value !== undefined && !isNaN(value)) {\n if (step !== undefined && !isNaN(step)) {\n value = snapValueToStep(value, minValue, maxValue, step);\n } else {\n value = clamp(value, minValue, maxValue);\n }\n }\n\n if (!isNaN(defaultValue)) {\n if (step !== undefined && !isNaN(step)) {\n defaultValue = snapValueToStep(defaultValue, minValue, maxValue, step);\n } else {\n defaultValue = clamp(defaultValue, minValue, maxValue);\n }\n }\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 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 = () => {\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 (step === undefined || 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 = 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 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
@@ -12,9 +12,9 @@ export interface NumberFieldState extends FormValidationState {
12
12
  */
13
13
  numberValue: number;
14
14
  /** The minimum value of the number field. */
15
- minValue: number;
15
+ minValue?: number;
16
16
  /** The maximum value of the number field. */
17
- maxValue: number;
17
+ maxValue?: number;
18
18
  /** Whether the current value can be incremented according to the maximum value and step. */
19
19
  canIncrement: boolean;
20
20
  /** Whether the current value can be decremented according to the minimum value and step. */
@@ -1 +1 @@
1
- {"mappings":";;AAkBA,iCAAkC,SAAQ,mBAAmB;IAC3D;;;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,CAsLlB","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":";;AAkBA,iCAAkC,SAAQ,mBAAmB;IAC3D;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,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,CA0MlB","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.7.0",
3
+ "version": "3.9.0",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -22,10 +22,10 @@
22
22
  "url": "https://github.com/adobe/react-spectrum"
23
23
  },
24
24
  "dependencies": {
25
- "@internationalized/number": "^3.4.0",
25
+ "@internationalized/number": "^3.5.0",
26
26
  "@react-stately/form": "^3.0.0",
27
27
  "@react-stately/utils": "^3.9.0",
28
- "@react-types/numberfield": "^3.7.0",
28
+ "@react-types/numberfield": "^3.8.0",
29
29
  "@swc/helpers": "^0.5.0"
30
30
  },
31
31
  "peerDependencies": {
@@ -34,5 +34,5 @@
34
34
  "publishConfig": {
35
35
  "access": "public"
36
36
  },
37
- "gitHead": "9ce2f674eab2cc8912800d3162dcf00a1ce94274"
37
+ "gitHead": "f040ff62678e6a31375b96c05396df0bae660350"
38
38
  }
@@ -28,9 +28,9 @@ export interface NumberFieldState extends FormValidationState {
28
28
  */
29
29
  numberValue: number,
30
30
  /** The minimum value of the number field. */
31
- minValue: number,
31
+ minValue?: number,
32
32
  /** The maximum value of the number field. */
33
- maxValue: number,
33
+ maxValue?: number,
34
34
  /** Whether the current value can be incremented according to the maximum value and step. */
35
35
  canIncrement: boolean,
36
36
  /** Whether the current value can be decremented according to the minimum value and step. */
@@ -83,13 +83,33 @@ export function useNumberFieldState(
83
83
  step,
84
84
  formatOptions,
85
85
  value,
86
- defaultValue,
86
+ defaultValue = NaN,
87
87
  onChange,
88
88
  locale,
89
89
  isDisabled,
90
90
  isReadOnly
91
91
  } = props;
92
92
 
93
+ if (value === null) {
94
+ value = NaN;
95
+ }
96
+
97
+ if (value !== undefined && !isNaN(value)) {
98
+ if (step !== undefined && !isNaN(step)) {
99
+ value = snapValueToStep(value, minValue, maxValue, step);
100
+ } else {
101
+ value = clamp(value, minValue, maxValue);
102
+ }
103
+ }
104
+
105
+ if (!isNaN(defaultValue)) {
106
+ if (step !== undefined && !isNaN(step)) {
107
+ defaultValue = snapValueToStep(defaultValue, minValue, maxValue, step);
108
+ } else {
109
+ defaultValue = clamp(defaultValue, minValue, maxValue);
110
+ }
111
+ }
112
+
93
113
  let [numberValue, setNumberValue] = useControlledState<number>(value, isNaN(defaultValue) ? NaN : defaultValue, onChange);
94
114
  let [inputValue, setInputValue] = useState(() => isNaN(numberValue) ? '' : new NumberFormatter(locale, formatOptions).format(numberValue));
95
115
 
@@ -104,8 +124,8 @@ export function useNumberFieldState(
104
124
  value: numberValue
105
125
  });
106
126
 
107
- let clampStep = !isNaN(step) ? step : 1;
108
- if (intlOptions.style === 'percent' && isNaN(step)) {
127
+ let clampStep = (step !== undefined && !isNaN(step)) ? step : 1;
128
+ if (intlOptions.style === 'percent' && (step === undefined || isNaN(step))) {
109
129
  clampStep = 0.01;
110
130
  }
111
131
 
@@ -139,7 +159,7 @@ export function useNumberFieldState(
139
159
 
140
160
  // Clamp to min and max, round to the nearest step, and round to specified number of digits
141
161
  let clampedValue: number;
142
- if (isNaN(step)) {
162
+ if (step === undefined || isNaN(step)) {
143
163
  clampedValue = clamp(parsedValue, minValue, maxValue);
144
164
  } else {
145
165
  clampedValue = snapValueToStep(parsedValue, minValue, maxValue, step);
@@ -152,7 +172,7 @@ export function useNumberFieldState(
152
172
  setInputValue(format(value === undefined ? clampedValue : numberValue));
153
173
  };
154
174
 
155
- let safeNextStep = (operation: '+' | '-', minMax: number) => {
175
+ let safeNextStep = (operation: '+' | '-', minMax: number = 0) => {
156
176
  let prev = parsedValue;
157
177
 
158
178
  if (isNaN(prev)) {
@@ -222,7 +242,7 @@ export function useNumberFieldState(
222
242
  !isReadOnly &&
223
243
  (
224
244
  isNaN(parsedValue) ||
225
- isNaN(maxValue) ||
245
+ (maxValue === undefined || isNaN(maxValue)) ||
226
246
  snapValueToStep(parsedValue, minValue, maxValue, clampStep) > parsedValue ||
227
247
  handleDecimalOperation('+', parsedValue, clampStep) <= maxValue
228
248
  )
@@ -233,7 +253,7 @@ export function useNumberFieldState(
233
253
  !isReadOnly &&
234
254
  (
235
255
  isNaN(parsedValue) ||
236
- isNaN(minValue) ||
256
+ (minValue === undefined || isNaN(minValue)) ||
237
257
  snapValueToStep(parsedValue, minValue, maxValue, clampStep) < parsedValue ||
238
258
  handleDecimalOperation('-', parsedValue, clampStep) >= minValue
239
259
  )