@vectara/vectara-ui 18.1.0 → 18.1.1

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.
@@ -10,52 +10,61 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
- import { forwardRef, useEffect, useState } from "react";
13
+ import { forwardRef, useEffect, useRef, useState } from "react";
14
14
  import { VuiBasicInput } from "./BasicInput";
15
15
  export const VuiNumberInput = forwardRef((_a, ref) => {
16
16
  var { value, onChange, max, min, step, allowUndefined } = _a, rest = __rest(_a, ["value", "onChange", "max", "min", "step", "allowUndefined"]);
17
+ // localValue (rather than binding to `value` directly) sidesteps a
18
+ // Firefox quirk: `<input type="number">` reports "" mid-decimal when the
19
+ // the user types "1,0", which would round-trip through the parent and
20
+ // erase the user's input.
17
21
  const [localValue, setLocalValue] = useState(value);
18
- // This is a hacky solution to the number input misbehaving on Firefox.
19
- // If we were to apply the value and onChange values directly to the
20
- // value and onChange props of the input, then a user who:
21
- // 1. Selects all
22
- // 2. Types 1.0
23
- // will have the input show "0" as soon as they enter a decimal point.
24
- // When that character is entered, onChange is called with undefined.
25
- // This value gets stored in the value state, which resets the value to 0.
26
- // For some reason, using a useState hook to store the value doesn't have
27
- // this problem.
22
+ // Last value exchanged with the parent. The resync effect ignores echoes
23
+ // of our own emits without it, a stale prop ("6" arriving after the
24
+ // user has already typed "65") would clobber the in-flight edit.
25
+ const lastSyncedRef = useRef(value);
28
26
  useEffect(() => {
29
- // Reflect the upstream value when it changes. Ignore 0 because that
30
- // indicates the user has entered a decimal point (Firefox workaround).
31
- // When allowUndefined is on, also ignore undefined — otherwise the
32
- // parent reflecting undefined back would clear the input mid-typing.
33
- const isUndefined = !(allowUndefined && value === undefined);
34
- if (value !== 0 && isUndefined) {
27
+ if (value !== lastSyncedRef.current) {
28
+ lastSyncedRef.current = value;
35
29
  setLocalValue(value);
36
30
  }
37
31
  }, [value]);
38
- // Propagate localValue changes upstream. Without allowUndefined, an
39
- // undefined localValue (empty input) is coerced to 0 so existing
40
- // consumers always receive a number. With allowUndefined, undefined
41
- // passes through so consumers can treat empty as "no value."
42
- useEffect(() => {
43
- onChange(allowUndefined ? localValue : localValue !== null && localValue !== void 0 ? localValue : 0);
44
- }, [localValue]);
32
+ const emit = (next) => {
33
+ const outgoing = allowUndefined ? next : next !== null && next !== void 0 ? next : 0;
34
+ lastSyncedRef.current = outgoing;
35
+ onChange(outgoing);
36
+ };
45
37
  const onChangeValue = (e) => {
46
- // Enable resetting the value to undefined.
47
- if (e.target.value === "")
48
- return setLocalValue(undefined);
38
+ if (e.target.value === "") {
39
+ setLocalValue(undefined);
40
+ emit(undefined);
41
+ return;
42
+ }
49
43
  const numberValue = Number(e.target.value);
50
- if (isNaN(numberValue))
51
- return setLocalValue(undefined);
52
- setLocalValue(Number(e.target.value));
44
+ if (isNaN(numberValue)) {
45
+ setLocalValue(undefined);
46
+ emit(undefined);
47
+ return;
48
+ }
49
+ setLocalValue(numberValue);
50
+ emit(numberValue);
53
51
  };
54
52
  const onBlur = () => {
55
- if (min !== undefined && value !== undefined && value < min)
56
- onChange(min);
57
- if (max !== undefined && value !== undefined && value > max)
58
- onChange(max);
53
+ // Clamp against the effective emitted value so empty + !allowUndefined
54
+ // (which emits 0) still clamps to min.
55
+ const current = allowUndefined ? localValue : localValue !== null && localValue !== void 0 ? localValue : 0;
56
+ if (current === undefined)
57
+ return;
58
+ if (min !== undefined && current < min) {
59
+ // Clamp min.
60
+ setLocalValue(min);
61
+ emit(min);
62
+ }
63
+ else if (max !== undefined && current > max) {
64
+ // Clamp max.
65
+ setLocalValue(max);
66
+ emit(max);
67
+ }
59
68
  };
60
69
  const props = Object.assign({ type: "number", value: localValue !== null && localValue !== void 0 ? localValue : "", onChange: onChangeValue, onBlur,
61
70
  max,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectara/vectara-ui",
3
- "version": "18.1.0",
3
+ "version": "18.1.1",
4
4
  "homepage": "./",
5
5
  "description": "Vectara's design system, codified as a React and Sass component library",
6
6
  "author": "Vectara",