@codefast/ui 0.3.11-canary.1 → 0.3.11

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.
Files changed (149) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +34 -14
  3. package/dist/components/accordion.d.ts +29 -10
  4. package/dist/components/accordion.js +38 -37
  5. package/dist/components/alert-dialog.d.ts +60 -24
  6. package/dist/components/alert-dialog.js +69 -72
  7. package/dist/components/alert.d.ts +27 -15
  8. package/dist/components/alert.js +29 -31
  9. package/dist/components/aspect-ratio.d.ts +8 -4
  10. package/dist/components/aspect-ratio.js +7 -5
  11. package/dist/components/avatar.d.ts +17 -6
  12. package/dist/components/avatar.js +19 -17
  13. package/dist/components/badge.d.ts +21 -14
  14. package/dist/components/badge.js +19 -22
  15. package/dist/components/breadcrumb.d.ts +35 -12
  16. package/dist/components/breadcrumb.js +48 -54
  17. package/dist/components/button-group.d.ts +29 -14
  18. package/dist/components/button-group.js +27 -32
  19. package/dist/components/button.d.ts +35 -25
  20. package/dist/components/button.js +50 -48
  21. package/dist/components/calendar.d.ts +24 -9
  22. package/dist/components/calendar.js +92 -98
  23. package/dist/components/card.d.ts +34 -10
  24. package/dist/components/card.js +39 -37
  25. package/dist/components/carousel.d.ts +55 -24
  26. package/dist/components/carousel.js +113 -130
  27. package/dist/components/chart.d.ts +72 -36
  28. package/dist/components/chart.js +154 -166
  29. package/dist/components/checkbox-cards.d.ts +15 -8
  30. package/dist/components/checkbox-cards.js +22 -25
  31. package/dist/components/checkbox-group.d.ts +16 -8
  32. package/dist/components/checkbox-group.js +19 -19
  33. package/dist/components/checkbox.d.ts +9 -4
  34. package/dist/components/checkbox.js +14 -14
  35. package/dist/components/collapsible.d.ts +14 -6
  36. package/dist/components/collapsible.js +18 -16
  37. package/dist/components/command.d.ts +55 -23
  38. package/dist/components/command.js +70 -85
  39. package/dist/components/context-menu.d.ts +72 -22
  40. package/dist/components/context-menu.js +88 -109
  41. package/dist/components/dialog.d.ts +53 -22
  42. package/dist/components/dialog.js +71 -85
  43. package/dist/components/drawer.d.ts +60 -28
  44. package/dist/components/drawer.js +59 -67
  45. package/dist/components/dropdown-menu.d.ts +73 -22
  46. package/dist/components/dropdown-menu.js +95 -112
  47. package/dist/components/empty.d.ts +37 -15
  48. package/dist/components/empty.js +43 -45
  49. package/dist/components/field.d.ts +63 -25
  50. package/dist/components/field.js +88 -104
  51. package/dist/components/form.d.ts +37 -15
  52. package/dist/components/form.js +65 -70
  53. package/dist/components/hover-card.d.ts +21 -7
  54. package/dist/components/hover-card.js +24 -24
  55. package/dist/components/input-group.d.ts +53 -28
  56. package/dist/components/input-group.js +63 -77
  57. package/dist/components/input-number.d.ts +28 -7
  58. package/dist/components/input-number.js +55 -59
  59. package/dist/components/input-otp.d.ts +24 -10
  60. package/dist/components/input-otp.js +33 -37
  61. package/dist/components/input-password.d.ts +12 -5
  62. package/dist/components/input-password.js +31 -32
  63. package/dist/components/input-search.d.ts +18 -8
  64. package/dist/components/input-search.js +43 -41
  65. package/dist/components/input.d.ts +10 -4
  66. package/dist/components/input.js +9 -7
  67. package/dist/components/item.d.ts +69 -32
  68. package/dist/components/item.js +89 -92
  69. package/dist/components/kbd.d.ts +13 -5
  70. package/dist/components/kbd.js +13 -11
  71. package/dist/components/label.d.ts +9 -4
  72. package/dist/components/label.js +9 -7
  73. package/dist/components/menubar.d.ts +80 -23
  74. package/dist/components/menubar.js +97 -118
  75. package/dist/components/native-select.d.ts +16 -6
  76. package/dist/components/native-select.js +25 -26
  77. package/dist/components/navigation-menu.d.ts +36 -12
  78. package/dist/components/navigation-menu.js +60 -75
  79. package/dist/components/pagination.d.ts +35 -14
  80. package/dist/components/pagination.js +54 -69
  81. package/dist/components/popover.d.ts +24 -8
  82. package/dist/components/popover.js +28 -28
  83. package/dist/components/progress-circle.d.ts +106 -88
  84. package/dist/components/progress-circle.js +106 -138
  85. package/dist/components/progress.d.ts +10 -4
  86. package/dist/components/progress.js +14 -14
  87. package/dist/components/radio-cards.d.ts +10 -4
  88. package/dist/components/radio-cards.js +22 -23
  89. package/dist/components/radio-group.d.ts +13 -5
  90. package/dist/components/radio-group.js +18 -16
  91. package/dist/components/radio.d.ts +12 -5
  92. package/dist/components/radio.js +10 -8
  93. package/dist/components/resizable.d.ts +18 -7
  94. package/dist/components/resizable.js +22 -22
  95. package/dist/components/scroll-area.d.ts +33 -20
  96. package/dist/components/scroll-area.js +84 -88
  97. package/dist/components/select.d.ts +50 -16
  98. package/dist/components/select.js +77 -96
  99. package/dist/components/separator.d.ts +29 -19
  100. package/dist/components/separator.js +36 -34
  101. package/dist/components/sheet.d.ts +62 -29
  102. package/dist/components/sheet.js +79 -100
  103. package/dist/components/sidebar.d.ts +160 -66
  104. package/dist/components/sidebar.js +294 -336
  105. package/dist/components/skeleton.d.ts +9 -4
  106. package/dist/components/skeleton.js +8 -6
  107. package/dist/components/slider.d.ts +13 -4
  108. package/dist/components/slider.js +34 -40
  109. package/dist/components/sonner.d.ts +10 -7
  110. package/dist/components/sonner.js +16 -14
  111. package/dist/components/spinner.d.ts +12 -5
  112. package/dist/components/spinner.js +33 -35
  113. package/dist/components/switch.d.ts +9 -4
  114. package/dist/components/switch.js +13 -11
  115. package/dist/components/table.d.ts +37 -11
  116. package/dist/components/table.js +47 -45
  117. package/dist/components/tabs.d.ts +21 -7
  118. package/dist/components/tabs.js +24 -22
  119. package/dist/components/textarea.d.ts +9 -4
  120. package/dist/components/textarea.js +8 -6
  121. package/dist/components/toggle-group.d.ts +24 -9
  122. package/dist/components/toggle-group.js +41 -42
  123. package/dist/components/toggle.d.ts +25 -17
  124. package/dist/components/toggle.js +30 -28
  125. package/dist/components/tooltip.d.ts +24 -8
  126. package/dist/components/tooltip.js +29 -32
  127. package/dist/hooks/use-animated-value.d.ts +4 -1
  128. package/dist/hooks/use-animated-value.js +57 -36
  129. package/dist/hooks/use-copy-to-clipboard.d.ts +11 -5
  130. package/dist/hooks/use-copy-to-clipboard.js +39 -20
  131. package/dist/hooks/use-is-mobile.d.ts +4 -1
  132. package/dist/hooks/use-is-mobile.js +21 -1
  133. package/dist/hooks/use-media-query.d.ts +4 -1
  134. package/dist/hooks/use-media-query.js +45 -18
  135. package/dist/hooks/use-mutation-observer.d.ts +6 -2
  136. package/dist/hooks/use-mutation-observer.js +34 -16
  137. package/dist/hooks/use-pagination.d.ts +22 -14
  138. package/dist/hooks/use-pagination.js +100 -49
  139. package/dist/index.d.ts +69 -130
  140. package/dist/index.js +11 -11
  141. package/dist/primitives/checkbox-group.d.ts +105 -83
  142. package/dist/primitives/checkbox-group.js +111 -84
  143. package/dist/primitives/input-number.d.ts +51 -40
  144. package/dist/primitives/input-number.js +483 -350
  145. package/dist/primitives/input.d.ts +48 -42
  146. package/dist/primitives/input.js +65 -43
  147. package/dist/primitives/progress-circle.d.ts +79 -48
  148. package/dist/primitives/progress-circle.js +154 -105
  149. package/package.json +6 -7
