@urbicon-ui/table 6.1.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 +153 -0
- package/dist/cells/ActionButtons.svelte +224 -0
- package/dist/cells/ActionButtons.svelte.d.ts +74 -0
- package/dist/cells/CopyButton.svelte +89 -0
- package/dist/cells/CopyButton.svelte.d.ts +33 -0
- package/dist/cells/CustomCell.svelte +136 -0
- package/dist/cells/CustomCell.svelte.d.ts +44 -0
- package/dist/cells/DateCell.svelte +194 -0
- package/dist/cells/DateCell.svelte.d.ts +39 -0
- package/dist/cells/LinkCell.svelte +240 -0
- package/dist/cells/LinkCell.svelte.d.ts +42 -0
- package/dist/cells/NumberCell.svelte +225 -0
- package/dist/cells/NumberCell.svelte.d.ts +47 -0
- package/dist/cells/StatusBadge.svelte +121 -0
- package/dist/cells/StatusBadge.svelte.d.ts +44 -0
- package/dist/cells/UserAvatar.svelte +71 -0
- package/dist/cells/UserAvatar.svelte.d.ts +37 -0
- package/dist/cells/index.d.ts +8 -0
- package/dist/cells/index.js +9 -0
- package/dist/core/EmptyState.svelte +161 -0
- package/dist/core/EmptyState.svelte.d.ts +16 -0
- package/dist/core/ErrorState.svelte +158 -0
- package/dist/core/ErrorState.svelte.d.ts +15 -0
- package/dist/core/GroupedRow.svelte +239 -0
- package/dist/core/GroupedRow.svelte.d.ts +18 -0
- package/dist/core/LoadingState.svelte +75 -0
- package/dist/core/LoadingState.svelte.d.ts +14 -0
- package/dist/core/MobileCard.svelte +151 -0
- package/dist/core/MobileCard.svelte.d.ts +15 -0
- package/dist/core/TableCell.svelte +105 -0
- package/dist/core/TableCell.svelte.d.ts +14 -0
- package/dist/core/TableDesktop.svelte +480 -0
- package/dist/core/TableDesktop.svelte.d.ts +26 -0
- package/dist/core/TableHead.svelte +314 -0
- package/dist/core/TableHead.svelte.d.ts +7 -0
- package/dist/core/TableMobile.svelte +112 -0
- package/dist/core/TableMobile.svelte.d.ts +13 -0
- package/dist/core/TableProvider.svelte +271 -0
- package/dist/core/TableProvider.svelte.d.ts +40 -0
- package/dist/core/TableRow.svelte +171 -0
- package/dist/core/TableRow.svelte.d.ts +16 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.js +14 -0
- package/dist/core/sticky-context.svelte.d.ts +48 -0
- package/dist/core/sticky-context.svelte.js +88 -0
- package/dist/core/table/Table.svelte +304 -0
- package/dist/core/table/Table.svelte.d.ts +26 -0
- package/dist/core/table/index.d.ts +448 -0
- package/dist/core/table/index.js +1 -0
- package/dist/core/table-style-context.d.ts +66 -0
- package/dist/core/table-style-context.js +26 -0
- package/dist/factories/ColumnValidation.d.ts +49 -0
- package/dist/factories/ColumnValidation.js +188 -0
- package/dist/factories/TableColumns.d.ts +97 -0
- package/dist/factories/TableColumns.js +262 -0
- package/dist/factories/TypedColumnBuilder.d.ts +41 -0
- package/dist/factories/TypedColumnBuilder.js +72 -0
- package/dist/factories/index.d.ts +12 -0
- package/dist/factories/index.js +13 -0
- package/dist/features/HeaderMenu.svelte +236 -0
- package/dist/features/HeaderMenu.svelte.d.ts +8 -0
- package/dist/features/LiveUpdateBanner.svelte +66 -0
- package/dist/features/LiveUpdateBanner.svelte.d.ts +6 -0
- package/dist/features/SearchHighlight.svelte +21 -0
- package/dist/features/SearchHighlight.svelte.d.ts +8 -0
- package/dist/features/SmartFilterBar/ChipsField.svelte +104 -0
- package/dist/features/SmartFilterBar/ChipsField.svelte.d.ts +5 -0
- package/dist/features/SmartFilterBar/ColumnVisibilityMenu.svelte +84 -0
- package/dist/features/SmartFilterBar/ColumnVisibilityMenu.svelte.d.ts +3 -0
- package/dist/features/SmartFilterBar/FilterMenu.svelte +367 -0
- package/dist/features/SmartFilterBar/FilterMenu.svelte.d.ts +3 -0
- package/dist/features/SmartFilterBar/GroupingMenu.svelte +82 -0
- package/dist/features/SmartFilterBar/GroupingMenu.svelte.d.ts +3 -0
- package/dist/features/SmartFilterBar/SmartFilterBar.svelte +109 -0
- package/dist/features/SmartFilterBar/SmartFilterBar.svelte.d.ts +11 -0
- package/dist/features/SmartFilterBar/SummaryMenu.svelte +118 -0
- package/dist/features/SmartFilterBar/SummaryMenu.svelte.d.ts +3 -0
- package/dist/features/SummaryRow.svelte +97 -0
- package/dist/features/SummaryRow.svelte.d.ts +8 -0
- package/dist/features/index.d.ts +4 -0
- package/dist/features/index.js +4 -0
- package/dist/i18n/index.d.ts +366 -0
- package/dist/i18n/index.js +21 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +41 -0
- package/dist/stores/TableStore.svelte.d.ts +192 -0
- package/dist/stores/TableStore.svelte.js +362 -0
- package/dist/stores/concerns/index.d.ts +15 -0
- package/dist/stores/concerns/index.js +14 -0
- package/dist/stores/concerns/types.d.ts +31 -0
- package/dist/stores/concerns/types.js +1 -0
- package/dist/stores/concerns/useColumnOrder.svelte.d.ts +16 -0
- package/dist/stores/concerns/useColumnOrder.svelte.js +81 -0
- package/dist/stores/concerns/useColumnVisibility.svelte.d.ts +16 -0
- package/dist/stores/concerns/useColumnVisibility.svelte.js +58 -0
- package/dist/stores/concerns/useExpansion.svelte.d.ts +9 -0
- package/dist/stores/concerns/useExpansion.svelte.js +32 -0
- package/dist/stores/concerns/useFiltering.svelte.d.ts +20 -0
- package/dist/stores/concerns/useFiltering.svelte.js +109 -0
- package/dist/stores/concerns/useFocusManagement.svelte.d.ts +15 -0
- package/dist/stores/concerns/useFocusManagement.svelte.js +52 -0
- package/dist/stores/concerns/useGrouping.svelte.d.ts +15 -0
- package/dist/stores/concerns/useGrouping.svelte.js +86 -0
- package/dist/stores/concerns/useLiveUpdates.svelte.d.ts +45 -0
- package/dist/stores/concerns/useLiveUpdates.svelte.js +175 -0
- package/dist/stores/concerns/usePagination.svelte.d.ts +18 -0
- package/dist/stores/concerns/usePagination.svelte.js +54 -0
- package/dist/stores/concerns/usePersistence.svelte.d.ts +36 -0
- package/dist/stores/concerns/usePersistence.svelte.js +167 -0
- package/dist/stores/concerns/useRemoteData.svelte.d.ts +21 -0
- package/dist/stores/concerns/useRemoteData.svelte.js +64 -0
- package/dist/stores/concerns/useSearch.svelte.d.ts +8 -0
- package/dist/stores/concerns/useSearch.svelte.js +16 -0
- package/dist/stores/concerns/useSelection.svelte.d.ts +21 -0
- package/dist/stores/concerns/useSelection.svelte.js +110 -0
- package/dist/stores/concerns/useSorting.svelte.d.ts +11 -0
- package/dist/stores/concerns/useSorting.svelte.js +70 -0
- package/dist/stores/concerns/useSummary.svelte.d.ts +18 -0
- package/dist/stores/concerns/useSummary.svelte.js +96 -0
- package/dist/stores/index.d.ts +1 -0
- package/dist/stores/index.js +1 -0
- package/dist/style/index.css +137 -0
- package/dist/style/index.d.ts +2 -0
- package/dist/style/index.js +2 -0
- package/dist/style/table-theme.css +131 -0
- package/dist/style/themes/comfortable.css +20 -0
- package/dist/style/themes/compact.css +20 -0
- package/dist/translations/de.d.ts +177 -0
- package/dist/translations/de.js +176 -0
- package/dist/translations/en.d.ts +177 -0
- package/dist/translations/en.js +176 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/tableTypes.d.ts +262 -0
- package/dist/types/tableTypes.js +1 -0
- package/dist/utils/index.d.ts +165 -0
- package/dist/utils/index.js +330 -0
- package/dist/utils/sticky-measure.d.ts +54 -0
- package/dist/utils/sticky-measure.js +107 -0
- package/dist/utils/virtualizer.d.ts +43 -0
- package/dist/utils/virtualizer.js +43 -0
- package/dist/variants/index.d.ts +11 -0
- package/dist/variants/index.js +15 -0
- package/dist/variants/table-cells.variants.d.ts +827 -0
- package/dist/variants/table-cells.variants.js +627 -0
- package/dist/variants/table-features.variants.d.ts +547 -0
- package/dist/variants/table-features.variants.js +412 -0
- package/dist/variants/table-states.variants.d.ts +594 -0
- package/dist/variants/table-states.variants.js +394 -0
- package/dist/variants/table.system.d.ts +301 -0
- package/dist/variants/table.system.js +314 -0
- package/dist/variants/table.variants.d.ts +428 -0
- package/dist/variants/table.variants.js +360 -0
- package/package.json +93 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getTableContext } from '../stores/TableStore.svelte';
|
|
3
|
+
import { useTableI18n } from '../i18n';
|
|
4
|
+
import EmptyState from './EmptyState.svelte';
|
|
5
|
+
import ErrorState from './ErrorState.svelte';
|
|
6
|
+
import GroupedRow from './GroupedRow.svelte';
|
|
7
|
+
import LoadingState from './LoadingState.svelte';
|
|
8
|
+
import TableHead from './TableHead.svelte';
|
|
9
|
+
import TableRow from './TableRow.svelte';
|
|
10
|
+
import SummaryRow from '../features/SummaryRow.svelte';
|
|
11
|
+
import { getTableStyleConfig, resolveSlotClass } from './table-style-context';
|
|
12
|
+
import { computeVirtualItems, ROW_HEIGHTS } from '../utils/virtualizer';
|
|
13
|
+
import { getStickyContext } from './sticky-context.svelte';
|
|
14
|
+
import type { Column, TableItem } from '../types/tableTypes';
|
|
15
|
+
import type { Snippet } from 'svelte';
|
|
16
|
+
|
|
17
|
+
const tt = useTableI18n();
|
|
18
|
+
|
|
19
|
+
const styleConfig = getTableStyleConfig();
|
|
20
|
+
const stickyContext = getStickyContext();
|
|
21
|
+
// When header or group-header pinning is enabled, the visible frame must NOT
|
|
22
|
+
// create its own scroll-ancestor (overflow:auto/hidden hijacks `position: sticky`).
|
|
23
|
+
// We trade the in-table horizontal scroll for page-level overflow in that case.
|
|
24
|
+
const scrollAreaOverflow = $derived(
|
|
25
|
+
stickyContext.mode.header || stickyContext.mode.group ? '' : 'overflow-x-auto'
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
tableStyles,
|
|
30
|
+
tableDomWidth = '100%',
|
|
31
|
+
size = 'md' as 'sm' | 'md' | 'lg',
|
|
32
|
+
expandable = false,
|
|
33
|
+
expandedRowContent = undefined as Snippet<[item: TableItem]> | undefined,
|
|
34
|
+
cell = undefined as Snippet<[item: TableItem, value: unknown, column: Column]> | undefined,
|
|
35
|
+
header = undefined as Snippet | undefined,
|
|
36
|
+
body = undefined as Snippet | undefined,
|
|
37
|
+
empty = undefined as Snippet | undefined,
|
|
38
|
+
loading = undefined as Snippet | undefined,
|
|
39
|
+
error = undefined as Snippet | undefined,
|
|
40
|
+
loadingText = '',
|
|
41
|
+
errorText = '',
|
|
42
|
+
noDataText = '',
|
|
43
|
+
onRowClick = undefined as ((item: TableItem) => void) | undefined,
|
|
44
|
+
virtualized = false,
|
|
45
|
+
groupHeaderContent = undefined as
|
|
46
|
+
| Snippet<[groupName: string, items: TableItem[], isExpanded: boolean]>
|
|
47
|
+
| undefined,
|
|
48
|
+
ariaLabel = undefined as string | undefined,
|
|
49
|
+
virtualHeight = '600px',
|
|
50
|
+
enableColumnReorder = false
|
|
51
|
+
} = $props();
|
|
52
|
+
|
|
53
|
+
const tableContext = getTableContext();
|
|
54
|
+
const { state: tableState } = tableContext;
|
|
55
|
+
const filteredItems = $derived(tableContext.filteredItems);
|
|
56
|
+
const paginatedItems = $derived(tableContext.paginatedItems);
|
|
57
|
+
const grouped = $derived(tableContext.grouped);
|
|
58
|
+
const groupedSummaryData = $derived(tableContext.groupedSummaryData);
|
|
59
|
+
|
|
60
|
+
let selectable = $derived(tableState.selectionMode !== 'none');
|
|
61
|
+
let interactive = $derived(selectable || expandable || !!onRowClick);
|
|
62
|
+
|
|
63
|
+
/** Total columns including expand + group + selection columns */
|
|
64
|
+
const totalColSpan = $derived.by(() => {
|
|
65
|
+
let count = tableState.columns.length;
|
|
66
|
+
if (expandable) count += 1;
|
|
67
|
+
if (tableState.groupByKey) count += 1;
|
|
68
|
+
if (selectable) count += 1;
|
|
69
|
+
return count;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
let tableElement = $state<HTMLTableElement | null>(null);
|
|
73
|
+
let scrollContainerEl = $state<HTMLDivElement | null>(null);
|
|
74
|
+
let scrollTop = $state(0);
|
|
75
|
+
let viewportHeight = $state(0);
|
|
76
|
+
|
|
77
|
+
// Virtualized = true bypasses pagination, uses all sorted items
|
|
78
|
+
const virtualizedActive = $derived(virtualized && !tableState.groupByKey);
|
|
79
|
+
const virtualItems = $derived(tableContext.sortedItems);
|
|
80
|
+
const rowHeight = $derived(ROW_HEIGHTS[size] ?? ROW_HEIGHTS.md);
|
|
81
|
+
|
|
82
|
+
const virtualResult = $derived(
|
|
83
|
+
virtualizedActive
|
|
84
|
+
? computeVirtualItems(scrollTop, viewportHeight, {
|
|
85
|
+
count: virtualItems.length,
|
|
86
|
+
rowHeight,
|
|
87
|
+
overscan: 5
|
|
88
|
+
})
|
|
89
|
+
: null
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
function handleVirtualScroll() {
|
|
93
|
+
if (scrollContainerEl) {
|
|
94
|
+
scrollTop = scrollContainerEl.scrollTop;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Observe container height for virtualizer
|
|
99
|
+
$effect(() => {
|
|
100
|
+
if (scrollContainerEl && virtualizedActive) {
|
|
101
|
+
viewportHeight = scrollContainerEl.clientHeight;
|
|
102
|
+
const observer = new ResizeObserver((entries) => {
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
viewportHeight = entry.contentRect.height;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
observer.observe(scrollContainerEl);
|
|
108
|
+
return () => observer.disconnect();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Reset focus when page/sort/filter changes
|
|
113
|
+
$effect(() => {
|
|
114
|
+
// Track dependencies so we reset on any data change
|
|
115
|
+
void tableState.currentPage;
|
|
116
|
+
void tableState.sortColumn;
|
|
117
|
+
void tableState.sortDirection;
|
|
118
|
+
void tableState.searchTerm;
|
|
119
|
+
void tableState.activeFilters;
|
|
120
|
+
tableContext.resetFocus();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
function focusRow(index: number) {
|
|
124
|
+
if (!tableElement) return;
|
|
125
|
+
const rows = tableElement.querySelectorAll<HTMLElement>('tbody tr[data-row-index]');
|
|
126
|
+
const targetRow = rows[index];
|
|
127
|
+
if (targetRow) {
|
|
128
|
+
targetRow.focus({ preventScroll: false });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function getItemIdAtIndex(index: number): string | number | undefined {
|
|
133
|
+
const items = Array.isArray(paginatedItems) ? paginatedItems : [];
|
|
134
|
+
const item = items[index];
|
|
135
|
+
if (!item) return undefined;
|
|
136
|
+
const id = item.id ?? item.__index;
|
|
137
|
+
return typeof id === 'string' || typeof id === 'number' ? id : undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function handleTableKeyDown(e: KeyboardEvent) {
|
|
141
|
+
if (!interactive) return;
|
|
142
|
+
|
|
143
|
+
// Only handle keys when focus is on or inside a row
|
|
144
|
+
const target = e.target as HTMLElement;
|
|
145
|
+
const isInsideInteractive = target.closest(
|
|
146
|
+
'button, input, select, textarea, a[href], [contenteditable]'
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// If the user is interacting with a form element inside a cell, don't capture keys
|
|
150
|
+
// Exception: we still handle arrow keys to navigate out
|
|
151
|
+
if (
|
|
152
|
+
isInsideInteractive &&
|
|
153
|
+
!['ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown'].includes(e.key)
|
|
154
|
+
) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const itemCount = Array.isArray(paginatedItems) ? paginatedItems.length : 0;
|
|
159
|
+
if (itemCount === 0) return;
|
|
160
|
+
|
|
161
|
+
switch (e.key) {
|
|
162
|
+
case 'ArrowDown': {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
tableContext.moveFocus('down');
|
|
165
|
+
focusRow(tableContext.focusedRowIndex);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case 'ArrowUp': {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
tableContext.moveFocus('up');
|
|
171
|
+
focusRow(tableContext.focusedRowIndex);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
case 'Home': {
|
|
175
|
+
e.preventDefault();
|
|
176
|
+
tableContext.moveFocus('first');
|
|
177
|
+
focusRow(tableContext.focusedRowIndex);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
case 'End': {
|
|
181
|
+
e.preventDefault();
|
|
182
|
+
tableContext.moveFocus('last');
|
|
183
|
+
focusRow(tableContext.focusedRowIndex);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
case ' ': {
|
|
187
|
+
// Space = toggle selection
|
|
188
|
+
if (selectable) {
|
|
189
|
+
e.preventDefault();
|
|
190
|
+
const id = getItemIdAtIndex(tableContext.focusedRowIndex);
|
|
191
|
+
if (id !== undefined) {
|
|
192
|
+
tableContext.toggleItem(id);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
case 'Enter': {
|
|
198
|
+
// Enter = expand row or trigger onRowClick
|
|
199
|
+
const id = getItemIdAtIndex(tableContext.focusedRowIndex);
|
|
200
|
+
if (id !== undefined) {
|
|
201
|
+
if (expandable) {
|
|
202
|
+
e.preventDefault();
|
|
203
|
+
tableContext.toggleExpand(id);
|
|
204
|
+
} else if (onRowClick) {
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
const items = Array.isArray(paginatedItems) ? paginatedItems : [];
|
|
207
|
+
const item = items[tableContext.focusedRowIndex];
|
|
208
|
+
if (item) onRowClick(item);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case 'Escape': {
|
|
214
|
+
// Escape = deselect all or collapse expanded rows
|
|
215
|
+
if (selectable && tableContext.selectedItems.length > 0) {
|
|
216
|
+
e.preventDefault();
|
|
217
|
+
tableContext.deselectAll();
|
|
218
|
+
}
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
case 'PageDown': {
|
|
222
|
+
// Next page
|
|
223
|
+
if (tableContext.totalPages > 1 && tableState.currentPage < tableContext.totalPages) {
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
tableContext.goToPage(tableState.currentPage + 1);
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
case 'PageUp': {
|
|
230
|
+
// Previous page
|
|
231
|
+
if (tableContext.totalPages > 1 && tableState.currentPage > 1) {
|
|
232
|
+
e.preventDefault();
|
|
233
|
+
tableContext.goToPage(tableState.currentPage - 1);
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
</script>
|
|
240
|
+
|
|
241
|
+
{#if virtualizedActive}
|
|
242
|
+
<!-- Virtualized mode: scroll container with fixed height -->
|
|
243
|
+
<div
|
|
244
|
+
class={resolveSlotClass(
|
|
245
|
+
tableStyles.scrollArea(),
|
|
246
|
+
styleConfig.slotClasses.scrollArea,
|
|
247
|
+
styleConfig.unstyled,
|
|
248
|
+
'desktop-only relative max-md:hidden'
|
|
249
|
+
)}
|
|
250
|
+
role="region"
|
|
251
|
+
aria-label={tt('aria.tableData')}
|
|
252
|
+
style="width: {tableDomWidth};"
|
|
253
|
+
>
|
|
254
|
+
<!-- Sticky header table -->
|
|
255
|
+
<table
|
|
256
|
+
bind:this={tableElement}
|
|
257
|
+
class={resolveSlotClass(
|
|
258
|
+
tableStyles.table(),
|
|
259
|
+
styleConfig.slotClasses.table,
|
|
260
|
+
styleConfig.unstyled,
|
|
261
|
+
'table-fixed'
|
|
262
|
+
)}
|
|
263
|
+
role={interactive ? 'grid' : undefined}
|
|
264
|
+
aria-label={ariaLabel}
|
|
265
|
+
aria-rowcount={virtualItems.length}
|
|
266
|
+
onkeydown={handleTableKeyDown}
|
|
267
|
+
data-testid="table-element"
|
|
268
|
+
>
|
|
269
|
+
{#if header}
|
|
270
|
+
{@render header()}
|
|
271
|
+
{:else}
|
|
272
|
+
<TableHead {expandable} {enableColumnReorder} {size} />
|
|
273
|
+
{/if}
|
|
274
|
+
</table>
|
|
275
|
+
|
|
276
|
+
<!-- Scrollable body -->
|
|
277
|
+
<div
|
|
278
|
+
bind:this={scrollContainerEl}
|
|
279
|
+
onscroll={handleVirtualScroll}
|
|
280
|
+
class="overflow-x-hidden overflow-y-auto"
|
|
281
|
+
style="height: {virtualHeight};"
|
|
282
|
+
role="presentation"
|
|
283
|
+
data-testid="virtual-scroll-container"
|
|
284
|
+
>
|
|
285
|
+
{#if filteredItems.length === 0}
|
|
286
|
+
<table
|
|
287
|
+
class={resolveSlotClass(
|
|
288
|
+
tableStyles.table(),
|
|
289
|
+
styleConfig.slotClasses.table,
|
|
290
|
+
styleConfig.unstyled,
|
|
291
|
+
'table-fixed'
|
|
292
|
+
)}
|
|
293
|
+
onkeydown={handleTableKeyDown}
|
|
294
|
+
>
|
|
295
|
+
<tbody
|
|
296
|
+
class={resolveSlotClass(
|
|
297
|
+
tableStyles.body(),
|
|
298
|
+
styleConfig.slotClasses.tbody,
|
|
299
|
+
styleConfig.unstyled
|
|
300
|
+
)}
|
|
301
|
+
>
|
|
302
|
+
{#if empty}
|
|
303
|
+
{@render empty()}
|
|
304
|
+
{:else}
|
|
305
|
+
<EmptyState message={noDataText} {size} colSpan={totalColSpan} />
|
|
306
|
+
{/if}
|
|
307
|
+
</tbody>
|
|
308
|
+
</table>
|
|
309
|
+
{:else if virtualResult}
|
|
310
|
+
<!-- Inner container with total height for scrollbar -->
|
|
311
|
+
<div style="height: {virtualResult.totalHeight}px; position: relative;">
|
|
312
|
+
<table
|
|
313
|
+
class="{resolveSlotClass(
|
|
314
|
+
tableStyles.table(),
|
|
315
|
+
styleConfig.slotClasses.table,
|
|
316
|
+
styleConfig.unstyled,
|
|
317
|
+
'table-fixed'
|
|
318
|
+
)} absolute top-0 left-0 w-full"
|
|
319
|
+
onkeydown={handleTableKeyDown}
|
|
320
|
+
>
|
|
321
|
+
<tbody
|
|
322
|
+
class={resolveSlotClass(
|
|
323
|
+
tableStyles.body(),
|
|
324
|
+
styleConfig.slotClasses.tbody,
|
|
325
|
+
styleConfig.unstyled
|
|
326
|
+
)}
|
|
327
|
+
>
|
|
328
|
+
{#each virtualResult.virtualItems as vItem (vItem.index)}
|
|
329
|
+
{@const item = virtualItems[vItem.index]}
|
|
330
|
+
{#if item}
|
|
331
|
+
<TableRow
|
|
332
|
+
{item}
|
|
333
|
+
{expandable}
|
|
334
|
+
{expandedRowContent}
|
|
335
|
+
{cell}
|
|
336
|
+
{size}
|
|
337
|
+
virtualized={true}
|
|
338
|
+
virtualIndex={vItem.index}
|
|
339
|
+
virtualItemHeight={rowHeight}
|
|
340
|
+
{onRowClick}
|
|
341
|
+
rowIndex={vItem.index}
|
|
342
|
+
/>
|
|
343
|
+
{/if}
|
|
344
|
+
{/each}
|
|
345
|
+
</tbody>
|
|
346
|
+
</table>
|
|
347
|
+
</div>
|
|
348
|
+
|
|
349
|
+
{#if tableState.showSummary && tableState.summaryConfigs.length > 0}
|
|
350
|
+
<table
|
|
351
|
+
class={resolveSlotClass(
|
|
352
|
+
tableStyles.table(),
|
|
353
|
+
styleConfig.slotClasses.table,
|
|
354
|
+
styleConfig.unstyled
|
|
355
|
+
)}
|
|
356
|
+
>
|
|
357
|
+
<tbody>
|
|
358
|
+
<SummaryRow {expandable} />
|
|
359
|
+
</tbody>
|
|
360
|
+
</table>
|
|
361
|
+
{/if}
|
|
362
|
+
{/if}
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
{:else}
|
|
366
|
+
<!-- Standard mode: normal table rendering -->
|
|
367
|
+
<div
|
|
368
|
+
class={resolveSlotClass(
|
|
369
|
+
tableStyles.scrollArea(),
|
|
370
|
+
styleConfig.slotClasses.scrollArea,
|
|
371
|
+
styleConfig.unstyled,
|
|
372
|
+
['desktop-only relative max-md:hidden', scrollAreaOverflow].filter(Boolean).join(' ')
|
|
373
|
+
)}
|
|
374
|
+
role="region"
|
|
375
|
+
aria-label={tt('aria.tableData')}
|
|
376
|
+
style="width: {tableDomWidth};"
|
|
377
|
+
>
|
|
378
|
+
<table
|
|
379
|
+
bind:this={tableElement}
|
|
380
|
+
class={resolveSlotClass(
|
|
381
|
+
tableStyles.table(),
|
|
382
|
+
styleConfig.slotClasses.table,
|
|
383
|
+
styleConfig.unstyled
|
|
384
|
+
)}
|
|
385
|
+
role={interactive ? 'grid' : undefined}
|
|
386
|
+
aria-label={ariaLabel}
|
|
387
|
+
aria-rowcount={filteredItems.length}
|
|
388
|
+
onkeydown={handleTableKeyDown}
|
|
389
|
+
data-testid="table-element"
|
|
390
|
+
>
|
|
391
|
+
{#if header}
|
|
392
|
+
{@render header()}
|
|
393
|
+
{:else}
|
|
394
|
+
<TableHead {expandable} {enableColumnReorder} {size} />
|
|
395
|
+
{/if}
|
|
396
|
+
|
|
397
|
+
<tbody
|
|
398
|
+
class={resolveSlotClass(
|
|
399
|
+
tableStyles.body(),
|
|
400
|
+
styleConfig.slotClasses.tbody,
|
|
401
|
+
styleConfig.unstyled
|
|
402
|
+
)}
|
|
403
|
+
>
|
|
404
|
+
{#if tableState.loading}
|
|
405
|
+
{#if loading}
|
|
406
|
+
{@render loading()}
|
|
407
|
+
{:else}
|
|
408
|
+
<LoadingState text={loadingText} {size} colSpan={totalColSpan} />
|
|
409
|
+
{/if}
|
|
410
|
+
{:else if tableState.error}
|
|
411
|
+
{#if error}
|
|
412
|
+
{@render error()}
|
|
413
|
+
{:else}
|
|
414
|
+
<ErrorState
|
|
415
|
+
title={errorText}
|
|
416
|
+
message={tableState.error}
|
|
417
|
+
{size}
|
|
418
|
+
colSpan={totalColSpan}
|
|
419
|
+
/>
|
|
420
|
+
{/if}
|
|
421
|
+
{:else if tableState.groupByKey}
|
|
422
|
+
{#if filteredItems.length === 0}
|
|
423
|
+
{#if empty}
|
|
424
|
+
{@render empty()}
|
|
425
|
+
{:else}
|
|
426
|
+
<EmptyState message={noDataText} {size} colSpan={totalColSpan} />
|
|
427
|
+
{/if}
|
|
428
|
+
{:else}
|
|
429
|
+
{#each Object.entries(grouped) as [groupName, groupItems] (groupName)}
|
|
430
|
+
<GroupedRow
|
|
431
|
+
{groupName}
|
|
432
|
+
items={groupItems}
|
|
433
|
+
{expandable}
|
|
434
|
+
{expandedRowContent}
|
|
435
|
+
{cell}
|
|
436
|
+
{size}
|
|
437
|
+
{groupHeaderContent}
|
|
438
|
+
{onRowClick}
|
|
439
|
+
/>
|
|
440
|
+
|
|
441
|
+
{#if tableState.showSummary && tableState.summaryConfigs.length > 0}
|
|
442
|
+
<SummaryRow
|
|
443
|
+
{expandable}
|
|
444
|
+
{groupName}
|
|
445
|
+
groupSummaryData={groupedSummaryData[groupName]}
|
|
446
|
+
/>
|
|
447
|
+
{/if}
|
|
448
|
+
{/each}
|
|
449
|
+
{/if}
|
|
450
|
+
{:else if filteredItems.length === 0}
|
|
451
|
+
{#if empty}
|
|
452
|
+
{@render empty()}
|
|
453
|
+
{:else}
|
|
454
|
+
<EmptyState message={noDataText} {size} colSpan={totalColSpan} />
|
|
455
|
+
{/if}
|
|
456
|
+
{:else}
|
|
457
|
+
{#if body}
|
|
458
|
+
{@render body()}
|
|
459
|
+
{:else}
|
|
460
|
+
{#each Array.isArray(paginatedItems) ? paginatedItems : [] as item, i (item.id ?? i)}
|
|
461
|
+
<TableRow
|
|
462
|
+
{item}
|
|
463
|
+
{expandable}
|
|
464
|
+
{expandedRowContent}
|
|
465
|
+
{cell}
|
|
466
|
+
{size}
|
|
467
|
+
{onRowClick}
|
|
468
|
+
rowIndex={i}
|
|
469
|
+
/>
|
|
470
|
+
{/each}
|
|
471
|
+
{/if}
|
|
472
|
+
|
|
473
|
+
{#if tableState.showSummary && tableState.summaryConfigs.length > 0}
|
|
474
|
+
<SummaryRow {expandable} />
|
|
475
|
+
{/if}
|
|
476
|
+
{/if}
|
|
477
|
+
</tbody>
|
|
478
|
+
</table>
|
|
479
|
+
</div>
|
|
480
|
+
{/if}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Column, TableItem } from '../types/tableTypes';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
declare const TableDesktop: import("svelte").Component<{
|
|
4
|
+
tableStyles: any;
|
|
5
|
+
tableDomWidth?: string;
|
|
6
|
+
size?: "sm" | "md" | "lg";
|
|
7
|
+
expandable?: boolean;
|
|
8
|
+
expandedRowContent?: Snippet<[item: TableItem]> | undefined;
|
|
9
|
+
cell?: Snippet<[item: TableItem, value: unknown, column: Column]> | undefined;
|
|
10
|
+
header?: Snippet | undefined;
|
|
11
|
+
body?: Snippet | undefined;
|
|
12
|
+
empty?: Snippet | undefined;
|
|
13
|
+
loading?: Snippet | undefined;
|
|
14
|
+
error?: Snippet | undefined;
|
|
15
|
+
loadingText?: string;
|
|
16
|
+
errorText?: string;
|
|
17
|
+
noDataText?: string;
|
|
18
|
+
onRowClick?: ((item: TableItem) => void) | undefined;
|
|
19
|
+
virtualized?: boolean;
|
|
20
|
+
groupHeaderContent?: Snippet<[groupName: string, items: TableItem[], isExpanded: boolean]> | undefined;
|
|
21
|
+
ariaLabel?: string | undefined;
|
|
22
|
+
virtualHeight?: string;
|
|
23
|
+
enableColumnReorder?: boolean;
|
|
24
|
+
}, {}, "">;
|
|
25
|
+
type TableDesktop = ReturnType<typeof TableDesktop>;
|
|
26
|
+
export default TableDesktop;
|