@svgrid/grid 2.2.26 → 2.2.28

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 (36) hide show
  1. package/README.md +79 -25
  2. package/dist/GridMenus.svelte +10 -4
  3. package/dist/SvColorInput.svelte +1 -1
  4. package/dist/SvContextMenu.svelte +4 -4
  5. package/dist/SvDrawer.svelte +4 -0
  6. package/dist/SvGrid.css +4 -0
  7. package/dist/SvPhoneInput.svelte +3 -1
  8. package/dist/a11y/dismissable.d.ts +11 -0
  9. package/dist/a11y/dismissable.js +21 -0
  10. package/dist/ai.js +3 -3
  11. package/dist/cdn/GridMenus-BbzEvTYO.js +421 -0
  12. package/dist/cdn/GridMenus-E220X2kM.js +417 -0
  13. package/dist/cdn/{SvDateTimePicker-CYC7gRER.js → SvDateTimePicker-B8GopjhC.js} +449 -442
  14. package/dist/cdn/{SvDateTimePicker-DtQKd4Ns.js → SvDateTimePicker-DB0aIEk8.js} +112 -105
  15. package/dist/cdn/{src-bYfr_7aR.js → src-BxNEslEo.js} +8089 -8089
  16. package/dist/cdn/{src-DysW9qbk.js → src-C4yKQZ5t.js} +5741 -5741
  17. package/dist/cdn/svgrid.js +8 -8
  18. package/dist/cdn/svgrid.svelte-external.js +8 -8
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +1 -1
  21. package/package.json +21 -4
  22. package/src/GridMenus.svelte +10 -4
  23. package/src/SvColorInput.svelte +1 -1
  24. package/src/SvContextMenu.svelte +4 -4
  25. package/src/SvDrawer.svelte +4 -0
  26. package/src/SvDrawer.test.ts +18 -1
  27. package/src/SvGrid.css +4 -0
  28. package/src/SvNavPane.test.ts +15 -4
  29. package/src/SvPhoneInput.svelte +3 -1
  30. package/src/a11y/dismissable.test.ts +25 -1
  31. package/src/a11y/dismissable.ts +20 -0
  32. package/src/ai.ts +3 -3
  33. package/src/index.ts +1 -0
  34. package/src/svgrid.filter-menu-scroll.test.ts +102 -0
  35. package/dist/cdn/GridMenus-GfCAHgkZ.js +0 -420
  36. package/dist/cdn/GridMenus-GiVNSeDU.js +0 -416
package/README.md CHANGED
@@ -10,21 +10,29 @@
10
10
  <a href="https://www.npmjs.com/package/@svgrid/grid"><img src="https://img.shields.io/npm/v/%40svgrid%2Fgrid.svg?label=%40svgrid%2Fgrid" alt="npm version" /></a>
11
11
  <a href="https://www.npmjs.com/package/@svgrid/grid"><img src="https://img.shields.io/npm/dm/%40svgrid%2Fgrid.svg" alt="npm downloads" /></a>
12
12
  <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License" /></a>
13
- <a href="./dist"><img src="https://img.shields.io/badge/types-included-blue.svg" alt="TypeScript" /></a>
13
+ <a href="https://www.npmjs.com/package/@svgrid/grid"><img src="https://img.shields.io/badge/types-included-blue.svg" alt="TypeScript" /></a>
14
14
  <a href="https://svelte.dev"><img src="https://img.shields.io/badge/svelte-5-ff3e00.svg" alt="Svelte 5" /></a>
15
15
  </p>
16
16
 
17
17
  <p align="center">
18
18
  <a href="https://svgrid.com">Website</a> ·
19
- <a href="https://svgrid.com/docs">Docs</a> ·
20
- <a href="https://svgrid.com/demos">280+ Demos</a> ·
21
- <a href="https://svgrid.com/pricing">Pricing</a> ·
22
- <a href="https://svgrid.com/roadmap">Roadmap</a>
19
+ <a href="https://svgrid.com/docs/">Docs</a> ·
20
+ <a href="https://svgrid.com/demos/">370+ Demos</a> ·
21
+ <a href="https://svgrid.com/pricing/">Pricing</a> ·
22
+ <a href="https://svgrid.com/roadmap/">Roadmap</a>
23
23
  </p>