@@ -1,381 +1,514 @@
1
+ import { Field as InputField, Input, createInputScope } from "./input.js";
1
2
  import { jsx } from "react/jsx-runtime";
2
- import { composeEventHandlers } from "@radix-ui/primitive";
3
+ import { useCallback, useEffect, useMemo, useRef } from "react";
3
4
  import { createContextScope } from "@radix-ui/react-context";
4
5
  import { useControllableState } from "@radix-ui/react-use-controllable-state";
5
- import { useCallback, useEffect, useMemo, useRef } from "react";
6
- import { Field, Root, createInputScope } from "./input.js";
6
+ import { composeEventHandlers } from "@radix-ui/primitive";
7
+ //#region src/primitives/input-number.tsx
8
+ /**
9
+ * The name of the InputNumber component constant.
10
+ */
7
11
  const NUMBER_INPUT_NAME = "InputNumber";
8
- const [createInputNumberContext, createInputNumberScope] = createContextScope(NUMBER_INPUT_NAME, [
9
- createInputScope
10
- ]);
12
+ const [createInputNumberContext, createInputNumberScope] = createContextScope(NUMBER_INPUT_NAME, [createInputScope]);
11
13
  const useInputScope = createInputScope();
12
14
  const [InputNumberContextProvider, useInputNumberContext] = createInputNumberContext(NUMBER_INPUT_NAME);
