@streamscloud/kit 0.76.5 → 0.77.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.
- package/dist/ui/form-field/cmp.form-field-validatable.svelte +3 -3
- package/dist/ui/form-field/cmp.form-field-validatable.svelte.d.ts +4 -4
- package/dist/ui/table/_table-styles.scss +37 -8
- package/dist/ui/table/cmp.table-draggable.svelte +63 -23
- package/dist/ui/table/cmp.table-draggable.svelte.d.ts +10 -2
- package/dist/ui/table/cmp.table.svelte +50 -19
- package/dist/ui/table/cmp.table.svelte.d.ts +10 -2
- package/dist/ui/table/table-cells/table-checkbox-cell.svelte +0 -1
- package/dist/ui/table/table-cells/table-move-row-cell.svelte +0 -2
- package/dist/ui/table/table-cells/table-text-cell.svelte +0 -1
- package/dist/ui/table/table-column-models.svelte.d.ts +5 -0
- package/dist/ui/table/table-column-models.svelte.js +6 -0
- package/dist/ui/table/table-headers/table-header-sortable.svelte +1 -4
- package/dist/ui/table/table-headers/table-header.svelte +1 -1
- package/dist/ui/table/table-view.svelte.d.ts +2 -2
- package/dist/ui/table/table-view.svelte.js +11 -4
- package/dist/ui/table/types.d.ts +17 -0
- package/dist/ui/validatable/cmp.validatable.svelte +3 -4
- package/dist/ui/validatable/cmp.validatable.svelte.d.ts +6 -6
- package/dist/ui/validatable/cmp.validation-error.svelte +4 -4
- package/dist/ui/validatable/cmp.validation-error.svelte.d.ts +7 -6
- package/dist/ui/validatable/field-frame.svelte +25 -15
- package/dist/ui/validatable/field-frame.svelte.d.ts +8 -8
- package/dist/ui/validatable/index.d.ts +1 -0
- package/dist/ui/validatable/types.d.ts +7 -0
- package/dist/ui/validatable/types.js +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts" generics="T extends Record<string, unknown>, K extends keyof T & string">import { Validatable } from '../validatable';
|
|
2
2
|
import { default as FormField } from './cmp.form-field.svelte';
|
|
3
|
-
let { handler, name, label, hint, required = false, validateOn, debounceMs, disableTouchedTracking,
|
|
3
|
+
let { handler, name, label, hint, required = false, validateOn, debounceMs, disableTouchedTracking, errorPlacement, on, children } = $props();
|
|
4
4
|
</script>
|
|
5
5
|
|
|
6
6
|
<FormField label={label} hint={hint} required={required}>
|
|
@@ -10,7 +10,7 @@ let { handler, name, label, hint, required = false, validateOn, debounceMs, disa
|
|
|
10
10
|
validateOn={validateOn}
|
|
11
11
|
debounceMs={debounceMs}
|
|
12
12
|
disableTouchedTracking={disableTouchedTracking}
|
|
13
|
-
|
|
13
|
+
errorPlacement={errorPlacement}
|
|
14
14
|
on={on}
|
|
15
15
|
children={children} />
|
|
16
16
|
</FormField>
|
|
@@ -41,5 +41,5 @@ Identical visual output to:
|
|
|
41
41
|
</FormField>
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Both `FormField`'s label/required and `Validatable`'s
|
|
44
|
+
Both `FormField`'s label/required and `Validatable`'s error slot (`errorPlacement`) apply unchanged — this cmp adds no styling, just collapses the wiring.
|
|
45
45
|
-->
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FormValidationHandler } from '../../core/validation';
|
|
2
|
-
import { type FieldBinding, type ValidateOnEvent } from '../validatable';
|
|
2
|
+
import { type ErrorPlacement, type FieldBinding, type ValidateOnEvent } from '../validatable';
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
4
|
declare function $$render<T extends Record<string, unknown>, K extends keyof T & string>(): {
|
|
5
5
|
props: {
|
|
@@ -19,8 +19,8 @@ declare function $$render<T extends Record<string, unknown>, K extends keyof T &
|
|
|
19
19
|
debounceMs?: number;
|
|
20
20
|
/** Show errors before the field has been touched. Forwarded to `<Validatable>`. @default false */
|
|
21
21
|
disableTouchedTracking?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/** Where the error message sits. Forwarded to `<Validatable>`. @default 'overlay' */
|
|
23
|
+
errorPlacement?: ErrorPlacement;
|
|
24
24
|
/** Extra consumer callbacks. Forwarded to `<Validatable>`. */
|
|
25
25
|
on?: {
|
|
26
26
|
input?: (value: T[K]) => void;
|
|
@@ -74,7 +74,7 @@ interface $$IsomorphicComponent {
|
|
|
74
74
|
* </FormField>
|
|
75
75
|
* ```
|
|
76
76
|
*
|
|
77
|
-
* Both `FormField`'s label/required and `Validatable`'s
|
|
77
|
+
* Both `FormField`'s label/required and `Validatable`'s error slot (`errorPlacement`) apply unchanged — this cmp adds no styling, just collapses the wiring.
|
|
78
78
|
*/
|
|
79
79
|
declare const Cmp: $$IsomorphicComponent;
|
|
80
80
|
type Cmp<T extends Record<string, unknown>, K extends keyof T & string> = InstanceType<typeof Cmp<T, K>>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@use 'styles/mixins';
|
|
2
2
|
@use 'styles/functions';
|
|
3
|
+
@use 'styles/transitions';
|
|
3
4
|
|
|
4
5
|
@mixin base {
|
|
5
6
|
.alternative-view {
|
|
@@ -16,11 +17,16 @@
|
|
|
16
17
|
.table-container {
|
|
17
18
|
--_table--row--height: var(--sc-kit--table--row--height, var(--_table--size-row-height));
|
|
18
19
|
--_table--cell--font-size: var(--sc-kit--table--cell--font-size, var(--_table--size-cell-font-size));
|
|
19
|
-
--_table--cell--
|
|
20
|
+
--_table--cell--padding-inline: var(--sc-kit--table--cell--padding-inline, var(--_table--size-cell-padding));
|
|
21
|
+
--_table--cell--padding-block: var(--sc-kit--table--cell--padding-block, 0);
|
|
22
|
+
--_table--cell--vertical-align: var(--sc-kit--table--cell--vertical-align, middle);
|
|
20
23
|
--_table--head--height: var(--sc-kit--table--head--height, var(--_table--size-head-height, var(--_table--row--height)));
|
|
21
24
|
|
|
22
25
|
--_table--head--background-color: var(--sc-kit--table--head--background-color, var(--sc-kit--color--bg--field-alt));
|
|
23
26
|
--_table--head--color: var(--sc-kit--table--head--color, var(--sc-kit--color--text--secondary));
|
|
27
|
+
--_table--head--font-weight: var(--sc-kit--table--head--font-weight, var(--sc-kit--font-weight--semibold));
|
|
28
|
+
--_table--head--letter-spacing: var(--sc-kit--table--head--letter-spacing, normal);
|
|
29
|
+
--_table--head--text-transform: var(--sc-kit--table--head--text-transform, none);
|
|
24
30
|
--_table--border-color: var(--sc-kit--table--border-color, var(--sc-kit--color--border));
|
|
25
31
|
--_table--row--color: var(--sc-kit--table--row--color, transparent);
|
|
26
32
|
--_table--row--alternative-color: var(--sc-kit--table--row--alternative-color, var(--sc-kit--color--bg--hover));
|
|
@@ -69,25 +75,31 @@
|
|
|
69
75
|
border-collapse: separate;
|
|
70
76
|
border-spacing: 0;
|
|
71
77
|
|
|
78
|
+
&--fixed {
|
|
79
|
+
table-layout: fixed;
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
&__head {
|
|
73
83
|
height: var(--_table--head--height);
|
|
74
84
|
max-height: var(--_table--head--height);
|
|
75
85
|
background: var(--_table--head--background-color);
|
|
76
86
|
color: var(--_table--head--color);
|
|
77
87
|
font-size: var(--sc-kit--table--head--font-size, var(--sc-kit--font-size--sm));
|
|
78
|
-
|
|
88
|
+
letter-spacing: var(--_table--head--letter-spacing);
|
|
89
|
+
text-transform: var(--_table--head--text-transform);
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
&__th {
|
|
82
|
-
padding: 0 var(--_table--cell--
|
|
93
|
+
padding: 0 var(--_table--cell--padding-inline);
|
|
94
|
+
// On th, not the head: the UA stylesheet's `th { font-weight: bold }` beats an inherited weight.
|
|
95
|
+
font-weight: var(--_table--head--font-weight);
|
|
83
96
|
text-align: left;
|
|
84
97
|
vertical-align: middle;
|
|
85
98
|
border-bottom: 1px solid var(--_table--border-color);
|
|
86
99
|
@include mixins.no-select;
|
|
87
100
|
|
|
88
101
|
&--centered {
|
|
89
|
-
|
|
90
|
-
--_table-header--justify-content: center;
|
|
102
|
+
--_--table-header--justify-content: center;
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
105
|
|
|
@@ -103,7 +115,15 @@
|
|
|
103
115
|
&:hover,
|
|
104
116
|
&--highlighted {
|
|
105
117
|
background: var(--_table--row--alternative-color);
|
|
106
|
-
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
&:hover,
|
|
121
|
+
&:focus-within {
|
|
122
|
+
--_table--row-reveal--opacity: 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@media (any-hover: none) {
|
|
126
|
+
--_table--row-reveal--opacity: 1;
|
|
107
127
|
}
|
|
108
128
|
|
|
109
129
|
&--selected {
|
|
@@ -120,11 +140,11 @@
|
|
|
120
140
|
}
|
|
121
141
|
|
|
122
142
|
&__td {
|
|
123
|
-
padding: 0
|
|
143
|
+
padding: 0;
|
|
124
144
|
border-bottom: 1px solid var(--_table--border-color);
|
|
125
145
|
font-size: var(--_table--cell--font-size);
|
|
126
146
|
color: var(--sc-kit--color--text--primary);
|
|
127
|
-
vertical-align:
|
|
147
|
+
vertical-align: var(--_table--cell--vertical-align);
|
|
128
148
|
|
|
129
149
|
&--centered {
|
|
130
150
|
text-align: center;
|
|
@@ -139,6 +159,15 @@
|
|
|
139
159
|
}
|
|
140
160
|
}
|
|
141
161
|
|
|
162
|
+
&__cell {
|
|
163
|
+
padding: var(--_table--cell--padding-block) var(--_table--cell--padding-inline);
|
|
164
|
+
|
|
165
|
+
&--reveal-on-row-hover {
|
|
166
|
+
opacity: var(--_table--row-reveal--opacity, 0);
|
|
167
|
+
@include transitions.transition(opacity);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
142
171
|
&__placeholder {
|
|
143
172
|
display: flex;
|
|
144
173
|
align-items: center;
|
|
@@ -3,7 +3,7 @@ import { EmptyState } from '../empty-state';
|
|
|
3
3
|
import { Icon } from '../icon';
|
|
4
4
|
import { TableCell, TableHideableCell } from './table-cells';
|
|
5
5
|
import { TableLocalization } from './table-localization';
|
|
6
|
-
import {
|
|
6
|
+
import { columnCellStyle, createTableView } from './table-view.svelte';
|
|
7
7
|
import IconReorderDotsHorizontal from '@fluentui/svg-icons/icons/re_order_dots_horizontal_20_regular.svg?raw';
|
|
8
8
|
import { flip } from 'svelte/animate';
|
|
9
9
|
import { dndzone } from 'svelte-dnd-action';
|
|
@@ -44,10 +44,12 @@ const transformDraggedElement = (draggedRow) => {
|
|
|
44
44
|
}
|
|
45
45
|
// Clone is reparented to <body>, losing the .table-container --_table--* vars; cells collapse without this.
|
|
46
46
|
const cellStyle = window.getComputedStyle(referenceColumns[0]);
|
|
47
|
+
// Read off the table, not a cell: the drag-handle cell zeroes its own padding and per-column cellPadding overrides live on their td.
|
|
48
|
+
const tableStyle = window.getComputedStyle(tableRef);
|
|
47
49
|
draggedRow.style.setProperty('--_table--cell--font-size', cellStyle.fontSize);
|
|
48
|
-
draggedRow.style.setProperty('--_table--cell--
|
|
49
|
-
|
|
50
|
-
draggedRow.style.setProperty('--_table--
|
|
50
|
+
draggedRow.style.setProperty('--_table--cell--padding-inline', tableStyle.getPropertyValue('--_table--cell--padding-inline'));
|
|
51
|
+
draggedRow.style.setProperty('--_table--cell--padding-block', tableStyle.getPropertyValue('--_table--cell--padding-block'));
|
|
52
|
+
draggedRow.style.setProperty('--_table--cell--vertical-align', cellStyle.verticalAlign);
|
|
51
53
|
};
|
|
52
54
|
</script>
|
|
53
55
|
|
|
@@ -59,9 +61,7 @@ const transformDraggedElement = (draggedRow) => {
|
|
|
59
61
|
{/if}
|
|
60
62
|
{/snippet}
|
|
61
63
|
|
|
62
|
-
<div
|
|
63
|
-
class="table-container table-container--{model.styles?.size ?? 'md'} table-container--{model.styles?.variant ?? 'plain'}"
|
|
64
|
-
class:table-container--zebra={model.styles?.zebra === true}>
|
|
64
|
+
<div class="table-container table-container--{model.styles?.size ?? 'md'} table-container--{model.styles?.variant ?? 'plain'}">
|
|
65
65
|
{#if alternativeView && showAlternativeView}
|
|
66
66
|
{#if view.showNoItemsPlaceholder}
|
|
67
67
|
<div class="alternative-view__placeholder">{@render noItemsContent()}</div>
|
|
@@ -69,13 +69,17 @@ const transformDraggedElement = (draggedRow) => {
|
|
|
69
69
|
{@render alternativeView()}
|
|
70
70
|
{/if}
|
|
71
71
|
{:else}
|
|
72
|
-
<table
|
|
72
|
+
<table
|
|
73
|
+
class="table"
|
|
74
|
+
class:table--sticky-header={model.styles?.stickyHeader === true}
|
|
75
|
+
class:table--fixed={model.styles?.layout === 'fixed'}
|
|
76
|
+
bind:this={tableRef}>
|
|
73
77
|
<thead class="table__head">
|
|
74
78
|
<tr>
|
|
75
79
|
<th class="table__th table__drag-handle"> </th>
|
|
76
80
|
{#each model.columns as column (column)}
|
|
77
81
|
<TableHideableCell column={column}>
|
|
78
|
-
<th class="table__th" class:table__th--centered={column.centered} style={
|
|
82
|
+
<th class="table__th" class:table__th--centered={column.centered} style={columnCellStyle(column)} title={column.title}>
|
|
79
83
|
<DynamicComponent model={column.headerView} />
|
|
80
84
|
</th>
|
|
81
85
|
</TableHideableCell>
|
|
@@ -117,7 +121,9 @@ const transformDraggedElement = (draggedRow) => {
|
|
|
117
121
|
class:table__td--flush={flushBottom}
|
|
118
122
|
onmousedown={startDrag}
|
|
119
123
|
ontouchstart={startDrag}>
|
|
120
|
-
<
|
|
124
|
+
<div class="table__cell">
|
|
125
|
+
<Icon src={IconReorderDotsHorizontal} />
|
|
126
|
+
</div>
|
|
121
127
|
</td>
|
|
122
128
|
{#each model.columns as column (column)}
|
|
123
129
|
<TableHideableCell column={column}>
|
|
@@ -126,9 +132,11 @@ const transformDraggedElement = (draggedRow) => {
|
|
|
126
132
|
class:table__td--centered={column.centered}
|
|
127
133
|
class:table__td--clickable={!!column.onClick}
|
|
128
134
|
class:table__td--flush={flushBottom}
|
|
129
|
-
style={
|
|
135
|
+
style={columnCellStyle(column)}
|
|
130
136
|
onclick={() => column.onClick && column.onClick(item)}>
|
|
131
|
-
<
|
|
137
|
+
<div class="table__cell" class:table__cell--reveal-on-row-hover={column.revealOnRowHover}>
|
|
138
|
+
<TableCell model={model} item={item} column={column} itemIndex={itemIndex} on={{ itemsReordered: on?.itemsReordered }} />
|
|
139
|
+
</div>
|
|
132
140
|
</td>
|
|
133
141
|
</TableHideableCell>
|
|
134
142
|
{/each}
|
|
@@ -154,15 +162,23 @@ preserve the current selection. For non-reorderable tables use `Table` (it ships
|
|
|
154
162
|
| `zebra` | `boolean` | Alternates row backgrounds with `--sc-kit--color--bg--field-alt` |
|
|
155
163
|
| `stickyHeader` | `boolean` | Sticks `thead` to the scrolling ancestor |
|
|
156
164
|
| `actionsPopoverTopDirection` | `boolean` | Forces the per-row actions popover above instead of below |
|
|
165
|
+
| `layout` | `'auto' \| 'fixed'` | `'fixed'` honours `cssWidth` exactly; percent columns share what fixed ones leave, `minCssWidth` is ignored (default `'auto'`) |
|
|
157
166
|
|
|
158
167
|
### CSS Custom Properties
|
|
159
168
|
| Property | Description | Default |
|
|
160
169
|
|---|---|---|
|
|
161
|
-
| `--sc-kit--table--row--height` | Row height | per size (
|
|
162
|
-
| `--sc-kit--table--cell--
|
|
170
|
+
| `--sc-kit--table--row--height` | Row height | per size (36 / 44 px) |
|
|
171
|
+
| `--sc-kit--table--cell--padding-inline` | Cell inline padding | per size (`--space--3` / `--space--4`) |
|
|
163
172
|
| `--sc-kit--table--cell--font-size` | Cell font size | per size (`--font-size--sm` / `--md`) |
|
|
173
|
+
| `--sc-kit--table--cell--padding-block` | Cell block padding | `0` |
|
|
174
|
+
| `--sc-kit--table--cell--vertical-align` | Cell content alignment — `top` keeps inputs aligned when one cell grows | `middle` |
|
|
175
|
+
| `--sc-kit--table--head--height` | Header row height | per size (32 / 40 px) |
|
|
164
176
|
| `--sc-kit--table--head--background-color` | Header background | `--sc-kit--color--bg--field-alt` |
|
|
165
177
|
| `--sc-kit--table--head--color` | Header text color | `--sc-kit--color--text--secondary` |
|
|
178
|
+
| `--sc-kit--table--head--font-size` | Header font size | `--sc-kit--font-size--sm` |
|
|
179
|
+
| `--sc-kit--table--head--font-weight` | Header font weight | `--sc-kit--font-weight--semibold` |
|
|
180
|
+
| `--sc-kit--table--head--letter-spacing` | Header letter spacing | `normal` |
|
|
181
|
+
| `--sc-kit--table--head--text-transform` | Header text transform | `none` |
|
|
166
182
|
| `--sc-kit--table--border-color` | Border color between rows | `--sc-kit--color--border` |
|
|
167
183
|
| `--sc-kit--table--row--color` | Row background | `transparent` |
|
|
168
184
|
| `--sc-kit--table--row--alternative-color` | Hover / highlighted row background | `--sc-kit--color--bg--hover` |
|
|
@@ -185,10 +201,15 @@ preserve the current selection. For non-reorderable tables use `Table` (it ships
|
|
|
185
201
|
.table-container {
|
|
186
202
|
--_table--row--height: var(--sc-kit--table--row--height, var(--_table--size-row-height));
|
|
187
203
|
--_table--cell--font-size: var(--sc-kit--table--cell--font-size, var(--_table--size-cell-font-size));
|
|
188
|
-
--_table--cell--
|
|
204
|
+
--_table--cell--padding-inline: var(--sc-kit--table--cell--padding-inline, var(--_table--size-cell-padding));
|
|
205
|
+
--_table--cell--padding-block: var(--sc-kit--table--cell--padding-block, 0);
|
|
206
|
+
--_table--cell--vertical-align: var(--sc-kit--table--cell--vertical-align, middle);
|
|
189
207
|
--_table--head--height: var(--sc-kit--table--head--height, var(--_table--size-head-height, var(--_table--row--height)));
|
|
190
208
|
--_table--head--background-color: var(--sc-kit--table--head--background-color, var(--sc-kit--color--bg--field-alt));
|
|
191
209
|
--_table--head--color: var(--sc-kit--table--head--color, var(--sc-kit--color--text--secondary));
|
|
210
|
+
--_table--head--font-weight: var(--sc-kit--table--head--font-weight, var(--sc-kit--font-weight--semibold));
|
|
211
|
+
--_table--head--letter-spacing: var(--sc-kit--table--head--letter-spacing, normal);
|
|
212
|
+
--_table--head--text-transform: var(--sc-kit--table--head--text-transform, none);
|
|
192
213
|
--_table--border-color: var(--sc-kit--table--border-color, var(--sc-kit--color--border));
|
|
193
214
|
--_table--row--color: var(--sc-kit--table--row--color, transparent);
|
|
194
215
|
--_table--row--alternative-color: var(--sc-kit--table--row--alternative-color, var(--sc-kit--color--bg--hover));
|
|
@@ -249,16 +270,21 @@ preserve the current selection. For non-reorderable tables use `Table` (it ships
|
|
|
249
270
|
border-collapse: separate;
|
|
250
271
|
border-spacing: 0;
|
|
251
272
|
}
|
|
273
|
+
.table--fixed {
|
|
274
|
+
table-layout: fixed;
|
|
275
|
+
}
|
|
252
276
|
.table__head {
|
|
253
277
|
height: var(--_table--head--height);
|
|
254
278
|
max-height: var(--_table--head--height);
|
|
255
279
|
background: var(--_table--head--background-color);
|
|
256
280
|
color: var(--_table--head--color);
|
|
257
281
|
font-size: var(--sc-kit--table--head--font-size, var(--sc-kit--font-size--sm));
|
|
258
|
-
|
|
282
|
+
letter-spacing: var(--_table--head--letter-spacing);
|
|
283
|
+
text-transform: var(--_table--head--text-transform);
|
|
259
284
|
}
|
|
260
285
|
.table__th {
|
|
261
|
-
padding: 0 var(--_table--cell--
|
|
286
|
+
padding: 0 var(--_table--cell--padding-inline);
|
|
287
|
+
font-weight: var(--_table--head--font-weight);
|
|
262
288
|
text-align: left;
|
|
263
289
|
vertical-align: middle;
|
|
264
290
|
border-bottom: 1px solid var(--_table--border-color);
|
|
@@ -269,8 +295,7 @@ preserve the current selection. For non-reorderable tables use `Table` (it ships
|
|
|
269
295
|
user-select: text;
|
|
270
296
|
}
|
|
271
297
|
.table__th--centered {
|
|
272
|
-
|
|
273
|
-
--_table-header--justify-content: center;
|
|
298
|
+
--_--table-header--justify-content: center;
|
|
274
299
|
}
|
|
275
300
|
.table__tr {
|
|
276
301
|
height: var(--_table--row--height);
|
|
@@ -282,7 +307,14 @@ preserve the current selection. For non-reorderable tables use `Table` (it ships
|
|
|
282
307
|
}
|
|
283
308
|
.table__tr:hover, .table__tr--highlighted {
|
|
284
309
|
background: var(--_table--row--alternative-color);
|
|
285
|
-
|
|
310
|
+
}
|
|
311
|
+
.table__tr:hover, .table__tr:focus-within {
|
|
312
|
+
--_table--row-reveal--opacity: 1;
|
|
313
|
+
}
|
|
314
|
+
@media (any-hover: none) {
|
|
315
|
+
.table__tr {
|
|
316
|
+
--_table--row-reveal--opacity: 1;
|
|
317
|
+
}
|
|
286
318
|
}
|
|
287
319
|
.table__tr--selected {
|
|
288
320
|
background: var(--_table--selected--color);
|
|
@@ -294,11 +326,11 @@ preserve the current selection. For non-reorderable tables use `Table` (it ships
|
|
|
294
326
|
background: var(--_table--row--color) !important;
|
|
295
327
|
}
|
|
296
328
|
.table__td {
|
|
297
|
-
padding: 0
|
|
329
|
+
padding: 0;
|
|
298
330
|
border-bottom: 1px solid var(--_table--border-color);
|
|
299
331
|
font-size: var(--_table--cell--font-size);
|
|
300
332
|
color: var(--sc-kit--color--text--primary);
|
|
301
|
-
vertical-align:
|
|
333
|
+
vertical-align: var(--_table--cell--vertical-align);
|
|
302
334
|
}
|
|
303
335
|
.table__td--centered {
|
|
304
336
|
text-align: center;
|
|
@@ -309,6 +341,13 @@ preserve the current selection. For non-reorderable tables use `Table` (it ships
|
|
|
309
341
|
.table__td--flush {
|
|
310
342
|
border-bottom: none;
|
|
311
343
|
}
|
|
344
|
+
.table__cell {
|
|
345
|
+
padding: var(--_table--cell--padding-block) var(--_table--cell--padding-inline);
|
|
346
|
+
}
|
|
347
|
+
.table__cell--reveal-on-row-hover {
|
|
348
|
+
opacity: var(--_table--row-reveal--opacity, 0);
|
|
349
|
+
transition: opacity var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
350
|
+
}
|
|
312
351
|
.table__placeholder {
|
|
313
352
|
display: flex;
|
|
314
353
|
align-items: center;
|
|
@@ -323,7 +362,8 @@ preserve the current selection. For non-reorderable tables use `Table` (it ships
|
|
|
323
362
|
}
|
|
324
363
|
|
|
325
364
|
.table__drag-handle {
|
|
326
|
-
|
|
365
|
+
--_table--cell--padding-inline: 0;
|
|
366
|
+
width: 3.25rem;
|
|
327
367
|
cursor: grab;
|
|
328
368
|
text-align: center;
|
|
329
369
|
--sc-kit--icon--size: 1.25rem;
|
|
@@ -58,15 +58,23 @@ interface $$IsomorphicComponent {
|
|
|
58
58
|
* | `zebra` | `boolean` | Alternates row backgrounds with `--sc-kit--color--bg--field-alt` |
|
|
59
59
|
* | `stickyHeader` | `boolean` | Sticks `thead` to the scrolling ancestor |
|
|
60
60
|
* | `actionsPopoverTopDirection` | `boolean` | Forces the per-row actions popover above instead of below |
|
|
61
|
+
* | `layout` | `'auto' \| 'fixed'` | `'fixed'` honours `cssWidth` exactly; percent columns share what fixed ones leave, `minCssWidth` is ignored (default `'auto'`) |
|
|
61
62
|
*
|
|
62
63
|
* ### CSS Custom Properties
|
|
63
64
|
* | Property | Description | Default |
|
|
64
65
|
* |---|---|---|
|
|
65
|
-
* | `--sc-kit--table--row--height` | Row height | per size (
|
|
66
|
-
* | `--sc-kit--table--cell--
|
|
66
|
+
* | `--sc-kit--table--row--height` | Row height | per size (36 / 44 px) |
|
|
67
|
+
* | `--sc-kit--table--cell--padding-inline` | Cell inline padding | per size (`--space--3` / `--space--4`) |
|
|
67
68
|
* | `--sc-kit--table--cell--font-size` | Cell font size | per size (`--font-size--sm` / `--md`) |
|
|
69
|
+
* | `--sc-kit--table--cell--padding-block` | Cell block padding | `0` |
|
|
70
|
+
* | `--sc-kit--table--cell--vertical-align` | Cell content alignment — `top` keeps inputs aligned when one cell grows | `middle` |
|
|
71
|
+
* | `--sc-kit--table--head--height` | Header row height | per size (32 / 40 px) |
|
|
68
72
|
* | `--sc-kit--table--head--background-color` | Header background | `--sc-kit--color--bg--field-alt` |
|
|
69
73
|
* | `--sc-kit--table--head--color` | Header text color | `--sc-kit--color--text--secondary` |
|
|
74
|
+
* | `--sc-kit--table--head--font-size` | Header font size | `--sc-kit--font-size--sm` |
|
|
75
|
+
* | `--sc-kit--table--head--font-weight` | Header font weight | `--sc-kit--font-weight--semibold` |
|
|
76
|
+
* | `--sc-kit--table--head--letter-spacing` | Header letter spacing | `normal` |
|
|
77
|
+
* | `--sc-kit--table--head--text-transform` | Header text transform | `none` |
|
|
70
78
|
* | `--sc-kit--table--border-color` | Border color between rows | `--sc-kit--color--border` |
|
|
71
79
|
* | `--sc-kit--table--row--color` | Row background | `transparent` |
|
|
72
80
|
* | `--sc-kit--table--row--alternative-color` | Hover / highlighted row background | `--sc-kit--color--bg--hover` |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { EmptyState } from '../empty-state';
|
|
3
3
|
import { TableCell, TableHideableCell } from './table-cells';
|
|
4
4
|
import { TableLocalization } from './table-localization';
|
|
5
|
-
import {
|
|
5
|
+
import { columnCellStyle, createTableView } from './table-view.svelte';
|
|
6
6
|
let { model, showAlternativeView = false, noItemsPlaceholderEnabled, noItemsPlaceholderDelay, grouping, on, alternativeView, customNoItemsPlaceholder } = $props();
|
|
7
7
|
const localization = new TableLocalization();
|
|
8
8
|
const view = createTableView(() => model, () => on, () => noItemsPlaceholderEnabled, () => noItemsPlaceholderDelay);
|
|
@@ -45,9 +45,7 @@ const visibleColspan = $derived(model.columns.filter((column) => !column.hidden)
|
|
|
45
45
|
{/if}
|
|
46
46
|
{/snippet}
|
|
47
47
|
|
|
48
|
-
<div
|
|
49
|
-
class="table-container table-container--{model.styles?.size ?? 'md'} table-container--{model.styles?.variant ?? 'plain'}"
|
|
50
|
-
class:table-container--zebra={model.styles?.zebra === true}>
|
|
48
|
+
<div class="table-container table-container--{model.styles?.size ?? 'md'} table-container--{model.styles?.variant ?? 'plain'}">
|
|
51
49
|
{#if alternativeView && showAlternativeView}
|
|
52
50
|
{#if view.showNoItemsPlaceholder}
|
|
53
51
|
<div class="alternative-view__placeholder">{@render noItemsContent()}</div>
|
|
@@ -55,12 +53,12 @@ const visibleColspan = $derived(model.columns.filter((column) => !column.hidden)
|
|
|
55
53
|
{@render alternativeView()}
|
|
56
54
|
{/if}
|
|
57
55
|
{:else}
|
|
58
|
-
<table class="table" class:table--sticky-header={model.styles?.stickyHeader === true}>
|
|
56
|
+
<table class="table" class:table--sticky-header={model.styles?.stickyHeader === true} class:table--fixed={model.styles?.layout === 'fixed'}>
|
|
59
57
|
<thead class="table__head">
|
|
60
58
|
<tr>
|
|
61
59
|
{#each model.columns as column (column)}
|
|
62
60
|
<TableHideableCell column={column}>
|
|
63
|
-
<th class="table__th" class:table__th--centered={column.centered} style={
|
|
61
|
+
<th class="table__th" class:table__th--centered={column.centered} style={columnCellStyle(column)} title={column.title}>
|
|
64
62
|
<DynamicComponent model={column.headerView} />
|
|
65
63
|
</th>
|
|
66
64
|
</TableHideableCell>
|
|
@@ -106,9 +104,11 @@ const visibleColspan = $derived(model.columns.filter((column) => !column.hidden)
|
|
|
106
104
|
class:table__td--centered={column.centered}
|
|
107
105
|
class:table__td--clickable={!!column.onClick}
|
|
108
106
|
class:table__td--flush={flushBottom}
|
|
109
|
-
style={
|
|
107
|
+
style={columnCellStyle(column)}
|
|
110
108
|
onclick={() => column.onClick && column.onClick(item)}>
|
|
111
|
-
<
|
|
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
112
|
</td>
|
|
113
113
|
</TableHideableCell>
|
|
114
114
|
{/each}
|
|
@@ -135,15 +135,23 @@ variant ships no dnd code).
|
|
|
135
135
|
| `zebra` | `boolean` | Alternates row backgrounds with `--sc-kit--color--bg--field-alt` |
|
|
136
136
|
| `stickyHeader` | `boolean` | Sticks `thead` to the scrolling ancestor |
|
|
137
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'`) |
|
|
138
139
|
|
|
139
140
|
### CSS Custom Properties
|
|
140
141
|
| Property | Description | Default |
|
|
141
142
|
|---|---|---|
|
|
142
|
-
| `--sc-kit--table--row--height` | Row height | per size (
|
|
143
|
-
| `--sc-kit--table--cell--
|
|
143
|
+
| `--sc-kit--table--row--height` | Row height | per size (36 / 44 px) |
|
|
144
|
+
| `--sc-kit--table--cell--padding-inline` | Cell inline padding | per size (`--space--3` / `--space--4`) |
|
|
144
145
|
| `--sc-kit--table--cell--font-size` | Cell font size | per size (`--font-size--sm` / `--md`) |
|
|
146
|
+
| `--sc-kit--table--cell--padding-block` | Cell block padding | `0` |
|
|
147
|
+
| `--sc-kit--table--cell--vertical-align` | Cell content alignment — `top` keeps inputs aligned when one cell grows | `middle` |
|
|
148
|
+
| `--sc-kit--table--head--height` | Header row height | per size (32 / 40 px) |
|
|
145
149
|
| `--sc-kit--table--head--background-color` | Header background | `--sc-kit--color--bg--field-alt` |
|
|
146
150
|
| `--sc-kit--table--head--color` | Header text color | `--sc-kit--color--text--secondary` |
|
|
151
|
+
| `--sc-kit--table--head--font-size` | Header font size | `--sc-kit--font-size--sm` |
|
|
152
|
+
| `--sc-kit--table--head--font-weight` | Header font weight | `--sc-kit--font-weight--semibold` |
|
|
153
|
+
| `--sc-kit--table--head--letter-spacing` | Header letter spacing | `normal` |
|
|
154
|
+
| `--sc-kit--table--head--text-transform` | Header text transform | `none` |
|
|
147
155
|
| `--sc-kit--table--border-color` | Border color between rows | `--sc-kit--color--border` |
|
|
148
156
|
| `--sc-kit--table--row--color` | Row background | `transparent` |
|
|
149
157
|
| `--sc-kit--table--row--alternative-color` | Hover / highlighted row background | `--sc-kit--color--bg--hover` |
|
|
@@ -166,10 +174,15 @@ variant ships no dnd code).
|
|
|
166
174
|
.table-container {
|
|
167
175
|
--_table--row--height: var(--sc-kit--table--row--height, var(--_table--size-row-height));
|
|
168
176
|
--_table--cell--font-size: var(--sc-kit--table--cell--font-size, var(--_table--size-cell-font-size));
|
|
169
|
-
--_table--cell--
|
|
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);
|
|
170
180
|
--_table--head--height: var(--sc-kit--table--head--height, var(--_table--size-head-height, var(--_table--row--height)));
|
|
171
181
|
--_table--head--background-color: var(--sc-kit--table--head--background-color, var(--sc-kit--color--bg--field-alt));
|
|
172
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);
|
|
173
186
|
--_table--border-color: var(--sc-kit--table--border-color, var(--sc-kit--color--border));
|
|
174
187
|
--_table--row--color: var(--sc-kit--table--row--color, transparent);
|
|
175
188
|
--_table--row--alternative-color: var(--sc-kit--table--row--alternative-color, var(--sc-kit--color--bg--hover));
|
|
@@ -230,16 +243,21 @@ variant ships no dnd code).
|
|
|
230
243
|
border-collapse: separate;
|
|
231
244
|
border-spacing: 0;
|
|
232
245
|
}
|
|
246
|
+
.table--fixed {
|
|
247
|
+
table-layout: fixed;
|
|
248
|
+
}
|
|
233
249
|
.table__head {
|
|
234
250
|
height: var(--_table--head--height);
|
|
235
251
|
max-height: var(--_table--head--height);
|
|
236
252
|
background: var(--_table--head--background-color);
|
|
237
253
|
color: var(--_table--head--color);
|
|
238
254
|
font-size: var(--sc-kit--table--head--font-size, var(--sc-kit--font-size--sm));
|
|
239
|
-
|
|
255
|
+
letter-spacing: var(--_table--head--letter-spacing);
|
|
256
|
+
text-transform: var(--_table--head--text-transform);
|
|
240
257
|
}
|
|
241
258
|
.table__th {
|
|
242
|
-
padding: 0 var(--_table--cell--
|
|
259
|
+
padding: 0 var(--_table--cell--padding-inline);
|
|
260
|
+
font-weight: var(--_table--head--font-weight);
|
|
243
261
|
text-align: left;
|
|
244
262
|
vertical-align: middle;
|
|
245
263
|
border-bottom: 1px solid var(--_table--border-color);
|
|
@@ -250,8 +268,7 @@ variant ships no dnd code).
|
|
|
250
268
|
user-select: text;
|
|
251
269
|
}
|
|
252
270
|
.table__th--centered {
|
|
253
|
-
|
|
254
|
-
--_table-header--justify-content: center;
|
|
271
|
+
--_--table-header--justify-content: center;
|
|
255
272
|
}
|
|
256
273
|
.table__tr {
|
|
257
274
|
height: var(--_table--row--height);
|
|
@@ -263,7 +280,14 @@ variant ships no dnd code).
|
|
|
263
280
|
}
|
|
264
281
|
.table__tr:hover, .table__tr--highlighted {
|
|
265
282
|
background: var(--_table--row--alternative-color);
|
|
266
|
-
|
|
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
|
+
}
|
|
267
291
|
}
|
|
268
292
|
.table__tr--selected {
|
|
269
293
|
background: var(--_table--selected--color);
|
|
@@ -275,11 +299,11 @@ variant ships no dnd code).
|
|
|
275
299
|
background: var(--_table--row--color) !important;
|
|
276
300
|
}
|
|
277
301
|
.table__td {
|
|
278
|
-
padding: 0
|
|
302
|
+
padding: 0;
|
|
279
303
|
border-bottom: 1px solid var(--_table--border-color);
|
|
280
304
|
font-size: var(--_table--cell--font-size);
|
|
281
305
|
color: var(--sc-kit--color--text--primary);
|
|
282
|
-
vertical-align:
|
|
306
|
+
vertical-align: var(--_table--cell--vertical-align);
|
|
283
307
|
}
|
|
284
308
|
.table__td--centered {
|
|
285
309
|
text-align: center;
|
|
@@ -290,6 +314,13 @@ variant ships no dnd code).
|
|
|
290
314
|
.table__td--flush {
|
|
291
315
|
border-bottom: none;
|
|
292
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
|
+
}
|
|
293
324
|
.table__placeholder {
|
|
294
325
|
display: flex;
|
|
295
326
|
align-items: center;
|
|
@@ -305,7 +336,7 @@ variant ships no dnd code).
|
|
|
305
336
|
|
|
306
337
|
.table__group-header {
|
|
307
338
|
padding-block: var(--sc-kit--space--6) var(--sc-kit--space--2);
|
|
308
|
-
padding-inline: var(--_table--cell--
|
|
339
|
+
padding-inline: var(--_table--cell--padding-inline);
|
|
309
340
|
font-size: var(--sc-kit--font-size--sm);
|
|
310
341
|
font-weight: var(--sc-kit--font-weight--semibold);
|
|
311
342
|
color: var(--sc-kit--color--text--secondary);
|
|
@@ -70,15 +70,23 @@ interface $$IsomorphicComponent {
|
|
|
70
70
|
* | `zebra` | `boolean` | Alternates row backgrounds with `--sc-kit--color--bg--field-alt` |
|
|
71
71
|
* | `stickyHeader` | `boolean` | Sticks `thead` to the scrolling ancestor |
|
|
72
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'`) |
|
|
73
74
|
*
|
|
74
75
|
* ### CSS Custom Properties
|
|
75
76
|
* | Property | Description | Default |
|
|
76
77
|
* |---|---|---|
|
|
77
|
-
* | `--sc-kit--table--row--height` | Row height | per size (
|
|
78
|
-
* | `--sc-kit--table--cell--
|
|
78
|
+
* | `--sc-kit--table--row--height` | Row height | per size (36 / 44 px) |
|
|
79
|
+
* | `--sc-kit--table--cell--padding-inline` | Cell inline padding | per size (`--space--3` / `--space--4`) |
|
|
79
80
|
* | `--sc-kit--table--cell--font-size` | Cell font size | per size (`--font-size--sm` / `--md`) |
|
|
81
|
+
* | `--sc-kit--table--cell--padding-block` | Cell block padding | `0` |
|
|
82
|
+
* | `--sc-kit--table--cell--vertical-align` | Cell content alignment — `top` keeps inputs aligned when one cell grows | `middle` |
|
|
83
|
+
* | `--sc-kit--table--head--height` | Header row height | per size (32 / 40 px) |
|
|
80
84
|
* | `--sc-kit--table--head--background-color` | Header background | `--sc-kit--color--bg--field-alt` |
|
|
81
85
|
* | `--sc-kit--table--head--color` | Header text color | `--sc-kit--color--text--secondary` |
|
|
86
|
+
* | `--sc-kit--table--head--font-size` | Header font size | `--sc-kit--font-size--sm` |
|
|
87
|
+
* | `--sc-kit--table--head--font-weight` | Header font weight | `--sc-kit--font-weight--semibold` |
|
|
88
|
+
* | `--sc-kit--table--head--letter-spacing` | Header letter spacing | `normal` |
|
|
89
|
+
* | `--sc-kit--table--head--text-transform` | Header text transform | `none` |
|
|
82
90
|
* | `--sc-kit--table--border-color` | Border color between rows | `--sc-kit--color--border` |
|
|
83
91
|
* | `--sc-kit--table--row--color` | Row background | `transparent` |
|
|
84
92
|
* | `--sc-kit--table--row--alternative-color` | Hover / highlighted row background | `--sc-kit--color--bg--hover` |
|
|
@@ -21,8 +21,6 @@ const onDown = () => on?.down?.();
|
|
|
21
21
|
gap: 0.125rem;
|
|
22
22
|
justify-content: space-evenly;
|
|
23
23
|
--sc-kit--icon--size: 1rem;
|
|
24
|
-
opacity: var(--_table--move-row-cell-opacity, 0);
|
|
25
|
-
transition: opacity var(--sc-kit--duration--base) var(--sc-kit--ease--default);
|
|
26
24
|
}
|
|
27
25
|
.move-row-cell button:disabled {
|
|
28
26
|
--sc-kit--icon--color: var(--sc-kit--color--text--muted);
|
|
@@ -44,7 +44,6 @@ Text cell for table rows. Supports plain text, date, date-time, and HTML formats
|
|
|
44
44
|
--_table-text-cell--clickable--color: var(--sc-kit--table-text-cell--clickable--color, var(--sc-kit--color--accent));
|
|
45
45
|
display: flex;
|
|
46
46
|
align-content: center;
|
|
47
|
-
font-size: var(--_table--cell--font-size);
|
|
48
47
|
}
|
|
49
48
|
.table-text-cell__single-line-no-trim {
|
|
50
49
|
white-space: nowrap;
|
|
@@ -24,6 +24,11 @@ export declare class ResolvedColumn<T extends WithId> {
|
|
|
24
24
|
get id(): keyof T;
|
|
25
25
|
get title(): string;
|
|
26
26
|
get centered(): boolean;
|
|
27
|
+
get revealOnRowHover(): boolean;
|
|
28
|
+
get cellPadding(): {
|
|
29
|
+
inline?: string;
|
|
30
|
+
block?: string;
|
|
31
|
+
};
|
|
27
32
|
get cssWidth(): string;
|
|
28
33
|
get minCssWidth(): string;
|
|
29
34
|
get hideable(): boolean;
|
|
@@ -31,6 +31,12 @@ export class ResolvedColumn {
|
|
|
31
31
|
get centered() {
|
|
32
32
|
return this.def.centered ?? false;
|
|
33
33
|
}
|
|
34
|
+
get revealOnRowHover() {
|
|
35
|
+
return this.def.revealOnRowHover ?? this.def.type === 'move-row';
|
|
36
|
+
}
|
|
37
|
+
get cellPadding() {
|
|
38
|
+
return this.def.cellPadding ?? {};
|
|
39
|
+
}
|
|
34
40
|
get cssWidth() {
|
|
35
41
|
return this.def.cssWidth ?? '';
|
|
36
42
|
}
|
|
@@ -18,7 +18,7 @@ const changeSort = (direction) => {
|
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
20
|
<TableHeader column={column}>
|
|
21
|
-
<div class="th-sortable"
|
|
21
|
+
<div class="th-sortable">
|
|
22
22
|
{@render children?.()}
|
|
23
23
|
<span> </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;
|
|
@@ -6,8 +6,8 @@ type SelectionCallbacks<T> = {
|
|
|
6
6
|
itemsSelected?: (e: T[]) => void;
|
|
7
7
|
itemsDeselected?: (e: T[]) => void;
|
|
8
8
|
};
|
|
9
|
-
/** Inline
|
|
10
|
-
export declare const
|
|
9
|
+
/** Inline declarations for a column's `th` / `td`: the preferred width, the floor the browser must honour, and the per-column padding override. */
|
|
10
|
+
export declare const columnCellStyle: <T extends WithId>(column: ResolvedColumn<T>) => string;
|
|
11
11
|
/** Shared presentation wiring for the table views: selection-event subscriptions + the no-items placeholder decision (on/off gate + anti-flicker delay). */
|
|
12
12
|
export declare const createTableView: <T extends WithId>(model: () => TableModel<T>, on: () => SelectionCallbacks<T> | undefined, noItemsPlaceholderEnabled?: () => boolean | undefined, noItemsPlaceholderDelay?: () => number | undefined) => {
|
|
13
13
|
readonly showNoItemsPlaceholder: boolean;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { untrack } from 'svelte';
|
|
2
|
-
/** Inline
|
|
3
|
-
export const
|
|
4
|
-
const
|
|
5
|
-
return
|
|
2
|
+
/** Inline declarations for a column's `th` / `td`: the preferred width, the floor the browser must honour, and the per-column padding override. */
|
|
3
|
+
export const columnCellStyle = (column) => {
|
|
4
|
+
const { inline, block } = column.cellPadding;
|
|
5
|
+
return [
|
|
6
|
+
column.cssWidth ? `width: ${column.cssWidth};` : 'width: auto;',
|
|
7
|
+
column.minCssWidth ? `min-width: ${column.minCssWidth};` : '',
|
|
8
|
+
inline ? `--_table--cell--padding-inline: ${inline};` : '',
|
|
9
|
+
block ? `--_table--cell--padding-block: ${block};` : ''
|
|
10
|
+
]
|
|
11
|
+
.filter(Boolean)
|
|
12
|
+
.join(' ');
|
|
6
13
|
};
|
|
7
14
|
/** Shared presentation wiring for the table views: selection-event subscriptions + the no-items placeholder decision (on/off gate + anti-flicker delay). */
|
|
8
15
|
export const createTableView = (model, on, noItemsPlaceholderEnabled = () => undefined, noItemsPlaceholderDelay = () => undefined) => {
|
package/dist/ui/table/types.d.ts
CHANGED
|
@@ -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;
|
|
@@ -122,6 +132,13 @@ export type TableStyles = {
|
|
|
122
132
|
zebra?: boolean;
|
|
123
133
|
/** Visual chrome around the table. `'plain'` blends with surrounding layout; `'card'` adds a bordered, rounded panel. @default 'plain' */
|
|
124
134
|
variant?: 'plain' | 'card';
|
|
135
|
+
/**
|
|
136
|
+
* Column width algorithm. `'auto'` treats `cssWidth` as a hint and lets content redistribute space. `'fixed'` honours `cssWidth` exactly and wraps or
|
|
137
|
+
* 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 a `min-width` wrapper inside a horizontally scrolling container instead.
|
|
139
|
+
* @default 'auto'
|
|
140
|
+
*/
|
|
141
|
+
layout?: 'auto' | 'fixed';
|
|
125
142
|
};
|
|
126
143
|
export type TableOrderByState<TOrder> = {
|
|
127
144
|
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,
|
|
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}
|
|
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
|
|
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
|
|
@@ -10,6 +10,7 @@ export type FieldBinding<V> = {
|
|
|
10
10
|
};
|
|
11
11
|
};
|
|
12
12
|
import type { FormValidationHandler } from '../../core/validation';
|
|
13
|
+
import type { ErrorPlacement } from './types';
|
|
13
14
|
import type { Snippet } from 'svelte';
|
|
14
15
|
declare function $$render<T extends Record<string, unknown>, K extends keyof T & string>(): {
|
|
15
16
|
props: {
|
|
@@ -31,11 +32,11 @@ declare function $$render<T extends Record<string, unknown>, K extends keyof T &
|
|
|
31
32
|
/** Show errors even when the field has not been touched. @default false */
|
|
32
33
|
disableTouchedTracking?: boolean;
|
|
33
34
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* @default
|
|
35
|
+
* Where the error message sits: `'overlay'` covers whatever is below the field, `'reserved'` keeps a
|
|
36
|
+
* permanent strip so toggling never shifts layout, `'inline'` grows the field only while an error is shown.
|
|
37
|
+
* @default 'overlay'
|
|
37
38
|
*/
|
|
38
|
-
|
|
39
|
+
errorPlacement?: ErrorPlacement;
|
|
39
40
|
/**
|
|
40
41
|
* Extra consumer callbacks. Run AFTER the validation wiring; they cannot override it.
|
|
41
42
|
* The `children` snippet always receives fully-wired `field.on.*`.
|
|
@@ -73,8 +74,7 @@ interface $$IsomorphicComponent {
|
|
|
73
74
|
/**
|
|
74
75
|
* Binds a form field to any kit input via a render-prop snippet. The base inputs stay unchanged;
|
|
75
76
|
* `Validatable` is the only place that knows about `FormValidationHandler`. Wraps children in an
|
|
76
|
-
* internal `FieldFrame` that
|
|
77
|
-
* layout; `reserveErrorSpace={false}` drops the reserve — the message overlays content below.
|
|
77
|
+
* internal `FieldFrame` that renders the error message below the field, placed per `errorPlacement`.
|
|
78
78
|
*
|
|
79
79
|
* ### Usage
|
|
80
80
|
* ```svelte
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts" generics="T extends Record<string, unknown>">import { default as FieldFrame } from './field-frame.svelte';
|
|
2
|
-
let { name, validationResult,
|
|
2
|
+
let { name, validationResult, errorPlacement = 'overlay', children } = $props();
|
|
3
3
|
const errorText = $derived(validationResult?.errors[name] ?? null);
|
|
4
4
|
</script>
|
|
5
5
|
|
|
6
|
-
<FieldFrame error={errorText}
|
|
6
|
+
<FieldFrame error={errorText} errorPlacement={errorPlacement}>
|
|
7
7
|
{@render children()}
|
|
8
8
|
</FieldFrame>
|
|
9
9
|
|
|
@@ -11,6 +11,6 @@ const errorText = $derived(validationResult?.errors[name] ?? null);
|
|
|
11
11
|
@component
|
|
12
12
|
Wraps a form field and displays validation errors from a `FormValidateResult` object. Stateless
|
|
13
13
|
sibling of `Validatable` for cases where the consumer already has the validation result in hand
|
|
14
|
-
(e.g. server-validated forms) and does not need a `FormValidationHandler`.
|
|
15
|
-
|
|
14
|
+
(e.g. server-validated forms) and does not need a `FormValidationHandler`. `errorPlacement` picks
|
|
15
|
+
where the message sits — same contract as `Validatable`.
|
|
16
16
|
-->
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FormValidateResult } from '../../core/validation';
|
|
2
|
+
import type { ErrorPlacement } from './types';
|
|
2
3
|
import type { Snippet } from 'svelte';
|
|
3
4
|
declare function $$render<T extends Record<string, unknown>>(): {
|
|
4
5
|
props: {
|
|
@@ -7,11 +8,11 @@ declare function $$render<T extends Record<string, unknown>>(): {
|
|
|
7
8
|
/** Validation result object containing error messages */
|
|
8
9
|
validationResult?: FormValidateResult<T>;
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @default
|
|
11
|
+
* Where the error message sits: `'overlay'` covers whatever is below the field, `'reserved'` keeps a
|
|
12
|
+
* permanent strip so toggling never shifts layout, `'inline'` grows the field only while an error is shown.
|
|
13
|
+
* @default 'overlay'
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
+
errorPlacement?: ErrorPlacement;
|
|
15
16
|
children: Snippet;
|
|
16
17
|
};
|
|
17
18
|
exports: {};
|
|
@@ -36,8 +37,8 @@ interface $$IsomorphicComponent {
|
|
|
36
37
|
/**
|
|
37
38
|
* Wraps a form field and displays validation errors from a `FormValidateResult` object. Stateless
|
|
38
39
|
* sibling of `Validatable` for cases where the consumer already has the validation result in hand
|
|
39
|
-
* (e.g. server-validated forms) and does not need a `FormValidationHandler`.
|
|
40
|
-
*
|
|
40
|
+
* (e.g. server-validated forms) and does not need a `FormValidationHandler`. `errorPlacement` picks
|
|
41
|
+
* where the message sits — same contract as `Validatable`.
|
|
41
42
|
*/
|
|
42
43
|
declare const Cmp: $$IsomorphicComponent;
|
|
43
44
|
type Cmp<T extends Record<string, unknown>> = InstanceType<typeof Cmp<T>>;
|
|
@@ -1,40 +1,50 @@
|
|
|
1
|
-
<script lang="ts">let { error,
|
|
1
|
+
<script lang="ts">let { error, errorPlacement = 'overlay', children } = $props();
|
|
2
2
|
export {};
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
|
-
<div class="field-frame" class:field-frame--
|
|
5
|
+
<div class="field-frame" class:field-frame--reserved={errorPlacement === 'reserved'}>
|
|
6
6
|
{@render children()}
|
|
7
7
|
{#if error}
|
|
8
|
-
<small class="field-frame__message">{error}</small>
|
|
8
|
+
<small class="field-frame__message field-frame__message--{errorPlacement}">{error}</small>
|
|
9
9
|
{/if}
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
12
|
<!--
|
|
13
13
|
@component
|
|
14
|
-
Internal helper — wraps a form field and renders its error message
|
|
15
|
-
below,
|
|
16
|
-
`
|
|
17
|
-
|
|
18
|
-
any other field-shaped kit consumer that needs the same visual contract).
|
|
14
|
+
Internal helper — wraps a form field and renders its error message below it, placed per `errorPlacement`: overlaying
|
|
15
|
+
the content below, inside a permanently reserved strip, or in normal flow so the field grows only while the error is
|
|
16
|
+
shown. Used internally by `Validatable` / `ValidationError` (and any other field-shaped kit consumer that needs the
|
|
17
|
+
same visual contract).
|
|
19
18
|
|
|
20
19
|
Not exported from `index.ts` — for kit-internal use only.
|
|
21
20
|
-->
|
|
22
21
|
|
|
23
22
|
<style>.field-frame {
|
|
24
23
|
position: relative;
|
|
25
|
-
margin-bottom: 1.1em;
|
|
26
24
|
display: block;
|
|
27
25
|
}
|
|
28
|
-
.field-frame--
|
|
29
|
-
|
|
26
|
+
.field-frame--reserved {
|
|
27
|
+
padding-block-end: 1.1rem;
|
|
30
28
|
}
|
|
31
29
|
.field-frame__message {
|
|
32
|
-
position: absolute;
|
|
33
|
-
top: calc(100% + 0.1em);
|
|
34
|
-
left: 0;
|
|
35
|
-
right: 0;
|
|
36
30
|
display: block;
|
|
37
31
|
color: var(--sc-kit--color--danger);
|
|
38
32
|
font-size: 0.6875rem;
|
|
33
|
+
line-height: 1.5;
|
|
39
34
|
font-weight: 400;
|
|
35
|
+
}
|
|
36
|
+
.field-frame__message--overlay {
|
|
37
|
+
position: absolute;
|
|
38
|
+
top: calc(100% + 0.0688rem);
|
|
39
|
+
left: 0;
|
|
40
|
+
right: 0;
|
|
41
|
+
}
|
|
42
|
+
.field-frame__message--reserved {
|
|
43
|
+
position: absolute;
|
|
44
|
+
bottom: 0;
|
|
45
|
+
left: 0;
|
|
46
|
+
right: 0;
|
|
47
|
+
}
|
|
48
|
+
.field-frame__message--inline {
|
|
49
|
+
margin-top: 0.0688rem;
|
|
40
50
|
}</style>
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
+
import type { ErrorPlacement } from './types';
|
|
1
2
|
import type { Snippet } from 'svelte';
|
|
2
3
|
type Props = {
|
|
3
|
-
/** Error message to display below the field. Falsy hides the message
|
|
4
|
+
/** Error message to display below the field. Falsy hides the message. */
|
|
4
5
|
error?: string | null | undefined;
|
|
5
|
-
/**
|
|
6
|
-
|
|
6
|
+
/** Where the error message sits relative to the field. @default 'overlay' */
|
|
7
|
+
errorPlacement?: ErrorPlacement;
|
|
7
8
|
children: Snippet;
|
|
8
9
|
};
|
|
9
10
|
/**
|
|
10
|
-
* Internal helper — wraps a form field and renders its error message
|
|
11
|
-
* below,
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
14
|
-
* any other field-shaped kit consumer that needs the same visual contract).
|
|
11
|
+
* Internal helper — wraps a form field and renders its error message below it, placed per `errorPlacement`: overlaying
|
|
12
|
+
* the content below, inside a permanently reserved strip, or in normal flow so the field grows only while the error is
|
|
13
|
+
* shown. Used internally by `Validatable` / `ValidationError` (and any other field-shaped kit consumer that needs the
|
|
14
|
+
* same visual contract).
|
|
15
15
|
*
|
|
16
16
|
* Not exported from `index.ts` — for kit-internal use only.
|
|
17
17
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { default as Validatable } from './cmp.validatable.svelte';
|
|
2
2
|
export type { FieldBinding, ValidateOnEvent } from './cmp.validatable.svelte';
|
|
3
3
|
export { default as ValidationError } from './cmp.validation-error.svelte';
|
|
4
|
+
export type { ErrorPlacement } from './types';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a field's validation message sits.
|
|
3
|
+
* - `'overlay'` — absolutely positioned below the field; no space reserved, the message covers whatever sits below.
|
|
4
|
+
* - `'reserved'` — absolutely positioned inside a permanently reserved strip, so toggling the error never shifts layout.
|
|
5
|
+
* - `'inline'` — in normal flow, only while an error is shown: the field grows on error and shrinks back when it clears.
|
|
6
|
+
*/
|
|
7
|
+
export type ErrorPlacement = 'overlay' | 'reserved' | 'inline';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|