@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.
Files changed (154) hide show
  1. package/README.md +153 -0
  2. package/dist/cells/ActionButtons.svelte +224 -0
  3. package/dist/cells/ActionButtons.svelte.d.ts +74 -0
  4. package/dist/cells/CopyButton.svelte +89 -0
  5. package/dist/cells/CopyButton.svelte.d.ts +33 -0
  6. package/dist/cells/CustomCell.svelte +136 -0
  7. package/dist/cells/CustomCell.svelte.d.ts +44 -0
  8. package/dist/cells/DateCell.svelte +194 -0
  9. package/dist/cells/DateCell.svelte.d.ts +39 -0
  10. package/dist/cells/LinkCell.svelte +240 -0
  11. package/dist/cells/LinkCell.svelte.d.ts +42 -0
  12. package/dist/cells/NumberCell.svelte +225 -0
  13. package/dist/cells/NumberCell.svelte.d.ts +47 -0
  14. package/dist/cells/StatusBadge.svelte +121 -0
  15. package/dist/cells/StatusBadge.svelte.d.ts +44 -0
  16. package/dist/cells/UserAvatar.svelte +71 -0
  17. package/dist/cells/UserAvatar.svelte.d.ts +37 -0
  18. package/dist/cells/index.d.ts +8 -0
  19. package/dist/cells/index.js +9 -0
  20. package/dist/core/EmptyState.svelte +161 -0
  21. package/dist/core/EmptyState.svelte.d.ts +16 -0
  22. package/dist/core/ErrorState.svelte +158 -0
  23. package/dist/core/ErrorState.svelte.d.ts +15 -0
  24. package/dist/core/GroupedRow.svelte +239 -0
  25. package/dist/core/GroupedRow.svelte.d.ts +18 -0
  26. package/dist/core/LoadingState.svelte +75 -0
  27. package/dist/core/LoadingState.svelte.d.ts +14 -0
  28. package/dist/core/MobileCard.svelte +151 -0
  29. package/dist/core/MobileCard.svelte.d.ts +15 -0
  30. package/dist/core/TableCell.svelte +105 -0
  31. package/dist/core/TableCell.svelte.d.ts +14 -0
  32. package/dist/core/TableDesktop.svelte +480 -0
  33. package/dist/core/TableDesktop.svelte.d.ts +26 -0
  34. package/dist/core/TableHead.svelte +314 -0
  35. package/dist/core/TableHead.svelte.d.ts +7 -0
  36. package/dist/core/TableMobile.svelte +112 -0
  37. package/dist/core/TableMobile.svelte.d.ts +13 -0
  38. package/dist/core/TableProvider.svelte +271 -0
  39. package/dist/core/TableProvider.svelte.d.ts +40 -0
  40. package/dist/core/TableRow.svelte +171 -0
  41. package/dist/core/TableRow.svelte.d.ts +16 -0
  42. package/dist/core/index.d.ts +17 -0
  43. package/dist/core/index.js +14 -0
  44. package/dist/core/sticky-context.svelte.d.ts +48 -0
  45. package/dist/core/sticky-context.svelte.js +88 -0
  46. package/dist/core/table/Table.svelte +304 -0
  47. package/dist/core/table/Table.svelte.d.ts +26 -0
  48. package/dist/core/table/index.d.ts +448 -0
  49. package/dist/core/table/index.js +1 -0
  50. package/dist/core/table-style-context.d.ts +66 -0
  51. package/dist/core/table-style-context.js +26 -0
  52. package/dist/factories/ColumnValidation.d.ts +49 -0
  53. package/dist/factories/ColumnValidation.js +188 -0
  54. package/dist/factories/TableColumns.d.ts +97 -0
  55. package/dist/factories/TableColumns.js +262 -0
  56. package/dist/factories/TypedColumnBuilder.d.ts +41 -0
  57. package/dist/factories/TypedColumnBuilder.js +72 -0
  58. package/dist/factories/index.d.ts +12 -0
  59. package/dist/factories/index.js +13 -0
  60. package/dist/features/HeaderMenu.svelte +236 -0
  61. package/dist/features/HeaderMenu.svelte.d.ts +8 -0
  62. package/dist/features/LiveUpdateBanner.svelte +66 -0
  63. package/dist/features/LiveUpdateBanner.svelte.d.ts +6 -0
  64. package/dist/features/SearchHighlight.svelte +21 -0
  65. package/dist/features/SearchHighlight.svelte.d.ts +8 -0
  66. package/dist/features/SmartFilterBar/ChipsField.svelte +104 -0
  67. package/dist/features/SmartFilterBar/ChipsField.svelte.d.ts +5 -0
  68. package/dist/features/SmartFilterBar/ColumnVisibilityMenu.svelte +84 -0
  69. package/dist/features/SmartFilterBar/ColumnVisibilityMenu.svelte.d.ts +3 -0
  70. package/dist/features/SmartFilterBar/FilterMenu.svelte +367 -0
  71. package/dist/features/SmartFilterBar/FilterMenu.svelte.d.ts +3 -0
  72. package/dist/features/SmartFilterBar/GroupingMenu.svelte +82 -0
  73. package/dist/features/SmartFilterBar/GroupingMenu.svelte.d.ts +3 -0
  74. package/dist/features/SmartFilterBar/SmartFilterBar.svelte +109 -0
  75. package/dist/features/SmartFilterBar/SmartFilterBar.svelte.d.ts +11 -0
  76. package/dist/features/SmartFilterBar/SummaryMenu.svelte +118 -0
  77. package/dist/features/SmartFilterBar/SummaryMenu.svelte.d.ts +3 -0
  78. package/dist/features/SummaryRow.svelte +97 -0
  79. package/dist/features/SummaryRow.svelte.d.ts +8 -0
  80. package/dist/features/index.d.ts +4 -0
  81. package/dist/features/index.js +4 -0
  82. package/dist/i18n/index.d.ts +366 -0
  83. package/dist/i18n/index.js +21 -0
  84. package/dist/index.d.ts +28 -0
  85. package/dist/index.js +41 -0
  86. package/dist/stores/TableStore.svelte.d.ts +192 -0
  87. package/dist/stores/TableStore.svelte.js +362 -0
  88. package/dist/stores/concerns/index.d.ts +15 -0
  89. package/dist/stores/concerns/index.js +14 -0
  90. package/dist/stores/concerns/types.d.ts +31 -0
  91. package/dist/stores/concerns/types.js +1 -0
  92. package/dist/stores/concerns/useColumnOrder.svelte.d.ts +16 -0
  93. package/dist/stores/concerns/useColumnOrder.svelte.js +81 -0
  94. package/dist/stores/concerns/useColumnVisibility.svelte.d.ts +16 -0
  95. package/dist/stores/concerns/useColumnVisibility.svelte.js +58 -0
  96. package/dist/stores/concerns/useExpansion.svelte.d.ts +9 -0
  97. package/dist/stores/concerns/useExpansion.svelte.js +32 -0
  98. package/dist/stores/concerns/useFiltering.svelte.d.ts +20 -0
  99. package/dist/stores/concerns/useFiltering.svelte.js +109 -0
  100. package/dist/stores/concerns/useFocusManagement.svelte.d.ts +15 -0
  101. package/dist/stores/concerns/useFocusManagement.svelte.js +52 -0
  102. package/dist/stores/concerns/useGrouping.svelte.d.ts +15 -0
  103. package/dist/stores/concerns/useGrouping.svelte.js +86 -0
  104. package/dist/stores/concerns/useLiveUpdates.svelte.d.ts +45 -0
  105. package/dist/stores/concerns/useLiveUpdates.svelte.js +175 -0
  106. package/dist/stores/concerns/usePagination.svelte.d.ts +18 -0
  107. package/dist/stores/concerns/usePagination.svelte.js +54 -0
  108. package/dist/stores/concerns/usePersistence.svelte.d.ts +36 -0
  109. package/dist/stores/concerns/usePersistence.svelte.js +167 -0
  110. package/dist/stores/concerns/useRemoteData.svelte.d.ts +21 -0
  111. package/dist/stores/concerns/useRemoteData.svelte.js +64 -0
  112. package/dist/stores/concerns/useSearch.svelte.d.ts +8 -0
  113. package/dist/stores/concerns/useSearch.svelte.js +16 -0
  114. package/dist/stores/concerns/useSelection.svelte.d.ts +21 -0
  115. package/dist/stores/concerns/useSelection.svelte.js +110 -0
  116. package/dist/stores/concerns/useSorting.svelte.d.ts +11 -0
  117. package/dist/stores/concerns/useSorting.svelte.js +70 -0
  118. package/dist/stores/concerns/useSummary.svelte.d.ts +18 -0
  119. package/dist/stores/concerns/useSummary.svelte.js +96 -0
  120. package/dist/stores/index.d.ts +1 -0
  121. package/dist/stores/index.js +1 -0
  122. package/dist/style/index.css +137 -0
  123. package/dist/style/index.d.ts +2 -0
  124. package/dist/style/index.js +2 -0
  125. package/dist/style/table-theme.css +131 -0
  126. package/dist/style/themes/comfortable.css +20 -0
  127. package/dist/style/themes/compact.css +20 -0
  128. package/dist/translations/de.d.ts +177 -0
  129. package/dist/translations/de.js +176 -0
  130. package/dist/translations/en.d.ts +177 -0
  131. package/dist/translations/en.js +176 -0
  132. package/dist/types/index.d.ts +1 -0
  133. package/dist/types/index.js +1 -0
  134. package/dist/types/tableTypes.d.ts +262 -0
  135. package/dist/types/tableTypes.js +1 -0
  136. package/dist/utils/index.d.ts +165 -0
  137. package/dist/utils/index.js +330 -0
  138. package/dist/utils/sticky-measure.d.ts +54 -0
  139. package/dist/utils/sticky-measure.js +107 -0
  140. package/dist/utils/virtualizer.d.ts +43 -0
  141. package/dist/utils/virtualizer.js +43 -0
  142. package/dist/variants/index.d.ts +11 -0
  143. package/dist/variants/index.js +15 -0
  144. package/dist/variants/table-cells.variants.d.ts +827 -0
  145. package/dist/variants/table-cells.variants.js +627 -0
  146. package/dist/variants/table-features.variants.d.ts +547 -0
  147. package/dist/variants/table-features.variants.js +412 -0
  148. package/dist/variants/table-states.variants.d.ts +594 -0
  149. package/dist/variants/table-states.variants.js +394 -0
  150. package/dist/variants/table.system.d.ts +301 -0
  151. package/dist/variants/table.system.js +314 -0
  152. package/dist/variants/table.variants.d.ts +428 -0
  153. package/dist/variants/table.variants.js +360 -0
  154. package/package.json +93 -0
