@eifi1/ui-kit 0.47.0 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +5 -3
  2. package/dist/admin/admin-sections.d.ts +8 -0
  3. package/dist/admin/admin-sections.js +2 -1
  4. package/dist/admin/admin-sections.js.map +1 -1
  5. package/dist/auth/verify-email.d.ts +9 -1
  6. package/dist/auth/verify-email.js +22 -13
  7. package/dist/auth/verify-email.js.map +1 -1
  8. package/dist/components/alert-banner.d.ts +29 -1
  9. package/dist/components/alert-banner.js +84 -7
  10. package/dist/components/alert-banner.js.map +1 -1
  11. package/dist/components/button-look.d.ts +4 -0
  12. package/dist/components/button-look.js +2 -0
  13. package/dist/components/button-look.js.map +1 -1
  14. package/dist/components/combobox-core.js +5 -1
  15. package/dist/components/combobox-core.js.map +1 -1
  16. package/dist/components/data-table-column-settings.d.ts +6 -1
  17. package/dist/components/data-table-column-settings.js +12 -2
  18. package/dist/components/data-table-column-settings.js.map +1 -1
  19. package/dist/components/data-table-fit.d.ts +44 -0
  20. package/dist/components/data-table-fit.js +103 -0
  21. package/dist/components/data-table-fit.js.map +1 -0
  22. package/dist/components/data-table-groups.d.ts +95 -0
  23. package/dist/components/data-table-groups.js +165 -0
  24. package/dist/components/data-table-groups.js.map +1 -0
  25. package/dist/components/data-table-head.js +3 -1
  26. package/dist/components/data-table-head.js.map +1 -1
  27. package/dist/components/data-table-labels.d.ts +7 -0
  28. package/dist/components/data-table-labels.js +3 -0
  29. package/dist/components/data-table-labels.js.map +1 -1
  30. package/dist/components/data-table-leading-row.d.ts +17 -0
  31. package/dist/components/data-table-leading-row.js +41 -0
  32. package/dist/components/data-table-leading-row.js.map +1 -0
  33. package/dist/components/data-table-resize.js +1 -1
  34. package/dist/components/data-table-resize.js.map +1 -1
  35. package/dist/components/data-table.d.ts +78 -2
  36. package/dist/components/data-table.js +348 -257
  37. package/dist/components/data-table.js.map +1 -1
  38. package/dist/components/disclosure.d.ts +3 -1
  39. package/dist/components/disclosure.js +10 -4
  40. package/dist/components/disclosure.js.map +1 -1
  41. package/dist/components/dropdown.js +8 -1
  42. package/dist/components/dropdown.js.map +1 -1
  43. package/dist/components/floating-panel.d.ts +14 -1
  44. package/dist/components/floating-panel.js +7 -2
  45. package/dist/components/floating-panel.js.map +1 -1
  46. package/dist/components/hover-menu.js +5 -1
  47. package/dist/components/hover-menu.js.map +1 -1
  48. package/dist/components/icon-button.js +2 -1
  49. package/dist/components/icon-button.js.map +1 -1
  50. package/dist/components/popover.js +5 -1
  51. package/dist/components/popover.js.map +1 -1
  52. package/dist/components/table-skeleton.d.ts +35 -0
  53. package/dist/components/table-skeleton.js +75 -0
  54. package/dist/components/table-skeleton.js.map +1 -0
  55. package/dist/components/tabs.d.ts +10 -12
  56. package/dist/components/tabs.js +20 -10
  57. package/dist/components/tabs.js.map +1 -1
  58. package/dist/components/use-table-state.d.ts +8 -7
  59. package/dist/components/use-table-state.js +37 -3
  60. package/dist/components/use-table-state.js.map +1 -1
  61. package/dist/data-table.d.ts +1 -0
  62. package/dist/data-table.js.map +1 -1
  63. package/dist/eslint/index.js +1 -1
  64. package/dist/feedback/feedback-table.d.ts +5 -0
  65. package/dist/feedback/feedback-table.js +30 -7
  66. package/dist/feedback/feedback-table.js.map +1 -1
  67. package/dist/hooks/use-dismiss.d.ts +30 -1
  68. package/dist/hooks/use-dismiss.js +30 -2
  69. package/dist/hooks/use-dismiss.js.map +1 -1
  70. package/dist/i18n/german.js +6 -1
  71. package/dist/i18n/german.js.map +1 -1
  72. package/dist/i18n/kit-labels.d.ts +4 -0
  73. package/dist/i18n/kit-labels.js.map +1 -1
  74. package/dist/i18n/locales/en.js +6 -1
  75. package/dist/i18n/locales/en.js.map +2 -2
  76. package/dist/i18n/locales/es.js +6 -1
  77. package/dist/i18n/locales/es.js.map +1 -1
  78. package/dist/i18n/locales/fr.js +6 -1
  79. package/dist/i18n/locales/fr.js.map +1 -1
  80. package/dist/i18n/locales/hu.js +6 -1
  81. package/dist/i18n/locales/hu.js.map +1 -1
  82. package/dist/i18n/locales/it.js +6 -1
  83. package/dist/i18n/locales/it.js.map +1 -1
  84. package/dist/i18n/locales/zh.js +6 -1
  85. package/dist/i18n/locales/zh.js.map +1 -1
  86. package/dist/i18n/shared-labels.js +3 -1
  87. package/dist/i18n/shared-labels.js.map +1 -1
  88. package/dist/index.d.ts +2 -0
  89. package/dist/index.js +2 -0
  90. package/dist/index.js.map +1 -1
  91. package/dist/lib/float-clearance.d.ts +8 -0
  92. package/dist/lib/float-clearance.js +29 -0
  93. package/dist/lib/float-clearance.js.map +1 -0
  94. package/dist/settings/settings-catalogue.d.ts +10 -0
  95. package/dist/settings/settings-catalogue.js.map +1 -1
  96. package/dist/settings/settings-layout.js +10 -3
  97. package/dist/settings/settings-layout.js.map +1 -1
  98. package/dist/shell/app-shell.d.ts +5 -0
  99. package/dist/shell/app-shell.js +5 -3
  100. package/dist/shell/app-shell.js.map +1 -1
  101. package/dist/shell/option-switcher-menu.d.ts +2 -0
  102. package/dist/shell/option-switcher-menu.js +6 -2
  103. package/dist/shell/option-switcher-menu.js.map +1 -1
  104. package/dist/shell/topbar-action-menu.js +2 -1
  105. package/dist/shell/topbar-action-menu.js.map +1 -1
  106. package/dist/shell/topbar-controls.js +5 -3
  107. package/dist/shell/topbar-controls.js.map +1 -1
  108. package/dist/tour/tour.js +1 -0
  109. package/dist/tour/tour.js.map +1 -1
  110. package/package.json +1 -1
  111. package/src/admin/admin-sections.tsx +8 -0
  112. package/src/auth/verify-email.tsx +34 -9
  113. package/src/components/alert-banner.tsx +131 -7
  114. package/src/components/button-look.ts +6 -0
  115. package/src/components/combobox-core.tsx +7 -1
  116. package/src/components/data-table-column-settings.tsx +23 -2
  117. package/src/components/data-table-fit.ts +187 -0
  118. package/src/components/data-table-groups.tsx +275 -0
  119. package/src/components/data-table-head.tsx +4 -1
  120. package/src/components/data-table-labels.ts +13 -0
  121. package/src/components/data-table-leading-row.tsx +55 -0
  122. package/src/components/data-table-resize.ts +2 -1
  123. package/src/components/data-table.tsx +246 -43
  124. package/src/components/disclosure.tsx +22 -5
  125. package/src/components/dropdown.tsx +11 -1
  126. package/src/components/floating-panel.tsx +24 -1
  127. package/src/components/hover-menu.tsx +6 -1
  128. package/src/components/icon-button.tsx +3 -3
  129. package/src/components/popover.tsx +7 -1
  130. package/src/components/table-skeleton.tsx +133 -0
  131. package/src/components/tabs.tsx +67 -37
  132. package/src/components/use-table-state.ts +54 -3
  133. package/src/data-table.ts +2 -0
  134. package/src/feedback/feedback-table.tsx +33 -1
  135. package/src/hooks/use-dismiss.ts +65 -3
  136. package/src/i18n/german.ts +5 -0
  137. package/src/i18n/kit-labels.tsx +4 -0
  138. package/src/i18n/locales/es.ts +5 -0
  139. package/src/i18n/locales/fr.ts +5 -0
  140. package/src/i18n/locales/hu.ts +5 -0
  141. package/src/i18n/locales/it.ts +5 -0
  142. package/src/i18n/locales/zh.ts +5 -0
  143. package/src/i18n/shared-labels.ts +2 -0
  144. package/src/index.ts +3 -0
  145. package/src/lib/float-clearance.ts +54 -0
  146. package/src/settings/settings-catalogue.ts +10 -0
  147. package/src/settings/settings-layout.tsx +18 -4
  148. package/src/shell/app-shell.tsx +17 -3
  149. package/src/shell/option-switcher-menu.tsx +16 -4
  150. package/src/shell/topbar-action-menu.tsx +22 -10
  151. package/src/shell/topbar-controls.tsx +18 -12
  152. package/src/tour/tour.tsx +5 -0
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { createElement } from "react";
4
- import { Fragment, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
4
+ import { Fragment as Fragment2, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
5
5
  import { Card } from "./card.js";
6
6
  import { Spinner } from "./spinner.js";
7
7
  import { isTableOwnColumn, useRowActionColumn } from "./data-table-row-actions.js";
@@ -19,6 +19,14 @@ import {
19
19
  } from "./data-table-shared.js";
20
20
  import { MobileFilters, MobileRow, MobileSelectBar, MobileSort, StackedFields } from "./data-table-mobile.js";
21
21
  import { DataTableHead } from "./data-table-head.js";
22
+ import { DataTableLeadingRow } from "./data-table-leading-row.js";
23
+ import {
24
+ DataTableGroupBand,
25
+ DataTableLoadMoreFooter,
26
+ groupLevels,
27
+ groupRows,
28
+ useGroupBandTops
29
+ } from "./data-table-groups.js";
22
30
  import { ColumnSettingsPanel } from "./data-table-column-settings.js";
23
31
  import { useLargeText } from "../hooks/use-large-text.js";
24
32
  import { cn } from "../lib/cn.js";
@@ -42,10 +50,12 @@ const DEFAULT_DATA_TABLE_SORT_LABELS = {
42
50
  const flipDir = (d) => d === "asc" ? "desc" : "asc";
43
51
  function moveRowFocus(from, key) {
44
52
  if (!(key in ROW_NAV_KEYS)) return false;
45
- const body = from.parentElement;
46
- if (!body) return false;
47
- const rows = Array.from(body.children).filter(
48
- (el) => el instanceof HTMLElement && el.hasAttribute("data-row-nav")
53
+ const table = from.closest("table");
54
+ if (!table) return false;
55
+ const rows = Array.from(table.tBodies).flatMap(
56
+ (body) => Array.from(body.children).filter(
57
+ (el) => el instanceof HTMLElement && el.hasAttribute("data-row-nav")
58
+ )
49
59
  );
50
60
  const i = rows.indexOf(from);
51
61
  const target = key === "ArrowDown" ? rows[i + 1] : key === "ArrowUp" ? rows[i - 1] : key === "Home" ? rows[0] : key === "End" ? rows[rows.length - 1] : void 0;
@@ -55,6 +65,7 @@ function moveRowFocus(from, key) {
55
65
  const ROW_NAV_KEYS = { ArrowDown: 1, ArrowUp: 1, Home: 1, End: 1 };
56
66
  import { DEFAULT_PERSIST_PREFIX, useTableState } from "./use-table-state.js";
57
67
  import { useMobileReveal } from "./use-mobile-reveal.js";
68
+ import { GROW_CELL_CLASS, GROW_PEER_CELL_CLASS, useColumnFit } from "./data-table-fit.js";
58
69
  function DataTable({
59
70
  rows,
60
71
  columns: columnsProp,
@@ -94,6 +105,9 @@ function DataTable({
94
105
  mobileSwipeActions,
95
106
  desktopSwipeActions,
96
107
  leadingRow,
108
+ leadingRowSticky = false,
109
+ groupBy,
110
+ loadMore,
97
111
  density = "comfortable",
98
112
  sortCycle = "tri",
99
113
  chrome = "full",
@@ -160,7 +174,13 @@ function DataTable({
160
174
  // Ignored in `serverPagination` mode, where the owner's is the page size.
161
175
  pageSizeProp: isServer ? void 0 : pageSizeProp
162
176
  });
163
- const widths = useMemo(() => resizable ? storedWidths : {}, [resizable, storedWidths]);
177
+ const widths = useMemo(() => {
178
+ if (!resizable) return {};
179
+ const grow = columns.find((c) => c.grow)?.key;
180
+ if (!grow || !(grow in storedWidths)) return storedWidths;
181
+ const { [grow]: _ignored, ...rest } = storedWidths;
182
+ return rest;
183
+ }, [resizable, storedWidths, columns]);
164
184
  const hiddenCols = useMemo(
165
185
  () => columnSettings ? storedHiddenCols : /* @__PURE__ */ new Set(),
166
186
  [columnSettings, storedHiddenCols]
@@ -221,7 +241,7 @@ function DataTable({
221
241
  });
222
242
  return copy;
223
243
  }, [filtered, sorts, columnSig, isServer]);
224
- const unpaged = !isServer && !paginated;
244
+ const unpaged = !isServer && (!paginated || !!loadMore);
225
245
  const effectivePageSize = isServer ? serverPagination.pageSize : unpaged || pageSize === Infinity ? sorted.length || 1 : pageSize;
226
246
  const paginationTotal = isServer ? serverPagination.total : sorted.length;
227
247
  const paginationPageSize = isServer ? serverPagination.pageSize : pageSize;
@@ -297,17 +317,26 @@ function DataTable({
297
317
  else setPage(p);
298
318
  announce(labels.pageChanged(p + 1, totalPages));
299
319
  };
320
+ const { autoHidden, keep: keepColumn } = useColumnFit({
321
+ columns,
322
+ hiddenCols,
323
+ headRefs,
324
+ scroller: () => desktopScroller.current,
325
+ enabled: !usePhoneLayout()
326
+ });
300
327
  const visibleColumns = useMemo(
301
- () => columns.filter((c) => !hiddenCols.has(c.key)),
302
- [columns, hiddenCols]
328
+ () => columns.filter((c) => !hiddenCols.has(c.key) && !autoHidden.has(c.key)),
329
+ [columns, hiddenCols, autoHidden]
303
330
  );
331
+ const growDrawn = visibleColumns.some((c) => c.grow);
304
332
  const linkColumn = rowHref ? visibleColumns.find((c) => c.mobilePrimary && !c.noRowLink) ?? visibleColumns.find((c) => !c.noRowLink) : void 0;
305
333
  const { startResize, moveResize, finishResize, autoSizeAll } = useColumnResize({
306
334
  headRefs,
307
335
  widths,
308
336
  setWidths,
309
337
  columns,
310
- hiddenCols
338
+ // The columns left out for width have no header to measure either.
339
+ hiddenCols: autoHidden.size > 0 ? /* @__PURE__ */ new Set([...hiddenCols, ...autoHidden]) : hiddenCols
311
340
  });
312
341
  const visibleCount = visibleColumns.length;
313
342
  const totalCount = columns.length;
@@ -323,7 +352,8 @@ function DataTable({
323
352
  const columnsCountLabel = labels.columnsCount(visibleCount - ownVisible, totalCount - ownColumns);
324
353
  const isMdUp = !usePhoneLayout();
325
354
  const desktopScroller = useRef(null);
326
- const measuresVisibleWidth = isMdUp && !!expandedRow;
355
+ const levels = groupLevels(groupBy);
356
+ const measuresVisibleWidth = isMdUp && (!!expandedRow || levels.length > 0 || !!loadMore);
327
357
  useEffect(() => {
328
358
  const el = desktopScroller.current;
329
359
  if (!measuresVisibleWidth || !el || typeof ResizeObserver === "undefined") return;
@@ -436,8 +466,17 @@ function DataTable({
436
466
  },
437
467
  rowKey(row)
438
468
  );
439
- const mobileGroups = mobileGroupBy ? mobileSlice.reduce((acc, row) => {
440
- const key = mobileGroupBy(row);
469
+ const tableRef = useRef(null);
470
+ const desktopGroups = isMdUp && levels.length > 0 ? groupRows(slice, levels) : null;
471
+ const bandTops = useGroupBandTops(
472
+ tableRef,
473
+ !!desktopGroups,
474
+ `${levels.length}:${leadingRowSticky && !!leadingRow}:${desktopGroups?.[0]?.key ?? ""}`
475
+ );
476
+ const mobileLevel = typeof mobileGroupBy === "function" ? null : mobileGroupBy ?? null;
477
+ const mobileKey = typeof mobileGroupBy === "function" ? mobileGroupBy : mobileGroupBy?.key;
478
+ const mobileGroups = mobileKey ? mobileSlice.reduce((acc, row) => {
479
+ const key = mobileKey(row);
441
480
  const last = acc[acc.length - 1];
442
481
  if (last && last.key === key) last.rows.push(row);
443
482
  else acc.push({ key, rows: [row] });
@@ -552,8 +591,11 @@ function DataTable({
552
591
  ),
553
592
  "aria-busy": isLoading || void 0,
554
593
  children: [
555
- mobileGroups ? mobileGroups.map((g) => /* @__PURE__ */ jsxs(Fragment, { children: [
556
- /* @__PURE__ */ jsx("li", { className: "sticky top-0 z-10 bg-surface-2 px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-muted backdrop-blur", children: mobileGroupLabel ? mobileGroupLabel(g.key) : g.key }),
594
+ mobileGroups ? mobileGroups.map((g) => /* @__PURE__ */ jsxs(Fragment2, { children: [
595
+ /* @__PURE__ */ jsxs("li", { className: "sticky top-0 z-10 flex flex-wrap items-baseline justify-between gap-x-3 bg-surface-2 px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-muted backdrop-blur", children: [
596
+ /* @__PURE__ */ jsx("span", { className: "min-w-0", children: mobileGroupLabel ? mobileGroupLabel(g.key) : mobileLevel?.label ? mobileLevel.label(g.key, g.rows) : g.key }),
597
+ mobileLevel?.end && /* @__PURE__ */ jsx("span", { className: "ms-auto tabular-nums", children: mobileLevel.end(g.key, g.rows) })
598
+ ] }),
557
599
  g.rows.map(renderMobileRow)
558
600
  ] }, `hb-group:${g.key}`)) : mobileSlice.map(renderMobileRow),
559
601
  mobileSlice.length === 0 && /* @__PURE__ */ jsx(
@@ -573,6 +615,16 @@ function DataTable({
573
615
  className: "px-4 py-4 text-center text-xs text-placeholder",
574
616
  children: /* @__PURE__ */ jsx("bdi", { children: labels.loading })
575
617
  }
618
+ ),
619
+ loadMore && !canRevealMoreMobile && /* @__PURE__ */ jsx(
620
+ DataTableLoadMoreFooter,
621
+ {
622
+ as: "li",
623
+ loadMore,
624
+ rowCount: sorted.length,
625
+ labels,
626
+ compact
627
+ }
576
628
  )
577
629
  ]
578
630
  }
@@ -596,7 +648,7 @@ function DataTable({
596
648
  "grid grid-cols-[auto_1fr] gap-x-3",
597
649
  compact ? "gap-y-0.5 text-xs" : "gap-y-1 text-sm"
598
650
  ),
599
- children: mobileColumns.filter((c) => c.footer).map((col) => /* @__PURE__ */ jsxs(Fragment, { children: [
651
+ children: mobileColumns.filter((c) => c.footer).map((col) => /* @__PURE__ */ jsxs(Fragment2, { children: [
600
652
  /* @__PURE__ */ jsx("dt", { className: "self-center text-xs uppercase tracking-wide text-muted", children: col.header }),
601
653
  /* @__PURE__ */ jsx("dd", { className: "min-w-0 self-center font-semibold text-primary", children: footerValue(col) })
602
654
  ] }, col.key))
@@ -605,7 +657,7 @@ function DataTable({
605
657
  ]
606
658
  }
607
659
  ),
608
- isServer && /* @__PURE__ */ jsx(
660
+ isServer && !loadMore && /* @__PURE__ */ jsx(
609
661
  Pagination,
610
662
  {
611
663
  page: safePage,
@@ -633,7 +685,7 @@ function DataTable({
633
685
  ] }),
634
686
  isMdUp && /* @__PURE__ */ jsxs("div", { className: cn("flex", fillHeight && "flex-1"), children: [
635
687
  /* @__PURE__ */ jsxs("div", { className: cn("relative min-w-0 flex-1", fillHeight && "flex flex-col"), children: [
636
- /* @__PURE__ */ jsx(
688
+ /* @__PURE__ */ jsxs(
637
689
  "div",
638
690
  {
639
691
  ref: desktopScroller,
@@ -641,257 +693,294 @@ function DataTable({
641
693
  className: cn("overflow-auto [scrollbar-gutter:stable]", fillHeight && "min-h-0 grow basis-48"),
642
694
  ...edgeFadeProps,
643
695
  style: fillHeight ? edgeFadeProps.style : { ...edgeFadeProps.style, maxHeight: maxBodyHeight },
644
- children: /* @__PURE__ */ jsxs(
645
- "table",
646
- {
647
- className: cn("w-full", compact ? "text-xs" : "text-sm"),
648
- "aria-label": labels.table,
649
- "aria-busy": isLoading || void 0,
650
- children: [
651
- /* @__PURE__ */ jsx(
652
- DataTableHead,
653
- {
654
- visibleColumns,
655
- selection,
656
- compact,
657
- labels,
658
- locale,
659
- isServer,
660
- hasSortCallback: !!onSortsChange,
661
- hasFilterCallback: !!onFiltersChange,
662
- sorts,
663
- filters,
664
- selectOptionsByKey,
665
- multiSort,
666
- resizable,
667
- widths,
668
- setWidths,
669
- headRefs,
670
- toggleSort,
671
- setFilterValue,
672
- clearFilter,
673
- startResize,
674
- moveResize,
675
- finishResize
676
- }
677
- ),
678
- /* @__PURE__ */ jsxs(
679
- "tbody",
680
- {
681
- className: cn(isLoading && slice.length > 0 && "opacity-60 transition-opacity"),
682
- children: [
683
- leadingRow && /* @__PURE__ */ jsx("tr", { className: "border-t border-subtle", children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0", children: leadingRow }) }),
684
- slice.map((row, rowIndex) => {
685
- const expanded = isExpanded?.(row) ?? false;
686
- const expansion = expanded ? expandedRow?.(row) : null;
687
- const rowInteractive = !!onRowClick || !!selection;
688
- const href = rowHref?.(row);
689
- const key = rowKey(row);
690
- const detailId = `${detailIdBase}-${rowIndex}`;
691
- const disclosure = rowsFocusable && expandedRow ? /* @__PURE__ */ jsx(
692
- "button",
696
+ children: [
697
+ /* @__PURE__ */ jsxs(
698
+ "table",
699
+ {
700
+ ref: tableRef,
701
+ className: cn("w-full", compact ? "text-xs" : "text-sm"),
702
+ "data-fit-grow": growDrawn ? "" : void 0,
703
+ "aria-label": labels.table,
704
+ "aria-busy": isLoading || void 0,
705
+ children: [
706
+ /* @__PURE__ */ jsx(
707
+ DataTableHead,
708
+ {
709
+ visibleColumns,
710
+ selection,
711
+ compact,
712
+ labels,
713
+ locale,
714
+ isServer,
715
+ hasSortCallback: !!onSortsChange,
716
+ hasFilterCallback: !!onFiltersChange,
717
+ sorts,
718
+ filters,
719
+ selectOptionsByKey,
720
+ multiSort,
721
+ resizable,
722
+ widths,
723
+ setWidths,
724
+ headRefs,
725
+ toggleSort,
726
+ setFilterValue,
727
+ clearFilter,
728
+ startResize,
729
+ moveResize,
730
+ finishResize
731
+ }
732
+ ),
733
+ (() => {
734
+ const bodyClass = cn(isLoading && slice.length > 0 && "opacity-60 transition-opacity");
735
+ const leading = leadingRow ? /* @__PURE__ */ jsx(DataTableLeadingRow, { colSpan: totalColSpan, sticky: leadingRowSticky, children: leadingRow }) : null;
736
+ const renderRow = (row, rowIndex) => {
737
+ const expanded = isExpanded?.(row) ?? false;
738
+ const expansion = expanded ? expandedRow?.(row) : null;
739
+ const rowInteractive = !!onRowClick || !!selection;
740
+ const href = rowHref?.(row);
741
+ const key = rowKey(row);
742
+ const detailId = `${detailIdBase}-${rowIndex}`;
743
+ const disclosure = rowsFocusable && expandedRow ? /* @__PURE__ */ jsx(
744
+ "button",
745
+ {
746
+ type: "button",
747
+ tabIndex: -1,
748
+ className: "sr-only-fixed",
749
+ "aria-label": labels.rowDetails,
750
+ "aria-expanded": expanded,
751
+ "aria-controls": expansion ? detailId : void 0,
752
+ onClick: () => onRowClick(row)
753
+ }
754
+ ) : null;
755
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
756
+ /* @__PURE__ */ jsxs(
757
+ "tr",
693
758
  {
694
- type: "button",
695
- tabIndex: -1,
696
- className: "sr-only-fixed",
697
- "aria-label": labels.rowDetails,
698
- "aria-expanded": expanded,
699
- "aria-controls": expansion ? detailId : void 0,
700
- onClick: () => onRowClick(row)
701
- }
702
- ) : null;
703
- return /* @__PURE__ */ jsxs(Fragment, { children: [
704
- /* @__PURE__ */ jsxs(
705
- "tr",
706
- {
707
- ...cleanAttrs(rowAttributes?.(row)),
708
- "data-row-nav": rowsFocusable ? "" : void 0,
709
- tabIndex: rowsFocusable ? key === tabRowKey ? 0 : -1 : void 0,
710
- onFocus: rowsFocusable ? (e) => {
711
- if (e.target === e.currentTarget) setFocusRowKey(key);
712
- } : void 0,
713
- onKeyDown: rowsFocusable ? (e) => {
714
- if (e.target !== e.currentTarget) return;
715
- if (e.altKey || e.ctrlKey || e.metaKey) return;
716
- if (e.key === "Enter" || e.key === " ") {
717
- e.preventDefault();
718
- onRowClick(row);
759
+ ...cleanAttrs(rowAttributes?.(row)),
760
+ "data-row-nav": rowsFocusable ? "" : void 0,
761
+ tabIndex: rowsFocusable ? key === tabRowKey ? 0 : -1 : void 0,
762
+ onFocus: rowsFocusable ? (e) => {
763
+ if (e.target === e.currentTarget) setFocusRowKey(key);
764
+ } : void 0,
765
+ onKeyDown: rowsFocusable ? (e) => {
766
+ if (e.target !== e.currentTarget) return;
767
+ if (e.altKey || e.ctrlKey || e.metaKey) return;
768
+ if (e.key === "Enter" || e.key === " ") {
769
+ e.preventDefault();
770
+ onRowClick(row);
771
+ return;
772
+ }
773
+ if (e.shiftKey) return;
774
+ if (moveRowFocus(e.currentTarget, e.key)) e.preventDefault();
775
+ } : void 0,
776
+ className: cn(
777
+ "border-t border-subtle",
778
+ rowInteractive && "cursor-pointer hover:bg-hover",
779
+ // The kit's focus frame width (§5, `--focus-ring-width`), drawn as an
780
+ // OUTLINE rather than FOCUS_RING's box-shadow: a shadow on a `<tr>`
781
+ // is not painted by every engine, an outline is.
782
+ rowsFocusable && "focus-visible:bg-hover focus-visible:outline-focus focus-visible:-outline-offset-2 focus-visible:outline-brand",
783
+ rowClassName?.(row)
784
+ ),
785
+ onClick: rowInteractive ? (e) => {
786
+ if (fromOwnControl(e.target, e.currentTarget)) return;
787
+ if (fromOutside(e.target, e.currentTarget)) return;
788
+ if (selection && canSelect(row)) {
789
+ if (e.shiftKey) {
790
+ window.getSelection?.()?.removeAllRanges();
791
+ if (selectRange(rowIndex)) return;
792
+ selectionAnchor.current = rowKey(row);
793
+ selection.onToggle(row, !selection.isSelected(row));
719
794
  return;
720
795
  }
721
- if (e.shiftKey) return;
722
- if (moveRowFocus(e.currentTarget, e.key)) e.preventDefault();
723
- } : void 0,
724
- className: cn(
725
- "border-t border-subtle",
726
- rowInteractive && "cursor-pointer hover:bg-hover",
727
- // The kit's focus frame width (§5, `--focus-ring-width`), drawn as an
728
- // OUTLINE rather than FOCUS_RING's box-shadow: a shadow on a `<tr>`
729
- // is not painted by every engine, an outline is.
730
- rowsFocusable && "focus-visible:bg-hover focus-visible:outline-focus focus-visible:-outline-offset-2 focus-visible:outline-brand",
731
- rowClassName?.(row)
732
- ),
733
- onClick: rowInteractive ? (e) => {
734
- if (fromOwnControl(e.target, e.currentTarget)) return;
735
- if (fromOutside(e.target, e.currentTarget)) return;
736
- if (selection && canSelect(row)) {
737
- if (e.shiftKey) {
738
- window.getSelection?.()?.removeAllRanges();
739
- if (selectRange(rowIndex)) return;
740
- selectionAnchor.current = rowKey(row);
741
- selection.onToggle(row, !selection.isSelected(row));
742
- return;
743
- }
744
- if (e.metaKey || e.ctrlKey) {
745
- selectionAnchor.current = rowKey(row);
746
- selection.onToggle(row, !selection.isSelected(row));
747
- return;
748
- }
796
+ if (e.metaKey || e.ctrlKey) {
797
+ selectionAnchor.current = rowKey(row);
798
+ selection.onToggle(row, !selection.isSelected(row));
799
+ return;
749
800
  }
750
- onRowClick?.(row);
751
- } : void 0,
752
- children: [
753
- selection && /* @__PURE__ */ jsx(
801
+ }
802
+ onRowClick?.(row);
803
+ } : void 0,
804
+ children: [
805
+ selection && /* @__PURE__ */ jsx(
806
+ "td",
807
+ {
808
+ className: cn("align-top", compact ? "w-8 px-2 py-1" : "w-10 px-3 py-2"),
809
+ onClick: (e) => e.stopPropagation(),
810
+ children: canSelect(row) && /* @__PURE__ */ jsx(
811
+ "input",
812
+ {
813
+ type: "checkbox",
814
+ className: cn("accent-indigo-600", compact ? "size-3.5" : "size-4"),
815
+ checked: selection.isSelected(row),
816
+ onClick: (e) => {
817
+ if (e.shiftKey && selectRange(rowIndex)) {
818
+ e.preventDefault();
819
+ rangeClick.current = e.nativeEvent;
820
+ }
821
+ },
822
+ onChange: (e) => {
823
+ if (rangeClick.current && e.nativeEvent === rangeClick.current) {
824
+ rangeClick.current = null;
825
+ return;
826
+ }
827
+ selectionAnchor.current = rowKey(row);
828
+ selection.onToggle(row, e.target.checked);
829
+ },
830
+ "aria-label": labels.selectRow
831
+ }
832
+ )
833
+ }
834
+ ),
835
+ visibleColumns.map((col, colIndex) => {
836
+ const width = widths[col.key];
837
+ const own = splitCellProps(col.cellProps?.(row));
838
+ const pinned = width ? { width, minWidth: width, maxWidth: width } : void 0;
839
+ return /* @__PURE__ */ createElement(
754
840
  "td",
755
841
  {
756
- className: cn("align-top", compact ? "w-8 px-2 py-1" : "w-10 px-3 py-2"),
757
- onClick: (e) => e.stopPropagation(),
758
- children: canSelect(row) && /* @__PURE__ */ jsx(
759
- "input",
760
- {
761
- type: "checkbox",
762
- className: cn("accent-indigo-600", compact ? "size-3.5" : "size-4"),
763
- checked: selection.isSelected(row),
764
- onClick: (e) => {
765
- if (e.shiftKey && selectRange(rowIndex)) {
766
- e.preventDefault();
767
- rangeClick.current = e.nativeEvent;
768
- }
769
- },
770
- onChange: (e) => {
771
- if (rangeClick.current && e.nativeEvent === rangeClick.current) {
772
- rangeClick.current = null;
773
- return;
774
- }
775
- selectionAnchor.current = rowKey(row);
776
- selection.onToggle(row, e.target.checked);
777
- },
778
- "aria-label": labels.selectRow
779
- }
842
+ ...own.attrs,
843
+ key: col.key,
844
+ "data-col": col.key,
845
+ style: own.style || pinned ? { ...own.style, ...pinned } : void 0,
846
+ className: cn(
847
+ "align-top",
848
+ compact ? "px-2 py-1" : "px-3 py-2",
849
+ width && "overflow-hidden text-ellipsis",
850
+ col.grow ? GROW_CELL_CLASS : growDrawn && GROW_PEER_CELL_CLASS,
851
+ logicalAlign(own.className),
852
+ logicalAlign(col.className)
780
853
  )
781
- }
782
- ),
783
- visibleColumns.map((col, colIndex) => {
784
- const width = widths[col.key];
785
- const own = splitCellProps(col.cellProps?.(row));
786
- const pinned = width ? { width, minWidth: width, maxWidth: width } : void 0;
787
- return /* @__PURE__ */ createElement(
788
- "td",
789
- {
790
- ...own.attrs,
791
- key: col.key,
792
- "data-col": col.key,
793
- style: own.style || pinned ? { ...own.style, ...pinned } : void 0,
794
- className: cn(
795
- "align-top",
796
- compact ? "px-2 py-1" : "px-3 py-2",
797
- width && "overflow-hidden text-ellipsis",
798
- logicalAlign(own.className),
799
- logicalAlign(col.className)
800
- )
801
- },
802
- colIndex === 0 && disclosure,
803
- col === linkColumn && href ? (
804
- // No `onActivate`: the click is left to bubble to the
805
- // <tr> handler above, which already owns expand-vs-select.
806
- // Calling it here as well would toggle the row twice.
807
- /* @__PURE__ */ jsx(RowLink, { href, className: "block", "data-row-link": "", children: col.cell(row) })
808
- ) : col.cell(row)
809
- );
810
- })
811
- ]
812
- }
813
- ),
814
- expansion && /* @__PURE__ */ jsx("tr", { id: disclosure ? detailId : void 0, className: "bg-surface-2 border-t border-subtle", children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0", children: /* @__PURE__ */ jsx("div", { className: "w-0 min-w-full", children: /* @__PURE__ */ jsx(
815
- "div",
816
- {
817
- className: cn(
818
- "sticky start-0 w-[var(--dt-visible-w,100%)] max-w-full",
819
- compact ? "px-2 py-2" : "px-3 py-3"
820
- ),
821
- children: expansion
822
- }
823
- ) }) }) })
824
- ] }, key);
825
- }),
826
- slice.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
827
- "td",
828
- {
829
- colSpan: totalColSpan,
830
- className: cn("text-center text-muted", compact ? "px-2 py-2" : "px-3 py-4"),
831
- children: isLoading ? /* @__PURE__ */ jsx(LoadingText, { label: labels.loading }) : empty ?? labels.empty
832
- }
833
- ) })
834
- ]
835
- }
836
- ),
837
- showFooter && // Sticky to the scroller's bottom edge for the same reason the header is
838
- // sticky to its top: the body scrolls inside a bounded wrapper, and a
839
- // total that scrolls away with the last row is a total nobody sees on a
840
- // 25-row page. Each cell carries its own background and its own `sticky`,
841
- // as the header's do — `position: sticky` on a `<tfoot>` is not reliable.
842
- /* @__PURE__ */ jsx(
843
- "tfoot",
844
- {
845
- "data-table-footer": "",
846
- className: cn(
847
- "font-semibold text-primary",
848
- isLoading && "opacity-60 transition-opacity"
849
- ),
850
- children: /* @__PURE__ */ jsxs("tr", { children: [
851
- selection && /* @__PURE__ */ jsx(
852
- "td",
853
- {
854
- className: cn(
855
- FOOTER_CELL,
856
- compact ? "w-8 px-2 py-1" : "w-10 px-3 py-2"
857
- )
858
- }
854
+ },
855
+ colIndex === 0 && disclosure,
856
+ col === linkColumn && href ? (
857
+ // No `onActivate`: the click is left to bubble to the
858
+ // <tr> handler above, which already owns expand-vs-select.
859
+ // Calling it here as well would toggle the row twice.
860
+ /* @__PURE__ */ jsx(RowLink, { href, className: "block", "data-row-link": "", children: col.cell(row) })
861
+ ) : col.cell(row)
862
+ );
863
+ })
864
+ ]
865
+ }
866
+ ),
867
+ expansion && /* @__PURE__ */ jsx("tr", { id: disclosure ? detailId : void 0, className: "bg-surface-2 border-t border-subtle", children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0", children: /* @__PURE__ */ jsx("div", { className: "w-0 min-w-full", children: /* @__PURE__ */ jsx(
868
+ "div",
869
+ {
870
+ className: cn(
871
+ "sticky start-0 w-[var(--dt-visible-w,100%)] max-w-full",
872
+ compact ? "px-2 py-2" : "px-3 py-3"
873
+ ),
874
+ children: expansion
875
+ }
876
+ ) }) }) })
877
+ ] }, key);
878
+ };
879
+ const emptyRow = slice.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
880
+ "td",
881
+ {
882
+ colSpan: totalColSpan,
883
+ className: cn("text-center text-muted", compact ? "px-2 py-2" : "px-3 py-4"),
884
+ children: isLoading ? /* @__PURE__ */ jsx(LoadingText, { label: labels.loading }) : empty ?? labels.empty
885
+ }
886
+ ) }) : null;
887
+ if (!desktopGroups) {
888
+ return /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
889
+ leading,
890
+ slice.map(renderRow),
891
+ emptyRow
892
+ ] });
893
+ }
894
+ const band = (level, def, g) => /* @__PURE__ */ jsx(
895
+ DataTableGroupBand,
896
+ {
897
+ level,
898
+ colSpan: totalColSpan,
899
+ top: level === 1 ? bandTops[0] : bandTops[1],
900
+ compact,
901
+ label: def.label ? def.label(g.key, g.rows) : g.key,
902
+ end: def.end?.(g.key, g.rows)
903
+ }
904
+ );
905
+ const rowsOf = (g) => g.rows.map((r, i) => renderRow(r, g.start + i));
906
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
907
+ (leading || emptyRow) && /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
908
+ leading,
909
+ emptyRow
910
+ ] }),
911
+ desktopGroups.map(
912
+ (g1) => g1.children ? g1.children.map((g2, j) => /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
913
+ j === 0 && band(1, levels[0], g1),
914
+ band(2, levels[1], g2),
915
+ rowsOf(g2)
916
+ ] }, `g:${g1.key}:${g2.key}:${g2.start}`)) : /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
917
+ band(1, levels[0], g1),
918
+ rowsOf(g1)
919
+ ] }, `g:${g1.key}:${g1.start}`)
920
+ )
921
+ ] });
922
+ })(),
923
+ showFooter && // Sticky to the scroller's bottom edge for the same reason the header is
924
+ // sticky to its top: the body scrolls inside a bounded wrapper, and a
925
+ // total that scrolls away with the last row is a total nobody sees on a
926
+ // 25-row page. Each cell carries its own background and its own `sticky`,
927
+ // as the header's do — `position: sticky` on a `<tfoot>` is not reliable.
928
+ /* @__PURE__ */ jsx(
929
+ "tfoot",
930
+ {
931
+ "data-table-footer": "",
932
+ className: cn(
933
+ "font-semibold text-primary",
934
+ isLoading && "opacity-60 transition-opacity"
859
935
  ),
860
- visibleColumns.map((col) => {
861
- const width = widths[col.key];
862
- const pinned = width ? { width, minWidth: width, maxWidth: width } : void 0;
863
- const isLabel = col === footerLabelCol;
864
- const Cell = isLabel ? "th" : "td";
865
- return /* @__PURE__ */ jsx(
866
- Cell,
936
+ children: /* @__PURE__ */ jsxs("tr", { children: [
937
+ selection && /* @__PURE__ */ jsx(
938
+ "td",
867
939
  {
868
- "data-col": col.key,
869
- scope: isLabel ? "row" : void 0,
870
- style: pinned,
871
940
  className: cn(
872
941
  FOOTER_CELL,
873
- "align-top",
874
- compact ? "px-2 py-1" : "px-3 py-2",
875
- width && "overflow-hidden text-ellipsis",
876
- // A `<th>` centres by default; the label reads from the start
877
- // like the cells above it, unless the column says otherwise.
878
- isLabel && "text-start",
879
- logicalAlign(col.className)
880
- ),
881
- children: col.footer ? footerValue(col) : isLabel ? footerLabel : null
882
- },
883
- col.key
884
- );
885
- })
886
- ] })
887
- }
888
- )
889
- ]
890
- }
891
- )
942
+ compact ? "w-8 px-2 py-1" : "w-10 px-3 py-2"
943
+ )
944
+ }
945
+ ),
946
+ visibleColumns.map((col) => {
947
+ const width = widths[col.key];
948
+ const pinned = width ? { width, minWidth: width, maxWidth: width } : void 0;
949
+ const isLabel = col === footerLabelCol;
950
+ const Cell = isLabel ? "th" : "td";
951
+ return /* @__PURE__ */ jsx(
952
+ Cell,
953
+ {
954
+ "data-col": col.key,
955
+ scope: isLabel ? "row" : void 0,
956
+ style: pinned,
957
+ className: cn(
958
+ FOOTER_CELL,
959
+ "align-top",
960
+ compact ? "px-2 py-1" : "px-3 py-2",
961
+ width && "overflow-hidden text-ellipsis",
962
+ // A `<th>` centres by default; the label reads from the start
963
+ // like the cells above it, unless the column says otherwise.
964
+ isLabel && "text-start",
965
+ col.grow ? GROW_CELL_CLASS : growDrawn && GROW_PEER_CELL_CLASS,
966
+ logicalAlign(col.className)
967
+ ),
968
+ children: col.footer ? footerValue(col) : isLabel ? footerLabel : null
969
+ },
970
+ col.key
971
+ );
972
+ })
973
+ ] })
974
+ }
975
+ )
976
+ ]
977
+ }
978
+ ),
979
+ loadMore && /* @__PURE__ */ jsx(DataTableLoadMoreFooter, { loadMore, rowCount: sorted.length, labels, compact })
980
+ ]
892
981
  }
893
982
  ),
894
- !unpaged && /* @__PURE__ */ jsx(
983
+ !unpaged && !loadMore && /* @__PURE__ */ jsx(
895
984
  Pagination,
896
985
  {
897
986
  page: safePage,
@@ -916,6 +1005,8 @@ function DataTable({
916
1005
  columns,
917
1006
  hiddenCols,
918
1007
  setHiddenCols,
1008
+ autoHidden,
1009
+ keepColumn,
919
1010
  showSettings,
920
1011
  setShowSettings,
921
1012
  columnsCountLabel,