@kuraykaraaslan/kui-react 1.0.1

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 (47) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +168 -0
  3. package/dist/AdvancedDataTable-F3DNXDKX.mjs +11 -0
  4. package/dist/DataTable-2G27T4E6.mjs +11 -0
  5. package/dist/DateRangePicker-AL32QB6L.mjs +11 -0
  6. package/dist/DropdownMenu-f5yV9dzM.d.mts +22 -0
  7. package/dist/DropdownMenu-f5yV9dzM.d.ts +22 -0
  8. package/dist/MapView-FERKPCDB.mjs +10 -0
  9. package/dist/ServerDataTable-RZV3K6KQ.mjs +11 -0
  10. package/dist/Tooltip-Bof5GvOc.d.mts +248 -0
  11. package/dist/Tooltip-Bof5GvOc.d.ts +248 -0
  12. package/dist/VideoPlayer-P3I6ESXJ.mjs +9 -0
  13. package/dist/app.d.mts +620 -0
  14. package/dist/app.d.ts +620 -0
  15. package/dist/app.js +7061 -0
  16. package/dist/app.mjs +100 -0
  17. package/dist/chunk-24BCQSLI.mjs +1 -0
  18. package/dist/chunk-45I3EDB2.mjs +90 -0
  19. package/dist/chunk-4IWCD7ID.mjs +1450 -0
  20. package/dist/chunk-5E2HXWFI.mjs +105 -0
  21. package/dist/chunk-C7AYI4XM.mjs +402 -0
  22. package/dist/chunk-J4D44TUA.mjs +1267 -0
  23. package/dist/chunk-KTEWZKNE.mjs +1020 -0
  24. package/dist/chunk-LMUQHL4Z.mjs +3829 -0
  25. package/dist/chunk-MD5OQ4J2.mjs +527 -0
  26. package/dist/chunk-MPJRPYIZ.mjs +1 -0
  27. package/dist/chunk-MPWUEQ7J.mjs +2422 -0
  28. package/dist/chunk-MTT5TKAJ.mjs +93 -0
  29. package/dist/chunk-RBDK7MWQ.mjs +46 -0
  30. package/dist/chunk-SVFQZPNZ.mjs +3648 -0
  31. package/dist/chunk-TZWBBMSG.mjs +1 -0
  32. package/dist/chunk-XA7J6PVJ.mjs +1488 -0
  33. package/dist/chunk-ZLYBRYWQ.mjs +726 -0
  34. package/dist/common.d.mts +921 -0
  35. package/dist/common.d.ts +921 -0
  36. package/dist/common.js +4991 -0
  37. package/dist/common.mjs +172 -0
  38. package/dist/index.d.mts +10 -0
  39. package/dist/index.d.ts +10 -0
  40. package/dist/index.js +17563 -0
  41. package/dist/index.mjs +349 -0
  42. package/dist/ui.d.mts +937 -0
  43. package/dist/ui.d.ts +937 -0
  44. package/dist/ui.js +10095 -0
  45. package/dist/ui.mjs +163 -0
  46. package/package.json +114 -0
  47. package/styles/index.css +129 -0
