@svgrid/grid 1.2.22 → 1.2.24
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/dist/GridFooter.svelte +34 -20
- package/dist/GridFooter.svelte.d.ts +8 -0
- package/dist/SvAutoComplete.svelte +89 -0
- package/dist/SvAutoComplete.svelte.d.ts +16 -0
- package/dist/SvButton.svelte +97 -0
- package/dist/SvButton.svelte.d.ts +23 -0
- package/dist/SvCalendar.svelte +409 -0
- package/dist/SvCalendar.svelte.d.ts +52 -0
- package/dist/SvCheckBox.svelte +82 -0
- package/dist/SvCheckBox.svelte.d.ts +19 -0
- package/dist/SvColorInput.svelte +160 -0
- package/dist/SvColorInput.svelte.d.ts +15 -0
- package/dist/SvComboBox.svelte +108 -0
- package/dist/SvComboBox.svelte.d.ts +15 -0
- package/dist/SvCountryInput.svelte +125 -0
- package/dist/SvCountryInput.svelte.d.ts +14 -0
- package/dist/SvDateTimePicker.svelte +265 -0
- package/dist/SvDateTimePicker.svelte.d.ts +38 -0
- package/dist/SvDropDownList.svelte +120 -0
- package/dist/SvDropDownList.svelte.d.ts +15 -0
- package/dist/SvForm.svelte +147 -0
- package/dist/SvForm.svelte.d.ts +29 -0
- package/dist/SvGauge.svelte +97 -0
- package/dist/SvGauge.svelte.d.ts +26 -0
- package/dist/SvGrid.controller.svelte.d.ts +16 -2
- package/dist/SvGrid.controller.svelte.js +63 -8
- package/dist/SvGrid.css +84 -31
- package/dist/SvGrid.helpers.d.ts +4 -0
- package/dist/SvGrid.helpers.js +40 -12
- package/dist/SvGrid.svelte +84 -5
- package/dist/SvGrid.types.d.ts +36 -0
- package/dist/SvGridDropdown.svelte +16 -30
- package/dist/SvGridDropdown.svelte.d.ts +4 -0
- package/dist/SvListBox.svelte +89 -0
- package/dist/SvListBox.svelte.d.ts +22 -0
- package/dist/SvMaskedInput.svelte +66 -0
- package/dist/SvMaskedInput.svelte.d.ts +15 -0
- package/dist/SvNumberInput.svelte +116 -0
- package/dist/SvNumberInput.svelte.d.ts +18 -0
- package/dist/SvPasswordInput.svelte +100 -0
- package/dist/SvPasswordInput.svelte.d.ts +18 -0
- package/dist/SvPhoneInput.svelte +87 -0
- package/dist/SvPhoneInput.svelte.d.ts +19 -0
- package/dist/SvRadioGroup.svelte +85 -0
- package/dist/SvRadioGroup.svelte.d.ts +20 -0
- package/dist/SvRating.svelte +91 -0
- package/dist/SvRating.svelte.d.ts +18 -0
- package/dist/SvRepeatButton.svelte +89 -0
- package/dist/SvRepeatButton.svelte.d.ts +21 -0
- package/dist/SvSlider.svelte +155 -0
- package/dist/SvSlider.svelte.d.ts +20 -0
- package/dist/SvSwitchButton.svelte +87 -0
- package/dist/SvSwitchButton.svelte.d.ts +14 -0
- package/dist/SvTabs.svelte +109 -0
- package/dist/SvTabs.svelte.d.ts +26 -0
- package/dist/SvTagsInput.svelte +70 -0
- package/dist/SvTagsInput.svelte.d.ts +14 -0
- package/dist/SvTimePicker.svelte +201 -0
- package/dist/SvTimePicker.svelte.d.ts +30 -0
- package/dist/SvToggleButton.svelte +57 -0
- package/dist/SvToggleButton.svelte.d.ts +16 -0
- package/dist/SvTree.svelte +131 -0
- package/dist/SvTree.svelte.d.ts +27 -0
- package/dist/build-api.js +78 -1
- package/dist/cdn/svgrid.js +11674 -5907
- package/dist/cdn/svgrid.svelte-external.js +10436 -4671
- package/dist/cell-render.d.ts +4 -0
- package/dist/cell-render.js +35 -1
- package/dist/clipboard.js +19 -1
- package/dist/collaboration.d.ts +2 -0
- package/dist/collaboration.js +4 -0
- package/dist/core.d.ts +22 -1
- package/dist/countries.d.ts +15 -0
- package/dist/countries.js +61 -0
- package/dist/createAutocomplete.svelte.d.ts +77 -0
- package/dist/createAutocomplete.svelte.js +100 -0
- package/dist/createCalendar.svelte.d.ts +192 -0
- package/dist/createCalendar.svelte.js +402 -0
- package/dist/createCheckbox.svelte.d.ts +45 -0
- package/dist/createCheckbox.svelte.js +26 -0
- package/dist/createColorInput.svelte.d.ts +67 -0
- package/dist/createColorInput.svelte.js +100 -0
- package/dist/createCombobox.svelte.d.ts +102 -0
- package/dist/createCombobox.svelte.js +171 -0
- package/dist/createCountryInput.svelte.d.ts +86 -0
- package/dist/createCountryInput.svelte.js +126 -0
- package/dist/createDateTimePicker.svelte.d.ts +83 -0
- package/dist/createDateTimePicker.svelte.js +202 -0
- package/dist/createDropdownList.svelte.d.ts +78 -0
- package/dist/createDropdownList.svelte.js +117 -0
- package/dist/createGauge.svelte.d.ts +56 -0
- package/dist/createGauge.svelte.js +49 -0
- package/dist/createListbox.svelte.d.ts +79 -0
- package/dist/createListbox.svelte.js +117 -0
- package/dist/createMaskedInput.svelte.d.ts +30 -0
- package/dist/createMaskedInput.svelte.js +62 -0
- package/dist/createNumberInput.svelte.d.ts +72 -0
- package/dist/createNumberInput.svelte.js +167 -0
- package/dist/createPasswordInput.svelte.d.ts +60 -0
- package/dist/createPasswordInput.svelte.js +72 -0
- package/dist/createPhoneInput.svelte.d.ts +46 -0
- package/dist/createPhoneInput.svelte.js +105 -0
- package/dist/createRadioGroup.svelte.d.ts +64 -0
- package/dist/createRadioGroup.svelte.js +73 -0
- package/dist/createRating.svelte.d.ts +70 -0
- package/dist/createRating.svelte.js +85 -0
- package/dist/createSlider.svelte.d.ts +71 -0
- package/dist/createSlider.svelte.js +123 -0
- package/dist/createSwitch.svelte.d.ts +43 -0
- package/dist/createSwitch.svelte.js +41 -0
- package/dist/createTabs.svelte.d.ts +78 -0
- package/dist/createTabs.svelte.js +92 -0
- package/dist/createTagsInput.svelte.d.ts +65 -0
- package/dist/createTagsInput.svelte.js +93 -0
- package/dist/createTimePicker.svelte.d.ts +106 -0
- package/dist/createTimePicker.svelte.js +248 -0
- package/dist/createToggle.svelte.d.ts +43 -0
- package/dist/createToggle.svelte.js +39 -0
- package/dist/createTree.svelte.d.ts +96 -0
- package/dist/createTree.svelte.js +194 -0
- package/dist/datetime/date-core.d.ts +88 -0
- package/dist/datetime/date-core.js +165 -0
- package/dist/datetime/date-format.d.ts +39 -0
- package/dist/datetime/date-format.js +371 -0
- package/dist/datetime/date-restrict.d.ts +23 -0
- package/dist/datetime/date-restrict.js +50 -0
- package/dist/datetime/date-selection.d.ts +25 -0
- package/dist/datetime/date-selection.js +94 -0
- package/dist/datetime/mask.d.ts +21 -0
- package/dist/datetime/mask.js +90 -0
- package/dist/editing.d.ts +1 -1
- package/dist/editing.js +7 -0
- package/dist/editor-contract.d.ts +48 -0
- package/dist/editor-contract.js +26 -0
- package/dist/editors/cell-editors.d.ts +2 -2
- package/dist/editors/cell-editors.js +3 -1
- package/dist/export-format.d.ts +178 -0
- package/dist/export-format.js +419 -0
- package/dist/hyperformula-adapter.d.ts +4 -0
- package/dist/hyperformula-adapter.js +15 -6
- package/dist/index.d.ts +60 -0
- package/dist/index.js +72 -0
- package/dist/list-option.d.ts +10 -0
- package/dist/list-option.js +7 -0
- package/dist/menus.js +18 -2
- package/dist/popover.d.ts +48 -0
- package/dist/popover.js +65 -0
- package/dist/row-resize.d.ts +4 -4
- package/dist/row-resize.js +8 -5
- package/dist/selection.d.ts +6 -0
- package/dist/selection.js +31 -5
- package/dist/server-data-source.d.ts +43 -0
- package/dist/server-data-source.js +100 -0
- package/dist/sparkline.js +8 -4
- package/dist/svgrid-wrapper.types.d.ts +26 -1
- package/package.json +6 -1
- package/src/GridFooter.svelte +34 -20
- package/src/SvAutoComplete.svelte +89 -0
- package/src/SvButton.svelte +97 -0
- package/src/SvCalendar.svelte +409 -0
- package/src/SvCalendar.test.ts +211 -0
- package/src/SvCheckBox.svelte +82 -0
- package/src/SvColorInput.svelte +160 -0
- package/src/SvComboBox.svelte +108 -0
- package/src/SvCountryInput.svelte +125 -0
- package/src/SvDateTimePicker.svelte +265 -0
- package/src/SvDateTimePicker.test.ts +136 -0
- package/src/SvDropDownList.svelte +120 -0
- package/src/SvForm.svelte +147 -0
- package/src/SvGauge.svelte +97 -0
- package/src/SvGrid.controller.svelte.ts +74 -8
- package/src/SvGrid.css +84 -31
- package/src/SvGrid.helpers.ts +41 -12
- package/src/SvGrid.svelte +84 -5
- package/src/SvGrid.types.ts +33 -0
- package/src/SvGridDropdown.svelte +16 -30
- package/src/SvListBox.svelte +89 -0
- package/src/SvMaskedInput.svelte +66 -0
- package/src/SvNumberInput.svelte +116 -0
- package/src/SvPasswordInput.svelte +100 -0
- package/src/SvPhoneInput.svelte +87 -0
- package/src/SvRadioGroup.svelte +85 -0
- package/src/SvRating.svelte +91 -0
- package/src/SvRepeatButton.svelte +89 -0
- package/src/SvSlider.svelte +155 -0
- package/src/SvSwitchButton.svelte +87 -0
- package/src/SvTabs.svelte +109 -0
- package/src/SvTagsInput.svelte +70 -0
- package/src/SvTimePicker.svelte +201 -0
- package/src/SvTimePicker.test.ts +98 -0
- package/src/SvToggleButton.svelte +57 -0
- package/src/SvTree.svelte +131 -0
- package/src/build-api.ts +115 -0
- package/src/cell-render.test.ts +38 -0
- package/src/cell-render.ts +43 -0
- package/src/clipboard.test.ts +18 -0
- package/src/clipboard.ts +24 -1
- package/src/collaboration.test.ts +30 -0
- package/src/collaboration.ts +6 -0
- package/src/core.ts +27 -3
- package/src/countries.ts +71 -0
- package/src/createAutocomplete.svelte.ts +104 -0
- package/src/createCalendar.svelte.ts +488 -0
- package/src/createCheckbox.svelte.ts +74 -0
- package/src/createColorInput.svelte.ts +115 -0
- package/src/createCombobox.svelte.ts +158 -0
- package/src/createCountryInput.svelte.ts +126 -0
- package/src/createDateTimePicker.svelte.ts +216 -0
- package/src/createDropdownList.svelte.ts +125 -0
- package/src/createGauge.svelte.ts +86 -0
- package/src/createListbox.svelte.ts +186 -0
- package/src/createMaskedInput.svelte.ts +81 -0
- package/src/createNumberInput.svelte.ts +193 -0
- package/src/createPasswordInput.svelte.ts +87 -0
- package/src/createPhoneInput.svelte.ts +119 -0
- package/src/createRadioGroup.svelte.ts +132 -0
- package/src/createRating.svelte.ts +145 -0
- package/src/createSlider.svelte.ts +160 -0
- package/src/createSwitch.svelte.ts +78 -0
- package/src/createTabs.svelte.ts +129 -0
- package/src/createTagsInput.svelte.ts +98 -0
- package/src/createTimePicker.svelte.ts +289 -0
- package/src/createToggle.svelte.ts +69 -0
- package/src/createTree.svelte.ts +216 -0
- package/src/datetime/date-core.test.ts +217 -0
- package/src/datetime/date-core.ts +204 -0
- package/src/datetime/date-format.test.ts +121 -0
- package/src/datetime/date-format.ts +317 -0
- package/src/datetime/date-restrict.ts +60 -0
- package/src/datetime/date-selection.test.ts +129 -0
- package/src/datetime/date-selection.ts +137 -0
- package/src/datetime/mask.test.ts +36 -0
- package/src/datetime/mask.ts +84 -0
- package/src/editing.test.ts +22 -0
- package/src/editing.ts +7 -1
- package/src/editor-contract.test.ts +32 -0
- package/src/editor-contract.ts +60 -0
- package/src/editors/cell-editors.ts +7 -1
- package/src/export-data-api.test.ts +126 -0
- package/src/export-format.test.ts +107 -0
- package/src/export-format.ts +598 -0
- package/src/hyperformula-adapter.test.ts +35 -1
- package/src/hyperformula-adapter.ts +18 -6
- package/src/index.ts +110 -0
- package/src/list-option.ts +15 -0
- package/src/menus.ts +21 -2
- package/src/popover.ts +79 -0
- package/src/row-resize.test.ts +25 -0
- package/src/row-resize.ts +8 -5
- package/src/selection.test.ts +36 -4
- package/src/selection.ts +30 -9
- package/src/server-data-source.test.ts +201 -0
- package/src/server-data-source.ts +148 -0
- package/src/sparkline.test.ts +9 -0
- package/src/sparkline.ts +9 -4
- package/src/svgrid-wrapper.types.ts +28 -1
- package/src/svgrid.behavior.test.ts +27 -0
- package/src/svgrid.new-features.wrapper.test.ts +27 -1
- package/src/ui-buttons.test.ts +105 -0
- package/src/ui-composite.test.ts +89 -0
- package/src/ui-inputs.test.ts +118 -0
- package/src/ui-range.test.ts +61 -0
- package/src/ui-selection.test.ts +119 -0
package/dist/menus.js
CHANGED
|
@@ -216,21 +216,37 @@ export function createMenus(ctx) {
|
|
|
216
216
|
ctx.filterRowValues = next;
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
|
+
// External pagination is controlled: emit the requested page/size and let
|
|
220
|
+
// the consumer fetch + update props. Never mutate local pagination state.
|
|
221
|
+
function emitExternalPagination(pageIndex, pageSize) {
|
|
222
|
+
if (ctx.props.externalPagination !== true)
|
|
223
|
+
return false;
|
|
224
|
+
ctx.props.onPaginationChange?.({ pageIndex: Math.max(0, pageIndex), pageSize });
|
|
225
|
+
ctx.scrollContainer?.scrollTo?.({ top: 0 });
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
219
228
|
function changePage(delta) {
|
|
229
|
+
if (emitExternalPagination((ctx.props.pageIndex ?? 0) + delta, ctx.props.pageSize ?? 10))
|
|
230
|
+
return;
|
|
220
231
|
ctx.grid.setPagination((prev) => ({
|
|
221
232
|
...prev,
|
|
222
233
|
pageIndex: Math.max((prev?.pageIndex ?? 0) + delta, 0),
|
|
223
234
|
}));
|
|
224
|
-
ctx.scrollContainer?.scrollTo({ top: 0 });
|
|
235
|
+
ctx.scrollContainer?.scrollTo?.({ top: 0 });
|
|
225
236
|
}
|
|
226
237
|
function goToPage(pageIndex) {
|
|
238
|
+
if (emitExternalPagination(pageIndex, ctx.props.pageSize ?? 10))
|
|
239
|
+
return;
|
|
227
240
|
ctx.grid.setPagination((prev) => ({
|
|
228
241
|
...prev,
|
|
229
242
|
pageIndex: Math.max(0, pageIndex),
|
|
230
243
|
}));
|
|
231
|
-
ctx.scrollContainer?.scrollTo({ top: 0 });
|
|
244
|
+
ctx.scrollContainer?.scrollTo?.({ top: 0 });
|
|
232
245
|
}
|
|
233
246
|
function setPageSize(pageSize) {
|
|
247
|
+
// External mode: changing page size resets to the first page.
|
|
248
|
+
if (emitExternalPagination(0, pageSize))
|
|
249
|
+
return;
|
|
234
250
|
ctx.grid.setPagination((prev) => {
|
|
235
251
|
const oldSize = prev?.pageSize ?? 10;
|
|
236
252
|
const oldIndex = prev?.pageIndex ?? 0;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* popover - shared helpers for panels that must escape the grid's
|
|
3
|
+
* `overflow:hidden` scroll container by portalling to <body>: the theme-var
|
|
4
|
+
* snapshot + portal action, and the fixed-position anchor/flip math. Rune-free
|
|
5
|
+
* and DOM-only so it is reused verbatim by SvGridDropdown, SvDateTimePicker and
|
|
6
|
+
* any future popover. The per-component `$effect` wiring (scroll/resize
|
|
7
|
+
* reposition, outside-click close) stays in the component since it needs the
|
|
8
|
+
* component's own open state.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Theme tokens a portalled panel must carry with it. `--sg-*` custom properties
|
|
12
|
+
* are scoped to whatever wrapper the grid sits inside (e.g. a per-preset theme
|
|
13
|
+
* class); moving the panel to <body> leaves that scope, so we snapshot the
|
|
14
|
+
* resolved values and pin them inline. Keep this list in sync with the tokens
|
|
15
|
+
* any popover panel actually consumes.
|
|
16
|
+
*/
|
|
17
|
+
export declare const PANEL_THEME_VARS: readonly ["--sg-accent", "--sg-bg", "--sg-fg", "--sg-border", "--sg-muted", "--sg-header-bg", "--sg-row-hover-bg", "--sg-selection-bg", "--sg-input-bg", "--sg-input-border", "--sg-selection-fg"];
|
|
18
|
+
/**
|
|
19
|
+
* Svelte action: snapshot the resolved theme tokens from the node's current
|
|
20
|
+
* (in-scope) position, then detach it and append to <body> so it can never be
|
|
21
|
+
* clipped by an ancestor's overflow. Removes itself on destroy.
|
|
22
|
+
*/
|
|
23
|
+
export declare function portalToBody(node: HTMLElement, vars?: ReadonlyArray<string>): {
|
|
24
|
+
destroy(): void;
|
|
25
|
+
};
|
|
26
|
+
export type AnchoredRect = {
|
|
27
|
+
top: number;
|
|
28
|
+
left: number;
|
|
29
|
+
width: number;
|
|
30
|
+
openUpward: boolean;
|
|
31
|
+
};
|
|
32
|
+
export type AnchorOptions = {
|
|
33
|
+
/** Estimated panel height, used to decide whether to flip upward. */
|
|
34
|
+
estimatedHeight: number;
|
|
35
|
+
/** Gap between trigger and panel. Default 2px. */
|
|
36
|
+
gap?: number;
|
|
37
|
+
/** Force the panel at least this wide (else it matches the trigger). */
|
|
38
|
+
minWidth?: number;
|
|
39
|
+
/** Keep the panel within the viewport horizontally. Default true. */
|
|
40
|
+
clampHorizontal?: boolean;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Compute a `position: fixed` rect anchored to `triggerRect`, flipping above the
|
|
44
|
+
* trigger when there isn't room below and there's more room above. Mirrors the
|
|
45
|
+
* behavior SvGridDropdown shipped with, generalized with min-width + horizontal
|
|
46
|
+
* clamping for wider panels (date/time popovers).
|
|
47
|
+
*/
|
|
48
|
+
export declare function anchoredRect(triggerRect: DOMRect, opts: AnchorOptions): AnchoredRect;
|
package/dist/popover.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* popover - shared helpers for panels that must escape the grid's
|
|
3
|
+
* `overflow:hidden` scroll container by portalling to <body>: the theme-var
|
|
4
|
+
* snapshot + portal action, and the fixed-position anchor/flip math. Rune-free
|
|
5
|
+
* and DOM-only so it is reused verbatim by SvGridDropdown, SvDateTimePicker and
|
|
6
|
+
* any future popover. The per-component `$effect` wiring (scroll/resize
|
|
7
|
+
* reposition, outside-click close) stays in the component since it needs the
|
|
8
|
+
* component's own open state.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Theme tokens a portalled panel must carry with it. `--sg-*` custom properties
|
|
12
|
+
* are scoped to whatever wrapper the grid sits inside (e.g. a per-preset theme
|
|
13
|
+
* class); moving the panel to <body> leaves that scope, so we snapshot the
|
|
14
|
+
* resolved values and pin them inline. Keep this list in sync with the tokens
|
|
15
|
+
* any popover panel actually consumes.
|
|
16
|
+
*/
|
|
17
|
+
export const PANEL_THEME_VARS = [
|
|
18
|
+
'--sg-accent', '--sg-bg', '--sg-fg', '--sg-border', '--sg-muted',
|
|
19
|
+
'--sg-header-bg', '--sg-row-hover-bg', '--sg-selection-bg',
|
|
20
|
+
'--sg-input-bg', '--sg-input-border', '--sg-selection-fg',
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Svelte action: snapshot the resolved theme tokens from the node's current
|
|
24
|
+
* (in-scope) position, then detach it and append to <body> so it can never be
|
|
25
|
+
* clipped by an ancestor's overflow. Removes itself on destroy.
|
|
26
|
+
*/
|
|
27
|
+
export function portalToBody(node, vars = PANEL_THEME_VARS) {
|
|
28
|
+
const cs = getComputedStyle(node);
|
|
29
|
+
for (const v of vars) {
|
|
30
|
+
const val = cs.getPropertyValue(v).trim();
|
|
31
|
+
if (val)
|
|
32
|
+
node.style.setProperty(v, val);
|
|
33
|
+
}
|
|
34
|
+
document.body.appendChild(node);
|
|
35
|
+
return {
|
|
36
|
+
destroy() {
|
|
37
|
+
if (node.parentNode === document.body)
|
|
38
|
+
document.body.removeChild(node);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Compute a `position: fixed` rect anchored to `triggerRect`, flipping above the
|
|
44
|
+
* trigger when there isn't room below and there's more room above. Mirrors the
|
|
45
|
+
* behavior SvGridDropdown shipped with, generalized with min-width + horizontal
|
|
46
|
+
* clamping for wider panels (date/time popovers).
|
|
47
|
+
*/
|
|
48
|
+
export function anchoredRect(triggerRect, opts) {
|
|
49
|
+
const gap = opts.gap ?? 2;
|
|
50
|
+
const spaceBelow = window.innerHeight - triggerRect.bottom;
|
|
51
|
+
const spaceAbove = triggerRect.top;
|
|
52
|
+
const openUpward = spaceBelow < opts.estimatedHeight && spaceAbove > spaceBelow;
|
|
53
|
+
const width = Math.max(triggerRect.width, opts.minWidth ?? 0);
|
|
54
|
+
let left = triggerRect.left;
|
|
55
|
+
if (opts.clampHorizontal !== false) {
|
|
56
|
+
const maxLeft = window.innerWidth - width - 4;
|
|
57
|
+
left = Math.max(4, Math.min(left, maxLeft));
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
top: openUpward ? triggerRect.top - opts.estimatedHeight - gap : triggerRect.bottom + gap,
|
|
61
|
+
left,
|
|
62
|
+
width,
|
|
63
|
+
openUpward,
|
|
64
|
+
};
|
|
65
|
+
}
|
package/dist/row-resize.d.ts
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* reads per-row heights from a reactive store.
|
|
5
5
|
*
|
|
6
6
|
* The action is OFF until a consumer attaches it AND a row-header
|
|
7
|
-
* gutter cell exists in each row. A row-header cell is
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `cellClass: 'sv-row-gutter'` on
|
|
7
|
+
* gutter cell exists in each row. A row-header cell is either the
|
|
8
|
+
* grid's built-in row-number gutter (enable `showRowNumbers`) or any
|
|
9
|
+
* TD tagged with the `sv-row-gutter` class (set
|
|
10
|
+
* `cellClass: 'sv-row-gutter'` on a custom gutter column).
|
|
11
11
|
*
|
|
12
12
|
* <script>
|
|
13
13
|
* let heights = $state<Record<number, number>>({})
|
package/dist/row-resize.js
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* reads per-row heights from a reactive store.
|
|
5
5
|
*
|
|
6
6
|
* The action is OFF until a consumer attaches it AND a row-header
|
|
7
|
-
* gutter cell exists in each row. A row-header cell is
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `cellClass: 'sv-row-gutter'` on
|
|
7
|
+
* gutter cell exists in each row. A row-header cell is either the
|
|
8
|
+
* grid's built-in row-number gutter (enable `showRowNumbers`) or any
|
|
9
|
+
* TD tagged with the `sv-row-gutter` class (set
|
|
10
|
+
* `cellClass: 'sv-row-gutter'` on a custom gutter column).
|
|
11
11
|
*
|
|
12
12
|
* <script>
|
|
13
13
|
* let heights = $state<Record<number, number>>({})
|
|
@@ -103,7 +103,10 @@ export function rowResize(node, opts) {
|
|
|
103
103
|
continue;
|
|
104
104
|
if (tr.classList.contains('sv-grid-row-spacer'))
|
|
105
105
|
continue;
|
|
106
|
-
|
|
106
|
+
// A row-header gutter cell: either a user column tagged
|
|
107
|
+
// `sv-row-gutter`, or the grid's built-in row-number gutter
|
|
108
|
+
// (`showRowNumbers`), whose cell is `.sv-grid-row-number-cell`.
|
|
109
|
+
const gutter = tr.querySelector('.sv-grid-cell.sv-row-gutter, .sv-grid-cell.sv-grid-row-number-cell');
|
|
107
110
|
if (!gutter)
|
|
108
111
|
continue;
|
|
109
112
|
if (gutter.querySelector(`:scope > .${STRIP_CLASS}`))
|
package/dist/selection.d.ts
CHANGED
|
@@ -15,6 +15,12 @@ export declare function createSelection<TFeatures extends TableFeatures = TableF
|
|
|
15
15
|
left: boolean;
|
|
16
16
|
right: boolean;
|
|
17
17
|
} | null;
|
|
18
|
+
fillMarqueeEdges: (rowIndex: number, colIndex: number) => {
|
|
19
|
+
top: boolean;
|
|
20
|
+
bottom: boolean;
|
|
21
|
+
left: boolean;
|
|
22
|
+
right: boolean;
|
|
23
|
+
} | null;
|
|
18
24
|
getSelectionRects: () => {
|
|
19
25
|
minRow: number;
|
|
20
26
|
maxRow: number;
|
package/dist/selection.js
CHANGED
|
@@ -264,6 +264,31 @@ export function createSelection(ctx) {
|
|
|
264
264
|
colIndex <= d.sourceMaxCol;
|
|
265
265
|
return !inSource;
|
|
266
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Which edges of the fill-drag rectangle a cell sits on, so the render can
|
|
269
|
+
* paint a single dashed marquee around the WHOLE target range (source +
|
|
270
|
+
* extension) - the Excel fill-handle look - instead of a thick per-cell
|
|
271
|
+
* border. Returns null when the cell isn't on the rectangle's boundary.
|
|
272
|
+
*/
|
|
273
|
+
function fillMarqueeEdges(rowIndex, colIndex) {
|
|
274
|
+
const d = ctx.fillDrag;
|
|
275
|
+
if (!d)
|
|
276
|
+
return null;
|
|
277
|
+
const minR = Math.min(d.sourceMinRow, d.targetRow);
|
|
278
|
+
const maxR = Math.max(d.sourceMaxRow, d.targetRow);
|
|
279
|
+
const minC = Math.min(d.sourceMinCol, d.targetCol);
|
|
280
|
+
const maxC = Math.max(d.sourceMaxCol, d.targetCol);
|
|
281
|
+
if (rowIndex < minR || rowIndex > maxR || colIndex < minC || colIndex > maxC) {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
const top = rowIndex === minR;
|
|
285
|
+
const bottom = rowIndex === maxR;
|
|
286
|
+
const left = colIndex === minC;
|
|
287
|
+
const right = colIndex === maxC;
|
|
288
|
+
if (!top && !bottom && !left && !right)
|
|
289
|
+
return null;
|
|
290
|
+
return { top, bottom, left, right };
|
|
291
|
+
}
|
|
267
292
|
/** Look up a column by id without depending on `buildApi`'s private
|
|
268
293
|
* closure (those helpers don't exist at this scope). */
|
|
269
294
|
function findColumnById(columnId) {
|
|
@@ -276,11 +301,11 @@ export function createSelection(ctx) {
|
|
|
276
301
|
const column = ctx.allColumns[colIndex];
|
|
277
302
|
if (!row || !column || isGroupRow(row))
|
|
278
303
|
return;
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
304
|
+
// NOTE: we no longer bail for checkbox columns. Bailing here meant a
|
|
305
|
+
// range drag could never START on a checkbox cell. A plain click still
|
|
306
|
+
// toggles the checkbox via onCellClick (a same-cell click), while a
|
|
307
|
+
// cross-cell drag fires its `click` on a common ancestor - not the cell -
|
|
308
|
+
// so it selects a range without toggling.
|
|
284
309
|
const active = ctx.grid.getState().activeCell;
|
|
285
310
|
ctx.activeAtPointerDown = active
|
|
286
311
|
? { rowIndex: active.rowIndex, colIndex: active.colIndex }
|
|
@@ -414,6 +439,7 @@ export function createSelection(ctx) {
|
|
|
414
439
|
extendSelection,
|
|
415
440
|
isCellInSelectedRange,
|
|
416
441
|
getCellRangeEdges,
|
|
442
|
+
fillMarqueeEdges,
|
|
417
443
|
getSelectionRects,
|
|
418
444
|
isInFillPreview,
|
|
419
445
|
findColumnById,
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* It's headless and framework-agnostic on purpose: wire `setSort` /
|
|
9
9
|
* `setFilter` / `setPage` to the grid's controlled callbacks, and render the
|
|
10
10
|
* grid from the `{ rows, total, loading }` it hands you. See the demo.
|
|
11
|
+
*
|
|
12
|
+
* The write side (`createRow` / `updateRow` / `deleteRow`) is optional: a
|
|
13
|
+
* source that only implements `getRows` stays a pure read model, and the
|
|
14
|
+
* matching controller methods throw a clear error if called. Mutations are
|
|
15
|
+
* non-optimistic for now - the grid reflects a change only after the
|
|
16
|
+
* follow-up re-fetch of the current page lands.
|
|
11
17
|
*/
|
|
12
18
|
export type ServerSortModel = Array<{
|
|
13
19
|
id: string;
|
|
@@ -45,11 +51,22 @@ export type ServerResult<TData> = {
|
|
|
45
51
|
};
|
|
46
52
|
export type ServerDataSource<TData> = {
|
|
47
53
|
getRows(request: ServerRequest): Promise<ServerResult<TData>>;
|
|
54
|
+
/**
|
|
55
|
+
* Optional write side. Implement whichever your backend supports; the
|
|
56
|
+
* controller exposes matching `createRow` / `updateRow` / `deleteRow`
|
|
57
|
+
* methods that call through and then `refresh()` the current page.
|
|
58
|
+
* Calling a controller method whose source counterpart is missing throws.
|
|
59
|
+
*/
|
|
60
|
+
createRow?(input: Partial<TData>): Promise<TData>;
|
|
61
|
+
updateRow?(id: string, patch: Partial<TData>): Promise<TData>;
|
|
62
|
+
deleteRow?(id: string): Promise<void>;
|
|
48
63
|
};
|
|
49
64
|
export type ServerState<TData> = {
|
|
50
65
|
rows: ReadonlyArray<TData>;
|
|
51
66
|
total: number;
|
|
52
67
|
loading: boolean;
|
|
68
|
+
/** True while a create / update / delete mutation is in flight. */
|
|
69
|
+
saving: boolean;
|
|
53
70
|
error: unknown;
|
|
54
71
|
pageIndex: number;
|
|
55
72
|
pageSize: number;
|
|
@@ -64,6 +81,23 @@ export type ServerController<TData> = {
|
|
|
64
81
|
setFilter(filterModel: ServerFilterModel): void;
|
|
65
82
|
setPage(pageIndex: number): void;
|
|
66
83
|
setPageSize(pageSize: number): void;
|
|
84
|
+
/**
|
|
85
|
+
* Create a row through the source, then refresh the current page. Resolves
|
|
86
|
+
* with the created row. Rejects if the source has no `createRow` (or if the
|
|
87
|
+
* create itself fails - the read state is left untouched on failure).
|
|
88
|
+
*/
|
|
89
|
+
createRow(input: Partial<TData>): Promise<TData>;
|
|
90
|
+
/**
|
|
91
|
+
* Update a row by id through the source. Non-optimistic: refreshes the page.
|
|
92
|
+
* Optimistic (see `optimistic` + `getRowId` options): patches the local row
|
|
93
|
+
* immediately, then reconciles with the server result, rolling back on error.
|
|
94
|
+
*/
|
|
95
|
+
updateRow(id: string, patch: Partial<TData>): Promise<TData>;
|
|
96
|
+
/**
|
|
97
|
+
* Delete a row by id through the source. Non-optimistic: refreshes the page.
|
|
98
|
+
* Optimistic: removes the local row immediately, restoring it on error.
|
|
99
|
+
*/
|
|
100
|
+
deleteRow(id: string): Promise<void>;
|
|
67
101
|
getState(): ServerState<TData>;
|
|
68
102
|
/** Stop accepting in-flight responses (call on unmount). */
|
|
69
103
|
dispose(): void;
|
|
@@ -72,5 +106,14 @@ export type ServerControllerOptions<TData> = {
|
|
|
72
106
|
pageSize?: number;
|
|
73
107
|
/** Called whenever any of `rows` / `total` / `loading` / page changes. */
|
|
74
108
|
onChange: (state: ServerState<TData>) => void;
|
|
109
|
+
/**
|
|
110
|
+
* Apply `updateRow` / `deleteRow` to the local rows immediately (before the
|
|
111
|
+
* server confirms) and roll back on error - so edits feel instant and no
|
|
112
|
+
* refetch is needed. Requires `getRowId` to locate rows; ignored without it.
|
|
113
|
+
* A subsequent `refresh()` reconciles ordering/filtering. Default false.
|
|
114
|
+
*/
|
|
115
|
+
optimistic?: boolean;
|
|
116
|
+
/** Resolve a row's stable id, so optimistic update/delete can find it in `rows`. */
|
|
117
|
+
getRowId?: (row: TData) => string;
|
|
75
118
|
};
|
|
76
119
|
export declare function createServerDataSource<TData>(source: ServerDataSource<TData>, options: ServerControllerOptions<TData>): ServerController<TData>;
|
|
@@ -3,6 +3,7 @@ export function createServerDataSource(source, options) {
|
|
|
3
3
|
rows: [],
|
|
4
4
|
total: 0,
|
|
5
5
|
loading: false,
|
|
6
|
+
saving: false,
|
|
6
7
|
error: null,
|
|
7
8
|
pageIndex: 0,
|
|
8
9
|
pageSize: options.pageSize ?? 50,
|
|
@@ -51,8 +52,100 @@ export function createServerDataSource(source, options) {
|
|
|
51
52
|
emit();
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
// Shared mutation lifecycle: flip `saving`, run the write, refresh the
|
|
56
|
+
// current page on success, and always clear `saving`. A missing source
|
|
57
|
+
// method (or a disposed controller) rejects before anything is emitted.
|
|
58
|
+
function mutate(name, thunk) {
|
|
59
|
+
if (disposed) {
|
|
60
|
+
return Promise.reject(new Error('createServerDataSource: controller is disposed'));
|
|
61
|
+
}
|
|
62
|
+
if (!thunk) {
|
|
63
|
+
return Promise.reject(new Error(`createServerDataSource: the datasource does not implement ${name}()`));
|
|
64
|
+
}
|
|
65
|
+
state.saving = true;
|
|
66
|
+
emit();
|
|
67
|
+
return (async () => {
|
|
68
|
+
try {
|
|
69
|
+
const result = await thunk();
|
|
70
|
+
await fetchPage();
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
state.saving = false;
|
|
75
|
+
emit();
|
|
76
|
+
}
|
|
77
|
+
})();
|
|
78
|
+
}
|
|
79
|
+
const optimistic = !!options.optimistic && !!options.getRowId;
|
|
80
|
+
const getRowId = options.getRowId;
|
|
81
|
+
// Optimistic update: patch the local row, reconcile with the server result,
|
|
82
|
+
// roll back on error. Falls back to the plain refresh path when the row
|
|
83
|
+
// isn't on the current page (nothing local to update).
|
|
84
|
+
async function optimisticUpdate(id, patch, fn) {
|
|
85
|
+
if (disposed)
|
|
86
|
+
throw new Error('createServerDataSource: controller is disposed');
|
|
87
|
+
const prevRows = state.rows;
|
|
88
|
+
const idx = prevRows.findIndex((r) => getRowId(r) === id);
|
|
89
|
+
if (idx < 0)
|
|
90
|
+
return mutate('updateRow', () => fn(id, patch));
|
|
91
|
+
state.rows = [...prevRows.slice(0, idx), { ...prevRows[idx], ...patch }, ...prevRows.slice(idx + 1)];
|
|
92
|
+
state.saving = true;
|
|
93
|
+
emit();
|
|
94
|
+
try {
|
|
95
|
+
const result = await fn(id, patch);
|
|
96
|
+
state.rows = state.rows.map((r) => (getRowId(r) === id ? result : r));
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
state.rows = prevRows;
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
state.saving = false;
|
|
105
|
+
emit();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Optimistic delete: drop the local row + decrement total, restore on error.
|
|
109
|
+
async function optimisticDelete(id, fn) {
|
|
110
|
+
if (disposed)
|
|
111
|
+
throw new Error('createServerDataSource: controller is disposed');
|
|
112
|
+
const prevRows = state.rows;
|
|
113
|
+
const prevTotal = state.total;
|
|
114
|
+
const next = prevRows.filter((r) => getRowId(r) !== id);
|
|
115
|
+
if (next.length === prevRows.length)
|
|
116
|
+
return mutate('deleteRow', () => fn(id));
|
|
117
|
+
state.rows = next;
|
|
118
|
+
state.total = Math.max(0, prevTotal - (prevRows.length - next.length));
|
|
119
|
+
state.saving = true;
|
|
120
|
+
emit();
|
|
121
|
+
try {
|
|
122
|
+
await fn(id);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
state.rows = prevRows;
|
|
126
|
+
state.total = prevTotal;
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
state.saving = false;
|
|
131
|
+
emit();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
54
134
|
return {
|
|
55
135
|
refresh: fetchPage,
|
|
136
|
+
createRow: (input) => mutate('createRow', source.createRow ? () => source.createRow(input) : null),
|
|
137
|
+
updateRow: (id, patch) => {
|
|
138
|
+
if (!source.updateRow)
|
|
139
|
+
return mutate('updateRow', null);
|
|
140
|
+
const fn = source.updateRow;
|
|
141
|
+
return optimistic ? optimisticUpdate(id, patch, fn) : mutate('updateRow', () => fn(id, patch));
|
|
142
|
+
},
|
|
143
|
+
deleteRow: (id) => {
|
|
144
|
+
if (!source.deleteRow)
|
|
145
|
+
return mutate('deleteRow', null);
|
|
146
|
+
const fn = source.deleteRow;
|
|
147
|
+
return optimistic ? optimisticDelete(id, fn) : mutate('deleteRow', () => fn(id));
|
|
148
|
+
},
|
|
56
149
|
setSort(sortModel) {
|
|
57
150
|
state.sortModel = sortModel;
|
|
58
151
|
state.pageIndex = 0;
|
|
@@ -78,6 +171,13 @@ export function createServerDataSource(source, options) {
|
|
|
78
171
|
getState: () => ({ ...state }),
|
|
79
172
|
dispose() {
|
|
80
173
|
disposed = true;
|
|
174
|
+
// An in-flight fetch's resolution short-circuits on `disposed`, so it
|
|
175
|
+
// never clears `loading`. Clear it here (and emit) so a disposed
|
|
176
|
+
// controller doesn't report a permanent loading state.
|
|
177
|
+
if (state.loading) {
|
|
178
|
+
state.loading = false;
|
|
179
|
+
emit();
|
|
180
|
+
}
|
|
81
181
|
},
|
|
82
182
|
};
|
|
83
183
|
}
|
package/dist/sparkline.js
CHANGED
|
@@ -72,12 +72,16 @@ export function buildSparkline(values, cfg = {}) {
|
|
|
72
72
|
const n = values.length;
|
|
73
73
|
const stepX = n > 1 ? innerW / (n - 1) : 0;
|
|
74
74
|
const pts = values.map((v, i) => ({
|
|
75
|
-
|
|
75
|
+
// A single point has no span to walk, so centre it rather than pinning it
|
|
76
|
+
// to the left edge.
|
|
77
|
+
x: n === 1 ? pad + innerW / 2 : pad + i * stepX,
|
|
76
78
|
y: pad + innerH - ((v - min) / span) * innerH,
|
|
77
79
|
}));
|
|
78
|
-
const linePath =
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
const linePath = n === 1
|
|
81
|
+
? // One point produces only a moveto, which draws nothing. Emit a tiny
|
|
82
|
+
// horizontal segment so a single value still renders a visible mark.
|
|
83
|
+
`M${round(pts[0].x - 1)},${round(pts[0].y)} L${round(pts[0].x + 1)},${round(pts[0].y)}`
|
|
84
|
+
: pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${round(p.x)},${round(p.y)}`).join(' ');
|
|
81
85
|
const first = pts[0];
|
|
82
86
|
const last = pts[pts.length - 1];
|
|
83
87
|
const baseY = pad + innerH;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ColumnDef, RowData, SvGridOptions, TableFeatures } from './core';
|
|
1
|
+
import type { CellFormatConfig, ColumnDef, RowData, SvGridOptions, TableFeatures } from './core';
|
|
2
|
+
import type { GridExportOptions, GridClipboardOptions } from './export-format';
|
|
2
3
|
export type SvGridFilterOperator = 'contains' | 'equals' | 'startsWith' | 'greaterThan' | 'lessThan' | 'between' | 'isBlank';
|
|
3
4
|
/**
|
|
4
5
|
* A serializable snapshot of everything that makes up the current "view":
|
|
@@ -168,7 +169,31 @@ export type SvGridApi<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
168
169
|
field?: string;
|
|
169
170
|
header: string;
|
|
170
171
|
visible: boolean;
|
|
172
|
+
/** The column's `format` config, when set. Lets an exporter reproduce
|
|
173
|
+
* the on-screen display value (currency, date pattern, etc.). */
|
|
174
|
+
format?: CellFormatConfig;
|
|
175
|
+
/** Effective horizontal alignment ('left' | 'center' | 'right'). */
|
|
176
|
+
align?: 'left' | 'center' | 'right';
|
|
171
177
|
}>;
|
|
178
|
+
/**
|
|
179
|
+
* Export the grid to a **CSV** file. Free in the community grid; the
|
|
180
|
+
* richer Excel / PDF / styled formats live in @svgrid/enterprise. Values
|
|
181
|
+
* are formatted as shown on screen (pass `rawValues: true` for raw). Rows
|
|
182
|
+
* default to the current view (`rows: 'selected' | 'all'` to change).
|
|
183
|
+
* Resolves with the serialized text; pass `download: false` to skip the
|
|
184
|
+
* browser download and just get the string.
|
|
185
|
+
*/
|
|
186
|
+
exportCsv(options?: GridExportOptions): Promise<string>;
|
|
187
|
+
/** Export the grid to a **TSV** file (tab-separated). See `exportCsv`. */
|
|
188
|
+
exportTsv(options?: GridExportOptions): Promise<string>;
|
|
189
|
+
/** Export the grid to a **JSON** file (array of `{ field: value }`). */
|
|
190
|
+
exportJson(options?: GridExportOptions): Promise<string>;
|
|
191
|
+
/**
|
|
192
|
+
* Copy the grid to the system clipboard. `format: 'tsv'` (default) pastes
|
|
193
|
+
* straight into Excel / Sheets; `'csv'` / `'markdown'` also supported.
|
|
194
|
+
* Resolves with the copied text.
|
|
195
|
+
*/
|
|
196
|
+
copyToClipboard(options?: GridClipboardOptions): Promise<string>;
|
|
172
197
|
/** Clear every checked row. Emits `onRowSelectionChange({}, [])`. */
|
|
173
198
|
clearRowSelection(): void;
|
|
174
199
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svgrid/grid",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.24",
|
|
4
4
|
"description": "SvGrid - a headless-first, Svelte 5-native data grid engine and render component.",
|
|
5
5
|
"author": "jQWidgets <boikom@jqwidgets.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,6 +42,11 @@
|
|
|
42
42
|
"svelte": "./dist/static-functions.js",
|
|
43
43
|
"import": "./dist/static-functions.js"
|
|
44
44
|
},
|
|
45
|
+
"./format": {
|
|
46
|
+
"types": "./dist/export-format.d.ts",
|
|
47
|
+
"svelte": "./dist/export-format.js",
|
|
48
|
+
"import": "./dist/export-format.js"
|
|
49
|
+
},
|
|
45
50
|
"./flex-render": {
|
|
46
51
|
"types": "./dist/flex-render.d.ts",
|
|
47
52
|
"svelte": "./dist/flex-render.js",
|
package/src/GridFooter.svelte
CHANGED
|
@@ -47,7 +47,23 @@
|
|
|
47
47
|
} from "./SvGrid.controller.svelte";
|
|
48
48
|
import type { SvGridController } from "./SvGrid.controller.svelte";
|
|
49
49
|
|
|
50
|
-
let {
|
|
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();
|
|
51
67
|
|
|
52
68
|
// View facade: re-bind the controller's reactive members so the markup
|
|
53
69
|
// (moved verbatim from SvGrid.svelte) stays identical.
|
|
@@ -63,7 +79,7 @@
|
|
|
63
79
|
const setPageSize = $derived(ctrl.setPageSize);
|
|
64
80
|
</script>
|
|
65
81
|
|
|
66
|
-
{#if statusBarEnabled && statusBarStats}
|
|
82
|
+
{#if showStatus && statusBarEnabled && statusBarStats}
|
|
67
83
|
{@const s = statusBarStats}
|
|
68
84
|
<div class="sv-grid-status-bar" role="status" aria-live="polite">
|
|
69
85
|
{#each statusBarAggregates as agg (agg)}
|
|
@@ -96,31 +112,29 @@
|
|
|
96
112
|
</div>
|
|
97
113
|
{/if}
|
|
98
114
|
|
|
99
|
-
{#if paginationEnabled}
|
|
100
|
-
{@const totalRows =
|
|
101
|
-
{@const pageSize =
|
|
115
|
+
{#if pager && paginationEnabled}
|
|
116
|
+
{@const totalRows = ctrl.paginationTotalRows}
|
|
117
|
+
{@const pageSize = ctrl.paginationPageSize}
|
|
102
118
|
{@const pageCount = Math.max(1, Math.ceil(totalRows / pageSize))}
|
|
103
|
-
{@const currentPage = Math.min(
|
|
119
|
+
{@const currentPage = Math.min(ctrl.paginationPageIndex + 1, pageCount)}
|
|
104
120
|
{@const rangeStart =
|
|
105
121
|
totalRows === 0 ? 0 : (currentPage - 1) * pageSize + 1}
|
|
106
122
|
{@const rangeEnd = Math.min(totalRows, currentPage * pageSize)}
|
|
107
123
|
{@const onFirst = currentPage <= 1}
|
|
108
124
|
{@const onLast = currentPage >= pageCount}
|
|
109
|
-
|
|
110
|
-
|
|
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">
|
|
111
128
|
<span>Page Size:</span>
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
<option value="100" selected={pageSize === 100}>100</option>
|
|
122
|
-
</select>
|
|
123
|
-
</label>
|
|
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>
|
|
124
138
|
<span class="sv-grid-pagination-range">
|
|
125
139
|
<strong>{rangeStart.toLocaleString()}</strong> to
|
|
126
140
|
<strong>{rangeEnd.toLocaleString()}</strong> of
|