nuxt-ui-tools 1.4.0 → 1.6.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 (97) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +3 -1
  3. package/dist/runtime/dashboard/composables/use-dashboard-remote-options.js +8 -3
  4. package/dist/runtime/dashboard/types/filters.d.ts +7 -1
  5. package/dist/runtime/dashboard/utils/builders/dashboard-filters.js +19 -2
  6. package/dist/runtime/form/composables/use-field-options.js +19 -0
  7. package/dist/runtime/form/composables/use-remote-field-options.js +21 -2
  8. package/dist/runtime/form/types/options.d.ts +22 -2
  9. package/dist/runtime/i18n/locales/en.js +23 -2
  10. package/dist/runtime/i18n/locales/fr.js +23 -2
  11. package/dist/runtime/i18n/types.d.ts +21 -0
  12. package/dist/runtime/shared/index.d.ts +1 -0
  13. package/dist/runtime/shared/index.js +1 -0
  14. package/dist/runtime/shared/types/query.d.ts +5 -1
  15. package/dist/runtime/shared/types/remote-options.d.ts +56 -0
  16. package/dist/runtime/shared/utils/define-remote-options.d.ts +23 -0
  17. package/dist/runtime/shared/utils/define-remote-options.js +24 -0
  18. package/dist/runtime/shared/utils/remote-options-query.d.ts +6 -0
  19. package/dist/runtime/shared/utils/remote-options-query.js +18 -0
  20. package/dist/runtime/table/components/data-list/data-list-content.vue +7 -1
  21. package/dist/runtime/table/components/data-list/data-list-error-state.d.vue.ts +18 -1
  22. package/dist/runtime/table/components/data-list/data-list-error-state.vue +65 -41
  23. package/dist/runtime/table/components/data-list/data-list-error-state.vue.d.ts +18 -1
  24. package/dist/runtime/table/components/data-list/data-list-filter-tags.d.vue.ts +1 -1
  25. package/dist/runtime/table/components/data-list/data-list-filter-tags.vue +1 -1
  26. package/dist/runtime/table/components/data-list/data-list-filter-tags.vue.d.ts +1 -1
  27. package/dist/runtime/table/components/data-list/sort-menu-panel.vue +33 -25
  28. package/dist/runtime/table/components/drawers/column-panel.vue +19 -6
  29. package/dist/runtime/table/components/filters/panel/filter-panel.vue +14 -2
  30. package/dist/runtime/table/components/filters/shared/dynamic-filter-picker.vue +1 -1
  31. package/dist/runtime/table/components/filters/shared/filter-editor-header.d.vue.ts +16 -0
  32. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue +46 -0
  33. package/dist/runtime/table/components/filters/shared/filter-editor-header.vue.d.ts +16 -0
  34. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.d.vue.ts +3 -1
  35. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue +6 -5
  36. package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue.d.ts +3 -1
  37. package/dist/runtime/table/components/filters/shared/filter-option-picker-content.vue +4 -1
  38. package/dist/runtime/table/components/filters/shared/filter-option-single-list.vue +4 -4
  39. package/dist/runtime/table/components/filters/shared/filter-option-tree-content.vue +12 -8
  40. package/dist/runtime/table/components/filters/shared/filter-popover-shell.vue +10 -1
  41. package/dist/runtime/table/components/filters/shared/filter-searchable-panel.vue +14 -6
  42. package/dist/runtime/table/components/filters/shared/filter-trigger-tag.vue +12 -2
  43. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.d.vue.ts +6 -1
  44. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue +19 -5
  45. package/dist/runtime/table/components/filters/tags/boolean-filter-tag.vue.d.ts +6 -1
  46. package/dist/runtime/table/components/filters/tags/date-filter-tag.d.vue.ts +6 -1
  47. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue +65 -29
  48. package/dist/runtime/table/components/filters/tags/date-filter-tag.vue.d.ts +6 -1
  49. package/dist/runtime/table/components/filters/tags/number-filter-tag.d.vue.ts +6 -1
  50. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue +66 -18
  51. package/dist/runtime/table/components/filters/tags/number-filter-tag.vue.d.ts +6 -1
  52. package/dist/runtime/table/components/filters/tags/option-filter-tag.d.vue.ts +2 -2
  53. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue +10 -34
  54. package/dist/runtime/table/components/filters/tags/option-filter-tag.vue.d.ts +2 -2
  55. package/dist/runtime/table/components/filters/tags/text-filter-tag.d.vue.ts +6 -1
  56. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue +39 -14
  57. package/dist/runtime/table/components/filters/tags/text-filter-tag.vue.d.ts +6 -1
  58. package/dist/runtime/table/components/grid/grid-renderer.d.vue.ts +8 -2
  59. package/dist/runtime/table/components/grid/grid-renderer.vue +47 -115
  60. package/dist/runtime/table/components/grid/grid-renderer.vue.d.ts +8 -2
  61. package/dist/runtime/table/components/layout/progress-line.d.vue.ts +9 -0
  62. package/dist/runtime/table/components/layout/progress-line.vue +43 -0
  63. package/dist/runtime/table/components/layout/progress-line.vue.d.ts +9 -0
  64. package/dist/runtime/table/components/table/table-empty-state.d.vue.ts +1 -1
  65. package/dist/runtime/table/components/table/table-empty-state.vue +1 -1
  66. package/dist/runtime/table/components/table/table-empty-state.vue.d.ts +1 -1
  67. package/dist/runtime/table/components/table/table-layout.d.ts +57 -0
  68. package/dist/runtime/table/components/table/table-layout.js +23 -0
  69. package/dist/runtime/table/components/table/table-renderer.d.vue.ts +8 -16
  70. package/dist/runtime/table/components/table/table-renderer.vue +180 -274
  71. package/dist/runtime/table/components/table/table-renderer.vue.d.ts +8 -16
  72. package/dist/runtime/table/components/table/table-row.d.vue.ts +19 -0
  73. package/dist/runtime/table/components/table/table-row.vue +51 -0
  74. package/dist/runtime/table/components/table/table-row.vue.d.ts +19 -0
  75. package/dist/runtime/table/components/table/table-skeleton-cell.d.ts +9 -0
  76. package/dist/runtime/table/components/table/table-skeleton-cell.js +104 -0
  77. package/dist/runtime/table/components/utils/search-query-input.vue +27 -1
  78. package/dist/runtime/table/composables/use-deferred-commit.d.ts +20 -0
  79. package/dist/runtime/table/composables/use-deferred-commit.js +31 -0
  80. package/dist/runtime/table/composables/use-table-data.d.ts +5 -1
  81. package/dist/runtime/table/composables/use-table-data.js +20 -2
  82. package/dist/runtime/table/composables/use-table-filter-selected-options.js +16 -1
  83. package/dist/runtime/table/composables/use-table-summaries.js +10 -1
  84. package/dist/runtime/table/types/columns.d.ts +15 -1
  85. package/dist/runtime/table/types/data-list.d.ts +1 -0
  86. package/dist/runtime/table/types/filters.d.ts +7 -0
  87. package/dist/runtime/table/types/remote-options.d.ts +3 -16
  88. package/dist/runtime/table/utils/columns/defs.js +1 -1
  89. package/dist/runtime/table/utils/filters/date.d.ts +6 -0
  90. package/dist/runtime/table/utils/filters/date.js +26 -2
  91. package/dist/runtime/table/utils/filters/index.d.ts +1 -0
  92. package/dist/runtime/table/utils/filters/index.js +1 -0
  93. package/dist/runtime/table/utils/filters/number.d.ts +5 -0
  94. package/dist/runtime/table/utils/filters/number.js +11 -0
  95. package/dist/runtime/table/utils/filters/ui.js +5 -5
  96. package/dist/runtime/table/utils/remote-table-options.js +37 -37
  97. package/package.json +1 -1
