@hyperpackai/hyperui 0.1.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 (321) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +82 -0
  3. package/dist/components/Accordion/index.d.ts +17 -0
  4. package/dist/components/Accordion/index.d.ts.map +1 -0
  5. package/dist/components/Accordion/index.js +46 -0
  6. package/dist/components/Alert/index.d.ts +13 -0
  7. package/dist/components/Alert/index.d.ts.map +1 -0
  8. package/dist/components/Alert/index.js +46 -0
  9. package/dist/components/AppBar/index.d.ts +19 -0
  10. package/dist/components/AppBar/index.d.ts.map +1 -0
  11. package/dist/components/AppBar/index.js +48 -0
  12. package/dist/components/AspectRatio/index.d.ts +8 -0
  13. package/dist/components/AspectRatio/index.d.ts.map +1 -0
  14. package/dist/components/AspectRatio/index.js +32 -0
  15. package/dist/components/Autocomplete/index.d.ts +21 -0
  16. package/dist/components/Autocomplete/index.d.ts.map +1 -0
  17. package/dist/components/Autocomplete/index.js +94 -0
  18. package/dist/components/Avatar/index.d.ts +12 -0
  19. package/dist/components/Avatar/index.d.ts.map +1 -0
  20. package/dist/components/Avatar/index.js +32 -0
  21. package/dist/components/AvatarGroup/index.d.ts +9 -0
  22. package/dist/components/AvatarGroup/index.d.ts.map +1 -0
  23. package/dist/components/AvatarGroup/index.js +24 -0
  24. package/dist/components/Backdrop/index.d.ts +10 -0
  25. package/dist/components/Backdrop/index.d.ts.map +1 -0
  26. package/dist/components/Backdrop/index.js +25 -0
  27. package/dist/components/Badge/index.d.ts +10 -0
  28. package/dist/components/Badge/index.d.ts.map +1 -0
  29. package/dist/components/Badge/index.js +30 -0
  30. package/dist/components/BottomNavigation/index.d.ts +16 -0
  31. package/dist/components/BottomNavigation/index.d.ts.map +1 -0
  32. package/dist/components/BottomNavigation/index.js +43 -0
  33. package/dist/components/Box/index.d.ts +21 -0
  34. package/dist/components/Box/index.d.ts.map +1 -0
  35. package/dist/components/Box/index.js +30 -0
  36. package/dist/components/Breadcrumb/index.d.ts +13 -0
  37. package/dist/components/Breadcrumb/index.d.ts.map +1 -0
  38. package/dist/components/Breadcrumb/index.js +19 -0
  39. package/dist/components/Button/index.d.ts +22 -0
  40. package/dist/components/Button/index.d.ts.map +1 -0
  41. package/dist/components/Button/index.js +93 -0
  42. package/dist/components/ButtonGroup/index.d.ts +9 -0
  43. package/dist/components/ButtonGroup/index.d.ts.map +1 -0
  44. package/dist/components/ButtonGroup/index.js +27 -0
  45. package/dist/components/Card/index.d.ts +14 -0
  46. package/dist/components/Card/index.d.ts.map +1 -0
  47. package/dist/components/Card/index.js +33 -0
  48. package/dist/components/Checkbox/index.d.ts +17 -0
  49. package/dist/components/Checkbox/index.d.ts.map +1 -0
  50. package/dist/components/Checkbox/index.js +42 -0
  51. package/dist/components/Chip/index.d.ts +18 -0
  52. package/dist/components/Chip/index.d.ts.map +1 -0
  53. package/dist/components/Chip/index.js +60 -0
  54. package/dist/components/CircularProgress/index.d.ts +14 -0
  55. package/dist/components/CircularProgress/index.d.ts.map +1 -0
  56. package/dist/components/CircularProgress/index.js +58 -0
  57. package/dist/components/ClickAwayListener/index.d.ts +11 -0
  58. package/dist/components/ClickAwayListener/index.d.ts.map +1 -0
  59. package/dist/components/ClickAwayListener/index.js +31 -0
  60. package/dist/components/Code/index.d.ts +9 -0
  61. package/dist/components/Code/index.d.ts.map +1 -0
  62. package/dist/components/Code/index.js +50 -0
  63. package/dist/components/Collapse/index.d.ts +11 -0
  64. package/dist/components/Collapse/index.d.ts.map +1 -0
  65. package/dist/components/Collapse/index.js +30 -0
  66. package/dist/components/CommandPalette/index.d.ts +26 -0
  67. package/dist/components/CommandPalette/index.d.ts.map +1 -0
  68. package/dist/components/CommandPalette/index.js +141 -0
  69. package/dist/components/Container/index.d.ts +11 -0
  70. package/dist/components/Container/index.d.ts.map +1 -0
  71. package/dist/components/Container/index.js +18 -0
  72. package/dist/components/DashboardLayout/index.d.ts +15 -0
  73. package/dist/components/DashboardLayout/index.d.ts.map +1 -0
  74. package/dist/components/DashboardLayout/index.js +67 -0
  75. package/dist/components/DataTable/index.d.ts +27 -0
  76. package/dist/components/DataTable/index.d.ts.map +1 -0
  77. package/dist/components/DataTable/index.js +59 -0
  78. package/dist/components/Dialog/index.d.ts +19 -0
  79. package/dist/components/Dialog/index.d.ts.map +1 -0
  80. package/dist/components/Dialog/index.js +96 -0
  81. package/dist/components/Divider/index.d.ts +13 -0
  82. package/dist/components/Divider/index.d.ts.map +1 -0
  83. package/dist/components/Divider/index.js +40 -0
  84. package/dist/components/Drawer/index.d.ts +17 -0
  85. package/dist/components/Drawer/index.d.ts.map +1 -0
  86. package/dist/components/Drawer/index.js +77 -0
  87. package/dist/components/DropdownMenu/index.d.ts +25 -0
  88. package/dist/components/DropdownMenu/index.d.ts.map +1 -0
  89. package/dist/components/DropdownMenu/index.js +67 -0
  90. package/dist/components/EmptyState/index.d.ts +11 -0
  91. package/dist/components/EmptyState/index.d.ts.map +1 -0
  92. package/dist/components/EmptyState/index.js +53 -0
  93. package/dist/components/ErrorState/index.d.ts +12 -0
  94. package/dist/components/ErrorState/index.d.ts.map +1 -0
  95. package/dist/components/ErrorState/index.js +54 -0
  96. package/dist/components/Fab/index.d.ts +16 -0
  97. package/dist/components/Fab/index.d.ts.map +1 -0
  98. package/dist/components/Fab/index.js +39 -0
  99. package/dist/components/Fade/index.d.ts +10 -0
  100. package/dist/components/Fade/index.d.ts.map +1 -0
  101. package/dist/components/Fade/index.js +13 -0
  102. package/dist/components/FocusTrap/index.d.ts +11 -0
  103. package/dist/components/FocusTrap/index.d.ts.map +1 -0
  104. package/dist/components/FocusTrap/index.js +69 -0
  105. package/dist/components/FormControl/index.d.ts +34 -0
  106. package/dist/components/FormControl/index.d.ts.map +1 -0
  107. package/dist/components/FormControl/index.js +52 -0
  108. package/dist/components/GlobalSearch/index.d.ts +23 -0
  109. package/dist/components/GlobalSearch/index.d.ts.map +1 -0
  110. package/dist/components/GlobalSearch/index.js +91 -0
  111. package/dist/components/Grid/index.d.ts +19 -0
  112. package/dist/components/Grid/index.d.ts.map +1 -0
  113. package/dist/components/Grid/index.js +30 -0
  114. package/dist/components/Grow/index.d.ts +10 -0
  115. package/dist/components/Grow/index.d.ts.map +1 -0
  116. package/dist/components/Grow/index.js +13 -0
  117. package/dist/components/IconButton/index.d.ts +16 -0
  118. package/dist/components/IconButton/index.d.ts.map +1 -0
  119. package/dist/components/IconButton/index.js +43 -0
  120. package/dist/components/ImageList/index.d.ts +21 -0
  121. package/dist/components/ImageList/index.d.ts.map +1 -0
  122. package/dist/components/ImageList/index.js +39 -0
  123. package/dist/components/Input/index.d.ts +33 -0
  124. package/dist/components/Input/index.d.ts.map +1 -0
  125. package/dist/components/Input/index.js +45 -0
  126. package/dist/components/Kbd/index.d.ts +8 -0
  127. package/dist/components/Kbd/index.d.ts.map +1 -0
  128. package/dist/components/Kbd/index.js +41 -0
  129. package/dist/components/KeyboardShortcut/index.d.ts +20 -0
  130. package/dist/components/KeyboardShortcut/index.d.ts.map +1 -0
  131. package/dist/components/KeyboardShortcut/index.js +63 -0
  132. package/dist/components/Label/index.d.ts +10 -0
  133. package/dist/components/Label/index.d.ts.map +1 -0
  134. package/dist/components/Label/index.js +17 -0
  135. package/dist/components/LinearProgress/index.d.ts +13 -0
  136. package/dist/components/LinearProgress/index.d.ts.map +1 -0
  137. package/dist/components/LinearProgress/index.js +69 -0
  138. package/dist/components/Link/index.d.ts +16 -0
  139. package/dist/components/Link/index.d.ts.map +1 -0
  140. package/dist/components/Link/index.js +25 -0
  141. package/dist/components/List/index.d.ts +40 -0
  142. package/dist/components/List/index.d.ts.map +1 -0
  143. package/dist/components/List/index.js +70 -0
  144. package/dist/components/LoadingState/index.d.ts +11 -0
  145. package/dist/components/LoadingState/index.d.ts.map +1 -0
  146. package/dist/components/LoadingState/index.js +66 -0
  147. package/dist/components/Menu/index.d.ts +40 -0
  148. package/dist/components/Menu/index.d.ts.map +1 -0
  149. package/dist/components/Menu/index.js +103 -0
  150. package/dist/components/MobileStepper/index.d.ts +16 -0
  151. package/dist/components/MobileStepper/index.d.ts.map +1 -0
  152. package/dist/components/MobileStepper/index.js +67 -0
  153. package/dist/components/Modal/index.d.ts +14 -0
  154. package/dist/components/Modal/index.d.ts.map +1 -0
  155. package/dist/components/Modal/index.js +51 -0
  156. package/dist/components/Navbar/index.d.ts +14 -0
  157. package/dist/components/Navbar/index.d.ts.map +1 -0
  158. package/dist/components/Navbar/index.js +27 -0
  159. package/dist/components/NoSSR/index.d.ts +7 -0
  160. package/dist/components/NoSSR/index.d.ts.map +1 -0
  161. package/dist/components/NoSSR/index.js +9 -0
  162. package/dist/components/PageLayout/index.d.ts +13 -0
  163. package/dist/components/PageLayout/index.d.ts.map +1 -0
  164. package/dist/components/PageLayout/index.js +41 -0
  165. package/dist/components/Pagination/index.d.ts +11 -0
  166. package/dist/components/Pagination/index.d.ts.map +1 -0
  167. package/dist/components/Pagination/index.js +56 -0
  168. package/dist/components/Paper/index.d.ts +12 -0
  169. package/dist/components/Paper/index.d.ts.map +1 -0
  170. package/dist/components/Paper/index.js +24 -0
  171. package/dist/components/Popover/index.d.ts +13 -0
  172. package/dist/components/Popover/index.d.ts.map +1 -0
  173. package/dist/components/Popover/index.js +34 -0
  174. package/dist/components/Progress/index.d.ts +13 -0
  175. package/dist/components/Progress/index.d.ts.map +1 -0
  176. package/dist/components/Progress/index.js +40 -0
  177. package/dist/components/Radio/index.d.ts +7 -0
  178. package/dist/components/Radio/index.d.ts.map +1 -0
  179. package/dist/components/Radio/index.js +28 -0
  180. package/dist/components/Rating/index.d.ts +15 -0
  181. package/dist/components/Rating/index.d.ts.map +1 -0
  182. package/dist/components/Rating/index.js +41 -0
  183. package/dist/components/ResizablePanel/index.d.ts +16 -0
  184. package/dist/components/ResizablePanel/index.d.ts.map +1 -0
  185. package/dist/components/ResizablePanel/index.js +41 -0
  186. package/dist/components/ScrollArea/index.d.ts +12 -0
  187. package/dist/components/ScrollArea/index.d.ts.map +1 -0
  188. package/dist/components/ScrollArea/index.js +50 -0
  189. package/dist/components/Select/index.d.ts +23 -0
  190. package/dist/components/Select/index.d.ts.map +1 -0
  191. package/dist/components/Select/index.js +52 -0
  192. package/dist/components/Sheet/index.d.ts +15 -0
  193. package/dist/components/Sheet/index.d.ts.map +1 -0
  194. package/dist/components/Sheet/index.js +61 -0
  195. package/dist/components/Sidebar/index.d.ts +23 -0
  196. package/dist/components/Sidebar/index.d.ts.map +1 -0
  197. package/dist/components/Sidebar/index.js +37 -0
  198. package/dist/components/Skeleton/index.d.ts +9 -0
  199. package/dist/components/Skeleton/index.d.ts.map +1 -0
  200. package/dist/components/Skeleton/index.js +22 -0
  201. package/dist/components/Slide/index.d.ts +12 -0
  202. package/dist/components/Slide/index.d.ts.map +1 -0
  203. package/dist/components/Slide/index.js +21 -0
  204. package/dist/components/Slider/index.d.ts +19 -0
  205. package/dist/components/Slider/index.d.ts.map +1 -0
  206. package/dist/components/Slider/index.js +49 -0
  207. package/dist/components/Snackbar/index.d.ts +28 -0
  208. package/dist/components/Snackbar/index.d.ts.map +1 -0
  209. package/dist/components/Snackbar/index.js +71 -0
  210. package/dist/components/SpeedDial/index.d.ts +18 -0
  211. package/dist/components/SpeedDial/index.d.ts.map +1 -0
  212. package/dist/components/SpeedDial/index.js +71 -0
  213. package/dist/components/Spinner/index.d.ts +10 -0
  214. package/dist/components/Spinner/index.d.ts.map +1 -0
  215. package/dist/components/Spinner/index.js +67 -0
  216. package/dist/components/SplitPane/index.d.ts +11 -0
  217. package/dist/components/SplitPane/index.d.ts.map +1 -0
  218. package/dist/components/SplitPane/index.js +46 -0
  219. package/dist/components/Stack/index.d.ts +17 -0
  220. package/dist/components/Stack/index.d.ts.map +1 -0
  221. package/dist/components/Stack/index.js +30 -0
  222. package/dist/components/Stepper/index.d.ts +20 -0
  223. package/dist/components/Stepper/index.d.ts.map +1 -0
  224. package/dist/components/Stepper/index.js +66 -0
  225. package/dist/components/Switch/index.d.ts +13 -0
  226. package/dist/components/Switch/index.d.ts.map +1 -0
  227. package/dist/components/Switch/index.js +38 -0
  228. package/dist/components/Tabs/index.d.ts +20 -0
  229. package/dist/components/Tabs/index.d.ts.map +1 -0
  230. package/dist/components/Tabs/index.js +62 -0
  231. package/dist/components/TextField/index.d.ts +35 -0
  232. package/dist/components/TextField/index.d.ts.map +1 -0
  233. package/dist/components/TextField/index.js +69 -0
  234. package/dist/components/Textarea/index.d.ts +22 -0
  235. package/dist/components/Textarea/index.d.ts.map +1 -0
  236. package/dist/components/Textarea/index.js +27 -0
  237. package/dist/components/Timeline/index.d.ts +16 -0
  238. package/dist/components/Timeline/index.d.ts.map +1 -0
  239. package/dist/components/Timeline/index.js +30 -0
  240. package/dist/components/Toast/index.d.ts +34 -0
  241. package/dist/components/Toast/index.d.ts.map +1 -0
  242. package/dist/components/Toast/index.js +106 -0
  243. package/dist/components/ToggleButton/index.d.ts +21 -0
  244. package/dist/components/ToggleButton/index.d.ts.map +1 -0
  245. package/dist/components/ToggleButton/index.js +81 -0
  246. package/dist/components/Tooltip/index.d.ts +14 -0
  247. package/dist/components/Tooltip/index.d.ts.map +1 -0
  248. package/dist/components/Tooltip/index.js +47 -0
  249. package/dist/components/TreeView/index.d.ts +19 -0
  250. package/dist/components/TreeView/index.d.ts.map +1 -0
  251. package/dist/components/TreeView/index.js +50 -0
  252. package/dist/components/Typography/index.d.ts +17 -0
  253. package/dist/components/Typography/index.d.ts.map +1 -0
  254. package/dist/components/Typography/index.js +44 -0
  255. package/dist/components/VisuallyHidden/index.d.ts +7 -0
  256. package/dist/components/VisuallyHidden/index.d.ts.map +1 -0
  257. package/dist/components/VisuallyHidden/index.js +18 -0
  258. package/dist/components/Zoom/index.d.ts +10 -0
  259. package/dist/components/Zoom/index.d.ts.map +1 -0
  260. package/dist/components/Zoom/index.js +13 -0
  261. package/dist/components/ai.d.ts +133 -0
  262. package/dist/components/ai.d.ts.map +1 -0
  263. package/dist/components/ai.js +184 -0
  264. package/dist/components/charts.d.ts +143 -0
  265. package/dist/components/charts.d.ts.map +1 -0
  266. package/dist/components/charts.js +435 -0
  267. package/dist/components/data.d.ts +192 -0
  268. package/dist/components/data.d.ts.map +1 -0
  269. package/dist/components/data.js +581 -0
  270. package/dist/components/date.d.ts +73 -0
  271. package/dist/components/date.d.ts.map +1 -0
  272. package/dist/components/date.js +199 -0
  273. package/dist/components/enterprise.d.ts +246 -0
  274. package/dist/components/enterprise.d.ts.map +1 -0
  275. package/dist/components/enterprise.js +428 -0
  276. package/dist/components/form.d.ts +132 -0
  277. package/dist/components/form.d.ts.map +1 -0
  278. package/dist/components/form.js +380 -0
  279. package/dist/components/index.d.ts +173 -0
  280. package/dist/components/index.d.ts.map +1 -0
  281. package/dist/components/index.js +107 -0
  282. package/dist/components/inputs.d.ts +169 -0
  283. package/dist/components/inputs.d.ts.map +1 -0
  284. package/dist/components/inputs.js +437 -0
  285. package/dist/components/layout.d.ts +224 -0
  286. package/dist/components/layout.d.ts.map +1 -0
  287. package/dist/components/layout.js +644 -0
  288. package/dist/components/overlay.d.ts +102 -0
  289. package/dist/components/overlay.d.ts.map +1 -0
  290. package/dist/components/overlay.js +318 -0
  291. package/dist/components/surfaces.d.ts +180 -0
  292. package/dist/components/surfaces.d.ts.map +1 -0
  293. package/dist/components/surfaces.js +423 -0
  294. package/dist/components/typography.d.ts +69 -0
  295. package/dist/components/typography.d.ts.map +1 -0
  296. package/dist/components/typography.js +206 -0
  297. package/dist/index.d.ts +23 -0
  298. package/dist/index.d.ts.map +1 -0
  299. package/dist/index.js +29 -0
  300. package/dist/portal.d.ts +3 -0
  301. package/dist/portal.d.ts.map +1 -0
  302. package/dist/portal.js +17 -0
  303. package/dist/theme/index.d.ts +31 -0
  304. package/dist/theme/index.d.ts.map +1 -0
  305. package/dist/theme/index.js +258 -0
  306. package/dist/tokens/component.d.ts +124 -0
  307. package/dist/tokens/component.d.ts.map +1 -0
  308. package/dist/tokens/component.js +113 -0
  309. package/dist/tokens/index.d.ts +494 -0
  310. package/dist/tokens/index.d.ts.map +1 -0
  311. package/dist/tokens/index.js +345 -0
  312. package/dist/tokens/primitives.d.ts +675 -0
  313. package/dist/tokens/primitives.d.ts.map +1 -0
  314. package/dist/tokens/primitives.js +356 -0
  315. package/dist/tokens/semantic.d.ts +252 -0
  316. package/dist/tokens/semantic.d.ts.map +1 -0
  317. package/dist/tokens/semantic.js +242 -0
  318. package/dist/tokens/themes.d.ts +18 -0
  319. package/dist/tokens/themes.d.ts.map +1 -0
  320. package/dist/tokens/themes.js +316 -0
  321. package/package.json +69 -0
