@streamscloud/kit 0.76.5 → 0.78.0

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 (34) hide show
  1. package/dist/core/utils/color-helper.d.ts +10 -0
  2. package/dist/core/utils/color-helper.js +25 -1
  3. package/dist/ui/form-field/cmp.form-field-validatable.svelte +3 -3
  4. package/dist/ui/form-field/cmp.form-field-validatable.svelte.d.ts +4 -4
  5. package/dist/ui/table/cmp.table-draggable.svelte +56 -304
  6. package/dist/ui/table/cmp.table-draggable.svelte.d.ts +30 -19
  7. package/dist/ui/table/cmp.table.svelte +23 -264
  8. package/dist/ui/table/cmp.table.svelte.d.ts +32 -18
  9. package/dist/ui/table/table-cells/table-checkbox-cell.svelte +0 -1
  10. package/dist/ui/table/table-cells/table-move-row-cell.svelte +0 -2
  11. package/dist/ui/table/table-cells/table-text-cell.svelte +0 -1
  12. package/dist/ui/table/table-column-models.svelte.d.ts +5 -0
  13. package/dist/ui/table/table-column-models.svelte.js +6 -0
  14. package/dist/ui/table/table-core.svelte +407 -0
  15. package/dist/ui/table/table-core.svelte.d.ts +88 -0
  16. package/dist/ui/table/table-headers/table-header-sortable.svelte +1 -4
  17. package/dist/ui/table/table-headers/table-header.svelte +1 -1
  18. package/dist/ui/table/table-localization.d.ts +1 -0
  19. package/dist/ui/table/table-localization.js +7 -0
  20. package/dist/ui/table/table-model.svelte.js +9 -1
  21. package/dist/ui/table/types.d.ts +22 -1
  22. package/dist/ui/validatable/cmp.validatable.svelte +3 -4
  23. package/dist/ui/validatable/cmp.validatable.svelte.d.ts +6 -6
  24. package/dist/ui/validatable/cmp.validation-error.svelte +4 -4
  25. package/dist/ui/validatable/cmp.validation-error.svelte.d.ts +7 -6
  26. package/dist/ui/validatable/field-frame.svelte +25 -15
  27. package/dist/ui/validatable/field-frame.svelte.d.ts +8 -8
  28. package/dist/ui/validatable/index.d.ts +1 -0
  29. package/dist/ui/validatable/types.d.ts +7 -0
  30. package/dist/ui/validatable/types.js +1 -0
  31. package/package.json +1 -1
  32. package/dist/ui/table/_table-styles.scss +0 -158
  33. package/dist/ui/table/table-view.svelte.d.ts +0 -15
  34. package/dist/ui/table/table-view.svelte.js +0 -47
@@ -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">&nbsp;</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;
@@ -18,7 +18,7 @@ const changeSort = (direction) => {
18
18
  </script>
19
19
 
20
20
  <TableHeader column={column}>
21
- <div class="th-sortable" class:th-sortable--centered={column.centered}>
21
+ <div class="th-sortable">
22
22
  {@render children?.()}
23
23
  <span>&nbsp;</span>
24
24
  <Popover position="bottom-start">
@@ -72,9 +72,6 @@ const changeSort = (direction) => {
72
72
  align-items: center;
73
73
  justify-content: flex-start;
74
74
  }
75
- .th-sortable--centered {
76
- justify-content: center;
77
- }
78
75
  .th-sortable__title, .th-sortable__marker {
79
76
  color: inherit;
80
77
  display: inline-block;
@@ -13,6 +13,6 @@ export {};
13
13
  <style>.table-header {
14
14
  display: flex;
15
15
  align-items: center;
16
- justify-content: var(--_table-header--justify-content, flex-start);
16
+ justify-content: var(--_--table-header--justify-content, flex-start);
17
17
  white-space: nowrap;
18
18
  }</style>
@@ -1,4 +1,5 @@
1
1
  export declare class TableLocalization {
2
2
  get noItemsFoundTitle(): string;
3
3
  get noItemsFoundBody(): string;
4
+ get actionsColumnTitle(): string;
4
5
  }
@@ -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: { column: { title: 'Actions' } }
128
+ props: {
129
+ column: {
130
+ get title() {
131
+ return localization.actionsColumnTitle;
132
+ }
133
+ }
134
+ }
127
135
  });
