@eifi1/ui-kit 0.47.0 → 0.48.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 (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 +95 -2
  36. package/dist/components/data-table.js +360 -267
  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 +279 -54
  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,
@@ -73,6 +84,7 @@ function DataTable({
73
84
  fillHeight = false,
74
85
  maxBodyHeight = "calc(100dvh - 12rem)",
75
86
  serverPagination,
87
+ serverRows: serverRowsProp,
76
88
  filters: filtersProp,
77
89
  onFiltersChange,
78
90
  sorts: sortsProp,
@@ -94,6 +106,9 @@ function DataTable({
94
106
  mobileSwipeActions,
95
107
  desktopSwipeActions,
96
108
  leadingRow,
109
+ leadingRowSticky = false,
110
+ groupBy,
111
+ loadMore,
97
112
  density = "comfortable",
98
113
  sortCycle = "tri",
99
114
  chrome = "full",
@@ -115,6 +130,7 @@ function DataTable({
115
130
  const resizable = resizableProp ?? !minimal;
116
131
  const multiSort = multiSortProp ?? !minimal;
117
132
  const isServer = !!serverPagination;
133
+ const serverRows = isServer || (serverRowsProp ?? !!loadMore);
118
134
  const isLoading = !!serverPagination?.isLoading;
119
135
  const labelOverrides = useKitLabelOverrides("dataTable");
120
136
  const providerStorageKeyPrefix = useKitTableStorageKeyPrefix();
@@ -160,7 +176,13 @@ function DataTable({
160
176
  // Ignored in `serverPagination` mode, where the owner's is the page size.
161
177
  pageSizeProp: isServer ? void 0 : pageSizeProp
162
178
  });
163
- const widths = useMemo(() => resizable ? storedWidths : {}, [resizable, storedWidths]);
179
+ const widths = useMemo(() => {
180
+ if (!resizable) return {};
181
+ const grow = columns.find((c) => c.grow)?.key;
182
+ if (!grow || !(grow in storedWidths)) return storedWidths;
183
+ const { [grow]: _ignored, ...rest } = storedWidths;
184
+ return rest;
185
+ }, [resizable, storedWidths, columns]);
164
186
  const hiddenCols = useMemo(
165
187
  () => columnSettings ? storedHiddenCols : /* @__PURE__ */ new Set(),
166
188
  [columnSettings, storedHiddenCols]
@@ -190,7 +212,7 @@ function DataTable({
190
212
  return map;
191
213
  }, [columnSig, selectOptionsSig, rows]);
192
214
  const filtered = useMemo(() => {
193
- if (isServer) return rows;
215
+ if (serverRows) return rows;
194
216
  let result = rows;
195
217
  for (const col of columns) {
196
218
  const state = filters[col.key];
@@ -198,9 +220,9 @@ function DataTable({
198
220
  result = result.filter((row) => rowMatches(col, row, state, locale));
199
221
  }
200
222
  return result;
201
- }, [rows, columnSig, filters, isServer, locale]);
223
+ }, [rows, columnSig, filters, serverRows, locale]);
202
224
  const sorted = useMemo(() => {
203
- if (isServer) return filtered;
225
+ if (serverRows) return filtered;
204
226
  const chain = sorts.map((s) => {
205
227
  const col = columns.find((c) => c.key === s.key);
206
228
  return col?.sortBy ? { sortBy: col.sortBy, dir: s.dir } : null;
@@ -220,8 +242,8 @@ function DataTable({
220
242
  return 0;
221
243
  });
222
244
  return copy;
223
- }, [filtered, sorts, columnSig, isServer]);
224
- const unpaged = !isServer && !paginated;
245
+ }, [filtered, sorts, columnSig, serverRows]);
246
+ const unpaged = !isServer && (!paginated || !!loadMore || serverRows);
225
247
  const effectivePageSize = isServer ? serverPagination.pageSize : unpaged || pageSize === Infinity ? sorted.length || 1 : pageSize;
226
248
  const paginationTotal = isServer ? serverPagination.total : sorted.length;
227
249
  const paginationPageSize = isServer ? serverPagination.pageSize : pageSize;
@@ -269,7 +291,7 @@ function DataTable({
269
291
  setPage(0);
270
292
  };
271
293
  const clearAllFilters = () => {
272
- const keys = columns.filter((col) => !isServer || onFiltersChange ? resolveFilter(col) : null).map((col) => col.key);
294
+ const keys = columns.filter((col) => !serverRows || onFiltersChange ? resolveFilter(col) : null).map((col) => col.key);
273
295
  if (!keys.length) return;
274
296
  if (onFiltersChange) {
275
297
  const rest = { ...filters };
@@ -289,25 +311,34 @@ function DataTable({
289
311
  useEffect(() => {
290
312
  if (announcedFilterKey.current === filterKey) return;
291
313
  announcedFilterKey.current = filterKey;
292
- if (isServer) return;
314
+ if (serverRows) return;
293
315
  announce(labels.filterResults(filtered.length, rows.length));
294
- }, [filterKey, filtered.length, rows.length, isServer, announce, labels]);
316
+ }, [filterKey, filtered.length, rows.length, serverRows, announce, labels]);
295
317
  const goToPage = (p) => {
296
318
  if (isServer) serverPagination.onPageChange(p);
297
319
  else setPage(p);
298
320
  announce(labels.pageChanged(p + 1, totalPages));
299
321
  };
322
+ const { autoHidden, keep: keepColumn } = useColumnFit({
323
+ columns,
324
+ hiddenCols,
325
+ headRefs,
326
+ scroller: () => desktopScroller.current,
327
+ enabled: !usePhoneLayout()
328
+ });
300
329
  const visibleColumns = useMemo(
301
- () => columns.filter((c) => !hiddenCols.has(c.key)),
302
- [columns, hiddenCols]
330
+ () => columns.filter((c) => !hiddenCols.has(c.key) && !autoHidden.has(c.key)),
331
+ [columns, hiddenCols, autoHidden]
303
332
  );
333
+ const growDrawn = visibleColumns.some((c) => c.grow);
304
334
  const linkColumn = rowHref ? visibleColumns.find((c) => c.mobilePrimary && !c.noRowLink) ?? visibleColumns.find((c) => !c.noRowLink) : void 0;
305
335
  const { startResize, moveResize, finishResize, autoSizeAll } = useColumnResize({
306
336
  headRefs,
307
337
  widths,
308
338
  setWidths,
309
339
  columns,
310
- hiddenCols
340
+ // The columns left out for width have no header to measure either.
341
+ hiddenCols: autoHidden.size > 0 ? /* @__PURE__ */ new Set([...hiddenCols, ...autoHidden]) : hiddenCols
311
342
  });
312
343
  const visibleCount = visibleColumns.length;
313
344
  const totalCount = columns.length;
@@ -323,7 +354,8 @@ function DataTable({
323
354
  const columnsCountLabel = labels.columnsCount(visibleCount - ownVisible, totalCount - ownColumns);
324
355
  const isMdUp = !usePhoneLayout();
325
356
  const desktopScroller = useRef(null);
326
- const measuresVisibleWidth = isMdUp && !!expandedRow;
357
+ const levels = groupLevels(groupBy);
358
+ const measuresVisibleWidth = isMdUp && (!!expandedRow || levels.length > 0 || !!loadMore);
327
359
  useEffect(() => {
328
360
  const el = desktopScroller.current;
329
361
  if (!measuresVisibleWidth || !el || typeof ResizeObserver === "undefined") return;
@@ -436,8 +468,17 @@ function DataTable({
436
468
  },
437
469
  rowKey(row)
438
470
  );
439
- const mobileGroups = mobileGroupBy ? mobileSlice.reduce((acc, row) => {
440
- const key = mobileGroupBy(row);
471
+ const tableRef = useRef(null);
472
+ const desktopGroups = isMdUp && levels.length > 0 ? groupRows(slice, levels) : null;
473
+ const bandTops = useGroupBandTops(
474
+ tableRef,
475
+ !!desktopGroups,
476
+ `${levels.length}:${leadingRowSticky && !!leadingRow}:${desktopGroups?.[0]?.key ?? ""}`
477
+ );
478
+ const mobileLevel = typeof mobileGroupBy === "function" ? null : mobileGroupBy ?? null;
479
+ const mobileKey = typeof mobileGroupBy === "function" ? mobileGroupBy : mobileGroupBy?.key;
480
+ const mobileGroups = mobileKey ? mobileSlice.reduce((acc, row) => {
481
+ const key = mobileKey(row);
441
482
  const last = acc[acc.length - 1];
442
483
  if (last && last.key === key) last.rows.push(row);
443
484
  else acc.push({ key, rows: [row] });
@@ -476,13 +517,13 @@ function DataTable({
476
517
  }
477
518
  ),
478
519
  !isMdUp && /* @__PURE__ */ jsxs("div", { ref: mobileRootRef, className: cn(fillHeight && "flex min-h-0 flex-1 flex-col"), children: [
479
- (!isServer || onFiltersChange) && /* @__PURE__ */ jsx(
520
+ (!serverRows || onFiltersChange) && /* @__PURE__ */ jsx(
480
521
  MobileFilters,
481
522
  {
482
523
  columns,
483
524
  filters,
484
525
  selectOptionsByKey,
485
- isServer,
526
+ isServer: serverRows,
486
527
  hasFilterCallback: !!onFiltersChange,
487
528
  onSetFilter: setFilterValue,
488
529
  onClearFilter: clearFilter,
@@ -494,7 +535,7 @@ function DataTable({
494
535
  mobileSort && /* @__PURE__ */ jsx(
495
536
  MobileSort,
496
537
  {
497
- columns: visibleColumns.filter((c) => (!isServer || !!onSortsChange) && !!c.sortBy),
538
+ columns: visibleColumns.filter((c) => (!serverRows || !!onSortsChange) && !!c.sortBy),
498
539
  sorts,
499
540
  sortCycle,
500
541
  compact,
@@ -552,8 +593,11 @@ function DataTable({
552
593
  ),
553
594
  "aria-busy": isLoading || void 0,
554
595
  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 }),
596
+ mobileGroups ? mobileGroups.map((g) => /* @__PURE__ */ jsxs(Fragment2, { children: [
597
+ /* @__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: [
598
+ /* @__PURE__ */ jsx("span", { className: "min-w-0", children: mobileGroupLabel ? mobileGroupLabel(g.key) : mobileLevel?.label ? mobileLevel.label(g.key, g.rows) : g.key }),
599
+ mobileLevel?.end && /* @__PURE__ */ jsx("span", { className: "ms-auto tabular-nums", children: mobileLevel.end(g.key, g.rows) })
600
+ ] }),
557
601
  g.rows.map(renderMobileRow)
558
602
  ] }, `hb-group:${g.key}`)) : mobileSlice.map(renderMobileRow),
559
603
  mobileSlice.length === 0 && /* @__PURE__ */ jsx(
@@ -573,6 +617,16 @@ function DataTable({
573
617
  className: "px-4 py-4 text-center text-xs text-placeholder",
574
618
  children: /* @__PURE__ */ jsx("bdi", { children: labels.loading })
575
619
  }
620
+ ),
621
+ loadMore && !canRevealMoreMobile && /* @__PURE__ */ jsx(
622
+ DataTableLoadMoreFooter,
623
+ {
624
+ as: "li",
625
+ loadMore,
626
+ rowCount: sorted.length,
627
+ labels,
628
+ compact
629
+ }
576
630
  )
577
631
  ]
578
632
  }
@@ -596,7 +650,7 @@ function DataTable({
596
650
  "grid grid-cols-[auto_1fr] gap-x-3",
597
651
  compact ? "gap-y-0.5 text-xs" : "gap-y-1 text-sm"
598
652
  ),
599
- children: mobileColumns.filter((c) => c.footer).map((col) => /* @__PURE__ */ jsxs(Fragment, { children: [
653
+ children: mobileColumns.filter((c) => c.footer).map((col) => /* @__PURE__ */ jsxs(Fragment2, { children: [
600
654
  /* @__PURE__ */ jsx("dt", { className: "self-center text-xs uppercase tracking-wide text-muted", children: col.header }),
601
655
  /* @__PURE__ */ jsx("dd", { className: "min-w-0 self-center font-semibold text-primary", children: footerValue(col) })
602
656
  ] }, col.key))
@@ -605,7 +659,7 @@ function DataTable({
605
659
  ]
606
660
  }
607
661
  ),
608
- isServer && /* @__PURE__ */ jsx(
662
+ isServer && !loadMore && /* @__PURE__ */ jsx(
609
663
  Pagination,
610
664
  {
611
665
  page: safePage,
@@ -633,7 +687,7 @@ function DataTable({
633
687
  ] }),
634
688
  isMdUp && /* @__PURE__ */ jsxs("div", { className: cn("flex", fillHeight && "flex-1"), children: [
635
689
  /* @__PURE__ */ jsxs("div", { className: cn("relative min-w-0 flex-1", fillHeight && "flex flex-col"), children: [
636
- /* @__PURE__ */ jsx(
690
+ /* @__PURE__ */ jsxs(
637
691
  "div",
638
692
  {
639
693
  ref: desktopScroller,
@@ -641,257 +695,294 @@ function DataTable({
641
695
  className: cn("overflow-auto [scrollbar-gutter:stable]", fillHeight && "min-h-0 grow basis-48"),
642
696
  ...edgeFadeProps,
643
697
  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",
698
+ children: [
699
+ /* @__PURE__ */ jsxs(
700
+ "table",
701
+ {
702
+ ref: tableRef,
703
+ className: cn("w-full", compact ? "text-xs" : "text-sm"),
704
+ "data-fit-grow": growDrawn ? "" : void 0,
705
+ "aria-label": labels.table,
706
+ "aria-busy": isLoading || void 0,
707
+ children: [
708
+ /* @__PURE__ */ jsx(
709
+ DataTableHead,
710
+ {
711
+ visibleColumns,
712
+ selection,
713
+ compact,
714
+ labels,
715
+ locale,
716
+ isServer: serverRows,
717
+ hasSortCallback: !!onSortsChange,
718
+ hasFilterCallback: !!onFiltersChange,
719
+ sorts,
720
+ filters,
721
+ selectOptionsByKey,
722
+ multiSort,
723
+ resizable,
724
+ widths,
725
+ setWidths,
726
+ headRefs,
727
+ toggleSort,
728
+ setFilterValue,
729
+ clearFilter,
730
+ startResize,
731
+ moveResize,
732
+ finishResize
733
+ }
734
+ ),
735
+ (() => {
736
+ const bodyClass = cn(isLoading && slice.length > 0 && "opacity-60 transition-opacity");
737
+ const leading = leadingRow ? /* @__PURE__ */ jsx(DataTableLeadingRow, { colSpan: totalColSpan, sticky: leadingRowSticky, children: leadingRow }) : null;
738
+ const renderRow = (row, rowIndex) => {
739
+ const expanded = isExpanded?.(row) ?? false;
740
+ const expansion = expanded ? expandedRow?.(row) : null;
741
+ const rowInteractive = !!onRowClick || !!selection;
742
+ const href = rowHref?.(row);
743
+ const key = rowKey(row);
744
+ const detailId = `${detailIdBase}-${rowIndex}`;
745
+ const disclosure = rowsFocusable && expandedRow ? /* @__PURE__ */ jsx(
746
+ "button",
747
+ {
748
+ type: "button",
749
+ tabIndex: -1,
750
+ className: "sr-only-fixed",
751
+ "aria-label": labels.rowDetails,
752
+ "aria-expanded": expanded,
753
+ "aria-controls": expansion ? detailId : void 0,
754
+ onClick: () => onRowClick(row)
755
+ }
756
+ ) : null;
757
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
758
+ /* @__PURE__ */ jsxs(
759
+ "tr",
693
760
  {
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);
761
+ ...cleanAttrs(rowAttributes?.(row)),
762
+ "data-row-nav": rowsFocusable ? "" : void 0,
763
+ tabIndex: rowsFocusable ? key === tabRowKey ? 0 : -1 : void 0,
764
+ onFocus: rowsFocusable ? (e) => {
765
+ if (e.target === e.currentTarget) setFocusRowKey(key);
766
+ } : void 0,
767
+ onKeyDown: rowsFocusable ? (e) => {
768
+ if (e.target !== e.currentTarget) return;
769
+ if (e.altKey || e.ctrlKey || e.metaKey) return;
770
+ if (e.key === "Enter" || e.key === " ") {
771
+ e.preventDefault();
772
+ onRowClick(row);
773
+ return;
774
+ }
775
+ if (e.shiftKey) return;
776
+ if (moveRowFocus(e.currentTarget, e.key)) e.preventDefault();
777
+ } : void 0,
778
+ className: cn(
779
+ "border-t border-subtle",
780
+ rowInteractive && "cursor-pointer hover:bg-hover",
781
+ // The kit's focus frame width (§5, `--focus-ring-width`), drawn as an
782
+ // OUTLINE rather than FOCUS_RING's box-shadow: a shadow on a `<tr>`
783
+ // is not painted by every engine, an outline is.
784
+ rowsFocusable && "focus-visible:bg-hover focus-visible:outline-focus focus-visible:-outline-offset-2 focus-visible:outline-brand",
785
+ rowClassName?.(row)
786
+ ),
787
+ onClick: rowInteractive ? (e) => {
788
+ if (fromOwnControl(e.target, e.currentTarget)) return;
789
+ if (fromOutside(e.target, e.currentTarget)) return;
790
+ if (selection && canSelect(row)) {
791
+ if (e.shiftKey) {
792
+ window.getSelection?.()?.removeAllRanges();
793
+ if (selectRange(rowIndex)) return;
794
+ selectionAnchor.current = rowKey(row);
795
+ selection.onToggle(row, !selection.isSelected(row));
719
796
  return;
720
797
  }
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
- }
798
+ if (e.metaKey || e.ctrlKey) {
799
+ selectionAnchor.current = rowKey(row);
800
+ selection.onToggle(row, !selection.isSelected(row));
801
+ return;
749
802
  }
750
- onRowClick?.(row);
751
- } : void 0,
752
- children: [
753
- selection && /* @__PURE__ */ jsx(
803
+ }
804
+ onRowClick?.(row);
805
+ } : void 0,
806
+ children: [
807
+ selection && /* @__PURE__ */ jsx(
808
+ "td",
809
+ {
810
+ className: cn("align-top", compact ? "w-8 px-2 py-1" : "w-10 px-3 py-2"),
811
+ onClick: (e) => e.stopPropagation(),
812
+ children: canSelect(row) && /* @__PURE__ */ jsx(
813
+ "input",
814
+ {
815
+ type: "checkbox",
816
+ className: cn("accent-indigo-600", compact ? "size-3.5" : "size-4"),
817
+ checked: selection.isSelected(row),
818
+ onClick: (e) => {
819
+ if (e.shiftKey && selectRange(rowIndex)) {
820
+ e.preventDefault();
821
+ rangeClick.current = e.nativeEvent;
822
+ }
823
+ },
824
+ onChange: (e) => {
825
+ if (rangeClick.current && e.nativeEvent === rangeClick.current) {
826
+ rangeClick.current = null;
827
+ return;
828
+ }
829
+ selectionAnchor.current = rowKey(row);
830
+ selection.onToggle(row, e.target.checked);
831
+ },
832
+ "aria-label": labels.selectRow
833
+ }
834
+ )
835
+ }
836
+ ),
837
+ visibleColumns.map((col, colIndex) => {
838
+ const width = widths[col.key];
839
+ const own = splitCellProps(col.cellProps?.(row));
840
+ const pinned = width ? { width, minWidth: width, maxWidth: width } : void 0;
841
+ return /* @__PURE__ */ createElement(
754
842
  "td",
755
843
  {
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
- }
844
+ ...own.attrs,
845
+ key: col.key,
846
+ "data-col": col.key,
847
+ style: own.style || pinned ? { ...own.style, ...pinned } : void 0,
848
+ className: cn(
849
+ "align-top",
850
+ compact ? "px-2 py-1" : "px-3 py-2",
851
+ width && "overflow-hidden text-ellipsis",
852
+ col.grow ? GROW_CELL_CLASS : growDrawn && GROW_PEER_CELL_CLASS,
853
+ logicalAlign(own.className),
854
+ logicalAlign(col.className)
780
855
  )
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
- }
856
+ },
857
+ colIndex === 0 && disclosure,
858
+ col === linkColumn && href ? (
859
+ // No `onActivate`: the click is left to bubble to the
860
+ // <tr> handler above, which already owns expand-vs-select.
861
+ // Calling it here as well would toggle the row twice.
862
+ /* @__PURE__ */ jsx(RowLink, { href, className: "block", "data-row-link": "", children: col.cell(row) })
863
+ ) : col.cell(row)
864
+ );
865
+ })
866
+ ]
867
+ }
868
+ ),
869
+ 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(
870
+ "div",
871
+ {
872
+ className: cn(
873
+ "sticky start-0 w-[var(--dt-visible-w,100%)] max-w-full",
874
+ compact ? "px-2 py-2" : "px-3 py-3"
875
+ ),
876
+ children: expansion
877
+ }
878
+ ) }) }) })
879
+ ] }, key);
880
+ };
881
+ const emptyRow = slice.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
882
+ "td",
883
+ {
884
+ colSpan: totalColSpan,
885
+ className: cn("text-center text-muted", compact ? "px-2 py-2" : "px-3 py-4"),
886
+ children: isLoading ? /* @__PURE__ */ jsx(LoadingText, { label: labels.loading }) : empty ?? labels.empty
887
+ }
888
+ ) }) : null;
889
+ if (!desktopGroups) {
890
+ return /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
891
+ leading,
892
+ slice.map(renderRow),
893
+ emptyRow
894
+ ] });
895
+ }
896
+ const band = (level, def, g) => /* @__PURE__ */ jsx(
897
+ DataTableGroupBand,
898
+ {
899
+ level,
900
+ colSpan: totalColSpan,
901
+ top: level === 1 ? bandTops[0] : bandTops[1],
902
+ compact,
903
+ label: def.label ? def.label(g.key, g.rows) : g.key,
904
+ end: def.end?.(g.key, g.rows)
905
+ }
906
+ );
907
+ const rowsOf = (g) => g.rows.map((r, i) => renderRow(r, g.start + i));
908
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
909
+ (leading || emptyRow) && /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
910
+ leading,
911
+ emptyRow
912
+ ] }),
913
+ desktopGroups.map(
914
+ (g1) => g1.children ? g1.children.map((g2, j) => /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
915
+ j === 0 && band(1, levels[0], g1),
916
+ band(2, levels[1], g2),
917
+ rowsOf(g2)
918
+ ] }, `g:${g1.key}:${g2.key}:${g2.start}`)) : /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
919
+ band(1, levels[0], g1),
920
+ rowsOf(g1)
921
+ ] }, `g:${g1.key}:${g1.start}`)
922
+ )
923
+ ] });
924
+ })(),
925
+ showFooter && // Sticky to the scroller's bottom edge for the same reason the header is
926
+ // sticky to its top: the body scrolls inside a bounded wrapper, and a
927
+ // total that scrolls away with the last row is a total nobody sees on a
928
+ // 25-row page. Each cell carries its own background and its own `sticky`,
929
+ // as the header's do — `position: sticky` on a `<tfoot>` is not reliable.
930
+ /* @__PURE__ */ jsx(
931
+ "tfoot",
932
+ {
933
+ "data-table-footer": "",
934
+ className: cn(
935
+ "font-semibold text-primary",
936
+ isLoading && "opacity-60 transition-opacity"
859
937
  ),
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,
938
+ children: /* @__PURE__ */ jsxs("tr", { children: [
939
+ selection && /* @__PURE__ */ jsx(
940
+ "td",
867
941
  {
868
- "data-col": col.key,
869
- scope: isLabel ? "row" : void 0,
870
- style: pinned,
871
942
  className: cn(
872
943
  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
- )
944
+ compact ? "w-8 px-2 py-1" : "w-10 px-3 py-2"
945
+ )
946
+ }
947
+ ),
948
+ visibleColumns.map((col) => {
949
+ const width = widths[col.key];
950
+ const pinned = width ? { width, minWidth: width, maxWidth: width } : void 0;
951
+ const isLabel = col === footerLabelCol;
952
+ const Cell = isLabel ? "th" : "td";
953
+ return /* @__PURE__ */ jsx(
954
+ Cell,
955
+ {
956
+ "data-col": col.key,
957
+ scope: isLabel ? "row" : void 0,
958
+ style: pinned,
959
+ className: cn(
960
+ FOOTER_CELL,
961
+ "align-top",
962
+ compact ? "px-2 py-1" : "px-3 py-2",
963
+ width && "overflow-hidden text-ellipsis",
964
+ // A `<th>` centres by default; the label reads from the start
965
+ // like the cells above it, unless the column says otherwise.
966
+ isLabel && "text-start",
967
+ col.grow ? GROW_CELL_CLASS : growDrawn && GROW_PEER_CELL_CLASS,
968
+ logicalAlign(col.className)
969
+ ),
970
+ children: col.footer ? footerValue(col) : isLabel ? footerLabel : null
971
+ },
972
+ col.key
973
+ );
974
+ })
975
+ ] })
976
+ }
977
+ )
978
+ ]
979
+ }
980
+ ),
981
+ loadMore && /* @__PURE__ */ jsx(DataTableLoadMoreFooter, { loadMore, rowCount: sorted.length, labels, compact })
982
+ ]
892
983
  }
893
984
  ),
894
- !unpaged && /* @__PURE__ */ jsx(
985
+ !unpaged && !loadMore && /* @__PURE__ */ jsx(
895
986
  Pagination,
896
987
  {
897
988
  page: safePage,
@@ -916,6 +1007,8 @@ function DataTable({
916
1007
  columns,
917
1008
  hiddenCols,
918
1009
  setHiddenCols,
1010
+ autoHidden,
1011
+ keepColumn,
919
1012
  showSettings,
920
1013
  setShowSettings,
921
1014
  columnsCountLabel,