@central-ui/react 0.1.2 → 0.2.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 (279) hide show
  1. package/LICENSE +49 -0
  2. package/dist/components/accordion/index.d.ts +4 -3
  3. package/dist/components/accordion/index.js +7 -4
  4. package/dist/components/autocomplete/index.d.ts +14 -25
  5. package/dist/components/autocomplete/index.js +70 -54
  6. package/dist/components/avatar/index.d.ts +1 -2
  7. package/dist/components/avatar/index.js +0 -1
  8. package/dist/components/badge/index.d.ts +0 -1
  9. package/dist/components/badge/index.js +2 -2
  10. package/dist/components/banner/index.d.ts +1 -1
  11. package/dist/components/banner/index.js +23 -25
  12. package/dist/components/barchart/index.d.ts +106 -0
  13. package/dist/components/barchart/index.js +122 -0
  14. package/dist/components/breadcrumb/index.d.ts +0 -1
  15. package/dist/components/breadcrumb/index.js +6 -7
  16. package/dist/components/button/index.d.ts +2 -3
  17. package/dist/components/button/index.js +11 -12
  18. package/dist/components/card/index.d.ts +0 -1
  19. package/dist/components/card/index.js +0 -1
  20. package/dist/components/checkbox/index.d.ts +13 -11
  21. package/dist/components/checkbox/index.js +51 -31
  22. package/dist/components/chip/index.d.ts +1 -1
  23. package/dist/components/chip/index.js +2 -3
  24. package/dist/components/combobox/index.d.ts +18 -30
  25. package/dist/components/combobox/index.js +90 -67
  26. package/dist/components/datepicker/index.d.ts +18 -31
  27. package/dist/components/datepicker/index.js +81 -99
  28. package/dist/components/dialog/index.d.ts +14 -27
  29. package/dist/components/dialog/index.js +17 -18
  30. package/dist/components/divider/index.d.ts +0 -1
  31. package/dist/components/divider/index.js +0 -1
  32. package/dist/components/donutchart/index.d.ts +100 -0
  33. package/dist/components/donutchart/index.js +178 -0
  34. package/dist/components/drawer/index.d.ts +12 -21
  35. package/dist/components/drawer/index.js +34 -36
  36. package/dist/components/empty/index.d.ts +1 -2
  37. package/dist/components/empty/index.js +2 -4
  38. package/dist/components/fileupload/index.d.ts +4 -9
  39. package/dist/components/fileupload/index.js +26 -38
  40. package/dist/components/form/index.d.ts +0 -1
  41. package/dist/components/form/index.js +0 -1
  42. package/dist/components/horizontalbarchart/index.d.ts +120 -0
  43. package/dist/components/horizontalbarchart/index.js +193 -0
  44. package/dist/components/input/index.d.ts +22 -22
  45. package/dist/components/input/index.js +76 -105
  46. package/dist/components/linechart/index.d.ts +114 -0
  47. package/dist/components/linechart/index.js +139 -0
  48. package/dist/components/menu/index.d.ts +9 -11
  49. package/dist/components/menu/index.js +42 -39
  50. package/dist/components/metric/index.d.ts +0 -1
  51. package/dist/components/metric/index.js +0 -1
  52. package/dist/components/numberinput/index.d.ts +14 -21
  53. package/dist/components/numberinput/index.js +40 -53
  54. package/dist/components/otpinput/index.d.ts +14 -9
  55. package/dist/components/otpinput/index.js +25 -18
  56. package/dist/components/page/index.d.ts +0 -1
  57. package/dist/components/page/index.js +1 -3
  58. package/dist/components/passwordinput/index.d.ts +14 -11
  59. package/dist/components/passwordinput/index.js +36 -45
  60. package/dist/components/popover/index.d.ts +21 -19
  61. package/dist/components/popover/index.js +34 -15
  62. package/dist/components/radio/index.d.ts +13 -9
  63. package/dist/components/radio/index.js +69 -60
  64. package/dist/components/ratinginput/index.d.ts +13 -8
  65. package/dist/components/ratinginput/index.js +37 -17
  66. package/dist/components/select/index.d.ts +25 -28
  67. package/dist/components/select/index.js +67 -38
  68. package/dist/components/skeleton/index.d.ts +0 -1
  69. package/dist/components/skeleton/index.js +0 -1
  70. package/dist/components/slider/index.d.ts +14 -9
  71. package/dist/components/slider/index.js +64 -63
  72. package/dist/components/spinner/index.d.ts +1 -2
  73. package/dist/components/spinner/index.js +2 -3
  74. package/dist/components/splitter/index.d.ts +1 -2
  75. package/dist/components/splitter/index.js +5 -6
  76. package/dist/components/stepper/index.d.ts +8 -20
  77. package/dist/components/stepper/index.js +15 -16
  78. package/dist/components/switch/index.d.ts +12 -10
  79. package/dist/components/switch/index.js +46 -30
  80. package/dist/components/table/index.d.ts +3 -3
  81. package/dist/components/table/index.js +14 -13
  82. package/dist/components/tabs/index.d.ts +0 -1
  83. package/dist/components/tabs/index.js +1 -2
  84. package/dist/components/textarea/index.d.ts +14 -11
  85. package/dist/components/textarea/index.js +37 -46
  86. package/dist/components/textlink/index.d.ts +3 -1
  87. package/dist/components/textlink/index.js +4 -1
  88. package/dist/components/toast/index.d.ts +8 -14
  89. package/dist/components/toast/index.js +48 -48
  90. package/dist/components/toggle/index.d.ts +0 -1
  91. package/dist/components/toggle/index.js +17 -24
  92. package/dist/components/tooltip/index.d.ts +11 -13
  93. package/dist/components/tooltip/index.js +12 -9
  94. package/dist/components/tour/index.d.ts +154 -0
  95. package/dist/components/tour/index.js +280 -0
  96. package/dist/index.d.ts +5 -2
  97. package/dist/index.js +5 -1
  98. package/dist/types/Icon.d.ts +0 -1
  99. package/dist/types/Icon.js +0 -1
  100. package/dist/types/Style.d.ts +0 -1
  101. package/dist/types/Style.js +0 -1
  102. package/dist/types/Tone.d.ts +0 -1
  103. package/dist/types/Tone.js +0 -1
  104. package/dist/types/Validation.d.ts +12 -0
  105. package/dist/types/Validation.js +1 -0
  106. package/dist/utils/ArkStateToDataAttributes.d.ts +0 -1
  107. package/dist/utils/ArkStateToDataAttributes.js +0 -1
  108. package/dist/utils/Tone.d.ts +0 -1
  109. package/dist/utils/Tone.js +0 -1
  110. package/dist/utils/Validation.d.ts +14 -0
  111. package/dist/utils/Validation.js +23 -0
  112. package/dist/utils/asChild.d.ts +41 -0
  113. package/dist/utils/asChild.js +15 -0
  114. package/dist/utils/icons.d.ts +24 -0
  115. package/dist/utils/icons.js +160 -0
  116. package/dist/utils/useForwardClickToInput.d.ts +0 -1
  117. package/dist/utils/useForwardClickToInput.js +0 -1
  118. package/dist/utils/virtualization/useVirtualizedAutocomplete.d.ts +0 -1
  119. package/dist/utils/virtualization/useVirtualizedAutocomplete.js +0 -1
  120. package/dist/utils/virtualization/useVirtualizedCombobox.d.ts +0 -1
  121. package/dist/utils/virtualization/useVirtualizedCombobox.js +0 -1
  122. package/dist/utils/virtualization/useVirtualizedTable.d.ts +0 -1
  123. package/dist/utils/virtualization/useVirtualizedTable.js +0 -1
  124. package/package.json +12 -11
  125. package/src/presets/shopify-polaris/shopify-polaris-autocomplete.css +2 -2
  126. package/src/presets/shopify-polaris/shopify-polaris-barchart.css +64 -0
  127. package/src/presets/shopify-polaris/shopify-polaris-checkbox.css +2 -2
  128. package/src/presets/shopify-polaris/shopify-polaris-combobox.css +2 -2
  129. package/src/presets/shopify-polaris/shopify-polaris-datepicker.css +4 -4
  130. package/src/presets/shopify-polaris/shopify-polaris-dialog.css +16 -0
  131. package/src/presets/shopify-polaris/shopify-polaris-donutchart.css +172 -0
  132. package/src/presets/shopify-polaris/shopify-polaris-drawer.css +16 -0
  133. package/src/presets/shopify-polaris/shopify-polaris-fileupload.css +1 -1
  134. package/src/presets/shopify-polaris/shopify-polaris-horizontalbarchart.css +60 -0
  135. package/src/presets/shopify-polaris/shopify-polaris-input.css +2 -2
  136. package/src/presets/shopify-polaris/shopify-polaris-linechart.css +68 -0
  137. package/src/presets/shopify-polaris/shopify-polaris-menu.css +16 -0
  138. package/src/presets/shopify-polaris/shopify-polaris-numberinput.css +2 -2
  139. package/src/presets/shopify-polaris/shopify-polaris-otpinput.css +2 -2
  140. package/src/presets/shopify-polaris/shopify-polaris-passwordinput.css +2 -2
  141. package/src/presets/shopify-polaris/shopify-polaris-popover.css +29 -0
  142. package/src/presets/shopify-polaris/shopify-polaris-radio.css +2 -2
  143. package/src/presets/shopify-polaris/shopify-polaris-ratinginput.css +2 -2
  144. package/src/presets/shopify-polaris/shopify-polaris-select.css +2 -2
  145. package/src/presets/shopify-polaris/shopify-polaris-slider.css +2 -2
  146. package/src/presets/shopify-polaris/shopify-polaris-stepper.css +16 -0
  147. package/src/presets/shopify-polaris/shopify-polaris-switch.css +2 -2
  148. package/src/presets/shopify-polaris/shopify-polaris-textarea.css +2 -2
  149. package/src/presets/shopify-polaris/shopify-polaris-tooltip.css +16 -0
  150. package/src/presets/shopify-polaris/shopify-polaris.css +4 -0
  151. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-autocomplete.css +2 -2
  152. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-barchart.css +64 -0
  153. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-checkbox.css +2 -2
  154. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-combobox.css +2 -2
  155. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-datepicker.css +2 -2
  156. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-dialog.css +9 -0
  157. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-donutchart.css +172 -0
  158. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-drawer.css +9 -0
  159. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-fileupload.css +1 -1
  160. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-horizontalbarchart.css +60 -0
  161. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-input.css +2 -2
  162. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-linechart.css +68 -0
  163. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-menu.css +9 -0
  164. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-numberinput.css +2 -2
  165. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-otpinput.css +2 -2
  166. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-passwordinput.css +2 -2
  167. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-popover.css +22 -0
  168. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-radio.css +2 -2
  169. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-ratinginput.css +2 -2
  170. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-slider.css +2 -2
  171. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-stepper.css +9 -0
  172. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-switch.css +2 -2
  173. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-textarea.css +2 -2
  174. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-tooltip.css +9 -0
  175. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy.css +4 -0
  176. package/dist/components/accordion/index.d.ts.map +0 -1
  177. package/dist/components/accordion/index.js.map +0 -1
  178. package/dist/components/autocomplete/index.d.ts.map +0 -1
  179. package/dist/components/autocomplete/index.js.map +0 -1
  180. package/dist/components/avatar/index.d.ts.map +0 -1
  181. package/dist/components/avatar/index.js.map +0 -1
  182. package/dist/components/badge/index.d.ts.map +0 -1
  183. package/dist/components/badge/index.js.map +0 -1
  184. package/dist/components/banner/index.d.ts.map +0 -1
  185. package/dist/components/banner/index.js.map +0 -1
  186. package/dist/components/breadcrumb/index.d.ts.map +0 -1
  187. package/dist/components/breadcrumb/index.js.map +0 -1
  188. package/dist/components/button/index.d.ts.map +0 -1
  189. package/dist/components/button/index.js.map +0 -1
  190. package/dist/components/card/index.d.ts.map +0 -1
  191. package/dist/components/card/index.js.map +0 -1
  192. package/dist/components/checkbox/index.d.ts.map +0 -1
  193. package/dist/components/checkbox/index.js.map +0 -1
  194. package/dist/components/chip/index.d.ts.map +0 -1
  195. package/dist/components/chip/index.js.map +0 -1
  196. package/dist/components/combobox/index.d.ts.map +0 -1
  197. package/dist/components/combobox/index.js.map +0 -1
  198. package/dist/components/datepicker/index.d.ts.map +0 -1
  199. package/dist/components/datepicker/index.js.map +0 -1
  200. package/dist/components/dialog/index.d.ts.map +0 -1
  201. package/dist/components/dialog/index.js.map +0 -1
  202. package/dist/components/divider/index.d.ts.map +0 -1
  203. package/dist/components/divider/index.js.map +0 -1
  204. package/dist/components/drawer/index.d.ts.map +0 -1
  205. package/dist/components/drawer/index.js.map +0 -1
  206. package/dist/components/empty/index.d.ts.map +0 -1
  207. package/dist/components/empty/index.js.map +0 -1
  208. package/dist/components/fileupload/index.d.ts.map +0 -1
  209. package/dist/components/fileupload/index.js.map +0 -1
  210. package/dist/components/form/index.d.ts.map +0 -1
  211. package/dist/components/form/index.js.map +0 -1
  212. package/dist/components/input/index.d.ts.map +0 -1
  213. package/dist/components/input/index.js.map +0 -1
  214. package/dist/components/menu/index.d.ts.map +0 -1
  215. package/dist/components/menu/index.js.map +0 -1
  216. package/dist/components/metric/index.d.ts.map +0 -1
  217. package/dist/components/metric/index.js.map +0 -1
  218. package/dist/components/numberinput/index.d.ts.map +0 -1
  219. package/dist/components/numberinput/index.js.map +0 -1
  220. package/dist/components/otpinput/index.d.ts.map +0 -1
  221. package/dist/components/otpinput/index.js.map +0 -1
  222. package/dist/components/page/index.d.ts.map +0 -1
  223. package/dist/components/page/index.js.map +0 -1
  224. package/dist/components/passwordinput/index.d.ts.map +0 -1
  225. package/dist/components/passwordinput/index.js.map +0 -1
  226. package/dist/components/popover/index.d.ts.map +0 -1
  227. package/dist/components/popover/index.js.map +0 -1
  228. package/dist/components/radio/index.d.ts.map +0 -1
  229. package/dist/components/radio/index.js.map +0 -1
  230. package/dist/components/ratinginput/index.d.ts.map +0 -1
  231. package/dist/components/ratinginput/index.js.map +0 -1
  232. package/dist/components/select/index.d.ts.map +0 -1
  233. package/dist/components/select/index.js.map +0 -1
  234. package/dist/components/skeleton/index.d.ts.map +0 -1
  235. package/dist/components/skeleton/index.js.map +0 -1
  236. package/dist/components/slider/index.d.ts.map +0 -1
  237. package/dist/components/slider/index.js.map +0 -1
  238. package/dist/components/spinner/index.d.ts.map +0 -1
  239. package/dist/components/spinner/index.js.map +0 -1
  240. package/dist/components/splitter/index.d.ts.map +0 -1
  241. package/dist/components/splitter/index.js.map +0 -1
  242. package/dist/components/stepper/index.d.ts.map +0 -1
  243. package/dist/components/stepper/index.js.map +0 -1
  244. package/dist/components/switch/index.d.ts.map +0 -1
  245. package/dist/components/switch/index.js.map +0 -1
  246. package/dist/components/table/index.d.ts.map +0 -1
  247. package/dist/components/table/index.js.map +0 -1
  248. package/dist/components/tabs/index.d.ts.map +0 -1
  249. package/dist/components/tabs/index.js.map +0 -1
  250. package/dist/components/textarea/index.d.ts.map +0 -1
  251. package/dist/components/textarea/index.js.map +0 -1
  252. package/dist/components/textlink/index.d.ts.map +0 -1
  253. package/dist/components/textlink/index.js.map +0 -1
  254. package/dist/components/toast/index.d.ts.map +0 -1
  255. package/dist/components/toast/index.js.map +0 -1
  256. package/dist/components/toggle/index.d.ts.map +0 -1
  257. package/dist/components/toggle/index.js.map +0 -1
  258. package/dist/components/tooltip/index.d.ts.map +0 -1
  259. package/dist/components/tooltip/index.js.map +0 -1
  260. package/dist/index.d.ts.map +0 -1
  261. package/dist/index.js.map +0 -1
  262. package/dist/types/Icon.d.ts.map +0 -1
  263. package/dist/types/Icon.js.map +0 -1
  264. package/dist/types/Style.d.ts.map +0 -1
  265. package/dist/types/Style.js.map +0 -1
  266. package/dist/types/Tone.d.ts.map +0 -1
  267. package/dist/types/Tone.js.map +0 -1
  268. package/dist/utils/ArkStateToDataAttributes.d.ts.map +0 -1
  269. package/dist/utils/ArkStateToDataAttributes.js.map +0 -1
  270. package/dist/utils/Tone.d.ts.map +0 -1
  271. package/dist/utils/Tone.js.map +0 -1
  272. package/dist/utils/useForwardClickToInput.d.ts.map +0 -1
  273. package/dist/utils/useForwardClickToInput.js.map +0 -1
  274. package/dist/utils/virtualization/useVirtualizedAutocomplete.d.ts.map +0 -1
  275. package/dist/utils/virtualization/useVirtualizedAutocomplete.js.map +0 -1
  276. package/dist/utils/virtualization/useVirtualizedCombobox.d.ts.map +0 -1
  277. package/dist/utils/virtualization/useVirtualizedCombobox.js.map +0 -1
  278. package/dist/utils/virtualization/useVirtualizedTable.d.ts.map +0 -1
  279. package/dist/utils/virtualization/useVirtualizedTable.js.map +0 -1
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { asChildRendering } from "../../utils/asChild.js";
2
3
  import { Menu as BaseMenu } from '@base-ui/react/menu';
3
4
  import { cloneElement, createContext, forwardRef, useContext, useEffect, useId, useState, } from 'react';
4
5
  import { twMerge } from 'tailwind-merge';
5
- import { Check, ChevronRight } from 'lucide-react';
6
+ import { Check, ChevronRight } from "../../utils/icons.js";
6
7
  /**
7
- * OpenHandler gives a button menu-trigger behavior. Popup already includes its portal and positioner. Choose ButtonItem for actions and LinkItem for navigation. Use the matching checkbox/radio composition for persistent choices; submenu parts belong in SubMenuRoot.
8
+ * OpenButton renders its own trigger button; pass asChild to give its behavior to your own button instead. Popup already includes its portal and positioner. Choose ButtonItem for actions and LinkItem for navigation. Use the matching checkbox/radio composition for persistent choices; submenu parts belong in SubMenuRoot.
8
9
  *
9
10
  * @example Complete composition
10
11
  * import { Button, Menu } from '@central-ui/react'
@@ -12,9 +13,9 @@ import { Check, ChevronRight } from 'lucide-react';
12
13
  * function Example() {
13
14
  * return (
14
15
  * <Menu.Root>
15
- * <Menu.OpenHandler>
16
+ * <Menu.OpenButton asChild>
16
17
  * <Button.default variant="primary">Options</Button.default>
17
- * </Menu.OpenHandler>
18
+ * </Menu.OpenButton>
18
19
  * <Menu.Popup>
19
20
  * <Menu.Group>
20
21
  * <Menu.GroupLabel>Account</Menu.GroupLabel>
@@ -48,11 +49,14 @@ import { Check, ChevronRight } from 'lucide-react';
48
49
  * </Menu.Popup>
49
50
  * </Menu.SubMenuRoot>
50
51
  */
51
- export const Root = BaseMenu.Root;
52
+ export function Root(props) {
53
+ return _jsx(BaseMenu.Root, { ...props });
54
+ }
52
55
  export const SubMenuRoot = BaseMenu.SubmenuRoot;
53
- /** Behavior only: give your child control this action. Style the child itself; this wrapper has no preset appearance or disableStyle prop. */
54
- export const OpenHandler = /* @__PURE__ */ forwardRef(function OpenHandler({ children, ...props }, ref) {
55
- return _jsx(BaseMenu.Trigger, { ...props, ref: ref, render: children });
56
+ /** Opens the menu. Pass asChild to give this behavior to your own element, like your app's Button. */
57
+ export const OpenButton = /* @__PURE__ */ forwardRef(function OpenButton({ asChild, disableStyle, children, ...props }, ref) {
58
+ const rendering = asChildRendering({ asChild, children, component: 'menu-openbutton', disableStyle });
59
+ return _jsx(BaseMenu.Trigger, { ...props, ref: ref, ...rendering });
56
60
  });
57
61
  /** Already includes the portal and positioner. Keep it in the matching Root and let the native open state control visibility. */
58
62
  export function Popup({ disableStyle, container, keepMounted, anchor, positionMethod, side = 'bottom', sideOffset = 5, align = 'start', alignOffset, collisionBoundary, collisionPadding, sticky, arrowPadding, disableAnchorTracking, collisionAvoidance, className, style, ...props }) {
@@ -102,7 +106,7 @@ export const Icon = /* @__PURE__ */ forwardRef(function Icon({ disableStyle, chi
102
106
  'data-component': 'menu-icon',
103
107
  'data-style-disabled': disableStyle ? '' : undefined,
104
108
  },
105
- className: twMerge(children?.props.className, className),
109
+ className: twMerge(children.props.className, className),
106
110
  });
107
111
  });
108
112
  /** A default icon is supplied. Omit children for the usual case; pass an icon only to replace that visual. */
@@ -118,49 +122,49 @@ export const SubMenuOpenButtonIcon = /* @__PURE__ */ forwardRef(function SubMenu
118
122
  className: twMerge(children?.props.className, className),
119
123
  });
120
124
  });