128
136
  result.push(actionsColumn);
129
137
  }
@@ -19,6 +19,16 @@ interface ITableColumnBase {
19
19
  /** Floor the column will not shrink below, taken out of columns whose `cssWidth` is merely a suggestion. Use it on the column that must keep space. */
20
20
  minCssWidth?: string;
21
21
  centered?: boolean;
22
+ /** Hide the cell until its row is hovered or holds focus. Always visible on touch-only devices. @default true for `move-row`, false otherwise */
23
+ revealOnRowHover?: boolean;
24
+ /**
25
+ * Overrides the table-wide cell padding for this column. `inline` applies to the header and body cells, `block` to body cells. Under the kit's
26
+ * border-box sizing `cssWidth` includes this padding.
27
+ */
28
+ cellPadding?: {
29
+ inline?: string;
30
+ block?: string;
31
+ };
22
32
  }
23
33
  export interface ITableColumn<T> extends ITableColumnBase {
24
34
  id: keyof T;
@@ -112,7 +122,11 @@ export type TableItemAction<T> = {
112
122
  visibilityFactory?: (item: T) => boolean;
113
123
  };
114
124
  export type TableStyles = {
115
- /** Stick the table header to the scrolling ancestor. @default false */
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
+ */
116
130
  stickyHeader?: boolean;
117
131
  /** Show the per-row actions popover above the row instead of below. @default false */
118
132
  actionsPopoverTopDirection?: boolean;
@@ -122,6 +136,13 @@ export type TableStyles = {
122
136
  zebra?: boolean;
123
137
  /** Visual chrome around the table. `'plain'` blends with surrounding layout; `'card'` adds a bordered, rounded panel. @default 'plain' */
124
138
  variant?: 'plain' | 'card';
139
+ /**
140
+ * Column width algorithm. `'auto'` treats `cssWidth` as a hint and lets content redistribute space. `'fixed'` honours `cssWidth` exactly and wraps or
141
+ * clips content instead of widening the column. Under `'fixed'`, percent columns summing to 100% share what the fixed columns leave in that ratio;
142
+ * `minCssWidth` does not apply — floor the whole table with `--sc-kit--table--min-width` instead.
143
+ * @default 'auto'
144
+ */
145
+ layout?: 'auto' | 'fixed';
125
146
  };
126
147
  export type TableOrderByState<TOrder> = {
127
148
  orderBy: TOrder;
@@ -3,7 +3,7 @@
3
3
 
4
4
  <script lang="ts" generics="T extends Record<string, unknown>, K extends keyof T & string">import { Utils } from '../../core/utils';
5
5
  import { default as FieldFrame } from './field-frame.svelte';
6
- let { handler, name, validateOn = ['input', 'change', 'blur'], debounceMs = 0, disableTouchedTracking = false, reserveErrorSpace = false, on, children } = $props();
6
+ let { handler, name, validateOn = ['input', 'change', 'blur'], debounceMs = 0, disableTouchedTracking = false, errorPlacement = 'overlay', on, children } = $props();
7
7
  // True between a value change and the next completed validation. Hides the stale error in the
8
8
  // UI without mutating handler.errors — the model keeps the last known result, the view just
9
9
  // doesn't trust it until the validator has caught up.
@@ -64,7 +64,7 @@ const field = $derived({
64
64
  });
65
65
  </script>
66
66
 
67
- <FieldFrame error={showErrors ? handler.errors[name] : null} reserveErrorSpace={reserveErrorSpace}>
67
+ <FieldFrame error={showErrors ? handler.errors[name] : null} errorPlacement={errorPlacement}>
68
68
  {@render children(field, pendingValidation)}
69
69
  </FieldFrame>
70
70
 
@@ -72,8 +72,7 @@ const field = $derived({
72
72
  @component
73
73
  Binds a form field to any kit input via a render-prop snippet. The base inputs stay unchanged;
74
74
  `Validatable` is the only place that knows about `FormValidationHandler`. Wraps children in an
75
- internal `FieldFrame` that reserves space for the error message so toggling doesn't shift
76
- layout; `reserveErrorSpace={false}` drops the reserve — the message overlays content below.
75
+ internal `FieldFrame` that renders the error message below the field, placed per `errorPlacement`.
77
76
 
78
77
  ### Usage
79
78
  ```svelte