24
24
 
25
25
  ---
26
26
 
27
- A production-grade data grid built from the first line for **Svelte 5 runes** - not a React grid wrapped in a Svelte shim. Compose the headless engine yourself, or drop in the full `<SvGrid />` render component and ship. The MIT core has **zero feature gating**: no license key, no watermark, no row-count cap.
27
+ A production-grade **Svelte 5 data grid and data table** built from the first line for **runes**, not a
28
+ React grid wrapped in a Svelte shim. Virtual scrolling over 100k+ rows, Excel-style filtering, inline
29
+ editing, row grouping, tree data, and server-side data. Compose the headless engine yourself, or drop in
30
+ the full `<SvGrid />` render component and ship. The MIT core has **zero feature gating**: no license key,
31
+ no watermark, no row-count cap.
32
+
33
+ <p align="center">
34
+ <img src="https://svgrid.com/brand/svgrid-hero.png" alt="A SvGrid trading desk: KPI cards, sector filter chips, and a virtualized data table with sparkline trend columns, conditional colour, and pinned columns." width="100%" />
35
+ </p>
28
36
 
29
37
  ## Install
30
38
 
@@ -61,21 +69,34 @@ npm install @svgrid/grid
61
69
  <SvGrid data={rows} columns={columns} />
62
70
  ```
63
71
 
64
- That is a real, accessible grid. Sorting, filtering, virtualization, cell selection, and inline editing all wire up the moment you turn on the matching prop.
72
+ That is a real, accessible grid. Sorting, filtering, virtualization, cell selection, and inline editing
73
+ all wire up the moment you turn on the matching prop.
74
+
75
+ ## Quick facts
76
+
77
+ | | |
78
+ |---|---|
79
+ | **License** | MIT, free for commercial use |
80
+ | **Requires** | `svelte@^5` (peer dependency), Node 16+ |
81
+ | **Bundle (gzip)** | ~2 KB headless core, ~80 KB full `<SvGrid>` + ~9 KB CSS |
82
+ | **Types** | Bundled, no `@types/` package needed |
83
+ | **Module format** | ESM, plus a CDN build at `@svgrid/grid/cdn` |
84
+ | **SSR** | Works under SvelteKit SSR and static builds |
85
+ | **Theming** | `--sg-*` CSS custom properties, dark mode + high contrast |
65
86
 
66
87
  ## Capabilities
67
88
 
68
- - **Virtualization** - row + column windowing; 100k x 100 stays smooth, with a 1M-row demo.
89
+ - **Virtual scrolling** - row + column windowing; 100k x 100 stays smooth, with a 1M-row demo.
69
90
  - **Filtering** - Excel-style filter menu, inline filter row, set / value-list filter, locale-aware matching, between operators.
70
91
  - **Editing** - 14 built-in `editorType`s (text, number, date, datetime, time, select, rich-select with typeahead, textarea, color, checkbox, list, chips, rating, password) plus a `cellEditor` snippet slot for anything custom.
71
92
  - **Selection** - cell-range click+drag and Shift+arrows, copy/paste as TSV, Excel-style fill handle, row selection.
72
- - **Views** - native Kanban board mode (drag cards between lanes), row grouping with aggregation, tree data, master/detail, full-width detail rows.
73
- - **Layout** - row + column pinning, sticky header + first column, header drag-to-reorder, autosize, keyboard-accessible column sizing, zebra rows.
93
+ - **Views** - row grouping with aggregation, tree data, master/detail, full-width detail rows, spreadsheet mode with formulas.
94
+ - **Layout** - row + column pinning, sticky header + first column, header drag-to-reorder, autosize, keyboard-accessible column sizing, zebra rows, responsive mode.
74
95
  - **Data operations** - find in grid (Ctrl+F), undo / redo, transaction API, optimistic updates, server-side row model with sort / filter / group pushdown.
75
- - **UI components** - a Svelte 5 component suite ships in the package (inputs, selection, date/time, overlays, layout, feedback) - standalone or as grid cell editors.
96
+ - **AI helpers, free** - natural-language filter, smart fill, summarize, classify, anomaly detection, and "chart this". Model-agnostic: register one provider, nothing is bundled.
97
+ - **UI components** - a Svelte 5 component suite ships in the package (inputs, selection, date/time, overlays, layout, feedback), standalone or as grid cell editors.
76
98
  - **Accessibility** - WAI-ARIA grid roles, full keyboard navigation, RTL, high-contrast theme.
77
99
  - **Engineered for production** - first-class TypeScript types, CSP / Trusted-Types safe rendering, SSR-friendly, themeable via `--sg-*` CSS tokens.
78
- - **Lean** - ~2 KB gzipped headless core, ~77 KB gzipped full grid (+ ~9 KB CSS); charts, cell editors and export load on demand as their own chunks, and the Kanban board + scheduler are Enterprise views. Svelte stays a peer dependency.
79
100
 
80
101
  ## Headless or render-ready
81
102
 
@@ -109,37 +130,70 @@ import {
109
130
  } from '@svgrid/grid'
110
131
  ```
