@jobber/components 9.3.0 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/dist/Autocomplete/index.cjs +0 -1
  2. package/dist/Autocomplete/index.mjs +0 -1
  3. package/dist/Card/index.cjs +7 -7
  4. package/dist/Card/index.mjs +7 -7
  5. package/dist/Chip/index.cjs +0 -1
  6. package/dist/Chip/index.mjs +0 -1
  7. package/dist/Chips/InternalChipDismissible/hooks/index.cjs +0 -1
  8. package/dist/Chips/InternalChipDismissible/hooks/index.mjs +0 -1
  9. package/dist/Chips/InternalChipDismissible/index.cjs +0 -1
  10. package/dist/Chips/InternalChipDismissible/index.mjs +0 -1
  11. package/dist/Chips/index.cjs +0 -1
  12. package/dist/Chips/index.mjs +0 -1
  13. package/dist/ComboboxChipRemove-cjs.js +181 -184
  14. package/dist/ComboboxChipRemove-es.js +4 -7
  15. package/dist/ConfirmationModal/index.cjs +0 -1
  16. package/dist/ConfirmationModal/index.mjs +0 -1
  17. package/dist/DataDump/DataDump.d.ts +1 -1
  18. package/dist/DataDump/DataDump.messages.d.ts +6 -0
  19. package/dist/DataDump/index.cjs +9 -9
  20. package/dist/DataDump/index.mjs +9 -9
  21. package/dist/DataDump-cjs.js +11 -1
  22. package/dist/DataDump-es.js +11 -1
  23. package/dist/DataList/components/DataListActions/index.cjs +0 -1
  24. package/dist/DataList/components/DataListActions/index.mjs +0 -1
  25. package/dist/DataList/components/DataListBulkActions/index.cjs +0 -1
  26. package/dist/DataList/components/DataListBulkActions/index.mjs +0 -1
  27. package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +0 -1
  28. package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +0 -1
  29. package/dist/DataList/components/DataListFilters/index.cjs +0 -1
  30. package/dist/DataList/components/DataListFilters/index.mjs +0 -1
  31. package/dist/DataList/components/DataListHeader/index.cjs +0 -1
  32. package/dist/DataList/components/DataListHeader/index.mjs +0 -1
  33. package/dist/DataList/components/DataListItem/index.cjs +0 -1
  34. package/dist/DataList/components/DataListItem/index.mjs +0 -1
  35. package/dist/DataList/components/DataListItemActions/index.cjs +0 -1
  36. package/dist/DataList/components/DataListItemActions/index.mjs +0 -1
  37. package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +0 -1
  38. package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +0 -1
  39. package/dist/DataList/components/DataListItems/index.cjs +0 -1
  40. package/dist/DataList/components/DataListItems/index.mjs +0 -1
  41. package/dist/DataList/components/DataListLayout/index.cjs +0 -1
  42. package/dist/DataList/components/DataListLayout/index.mjs +0 -1
  43. package/dist/DataList/components/DataListLayoutActions/index.cjs +0 -1
  44. package/dist/DataList/components/DataListLayoutActions/index.mjs +0 -1
  45. package/dist/DataList/index.cjs +0 -1
  46. package/dist/DataList/index.mjs +0 -1
  47. package/dist/DataTable/DataTable.messages.d.ts +22 -0
  48. package/dist/DataTable/index.cjs +2 -2
  49. package/dist/DataTable/index.mjs +2 -2
  50. package/dist/DataTable-cjs.js +34 -5
  51. package/dist/DataTable-es.js +34 -5
  52. package/dist/DatePicker/index.cjs +0 -2
  53. package/dist/DatePicker/index.mjs +0 -2
  54. package/dist/DatePicker-cjs.js +19 -18
  55. package/dist/DatePicker-es.js +2 -1
  56. package/dist/Dialog/index.cjs +11 -6
  57. package/dist/Dialog/index.mjs +11 -6
  58. package/dist/DrawerDescription-cjs.js +7 -8
  59. package/dist/DrawerDescription-es.js +1 -2
  60. package/dist/DrawerTitle-cjs.js +4 -4
  61. package/dist/DrawerTitle-es.js +1 -1
  62. package/dist/FeatureSwitch/FeatureSwitch.messages.d.ts +10 -0
  63. package/dist/FeatureSwitch/index.cjs +2 -0
  64. package/dist/FeatureSwitch/index.mjs +2 -0
  65. package/dist/FeatureSwitch-cjs.js +15 -2
  66. package/dist/FeatureSwitch-es.js +15 -2
  67. package/dist/FieldDescription-cjs.js +49 -49
  68. package/dist/FieldDescription-es.js +2 -2
  69. package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +0 -1
  70. package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +0 -1
  71. package/dist/FilterPicker/components/FilterPickerContent/index.cjs +0 -1
  72. package/dist/FilterPicker/components/FilterPickerContent/index.mjs +0 -1
  73. package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +0 -1
  74. package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +0 -1
  75. package/dist/FilterPicker/index.cjs +0 -1
  76. package/dist/FilterPicker/index.mjs +0 -1
  77. package/dist/FormatFile/FormatFile.messages.d.ts +18 -0
  78. package/dist/FormatFile/index.cjs +2 -3
  79. package/dist/FormatFile/index.mjs +2 -3
  80. package/dist/FormatFile-cjs.js +24 -2
  81. package/dist/FormatFile-es.js +24 -2
  82. package/dist/Gallery/index.cjs +0 -1
  83. package/dist/Gallery/index.mjs +0 -1
  84. package/dist/HelperText-cjs.js +3 -2
  85. package/dist/HelperText-es.js +3 -2
  86. package/dist/InputDate/index.cjs +0 -2
  87. package/dist/InputDate/index.mjs +0 -2
  88. package/dist/InputNumber/InputNumber.d.ts +75 -5
  89. package/dist/InputNumber/index.cjs +27 -3
  90. package/dist/InputNumber/index.d.ts +2 -2
  91. package/dist/InputNumber/index.mjs +27 -3
  92. package/dist/{primitives/InputNumberExperimental → InputNumber}/types.d.ts +48 -35
  93. package/dist/InputNumber-cjs.js +267 -4986
  94. package/dist/InputNumber-es.js +268 -4987
  95. package/dist/LightBox/index.cjs +0 -1
  96. package/dist/LightBox/index.mjs +0 -1
  97. package/dist/Menu/index.cjs +7 -7
  98. package/dist/Menu/index.mjs +7 -7
  99. package/dist/Menu-cjs.js +5 -1
  100. package/dist/Menu-es.js +5 -2
  101. package/dist/MenuSubmenuTrigger-cjs.js +142 -143
  102. package/dist/MenuSubmenuTrigger-es.js +5 -6
  103. package/dist/Modal/index.cjs +0 -1
  104. package/dist/Modal/index.mjs +0 -1
  105. package/dist/NumberFieldInput-cjs.js +63 -63
  106. package/dist/NumberFieldInput-es.js +5 -5
  107. package/dist/Page/index.cjs +18 -19
  108. package/dist/Page/index.mjs +18 -19
  109. package/dist/Page-cjs.js +6 -3
  110. package/dist/Page-es.js +6 -3
  111. package/dist/Popover/index.cjs +0 -1
  112. package/dist/Popover/index.mjs +0 -1
  113. package/dist/ProgressBar/ProgressBar.messages.d.ts +6 -0
  114. package/dist/ProgressBar/index.cjs +2 -0
  115. package/dist/ProgressBar/index.mjs +2 -0
  116. package/dist/ProgressBar-cjs.js +13 -1
  117. package/dist/ProgressBar-es.js +13 -1
  118. package/dist/ScrollAreaViewport-cjs.js +119 -121
  119. package/dist/ScrollAreaViewport-es.js +3 -5
  120. package/dist/Select/index.cjs +3 -9
  121. package/dist/Select/index.mjs +3 -9
  122. package/dist/SelectGroupLabel-cjs.js +117 -107
  123. package/dist/SelectGroupLabel-es.js +20 -12
  124. package/dist/Toast/Toast.messages.d.ts +6 -0
  125. package/dist/Toast/index.cjs +3 -1
  126. package/dist/Toast/index.mjs +3 -1
  127. package/dist/Toast/showToast.d.ts +12 -1
  128. package/dist/Tooltip/index.cjs +0 -1
  129. package/dist/Tooltip/index.mjs +0 -1
  130. package/dist/dialogReturnFocus-cjs.js +4 -1
  131. package/dist/dialogReturnFocus-es.js +4 -1
  132. package/dist/docs/DataDump/DataDump.md +1 -1
  133. package/dist/docs/InputNumber/InputNumber.md +564 -56
  134. package/dist/docs/Page/Page.md +1 -1
  135. package/dist/docs/index.md +0 -1
  136. package/dist/floating-ui.react-cjs.js +34 -35
  137. package/dist/floating-ui.react-dom-cjs.js +64 -65
  138. package/dist/floating-ui.react-dom-es.js +1 -2
  139. package/dist/floating-ui.react-es.js +1 -2
  140. package/dist/floating-ui.utils-cjs.js +183 -0
  141. package/dist/floating-ui.utils-es.js +165 -1
  142. package/dist/index.cjs +10 -15
  143. package/dist/index.mjs +10 -15
  144. package/dist/primitives/BottomSheet/index.cjs +2 -5
  145. package/dist/primitives/BottomSheet/index.mjs +2 -5
  146. package/dist/primitives/ComboboxPrimitive/index.cjs +3 -7
  147. package/dist/primitives/ComboboxPrimitive/index.mjs +3 -7
  148. package/dist/primitives/HelperText/HelperText.d.ts +4 -1
  149. package/dist/primitives/HelperText/index.cjs +1 -1
  150. package/dist/primitives/HelperText/index.mjs +1 -1
  151. package/dist/primitives/SelectPrimitive/index.cjs +3 -9
  152. package/dist/primitives/SelectPrimitive/index.mjs +3 -9
  153. package/dist/primitives/index.cjs +2 -16
  154. package/dist/primitives/index.d.ts +0 -2
  155. package/dist/primitives/index.mjs +2 -15
  156. package/dist/scrollEdges-cjs.js +6 -2
  157. package/dist/scrollEdges-es.js +5 -2
  158. package/dist/sharedHelpers/i18n/renderWithLocale.d.ts +11 -0
  159. package/dist/showToast-cjs.js +20 -6
  160. package/dist/showToast-es.js +20 -6
  161. package/dist/styles.css +573 -902
  162. package/dist/unstyledPrimitives/index.cjs +105 -111
  163. package/dist/unstyledPrimitives/index.mjs +5 -11
  164. package/dist/useCompositeListItem-cjs.js +516 -90
  165. package/dist/useCompositeListItem-es.js +407 -6
  166. package/dist/useLabel-cjs.js +10 -10
  167. package/dist/useLabel-es.js +3 -3
  168. package/dist/useRenderElement-es.js +1 -1
  169. package/dist/useScrollLock-cjs.js +1332 -203
  170. package/dist/useScrollLock-es.js +1068 -5
  171. package/dist/utils/meta/meta.json +12 -0
  172. package/package.json +2 -3
  173. package/dist/InputNumber/InputNumber.types.d.ts +0 -20
  174. package/dist/InputNumberExperimental-cjs.js +0 -277
  175. package/dist/InputNumberExperimental-es.js +0 -275
  176. package/dist/clamp-cjs.js +0 -7
  177. package/dist/clamp-es.js +0 -5
  178. package/dist/clsx-cjs.js +0 -5
  179. package/dist/clsx-es.js +0 -3
  180. package/dist/docs/InputNumberExperimental/InputNumberExperimental.md +0 -578
  181. package/dist/floating-ui.utils.dom-cjs.js +0 -185
  182. package/dist/floating-ui.utils.dom-es.js +0 -165
  183. package/dist/isElementDisabled-cjs.js +0 -40
  184. package/dist/isElementDisabled-es.js +0 -18
  185. package/dist/primitives/InputNumberExperimental/InputNumberExperimental.d.ts +0 -77
  186. package/dist/primitives/InputNumberExperimental/index.cjs +0 -30
  187. package/dist/primitives/InputNumberExperimental/index.d.ts +0 -2
  188. package/dist/primitives/InputNumberExperimental/index.mjs +0 -24
  189. package/dist/resolveAriaLabelledBy-cjs.js +0 -200
  190. package/dist/resolveAriaLabelledBy-es.js +0 -166
  191. package/dist/useButton-cjs.js +0 -1164
  192. package/dist/useButton-es.js +0 -1071
  193. package/dist/useLabelableId-cjs.js +0 -278
  194. package/dist/useLabelableId-es.js +0 -247
