nuxt-ui-tools 1.17.0 → 1.18.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 (122) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +1 -0
  3. package/dist/runtime/form/components/page/form-page-navigation.d.vue.ts +5 -3
  4. package/dist/runtime/form/components/page/form-page-navigation.vue +6 -3
  5. package/dist/runtime/form/components/page/form-page-navigation.vue.d.ts +5 -3
  6. package/dist/runtime/form/components/page/form-page-section.vue +6 -5
  7. package/dist/runtime/form/components/page/form-page-sections.d.vue.ts +4 -3
  8. package/dist/runtime/form/components/page/form-page-sections.vue +7 -2
  9. package/dist/runtime/form/components/page/form-page-sections.vue.d.ts +4 -3
  10. package/dist/runtime/form/components/page/form-page.d.vue.ts +2 -0
  11. package/dist/runtime/form/components/page/form-page.vue +10 -2
  12. package/dist/runtime/form/components/page/form-page.vue.d.ts +2 -0
  13. package/dist/runtime/form/components/renderer/form-field-renderer.vue +6 -4
  14. package/dist/runtime/form/components/renderer/form-field-shell.d.vue.ts +4 -4
  15. package/dist/runtime/form/components/renderer/form-field-shell.vue +28 -4
  16. package/dist/runtime/form/components/renderer/form-field-shell.vue.d.ts +4 -4
  17. package/dist/runtime/form/components/root/form.vue +2 -2
  18. package/dist/runtime/form/composables/use-field-control.d.ts +1 -1
  19. package/dist/runtime/form/composables/use-field-control.js +9 -21
  20. package/dist/runtime/form/composables/use-field-options.js +8 -4
  21. package/dist/runtime/form/composables/use-form-page.d.ts +11 -1
  22. package/dist/runtime/form/composables/use-form-page.js +12 -6
  23. package/dist/runtime/form/composables/use-form-runtime.js +1 -1
  24. package/dist/runtime/form/fields/array-primitive/types.d.ts +1 -1
  25. package/dist/runtime/form/fields/checkbox-card/component.vue +54 -28
  26. package/dist/runtime/form/fields/checkbox-card/config.d.ts +1 -0
  27. package/dist/runtime/form/fields/checkbox-card/config.js +1 -1
  28. package/dist/runtime/form/fields/checkbox-card/types.d.ts +3 -3
  29. package/dist/runtime/form/fields/choice-card/choice-card-label.d.vue.ts +7 -0
  30. package/dist/runtime/form/fields/choice-card/choice-card-label.vue +27 -1
  31. package/dist/runtime/form/fields/choice-card/choice-card-label.vue.d.ts +7 -0
  32. package/dist/runtime/form/fields/choice-card/types.d.ts +54 -1
  33. package/dist/runtime/form/fields/choice-card/use-choice-card.d.ts +7 -3
  34. package/dist/runtime/form/fields/choice-card/use-choice-card.js +37 -15
  35. package/dist/runtime/form/fields/choice-card/utils.d.ts +27 -0
  36. package/dist/runtime/form/fields/choice-card/utils.js +17 -0
  37. package/dist/runtime/form/fields/hierarchy/component.vue +3 -2
  38. package/dist/runtime/form/fields/index.d.ts +2 -0
  39. package/dist/runtime/form/fields/radio-card/component.vue +58 -28
  40. package/dist/runtime/form/fields/radio-card/config.d.ts +1 -0
  41. package/dist/runtime/form/fields/radio-card/config.js +1 -1
  42. package/dist/runtime/form/fields/radio-card/types.d.ts +3 -3
  43. package/dist/runtime/form/types/field-base.d.ts +6 -0
  44. package/dist/runtime/form/types/field-kind.d.ts +3 -1
  45. package/dist/runtime/form/types/field.d.ts +1 -0
  46. package/dist/runtime/form/types/page.d.ts +20 -8
  47. package/dist/runtime/form/types/ui.d.ts +24 -4
  48. package/dist/runtime/form/utils/disabled.d.ts +11 -0
  49. package/dist/runtime/form/utils/disabled.js +11 -0
  50. package/dist/runtime/form/utils/field-instance.js +3 -0
  51. package/dist/runtime/form/utils/options.d.ts +22 -1
  52. package/dist/runtime/form/utils/options.js +15 -0
  53. package/dist/runtime/form/utils/page-surface.d.ts +21 -0
  54. package/dist/runtime/form/utils/page-surface.js +27 -0
  55. package/dist/runtime/form/utils/page.d.ts +21 -1
  56. package/dist/runtime/form/utils/page.js +24 -2
  57. package/dist/runtime/i18n/locales/en.js +28 -8
  58. package/dist/runtime/i18n/locales/fr.js +28 -8
  59. package/dist/runtime/i18n/types.d.ts +46 -7
  60. package/dist/runtime/table/components/actions/row-actions.d.vue.ts +2 -0
  61. package/dist/runtime/table/components/actions/row-actions.vue +8 -3
  62. package/dist/runtime/table/components/actions/row-actions.vue.d.ts +2 -0
  63. package/dist/runtime/table/components/data-list/data-list-content.vue +7 -1
  64. package/dist/runtime/table/components/data-list/data-list-default.d.vue.ts +16 -16
  65. package/dist/runtime/table/components/data-list/data-list-default.vue +3 -0
  66. package/dist/runtime/table/components/data-list/data-list-default.vue.d.ts +16 -16
  67. package/dist/runtime/table/components/data-list/data-list-layout-switch.vue +7 -9
  68. package/dist/runtime/table/components/data-list/data-list-root.vue +5 -6
  69. package/dist/runtime/table/components/data-list/data-list-sort-menu.vue +3 -1
  70. package/dist/runtime/table/components/data-list/data-list-table.d.vue.ts +8 -1
  71. package/dist/runtime/table/components/data-list/data-list-table.vue +18 -1
  72. package/dist/runtime/table/components/data-list/data-list-table.vue.d.ts +8 -1
  73. package/dist/runtime/table/components/data-list/data-list-view-toggle.d.vue.ts +25 -0
  74. package/dist/runtime/table/components/data-list/data-list-view-toggle.vue +57 -0
  75. package/dist/runtime/table/components/data-list/data-list-view-toggle.vue.d.ts +25 -0
  76. package/dist/runtime/table/components/data-list/sort-menu-panel.vue +1 -1
  77. package/dist/runtime/table/components/layout/table-footer.d.vue.ts +6 -4
  78. package/dist/runtime/table/components/layout/table-footer.vue +80 -3
  79. package/dist/runtime/table/components/layout/table-footer.vue.d.ts +6 -4
  80. package/dist/runtime/table/components/summary/summary-content.d.ts +13 -0
  81. package/dist/runtime/table/components/summary/summary-content.js +5 -0
  82. package/dist/runtime/table/components/summary/summary-renderer.d.vue.ts +31 -0
  83. package/dist/runtime/table/components/summary/summary-renderer.vue +282 -0
  84. package/dist/runtime/table/components/summary/summary-renderer.vue.d.ts +31 -0
  85. package/dist/runtime/table/components/summary/summary-row.d.vue.ts +19 -0
  86. package/dist/runtime/table/components/summary/summary-row.vue +102 -0
  87. package/dist/runtime/table/components/summary/summary-row.vue.d.ts +19 -0
  88. package/dist/runtime/table/components/table/table-empty-state.vue +39 -9
  89. package/dist/runtime/table/components/table/table-renderer.vue +1 -1
  90. package/dist/runtime/table/components/table/table-row-actions-control.vue +10 -6
  91. package/dist/runtime/table/components/table/table-selection-control.d.vue.ts +5 -1
  92. package/dist/runtime/table/components/table/table-selection-control.vue +17 -4
  93. package/dist/runtime/table/components/table/table-selection-control.vue.d.ts +5 -1
  94. package/dist/runtime/table/components/table/table-tree-control.vue +2 -1
  95. package/dist/runtime/table/components/table/table-tree-header.vue +0 -1
  96. package/dist/runtime/table/composables/use-table-api.d.ts +2 -0
  97. package/dist/runtime/table/composables/use-table-api.js +8 -0
  98. package/dist/runtime/table/composables/use-table-columns.d.ts +2 -0
  99. package/dist/runtime/table/composables/use-table-controls.d.ts +6 -0
  100. package/dist/runtime/table/composables/use-table-controls.js +6 -3
  101. package/dist/runtime/table/composables/use-table-internals.d.ts +33 -0
  102. package/dist/runtime/table/composables/use-table-internals.js +9 -0
  103. package/dist/runtime/table/composables/use-table-row-labels.d.ts +16 -0
  104. package/dist/runtime/table/composables/use-table-row-labels.js +22 -0
  105. package/dist/runtime/table/composables/use-table-row-summary.d.ts +18 -0
  106. package/dist/runtime/table/composables/use-table-row-summary.js +41 -0
  107. package/dist/runtime/table/constants/mobile.d.ts +79 -0
  108. package/dist/runtime/table/constants/mobile.js +17 -0
  109. package/dist/runtime/table/constants/summary.d.ts +67 -0
  110. package/dist/runtime/table/constants/summary.js +31 -0
  111. package/dist/runtime/table/types/data-list.d.ts +58 -8
  112. package/dist/runtime/table/types/layout.d.ts +73 -0
  113. package/dist/runtime/table/types/schema.d.ts +9 -1
  114. package/dist/runtime/table/types/source.d.ts +6 -1
  115. package/dist/runtime/table/types/table-api.d.ts +12 -0
  116. package/dist/runtime/table/utils/columns/defs.js +3 -1
  117. package/dist/runtime/table/utils/columns/render.d.ts +2 -0
  118. package/dist/runtime/table/utils/columns/render.js +2 -2
  119. package/dist/runtime/table/utils/data-list-ui.js +3 -1
  120. package/dist/runtime/table/utils/mobile-defaults.d.ts +7 -0
  121. package/dist/runtime/table/utils/mobile-defaults.js +25 -0
  122. package/package.json +1 -1
