@svgrid/grid 1.0.2 → 1.1.1

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 (216) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +137 -39
  3. package/dist/GridFooter.svelte +164 -0
  4. package/dist/GridFooter.svelte.d.ts +29 -0
  5. package/dist/GridMenus.svelte +648 -0
  6. package/dist/GridMenus.svelte.d.ts +31 -0
  7. package/dist/SvGrid.controller.svelte.d.ts +472 -0
  8. package/dist/SvGrid.controller.svelte.js +1917 -0
  9. package/dist/SvGrid.css +2012 -0
  10. package/dist/SvGrid.helpers.d.ts +35 -0
  11. package/dist/SvGrid.helpers.js +160 -0
  12. package/dist/SvGrid.helpers.test.d.ts +1 -0
  13. package/dist/SvGrid.helpers.test.js +298 -0
  14. package/dist/SvGrid.svelte +2346 -8742
  15. package/dist/SvGrid.svelte.d.ts +4 -357
  16. package/dist/SvGrid.types.d.ts +523 -0
  17. package/dist/SvGrid.types.js +1 -0
  18. package/dist/SvGridChart.svelte +1060 -23
  19. package/dist/SvGridChart.svelte.d.ts +17 -0
  20. package/dist/aligned-grids.d.ts +6 -0
  21. package/dist/aligned-grids.js +84 -0
  22. package/dist/aligned-grids.test.d.ts +1 -0
  23. package/dist/aligned-grids.test.js +75 -0
  24. package/dist/build-api.coverage.test.d.ts +20 -0
  25. package/dist/build-api.coverage.test.js +505 -0
  26. package/dist/build-api.d.ts +5 -0
  27. package/dist/build-api.js +557 -0
  28. package/dist/cell-render.d.ts +15 -0
  29. package/dist/cell-render.js +246 -0
  30. package/dist/cell-render.test.d.ts +1 -0
  31. package/dist/cell-render.test.js +338 -0
  32. package/dist/cell-values.d.ts +28 -0
  33. package/dist/cell-values.js +89 -0
  34. package/dist/chart-export.test.d.ts +1 -0
  35. package/dist/chart-export.test.js +302 -0
  36. package/dist/chart.coverage.test.d.ts +1 -0
  37. package/dist/chart.coverage.test.js +748 -0
  38. package/dist/chart.d.ts +370 -3
  39. package/dist/chart.js +1135 -42
  40. package/dist/clipboard.d.ts +15 -0
  41. package/dist/clipboard.js +420 -0
  42. package/dist/clipboard.test.d.ts +1 -0
  43. package/dist/clipboard.test.js +700 -0
  44. package/dist/collaboration.coverage.test.d.ts +1 -0
  45. package/dist/collaboration.coverage.test.js +200 -0
  46. package/dist/column-groups.d.ts +19 -0
  47. package/dist/column-groups.js +62 -0
  48. package/dist/column-groups.test.d.ts +1 -0
  49. package/dist/column-groups.test.js +56 -0
  50. package/dist/column-types.d.ts +10 -0
  51. package/dist/column-types.js +63 -0
  52. package/dist/column-types.test.d.ts +1 -0
  53. package/dist/column-types.test.js +62 -0
  54. package/dist/columns.d.ts +30 -0
  55. package/dist/columns.js +277 -0
  56. package/dist/columns.test.d.ts +1 -0
  57. package/dist/columns.test.js +625 -0
  58. package/dist/core.d.ts +86 -1
  59. package/dist/css.d.ts +3 -0
  60. package/dist/editing.d.ts +31 -0
  61. package/dist/editing.js +529 -0
  62. package/dist/editing.test.d.ts +1 -0
  63. package/dist/editing.test.js +732 -0
  64. package/dist/editors/cell-editors.coverage.test.d.ts +1 -0
  65. package/dist/editors/cell-editors.coverage.test.js +139 -0
  66. package/dist/editors/cell-editors.d.ts +1 -1
  67. package/dist/facet-buckets.d.ts +13 -0
  68. package/dist/facet-buckets.js +54 -0
  69. package/dist/facet-buckets.test.d.ts +1 -0
  70. package/dist/facet-buckets.test.js +296 -0
  71. package/dist/features.d.ts +5 -0
  72. package/dist/features.js +30 -0
  73. package/dist/filter-operators.d.ts +16 -0
  74. package/dist/filter-operators.js +69 -0
  75. package/dist/filter-operators.test.d.ts +1 -0
  76. package/dist/filter-operators.test.js +135 -0
  77. package/dist/hyperformula-adapter.d.ts +82 -0
  78. package/dist/hyperformula-adapter.js +73 -0
  79. package/dist/hyperformula-adapter.test.d.ts +1 -0
  80. package/dist/hyperformula-adapter.test.js +205 -0
  81. package/dist/index.d.ts +9 -3
  82. package/dist/index.js +8 -2
  83. package/dist/keyboard-handlers.coverage.test.d.ts +1 -0
  84. package/dist/keyboard-handlers.coverage.test.js +495 -0
  85. package/dist/keyboard-handlers.d.ts +7 -0
  86. package/dist/keyboard-handlers.js +202 -0
  87. package/dist/menus.d.ts +40 -0
  88. package/dist/menus.js +390 -0
  89. package/dist/menus.test.d.ts +1 -0
  90. package/dist/menus.test.js +560 -0
  91. package/dist/named-views.coverage.test.d.ts +1 -0
  92. package/dist/named-views.coverage.test.js +180 -0
  93. package/dist/named-views.d.ts +27 -0
  94. package/dist/named-views.js +39 -0
  95. package/dist/row-drag.d.ts +49 -0
  96. package/dist/row-drag.js +221 -0
  97. package/dist/row-drag.test.d.ts +1 -0
  98. package/dist/row-drag.test.js +142 -0
  99. package/dist/row-resize.d.ts +43 -0
  100. package/dist/row-resize.js +158 -0
  101. package/dist/row-resize.test.d.ts +1 -0
  102. package/dist/row-resize.test.js +329 -0
  103. package/dist/scroll-sync.d.ts +9 -0
  104. package/dist/scroll-sync.js +89 -0
  105. package/dist/scroll-sync.test.d.ts +1 -0
  106. package/dist/scroll-sync.test.js +290 -0
  107. package/dist/selection.d.ts +32 -0
  108. package/dist/selection.js +427 -0
  109. package/dist/selection.multi-range.test.d.ts +1 -0
  110. package/dist/selection.multi-range.test.js +55 -0
  111. package/dist/selection.test.d.ts +1 -0
  112. package/dist/selection.test.js +647 -0
  113. package/dist/server-data-source.coverage.test.d.ts +1 -0
  114. package/dist/server-data-source.coverage.test.js +154 -0
  115. package/dist/spreadsheet.d.ts +110 -0
  116. package/dist/spreadsheet.js +242 -0
  117. package/dist/spreadsheet.test.d.ts +1 -0
  118. package/dist/spreadsheet.test.js +446 -0
  119. package/dist/summaries.d.ts +12 -0
  120. package/dist/summaries.js +65 -0
  121. package/dist/sv-grid-scrollbar.js +13 -1
  122. package/dist/svgrid-wrapper.types.d.ts +31 -1
  123. package/dist/svgrid.behavior.test.js +20 -0
  124. package/dist/svgrid.comments-autocomplete.test.d.ts +1 -0
  125. package/dist/svgrid.comments-autocomplete.test.js +96 -0
  126. package/dist/svgrid.context-menu.test.d.ts +1 -0
  127. package/dist/svgrid.context-menu.test.js +102 -0
  128. package/dist/svgrid.interaction.test.js +31 -0
  129. package/dist/svgrid.new-features.wrapper.test.js +64 -6
  130. package/dist/svgrid.wrapper.test.js +27 -1
  131. package/dist/test-setup.js +9 -3
  132. package/dist/virtualization/column-virtualizer.d.ts +2 -0
  133. package/dist/virtualization/scroll-scaling.d.ts +45 -0
  134. package/dist/virtualization/scroll-scaling.js +99 -0
  135. package/dist/virtualization/scroll-scaling.test.d.ts +1 -0
  136. package/dist/virtualization/scroll-scaling.test.js +127 -0
  137. package/dist/virtualization/svelte-virtualizer.svelte.d.ts +2 -0
  138. package/dist/virtualization/svelte-virtualizer.svelte.js +2 -0
  139. package/dist/virtualization/virtualizer.d.ts +7 -0
  140. package/dist/virtualization/virtualizer.js +30 -0
  141. package/package.json +2 -1
  142. package/src/GridFooter.svelte +164 -0
  143. package/src/GridMenus.svelte +648 -0
  144. package/src/SvGrid.controller.svelte.ts +2352 -0
  145. package/src/SvGrid.css +2012 -0
  146. package/src/SvGrid.helpers.test.ts +415 -0
  147. package/src/SvGrid.helpers.ts +185 -0
  148. package/src/SvGrid.svelte +2346 -8742
  149. package/src/SvGrid.types.ts +537 -0
  150. package/src/SvGridChart.svelte +1060 -23
  151. package/src/aligned-grids.test.ts +80 -0
  152. package/src/aligned-grids.ts +87 -0
  153. package/src/build-api.coverage.test.ts +532 -0
  154. package/src/build-api.ts +683 -0
  155. package/src/cell-render.test.ts +451 -0
  156. package/src/cell-render.ts +426 -0
  157. package/src/cell-values.ts +114 -0
  158. package/src/chart-export.test.ts +370 -0
  159. package/src/chart.coverage.test.ts +814 -0
  160. package/src/chart.ts +1352 -47
  161. package/src/clipboard.test.ts +780 -0
  162. package/src/clipboard.ts +552 -0
  163. package/src/collaboration.coverage.test.ts +220 -0
  164. package/src/column-groups.test.ts +59 -0
  165. package/src/column-groups.ts +80 -0
  166. package/src/column-types.test.ts +68 -0
  167. package/src/column-types.ts +82 -0
  168. package/src/columns.test.ts +702 -0
  169. package/src/columns.ts +419 -0
  170. package/src/core.ts +86 -0
  171. package/src/css.d.ts +3 -0
  172. package/src/editing.test.ts +837 -0
  173. package/src/editing.ts +669 -0
  174. package/src/editors/cell-editors.coverage.test.ts +156 -0
  175. package/src/editors/cell-editors.ts +1 -0
  176. package/src/facet-buckets.test.ts +353 -0
  177. package/src/facet-buckets.ts +67 -0
  178. package/src/features.ts +128 -0
  179. package/src/filter-operators.test.ts +174 -0
  180. package/src/filter-operators.ts +87 -0
  181. package/src/hyperformula-adapter.test.ts +256 -0
  182. package/src/hyperformula-adapter.ts +124 -0
  183. package/src/index.ts +49 -0
  184. package/src/keyboard-handlers.coverage.test.ts +560 -0
  185. package/src/keyboard-handlers.ts +353 -0
  186. package/src/keyboard.ts +97 -97
  187. package/src/menus.test.ts +620 -0
  188. package/src/menus.ts +555 -0
  189. package/src/named-views.coverage.test.ts +210 -0
  190. package/src/named-views.ts +48 -0
  191. package/src/row-drag.test.ts +168 -0
  192. package/src/row-drag.ts +255 -0
  193. package/src/row-resize.test.ts +369 -0
  194. package/src/row-resize.ts +171 -0
  195. package/src/scroll-sync.test.ts +330 -0
  196. package/src/scroll-sync.ts +218 -0
  197. package/src/selection.multi-range.test.ts +61 -0
  198. package/src/selection.test.ts +722 -0
  199. package/src/selection.ts +579 -0
  200. package/src/server-data-source.coverage.test.ts +180 -0
  201. package/src/spreadsheet.test.ts +489 -0
  202. package/src/spreadsheet.ts +304 -0
  203. package/src/summaries.ts +204 -0
  204. package/src/sv-grid-scrollbar.ts +13 -1
  205. package/src/svgrid-wrapper.types.ts +31 -1
  206. package/src/svgrid.behavior.test.ts +22 -0
  207. package/src/svgrid.comments-autocomplete.test.ts +112 -0
  208. package/src/svgrid.context-menu.test.ts +126 -0
  209. package/src/svgrid.interaction.test.ts +30 -0
  210. package/src/svgrid.new-features.wrapper.test.ts +67 -6
  211. package/src/svgrid.wrapper.test.ts +27 -1
  212. package/src/test-setup.ts +9 -6
  213. package/src/virtualization/scroll-scaling.test.ts +148 -0
  214. package/src/virtualization/scroll-scaling.ts +121 -0
  215. package/src/virtualization/svelte-virtualizer.svelte.ts +2 -0
  216. package/src/virtualization/virtualizer.ts +26 -0
