@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.
- package/LICENSE +1 -1
- package/README.md +137 -39
- package/dist/GridFooter.svelte +164 -0
- package/dist/GridFooter.svelte.d.ts +29 -0
- package/dist/GridMenus.svelte +648 -0
- package/dist/GridMenus.svelte.d.ts +31 -0
- package/dist/SvGrid.controller.svelte.d.ts +472 -0
- package/dist/SvGrid.controller.svelte.js +1917 -0
- package/dist/SvGrid.css +2012 -0
- package/dist/SvGrid.helpers.d.ts +35 -0
- package/dist/SvGrid.helpers.js +160 -0
- package/dist/SvGrid.helpers.test.d.ts +1 -0
- package/dist/SvGrid.helpers.test.js +298 -0
- package/dist/SvGrid.svelte +2346 -8742
- package/dist/SvGrid.svelte.d.ts +4 -357
- package/dist/SvGrid.types.d.ts +523 -0
- package/dist/SvGrid.types.js +1 -0
- package/dist/SvGridChart.svelte +1060 -23
- package/dist/SvGridChart.svelte.d.ts +17 -0
- package/dist/aligned-grids.d.ts +6 -0
- package/dist/aligned-grids.js +84 -0
- package/dist/aligned-grids.test.d.ts +1 -0
- package/dist/aligned-grids.test.js +75 -0
- package/dist/build-api.coverage.test.d.ts +20 -0
- package/dist/build-api.coverage.test.js +505 -0
- package/dist/build-api.d.ts +5 -0
- package/dist/build-api.js +557 -0
- package/dist/cell-render.d.ts +15 -0
- package/dist/cell-render.js +246 -0
- package/dist/cell-render.test.d.ts +1 -0
- package/dist/cell-render.test.js +338 -0
- package/dist/cell-values.d.ts +28 -0
- package/dist/cell-values.js +89 -0
- package/dist/chart-export.test.d.ts +1 -0
- package/dist/chart-export.test.js +302 -0
- package/dist/chart.coverage.test.d.ts +1 -0
- package/dist/chart.coverage.test.js +748 -0
- package/dist/chart.d.ts +370 -3
- package/dist/chart.js +1135 -42
- package/dist/clipboard.d.ts +15 -0
- package/dist/clipboard.js +420 -0
- package/dist/clipboard.test.d.ts +1 -0
- package/dist/clipboard.test.js +700 -0
- package/dist/collaboration.coverage.test.d.ts +1 -0
- package/dist/collaboration.coverage.test.js +200 -0
- package/dist/column-groups.d.ts +19 -0
- package/dist/column-groups.js +62 -0
- package/dist/column-groups.test.d.ts +1 -0
- package/dist/column-groups.test.js +56 -0
- package/dist/column-types.d.ts +10 -0
- package/dist/column-types.js +63 -0
- package/dist/column-types.test.d.ts +1 -0
- package/dist/column-types.test.js +62 -0
- package/dist/columns.d.ts +30 -0
- package/dist/columns.js +277 -0
- package/dist/columns.test.d.ts +1 -0
- package/dist/columns.test.js +625 -0
- package/dist/core.d.ts +86 -1
- package/dist/css.d.ts +3 -0
- package/dist/editing.d.ts +31 -0
- package/dist/editing.js +529 -0
- package/dist/editing.test.d.ts +1 -0
- package/dist/editing.test.js +732 -0
- package/dist/editors/cell-editors.coverage.test.d.ts +1 -0
- package/dist/editors/cell-editors.coverage.test.js +139 -0
- package/dist/editors/cell-editors.d.ts +1 -1
- package/dist/facet-buckets.d.ts +13 -0
- package/dist/facet-buckets.js +54 -0
- package/dist/facet-buckets.test.d.ts +1 -0
- package/dist/facet-buckets.test.js +296 -0
- package/dist/features.d.ts +5 -0
- package/dist/features.js +30 -0
- package/dist/filter-operators.d.ts +16 -0
- package/dist/filter-operators.js +69 -0
- package/dist/filter-operators.test.d.ts +1 -0
- package/dist/filter-operators.test.js +135 -0
- package/dist/hyperformula-adapter.d.ts +82 -0
- package/dist/hyperformula-adapter.js +73 -0
- package/dist/hyperformula-adapter.test.d.ts +1 -0
- package/dist/hyperformula-adapter.test.js +205 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.js +8 -2
- package/dist/keyboard-handlers.coverage.test.d.ts +1 -0
- package/dist/keyboard-handlers.coverage.test.js +495 -0
- package/dist/keyboard-handlers.d.ts +7 -0
- package/dist/keyboard-handlers.js +202 -0
- package/dist/menus.d.ts +40 -0
- package/dist/menus.js +390 -0
- package/dist/menus.test.d.ts +1 -0
- package/dist/menus.test.js +560 -0
- package/dist/named-views.coverage.test.d.ts +1 -0
- package/dist/named-views.coverage.test.js +180 -0
- package/dist/named-views.d.ts +27 -0
- package/dist/named-views.js +39 -0
- package/dist/row-drag.d.ts +49 -0
- package/dist/row-drag.js +221 -0
- package/dist/row-drag.test.d.ts +1 -0
- package/dist/row-drag.test.js +142 -0
- package/dist/row-resize.d.ts +43 -0
- package/dist/row-resize.js +158 -0
- package/dist/row-resize.test.d.ts +1 -0
- package/dist/row-resize.test.js +329 -0
- package/dist/scroll-sync.d.ts +9 -0
- package/dist/scroll-sync.js +89 -0
- package/dist/scroll-sync.test.d.ts +1 -0
- package/dist/scroll-sync.test.js +290 -0
- package/dist/selection.d.ts +32 -0
- package/dist/selection.js +427 -0
- package/dist/selection.multi-range.test.d.ts +1 -0
- package/dist/selection.multi-range.test.js +55 -0
- package/dist/selection.test.d.ts +1 -0
- package/dist/selection.test.js +647 -0
- package/dist/server-data-source.coverage.test.d.ts +1 -0
- package/dist/server-data-source.coverage.test.js +154 -0
- package/dist/spreadsheet.d.ts +110 -0
- package/dist/spreadsheet.js +242 -0
- package/dist/spreadsheet.test.d.ts +1 -0
- package/dist/spreadsheet.test.js +446 -0
- package/dist/summaries.d.ts +12 -0
- package/dist/summaries.js +65 -0
- package/dist/sv-grid-scrollbar.js +13 -1
- package/dist/svgrid-wrapper.types.d.ts +31 -1
- package/dist/svgrid.behavior.test.js +20 -0
- package/dist/svgrid.comments-autocomplete.test.d.ts +1 -0
- package/dist/svgrid.comments-autocomplete.test.js +96 -0
- package/dist/svgrid.context-menu.test.d.ts +1 -0
- package/dist/svgrid.context-menu.test.js +102 -0
- package/dist/svgrid.interaction.test.js +31 -0
- package/dist/svgrid.new-features.wrapper.test.js +64 -6
- package/dist/svgrid.wrapper.test.js +27 -1
- package/dist/test-setup.js +9 -3
- package/dist/virtualization/column-virtualizer.d.ts +2 -0
- package/dist/virtualization/scroll-scaling.d.ts +45 -0
- package/dist/virtualization/scroll-scaling.js +99 -0
- package/dist/virtualization/scroll-scaling.test.d.ts +1 -0
- package/dist/virtualization/scroll-scaling.test.js +127 -0
- package/dist/virtualization/svelte-virtualizer.svelte.d.ts +2 -0
- package/dist/virtualization/svelte-virtualizer.svelte.js +2 -0
- package/dist/virtualization/virtualizer.d.ts +7 -0
- package/dist/virtualization/virtualizer.js +30 -0
- package/package.json +2 -1
- package/src/GridFooter.svelte +164 -0
- package/src/GridMenus.svelte +648 -0
- package/src/SvGrid.controller.svelte.ts +2352 -0
- package/src/SvGrid.css +2012 -0
- package/src/SvGrid.helpers.test.ts +415 -0
- package/src/SvGrid.helpers.ts +185 -0
- package/src/SvGrid.svelte +2346 -8742
- package/src/SvGrid.types.ts +537 -0
- package/src/SvGridChart.svelte +1060 -23
- package/src/aligned-grids.test.ts +80 -0
- package/src/aligned-grids.ts +87 -0
- package/src/build-api.coverage.test.ts +532 -0
- package/src/build-api.ts +683 -0
- package/src/cell-render.test.ts +451 -0
- package/src/cell-render.ts +426 -0
- package/src/cell-values.ts +114 -0
- package/src/chart-export.test.ts +370 -0
- package/src/chart.coverage.test.ts +814 -0
- package/src/chart.ts +1352 -47
- package/src/clipboard.test.ts +780 -0
- package/src/clipboard.ts +552 -0
- package/src/collaboration.coverage.test.ts +220 -0
- package/src/column-groups.test.ts +59 -0
- package/src/column-groups.ts +80 -0
- package/src/column-types.test.ts +68 -0
- package/src/column-types.ts +82 -0
- package/src/columns.test.ts +702 -0
- package/src/columns.ts +419 -0
- package/src/core.ts +86 -0
- package/src/css.d.ts +3 -0
- package/src/editing.test.ts +837 -0
- package/src/editing.ts +669 -0
- package/src/editors/cell-editors.coverage.test.ts +156 -0
- package/src/editors/cell-editors.ts +1 -0
- package/src/facet-buckets.test.ts +353 -0
- package/src/facet-buckets.ts +67 -0
- package/src/features.ts +128 -0
- package/src/filter-operators.test.ts +174 -0
- package/src/filter-operators.ts +87 -0
- package/src/hyperformula-adapter.test.ts +256 -0
- package/src/hyperformula-adapter.ts +124 -0
- package/src/index.ts +49 -0
- package/src/keyboard-handlers.coverage.test.ts +560 -0
- package/src/keyboard-handlers.ts +353 -0
- package/src/keyboard.ts +97 -97
- package/src/menus.test.ts +620 -0
- package/src/menus.ts +555 -0
- package/src/named-views.coverage.test.ts +210 -0
- package/src/named-views.ts +48 -0
- package/src/row-drag.test.ts +168 -0
- package/src/row-drag.ts +255 -0
- package/src/row-resize.test.ts +369 -0
- package/src/row-resize.ts +171 -0
- package/src/scroll-sync.test.ts +330 -0
- package/src/scroll-sync.ts +218 -0
- package/src/selection.multi-range.test.ts +61 -0
- package/src/selection.test.ts +722 -0
- package/src/selection.ts +579 -0
- package/src/server-data-source.coverage.test.ts +180 -0
- package/src/spreadsheet.test.ts +489 -0
- package/src/spreadsheet.ts +304 -0
- package/src/summaries.ts +204 -0
- package/src/sv-grid-scrollbar.ts +13 -1
- package/src/svgrid-wrapper.types.ts +31 -1
- package/src/svgrid.behavior.test.ts +22 -0
- package/src/svgrid.comments-autocomplete.test.ts +112 -0
- package/src/svgrid.context-menu.test.ts +126 -0
- package/src/svgrid.interaction.test.ts +30 -0
- package/src/svgrid.new-features.wrapper.test.ts +67 -6
- package/src/svgrid.wrapper.test.ts +27 -1
- package/src/test-setup.ts +9 -6
- package/src/virtualization/scroll-scaling.test.ts +148 -0
- package/src/virtualization/scroll-scaling.ts +121 -0
- package/src/virtualization/svelte-virtualizer.svelte.ts +2 -0
- package/src/virtualization/virtualizer.ts +26 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createRowScrollScaling, resolveMaxDomHeight } from './scroll-scaling';
|
|
3
|
+
const VIEWPORT = 600;
|
|
4
|
+
const FALLBACK = 8000000;
|
|
5
|
+
describe('createRowScrollScaling - inert (content fits under the cap)', () => {
|
|
6
|
+
// 1M rows * 18px = 18M, under a 33.5M (Chrome) cap -> no scaling.
|
|
7
|
+
const s = createRowScrollScaling(18000000, 33554400, VIEWPORT);
|
|
8
|
+
it('is inactive and exposes the true height as the DOM height', () => {
|
|
9
|
+
expect(s.active).toBe(false);
|
|
10
|
+
expect(s.domTotal).toBe(18000000);
|
|
11
|
+
});
|
|
12
|
+
it('both maps are the identity', () => {
|
|
13
|
+
for (const v of [0, 1, 1234, 9000000, 17999400]) {
|
|
14
|
+
expect(s.domToLogical(v)).toBe(v);
|
|
15
|
+
expect(s.logicalToDom(v)).toBe(v);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe('createRowScrollScaling - active (content exceeds the cap)', () => {
|
|
20
|
+
// 1M rows * 32px = 32M, over Firefox's ~17.9M cap -> scaling engages.
|
|
21
|
+
const trueTotal = 32000000;
|
|
22
|
+
const maxDom = 17895697;
|
|
23
|
+
const s = createRowScrollScaling(trueTotal, maxDom, VIEWPORT);
|
|
24
|
+
const domMax = s.domTotal - VIEWPORT;
|
|
25
|
+
const logicalMax = trueTotal - VIEWPORT;
|
|
26
|
+
it('caps the DOM height at the browser limit', () => {
|
|
27
|
+
expect(s.active).toBe(true);
|
|
28
|
+
expect(s.domTotal).toBe(maxDom);
|
|
29
|
+
expect(s.domTotal).toBeLessThanOrEqual(maxDom);
|
|
30
|
+
});
|
|
31
|
+
it('maps the endpoints exactly so the first and last rows are reachable', () => {
|
|
32
|
+
expect(s.domToLogical(0)).toBe(0);
|
|
33
|
+
// The bottom of the DOM scroll range reaches the bottom of the logical range.
|
|
34
|
+
expect(s.domToLogical(domMax)).toBeCloseTo(logicalMax, 3);
|
|
35
|
+
expect(s.logicalToDom(0)).toBe(0);
|
|
36
|
+
expect(s.logicalToDom(logicalMax)).toBeCloseTo(domMax, 3);
|
|
37
|
+
});
|
|
38
|
+
it('clamps out-of-range inputs instead of overshooting', () => {
|
|
39
|
+
expect(s.domToLogical(-100)).toBe(0);
|
|
40
|
+
expect(s.domToLogical(domMax + 10000)).toBeCloseTo(logicalMax, 3);
|
|
41
|
+
expect(s.logicalToDom(-100)).toBe(0);
|
|
42
|
+
expect(s.logicalToDom(logicalMax + 10000)).toBeCloseTo(domMax, 3);
|
|
43
|
+
});
|
|
44
|
+
it('logicalToDom is the inverse of domToLogical across the range', () => {
|
|
45
|
+
for (let i = 0; i <= 10; i += 1) {
|
|
46
|
+
const domTop = (domMax * i) / 10;
|
|
47
|
+
const roundTrip = s.logicalToDom(s.domToLogical(domTop));
|
|
48
|
+
expect(roundTrip).toBeCloseTo(domTop, 2);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
it('both maps are monotonic non-decreasing', () => {
|
|
52
|
+
let prevL = -1;
|
|
53
|
+
let prevD = -1;
|
|
54
|
+
for (let i = 0; i <= 20; i += 1) {
|
|
55
|
+
const domTop = (domMax * i) / 20;
|
|
56
|
+
const logical = s.domToLogical(domTop);
|
|
57
|
+
expect(logical).toBeGreaterThanOrEqual(prevL);
|
|
58
|
+
prevL = logical;
|
|
59
|
+
const logIn = (logicalMax * i) / 20;
|
|
60
|
+
const dom = s.logicalToDom(logIn);
|
|
61
|
+
expect(dom).toBeGreaterThanOrEqual(prevD);
|
|
62
|
+
prevD = dom;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('resolveMaxDomHeight - picks the smaller, real scrollable cap', () => {
|
|
67
|
+
it('keeps the desktop cap when both signals agree', () => {
|
|
68
|
+
// Chrome ~33.5M; offsetHeight and scrollHeight match. 0.5% safety shave.
|
|
69
|
+
const cap = resolveMaxDomHeight(33554400, 33554400, FALLBACK);
|
|
70
|
+
expect(cap).toBeLessThan(33554400);
|
|
71
|
+
expect(cap).toBeGreaterThan(33000000);
|
|
72
|
+
});
|
|
73
|
+
it('uses the SCROLL cap when a phone over-reports offsetHeight', () => {
|
|
74
|
+
// High-DPR mobile: layout says 24M, but the container only scrolls ~6M.
|
|
75
|
+
// Trusting offsetHeight is exactly what stranded the last rows.
|
|
76
|
+
const cap = resolveMaxDomHeight(24000000, 6000000, FALLBACK);
|
|
77
|
+
expect(cap).toBeLessThanOrEqual(6000000);
|
|
78
|
+
expect(cap).toBeGreaterThan(5900000);
|
|
79
|
+
});
|
|
80
|
+
it('shaves a safety margin so the spacer never sits at the exact edge', () => {
|
|
81
|
+
const cap = resolveMaxDomHeight(10000000, 10000000, FALLBACK);
|
|
82
|
+
expect(cap).toBeLessThan(10000000);
|
|
83
|
+
});
|
|
84
|
+
it('falls back when both readings are junk (jsdom returns 0)', () => {
|
|
85
|
+
expect(resolveMaxDomHeight(0, 0, FALLBACK)).toBe(FALLBACK);
|
|
86
|
+
});
|
|
87
|
+
it('falls back when the browser did not clamp (returned ~1e9)', () => {
|
|
88
|
+
expect(resolveMaxDomHeight(1000000000, 1000000000, FALLBACK)).toBe(FALLBACK);
|
|
89
|
+
});
|
|
90
|
+
it('ignores a junk signal but trusts the valid one', () => {
|
|
91
|
+
// offsetHeight unclamped/garbage, scrollHeight is the real cap.
|
|
92
|
+
const cap = resolveMaxDomHeight(1000000000, 5000000, FALLBACK);
|
|
93
|
+
expect(cap).toBeLessThanOrEqual(5000000);
|
|
94
|
+
expect(cap).toBeGreaterThan(4900000);
|
|
95
|
+
});
|
|
96
|
+
it('feeds straight into the scaler so the last row stays reachable', () => {
|
|
97
|
+
const maxDom = resolveMaxDomHeight(24000000, 6000000, FALLBACK);
|
|
98
|
+
const trueTotal = 500000 * 32; // 16M px of rows, over the phone cap
|
|
99
|
+
const s = createRowScrollScaling(trueTotal, maxDom, VIEWPORT);
|
|
100
|
+
expect(s.active).toBe(true);
|
|
101
|
+
expect(s.domTotal).toBeLessThanOrEqual(maxDom);
|
|
102
|
+
const domMax = s.domTotal - VIEWPORT;
|
|
103
|
+
expect(s.domToLogical(domMax)).toBeCloseTo(trueTotal - VIEWPORT, 0);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('createRowScrollScaling - edge cases', () => {
|
|
107
|
+
it('handles an empty grid without dividing by zero', () => {
|
|
108
|
+
const s = createRowScrollScaling(0, 17895697, VIEWPORT);
|
|
109
|
+
expect(s.active).toBe(false);
|
|
110
|
+
expect(s.domTotal).toBe(0);
|
|
111
|
+
expect(s.domToLogical(0)).toBe(0);
|
|
112
|
+
expect(s.logicalToDom(0)).toBe(0);
|
|
113
|
+
});
|
|
114
|
+
it('handles content shorter than the viewport', () => {
|
|
115
|
+
const s = createRowScrollScaling(200, 17895697, VIEWPORT);
|
|
116
|
+
expect(s.active).toBe(false);
|
|
117
|
+
expect(s.domToLogical(0)).toBe(0);
|
|
118
|
+
});
|
|
119
|
+
it('extreme scale (100M rows) keeps the last row reachable', () => {
|
|
120
|
+
const trueTotal = 100000000 * 32; // 3.2 billion px
|
|
121
|
+
const s = createRowScrollScaling(trueTotal, 33554400, VIEWPORT);
|
|
122
|
+
expect(s.active).toBe(true);
|
|
123
|
+
expect(s.domTotal).toBe(33554400);
|
|
124
|
+
const domMax = s.domTotal - VIEWPORT;
|
|
125
|
+
expect(s.domToLogical(domMax)).toBeCloseTo(trueTotal - VIEWPORT, 0);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -7,5 +7,7 @@ export declare function createSvelteVirtualizer(options: VirtualizerOptions): {
|
|
|
7
7
|
scrollToIndex: (index: number) => void;
|
|
8
8
|
getVirtualItems: () => import("./types").VirtualItem[];
|
|
9
9
|
getTotalSize: () => number;
|
|
10
|
+
getOffsetForIndex: (index: number) => number;
|
|
11
|
+
getSizeForIndex: (index: number) => number;
|
|
10
12
|
getState: () => import("./types").VirtualizerState;
|
|
11
13
|
};
|
|
@@ -15,6 +15,8 @@ export function createSvelteVirtualizer(options) {
|
|
|
15
15
|
scrollToIndex: virtualizer.scrollToIndex,
|
|
16
16
|
getVirtualItems: virtualizer.getVirtualItems,
|
|
17
17
|
getTotalSize: virtualizer.getTotalSize,
|
|
18
|
+
getOffsetForIndex: virtualizer.getOffsetForIndex,
|
|
19
|
+
getSizeForIndex: virtualizer.getSizeForIndex,
|
|
18
20
|
getState: virtualizer.getState,
|
|
19
21
|
};
|
|
20
22
|
}
|
|
@@ -7,6 +7,13 @@ export declare function createVirtualizer(initial: VirtualizerOptions): {
|
|
|
7
7
|
scrollToIndex(index: number): void;
|
|
8
8
|
getVirtualItems(): VirtualItem[];
|
|
9
9
|
getTotalSize(): number;
|
|
10
|
+
/** Cumulative offset of row `index` from the top in px, regardless
|
|
11
|
+
* of whether `estimateSize` is uniform or per-index. Uses the
|
|
12
|
+
* cached offsets array under function-form sizing so the lookup
|
|
13
|
+
* is O(1) instead of O(index). */
|
|
14
|
+
getOffsetForIndex(index: number): number;
|
|
15
|
+
/** Height of row `index` in px (whichever estimateSize provides). */
|
|
16
|
+
getSizeForIndex(index: number): number;
|
|
10
17
|
getState(): VirtualizerState;
|
|
11
18
|
subscribe(listener: VirtualizerListener): () => boolean;
|
|
12
19
|
};
|
|
@@ -221,6 +221,36 @@ export function createVirtualizer(initial) {
|
|
|
221
221
|
getTotalSize() {
|
|
222
222
|
return state.totalSize;
|
|
223
223
|
},
|
|
224
|
+
/** Cumulative offset of row `index` from the top in px, regardless
|
|
225
|
+
* of whether `estimateSize` is uniform or per-index. Uses the
|
|
226
|
+
* cached offsets array under function-form sizing so the lookup
|
|
227
|
+
* is O(1) instead of O(index). */
|
|
228
|
+
getOffsetForIndex(index) {
|
|
229
|
+
if (index <= 0)
|
|
230
|
+
return 0;
|
|
231
|
+
const count = Math.max(options.count, 0);
|
|
232
|
+
const bounded = Math.min(index, count);
|
|
233
|
+
if (typeof options.estimateSize === 'function') {
|
|
234
|
+
const offsets = getOffsets();
|
|
235
|
+
if (offsets)
|
|
236
|
+
return offsets[bounded] ?? 0;
|
|
237
|
+
let acc = 0;
|
|
238
|
+
const fn = options.estimateSize;
|
|
239
|
+
for (let i = 0; i < bounded; i += 1)
|
|
240
|
+
acc += Math.max(fn(i), 1);
|
|
241
|
+
return acc;
|
|
242
|
+
}
|
|
243
|
+
return bounded * Math.max(options.estimateSize, 1);
|
|
244
|
+
},
|
|
245
|
+
/** Height of row `index` in px (whichever estimateSize provides). */
|
|
246
|
+
getSizeForIndex(index) {
|
|
247
|
+
if (index < 0 || index >= options.count)
|
|
248
|
+
return 0;
|
|
249
|
+
if (typeof options.estimateSize === 'function') {
|
|
250
|
+
return Math.max(options.estimateSize(index), 1);
|
|
251
|
+
}
|
|
252
|
+
return Math.max(options.estimateSize, 1);
|
|
253
|
+
},
|
|
224
254
|
getState() {
|
|
225
255
|
return state;
|
|
226
256
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svgrid/grid",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"@vitest/coverage-v8": "^4.1.5",
|
|
61
61
|
"eslint-plugin-svelte": "^3.17.1",
|
|
62
62
|
"jsdom": "^29.1.1",
|
|
63
|
+
"publint": "^0.3.21",
|
|
63
64
|
"svelte": "^5.55.5",
|
|
64
65
|
"svelte-check": "^4.4.6"
|
|
65
66
|
},
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<script lang="ts" generics="TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData">
|
|
2
|
+
import {
|
|
3
|
+
getGridCellA11yProps,
|
|
4
|
+
getGridCellDomId,
|
|
5
|
+
getGridHeaderA11yProps,
|
|
6
|
+
getGridRootA11yProps,
|
|
7
|
+
getGridRowA11yProps,
|
|
8
|
+
type EditorContext,
|
|
9
|
+
type CellEditorOption,
|
|
10
|
+
type Column,
|
|
11
|
+
type Row,
|
|
12
|
+
type RowData,
|
|
13
|
+
type TableFeatures,
|
|
14
|
+
} from "./index";
|
|
15
|
+
import "./sv-grid-scrollbar";
|
|
16
|
+
import type { Snippet } from "svelte";
|
|
17
|
+
import {
|
|
18
|
+
RenderSnippetConfig,
|
|
19
|
+
RenderComponentConfig,
|
|
20
|
+
} from "./render-component";
|
|
21
|
+
import {
|
|
22
|
+
buildSparkline,
|
|
23
|
+
toSparklineValues,
|
|
24
|
+
} from "./sparkline";
|
|
25
|
+
import SvGridDropdown from "./SvGridDropdown.svelte";
|
|
26
|
+
import type {
|
|
27
|
+
Props,
|
|
28
|
+
SelectionPoint,
|
|
29
|
+
SelectionRange,
|
|
30
|
+
CellEditState,
|
|
31
|
+
FilterOperator,
|
|
32
|
+
FilterOption,
|
|
33
|
+
MenuPosition,
|
|
34
|
+
} from "./SvGrid.types";
|
|
35
|
+
import {
|
|
36
|
+
cfTextStyle,
|
|
37
|
+
fmtStat,
|
|
38
|
+
getEditableInputValue,
|
|
39
|
+
getEditorInputType,
|
|
40
|
+
toValueArray,
|
|
41
|
+
getOptionLabel,
|
|
42
|
+
getOptionColor,
|
|
43
|
+
colorfulChipStyle,
|
|
44
|
+
getEditorClass,
|
|
45
|
+
} from "./SvGrid.helpers";
|
|
46
|
+
import {
|
|
47
|
+
} from "./SvGrid.controller.svelte";
|
|
48
|
+
import type { SvGridController } from "./SvGrid.controller.svelte";
|
|
49
|
+
|
|
50
|
+
let { ctrl }: { ctrl: SvGridController<TFeatures, TData> } = $props();
|
|
51
|
+
|
|
52
|
+
// View facade: re-bind the controller's reactive members so the markup
|
|
53
|
+
// (moved verbatim from SvGrid.svelte) stays identical.
|
|
54
|
+
const paginationEnabled = $derived(ctrl.paginationEnabled);
|
|
55
|
+
const grid = $derived(ctrl.grid);
|
|
56
|
+
const paginationState = $derived(ctrl.paginationState);
|
|
57
|
+
const allRowsBeforePagination = $derived(ctrl.allRowsBeforePagination);
|
|
58
|
+
const statusBarEnabled = $derived(ctrl.statusBarEnabled);
|
|
59
|
+
const statusBarAggregates = $derived(ctrl.statusBarAggregates);
|
|
60
|
+
const statusBarStats = $derived(ctrl.statusBarStats);
|
|
61
|
+
const changePage = $derived(ctrl.changePage);
|
|
62
|
+
const goToPage = $derived(ctrl.goToPage);
|
|
63
|
+
const setPageSize = $derived(ctrl.setPageSize);
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
{#if statusBarEnabled && statusBarStats}
|
|
67
|
+
{@const s = statusBarStats}
|
|
68
|
+
<div class="sv-grid-status-bar" role="status" aria-live="polite">
|
|
69
|
+
{#each statusBarAggregates as agg (agg)}
|
|
70
|
+
{#if agg === "count"}
|
|
71
|
+
<span class="sv-grid-status-item"
|
|
72
|
+
><span class="sv-grid-status-label">Count</span>{fmtStat(s.count)}</span
|
|
73
|
+
>
|
|
74
|
+
{:else if agg === "numericCount"}
|
|
75
|
+
<span class="sv-grid-status-item"
|
|
76
|
+
><span class="sv-grid-status-label">Numeric</span>{fmtStat(s.numericCount)}</span
|
|
77
|
+
>
|
|
78
|
+
{:else if s.numericCount > 0 && agg === "sum"}
|
|
79
|
+
<span class="sv-grid-status-item"
|
|
80
|
+
><span class="sv-grid-status-label">Sum</span>{fmtStat(s.sum)}</span
|
|
81
|
+
>
|
|
82
|
+
{:else if s.numericCount > 0 && agg === "avg"}
|
|
83
|
+
<span class="sv-grid-status-item"
|
|
84
|
+
><span class="sv-grid-status-label">Avg</span>{fmtStat(s.avg)}</span
|
|
85
|
+
>
|
|
86
|
+
{:else if s.numericCount > 0 && agg === "min"}
|
|
87
|
+
<span class="sv-grid-status-item"
|
|
88
|
+
><span class="sv-grid-status-label">Min</span>{fmtStat(s.min)}</span
|
|
89
|
+
>
|
|
90
|
+
{:else if s.numericCount > 0 && agg === "max"}
|
|
91
|
+
<span class="sv-grid-status-item"
|
|
92
|
+
><span class="sv-grid-status-label">Max</span>{fmtStat(s.max)}</span
|
|
93
|
+
>
|
|
94
|
+
{/if}
|
|
95
|
+
{/each}
|
|
96
|
+
</div>
|
|
97
|
+
{/if}
|
|
98
|
+
|
|
99
|
+
{#if paginationEnabled}
|
|
100
|
+
{@const totalRows = allRowsBeforePagination.length}
|
|
101
|
+
{@const pageSize = paginationState.pageSize}
|
|
102
|
+
{@const pageCount = Math.max(1, Math.ceil(totalRows / pageSize))}
|
|
103
|
+
{@const currentPage = Math.min(paginationState.pageIndex + 1, pageCount)}
|
|
104
|
+
{@const rangeStart =
|
|
105
|
+
totalRows === 0 ? 0 : (currentPage - 1) * pageSize + 1}
|
|
106
|
+
{@const rangeEnd = Math.min(totalRows, currentPage * pageSize)}
|
|
107
|
+
{@const onFirst = currentPage <= 1}
|
|
108
|
+
{@const onLast = currentPage >= pageCount}
|
|
109
|
+
<div class="sv-grid-pagination" role="navigation" aria-label="Pagination">
|
|
110
|
+
<label class="sv-grid-pagination-pagesize">
|
|
111
|
+
<span>Page Size:</span>
|
|
112
|
+
<select
|
|
113
|
+
onchange={(event) =>
|
|
114
|
+
setPageSize(
|
|
115
|
+
parseInt((event.currentTarget as HTMLSelectElement).value, 10),
|
|
116
|
+
)}
|
|
117
|
+
>
|
|
118
|
+
<option value="10" selected={pageSize === 10}>10</option>
|
|
119
|
+
<option value="25" selected={pageSize === 25}>25</option>
|
|
120
|
+
<option value="50" selected={pageSize === 50}>50</option>
|
|
121
|
+
<option value="100" selected={pageSize === 100}>100</option>
|
|
122
|
+
</select>
|
|
123
|
+
</label>
|
|
124
|
+
<span class="sv-grid-pagination-range">
|
|
125
|
+
<strong>{rangeStart.toLocaleString()}</strong> to
|
|
126
|
+
<strong>{rangeEnd.toLocaleString()}</strong> of
|
|
127
|
+
<strong>{totalRows.toLocaleString()}</strong>
|
|
128
|
+
</span>
|
|
129
|
+
<div class="sv-grid-pagination-nav">
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
class="sv-grid-pagination-btn"
|
|
133
|
+
disabled={onFirst}
|
|
134
|
+
onclick={() => goToPage(0)}
|
|
135
|
+
aria-label="First page">⇤</button
|
|
136
|
+
>
|
|
137
|
+
<button
|
|
138
|
+
type="button"
|
|
139
|
+
class="sv-grid-pagination-btn"
|
|
140
|
+
disabled={onFirst}
|
|
141
|
+
onclick={() => changePage(-1)}
|
|
142
|
+
aria-label="Previous page">‹</button
|
|
143
|
+
>
|
|
144
|
+
<span class="sv-grid-pagination-label">
|
|
145
|
+
Page <strong>{currentPage.toLocaleString()}</strong> of
|
|
146
|
+
<strong>{pageCount.toLocaleString()}</strong>
|
|
147
|
+
</span>
|
|
148
|
+
<button
|
|
149
|
+
type="button"
|
|
150
|
+
class="sv-grid-pagination-btn"
|
|
151
|
+
disabled={onLast}
|
|
152
|
+
onclick={() => changePage(1)}
|
|
153
|
+
aria-label="Next page">›</button
|
|
154
|
+
>
|
|
155
|
+
<button
|
|
156
|
+
type="button"
|
|
157
|
+
class="sv-grid-pagination-btn"
|
|
158
|
+
disabled={onLast}
|
|
159
|
+
onclick={() => goToPage(pageCount - 1)}
|
|
160
|
+
aria-label="Last page">⇥</button
|
|
161
|
+
>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
{/if}
|