@@ -0,0 +1,314 @@
1
+ <script lang="ts">
2
+ import {
3
+ resolveIcon,
4
+ Checkbox,
5
+ createDraggable,
6
+ findDropTarget,
7
+ ChevronDownIcon as ChevronDownIconDefault,
8
+ ChevronUpIcon as ChevronUpIconDefault
9
+ } from '@urbicon-ui/blocks';
10
+ import { getTableContext } from '../stores/TableStore.svelte';
11
+ import { resolveColumnId } from '../utils';
12
+
13
+ const ChevronDownIcon = resolveIcon('chevronDown', ChevronDownIconDefault);
14
+ const ChevronUpIcon = resolveIcon('chevronUp', ChevronUpIconDefault);
15
+ import HeaderMenu from '../features/HeaderMenu.svelte';
16
+ import { useTableI18n } from '../i18n';
17
+ import { tableHeaderVariants, headerIndicatorVariants } from '../variants';
18
+ import { TABLE_INDICATORS, TABLE_RESPONSIVE } from '../variants/table.system';
19
+ import { getTableStyleConfig, resolveSlotClass } from './table-style-context';
20
+ import { getStickyContext } from './sticky-context.svelte';
21
+ import { measureToCssVar } from '../utils/sticky-measure';
22
+
23
+ const tt = useTableI18n();
24
+
25
+ let { expandable = false, enableColumnReorder = false, size = 'md' as const } = $props();
26
+
27
+ const tableContext = getTableContext();
28
+ const { state: tableState, handleSort, toggleAllGroups } = tableContext;
29
+ const styleConfig = getTableStyleConfig();
30
+ const stickyContext = getStickyContext();
31
+
32
+ let selectable = $derived(tableState.selectionMode !== 'none');
33
+ let multiSelect = $derived(tableState.selectionMode === 'multi');
34
+
35
+ // Column reorder state
36
+ let dragFromIndex = $state<number | null>(null);
37
+ let dropIndicatorIndex = $state<number | null>(null);
38
+
39
+ // Use orderedColumns when column reorder is enabled
40
+ const displayColumns = $derived(
41
+ enableColumnReorder ? tableContext.orderedColumns : tableState.columns
42
+ );
43
+
44
+ function makeDraggable(colIndex: number) {
45
+ if (!enableColumnReorder) {
46
+ // No-op attachment when reorder is disabled.
47
+ return () => {};
48
+ }
49
+
50
+ return createDraggable({
51
+ axis: 'horizontal',
52
+ threshold: 8,
53
+ cursor: 'grabbing',
54
+ disabled: !enableColumnReorder,
55
+ onDragStart: ({ element }) => {
56
+ dragFromIndex = colIndex;
57
+ element.style.opacity = '0.5';
58
+ },
59
+ onDragMove: ({ clientX, clientY }) => {
60
+ const target = findDropTarget(clientX, clientY, 'reorderCol');
61
+ if (target) {
62
+ dropIndicatorIndex = Number(target.dataset.reorderCol);
63
+ }
64
+ },
65
+ onDragEnd: ({ element, didDrag }) => {
66
+ element.style.opacity = '';
67
+ if (
68
+ didDrag &&
69
+ dragFromIndex !== null &&
70
+ dropIndicatorIndex !== null &&
71
+ dragFromIndex !== dropIndicatorIndex
72
+ ) {
73
+ tableContext.reorderColumn(dragFromIndex, dropIndicatorIndex);
74
+ }
75
+ dragFromIndex = null;
76
+ dropIndicatorIndex = null;
77
+ }
78
+ });
79
+ }
80
+
81
+ function handleHeaderKeyDown(e: KeyboardEvent, colIndex: number) {
82
+ if (!enableColumnReorder) return;
83
+ if (!e.shiftKey) return;
84
+
85
+ if (e.key === 'ArrowLeft' && colIndex > 0) {
86
+ e.preventDefault();
87
+ tableContext.reorderColumn(colIndex, colIndex - 1);
88
+ } else if (e.key === 'ArrowRight' && colIndex < displayColumns.length - 1) {
89
+ e.preventDefault();
90
+ tableContext.reorderColumn(colIndex, colIndex + 1);
91
+ }
92
+ }
93
+
94
+ function hasActiveFilter(columnKey: string): boolean {
95
+ return tableState.activeFilters.some((filter) => filter.column === columnKey);
96
+ }
97
+
98
+ function isGroupedColumn(columnKey: string): boolean {
99
+ return tableState.groupByKey === columnKey;
100
+ }
101
+
102
+ function hasSummary(columnKey: string): boolean {
103
+ return tableState.summaryConfigs.some((config) => config.column === columnKey);
104
+ }
105
+
106
+ function getSummaryTypes(columnKey: string): string[] {
107
+ return tableState.summaryConfigs
108
+ .filter((config) => config.column === columnKey)
109
+ .map((config) => {
110
+ switch (config.type) {
111
+ case 'sum':
112
+ return '∑';
113
+ case 'avg':
114
+ return '⌀';
115
+ case 'count':
116
+ return '#';
117
+ case 'min':
118
+ return '↓';
119
+ case 'max':
120
+ return '↑';
121
+ default:
122
+ return '?';
123
+ }
124
+ });
125
+ }
126
+
127
+ function getActionIndicators(columnKey: string) {
128
+ const indicators: Array<{ type: 'filter' | 'group' | 'summary' }> = [];
129
+ if (hasActiveFilter(columnKey)) indicators.push({ type: 'filter' });
130
+ if (isGroupedColumn(columnKey)) indicators.push({ type: 'group' });
131
+ if (hasSummary(columnKey)) indicators.push({ type: 'summary' });
132
+ return indicators;
133
+ }
134
+
135
+ const INDICATOR_BG: Record<string, string> = {
136
+ filter: TABLE_INDICATORS.dot.intent.filter,
137
+ group: TABLE_INDICATORS.dot.intent.group,
138
+ summary: TABLE_INDICATORS.dot.intent.summary
139
+ };
140
+
141
+ const headerStyles = $derived(tableHeaderVariants({ size, sticky: stickyContext.mode.header }));
142
+ </script>
143
+
144
+ <thead
145
+ class={resolveSlotClass(
146
+ headerStyles.header(),
147
+ styleConfig.slotClasses.thead,
148
+ styleConfig.unstyled
149
+ )}
150
+ {@attach stickyContext.mode.header ? measureToCssVar('--blocks-table-thead-h') : () => {}}
151
+ >
152
+ <tr
153
+ class={resolveSlotClass(
154
+ headerStyles.row(),
155
+ styleConfig.slotClasses.headerRow,
156
+ styleConfig.unstyled
157
+ )}
158
+ >
159
+ {#if tableState.groupByKey}
160
+ <th class="{headerStyles.cell()} w-10 text-center">
161
+ <button
162
+ onclick={() => toggleAllGroups()}
163
+ class="text-text-secondary hover:text-text-primary hover:bg-surface-hover rounded-modify flex h-6 w-6 items-center justify-center transition-colors"
164
+ aria-label={tableState.allGroupsExpanded
165
+ ? tt('header.collapseAllGroups')
166
+ : tt('header.expandAllGroups')}
167
+ data-testid="toggle-all-groups"
168
+ >
169
+ {#if tableState.allGroupsExpanded}
170
+ <ChevronUpIcon class="h-4 w-4" />
171
+ {:else}
172
+ <ChevronDownIcon class="h-4 w-4" />
173
+ {/if}
174
+ </button>
175
+ </th>
176
+ {/if}
177
+
178
+ {#if selectable}
179
+ <th class="{headerStyles.cell()} w-12 text-center" data-testid="selection-header">
180
+ {#if multiSelect}
181
+ <Checkbox
182
+ checked={tableContext.allSelected}
183
+ indeterminate={tableContext.someSelected}
184
+ onchange={() => tableContext.toggleAll()}
185
+ aria-label={tableContext.allSelected
186
+ ? tt('selection.deselectAllRows')
187
+ : tt('selection.selectAllRows')}
188
+ size="sm"
189
+ />
190
+ {/if}
191
+ </th>
192
+ {/if}
193
+
194
+ {#if expandable}
195
+ <th class="{headerStyles.cell()} w-10 text-center" aria-hidden="true"></th>
196
+ {/if}
197
+
198
+ {#each displayColumns as column, colIdx (resolveColumnId(column))}
199
+ {@const columnId = resolveColumnId(column)}
200
+ {@const hasFilter = hasActiveFilter(columnId)}
201
+ {@const isGrouped = isGroupedColumn(columnId)}
202
+ {@const columnHasSummary = hasSummary(columnId)}
203
+ {@const summaryTypes = getSummaryTypes(columnId)}
204
+ {@const actionIndicators = getActionIndicators(columnId)}
205
+ {@const isActiveSorted = tableState.sortColumn === columnId}
206
+ {@const sortedState = isActiveSorted ? tableState.sortDirection : 'none'}
207
+ {@const isSortable =
208
+ column.accessor !== undefined && (column.sortable === undefined || column.sortable)}
209
+ {@const columnStyles = tableHeaderVariants({
210
+ size,
211
+ sortable: isSortable,
212
+ sorted: sortedState
213
+ })}
214
+ {@const isDragOver =
215
+ dropIndicatorIndex === colIdx && dragFromIndex !== null && dragFromIndex !== colIdx}
216
+
217
+ <th
218
+ {@attach makeDraggable(colIdx)}
219
+ style={column.width
220
+ ? `width: ${column.width}; min-width: ${column.minWidth || '4rem'};`
221
+ : ''}
222
+ class="{columnStyles.cell()} whitespace-nowrap {column.flex
223
+ ? 'flex-col'
224
+ : ''} {column.priority
225
+ ? (TABLE_RESPONSIVE.priority[column.priority as keyof typeof TABLE_RESPONSIVE.priority] ??
226
+ '')
227
+ : ''} {enableColumnReorder ? 'cursor-grab' : ''} {isDragOver
228
+ ? 'outline-primary outline outline-2 outline-offset-[-2px]'
229
+ : ''}"
230
+ aria-sort={isActiveSorted
231
+ ? tableState.sortDirection === 'asc'
232
+ ? 'ascending'
233
+ : 'descending'
234
+ : 'none'}
235
+ onkeydown={(e) => handleHeaderKeyDown(e, colIdx)}
236
+ data-reorder-col={colIdx}
237
+ data-testid={`column-header-${columnId}`}
238
+ >
239
+ <div class={columnStyles.cellContent()}>
240
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
241
+ <div
242
+ class="{columnStyles.titleContainer()} {isSortable ? 'cursor-pointer' : ''}"
243
+ onclick={() => isSortable && handleSort(columnId)}
244
+ role={isSortable ? 'button' : undefined}
245
+ tabindex={isSortable ? 0 : undefined}
246
+ onkeydown={(e) => {
247
+ if (isSortable && (e.key === 'Enter' || e.key === ' ')) {
248
+ e.preventDefault();
249
+ handleSort(columnId);
250
+ }
251
+ }}
252
+ >
253
+ <div class={columnStyles.titleContent()}>
254
+ <span class={columnStyles.title()}>{column.title}</span>
255
+
256
+ <div class={columnStyles.indicators()}>
257
+ {#if hasFilter}
258
+ <div
259
+ class={headerIndicatorVariants({ type: 'filter', state: 'default' })}
260
+ title={tt('header.activeFilter')}
261
+ data-testid={`filter-indicator-${columnId}`}
262
+ ></div>
263
+ {/if}
264
+
265
+ {#if isGrouped}
266
+ <div
267
+ class={headerIndicatorVariants({ type: 'group', state: 'default' })}
268
+ title={tt('header.groupedColumn')}
269
+ data-testid={`group-indicator-${columnId}`}
270
+ ></div>
271
+ {/if}
272
+
273
+ {#if columnHasSummary}
274
+ <div
275
+ class={headerIndicatorVariants({ type: 'summary', state: 'default' })}
276
+ title={tt('header.summarizedColumn') + ': ' + summaryTypes.join(', ')}
277
+ data-testid={`summary-indicator-${columnId}`}
278
+ ></div>
279
+ {/if}
280
+ </div>
281
+ </div>
282
+
283
+ {#if isActiveSorted}
284
+ <span class={columnStyles.sortIcon()} aria-hidden="true">
285
+ {#if tableState.sortDirection === 'asc'}
286
+ <ChevronUpIcon class="h-4 w-4" />
287
+ {:else}
288
+ <ChevronDownIcon class="h-4 w-4" />
289
+ {/if}
290
+ </span>
291
+ {/if}
292
+ </div>
293
+
294
+ <HeaderMenu {column} isActive={isActiveSorted || isGrouped || columnHasSummary} />
295
+ </div>
296
+
297
+ {#if actionIndicators.length > 0}
298
+ <div
299
+ class={columnStyles.actionIndicators()}
300
+ data-testid={`action-indicators-${columnId}`}
301
+ >
302
+ {#each actionIndicators as indicator (indicator.type)}
303
+ <div
304
+ class="{columnStyles.actionIndicatorBar()} {INDICATOR_BG[indicator.type]}"
305
+ title={tt('header.activeIndicator', { type: indicator.type })}
306
+ data-action={indicator.type}
307
+ ></div>
308
+ {/each}
309
+ </div>
310
+ {/if}
311
+ </th>
312
+ {/each}
313
+ </tr>
314
+ </thead>
@@ -0,0 +1,7 @@
1
+ declare const TableHead: import("svelte").Component<{
2
+ expandable?: boolean;
3
+ enableColumnReorder?: boolean;
4
+ size?: const;
5
+ }, {}, "">;
6
+ type TableHead = ReturnType<typeof TableHead>;
7
+ export default TableHead;
@@ -0,0 +1,112 @@
1
+ <script lang="ts">
2
+ import { getTableContext } from '../stores/TableStore.svelte';
3
+ import { useTableI18n } from '../i18n';
4
+ import MobileCard from './MobileCard.svelte';
5
+ import { resolveColumnId } from '../utils';
6
+ import type { Column, TableItem } from '../types/tableTypes';
7
+ import type { Snippet } from 'svelte';
8
+
9
+ const tt = useTableI18n();
10
+
11
+ let {
12
+ size = 'md' as 'sm' | 'md' | 'lg',
13
+ expandable = false,
14
+ expandedRowContent = undefined as Snippet<[item: TableItem]> | undefined,
15
+ cell = undefined as Snippet<[item: TableItem, value: unknown, column: Column]> | undefined,
16
+ empty = undefined as Snippet | undefined,
17
+ noDataText = '',
18
+ onRowClick = undefined as ((item: TableItem) => void) | undefined
19
+ } = $props();
20
+
21
+ const tableContext = getTableContext();
22
+ const { state: tableState, groupedSummaryData } = tableContext;
23
+ const filteredItems = $derived(tableContext.filteredItems);
24
+ const paginatedItems = $derived(tableContext.paginatedItems);
25
+ const grouped = $derived(tableContext.grouped);
26
+ </script>
27
+
28
+ <div class="mobile-only md:hidden" data-testid="mobile-table">
29
+ {#if filteredItems.length === 0}
30
+ {#if empty}
31
+ {@render empty()}
32
+ {:else}
33
+ <div class="text-text-secondary py-6 text-center text-sm" data-testid="empty-state-mobile">
34
+ {noDataText}
35
+ </div>
36
+ {/if}
37
+ {:else if tableState.groupByKey}
38
+ {#each Object.entries(grouped) as [groupName, groupItems] (groupName)}
39
+ <div class="mb-6">
40
+ <h3
41
+ class="text-text-primary border-border-subtle mb-3 flex min-h-11 items-center border-b pb-2 text-base font-semibold"
42
+ >
43
+ {groupName}
44
+ <span class="text-text-tertiary ml-1.5 text-sm font-normal">
45
+ ({groupItems.length}
46
+ {groupItems.length === 1 ? tt('group.item') : tt('group.items')})
47
+ </span>
48
+ </h3>
49
+ {#each groupItems as item, i (item.id ?? i)}
50
+ <MobileCard {item} {expandable} {expandedRowContent} {cell} {size} onClick={onRowClick} />
51
+ {/each}
52
+
53
+ {#if tableState.showSummary && tableState.summaryConfigs.length > 0}
54
+ <div class="bg-surface-elevated border-border-subtle rounded-contain mt-3 border p-4">
55
+ <h4 class="text-text-primary mb-2 text-sm font-semibold">
56
+ {tt('group.summaryFor')}
57
+ {groupName}:
58
+ </h4>
59
+ <div class="space-y-1.5 text-sm">
60
+ {#each tableState.summaryConfigs as config (config.column)}
61
+ {#if groupedSummaryData[groupName] && groupedSummaryData[groupName][config.column] !== undefined}
62
+ <div class="flex min-h-8 items-center justify-between">
63
+ <span class="text-text-secondary">
64
+ {tableState.columns.find((c) => resolveColumnId(c) === config.column)
65
+ ?.title || config.column}:
66
+ </span>
67
+ <span class="text-text-primary font-medium">
68
+ {tableContext.getFormattedSummaryValue(
69
+ config.column,
70
+ groupedSummaryData[groupName][config.column]
71
+ )}
72
+ </span>
73
+ </div>
74
+ {/if}
75
+ {/each}
76
+ </div>
77
+ </div>
78
+ {/if}
79
+ </div>
80
+ {/each}
81
+ {:else}
82
+ {#each Array.isArray(paginatedItems) ? paginatedItems : [] as item, i (item.id ?? i)}
83
+ <MobileCard {item} {expandable} {expandedRowContent} {cell} {size} onClick={onRowClick} />
84
+ {/each}
85
+
86
+ {#if tableState.showSummary && tableState.summaryConfigs.length > 0}
87
+ <div class="bg-surface-elevated border-border-subtle rounded-contain mt-4 border p-4">
88
+ <h4 class="text-text-primary mb-2 text-sm font-semibold">
89
+ {tt('table.summary.totalSummary')}
90
+ </h4>
91
+ <div class="space-y-1.5 text-sm">
92
+ {#each tableState.summaryConfigs as config (config.column)}
93
+ {#if tableContext.summaryData[config.column] !== undefined}
94
+ <div class="flex min-h-8 items-center justify-between">
95
+ <span class="text-text-secondary">
96
+ {tableState.columns.find((c) => resolveColumnId(c) === config.column)?.title ||
97
+ config.column}:
98
+ </span>
99
+ <span class="text-text-primary font-medium">
100
+ {tableContext.getFormattedSummaryValue(
101
+ config.column,
102
+ tableContext.summaryData[config.column]
103
+ )}
104
+ </span>
105
+ </div>
106
+ {/if}
107
+ {/each}
108
+ </div>
109
+ </div>
110
+ {/if}
111
+ {/if}
112
+ </div>
@@ -0,0 +1,13 @@
1
+ import type { Column, TableItem } from '../types/tableTypes';
2
+ import type { Snippet } from 'svelte';
3
+ declare const TableMobile: import("svelte").Component<{
4
+ size?: "sm" | "md" | "lg";
5
+ expandable?: boolean;
6
+ expandedRowContent?: Snippet<[item: TableItem]> | undefined;
7
+ cell?: Snippet<[item: TableItem, value: unknown, column: Column]> | undefined;
8
+ empty?: Snippet | undefined;
9
+ noDataText?: string;
10
+ onRowClick?: ((item: TableItem) => void) | undefined;
11
+ }, {}, "">;
12
+ type TableMobile = ReturnType<typeof TableMobile>;
13
+ export default TableMobile;