@@ -0,0 +1,282 @@
1
+ <script setup>
2
+ import { useVirtualizer } from "@tanstack/vue-virtual";
3
+ import { useElementSize } from "@vueuse/core";
4
+ import { computed, nextTick, useTemplateRef, watch } from "vue";
5
+ import { useUiToolsLocale } from "#ui-tools/i18n";
6
+ import { useDataListUi } from "../../composables/use-data-list-ui";
7
+ import { useTableInternals } from "../../composables/use-table-internals";
8
+ import { useTableRowLabels } from "../../composables/use-table-row-labels";
9
+ import {
10
+ TABLE_SUMMARY_LOAD_MORE_THRESHOLD,
11
+ TABLE_SUMMARY_SIZE_TOKENS,
12
+ TABLE_SUMMARY_SKELETON_MIN_ROWS,
13
+ TABLE_SUMMARY_SLOT_CLASSES,
14
+ TABLE_SUMMARY_VIRTUALIZE_THRESHOLD
15
+ } from "../../constants/summary";
16
+ import { mergeDataListUiClass } from "../../utils";
17
+ import { ROW_ACTIONS_COLUMN_ID } from "../../utils/columns/types";
18
+ import DataListErrorState from "../data-list/data-list-error-state.vue";
19
+ import ProgressLine from "../layout/progress-line.vue";
20
+ import TableEmptyState from "../table/table-empty-state.vue";
21
+ import TableSelectionControl from "../table/table-selection-control.vue";
22
+ import SummaryRow from "./summary-row.vue";
23
+ const props = defineProps({
24
+ height: { type: String, required: false },
25
+ size: { type: String, required: false },
26
+ fill: { type: Boolean, required: false },
27
+ ui: { type: Object, required: false }
28
+ });
29
+ const internals = useTableInternals();
30
+ const { selection } = internals;
31
+ const { status } = internals.queryContent;
32
+ const dataListUi = useDataListUi();
33
+ const labels = useTableRowLabels();
34
+ const { t } = useUiToolsLocale();
35
+ const rows = computed(
36
+ () => internals.queryContent.error.value ? [] : internals.queryContent.data.value.rows
37
+ );
38
+ const isFirstLoad = computed(
39
+ () => status.value.isBooting || (status.value.isPending || status.value.isFetching) && rows.value.length === 0
40
+ );
41
+ const refreshing = computed(
42
+ () => rows.value.length > 0 && (status.value.isFetching || status.value.isRefreshing || status.value.isRevalidating)
43
+ );
44
+ const replacing = computed(() => rows.value.length > 0 && status.value.isReplacing);
45
+ const error = computed(() => rows.value.length === 0 ? internals.queryContent.error.value : null);
46
+ const empty = computed(() => !isFirstLoad.value && !error.value && rows.value.length === 0);
47
+ const scrolls = computed(() => Boolean(props.fill || props.height));
48
+ const resolvedSize = computed(
49
+ () => props.size ?? dataListUi.ui.value.table?.size ?? dataListUi.controlSize.value
50
+ );
51
+ const tokens = computed(() => TABLE_SUMMARY_SIZE_TOKENS[resolvedSize.value]);
52
+ const tokenStyle = computed(() => ({
53
+ "--nut-dl-summary-font": `${tokens.value.font}px`,
54
+ "--nut-dl-summary-meta": `${tokens.value.font - 1}px`,
55
+ "--nut-dl-summary-row-h": `${tokens.value.row}px`,
56
+ "--nut-dl-summary-tile": `${tokens.value.tile}px`,
57
+ "--nut-dl-summary-x": `${dataListUi.ui.value.table?.gutter ?? tokens.value.gutter}px`
58
+ }));
59
+ const hasActions = computed(
60
+ () => internals.tableColumns.runtimeColumns.value.some((column) => column.id === ROW_ACTIONS_COLUMN_ID)
61
+ );
62
+ const selectAllState = computed(
63
+ () => selection.allSelected.value || selection.partiallySelected.value && "indeterminate"
64
+ );
65
+ function css(slot, extra = "") {
66
+ return mergeDataListUiClass(
67
+ `${TABLE_SUMMARY_SLOT_CLASSES[slot]} ${extra}`,
68
+ void 0,
69
+ props.ui?.[slot]
70
+ );
71
+ }
72
+ function rowIdAt(index) {
73
+ const row = rows.value[index];
74
+ return row ? selection.getRowId({ index, row }) : String(index);
75
+ }
76
+ function refresh() {
77
+ void internals.queryContent.refreshData()();
78
+ }
79
+ function toggleAll() {
80
+ selection.toggleAllRows({ selected: !selection.allSelected.value });
81
+ }
82
+ function onSelectRow(rowId, event) {
83
+ selection.toggleRowSelection({
84
+ rowId,
85
+ selected: !selection.isRowSelected({ rowId }),
86
+ shiftKey: event.shiftKey
87
+ });
88
+ }
89
+ function skeletonTitleWidth(seed) {
90
+ return 45 + seed * 37 % 40;
91
+ }
92
+ const scrollRef = useTemplateRef("scrollRef");
93
+ const { height: viewportHeight } = useElementSize(scrollRef);
94
+ const skeletonCount = computed(
95
+ () => Math.max(TABLE_SUMMARY_SKELETON_MIN_ROWS, Math.ceil(viewportHeight.value / tokens.value.row))
96
+ );
97
+ const virtualized = computed(() => rows.value.length >= TABLE_SUMMARY_VIRTUALIZE_THRESHOLD);
98
+ const rowVirtualizer = useVirtualizer(
99
+ computed(() => ({
100
+ count: virtualized.value ? rows.value.length : 0,
101
+ estimateSize: () => tokens.value.row,
102
+ getItemKey: (index) => rowIdAt(index),
103
+ getScrollElement: () => scrollRef.value ?? null,
104
+ measureElement: (element) => element.getBoundingClientRect().height,
105
+ overscan: 12
106
+ }))
107
+ );
108
+ const virtualRows = computed(
109
+ () => virtualized.value ? rowVirtualizer.value.getVirtualItems() : []
110
+ );
111
+ const paddingTop = computed(() => virtualRows.value[0]?.start ?? 0);
112
+ const paddingBottom = computed(
113
+ () => virtualRows.value.length ? rowVirtualizer.value.getTotalSize() - (virtualRows.value.at(-1)?.end ?? 0) : 0
114
+ );
115
+ const renderedRows = computed(
116
+ () => virtualized.value ? virtualRows.value.flatMap((item) => {
117
+ const row = rows.value[item.index];
118
+ return row ? [{ index: item.index, row, rowId: rowIdAt(item.index) }] : [];
119
+ }) : rows.value.map((row, index) => ({ index, row, rowId: rowIdAt(index) }))
120
+ );
121
+ function measureRow(target) {
122
+ const element = target instanceof Element ? target : target?.$el;
123
+ if (!(element instanceof HTMLElement)) return;
124
+ if (element.isConnected) rowVirtualizer.value.measureElement(element);
125
+ else nextTick().then(() => element.isConnected && rowVirtualizer.value.measureElement(element));
126
+ }
127
+ const cursorMode = computed(() => internals.pagination.mode.value === "cursor");
128
+ const loadingMore = computed(
129
+ () => cursorMode.value && internals.pagination.state.value.isLoadingMore
130
+ );
131
+ const hasNextPage = computed(() => cursorMode.value && internals.pagination.state.value.hasNextPage);
132
+ watch(
133
+ () => [
134
+ virtualRows.value.at(-1)?.index ?? rows.value.length - 1,
135
+ rows.value.length,
136
+ hasNextPage.value,
137
+ loadingMore.value
138
+ ],
139
+ ([lastIndex, count, more, loading]) => {
140
+ if (!more || loading || !count) return;
141
+ const threshold = Math.max(TABLE_SUMMARY_LOAD_MORE_THRESHOLD, virtualRows.value.length);
142
+ if (lastIndex >= count - 1 - threshold) void internals.pagination.loadMore();
143
+ }
144
+ );
145
+ let scrollResetPending = false;
146
+ watch(
147
+ () => [
148
+ internals.pagination.currentPage.value,
149
+ internals.pagination.pageSize.value,
150
+ JSON.stringify(internals.tableColumns.sortingState.value),
151
+ JSON.stringify(internals.queryState.filters.value),
152
+ internals.filters.searchQuery.value
153
+ ],
154
+ () => {
155
+ if (!cursorMode.value) scrollResetPending = true;
156
+ }
157
+ );
158
+ function resetScroll() {
159
+ if (!scrollResetPending) return;
160
+ scrollResetPending = false;
161
+ scrollRef.value?.scrollTo({ top: 0 });
162
+ }
163
+ watch(rows, resetScroll);
164
+ watch(
165
+ () => status.value.isFetching,
166
+ (fetching) => !fetching && resetScroll()
167
+ );
168
+ </script>
169
+
170
+ <template>
171
+ <div
172
+ :class="
173
+ mergeDataListUiClass(
174
+ `${TABLE_SUMMARY_SLOT_CLASSES.root} ${fill ? 'h-full' : ''}`,
175
+ void 0,
176
+ ui?.root
177
+ )
178
+ "
179
+ :style="{ ...tokenStyle, height: !fill && height ? height : void 0 }"
180
+ :data-size="resolvedSize"
181
+ :data-virtualized="virtualized"
182
+ :data-loading="isFirstLoad"
183
+ :data-replacing="replacing"
184
+ >
185
+ <div v-if="selection.selectionEnabled.value && rows.length > 0" :class="css('header')">
186
+ <TableSelectionControl
187
+ class="min-h-11 min-w-11 justify-center"
188
+ :model-value="selectAllState"
189
+ @toggle="toggleAll"
190
+ />
191
+ <span>{{ labels.selectAll() }}</span>
192
+ </div>
193
+
194
+ <div ref="scrollRef" :class="css('viewport', scrolls ? 'flex flex-1 flex-col' : '')">
195
+ <div
196
+ v-if="error"
197
+ class="nut-dl-summary__state flex w-full flex-col"
198
+ :class="scrolls ? 'min-h-0 flex-1' : ''"
199
+ >
200
+ <slot name="error" :error="error" :retry="refresh">
201
+ <DataListErrorState
202
+ :min-height="scrolls ? void 0 : '16rem'"
203
+ :size="resolvedSize"
204
+ class="flex-1"
205
+ @retry="refresh"
206
+ />
207
+ </slot>
208
+ </div>
209
+ <div
210
+ v-else-if="empty"
211
+ class="nut-dl-summary__state flex w-full flex-col"
212
+ :class="scrolls ? 'min-h-0 flex-1' : ''"
213
+ >
214
+ <slot name="empty">
215
+ <TableEmptyState
216
+ :min-height="scrolls ? void 0 : '16rem'"
217
+ :size="resolvedSize"
218
+ class="flex-1"
219
+ />
220
+ </slot>
221
+ </div>
222
+ <ul v-else role="list" :class="css('list')">
223
+ <li
224
+ v-for="placeholder in isFirstLoad ? skeletonCount : 0"
225
+ :key="`skeleton-${placeholder}`"
226
+ :class="css('skeleton')"
227
+ aria-hidden="true"
228
+ >
229
+ <span
230
+ class="nut-dl-skeleton size-[var(--nut-dl-summary-tile)] shrink-0 rounded-lg"
231
+ :style="{ '--nut-dl-i': placeholder }"
232
+ />
233
+ <span class="min-w-0 flex-1">
234
+ <span
235
+ class="nut-dl-skeleton block h-3 rounded-full"
236
+ :style="{ '--nut-dl-i': placeholder, width: `${skeletonTitleWidth(placeholder)}%` }"
237
+ />
238
+ <span
239
+ class="nut-dl-skeleton mt-2 block h-2.5 w-1/3 rounded-full"
240
+ :style="{ '--nut-dl-i': placeholder }"
241
+ />
242
+ </span>
243
+ </li>
244
+
245
+ <li v-if="paddingTop" :style="{ height: `${paddingTop}px` }" aria-hidden="true" />
246
+ <SummaryRow
247
+ v-for="entry in renderedRows"
248
+ :key="entry.rowId"
249
+ :ref="measureRow"
250
+ :row-id="entry.rowId"
251
+ :original="entry.row"
252
+ :index="entry.index"
253
+ :selected="selection.isRowSelected({ rowId: entry.rowId })"
254
+ :selectable="selection.selectionEnabled.value"
255
+ :actions="hasActions"
256
+ :ui="ui"
257
+ @select="onSelectRow(entry.rowId, $event)"
258
+ />
259
+ <li v-if="paddingBottom" :style="{ height: `${paddingBottom}px` }" aria-hidden="true" />
260
+
261
+ <li v-if="loadingMore" :class="css('loadingMore')" aria-hidden="true">
262
+ <span
263
+ class="nut-dl-spinner size-3.5 rounded-full border-2 border-accented border-t-primary"
264
+ />
265
+ <span>{{ t("table.controls.loadingMore") }}</span>
266
+ </li>
267
+ </ul>
268
+ </div>
269
+
270
+ <ProgressLine
271
+ :active="refreshing"
272
+ :restart-key="internals.queryContent.refreshes.value"
273
+ class="absolute inset-x-0 top-0 z-[6]"
274
+ />
275
+ </div>
276
+ </template>
277
+
278
+ <style>
279
+ .nut-dl-summary__list{transition:opacity .16s ease-out}.nut-dl-summary[data-replacing=true] .nut-dl-summary__list{opacity:.55;transition:opacity .2s ease-in .18s}.nut-dl-summary__row{transition:background-color .1s}.nut-dl-summary__row[data-interactive=true]:active{background:var(--nut-dl-summary-row-pressed,var(--ui-bg-muted))}@media (hover:hover){.nut-dl-summary__row[data-interactive=true]:hover{background:var(--nut-dl-summary-row-hover,var(--ui-bg-elevated))}}.nut-dl-summary__row[data-selected=true],.nut-dl-summary__row[data-selected=true]:hover{background:var(
280
+ --nut-dl-summary-row-selected,color-mix(in srgb,var(--ui-primary) 10%,var(--ui-bg))
281
+ )}.nut-dl-summary__skeleton{animation:nut-dl-summary-in .18s ease .12s both}@keyframes nut-dl-summary-in{0%{opacity:0}to{opacity:1}}@media (prefers-reduced-motion:reduce){.nut-dl-summary__list,.nut-dl-summary__row{transition:none}.nut-dl-summary__skeleton{animation:none}}
282
+ </style>
@@ -0,0 +1,31 @@
1
+ import type { DataListControlSize, DataListSummaryUi } from '../../types/index.js';
2
+ /**
3
+ * The table layout read as one compact row per record, the way a phone screen needs it. It takes
4
+ * the table renderer's place while `table.summary` is active and keeps its states: first-load
5
+ * placeholders, error, empty, refreshing, cursor pages and a virtualized window for long lists.
6
+ */
7
+ type __VLS_Props = {
8
+ height?: string;
9
+ size?: DataListControlSize;
10
+ fill?: boolean;
11
+ ui?: DataListSummaryUi;
12
+ };
13
+ declare function refresh(): void;
14
+ declare var __VLS_8: {
15
+ error: {};
16
+ retry: typeof refresh;
17
+ }, __VLS_17: {};
18
+ type __VLS_Slots = {} & {
19
+ error?: (props: typeof __VLS_8) => any;
20
+ } & {
21
+ empty?: (props: typeof __VLS_17) => any;
22
+ };
23
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
24
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
25
+ declare const _default: typeof __VLS_export;
26
+ export default _default;
27
+ type __VLS_WithSlots<T, S> = T & {
28
+ new (): {
29
+ $slots: S;
30
+ };
31
+ };
@@ -0,0 +1,19 @@
1
+ import type { DataListSummaryUi, GenericObject } from '../../types/index.js';
2
+ type __VLS_Props = {
3
+ rowId: string;
4
+ original: GenericObject;
5
+ index: number;
6
+ selected: boolean;
7
+ /** The row carries a checkbox. */
8
+ selectable: boolean;
9
+ /** The row carries the row-actions button. */
10
+ actions: boolean;
11
+ ui?: DataListSummaryUi;
12
+ };
13
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
+ select: (event: MouseEvent) => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
+ onSelect?: ((event: MouseEvent) => any) | undefined;
17
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
@@ -0,0 +1,102 @@
1
+ <script setup>
2
+ import ULink from "@nuxt/ui/components/Link.vue";
3
+ import { computed } from "vue";
4
+ import { isNullish } from "../../../shared/utils/predicate";
5
+ import { useTableInternals } from "../../composables/use-table-internals";
6
+ import {
7
+ TABLE_SUMMARY_DETAILS_SEPARATOR,
8
+ TABLE_SUMMARY_SLOT_CLASSES
9
+ } from "../../constants/summary";
10
+ import { mergeDataListUiClass } from "../../utils";
11
+ import TableRowScopeProvider from "../actions/table-row-scope-provider.vue";
12
+ import TableRowActionsControl from "../table/table-row-actions-control.vue";
13
+ import TableSelectionControl from "../table/table-selection-control.vue";
14
+ import SummaryContent from "./summary-content";
15
+ const props = defineProps({
16
+ rowId: { type: String, required: true },
17
+ original: { type: Object, required: true },
18
+ index: { type: Number, required: true },
19
+ selected: { type: Boolean, required: true },
20
+ selectable: { type: Boolean, required: true },
21
+ actions: { type: Boolean, required: true },
22
+ ui: { type: Object, required: false }
23
+ });
24
+ const emit = defineEmits(["select"]);
25
+ const internals = useTableInternals();
26
+ const summary = computed(() => internals.schema.value.table?.summary);
27
+ const params = computed(() => ({
28
+ context: internals.queryContent.contextData.value,
29
+ index: props.index,
30
+ layout: internals.controls.tableLayout.value,
31
+ pageContext: internals.queryContent.pageContextData.value,
32
+ row: props.original,
33
+ tableApi: internals.tableApi
34
+ }));
35
+ const title = computed(() => String(summary.value?.title(params.value) ?? ""));
36
+ const target = computed(() => summary.value?.to?.(params.value));
37
+ const interactive = computed(() => target.value !== void 0 || Boolean(summary.value?.onSelect));
38
+ const details = computed(() => joinDetails(summary.value?.details?.(params.value) ?? []));
39
+ function joinDetails(values) {
40
+ return values.filter((value) => value !== false && value !== "" && !isNullish(value)).join(TABLE_SUMMARY_DETAILS_SEPARATOR);
41
+ }
42
+ function css(slot, extra = "") {
43
+ return mergeDataListUiClass(
44
+ `${TABLE_SUMMARY_SLOT_CLASSES[slot]} ${extra}`,
45
+ void 0,
46
+ props.ui?.[slot]
47
+ );
48
+ }
49
+ function select() {
50
+ summary.value?.onSelect?.(params.value);
51
+ }
52
+ </script>
53
+
54
+ <template>
55
+ <li
56
+ :class="css('row')"
57
+ :data-row-id="rowId"
58
+ :data-index="index"
59
+ :data-selected="selected"
60
+ :data-interactive="interactive"
61
+ >
62
+ <div v-if="selectable" :class="css('select')">
63
+ <TableSelectionControl
64
+ class="min-h-11 min-w-11 justify-center"
65
+ :model-value="selected"
66
+ :row="original"
67
+ :index="index"
68
+ @toggle="emit('select', $event)"
69
+ />
70
+ </div>
71
+ <ULink
72
+ :as="interactive ? 'button' : 'div'"
73
+ :to="target"
74
+ raw
75
+ :class="
76
+ css(
77
+ 'main',
78
+ `${selectable ? 'ps-1' : 'ps-[var(--nut-dl-summary-x)]'} ${actions ? 'pe-1' : 'pe-[var(--nut-dl-summary-x)]'}`
79
+ )
80
+ "
81
+ @click="select"
82
+ >
83
+ <span v-if="summary?.leading" :class="css('leading')">
84
+ <SummaryContent :render="summary.leading" :params="params" />
85
+ </span>
86
+ <span :class="css('body')">
87
+ <span :class="css('title')" :title="title">{{ title }}</span>
88
+ <span v-if="summary?.status || details" :class="css('meta')">
89
+ <span v-if="summary?.status" :class="css('status')">
90
+ <SummaryContent :render="summary.status" :params="params" />
91
+ </span>
92
+ <span v-if="details" :class="css('details')">{{ details }}</span>
93
+ </span>
94
+ </span>
95
+ </ULink>
96
+ <div v-if="actions" :class="css('actions')">
97
+ <TableRowScopeProvider :scope="params">
98
+ <TableRowActionsControl />
99
+ </TableRowScopeProvider>
100
+ </div>
101
+ </li>
102
+ </template>
@@ -0,0 +1,19 @@
1
+ import type { DataListSummaryUi, GenericObject } from '../../types/index.js';
2
+ type __VLS_Props = {
3
+ rowId: string;
4
+ original: GenericObject;
5
+ index: number;
6
+ selected: boolean;
7
+ /** The row carries a checkbox. */
8
+ selectable: boolean;
9
+ /** The row carries the row-actions button. */
10
+ actions: boolean;
11
+ ui?: DataListSummaryUi;
12
+ };
13
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
+ select: (event: MouseEvent) => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
+ onSelect?: ((event: MouseEvent) => any) | undefined;
17
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
@@ -3,9 +3,10 @@ import UButton from "@nuxt/ui/components/Button.vue";
3
3
  import UIcon from "@nuxt/ui/components/Icon.vue";
