@epoch32/visage 1.1.3 → 1.1.5

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 (52) hide show
  1. package/dist/api/index.js +55 -4085
  2. package/dist/charts/index.js +48 -2806
  3. package/dist/devtools/index.js +18 -4771
  4. package/dist/dom/index.js +32 -1639
  5. package/dist/form/index.js +13 -1498
  6. package/dist/index-061jf06a.js +848 -0
  7. package/dist/index-0madxn60.js +442 -0
  8. package/dist/index-1xtrtz1t.js +256 -0
  9. package/dist/index-2zz0g2n2.js +212 -0
  10. package/dist/index-5s255b3w.js +303 -0
  11. package/dist/index-6kfe1nr4.js +1179 -0
  12. package/dist/index-8czhpea8.js +338 -0
  13. package/dist/index-8x8xz80v.js +727 -0
  14. package/dist/index-921pn75b.js +0 -0
  15. package/dist/index-965ryh36.js +537 -0
  16. package/dist/index-9fvarkrx.js +98 -0
  17. package/dist/index-a4xns6cc.js +219 -0
  18. package/dist/index-b5wc752a.js +1696 -0
  19. package/dist/index-df5e614s.js +334 -0
  20. package/dist/index-dyvgtp43.js +1094 -0
  21. package/dist/index-grhdtcb5.js +234 -0
  22. package/dist/index-h6tjqk6e.js +158 -0
  23. package/dist/index-js199er4.js +101 -0
  24. package/dist/index-x2rs3vab.js +124 -0
  25. package/dist/index-y9bq7h9g.js +1162 -0
  26. package/dist/index-zr6cvset.js +2092 -0
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +458 -11920
  29. package/dist/jsx-runtime/index.js +13 -1719
  30. package/dist/query/index.js +47 -2505
  31. package/dist/router/index.js +25 -1945
  32. package/dist/rpc/index.js +39 -3567
  33. package/dist/server/index.js +11 -1256
  34. package/dist/signals/index.js +52 -1160
  35. package/dist/style/index.js +77 -2429
  36. package/dist/template/index.js +7 -1232
  37. package/dist/ui/index.d.ts +1 -1
  38. package/dist/ui/index.js +150 -4249
  39. package/dist/ui/tabs.d.ts +23 -1
  40. package/dist/virtual/index.js +12 -2044
  41. package/dist/vite/index.js +27 -746
  42. package/package.json +2 -2
  43. package/src/dom/render.ts +20 -7
  44. package/src/index.ts +2 -0
  45. package/src/signals/computed.ts +19 -12
  46. package/src/signals/effect.ts +7 -7
  47. package/src/signals/signal.ts +16 -12
  48. package/src/signals/system.ts +50 -21
  49. package/src/ui/index.ts +2 -0
  50. package/src/ui/tabs.tsx +211 -10
  51. package/src/ui/utils.ts +1 -3
  52. package/src/vite/lucide.ts +22 -2
