@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
package/README.md CHANGED
@@ -118,9 +118,16 @@ Compatibility aliases are provided for the framework-neutral names: `createGrid`
118
118
 
119
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.
120
120
 
121
- ## Enterprise
121
+ ## Enterprise features
122
122
 
123
- The MIT core is free for any use, including commercial. The optional [`@svgrid/enterprise`](https://www.npmjs.com/package/@svgrid/enterprise) pack adds Excel / PDF / CSV / TSV / HTML export, paginated print, pivot tables with a drag-and-drop Designer, and AI helpers. OSS projects receive an Enterprise key free. See [Pricing](https://svgrid.com/pricing).
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:
124
+
125
+ - **Excel / PDF / CSV / TSV / HTML export** plus paginated print.
126
+ - **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.
129
+
130
+ OSS projects receive an Enterprise key free. See the [Enterprise docs](https://svgrid.com/docs) and [Pricing](https://svgrid.com/pricing).
124
131
 
125
132
  ## Documentation
126
133
 
@@ -0,0 +1,306 @@
1
+ <script lang="ts">
2
+ /**
3
+ * DockNodeView - the recursive renderer for one node of a <SvDockLayout> tree.
4
+ * A `group` becomes a flex row/column of children with draggable splitters
5
+ * between them; a `tabs` leaf becomes a tab strip over the active pane's
6
+ * content. All tree edits go through the pure `dock-model`; this file only
7
+ * paints and wires gestures. It renders itself for nested groups.
8
+ */
9
+ import { getContext } from 'svelte'
10
+ import Self from './DockNodeView.svelte'
11
+ import { DOCK_CONTEXT, type DockContext } from './dock-context'
12
+ import { leafMinSize, type DockNode, type DockGroup } from './dock-model'
13
+
14
+ let { node }: { node: DockNode } = $props()
15
+
16
+ const ctx = getContext<DockContext>(DOCK_CONTEXT)
17
+
18
+ // ---- splitter drag (group children resize) ----------------------------
19
+ let groupEl = $state<HTMLElement | null>(null)
20
+
21
+ /** A child's minimum size (px): a leaf's largest per-pane `minSize`, else 0. */
22
+ function nodeMin(n: DockNode | undefined): number {
23
+ return n && n.type === 'tabs' ? leafMinSize(n) : 0
24
+ }
25
+
26
+ function onSplitterDown(e: PointerEvent, group: DockGroup, index: number) {
27
+ if (e.button !== 0 || !groupEl) return
28
+ e.preventDefault()
29
+ const vertical = group.direction === 'column'
30
+ const rect = groupEl.getBoundingClientRect()
31
+ const total = vertical ? rect.height : rect.width
32
+ if (total <= 0) return
33
+ const startSizes = group.sizes.slice()
34
+ const startPos = vertical ? e.clientY : e.clientX
35
+ // Only the two panes adjacent to this gutter move; the rest stay put. Each
36
+ // honors its own minimum (a leaf's largest per-pane `minSize`, else global).
37
+ const a = index
38
+ const b = index + 1
39
+ const minA = Math.max(ctx.minSize(), nodeMin(group.children[a])) / total
40
+ const minB = Math.max(ctx.minSize(), nodeMin(group.children[b])) / total
41
+ const pairTotal = startSizes[a]! + startSizes[b]!
42
+
43
+ const onMove = (ev: PointerEvent) => {
44
+ const delta = ((vertical ? ev.clientY : ev.clientX) - startPos) / total
45
+ let fa = Math.min(Math.max(startSizes[a]! + delta, minA), pairTotal - minB)
46
+ const next = startSizes.slice()
47
+ next[a] = fa
48
+ next[b] = pairTotal - fa
49
+ ctx.resize(group.id, next)
50
+ }
51
+ const onUp = () => {
52
+ window.removeEventListener('pointermove', onMove)
53
+ window.removeEventListener('pointerup', onUp)
54
+ document.body.style.userSelect = ''
55
+ document.body.style.cursor = ''
56
+ }
57
+ document.body.style.userSelect = 'none'
58
+ document.body.style.cursor = vertical ? 'row-resize' : 'col-resize'
59
+ window.addEventListener('pointermove', onMove)
60
+ window.addEventListener('pointerup', onUp)
61
+ }
62
+ </script>
63
+
64
+ {#if node.type === 'group'}
65
+ <div
66
+ class="sv-dock__group sv-dock__group--{node.direction}"
67
+ bind:this={groupEl}
68
+ role="group"
69
+ >
70
+ {#each node.children as child, i (child.id)}
71
+ <div class="sv-dock__cell" style:flex-grow={node.sizes[i] ?? 1} style:flex-basis="0">
72
+ <Self node={child} />
73
+ </div>
74
+ {#if i < node.children.length - 1}
75
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
76
+ <div
77
+ class="sv-dock__splitter sv-dock__splitter--{node.direction}"
78
+ role="separator"
79
+ aria-orientation={node.direction === 'column' ? 'horizontal' : 'vertical'}
80
+ onpointerdown={(e) => onSplitterDown(e, node as DockGroup, i)}
81
+ ></div>
82
+ {/if}
83
+ {/each}
84
+ </div>
85
+ {:else}
86
+ {@const active = node.panes[node.active] ?? node.panes[0]}
87
+ {@const reorder = ctx.reorderTarget?.() ?? null}
88
+ {@const reorderHere = reorder && reorder.tabsId === node.id ? reorder.index : -1}
89
+ {@const hp = ctx.headerPosition?.() ?? 'top'}
90
+ <!-- A single-pane leaf in an auto-hide fly-out drops its (redundant with the edge strip)
91
+ tab button, leaving just the leaf actions (pin). -->
92
+ {@const bareSingle = !!ctx.hideSingleTab?.() && node.panes.length === 1}
93
+ <div class="sv-dock__leaf sv-dock__leaf--h-{hp}" class:is-focused={ctx.focusedLeaf?.() === node.id} data-dock-tabs={node.id}>
94
+ <div class="sv-dock__tabstrip" class:is-bare={bareSingle} role="tablist">
95
+ {#if !bareSingle}
96
+ {#each node.panes as p, i (p.id)}
97
+ {#if reorderHere === i}<div class="sv-dock__ins" aria-hidden="true"></div>{/if}
98
+ <div
99
+ class="sv-dock__tab"
100
+ class:is-active={i === node.active}
101
+ role="tab"
102
+ aria-selected={i === node.active}
103
+ tabindex={i === node.active ? 0 : -1}
104
+ data-dock-tab={p.id}
105
+ data-tab-index={i}
106
+ onpointerdown={(e) => {
107
+ if (e.button === 0) { ctx.activate(node.id, i); ctx.beginDrag(e, p.id, node.id) }
108
+ }}
109
+ onkeydown={(e) => {
110
+ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); ctx.activate(node.id, i) }
111
+ }}
112
+ >
113
+ <span class="sv-dock__tab-label">{p.title}</span>
114
+ {#if p.closable !== false}
115
+ <button
116
+ type="button"
117
+ class="sv-dock__tab-close"
118
+ aria-label={`Close ${p.title}`}
119
+ title="Close"
120
+ onpointerdown={(e) => e.stopPropagation()}
121
+ onclick={(e) => { e.stopPropagation(); ctx.close(p.id) }}
122
+ >&times;</button>
123
+ {/if}
124
+ </div>
125
+ {/each}
126
+ {#if reorderHere === node.panes.length}<div class="sv-dock__ins" aria-hidden="true"></div>{/if}
127
+ {/if}
128
+ {#if ctx.leafActions}
129
+ <div class="sv-dock__leafctl">{@render ctx.leafActions(node)}</div>
130
+ {/if}
131
+ </div>
132
+
133
+ {#if ctx.keepAlive?.()}
134
+ <!-- Keep every tab mounted; hide the inactive ones so their state persists. -->
135
+ {#each node.panes as p (p.id)}
136
+ <div class="sv-dock__content" role="tabpanel" style:display={p.id === active?.id ? '' : 'none'}>
137
+ {@render ctx.pane(p)}
138
+ </div>
139
+ {/each}
140
+ {:else}
141
+ <!-- Only the active tab renders; `{#key}` remounts on switch so a pane's
142
+ content never bleeds into the next (use keepAlive to persist state). -->
143
+ {#if active}
144
+ {#key active.id}
145
+ <div class="sv-dock__content" role="tabpanel">{@render ctx.pane(active)}</div>
146
+ {/key}
147
+ {/if}
148
+ {/if}
149
+
150
+ {#if ctx.dropTarget()?.tabsId === node.id}
151
+ {@const dt = ctx.dropTarget()!}
152
+ {@const zone = dt.zone}
153
+ {@const centerOnly = dt.centerOnly === true}
154
+ <div class="sv-dock__dropzone" aria-hidden="true">
155
+ <!-- Preview: the region the panel will occupy (only when over a chip). -->
156
+ {#if zone}<div class="sv-dock__drop-hint sv-dock__drop-hint--{zone}"></div>{/if}
157
+ <!-- Guide: dock targets. Drop ON a chip to dock; off it, the tab floats. -->
158
+ <div class="sv-dock__guide" class:is-center-only={centerOnly}>
159
+ {#if !centerOnly}
160
+ <div class="sv-dock__guide-cell sv-dock__guide-cell--top" class:is-on={zone === 'top'}></div>
161
+ {/if}
162
+ <div class="sv-dock__guide-row">
163
+ {#if !centerOnly}
164
+ <div class="sv-dock__guide-cell sv-dock__guide-cell--left" class:is-on={zone === 'left'}></div>
165
+ {/if}
166
+ <div class="sv-dock__guide-cell sv-dock__guide-cell--center" class:is-on={zone === 'center'}></div>
167
+ {#if !centerOnly}
168
+ <div class="sv-dock__guide-cell sv-dock__guide-cell--right" class:is-on={zone === 'right'}></div>
169
+ {/if}
170
+ </div>
171
+ {#if !centerOnly}
172
+ <div class="sv-dock__guide-cell sv-dock__guide-cell--bottom" class:is-on={zone === 'bottom'}></div>
173
+ {/if}
174
+ </div>
175
+ </div>
176
+ {/if}
177
+ </div>
178
+ {/if}
179
+
180
+ <style>
181
+ .sv-dock__group { display: flex; width: 100%; height: 100%; min-width: 0; min-height: 0; }
182
+ .sv-dock__group--row { flex-direction: row; }
183
+ .sv-dock__group--column { flex-direction: column; }
184
+ .sv-dock__cell { min-width: 0; min-height: 0; overflow: hidden; display: flex; }
185
+ .sv-dock__cell > :global(*) { flex: 1; min-width: 0; min-height: 0; }
186
+
187
+ .sv-dock__splitter { flex: none; background: var(--sg-border, #e2e8f0); position: relative; touch-action: none; }
188
+ .sv-dock__splitter::after { content: ''; position: absolute; inset: -3px; }
189
+ .sv-dock__splitter--row { width: 4px; cursor: col-resize; }
190
+ .sv-dock__splitter--column { height: 4px; cursor: row-resize; }
191
+ .sv-dock__splitter:hover { background: var(--sg-accent, #2563eb); }
192
+
193
+ .sv-dock__leaf {
194
+ position: relative; display: flex; flex-direction: column; width: 100%; height: 100%;
195
+ min-width: 0; min-height: 0; background: var(--sg-bg, #fff);
196
+ border: 1px solid var(--sg-border, #e2e8f0); border-radius: 8px; overflow: hidden;
197
+ }
198
+ .sv-dock__tabstrip {
199
+ display: flex; flex: none; gap: 2px; padding: 4px 4px 0; overflow-x: auto;
200
+ background: var(--sg-header-bg, #f6f7f9); border-bottom: 1px solid var(--sg-border, #e2e8f0);
201
+ }
202
+ /* Focused leaf: accent-tinted header + active-tab underline. */
203
+ .sv-dock__leaf.is-focused > .sv-dock__tabstrip { background: color-mix(in srgb, var(--sg-accent, #2563eb) 8%, var(--sg-header-bg, #f6f7f9)); }
204
+ .sv-dock__leaf.is-focused .sv-dock__tab.is-active { box-shadow: inset 0 -2px 0 var(--sg-accent, #2563eb); }
205
+ /* Header on the bottom: content first, strip below with flipped chrome. */
206
+ .sv-dock__leaf--h-bottom { flex-direction: column-reverse; }
207
+ .sv-dock__leaf--h-bottom .sv-dock__tabstrip { padding: 0 4px 4px; border-bottom: none; border-top: 1px solid var(--sg-border, #e2e8f0); }
208
+ .sv-dock__leaf--h-bottom .sv-dock__tab { border-radius: 0 0 7px 7px; border-top: none; border-bottom: 1px solid transparent; }
209
+ .sv-dock__leaf--h-bottom .sv-dock__tab.is-active { border-color: var(--sg-border, #e2e8f0); border-top: none; }
210
+
211
+ /* Header on the left/right: a VERTICAL tab strip beside the content. */
212
+ .sv-dock__leaf--h-left { flex-direction: row; }
213
+ .sv-dock__leaf--h-right { flex-direction: row-reverse; }
214
+ .sv-dock__leaf--h-left .sv-dock__tabstrip,
215
+ .sv-dock__leaf--h-right .sv-dock__tabstrip {
216
+ flex-direction: column; overflow-x: hidden; overflow-y: auto; padding: 4px 0 4px 4px;
217
+ border-bottom: none;
218
+ }
219
+ .sv-dock__leaf--h-left .sv-dock__tabstrip { border-right: 1px solid var(--sg-border, #e2e8f0); }
220
+ .sv-dock__leaf--h-right .sv-dock__tabstrip { padding: 4px 4px 4px 0; border-left: 1px solid var(--sg-border, #e2e8f0); }
221
+ .sv-dock__leaf--h-left .sv-dock__tab,
222
+ .sv-dock__leaf--h-right .sv-dock__tab {
223
+ writing-mode: vertical-rl; padding: 10px 6px; border-radius: 7px 0 0 7px; border-bottom: 1px solid transparent;
224
+ max-height: 200px; overflow: hidden;
225
+ }
226
+ .sv-dock__leaf--h-right .sv-dock__tab { border-radius: 0 7px 7px 0; }
227
+ .sv-dock__leaf--h-left .sv-dock__tab.is-active,
228
+ .sv-dock__leaf--h-right .sv-dock__tab.is-active { border-color: var(--sg-border, #e2e8f0); }
229
+ /* Stack controls go to the bottom of a vertical strip. */
230
+ .sv-dock__leaf--h-left .sv-dock__leafctl,
231
+ .sv-dock__leaf--h-right .sv-dock__leafctl { flex-direction: column; margin-inline-start: 0; margin-top: auto; padding: 2px 0; align-self: center; }
232
+ /* Reorder insertion line runs horizontally between stacked tabs. */
233
+ .sv-dock__leaf--h-left .sv-dock__ins,
234
+ .sv-dock__leaf--h-right .sv-dock__ins { width: auto; height: 3px; align-self: stretch; margin: 0 3px; }
235
+ .sv-dock__tab {
236
+ display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; cursor: grab;
237
+ font-size: 12.5px; font-weight: 600; color: var(--sg-muted, #64748b); white-space: nowrap;
238
+ border: 1px solid transparent; border-bottom: none; border-radius: 7px 7px 0 0; user-select: none;
239
+ touch-action: none;
240
+ }
241
+ .sv-dock__tab:hover { color: var(--sg-fg, #0f172a); }
242
+ .sv-dock__tab.is-active {
243
+ color: var(--sg-fg, #0f172a); background: var(--sg-bg, #fff);
244
+ border-color: var(--sg-border, #e2e8f0);
245
+ }
246
+ .sv-dock__tab-close {
247
+ display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px;
248
+ padding: 0; font-size: 15px; line-height: 1; color: var(--sg-muted, #94a3b8); cursor: pointer;
249
+ background: none; border: none; border-radius: 4px;
250
+ }
251
+ .sv-dock__tab-close:hover { background: var(--sg-row-hover-bg, #f1f5f9); color: var(--sg-danger, #dc2626); }
252
+ /* Stack-header controls, pinned to the right of the tab strip. */
253
+ .sv-dock__leafctl { margin-inline-start: auto; align-self: center; display: flex; align-items: center; gap: 1px; padding: 0 2px 3px; }
254
+ .sv-dock__content { flex: 1; min-height: 0; overflow: auto; }
255
+
256
+ /* Tab-reorder insertion line. */
257
+ .sv-dock__ins {
258
+ flex: none; align-self: stretch; width: 3px; margin: 3px 0; border-radius: 2px;
259
+ background: var(--sg-accent, #2563eb); box-shadow: 0 0 0 1px color-mix(in srgb, var(--sg-accent, #2563eb) 40%, transparent);
260
+ }
261
+
262
+ /* Drop-zone indicators shown on the leaf under the pointer while dragging. */
263
+ .sv-dock__dropzone { position: absolute; inset: 0; pointer-events: none; z-index: 6; }
264
+ /* Ring around the whole targeted leaf, so which pane you're over is obvious. */
265
+ .sv-dock__dropzone::before {
266
+ content: ''; position: absolute; inset: 2px; border-radius: 7px;
267
+ box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 32%, transparent);
268
+ }
269
+ .sv-dock__drop-hint {
270
+ position: absolute; background: color-mix(in srgb, var(--sg-accent, #2563eb) 22%, transparent);
271
+ border: 2px solid var(--sg-accent, #2563eb); border-radius: 6px; transition: inset 90ms ease;
272
+ }
273
+ .sv-dock__drop-hint--center { inset: 8px; }
274
+ .sv-dock__drop-hint--left { inset: 4px 50% 4px 4px; }
275
+ .sv-dock__drop-hint--right { inset: 4px 4px 4px 50%; }
276
+ .sv-dock__drop-hint--top { inset: 4px 4px 50% 4px; }
277
+ .sv-dock__drop-hint--bottom { inset: 50% 4px 4px 4px; }
278
+
279
+ /* The 5-direction dock guide (VS-style): a plus of targets, active one lit. */
280
+ .sv-dock__guide {
281
+ position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
282
+ display: flex; flex-direction: column; align-items: center; gap: 4px;
283
+ padding: 6px; border-radius: 10px;
284
+ background: color-mix(in srgb, var(--sg-bg, #fff) 82%, transparent);
285
+ box-shadow: 0 6px 20px -6px rgba(15,23,42,0.45); border: 1px solid var(--sg-border, #e2e8f0);
286
+ }
287
+ .sv-dock__guide-row { display: flex; gap: 4px; }
288
+ .sv-dock__guide-cell {
289
+ width: 26px; height: 26px; border-radius: 6px; position: relative;
290
+ background: var(--sg-header-bg, #f1f5f9); border: 1px solid var(--sg-border, #cbd5e1);
291
+ }
292
+ .sv-dock__guide-cell::after {
293
+ content: ''; position: absolute; border-radius: 3px;
294
+ background: color-mix(in srgb, var(--sg-accent, #2563eb) 55%, transparent);
295
+ }
296
+ .sv-dock__guide-cell--center::after { inset: 5px; }
297
+ .sv-dock__guide-cell--left::after { inset: 5px 13px 5px 5px; }
298
+ .sv-dock__guide-cell--right::after { inset: 5px 5px 5px 13px; }
299
+ .sv-dock__guide-cell--top::after { inset: 5px 5px 13px 5px; }
300
+ .sv-dock__guide-cell--bottom::after { inset: 13px 5px 5px 5px; }
301
+ .sv-dock__guide-cell.is-on {
302
+ background: color-mix(in srgb, var(--sg-accent, #2563eb) 18%, var(--sg-bg, #fff));
303
+ border-color: var(--sg-accent, #2563eb);
304
+ }
305
+ .sv-dock__guide-cell.is-on::after { background: var(--sg-accent, #2563eb); }
306
+ </style>
@@ -0,0 +1,7 @@
1
+ import { type DockNode } from './dock-model';
2
+ type $$ComponentProps = {
3
+ node: DockNode;
4
+ };
5
+ declare const DockNodeView: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type DockNodeView = ReturnType<typeof DockNodeView>;
7
+ export default DockNodeView;
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ /**
3
+ * DockPopoutHost - the tiny root mounted into a popped-out browser window. It
4
+ * just renders the manager's `pane` snippet for the popped pane, so a pop-out
5
+ * shows the exact same content as the docked view.
6
+ */
7
+ import type { Snippet } from 'svelte'
8
+ import type { DockPane } from './dock-model'
9
+
10
+ let { pane, paneData }: { pane: Snippet<[DockPane]>; paneData: DockPane } = $props()
11
+ </script>
12
+
13
+ <div class="sv-popout-host">{@render pane(paneData)}</div>
14
+
15
+ <style>
16
+ .sv-popout-host { width: 100%; height: 100vh; overflow: auto; box-sizing: border-box; }
17
+ </style>
@@ -0,0 +1,14 @@
1
+ /**
2
+ * DockPopoutHost - the tiny root mounted into a popped-out browser window. It
3
+ * just renders the manager's `pane` snippet for the popped pane, so a pop-out
4
+ * shows the exact same content as the docked view.
5
+ */
6
+ import type { Snippet } from 'svelte';
7
+ import type { DockPane } from './dock-model';
8
+ type $$ComponentProps = {
9
+ pane: Snippet<[DockPane]>;
10
+ paneData: DockPane;
11
+ };
12
+ declare const DockPopoutHost: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type DockPopoutHost = ReturnType<typeof DockPopoutHost>;
14
+ export default DockPopoutHost;
@@ -1,96 +1,96 @@
1
- <script
2
- lang="ts"
3
- generics="TData extends RowData"
4
- >
5
- import { isFunction } from './core'
6
- import {
7
- RenderComponentConfig,
8
- RenderSnippetConfig,
9
- } from './render-component'
10
- import type {
11
- Cell,
12
- CellContext,
13
- ColumnDefTemplate,
14
- Header,
15
- HeaderContext,
16
- RowData,
17
- } from './core'
18
-
19
- type Props =
20
- | {
21
- content?: ColumnDefTemplate<
22
- | HeaderContext<TData>
23
- | CellContext<TData>
24
- >
25
- context:
26
- | HeaderContext<TData>
27
- | CellContext<TData>
28
- cell?: never
29
- header?: never
30
- footer?: never
31
- }
32
- | {
33
- cell: Cell<TData>
34
- content?: never
35
- context?: never
36
- header?: never
37
- footer?: never
38
- }
39
- | {
40
- header: Header<TData>
41
- content?: never
42
- context?: never
43
- cell?: never
44
- footer?: never
45
- }
46
- | {
47
- footer: Header<TData>
48
- content?: never
49
- context?: never
50
- cell?: never
51
- header?: never
52
- }
53
-
54
- let props: Props = $props()
55
-
56
- const resolved = $derived.by(() => {
57
- if ('cell' in props && props.cell) {
58
- return {
59
- content: props.cell.column.columnDef.cell,
60
- context: props.cell.getContext(),
61
- }
62
- }
63
- if ('header' in props && props.header) {
64
- return {
65
- content: props.header.column.columnDef.header,
66
- context: props.header.getContext(),
67
- }
68
- }
69
- if ('footer' in props && props.footer) {
70
- return {
71
- content: props.footer.column.columnDef.footer,
72
- context: props.footer.getContext(),
73
- }
74
- }
75
- return {
76
- content: props.content,
77
- context: props.context,
78
- }
79
- })
80
-
81
- const result = $derived(
82
- isFunction(resolved.content)
83
- ? resolved.content(resolved.context as any)
84
- : undefined,
85
- )
86
- </script>
87
-
88
- {#if typeof resolved.content === 'string'}
89
- {resolved.content}
90
- {:else if result instanceof RenderComponentConfig}
91
- <result.component {...result.props} />
92
- {:else if result instanceof RenderSnippetConfig}
93
- {@render result.snippet(result.params)}
94
- {:else if result !== undefined}
95
- {result}
96
- {/if}
1
+ <script
2
+ lang="ts"
3
+ generics="TData extends RowData"
4
+ >
5
+ import { isFunction } from './core'
6
+ import {
7
+ RenderComponentConfig,
8
+ RenderSnippetConfig,
9
+ } from './render-component'
10
+ import type {
11
+ Cell,
12
+ CellContext,
13
+ ColumnDefTemplate,
14
+ Header,
15
+ HeaderContext,
16
+ RowData,
17
+ } from './core'
18
+
19
+ type Props =
20
+ | {
21
+ content?: ColumnDefTemplate<
22
+ | HeaderContext<TData>
23
+ | CellContext<TData>
24
+ >
25
+ context:
26
+ | HeaderContext<TData>
27
+ | CellContext<TData>
28
+ cell?: never
29
+ header?: never
30
+ footer?: never
31
+ }
32
+ | {
33
+ cell: Cell<TData>
34
+ content?: never
35
+ context?: never
36
+ header?: never
37
+ footer?: never
38
+ }
39
+ | {
40
+ header: Header<TData>
41
+ content?: never
42
+ context?: never
43
+ cell?: never
44
+ footer?: never
45
+ }
46
+ | {
47
+ footer: Header<TData>
48
+ content?: never
49
+ context?: never
50
+ cell?: never
51
+ header?: never
52
+ }
53
+
54
+ let props: Props = $props()
55
+
56
+ const resolved = $derived.by(() => {
57
+ if ('cell' in props && props.cell) {
58
+ return {
59
+ content: props.cell.column.columnDef.cell,
60
+ context: props.cell.getContext(),
61
+ }
62
+ }
63
+ if ('header' in props && props.header) {
64
+ return {
65
+ content: props.header.column.columnDef.header,
66
+ context: props.header.getContext(),
67
+ }
68
+ }
69
+ if ('footer' in props && props.footer) {
70
+ return {
71
+ content: props.footer.column.columnDef.footer,
72
+ context: props.footer.getContext(),
73
+ }
74
+ }
75
+ return {
76
+ content: props.content,
77
+ context: props.context,
78
+ }
79
+ })
80
+
81
+ const result = $derived(
82
+ isFunction(resolved.content)
83
+ ? resolved.content(resolved.context as any)
84
+ : undefined,
85
+ )
86
+ </script>
87
+
88
+ {#if typeof resolved.content === 'string'}
89
+ {resolved.content}
90
+ {:else if result instanceof RenderComponentConfig}
91
+ <result.component {...result.props} />
92
+ {:else if result instanceof RenderSnippetConfig}
93
+ {@render result.snippet(result.params)}
94
+ {:else if result !== undefined}
95
+ {result}
96
+ {/if}