@ahrowe/ui 0.22.0 → 0.24.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 (86) hide show
  1. package/dist/esm/common/alert/alert.types.mjs.map +1 -1
  2. package/dist/esm/common/badge/badge.types.mjs.map +1 -1
  3. package/dist/esm/common/button/button.mjs +1 -1
  4. package/dist/esm/common/button/button.mjs.map +1 -1
  5. package/dist/esm/common/button/button.module.mjs +1 -1
  6. package/dist/esm/common/button/button.module.mjs.map +1 -1
  7. package/dist/esm/common/button/button.types.mjs +1 -1
  8. package/dist/esm/common/button/button.types.mjs.map +1 -1
  9. package/dist/esm/common/card/Card.mjs +1 -1
  10. package/dist/esm/common/card/Card.mjs.map +1 -1
  11. package/dist/esm/common/card/card.types.mjs.map +1 -1
  12. package/dist/esm/common/carousel/carousel.mjs +1 -1
  13. package/dist/esm/common/carousel/carousel.mjs.map +1 -1
  14. package/dist/esm/common/carousel/carousel.module.mjs +1 -1
  15. package/dist/esm/common/carousel/carousel.module.mjs.map +1 -1
  16. package/dist/esm/common/chip/chip.mjs +1 -1
  17. package/dist/esm/common/chip/chip.mjs.map +1 -1
  18. package/dist/esm/common/chip/chip.types.mjs.map +1 -1
  19. package/dist/esm/common/dropZone/dropZone.types.mjs.map +1 -1
  20. package/dist/esm/common/floatingMenu/floatingMenu.types.mjs.map +1 -1
  21. package/dist/esm/common/hooks/useCoarsePointer.mjs +2 -0
  22. package/dist/esm/common/hooks/useCoarsePointer.mjs.map +1 -0
  23. package/dist/esm/common/hooks/useSwipeDismiss.mjs +1 -1
  24. package/dist/esm/common/hooks/useSwipeDismiss.mjs.map +1 -1
  25. package/dist/esm/common/iconPicker/iconPicker.mjs +1 -1
  26. package/dist/esm/common/iconPicker/iconPicker.mjs.map +1 -1
  27. package/dist/esm/common/input/input.types.mjs.map +1 -1
  28. package/dist/esm/common/interactableDiv/interactableDiv.mjs +1 -1
  29. package/dist/esm/common/interactableDiv/interactableDiv.mjs.map +1 -1
  30. package/dist/esm/common/numberInput/index.mjs.map +1 -1
  31. package/dist/esm/common/numberInput/numberInput.mjs +1 -1
  32. package/dist/esm/common/numberInput/numberInput.mjs.map +1 -1
  33. package/dist/esm/common/numberInput/numberInput.module.mjs +1 -1
  34. package/dist/esm/common/numberInput/numberInput.module.mjs.map +1 -1
  35. package/dist/esm/common/numberInput/useStepRepeat.mjs +2 -0
  36. package/dist/esm/common/numberInput/useStepRepeat.mjs.map +1 -0
  37. package/dist/esm/common/otpInput/otpInput.types.mjs.map +1 -1
  38. package/dist/esm/common/popover/popover.types.mjs.map +1 -1
  39. package/dist/esm/common/radioGroup/radioGroup.types.mjs.map +1 -1
  40. package/dist/esm/common/sectionHeader/sectionHeader.types.mjs.map +1 -1
  41. package/dist/index.cjs +6 -6
  42. package/dist/index.cjs.map +1 -1
  43. package/dist/style.css +1 -1
  44. package/dist/types/package/common/alert/alert.types.d.ts +1 -1
  45. package/dist/types/package/common/animatedLogo/animatedLogo.types.d.ts +11 -3
  46. package/dist/types/package/common/avatar/avatar.types.d.ts +1 -1
  47. package/dist/types/package/common/badge/badge.types.d.ts +1 -1
  48. package/dist/types/package/common/button/button.types.d.ts +10 -4
  49. package/dist/types/package/common/card/card.types.d.ts +1 -1
  50. package/dist/types/package/common/carousel/carousel.types.d.ts +3 -1
  51. package/dist/types/package/common/checkbox/checkbox.types.d.ts +1 -1
  52. package/dist/types/package/common/chip/chip.types.d.ts +2 -0
  53. package/dist/types/package/common/colorPicker/colorPicker.types.d.ts +1 -1
  54. package/dist/types/package/common/configProvider/configProvider.types.d.ts +1 -1
  55. package/dist/types/package/common/dropZone/dropZone.types.d.ts +1 -1
  56. package/dist/types/package/common/dropdown/dropdown.types.d.ts +1 -1
  57. package/dist/types/package/common/errorBoundary/errorBoundary.types.d.ts +1 -1
  58. package/dist/types/package/common/floatingMenu/floatingMenu.types.d.ts +1 -1
  59. package/dist/types/package/common/hooks/useCoarsePointer.d.ts +10 -0
  60. package/dist/types/package/common/iconPicker/iconPicker.types.d.ts +1 -1
  61. package/dist/types/package/common/input/input.types.d.ts +2 -2
  62. package/dist/types/package/common/inputDropdown/inputDropdown.types.d.ts +1 -1
  63. package/dist/types/package/common/interactableDiv/interactableDiv.d.ts +2 -2
  64. package/dist/types/package/common/kanbanBoard/kanbanBoard.types.d.ts +1 -1
  65. package/dist/types/package/common/numberInput/index.d.ts +1 -0
  66. package/dist/types/package/common/numberInput/numberInput.d.ts +3 -32
  67. package/dist/types/package/common/numberInput/numberInput.types.d.ts +61 -0
  68. package/dist/types/package/common/numberInput/useStepRepeat.d.ts +17 -0
  69. package/dist/types/package/common/optionPicker/optionPicker.types.d.ts +1 -1
  70. package/dist/types/package/common/otpInput/otpInput.types.d.ts +1 -1
  71. package/dist/types/package/common/popover/popover.types.d.ts +1 -1
  72. package/dist/types/package/common/radioGroup/radioGroup.types.d.ts +1 -1
  73. package/dist/types/package/common/rating/rating.types.d.ts +1 -1
  74. package/dist/types/package/common/searchInput/searchInput.types.d.ts +1 -1
  75. package/dist/types/package/common/sectionHeader/sectionHeader.types.d.ts +1 -1
  76. package/dist/types/package/common/slider/slider.types.d.ts +1 -1
  77. package/dist/types/package/common/switch/switch.types.d.ts +1 -1
  78. package/dist/types/package/common/tabHeader/tabHeader.types.d.ts +1 -1
  79. package/dist/types/package/common/timeInput/timeInput.types.d.ts +1 -1
  80. package/dist/types/package/common/types/baseProps.types.d.ts +20 -5
  81. package/dist/types/package/common/types/slots.types.d.ts +1 -1
  82. package/docs/Button.md +23 -2
  83. package/docs/Carousel.md +19 -0
  84. package/docs/Chip.md +1 -0
  85. package/docs/NumberInput.md +66 -2
  86. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
 
