@svgrid/grid 2.1.21 → 2.1.23
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/README.md +9 -2
- package/dist/DockNodeView.svelte +306 -0
- package/dist/DockNodeView.svelte.d.ts +7 -0
- package/dist/DockPopoutHost.svelte +17 -0
- package/dist/DockPopoutHost.svelte.d.ts +14 -0
- package/dist/GridMenus.svelte +83 -3
- package/dist/SvAutoComplete.svelte +5 -3
- package/dist/SvCarousel.svelte +20 -42
- package/dist/SvColorInput.svelte +4 -3
- package/dist/SvColorInput.svelte.d.ts +1 -1
- package/dist/SvComboBox.svelte +7 -3
- package/dist/SvCommand.svelte +28 -85
- package/dist/SvCountryInput.svelte +1 -1
- package/dist/SvDateRangeInput.svelte +1 -1
- package/dist/SvDateTimePicker.svelte +9 -3
- package/dist/SvDateTimePicker.svelte.d.ts +3 -0
- package/dist/SvDockLayout.svelte +244 -0
- package/dist/SvDockLayout.svelte.d.ts +77 -0
- package/dist/SvDockManager.svelte +922 -0
- package/dist/SvDockManager.svelte.d.ts +106 -0
- package/dist/SvDrawer.svelte +69 -47
- package/dist/SvDrawer.svelte.d.ts +4 -0
- package/dist/SvDropDownList.svelte +85 -13
- package/dist/SvDropDownList.svelte.d.ts +3 -0
- package/dist/SvField.svelte +21 -4
- package/dist/SvField.svelte.d.ts +1 -0
- package/dist/SvForm.svelte +201 -91
- package/dist/SvForm.svelte.d.ts +15 -22
- package/dist/SvGrid.controller.svelte.d.ts +32 -1
- package/dist/SvGrid.controller.svelte.js +156 -27
- package/dist/SvGrid.css +193 -5
- package/dist/SvGrid.svelte +357 -12
- package/dist/SvGrid.types.d.ts +452 -1
- package/dist/SvGridDropdown.svelte +8 -1
- package/dist/SvGridSelect.svelte +1 -0
- package/dist/SvListBox.svelte +36 -14
- package/dist/SvMaskedInput.svelte +4 -3
- package/dist/SvMaskedInput.svelte.d.ts +1 -1
- package/dist/SvMenu.svelte +1 -1
- package/dist/SvMenuList.svelte +23 -71
- package/dist/SvMenuList.svelte.d.ts +2 -13
- package/dist/SvModal.svelte +12 -29
- package/dist/SvMultiSelect.svelte +1 -0
- package/dist/SvNumberInput.svelte +4 -3
- package/dist/SvNumberInput.svelte.d.ts +1 -1
- package/dist/SvPagination.svelte +18 -18
- package/dist/SvPasswordInput.svelte +3 -2
- package/dist/SvPasswordInput.svelte.d.ts +1 -1
- package/dist/SvPhoneInput.svelte +3 -2
- package/dist/SvPhoneInput.svelte.d.ts +1 -1
- package/dist/SvPopover.svelte +1 -1
- package/dist/SvStepper.svelte +6 -9
- package/dist/SvTagsInput.svelte +3 -2
- package/dist/SvTagsInput.svelte.d.ts +1 -1
- package/dist/SvTooltip.svelte +12 -23
- package/dist/SvTour.svelte +32 -4
- package/dist/SvTreeSelect.svelte +1 -0
- package/dist/build-api.js +76 -54
- package/dist/cdn/svgrid.js +10907 -6444
- package/dist/cdn/svgrid.svelte-external.js +9622 -5475
- package/dist/columns.js +6 -0
- package/dist/conditional-formatting.d.ts +67 -3
- package/dist/conditional-formatting.js +124 -17
- package/dist/createCarousel.svelte.d.ts +72 -0
- package/dist/createCarousel.svelte.js +80 -0
- package/dist/createCommand.svelte.d.ts +62 -0
- package/dist/createCommand.svelte.js +109 -0
- package/dist/createForm.svelte.d.ts +62 -0
- package/dist/createForm.svelte.js +374 -0
- package/dist/createMenu.svelte.d.ts +88 -0
- package/dist/createMenu.svelte.js +153 -0
- package/dist/createOverlay.svelte.d.ts +38 -0
- package/dist/createOverlay.svelte.js +68 -0
- package/dist/createPagination.d.ts +74 -0
- package/dist/createPagination.js +71 -0
- package/dist/createPopoverSelect.svelte.js +6 -12
- package/dist/createStepper.d.ts +44 -0
- package/dist/createStepper.js +26 -0
- package/dist/createTooltip.svelte.d.ts +32 -0
- package/dist/createTooltip.svelte.js +61 -0
- package/dist/datetime/timezone.d.ts +36 -0
- package/dist/datetime/timezone.js +110 -0
- package/dist/dock-context.d.ts +52 -0
- package/dist/dock-context.js +1 -0
- package/dist/dock-manager-model.d.ts +136 -0
- package/dist/dock-manager-model.js +514 -0
- package/dist/dock-model.d.ts +105 -0
- package/dist/dock-model.js +288 -0
- package/dist/dock-popout.d.ts +30 -0
- package/dist/dock-popout.js +81 -0
- package/dist/editor-contract.d.ts +2 -0
- package/dist/filter-operators.d.ts +6 -0
- package/dist/filter-operators.js +35 -2
- package/dist/filtering/excel-filters.d.ts +18 -1
- package/dist/filtering/excel-filters.js +56 -0
- package/dist/form-field.d.ts +73 -0
- package/dist/form-field.js +15 -0
- package/dist/index.d.ts +25 -5
- package/dist/index.js +40 -4
- package/dist/list-nav.d.ts +18 -0
- package/dist/list-nav.js +30 -0
- package/dist/list-option.d.ts +2 -0
- package/dist/menu-item.d.ts +20 -0
- package/dist/menu-item.js +1 -0
- package/dist/menus.d.ts +5 -0
- package/dist/menus.js +68 -16
- package/dist/panel-resize.d.ts +25 -0
- package/dist/panel-resize.js +40 -0
- package/dist/popover.d.ts +29 -3
- package/dist/popover.js +22 -6
- package/dist/recurrence.d.ts +33 -7
- package/dist/recurrence.js +151 -13
- package/dist/row-drag.d.ts +8 -0
- package/dist/row-drag.js +11 -0
- package/dist/scheduler-ical.d.ts +30 -0
- package/dist/scheduler-ical.js +180 -0
- package/dist/scheduler-model.d.ts +269 -0
- package/dist/scheduler-model.js +602 -0
- package/dist/scheduler-view.svelte.d.ts +22 -0
- package/dist/scheduler-view.svelte.js +13 -0
- package/dist/spreadsheet.js +20 -12
- package/dist/ui-app.types.d.ts +3 -0
- package/package.json +5 -1
- package/src/DockNodeView.svelte +306 -0
- package/src/DockPopoutHost.svelte +17 -0
- package/src/GridMenus.svelte +83 -3
- package/src/SvAutoComplete.svelte +5 -3
- package/src/SvCarousel.svelte +20 -42
- package/src/SvCarousel.test.ts +68 -0
- package/src/SvColorInput.svelte +4 -3
- package/src/SvComboBox.svelte +7 -3
- package/src/SvCommand.svelte +28 -85
- package/src/SvCommand.test.ts +78 -0
- package/src/SvCountryInput.svelte +1 -1
- package/src/SvDateRangeInput.svelte +1 -1
- package/src/SvDateTimePicker.svelte +9 -3
- package/src/SvDockLayout.svelte +244 -0
- package/src/SvDockManager.svelte +922 -0
- package/src/SvDrawer.svelte +69 -47
- package/src/SvDropDownList.svelte +85 -13
- package/src/SvField.svelte +21 -4
- package/src/SvForm.svelte +201 -91
- package/src/SvForm.test.ts +299 -0
- package/src/SvGrid.controller.svelte.ts +152 -28
- package/src/SvGrid.css +193 -5
- package/src/SvGrid.svelte +357 -12
- package/src/SvGrid.types.ts +463 -1
- package/src/SvGridDropdown.svelte +8 -1
- package/src/SvGridSelect.svelte +1 -0
- package/src/SvListBox.svelte +36 -14
- package/src/SvMaskedInput.svelte +4 -3
- package/src/SvMenu.svelte +1 -1
- package/src/SvMenu.test.ts +24 -0
- package/src/SvMenuList.svelte +23 -71
- package/src/SvModal.svelte +12 -29
- package/src/SvModal.test.ts +82 -0
- package/src/SvMultiSelect.svelte +1 -0
- package/src/SvNumberInput.svelte +4 -3
- package/src/SvPagination.svelte +18 -18
- package/src/SvPasswordInput.svelte +3 -2
- package/src/SvPhoneInput.svelte +3 -2
- package/src/SvPopover.svelte +1 -1
- package/src/SvStepper.svelte +6 -9
- package/src/SvTagsInput.svelte +3 -2
- package/src/SvTooltip.svelte +12 -23
- package/src/SvTooltip.test.ts +73 -0
- package/src/SvTour.svelte +32 -4
- package/src/SvTreeSelect.svelte +1 -0
- package/src/build-api.ts +74 -51
- package/src/columns.ts +5 -0
- package/src/conditional-formatting.test.ts +104 -0
- package/src/conditional-formatting.ts +176 -19
- package/src/createCarousel.svelte.ts +104 -0
- package/src/createCommand.svelte.ts +125 -0
- package/src/createForm.svelte.ts +333 -0
- package/src/createMenu.svelte.ts +202 -0
- package/src/createOverlay.svelte.ts +101 -0
- package/src/createPagination.test.ts +65 -0
- package/src/createPagination.ts +84 -0
- package/src/createPopoverSelect.svelte.ts +5 -9
- package/src/createStepper.test.ts +62 -0
- package/src/createStepper.ts +61 -0
- package/src/createTooltip.svelte.ts +86 -0
- package/src/datetime/timezone.test.ts +69 -0
- package/src/datetime/timezone.ts +134 -0
- package/src/dock-context.ts +47 -0
- package/src/dock-manager-model.test.ts +275 -0
- package/src/dock-manager-model.ts +593 -0
- package/src/dock-model.test.ts +163 -0
- package/src/dock-model.ts +368 -0
- package/src/dock-popout.ts +94 -0
- package/src/editor-contract.ts +2 -0
- package/src/filter-operators.test.ts +59 -0
- package/src/filter-operators.ts +35 -2
- package/src/filtering/excel-filters.test.ts +86 -1
- package/src/filtering/excel-filters.ts +65 -0
- package/src/form-field.ts +87 -0
- package/src/index.ts +158 -4
- package/src/list-nav.test.ts +49 -0
- package/src/list-nav.ts +29 -0
- package/src/list-option.ts +2 -0
- package/src/menu-item.ts +21 -0
- package/src/menus.test.ts +48 -0
- package/src/menus.ts +73 -16
- package/src/panel-resize.ts +52 -0
- package/src/popover.bounds.test.ts +93 -0
- package/src/popover.ts +51 -7
- package/src/recurrence.test.ts +88 -1
- package/src/recurrence.ts +166 -15
- package/src/row-drag.ts +10 -0
- package/src/scheduler-ical.test.ts +68 -0
- package/src/scheduler-ical.ts +180 -0
- package/src/scheduler-model.test.ts +546 -0
- package/src/scheduler-model.ts +853 -0
- package/src/scheduler-view.svelte.ts +33 -0
- package/src/spreadsheet.ts +20 -12
- package/src/svgrid.effect-driven-filter.svelte.test.ts +116 -0
- package/src/ui-app.types.ts +3 -0
- package/src/virtual.test.ts +6 -4
|
@@ -34,6 +34,7 @@ import "./sv-grid-scrollbar";
|
|
|
34
34
|
import {
|
|
35
35
|
computeColumnStat,
|
|
36
36
|
formatsNeedingStats,
|
|
37
|
+
formatNeedsStats,
|
|
37
38
|
type ColumnStat,
|
|
38
39
|
} from "./conditional-formatting";
|
|
39
40
|
import SvGridDropdown from "./SvGridDropdown.svelte";
|
|
@@ -143,9 +144,12 @@ const MAX_DOM_SCROLL_HEIGHT_FALLBACK = 8_000_000;
|
|
|
143
144
|
* matters because mobile WebKit/Blink can report a generous `offsetHeight` yet
|
|
144
145
|
* expose a smaller scrollable range - trusting the layout height alone is what
|
|
145
146
|
* stranded the last rows on phones. Using a real scroll container also folds in
|
|
146
|
-
* DPR clamping for free. Cached
|
|
147
|
+
* DPR clamping for free. Cached, but re-probed when the device pixel ratio
|
|
148
|
+
* changes (browser zoom) since the physical-pixel cap moves with it (#85).
|
|
147
149
|
*/
|
|
148
150
|
let detectedMaxDomHeight: number | null = null;
|
|
151
|
+
let detectedAtDpr = 0;
|
|
152
|
+
const currentDpr = () => (typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1);
|
|
149
153
|
/**
|
|
150
154
|
* Seed the `hiddenColumns` map from any column def marked `visible: false`.
|
|
151
155
|
* Walks groups so a hidden group hides all of its leaf columns. Keyed by the
|
|
@@ -190,7 +194,7 @@ function getMaxDomScrollHeight(): number {
|
|
|
190
194
|
return forced;
|
|
191
195
|
}
|
|
192
196
|
}
|
|
193
|
-
if (detectedMaxDomHeight != null) return detectedMaxDomHeight;
|
|
197
|
+
if (detectedMaxDomHeight != null && detectedAtDpr === currentDpr()) return detectedMaxDomHeight;
|
|
194
198
|
if (typeof document === "undefined" || !document.body) {
|
|
195
199
|
return MAX_DOM_SCROLL_HEIGHT_FALLBACK;
|
|
196
200
|
}
|
|
@@ -218,6 +222,7 @@ function getMaxDomScrollHeight(): number {
|
|
|
218
222
|
} catch {
|
|
219
223
|
detectedMaxDomHeight = MAX_DOM_SCROLL_HEIGHT_FALLBACK;
|
|
220
224
|
}
|
|
225
|
+
detectedAtDpr = currentDpr();
|
|
221
226
|
return detectedMaxDomHeight;
|
|
222
227
|
}
|
|
223
228
|
|
|
@@ -429,6 +434,12 @@ export function createSvGridController<
|
|
|
429
434
|
let filterMenuPos = $state<MenuPosition>({ x: 0, y: 0 });
|
|
430
435
|
let operatorMenuFor = $state<string | null>(null);
|
|
431
436
|
let operatorMenuPos = $state<MenuPosition>({ x: 0, y: 0 });
|
|
437
|
+
// `in` / `notIn` value-suggestions dropdown: the column whose chip input is
|
|
438
|
+
// active, its anchor position, and the current typed query (for filtering
|
|
439
|
+
// the distinct-value list as the user types).
|
|
440
|
+
let inSuggestFor = $state<string | null>(null);
|
|
441
|
+
let inSuggestPos = $state<MenuPosition>({ x: 0, y: 0 });
|
|
442
|
+
let inSuggestQuery = $state("");
|
|
432
443
|
let chooseColumnsPos = $state<MenuPosition | null>(null);
|
|
433
444
|
let contextMenuFor = $state<ContextMenuTarget<TData> | null>(null);
|
|
434
445
|
let contextMenuPos = $state<MenuPosition>({ x: 0, y: 0 });
|
|
@@ -563,6 +574,10 @@ export function createSvGridController<
|
|
|
563
574
|
let hiddenColumns = $state<Record<string, boolean>>(
|
|
564
575
|
initialHiddenColumns(props.columns),
|
|
565
576
|
);
|
|
577
|
+
// The columns declared `visible: false` in the ColumnDefs. Kept separately so
|
|
578
|
+
// applying a saved view (setState) can re-seed from these and never un-hide a
|
|
579
|
+
// column the app declared hidden (#54).
|
|
580
|
+
const declaredHiddenColumns = $derived(initialHiddenColumns(props.columns));
|
|
566
581
|
|
|
567
582
|
// Collapsible column groups (columnGroupShow). Meta is derived from the tree;
|
|
568
583
|
// `collapsedColumnGroups` is the live set of collapsed group ids, seeded once
|
|
@@ -940,6 +955,10 @@ export function createSvGridController<
|
|
|
940
955
|
const left: Record<string, number> = {};
|
|
941
956
|
let leftAcc = rowNumberWidth + selectionWidth;
|
|
942
957
|
for (const id of effectivePinning.left) {
|
|
958
|
+
// Hidden pinned columns don't render, so they must contribute neither an
|
|
959
|
+
// offset entry nor width - otherwise the next visible pinned column is
|
|
960
|
+
// pushed out by a phantom gap (#75).
|
|
961
|
+
if (hiddenColumns[id] || hiddenByGroupCollapse[id]) continue;
|
|
943
962
|
left[id] = leftAcc;
|
|
944
963
|
leftAcc += getColumnWidth(id);
|
|
945
964
|
}
|
|
@@ -948,6 +967,7 @@ export function createSvGridController<
|
|
|
948
967
|
for (let i = effectivePinning.right.length - 1; i >= 0; i -= 1) {
|
|
949
968
|
const id = effectivePinning.right[i];
|
|
950
969
|
if (!id) continue;
|
|
970
|
+
if (hiddenColumns[id] || hiddenByGroupCollapse[id]) continue;
|
|
951
971
|
right[id] = rightAcc;
|
|
952
972
|
rightAcc += getColumnWidth(id);
|
|
953
973
|
}
|
|
@@ -990,26 +1010,35 @@ export function createSvGridController<
|
|
|
990
1010
|
const map = new Map<string, ColumnStat>();
|
|
991
1011
|
const formats = props.conditionalFormats;
|
|
992
1012
|
if (!formats?.length || !formatsNeedingStats(formats)) return map;
|
|
1013
|
+
// `visible` (default): the currently displayed rows (filtered + paged),
|
|
1014
|
+
// so the heat map reflects what the user is looking at. `all`: the full
|
|
1015
|
+
// unfiltered dataset, for a stable scale independent of filter/paging.
|
|
1016
|
+
const scanAll = props.conditionalStatScope === "all";
|
|
993
1017
|
for (const column of allColumns) {
|
|
994
1018
|
const needs = formats.some(
|
|
995
|
-
(f) =>
|
|
996
|
-
(f.type === "colorScale" || f.type === "dataBar") &&
|
|
997
|
-
(!f.columns || f.columns.includes(column.id)),
|
|
1019
|
+
(f) => formatNeedsStats(f) && (!f.columns || f.columns.includes(column.id)),
|
|
998
1020
|
);
|
|
999
1021
|
if (!needs) continue;
|
|
1000
1022
|
const def = column.columnDef;
|
|
1001
1023
|
const fieldFn = def.fieldFn;
|
|
1002
1024
|
const field = def.field;
|
|
1003
1025
|
const stat = computeColumnStat(
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1026
|
+
scanAll
|
|
1027
|
+
? (function* () {
|
|
1028
|
+
// Raw data objects (unfiltered); field/fieldFn only - no Row wrapper.
|
|
1029
|
+
for (const obj of internalData as ReadonlyArray<Record<string, unknown>>) {
|
|
1030
|
+
yield fieldFn ? fieldFn(obj as any) : field ? obj[field] : undefined;
|
|
1031
|
+
}
|
|
1032
|
+
})()
|
|
1033
|
+
: (function* () {
|
|
1034
|
+
for (const row of allRows) {
|
|
1035
|
+
yield fieldFn
|
|
1036
|
+
? fieldFn(row.original)
|
|
1037
|
+
: field
|
|
1038
|
+
? (row.original as Record<string, unknown>)[field]
|
|
1039
|
+
: row.getCellValueByColumnId(column.id);
|
|
1040
|
+
}
|
|
1041
|
+
})(),
|
|
1013
1042
|
);
|
|
1014
1043
|
if (stat) map.set(column.id, stat);
|
|
1015
1044
|
}
|
|
@@ -1076,7 +1105,7 @@ export function createSvGridController<
|
|
|
1076
1105
|
|
|
1077
1106
|
// A single condition is "active" if it has the value(s) it needs.
|
|
1078
1107
|
const condActive = (op: FilterOperator, value: string, valueTo?: string): boolean => {
|
|
1079
|
-
if (op === "isBlank") return true;
|
|
1108
|
+
if (op === "isBlank" || op === "isNotBlank") return true;
|
|
1080
1109
|
if (op === "between") return value.trim().length > 0 && (valueTo ?? "").trim().length > 0;
|
|
1081
1110
|
return value.trim().length > 0;
|
|
1082
1111
|
};
|
|
@@ -1198,10 +1227,13 @@ export function createSvGridController<
|
|
|
1198
1227
|
lastSelectionSerialized = serialized;
|
|
1199
1228
|
const callback = props.onRowSelectionChange;
|
|
1200
1229
|
if (!callback) return;
|
|
1201
|
-
|
|
1230
|
+
// Resolve selected rows by row id (not numeric index) so this is correct
|
|
1231
|
+
// with a custom getRowId and after sorting (#45). Iterate the full filtered
|
|
1232
|
+
// model - not the paginated slice - so selections on other pages are kept.
|
|
1202
1233
|
const selectedRows: TData[] = [];
|
|
1203
|
-
for (
|
|
1204
|
-
if (
|
|
1234
|
+
for (const row of allRowsBeforePagination) {
|
|
1235
|
+
if (isGroupRow(row)) continue;
|
|
1236
|
+
if (rowSelectionState[row.id]) selectedRows.push(row.original as TData);
|
|
1205
1237
|
}
|
|
1206
1238
|
callback(rowSelectionState, selectedRows);
|
|
1207
1239
|
});
|
|
@@ -1683,7 +1715,7 @@ export function createSvGridController<
|
|
|
1683
1715
|
if (!props.onFiltersChange) return;
|
|
1684
1716
|
const menuEntries = Object.entries(filterMenuValues)
|
|
1685
1717
|
.filter(([, f]) => {
|
|
1686
|
-
if (f.operator === "isBlank") return true;
|
|
1718
|
+
if (f.operator === "isBlank" || f.operator === "isNotBlank") return true;
|
|
1687
1719
|
if (f.operator === "between") {
|
|
1688
1720
|
return f.value.trim().length > 0 && (f.valueTo ?? "").trim().length > 0;
|
|
1689
1721
|
}
|
|
@@ -1912,6 +1944,43 @@ export function createSvGridController<
|
|
|
1912
1944
|
Math.max(totalColumnWidth - columnWindowEnd, 0),
|
|
1913
1945
|
);
|
|
1914
1946
|
|
|
1947
|
+
/**
|
|
1948
|
+
* `groupHeaderRows` clipped to the rendered column window. The multi-level
|
|
1949
|
+
* group-header band must virtualize in lock-step with the leaf-header row
|
|
1950
|
+
* and the body: otherwise a group cell spanning off-window leaves renders a
|
|
1951
|
+
* header over empty space (its cells were dropped by body virtualization),
|
|
1952
|
+
* so wide grouped/pivot grids show group headers with no cells beneath.
|
|
1953
|
+
*
|
|
1954
|
+
* We reuse each cell's `firstLeafIndex` + `colSpan` (leaf-index range) to
|
|
1955
|
+
* clip against the rendered leaf window, dropping fully-off-window cells and
|
|
1956
|
+
* shrinking partially-visible ones to their visible leaves. The template
|
|
1957
|
+
* frames the result with the SAME left/right spacers the leaf row uses, so
|
|
1958
|
+
* everything stays pixel-aligned. When virtualization is off this is a
|
|
1959
|
+
* pass-through.
|
|
1960
|
+
*/
|
|
1961
|
+
const groupHeaderRowsWindowed = $derived.by(() => {
|
|
1962
|
+
const base = groupHeaderRows;
|
|
1963
|
+
if (!columnVirtualizationEnabled || base.length === 0) return base;
|
|
1964
|
+
const items = renderedColumnItems;
|
|
1965
|
+
if (items.length === 0) return base;
|
|
1966
|
+
const winFirst = items[0]!.index;
|
|
1967
|
+
const winLast = items[items.length - 1]!.index; // inclusive
|
|
1968
|
+
return base.map((row) => {
|
|
1969
|
+
const cells: GroupHeaderCell[] = [];
|
|
1970
|
+
for (const cell of row.cells) {
|
|
1971
|
+
const start = cell.firstLeafIndex;
|
|
1972
|
+
const end = start + cell.colSpan; // exclusive
|
|
1973
|
+
const from = Math.max(start, winFirst);
|
|
1974
|
+
const to = Math.min(end, winLast + 1);
|
|
1975
|
+
if (to <= from) continue; // cell is entirely outside the window
|
|
1976
|
+
let widthPx = 0;
|
|
1977
|
+
for (let i = from; i < to; i += 1) widthPx += getColumnWidth(allColumns[i]!.id);
|
|
1978
|
+
cells.push({ ...cell, colSpan: to - from, widthPx, firstLeafIndex: from });
|
|
1979
|
+
}
|
|
1980
|
+
return { ...row, cells };
|
|
1981
|
+
});
|
|
1982
|
+
});
|
|
1983
|
+
|
|
1915
1984
|
const activeCell = $derived.by(() => {
|
|
1916
1985
|
gridStateVersion;
|
|
1917
1986
|
return (
|
|
@@ -2212,7 +2281,9 @@ export function createSvGridController<
|
|
|
2212
2281
|
viewportVersion;
|
|
2213
2282
|
// Narrow responsive mode pans instead of scaling, so skip fit scaling.
|
|
2214
2283
|
if (!props.fitColumns || isNarrowResponsive) return null;
|
|
2215
|
-
|
|
2284
|
+
// Exclude columns hidden by a collapsed group too (#57) - otherwise their
|
|
2285
|
+
// widths stay in the fit distribution and leave a blank gap at the edge.
|
|
2286
|
+
const cols = grid.getAllColumns().filter((c) => !hiddenColumns[c.id] && !hiddenByGroupCollapse[c.id]);
|
|
2216
2287
|
if (!cols.length) return null;
|
|
2217
2288
|
const rowNumberWidth = showRowNumbersEffective ? rowNumberColumnWidth : 0;
|
|
2218
2289
|
const selectionWidth = showRowSelectionEffective ? selectionColumnWidth : 0;
|
|
@@ -2425,15 +2496,19 @@ export function createSvGridController<
|
|
|
2425
2496
|
};
|
|
2426
2497
|
});
|
|
2427
2498
|
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2499
|
+
/**
|
|
2500
|
+
* Distinct values for a column, for the set-filter checklist and the
|
|
2501
|
+
* `in` / `notIn` suggestions dropdown. Server-provided values win; numeric /
|
|
2502
|
+
* date columns with many values collapse into range buckets; otherwise the
|
|
2503
|
+
* distinct raw values are collected and sorted naturally. Callable for any
|
|
2504
|
+
* column id (not just the open menu) so the filter row can drive it too.
|
|
2505
|
+
*/
|
|
2506
|
+
function facetValuesForColumn(columnId: string): Array<string> {
|
|
2507
|
+
if (!columnId) return [];
|
|
2433
2508
|
// Server-provided distinct values win (fetched + cached above).
|
|
2434
2509
|
if (props.serverFilterValues) return serverFacetValues[columnId] ?? [];
|
|
2435
2510
|
const column = allColumns.find((entry) => entry.id === columnId);
|
|
2436
|
-
if (!column) return []
|
|
2511
|
+
if (!column) return [];
|
|
2437
2512
|
// Range-bucketed facets for numeric / date columns with many values.
|
|
2438
2513
|
const buckets = facetBucketsByColumn.get(columnId);
|
|
2439
2514
|
if (buckets) return buckets.map((b) => b.label);
|
|
@@ -2445,6 +2520,14 @@ export function createSvGridController<
|
|
|
2445
2520
|
return Array.from(seen).sort((a, b) =>
|
|
2446
2521
|
a.localeCompare(b, undefined, { numeric: true }),
|
|
2447
2522
|
);
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
const columnMenuFacetValues = $derived.by(() => {
|
|
2526
|
+
// The funnel popover drives via `filterMenuFor`; the column menu's Filter
|
|
2527
|
+
// tab drives via `columnMenuFor`. Support whichever is open.
|
|
2528
|
+
const columnId = filterMenuFor ?? columnMenuFor;
|
|
2529
|
+
if (!columnId) return [] as Array<string>;
|
|
2530
|
+
return facetValuesForColumn(columnId);
|
|
2448
2531
|
});
|
|
2449
2532
|
|
|
2450
2533
|
const columnMenuVisibleFacets = $derived.by(() => {
|
|
@@ -2455,6 +2538,20 @@ export function createSvGridController<
|
|
|
2455
2538
|
);
|
|
2456
2539
|
});
|
|
2457
2540
|
|
|
2541
|
+
// Distinct values offered in the `in` / `notIn` suggestions dropdown for the
|
|
2542
|
+
// active chip input, narrowed by whatever the user has typed. Capped so a
|
|
2543
|
+
// high-cardinality column can't render thousands of rows into the popover.
|
|
2544
|
+
const IN_SUGGEST_LIMIT = 200;
|
|
2545
|
+
const inSuggestValues = $derived.by(() => {
|
|
2546
|
+
if (!inSuggestFor) return [] as Array<string>;
|
|
2547
|
+
const query = inSuggestQuery.trim().toLowerCase();
|
|
2548
|
+
const all = facetValuesForColumn(inSuggestFor);
|
|
2549
|
+
const matched = query
|
|
2550
|
+
? all.filter((value) => value.toLowerCase().includes(query))
|
|
2551
|
+
: all;
|
|
2552
|
+
return matched.slice(0, IN_SUGGEST_LIMIT);
|
|
2553
|
+
});
|
|
2554
|
+
|
|
2458
2555
|
|
|
2459
2556
|
|
|
2460
2557
|
|
|
@@ -2591,6 +2688,14 @@ export function createSvGridController<
|
|
|
2591
2688
|
set operatorMenuFor(v) { operatorMenuFor = v as never; },
|
|
2592
2689
|
get operatorMenuPos() { return operatorMenuPos; },
|
|
2593
2690
|
set operatorMenuPos(v) { operatorMenuPos = v as never; },
|
|
2691
|
+
get inSuggestFor() { return inSuggestFor; },
|
|
2692
|
+
set inSuggestFor(v) { inSuggestFor = v as never; },
|
|
2693
|
+
get inSuggestPos() { return inSuggestPos; },
|
|
2694
|
+
set inSuggestPos(v) { inSuggestPos = v as never; },
|
|
2695
|
+
get inSuggestQuery() { return inSuggestQuery; },
|
|
2696
|
+
set inSuggestQuery(v) { inSuggestQuery = v as never; },
|
|
2697
|
+
get inSuggestValues() { return inSuggestValues; },
|
|
2698
|
+
get facetValuesForColumn() { return facetValuesForColumn; },
|
|
2594
2699
|
get chooseColumnsPos() { return chooseColumnsPos; },
|
|
2595
2700
|
set chooseColumnsPos(v) { chooseColumnsPos = v as never; },
|
|
2596
2701
|
get contextMenuFor() { return contextMenuFor; },
|
|
@@ -2623,6 +2728,7 @@ export function createSvGridController<
|
|
|
2623
2728
|
set internalColumns(v) { internalColumns = v as never; },
|
|
2624
2729
|
get hiddenColumns() { return hiddenColumns; },
|
|
2625
2730
|
set hiddenColumns(v) { hiddenColumns = v as never; },
|
|
2731
|
+
get declaredHiddenColumns() { return declaredHiddenColumns; },
|
|
2626
2732
|
get toggleColumnGroup() { return toggleColumnGroup; },
|
|
2627
2733
|
get isColumnGroupCollapsed() { return isColumnGroupCollapsed; },
|
|
2628
2734
|
get externalSortEnabled() { return externalSortEnabled; },
|
|
@@ -2637,6 +2743,7 @@ export function createSvGridController<
|
|
|
2637
2743
|
get allColumns() { return allColumns; },
|
|
2638
2744
|
get headerGroups() { return headerGroups; },
|
|
2639
2745
|
get groupHeaderRows() { return groupHeaderRows; },
|
|
2746
|
+
get groupHeaderRowsWindowed() { return groupHeaderRowsWindowed; },
|
|
2640
2747
|
get pinnedOffsets() { return pinnedOffsets; },
|
|
2641
2748
|
get cellPinStyle() { return cellPinStyle; },
|
|
2642
2749
|
get isColumnPinned() { return isColumnPinned; },
|
|
@@ -2981,6 +3088,11 @@ export function createSvGridController<
|
|
|
2981
3088
|
get openColumnMenu() { return openColumnMenu; },
|
|
2982
3089
|
get openFilterMenu() { return openFilterMenu; },
|
|
2983
3090
|
get openOperatorMenu() { return openOperatorMenu; },
|
|
3091
|
+
get openInSuggest() { return openInSuggest; },
|
|
3092
|
+
get closeInSuggest() { return closeInSuggest; },
|
|
3093
|
+
get addFilterToken() { return addFilterToken; },
|
|
3094
|
+
get removeFilterToken() { return removeFilterToken; },
|
|
3095
|
+
get toggleFilterToken() { return toggleFilterToken; },
|
|
2984
3096
|
get sortColumnFromMenu() { return sortColumnFromMenu; },
|
|
2985
3097
|
get clearColumnSort() { return clearColumnSort; },
|
|
2986
3098
|
get groupByColumnFromMenu() { return groupByColumnFromMenu; },
|
|
@@ -3007,12 +3119,12 @@ export function createSvGridController<
|
|
|
3007
3119
|
const { showTooltipFor, hideTooltip, flushScheduledScrollSync, scheduleScrollSync, onBodyScroll } = createScrollSync<TFeatures, TData>(ctx);
|
|
3008
3120
|
const { onGridKeyDown, onWindowKeydown, onHeaderSortClick } = createKeyboard<TFeatures, TData>(ctx);
|
|
3009
3121
|
const { computeSummaries, hasRenderedColumn } = createSummaries<TFeatures, TData>(ctx);
|
|
3010
|
-
const { updateFilterRow, updateFilterOperator, updateFilterMenuValue, updateFilterMenuValueTo, toggleCheckboxWithKeyboard, isColumnFiltered, closeMenus, openChooseColumns, openColumnMenu, openFilterMenu, openOperatorMenu, sortColumnFromMenu, clearColumnSort, groupByColumnFromMenu, clearGroupingFromMenu, isFacetChecked, toggleFacetValue, isAllFacetsChecked, toggleAllFacets, clearColumnFilter, changePage, goToPage, setPageSize, openContextMenu, closeContextMenu, contextMenuItems, saveComment, removeComment, closeCommentEditor } = createMenus<TFeatures, TData>(ctx);
|
|
3122
|
+
const { updateFilterRow, updateFilterOperator, updateFilterMenuValue, updateFilterMenuValueTo, addFilterToken, removeFilterToken, toggleFilterToken, toggleCheckboxWithKeyboard, isColumnFiltered, closeMenus, openInSuggest, closeInSuggest, openChooseColumns, openColumnMenu, openFilterMenu, openOperatorMenu, sortColumnFromMenu, clearColumnSort, groupByColumnFromMenu, clearGroupingFromMenu, isFacetChecked, toggleFacetValue, isAllFacetsChecked, toggleAllFacets, clearColumnFilter, changePage, goToPage, setPageSize, openContextMenu, closeContextMenu, contextMenuItems, saveComment, removeComment, closeCommentEditor } = createMenus<TFeatures, TData>(ctx);
|
|
3011
3123
|
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellValidity, computeCellNote, getColumnEditorOptions, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender<TFeatures, TData>(ctx);
|
|
3012
3124
|
const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, startEditing, stopEditing, startFullRowEdit, setFullRowDraft, commitFullRowEdit, cancelFullRowEdit, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, focusOnMount, onCellDoubleClick, pasteFromClipboard, onGridPaste } = createEditing<TFeatures, TData>(ctx);
|
|
3013
3125
|
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, fillMarqueeEdges, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection<TFeatures, TData>(ctx);
|
|
3014
3126
|
const { cellPinStyle, isColumnPinned, getCurrentColumnOrder, emitColumnOrder, setColumnOrderInternal, applyColumnDrop, onColumnHeaderDragStart, onColumnHeaderDragOver, onColumnHeaderDragLeave, onColumnHeaderDrop, onColumnHeaderDragEnd, pinColumnLeft, pinColumnRight, unpinColumn, toggleColumnVisibleInPanel, moveColumnInPanel, toggleGroupInPanel, getColumnBaseWidth, getColumnWidth, startColumnResize, onColumnResizeMove, endColumnResize, measureText, autosizeColumn, autosizeAllColumns, resetColumns } = createColumns<TFeatures, TData>(ctx);
|
|
3015
|
-
const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd } = createRowDrag<TFeatures, TData>(ctx);
|
|
3127
|
+
const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd, destroyRowDrag } = createRowDrag<TFeatures, TData>(ctx);
|
|
3016
3128
|
const { register: registerAlignedGrid, broadcastScroll: broadcastAlignedScroll, broadcastWidths: broadcastAlignedWidths } = createAlignedGrids<TFeatures, TData>(ctx);
|
|
3017
3129
|
const { buildApi } = createGridApi<TFeatures, TData>(ctx);
|
|
3018
3130
|
const { readCellRaw, writeCellRaw, applyFillPattern, clearSelectedCellValues, startFillDrag, onFillPointerMove, onFillPointerUp, toggleBooleanCell, copySelectionToClipboard, clearSelectedCells, cutSelectionToClipboard } = createClipboard(ctx);
|
|
@@ -3030,5 +3142,17 @@ export function createSvGridController<
|
|
|
3030
3142
|
broadcastAlignedWidths();
|
|
3031
3143
|
});
|
|
3032
3144
|
|
|
3145
|
+
// Unmount teardown. Reads no reactive state, so the cleanup runs once on
|
|
3146
|
+
// destroy - tearing down an in-flight column resize (its document pointer
|
|
3147
|
+
// listeners, #58), a pending tooltip timer (#64), and any managed row drag
|
|
3148
|
+
// this grid still owns (#68). Each of these is a no-op when idle.
|
|
3149
|
+
$effect(() => {
|
|
3150
|
+
return () => {
|
|
3151
|
+
endColumnResize();
|
|
3152
|
+
hideTooltip();
|
|
3153
|
+
destroyRowDrag();
|
|
3154
|
+
};
|
|
3155
|
+
});
|
|
3156
|
+
|
|
3033
3157
|
return ctx;
|
|
3034
3158
|
}
|
package/src/SvGrid.css
CHANGED
|
@@ -485,13 +485,28 @@
|
|
|
485
485
|
justify-content: flex-end;
|
|
486
486
|
}
|
|
487
487
|
/* The custom vertical scrollbar overlays the right 16px of the viewport
|
|
488
|
-
(absolute, z-index 40)
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
.
|
|
492
|
-
|
|
488
|
+
(absolute, z-index 40) and does NOT shrink the scroll container's
|
|
489
|
+
clientWidth. Two cases keep the last column clear of it:
|
|
490
|
+
|
|
491
|
+
1. No horizontal overflow (content fits, nothing to scroll): nudge the last
|
|
492
|
+
column's right-aligned content in by the scrollbar width so numbers
|
|
493
|
+
aren't hidden underneath it. Left/center content already clears it.
|
|
494
|
+
2. Horizontal overflow: there's nothing to nudge (you scroll to the end and
|
|
495
|
+
the last 16px sit under the bar with no room left). Instead reserve a
|
|
496
|
+
trailing gutter on the scroll container so you can scroll the last column
|
|
497
|
+
fully out from under the overlay. See `.sv-grid-has-vscroll.sv-grid-has-hscroll`. */
|
|
498
|
+
.sv-grid-has-vscroll:not(.sv-grid-has-hscroll) .sv-grid-table tr > :last-child.sv-grid-cell[data-align="right"],
|
|
499
|
+
.sv-grid-has-vscroll:not(.sv-grid-has-hscroll) .sv-grid-table tr > :last-child.sv-grid-column[data-align="right"] {
|
|
493
500
|
padding-right: calc(var(--sg-cell-px, 7px) + 16px);
|
|
494
501
|
}
|
|
502
|
+
/* Case 2: reserve the overlay scrollbar's width as end-of-scroll space so the
|
|
503
|
+
final column can be scrolled clear of the vertical scrollbar. Modern browsers
|
|
504
|
+
include a scroll container's inline-end padding in its scrollable overflow;
|
|
505
|
+
the custom scrollbars are siblings of this container, so their layout is
|
|
506
|
+
unaffected. */
|
|
507
|
+
.sv-grid-container.sv-grid-has-vscroll.sv-grid-has-hscroll {
|
|
508
|
+
padding-right: 16px;
|
|
509
|
+
}
|
|
495
510
|
.sv-grid-column[data-align="center"] .sv-grid-header-sort {
|
|
496
511
|
justify-content: center;
|
|
497
512
|
}
|
|
@@ -857,6 +872,11 @@
|
|
|
857
872
|
flex-direction: row;
|
|
858
873
|
align-items: center;
|
|
859
874
|
gap: 4px;
|
|
875
|
+
box-sizing: border-box;
|
|
876
|
+
/* Inset the operator button + input from the cell edges so the filter row
|
|
877
|
+
lines up with the header label / body text (same --sg-cell-px gutter) and
|
|
878
|
+
isn't cramped against the top/side borders. */
|
|
879
|
+
padding: 5px var(--sg-filter-cell-px, var(--sg-cell-px, 7px));
|
|
860
880
|
}
|
|
861
881
|
|
|
862
882
|
.sv-grid-filter-operator {
|
|
@@ -871,6 +891,100 @@
|
|
|
871
891
|
box-sizing: border-box;
|
|
872
892
|
}
|
|
873
893
|
|
|
894
|
+
/* Invalid regex pattern: flag the input so the user knows the half-typed
|
|
895
|
+
pattern isn't being applied (it matches nothing until it compiles). */
|
|
896
|
+
.sv-grid-filter-value.sv-grid-filter-value-invalid,
|
|
897
|
+
.sv-grid-tp-filter-input.sv-grid-filter-value-invalid {
|
|
898
|
+
border-color: var(--sg-danger, #e5484d) !important;
|
|
899
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--sg-danger, #e5484d) 45%, transparent);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/* In / Not-in chip (token) input: chips + a flex-growing text box share one
|
|
903
|
+
bordered control that reads like a single field. */
|
|
904
|
+
.sv-grid-filter-chips {
|
|
905
|
+
flex: 3 1 0;
|
|
906
|
+
min-width: 0;
|
|
907
|
+
box-sizing: border-box;
|
|
908
|
+
display: flex;
|
|
909
|
+
flex-wrap: nowrap;
|
|
910
|
+
align-items: center;
|
|
911
|
+
gap: 3px;
|
|
912
|
+
padding: 2px 4px;
|
|
913
|
+
border: 1px solid var(--sg-input-border, #8794a8);
|
|
914
|
+
border-radius: 4px;
|
|
915
|
+
background: var(--sg-input-bg, #fff);
|
|
916
|
+
overflow: hidden;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/* A few chips stay visible; the rest collapse into a non-wrapping "+N" pill
|
|
920
|
+
that opens the value dropdown (select / unselect). */
|
|
921
|
+
.sv-grid-filter-chip .sv-grid-filter-chip-label {
|
|
922
|
+
max-width: 84px;
|
|
923
|
+
}
|
|
924
|
+
.sv-grid-filter-chip-more {
|
|
925
|
+
flex: none;
|
|
926
|
+
border: 0;
|
|
927
|
+
cursor: pointer;
|
|
928
|
+
font: inherit;
|
|
929
|
+
font-size: 11px;
|
|
930
|
+
line-height: 16px;
|
|
931
|
+
color: var(--sg-text, inherit);
|
|
932
|
+
}
|
|
933
|
+
.sv-grid-filter-chip-more:hover {
|
|
934
|
+
background: color-mix(in srgb, var(--sg-accent, #0b63f3) 26%, transparent);
|
|
935
|
+
}
|
|
936
|
+
.sv-grid-filter-chips:focus-within {
|
|
937
|
+
border-color: var(--sg-accent, #0b63f3);
|
|
938
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--sg-accent, #0b63f3) 45%, transparent);
|
|
939
|
+
}
|
|
940
|
+
.sv-grid-filter-chip {
|
|
941
|
+
display: inline-flex;
|
|
942
|
+
align-items: center;
|
|
943
|
+
gap: 2px;
|
|
944
|
+
max-width: 120px;
|
|
945
|
+
padding: 0 2px 0 6px;
|
|
946
|
+
border-radius: 3px;
|
|
947
|
+
font-size: 11px;
|
|
948
|
+
line-height: 16px;
|
|
949
|
+
background: color-mix(in srgb, var(--sg-accent, #0b63f3) 16%, transparent);
|
|
950
|
+
color: var(--sg-text, inherit);
|
|
951
|
+
}
|
|
952
|
+
.sv-grid-filter-chip-label {
|
|
953
|
+
overflow: hidden;
|
|
954
|
+
text-overflow: ellipsis;
|
|
955
|
+
white-space: nowrap;
|
|
956
|
+
}
|
|
957
|
+
.sv-grid-filter-chip-x {
|
|
958
|
+
flex: none;
|
|
959
|
+
display: inline-flex;
|
|
960
|
+
align-items: center;
|
|
961
|
+
justify-content: center;
|
|
962
|
+
width: 14px;
|
|
963
|
+
height: 14px;
|
|
964
|
+
padding: 0;
|
|
965
|
+
border: 0;
|
|
966
|
+
border-radius: 3px;
|
|
967
|
+
background: transparent;
|
|
968
|
+
color: inherit;
|
|
969
|
+
font-size: 13px;
|
|
970
|
+
line-height: 1;
|
|
971
|
+
cursor: pointer;
|
|
972
|
+
opacity: 0.7;
|
|
973
|
+
}
|
|
974
|
+
.sv-grid-filter-chip-x:hover {
|
|
975
|
+
opacity: 1;
|
|
976
|
+
background: color-mix(in srgb, var(--sg-accent, #0b63f3) 28%, transparent);
|
|
977
|
+
}
|
|
978
|
+
.sv-grid-filter-chips .sv-grid-filter-chip-input {
|
|
979
|
+
flex: 1 1 14px;
|
|
980
|
+
min-width: 14px;
|
|
981
|
+
border: 0 !important;
|
|
982
|
+
padding: 0 2px;
|
|
983
|
+
background: transparent;
|
|
984
|
+
box-shadow: none !important;
|
|
985
|
+
outline: none;
|
|
986
|
+
}
|
|
987
|
+
|
|
874
988
|
/* Brief glow used when the user clicks the funnel icon while the grid
|
|
875
989
|
is in row-filter mode - draws attention to where the input is so
|
|
876
990
|
the click isn't a no-op. */
|
|
@@ -1220,6 +1334,22 @@ select.sv-grid-fr-editor {
|
|
|
1220
1334
|
width: 100%;
|
|
1221
1335
|
font-size: 0.8125rem;
|
|
1222
1336
|
}
|
|
1337
|
+
/* The global "Filter all rows" search: a labelled input bar above the grid. Ships
|
|
1338
|
+
styled so it never renders as a bare, unpositioned text label + input. */
|
|
1339
|
+
.sv-grid-global-filter {
|
|
1340
|
+
display: inline-flex;
|
|
1341
|
+
align-items: center;
|
|
1342
|
+
gap: 8px;
|
|
1343
|
+
margin-bottom: 8px;
|
|
1344
|
+
font-size: 0.8125rem;
|
|
1345
|
+
font-weight: 550;
|
|
1346
|
+
color: var(--sg-muted, #64748b);
|
|
1347
|
+
}
|
|
1348
|
+
.sv-grid-global-filter input {
|
|
1349
|
+
height: 30px;
|
|
1350
|
+
min-width: 220px;
|
|
1351
|
+
font-size: 0.8125rem;
|
|
1352
|
+
}
|
|
1223
1353
|
.sv-grid-column-filter::placeholder,
|
|
1224
1354
|
.sv-grid-filter-value::placeholder,
|
|
1225
1355
|
.sv-grid-menu-search::placeholder,
|
|
@@ -1752,6 +1882,38 @@ select.sv-grid-fr-editor {
|
|
|
1752
1882
|
background: var(--sg-row-hover-bg, #eef2f8);
|
|
1753
1883
|
}
|
|
1754
1884
|
|
|
1885
|
+
/* In / Not-in value-suggestions dropdown (anchored under the chip input).
|
|
1886
|
+
Defined AFTER .sv-grid-menu so its tighter sizing wins - the element
|
|
1887
|
+
carries both classes. Compact rows keep a long value list scannable. */
|
|
1888
|
+
.sv-grid-menu.sv-grid-in-suggest {
|
|
1889
|
+
min-width: 140px;
|
|
1890
|
+
max-width: 260px;
|
|
1891
|
+
max-height: 220px;
|
|
1892
|
+
overflow-y: auto;
|
|
1893
|
+
font-size: 12px;
|
|
1894
|
+
}
|
|
1895
|
+
.sv-grid-in-suggest-item {
|
|
1896
|
+
gap: 6px;
|
|
1897
|
+
padding: 3px 8px;
|
|
1898
|
+
border-radius: 4px;
|
|
1899
|
+
line-height: 1.4;
|
|
1900
|
+
}
|
|
1901
|
+
.sv-grid-in-suggest-item[aria-selected="true"] {
|
|
1902
|
+
color: var(--sg-accent, #0b63f3);
|
|
1903
|
+
font-weight: 600;
|
|
1904
|
+
}
|
|
1905
|
+
.sv-grid-in-suggest-check {
|
|
1906
|
+
flex: none;
|
|
1907
|
+
width: 12px;
|
|
1908
|
+
text-align: center;
|
|
1909
|
+
color: var(--sg-accent, #0b63f3);
|
|
1910
|
+
}
|
|
1911
|
+
.sv-grid-in-suggest-label {
|
|
1912
|
+
overflow: hidden;
|
|
1913
|
+
text-overflow: ellipsis;
|
|
1914
|
+
white-space: nowrap;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1755
1917
|
.sv-grid-menu-item[aria-checked="true"] {
|
|
1756
1918
|
background: var(--sg-selection-bg, #eaf2ff);
|
|
1757
1919
|
color: var(--sg-accent, #0b63f3);
|
|
@@ -2376,6 +2538,32 @@ select.sv-grid-fr-editor {
|
|
|
2376
2538
|
color: var(--sg-fg, #101828);
|
|
2377
2539
|
}
|
|
2378
2540
|
|
|
2541
|
+
/* Placeholder shown when `scheduler` is set but the enterprise renderer that
|
|
2542
|
+
powers the calendar view has not been registered. */
|
|
2543
|
+
.sv-grid-scheduler-upsell {
|
|
2544
|
+
display: flex;
|
|
2545
|
+
flex-direction: column;
|
|
2546
|
+
gap: 6px;
|
|
2547
|
+
align-items: flex-start;
|
|
2548
|
+
justify-content: center;
|
|
2549
|
+
height: 100%;
|
|
2550
|
+
padding: 24px;
|
|
2551
|
+
text-align: left;
|
|
2552
|
+
border: 1px dashed var(--sg-border, #e4e7ec);
|
|
2553
|
+
border-radius: var(--sg-radius, 8px);
|
|
2554
|
+
background: color-mix(in srgb, var(--sg-fg, #1f2937) 5%, transparent);
|
|
2555
|
+
color: color-mix(in srgb, var(--sg-fg, #101828) 70%, transparent);
|
|
2556
|
+
}
|
|
2557
|
+
.sv-grid-scheduler-upsell strong { font-size: 0.95rem; }
|
|
2558
|
+
.sv-grid-scheduler-upsell p { margin: 0; font-size: 0.85rem; max-width: 42ch; }
|
|
2559
|
+
.sv-grid-scheduler-upsell code {
|
|
2560
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
2561
|
+
font-size: 0.82em;
|
|
2562
|
+
padding: 1px 4px;
|
|
2563
|
+
border-radius: 4px;
|
|
2564
|
+
background: color-mix(in srgb, var(--sg-fg, #101828) 8%, transparent);
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2379
2567
|
/* ===================================================================
|
|
2380
2568
|
Integrated chart panel (the `charting` prop)
|
|
2381
2569
|
=================================================================== */
|