@svgrid/grid 3.0.2 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/README.md +2 -1
  2. package/dist/GridMenus.svelte +44 -2
  3. package/dist/SvChartMenu.svelte +69 -0
  4. package/dist/SvChartMenu.svelte.d.ts +11 -0
  5. package/dist/SvChartPanes.svelte +113 -0
  6. package/dist/SvChartPanes.svelte.d.ts +20 -0
  7. package/dist/SvGrid.controller.svelte.d.ts +110 -39
  8. package/dist/SvGrid.controller.svelte.js +655 -200
  9. package/dist/SvGrid.css +105 -2
  10. package/dist/SvGrid.svelte +386 -514
  11. package/dist/SvGrid.types.d.ts +210 -9
  12. package/dist/SvGridCellEditor.svelte +17 -0
  13. package/dist/SvGridChart.svelte +2257 -329
  14. package/dist/SvGridChart.svelte.d.ts +6 -106
  15. package/dist/SvGridChart.types.d.ts +338 -0
  16. package/dist/SvGridChart.types.js +1 -0
  17. package/dist/SvGridChartBuilder.svelte +323 -0
  18. package/dist/SvGridChartBuilder.svelte.d.ts +34 -0
  19. package/dist/SvGridChartPanel.svelte +221 -275
  20. package/dist/SvGridChartPickers.svelte +417 -0
  21. package/dist/SvGridChartPickers.svelte.d.ts +70 -0
  22. package/dist/SvGridChartView.svelte +13 -14
  23. package/dist/SvModal.svelte +7 -1
  24. package/dist/SvModal.svelte.d.ts +5 -0
  25. package/dist/ai.d.ts +40 -8
  26. package/dist/ai.js +135 -24
  27. package/dist/aria.d.ts +11 -0
  28. package/dist/build-api.js +108 -22
  29. package/dist/cdn/GridMenus-7kbpnnBW.js +635 -0
  30. package/dist/cdn/GridMenus-BL7ZgQvU.js +636 -0
  31. package/dist/cdn/SvChartMenu-Bl6PBbkT.js +58 -0
  32. package/dist/cdn/SvChartMenu-FBSMINA6.js +59 -0
  33. package/dist/cdn/{SvDateRangeInput-DMLKmGEc.js → SvDateRangeInput-BeU_TkbQ.js} +15 -14
  34. package/dist/cdn/{SvDateRangeInput-CaOuMs8O.js → SvDateRangeInput-yX8vzleW.js} +38 -37
  35. package/dist/cdn/{SvDateTimePicker-sonaH0oh.js → SvDateTimePicker-DQwt4UAs.js} +139 -180
  36. package/dist/cdn/SvDateTimePicker-vNU6bZ-q.js +775 -0
  37. package/dist/cdn/{SvGridCellEditor-CjEJMJKc.js → SvGridCellEditor-B1p-vCK5.js} +188 -178
  38. package/dist/cdn/{SvGridCellEditor-BWnTi2N7.js → SvGridCellEditor-D_0q4xAu.js} +98 -88
  39. package/dist/cdn/SvGridChart-C3EWAZaM.js +3574 -0
  40. package/dist/cdn/SvGridChart-DzLkSwCH.js +3572 -0
  41. package/dist/cdn/SvGridChartBuilder-BE2T1ykB.js +783 -0
  42. package/dist/cdn/SvGridChartBuilder-CfII62sZ.js +784 -0
  43. package/dist/cdn/SvGridChartPanel-CCX5_Wgd.js +675 -0
  44. package/dist/cdn/SvGridChartPanel-xf4sWVTo.js +699 -0
  45. package/dist/cdn/SvGridChartView-CfuXmY5I.js +48 -0
  46. package/dist/cdn/SvGridChartView-DYwabQWj.js +47 -0
  47. package/dist/cdn/SvMenuList-CLN8OuIK.js +452 -0
  48. package/dist/cdn/SvMenuList-DkGhNKjf.js +451 -0
  49. package/dist/cdn/SvModal-BAE-pjZX.js +396 -0
  50. package/dist/cdn/SvModal-CIZWCcad.js +397 -0
  51. package/dist/cdn/chart-Dz7SqMXH.js +4341 -0
  52. package/dist/cdn/chart-export-BXqueeWJ.js +135 -0
  53. package/dist/cdn/chart-export-pdf-DONFUnNY.js +110 -0
  54. package/dist/cdn/chart-panel-messages-CUbf2R4i.js +1052 -0
  55. package/dist/cdn/chart-panel-messages-CmNrMdsr.js +1053 -0
  56. package/dist/cdn/chart-summary-BJW_tg_X.js +138 -0
  57. package/dist/cdn/chart-trend-CaN9mDEV.js +50 -0
  58. package/dist/cdn/chart-validate-HcOVFAfJ.js +188 -0
  59. package/dist/cdn/{column-resize-DsfNXMom.js → column-resize-DpmOLfRp.js} +3 -3
  60. package/dist/cdn/{date-format-BnnHlqGw.js → date-format-CtquV-p3.js} +583 -603
  61. package/dist/cdn/{date-format-BNii4zeD.js → date-format-D6KFzU_W.js} +347 -367
  62. package/dist/cdn/dismissable-DAHetSNk.js +44 -0
  63. package/dist/cdn/editor-contract-LXgQJAgd.js +22 -0
  64. package/dist/cdn/focus-trap-BBOQYBma.js +65 -0
  65. package/dist/cdn/{row-resize-BRcimkUT.js → row-resize-niQCp040.js} +44 -36
  66. package/dist/cdn/{src-DNbz94Ct.js → src-BKhZ6eXd.js} +10227 -9787
  67. package/dist/cdn/{src-BJ2opd0j.js → src-V1uu8iE9.js} +6568 -6128
  68. package/dist/cdn/svgrid.js +26 -15
  69. package/dist/cdn/svgrid.svelte-external.js +26 -15
  70. package/dist/chart-axes.d.ts +53 -0
  71. package/dist/chart-axes.js +351 -0
  72. package/dist/chart-cartesian.d.ts +88 -0
  73. package/dist/chart-cartesian.js +1862 -0
  74. package/dist/chart-decimate.d.ts +51 -0
  75. package/dist/chart-decimate.js +199 -0
  76. package/dist/chart-export-pdf.d.ts +47 -0
  77. package/dist/chart-export-pdf.js +187 -0
  78. package/dist/chart-export.d.ts +2 -1
  79. package/dist/chart-export.js +18 -3
  80. package/dist/chart-financial.d.ts +91 -0
  81. package/dist/chart-financial.js +175 -0
  82. package/dist/chart-flow.d.ts +10 -0
  83. package/dist/chart-flow.js +298 -0
  84. package/dist/chart-format.d.ts +18 -0
  85. package/dist/chart-format.js +65 -0
  86. package/dist/chart-grid.d.ts +8 -0
  87. package/dist/chart-grid.js +190 -0
  88. package/dist/chart-hierarchy.d.ts +17 -0
  89. package/dist/chart-hierarchy.js +146 -0
  90. package/dist/chart-indicators.d.ts +115 -0
  91. package/dist/chart-indicators.js +389 -0
  92. package/dist/chart-messages.d.ts +95 -0
  93. package/dist/chart-messages.js +92 -0
  94. package/dist/chart-motion.d.ts +16 -0
  95. package/dist/chart-motion.js +141 -0
  96. package/dist/chart-panel-messages.d.ts +265 -0
  97. package/dist/chart-panel-messages.js +285 -0
  98. package/dist/chart-pivot.d.ts +87 -0
  99. package/dist/chart-pivot.js +156 -0
  100. package/dist/chart-polar.d.ts +25 -0
  101. package/dist/chart-polar.js +700 -0
  102. package/dist/chart-samples.d.ts +16 -0
  103. package/dist/chart-samples.js +153 -0
  104. package/dist/chart-scale.d.ts +113 -0
  105. package/dist/chart-scale.js +405 -0
  106. package/dist/chart-stats.d.ts +111 -0
  107. package/dist/chart-stats.js +419 -0
  108. package/dist/chart-stream.d.ts +45 -0
  109. package/dist/chart-stream.js +91 -0
  110. package/dist/chart-summary.d.ts +27 -0
  111. package/dist/chart-summary.js +205 -0
  112. package/dist/chart-sync.svelte.d.ts +32 -0
  113. package/dist/chart-sync.svelte.js +23 -0
  114. package/dist/chart-table.d.ts +35 -0
  115. package/dist/chart-table.js +165 -0
  116. package/dist/chart-trend.d.ts +19 -0
  117. package/dist/chart-trend.js +61 -0
  118. package/dist/chart-types.d.ts +1456 -0
  119. package/dist/chart-types.js +6 -0
  120. package/dist/chart-validate.d.ts +43 -0
  121. package/dist/chart-validate.js +272 -0
  122. package/dist/chart-zoom.d.ts +40 -0
  123. package/dist/chart-zoom.js +145 -0
  124. package/dist/chart.d.ts +186 -865
  125. package/dist/chart.js +460 -2204
  126. package/dist/clipboard.d.ts +1 -0
  127. package/dist/clipboard.js +137 -18
  128. package/dist/column-resize.d.ts +3 -0
  129. package/dist/column-resize.js +4 -1
  130. package/dist/columns.js +3 -0
  131. package/dist/command-context.d.ts +19 -0
  132. package/dist/command-context.js +116 -0
  133. package/dist/core.d.ts +7 -0
  134. package/dist/createPopoverSelect.svelte.d.ts +2 -2
  135. package/dist/editing.d.ts +2 -8
  136. package/dist/editing.js +262 -33
  137. package/dist/fill-patterns.d.ts +0 -5
  138. package/dist/fill-patterns.js +56 -2
  139. package/dist/grid-messages.d.ts +10 -3
  140. package/dist/grid-messages.js +5 -0
  141. package/dist/history.d.ts +83 -0
  142. package/dist/history.js +133 -0
  143. package/dist/index.d.ts +24 -6
  144. package/dist/index.js +20 -2
  145. package/dist/keyboard-handlers.js +88 -31
  146. package/dist/keyboard.d.ts +55 -0
  147. package/dist/keyboard.js +132 -0
  148. package/dist/menus.d.ts +3 -1
  149. package/dist/menus.js +7 -2
  150. package/dist/merges.d.ts +97 -0
  151. package/dist/merges.js +147 -0
  152. package/dist/row-resize.d.ts +3 -0
  153. package/dist/row-resize.js +14 -0
  154. package/dist/scroll-sync.d.ts +1 -0
  155. package/dist/scroll-sync.js +24 -0
  156. package/dist/selection.d.ts +16 -0
  157. package/dist/selection.js +108 -16
  158. package/dist/shortcut-registry.d.ts +145 -0
  159. package/dist/shortcut-registry.js +44 -0
  160. package/dist/svgrid-wrapper.types.d.ts +213 -22
  161. package/dist/virtualization/virtualizer.js +38 -17
  162. package/package.json +6 -1
  163. package/src/GridMenus.svelte +44 -2
  164. package/src/SvChartMenu.svelte +69 -0
  165. package/src/SvChartPanes.svelte +113 -0
  166. package/src/SvChartPanes.test.ts +66 -0
  167. package/src/SvGrid.controller.svelte.ts +629 -172
  168. package/src/SvGrid.css +105 -2
  169. package/src/SvGrid.svelte +386 -514
  170. package/src/SvGrid.types.ts +188 -8
  171. package/src/SvGridCellEditor.svelte +17 -0
  172. package/src/SvGridChart.svelte +2257 -329
  173. package/src/SvGridChart.test.ts +1419 -1
  174. package/src/SvGridChart.types.ts +336 -0
  175. package/src/SvGridChartBuilder.svelte +323 -0
  176. package/src/SvGridChartPanel.svelte +221 -275
  177. package/src/SvGridChartPickers.svelte +417 -0
  178. package/src/SvGridChartView.svelte +13 -14
  179. package/src/SvModal.svelte +7 -1
  180. package/src/SvModal.test.ts +9 -0
  181. package/src/ai.test.ts +57 -2
  182. package/src/ai.ts +166 -22
  183. package/src/aria.d.ts +11 -0
  184. package/src/build-api.coverage.test.ts +184 -0
  185. package/src/build-api.ts +107 -18
  186. package/src/chart-axes.test.ts +573 -0
  187. package/src/chart-axes.ts +342 -0
  188. package/src/chart-cartesian.ts +1863 -0
  189. package/src/chart-decimate.test.ts +249 -0
  190. package/src/chart-decimate.ts +186 -0
  191. package/src/chart-export-pdf.test.ts +91 -0
  192. package/src/chart-export-pdf.ts +207 -0
  193. package/src/chart-export.test.ts +14 -0
  194. package/src/chart-export.ts +13 -3
  195. package/src/chart-financial.ts +208 -0
  196. package/src/chart-flow.ts +286 -0
  197. package/src/chart-format.ts +67 -0
  198. package/src/chart-grid.ts +179 -0
  199. package/src/chart-hierarchy.ts +135 -0
  200. package/src/chart-indicators.test.ts +443 -0
  201. package/src/chart-indicators.ts +384 -0
  202. package/src/chart-messages.test.ts +21 -0
  203. package/src/chart-messages.ts +184 -0
  204. package/src/chart-motion.test.ts +71 -0
  205. package/src/chart-motion.ts +148 -0
  206. package/src/chart-panel-messages.test.ts +29 -0
  207. package/src/chart-panel-messages.ts +550 -0
  208. package/src/chart-pivot.test.ts +54 -0
  209. package/src/chart-pivot.ts +225 -0
  210. package/src/chart-polar.ts +674 -0
  211. package/src/chart-samples.ts +106 -0
  212. package/src/chart-scale.ts +394 -0
  213. package/src/chart-series-types.test.ts +714 -0
  214. package/src/chart-stats.ts +409 -0
  215. package/src/chart-stream.test.ts +75 -0
  216. package/src/chart-stream.ts +114 -0
  217. package/src/chart-summary.test.ts +90 -0
  218. package/src/chart-summary.ts +197 -0
  219. package/src/chart-sync.svelte.ts +41 -0
  220. package/src/chart-table.test.ts +76 -0
  221. package/src/chart-table.ts +178 -0
  222. package/src/chart-trend.ts +47 -0
  223. package/src/chart-types.ts +1309 -0
  224. package/src/chart-validate.test.ts +100 -0
  225. package/src/chart-validate.ts +247 -0
  226. package/src/chart-zoom.test.ts +137 -0
  227. package/src/chart-zoom.ts +124 -0
  228. package/src/chart.coverage.test.ts +81 -0
  229. package/src/chart.test.ts +56 -3
  230. package/src/chart.ts +557 -2784
  231. package/src/clipboard.test.ts +151 -0
  232. package/src/clipboard.ts +145 -16
  233. package/src/column-resize.ts +7 -1
  234. package/src/columns.ts +2 -0
  235. package/src/command-context.test.ts +200 -0
  236. package/src/command-context.ts +137 -0
  237. package/src/core.ts +7 -0
  238. package/src/editing.test.ts +281 -3
  239. package/src/editing.ts +256 -38
  240. package/src/fill-patterns.test.ts +35 -0
  241. package/src/fill-patterns.ts +56 -2
  242. package/src/grid-messages.ts +16 -3
  243. package/src/history.test.ts +196 -0
  244. package/src/history.ts +162 -0
  245. package/src/icon-seam.test.ts +3 -2
  246. package/src/index.ts +150 -2
  247. package/src/keyboard-handlers.coverage.test.ts +126 -6
  248. package/src/keyboard-handlers.ts +91 -30
  249. package/src/keyboard-shortcuts.seam.test.ts +200 -0
  250. package/src/keyboard.test.ts +124 -1
  251. package/src/keyboard.ts +142 -0
  252. package/src/menus.test.ts +26 -0
  253. package/src/menus.ts +10 -3
  254. package/src/merges.test.ts +112 -0
  255. package/src/merges.ts +168 -0
  256. package/src/pivot.grid.test.ts +40 -5
  257. package/src/row-resize.test.ts +44 -0
  258. package/src/row-resize.ts +17 -0
  259. package/src/scroll-sync.test.ts +42 -1
  260. package/src/scroll-sync.ts +25 -0
  261. package/src/selection.multi-range.test.ts +34 -0
  262. package/src/selection.test.ts +30 -2
  263. package/src/selection.ts +111 -19
  264. package/src/shortcut-registry.test.ts +97 -0
  265. package/src/shortcut-registry.ts +181 -0
  266. package/src/svgrid-wrapper.types.ts +184 -13
  267. package/src/svgrid.behavior.test.ts +135 -0
  268. package/src/svgrid.charting.test.ts +513 -3
  269. package/src/svgrid.context-menu.test.ts +31 -0
  270. package/src/svgrid.new-features.wrapper.test.ts +10 -3
  271. package/src/virtualization/virtualizer.ts +39 -17
  272. package/dist/cdn/GridMenus-DyFA-QxA.js +0 -602
  273. package/dist/cdn/GridMenus-M2XP9t0_.js +0 -603
  274. package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +0 -816
  275. package/dist/cdn/SvGridChart-DAb9RbRs.js +0 -1480
  276. package/dist/cdn/SvGridChart-Yl-IXZFO.js +0 -1481
  277. package/dist/cdn/SvGridChartPanel-DayQpDsR.js +0 -834
  278. package/dist/cdn/SvGridChartPanel-aqf8n-vJ.js +0 -810
  279. package/dist/cdn/SvGridChartView-CxfakxR0.js +0 -56
  280. package/dist/cdn/SvGridChartView-Dm_YEx9L.js +0 -55
  281. package/dist/cdn/chart-CKJRCPOk.js +0 -1652