13
15
  function InputNumber(numberInputProps) {
14
- const { __scopeInputNumber, ariaDecrementLabel, ariaIncrementLabel, defaultValue, formatOptions = {
15
- minimumFractionDigits: 0,
16
- style: "decimal"
17
- }, id, locale, max, min, onChange, step = 1, value: valueProperty, ...props } = numberInputProps;
18
- const inputScope = useInputScope(__scopeInputNumber);
19
- const inputRef = useRef(null);
20
- const [value, setValue] = useControllableState({
21
- defaultProp: defaultValue,
22
- onChange,
23
- prop: valueProperty
24
- });
25
- const { decimalSeparator, thousandSeparator } = useMemo(()=>getNumberFormatSeparators(locale), [
26
- locale
27
- ]);
28
- const formatValue = useCallback((inputValue)=>{
29
- if (void 0 === inputValue || Number.isNaN(inputValue)) return "";
30
- return new Intl.NumberFormat(locale, formatOptions).format(inputValue);
31
- }, [
32
- formatOptions,
33
- locale
34
- ]);
35
- const parseValue = useCallback((inputValue)=>{
36
- if ("number" == typeof inputValue) return clamp(inputValue, min, max);
37
- if ("string" != typeof inputValue) return NaN;
38
- const cleanedValue = inputValue.trim().replaceAll(/[^\d.,\-()]/g, "");
39
- if ("" === cleanedValue) return NaN;
40
- const normalizedValue = normalizeInputValue(cleanedValue, thousandSeparator, decimalSeparator);
41
- let parsedValue = Number.parseFloat(normalizedValue);
42
- if ("percent" === formatOptions.style) parsedValue /= 100;
43
- return Number.isNaN(parsedValue) ? 0 : clamp(parsedValue, min, max);
44
- }, [
45
- decimalSeparator,
46
- formatOptions.style,
47
- max,
48
- min,
49
- thousandSeparator
50
- ]);
51
- const changeNumberValue = useCallback((operation)=>{
52
- const inputElement = inputRef.current;
53
- if (!inputElement || props.disabled || props.readOnly) return;
54
- const currentValue = parseValue(inputElement.value) || 0;
55
- const newValue = clamp(operation(currentValue), min, max);
56
- inputElement.value = formatValue(newValue);
57
- setValue(newValue);
58
- }, [
59
- props.disabled,
60
- formatValue,
61
- max,
62
- min,
63
- parseValue,
64
- props.readOnly,
65
- setValue
66
- ]);
67
- const handleIncrement = useCallback(()=>{
68
- changeNumberValue((number)=>number + step);
69
- }, [
70
- changeNumberValue,
71
- step
72
- ]);
73
- const handleDecrement = useCallback(()=>{
74
- changeNumberValue((number)=>number - step);
75
- }, [
76
- changeNumberValue,
77
- step
78
- ]);
79
- const handleIncrementToMax = useCallback(()=>{
80
- changeNumberValue((number)=>max ?? number + step);
81
- }, [
82
- changeNumberValue,
83
- max,
84
- step
85
- ]);
86
- const handleDecrementToMin = useCallback(()=>{
87
- changeNumberValue((number)=>min ?? number - step);
88
- }, [
89
- changeNumberValue,
90
- min,
91
- step
92
- ]);
93
- return /*#__PURE__*/ jsx(InputNumberContextProvider, {
94
- ariaDecrementLabel: ariaDecrementLabel,
95
- ariaIncrementLabel: ariaIncrementLabel,
96
- defaultValue: defaultValue,
97
- disabled: props.disabled,
98
- formatOptions: formatOptions,
99
- formatValue: formatValue,
100
- id: id,
101
- inputRef: inputRef,
102
- max: max,
103
- min: min,
104
- parseValue: parseValue,
105
- readOnly: props.readOnly,
106
- scope: __scopeInputNumber,
107
- value: value,
108
- onChange: setValue,
109
- onDecrement: handleDecrement,
110
- onDecrementToMin: handleDecrementToMin,
111
- onIncrement: handleIncrement,
112
- onIncrementToMax: handleIncrementToMax,
113
- children: /*#__PURE__*/ jsx(Root, {
114
- ...inputScope,
115
- ...props
116
- })
117
- });
16
+ const { __scopeInputNumber, ariaDecrementLabel, ariaIncrementLabel, defaultValue, formatOptions = {
17
+ minimumFractionDigits: 0,
18
+ style: "decimal"
19
+ }, id, locale, max, min, onChange, step = 1, value: valueProperty, ...props } = numberInputProps;
20
+ /** Scope for the input component */
21
+ const inputScope = useInputScope(__scopeInputNumber);
22
+ /** Reference to the input element */
23
+ const inputRef = useRef(null);
24
+ /** Controlled or uncontrolled value state */
25
+ const [value, setValue] = useControllableState({
26
+ defaultProp: defaultValue,
27
+ onChange,
28
+ prop: valueProperty
29
+ });
30
+ /** Separators used for number formatting based on locale */
31
+ const { decimalSeparator, thousandSeparator } = useMemo(() => getNumberFormatSeparators(locale), [locale]);
32
+ /**
33
+ * Formats a number value into a string representation
34
+ * @param inputValue - The number to format
35
+ * @returns A formatted string representation of the number
36
+ */
37
+ const formatValue = useCallback((inputValue) => {
38
+ if (inputValue === void 0 || Number.isNaN(inputValue)) return "";
39
+ return new Intl.NumberFormat(locale, formatOptions).format(inputValue);
40
+ }, [formatOptions, locale]);
41
+ /**
42
+ * Parses a string or number input into a normalized number value
43
+ * @param inputValue - The value to parse
44
+ * @returns The parsed number value, clamped between min and max
45
+ */
46
+ const parseValue = useCallback((inputValue) => {
47
+ if (typeof inputValue === "number") return clamp(inputValue, min, max);
48
+ if (typeof inputValue !== "string") return NaN;
49
+ const cleanedValue = inputValue.trim().replaceAll(/[^\d.,\-()]/g, "");
50
+ if (cleanedValue === "") return NaN;
51
+ const normalizedValue = normalizeInputValue(cleanedValue, thousandSeparator, decimalSeparator);
52
+ let parsedValue = Number.parseFloat(normalizedValue);
53
+ if (formatOptions.style === "percent") parsedValue /= 100;
54
+ return Number.isNaN(parsedValue) ? 0 : clamp(parsedValue, min, max);
55
+ }, [
56
+ decimalSeparator,
57
+ formatOptions.style,
58
+ max,
59
+ min,
60
+ thousandSeparator
61
+ ]);
62
+ /**
63
+ * Changes the current value based on a provided operation
64
+ * @param operation - Function that takes the current value and returns a new value
65
+ */
66
+ const changeNumberValue = useCallback((operation) => {
67
+ const inputElement = inputRef.current;
68
+ if (!inputElement || props.disabled || props.readOnly) return;
69
+ const newValue = clamp(operation(parseValue(inputElement.value) || 0), min, max);
70
+ inputElement.value = formatValue(newValue);
71
+ setValue(newValue);
72
+ }, [
73
+ props.disabled,
74
+ formatValue,
75
+ max,
76
+ min,
77
+ parseValue,
78
+ props.readOnly,
79
+ setValue
80
+ ]);
81
+ /**
82
+ * Increments the current value by the step amount
83
+ */
84
+ const handleIncrement = useCallback(() => {
85
+ changeNumberValue((number) => number + step);
86
+ }, [changeNumberValue, step]);
87
+ /**
88
+ * Decrements the current value by the step amount
89
+ */
90
+ const handleDecrement = useCallback(() => {
91
+ changeNumberValue((number) => number - step);
92
+ }, [changeNumberValue, step]);
93
+ /**
94
+ * Sets the value to the maximum allowed
95
+ */
96
+ const handleIncrementToMax = useCallback(() => {
97
+ changeNumberValue((number) => max ?? number + step);
98
+ }, [
99
+ changeNumberValue,
100
+ max,
101
+ step
102
+ ]);
103
+ /**
104
+ * Sets the value to the minimum allowed
105
+ */
106
+ const handleDecrementToMin = useCallback(() => {
107
+ changeNumberValue((number) => min ?? number - step);
108
+ }, [
109
+ changeNumberValue,
110
+ min,
111
+ step
112
+ ]);
113
+ return /* @__PURE__ */ jsx(InputNumberContextProvider, {
114
+ ariaDecrementLabel,
115
+ ariaIncrementLabel,
116
+ defaultValue,
117
+ disabled: props.disabled,
118
+ formatOptions,
119
+ formatValue,
120
+ id,
121
+ inputRef,
122
+ max,
123
+ min,
124
+ parseValue,
125
+ readOnly: props.readOnly,
126
+ scope: __scopeInputNumber,
127
+ value,
128
+ onChange: setValue,
129
+ onDecrement: handleDecrement,
130
+ onDecrementToMin: handleDecrementToMin,
131
+ onIncrement: handleIncrement,
132
+ onIncrementToMax: handleIncrementToMax,
133
+ children: /* @__PURE__ */ jsx(Input, {
134
+ ...inputScope,
135
+ ...props
136
+ })
137
+ });
118
138
  }
