@hilum/ui 3.3.4 → 3.4.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/index.d.ts +11 -7
- package/dist/index.js +48 -14
- package/dist/index.js.map +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/src/components/input-number.tsx +62 -21
package/llms.txt
CHANGED
package/package.json
CHANGED
|
@@ -7,17 +7,21 @@ interface InputNumberProps extends Omit<
|
|
|
7
7
|
React.ComponentProps<"input">,
|
|
8
8
|
"type" | "value" | "onChange"
|
|
9
9
|
> {
|
|
10
|
-
value: number;
|
|
10
|
+
value: number | null;
|
|
11
11
|
onChange: (next: number) => void;
|
|
12
|
-
min?: number;
|
|
13
|
-
max?: number;
|
|
14
|
-
step?: number;
|
|
12
|
+
min?: number | undefined;
|
|
13
|
+
max?: number | undefined;
|
|
14
|
+
step?: number | undefined;
|
|
15
15
|
/** Suffix shown after the number (e.g. "px", "mm", "°", "%"). */
|
|
16
|
-
unit?: string;
|
|
16
|
+
unit?: string | undefined;
|
|
17
17
|
/** Number of decimals to display. Default: 0. */
|
|
18
|
-
precision?: number;
|
|
18
|
+
precision?: number | undefined;
|
|
19
|
+
/** Text shown when value is null. Useful for mixed multi-selection values. */
|
|
20
|
+
mixedLabel?: string | undefined;
|
|
21
|
+
/** Calls onChange while typing instead of waiting for blur/Enter. */
|
|
22
|
+
commitOnChange?: boolean | undefined;
|
|
19
23
|
/** Hides the up/down stepper buttons. */
|
|
20
|
-
hideSteppers?: boolean;
|
|
24
|
+
hideSteppers?: boolean | undefined;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
/**
|
|
@@ -34,39 +38,59 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>(
|
|
|
34
38
|
step = 1,
|
|
35
39
|
unit,
|
|
36
40
|
precision = 0,
|
|
41
|
+
mixedLabel = "Mixed",
|
|
42
|
+
commitOnChange = false,
|
|
37
43
|
hideSteppers = false,
|
|
38
44
|
className,
|
|
39
45
|
disabled,
|
|
46
|
+
onBlur,
|
|
47
|
+
onFocus,
|
|
48
|
+
onKeyDown,
|
|
40
49
|
...rest
|
|
41
50
|
},
|
|
42
51
|
ref,
|
|
43
52
|
) => {
|
|
44
|
-
const
|
|
53
|
+
const formatValue = React.useCallback(
|
|
54
|
+
(next: number | null) => (next === null ? mixedLabel : next.toFixed(precision)),
|
|
55
|
+
[mixedLabel, precision],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const [text, setText] = React.useState<string>(formatValue(value));
|
|
45
59
|
|
|
46
60
|
React.useEffect(() => {
|
|
47
|
-
setText(value
|
|
48
|
-
}, [
|
|
61
|
+
setText(formatValue(value));
|
|
62
|
+
}, [formatValue, value]);
|
|
49
63
|
|
|
50
64
|
const clamp = React.useCallback((n: number) => Math.min(max, Math.max(min, n)), [min, max]);
|
|
51
65
|
|
|
52
|
-
const
|
|
66
|
+
const parseAndClamp = React.useCallback((raw: string) => {
|
|
53
67
|
const n = parseFloat(raw);
|
|
54
68
|
if (Number.isFinite(n)) {
|
|
55
|
-
|
|
56
|
-
onChange(next);
|
|
57
|
-
setText(next.toFixed(precision));
|
|
58
|
-
} else {
|
|
59
|
-
setText(value.toFixed(precision));
|
|
69
|
+
return clamp(n);
|
|
60
70
|
}
|
|
71
|
+
return null;
|
|
72
|
+
}, [clamp]);
|
|
73
|
+
|
|
74
|
+
const commit = (raw: string) => {
|
|
75
|
+
const next = parseAndClamp(raw);
|
|
76
|
+
if (next === null) {
|
|
77
|
+
setText(formatValue(value));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
onChange(next);
|
|
81
|
+
setText(next.toFixed(precision));
|
|
61
82
|
};
|
|
62
83
|
|
|
63
84
|
const bump = (delta: number) => {
|
|
64
|
-
const next = clamp(value + delta);
|
|
85
|
+
const next = clamp((value ?? 0) + delta);
|
|
65
86
|
onChange(next);
|
|
66
87
|
setText(next.toFixed(precision));
|
|
67
88
|
};
|
|
68
89
|
|
|
69
|
-
const
|
|
90
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
91
|
+
onKeyDown?.(e);
|
|
92
|
+
if (e.defaultPrevented) return;
|
|
93
|
+
|
|
70
94
|
if (e.key === "ArrowUp") {
|
|
71
95
|
e.preventDefault();
|
|
72
96
|
bump(step * (e.shiftKey ? 10 : 1));
|
|
@@ -93,9 +117,26 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>(
|
|
|
93
117
|
inputMode="decimal"
|
|
94
118
|
spellCheck={false}
|
|
95
119
|
value={text}
|
|
96
|
-
onChange={(e) =>
|
|
97
|
-
|
|
98
|
-
|
|
120
|
+
onChange={(e) => {
|
|
121
|
+
setText(e.target.value);
|
|
122
|
+
if (commitOnChange) {
|
|
123
|
+
const next = parseAndClamp(e.target.value);
|
|
124
|
+
if (next !== null) {
|
|
125
|
+
onChange(next);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}}
|
|
129
|
+
onFocus={(e) => {
|
|
130
|
+
if (value === null) {
|
|
131
|
+
setText("");
|
|
132
|
+
}
|
|
133
|
+
onFocus?.(e);
|
|
134
|
+
}}
|
|
135
|
+
onBlur={(e) => {
|
|
136
|
+
commit(e.target.value);
|
|
137
|
+
onBlur?.(e);
|
|
138
|
+
}}
|
|
139
|
+
onKeyDown={handleKeyDown}
|
|
99
140
|
disabled={disabled}
|
|
100
141
|
className="w-full caption text-foreground px-2 bg-transparent focus:outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none text-right"
|
|
101
142
|
{...rest}
|