@@ -1,19 +1,8 @@
1
1
  <script setup>
2
2
  import { useVirtualizer } from "@tanstack/vue-virtual";
3
- import {
4
- computed,
5
- h,
6
- nextTick,
7
- onBeforeUnmount,
8
- onMounted,
9
- ref,
10
- shallowRef,
11
- TransitionGroup,
12
- useTemplateRef,
13
- watch
14
- } from "vue";
3
+ import { computed, h, nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from "vue";
15
4
  import { useUiToolsLocale } from "#ui-tools/i18n";
16
- import { isDefined, isFunction, isNumber, isObject } from "../../../shared/utils/predicate";
5
+ import { isFunction, isNumber, isObject } from "../../../shared/utils/predicate";
17
6
  import { useDataListUi } from "../../composables/use-data-list-ui";
18
7
  import { useTableInternals } from "../../composables/use-table-internals";
19
8
  import { useTanstackTable } from "../../composables/use-tanstack-table";
@@ -24,16 +13,20 @@ import {
24
13
  SELECT_COLUMN_WIDTH
25
14
  } from "../../utils/columns/types";
26
15
  import DataListErrorState from "../data-list/data-list-error-state.vue";
16
+ import ProgressLine from "../layout/progress-line.vue";
27
17
  import TableCell from "./table-cell";
28
18
  import TableColumnHeader from "./table-column-header.vue";
29
19
  import TableEmptyState from "./table-empty-state.vue";
20
+ import { columnCellLayout, tableRowCells } from "./table-layout";
30
21
  import TableOverlayScrollbars from "./table-overlay-scrollbars.vue";
22
+ import TableRow from "./table-row.vue";
23
+ import TableSkeletonCell from "./table-skeleton-cell";
31
24
  const LOAD_MORE_THRESHOLD = 6;
32
25
  const PREFETCH_VIEWPORTS = 3;
33
26
  const VIRTUALIZE_ROW_THRESHOLD = 40;
34
27
  const VIRTUALIZE_CELL_THRESHOLD = 240;
35
- const VIRTUALIZE_COLUMN_THRESHOLD = 32;
36
- const ANIMATION_WINDOW = 600;
28
+ const VIRTUALIZE_COLUMN_THRESHOLD = 12;
29
+ const APPEND_WINDOW = 400;
37
30
  const SKELETON_MIN_ROWS = 6;
38
31
  const SIZE_TOKENS = {
39
32
  lg: { font: 13.5, head: 46, row: 52, x: 16 },
@@ -59,11 +52,12 @@ const { status } = internals.queryContent;
59
52
  const rowsMounted = ref(false);
60
53
  let rowMountFrame = 0;
61
54
  const isFirstLoad = computed(
62
- () => status.value.isBooting || (status.value.isPending || status.value.isFetching) && rows.value.length === 0 || !rowsMounted.value && rows.value.length > 0
55
+ () => status.value.isBooting || (status.value.isPending || status.value.isFetching) && rows.value.length === 0
63
56
  );
64
57
  const refreshing = computed(
65
58
  () => rows.value.length > 0 && (status.value.isFetching || status.value.isRefreshing || status.value.isRevalidating)
66
59
  );
60
+ const replacing = computed(() => rows.value.length > 0 && status.value.isReplacing);
67
61
  const error = computed(() => rows.value.length === 0 ? internals.queryContent.error.value : null);
68
62
  const empty = computed(() => !isFirstLoad.value && !error.value && rows.value.length === 0);
69
63
  function refresh() {
@@ -125,13 +119,18 @@ const virtualized = computed(
125
119
  );
126
120
  const scrollRef = useTemplateRef("scrollRef");
127
121
  const rowHeights = /* @__PURE__ */ new Map();
128
- function measureRowHeight(element) {
122
+ function measureRowHeight(element, entry) {
129
123
  const key = element instanceof HTMLElement ? element.dataset.rowId : void 0;
130
- const { height } = element.getBoundingClientRect();
124
+ const known = key === void 0 ? void 0 : rowHeights.get(key);
125
+ const box = entry?.borderBoxSize?.[0];
126
+ if (!box) {
127
+ return known ?? rowHeight.value;
128
+ }
129
+ const height = box.blockSize;
131
130
  if (key === void 0) {
132
131
  return height;
133
132
  }
134
- const next = Math.max(rowHeights.get(key) ?? 0, height);
133
+ const next = Math.max(known ?? 0, height);
135
134
  rowHeights.set(key, next);
136
135
  return next;
137
136
  }
@@ -141,20 +140,17 @@ const rowVirtualizer = useVirtualizer(
141
140
  estimateSize: () => rowHeight.value,
142
141
  getItemKey: (index) => renderRows.value[index] ? getRowId(renderRows.value[index], index) : index,
143
142
  getScrollElement: () => scrollRef.value ?? null,
144
- measureElement: (element) => measureRowHeight(element),
145
- overscan: 8
143
+ measureElement: (element, entry) => measureRowHeight(element, entry),
144
+ overscan: 16
146
145
  }))
147
146
  );
148
147
  const virtualRows = computed(
149
148
  () => virtualized.value ? rowVirtualizer.value.getVirtualItems() : []
150
149
  );
151
- const materializedRows = computed(
152
- () => virtualized.value ? virtualRows.value.map((item) => renderRows.value[item.index]).filter(isDefined) : renderRows.value
153
- );
154
150
  const { table, resetColumnSizing } = useTanstackTable({
155
151
  columnSizing,
156
152
  columns: columnDefs,
157
- data: materializedRows,
153
+ data: renderRows,
158
154
  getRowId,
159
155
  pinned
160
156
  });
@@ -186,6 +182,20 @@ const columnVirtualizer = useVirtualizer(
186
182
  }))
187
183
  );
188
184
  const virtualColumns = computed(() => columnVirtualizer.value.getVirtualItems());