@@ -0,0 +1,648 @@
1
+ <script lang="ts" generics="TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData">
2
+ import {
3
+ applyExcelFilter,
4
+ normalizeForFilter,
5
+ createColumnVirtualizer,
6
+ createCoreRowModel,
7
+ createExpandedRowModel,
8
+ createFilteredRowModel,
9
+ createGroupedRowModel,
10
+ createPaginatedRowModel,
11
+ createSvelteVirtualizer,
12
+ createSortedRowModel,
13
+ createSvGrid,
14
+ filterFns,
15
+ getGridCellA11yProps,
16
+ getGridCellDomId,
17
+ getGridHeaderA11yProps,
18
+ getGridRootA11yProps,
19
+ getGridRowA11yProps,
20
+ parseEditorValue,
21
+ normalizeEditorOptions,
22
+ sortFns,
23
+ tableFeatures,
24
+ rowSortingFeature,
25
+ columnFilteringFeature,
26
+ columnGroupingFeature,
27
+ type CellContext,
28
+ type EditorContext,
29
+ type CellEditorOption,
30
+ type CellEditorType,
31
+ type CellFormatter,
32
+ type CellFormatConfig,
33
+ type Column,
34
+ type ColumnDef,
35
+ type Row,
36
+ type RowData,
37
+ type SvGridApi,
38
+ type TableFeatures,
39
+ } from "./index";
40
+ import "./sv-grid-scrollbar";
41
+ import type { Snippet } from "svelte";
42
+ import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
43
+ import {
44
+ formatNumericWithConfig,
45
+ getDateFormatter,
46
+ resolveDatePattern,
47
+ } from "./cell-formatting";
48
+ import {
49
+ RenderSnippetConfig,
50
+ RenderComponentConfig,
51
+ } from "./render-component";
52
+ import { buildFillPattern } from "./fill-patterns";
53
+ import { buildSparkline, toSparklineValues } from "./sparkline";
54
+ import {
55
+ resolveCellFormat,
56
+ computeColumnStat,
57
+ formatsNeedingStats,
58
+ type ColumnStat,
59
+ type ResolvedCellFormat,
60
+ } from "./conditional-formatting";
61
+ import SvGridDropdown from "./SvGridDropdown.svelte";
62
+ import type {
63
+ Props,
64
+ SelectionPoint,
65
+ SelectionRange,
66
+ CellEditState,
67
+ FilterOperator,
68
+ FilterOption,
69
+ MenuPosition,
70
+ } from "./SvGrid.types";
71
+ import {
72
+ cfTextStyle,
73
+ fmtStat,
74
+ getCellKey,
75
+ resolveClassList,
76
+ toDateInputValue,
77
+ toDateTimeLocalInputValue,
78
+ getEditableInputValue,
79
+ getEditorInputType,
80
+ toValueArray,
81
+ getOptionLabel,
82
+ getOptionColor,
83
+ colorfulChipStyle,
84
+ getEditorClass,
85
+ asDate,
86
+ clampMenuX,
87
+ cssEscape,
88
+ rawToNumber,
89
+ formatFacetNumber,
90
+ formatFacetDate,
91
+ } from "./SvGrid.helpers";
92
+
93
+ import type { SvGridController } from "./SvGrid.controller.svelte";
94
+
95
+ let { ctrl, icon }: { ctrl: SvGridController<TFeatures, TData>; icon: Snippet<[string]> } = $props();
96
+
97
+ // View facade: re-bind the controller's reactive members so the markup
98
+ // (moved verbatim from SvGrid.svelte) stays identical.
99
+ const groupingControlsEnabled = $derived(ctrl.groupingControlsEnabled);
100
+ const filterMenuValues = $derived(ctrl.filterMenuValues);
101
+ const tooltip = $derived(ctrl.tooltip);
102
+ const showTooltipFor = $derived(ctrl.showTooltipFor);
103
+ const columnMenuFor = $derived(ctrl.columnMenuFor);
104
+ const columnMenuPos = $derived(ctrl.columnMenuPos);
105
+ const filterMenuFor = $derived(ctrl.filterMenuFor);
106
+ const filterMenuPos = $derived(ctrl.filterMenuPos);
107
+ const operatorMenuFor = $derived(ctrl.operatorMenuFor);
108
+ const operatorMenuPos = $derived(ctrl.operatorMenuPos);
109
+ const chooseColumnsPos = $derived(ctrl.chooseColumnsPos);
110
+ const showColumnFiltersEffective = $derived(ctrl.showColumnFiltersEffective);
111
+ const columnMenuTabsEnabled = $derived(ctrl.props.columnMenuTabs === true);
112
+ const grid = $derived(ctrl.grid);
113
+ const allColumns = $derived(ctrl.allColumns);
114
+ const isColumnPinned = $derived(ctrl.isColumnPinned);
115
+ const pinColumnLeft = $derived(ctrl.pinColumnLeft);
116
+ const pinColumnRight = $derived(ctrl.pinColumnRight);
117
+ const unpinColumn = $derived(ctrl.unpinColumn);
118
+ const sortDirectionByColumn = $derived(ctrl.sortDirectionByColumn);
119
+ const groupingColumns = $derived(ctrl.groupingColumns);
120
+ const columnVirtualizationEnabled = $derived(ctrl.columnVirtualizationEnabled);
121
+ const updateFilterOperator = $derived(ctrl.updateFilterOperator);
122
+ const updateFilterMenuValue = $derived(ctrl.updateFilterMenuValue);
123
+ const updateFilterMenuValueTo = $derived(ctrl.updateFilterMenuValueTo);
124
+ const operatorsForColumn = $derived(ctrl.operatorsForColumn);
125
+ const defaultOperatorFor = $derived(ctrl.defaultOperatorFor);
126
+ const operatorLabelFor = $derived(ctrl.operatorLabelFor);
127
+
128
+ // Patch a column's menu filter (used for the optional 2nd condition + join
129
+ // of multi-condition filtering). Merges into the existing entry, creating a
130
+ // base entry from the column's default operator when none exists yet.
131
+ function patchFilter(columnId: string, patch: Record<string, unknown>) {
132
+ const col = allColumns.find((c) => c.id === columnId);
133
+ const cur = ctrl.filterMenuValues[columnId] ?? { operator: defaultOperatorFor(col), value: "" };
134
+ ctrl.filterMenuValues = { ...ctrl.filterMenuValues, [columnId]: { ...cur, ...patch } };
135
+ }
136
+ const isColumnFiltered = $derived(ctrl.isColumnFiltered);
137
+ const closeMenus = $derived(ctrl.closeMenus);
138
+ const autosizeColumn = $derived(ctrl.autosizeColumn);
139
+ const autosizeAllColumns = $derived(ctrl.autosizeAllColumns);
140
+ const resetColumns = $derived(ctrl.resetColumns);
141
+ const openChooseColumns = $derived(ctrl.openChooseColumns);
142
+ const sortColumnFromMenu = $derived(ctrl.sortColumnFromMenu);
143
+ const clearColumnSort = $derived(ctrl.clearColumnSort);
144
+ const groupByColumnFromMenu = $derived(ctrl.groupByColumnFromMenu);
145
+ const clearGroupingFromMenu = $derived(ctrl.clearGroupingFromMenu);
146
+ const columnMenuFacetValues = $derived(ctrl.columnMenuFacetValues);
147
+ const columnMenuVisibleFacets = $derived(ctrl.columnMenuVisibleFacets);
148
+ const isFacetChecked = $derived(ctrl.isFacetChecked);
149
+ const toggleFacetValue = $derived(ctrl.toggleFacetValue);
150
+ const isAllFacetsChecked = $derived(ctrl.isAllFacetsChecked);
151
+ const toggleAllFacets = $derived(ctrl.toggleAllFacets);
152
+ const clearColumnFilter = $derived(ctrl.clearColumnFilter);
153
+ const contextMenuFor = $derived(ctrl.contextMenuFor);
154
+ const contextMenuPos = $derived(ctrl.contextMenuPos);
155
+ const contextMenuItems = $derived(ctrl.contextMenuItems);
156
+ const closeContextMenu = $derived(ctrl.closeContextMenu);
157
+ const commentEditFor = $derived(ctrl.commentEditFor);
158
+ const saveComment = $derived(ctrl.saveComment);
159
+ const removeComment = $derived(ctrl.removeComment);
160
+ const closeCommentEditor = $derived(ctrl.closeCommentEditor);
161
+ </script>
162
+
163
+ {#if tooltip}
164
+ <!-- Custom hover tooltip. Floats above the grid; positioned by
165
+ showTooltipFor(). Notes and column tooltips both surface here. -->
166
+ <div
167
+ class="sv-grid-tooltip"
168
+ class:sv-grid-tooltip-above={!tooltip.below}
169
+ style={`left: ${tooltip.x}px; top: ${tooltip.y}px; ${tooltip.below ? '' : 'transform: translateY(-100%);'}`}
170
+ role="tooltip"
171
+ >{tooltip.text}</div>
172
+ {/if}
173
+
174
+ {#if columnMenuFor || filterMenuFor || operatorMenuFor || chooseColumnsPos}
175
+ <div
176
+ class="sv-grid-menu-backdrop"
177
+ role="presentation"
178
+ onclick={closeMenus}
179
+ ></div>
180
+ {/if}
181
+
182
+ <!-- Shared filter UI: rendered both in the funnel popover and in the column
183
+ menu's Filter tab, so the two never diverge. -->
184
+ {#snippet filterPanelBody(colId: string)}
185
+ {@const menuColumn = allColumns.find((c) => c.id === colId)}
186
+ {@const menuOperatorOptions = operatorsForColumn(menuColumn)}
187
+ {@const menuActiveOperator = filterMenuValues[colId]?.operator ?? defaultOperatorFor(menuColumn)}
188
+ {@const menuInputType = getEditorInputType((menuColumn?.columnDef.editorType ?? "text") as CellEditorType)}
189
+ <div class="sv-grid-menu-filter">
190
+ <div class="sv-grid-menu-filter-head">
191
+ <span class="sv-grid-header-icon">{@render icon("filter")}</span> Filter
192
+ condition
193
+ </div>
194
+ <select
195
+ class="sv-grid-menu-operator-select"
196
+ aria-label="Filter condition"
197
+ value={menuActiveOperator}
198
+ onchange={(event) =>
199
+ updateFilterOperator(colId, (event.currentTarget as HTMLSelectElement).value as FilterOperator)}
200
+ >
201
+ {#each menuOperatorOptions as option (option.value)}
202
+ <option value={option.value}>{operatorLabelFor(option, menuColumn)}</option>
203
+ {/each}
204
+ </select>
205
+ {#if menuActiveOperator !== "isBlank"}
206
+ <input
207
+ class="sv-grid-menu-condition-value"
208
+ type={menuInputType}
209
+ value={filterMenuValues[colId]?.value ?? ""}
210
+ placeholder={menuActiveOperator === "between" ? "From" : "Filter value..."}
211
+ oninput={(event) => updateFilterMenuValue(colId, (event.currentTarget as HTMLInputElement).value)}
212
+ />
213
+ {#if menuActiveOperator === "between"}
214
+ <input
215
+ class="sv-grid-menu-condition-value"
216
+ type={menuInputType}
217
+ value={filterMenuValues[colId]?.valueTo ?? ""}
218
+ placeholder="To"
219
+ oninput={(event) => updateFilterMenuValueTo(colId, (event.currentTarget as HTMLInputElement).value)}
220
+ />
221
+ {/if}
222
+ {@const mf = filterMenuValues[colId]}
223
+ {#if mf?.operator2}
224
+ {@const op2 = mf.operator2 ?? defaultOperatorFor(menuColumn)}
225
+ <div class="sv-grid-menu-join" role="radiogroup" aria-label="Combine conditions">
226
+ <button type="button" class:is-on={(mf.join ?? "AND") === "AND"}
227
+ onclick={() => patchFilter(colId, { join: "AND" })}>AND</button>
228
+ <button type="button" class:is-on={mf.join === "OR"}
229
+ onclick={() => patchFilter(colId, { join: "OR" })}>OR</button>
230
+ <button type="button" class="sv-grid-menu-join-x" title="Remove second condition"
231
+ aria-label="Remove second condition"
232
+ onclick={() => patchFilter(colId, { operator2: undefined, value2: undefined, valueTo2: undefined, join: undefined })}>×</button>
233
+ </div>
234
+ <select
235
+ class="sv-grid-menu-operator-select"
236
+ aria-label="Second filter condition"
237
+ value={op2}
238
+ onchange={(event) =>
239
+ patchFilter(colId, { operator2: (event.currentTarget as HTMLSelectElement).value as FilterOperator })}
240
+ >
241
+ {#each menuOperatorOptions as option (option.value)}
242
+ <option value={option.value}>{operatorLabelFor(option, menuColumn)}</option>
243
+ {/each}
244
+ </select>
245
+ {#if op2 !== "isBlank"}
246
+ <input
247
+ class="sv-grid-menu-condition-value"
248
+ type={menuInputType}
249
+ value={mf.value2 ?? ""}
250
+ placeholder={op2 === "between" ? "From" : "Filter value..."}
251
+ oninput={(event) => patchFilter(colId, { value2: (event.currentTarget as HTMLInputElement).value })}
252
+ />
253
+ {#if op2 === "between"}
254
+ <input
255
+ class="sv-grid-menu-condition-value"
256
+ type={menuInputType}
257
+ value={mf.valueTo2 ?? ""}
258
+ placeholder="To"
259
+ oninput={(event) => patchFilter(colId, { valueTo2: (event.currentTarget as HTMLInputElement).value })}
260
+ />
261
+ {/if}
262
+ {/if}
263
+ {:else}
264
+ <button type="button" class="sv-grid-menu-add-cond"
265
+ onclick={() => patchFilter(colId, { operator2: defaultOperatorFor(menuColumn), value2: "", join: "AND" })}>
266
+ + Add condition
267
+ </button>
268
+ {/if}
269
+ {/if}
270
+ <div class="sv-grid-menu-sep"></div>
271
+ <div class="sv-grid-menu-filter-head">Values</div>
272
+ <input
273
+ class="sv-grid-menu-search"
274
+ placeholder="Search values..."
275
+ bind:value={ctrl.columnMenuSearch}
276
+ />
277
+ <label class="sv-grid-facet sv-grid-facet-all">
278
+ <input type="checkbox" checked={isAllFacetsChecked(colId)} onchange={() => toggleAllFacets(colId)} />
279
+ <span class="sv-grid-facet-label">(Select all)</span>
280
+ </label>
281
+ <div class="sv-grid-facet-list">
282
+ {#each columnMenuVisibleFacets as value (value)}
283
+ <label class="sv-grid-facet">
284
+ <input type="checkbox" checked={isFacetChecked(colId, value)} onchange={() => toggleFacetValue(colId, value)} />
285
+ <span class="sv-grid-facet-label">{value === "" ? "(Blanks)" : value}</span>
286
+ </label>
287
+ {:else}
288
+ <div class="sv-grid-facet-empty">No values</div>
289
+ {/each}
290
+ </div>
291
+ {#if columnMenuFacetValues.length > columnMenuVisibleFacets.length}
292
+ <div class="sv-grid-facet-note">
293
+ Showing {columnMenuVisibleFacets.length} of {columnMenuFacetValues.length}
294
+ </div>
295
+ {/if}
296
+ <div class="sv-grid-menu-actions">
297
+ <button
298
+ type="button"
299
+ class="sv-grid-menu-btn"
300
+ disabled={!isColumnFiltered(colId)}
301
+ onclick={() => clearColumnFilter(colId)}
302
+ >
303
+ Clear filter
304
+ </button>
305
+ <button type="button" class="sv-grid-menu-btn sv-grid-menu-btn-primary" onclick={closeMenus}>
306
+ Done
307
+ </button>
308
+ </div>
309
+ </div>
310
+ {/snippet}
311
+
312
+ {#if columnMenuFor}
313
+ {@const menuColumnId = columnMenuFor}
314
+ {@const menuCol = allColumns.find((c) => c.id === menuColumnId)}
315
+ {@const menuCanSort = menuCol?.getCanSort?.() ?? false}
316
+ {@const menuCanFilter = (menuCol?.columnDef.field != null) && (menuCol?.columnDef.filterable !== false)}
317
+ {@const menuTab = ctrl.columnMenuTab}
318
+ <div
319
+ class="sv-grid-menu sv-grid-column-menu"
320
+ role="menu"
321
+ style={`left: ${columnMenuPos.x}px; top: ${columnMenuPos.y}px; max-height: calc(100vh - ${columnMenuPos.y}px - 12px);`}
322
+ >
323
+ {#if columnMenuTabsEnabled}
324
+ <div class="sv-grid-menu-tabs" role="tablist">
325
+ <button type="button" class="sv-grid-menu-tab" class:is-active={menuTab === "general"}
326
+ role="tab" aria-selected={menuTab === "general"}
327
+ onclick={() => (ctrl.columnMenuTab = "general")}>General</button>
328
+ {#if menuCanFilter && showColumnFiltersEffective}
329
+ <button type="button" class="sv-grid-menu-tab" class:is-active={menuTab === "filter"}
330
+ role="tab" aria-selected={menuTab === "filter"}
331
+ onclick={() => (ctrl.columnMenuTab = "filter")}>Filter</button>
332
+ {/if}
333
+ <button type="button" class="sv-grid-menu-tab" class:is-active={menuTab === "columns"}
334
+ role="tab" aria-selected={menuTab === "columns"}
335
+ onclick={() => (ctrl.columnMenuTab = "columns")}>Columns</button>
336
+ </div>
337
+ {/if}
338
+ {#if columnMenuTabsEnabled && menuTab === "filter" && menuCanFilter && showColumnFiltersEffective}
339
+ {@render filterPanelBody(menuColumnId)}
340
+ {:else if columnMenuTabsEnabled && menuTab === "columns"}
341
+ <div class="sv-grid-menu-filter-head">Visible columns</div>
342
+ <div class="sv-grid-facet-list">
343
+ {#each grid.getAllColumns() as column (column.id)}
344
+ <label class="sv-grid-facet">
345
+ <input
346
+ type="checkbox"
347
+ checked={!ctrl.hiddenColumns[column.id]}
348
+ onchange={(event) => {
349
+ const visible = (event.currentTarget as HTMLInputElement).checked;
350
+ if (visible) {
351
+ const next = { ...ctrl.hiddenColumns };
352
+ delete next[column.id];
353
+ ctrl.hiddenColumns = next;
354
+ } else {
355
+ ctrl.hiddenColumns = { ...ctrl.hiddenColumns, [column.id]: true };
356
+ }
357
+ }}
358
+ />
359
+ <span class="sv-grid-facet-label"
360
+ >{typeof column.columnDef.header === "string" ? column.columnDef.header : column.id}</span
361
+ >
362
+ </label>
363
+ {/each}
364
+ </div>
365
+ {:else}
366
+ {#if menuCanSort}
367
+ <button
368
+ type="button"
369
+ class="sv-grid-menu-item"
370
+ role="menuitem"
371
+ onclick={() => sortColumnFromMenu(menuColumnId, false)}
372
+ >
373
+ <span class="sv-grid-header-icon">{@render icon("sort-asc")}</span> Sort
374
+ ascending
375
+ </button>
376
+ <button
377
+ type="button"
378
+ class="sv-grid-menu-item"
379
+ role="menuitem"
380
+ onclick={() => sortColumnFromMenu(menuColumnId, true)}
381
+ >
382
+ <span class="sv-grid-header-icon">{@render icon("sort-desc")}</span> Sort
383
+ descending
384
+ </button>
385
+ <button
386
+ type="button"
387
+ class="sv-grid-menu-item"
388
+ role="menuitem"
389
+ disabled={!sortDirectionByColumn[menuColumnId]}
390
+ onclick={() => clearColumnSort(menuColumnId)}
391
+ >
392
+ <span class="sv-grid-header-icon">{@render icon("x")}</span> Remove sort
393
+ </button>
394
+ {/if}
395
+ {#if !columnVirtualizationEnabled}
396
+ {@const menuPinSide = isColumnPinned(menuColumnId)}
397
+ <div class="sv-grid-menu-sep"></div>
398
+ <button
399
+ type="button"
400
+ class="sv-grid-menu-item"
401
+ role="menuitem"
402
+ disabled={menuPinSide === "left"}
403
+ onclick={() => pinColumnLeft(menuColumnId)}
404
+ >
405
+ <span class="sv-grid-header-icon"
406
+ >{@render icon("op-startsWith")}</span
407
+ > Pin to left
408
+ </button>
409
+ <button
410
+ type="button"
411
+ class="sv-grid-menu-item"
412
+ role="menuitem"
413
+ disabled={menuPinSide === "right"}
414
+ onclick={() => pinColumnRight(menuColumnId)}
415
+ >
416
+ <span class="sv-grid-header-icon"
417
+ >{@render icon("op-greaterThan")}</span
418
+ > Pin to right
419
+ </button>
420
+ <button
421
+ type="button"
422
+ class="sv-grid-menu-item"
423
+ role="menuitem"
424
+ disabled={!menuPinSide}
425
+ onclick={() => unpinColumn(menuColumnId)}
426
+ >
427
+ <span class="sv-grid-header-icon">{@render icon("x")}</span> Unpin column
428
+ </button>
429
+ {/if}
430
+ <div class="sv-grid-menu-sep"></div>
431
+ <button
432
+ type="button"
433
+ class="sv-grid-menu-item"
434
+ role="menuitem"
435
+ onclick={() => {
436
+ autosizeColumn(menuColumnId);
437
+ closeMenus();
438
+ }}
439
+ >
440
+ <span class="sv-grid-header-icon">{@render icon("autosize")}</span> Autosize
441
+ this column
442
+ </button>
443
+ <button
444
+ type="button"
445
+ class="sv-grid-menu-item"
446
+ role="menuitem"
447
+ onclick={() => {
448
+ autosizeAllColumns();
449
+ closeMenus();
450
+ }}
451
+ >
452
+ <span class="sv-grid-header-icon">{@render icon("autosize")}</span> Autosize
453
+ all columns
454
+ </button>
455
+ {#if groupingControlsEnabled}
456
+ <div class="sv-grid-menu-sep"></div>
457
+ <button
458
+ type="button"
459
+ class="sv-grid-menu-item"
460
+ role="menuitem"
461
+ disabled={groupingColumns.includes(menuColumnId)}
462
+ onclick={() => groupByColumnFromMenu(menuColumnId)}
463
+ >
464
+ <span class="sv-grid-header-icon">{@render icon("group")}</span> Group
465
+ by this column
466
+ </button>
467
+ <button
468
+ type="button"
469
+ class="sv-grid-menu-item"
470
+ role="menuitem"
471
+ disabled={!groupingColumns.includes(menuColumnId)}
472
+ onclick={() => clearGroupingFromMenu(menuColumnId)}
473
+ >
474
+ <span class="sv-grid-header-icon">{@render icon("x")}</span> Remove grouping
475
+ </button>
476
+ {/if}
477
+ <div class="sv-grid-menu-sep"></div>
478
+ {#if !columnMenuTabsEnabled}
479
+ <!-- Flat menu: reach the column chooser via a submenu (tabbed mode has
480
+ a Columns tab instead). -->
481
+ <button
482
+ type="button"
483
+ class="sv-grid-menu-item"
484
+ class:is-open={chooseColumnsPos !== null}
485
+ role="menuitem"
486
+ aria-haspopup="menu"
487
+ aria-expanded={chooseColumnsPos !== null}
488
+ onclick={(event) => openChooseColumns(event)}
489
+ >
490
+ <span class="sv-grid-header-icon">{@render icon("columns")}</span>
491
+ Choose columns
492
+ <span class="sv-grid-header-icon sv-grid-menu-item-chevron">{@render icon("chevron-down")}</span>
493
+ </button>
494
+ {/if}
495
+ <button
496
+ type="button"
497
+ class="sv-grid-menu-item"
498
+ role="menuitem"
499
+ onclick={() => {
500
+ resetColumns();
501
+ closeMenus();
502
+ }}
503
+ >
504
+ <span class="sv-grid-header-icon">{@render icon("reset")}</span> Reset columns
505
+ </button>
506
+ {/if}
507
+ </div>
508
+ {/if}
509
+
510
+ {#if filterMenuFor && showColumnFiltersEffective}
511
+ <div
512
+ class="sv-grid-menu sv-grid-filter-menu"
513
+ role="menu"
514
+ style={`left: ${filterMenuPos.x}px; top: ${filterMenuPos.y}px; max-height: calc(100vh - ${filterMenuPos.y}px - 12px);`}
515
+ >
516
+ {@render filterPanelBody(filterMenuFor)}
517
+ </div>
518
+ {/if}
519
+
520
+ {#if chooseColumnsPos}
521
+ {@const everyColumn = grid.getAllColumns()}
522
+ <div
523
+ class="sv-grid-menu sv-grid-choose-columns-menu"
524
+ role="menu"
525
+ style={`left: ${chooseColumnsPos.x}px; top: ${chooseColumnsPos.y}px;`}
526
+ >
527
+ <div class="sv-grid-menu-filter-head">Visible columns</div>
528
+ <div class="sv-grid-facet-list">
529
+ {#each everyColumn as column (column.id)}
530
+ <label class="sv-grid-facet">
531
+ <input
532
+ type="checkbox"
533
+ checked={!ctrl.hiddenColumns[column.id]}
534
+ onchange={(event) => {
535
+ const visible = (event.currentTarget as HTMLInputElement)
536
+ .checked;
537
+ if (visible) {
538
+ const next = { ...ctrl.hiddenColumns };
539
+ delete next[column.id];
540
+ ctrl.hiddenColumns = next;
541
+ } else {
542
+ ctrl.hiddenColumns = { ...ctrl.hiddenColumns, [column.id]: true };
543
+ }
544
+ }}
545
+ />
546
+ <span class="sv-grid-facet-label"
547
+ >{typeof column.columnDef.header === "string"
548
+ ? column.columnDef.header
549
+ : column.id}</span
550
+ >
551
+ </label>
552
+ {/each}
553
+ </div>
554
+ </div>
555
+ {/if}
556
+
557
+ {#if operatorMenuFor}
558
+ {@const opColumnId = operatorMenuFor}
559
+ {@const opColumn = allColumns.find((c) => c.id === opColumnId)}
560
+ {@const opOptions = operatorsForColumn(opColumn)}
561
+ {@const opActive =
562
+ filterMenuValues[opColumnId]?.operator ?? defaultOperatorFor(opColumn)}
563
+ <div
564
+ class="sv-grid-menu sv-grid-operator-menu"
565
+ role="menu"
566
+ style={`left: ${operatorMenuPos.x}px; top: ${operatorMenuPos.y}px;`}
567
+ >
568
+ {#each opOptions as option (option.value)}
569
+ <button
570
+ type="button"
571
+ class="sv-grid-menu-item"
572
+ role="menuitemradio"
573
+ aria-checked={opActive === option.value}
574
+ onclick={() => {
575
+ updateFilterOperator(opColumnId, option.value);
576
+ closeMenus();
577
+ }}
578
+ >
579
+ <span class="sv-grid-header-icon"
580
+ >{@render icon(option.iconName)}</span
581
+ >
582
+ {operatorLabelFor(option, opColumn)}
583
+ </button>
584
+ {/each}
585
+ </div>
586
+ {/if}
587
+
588
+ {#if contextMenuFor}
589
+ <div
590
+ class="sv-grid-menu-backdrop"
591
+ role="presentation"
592
+ oncontextmenu={(e) => { e.preventDefault(); closeContextMenu(); }}
593
+ onclick={closeContextMenu}
594
+ ></div>
595
+ <div
596
+ class="sv-grid-menu sv-grid-context-menu"
597
+ role="menu"
598
+ style={`left: ${contextMenuPos.x}px; top: ${contextMenuPos.y}px;`}
599
+ >
600
+ {#each contextMenuItems() as item (item.key)}
601
+ {#if item.separator}
602
+ <div class="sv-grid-menu-sep"></div>
603
+ {:else}
604
+ <button
605
+ type="button"
606
+ class="sv-grid-menu-item"
607
+ role="menuitem"
608
+ disabled={item.disabled}
609
+ onclick={() => { item.run?.(); closeContextMenu(); }}
610
+ >{item.label}</button>
611
+ {/if}
612
+ {/each}
613
+ </div>
614
+ {/if}
615
+
616
+ {#if commentEditFor}
617
+ <div
618
+ class="sv-grid-menu-backdrop"
619
+ role="presentation"
620
+ onclick={closeCommentEditor}
621
+ ></div>
622
+ <div
623
+ class="sv-grid-menu sv-grid-comment-editor"
624
+ role="dialog"
625
+ aria-label="Edit comment"
626
+ style={`left: ${commentEditFor.x}px; top: ${commentEditFor.y}px;`}
627
+ >
628
+ <!-- svelte-ignore a11y_autofocus -->
629
+ <textarea
630
+ class="sv-grid-comment-textarea"
631
+ rows="3"
632
+ placeholder="Add a comment…"
633
+ autofocus
634
+ bind:value={ctrl.commentDraft}
635
+ onkeydown={(e) => {
636
+ e.stopPropagation();
637
+ if (e.key === "Escape") { e.preventDefault(); closeCommentEditor(); }
638
+ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); saveComment(); }
639
+ }}
640
+ ></textarea>
641
+ <div class="sv-grid-comment-actions">
642
+ <button type="button" class="sv-grid-comment-remove" onclick={removeComment}>Remove</button>
643
+ <span class="sv-grid-comment-spacer"></span>
644
+ <button type="button" class="sv-grid-comment-cancel" onclick={closeCommentEditor}>Cancel</button>
645
+ <button type="button" class="sv-grid-comment-save" onclick={saveComment}>Save</button>
646
+ </div>
647
+ </div>
648
+ {/if}
@@ -0,0 +1,31 @@
1
+ import { type RowData, type TableFeatures } from "./index";
2
+ import "./sv-grid-scrollbar";
3
+ import type { Snippet } from "svelte";
4
+ import type { SvGridController } from "./SvGrid.controller.svelte";
5
+ declare function $$render<TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData>(): {
6
+ props: {
7
+ ctrl: SvGridController<TFeatures, TData>;
8
+ icon: Snippet<[string]>;
9
+ };
10
+ exports: {};
11
+ bindings: "";
12
+ slots: {};
13
+ events: {};
14
+ };
15
+ declare class __sveltets_Render<TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData> {
16
+ props(): ReturnType<typeof $$render<TFeatures, TData>>['props'];
17
+ events(): ReturnType<typeof $$render<TFeatures, TData>>['events'];
18
+ slots(): ReturnType<typeof $$render<TFeatures, TData>>['slots'];
19
+ bindings(): "";
20
+ exports(): {};
21
+ }
22
+ interface $$IsomorphicComponent {
23
+ new <TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TFeatures, TData>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TFeatures, TData>['props']>, ReturnType<__sveltets_Render<TFeatures, TData>['events']>, ReturnType<__sveltets_Render<TFeatures, TData>['slots']>> & {
24
+ $$bindings?: ReturnType<__sveltets_Render<TFeatures, TData>['bindings']>;
25
+ } & ReturnType<__sveltets_Render<TFeatures, TData>['exports']>;
26
+ <TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData>(internal: unknown, props: ReturnType<__sveltets_Render<TFeatures, TData>['props']> & {}): ReturnType<__sveltets_Render<TFeatures, TData>['exports']>;
27
+ z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
28
+ }
29
+ declare const GridMenus: $$IsomorphicComponent;
30
+ type GridMenus<TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData> = InstanceType<typeof GridMenus<TFeatures, TData>>;
31
+ export default GridMenus;