@svelte-lean/table 0.13.3 → 0.14.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/CHANGELOG.md +19 -0
- package/README.md +3 -2
- package/dist/style.css +56 -17
- package/dist/table/data-table.svelte +12 -5
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @svelte-lean/table
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6e9ed18: The theme hooks follow the palette names: `--slean-table-accent` is renamed `--slean-table-primary` and `--slean-table-danger` is renamed `--slean-table-error` (breaking in 0.x; no alias).
|
|
8
|
+
- ab929e6: `DataTable` takes `refreshing` for a quiet background refresh (a poll, an invalidation): the root carries `data-refreshing` and the stylesheet draws a thin line along the top edge (`--slean-table-refreshing-color`, the accent; `--slean-table-refreshing-height`, 2px), which pulses only when the user has not asked for reduced motion. Rows stay as they are: no skeleton, no dimming, no `aria-busy`, no status text, and the page clamp stays active. `loading` and `fetching` win over it. New token `--slean-table-skeleton-delay` (default `0s`) keeps skeleton rows invisible for that long with a CSS animation delay and no timer, so an answer faster than the delay shows no skeleton flash.
|
|
9
|
+
|
|
10
|
+
Size budget raised for this feature: the table stylesheet from 7950 B to 8250 B gzip (8102 B measured, was 7873 B). The renderer stays within its budget (22734 B of 22800 B).
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- a9d1e1f: Follows the renamed `@svelte-lean/styles` tokens (`--slean-primary-main`, `--slean-error-main`, `--slean-elevation-*`, …; fallbacks kept) and writes `data-size="small"` on the page-size select (ADR 0011 §3.3).
|
|
15
|
+
|
|
16
|
+
## 0.13.4
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- c97ea03: A grid-layout table with grouped columns draws its header rows straight again: a header row kept the cells that span down to it after the grouped ones, and grid auto-placement, which never moves back along a row, pushed the grouped leaves onto a second line. The cells are now in column order, and the room a spanning cell leaves above it takes the header's surface.
|
|
21
|
+
|
|
3
22
|
## 0.13.3
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -70,7 +70,8 @@ Composition hooks:
|
|
|
70
70
|
- Server tables: `manual` with `rowCount`, or `pageCount` when the total is unknown; `searchDelay`
|
|
71
71
|
debounces the search box and the filter inputs; `searchText` replaces what a row is searched by;
|
|
72
72
|
`state.search` applies without the table's own search box. `loading` without rows draws
|
|
73
|
-
`skeletonRows
|
|
73
|
+
`skeletonRows` (held invisible for `--slean-table-skeleton-delay`); `fetching` dims the body;
|
|
74
|
+
`refreshing` (a poll, an invalidation) only draws a thin line along the top edge.
|
|
74
75
|
- `pageSizeOptions`, numbered pages, `placeholder` for empty values, `headerClass`, per-row `class`.
|
|
75
76
|
|
|
76
77
|
## Below the component
|
|
@@ -117,7 +118,7 @@ import '@svelte-lean/table/style.css';
|
|
|
117
118
|
|
|
118
119
|
`style.css` styles the `slean-table-` classes. Its rules sit in the `slean.components` cascade layer
|
|
119
120
|
with `:where()` selectors, so unlayered application CSS overrides them without `!important`. It reads
|
|
120
|
-
`@svelte-lean/styles` tokens (`--slean-surface`, `--slean-fg`, `--slean-border`, `--slean-
|
|
121
|
+
`@svelte-lean/styles` tokens (`--slean-surface`, `--slean-fg`, `--slean-border`, `--slean-primary-main`,
|
|
121
122
|
`--slean-radius-*`, …) when they are defined; the fallbacks are the same defaults written with
|
|
122
123
|
`light-dark()`, so they follow the page's `color-scheme`. Override any `--slean-table-*` property
|
|
123
124
|
(`--slean-table-header-bg`, `--slean-table-row-hover`, `--slean-table-cell-padding-y`, … listed on
|
package/dist/style.css
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
inline-size: 1rem;
|
|
14
14
|
block-size: 1rem;
|
|
15
15
|
margin: 0;
|
|
16
|
-
accent-color: var(--slean-table-
|
|
16
|
+
accent-color: var(--slean-table-primary);
|
|
17
17
|
vertical-align: middle;
|
|
18
18
|
cursor: pointer;
|
|
19
19
|
}
|
|
@@ -35,15 +35,18 @@
|
|
|
35
35
|
--slean-muted,
|
|
36
36
|
light-dark(oklch(95.8% 0.004 260), oklch(26% 0.011 260))
|
|
37
37
|
);
|
|
38
|
-
--slean-table-
|
|
38
|
+
--slean-table-primary: var(
|
|
39
|
+
--slean-primary-main,
|
|
40
|
+
light-dark(oklch(54% 0.19 258), oklch(56% 0.18 258))
|
|
41
|
+
);
|
|
39
42
|
--slean-table-selected: var(
|
|
40
|
-
--slean-
|
|
43
|
+
--slean-primary-soft,
|
|
41
44
|
light-dark(oklch(95% 0.03 258), oklch(28% 0.07 258))
|
|
42
45
|
);
|
|
43
|
-
--slean-table-
|
|
44
|
-
--slean-table-focus: var(--slean-focus-ring-color, var(--slean-table-
|
|
46
|
+
--slean-table-error: var(--slean-error-main, light-dark(oklch(55% 0.2 25), oklch(58% 0.19 25)));
|
|
47
|
+
--slean-table-focus: var(--slean-focus-ring-color, var(--slean-table-primary));
|
|
45
48
|
--slean-table-shadow: var(
|
|
46
|
-
--slean-
|
|
49
|
+
--slean-elevation-4,
|
|
47
50
|
0 16px 40px oklch(0% 0 0 / 0.18),
|
|
48
51
|
0 2px 6px oklch(0% 0 0 / 0.08)
|
|
49
52
|
);
|
|
@@ -78,11 +81,14 @@
|
|
|
78
81
|
--slean-table-row-selected: var(--slean-table-selected);
|
|
79
82
|
--slean-table-pin-shadow: light-dark(oklch(0% 0 0 / 0.1), oklch(0% 0 0 / 0.45));
|
|
80
83
|
--slean-table-row-lifted-shadow: var(
|
|
81
|
-
--slean-
|
|
84
|
+
--slean-elevation-3,
|
|
82
85
|
0 4px 12px light-dark(oklch(0% 0 0 / 0.1), oklch(0% 0 0 / 0.5)),
|
|
83
86
|
0 1px 3px light-dark(oklch(0% 0 0 / 0.08), oklch(0% 0 0 / 0.4))
|
|
84
87
|
);
|
|
85
88
|
--slean-table-fetching-opacity: 0.55;
|
|
89
|
+
--slean-table-skeleton-delay: 0s;
|
|
90
|
+
--slean-table-refreshing-color: var(--slean-table-primary);
|
|
91
|
+
--slean-table-refreshing-height: 2px;
|
|
86
92
|
/* Set per row by hover, stripes, selection or `rowClass`; reset here so it never leaks into a
|
|
87
93
|
table nested in a detail row. */
|
|
88
94
|
--slean-table-row-bg: initial;
|
|
@@ -162,10 +168,7 @@
|
|
|
162
168
|
:where(.slean-table-input:focus-visible) {
|
|
163
169
|
border-color: var(--slean-table-focus);
|
|
164
170
|
outline: 2px solid transparent;
|
|
165
|
-
box-shadow: var(
|
|
166
|
-
--slean-control-ring,
|
|
167
|
-
0 0 0 3px color-mix(in oklab, var(--slean-table-focus) 22%, transparent)
|
|
168
|
-
);
|
|
171
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--slean-table-focus) 22%, transparent);
|
|
169
172
|
}
|
|
170
173
|
:where(
|
|
171
174
|
.slean-table-button,
|
|
@@ -451,7 +454,7 @@
|
|
|
451
454
|
touch-action: none;
|
|
452
455
|
}
|
|
453
456
|
:where(.slean-table-resize:hover, .slean-table-resize:focus-visible) {
|
|
454
|
-
background: linear-gradient(var(--slean-table-
|
|
457
|
+
background: linear-gradient(var(--slean-table-primary), var(--slean-table-primary)) center / 2px
|
|
455
458
|
100% no-repeat;
|
|
456
459
|
}
|
|
457
460
|
:where(.slean-table-filter) {
|
|
@@ -500,7 +503,7 @@
|
|
|
500
503
|
stroke-width: 2.5;
|
|
501
504
|
}
|
|
502
505
|
|
|
503
|
-
/* Row controls: 24px
|
|
506
|
+
/* Row controls: 24px borderless icon buttons. */
|
|
504
507
|
:where(.slean-table-expand, .slean-table-edit, .slean-table-grip) {
|
|
505
508
|
min-inline-size: 1.5rem;
|
|
506
509
|
min-block-size: 1.5rem;
|
|
@@ -652,7 +655,7 @@
|
|
|
652
655
|
}
|
|
653
656
|
:where(.slean-table-scroll > table > tbody > tr > td[data-range='true']) {
|
|
654
657
|
background: var(--slean-table-selected);
|
|
655
|
-
box-shadow: inset 0 0 0 1px var(--slean-table-
|
|
658
|
+
box-shadow: inset 0 0 0 1px var(--slean-table-primary);
|
|
656
659
|
}
|
|
657
660
|
|
|
658
661
|
/* A dragged row (row-drag.ts): opaque and raised over the body, below the sticky header (5);
|
|
@@ -713,13 +716,31 @@
|
|
|
713
716
|
:where(.slean-table[data-fetching] > .slean-table-scroll > table > tbody) {
|
|
714
717
|
opacity: var(--slean-table-fetching-opacity);
|
|
715
718
|
}
|
|
719
|
+
/* Skeleton rows stay invisible for the delay (no timer); `refreshing` draws a top line. */
|
|
720
|
+
:where(.slean-table-skeleton-row) {
|
|
721
|
+
animation: slean-table-appear 0s var(--slean-table-skeleton-delay) both;
|
|
722
|
+
}
|
|
723
|
+
:where(.slean-table[data-refreshing]) {
|
|
724
|
+
position: relative;
|
|
725
|
+
}
|
|
726
|
+
:where(.slean-table[data-refreshing])::after {
|
|
727
|
+
content: '';
|
|
728
|
+
position: absolute;
|
|
729
|
+
inset-block-start: 0;
|
|
730
|
+
inset-inline: var(--slean-table-radius);
|
|
731
|
+
z-index: 6;
|
|
732
|
+
block-size: var(--slean-table-refreshing-height);
|
|
733
|
+
background: var(--slean-table-refreshing-color);
|
|
734
|
+
opacity: 0.6;
|
|
735
|
+
pointer-events: none;
|
|
736
|
+
}
|
|
716
737
|
:where(.slean-table-error) {
|
|
717
738
|
display: flex;
|
|
718
739
|
align-items: center;
|
|
719
740
|
gap: 0.75rem;
|
|
720
741
|
padding: 0.625rem 0.75rem;
|
|
721
|
-
color: var(--slean-table-
|
|
722
|
-
background: color-mix(in oklab, var(--slean-table-
|
|
742
|
+
color: var(--slean-table-error);
|
|
743
|
+
background: color-mix(in oklab, var(--slean-table-error) 8%, var(--slean-table-bg));
|
|
723
744
|
}
|
|
724
745
|
:where(.slean-table-editor) {
|
|
725
746
|
display: flex;
|
|
@@ -732,7 +753,7 @@
|
|
|
732
753
|
min-width: 4.5rem;
|
|
733
754
|
}
|
|
734
755
|
:where(.slean-table-editor > [role='alert']) {
|
|
735
|
-
color: var(--slean-table-
|
|
756
|
+
color: var(--slean-table-error);
|
|
736
757
|
width: 100%;
|
|
737
758
|
font-size: 0.75rem;
|
|
738
759
|
}
|
|
@@ -863,6 +884,20 @@
|
|
|
863
884
|
0 0 / 200% 100% var(--slean-table-soft);
|
|
864
885
|
animation: slean-table-shimmer 1.6s linear infinite;
|
|
865
886
|
}
|
|
887
|
+
/* The line pulses; with reduced motion it stays still. */
|
|
888
|
+
:where(.slean-table[data-refreshing])::after {
|
|
889
|
+
animation: slean-table-pulse 0.8s ease-in-out infinite alternate;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
@keyframes slean-table-appear {
|
|
893
|
+
from {
|
|
894
|
+
opacity: 0;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
@keyframes slean-table-pulse {
|
|
898
|
+
from {
|
|
899
|
+
opacity: 0.2;
|
|
900
|
+
}
|
|
866
901
|
}
|
|
867
902
|
@keyframes slean-table-shimmer {
|
|
868
903
|
from {
|
|
@@ -936,6 +971,10 @@
|
|
|
936
971
|
}
|
|
937
972
|
/* Each body row its own stacking context: sticky pinned cells then layerize per row, not per
|
|
938
973
|
cell, which keeps compositing cheap while a long page scrolls. */
|
|
974
|
+
/* A header row is a grid too: the room a spanning cell leaves above it is the head's surface. */
|
|
975
|
+
:where(.slean-table[data-layout='grid'] > .slean-table-scroll > table > thead > tr) {
|
|
976
|
+
background: var(--slean-table-header-bg);
|
|
977
|
+
}
|
|
939
978
|
:where(.slean-table[data-layout='grid'] > .slean-table-scroll > table > tbody > tr) {
|
|
940
979
|
isolation: isolate;
|
|
941
980
|
}
|
|
@@ -111,6 +111,7 @@
|
|
|
111
111
|
onExpandedRowsChange,
|
|
112
112
|
loading = false,
|
|
113
113
|
fetching = false,
|
|
114
|
+
refreshing = false,
|
|
114
115
|
skeletonRows,
|
|
115
116
|
measureKey,
|
|
116
117
|
error,
|
|
@@ -776,7 +777,8 @@
|
|
|
776
777
|
/**
|
|
777
778
|
* Header rows. In grid layout every row is a grid of its own, so a cell spanning rows sits in
|
|
778
779
|
* the lowest row it spans, placed at its first column; the leading control headers sit there
|
|
779
|
-
* too.
|
|
780
|
+
* too. A row's cells are kept in column order: auto-placement never moves back along a row, so
|
|
781
|
+
* a cell placed before one to its left would push that one onto a second grid row.
|
|
780
782
|
*/
|
|
781
783
|
const leafIndex = $derived(new Map(cols.map((col, i) => [col.id, i])));
|
|
782
784
|
function firstLeaf(column: Column<T>): number {
|
|
@@ -787,15 +789,19 @@
|
|
|
787
789
|
const levels = table.headers;
|
|
788
790
|
if (!grid) return levels.map((row) => row.map((cell) => ({ cell })));
|
|
789
791
|
const lead = leads.length;
|
|
790
|
-
const result: { cell: HeaderCell<T>; place?: string }[][] = levels.map(() => []);
|
|
792
|
+
const result: { cell: HeaderCell<T>; place?: string; start: number }[][] = levels.map(() => []);
|
|
791
793
|
levels.forEach((row, level) =>
|
|
792
794
|
row.forEach((cell) => {
|
|
793
795
|
const target = Math.min(levels.length - 1, level + cell.rowSpan - 1);
|
|
794
796
|
const start = lead + firstLeaf(cell.column) + 1;
|
|
795
|
-
result[target]!.push({
|
|
797
|
+
result[target]!.push({
|
|
798
|
+
cell,
|
|
799
|
+
start,
|
|
800
|
+
place: `grid-column:${start} / span ${cell.colSpan};`
|
|
801
|
+
});
|
|
796
802
|
})
|
|
797
803
|
);
|
|
798
|
-
return result;
|
|
804
|
+
return result.map((row) => row.sort((a, b) => a.start - b.start));
|
|
799
805
|
});
|
|
800
806
|
const leadLevel = $derived(grid ? table.headers.length - 1 : 0);
|
|
801
807
|
/**
|
|
@@ -1603,6 +1609,7 @@
|
|
|
1603
1609
|
data-bordered={bordered}
|
|
1604
1610
|
data-hoverable={rowHoverable}
|
|
1605
1611
|
data-fetching={fetching || undefined}
|
|
1612
|
+
data-refreshing={(refreshing && !loading && !fetching) || undefined}
|
|
1606
1613
|
{...dir ? { dir } : {}}
|
|
1607
1614
|
>
|
|
1608
1615
|
{#if title}<div class="slean-table-title">{@render title(model)}</div>{/if}
|
|
@@ -1874,7 +1881,7 @@
|
|
|
1874
1881
|
· <span class="slean-table-selected">{m.selected(s.selection.length)}</span>{/if}</span
|
|
1875
1882
|
>{#if pageSizeSelect === 'custom'}<span class="slean-table-page-size"
|
|
1876
1883
|
><span id="{uid}-page-size">{m.pageSize}</span>
|
|
1877
|
-
<div class="slean-table-page-select" {...SELECT_ROOT} data-size="
|
|
1884
|
+
<div class="slean-table-page-select" {...SELECT_ROOT} data-size="small">
|
|
1878
1885
|
<select
|
|
1879
1886
|
data-slean-part="native"
|
|
1880
1887
|
aria-labelledby="{uid}-page-size"
|
package/dist/types.d.ts
CHANGED
|
@@ -274,9 +274,17 @@ export interface TableProps<T> {
|
|
|
274
274
|
onExpandedRowsChange?: (rows: RowId[]) => void;
|
|
275
275
|
loading?: boolean;
|
|
276
276
|
fetching?: boolean;
|
|
277
|
+
/**
|
|
278
|
+
* A quiet background refresh (a poll, an invalidation): a thin line along the top edge, nothing
|
|
279
|
+
* else. Rows are never replaced by skeletons or dimmed, no `aria-busy`, no status text, and page
|
|
280
|
+
* clamping stays active. Ignored while `loading` or `fetching` is set, which say more.
|
|
281
|
+
*/
|
|
282
|
+
refreshing?: boolean;
|
|
277
283
|
/**
|
|
278
284
|
* Skeleton rows drawn while `loading` and no row is present. Default: the page size, at most
|
|
279
285
|
* 25, when paginated (so nothing below the table moves when the rows arrive); otherwise 5.
|
|
286
|
+
* `--slean-table-skeleton-delay` holds them invisible for a moment (CSS only, no timer), so a
|
|
287
|
+
* fast first answer shows no skeleton flash.
|
|
280
288
|
*/
|
|
281
289
|
skeletonRows?: number;
|
|
282
290
|
/**
|