185
+ const filled = computed(
186
+ () => !columnsOverflow.value && bodyWidth.value > 0 && table.getTotalSize() < bodyWidth.value
187
+ );
188
+ const tableCols = computed(() => {
189
+ const cols = leafColumns.value.map((leaf) => ({
190
+ key: leaf.id,
191
+ width: `${leaf.getSize()}px`
192
+ }));
193
+ if (filled.value) {
194
+ cols.splice(cols.length - table.getEndVisibleLeafColumns().length, 0, { key: "fill" });
195
+ }
196
+ return cols;
197
+ });
198
+ const columnCount = computed(() => tableCols.value.length);
189
199
  const columnSlots = computed(() => {
190
200
  function asSlot(column) {
191
201
  return {
@@ -196,7 +206,11 @@ const columnSlots = computed(() => {
196
206
  }
197
207
  const slots = table.getStartVisibleLeafColumns().map(asSlot);
198
208
  if (!columnsOverflow.value) {
199
- slots.push(...centerColumns.value.map(asSlot), ...table.getEndVisibleLeafColumns().map(asSlot));
209
+ slots.push(...centerColumns.value.map(asSlot));
210
+ if (filled.value) {
211
+ slots.push({ key: "fill", kind: "fill" });
212
+ }
213
+ slots.push(...table.getEndVisibleLeafColumns().map(asSlot));
200
214
  return slots;
201
215
  }
202
216
  const first = virtualColumns.value[0];
@@ -217,23 +231,33 @@ const columnSlots = computed(() => {
217
231
  slots.push(...table.getEndVisibleLeafColumns().map(asSlot));
218
232
  return slots;
219
233
  });
220
- function rowColumnSlots(row) {
221
- const cells = new Map(row.getVisibleCells().map((cell) => [cell.column.id, cell]));
222
- const slots = [];
223
- for (const slot of columnSlots.value) {
224
- if (slot.kind === "spacer") {
225
- slots.push(slot);
226
- continue;
227
- }
228
- const cell = cells.get(slot.columnId);
229
- if (cell) {
230
- slots.push({ cell, key: cell.id, kind: "cell" });
231
- }
232
- }
233
- return slots;
234
- }
235
234
  const headers = computed(() => table.getHeaderGroups()[0]?.headers ?? []);
236
235
  const headerByColumnId = computed(() => new Map(headers.value.map((h2) => [h2.column.id, h2])));
236
+ const headerCellProps = computed(
237
+ () => new Map(
238
+ headers.value.map((header) => {
239
+ const { column } = header;
240
+ const meta = column.columnDef.meta;
241
+ return [
242
+ column.id,
243
+ {
244
+ align: meta?.align,
245
+ icon: meta?.icon,
246
+ items: internals.tableColumns.getMenuItems({ columnId: column.id }),
247
+ label: meta?.label ?? "",
248
+ pinned: Boolean(internals.tableColumns.getPinnedState({ columnId: column.id })),
249
+ resetSize: () => column.resetSize(),
250
+ resizable: column.getCanResize(),
251
+ resize: header.getResizeHandler(),
252
+ resizing: column.getIsResizing(),
253
+ sortState: internals.tableColumns.getSortState({ columnId: column.id }),
254
+ sortable: meta?.sortable
255
+ }
256
+ ];
257
+ })
258
+ )
259
+ );
260
+ const HEADER_ROW = {};
237
261
  const virtualPaddingTop = computed(() => virtualRows.value[0]?.start ?? 0);
238
262
  const virtualPaddingBottom = computed(() => {
239
263
  if (!virtualized.value || !virtualRows.value.length) {
@@ -241,26 +265,36 @@ const virtualPaddingBottom = computed(() => {
241
265
  }
242
266
  return rowVirtualizer.value.getTotalSize() - (virtualRows.value.at(-1)?.end ?? 0);
243
267
  });
244
- const virtualRowByKey = computed(() => {
245
- const byKey = /* @__PURE__ */ new Map();
246
- for (const item of virtualRows.value) {
247
- const row = renderRows.value[item.index];
248
- if (row) {
249
- byKey.set(getRowId(row, item.index), { index: item.index, key: String(item.key) });
250
- }
251
- }
252
- return byKey;
253
- });
254
268
  const renderedRows = computed(() => {
255
269
  if (!virtualized.value) {
256
270
  return tableRows.value.map((row, index) => ({ row, virtual: { index, key: String(row.id) } }));
257
271
  }
258
- return tableRows.value.map((row) => {
259
- const virtual = virtualRowByKey.value.get(String(row.id));
260
- return virtual ? { row, virtual } : void 0;
261
- }).filter(isDefined);
272
+ return virtualRows.value.flatMap((item) => {
273
+ const row = tableRows.value[item.index];
274
+ return row ? [{ row, virtual: { index: item.index, key: String(item.key) } }] : [];
275
+ });
262
276
  });
263
- function measureRow(element) {
277
+ const rowCells = computed(
278
+ () => tableRowCells({
279
+ layouts: new Map(
280
+ leafColumns.value.map((column) => [
281
+ column.id,
282
+ columnCellLayout({
283
+ firstEnd: isFirstEnd(column),
284
+ lastStart: isLastStart(column),
285
+ meta: column.columnDef.meta,
286
+ offset: pinnedOffset(column),
287
+ pinned: column.getIsPinned(),
288
+ tdClass: mergeDataListUiClass(void 0, void 0, props.ui?.td)
289
+ })
290
+ ])
291
+ ),
292
+ slots: columnSlots.value
293
+ })
294
+ );
295
+ const rowClass = computed(() => mergeDataListUiClass(void 0, void 0, props.ui?.tr));
296
+ function measureRow(target) {
297
+ const element = target instanceof Element ? target : target?.$el;
264
298
  if (!(element instanceof HTMLElement)) {
265
299
  return;
266
300
  }
@@ -335,8 +369,11 @@ const summaryLabel = computed(() => {
335
369
  if (label) {
336
370
  return isFunction(label) ? String(label()) : String(label);
337
371
  }
338
- return t("table.summaries.total");
372
+ return summaries.scope.value === "filtered" ? t("table.summaries.total") : t(`table.summaries.${summaries.scope.value}`);
339
373
  });
374
+ const summaryUnit = computed(
375
+ () => t(summaries.count.value === 1 ? "table.summaries.rowOne" : "table.summaries.rowOther")
376
+ );
340
377
  function formatNumber(value) {
341
378
  return isNumber(value) ? new Intl.NumberFormat(locale.value.code).format(value).replaceAll("\u202F", "\xA0") : String(value ?? "");
342
379
  }
@@ -354,52 +391,9 @@ function renderSummary(columnId) {
354
391
  const value = summaries.format(columnId);
355
392
  return () => h("span", formatNumber(value));
356
393
  }
357
- const animateEpoch = ref(false);
358
- const enterFrom = ref(Number.POSITIVE_INFINITY);
359
- const previousIds = shallowRef(/* @__PURE__ */ new Set());
360
- const flipPositions = /* @__PURE__ */ new Map();
361
- watch(
362
- rows,
363
- () => {
364
- previousIds.value = new Set(rows.value.map((row, index) => getRowId(row, index)));
365
- flipPositions.clear();
366
- const elements = scrollRef.value?.querySelectorAll("tr.nut-dl-row[data-row-id]") ?? [];
367
- for (const element of elements) {
368
- flipPositions.set(element.dataset.rowId ?? "", element.getBoundingClientRect().top);
369
- }
370
- },
371
- { flush: "pre" }
372
- );
373
- watch(rows, () => nextTick().then(flipRows), { flush: "post" });
374
- function flipRows() {
375
- if (!flipPositions.size) {
376
- return;
377
- }
378
- const elements = scrollRef.value?.querySelectorAll("tr.nut-dl-row[data-row-id]") ?? [];
379
- for (const element of elements) {
380
- const previous = flipPositions.get(element.dataset.rowId ?? "");
381
- if (previous === void 0) {
382
- continue;
383
- }
384
- const delta = previous - element.getBoundingClientRect().top;
385
- if (Math.abs(delta) > 1) {
386
- element.animate([{ transform: `translateY(${delta}px)` }, { transform: "none" }], {
387
- duration: 240,
388
- easing: "cubic-bezier(0.2, 0.9, 0.3, 1)"
389
- });
390
- }
391
- }
392
- flipPositions.clear();
393
- }
394
- let animateTimer = 0;
395
- function armAnimation(from = Number.POSITIVE_INFINITY) {
396
- enterFrom.value = from;
397
- animateEpoch.value = true;
398
- window.clearTimeout(animateTimer);
399
- animateTimer = window.setTimeout(() => {
400
- animateEpoch.value = false;
401
- }, ANIMATION_WINDOW);
402
- }
394
+ const appendedFrom = ref(Number.POSITIVE_INFINITY);
395
+ const scrollResetPending = ref(false);
396
+ let appendTimer = 0;
403
397
  watch(
404
398
  () => [
405
399
  internals.pagination.currentPage.value,
@@ -409,75 +403,38 @@ watch(
409
403
  internals.filters.searchQuery.value
410
404
  ],
411
405
  () => {
412
- if (!rowsMounted.value || cursorMode.value) {
413
- return;
414
- }
406
+ if (!rowsMounted.value || cursorMode.value) return;
415
407
  rowHeights.clear();
416
- armAnimation();
417
- scrollRef.value?.scrollTo({ top: 0 });
408
+ scrollResetPending.value = true;
418
409
  }
419
410
  );
420
- const SKELETON_WIDTHS = [62, 44, 78, 54, 70, 38, 66, 48];
421
- function skeletonWidth(seed) {
422
- return SKELETON_WIDTHS[seed % SKELETON_WIDTHS.length];
423
- }
424
- function renderSkeletonCell(columnId, rowIndex) {
425
- const meta = headerByColumnId.value.get(columnId)?.column.columnDef.meta;
426
- const kind = meta?.internal === "selection" ? "check" : meta?.internal ? "none" : meta?.skeleton ?? "text";
427
- const seed = rowIndex * 7 + columnId.length;
428
- function line(width, extra = "") {
429
- return h("span", {
430
- class: `nut-dl-skeleton block h-3 rounded ${extra}`,
431
- style: { width: `${width}%` }
432
- });
433
- }
434
- return () => {
435
- if (kind === "none") {
436
- return null;
437
- }
438
- if (kind === "check") {
439
- return h("span", { class: "nut-dl-skeleton block size-4 rounded-[4px]" });
440
- }
441
- if (kind === "dot") {
442
- return h("span", { class: "flex items-center gap-2" }, [
443
- h("span", { class: "nut-dl-skeleton size-[7px] rounded-full" }),
444
- line(skeletonWidth(seed) - 10)
445
- ]);
446
- }
447
- if (kind === "avatar") {
448
- return h("span", { class: "flex items-center gap-2.5" }, [
449
- h("span", { class: "nut-dl-skeleton size-7 shrink-0 rounded-md" }),
450
- h("span", { class: "grid flex-1 gap-1.5" }, [
451
- line(skeletonWidth(seed)),
452
- line(skeletonWidth(seed + 3) - 20, "h-2.5")
453
- ])
454
- ]);
455
- }
456
- if (kind === "badge") {
457
- return h("span", { class: "nut-dl-skeleton inline-block h-5 w-16 rounded-full" });
458
- }
459
- if (kind === "number") {
460
- return h("span", {
461
- class: "nut-dl-skeleton ml-auto block h-3 rounded",
462
- style: { width: "36%" }
463
- });
464
- }
465
- return line(skeletonWidth(seed));
466
- };
411
+ function resetScroll() {
412
+ if (!scrollResetPending.value) return;
413
+ scrollResetPending.value = false;
414
+ scrollRef.value?.scrollTo({ top: 0 });
467
415
  }
468
- watch(isFirstLoad, (loading, was) => {
469
- if (was && !loading) {
470
- armAnimation();
471
- }
472
- });
416
+ watch(rows, resetScroll);
417
+ watch(
418
+ () => status.value.isFetching,
419
+ (fetching) => !fetching && resetScroll()
420
+ );
473
421
  watch(
474
422
  () => rows.value.length,
475
423
  (next, previous) => {
476
- if (cursorMode.value && previous > 0 && next > previous) {
477
- armAnimation(previous);
478
- }
424
+ if (!cursorMode.value || previous === 0 || next <= previous) return;
425
+ appendedFrom.value = previous;
426
+ window.clearTimeout(appendTimer);
427
+ appendTimer = window.setTimeout(() => {
428
+ appendedFrom.value = Number.POSITIVE_INFINITY;
429
+ }, APPEND_WINDOW);
479
430
  }
480
431
  );
432
+ function skeletonColumn(columnId) {
433
+ const meta = headerByColumnId.value.get(columnId)?.column.columnDef.meta;
434
+ if (meta?.internal === "selection") return { align: meta.align, skeleton: "check" };
435
+ if (meta?.internal) return { align: meta.align, skeleton: "none" };
436
+ return { align: meta?.align, skeleton: meta?.skeleton };
437
+ }
481
438
  const cursorMode = computed(() => internals.pagination.mode.value === "cursor");
482
439
  const loadingMore = computed(
483
440
  () => cursorMode.value && internals.pagination.state.value.isLoadingMore
@@ -539,7 +496,7 @@ defineExpose({ resetColumnSizing });
539
496
  ref="scrollRef"
540
497
  class="nut-dl-table__scroll relative min-h-0 overflow-auto overscroll-x-contain bg-[var(--nut-dl-surface)]"
541
498
  :class="[
542
- fill || height ? 'flex-1' : '',
499
+ fill || height ? 'flex flex-1 flex-col' : '',
543
500
  mergeDataListUiClass(void 0, void 0, ui?.root)
544
501
  ]"
545
502
  @scroll.passive="onScrollLoadMore"
@@ -551,16 +508,21 @@ defineExpose({ resetColumnSizing });
551
508
  >
552
509
  <colgroup>
553
510
  <col
554
- v-for="leaf in leafColumns"
555
- :key="leaf.id"
556
- :style="{ width: `${leaf.getSize()}px` }"
511
+ v-for="col in tableCols"
512
+ :key="col.key"
513
+ :style="col.width ? { width: col.width } : void 0"
557
514
  />
558
515
  </colgroup>
559
516
  <thead :class="mergeDataListUiClass('nut-dl-table__head', void 0, ui?.thead)">
560
517
  <tr class="nut-dl-table__head-row">
561
518
  <template v-for="slot in columnSlots" :key="slot.key">
562
519
  <th
563
- v-if="slot.kind === 'spacer'"
520
+ v-if="slot.kind === 'fill'"
521
+ class="nut-dl-table__fill sticky top-0 z-[3] p-0"
522
+ aria-hidden="true"
523
+ />
524
+ <th
525
+ v-else-if="slot.kind === 'spacer'"
564
526
  :colspan="slot.colSpan"
565
527
  class="nut-dl-table__spacer sticky top-0 z-[3] p-0"
566
528
  aria-hidden="true"
@@ -593,53 +555,27 @@ defineExpose({ resetColumnSizing });
593
555
  headerByColumnId.get(slot.columnId).column.columnDef.meta?.renderHeader
594
556
  "
595
557
  :render="
596
- () => headerByColumnId.get(slot.columnId).column.columnDef.meta.renderHeader()
558
+ headerByColumnId.get(slot.columnId).column.columnDef.meta.renderHeader
597
559
  "
598
- :row="{}"
560
+ :row="HEADER_ROW"
599
561
  :index="-1"
600
562
  />
601
563
  </div>
602
564
  </template>
603
565
  <TableColumnHeader
604
566
  v-else
567
+ v-bind="headerCellProps.get(slot.columnId)"
605
568
  :open="openMenuId === slot.columnId"
606
- :label="headerByColumnId.get(slot.columnId).column.columnDef.meta.label"
607
- :icon="headerByColumnId.get(slot.columnId).column.columnDef.meta.icon"
608
- :align="headerByColumnId.get(slot.columnId).column.columnDef.meta.align"
609
- :sortable="headerByColumnId.get(slot.columnId).column.columnDef.meta.sortable"
610
- :sort-state="internals.tableColumns.getSortState({ columnId: slot.columnId })"
611
- :pinned="
612
- Boolean(internals.tableColumns.getPinnedState({ columnId: slot.columnId }))
613
- "
614
- :items="internals.tableColumns.getMenuItems({ columnId: slot.columnId })"
615
- :resizable="headerByColumnId.get(slot.columnId).column.getCanResize()"
616
- :resizing="headerByColumnId.get(slot.columnId).column.getIsResizing()"
617
- :reset-size="() => headerByColumnId.get(slot.columnId).column.resetSize()"
618
- :resize="headerByColumnId.get(slot.columnId).getResizeHandler()"
619
569
  @update:open="openMenuId = $event ? slot.columnId : null"
620
570
  />
621
571
  </th>
622
572
  </template>
623
573
  </tr>
624
- <tr class="nut-dl-progress" :data-active="refreshing" aria-hidden="true">
625
- <th
626
- :colspan="leafColumns.length"
627
- class="sticky top-[var(--nut-dl-head-h)] z-[3] h-0 p-0"
628
- >
629
- <span />
630
- </th>
631
- </tr>
632
574
  </thead>
633
575
 
634
- <component
635
- :is="virtualized ? 'tbody' : TransitionGroup"
636
- :key="virtualized ? 'virtual' : 'rows'"
637
- :tag="virtualized ? void 0 : 'tbody'"
576
+ <tbody
638
577
  :class="mergeDataListUiClass('nut-dl-table__body', void 0, ui?.tbody)"
639
- :move-class="virtualized ? void 0 : 'nut-dl-row--moving'"
640
- :enter-active-class="virtualized ? void 0 : 'nut-dl-row--settling'"
641
- :enter-from-class="virtualized ? void 0 : 'nut-dl-row--from'"
642
- :leave-active-class="virtualized ? void 0 : 'hidden'"
578
+ :data-replacing="replacing"
643
579
  >
644
580
  <tr
645
581
  v-if="virtualPaddingTop"
@@ -647,70 +583,21 @@ defineExpose({ resetColumnSizing });
647
583
  :style="{ height: `${virtualPaddingTop}px` }"
648
584
  aria-hidden="true"
649
585
  >
650
- <td :colspan="leafColumns.length" class="p-0" />
586
+ <td :colspan="columnCount" class="p-0" />
651
587
  </tr>
652
588
 
653
- <tr
589
+ <TableRow
654
590
  v-for="{ row, virtual } in renderedRows"
655
591
  :key="virtual.key"
656
592
  :ref="measureRow"
657
- class="nut-dl-row group/row"
658
- :class="[
659
- internals.selection.isRowSelected({ rowId: String(row.id) }) ? 'nut-dl-row--selected' : '',
660
- mergeDataListUiClass(void 0, void 0, ui?.tr)
661
- ]"
662
- :data-index="virtual.index"
663
- :data-row-id="row.id"
664
- :style="
665
- virtualized && animateEpoch && (virtual.index >= enterFrom || !previousIds.has(String(row.id))) ? {
666
- '--nut-dl-i': Math.min(
667
- virtual.index - Math.max(enterFrom, virtualRows[0]?.index ?? 0),
668
- 24
669
- )
670
- } : void 0
671
- "
672
- >
673
- <template v-for="slot in rowColumnSlots(row)" :key="slot.key">
674
- <td
675
- v-if="slot.kind === 'spacer'"
676
- :colspan="slot.colSpan"
677
- class="nut-dl-table__spacer p-0"
678
- aria-hidden="true"
679
- />
680
- <td
681
- v-else
682
- class="nut-dl-td h-[var(--nut-dl-row-h)] py-0 align-middle"
683
- :class="[
684
- slot.cell.column.getIsPinned() === 'start' ? 'nut-dl-pin nut-dl-pin--start z-[2]' : '',
685
- slot.cell.column.getIsPinned() === 'end' ? 'nut-dl-pin nut-dl-pin--end z-[2]' : '',
686
- isLastStart(slot.cell.column) ? 'nut-dl-pin--last-start' : '',
687
- isFirstEnd(slot.cell.column) ? 'nut-dl-pin--first-end' : '',
688
- slot.cell.column.columnDef.meta?.align === 'right' ? 'text-right' : slot.cell.column.columnDef.meta?.align === 'center' ? 'text-center' : '',
689
- slot.cell.column.columnDef.meta?.internal ? `nut-dl-td--${slot.cell.column.columnDef.meta.internal}` : '',
690
- slot.cell.column.columnDef.meta?.ellipsis ? 'nut-dl-td--ellipsis' : '',
691
- mergeDataListUiClass(void 0, void 0, ui?.td)
692
- ]"
693
- :data-col="slot.cell.column.id"
694
- :style="[
695
- pinnedOffset(slot.cell.column),
696
- slot.cell.column.columnDef.meta?.lines ? { '--nut-dl-lines': slot.cell.column.columnDef.meta.lines } : void 0
697
- ]"
698
- >
699
- <div
700
- class="nut-dl-td__inner min-w-0"
701
- :class="
702
- slot.cell.column.columnDef.meta?.align === 'right' ? 'justify-end' : slot.cell.column.columnDef.meta?.align === 'center' ? 'justify-center' : ''
703
- "
704
- >
705
- <TableCell
706
- :index="virtual.index"
707
- :render="slot.cell.column.columnDef.meta.render"
708
- :row="row.original"
709
- />
710
- </div>
711
- </td>
712
- </template>
713
- </tr>
593
+ :row-id="String(row.id)"
594
+ :original="row.original"
595
+ :index="virtual.index"
596
+ :cells="rowCells"
597
+ :selected="internals.selection.isRowSelected({ rowId: String(row.id) })"
598
+ :appended="virtual.index >= appendedFrom"
599
+ :row-class="rowClass"
600
+ />
714
601
 
715
602
  <tr
716
603
  v-if="virtualPaddingBottom"
@@ -718,7 +605,7 @@ defineExpose({ resetColumnSizing });
718
605
  :style="{ height: `${virtualPaddingBottom}px` }"
719
606
  aria-hidden="true"
720
607
  >
721
- <td :colspan="leafColumns.length" class="p-0" />
608
+ <td :colspan="columnCount" class="p-0" />
722
609
  </tr>
723
610
  <tr
724
611
  v-if="loadingMore"
@@ -726,10 +613,7 @@ defineExpose({ resetColumnSizing });
726
613
  class="nut-dl-row nut-dl-row--loading-more"
727
614
  aria-hidden="true"
728
615
  >
729
- <td
730
- :colspan="leafColumns.length"
731
- class="nut-dl-td h-[var(--nut-dl-row-h)] py-0 align-middle"
732
- >
616
+ <td :colspan="columnCount" class="nut-dl-td h-[var(--nut-dl-row-h)] py-0 align-middle">
733
617
  <div
734
618
  class="nut-dl-loading-more flex items-center gap-2.5 px-[var(--nut-dl-gutter)] text-[12.5px] text-muted"
735
619
  >
@@ -745,11 +629,11 @@ defineExpose({ resetColumnSizing });
745
629
  v-for="placeholder in isFirstLoad ? skeletonRows : 0"
746
630
  :key="`skeleton-${placeholder}`"
747
631
  class="nut-dl-row nut-dl-row--skeleton"
748
- :style="{ '--nut-dl-i': placeholder }"
749
632
  aria-hidden="true"
750
633
  >
751
634
  <template v-for="slot in columnSlots" :key="slot.key">
752
- <td v-if="slot.kind === 'spacer'" :colspan="slot.colSpan" class="p-0" />
635
+ <td v-if="slot.kind === 'fill'" class="nut-dl-table__fill p-0" />
636
+ <td v-else-if="slot.kind === 'spacer'" :colspan="slot.colSpan" class="p-0" />
753
637
  <td
754
638
  v-else
755
639
  class="nut-dl-td h-[var(--nut-dl-row-h)] py-0 align-middle"
@@ -762,11 +646,14 @@ defineExpose({ resetColumnSizing });
762
646
  headerByColumnId.get(slot.columnId) ? pinnedOffset(headerByColumnId.get(slot.columnId).column) : void 0
763
647
  "
764
648
  >
765
- <component :is="renderSkeletonCell(slot.columnId, placeholder)" />
649
+ <TableSkeletonCell
650
+ v-bind="skeletonColumn(slot.columnId)"
651
+ :seed="placeholder * 7 + slot.columnId.length"
652
+ />
766
653
  </td>
767
654
  </template>
768
655
  </tr>
769
- </component>
656
+ </tbody>
770
657
 
771
658
  <tfoot
772
659
  v-if="summaries.enabled.value && !isFirstLoad && !empty"
@@ -775,7 +662,12 @@ defineExpose({ resetColumnSizing });
775
662
  <tr class="nut-dl-table__foot-row">
776
663
  <template v-for="slot in columnSlots" :key="`foot-${slot.key}`">
777
664
  <td
778
- v-if="slot.kind === 'spacer'"
665
+ v-if="slot.kind === 'fill'"
666
+ class="nut-dl-table__fill nut-dl-tf sticky bottom-0 z-[3] p-0"
667
+ aria-hidden="true"
668
+ />
669
+ <td
670
+ v-else-if="slot.kind === 'spacer'"
779
671
  :colspan="slot.colSpan"
780
672
  class="nut-dl-table__spacer nut-dl-tf sticky bottom-0 z-[3] p-0"
781
673
  aria-hidden="true"
@@ -806,6 +698,9 @@ defineExpose({ resetColumnSizing });
806
698
  <span class="nut-dl-tf__count tabular-nums">{{
807
699
  formatNumber(summaries.count.value)
808
700
  }}</span>
701
+ <span class="nut-dl-tf__unit -ml-1 font-normal text-muted">{{
702
+ summaryUnit
703
+ }}</span>
809
704
  </div>
810
705
  <template v-else-if="summaryColumnIds.has(slot.columnId)">
811
706
  <span
@@ -831,26 +726,37 @@ defineExpose({ resetColumnSizing });
831
726
 
832
727
  <div
833
728
  v-if="error"
834
- class="nut-dl-table__error sticky left-0 w-full"
835
- :style="{ height: fill ? 'calc(100% - var(--nut-dl-head-h))' : void 0 }"
729
+ class="nut-dl-table__error sticky left-0 flex w-full flex-col"
730
+ :class="fill || height ? 'min-h-0 flex-1' : ''"
836
731
  >
837
732
  <slot name="error" :error="error" :retry="refresh">
838
733
  <DataListErrorState
839
- :min-height="fill ? '100%' : '16rem'"
734
+ :min-height="fill || height ? void 0 : '16rem'"
840
735
  :size="resolvedSize"
736
+ class="flex-1"
841
737
  @retry="refresh"
842
738
  />
843
739
  </slot>
844
740
  </div>
845
- <div v-else-if="empty" class="nut-dl-table__empty sticky left-0 w-full">
741
+ <div
742
+ v-else-if="empty"
743
+ class="nut-dl-table__empty sticky left-0 flex w-full flex-col"
744
+ :class="fill || height ? 'min-h-0 flex-1' : ''"
745
+ >
846
746
  <slot name="empty">
847
747
  <TableEmptyState
848
- :min-height="fill ? 'calc(100% - var(--nut-dl-head-h))' : '16rem'"
748
+ :min-height="fill || height ? void 0 : '16rem'"
849
749
  :size="resolvedSize"
750
+ class="flex-1"
850
751
  />
851
752
  </slot>
852
753
  </div>
853
754
  </div>
755
+ <ProgressLine
756
+ :active="refreshing"
757
+ :restart-key="internals.queryContent.refreshes.value"
758
+ class="absolute inset-x-0 top-[calc(var(--nut-dl-head-h)-1px)] z-[6]"
759
+ />
854
760
  <TableOverlayScrollbars ref="scrollbarsRef" :target="scrollRef" />
855
761
  </div>
856
762
  </template>
@@ -858,5 +764,5 @@ defineExpose({ resetColumnSizing });
858
764
  <style>
859
765
  .nut-dl-table{--nut-dl-surface:var(--nut-dl-table-surface,var(--ui-bg-elevated));--nut-dl-line:var(--nut-dl-table-line,var(--ui-border));--nut-dl-line-soft:var(--nut-dl-table-line-soft,var(--ui-border-muted));--nut-dl-head-bg:var(--nut-dl-table-head-bg,var(--ui-bg-muted));--nut-dl-head-fg:var(--nut-dl-table-head-fg,var(--ui-text-muted));--nut-dl-head-font:var(--nut-dl-table-head-font,11px);--nut-dl-head-weight:var(--nut-dl-table-head-weight,600);--nut-dl-head-tracking:var(--nut-dl-table-head-tracking,0.04em);--nut-dl-head-transform:var(--nut-dl-table-head-transform,none);--nut-dl-row-hover:var(--nut-dl-table-row-hover,var(--ui-bg-muted));--nut-dl-row-selected:var(
860
766
  --nut-dl-table-row-selected,color-mix(in srgb,var(--ui-primary) 10%,var(--nut-dl-surface))
861
- );--nut-dl-accent:var(--ui-primary);--nut-dl-cell-fg:var(--nut-dl-table-cell-fg,var(--ui-text-toned));--nut-dl-pin-line:var(--nut-dl-table-pin-line,var(--nut-dl-line))}.nut-dl-table__scroll{overflow-anchor:none;scrollbar-width:none}.nut-dl-table__scroll::-webkit-scrollbar{display:none}.nut-dl-th{color:var(--nut-dl-head-fg);font-size:var(--nut-dl-head-font);font-weight:var(--nut-dl-head-weight);letter-spacing:var(--nut-dl-head-tracking);text-transform:var(--nut-dl-head-transform);-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.nut-dl-table__spacer,.nut-dl-th{border-bottom:1px solid var(--nut-dl-line)}.nut-dl-table__spacer,.nut-dl-tf,.nut-dl-th{background:var(--nut-dl-head-bg);border-top:1px solid var(--nut-dl-line)}.nut-dl-tf{color:var(--ui-text-highlighted);font-weight:var(--nut-dl-table-foot-weight,500);white-space:nowrap}tfoot .nut-dl-table__spacer{border-bottom:0;border-top:1px solid var(--nut-dl-line)}.nut-dl-spinner{animation:nut-dl-spin .8s linear infinite}@keyframes nut-dl-spin{to{transform:rotate(1turn)}}tbody .nut-dl-table__spacer{background:var(--nut-dl-surface);border-bottom:1px solid var(--nut-dl-line-soft)}.nut-dl-th__btn--open{background:color-mix(in srgb,var(--nut-dl-head-fg) 8%,transparent)}.nut-dl-rz:after{background:var(--ui-border-accented);bottom:10px;content:"";opacity:0;position:absolute;right:0;top:10px;transition:opacity .12s;width:1px}.nut-dl-rz:hover:after,.nut-dl-th:hover .nut-dl-rz:after{opacity:1}.nut-dl-rz--active:after,.nut-dl-rz:hover:after{background:var(--nut-dl-accent);bottom:6px;left:2.5px;opacity:1;top:6px;width:2px}.nut-dl-row{background:var(--nut-dl-surface);transition:background-color .1s}.nut-dl-row:hover{background:var(--nut-dl-row-hover)}.nut-dl-row--selected,.nut-dl-row--selected:hover{background:var(--nut-dl-row-selected)}.nut-dl-td,.nut-dl-th__btn,.nut-dl-th__static{padding-left:var(--nut-dl-cell-l,var(--nut-dl-cell-x));padding-right:var(--nut-dl-cell-r,var(--nut-dl-cell-x))}.nut-dl-row>.nut-dl-td:first-child,.nut-dl-table__head-row>.nut-dl-th:first-child{--nut-dl-cell-l:var(--nut-dl-gutter)}.nut-dl-row>.nut-dl-td:last-child,.nut-dl-table__head-row>.nut-dl-th:last-child{--nut-dl-cell-r:var(--nut-dl-gutter)}.nut-dl-row>.nut-dl-td--actions:last-child,.nut-dl-table__head-row>.nut-dl-th--actions:last-child,.nut-dl-td--actions{--nut-dl-cell-l:var(--nut-dl-cell-x);--nut-dl-cell-r:var(--nut-dl-cell-x)}.nut-dl-td--actions .nut-dl-td__inner{justify-content:flex-end}.nut-dl-td{background:inherit;border-bottom:1px solid var(--nut-dl-line-soft);box-sizing:border-box;color:var(--nut-dl-cell-fg);overflow:hidden;overflow-wrap:anywhere;padding-block:var(--nut-dl-cell-y,6px);white-space:normal}.nut-dl-td__inner{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--nut-dl-lines,3);line-clamp:var(--nut-dl-lines,3);max-height:calc(var(--nut-dl-lines, 3)*1.5em);overflow:hidden}.nut-dl-td--ellipsis{--nut-dl-lines:1;white-space:nowrap}.nut-dl-td--ellipsis .nut-dl-td__inner{display:block;text-overflow:ellipsis}.nut-dl-td--actions .nut-dl-td__inner,.nut-dl-td--selection .nut-dl-td__inner{align-items:center;display:flex}.nut-dl-td--actions,.nut-dl-td--selection{white-space:nowrap}.nut-dl-td--actions .nut-dl-td__inner>*{opacity:.55;transition:opacity .12s}.nut-dl-row:hover .nut-dl-td--actions .nut-dl-td__inner>*{opacity:1}.nut-dl-pin{position:sticky}.nut-dl-pin--first-end:before,.nut-dl-pin--last-start:after{background:var(--nut-dl-line);bottom:0;content:"";pointer-events:none;position:absolute;top:0;width:1px}.nut-dl-pin--last-start:after{right:0}.nut-dl-pin--first-end:before{left:0}.nut-dl-pin--first-end:before,.nut-dl-pin--last-start:after{background:var(--nut-dl-pin-line)}.nut-dl-td--actions.nut-dl-pin--first-end:before,.nut-dl-th--actions.nut-dl-pin--first-end:before{display:none}.nut-dl-progress th{overflow:hidden}.nut-dl-progress th span{animation:nut-dl-progress 1s ease-in-out infinite paused;background:linear-gradient(90deg,transparent,var(--nut-dl-accent),transparent);background-size:40% 100%;height:2px;left:0;opacity:0;position:absolute;right:0;top:0;transition:opacity .12s ease-out}.nut-dl-progress[data-active=true] th span{animation-play-state:running;opacity:1}@keyframes nut-dl-progress{0%{background-position:-40% 0}to{background-position:140% 0}}.nut-dl-skeleton{animation:nut-dl-shimmer 1.6s ease-in-out infinite;animation-delay:calc(var(--nut-dl-i, 0)*-90ms);background:linear-gradient(90deg,var(--ui-bg-accented) 0,var(--ui-bg-muted) 45%,var(--ui-bg-accented) 100%);background-size:200% 100%}.nut-dl-row--skeleton{animation:nut-dl-fade-in .2s ease both;animation-delay:calc(var(--nut-dl-i, 0)*18ms)}@keyframes nut-dl-fade-in{0%{opacity:0}to{opacity:1}}@keyframes nut-dl-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}.nut-dl-table[data-virtualized=true] .nut-dl-row[style*="--nut-dl-i"]{animation:nut-dl-row-in .22s cubic-bezier(.2,.9,.3,1) both;animation-delay:calc(var(--nut-dl-i)*10ms)}@keyframes nut-dl-row-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.nut-dl-row--moving{transition:transform .24s cubic-bezier(.2,.9,.3,1)}.nut-dl-row--settling{transition:opacity .18s ease,transform .24s cubic-bezier(.2,.9,.3,1)}.nut-dl-row--from{opacity:0;transform:translateY(4px)}@media (prefers-reduced-motion:reduce){.nut-dl-row,.nut-dl-row--moving,.nut-dl-row--settling{transition:none}.nut-dl-progress th span,.nut-dl-skeleton,.nut-dl-table[data-virtualized=true] .nut-dl-row[style*="--nut-dl-i"]{animation:none}}
767
+ );--nut-dl-accent:var(--ui-primary);--nut-dl-cell-fg:var(--nut-dl-table-cell-fg,var(--ui-text-toned));--nut-dl-pin-line:var(--nut-dl-table-pin-line,var(--nut-dl-line))}.nut-dl-table__scroll{overflow-anchor:none;scrollbar-width:none}.nut-dl-table__scroll::-webkit-scrollbar{display:none}.nut-dl-th{color:var(--nut-dl-head-fg);font-size:var(--nut-dl-head-font);font-weight:var(--nut-dl-head-weight);letter-spacing:var(--nut-dl-head-tracking);text-transform:var(--nut-dl-head-transform);-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.nut-dl-table__fill,.nut-dl-table__spacer,.nut-dl-th{background:var(--nut-dl-head-bg);border-bottom:1px solid var(--nut-dl-line)}.nut-dl-tf{background:var(--nut-dl-head-bg);border-top:1px solid var(--nut-dl-line);color:var(--ui-text-highlighted);font-weight:var(--nut-dl-table-foot-weight,500);white-space:nowrap}tfoot .nut-dl-table__fill,tfoot .nut-dl-table__spacer{border-bottom:0;border-top:1px solid var(--nut-dl-line)}.nut-dl-spinner{animation:nut-dl-spin .8s linear infinite}@keyframes nut-dl-spin{to{transform:rotate(1turn)}}tbody .nut-dl-table__spacer{background:var(--nut-dl-surface);border-bottom:1px solid var(--nut-dl-line-soft)}tbody .nut-dl-table__fill{background:inherit;border-bottom:1px solid var(--nut-dl-line-soft)}.nut-dl-th__btn--open{background:color-mix(in srgb,var(--nut-dl-head-fg) 8%,transparent)}.nut-dl-rz:after{background:var(--ui-border-accented);bottom:10px;content:"";opacity:0;position:absolute;right:0;top:10px;transition:opacity .12s;width:1px}.nut-dl-rz:hover:after,.nut-dl-th:hover .nut-dl-rz:after{opacity:1}.nut-dl-rz--active:after,.nut-dl-rz:hover:after{background:var(--nut-dl-accent);bottom:6px;left:2.5px;opacity:1;top:6px;width:2px}.nut-dl-row{background:var(--nut-dl-surface);transition:background-color .1s}.nut-dl-row:hover{background:var(--nut-dl-row-hover)}.nut-dl-row--selected,.nut-dl-row--selected:hover{background:var(--nut-dl-row-selected)}.nut-dl-td,.nut-dl-tf[data-col],.nut-dl-th__btn,.nut-dl-th__static{padding-left:var(--nut-dl-cell-l,var(--nut-dl-cell-x));padding-right:var(--nut-dl-cell-r,var(--nut-dl-cell-x))}.nut-dl-row>.nut-dl-td:first-child,.nut-dl-table__foot-row>.nut-dl-tf:first-child,.nut-dl-table__head-row>.nut-dl-th:first-child{--nut-dl-cell-l:var(--nut-dl-gutter)}.nut-dl-row>.nut-dl-td:last-child,.nut-dl-table__foot-row>.nut-dl-tf:last-child,.nut-dl-table__head-row>.nut-dl-th:last-child{--nut-dl-cell-r:var(--nut-dl-gutter)}.nut-dl-row>.nut-dl-td--actions:last-child,.nut-dl-table__foot-row>.nut-dl-tf--actions:last-child,.nut-dl-table__head-row>.nut-dl-th--actions:last-child,.nut-dl-td--actions,.nut-dl-tf--actions{--nut-dl-cell-l:var(--nut-dl-cell-x);--nut-dl-cell-r:var(--nut-dl-cell-x)}.nut-dl-td--actions .nut-dl-td__inner{justify-content:flex-end}.nut-dl-td{background:inherit;border-bottom:1px solid var(--nut-dl-line-soft);box-sizing:border-box;color:var(--nut-dl-cell-fg);contain:paint;overflow:hidden;overflow-wrap:anywhere;padding-block:var(--nut-dl-cell-y,6px);white-space:normal}.nut-dl-td__inner{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--nut-dl-lines,3);line-clamp:var(--nut-dl-lines,3);max-height:calc(var(--nut-dl-lines, 3)*1.5em);overflow:hidden}.nut-dl-td--ellipsis{--nut-dl-lines:1;white-space:nowrap}.nut-dl-td--ellipsis .nut-dl-td__inner{display:block;text-overflow:ellipsis}.nut-dl-td--actions .nut-dl-td__inner,.nut-dl-td--selection .nut-dl-td__inner{align-items:center;display:flex}.nut-dl-td--actions,.nut-dl-td--selection{white-space:nowrap}.nut-dl-td--actions .nut-dl-td__inner>*{opacity:.55;transition:opacity .12s}.nut-dl-row:hover .nut-dl-td--actions .nut-dl-td__inner>*{opacity:1}.nut-dl-pin{position:sticky}.nut-dl-pin--first-end:before,.nut-dl-pin--last-start:after{background:var(--nut-dl-line);bottom:0;content:"";pointer-events:none;position:absolute;top:0;width:1px}.nut-dl-pin--last-start:after{right:0}.nut-dl-pin--first-end:before{left:0}.nut-dl-pin--first-end:before,.nut-dl-pin--last-start:after{background:var(--nut-dl-pin-line)}.nut-dl-td--actions.nut-dl-pin--first-end:before,.nut-dl-th--actions.nut-dl-pin--first-end:before{display:none}.nut-dl-skeleton{animation:nut-dl-shimmer 1.6s ease-in-out infinite;animation-delay:calc(var(--nut-dl-i, 0)*-90ms);background:linear-gradient(90deg,var(--ui-bg-accented) 0,var(--ui-bg-muted) 45%,var(--ui-bg-accented) 100%);background-size:200% 100%}.nut-dl-row--skeleton{animation:nut-dl-fade-in .18s ease .12s both}.nut-dl-row--appended{animation:nut-dl-fade-in .2s ease both}.nut-dl-table__body{transition:opacity .16s ease-out}.nut-dl-table__body[data-replacing=true]{opacity:.55;transition:opacity .2s ease-in .18s}@keyframes nut-dl-fade-in{0%{opacity:0}to{opacity:1}}@keyframes nut-dl-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media (prefers-reduced-motion:reduce){.nut-dl-row,.nut-dl-table__body{transition:none}.nut-dl-row--appended,.nut-dl-row--skeleton,.nut-dl-skeleton{animation:none}}
862
768
  </style>