@@ -1,71 +1,579 @@
1
1
  # InputNumber
2
2
 
3
- InputNumber is used in forms that accept numbers as an answer.
3
+ ## Summary
4
4
 
5
- ## Design & usage guidelines
5
+ `InputNumber` collects a single numeric value in a form. Reach for it when the
6
+ value benefits from being nudged up or down, such as quantities, prices,
7
+ durations, or counts.
6
8
 
7
- This is best suited for input data that benefits from being modified in
8
- increments, such as quantity, price, or days (ie 2 days -> 3 days).
9
+ Most fields only need the props shown below. For rare layouts the props can't
10
+ handle, you can build the same field from smaller pieces. See the **Implement**
11
+ tab.
9
12
 
10
- While some types of data may technically be numbers, they can be ill-suited for
11
- using a number input. For example, phone numbers and credit card numbers provide
12
- no value to the user by offering an incrementer.
13
+ ### When to use
13
14
 
14
- ## Clearable
15
+ * The value is a number the user increments or decrements, like a quantity,
16
+ price, day count, or number of repetitions
17
+ * A stepper, min/max bounds, or number formatting would help the user
15
18
 
16
- InputNumber does not show a clear button. The component enforces
17
- `clearable="never"` to align with numeric, increment/decrement-focused usage.
19
+ ### When not to use
20
+
21
+ * The value is a sequence of digits that is never calculated with, such as phone
22
+ numbers, credit-card numbers, or postal codes. A stepper adds no value there;
23
+ use [InputText](../InputText/InputText.md) instead.
24
+
25
+ ## Anatomy
26
+
27
+ `InputNumber` typically includes:
28
+
29
+ * Label (required): names the value the field collects
30
+ * Value field (required): the number the user types or steps through
31
+ * Stepper (optional): increment and decrement controls that change the value by
32
+ `step`
33
+ * Prefix or suffix (optional): a unit or symbol shown alongside the value, like
34
+ $ or kg
35
+ * Loading indicator (optional): replaces the stepper while background work runs
36
+
37
+ ## Behavior
38
+
39
+ * The field is controlled through `value`, where a number sets the value and
40
+ `null` leaves it empty.
41
+ * `onValueCommitted` fires when the user commits a value: on blur, on Enter, or
42
+ when they use the stepper or arrow keys. Use `onValueChange` for per-keystroke
43
+ updates.
44
+ * The stepper buttons and the Up and Down arrow keys change the value by `step`
45
+ (default 1).
46
+ * `min` and `max` bound the value, and the stepper stops at each limit.
47
+ * `loading` hides the stepper and shows an indicator in its place. The field
48
+ stays editable, so use `readOnly` or `disabled` to lock it.
49
+
50
+ ## Options
51
+
52
+ ### Basic
53
+
54
+ Pass `label`, a controlled `value`, and `onValueCommitted`. Bounds (`min` /
55
+ `max`) and `step` are optional.
56
+
57
+ ```tsx
58
+ import React, { useState } from "react";
59
+ import type { InputNumberProps } from "@jobber/components";
60
+ import { InputNumber } from "@jobber/components";
61
+
62
+ export function InputNumberBasicExample(props: Partial<InputNumberProps>) {
63
+ const [value, setValue] = useState<number | null>(3);
64
+
65
+ return (
66
+ <InputNumber
67
+ label="Quantity"
68
+ min={0}
69
+ max={100}
70
+ {...props}
71
+ value={value}
72
+ onValueCommitted={setValue}
73
+ />
74
+ );
75
+ }
76
+ ```
77
+
78
+ ### Prefixes and suffixes
79
+
80
+ Use `prefix` or `suffix` to add a unit or symbol. A suffix can be a label, an
81
+ icon, or a clickable icon that runs an action (give it an `ariaLabel`).
82
+
83
+ ```tsx
84
+ import React, { useState } from "react";
85
+ import { InputNumber } from "@jobber/components";
86
+ import { Content } from "@jobber/components/Content";
87
+
88
+ export function InputNumberAffixesExample() {
89
+ const [price, setPrice] = useState<number | null>(42);
90
+ const [days, setDays] = useState<number | null>(7);
91
+ const [reps, setReps] = useState<number | null>(3);
92
+
93
+ return (
94
+ <Content>
95
+ <InputNumber
96
+ label="Price"
97
+ prefix={{ label: "$" }}
98
+ suffix={{ label: "USD" }}
99
+ value={price}
100
+ onValueCommitted={setPrice}
101
+ />
102
+
103
+ <InputNumber
104
+ label="Follow-up in"
105
+ suffix={{ icon: "calendar", label: "days" }}
106
+ value={days}
107
+ onValueCommitted={setDays}
108
+ />
109
+
110
+ <InputNumber
111
+ label="Repetitions"
112
+ suffix={{
113
+ icon: "cross",
114
+ ariaLabel: "Clear value",
115
+ onClick: () => setReps(null),
116
+ }}
117
+ value={reps}
118
+ onValueCommitted={setReps}
119
+ />
120
+ </Content>
121
+ );
122
+ }
123
+ ```
124
+
125
+ ### Sizes
126
+
127
+ 3 sizes are available. `default` fits almost every form; use `small` only in
128
+ tight spaces and `large` only in especially spacious layouts.
129
+
130
+ ```tsx
131
+ import React, { useState } from "react";
132
+ import { InputNumber } from "@jobber/components";
133
+ import { Content } from "@jobber/components/Content";
134
+
135
+ export function InputNumberSizesExample() {
136
+ const [small, setSmall] = useState<number | null>(42);
137
+ const [base, setBase] = useState<number | null>(42);
138
+ const [large, setLarge] = useState<number | null>(42);
139
+
140
+ return (
141
+ <Content>
142
+ <InputNumber
143
+ label="Small"
144
+ size="small"
145
+ suffix={{ label: "items" }}
146
+ value={small}
147
+ onValueCommitted={setSmall}
148
+ />
149
+ <InputNumber
150
+ label="Default"
151
+ size="default"
152
+ suffix={{ label: "items" }}
153
+ value={base}
154
+ onValueCommitted={setBase}
155
+ />
156
+ <InputNumber
157
+ label="Large"
158
+ size="large"
159
+ suffix={{ label: "items" }}
160
+ value={large}
161
+ onValueCommitted={setLarge}
162
+ />
163
+ </Content>
164
+ );
165
+ }
166
+ ```
167
+
168
+ ### Formatting
169
+
170
+ `format` takes any `Intl.NumberFormatOptions` and controls only how the value is
171
+ displayed; the committed value stays a plain number. See the **Implement** tab
172
+ for how percent and currency values map to the underlying number.
173
+
174
+ ```tsx
175
+ import React, { useState } from "react";
176
+ import { InputNumber } from "@jobber/components";
177
+ import { Content } from "@jobber/components/Content";
178
+
179
+ export function InputNumberFormattingExample() {
180
+ const [currency, setCurrency] = useState<number | null>(1234.5);
181
+ const [percent, setPercent] = useState<number | null>(0.5);
182
+ const [decimal, setDecimal] = useState<number | null>(11.13);
183
+
184
+ return (
185
+ <Content>
186
+ <InputNumber
187
+ label="Currency"
188
+ description='{ style: "currency", currency: "USD" }'
189
+ format={{ style: "currency", currency: "USD" }}
190
+ value={currency}
191
+ onValueCommitted={setCurrency}
192
+ />
193
+ <InputNumber
194
+ label="Percent"
195
+ description='{ style: "percent" } — value is a ratio: 0.5 → 50%'
196
+ format={{ style: "percent", maximumFractionDigits: 2 }}
197
+ value={percent}
198
+ onValueCommitted={setPercent}
199
+ />
200
+ <InputNumber
201
+ label="Decimal"
202
+ description="{ maximumFractionDigits: 2 }"
203
+ format={{ maximumFractionDigits: 2 }}
204
+ value={decimal}
205
+ onValueCommitted={setDecimal}
206
+ />
207
+ </Content>
208
+ );
209
+ }
210
+ ```
211
+
212
+ ### Loading
213
+
214
+ `loading` shows a non-blocking indicator in the stepper's slot for background
215
+ work, like saving. The field stays editable and the stepper is hidden while
216
+ loading.
217
+
218
+ ```tsx
219
+ import React, { useState } from "react";
220
+ import { InputNumber } from "@jobber/components";
221
+
222
+ export function InputNumberLoadingExample() {
223
+ const [value, setValue] = useState<number | null>(42);
224
+
225
+ return (
226
+ <InputNumber
227
+ loading
228
+ label="Quantity"
229
+ suffix={{ label: "items" }}
230
+ value={value}
231
+ onValueCommitted={setValue}
232
+ />
233
+ );
234
+ }
235
+ ```
236
+
237
+ ## Content guidelines
238
+
239
+ ### Label the unit, don't repeat it
240
+
241
+ Put the unit in the label or an affix, not both.
242
+
243
+ | ✅ Do | ❌ Don't |
244
+ | ------------------------------- | --------------------------------------- |
245
+ | Label "Weight", suffix "kg" | Label "Weight (kg)", suffix "kg" |
246
+ | Label "Duration", suffix "days" | Label "Duration in days", suffix "days" |
247
+
248
+ ### Keep labels short and sentence case
249
+
250
+ | ✅ Do | ❌ Don't |
251
+ | -------- | ----------------------- |
252
+ | Quantity | Enter the quantity here |
253
+ | Discount | DISCOUNT % |
254
+
255
+ ### Put the symbol where it's read
256
+
257
+ Use a prefix for a leading symbol and a suffix for a trailing unit, matching how
258
+ the value is spoken.
259
+
260
+ | ✅ Do | ❌ Don't |
261
+ | -------------------- | -------------------- |
262
+ | Prefix "$", value 40 | Suffix "$", value 40 |
263
+ | Suffix "%", value 15 | Prefix "%", value 15 |
264
+
265
+ ### Keep validation errors helpful
266
+
267
+ When a value breaks `min` or `max`, provide helpful guidance on what values will
268
+ be accepted as opposed to just providing a generic error.
269
+
270
+ | ✅ Do | ❌ Don't |
271
+ | ------------------------------ | ------------- |
272
+ | Enter a value between 1 and 99 | Invalid input |
273
+ | Quantity can't be more than 50 | Error |
274
+
275
+ ### Use numbers as opposed to spelling them
276
+
277
+ Use numerals in labels, helper text, affixes, and bounds.
278
+
279
+ | ✅ Do | ❌ Don't |
280
+ | ----------- | --------------- |
281
+ | Max 3 items | Max three items |
282
+
283
+ ## Do's and Don'ts
284
+
285
+ #### Do:
286
+
287
+ * ✅ Use for values the user increments or decrements
288
+ * ✅ Set `min` and `max` when the value has real bounds
289
+ * ✅ Use `format` for currency, percent, and decimals rather than formatting the
290
+ value yourself
291
+ * ✅ Use `loading` for background work so the field stays usable
292
+
293
+ #### Don't:
294
+
295
+ * ❌ Use it for digit sequences that are never calculated with, like phone or
296
+ credit card numbers
297
+ * ❌ Disable the field to communicate an error; show an `error` message instead
298
+ * ❌ Repeat the unit in both the label and an affix
299
+
300
+ ## Accessibility notes
301
+
302
+ The field is a native number input, so it is reachable and operable by keyboard
303
+ and assistive technology.
304
+
305
+ | Key | Behavior |
306
+ | ---------------- | ------------------------------- |
307
+ | Tab | Moves focus to the field |
308
+ | Up / Down arrows | Increment / decrement by `step` |
309
+ | Enter | Commits the current value |
310
+ | Type | Replaces the value |
311
+
312
+ Give a clickable affix a clear `ariaLabel` describing its action, like "Clear
313
+ value".
314
+
315
+ ## Related components
316
+
317
+ * For digit sequences that are not calculated with, like phone or credit card
318
+ numbers, use [InputText](../InputText/InputText.md).
319
+ * For dates, use [InputDate](../InputDate/InputDate.md).
320
+
321
+
322
+ ## Anatomy
323
+
324
+ The prop-driven `<InputNumber>` composes a set of parts. You only need these
325
+ when the props can't express a layout; otherwise reach for the props shown on
326
+ the **Design** tab.
327
+
328
+ | Part | Description |
329
+ | ----------------------- | -------------------------------------------------------------------- |
330
+ | `Wrapper` | Owns the field configuration and state; provides it to the parts |
331
+ | `Group` | The bordered field row |
332
+ | `Input` | The input area; holds the `Label` and the `Stepper` / `Loading` slot |
333
+ | `Label` | Floating field label |
334
+ | `Stepper` | The increment / decrement button pair |
335
+ | `Increment` `Decrement` | The individual stepper buttons |
336
+ | `Affix` | Prefix / suffix content (label, icon, or clickable icon) |
337
+ | `Loading` | Non-blocking loading indicator slot |
338
+ | `Footer` | Below-field row that holds `Description` and `Error` |
339
+ | `Description` | Helper text below the field |
340
+ | `Error` | Styled error message below the field |
341
+
342
+ ## Composition
343
+
344
+ The prop-driven component is sugar: it renders exactly the tree you would write
345
+ by hand with `<InputNumber.Wrapper>` and the parts. To customize a single piece,
346
+ compose the tree yourself and swap that one part — the other parts keep their
347
+ defaults. The sugar does not merge consumer-provided parts into its render, so
348
+ there is no per-slot precedence to reason about.
349
+
350
+ `Wrapper` owns the field state and shares it with the parts through context, so
351
+ every part must be rendered inside a `Wrapper` (a part used outside one throws).
352
+
353
+ The example below replaces the default stepper icons with `+` / `−` and leaves
354
+ everything else as the default:
355
+
356
+ ```tsx
357
+ import React, { useState } from "react";
358
+ import { InputNumber } from "@jobber/components";
359
+
360
+ export function InputNumberCompositionExample() {
361
+ const [value, setValue] = useState<number | null>(3);
362
+
363
+ return (
364
+ <InputNumber.Wrapper value={value} onValueCommitted={setValue}>
365
+ <InputNumber.Group>
366
+ <InputNumber.Input>
367
+ <InputNumber.Label>Quantity</InputNumber.Label>
368
+ <InputNumber.Stepper>
369
+ <InputNumber.Increment ariaLabel="Increase Quantity">
370
+ +
371
+ </InputNumber.Increment>
372
+ <InputNumber.Decrement ariaLabel="Decrease Quantity">
373
+
374
+ </InputNumber.Decrement>
375
+ </InputNumber.Stepper>
376
+ </InputNumber.Input>
377
+ </InputNumber.Group>
378
+ </InputNumber.Wrapper>
379
+ );
380
+ }
381
+ ```
382
+
383
+ ## Controlled usage
384
+
385
+ The field is controlled: pass `value` (a `number`, or `null` for empty) and read
386
+ changes back through one of two callbacks.
387
+
388
+ | Callback | Fires | Use for |
389
+ | ------------------ | ----------------------------------------------------------- | ---------------------------------- |
390
+ | `onValueChange` | On every parsed change (typing, paste, stepper, arrow step) | Live-updating UI as the user types |
391
+ | `onValueCommitted` | When the user commits (blur, Enter, stepper, arrow step) | Saving / validating a final value |
392
+
393
+ Both emit `null` when the field is empty. Prefer `onValueCommitted` for
394
+ persistence so you are not writing on every keystroke.
395
+
396
+ ## Formatting semantics
397
+
398
+ `format` is forwarded to Base UI's `NumberField` `format` and accepts any
399
+ `Intl.NumberFormatOptions`. It changes the display only; the committed value is
400
+ always a plain number. Two things to know:
401
+
402
+ * **Percent** (`{ style: "percent" }`) treats the value as a ratio: `0.5`
403
+ renders `50%`, and the stepper moves in ratio units. If you want the value to
404
+ be the number itself (`50` → `50%`), use `{ style: "unit", unit: "percent" }`.
405
+ * With no `format`, typed decimals are preserved (up to 12 fractional digits)
406
+ and thousands grouping follows the locale default, so `1234.5` renders as
407
+ `1,234.5`. Pass `{ useGrouping: false }` to render without separators.
18
408
 
