@svgrid/grid 2.2.0 → 2.2.2

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 (114) hide show
  1. package/dist/FlexRender.svelte +96 -96
  2. package/dist/GridFooter.svelte +178 -178
  3. package/dist/SvCarousel.svelte +141 -141
  4. package/dist/SvColorInput.svelte +187 -187
  5. package/dist/SvContextMenu.svelte +116 -116
  6. package/dist/SvDrawer.svelte +250 -250
  7. package/dist/SvGrid.svelte +3131 -3131
  8. package/dist/SvGridBoard.svelte +2339 -2339
  9. package/dist/SvGridChart.svelte +1724 -1724
  10. package/dist/SvGridChartPanel.svelte +485 -485
  11. package/dist/SvGridDropdown.svelte +689 -689
  12. package/dist/SvGroupCell.svelte +109 -109
  13. package/dist/SvMenu.svelte +124 -124
  14. package/dist/SvMenuList.svelte +117 -117
  15. package/dist/SvNumberInput.svelte +131 -131
  16. package/dist/SvOtpInput.svelte +158 -158
  17. package/dist/SvPopover.svelte +139 -139
  18. package/dist/SvRowGroupPanel.svelte +170 -170
  19. package/dist/SvScrollArea.svelte +61 -61
  20. package/dist/SvTooltip.svelte +127 -127
  21. package/dist/SvTour.svelte +204 -204
  22. package/dist/SvTree.svelte +377 -377
  23. package/dist/SvTreeSelect.svelte +230 -230
  24. package/dist/cdn/svgrid.js +8 -8
  25. package/dist/cdn/svgrid.svelte-external.js +8 -8
  26. package/dist/chart-export.js +8 -8
  27. package/package.json +10 -10
  28. package/src/FlexRender.svelte +96 -96
  29. package/src/GridFooter.svelte +178 -178
  30. package/src/SvCarousel.svelte +141 -141
  31. package/src/SvColorInput.svelte +187 -187
  32. package/src/SvContextMenu.svelte +116 -116
  33. package/src/SvDrawer.svelte +250 -250
  34. package/src/SvGrid.controller.svelte.ts +3158 -3158
  35. package/src/SvGrid.svelte +3131 -3131
  36. package/src/SvGrid.types.ts +1489 -1489
  37. package/src/SvGridBoard.svelte +2339 -2339
  38. package/src/SvGridChart.svelte +1724 -1724
  39. package/src/SvGridChartPanel.svelte +485 -485
  40. package/src/SvGridDropdown.svelte +689 -689
  41. package/src/SvGroupCell.svelte +109 -109
  42. package/src/SvMenu.svelte +124 -124
  43. package/src/SvMenu.test.ts +97 -97
  44. package/src/SvMenuList.svelte +117 -117
  45. package/src/SvNumberInput.svelte +131 -131
  46. package/src/SvOtpInput.svelte +158 -158
  47. package/src/SvPopover.svelte +139 -139
  48. package/src/SvRowGroupPanel.svelte +170 -170
  49. package/src/SvScrollArea.svelte +61 -61
  50. package/src/SvTooltip.svelte +127 -127
  51. package/src/SvTour.svelte +204 -204
  52. package/src/SvTree.svelte +377 -377
  53. package/src/SvTreeSelect.svelte +230 -230
  54. package/src/a11y.contract.test.ts +49 -49
  55. package/src/a11y.test.ts +59 -59
  56. package/src/a11y.ts +61 -61
  57. package/src/build-api.ts +870 -870
  58. package/src/cell-formatting.ts +169 -169
  59. package/src/cell-render.ts +469 -469
  60. package/src/chart-export.ts +201 -201
  61. package/src/chart.ts +2296 -2296
  62. package/src/collaboration.test.ts +104 -104
  63. package/src/collaboration.ts +167 -167
  64. package/src/core.performance.test.ts +30 -30
  65. package/src/core.ts +1111 -1111
  66. package/src/createAutocomplete.svelte.ts +132 -132
  67. package/src/createCombobox.svelte.ts +179 -179
  68. package/src/createCountryInput.svelte.ts +157 -157
  69. package/src/createDropdownList.svelte.ts +162 -162
  70. package/src/createGrid.svelte.ts +42 -42
  71. package/src/createGrid.test.ts +10 -10
  72. package/src/createGridState.svelte.ts +17 -17
  73. package/src/createMenu.svelte.ts +202 -202
  74. package/src/createPopoverSelect.svelte.ts +206 -206
  75. package/src/createTree.svelte.ts +319 -319
  76. package/src/editing.test.ts +859 -859
  77. package/src/editing.ts +675 -675
  78. package/src/export-data-api.test.ts +126 -126
  79. package/src/export-format.test.ts +107 -107
  80. package/src/export-format.ts +598 -598
  81. package/src/flex-render.ts +3 -3
  82. package/src/index.ts +623 -623
  83. package/src/keyboard.test.ts +59 -59
  84. package/src/keyboard.ts +97 -97
  85. package/src/list-nav.test.ts +49 -49
  86. package/src/list-nav.ts +29 -29
  87. package/src/menus.ts +639 -639
  88. package/src/merge-objects.ts +48 -48
  89. package/src/overlays.test.ts +90 -90
  90. package/src/render-component.ts +28 -28
  91. package/src/selection.test.ts +754 -754
  92. package/src/selection.ts +600 -600
  93. package/src/server-data-source.test.ts +289 -289
  94. package/src/server-data-source.ts +413 -413
  95. package/src/sparkline.test.ts +68 -68
  96. package/src/sparkline.ts +169 -169
  97. package/src/spreadsheet.test.ts +489 -489
  98. package/src/spreadsheet.ts +312 -312
  99. package/src/static-functions.ts +11 -11
  100. package/src/subscribe.ts +38 -38
  101. package/src/svgrid-wrapper.types.ts +501 -501
  102. package/src/svgrid.behavior.test.ts +706 -706
  103. package/src/svgrid.charting.test.ts +527 -527
  104. package/src/svgrid.features.test.ts +157 -157
  105. package/src/svgrid.new-features.wrapper.test.ts +251 -251
  106. package/src/svgrid.wrapper.test.ts +40 -40
  107. package/src/test-setup.ts +62 -62
  108. package/src/themes/index.ts +159 -159
  109. package/src/virtualization/column-virtualizer.test.ts +27 -27
  110. package/src/virtualization/column-virtualizer.ts +30 -30
  111. package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
  112. package/src/virtualization/types.ts +30 -30
  113. package/src/virtualization/virtualizer.test.ts +47 -47
  114. package/src/virtualization/virtualizer.ts +296 -296