4
4
  import { computed } from "vue";
5
5
  import { useUiToolsLocale } from "#ui-tools/i18n";
6
+ import { resolveTextValue } from "../../../shared/utils/render";
6
7
  import { useDataListUi } from "../../composables/use-data-list-ui";
7
8
  import { useTableInternals } from "../../composables/use-table-internals";
8
- import { mergeDataListUiClass } from "../../utils";
9
+ import { mergeDataListProps, mergeDataListUiClass } from "../../utils";
9
10
  const props = defineProps({
10
11
  minHeight: { type: String, required: false },
11
12
  size: { type: String, required: false }
@@ -14,22 +15,42 @@ const { t } = useUiToolsLocale();
14
15
  const internals = useTableInternals();
15
16
  const dataListUi = useDataListUi();
16
17
  const ui = computed(() => dataListUi.ui.value.table?.ui);
17
- const emptyProps = computed(() => dataListUi.ui.value.table?.props?.empty);
18
18
  const filtered = computed(
19
19
  () => internals.filters.hasActiveUiFilters.value || Boolean(internals.filters.searchQuery.value)
20
20
  );
21
- const custom = computed(() => filtered.value ? void 0 : emptyProps.value);
21
+ const custom = computed(() => {
22
+ if (filtered.value) return void 0;
23
+ const own = internals.schema.value.empty;
24
+ const app = dataListUi.ui.value.table?.props?.empty;
25
+ return {
26
+ action: own?.action ?? app?.action,
27
+ description: own?.description ?? app?.description,
28
+ icon: own?.icon ?? app?.icon,
29
+ title: own?.title ?? app?.title
30
+ };
31
+ });
22
32
  const icon = computed(
23
33
  () => custom.value?.icon ?? (filtered.value ? "i-lucide-search-x" : "i-lucide-inbox")
24
34
  );
25
35
  const title = computed(
26
- () => custom.value?.title ?? (filtered.value ? t("table.states.empty.filteredTitle") : t("table.states.empty.title"))
36
+ () => resolveTextValue(
37
+ custom.value?.title,
38
+ filtered.value ? t("table.states.empty.filteredTitle") : t("table.states.empty.title")
39
+ )
27
40
  );
28
41
  const description = computed(() => {
29
- if (custom.value?.description === false) {
30
- return "";
31
- }
32
- return custom.value?.description || (filtered.value ? t("table.states.empty.filteredDescription") : t("table.states.empty.description"));
42
+ if (custom.value?.description === false) return "";
43
+ return resolveTextValue(custom.value?.description) || (filtered.value ? t("table.states.empty.filteredDescription") : t("table.states.empty.description"));
44
+ });
45
+ const action = computed(() => {
46
+ const configured = custom.value?.action;
47
+ if (!configured) return void 0;
48
+ const { label, onClick, ...button } = configured;
49
+ return {
50
+ button: mergeDataListProps({ color: "primary", size: "md" }, button),
51
+ label: resolveTextValue(label),
52
+ onClick
53
+ };
33
54
  });
34
55
  function reset() {
35
56
  internals.filters.clearAllFilters();
@@ -66,18 +87,27 @@ function reset() {
66
87
  {{ description }}
67
88
  </p>
68
89
  <div
69
- v-if="filtered || $slots.actions"
90
+ v-if="filtered || action || $slots.actions"
70
91
  class="nut-dl-empty__actions mt-4 flex items-center gap-2"
71
92
  >
72
93
  <slot name="actions">
73
94
  <UButton
95
+ v-if="filtered"
74
96
  color="neutral"
75
97
  variant="outline"
76
98
  size="sm"
77
99
  icon="i-lucide-rotate-ccw"
78
100
  :label="t('table.states.empty.reset')"
101
+ :ui="{ base: mergeDataListUiClass('nut-dl-empty__action', void 0, ui?.emptyAction) }"
79
102
  @click="reset"
80
103
  />
104
+ <UButton
105
+ v-else-if="action"
106
+ v-bind="action.button"
107
+ :label="action.label"
108
+ :ui="{ base: mergeDataListUiClass('nut-dl-empty__action', void 0, ui?.emptyAction) }"
109
+ @click="action.onClick"
110
+ />
81
111
  </slot>
82
112
  </div>
83
113
  </div>
@@ -934,5 +934,5 @@ defineExpose({ resetColumnSizing });
934
934
  <style>
935
935
  .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(
936
936
  --nut-dl-table-row-selected,color-mix(in srgb,var(--ui-primary) 10%,var(--nut-dl-surface))
937
- );--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-rail:var(--nut-dl-table-rail,var(--ui-border-accented));--nut-dl-rail-lit:var(--nut-dl-table-rail-lit,var(--ui-primary));--nut-dl-rail-radius:var(--nut-dl-table-rail-radius,calc(var(--ui-radius)*1.5));--nut-dl-toggle-hover:var(--nut-dl-table-toggle-hover,var(--ui-bg-accented));--nut-dl-tree-shift:var(--nut-dl-table-tree-shift,6px)}.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);overflow:hidden;overflow-wrap:anywhere;padding-block:var(--nut-dl-cell-y,6px);white-space:normal}.nut-dl-table__table tbody:has(+tfoot)>.nut-dl-row:last-child>.nut-dl-td{border-bottom:0}.nut-dl-td{contain:paint}.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}}
937
+ );--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-rail:var(--nut-dl-table-rail,var(--ui-border-accented));--nut-dl-rail-lit:var(--nut-dl-table-rail-lit,var(--ui-primary));--nut-dl-rail-radius:var(--nut-dl-table-rail-radius,calc(var(--ui-radius)*1.5));--nut-dl-toggle-hover:var(--nut-dl-table-toggle-hover,var(--ui-bg-accented));--nut-dl-tree-shift:var(--nut-dl-table-tree-shift,6px)}.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);overflow:hidden;overflow-wrap:anywhere;padding-block:var(--nut-dl-cell-y,6px);white-space:normal}.nut-dl-table__table tbody:has(+tfoot)>.nut-dl-row:last-child>.nut-dl-td{border-bottom:0}.nut-dl-td{contain:paint}.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-cell-y:0px}.nut-dl-td--selection .nut-dl-td__inner{overflow:visible}.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}}
938
938
  </style>