19
409
 
20
410
  ## Props
21
411
 
22
412
  ### Web
23
413
 
414
+ #### InputNumber
415
+
416
+ | Prop | Type | Required | Default | Description |
417
+ |------|------|----------|---------|-------------|
418
+ | `align` | `"center" | "right"` | No | — | |
419
+ | `aria-label` | `string` | No | — | |
420
+ | `aria-labelledby` | `string` | No | — | |
421
+ | `autoComplete` | `InputNumberAutoComplete` | No | — | |
422
+ | `className` | `string` | No | — | |
423
+ | `description` | `ReactNode` | No | — | |
424
+ | `disabled` | `boolean` | No | — | |
425
+ | `error` | `string` | No | — | Renders a styled error message below the field. |
426
+ | `format` | `NumberFormatOptions` | No | — | Number formatting for the displayed value, forwarded to Base UI `NumberField`'s native `format`. When omitted, typed ... |
427
+ | `id` | `string` | No | — | |
428
+ | `inline` | `boolean` | No | — | Shrink-wrap the field to its content (auto width). |
429
+ | `inputMode` | `"decimal" | "numeric"` | No | — | |
430
+ | `invalid` | `boolean` | No | — | Style the error border without showing an error message. |
431
+ | `label` | `string` | No | — | Floating field label. |
432
+ | `loading` | `boolean` | No | — | Shows a non-blocking loading indicator in the stepper's slot. The field stays editable (use `readOnly`/`disabled` to ... |
433
+ | `max` | `number` | No | — | |
434
+ | `min` | `number` | No | — | |
435
+ | `name` | `string` | No | — | |
436
+ | `onBlur` | `(event?: FocusEvent<HTMLInputElement, Element>) => void` | No | — | Also fires once per Enter press: committing on Enter is implemented as a `blur()`/`focus()` round-trip, so `onBlur` a... |
437
+ | `onEnter` | `(event: KeyboardEvent<HTMLInputElement>) => void` | No | — | Fires when Enter is pressed without modifier keys (Shift/Ctrl/Meta). |
438
+ | `onFocus` | `(event?: FocusEvent<HTMLInputElement, Element>) => void` | No | — | Also fires once per Enter press; see `onBlur` for the commit mechanics. |
439
+ | `onKeyDown` | `(event: KeyboardEvent<HTMLInputElement>) => void` | No | — | |
440
+ | `onKeyUp` | `(event: KeyboardEvent<HTMLInputElement>) => void` | No | — | |
441
+ | `onValueChange` | `(newValue: number) => void` | No | — | Fires on every parsed value change (typing, paste, stepper, arrow step). Emits `null` when the field is empty. |
442
+ | `onValueCommitted` | `(newValue: number) => void` | No | — | Fires when the user commits a value (blur, Enter, stepper, arrow step). Emits `null` when committed empty. For per-ke... |
443
+ | `prefix` | `InputNumberAffix` | No | — | |
444
+ | `readOnly` | `boolean` | No | — | |
445
+ | `ref` | `Ref<InputNumberRef>` | No | — | |
446
+ | `showMiniLabel` | `boolean` | No | — | Default `true`. When `false`, the floating label is hidden. |
447
+ | `size` | `InputNumberSize` | No | — | |
448
+ | `step` | `"any" | number` | No | — | Amount the stepper buttons and ArrowUp/ArrowDown keys change the value by. Default `"any"`, which steps by `1` while ... |
449
+ | `style` | `CSSProperties` | No | — | |
450
+ | `suffix` | `InputNumberSuffixProp` | No | — | |
451
+ | `value` | `number` | No | — | Controlled value. `number` sets the value; `null` (or `undefined`) is an empty field. |
452
+
453
+ #### InputNumber.Affix
454
+
455
+ | Prop | Type | Required | Default | Description |
456
+ |------|------|----------|---------|-------------|
457
+ | `variation` | `"prefix" | "suffix"` | Yes | — | |
458
+ | `ariaLabel` | `string` | No | — | |
459
+ | `children` | `ReactNode` | No | — | Arbitrary affix content, beyond the built-in `label`/`icon`. |
460
+ | `className` | `string` | No | — | |
461
+ | `icon` | `IconNames` | No | — | |
462
+ | `label` | `string` | No | — | |
463
+ | `onClick` | `() => void` | No | — | |
464
+ | `style` | `CSSProperties` | No | — | |
465
+
466
+ #### InputNumber.Decrement
467
+
468
+ | Prop | Type | Required | Default | Description |
469
+ |------|------|----------|---------|-------------|
470
+ | `ariaLabel` | `string` | No | — | Accessible label for the button. |
471
+ | `children` | `ReactNode` | No | — | Icon content. Falls back to the default Atlantis stepper icon. |
472
+ | `className` | `string` | No | — | |
473
+ | `style` | `CSSProperties` | No | — | |
474
+
475
+ #### InputNumber.Description
476
+
477
+ | Prop | Type | Required | Default | Description |
478
+ |------|------|----------|---------|-------------|
479
+ | `className` | `string` | No | — | |
480
+ | `style` | `CSSProperties` | No | — | |
481
+
482
+ #### InputNumber.Error
483
+
484
+ | Prop | Type | Required | Default | Description |
485
+ |------|------|----------|---------|-------------|
486
+ | `children` | `string` | No | — | Error message. Rendered with an alert icon via the `HelperText` primitive. |
487
+ | `className` | `string` | No | — | |
488
+ | `style` | `CSSProperties` | No | — | |
489
+
490
+ #### InputNumber.Footer
491
+
492
+ | Prop | Type | Required | Default | Description |
493
+ |------|------|----------|---------|-------------|
494
+ | `className` | `string` | No | — | |
495
+ | `style` | `CSSProperties` | No | — | |
496
+
497
+ #### InputNumber.Group
498
+
499
+ | Prop | Type | Required | Default | Description |
500
+ |------|------|----------|---------|-------------|
501
+ | `className` | `string` | No | — | |
502
+ | `style` | `CSSProperties` | No | — | |
503
+
504
+ #### InputNumber.Increment
505
+
506
+ | Prop | Type | Required | Default | Description |
507
+ |------|------|----------|---------|-------------|
508
+ | `ariaLabel` | `string` | No | — | Accessible label for the button. |
509
+ | `children` | `ReactNode` | No | — | Icon content. Falls back to the default Atlantis stepper icon. |
510
+ | `className` | `string` | No | — | |
511
+ | `style` | `CSSProperties` | No | — | |
512
+
513
+ #### InputNumber.Input
514
+
515
+ | Prop | Type | Required | Default | Description |
516
+ |------|------|----------|---------|-------------|
517
+ | `children` | `ReactNode` | No | — | Content rendered inside the input area (e.g. `.Label`, `.Stepper`). |
518
+ | `className` | `string` | No | — | |
519
+ | `style` | `CSSProperties` | No | — | |
520
+
521
+ #### InputNumber.Label
522
+
523
+ | Prop | Type | Required | Default | Description |
524
+ |------|------|----------|---------|-------------|
525
+ | `className` | `string` | No | — | |
526
+ | `style` | `CSSProperties` | No | — | |
527
+
528
+ #### InputNumber.Loading
529
+
530
+ | Prop | Type | Required | Default | Description |
531
+ |------|------|----------|---------|-------------|
532
+ | `children` | `ReactNode` | No | — | Indicator content. Falls back to the default `ActivityIndicator`. |
533
+ | `className` | `string` | No | — | |
534
+ | `style` | `CSSProperties` | No | — | |
535
+
536
+ #### InputNumber.Stepper
537
+
538
+ | Prop | Type | Required | Default | Description |
539
+ |------|------|----------|---------|-------------|
540
+ | `children` | `ReactNode` | No | — | Stepper buttons. Falls back to the default increment/decrement pair. |
541
+ | `className` | `string` | No | — | |
542
+ | `decrementLabel` | `string` | No | — | Accessible label for the decrement button. Defaults to `Decrease value`. |
543
+ | `incrementLabel` | `string` | No | — | Accessible label for the increment button. Defaults to `Increase value`. |
544
+ | `style` | `CSSProperties` | No | — | |
545
+
546
+ #### InputNumber.Wrapper
547
+
24
548
  | Prop | Type | Required | Default | Description |