@@ -0,0 +1,1696 @@
1
+ import {
2
+ onCleanup2,
3
+ createContext2,
4
+ useContext2,
5
+ signal2,
6
+ createSignal2
7
+ } from "./index-6kfe1nr4.js";
8
+ import {
9
+ Show2,
10
+ For2,
11
+ Portal2
12
+ } from "./index-5s255b3w.js";
13
+ import {
14
+ Fragment2,
15
+ jsx2,
16
+ jsxs2
17
+ } from "./index-9fvarkrx.js";
18
+ import {
19
+ createId2,
20
+ createControllableSignal2,
21
+ trapFocus2,
22
+ onClickOutside2
23
+ } from "./index-8x8xz80v.js";
24
+
25
+ // src/ui/accordion.tsx
26
+ var AccordionContext = createContext2(null);
27
+ function useAccordionContext2() {
28
+ return useContext2(AccordionContext);
29
+ }
30
+ function AccordionRoot(props) {
31
+ const isSingle = props.type === "single";
32
+ const collapsible = props.collapsible ?? !isSingle;
33
+ const [singleValue, setSingleValue] = createControllableSignal2({
34
+ value: isSingle ? props.value : undefined,
35
+ defaultValue: isSingle ? props.defaultValue ?? "" : "",
36
+ onChange: isSingle ? props.onValueChange : undefined
37
+ });
38
+ const [multiValue, setMultiValue] = createControllableSignal2({
39
+ value: !isSingle ? props.value : undefined,
40
+ defaultValue: !isSingle ? props.defaultValue ?? [] : [],
41
+ onChange: !isSingle ? props.onValueChange : undefined
42
+ });
43
+ const isExpanded = (val) => {
44
+ if (isSingle) {
45
+ return singleValue() === val;
46
+ }
47
+ return multiValue().includes(val);
48
+ };
49
+ const toggleItem = (val) => {
50
+ if (isSingle) {
51
+ if (singleValue() === val) {
52
+ if (collapsible) {
53
+ setSingleValue("");
54
+ }
55
+ } else {
56
+ setSingleValue(val);
57
+ }
58
+ } else {
59
+ const current = multiValue();
60
+ if (current.includes(val)) {
61
+ setMultiValue(current.filter((item) => item !== val));
62
+ } else {
63
+ setMultiValue([...current, val]);
64
+ }
65
+ }
66
+ };
67
+ const ctx = {
68
+ isExpanded,
69
+ toggleItem,
70
+ type: props.type,
71
+ collapsible
72
+ };
73
+ const {
74
+ as: Tag = "div",
75
+ items,
76
+ itemClass,
77
+ triggerClass,
78
+ contentClass,
79
+ children,
80
+ class: className,
81
+ style,
82
+ ...rest
83
+ } = props;
84
+ return AccordionContext.Provider({
85
+ value: ctx,
86
+ children: () => /* @__PURE__ */ jsxs2(Tag, {
87
+ class: className,
88
+ style,
89
+ ...rest,
90
+ children: [
91
+ items ? items.map((item) => /* @__PURE__ */ jsx2(AccordionItem2, {
92
+ value: item.value,
93
+ disabled: item.disabled,
94
+ class: itemClass,
95
+ children: () => /* @__PURE__ */ jsxs2(Fragment2, {
96
+ children: [
97
+ /* @__PURE__ */ jsx2(AccordionTrigger2, {
98
+ class: triggerClass,
99
+ children: typeof item.trigger === "function" ? item.trigger() : item.trigger
100
+ }),
101
+ /* @__PURE__ */ jsx2(AccordionContent2, {
102
+ class: contentClass,
103
+ children: typeof item.content === "function" ? item.content() : item.content
104
+ })
105
+ ]
106
+ })
107
+ })) : null,
108
+ typeof children === "function" ? children() : children
109
+ ]
110
+ })
111
+ });
112
+ }
113
+ var AccordionItemContext = createContext2(null);
114
+ function useAccordionItemContext2() {
115
+ return useContext2(AccordionItemContext);
116
+ }
117
+ function AccordionItem2(props) {
118
+ const itemId = createId2("accordion-item");
119
+ const ctx = {
120
+ value: props.value,
121
+ itemId
122
+ };
123
+ const accordion = useContext2(AccordionContext);
124
+ const isOpen = () => accordion ? accordion.isExpanded(props.value) : false;
125
+ const { value, disabled, as: Tag = "div", children, class: className, style, ...rest } = props;
126
+ return AccordionItemContext.Provider({
127
+ value: ctx,
128
+ children: () => /* @__PURE__ */ jsx2(Tag, {
129
+ "data-state": () => isOpen() ? "open" : "closed",
130
+ "data-disabled": disabled,
131
+ class: className,
132
+ style,
133
+ ...rest,
134
+ children: typeof children === "function" ? children() : children
135
+ })
136
+ });
137
+ }
138
+ function AccordionTrigger2(props) {
139
+ const accordion = useContext2(AccordionContext);
140
+ const item = useContext2(AccordionItemContext);
141
+ const isOpen = () => accordion && item ? accordion.isExpanded(item.value) : false;
142
+ const handleClick = (e) => {
143
+ if (props.onClick)
144
+ props.onClick(e);
145
+ if (!e.defaultPrevented && accordion && item) {
146
+ accordion.toggleItem(item.value);
147
+ }
148
+ };
149
+ const { as: Tag = "button", children, onClick, class: className, style, ...rest } = props;
150
+ return /* @__PURE__ */ jsx2(Tag, {
151
+ id: item ? `${item.itemId}-trigger` : undefined,
152
+ type: Tag === "button" ? "button" : undefined,
153
+ "aria-expanded": isOpen,
154
+ "aria-controls": item ? `${item.itemId}-content` : undefined,
155
+ "data-state": () => isOpen() ? "open" : "closed",
156
+ class: className,
157
+ style,
158
+ onClick: handleClick,
159
+ ...rest,
160
+ children
161
+ });
162
+ }
163
+ function AccordionContent2(props) {
164
+ const accordion = useContext2(AccordionContext);
165
+ const item = useContext2(AccordionItemContext);
166
+ const isOpen = () => accordion && item ? accordion.isExpanded(item.value) : false;
167
+ const { forceMount, as: Tag = "div", children, class: className, style, ...rest } = props;
168
+ if (forceMount) {
169
+ return /* @__PURE__ */ jsx2(Tag, {
170
+ id: item ? `${item.itemId}-content` : undefined,
171
+ role: "region",
172
+ "aria-labelledby": item ? `${item.itemId}-trigger` : undefined,
173
+ "data-state": () => isOpen() ? "open" : "closed",
174
+ hidden: () => !isOpen(),
175
+ class: className,
176
+ style,
177
+ ...rest,
178
+ children
179
+ });
180
+ }
181
+ return /* @__PURE__ */ jsx2(Show2, {
182
+ when: isOpen,
183
+ children: /* @__PURE__ */ jsx2(Tag, {
184
+ id: item ? `${item.itemId}-content` : undefined,
185
+ role: "region",
186
+ "aria-labelledby": item ? `${item.itemId}-trigger` : undefined,
187
+ "data-state": "open",
188
+ class: className,
189
+ style,
190
+ ...rest,
191
+ children
192
+ })
193
+ });
194
+ }
195
+ var Accordion2 = Object.assign(AccordionRoot, {
196
+ Root: AccordionRoot,
197
+ Item: AccordionItem2,
198
+ Trigger: AccordionTrigger2,
199
+ Content: AccordionContent2
200
+ });
201
+ // src/ui/command-dialog.tsx
202
+ function CommandDialog2(props) {
203
+ const [open, setOpen] = createControllableSignal2({
204
+ value: props.open,
205
+ defaultValue: props.defaultOpen ?? false,
206
+ onChange: props.onOpenChange
207
+ });
208
+ const { overlayClass, contentClass, style, ...rest } = props;
209
+ return /* @__PURE__ */ jsx2(Portal2, {
210
+ children: /* @__PURE__ */ jsx2("div", {
211
+ style: () => ({
212
+ display: open() ? "flex" : "none",
213
+ position: "fixed",
214
+ inset: 0,
215
+ background: "rgba(0, 0, 0, 0.6)",
216
+ backdropFilter: "blur(4px)",
217
+ alignItems: "flex-start",
218
+ justifyContent: "center",
219
+ paddingTop: "15vh",
220
+ zIndex: 9999
221
+ }),
222
+ class: overlayClass,
223
+ onClick: (e) => {
224
+ if (e.target === e.currentTarget)
225
+ setOpen(false);
226
+ },
227
+ children: /* @__PURE__ */ jsx2("div", {
228
+ style: { width: "100%", maxWidth: "560px" },
229
+ class: contentClass,
230
+ children: /* @__PURE__ */ jsx2(CommandRoot, {
231
+ ...rest
232
+ })
233
+ })
234
+ })
235
+ });
236
+ }
237
+
238
+ // src/ui/command.tsx
239
+ var CommandContext = createContext2(null);
240
+ function useCommandContext2() {
241
+ return useContext2(CommandContext);
242
+ }
243
+ function CommandRoot(props) {
244
+ const [search, setSearch] = createControllableSignal2({
245
+ value: props.search,
246
+ defaultValue: "",
247
+ onChange: props.onSearchChange
248
+ });
249
+ const [activeValue, setActiveValue] = createControllableSignal2({
250
+ value: props.value,
251
+ defaultValue: props.defaultValue ?? "",
252
+ onChange: props.onValueChange
253
+ });
254
+ const commandId = createId2("command");
255
+ const filterMatches = (text, keywords) => {
256
+ const q = search().toLowerCase().trim();
257
+ if (!q)
258
+ return true;
259
+ if (text.toLowerCase().includes(q))
260
+ return true;
261
+ if (keywords?.some((k) => k.toLowerCase().includes(q)))
262
+ return true;
263
+ return false;
264
+ };
265
+ const ctx = {
266
+ search,
267
+ setSearch,
268
+ activeValue,
269
+ setActiveValue,
270
+ filterMatches,
271
+ commandId
272
+ };
273
+ const {
274
+ as: Tag = "div",
275
+ placeholder = "Type a command or search...",
276
+ groups,
277
+ items,
278
+ emptyText = "No results found.",
279
+ class: className,
280
+ style,
281
+ inputClass,
282
+ listClass,
283
+ itemClass,
284
+ children,
285
+ ...rest
286
+ } = props;
287
+ if (groups || items) {
288
+ return CommandContext.Provider({
289
+ value: ctx,
290
+ children: () => /* @__PURE__ */ jsxs2(Tag, {
291
+ class: className,
292
+ style: { display: "flex", flexDirection: "column", overflow: "hidden", ...style },
293
+ ...rest,
294
+ children: [
295
+ /* @__PURE__ */ jsx2(CommandInput2, {
296
+ placeholder,
297
+ class: inputClass
298
+ }),
299
+ /* @__PURE__ */ jsxs2(CommandList2, {
300
+ class: listClass,
301
+ children: [
302
+ /* @__PURE__ */ jsx2(CommandEmpty2, {
303
+ children: emptyText
304
+ }),
305
+ groups?.map((grp) => /* @__PURE__ */ jsx2(CommandGroup2, {
306
+ heading: grp.heading,
307
+ children: grp.items.map((item) => /* @__PURE__ */ jsxs2(CommandItem2, {
308
+ value: item.value,
309
+ shortcut: item.shortcut,
310
+ disabled: item.disabled,
311
+ onSelect: item.onSelect,
312
+ keywords: item.keywords,
313
+ class: item.class ?? itemClass,
314
+ children: [
315
+ item.icon ? /* @__PURE__ */ jsx2("span", {
316
+ style: { display: "inline-flex", alignItems: "center", marginRight: "8px" },
317
+ children: typeof item.icon === "function" ? item.icon() : item.icon
318
+ }) : null,
319
+ typeof item.label === "function" ? item.label() : item.label
320
+ ]
321
+ }))
322
+ })),
323
+ items?.map((item) => /* @__PURE__ */ jsxs2(CommandItem2, {
324
+ value: item.value,
325
+ shortcut: item.shortcut,
326
+ disabled: item.disabled,
327
+ onSelect: item.onSelect,
328
+ keywords: item.keywords,
329
+ class: item.class ?? itemClass,
330
+ children: [
331
+ item.icon ? /* @__PURE__ */ jsx2("span", {
332
+ style: { display: "inline-flex", alignItems: "center", marginRight: "8px" },
333
+ children: typeof item.icon === "function" ? item.icon() : item.icon
334
+ }) : null,
335
+ typeof item.label === "function" ? item.label() : item.label
336
+ ]
337
+ })),
338
+ typeof children === "function" ? children() : children
339
+ ]
340
+ })
341
+ ]
342
+ })
343
+ });
344
+ }
345
+ return CommandContext.Provider({
346
+ value: ctx,
347
+ children: () => /* @__PURE__ */ jsx2(Tag, {
348
+ class: className,
349
+ style,
350
+ ...rest,
351
+ children: typeof children === "function" ? children() : children
352
+ })
353
+ });
354
+ }
355
+ function CommandInput2(props) {
356
+ const ctx = useContext2(CommandContext);
357
+ const handleKeyDown = (e) => {
358
+ const list = e.currentTarget.closest('[role="combobox"], [data-command-root], div')?.querySelector('[role="listbox"]');
359
+ if (!list)
360
+ return;
361
+ const items = Array.from(list.querySelectorAll('[role="option"]:not([aria-disabled="true"]):not([style*="display: none"])'));
362
+ if (items.length === 0)
363
+ return;
364
+ const currentActive = items.find((el) => el.getAttribute("aria-selected") === "true") ?? items[0];
365
+ const index = currentActive ? items.indexOf(currentActive) : 0;
366
+ if (e.key === "ArrowDown") {
367
+ e.preventDefault();
368
+ const nextIndex = (index + 1) % items.length;
369
+ const nextItem = items[nextIndex];
370
+ if (nextItem && ctx) {
371
+ ctx.setActiveValue(nextItem.getAttribute("data-value") ?? "");
372
+ nextItem.scrollIntoView({ block: "nearest" });
373
+ }
374
+ } else if (e.key === "ArrowUp") {
375
+ e.preventDefault();
376
+ const prevIndex = (index - 1 + items.length) % items.length;
377
+ const prevItem = items[prevIndex];
378
+ if (prevItem && ctx) {
379
+ ctx.setActiveValue(prevItem.getAttribute("data-value") ?? "");
380
+ prevItem.scrollIntoView({ block: "nearest" });
381
+ }
382
+ } else if (e.key === "Enter" && currentActive) {
383
+ e.preventDefault();
384
+ currentActive.click();
385
+ }
386
+ };
387
+ const { placeholder = "Type a command...", class: className, style, ...rest } = props;
388
+ return /* @__PURE__ */ jsx2("input", {
389
+ type: "text",
390
+ role: "searchbox",
391
+ placeholder,
392
+ value: () => ctx?.search() ?? "",
393
+ onInput: (e) => ctx?.setSearch(e.target.value),
394
+ onKeyDown: handleKeyDown,
395
+ class: className,
396
+ style,
397
+ ...rest
398
+ });
399
+ }
400
+ function CommandList2(props) {
401
+ const { as: Tag = "div", children, class: className, style, ...rest } = props;
402
+ return /* @__PURE__ */ jsx2(Tag, {
403
+ role: "listbox",
404
+ class: className,
405
+ style: { overflowY: "auto", ...style },
406
+ ...rest,
407
+ children
408
+ });
409
+ }
410
+ function CommandEmpty2(props) {
411
+ const ctx = useContext2(CommandContext);
412
+ const { as: Tag = "div", children, class: className, style, ...rest } = props;
413
+ return /* @__PURE__ */ jsx2(Tag, {
414
+ class: className,
415
+ style: () => ({
416
+ display: ctx && ctx.search().trim().length > 0 ? undefined : "none",
417
+ textAlign: "center",
418
+ padding: "16px",
419
+ color: "#94a3b8",
420
+ ...style
421
+ }),
422
+ ...rest,
423
+ children
424
+ });
425
+ }
426
+ function CommandGroup2(props) {
427
+ const { heading, as: Tag = "div", children, class: className, style, ...rest } = props;
428
+ return /* @__PURE__ */ jsxs2(Tag, {
429
+ role: "group",
430
+ class: className,
431
+ style,
432
+ ...rest,
433
+ children: [
434
+ heading ? /* @__PURE__ */ jsx2("div", {
435
+ style: {
436
+ fontSize: "11px",
437
+ fontWeight: 700,
438
+ textTransform: "uppercase",
439
+ letterSpacing: "0.05em",
440
+ color: "#64748b",
441
+ padding: "6px 10px"
442
+ },
443
+ children: heading
444
+ }) : null,
445
+ children
446
+ ]
447
+ });
448
+ }
449
+ function CommandItem2(props) {
450
+ const ctx = useContext2(CommandContext);
451
+ const itemValue = props.value ?? (typeof props.children === "string" ? props.children : createId2("item"));
452
+ const isVisible = () => {
453
+ if (!ctx)
454
+ return true;
455
+ const text = typeof props.children === "string" ? props.children : itemValue;
456
+ return ctx.filterMatches(text, props.keywords);
457
+ };
458
+ const isSelected = () => ctx ? ctx.activeValue() === itemValue : false;
459
+ const handleClick = () => {
460
+ if (props.disabled)
461
+ return;
462
+ if (props.onSelect)
463
+ props.onSelect();
464
+ };
465
+ const { as: Tag = "div", shortcut, disabled, children, class: className, style, ...rest } = props;
466
+ return /* @__PURE__ */ jsxs2(Tag, {
467
+ role: "option",
468
+ "data-value": itemValue,
469
+ "aria-selected": isSelected,
470
+ "aria-disabled": disabled,
471
+ class: className,
472
+ style: () => ({
473
+ display: isVisible() ? "flex" : "none",
474
+ alignItems: "center",
475
+ justifyContent: "space-between",
476
+ cursor: disabled ? "not-allowed" : "pointer",
477
+ ...style
478
+ }),
479
+ onClick: handleClick,
480
+ ...rest,
481
+ children: [
482
+ /* @__PURE__ */ jsx2("div", {
483
+ style: { display: "flex", alignItems: "center" },
484
+ children
485
+ }),
486
+ shortcut ? /* @__PURE__ */ jsx2("span", {
487
+ style: {
488
+ fontSize: "11px",
489
+ color: "#64748b",
490
+ background: "#1e293b",
491
+ padding: "2px 6px",
492
+ borderRadius: "4px"
493
+ },
494
+ children: shortcut
495
+ }) : null
496
+ ]
497
+ });
498
+ }
499
+ var Command2 = Object.assign(CommandRoot, {
500
+ Root: CommandRoot,
501
+ Dialog: CommandDialog2,
502
+ Input: CommandInput2,
503
+ List: CommandList2,
504
+ Empty: CommandEmpty2,
505
+ Group: CommandGroup2,
506
+ Item: CommandItem2
507
+ });
508
+ // src/ui/data-table.tsx
509
+ function DataTable2(props) {
510
+ const [sortState, setSortState] = createControllableSignal2({
511
+ value: props.sort,
512
+ defaultValue: { column: "", direction: null },
513
+ onChange: props.onSortChange
514
+ });
515
+ const [selectedKeys, setSelectedKeys] = createControllableSignal2({
516
+ value: props.selectedKeys,
517
+ defaultValue: [],
518
+ onChange: props.onSelectionChange
519
+ });
520
+ const [currentPage, setCurrentPage] = createSignal2(0, { internal: true });
521
+ const pageSize = props.pageSize ?? 10;
522
+ const selectionMode = props.selection ?? "none";
523
+ const getRowId = (row, index) => {
524
+ if (props.getRowId)
525
+ return props.getRowId(row, index);
526
+ if (row && typeof row === "object" && "id" in row)
527
+ return String(row.id);
528
+ return String(index);
529
+ };
530
+ const filteredData = () => {
531
+ const raw = typeof props.data === "function" ? props.data() : props.data;
532
+ if (!raw)
533
+ return [];
534
+ const filterQuery = props.filter ? (typeof props.filter === "function" ? props.filter() : props.filter)?.toLowerCase().trim() : "";
535
+ if (!filterQuery)
536
+ return raw;
537
+ return raw.filter((row) => Object.values(row).some((val) => String(val).toLowerCase().includes(filterQuery)));
538
+ };
539
+ const sortedData = () => {
540
+ const list = [...filteredData()];
541
+ const currentSort = sortState();
542
+ if (!currentSort.column || !currentSort.direction)
543
+ return list;
544
+ return list.sort((a, b) => {
545
+ const aVal = a[currentSort.column];
546
+ const bVal = b[currentSort.column];
547
+ if (aVal === bVal)
548
+ return 0;
549
+ if (aVal === null || aVal === undefined)
550
+ return 1;
551
+ if (bVal === null || bVal === undefined)
552
+ return -1;
553
+ const comp = aVal < bVal ? -1 : 1;
554
+ return currentSort.direction === "asc" ? comp : -comp;
555
+ });
556
+ };
557
+ const totalPages = () => Math.ceil(sortedData().length / pageSize) || 1;
558
+ const paginatedData = () => {
559
+ if (!props.pageSize)
560
+ return sortedData();
561
+ const start = currentPage() * pageSize;
562
+ return sortedData().slice(start, start + pageSize);
563
+ };
564
+ const handleSort = (colId) => {
565
+ const current = sortState();
566
+ if (current.column !== colId) {
567
+ setSortState({ column: colId, direction: "asc" });
568
+ } else if (current.direction === "asc") {
569
+ setSortState({ column: colId, direction: "desc" });
570
+ } else {
571
+ setSortState({ column: "", direction: null });
572
+ }
573
+ };
574
+ const handleSelectRow = (key, e) => {
575
+ e.stopPropagation();
576
+ if (selectionMode === "single") {
577
+ setSelectedKeys(selectedKeys().includes(key) ? [] : [key]);
578
+ } else if (selectionMode === "multi") {
579
+ const current = selectedKeys();
580
+ if (current.includes(key)) {
581
+ setSelectedKeys(current.filter((k) => k !== key));
582
+ } else {
583
+ setSelectedKeys([...current, key]);
584
+ }
585
+ }
586
+ };
587
+ const handleSelectAll = () => {
588
+ if (selectionMode !== "multi")
589
+ return;
590
+ const allCurrentKeys = paginatedData().map((row, i) => getRowId(row, i));
591
+ const allSelected = allCurrentKeys.every((k) => selectedKeys().includes(k));
592
+ if (allSelected) {
593
+ setSelectedKeys(selectedKeys().filter((k) => !allCurrentKeys.includes(k)));
594
+ } else {
595
+ setSelectedKeys(Array.from(new Set([...selectedKeys(), ...allCurrentKeys])));
596
+ }
597
+ };
598
+ const {
599
+ class: className,
600
+ style,
601
+ tableClass,
602
+ headerClass,
603
+ rowClass,
604
+ cellClass,
605
+ paginationClass,
606
+ emptyText = "No data available",
607
+ ...rest
608
+ } = props;
609
+ return /* @__PURE__ */ jsxs2("div", {
610
+ class: className,
611
+ style: { display: "flex", flexDirection: "column", width: "100%", ...style },
612
+ ...rest,
613
+ children: [
614
+ /* @__PURE__ */ jsx2("div", {
615
+ style: { overflowX: "auto", width: "100%" },
616
+ children: /* @__PURE__ */ jsxs2("table", {
617
+ class: tableClass,
618
+ style: { width: "100%", borderCollapse: "collapse" },
619
+ children: [
620
+ /* @__PURE__ */ jsx2("thead", {
621
+ class: headerClass,
622
+ children: /* @__PURE__ */ jsxs2("tr", {
623
+ children: [
624
+ selectionMode === "multi" ? /* @__PURE__ */ jsx2("th", {
625
+ style: { width: "40px", padding: "8px" },
626
+ children: /* @__PURE__ */ jsx2("input", {
627
+ type: "checkbox",
628
+ checked: () => {
629
+ const keys = paginatedData().map((row, i) => getRowId(row, i));
630
+ return keys.length > 0 && keys.every((k) => selectedKeys().includes(k));
631
+ },
632
+ onChange: handleSelectAll
633
+ })
634
+ }) : null,
635
+ props.columns.map((col) => {
636
+ const isSorted = () => sortState().column === col.id;
637
+ const sortDir = () => isSorted() ? sortState().direction : null;
638
+ return /* @__PURE__ */ jsx2("th", {
639
+ style: {
640
+ width: col.width,
641
+ textAlign: col.align ?? "left",
642
+ cursor: col.sortable ? "pointer" : "default",
643
+ userSelect: col.sortable ? "none" : "auto",
644
+ padding: "10px 12px"
645
+ },
646
+ onClick: () => {
647
+ if (col.sortable)
648
+ handleSort(col.id);
649
+ },
650
+ children: /* @__PURE__ */ jsxs2("div", {
651
+ style: { display: "inline-flex", alignItems: "center", gap: "6px" },
652
+ children: [
653
+ /* @__PURE__ */ jsx2("span", {
654
+ children: typeof col.header === "function" ? col.header() : col.header
655
+ }),
656
+ col.sortable ? /* @__PURE__ */ jsx2("span", {
657
+ style: { fontSize: "10px", color: isSorted() ? "#38bdf8" : "#64748b" },
658
+ children: () => sortDir() === "asc" ? "▲" : sortDir() === "desc" ? "▼" : "⇅"
659
+ }) : null
660
+ ]
661
+ })
662
+ });
663
+ })
664
+ ]
665
+ })
666
+ }),
667
+ /* @__PURE__ */ jsx2("tbody", {
668
+ children: /* @__PURE__ */ jsx2(For2, {
669
+ each: paginatedData,
670
+ fallback: /* @__PURE__ */ jsx2("tr", {
671
+ children: /* @__PURE__ */ jsx2("td", {
672
+ colSpan: props.columns.length + (selectionMode === "multi" ? 1 : 0),
673
+ style: { textAlign: "center", padding: "24px", color: "#64748b" },
674
+ children: emptyText
675
+ })
676
+ }),
677
+ children: (row, idx) => {
678
+ const key = () => getRowId(row, idx());
679
+ const isSelected = () => selectedKeys().includes(key());
680
+ return /* @__PURE__ */ jsxs2("tr", {
681
+ class: rowClass,
682
+ style: { cursor: props.onRowClick ? "pointer" : "default" },
683
+ "data-selected": isSelected,
684
+ onClick: () => {
685
+ if (props.onRowClick)
686
+ props.onRowClick(row, idx());
687
+ },
688
+ children: [
689
+ selectionMode === "multi" ? /* @__PURE__ */ jsx2("td", {
690
+ style: { width: "40px", padding: "8px" },
691
+ children: /* @__PURE__ */ jsx2("input", {
692
+ type: "checkbox",
693
+ checked: isSelected,
694
+ onClick: (e) => handleSelectRow(key(), e)
695
+ })
696
+ }) : null,
697
+ props.columns.map((col) => {
698
+ const content = col.cell ? col.cell(row, idx()) : col.accessorKey ? row[col.accessorKey] : row[col.id];
699
+ return /* @__PURE__ */ jsx2("td", {
700
+ class: cellClass,
701
+ style: {
702
+ textAlign: col.align ?? "left",
703
+ padding: "10px 12px"
704
+ },
705
+ children: typeof content === "function" ? content() : content
706
+ });
707
+ })
708
+ ]
709
+ });
710
+ }
711
+ })
712
+ })
713
+ ]
714
+ })
715
+ }),
716
+ props.pageSize ? /* @__PURE__ */ jsxs2("div", {
717
+ class: paginationClass,
718
+ style: {
719
+ display: "flex",
720
+ alignItems: "center",
721
+ justifyContent: "space-between",
722
+ padding: "10px 12px",
723
+ borderTop: "1px solid #1e293b",
724
+ fontSize: "12px",
725
+ color: "#94a3b8"
726
+ },
727
+ children: [
728
+ /* @__PURE__ */ jsxs2("div", {
729
+ children: [
730
+ "Showing ",
731
+ /* @__PURE__ */ jsx2("strong", {
732
+ children: () => currentPage() * pageSize + 1
733
+ }),
734
+ " to",
735
+ " ",
736
+ /* @__PURE__ */ jsx2("strong", {
737
+ children: () => Math.min((currentPage() + 1) * pageSize, sortedData().length)
738
+ }),
739
+ " ",
740
+ "of ",
741
+ /* @__PURE__ */ jsx2("strong", {
742
+ children: () => sortedData().length
743
+ }),
744
+ " entries"
745
+ ]
746
+ }),
747
+ /* @__PURE__ */ jsxs2("div", {
748
+ style: { display: "flex", gap: "6px", alignItems: "center" },
749
+ children: [
750
+ /* @__PURE__ */ jsx2("button", {
751
+ type: "button",
752
+ disabled: () => currentPage() === 0,
753
+ onClick: () => setCurrentPage((p) => Math.max(0, p - 1)),
754
+ style: { padding: "4px 8px", cursor: "pointer" },
755
+ children: "Previous"
756
+ }),
757
+ /* @__PURE__ */ jsxs2("span", {
758
+ children: [
759
+ "Page ",
760
+ /* @__PURE__ */ jsx2("strong", {
761
+ children: () => currentPage() + 1
762
+ }),
763
+ " of ",
764
+ /* @__PURE__ */ jsx2("strong", {
765
+ children: totalPages
766
+ })
767
+ ]
768
+ }),
769
+ /* @__PURE__ */ jsx2("button", {
770
+ type: "button",
771
+ disabled: () => currentPage() >= totalPages() - 1,
772
+ onClick: () => setCurrentPage((p) => Math.min(totalPages() - 1, p + 1)),
773
+ style: { padding: "4px 8px", cursor: "pointer" },
774
+ children: "Next"
775
+ })
776
+ ]
777
+ })
778
+ ]
779
+ }) : null
780
+ ]
781
+ });
782
+ }
783
+ // src/ui/dialog.tsx
784
+ var DialogContext = createContext2(null);
785
+ function useDialogContext2() {
786
+ const ctx = useContext2(DialogContext);
787
+ return ctx;
788
+ }
789
+ function DialogRoot(props) {
790
+ const [open, setOpen] = createControllableSignal2({
791
+ value: props.open,
792
+ defaultValue: props.defaultOpen ?? false,
793
+ onChange: props.onOpenChange
794
+ });
795
+ const titleId = createId2("dialog-title");
796
+ const descriptionId = createId2("dialog-desc");
797
+ const contentRef = { current: null };
798
+ const ctx = {
799
+ open,
800
+ setOpen,
801
+ titleId,
802
+ descriptionId,
803
+ contentRef
804
+ };
805
+ if (props.trigger !== undefined || props.title !== undefined || props.description !== undefined) {
806
+ return DialogContext.Provider({
807
+ value: ctx,
808
+ children: () => /* @__PURE__ */ jsxs2(Fragment2, {
809
+ children: [
810
+ props.trigger ? /* @__PURE__ */ jsx2(DialogTrigger2, {
811
+ as: "span",
812
+ class: props.triggerClass,
813
+ children: typeof props.trigger === "function" ? props.trigger() : props.trigger
814
+ }) : null,
815
+ /* @__PURE__ */ jsx2(DialogOverlay2, {
816
+ class: props.overlayClass,
817
+ portalled: props.portalled
818
+ }),
819
+ /* @__PURE__ */ jsxs2(DialogContent2, {
820
+ portalled: props.portalled,
821
+ class: props.contentClass,
822
+ children: [
823
+ props.title ? /* @__PURE__ */ jsx2(DialogTitle2, {
824
+ class: props.titleClass,
825
+ children: typeof props.title === "function" ? props.title() : props.title
826
+ }) : null,
827
+ props.description ? /* @__PURE__ */ jsx2(DialogDescription2, {
828
+ class: props.descriptionClass,
829
+ children: typeof props.description === "function" ? props.description() : props.description
830
+ }) : null,
831
+ typeof props.children === "function" ? props.children() : props.children,
832
+ props.footer ? /* @__PURE__ */ jsx2("div", {
833
+ class: props.footerClass,
834
+ children: typeof props.footer === "function" ? props.footer() : props.footer
835
+ }) : null
836
+ ]
837
+ })
838
+ ]
839
+ })
840
+ });
841
+ }
842
+ return DialogContext.Provider({
843
+ value: ctx,
844
+ children: props.children
845
+ });
846
+ }
847
+ function DialogTrigger2(props) {
848
+ const ctx = useContext2(DialogContext);
849
+ const handleClick = (e) => {
850
+ if (props.onClick)
851
+ props.onClick(e);
852
+ if (!e.defaultPrevented && ctx) {
853
+ ctx.setOpen(true);
854
+ }
855
+ };
856
+ const { as: Tag = "button", children, onClick, class: className, style, ...rest } = props;
857
+ return /* @__PURE__ */ jsx2(Tag, {
858
+ type: Tag === "button" ? "button" : undefined,
859
+ "aria-haspopup": "dialog",
860
+ "aria-expanded": () => ctx?.open() ?? false,
861
+ "data-state": () => ctx?.open() ? "open" : "closed",
862
+ class: className,
863
+ style,
864
+ onClick: handleClick,
865
+ ...rest,
866
+ children
867
+ });
868
+ }
869
+ function DialogPortal2(props) {
870
+ return /* @__PURE__ */ jsx2(Portal2, {
871
+ mount: props.mount,
872
+ children: props.children
873
+ });
874
+ }
875
+ function DialogOverlay2(props) {
876
+ const ctx = useContext2(DialogContext);
877
+ const handleClick = (e) => {
878
+ if (props.onClick)
879
+ props.onClick(e);
880
+ if (!e.defaultPrevented && ctx) {
881
+ ctx.setOpen(false);
882
+ }
883
+ };
884
+ const {
885
+ as: Tag = "div",
886
+ children,
887
+ onClick,
888
+ class: className,
889
+ style,
890
+ portalled = true,
891
+ mount,
892
+ ...rest
893
+ } = props;
894
+ const overlay = /* @__PURE__ */ jsx2(Tag, {
895
+ "data-state": () => ctx?.open() ? "open" : "closed",
896
+ style: () => ({
897
+ display: ctx?.open() ? undefined : "none",
898
+ ...style
899
+ }),
900
+ class: className,
901
+ onClick: handleClick,
902
+ "aria-hidden": "true",
903
+ ...rest,
904
+ children
905
+ });
906
+ return portalled ? /* @__PURE__ */ jsx2(Portal2, {
907
+ mount,
908
+ children: overlay
909
+ }) : overlay;
910
+ }
911
+ function DialogContent2(props) {
912
+ const ctx = useContext2(DialogContext);
913
+ const handleKeyDown = (e) => {
914
+ if (props.onKeyDown)
915
+ props.onKeyDown(e);
916
+ if (e.defaultPrevented)
917
+ return;
918
+ if (e.key === "Escape") {
919
+ ctx?.setOpen(false);
920
+ e.stopPropagation();
921
+ } else if (e.key === "Tab" && ctx?.contentRef.current) {
922
+ trapFocus2(ctx.contentRef.current, e);
923
+ }
924
+ };
925
+ const handleClick = (e) => {
926
+ if (props.onClick)
927
+ props.onClick(e);
928
+ if (e.defaultPrevented)
929
+ return;
930
+ const target = e.target;
931
+ if (target?.closest("[data-dialog-close]")) {
932
+ ctx?.setOpen(false);
933
+ }
934
+ };
935
+ const {
936
+ as: Tag = "div",
937
+ children,
938
+ onKeyDown,
939
+ onClick: _onClick,
940
+ class: className,
941
+ style,
942
+ portalled = true,
943
+ mount,
944
+ ...rest
945
+ } = props;
946
+ const content = /* @__PURE__ */ jsx2(Tag, {
947
+ ref: (el) => {
948
+ if (ctx) {
949
+ ctx.contentRef.current = el;
950
+ if (el && ctx.open()) {
951
+ const focusable = el.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
952
+ if (focusable) {
953
+ setTimeout(() => focusable.focus(), 0);
954
+ }
955
+ }
956
+ }
957
+ },
958
+ role: "dialog",
959
+ "aria-modal": "true",
960
+ "aria-labelledby": ctx?.titleId,
961
+ "aria-describedby": ctx?.descriptionId,
962
+ "data-state": () => ctx?.open() ? "open" : "closed",
963
+ style: () => ({
964
+ display: ctx?.open() ? undefined : "none",
965
+ ...style
966
+ }),
967
+ class: className,
968
+ onKeyDown: handleKeyDown,
969
+ onClick: handleClick,
970
+ tabIndex: -1,
971
+ ...rest,
972
+ children
973
+ });
974
+ return portalled ? /* @__PURE__ */ jsx2(Portal2, {
975
+ mount,
976
+ children: content
977
+ }) : content;
978
+ }
979
+ function DialogTitle2(props) {
980
+ const ctx = useContext2(DialogContext);
981
+ const { as: Tag = "h2", children, class: className, style, ...rest } = props;
982
+ return /* @__PURE__ */ jsx2(Tag, {
983
+ id: ctx?.titleId,
984
+ class: className,
985
+ style,
986
+ ...rest,
987
+ children
988
+ });
989
+ }
990
+ function DialogDescription2(props) {
991
+ const ctx = useContext2(DialogContext);
992
+ const { as: Tag = "p", children, class: className, style, ...rest } = props;
993
+ return /* @__PURE__ */ jsx2(Tag, {
994
+ id: ctx?.descriptionId,
995
+ class: className,
996
+ style,
997
+ ...rest,
998
+ children
999
+ });
1000
+ }
1001
+ function DialogClose2(props) {
1002
+ const ctx = useContext2(DialogContext);
1003
+ const handleClick = (e) => {
1004
+ if (props.onClick)
1005
+ props.onClick(e);
1006
+ if (!e.defaultPrevented && ctx) {
1007
+ ctx.setOpen(false);
1008
+ }
1009
+ };
1010
+ const { as: Tag = "button", children, onClick, class: className, style, ...rest } = props;
1011
+ return /* @__PURE__ */ jsx2(Tag, {
1012
+ type: Tag === "button" ? "button" : undefined,
1013
+ "data-dialog-close": "",
1014
+ class: className,
1015
+ style,
1016
+ onClick: handleClick,
1017
+ ...rest,
1018
+ children
1019
+ });
1020
+ }
1021
+ var Dialog2 = Object.assign(DialogRoot, {
1022
+ Root: DialogRoot,
1023
+ Trigger: DialogTrigger2,
1024
+ Portal: DialogPortal2,
1025
+ Overlay: DialogOverlay2,
1026
+ Content: DialogContent2,
1027
+ Title: DialogTitle2,
1028
+ Description: DialogDescription2,
1029
+ Close: DialogClose2
1030
+ });
1031
+ // src/ui/dropdown-menu.tsx
1032
+ var DropdownMenuContext = createContext2(null);
1033
+ function useDropdownMenuContext2() {
1034
+ return useContext2(DropdownMenuContext);
1035
+ }
1036
+ function DropdownMenuRoot(props) {
1037
+ const [open, setOpen] = createControllableSignal2({
1038
+ value: props.open,
1039
+ defaultValue: props.defaultOpen ?? false,
1040
+ onChange: props.onOpenChange
1041
+ });
1042
+ const triggerRef = { current: null };
1043
+ const contentRef = { current: null };
1044
+ const menuId = createId2("dropdown-menu");
1045
+ const ctx = {
1046
+ open,
1047
+ setOpen,
1048
+ triggerRef,
1049
+ contentRef,
1050
+ menuId
1051
+ };
1052
+ if (props.trigger !== undefined || props.items !== undefined) {
1053
+ return DropdownMenuContext.Provider({
1054
+ value: ctx,
1055
+ children: () => /* @__PURE__ */ jsxs2("div", {
1056
+ class: "visage-dropdown-root",
1057
+ style: { position: "relative", display: "inline-block" },
1058
+ children: [
1059
+ props.trigger ? /* @__PURE__ */ jsx2(DropdownMenuTrigger2, {
1060
+ as: "span",
1061
+ class: props.triggerClass,
1062
+ children: typeof props.trigger === "function" ? props.trigger() : props.trigger
1063
+ }) : null,
1064
+ /* @__PURE__ */ jsxs2(DropdownMenuContent2, {
1065
+ portalled: props.portalled ?? false,
1066
+ class: props.contentClass,
1067
+ children: [
1068
+ props.items ? props.items.map((item) => {
1069
+ if (item.separator) {
1070
+ return /* @__PURE__ */ jsx2(DropdownMenuSeparator2, {
1071
+ class: item.class ?? props.separatorClass
1072
+ });
1073
+ }
1074
+ return /* @__PURE__ */ jsxs2(DropdownMenuItem2, {
1075
+ disabled: item.disabled,
1076
+ onSelect: item.onSelect,
1077
+ class: [props.itemClass, item.class, item.danger ? "danger" : ""].filter(Boolean).join(" "),
1078
+ children: [
1079
+ item.icon ? /* @__PURE__ */ jsx2("span", {
1080
+ class: "dropdown-item-icon",
1081
+ style: {
1082
+ display: "inline-flex",
1083
+ alignItems: "center",
1084
+ marginRight: "8px"
1085
+ },
1086
+ children: typeof item.icon === "function" ? item.icon() : item.icon
1087
+ }) : null,
1088
+ typeof item.label === "function" ? item.label() : item.label
1089
+ ]
1090
+ });
1091
+ }) : null,
1092
+ typeof props.children === "function" ? props.children() : props.children
1093
+ ]
1094
+ })
1095
+ ]
1096
+ })
1097
+ });
1098
+ }
1099
+ return DropdownMenuContext.Provider({
1100
+ value: ctx,
1101
+ children: props.children
1102
+ });
1103
+ }
1104
+ function DropdownMenuTrigger2(props) {
1105
+ const ctx = useContext2(DropdownMenuContext);
1106
+ const handleClick = (e) => {
1107
+ if (props.onClick)
1108
+ props.onClick(e);
1109
+ if (!e.defaultPrevented && ctx) {
1110
+ ctx.setOpen(!ctx.open());
1111
+ }
1112
+ };
1113
+ const handleKeyDown = (e) => {
1114
+ if (props.onKeyDown)
1115
+ props.onKeyDown(e);
1116
+ if (e.defaultPrevented)
1117
+ return;
1118
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
1119
+ if (ctx) {
1120
+ ctx.setOpen(true);
1121
+ e.preventDefault();
1122
+ setTimeout(() => {
1123
+ const firstItem = ctx.contentRef.current?.querySelector('[role="menuitem"]:not([disabled])');
1124
+ firstItem?.focus();
1125
+ }, 0);
1126
+ }
1127
+ }
1128
+ };
1129
+ const {
1130
+ as: Tag = "button",
1131
+ children,
1132
+ onClick,
1133
+ onKeyDown,
1134
+ class: className,
1135
+ style,
1136
+ ...rest
1137
+ } = props;
1138
+ return /* @__PURE__ */ jsx2(Tag, {
1139
+ ref: (el) => {
1140
+ if (ctx)
1141
+ ctx.triggerRef.current = el;
1142
+ },
1143
+ type: Tag === "button" ? "button" : undefined,
1144
+ "aria-haspopup": "menu",
1145
+ "aria-expanded": () => ctx?.open() ?? false,
1146
+ "aria-controls": ctx?.menuId,
1147
+ "data-state": () => ctx?.open() ? "open" : "closed",
1148
+ class: className,
1149
+ style,
1150
+ onClick: handleClick,
1151
+ onKeyDown: handleKeyDown,
1152
+ ...rest,
1153
+ children
1154
+ });
1155
+ }
1156
+ function DropdownMenuPortal2(props) {
1157
+ return /* @__PURE__ */ jsx2(Portal2, {
1158
+ mount: props.mount,
1159
+ children: props.children
1160
+ });
1161
+ }
1162
+ function DropdownMenuContent2(props) {
1163
+ const ctx = useContext2(DropdownMenuContext);
1164
+ const handleKeyDown = (e) => {
1165
+ if (props.onKeyDown)
1166
+ props.onKeyDown(e);
1167
+ if (e.defaultPrevented)
1168
+ return;
1169
+ const el = ctx?.contentRef.current;
1170
+ if (!el)
1171
+ return;
1172
+ const items = Array.from(el.querySelectorAll('[role="menuitem"]:not([disabled])'));
1173
+ const active = document.activeElement;
1174
+ const index = active ? items.indexOf(active) : -1;
1175
+ if (e.key === "ArrowDown") {
1176
+ const nextIndex = index === items.length - 1 || index === -1 ? 0 : index + 1;
1177
+ items[nextIndex]?.focus();
1178
+ e.preventDefault();
1179
+ } else if (e.key === "ArrowUp") {
1180
+ const nextIndex = index <= 0 ? items.length - 1 : index - 1;
1181
+ items[nextIndex]?.focus();
1182
+ e.preventDefault();
1183
+ } else if (e.key === "Home") {
1184
+ items[0]?.focus();
1185
+ e.preventDefault();
1186
+ } else if (e.key === "End") {
1187
+ items[items.length - 1]?.focus();
1188
+ e.preventDefault();
1189
+ } else if (e.key === "Escape") {
1190
+ if (ctx) {
1191
+ ctx.setOpen(false);
1192
+ ctx.triggerRef.current?.focus();
1193
+ }
1194
+ e.preventDefault();
1195
+ } else if (e.key === "Tab") {
1196
+ if (ctx)
1197
+ ctx.setOpen(false);
1198
+ }
1199
+ };
1200
+ const {
1201
+ as: Tag = "div",
1202
+ children,
1203
+ onKeyDown,
1204
+ class: className,
1205
+ style,
1206
+ portalled = true,
1207
+ mount,
1208
+ ...rest
1209
+ } = props;
1210
+ const content = /* @__PURE__ */ jsx2(Tag, {
1211
+ ref: (el) => {
1212
+ if (ctx) {
1213
+ ctx.contentRef.current = el;
1214
+ if (el) {
1215
+ const cleanup = onClickOutside2([ctx.triggerRef.current, el], () => {
1216
+ if (ctx.open()) {
1217
+ ctx.setOpen(false);
1218
+ }
1219
+ });
1220
+ onCleanup2(cleanup);
1221
+ }
1222
+ }
1223
+ },
1224
+ id: ctx?.menuId,
1225
+ role: "menu",
1226
+ "aria-orientation": "vertical",
1227
+ "data-state": () => ctx?.open() ? "open" : "closed",
1228
+ class: className,
1229
+ style: () => ({
1230
+ display: ctx?.open() ? undefined : "none",
1231
+ ...style
1232
+ }),
1233
+ onKeyDown: handleKeyDown,
1234
+ tabIndex: -1,
1235
+ ...rest,
1236
+ children
1237
+ });
1238
+ return portalled ? /* @__PURE__ */ jsx2(Portal2, {
1239
+ mount,
1240
+ children: content
1241
+ }) : content;
1242
+ }
1243
+ function DropdownMenuItem2(props) {
1244
+ const ctx = useContext2(DropdownMenuContext);
1245
+ const closeOnSelect = props.closeOnSelect ?? true;
1246
+ const handleSelect = (e) => {
1247
+ if (props.disabled)
1248
+ return;
1249
+ if (props.onSelect)
1250
+ props.onSelect(e);
1251
+ if (!e.defaultPrevented && closeOnSelect && ctx) {
1252
+ ctx.setOpen(false);
1253
+ ctx.triggerRef.current?.focus();
1254
+ }
1255
+ };
1256
+ const handleClick = (e) => {
1257
+ if (props.onClick)
1258
+ props.onClick(e);
1259
+ handleSelect(e);
1260
+ };
1261
+ const handleKeyDown = (e) => {
1262
+ if (props.onKeyDown)
1263
+ props.onKeyDown(e);
1264
+ if (e.key === "Enter" || e.key === " ") {
1265
+ e.preventDefault();
1266
+ handleSelect(e);
1267
+ }
1268
+ };
1269
+ const {
1270
+ disabled,
1271
+ closeOnSelect: _cos,
1272
+ as: Tag = "div",
1273
+ children,
1274
+ onClick,
1275
+ onKeyDown,
1276
+ onSelect: _os,
1277
+ class: className,
1278
+ style,
1279
+ ...rest
1280
+ } = props;
1281
+ return /* @__PURE__ */ jsx2(Tag, {
1282
+ role: "menuitem",
1283
+ tabIndex: -1,
1284
+ "aria-disabled": disabled,
1285
+ class: className,
1286
+ style,
1287
+ onClick: handleClick,
1288
+ onKeyDown: handleKeyDown,
1289
+ ...rest,
1290
+ children
1291
+ });
1292
+ }
1293
+ function DropdownMenuSeparator2(props) {
1294
+ const { as: Tag = "div", class: className, style, ...rest } = props;
1295
+ return /* @__PURE__ */ jsx2(Tag, {
1296
+ role: "separator",
1297
+ "aria-orientation": "horizontal",
1298
+ class: className,
1299
+ style,
1300
+ ...rest
1301
+ });
1302
+ }
1303
+ var DropdownMenu2 = Object.assign(DropdownMenuRoot, {
1304
+ Root: DropdownMenuRoot,
1305
+ Trigger: DropdownMenuTrigger2,
1306
+ Portal: DropdownMenuPortal2,
1307
+ Content: DropdownMenuContent2,
1308
+ Item: DropdownMenuItem2,
1309
+ Separator: DropdownMenuSeparator2
1310
+ });
1311
+ // src/ui/icon.tsx
1312
+ function createIcon2(name, iconNode) {
1313
+ const IconComponent = function Icon(props = {}) {
1314
+ const size = () => typeof props.size === "function" ? props.size() : props.size ?? 24;
1315
+ const color = () => typeof props.color === "function" ? props.color() : props.color ?? "currentColor";
1316
+ const strokeWidth = () => typeof props.strokeWidth === "function" ? props.strokeWidth() : props.strokeWidth ?? 2;
1317
+ const {
1318
+ size: _s,
1319
+ color: _c,
1320
+ strokeWidth: _sw,
1321
+ class: className,
1322
+ style,
1323
+ children,
1324
+ ...rest
1325
+ } = props;
1326
+ return /* @__PURE__ */ jsxs2("svg", {
1327
+ xmlns: "http://www.w3.org/2000/svg",
1328
+ width: size,
1329
+ height: size,
1330
+ viewBox: "0 0 24 24",
1331
+ fill: "none",
1332
+ stroke: color,
1333
+ "stroke-width": strokeWidth,
1334
+ "stroke-linecap": "round",
1335
+ "stroke-linejoin": "round",
1336
+ class: className,
1337
+ style,
1338
+ role: "img",
1339
+ "aria-label": props["aria-label"] ?? name,
1340
+ ...rest,
1341
+ children: [
1342
+ /* @__PURE__ */ jsx2("title", {
1343
+ children: props.title ?? name
1344
+ }),
1345
+ iconNode.map(([Tag, attrs]) => /* @__PURE__ */ jsx2(Tag, {
1346
+ ...attrs
1347
+ })),
1348
+ typeof children === "function" ? children() : children
1349
+ ]
1350
+ });
1351
+ };
1352
+ IconComponent.displayName = name;
1353
+ return IconComponent;
1354
+ }
1355
+ // src/ui/popover.tsx
1356
+ var PopoverContext = createContext2(null);
1357
+ function usePopoverContext2() {
1358
+ return useContext2(PopoverContext);
1359
+ }
1360
+ function PopoverRoot(props) {
1361
+ const [open, setOpen] = createControllableSignal2({
1362
+ value: props.open,
1363
+ defaultValue: props.defaultOpen ?? false,
1364
+ onChange: props.onOpenChange
1365
+ });
1366
+ const triggerRef = { current: null };
1367
+ const contentRef = { current: null };
1368
+ const contentId = createId2("popover-content");
1369
+ const ctx = {
1370
+ open,
1371
+ setOpen,
1372
+ triggerRef,
1373
+ contentRef,
1374
+ contentId
1375
+ };
1376
+ if (props.trigger !== undefined || props.content !== undefined) {
1377
+ return PopoverContext.Provider({
1378
+ value: ctx,
1379
+ children: () => /* @__PURE__ */ jsxs2(Fragment2, {
1380
+ children: [
1381
+ props.trigger ? /* @__PURE__ */ jsx2(PopoverTrigger2, {
1382
+ as: "span",
1383
+ class: props.triggerClass,
1384
+ children: typeof props.trigger === "function" ? props.trigger() : props.trigger
1385
+ }) : null,
1386
+ /* @__PURE__ */ jsxs2(PopoverContent2, {
1387
+ portalled: props.portalled,
1388
+ class: props.contentClass,
1389
+ children: [
1390
+ typeof props.content === "function" ? props.content() : props.content,
1391
+ typeof props.children === "function" ? props.children() : props.children
1392
+ ]
1393
+ })
1394
+ ]
1395
+ })
1396
+ });
1397
+ }
1398
+ return PopoverContext.Provider({
1399
+ value: ctx,
1400
+ children: props.children
1401
+ });
1402
+ }
1403
+ function PopoverTrigger2(props) {
1404
+ const ctx = useContext2(PopoverContext);
1405
+ const handleClick = (e) => {
1406
+ if (props.onClick)
1407
+ props.onClick(e);
1408
+ if (!e.defaultPrevented && ctx) {
1409
+ ctx.setOpen(!ctx.open());
1410
+ }
1411
+ };
1412
+ const { as: Tag = "button", children, onClick, class: className, style, ...rest } = props;
1413
+ return /* @__PURE__ */ jsx2(Tag, {
1414
+ ref: (el) => {
1415
+ if (ctx)
1416
+ ctx.triggerRef.current = el;
1417
+ },
1418
+ type: Tag === "button" ? "button" : undefined,
1419
+ "aria-haspopup": "dialog",
1420
+ "aria-expanded": () => ctx?.open() ?? false,
1421
+ "aria-controls": ctx?.contentId,
1422
+ "data-state": () => ctx?.open() ? "open" : "closed",
1423
+ class: className,
1424
+ style,
1425
+ onClick: handleClick,
1426
+ ...rest,
1427
+ children
1428
+ });
1429
+ }
1430
+ function PopoverPortal2(props) {
1431
+ return /* @__PURE__ */ jsx2(Portal2, {
1432
+ mount: props.mount,
1433
+ children: props.children
1434
+ });
1435
+ }
1436
+ function PopoverContent2(props) {
1437
+ const ctx = useContext2(PopoverContext);
1438
+ const handleKeyDown = (e) => {
1439
+ if (props.onKeyDown)
1440
+ props.onKeyDown(e);
1441
+ if (e.defaultPrevented)
1442
+ return;
1443
+ if (e.key === "Escape" && ctx) {
1444
+ ctx.setOpen(false);
1445
+ ctx.triggerRef.current?.focus();
1446
+ e.stopPropagation();
1447
+ }
1448
+ };
1449
+ const handleClick = (e) => {
1450
+ if (props.onClick)
1451
+ props.onClick(e);
1452
+ if (e.defaultPrevented)
1453
+ return;
1454
+ const target = e.target;
1455
+ if (target?.closest("[data-popover-close]")) {
1456
+ ctx?.setOpen(false);
1457
+ }
1458
+ };
1459
+ const {
1460
+ as: Tag = "div",
1461
+ children,
1462
+ onKeyDown,
1463
+ onClick: _onClick,
1464
+ class: className,
1465
+ style,
1466
+ portalled = true,
1467
+ mount,
1468
+ ...rest
1469
+ } = props;
1470
+ const content = /* @__PURE__ */ jsx2(Tag, {
1471
+ ref: (el) => {
1472
+ if (ctx) {
1473
+ ctx.contentRef.current = el;
1474
+ if (el) {
1475
+ const cleanup = onClickOutside2([ctx.triggerRef.current, el], () => {
1476
+ if (ctx.open()) {
1477
+ ctx.setOpen(false);
1478
+ }
1479
+ });
1480
+ onCleanup2(cleanup);
1481
+ }
1482
+ }
1483
+ },
1484
+ id: ctx?.contentId,
1485
+ role: "dialog",
1486
+ "data-state": () => ctx?.open() ? "open" : "closed",
1487
+ class: className,
1488
+ style: () => ({
1489
+ display: ctx?.open() ? undefined : "none",
1490
+ ...style
1491
+ }),
1492
+ onKeyDown: handleKeyDown,
1493
+ onClick: handleClick,
1494
+ tabIndex: -1,
1495
+ ...rest,
1496
+ children
1497
+ });
1498
+ return portalled ? /* @__PURE__ */ jsx2(Portal2, {
1499
+ mount,
1500
+ children: content
1501
+ }) : content;
1502
+ }
1503
+ function PopoverClose2(props) {
1504
+ const ctx = useContext2(PopoverContext);
1505
+ const handleClick = (e) => {
1506
+ if (props.onClick)
1507
+ props.onClick(e);
1508
+ if (!e.defaultPrevented && ctx) {
1509
+ ctx.setOpen(false);
1510
+ }
1511
+ };
1512
+ const { as: Tag = "button", children, onClick, class: className, style, ...rest } = props;
1513
+ return /* @__PURE__ */ jsx2(Tag, {
1514
+ type: Tag === "button" ? "button" : undefined,
1515
+ "data-popover-close": "",
1516
+ class: className,
1517
+ style,
1518
+ onClick: handleClick,
1519
+ ...rest,
1520
+ children
1521
+ });
1522
+ }
1523
+ var Popover2 = Object.assign(PopoverRoot, {
1524
+ Root: PopoverRoot,
1525
+ Trigger: PopoverTrigger2,
1526
+ Portal: PopoverPortal2,
1527
+ Content: PopoverContent2,
1528
+ Close: PopoverClose2
1529
+ });
1530
+ // src/ui/toast.tsx
1531
+ function createToastManager2() {
1532
+ const toasts = signal2([]);
1533
+ const dismiss = (id) => {
1534
+ const item = toasts().find((t) => t.id === id);
1535
+ if (item?.onDismiss)
1536
+ item.onDismiss();
1537
+ toasts.update((list) => list.filter((t) => t.id !== id));
1538
+ };
1539
+ const show = (options) => {
1540
+ const id = createId2("toast");
1541
+ const normalized = typeof options === "string" ? { title: options } : options;
1542
+ const data = {
1543
+ id,
1544
+ title: normalized.title,
1545
+ description: normalized.description,
1546
+ variant: normalized.variant ?? "default",
1547
+ duration: normalized.duration ?? 5000,
1548
+ onDismiss: normalized.onDismiss
1549
+ };
1550
+ toasts.update((list) => [...list, data]);
1551
+ if (data.duration && data.duration > 0) {
1552
+ setTimeout(() => {
1553
+ dismiss(id);
1554
+ }, data.duration);
1555
+ }
1556
+ return id;
1557
+ };
1558
+ const success = (title, options) => show({ ...options, title, variant: "success" });
1559
+ const error = (title, options) => show({ ...options, title, variant: "error" });
1560
+ const warning = (title, options) => show({ ...options, title, variant: "warning" });
1561
+ const info = (title, options) => show({ ...options, title, variant: "info" });
1562
+ const clear = () => {
1563
+ toasts.set([]);
1564
+ };
1565
+ const fn = function toast(options) {
1566
+ return show(options);
1567
+ };
1568
+ fn.toasts = toasts;
1569
+ fn.show = show;
1570
+ fn.success = success;
1571
+ fn.error = error;
1572
+ fn.warning = warning;
1573
+ fn.info = info;
1574
+ fn.dismiss = dismiss;
1575
+ fn.clear = clear;
1576
+ return fn;
1577
+ }
1578
+ var defaultToastManager2 = createToastManager2();
1579
+ var toast2 = defaultToastManager2;
1580
+ var ToastContext = createContext2(defaultToastManager2);
1581
+ function useToast2() {
1582
+ return useContext2(ToastContext) ?? defaultToastManager2;
1583
+ }
1584
+ function ToastProvider2(props) {
1585
+ const manager = props.manager ?? defaultToastManager2;
1586
+ return ToastContext.Provider({
1587
+ value: manager,
1588
+ children: props.children
1589
+ });
1590
+ }
1591
+ function ToastViewport2(props) {
1592
+ const manager = useToast2();
1593
+ const { as: Tag = "section", children, class: className, style, ...rest } = props;
1594
+ const defaultRender = (t) => /* @__PURE__ */ jsxs2(Toast2, {
1595
+ id: t.id,
1596
+ variant: t.variant,
1597
+ class: `visage-toast toast toast-${t.variant ?? "default"}`,
1598
+ onDismiss: () => manager.dismiss(t.id),
1599
+ children: [
1600
+ /* @__PURE__ */ jsxs2("div", {
1601
+ class: "visage-toast-content",
1602
+ children: [
1603
+ t.title && /* @__PURE__ */ jsx2(ToastTitle2, {
1604
+ class: "visage-toast-title",
1605
+ children: t.title
1606
+ }),
1607
+ t.description && /* @__PURE__ */ jsx2(ToastDescription2, {
1608
+ class: "visage-toast-description",
1609
+ children: t.description
1610
+ })
1611
+ ]
1612
+ }),
1613
+ /* @__PURE__ */ jsx2(ToastClose2, {
1614
+ class: "visage-toast-close",
1615
+ onClick: () => manager.dismiss(t.id)
1616
+ })
1617
+ ]
1618
+ });
1619
+ const renderItem = children ?? defaultRender;
1620
+ return /* @__PURE__ */ jsx2(Tag, {
1621
+ role: "region",
1622
+ "aria-label": "Notifications",
1623
+ class: className,
1624
+ style,
1625
+ tabIndex: -1,
1626
+ ...rest,
1627
+ children: /* @__PURE__ */ jsx2(For2, {
1628
+ each: manager.toasts,
1629
+ children: (t) => renderItem(t)
1630
+ })
1631
+ });
1632
+ }
1633
+ function ToastRoot(props) {
1634
+ const {
1635
+ id,
1636
+ variant = "default",
1637
+ onDismiss,
1638
+ as: Tag = "div",
1639
+ children,
1640
+ class: className,
1641
+ style,
1642
+ ...rest
1643
+ } = props;
1644
+ const role = variant === "error" ? "alert" : "status";
1645
+ const ariaLive = variant === "error" ? "assertive" : "polite";
1646
+ return /* @__PURE__ */ jsx2(Tag, {
1647
+ id,
1648
+ role,
1649
+ "aria-live": ariaLive,
1650
+ "data-variant": variant,
1651
+ "data-state": "open",
1652
+ class: className,
1653
+ style,
1654
+ ...rest,
1655
+ children
1656
+ });
1657
+ }
1658
+ function ToastTitle2(props) {
1659
+ const { as: Tag = "div", children, class: className, style, ...rest } = props;
1660
+ return /* @__PURE__ */ jsx2(Tag, {
1661
+ class: className,
1662
+ style,
1663
+ ...rest,
1664
+ children
1665
+ });
1666
+ }
1667
+ function ToastDescription2(props) {
1668
+ const { as: Tag = "div", children, class: className, style, ...rest } = props;
1669
+ return /* @__PURE__ */ jsx2(Tag, {
1670
+ class: className,
1671
+ style,
1672
+ ...rest,
1673
+ children
1674
+ });
1675
+ }
1676
+ function ToastClose2(props) {
1677
+ const { as: Tag = "button", children = "✕", onClick, class: className, style, ...rest } = props;
1678
+ return /* @__PURE__ */ jsx2(Tag, {
1679
+ type: Tag === "button" ? "button" : undefined,
1680
+ "aria-label": "Close notification",
1681
+ class: className,
1682
+ style,
1683
+ onClick,
1684
+ ...rest,
1685
+ children
1686
+ });
1687
+ }
1688
+ var Toast2 = Object.assign(ToastRoot, {
1689
+ Root: ToastRoot,
1690
+ Provider: ToastProvider2,
1691
+ Viewport: ToastViewport2,
1692
+ Title: ToastTitle2,
1693
+ Description: ToastDescription2,
1694
+ Close: ToastClose2
1695
+ });
1696
+ export { useAccordionContext2, useAccordionItemContext2, AccordionItem2, AccordionTrigger2, AccordionContent2, Accordion2, CommandDialog2, useCommandContext2, CommandInput2, CommandList2, CommandEmpty2, CommandGroup2, CommandItem2, Command2, DataTable2, useDialogContext2, DialogTrigger2, DialogPortal2, DialogOverlay2, DialogContent2, DialogTitle2, DialogDescription2, DialogClose2, Dialog2, useDropdownMenuContext2, DropdownMenuTrigger2, DropdownMenuPortal2, DropdownMenuContent2, DropdownMenuItem2, DropdownMenuSeparator2, DropdownMenu2, createIcon2, usePopoverContext2, PopoverTrigger2, PopoverPortal2, PopoverContent2, PopoverClose2, Popover2, createToastManager2, defaultToastManager2, toast2, useToast2, ToastProvider2, ToastViewport2, ToastTitle2, ToastDescription2, ToastClose2, Toast2 };