@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
@@ -1,178 +1,178 @@
1
- <script lang="ts" generics="TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData">
2
- import {
3
- getGridCellA11yProps,
4
- getGridCellDomId,
5
- getGridHeaderA11yProps,
6
- getGridRootA11yProps,
7
- getGridRowA11yProps,
8
- type EditorContext,
9
- type CellEditorOption,
10
- type Column,
11
- type Row,
12
- type RowData,
13
- type TableFeatures,
14
- } from "./index";
15
- import "./sv-grid-scrollbar";
16
- import type { Snippet } from "svelte";
17
- import {
18
- RenderSnippetConfig,
19
- RenderComponentConfig,
20
- } from "./render-component";
21
- import {
22
- buildSparkline,
23
- toSparklineValues,
24
- } from "./sparkline";
25
- import SvGridDropdown from "./SvGridDropdown.svelte";
26
- import type {
27
- Props,
28
- SelectionPoint,
29
- SelectionRange,
30
- CellEditState,
31
- FilterOperator,
32
- FilterOption,
33
- MenuPosition,
34
- } from "./SvGrid.types";
35
- import {
36
- cfTextStyle,
37
- fmtStat,
38
- getEditableInputValue,
39
- getEditorInputType,
40
- toValueArray,
41
- getOptionLabel,
42
- getOptionColor,
43
- colorfulChipStyle,
44
- getEditorClass,
45
- } from "./SvGrid.helpers";
46
- import {
47
- } from "./SvGrid.controller.svelte";
48
- import type { SvGridController } from "./SvGrid.controller.svelte";
49
-
50
- let {
51
- ctrl,
52
- pager = true,
53
- showStatus = true,
54
- top = false,
55
- pageSizeOptions,
56
- }: {
57
- ctrl: SvGridController<TFeatures, TData>;
58
- /** Render the pagination controls in this instance. */
59
- pager?: boolean;
60
- /** Render the status bar in this instance. */
61
- showStatus?: boolean;
62
- /** This is a top-positioned footer (styling only). */
63
- top?: boolean;
64
- /** Page-size choices for the selector. Defaults to `[10, 25, 50, 100]`. */
65
- pageSizeOptions?: number[];
66
- } = $props();
67
-
68
- // View facade: re-bind the controller's reactive members so the markup
69
- // (moved verbatim from SvGrid.svelte) stays identical.
70
- const paginationEnabled = $derived(ctrl.paginationEnabled);
71
- const grid = $derived(ctrl.grid);
72
- const paginationState = $derived(ctrl.paginationState);
73
- const allRowsBeforePagination = $derived(ctrl.allRowsBeforePagination);
74
- const statusBarEnabled = $derived(ctrl.statusBarEnabled);
75
- const statusBarAggregates = $derived(ctrl.statusBarAggregates);
76
- const statusBarStats = $derived(ctrl.statusBarStats);
77
- const changePage = $derived(ctrl.changePage);
78
- const goToPage = $derived(ctrl.goToPage);
79
- const setPageSize = $derived(ctrl.setPageSize);
80
- </script>
81
-
82
- {#if showStatus && statusBarEnabled && statusBarStats}
83
- {@const s = statusBarStats}
84
- <div class="sv-grid-status-bar" role="status" aria-live="polite">
85
- {#each statusBarAggregates as agg (agg)}
86
- {#if agg === "count"}
87
- <span class="sv-grid-status-item"
88
- ><span class="sv-grid-status-label">Count</span>{fmtStat(s.count)}</span
89
- >
90
- {:else if agg === "numericCount"}
91
- <span class="sv-grid-status-item"
92
- ><span class="sv-grid-status-label">Numeric</span>{fmtStat(s.numericCount)}</span
93
- >
94
- {:else if s.numericCount > 0 && agg === "sum"}
95
- <span class="sv-grid-status-item"
96
- ><span class="sv-grid-status-label">Sum</span>{fmtStat(s.sum)}</span
97
- >
98
- {:else if s.numericCount > 0 && agg === "avg"}
99
- <span class="sv-grid-status-item"
100
- ><span class="sv-grid-status-label">Avg</span>{fmtStat(s.avg)}</span
101
- >
102
- {:else if s.numericCount > 0 && agg === "min"}
103
- <span class="sv-grid-status-item"
104
- ><span class="sv-grid-status-label">Min</span>{fmtStat(s.min)}</span
105
- >
106
- {:else if s.numericCount > 0 && agg === "max"}
107
- <span class="sv-grid-status-item"
108
- ><span class="sv-grid-status-label">Max</span>{fmtStat(s.max)}</span
109
- >
110
- {/if}
111
- {/each}
112
- </div>
113
- {/if}
114
-
115
- {#if pager && paginationEnabled}
116
- {@const totalRows = ctrl.paginationTotalRows}
117
- {@const pageSize = ctrl.paginationPageSize}
118
- {@const pageCount = Math.max(1, Math.ceil(totalRows / pageSize))}
119
- {@const currentPage = Math.min(ctrl.paginationPageIndex + 1, pageCount)}
120
- {@const rangeStart =
121
- totalRows === 0 ? 0 : (currentPage - 1) * pageSize + 1}
122
- {@const rangeEnd = Math.min(totalRows, currentPage * pageSize)}
123
- {@const onFirst = currentPage <= 1}
124
- {@const onLast = currentPage >= pageCount}
125
- {@const sizes = pageSizeOptions && pageSizeOptions.length ? pageSizeOptions : [10, 25, 50, 100]}
126
- <div class="sv-grid-pagination" class:sv-grid-pagination-top={top} role="navigation" aria-label="Pagination">
127
- <div class="sv-grid-pagination-pagesize">
128
- <span>Page Size:</span>
129
- <div class="sv-grid-pagination-pagesize-dd">
130
- <SvGridDropdown
131
- options={sizes.map((n) => ({ value: n, label: String(n) }))}
132
- value={pageSize}
133
- autoOpen={false}
134
- onChange={(v) => setPageSize(Number(v))}
135
- />
136
- </div>
137
- </div>
138
- <span class="sv-grid-pagination-range">
139
- <strong>{rangeStart.toLocaleString()}</strong> to
140
- <strong>{rangeEnd.toLocaleString()}</strong> of
141
- <strong>{totalRows.toLocaleString()}</strong>
142
- </span>
143
- <div class="sv-grid-pagination-nav">
144
- <button
145
- type="button"
146
- class="sv-grid-pagination-btn"
147
- disabled={onFirst}
148
- onclick={() => goToPage(0)}
149
- aria-label="First page">⇤</button
150
- >
151
- <button
152
- type="button"
153
- class="sv-grid-pagination-btn"
154
- disabled={onFirst}
155
- onclick={() => changePage(-1)}
156
- aria-label="Previous page">‹</button
157
- >
158
- <span class="sv-grid-pagination-label">
159
- Page <strong>{currentPage.toLocaleString()}</strong> of
160
- <strong>{pageCount.toLocaleString()}</strong>
161
- </span>
162
- <button
163
- type="button"
164
- class="sv-grid-pagination-btn"
165
- disabled={onLast}
166
- onclick={() => changePage(1)}
167
- aria-label="Next page">›</button
168
- >
169
- <button
170
- type="button"
171
- class="sv-grid-pagination-btn"
172
- disabled={onLast}
173
- onclick={() => goToPage(pageCount - 1)}
174
- aria-label="Last page">⇥</button
175
- >
176
- </div>
177
- </div>
178
- {/if}
1
+ <script lang="ts" generics="TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData">
2
+ import {
3
+ getGridCellA11yProps,
4
+ getGridCellDomId,
5
+ getGridHeaderA11yProps,
6
+ getGridRootA11yProps,
7
+ getGridRowA11yProps,
8
+ type EditorContext,
9
+ type CellEditorOption,
10
+ type Column,
11
+ type Row,
12
+ type RowData,
13
+ type TableFeatures,
14
+ } from "./index";
15
+ import "./sv-grid-scrollbar";
16
+ import type { Snippet } from "svelte";
17
+ import {
18
+ RenderSnippetConfig,
19
+ RenderComponentConfig,
20
+ } from "./render-component";
21
+ import {
22
+ buildSparkline,
23
+ toSparklineValues,
24
+ } from "./sparkline";
25
+ import SvGridDropdown from "./SvGridDropdown.svelte";
26
+ import type {
27
+ Props,
28
+ SelectionPoint,
29
+ SelectionRange,
30
+ CellEditState,
31
+ FilterOperator,
32
+ FilterOption,
33
+ MenuPosition,
34
+ } from "./SvGrid.types";
35
+ import {
36
+ cfTextStyle,
37
+ fmtStat,
38
+ getEditableInputValue,
39
+ getEditorInputType,
40
+ toValueArray,
41
+ getOptionLabel,
42
+ getOptionColor,
43
+ colorfulChipStyle,
44
+ getEditorClass,
45
+ } from "./SvGrid.helpers";
46
+ import {
47
+ } from "./SvGrid.controller.svelte";
48
+ import type { SvGridController } from "./SvGrid.controller.svelte";
49
+
50
+ let {
51
+ ctrl,
52
+ pager = true,
53
+ showStatus = true,
54
+ top = false,
55
+ pageSizeOptions,
56
+ }: {
57
+ ctrl: SvGridController<TFeatures, TData>;
58
+ /** Render the pagination controls in this instance. */
59
+ pager?: boolean;
60
+ /** Render the status bar in this instance. */
61
+ showStatus?: boolean;
62
+ /** This is a top-positioned footer (styling only). */
63
+ top?: boolean;
64
+ /** Page-size choices for the selector. Defaults to `[10, 25, 50, 100]`. */
65
+ pageSizeOptions?: number[];
66
+ } = $props();
67
+
68
+ // View facade: re-bind the controller's reactive members so the markup
69
+ // (moved verbatim from SvGrid.svelte) stays identical.
70
+ const paginationEnabled = $derived(ctrl.paginationEnabled);
71
+ const grid = $derived(ctrl.grid);
72
+ const paginationState = $derived(ctrl.paginationState);
73
+ const allRowsBeforePagination = $derived(ctrl.allRowsBeforePagination);
74
+ const statusBarEnabled = $derived(ctrl.statusBarEnabled);
75
+ const statusBarAggregates = $derived(ctrl.statusBarAggregates);
76
+ const statusBarStats = $derived(ctrl.statusBarStats);
77
+ const changePage = $derived(ctrl.changePage);
78
+ const goToPage = $derived(ctrl.goToPage);
79
+ const setPageSize = $derived(ctrl.setPageSize);
80
+ </script>
81
+
82
+ {#if showStatus && statusBarEnabled && statusBarStats}
83
+ {@const s = statusBarStats}
84
+ <div class="sv-grid-status-bar" role="status" aria-live="polite">
85
+ {#each statusBarAggregates as agg (agg)}
86
+ {#if agg === "count"}
87
+ <span class="sv-grid-status-item"
88
+ ><span class="sv-grid-status-label">Count</span>{fmtStat(s.count)}</span
89
+ >
90
+ {:else if agg === "numericCount"}
91
+ <span class="sv-grid-status-item"
92
+ ><span class="sv-grid-status-label">Numeric</span>{fmtStat(s.numericCount)}</span
93
+ >
94
+ {:else if s.numericCount > 0 && agg === "sum"}
95
+ <span class="sv-grid-status-item"
96
+ ><span class="sv-grid-status-label">Sum</span>{fmtStat(s.sum)}</span
97
+ >
98
+ {:else if s.numericCount > 0 && agg === "avg"}
99
+ <span class="sv-grid-status-item"
100
+ ><span class="sv-grid-status-label">Avg</span>{fmtStat(s.avg)}</span
101
+ >
102
+ {:else if s.numericCount > 0 && agg === "min"}
103
+ <span class="sv-grid-status-item"
104
+ ><span class="sv-grid-status-label">Min</span>{fmtStat(s.min)}</span
105
+ >
106
+ {:else if s.numericCount > 0 && agg === "max"}
107
+ <span class="sv-grid-status-item"
108
+ ><span class="sv-grid-status-label">Max</span>{fmtStat(s.max)}</span
109
+ >
110
+ {/if}
111
+ {/each}
112
+ </div>
113
+ {/if}
114
+
115
+ {#if pager && paginationEnabled}
116
+ {@const totalRows = ctrl.paginationTotalRows}
117
+ {@const pageSize = ctrl.paginationPageSize}
118
+ {@const pageCount = Math.max(1, Math.ceil(totalRows / pageSize))}
119
+ {@const currentPage = Math.min(ctrl.paginationPageIndex + 1, pageCount)}
120
+ {@const rangeStart =
121
+ totalRows === 0 ? 0 : (currentPage - 1) * pageSize + 1}
122
+ {@const rangeEnd = Math.min(totalRows, currentPage * pageSize)}
123
+ {@const onFirst = currentPage <= 1}
124
+ {@const onLast = currentPage >= pageCount}
125
+ {@const sizes = pageSizeOptions && pageSizeOptions.length ? pageSizeOptions : [10, 25, 50, 100]}
126
+ <div class="sv-grid-pagination" class:sv-grid-pagination-top={top} role="navigation" aria-label="Pagination">
127
+ <div class="sv-grid-pagination-pagesize">
128
+ <span>Page Size:</span>
129
+ <div class="sv-grid-pagination-pagesize-dd">
130
+ <SvGridDropdown
131
+ options={sizes.map((n) => ({ value: n, label: String(n) }))}
132
+ value={pageSize}
133
+ autoOpen={false}
134
+ onChange={(v) => setPageSize(Number(v))}
135
+ />
136
+ </div>
137
+ </div>
138
+ <span class="sv-grid-pagination-range">
139
+ <strong>{rangeStart.toLocaleString()}</strong> to
140
+ <strong>{rangeEnd.toLocaleString()}</strong> of
141
+ <strong>{totalRows.toLocaleString()}</strong>
142
+ </span>
143
+ <div class="sv-grid-pagination-nav">
144
+ <button
145
+ type="button"
146
+ class="sv-grid-pagination-btn"
147
+ disabled={onFirst}
148
+ onclick={() => goToPage(0)}
149
+ aria-label="First page">⇤</button
150
+ >
151
+ <button
152
+ type="button"
153
+ class="sv-grid-pagination-btn"
154
+ disabled={onFirst}
155
+ onclick={() => changePage(-1)}
156
+ aria-label="Previous page">‹</button
157
+ >
158
+ <span class="sv-grid-pagination-label">
159
+ Page <strong>{currentPage.toLocaleString()}</strong> of
160
+ <strong>{pageCount.toLocaleString()}</strong>
161
+ </span>
162
+ <button
163
+ type="button"
164
+ class="sv-grid-pagination-btn"
165
+ disabled={onLast}
166
+ onclick={() => changePage(1)}
167
+ aria-label="Next page">›</button
168
+ >
169
+ <button
170
+ type="button"
171
+ class="sv-grid-pagination-btn"
172
+ disabled={onLast}
173
+ onclick={() => goToPage(pageCount - 1)}
174
+ aria-label="Last page">⇥</button
175
+ >
176
+ </div>
177
+ </div>
178
+ {/if}
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  applyExcelFilter,
4
4
  normalizeForFilter,
5
+ splitInTokens,
5
6
  createColumnVirtualizer,
6
7
  createCoreRowModel,
7
8
  createExpandedRowModel,
@@ -124,6 +125,12 @@
124
125
  const operatorsForColumn = $derived(ctrl.operatorsForColumn);
125
126
  const defaultOperatorFor = $derived(ctrl.defaultOperatorFor);
126
127
  const operatorLabelFor = $derived(ctrl.operatorLabelFor);
128
+ const filterRowValues = $derived(ctrl.filterRowValues);
129
+ const inSuggestFor = $derived(ctrl.inSuggestFor);
130
+ const inSuggestPos = $derived(ctrl.inSuggestPos);
131
+ const inSuggestValues = $derived(ctrl.inSuggestValues);
132
+ const toggleFilterToken = $derived(ctrl.toggleFilterToken);
133
+ const facetValuesForColumn = $derived(ctrl.facetValuesForColumn);
127
134
 
128
135
  // Patch a column's menu filter (used for the optional 2nd condition + join
129
136
  // of multi-condition filtering). Merges into the existing entry, creating a
@@ -135,6 +142,22 @@
135
142
  }
136
143
  const isColumnFiltered = $derived(ctrl.isColumnFiltered);
137
144
  const closeMenus = $derived(ctrl.closeMenus);
145
+
146
+ // Column/filter/operator menus are position:fixed and anchored to their
147
+ // trigger by a one-time measurement taken when they open. On scroll they'd
148
+ // otherwise detach and float over the wrong place (#88), so close them on any
149
+ // scroll (capture phase, to catch inner scroll containers) or resize.
150
+ $effect(() => {
151
+ if (!(columnMenuFor || filterMenuFor || operatorMenuFor || chooseColumnsPos)) return;
152
+ const close = () => closeMenus();
153
+ window.addEventListener('scroll', close, true);
154
+ window.addEventListener('resize', close);
155
+ return () => {
156
+ window.removeEventListener('scroll', close, true);
157
+ window.removeEventListener('resize', close);
158
+ };
159
+ });
160
+
138
161
  const autosizeColumn = $derived(ctrl.autosizeColumn);
139
162
  const autosizeAllColumns = $derived(ctrl.autosizeAllColumns);
140
163
  const resetColumns = $derived(ctrl.resetColumns);
@@ -202,14 +225,30 @@
202
225
  <option value={option.value}>{operatorLabelFor(option, menuColumn)}</option>
203
226
  {/each}
204
227
  </select>
205
- {#if menuActiveOperator !== "isBlank"}
228
+ {#if menuActiveOperator !== "isBlank" && menuActiveOperator !== "isNotBlank"}
229
+ {@const isSetOp =
230
+ menuActiveOperator === "in" || menuActiveOperator === "notIn"}
206
231
  <input
207
232
  class="sv-grid-menu-condition-value"
208
- type={menuInputType}
233
+ type={menuActiveOperator === "regex" ? "text" : menuInputType}
234
+ list={isSetOp ? `sv-in-list-${colId}` : undefined}
209
235
  value={filterMenuValues[colId]?.value ?? ""}
210
- placeholder={menuActiveOperator === "between" ? "From" : "Filter value..."}
236
+ placeholder={menuActiveOperator === "between"
237
+ ? "From"
238
+ : isSetOp
239
+ ? "value, value..."
240
+ : menuActiveOperator === "regex"
241
+ ? "Pattern..."
242
+ : "Filter value..."}
211
243
  oninput={(event) => updateFilterMenuValue(colId, (event.currentTarget as HTMLInputElement).value)}
212
244
  />
245
+ {#if isSetOp}
246
+ <datalist id={`sv-in-list-${colId}`}>
247
+ {#each facetValuesForColumn(colId).slice(0, 200) as v (v)}
248
+ <option value={v}></option>
249
+ {/each}
250
+ </datalist>
251
+ {/if}
213
252
  {#if menuActiveOperator === "between"}
214
253
  <input
215
254
  class="sv-grid-menu-condition-value"
@@ -585,6 +624,47 @@
585
624
  </div>
586
625
  {/if}
587
626
 
627
+ {#if inSuggestFor}
628
+ {@const suggestColId = inSuggestFor}
629
+ {@const selectedTokens = new Set(
630
+ splitInTokens(filterRowValues[suggestColId] ?? ""),
631
+ )}
632
+ <!-- Value suggestions for an `in` / `notIn` chip input. Items use
633
+ `onmousedown` (preventDefault) so clicking one keeps focus in the
634
+ chip input and the dropdown stays open for multi-select; the input's
635
+ own blur handler closes it when focus truly leaves. -->
636
+ <div
637
+ class="sv-grid-menu sv-grid-in-suggest"
638
+ role="listbox"
639
+ aria-multiselectable="true"
640
+ aria-label="Filter values"
641
+ style={`left: ${inSuggestPos.x}px; top: ${inSuggestPos.y}px;`}
642
+ >
643
+ {#each inSuggestValues as value (value)}
644
+ {@const checked = selectedTokens.has(value)}
645
+ <button
646
+ type="button"
647
+ class="sv-grid-menu-item sv-grid-in-suggest-item"
648
+ role="option"
649
+ aria-selected={checked}
650
+ onmousedown={(event) => {
651
+ event.preventDefault();
652
+ toggleFilterToken(suggestColId, value);
653
+ }}
654
+ >
655
+ <span class="sv-grid-in-suggest-check" aria-hidden="true"
656
+ >{checked ? "✓" : ""}</span
657
+ >
658
+ <span class="sv-grid-in-suggest-label"
659
+ >{value === "" ? "(Blanks)" : value}</span
660
+ >
661
+ </button>
662
+ {:else}
663
+ <div class="sv-grid-facet-empty">No values</div>
664
+ {/each}
665
+ </div>
666
+ {/if}
667
+
588
668
  {#if contextMenuFor}
589
669
  <div
590
670
  class="sv-grid-menu-backdrop"
@@ -47,7 +47,7 @@
47
47
 
48
48
  let fieldEl = $state<HTMLInputElement | null>(null)
49
49
  let panelEl = $state<HTMLDivElement | null>(null)
50
- let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false })
50
+ let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false, maxHeight: 0, availHeight: 0 })
51
51
 
52
52
  const ac = createAutocomplete({
53
53
  value: () => value,
@@ -66,7 +66,9 @@
66
66
 
67
67
  function updatePos() {
68
68
  if (!fieldEl) return
69
- rect = anchoredRect(fieldEl.getBoundingClientRect(), { estimatedHeight: Math.min(ac.filtered.length, 8) * 34 + 8 })
69
+ // estimatedHeight only picks the flip direction; real max-height is availHeight
70
+ // below, so the panel takes its natural content height. See SvComboBox.
71
+ rect = anchoredRect(fieldEl.getBoundingClientRect(), { estimatedHeight: Math.min(ac.filtered.length, 8) * 34 + 12 })
70
72
  }
71
73
 
72
74
  $effect(() => {
@@ -92,7 +94,7 @@
92
94
  </SvField>
93
95
 
94
96
  {#if ac.open}
95
- <div bind:this={panelEl} class="sv-ddl__panel" use:portalToBody use:popIn={{ up: rect.openUpward }} style:position="fixed" style:top={`${rect.top}px`} style:left={`${rect.left}px`} style:min-width={`${rect.width}px`} {...ac.listboxProps()}>
97
+ <div bind:this={panelEl} class="sv-ddl__panel" use:portalToBody use:popIn={{ up: rect.openUpward }} style:position="fixed" style:top={rect.openUpward ? undefined : `${rect.top}px`} style:bottom={rect.openUpward ? `${rect.bottom}px` : undefined} style:left={`${rect.left}px`} style:min-width={`${rect.width}px`} style:max-height={`${Math.min(rect.availHeight, 288)}px`} {...ac.listboxProps()}>
96
98
  {#each ac.filtered as opt, i (opt.value)}
97
99
  <!-- svelte-ignore a11y_click_events_have_key_events a11y_interactive_supports_focus -->
98
100
  <div class="sv-ddl__opt" class:is-active={ac.isActive(i)} {...ac.optionProps(i)}>{opt.label}</div>