139
+ /**
140
+ * The name of the InputNumberField component constant.
141
+ */
119
142
  const NUMBER_INPUT_FIELD_NAME = "InputNumberField";
120
143
  function InputNumberField({ __scopeInputNumber, onBlur, onKeyDown, ...props }) {
121
- const inputScope = useInputScope(__scopeInputNumber);
122
- const { defaultValue, disabled, formatValue, id, inputRef, max, min, onChange, onDecrement, onDecrementToMin, onIncrement, onIncrementToMax, parseValue, readOnly, step, value } = useInputNumberContext(NUMBER_INPUT_FIELD_NAME, __scopeInputNumber);
123
- const handleBlur = useCallback((event)=>{
124
- const numericValue = parseValue(event.target.value);
125
- const formattedValue = formatValue(numericValue);
126
- if (formattedValue !== event.target.value) event.target.value = formattedValue;
127
- onChange(numericValue);
128
- }, [
129
- formatValue,
130
- onChange,
131
- parseValue
132
- ]);
133
- const handleKeyDown = useCallback((event)=>{
134
- switch(event.key){
135
- case "ArrowUp":
136
- onIncrement();
137
- event.preventDefault();
138
- break;
139
- case "PageUp":
140
- onIncrementToMax();
141
- event.preventDefault();
142
- break;
143
- case "ArrowDown":
144
- onDecrement();
145
- event.preventDefault();
146
- break;
147
- case "PageDown":
148
- onDecrementToMin();
149
- event.preventDefault();
150
- break;
151
- default:
152
- break;
153
- }
154
- }, [
155
- onIncrement,
156
- onIncrementToMax,
157
- onDecrement,
158
- onDecrementToMin
159
- ]);
160
- const handleKeyDownPrevent = useCallback((event)=>{
161
- switch(event.key){
162
- case "ArrowUp":
163
- case "ArrowDown":
164
- case "ArrowLeft":
165
- case "ArrowRight":
166
- case "PageUp":
167
- case "PageDown":
168
- case "Tab":
169
- case "Escape":
170
- case "Enter":
171
- case "Backspace":
172
- case "Delete":
173
- case "Home":
174
- case "End":
175
- case ".":
176
- case ",":
177
- case "-":
178
- case "%":
179
- return;
180
- default:
181
- if (isNumberKey(event.key) || isModifierKey(event) || isFunctionKey(event.key)) return;
182
- event.preventDefault();
183
- }
184
- }, []);
185
- const handleKeyDownEnter = useCallback((event)=>{
186
- const inputElement = inputRef.current;
187
- if ("Enter" !== event.key || !inputElement) return;
188
- const numericValue = parseValue(inputElement.value);
189
- const formattedValue = formatValue(numericValue);
190
- if (formattedValue !== inputElement.value) inputElement.value = formattedValue;
191
- onChange(numericValue);
192
- }, [
193
- formatValue,
194
- inputRef,
195
- onChange,
196
- parseValue
197
- ]);
198
- const combinedKeyDownHandler = useCallback((event)=>{
199
- composeEventHandlers(onKeyDown, chain(handleKeyDownPrevent, handleKeyDown, handleKeyDownEnter))(event);
200
- }, [
201
- onKeyDown,
202
- handleKeyDown,
203
- handleKeyDownEnter,
204
- handleKeyDownPrevent
205
- ]);
206
- useEffect(()=>{
207
- const handleWheel = (event)=>{
208
- const inputElement = inputRef.current;
209
- if (!inputElement || disabled || readOnly || document.activeElement !== inputElement) return;
210
- event.preventDefault();
211
- if (event.deltaY > 0) onIncrement();
212
- else onDecrement();
213
- };
214
- const inputElement = inputRef.current;
215
- inputElement?.addEventListener("wheel", handleWheel);
216
- return ()=>{
217
- inputElement?.removeEventListener("wheel", handleWheel);
218
- };
219
- }, [
220
- onIncrement,
221
- onDecrement,
222
- inputRef,
223
- disabled,
224
- readOnly
225
- ]);
226
- useEffect(()=>{
227
- const inputElement = inputRef.current;
228
- if (inputElement && inputElement !== document.activeElement) inputElement.value = formatValue(value);
229
- }, [
230
- formatValue,
231
- inputRef,
232
- value
233
- ]);
234
- useEffect(()=>{
235
- const inputElement = inputRef.current;
236
- if (!inputElement) return;
237
- const handleReset = ()=>{
238
- onChange(parseValue(defaultValue));
239
- };
240
- const form = inputElement.form;
241
- form?.addEventListener("reset", handleReset);
242
- return ()=>{
243
- form?.removeEventListener("reset", handleReset);
244
- };
245
- }, [
246
- defaultValue,
247
- inputRef,
248
- onChange,
249
- parseValue
250
- ]);
251
- return /*#__PURE__*/ jsx(Field, {
252
- ref: inputRef,
253
- defaultValue: formatValue(value),
254
- disabled: disabled,
255
- id: id,
256
- inputMode: "decimal",
257
- max: max,
258
- min: min,
259
- readOnly: readOnly,
260
- step: step,
261
- onBlur: composeEventHandlers(onBlur, handleBlur),
262
- onKeyDown: combinedKeyDownHandler,
263
- ...inputScope,
264
- ...props
265
- });
144
+ const inputScope = useInputScope(__scopeInputNumber);
145
+ const { defaultValue, disabled, formatValue, id, inputRef, max, min, onChange, onDecrement, onDecrementToMin, onIncrement, onIncrementToMax, parseValue, readOnly, step, value } = useInputNumberContext(NUMBER_INPUT_FIELD_NAME, __scopeInputNumber);
146
+ /**
147
+ * Handles the blur event to format the value of the input.
148
+ *
149
+ * @param event - The blur event triggered when the input loses focus.
150
+ */
151
+ const handleBlur = useCallback((event) => {
152
+ const numericValue = parseValue(event.target.value);
153
+ const formattedValue = formatValue(numericValue);
154
+ if (formattedValue !== event.target.value) event.target.value = formattedValue;
155
+ onChange(numericValue);
156
+ }, [
157
+ formatValue,
158
+ onChange,
159
+ parseValue
160
+ ]);
161
+ /**
162
+ * Handles keydown events to increment, decrement, or perform other actions.
163
+ *
164
+ * @param event - The keyboard event triggered by key presses.
165
+ */
166
+ const handleKeyDown = useCallback((event) => {
167
+ switch (event.key) {
168
+ case "ArrowUp":
169
+ onIncrement();
170
+ event.preventDefault();
171
+ break;
172
+ case "PageUp":
173
+ onIncrementToMax();
174
+ event.preventDefault();
175
+ break;
176
+ case "ArrowDown":
177
+ onDecrement();
178
+ event.preventDefault();
179
+ break;
180
+ case "PageDown":
181
+ onDecrementToMin();
182
+ event.preventDefault();
183
+ break;
184
+ default: break;
185
+ }
186
+ }, [
187
+ onIncrement,
188
+ onIncrementToMax,
189
+ onDecrement,
190
+ onDecrementToMin
191
+ ]);
192
+ /**
193
+ * Prevents invalid keyboard input for the numeric input field.
194
+ *
195
+ * @param event - The keyboard event to handle.
196
+ */
197
+ const handleKeyDownPrevent = useCallback((event) => {
198
+ switch (event.key) {
199
+ case "ArrowUp":
200
+ case "ArrowDown":
201
+ case "ArrowLeft":
202
+ case "ArrowRight":
203
+ case "PageUp":
204
+ case "PageDown":
205
+ case "Tab":
206
+ case "Escape":
207
+ case "Enter":
208
+ case "Backspace":
209
+ case "Delete":
210
+ case "Home":
211
+ case "End":
212
+ case ".":
213
+ case ",":
214
+ case "-":
215
+ case "%": return;
216
+ default:
217
+ if (isNumberKey(event.key) || isModifierKey(event) || isFunctionKey(event.key)) return;
218
+ event.preventDefault();
219
+ }
220
+ }, []);
221
+ /**
222
+ * Handles the Enter key to format the value of the input.
223
+ *
224
+ * @param event - The keyboard event triggered by pressing Enter.
225
+ */
226
+ const handleKeyDownEnter = useCallback((event) => {
227
+ const inputElement = inputRef.current;
228
+ if (event.key !== "Enter" || !inputElement) return;
229
+ const numericValue = parseValue(inputElement.value);
230
+ const formattedValue = formatValue(numericValue);
231
+ if (formattedValue !== inputElement.value) inputElement.value = formattedValue;
232
+ onChange(numericValue);
233
+ }, [
234
+ formatValue,
235
+ inputRef,
236
+ onChange,
237
+ parseValue
238
+ ]);
239
+ /**
240
+ * Creates a combined keydown event handler that processes keyboard events in sequence.
241
+ *
242
+ * The handler chain executes in the following order:
243
+ * 1. User-provided onKeyDown handler (if any)
244
+ * 2. handleKeyDownPrevent - Prevents non-numeric input
245
+ * 3. handleKeyDown - Handles arrow keys and page up/down for value adjustments
246
+ * 4. handleKeyDownEnter - Handles Enter key press to format and update the value
247
+ *
248
+ * @returns A composed event handler function for the onKeyDown event
249
+ */
250
+ const combinedKeyDownHandler = useCallback((event) => {
251
+ composeEventHandlers(onKeyDown, chain(handleKeyDownPrevent, handleKeyDown, handleKeyDownEnter))(event);
252
+ }, [
253
+ onKeyDown,
254
+ handleKeyDown,
255
+ handleKeyDownEnter,
256
+ handleKeyDownPrevent
257
+ ]);
258
+ /**
259
+ * Adds a listener to handle wheel events for incrementing or decrementing the value.
260
+ */
261
+ useEffect(() => {
262
+ const handleWheel = (event) => {
263
+ const inputElement = inputRef.current;
264
+ if (!inputElement || disabled || readOnly || document.activeElement !== inputElement) return;
265
+ event.preventDefault();
266
+ if (event.deltaY > 0) onIncrement();
267
+ else onDecrement();
268
+ };
269
+ const inputElement = inputRef.current;
270
+ inputElement?.addEventListener("wheel", handleWheel);
271
+ return () => {
272
+ inputElement?.removeEventListener("wheel", handleWheel);
273
+ };
274
+ }, [
275
+ onIncrement,
276
+ onDecrement,
277
+ inputRef,
278
+ disabled,
279
+ readOnly
280
+ ]);
281
+ /**
282
+ * Updates the input field's value when it changes in the context.
283
+ */
284
+ useEffect(() => {
285
+ const inputElement = inputRef.current;
286
+ if (inputElement && inputElement !== document.activeElement) inputElement.value = formatValue(value);
287
+ }, [
288
+ formatValue,
289
+ inputRef,
290
+ value
291
+ ]);
292
+ /**
293
+ * Adds a listener to handle form reset events by clearing the input value.
294
+ */
295
+ useEffect(() => {
296
+ const inputElement = inputRef.current;
297
+ if (!inputElement) return;
298
+ const handleReset = () => {
299
+ onChange(parseValue(defaultValue));
300
+ };
301
+ const form = inputElement.form;
302
+ form?.addEventListener("reset", handleReset);
303
+ return () => {
304
+ form?.removeEventListener("reset", handleReset);
305
+ };
306
+ }, [
307
+ defaultValue,
308
+ inputRef,
309
+ onChange,
310
+ parseValue
311
+ ]);
312
+ return /* @__PURE__ */ jsx(InputField, {
313
+ ref: inputRef,
314
+ defaultValue: formatValue(value),
315
+ disabled,
316
+ id,
317
+ inputMode: "decimal",
318
+ max,
319
+ min,
320
+ readOnly,
321
+ step,
322
+ onBlur: composeEventHandlers(onBlur, handleBlur),
323
+ onKeyDown: combinedKeyDownHandler,
324
+ ...inputScope,
325
+ ...props
326
+ });
266
327
  }
