@homebound/beam 2.101.4 → 2.101.5

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.
@@ -19,6 +19,8 @@ export interface NumberFieldProps {
19
19
  /** Styles overrides */
20
20
  xss?: Xss<"textAlign" | "justifyContent">;
21
21
  displayDirection?: boolean;
22
+ numFractionDigits?: number;
23
+ truncate?: boolean;
22
24
  }
23
25
  export declare function NumberField(props: NumberFieldProps): import("@emotion/react/jsx-runtime").JSX.Element;
24
- export declare function parseRawInput(rawInputValue: string | undefined, factor: number, type?: NumberFieldType): number | undefined;
26
+ export declare function formatValue(value: number, factor: number, numFractionDigits: number | undefined): number | undefined;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseRawInput = exports.NumberField = void 0;
3
+ exports.formatValue = exports.NumberField = void 0;
4
4
  const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
+ const number_1 = require("@internationalized/number");
5
6
  const react_1 = require("react");
6
7
  const react_aria_1 = require("react-aria");
7
8
  const react_stately_1 = require("react-stately");
@@ -12,34 +13,34 @@ function NumberField(props) {
12
13
  // Determine default alignment based on presentation context
13
14
  const { fieldProps } = (0, PresentationContext_1.usePresentationContext)();
14
15
  const alignment = (fieldProps === null || fieldProps === void 0 ? void 0 : fieldProps.numberAlignment) === "right" ? Css_1.Css.tr.jcfe.$ : Css_1.Css.tl.jcfs.$;
15
- const { disabled = false, required, readOnly = false, type, label, onBlur, onFocus, errorMsg, helperText, value, onChange, xss, displayDirection = false, ...otherProps } = props;
16
+ const { disabled = false, required, readOnly = false, type, label, onBlur, onFocus, errorMsg, helperText, value, onChange, xss, displayDirection = false, numFractionDigits, truncate = false, ...otherProps } = props;
16
17
  const factor = type === "percent" || type === "cents" ? 100 : type === "basisPoints" ? 10000 : 1;
17
18
  const signDisplay = displayDirection ? "exceptZero" : "auto";
19
+ const fractionFormatOptions = { [truncate ? "maximumFractionDigits" : "minimumFractionDigits"]: numFractionDigits };
20
+ const { locale } = (0, react_aria_1.useLocale)();
18
21
  // If formatOptions isn't memo'd, a useEffect in useNumberStateField will cause jank,
19
22
  // see: https://github.com/adobe/react-spectrum/issues/1893.
20
23
  const formatOptions = (0, react_1.useMemo)(() => {
21
24
  return type === "percent"
22
- ? { style: "percent", signDisplay }
25
+ ? { style: "percent", signDisplay, ...fractionFormatOptions }
23
26
  : type === "basisPoints"
24
27
  ? { style: "percent", minimumFractionDigits: 2, signDisplay }
25
28
  : type === "cents"
26
29
  ? { style: "currency", currency: "USD", minimumFractionDigits: 2, signDisplay }
27
30
  : type === "days"
28
31
  ? { style: "unit", unit: "day", unitDisplay: "long", maximumFractionDigits: 0, signDisplay }
29
- : undefined;
32
+ : fractionFormatOptions;
30
33
  }, [type]);
34
+ const numberParser = (0, react_1.useMemo)(() => new number_1.NumberParser(locale, formatOptions), [locale, formatOptions]);
31
35
  const valueRef = (0, react_1.useRef)({ wip: false });
32
- const { locale } = (0, react_aria_1.useLocale)();
33
36
  // We can use this for both useNumberFieldState + useNumberField
34
37
  const useProps = {
35
38
  locale,
36
39
  // We want percents && cents to be integers, useNumberFieldState excepts them as decimals
37
40
  value: valueRef.current.wip ? valueRef.current.value : value === undefined ? Number.NaN : value / factor,
38
- // This is called on blur with the final/committed value.
41
+ // // This is called on blur with the final/committed value.
39
42
  onChange: (value) => {
40
- // `value` for percentage style inputs will be in a number format, i.e. if input value is 4%, the `value` param will equal `.04`
41
- // Reverse the integer/decimal conversion
42
- onChange(Number.isNaN(value) ? undefined : factor !== 1 ? Math.round(value * factor) : value);
43
+ onChange(formatValue(value, factor, numFractionDigits));
43
44
  },
44
45
  onFocus: () => {
45
46
  valueRef.current = { wip: true, value: value === undefined ? Number.NaN : value / factor };
@@ -71,20 +72,17 @@ function NumberField(props) {
71
72
  return ((0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, Object.assign({ xss: { ...alignment, ...xss }, groupProps: groupProps, labelProps: labelProps, label: label, required: required, inputProps: inputProps,
72
73
  // This is called on each DOM change, to push the latest value into the field
73
74
  onChange: (rawInputValue) => {
74
- const changeValue = parseRawInput(rawInputValue, factor, type);
75
- onChange(changeValue);
75
+ const parsedValue = numberParser.parse(rawInputValue || "");
76
+ onChange(formatValue(parsedValue, factor, numFractionDigits));
76
77
  }, inputRef: inputRef, onBlur: onBlur, onFocus: onFocus, errorMsg: errorMsg, helperText: helperText, readOnly: readOnly }, otherProps), void 0));
77
78
  }
78
79
  exports.NumberField = NumberField;
79
- function parseRawInput(rawInputValue = "", factor, type) {
80
- // If the wip value is invalid, i.e. it's `10b`, don't push that back into the field state
81
- const wip = parseFloat(rawInputValue);
82
- if (isNaN(wip))
83
- return;
84
- // For percentage values we need to initially divide by 100 in order to get their "number value" ("4%" = .04) for the factor multiplier to be accurate.
85
- // For example, if the using basisPoints and the user enters "4.31%", then we would expect the response to be 431 basisPoints. If only basing off the `factor` value, then 4.31 * 10000 = 43100, which would not be correct.
86
- const value = type === "percent" || type === "basisPoints" ? wip / 100 : wip;
87
- // Since the values returned is exactly what is in the field
88
- return factor !== 1 ? Math.round(value * factor) : value;
80
+ function formatValue(value, factor, numFractionDigits) {
81
+ // We treat percents & cents as (mostly) integers, while useNumberField wants decimals, so
82
+ // undo that via `* factor` and `Math.round`, but also keep any specifically-requested `numFractionDigits`,
83
+ // i.e. for `type=percent value=12.34`, `value` will be `0.1234` that we want turn into `12.34`.
84
+ const maybeAdjustForDecimals = numFractionDigits ? Math.pow(10, numFractionDigits) : 1;
85
+ // Reverse the integer/decimal conversion
86
+ return Number.isNaN(value) ? undefined : Math.round(value * factor * maybeAdjustForDecimals) / maybeAdjustForDecimals;
89
87
  }
90
- exports.parseRawInput = parseRawInput;
88
+ exports.formatValue = formatValue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.101.4",
3
+ "version": "2.101.5",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -34,6 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@homebound/form-state": "^2.2.13",
37
+ "@internationalized/number": "^3.0.3",
37
38
  "@react-aria/utils": "^3.9.0",
38
39
  "@react-hook/resize-observer": "^1.2.2",
39
40
  "@types/tinycolor2": "^1.4.2",