121
- export const CheckboxContainer = /* @__PURE__ */ forwardRef(function CheckboxContainer({ disableStyle, className, ...props }, ref) {
122
- const id = useId();
123
- const [label, setLabel] = useState('');
124
- const [state, setState] = useState({
125
- checked: false,
126
- disabled: false,
127
- highlighted: false,
128
- });
129
- return (_jsx(CheckableContext.Provider, { value: { id, label, setLabel, state, setState }, children: _jsx("div", { ...props, ref: ref, "data-disabled": state.disabled ? '' : undefined, "data-highlighted": state.highlighted ? '' : undefined, "data-checked": state.checked ? '' : undefined, "data-component": "menu-checkboxcontainer", "data-style-disabled": disableStyle ? '' : undefined, className: className }) }));
125
+ export const CheckboxContainer = /* @__PURE__ */ forwardRef(function CheckboxContainer(props, ref) {
126
+ return _jsx(CheckableContainer, { ...props, ref: ref, component: "menu-checkboxcontainer" });
130
127
  });
131
128
  export const CheckboxButtonItem = /* @__PURE__ */ forwardRef(function CheckboxButtonItem({ disableStyle, className, label, ...props }, ref) {
132
129
  const context = useCheckableContext();
133
130
  return (_jsx(BaseMenu.CheckboxItem, { ...props, ref: ref, id: context.id, "aria-labelledby": context.id + '-label', label: label ?? context.label, nativeButton: true, "data-component": "menu-checkboxbuttonitem", "data-style-disabled": disableStyle ? '' : undefined, className: className, render: (buttonProps, state) => (_jsx(CheckableButtonSurface, { ...buttonProps, state: state })) }));
134
131
  });
135
- export const CheckboxLabel = /* @__PURE__ */ forwardRef(function CheckboxLabel({ disableStyle, className, children, ...props }, ref) {
136
- const context = useCheckableContext();
137
- return (_jsx("label", { ...props, ref: ref, id: context.id + '-label', htmlFor: context.id, "data-disabled": context.state.disabled ? '' : undefined, "data-component": "menu-checkboxlabel", "data-style-disabled": disableStyle ? '' : undefined, className: className, children: _jsx(CheckableLabelText, { children: children }) }));
132
+ export const CheckboxLabel = /* @__PURE__ */ forwardRef(function CheckboxLabel(props, ref) {
133
+ return _jsx(CheckableLabel, { ...props, ref: ref, component: "menu-checkboxlabel" });
138
134
  });
139
135
  export const CheckboxItemIndicator = /* @__PURE__ */ forwardRef(function CheckboxItemIndicator({ disableStyle, children, className, ...props }, ref) {
140
136
  return (_jsx(BaseMenu.CheckboxItemIndicator, { ...props, ref: ref, "data-component": "menu-checkboxitemindicator", "data-style-disabled": disableStyle ? '' : undefined, className: className, children: children ?? (_jsx(Check, { "aria-hidden": true, strokeWidth: 3.5, "data-component": "menu-checkboxitemindicator-1" })) }));
141
137
  });
142
- export const RadioContainer = /* @__PURE__ */ forwardRef(function RadioContainer({ disableStyle, className, ...props }, ref) {
143
- const id = useId();
144
- const [label, setLabel] = useState('');
145
- const [state, setState] = useState({
146
- checked: false,
147
- disabled: false,
148
- highlighted: false,
149
- });
150
- return (_jsx(CheckableContext.Provider, { value: { id, label, setLabel, state, setState }, children: _jsx("div", { ...props, ref: ref, "data-disabled": state.disabled ? '' : undefined, "data-highlighted": state.highlighted ? '' : undefined, "data-checked": state.checked ? '' : undefined, "data-component": "menu-radiocontainer", "data-style-disabled": disableStyle ? '' : undefined, className: className }) }));
138
+ export const RadioContainer = /* @__PURE__ */ forwardRef(function RadioContainer(props, ref) {
139
+ return _jsx(CheckableContainer, { ...props, ref: ref, component: "menu-radiocontainer" });
151
140
  });
152
141
  export const RadioButtonItem = /* @__PURE__ */ forwardRef(function RadioButtonItem({ disableStyle, className, label, ...props }, ref) {
153
142
  const context = useCheckableContext();
154
143
  return (_jsx(BaseMenu.RadioItem, { ...props, ref: ref, id: context.id, "aria-labelledby": context.id + '-label', label: label ?? context.label, nativeButton: true, "data-component": "menu-radiobuttonitem", "data-style-disabled": disableStyle ? '' : undefined, className: className, render: (buttonProps, state) => (_jsx(CheckableButtonSurface, { ...buttonProps, state: state })) }));
155
144
  });
156
- export const RadioLabel = /* @__PURE__ */ forwardRef(function RadioLabel({ disableStyle, className, children, ...props }, ref) {
157
- const context = useCheckableContext();
158
- return (_jsx("label", { ...props, ref: ref, id: context.id + '-label', htmlFor: context.id, "data-disabled": context.state.disabled ? '' : undefined, "data-component": "menu-radiolabel", "data-style-disabled": disableStyle ? '' : undefined, className: className, children: _jsx(CheckableLabelText, { children: children }) }));
145
+ export const RadioLabel = /* @__PURE__ */ forwardRef(function RadioLabel(props, ref) {
146
+ return _jsx(CheckableLabel, { ...props, ref: ref, component: "menu-radiolabel" });
159
147
  });
160
148
  export const RadioItemIndicator = /* @__PURE__ */ forwardRef(function RadioItemIndicator({ disableStyle, children, className, ...props }, ref) {
161
149
  return (_jsx(BaseMenu.RadioItemIndicator, { ...props, ref: ref, "data-component": "menu-radioitemindicator", "data-style-disabled": disableStyle ? '' : undefined, className: className, children: children ?? _jsx("span", { "data-component": "menu-radioitemindicator-1" }) }));
162
150
  });
163
151
  const CheckableContext = /* @__PURE__ */ createContext(null);
152
+ // Shared by CheckboxContainer and RadioContainer, which only differ by their data-component name
153
+ const CheckableContainer = /* @__PURE__ */ forwardRef(function CheckableContainer({ disableStyle, className, component, ...props }, ref) {
154
+ const id = useId();
155
+ const [label, setLabel] = useState('');
156
+ const [state, setState] = useState({
157
+ checked: false,
158
+ disabled: false,
159
+ highlighted: false,
160
+ });
161
+ return (_jsx(CheckableContext.Provider, { value: { id, label, setLabel, state, setState }, children: _jsx("div", { ...props, ref: ref, "data-disabled": state.disabled ? '' : undefined, "data-highlighted": state.highlighted ? '' : undefined, "data-checked": state.checked ? '' : undefined, "data-component": component, "data-style-disabled": disableStyle ? '' : undefined, className: className }) }));
162
+ });
163
+ // Shared by CheckboxLabel and RadioLabel, which only differ by their data-component name
164
+ const CheckableLabel = /* @__PURE__ */ forwardRef(function CheckableLabel({ disableStyle, className, children, component, ...props }, ref) {
165
+ const context = useCheckableContext();
166
+ return (_jsx("label", { ...props, ref: ref, id: context.id + '-label', htmlFor: context.id, "data-disabled": context.state.disabled ? '' : undefined, "data-component": component, "data-style-disabled": disableStyle ? '' : undefined, className: className, children: _jsx(CheckableLabelText, { children: children }) }));
167
+ });
164
168
  function useCheckableContext() {
165
169
  const context = useContext(CheckableContext);
166
170
  if (!context)
@@ -168,21 +172,20 @@ function useCheckableContext() {
168
172
  return context;
169
173
  }
170
174
  const CheckableButtonSurface = /* @__PURE__ */ forwardRef(function CheckableButtonSurface({ state, ...props }, ref) {
171
- const { setState } = useCheckableContext();
175
+ const context = useCheckableContext();
172
176
  useEffect(() => {
173
- setState({
177
+ context.setState({
174
178
  checked: state.checked,
175
179
  disabled: state.disabled,
176
180
  highlighted: state.highlighted,
177
181
  });
178
- }, [state.checked, state.disabled, state.highlighted, setState]);
182
+ }, [state.checked, state.disabled, state.highlighted, context.setState]);
179
183
  return _jsx("button", { type: "button", ...props, ref: ref });
180
184
  });
181
185
  function CheckableLabelText({ children }) {
182
- const { setLabel } = useCheckableContext();
186
+ const context = useCheckableContext();
183
187
  return (_jsx("span", { ref: (element) => {
184
188
  if (element)
185
- setLabel(element.textContent ?? '');
189
+ context.setLabel(element.textContent ?? '');
186
190
  }, children: children }));
187
191
  }
188
- //# sourceMappingURL=index.js.map
@@ -21,4 +21,3 @@ export declare function Number({ disableStyle, children, className, ...props }:
21
21
  children: number;
22
22
  }) & StyleProps): import("react").JSX.Element;
23
23
  export declare function Unit({ disableStyle, className, ...props }: HTMLAttributes<HTMLParagraphElement> & StyleProps): import("react").JSX.Element;
24
- //# sourceMappingURL=index.d.ts.map
@@ -23,4 +23,3 @@ export function Number({ disableStyle, children, className, ...props }) {
23
23
  export function Unit({ disableStyle, className, ...props }) {
24
24
  return (_jsx("p", { "data-component": "metric-unit", "data-style-disabled": disableStyle ? '' : undefined, className: className, ...props }));
25
25
  }
26
- //# sourceMappingURL=index.js.map
@@ -2,13 +2,15 @@ import type { StyleProps } from '../../types/Style.ts';
2
2
  import { NumberField as BaseNumber } from '@base-ui/react/number-field';
3
3
  import type { SVGIconProps } from '../../types/Icon.ts';
4
4
  import type { ComponentProps, ReactElement } from 'react';
5
+ import type { Validation } from '../../types/Validation.ts';
6
+ import type { ValidationMessageChildren } from '../../utils/Validation.ts';
5
7
  export type RootProps = Omit<BaseNumber.Root.Props, 'className' | 'render'> & {
6
8
  className?: string;
7
- /** Choose the visual tone. This does not validate data or show/hide feedback messages. */
8
- tone?: 'neutral' | 'valid' | 'invalid';
9
+ /** The field's validation state, with an optional message: `'error'` styles it as invalid, `['error', 'Email is required']` also shows that message in Error. Same for `'success'` and Success. Omit it for a neutral field. */
10
+ validation?: Validation;
9
11
  };
10
12
  /**
11
- * Configure value, bounds, and step on Root. IncrementButton and DecrementButton update the number automatically; no custom arithmetic handlers needed. Use ButtonsList to organize them beside Input, or behavior-only handlers for custom buttons.
13
+ * Configure value, bounds, and step on Root. IncrementButton and DecrementButton update the number automatically; no custom arithmetic handlers needed. Use ButtonsList to organize them beside Input.
12
14
  *
13
15
  * @example Complete composition
14
16
  * import { NumberInput } from '@central-ui/react'
@@ -44,17 +46,20 @@ export type RootProps = Omit<BaseNumber.Root.Props, 'className' | 'render'> & {
44
46
  * </NumberInput.Root>
45
47
  */
46
48
  export declare const Root: import("react").ForwardRefExoticComponent<Omit<RootProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
47
- export declare const Input: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").NumberFieldInputProps, "form" | "className" | "defaultValue" | "id" | "name" | "disabled" | "value" | "render" | "required" | "readOnly"> & {
49
+ export declare const Input: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").NumberFieldInputProps, "className" | "id" | "name" | "form" | "defaultValue" | "disabled" | "value" | "render" | "required" | "readOnly"> & {
48
50
  className?: string;
49
51
  } & StyleProps, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
50
52
  export declare const Container: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
51
53
  /** Labels the associated control automatically. Supply the visible text; do not wire a second label association. */
52
54
  export declare const Label: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, "htmlFor"> & StyleProps, "ref"> & import("react").RefAttributes<HTMLLabelElement>>;
53
55
  export declare const Description: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
54
- /** You decide when to show this feedback. Changing tone does not validate data or render the message for you. */
55
- export declare const Error: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
56
- /** You decide when to show this feedback. Changing tone does not validate data or render the message for you. */
57
- export declare const Success: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
56
+ type MessageProps = Omit<ComponentProps<'div'>, 'children'> & StyleProps & {
57
+ children?: ValidationMessageChildren;
58
+ };
59
+ /** Shows itself only while Root's validation is `'error'`: with no children it shows the message from `['error', message]`, a function children formats that message, and other children replace it. */
60
+ export declare const Error: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
61
+ /** Shows itself only while Root's validation is `'success'`: with no children it shows the message from `['success', message]`, a function children formats that message, and other children replace it. */
62
+ export declare const Success: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
58
63
  export declare const Addon: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & {
59
64
  /** Choose the preset appearance; this does not add action behavior. */
60
65
  variant?: "normal" | "emphasized";
@@ -77,12 +82,6 @@ export declare const ValueText: import("react").ForwardRefExoticComponent<Omit<O
77
82
  export declare const IncrementButton: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").NumberFieldIncrementProps, "className"> & {
78
83
  className?: string;
79
84
  } & StyleProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
80
- /** Behavior only: give your child control this action. Style the child itself; this wrapper has no preset appearance or disableStyle prop. */
81
- export declare const IncrementHandler: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").NumberFieldIncrementProps, "children" | "className" | "render"> & {
82
- className?: string;
83
- /** Supply your own accessible control. It receives the behavior; its own className owns the appearance. */
84
- children: ReactElement;
85
- }, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
86
85
  /** A default icon is supplied. Omit children for the usual case; pass an icon only to replace that visual. */
87
86
  export declare function IncrementButtonIcon({ disableStyle, children, className, ...props }: (Omit<ComponentProps<'svg'>, 'children'> & {
88
87
  /** Omit for the supplied default icon; pass a child only to replace it. */
@@ -91,15 +90,9 @@ export declare function IncrementButtonIcon({ disableStyle, children, className,
91
90
  export declare const DecrementButton: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").NumberFieldDecrementProps, "className"> & {
92
91
  className?: string;
93
92
  } & StyleProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
94
- /** Behavior only: give your child control this action. Style the child itself; this wrapper has no preset appearance or disableStyle prop. */
95
- export declare const DecrementHandler: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").NumberFieldDecrementProps, "children" | "className" | "render"> & {
96
- className?: string;
97
- /** Supply your own accessible control. It receives the behavior; its own className owns the appearance. */
98
- children: ReactElement;
99
- }, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
100
93
  /** A default icon is supplied. Omit children for the usual case; pass an icon only to replace that visual. */
101
94
  export declare function DecrementButtonIcon({ disableStyle, children, className, ...props }: (Omit<ComponentProps<'svg'>, 'children'> & {
102
95
  /** Omit for the supplied default icon; pass a child only to replace it. */
103
96
  children?: ReactElement<ComponentProps<'svg'>>;
104
97
  }) & StyleProps): ReactElement<import("react").SVGProps<SVGSVGElement>, string | import("react").JSXElementConstructor<any>>;
105
- //# sourceMappingURL=index.d.ts.map
98
+ export {};
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { NumberField as BaseNumber } from '@base-ui/react/number-field';
3
- import { ChevronUp, ChevronDown, LoaderCircle } from 'lucide-react';
3
+ import { ChevronUp, ChevronDown, LoaderCircle } from "../../utils/icons.js";
4
4
  import { useForwardClickToInput } from "../../utils/useForwardClickToInput.js";
5
5
  import { cloneElement, createContext, forwardRef, useContext, useEffect, useId, useState, } from 'react';
6
6
  import { twMerge } from 'tailwind-merge';
7
- import { ToneProvider, useToneContext } from "../../utils/Tone.js";
7
+ import { readValidation, validationAttributes, validationMessageContent } from "../../utils/Validation.js";
8
8
  /**
9
- * Configure value, bounds, and step on Root. IncrementButton and DecrementButton update the number automatically; no custom arithmetic handlers needed. Use ButtonsList to organize them beside Input, or behavior-only handlers for custom buttons.
9
+ * Configure value, bounds, and step on Root. IncrementButton and DecrementButton update the number automatically; no custom arithmetic handlers needed. Use ButtonsList to organize them beside Input.
10
10
  *
11
11
  * @example Complete composition
12
12
  * import { NumberInput } from '@central-ui/react'
@@ -41,25 +41,24 @@ import { ToneProvider, useToneContext } from "../../utils/Tone.js";
41
41
  * }
42
42
  * </NumberInput.Root>
43
43
  */
44
- export const Root = /* @__PURE__ */ forwardRef(function Root({ id, tone = 'neutral', children, ...props }, ref) {
44
+ export const Root = /* @__PURE__ */ forwardRef(function Root({ id, validation, children, ...props }, ref) {
45
45
  const generatedId = useId();
46
46
  const inputId = id ?? generatedId;
47
- const [connections, setConnections] = useState({
48
- descriptionIds: [],
49
- });
50
- return (_jsx(ToneProvider, { value: tone, children: _jsx(BaseNumber.Root, { ...props, id: inputId, ref: ref, render: (rootProps, state) => (_jsx(InputContext.Provider, { value: {
51
- ...state,
52
- inputId,
53
- connections,
54
- setConnections,
55
- min: props.min,
56
- max: props.max,
57
- }, children: _jsx("div", { ...rootProps }) })), children: children }) }));
47
+ // Holding the ids of Description, Error and Success, so the control announces them to screen readers through aria-describedby
48
+ const [describedByIds, setDescribedByIds] = useState([]);
49
+ return (_jsx(BaseNumber.Root, { ...props, id: inputId, ref: ref, render: (rootProps, state) => (_jsx(InputContext.Provider, { value: {
50
+ ...state,
51
+ inputId,
52
+ describedByIds,
53
+ setDescribedByIds,
54
+ min: props.min,
55
+ max: props.max,
56
+ validation: readValidation(validation),
57
+ }, children: _jsx("div", { ...rootProps }) })), children: children }));
58
58
  });
59
59
  export const Input = /* @__PURE__ */ forwardRef(function Input({ disableStyle, className, 'aria-describedby': describedBy, ...props }, ref) {
60
60
  const context = useInputContext();
61
- const tone = useToneContext();
62
- return (_jsx(BaseNumber.Input, { ...props, ref: ref, "data-tone": tone, "aria-invalid": tone === 'invalid' || props['aria-invalid'] || undefined, "aria-describedby": [describedBy, ...context.connections.descriptionIds]
61
+ return (_jsx(BaseNumber.Input, { ...props, ref: ref, ...validationAttributes(context.validation), "aria-invalid": context.validation.state === 'error' || props['aria-invalid'] || undefined, "aria-describedby": [describedBy, ...context.describedByIds]
63
62
  .filter(Boolean)
64
63
  .join(' ') || undefined, "data-component": "numberinput-input", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
65
64
  });
@@ -72,13 +71,6 @@ export const Label = /* @__PURE__ */ forwardRef(function Label({ disableStyle, c
72
71
  const context = useInputContext();
73
72
  const labelId = id ?? context.inputId + '-label';
74
73
  const attributes = usePartAttributes();
75
- useEffect(() => {
76
- context.setConnections((current) => ({ ...current, labelId }));
77
- return () => context.setConnections((current) => ({
78
- ...current,
79
- labelId: current.labelId === labelId ? undefined : current.labelId,
80
- }));
81
- }, [labelId, context.setConnections]);
82
74
  return (_jsx("label", { ...attributes, ...props, id: labelId, ref: ref, htmlFor: context.inputId, "data-component": "numberinput-label", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
83
75
  });
84
76
  export const Description = /* @__PURE__ */ forwardRef(function Description({ disableStyle, className, id, ...props }, ref) {
@@ -86,17 +78,27 @@ export const Description = /* @__PURE__ */ forwardRef(function Description({ dis
86
78
  const attributes = usePartAttributes();
87
79
  return (_jsx("div", { ...attributes, ...props, id: messageId, ref: ref, "data-component": "numberinput-description", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
88
80
  });
89
- /** You decide when to show this feedback. Changing tone does not validate data or render the message for you. */
90
- export const Error = /* @__PURE__ */ forwardRef(function Error({ disableStyle, className, id, ...props }, ref) {
91
- const messageId = useMessageId(id);
92
- const attributes = usePartAttributes();
93
- return (_jsx("div", { role: "alert", "aria-atomic": "true", ...attributes, ...props, id: messageId, ref: ref, "data-component": "numberinput-error", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
81
+ /** Shows itself only while Root's validation is `'error'`: with no children it shows the message from `['error', message]`, a function children formats that message, and other children replace it. */
82
+ export const Error = /* @__PURE__ */ forwardRef(function Error({ children, ...props }, ref) {
83
+ const context = useInputContext();
84
+ const content = validationMessageContent(context.validation, 'error', children);
85
+ if (content === undefined)
86
+ return null;
87
+ return _jsx(Message, { ...props, ref: ref, role: "alert", component: "numberinput-error", children: content });
94
88
  });
95
- /** You decide when to show this feedback. Changing tone does not validate data or render the message for you. */
96
- export const Success = /* @__PURE__ */ forwardRef(function Success({ disableStyle, className, id, ...props }, ref) {
89
+ /** Shows itself only while Root's validation is `'success'`: with no children it shows the message from `['success', message]`, a function children formats that message, and other children replace it. */
90
+ export const Success = /* @__PURE__ */ forwardRef(function Success({ children, ...props }, ref) {
91
+ const context = useInputContext();
92
+ const content = validationMessageContent(context.validation, 'success', children);
93
+ if (content === undefined)
94
+ return null;
95
+ return _jsx(Message, { ...props, ref: ref, role: "status", component: "numberinput-success", children: content });
96
+ });
97
+ // The shared body of Error and Success, mounted only while they show, so only visible messages describe the number input
98
+ const Message = /* @__PURE__ */ forwardRef(function Message({ disableStyle, className, id, component, ...props }, ref) {
97
99
  const messageId = useMessageId(id);
98
100
  const attributes = usePartAttributes();
99
- return (_jsx("div", { role: "status", "aria-atomic": "true", ...attributes, ...props, id: messageId, ref: ref, "data-component": "numberinput-success", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
101
+ return _jsx("div", { "aria-atomic": "true", ...attributes, ...props, id: messageId, ref: ref, "data-component": component, "data-style-disabled": disableStyle ? '' : undefined, className: className });
100
102
  });
101
103
  export const Addon = /* @__PURE__ */ forwardRef(function Addon({ disableStyle, className, variant = 'normal', ...props }, ref) {
102
104
  const attributes = usePartAttributes();
@@ -149,10 +151,6 @@ export const ValueText = /* @__PURE__ */ forwardRef(function ValueText({ disable
149
151
  export const IncrementButton = /* @__PURE__ */ forwardRef(function IncrementButton({ disableStyle, className, ...props }, ref) {
150
152
  return (_jsx(BaseNumber.Increment, { ...props, ref: ref, "data-component": "numberinput-incrementbutton", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
151
153
  });
152
- /** Behavior only: give your child control this action. Style the child itself; this wrapper has no preset appearance or disableStyle prop. */
153
- export const IncrementHandler = /* @__PURE__ */ forwardRef(function IncrementHandler({ children, ...props }, ref) {
154
- return _jsx(BaseNumber.Increment, { ...props, ref: ref, render: children });
155
- });
156
154
  /** A default icon is supplied. Omit children for the usual case; pass an icon only to replace that visual. */
157
155
  export function IncrementButtonIcon({ disableStyle, children = _jsx(ChevronUp, {}), className, ...props }) {
158
156
  const context = useInputContext();
@@ -178,10 +176,6 @@ export function IncrementButtonIcon({ disableStyle, children = _jsx(ChevronUp, {
178
176
  export const DecrementButton = /* @__PURE__ */ forwardRef(function DecrementButton({ disableStyle, className, ...props }, ref) {
179
177
  return (_jsx(BaseNumber.Decrement, { ...props, ref: ref, "data-component": "numberinput-decrementbutton", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
180
178
  });
181
- /** Behavior only: give your child control this action. Style the child itself; this wrapper has no preset appearance or disableStyle prop. */
182
- export const DecrementHandler = /* @__PURE__ */ forwardRef(function DecrementHandler({ children, ...props }, ref) {
183
- return _jsx(BaseNumber.Decrement, { ...props, ref: ref, render: children });
184
- });
185
179
  /** A default icon is supplied. Omit children for the usual case; pass an icon only to replace that visual. */
186
180
  export function DecrementButtonIcon({ disableStyle, children = _jsx(ChevronDown, {}), className, ...props }) {
187
181
  const context = useInputContext();
@@ -213,28 +207,21 @@ function useInputContext() {
213
207
  }
214
208
  function usePartAttributes() {
215
209
  const context = useInputContext();
216
- const tone = useToneContext();
217
210
  return {
218
211
  'data-disabled': context.disabled ? '' : undefined,
219
212
  'data-readonly': context.readOnly ? '' : undefined,
220
213
  'data-required': context.required ? '' : undefined,
221
- 'data-tone': tone,
214
+ ...validationAttributes(context.validation),
222
215
  };
223
216
  }
217
+ // Registering a message part's id in the control's aria-describedby while the part is mounted
224
218
  function useMessageId(id) {
219
+ const context = useInputContext();
225
220
  const generatedId = useId();
226
221
  const messageId = id ?? generatedId;
227
- const context = useInputContext();
228
222
  useEffect(() => {
229
- context.setConnections((current) => ({
230
- ...current,
231
- descriptionIds: [...current.descriptionIds, messageId],
232
- }));
233
- return () => context.setConnections((current) => ({
234
- ...current,
235
- descriptionIds: current.descriptionIds.filter((id) => id !== messageId),
236
- }));
237
- }, [messageId, context.setConnections]);
223
+ context.setDescribedByIds((current) => [...current, messageId]);
224
+ return () => context.setDescribedByIds((current) => current.filter((currentId) => currentId !== messageId));
225
+ }, [messageId, context.setDescribedByIds]);
238
226
  return messageId;
239
227
  }
240
- //# sourceMappingURL=index.js.map
@@ -1,10 +1,12 @@
1
1
  import type { StyleProps } from '../../types/Style.ts';
2
2
  import { OTPField as BaseOtp } from '@base-ui/react/otp-field';
3
- import type { ReactNode } from 'react';
3
+ import type { ComponentProps, ReactNode } from 'react';
4
+ import type { Validation } from '../../types/Validation.ts';
5
+ import type { ValidationMessageChildren } from '../../utils/Validation.ts';
4
6
  export type RootProps = Omit<BaseOtp.Root.Props, 'className' | 'render'> & {
5
7
  className?: string;
6
- /** Choose the visual tone. This does not validate data or show/hide feedback messages. */
7
- tone?: 'neutral' | 'valid' | 'invalid';
8
+ /** The field's validation state, with an optional message: `'error'` styles it as invalid, `['error', 'Email is required']` also shows that message in Error. Same for `'success'` and Success. Omit it for a neutral field. */
9
+ validation?: Validation;
8
10
  };
9
11
  /**
10
12
  * Configure length and value on Root. Render an Input for each callback invocation from List; slot order is managed automatically. Keyboard navigation and paste distribution are built in; do not split or synchronize the code yourself.
@@ -41,17 +43,20 @@ export type RootProps = Omit<BaseOtp.Root.Props, 'className' | 'render'> & {
41
43
  * </OtpInput.Root>
42
44
  */
43
45
  export declare const Root: import("react").ForwardRefExoticComponent<Omit<RootProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
44
- export declare const Input: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").OTPFieldInputProps, "form" | "className" | "defaultValue" | "id" | "name" | "disabled" | "value" | "render" | "required" | "readOnly"> & {
46
+ export declare const Input: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").OTPFieldInputProps, "className" | "id" | "name" | "form" | "defaultValue" | "disabled" | "value" | "render" | "required" | "readOnly"> & {
45
47
  className?: string;
46
48
  } & StyleProps, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
47
49
  export declare const Container: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
48
50
  /** Labels the associated control automatically. Supply the visible text; do not wire a second label association. */
49
51
  export declare const Label: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, "htmlFor"> & StyleProps, "ref"> & import("react").RefAttributes<HTMLLabelElement>>;
50
52
  export declare const Description: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
51
- /** You decide when to show this feedback. Changing tone does not validate data or render the message for you. */
52
- export declare const Error: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
53
- /** You decide when to show this feedback. Changing tone does not validate data or render the message for you. */
54
- export declare const Success: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
53
+ type MessageProps = Omit<ComponentProps<'div'>, 'children'> & StyleProps & {
54
+ children?: ValidationMessageChildren;
55
+ };
56
+ /** Shows itself only while Root's validation is `'error'`: with no children it shows the message from `['error', message]`, a function children formats that message, and other children replace it. */
57
+ export declare const Error: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
58
+ /** Shows itself only while Root's validation is `'success'`: with no children it shows the message from `['success', message]`, a function children formats that message, and other children replace it. */
59
+ export declare const Success: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
55
60
  /** Calls children for each code slot. Render Input inside each callback; the component handles slot ordering and keyboard/paste behavior. */
56
61
  export declare const List: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "children"> & {
57
62
  /** Render an Input for each slot. Use the index for keys or separator placement; input ordering is automatic. */
@@ -60,4 +65,4 @@ export declare const List: import("react").ForwardRefExoticComponent<Omit<Omit<i
60
65
  export declare const Separator: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").SeparatorProps, "className"> & {
61
66
  className?: string;
62
67
  } & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
63
- //# sourceMappingURL=index.d.ts.map
68
+ export {};
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { OTPField as BaseOtp } from '@base-ui/react/otp-field';
3
3
  import { Fragment, createContext, forwardRef, useContext, useEffect, useId, useState, } from 'react';
4
- import { ToneProvider, useToneContext } from "../../utils/Tone.js";
4
+ import { readValidation, validationAttributes, validationMessageContent } from "../../utils/Validation.js";
5
5
  /**
6
6
  * Configure length and value on Root. Render an Input for each callback invocation from List; slot order is managed automatically. Keyboard navigation and paste distribution are built in; do not split or synchronize the code yourself.
7
7
  *
@@ -36,20 +36,19 @@ import { ToneProvider, useToneContext } from "../../utils/Tone.js";
36
36
  * }
37
37
  * </OtpInput.Root>
38
38
  */
39
- export const Root = /* @__PURE__ */ forwardRef(function Root({ id, tone = 'neutral', children, ...props }, ref) {
39
+ export const Root = /* @__PURE__ */ forwardRef(function Root({ id, validation, children, ...props }, ref) {
40
40
  const generatedId = useId();
41
41
  const inputId = id ?? generatedId;
42
42
  const [connections, setConnections] = useState({
43
43
  descriptionIds: [],
44
44
  });
45
- return (_jsx(ToneProvider, { value: tone, children: _jsx(BaseOtp.Root, { ...props, id: inputId, ref: ref, "aria-labelledby": connections.labelId ?? props['aria-labelledby'], "aria-describedby": [props['aria-describedby'], ...connections.descriptionIds]
46
- .filter(Boolean)
47
- .join(' ') || undefined, render: (rootProps, state) => (_jsx(InputContext.Provider, { value: { ...state, inputId, connections, setConnections }, children: _jsx("div", { ...rootProps }) })), children: children }) }));
45
+ return (_jsx(BaseOtp.Root, { ...props, id: inputId, ref: ref, "aria-labelledby": connections.labelId ?? props['aria-labelledby'], "aria-describedby": [props['aria-describedby'], ...connections.descriptionIds]
46
+ .filter(Boolean)
47
+ .join(' ') || undefined, render: (rootProps, state) => (_jsx(InputContext.Provider, { value: { ...state, inputId, connections, setConnections, validation: readValidation(validation) }, children: _jsx("div", { ...rootProps }) })), children: children }));
48
48
  });
49
49
  export const Input = /* @__PURE__ */ forwardRef(function Input({ disableStyle, className, 'aria-describedby': describedBy, ...props }, ref) {
50
50
  const context = useInputContext();
51
- const tone = useToneContext();
52
- return (_jsx(BaseOtp.Input, { ...props, ref: ref, "data-tone": tone, "aria-invalid": tone === 'invalid' || props['aria-invalid'] || undefined, "aria-describedby": [describedBy, ...context.connections.descriptionIds]
51
+ return (_jsx(BaseOtp.Input, { ...props, ref: ref, ...validationAttributes(context.validation), "aria-invalid": context.validation.state === 'error' || props['aria-invalid'] || undefined, "aria-describedby": [describedBy, ...context.connections.descriptionIds]
53
52
  .filter(Boolean)
54
53
  .join(' ') || undefined, "aria-labelledby": props['aria-labelledby'] ?? context.connections.labelId, "data-component": "otpinput-input", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
55
54
  });
@@ -76,17 +75,27 @@ export const Description = /* @__PURE__ */ forwardRef(function Description({ dis
76
75
  const attributes = usePartAttributes();
77
76
  return (_jsx("div", { ...attributes, ...props, id: messageId, ref: ref, "data-component": "otpinput-description", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
78
77
  });
79
- /** You decide when to show this feedback. Changing tone does not validate data or render the message for you. */
80
- export const Error = /* @__PURE__ */ forwardRef(function Error({ disableStyle, className, id, ...props }, ref) {
81
- const messageId = useMessageId(id);
82
- const attributes = usePartAttributes();
83
- return (_jsx("div", { role: "alert", "aria-atomic": "true", ...attributes, ...props, id: messageId, ref: ref, "data-component": "otpinput-error", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
78
+ /** Shows itself only while Root's validation is `'error'`: with no children it shows the message from `['error', message]`, a function children formats that message, and other children replace it. */
79
+ export const Error = /* @__PURE__ */ forwardRef(function Error({ children, ...props }, ref) {
80
+ const context = useInputContext();
81
+ const content = validationMessageContent(context.validation, 'error', children);
82
+ if (content === undefined)
83
+ return null;
84
+ return _jsx(Message, { ...props, ref: ref, role: "alert", component: "otpinput-error", children: content });
85
+ });
86
+ /** Shows itself only while Root's validation is `'success'`: with no children it shows the message from `['success', message]`, a function children formats that message, and other children replace it. */
87
+ export const Success = /* @__PURE__ */ forwardRef(function Success({ children, ...props }, ref) {
88
+ const context = useInputContext();
89
+ const content = validationMessageContent(context.validation, 'success', children);
90
+ if (content === undefined)
91
+ return null;
92
+ return _jsx(Message, { ...props, ref: ref, role: "status", component: "otpinput-success", children: content });
84
93
  });
85
- /** You decide when to show this feedback. Changing tone does not validate data or render the message for you. */
86
- export const Success = /* @__PURE__ */ forwardRef(function Success({ disableStyle, className, id, ...props }, ref) {
94
+ // The shared body of Error and Success, mounted only while they show, so only visible messages describe the otp input
95
+ const Message = /* @__PURE__ */ forwardRef(function Message({ disableStyle, className, id, component, ...props }, ref) {
87
96
  const messageId = useMessageId(id);
88
97
  const attributes = usePartAttributes();
89
- return (_jsx("div", { role: "status", "aria-atomic": "true", ...attributes, ...props, id: messageId, ref: ref, "data-component": "otpinput-success", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
98
+ return _jsx("div", { "aria-atomic": "true", ...attributes, ...props, id: messageId, ref: ref, "data-component": component, "data-style-disabled": disableStyle ? '' : undefined, className: className });
90
99
  });
91
100
  /** Calls children for each code slot. Render Input inside each callback; the component handles slot ordering and keyboard/paste behavior. */
92
101
  export const List = /* @__PURE__ */ forwardRef(function List({ disableStyle, className, children, ...props }, ref) {
@@ -106,12 +115,11 @@ function useInputContext() {
106
115
  }
107
116
  function usePartAttributes() {
108
117
  const context = useInputContext();
109
- const tone = useToneContext();
110
118
  return {
111
119
  'data-disabled': context.disabled ? '' : undefined,
112
120
  'data-readonly': context.readOnly ? '' : undefined,
113
121
  'data-required': context.required ? '' : undefined,
114
- 'data-tone': tone,
122
+ ...validationAttributes(context.validation),
115
123
  };
116
124
  }
117
125
  function useMessageId(id) {
@@ -130,4 +138,3 @@ function useMessageId(id) {
130
138
  }, [messageId, context.setConnections]);
131
139
  return messageId;
132
140
  }
133
- //# sourceMappingURL=index.js.map
@@ -51,4 +51,3 @@ export declare function BodySectionHeader({ disableStyle, className, ...props }:
51
51
  export declare function BodySectionTitle({ disableStyle, className, ...props }: ComponentProps<'h2'> & StyleProps): import("react").JSX.Element;
52
52
  export declare function Footer({ disableStyle, className, ...props }: ComponentProps<'div'> & StyleProps): import("react").JSX.Element;
53
53
  export {};
54
- //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { ArrowLeft } from 'lucide-react';
2
+ import { ArrowLeft } from "../../utils/icons.js";
3
3
  import { cloneElement, createContext, useContext } from 'react';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  const SizeContext = /* @__PURE__ */ createContext('medium');
@@ -58,7 +58,6 @@ export function HeaderBackButton({ disableStyle, type = 'button', className, ...
58
58
  export function HeaderBackButtonIcon({ disableStyle, children = _jsx(ArrowLeft, {}), className, ...props }) {
59
59
  const size = useContext(SizeContext);
60
60
  return cloneElement(children, {
61
- ...children.props,
62
61
  ...props,
63
62
  'data-size': size,
64
63
  ...{
@@ -97,4 +96,3 @@ export function Footer({ disableStyle, className, ...props }) {
97
96
  const size = useContext(SizeContext);
98
97
  return (_jsx("div", { ...props, "data-size": size, "data-component": "page-footer", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
99
98
  }
100
- //# sourceMappingURL=index.js.map