@svgrid/grid 2.1.22 → 2.2.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.
Files changed (295) hide show
  1. package/README.md +9 -2
  2. package/dist/DockNodeView.svelte +306 -0
  3. package/dist/DockNodeView.svelte.d.ts +7 -0
  4. package/dist/DockPopoutHost.svelte +17 -0
  5. package/dist/DockPopoutHost.svelte.d.ts +14 -0
  6. package/dist/FlexRender.svelte +96 -96
  7. package/dist/GridFooter.svelte +178 -178
  8. package/dist/GridMenus.svelte +83 -3
  9. package/dist/SvAutoComplete.svelte +5 -3
  10. package/dist/SvCarousel.svelte +141 -163
  11. package/dist/SvColorInput.svelte +187 -186
  12. package/dist/SvColorInput.svelte.d.ts +1 -1
  13. package/dist/SvComboBox.svelte +7 -3
  14. package/dist/SvCommand.svelte +28 -85
  15. package/dist/SvContextMenu.svelte +116 -116
  16. package/dist/SvCountryInput.svelte +1 -1
  17. package/dist/SvDateRangeInput.svelte +1 -1
  18. package/dist/SvDateTimePicker.svelte +9 -3
  19. package/dist/SvDateTimePicker.svelte.d.ts +3 -0
  20. package/dist/SvDockLayout.svelte +244 -0
  21. package/dist/SvDockLayout.svelte.d.ts +77 -0
  22. package/dist/SvDockManager.svelte +922 -0
  23. package/dist/SvDockManager.svelte.d.ts +106 -0
  24. package/dist/SvDrawer.svelte +250 -228
  25. package/dist/SvDrawer.svelte.d.ts +4 -0
  26. package/dist/SvDropDownList.svelte +85 -13
  27. package/dist/SvDropDownList.svelte.d.ts +3 -0
  28. package/dist/SvField.svelte +21 -4
  29. package/dist/SvField.svelte.d.ts +1 -0
  30. package/dist/SvForm.svelte +201 -91
  31. package/dist/SvForm.svelte.d.ts +15 -22
  32. package/dist/SvGrid.controller.svelte.d.ts +32 -1
  33. package/dist/SvGrid.controller.svelte.js +156 -27
  34. package/dist/SvGrid.css +193 -5
  35. package/dist/SvGrid.svelte +3131 -2786
  36. package/dist/SvGrid.types.d.ts +452 -1
  37. package/dist/SvGridBoard.svelte +2339 -2339
  38. package/dist/SvGridChart.svelte +1724 -1724
  39. package/dist/SvGridChartPanel.svelte +485 -485
  40. package/dist/SvGridDropdown.svelte +689 -682
  41. package/dist/SvGridSelect.svelte +1 -0
  42. package/dist/SvGroupCell.svelte +109 -109
  43. package/dist/SvListBox.svelte +36 -14
  44. package/dist/SvMaskedInput.svelte +4 -3
  45. package/dist/SvMaskedInput.svelte.d.ts +1 -1
  46. package/dist/SvMenu.svelte +124 -124
  47. package/dist/SvMenuList.svelte +117 -165
  48. package/dist/SvMenuList.svelte.d.ts +2 -13
  49. package/dist/SvModal.svelte +12 -29
  50. package/dist/SvMultiSelect.svelte +1 -0
  51. package/dist/SvNumberInput.svelte +131 -130
  52. package/dist/SvNumberInput.svelte.d.ts +1 -1
  53. package/dist/SvOtpInput.svelte +158 -158
  54. package/dist/SvPagination.svelte +18 -18
  55. package/dist/SvPasswordInput.svelte +3 -2
  56. package/dist/SvPasswordInput.svelte.d.ts +1 -1
  57. package/dist/SvPhoneInput.svelte +3 -2
  58. package/dist/SvPhoneInput.svelte.d.ts +1 -1
  59. package/dist/SvPopover.svelte +139 -139
  60. package/dist/SvRowGroupPanel.svelte +170 -170
  61. package/dist/SvScrollArea.svelte +61 -61
  62. package/dist/SvStepper.svelte +6 -9
  63. package/dist/SvTagsInput.svelte +3 -2
  64. package/dist/SvTagsInput.svelte.d.ts +1 -1
  65. package/dist/SvTooltip.svelte +127 -138
  66. package/dist/SvTour.svelte +204 -176
  67. package/dist/SvTree.svelte +377 -377
  68. package/dist/SvTreeSelect.svelte +230 -229
  69. package/dist/build-api.js +76 -54
  70. package/dist/cdn/svgrid.js +10913 -6450
  71. package/dist/cdn/svgrid.svelte-external.js +9628 -5481
  72. package/dist/chart-export.js +8 -8
  73. package/dist/columns.js +6 -0
  74. package/dist/conditional-formatting.d.ts +67 -3
  75. package/dist/conditional-formatting.js +124 -17
  76. package/dist/createCarousel.svelte.d.ts +72 -0
  77. package/dist/createCarousel.svelte.js +80 -0
  78. package/dist/createCommand.svelte.d.ts +62 -0
  79. package/dist/createCommand.svelte.js +109 -0
  80. package/dist/createForm.svelte.d.ts +62 -0
  81. package/dist/createForm.svelte.js +374 -0
  82. package/dist/createMenu.svelte.d.ts +88 -0
  83. package/dist/createMenu.svelte.js +153 -0
  84. package/dist/createOverlay.svelte.d.ts +38 -0
  85. package/dist/createOverlay.svelte.js +68 -0
  86. package/dist/createPagination.d.ts +74 -0
  87. package/dist/createPagination.js +71 -0
  88. package/dist/createPopoverSelect.svelte.js +6 -12
  89. package/dist/createStepper.d.ts +44 -0
  90. package/dist/createStepper.js +26 -0
  91. package/dist/createTooltip.svelte.d.ts +32 -0
  92. package/dist/createTooltip.svelte.js +61 -0
  93. package/dist/datetime/timezone.d.ts +36 -0
  94. package/dist/datetime/timezone.js +110 -0
  95. package/dist/dock-context.d.ts +52 -0
  96. package/dist/dock-context.js +1 -0
  97. package/dist/dock-manager-model.d.ts +136 -0
  98. package/dist/dock-manager-model.js +514 -0
  99. package/dist/dock-model.d.ts +105 -0
  100. package/dist/dock-model.js +288 -0
  101. package/dist/dock-popout.d.ts +30 -0
  102. package/dist/dock-popout.js +81 -0
  103. package/dist/editor-contract.d.ts +2 -0
  104. package/dist/filter-operators.d.ts +6 -0
  105. package/dist/filter-operators.js +35 -2
  106. package/dist/filtering/excel-filters.d.ts +18 -1
  107. package/dist/filtering/excel-filters.js +56 -0
  108. package/dist/form-field.d.ts +73 -0
  109. package/dist/form-field.js +15 -0
  110. package/dist/index.d.ts +25 -5
  111. package/dist/index.js +40 -4
  112. package/dist/list-nav.d.ts +18 -0
  113. package/dist/list-nav.js +30 -0
  114. package/dist/list-option.d.ts +2 -0
  115. package/dist/menu-item.d.ts +20 -0
  116. package/dist/menu-item.js +1 -0
  117. package/dist/menus.d.ts +5 -0
  118. package/dist/menus.js +68 -16
  119. package/dist/panel-resize.d.ts +25 -0
  120. package/dist/panel-resize.js +40 -0
  121. package/dist/popover.d.ts +29 -3
  122. package/dist/popover.js +22 -6
  123. package/dist/recurrence.d.ts +33 -7
  124. package/dist/recurrence.js +151 -13
  125. package/dist/row-drag.d.ts +8 -0
  126. package/dist/row-drag.js +11 -0
  127. package/dist/scheduler-ical.d.ts +30 -0
  128. package/dist/scheduler-ical.js +180 -0
  129. package/dist/scheduler-model.d.ts +269 -0
  130. package/dist/scheduler-model.js +602 -0
  131. package/dist/scheduler-view.svelte.d.ts +22 -0
  132. package/dist/scheduler-view.svelte.js +13 -0
  133. package/dist/spreadsheet.js +20 -12
  134. package/dist/ui-app.types.d.ts +3 -0
  135. package/package.json +14 -10
  136. package/src/DockNodeView.svelte +306 -0
  137. package/src/DockPopoutHost.svelte +17 -0
  138. package/src/FlexRender.svelte +96 -96
  139. package/src/GridFooter.svelte +178 -178
  140. package/src/GridMenus.svelte +83 -3
  141. package/src/SvAutoComplete.svelte +5 -3
  142. package/src/SvCarousel.svelte +141 -163
  143. package/src/SvCarousel.test.ts +68 -0
  144. package/src/SvColorInput.svelte +187 -186
  145. package/src/SvComboBox.svelte +7 -3
  146. package/src/SvCommand.svelte +28 -85
  147. package/src/SvCommand.test.ts +78 -0
  148. package/src/SvContextMenu.svelte +116 -116
  149. package/src/SvCountryInput.svelte +1 -1
  150. package/src/SvDateRangeInput.svelte +1 -1
  151. package/src/SvDateTimePicker.svelte +9 -3
  152. package/src/SvDockLayout.svelte +244 -0
  153. package/src/SvDockManager.svelte +922 -0
  154. package/src/SvDrawer.svelte +250 -228
  155. package/src/SvDropDownList.svelte +85 -13
  156. package/src/SvField.svelte +21 -4
  157. package/src/SvForm.svelte +201 -91
  158. package/src/SvForm.test.ts +299 -0
  159. package/src/SvGrid.controller.svelte.ts +3158 -3034
  160. package/src/SvGrid.css +193 -5
  161. package/src/SvGrid.svelte +3131 -2786
  162. package/src/SvGrid.types.ts +1489 -1027
  163. package/src/SvGridBoard.svelte +2339 -2339
  164. package/src/SvGridChart.svelte +1724 -1724
  165. package/src/SvGridChartPanel.svelte +485 -485
  166. package/src/SvGridDropdown.svelte +689 -682
  167. package/src/SvGridSelect.svelte +1 -0
  168. package/src/SvGroupCell.svelte +109 -109
  169. package/src/SvListBox.svelte +36 -14
  170. package/src/SvMaskedInput.svelte +4 -3
  171. package/src/SvMenu.svelte +124 -124
  172. package/src/SvMenu.test.ts +97 -73
  173. package/src/SvMenuList.svelte +117 -165
  174. package/src/SvModal.svelte +12 -29
  175. package/src/SvModal.test.ts +82 -0
  176. package/src/SvMultiSelect.svelte +1 -0
  177. package/src/SvNumberInput.svelte +131 -130
  178. package/src/SvOtpInput.svelte +158 -158
  179. package/src/SvPagination.svelte +18 -18
  180. package/src/SvPasswordInput.svelte +3 -2
  181. package/src/SvPhoneInput.svelte +3 -2
  182. package/src/SvPopover.svelte +139 -139
  183. package/src/SvRowGroupPanel.svelte +170 -170
  184. package/src/SvScrollArea.svelte +61 -61
  185. package/src/SvStepper.svelte +6 -9
  186. package/src/SvTagsInput.svelte +3 -2
  187. package/src/SvTooltip.svelte +127 -138
  188. package/src/SvTooltip.test.ts +73 -0
  189. package/src/SvTour.svelte +204 -176
  190. package/src/SvTree.svelte +377 -377
  191. package/src/SvTreeSelect.svelte +230 -229
  192. package/src/a11y.contract.test.ts +49 -49
  193. package/src/a11y.test.ts +59 -59
  194. package/src/a11y.ts +61 -61
  195. package/src/build-api.ts +870 -847
  196. package/src/cell-formatting.ts +169 -169
  197. package/src/cell-render.ts +469 -469
  198. package/src/chart-export.ts +201 -201
  199. package/src/chart.ts +2296 -2296
  200. package/src/collaboration.test.ts +104 -104
  201. package/src/collaboration.ts +167 -167
  202. package/src/columns.ts +5 -0
  203. package/src/conditional-formatting.test.ts +104 -0
  204. package/src/conditional-formatting.ts +176 -19
  205. package/src/core.performance.test.ts +30 -30
  206. package/src/core.ts +1111 -1111
  207. package/src/createAutocomplete.svelte.ts +132 -132
  208. package/src/createCarousel.svelte.ts +104 -0
  209. package/src/createCombobox.svelte.ts +179 -179
  210. package/src/createCommand.svelte.ts +125 -0
  211. package/src/createCountryInput.svelte.ts +157 -157
  212. package/src/createDropdownList.svelte.ts +162 -162
  213. package/src/createForm.svelte.ts +333 -0
  214. package/src/createGrid.svelte.ts +42 -42
  215. package/src/createGrid.test.ts +10 -10
  216. package/src/createGridState.svelte.ts +17 -17
  217. package/src/createMenu.svelte.ts +202 -0
  218. package/src/createOverlay.svelte.ts +101 -0
  219. package/src/createPagination.test.ts +65 -0
  220. package/src/createPagination.ts +84 -0
  221. package/src/createPopoverSelect.svelte.ts +206 -210
  222. package/src/createStepper.test.ts +62 -0
  223. package/src/createStepper.ts +61 -0
  224. package/src/createTooltip.svelte.ts +86 -0
  225. package/src/createTree.svelte.ts +319 -319
  226. package/src/datetime/timezone.test.ts +69 -0
  227. package/src/datetime/timezone.ts +134 -0
  228. package/src/dock-context.ts +47 -0
  229. package/src/dock-manager-model.test.ts +275 -0
  230. package/src/dock-manager-model.ts +593 -0
  231. package/src/dock-model.test.ts +163 -0
  232. package/src/dock-model.ts +368 -0
  233. package/src/dock-popout.ts +94 -0
  234. package/src/editing.test.ts +859 -859
  235. package/src/editing.ts +675 -675
  236. package/src/editor-contract.ts +2 -0
  237. package/src/export-data-api.test.ts +126 -126
  238. package/src/export-format.test.ts +107 -107
  239. package/src/export-format.ts +598 -598
  240. package/src/filter-operators.test.ts +59 -0
  241. package/src/filter-operators.ts +35 -2
  242. package/src/filtering/excel-filters.test.ts +86 -1
  243. package/src/filtering/excel-filters.ts +65 -0
  244. package/src/flex-render.ts +3 -3
  245. package/src/form-field.ts +87 -0
  246. package/src/index.ts +623 -469
  247. package/src/keyboard.test.ts +59 -59
  248. package/src/keyboard.ts +97 -97
  249. package/src/list-nav.test.ts +49 -0
  250. package/src/list-nav.ts +29 -0
  251. package/src/list-option.ts +2 -0
  252. package/src/menu-item.ts +21 -0
  253. package/src/menus.test.ts +48 -0
  254. package/src/menus.ts +639 -582
  255. package/src/merge-objects.ts +48 -48
  256. package/src/overlays.test.ts +90 -90
  257. package/src/panel-resize.ts +52 -0
  258. package/src/popover.bounds.test.ts +93 -0
  259. package/src/popover.ts +51 -7
  260. package/src/recurrence.test.ts +88 -1
  261. package/src/recurrence.ts +166 -15
  262. package/src/render-component.ts +28 -28
  263. package/src/row-drag.ts +10 -0
  264. package/src/scheduler-ical.test.ts +68 -0
  265. package/src/scheduler-ical.ts +180 -0
  266. package/src/scheduler-model.test.ts +546 -0
  267. package/src/scheduler-model.ts +853 -0
  268. package/src/scheduler-view.svelte.ts +33 -0
  269. package/src/selection.test.ts +754 -754
  270. package/src/selection.ts +600 -600
  271. package/src/server-data-source.test.ts +289 -289
  272. package/src/server-data-source.ts +413 -413
  273. package/src/sparkline.test.ts +68 -68
  274. package/src/sparkline.ts +169 -169
  275. package/src/spreadsheet.test.ts +489 -489
  276. package/src/spreadsheet.ts +312 -304
  277. package/src/static-functions.ts +11 -11
  278. package/src/subscribe.ts +38 -38
  279. package/src/svgrid-wrapper.types.ts +501 -501
  280. package/src/svgrid.behavior.test.ts +706 -706
  281. package/src/svgrid.charting.test.ts +527 -527
  282. package/src/svgrid.effect-driven-filter.svelte.test.ts +116 -0
  283. package/src/svgrid.features.test.ts +157 -157
  284. package/src/svgrid.new-features.wrapper.test.ts +251 -251
  285. package/src/svgrid.wrapper.test.ts +40 -40
  286. package/src/test-setup.ts +62 -62
  287. package/src/themes/index.ts +159 -159
  288. package/src/ui-app.types.ts +3 -0
  289. package/src/virtual.test.ts +6 -4
  290. package/src/virtualization/column-virtualizer.test.ts +27 -27
  291. package/src/virtualization/column-virtualizer.ts +30 -30
  292. package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
  293. package/src/virtualization/types.ts +30 -30
  294. package/src/virtualization/virtualizer.test.ts +47 -47
  295. package/src/virtualization/virtualizer.ts +296 -296