111
132
 
112
- - **Headless** - drive `createSvGrid` and the row-model factories, render your own markup. `getGrid*A11yProps` helpers keep custom markup accessible.
133
+ - **Headless** - drive `createSvGrid` and the row-model factories, render your own markup.
134
+ `getGrid*A11yProps` helpers keep custom markup accessible. About 2 KB gzipped.
113
135
  - **Render-ready** - `<SvGrid />` is a complete, accessible, themeable grid you configure with props.
114
136
 
115
- Compatibility aliases are provided for the framework-neutral names: `createGrid`, `createGridState`, `subscribeGrid`.
137
+ Compatibility aliases are provided for the framework-neutral names: `createGrid`, `createGridState`,
138
+ `subscribeGrid`.
116
139
 
117
140
  ## AI-native
118
141
 
119
- SvGrid publishes an [MCP server](https://www.npmjs.com/package/@svgrid/mcp) and `llms.txt` / `llms-full.txt`, so Claude, Cursor, and Zed answer with accurate, version-pinned APIs and all 280+ demo sources as context - not hallucinated methods.
142
+ SvGrid publishes an [MCP server](https://www.npmjs.com/package/@svgrid/mcp) plus
143
+ [llms.txt](https://svgrid.com/llms.txt) and [llms-full.txt](https://svgrid.com/llms-full.txt), so Claude,
144
+ Cursor, and Zed answer with accurate, version-pinned APIs and all 370+ demo sources as context rather
145
+ than hallucinated methods.
146
+
147
+ ```bash
148
+ claude mcp add svgrid -- npx -y @svgrid/mcp
149
+ npx skills add sv-grid/sv-grid # always-on house-style rules
150
+ ```
151
+
152
+ See [Use sv-grid docs as LLM context](https://svgrid.com/docs/help/llm-grounding/).
120
153
 
121
154
  ## Enterprise features
122
155
 
123
- The MIT core is free for any use, including commercial, with **no feature gating** - sorting, filtering, editing, virtualization, and the **server-side row model** (sort / filter / group pushdown) all ship in the free package. The optional [`@svgrid/enterprise`](https://www.npmjs.com/package/@svgrid/enterprise) pack layers on the export- and analytics-heavy features teams tend to need last:
156
+ The MIT core is free for any use, including commercial, with **no feature gating**. Sorting, filtering,
157
+ editing, virtualization, the **server-side row model**, and the **AI helpers** all ship in the free
158
+ package. The optional [`@svgrid/enterprise`](https://www.npmjs.com/package/@svgrid/enterprise) pack layers
159
+ on the export- and analytics-heavy features teams tend to need last:
124
160
 
125
161
  - **Excel / PDF / CSV / TSV / HTML export** plus paginated print.
162
+ - **Excel / CSV / TSV / JSON import** with auto-mapping and per-row validation.
126
163
  - **Pivot tables** with a drag-and-drop pivot Designer.
127
- - **Scheduler / calendar view** and Kanban roll-ups for larger deployments.
128
- - **AI helpers** and SvGrid Studio for data-app scaffolding.
164
+ - **Kanban board and scheduler / calendar renderers** for the `board` and `scheduler` props.
165
+
166
+ OSS projects receive an Enterprise key free. See [Pricing](https://svgrid.com/pricing/).
167
+
168
+ ## Comparison
169
+
170
+ | | SvGrid | AG Grid Community | TanStack Table |
171
+ |---|---|---|---|
172
+ | **Svelte 5 runes native** | Yes | No, JS core + wrapper | Adapter only |
173
+ | **Ships a renderer** | Yes, plus headless | Yes | No, headless only |
174
+ | **Bundle (gzip)** | ~2 KB headless / ~80 KB full | ~340 KB | ~12-14 KB |
175
+ | **Master/detail, tree, range selection** | Free | Enterprise only | Build it yourself |
129
176
 
130
- OSS projects receive an Enterprise key free. See the [Enterprise docs](https://svgrid.com/docs) and [Pricing](https://svgrid.com/pricing).
177
+ Full detail: [SvGrid vs AG Grid vs TanStack Table](https://svgrid.com/docs/help/comparison/) ·
178
+ [Migrating from AG Grid](https://svgrid.com/docs/help/migrating-from-ag-grid/)
131
179
 
132
180
  ## Documentation
133
181
 
134
- - [Getting started](https://svgrid.com/docs) - end-to-end walkthrough.
135
- - [280+ live demos](https://svgrid.com/demos) - copy-paste recipes for every feature.
136
- - [Why headless?](https://svgrid.com/docs) - when to compose vs. drop in.
137
- - [Theming](https://svgrid.com/docs) - re-skin via `--sg-*` tokens and dark mode.
182
+ - [Getting started](https://svgrid.com/docs/getting-started/) - end-to-end walkthrough.
183
+ - [370+ live demos](https://svgrid.com/demos/) - copy-paste recipes for every feature.
184
+ - [Why headless?](https://svgrid.com/docs/why-headless/) - when to compose vs. drop in.
185
+ - [Theming and Tailwind](https://svgrid.com/docs/help/tailwind/) - re-skin via `--sg-*` tokens and dark mode.
186
+ - [Accessibility](https://svgrid.com/docs/help/accessibility/) - the WAI-ARIA grid pattern as implemented.
187
+ - [Bundle size](https://svgrid.com/docs/reference/bundle-size/) - measured numbers and how to reproduce them.
138
188
 
139
189
  ## Who's behind it
140
190
 
141
- SvGrid is built by [jQWidgets](https://www.jqwidgets.com), shipping UI components since 2011 to 5,000+ companies including Samsung, Boeing, NVIDIA, Microsoft, Nokia, and Intel. SvGrid is our Svelte 5 native effort.
191
+ SvGrid is built by [jQWidgets](https://www.jqwidgets.com), shipping UI components since 2011 to 5,000+
192
+ companies including Samsung, Boeing, NVIDIA, Microsoft, Nokia, and Intel. SvGrid is our Svelte 5 native
193
+ effort.
142
194
 
143
195
  ## License & trademark
144
196
 
145
- The source code is **MIT-licensed** - see [LICENSE](./LICENSE). It is free for commercial use. SvGrid&trade; and sv-grid&trade; are trademarks of jQWidgets Ltd; the license covers the code only, not the name or logo.
197
+ The source code is **MIT-licensed** - see [LICENSE](./LICENSE). It is free for commercial use.
198
+ SvGrid&trade; and sv-grid&trade; are trademarks of jQWidgets Ltd; the license covers the code only, not
199
+ the name or logo.
@@ -39,6 +39,7 @@
39
39
  type TableFeatures,
40
40
  } from "./index";
41
41
  import "./sv-grid-scrollbar";
42
+ import { onScrollOutside } from "./a11y/dismissable";
42
43
  import type { Snippet } from "svelte";
43
44
  import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
44
45
  import {
@@ -145,15 +146,20 @@
145
146
 
146
147
  // Column/filter/operator menus are position:fixed and anchored to their
147
148
  // trigger by a one-time measurement taken when they open. On scroll they'd
148
- // otherwise detach and float over the wrong place (#88), so close them on any
149
- // scroll (capture phase, to catch inner scroll containers) or resize.
149
+ // otherwise detach and float over the wrong place (#88), so close them when
150
+ // anything OUTSIDE them scrolls, or on resize. Scrolling the menu's own body
151
+ // or its facet list has to keep it open - hence onScrollOutside rather than a
152
+ // bare capture-phase listener.
150
153
  $effect(() => {
151
154
  if (!(columnMenuFor || filterMenuFor || operatorMenuFor || chooseColumnsPos)) return;
152
155
  const close = () => closeMenus();
153
- window.addEventListener('scroll', close, true);
156
+ const offScroll = onScrollOutside(
157
+ () => Array.from(document.querySelectorAll<HTMLElement>('.sv-grid-menu')),
158
+ close,
159
+ );
154
160
  window.addEventListener('resize', close);
155
161
  return () => {
156
- window.removeEventListener('scroll', close, true);
162
+ offScroll();
157
163
  window.removeEventListener('resize', close);
158
164
  };
159
165
  });
@@ -107,7 +107,7 @@
107
107
  <SvField frame id={uid} {label} {hint} {error} {required} {dir} {size} {invalid} {disabled} {readonly} {loading} {block} width={block ? undefined : 'fit-content'}>
108
108
  <button
109
109
  bind:this={triggerEl}
110
- class="sv-color__trigger"
110
+ class="sv-color sv-color__trigger"
111
111
  class:is-disabled={disabled}
112
112
  {...col.swatchProps()}
113
113
  use:focusOpen
@@ -17,7 +17,7 @@
17
17
  */
18
18
  import type { Snippet } from 'svelte'
19
19
  import { portalToBody, popIn } from './popover'
20
- import { createDismissableLayer } from './a11y/dismissable'
20
+ import { createDismissableLayer, onScrollOutside } from './a11y/dismissable'
21
21
  import { createFocusTrap } from './a11y/focus-trap'
22
22
  import SvMenuList, { type MenuItem } from './SvMenuList.svelte'
23
23
 
@@ -76,9 +76,9 @@
76
76
  })
77
77
  layer.activate()
78
78
  // Reposition-close on scroll (a moved anchor would leave the menu stranded).
79
- const onScroll = () => (open = false)
80
- window.addEventListener('scroll', onScroll, true)
81
- return () => { layer.release(); trap.release(); window.removeEventListener('scroll', onScroll, true) }
79
+ // Scrolling the menu's own item list doesn't count as the anchor moving.
80
+ const offScroll = onScrollOutside(() => panelEl, () => (open = false))
81
+ return () => { layer.release(); trap.release(); offScroll() }
82
82
  })
83
83
  </script>
84
84
 
@@ -99,6 +99,10 @@
99
99
  leaving = true
100
100
  leaveTimer = setTimeout(() => { show = false; leaving = false; onClosed?.() }, reduceMotion ? 0 : EXIT_MS)
101
101
  }
102
+ // `open` is this effect's only dependency, so the cleanup runs on reopen (a
103
+ // no-op after the clearTimeout above) or on destroy - where it matters, so a
104
+ // drawer unmounted mid-exit doesn't leave a timer firing into a dead scope.
105
+ return () => clearTimeout(leaveTimer)
102
106
  })
103
107
 
104
108
  // Swipe-down-to-close for the sheet grab handle.
package/dist/SvGrid.css CHANGED
@@ -1780,6 +1780,9 @@ select.sv-grid-fr-editor {
1780
1780
  z-index: 901;
1781
1781
  max-height: calc(100vh - 24px);
1782
1782
  overflow: auto;
1783
+ /* Keep a wheel that reaches the end of the menu from scrolling the grid
1784
+ underneath it - that scroll would close the menu out from under the user. */
1785
+ overscroll-behavior: contain;
1783
1786
  padding: 4px;
1784
1787
  background: var(--sg-bg, #fff);
1785
1788
  border: 1px solid var(--sg-border, #d6dee9);
@@ -2062,6 +2065,7 @@ select.sv-grid-fr-editor {
2062
2065
  .sv-grid-facet-list {
2063
2066
  max-height: 200px;
2064
2067
  overflow: auto;
2068
+ overscroll-behavior: contain;
2065
2069
  border: 1px solid var(--sg-border, #eaeef4);
2066
2070
  border-radius: 5px;
2067
2071
  }
@@ -105,7 +105,9 @@
105
105
  {width}
106
106
  leading={countryChip}
107
107
  >
108
- <input class="sv-phone__number" {...ph.inputProps()} />
108
+ <!-- `sv-phone` on the control is the component's public CSS/query hook, the
109
+ same convention SvCountryInput and the rest of the suite follow. -->
110
+ <input class="sv-phone sv-phone__number" {...ph.inputProps()} />
109
111
  {#if name}<input type="hidden" {name} value={ph.value} />{/if}
110
112
  </SvField>
111
113
 
@@ -29,4 +29,15 @@ export type DismissableLayer = {
29
29
  export declare function createDismissableLayer(options: DismissableOptions): DismissableLayer;
30
30
  /** Test/introspection helper: current number of active layers. */
31
31
  export declare function dismissableDepth(): number;
32
+ /**
33
+ * Close-on-scroll for popovers that are position:fixed and anchored by a
34
+ * one-time measurement taken when they open. Scrolling an ancestor slides the
35
+ * anchor out from under them, so they have to close - but scrolling INSIDE
36
+ * them (a long menu, a facet list) must not, which is what a bare capture-phase
37
+ * window listener gets wrong: inner scroll containers don't bubble, so the
38
+ * capture listener sees them and dismisses the panel the user is scrolling.
39
+ *
40
+ * Returns the unsubscribe function.
41
+ */
42
+ export declare function onScrollOutside(element: () => DismissTarget, onOutside: () => void): () => void;
32
43
  export {};
@@ -52,3 +52,24 @@ export function createDismissableLayer(options) {
52
52
  export function dismissableDepth() {
53
53
  return stack.length;
54
54
  }
55
+ /**
56
+ * Close-on-scroll for popovers that are position:fixed and anchored by a
57
+ * one-time measurement taken when they open. Scrolling an ancestor slides the
58
+ * anchor out from under them, so they have to close - but scrolling INSIDE
59
+ * them (a long menu, a facet list) must not, which is what a bare capture-phase
60
+ * window listener gets wrong: inner scroll containers don't bubble, so the
61
+ * capture listener sees them and dismisses the panel the user is scrolling.
62
+ *
63
+ * Returns the unsubscribe function.
64
+ */
65
+ export function onScrollOutside(element, onOutside) {
66
+ if (typeof window === 'undefined')
67
+ return () => { };
68
+ const handler = (event) => {
69
+ if (containsTarget(element(), event.target))
70
+ return;
71
+ onOutside();
72
+ };
73
+ window.addEventListener('scroll', handler, true);
74
+ return () => window.removeEventListener('scroll', handler, true);
75
+ }
package/dist/ai.js CHANGED
@@ -42,14 +42,14 @@ export function hasAIProvider() {
42
42
  }
43
43
  class NoProviderError extends Error {
44
44
  constructor() {
45
- super('@svgrid/enterprise/ai: no AI provider registered. Call setAIProvider(fn) ' +
45
+ super('@svgrid/grid ai: no AI provider registered. Call setAIProvider(fn) ' +
46
46
  'with an adapter that talks to OpenAI / Anthropic / your proxy.');
47
47
  this.name = 'NoProviderError';
48
48
  }
49
49
  }
50
50
  class BadJsonError extends Error {
51
51
  constructor(raw) {
52
- super('@svgrid/enterprise/ai: provider returned non-JSON for a json-format request. ' +
52
+ super('@svgrid/grid ai: provider returned non-JSON for a json-format request. ' +
53
53
  `First 200 chars: ${raw.slice(0, 200)}`);
54
54
  this.name = 'BadJsonError';
55
55
  }
@@ -183,7 +183,7 @@ export async function aiFilter(api, query, opts = {}) {
183
183
  */
184
184
  export async function aiSmartFill(api, opts) {
185
185
  if (opts.examples.length === 0) {
186
- throw new Error('@svgrid/enterprise/ai: aiSmartFill requires at least one example.');
186
+ throw new Error('@svgrid/grid ai: aiSmartFill requires at least one example.');
187
187
  }
188
188
  const data = api.getData();
189
189
  const targets = opts.targetRowIndices ??