@@ -0,0 +1,1450 @@
1
+ "use client";
2
+ import {
3
+ SearchBar,
4
+ Spinner
5
+ } from "./chunk-5E2HXWFI.mjs";
6
+ import {
7
+ __spreadProps,
8
+ __spreadValues,
9
+ cn
10
+ } from "./chunk-RBDK7MWQ.mjs";
11
+
12
+ // modules/ui/Table/Table.tsx
13
+ import { useState } from "react";
14
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
15
+ import { faChevronUp, faChevronDown, faSort } from "@fortawesome/free-solid-svg-icons";
16
+
17
+ // modules/ui/Table/core/columnHelpers.ts
18
+ var alignClass = {
19
+ left: "text-left",
20
+ center: "text-center",
21
+ right: "text-right"
22
+ };
23
+ function headerClassFor(col, base) {
24
+ const parts = [base];
25
+ if (col.align === "center") parts.push(alignClass.center);
26
+ else if (col.align === "right") parts.push(alignClass.right);
27
+ else parts.push(alignClass.left);
28
+ if (col.thClass) parts.push(col.thClass);
29
+ return parts.join(" ");
30
+ }
31
+ function cellClassFor(col, base) {
32
+ const parts = [base];
33
+ if (col.align === "center") parts.push(alignClass.center);
34
+ else if (col.align === "right") parts.push(alignClass.right);
35
+ if (col.tdClass) parts.push(col.tdClass);
36
+ return parts.join(" ");
37
+ }
38
+
39
+ // modules/ui/Table/Table.tsx
40
+ import { jsx, jsxs } from "react/jsx-runtime";
41
+ function Table({
42
+ columns,
43
+ rows,
44
+ caption,
45
+ emptyMessage = "No results found.",
46
+ defaultSortKey,
47
+ defaultSortDir,
48
+ className
49
+ }) {
50
+ const [sortKey, setSortKey] = useState(defaultSortKey != null ? defaultSortKey : "");
51
+ const [sortDir, setSortDir] = useState(
52
+ defaultSortDir != null ? defaultSortDir : null
53
+ );
54
+ function handleSort(key) {
55
+ if (sortKey !== key) {
56
+ setSortKey(key);
57
+ setSortDir("asc");
58
+ return;
59
+ }
60
+ if (sortDir === "asc") {
61
+ setSortDir("desc");
62
+ return;
63
+ }
64
+ setSortDir(null);
65
+ setSortKey("");
66
+ }
67
+ const sorted = sortKey && sortDir ? [...rows].sort((a, b) => {
68
+ var _a, _b;
69
+ const av = (_a = a[sortKey]) != null ? _a : "";
70
+ const bv = (_b = b[sortKey]) != null ? _b : "";
71
+ const cmp = String(av).localeCompare(String(bv), void 0, {
72
+ numeric: true
73
+ });
74
+ return sortDir === "asc" ? cmp : -cmp;
75
+ }) : rows;
76
+ return /* @__PURE__ */ jsx(
77
+ "div",
78
+ {
79
+ className: cn(
80
+ "w-full overflow-x-auto rounded-lg border border-border",
81
+ className
82
+ ),
83
+ children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
84
+ caption && /* @__PURE__ */ jsx("caption", { className: "sr-only", children: caption }),
85
+ /* @__PURE__ */ jsx("thead", { className: "bg-surface-sunken border-b border-border", children: /* @__PURE__ */ jsx("tr", { children: columns.map((col) => {
86
+ const isSorted = sortKey === String(col.key);
87
+ const dir = isSorted ? sortDir : null;
88
+ return /* @__PURE__ */ jsx(
89
+ "th",
90
+ {
91
+ scope: "col",
92
+ "aria-sort": col.sortable ? isSorted && dir === "asc" ? "ascending" : isSorted && dir === "desc" ? "descending" : "none" : void 0,
93
+ className: cn(
94
+ headerClassFor(
95
+ col,
96
+ "px-4 py-3 text-xs font-semibold text-text-secondary uppercase tracking-wider"
97
+ ),
98
+ col.sortable && "cursor-pointer select-none hover:text-text-primary transition-colors"
99
+ ),
100
+ onClick: col.sortable ? () => handleSort(String(col.key)) : void 0,
101
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
102
+ col.header,
103
+ col.sortable && /* @__PURE__ */ jsx(
104
+ FontAwesomeIcon,
105
+ {
106
+ icon: dir === "asc" ? faChevronUp : dir === "desc" ? faChevronDown : faSort,
107
+ className: "w-2.5 h-2.5",
108
+ "aria-hidden": "true"
109
+ }
110
+ )
111
+ ] })
112
+ },
113
+ String(col.key)
114
+ );
115
+ }) }) }),
116
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-border bg-surface-base", children: sorted.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
117
+ "td",
118
+ {
119
+ colSpan: columns.length,
120
+ className: "px-4 py-8 text-center text-text-secondary",
121
+ children: emptyMessage
122
+ }
123
+ ) }) : sorted.map((row, i) => /* @__PURE__ */ jsx(
124
+ "tr",
125
+ {
126
+ className: "hover:bg-surface-overlay transition-colors",
127
+ children: columns.map((col) => {
128
+ var _a;
129
+ return /* @__PURE__ */ jsx(
130
+ "td",
131
+ {
132
+ className: cellClassFor(col, "px-4 py-3 text-text-primary"),
133
+ children: col.render ? col.render(row) : String((_a = row[col.key]) != null ? _a : "")
134
+ },
135
+ String(col.key)
136
+ );
137
+ })
138
+ },
139
+ i
140
+ )) })
141
+ ] })
142
+ }
143
+ );
144
+ }
145
+
146
+ // modules/ui/Pagination.tsx
147
+ import { useState as useState2 } from "react";
148
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
149
+ var sizeMap = {
150
+ sm: { page: "w-7 h-7 text-xs", nav: "px-2 py-1 text-xs" },
151
+ md: { page: "w-9 h-9 text-sm", nav: "px-3 py-1.5 text-sm" },
152
+ lg: { page: "w-10 h-10 text-base", nav: "px-4 py-2 text-base" }
153
+ };
154
+ function Pagination({
155
+ page,
156
+ totalPages,
157
+ onPageChange,
158
+ size = "md",
159
+ showFirstLast = false,
160
+ showJumpTo = false,
161
+ className
162
+ }) {
163
+ const [jumpValue, setJumpValue] = useState2("");
164
+ const s = sizeMap[size];
165
+ const pages = Array.from({ length: totalPages }, (_, i) => i + 1);
166
+ const visiblePages = pages.filter(
167
+ (p) => p === 1 || p === totalPages || Math.abs(p - page) <= 1
168
+ );
169
+ const withEllipsis = [];
170
+ let prev = null;
171
+ for (const p of visiblePages) {
172
+ if (prev !== null && p - prev > 1) withEllipsis.push("ellipsis");
173
+ withEllipsis.push(p);
174
+ prev = p;
175
+ }
176
+ function navBtnClass(disabled) {
177
+ return cn(
178
+ "rounded-md font-medium border transition-colors",
179
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
180
+ s.nav,
181
+ disabled ? "border-border text-text-disabled cursor-not-allowed opacity-50" : "border-border text-text-secondary hover:bg-surface-overlay hover:text-text-primary"
182
+ );
183
+ }
184
+ function handleJump(e) {
185
+ e.preventDefault();
186
+ const n = parseInt(jumpValue, 10);
187
+ if (!isNaN(n) && n >= 1 && n <= totalPages) {
188
+ onPageChange(n);
189
+ setJumpValue("");
190
+ }
191
+ }
192
+ return /* @__PURE__ */ jsxs2("nav", { "aria-label": "Pagination", className: cn("flex items-center gap-1 flex-wrap", className), children: [
193
+ showFirstLast && /* @__PURE__ */ jsx2(
194
+ "button",
195
+ {
196
+ type: "button",
197
+ onClick: () => onPageChange(1),
198
+ disabled: page <= 1,
199
+ "aria-label": "First page",
200
+ className: navBtnClass(page <= 1),
201
+ children: "\xAB"
202
+ }
203
+ ),
204
+ /* @__PURE__ */ jsx2(
205
+ "button",
206
+ {
207
+ type: "button",
208
+ onClick: () => onPageChange(page - 1),
209
+ disabled: page <= 1,
210
+ "aria-label": "Previous page",
211
+ className: navBtnClass(page <= 1),
212
+ children: "\u2039"
213
+ }
214
+ ),
215
+ withEllipsis.map(
216
+ (item, i) => item === "ellipsis" ? /* @__PURE__ */ jsx2("span", { className: cn("text-text-disabled", s.nav), children: "\u2026" }, `e-${i}`) : /* @__PURE__ */ jsx2(
217
+ "button",
218
+ {
219
+ type: "button",
220
+ onClick: () => onPageChange(item),
221
+ "aria-label": `Page ${item}`,
222
+ "aria-current": item === page ? "page" : void 0,
223
+ className: cn(
224
+ "rounded-md font-medium border transition-colors",
225
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
226
+ s.page,
227
+ item === page ? "bg-primary text-primary-fg border-primary" : "border-border text-text-secondary hover:bg-surface-overlay hover:text-text-primary"
228
+ ),
229
+ children: item
230
+ },
231
+ item
232
+ )
233
+ ),
234
+ /* @__PURE__ */ jsx2(
235
+ "button",
236
+ {
237
+ type: "button",
238
+ onClick: () => onPageChange(page + 1),
239
+ disabled: page >= totalPages,
240
+ "aria-label": "Next page",
241
+ className: navBtnClass(page >= totalPages),
242
+ children: "\u203A"
243
+ }
244
+ ),
245
+ showFirstLast && /* @__PURE__ */ jsx2(
246
+ "button",
247
+ {
248
+ type: "button",
249
+ onClick: () => onPageChange(totalPages),
250
+ disabled: page >= totalPages,
251
+ "aria-label": "Last page",
252
+ className: navBtnClass(page >= totalPages),
253
+ children: "\xBB"
254
+ }
255
+ ),
256
+ showJumpTo && /* @__PURE__ */ jsxs2("form", { onSubmit: handleJump, className: "flex items-center gap-1.5 ml-2", children: [
257
+ /* @__PURE__ */ jsx2("label", { htmlFor: "pagination-jump", className: "text-xs text-text-secondary whitespace-nowrap", children: "Go to" }),
258
+ /* @__PURE__ */ jsx2(
259
+ "input",
260
+ {
261
+ id: "pagination-jump",
262
+ type: "number",
263
+ min: 1,
264
+ max: totalPages,
265
+ value: jumpValue,
266
+ onChange: (e) => setJumpValue(e.target.value),
267
+ "aria-label": `Jump to page, 1\u2013${totalPages}`,
268
+ className: cn(
269
+ "w-14 rounded-md border border-border bg-surface-base text-center text-sm text-text-primary",
270
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
271
+ "py-1 px-1"
272
+ )
273
+ }
274
+ ),
275
+ /* @__PURE__ */ jsx2(
276
+ "button",
277
+ {
278
+ type: "submit",
279
+ className: cn(
280
+ "rounded-md border border-border text-sm font-medium transition-colors",
281
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
282
+ "px-2 py-1 text-text-secondary hover:bg-surface-overlay hover:text-text-primary"
283
+ ),
284
+ children: "Go"
285
+ }
286
+ )
287
+ ] })
288
+ ] });
289
+ }
290
+
291
+ // modules/ui/Table/DataTable.tsx
292
+ import { useId, useState as useState6 } from "react";
293
+
294
+ // modules/ui/Table/parts/HeaderCell.tsx
295
+ import { FontAwesomeIcon as FontAwesomeIcon3 } from "@fortawesome/react-fontawesome";
296
+ import {
297
+ faChevronUp as faChevronUp2,
298
+ faChevronDown as faChevronDown2,
299
+ faSort as faSort2
300
+ } from "@fortawesome/free-solid-svg-icons";
301
+
302
+ // modules/ui/Table/parts/FilterPopover.tsx
303
+ import { useEffect, useRef, useState as useState3 } from "react";
304
+ import { FontAwesomeIcon as FontAwesomeIcon2 } from "@fortawesome/react-fontawesome";
305
+ import { faFilter } from "@fortawesome/free-solid-svg-icons";
306
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
307
+ function FilterPopover({
308
+ column,
309
+ value,
310
+ onChange
311
+ }) {
312
+ var _a, _b, _c, _d, _e;
313
+ const [open, setOpen] = useState3(false);
314
+ const wrapperRef = useRef(null);
315
+ const [draft, setDraft] = useState3(value);
316
+ useEffect(() => {
317
+ setDraft(value);
318
+ }, [value]);
319
+ useEffect(() => {
320
+ if (!open) return;
321
+ function onOutside(e) {
322
+ if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
323
+ setOpen(false);
324
+ }
325
+ }
326
+ function onKey(e) {
327
+ if (e.key === "Escape") setOpen(false);
328
+ }
329
+ document.addEventListener("mousedown", onOutside);
330
+ document.addEventListener("keydown", onKey);
331
+ return () => {
332
+ document.removeEventListener("mousedown", onOutside);
333
+ document.removeEventListener("keydown", onKey);
334
+ };
335
+ }, [open]);
336
+ const active = !!value;
337
+ function apply() {
338
+ onChange(draft.trim());
339
+ setOpen(false);
340
+ }
341
+ function clear() {
342
+ setDraft("");
343
+ onChange("");
344
+ setOpen(false);
345
+ }
346
+ return /* @__PURE__ */ jsxs3("span", { ref: wrapperRef, className: "relative inline-flex", children: [
347
+ /* @__PURE__ */ jsx3(
348
+ "button",
349
+ {
350
+ type: "button",
351
+ "aria-label": active ? `Edit filter on ${column.header}` : `Filter ${column.header}`,
352
+ "aria-expanded": open,
353
+ onClick: (e) => {
354
+ e.stopPropagation();
355
+ setOpen((o) => !o);
356
+ },
357
+ className: cn(
358
+ "inline-flex items-center justify-center rounded p-1 transition-colors",
359
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
360
+ active ? "text-primary hover:text-primary-hover" : "text-text-disabled hover:text-text-primary"
361
+ ),
362
+ children: /* @__PURE__ */ jsx3(FontAwesomeIcon2, { icon: faFilter, className: "w-2.5 h-2.5", "aria-hidden": "true" })
363
+ }
364
+ ),
365
+ open && /* @__PURE__ */ jsxs3(
366
+ "div",
367
+ {
368
+ role: "dialog",
369
+ "aria-label": `Filter ${column.header}`,
370
+ onClick: (e) => e.stopPropagation(),
371
+ className: cn(
372
+ "absolute top-full left-0 mt-1 z-[70] min-w-[12rem] rounded-lg border border-border bg-surface-raised shadow-xl p-3"
373
+ ),
374
+ children: [
375
+ ((_a = column.filter) == null ? void 0 : _a.kind) === "select" ? /* @__PURE__ */ jsxs3(
376
+ "select",
377
+ {
378
+ autoFocus: true,
379
+ value: draft,
380
+ onChange: (e) => setDraft(e.target.value),
381
+ className: cn(
382
+ "w-full rounded-md border border-border bg-surface-base px-2 py-1.5 text-sm text-text-primary",
383
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus"
384
+ ),
385
+ children: [
386
+ /* @__PURE__ */ jsx3("option", { value: "", children: "All" }),
387
+ ((_c = (_b = column.filter) == null ? void 0 : _b.options) != null ? _c : []).map((opt) => /* @__PURE__ */ jsx3("option", { value: opt.value, children: opt.label }, opt.value))
388
+ ]
389
+ }
390
+ ) : /* @__PURE__ */ jsx3(
391
+ "input",
392
+ {
393
+ autoFocus: true,
394
+ type: "text",
395
+ value: draft,
396
+ placeholder: (_e = (_d = column.filter) == null ? void 0 : _d.placeholder) != null ? _e : "Contains\u2026",
397
+ onChange: (e) => setDraft(e.target.value),
398
+ onKeyDown: (e) => {
399
+ if (e.key === "Enter") apply();
400
+ },
401
+ className: cn(
402
+ "w-full rounded-md border border-border bg-surface-base px-2 py-1.5 text-sm text-text-primary",
403
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus"
404
+ )
405
+ }
406
+ ),
407
+ /* @__PURE__ */ jsxs3("div", { className: "mt-2 flex items-center justify-end gap-2", children: [
408
+ /* @__PURE__ */ jsx3(
409
+ "button",
410
+ {
411
+ type: "button",
412
+ onClick: clear,
413
+ className: cn(
414
+ "rounded-md px-2 py-1 text-xs font-medium text-text-secondary",
415
+ "hover:bg-surface-overlay focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus"
416
+ ),
417
+ children: "Clear"
418
+ }
419
+ ),
420
+ /* @__PURE__ */ jsx3(
421
+ "button",
422
+ {
423
+ type: "button",
424
+ onClick: apply,
425
+ className: cn(
426
+ "rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-fg",
427
+ "hover:bg-primary-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus"
428
+ ),
429
+ children: "Apply"
430
+ }
431
+ )
432
+ ] })
433
+ ]
434
+ }
435
+ )
436
+ ] });
437
+ }
438
+
439
+ // modules/ui/Table/parts/HeaderCell.tsx
440
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
441
+ function HeaderCell({
442
+ column,
443
+ sort,
444
+ filterValue,
445
+ onToggleSort,
446
+ onSetFilter
447
+ }) {
448
+ var _a;
449
+ const key = String(column.key);
450
+ const sortEntry = sort.find((s) => s.key === key);
451
+ const dir = (_a = sortEntry == null ? void 0 : sortEntry.dir) != null ? _a : null;
452
+ const ariaSort = column.sortable ? dir === "asc" ? "ascending" : dir === "desc" ? "descending" : "none" : void 0;
453
+ const sortOrder = sortEntry && sort.length > 1 ? sort.findIndex((s) => s.key === key) + 1 : null;
454
+ const headerClass = headerClassFor(
455
+ column,
456
+ "px-4 py-3 text-xs font-semibold text-text-secondary uppercase tracking-wider"
457
+ );
458
+ return /* @__PURE__ */ jsx4(
459
+ "th",
460
+ {
461
+ scope: "col",
462
+ "aria-sort": ariaSort,
463
+ className: cn(
464
+ headerClass,
465
+ column.sortable && "cursor-pointer select-none hover:text-text-primary transition-colors"
466
+ ),
467
+ onClick: column.sortable ? (e) => onToggleSort(key, e.shiftKey) : void 0,
468
+ children: /* @__PURE__ */ jsxs4("span", { className: "inline-flex items-center gap-1", children: [
469
+ column.header,
470
+ column.sortable && /* @__PURE__ */ jsx4(
471
+ FontAwesomeIcon3,
472
+ {
473
+ icon: dir === "asc" ? faChevronUp2 : dir === "desc" ? faChevronDown2 : faSort2,
474
+ className: "w-2.5 h-2.5",
475
+ "aria-hidden": "true"
476
+ }
477
+ ),
478
+ sortOrder !== null && /* @__PURE__ */ jsx4(
479
+ "span",
480
+ {
481
+ "aria-hidden": "true",
482
+ className: "ml-0.5 inline-flex h-4 min-w-[1rem] items-center justify-center rounded-full bg-primary text-[10px] font-bold text-primary-fg px-1",
483
+ children: sortOrder
484
+ }
485
+ ),
486
+ column.filter && /* @__PURE__ */ jsx4(
487
+ FilterPopover,
488
+ {
489
+ column,
490
+ value: filterValue,
491
+ onChange: (v) => onSetFilter(key, v)
492
+ }
493
+ )
494
+ ] })
495
+ }
496
+ );
497
+ }
498
+
499
+ // modules/ui/Table/parts/BodyRow.tsx
500
+ import { jsx as jsx5 } from "react/jsx-runtime";
501
+ function BodyRow({
502
+ row,
503
+ columns,
504
+ onClick
505
+ }) {
506
+ return /* @__PURE__ */ jsx5(
507
+ "tr",
508
+ {
509
+ onClick: onClick ? () => onClick(row) : void 0,
510
+ className: cn(
511
+ "hover:bg-surface-overlay transition-colors",
512
+ onClick && "cursor-pointer"
513
+ ),
514
+ children: columns.map((col) => {
515
+ var _a;
516
+ return /* @__PURE__ */ jsx5(
517
+ "td",
518
+ {
519
+ className: cellClassFor(col, "px-4 py-3 text-text-primary"),
520
+ children: col.render ? col.render(row) : String((_a = row[col.key]) != null ? _a : "")
521
+ },
522
+ String(col.key)
523
+ );
524
+ })
525
+ }
526
+ );
527
+ }
528
+
529
+ // modules/ui/Table/parts/Toolbar.tsx
530
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
531
+ function Toolbar({
532
+ searchable,
533
+ searchValue,
534
+ searchPlaceholder,
535
+ onSearchChange,
536
+ pageSize,
537
+ pageSizeOptions,
538
+ onPageSizeChange,
539
+ rowsPerPageLabel,
540
+ className,
541
+ id
542
+ }) {
543
+ if (!searchable && !pageSizeOptions) return null;
544
+ return /* @__PURE__ */ jsxs5("div", { className: cn("flex items-center gap-2 flex-wrap", className), children: [
545
+ searchable && /* @__PURE__ */ jsx6(
546
+ SearchBar,
547
+ {
548
+ id: id ? `${id}-search` : "dt-search",
549
+ value: searchValue,
550
+ onChange: onSearchChange,
551
+ placeholder: searchPlaceholder,
552
+ className: "flex-1 min-w-40"
553
+ }
554
+ ),
555
+ pageSizeOptions && onPageSizeChange && pageSize !== void 0 && /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 shrink-0", children: [
556
+ /* @__PURE__ */ jsx6(
557
+ "label",
558
+ {
559
+ htmlFor: id ? `${id}-pagesize` : "dt-pagesize",
560
+ className: "text-xs text-text-secondary whitespace-nowrap",
561
+ children: rowsPerPageLabel != null ? rowsPerPageLabel : "Rows per page:"
562
+ }
563
+ ),
564
+ /* @__PURE__ */ jsx6(
565
+ "select",
566
+ {
567
+ id: id ? `${id}-pagesize` : "dt-pagesize",
568
+ value: pageSize,
569
+ onChange: (e) => onPageSizeChange(Number(e.target.value)),
570
+ className: cn(
571
+ "rounded-md border border-border bg-surface-base px-2 py-1.5 text-sm text-text-primary",
572
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus"
573
+ ),
574
+ children: pageSizeOptions.map((s) => /* @__PURE__ */ jsx6("option", { value: s, children: s }, s))
575
+ }
576
+ )
577
+ ] })
578
+ ] });
579
+ }
580
+
581
+ // modules/ui/Table/parts/EmptyState.tsx
582
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
583
+ function StateRow({
584
+ state,
585
+ colSpan,
586
+ message
587
+ }) {
588
+ if (state === "loading") {
589
+ return /* @__PURE__ */ jsx7("tr", { children: /* @__PURE__ */ jsx7(
590
+ "td",
591
+ {
592
+ colSpan,
593
+ className: "px-4 py-10 text-center text-sm text-text-secondary",
594
+ children: /* @__PURE__ */ jsxs6("span", { className: "inline-flex items-center gap-2", children: [
595
+ /* @__PURE__ */ jsx7(Spinner, { size: "sm" }),
596
+ message
597
+ ] })
598
+ }
599
+ ) });
600
+ }
601
+ if (state === "error") {
602
+ return /* @__PURE__ */ jsx7("tr", { children: /* @__PURE__ */ jsx7(
603
+ "td",
604
+ {
605
+ colSpan,
606
+ className: "px-4 py-10 text-center text-sm text-error-fg bg-error-subtle",
607
+ children: message
608
+ }
609
+ ) });
610
+ }
611
+ return /* @__PURE__ */ jsx7("tr", { children: /* @__PURE__ */ jsx7(
612
+ "td",
613
+ {
614
+ colSpan,
615
+ className: "px-4 py-10 text-center text-sm text-text-secondary",
616
+ children: message
617
+ }
618
+ ) });
619
+ }
620
+
621
+ // modules/ui/Table/core/useTable.ts
622
+ import { useCallback, useMemo, useState as useState4 } from "react";
623
+ function compareValues(a, b) {
624
+ const av = a === void 0 || a === null ? "" : a;
625
+ const bv = b === void 0 || b === null ? "" : b;
626
+ return String(av).localeCompare(String(bv), void 0, { numeric: true });
627
+ }
628
+ function applySort(rows, sort) {
629
+ if (!sort.length) return rows;
630
+ return [...rows].sort((a, b) => {
631
+ for (const s of sort) {
632
+ const cmp = compareValues(a[s.key], b[s.key]);
633
+ if (cmp !== 0) return s.dir === "asc" ? cmp : -cmp;
634
+ }
635
+ return 0;
636
+ });
637
+ }
638
+ function applySearch(rows, columns, query) {
639
+ const q = query.trim().toLowerCase();
640
+ if (!q) return rows;
641
+ return rows.filter(
642
+ (row) => columns.some((col) => {
643
+ var _a;
644
+ if (col.render) {
645
+ const raw = row[col.key];
646
+ if (raw === void 0 || raw === null) return false;
647
+ return String(raw).toLowerCase().includes(q);
648
+ }
649
+ return String((_a = row[col.key]) != null ? _a : "").toLowerCase().includes(q);
650
+ })
651
+ );
652
+ }
653
+ function applyColumnFilters(rows, columns, filters) {
654
+ const active = columns.filter((c) => c.filter && filters[String(c.key)]);
655
+ if (active.length === 0) return rows;
656
+ return rows.filter(
657
+ (row) => active.every((col) => {
658
+ var _a, _b, _c, _d;
659
+ const key = String(col.key);
660
+ const target = String((_a = row[col.key]) != null ? _a : "").toLowerCase();
661
+ const filterValue = (_c = (_b = filters[key]) == null ? void 0 : _b.toLowerCase()) != null ? _c : "";
662
+ if (!filterValue) return true;
663
+ if (((_d = col.filter) == null ? void 0 : _d.kind) === "select") return target === filterValue;
664
+ return target.includes(filterValue);
665
+ })
666
+ );
667
+ }
668
+ function nextSortState(current, key, shiftKey) {
669
+ const idx = current.findIndex((s) => s.key === key);
670
+ if (!shiftKey) {
671
+ if (idx < 0) return [{ key, dir: "asc" }];
672
+ const existing2 = current[idx];
673
+ if (existing2.dir === "asc") return [{ key, dir: "desc" }];
674
+ return [];
675
+ }
676
+ const next = [...current];
677
+ if (idx < 0) {
678
+ next.push({ key, dir: "asc" });
679
+ return next;
680
+ }
681
+ const existing = next[idx];
682
+ if (existing.dir === "asc") {
683
+ next[idx] = { key, dir: "desc" };
684
+ return next;
685
+ }
686
+ next.splice(idx, 1);
687
+ return next;
688
+ }
689
+ function useTable({
690
+ rows,
691
+ columns,
692
+ initialSort = [],
693
+ initialPageSize = 10,
694
+ initialFilters = {},
695
+ initialSearch = ""
696
+ }) {
697
+ const [sort, setSort] = useState4(initialSort);
698
+ const [page, setPage] = useState4(1);
699
+ const [pageSize, setPageSize] = useState4(initialPageSize);
700
+ const [search, setSearch] = useState4(initialSearch);
701
+ const [filters, setFilters] = useState4(initialFilters);
702
+ const filtered = useMemo(
703
+ () => applyColumnFilters(applySearch(rows, columns, search), columns, filters),
704
+ [rows, columns, search, filters]
705
+ );
706
+ const sorted = useMemo(() => applySort(filtered, sort), [filtered, sort]);
707
+ const total = sorted.length;
708
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
709
+ const safePage = Math.min(page, totalPages);
710
+ const toggleSort = useCallback(
711
+ (key, shiftKey) => {
712
+ setSort((curr) => nextSortState(curr, key, shiftKey));
713
+ setPage(1);
714
+ },
715
+ []
716
+ );
717
+ const setColumnFilter = useCallback(
718
+ (key, value) => {
719
+ setFilters((curr) => {
720
+ if (!value) {
721
+ const next = __spreadValues({}, curr);
722
+ delete next[key];
723
+ return next;
724
+ }
725
+ return __spreadProps(__spreadValues({}, curr), { [key]: value });
726
+ });
727
+ setPage(1);
728
+ },
729
+ []
730
+ );
731
+ const setGlobalSearch = useCallback((v) => {
732
+ setSearch(v);
733
+ setPage(1);
734
+ }, []);
735
+ const changePageSize = useCallback((n) => {
736
+ setPageSize(n);
737
+ setPage(1);
738
+ }, []);
739
+ return {
740
+ // State
741
+ sort,
742
+ page: safePage,
743
+ pageSize,
744
+ search,
745
+ filters,
746
+ // Derived
747
+ rows: sorted,
748
+ total,
749
+ totalPages,
750
+ // Actions
751
+ toggleSort,
752
+ setColumnFilter,
753
+ setGlobalSearch,
754
+ changePageSize,
755
+ setPage,
756
+ setSort,
757
+ setFilters
758
+ };
759
+ }
760
+
761
+ // modules/ui/Table/core/useServerTable.ts
762
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState5 } from "react";
763
+ function useServerTable({
764
+ fetchPage,
765
+ initialPageSize = 10,
766
+ initialSort = [],
767
+ initialSearch = "",
768
+ initialFilters = {},
769
+ externalError = null
770
+ }) {
771
+ const [rows, setRows] = useState5([]);
772
+ const [total, setTotal] = useState5(0);
773
+ const [sort, setSort] = useState5(initialSort);
774
+ const [page, setPage] = useState5(1);
775
+ const [pageSize, setPageSize] = useState5(initialPageSize);
776
+ const [search, setSearch] = useState5(initialSearch);
777
+ const [filters, setFilters] = useState5(initialFilters);
778
+ const [loading, setLoading] = useState5(false);
779
+ const [error, setError] = useState5(externalError);
780
+ const fetchRef = useRef2(fetchPage);
781
+ fetchRef.current = fetchPage;
782
+ const reqIdRef = useRef2(0);
783
+ useEffect2(() => {
784
+ const id = ++reqIdRef.current;
785
+ let cancelled = false;
786
+ setLoading(true);
787
+ setError(null);
788
+ fetchRef.current({ page, pageSize, sort, search, filters }).then((res) => {
789
+ if (cancelled || id !== reqIdRef.current) return;
790
+ setRows(res.rows);
791
+ setTotal(res.total);
792
+ }).catch((err) => {
793
+ if (cancelled || id !== reqIdRef.current) return;
794
+ setError(err instanceof Error ? err.message : "Fetch failed");
795
+ }).finally(() => {
796
+ if (cancelled || id !== reqIdRef.current) return;
797
+ setLoading(false);
798
+ });
799
+ return () => {
800
+ cancelled = true;
801
+ };
802
+ }, [page, pageSize, sort, search, filters]);
803
+ useEffect2(() => {
804
+ setError(externalError);
805
+ }, [externalError]);
806
+ const toggleSort = useCallback2((key, shiftKey) => {
807
+ setSort((curr) => nextSortState(curr, key, shiftKey));
808
+ setPage(1);
809
+ }, []);
810
+ const setColumnFilter = useCallback2((key, value) => {
811
+ setFilters((curr) => {
812
+ if (!value) {
813
+ const next = __spreadValues({}, curr);
814
+ delete next[key];
815
+ return next;
816
+ }
817
+ return __spreadProps(__spreadValues({}, curr), { [key]: value });
818
+ });
819
+ setPage(1);
820
+ }, []);
821
+ const setGlobalSearch = useCallback2((v) => {
822
+ setSearch(v);
823
+ setPage(1);
824
+ }, []);
825
+ const changePageSize = useCallback2((n) => {
826
+ setPageSize(n);
827
+ setPage(1);
828
+ }, []);
829
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
830
+ return {
831
+ rows,
832
+ total,
833
+ totalPages,
834
+ page,
835
+ pageSize,
836
+ sort,
837
+ search,
838
+ filters,
839
+ loading,
840
+ error,
841
+ toggleSort,
842
+ setColumnFilter,
843
+ setGlobalSearch,
844
+ changePageSize,
845
+ setPage,
846
+ setSort,
847
+ setFilters
848
+ };
849
+ }
850
+
851
+ // modules/ui/Table/DataTable.tsx
852
+ import { FontAwesomeIcon as FontAwesomeIcon4 } from "@fortawesome/react-fontawesome";
853
+ import {
854
+ faChevronDown as faChevronDown3,
855
+ faChevronRight
856
+ } from "@fortawesome/free-solid-svg-icons";
857
+
858
+ // modules/ui/Table/types.ts
859
+ var DEFAULT_MESSAGES = {
860
+ empty: "No results found.",
861
+ loading: "Loading\u2026",
862
+ error: "Something went wrong.",
863
+ searchPlaceholder: "Search\u2026",
864
+ rowsPerPage: "Rows per page:",
865
+ filter: "Filter",
866
+ clearFilter: "Clear",
867
+ apply: "Apply"
868
+ };
869
+
870
+ // modules/ui/Table/DataTable.tsx
871
+ import { Fragment, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
872
+ function DataTable(props) {
873
+ var _a, _b;
874
+ const generatedId = useId();
875
+ const id = (_a = props.id) != null ? _a : `dt-${generatedId.replace(/:/g, "")}`;
876
+ if (props.legacyAdvancedRows !== void 0) {
877
+ return /* @__PURE__ */ jsx8(LegacyAdvancedView, __spreadProps(__spreadValues({}, props), { id }));
878
+ }
879
+ if (props.serverControlled) {
880
+ return /* @__PURE__ */ jsx8(LegacyServerView, __spreadProps(__spreadValues({}, props), { id }));
881
+ }
882
+ const mode = (_b = props.mode) != null ? _b : "paginated";
883
+ if (mode === "server") {
884
+ return /* @__PURE__ */ jsx8(ServerView, __spreadProps(__spreadValues({}, props), { id }));
885
+ }
886
+ return /* @__PURE__ */ jsx8(ClientView, __spreadProps(__spreadValues({}, props), { mode, id }));
887
+ }
888
+ function ClientView(props) {
889
+ const {
890
+ columns,
891
+ rows = [],
892
+ caption,
893
+ searchable: searchableProp,
894
+ searchPlaceholder,
895
+ pageSize: defaultPageSize = 10,
896
+ pageSizeOptions = [5, 10, 25, 50],
897
+ emptyMessage,
898
+ state,
899
+ loadingMessage,
900
+ errorMessage,
901
+ onRowClick,
902
+ messages,
903
+ initialSort = [],
904
+ className,
905
+ mode,
906
+ id
907
+ } = props;
908
+ const msgs = __spreadValues(__spreadValues({}, DEFAULT_MESSAGES), messages);
909
+ const searchable = searchableProp != null ? searchableProp : true;
910
+ const isStatic = mode === "static";
911
+ const table = useTable({
912
+ rows,
913
+ columns,
914
+ initialSort,
915
+ initialPageSize: isStatic ? Math.max(rows.length, defaultPageSize) : defaultPageSize
916
+ });
917
+ const total = table.total;
918
+ const pageRows = isStatic ? table.rows : table.rows.slice(
919
+ (table.page - 1) * table.pageSize,
920
+ table.page * table.pageSize
921
+ );
922
+ const start = total === 0 ? 0 : (table.page - 1) * table.pageSize + 1;
923
+ const end = Math.min(table.page * table.pageSize, total);
924
+ const resolvedState = state != null ? state : pageRows.length === 0 ? "empty" : "ready";
925
+ return /* @__PURE__ */ jsxs7("div", { className: cn("space-y-3", className), "data-dt-id": id, children: [
926
+ (searchable || !isStatic && searchable) && /* @__PURE__ */ jsx8(
927
+ Toolbar,
928
+ {
929
+ id,
930
+ searchable,
931
+ searchValue: table.search,
932
+ searchPlaceholder: searchPlaceholder != null ? searchPlaceholder : msgs.searchPlaceholder,
933
+ onSearchChange: table.setGlobalSearch,
934
+ pageSize: isStatic ? void 0 : table.pageSize,
935
+ pageSizeOptions: isStatic ? void 0 : pageSizeOptions,
936
+ onPageSizeChange: isStatic ? void 0 : table.changePageSize,
937
+ rowsPerPageLabel: msgs.rowsPerPage
938
+ }
939
+ ),
940
+ /* @__PURE__ */ jsx8("div", { className: "w-full overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs7("table", { className: "w-full text-sm", children: [
941
+ caption && /* @__PURE__ */ jsx8("caption", { className: "sr-only", children: caption }),
942
+ /* @__PURE__ */ jsx8("thead", { className: "bg-surface-sunken border-b border-border", children: /* @__PURE__ */ jsx8("tr", { children: columns.map((col) => {
943
+ var _a;
944
+ return /* @__PURE__ */ jsx8(
945
+ HeaderCell,
946
+ {
947
+ column: col,
948
+ sort: table.sort,
949
+ filterValue: (_a = table.filters[String(col.key)]) != null ? _a : "",
950
+ onToggleSort: table.toggleSort,
951
+ onSetFilter: table.setColumnFilter
952
+ },
953
+ String(col.key)
954
+ );
955
+ }) }) }),
956
+ /* @__PURE__ */ jsx8("tbody", { className: "divide-y divide-border bg-surface-base", children: resolvedState !== "ready" ? /* @__PURE__ */ jsx8(
957
+ StateRow,
958
+ {
959
+ state: resolvedState,
960
+ colSpan: columns.length,
961
+ message: resolvedState === "loading" ? loadingMessage != null ? loadingMessage : msgs.loading : resolvedState === "error" ? errorMessage != null ? errorMessage : msgs.error : table.search && total === 0 ? `No results for "${table.search}"` : emptyMessage != null ? emptyMessage : msgs.empty
962
+ }
963
+ ) : pageRows.map((row, i) => /* @__PURE__ */ jsx8(
964
+ BodyRow,
965
+ {
966
+ row,
967
+ columns,
968
+ onClick: onRowClick
969
+ },
970
+ i
971
+ )) })
972
+ ] }) }),
973
+ !isStatic && /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-4 flex-wrap", children: [
974
+ /* @__PURE__ */ jsx8("p", { className: "text-xs text-text-secondary", children: total === 0 ? "No results" : `Showing ${start}\u2013${end} of ${total}${table.search ? ` (filtered from ${rows.length})` : ""}` }),
975
+ /* @__PURE__ */ jsx8(
976
+ Pagination,
977
+ {
978
+ page: table.page,
979
+ totalPages: table.totalPages,
980
+ onPageChange: table.setPage
981
+ }
982
+ )
983
+ ] })
984
+ ] });
985
+ }
986
+ function ServerView(props) {
987
+ var _a;
988
+ const {
989
+ columns,
990
+ fetchPage,
991
+ caption,
992
+ searchable: searchableProp,
993
+ searchPlaceholder,
994
+ pageSize: defaultPageSize = 10,
995
+ pageSizeOptions = [5, 10, 25, 50],
996
+ emptyMessage,
997
+ state: stateOverride,
998
+ loadingMessage,
999
+ errorMessage,
1000
+ onRowClick,
1001
+ messages,
1002
+ initialSort = [],
1003
+ className,
1004
+ id
1005
+ } = props;
1006
+ const msgs = __spreadValues(__spreadValues({}, DEFAULT_MESSAGES), messages);
1007
+ const searchable = searchableProp != null ? searchableProp : true;
1008
+ if (!fetchPage) {
1009
+ throw new Error('DataTable mode="server" requires a `fetchPage` prop.');
1010
+ }
1011
+ const table = useServerTable({
1012
+ fetchPage,
1013
+ initialPageSize: defaultPageSize,
1014
+ initialSort
1015
+ });
1016
+ const resolvedState = stateOverride != null ? stateOverride : table.loading ? "loading" : table.error ? "error" : table.rows.length === 0 ? "empty" : "ready";
1017
+ const start = table.total === 0 ? 0 : (table.page - 1) * table.pageSize + 1;
1018
+ const end = Math.min(table.page * table.pageSize, table.total);
1019
+ return /* @__PURE__ */ jsxs7("div", { className: cn("space-y-3", className), "data-dt-id": id, children: [
1020
+ /* @__PURE__ */ jsx8(
1021
+ Toolbar,
1022
+ {
1023
+ id,
1024
+ searchable,
1025
+ searchValue: table.search,
1026
+ searchPlaceholder: searchPlaceholder != null ? searchPlaceholder : msgs.searchPlaceholder,
1027
+ onSearchChange: table.setGlobalSearch,
1028
+ pageSize: table.pageSize,
1029
+ pageSizeOptions,
1030
+ onPageSizeChange: table.changePageSize,
1031
+ rowsPerPageLabel: msgs.rowsPerPage
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ jsx8("div", { className: "w-full overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs7("table", { className: "w-full text-sm", children: [
1035
+ caption && /* @__PURE__ */ jsx8("caption", { className: "sr-only", children: caption }),
1036
+ /* @__PURE__ */ jsx8("thead", { className: "bg-surface-sunken border-b border-border", children: /* @__PURE__ */ jsx8("tr", { children: columns.map((col) => {
1037
+ var _a2;
1038
+ return /* @__PURE__ */ jsx8(
1039
+ HeaderCell,
1040
+ {
1041
+ column: col,
1042
+ sort: table.sort,
1043
+ filterValue: (_a2 = table.filters[String(col.key)]) != null ? _a2 : "",
1044
+ onToggleSort: table.toggleSort,
1045
+ onSetFilter: table.setColumnFilter
1046
+ },
1047
+ String(col.key)
1048
+ );
1049
+ }) }) }),
1050
+ /* @__PURE__ */ jsx8("tbody", { className: "divide-y divide-border bg-surface-base", children: resolvedState !== "ready" ? /* @__PURE__ */ jsx8(
1051
+ StateRow,
1052
+ {
1053
+ state: resolvedState,
1054
+ colSpan: columns.length,
1055
+ message: resolvedState === "loading" ? loadingMessage != null ? loadingMessage : msgs.loading : resolvedState === "error" ? (_a = errorMessage != null ? errorMessage : table.error) != null ? _a : msgs.error : emptyMessage != null ? emptyMessage : msgs.empty
1056
+ }
1057
+ ) : table.rows.map((row, i) => /* @__PURE__ */ jsx8(
1058
+ BodyRow,
1059
+ {
1060
+ row,
1061
+ columns,
1062
+ onClick: onRowClick
1063
+ },
1064
+ i
1065
+ )) })
1066
+ ] }) }),
1067
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-4 flex-wrap", children: [
1068
+ /* @__PURE__ */ jsx8("p", { className: "text-xs text-text-secondary", children: table.total === 0 ? "No results" : `Showing ${start}\u2013${end} of ${table.total}` }),
1069
+ /* @__PURE__ */ jsx8(
1070
+ Pagination,
1071
+ {
1072
+ page: table.page,
1073
+ totalPages: table.totalPages,
1074
+ onPageChange: table.setPage
1075
+ }
1076
+ )
1077
+ ] })
1078
+ ] });
1079
+ }
1080
+ function LegacyAdvancedView(props) {
1081
+ const {
1082
+ columns,
1083
+ legacyAdvancedRows = [],
1084
+ caption,
1085
+ selectable = false,
1086
+ stickyHeader = false,
1087
+ emptyMessage = "No results found.",
1088
+ onSelectionChange,
1089
+ className
1090
+ } = props;
1091
+ const [selected, setSelected] = useState6(/* @__PURE__ */ new Set());
1092
+ const [expanded, setExpanded] = useState6(/* @__PURE__ */ new Set());
1093
+ function toggleRow(i) {
1094
+ setSelected((prev) => {
1095
+ const next = new Set(prev);
1096
+ if (next.has(i)) next.delete(i);
1097
+ else next.add(i);
1098
+ onSelectionChange == null ? void 0 : onSelectionChange([...next]);
1099
+ return next;
1100
+ });
1101
+ }
1102
+ function toggleAll() {
1103
+ if (selected.size === legacyAdvancedRows.length) {
1104
+ setSelected(/* @__PURE__ */ new Set());
1105
+ onSelectionChange == null ? void 0 : onSelectionChange([]);
1106
+ } else {
1107
+ const all = new Set(legacyAdvancedRows.map((_, i) => i));
1108
+ setSelected(all);
1109
+ onSelectionChange == null ? void 0 : onSelectionChange([...all]);
1110
+ }
1111
+ }
1112
+ function toggleExpand(i) {
1113
+ setExpanded((prev) => {
1114
+ const next = new Set(prev);
1115
+ if (next.has(i)) next.delete(i);
1116
+ else next.add(i);
1117
+ return next;
1118
+ });
1119
+ }
1120
+ const allSelected = legacyAdvancedRows.length > 0 && selected.size === legacyAdvancedRows.length;
1121
+ const someSelected = selected.size > 0 && selected.size < legacyAdvancedRows.length;
1122
+ const hasAnyExpand = legacyAdvancedRows.some((r) => r._expanded !== void 0);
1123
+ const totalCols = columns.length + (selectable ? 1 : 0) + (hasAnyExpand ? 1 : 0);
1124
+ return /* @__PURE__ */ jsxs7("div", { className: cn("space-y-2", className), children: [
1125
+ selectable && selected.size > 0 && /* @__PURE__ */ jsxs7("p", { className: "text-xs text-text-secondary", children: [
1126
+ selected.size,
1127
+ " of ",
1128
+ legacyAdvancedRows.length,
1129
+ " row",
1130
+ legacyAdvancedRows.length !== 1 ? "s" : "",
1131
+ " selected"
1132
+ ] }),
1133
+ /* @__PURE__ */ jsx8(
1134
+ "div",
1135
+ {
1136
+ className: cn(
1137
+ "w-full rounded-lg border border-border",
1138
+ stickyHeader && "overflow-auto max-h-80"
1139
+ ),
1140
+ children: /* @__PURE__ */ jsxs7("table", { className: "w-full text-sm", children: [
1141
+ caption && /* @__PURE__ */ jsx8("caption", { className: "sr-only", children: caption }),
1142
+ /* @__PURE__ */ jsx8(
1143
+ "thead",
1144
+ {
1145
+ className: cn(
1146
+ "bg-surface-sunken border-b border-border",
1147
+ stickyHeader && "sticky top-0 z-10"
1148
+ ),
1149
+ children: /* @__PURE__ */ jsxs7("tr", { children: [
1150
+ selectable && /* @__PURE__ */ jsx8("th", { scope: "col", className: "w-10 px-4 py-3", children: /* @__PURE__ */ jsx8(
1151
+ "input",
1152
+ {
1153
+ type: "checkbox",
1154
+ "aria-label": "Select all rows",
1155
+ checked: allSelected,
1156
+ ref: (el) => {
1157
+ if (el) el.indeterminate = someSelected;
1158
+ },
1159
+ onChange: toggleAll,
1160
+ className: "h-4 w-4 rounded border-border text-primary focus-visible:ring-2 focus-visible:ring-border-focus"
1161
+ }
1162
+ ) }),
1163
+ hasAnyExpand && /* @__PURE__ */ jsx8("th", { scope: "col", className: "w-10 px-4 py-3", "aria-label": "Expand" }),
1164
+ columns.map((col) => /* @__PURE__ */ jsx8(
1165
+ "th",
1166
+ {
1167
+ scope: "col",
1168
+ className: cn(
1169
+ "px-4 py-3 text-xs font-semibold text-text-secondary uppercase tracking-wider",
1170
+ col.align === "center" && "text-center",
1171
+ col.align === "right" && "text-right",
1172
+ !col.align && "text-left"
1173
+ ),
1174
+ children: col.header
1175
+ },
1176
+ String(col.key)
1177
+ ))
1178
+ ] })
1179
+ }
1180
+ ),
1181
+ /* @__PURE__ */ jsx8("tbody", { className: "divide-y divide-border bg-surface-base", children: legacyAdvancedRows.length === 0 ? /* @__PURE__ */ jsx8("tr", { children: /* @__PURE__ */ jsx8(
1182
+ "td",
1183
+ {
1184
+ colSpan: totalCols,
1185
+ className: "px-4 py-10 text-center text-sm text-text-secondary",
1186
+ children: emptyMessage
1187
+ }
1188
+ ) }) : legacyAdvancedRows.map((row, i) => {
1189
+ const isSelected = selected.has(i);
1190
+ const isExpanded = expanded.has(i);
1191
+ const hasExpand = row._expanded !== void 0;
1192
+ return /* @__PURE__ */ jsx8(
1193
+ LegacyAdvancedRow,
1194
+ {
1195
+ row,
1196
+ rowIndex: i,
1197
+ columns,
1198
+ selectable,
1199
+ isSelected,
1200
+ isExpanded,
1201
+ hasExpand,
1202
+ hasAnyExpand,
1203
+ totalCols,
1204
+ onToggleRow: toggleRow,
1205
+ onToggleExpand: toggleExpand
1206
+ },
1207
+ i
1208
+ );
1209
+ }) })
1210
+ ] })
1211
+ }
1212
+ )
1213
+ ] });
1214
+ }
1215
+ function LegacyAdvancedRow({
1216
+ row,
1217
+ rowIndex,
1218
+ columns,
1219
+ selectable,
1220
+ isSelected,
1221
+ isExpanded,
1222
+ hasExpand,
1223
+ hasAnyExpand,
1224
+ totalCols,
1225
+ onToggleRow,
1226
+ onToggleExpand
1227
+ }) {
1228
+ return /* @__PURE__ */ jsxs7(Fragment, { children: [
1229
+ /* @__PURE__ */ jsxs7(
1230
+ "tr",
1231
+ {
1232
+ className: cn(
1233
+ "hover:bg-surface-overlay transition-colors",
1234
+ isSelected && "bg-primary-subtle"
1235
+ ),
1236
+ children: [
1237
+ selectable && /* @__PURE__ */ jsx8("td", { className: "w-10 px-4 py-3", children: /* @__PURE__ */ jsx8(
1238
+ "input",
1239
+ {
1240
+ type: "checkbox",
1241
+ "aria-label": `Select row ${rowIndex + 1}`,
1242
+ checked: isSelected,
1243
+ onChange: () => onToggleRow(rowIndex),
1244
+ className: "h-4 w-4 rounded border-border text-primary focus-visible:ring-2 focus-visible:ring-border-focus"
1245
+ }
1246
+ ) }),
1247
+ hasExpand && /* @__PURE__ */ jsx8("td", { className: "w-10 px-4 py-3", children: /* @__PURE__ */ jsx8(
1248
+ "button",
1249
+ {
1250
+ type: "button",
1251
+ "aria-label": isExpanded ? "Collapse row" : "Expand row",
1252
+ "aria-expanded": isExpanded,
1253
+ onClick: () => onToggleExpand(rowIndex),
1254
+ className: "text-text-disabled hover:text-text-primary transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus rounded",
1255
+ children: /* @__PURE__ */ jsx8(
1256
+ FontAwesomeIcon4,
1257
+ {
1258
+ icon: isExpanded ? faChevronDown3 : faChevronRight,
1259
+ className: "w-2.5 h-2.5",
1260
+ "aria-hidden": "true"
1261
+ }
1262
+ )
1263
+ }
1264
+ ) }),
1265
+ !hasExpand && hasAnyExpand && /* @__PURE__ */ jsx8("td", { className: "w-10 px-4 py-3" }),
1266
+ columns.map((col) => {
1267
+ var _a;
1268
+ return /* @__PURE__ */ jsx8(
1269
+ "td",
1270
+ {
1271
+ className: cellClassFor(col, "px-4 py-3 text-text-primary"),
1272
+ children: col.render ? col.render(row) : String((_a = row[col.key]) != null ? _a : "")
1273
+ },
1274
+ String(col.key)
1275
+ );
1276
+ })
1277
+ ]
1278
+ }
1279
+ ),
1280
+ hasExpand && isExpanded && /* @__PURE__ */ jsx8("tr", { className: "bg-surface-sunken", children: /* @__PURE__ */ jsx8(
1281
+ "td",
1282
+ {
1283
+ colSpan: totalCols,
1284
+ className: "px-6 py-3 text-sm text-text-secondary",
1285
+ children: row._expanded
1286
+ }
1287
+ ) })
1288
+ ] });
1289
+ }
1290
+ function LegacyServerView(props) {
1291
+ const {
1292
+ columns,
1293
+ rows = [],
1294
+ caption,
1295
+ emptyMessage = "No results found.",
1296
+ onRowClick,
1297
+ className,
1298
+ serverControlled
1299
+ } = props;
1300
+ if (!serverControlled) return null;
1301
+ const {
1302
+ page,
1303
+ totalPages,
1304
+ total,
1305
+ pageSize,
1306
+ onPageChange,
1307
+ getRowKey,
1308
+ loading = false,
1309
+ title,
1310
+ subtitle,
1311
+ headerRight,
1312
+ toolbar
1313
+ } = serverControlled;
1314
+ const safeTotalPages = Math.max(1, totalPages);
1315
+ const rangeStart = total !== void 0 && pageSize ? (page - 1) * pageSize + 1 : null;
1316
+ const rangeEnd = total !== void 0 && pageSize ? Math.min(page * pageSize, total) : null;
1317
+ return /* @__PURE__ */ jsxs7(
1318
+ "div",
1319
+ {
1320
+ className: cn(
1321
+ "rounded-xl border border-border bg-surface-raised shadow-sm overflow-hidden",
1322
+ className
1323
+ ),
1324
+ children: [
1325
+ (title || headerRight) && /* @__PURE__ */ jsxs7("div", { className: "flex items-start justify-between gap-3 px-6 py-4 border-b border-border", children: [
1326
+ /* @__PURE__ */ jsxs7("div", { children: [
1327
+ title && /* @__PURE__ */ jsx8("h3", { className: "text-sm font-semibold text-text-primary", children: title }),
1328
+ subtitle && /* @__PURE__ */ jsx8("p", { className: "text-xs text-text-secondary mt-0.5", children: subtitle })
1329
+ ] }),
1330
+ headerRight && /* @__PURE__ */ jsx8("div", { className: "shrink-0", children: headerRight })
1331
+ ] }),
1332
+ toolbar && /* @__PURE__ */ jsx8("div", { className: "px-6 pt-4 pb-0", children: toolbar }),
1333
+ loading ? /* @__PURE__ */ jsx8("div", { className: "flex justify-center py-12", children: /* @__PURE__ */ jsx8("span", { className: "inline-block h-8 w-8 animate-spin rounded-full border-2 border-border border-t-primary" }) }) : /* @__PURE__ */ jsx8("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs7("table", { className: "w-full text-sm", children: [
1334
+ caption && /* @__PURE__ */ jsx8("caption", { className: "sr-only", children: caption }),
1335
+ /* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsx8("tr", { className: "border-b border-border bg-surface-sunken", children: columns.map((col) => /* @__PURE__ */ jsx8(
1336
+ "th",
1337
+ {
1338
+ scope: "col",
1339
+ className: cn(
1340
+ "px-6 py-3 text-xs font-semibold text-text-secondary uppercase tracking-wider",
1341
+ col.align === "center" && "text-center",
1342
+ col.align === "right" && "text-right",
1343
+ !col.align && "text-left"
1344
+ ),
1345
+ children: col.header
1346
+ },
1347
+ String(col.key)
1348
+ )) }) }),
1349
+ /* @__PURE__ */ jsx8("tbody", { className: "divide-y divide-border bg-surface-base", children: rows.length === 0 ? /* @__PURE__ */ jsx8("tr", { children: /* @__PURE__ */ jsx8(
1350
+ "td",
1351
+ {
1352
+ colSpan: columns.length,
1353
+ className: "px-6 py-10 text-center text-sm text-text-secondary",
1354
+ children: emptyMessage
1355
+ }
1356
+ ) }) : rows.map((row) => /* @__PURE__ */ jsx8(
1357
+ "tr",
1358
+ {
1359
+ onClick: () => onRowClick == null ? void 0 : onRowClick(row),
1360
+ className: cn(
1361
+ "hover:bg-surface-overlay transition-colors",
1362
+ onRowClick && "cursor-pointer"
1363
+ ),
1364
+ children: columns.map((col) => {
1365
+ var _a;
1366
+ return /* @__PURE__ */ jsx8(
1367
+ "td",
1368
+ {
1369
+ className: cn(
1370
+ "px-6 py-4 text-text-primary",
1371
+ col.align === "center" && "text-center",
1372
+ col.align === "right" && "text-right"
1373
+ ),
1374
+ children: col.render ? col.render(row) : String((_a = row[col.key]) != null ? _a : "\u2014")
1375
+ },
1376
+ String(col.key)
1377
+ );
1378
+ })
1379
+ },
1380
+ getRowKey(row)
1381
+ )) })
1382
+ ] }) }),
1383
+ !loading && /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-4 px-6 py-4 border-t border-border flex-wrap", children: [
1384
+ /* @__PURE__ */ jsx8("p", { className: "text-xs text-text-secondary", children: total !== void 0 && rangeStart !== null && rangeEnd !== null ? `Showing ${rangeStart}\u2013${rangeEnd} of ${total}` : total !== void 0 ? `${total} result${total !== 1 ? "s" : ""}` : null }),
1385
+ /* @__PURE__ */ jsx8(
1386
+ Pagination,
1387
+ {
1388
+ page,
1389
+ totalPages: safeTotalPages,
1390
+ onPageChange,
1391
+ showFirstLast: true
1392
+ }
1393
+ )
1394
+ ] })
1395
+ ]
1396
+ }
1397
+ );
1398
+ }
1399
+
1400
+ // modules/ui/Table/index.tsx
1401
+ import { jsx as jsx9 } from "react/jsx-runtime";
1402
+ function AdvancedDataTable(props) {
1403
+ return /* @__PURE__ */ jsx9(
1404
+ DataTable,
1405
+ {
1406
+ columns: props.columns,
1407
+ legacyAdvancedRows: props.rows,
1408
+ caption: props.caption,
1409
+ selectable: props.selectable,
1410
+ stickyHeader: props.stickyHeader,
1411
+ emptyMessage: props.emptyMessage,
1412
+ onSelectionChange: props.onSelectionChange,
1413
+ className: props.className
1414
+ }
1415
+ );
1416
+ }
1417
+ function ServerDataTable(props) {
1418
+ return /* @__PURE__ */ jsx9(
1419
+ DataTable,
1420
+ {
1421
+ columns: props.columns,
1422
+ rows: props.rows,
1423
+ caption: props.caption,
1424
+ emptyMessage: props.emptyMessage,
1425
+ onRowClick: props.onRowClick,
1426
+ className: props.className,
1427
+ serverControlled: {
1428
+ page: props.page,
1429
+ totalPages: props.totalPages,
1430
+ total: props.total,
1431
+ pageSize: props.pageSize,
1432
+ onPageChange: props.onPageChange,
1433
+ getRowKey: props.getRowKey,
1434
+ loading: props.loading,
1435
+ title: props.title,
1436
+ subtitle: props.subtitle,
1437
+ headerRight: props.headerRight,
1438
+ toolbar: props.toolbar
1439
+ }
1440
+ }
1441
+ );
1442
+ }
1443
+
1444
+ export {
1445
+ Table,
1446
+ Pagination,
1447
+ DataTable,
1448
+ AdvancedDataTable,
1449
+ ServerDataTable
1450
+ };