@@ -121,6 +121,11 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
121
121
  filterMenuPos: MenuPosition;
122
122
  operatorMenuFor: string | null;
123
123
  operatorMenuPos: MenuPosition;
124
+ inSuggestFor: string | null;
125
+ inSuggestPos: MenuPosition;
126
+ inSuggestQuery: string;
127
+ readonly inSuggestValues: string[];
128
+ readonly facetValuesForColumn: (columnId: string) => Array<string>;
124
129
  chooseColumnsPos: MenuPosition | null;
125
130
  contextMenuFor: ContextMenuTarget<TData> | null;
126
131
  contextMenuPos: MenuPosition;
@@ -155,6 +160,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
155
160
  internalData: readonly TData[];
156
161
  internalColumns: ColumnDef<TFeatures, TData>[];
157
162
  hiddenColumns: Record<string, boolean>;
163
+ readonly declaredHiddenColumns: Record<string, boolean>;
158
164
  readonly toggleColumnGroup: (groupId: string) => void;
159
165
  readonly isColumnGroupCollapsed: (groupId: string) => boolean;
160
166
  readonly externalSortEnabled: boolean;
@@ -188,6 +194,26 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
188
194
  collapsed: boolean;
189
195
  }[];
190
196
  }[];
197
+ readonly groupHeaderRowsWindowed: {
198
+ id: string;
199
+ cells: {
200
+ key: string;
201
+ label: string;
202
+ colSpan: number;
203
+ widthPx: number;
204
+ /** First leaf-column index this cell spans. */
205
+ firstLeafIndex: number;
206
+ /** True for the placeholder cells that fill the column above an
207
+ * early-bottoming leaf (e.g. the row-label column to the left of a
208
+ * multi-level value tree). They render as empty cells so the
209
+ * layout stays aligned without showing duplicate labels. */
210
+ isPlaceholder: boolean;
211
+ /** Set when this group cell has a collapse toggle. */
212
+ groupId?: string;
213
+ collapsible: boolean;
214
+ collapsed: boolean;
215
+ }[];
216
+ }[];
191
217
  readonly pinnedOffsets: {
192
218
  left: Record<string, number>;
193
219
  right: Record<string, number>;
@@ -435,7 +461,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
435
461
  readonly computePinnedCellClass: (rowData: TData, column: Column<TData>) => string;
436
462
  readonly isRowSelected: (rowId: string) => boolean;
437
463
  readonly toggleRowSelectionById: (rowId: string) => void;
438
- readonly headerSelectionState: "all" | "none" | "some";
464
+ readonly headerSelectionState: "none" | "all" | "some";
439
465
  readonly toggleSelectAllRows: () => void;
440
466
  userHasActivatedCell: boolean;
441
467
  readonly setActiveCell: (rowIndex: number, colIndex: number) => void;
@@ -552,6 +578,11 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
552
578
  readonly openColumnMenu: (event: MouseEvent, columnId: string) => void;
553
579
  readonly openFilterMenu: (event: MouseEvent, columnId: string) => void;
554
580
  readonly openOperatorMenu: (event: MouseEvent, columnId: string) => void;
581
+ readonly openInSuggest: (anchor: HTMLElement, columnId: string) => void;
582
+ readonly closeInSuggest: () => void;
583
+ readonly addFilterToken: (columnId: string, token: string) => void;
584
+ readonly removeFilterToken: (columnId: string, token: string) => void;
585
+ readonly toggleFilterToken: (columnId: string, token: string) => void;
555
586
  readonly sortColumnFromMenu: (columnId: string, desc: boolean) => void;
556
587
  readonly clearColumnSort: (columnId: string) => void;
557
588
  readonly groupByColumnFromMenu: (columnId: string) => void;
@@ -1,7 +1,7 @@
1
1
  import { applyExcelFilter, normalizeForFilter, createColumnVirtualizer, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createSvelteVirtualizer, createSortedRowModel, createSvGrid, getGridCellDomId, rowsToChartSpec, sortFns, } from "./index";
2
2
  import { createRowScrollScaling, resolveMaxDomHeight, } from "./virtualization/scroll-scaling";
3
3
  import "./sv-grid-scrollbar";
4
- import { computeColumnStat, formatsNeedingStats, } from "./conditional-formatting";
4
+ import { computeColumnStat, formatsNeedingStats, formatNeedsStats, } from "./conditional-formatting";
5
5
  import { rawToNumber, } from "./SvGrid.helpers";
6
6
  import { createFeatures } from "./features";
7
7
  import { createScrollSync, } from "./scroll-sync";
@@ -41,9 +41,12 @@ const MAX_DOM_SCROLL_HEIGHT_FALLBACK = 8000000;
41
41
  * matters because mobile WebKit/Blink can report a generous `offsetHeight` yet
42
42
  * expose a smaller scrollable range - trusting the layout height alone is what
43
43
  * stranded the last rows on phones. Using a real scroll container also folds in
44
- * DPR clamping for free. Cached for the page lifetime; constant per browser.
44
+ * DPR clamping for free. Cached, but re-probed when the device pixel ratio
45
+ * changes (browser zoom) since the physical-pixel cap moves with it (#85).
45
46
  */
46
47
  let detectedMaxDomHeight = null;
48
+ let detectedAtDpr = 0;
49
+ const currentDpr = () => (typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1);
47
50
  /**
48
51
  * Seed the `hiddenColumns` map from any column def marked `visible: false`.
49
52
  * Walks groups so a hidden group hides all of its leaf columns. Keyed by the
@@ -81,7 +84,7 @@ function getMaxDomScrollHeight() {
81
84
  return forced;
82
85
  }
83
86
  }
84
- if (detectedMaxDomHeight != null)
87
+ if (detectedMaxDomHeight != null && detectedAtDpr === currentDpr())
85
88
  return detectedMaxDomHeight;
86
89
  if (typeof document === "undefined" || !document.body) {
87
90
  return MAX_DOM_SCROLL_HEIGHT_FALLBACK;
@@ -107,6 +110,7 @@ function getMaxDomScrollHeight() {
107
110
  catch {
108
111
  detectedMaxDomHeight = MAX_DOM_SCROLL_HEIGHT_FALLBACK;
109
112
  }
113
+ detectedAtDpr = currentDpr();
110
114
  return detectedMaxDomHeight;
111
115
  }
112
116
  /**
@@ -258,6 +262,12 @@ export function createSvGridController(props) {
258
262
  let filterMenuPos = $state({ x: 0, y: 0 });
259
263
  let operatorMenuFor = $state(null);
260
264
  let operatorMenuPos = $state({ x: 0, y: 0 });
265
+ // `in` / `notIn` value-suggestions dropdown: the column whose chip input is
266
+ // active, its anchor position, and the current typed query (for filtering
267
+ // the distinct-value list as the user types).
268
+ let inSuggestFor = $state(null);
269
+ let inSuggestPos = $state({ x: 0, y: 0 });
270
+ let inSuggestQuery = $state("");
261
271
  let chooseColumnsPos = $state(null);
262
272
  let contextMenuFor = $state(null);
263
273
  let contextMenuPos = $state({ x: 0, y: 0 });
@@ -366,6 +376,10 @@ export function createSvGridController(props) {
366
376
  let internalColumns = $state.raw(resolveCols(props.columns));
367
377
  // svelte-ignore state_referenced_locally
368
378
  let hiddenColumns = $state(initialHiddenColumns(props.columns));
379
+ // The columns declared `visible: false` in the ColumnDefs. Kept separately so
380
+ // applying a saved view (setState) can re-seed from these and never un-hide a
381
+ // column the app declared hidden (#54).
382
+ const declaredHiddenColumns = $derived(initialHiddenColumns(props.columns));
369
383
  // Collapsible column groups (columnGroupShow). Meta is derived from the tree;
370
384
  // `collapsedColumnGroups` is the live set of collapsed group ids, seeded once
371
385
  // from each collapsible group's `openByDefault` (default: collapsed).
@@ -680,6 +694,11 @@ export function createSvGridController(props) {
680
694
  const left = {};
681
695
  let leftAcc = rowNumberWidth + selectionWidth;
682
696
  for (const id of effectivePinning.left) {
697
+ // Hidden pinned columns don't render, so they must contribute neither an
698
+ // offset entry nor width - otherwise the next visible pinned column is
699
+ // pushed out by a phantom gap (#75).
700
+ if (hiddenColumns[id] || hiddenByGroupCollapse[id])
701
+ continue;
683
702
  left[id] = leftAcc;
684
703
  leftAcc += getColumnWidth(id);
685
704
  }
@@ -689,6 +708,8 @@ export function createSvGridController(props) {
689
708
  const id = effectivePinning.right[i];
690
709
  if (!id)
691
710
  continue;
711
+ if (hiddenColumns[id] || hiddenByGroupCollapse[id])
712
+ continue;
692
713
  right[id] = rightAcc;
693
714
  rightAcc += getColumnWidth(id);
694
715
  }
@@ -717,23 +738,33 @@ export function createSvGridController(props) {
717
738
  const formats = props.conditionalFormats;
718
739
  if (!formats?.length || !formatsNeedingStats(formats))
719
740
  return map;
741
+ // `visible` (default): the currently displayed rows (filtered + paged),
742
+ // so the heat map reflects what the user is looking at. `all`: the full
743
+ // unfiltered dataset, for a stable scale independent of filter/paging.
744
+ const scanAll = props.conditionalStatScope === "all";
720
745
  for (const column of allColumns) {
721
- const needs = formats.some((f) => (f.type === "colorScale" || f.type === "dataBar") &&
722
- (!f.columns || f.columns.includes(column.id)));
746
+ const needs = formats.some((f) => formatNeedsStats(f) && (!f.columns || f.columns.includes(column.id)));
723
747
  if (!needs)
724
748
  continue;
725
749
  const def = column.columnDef;
726
750
  const fieldFn = def.fieldFn;
727
751
  const field = def.field;
728
- const stat = computeColumnStat((function* () {
729
- for (const row of allRows) {
730
- yield fieldFn
731
- ? fieldFn(row.original)
732
- : field
733
- ? row.original[field]
734
- : row.getCellValueByColumnId(column.id);
735
- }
736
- })());
752
+ const stat = computeColumnStat(scanAll
753
+ ? (function* () {
754
+ // Raw data objects (unfiltered); field/fieldFn only - no Row wrapper.
755
+ for (const obj of internalData) {
756
+ yield fieldFn ? fieldFn(obj) : field ? obj[field] : undefined;
757
+ }
758
+ })()
759
+ : (function* () {
760
+ for (const row of allRows) {
761
+ yield fieldFn
762
+ ? fieldFn(row.original)
763
+ : field
764
+ ? row.original[field]
765
+ : row.getCellValueByColumnId(column.id);
766
+ }
767
+ })());
737
768
  if (stat)
738
769
  map.set(column.id, stat);
739
770
  }
@@ -785,7 +816,7 @@ export function createSvGridController(props) {
785
816
  }
786
817
  // A single condition is "active" if it has the value(s) it needs.
787
818
  const condActive = (op, value, valueTo) => {
788
- if (op === "isBlank")
819
+ if (op === "isBlank" || op === "isNotBlank")
789
820
  return true;
790
821
  if (op === "between")
791
822
  return value.trim().length > 0 && (valueTo ?? "").trim().length > 0;
@@ -890,11 +921,15 @@ export function createSvGridController(props) {
890
921
  const callback = props.onRowSelectionChange;
891
922
  if (!callback)
892
923
  return;
893
- const data = internalData;
924
+ // Resolve selected rows by row id (not numeric index) so this is correct
925
+ // with a custom getRowId and after sorting (#45). Iterate the full filtered
926
+ // model - not the paginated slice - so selections on other pages are kept.
894
927
  const selectedRows = [];
895
- for (let i = 0; i < data.length; i++) {
896
- if (rowSelectionState[String(i)])
897
- selectedRows.push(data[i]);
928
+ for (const row of allRowsBeforePagination) {
929
+ if (isGroupRow(row))
930
+ continue;
931
+ if (rowSelectionState[row.id])
932
+ selectedRows.push(row.original);
898
933
  }
899
934
  callback(rowSelectionState, selectedRows);
900
935
  });
@@ -1389,7 +1424,7 @@ export function createSvGridController(props) {
1389
1424
  return;
1390
1425
  const menuEntries = Object.entries(filterMenuValues)
1391
1426
  .filter(([, f]) => {
1392
- if (f.operator === "isBlank")
1427
+ if (f.operator === "isBlank" || f.operator === "isNotBlank")
1393
1428
  return true;
1394
1429
  if (f.operator === "between") {
1395
1430
  return f.value.trim().length > 0 && (f.valueTo ?? "").trim().length > 0;
@@ -1586,6 +1621,46 @@ export function createSvGridController(props) {
1586
1621
  const columnWindowStart = $derived.by(() => renderedColumnItems[0]?.start ?? 0);
1587
1622
  const columnWindowEnd = $derived.by(() => renderedColumnItems[renderedColumnItems.length - 1]?.end ?? 0);
1588
1623
  const columnWindowRightSpacer = $derived.by(() => Math.max(totalColumnWidth - columnWindowEnd, 0));
1624
+ /**
1625
+ * `groupHeaderRows` clipped to the rendered column window. The multi-level
1626
+ * group-header band must virtualize in lock-step with the leaf-header row
1627
+ * and the body: otherwise a group cell spanning off-window leaves renders a
1628
+ * header over empty space (its cells were dropped by body virtualization),
1629
+ * so wide grouped/pivot grids show group headers with no cells beneath.
1630
+ *
1631
+ * We reuse each cell's `firstLeafIndex` + `colSpan` (leaf-index range) to
1632
+ * clip against the rendered leaf window, dropping fully-off-window cells and
1633
+ * shrinking partially-visible ones to their visible leaves. The template
1634
+ * frames the result with the SAME left/right spacers the leaf row uses, so
1635
+ * everything stays pixel-aligned. When virtualization is off this is a
1636
+ * pass-through.
1637
+ */
1638
+ const groupHeaderRowsWindowed = $derived.by(() => {
1639
+ const base = groupHeaderRows;
1640
+ if (!columnVirtualizationEnabled || base.length === 0)
1641
+ return base;
1642
+ const items = renderedColumnItems;
1643
+ if (items.length === 0)
1644
+ return base;
1645
+ const winFirst = items[0].index;
1646
+ const winLast = items[items.length - 1].index; // inclusive
1647
+ return base.map((row) => {
1648
+ const cells = [];
1649
+ for (const cell of row.cells) {
1650
+ const start = cell.firstLeafIndex;
1651
+ const end = start + cell.colSpan; // exclusive
1652
+ const from = Math.max(start, winFirst);
1653
+ const to = Math.min(end, winLast + 1);
1654
+ if (to <= from)
1655
+ continue; // cell is entirely outside the window
1656
+ let widthPx = 0;
1657
+ for (let i = from; i < to; i += 1)
1658
+ widthPx += getColumnWidth(allColumns[i].id);
1659
+ cells.push({ ...cell, colSpan: to - from, widthPx, firstLeafIndex: from });
1660
+ }
1661
+ return { ...row, cells };
1662
+ });
1663
+ });
1589
1664
  const activeCell = $derived.by(() => {
1590
1665
  gridStateVersion;
1591
1666
  return (grid.getState().activeCell ?? { rowIndex: 0, colIndex: 0, cellId: null });
@@ -1848,7 +1923,9 @@ export function createSvGridController(props) {
1848
1923
  // Narrow responsive mode pans instead of scaling, so skip fit scaling.
1849
1924
  if (!props.fitColumns || isNarrowResponsive)
1850
1925
  return null;
1851
- const cols = grid.getAllColumns().filter((c) => !hiddenColumns[c.id]);
1926
+ // Exclude columns hidden by a collapsed group too (#57) - otherwise their
1927
+ // widths stay in the fit distribution and leave a blank gap at the edge.
1928
+ const cols = grid.getAllColumns().filter((c) => !hiddenColumns[c.id] && !hiddenByGroupCollapse[c.id]);
1852
1929
  if (!cols.length)
1853
1930
  return null;
1854
1931
  const rowNumberWidth = showRowNumbersEffective ? rowNumberColumnWidth : 0;
@@ -1993,10 +2070,14 @@ export function createSvGridController(props) {
1993
2070
  cancelled = true;
1994
2071
  };
1995
2072
  });
1996
- const columnMenuFacetValues = $derived.by(() => {
1997
- // The funnel popover drives via `filterMenuFor`; the column menu's Filter
1998
- // tab drives via `columnMenuFor`. Support whichever is open.
1999
- const columnId = filterMenuFor ?? columnMenuFor;
2073
+ /**
2074
+ * Distinct values for a column, for the set-filter checklist and the
2075
+ * `in` / `notIn` suggestions dropdown. Server-provided values win; numeric /
2076
+ * date columns with many values collapse into range buckets; otherwise the
2077
+ * distinct raw values are collected and sorted naturally. Callable for any
2078
+ * column id (not just the open menu) so the filter row can drive it too.
2079
+ */
2080
+ function facetValuesForColumn(columnId) {
2000
2081
  if (!columnId)
2001
2082
  return [];
2002
2083
  // Server-provided distinct values win (fetched + cached above).
@@ -2015,6 +2096,14 @@ export function createSvGridController(props) {
2015
2096
  seen.add(String(getColumnAccessorValue(rowData, column) ?? ""));
2016
2097
  }
2017
2098
  return Array.from(seen).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
2099
+ }
2100
+ const columnMenuFacetValues = $derived.by(() => {
2101
+ // The funnel popover drives via `filterMenuFor`; the column menu's Filter
2102
+ // tab drives via `columnMenuFor`. Support whichever is open.
2103
+ const columnId = filterMenuFor ?? columnMenuFor;
2104
+ if (!columnId)
2105
+ return [];
2106
+ return facetValuesForColumn(columnId);
2018
2107
  });
2019
2108
  const columnMenuVisibleFacets = $derived.by(() => {
2020
2109
  const query = columnMenuSearch.trim().toLowerCase();
@@ -2022,6 +2111,20 @@ export function createSvGridController(props) {
2022
2111
  return columnMenuFacetValues;
2023
2112
  return columnMenuFacetValues.filter((value) => value.toLowerCase().includes(query));
2024
2113
  });
2114
+ // Distinct values offered in the `in` / `notIn` suggestions dropdown for the
2115
+ // active chip input, narrowed by whatever the user has typed. Capped so a
2116
+ // high-cardinality column can't render thousands of rows into the popover.
2117
+ const IN_SUGGEST_LIMIT = 200;
2118
+ const inSuggestValues = $derived.by(() => {
2119
+ if (!inSuggestFor)
2120
+ return [];
2121
+ const query = inSuggestQuery.trim().toLowerCase();
2122
+ const all = facetValuesForColumn(inSuggestFor);
2123
+ const matched = query
2124
+ ? all.filter((value) => value.toLowerCase().includes(query))
2125
+ : all;
2126
+ return matched.slice(0, IN_SUGGEST_LIMIT);
2127
+ });
2025
2128
  // Fire onApiReady exactly once when the grid is first ready. Wrapping in
2026
2129
  // an effect that tracks `props.onApiReady` was racy - every parent render
2027
2130
  // creates a new inline arrow, the effect re-fired, and any synchronous
@@ -2151,6 +2254,14 @@ export function createSvGridController(props) {
2151
2254
  set operatorMenuFor(v) { operatorMenuFor = v; },
2152
2255
  get operatorMenuPos() { return operatorMenuPos; },
2153
2256
  set operatorMenuPos(v) { operatorMenuPos = v; },
2257
+ get inSuggestFor() { return inSuggestFor; },
2258
+ set inSuggestFor(v) { inSuggestFor = v; },
2259
+ get inSuggestPos() { return inSuggestPos; },
2260
+ set inSuggestPos(v) { inSuggestPos = v; },
2261
+ get inSuggestQuery() { return inSuggestQuery; },
2262
+ set inSuggestQuery(v) { inSuggestQuery = v; },
2263
+ get inSuggestValues() { return inSuggestValues; },
2264
+ get facetValuesForColumn() { return facetValuesForColumn; },
2154
2265
  get chooseColumnsPos() { return chooseColumnsPos; },
2155
2266
  set chooseColumnsPos(v) { chooseColumnsPos = v; },
2156
2267
  get contextMenuFor() { return contextMenuFor; },
@@ -2183,6 +2294,7 @@ export function createSvGridController(props) {
2183
2294
  set internalColumns(v) { internalColumns = v; },
2184
2295
  get hiddenColumns() { return hiddenColumns; },
2185
2296
  set hiddenColumns(v) { hiddenColumns = v; },
2297
+ get declaredHiddenColumns() { return declaredHiddenColumns; },
2186
2298
  get toggleColumnGroup() { return toggleColumnGroup; },
2187
2299
  get isColumnGroupCollapsed() { return isColumnGroupCollapsed; },
2188
2300
  get externalSortEnabled() { return externalSortEnabled; },
@@ -2197,6 +2309,7 @@ export function createSvGridController(props) {
2197
2309
  get allColumns() { return allColumns; },
2198
2310
  get headerGroups() { return headerGroups; },
2199
2311
  get groupHeaderRows() { return groupHeaderRows; },
2312
+ get groupHeaderRowsWindowed() { return groupHeaderRowsWindowed; },
2200
2313
  get pinnedOffsets() { return pinnedOffsets; },
2201
2314
  get cellPinStyle() { return cellPinStyle; },
2202
2315
  get isColumnPinned() { return isColumnPinned; },
@@ -2545,6 +2658,11 @@ export function createSvGridController(props) {
2545
2658
  get openColumnMenu() { return openColumnMenu; },
2546
2659
  get openFilterMenu() { return openFilterMenu; },
2547
2660
  get openOperatorMenu() { return openOperatorMenu; },
2661
+ get openInSuggest() { return openInSuggest; },
2662
+ get closeInSuggest() { return closeInSuggest; },
2663
+ get addFilterToken() { return addFilterToken; },
2664
+ get removeFilterToken() { return removeFilterToken; },
2665
+ get toggleFilterToken() { return toggleFilterToken; },
2548
2666
  get sortColumnFromMenu() { return sortColumnFromMenu; },
2549
2667
  get clearColumnSort() { return clearColumnSort; },
2550
2668
  get groupByColumnFromMenu() { return groupByColumnFromMenu; },
@@ -2571,12 +2689,12 @@ export function createSvGridController(props) {
2571
2689
  const { showTooltipFor, hideTooltip, flushScheduledScrollSync, scheduleScrollSync, onBodyScroll } = createScrollSync(ctx);
2572
2690
  const { onGridKeyDown, onWindowKeydown, onHeaderSortClick } = createKeyboard(ctx);
2573
2691
  const { computeSummaries, hasRenderedColumn } = createSummaries(ctx);
2574
- 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(ctx);
2692
+ 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(ctx);
2575
2693
  const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellValidity, computeCellNote, getColumnEditorOptions, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender(ctx);
2576
2694
  const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, startEditing, stopEditing, startFullRowEdit, setFullRowDraft, commitFullRowEdit, cancelFullRowEdit, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, focusOnMount, onCellDoubleClick, pasteFromClipboard, onGridPaste } = createEditing(ctx);
2577
2695
  const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, fillMarqueeEdges, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection(ctx);
2578
2696
  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(ctx);
2579
- const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd } = createRowDrag(ctx);
2697
+ const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd, destroyRowDrag } = createRowDrag(ctx);
2580
2698
  const { register: registerAlignedGrid, broadcastScroll: broadcastAlignedScroll, broadcastWidths: broadcastAlignedWidths } = createAlignedGrids(ctx);
2581
2699
  const { buildApi } = createGridApi(ctx);
2582
2700
  const { readCellRaw, writeCellRaw, applyFillPattern, clearSelectedCellValues, startFillDrag, onFillPointerMove, onFillPointerUp, toggleBooleanCell, copySelectionToClipboard, clearSelectedCells, cutSelectionToClipboard } = createClipboard(ctx);
@@ -2593,5 +2711,16 @@ export function createSvGridController(props) {
2593
2711
  void columnWidths;
2594
2712
  broadcastAlignedWidths();
2595
2713
  });
2714
+ // Unmount teardown. Reads no reactive state, so the cleanup runs once on
2715
+ // destroy - tearing down an in-flight column resize (its document pointer
2716
+ // listeners, #58), a pending tooltip timer (#64), and any managed row drag
2717
+ // this grid still owns (#68). Each of these is a no-op when idle.
2718
+ $effect(() => {
2719
+ return () => {
2720
+ endColumnResize();
2721
+ hideTooltip();
2722
+ destroyRowDrag();
2723
+ };
2724
+ });
2596
2725
  return ctx;
2597
2726
  }
package/dist/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). Nudge the last column's right-aligned content in by
489
- that width when the scrollbar is visible, so numbers/values aren't hidden
490
- underneath it. Left/center content already clears the right edge. */
491
- .sv-grid-has-vscroll .sv-grid-table tr > :last-child.sv-grid-cell[data-align="right"],
492
- .sv-grid-has-vscroll .sv-grid-table tr > :last-child.sv-grid-column[data-align="right"] {
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
  =================================================================== */