@streamscloud/kit 0.77.0 → 0.78.1
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/dist/core/utils/color-helper.d.ts +10 -0
- package/dist/core/utils/color-helper.js +25 -1
- package/dist/ui/color-picker/cmp.color-picker.svelte +1 -1
- package/dist/ui/table/cmp.table-draggable.svelte +47 -335
- package/dist/ui/table/cmp.table-draggable.svelte.d.ts +21 -18
- package/dist/ui/table/cmp.table.svelte +14 -286
- package/dist/ui/table/cmp.table.svelte.d.ts +23 -17
- package/dist/ui/table/table-core.svelte +407 -0
- package/dist/ui/table/table-core.svelte.d.ts +88 -0
- package/dist/ui/table/table-localization.d.ts +1 -0
- package/dist/ui/table/table-localization.js +7 -0
- package/dist/ui/table/table-model.svelte.js +9 -1
- package/dist/ui/table/types.d.ts +6 -2
- package/package.json +1 -1
- package/dist/ui/table/_table-styles.scss +0 -187
- package/dist/ui/table/table-view.svelte.d.ts +0 -15
- package/dist/ui/table/table-view.svelte.js +0 -54
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
<script lang="ts" module>export const TABLE_ROW_FLIP_DURATION_MS = 200;
|
|
2
|
+
/**
|
|
3
|
+
* Restores the look of a dragged row's clone, which the drag library reparents to `<body>` — out of reach of the table's private vars and layout.
|
|
4
|
+
* `rowIndex` is the dragged row's position among the `<tbody>` rows.
|
|
5
|
+
*/
|
|
6
|
+
export const styleTableRowClone = (clone, tbody, rowIndex) => {
|
|
7
|
+
const table = tbody.closest('table');
|
|
8
|
+
const sourceRow = tbody.children[rowIndex];
|
|
9
|
+
if (!table || !sourceRow) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const sourceCells = sourceRow.querySelectorAll('td');
|
|
13
|
+
const cloneCells = clone.querySelectorAll('td');
|
|
14
|
+
if (sourceCells.length !== cloneCells.length) {
|
|
15
|
+
console.error('DraggableTable: dragged row column count does not match its source row');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
for (let i = 0, length = sourceCells.length; i < length; i++) {
|
|
19
|
+
cloneCells[i].style.width = `${sourceCells[i].clientWidth}px`;
|
|
20
|
+
cloneCells[i].style.height = `${sourceCells[i].clientHeight}px`;
|
|
21
|
+
}
|
|
22
|
+
const tableStyle = window.getComputedStyle(table);
|
|
23
|
+
for (const name of ['--_table--cell--font-size', '--_table--cell--padding-inline', '--_table--cell--padding-block', '--_table--cell--vertical-align']) {
|
|
24
|
+
clone.style.setProperty(name, tableStyle.getPropertyValue(name));
|
|
25
|
+
}
|
|
26
|
+
clone.style.setProperty('--_table--border-color', 'transparent');
|
|
27
|
+
clone.style.backgroundColor = tableStyle.getPropertyValue('--_table--row--alternative-color');
|
|
28
|
+
clone.style.outline = 'none';
|
|
29
|
+
};
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<script lang="ts" generics="T extends {id: string}">import { DynamicComponent } from '../dynamic-component';
|
|
33
|
+
import { EmptyState } from '../empty-state';
|
|
34
|
+
import { Icon } from '../icon';
|
|
35
|
+
import { TableCell, TableHideableCell } from './table-cells';
|
|
36
|
+
import { TableLocalization } from './table-localization';
|
|
37
|
+
import IconReorderDotsHorizontal from '@fluentui/svg-icons/icons/re_order_dots_horizontal_20_regular.svg?raw';
|
|
38
|
+
import { untrack } from 'svelte';
|
|
39
|
+
import { flip } from 'svelte/animate';
|
|
40
|
+
let { model, rows, showAlternativeView = false, noItemsPlaceholderDelay, reorder, on, alternativeView, noItemsPlaceholder, toolbar, groupHeader } = $props();
|
|
41
|
+
const localization = new TableLocalization();
|
|
42
|
+
$effect(() => {
|
|
43
|
+
const selection = model.selection;
|
|
44
|
+
return untrack(() => {
|
|
45
|
+
const subscriptions = [
|
|
46
|
+
selection.subscribeOnChange((values) => on?.selectionChanged?.(values)),
|
|
47
|
+
selection.subscribeOnAdd((values) => on?.itemsSelected?.(values)),
|
|
48
|
+
selection.subscribeOnRemove((values) => on?.itemsDeselected?.(values))
|
|
49
|
+
];
|
|
50
|
+
return () => subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
let showNoItemsPlaceholder = $state(false);
|
|
54
|
+
let placeholderTimeout = 0;
|
|
55
|
+
$effect(() => {
|
|
56
|
+
if (model.items.length || noItemsPlaceholder === false) {
|
|
57
|
+
clearTimeout(placeholderTimeout);
|
|
58
|
+
showNoItemsPlaceholder = false;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const delay = noItemsPlaceholderDelay ?? 0;
|
|
62
|
+
if (delay <= 0) {
|
|
63
|
+
clearTimeout(placeholderTimeout);
|
|
64
|
+
showNoItemsPlaceholder = true;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
placeholderTimeout = window.setTimeout(() => {
|
|
68
|
+
showNoItemsPlaceholder = !model.items.length;
|
|
69
|
+
}, delay);
|
|
70
|
+
return () => clearTimeout(placeholderTimeout);
|
|
71
|
+
});
|
|
72
|
+
const visibleColspan = $derived(model.columns.filter((column) => !column.hidden).length + (reorder ? 1 : 0));
|
|
73
|
+
const itemCount = $derived(rows.filter((row) => row.kind === 'item').length);
|
|
74
|
+
const showColumnHeader = $derived(!toolbar || model.items.length > 0);
|
|
75
|
+
const columnCellStyle = (column) => {
|
|
76
|
+
const { inline, block } = column.cellPadding;
|
|
77
|
+
return [
|
|
78
|
+
column.cssWidth ? `width: ${column.cssWidth};` : 'width: auto;',
|
|
79
|
+
column.minCssWidth ? `min-width: ${column.minCssWidth};` : '',
|
|
80
|
+
inline ? `--_table--cell--padding-inline: ${inline};` : '',
|
|
81
|
+
block ? `--_table--cell--padding-block: ${block};` : ''
|
|
82
|
+
]
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.join(' ');
|
|
85
|
+
};
|
|
86
|
+
const rowState = (row) => {
|
|
87
|
+
const selected = model.selection.has(row.item.id);
|
|
88
|
+
const highlighted = model.highlightedItemIds.includes(row.item.id);
|
|
89
|
+
return {
|
|
90
|
+
selected,
|
|
91
|
+
highlighted,
|
|
92
|
+
zebra: model.styles?.zebra === true && row.index % 2 === 1 && !selected && !highlighted,
|
|
93
|
+
flushBottom: model.styles?.variant === 'card' && row.index === itemCount - 1
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
{#snippet noItemsContent()}
|
|
99
|
+
{#if noItemsPlaceholder}
|
|
100
|
+
{@render noItemsPlaceholder()}
|
|
101
|
+
{:else}
|
|
102
|
+
<EmptyState graphic="zero-content" title={localization.noItemsFoundTitle} description={localization.noItemsFoundBody} />
|
|
103
|
+
{/if}
|
|
104
|
+
{/snippet}
|
|
105
|
+
|
|
106
|
+
<div
|
|
107
|
+
class="table-container table-container--{model.styles?.size ?? 'md'} table-container--{model.styles?.variant ?? 'plain'}"
|
|
108
|
+
class:table-container--with-toolbar={!!toolbar}>
|
|
109
|
+
{#if toolbar}
|
|
110
|
+
<div class="table-container__toolbar">{@render toolbar()}</div>
|
|
111
|
+
{/if}
|
|
112
|
+
<div class="table-container__scroll">
|
|
113
|
+
{#if alternativeView && showAlternativeView}
|
|
114
|
+
{#if showNoItemsPlaceholder}
|
|
115
|
+
<div class="table-container__placeholder">{@render noItemsContent()}</div>
|
|
116
|
+
{:else}
|
|
117
|
+
{@render alternativeView()}
|
|
118
|
+
{/if}
|
|
119
|
+
{:else}
|
|
120
|
+
<table class="table" class:table--sticky-header={model.styles?.stickyHeader === true || !!toolbar} class:table--fixed={model.styles?.layout === 'fixed'}>
|
|
121
|
+
{#if showColumnHeader}
|
|
122
|
+
<thead class="table__head">
|
|
123
|
+
<tr>
|
|
124
|
+
{#if reorder}
|
|
125
|
+
<th class="table__th table__drag-handle"> </th>
|
|
126
|
+
{/if}
|
|
127
|
+
{#each model.columns as column (column)}
|
|
128
|
+
<TableHideableCell column={column}>
|
|
129
|
+
<th class="table__th" class:table__th--centered={column.centered} style={columnCellStyle(column)} title={column.title}>
|
|
130
|
+
<DynamicComponent model={column.headerView} />
|
|
131
|
+
</th>
|
|
132
|
+
</TableHideableCell>
|
|
133
|
+
{/each}
|
|
134
|
+
</tr>
|
|
135
|
+
</thead>
|
|
136
|
+
{/if}
|
|
137
|
+
<tbody {@attach reorder?.attachment}>
|
|
138
|
+
{#if showNoItemsPlaceholder}
|
|
139
|
+
<tr>
|
|
140
|
+
<td colspan={visibleColspan}>
|
|
141
|
+
<div class="table-container__placeholder">{@render noItemsContent()}</div>
|
|
142
|
+
</td>
|
|
143
|
+
</tr>
|
|
144
|
+
{:else}
|
|
145
|
+
{#each rows as row (row.kind === 'group' ? 'g:' + row.key : row.key)}
|
|
146
|
+
{@const state = row.kind === 'item' ? rowState(row) : null}
|
|
147
|
+
<tr
|
|
148
|
+
animate:flip={{ duration: TABLE_ROW_FLIP_DURATION_MS }}
|
|
149
|
+
id={row.kind === 'item' ? row.item.id : undefined}
|
|
150
|
+
class:table__tr={row.kind === 'item'}
|
|
151
|
+
class:table__tr--selected={state?.selected}
|
|
152
|
+
class:table__tr--highlighted={state?.highlighted}
|
|
153
|
+
class:table__tr--zebra={state?.zebra}>
|
|
154
|
+
{#if row.kind === 'group'}
|
|
155
|
+
<td class="table__group-header" colspan={visibleColspan}>
|
|
156
|
+
{#if groupHeader}
|
|
157
|
+
{@render groupHeader({ key: row.key, label: row.label, count: row.count })}
|
|
158
|
+
{:else}
|
|
159
|
+
{row.label}
|
|
160
|
+
{/if}
|
|
161
|
+
</td>
|
|
162
|
+
{:else}
|
|
163
|
+
{#if reorder}
|
|
164
|
+
<td class="table__td table__drag-handle" class:table__td--flush={state?.flushBottom}>
|
|
165
|
+
<div class="table__drag-handle-grip" {@attach reorder.handleAttachment}>
|
|
166
|
+
<Icon src={IconReorderDotsHorizontal} />
|
|
167
|
+
</div>
|
|
168
|
+
</td>
|
|
169
|
+
{/if}
|
|
170
|
+
{#each model.columns as column (column)}
|
|
171
|
+
<TableHideableCell column={column}>
|
|
172
|
+
<td
|
|
173
|
+
class="table__td"
|
|
174
|
+
class:table__td--centered={column.centered}
|
|
175
|
+
class:table__td--clickable={!!column.onClick}
|
|
176
|
+
class:table__td--flush={state?.flushBottom}
|
|
177
|
+
style={columnCellStyle(column)}
|
|
178
|
+
onclick={() => column.onClick && column.onClick(row.item)}>
|
|
179
|
+
<div class="table__cell" class:table__cell--reveal-on-row-hover={column.revealOnRowHover}>
|
|
180
|
+
<TableCell model={model} item={row.item} column={column} itemIndex={row.index} on={{ itemsReordered: on?.itemsReordered }} />
|
|
181
|
+
</div>
|
|
182
|
+
</td>
|
|
183
|
+
</TableHideableCell>
|
|
184
|
+
{/each}
|
|
185
|
+
{/if}
|
|
186
|
+
</tr>
|
|
187
|
+
{/each}
|
|
188
|
+
{/if}
|
|
189
|
+
</tbody>
|
|
190
|
+
</table>
|
|
191
|
+
{/if}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<!--
|
|
196
|
+
@component
|
|
197
|
+
Internal rendering core shared by `Table` and `DraggableTable`: container, header, rows and cells. Each public wrapper owns its
|
|
198
|
+
props and hands the core finished display rows, plus a group-header snippet (`Table`) or the row reorder wiring (`DraggableTable`).
|
|
199
|
+
|
|
200
|
+
Not exported from `index.ts` — for kit-internal use only.
|
|
201
|
+
-->
|
|
202
|
+
|
|
203
|
+
<style>.table-container {
|
|
204
|
+
--_table--row--height: var(--sc-kit--table--row--height, var(--_table--size-row-height));
|
|
205
|
+
--_table--cell--font-size: var(--sc-kit--table--cell--font-size, var(--_table--size-cell-font-size));
|
|
206
|
+
--_table--cell--padding-inline: var(--sc-kit--table--cell--padding-inline, var(--_table--size-cell-padding));
|
|
207
|
+
--_table--cell--padding-block: var(--sc-kit--table--cell--padding-block, 0);
|
|
208
|
+
--_table--cell--vertical-align: var(--sc-kit--table--cell--vertical-align, middle);
|
|
209
|
+
--_table--head--height: var(--sc-kit--table--head--height, var(--_table--size-head-height, var(--_table--row--height)));
|
|
210
|
+
--_table--head--background-default: var(--sc-kit--color--bg--field-alt);
|
|
211
|
+
--_table--head--background-color: var(--sc-kit--table--head--background-color, var(--_table--head--background-default));
|
|
212
|
+
--_table--toolbar--background-color: var(--sc-kit--table--toolbar--background-color, var(--sc-kit--color--bg--field-alt));
|
|
213
|
+
--_table--toolbar--padding-block: var(--sc-kit--table--toolbar--padding-block, var(--sc-kit--space--3));
|
|
214
|
+
--_table--toolbar--padding-inline: var(--sc-kit--table--toolbar--padding-inline, var(--_table--cell--padding-inline));
|
|
215
|
+
--_table--head--color: var(--sc-kit--table--head--color, var(--sc-kit--color--text--secondary));
|
|
216
|
+
--_table--head--font-weight: var(--sc-kit--table--head--font-weight, var(--sc-kit--font-weight--semibold));
|
|
217
|
+
--_table--head--letter-spacing: var(--sc-kit--table--head--letter-spacing, normal);
|
|
218
|
+
--_table--head--text-transform: var(--sc-kit--table--head--text-transform, none);
|
|
219
|
+
--_table--border-color: var(--sc-kit--table--border-color, var(--sc-kit--color--border));
|
|
220
|
+
--_table--row--color: var(--sc-kit--table--row--color, transparent);
|
|
221
|
+
--_table--row--alternative-color: var(--sc-kit--table--row--alternative-color, var(--sc-kit--color--bg--hover));
|
|
222
|
+
--_table--zebra--color: var(--sc-kit--table--zebra--color, light-dark(#fcfcfd, #232323));
|
|
223
|
+
--_table--selected--color: var(--sc-kit--table--selected--color, var(--sc-kit--color--accent--softer));
|
|
224
|
+
--_table--selected--color-hover: var(--sc-kit--table--selected--color--hover, var(--sc-kit--color--accent--soft));
|
|
225
|
+
--_table--card--background: var(--sc-kit--table--card--background, var(--sc-kit--color--bg--panel));
|
|
226
|
+
--_table--card--border-color: var(--sc-kit--table--card--border-color, var(--sc-kit--color--border));
|
|
227
|
+
--_table--card--border-radius: var(--sc-kit--table--card--border-radius, var(--sc-kit--radius--lg));
|
|
228
|
+
--_table--card--padding-bottom: var(--sc-kit--table--card--padding-bottom, var(--sc-kit--space--4));
|
|
229
|
+
--_table--min-width: var(--sc-kit--table--min-width, auto);
|
|
230
|
+
--_table--body--background-color: var(--sc-kit--color--bg--panel);
|
|
231
|
+
--_table--scroll--padding-bottom: 0;
|
|
232
|
+
display: flex;
|
|
233
|
+
flex-direction: column;
|
|
234
|
+
height: 100%;
|
|
235
|
+
}
|
|
236
|
+
.table-container--md {
|
|
237
|
+
--_table--size-row-height: 2.75rem;
|
|
238
|
+
--_table--size-head-height: 2.5rem;
|
|
239
|
+
--_table--size-cell-font-size: var(--sc-kit--font-size--md);
|
|
240
|
+
--_table--size-cell-padding: var(--sc-kit--space--4);
|
|
241
|
+
}
|
|
242
|
+
.table-container--sm {
|
|
243
|
+
--_table--size-row-height: 2.25rem;
|
|
244
|
+
--_table--size-head-height: 2rem;
|
|
245
|
+
--_table--size-cell-font-size: var(--sc-kit--font-size--sm);
|
|
246
|
+
--_table--size-cell-padding: var(--sc-kit--space--3);
|
|
247
|
+
}
|
|
248
|
+
.table-container--card {
|
|
249
|
+
--_table--body--background-color: var(--_table--card--background);
|
|
250
|
+
--_table--scroll--padding-bottom: var(--_table--card--padding-bottom);
|
|
251
|
+
background: var(--_table--card--background);
|
|
252
|
+
border: 1px solid var(--_table--card--border-color);
|
|
253
|
+
border-radius: var(--_table--card--border-radius);
|
|
254
|
+
overflow: hidden;
|
|
255
|
+
}
|
|
256
|
+
.table-container--with-toolbar {
|
|
257
|
+
--_table--head--background-default: var(--_table--body--background-color);
|
|
258
|
+
}
|
|
259
|
+
.table-container__toolbar {
|
|
260
|
+
flex: none;
|
|
261
|
+
padding: var(--_table--toolbar--padding-block) var(--_table--toolbar--padding-inline);
|
|
262
|
+
background: var(--_table--toolbar--background-color);
|
|
263
|
+
border-bottom: 1px solid var(--_table--border-color);
|
|
264
|
+
}
|
|
265
|
+
.table-container__scroll {
|
|
266
|
+
display: flex;
|
|
267
|
+
flex: 1 1 auto;
|
|
268
|
+
min-height: 0;
|
|
269
|
+
padding-bottom: var(--_table--scroll--padding-bottom);
|
|
270
|
+
overflow: auto;
|
|
271
|
+
--_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
|
|
272
|
+
--_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
|
|
273
|
+
}
|
|
274
|
+
.table-container__scroll::-webkit-scrollbar {
|
|
275
|
+
width: 6px;
|
|
276
|
+
height: 6px;
|
|
277
|
+
}
|
|
278
|
+
.table-container__scroll::-webkit-scrollbar-track {
|
|
279
|
+
background: var(--_cross-browser-scrollbar--track-color);
|
|
280
|
+
border-radius: 100vw;
|
|
281
|
+
}
|
|
282
|
+
.table-container__scroll::-webkit-scrollbar-thumb {
|
|
283
|
+
background: var(--_cross-browser-scrollbar--thumb-color);
|
|
284
|
+
border-radius: 100vw;
|
|
285
|
+
}
|
|
286
|
+
@supports (scrollbar-color: transparent transparent) {
|
|
287
|
+
.table-container__scroll {
|
|
288
|
+
scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
|
|
289
|
+
scrollbar-width: thin;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
.table-container__placeholder {
|
|
293
|
+
width: 100%;
|
|
294
|
+
display: flex;
|
|
295
|
+
align-items: center;
|
|
296
|
+
justify-content: center;
|
|
297
|
+
font-size: 80%;
|
|
298
|
+
user-select: none;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.table {
|
|
302
|
+
width: 100%;
|
|
303
|
+
min-width: var(--_table--min-width);
|
|
304
|
+
table-layout: auto;
|
|
305
|
+
height: fit-content;
|
|
306
|
+
border-collapse: separate;
|
|
307
|
+
border-spacing: 0;
|
|
308
|
+
}
|
|
309
|
+
.table--fixed {
|
|
310
|
+
table-layout: fixed;
|
|
311
|
+
}
|
|
312
|
+
.table__head {
|
|
313
|
+
height: var(--_table--head--height);
|
|
314
|
+
max-height: var(--_table--head--height);
|
|
315
|
+
background: var(--_table--head--background-color);
|
|
316
|
+
color: var(--_table--head--color);
|
|
317
|
+
font-size: var(--sc-kit--table--head--font-size, var(--sc-kit--font-size--sm));
|
|
318
|
+
letter-spacing: var(--_table--head--letter-spacing);
|
|
319
|
+
text-transform: var(--_table--head--text-transform);
|
|
320
|
+
}
|
|
321
|
+
.table__th {
|
|
322
|
+
padding: 0 var(--_table--cell--padding-inline);
|
|
323
|
+
font-weight: var(--_table--head--font-weight);
|
|
324
|
+
text-align: left;
|
|
325
|
+
vertical-align: middle;
|
|
326
|
+
border-bottom: 1px solid var(--_table--border-color);
|
|
327
|
+
-webkit-user-drag: none;
|
|
328
|
+
user-select: none;
|
|
329
|
+
}
|
|
330
|
+
.table__th :global([contenteditable]) {
|
|
331
|
+
user-select: text;
|
|
332
|
+
}
|
|
333
|
+
.table__th--centered {
|
|
334
|
+
--_--table-header--justify-content: center;
|
|
335
|
+
}
|
|
336
|
+
.table__tr {
|
|
337
|
+
height: var(--_table--row--height);
|
|
338
|
+
max-height: var(--_table--row--height);
|
|
339
|
+
background: var(--_table--row--color);
|
|
340
|
+
}
|
|
341
|
+
.table__tr--zebra {
|
|
342
|
+
background: var(--_table--zebra--color);
|
|
343
|
+
}
|
|
344
|
+
.table__tr:hover, .table__tr--highlighted {
|
|
345
|
+
background: var(--_table--row--alternative-color);
|
|
346
|
+
}
|
|
347
|
+
.table__tr:hover, .table__tr:focus-within {
|
|
348
|
+
--_table--row-reveal--opacity: 1;
|
|
349
|
+
}
|
|
350
|
+
@media (any-hover: none) {
|
|
351
|
+
.table__tr {
|
|
352
|
+
--_table--row-reveal--opacity: 1;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
.table__tr--selected {
|
|
356
|
+
background: var(--_table--selected--color);
|
|
357
|
+
}
|
|
358
|
+
.table__tr--selected:hover {
|
|
359
|
+
background: var(--_table--selected--color-hover);
|
|
360
|
+
}
|
|
361
|
+
.table__td {
|
|
362
|
+
padding: 0;
|
|
363
|
+
border-bottom: 1px solid var(--_table--border-color);
|
|
364
|
+
font-size: var(--_table--cell--font-size);
|
|
365
|
+
color: var(--sc-kit--color--text--primary);
|
|
366
|
+
vertical-align: var(--_table--cell--vertical-align);
|
|
367
|
+
}
|
|
368
|
+
.table__td--centered {
|
|
369
|
+
text-align: center;
|
|
370
|
+
}
|
|
371
|
+
.table__td--clickable {
|
|
372
|
+
cursor: pointer;
|
|
373
|
+
}
|
|
374
|
+
.table__td--flush {
|
|
375
|
+
border-bottom: none;
|
|
376
|
+
}
|
|
377
|
+
.table__cell {
|
|
378
|
+
padding: var(--_table--cell--padding-block) var(--_table--cell--padding-inline);
|
|
379
|
+
}
|
|
380
|
+
.table__cell--reveal-on-row-hover {
|
|
381
|
+
opacity: var(--_table--row-reveal--opacity, 0);
|
|
382
|
+
transition: opacity var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
383
|
+
}
|
|
384
|
+
.table__group-header {
|
|
385
|
+
padding-block: var(--sc-kit--space--6) var(--sc-kit--space--2);
|
|
386
|
+
padding-inline: var(--_table--cell--padding-inline);
|
|
387
|
+
font-size: var(--sc-kit--font-size--sm);
|
|
388
|
+
font-weight: var(--sc-kit--font-weight--semibold);
|
|
389
|
+
color: var(--sc-kit--color--text--secondary);
|
|
390
|
+
}
|
|
391
|
+
.table__drag-handle {
|
|
392
|
+
width: 3.25rem;
|
|
393
|
+
padding: 0;
|
|
394
|
+
--sc-kit--icon--size: 1.25rem;
|
|
395
|
+
--sc-kit--icon--color: var(--sc-kit--color--text--muted);
|
|
396
|
+
}
|
|
397
|
+
.table__drag-handle-grip {
|
|
398
|
+
display: flex;
|
|
399
|
+
align-items: center;
|
|
400
|
+
justify-content: center;
|
|
401
|
+
min-height: var(--_table--row--height);
|
|
402
|
+
}
|
|
403
|
+
.table--sticky-header thead {
|
|
404
|
+
position: sticky;
|
|
405
|
+
top: 0;
|
|
406
|
+
z-index: 1;
|
|
407
|
+
}</style>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Attachment } from 'svelte/attachments';
|
|
2
|
+
export type TableDisplayRow<T> = {
|
|
3
|
+
kind: 'group';
|
|
4
|
+
key: string;
|
|
5
|
+
label: string;
|
|
6
|
+
count: number;
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'item';
|
|
9
|
+
/** Keyed-each identity of the row. */
|
|
10
|
+
key: unknown;
|
|
11
|
+
item: T;
|
|
12
|
+
/** Position among the item rows — drives zebra striping, the flush last row and move-row bounds. */
|
|
13
|
+
index: number;
|
|
14
|
+
};
|
|
15
|
+
/** Rows reordered by dragging their handle: the core renders the handle column, the caller wires the reordering. */
|
|
16
|
+
export type TableRowReorder = {
|
|
17
|
+
attachment: Attachment<HTMLElement>;
|
|
18
|
+
handleAttachment: Attachment<HTMLElement>;
|
|
19
|
+
};
|
|
20
|
+
export declare const TABLE_ROW_FLIP_DURATION_MS = 200;
|
|
21
|
+
/**
|
|
22
|
+
* Restores the look of a dragged row's clone, which the drag library reparents to `<body>` — out of reach of the table's private vars and layout.
|
|
23
|
+
* `rowIndex` is the dragged row's position among the `<tbody>` rows.
|
|
24
|
+
*/
|
|
25
|
+
export declare const styleTableRowClone: (clone: HTMLElement, tbody: HTMLElement, rowIndex: number) => void;
|
|
26
|
+
import type { TableModel } from './table-model.svelte';
|
|
27
|
+
import { type Snippet } from 'svelte';
|
|
28
|
+
declare function $$render<T extends {
|
|
29
|
+
id: string;
|
|
30
|
+
}>(): {
|
|
31
|
+
props: {
|
|
32
|
+
model: TableModel<T>;
|
|
33
|
+
rows: TableDisplayRow<T>[];
|
|
34
|
+
showAlternativeView?: boolean;
|
|
35
|
+
noItemsPlaceholderDelay?: number;
|
|
36
|
+
reorder?: TableRowReorder;
|
|
37
|
+
on?: {
|
|
38
|
+
selectionChanged?: (e: T[]) => void;
|
|
39
|
+
itemsSelected?: (e: T[]) => void;
|
|
40
|
+
itemsDeselected?: (e: T[]) => void;
|
|
41
|
+
itemsReordered?: (e: T[]) => void;
|
|
42
|
+
};
|
|
43
|
+
alternativeView?: Snippet;
|
|
44
|
+
noItemsPlaceholder?: Snippet | false;
|
|
45
|
+
toolbar?: Snippet;
|
|
46
|
+
/** Custom rendering of a group row; the label is rendered as text without it. */
|
|
47
|
+
groupHeader?: Snippet<[{
|
|
48
|
+
key: string;
|
|
49
|
+
label: string;
|
|
50
|
+
count: number;
|
|
51
|
+
}]>;
|
|
52
|
+
};
|
|
53
|
+
exports: {};
|
|
54
|
+
bindings: "";
|
|
55
|
+
slots: {};
|
|
56
|
+
events: {};
|
|
57
|
+
};
|
|
58
|
+
declare class __sveltets_Render<T extends {
|
|
59
|
+
id: string;
|
|
60
|
+
}> {
|
|
61
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
62
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
63
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
64
|
+
bindings(): "";
|
|
65
|
+
exports(): {};
|
|
66
|
+
}
|
|
67
|
+
interface $$IsomorphicComponent {
|
|
68
|
+
new <T extends {
|
|
69
|
+
id: string;
|
|
70
|
+
}>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
71
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
72
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
73
|
+
<T extends {
|
|
74
|
+
id: string;
|
|
75
|
+
}>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
76
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Internal rendering core shared by `Table` and `DraggableTable`: container, header, rows and cells. Each public wrapper owns its
|
|
80
|
+
* props and hands the core finished display rows, plus a group-header snippet (`Table`) or the row reorder wiring (`DraggableTable`).
|
|
81
|
+
*
|
|
82
|
+
* Not exported from `index.ts` — for kit-internal use only.
|
|
83
|
+
*/
|
|
84
|
+
declare const TableCore: $$IsomorphicComponent;
|
|
85
|
+
type TableCore<T extends {
|
|
86
|
+
id: string;
|
|
87
|
+
}> = InstanceType<typeof TableCore<T>>;
|
|
88
|
+
export default TableCore;
|
|
@@ -6,6 +6,9 @@ export class TableLocalization {
|
|
|
6
6
|
get noItemsFoundBody() {
|
|
7
7
|
return loc.noItemsFoundBody[AppLocale.current];
|
|
8
8
|
}
|
|
9
|
+
get actionsColumnTitle() {
|
|
10
|
+
return loc.actionsColumnTitle[AppLocale.current];
|
|
11
|
+
}
|
|
9
12
|
}
|
|
10
13
|
const loc = {
|
|
11
14
|
noItemsFoundTitle: {
|
|
@@ -15,5 +18,9 @@ const loc = {
|
|
|
15
18
|
noItemsFoundBody: {
|
|
16
19
|
en: 'No data found. Create one to get started',
|
|
17
20
|
no: 'Ingen data funnet. Opprett en for å komme i gang'
|
|
21
|
+
},
|
|
22
|
+
actionsColumnTitle: {
|
|
23
|
+
en: 'Actions',
|
|
24
|
+
no: 'Handlinger'
|
|
18
25
|
}
|
|
19
26
|
};
|
|
@@ -3,6 +3,7 @@ import { createRepository } from '../../core/repository';
|
|
|
3
3
|
import { DynamicComponentModel } from '../dynamic-component';
|
|
4
4
|
import { ResolvedColumn } from './table-column-models.svelte';
|
|
5
5
|
import { TableCheckboxHeader, TableHeader, TableHeaderSortable } from './table-headers';
|
|
6
|
+
import { TableLocalization } from './table-localization';
|
|
6
7
|
import { untrack } from 'svelte';
|
|
7
8
|
export class TableModel {
|
|
8
9
|
_init;
|
|
@@ -121,9 +122,16 @@ export class TableModel {
|
|
|
121
122
|
// Append actions column if needed
|
|
122
123
|
if (this._itemActions.length) {
|
|
123
124
|
const actionsColumn = new ResolvedColumn({ type: 'actions', centered: true, cssWidth: Css.em(75) });
|
|
125
|
+
const localization = new TableLocalization();
|
|
124
126
|
actionsColumn.headerView = new DynamicComponentModel({
|
|
125
127
|
component: TableHeader,
|
|
126
|
-
props: {
|
|
128
|
+
props: {
|
|
129
|
+
column: {
|
|
130
|
+
get title() {
|
|
131
|
+
return localization.actionsColumnTitle;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
127
135
|
});
|
|
128
136
|
result.push(actionsColumn);
|
|
129
137
|
}
|
package/dist/ui/table/types.d.ts
CHANGED
|
@@ -122,7 +122,11 @@ export type TableItemAction<T> = {
|
|
|
122
122
|
visibilityFactory?: (item: T) => boolean;
|
|
123
123
|
};
|
|
124
124
|
export type TableStyles = {
|
|
125
|
-
/**
|
|
125
|
+
/**
|
|
126
|
+
* Stick the column header to the top of the table's own scroll area, so it only takes effect when the table is height-constrained. Off when `styles`
|
|
127
|
+
* is passed without it; a model created without `styles` at all gets `{ stickyHeader: true }`. Always on with a `toolbar`.
|
|
128
|
+
* @default false
|
|
129
|
+
*/
|
|
126
130
|
stickyHeader?: boolean;
|
|
127
131
|
/** Show the per-row actions popover above the row instead of below. @default false */
|
|
128
132
|
actionsPopoverTopDirection?: boolean;
|
|
@@ -135,7 +139,7 @@ export type TableStyles = {
|
|
|
135
139
|
/**
|
|
136
140
|
* Column width algorithm. `'auto'` treats `cssWidth` as a hint and lets content redistribute space. `'fixed'` honours `cssWidth` exactly and wraps or
|
|
137
141
|
* clips content instead of widening the column. Under `'fixed'`, percent columns summing to 100% share what the fixed columns leave in that ratio;
|
|
138
|
-
* `minCssWidth` does not apply — floor the whole table with
|
|
142
|
+
* `minCssWidth` does not apply — floor the whole table with `--sc-kit--table--min-width` instead.
|
|
139
143
|
* @default 'auto'
|
|
140
144
|
*/
|
|
141
145
|
layout?: 'auto' | 'fixed';
|