@happyvertical/smrt-ui 0.42.2 → 0.42.4
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.
- package/README.md +121 -0
- package/dist/components/data/DataTable.svelte +391 -105
- package/dist/components/data/DataTable.svelte.d.ts.map +1 -1
- package/dist/components/data/DataTableController.d.ts +195 -0
- package/dist/components/data/DataTableController.d.ts.map +1 -0
- package/dist/components/data/DataTableController.js +650 -0
- package/dist/components/data/DataTableIdentity.d.ts +21 -0
- package/dist/components/data/DataTableIdentity.d.ts.map +1 -0
- package/dist/components/data/DataTableIdentity.js +28 -0
- package/dist/components/data/__tests__/DataTable.test.js +257 -19
- package/dist/components/data/__tests__/DataTableController.test.js +214 -0
- package/dist/components/data/__tests__/DataTableIdentity.test.js +29 -0
- package/dist/components/data/index.d.ts +2 -0
- package/dist/components/data/index.d.ts.map +1 -1
- package/dist/components/data/index.js +2 -0
- package/dist/components/data/types.d.ts +27 -1
- package/dist/components/data/types.d.ts.map +1 -1
- package/dist/i18n/strings.d.ts +1 -0
- package/dist/i18n/strings.d.ts.map +1 -1
- package/dist/i18n/strings.js +1 -0
- package/dist/svelte/playground/DataTablePreview.svelte +34 -13
- package/dist/svelte/playground/DataTablePreview.svelte.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -16,12 +16,20 @@ import { M } from '../../i18n/strings.js';
|
|
|
16
16
|
import Trans from '../../i18n/Trans.svelte';
|
|
17
17
|
import { useI18n } from '../../i18n/use-i18n.js';
|
|
18
18
|
import Pagination from '../ui/Pagination.svelte';
|
|
19
|
-
import
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
import {
|
|
20
|
+
compareDataTableRowIds,
|
|
21
|
+
createDataTableController,
|
|
22
|
+
type DataTableCommand,
|
|
23
|
+
type DataTableController,
|
|
24
|
+
type DataTableFilter,
|
|
25
|
+
type DataTableModes,
|
|
26
|
+
type DataTableRowId,
|
|
27
|
+
type DataTableSnapshot,
|
|
28
|
+
type DataTableViewState,
|
|
29
|
+
type DataTableViewStateInput,
|
|
30
|
+
} from './DataTableController.js';
|
|
31
|
+
import { resolveDataTableRows } from './DataTableIdentity.js';
|
|
32
|
+
import type { DataTableColumn, DataTableProps, SortState } from './types.js';
|
|
25
33
|
import { defaultSort, getNestedValue } from './types.js';
|
|
26
34
|
|
|
27
35
|
const { t } = useI18n();
|
|
@@ -37,6 +45,7 @@ let {
|
|
|
37
45
|
data = [],
|
|
38
46
|
columns = [],
|
|
39
47
|
rowKey,
|
|
48
|
+
agentAddressable = false,
|
|
40
49
|
selectable = false,
|
|
41
50
|
selected = $bindable(new Set<string | number>()),
|
|
42
51
|
onSelectionChange,
|
|
@@ -68,85 +77,227 @@ let {
|
|
|
68
77
|
caption,
|
|
69
78
|
dense = false,
|
|
70
79
|
cell,
|
|
80
|
+
controller,
|
|
81
|
+
state: controlledState,
|
|
82
|
+
initialState,
|
|
83
|
+
onStateChange,
|
|
84
|
+
modes,
|
|
71
85
|
}: ExtendedProps<T> = $props();
|
|
72
86
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
function legacyModes(): DataTableModes {
|
|
88
|
+
return {
|
|
89
|
+
filtering: modes?.filtering ?? 'local',
|
|
90
|
+
sorting: modes?.sorting ?? (manualSorting ? 'manual' : 'local'),
|
|
91
|
+
pagination: modes?.pagination ?? (manualPagination ? 'manual' : 'local'),
|
|
92
|
+
};
|
|
78
93
|
}
|
|
79
94
|
|
|
80
|
-
function
|
|
81
|
-
return
|
|
95
|
+
function legacyViewState(): Partial<DataTableViewState> {
|
|
96
|
+
return {
|
|
97
|
+
sorting:
|
|
98
|
+
sort.columnId && sort.direction
|
|
99
|
+
? [{ columnId: sort.columnId, direction: sort.direction }]
|
|
100
|
+
: [],
|
|
101
|
+
page,
|
|
102
|
+
pageSize: pageSize ?? null,
|
|
103
|
+
columnOrder: columns.map((column) => column.id),
|
|
104
|
+
columnVisibility: columns.map((column) => ({
|
|
105
|
+
columnId: column.id,
|
|
106
|
+
visible: !visibleColumnIds || visibleColumnIds.has(column.id),
|
|
107
|
+
})),
|
|
108
|
+
selectedRowIds: [...selected],
|
|
109
|
+
expandedRowIds: [...expanded],
|
|
110
|
+
};
|
|
82
111
|
}
|
|
83
112
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
113
|
+
function mergedLegacyState(): Omit<DataTableViewState, 'selection'> {
|
|
114
|
+
const { selection: _selection, ...current } = localController.getState();
|
|
115
|
+
return { ...current, ...legacyViewState() };
|
|
116
|
+
}
|
|
87
117
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
? sort.direction === 'asc'
|
|
93
|
-
? 'desc'
|
|
94
|
-
: sort.direction === 'desc'
|
|
95
|
-
? null
|
|
96
|
-
: 'asc'
|
|
97
|
-
: 'asc',
|
|
98
|
-
};
|
|
118
|
+
const localController = createDataTableController({
|
|
119
|
+
modes: legacyModes(),
|
|
120
|
+
onStateChange: (next, command) => onStateChange?.(next, command),
|
|
121
|
+
});
|
|
99
122
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
let controllerSnapshot = $state<DataTableSnapshot>(localController.snapshot());
|
|
124
|
+
let publishedLegacySignature: string | undefined;
|
|
125
|
+
let localControllerInitialized = false;
|
|
103
126
|
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
function activeController(): DataTableController {
|
|
128
|
+
return controller ?? localController;
|
|
106
129
|
}
|
|
107
130
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
131
|
+
function legacySignature(): string {
|
|
132
|
+
const legacy = legacyViewState();
|
|
133
|
+
return JSON.stringify({
|
|
134
|
+
sorting: legacy.sorting,
|
|
135
|
+
page: legacy.page,
|
|
136
|
+
pageSize: legacy.pageSize,
|
|
137
|
+
columnVisibility: legacy.columnVisibility,
|
|
138
|
+
selectedRowIds: [...(legacy.selectedRowIds ?? [])].sort(),
|
|
139
|
+
expandedRowIds: [...(legacy.expandedRowIds ?? [])].sort(),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
112
142
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
143
|
+
function legacySort(next: DataTableViewState): SortState {
|
|
144
|
+
const rule = next.sorting[0];
|
|
145
|
+
return rule
|
|
146
|
+
? { columnId: rule.columnId, direction: rule.direction }
|
|
147
|
+
: { columnId: null, direction: null };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function publishLegacyState(
|
|
151
|
+
next: DataTableViewState,
|
|
152
|
+
command: DataTableCommand,
|
|
153
|
+
previous: DataTableViewState,
|
|
154
|
+
) {
|
|
155
|
+
const nextSort = legacySort(next);
|
|
156
|
+
const previousSort = legacySort(previous);
|
|
157
|
+
sort = nextSort;
|
|
158
|
+
page = next.page;
|
|
159
|
+
selected = new Set(next.selectedRowIds);
|
|
160
|
+
expanded = new Set(next.expandedRowIds);
|
|
161
|
+
publishedLegacySignature = legacySignature();
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
nextSort.columnId !== previousSort.columnId ||
|
|
165
|
+
nextSort.direction !== previousSort.direction
|
|
166
|
+
) {
|
|
167
|
+
onSortChange?.(nextSort);
|
|
168
|
+
}
|
|
169
|
+
if (
|
|
170
|
+
JSON.stringify(next.selectedRowIds) !==
|
|
171
|
+
JSON.stringify(previous.selectedRowIds)
|
|
172
|
+
) {
|
|
173
|
+
onSelectionChange?.(new Set(next.selectedRowIds));
|
|
174
|
+
}
|
|
175
|
+
if (
|
|
176
|
+
JSON.stringify(next.expandedRowIds) !==
|
|
177
|
+
JSON.stringify(previous.expandedRowIds)
|
|
178
|
+
) {
|
|
179
|
+
onExpandedChange?.(new Set(next.expandedRowIds));
|
|
180
|
+
}
|
|
181
|
+
if (next.page !== previous.page) onPageChange?.(next.page);
|
|
182
|
+
void command;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
$effect(() => {
|
|
186
|
+
if (controller) return;
|
|
187
|
+
|
|
188
|
+
localController.setControlled(controlledState !== undefined);
|
|
189
|
+
localController.setModes(legacyModes());
|
|
190
|
+
localController.setColumnIds(columns.map((column) => column.id));
|
|
191
|
+
|
|
192
|
+
if (!localControllerInitialized) {
|
|
193
|
+
localControllerInitialized = true;
|
|
194
|
+
const nextState: DataTableViewStateInput = controlledState ?? {
|
|
195
|
+
...mergedLegacyState(),
|
|
196
|
+
...initialState,
|
|
197
|
+
};
|
|
198
|
+
localController.replaceState(nextState);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (controlledState !== undefined) {
|
|
203
|
+
localController.replaceState(controlledState);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const signature = legacySignature();
|
|
208
|
+
if (signature === publishedLegacySignature) {
|
|
209
|
+
publishedLegacySignature = undefined;
|
|
210
|
+
return;
|
|
117
211
|
}
|
|
212
|
+
localController.replaceState(mergedLegacyState());
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
$effect(() => {
|
|
216
|
+
const current = activeController();
|
|
217
|
+
current.setColumnIds(columns.map((column) => column.id));
|
|
218
|
+
controllerSnapshot = current.snapshot();
|
|
219
|
+
return current.subscribe((transition) => {
|
|
220
|
+
controllerSnapshot = transition.next;
|
|
221
|
+
if (
|
|
222
|
+
current === localController &&
|
|
223
|
+
controlledState === undefined &&
|
|
224
|
+
transition.command
|
|
225
|
+
) {
|
|
226
|
+
publishLegacyState(
|
|
227
|
+
transition.next.state,
|
|
228
|
+
transition.command,
|
|
229
|
+
transition.previous.state,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
});
|
|
118
234
|
|
|
119
|
-
|
|
120
|
-
|
|
235
|
+
const tableState = $derived(controllerSnapshot.state);
|
|
236
|
+
const tableModes = $derived(controllerSnapshot.modes);
|
|
237
|
+
const requiresStableRowIdentity = $derived(
|
|
238
|
+
selectable ||
|
|
239
|
+
Boolean(expandedContent) ||
|
|
240
|
+
agentAddressable ||
|
|
241
|
+
tableModes.filtering === 'manual' ||
|
|
242
|
+
tableModes.sorting === 'manual' ||
|
|
243
|
+
tableModes.pagination === 'manual',
|
|
244
|
+
);
|
|
245
|
+
const sourceRows = $derived.by(() =>
|
|
246
|
+
resolveDataTableRows(data, rowKey, {
|
|
247
|
+
requireStableIdentity: requiresStableRowIdentity,
|
|
248
|
+
}),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
function dispatch(command: DataTableCommand) {
|
|
252
|
+
activeController().dispatch(command);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function handleSort(column: DataTableColumn<T>, event: MouseEvent) {
|
|
256
|
+
if (!sortable || !column.sortable) return;
|
|
257
|
+
dispatch({
|
|
258
|
+
type: 'toggleSorting',
|
|
259
|
+
columnId: column.id,
|
|
260
|
+
multi: event.shiftKey,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function handleRowSelect(key: DataTableRowId, event: Event) {
|
|
265
|
+
event.stopPropagation();
|
|
266
|
+
dispatch({ type: 'toggleRowSelection', rowId: key });
|
|
121
267
|
}
|
|
122
268
|
|
|
123
|
-
// Handle select all
|
|
124
269
|
function handleSelectAll() {
|
|
270
|
+
if (tableState.selection.scope === 'allMatching') {
|
|
271
|
+
dispatch({
|
|
272
|
+
type: 'setSelection',
|
|
273
|
+
selection: { scope: 'explicit', rowIds: [] },
|
|
274
|
+
});
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const selectedIds = new Set(tableState.selectedRowIds);
|
|
278
|
+
const visibleIds = displayRows.map(({ rowId }) => rowId);
|
|
279
|
+
if (tableState.selection.scope === 'page') {
|
|
280
|
+
dispatch({
|
|
281
|
+
type: 'setPageSelection',
|
|
282
|
+
rowIds: allSelected ? [] : visibleIds,
|
|
283
|
+
});
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
125
286
|
if (allSelected) {
|
|
126
|
-
const
|
|
127
|
-
displayData.map((row, i) => getDisplayRowKey(row, i)),
|
|
128
|
-
);
|
|
129
|
-
selected = new Set([...selected].filter((key) => !visibleKeys.has(key)));
|
|
287
|
+
for (const key of visibleIds) selectedIds.delete(key);
|
|
130
288
|
} else {
|
|
131
|
-
|
|
132
|
-
...selected,
|
|
133
|
-
...displayData.map((row, i) => getDisplayRowKey(row, i)),
|
|
134
|
-
]);
|
|
289
|
+
for (const key of visibleIds) selectedIds.add(key);
|
|
135
290
|
}
|
|
136
|
-
|
|
291
|
+
dispatch({ type: 'setSelectedRows', rowIds: [...selectedIds] });
|
|
137
292
|
}
|
|
138
293
|
|
|
139
|
-
function handleExpanded(key:
|
|
294
|
+
function handleExpanded(key: DataTableRowId, event: Event) {
|
|
140
295
|
event.stopPropagation();
|
|
141
|
-
|
|
142
|
-
next.has(key) ? next.delete(key) : next.add(key);
|
|
143
|
-
expanded = next;
|
|
144
|
-
onExpandedChange?.(next);
|
|
296
|
+
dispatch({ type: 'toggleRowExpansion', rowId: key });
|
|
145
297
|
}
|
|
146
298
|
|
|
147
299
|
function handlePageChange(next: number) {
|
|
148
|
-
|
|
149
|
-
onPageChange?.(page);
|
|
300
|
+
dispatch({ type: 'setPage', page: next });
|
|
150
301
|
}
|
|
151
302
|
|
|
152
303
|
// Handle row click
|
|
@@ -164,55 +315,190 @@ function setIndeterminate(node: HTMLInputElement, value: boolean) {
|
|
|
164
315
|
};
|
|
165
316
|
}
|
|
166
317
|
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
)
|
|
318
|
+
const visibleColumns = $derived.by(() => {
|
|
319
|
+
const configuredOrder = new Map(
|
|
320
|
+
tableState.columnOrder.map((id, index) => [id, index]),
|
|
321
|
+
);
|
|
322
|
+
const visibility = new Map(
|
|
323
|
+
tableState.columnVisibility.map((entry) => [entry.columnId, entry.visible]),
|
|
324
|
+
);
|
|
325
|
+
return columns
|
|
326
|
+
.filter((column) => !column.hidden && visibility.get(column.id) !== false)
|
|
327
|
+
.slice()
|
|
328
|
+
.sort((left, right) => {
|
|
329
|
+
const leftOrder = configuredOrder.get(left.id) ?? Number.MAX_SAFE_INTEGER;
|
|
330
|
+
const rightOrder =
|
|
331
|
+
configuredOrder.get(right.id) ?? Number.MAX_SAFE_INTEGER;
|
|
332
|
+
return leftOrder - rightOrder;
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
function sameFilterValue(value: unknown, expected: unknown): boolean {
|
|
337
|
+
return Object.is(value, expected);
|
|
338
|
+
}
|
|
173
339
|
|
|
174
|
-
|
|
340
|
+
function textValue(value: unknown): string {
|
|
341
|
+
return String(value ?? '').toLowerCase();
|
|
342
|
+
}
|
|
175
343
|
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
344
|
+
function compareFilterValues(left: unknown, right: unknown): number {
|
|
345
|
+
const leftValue = left instanceof Date ? left.getTime() : left;
|
|
346
|
+
const rightValue = right instanceof Date ? right.getTime() : right;
|
|
347
|
+
if (typeof leftValue === 'number' && typeof rightValue === 'number')
|
|
348
|
+
return leftValue - rightValue;
|
|
349
|
+
const a = String(leftValue ?? '');
|
|
350
|
+
const b = String(rightValue ?? '');
|
|
351
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
352
|
+
}
|
|
179
353
|
|
|
180
|
-
|
|
181
|
-
|
|
354
|
+
function matchesFilter(
|
|
355
|
+
row: T,
|
|
356
|
+
column: DataTableColumn<T>,
|
|
357
|
+
filter: DataTableFilter,
|
|
358
|
+
): boolean {
|
|
359
|
+
if (column.filterable === false) return true;
|
|
360
|
+
const value = getCellValue(row, column);
|
|
361
|
+
if (column.filterFn) return column.filterFn(row, value, filter);
|
|
362
|
+
const expected = filter.value;
|
|
363
|
+
const valueText = textValue(value);
|
|
364
|
+
const expectedText = textValue(expected);
|
|
365
|
+
switch (filter.operator) {
|
|
366
|
+
case 'equals':
|
|
367
|
+
return sameFilterValue(value, expected);
|
|
368
|
+
case 'notEquals':
|
|
369
|
+
return !sameFilterValue(value, expected);
|
|
370
|
+
case 'contains':
|
|
371
|
+
return valueText.includes(expectedText);
|
|
372
|
+
case 'notContains':
|
|
373
|
+
return !valueText.includes(expectedText);
|
|
374
|
+
case 'startsWith':
|
|
375
|
+
return valueText.startsWith(expectedText);
|
|
376
|
+
case 'endsWith':
|
|
377
|
+
return valueText.endsWith(expectedText);
|
|
378
|
+
case 'in':
|
|
379
|
+
return (
|
|
380
|
+
Array.isArray(expected) &&
|
|
381
|
+
expected.some((entry) => sameFilterValue(value, entry))
|
|
382
|
+
);
|
|
383
|
+
case 'notIn':
|
|
384
|
+
return (
|
|
385
|
+
Array.isArray(expected) &&
|
|
386
|
+
!expected.some((entry) => sameFilterValue(value, entry))
|
|
387
|
+
);
|
|
388
|
+
case 'gt':
|
|
389
|
+
return compareFilterValues(value, expected) > 0;
|
|
390
|
+
case 'gte':
|
|
391
|
+
return compareFilterValues(value, expected) >= 0;
|
|
392
|
+
case 'lt':
|
|
393
|
+
return compareFilterValues(value, expected) < 0;
|
|
394
|
+
case 'lte':
|
|
395
|
+
return compareFilterValues(value, expected) <= 0;
|
|
396
|
+
case 'isNull':
|
|
397
|
+
return value == null;
|
|
398
|
+
case 'isNotNull':
|
|
399
|
+
return value != null;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
182
402
|
|
|
183
|
-
|
|
184
|
-
|
|
403
|
+
const filteredRows = $derived.by(() => {
|
|
404
|
+
if (tableModes.filtering === 'manual') return sourceRows;
|
|
405
|
+
const search = tableState.search.toLowerCase();
|
|
406
|
+
return sourceRows.filter(({ row, sourceIndex }) => {
|
|
407
|
+
if (filterFn && !filterFn(row, sourceIndex)) return false;
|
|
408
|
+
if (
|
|
409
|
+
search &&
|
|
410
|
+
!columns.some(
|
|
411
|
+
(column) =>
|
|
412
|
+
column.searchable !== false &&
|
|
413
|
+
textValue(getCellValue(row, column)).includes(search),
|
|
414
|
+
)
|
|
415
|
+
) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
return tableState.filters.every((filter) => {
|
|
419
|
+
const column = columns.find(
|
|
420
|
+
(candidate) => candidate.id === filter.columnId,
|
|
421
|
+
);
|
|
422
|
+
return Boolean(column && matchesFilter(row, column, filter));
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
});
|
|
185
426
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
427
|
+
const sortedRows = $derived.by(() => {
|
|
428
|
+
if (tableModes.sorting === 'manual' || tableState.sorting.length === 0)
|
|
429
|
+
return filteredRows;
|
|
430
|
+
return filteredRows.slice().sort((left, right) => {
|
|
431
|
+
for (const rule of tableState.sorting) {
|
|
432
|
+
const column = columns.find(
|
|
433
|
+
(candidate) => candidate.id === rule.columnId,
|
|
434
|
+
);
|
|
435
|
+
if (!column) continue;
|
|
436
|
+
const result = column.sortFn
|
|
437
|
+
? column.sortFn(left.row, right.row, rule.direction)
|
|
438
|
+
: defaultSort(
|
|
439
|
+
left.row,
|
|
440
|
+
right.row,
|
|
441
|
+
String(column.accessor ?? column.id),
|
|
442
|
+
rule.direction,
|
|
443
|
+
);
|
|
444
|
+
if (result !== 0) return result;
|
|
189
445
|
}
|
|
190
|
-
return
|
|
446
|
+
return rowKey
|
|
447
|
+
? compareDataTableRowIds(left.rowId, right.rowId)
|
|
448
|
+
: left.sourceIndex - right.sourceIndex;
|
|
191
449
|
});
|
|
192
450
|
});
|
|
193
451
|
|
|
194
|
-
const totalRowCount = $derived(
|
|
452
|
+
const totalRowCount = $derived(
|
|
453
|
+
tableModes.pagination === 'manual' ? totalRows : sortedRows.length,
|
|
454
|
+
);
|
|
195
455
|
const totalPages = $derived(
|
|
196
|
-
|
|
456
|
+
tableState.pageSize
|
|
457
|
+
? totalRowCount === undefined
|
|
458
|
+
? null
|
|
459
|
+
: Math.max(1, Math.ceil(totalRowCount / tableState.pageSize))
|
|
460
|
+
: 1,
|
|
197
461
|
);
|
|
198
|
-
const
|
|
199
|
-
if (!pageSize ||
|
|
200
|
-
|
|
201
|
-
|
|
462
|
+
const displayRows = $derived.by(() => {
|
|
463
|
+
if (!tableState.pageSize || tableModes.pagination === 'manual')
|
|
464
|
+
return sortedRows;
|
|
465
|
+
const start = (tableState.page - 1) * tableState.pageSize;
|
|
466
|
+
return sortedRows.slice(start, start + tableState.pageSize);
|
|
202
467
|
});
|
|
203
|
-
const displayIndexOffset = $derived(pageSize ? (page - 1) * pageSize : 0);
|
|
204
468
|
|
|
205
469
|
$effect(() => {
|
|
206
|
-
|
|
470
|
+
const total = totalRows;
|
|
471
|
+
if (total === undefined) return;
|
|
472
|
+
if (tableModes.pagination !== 'manual') {
|
|
473
|
+
throw new TypeError(
|
|
474
|
+
'DataTable totalRows is only valid when pagination mode is manual',
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
if (!Number.isFinite(total) || !Number.isInteger(total) || total < 0) {
|
|
478
|
+
throw new TypeError(
|
|
479
|
+
'DataTable totalRows must be a non-negative integer when supplied',
|
|
480
|
+
);
|
|
481
|
+
}
|
|
207
482
|
});
|
|
208
483
|
|
|
209
|
-
|
|
484
|
+
$effect(() => {
|
|
485
|
+
void tableState.page;
|
|
486
|
+
void tableState.pageSize;
|
|
487
|
+
activeController().clampPage(totalRowCount);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
const selectedIds = $derived(new Set(tableState.selectedRowIds));
|
|
491
|
+
const expandedIds = $derived(new Set(tableState.expandedRowIds));
|
|
492
|
+
const currentSort = $derived(legacySort(tableState));
|
|
493
|
+
|
|
210
494
|
const allSelected = $derived(
|
|
211
|
-
|
|
212
|
-
|
|
495
|
+
tableState.selection.scope === 'allMatching' ||
|
|
496
|
+
(displayRows.length > 0 &&
|
|
497
|
+
displayRows.every(({ rowId }) => selectedIds.has(rowId))),
|
|
213
498
|
);
|
|
214
499
|
const someSelected = $derived(
|
|
215
|
-
|
|
500
|
+
tableState.selection.scope !== 'allMatching' &&
|
|
501
|
+
displayRows.some(({ rowId }) => selectedIds.has(rowId)) &&
|
|
216
502
|
!allSelected,
|
|
217
503
|
);
|
|
218
504
|
|
|
@@ -220,13 +506,11 @@ const columnCount = $derived(
|
|
|
220
506
|
visibleColumns.length + (selectable ? 1 : 0) + (expandedContent ? 1 : 0),
|
|
221
507
|
);
|
|
222
508
|
|
|
223
|
-
// Get cell value
|
|
224
509
|
function getCellValue(row: T, column: DataTableColumn<T>): unknown {
|
|
225
510
|
const accessor = column.accessor ?? column.id;
|
|
226
511
|
return getNestedValue(row, String(accessor));
|
|
227
512
|
}
|
|
228
513
|
|
|
229
|
-
// Size classes
|
|
230
514
|
const sizeClasses = {
|
|
231
515
|
sm: 'data-table--sm',
|
|
232
516
|
md: 'data-table--md',
|
|
@@ -257,7 +541,7 @@ const sizeClasses = {
|
|
|
257
541
|
checked={allSelected}
|
|
258
542
|
use:setIndeterminate={someSelected}
|
|
259
543
|
onchange={handleSelectAll}
|
|
260
|
-
aria-label={t(M['ui.data_table.
|
|
544
|
+
aria-label={t(M['ui.data_table.select_current_page'])}
|
|
261
545
|
class="data-table__checkbox"
|
|
262
546
|
/>
|
|
263
547
|
</th>
|
|
@@ -267,14 +551,14 @@ const sizeClasses = {
|
|
|
267
551
|
<th
|
|
268
552
|
class="data-table__cell data-table__cell--header"
|
|
269
553
|
class:data-table__cell--sortable={sortable && column.sortable}
|
|
270
|
-
class:data-table__cell--sorted={
|
|
554
|
+
class:data-table__cell--sorted={currentSort.columnId === column.id}
|
|
271
555
|
style:width={column.width}
|
|
272
556
|
style:min-width={column.minWidth}
|
|
273
557
|
style:max-width={column.maxWidth}
|
|
274
558
|
style:text-align={column.align}
|
|
275
559
|
scope="col"
|
|
276
|
-
aria-sort={
|
|
277
|
-
?
|
|
560
|
+
aria-sort={currentSort.columnId === column.id
|
|
561
|
+
? currentSort.direction === 'asc'
|
|
278
562
|
? 'ascending'
|
|
279
563
|
: 'descending'
|
|
280
564
|
: undefined}
|
|
@@ -285,12 +569,12 @@ const sizeClasses = {
|
|
|
285
569
|
<button
|
|
286
570
|
type="button"
|
|
287
571
|
class="data-table__sort-button"
|
|
288
|
-
onclick={() => handleSort(column)}
|
|
572
|
+
onclick={(event) => handleSort(column, event)}
|
|
289
573
|
>
|
|
290
574
|
<span>{column.label}</span>
|
|
291
575
|
<span class="data-table__sort-icon" aria-hidden="true">
|
|
292
|
-
{#if
|
|
293
|
-
{
|
|
576
|
+
{#if currentSort.columnId === column.id}
|
|
577
|
+
{currentSort.direction === 'asc' ? '↑' : '↓'}
|
|
294
578
|
{:else}
|
|
295
579
|
↕
|
|
296
580
|
{/if}
|
|
@@ -317,7 +601,7 @@ const sizeClasses = {
|
|
|
317
601
|
</div>
|
|
318
602
|
</td>
|
|
319
603
|
</tr>
|
|
320
|
-
{:else if
|
|
604
|
+
{:else if displayRows.length === 0}
|
|
321
605
|
<tr class="data-table__row data-table__row--empty">
|
|
322
606
|
<td
|
|
323
607
|
class="data-table__cell data-table__cell--empty"
|
|
@@ -333,10 +617,11 @@ const sizeClasses = {
|
|
|
333
617
|
</td>
|
|
334
618
|
</tr>
|
|
335
619
|
{:else}
|
|
336
|
-
{#each
|
|
337
|
-
{@const
|
|
338
|
-
{@const
|
|
339
|
-
{@const
|
|
620
|
+
{#each displayRows as entry, index (entry.rowId)}
|
|
621
|
+
{@const row = entry.row}
|
|
622
|
+
{@const key = entry.rowId}
|
|
623
|
+
{@const isSelected = tableState.selection.scope === 'allMatching' || selectedIds.has(key)}
|
|
624
|
+
{@const isExpanded = expandedIds.has(key)}
|
|
340
625
|
{@const rowCanExpand = canExpand?.(row, index) ?? true}
|
|
341
626
|
<tr
|
|
342
627
|
class="data-table__row {rowClass?.(row, index) ?? ''}"
|
|
@@ -361,6 +646,7 @@ const sizeClasses = {
|
|
|
361
646
|
<input
|
|
362
647
|
type="checkbox"
|
|
363
648
|
checked={isSelected}
|
|
649
|
+
disabled={tableState.selection.scope === 'allMatching'}
|
|
364
650
|
onchange={(e) => handleRowSelect(key, e)}
|
|
365
651
|
aria-label={t(M['ui.data_table.select_row'])}
|
|
366
652
|
class="data-table__checkbox"
|
|
@@ -393,11 +679,11 @@ const sizeClasses = {
|
|
|
393
679
|
{/if}
|
|
394
680
|
</tbody>
|
|
395
681
|
{#if footer}
|
|
396
|
-
<tfoot><tr><td class="data-table__cell data-table__footer" colspan={columnCount}>{@render footer({ rows:
|
|
682
|
+
<tfoot><tr><td class="data-table__cell data-table__footer" colspan={columnCount}>{@render footer({ rows: displayRows.map(({ row }) => row) })}</td></tr></tfoot>
|
|
397
683
|
{/if}
|
|
398
684
|
</table>
|
|
399
685
|
</div>
|
|
400
|
-
{#if pageSize && totalPages > 1}<Pagination currentPage={page} {totalPages} onPageChange={handlePageChange} aria-label="Table pages" />{/if}
|
|
686
|
+
{#if tableState.pageSize && totalPages && totalPages > 1}<Pagination currentPage={tableState.page} {totalPages} onPageChange={handlePageChange} aria-label="Table pages" />{/if}
|
|
401
687
|
|
|
402
688
|
<style>
|
|
403
689
|
.data-table-container {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/data/DataTable.svelte.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"DataTable.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/data/DataTable.svelte.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAkBtC,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAa,MAAM,YAAY,CAAC;AAI7E,UAAU,aAAa,CAAC,CAAC,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IAClD,+DAA+D;IAC/D,IAAI,CAAC,EAAE,OAAO,CACZ;QAAC;YAAE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;YAAC,GAAG,EAAE,CAAC,CAAC;YAAC,KAAK,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE;KAAC,CACxE,CAAC;CACH;AAAC,iBAAS,QAAQ,CAAC,CAAC;WAomBQ,aAAa,CAAC,CAAC,CAAC;;;;;EAAgH;AAC7J,cAAM,iBAAiB,CAAC,CAAC;IACrB,KAAK,IAAI,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAChD,MAAM,IAAI,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAClD,KAAK,IAAI,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAChD,QAAQ;IACR,OAAO;CACV;AAED,UAAU,qBAAqB;IAC3B,KAAK,CAAC,EAAE,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAA;KAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5X,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3H,YAAY,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;CACjE;AACD,QAAA,MAAM,SAAS,EAAE,qBAAmC,CAAC;AACnC,KAAK,SAAS,CAAC,CAAC,IAAI,YAAY,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,eAAe,SAAS,CAAC"}
|