328
+ /**
329
+ * The name of the NumberStepperButton component constant.
330
+ */
267
331
  const NUMBER_STEPPER_BUTTON_NAME = "NumberStepperButton";
268
332
  function NumberStepperButton({ __scopeInputNumber, operation, ...props }) {
269
- const { ariaDecrementLabel, ariaIncrementLabel, disabled, id, max, min, onDecrement, onIncrement, value } = useInputNumberContext(NUMBER_STEPPER_BUTTON_NAME, __scopeInputNumber);
270
- const isDisabled = useMemo(()=>{
271
- const atMin = void 0 !== min && void 0 !== value && value <= min;
272
- const atMax = void 0 !== max && void 0 !== value && value >= max;
273
- return (disabled ?? atMin) || atMax;
274
- }, [
275
- min,
276
- max,
277
- value,
278
- disabled
279
- ]);
280
- const timeoutIdRef = useRef(null);
281
- const startActionInterval = useCallback((callback)=>{
282
- const interval = 100;
283
- const repeatAction = ()=>{
284
- callback();
285
- timeoutIdRef.current = setTimeout(repeatAction, interval);
286
- };
287
- callback();
288
- timeoutIdRef.current = setTimeout(repeatAction, 2 * interval);
289
- }, []);
290
- const clearActionInterval = useCallback(()=>{
291
- if (timeoutIdRef.current) {
292
- clearTimeout(timeoutIdRef.current);
293
- timeoutIdRef.current = null;
294
- }
295
- }, []);
296
- const handlePointerDown = useCallback(()=>{
297
- const action = "increment" === operation ? onIncrement : onDecrement;
298
- startActionInterval(action);
299
- }, [
300
- onDecrement,
301
- onIncrement,
302
- operation,
303
- startActionInterval
304
- ]);
305
- const handleContextMenu = useCallback((event)=>{
306
- event.preventDefault();
307
- }, []);
308
- const handleKeyDown = useCallback((event)=>{
309
- if ("Enter" === event.key || " " === event.key) {
310
- event.preventDefault();
311
- const action = "increment" === operation ? onIncrement : onDecrement;
312
- action();
313
- }
314
- }, [
315
- onDecrement,
316
- onIncrement,
317
- operation
318
- ]);
319
- return /*#__PURE__*/ jsx("button", {
320
- "aria-controls": id,
321
- "aria-label": "increment" === operation ? ariaIncrementLabel : ariaDecrementLabel,
322
- "aria-live": "polite",
323
- disabled: isDisabled,
324
- type: "button",
325
- onContextMenu: handleContextMenu,
326
- onKeyDown: handleKeyDown,
327
- onPointerCancel: clearActionInterval,
328
- onPointerDown: handlePointerDown,
329
- onPointerLeave: clearActionInterval,
330
- onPointerUp: clearActionInterval,
331
- ...props
332
- });
333
+ const { ariaDecrementLabel, ariaIncrementLabel, disabled, id, max, min, onDecrement, onIncrement, value } = useInputNumberContext(NUMBER_STEPPER_BUTTON_NAME, __scopeInputNumber);
334
+ const isDisabled = useMemo(() => {
335
+ return (disabled ?? (min !== void 0 && value !== void 0 && value <= min)) || max !== void 0 && value !== void 0 && value >= max;
336
+ }, [
337
+ min,
338
+ max,
339
+ value,
340
+ disabled
341
+ ]);
342
+ /**
343
+ * Ref to store a timeout ID for managing repeated button actions.
344
+ */
345
+ const timeoutIdRef = useRef(null);
346
+ /**
347
+ * Starts a repeated action at a regular interval.
348
+ * The action begins immediately and then continues with a delay.
349
+ *
350
+ * @param callback - The callback function to execute repeatedly.
351
+ */
352
+ const startActionInterval = useCallback((callback) => {
353
+ const interval = 100;
354
+ const repeatAction = () => {
355
+ callback();
356
+ timeoutIdRef.current = setTimeout(repeatAction, interval);
357
+ };
358
+ callback();
359
+ timeoutIdRef.current = setTimeout(repeatAction, interval * 2);
360
+ }, []);
361
+ /**
362
+ * Clears any ongoing action intervals.
363
+ */
364
+ const clearActionInterval = useCallback(() => {
365
+ if (timeoutIdRef.current) {
366
+ clearTimeout(timeoutIdRef.current);
367
+ timeoutIdRef.current = null;
368
+ }
369
+ }, []);
370
+ /**
371
+ * Handles pointer down events and triggers the appropriate action
372
+ * (`increment` or `decrement`).
373
+ */
374
+ const handlePointerDown = useCallback(() => {
375
+ startActionInterval(operation === "increment" ? onIncrement : onDecrement);
376
+ }, [
377
+ onDecrement,
378
+ onIncrement,
379
+ operation,
380
+ startActionInterval
381
+ ]);
382
+ /**
383
+ * Prevents the context menu from displaying when the button is right-clicked.
384
+ *
385
+ * @param event - The mouse event triggered by the right-click.
386
+ */
387
+ const handleContextMenu = useCallback((event) => {
388
+ event.preventDefault();
389
+ }, []);
390
+ /**
391
+ * Handles keyboard events to support activation of the button using
392
+ * keyboard navigation (Enter or Space).
393
+ *
394
+ * @param event - The keyboard event with the triggered key.
395
+ */
396
+ const handleKeyDown = useCallback((event) => {
397
+ if (event.key === "Enter" || event.key === " ") {
398
+ event.preventDefault();
399
+ (operation === "increment" ? onIncrement : onDecrement)();
400
+ }
401
+ }, [
402
+ onDecrement,
403
+ onIncrement,
404
+ operation
405
+ ]);
406
+ return /* @__PURE__ */ jsx("button", {
407
+ "aria-controls": id,
408
+ "aria-label": operation === "increment" ? ariaIncrementLabel : ariaDecrementLabel,
409
+ "aria-live": "polite",
410
+ disabled: isDisabled,
411
+ type: "button",
412
+ onContextMenu: handleContextMenu,
413
+ onKeyDown: handleKeyDown,
414
+ onPointerCancel: clearActionInterval,
415
+ onPointerDown: handlePointerDown,
416
+ onPointerLeave: clearActionInterval,
417
+ onPointerUp: clearActionInterval,
418
+ ...props
419
+ });
333
420
  }