@@ -0,0 +1,644 @@
1
+ /**
2
+ * HyperUI Layout & Navigation Components
3
+ * Card · Alert · Badge · Avatar · Skeleton · Progress · Accordion
4
+ * Tabs · Breadcrumb · Pagination · Navbar · Sidebar · DataTable
5
+ */
6
+ import { signal } from "@hyperpackai/hyperion";
7
+ import { injectCSS, cn, h } from "../theme/index.js";
8
+ // ============================================================
9
+ // CARD
10
+ // ============================================================
11
+ const CARD_CSS = `
12
+ .hu-card {
13
+ background: var(--hu-bg); border: 1px solid var(--hu-border);
14
+ border-radius: var(--hu-radius-lg); overflow: hidden;
15
+ box-shadow: var(--hu-shadow-sm);
16
+ }
17
+ .hu-card--hoverable { transition: box-shadow var(--hu-duration) var(--hu-ease), transform var(--hu-duration) var(--hu-ease); cursor: pointer; }
18
+ .hu-card--hoverable:hover { box-shadow: var(--hu-shadow-md); transform: translateY(-2px); }
19
+ .hu-card--ghost { border: none; box-shadow: none; background: transparent; }
20
+ .hu-card--elevated { box-shadow: var(--hu-shadow-md); border: none; }
21
+
22
+ .hu-card-header { padding: var(--hu-space-5) var(--hu-space-6) 0; }
23
+ .hu-card-title { font-size: var(--hu-font-size-md); font-weight: var(--hu-font-weight-semibold); color: var(--hu-text); }
24
+ .hu-card-desc { font-size: var(--hu-font-size-sm); color: var(--hu-text-2); margin-top: var(--hu-space-1); }
25
+ .hu-card-media img, .hu-card-media video { width: 100%; display: block; }
26
+ .hu-card-body { padding: var(--hu-space-5) var(--hu-space-6); }
27
+ .hu-card-footer {
28
+ padding: var(--hu-space-4) var(--hu-space-6);
29
+ border-top: 1px solid var(--hu-border);
30
+ display: flex; align-items: center; gap: var(--hu-space-2);
31
+ }
32
+ `;
33
+ export function Card(props) {
34
+ injectCSS("hu-card", CARD_CSS);
35
+ const { title, description, media, footer, hoverable = false, variant = "default", onClick, children } = props;
36
+ return h("div", {
37
+ class: cn("hu-card", hoverable && "hu-card--hoverable", variant !== "default" && `hu-card--${variant}`, props.class),
38
+ onClick
39
+ }, media && h("div", { class: "hu-card-media" }, media), (title || description) && h("div", { class: "hu-card-header" }, title && h("h3", { class: "hu-card-title" }, title), description && h("p", { class: "hu-card-desc" }, description)), children && h("div", { class: "hu-card-body" }, children), footer && h("div", { class: "hu-card-footer" }, footer));
40
+ }
41
+ // ============================================================
42
+ // ALERT
43
+ // ============================================================
44
+ const ALERT_CSS = `
45
+ .hu-alert {
46
+ display: flex; align-items: flex-start; gap: var(--hu-space-3);
47
+ padding: var(--hu-space-4); border-radius: var(--hu-radius-md);
48
+ border: 1px solid; font-size: var(--hu-font-size-sm);
49
+ }
50
+ .hu-alert--info { background: var(--hu-info-bg); border-color: var(--hu-info); color: var(--hu-info-text); }
51
+ .hu-alert--success { background: var(--hu-success-bg); border-color: var(--hu-success); color: var(--hu-success-text); }
52
+ .hu-alert--warning { background: var(--hu-warning-bg); border-color: var(--hu-warning); color: var(--hu-warning-text); }
53
+ .hu-alert--error { background: var(--hu-error-bg); border-color: var(--hu-error); color: var(--hu-error-text); }
54
+ .hu-alert-icon { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; }
55
+ .hu-alert-title { font-weight: var(--hu-font-weight-semibold); margin-bottom: 2px; }
56
+ .hu-alert-desc { opacity: .85; line-height: 1.45; }
57
+ `;
58
+ const ALERT_ICONS = {
59
+ info: `<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z"/></svg>`,
60
+ success: `<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"/></svg>`,
61
+ warning: `<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z"/></svg>`,
62
+ error: `<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z"/></svg>`
63
+ };
64
+ export function Alert(props) {
65
+ injectCSS("hu-alert", ALERT_CSS);
66
+ const { type = "info", title, description, icon, children } = props;
67
+ const iconContent = icon === false ? null : icon ?? h("span", { innerHTML: ALERT_ICONS[type], "aria-hidden": "true" });
68
+ return h("div", { class: cn("hu-alert", `hu-alert--${type}`, props.class), role: "alert" }, iconContent && h("span", { class: "hu-alert-icon" }, iconContent), h("div", {}, title && h("div", { class: "hu-alert-title" }, title), (description || children) && h("div", { class: "hu-alert-desc" }, description ?? children)));
69
+ }
70
+ // ============================================================
71
+ // BADGE
72
+ // ============================================================
73
+ const BADGE_CSS = `
74
+ .hu-badge {
75
+ display: inline-flex; align-items: center; justify-content: center; gap: 4px;
76
+ border-radius: var(--hu-radius-full); font-size: 11px; font-weight: var(--hu-font-weight-semibold);
77
+ line-height: 1; white-space: nowrap;
78
+ }
79
+ .hu-badge--xs { padding: 2px 6px; }
80
+ .hu-badge--sm { padding: 3px 8px; font-size: 11px; }
81
+ .hu-badge--md { padding: 4px 10px; font-size: var(--hu-font-size-xs); }
82
+ .hu-badge--lg { padding: 5px 12px; font-size: var(--hu-font-size-sm); }
83
+
84
+ .hu-badge--default { background: var(--hu-bg-3); color: var(--hu-text-2); }
85
+ .hu-badge--primary { background: var(--hu-primary-bg); color: var(--hu-primary); }
86
+ .hu-badge--success { background: var(--hu-success-bg); color: var(--hu-success-text); }
87
+ .hu-badge--warning { background: var(--hu-warning-bg); color: var(--hu-warning-text); }
88
+ .hu-badge--error { background: var(--hu-error-bg); color: var(--hu-error-text); }
89
+ .hu-badge--info { background: var(--hu-info-bg); color: var(--hu-info-text); }
90
+ .hu-badge--solid-primary { background: var(--hu-primary); color: white; }
91
+ .hu-badge--solid-success { background: var(--hu-success); color: white; }
92
+ .hu-badge--solid-error { background: var(--hu-error); color: white; }
93
+ .hu-badge--dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
94
+ `;
95
+ export function Badge(props) {
96
+ injectCSS("hu-badge", BADGE_CSS);
97
+ const { variant = "default", size = "sm", dot = false } = props;
98
+ return h("span", {
99
+ class: cn("hu-badge", `hu-badge--${size}`, `hu-badge--${variant}`, dot && "hu-badge--dot", props.class)
100
+ }, props.children);
101
+ }
102
+ // ============================================================
103
+ // AVATAR
104
+ // ============================================================
105
+ const AVATAR_CSS = `
106
+ .hu-avatar {
107
+ display: inline-flex; align-items: center; justify-content: center;
108
+ border-radius: var(--hu-radius-full); overflow: hidden; flex-shrink: 0;
109
+ background: var(--hu-primary-bg); color: var(--hu-primary);
110
+ font-weight: var(--hu-font-weight-semibold); user-select: none;
111
+ }
112
+ .hu-avatar img { width: 100%; height: 100%; object-fit: cover; }
113
+ .hu-avatar--xs { width: 24px; height: 24px; font-size: 10px; }
114
+ .hu-avatar--sm { width: 32px; height: 32px; font-size: 12px; }
115
+ .hu-avatar--md { width: 40px; height: 40px; font-size: 14px; }
116
+ .hu-avatar--lg { width: 48px; height: 48px; font-size: 16px; }
117
+ .hu-avatar--xl { width: 64px; height: 64px; font-size: 22px; }
118
+ .hu-avatar--2xl { width: 80px; height: 80px; font-size: 28px; }
119
+ .hu-avatar--square { border-radius: var(--hu-radius-md); }
120
+ .hu-avatar-group { display: flex; }
121
+ .hu-avatar-group .hu-avatar { border: 2px solid var(--hu-bg); margin-left: -8px; }
122
+ .hu-avatar-group .hu-avatar:first-child { margin-left: 0; }
123
+ `;
124
+ export function Avatar(props) {
125
+ injectCSS("hu-avatar", AVATAR_CSS);
126
+ const { src, alt, name, size = "md", shape = "circle" } = props;
127
+ const initials = name
128
+ ? name.split(" ").slice(0, 2).map((p) => p[0]?.toUpperCase() ?? "").join("")
129
+ : "?";
130
+ return h("span", {
131
+ class: cn("hu-avatar", `hu-avatar--${size}`, shape === "square" && "hu-avatar--square", props.class),
132
+ style: props.color ? `background: ${props.color}20; color: ${props.color}` : undefined,
133
+ "aria-label": alt ?? name
134
+ }, src ? h("img", { src, alt: alt ?? name ?? "", loading: "lazy" }) : initials);
135
+ }
136
+ // ============================================================
137
+ // SKELETON
138
+ // ============================================================
139
+ const SKELETON_CSS = `
140
+ .hu-skeleton {
141
+ display: block; border-radius: var(--hu-radius);
142
+ background: linear-gradient(90deg, var(--hu-bg-3) 25%, var(--hu-bg-4) 50%, var(--hu-bg-3) 75%);
143
+ background-size: 200% 100%; animation: hu-shimmer 1.5s infinite;
144
+ }
145
+ .hu-skeleton--text { height: 14px; border-radius: var(--hu-radius-sm); }
146
+ .hu-skeleton--circle { border-radius: var(--hu-radius-full); }
147
+ .hu-skeleton--title { height: 24px; border-radius: var(--hu-radius-sm); }
148
+ `;
149
+ export function Skeleton(props) {
150
+ injectCSS("hu-skeleton", SKELETON_CSS);
151
+ const { width, height, variant = "rect" } = props;
152
+ const w = typeof width === "number" ? `${width}px` : width;
153
+ const h_ = typeof height === "number" ? `${height}px` : height;
154
+ return h("span", {
155
+ class: cn("hu-skeleton", variant !== "rect" && `hu-skeleton--${variant}`, props.class),
156
+ style: [w && `width:${w}`, h_ && `height:${h_}`].filter(Boolean).join(";"),
157
+ "aria-hidden": "true"
158
+ });
159
+ }
160
+ // ============================================================
161
+ // PROGRESS
162
+ // ============================================================
163
+ const PROGRESS_CSS = `
164
+ .hu-progress-wrap { display: flex; flex-direction: column; gap: var(--hu-space-1); }
165
+ .hu-progress-header { display: flex; justify-content: space-between; align-items: center; font-size: var(--hu-font-size-xs); color: var(--hu-text-2); }
166
+ .hu-progress-label { font-weight: var(--hu-font-weight-medium); }
167
+ .hu-progress-track {
168
+ width: 100%; background: var(--hu-bg-3); border-radius: var(--hu-radius-full);
169
+ overflow: hidden;
170
+ }
171
+ .hu-progress-track--sm { height: 4px; }
172
+ .hu-progress-track--md { height: 8px; }
173
+ .hu-progress-track--lg { height: 12px; }
174
+ .hu-progress-bar {
175
+ height: 100%; background: var(--hu-primary); border-radius: var(--hu-radius-full);
176
+ transition: width .4s var(--hu-ease-out);
177
+ }
178
+ .hu-progress-bar--success { background: var(--hu-success); }
179
+ .hu-progress-bar--error { background: var(--hu-error); }
180
+ .hu-progress-bar--warning { background: var(--hu-warning); }
181
+ .hu-progress-bar--indeterminate {
182
+ width: 40% !important; animation: hu-progress-indeterminate 1.5s infinite;
183
+ }
184
+ @keyframes hu-progress-indeterminate {
185
+ 0% { transform: translateX(-100%); }
186
+ 100%{ transform: translateX(350%); }
187
+ }
188
+ `;
189
+ export function Progress(props) {
190
+ injectCSS("hu-progress", PROGRESS_CSS);
191
+ const { value = 0, max = 100, label, showValue = false, size = "md", color = "primary", indeterminate = false } = props;
192
+ const pct = Math.min(100, Math.max(0, (value / max) * 100));
193
+ return h("div", { class: cn("hu-progress-wrap", props.class) }, (label || showValue) && h("div", { class: "hu-progress-header" }, label && h("span", { class: "hu-progress-label" }, label), showValue && h("span", {}, `${Math.round(pct)}%`)), h("div", {
194
+ class: cn("hu-progress-track", `hu-progress-track--${size}`),
195
+ role: "progressbar", "aria-valuenow": indeterminate ? undefined : value,
196
+ "aria-valuemin": 0, "aria-valuemax": max, "aria-label": label
197
+ }, h("div", {
198
+ class: cn("hu-progress-bar", color !== "primary" && `hu-progress-bar--${color}`, indeterminate && "hu-progress-bar--indeterminate"),
199
+ style: !indeterminate ? `width:${pct}%` : undefined
200
+ })));
201
+ }
202
+ // ============================================================
203
+ // TABS
204
+ // ============================================================
205
+ const TABS_CSS = `
206
+ .hu-tabs { display: flex; flex-direction: column; gap: 0; }
207
+ .hu-tabs-list {
208
+ display: flex; border-bottom: 2px solid var(--hu-border); overflow-x: auto;
209
+ scrollbar-width: none; gap: 0;
210
+ }
211
+ .hu-tabs-list::-webkit-scrollbar { display: none; }
212
+ .hu-tabs-list--pill {
213
+ background: var(--hu-bg-3); border-radius: var(--hu-radius-md); padding: 3px;
214
+ border-bottom: none; gap: 2px;
215
+ }
216
+ .hu-tabs-trigger {
217
+ display: inline-flex; align-items: center; gap: var(--hu-space-2); padding: 9px 16px;
218
+ font-size: var(--hu-font-size-sm); font-weight: var(--hu-font-weight-medium);
219
+ color: var(--hu-text-3); border: none; background: transparent; cursor: pointer;
220
+ position: relative; white-space: nowrap; border-radius: 0;
221
+ transition: color var(--hu-duration) var(--hu-ease);
222
+ }
223
+ .hu-tabs-trigger:hover { color: var(--hu-text); }
224
+ .hu-tabs-trigger[aria-selected="true"] { color: var(--hu-primary); }
225
+ .hu-tabs-trigger[aria-selected="true"]::after {
226
+ content: ""; position: absolute; bottom: -2px; left: 0; right: 0; height: 2px; background: var(--hu-primary);
227
+ }
228
+ .hu-tabs-list--pill .hu-tabs-trigger { border-radius: var(--hu-radius-sm); padding: 7px 14px; }
229
+ .hu-tabs-list--pill .hu-tabs-trigger[aria-selected="true"] {
230
+ background: var(--hu-bg); color: var(--hu-text); box-shadow: var(--hu-shadow-xs); font-weight: var(--hu-font-weight-semibold);
231
+ }
232
+ .hu-tabs-list--pill .hu-tabs-trigger[aria-selected="true"]::after { display: none; }
233
+ .hu-tabs-trigger:disabled { opacity: .4; cursor: not-allowed; }
234
+ .hu-tabs-badge { background: var(--hu-bg-4); color: var(--hu-text-2); font-size: 10px; padding: 1px 6px; border-radius: var(--hu-radius-full); }
235
+ .hu-tabs-content { padding-top: var(--hu-space-5); }
236
+ .hu-tabs-content[hidden] { display: none; }
237
+ `;
238
+ export function Tabs(props) {
239
+ injectCSS("hu-tabs", TABS_CSS);
240
+ const { items, variant = "underline", onChange } = props;
241
+ const controlled = typeof props.activeTab === "object" && props.activeTab !== null && "peek" in props.activeTab;
242
+ const active = controlled
243
+ ? props.activeTab.value
244
+ : props.activeTab ?? props.defaultTab ?? items[0]?.id ?? "";
245
+ return h("div", { class: cn("hu-tabs", props.class) }, h("div", { class: cn("hu-tabs-list", variant === "pill" && "hu-tabs-list--pill"), role: "tablist" }, ...items.map((item) => h("button", {
246
+ key: item.id, id: `tab-${item.id}`, role: "tab",
247
+ class: "hu-tabs-trigger",
248
+ "aria-selected": active === item.id ? "true" : "false",
249
+ "aria-controls": `tabpanel-${item.id}`,
250
+ disabled: item.disabled,
251
+ onClick: () => onChange?.(item.id)
252
+ }, item.icon && h("span", { "aria-hidden": "true" }, item.icon), item.label, item.badge !== undefined && h("span", { class: "hu-tabs-badge" }, item.badge)))), ...items.map((item) => h("div", {
253
+ key: item.id, id: `tabpanel-${item.id}`,
254
+ class: "hu-tabs-content", role: "tabpanel",
255
+ hidden: active !== item.id || undefined,
256
+ "aria-labelledby": `tab-${item.id}`
257
+ }, active === item.id ? item.content : null)));
258
+ }
259
+ // ============================================================
260
+ // ACCORDION
261
+ // ============================================================
262
+ const ACCORDION_CSS = `
263
+ .hu-accordion { border: 1px solid var(--hu-border); border-radius: var(--hu-radius-md); overflow: hidden; }
264
+ .hu-accordion-item { border-bottom: 1px solid var(--hu-border); }
265
+ .hu-accordion-item:last-child { border-bottom: none; }
266
+ .hu-accordion-trigger {
267
+ display: flex; align-items: center; justify-content: space-between; width: 100%;
268
+ padding: var(--hu-space-4) var(--hu-space-5); background: transparent; border: none;
269
+ font-size: var(--hu-font-size-sm); font-weight: var(--hu-font-weight-medium); color: var(--hu-text);
270
+ cursor: pointer; text-align: left; transition: background var(--hu-duration) var(--hu-ease);
271
+ }
272
+ .hu-accordion-trigger:hover { background: var(--hu-bg-2); }
273
+ .hu-accordion-trigger[aria-expanded="true"] { color: var(--hu-primary); }
274
+ .hu-accordion-icon { width: 16px; height: 16px; flex-shrink: 0; color: var(--hu-text-3); transition: transform var(--hu-duration) var(--hu-ease); }
275
+ .hu-accordion-trigger[aria-expanded="true"] .hu-accordion-icon { transform: rotate(180deg); color: var(--hu-primary); }
276
+ .hu-accordion-content { padding: 0 var(--hu-space-5) var(--hu-space-4); font-size: var(--hu-font-size-sm); color: var(--hu-text-2); line-height: 1.6; animation: hu-slide-down var(--hu-duration-fast) var(--hu-ease); }
277
+ .hu-accordion--ghost { border: none; }
278
+ .hu-accordion--ghost .hu-accordion-item { border-bottom: 1px solid var(--hu-border); }
279
+ .hu-accordion--ghost .hu-accordion-item:last-child { border-bottom: none; }
280
+ `;
281
+ export function Accordion(props) {
282
+ injectCSS("hu-accordion", ACCORDION_CSS);
283
+ const { items, multiple = false, defaultOpen = [], variant = "default" } = props;
284
+ const open = signal(defaultOpen);
285
+ const toggle = (id) => {
286
+ const current = open.value;
287
+ if (multiple) {
288
+ open.value = current.includes(id) ? current.filter((i) => i !== id) : [...current, id];
289
+ }
290
+ else {
291
+ open.value = current.includes(id) ? [] : [id];
292
+ }
293
+ };
294
+ const chevronSVG = `<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>`;
295
+ return h("div", { class: cn("hu-accordion", variant !== "default" && `hu-accordion--${variant}`, props.class) }, ...items.map((item) => {
296
+ const isOpen = open.value.includes(item.id);
297
+ return h("div", { key: item.id, class: "hu-accordion-item" }, h("button", {
298
+ class: "hu-accordion-trigger",
299
+ "aria-expanded": isOpen ? "true" : "false",
300
+ "aria-controls": `accordion-${item.id}`,
301
+ disabled: item.disabled,
302
+ onClick: () => toggle(item.id)
303
+ }, h("span", { style: "display:flex;align-items:center;gap:8px;" }, item.icon && h("span", {}, item.icon), item.title), h("span", { class: "hu-accordion-icon", innerHTML: chevronSVG, "aria-hidden": "true" })), isOpen && h("div", { id: `accordion-${item.id}`, class: "hu-accordion-content" }, item.content));
304
+ }));
305
+ }
306
+ // ============================================================
307
+ // BREADCRUMB
308
+ // ============================================================
309
+ const BREADCRUMB_CSS = `
310
+ .hu-breadcrumb { display: flex; align-items: center; gap: var(--hu-space-1); flex-wrap: wrap; }
311
+ .hu-breadcrumb-item { display: flex; align-items: center; gap: var(--hu-space-1); }
312
+ .hu-breadcrumb-link { font-size: var(--hu-font-size-sm); color: var(--hu-text-3); text-decoration: none; transition: color var(--hu-duration) var(--hu-ease); }
313
+ .hu-breadcrumb-link:hover { color: var(--hu-text); }
314
+ .hu-breadcrumb-current { font-size: var(--hu-font-size-sm); color: var(--hu-text); font-weight: var(--hu-font-weight-medium); }
315
+ .hu-breadcrumb-sep { color: var(--hu-text-3); font-size: var(--hu-font-size-sm); user-select: none; }
316
+ `;
317
+ export function Breadcrumb(props) {
318
+ injectCSS("hu-breadcrumb", BREADCRUMB_CSS);
319
+ const { items, separator = "/" } = props;
320
+ return h("nav", { class: cn("hu-breadcrumb", props.class), "aria-label": "Breadcrumb" }, ...items.map((item, i) => h("span", { key: item.label, class: "hu-breadcrumb-item" }, i < items.length - 1
321
+ ? h(item.href ? "a" : "button", {
322
+ class: "hu-breadcrumb-link",
323
+ href: item.href, onClick: item.onClick
324
+ }, item.label)
325
+ : h("span", { class: "hu-breadcrumb-current", "aria-current": "page" }, item.label), i < items.length - 1 && h("span", { class: "hu-breadcrumb-sep", "aria-hidden": "true" }, separator))));
326
+ }
327
+ // ============================================================
328
+ // PAGINATION
329
+ // ============================================================
330
+ const PAGINATION_CSS = `
331
+ .hu-pagination { display: flex; align-items: center; gap: var(--hu-space-1); }
332
+ .hu-pagination-btn {
333
+ display: inline-flex; align-items: center; justify-content: center;
334
+ min-width: 36px; height: 36px; padding: 0 var(--hu-space-2);
335
+ border: 1px solid var(--hu-border); border-radius: var(--hu-radius);
336
+ background: var(--hu-bg); font-size: var(--hu-font-size-sm); color: var(--hu-text-2);
337
+ cursor: pointer; transition: all var(--hu-duration) var(--hu-ease);
338
+ }
339
+ .hu-pagination-btn:hover:not(:disabled) { background: var(--hu-bg-3); color: var(--hu-text); border-color: var(--hu-border-2); }
340
+ .hu-pagination-btn--active { background: var(--hu-primary); border-color: var(--hu-primary); color: white; font-weight: var(--hu-font-weight-semibold); }
341
+ .hu-pagination-btn--active:hover { background: var(--hu-primary-dk); }
342
+ .hu-pagination-btn:disabled { opacity: .4; cursor: not-allowed; }
343
+ .hu-pagination-ellipsis { display: inline-flex; align-items: center; justify-content: center; min-width: 36px; height: 36px; color: var(--hu-text-3); font-size: var(--hu-font-size-sm); }
344
+ `;
345
+ export function Pagination(props) {
346
+ injectCSS("hu-pagination", PAGINATION_CSS);
347
+ const { page, totalPages, siblingCount = 1, onChange, showFirstLast = true } = props;
348
+ const pages = [];
349
+ const delta = siblingCount;
350
+ const left = page - delta;
351
+ const right = page + delta + 1;
352
+ const range = [];
353
+ const rangeWithDots = [];
354
+ for (let i = 1; i <= totalPages; i++) {
355
+ if (i === 1 || i === totalPages || (i >= left && i < right))
356
+ range.push(i);
357
+ }
358
+ let prev;
359
+ for (const i of range) {
360
+ if (prev !== undefined) {
361
+ if (i - prev === 2)
362
+ rangeWithDots.push(prev + 1);
363
+ else if (i - prev > 2)
364
+ rangeWithDots.push("...");
365
+ }
366
+ rangeWithDots.push(i);
367
+ prev = i;
368
+ }
369
+ const prevSVG = `<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 4L6 8l4 4"/></svg>`;
370
+ const nextSVG = `<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 4l4 4-4 4"/></svg>`;
371
+ return h("nav", { class: cn("hu-pagination", props.class), "aria-label": "Pagination" }, h("button", { class: "hu-pagination-btn", onClick: () => onChange(page - 1), disabled: page <= 1, "aria-label": "Previous page", innerHTML: prevSVG }), ...rangeWithDots.map((p, i) => p === "..."
372
+ ? h("span", { key: `ellipsis-${i}`, class: "hu-pagination-ellipsis" }, "…")
373
+ : h("button", {
374
+ key: p,
375
+ class: cn("hu-pagination-btn", p === page && "hu-pagination-btn--active"),
376
+ onClick: () => onChange(p),
377
+ "aria-label": `Page ${p}`, "aria-current": p === page ? "page" : undefined
378
+ }, p)), h("button", { class: "hu-pagination-btn", onClick: () => onChange(page + 1), disabled: page >= totalPages, "aria-label": "Next page", innerHTML: nextSVG }));
379
+ }
380
+ // ============================================================
381
+ // STEPPER
382
+ // ============================================================
383
+ const STEPPER_CSS = `
384
+ .hu-stepper { display: flex; gap: var(--hu-space-4); width: 100%; }
385
+ .hu-stepper--vertical { flex-direction: column; }
386
+ .hu-stepper--horizontal { align-items: flex-start; }
387
+ .hu-stepper-step { display: flex; align-items: flex-start; gap: var(--hu-space-3); min-width: 0; position: relative; flex: 1; }
388
+ .hu-stepper--vertical .hu-stepper-step { flex: none; }
389
+ .hu-stepper-step:not(:last-child)::after {
390
+ content: ""; position: absolute; background: var(--hu-border);
391
+ }
392
+ .hu-stepper--horizontal .hu-stepper-step:not(:last-child)::after {
393
+ inset-inline-start: calc(var(--hu-space-8) + var(--hu-space-3)); inset-inline-end: var(--hu-space-3);
394
+ inset-block-start: 16px; height: 1px;
395
+ }
396
+ .hu-stepper--vertical .hu-stepper-step:not(:last-child)::after {
397
+ inset-inline-start: 16px; inset-block-start: 36px; inset-block-end: calc(-1 * var(--hu-space-4)); width: 1px;
398
+ }
399
+ .hu-stepper-step--complete:not(:last-child)::after { background: var(--hu-success); }
400
+ .hu-stepper-indicator {
401
+ width: 32px; height: 32px; border-radius: var(--hu-radius-full);
402
+ display: inline-flex; align-items: center; justify-content: center; flex: none;
403
+ border: 1px solid var(--hu-border); background: var(--hu-bg); color: var(--hu-text-2);
404
+ font-size: var(--hu-font-size-sm); font-weight: var(--hu-font-weight-semibold);
405
+ }
406
+ .hu-stepper-step--active .hu-stepper-indicator { border-color: var(--hu-primary); background: var(--hu-primary); color: var(--hu-primary-text); }
407
+ .hu-stepper-step--complete .hu-stepper-indicator { border-color: var(--hu-success); background: var(--hu-success); color: #fff; }
408
+ .hu-stepper-step--error .hu-stepper-indicator { border-color: var(--hu-error); background: var(--hu-error); color: #fff; }
409
+ .hu-stepper-step--disabled { opacity: .5; }
410
+ .hu-stepper-content { min-width: 0; }
411
+ .hu-stepper-label { font-size: var(--hu-font-size-sm); font-weight: var(--hu-font-weight-semibold); color: var(--hu-text); }
412
+ .hu-stepper-description { margin-block-start: 2px; font-size: var(--hu-font-size-xs); color: var(--hu-text-2); line-height: 1.5; }
413
+ @media (max-width: 640px) {
414
+ .hu-stepper--horizontal { flex-direction: column; }
415
+ .hu-stepper--horizontal .hu-stepper-step:not(:last-child)::after {
416
+ inset-inline-start: 16px; inset-inline-end: auto; inset-block-start: 36px; inset-block-end: calc(-1 * var(--hu-space-4));
417
+ width: 1px; height: auto;
418
+ }
419
+ }
420
+ `;
421
+ export function Stepper(props) {
422
+ injectCSS("hu-stepper", STEPPER_CSS);
423
+ const { steps, completed = [], orientation = "horizontal", onStepClick } = props;
424
+ const activeIndex = typeof props.activeStep === "number"
425
+ ? props.activeStep
426
+ : Math.max(0, steps.findIndex((step) => step.id === props.activeStep));
427
+ return h("ol", {
428
+ class: cn("hu-stepper", `hu-stepper--${orientation}`, props.class),
429
+ "aria-label": "Progress"
430
+ }, ...steps.map((step, index) => {
431
+ const active = index === activeIndex;
432
+ const complete = completed.includes(step.id) || index < activeIndex;
433
+ const state = step.error ? "error" : complete ? "complete" : active ? "active" : "pending";
434
+ const indicator = step.icon ?? (complete ? "✓" : index + 1);
435
+ return h("li", {
436
+ key: step.id,
437
+ class: cn("hu-stepper-step", `hu-stepper-step--${state}`, step.disabled && "hu-stepper-step--disabled"),
438
+ "aria-current": active ? "step" : undefined
439
+ }, h(onStepClick && !step.disabled ? "button" : "span", {
440
+ class: "hu-stepper-indicator",
441
+ type: onStepClick && !step.disabled ? "button" : undefined,
442
+ disabled: step.disabled,
443
+ onClick: onStepClick && !step.disabled ? () => onStepClick(step.id, index) : undefined,
444
+ "aria-label": `${step.label}: ${state}`
445
+ }, indicator), h("div", { class: "hu-stepper-content" }, h("div", { class: "hu-stepper-label" }, step.label, step.optional && " (optional)"), step.description && h("div", { class: "hu-stepper-description" }, step.description)));
446
+ }));
447
+ }
448
+ // ============================================================
449
+ // TIMELINE
450
+ // ============================================================
451
+ const TIMELINE_CSS = `
452
+ .hu-timeline { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0; }
453
+ .hu-timeline-item { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--hu-space-3); position: relative; min-width: 0; }
454
+ .hu-timeline-item:not(:last-child) { padding-block-end: var(--hu-space-5); }
455
+ .hu-timeline-marker-wrap { position: relative; display: flex; justify-content: center; min-width: 24px; }
456
+ .hu-timeline-marker {
457
+ width: 12px; height: 12px; border-radius: var(--hu-radius-full);
458
+ margin-block-start: 5px; background: var(--hu-border-2); border: 2px solid var(--hu-bg); box-shadow: 0 0 0 1px var(--hu-border);
459
+ }
460
+ .hu-timeline-item:not(:last-child) .hu-timeline-marker-wrap::after {
461
+ content: ""; position: absolute; inset-block-start: 24px; inset-block-end: 0; width: 1px; background: var(--hu-border);
462
+ }
463
+ .hu-timeline-item--success .hu-timeline-marker { background: var(--hu-success); box-shadow: 0 0 0 1px var(--hu-success); }
464
+ .hu-timeline-item--warning .hu-timeline-marker { background: var(--hu-warning); box-shadow: 0 0 0 1px var(--hu-warning); }
465
+ .hu-timeline-item--error .hu-timeline-marker { background: var(--hu-error); box-shadow: 0 0 0 1px var(--hu-error); }
466
+ .hu-timeline-item--info .hu-timeline-marker { background: var(--hu-info); box-shadow: 0 0 0 1px var(--hu-info); }
467
+ .hu-timeline-content { min-width: 0; }
468
+ .hu-timeline-title { font-size: var(--hu-font-size-sm); font-weight: var(--hu-font-weight-semibold); color: var(--hu-text); }
469
+ .hu-timeline-time { margin-block-start: 1px; font-size: var(--hu-font-size-xs); color: var(--hu-text-3); }
470
+ .hu-timeline-description { margin-block-start: var(--hu-space-1); font-size: var(--hu-font-size-sm); color: var(--hu-text-2); line-height: 1.55; }
471
+ .hu-timeline-actions { margin-block-start: var(--hu-space-2); }
472
+ `;
473
+ export function Timeline(props) {
474
+ injectCSS("hu-timeline", TIMELINE_CSS);
475
+ return h("ol", { class: cn("hu-timeline", props.class), "aria-label": "Timeline" }, ...props.items.map((item) => h("li", {
476
+ key: item.id,
477
+ class: cn("hu-timeline-item", item.status && item.status !== "default" && `hu-timeline-item--${item.status}`)
478
+ }, h("div", { class: "hu-timeline-marker-wrap", "aria-hidden": "true" }, h("span", { class: "hu-timeline-marker" }, item.marker)), h("div", { class: "hu-timeline-content" }, h("div", { class: "hu-timeline-title" }, item.title), item.time && h("time", { class: "hu-timeline-time" }, item.time), item.description && h("div", { class: "hu-timeline-description" }, item.description), item.actions && h("div", { class: "hu-timeline-actions" }, item.actions)))));
479
+ }
480
+ // ============================================================
481
+ // TREE VIEW
482
+ // ============================================================
483
+ const TREE_VIEW_CSS = `
484
+ .hu-tree { list-style: none; margin: 0; padding: 0; font-size: var(--hu-font-size-sm); color: var(--hu-text); }
485
+ .hu-tree-group { list-style: none; margin: 0; padding: 0 0 0 var(--hu-space-5); }
486
+ [dir="rtl"] .hu-tree-group { padding: 0 var(--hu-space-5) 0 0; }
487
+ .hu-tree-item { margin: 1px 0; }
488
+ .hu-tree-row {
489
+ display: flex; align-items: center; gap: var(--hu-space-2); width: 100%;
490
+ min-height: 32px; padding: 4px var(--hu-space-2); border-radius: var(--hu-radius);
491
+ color: var(--hu-text-2); background: transparent; border: none; text-align: start;
492
+ font: inherit; cursor: pointer;
493
+ }
494
+ .hu-tree-row:hover { background: var(--hu-bg-3); color: var(--hu-text); }
495
+ .hu-tree-row[aria-selected="true"] { background: var(--hu-primary-bg); color: var(--hu-primary); font-weight: var(--hu-font-weight-medium); }
496
+ .hu-tree-row:focus-visible { outline: 2px solid var(--hu-primary); outline-offset: 1px; }
497
+ .hu-tree-toggle { width: 16px; display: inline-flex; align-items: center; justify-content: center; color: var(--hu-text-3); flex: none; }
498
+ .hu-tree-toggle--leaf { visibility: hidden; }
499
+ .hu-tree-icon { width: 18px; display: inline-flex; align-items: center; justify-content: center; flex: none; }
500
+ .hu-tree-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
501
+ .hu-tree-badge { margin-inline-start: auto; }
502
+ `;
503
+ export function TreeView(props) {
504
+ injectCSS("hu-tree", TREE_VIEW_CSS);
505
+ const expanded = new Set(props.expanded ?? []);
506
+ const renderItems = (items, level) => items.map((item) => {
507
+ const hasChildren = Boolean(item.children?.length);
508
+ const isExpanded = hasChildren && expanded.has(item.id);
509
+ const isSelected = props.selected === item.id;
510
+ return h("li", { key: item.id, class: "hu-tree-item", role: "none" }, h("button", {
511
+ class: "hu-tree-row",
512
+ role: "treeitem",
513
+ type: "button",
514
+ disabled: item.disabled,
515
+ "aria-level": level,
516
+ "aria-expanded": hasChildren ? String(isExpanded) : undefined,
517
+ "aria-selected": isSelected ? "true" : "false",
518
+ onClick: () => {
519
+ if (item.disabled)
520
+ return;
521
+ if (hasChildren)
522
+ props.onToggle?.(item.id, !isExpanded);
523
+ props.onSelect?.(item.id);
524
+ }
525
+ }, h("span", {
526
+ class: cn("hu-tree-toggle", !hasChildren && "hu-tree-toggle--leaf"),
527
+ "aria-hidden": "true"
528
+ }, isExpanded ? "▾" : "▸"), item.icon && h("span", { class: "hu-tree-icon", "aria-hidden": "true" }, item.icon), h("span", { class: "hu-tree-label" }, item.label), item.badge && h("span", { class: "hu-tree-badge" }, item.badge)), hasChildren && isExpanded && h("ul", { class: "hu-tree-group", role: "group" }, ...renderItems(item.children, level + 1)));
529
+ });
530
+ return h("ul", { class: cn("hu-tree", props.class), role: "tree" }, ...renderItems(props.items, 1));
531
+ }
532
+ // ============================================================
533
+ // NAVBAR
534
+ // ============================================================
535
+ const NAVBAR_CSS = `
536
+ .hu-navbar {
537
+ display: flex; align-items: center; justify-content: space-between;
538
+ padding: 0 var(--hu-space-6); height: 60px;
539
+ background: var(--hu-bg); border-bottom: 1px solid var(--hu-border);
540
+ position: sticky; top: 0; z-index: var(--hu-z-sticky);
541
+ backdrop-filter: blur(8px); background: color-mix(in srgb, var(--hu-bg) 90%, transparent);
542
+ }
543
+ .hu-navbar-brand { display: flex; align-items: center; gap: var(--hu-space-3); text-decoration: none; color: var(--hu-text); font-weight: var(--hu-font-weight-bold); font-size: var(--hu-font-size-md); }
544
+ .hu-navbar-nav { display: flex; align-items: center; gap: var(--hu-space-1); }
545
+ .hu-navbar-link { padding: 6px 12px; border-radius: var(--hu-radius); font-size: var(--hu-font-size-sm); color: var(--hu-text-2); text-decoration: none; font-weight: var(--hu-font-weight-medium); transition: all var(--hu-duration) var(--hu-ease); }
546
+ .hu-navbar-link:hover { color: var(--hu-text); background: var(--hu-bg-3); }
547
+ .hu-navbar-link--active { color: var(--hu-primary); background: var(--hu-primary-bg); }
548
+ .hu-navbar-actions { display: flex; align-items: center; gap: var(--hu-space-2); }
549
+ `;
550
+ export function Navbar(props) {
551
+ injectCSS("hu-navbar", NAVBAR_CSS);
552
+ const { brand, links = [], actions } = props;
553
+ return h("header", { class: cn("hu-navbar", props.class) }, brand && h("a", { class: "hu-navbar-brand", href: "/" }, brand), links.length > 0 && h("nav", { class: "hu-navbar-nav", "aria-label": "Main navigation" }, ...links.map((l) => h("a", { key: l.href, href: l.href, class: cn("hu-navbar-link", l.active && "hu-navbar-link--active") }, l.label))), actions && h("div", { class: "hu-navbar-actions" }, actions));
554
+ }
555
+ // ============================================================
556
+ // SIDEBAR
557
+ // ============================================================
558
+ const SIDEBAR_CSS = `
559
+ .hu-sidebar {
560
+ display: flex; flex-direction: column; height: 100vh; overflow: hidden;
561
+ background: var(--hu-bg); border-right: 1px solid var(--hu-border);
562
+ position: sticky; top: 0;
563
+ }
564
+ .hu-sidebar--sm { width: 220px; min-width: 220px; }
565
+ .hu-sidebar--md { width: 260px; min-width: 260px; }
566
+ .hu-sidebar--lg { width: 300px; min-width: 300px; }
567
+ .hu-sidebar-header { padding: var(--hu-space-4) var(--hu-space-4) var(--hu-space-3); border-bottom: 1px solid var(--hu-border); }
568
+ .hu-sidebar-nav { flex: 1; overflow-y: auto; padding: var(--hu-space-2); scrollbar-width: thin; }
569
+ .hu-sidebar-section { margin-bottom: var(--hu-space-4); }
570
+ .hu-sidebar-section-label { padding: var(--hu-space-2) var(--hu-space-3); font-size: 11px; font-weight: var(--hu-font-weight-semibold); color: var(--hu-text-3); text-transform: uppercase; letter-spacing: .06em; }
571
+ .hu-sidebar-item {
572
+ display: flex; align-items: center; gap: var(--hu-space-3); width: 100%;
573
+ padding: 8px var(--hu-space-3); border-radius: var(--hu-radius); font-size: var(--hu-font-size-sm);
574
+ color: var(--hu-text-2); text-decoration: none; border: none; background: transparent;
575
+ cursor: pointer; font-weight: var(--hu-font-weight-medium); transition: all var(--hu-duration) var(--hu-ease);
576
+ }
577
+ .hu-sidebar-item:hover { background: var(--hu-bg-3); color: var(--hu-text); }
578
+ .hu-sidebar-item--active { background: var(--hu-primary-bg); color: var(--hu-primary); }
579
+ .hu-sidebar-item-icon { width: 18px; height: 18px; flex-shrink: 0; }
580
+ .hu-sidebar-item-badge { margin-left: auto; }
581
+ .hu-sidebar-footer { padding: var(--hu-space-3) var(--hu-space-4); border-top: 1px solid var(--hu-border); }
582
+ `;
583
+ export function Sidebar(props) {
584
+ injectCSS("hu-sidebar", SIDEBAR_CSS);
585
+ const { header, sections, footer, width = "md" } = props;
586
+ return h("aside", { class: cn("hu-sidebar", `hu-sidebar--${width}`, props.class) }, header && h("div", { class: "hu-sidebar-header" }, header), h("nav", { class: "hu-sidebar-nav", "aria-label": "Sidebar navigation" }, ...sections.map((section, si) => h("div", { key: si, class: "hu-sidebar-section" }, section.label && h("div", { class: "hu-sidebar-section-label" }, section.label), ...section.items.map((item) => h(item.href ? "a" : "button", {
587
+ key: item.id, class: cn("hu-sidebar-item", item.active && "hu-sidebar-item--active"),
588
+ href: item.href, onClick: item.onClick, "aria-current": item.active ? "page" : undefined
589
+ }, item.icon && h("span", { class: "hu-sidebar-item-icon", "aria-hidden": "true" }, item.icon), item.label, item.badge && h("span", { class: "hu-sidebar-item-badge" }, item.badge)))))), footer && h("div", { class: "hu-sidebar-footer" }, footer));
590
+ }
591
+ // ============================================================
592
+ // DATA TABLE
593
+ // ============================================================
594
+ const TABLE_CSS = `
595
+ .hu-table-wrap { overflow-x: auto; border-radius: var(--hu-radius-md); border: 1px solid var(--hu-border); }
596
+ .hu-table { width: 100%; border-collapse: collapse; font-size: var(--hu-font-size-sm); }
597
+ .hu-table th {
598
+ padding: 10px 16px; text-align: left; font-size: 11px; font-weight: var(--hu-font-weight-semibold);
599
+ color: var(--hu-text-3); text-transform: uppercase; letter-spacing: .05em;
600
+ background: var(--hu-bg-2); border-bottom: 1px solid var(--hu-border);
601
+ white-space: nowrap;
602
+ }
603
+ .hu-table th.hu-table-sortable { cursor: pointer; user-select: none; }
604
+ .hu-table th.hu-table-sortable:hover { background: var(--hu-bg-3); color: var(--hu-text); }
605
+ .hu-table-sort-icon { display: inline-flex; align-items: center; margin-left: 4px; opacity: .4; }
606
+ .hu-table-sort-icon--asc, .hu-table-sort-icon--desc { opacity: 1; color: var(--hu-primary); }
607
+ .hu-table td { padding: 12px 16px; border-bottom: 1px solid var(--hu-border); color: var(--hu-text); vertical-align: middle; }
608
+ .hu-table tr:last-child td { border-bottom: none; }
609
+ .hu-table tr:hover td { background: var(--hu-bg-2); }
610
+ .hu-table-empty { text-align: center; padding: var(--hu-space-12) var(--hu-space-6); color: var(--hu-text-3); }
611
+ .hu-table-footer { display: flex; align-items: center; justify-content: space-between; padding: var(--hu-space-3) var(--hu-space-4); border-top: 1px solid var(--hu-border); background: var(--hu-bg-2); font-size: var(--hu-font-size-xs); color: var(--hu-text-3); }
612
+ .hu-table-checkbox-col { width: 40px; padding-left: 16px !important; }
613
+ `;
614
+ export function DataTable(props) {
615
+ injectCSS("hu-table", TABLE_CSS);
616
+ const { columns, rows, rowKey, sortColumn, sortDir, onSort, onRowClick, emptyMessage = "No data available.", footer, selectable, selectedRows, onSelectRow, onSelectAll } = props;
617
+ const sortSVG = (dir) => dir === "asc"
618
+ ? `<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor"><path d="M6 3l4 5H2l4-5z"/></svg>`
619
+ : dir === "desc"
620
+ ? `<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor"><path d="M6 9L2 4h8L6 9z"/></svg>`
621
+ : `<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor" opacity=".4"><path d="M6 3l3 3H3l3-3zM6 9L3 6h6L6 9z"/></svg>`;
622
+ const allSelected = rows.length > 0 && rows.every((r) => selectedRows?.has(rowKey?.(r) ?? String(r["id"] ?? "")));
623
+ return h("div", { class: cn("hu-table-wrap", props.class) }, h("table", { class: "hu-table", role: "table" }, h("thead", {}, h("tr", {}, selectable && h("th", { class: "hu-table-checkbox-col" }, h("input", { type: "checkbox", checked: allSelected, onChange: (e) => onSelectAll?.(e.target.checked) })), ...columns.map((col) => h("th", {
624
+ key: col.key,
625
+ class: col.sortable ? "hu-table-sortable" : undefined,
626
+ style: [col.width && `width:${col.width}`, col.align && `text-align:${col.align}`].filter(Boolean).join(";"),
627
+ onClick: col.sortable ? () => onSort?.(col.key) : undefined
628
+ }, col.header, col.sortable && h("span", {
629
+ class: cn("hu-table-sort-icon", sortColumn === col.key && `hu-table-sort-icon--${sortDir}`),
630
+ innerHTML: sortSVG(sortColumn === col.key ? sortDir ?? "" : ""),
631
+ "aria-hidden": "true"
632
+ }))))), h("tbody", {}, rows.length === 0
633
+ ? h("tr", {}, h("td", { colspan: columns.length + (selectable ? 1 : 0), class: "hu-table-empty" }, emptyMessage))
634
+ : rows.map((row, i) => {
635
+ const key = rowKey?.(row) ?? String(row["id"] ?? i);
636
+ return h("tr", {
637
+ key, style: onRowClick ? "cursor:pointer" : undefined,
638
+ onClick: onRowClick ? () => onRowClick(row) : undefined
639
+ }, selectable && h("td", { class: "hu-table-checkbox-col" }, h("input", {
640
+ type: "checkbox", checked: selectedRows?.has(key) ?? false,
641
+ onChange: (e) => { e.stopPropagation(); onSelectRow?.(key, e.target.checked); }
642
+ })), ...columns.map((col) => h("td", { key: col.key, style: col.align ? `text-align:${col.align}` : undefined }, col.render ? col.render(row, i) : row[col.key])));
643
+ }))), footer && h("div", { class: "hu-table-footer" }, footer));
644
+ }