@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,13 @@
1
+ // === TABLE FACTORIES ===
2
+ // === CELL COMPONENTS ===
3
+ export { default as ActionButtons } from '../cells/ActionButtons.svelte';
4
+ export { default as CopyButton } from '../cells/CopyButton.svelte';
5
+ export { default as CustomCell } from '../cells/CustomCell.svelte';
6
+ export { default as DateCell } from '../cells/DateCell.svelte';
7
+ export { default as LinkCell } from '../cells/LinkCell.svelte';
8
+ export { default as NumberCell } from '../cells/NumberCell.svelte';
9
+ export { default as StatusBadge } from '../cells/StatusBadge.svelte';
10
+ export { default as UserAvatar } from '../cells/UserAvatar.svelte';
11
+ export { ColumnValidation, ValidationHelpers } from './ColumnValidation';
12
+ export { TableColumns } from './TableColumns';
13
+ export { TypedColumnBuilder } from './TypedColumnBuilder';
@@ -0,0 +1,236 @@
1
+ <script lang="ts">
2
+ import { getTableContext } from '../stores/TableStore.svelte';
3
+ import { headerMenuItemVariants, headerMenuVariants } from '../variants';
4
+ import {
5
+ Button,
6
+ Popover,
7
+ resolveIcon,
8
+ ArrowDownIcon as ArrowDownIconDefault,
9
+ ArrowUpIcon as ArrowUpIconDefault,
10
+ CalculatorIcon as CalculatorIconDefault,
11
+ EyeIcon as EyeIconDefault,
12
+ EyeOffIcon as EyeOffIconDefault,
13
+ FilterXIcon as FilterXIconDefault,
14
+ MoreVerticalIcon as MoreVerticalIconDefault,
15
+ UsersIcon as UsersIconDefault
16
+ } from '@urbicon-ui/blocks';
17
+ import { useTableI18n } from '../i18n';
18
+ import { resolveColumnId, resolveColumnLabel } from '../utils';
19
+ import type { Column } from '../types/tableTypes';
20
+
21
+ const tt = useTableI18n();
22
+
23
+ type HeaderMenuProps = {
24
+ column: Column;
25
+ isActive?: boolean;
26
+ };
27
+
28
+ const ArrowDownIcon = resolveIcon('arrowDown', ArrowDownIconDefault);
29
+ const ArrowUpIcon = resolveIcon('arrowUp', ArrowUpIconDefault);
30
+ const CalculatorIcon = resolveIcon('calculator', CalculatorIconDefault);
31
+ const EyeIcon = resolveIcon('eye', EyeIconDefault);
32
+ const EyeOffIcon = resolveIcon('eyeOff', EyeOffIconDefault);
33
+ const FilterXIcon = resolveIcon('filterX', FilterXIconDefault);
34
+ const MoreVerticalIcon = resolveIcon('moreVertical', MoreVerticalIconDefault);
35
+ const UsersIcon = resolveIcon('users', UsersIconDefault);
36
+
37
+ let { column, isActive = false }: HeaderMenuProps = $props();
38
+
39
+ const tableContext = getTableContext();
40
+ const {
41
+ state: tableState,
42
+ setGroupByKey,
43
+ addSummaryConfig,
44
+ removeSummaryConfig,
45
+ removeFiltersByColumn,
46
+ hideColumn,
47
+ showColumn
48
+ } = tableContext;
49
+
50
+ let menuOpen = $state(false);
51
+
52
+ const columnId = $derived(resolveColumnId(column));
53
+ // Synthetic columns (no accessor) cannot participate in derived ops.
54
+ const canSort = $derived(column.accessor !== undefined && column.sortable !== false);
55
+ const canGroup = $derived(column.accessor !== undefined && column.groupable !== false);
56
+
57
+ const styles = $derived(headerMenuVariants({ active: isActive }));
58
+
59
+ function handleSortAsc() {
60
+ tableState.sortColumn = columnId;
61
+ tableState.sortDirection = 'asc';
62
+ menuOpen = false;
63
+ }
64
+
65
+ function handleSortDesc() {
66
+ tableState.sortColumn = columnId;
67
+ tableState.sortDirection = 'desc';
68
+ menuOpen = false;
69
+ }
70
+
71
+ function handleGroupBy() {
72
+ if (tableState.groupByKey === columnId) {
73
+ setGroupByKey(null);
74
+ } else {
75
+ setGroupByKey(columnId);
76
+ }
77
+ menuOpen = false;
78
+ }
79
+
80
+ function handleToggleSummary() {
81
+ const hasSummary = tableState.summaryConfigs.some((c) => c.column === columnId);
82
+
83
+ if (hasSummary) {
84
+ removeSummaryConfig(columnId);
85
+ } else {
86
+ // Synthetic columns can't reach this handler (gated by isColumnSummable),
87
+ // so `dataType` is reachable on the narrowed data-column shape.
88
+ const dataType = 'dataType' in column ? column.dataType : undefined;
89
+ const summaryType = dataType === 'number' ? 'sum' : 'count';
90
+ addSummaryConfig({
91
+ column: columnId,
92
+ type: summaryType
93
+ });
94
+ }
95
+ menuOpen = false;
96
+ }
97
+
98
+ function handleRemoveFilters() {
99
+ removeFiltersByColumn(columnId);
100
+ menuOpen = false;
101
+ }
102
+
103
+ function handleHideColumn() {
104
+ hideColumn(columnId);
105
+ menuOpen = false;
106
+ }
107
+
108
+ function itemClass(
109
+ intent: 'default' | 'filter' | 'group' | 'summary' | 'danger',
110
+ active = false
111
+ ) {
112
+ return headerMenuItemVariants({ intent, active });
113
+ }
114
+
115
+ function isColumnSummable(col: Column): boolean {
116
+ // Synthetic columns have no source value — they cannot be summed regardless of name.
117
+ if (col.accessor === undefined) return false;
118
+ // After the accessor check, TS narrows `col` to the derivable shapes.
119
+ const dataCol = col as Exclude<Column, { accessor?: never }>;
120
+ if (dataCol.summable !== undefined) return dataCol.summable === true;
121
+ return (
122
+ dataCol.dataType === 'number' ||
123
+ /^(age|salary|price|amount|count|number|projectsCompleted|rating|score)$/i.test(
124
+ resolveColumnId(col)
125
+ )
126
+ );
127
+ }
128
+
129
+ let isSorted = $derived(tableState.sortColumn === columnId);
130
+ let isGrouped = $derived(tableState.groupByKey === columnId);
131
+ let hasSummary = $derived(tableState.summaryConfigs.some((c) => c.column === columnId));
132
+ let hasFilter = $derived(tableState.activeFilters.some((f) => f.column === columnId));
133
+
134
+ const hiddenColumns = $derived.by(() =>
135
+ tableContext.allColumns.filter((col) => tableContext.hiddenColumnKeys.has(resolveColumnId(col)))
136
+ );
137
+
138
+ function handleShowColumn(key: string) {
139
+ showColumn(key);
140
+ menuOpen = false;
141
+ }
142
+ </script>
143
+
144
+ <div class={styles.container()} data-testid={`header-menu-${columnId}`}>
145
+ <Popover bind:open={menuOpen} placement="bottom-start">
146
+ {#snippet trigger()}
147
+ <Button
148
+ variant="ghost"
149
+ size="sm"
150
+ class={styles.trigger()}
151
+ aria-label="{tt('headerMenu.columnOptions')} {resolveColumnLabel(column)}"
152
+ data-testid={`header-menu-trigger-${columnId}`}
153
+ >
154
+ <MoreVerticalIcon class="h-4 w-4" />
155
+ </Button>
156
+ {/snippet}
157
+
158
+ <div class={styles.menu()}>
159
+ {#if canSort}
160
+ <Button
161
+ variant="ghost"
162
+ size="sm"
163
+ class={itemClass('default', isSorted && tableState.sortDirection === 'asc')}
164
+ onclick={handleSortAsc}
165
+ >
166
+ <ArrowUpIcon class="h-4 w-4" />
167
+ {tt('headerMenu.sortAscending')}
168
+ </Button>
169
+
170
+ <Button
171
+ variant="ghost"
172
+ size="sm"
173
+ class={itemClass('default', isSorted && tableState.sortDirection === 'desc')}
174
+ onclick={handleSortDesc}
175
+ >
176
+ <ArrowDownIcon class="h-4 w-4" />
177
+ {tt('headerMenu.sortDescending')}
178
+ </Button>
179
+
180
+ <div class={styles.separator()}></div>
181
+ {/if}
182
+
183
+ {#if hasFilter}
184
+ <Button variant="ghost" size="sm" class={itemClass('filter')} onclick={handleRemoveFilters}>
185
+ <FilterXIcon class="h-4 w-4" />
186
+ {tt('headerMenu.removeFilter')}
187
+ </Button>
188
+ {/if}
189
+
190
+ {#if canGroup}
191
+ <Button
192
+ variant="ghost"
193
+ size="sm"
194
+ class={itemClass(isGrouped ? 'group' : 'default', isGrouped)}
195
+ onclick={handleGroupBy}
196
+ >
197
+ <UsersIcon class="h-4 w-4" />
198
+ {isGrouped ? tt('headerMenu.removeGrouping') : tt('headerMenu.groupByColumn')}
199
+ </Button>
200
+ {/if}
201
+
202
+ {#if isColumnSummable(column)}
203
+ <Button
204
+ variant="ghost"
205
+ size="sm"
206
+ class={itemClass(hasSummary ? 'summary' : 'default', hasSummary)}
207
+ onclick={handleToggleSummary}
208
+ >
209
+ <CalculatorIcon class="h-4 w-4" />
210
+ {hasSummary ? tt('headerMenu.removeSummary') : tt('headerMenu.addSummary')}
211
+ </Button>
212
+ {/if}
213
+
214
+ <div class={styles.separator()}></div>
215
+ <Button variant="ghost" size="sm" class={itemClass('danger')} onclick={handleHideColumn}>
216
+ <EyeOffIcon class="h-4 w-4" />
217
+ {tt('headerMenu.hideColumn')}
218
+ </Button>
219
+
220
+ {#if hiddenColumns.length > 0}
221
+ <div class={styles.separator()}></div>
222
+ {#each hiddenColumns as col (resolveColumnId(col))}
223
+ <Button
224
+ variant="ghost"
225
+ size="sm"
226
+ class={itemClass('default')}
227
+ onclick={() => handleShowColumn(resolveColumnId(col))}
228
+ >
229
+ <EyeIcon class="h-4 w-4" />
230
+ {tt('headerMenu.showColumn')} "{resolveColumnLabel(col)}"
231
+ </Button>
232
+ {/each}
233
+ {/if}
234
+ </div>
235
+ </Popover>
236
+ </div>
@@ -0,0 +1,8 @@
1
+ import type { Column } from '../types/tableTypes';
2
+ type HeaderMenuProps = {
3
+ column: Column;
4
+ isActive?: boolean;
5
+ };
6
+ declare const HeaderMenu: import("svelte").Component<HeaderMenuProps, {}, "">;
7
+ type HeaderMenu = ReturnType<typeof HeaderMenu>;
8
+ export default HeaderMenu;
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ import { Button } from '@urbicon-ui/blocks';
3
+ import { getTableContext } from '../stores/TableStore.svelte';
4
+ import { useTableI18n } from '../i18n';
5
+
6
+ const tt = useTableI18n();
7
+
8
+ let { class: className = '' }: { class?: string } = $props();
9
+
10
+ const tableContext = getTableContext();
11
+ const counts = $derived(tableContext.liveUpdateCounts);
12
+ const hasPending = $derived(tableContext.hasPendingUpdates);
13
+ </script>
14
+
15
+ {#if hasPending}
16
+ <div
17
+ class="border-primary/20 bg-primary-subtle text-text-primary rounded-contain flex items-center justify-between gap-3 border px-4 py-2.5 text-sm {className}"
18
+ role="status"
19
+ aria-live="polite"
20
+ data-testid="live-update-banner"
21
+ >
22
+ <div class="flex items-center gap-2">
23
+ <span class="relative flex h-2.5 w-2.5">
24
+ <span
25
+ class="bg-primary absolute inline-flex h-full w-full animate-ping rounded-full opacity-75"
26
+ ></span>
27
+ <span class="bg-primary relative inline-flex h-2.5 w-2.5 rounded-full"></span>
28
+ </span>
29
+
30
+ <span>
31
+ {#if counts.inserts > 0}
32
+ <strong>{counts.inserts}</strong> {tt('liveUpdates.newItems')}
33
+ {/if}
34
+ {#if counts.inserts > 0 && (counts.updates > 0 || counts.deletes > 0)},
35
+ {/if}
36
+ {#if counts.updates > 0}
37
+ <strong>{counts.updates}</strong> {tt('liveUpdates.updatedItems')}
38
+ {/if}
39
+ {#if counts.updates > 0 && counts.deletes > 0},
40
+ {/if}
41
+ {#if counts.deletes > 0}
42
+ <strong>{counts.deletes}</strong> {tt('liveUpdates.deletedItems')}
43
+ {/if}
44
+ </span>
45
+ </div>
46
+
47
+ <div class="flex items-center gap-2">
48
+ <Button
49
+ size="sm"
50
+ variant="ghost"
51
+ onclick={() => tableContext.dismissAllUpdates()}
52
+ data-testid="live-update-dismiss"
53
+ >
54
+ {tt('liveUpdates.dismiss')}
55
+ </Button>
56
+ <Button
57
+ size="sm"
58
+ intent="primary"
59
+ onclick={() => tableContext.applyAllUpdates()}
60
+ data-testid="live-update-apply"
61
+ >
62
+ {tt('liveUpdates.apply')}
63
+ </Button>
64
+ </div>
65
+ </div>
66
+ {/if}
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ class?: string;
3
+ };
4
+ declare const LiveUpdateBanner: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type LiveUpdateBanner = ReturnType<typeof LiveUpdateBanner>;
6
+ export default LiveUpdateBanner;
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import { splitSearchSegments } from '../utils';
3
+
4
+ let {
5
+ text = '',
6
+ searchTerm = '',
7
+ highlightClass = 'table-search-highlight'
8
+ }: {
9
+ text: string;
10
+ searchTerm?: string;
11
+ highlightClass?: string;
12
+ } = $props();
13
+
14
+ let segments = $derived(splitSearchSegments(text, searchTerm));
15
+ </script>
16
+
17
+ <span class="inline"
18
+ >{#each segments as segment, i (i)}{#if segment.highlighted}<mark class={highlightClass}
19
+ >{segment.text}</mark
20
+ >{:else}{segment.text}{/if}{/each}</span
21
+ >
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ text: string;
3
+ searchTerm?: string;
4
+ highlightClass?: string;
5
+ };
6
+ declare const SearchHighlight: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type SearchHighlight = ReturnType<typeof SearchHighlight>;
8
+ export default SearchHighlight;
@@ -0,0 +1,104 @@
1
+ <script lang="ts">
2
+ import { getTableContext, type TableAction, useTableI18n } from '../..';
3
+ import type { SummaryConfig } from '../../stores/TableStore.svelte';
4
+ import { findColumnById, resolveColumnLabel } from '../../utils';
5
+ import { Badge } from '@urbicon-ui/blocks';
6
+
7
+ const tt = useTableI18n();
8
+
9
+ interface ChipItem {
10
+ type: TableAction;
11
+ id: string;
12
+ content: string;
13
+ onRemove: () => void;
14
+ }
15
+
16
+ let { class: className = '' } = $props();
17
+
18
+ const tableContext = getTableContext();
19
+ const { state: tableState, removeFilter, setGroupByKey, removeSummaryConfig } = tableContext;
20
+
21
+ function getColumnTitle(id: string): string {
22
+ // Raw-id fallback for persisted state that references a removed column.
23
+ const column = findColumnById(tableState.columns, id);
24
+ return column ? resolveColumnLabel(column) : id;
25
+ }
26
+
27
+ // Aggregation codes (avg/min/max) differ from their translation keys
28
+ // (average/minimum/maximum). Map explicitly — interpolating the raw code into
29
+ // `summary.types.${type}` produced missing keys ("summary.types.avg") for
30
+ // avg/min/max. The map is type-checked against the translation keys and the
31
+ // SummaryConfig union, so a drift on either side is now a compile error.
32
+ const SUMMARY_TYPE_KEY = {
33
+ sum: 'summary.types.sum',
34
+ avg: 'summary.types.average',
35
+ count: 'summary.types.count',
36
+ min: 'summary.types.minimum',
37
+ max: 'summary.types.maximum'
38
+ } as const satisfies Record<SummaryConfig['type'], string>;
39
+
40
+ function getSummaryLabel(config: SummaryConfig): string {
41
+ const columnTitle = getColumnTitle(config.column);
42
+ const typeLabel = tt(SUMMARY_TYPE_KEY[config.type]);
43
+ return `${typeLabel}: ${columnTitle}`;
44
+ }
45
+
46
+ const allChips = $derived.by((): ChipItem[] => {
47
+ const chips: ChipItem[] = [];
48
+
49
+ // Filter Chips
50
+ tableState.activeFilters.forEach((filter, index) => {
51
+ chips.push({
52
+ type: 'filter',
53
+ id: `filter-${index}`,
54
+ content: `${getColumnTitle(filter.column)}: ${filter.value}`,
55
+ onRemove: () => removeFilter(index)
56
+ });
57
+ });
58
+
59
+ if (tableState.groupByKey) {
60
+ chips.push({
61
+ type: 'group',
62
+ id: 'group',
63
+ content: getColumnTitle(tableState.groupByKey),
64
+ onRemove: () => setGroupByKey(null)
65
+ });
66
+ }
67
+
68
+ tableState.summaryConfigs.forEach((config, index) => {
69
+ chips.push({
70
+ type: 'summary',
71
+ id: `summary-${index}`,
72
+ content: getSummaryLabel(config),
73
+ onRemove: () => removeSummaryConfig(config.column)
74
+ });
75
+ });
76
+
77
+ return chips;
78
+ });
79
+
80
+ const hasChips = $derived(allChips.length > 0);
81
+
82
+ const CHIP_COLORS: Record<string, string> = {
83
+ filter: 'bg-filter-subtle text-filter border-filter/30',
84
+ group: 'bg-group-subtle text-group border-group/30',
85
+ summary: 'bg-summary-subtle text-summary border-summary/30'
86
+ };
87
+ </script>
88
+
89
+ {#if hasChips}
90
+ <div class="flex flex-wrap gap-1.5 {className}">
91
+ {#each allChips as chip (chip.id)}
92
+ <Badge
93
+ removable={true}
94
+ onRemove={chip.onRemove}
95
+ variant="outlined"
96
+ size="sm"
97
+ class={CHIP_COLORS[chip.type] ?? ''}
98
+ aria-label={tt('aria.removeItem', { content: chip.content })}
99
+ >
100
+ {chip.content}
101
+ </Badge>
102
+ {/each}
103
+ </div>
104
+ {/if}
@@ -0,0 +1,5 @@
1
+ declare const ChipsField: import("svelte").Component<{
2
+ class?: string;
3
+ }, {}, "">;
4
+ type ChipsField = ReturnType<typeof ChipsField>;
5
+ export default ChipsField;
@@ -0,0 +1,84 @@
1
+ <script lang="ts">
2
+ import { getTableContext, useTableI18n } from '../..';
3
+ import { resolveColumnId, resolveColumnLabel } from '../../utils';
4
+ import {
5
+ Badge,
6
+ Button,
7
+ Select,
8
+ Tooltip,
9
+ resolveIcon,
10
+ EyeIcon as EyeIconDefault
11
+ } from '@urbicon-ui/blocks';
12
+
13
+ const tt = useTableI18n();
14
+
15
+ const EyeIcon = resolveIcon('eye', EyeIconDefault);
16
+
17
+ const tableContext = getTableContext();
18
+ const { toggleColumnVisibility } = tableContext;
19
+
20
+ let menuOpen = $state(false);
21
+
22
+ const hiddenCount = $derived(tableContext.hiddenColumnKeys.size);
23
+
24
+ const columnItems = $derived.by(() =>
25
+ tableContext.allColumns.map((col) => ({
26
+ label: resolveColumnLabel(col),
27
+ value: resolveColumnId(col)
28
+ }))
29
+ );
30
+
31
+ const visibleValues = $derived.by(() =>
32
+ tableContext.allColumns
33
+ .filter((col) => !tableContext.hiddenColumnKeys.has(resolveColumnId(col)))
34
+ .map((col) => resolveColumnId(col))
35
+ );
36
+
37
+ function handleValueChange(values: string | string[] | null) {
38
+ if (!Array.isArray(values)) return;
39
+ const newVisible = new Set(values);
40
+ for (const col of tableContext.allColumns) {
41
+ const id = resolveColumnId(col);
42
+ const isCurrentlyHidden = tableContext.hiddenColumnKeys.has(id);
43
+ const shouldBeVisible = newVisible.has(id);
44
+ if (isCurrentlyHidden && shouldBeVisible) {
45
+ toggleColumnVisibility(id);
46
+ } else if (!isCurrentlyHidden && !shouldBeVisible) {
47
+ toggleColumnVisibility(id);
48
+ }
49
+ }
50
+ }
51
+ </script>
52
+
53
+ {#snippet customTrigger(_selected: unknown[], _open: boolean, _clear: () => void)}
54
+ <Tooltip label={tt('columns.visibility')}>
55
+ <Button
56
+ variant="ghost"
57
+ intent="neutral"
58
+ size="sm"
59
+ active={hiddenCount > 0}
60
+ aria-expanded={menuOpen}
61
+ aria-haspopup="listbox"
62
+ onclick={() => (menuOpen = !menuOpen)}
63
+ >
64
+ <EyeIcon class="h-4 w-4" />
65
+ {#if hiddenCount > 0}
66
+ <Badge variant="filled" intent="primary" size="xs" counter class="ml-1">
67
+ {hiddenCount}
68
+ </Badge>
69
+ {/if}
70
+ </Button>
71
+ </Tooltip>
72
+ {/snippet}
73
+
74
+ <Select
75
+ options={columnItems}
76
+ multiple
77
+ value={visibleValues}
78
+ bind:open={menuOpen}
79
+ onValueChange={handleValueChange}
80
+ size="sm"
81
+ syncWidth={false}
82
+ selectionIndicator="checkmark"
83
+ {customTrigger}
84
+ />
@@ -0,0 +1,3 @@
1
+ declare const ColumnVisibilityMenu: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ColumnVisibilityMenu = ReturnType<typeof ColumnVisibilityMenu>;
3
+ export default ColumnVisibilityMenu;