@hyperpackai/hyperui 0.2.0 → 0.3.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 (98) hide show
  1. package/README.md +13 -0
  2. package/dist/components/Accordion/index.d.ts +6 -0
  3. package/dist/components/Accordion/index.d.ts.map +1 -1
  4. package/dist/components/Accordion/index.js +65 -9
  5. package/dist/components/Autocomplete/index.d.ts +12 -2
  6. package/dist/components/Autocomplete/index.d.ts.map +1 -1
  7. package/dist/components/Autocomplete/index.js +148 -24
  8. package/dist/components/Backdrop/index.d.ts +2 -1
  9. package/dist/components/Backdrop/index.d.ts.map +1 -1
  10. package/dist/components/Backdrop/index.js +6 -3
  11. package/dist/components/Checkbox/index.d.ts +1 -0
  12. package/dist/components/Checkbox/index.d.ts.map +1 -1
  13. package/dist/components/Checkbox/index.js +6 -2
  14. package/dist/components/DashboardLayout/index.d.ts +13 -0
  15. package/dist/components/DashboardLayout/index.d.ts.map +1 -1
  16. package/dist/components/DashboardLayout/index.js +50 -7
  17. package/dist/components/DataTable/index.d.ts +43 -0
  18. package/dist/components/DataTable/index.d.ts.map +1 -1
  19. package/dist/components/DataTable/index.js +126 -21
  20. package/dist/components/Dialog/index.d.ts +9 -3
  21. package/dist/components/Dialog/index.d.ts.map +1 -1
  22. package/dist/components/Dialog/index.js +46 -30
  23. package/dist/components/Drawer/index.d.ts +11 -3
  24. package/dist/components/Drawer/index.d.ts.map +1 -1
  25. package/dist/components/Drawer/index.js +66 -11
  26. package/dist/components/DropdownMenu/index.d.ts +5 -3
  27. package/dist/components/DropdownMenu/index.d.ts.map +1 -1
  28. package/dist/components/DropdownMenu/index.js +56 -13
  29. package/dist/components/FocusTrap/index.d.ts.map +1 -1
  30. package/dist/components/FocusTrap/index.js +34 -32
  31. package/dist/components/Input/index.d.ts +2 -0
  32. package/dist/components/Input/index.d.ts.map +1 -1
  33. package/dist/components/Input/index.js +18 -4
  34. package/dist/components/Menu/index.d.ts +6 -2
  35. package/dist/components/Menu/index.d.ts.map +1 -1
  36. package/dist/components/Menu/index.js +50 -15
  37. package/dist/components/Modal/index.d.ts +3 -1
  38. package/dist/components/Modal/index.d.ts.map +1 -1
  39. package/dist/components/Modal/index.js +27 -9
  40. package/dist/components/NestedNavbar/index.d.ts +33 -0
  41. package/dist/components/NestedNavbar/index.d.ts.map +1 -0
  42. package/dist/components/NestedNavbar/index.js +435 -0
  43. package/dist/components/NestedSidebar/index.d.ts +48 -0
  44. package/dist/components/NestedSidebar/index.d.ts.map +1 -0
  45. package/dist/components/NestedSidebar/index.js +368 -0
  46. package/dist/components/Popover/index.d.ts +11 -3
  47. package/dist/components/Popover/index.d.ts.map +1 -1
  48. package/dist/components/Popover/index.js +45 -9
  49. package/dist/components/Radio/index.d.ts +26 -1
  50. package/dist/components/Radio/index.d.ts.map +1 -1
  51. package/dist/components/Radio/index.js +61 -2
  52. package/dist/components/Select/index.d.ts +5 -0
  53. package/dist/components/Select/index.d.ts.map +1 -1
  54. package/dist/components/Select/index.js +22 -5
  55. package/dist/components/Sheet/index.d.ts +9 -3
  56. package/dist/components/Sheet/index.d.ts.map +1 -1
  57. package/dist/components/Sheet/index.js +48 -23
  58. package/dist/components/Sidebar/index.d.ts +20 -1
  59. package/dist/components/Sidebar/index.d.ts.map +1 -1
  60. package/dist/components/Sidebar/index.js +285 -8
  61. package/dist/components/SpeedDial/index.d.ts +10 -0
  62. package/dist/components/SpeedDial/index.d.ts.map +1 -1
  63. package/dist/components/SpeedDial/index.js +61 -11
  64. package/dist/components/Switch/index.d.ts +2 -0
  65. package/dist/components/Switch/index.d.ts.map +1 -1
  66. package/dist/components/Switch/index.js +6 -2
  67. package/dist/components/Tabs/index.d.ts +3 -0
  68. package/dist/components/Tabs/index.d.ts.map +1 -1
  69. package/dist/components/Tabs/index.js +47 -8
  70. package/dist/components/TextField/index.d.ts +2 -0
  71. package/dist/components/TextField/index.d.ts.map +1 -1
  72. package/dist/components/TextField/index.js +12 -4
  73. package/dist/components/Textarea/index.d.ts +5 -0
  74. package/dist/components/Textarea/index.d.ts.map +1 -1
  75. package/dist/components/Textarea/index.js +21 -4
  76. package/dist/components/Transition/index.d.ts +14 -0
  77. package/dist/components/Transition/index.d.ts.map +1 -0
  78. package/dist/components/Transition/index.js +49 -0
  79. package/dist/components/TransitionGroup/index.d.ts +16 -0
  80. package/dist/components/TransitionGroup/index.d.ts.map +1 -0
  81. package/dist/components/TransitionGroup/index.js +95 -0
  82. package/dist/components/data.d.ts +81 -16
  83. package/dist/components/data.d.ts.map +1 -1
  84. package/dist/components/data.js +163 -31
  85. package/dist/components/enterprise.d.ts +85 -26
  86. package/dist/components/enterprise.d.ts.map +1 -1
  87. package/dist/components/enterprise.js +211 -36
  88. package/dist/components/index.d.ts +21 -13
  89. package/dist/components/index.d.ts.map +1 -1
  90. package/dist/components/index.js +7 -2
  91. package/dist/portal.d.ts.map +1 -1
  92. package/dist/portal.js +3 -0
  93. package/dist/theme/index.d.ts +5 -6
  94. package/dist/theme/index.d.ts.map +1 -1
  95. package/dist/theme/index.js +30 -0
  96. package/dist/tokens/index.d.ts.map +1 -1
  97. package/dist/tokens/index.js +11 -0
  98. package/package.json +6 -1