334
421
  function InputNumberIncrementButton(props) {
335
- return /*#__PURE__*/ jsx(NumberStepperButton, {
336
- operation: "increment",
337
- ...props
338
- });
422
+ return /* @__PURE__ */ jsx(NumberStepperButton, {
423
+ operation: "increment",
424
+ ...props
425
+ });
339
426
  }
340
427
  function InputNumberDecrementButton(props) {
341
- return /*#__PURE__*/ jsx(NumberStepperButton, {
342
- operation: "decrement",
343
- ...props
344
- });
428
+ return /* @__PURE__ */ jsx(NumberStepperButton, {
429
+ operation: "decrement",
430
+ ...props
431
+ });
345
432
  }
433
+ /**
434
+ * Chains multiple callbacks into a single function
435
+ *
436
+ * @param callbacks - Array of callback functions that will be executed in order
437
+ * @returns A single function that executes all callbacks
438
+ */
346
439
  function chain(...callbacks) {
347
- return (...args)=>{
348
- for (const callback of callbacks)callback(...args);
349
- };
440
+ return (...args) => {
441
+ for (const callback of callbacks) callback(...args);
442
+ };
350
443
  }
444
+ /**
445
+ * Extracts decimal and a thousand separators from a given locale's number format
446
+ *
447
+ * @param locale - The locale string to use for number formatting (e.g., 'en-US', 'de-DE')
448
+ * @returns Object containing decimal and a thousand separators
449
+ */
351
450
  function getNumberFormatSeparators(locale) {
352
- const numberFormat = new Intl.NumberFormat(locale);
353
- const parts = numberFormat.formatToParts(12345.6);
354
- let thousandSeparator = "";
355
- let decimalSeparator = "";
356
- for (const part of parts){
357
- if ("group" === part.type) thousandSeparator = part.value;
358
- if ("decimal" === part.type) decimalSeparator = part.value;
359
- if (thousandSeparator && decimalSeparator) break;
360
- }
361
- return {
362
- decimalSeparator,
363
- thousandSeparator
364
- };
451
+ const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);
452
+ let thousandSeparator = "";
453
+ let decimalSeparator = "";
454
+ for (const part of parts) {
455
+ if (part.type === "group") thousandSeparator = part.value;
456
+ if (part.type === "decimal") decimalSeparator = part.value;
457
+ if (thousandSeparator && decimalSeparator) break;
458
+ }
459
+ return {
460
+ decimalSeparator,
461
+ thousandSeparator
462
+ };
365
463
  }