3
3
  **When to use:** Numeric input with formatting — prices, quantities, percentages, phone numbers with separators. Wraps `react-number-format` with the same floating-label style as `Input`.
4
4
 
5
- **Keywords:** currency input, quantity field, numeric field
5
+ **Keywords:** currency input, quantity field, numeric field, spinner, stepper, up down arrows, increment, decrement, plus minus, range, bounds, disabled
6
6
 
7
7
  **Import:** `import { NumberInput } from '@ahrowe/ui'`
8
8
 
@@ -32,7 +32,18 @@ const priceValidator = new FormValidator<number | undefined>(undefined, [Validat
32
32
  <NumberInput label="Price" formValidator={priceValidator} prefix="€" decimalScale={2} />
33
33
 
34
34
  // Percentage
35
- <NumberInput label="Discount" suffix="%" decimalScale={1} max={100} />
35
+ <NumberInput label="Discount" suffix="%" decimalScale={1} />
36
+
37
+ // Stepper buttons — hold to repeat, shift for ±10, clamped to min/max
38
+ <NumberInput
39
+ label="Guests"
40
+ value={guests}
41
+ onChange={setGuests}
42
+ decimalScale={0}
43
+ showArrows
44
+ min={0}
45
+ max={10}
46
+ />
36
47
 
37
48
  // Max decimals without padding — cap at 2 decimals, but keep what the user typed
38
49
  // (12 stays "12", 12.1 stays "12.1"; 12.111 is truncated to "12.11")
@@ -58,5 +69,58 @@ const priceValidator = new FormValidator<number | undefined>(undefined, [Validat
58
69
  | `isRequired` | `boolean` | Shows the required mark (`*`) next to the label. Also inferred automatically when `formValidator` has a `required` validator. Default: `false` |
59
70
  | `placeholder` | `string` | |
60
71
  | `error` | `string` | Manual error message |
72
+ | `showArrows` | `boolean` | Show stepper buttons. Default: `false` |
73
+ | `step` | `number` | Amount one step changes the value by. Default: `1` (hold shift for ×10) |
74
+ | `min` | `number` | Lower bound — see the note below |
75
+ | `max` | `number` | Upper bound — see the note below |
76
+ | `arrowsLayout` | `'auto' \| 'stacked' \| 'split'` | Stepper layout. Default: `'auto'` |
77
+ | `increaseLabel` | `string` | Accessible name for the increase button. Default: `'Increase'` |
78
+ | `decreaseLabel` | `string` | Accessible name for the decrease button. Default: `'Decrease'` |
79
+
80
+ **Steppers (`showArrows`):** two layouts, picked by `arrowsLayout`:
81
+
82
+ - `'stacked'` — chevrons inside the field's right edge, each roughly 27x17 at the default text
83
+ size. Compact and mouse-oriented: two targets stacked inside a 34px field cannot reach the 24px
84
+ minimum that WCAG 2.5.8 asks for, which is why `'auto'` prefers `'split'` on touch.
85
+ - `'split'` — full-height `−` / `+` buttons flanking the field, each a 44px touch target. The two
86
+ are far apart, so a mis-tap can't step the value the wrong way.
87
+ - `'auto'` (default) — `split` on a coarse (touch) pointer, `stacked` otherwise, detected with
88
+ `matchMedia('(pointer: coarse)')` and re-evaluated when the pointer type changes.
89
+
90
+ The split buttons are the shared `Button` component, so press feedback is the same ripple as everywhere else in the library.
91
+
92
+ Press and hold a button to repeat: one step immediately, then a repeat after 400ms that accelerates
93
+ after about a second. Hold shift for ×10. The field itself carries `role="spinbutton"` with
94
+ `aria-valuenow` / `aria-valuemin` / `aria-valuemax`, so ↑/↓ stepping is announced; the buttons stay
95
+ out of the tab order rather than adding two tab stops per field.
96
+
97
+ **`min` / `max` clamp, they do not block typing.** Both bounds constrain the steppers and the
98
+ ↑/↓ keys, disable the corresponding button once reached, and pull a typed value back into range
99
+ when it stops being mid-edit: on blur, and on Enter (which submits a surrounding form without ever
100
+ blurring the field). An empty field is never clamped, so blurring an untouched field does not fill
101
+ in `min`.
102
+
103
+ Nothing is rejected as it is typed, deliberately. Editing inserts digits into an existing value, so
104
+ the intermediate state routinely leaves the range even when the destination is inside it — with
105
+ `max={10}`, putting the caret in front of `8` and typing `1` on the way to `18` would simply do
106
+ nothing, with no feedback. Blocking input suits *format* rules (digits only, a fixed length), where
107
+ every rejected keystroke is unambiguously wrong; a value range is not one of those.
108
+
109
+ The Enter clamp is exact for a `formValidator`-driven field, because `formValidator.set` is
110
+ imperative and lands before the submit handler runs. For a plain `value`/`onChange` field it is
111
+ best-effort: the consumer's own state update may not have flushed by the time their `onSubmit`
112
+ reads it. If an out-of-range value must be *impossible* to submit rather than merely corrected, add
113
+ a validator — `@ahrowe/form-validation` has no numeric range validator (`minLength`/`maxLength` are
114
+ string length), so write one:
115
+
116
+ ```tsx
117
+ const inRange = (min: number, max: number): ValidatorFunction => (value) =>
118
+ typeof value === 'number' && (value < min || value > max)
119
+ ? { id: 'range', message: `Must be between ${min} and ${max}` }
120
+ : null;
121
+
122
+ const qty = new FormValidator<number | undefined>(undefined, [inRange(10, 100)]);
123
+ <NumberInput label="Quantity" formValidator={qty} min={10} max={100} showArrows decimalScale={0} />
124
+ ```
61
125
 
62
126
  **Mobile keyboard layout:** shows the numeric on-screen keyboard automatically (`inputMode="numeric"` when `decimalScale={0}`, `"decimal"` otherwise, so a decimal separator key is available whenever fractional values are allowed). Pass `inputMode` directly to override.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahrowe/ui",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },