@qeetrix/react 0.0.1 → 0.0.4

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 (263) hide show
  1. package/CHANGELOG.md +288 -0
  2. package/README.md +240 -4
  3. package/dist/accordion/accordion.d.ts +42 -0
  4. package/dist/accordion/accordion.js +72 -0
  5. package/dist/accordion/index.d.ts +2 -0
  6. package/dist/accordion/index.js +1 -0
  7. package/dist/avatar/avatar.d.ts +21 -0
  8. package/dist/avatar/avatar.js +43 -0
  9. package/dist/avatar/index.d.ts +2 -0
  10. package/dist/avatar/index.js +1 -0
  11. package/dist/badge/badge.d.ts +14 -0
  12. package/dist/badge/badge.js +25 -0
  13. package/dist/badge/index.d.ts +2 -0
  14. package/dist/badge/index.js +1 -0
  15. package/dist/box/box.d.ts +27 -0
  16. package/dist/box/box.js +21 -0
  17. package/dist/box/index.d.ts +2 -0
  18. package/dist/box/index.js +1 -0
  19. package/dist/button/button.d.ts +50 -0
  20. package/dist/button/button.js +78 -0
  21. package/dist/button/index.d.ts +2 -0
  22. package/dist/button/index.js +1 -0
  23. package/dist/card/card.d.ts +30 -0
  24. package/dist/card/card.js +56 -0
  25. package/dist/card/index.d.ts +2 -0
  26. package/dist/card/index.js +1 -0
  27. package/dist/checkbox/checkbox.d.ts +33 -0
  28. package/dist/checkbox/checkbox.js +61 -0
  29. package/dist/checkbox/index.d.ts +2 -0
  30. package/dist/checkbox/index.js +1 -0
  31. package/dist/css/accordion.css +73 -0
  32. package/dist/css/avatar.css +50 -0
  33. package/dist/css/badge.css +59 -0
  34. package/dist/css/box.css +163 -0
  35. package/dist/css/button.css +243 -0
  36. package/dist/css/card.css +75 -0
  37. package/dist/css/checkbox.css +67 -0
  38. package/dist/css/dialog.css +128 -0
  39. package/dist/css/drawer.css +129 -0
  40. package/dist/css/experimental/alert.css +89 -0
  41. package/dist/css/experimental/app-shell.css +258 -0
  42. package/dist/css/experimental/breadcrumb.css +67 -0
  43. package/dist/css/experimental/copy-button.css +13 -0
  44. package/dist/css/experimental/empty-state.css +63 -0
  45. package/dist/css/experimental/otp-input.css +84 -0
  46. package/dist/css/experimental/page-header.css +110 -0
  47. package/dist/css/experimental/password-input.css +21 -0
  48. package/dist/css/experimental/sidebar.css +234 -0
  49. package/dist/css/experimental/stat.css +115 -0
  50. package/dist/css/experimental/top-bar.css +299 -0
  51. package/dist/css/field.css +121 -0
  52. package/dist/css/fieldset.css +37 -0
  53. package/dist/css/grid.css +86 -0
  54. package/dist/css/heading.css +93 -0
  55. package/dist/css/inline.css +85 -0
  56. package/dist/css/input.css +108 -0
  57. package/dist/css/link.css +45 -0
  58. package/dist/css/menu.css +166 -0
  59. package/dist/css/popover.css +72 -0
  60. package/dist/css/progress.css +102 -0
  61. package/dist/css/radio.css +54 -0
  62. package/dist/css/scroll-area.css +70 -0
  63. package/dist/css/select.css +189 -0
  64. package/dist/css/separator.css +35 -0
  65. package/dist/css/skeleton.css +39 -0
  66. package/dist/css/skip-link.css +33 -0
  67. package/dist/css/slider.css +108 -0
  68. package/dist/css/spinner.css +55 -0
  69. package/dist/css/stack.css +76 -0
  70. package/dist/css/switch.css +112 -0
  71. package/dist/css/table.css +68 -0
  72. package/dist/css/tabs.css +123 -0
  73. package/dist/css/text-field.css +4 -0
  74. package/dist/css/text.css +106 -0
  75. package/dist/css/toast.css +115 -0
  76. package/dist/css/toggle.css +125 -0
  77. package/dist/css/tooltip.css +47 -0
  78. package/dist/css/visually-hidden.css +15 -0
  79. package/dist/dialog/dialog.d.ts +54 -0
  80. package/dist/dialog/dialog.js +121 -0
  81. package/dist/dialog/index.d.ts +2 -0
  82. package/dist/dialog/index.js +1 -0
  83. package/dist/drawer/drawer.d.ts +49 -0
  84. package/dist/drawer/drawer.js +102 -0
  85. package/dist/drawer/index.d.ts +2 -0
  86. package/dist/drawer/index.js +1 -0
  87. package/dist/experimental/alert/alert.d.ts +29 -0
  88. package/dist/experimental/alert/alert.js +72 -0
  89. package/dist/experimental/alert/index.d.ts +2 -0
  90. package/dist/experimental/app-shell/app-shell.d.ts +60 -0
  91. package/dist/experimental/app-shell/app-shell.js +224 -0
  92. package/dist/experimental/app-shell/index.d.ts +2 -0
  93. package/dist/experimental/breadcrumb/breadcrumb.d.ts +27 -0
  94. package/dist/experimental/breadcrumb/breadcrumb.js +48 -0
  95. package/dist/experimental/breadcrumb/index.d.ts +2 -0
  96. package/dist/experimental/copy-button/copy-button.d.ts +27 -0
  97. package/dist/experimental/copy-button/copy-button.js +61 -0
  98. package/dist/experimental/copy-button/index.d.ts +2 -0
  99. package/dist/experimental/empty-state/data-state.d.ts +23 -0
  100. package/dist/experimental/empty-state/data-state.js +40 -0
  101. package/dist/experimental/empty-state/empty-state.d.ts +26 -0
  102. package/dist/experimental/empty-state/empty-state.js +46 -0
  103. package/dist/experimental/empty-state/index.d.ts +3 -0
  104. package/dist/experimental/index.d.ts +24 -0
  105. package/dist/experimental/index.js +13 -0
  106. package/dist/experimental/otp-input/index.d.ts +2 -0
  107. package/dist/experimental/otp-input/otp-input.d.ts +27 -0
  108. package/dist/experimental/otp-input/otp-input.js +39 -0
  109. package/dist/experimental/page-header/index.d.ts +2 -0
  110. package/dist/experimental/page-header/page-header.d.ts +32 -0
  111. package/dist/experimental/page-header/page-header.js +73 -0
  112. package/dist/experimental/password-input/index.d.ts +2 -0
  113. package/dist/experimental/password-input/password-input.d.ts +26 -0
  114. package/dist/experimental/password-input/password-input.js +103 -0
  115. package/dist/experimental/sidebar/index.d.ts +2 -0
  116. package/dist/experimental/sidebar/sidebar.d.ts +64 -0
  117. package/dist/experimental/sidebar/sidebar.js +200 -0
  118. package/dist/experimental/stat/index.d.ts +2 -0
  119. package/dist/experimental/stat/stat.d.ts +31 -0
  120. package/dist/experimental/stat/stat.js +90 -0
  121. package/dist/experimental/top-bar/index.d.ts +2 -0
  122. package/dist/experimental/top-bar/top-bar.d.ts +60 -0
  123. package/dist/experimental/top-bar/top-bar.js +162 -0
  124. package/dist/field/choice.d.ts +21 -0
  125. package/dist/field/choice.js +67 -0
  126. package/dist/field/field.d.ts +31 -0
  127. package/dist/field/field.js +78 -0
  128. package/dist/field/index.d.ts +3 -0
  129. package/dist/field/index.js +1 -0
  130. package/dist/fieldset/fieldset.d.ts +19 -0
  131. package/dist/fieldset/fieldset.js +29 -0
  132. package/dist/fieldset/index.d.ts +2 -0
  133. package/dist/fieldset/index.js +1 -0
  134. package/dist/format/format.d.ts +12 -0
  135. package/dist/format/format.js +50 -0
  136. package/dist/format/index.d.ts +2 -0
  137. package/dist/format/index.js +1 -0
  138. package/dist/grid/grid.d.ts +30 -0
  139. package/dist/grid/grid.js +36 -0
  140. package/dist/grid/index.d.ts +2 -0
  141. package/dist/grid/index.js +1 -0
  142. package/dist/heading/heading.d.ts +23 -0
  143. package/dist/heading/heading.js +30 -0
  144. package/dist/heading/index.d.ts +2 -0
  145. package/dist/heading/index.js +1 -0
  146. package/dist/index.d.ts +84 -1
  147. package/dist/index.js +40 -0
  148. package/dist/inline/index.d.ts +2 -0
  149. package/dist/inline/index.js +1 -0
  150. package/dist/inline/inline.d.ts +19 -0
  151. package/dist/inline/inline.js +16 -0
  152. package/dist/input/index.d.ts +2 -0
  153. package/dist/input/index.js +1 -0
  154. package/dist/input/input.d.ts +37 -0
  155. package/dist/input/input.js +63 -0
  156. package/dist/internal/control.d.ts +9 -0
  157. package/dist/internal/control.js +4 -0
  158. package/dist/internal/cx.js +7 -0
  159. package/dist/internal/media.js +15 -0
  160. package/dist/internal/messages.d.ts +75 -0
  161. package/dist/internal/messages.js +61 -0
  162. package/dist/internal/refs.js +23 -0
  163. package/dist/internal/render.d.ts +6 -0
  164. package/dist/internal/render.js +22 -0
  165. package/dist/internal/space.d.ts +9 -0
  166. package/dist/internal/text.d.ts +5 -0
  167. package/dist/internal/text.js +18 -0
  168. package/dist/link/index.d.ts +2 -0
  169. package/dist/link/index.js +1 -0
  170. package/dist/link/link.d.ts +19 -0
  171. package/dist/link/link.js +32 -0
  172. package/dist/locales/hi.d.ts +8 -0
  173. package/dist/locales/hi.js +60 -0
  174. package/dist/menu/index.d.ts +2 -0
  175. package/dist/menu/index.js +1 -0
  176. package/dist/menu/menu.d.ts +76 -0
  177. package/dist/menu/menu.js +172 -0
  178. package/dist/popover/index.d.ts +2 -0
  179. package/dist/popover/index.js +1 -0
  180. package/dist/popover/popover.d.ts +33 -0
  181. package/dist/popover/popover.js +48 -0
  182. package/dist/progress/index.d.ts +2 -0
  183. package/dist/progress/index.js +1 -0
  184. package/dist/progress/progress.d.ts +26 -0
  185. package/dist/progress/progress.js +62 -0
  186. package/dist/provider/index.d.ts +3 -0
  187. package/dist/provider/index.js +1 -0
  188. package/dist/provider/provider.d.ts +46 -0
  189. package/dist/provider/provider.js +90 -0
  190. package/dist/radio/index.d.ts +2 -0
  191. package/dist/radio/index.js +1 -0
  192. package/dist/radio/radio.d.ts +31 -0
  193. package/dist/radio/radio.js +47 -0
  194. package/dist/scroll-area/index.d.ts +2 -0
  195. package/dist/scroll-area/index.js +1 -0
  196. package/dist/scroll-area/scroll-area.d.ts +18 -0
  197. package/dist/scroll-area/scroll-area.js +39 -0
  198. package/dist/select/index.d.ts +2 -0
  199. package/dist/select/index.js +1 -0
  200. package/dist/select/select.d.ts +36 -0
  201. package/dist/select/select.js +82 -0
  202. package/dist/separator/index.d.ts +2 -0
  203. package/dist/separator/index.js +1 -0
  204. package/dist/separator/separator.d.ts +16 -0
  205. package/dist/separator/separator.js +16 -0
  206. package/dist/skeleton/index.d.ts +2 -0
  207. package/dist/skeleton/index.js +1 -0
  208. package/dist/skeleton/skeleton.d.ts +12 -0
  209. package/dist/skeleton/skeleton.js +18 -0
  210. package/dist/skip-link/index.d.ts +2 -0
  211. package/dist/skip-link/index.js +1 -0
  212. package/dist/skip-link/skip-link.d.ts +13 -0
  213. package/dist/skip-link/skip-link.js +18 -0
  214. package/dist/slider/index.d.ts +2 -0
  215. package/dist/slider/index.js +1 -0
  216. package/dist/slider/slider.d.ts +20 -0
  217. package/dist/slider/slider.js +58 -0
  218. package/dist/spinner/index.d.ts +2 -0
  219. package/dist/spinner/index.js +1 -0
  220. package/dist/spinner/spinner.d.ts +15 -0
  221. package/dist/spinner/spinner.js +19 -0
  222. package/dist/stack/index.d.ts +2 -0
  223. package/dist/stack/index.js +1 -0
  224. package/dist/stack/stack.d.ts +17 -0
  225. package/dist/stack/stack.js +15 -0
  226. package/dist/styles.css +4548 -0
  227. package/dist/switch/index.d.ts +2 -0
  228. package/dist/switch/index.js +1 -0
  229. package/dist/switch/switch.d.ts +18 -0
  230. package/dist/switch/switch.js +31 -0
  231. package/dist/table/index.d.ts +2 -0
  232. package/dist/table/index.js +1 -0
  233. package/dist/table/table.d.ts +36 -0
  234. package/dist/table/table.js +69 -0
  235. package/dist/tabs/index.d.ts +2 -0
  236. package/dist/tabs/index.js +1 -0
  237. package/dist/tabs/tabs.d.ts +43 -0
  238. package/dist/tabs/tabs.js +66 -0
  239. package/dist/text/index.d.ts +3 -0
  240. package/dist/text/index.js +1 -0
  241. package/dist/text/text.d.ts +24 -0
  242. package/dist/text/text.js +24 -0
  243. package/dist/text-field/index.d.ts +2 -0
  244. package/dist/text-field/index.js +1 -0
  245. package/dist/text-field/text-field.d.ts +15 -0
  246. package/dist/text-field/text-field.js +43 -0
  247. package/dist/toast/index.d.ts +2 -0
  248. package/dist/toast/index.js +1 -0
  249. package/dist/toast/toast.d.ts +57 -0
  250. package/dist/toast/toast.js +124 -0
  251. package/dist/toggle/index.d.ts +2 -0
  252. package/dist/toggle/index.js +1 -0
  253. package/dist/toggle/toggle.d.ts +48 -0
  254. package/dist/toggle/toggle.js +54 -0
  255. package/dist/tooltip/index.d.ts +2 -0
  256. package/dist/tooltip/index.js +1 -0
  257. package/dist/tooltip/tooltip.d.ts +26 -0
  258. package/dist/tooltip/tooltip.js +39 -0
  259. package/dist/visually-hidden/index.d.ts +2 -0
  260. package/dist/visually-hidden/index.js +1 -0
  261. package/dist/visually-hidden/visually-hidden.d.ts +8 -0
  262. package/dist/visually-hidden/visually-hidden.js +12 -0
  263. package/package.json +170 -2
@@ -0,0 +1,224 @@
1
+ "use client";
2
+ import { cx } from "../../internal/cx.js";
3
+ import { Tooltip } from "../../tooltip/tooltip.js";
4
+ import { IconButton } from "../../button/button.js";
5
+ import { useMediaQuery } from "../../internal/media.js";
6
+ import { useMessages } from "../../provider/provider.js";
7
+ import { SkipLink } from "../../skip-link/skip-link.js";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { createContext, use, useEffect, useId, useRef, useState } from "react";
10
+ import { useDirection } from "@base-ui/react/direction-provider";
11
+ import { XIcon } from "@qeetrix/icons/icons/x";
12
+ import { Drawer } from "@base-ui/react/drawer";
13
+ import { PanelLeftCloseIcon } from "@qeetrix/icons/icons/panel-left-close";
14
+ import { PanelLeftOpenIcon } from "@qeetrix/icons/icons/panel-left-open";
15
+ // src/experimental/app-shell/app-shell.tsx
16
+ /** The `expanded` window class (tokens `breakpoint.expanded`): the sidebar is a column from here. */
17
+ const WIDE = "(width >= 840px)";
18
+ /** How long the pointer rests on the rail before it expands over the page. */
19
+ const PEEK_DELAY = 200;
20
+ const ShellContext = createContext(null);
21
+ const outside = {
22
+ collapsed: false,
23
+ inDrawer: false,
24
+ narrow: false,
25
+ expand: () => {},
26
+ closeDrawer: () => {},
27
+ openNavigation: () => {},
28
+ drawers: [],
29
+ activeDrawer: null,
30
+ toggleDrawer: () => {},
31
+ strings: {
32
+ collapse: "",
33
+ expand: "",
34
+ open: "",
35
+ navigation: "",
36
+ tools: "",
37
+ closePanel: ""
38
+ }
39
+ };
40
+ /** The shell's state, for its own parts. Outside an AppShell, a sidebar is expanded. */
41
+ function useShellState() {
42
+ return use(ShellContext) ?? outside;
43
+ }
44
+ /** The AppShell's state, for content inside its sidebar or bar. */
45
+ function useAppShell() {
46
+ const { collapsed, inDrawer } = useShellState();
47
+ return { collapsed: collapsed && !inDrawer };
48
+ }
49
+ /**
50
+ * The frame of a Qeet console (07-components.md, C1 · Ruled): the black scope bar across the full
51
+ * width; the ruled navigation column, which collapses to a 48px icon rail that expands while
52
+ * hovered or focused; the page; and a panel on the end edge that the bar's tools open. Seams are
53
+ * 1px rules, never shadows. Compact by default.
54
+ */
55
+ function AppShell({ topBar, sidebar, drawers = [], activeDrawer, defaultActiveDrawer = null, onActiveDrawerChange, notifications, density = "compact", children, sidebarCollapsed, defaultSidebarCollapsed = false, onSidebarCollapsedChange, mainId = "main", messages, className, ...props }) {
56
+ const strings = useMessages("appShell", messages);
57
+ const skip = useMessages("skipLink");
58
+ const wide = useMediaQuery(WIDE, true);
59
+ const rtl = useDirection() === "rtl";
60
+ const panelTitle = useId();
61
+ const [ownCollapsed, setOwnCollapsed] = useState(defaultSidebarCollapsed);
62
+ const collapsed = sidebarCollapsed ?? ownCollapsed;
63
+ const setCollapsed = (next) => {
64
+ if (sidebarCollapsed === void 0) setOwnCollapsed(next);
65
+ onSidebarCollapsedChange?.(next);
66
+ };
67
+ const [ownDrawer, setOwnDrawer] = useState(defaultActiveDrawer);
68
+ const active = activeDrawer === void 0 ? ownDrawer : activeDrawer;
69
+ const setActive = (next) => {
70
+ if (activeDrawer === void 0) setOwnDrawer(next);
71
+ onActiveDrawerChange?.(next);
72
+ };
73
+ const panel = drawers.find((drawer) => drawer.id === active);
74
+ const [navOpen, setNavOpen] = useState(false);
75
+ const [peek, setPeek] = useState(false);
76
+ const timer = useRef(void 0);
77
+ useEffect(() => () => window.clearTimeout(timer.current), []);
78
+ const startPeek = () => {
79
+ if (!collapsed) return;
80
+ window.clearTimeout(timer.current);
81
+ timer.current = window.setTimeout(() => setPeek(true), PEEK_DELAY);
82
+ };
83
+ const endPeek = () => {
84
+ window.clearTimeout(timer.current);
85
+ setPeek(false);
86
+ };
87
+ const shell = {
88
+ collapsed: collapsed && !peek,
89
+ inDrawer: false,
90
+ narrow: !wide,
91
+ expand: () => {
92
+ endPeek();
93
+ setCollapsed(false);
94
+ },
95
+ closeDrawer: () => setNavOpen(false),
96
+ openNavigation: () => setNavOpen(true),
97
+ drawers,
98
+ activeDrawer: active,
99
+ toggleDrawer: (id) => setActive(active === id ? null : id),
100
+ strings
101
+ };
102
+ const collapseControl = /* @__PURE__ */ jsxs("button", {
103
+ type: "button",
104
+ className: "qx-app-shell-collapse",
105
+ "aria-expanded": !collapsed,
106
+ onClick: () => {
107
+ endPeek();
108
+ setCollapsed(!collapsed);
109
+ },
110
+ children: [/* @__PURE__ */ jsx(collapsed ? PanelLeftOpenIcon : PanelLeftCloseIcon, {
111
+ shape: "round",
112
+ size: 16,
113
+ "aria-hidden": true,
114
+ className: "qx-app-shell-collapse-icon"
115
+ }), /* @__PURE__ */ jsx("span", {
116
+ className: "qx-app-shell-collapse-label",
117
+ children: collapsed ? strings.expand : strings.collapse
118
+ })]
119
+ });
120
+ return /* @__PURE__ */ jsx(ShellContext, {
121
+ value: shell,
122
+ children: /* @__PURE__ */ jsxs("div", {
123
+ ...props,
124
+ className: cx("qx-app-shell", className),
125
+ "data-slot": "app-shell",
126
+ "data-qx-density": density === "inherit" ? void 0 : density,
127
+ "data-collapsed": collapsed ? "" : void 0,
128
+ "data-drawer-open": panel ? "" : void 0,
129
+ children: [
130
+ /* @__PURE__ */ jsx(SkipLink, {
131
+ href: `#${mainId}`,
132
+ children: skip.label
133
+ }),
134
+ topBar != null && /* @__PURE__ */ jsx("div", {
135
+ className: "qx-app-shell-top",
136
+ children: topBar
137
+ }),
138
+ sidebar != null && /* @__PURE__ */ jsx("div", {
139
+ className: "qx-app-shell-nav",
140
+ "data-slot": "app-shell-nav",
141
+ "data-peek": peek ? "" : void 0,
142
+ onPointerEnter: startPeek,
143
+ onPointerLeave: endPeek,
144
+ onFocus: () => collapsed && setPeek(true),
145
+ onBlur: (event) => {
146
+ if (!event.currentTarget.contains(event.relatedTarget)) setPeek(false);
147
+ },
148
+ children: /* @__PURE__ */ jsxs("div", {
149
+ className: "qx-app-shell-nav-panel",
150
+ children: [/* @__PURE__ */ jsx("div", {
151
+ className: "qx-app-shell-nav-body",
152
+ children: sidebar
153
+ }), /* @__PURE__ */ jsx("div", {
154
+ className: "qx-app-shell-nav-foot",
155
+ children: /* @__PURE__ */ jsx(Tooltip, {
156
+ content: strings.expand,
157
+ side: "inline-end",
158
+ disabled: !collapsed || peek,
159
+ children: collapseControl
160
+ })
161
+ })]
162
+ })
163
+ }),
164
+ /* @__PURE__ */ jsxs("main", {
165
+ id: mainId,
166
+ tabIndex: -1,
167
+ className: "qx-app-shell-main",
168
+ "data-slot": "app-shell-main",
169
+ children: [notifications != null && /* @__PURE__ */ jsx("div", {
170
+ className: "qx-app-shell-notifications",
171
+ "data-slot": "app-shell-notifications",
172
+ children: notifications
173
+ }), children]
174
+ }),
175
+ panel && /* @__PURE__ */ jsxs("aside", {
176
+ className: "qx-app-shell-panel",
177
+ "data-slot": "app-shell-panel",
178
+ "aria-labelledby": panelTitle,
179
+ children: [/* @__PURE__ */ jsxs("div", {
180
+ className: "qx-app-shell-panel-header",
181
+ children: [/* @__PURE__ */ jsx("h2", {
182
+ id: panelTitle,
183
+ className: "qx-app-shell-panel-title",
184
+ children: panel.label
185
+ }), /* @__PURE__ */ jsx(IconButton, {
186
+ icon: XIcon,
187
+ "aria-label": strings.closePanel,
188
+ size: "sm",
189
+ tone: "neutral",
190
+ tooltip: false,
191
+ onClick: () => setActive(null)
192
+ })]
193
+ }), /* @__PURE__ */ jsx("div", {
194
+ className: "qx-app-shell-panel-body",
195
+ children: panel.content
196
+ })]
197
+ }),
198
+ sidebar != null && /* @__PURE__ */ jsx(Drawer.Root, {
199
+ open: navOpen && !wide,
200
+ onOpenChange: setNavOpen,
201
+ swipeDirection: rtl ? "right" : "left",
202
+ children: /* @__PURE__ */ jsxs(Drawer.Portal, { children: [/* @__PURE__ */ jsx(Drawer.Backdrop, { className: "qx-app-shell-backdrop" }), /* @__PURE__ */ jsx(Drawer.Viewport, {
203
+ className: "qx-app-shell-viewport",
204
+ children: /* @__PURE__ */ jsx(Drawer.Popup, {
205
+ className: "qx-app-shell-drawer",
206
+ "data-slot": "app-shell-drawer",
207
+ "data-qx-density": density === "inherit" ? void 0 : density,
208
+ "aria-label": strings.navigation,
209
+ children: /* @__PURE__ */ jsx(ShellContext, {
210
+ value: {
211
+ ...shell,
212
+ collapsed: false,
213
+ inDrawer: true
214
+ },
215
+ children: sidebar
216
+ })
217
+ })
218
+ })] })
219
+ })
220
+ ]
221
+ })
222
+ });
223
+ }
224
+ export { AppShell, useAppShell, useShellState };
@@ -0,0 +1,2 @@
1
+ import { AppShell, AppShellDensity, AppShellDrawer, AppShellProps, AppShellState, useAppShell } from "./app-shell.js";
2
+ export { AppShell, type AppShellDensity, type AppShellDrawer, type AppShellProps, type AppShellState, useAppShell };
@@ -0,0 +1,27 @@
1
+ import { RenderProp } from "../../internal/render.js";
2
+ import { ComponentProps, ReactElement, ReactNode } from "react";
3
+ export interface BreadcrumbProps extends ComponentProps<"nav"> {
4
+ /**
5
+ * Names the navigation landmark. Default "Breadcrumb". Breadcrumb is server-safe, so
6
+ * QeetrixProvider's strings don't reach it: pass a translation here.
7
+ */
8
+ label?: string;
9
+ /** BreadcrumbItems, the current page last. */
10
+ children?: ReactNode;
11
+ }
12
+ export interface BreadcrumbItemProps extends Omit<ComponentProps<"li">, "children"> {
13
+ /** Where it goes. The current item takes none. */
14
+ href?: string;
15
+ /** A router link instead of `<a>`, e.g. `render={<Link to="/users" />}`. */
16
+ render?: RenderProp;
17
+ /** The current page: plain text with `aria-current="page"`. */
18
+ current?: boolean;
19
+ children: ReactNode;
20
+ }
21
+ /**
22
+ * Where the page sits, in a console header (07-components.md, C1): an ordered list of links,
23
+ * separated by sharp chevrons that mirror in right-to-left text. Server-safe.
24
+ */
25
+ export declare function Breadcrumb({ label, className, children, ...props }: BreadcrumbProps): ReactElement;
26
+ /** One step. A long label truncates and keeps its full text as a title. */
27
+ export declare function BreadcrumbItem({ href, render, current, className, children, ...props }: BreadcrumbItemProps): ReactElement;
@@ -0,0 +1,48 @@
1
+ import { cx } from "../../internal/cx.js";
2
+ import { renderElement } from "../../internal/render.js";
3
+ import { messages } from "../../internal/messages.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { ChevronRightIcon } from "@qeetrix/icons/icons/chevron-right";
6
+ // src/experimental/breadcrumb/breadcrumb.tsx
7
+ /**
8
+ * Where the page sits, in a console header (07-components.md, C1): an ordered list of links,
9
+ * separated by sharp chevrons that mirror in right-to-left text. Server-safe.
10
+ */
11
+ function Breadcrumb({ label = messages.breadcrumb.label, className, children, ...props }) {
12
+ return /* @__PURE__ */ jsx("nav", {
13
+ ...props,
14
+ "aria-label": label,
15
+ className: cx("qx-breadcrumb", className),
16
+ "data-slot": "breadcrumb",
17
+ children: /* @__PURE__ */ jsx("ol", {
18
+ className: "qx-breadcrumb-list",
19
+ children
20
+ })
21
+ });
22
+ }
23
+ /** One step. A long label truncates and keeps its full text as a title. */
24
+ function BreadcrumbItem({ href, render, current, className, children, ...props }) {
25
+ const title = typeof children === "string" ? children : void 0;
26
+ return /* @__PURE__ */ jsxs("li", {
27
+ ...props,
28
+ className: cx("qx-breadcrumb-item", className),
29
+ "data-slot": "breadcrumb-item",
30
+ children: [/* @__PURE__ */ jsx(ChevronRightIcon, {
31
+ shape: "sharp",
32
+ size: 12,
33
+ "aria-hidden": true,
34
+ className: "qx-breadcrumb-separator"
35
+ }), current ? /* @__PURE__ */ jsx("span", {
36
+ className: "qx-breadcrumb-current",
37
+ "aria-current": "page",
38
+ title,
39
+ children
40
+ }) : renderElement("a", render, {
41
+ href,
42
+ className: "qx-breadcrumb-link",
43
+ title,
44
+ children
45
+ })]
46
+ });
47
+ }
48
+ export { Breadcrumb, BreadcrumbItem };
@@ -0,0 +1,2 @@
1
+ import { Breadcrumb, BreadcrumbItem, BreadcrumbItemProps, BreadcrumbProps } from "./breadcrumb.js";
2
+ export { Breadcrumb, BreadcrumbItem, type BreadcrumbItemProps, type BreadcrumbProps };
@@ -0,0 +1,27 @@
1
+ import { IconButtonProps } from "../../button/button.js";
2
+ import { QeetrixMessages } from "../../internal/messages.js";
3
+ import { ReactElement, ReactNode } from "react";
4
+ /** Where a copy stands: ready, just copied, or failed (no clipboard access). */
5
+ export type CopyButtonState = "idle" | "copied" | "failed";
6
+ export interface CopyButtonProps extends Omit<IconButtonProps, "icon" | "aria-label" | "tooltip" | "loading" | "onClick" | "onCopy" | "value"> {
7
+ /** The text to copy, or a function that returns it (it may be async, e.g. a fresh token). */
8
+ value: string | (() => string | Promise<string>);
9
+ /** What it copies, as the button's name and tooltip: "Copy user ID". Default "Copy". */
10
+ label?: string;
11
+ /** A visible label makes it a Button (label first, icon last) instead of an IconButton. */
12
+ children?: ReactNode;
13
+ /** Called with the text once it's on the clipboard. */
14
+ onCopied?: (value: string) => void;
15
+ /** Called when copying fails, e.g. no clipboard access outside a secure context. */
16
+ onCopyError?: (error: unknown) => void;
17
+ /** How long "Copied" (or the failure) shows before it resets, in milliseconds. Default 2000. */
18
+ resetAfter?: number;
19
+ /** This button's strings, over QeetrixProvider's and the English defaults. */
20
+ messages?: Partial<QeetrixMessages["copyButton"]>;
21
+ }
22
+ /**
23
+ * Copies text to the clipboard (07-components.md, C2). Focus stays on the button; the icon turns
24
+ * into a check, and "Copied" is announced politely, then it resets. It fails visibly, never
25
+ * silently, where the page has no clipboard access.
26
+ */
27
+ export declare function CopyButton({ value, label, children, onCopied, onCopyError, resetAfter, messages, variant, tone, className, ...props }: CopyButtonProps): ReactElement;
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { cx } from "../../internal/cx.js";
3
+ import { Button, IconButton } from "../../button/button.js";
4
+ import { useMessages } from "../../provider/provider.js";
5
+ import { VisuallyHidden } from "../../visually-hidden/visually-hidden.js";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { useEffect, useRef, useState } from "react";
8
+ import { CheckIcon } from "@qeetrix/icons/icons/check";
9
+ import { CircleAlertIcon } from "@qeetrix/icons/icons/circle-alert";
10
+ import { CopyIcon } from "@qeetrix/icons/icons/copy";
11
+ // src/experimental/copy-button/copy-button.tsx
12
+ /**
13
+ * Copies text to the clipboard (07-components.md, C2). Focus stays on the button; the icon turns
14
+ * into a check, and "Copied" is announced politely, then it resets. It fails visibly, never
15
+ * silently, where the page has no clipboard access.
16
+ */
17
+ function CopyButton({ value, label, children, onCopied, onCopyError, resetAfter = 2e3, messages, variant = "ghost", tone = "neutral", className, ...props }) {
18
+ const strings = useMessages("copyButton", messages);
19
+ const [state, setState] = useState("idle");
20
+ const timer = useRef(void 0);
21
+ useEffect(() => () => window.clearTimeout(timer.current), []);
22
+ const copy = async () => {
23
+ window.clearTimeout(timer.current);
24
+ setState("idle");
25
+ try {
26
+ const text = typeof value === "function" ? await value() : value;
27
+ if (!navigator.clipboard?.writeText) throw new Error("The clipboard isn't available here.");
28
+ await navigator.clipboard.writeText(text);
29
+ setState("copied");
30
+ onCopied?.(text);
31
+ } catch (error) {
32
+ setState("failed");
33
+ onCopyError?.(error);
34
+ }
35
+ timer.current = window.setTimeout(() => setState("idle"), resetAfter);
36
+ };
37
+ const Icon = state === "copied" ? CheckIcon : state === "failed" ? CircleAlertIcon : CopyIcon;
38
+ const announcement = state === "copied" ? strings.copied : state === "failed" ? strings.failed : "";
39
+ const name = label ?? strings.copy;
40
+ const shared = {
41
+ ...props,
42
+ variant,
43
+ tone,
44
+ icon: Icon,
45
+ className: cx("qx-copy-button", className),
46
+ "data-state": state,
47
+ onClick: copy
48
+ };
49
+ return /* @__PURE__ */ jsxs(Fragment, { children: [children != null ? /* @__PURE__ */ jsx(Button, {
50
+ ...shared,
51
+ children
52
+ }) : /* @__PURE__ */ jsx(IconButton, {
53
+ ...shared,
54
+ "aria-label": name,
55
+ tooltip: announcement || name
56
+ }), /* @__PURE__ */ jsx(VisuallyHidden, {
57
+ role: "status",
58
+ children: announcement
59
+ })] });
60
+ }
61
+ export { CopyButton };
@@ -0,0 +1,2 @@
1
+ import { CopyButton, CopyButtonProps, CopyButtonState } from "./copy-button.js";
2
+ export { CopyButton, type CopyButtonProps, type CopyButtonState };
@@ -0,0 +1,23 @@
1
+ import { QeetrixMessages } from "../../internal/messages.js";
2
+ import { ReactElement, ReactNode } from "react";
3
+ export interface DataStateProps {
4
+ /** The content is loading. It wins over every other state. */
5
+ loading?: boolean;
6
+ /** Anything truthy shows the error state. The raw error is never shown: log it in the product. */
7
+ error?: unknown;
8
+ /** The content loaded and there's nothing in it. */
9
+ empty?: boolean;
10
+ /** The EmptyState to show when empty. */
11
+ emptyState?: ReactNode;
12
+ /** Adds a "Try again" button to the error state. */
13
+ onRetry?: () => void;
14
+ /** The content, shown when it's loaded and not empty. */
15
+ children?: ReactNode;
16
+ /** The error state's strings, over QeetrixProvider's and the English defaults. */
17
+ messages?: Partial<QeetrixMessages["dataState"]>;
18
+ }
19
+ /**
20
+ * The four states of loaded content in one place (07-components.md, C1): loading, then error, then
21
+ * empty, then the content.
22
+ */
23
+ export declare function DataState({ loading, error, empty, emptyState, onRetry, children, messages }: DataStateProps): ReactElement;
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { Spinner } from "../../spinner/spinner.js";
3
+ import { Button } from "../../button/button.js";
4
+ import { useMessages } from "../../provider/provider.js";
5
+ import { EmptyState } from "./empty-state.js";
6
+ import { Fragment, jsx } from "react/jsx-runtime";
7
+ import { CircleAlertIcon } from "@qeetrix/icons/icons/circle-alert";
8
+ // src/experimental/empty-state/data-state.tsx
9
+ /**
10
+ * The four states of loaded content in one place (07-components.md, C1): loading, then error, then
11
+ * empty, then the content.
12
+ */
13
+ function DataState({ loading, error, empty, emptyState, onRetry, children, messages }) {
14
+ const strings = useMessages("dataState", messages);
15
+ const spinner = useMessages("spinner");
16
+ if (loading) return /* @__PURE__ */ jsx("div", {
17
+ className: "qx-data-state",
18
+ "data-slot": "data-state",
19
+ "aria-busy": "true",
20
+ children: /* @__PURE__ */ jsx(Spinner, {
21
+ size: "lg",
22
+ label: spinner.label
23
+ })
24
+ });
25
+ if (error) return /* @__PURE__ */ jsx(EmptyState, {
26
+ size: "section",
27
+ tone: "danger",
28
+ icon: CircleAlertIcon,
29
+ title: strings.errorTitle,
30
+ description: strings.errorDescription,
31
+ actions: onRetry && /* @__PURE__ */ jsx(Button, {
32
+ variant: "soft",
33
+ onClick: onRetry,
34
+ children: strings.retry
35
+ })
36
+ });
37
+ if (empty) return /* @__PURE__ */ jsx(Fragment, { children: emptyState });
38
+ return /* @__PURE__ */ jsx(Fragment, { children });
39
+ }
40
+ export { DataState };
@@ -0,0 +1,26 @@
1
+ import { HeadingLevel } from "../../heading/heading.js";
2
+ import { QeetrixIcon } from "../../internal/control.js";
3
+ import { ComponentProps, ReactElement, ReactNode } from "react";
4
+ export type EmptyStateSize = "page" | "section";
5
+ export type EmptyStateTone = "neutral" | "danger";
6
+ export interface EmptyStateProps extends Omit<ComponentProps<"div">, "title"> {
7
+ /** A 24px sharp icon from `@qeetrix/icons`. */
8
+ icon?: QeetrixIcon;
9
+ /** What's missing or what went wrong, in a few words: "No users yet". */
10
+ title: ReactNode;
11
+ /** One or two sentences: why, and what to do. */
12
+ description?: ReactNode;
13
+ /** Buttons, one primary at most. */
14
+ actions?: ReactNode;
15
+ /** `page` (default) for a whole view; `section` inside a card or table. */
16
+ size?: EmptyStateSize;
17
+ /** `danger` colours the icon for an error. Default `neutral`. */
18
+ tone?: EmptyStateTone;
19
+ /** Make the title a heading, when this is the page's main message. Otherwise it's styled text. */
20
+ level?: HeadingLevel;
21
+ }
22
+ /**
23
+ * The message where content would be: nothing yet, no results, or an error (07-components.md, C1).
24
+ * Centred, at most 480px wide. Server-safe.
25
+ */
26
+ export declare function EmptyState({ icon: Icon, title, description, actions, size, tone, level, className, ...props }: EmptyStateProps): ReactElement;
@@ -0,0 +1,46 @@
1
+ import { cx } from "../../internal/cx.js";
2
+ import { Heading } from "../../heading/heading.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ // src/experimental/empty-state/empty-state.tsx
5
+ /**
6
+ * The message where content would be: nothing yet, no results, or an error (07-components.md, C1).
7
+ * Centred, at most 480px wide. Server-safe.
8
+ */
9
+ function EmptyState({ icon: Icon, title, description, actions, size = "page", tone = "neutral", level, className, ...props }) {
10
+ return /* @__PURE__ */ jsxs("div", {
11
+ ...props,
12
+ className: cx("qx-empty-state", className),
13
+ "data-slot": "empty-state",
14
+ "data-size": size,
15
+ "data-tone": tone,
16
+ children: [
17
+ Icon && /* @__PURE__ */ jsx(Icon, {
18
+ shape: "sharp",
19
+ size: 24,
20
+ "aria-hidden": true,
21
+ className: "qx-empty-state-icon"
22
+ }),
23
+ level !== void 0 ? /* @__PURE__ */ jsx(Heading, {
24
+ level,
25
+ size: size === "page" ? "title-md" : "title-sm",
26
+ align: "center",
27
+ children: title
28
+ }) : /* @__PURE__ */ jsx("p", {
29
+ className: "qx-empty-state-title",
30
+ "data-slot": "empty-state-title",
31
+ children: title
32
+ }),
33
+ description != null && /* @__PURE__ */ jsx("p", {
34
+ className: "qx-empty-state-description",
35
+ "data-slot": "empty-state-description",
36
+ children: description
37
+ }),
38
+ actions != null && /* @__PURE__ */ jsx("div", {
39
+ className: "qx-empty-state-actions",
40
+ "data-slot": "empty-state-actions",
41
+ children: actions
42
+ })
43
+ ]
44
+ });
45
+ }
46
+ export { EmptyState };
@@ -0,0 +1,3 @@
1
+ import { DataState, DataStateProps } from "./data-state.js";
2
+ import { EmptyState, EmptyStateProps, EmptyStateSize, EmptyStateTone } from "./empty-state.js";
3
+ export { DataState, type DataStateProps, EmptyState, type EmptyStateProps, type EmptyStateSize, type EmptyStateTone };
@@ -0,0 +1,24 @@
1
+ import { Alert, AlertProps, AlertTone } from "./alert/alert.js";
2
+ import "./alert/index.js";
3
+ import { AppShell, AppShellDensity, AppShellDrawer, AppShellProps, AppShellState, useAppShell } from "./app-shell/app-shell.js";
4
+ import "./app-shell/index.js";
5
+ import { Breadcrumb, BreadcrumbItem, BreadcrumbItemProps, BreadcrumbProps } from "./breadcrumb/breadcrumb.js";
6
+ import "./breadcrumb/index.js";
7
+ import { CopyButton, CopyButtonProps, CopyButtonState } from "./copy-button/copy-button.js";
8
+ import "./copy-button/index.js";
9
+ import { DataState, DataStateProps } from "./empty-state/data-state.js";
10
+ import { EmptyState, EmptyStateProps, EmptyStateSize, EmptyStateTone } from "./empty-state/empty-state.js";
11
+ import "./empty-state/index.js";
12
+ import { OTPInput, OTPInputProps, OTPInputSize } from "./otp-input/otp-input.js";
13
+ import "./otp-input/index.js";
14
+ import { PageContent, PageContentProps, PageContentWidth, PageHeader, PageHeaderProps } from "./page-header/page-header.js";
15
+ import "./page-header/index.js";
16
+ import { PasswordInput, PasswordInputProps } from "./password-input/password-input.js";
17
+ import "./password-input/index.js";
18
+ import { Sidebar, SidebarGroup, SidebarGroupProps, SidebarItem, SidebarItemProps, SidebarProps } from "./sidebar/sidebar.js";
19
+ import "./sidebar/index.js";
20
+ import { Stat, StatGroup, StatGroupProps, StatProps, StatTone } from "./stat/stat.js";
21
+ import "./stat/index.js";
22
+ import { TopBar, TopBarProps, TopBarSearch, TopBarSearchProps, TopBarStatus, TopBarSwitcher, TopBarSwitcherProps } from "./top-bar/top-bar.js";
23
+ import "./top-bar/index.js";
24
+ export { Alert, type AlertProps, type AlertTone, AppShell, type AppShellDensity, type AppShellDrawer, type AppShellProps, type AppShellState, Breadcrumb, BreadcrumbItem, type BreadcrumbItemProps, type BreadcrumbProps, CopyButton, type CopyButtonProps, type CopyButtonState, DataState, type DataStateProps, EmptyState, type EmptyStateProps, type EmptyStateSize, type EmptyStateTone, OTPInput, type OTPInputProps, type OTPInputSize, PageContent, type PageContentProps, type PageContentWidth, PageHeader, type PageHeaderProps, PasswordInput, type PasswordInputProps, Sidebar, SidebarGroup, type SidebarGroupProps, SidebarItem, type SidebarItemProps, type SidebarProps, Stat, StatGroup, type StatGroupProps, type StatProps, type StatTone, TopBar, type TopBarProps, TopBarSearch, type TopBarSearchProps, type TopBarStatus, TopBarSwitcher, type TopBarSwitcherProps, useAppShell };
@@ -0,0 +1,13 @@
1
+ import { Alert } from "./alert/alert.js";
2
+ import { AppShell, useAppShell } from "./app-shell/app-shell.js";
3
+ import { Breadcrumb, BreadcrumbItem } from "./breadcrumb/breadcrumb.js";
4
+ import { CopyButton } from "./copy-button/copy-button.js";
5
+ import { EmptyState } from "./empty-state/empty-state.js";
6
+ import { DataState } from "./empty-state/data-state.js";
7
+ import { OTPInput } from "./otp-input/otp-input.js";
8
+ import { PageContent, PageHeader } from "./page-header/page-header.js";
9
+ import { PasswordInput } from "./password-input/password-input.js";
10
+ import { Sidebar, SidebarGroup, SidebarItem } from "./sidebar/sidebar.js";
11
+ import { Stat, StatGroup } from "./stat/stat.js";
12
+ import { TopBar, TopBarSearch, TopBarSwitcher } from "./top-bar/top-bar.js";
13
+ export { Alert, AppShell, Breadcrumb, BreadcrumbItem, CopyButton, DataState, EmptyState, OTPInput, PageContent, PageHeader, PasswordInput, Sidebar, SidebarGroup, SidebarItem, Stat, StatGroup, TopBar, TopBarSearch, TopBarSwitcher, useAppShell };
@@ -0,0 +1,2 @@
1
+ import { OTPInput, OTPInputProps, OTPInputSize } from "./otp-input.js";
2
+ export { OTPInput, type OTPInputProps, type OTPInputSize };
@@ -0,0 +1,27 @@
1
+ import { QeetrixMessages } from "../../internal/messages.js";
2
+ import { CSSProperties, ReactElement } from "react";
3
+ import { OTPField } from "@base-ui/react/otp-field";
4
+ export type OTPInputSize = "md" | "lg";
5
+ export interface OTPInputProps extends Omit<OTPField.Root.Props, "className" | "style" | "children" | "render" | "length"> {
6
+ /** How many characters. Default 6. */
7
+ length?: number;
8
+ /** Split the slots into groups, e.g. `[3, 3]` shows 123 – 456. The sizes must add up to `length`. */
9
+ groups?: readonly number[];
10
+ /** `md` (default) in consoles; `lg` on sign-in pages. */
11
+ size?: OTPInputSize;
12
+ /** Names the first slot when there's no Field label. */
13
+ "aria-label"?: string;
14
+ "aria-labelledby"?: string;
15
+ /** Placement only. */
16
+ className?: string;
17
+ style?: CSSProperties;
18
+ /** This input's strings, over QeetrixProvider's and the English defaults. */
19
+ messages?: Partial<QeetrixMessages["otpInput"]>;
20
+ }
21
+ /**
22
+ * A one-time code in separate slots (07-components.md, C2), on Base UI's OTP Field: typing moves on,
23
+ * Backspace moves back, pasting fills every slot, and the phone's SMS autofill
24
+ * (`autocomplete="one-time-code"`) lands in one go. Numeric by default. The first slot takes the
25
+ * field's label; the others are announced as "Character 2 of 6".
26
+ */
27
+ export declare function OTPInput({ length, groups, size, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className, style, messages, ...props }: OTPInputProps): ReactElement;
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { cx } from "../../internal/cx.js";
3
+ import { useMessages } from "../../provider/provider.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { Fragment as Fragment$1 } from "react";
6
+ import { OTPField } from "@base-ui/react/otp-field";
7
+ // src/experimental/otp-input/otp-input.tsx
8
+ /**
9
+ * A one-time code in separate slots (07-components.md, C2), on Base UI's OTP Field: typing moves on,
10
+ * Backspace moves back, pasting fills every slot, and the phone's SMS autofill
11
+ * (`autocomplete="one-time-code"`) lands in one go. Numeric by default. The first slot takes the
12
+ * field's label; the others are announced as "Character 2 of 6".
13
+ */
14
+ function OTPInput({ length = 6, groups, size = "md", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className, style, messages, ...props }) {
15
+ const strings = useMessages("otpInput", messages);
16
+ const breaks = /* @__PURE__ */ new Set();
17
+ let at = 0;
18
+ for (const group of groups ?? []) {
19
+ at += group;
20
+ if (at < length) breaks.add(at);
21
+ }
22
+ return /* @__PURE__ */ jsx(OTPField.Root, {
23
+ ...props,
24
+ length,
25
+ className: cx("qx-otp-input", className),
26
+ style,
27
+ "data-slot": "otp-input",
28
+ "data-size": size,
29
+ children: Array.from({ length }, (_, index) => /* @__PURE__ */ jsxs(Fragment$1, { children: [breaks.has(index) && /* @__PURE__ */ jsx(OTPField.Separator, { className: "qx-otp-input-separator" }), /* @__PURE__ */ jsx(OTPField.Input, {
30
+ className: "qx-otp-input-slot",
31
+ "data-slot": "otp-input-slot",
32
+ ...index === 0 ? {
33
+ "aria-label": ariaLabel,
34
+ "aria-labelledby": ariaLabelledBy
35
+ } : { "aria-label": strings.character.replace("{index}", String(index + 1)).replace("{length}", String(length)) }
36
+ })] }, index))
37
+ });
38
+ }
39
+ export { OTPInput };