25
549
  |------|------|----------|---------|-------------|
26
- | `align` | `"center" | "right"` | No | — | Determines the alignment of the text inside the input. |
27
- | `aria-activedescendant` | `string` | No | — | ID of the currently active descendant element. Used for composite widgets like combobox or listbox. @see {@link https... |
28
- | `aria-autocomplete` | `"both" | "inline" | "list" | "none"` | No | — | Indicates the type of autocomplete interaction. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-autocomplete} |
29
- | `aria-controls` | `string` | No | — | Indicates the element that controls the current element. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-controls} |
30
- | `aria-describedby` | `string` | No | — | Identifies the element (or elements) that describes the object. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-... |
31
- | `aria-details` | `string` | No | — | Identifies the element (or elements) that provide a detailed, extended description. @see {@link https://www.w3.org/TR... |
32
- | `aria-expanded` | `Booleanish` | No | — | Indicates whether the element is expanded or collapsed. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-expanded} |
33
- | `aria-label` | `string` | No | — | Defines a string value that labels the current element. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-label} |
34
- | `aria-labelledby` | `string` | No | — | Identifies the element (or elements) that labels the current element. @see {@link https://www.w3.org/TR/wai-aria-1.2/... |
35
- | `aria-required` | `Booleanish` | No | — | Indicates that user input is required before form submission. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-re... |
36
- | `autoComplete` | `string` | No | — | Autocomplete behavior for the input (React casing, string values only). Use standard HTML autocomplete values or "on"... |
37
- | `autoFocus` | `boolean` | No | — | Whether the input should be auto-focused (React casing). |
38
- | `defaultValue` | `number` | No | — | |
39
- | `description` | `ReactNode` | No | — | Further description of the input, can be used for a hint. |
40
- | `disabled` | `boolean` | No | — | Whether the input is disabled. |
41
- | `error` | `string` | No | — | Error message to display. This also highlights the field red. |
42
- | `formatOptions` | `NumberFormatOptions` | No | — | |
43
- | `id` | `string` | No | — | The unique identifier for the input element. |
44
- | `inline` | `boolean` | No | — | Adjusts the form field to go inline with content. |
45
- | `inputMode` | `"decimal" | "email" | "none" | "numeric" | "search" | "tel" | "text" | "url"` | No | — | Input mode hint for virtual keyboards. |
46
- | `invalid` | `boolean` | No | — | Highlights the field red to indicate an error. |
47
- | `loading` | `boolean` | No | — | Show a spinner to indicate loading. |
48
- | `maxLength` | `number` | No | — | Maximum number of characters allowed in the input. |
49
- | `maxValue` | `number` | No | — | |
50
- | `minValue` | `number` | No | — | |
51
- | `name` | `string` | No | — | The name attribute for the input element. |
52
- | `onBlur` | `(event: FocusEvent<HTMLInputElement, Element>) => void` | No | — | Blur event handler. |
53
- | `onChange` | `(newValue: number, event?: ChangeEvent<HTMLInputElement>) => void` | No | — | Callback for value changes. @param newValue - The new numeric value @param event - Optional change event |
54
- | `onClick` | `(event: MouseEvent<HTMLInputElement, MouseEvent>) => void` | No | — | Click event handler. |
55
- | `onFocus` | `(event: FocusEvent<HTMLInputElement, Element>) => void` | No | — | Focus event handler. |
56
- | `onKeyDown` | `(event: KeyboardEvent<HTMLInputElement>) => void` | No | — | Key down event handler. |
57
- | `onKeyUp` | `(event: KeyboardEvent<HTMLInputElement>) => void` | No | — | Key up event handler. |
58
- | `onMouseDown` | `(event: MouseEvent<HTMLInputElement, MouseEvent>) => void` | No | — | Mouse down event handler. |
59
- | `onMouseUp` | `(event: MouseEvent<HTMLInputElement, MouseEvent>) => void` | No | — | Mouse up event handler. |
60
- | `onPointerDown` | `(event: PointerEvent<HTMLInputElement>) => void` | No | — | Pointer down event handler. |
61
- | `onPointerUp` | `(event: PointerEvent<HTMLInputElement>) => void` | No | — | Pointer up event handler. |
62
- | `pattern` | `string` | No | — | Validation pattern (regex) for the input. |
63
- | `placeholder` | `string` | No | — | Text that appears inside the input when empty and floats above the value as a mini label once the user enters a value... |
64
- | `readOnly` | `boolean` | No | — | Whether the input is read-only (HTML standard casing). |
65
- | `ref` | `Ref<InputNumberRef>` | No | — | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (... |
66
- | `required` | `boolean` | No | — | Whether the input is required before form submission. |
67
- | `role` | `string` | No | — | Role attribute for accessibility. |
68
- | `showMiniLabel` | `boolean` | No | — | |
69
- | `size` | `"large" | "small"` | No | — | Adjusts the interface to either have small or large spacing. |
70
- | `tabIndex` | `number` | No | — | Tab index for keyboard navigation. |
71
- | `value` | `number` | No | — | |
550
+ | `align` | `"center" | "right"` | No | — | |
551
+ | `aria-label` | `string` | No | — | |
552
+ | `aria-labelledby` | `string` | No | — | |
553
+ | `autoComplete` | `InputNumberAutoComplete` | No | — | |
554
+ | `children` | `ReactNode` | No | — | Composed parts (`.Group`, `.Footer`, and the parts within them). |
555
+ | `className` | `string` | No | — | |
556
+ | `disabled` | `boolean` | No | — | |
557
+ | `format` | `NumberFormatOptions` | No | — | Number formatting for the displayed value, forwarded to Base UI `NumberField`'s native `format`. When omitted, typed ... |
558
+ | `id` | `string` | No | — | |
559
+ | `inline` | `boolean` | No | — | Shrink-wrap the field to its content (auto width). |
560
+ | `inputMode` | `"decimal" | "numeric"` | No | — | |
561
+ | `invalid` | `boolean` | No | — | Style the error border without showing an error message. |
562
+ | `loading` | `boolean` | No | — | Shows a non-blocking loading indicator in the stepper's slot. The field stays editable (use `readOnly`/`disabled` to ... |
563
+ | `max` | `number` | No | — | |
564
+ | `min` | `number` | No | — | |
565
+ | `name` | `string` | No | — | |
566
+ | `onBlur` | `(event?: FocusEvent<HTMLInputElement, Element>) => void` | No | — | Also fires once per Enter press: committing on Enter is implemented as a `blur()`/`focus()` round-trip, so `onBlur` a... |
567
+ | `onEnter` | `(event: KeyboardEvent<HTMLInputElement>) => void` | No | — | Fires when Enter is pressed without modifier keys (Shift/Ctrl/Meta). |
568
+ | `onFocus` | `(event?: FocusEvent<HTMLInputElement, Element>) => void` | No | — | Also fires once per Enter press; see `onBlur` for the commit mechanics. |
569
+ | `onKeyDown` | `(event: KeyboardEvent<HTMLInputElement>) => void` | No | — | |
570
+ | `onKeyUp` | `(event: KeyboardEvent<HTMLInputElement>) => void` | No | — | |
571
+ | `onValueChange` | `(newValue: number) => void` | No | — | Fires on every parsed value change (typing, paste, stepper, arrow step). Emits `null` when the field is empty. |
572
+ | `onValueCommitted` | `(newValue: number) => void` | No | — | Fires when the user commits a value (blur, Enter, stepper, arrow step). Emits `null` when committed empty. For per-ke... |
573
+ | `readOnly` | `boolean` | No | — | |
574
+ | `ref` | `Ref<InputNumberRef>` | No | — | |
575
+ | `showMiniLabel` | `boolean` | No | — | Default `true`. When `false`, the floating label is hidden. |
576
+ | `size` | `InputNumberSize` | No | — | |
577
+ | `step` | `"any" | number` | No | — | Amount the stepper buttons and ArrowUp/ArrowDown keys change the value by. Default `"any"`, which steps by `1` while ... |
578
+ | `style` | `CSSProperties` | No | — | |
579
+ | `value` | `number` | No | — | Controlled value. `number` sets the value; `null` (or `undefined`) is an empty field. |
@@ -337,7 +337,7 @@ composable actions, this is the simplest way to use Page:
337
337
 
338
338
  | Prop | Type | Required | Default | Description |
339
339
  |------|------|----------|---------|-------------|
340
- | `triggerLabel` | `string` | No | `More Actions` | |
340
+ | `triggerLabel` | `string` | No | | |
341
341
 
342
342
  #### Page.PrimaryButton
343
343
 
@@ -54,7 +54,6 @@
54
54
  [InputFile](./InputFile/InputFile.md)
55
55
  [InputGroup](./InputGroup/InputGroup.md)
56
56
  [InputNumber](./InputNumber/InputNumber.md)
57
- [InputNumberExperimental](./InputNumberExperimental/InputNumberExperimental.md)
58
57
  [InputPhoneNumber](./InputPhoneNumber/InputPhoneNumber.md)
59
58
  [InputText](./InputText/InputText.md)
60
59
  [InputTime](./InputTime/InputTime.md)