464
+ /**
465
+ * Normalizes an input value by removing formatting characters
466
+ *
467
+ * @param value - The input string to normalize
468
+ * @param thousandSeparator - The thousand-separator character to remove
469
+ * @param decimalSeparator - The decimal separator to convert to standard dot notation
470
+ * @returns Normalized string value ready for numeric conversion
471
+ */
366
472
  function normalizeInputValue(value, thousandSeparator, decimalSeparator) {
367
- return value.replaceAll(new RegExp(`\\${thousandSeparator}`, "g"), "").replace(new RegExp(`\\${decimalSeparator}`), ".").replaceAll(/[()]/g, "-");
473
+ return value.replaceAll(new RegExp(`\\${thousandSeparator}`, "g"), "").replace(new RegExp(`\\${decimalSeparator}`), ".").replaceAll(/[()]/g, "-");
368
474
  }
475
+ /**
476
+ * Checks if a keyboard event includes modifier keys (Ctrl, Alt, Meta, Shift)
477
+ *
478
+ * @param event - The keyboard event to check
479
+ * @returns True if any modifier key is pressed
480
+ */
369
481
  function isModifierKey(event) {
370
- return event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
482
+ return event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
371
483
  }
484
+ /**
485
+ * Determines if a key is a function key (F1-F12)
486
+ *
487
+ * @param key - The key name to check
488
+ * @returns True if the key is a function key
489
+ */
372
490
  function isFunctionKey(key) {
373
- return key.startsWith("F") && key.length > 1;
491
+ return key.startsWith("F") && key.length > 1;
374
492
  }