@@ -5,6 +5,7 @@ import { useDataListUi } from "../../composables/use-data-list-ui";
5
5
  import { mergeDataListProps } from "../../utils";
6
6
  import RowActions from "../actions/row-actions.vue";
7
7
  const dataListUi = useDataListUi();
8
+ const ui = computed(() => dataListUi.ui.value.table?.ui);
8
9
  const buttonProps = computed(
9
10
  () => mergeDataListProps(
10
11
  {
@@ -31,12 +32,15 @@ const buttonProps = computed(
31
32
  itemLeadingIcon: 'size-3.5'
32
33
  }"
33
34
  >
34
- <UButton
35
- v-bind="buttonProps"
36
- square
37
- aria-label="Row actions"
38
- class="nut-dl-rowbtn text-muted hover:text-default focus-visible:text-default"
39
- />
35
+ <template #default="{ label }">
36
+ <UButton
37
+ v-bind="buttonProps"
38
+ square
39
+ :aria-label="label"
40
+ :ui="{ base: ui?.rowAction }"
41
+ class="nut-dl-rowbtn text-muted hover:text-default focus-visible:text-default"
42
+ />
43
+ </template>
40
44
  </RowActions>
41
45
  </div>
42
46
  </template>
@@ -1,6 +1,10 @@
1
+ import type { GenericObject } from '../../types/index.js';
1
2
  type __VLS_Props = {
2
3
  modelValue: boolean | 'indeterminate';
3
- ariaLabel: string;
4
+ /** The record this checkbox selects; without one it selects every row. */
5
+ row?: GenericObject;
6
+ /** Where the record sits in the rendered rows: it names the record when the schema has no `rowLabel`. */
7
+ index?: number;
4
8
  };
5
9
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
10
  toggle: (event: MouseEvent) => any;
@@ -2,13 +2,26 @@
2
2
  import UCheckbox from "@nuxt/ui/components/Checkbox.vue";
3
3
  import { computed } from "vue";
4
4
  import { useDataListUi } from "../../composables/use-data-list-ui";
5
- import { mergeDataListProps } from "../../utils";
5
+ import { useTableRowLabels } from "../../composables/use-table-row-labels";
6
+ import { mergeDataListProps, mergeDataListUiClass } from "../../utils";
6
7
  const props = defineProps({
7
8
  modelValue: { type: [Boolean, String], required: true },
8
- ariaLabel: { type: String, required: true }
9
+ row: { type: Object, required: false },
10
+ index: { type: Number, required: false }
9
11
  });
10
12
  const emit = defineEmits(["toggle"]);
11
13
  const dataListUi = useDataListUi();
14
+ const labels = useTableRowLabels();
15
+ const areaClass = computed(
16
+ () => mergeDataListUiClass(
17
+ "nut-dl-select inline-flex items-center",
18
+ void 0,
19
+ dataListUi.ui.value.table?.ui?.selectControl
20
+ )
21
+ );
22
+ const ariaLabel = computed(
23
+ () => props.row ? labels.selectRow({ index: props.index ?? 0, row: props.row }) : labels.selectAll()
24
+ );
12
25
  const checkboxProps = computed(
13
26
  () => mergeDataListProps(
14
27
  { color: "primary", size: dataListUi.controlSize.value },
@@ -23,11 +36,11 @@ function toggle(event) {
23
36
  </script>
24
37
 
25
38
  <template>
26
- <span class="inline-flex items-center" @click="toggle">
39
+ <span :class="areaClass" @click="toggle">
27
40
  <UCheckbox
28
41
  v-bind="checkboxProps"
29
42
  :model-value="props.modelValue"
30
- :aria-label="props.ariaLabel"
43
+ :aria-label="ariaLabel"
31
44
  class="nut-dl-check"
32
45
  />
33
46
  </span>