@svgrid/grid 1.2.1 → 1.2.4
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/SvGrid.controller.svelte.js +84 -18
- package/dist/SvGrid.css +294 -16
- package/dist/SvGrid.svelte +1 -0
- package/dist/a11y.d.ts +1 -1
- package/dist/a11y.js +1 -1
- package/dist/cdn/svgrid.js +588 -552
- package/dist/cdn/svgrid.svelte-external.js +850 -814
- package/dist/menus.js +9 -1
- package/dist/scroll-sync.js +1 -1
- package/dist/sv-grid-scrollbar.js +16 -1
- package/package.json +1 -1
- package/src/SvGrid.controller.svelte.ts +82 -17
- package/src/SvGrid.css +294 -16
- package/src/SvGrid.svelte +1 -0
- package/src/a11y.ts +1 -1
- package/src/menus.ts +9 -1
- package/src/scroll-sync.ts +1 -1
- package/src/sv-grid-scrollbar.ts +14 -1
|
@@ -109,6 +109,36 @@ function getMaxDomScrollHeight() {
|
|
|
109
109
|
}
|
|
110
110
|
return detectedMaxDomHeight;
|
|
111
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Observe an element's size, but run the callback on the next animation frame
|
|
114
|
+
* and coalesce bursts into a single call. This is what keeps the benign but
|
|
115
|
+
* noisy "ResizeObserver loop completed with undelivered notifications" warning
|
|
116
|
+
* out of the console: the browser emits it when an observer callback
|
|
117
|
+
* synchronously mutates layout in a way that would require another notification
|
|
118
|
+
* within the same delivery cycle - which our callbacks do (they bump reactive
|
|
119
|
+
* versions / remeasure, driving a re-layout of the observed element). Deferring
|
|
120
|
+
* the work to the next frame lets the current delivery finish cleanly, so the
|
|
121
|
+
* loop never spans a single cycle. This is especially visible when swapping the
|
|
122
|
+
* whole grid (e.g. switching demos), which remounts everything at once.
|
|
123
|
+
* Returns a disconnect function suitable for an $effect cleanup.
|
|
124
|
+
*/
|
|
125
|
+
function observeSizeRaf(el, cb) {
|
|
126
|
+
let frame = 0;
|
|
127
|
+
const observer = new ResizeObserver(() => {
|
|
128
|
+
if (frame)
|
|
129
|
+
return;
|
|
130
|
+
frame = requestAnimationFrame(() => {
|
|
131
|
+
frame = 0;
|
|
132
|
+
cb();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
observer.observe(el);
|
|
136
|
+
return () => {
|
|
137
|
+
if (frame)
|
|
138
|
+
cancelAnimationFrame(frame);
|
|
139
|
+
observer.disconnect();
|
|
140
|
+
};
|
|
141
|
+
}
|
|
112
142
|
export function createSvGridController(props) {
|
|
113
143
|
// Resolved capability gates. Capabilities are OFF by default - a bare
|
|
114
144
|
// grid is a plain read-only table, and each power feature is opted into
|
|
@@ -770,6 +800,17 @@ export function createSvGridController(props) {
|
|
|
770
800
|
const start = pageIndex * pageSize;
|
|
771
801
|
return rows.slice(start, start + pageSize);
|
|
772
802
|
});
|
|
803
|
+
// When a filter reduces the dataset, the stored pageIndex can point beyond
|
|
804
|
+
// the last valid page. Reset to page 0 so the grid never shows a blank body.
|
|
805
|
+
$effect(() => {
|
|
806
|
+
if (!paginationEnabled)
|
|
807
|
+
return;
|
|
808
|
+
const { pageIndex, pageSize } = paginationState;
|
|
809
|
+
const pageCount = Math.ceil(allRowsBeforePagination.length / pageSize);
|
|
810
|
+
if (pageCount > 0 && pageIndex >= pageCount) {
|
|
811
|
+
grid.setPagination({ pageIndex: 0, pageSize });
|
|
812
|
+
}
|
|
813
|
+
});
|
|
773
814
|
const rowSelectionState = $derived.by(() => {
|
|
774
815
|
gridStateVersion;
|
|
775
816
|
return grid.getState().rowSelection ?? {};
|
|
@@ -1045,15 +1086,46 @@ export function createSvGridController(props) {
|
|
|
1045
1086
|
return columnVirtualizer.getTotalSize();
|
|
1046
1087
|
});
|
|
1047
1088
|
const renderedColumnItems = $derived.by(() => {
|
|
1048
|
-
if (columnVirtualizationEnabled)
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1089
|
+
if (!columnVirtualizationEnabled) {
|
|
1090
|
+
let start = 0;
|
|
1091
|
+
return allColumns.map((column, index) => {
|
|
1092
|
+
const size = getColumnWidth(column.id);
|
|
1093
|
+
const item = { index, key: index, size, start, end: start + size };
|
|
1094
|
+
start += size;
|
|
1095
|
+
return item;
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
// Pinned columns are position:sticky, so they only stay pinned while their
|
|
1099
|
+
// cell is in the DOM. Plain column virtualization drops them once they leave
|
|
1100
|
+
// the scroll window, and the pinned column vanishes. Because allColumns is
|
|
1101
|
+
// ordered [pinnedLeft, unpinned, pinnedRight], we keep the rendered window
|
|
1102
|
+
// CONTIGUOUS from the pinned-left prefix (index 0) through the pinned-right
|
|
1103
|
+
// suffix (last index) whenever those exist. The pinned cells are then always
|
|
1104
|
+
// rendered - the existing single-spacer layout positions everything, so no
|
|
1105
|
+
// markup changes are needed. (Cost: with a pinned side, the columns between
|
|
1106
|
+
// that edge and the window are also rendered; negligible for typical grids,
|
|
1107
|
+
// and correctness beats shaving a few off-screen cells.)
|
|
1108
|
+
const window = virtualColumns;
|
|
1109
|
+
const hasLeft = columnPinning.left.length > 0;
|
|
1110
|
+
const hasRight = columnPinning.right.length > 0;
|
|
1111
|
+
if ((!hasLeft && !hasRight) || window.length === 0)
|
|
1112
|
+
return window;
|
|
1113
|
+
const firstIdx = window[0].index;
|
|
1114
|
+
const lastIdx = window[window.length - 1].index;
|
|
1115
|
+
const startIndex = hasLeft ? 0 : firstIdx;
|
|
1116
|
+
const endIndex = hasRight ? allColumns.length - 1 : lastIdx;
|
|
1117
|
+
if (startIndex === firstIdx && endIndex === lastIdx)
|
|
1118
|
+
return window;
|
|
1119
|
+
const items = [];
|
|
1120
|
+
let offset = 0;
|
|
1121
|
+
for (let i = 0; i < startIndex; i += 1)
|
|
1122
|
+
offset += getColumnWidth(allColumns[i].id);
|
|
1123
|
+
for (let i = startIndex; i <= endIndex; i += 1) {
|
|
1124
|
+
const size = getColumnWidth(allColumns[i].id);
|
|
1125
|
+
items.push({ index: i, key: i, size, start: offset, end: offset + size });
|
|
1126
|
+
offset += size;
|
|
1127
|
+
}
|
|
1128
|
+
return items;
|
|
1057
1129
|
});
|
|
1058
1130
|
const renderedColumns = $derived.by(() => renderedColumnItems
|
|
1059
1131
|
.map((item) => ({ item, column: allColumns[item.index] }))
|
|
@@ -1137,11 +1209,9 @@ export function createSvGridController(props) {
|
|
|
1137
1209
|
if (!theadEl)
|
|
1138
1210
|
return;
|
|
1139
1211
|
headerHeight = theadEl.offsetHeight;
|
|
1140
|
-
|
|
1212
|
+
return observeSizeRaf(theadEl, () => {
|
|
1141
1213
|
headerHeight = theadEl?.offsetHeight ?? 0;
|
|
1142
1214
|
});
|
|
1143
|
-
observer.observe(theadEl);
|
|
1144
|
-
return () => observer.disconnect();
|
|
1145
1215
|
});
|
|
1146
1216
|
// Bump scrollVersion when the table's layout size changes so scrollbar
|
|
1147
1217
|
// visibility (and the thumb math that depends on scroll metrics) updates
|
|
@@ -1149,11 +1219,9 @@ export function createSvGridController(props) {
|
|
|
1149
1219
|
$effect(() => {
|
|
1150
1220
|
if (!gridRootEl)
|
|
1151
1221
|
return;
|
|
1152
|
-
|
|
1222
|
+
return observeSizeRaf(gridRootEl, () => {
|
|
1153
1223
|
scrollVersion += 1;
|
|
1154
1224
|
});
|
|
1155
|
-
observer.observe(gridRootEl);
|
|
1156
|
-
return () => observer.disconnect();
|
|
1157
1225
|
});
|
|
1158
1226
|
$effect(() => {
|
|
1159
1227
|
if (!allRows.length || !allColumns.length)
|
|
@@ -1270,13 +1338,11 @@ export function createSvGridController(props) {
|
|
|
1270
1338
|
$effect(() => {
|
|
1271
1339
|
if (!scrollContainer)
|
|
1272
1340
|
return;
|
|
1273
|
-
|
|
1341
|
+
return observeSizeRaf(scrollContainer, () => {
|
|
1274
1342
|
viewportVersion += 1;
|
|
1275
1343
|
if (!hasMeasured)
|
|
1276
1344
|
hasMeasured = true;
|
|
1277
1345
|
});
|
|
1278
|
-
observer.observe(scrollContainer);
|
|
1279
|
-
return () => observer.disconnect();
|
|
1280
1346
|
});
|
|
1281
1347
|
$effect(() => {
|
|
1282
1348
|
// Reading columnWidths here registers it as a reactive dependency so
|
package/dist/SvGrid.css
CHANGED
|
@@ -336,6 +336,21 @@
|
|
|
336
336
|
background: var(--sg-bg, #fff);
|
|
337
337
|
color: var(--sg-fg, #0f172a);
|
|
338
338
|
user-select: none;
|
|
339
|
+
/* Grid lines ship with the grid so it looks complete out of the box - no
|
|
340
|
+
host stylesheet required. Only right + bottom per cell (border-spacing is
|
|
341
|
+
0), so adjacent cells share a single 1px line rather than doubling. */
|
|
342
|
+
border-right: 1px solid var(--sg-border, #e2e8f0);
|
|
343
|
+
border-bottom: 1px solid var(--sg-border, #e2e8f0);
|
|
344
|
+
}
|
|
345
|
+
/* The last cell / column in each row drops its right border so the grid's
|
|
346
|
+
right edge stays clean (the shell provides the outer frame). */
|
|
347
|
+
.sv-grid-table tr > :last-child.sv-grid-column,
|
|
348
|
+
.sv-grid-table tr > :last-child.sv-grid-cell {
|
|
349
|
+
border-right: 0;
|
|
350
|
+
}
|
|
351
|
+
/* Row hover tint. */
|
|
352
|
+
.sv-grid-table tbody tr:hover > .sv-grid-cell {
|
|
353
|
+
background: var(--sg-row-hover-bg, #eef2ff);
|
|
339
354
|
}
|
|
340
355
|
/* Only the active cell + the cell hosting the fill handle become a
|
|
341
356
|
positioning context (so the absolutely-positioned handle anchors
|
|
@@ -384,14 +399,14 @@
|
|
|
384
399
|
always points toward the scrollable middle. */
|
|
385
400
|
.sv-grid-cell[data-pinned="left"],
|
|
386
401
|
.sv-grid-column[data-pinned="left"] {
|
|
387
|
-
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9)
|
|
402
|
+
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
388
403
|
box-shadow:
|
|
389
404
|
inset -1px 0 0 var(--sg-pinned-divider, var(--sg-border, #cbd5e1)),
|
|
390
405
|
8px 0 12px -6px rgba(15, 23, 42, 0.22);
|
|
391
406
|
}
|
|
392
407
|
.sv-grid-cell[data-pinned="right"],
|
|
393
408
|
.sv-grid-column[data-pinned="right"] {
|
|
394
|
-
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9)
|
|
409
|
+
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
395
410
|
box-shadow:
|
|
396
411
|
inset 1px 0 0 var(--sg-pinned-divider, var(--sg-border, #cbd5e1)),
|
|
397
412
|
-8px 0 12px -6px rgba(15, 23, 42, 0.22);
|
|
@@ -400,7 +415,7 @@
|
|
|
400
415
|
so the frozen header itself reads as part of the grid chrome. */
|
|
401
416
|
.sv-grid-head .sv-grid-column[data-pinned="left"],
|
|
402
417
|
.sv-grid-head .sv-grid-column[data-pinned="right"] {
|
|
403
|
-
background: var(--sg-pinned-header-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9)
|
|
418
|
+
background: var(--sg-pinned-header-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 86%, var(--sg-accent, #2563eb) 14%));
|
|
404
419
|
font-weight: 600;
|
|
405
420
|
}
|
|
406
421
|
/* Zebra striping, opt-in via the `zebraRows` prop (which adds
|
|
@@ -414,7 +429,7 @@
|
|
|
414
429
|
/* Zebra rows: keep the pinned tint visible (don't let the row-alt
|
|
415
430
|
background bleed through) by re-painting the pinned cells. */
|
|
416
431
|
.sv-grid-row-alt > .sv-grid-cell[data-pinned] {
|
|
417
|
-
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9)
|
|
432
|
+
background: var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
418
433
|
}
|
|
419
434
|
/* When the row is selected, keep the pinned tint but layer the
|
|
420
435
|
selection color over it so the row still reads as selected. */
|
|
@@ -424,7 +439,7 @@
|
|
|
424
439
|
color-mix(in srgb, var(--sg-selection-bg, #dbeafe) 65%, transparent),
|
|
425
440
|
color-mix(in srgb, var(--sg-selection-bg, #dbeafe) 65%, transparent)
|
|
426
441
|
),
|
|
427
|
-
var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9)
|
|
442
|
+
var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
428
443
|
}
|
|
429
444
|
/* Hovered row keeps the pinned tint distinguishable from the hover. */
|
|
430
445
|
.sv-grid-row:hover > .sv-grid-cell[data-pinned] {
|
|
@@ -433,7 +448,7 @@
|
|
|
433
448
|
color-mix(in srgb, var(--sg-row-hover-bg, #eef2ff) 55%, transparent),
|
|
434
449
|
color-mix(in srgb, var(--sg-row-hover-bg, #eef2ff) 55%, transparent)
|
|
435
450
|
),
|
|
436
|
-
var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9)
|
|
451
|
+
var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
437
452
|
}
|
|
438
453
|
|
|
439
454
|
.sv-grid-column {
|
|
@@ -462,6 +477,14 @@
|
|
|
462
477
|
.sv-grid-column[data-align="right"] .sv-grid-header-sort {
|
|
463
478
|
justify-content: flex-end;
|
|
464
479
|
}
|
|
480
|
+
/* The custom vertical scrollbar overlays the right 16px of the viewport
|
|
481
|
+
(absolute, z-index 40). Nudge the last column's right-aligned content in by
|
|
482
|
+
that width when the scrollbar is visible, so numbers/values aren't hidden
|
|
483
|
+
underneath it. Left/center content already clears the right edge. */
|
|
484
|
+
.sv-grid-has-vscroll .sv-grid-table tr > :last-child.sv-grid-cell[data-align="right"],
|
|
485
|
+
.sv-grid-has-vscroll .sv-grid-table tr > :last-child.sv-grid-column[data-align="right"] {
|
|
486
|
+
padding-right: calc(var(--sg-cell-px, 7px) + 16px);
|
|
487
|
+
}
|
|
465
488
|
.sv-grid-column[data-align="center"] .sv-grid-header-sort {
|
|
466
489
|
justify-content: center;
|
|
467
490
|
}
|
|
@@ -478,9 +501,26 @@
|
|
|
478
501
|
transition: background-color 100ms ease;
|
|
479
502
|
}
|
|
480
503
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
504
|
+
/* An accent center pill (rather than tinting the whole strip): faint on column
|
|
505
|
+
hover, bright when the handle itself is hovered or while dragging. */
|
|
506
|
+
.sv-grid-resize-handle::after {
|
|
507
|
+
content: "";
|
|
508
|
+
position: absolute;
|
|
509
|
+
inset: 0;
|
|
510
|
+
margin: auto 2px;
|
|
511
|
+
width: 2px;
|
|
512
|
+
background: var(--sg-fg, #0f172a);
|
|
513
|
+
opacity: 0;
|
|
514
|
+
border-radius: 1px;
|
|
515
|
+
transition: background-color 100ms ease, opacity 100ms ease;
|
|
516
|
+
}
|
|
517
|
+
.sv-grid-column:hover .sv-grid-resize-handle::after {
|
|
518
|
+
opacity: 0.3;
|
|
519
|
+
}
|
|
520
|
+
.sv-grid-column:hover .sv-grid-resize-handle:hover::after,
|
|
521
|
+
.sv-grid-resize-handle.is-resizing::after {
|
|
522
|
+
background: var(--sg-accent, #2563eb);
|
|
523
|
+
opacity: 1;
|
|
484
524
|
}
|
|
485
525
|
|
|
486
526
|
/* Row resize handle (counterpart of the column resize handle). Lives
|
|
@@ -907,8 +947,9 @@ select.sv-grid-fr-editor {
|
|
|
907
947
|
|
|
908
948
|
.sv-grid-checkbox[aria-checked="true"],
|
|
909
949
|
.sv-grid-checkbox[aria-checked="mixed"] {
|
|
910
|
-
border-color: var(--sg-accent, #
|
|
911
|
-
background: var(--sg-
|
|
950
|
+
border-color: var(--sg-accent, #2563eb);
|
|
951
|
+
background: var(--sg-accent, #2563eb);
|
|
952
|
+
color: var(--sg-on-accent, #fff);
|
|
912
953
|
}
|
|
913
954
|
|
|
914
955
|
.sv-grid-checkbox[aria-checked="true"]::after {
|
|
@@ -1108,16 +1149,49 @@ select.sv-grid-fr-editor {
|
|
|
1108
1149
|
padding: 0 8px;
|
|
1109
1150
|
}
|
|
1110
1151
|
|
|
1111
|
-
/*
|
|
1152
|
+
/* Base look for the filter inputs (header filter row, per-column filter, the
|
|
1153
|
+
global search box, and the menu's search / condition inputs). The inline cell
|
|
1154
|
+
editor is styled separately - it's borderless and fills its cell. Shipping
|
|
1155
|
+
this with the grid means filters look like real inputs with no host
|
|
1156
|
+
stylesheet. */
|
|
1157
|
+
.sv-grid-column-filter,
|
|
1158
|
+
.sv-grid-filter-value,
|
|
1159
|
+
.sv-grid-global-filter input,
|
|
1160
|
+
.sv-grid-menu-search,
|
|
1161
|
+
.sv-grid-menu-condition-value {
|
|
1162
|
+
background: var(--sg-input-bg, #fff);
|
|
1163
|
+
color: var(--sg-fg, #0f172a);
|
|
1164
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
1165
|
+
border-radius: 5px;
|
|
1166
|
+
padding: 0 8px;
|
|
1167
|
+
box-sizing: border-box;
|
|
1168
|
+
font: inherit;
|
|
1169
|
+
}
|
|
1170
|
+
.sv-grid-column-filter {
|
|
1171
|
+
height: 28px;
|
|
1172
|
+
width: 100%;
|
|
1173
|
+
font-size: 0.8125rem;
|
|
1174
|
+
}
|
|
1175
|
+
.sv-grid-column-filter::placeholder,
|
|
1176
|
+
.sv-grid-filter-value::placeholder,
|
|
1177
|
+
.sv-grid-menu-search::placeholder,
|
|
1178
|
+
.sv-grid-global-filter input::placeholder,
|
|
1179
|
+
.sv-grid-cell-editor::placeholder {
|
|
1180
|
+
color: var(--sg-muted, #64748b);
|
|
1181
|
+
opacity: 0.7;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
/* Calm, theme-aware focus (a soft box-shadow rather than a hard outline). */
|
|
1112
1185
|
.sv-grid-global-filter input:focus,
|
|
1113
1186
|
.sv-grid-filter-value:focus,
|
|
1114
1187
|
.sv-grid-column-filter:focus,
|
|
1115
1188
|
.sv-grid-menu-search:focus,
|
|
1116
1189
|
.sv-grid-menu-condition-value:focus,
|
|
1117
|
-
.sv-grid-menu-operator:focus
|
|
1118
|
-
|
|
1119
|
-
outline
|
|
1120
|
-
border-color: var(--sg-accent, #
|
|
1190
|
+
.sv-grid-menu-operator:focus,
|
|
1191
|
+
.sv-grid-cell-editor:focus {
|
|
1192
|
+
outline: none;
|
|
1193
|
+
border-color: var(--sg-accent, #2563eb);
|
|
1194
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 30%, transparent);
|
|
1121
1195
|
}
|
|
1122
1196
|
|
|
1123
1197
|
.sv-grid-cell-editor-number,
|
|
@@ -2010,3 +2084,207 @@ select.sv-grid-fr-editor {
|
|
|
2010
2084
|
color: var(--sg-fg, #0f172a);
|
|
2011
2085
|
}
|
|
2012
2086
|
.sv-grid-find-step:disabled { opacity: 0.30; cursor: default; }
|
|
2087
|
+
|
|
2088
|
+
/* ---- Pagination footer (GridFooter.svelte) ------------------------------
|
|
2089
|
+
Ships with the grid so the pager is styled out of the box - previously this
|
|
2090
|
+
lived only in the examples' host stylesheet, leaving bare consumers with an
|
|
2091
|
+
unstyled pager. Uses --sg-* tokens with fallbacks like the rest of the theme. */
|
|
2092
|
+
.sv-grid-pagination {
|
|
2093
|
+
display: flex;
|
|
2094
|
+
align-items: center;
|
|
2095
|
+
justify-content: flex-end;
|
|
2096
|
+
gap: 24px;
|
|
2097
|
+
padding: 12px 16px;
|
|
2098
|
+
margin-top: 0;
|
|
2099
|
+
border: 1px solid var(--sg-border, #e2e8f0);
|
|
2100
|
+
border-top: 0;
|
|
2101
|
+
border-radius: 0 0 var(--sg-radius, 6px) var(--sg-radius, 6px);
|
|
2102
|
+
background: var(--sg-header-bg, #f1f5f9);
|
|
2103
|
+
color: var(--sg-fg, #0f172a);
|
|
2104
|
+
font-size: 13px;
|
|
2105
|
+
}
|
|
2106
|
+
.sv-grid-pagination-pagesize {
|
|
2107
|
+
display: inline-flex;
|
|
2108
|
+
align-items: center;
|
|
2109
|
+
gap: 8px;
|
|
2110
|
+
color: var(--sg-muted, #64748b);
|
|
2111
|
+
}
|
|
2112
|
+
.sv-grid-pagination-pagesize select {
|
|
2113
|
+
height: 28px;
|
|
2114
|
+
padding: 0 22px 0 8px;
|
|
2115
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
2116
|
+
border-radius: 5px;
|
|
2117
|
+
background: var(--sg-input-bg, #fff);
|
|
2118
|
+
color: var(--sg-fg, #0f172a);
|
|
2119
|
+
font: inherit;
|
|
2120
|
+
font-size: 13px;
|
|
2121
|
+
cursor: pointer;
|
|
2122
|
+
appearance: none;
|
|
2123
|
+
-webkit-appearance: none;
|
|
2124
|
+
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
|
|
2125
|
+
linear-gradient(135deg, currentColor 50%, transparent 50%);
|
|
2126
|
+
background-position: calc(100% - 12px) 12px, calc(100% - 8px) 12px;
|
|
2127
|
+
background-size: 4px 4px, 4px 4px;
|
|
2128
|
+
background-repeat: no-repeat;
|
|
2129
|
+
}
|
|
2130
|
+
.sv-grid-pagination-pagesize select:focus {
|
|
2131
|
+
outline: none;
|
|
2132
|
+
border-color: var(--sg-accent, #2563eb);
|
|
2133
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 30%, transparent);
|
|
2134
|
+
}
|
|
2135
|
+
.sv-grid-pagination-range {
|
|
2136
|
+
color: var(--sg-fg, #0f172a);
|
|
2137
|
+
}
|
|
2138
|
+
.sv-grid-pagination-nav {
|
|
2139
|
+
display: inline-flex;
|
|
2140
|
+
align-items: center;
|
|
2141
|
+
gap: 4px;
|
|
2142
|
+
}
|
|
2143
|
+
.sv-grid-pagination-btn {
|
|
2144
|
+
display: inline-flex;
|
|
2145
|
+
align-items: center;
|
|
2146
|
+
justify-content: center;
|
|
2147
|
+
width: 28px;
|
|
2148
|
+
height: 28px;
|
|
2149
|
+
padding: 0;
|
|
2150
|
+
border: 0;
|
|
2151
|
+
border-radius: 5px;
|
|
2152
|
+
background: transparent;
|
|
2153
|
+
color: var(--sg-fg, #0f172a);
|
|
2154
|
+
font: inherit;
|
|
2155
|
+
font-size: 16px;
|
|
2156
|
+
cursor: pointer;
|
|
2157
|
+
transition: background-color 100ms ease, color 100ms ease;
|
|
2158
|
+
}
|
|
2159
|
+
.sv-grid-pagination-btn:hover:not(:disabled) {
|
|
2160
|
+
background: var(--sg-input-bg, #fff);
|
|
2161
|
+
color: var(--sg-accent, #2563eb);
|
|
2162
|
+
}
|
|
2163
|
+
.sv-grid-pagination-btn:disabled {
|
|
2164
|
+
color: var(--sg-muted, #64748b);
|
|
2165
|
+
opacity: 0.4;
|
|
2166
|
+
cursor: default;
|
|
2167
|
+
}
|
|
2168
|
+
.sv-grid-pagination-label {
|
|
2169
|
+
margin: 0 8px;
|
|
2170
|
+
color: var(--sg-fg, #0f172a);
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
/* ---- Menu / filter chrome completeness -----------------------------------
|
|
2174
|
+
These styles previously lived only in the examples' host stylesheet. Shipping
|
|
2175
|
+
them here means the column menu, filter/choose-columns popovers, facet
|
|
2176
|
+
checklist, operator select, filter buttons, and the scrollbar corner are
|
|
2177
|
+
fully themed out of the box for any consumer - all via --sg-* tokens. */
|
|
2178
|
+
|
|
2179
|
+
/* Hide the scrollbar corner when its scrollbar is absent. */
|
|
2180
|
+
.sv-grid-no-scroll .sv-grid-scrollbar-corner,
|
|
2181
|
+
.sv-grid-no-scroll .sv-grid-scrollbar {
|
|
2182
|
+
display: none;
|
|
2183
|
+
}
|
|
2184
|
+
/* Scrollbar corner divider uses the theme border token. */
|
|
2185
|
+
.sv-grid-scrollbar-corner,
|
|
2186
|
+
.sv-grid-scrollbar-corner-br {
|
|
2187
|
+
border-bottom-color: var(--sg-border, #e2e8f0);
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
/* Filter operator <select> (the dropdown that replaced the radio group). */
|
|
2191
|
+
.sv-grid-menu-operator-select {
|
|
2192
|
+
width: 100%;
|
|
2193
|
+
height: 28px;
|
|
2194
|
+
padding: 0 22px 0 8px;
|
|
2195
|
+
margin-bottom: 6px;
|
|
2196
|
+
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
2197
|
+
border-radius: 5px;
|
|
2198
|
+
background: var(--sg-input-bg, #fff);
|
|
2199
|
+
color: var(--sg-fg, #0f172a);
|
|
2200
|
+
font: inherit;
|
|
2201
|
+
font-size: 13px;
|
|
2202
|
+
cursor: pointer;
|
|
2203
|
+
appearance: none;
|
|
2204
|
+
-webkit-appearance: none;
|
|
2205
|
+
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
|
|
2206
|
+
linear-gradient(135deg, currentColor 50%, transparent 50%);
|
|
2207
|
+
background-position: calc(100% - 12px) 12px, calc(100% - 8px) 12px;
|
|
2208
|
+
background-size: 4px 4px, 4px 4px;
|
|
2209
|
+
background-repeat: no-repeat;
|
|
2210
|
+
}
|
|
2211
|
+
.sv-grid-menu-operator-select:focus {
|
|
2212
|
+
outline: none;
|
|
2213
|
+
border-color: var(--sg-accent, #2563eb);
|
|
2214
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 40%, transparent);
|
|
2215
|
+
}
|
|
2216
|
+
/* Operator toggle button (active) uses the on-accent token. */
|
|
2217
|
+
.sv-grid-menu-operator-btn.is-active {
|
|
2218
|
+
color: var(--sg-on-accent, #fff);
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
/* Filter-only + choose-columns popover widths. */
|
|
2222
|
+
.sv-grid-filter-menu {
|
|
2223
|
+
width: 260px;
|
|
2224
|
+
}
|
|
2225
|
+
.sv-grid-choose-columns-menu {
|
|
2226
|
+
width: 240px;
|
|
2227
|
+
}
|
|
2228
|
+
.sv-grid-choose-columns-menu .sv-grid-facet-list {
|
|
2229
|
+
max-height: 280px;
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
/* Native checkboxes in the facet list pick up the theme accent. */
|
|
2233
|
+
.sv-grid-facet input[type="checkbox"] {
|
|
2234
|
+
accent-color: var(--sg-accent, #2563eb);
|
|
2235
|
+
width: 14px;
|
|
2236
|
+
height: 14px;
|
|
2237
|
+
cursor: pointer;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
/* Submenu right-chevron on the "Choose columns" item. */
|
|
2241
|
+
.sv-grid-menu-item-chevron {
|
|
2242
|
+
margin-left: auto;
|
|
2243
|
+
transform: rotate(-90deg);
|
|
2244
|
+
opacity: 0.55;
|
|
2245
|
+
}
|
|
2246
|
+
.sv-grid-menu-item.is-open .sv-grid-menu-item-chevron {
|
|
2247
|
+
opacity: 1;
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
/* Magnifier prefix on the menu's search input. */
|
|
2251
|
+
.sv-grid-menu-search {
|
|
2252
|
+
padding-left: 28px;
|
|
2253
|
+
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='6'/%3E%3Cpath d='M20 20l-4.5-4.5'/%3E%3C/svg%3E");
|
|
2254
|
+
background-position: 8px center;
|
|
2255
|
+
background-repeat: no-repeat;
|
|
2256
|
+
background-size: 14px 14px;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
/* Filter menu vertical rhythm. */
|
|
2260
|
+
.sv-grid-menu-filter > * + * {
|
|
2261
|
+
margin-top: 6px;
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
/* Column-header filter (funnel) button tints accent when a filter is active. */
|
|
2265
|
+
.sv-grid-col-filter-btn.is-active {
|
|
2266
|
+
color: var(--sg-accent, #2563eb);
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
/* Row-number gutter: neutralize the right border so it fuses with the header. */
|
|
2270
|
+
.sv-grid-row-number-column,
|
|
2271
|
+
.sv-grid-row-number-cell {
|
|
2272
|
+
border-right-color: var(--sg-header-bg, #f1f5f9);
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
/* Group row label colour. */
|
|
2276
|
+
.sv-grid-group-row > .sv-grid-cell {
|
|
2277
|
+
color: var(--sg-header-fg, #0f172a);
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
/* Filter-row operator button: soften when idle, accent border on hover/open. */
|
|
2281
|
+
.sv-grid-filter-operator-btn {
|
|
2282
|
+
opacity: 0.85;
|
|
2283
|
+
}
|
|
2284
|
+
.sv-grid-filter-operator-btn:hover {
|
|
2285
|
+
opacity: 1;
|
|
2286
|
+
border-color: var(--sg-accent, #2563eb);
|
|
2287
|
+
}
|
|
2288
|
+
.sv-grid-filter-operator-btn.is-open {
|
|
2289
|
+
opacity: 1;
|
|
2290
|
+
}
|
package/dist/SvGrid.svelte
CHANGED
|
@@ -1189,6 +1189,7 @@
|
|
|
1189
1189
|
>
|
|
1190
1190
|
<div
|
|
1191
1191
|
class="sv-grid-container sv-grid-container-custom-scrollbars"
|
|
1192
|
+
class:sv-grid-has-vscroll={hasMeasured && hasVerticalOverflow}
|
|
1192
1193
|
bind:this={ctrl.scrollContainer}
|
|
1193
1194
|
onscroll={onBodyScroll}
|
|
1194
1195
|
style={`overflow: auto; position: relative; height: calc(100% - ${hasMeasured && hasHorizontalOverflow ? 16 : 0}px);`}
|
package/dist/a11y.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare function getGridRowA11yProps(rowIndex?: number): {
|
|
|
29
29
|
readonly role: "row";
|
|
30
30
|
};
|
|
31
31
|
export declare function getGridCellA11yProps(input?: GridCellA11yInput): {
|
|
32
|
-
readonly 'aria-selected'
|
|
32
|
+
readonly 'aria-selected': boolean;
|
|
33
33
|
readonly 'aria-rowindex'?: number | undefined;
|
|
34
34
|
readonly 'aria-colindex'?: number | undefined;
|
|
35
35
|
readonly id?: string | undefined;
|
package/dist/a11y.js
CHANGED
|
@@ -29,7 +29,7 @@ export function getGridCellA11yProps(input = {}) {
|
|
|
29
29
|
...(id ? { id } : {}),
|
|
30
30
|
...(colIndex !== undefined ? { 'aria-colindex': colIndex } : {}),
|
|
31
31
|
...(rowIndex !== undefined ? { 'aria-rowindex': rowIndex } : {}),
|
|
32
|
-
|
|
32
|
+
'aria-selected': selected,
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
export function getGridCellDomId(baseId, rowIndex, colIndex) {
|