493
+ /**
494
+ * Checks if a key represents a number (0-9)
495
+ *
496
+ * @param key - The key name to check
497
+ * @returns True if the key represents a number
498
+ */
375
499
  function isNumberKey(key) {
376
- return !Number.isNaN(Number(key));
500
+ return !Number.isNaN(Number(key));
377
501
  }
378
- function clamp(value, min = -1 / 0, max = 1 / 0) {
379
- return Math.min(Math.max(value, min), max);
502
+ /**
503
+ * Clamps a numeric value between a minimum and maximum
504
+ *
505
+ * @param value - The value to clamp
506
+ * @param min - The minimum allowed value (defaults to \-Infinity)
507
+ * @param max - The maximum allowed value (defaults to Infinity)
508
+ * @returns The clamped value
509
+ */
510
+ function clamp(value, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY) {
511
+ return Math.min(Math.max(value, min), max);
380
512
  }
381
- export { InputNumberDecrementButton as DecrementButton, InputNumberField as Field, InputNumberIncrementButton as IncrementButton, InputNumber, InputNumberDecrementButton, InputNumberField, InputNumberIncrementButton, InputNumber as Root, createInputNumberScope };
513
+ //#endregion
514
+ export { InputNumberDecrementButton as DecrementButton, InputNumberDecrementButton, InputNumberField as Field, InputNumberField, InputNumberIncrementButton as IncrementButton, InputNumberIncrementButton, InputNumber, InputNumber as Root, createInputNumberScope };