@svgrid/grid 2.6.22 → 2.7.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.
- package/CHANGELOG.md +88 -88
- package/README.md +199 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +181 -181
- package/dist/GridMenus.svelte +184 -6
- package/dist/SvAutoComplete.svelte +169 -165
- package/dist/SvAvatar.svelte +75 -75
- package/dist/SvCalendar.svelte +503 -503
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvCheckBox.svelte +102 -102
- package/dist/SvCircularProgress.svelte +109 -109
- package/dist/SvColorInput.svelte +181 -181
- package/dist/SvComboBox.svelte +279 -275
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +163 -163
- package/dist/SvDateRangeInput.svelte +25 -3
- package/dist/SvDrawer.svelte +254 -254
- package/dist/SvDropDownList.svelte +378 -374
- package/dist/SvDurationInput.svelte +126 -126
- package/dist/SvField.svelte +293 -293
- package/dist/SvForm.svelte +437 -437
- package/dist/SvGrid.controller.svelte.d.ts +48 -1
- package/dist/SvGrid.controller.svelte.js +214 -52
- package/dist/SvGrid.css +221 -0
- package/dist/SvGrid.helpers.d.ts +55 -0
- package/dist/SvGrid.helpers.js +83 -0
- package/dist/SvGrid.svelte +145 -0
- package/dist/SvGrid.types.d.ts +105 -0
- package/dist/SvGridCellEditor.svelte +744 -662
- package/dist/SvGridChart.svelte +1716 -1716
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridChartView.svelte +70 -70
- package/dist/SvGridDropdown.svelte +728 -728
- package/dist/SvGridSelect.svelte +270 -266
- package/dist/SvGridSelect.svelte.d.ts +1 -1
- package/dist/SvGroupCell.svelte +116 -116
- package/dist/SvListBox.svelte +334 -326
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +293 -289
- package/dist/SvMultiSelect.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte +172 -168
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +133 -133
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvProgress.svelte +116 -116
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRating.svelte +112 -112
- package/dist/SvResult.svelte +73 -73
- package/dist/SvRichCell.svelte +125 -0
- package/dist/SvRichCell.svelte.d.ts +15 -0
- package/dist/SvRichText.svelte +211 -211
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +203 -203
- package/dist/SvSwitchButton.svelte +108 -108
- package/dist/SvTagsInput.svelte +115 -115
- package/dist/SvTextArea.svelte +5 -1
- package/dist/SvTextInput.svelte +147 -147
- package/dist/SvTimePicker.svelte +245 -245
- package/dist/SvToaster.svelte +159 -159
- package/dist/SvToggleButton.svelte +85 -85
- package/dist/SvTooltip.svelte +161 -161
- package/dist/SvTour.svelte +208 -208
- package/dist/SvTree.svelte +444 -444
- package/dist/SvTreeSelect.svelte +239 -235
- package/dist/SvTreeSelect.svelte.d.ts +1 -1
- package/dist/builtin-editors.d.ts +2 -2
- package/dist/builtin-editors.js +19 -3
- package/dist/cdn/GridMenus-C4oLsJyj.js +600 -0
- package/dist/cdn/GridMenus-yEivjNwl.js +601 -0
- package/dist/cdn/SvDateRangeInput-Bh0A0JkF.js +199 -0
- package/dist/cdn/SvDateRangeInput-DflbiP7N.js +198 -0
- package/dist/cdn/SvDateTimePicker-BWwpfB_o.js +812 -0
- package/dist/cdn/SvDateTimePicker-Bivn8dAP.js +816 -0
- package/dist/cdn/SvGridCellEditor-AXL8cHGO.js +550 -0
- package/dist/cdn/SvGridCellEditor-Ba7rY3eu.js +549 -0
- package/dist/cdn/{SvGridChart-BwVos976.js → SvGridChart-Bs2GIR2Q.js} +1 -1
- package/dist/cdn/{SvGridChart-BEJmNNx9.js → SvGridChart-CwhFz7GV.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-BOknOkrP.js → SvGridChartPanel-1WXuStzM.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-D2PjII4O.js → SvGridChartPanel-e5_nqNP9.js} +1 -1
- package/dist/cdn/{SvGridChartView-00LPUHL1.js → SvGridChartView-SmPW10dI.js} +1 -1
- package/dist/cdn/{SvGridChartView-BhUEJ5ki.js → SvGridChartView-eSPuJE6g.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D0VdjeR8.js → SvGridDropdown-B1ZcLpgs.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D13MtJ2j.js → SvGridDropdown-CtdbHcIS.js} +1 -1
- package/dist/cdn/date-format-CcP1tafP.js +1395 -0
- package/dist/cdn/date-format-DZT7T1wf.js +1393 -0
- package/dist/cdn/{editor-registry-CiI0xlKg.js → editor-registry-BhgE3S84.js} +51 -25
- package/dist/cdn/{src-C9Hihx1W.js → src-COC0vnpg.js} +7233 -6701
- package/dist/cdn/{src-D1lXwq1l.js → src-pYGV2rMR.js} +5747 -5215
- package/dist/cdn/svgrid.js +15 -13
- package/dist/cdn/svgrid.svelte-external.js +15 -13
- package/dist/cell-values.d.ts +16 -0
- package/dist/cell-values.js +28 -0
- package/dist/chart-export.js +8 -8
- package/dist/clipboard.d.ts +22 -0
- package/dist/clipboard.js +405 -27
- package/dist/editing.js +12 -7
- package/dist/editors/cell-editors.d.ts +11 -0
- package/dist/editors/cell-editors.js +18 -1
- package/dist/grid-messages.d.ts +15 -0
- package/dist/grid-messages.js +15 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/list-option.d.ts +6 -0
- package/dist/menus.js +40 -7
- package/dist/sanitize-html.d.ts +37 -0
- package/dist/sanitize-html.js +234 -0
- package/dist/selection-bar-view.svelte.d.ts +25 -0
- package/dist/selection-bar-view.svelte.js +13 -0
- package/dist/selection.d.ts +2 -1
- package/dist/selection.js +129 -13
- package/package.json +11 -11
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +181 -181
- package/src/GridMenus.svelte +184 -6
- package/src/SvAutoComplete.svelte +169 -165
- package/src/SvAvatar.svelte +75 -75
- package/src/SvCalendar.svelte +503 -503
- package/src/SvCalendar.test.ts +226 -226
- package/src/SvCarousel.svelte +141 -141
- package/src/SvCheckBox.svelte +102 -102
- package/src/SvCircularProgress.svelte +109 -109
- package/src/SvColorInput.svelte +181 -181
- package/src/SvComboBox.svelte +279 -275
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +163 -163
- package/src/SvDateRangeInput.svelte +25 -3
- package/src/SvDrawer.svelte +254 -254
- package/src/SvDropDownList.svelte +378 -374
- package/src/SvDurationInput.svelte +126 -126
- package/src/SvField.svelte +293 -293
- package/src/SvForm.svelte +437 -437
- package/src/SvForm.test.ts +411 -411
- package/src/SvGrid.controller.svelte.ts +251 -61
- package/src/SvGrid.css +221 -0
- package/src/SvGrid.helpers.ts +87 -0
- package/src/SvGrid.svelte +145 -0
- package/src/SvGrid.types.ts +2092 -1980
- package/src/SvGridCellEditor.svelte +744 -662
- package/src/SvGridChart.svelte +1716 -1716
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridChartView.svelte +70 -70
- package/src/SvGridDropdown.svelte +728 -728
- package/src/SvGridSelect.svelte +270 -266
- package/src/SvGroupCell.svelte +116 -116
- package/src/SvListBox.svelte +334 -326
- package/src/SvMaskedInput.svelte +122 -122
- package/src/SvMenu.svelte +124 -124
- package/src/SvMenu.test.ts +97 -97
- package/src/SvMenuList.svelte +146 -146
- package/src/SvMultiSelect.svelte +293 -289
- package/src/SvNumberInput.svelte +172 -168
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +133 -133
- package/src/SvPopover.svelte +197 -197
- package/src/SvProgress.svelte +116 -116
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRating.svelte +112 -112
- package/src/SvResult.svelte +73 -73
- package/src/SvRichCell.svelte +125 -0
- package/src/SvRichText.svelte +211 -211
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +203 -203
- package/src/SvSwitchButton.svelte +108 -108
- package/src/SvTagsInput.svelte +115 -115
- package/src/SvTextArea.svelte +5 -1
- package/src/SvTextInput.svelte +147 -147
- package/src/SvTimePicker.svelte +245 -245
- package/src/SvToaster.svelte +159 -159
- package/src/SvToaster.test.ts +95 -95
- package/src/SvToggleButton.svelte +85 -85
- package/src/SvTooltip.svelte +161 -161
- package/src/SvTour.svelte +208 -208
- package/src/SvTree.svelte +444 -444
- package/src/SvTreeSelect.svelte +239 -235
- package/src/a11y/dismissable.test.ts +119 -119
- package/src/a11y/dismissable.ts +114 -114
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/ai.test.ts +502 -502
- package/src/ai.ts +1419 -1419
- package/src/build-api.coverage.test.ts +633 -633
- package/src/build-api.ts +846 -846
- package/src/builtin-editors.grid.test.ts +83 -83
- package/src/builtin-editors.test.ts +18 -1
- package/src/builtin-editors.ts +19 -3
- package/src/cell-formatting.ts +171 -171
- package/src/cell-render.test.ts +513 -513
- package/src/cell-render.ts +496 -496
- package/src/cell-values.ts +148 -118
- package/src/chart-export.ts +202 -202
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2321 -2321
- package/src/clipboard.test.ts +93 -20
- package/src/clipboard.ts +433 -33
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/column-groups.ts +78 -78
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1865 -1865
- package/src/createAutocomplete.svelte.ts +132 -132
- package/src/createCombobox.svelte.ts +191 -191
- package/src/createCountryInput.svelte.ts +157 -157
- package/src/createDropdownList.svelte.ts +168 -168
- package/src/createForm.svelte.ts +386 -386
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/createListbox.svelte.ts +250 -250
- package/src/createMenu.svelte.ts +224 -224
- package/src/createPopoverSelect.svelte.ts +213 -213
- package/src/createSlider.svelte.ts +191 -191
- package/src/createTooltip.svelte.ts +144 -144
- package/src/createTree.svelte.ts +322 -322
- package/src/date-string-column.test.ts +87 -0
- package/src/datetime/date-core.ts +206 -206
- package/src/datetime/date-restrict.ts +61 -61
- package/src/datetime/timezone.ts +135 -135
- package/src/dock-manager-model.ts +596 -596
- package/src/dock-model.ts +374 -374
- package/src/editing.test.ts +974 -949
- package/src/editing.ts +609 -606
- package/src/editor-block-prop.test.ts +132 -0
- package/src/editor-contract.ts +171 -171
- package/src/editor-registry.grid.test.ts +144 -144
- package/src/editor-registry.ts +122 -122
- package/src/editors/cell-editors.ts +27 -1
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +601 -601
- package/src/filter-operators.ts +160 -160
- package/src/filtering/excel-filters.ts +325 -325
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/grid-messages.ts +34 -0
- package/src/group-display.test.ts +167 -167
- package/src/group-display.ts +200 -200
- package/src/headless.ts +87 -87
- package/src/index.ts +13 -0
- package/src/js-scroller.svelte.ts +173 -173
- package/src/keyboard-handlers.ts +270 -270
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-nav.test.ts +49 -49
- package/src/list-nav.ts +29 -29
- package/src/list-option.test.ts +56 -56
- package/src/list-option.ts +179 -173
- package/src/menus.test.ts +43 -0
- package/src/menus.ts +597 -567
- package/src/merge-objects.ts +48 -48
- package/src/move-cells.test.ts +850 -0
- package/src/number-editor-literal.test.ts +84 -0
- package/src/number-editor.grid.test.ts +160 -0
- package/src/overlays.test.ts +90 -90
- package/src/positioning.ts +268 -268
- package/src/render-component.ts +28 -28
- package/src/row-drag-touch.ts +211 -211
- package/src/row-drag.test.ts +353 -353
- package/src/row-drag.ts +415 -415
- package/src/row-resize.test.ts +524 -524
- package/src/row-resize.ts +228 -228
- package/src/sanitize-html.test.ts +182 -0
- package/src/sanitize-html.ts +235 -0
- package/src/scheduler-ical.ts +181 -181
- package/src/scheduler-model.test.ts +562 -562
- package/src/scheduler-model.ts +873 -873
- package/src/selection-bar-view.svelte.ts +36 -0
- package/src/selection.test.ts +900 -743
- package/src/selection.ts +131 -13
- package/src/server-data-source.test.ts +383 -383
- package/src/server-data-source.ts +469 -469
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +488 -488
- package/src/spreadsheet.ts +312 -312
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/summaries.ts +113 -113
- package/src/svgrid-wrapper.types.ts +563 -563
- package/src/svgrid.async-editor-options.test.ts +273 -273
- package/src/svgrid.auto-row-height.test.ts +204 -204
- package/src/svgrid.behavior.test.ts +910 -910
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.comments-autocomplete.test.ts +127 -127
- package/src/svgrid.context-menu.test.ts +147 -147
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.filter-depth.test.ts +163 -163
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +377 -331
- package/src/svgrid.filter-menu-scroll.test.ts +112 -112
- package/src/svgrid.grand-total.test.ts +188 -188
- package/src/svgrid.group-display-mode.test.ts +171 -171
- package/src/svgrid.group-footers.test.ts +121 -121
- package/src/svgrid.group-pagination.test.ts +153 -153
- package/src/svgrid.in-suggest-snapshot.svelte.test.ts +91 -0
- package/src/svgrid.menu-scroll-close.test.ts +204 -0
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.selection-bar-seam.test.ts +185 -0
- package/src/svgrid.tree-data.test.ts +186 -186
- package/src/svgrid.upsell-license.test.ts +117 -0
- package/src/svgrid.wrapper.test.ts +63 -63
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -195
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +288 -288
- package/src/toast-store.svelte.ts +250 -250
- package/src/toast-store.test.ts +147 -147
- package/src/tree-row-model.test.ts +168 -168
- package/src/ui-buttons.test.ts +144 -144
- package/src/ui-inputs.test.ts +118 -118
- package/src/ui-localization.test.ts +113 -113
- package/src/ui-range.test.ts +70 -70
- package/src/ui-selection.test.ts +155 -155
- package/src/ui-tier1.test.ts +142 -142
- package/src/virtual.test.ts +88 -88
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +322 -322
- package/dist/cdn/GridMenus-BuoBPqxx.js +0 -493
- package/dist/cdn/GridMenus-n4llxoOI.js +0 -494
- package/dist/cdn/SvDateTimePicker-CHnUkWcH.js +0 -2206
- package/dist/cdn/SvDateTimePicker-CRQH_U7E.js +0 -2201
- package/dist/cdn/SvGridCellEditor-BGUCzuPB.js +0 -523
- package/dist/cdn/SvGridCellEditor-G8IMWIws.js +0 -522
package/src/GridFooter.svelte
CHANGED
|
@@ -1,181 +1,181 @@
|
|
|
1
|
-
<script lang="ts" generics="TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData">
|
|
2
|
-
import {
|
|
3
|
-
type RowData,
|
|
4
|
-
type TableFeatures,
|
|
5
|
-
} from "./index";
|
|
6
|
-
import "./sv-grid-scrollbar";
|
|
7
|
-
import {
|
|
8
|
-
fmtStat,
|
|
9
|
-
} from "./SvGrid.helpers";
|
|
10
|
-
import type { SvGridController } from "./SvGrid.controller.svelte";
|
|
11
|
-
|
|
12
|
-
let {
|
|
13
|
-
ctrl,
|
|
14
|
-
pager = true,
|
|
15
|
-
showStatus = true,
|
|
16
|
-
top = false,
|
|
17
|
-
pageSizeOptions,
|
|
18
|
-
}: {
|
|
19
|
-
ctrl: SvGridController<TFeatures, TData>;
|
|
20
|
-
/** Render the pagination controls in this instance. */
|
|
21
|
-
pager?: boolean;
|
|
22
|
-
/** Render the status bar in this instance. */
|
|
23
|
-
showStatus?: boolean;
|
|
24
|
-
/** This is a top-positioned footer (styling only). */
|
|
25
|
-
top?: boolean;
|
|
26
|
-
/** Page-size choices for the selector. Defaults to `[10, 25, 50, 100]`. */
|
|
27
|
-
pageSizeOptions?: number[];
|
|
28
|
-
} = $props();
|
|
29
|
-
|
|
30
|
-
// SvGridDropdown is ~22 KB and exists here only to pick one of four numbers.
|
|
31
|
-
// Importing it statically also dragged it into the base bundle and silently
|
|
32
|
-
// defeated the lazy `import()` in SvGrid.svelte, which loads the same
|
|
33
|
-
// component as a cell editor. So the closed trigger below is plain markup
|
|
34
|
-
// reusing the dropdown's own classes (identical appearance), and the real
|
|
35
|
-
// component is fetched on first open. Keep this lazy: a static import here
|
|
36
|
-
// re-breaks the cell-editor split too, not just the footer.
|
|
37
|
-
let PageSizeDropdown = $state<typeof import("./SvGridDropdown.svelte").default | null>(null);
|
|
38
|
-
let loadingPageSize = $state(false);
|
|
39
|
-
|
|
40
|
-
async function openPageSize() {
|
|
41
|
-
if (PageSizeDropdown || loadingPageSize) return;
|
|
42
|
-
loadingPageSize = true;
|
|
43
|
-
try {
|
|
44
|
-
PageSizeDropdown = (await import("./SvGridDropdown.svelte")).default;
|
|
45
|
-
} finally {
|
|
46
|
-
loadingPageSize = false;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// View facade: re-bind the controller's reactive members so the markup
|
|
51
|
-
// (moved verbatim from SvGrid.svelte) stays identical.
|
|
52
|
-
const paginationEnabled = $derived(ctrl.paginationEnabled);
|
|
53
|
-
const statusBarEnabled = $derived(ctrl.statusBarEnabled);
|
|
54
|
-
const statusBarAggregates = $derived(ctrl.statusBarAggregates);
|
|
55
|
-
const statusBarStats = $derived(ctrl.statusBarStats);
|
|
56
|
-
const changePage = $derived(ctrl.changePage);
|
|
57
|
-
const goToPage = $derived(ctrl.goToPage);
|
|
58
|
-
const setPageSize = $derived(ctrl.setPageSize);
|
|
59
|
-
const messages = $derived(ctrl.messages);
|
|
60
|
-
</script>
|
|
61
|
-
|
|
62
|
-
{#if showStatus && statusBarEnabled && statusBarStats}
|
|
63
|
-
{@const s = statusBarStats}
|
|
64
|
-
<div class="sv-grid-status-bar" role="status" aria-live="polite">
|
|
65
|
-
{#each statusBarAggregates as agg (agg)}
|
|
66
|
-
{#if agg === "count"}
|
|
67
|
-
<span class="sv-grid-status-item"
|
|
68
|
-
><span class="sv-grid-status-label">{messages.statCount}</span>{fmtStat(s.count)}</span
|
|
69
|
-
>
|
|
70
|
-
{:else if agg === "numericCount"}
|
|
71
|
-
<span class="sv-grid-status-item"
|
|
72
|
-
><span class="sv-grid-status-label">Numeric</span>{fmtStat(s.numericCount)}</span
|
|
73
|
-
>
|
|
74
|
-
{:else if s.numericCount > 0 && agg === "sum"}
|
|
75
|
-
<span class="sv-grid-status-item"
|
|
76
|
-
><span class="sv-grid-status-label">{messages.statSum}</span>{fmtStat(s.sum)}</span
|
|
77
|
-
>
|
|
78
|
-
{:else if s.numericCount > 0 && agg === "avg"}
|
|
79
|
-
<span class="sv-grid-status-item"
|
|
80
|
-
><span class="sv-grid-status-label">{messages.statAvg}</span>{fmtStat(s.avg)}</span
|
|
81
|
-
>
|
|
82
|
-
{:else if s.numericCount > 0 && agg === "min"}
|
|
83
|
-
<span class="sv-grid-status-item"
|
|
84
|
-
><span class="sv-grid-status-label">{messages.statMin}</span>{fmtStat(s.min)}</span
|
|
85
|
-
>
|
|
86
|
-
{:else if s.numericCount > 0 && agg === "max"}
|
|
87
|
-
<span class="sv-grid-status-item"
|
|
88
|
-
><span class="sv-grid-status-label">{messages.statMax}</span>{fmtStat(s.max)}</span
|
|
89
|
-
>
|
|
90
|
-
{/if}
|
|
91
|
-
{/each}
|
|
92
|
-
</div>
|
|
93
|
-
{/if}
|
|
94
|
-
|
|
95
|
-
{#if pager && paginationEnabled}
|
|
96
|
-
{@const totalRows = ctrl.paginationTotalRows}
|
|
97
|
-
{@const pageSize = ctrl.paginationPageSize}
|
|
98
|
-
{@const pageCount = Math.max(1, Math.ceil(totalRows / pageSize))}
|
|
99
|
-
{@const currentPage = Math.min(ctrl.paginationPageIndex + 1, pageCount)}
|
|
100
|
-
{@const rangeStart =
|
|
101
|
-
totalRows === 0 ? 0 : (currentPage - 1) * pageSize + 1}
|
|
102
|
-
{@const rangeEnd = Math.min(totalRows, currentPage * pageSize)}
|
|
103
|
-
{@const onFirst = currentPage <= 1}
|
|
104
|
-
{@const onLast = currentPage >= pageCount}
|
|
105
|
-
{@const sizes = pageSizeOptions && pageSizeOptions.length ? pageSizeOptions : [10, 25, 50, 100]}
|
|
106
|
-
<div class="sv-grid-pagination" class:sv-grid-pagination-top={top} role="navigation" aria-label="Pagination">
|
|
107
|
-
<div class="sv-grid-pagination-pagesize">
|
|
108
|
-
<span>{messages.pageSize}</span>
|
|
109
|
-
<div class="sv-grid-pagination-pagesize-dd">
|
|
110
|
-
{#if PageSizeDropdown}
|
|
111
|
-
<!-- autoOpen so the click that triggered the load also opens the
|
|
112
|
-
panel, rather than making the user click a second time. -->
|
|
113
|
-
<PageSizeDropdown
|
|
114
|
-
options={sizes.map((n) => ({ value: n, label: String(n) }))}
|
|
115
|
-
value={pageSize}
|
|
116
|
-
autoOpen={true}
|
|
117
|
-
onChange={(v) => setPageSize(Number(v))}
|
|
118
|
-
/>
|
|
119
|
-
{:else}
|
|
120
|
-
<!-- Mirrors SvGridDropdown's closed trigger (same classes, same
|
|
121
|
-
ARIA). Its box styling comes from the global
|
|
122
|
-
.sv-grid-pagination-pagesize-dd rules in SvGrid.css, NOT from
|
|
123
|
-
SvGridDropdown's scoped styles (those only exist once the chunk
|
|
124
|
-
loads) - so keep the two in step or the placeholder renders as
|
|
125
|
-
a stock <button> until the first click. -->
|
|
126
|
-
<div class="sv-grid-dropdown">
|
|
127
|
-
<button
|
|
128
|
-
type="button"
|
|
129
|
-
class="sv-grid-dropdown-trigger"
|
|
130
|
-
aria-haspopup="listbox"
|
|
131
|
-
aria-expanded="false"
|
|
132
|
-
onclick={openPageSize}
|
|
133
|
-
>
|
|
134
|
-
<span class="sv-grid-dropdown-label">{pageSize}</span>
|
|
135
|
-
<span class="sv-grid-dropdown-caret" aria-hidden="true">▾</span>
|
|
136
|
-
</button>
|
|
137
|
-
</div>
|
|
138
|
-
{/if}
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
<span class="sv-grid-pagination-range">
|
|
142
|
-
<strong>{rangeStart.toLocaleString()}</strong> to
|
|
143
|
-
<strong>{rangeEnd.toLocaleString()}</strong> of
|
|
144
|
-
<strong>{totalRows.toLocaleString()}</strong>
|
|
145
|
-
</span>
|
|
146
|
-
<div class="sv-grid-pagination-nav">
|
|
147
|
-
<button
|
|
148
|
-
type="button"
|
|
149
|
-
class="sv-grid-pagination-btn"
|
|
150
|
-
disabled={onFirst}
|
|
151
|
-
onclick={() => goToPage(0)}
|
|
152
|
-
aria-label={messages.firstPage}>⇤</button
|
|
153
|
-
>
|
|
154
|
-
<button
|
|
155
|
-
type="button"
|
|
156
|
-
class="sv-grid-pagination-btn"
|
|
157
|
-
disabled={onFirst}
|
|
158
|
-
onclick={() => changePage(-1)}
|
|
159
|
-
aria-label={messages.prevPage}>‹</button
|
|
160
|
-
>
|
|
161
|
-
<span class="sv-grid-pagination-label">
|
|
162
|
-
{messages.page} <strong>{currentPage.toLocaleString()}</strong> {messages.of}
|
|
163
|
-
<strong>{pageCount.toLocaleString()}</strong>
|
|
164
|
-
</span>
|
|
165
|
-
<button
|
|
166
|
-
type="button"
|
|
167
|
-
class="sv-grid-pagination-btn"
|
|
168
|
-
disabled={onLast}
|
|
169
|
-
onclick={() => changePage(1)}
|
|
170
|
-
aria-label={messages.nextPage}>›</button
|
|
171
|
-
>
|
|
172
|
-
<button
|
|
173
|
-
type="button"
|
|
174
|
-
class="sv-grid-pagination-btn"
|
|
175
|
-
disabled={onLast}
|
|
176
|
-
onclick={() => goToPage(pageCount - 1)}
|
|
177
|
-
aria-label={messages.lastPage}>⇥</button
|
|
178
|
-
>
|
|
179
|
-
</div>
|
|
180
|
-
</div>
|
|
181
|
-
{/if}
|
|
1
|
+
<script lang="ts" generics="TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData">
|
|
2
|
+
import {
|
|
3
|
+
type RowData,
|
|
4
|
+
type TableFeatures,
|
|
5
|
+
} from "./index";
|
|
6
|
+
import "./sv-grid-scrollbar";
|
|
7
|
+
import {
|
|
8
|
+
fmtStat,
|
|
9
|
+
} from "./SvGrid.helpers";
|
|
10
|
+
import type { SvGridController } from "./SvGrid.controller.svelte";
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
ctrl,
|
|
14
|
+
pager = true,
|
|
15
|
+
showStatus = true,
|
|
16
|
+
top = false,
|
|
17
|
+
pageSizeOptions,
|
|
18
|
+
}: {
|
|
19
|
+
ctrl: SvGridController<TFeatures, TData>;
|
|
20
|
+
/** Render the pagination controls in this instance. */
|
|
21
|
+
pager?: boolean;
|
|
22
|
+
/** Render the status bar in this instance. */
|
|
23
|
+
showStatus?: boolean;
|
|
24
|
+
/** This is a top-positioned footer (styling only). */
|
|
25
|
+
top?: boolean;
|
|
26
|
+
/** Page-size choices for the selector. Defaults to `[10, 25, 50, 100]`. */
|
|
27
|
+
pageSizeOptions?: number[];
|
|
28
|
+
} = $props();
|
|
29
|
+
|
|
30
|
+
// SvGridDropdown is ~22 KB and exists here only to pick one of four numbers.
|
|
31
|
+
// Importing it statically also dragged it into the base bundle and silently
|
|
32
|
+
// defeated the lazy `import()` in SvGrid.svelte, which loads the same
|
|
33
|
+
// component as a cell editor. So the closed trigger below is plain markup
|
|
34
|
+
// reusing the dropdown's own classes (identical appearance), and the real
|
|
35
|
+
// component is fetched on first open. Keep this lazy: a static import here
|
|
36
|
+
// re-breaks the cell-editor split too, not just the footer.
|
|
37
|
+
let PageSizeDropdown = $state<typeof import("./SvGridDropdown.svelte").default | null>(null);
|
|
38
|
+
let loadingPageSize = $state(false);
|
|
39
|
+
|
|
40
|
+
async function openPageSize() {
|
|
41
|
+
if (PageSizeDropdown || loadingPageSize) return;
|
|
42
|
+
loadingPageSize = true;
|
|
43
|
+
try {
|
|
44
|
+
PageSizeDropdown = (await import("./SvGridDropdown.svelte")).default;
|
|
45
|
+
} finally {
|
|
46
|
+
loadingPageSize = false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// View facade: re-bind the controller's reactive members so the markup
|
|
51
|
+
// (moved verbatim from SvGrid.svelte) stays identical.
|
|
52
|
+
const paginationEnabled = $derived(ctrl.paginationEnabled);
|
|
53
|
+
const statusBarEnabled = $derived(ctrl.statusBarEnabled);
|
|
54
|
+
const statusBarAggregates = $derived(ctrl.statusBarAggregates);
|
|
55
|
+
const statusBarStats = $derived(ctrl.statusBarStats);
|
|
56
|
+
const changePage = $derived(ctrl.changePage);
|
|
57
|
+
const goToPage = $derived(ctrl.goToPage);
|
|
58
|
+
const setPageSize = $derived(ctrl.setPageSize);
|
|
59
|
+
const messages = $derived(ctrl.messages);
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
{#if showStatus && statusBarEnabled && statusBarStats}
|
|
63
|
+
{@const s = statusBarStats}
|
|
64
|
+
<div class="sv-grid-status-bar" role="status" aria-live="polite">
|
|
65
|
+
{#each statusBarAggregates as agg (agg)}
|
|
66
|
+
{#if agg === "count"}
|
|
67
|
+
<span class="sv-grid-status-item"
|
|
68
|
+
><span class="sv-grid-status-label">{messages.statCount}</span>{fmtStat(s.count)}</span
|
|
69
|
+
>
|
|
70
|
+
{:else if agg === "numericCount"}
|
|
71
|
+
<span class="sv-grid-status-item"
|
|
72
|
+
><span class="sv-grid-status-label">Numeric</span>{fmtStat(s.numericCount)}</span
|
|
73
|
+
>
|
|
74
|
+
{:else if s.numericCount > 0 && agg === "sum"}
|
|
75
|
+
<span class="sv-grid-status-item"
|
|
76
|
+
><span class="sv-grid-status-label">{messages.statSum}</span>{fmtStat(s.sum)}</span
|
|
77
|
+
>
|
|
78
|
+
{:else if s.numericCount > 0 && agg === "avg"}
|
|
79
|
+
<span class="sv-grid-status-item"
|
|
80
|
+
><span class="sv-grid-status-label">{messages.statAvg}</span>{fmtStat(s.avg)}</span
|
|
81
|
+
>
|
|
82
|
+
{:else if s.numericCount > 0 && agg === "min"}
|
|
83
|
+
<span class="sv-grid-status-item"
|
|
84
|
+
><span class="sv-grid-status-label">{messages.statMin}</span>{fmtStat(s.min)}</span
|
|
85
|
+
>
|
|
86
|
+
{:else if s.numericCount > 0 && agg === "max"}
|
|
87
|
+
<span class="sv-grid-status-item"
|
|
88
|
+
><span class="sv-grid-status-label">{messages.statMax}</span>{fmtStat(s.max)}</span
|
|
89
|
+
>
|
|
90
|
+
{/if}
|
|
91
|
+
{/each}
|
|
92
|
+
</div>
|
|
93
|
+
{/if}
|
|
94
|
+
|
|
95
|
+
{#if pager && paginationEnabled}
|
|
96
|
+
{@const totalRows = ctrl.paginationTotalRows}
|
|
97
|
+
{@const pageSize = ctrl.paginationPageSize}
|
|
98
|
+
{@const pageCount = Math.max(1, Math.ceil(totalRows / pageSize))}
|
|
99
|
+
{@const currentPage = Math.min(ctrl.paginationPageIndex + 1, pageCount)}
|
|
100
|
+
{@const rangeStart =
|
|
101
|
+
totalRows === 0 ? 0 : (currentPage - 1) * pageSize + 1}
|
|
102
|
+
{@const rangeEnd = Math.min(totalRows, currentPage * pageSize)}
|
|
103
|
+
{@const onFirst = currentPage <= 1}
|
|
104
|
+
{@const onLast = currentPage >= pageCount}
|
|
105
|
+
{@const sizes = pageSizeOptions && pageSizeOptions.length ? pageSizeOptions : [10, 25, 50, 100]}
|
|
106
|
+
<div class="sv-grid-pagination" class:sv-grid-pagination-top={top} role="navigation" aria-label="Pagination">
|
|
107
|
+
<div class="sv-grid-pagination-pagesize">
|
|
108
|
+
<span>{messages.pageSize}</span>
|
|
109
|
+
<div class="sv-grid-pagination-pagesize-dd">
|
|
110
|
+
{#if PageSizeDropdown}
|
|
111
|
+
<!-- autoOpen so the click that triggered the load also opens the
|
|
112
|
+
panel, rather than making the user click a second time. -->
|
|
113
|
+
<PageSizeDropdown
|
|
114
|
+
options={sizes.map((n) => ({ value: n, label: String(n) }))}
|
|
115
|
+
value={pageSize}
|
|
116
|
+
autoOpen={true}
|
|
117
|
+
onChange={(v) => setPageSize(Number(v))}
|
|
118
|
+
/>
|
|
119
|
+
{:else}
|
|
120
|
+
<!-- Mirrors SvGridDropdown's closed trigger (same classes, same
|
|
121
|
+
ARIA). Its box styling comes from the global
|
|
122
|
+
.sv-grid-pagination-pagesize-dd rules in SvGrid.css, NOT from
|
|
123
|
+
SvGridDropdown's scoped styles (those only exist once the chunk
|
|
124
|
+
loads) - so keep the two in step or the placeholder renders as
|
|
125
|
+
a stock <button> until the first click. -->
|
|
126
|
+
<div class="sv-grid-dropdown">
|
|
127
|
+
<button
|
|
128
|
+
type="button"
|
|
129
|
+
class="sv-grid-dropdown-trigger"
|
|
130
|
+
aria-haspopup="listbox"
|
|
131
|
+
aria-expanded="false"
|
|
132
|
+
onclick={openPageSize}
|
|
133
|
+
>
|
|
134
|
+
<span class="sv-grid-dropdown-label">{pageSize}</span>
|
|
135
|
+
<span class="sv-grid-dropdown-caret" aria-hidden="true">▾</span>
|
|
136
|
+
</button>
|
|
137
|
+
</div>
|
|
138
|
+
{/if}
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
<span class="sv-grid-pagination-range">
|
|
142
|
+
<strong>{rangeStart.toLocaleString()}</strong> to
|
|
143
|
+
<strong>{rangeEnd.toLocaleString()}</strong> of
|
|
144
|
+
<strong>{totalRows.toLocaleString()}</strong>
|
|
145
|
+
</span>
|
|
146
|
+
<div class="sv-grid-pagination-nav">
|
|
147
|
+
<button
|
|
148
|
+
type="button"
|
|
149
|
+
class="sv-grid-pagination-btn"
|
|
150
|
+
disabled={onFirst}
|
|
151
|
+
onclick={() => goToPage(0)}
|
|
152
|
+
aria-label={messages.firstPage}>⇤</button
|
|
153
|
+
>
|
|
154
|
+
<button
|
|
155
|
+
type="button"
|
|
156
|
+
class="sv-grid-pagination-btn"
|
|
157
|
+
disabled={onFirst}
|
|
158
|
+
onclick={() => changePage(-1)}
|
|
159
|
+
aria-label={messages.prevPage}>‹</button
|
|
160
|
+
>
|
|
161
|
+
<span class="sv-grid-pagination-label">
|
|
162
|
+
{messages.page} <strong>{currentPage.toLocaleString()}</strong> {messages.of}
|
|
163
|
+
<strong>{pageCount.toLocaleString()}</strong>
|
|
164
|
+
</span>
|
|
165
|
+
<button
|
|
166
|
+
type="button"
|
|
167
|
+
class="sv-grid-pagination-btn"
|
|
168
|
+
disabled={onLast}
|
|
169
|
+
onclick={() => changePage(1)}
|
|
170
|
+
aria-label={messages.nextPage}>›</button
|
|
171
|
+
>
|
|
172
|
+
<button
|
|
173
|
+
type="button"
|
|
174
|
+
class="sv-grid-pagination-btn"
|
|
175
|
+
disabled={onLast}
|
|
176
|
+
onclick={() => goToPage(pageCount - 1)}
|
|
177
|
+
aria-label={messages.lastPage}>⇥</button
|
|
178
|
+
>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
{/if}
|
package/src/GridMenus.svelte
CHANGED
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
} from "./SvGrid.types";
|
|
17
17
|
import {
|
|
18
18
|
getEditorInputType,
|
|
19
|
+
usesRichDateFilter,
|
|
20
|
+
toIsoDateLocal,
|
|
21
|
+
fromIsoDateLocal,
|
|
19
22
|
} from "./SvGrid.helpers";
|
|
20
23
|
|
|
21
24
|
import type { SvGridController } from "./SvGrid.controller.svelte";
|
|
@@ -72,11 +75,45 @@
|
|
|
72
75
|
// anything OUTSIDE them scrolls, or on resize. Scrolling the menu's own body
|
|
73
76
|
// or its facet list has to keep it open - hence onScrollOutside rather than a
|
|
74
77
|
// bare capture-phase listener.
|
|
78
|
+
//
|
|
79
|
+
// The cell context menu and the comment editor were missing from this gate.
|
|
80
|
+
// Both are positioned from a one-time cursor measurement and rendered
|
|
81
|
+
// `position: fixed`, so after a scroll they float over unrelated cells - the
|
|
82
|
+
// exact defect the effect exists to prevent. Their backdrop swallows a wheel
|
|
83
|
+
// over the grid, but it does not stop the PAGE scrolling underneath them, and
|
|
84
|
+
// it does not stop a programmatic scroll.
|
|
85
|
+
//
|
|
86
|
+
// The comment editor is dismissed by SAVING rather than discarding. Clicking
|
|
87
|
+
// the backdrop or pressing Escape throws the draft away, and that is fine -
|
|
88
|
+
// both are the user saying "away with this". A scroll is not a dismissal
|
|
89
|
+
// gesture, so it must not destroy text the user has typed.
|
|
75
90
|
$effect(() => {
|
|
76
|
-
if (
|
|
77
|
-
|
|
91
|
+
if (
|
|
92
|
+
!(
|
|
93
|
+
columnMenuFor ||
|
|
94
|
+
filterMenuFor ||
|
|
95
|
+
operatorMenuFor ||
|
|
96
|
+
chooseColumnsPos ||
|
|
97
|
+
contextMenuFor ||
|
|
98
|
+
commentEditFor
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
return;
|
|
102
|
+
const close = () => {
|
|
103
|
+
closeMenus();
|
|
104
|
+
closeContextMenu();
|
|
105
|
+
if (ctrl.commentEditFor) saveComment();
|
|
106
|
+
};
|
|
78
107
|
const offScroll = onScrollOutside(
|
|
79
|
-
|
|
108
|
+
// Scoped to THIS grid's menus. A bare document query treats a second
|
|
109
|
+
// grid's open menu as "inside", so scrolling in that other menu would
|
|
110
|
+
// leave this one stranded at its stale coordinates.
|
|
111
|
+
() =>
|
|
112
|
+
Array.from(
|
|
113
|
+
document.querySelectorAll<HTMLElement>(
|
|
114
|
+
`.sv-grid-menu[data-svgrid-menu="${ctrl.gridDomId}"]`,
|
|
115
|
+
),
|
|
116
|
+
),
|
|
80
117
|
close,
|
|
81
118
|
);
|
|
82
119
|
window.addEventListener('resize', close);
|
|
@@ -96,6 +133,71 @@
|
|
|
96
133
|
const clearGroupingFromMenu = $derived(ctrl.clearGroupingFromMenu);
|
|
97
134
|
const columnMenuFacetValues = $derived(ctrl.columnMenuFacetValues);
|
|
98
135
|
const columnMenuVisibleFacets = $derived(ctrl.columnMenuVisibleFacets);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* How many value rows the checklist shows. It was pinned at 7 regardless of
|
|
139
|
+
* the window, so a tall screen left most of the space under the menu empty
|
|
140
|
+
* while the list stayed a 7-row porthole over a few hundred values.
|
|
141
|
+
*
|
|
142
|
+
* Read once per open rather than on resize: the popover is transient, and
|
|
143
|
+
* the menu is already capped by its own `max-height`.
|
|
144
|
+
*/
|
|
145
|
+
const FACET_ROW_HEIGHT = 24;
|
|
146
|
+
const facetRows = $derived.by(function facetRows_d() {
|
|
147
|
+
if (typeof window === "undefined") return 7;
|
|
148
|
+
const top = (filterMenuFor ? filterMenuPos.y : columnMenuPos.y) || 0;
|
|
149
|
+
// What is left under the menu's top edge once the condition block, the
|
|
150
|
+
// search row and the action bar have taken their share.
|
|
151
|
+
const room = window.innerHeight - top - 320;
|
|
152
|
+
return Math.max(7, Math.min(16, Math.floor(room / FACET_ROW_HEIGHT)));
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The same date controls the filter ROW uses. Without them a date column
|
|
157
|
+
* offered two different date UIs depending on which surface you touched:
|
|
158
|
+
* a rich picker under the header, a bare `<input type="date">` in the menu.
|
|
159
|
+
*
|
|
160
|
+
* Lazy even though GridMenus is itself a lazy chunk - the picker drags in
|
|
161
|
+
* the date-format engine, and most grids that open a column menu have no
|
|
162
|
+
* date column at all.
|
|
163
|
+
*/
|
|
164
|
+
let MenuDatePicker = $state<typeof import("./SvDateTimePicker.svelte").default | null>(null);
|
|
165
|
+
let MenuRangePicker = $state<typeof import("./SvDateRangeInput.svelte").default | null>(null);
|
|
166
|
+
$effect(() => {
|
|
167
|
+
const colId = filterMenuFor ?? columnMenuFor;
|
|
168
|
+
if (!colId) return;
|
|
169
|
+
const column = allColumns.find((c) => c.id === colId);
|
|
170
|
+
if (!usesRichDateFilter(column?.columnDef.editorType)) return;
|
|
171
|
+
if (!MenuDatePicker)
|
|
172
|
+
import("./SvDateTimePicker.svelte").then((m) => (MenuDatePicker = m.default));
|
|
173
|
+
if (!MenuRangePicker)
|
|
174
|
+
import("./SvDateRangeInput.svelte").then((m) => (MenuRangePicker = m.default));
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Opening the filter popover used to leave focus on the funnel button
|
|
179
|
+
* outside it, so a keyboard user landed nowhere and had to tab in past the
|
|
180
|
+
* whole menu. Focus the first control instead - the condition value if the
|
|
181
|
+
* column has one, else the value search.
|
|
182
|
+
*/
|
|
183
|
+
function focusFilterPanel(node: HTMLElement) {
|
|
184
|
+
requestAnimationFrame(() => {
|
|
185
|
+
const first = node.querySelector<HTMLElement>(
|
|
186
|
+
".sv-grid-menu-condition-value, .sv-dtp__input, .sv-dri__input, .sv-grid-menu-search",
|
|
187
|
+
);
|
|
188
|
+
first?.focus({ preventScroll: true });
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Enter anywhere in the panel means "done" - filtering is already live. */
|
|
193
|
+
function onFilterPanelKeydown(event: KeyboardEvent) {
|
|
194
|
+
if (event.key !== "Enter" || event.shiftKey) return;
|
|
195
|
+
const target = event.target as HTMLElement | null;
|
|
196
|
+
// Let the listbox and real buttons keep Enter for their own activation.
|
|
197
|
+
if (target?.closest(".sv-listbox") || target?.tagName === "BUTTON") return;
|
|
198
|
+
event.preventDefault();
|
|
199
|
+
closeMenus();
|
|
200
|
+
}
|
|
99
201
|
const columnMenuFacetOptions = $derived(ctrl.columnMenuFacetOptions);
|
|
100
202
|
const columnMenuSelectedFacets = $derived(ctrl.columnMenuSelectedFacets);
|
|
101
203
|
const setFacetSelection = $derived(ctrl.setFacetSelection);
|
|
@@ -187,6 +289,21 @@
|
|
|
187
289
|
{@const menuOperatorOptions = operatorsForColumn(menuColumn)}
|
|
188
290
|
{@const menuActiveOperator = filterMenuValues[colId]?.operator ?? defaultOperatorFor(menuColumn)}
|
|
189
291
|
{@const menuInputType = getEditorInputType((menuColumn?.columnDef.editorType ?? "text") as CellEditorType)}
|
|
292
|
+
{@const richDate =
|
|
293
|
+
usesRichDateFilter(menuColumn?.columnDef.editorType) &&
|
|
294
|
+
menuActiveOperator !== "isBlank" &&
|
|
295
|
+
menuActiveOperator !== "isNotBlank" &&
|
|
296
|
+
menuActiveOperator !== "regex" &&
|
|
297
|
+
menuActiveOperator !== "in" &&
|
|
298
|
+
menuActiveOperator !== "notIn"}
|
|
299
|
+
{@const searching =
|
|
300
|
+
columnMenuVisibleFacets.length !== columnMenuFacetValues.length}
|
|
301
|
+
{@const conditionActive =
|
|
302
|
+
menuActiveOperator === "isBlank" ||
|
|
303
|
+
menuActiveOperator === "isNotBlank" ||
|
|
304
|
+
(filterMenuValues[colId]?.value ?? "").trim().length > 0}
|
|
305
|
+
{@const pickedCount = columnMenuSelectedFacets.size}
|
|
306
|
+
{@const totalCount = columnMenuFacetValues.length}
|
|
190
307
|
<div class="sv-grid-menu-filter">
|
|
191
308
|
<div class="sv-grid-menu-filter-head">
|
|
192
309
|
<span class="sv-grid-header-icon">{@render icon("filter")}</span> Filter
|
|
@@ -204,6 +321,40 @@
|
|
|
204
321
|
{/each}
|
|
205
322
|
</select>
|
|
206
323
|
{#if menuActiveOperator !== "isBlank" && menuActiveOperator !== "isNotBlank"}
|
|
324
|
+
{#if richDate}
|
|
325
|
+
<!-- Same controls the filter row uses, so a date column behaves the
|
|
326
|
+
same wherever you touch it: a picker, or a range field for
|
|
327
|
+
`between`. `regex` and the set operators keep the text box - a
|
|
328
|
+
pattern and a token list are not dates. -->
|
|
329
|
+
{#if menuActiveOperator === "between"}
|
|
330
|
+
{#if MenuRangePicker}
|
|
331
|
+
{@const RangePicker = MenuRangePicker}
|
|
332
|
+
{@const from = fromIsoDateLocal(filterMenuValues[colId]?.value)}
|
|
333
|
+
{@const to = fromIsoDateLocal(filterMenuValues[colId]?.valueTo)}
|
|
334
|
+
<RangePicker
|
|
335
|
+
value={from && to ? [from, to] : null}
|
|
336
|
+
formatString="yyyy-MM-dd"
|
|
337
|
+
block
|
|
338
|
+
placeholder="Pick a range..."
|
|
339
|
+
onChange={(range) => {
|
|
340
|
+
updateFilterMenuValue(colId, range ? toIsoDateLocal(range[0]) : "");
|
|
341
|
+
updateFilterMenuValueTo(colId, range ? toIsoDateLocal(range[1]) : "");
|
|
342
|
+
}}
|
|
343
|
+
/>
|
|
344
|
+
{/if}
|
|
345
|
+
{:else if MenuDatePicker}
|
|
346
|
+
{@const DatePicker = MenuDatePicker}
|
|
347
|
+
<DatePicker
|
|
348
|
+
value={(filterMenuValues[colId]?.value ?? "") || null}
|
|
349
|
+
formatString="yyyy-MM-dd"
|
|
350
|
+
dropDownDisplayMode="calendar"
|
|
351
|
+
block
|
|
352
|
+
nullable
|
|
353
|
+
placeholder="Filter value..."
|
|
354
|
+
onChange={(d) => updateFilterMenuValue(colId, toIsoDateLocal(d))}
|
|
355
|
+
/>
|
|
356
|
+
{/if}
|
|
357
|
+
{:else}
|
|
207
358
|
{@const isSetOp =
|
|
208
359
|
menuActiveOperator === "in" || menuActiveOperator === "notIn"}
|
|
209
360
|
<!-- in/notIn drives the shared value-suggestions dropdown (a windowed
|
|
@@ -239,6 +390,9 @@
|
|
|
239
390
|
oninput={(event) => updateFilterMenuValueTo(colId, (event.currentTarget as HTMLInputElement).value)}
|
|
240
391
|
/>
|
|
241
392
|
{/if}
|
|
393
|
+
{/if}
|
|
394
|
+
<!-- A second condition is offered for every column type, date
|
|
395
|
+
included, so this sits outside the value-control branch. -->
|
|
242
396
|
{@const mf = filterMenuValues[colId]}
|
|
243
397
|
{#if mf?.operator2}
|
|
244
398
|
{@const op2 = mf.operator2 ?? defaultOperatorFor(menuColumn)}
|
|
@@ -288,7 +442,16 @@
|
|
|
288
442
|
{/if}
|
|
289
443
|
{/if}
|
|
290
444
|
<div class="sv-grid-menu-sep"></div>
|
|
291
|
-
<div class="sv-grid-menu-filter-head">
|
|
445
|
+
<div class="sv-grid-menu-filter-head">
|
|
446
|
+
Values
|
|
447
|
+
<!-- The condition above and this checklist are ANDed. Nothing said so,
|
|
448
|
+
so a row could vanish for a reason neither half showed on its own. -->
|
|
449
|
+
{#if conditionActive}
|
|
450
|
+
<span class="sv-grid-menu-head-note">and the condition above</span>
|
|
451
|
+
{:else if pickedCount < totalCount}
|
|
452
|
+
<span class="sv-grid-menu-head-note">{pickedCount} of {totalCount}</span>
|
|
453
|
+
{/if}
|
|
454
|
+
</div>
|
|
292
455
|
<input
|
|
293
456
|
class="sv-grid-menu-search"
|
|
294
457
|
placeholder="Search values..."
|
|
@@ -296,7 +459,11 @@
|
|
|
296
459
|
/>
|
|
297
460
|
<label class="sv-grid-facet sv-grid-facet-all">
|
|
298
461
|
<input type="checkbox" checked={isAllFacetsChecked(colId)} onchange={() => toggleAllFacets(colId)} />
|
|
299
|
-
|
|
462
|
+
<!-- Says what it will actually do: with a query typed it acts on the
|
|
463
|
+
matches only, leaving the rest of the selection untouched. -->
|
|
464
|
+
<span class="sv-grid-facet-label"
|
|
465
|
+
>{searching ? "(Select all results)" : "(Select all)"}</span
|
|
466
|
+
>
|
|
300
467
|
</label>
|
|
301
468
|
<!-- Windowed checklist. A high-cardinality column (10k distinct symbols
|
|
302
469
|
on a live feed) would otherwise mount a label + checkbox per value -
|
|
@@ -308,7 +475,7 @@
|
|
|
308
475
|
checkbox
|
|
309
476
|
multiple
|
|
310
477
|
virtual={columnMenuFacetOptions.length > FACET_VIRTUAL_THRESHOLD}
|
|
311
|
-
rows={
|
|
478
|
+
rows={facetRows}
|
|
312
479
|
rowHeight={24}
|
|
313
480
|
size="sm"
|
|
314
481
|
ariaLabel="Filter values"
|
|
@@ -348,6 +515,7 @@
|
|
|
348
515
|
{@const menuTab = ctrl.columnMenuTab}
|
|
349
516
|
<div
|
|
350
517
|
class="sv-grid-menu sv-grid-column-menu"
|
|
518
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
351
519
|
role="menu"
|
|
352
520
|
style={`left: ${columnMenuPos.x}px; top: ${columnMenuPos.y}px; max-height: calc(100vh - ${columnMenuPos.y}px - 12px);`}
|
|
353
521
|
>
|
|
@@ -535,10 +703,15 @@
|
|
|
535
703
|
{/if}
|
|
536
704
|
|
|
537
705
|
{#if filterMenuFor && showColumnFiltersEffective}
|
|
706
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
538
707
|
<div
|
|
539
708
|
class="sv-grid-menu sv-grid-filter-menu"
|
|
709
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
540
710
|
role="menu"
|
|
541
711
|
style={`left: ${filterMenuPos.x}px; top: ${filterMenuPos.y}px; max-height: calc(100vh - ${filterMenuPos.y}px - 12px);`}
|
|
712
|
+
tabindex="-1"
|
|
713
|
+
use:focusFilterPanel
|
|
714
|
+
onkeydown={onFilterPanelKeydown}
|
|
542
715
|
>
|
|
543
716
|
{@render filterPanelBody(filterMenuFor)}
|
|
544
717
|
</div>
|
|
@@ -548,6 +721,7 @@
|
|
|
548
721
|
{@const everyColumn = grid.getAllColumns()}
|
|
549
722
|
<div
|
|
550
723
|
class="sv-grid-menu sv-grid-choose-columns-menu"
|
|
724
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
551
725
|
role="menu"
|
|
552
726
|
style={`left: ${chooseColumnsPos.x}px; top: ${chooseColumnsPos.y}px;`}
|
|
553
727
|
>
|
|
@@ -576,6 +750,7 @@
|
|
|
576
750
|
filterMenuValues[opColumnId]?.operator ?? defaultOperatorFor(opColumn)}
|
|
577
751
|
<div
|
|
578
752
|
class="sv-grid-menu sv-grid-operator-menu"
|
|
753
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
579
754
|
role="menu"
|
|
580
755
|
style={`left: ${operatorMenuPos.x}px; top: ${operatorMenuPos.y}px;`}
|
|
581
756
|
>
|
|
@@ -613,6 +788,7 @@
|
|
|
613
788
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
614
789
|
<div
|
|
615
790
|
class="sv-grid-menu sv-grid-in-suggest"
|
|
791
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
616
792
|
style={`left: ${inSuggestPos.x}px; top: ${inSuggestPos.y}px;`}
|
|
617
793
|
onmousedown={(event) => event.preventDefault()}
|
|
618
794
|
>
|
|
@@ -656,6 +832,7 @@
|
|
|
656
832
|
></div>
|
|
657
833
|
<div
|
|
658
834
|
class="sv-grid-menu sv-grid-context-menu"
|
|
835
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
659
836
|
role="menu"
|
|
660
837
|
style={`left: ${contextMenuPos.x}px; top: ${contextMenuPos.y}px;`}
|
|
661
838
|
>
|
|
@@ -683,6 +860,7 @@
|
|
|
683
860
|
></div>
|
|
684
861
|
<div
|
|
685
862
|
class="sv-grid-menu sv-grid-comment-editor"
|
|
863
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
686
864
|
role="dialog"
|
|
687
865
|
aria-label="Edit comment"
|
|
688
866
|
style={`left: ${commentEditFor.x}px; top: ${commentEditFor.y}px;`}
|