@svgrid/grid 2.1.21 → 2.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) 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/GridMenus.svelte +83 -3
  7. package/dist/SvAutoComplete.svelte +5 -3
  8. package/dist/SvCarousel.svelte +20 -42
  9. package/dist/SvColorInput.svelte +4 -3
  10. package/dist/SvColorInput.svelte.d.ts +1 -1
  11. package/dist/SvComboBox.svelte +7 -3
  12. package/dist/SvCommand.svelte +28 -85
  13. package/dist/SvCountryInput.svelte +1 -1
  14. package/dist/SvDateRangeInput.svelte +1 -1
  15. package/dist/SvDateTimePicker.svelte +9 -3
  16. package/dist/SvDateTimePicker.svelte.d.ts +3 -0
  17. package/dist/SvDockLayout.svelte +244 -0
  18. package/dist/SvDockLayout.svelte.d.ts +77 -0
  19. package/dist/SvDockManager.svelte +922 -0
  20. package/dist/SvDockManager.svelte.d.ts +106 -0
  21. package/dist/SvDrawer.svelte +69 -47
  22. package/dist/SvDrawer.svelte.d.ts +4 -0
  23. package/dist/SvDropDownList.svelte +85 -13
  24. package/dist/SvDropDownList.svelte.d.ts +3 -0
  25. package/dist/SvField.svelte +21 -4
  26. package/dist/SvField.svelte.d.ts +1 -0
  27. package/dist/SvForm.svelte +201 -91
  28. package/dist/SvForm.svelte.d.ts +15 -22
  29. package/dist/SvGrid.controller.svelte.d.ts +32 -1
  30. package/dist/SvGrid.controller.svelte.js +156 -27
  31. package/dist/SvGrid.css +193 -5
  32. package/dist/SvGrid.svelte +357 -12
  33. package/dist/SvGrid.types.d.ts +452 -1
  34. package/dist/SvGridDropdown.svelte +8 -1
  35. package/dist/SvGridSelect.svelte +1 -0
  36. package/dist/SvListBox.svelte +36 -14
  37. package/dist/SvMaskedInput.svelte +4 -3
  38. package/dist/SvMaskedInput.svelte.d.ts +1 -1
  39. package/dist/SvMenu.svelte +1 -1
  40. package/dist/SvMenuList.svelte +23 -71
  41. package/dist/SvMenuList.svelte.d.ts +2 -13
  42. package/dist/SvModal.svelte +12 -29
  43. package/dist/SvMultiSelect.svelte +1 -0
  44. package/dist/SvNumberInput.svelte +4 -3
  45. package/dist/SvNumberInput.svelte.d.ts +1 -1
  46. package/dist/SvPagination.svelte +18 -18
  47. package/dist/SvPasswordInput.svelte +3 -2
  48. package/dist/SvPasswordInput.svelte.d.ts +1 -1
  49. package/dist/SvPhoneInput.svelte +3 -2
  50. package/dist/SvPhoneInput.svelte.d.ts +1 -1
  51. package/dist/SvPopover.svelte +1 -1
  52. package/dist/SvStepper.svelte +6 -9
  53. package/dist/SvTagsInput.svelte +3 -2
  54. package/dist/SvTagsInput.svelte.d.ts +1 -1
  55. package/dist/SvTooltip.svelte +12 -23
  56. package/dist/SvTour.svelte +32 -4
  57. package/dist/SvTreeSelect.svelte +1 -0
  58. package/dist/build-api.js +76 -54
  59. package/dist/cdn/svgrid.js +10907 -6444
  60. package/dist/cdn/svgrid.svelte-external.js +9622 -5475
  61. package/dist/columns.js +6 -0
  62. package/dist/conditional-formatting.d.ts +67 -3
  63. package/dist/conditional-formatting.js +124 -17
  64. package/dist/createCarousel.svelte.d.ts +72 -0
  65. package/dist/createCarousel.svelte.js +80 -0
  66. package/dist/createCommand.svelte.d.ts +62 -0
  67. package/dist/createCommand.svelte.js +109 -0
  68. package/dist/createForm.svelte.d.ts +62 -0
  69. package/dist/createForm.svelte.js +374 -0
  70. package/dist/createMenu.svelte.d.ts +88 -0
  71. package/dist/createMenu.svelte.js +153 -0
  72. package/dist/createOverlay.svelte.d.ts +38 -0
  73. package/dist/createOverlay.svelte.js +68 -0
  74. package/dist/createPagination.d.ts +74 -0
  75. package/dist/createPagination.js +71 -0
  76. package/dist/createPopoverSelect.svelte.js +6 -12
  77. package/dist/createStepper.d.ts +44 -0
  78. package/dist/createStepper.js +26 -0
  79. package/dist/createTooltip.svelte.d.ts +32 -0
  80. package/dist/createTooltip.svelte.js +61 -0
  81. package/dist/datetime/timezone.d.ts +36 -0
  82. package/dist/datetime/timezone.js +110 -0
  83. package/dist/dock-context.d.ts +52 -0
  84. package/dist/dock-context.js +1 -0
  85. package/dist/dock-manager-model.d.ts +136 -0
  86. package/dist/dock-manager-model.js +514 -0
  87. package/dist/dock-model.d.ts +105 -0
  88. package/dist/dock-model.js +288 -0
  89. package/dist/dock-popout.d.ts +30 -0
  90. package/dist/dock-popout.js +81 -0
  91. package/dist/editor-contract.d.ts +2 -0
  92. package/dist/filter-operators.d.ts +6 -0
  93. package/dist/filter-operators.js +35 -2
  94. package/dist/filtering/excel-filters.d.ts +18 -1
  95. package/dist/filtering/excel-filters.js +56 -0
  96. package/dist/form-field.d.ts +73 -0
  97. package/dist/form-field.js +15 -0
  98. package/dist/index.d.ts +25 -5
  99. package/dist/index.js +40 -4
  100. package/dist/list-nav.d.ts +18 -0
  101. package/dist/list-nav.js +30 -0
  102. package/dist/list-option.d.ts +2 -0
  103. package/dist/menu-item.d.ts +20 -0
  104. package/dist/menu-item.js +1 -0
  105. package/dist/menus.d.ts +5 -0
  106. package/dist/menus.js +68 -16
  107. package/dist/panel-resize.d.ts +25 -0
  108. package/dist/panel-resize.js +40 -0
  109. package/dist/popover.d.ts +29 -3
  110. package/dist/popover.js +22 -6
  111. package/dist/recurrence.d.ts +33 -7
  112. package/dist/recurrence.js +151 -13
  113. package/dist/row-drag.d.ts +8 -0
  114. package/dist/row-drag.js +11 -0
  115. package/dist/scheduler-ical.d.ts +30 -0
  116. package/dist/scheduler-ical.js +180 -0
  117. package/dist/scheduler-model.d.ts +269 -0
  118. package/dist/scheduler-model.js +602 -0
  119. package/dist/scheduler-view.svelte.d.ts +22 -0
  120. package/dist/scheduler-view.svelte.js +13 -0
  121. package/dist/spreadsheet.js +20 -12
  122. package/dist/ui-app.types.d.ts +3 -0
  123. package/package.json +5 -1
  124. package/src/DockNodeView.svelte +306 -0
  125. package/src/DockPopoutHost.svelte +17 -0
  126. package/src/GridMenus.svelte +83 -3
  127. package/src/SvAutoComplete.svelte +5 -3
  128. package/src/SvCarousel.svelte +20 -42
  129. package/src/SvCarousel.test.ts +68 -0
  130. package/src/SvColorInput.svelte +4 -3
  131. package/src/SvComboBox.svelte +7 -3
  132. package/src/SvCommand.svelte +28 -85
  133. package/src/SvCommand.test.ts +78 -0
  134. package/src/SvCountryInput.svelte +1 -1
  135. package/src/SvDateRangeInput.svelte +1 -1
  136. package/src/SvDateTimePicker.svelte +9 -3
  137. package/src/SvDockLayout.svelte +244 -0
  138. package/src/SvDockManager.svelte +922 -0
  139. package/src/SvDrawer.svelte +69 -47
  140. package/src/SvDropDownList.svelte +85 -13
  141. package/src/SvField.svelte +21 -4
  142. package/src/SvForm.svelte +201 -91
  143. package/src/SvForm.test.ts +299 -0
  144. package/src/SvGrid.controller.svelte.ts +152 -28
  145. package/src/SvGrid.css +193 -5
  146. package/src/SvGrid.svelte +357 -12
  147. package/src/SvGrid.types.ts +463 -1
  148. package/src/SvGridDropdown.svelte +8 -1
  149. package/src/SvGridSelect.svelte +1 -0
  150. package/src/SvListBox.svelte +36 -14
  151. package/src/SvMaskedInput.svelte +4 -3
  152. package/src/SvMenu.svelte +1 -1
  153. package/src/SvMenu.test.ts +24 -0
  154. package/src/SvMenuList.svelte +23 -71
  155. package/src/SvModal.svelte +12 -29
  156. package/src/SvModal.test.ts +82 -0
  157. package/src/SvMultiSelect.svelte +1 -0
  158. package/src/SvNumberInput.svelte +4 -3
  159. package/src/SvPagination.svelte +18 -18
  160. package/src/SvPasswordInput.svelte +3 -2
  161. package/src/SvPhoneInput.svelte +3 -2
  162. package/src/SvPopover.svelte +1 -1
  163. package/src/SvStepper.svelte +6 -9
  164. package/src/SvTagsInput.svelte +3 -2
  165. package/src/SvTooltip.svelte +12 -23
  166. package/src/SvTooltip.test.ts +73 -0
  167. package/src/SvTour.svelte +32 -4
  168. package/src/SvTreeSelect.svelte +1 -0
  169. package/src/build-api.ts +74 -51
  170. package/src/columns.ts +5 -0
  171. package/src/conditional-formatting.test.ts +104 -0
  172. package/src/conditional-formatting.ts +176 -19
  173. package/src/createCarousel.svelte.ts +104 -0
  174. package/src/createCommand.svelte.ts +125 -0
  175. package/src/createForm.svelte.ts +333 -0
  176. package/src/createMenu.svelte.ts +202 -0
  177. package/src/createOverlay.svelte.ts +101 -0
  178. package/src/createPagination.test.ts +65 -0
  179. package/src/createPagination.ts +84 -0
  180. package/src/createPopoverSelect.svelte.ts +5 -9
  181. package/src/createStepper.test.ts +62 -0
  182. package/src/createStepper.ts +61 -0
  183. package/src/createTooltip.svelte.ts +86 -0
  184. package/src/datetime/timezone.test.ts +69 -0
  185. package/src/datetime/timezone.ts +134 -0
  186. package/src/dock-context.ts +47 -0
  187. package/src/dock-manager-model.test.ts +275 -0
  188. package/src/dock-manager-model.ts +593 -0
  189. package/src/dock-model.test.ts +163 -0
  190. package/src/dock-model.ts +368 -0
  191. package/src/dock-popout.ts +94 -0
  192. package/src/editor-contract.ts +2 -0
  193. package/src/filter-operators.test.ts +59 -0
  194. package/src/filter-operators.ts +35 -2
  195. package/src/filtering/excel-filters.test.ts +86 -1
  196. package/src/filtering/excel-filters.ts +65 -0
  197. package/src/form-field.ts +87 -0
  198. package/src/index.ts +158 -4
  199. package/src/list-nav.test.ts +49 -0
  200. package/src/list-nav.ts +29 -0
  201. package/src/list-option.ts +2 -0
  202. package/src/menu-item.ts +21 -0
  203. package/src/menus.test.ts +48 -0
  204. package/src/menus.ts +73 -16
  205. package/src/panel-resize.ts +52 -0
  206. package/src/popover.bounds.test.ts +93 -0
  207. package/src/popover.ts +51 -7
  208. package/src/recurrence.test.ts +88 -1
  209. package/src/recurrence.ts +166 -15
  210. package/src/row-drag.ts +10 -0
  211. package/src/scheduler-ical.test.ts +68 -0
  212. package/src/scheduler-ical.ts +180 -0
  213. package/src/scheduler-model.test.ts +546 -0
  214. package/src/scheduler-model.ts +853 -0
  215. package/src/scheduler-view.svelte.ts +33 -0
  216. package/src/spreadsheet.ts +20 -12
  217. package/src/svgrid.effect-driven-filter.svelte.test.ts +116 -0
  218. package/src/ui-app.types.ts +3 -0
  219. package/src/virtual.test.ts +6 -4
