@svgrid/grid 2.2.31 → 2.2.33

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 (196) hide show
  1. package/README.md +1 -1
  2. package/dist/DockNodeView.svelte +1 -1
  3. package/dist/GridFooter.svelte +0 -40
  4. package/dist/GridMenus.svelte +0 -77
  5. package/dist/SvAvatar.svelte +1 -1
  6. package/dist/SvCheckBox.svelte +2 -1
  7. package/dist/SvCircularProgress.svelte +1 -1
  8. package/dist/SvDateRangeInput.svelte +1 -1
  9. package/dist/SvGrid.controller.svelte.d.ts +20 -0
  10. package/dist/SvGrid.controller.svelte.js +358 -18
  11. package/dist/SvGrid.css +103 -3
  12. package/dist/SvGrid.svelte +164 -35
  13. package/dist/SvGrid.types.d.ts +81 -0
  14. package/dist/SvGridChart.svelte +2 -10
  15. package/dist/SvGridDropdown.svelte +38 -6
  16. package/dist/SvGridDropdown.svelte.d.ts +4 -0
  17. package/dist/SvGridSelect.svelte +12 -5
  18. package/dist/SvGridSelect.svelte.d.ts +6 -0
  19. package/dist/SvGroupCell.svelte +9 -2
  20. package/dist/SvKbd.svelte +1 -1
  21. package/dist/SvList.svelte +1 -1
  22. package/dist/SvMenubar.svelte +1 -1
  23. package/dist/SvMultiSelect.svelte +12 -4
  24. package/dist/SvMultiSelect.svelte.d.ts +6 -0
  25. package/dist/SvOtpInput.svelte +1 -1
  26. package/dist/SvPasswordInput.svelte +1 -1
  27. package/dist/SvProgress.svelte +1 -1
  28. package/dist/SvRating.svelte +1 -1
  29. package/dist/SvResult.svelte +1 -1
  30. package/dist/SvRichText.svelte +35 -16
  31. package/dist/SvRichText.svelte.d.ts +9 -0
  32. package/dist/SvSwitchButton.svelte +2 -1
  33. package/dist/SvToaster.svelte +1 -1
  34. package/dist/SvTour.svelte +4 -0
  35. package/dist/SvTree.svelte +9 -2
  36. package/dist/SvTree.svelte.d.ts +5 -0
  37. package/dist/SvTreeSelect.svelte +6 -4
  38. package/dist/build-api.js +16 -0
  39. package/dist/cdn/{GridMenus-Ds3OpzZT.js → GridMenus-B7XhhqdN.js} +34 -36
  40. package/dist/cdn/{GridMenus-Da_p7SKS.js → GridMenus-BxmIvCJh.js} +35 -37
  41. package/dist/cdn/{SvDateTimePicker-DB0aIEk8.js → SvDateTimePicker-ClqtVIj3.js} +2 -1
  42. package/dist/cdn/{SvDateTimePicker-B8GopjhC.js → SvDateTimePicker-o94wfSUa.js} +3 -2
  43. package/dist/cdn/{SvGridChart-BcLCYvw9.js → SvGridChart-Cr_90pPD.js} +1 -1
  44. package/dist/cdn/{SvGridChartPanel-DvjwxLvK.js → SvGridChartPanel-ICbxiUvK.js} +4 -4
  45. package/dist/cdn/{SvGridChartView-PwqI4s02.js → SvGridChartView-Bx42PMvf.js} +2 -2
  46. package/dist/cdn/{SvGridDropdown-hpnHvOGt.js → SvGridDropdown-CUOdm7YA.js} +93 -88
  47. package/dist/cdn/{SvGridDropdown-DjALc_4f.js → SvGridDropdown-D0VdjeR8.js} +129 -123
  48. package/dist/cdn/{disclose-version-DoD9AFD_.js → disclose-version-DljhZohK.js} +45 -45
  49. package/dist/cdn/{src-DYDCiC0D.js → src-erjBRYfX.js} +5952 -5615
  50. package/dist/cdn/{src-BLJfdyL0.js → src-ktbQVOk-.js} +4190 -3851
  51. package/dist/cdn/svgrid.js +9 -9
  52. package/dist/cdn/svgrid.svelte-external.js +8 -8
  53. package/dist/cell-render.d.ts +1 -0
  54. package/dist/cell-render.js +115 -1
  55. package/dist/cell-values.js +5 -0
  56. package/dist/chart.d.ts +2 -0
  57. package/dist/chart.js +1 -4
  58. package/dist/core.d.ts +80 -9
  59. package/dist/core.js +89 -0
  60. package/dist/createMenu.svelte.js +0 -1
  61. package/dist/createPopoverSelect.svelte.d.ts +2 -2
  62. package/dist/editing.js +1 -1
  63. package/dist/editor-contract.d.ts +12 -5
  64. package/dist/editor-registry.d.ts +18 -7
  65. package/dist/editor-registry.js +10 -1
  66. package/dist/export-format.js +3 -0
  67. package/dist/group-display.d.ts +39 -0
  68. package/dist/group-display.js +52 -0
  69. package/dist/index.d.ts +2 -2
  70. package/dist/index.js +2 -2
  71. package/dist/keyboard-handlers.js +16 -0
  72. package/dist/recurrence.js +3 -2
  73. package/dist/selection.js +0 -1
  74. package/dist/svgrid-wrapper.types.d.ts +8 -2
  75. package/dist/themes/index.d.ts +10 -0
  76. package/dist/themes/index.js +26 -0
  77. package/package.json +3 -2
  78. package/src/DockNodeView.svelte +1 -1
  79. package/src/GridFooter.svelte +0 -40
  80. package/src/GridMenus.svelte +0 -77
  81. package/src/SvAccordion.test.ts +5 -5
  82. package/src/SvAvatar.svelte +1 -1
  83. package/src/SvButtonGroup.test.ts +6 -6
  84. package/src/SvCalendar.test.ts +1 -1
  85. package/src/SvCheckBox.svelte +2 -1
  86. package/src/SvCircularProgress.svelte +1 -1
  87. package/src/SvDateRangeInput.svelte +1 -1
  88. package/src/SvFileUpload.test.ts +1 -1
  89. package/src/SvGrid.controller.svelte.ts +367 -22
  90. package/src/SvGrid.css +103 -3
  91. package/src/SvGrid.svelte +164 -35
  92. package/src/SvGrid.types.ts +81 -1
  93. package/src/SvGridChart.svelte +2 -10
  94. package/src/SvGridDropdown.svelte +38 -6
  95. package/src/SvGridSelect.svelte +12 -5
  96. package/src/SvGroupCell.svelte +9 -2
  97. package/src/SvKbd.svelte +1 -1
  98. package/src/SvList.svelte +1 -1
  99. package/src/SvMenubar.svelte +1 -1
  100. package/src/SvMultiSelect.svelte +12 -4
  101. package/src/SvOtpInput.svelte +1 -1
  102. package/src/SvPasswordInput.svelte +1 -1
  103. package/src/SvProgress.svelte +1 -1
  104. package/src/SvRating.svelte +1 -1
  105. package/src/SvResult.svelte +1 -1
  106. package/src/SvRichText.svelte +35 -16
  107. package/src/SvSwitchButton.svelte +2 -1
  108. package/src/SvToaster.svelte +1 -1
  109. package/src/SvTour.svelte +4 -0
  110. package/src/SvTree.svelte +9 -2
  111. package/src/SvTreeSelect.svelte +6 -4
  112. package/src/ai.test.ts +1 -1
  113. package/src/build-api.ts +15 -105
  114. package/src/builtin-editors.test.ts +3 -1
  115. package/src/cell-formatting.test.ts +1 -1
  116. package/src/cell-render.test.ts +27 -3
  117. package/src/cell-render.ts +122 -95
  118. package/src/cell-values.ts +4 -0
  119. package/src/chart-export.test.ts +2 -2
  120. package/src/chart.ts +1 -2
  121. package/src/clipboard.test.ts +0 -2
  122. package/src/clipboard.ts +0 -102
  123. package/src/columns.ts +0 -115
  124. package/src/core.ts +157 -8
  125. package/src/createMenu.svelte.ts +0 -1
  126. package/src/editing.test.ts +1 -1
  127. package/src/editing.ts +2 -105
  128. package/src/editor-contract.ts +12 -5
  129. package/src/editor-registry.grid.test.ts +48 -2
  130. package/src/editor-registry.test.ts +3 -1
  131. package/src/editor-registry.ts +23 -7
  132. package/src/export-format.ts +3 -0
  133. package/src/facet-buckets.test.ts +33 -33
  134. package/src/features.ts +0 -90
  135. package/src/group-display.test.ts +91 -1
  136. package/src/group-display.ts +83 -0
  137. package/src/index.ts +9 -0
  138. package/src/keyboard-handlers.coverage.test.ts +1 -1
  139. package/src/keyboard-handlers.ts +17 -120
  140. package/src/list-power.test.ts +2 -2
  141. package/src/menus.test.ts +4 -5
  142. package/src/menus.ts +0 -112
  143. package/src/named-views.coverage.test.ts +3 -3
  144. package/src/new-features.test.ts +3 -1
  145. package/src/recurrence.ts +3 -2
  146. package/src/row-drag.test.ts +1 -1
  147. package/src/row-resize.test.ts +41 -42
  148. package/src/scheduler-model.ts +1 -1
  149. package/src/scroll-sync.test.ts +4 -7
  150. package/src/scroll-sync.ts +0 -122
  151. package/src/selection.ts +0 -109
  152. package/src/server-data-source.coverage.test.ts +1 -1
  153. package/src/server-group-model.test.ts +8 -8
  154. package/src/spreadsheet.test.ts +0 -1
  155. package/src/summaries.ts +0 -112
  156. package/src/svgrid-wrapper.types.ts +11 -9
  157. package/src/svgrid.async-editor-options.test.ts +271 -0
  158. package/src/svgrid.auto-row-height.test.ts +204 -0
  159. package/src/svgrid.behavior.test.ts +0 -1
  160. package/src/svgrid.charting.test.ts +4 -4
  161. package/src/svgrid.column-reorder.test.ts +0 -8
  162. package/src/svgrid.comments-autocomplete.test.ts +8 -2
  163. package/src/svgrid.context-menu.test.ts +25 -9
  164. package/src/svgrid.filter-depth.test.ts +163 -0
  165. package/src/svgrid.filter-menu-listbox.svelte.test.ts +16 -2
  166. package/src/svgrid.filter-menu-scroll.test.ts +10 -2
  167. package/src/svgrid.grand-total.test.ts +188 -0
  168. package/src/svgrid.group-display-mode.test.ts +171 -0
  169. package/src/svgrid.group-footers.test.ts +121 -0
  170. package/src/svgrid.group-pagination.test.ts +153 -0
  171. package/src/svgrid.tree-data.test.ts +186 -0
  172. package/src/svgriddropdown.async-panel.svelte.test.ts +195 -0
  173. package/src/themes/index.ts +42 -0
  174. package/src/tree-edit.test.ts +4 -4
  175. package/src/tree-row-model.test.ts +168 -0
  176. package/src/ui-localization.test.ts +113 -0
  177. package/themes/ag-alpine.css +22 -0
  178. package/themes/antd.css +22 -0
  179. package/themes/atlassian.css +22 -0
  180. package/themes/bootstrap.css +22 -0
  181. package/themes/carbon.css +22 -0
  182. package/themes/catppuccin.css +22 -0
  183. package/themes/dracula.css +22 -0
  184. package/themes/ember.css +22 -0
  185. package/themes/excel.css +22 -0
  186. package/themes/fluent.css +22 -0
  187. package/themes/github.css +22 -0
  188. package/themes/linear.css +22 -0
  189. package/themes/material.css +22 -0
  190. package/themes/nord.css +22 -0
  191. package/themes/notion.css +22 -0
  192. package/themes/salesforce.css +22 -0
  193. package/themes/sap.css +22 -0
  194. package/themes/shadcn.css +22 -0
  195. package/themes/tailwind.css +22 -0
  196. package/themes/vercel.css +22 -0