@@ -1,206 +1,206 @@
1
- /**
2
- * createPopoverSelect - the shared HEADLESS engine behind the kit's dropdown
3
- * selects (SvMultiSelect, SvTreeSelect, SvGridSelect and any future one). It
4
- * owns the parts every anchored-panel select repeats: open/close state, the
5
- * `anchoredRect` positioning (reposition on scroll/resize), outside/Escape
6
- * dismissal via the shared layer stack, a roving `active` index over the visible
7
- * items, the WAI-ARIA combobox wiring (`aria-expanded` / `aria-controls` /
8
- * `aria-activedescendant` + stable option ids), and focus management (focus
9
- * moves into the panel on open, back to the trigger on close).
10
- *
11
- * The component owns its own `bind:this` refs and item rendering; it passes the
12
- * refs in as getters and spreads the returned prop-getters onto its trigger,
13
- * panel and items. Runes-based, like `createListbox`.
14
- */
15
- import { anchoredRect, type AnchoredRect } from './popover'
16
- import { createDismissableLayer } from './a11y/dismissable'
17
- import { enabledIndices as enabledIndicesOf, wrapMove } from './list-nav'
18
-
19
- let uidSeq = 0
20
-
21
- export type PopoverSelectConfig = {
22
- /** Number of currently-visible/navigable items (post-filter). */
23
- itemCount: () => number
24
- /** Whether item `i` is disabled (skipped by roving, not selectable). */
25
- disabled?: (index: number) => boolean
26
- /** Commit the item at `index` (Enter or a click the component forwards). */
27
- onSelect: (index: number) => void
28
- /** Notified whenever the open state changes. */
29
- onOpenChange?: (open: boolean) => void
30
- /** The trigger element (component owns `bind:this`, passes a getter). */
31
- getTrigger: () => HTMLElement | null
32
- /** The panel element. */
33
- getPanel: () => HTMLElement | null
34
- /** Element focused when the panel opens (e.g. a search input). Defaults to the
35
- * panel itself. Its id-holder also carries `aria-activedescendant`. */
36
- getInitialFocus?: () => HTMLElement | null
37
- /** Estimated panel height, for the flip-up decision. Default 280. */
38
- estimatedHeight?: () => number
39
- /** Minimum panel width. Default 200. */
40
- minWidth?: () => number
41
- /** Close after a selection (single-select). Default true. */
42
- closeOnSelect?: () => boolean
43
- /** Active index to start on when opening (e.g. the selected row). Default 0. */
44
- initialActive?: () => number
45
- /** Handle a key the core doesn't (e.g. tree ArrowLeft/Right). Return true if
46
- * handled to stop the core's default handling. */
47
- onExtraKey?: (event: KeyboardEvent, active: number) => boolean
48
- idPrefix?: string
49
- }
50
-
51
- export function createPopoverSelect(config: PopoverSelectConfig) {
52
- const id = `${config.idPrefix ?? 'sv-pop'}-${uidSeq++}`
53
- const panelId = `${id}-panel`
54
- const optionId = (i: number) => `${id}-opt-${i}`
55
-
56
- let open = $state(false)
57
- let active = $state(0)
58
- let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false, maxHeight: 0, availHeight: 0 })
59
-
60
- const count = () => config.itemCount()
61
- const isDisabled = (i: number) => config.disabled?.(i) ?? false
62
-
63
- function enabledIndices(): number[] {
64
- return enabledIndicesOf(count(), isDisabled)
65
- }
66
- function clampActive(i: number): number {
67
- const n = count()
68
- return n <= 0 ? 0 : Math.max(0, Math.min(i, n - 1))
69
- }
70
-
71
- function setOpen(v: boolean) {
72
- if (v === open) return
73
- open = v
74
- config.onOpenChange?.(v)
75
- }
76
- function openPanel() {
77
- active = clampActive(config.initialActive?.() ?? 0)
78
- setOpen(true)
79
- }
80
- function closePanel() {
81
- setOpen(false)
82
- config.getTrigger()?.focus()
83
- }
84
- function toggle() {
85
- open ? setOpen(false) : openPanel()
86
- }
87
-
88
- function move(delta: number) {
89
- const next = wrapMove(enabledIndices(), active, delta)
90
- if (next >= 0) active = next
91
- }
92
- function first() { active = enabledIndices()[0] ?? 0 }
93
- function last() { active = enabledIndices().at(-1) ?? 0 }
94
- function selectActive() {
95
- if (count() === 0 || isDisabled(active)) return
96
- config.onSelect(active)
97
- if (config.closeOnSelect?.() ?? true) closePanel()
98
- }
99
-
100
- function updatePos() {
101
- const t = config.getTrigger()
102
- if (!t) return
103
- rect = anchoredRect(t.getBoundingClientRect(), {
104
- estimatedHeight: config.estimatedHeight?.() ?? 280,
105
- minWidth: config.minWidth?.() ?? 200,
106
- })
107
- }
108
-
109
- // While open: position, focus into the panel, reposition on scroll/resize, and
110
- // register a dismissable layer (Escape / outside-click, top-of-stack first).
111
- $effect(() => {
112
- if (!open) return
113
- updatePos()
114
- const initial = config.getInitialFocus?.() ?? config.getPanel()
115
- queueMicrotask(() => initial?.focus())
116
- const rp = () => updatePos()
117
- window.addEventListener('scroll', rp, true)
118
- window.addEventListener('resize', rp)
119
- const layer = createDismissableLayer({
120
- element: () => [config.getTrigger(), config.getPanel()],
121
- onDismiss: () => setOpen(false),
122
- })
123
- layer.activate()
124
- return () => {
125
- window.removeEventListener('scroll', rp, true)
126
- window.removeEventListener('resize', rp)
127
- layer.release()
128
- }
129
- })
130
-
131
- function onTriggerKeydown(e: KeyboardEvent) {
132
- if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
133
- e.preventDefault()
134
- openPanel()
135
- } else if (e.key === 'ArrowUp') {
136
- e.preventDefault()
137
- openPanel()
138
- last()
139
- } else if (e.key === 'Escape') {
140
- setOpen(false)
141
- }
142
- }
143
- function onPanelKeydown(e: KeyboardEvent) {
144
- if (config.onExtraKey?.(e, active)) return
145
- switch (e.key) {
146
- case 'ArrowDown': e.preventDefault(); move(1); break
147
- case 'ArrowUp': e.preventDefault(); move(-1); break
148
- case 'Home': e.preventDefault(); first(); break
149
- case 'End': e.preventDefault(); last(); break
150
- case 'Enter': e.preventDefault(); selectActive(); break
151
- case 'Escape': e.preventDefault(); closePanel(); break
152
- case 'Tab': setOpen(false); break
153
- }
154
- }
155
-
156
- /** The current active-descendant id (for the focused element), or undefined. */
157
- const activeDescendant = () => (open && count() > 0 ? optionId(active) : undefined)
158
-
159
- return {
160
- get open() { return open },
161
- set open(v: boolean) { setOpen(v) },
162
- get active() { return active },
163
- set active(i: number) { active = clampActive(i) },
164
- get rect() { return rect },
165
- /** Stable id of the panel element (for aria-controls / labelling). */
166
- panelId,
167
- /** Stable id of the option at `i` (for aria-activedescendant + item id). */
168
- optionId,
169
- activeDescendant,
170
- openPanel,
171
- closePanel,
172
- toggle,
173
- move,
174
- first,
175
- last,
176
- selectActive,
177
- updatePos,
178
- onPanelKeydown,
179
- /** True when item `i` is the roving-active one. */
180
- isActive: (i: number) => i === active,
181
- /** Spread onto the trigger button. Pass the ARIA popup type the panel
182
- * exposes (mirrors `focusOwnerProps(role)`); defaults to 'listbox' for
183
- * the classic select panels. */
184
- triggerProps: (haspopup: 'listbox' | 'tree' | 'grid' = 'listbox') => ({
185
- 'aria-haspopup': haspopup,
186
- 'aria-expanded': open,
187
- 'aria-controls': open ? panelId : undefined,
188
- onkeydown: onTriggerKeydown,
189
- }),
190
- /** Attributes for the element that OWNS focus + activedescendant (the panel,
191
- * or a search input). Pass the ARIA role the panel should expose. */
192
- focusOwnerProps: (role: 'listbox' | 'tree' | 'grid' = 'listbox') => ({
193
- role,
194
- 'aria-activedescendant': activeDescendant(),
195
- onkeydown: onPanelKeydown,
196
- }),
197
- /** Spread onto the item element at `i`. */
198
- itemProps: (i: number) => ({
199
- id: optionId(i),
200
- 'data-active': i === active ? '' : undefined,
201
- onpointermove: () => { if (!isDisabled(i)) active = i },
202
- }),
203
- }
204
- }
205
-
206
- export type PopoverSelect = ReturnType<typeof createPopoverSelect>
1
+ /**
2
+ * createPopoverSelect - the shared HEADLESS engine behind the kit's dropdown
3
+ * selects (SvMultiSelect, SvTreeSelect, SvGridSelect and any future one). It
4
+ * owns the parts every anchored-panel select repeats: open/close state, the
5
+ * `anchoredRect` positioning (reposition on scroll/resize), outside/Escape
6
+ * dismissal via the shared layer stack, a roving `active` index over the visible
7
+ * items, the WAI-ARIA combobox wiring (`aria-expanded` / `aria-controls` /
8
+ * `aria-activedescendant` + stable option ids), and focus management (focus
9
+ * moves into the panel on open, back to the trigger on close).
10
+ *
11
+ * The component owns its own `bind:this` refs and item rendering; it passes the
12
+ * refs in as getters and spreads the returned prop-getters onto its trigger,
13
+ * panel and items. Runes-based, like `createListbox`.
14
+ */
15
+ import { anchoredRect, type AnchoredRect } from './popover'
16
+ import { createDismissableLayer } from './a11y/dismissable'
17
+ import { enabledIndices as enabledIndicesOf, wrapMove } from './list-nav'
18
+
19
+ let uidSeq = 0
20
+
21
+ export type PopoverSelectConfig = {
22
+ /** Number of currently-visible/navigable items (post-filter). */
23
+ itemCount: () => number
24
+ /** Whether item `i` is disabled (skipped by roving, not selectable). */
25
+ disabled?: (index: number) => boolean
26
+ /** Commit the item at `index` (Enter or a click the component forwards). */
27
+ onSelect: (index: number) => void
28
+ /** Notified whenever the open state changes. */
29
+ onOpenChange?: (open: boolean) => void
30
+ /** The trigger element (component owns `bind:this`, passes a getter). */
31
+ getTrigger: () => HTMLElement | null
32
+ /** The panel element. */
33
+ getPanel: () => HTMLElement | null
34
+ /** Element focused when the panel opens (e.g. a search input). Defaults to the
35
+ * panel itself. Its id-holder also carries `aria-activedescendant`. */
36
+ getInitialFocus?: () => HTMLElement | null
37
+ /** Estimated panel height, for the flip-up decision. Default 280. */
38
+ estimatedHeight?: () => number
39
+ /** Minimum panel width. Default 200. */
40
+ minWidth?: () => number
41
+ /** Close after a selection (single-select). Default true. */
42
+ closeOnSelect?: () => boolean
43
+ /** Active index to start on when opening (e.g. the selected row). Default 0. */
44
+ initialActive?: () => number
45
+ /** Handle a key the core doesn't (e.g. tree ArrowLeft/Right). Return true if
46
+ * handled to stop the core's default handling. */
47
+ onExtraKey?: (event: KeyboardEvent, active: number) => boolean
48
+ idPrefix?: string
49
+ }
50
+
51
+ export function createPopoverSelect(config: PopoverSelectConfig) {
52
+ const id = `${config.idPrefix ?? 'sv-pop'}-${uidSeq++}`
53
+ const panelId = `${id}-panel`
54
+ const optionId = (i: number) => `${id}-opt-${i}`
55
+
56
+ let open = $state(false)
57
+ let active = $state(0)
58
+ let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false, maxHeight: 0, availHeight: 0 })
59
+
60
+ const count = () => config.itemCount()
61
+ const isDisabled = (i: number) => config.disabled?.(i) ?? false
62
+
63
+ function enabledIndices(): number[] {
64
+ return enabledIndicesOf(count(), isDisabled)
65
+ }
66
+ function clampActive(i: number): number {
67
+ const n = count()
68
+ return n <= 0 ? 0 : Math.max(0, Math.min(i, n - 1))
69
+ }
70
+
71
+ function setOpen(v: boolean) {
72
+ if (v === open) return
73
+ open = v
74
+ config.onOpenChange?.(v)
75
+ }
76
+ function openPanel() {
77
+ active = clampActive(config.initialActive?.() ?? 0)
78
+ setOpen(true)
79
+ }
80
+ function closePanel() {
81
+ setOpen(false)
82
+ config.getTrigger()?.focus()
83
+ }
84
+ function toggle() {
85
+ open ? setOpen(false) : openPanel()
86
+ }
87
+
88
+ function move(delta: number) {
89
+ const next = wrapMove(enabledIndices(), active, delta)
90
+ if (next >= 0) active = next
91
+ }
92
+ function first() { active = enabledIndices()[0] ?? 0 }
93
+ function last() { active = enabledIndices().at(-1) ?? 0 }
94
+ function selectActive() {
95
+ if (count() === 0 || isDisabled(active)) return
96
+ config.onSelect(active)
97
+ if (config.closeOnSelect?.() ?? true) closePanel()
98
+ }
99
+
100
+ function updatePos() {
101
+ const t = config.getTrigger()
102
+ if (!t) return
103
+ rect = anchoredRect(t.getBoundingClientRect(), {
104
+ estimatedHeight: config.estimatedHeight?.() ?? 280,
105
+ minWidth: config.minWidth?.() ?? 200,
106
+ })
107
+ }
108
+
109
+ // While open: position, focus into the panel, reposition on scroll/resize, and
110
+ // register a dismissable layer (Escape / outside-click, top-of-stack first).
111
+ $effect(() => {
112
+ if (!open) return
113
+ updatePos()
114
+ const initial = config.getInitialFocus?.() ?? config.getPanel()
115
+ queueMicrotask(() => initial?.focus())
116
+ const rp = () => updatePos()
117
+ window.addEventListener('scroll', rp, true)
118
+ window.addEventListener('resize', rp)
119
+ const layer = createDismissableLayer({
120
+ element: () => [config.getTrigger(), config.getPanel()],
121
+ onDismiss: () => setOpen(false),
122
+ })
123
+ layer.activate()
124
+ return () => {
125
+ window.removeEventListener('scroll', rp, true)
126
+ window.removeEventListener('resize', rp)
127
+ layer.release()
128
+ }
129
+ })
130
+
131
+ function onTriggerKeydown(e: KeyboardEvent) {
132
+ if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
133
+ e.preventDefault()
134
+ openPanel()
135
+ } else if (e.key === 'ArrowUp') {
136
+ e.preventDefault()
137
+ openPanel()
138
+ last()
139
+ } else if (e.key === 'Escape') {
140
+ setOpen(false)
141
+ }
142
+ }
143
+ function onPanelKeydown(e: KeyboardEvent) {
144
+ if (config.onExtraKey?.(e, active)) return
145
+ switch (e.key) {
146
+ case 'ArrowDown': e.preventDefault(); move(1); break
147
+ case 'ArrowUp': e.preventDefault(); move(-1); break
148
+ case 'Home': e.preventDefault(); first(); break
149
+ case 'End': e.preventDefault(); last(); break
150
+ case 'Enter': e.preventDefault(); selectActive(); break
151
+ case 'Escape': e.preventDefault(); closePanel(); break
152
+ case 'Tab': setOpen(false); break
153
+ }
154
+ }
155
+
156
+ /** The current active-descendant id (for the focused element), or undefined. */
157
+ const activeDescendant = () => (open && count() > 0 ? optionId(active) : undefined)
158
+
159
+ return {
160
+ get open() { return open },
161
+ set open(v: boolean) { setOpen(v) },
162
+ get active() { return active },
163
+ set active(i: number) { active = clampActive(i) },
164
+ get rect() { return rect },
165
+ /** Stable id of the panel element (for aria-controls / labelling). */
166
+ panelId,
167
+ /** Stable id of the option at `i` (for aria-activedescendant + item id). */
168
+ optionId,
169
+ activeDescendant,
170
+ openPanel,
171
+ closePanel,
172
+ toggle,
173
+ move,
174
+ first,
175
+ last,
176
+ selectActive,
177
+ updatePos,
178
+ onPanelKeydown,
179
+ /** True when item `i` is the roving-active one. */
180
+ isActive: (i: number) => i === active,
181
+ /** Spread onto the trigger button. Pass the ARIA popup type the panel
182
+ * exposes (mirrors `focusOwnerProps(role)`); defaults to 'listbox' for
183
+ * the classic select panels. */
184
+ triggerProps: (haspopup: 'listbox' | 'tree' | 'grid' = 'listbox') => ({
185
+ 'aria-haspopup': haspopup,
186
+ 'aria-expanded': open,
187
+ 'aria-controls': open ? panelId : undefined,
188
+ onkeydown: onTriggerKeydown,
189
+ }),
190
+ /** Attributes for the element that OWNS focus + activedescendant (the panel,
191
+ * or a search input). Pass the ARIA role the panel should expose. */
192
+ focusOwnerProps: (role: 'listbox' | 'tree' | 'grid' = 'listbox') => ({
193
+ role,
194
+ 'aria-activedescendant': activeDescendant(),
195
+ onkeydown: onPanelKeydown,
196
+ }),
197
+ /** Spread onto the item element at `i`. */
198
+ itemProps: (i: number) => ({
199
+ id: optionId(i),
200
+ 'data-active': i === active ? '' : undefined,
201
+ onpointermove: () => { if (!isDisabled(i)) active = i },
202
+ }),
203
+ }
204
+ }
205
+
206
+ export type PopoverSelect = ReturnType<typeof createPopoverSelect>