@@ -0,0 +1,368 @@
1
+ import { signal } from "@hyperpackai/hyperion";
2
+ import { injectCSS, cn, h } from "../../theme/index.js";
3
+ import { FocusTrap } from "../FocusTrap/index.js";
4
+ let sidebarCounter = 0;
5
+ const NESTED_SIDEBAR_CSS = `
6
+ /* ── Base ───────────────────────────────────────────────────────────── */
7
+ .hu-nested-sidebar {
8
+ --hu-ns-width: 260px;
9
+ --hu-ns-top: 0px;
10
+ --hu-ns-height: 100vh;
11
+ display: flex; flex-direction: column;
12
+ width: var(--hu-ns-width); min-width: var(--hu-ns-width);
13
+ height: var(--hu-ns-height); overflow: hidden;
14
+ background: linear-gradient(180deg, var(--hu-bg), color-mix(in srgb, var(--hu-bg-2) 64%, var(--hu-bg)));
15
+ border-right: 1px solid color-mix(in srgb, var(--hu-border) 82%, transparent);
16
+ position: sticky; top: var(--hu-ns-top);
17
+ }
18
+ .hu-nested-sidebar--sm { --hu-ns-width: 220px; }
19
+ .hu-nested-sidebar--md { --hu-ns-width: 260px; }
20
+ .hu-nested-sidebar--lg { --hu-ns-width: 300px; }
21
+ .hu-nested-sidebar--static { position: static; }
22
+ .hu-nested-sidebar--sticky { position: sticky; top: var(--hu-ns-top); }
23
+ .hu-nested-sidebar--fixed { position: fixed; top: var(--hu-ns-top); left: 0; bottom: 0; }
24
+
25
+ .hu-nested-sidebar__header {
26
+ padding: var(--hu-space-4) var(--hu-space-4) var(--hu-space-3);
27
+ border-bottom: 1px solid var(--hu-border); flex-shrink: 0;
28
+ }
29
+ .hu-nested-sidebar__nav { flex: 1; overflow-y: auto; padding: var(--hu-space-2); scrollbar-width: thin; }
30
+ .hu-nested-sidebar__footer { padding: var(--hu-space-3) var(--hu-space-4); border-top: 1px solid var(--hu-border); flex-shrink: 0; }
31
+
32
+ /* Section */
33
+ .hu-nested-sidebar__section { margin-bottom: var(--hu-space-4); }
34
+ .hu-nested-sidebar__section-label {
35
+ padding: var(--hu-space-2) var(--hu-space-3); font-size: 11px;
36
+ font-weight: var(--hu-font-weight-semibold); color: var(--hu-text-3);
37
+ text-transform: uppercase; letter-spacing: .06em;
38
+ }
39
+
40
+ /* Flat item */
41
+ .hu-nested-sidebar__item {
42
+ display: flex; align-items: center; gap: var(--hu-space-3); width: 100%;
43
+ min-height: 38px; padding: 8px var(--hu-space-3); border-radius: var(--hu-radius-md);
44
+ font-size: var(--hu-font-size-sm); font-family: inherit;
45
+ color: var(--hu-text-2); text-decoration: none;
46
+ border: none; background: transparent; cursor: pointer;
47
+ font-weight: var(--hu-font-weight-medium); text-align: left;
48
+ transition: background var(--hu-duration) var(--hu-ease), color var(--hu-duration) var(--hu-ease), transform var(--hu-duration-fast) var(--hu-ease);
49
+ }
50
+ .hu-nested-sidebar__item:hover { background: var(--hu-bg-3); color: var(--hu-text); transform: translateX(1px); }
51
+ .hu-nested-sidebar__item:focus-visible { outline: 2px solid var(--hu-primary); outline-offset: 2px; }
52
+ .hu-nested-sidebar__item--active { background: var(--hu-primary-bg); color: var(--hu-primary); box-shadow: inset 3px 0 0 var(--hu-primary); }
53
+ .hu-nested-sidebar__item-icon { width: 18px; height: 18px; flex-shrink: 0; }
54
+ .hu-nested-sidebar__item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
55
+ .hu-nested-sidebar__item-badge { margin-left: auto; flex-shrink: 0; }
56
+
57
+ /* Nested group trigger */
58
+ .hu-nested-sidebar__group-trigger {
59
+ display: flex; align-items: center; gap: var(--hu-space-3); width: 100%;
60
+ min-height: 38px; padding: 8px var(--hu-space-3); border-radius: var(--hu-radius-md);
61
+ font-size: var(--hu-font-size-sm); font-family: inherit;
62
+ color: var(--hu-text-2); background: transparent; border: none;
63
+ cursor: pointer; font-weight: var(--hu-font-weight-medium); text-align: left;
64
+ transition: background var(--hu-duration) var(--hu-ease), color var(--hu-duration) var(--hu-ease);
65
+ }
66
+ .hu-nested-sidebar__group-trigger:hover { background: var(--hu-bg-3); color: var(--hu-text); }
67
+ .hu-nested-sidebar__group-trigger:focus-visible { outline: 2px solid var(--hu-primary); outline-offset: 2px; }
68
+ .hu-nested-sidebar__group-trigger--active { color: var(--hu-primary); }
69
+ .hu-nested-sidebar__group-chevron {
70
+ width: 14px; height: 14px; margin-left: auto; flex-shrink: 0;
71
+ transition: transform var(--hu-duration) var(--hu-ease);
72
+ }
73
+ .hu-nested-sidebar__group-trigger[aria-expanded="true"] .hu-nested-sidebar__group-chevron { transform: rotate(90deg); }
74
+
75
+ /* Child items */
76
+ .hu-nested-sidebar__children {
77
+ padding-left: var(--hu-space-4); margin-top: 2px; margin-bottom: 2px;
78
+ border-left: 2px solid var(--hu-border);
79
+ margin-left: calc(var(--hu-space-3) + 9px);
80
+ }
81
+ .hu-nested-sidebar__group:not([data-expanded="true"]) .hu-nested-sidebar__children { display: none !important; }
82
+ .hu-nested-sidebar__child {
83
+ display: flex; align-items: center; gap: var(--hu-space-2); width: 100%;
84
+ min-height: 34px; padding: 6px var(--hu-space-2); border-radius: var(--hu-radius-sm);
85
+ font-size: var(--hu-font-size-sm); font-family: inherit;
86
+ color: var(--hu-text-2); text-decoration: none;
87
+ border: none; background: transparent; cursor: pointer;
88
+ font-weight: var(--hu-font-weight-medium); text-align: left;
89
+ transition: background var(--hu-duration) var(--hu-ease), color var(--hu-duration) var(--hu-ease);
90
+ }
91
+ .hu-nested-sidebar__child:hover { background: var(--hu-bg-3); color: var(--hu-text); }
92
+ .hu-nested-sidebar__child:focus-visible { outline: 2px solid var(--hu-primary); outline-offset: 2px; }
93
+ .hu-nested-sidebar__child--active { color: var(--hu-primary); background: var(--hu-primary-bg); }
94
+
95
+ /* ── Mobile Bar & Toggle ────────────────────────────────────────────── */
96
+ .hu-nested-sidebar__root { display: contents; }
97
+ .hu-nested-sidebar__mobile-shell { display: none; }
98
+ .hu-nested-sidebar__mobile-bar {
99
+ width: 100%; padding: var(--hu-space-2) var(--hu-space-3);
100
+ background: color-mix(in srgb, var(--hu-bg) 92%, transparent);
101
+ border-bottom: 1px solid var(--hu-border);
102
+ }
103
+ .hu-nested-sidebar__menu-btn {
104
+ display: inline-flex; align-items: center; gap: var(--hu-space-2);
105
+ min-height: 44px; padding: 0 var(--hu-space-3); max-width: 100%;
106
+ background: var(--hu-bg); border: 1px solid var(--hu-border);
107
+ border-radius: var(--hu-radius-md); box-shadow: var(--hu-shadow-xs);
108
+ font: inherit; font-size: var(--hu-font-size-sm);
109
+ font-weight: var(--hu-font-weight-semibold); color: var(--hu-text); cursor: pointer;
110
+ transition: border-color var(--hu-duration) var(--hu-ease), background var(--hu-duration) var(--hu-ease);
111
+ }
112
+ .hu-nested-sidebar__menu-btn:hover { background: var(--hu-bg-2); border-color: var(--hu-border-2); }
113
+ .hu-nested-sidebar__menu-btn:focus-visible { outline: 2px solid var(--hu-primary); outline-offset: 2px; }
114
+ .hu-nested-sidebar__menu-btn-icon { display: inline-flex; align-items: center; width: 20px; height: 20px; flex-shrink: 0; }
115
+
116
+ /* ── Mobile Backdrop ────────────────────────────────────────────────── */
117
+ .hu-nested-sidebar__backdrop {
118
+ position: fixed; inset: 0; z-index: var(--hu-z-backdrop);
119
+ background: var(--hu-overlay);
120
+ animation: hu-ns-fade-in var(--hu-duration-fast) var(--hu-ease);
121
+ }
122
+ @keyframes hu-ns-fade-in { from { opacity: 0; } to { opacity: 1; } }
123
+
124
+ /* ── Mobile Drawer ──────────────────────────────────────────────────── */
125
+ .hu-nested-sidebar__drawer {
126
+ position: fixed; inset-y: 0; left: 0; z-index: var(--hu-z-modal);
127
+ width: min(82vw, 320px);
128
+ display: flex; flex-direction: column;
129
+ background: linear-gradient(180deg, var(--hu-bg), color-mix(in srgb, var(--hu-bg-2) 74%, var(--hu-bg)));
130
+ border-right: 1px solid var(--hu-border);
131
+ box-shadow: var(--hu-shadow-xl);
132
+ animation: hu-ns-drawer-in var(--hu-duration-slow) var(--hu-ease-out);
133
+ }
134
+ @keyframes hu-ns-drawer-in { from { transform: translateX(-100%); } to { transform: translateX(0); } }
135
+ .hu-nested-sidebar__drawer-header {
136
+ display: flex; align-items: center; justify-content: space-between;
137
+ padding: var(--hu-space-4); border-bottom: 1px solid var(--hu-border);
138
+ }
139
+ .hu-nested-sidebar__drawer-title { font-weight: var(--hu-font-weight-semibold); color: var(--hu-text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
140
+ .hu-nested-sidebar__drawer-close {
141
+ display: inline-flex; align-items: center; justify-content: center;
142
+ width: 34px; height: 34px; padding: 0;
143
+ background: var(--hu-bg); border: 1px solid var(--hu-border);
144
+ border-radius: var(--hu-radius-md); cursor: pointer; color: var(--hu-text-2);
145
+ }
146
+ .hu-nested-sidebar__drawer-close:hover { color: var(--hu-text); background: var(--hu-bg-3); }
147
+ .hu-nested-sidebar__drawer-close:focus-visible { outline: 2px solid var(--hu-primary); outline-offset: 2px; }
148
+ .hu-nested-sidebar__drawer-body { flex: 1; overflow-y: auto; padding: var(--hu-space-2); }
149
+
150
+ /* The drawer items use the same .hu-nested-sidebar__item / .hu-nested-sidebar__child styles */
151
+ .hu-nested-sidebar__drawer .hu-nested-sidebar__item { min-height: 44px; }
152
+ .hu-nested-sidebar__drawer .hu-nested-sidebar__child { min-height: 40px; }
153
+
154
+ /* ── Mobile Bottom Sheet ────────────────────────────────────────────── */
155
+ .hu-nested-sidebar__sheet {
156
+ position: fixed; inset-x: 0; bottom: 0; z-index: var(--hu-z-modal);
157
+ max-height: 85vh; display: flex; flex-direction: column;
158
+ background: var(--hu-bg); border-top: 1px solid var(--hu-border);
159
+ border-radius: var(--hu-radius-xl) var(--hu-radius-xl) 0 0;
160
+ box-shadow: var(--hu-shadow-xl);
161
+ animation: hu-ns-sheet-in var(--hu-duration-slow) var(--hu-ease-out);
162
+ }
163
+ @keyframes hu-ns-sheet-in { from { transform: translateY(100%); } to { transform: translateY(0); } }
164
+ .hu-nested-sidebar__sheet-handle {
165
+ width: 36px; height: 4px; border-radius: 2px;
166
+ background: var(--hu-border); margin: var(--hu-space-3) auto var(--hu-space-1); flex-shrink: 0;
167
+ }
168
+ .hu-nested-sidebar__sheet-header {
169
+ display: flex; align-items: center; justify-content: space-between;
170
+ padding: var(--hu-space-2) var(--hu-space-4) var(--hu-space-3);
171
+ border-bottom: 1px solid var(--hu-border);
172
+ }
173
+ .hu-nested-sidebar__sheet-title { font-weight: var(--hu-font-weight-semibold); color: var(--hu-text); font-size: var(--hu-font-size-md); }
174
+ .hu-nested-sidebar__sheet-close {
175
+ display: inline-flex; align-items: center; justify-content: center;
176
+ width: 34px; height: 34px; padding: 0;
177
+ background: var(--hu-bg); border: 1px solid var(--hu-border);
178
+ border-radius: var(--hu-radius-md); cursor: pointer; color: var(--hu-text-2);
179
+ }
180
+ .hu-nested-sidebar__sheet-close:hover { background: var(--hu-bg-3); color: var(--hu-text); }
181
+ .hu-nested-sidebar__sheet-close:focus-visible { outline: 2px solid var(--hu-primary); outline-offset: 2px; }
182
+ .hu-nested-sidebar__sheet-body { flex: 1; overflow-y: auto; padding: var(--hu-space-2); }
183
+ .hu-nested-sidebar__sheet .hu-nested-sidebar__item { min-height: 44px; }
184
+ .hu-nested-sidebar__sheet .hu-nested-sidebar__child { min-height: 40px; }
185
+
186
+ /* ── Responsive Breakpoint ──────────────────────────────────────────── */
187
+ @media (max-width: 1024px) {
188
+ .hu-nested-sidebar--responsive { display: none; }
189
+ .hu-nested-sidebar__root { display: block; width: 100%; }
190
+ .hu-nested-sidebar__mobile-shell { display: block; }
191
+ .hu-nested-sidebar__mobile-bar { position: sticky; top: var(--hu-ns-top); z-index: var(--hu-z-sticky); }
192
+ }
193
+ `;
194
+ const nestedSidebarState = new Map();
195
+ function getSidebarState(key, sections, defaultOpen) {
196
+ let state = nestedSidebarState.get(key);
197
+ const defaultExpanded = collectDefaultExpanded(sections);
198
+ if (!state) {
199
+ state = {
200
+ expandedItems: signal(defaultExpanded),
201
+ uncontrolledOpen: signal(defaultOpen),
202
+ seededDefaults: defaultExpanded
203
+ };
204
+ nestedSidebarState.set(key, state);
205
+ return state;
206
+ }
207
+ for (const itemId of defaultExpanded) {
208
+ if (!state.seededDefaults.has(itemId)) {
209
+ const next = new Set(state.expandedItems.value);
210
+ next.add(itemId);
211
+ state.expandedItems.value = next;
212
+ state.seededDefaults.add(itemId);
213
+ }
214
+ }
215
+ return state;
216
+ }
217
+ function collectDefaultExpanded(sections) {
218
+ const expanded = new Set();
219
+ for (const section of sections) {
220
+ for (const item of section.items) {
221
+ if (item.defaultExpanded && item.children?.length)
222
+ expanded.add(item.id);
223
+ }
224
+ }
225
+ return expanded;
226
+ }
227
+ function getSidebarKey(props) {
228
+ return props.id ?? props.class ?? props.drawerTitle ?? props.menuLabel ?? props.sections.map((section) => section.items.map((item) => item.id).join(",")).join("|") ?? `nested-sidebar-${sidebarCounter + 1}`;
229
+ }
230
+ // ── SVG helpers ───────────────────────────────────────────────────────────────
231
+ const chevronRight = h("svg", { class: "hu-nested-sidebar__group-chevron", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, h("path", { d: "M6 4l4 4-4 4" }));
232
+ const closeIcon = h("svg", { viewBox: "0 0 16 16", width: "16", height: "16", fill: "none", stroke: "currentColor", "stroke-width": "2", "aria-hidden": "true" }, h("path", { d: "M2 2l12 12M14 2L2 14" }));
233
+ const menuHamburger = h("svg", { viewBox: "0 0 20 20", width: "18", height: "18", fill: "none", stroke: "currentColor", "stroke-width": "1.75", "aria-hidden": "true" }, h("path", { d: "M3 5h14M3 10h14M3 15h14" }));
234
+ // ── Helpers ───────────────────────────────────────────────────────────────────
235
+ function isItemActive(active) {
236
+ if (typeof active === "function")
237
+ return active();
238
+ if (typeof active === "object" && active != null && "peek" in active)
239
+ return active.value;
240
+ return active === true;
241
+ }
242
+ function formatCssSize(value) {
243
+ return typeof value === "number" ? `${value}px` : value;
244
+ }
245
+ // ── Component ─────────────────────────────────────────────────────────────────
246
+ export function NestedSidebar(props) {
247
+ injectCSS("hu-nested-sidebar", NESTED_SIDEBAR_CSS);
248
+ const { sections, width = "md", position = "sticky", responsive = true, mobileVariant = "drawer" } = props;
249
+ const sid = props.id ?? `hu-ns-${++sidebarCounter}`;
250
+ const state = getSidebarState(getSidebarKey(props), sections, props.defaultOpen ?? false);
251
+ const { expandedItems, uncontrolledOpen } = state;
252
+ // Controlled / uncontrolled mobile open state
253
+ const isControlled = () => props.open !== undefined;
254
+ const isOpen = () => {
255
+ if (typeof props.open === "boolean")
256
+ return props.open;
257
+ if (typeof props.open === "object" && props.open != null && "peek" in props.open)
258
+ return props.open.value;
259
+ return uncontrolledOpen.value;
260
+ };
261
+ const setOpen = (next) => {
262
+ if (!isControlled())
263
+ uncontrolledOpen.value = next;
264
+ if (typeof props.open === "object" && props.open != null && "peek" in props.open)
265
+ props.open.value = next;
266
+ props.onOpenChange?.(next);
267
+ };
268
+ const closeMobile = () => setOpen(false);
269
+ const syncGroup = (groupEl, expanded) => {
270
+ groupEl?.setAttribute("data-expanded", String(expanded));
271
+ groupEl?.querySelector(".hu-nested-sidebar__group-trigger")?.setAttribute("aria-expanded", String(expanded));
272
+ groupEl?.querySelectorAll(".hu-nested-sidebar__children").forEach((panel) => {
273
+ panel.hidden = !expanded;
274
+ });
275
+ };
276
+ const toggleGroup = (event, id) => {
277
+ const next = new Set(expandedItems.value);
278
+ const groupEl = event ? event.currentTarget.closest(".hu-nested-sidebar__group") : null;
279
+ const expanded = !(groupEl?.dataset.expanded === "true" || next.has(id));
280
+ if (expanded)
281
+ next.add(id);
282
+ else
283
+ next.delete(id);
284
+ expandedItems.value = next;
285
+ syncGroup(groupEl, expanded);
286
+ };
287
+ const styleValue = [
288
+ props.topOffset !== undefined ? `--hu-ns-top:${formatCssSize(props.topOffset)}` : "",
289
+ props.height !== undefined ? `--hu-ns-height:${formatCssSize(props.height)}` : "",
290
+ ].filter(Boolean).join(";");
291
+ // ── Nav content renderer ───────────────────────────────────────────────────
292
+ const renderNav = (onClose) => h("nav", { class: "hu-nested-sidebar__nav", "aria-label": "Sidebar navigation" }, ...sections.map((section, si) => h("div", { key: si, class: "hu-nested-sidebar__section" }, section.label && h("div", { class: "hu-nested-sidebar__section-label" }, section.label), ...section.items.map((item) => {
293
+ const active = () => isItemActive(item.active);
294
+ if (!item.children?.length) {
295
+ return h(item.href ? "a" : "button", {
296
+ key: item.id,
297
+ class: () => cn("hu-nested-sidebar__item", active() && "hu-nested-sidebar__item--active"),
298
+ href: item.href,
299
+ type: item.href ? undefined : "button",
300
+ "aria-current": () => active() ? "page" : undefined,
301
+ onClick: () => { item.onClick?.(); onClose?.(); },
302
+ }, item.icon && h("span", { class: "hu-nested-sidebar__item-icon", "aria-hidden": "true" }, item.icon), h("span", { class: "hu-nested-sidebar__item-label" }, item.label), item.badge && h("span", { class: "hu-nested-sidebar__item-badge" }, item.badge));
303
+ }
304
+ // Group with children
305
+ const isExpanded = () => expandedItems.value.has(item.id);
306
+ return h("div", {
307
+ key: item.id,
308
+ class: "hu-nested-sidebar__group",
309
+ "data-expanded": () => String(isExpanded())
310
+ }, h("button", {
311
+ type: "button",
312
+ class: () => cn("hu-nested-sidebar__group-trigger", active() && "hu-nested-sidebar__group-trigger--active"),
313
+ "aria-expanded": () => String(isExpanded()),
314
+ onClick: (event) => toggleGroup(event, item.id),
315
+ }, item.icon && h("span", { class: "hu-nested-sidebar__item-icon", "aria-hidden": "true" }, item.icon), h("span", { class: "hu-nested-sidebar__item-label" }, item.label), item.badge && h("span", { class: "hu-nested-sidebar__item-badge" }, item.badge), chevronRight), h("div", {
316
+ class: "hu-nested-sidebar__children",
317
+ hidden: () => !isExpanded()
318
+ }, ...item.children.map((child) => {
319
+ const childActive = () => isItemActive(child.active);
320
+ return h(child.href ? "a" : "button", {
321
+ key: child.id,
322
+ class: () => cn("hu-nested-sidebar__child", childActive() && "hu-nested-sidebar__child--active"),
323
+ href: child.href,
324
+ type: child.href ? undefined : "button",
325
+ "aria-current": () => childActive() ? "page" : undefined,
326
+ onClick: () => { child.onClick?.(); onClose?.(); },
327
+ }, child.icon && h("span", { class: "hu-nested-sidebar__item-icon", "aria-hidden": "true" }, child.icon), child.label);
328
+ })));
329
+ }))));
330
+ // ── Mobile overlay ─────────────────────────────────────────────────────────
331
+ const renderOverlay = () => {
332
+ if (!isOpen())
333
+ return null;
334
+ const drawerTitle = props.drawerTitle ?? props.menuLabel ?? "Menu";
335
+ const closeBtn = (cls) => h("button", {
336
+ type: "button", class: cls, "aria-label": "Close menu", onClick: closeMobile,
337
+ }, closeIcon);
338
+ const overlayEl = mobileVariant === "bottom-sheet"
339
+ ? h("div", {
340
+ class: "hu-nested-sidebar__sheet",
341
+ role: "dialog", "aria-modal": "true", "aria-label": drawerTitle,
342
+ }, h("div", { class: "hu-nested-sidebar__sheet-handle" }), h("div", { class: "hu-nested-sidebar__sheet-header" }, h("span", { class: "hu-nested-sidebar__sheet-title" }, drawerTitle), closeBtn("hu-nested-sidebar__sheet-close")), h("div", { class: "hu-nested-sidebar__sheet-body" }, renderNav(closeMobile)))
343
+ : h("aside", {
344
+ class: "hu-nested-sidebar__drawer",
345
+ role: "dialog", "aria-modal": "true", "aria-label": drawerTitle,
346
+ }, h("div", { class: "hu-nested-sidebar__drawer-header" }, h("div", { class: "hu-nested-sidebar__drawer-title" }, drawerTitle), closeBtn("hu-nested-sidebar__drawer-close")), h("div", { class: "hu-nested-sidebar__drawer-body" }, props.header && h("div", { class: "hu-nested-sidebar__header" }, props.header), renderNav(closeMobile), props.footer && h("div", { class: "hu-nested-sidebar__footer" }, props.footer)));
347
+ return h("div", {}, h("div", { class: "hu-nested-sidebar__backdrop", onClick: closeMobile, "aria-hidden": "true" }), FocusTrap({ active: true, restoreFocus: true, autoFocus: true, onEscape: closeMobile, children: overlayEl }));
348
+ };
349
+ // ── Desktop sidebar VNode ──────────────────────────────────────────────────
350
+ const sidebarEl = h("aside", {
351
+ class: cn("hu-nested-sidebar", `hu-nested-sidebar--${width}`, `hu-nested-sidebar--${position}`, responsive && "hu-nested-sidebar--responsive", props.class),
352
+ style: styleValue || undefined,
353
+ id: sid,
354
+ }, props.header && h("div", { class: "hu-nested-sidebar__header" }, props.header), renderNav(), props.footer && h("div", { class: "hu-nested-sidebar__footer" }, props.footer));
355
+ if (!responsive)
356
+ return sidebarEl;
357
+ // ── Responsive root: sidebar + mobile bar + overlay ────────────────────────
358
+ const menuIcon = props.menuIcon ?? menuHamburger;
359
+ const menuLabel = props.menuLabel ?? "Menu";
360
+ return h("div", { class: "hu-nested-sidebar__root", style: styleValue || undefined }, sidebarEl, h("div", { class: "hu-nested-sidebar__mobile-shell" }, h("div", { class: "hu-nested-sidebar__mobile-bar" }, h("button", {
361
+ type: "button",
362
+ class: "hu-nested-sidebar__menu-btn",
363
+ "aria-label": menuLabel,
364
+ "aria-haspopup": "dialog",
365
+ "aria-expanded": () => String(isOpen()),
366
+ onClick: () => setOpen(true),
367
+ }, h("span", { class: "hu-nested-sidebar__menu-btn-icon", "aria-hidden": "true" }, menuIcon), menuLabel)), renderOverlay));
368
+ }
@@ -1,13 +1,21 @@
1
1
  import { type Signal } from "@hyperpackai/hyperion";
2
2
  import { type VNode } from "../../theme/index.js";
3
3
  export interface PopoverProps {
4
- open: boolean | Signal<boolean>;
4
+ open?: boolean | Signal<boolean>;
5
+ defaultOpen?: boolean;
5
6
  side?: "top" | "bottom" | "left" | "right";
6
7
  trigger?: unknown;
7
- onClose?: () => void;
8
- onOpenChange?: (open: boolean) => void;
8
+ closeOnEscape?: boolean;
9
+ closeOnClickAway?: boolean;
10
+ id?: string;
11
+ role?: "dialog" | "menu" | "listbox" | "tooltip";
12
+ "aria-label"?: string;
13
+ "aria-labelledby"?: string;
14
+ onClose?: (reason?: PopoverCloseReason) => void;
15
+ onOpenChange?: (open: boolean, reason?: PopoverCloseReason) => void;
9
16
  children?: unknown;
10
17
  class?: string;
11
18
  }
19
+ export type PopoverCloseReason = "click-away" | "escape" | "programmatic" | "trigger";
12
20
  export declare function Popover(props: PopoverProps): VNode;
13
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Popover/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAgBpE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,CAuBlD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Popover/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAiBpE,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAChD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,QAAQ,GAAG,cAAc,GAAG,SAAS,CAAC;AAItF,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,CA2DlD"}
@@ -1,4 +1,6 @@
1
+ import { signal } from "@hyperpackai/hyperion";
1
2
  import { injectCSS, cn, h } from "../../theme/index.js";
3
+ import { ClickAwayListener } from "../ClickAwayListener/index.js";
2
4
  const POPOVER_CSS = `
3
5
  .hu-popover-wrap { position: relative; display: inline-flex; }
4
6
  .hu-popover-content {
@@ -12,23 +14,57 @@ const POPOVER_CSS = `
12
14
  .hu-popover-content--left { right: calc(100% + 6px); top: 0; }
13
15
  .hu-popover-content--right { left: calc(100% + 6px); top: 0; }
14
16
  `;
17
+ let popoverId = 0;
15
18
  export function Popover(props) {
16
19
  injectCSS("hu-popover", POPOVER_CSS);
17
- const { side = "bottom", trigger, children } = props;
18
- const isSignalOpen = typeof props.open === "object" && "peek" in props.open;
19
- const isOpen = () => isSignalOpen
20
- ? props.open.value
21
- : props.open;
20
+ const { side = "bottom", trigger, children, closeOnEscape = true, closeOnClickAway = true, role = "dialog" } = props;
21
+ const id = props.id ?? `hu-popover-${++popoverId}`;
22
+ const signalOpen = getSignalProp(props.open);
23
+ const internalOpen = signal(props.defaultOpen ?? false);
24
+ const isControlled = typeof props.open === "boolean" || !!signalOpen;
25
+ const isOpen = () => signalOpen ? signalOpen.value : typeof props.open === "boolean" ? props.open : internalOpen.value;
26
+ const setOpen = (next, reason = "programmatic") => {
27
+ if (signalOpen)
28
+ signalOpen.value = next;
29
+ else if (!isControlled)
30
+ internalOpen.value = next;
31
+ props.onOpenChange?.(next, reason);
32
+ if (!next)
33
+ props.onClose?.(reason);
34
+ };
35
+ const close = (reason = "programmatic") => setOpen(false, reason);
36
+ const toggle = () => setOpen(!isOpen(), "trigger");
22
37
  const overlay = () => isOpen()
23
38
  ? h("div", {
39
+ id,
24
40
  class: cn("hu-popover-content", `hu-popover-content--${side}`),
41
+ role,
42
+ tabindex: "-1",
43
+ "aria-label": props["aria-label"],
44
+ "aria-labelledby": props["aria-labelledby"],
25
45
  onKeyDown: (e) => {
26
- if (e.key === "Escape") {
27
- props.onOpenChange?.(false);
28
- props.onClose?.();
46
+ if (closeOnEscape && e.key === "Escape") {
47
+ e.preventDefault();
48
+ close("escape");
29
49
  }
30
50
  }
31
51
  }, children)
32
52
  : null;
33
- return h("div", { class: cn("hu-popover-wrap", props.class) }, trigger, typeof props.open === "boolean" ? overlay() : overlay);
53
+ const content = h("div", {
54
+ class: cn("hu-popover-wrap", props.class),
55
+ onBlur: closeOnClickAway ? (event) => {
56
+ if (!event.currentTarget.contains(event.relatedTarget))
57
+ close("click-away");
58
+ } : undefined
59
+ }, trigger && (isControlled
60
+ ? trigger
61
+ : h("div", { onClick: toggle, style: "display:contents;", "aria-controls": id, "aria-expanded": isOpen() ? "true" : "false" }, trigger)), typeof props.open === "boolean" ? overlay() : overlay);
62
+ return ClickAwayListener({
63
+ disabled: !closeOnClickAway || !isOpen(),
64
+ onClickAway: () => close("click-away"),
65
+ children: content
66
+ });
67
+ }
68
+ function getSignalProp(value) {
69
+ return typeof value === "object" && value != null && "peek" in value ? value : undefined;
34
70
  }
@@ -1,7 +1,32 @@
1
+ import { type Signal } from "@hyperpackai/hyperion";
1
2
  import { type VNode } from "../../theme/index.js";
2
3
  import type { CheckboxProps } from "../Checkbox/index.js";
3
- export interface RadioProps extends Omit<CheckboxProps, "indeterminate" | "defaultChecked"> {
4
+ export interface RadioProps extends Omit<CheckboxProps, "indeterminate"> {
4
5
  value: string;
6
+ onValueChange?: (value: string, e: Event) => void;
7
+ }
8
+ export interface RadioGroupOption {
9
+ value: string;
10
+ label: string;
11
+ description?: string;
12
+ disabled?: boolean;
13
+ }
14
+ export interface RadioGroupProps {
15
+ options: RadioGroupOption[];
16
+ value?: string | Signal<string>;
17
+ defaultValue?: string;
18
+ name?: string;
19
+ label?: string;
20
+ helper?: string;
21
+ error?: string;
22
+ required?: boolean;
23
+ disabled?: boolean;
24
+ direction?: "vertical" | "horizontal";
25
+ id?: string;
26
+ class?: string;
27
+ onChange?: (e: Event) => void;
28
+ onValueChange?: (value: string, e: Event) => void;
5
29
  }
6
30
  export declare function Radio(props: RadioProps): VNode;
31
+ export declare function RadioGroup(props: RadioGroupProps): VNode;
7
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Radio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAyB1D,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,aAAa,EAAE,eAAe,GAAG,gBAAgB,CAAC;IACzF,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CAW9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Radio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA8B1D,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACtC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;CACnD;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CAgB9C;AAID,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,CA4DxD"}
@@ -1,3 +1,4 @@
1
+ import { signal } from "@hyperpackai/hyperion";
1
2
  import { injectCSS, cn, h } from "../../theme/index.js";
2
3
  const RADIO_CSS = `
3
4
  .hu-radio-wrap { display: inline-flex; align-items: center; gap: var(--hu-space-2); cursor: pointer; min-height: 28px; max-width: 100%; }
@@ -20,9 +21,67 @@ const RADIO_CSS = `
20
21
  .hu-radio:focus-visible { outline: 2px solid var(--hu-primary); outline-offset: 2px; }
21
22
  .hu-radio-group { display: flex; flex-direction: column; gap: var(--hu-space-2); }
22
23
  .hu-radio-group--horizontal { flex-direction: row; flex-wrap: wrap; }
24
+ .hu-radio-group-wrap { display: flex; flex-direction: column; gap: 6px; }
25
+ .hu-radio-group__label { color: var(--hu-text); font-size: var(--hu-font-size-sm); font-weight: var(--hu-font-weight-semibold); line-height: 1.35; }
26
+ .hu-radio-group__label--required::after { content: " *"; color: var(--hu-error); }
27
+ .hu-radio-group__helper { font-size: var(--hu-font-size-xs); color: var(--hu-text-2); line-height: 1.45; }
28
+ .hu-radio-group__error { font-size: var(--hu-font-size-xs); color: var(--hu-error); line-height: 1.45; }
23
29
  `;
24
30
  export function Radio(props) {
25
31
  injectCSS("hu-radio", RADIO_CSS);
26
- const { label, description, disabled, id, ...rest } = props;
27
- return h("label", { class: cn("hu-radio-wrap", disabled && "hu-radio-wrap--disabled", props.class) }, h("input", { ...rest, type: "radio", id, disabled, class: "hu-radio" }), (label || description) && h("span", { class: "hu-checkbox-text" }, label && h("span", { class: "hu-checkbox-label" }, label), description && h("span", { class: "hu-checkbox-desc" }, description)));
32
+ const { label, description, disabled, id, onChange, onCheckedChange, onValueChange, ...rest } = props;
33
+ const handleChange = (event) => {
34
+ onChange?.(event);
35
+ onCheckedChange?.(event.target.checked, event);
36
+ if (event.target.checked)
37
+ onValueChange?.(props.value, event);
38
+ };
39
+ return h("label", { class: cn("hu-radio-wrap", disabled && "hu-radio-wrap--disabled", props.class) }, h("input", { ...rest, type: "radio", id, disabled, onChange: handleChange, class: "hu-radio" }), (label || description) && h("span", { class: "hu-checkbox-text" }, label && h("span", { class: "hu-checkbox-label" }, label), description && h("span", { class: "hu-checkbox-desc" }, description)));
40
+ }
41
+ let radioGroupId = 0;
42
+ export function RadioGroup(props) {
43
+ injectCSS("hu-radio", RADIO_CSS);
44
+ const { options, label, helper, error, required, disabled, direction = "vertical", onChange, onValueChange } = props;
45
+ const generatedId = props.id ?? `hu-radio-group-${++radioGroupId}`;
46
+ const labelId = label ? `${generatedId}-label` : undefined;
47
+ const helperId = helper && !error ? `${generatedId}-helper` : undefined;
48
+ const errorId = error ? `${generatedId}-error` : undefined;
49
+ const describedBy = [errorId, helperId].filter(Boolean).join(" ") || undefined;
50
+ const name = props.name ?? generatedId;
51
+ const valueSignal = getSignalProp(props.value);
52
+ const isControlled = typeof props.value === "string" || !!valueSignal;
53
+ const internalValue = signal(typeof props.value === "string" ? props.value : props.defaultValue ?? "");
54
+ const currentValue = () => valueSignal ? valueSignal.value : typeof props.value === "string" ? props.value : internalValue.value;
55
+ const handleValueChange = (value, event) => {
56
+ if (valueSignal)
57
+ valueSignal.value = value;
58
+ else if (!isControlled)
59
+ internalValue.value = value;
60
+ onChange?.(event);
61
+ onValueChange?.(value, event);
62
+ };
63
+ return h("div", { class: cn("hu-radio-group-wrap", props.class) }, label && h("div", {
64
+ id: labelId,
65
+ class: cn("hu-radio-group__label", required && "hu-radio-group__label--required")
66
+ }, label), h("div", {
67
+ class: cn("hu-radio-group", direction === "horizontal" && "hu-radio-group--horizontal"),
68
+ role: "radiogroup",
69
+ "aria-labelledby": labelId,
70
+ "aria-describedby": describedBy,
71
+ "aria-invalid": error ? "true" : undefined,
72
+ "aria-required": required ? "true" : undefined
73
+ }, ...options.map((option, index) => Radio({
74
+ id: `${generatedId}-${index}`,
75
+ name,
76
+ value: option.value,
77
+ label: option.label,
78
+ description: option.description,
79
+ checked: currentValue() === option.value,
80
+ required,
81
+ disabled: disabled || option.disabled,
82
+ onValueChange: handleValueChange
83
+ }))), error && h("span", { id: errorId, class: "hu-radio-group__error", role: "alert" }, error), !error && helper && h("span", { id: helperId, class: "hu-radio-group__helper" }, helper));
84
+ }
85
+ function getSignalProp(value) {
86
+ return typeof value === "object" && value != null && "peek" in value ? value : undefined;
28
87
  }
@@ -7,17 +7,22 @@ export interface SelectOption {
7
7
  }
8
8
  export interface SelectProps {
9
9
  value?: string;
10
+ defaultValue?: string;
10
11
  options: SelectOption[];
11
12
  placeholder?: string;
12
13
  disabled?: boolean;
13
14
  required?: boolean;
14
15
  size?: "sm" | "md" | "lg";
15
16
  error?: string;
17
+ helper?: string;
16
18
  label?: string;
17
19
  id?: string;
18
20
  name?: string;
19
21
  class?: string;
22
+ "aria-label"?: string;
23
+ "aria-describedby"?: string;
20
24
  onChange?: (e: Event) => void;
25
+ onValueChange?: (value: string, e: Event) => void;
21
26
  }
22
27
  export declare function Select(props: SelectProps): VNode;
23
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Select/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AA0BpE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,KAAK,CAoChD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Select/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AA0BpE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;CACnD;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,KAAK,CAkEhD"}