@@ -19,8 +19,8 @@ describe('groupOptions', () => {
19
19
  it('buckets by group in first-seen order and preserves the flat index', () => {
20
20
  const groups = groupOptions(opts)
21
21
  expect(groups.map((g) => g.group)).toEqual(['Americas', 'Europe'])
22
- expect(groups[0].options.map((o) => o.index)).toEqual([0, 1])
23
- expect(groups[1].options.map((o) => o.index)).toEqual([2, 3])
22
+ expect(groups[0]!.options.map((o) => o.index)).toEqual([0, 1])
23
+ expect(groups[1]!.options.map((o) => o.index)).toEqual([2, 3])
24
24
  expect(hasGroups(opts)).toBe(true)
25
25
  })
26
26
  it('returns a single null group when nothing is grouped', () => {
package/src/menus.test.ts CHANGED
@@ -557,8 +557,8 @@ describe('createMenus - contextMenuItems', () => {
557
557
  expect(labels).toContain('Remove row')
558
558
  expect(labels).toContain('Remove column')
559
559
  // no leading/trailing separator
560
- expect(items[0].separator).toBeFalsy()
561
- expect(items[items.length - 1].separator).toBeFalsy()
560
+ expect(items[0]!.separator).toBeFalsy()
561
+ expect(items[items.length - 1]!.separator).toBeFalsy()
562
562
  })
563
563
 
564
564
  it('built-in run wiring invokes clipboard helpers', () => {
@@ -597,7 +597,6 @@ describe('createMenus - contextMenuItems', () => {
597
597
  internalData: [rowA, rowB],
598
598
  contextMenuFor: { rowIndex: 1, colIndex: 0, columnId: 'name', rowId: 'r1', row: rowB },
599
599
  }))
600
- const ctx2 = (m2 as any)
601
600
  const removeItem = m2.contextMenuItems().find((i) => i.key === 'remove_row')!
602
601
  removeItem.run!()
603
602
  })
@@ -627,8 +626,8 @@ describe('createMenus - contextMenuItems', () => {
627
626
  const m = createMenus(ctx)
628
627
  const items = m.contextMenuItems()
629
628
  expect(items.map((i) => i.key)).toEqual(['shown'])
630
- expect(items[0].disabled).toBe(true)
631
- items[0].run!()
629
+ expect(items[0]!.disabled).toBe(true)
630
+ items[0]!.run!()
632
631
  expect(action).toHaveBeenCalled()
633
632
  })
634
633
 
package/src/menus.ts CHANGED
@@ -2,131 +2,19 @@
2
2
  // reading/writing controller state via the `ctx` handle; the reactive core
3
3
  // ($state/$derived/$effect) stays in the controller.
4
4
  import {
5
- applyExcelFilter,
6
- normalizeForFilter,
7
- createColumnVirtualizer,
8
- createCoreRowModel,
9
- createExpandedRowModel,
10
- createFilteredRowModel,
11
- createGroupedRowModel,
12
- createPaginatedRowModel,
13
- createSvelteVirtualizer,
14
- createSortedRowModel,
15
- createSvGrid,
16
- filterFns,
17
- getGridCellA11yProps,
18
- getGridCellDomId,
19
- getGridHeaderA11yProps,
20
- getGridRootA11yProps,
21
- getGridRowA11yProps,
22
- parseEditorValue,
23
- normalizeEditorOptions,
24
- sortFns,
25
- tableFeatures,
26
- rowSortingFeature,
27
- columnFilteringFeature,
28
- columnGroupingFeature,
29
- type CellContext,
30
- type EditorContext,
31
- type CellEditorOption,
32
- type CellEditorType,
33
- type CellFormatter,
34
- type CellFormatConfig,
35
- type Column,
36
- type ColumnDef,
37
- type Row,
38
5
  type RowData,
39
- type SvGridApi,
40
6
  type TableFeatures,
41
7
  } from "./index";
42
8
  import "./sv-grid-scrollbar";
43
9
  import { untrack } from "svelte";
44
- import type { Snippet } from "svelte";
45
- import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
46
- import {
47
- formatNumericWithConfig,
48
- getDateFormatter,
49
- resolveDatePattern,
50
- } from "./cell-formatting";
51
- import {
52
- RenderSnippetConfig,
53
- RenderComponentConfig,
54
- } from "./render-component";
55
- import { buildFillPattern } from "./fill-patterns";
56
- import { buildSparkline, toSparklineValues } from "./sparkline";
57
- import {
58
- resolveCellFormat,
59
- computeColumnStat,
60
- formatsNeedingStats,
61
- type ColumnStat,
62
- type ResolvedCellFormat,
63
- } from "./conditional-formatting";
64
- import SvGridDropdown from "./SvGridDropdown.svelte";
65
10
  import type {
66
- Props,
67
- SelectionPoint,
68
- SelectionRange,
69
- CellEditState,
70
11
  FilterOperator,
71
- FilterOption,
72
- MenuPosition,
73
12
  } from "./SvGrid.types";
74
13
  import {
75
- cfTextStyle,
76
- fmtStat,
77
- getCellKey,
78
- resolveClassList,
79
- toDateInputValue,
80
- toDateTimeLocalInputValue,
81
- getEditableInputValue,
82
- getEditorInputType,
83
- toValueArray,
84
- getOptionLabel,
85
- getOptionColor,
86
- colorfulChipStyle,
87
- getEditorClass,
88
- asDate,
89
14
  clampMenuX,
90
15
  cssEscape,
91
- rawToNumber,
92
- formatFacetNumber,
93
- formatFacetDate,
94
16
  } from "./SvGrid.helpers";
95
- import { createCellRender } from "./cell-render";
96
- import { createEditing } from "./editing";
97
- import { createSelection } from "./selection";
98
- import { createColumns } from "./columns";
99
- import { createGridApi } from "./build-api";
100
- import { createClipboard } from "./clipboard";
101
- import {
102
- filterOperatorOptions,
103
- fallbackOperatorOption,
104
- TEXT_OPERATORS,
105
- NUMBER_OPERATORS,
106
- DATE_OPERATORS,
107
- CHECKBOX_OPERATORS,
108
- operatorOption,
109
- operatorsForColumn,
110
- defaultOperatorFor,
111
- operatorLabelFor,
112
- } from "./filter-operators";
113
- import {
114
- type FacetBucket,
115
- isBucketableColumn,
116
- buildBuckets,
117
- isInBucket,
118
- } from "./facet-buckets";
119
17
  import { splitInTokens, joinInTokens } from "./filtering/excel-filters";
120
- import {
121
- getColumnBaseValue,
122
- isGroupRow,
123
- toolPanelHeaderLabel,
124
- formatSummaryNumeric,
125
- getColumnAlign,
126
- getPinnedCellValue,
127
- getColumnAccessorValue,
128
- columnDefMatchesId,
129
- } from "./cell-values";
130
18
 
131
19
  export function createMenus<
132
20
  TFeatures extends TableFeatures = TableFeatures,
@@ -144,7 +144,7 @@ describe('attachAutoSavedView', () => {
144
144
  const off = attachAutoSavedView(host, views, { intervalMs: 500 })
145
145
  expect(views.has('__autosave')).toBe(false)
146
146
  // mutate the host state, then let the poller tick
147
- host.set({ sorting: [{ id: 'new', desc: false }], columnFilters: [] } as SvGridViewState)
147
+ host.set({ sorting: [{ id: 'new', desc: false }], columnFilters: [] } as unknown as SvGridViewState)
148
148
  vi.advanceTimersByTime(500)
149
149
  expect(views.has('__autosave')).toBe(true)
150
150
  off()
@@ -164,7 +164,7 @@ describe('attachAutoSavedView', () => {
164
164
  const host = fakeHost({ sorting: [] })
165
165
  const views = createNamedViews(host, { storage: memoryViews() })
166
166
  const off = attachAutoSavedView(host, views, { name: 'mySlot', intervalMs: 200 })
167
- host.set({ sorting: [{ id: 'q', desc: true }], columnFilters: [] } as SvGridViewState)
167
+ host.set({ sorting: [{ id: 'q', desc: true }], columnFilters: [] } as unknown as SvGridViewState)
168
168
  vi.advanceTimersByTime(200)
169
169
  expect(views.has('mySlot')).toBe(true)
170
170
  off()
@@ -185,7 +185,7 @@ describe('attachAutoSavedView', () => {
185
185
  const host = {
186
186
  getState: () => {
187
187
  if (throwing) throw new Error('boom')
188
- return { sorting: [], columnFilters: [] } as SvGridViewState
188
+ return { sorting: [], columnFilters: [] } as unknown as SvGridViewState
189
189
  },
190
190
  setState: vi.fn(),
191
191
  }
@@ -58,7 +58,8 @@ describe('createSvGrid - getRowId', () => {
58
58
 
59
59
  it('passes the row index to getRowId', () => {
60
60
  const seen: Array<{ id: string; index: number }> = []
61
- createSvGrid<typeof features, Row>({
61
+ // The row model is lazy, so it has to be read for getRowId to run at all.
62
+ const rows = createSvGrid<typeof features, Row>({
62
63
  _features: features,
63
64
  _rowModels: { coreRowModel: createCoreRowModel<Row>() },
64
65
  columns,
@@ -68,6 +69,7 @@ describe('createSvGrid - getRowId', () => {
68
69
  return row.id
69
70
  },
70
71
  }).getRowModel().rows
72
+ expect(rows).toHaveLength(data.length)
71
73
  expect(seen).toEqual([
72
74
  { id: 'ada', index: 0 },
73
75
  { id: 'linus', index: 1 },
package/src/recurrence.ts CHANGED
@@ -189,8 +189,9 @@ const ORD: Record<number, string> = { 1: 'first', 2: 'second', 3: 'third', 4: 'f
189
189
 
190
190
  // Where in the month a monthly / yearly rule lands, as a phrase.
191
191
  function whereInMonth(r: RecurrenceRule, wd: ReadonlyArray<string>, ord: Record<number, string>): string {
192
- if (r.weekOfMonth != null && r.weekdays && r.weekdays.length) {
193
- return `the ${ord[r.weekOfMonth] ?? r.weekOfMonth} ${wd[r.weekdays[0]]}`
192
+ const firstWeekday = r.weekdays?.[0]
193
+ if (r.weekOfMonth != null && firstWeekday != null) {
194
+ return `the ${ord[r.weekOfMonth] ?? r.weekOfMonth} ${wd[firstWeekday]}`
194
195
  }
195
196
  if (r.day === -1) return 'the last day'
196
197
  if (r.day != null && r.day < 0) return `the ${-r.day} day(s) from the end`
@@ -6,7 +6,7 @@
6
6
  * Because the bus is shared, we can create TWO fake ctxs (two grids) and drive
7
7
  * a genuine grid-to-grid move without mounting anything.
8
8
  */
9
- import { beforeEach, describe, expect, it, vi } from 'vitest'
9
+ import { describe, expect, it, vi } from 'vitest'
10
10
  import { createRowDrag, rowDropZone } from './row-drag'
11
11
 
12
12
  type AnyCtx = Record<string, any>
@@ -1,6 +1,5 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
2
  import { rowResize } from './row-resize'
3
- import type { RowResizeOptions } from './row-resize'
4
3
 
5
4
  const STRIP_CLASS = 'sv-grid-row-resize-handle'
6
5
 
@@ -97,8 +96,8 @@ describe('rowResize - strip injection', () => {
97
96
  expect(strip!.style.cursor).toBe('row-resize')
98
97
  }
99
98
  // Gutter cells become a positioning context with visible overflow.
100
- expect(gutterOf(rows[0]).style.position).toBe('relative')
101
- expect(gutterOf(rows[0]).style.overflow).toBe('visible')
99
+ expect(gutterOf(rows[0]!).style.position).toBe('relative')
100
+ expect(gutterOf(rows[0]!).style.overflow).toBe('visible')
102
101
  action.destroy()
103
102
  })
104
103
 
@@ -106,12 +105,12 @@ describe('rowResize - strip injection', () => {
106
105
  const { host, rows } = buildGrid([{}])
107
106
  track(host)
108
107
  const action = rowResize(host, { onResize: vi.fn() })
109
- expect(rows[0].querySelectorAll(`.${STRIP_CLASS}`).length).toBe(1)
108
+ expect(rows[0]!.querySelectorAll(`.${STRIP_CLASS}`).length).toBe(1)
110
109
  // Trigger the MutationObserver by appending an unrelated node.
111
- gutterOf(rows[0]).appendChild(document.createElement('span'))
110
+ gutterOf(rows[0]!).appendChild(document.createElement('span'))
112
111
  await Promise.resolve()
113
112
  await new Promise((r) => setTimeout(r, 0))
114
- expect(rows[0].querySelectorAll(`.${STRIP_CLASS}`).length).toBe(1)
113
+ expect(rows[0]!.querySelectorAll(`.${STRIP_CLASS}`).length).toBe(1)
115
114
  action.destroy()
116
115
  })
117
116
 
@@ -124,10 +123,10 @@ describe('rowResize - strip injection', () => {
124
123
  ])
125
124
  track(host)
126
125
  const action = rowResize(host, { onResize: vi.fn() })
127
- expect(stripOf(rows[0])).toBeNull()
128
- expect(stripOf(rows[1])).toBeNull()
129
- expect(stripOf(rows[2])).toBeNull()
130
- expect(stripOf(rows[3])).not.toBeNull()
126
+ expect(stripOf(rows[0]!)).toBeNull()
127
+ expect(stripOf(rows[1]!)).toBeNull()
128
+ expect(stripOf(rows[2]!)).toBeNull()
129
+ expect(stripOf(rows[3]!)).not.toBeNull()
131
130
  action.destroy()
132
131
  })
133
132
 
@@ -159,9 +158,9 @@ describe('rowResize - strip injection', () => {
159
158
  it('does not override an already-positioned gutter cell', () => {
160
159
  const { host, rows } = buildGrid([{}])
161
160
  track(host)
162
- gutterOf(rows[0]).style.position = 'sticky'
161
+ gutterOf(rows[0]!).style.position = 'sticky'
163
162
  const action = rowResize(host, { onResize: vi.fn() })
164
- expect(gutterOf(rows[0]).style.position).toBe('sticky')
163
+ expect(gutterOf(rows[0]!).style.position).toBe('sticky')
165
164
  action.destroy()
166
165
  })
167
166
 
@@ -169,8 +168,8 @@ describe('rowResize - strip injection', () => {
169
168
  const { host, rows } = buildGrid([{}, {}])
170
169
  track(host)
171
170
  const action = rowResize(host, { onResize: vi.fn(), disabled: true })
172
- expect(stripOf(rows[0])).toBeNull()
173
- expect(stripOf(rows[1])).toBeNull()
171
+ expect(stripOf(rows[0]!)).toBeNull()
172
+ expect(stripOf(rows[1]!)).toBeNull()
174
173
  action.destroy()
175
174
  })
176
175
  })
@@ -183,11 +182,11 @@ describe('rowResize - drag', () => {
183
182
  it('reports the new height on pointerup and fires move callbacks', () => {
184
183
  const { host, rows } = buildGrid([{ rowIndex: 0 }])
185
184
  track(host)
186
- mockRowHeight(rows[0], 32)
185
+ mockRowHeight(rows[0]!, 32)
187
186
  const onResize = vi.fn()
188
187
  const onResizeMove = vi.fn()
189
188
  const action = rowResize(host, { onResize, onResizeMove })
190
- const strip = stripOf(rows[0])!
189
+ const strip = stripOf(rows[0]!)!
191
190
 
192
191
  strip.dispatchEvent(pointer('pointerdown', { clientY: 100 }))
193
192
  expect(strip.classList.contains('is-resizing')).toBe(true)
@@ -196,7 +195,7 @@ describe('rowResize - drag', () => {
196
195
  // Drag down 20px -> 52.
197
196
  window.dispatchEvent(pointer('pointermove', { clientY: 120 }))
198
197
  expect(onResizeMove).toHaveBeenLastCalledWith(0, 52)
199
- expect(rows[0].style.height).toBe('52px')
198
+ expect(rows[0]!.style.height).toBe('52px')
200
199
 
201
200
  window.dispatchEvent(pointer('pointerup', { clientY: 120 }))
202
201
  expect(onResize).toHaveBeenCalledWith(0, 52)
@@ -208,10 +207,10 @@ describe('rowResize - drag', () => {
208
207
  it('clamps the height to the min bound', () => {
209
208
  const { host, rows } = buildGrid([{ rowIndex: 0 }])
210
209
  track(host)
211
- mockRowHeight(rows[0], 32)
210
+ mockRowHeight(rows[0]!, 32)
212
211
  const onResize = vi.fn()
213
212
  const action = rowResize(host, { onResize, min: 24 })
214
- const strip = stripOf(rows[0])!
213
+ const strip = stripOf(rows[0]!)!
215
214
  strip.dispatchEvent(pointer('pointerdown', { clientY: 100 }))
216
215
  // Drag far up -> would be negative, clamps to 24.
217
216
  window.dispatchEvent(pointer('pointerup', { clientY: 0 }))
@@ -222,10 +221,10 @@ describe('rowResize - drag', () => {
222
221
  it('clamps the height to the max bound', () => {
223
222
  const { host, rows } = buildGrid([{ rowIndex: 0 }])
224
223
  track(host)
225
- mockRowHeight(rows[0], 32)
224
+ mockRowHeight(rows[0]!, 32)
226
225
  const onResize = vi.fn()
227
226
  const action = rowResize(host, { onResize, max: 50 })
228
- const strip = stripOf(rows[0])!
227
+ const strip = stripOf(rows[0]!)!
229
228
  strip.dispatchEvent(pointer('pointerdown', { clientY: 100 }))
230
229
  window.dispatchEvent(pointer('pointerup', { clientY: 1000 }))
231
230
  expect(onResize).toHaveBeenCalledWith(0, 50)
@@ -235,10 +234,10 @@ describe('rowResize - drag', () => {
235
234
  it('works without an onResizeMove callback (optional)', () => {
236
235
  const { host, rows } = buildGrid([{ rowIndex: 0 }])
237
236
  track(host)
238
- mockRowHeight(rows[0], 30)
237
+ mockRowHeight(rows[0]!, 30)
239
238
  const onResize = vi.fn()
240
239
  const action = rowResize(host, { onResize })
241
- const strip = stripOf(rows[0])!
240
+ const strip = stripOf(rows[0]!)!
242
241
  strip.dispatchEvent(pointer('pointerdown', { clientY: 50 }))
243
242
  expect(() => window.dispatchEvent(pointer('pointermove', { clientY: 60 }))).not.toThrow()
244
243
  window.dispatchEvent(pointer('pointerup', { clientY: 60 }))
@@ -247,7 +246,7 @@ describe('rowResize - drag', () => {
247
246
  })
248
247
 
249
248
  it('ignores pointermove / pointerup when no drag is active', () => {
250
- const { host, rows } = buildGrid([{ rowIndex: 0 }])
249
+ const { host } = buildGrid([{ rowIndex: 0 }])
251
250
  track(host)
252
251
  const onResize = vi.fn()
253
252
  const onResizeMove = vi.fn()
@@ -270,11 +269,11 @@ describe('rowResize - pointerdown guards', () => {
270
269
  it('ignores pointerdown when disabled', () => {
271
270
  const { host, rows } = buildGrid([{ rowIndex: 0 }])
272
271
  track(host)
273
- mockRowHeight(rows[0], 32)
272
+ mockRowHeight(rows[0]!, 32)
274
273
  const onResize = vi.fn()
275
274
  // Start enabled so a strip exists, then disable via update.
276
275
  const action = rowResize(host, { onResize })
277
- const strip = stripOf(rows[0])!
276
+ const strip = stripOf(rows[0]!)!
278
277
  action.update({ onResize, disabled: true })
279
278
  // update removes strips, but dispatch on the (now-detached) strip anyway:
280
279
  // even if it were still in the DOM, current.disabled short-circuits.
@@ -289,7 +288,7 @@ describe('rowResize - pointerdown guards', () => {
289
288
  track(host)
290
289
  const onResize = vi.fn()
291
290
  const action = rowResize(host, { onResize })
292
- gutterOf(rows[0]).dispatchEvent(pointer('pointerdown', { clientY: 10 }))
291
+ gutterOf(rows[0]!).dispatchEvent(pointer('pointerdown', { clientY: 10 }))
293
292
  expect(document.body.style.cursor).toBe('')
294
293
  action.destroy()
295
294
  })
@@ -298,10 +297,10 @@ describe('rowResize - pointerdown guards', () => {
298
297
  const { host, rows } = buildGrid([{}])
299
298
  track(host)
300
299
  // Remove the data-svgrid-row marker so rowIndexOf returns NaN.
301
- rows[0].querySelector('[data-svgrid-row]')!.removeAttribute('data-svgrid-row')
300
+ rows[0]!.querySelector('[data-svgrid-row]')!.removeAttribute('data-svgrid-row')
302
301
  const onResize = vi.fn()
303
302
  const action = rowResize(host, { onResize })
304
- const strip = stripOf(rows[0])!
303
+ const strip = stripOf(rows[0]!)!
305
304
  strip.dispatchEvent(pointer('pointerdown', { clientY: 10 }))
306
305
  expect(document.body.style.cursor).toBe('')
307
306
  expect(strip.classList.contains('is-resizing')).toBe(false)
@@ -333,14 +332,14 @@ describe('rowResize - update + destroy', () => {
333
332
  track(host)
334
333
  const onResize = vi.fn()
335
334
  const action = rowResize(host, { onResize, disabled: false })
336
- expect(stripOf(rows[0])).not.toBeNull()
335
+ expect(stripOf(rows[0]!)).not.toBeNull()
337
336
 
338
337
  action.update({ onResize, disabled: true })
339
- expect(stripOf(rows[0])).toBeNull()
340
- expect(stripOf(rows[1])).toBeNull()
338
+ expect(stripOf(rows[0]!)).toBeNull()
339
+ expect(stripOf(rows[1]!)).toBeNull()
341
340
 
342
341
  action.update({ onResize, disabled: false })
343
- expect(stripOf(rows[0])).not.toBeNull()
342
+ expect(stripOf(rows[0]!)).not.toBeNull()
344
343
  action.destroy()
345
344
  })
346
345
 
@@ -348,29 +347,29 @@ describe('rowResize - update + destroy', () => {
348
347
  const { host, rows } = buildGrid([{}])
349
348
  track(host)
350
349
  const action = rowResize(host, { onResize: vi.fn() })
351
- const firstStrip = stripOf(rows[0])
350
+ const firstStrip = stripOf(rows[0]!)
352
351
  // Same disabled value -> decorate is NOT called, strip stays the same node.
353
352
  action.update({ onResize: vi.fn() })
354
- expect(stripOf(rows[0])).toBe(firstStrip)
353
+ expect(stripOf(rows[0]!)).toBe(firstStrip)
355
354
  action.destroy()
356
355
  })
357
356
 
358
357
  it('destroy removes all strips and detaches listeners', () => {
359
358
  const { host, rows } = buildGrid([{ rowIndex: 0 }])
360
359
  track(host)
361
- mockRowHeight(rows[0], 32)
360
+ mockRowHeight(rows[0]!, 32)
362
361
  const onResize = vi.fn()
363
362
  const action = rowResize(host, { onResize })
364
- expect(stripOf(rows[0])).not.toBeNull()
363
+ expect(stripOf(rows[0]!)).not.toBeNull()
365
364
 
366
365
  action.destroy()
367
- expect(stripOf(rows[0])).toBeNull()
366
+ expect(stripOf(rows[0]!)).toBeNull()
368
367
 
369
368
  // After destroy the pointerdown listener is gone; re-create a strip
370
369
  // manually and confirm no drag starts.
371
370
  const strip = document.createElement('div')
372
371
  strip.className = STRIP_CLASS
373
- gutterOf(rows[0]).appendChild(strip)
372
+ gutterOf(rows[0]!).appendChild(strip)
374
373
  strip.dispatchEvent(pointer('pointerdown', { clientY: 10 }))
375
374
  expect(document.body.style.cursor).toBe('')
376
375
  })
@@ -378,14 +377,14 @@ describe('rowResize - update + destroy', () => {
378
377
  it('uses default min=20 / max=320 when bounds are omitted', () => {
379
378
  const { host, rows } = buildGrid([{ rowIndex: 0 }])
380
379
  track(host)
381
- mockRowHeight(rows[0], 100)
380
+ mockRowHeight(rows[0]!, 100)
382
381
  const onResize = vi.fn()
383
382
  const action = rowResize(host, { onResize })
384
- const strip = stripOf(rows[0])!
383
+ const strip = stripOf(rows[0]!)!
385
384
  strip.dispatchEvent(pointer('pointerdown', { clientY: 0 }))
386
385
  // Drag way past max -> clamps to 320.
387
386
  window.dispatchEvent(pointer('pointermove', { clientY: 10000 }))
388
- expect(rows[0].style.height).toBe('320px')
387
+ expect(rows[0]!.style.height).toBe('320px')
389
388
  // Drag way below min -> clamps to 20.
390
389
  window.dispatchEvent(pointer('pointerup', { clientY: -10000 }))
391
390
  expect(onResize).toHaveBeenCalledWith(0, 20)
@@ -797,7 +797,7 @@ export function timelineAxis(
797
797
  } else if (view === 'timelineYear') {
798
798
  const y = rangeStart.getFullYear()
799
799
  for (let m = 0; m < 12; m++) {
800
- ticks.push(mk(new Date(y, m, 1), new Date(y, m + 1, 1), TL_MO[m]))
800
+ ticks.push(mk(new Date(y, m, 1), new Date(y, m + 1, 1), TL_MO[m]!))
801
801
  }
802
802
  majors = groupMajors(
803
803
  ticks,
@@ -102,7 +102,7 @@ describe('showTooltipFor / hideTooltip', () => {
102
102
  })
103
103
 
104
104
  it('clears a pending timer before scheduling a new tooltip', () => {
105
- const { ctx, ss } = syncFor()
105
+ const { ss } = syncFor()
106
106
  const clearSpy = vi.spyOn(window, 'clearTimeout')
107
107
  const el = anchorEl({ left: 10, right: 30, top: 10, bottom: 30 })
108
108
  ss.showTooltipFor(el, 'first')
@@ -204,13 +204,10 @@ describe('scheduleScrollSync / flushScheduledScrollSync', () => {
204
204
  })
205
205
 
206
206
  describe('onBodyScroll', () => {
207
- let rafCb: FrameRequestCallback | null = null
208
207
  beforeEach(() => {
209
- rafCb = null
210
- vi.stubGlobal('requestAnimationFrame', (cb: FrameRequestCallback) => {
211
- rafCb = cb
212
- return 7
213
- })
208
+ // These tests assert what happens BEFORE the frame runs, so the stub just
209
+ // hands back a handle and never invokes the callback.
210
+ vi.stubGlobal('requestAnimationFrame', () => 7)
214
211
  })
215
212
  afterEach(() => {
216
213
  vi.unstubAllGlobals()
@@ -2,132 +2,10 @@
2
2
  // reading/writing controller state via the `ctx` handle; the reactive core
3
3
  // ($state/$derived/$effect) stays in the controller.
4
4
  import {
5
- applyExcelFilter,
6
- normalizeForFilter,
7
- createColumnVirtualizer,
8
- createCoreRowModel,
9
- createExpandedRowModel,
10
- createFilteredRowModel,
11
- createGroupedRowModel,
12
- createPaginatedRowModel,
13
- createSvelteVirtualizer,
14
- createSortedRowModel,
15
- createSvGrid,
16
- filterFns,
17
- getGridCellA11yProps,
18
- getGridCellDomId,
19
- getGridHeaderA11yProps,
20
- getGridRootA11yProps,
21
- getGridRowA11yProps,
22
- parseEditorValue,
23
- normalizeEditorOptions,
24
- sortFns,
25
- tableFeatures,
26
- rowSortingFeature,
27
- columnFilteringFeature,
28
- columnGroupingFeature,
29
- type CellContext,
30
- type EditorContext,
31
- type CellEditorOption,
32
- type CellEditorType,
33
- type CellFormatter,
34
- type CellFormatConfig,
35
- type Column,
36
- type ColumnDef,
37
- type Row,
38
5
  type RowData,
39
- type SvGridApi,
40
6
  type TableFeatures,
41
7
  } from "./index";
42
8
  import "./sv-grid-scrollbar";
43
- import type { Snippet } from "svelte";
44
- import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
45
- import {
46
- formatNumericWithConfig,
47
- getDateFormatter,
48
- resolveDatePattern,
49
- } from "./cell-formatting";
50
- import {
51
- RenderSnippetConfig,
52
- RenderComponentConfig,
53
- } from "./render-component";
54
- import { buildFillPattern } from "./fill-patterns";
55
- import { buildSparkline, toSparklineValues } from "./sparkline";
56
- import {
57
- resolveCellFormat,
58
- computeColumnStat,
59
- formatsNeedingStats,
60
- type ColumnStat,
61
- type ResolvedCellFormat,
62
- } from "./conditional-formatting";
63
- import SvGridDropdown from "./SvGridDropdown.svelte";
64
- import type {
65
- Props,
66
- SelectionPoint,
67
- SelectionRange,
68
- CellEditState,
69
- FilterOperator,
70
- FilterOption,
71
- MenuPosition,
72
- } from "./SvGrid.types";
73
- import {
74
- cfTextStyle,
75
- fmtStat,
76
- getCellKey,
77
- resolveClassList,
78
- toDateInputValue,
79
- toDateTimeLocalInputValue,
80
- getEditableInputValue,
81
- getEditorInputType,
82
- toValueArray,
83
- getOptionLabel,
84
- getOptionColor,
85
- colorfulChipStyle,
86
- getEditorClass,
87
- asDate,
88
- clampMenuX,
89
- cssEscape,
90
- rawToNumber,
91
- formatFacetNumber,
92
- formatFacetDate,
93
- } from "./SvGrid.helpers";
94
- import { createKeyboard } from "./keyboard-handlers";
95
- import { createSummaries } from "./summaries";
96
- import { createMenus } from "./menus";
97
- import { createCellRender } from "./cell-render";
98
- import { createEditing } from "./editing";
99
- import { createSelection } from "./selection";
100
- import { createColumns } from "./columns";
101
- import { createGridApi } from "./build-api";
102
- import { createClipboard } from "./clipboard";
103
- import {
104
- filterOperatorOptions,
105
- fallbackOperatorOption,
106
- TEXT_OPERATORS,
107
- NUMBER_OPERATORS,
108
- DATE_OPERATORS,
109
- CHECKBOX_OPERATORS,
110
- operatorOption,
111
- operatorsForColumn,
112
- defaultOperatorFor,
113
- operatorLabelFor,
114
- } from "./filter-operators";
115
- import {
116
- type FacetBucket,
117
- isBucketableColumn,
118
- buildBuckets,
119
- isInBucket,
120
- } from "./facet-buckets";
121
- import {
122
- getColumnBaseValue,
123
- isGroupRow,
124
- toolPanelHeaderLabel,
125
- formatSummaryNumeric,
126
- getColumnAlign,
127
- getPinnedCellValue,
128
- getColumnAccessorValue,
129
- columnDefMatchesId,
130
- } from "./cell-values";
131
9
 
132
10
  export function createScrollSync<
133
11
  TFeatures extends TableFeatures = TableFeatures,