package/README.md CHANGED
@@ -193,10 +193,11 @@ inside `@svgrid/grid`; the tools after it install separately and are entirely op
193
193
  |---|---|---|---|
194
194
  | **Svelte 5 runes native** | Yes | No, JS core + wrapper | Adapter only |
195
195
  | **Ships a renderer** | Yes, plus headless | Yes | No, headless only |
196
- | **Bundle (gzip)** | ~2 KB headless / ~80 KB full | ~340 KB | ~12-14 KB |
196
+ | **Bundle (gzip)** | measured on [the comparison page](https://svgrid.com/compare/ag-grid/) | measured, same page | measured, [same method](https://svgrid.com/compare/tanstack-table/) |
197
197
  | **Master/detail, tree, range selection** | Free | Enterprise only | Build it yourself |
198
198
 
199
199
  Full detail: [SvGrid vs AG Grid vs TanStack Table](https://svgrid.com/docs/help/comparison/) ·
200
+ [All comparisons, with dated sources](https://svgrid.com/compare/) ·
200
201
  [Migrating from AG Grid](https://svgrid.com/docs/help/migrating-from-ag-grid/)
201
202
 
202
203
  ## Documentation
@@ -14,6 +14,7 @@
14
14
  import SvListBox from "./SvListBox.svelte";
15
15
  import type {
16
16
  FilterOperator,
17
+ MenuPosition,
17
18
  } from "./SvGrid.types";
18
19
  import {
19
20
  getEditorInputType,
@@ -175,6 +176,29 @@
175
176
  import("./SvDateRangeInput.svelte").then((m) => (MenuRangePicker = m.default));
176
177
  });
177
178
 
179
+ /**
180
+ * The context menu is placed on a guessed height (280px, flipped above the
181
+ * click when that would not fit) before it exists. A consumer's menu can be
182
+ * far taller - the spreadsheet shell's has thirteen entries - and then the
183
+ * guess leaves its tail below the viewport. Once the menu is in the DOM its
184
+ * real box is measured and the position pulled back inside: up when it runs
185
+ * off the bottom, left when it runs off the right. The style reads
186
+ * `contextMenuPos`, so writing it moves the menu; the action's `update`
187
+ * runs on every position change and settles in one pass.
188
+ */
189
+ function keepInViewport(node: HTMLElement, _pos: MenuPosition) {
190
+ const fit = () => {
191
+ const pos = ctrl.contextMenuPos;
192
+ const box = node.getBoundingClientRect();
193
+ if (!box.width && !box.height) return;
194
+ const x = Math.max(8, Math.min(pos.x, window.innerWidth - box.width - 8));
195
+ const y = Math.max(8, Math.min(pos.y, window.innerHeight - box.height - 8));
196
+ if (x !== pos.x || y !== pos.y) ctrl.contextMenuPos = { x, y };
197
+ };
198
+ fit();
199
+ return { update: fit };
200
+ }
201
+
178
202
  /**
179
203
  * Opening the filter popover used to leave focus on the funnel button
180
204
  * outside it, so a keyboard user landed nowhere and had to tab in past the
@@ -832,6 +856,8 @@
832
856
  {/if}
833
857
 
834
858
  {#if contextMenuFor}
859
+ {@const items = contextMenuItems()}
860
+ {@const gutter = items.some((it) => it.icon)}
835
861
  <div
836
862
  class="sv-grid-menu-backdrop"
837
863
  role="presentation"
@@ -843,8 +869,9 @@
843
869
  data-svgrid-menu={ctrl.gridDomId}
844
870
  role="menu"
845
871
  style={`left: ${contextMenuPos.x}px; top: ${contextMenuPos.y}px;`}
872
+ use:keepInViewport={contextMenuPos}
846
873
  >
847
- {#each contextMenuItems() as item (item.key)}
874
+ {#each items as item (item.key)}
848
875
  {#if item.separator}
849
876
  <div class="sv-grid-menu-sep"></div>
850
877
  {:else}
@@ -854,7 +881,22 @@
854
881
  role="menuitem"
855
882
  disabled={item.disabled}
856
883
  onclick={() => { item.run?.(); closeContextMenu(); }}
857
- >{item.label}</button>
884
+ >
885
+ <!-- Every item keeps the gutter once one has an icon, so the labels
886
+ line up. The label is its own span with no whitespace text
887
+ around it, so the button reads exactly its label. -->
888
+ {#if gutter}<span class="sv-grid-menu-icon" aria-hidden="true">
889
+ {#if typeof item.icon === "string"}
890
+ {@render icon(item.icon)}
891
+ {:else if item.icon}
892
+ <svg class="sv-grid-menu-icon-own" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" focusable="false">
893
+ {#each item.icon.paths as path, i (i)}
894
+ <path d={path.d} fill={path.fill ? "currentColor" : "none"} stroke-width={path.width} stroke-dasharray={path.dash} />
895
+ {/each}
896
+ </svg>
897
+ {/if}
898
+ </span>{/if}<span class="sv-grid-menu-label">{item.label}</span>
899
+ </button>
858
900
  {/if}
859
901
  {/each}
860
902
  </div>
@@ -0,0 +1,69 @@
1
+ <script lang="ts">
2
+ /**
3
+ * The chart's right-click menu: the same portalled, focus-trapped,
4
+ * dismissable panel `SvContextMenu` opens, without the zone `<div>` that
5
+ * component wraps its children in. The chart owns its own `contextmenu`
6
+ * handler (the SVG is the zone) and hands this the items and a position.
7
+ *
8
+ * Lazy-loaded by `SvGridChart` on the first right-click, so a chart that is
9
+ * never right-clicked never pays for the menu machinery.
10
+ */
11
+ import { portalToBody, popIn } from './popover'
12
+ import { createDismissableLayer, onScrollOutside } from './a11y/dismissable'
13
+ import { createFocusTrap } from './a11y/focus-trap'
14
+ import SvMenuList, { type MenuItem } from './SvMenuList.svelte'
15
+
16
+ type Props = {
17
+ items: ReadonlyArray<MenuItem>
18
+ x: number
19
+ y: number
20
+ onClose: () => void
21
+ ariaLabel?: string
22
+ }
23
+ let { items, x, y, onClose, ariaLabel = 'Chart menu' }: Props = $props()
24
+
25
+ let panelEl = $state<HTMLDivElement | null>(null)
26
+ const MENU_W = 220
27
+ const pos = $derived.by(() => {
28
+ if (typeof window === 'undefined') return { x, y }
29
+ const estH = Math.min(items.length, 12) * 34 + 8
30
+ return {
31
+ x: Math.max(6, Math.min(x, window.innerWidth - MENU_W - 6)),
32
+ y: Math.max(6, Math.min(y, window.innerHeight - estH - 6)),
33
+ }
34
+ })
35
+
36
+ $effect(() => {
37
+ if (!panelEl) return
38
+ const trap = createFocusTrap(panelEl, {
39
+ initialFocus: () => panelEl?.querySelector<HTMLElement>('[role="menuitem"]:not([disabled])') ?? null,
40
+ })
41
+ trap.activate()
42
+ const layer = createDismissableLayer({ element: () => panelEl, onDismiss: onClose })
43
+ layer.activate()
44
+ const offScroll = onScrollOutside(() => panelEl, onClose)
45
+ return () => { layer.release(); trap.release(); offScroll() }
46
+ })
47
+ </script>
48
+
49
+ <div
50
+ bind:this={panelEl}
51
+ class="sv-ctx sv-grid-chart-menu"
52
+ use:portalToBody
53
+ use:popIn={{}}
54
+ style:position="fixed"
55
+ style:top={`${pos.y}px`}
56
+ style:left={`${pos.x}px`}
57
+ aria-label={ariaLabel}
58
+ >
59
+ <SvMenuList {items} onclose={onClose} onselect={() => onClose()} />
60
+ </div>
61
+
62
+ <style>
63
+ :global(.sv-grid-chart-menu) {
64
+ z-index: 2147483646; min-width: 220px;
65
+ background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
66
+ border: 1px solid var(--sg-border, #e2e8f0); border-radius: 10px;
67
+ box-shadow: 0 16px 48px -12px rgba(15, 23, 42, 0.35); font-size: 13px;
68
+ }
69
+ </style>
@@ -0,0 +1,11 @@
1
+ import { type MenuItem } from './SvMenuList.svelte';
2
+ type Props = {
3
+ items: ReadonlyArray<MenuItem>;
4
+ x: number;
5
+ y: number;
6
+ onClose: () => void;
7
+ ariaLabel?: string;
8
+ };
9
+ declare const SvChartMenu: import("svelte").Component<Props, {}, "">;
10
+ type SvChartMenu = ReturnType<typeof SvChartMenu>;
11
+ export default SvChartMenu;
@@ -0,0 +1,113 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvChartPanes - a price chart with indicator panes stacked under it, the
4
+ * way a trading terminal lays them out: one shared x axis (labelled on the
5
+ * last pane only), one value-axis gutter width so the plots line up, and
6
+ * one crosshair / zoom window across all of them.
7
+ *
8
+ * Composition, not a new layout: every pane is a full `SvChart` in a sync
9
+ * group of its own, so tooltips, keyboard navigation, the screen-reader
10
+ * table, export and the context menu all come for free, and the geometry
11
+ * stays single-plot. The main chart takes every `SvChart` prop.
12
+ */
13
+ import SvGridChart from './SvGridChart.svelte'
14
+ import { indicatorPane, type ChartIndicatorSpec } from './chart-indicators'
15
+ import type { ChartSpec, ChartZoomWindow } from './chart-types'
16
+ import type { SvChartProps } from './SvGridChart.types'
17
+
18
+ type Props = Omit<SvChartProps, 'spec' | 'syncGroup' | 'zoom'> & {
19
+ /** The price spec: a candlestick / OHLC / line chart with the series the
20
+ * indicators read (`ohlc` and `volumes` on it for the volume-based ones). */
21
+ spec: ChartSpec
22
+ /** The panes under the price, top to bottom. */
23
+ indicators?: ChartIndicatorSpec[]
24
+ /** A sync group to share with charts outside the stack. Default: private. */
25
+ syncGroup?: string
26
+ /** Width of the value-axis gutter every pane uses, so the plots line up.
27
+ * Default 56. */
28
+ axisWidth?: number
29
+ /** The shared zoom window, bindable. */
30
+ zoom?: ChartZoomWindow | null
31
+ }
32
+ let {
33
+ spec,
34
+ indicators = [],
35
+ syncGroup,
36
+ axisWidth = 56,
37
+ zoom = $bindable(null),
38
+ onZoom,
39
+ zoomable,
40
+ legend,
41
+ toolbar,
42
+ ...rest
43
+ }: Props = $props()
44
+
45
+ const uid = `svcp-${Math.random().toString(36).slice(2, 8)}`
46
+ const group = $derived(syncGroup ?? uid)
47
+ // Every pane is laid out at the stack's pixel width, so the panes line up
48
+ // and none of them is a 520px viewBox stretched across a 900px column.
49
+ // Heights stay what the specs say; only the width follows the container.
50
+ let hostW = $state(0)
51
+ const widthOf = (s: ChartSpec) => (hostW > 0 && !s.width ? { width: Math.round(hostW) } : {})
52
+ /** The price chart hides its x labels when a pane below will show them. */
53
+ const mainSpec = $derived<ChartSpec>({
54
+ ...spec,
55
+ ...widthOf(spec),
56
+ xAxis: { ...spec.xAxis, ...(indicators.length ? { labels: false } : {}) },
57
+ yAxis: { ...spec.yAxis, width: axisWidth },
58
+ ...(spec.y2Axis || spec.series.some((s) => s.axis === 'right') ? { y2Axis: { ...spec.y2Axis, width: axisWidth } } : {}),
59
+ })
60
+ // The pane's title is drawn by this component as a small corner label
61
+ // rather than by the chart frame, which would take a third of a 100px
62
+ // pane for it.
63
+ const panes = $derived(
64
+ indicators.map((ind, i) => {
65
+ const { title, ...pane } = indicatorPane(spec, ind)
66
+ return {
67
+ title: title ?? ind.kind,
68
+ spec: {
69
+ ...pane,
70
+ ...widthOf(pane),
71
+ xAxis: { ...pane.xAxis, labels: i === indicators.length - 1 },
72
+ yAxis: { ...pane.yAxis, width: axisWidth },
73
+ } satisfies ChartSpec,
74
+ }
75
+ }),
76
+ )
77
+ </script>
78
+
79
+ <div class="sv-chart-panes" bind:clientWidth={hostW}>
80
+ <div class="sv-chart-pane is-main">
81
+ <SvGridChart spec={mainSpec} syncGroup={group} bind:zoom {onZoom} {zoomable} {legend} {toolbar} {...rest} />
82
+ </div>
83
+ {#each panes as pane, i (`${indicators[i]?.kind}-${i}`)}
84
+ <div class="sv-chart-pane is-indicator" data-indicator={indicators[i]?.kind}>
85
+ <span class="sv-chart-pane-title" style:left={`${axisWidth + 6}px`}>{pane.title}</span>
86
+ <SvGridChart spec={pane.spec} syncGroup={group} zoomable={zoomable ? { drag: false, wheel: typeof zoomable === 'object' ? zoomable.wheel : false, pan: typeof zoomable === 'object' ? zoomable.pan : false } : false} legend={false} toolbar={false} interactive localeText={rest.localeText} />
87
+ </div>
88
+ {/each}
89
+ </div>
90
+
91
+ <style>
92
+ .sv-chart-panes {
93
+ display: flex;
94
+ flex-direction: column;
95
+ gap: 2px;
96
+ min-width: 0;
97
+ }
98
+ .sv-chart-pane {
99
+ position: relative;
100
+ min-width: 0;
101
+ }
102
+ /* The title of a pane sits in its top-left corner, small, like a terminal. */
103
+ /* Placed just right of the value-axis gutter, clear of the tick labels. */
104
+ .sv-chart-pane-title {
105
+ position: absolute;
106
+ top: 2px;
107
+ z-index: 1;
108
+ font-size: 10px;
109
+ font-weight: 600;
110
+ color: var(--sg-muted, #64748b);
111
+ pointer-events: none;
112
+ }
113
+ </style>
@@ -0,0 +1,20 @@
1
+ import { type ChartIndicatorSpec } from './chart-indicators.js';
2
+ import type { ChartSpec, ChartZoomWindow } from './chart-types.js';
3
+ import type { SvChartProps } from './SvGridChart.types.js';
4
+ type Props = Omit<SvChartProps, 'spec' | 'syncGroup' | 'zoom'> & {
5
+ /** The price spec: a candlestick / OHLC / line chart with the series the
6
+ * indicators read (`ohlc` and `volumes` on it for the volume-based ones). */
7
+ spec: ChartSpec;
8
+ /** The panes under the price, top to bottom. */
9
+ indicators?: ChartIndicatorSpec[];
10
+ /** A sync group to share with charts outside the stack. Default: private. */
11
+ syncGroup?: string;
12
+ /** Width of the value-axis gutter every pane uses, so the plots line up.
13
+ * Default 56. */
14
+ axisWidth?: number;
15
+ /** The shared zoom window, bindable. */
16
+ zoom?: ChartZoomWindow | null;
17
+ };
18
+ declare const SvChartPanes: import("svelte").Component<Props, {}, "zoom">;
19
+ type SvChartPanes = ReturnType<typeof SvChartPanes>;
20
+ export default SvChartPanes;
@@ -1,7 +1,8 @@
1
- import { type GridPredicateExpr, type CellEditorOption, type Column, type ColumnDef, type Row, type RowData, type TableFeatures, type ChartType, type ChartSpec, type ChartValueFormat } from "./index.js";
1
+ import { type GridPredicateExpr, type CellEditorOption, type Column, type ColumnDef, type Row, type RowData, type TableFeatures, type ChartType, type ChartSpec, type ChartReducer, type ChartTimeBucket, type ChartValueFormat, type ChartZoomWindow, type ChartPanelIndicator, type ChartFormatState, type OhlcColumns } from "./index.js";
2
2
  import "./sv-grid-scrollbar.js";
3
3
  import { type ColumnStat } from "./conditional-formatting.js";
4
4
  import type { Props, SelectionRange, CellEditState, FilterOperator, MenuPosition, ContextMenuTarget, ChartingConfig } from "./SvGrid.types.js";
5
+ import { type HistoryStep as SharedHistoryStep } from "./history.js";
5
6
  import { operatorOption, operatorsForColumn, defaultOperatorFor, operatorLabelFor } from "./filter-operators.js";
6
7
  import { type FacetBucket, isBucketableColumn, buildBuckets, isInBucket } from "./facet-buckets.js";
7
8
  import { getColumnBaseValue, isGroupRow, toolPanelHeaderLabel, formatSummaryNumeric, getColumnAlign, getPinnedCellValue, getColumnAccessorValue, columnDefMatchesId } from "./cell-values.js";
@@ -74,14 +75,9 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
74
75
  } | null;
75
76
  editedCellValues: Record<string, unknown>;
76
77
  readonly UNDO_LIMIT: number;
77
- history: {
78
- rowId: string;
79
- columnId: string;
80
- field: string;
81
- before: unknown;
82
- after: unknown;
83
- }[];
78
+ history: SharedHistoryStep[];
84
79
  historyPtr: number;
80
+ historyGroupId: string | undefined;
85
81
  historyVersion: number;
86
82
  tooltip: {
87
83
  text: string;
@@ -104,6 +100,9 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
104
100
  headerHeight: number;
105
101
  editorSelectAll: boolean;
106
102
  columnWidths: Record<string, number>;
103
+ readonly collapsedColumns: Record<string, boolean>;
104
+ readonly mergeIndex: import("./merges.js").MergeIndex | null;
105
+ readonly setColumnCollapsed: (columnId: string, collapsed: boolean) => void;
107
106
  readonly MIN_COLUMN_WIDTH: number;
108
107
  columnPinning: {
109
108
  left: Array<string>;
@@ -159,8 +158,6 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
159
158
  readonly scrollMetrics: {
160
159
  scrollTop: number;
161
160
  scrollLeft: number;
162
- clientHeight: number;
163
- clientWidth: number;
164
161
  scrollHeight: number;
165
162
  scrollWidth: number;
166
163
  };
@@ -175,6 +172,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
175
172
  readonly moveCellsEffective: boolean;
176
173
  readonly flushScheduledScrollSync: () => void;
177
174
  readonly scheduleScrollSync: (scrollTop: number, scrollLeft: number) => void;
175
+ readonly syncScrollNow: (scrollTop: number, scrollLeft: number) => void;
178
176
  internalData: readonly TData[];
179
177
  internalColumns: ColumnDef<TFeatures, TData>[];
180
178
  hiddenColumns: Record<string, boolean>;
@@ -318,7 +316,9 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
318
316
  readonly toolPanelHeaderLabel: typeof toolPanelHeaderLabel;
319
317
  readonly toggleColumnVisibleInPanel: (columnId: string) => void;
320
318
  readonly moveColumnInPanel: (columnId: string, dir: -1 | 1) => void;
321
- readonly messages: import("./grid-messages.js").GridMessages;
319
+ readonly messages: import("./grid-messages.js").GridChromeMessages;
320
+ /** The raw `localization.text` map, for the lazy chart panel's own strings. */
321
+ readonly localizationText: Partial<import("./grid-messages.js").GridMessages> | undefined;
322
322
  readonly pivotConfig: import("./pivot-view.svelte.js").GridPivotConfig<TData> | null;
323
323
  readonly pivotActive: boolean;
324
324
  readonly pivotResult: {
@@ -341,7 +341,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
341
341
  readonly chartIsCustom: boolean;
342
342
  chartPanelOpen: boolean;
343
343
  chartType: ChartType;
344
- chartReduce: "sum" | "avg" | "count";
344
+ chartReduce: ChartReducer;
345
345
  chartDimensionId: string | null;
346
346
  chartMeasureId: string | null;
347
347
  chartSeriesId: string | null;
@@ -349,6 +349,21 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
349
349
  chartDataLabels: boolean;
350
350
  chartLogScale: boolean;
351
351
  chartTimeAxis: boolean;
352
+ chartBucket: ChartTimeBucket | null;
353
+ chartBins: number | null;
354
+ chartFunnelShape: "trapezoid" | "pyramid" | "cone" | null;
355
+ chartCandleStyle: "classic" | "hollow" | "heikin-ashi" | null;
356
+ chartFormat: ChartFormatState | null;
357
+ readonly chartFrozen: {
358
+ spec: ChartSpec;
359
+ at: string;
360
+ } | null;
361
+ readonly chartIsOhlc: boolean;
362
+ chartOhlc: OhlcColumns;
363
+ readonly chartOhlcDateId: string | null;
364
+ chartIndicators: ChartPanelIndicator[];
365
+ readonly chartIndicatorPanes: import("./chart-indicators.js").ChartIndicatorSpec[];
366
+ chartZoom: ChartZoomWindow | null;
352
367
  chartValueFormat: ChartValueFormat;
353
368
  readonly chartDimensionIsDate: boolean;
354
369
  chartSize: number | null;
@@ -386,27 +401,32 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
386
401
  }[];
387
402
  activeChartIndex: number;
388
403
  addChart(): void;
404
+ /** Unlink the active chart from the grid: it keeps the spec it has now. */
405
+ freezeChart(): void;
406
+ /** Link the active chart back to the grid's rows. */
407
+ unfreezeChart(): void;
389
408
  removeChart(index?: number): void;
390
409
  renameChart(index: number, title: string): void;
391
- getChartsState(): {
392
- title: string;
393
- type: ChartType;
394
- dimension: string | null;
395
- series: string | null;
396
- measure: string | null;
397
- measure2: string | null;
398
- orientation: "horizontal" | "vertical" | null;
399
- stacked100: boolean | null;
400
- donut: boolean | null;
401
- palette: string[] | null;
402
- reduce: "sum" | "avg" | "count";
403
- stacked: boolean | null;
404
- dataLabels: boolean | null;
405
- logScale: boolean | null;
406
- timeAxis: boolean | null;
407
- valueFormat: ChartValueFormat | null;
408
- }[];
410
+ getChartsState(): Record<string, unknown>[];
409
411
  applyChartsState(list: ReadonlyArray<Record<string, unknown>>, active?: number): void;
412
+ readonly savedCharts: {
413
+ name: string;
414
+ savedAt: string;
415
+ tab: Record<string, unknown>;
416
+ }[];
417
+ /** Keep the active tab's configuration under a name (replacing a same-named one). */
418
+ saveChart(name: string): void;
419
+ /** Apply a saved chart to the active tab; the tab keeps its id and title. */
420
+ applySavedChart(name: string): boolean;
421
+ removeSavedChart(name: string): void;
422
+ getSavedCharts(): {
423
+ tab: {
424
+ [x: string]: unknown;
425
+ };
426
+ name: string;
427
+ savedAt: string;
428
+ }[];
429
+ applySavedCharts(list: ReadonlyArray<Record<string, unknown>>): void;
410
430
  readonly chartCrossFilterable: boolean;
411
431
  chartOrientation: "horizontal" | "vertical";
412
432
  chartStacked100: boolean;
@@ -415,18 +435,46 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
415
435
  chartMeasure2Id: string | null;
416
436
  readonly effectiveChartMeasure2Id: string | null;
417
437
  chartAiHandler: ((prompt: string) => Promise<Record<string, unknown> | null>) | null;
438
+ chartExplainHandler: (() => Promise<{
439
+ summary: string;
440
+ insights: string[];
441
+ } | null>) | null;
418
442
  applyChartConfig(config: {
419
443
  open?: boolean;
420
444
  type?: ChartType;
421
445
  dimension?: string | null;
422
446
  series?: string | null;
423
447
  measure?: string | null;
424
- reduce?: "sum" | "avg" | "count";
448
+ reduce?: ChartReducer;
425
449
  stacked?: boolean;
426
450
  dataLabels?: boolean;
427
451
  logScale?: boolean;
428
452
  timeAxis?: boolean;
429
453
  valueFormat?: ChartValueFormat;
454
+ bucket?: ChartTimeBucket | null;
455
+ bins?: number | null;
456
+ funnelShape?: "trapezoid" | "pyramid" | "cone" | null;
457
+ candleStyle?: "classic" | "hollow" | "heikin-ashi" | null;
458
+ /** Second measure: scatter Y, range high end, bullet target. */
459
+ measure2?: string | null;
460
+ /** Candlestick / OHLC price columns (ids or fields). */
461
+ ohlc?: {
462
+ open?: string | null;
463
+ high?: string | null;
464
+ low?: string | null;
465
+ close?: string | null;
466
+ volume?: string | null;
467
+ } | null;
468
+ /** Indicators: panes (volume, rsi, macd, stochastic, atr, obv) and
469
+ * overlays (sma, ema, bb, vwap). */
470
+ indicators?: ChartPanelIndicator[];
471
+ /** The builder's format (titles, axes, legend, series style); `null` resets it. */
472
+ format?: ChartFormatState | null;
473
+ /** Unlink the chart from the grid (true) or link it back (false). */
474
+ frozen?: boolean;
475
+ /** A saved chart's name: applied to the active tab first, then the
476
+ * other keys on top. */
477
+ saved?: string;
430
478
  }): void;
431
479
  readonly toggleGroupInPanel: (columnId: string) => void;
432
480
  lastSortingSerialized: string;
@@ -471,8 +519,17 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
471
519
  readonly measuredRowHeightPx: (index: number) => number | undefined;
472
520
  /** True when the `rowResize` prop is on and nothing overrides it. */
473
521
  readonly rowResizeOn: boolean;
522
+ readonly frozenRowCount: number;
523
+ readonly frozenRowList: Row<TData>[];
524
+ readonly frozenBandHeight: number;
525
+ readonly frozenRowTop: (i: number) => number;
474
526
  /** Record the height the user dragged a row to. */
475
- readonly setRowResizeHeight: (index: number, height: number) => void;
527
+ readonly setRowResizeHeight: (index: number, height: number | null) => void;
528
+ readonly isRowCollapsed: (index: number) => boolean;
529
+ readonly setRowCollapsed: (index: number, collapsed: boolean) => void;
530
+ readonly resizeRowByUser: (index: number, height: number | null) => void;
531
+ readonly resizeColumnByUser: (columnId: string, width: number, before: number) => void;
532
+ readonly autosizeColumnByUser: (columnId: string) => void;
476
533
  /** A row's dragged height, or undefined when it has not been resized. */
477
534
  readonly rowResizeHeightPx: (index: number) => number | undefined;
478
535
  readonly virtualRowTotalSize: number;
@@ -546,6 +603,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
546
603
  readonly headerSelectionState: "none" | "all" | "some";
547
604
  readonly toggleSelectAllRows: () => void;
548
605
  userHasActivatedCell: boolean;
606
+ tabRunOrigin: number | null;
549
607
  readonly setActiveCell: (rowIndex: number, colIndex: number) => void;
550
608
  readonly scrollActiveCellIntoView: (rowIndex: number, colIndex: number) => void;
551
609
  readonly getColumnBaseWidth: (columnId: string) => any;
@@ -560,6 +618,22 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
560
618
  left: boolean;
561
619
  right: boolean;
562
620
  } | null;
621
+ readonly activeRangeRect: () => {
622
+ minRow: number;
623
+ maxRow: number;
624
+ minCol: number;
625
+ maxCol: number;
626
+ } | null;
627
+ readonly cellSelectionState: (rowIndex: number, colIndex: number) => {
628
+ active: boolean;
629
+ edges: {
630
+ top: boolean;
631
+ bottom: boolean;
632
+ left: boolean;
633
+ right: boolean;
634
+ } | null;
635
+ fillHandle: boolean;
636
+ } | null;
563
637
  readonly getSelectionRects: () => {
564
638
  minRow: number;
565
639
  maxRow: number;
@@ -593,6 +667,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
593
667
  readonly startFillDrag: (event: PointerEvent, rowIndex: number, colIndex: number) => void;
594
668
  readonly onFillPointerMove: (event: PointerEvent) => void;
595
669
  readonly onFillPointerUp: () => void;
670
+ readonly fillDownToNeighbour: () => void;
596
671
  readonly isOnMoveGrabStrip: (cell: HTMLElement, rowIndex: number, colIndex: number, clientX: number, clientY: number) => boolean;
597
672
  readonly startMoveDrag: (event: PointerEvent, rowIndex: number, colIndex: number) => boolean;
598
673
  readonly onMovePointerMove: (event: PointerEvent) => void;
@@ -626,6 +701,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
626
701
  readonly cutSelectionToClipboard: () => Promise<void>;
627
702
  readonly pasteFromClipboard: () => Promise<void>;
628
703
  readonly onGridPaste: (event: ClipboardEvent) => void;
704
+ readonly armPasteFallback: () => void;
629
705
  readonly clearSelectedCells: () => boolean;
630
706
  readonly onCellDoubleClick: (rowIndex: number, colIndex: number) => void;
631
707
  readonly startEditingWithChar: (rowIndex: number, colIndex: number, char: string) => boolean;
@@ -636,13 +712,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
636
712
  readonly commitFullRowEdit: () => void;
637
713
  readonly cancelFullRowEdit: () => void;
638
714
  readonly saveEditingCell: () => void;
639
- readonly applyHistoryStep: (step: {
640
- rowId: string;
641
- columnId: string;
642
- field: string;
643
- before: unknown;
644
- after: unknown;
645
- }, direction: "undo" | "redo") => void;
715
+ readonly applyHistoryStep: (step: SharedHistoryStep, direction: "undo" | "redo") => void;
646
716
  readonly updateEditingCellValue: (value: unknown) => void;
647
717
  readonly onEditorKeyDown: (event: KeyboardEvent) => void;
648
718
  readonly commitAndMoveByTab: (shiftKey: boolean) => void;
@@ -657,6 +727,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
657
727
  readonly contextMenuItems: () => {
658
728
  key: string;
659
729
  label: string;
730
+ icon?: import("./grid-icons.js").GridIconName | import("./SvGrid.types.js").ContextMenuIcon;
660
731
  separator?: boolean;
661
732
  disabled?: boolean;
662
733
  run?: (() => void) | undefined;