@@ -0,0 +1,33 @@
1
+ /**
2
+ * scheduler-view registry - the injection seam for the calendar / scheduler
3
+ * *view of the grid*. Setting the `scheduler` prop on `<SvGrid>` switches the
4
+ * table for a calendar, but the renderer itself ships in `@svgrid/enterprise`
5
+ * (a paid feature). This tiny reactive holder lets enterprise register that
6
+ * renderer at import time; the grid looks it up and mounts it, or shows an
7
+ * upsell placeholder when it is absent. Mirrors the `editor-registry` pattern.
8
+ *
9
+ * ```ts
10
+ * // in @svgrid/enterprise, at module load:
11
+ * import { registerSchedulerView } from '@svgrid/grid'
12
+ * import SvGridScheduler from './SvGridScheduler.svelte'
13
+ * registerSchedulerView(SvGridScheduler)
14
+ * ```
15
+ */
16
+ import type { Component } from 'svelte'
17
+
18
+ let renderer = $state<Component<any> | null>(null)
19
+
20
+ /** Register the component that renders `scheduler` mode. Enterprise calls this. */
21
+ export function registerSchedulerView(component: Component<any>): void {
22
+ renderer = component
23
+ }
24
+
25
+ /** The registered scheduler renderer, or null when enterprise is not installed. */
26
+ export function getSchedulerView(): Component<any> | null {
27
+ return renderer
28
+ }
29
+
30
+ /** Whether a scheduler renderer has been registered. */
31
+ export function hasSchedulerView(): boolean {
32
+ return renderer != null
33
+ }
@@ -265,6 +265,18 @@ function apply(root: HTMLElement, opts: SpreadsheetActionOptions): void {
265
265
  export function spreadsheetLayout(node: HTMLElement, opts: SpreadsheetActionOptions) {
266
266
  let current = opts
267
267
  let frame = 0
268
+ const OBSERVE_OPTIONS: MutationObserverInit = {
269
+ childList: true,
270
+ subtree: true,
271
+ attributes: true,
272
+ attributeFilter: [
273
+ 'data-svgrid-row', 'data-col-id', 'style',
274
+ // Watch selection-range attrs so we can transfer them from
275
+ // hidden covered cells to merge origins.
276
+ 'data-range-top', 'data-range-bottom', 'data-range-left',
277
+ 'data-range-right', 'data-selected-range',
278
+ ],
279
+ }
268
280
  // The MutationObserver fires for EVERY DOM change inside the grid -
269
281
  // batch them into one rAF so a big virtualization scroll doesn't run
270
282
  // apply() dozens of times in a tick.
@@ -272,22 +284,18 @@ export function spreadsheetLayout(node: HTMLElement, opts: SpreadsheetActionOpti
272
284
  if (frame) return
273
285
  frame = requestAnimationFrame(() => {
274
286
  frame = 0
287
+ // Stop observing while apply() mutates the DOM itself (it writes cell
288
+ // `style.display`/`position` and appends overlay children). Otherwise
289
+ // those writes re-trigger this observer and loop at 60fps on merged
290
+ // cells (#50). A full apply() recomputes from the live DOM, so discarding
291
+ // the queued records here loses nothing.
292
+ observer.disconnect()
275
293
  apply(node, current)
294
+ observer.observe(node, OBSERVE_OPTIONS)
276
295
  })
277
296
  }
278
297
  const observer = new MutationObserver(schedule)
279
- observer.observe(node, {
280
- childList: true,
281
- subtree: true,
282
- attributes: true,
283
- attributeFilter: [
284
- 'data-svgrid-row', 'data-col-id', 'style',
285
- // Watch selection-range attrs so we can transfer them from
286
- // hidden covered cells to merge origins.
287
- 'data-range-top', 'data-range-bottom', 'data-range-left',
288
- 'data-range-right', 'data-selected-range',
289
- ],
290
- })
298
+ observer.observe(node, OBSERVE_OPTIONS)
291
299
  // First-paint pass: schedule the same way so we don't run before the
292
300
  // grid's initial render landed.
293
301
  schedule()
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Regression: driving the imperative filter API from a reactive `$effect`.
3
+ *
4
+ * The filter mutators (`setFilter` / `clearFilter` / `setFacetFilter` /
5
+ * `clearAllFilters`) do a read-modify-write on the grid's filter state
6
+ * (e.g. `{ ...filterMenuValues, [id]: ... }`). If those internal reads are
7
+ * captured as dependencies of a caller's `$effect`, the subsequent write
8
+ * re-dirties the effect and it loops forever (Svelte throws
9
+ * `effect_update_depth_exceeded`). This bit demo 141, whose "top performers
10
+ * only" toggle drove `api.setFilter` from an `$effect` and hung the grid for
11
+ * ~7s before crashing.
12
+ *
13
+ * The fix wraps those mutators in `untrack`, so this pattern must now settle
14
+ * to a stable filtered row set in a bounded number of flushes.
15
+ */
16
+
17
+ import { describe, expect, it } from 'vitest'
18
+ import { mount, unmount, flushSync } from 'svelte'
19
+ import SvGrid from './SvGrid.svelte'
20
+ import {
21
+ columnFilteringFeature,
22
+ createCoreRowModel,
23
+ createFilteredRowModel,
24
+ createSortedRowModel,
25
+ rowSortingFeature,
26
+ sortFns,
27
+ tableFeatures,
28
+ } from './index'
29
+ import type { ColumnDef, SvGridApi } from './index'
30
+
31
+ type Row = { id: number; name: string; team: string; salary: number }
32
+
33
+ const features = tableFeatures({ columnFilteringFeature, rowSortingFeature })
34
+
35
+ const sampleRows: Row[] = [
36
+ { id: 1, name: 'Ada Lovelace', team: 'Research', salary: 142_000 },
37
+ { id: 2, name: 'Grace Hopper', team: 'Compilers', salary: 158_000 },
38
+ { id: 3, name: 'Alan Turing', team: 'Research', salary: 138_000 },
39
+ { id: 4, name: 'Margaret Hamilton', team: 'Apollo', salary: 165_000 },
40
+ { id: 5, name: 'Linus Torvalds', team: 'Kernel', salary: 175_000 },
41
+ ]
42
+
43
+ const sampleColumns: ColumnDef<typeof features, Row>[] = [
44
+ { field: 'name', header: 'Name', width: 200 },
45
+ { field: 'team', header: 'Team', width: 160 },
46
+ { field: 'salary', header: 'Salary', width: 140 },
47
+ ]
48
+
49
+ function mountGrid(): { api: SvGridApi<typeof features, Row>; destroy: () => void } {
50
+ const target = document.createElement('div')
51
+ document.body.appendChild(target)
52
+ let capturedApi: SvGridApi<typeof features, Row> | null = null
53
+ const app = mount(SvGrid, {
54
+ target,
55
+ props: {
56
+ data: sampleRows,
57
+ columns: sampleColumns,
58
+ features,
59
+ _rowModels: {
60
+ coreRowModel: createCoreRowModel(),
61
+ filteredRowModel: createFilteredRowModel(),
62
+ sortedRowModel: createSortedRowModel(sortFns),
63
+ },
64
+ rowHeight: 36,
65
+ containerHeight: 480,
66
+ virtualization: false,
67
+ onApiReady(api: SvGridApi<typeof features, Row>) {
68
+ capturedApi = api
69
+ },
70
+ } as any,
71
+ })
72
+ flushSync()
73
+ if (!capturedApi) throw new Error('onApiReady never fired')
74
+ return { api: capturedApi, destroy: () => { unmount(app); target.remove() } }
75
+ }
76
+
77
+ describe('imperative filter API driven from a reactive $effect', () => {
78
+ it('setFilter/clearFilter inside an $effect settle without an update loop', () => {
79
+ const { api, destroy } = mountGrid()
80
+ // Independent root so we own the effect lifecycle and can flush it.
81
+ let topOnly = $state(false)
82
+ let runs = 0
83
+ const stop = $effect.root(() => {
84
+ $effect(() => {
85
+ runs += 1
86
+ // Reads happen inside the mutators; the guard against looping is that
87
+ // those reads are untracked, so this effect must NOT re-run per write.
88
+ if (topOnly) api.setFilter('salary', { operator: 'greaterThan', value: '150000' })
89
+ else api.clearFilter('salary')
90
+ })
91
+ })
92
+ try {
93
+ flushSync()
94
+ // Turning the filter on must narrow the set and NOT blow the update depth.
95
+ topOnly = true
96
+ // If the mutator's reads leaked, this flush would throw
97
+ // effect_update_depth_exceeded.
98
+ expect(() => flushSync()).not.toThrow()
99
+
100
+ const displayed = api.getDisplayedRows()
101
+ expect(displayed.map((r) => (r as Row).id).sort()).toEqual([2, 4, 5])
102
+
103
+ // The effect ran a bounded number of times: once on mount + once for the
104
+ // toggle. A leaked dependency would push this into the hundreds.
105
+ expect(runs).toBeLessThanOrEqual(3)
106
+
107
+ // Toggling back off clears cleanly too.
108
+ topOnly = false
109
+ expect(() => flushSync()).not.toThrow()
110
+ expect(api.getDisplayedRows().length).toBe(sampleRows.length)
111
+ } finally {
112
+ stop()
113
+ destroy()
114
+ }
115
+ })
116
+ })
@@ -72,6 +72,9 @@ export type TourStep = {
72
72
  target?: string | HTMLElement
73
73
  title?: string
74
74
  content: string
75
+ /** Optional illustration shown as a banner above the title. A URL / data-URI
76
+ * (rendered as an `<img>`) or inline SVG/HTML markup (author-controlled). */
77
+ image?: string
75
78
  }
76
79
 
77
80
  /** One entry in a timeline. */
@@ -48,10 +48,12 @@ describe('SvListBox virtual', () => {
48
48
  const rendered = target.querySelectorAll('.sv-listbox__opt').length
49
49
  expect(rendered).toBeGreaterThan(0)
50
50
  expect(rendered).toBeLessThan(60) // nowhere near 5000
51
- // A fixed-height sizer keeps the scrollbar honest.
52
- const sizer = target.querySelector<HTMLElement>('.sv-listbox__sizer')!
53
- expect(sizer).not.toBeNull()
54
- expect(sizer.style.height).toBe(`${5000 * 32}px`)
51
+ // Top + bottom spacers + the windowed rows reserve the full scroll height,
52
+ // keeping the scrollbar honest without positioning rows out of flow.
53
+ const spacers = target.querySelectorAll<HTMLElement>('.sv-listbox__spacer')
54
+ expect(spacers.length).toBe(2)
55
+ const spacerPx = [...spacers].reduce((sum, s) => sum + parseFloat(s.style.height || '0'), 0)
56
+ expect(spacerPx + rendered * 32).toBe(5000 * 32)
55
57
  } finally { unmount(app); target.remove() }
56
58
  })
57
59