@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.
@@ -1,16 +1,11 @@
1
- <script lang="ts" generics="T extends {id: string}">import { DynamicComponent } from '../dynamic-component';
2
- import { EmptyState } from '../empty-state';
3
- import { TableCell, TableHideableCell } from './table-cells';
4
- import { TableLocalization } from './table-localization';
5
- import { columnCellStyle, createTableView } from './table-view.svelte';
6
- let { model, showAlternativeView = false, noItemsPlaceholderEnabled, noItemsPlaceholderDelay, grouping, on, alternativeView, customNoItemsPlaceholder } = $props();
7
- const localization = new TableLocalization();
8
- const view = createTableView(() => model, () => on, () => noItemsPlaceholderEnabled, () => noItemsPlaceholderDelay);
1
+ <script lang="ts" generics="T extends {id: string}">import { default as TableCore } from './table-core.svelte';
2
+ let { model, grouping, ...shared } = $props();
3
+ const itemRow = (item, index) => ({ kind: 'item', key: item.id, item, index });
9
4
  const displayRows = $derived.by(() => {
10
5
  const items = model.items;
11
6
  const grp = grouping;
12
7
  if (!grp) {
13
- return items.map((item, index) => ({ kind: 'item', item, index }));
8
+ return items.map(itemRow);
14
9
  }
15
10
  const keys = items.map((item) => grp.key(item));
16
11
  const out = [];
@@ -18,7 +13,7 @@ const displayRows = $derived.by(() => {
18
13
  while (i < items.length) {
19
14
  const key = keys[i];
20
15
  if (key === null) {
21
- out.push({ kind: 'item', item: items[i], index: i });
16
+ out.push(itemRow(items[i], i));
22
17
  i += 1;
23
18
  continue;
24
19
  }
@@ -28,98 +23,15 @@ const displayRows = $derived.by(() => {
28
23
  }
29
24
  out.push({ kind: 'group', key, label: grp.label ? grp.label(key, items[i]) : key, count: j - i });
30
25
  for (let k = i; k < j; k += 1) {
31
- out.push({ kind: 'item', item: items[k], index: k });
26
+ out.push(itemRow(items[k], k));
32
27
  }
33
28
  i = j;
34
29
  }
35
30
  return out;
36
31
  });
37
- const visibleColspan = $derived(model.columns.filter((column) => !column.hidden).length);
38
32
  </script>
39
33
 
40
- {#snippet noItemsContent()}
41
- {#if customNoItemsPlaceholder}
42
- {@render customNoItemsPlaceholder()}
43
- {:else}
44
- <EmptyState graphic="zero-content" title={localization.noItemsFoundTitle} description={localization.noItemsFoundBody} />
45
- {/if}
46
- {/snippet}
47
-
48
- <div class="table-container table-container--{model.styles?.size ?? 'md'} table-container--{model.styles?.variant ?? 'plain'}">
49
- {#if alternativeView && showAlternativeView}
50
- {#if view.showNoItemsPlaceholder}
51
- <div class="alternative-view__placeholder">{@render noItemsContent()}</div>
52
- {:else}
53
- {@render alternativeView()}
54
- {/if}
55
- {:else}
56
- <table class="table" class:table--sticky-header={model.styles?.stickyHeader === true} class:table--fixed={model.styles?.layout === 'fixed'}>
57
- <thead class="table__head">
58
- <tr>
59
- {#each model.columns as column (column)}
60
- <TableHideableCell column={column}>
61
- <th class="table__th" class:table__th--centered={column.centered} style={columnCellStyle(column)} title={column.title}>
62
- <DynamicComponent model={column.headerView} />
63
- </th>
64
- </TableHideableCell>
65
- {/each}
66
- </tr>
67
- </thead>
68
- <tbody>
69
- {#if view.showNoItemsPlaceholder}
70
- <tr class="table__tr table__tr--placeholder">
71
- <td colspan={visibleColspan}>
72
- <div class="table__placeholder">{@render noItemsContent()}</div>
73
- </td>
74
- </tr>
75
- {:else}
76
- {#each displayRows as row (row.kind === 'group' ? 'g:' + row.key : 'i:' + row.item.id)}
77
- {#if row.kind === 'group'}
78
- <tr>
79
- <td class="table__group-header" colspan={visibleColspan}>
80
- {#if grouping?.header}
81
- {@render grouping.header({ key: row.key, label: row.label, count: row.count })}
82
- {:else}
83
- {row.label}
84
- {/if}
85
- </td>
86
- </tr>
87
- {:else}
88
- {@const item = row.item}
89
- {@const itemIndex = row.index}
90
- {@const isSelected = model.selection.has(item.id)}
91
- {@const isHighlighted = model.highlightedItemIds.includes(item.id)}
92
- {@const flushBottom = model.styles?.variant === 'card' && itemIndex === model.items.length - 1}
93
- {@const zebra = model.styles?.zebra === true && itemIndex % 2 === 1 && !isSelected && !isHighlighted}
94
- <tr
95
- class="table__tr"
96
- id={item.id}
97
- class:table__tr--selected={isSelected}
98
- class:table__tr--highlighted={isHighlighted}
99
- class:table__tr--zebra={zebra}>
100
- {#each model.columns as column (column)}
101
- <TableHideableCell column={column}>
102
- <td
103
- class="table__td"
104
- class:table__td--centered={column.centered}
105
- class:table__td--clickable={!!column.onClick}
106
- class:table__td--flush={flushBottom}
107
- style={columnCellStyle(column)}
108
- onclick={() => column.onClick && column.onClick(item)}>
109
- <div class="table__cell" class:table__cell--reveal-on-row-hover={column.revealOnRowHover}>
110
- <TableCell model={model} item={item} column={column} itemIndex={itemIndex} on={{ itemsReordered: on?.itemsReordered }} />
111
- </div>
112
- </td>
113
- </TableHideableCell>
114
- {/each}
115
- </tr>
116
- {/if}
117
- {/each}
118
- {/if}
119
- </tbody>
120
- </table>
121
- {/if}
122
- </div>
34
+ <TableCore {...shared} model={model} rows={displayRows} groupHeader={grouping?.header} />
123
35
 
124
36
  <!--
125
37
  @component
@@ -127,16 +39,6 @@ Data table with sorting, selection, column visibility management, optional consu
127
39
  (`grouping`), and group actions. For drag-and-drop row reordering use `DraggableTable` instead (this static
128
40
  variant ships no dnd code).
129
41
 
130
- ### TableStyles (via `model.styles`)
131
- | Field | Type | Effect |
132
- |---|---|---|
133
- | `size` | `'sm' \| 'md'` | Row height + cell padding + font-size preset (default `'md'`) |
134
- | `variant` | `'plain' \| 'card'` | `'card'` wraps the table in a bordered rounded panel (default `'plain'`) |
135
- | `zebra` | `boolean` | Alternates row backgrounds with `--sc-kit--color--bg--field-alt` |
136
- | `stickyHeader` | `boolean` | Sticks `thead` to the scrolling ancestor |
137
- | `actionsPopoverTopDirection` | `boolean` | Forces the per-row actions popover above instead of below |
138
- | `layout` | `'auto' \| 'fixed'` | `'fixed'` honours `cssWidth` exactly; percent columns share what fixed ones leave, `minCssWidth` is ignored (default `'auto'`) |
139
-
140
42
  ### CSS Custom Properties
141
43
  | Property | Description | Default |
142
44
  |---|---|---|
@@ -146,7 +48,10 @@ variant ships no dnd code).
146
48
  | `--sc-kit--table--cell--padding-block` | Cell block padding | `0` |
147
49
  | `--sc-kit--table--cell--vertical-align` | Cell content alignment — `top` keeps inputs aligned when one cell grows | `middle` |
148
50
  | `--sc-kit--table--head--height` | Header row height | per size (32 / 40 px) |
149
- | `--sc-kit--table--head--background-color` | Header background | `--sc-kit--color--bg--field-alt` |
51
+ | `--sc-kit--table--head--background-color` | Header background | `--sc-kit--color--bg--field-alt`; with a `toolbar`, the card / panel background |
52
+ | `--sc-kit--table--toolbar--background-color` | Toolbar band background | `--sc-kit--color--bg--field-alt` |
53
+ | `--sc-kit--table--toolbar--padding-block` | Toolbar band block padding — `0` hands the band to the toolbar content | `--sc-kit--space--3` |
54
+ | `--sc-kit--table--toolbar--padding-inline` | Toolbar band inline padding | the cell inline padding |
150
55
  | `--sc-kit--table--head--color` | Header text color | `--sc-kit--color--text--secondary` |
151
56
  | `--sc-kit--table--head--font-size` | Header font size | `--sc-kit--font-size--sm` |
152
57
  | `--sc-kit--table--head--font-weight` | Header font weight | `--sc-kit--font-weight--semibold` |
@@ -156,188 +61,11 @@ variant ships no dnd code).
156
61
  | `--sc-kit--table--row--color` | Row background | `transparent` |
157
62
  | `--sc-kit--table--row--alternative-color` | Hover / highlighted row background | `--sc-kit--color--bg--hover` |
158
63
  | `--sc-kit--table--zebra--color` | Zebra-row background (when `zebra` on) | `light-dark(#fcfcfd, #232323)` |
64
+ | `--sc-kit--table--selected--color` | Selected row background | `--sc-kit--color--accent--softer` |
65
+ | `--sc-kit--table--selected--color--hover` | Selected row background on hover | `--sc-kit--color--accent--soft` |
159
66
  | `--sc-kit--table--card--background` | Card variant background | `--sc-kit--color--bg--panel` |
160
67
  | `--sc-kit--table--card--border-color` | Card variant border | `--sc-kit--color--border` |
161
68
  | `--sc-kit--table--card--border-radius` | Card variant radius | `--sc-kit--radius--lg` |
162
69
  | `--sc-kit--table--card--padding-bottom` | Card variant bottom padding | `--sc-kit--space--4` |
70
+ | `--sc-kit--table--min-width` | Table floor — past it the columns scroll horizontally inside the chrome, the toolbar stays. The column-floor substitute under `layout: 'fixed'` | `auto` |
163
71
  -->
164
-
165
- <style>.alternative-view__placeholder {
166
- width: 100%;
167
- display: flex;
168
- align-items: center;
169
- justify-content: center;
170
- font-size: 80%;
171
- user-select: none;
172
- }
173
-
174
- .table-container {
175
- --_table--row--height: var(--sc-kit--table--row--height, var(--_table--size-row-height));
176
- --_table--cell--font-size: var(--sc-kit--table--cell--font-size, var(--_table--size-cell-font-size));
177
- --_table--cell--padding-inline: var(--sc-kit--table--cell--padding-inline, var(--_table--size-cell-padding));
178
- --_table--cell--padding-block: var(--sc-kit--table--cell--padding-block, 0);
179
- --_table--cell--vertical-align: var(--sc-kit--table--cell--vertical-align, middle);
180
- --_table--head--height: var(--sc-kit--table--head--height, var(--_table--size-head-height, var(--_table--row--height)));
181
- --_table--head--background-color: var(--sc-kit--table--head--background-color, var(--sc-kit--color--bg--field-alt));
182
- --_table--head--color: var(--sc-kit--table--head--color, var(--sc-kit--color--text--secondary));
183
- --_table--head--font-weight: var(--sc-kit--table--head--font-weight, var(--sc-kit--font-weight--semibold));
184
- --_table--head--letter-spacing: var(--sc-kit--table--head--letter-spacing, normal);
185
- --_table--head--text-transform: var(--sc-kit--table--head--text-transform, none);
186
- --_table--border-color: var(--sc-kit--table--border-color, var(--sc-kit--color--border));
187
- --_table--row--color: var(--sc-kit--table--row--color, transparent);
188
- --_table--row--alternative-color: var(--sc-kit--table--row--alternative-color, var(--sc-kit--color--bg--hover));
189
- --_table--zebra--color: var(--sc-kit--table--zebra--color, light-dark(#fcfcfd, #232323));
190
- --_table--selected--color: var(--sc-kit--table--selected--color, var(--sc-kit--color--accent--softer));
191
- --_table--selected--color-hover: var(--sc-kit--table--selected--color--hover, var(--sc-kit--color--accent--soft));
192
- --_table--card--background: var(--sc-kit--table--card--background, var(--sc-kit--color--bg--panel));
193
- --_table--card--border-color: var(--sc-kit--table--card--border-color, var(--sc-kit--color--border));
194
- --_table--card--border-radius: var(--sc-kit--table--card--border-radius, var(--sc-kit--radius--lg));
195
- --_table--card--padding-bottom: var(--sc-kit--table--card--padding-bottom, var(--sc-kit--space--4));
196
- display: flex;
197
- overflow-x: auto;
198
- height: 100%;
199
- --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
200
- --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
201
- }
202
- .table-container::-webkit-scrollbar {
203
- width: 6px;
204
- height: 6px;
205
- }
206
- .table-container::-webkit-scrollbar-track {
207
- background: var(--_cross-browser-scrollbar--track-color);
208
- border-radius: 100vw;
209
- }
210
- .table-container::-webkit-scrollbar-thumb {
211
- background: var(--_cross-browser-scrollbar--thumb-color);
212
- border-radius: 100vw;
213
- }
214
- @supports (scrollbar-color: transparent transparent) {
215
- .table-container {
216
- scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
217
- scrollbar-width: thin;
218
- }
219
- }
220
- .table-container--md {
221
- --_table--size-row-height: 2.75rem;
222
- --_table--size-head-height: 2.5rem;
223
- --_table--size-cell-font-size: var(--sc-kit--font-size--md);
224
- --_table--size-cell-padding: var(--sc-kit--space--4);
225
- }
226
- .table-container--sm {
227
- --_table--size-row-height: 2.25rem;
228
- --_table--size-head-height: 2rem;
229
- --_table--size-cell-font-size: var(--sc-kit--font-size--sm);
230
- --_table--size-cell-padding: var(--sc-kit--space--3);
231
- }
232
- .table-container--card {
233
- background: var(--_table--card--background);
234
- border: 1px solid var(--_table--card--border-color);
235
- border-radius: var(--_table--card--border-radius);
236
- padding-bottom: var(--_table--card--padding-bottom);
237
- }
238
-
239
- .table {
240
- width: 100%;
241
- table-layout: auto;
242
- height: fit-content;
243
- border-collapse: separate;
244
- border-spacing: 0;
245
- }
246
- .table--fixed {
247
- table-layout: fixed;
248
- }
249
- .table__head {
250
- height: var(--_table--head--height);
251
- max-height: var(--_table--head--height);
252
- background: var(--_table--head--background-color);
253
- color: var(--_table--head--color);
254
- font-size: var(--sc-kit--table--head--font-size, var(--sc-kit--font-size--sm));
255
- letter-spacing: var(--_table--head--letter-spacing);
256
- text-transform: var(--_table--head--text-transform);
257
- }
258
- .table__th {
259
- padding: 0 var(--_table--cell--padding-inline);
260
- font-weight: var(--_table--head--font-weight);
261
- text-align: left;
262
- vertical-align: middle;
263
- border-bottom: 1px solid var(--_table--border-color);
264
- -webkit-user-drag: none;
265
- user-select: none;
266
- }
267
- .table__th :global([contenteditable]) {
268
- user-select: text;
269
- }
270
- .table__th--centered {
271
- --_--table-header--justify-content: center;
272
- }
273
- .table__tr {
274
- height: var(--_table--row--height);
275
- max-height: var(--_table--row--height);
276
- background: var(--_table--row--color);
277
- }
278
- .table__tr--zebra {
279
- background: var(--_table--zebra--color);
280
- }
281
- .table__tr:hover, .table__tr--highlighted {
282
- background: var(--_table--row--alternative-color);
283
- }
284
- .table__tr:hover, .table__tr:focus-within {
285
- --_table--row-reveal--opacity: 1;
286
- }
287
- @media (any-hover: none) {
288
- .table__tr {
289
- --_table--row-reveal--opacity: 1;
290
- }
291
- }
292
- .table__tr--selected {
293
- background: var(--_table--selected--color);
294
- }
295
- .table__tr--selected:hover {
296
- background: var(--_table--selected--color-hover);
297
- }
298
- .table__tr--placeholder {
299
- background: var(--_table--row--color) !important;
300
- }
301
- .table__td {
302
- padding: 0;
303
- border-bottom: 1px solid var(--_table--border-color);
304
- font-size: var(--_table--cell--font-size);
305
- color: var(--sc-kit--color--text--primary);
306
- vertical-align: var(--_table--cell--vertical-align);
307
- }
308
- .table__td--centered {
309
- text-align: center;
310
- }
311
- .table__td--clickable {
312
- cursor: pointer;
313
- }
314
- .table__td--flush {
315
- border-bottom: none;
316
- }
317
- .table__cell {
318
- padding: var(--_table--cell--padding-block) var(--_table--cell--padding-inline);
319
- }
320
- .table__cell--reveal-on-row-hover {
321
- opacity: var(--_table--row-reveal--opacity, 0);
322
- transition: opacity var(--sc-kit--duration--base) var(--sc-kit--ease--default);
323
- }
324
- .table__placeholder {
325
- display: flex;
326
- align-items: center;
327
- justify-content: center;
328
- font-size: 80%;
329
- user-select: none;
330
- }
331
- .table--sticky-header thead {
332
- position: sticky;
333
- top: 0;
334
- z-index: 1;
335
- }
336
-
337
- .table__group-header {
338
- padding-block: var(--sc-kit--space--6) var(--sc-kit--space--2);
339
- padding-inline: var(--_table--cell--padding-inline);
340
- font-size: var(--sc-kit--font-size--sm);
341
- font-weight: var(--sc-kit--font-weight--semibold);
342
- color: var(--sc-kit--color--text--secondary);
343
- }</style>
@@ -6,12 +6,13 @@ declare function $$render<T extends {
6
6
  props: {
7
7
  model: TableModel<T>;
8
8
  showAlternativeView?: boolean;
9
- /** Whether an empty list turns into the no-items placeholder at all — `false` pairs with your own loading / empty UI. Rows always win: the placeholder
10
- * is never rendered over a non-empty list. @default true */
11
- noItemsPlaceholderEnabled?: boolean;
12
- /** Anti-flicker delay in ms before the placeholder appears, once `noItemsPlaceholderEnabled`. `0` shows it as soon as the list is empty. @default 0 */
9
+ /** Anti-flicker delay in ms before the placeholder appears. `0` shows it as soon as the list is empty. @default 0 */
13
10
  noItemsPlaceholderDelay?: number;
14
- /** Group consecutive (already-sorted) rows under section headers. `key` returns a group id per item (`null` = ungrouped); a header row renders wherever the key changes between neighbours. */
11
+ /**
12
+ * Group rows under section headers, for read-only lists. `key` returns a group id per item (`null` = ungrouped). Items must be sorted so each group
13
+ * forms one contiguous run — a group id that reappears after another group is invalid input. A `move-row` column is not supported with grouping: it can
14
+ * move an item across a group boundary and break that rule.
15
+ */
15
16
  grouping?: {
16
17
  key: (item: T) => string | null;
17
18
  /** Header text — defaults to the group key. */
@@ -30,7 +31,16 @@ declare function $$render<T extends {
30
31
  itemsReordered?: (e: T[]) => void;
31
32
  };
32
33
  alternativeView?: Snippet;
33
- customNoItemsPlaceholder?: Snippet;
34
+ /**
35
+ * What an empty list shows: the built-in empty state when omitted, your own content as a snippet, nothing with `false` — pairs with your own loading /
36
+ * empty UI. Rows always win: the placeholder is never rendered over a non-empty list.
37
+ */
38
+ noItemsPlaceholder?: Snippet | false;
39
+ /**
40
+ * Band inside the table chrome above the column header row — a title and actions. With a toolbar the column header always sticks under it and takes
41
+ * the row background; an empty list drops the column header, leaving the toolbar and the no-items placeholder.
42
+ */
43
+ toolbar?: Snippet;
34
44
  };
35
45
  exports: {};
36
46
  bindings: "";
@@ -62,16 +72,6 @@ interface $$IsomorphicComponent {
62
72
  * (`grouping`), and group actions. For drag-and-drop row reordering use `DraggableTable` instead (this static
63
73
  * variant ships no dnd code).
64
74
  *
65
- * ### TableStyles (via `model.styles`)
66
- * | Field | Type | Effect |
67
- * |---|---|---|
68
- * | `size` | `'sm' \| 'md'` | Row height + cell padding + font-size preset (default `'md'`) |
69
- * | `variant` | `'plain' \| 'card'` | `'card'` wraps the table in a bordered rounded panel (default `'plain'`) |
70
- * | `zebra` | `boolean` | Alternates row backgrounds with `--sc-kit--color--bg--field-alt` |
71
- * | `stickyHeader` | `boolean` | Sticks `thead` to the scrolling ancestor |
72
- * | `actionsPopoverTopDirection` | `boolean` | Forces the per-row actions popover above instead of below |
73
- * | `layout` | `'auto' \| 'fixed'` | `'fixed'` honours `cssWidth` exactly; percent columns share what fixed ones leave, `minCssWidth` is ignored (default `'auto'`) |
74
- *
75
75
  * ### CSS Custom Properties
76
76
  * | Property | Description | Default |
77
77
  * |---|---|---|
@@ -81,7 +81,10 @@ interface $$IsomorphicComponent {
81
81
  * | `--sc-kit--table--cell--padding-block` | Cell block padding | `0` |
82
82
  * | `--sc-kit--table--cell--vertical-align` | Cell content alignment — `top` keeps inputs aligned when one cell grows | `middle` |
83
83
  * | `--sc-kit--table--head--height` | Header row height | per size (32 / 40 px) |
84
- * | `--sc-kit--table--head--background-color` | Header background | `--sc-kit--color--bg--field-alt` |
84
+ * | `--sc-kit--table--head--background-color` | Header background | `--sc-kit--color--bg--field-alt`; with a `toolbar`, the card / panel background |
85
+ * | `--sc-kit--table--toolbar--background-color` | Toolbar band background | `--sc-kit--color--bg--field-alt` |
86
+ * | `--sc-kit--table--toolbar--padding-block` | Toolbar band block padding — `0` hands the band to the toolbar content | `--sc-kit--space--3` |
87
+ * | `--sc-kit--table--toolbar--padding-inline` | Toolbar band inline padding | the cell inline padding |
85
88
  * | `--sc-kit--table--head--color` | Header text color | `--sc-kit--color--text--secondary` |
86
89
  * | `--sc-kit--table--head--font-size` | Header font size | `--sc-kit--font-size--sm` |
87
90
  * | `--sc-kit--table--head--font-weight` | Header font weight | `--sc-kit--font-weight--semibold` |
@@ -91,10 +94,13 @@ interface $$IsomorphicComponent {
91
94
  * | `--sc-kit--table--row--color` | Row background | `transparent` |
92
95
  * | `--sc-kit--table--row--alternative-color` | Hover / highlighted row background | `--sc-kit--color--bg--hover` |
93
96
  * | `--sc-kit--table--zebra--color` | Zebra-row background (when `zebra` on) | `light-dark(#fcfcfd, #232323)` |
97
+ * | `--sc-kit--table--selected--color` | Selected row background | `--sc-kit--color--accent--softer` |
98
+ * | `--sc-kit--table--selected--color--hover` | Selected row background on hover | `--sc-kit--color--accent--soft` |
94
99
  * | `--sc-kit--table--card--background` | Card variant background | `--sc-kit--color--bg--panel` |
95
100
  * | `--sc-kit--table--card--border-color` | Card variant border | `--sc-kit--color--border` |
96
101
  * | `--sc-kit--table--card--border-radius` | Card variant radius | `--sc-kit--radius--lg` |
97
102
  * | `--sc-kit--table--card--padding-bottom` | Card variant bottom padding | `--sc-kit--space--4` |
103
+ * | `--sc-kit--table--min-width` | Table floor — past it the columns scroll horizontally inside the chrome, the toolbar stays. The column-floor substitute under `layout: 'fixed'` | `auto` |
98
104
  */
99
105
  declare const Cmp: $$IsomorphicComponent;
100
106
  type Cmp<T extends {