@timeax/form-palette 0.0.3 → 0.0.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.
- package/{src/schema/adapter.ts → dist/adapters.d.mts} +118 -43
- package/dist/adapters.d.ts +292 -0
- package/dist/adapters.js +13283 -0
- package/dist/adapters.js.map +1 -0
- package/dist/adapters.mjs +13269 -0
- package/dist/adapters.mjs.map +1 -0
- package/dist/index.d.mts +3744 -0
- package/dist/index.d.ts +3744 -0
- package/dist/index.js +43014 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +42965 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +22 -7
- package/.scaffold-cache.json +0 -537
- package/src/.scaffold-cache.json +0 -544
- package/src/adapters/axios.ts +0 -117
- package/src/adapters/index.ts +0 -91
- package/src/adapters/inertia.ts +0 -187
- package/src/core/adapter-registry.ts +0 -87
- package/src/core/bound/bind-host.ts +0 -14
- package/src/core/bound/observe-bound-field.ts +0 -172
- package/src/core/bound/wait-for-bound-field.ts +0 -57
- package/src/core/context.ts +0 -23
- package/src/core/core-provider.tsx +0 -818
- package/src/core/core-root.tsx +0 -72
- package/src/core/core-shell.tsx +0 -44
- package/src/core/errors/error-strip.tsx +0 -71
- package/src/core/errors/index.ts +0 -2
- package/src/core/errors/map-error-bag.ts +0 -51
- package/src/core/errors/map-zod.ts +0 -39
- package/src/core/hooks/use-button.ts +0 -220
- package/src/core/hooks/use-core-context.ts +0 -20
- package/src/core/hooks/use-core-utility.ts +0 -0
- package/src/core/hooks/use-core.ts +0 -13
- package/src/core/hooks/use-field.ts +0 -497
- package/src/core/hooks/use-optional-field.ts +0 -28
- package/src/core/index.ts +0 -0
- package/src/core/registry/binder-registry.ts +0 -82
- package/src/core/registry/field-registry.ts +0 -187
- package/src/core/test.tsx +0 -17
- package/src/global.d.ts +0 -14
- package/src/index.ts +0 -68
- package/src/input/index.ts +0 -4
- package/src/input/input-field.tsx +0 -854
- package/src/input/input-layout-graph.ts +0 -230
- package/src/input/input-props.ts +0 -190
- package/src/lib/get-global-countries.ts +0 -87
- package/src/lib/utils.ts +0 -6
- package/src/presets/index.ts +0 -0
- package/src/presets/shadcn-preset.ts +0 -0
- package/src/presets/shadcn-variants/checkbox.tsx +0 -849
- package/src/presets/shadcn-variants/chips.tsx +0 -756
- package/src/presets/shadcn-variants/color.tsx +0 -284
- package/src/presets/shadcn-variants/custom.tsx +0 -227
- package/src/presets/shadcn-variants/date.tsx +0 -796
- package/src/presets/shadcn-variants/file.tsx +0 -764
- package/src/presets/shadcn-variants/keyvalue.tsx +0 -556
- package/src/presets/shadcn-variants/multiselect.tsx +0 -1132
- package/src/presets/shadcn-variants/number.tsx +0 -176
- package/src/presets/shadcn-variants/password.tsx +0 -737
- package/src/presets/shadcn-variants/phone.tsx +0 -628
- package/src/presets/shadcn-variants/radio.tsx +0 -578
- package/src/presets/shadcn-variants/select.tsx +0 -956
- package/src/presets/shadcn-variants/slider.tsx +0 -622
- package/src/presets/shadcn-variants/text.tsx +0 -343
- package/src/presets/shadcn-variants/textarea.tsx +0 -66
- package/src/presets/shadcn-variants/toggle.tsx +0 -218
- package/src/presets/shadcn-variants/treeselect.tsx +0 -784
- package/src/presets/ui/badge.tsx +0 -46
- package/src/presets/ui/button.tsx +0 -60
- package/src/presets/ui/calendar.tsx +0 -214
- package/src/presets/ui/checkbox.tsx +0 -115
- package/src/presets/ui/custom.tsx +0 -0
- package/src/presets/ui/dialog.tsx +0 -141
- package/src/presets/ui/field.tsx +0 -246
- package/src/presets/ui/input-mask.tsx +0 -739
- package/src/presets/ui/input-otp.tsx +0 -77
- package/src/presets/ui/input.tsx +0 -1011
- package/src/presets/ui/label.tsx +0 -22
- package/src/presets/ui/number.tsx +0 -1370
- package/src/presets/ui/popover.tsx +0 -46
- package/src/presets/ui/radio-group.tsx +0 -43
- package/src/presets/ui/scroll-area.tsx +0 -56
- package/src/presets/ui/select.tsx +0 -190
- package/src/presets/ui/separator.tsx +0 -28
- package/src/presets/ui/slider.tsx +0 -61
- package/src/presets/ui/switch.tsx +0 -32
- package/src/presets/ui/textarea.tsx +0 -634
- package/src/presets/ui/time-dropdowns.tsx +0 -350
- package/src/schema/core.ts +0 -429
- package/src/schema/field-map.ts +0 -0
- package/src/schema/field.ts +0 -224
- package/src/schema/index.ts +0 -0
- package/src/schema/input-field.ts +0 -260
- package/src/schema/presets.ts +0 -0
- package/src/schema/variant.ts +0 -216
- package/src/variants/core/checkbox.tsx +0 -54
- package/src/variants/core/chips.tsx +0 -22
- package/src/variants/core/color.tsx +0 -16
- package/src/variants/core/custom.tsx +0 -18
- package/src/variants/core/date.tsx +0 -25
- package/src/variants/core/file.tsx +0 -9
- package/src/variants/core/keyvalue.tsx +0 -12
- package/src/variants/core/multiselect.tsx +0 -28
- package/src/variants/core/number.tsx +0 -115
- package/src/variants/core/password.tsx +0 -35
- package/src/variants/core/phone.tsx +0 -16
- package/src/variants/core/radio.tsx +0 -38
- package/src/variants/core/select.tsx +0 -15
- package/src/variants/core/slider.tsx +0 -55
- package/src/variants/core/text.tsx +0 -114
- package/src/variants/core/textarea.tsx +0 -22
- package/src/variants/core/toggle.tsx +0 -50
- package/src/variants/core/treeselect.tsx +0 -11
- package/src/variants/helpers/selection-summary.tsx +0 -236
- package/src/variants/index.ts +0 -75
- package/src/variants/registry.ts +0 -38
- package/src/variants/select-shared.ts +0 -0
- package/src/variants/shared.ts +0 -126
- package/tsconfig.json +0 -14
|
@@ -1,1370 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { ShadcnTextVariantProps } from "../shadcn-variants/text";
|
|
3
|
-
import { FieldSize } from "@/variants/shared";
|
|
4
|
-
import { Input } from "./input";
|
|
5
|
-
// Adjust this import to your actual text variant path:
|
|
6
|
-
//// import { ShadcnTextVariant } from "@/presets/shadcn-variants/text";
|
|
7
|
-
|
|
8
|
-
type InputRef = HTMLInputElement;
|
|
9
|
-
|
|
10
|
-
export interface InputNumberValueChangeEvent {
|
|
11
|
-
originalEvent: React.SyntheticEvent<any> | null;
|
|
12
|
-
value: number | null;
|
|
13
|
-
stopPropagation(): void;
|
|
14
|
-
preventDefault(): void;
|
|
15
|
-
target: {
|
|
16
|
-
name?: string | null;
|
|
17
|
-
id?: string | null;
|
|
18
|
-
value: number | null;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface InputNumberProps
|
|
23
|
-
extends Omit<ShadcnTextVariantProps, 'min' | 'max' | 'value'>, Omit<
|
|
24
|
-
React.InputHTMLAttributes<HTMLInputElement>,
|
|
25
|
-
| "value"
|
|
26
|
-
| "defaultValue"
|
|
27
|
-
| "onChange"
|
|
28
|
-
| "onInput"
|
|
29
|
-
| "onKeyDown"
|
|
30
|
-
| "onKeyUp"
|
|
31
|
-
| "size"
|
|
32
|
-
| 'max'
|
|
33
|
-
| 'min'
|
|
34
|
-
> {
|
|
35
|
-
onKeyUp?(event: React.KeyboardEvent<HTMLInputElement>): unknown;
|
|
36
|
-
onKeyDown?(event: React.KeyboardEvent<HTMLInputElement>): unknown;
|
|
37
|
-
// Prime-style props we actually use
|
|
38
|
-
|
|
39
|
-
value?: number | null;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Emitted when the numeric value changes (Prime-style).
|
|
43
|
-
*/
|
|
44
|
-
onValueChange?: (e: InputNumberValueChangeEvent) => void;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Optional simple change handler (numeric value).
|
|
48
|
-
*/
|
|
49
|
-
onChange?: (e: { originalEvent: React.SyntheticEvent<any>; value: number | null }) => void;
|
|
50
|
-
|
|
51
|
-
locale?: string;
|
|
52
|
-
localeMatcher?: Intl.NumberFormatOptions["localeMatcher"];
|
|
53
|
-
|
|
54
|
-
mode?: "decimal" | "currency";
|
|
55
|
-
currency?: string;
|
|
56
|
-
currencyDisplay?: Intl.NumberFormatOptions["currencyDisplay"];
|
|
57
|
-
|
|
58
|
-
useGrouping?: boolean;
|
|
59
|
-
|
|
60
|
-
minFractionDigits?: number;
|
|
61
|
-
maxFractionDigits?: number;
|
|
62
|
-
|
|
63
|
-
roundingMode?: Intl.NumberFormatOptions["roundingMode"];
|
|
64
|
-
|
|
65
|
-
min?: number | null;
|
|
66
|
-
max?: number | null;
|
|
67
|
-
|
|
68
|
-
step?: number;
|
|
69
|
-
|
|
70
|
-
allowEmpty?: boolean;
|
|
71
|
-
|
|
72
|
-
format?: boolean;
|
|
73
|
-
|
|
74
|
-
inputId?: string;
|
|
75
|
-
inputClassName?: string;
|
|
76
|
-
inputStyle?: React.CSSProperties;
|
|
77
|
-
inputRef?: React.Ref<InputRef> | null;
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* String prefix/suffix (like Prime). They are part of formatting logic.
|
|
81
|
-
* You can ALSO forward them to your text variant as visual adornments.
|
|
82
|
-
*/
|
|
83
|
-
prefix?: string;
|
|
84
|
-
suffix?: string;
|
|
85
|
-
|
|
86
|
-
// We keep size as number | undefined to mirror InputText
|
|
87
|
-
size?: FieldSize;
|
|
88
|
-
|
|
89
|
-
invalid?: boolean;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export const InputNumber = React.memo(
|
|
93
|
-
React.forwardRef<InputRef, InputNumberProps>((inProps, ref) => {
|
|
94
|
-
const props: InputNumberProps = {
|
|
95
|
-
allowEmpty: true,
|
|
96
|
-
autoFocus: false,
|
|
97
|
-
format: true,
|
|
98
|
-
locale: undefined,
|
|
99
|
-
localeMatcher: undefined,
|
|
100
|
-
mode: "decimal",
|
|
101
|
-
useGrouping: true,
|
|
102
|
-
step: 1,
|
|
103
|
-
roundingMode: undefined,
|
|
104
|
-
type: "text",
|
|
105
|
-
...inProps,
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const [focusedState, setFocusedState] = React.useState(false);
|
|
109
|
-
|
|
110
|
-
const elementRef = React.useRef<HTMLSpanElement | null>(null);
|
|
111
|
-
const inputRef = React.useRef<InputRef | null>(null);
|
|
112
|
-
const timer = React.useRef<number | undefined>(undefined);
|
|
113
|
-
const lastValue = React.useRef<string>("");
|
|
114
|
-
|
|
115
|
-
const numberFormat = React.useRef<Intl.NumberFormat | null>(null);
|
|
116
|
-
const groupChar = React.useRef<string>("");
|
|
117
|
-
const prefixChar = React.useRef<string>("");
|
|
118
|
-
const suffixChar = React.useRef<string>("");
|
|
119
|
-
|
|
120
|
-
const _numeral = React.useRef<RegExp | null>(null);
|
|
121
|
-
const _group = React.useRef<RegExp | null>(null);
|
|
122
|
-
const _minusSign = React.useRef<RegExp | null>(null);
|
|
123
|
-
const _currency = React.useRef<RegExp | null>(null);
|
|
124
|
-
const _decimal = React.useRef<RegExp | null>(null);
|
|
125
|
-
const _decimalSeparator = React.useRef<string>(".");
|
|
126
|
-
const _suffix = React.useRef<RegExp | null>(null);
|
|
127
|
-
const _prefix = React.useRef<RegExp | null>(null);
|
|
128
|
-
const _index = React.useRef<(d: string) => number | undefined>(() => 0);
|
|
129
|
-
|
|
130
|
-
const isFocusedByClick = React.useRef(false);
|
|
131
|
-
|
|
132
|
-
const resolveLocale = React.useCallback((): string => {
|
|
133
|
-
if (props.locale) return props.locale;
|
|
134
|
-
if (typeof navigator !== "undefined" && navigator.language) {
|
|
135
|
-
return navigator.language;
|
|
136
|
-
}
|
|
137
|
-
return "en-US";
|
|
138
|
-
}, [props.locale]);
|
|
139
|
-
|
|
140
|
-
const _locale = resolveLocale();
|
|
141
|
-
|
|
142
|
-
const inputMode =
|
|
143
|
-
props.inputMode ||
|
|
144
|
-
(props.mode === "decimal" && !props.minFractionDigits && !props.maxFractionDigits
|
|
145
|
-
? "numeric"
|
|
146
|
-
: "decimal");
|
|
147
|
-
|
|
148
|
-
const getOptions = React.useCallback((): Intl.NumberFormatOptions => {
|
|
149
|
-
return {
|
|
150
|
-
localeMatcher: props.localeMatcher,
|
|
151
|
-
style: props.mode,
|
|
152
|
-
currency: props.currency,
|
|
153
|
-
currencyDisplay: props.currencyDisplay,
|
|
154
|
-
useGrouping: props.useGrouping,
|
|
155
|
-
minimumFractionDigits:
|
|
156
|
-
props.minFractionDigits !== undefined ? props.minFractionDigits : undefined,
|
|
157
|
-
maximumFractionDigits:
|
|
158
|
-
props.maxFractionDigits !== undefined ? props.maxFractionDigits : undefined,
|
|
159
|
-
roundingMode: props.roundingMode,
|
|
160
|
-
};
|
|
161
|
-
}, [
|
|
162
|
-
props.localeMatcher,
|
|
163
|
-
props.mode,
|
|
164
|
-
props.currency,
|
|
165
|
-
props.currencyDisplay,
|
|
166
|
-
props.useGrouping,
|
|
167
|
-
props.minFractionDigits,
|
|
168
|
-
props.maxFractionDigits,
|
|
169
|
-
props.roundingMode,
|
|
170
|
-
]);
|
|
171
|
-
|
|
172
|
-
const escapeRegExp = (text: string): string =>
|
|
173
|
-
text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
174
|
-
|
|
175
|
-
const constructParser = React.useCallback(() => {
|
|
176
|
-
const loc = _locale;
|
|
177
|
-
numberFormat.current = new Intl.NumberFormat(loc, getOptions());
|
|
178
|
-
|
|
179
|
-
// numerals
|
|
180
|
-
const numerals = [
|
|
181
|
-
...new Intl.NumberFormat(loc, { useGrouping: false }).format(9876543210),
|
|
182
|
-
].reverse();
|
|
183
|
-
const index = new Map<string, number>(numerals.map((d, i) => [d, i]));
|
|
184
|
-
_numeral.current = new RegExp("[" + numerals.join("") + "]", "g");
|
|
185
|
-
_index.current = (d: string) => index.get(d) ?? 0;
|
|
186
|
-
|
|
187
|
-
// grouping
|
|
188
|
-
const formatterGroup = new Intl.NumberFormat(loc, { useGrouping: true });
|
|
189
|
-
groupChar.current = formatterGroup
|
|
190
|
-
.format(1000000)
|
|
191
|
-
.trim()
|
|
192
|
-
.replace(_numeral.current, "")
|
|
193
|
-
.charAt(0);
|
|
194
|
-
_group.current = new RegExp("[" + groupChar.current + "]", "g");
|
|
195
|
-
|
|
196
|
-
// minus
|
|
197
|
-
const formatterMinus = new Intl.NumberFormat(loc, { useGrouping: false });
|
|
198
|
-
const minusString = formatterMinus
|
|
199
|
-
.format(-1)
|
|
200
|
-
.trim()
|
|
201
|
-
.replace(_numeral.current, "");
|
|
202
|
-
_minusSign.current = new RegExp("[" + minusString + "]", "g");
|
|
203
|
-
|
|
204
|
-
// currency
|
|
205
|
-
if (props.currency) {
|
|
206
|
-
const formatterCurrency = new Intl.NumberFormat(loc, {
|
|
207
|
-
style: "currency",
|
|
208
|
-
currency: props.currency,
|
|
209
|
-
currencyDisplay: props.currencyDisplay,
|
|
210
|
-
minimumFractionDigits: 0,
|
|
211
|
-
maximumFractionDigits: 0,
|
|
212
|
-
roundingMode: props.roundingMode,
|
|
213
|
-
});
|
|
214
|
-
_currency.current = new RegExp(
|
|
215
|
-
"[" +
|
|
216
|
-
formatterCurrency
|
|
217
|
-
.format(1)
|
|
218
|
-
.replace(/\s/g, "")
|
|
219
|
-
.replace(_numeral.current, "")
|
|
220
|
-
.replace(_group.current, "") +
|
|
221
|
-
"]",
|
|
222
|
-
"g"
|
|
223
|
-
);
|
|
224
|
-
} else {
|
|
225
|
-
_currency.current = new RegExp("[]", "g");
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// decimal separator + expression
|
|
229
|
-
const formatterDecimal = new Intl.NumberFormat(loc, {
|
|
230
|
-
useGrouping: false,
|
|
231
|
-
});
|
|
232
|
-
const decSample = formatterDecimal.format(1.1).trim().replace(_numeral.current, "");
|
|
233
|
-
_decimalSeparator.current = decSample || ".";
|
|
234
|
-
const formatterDecOptions = new Intl.NumberFormat(loc, {
|
|
235
|
-
...getOptions(),
|
|
236
|
-
useGrouping: false,
|
|
237
|
-
});
|
|
238
|
-
_decimal.current = new RegExp(
|
|
239
|
-
"[" +
|
|
240
|
-
formatterDecOptions
|
|
241
|
-
.format(1.1)
|
|
242
|
-
.replace(_currency.current, "")
|
|
243
|
-
.trim()
|
|
244
|
-
.replace(_numeral.current, "") +
|
|
245
|
-
"]",
|
|
246
|
-
"g"
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
// prefix
|
|
250
|
-
if (props.prefix) {
|
|
251
|
-
prefixChar.current = props.prefix;
|
|
252
|
-
} else {
|
|
253
|
-
const formatterPrefix = new Intl.NumberFormat(loc, {
|
|
254
|
-
style: props.mode,
|
|
255
|
-
currency: props.currency,
|
|
256
|
-
currencyDisplay: props.currencyDisplay,
|
|
257
|
-
});
|
|
258
|
-
prefixChar.current = formatterPrefix.format(1).split("1")[0];
|
|
259
|
-
}
|
|
260
|
-
_prefix.current = new RegExp(escapeRegExp(prefixChar.current || ""), "g");
|
|
261
|
-
|
|
262
|
-
// suffix
|
|
263
|
-
if (props.suffix) {
|
|
264
|
-
suffixChar.current = props.suffix;
|
|
265
|
-
} else {
|
|
266
|
-
const formatterSuffix = new Intl.NumberFormat(loc, {
|
|
267
|
-
style: props.mode,
|
|
268
|
-
currency: props.currency,
|
|
269
|
-
currencyDisplay: props.currencyDisplay,
|
|
270
|
-
minimumFractionDigits: 0,
|
|
271
|
-
maximumFractionDigits: 0,
|
|
272
|
-
roundingMode: props.roundingMode,
|
|
273
|
-
});
|
|
274
|
-
suffixChar.current = formatterSuffix.format(1).split("1")[1];
|
|
275
|
-
}
|
|
276
|
-
_suffix.current = new RegExp(escapeRegExp(suffixChar.current || ""), "g");
|
|
277
|
-
}, [
|
|
278
|
-
_locale,
|
|
279
|
-
getOptions,
|
|
280
|
-
props.currency,
|
|
281
|
-
props.currencyDisplay,
|
|
282
|
-
props.mode,
|
|
283
|
-
props.prefix,
|
|
284
|
-
props.roundingMode,
|
|
285
|
-
props.suffix,
|
|
286
|
-
]);
|
|
287
|
-
|
|
288
|
-
const formatValue = React.useCallback(
|
|
289
|
-
(value: number | string | null | undefined): string => {
|
|
290
|
-
if (value == null) return "";
|
|
291
|
-
if (value === "-") return "-";
|
|
292
|
-
|
|
293
|
-
const numeric =
|
|
294
|
-
typeof value === "number"
|
|
295
|
-
? value
|
|
296
|
-
: typeof value === "string"
|
|
297
|
-
? Number(value)
|
|
298
|
-
: Number.NaN;
|
|
299
|
-
|
|
300
|
-
if (Number.isNaN(numeric)) return "";
|
|
301
|
-
|
|
302
|
-
if (props.format) {
|
|
303
|
-
const formatter =
|
|
304
|
-
numberFormat.current || new Intl.NumberFormat(_locale, getOptions());
|
|
305
|
-
let formatted = formatter.format(numeric);
|
|
306
|
-
|
|
307
|
-
if (props.prefix) {
|
|
308
|
-
formatted = props.prefix + formatted;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (props.suffix) {
|
|
312
|
-
formatted = formatted + props.suffix;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return formatted;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
return numeric.toString();
|
|
319
|
-
},
|
|
320
|
-
[getOptions, _locale, props.format, props.prefix, props.suffix]
|
|
321
|
-
);
|
|
322
|
-
|
|
323
|
-
const parseValue = React.useCallback(
|
|
324
|
-
(text: string): number | string | null => {
|
|
325
|
-
if (!text) return null;
|
|
326
|
-
|
|
327
|
-
let filteredText = text;
|
|
328
|
-
|
|
329
|
-
if (_suffix.current) {
|
|
330
|
-
filteredText = filteredText.replace(_suffix.current, "");
|
|
331
|
-
}
|
|
332
|
-
if (_prefix.current) {
|
|
333
|
-
filteredText = filteredText.replace(_prefix.current, "");
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
filteredText = filteredText
|
|
337
|
-
.trim()
|
|
338
|
-
.replace(/\s/g, "")
|
|
339
|
-
.replace(_currency.current!, "")
|
|
340
|
-
.replace(_group.current!, "")
|
|
341
|
-
.replace(_minusSign.current!, "-")
|
|
342
|
-
.replace(_decimal.current!, ".")
|
|
343
|
-
.replace(_numeral.current!, (d: string) =>
|
|
344
|
-
String(_index.current(d) ?? "")
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
if (!filteredText) return null;
|
|
348
|
-
|
|
349
|
-
if (filteredText === "-") {
|
|
350
|
-
return "-";
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const parsedValue = +filteredText;
|
|
354
|
-
|
|
355
|
-
return Number.isNaN(parsedValue) ? null : parsedValue;
|
|
356
|
-
},
|
|
357
|
-
[]
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
const addWithPrecision = (base: number, increment: number, precision = 10) =>
|
|
361
|
-
Math.round((base + increment) * precision) / precision;
|
|
362
|
-
|
|
363
|
-
const clearTimer = () => {
|
|
364
|
-
if (timer.current != null) {
|
|
365
|
-
window.clearInterval(timer.current);
|
|
366
|
-
timer.current = undefined;
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
const allowMinusSign = () =>
|
|
371
|
-
props.min == null || props.min < 0;
|
|
372
|
-
|
|
373
|
-
const isMinusSign = (ch: string) => {
|
|
374
|
-
if ((_minusSign.current && _minusSign.current.test(ch)) || ch === "-") {
|
|
375
|
-
_minusSign.current && (_minusSign.current.lastIndex = 0);
|
|
376
|
-
return true;
|
|
377
|
-
}
|
|
378
|
-
return false;
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
const isDecimalMode = () => props.mode === "decimal";
|
|
382
|
-
|
|
383
|
-
const isFloat = (val: number) => {
|
|
384
|
-
const formatter = new Intl.NumberFormat(_locale, getOptions());
|
|
385
|
-
const parsed = parseValue(formatter.format(val));
|
|
386
|
-
if (parsed === null || typeof parsed !== "number") {
|
|
387
|
-
return false;
|
|
388
|
-
}
|
|
389
|
-
return parsed % 1 !== 0;
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
const replaceDecimalSeparator = (val: number | string): string | number => {
|
|
393
|
-
if (typeof val === "number" && isFloat(val)) {
|
|
394
|
-
return val.toString().replace(/\.(?=[^.]*$)/, _decimalSeparator.current);
|
|
395
|
-
}
|
|
396
|
-
return val;
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
const isDecimalSign = (ch: string) => {
|
|
400
|
-
if (_decimal.current && (_decimal.current.test(ch) || isFloat(Number(ch)))) {
|
|
401
|
-
_decimal.current.lastIndex = 0;
|
|
402
|
-
return true;
|
|
403
|
-
}
|
|
404
|
-
return false;
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
const getDecimalCharIndexes = (val: string) => {
|
|
408
|
-
let decimalCharIndex = -1;
|
|
409
|
-
let decimalCharIndexWithoutPrefix = -1;
|
|
410
|
-
|
|
411
|
-
if (_decimal.current) {
|
|
412
|
-
decimalCharIndex = val.search(_decimal.current);
|
|
413
|
-
_decimal.current.lastIndex = 0;
|
|
414
|
-
|
|
415
|
-
let filteredVal = val;
|
|
416
|
-
if (_prefix.current) {
|
|
417
|
-
filteredVal = filteredVal.replace(_prefix.current, "");
|
|
418
|
-
}
|
|
419
|
-
filteredVal = filteredVal.trim().replace(/\s/g, "").replace(_currency.current!, "");
|
|
420
|
-
|
|
421
|
-
decimalCharIndexWithoutPrefix = filteredVal.search(_decimal.current);
|
|
422
|
-
_decimal.current.lastIndex = 0;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return { decimalCharIndex, decimalCharIndexWithoutPrefix };
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
const getCharIndexes = (val: string) => {
|
|
429
|
-
let decimalCharIndex = -1;
|
|
430
|
-
let minusCharIndex = -1;
|
|
431
|
-
let suffixCharIndex = -1;
|
|
432
|
-
let currencyCharIndex = -1;
|
|
433
|
-
|
|
434
|
-
if (_decimal.current) {
|
|
435
|
-
decimalCharIndex = val.search(_decimal.current);
|
|
436
|
-
_decimal.current.lastIndex = 0;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (_minusSign.current) {
|
|
440
|
-
minusCharIndex = val.search(_minusSign.current);
|
|
441
|
-
_minusSign.current.lastIndex = 0;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (_suffix.current) {
|
|
445
|
-
suffixCharIndex = val.search(_suffix.current);
|
|
446
|
-
_suffix.current.lastIndex = 0;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (_currency.current) {
|
|
450
|
-
currencyCharIndex = val.search(_currency.current);
|
|
451
|
-
if (currencyCharIndex === 0 && prefixChar.current && prefixChar.current.length > 1) {
|
|
452
|
-
currencyCharIndex = prefixChar.current.trim().length;
|
|
453
|
-
}
|
|
454
|
-
_currency.current.lastIndex = 0;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return { decimalCharIndex, minusCharIndex, suffixCharIndex, currencyCharIndex };
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
const resetRegex = () => {
|
|
461
|
-
if (_numeral.current) _numeral.current.lastIndex = 0;
|
|
462
|
-
if (_decimal.current) _decimal.current.lastIndex = 0;
|
|
463
|
-
if (_group.current) _group.current.lastIndex = 0;
|
|
464
|
-
if (_minusSign.current) _minusSign.current.lastIndex = 0;
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
const isNumeralChar = (ch: string) => {
|
|
468
|
-
if (
|
|
469
|
-
ch.length === 1 &&
|
|
470
|
-
((_numeral.current && _numeral.current.test(ch)) ||
|
|
471
|
-
(_decimal.current && _decimal.current.test(ch)) ||
|
|
472
|
-
(_group.current && _group.current.test(ch)) ||
|
|
473
|
-
(_minusSign.current && _minusSign.current.test(ch)))
|
|
474
|
-
) {
|
|
475
|
-
resetRegex();
|
|
476
|
-
return true;
|
|
477
|
-
}
|
|
478
|
-
return false;
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
const evaluateEmpty = (newValue: number | null | string | undefined) => {
|
|
482
|
-
if ((newValue == null || newValue === "") && !props.allowEmpty) {
|
|
483
|
-
return props.min ?? 0;
|
|
484
|
-
}
|
|
485
|
-
return newValue;
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
const validateValueByLimit = (value: number | null | string): number | null => {
|
|
489
|
-
if (value === "-" || value === null || value === "") {
|
|
490
|
-
return null;
|
|
491
|
-
}
|
|
492
|
-
let num = typeof value === "number" ? value : Number(value);
|
|
493
|
-
if (Number.isNaN(num)) {
|
|
494
|
-
return null;
|
|
495
|
-
}
|
|
496
|
-
if (props.min != null && num < props.min) num = props.min;
|
|
497
|
-
if (props.max != null && num > props.max) num = props.max;
|
|
498
|
-
return num;
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
const validateValue = (value: number | null | string): number | null => {
|
|
502
|
-
if (value === "-") return null;
|
|
503
|
-
return validateValueByLimit(value);
|
|
504
|
-
};
|
|
505
|
-
|
|
506
|
-
const formattedValue = (val: number | null | string | undefined): string => {
|
|
507
|
-
const newVal = evaluateEmpty(val);
|
|
508
|
-
return formatValue(newVal as any);
|
|
509
|
-
};
|
|
510
|
-
|
|
511
|
-
const updateModel = (
|
|
512
|
-
event: React.SyntheticEvent<any> | null,
|
|
513
|
-
value: number | null
|
|
514
|
-
) => {
|
|
515
|
-
const finalValue = value;
|
|
516
|
-
|
|
517
|
-
if (props.onValueChange) {
|
|
518
|
-
props.onValueChange({
|
|
519
|
-
originalEvent: event,
|
|
520
|
-
value: finalValue,
|
|
521
|
-
stopPropagation() {
|
|
522
|
-
event?.stopPropagation();
|
|
523
|
-
},
|
|
524
|
-
preventDefault() {
|
|
525
|
-
event?.preventDefault();
|
|
526
|
-
},
|
|
527
|
-
target: {
|
|
528
|
-
name: props.name ?? null,
|
|
529
|
-
id: props.id ?? null,
|
|
530
|
-
value: finalValue,
|
|
531
|
-
},
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
if (props.onChange && event) {
|
|
536
|
-
props.onChange({ originalEvent: event, value: finalValue });
|
|
537
|
-
}
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
const handleOnChange = (
|
|
541
|
-
event: React.SyntheticEvent<any>,
|
|
542
|
-
currentValue: string,
|
|
543
|
-
newValue: number | null
|
|
544
|
-
) => {
|
|
545
|
-
if (!props.onChange) return;
|
|
546
|
-
const parsedCurrent =
|
|
547
|
-
typeof currentValue === "string" ? (parseValue(currentValue) as number | null) : null;
|
|
548
|
-
const changed = newValue !== parsedCurrent;
|
|
549
|
-
if (changed) {
|
|
550
|
-
props.onChange({ originalEvent: event, value: newValue });
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
|
|
554
|
-
const concatValues = (val1: string, val2: string): string => {
|
|
555
|
-
if (val1 && val2) {
|
|
556
|
-
const decimalCharIndex = val2.search(_decimal.current!);
|
|
557
|
-
_decimal.current!.lastIndex = 0;
|
|
558
|
-
|
|
559
|
-
const newVal1 = replaceDecimalSeparator(val1) as string;
|
|
560
|
-
const base = newVal1.split(_decimal.current!)[0].replace(_suffix.current!, "").trim();
|
|
561
|
-
|
|
562
|
-
return decimalCharIndex !== -1 ? base + val2.slice(decimalCharIndex) : val1;
|
|
563
|
-
}
|
|
564
|
-
return val1;
|
|
565
|
-
};
|
|
566
|
-
|
|
567
|
-
const getDecimalLength = (value: string): number => {
|
|
568
|
-
if (value) {
|
|
569
|
-
const valueSplit = value.split(_decimal.current!);
|
|
570
|
-
if (valueSplit.length === 2) {
|
|
571
|
-
return valueSplit[1].replace(_suffix.current!, "").length;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
return 0;
|
|
575
|
-
};
|
|
576
|
-
|
|
577
|
-
const deleteRange = (value: string, start: number, end: number): string => {
|
|
578
|
-
if (end - start === value.length) {
|
|
579
|
-
return "";
|
|
580
|
-
} else if (start === 0) {
|
|
581
|
-
return value.slice(end);
|
|
582
|
-
} else if (end === value.length) {
|
|
583
|
-
return value.slice(0, start);
|
|
584
|
-
}
|
|
585
|
-
return value.slice(0, start) + value.slice(end);
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
const replaceSuffix = (value: string) =>
|
|
589
|
-
value
|
|
590
|
-
? value
|
|
591
|
-
.replace(_suffix.current!, "")
|
|
592
|
-
.trim()
|
|
593
|
-
.replace(/\s/g, "")
|
|
594
|
-
.replace(_currency.current!, "")
|
|
595
|
-
: value;
|
|
596
|
-
|
|
597
|
-
const insertText = (value: string, text: string, start: number, end: number): string => {
|
|
598
|
-
const textSplit = isDecimalSign(text) ? text : text.split(_decimal.current!);
|
|
599
|
-
|
|
600
|
-
if (textSplit.length === 2) {
|
|
601
|
-
const local = value.slice(start, end);
|
|
602
|
-
const decimalCharIndex = local.search(_decimal.current!);
|
|
603
|
-
_decimal.current!.lastIndex = 0;
|
|
604
|
-
|
|
605
|
-
return decimalCharIndex > 0
|
|
606
|
-
? value.slice(0, start) + formatValue(text as any) + replaceSuffix(value).slice(end)
|
|
607
|
-
: value || formatValue(text as any);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
if (isDecimalSign(text) && value.length === 0) {
|
|
611
|
-
return formatValue("0." as any);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
if (end - start === value.length) {
|
|
615
|
-
return formatValue(text as any);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
if (start === 0) {
|
|
619
|
-
const suffix = /[A-Za-z]$/.test(value[end]) ? end - 1 : end;
|
|
620
|
-
return text + value.slice(suffix);
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
if (end === value.length) {
|
|
624
|
-
return value.slice(0, start) + text;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
const selectionValue = value.slice(start, end);
|
|
628
|
-
const space = /\s$/.test(selectionValue) ? " " : "";
|
|
629
|
-
return value.slice(0, start) + text + space + value.slice(end);
|
|
630
|
-
};
|
|
631
|
-
|
|
632
|
-
const evaluateEmptyForUpdate = (
|
|
633
|
-
newValue: number | null | string | undefined
|
|
634
|
-
): number | null | string | undefined => evaluateEmpty(newValue);
|
|
635
|
-
|
|
636
|
-
const updateInput = (
|
|
637
|
-
value: number | null,
|
|
638
|
-
insertedValueStr: string | null,
|
|
639
|
-
operation: string,
|
|
640
|
-
valueStr?: string | null
|
|
641
|
-
) => {
|
|
642
|
-
insertedValueStr = insertedValueStr || "";
|
|
643
|
-
|
|
644
|
-
const inputEl = inputRef.current;
|
|
645
|
-
if (!inputEl) return;
|
|
646
|
-
|
|
647
|
-
const inputValue = inputEl.value;
|
|
648
|
-
let newValue = formatValue(value);
|
|
649
|
-
|
|
650
|
-
const currentLength = inputValue.length;
|
|
651
|
-
|
|
652
|
-
if (newValue !== valueStr && valueStr != null) {
|
|
653
|
-
newValue = concatValues(newValue, valueStr);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
if (currentLength === 0) {
|
|
657
|
-
inputEl.value = newValue;
|
|
658
|
-
inputEl.setSelectionRange(0, 0);
|
|
659
|
-
|
|
660
|
-
const index = initCursor();
|
|
661
|
-
const selectionEnd = index + insertedValueStr.length + (isDecimalSign(insertedValueStr) ? 1 : 0);
|
|
662
|
-
inputEl.setSelectionRange(selectionEnd, selectionEnd);
|
|
663
|
-
} else {
|
|
664
|
-
let selectionStart = inputEl.selectionStart ?? 0;
|
|
665
|
-
let selectionEnd = inputEl.selectionEnd ?? 0;
|
|
666
|
-
|
|
667
|
-
if (props.maxLength && props.maxLength < newValue.length) {
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
inputEl.value = newValue;
|
|
672
|
-
const newLength = newValue.length;
|
|
673
|
-
|
|
674
|
-
if (operation === "range-insert") {
|
|
675
|
-
const startValue = parseValue((inputValue || "").slice(0, selectionStart));
|
|
676
|
-
const startValueStr = startValue != null ? String(startValue) : "";
|
|
677
|
-
const startExpr = startValueStr.split("").join("(" + groupChar.current + ")?");
|
|
678
|
-
const sRegex = new RegExp(startExpr, "g");
|
|
679
|
-
sRegex.test(newValue);
|
|
680
|
-
|
|
681
|
-
const tExpr = insertedValueStr.split("").join("(" + groupChar.current + ")?");
|
|
682
|
-
const tRegex = new RegExp(tExpr, "g");
|
|
683
|
-
tRegex.test(newValue.slice(sRegex.lastIndex));
|
|
684
|
-
|
|
685
|
-
selectionEnd = sRegex.lastIndex + tRegex.lastIndex;
|
|
686
|
-
inputEl.setSelectionRange(selectionEnd, selectionEnd);
|
|
687
|
-
} else if (newLength === currentLength) {
|
|
688
|
-
if (operation === "insert" || operation === "delete-back-single") {
|
|
689
|
-
let newSelectionEnd = selectionEnd;
|
|
690
|
-
if (insertedValueStr === "0") {
|
|
691
|
-
newSelectionEnd = selectionEnd + 1;
|
|
692
|
-
} else {
|
|
693
|
-
newSelectionEnd =
|
|
694
|
-
newSelectionEnd + Number(isDecimalSign(value as any) || isDecimalSign(insertedValueStr));
|
|
695
|
-
}
|
|
696
|
-
inputEl.setSelectionRange(newSelectionEnd, newSelectionEnd);
|
|
697
|
-
} else if (operation === "delete-single") {
|
|
698
|
-
inputEl.setSelectionRange(selectionEnd - 1, selectionEnd - 1);
|
|
699
|
-
} else if (operation === "delete-range" || operation === "spin") {
|
|
700
|
-
inputEl.setSelectionRange(selectionEnd, selectionEnd);
|
|
701
|
-
}
|
|
702
|
-
} else if (operation === "delete-back-single") {
|
|
703
|
-
const prevChar = inputValue.charAt(selectionEnd - 1);
|
|
704
|
-
const nextChar = inputValue.charAt(selectionEnd);
|
|
705
|
-
const diff = currentLength - newLength;
|
|
706
|
-
const isGroupChar = _group.current!.test(nextChar);
|
|
707
|
-
if (isGroupChar && diff === 1) {
|
|
708
|
-
selectionEnd = selectionEnd + 1;
|
|
709
|
-
} else if (!isGroupChar && isNumeralChar(prevChar)) {
|
|
710
|
-
selectionEnd = selectionEnd + (-1 * diff + 1);
|
|
711
|
-
}
|
|
712
|
-
_group.current!.lastIndex = 0;
|
|
713
|
-
inputEl.setSelectionRange(selectionEnd, selectionEnd);
|
|
714
|
-
} else if (inputValue === "-" && operation === "insert") {
|
|
715
|
-
inputEl.setSelectionRange(0, 0);
|
|
716
|
-
const idx = initCursor();
|
|
717
|
-
const end = idx + insertedValueStr.length + 1;
|
|
718
|
-
inputEl.setSelectionRange(end, end);
|
|
719
|
-
} else {
|
|
720
|
-
selectionEnd = selectionEnd + (newLength - currentLength);
|
|
721
|
-
inputEl.setSelectionRange(selectionEnd, selectionEnd);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
inputEl.setAttribute("aria-valuenow", value == null ? "" : String(value));
|
|
726
|
-
};
|
|
727
|
-
|
|
728
|
-
const updateInputValue = (newValue: number | null) => {
|
|
729
|
-
const evaluated = evaluateEmptyForUpdate(newValue) as number | null;
|
|
730
|
-
const inputEl = inputRef.current;
|
|
731
|
-
if (!inputEl) return;
|
|
732
|
-
|
|
733
|
-
const current = inputEl.value;
|
|
734
|
-
const formatted = formattedValue(evaluated);
|
|
735
|
-
|
|
736
|
-
if (current !== formatted) {
|
|
737
|
-
inputEl.value = formatted;
|
|
738
|
-
inputEl.setAttribute("aria-valuenow", evaluated == null ? "" : String(evaluated));
|
|
739
|
-
}
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
const isValueChanged = (currentValue: string, newValue: number | null) => {
|
|
743
|
-
if (newValue == null && currentValue != null) return true;
|
|
744
|
-
if (newValue != null) {
|
|
745
|
-
const parsedCurrent =
|
|
746
|
-
typeof currentValue === "string" ? (parseValue(currentValue) as number | null) : null;
|
|
747
|
-
return newValue !== parsedCurrent;
|
|
748
|
-
}
|
|
749
|
-
return false;
|
|
750
|
-
};
|
|
751
|
-
|
|
752
|
-
const updateValue = (
|
|
753
|
-
event: React.SyntheticEvent<any>,
|
|
754
|
-
valueStr: string | null,
|
|
755
|
-
insertedValueStr: string | null,
|
|
756
|
-
operation: string
|
|
757
|
-
) => {
|
|
758
|
-
const inputEl = inputRef.current;
|
|
759
|
-
if (!inputEl) return;
|
|
760
|
-
|
|
761
|
-
const currentValue = inputEl.value;
|
|
762
|
-
|
|
763
|
-
if (valueStr != null) {
|
|
764
|
-
const parsed = parseValue(valueStr);
|
|
765
|
-
const newValue = evaluateEmpty(parsed) as number | null;
|
|
766
|
-
const limited = validateValueByLimit(parsed as any);
|
|
767
|
-
|
|
768
|
-
updateInput(limited, insertedValueStr, operation, valueStr);
|
|
769
|
-
if (
|
|
770
|
-
event &&
|
|
771
|
-
typeof currentValue === "string" &&
|
|
772
|
-
typeof limited === "number" &&
|
|
773
|
-
isValueChanged(currentValue, limited)
|
|
774
|
-
) {
|
|
775
|
-
handleOnChange(event, currentValue, limited);
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
// update model immediately for spin/insert/delete etc
|
|
779
|
-
updateModel(event, limited);
|
|
780
|
-
}
|
|
781
|
-
};
|
|
782
|
-
|
|
783
|
-
const spin = (event: React.SyntheticEvent<any>, dir: 1 | -1) => {
|
|
784
|
-
const inputEl = inputRef.current;
|
|
785
|
-
if (!inputEl) return;
|
|
786
|
-
|
|
787
|
-
const step = (props.step ?? 1) * dir;
|
|
788
|
-
const currentValue = (parseValue(inputEl.value) as number | null) ?? 0;
|
|
789
|
-
const newValue = validateValue(addWithPrecision(currentValue, step));
|
|
790
|
-
if (newValue == null) return;
|
|
791
|
-
|
|
792
|
-
if (props.maxLength && props.maxLength < formatValue(newValue).length) {
|
|
793
|
-
return;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
// onChange before onValueChange, like Prime
|
|
797
|
-
handleOnChange(event as any, inputEl.value, newValue);
|
|
798
|
-
updateInput(newValue, null, "spin");
|
|
799
|
-
updateModel(event as any, newValue);
|
|
800
|
-
};
|
|
801
|
-
|
|
802
|
-
const _repeat = (
|
|
803
|
-
event: React.SyntheticEvent<any>,
|
|
804
|
-
interval: number | null,
|
|
805
|
-
dir: 1 | -1
|
|
806
|
-
) => {
|
|
807
|
-
const i = interval ?? 500;
|
|
808
|
-
clearTimer();
|
|
809
|
-
timer.current = window.setTimeout(() => {
|
|
810
|
-
_repeat(event, 40, dir);
|
|
811
|
-
}, i) as any;
|
|
812
|
-
spin(event, dir);
|
|
813
|
-
};
|
|
814
|
-
|
|
815
|
-
const insert = (
|
|
816
|
-
event: React.KeyboardEvent<HTMLInputElement> | React.SyntheticEvent<any>,
|
|
817
|
-
text: string,
|
|
818
|
-
sign: { isDecimalSign: boolean; isMinusSign: boolean } = {
|
|
819
|
-
isDecimalSign: false,
|
|
820
|
-
isMinusSign: false,
|
|
821
|
-
}
|
|
822
|
-
) => {
|
|
823
|
-
const inputEl = inputRef.current;
|
|
824
|
-
if (!inputEl) return;
|
|
825
|
-
|
|
826
|
-
const minusCharIndexOnText = text.search(_minusSign.current!);
|
|
827
|
-
_minusSign.current!.lastIndex = 0;
|
|
828
|
-
|
|
829
|
-
if (!allowMinusSign() && minusCharIndexOnText !== -1) {
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
let selectionStart = inputEl.selectionStart ?? 0;
|
|
834
|
-
let selectionEnd = inputEl.selectionEnd ?? 0;
|
|
835
|
-
const inputValue = inputEl.value.trim();
|
|
836
|
-
const { decimalCharIndex, minusCharIndex, suffixCharIndex, currencyCharIndex } =
|
|
837
|
-
getCharIndexes(inputValue);
|
|
838
|
-
const maxFractionDigits = numberFormat.current?.resolvedOptions().maximumFractionDigits ?? 0;
|
|
839
|
-
const hasBoundOrAffix = !!(props.min || props.max || props.suffix || props.prefix);
|
|
840
|
-
let newValueStr: string | null = null;
|
|
841
|
-
|
|
842
|
-
if (sign.isMinusSign) {
|
|
843
|
-
const isNewMinusSign = minusCharIndex === -1;
|
|
844
|
-
if (selectionStart === 0 || selectionStart === currencyCharIndex + 1) {
|
|
845
|
-
newValueStr = inputValue;
|
|
846
|
-
if (isNewMinusSign || selectionEnd !== 0) {
|
|
847
|
-
newValueStr = insertText(inputValue, text, 0, selectionEnd);
|
|
848
|
-
}
|
|
849
|
-
updateValue(event as any, newValueStr, text, "insert");
|
|
850
|
-
}
|
|
851
|
-
} else if (sign.isDecimalSign) {
|
|
852
|
-
if (decimalCharIndex > 0 && selectionStart === decimalCharIndex) {
|
|
853
|
-
updateValue(event as any, inputValue, text, "insert");
|
|
854
|
-
} else if (decimalCharIndex > selectionStart && decimalCharIndex < selectionEnd) {
|
|
855
|
-
newValueStr = insertText(inputValue, text, selectionStart, selectionEnd);
|
|
856
|
-
updateValue(event as any, newValueStr, text, "insert");
|
|
857
|
-
} else if (decimalCharIndex === -1 && (maxFractionDigits || props.maxFractionDigits)) {
|
|
858
|
-
const allowedDecimal =
|
|
859
|
-
inputMode !== "numeric" || (inputMode === "numeric" && hasBoundOrAffix);
|
|
860
|
-
if (allowedDecimal) {
|
|
861
|
-
newValueStr = insertText(inputValue, text, selectionStart, selectionEnd);
|
|
862
|
-
updateValue(event as any, newValueStr, text, "insert");
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
} else {
|
|
866
|
-
const operation = selectionStart !== selectionEnd ? "range-insert" : "insert";
|
|
867
|
-
if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) {
|
|
868
|
-
if (selectionStart + text.length - (decimalCharIndex + 1) <= maxFractionDigits) {
|
|
869
|
-
const charIndex =
|
|
870
|
-
currencyCharIndex >= selectionStart
|
|
871
|
-
? currencyCharIndex - 1
|
|
872
|
-
: suffixCharIndex >= selectionStart
|
|
873
|
-
? suffixCharIndex
|
|
874
|
-
: inputValue.length;
|
|
875
|
-
newValueStr =
|
|
876
|
-
inputValue.slice(0, selectionStart) +
|
|
877
|
-
text +
|
|
878
|
-
inputValue.slice(selectionStart + text.length, charIndex) +
|
|
879
|
-
inputValue.slice(charIndex);
|
|
880
|
-
updateValue(event as any, newValueStr, text, operation);
|
|
881
|
-
}
|
|
882
|
-
} else {
|
|
883
|
-
newValueStr = insertText(inputValue, text, selectionStart, selectionEnd);
|
|
884
|
-
updateValue(event as any, newValueStr, text, operation);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
};
|
|
888
|
-
|
|
889
|
-
const initCursor = () => {
|
|
890
|
-
const inputEl = inputRef.current;
|
|
891
|
-
if (!inputEl) return 0;
|
|
892
|
-
|
|
893
|
-
let selectionStart = inputEl.selectionStart ?? 0;
|
|
894
|
-
let inputValue = inputEl.value;
|
|
895
|
-
const valueLength = inputValue.length;
|
|
896
|
-
let index: number | null = null;
|
|
897
|
-
|
|
898
|
-
const prefixLength = (prefixChar.current || "").length;
|
|
899
|
-
inputValue = inputValue.replace(_prefix.current!, "");
|
|
900
|
-
selectionStart = selectionStart - prefixLength;
|
|
901
|
-
|
|
902
|
-
let ch = inputValue.charAt(selectionStart);
|
|
903
|
-
if (isNumeralChar(ch)) {
|
|
904
|
-
return selectionStart + prefixLength;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
// search left
|
|
908
|
-
let i = selectionStart - 1;
|
|
909
|
-
while (i >= 0) {
|
|
910
|
-
ch = inputValue.charAt(i);
|
|
911
|
-
if (isNumeralChar(ch)) {
|
|
912
|
-
index = i + prefixLength;
|
|
913
|
-
break;
|
|
914
|
-
}
|
|
915
|
-
i--;
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
if (index != null) {
|
|
919
|
-
inputEl.setSelectionRange(index + 1, index + 1);
|
|
920
|
-
} else {
|
|
921
|
-
i = selectionStart;
|
|
922
|
-
while (i < valueLength) {
|
|
923
|
-
ch = inputValue.charAt(i);
|
|
924
|
-
if (isNumeralChar(ch)) {
|
|
925
|
-
index = i + prefixLength;
|
|
926
|
-
break;
|
|
927
|
-
}
|
|
928
|
-
i++;
|
|
929
|
-
}
|
|
930
|
-
if (index != null) {
|
|
931
|
-
inputEl.setSelectionRange(index, index);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
return index ?? 0;
|
|
936
|
-
};
|
|
937
|
-
|
|
938
|
-
const onInputPointerDown = () => {
|
|
939
|
-
isFocusedByClick.current = true;
|
|
940
|
-
};
|
|
941
|
-
|
|
942
|
-
const onInputClick = () => {
|
|
943
|
-
initCursor();
|
|
944
|
-
};
|
|
945
|
-
|
|
946
|
-
const onInput = (event: React.FormEvent<HTMLInputElement>) => {
|
|
947
|
-
if (props.disabled || props.readOnly) return;
|
|
948
|
-
|
|
949
|
-
if (utilsIsSpecialChar.current) {
|
|
950
|
-
event.currentTarget.value = lastValue.current;
|
|
951
|
-
utilsIsSpecialChar.current = false;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// Chrome accent-dead fix is in nativeEvent.data; we can skip deep check here
|
|
955
|
-
};
|
|
956
|
-
|
|
957
|
-
// track special char for alt/ctrl/meta + keys
|
|
958
|
-
const utilsIsSpecialChar = React.useRef(false);
|
|
959
|
-
|
|
960
|
-
const onInputAndroidKey = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
961
|
-
const isAndroid = /Android/i.test(navigator.userAgent);
|
|
962
|
-
if (!isAndroid || props.disabled || props.readOnly) return;
|
|
963
|
-
|
|
964
|
-
props.onKeyUp?.(event);
|
|
965
|
-
|
|
966
|
-
if (event.defaultPrevented) return;
|
|
967
|
-
|
|
968
|
-
const code = event.which || event.keyCode;
|
|
969
|
-
if (code !== 13) {
|
|
970
|
-
event.preventDefault();
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
const ch = String.fromCharCode(code);
|
|
974
|
-
const decimal = isDecimalSign(ch);
|
|
975
|
-
const minus = isMinusSign(ch);
|
|
976
|
-
|
|
977
|
-
if ((code >= 48 && code <= 57) || minus || decimal) {
|
|
978
|
-
insert(event, ch, { isDecimalSign: decimal, isMinusSign: minus });
|
|
979
|
-
} else {
|
|
980
|
-
const inputVal = (event.target as HTMLInputElement).value;
|
|
981
|
-
updateValue(event, inputVal, null, "delete-single");
|
|
982
|
-
}
|
|
983
|
-
};
|
|
984
|
-
|
|
985
|
-
const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
986
|
-
if (props.disabled || props.readOnly) return;
|
|
987
|
-
|
|
988
|
-
if (event.altKey || event.ctrlKey || event.metaKey) {
|
|
989
|
-
// cut is treated as normal char
|
|
990
|
-
if (event.key.toLowerCase() === "x" && (event.ctrlKey || event.metaKey)) {
|
|
991
|
-
utilsIsSpecialChar.current = false;
|
|
992
|
-
} else {
|
|
993
|
-
utilsIsSpecialChar.current = true;
|
|
994
|
-
}
|
|
995
|
-
return;
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
props.onKeyDown?.(event);
|
|
999
|
-
|
|
1000
|
-
if (event.defaultPrevented) return;
|
|
1001
|
-
|
|
1002
|
-
const inputEl = event.currentTarget;
|
|
1003
|
-
lastValue.current = inputEl.value;
|
|
1004
|
-
|
|
1005
|
-
const isAndroid = /Android/i.test(navigator.userAgent);
|
|
1006
|
-
if (isAndroid) return;
|
|
1007
|
-
|
|
1008
|
-
let selectionStart = inputEl.selectionStart ?? 0;
|
|
1009
|
-
let selectionEnd = inputEl.selectionEnd ?? 0;
|
|
1010
|
-
const inputValue = inputEl.value;
|
|
1011
|
-
let newValueStr: string | null = null;
|
|
1012
|
-
|
|
1013
|
-
switch (event.code) {
|
|
1014
|
-
case "ArrowUp":
|
|
1015
|
-
spin(event, 1);
|
|
1016
|
-
event.preventDefault();
|
|
1017
|
-
break;
|
|
1018
|
-
case "ArrowDown":
|
|
1019
|
-
spin(event, -1);
|
|
1020
|
-
event.preventDefault();
|
|
1021
|
-
break;
|
|
1022
|
-
case "ArrowLeft": {
|
|
1023
|
-
const charPrev = inputValue.charAt(selectionStart - 1);
|
|
1024
|
-
if (!isNumeralChar(charPrev)) {
|
|
1025
|
-
event.preventDefault();
|
|
1026
|
-
}
|
|
1027
|
-
break;
|
|
1028
|
-
}
|
|
1029
|
-
case "ArrowRight": {
|
|
1030
|
-
const charNext = inputValue.charAt(selectionStart);
|
|
1031
|
-
if (!isNumeralChar(charNext)) {
|
|
1032
|
-
event.preventDefault();
|
|
1033
|
-
}
|
|
1034
|
-
break;
|
|
1035
|
-
}
|
|
1036
|
-
case "Tab":
|
|
1037
|
-
case "Enter":
|
|
1038
|
-
case "NumpadEnter": {
|
|
1039
|
-
const parsedVal = validateValue(parseValue(inputValue) as any);
|
|
1040
|
-
inputRef.current!.value = formatValue(parsedVal as any);
|
|
1041
|
-
inputRef.current!.setAttribute("aria-valuenow", parsedVal == null ? "" : String(parsedVal));
|
|
1042
|
-
updateModel(event, parsedVal);
|
|
1043
|
-
break;
|
|
1044
|
-
}
|
|
1045
|
-
case "Backspace": {
|
|
1046
|
-
event.preventDefault();
|
|
1047
|
-
if (selectionStart === selectionEnd) {
|
|
1048
|
-
const deleteChar = inputValue.charAt(selectionStart - 1);
|
|
1049
|
-
if (isNumeralChar(deleteChar)) {
|
|
1050
|
-
const {
|
|
1051
|
-
decimalCharIndex,
|
|
1052
|
-
decimalCharIndexWithoutPrefix,
|
|
1053
|
-
} = getDecimalCharIndexes(inputValue);
|
|
1054
|
-
const decimalLength = getDecimalLength(inputValue);
|
|
1055
|
-
if (_group.current!.test(deleteChar)) {
|
|
1056
|
-
_group.current!.lastIndex = 0;
|
|
1057
|
-
newValueStr =
|
|
1058
|
-
inputValue.slice(0, selectionStart - 2) + inputValue.slice(selectionStart - 1);
|
|
1059
|
-
} else if (_decimal.current!.test(deleteChar)) {
|
|
1060
|
-
_decimal.current!.lastIndex = 0;
|
|
1061
|
-
if (decimalLength) {
|
|
1062
|
-
inputRef.current!.setSelectionRange(selectionStart - 1, selectionStart - 1);
|
|
1063
|
-
} else {
|
|
1064
|
-
newValueStr =
|
|
1065
|
-
inputValue.slice(0, selectionStart - 1) + inputValue.slice(selectionStart);
|
|
1066
|
-
}
|
|
1067
|
-
} else if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) {
|
|
1068
|
-
const insertedText =
|
|
1069
|
-
isDecimalMode() && (props.minFractionDigits || 0) < decimalLength ? "" : "0";
|
|
1070
|
-
newValueStr =
|
|
1071
|
-
inputValue.slice(0, selectionStart - 1) +
|
|
1072
|
-
insertedText +
|
|
1073
|
-
inputValue.slice(selectionStart);
|
|
1074
|
-
} else if (decimalCharIndexWithoutPrefix === 1) {
|
|
1075
|
-
newValueStr =
|
|
1076
|
-
inputValue.slice(0, selectionStart - 1) +
|
|
1077
|
-
"0" +
|
|
1078
|
-
inputValue.slice(selectionStart);
|
|
1079
|
-
newValueStr = (parseValue(newValueStr) as number) > 0 ? newValueStr : "";
|
|
1080
|
-
} else {
|
|
1081
|
-
newValueStr =
|
|
1082
|
-
inputValue.slice(0, selectionStart - 1) + inputValue.slice(selectionStart);
|
|
1083
|
-
}
|
|
1084
|
-
} else if (_currency.current!.test(deleteChar)) {
|
|
1085
|
-
const {
|
|
1086
|
-
minusCharIndex,
|
|
1087
|
-
currencyCharIndex,
|
|
1088
|
-
} = getCharIndexes(inputValue);
|
|
1089
|
-
if (minusCharIndex === currencyCharIndex - 1) {
|
|
1090
|
-
newValueStr =
|
|
1091
|
-
inputValue.slice(0, minusCharIndex) + inputValue.slice(selectionStart);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
updateValue(event, newValueStr, null, "delete-single");
|
|
1095
|
-
} else {
|
|
1096
|
-
newValueStr = deleteRange(inputValue, selectionStart, selectionEnd);
|
|
1097
|
-
updateValue(event, newValueStr, null, "delete-range");
|
|
1098
|
-
}
|
|
1099
|
-
break;
|
|
1100
|
-
}
|
|
1101
|
-
case "Delete": {
|
|
1102
|
-
event.preventDefault();
|
|
1103
|
-
if (selectionStart === selectionEnd) {
|
|
1104
|
-
const deleteChar = inputValue.charAt(selectionStart);
|
|
1105
|
-
const {
|
|
1106
|
-
decimalCharIndex,
|
|
1107
|
-
decimalCharIndexWithoutPrefix,
|
|
1108
|
-
} = getDecimalCharIndexes(inputValue);
|
|
1109
|
-
if (isNumeralChar(deleteChar)) {
|
|
1110
|
-
const decimalLength = getDecimalLength(inputValue);
|
|
1111
|
-
if (_group.current!.test(deleteChar)) {
|
|
1112
|
-
_group.current!.lastIndex = 0;
|
|
1113
|
-
newValueStr =
|
|
1114
|
-
inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 2);
|
|
1115
|
-
} else if (_decimal.current!.test(deleteChar)) {
|
|
1116
|
-
_decimal.current!.lastIndex = 0;
|
|
1117
|
-
if (decimalLength) {
|
|
1118
|
-
inputRef.current!.setSelectionRange(selectionStart + 1, selectionStart + 1);
|
|
1119
|
-
} else {
|
|
1120
|
-
newValueStr =
|
|
1121
|
-
inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 1);
|
|
1122
|
-
}
|
|
1123
|
-
} else if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) {
|
|
1124
|
-
const insertedText =
|
|
1125
|
-
isDecimalMode() && (props.minFractionDigits || 0) < decimalLength ? "" : "0";
|
|
1126
|
-
newValueStr =
|
|
1127
|
-
inputValue.slice(0, selectionStart) +
|
|
1128
|
-
insertedText +
|
|
1129
|
-
inputValue.slice(selectionStart + 1);
|
|
1130
|
-
} else if (decimalCharIndexWithoutPrefix === 1) {
|
|
1131
|
-
newValueStr =
|
|
1132
|
-
inputValue.slice(0, selectionStart) +
|
|
1133
|
-
"0" +
|
|
1134
|
-
inputValue.slice(selectionStart + 1);
|
|
1135
|
-
newValueStr = (parseValue(newValueStr) as number) > 0 ? newValueStr : "";
|
|
1136
|
-
} else {
|
|
1137
|
-
newValueStr =
|
|
1138
|
-
inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 1);
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
updateValue(event, newValueStr, null, "delete-back-single");
|
|
1142
|
-
} else {
|
|
1143
|
-
newValueStr = deleteRange(inputValue, selectionStart, selectionEnd);
|
|
1144
|
-
updateValue(event, newValueStr, null, "delete-range");
|
|
1145
|
-
}
|
|
1146
|
-
break;
|
|
1147
|
-
}
|
|
1148
|
-
case "End":
|
|
1149
|
-
event.preventDefault();
|
|
1150
|
-
if (props.max != null) {
|
|
1151
|
-
updateModel(event, props.max);
|
|
1152
|
-
updateInputValue(props.max);
|
|
1153
|
-
}
|
|
1154
|
-
break;
|
|
1155
|
-
case "Home":
|
|
1156
|
-
event.preventDefault();
|
|
1157
|
-
if (props.min != null) {
|
|
1158
|
-
updateModel(event, props.min);
|
|
1159
|
-
updateInputValue(props.min);
|
|
1160
|
-
}
|
|
1161
|
-
break;
|
|
1162
|
-
default: {
|
|
1163
|
-
event.preventDefault();
|
|
1164
|
-
let ch = event.key;
|
|
1165
|
-
if (!ch) break;
|
|
1166
|
-
|
|
1167
|
-
if (ch === ".") {
|
|
1168
|
-
ch = _decimalSeparator.current;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
const decimal = isDecimalSign(ch);
|
|
1172
|
-
const minus = isMinusSign(ch);
|
|
1173
|
-
if ((ch >= "0" && ch <= "9") || minus || decimal) {
|
|
1174
|
-
insert(event, ch, { isDecimalSign: decimal, isMinusSign: minus });
|
|
1175
|
-
}
|
|
1176
|
-
break;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
};
|
|
1180
|
-
|
|
1181
|
-
const onPaste = (event: React.ClipboardEvent<HTMLInputElement>) => {
|
|
1182
|
-
event.preventDefault();
|
|
1183
|
-
if (props.disabled || props.readOnly) return;
|
|
1184
|
-
|
|
1185
|
-
const data = (event.clipboardData || (window as any).clipboardData).getData("Text");
|
|
1186
|
-
if (!data) return;
|
|
1187
|
-
|
|
1188
|
-
const filteredData = parseValue(data);
|
|
1189
|
-
if (filteredData != null) {
|
|
1190
|
-
if (typeof filteredData === "number" && isFloat(filteredData)) {
|
|
1191
|
-
const formatted = formatValue(filteredData);
|
|
1192
|
-
if (inputRef.current) {
|
|
1193
|
-
inputRef.current.value = formatted;
|
|
1194
|
-
}
|
|
1195
|
-
updateModel(event, filteredData);
|
|
1196
|
-
} else {
|
|
1197
|
-
insert(event, String(filteredData));
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
};
|
|
1201
|
-
|
|
1202
|
-
const onInputFocus = (event: React.FocusEvent<HTMLInputElement>) => {
|
|
1203
|
-
setFocusedState(true);
|
|
1204
|
-
props.onFocus?.(event);
|
|
1205
|
-
|
|
1206
|
-
if ((props.suffix || props.currency || props.prefix) && inputRef.current && !isFocusedByClick.current) {
|
|
1207
|
-
const inputValue = inputRef.current.value;
|
|
1208
|
-
const prefixLength = (prefixChar.current || "").length;
|
|
1209
|
-
const suffixLength = (suffixChar.current || "").length;
|
|
1210
|
-
const end = inputValue.length === 0 ? 0 : inputValue.length - suffixLength;
|
|
1211
|
-
inputRef.current.setSelectionRange(prefixLength, end);
|
|
1212
|
-
}
|
|
1213
|
-
};
|
|
1214
|
-
|
|
1215
|
-
const onInputBlur = (event: React.FocusEvent<HTMLInputElement>) => {
|
|
1216
|
-
setFocusedState(false);
|
|
1217
|
-
isFocusedByClick.current = false;
|
|
1218
|
-
|
|
1219
|
-
if (inputRef.current) {
|
|
1220
|
-
const currentValue = inputRef.current.value;
|
|
1221
|
-
if (isValueChanged(currentValue, props.value ?? null)) {
|
|
1222
|
-
const newValue = validateValue(parseValue(currentValue) as any);
|
|
1223
|
-
updateInputValue(newValue);
|
|
1224
|
-
updateModel(event, newValue);
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
props.onBlur?.(event);
|
|
1229
|
-
};
|
|
1230
|
-
|
|
1231
|
-
const changeValue = () => {
|
|
1232
|
-
const val = validateValueByLimit(props.value as any);
|
|
1233
|
-
updateInputValue(props.format ? (val as any) : (replaceDecimalSeparator(val as any) as any));
|
|
1234
|
-
const newValue = validateValue(props.value as any);
|
|
1235
|
-
if (props.value != null && props.value !== newValue) {
|
|
1236
|
-
updateModel(null, newValue);
|
|
1237
|
-
}
|
|
1238
|
-
};
|
|
1239
|
-
|
|
1240
|
-
React.useImperativeHandle(ref, () => inputRef.current as InputRef);
|
|
1241
|
-
|
|
1242
|
-
// attach provided inputRef
|
|
1243
|
-
React.useEffect(() => {
|
|
1244
|
-
if (props.inputRef) {
|
|
1245
|
-
if (typeof props.inputRef === "function") {
|
|
1246
|
-
props.inputRef(inputRef.current);
|
|
1247
|
-
} else {
|
|
1248
|
-
(props.inputRef as React.MutableRefObject<InputRef | null>).current =
|
|
1249
|
-
inputRef.current;
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
}, [props.inputRef]);
|
|
1253
|
-
|
|
1254
|
-
// unmount cleanup
|
|
1255
|
-
React.useEffect(
|
|
1256
|
-
() => () => {
|
|
1257
|
-
clearTimer();
|
|
1258
|
-
},
|
|
1259
|
-
[]
|
|
1260
|
-
);
|
|
1261
|
-
|
|
1262
|
-
// mount init
|
|
1263
|
-
React.useEffect(() => {
|
|
1264
|
-
constructParser();
|
|
1265
|
-
const newValue = validateValue(props.value as any);
|
|
1266
|
-
if (props.value != null && props.value !== newValue) {
|
|
1267
|
-
updateModel(null, newValue);
|
|
1268
|
-
}
|
|
1269
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1270
|
-
}, []);
|
|
1271
|
-
|
|
1272
|
-
// update on locale/options changes
|
|
1273
|
-
React.useEffect(() => {
|
|
1274
|
-
constructParser();
|
|
1275
|
-
changeValue();
|
|
1276
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1277
|
-
}, [
|
|
1278
|
-
_locale,
|
|
1279
|
-
props.locale,
|
|
1280
|
-
props.localeMatcher,
|
|
1281
|
-
props.mode,
|
|
1282
|
-
props.currency,
|
|
1283
|
-
props.currencyDisplay,
|
|
1284
|
-
props.useGrouping,
|
|
1285
|
-
props.minFractionDigits,
|
|
1286
|
-
props.maxFractionDigits,
|
|
1287
|
-
props.suffix,
|
|
1288
|
-
props.prefix,
|
|
1289
|
-
]);
|
|
1290
|
-
|
|
1291
|
-
// update on value changes
|
|
1292
|
-
React.useEffect(() => {
|
|
1293
|
-
changeValue();
|
|
1294
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1295
|
-
}, [props.value]);
|
|
1296
|
-
|
|
1297
|
-
// disable timer if disabled
|
|
1298
|
-
React.useEffect(() => {
|
|
1299
|
-
if (props.disabled) clearTimer();
|
|
1300
|
-
}, [props.disabled]);
|
|
1301
|
-
|
|
1302
|
-
// ---- render ----
|
|
1303
|
-
|
|
1304
|
-
const inputClassName = [
|
|
1305
|
-
props.inputClassName,
|
|
1306
|
-
props.invalid ? "p-invalid" : undefined,
|
|
1307
|
-
]
|
|
1308
|
-
.filter(Boolean)
|
|
1309
|
-
.join(" ");
|
|
1310
|
-
|
|
1311
|
-
const valueToRender = formattedValue(props.value ?? null);
|
|
1312
|
-
|
|
1313
|
-
// Destructure once near the top of your render/component:
|
|
1314
|
-
const {
|
|
1315
|
-
inputId,
|
|
1316
|
-
inputStyle,
|
|
1317
|
-
leadingControl,
|
|
1318
|
-
trailingControl,
|
|
1319
|
-
leadingControlClassName,
|
|
1320
|
-
trailingControlClassName,
|
|
1321
|
-
value,
|
|
1322
|
-
icon,
|
|
1323
|
-
iconGap,
|
|
1324
|
-
|
|
1325
|
-
// anything you *don’t* want to pass down can be pulled out here too
|
|
1326
|
-
// e.g. internal-only props
|
|
1327
|
-
|
|
1328
|
-
...passThroughProps // everything else goes straight to ShadcnTextVariant
|
|
1329
|
-
} = props;
|
|
1330
|
-
|
|
1331
|
-
return (
|
|
1332
|
-
//@ts-ignore
|
|
1333
|
-
<Input
|
|
1334
|
-
value={value as any}
|
|
1335
|
-
ref={inputRef}
|
|
1336
|
-
// 1. forward all “normal” input props, aria props, etc.
|
|
1337
|
-
{...passThroughProps}
|
|
1338
|
-
|
|
1339
|
-
// 2. override / shape the ones we control
|
|
1340
|
-
id={inputId ?? props.id}
|
|
1341
|
-
style={inputStyle ?? props.style}
|
|
1342
|
-
role="spinbutton"
|
|
1343
|
-
className={inputClassName || props.className}
|
|
1344
|
-
defaultValue={valueToRender}
|
|
1345
|
-
type={props.type ?? "text"}
|
|
1346
|
-
inputMode={inputMode}
|
|
1347
|
-
|
|
1348
|
-
// 3. internal event handlers (your logic wins over anything from props)
|
|
1349
|
-
onKeyDown={onInputKeyDown}
|
|
1350
|
-
onKeyPress={onInputAndroidKey}
|
|
1351
|
-
onInput={onInput}
|
|
1352
|
-
onClick={onInputClick}
|
|
1353
|
-
onPointerDown={onInputPointerDown}
|
|
1354
|
-
onBlur={onInputBlur}
|
|
1355
|
-
onFocus={onInputFocus}
|
|
1356
|
-
onPaste={onPaste}
|
|
1357
|
-
|
|
1358
|
-
// 4. controls / adornments
|
|
1359
|
-
leadingControl={leadingControl}
|
|
1360
|
-
trailingControl={trailingControl}
|
|
1361
|
-
leadingControlClassName={leadingControlClassName}
|
|
1362
|
-
trailingControlClassName={trailingControlClassName}
|
|
1363
|
-
icon={icon}
|
|
1364
|
-
iconGap={iconGap}
|
|
1365
|
-
/>
|
|
1366
|
-
);
|
|
1367
|
-
})
|
|
1368
|
-
);
|
|
1369
|
-
|
|
1370
|
-
InputNumber.displayName = "InputNumber";
|