@svgrid/grid 1.1.0 → 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/dist/GridMenus.svelte +205 -127
- package/dist/SvGrid.controller.svelte.d.ts +44 -1
- package/dist/SvGrid.controller.svelte.js +223 -38
- package/dist/SvGrid.css +2012 -1709
- package/dist/SvGrid.helpers.test.d.ts +1 -0
- package/dist/SvGrid.helpers.test.js +298 -0
- package/dist/SvGrid.svelte +2346 -2043
- package/dist/SvGrid.types.d.ts +91 -4
- 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.js +59 -29
- package/dist/cell-render.test.d.ts +1 -0
- package/dist/cell-render.test.js +338 -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/clipboard.js +88 -24
- 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.test.d.ts +1 -0
- package/dist/columns.test.js +625 -0
- package/dist/core.d.ts +85 -0
- package/dist/editing.d.ts +7 -0
- package/dist/editing.js +191 -5
- 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/facet-buckets.test.d.ts +1 -0
- package/dist/facet-buckets.test.js +296 -0
- package/dist/filter-operators.test.d.ts +1 -0
- package/dist/filter-operators.test.js +135 -0
- package/dist/hyperformula-adapter.test.d.ts +1 -0
- package/dist/hyperformula-adapter.test.js +205 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/keyboard-handlers.coverage.test.d.ts +1 -0
- package/dist/keyboard-handlers.coverage.test.js +495 -0
- package/dist/keyboard-handlers.js +7 -2
- package/dist/menus.js +1 -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/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.test.d.ts +1 -0
- package/dist/row-resize.test.js +329 -0
- package/dist/scroll-sync.js +3 -0
- package/dist/scroll-sync.test.d.ts +1 -0
- package/dist/scroll-sync.test.js +290 -0
- package/dist/selection.d.ts +7 -1
- package/dist/selection.js +76 -36
- 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 +30 -0
- package/dist/spreadsheet.js +48 -0
- package/dist/spreadsheet.test.d.ts +1 -0
- package/dist/spreadsheet.test.js +446 -0
- package/dist/sv-grid-scrollbar.js +13 -1
- package/dist/svgrid-wrapper.types.d.ts +19 -0
- package/dist/svgrid.behavior.test.js +20 -0
- package/dist/svgrid.interaction.test.js +31 -0
- package/dist/svgrid.new-features.wrapper.test.js +34 -2
- package/dist/test-setup.js +9 -3
- package/dist/virtualization/scroll-scaling.d.ts +17 -0
- package/dist/virtualization/scroll-scaling.js +35 -0
- package/dist/virtualization/scroll-scaling.test.js +42 -1
- package/package.json +2 -1
- package/src/GridMenus.svelte +205 -127
- package/src/SvGrid.controller.svelte.ts +2352 -2195
- package/src/SvGrid.css +2012 -1747
- package/src/SvGrid.svelte +2346 -2047
- package/src/SvGrid.types.ts +537 -456
- package/src/aligned-grids.test.ts +80 -0
- package/src/aligned-grids.ts +87 -0
- package/src/build-api.ts +683 -663
- package/src/clipboard.test.ts +49 -0
- package/src/clipboard.ts +51 -23
- 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/core.ts +78 -0
- package/src/editing.ts +669 -513
- package/src/index.ts +12 -0
- package/src/menus.ts +1 -0
- package/src/row-drag.test.ts +168 -0
- package/src/row-drag.ts +255 -0
- package/src/scroll-sync.ts +2 -0
- package/src/selection.multi-range.test.ts +61 -0
- package/src/selection.ts +71 -37
- package/src/spreadsheet.test.ts +489 -445
- package/src/spreadsheet.ts +304 -246
- package/src/svgrid-wrapper.types.ts +19 -0
- package/src/svgrid.new-features.wrapper.test.ts +2 -2
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { createServerDataSource } from './server-data-source';
|
|
3
|
+
const flush = () => new Promise((r) => setTimeout(r, 0));
|
|
4
|
+
describe('createServerDataSource - request params', () => {
|
|
5
|
+
it('passes startRow/endRow/page/sort/filter through to getRows', async () => {
|
|
6
|
+
const getRows = vi.fn(async (_req) => ({ rows: [], rowCount: 0 }));
|
|
7
|
+
const ctl = createServerDataSource({ getRows }, { pageSize: 20, onChange: () => { } });
|
|
8
|
+
ctl.setFilter({ global: 'abc', columns: { name: { operator: 'eq', value: 'x' } } });
|
|
9
|
+
await flush();
|
|
10
|
+
const req = getRows.mock.calls.at(-1)[0];
|
|
11
|
+
expect(req).toMatchObject({
|
|
12
|
+
startRow: 0,
|
|
13
|
+
endRow: 20,
|
|
14
|
+
pageIndex: 0,
|
|
15
|
+
pageSize: 20,
|
|
16
|
+
filterModel: { global: 'abc', columns: { name: { operator: 'eq', value: 'x' } } },
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
it('setFilter resets to page 0', async () => {
|
|
20
|
+
const getRows = vi.fn(async () => ({ rows: [], rowCount: 100 }));
|
|
21
|
+
const ctl = createServerDataSource({ getRows }, { pageSize: 10, onChange: () => { } });
|
|
22
|
+
ctl.setPage(3);
|
|
23
|
+
await flush();
|
|
24
|
+
ctl.setFilter({ global: 'q' });
|
|
25
|
+
await flush();
|
|
26
|
+
expect(ctl.getState().pageIndex).toBe(0);
|
|
27
|
+
const req = getRows.mock.calls.at(-1)[0];
|
|
28
|
+
expect(req.startRow).toBe(0);
|
|
29
|
+
});
|
|
30
|
+
it('uses the default page size of 50 when unspecified', () => {
|
|
31
|
+
const ctl = createServerDataSource({ getRows: async () => ({ rows: [], rowCount: 0 }) }, { onChange: () => { } });
|
|
32
|
+
expect(ctl.getState().pageSize).toBe(50);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('createServerDataSource - setPage edge cases', () => {
|
|
36
|
+
it('clamps a negative page index to 0', async () => {
|
|
37
|
+
const getRows = vi.fn(async () => ({ rows: [], rowCount: 0 }));
|
|
38
|
+
const ctl = createServerDataSource({ getRows }, { pageSize: 10, onChange: () => { } });
|
|
39
|
+
ctl.setPage(5);
|
|
40
|
+
await flush();
|
|
41
|
+
getRows.mockClear();
|
|
42
|
+
ctl.setPage(-3);
|
|
43
|
+
await flush();
|
|
44
|
+
expect(ctl.getState().pageIndex).toBe(0);
|
|
45
|
+
expect(getRows).toHaveBeenCalledTimes(1);
|
|
46
|
+
});
|
|
47
|
+
it('does nothing when the page index is unchanged', async () => {
|
|
48
|
+
const getRows = vi.fn(async () => ({ rows: [], rowCount: 0 }));
|
|
49
|
+
const ctl = createServerDataSource({ getRows }, { pageSize: 10, onChange: () => { } });
|
|
50
|
+
await flush();
|
|
51
|
+
getRows.mockClear();
|
|
52
|
+
ctl.setPage(0); // already on page 0
|
|
53
|
+
await flush();
|
|
54
|
+
expect(getRows).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('createServerDataSource - setPageSize', () => {
|
|
58
|
+
it('clamps page size to a minimum of 1, resets page, and re-fetches', async () => {
|
|
59
|
+
const getRows = vi.fn(async () => ({ rows: [], rowCount: 0 }));
|
|
60
|
+
const ctl = createServerDataSource({ getRows }, { pageSize: 10, onChange: () => { } });
|
|
61
|
+
ctl.setPage(2);
|
|
62
|
+
await flush();
|
|
63
|
+
getRows.mockClear();
|
|
64
|
+
ctl.setPageSize(0);
|
|
65
|
+
await flush();
|
|
66
|
+
expect(ctl.getState().pageSize).toBe(1);
|
|
67
|
+
expect(ctl.getState().pageIndex).toBe(0);
|
|
68
|
+
expect(getRows).toHaveBeenCalledTimes(1);
|
|
69
|
+
});
|
|
70
|
+
it('recomputes pageCount from total and page size', async () => {
|
|
71
|
+
const getRows = vi.fn(async () => ({ rows: [], rowCount: 95 }));
|
|
72
|
+
const ctl = createServerDataSource({ getRows }, { pageSize: 10, onChange: () => { } });
|
|
73
|
+
ctl.refresh();
|
|
74
|
+
await flush();
|
|
75
|
+
expect(ctl.getState().pageCount).toBe(10); // ceil(95/10)
|
|
76
|
+
ctl.setPageSize(20);
|
|
77
|
+
await flush();
|
|
78
|
+
expect(ctl.getState().pageCount).toBe(5); // ceil(95/20)
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('createServerDataSource - error handling', () => {
|
|
82
|
+
it('captures a rejection into state.error and clears rows', async () => {
|
|
83
|
+
const boom = new Error('network down');
|
|
84
|
+
const ctl = createServerDataSource({ getRows: async () => { throw boom; } }, { pageSize: 10, onChange: () => { } });
|
|
85
|
+
ctl.refresh();
|
|
86
|
+
await flush();
|
|
87
|
+
const s = ctl.getState();
|
|
88
|
+
expect(s.error).toBe(boom);
|
|
89
|
+
expect(s.rows).toEqual([]);
|
|
90
|
+
expect(s.loading).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
it('clears a prior error on the next successful fetch', async () => {
|
|
93
|
+
let fail = true;
|
|
94
|
+
const ctl = createServerDataSource({
|
|
95
|
+
getRows: async () => {
|
|
96
|
+
if (fail)
|
|
97
|
+
throw new Error('x');
|
|
98
|
+
return { rows: [{ id: 1 }], rowCount: 1 };
|
|
99
|
+
},
|
|
100
|
+
}, { pageSize: 10, onChange: () => { } });
|
|
101
|
+
ctl.refresh();
|
|
102
|
+
await flush();
|
|
103
|
+
expect(ctl.getState().error).toBeInstanceOf(Error);
|
|
104
|
+
fail = false;
|
|
105
|
+
ctl.refresh();
|
|
106
|
+
await flush();
|
|
107
|
+
expect(ctl.getState().error).toBe(null);
|
|
108
|
+
expect(ctl.getState().rows).toEqual([{ id: 1 }]);
|
|
109
|
+
});
|
|
110
|
+
it('ignores a stale REJECTION when a newer request superseded it', async () => {
|
|
111
|
+
let call = 0;
|
|
112
|
+
const src = {
|
|
113
|
+
getRows: vi.fn(async (req) => {
|
|
114
|
+
call += 1;
|
|
115
|
+
if (call === 1) {
|
|
116
|
+
await new Promise((r) => setTimeout(r, 40));
|
|
117
|
+
throw new Error('stale failure');
|
|
118
|
+
}
|
|
119
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
120
|
+
return { rows: [{ id: req.startRow }], rowCount: 100 };
|
|
121
|
+
}),
|
|
122
|
+
};
|
|
123
|
+
const ctl = createServerDataSource(src, { pageSize: 10, onChange: () => { } });
|
|
124
|
+
ctl.refresh(); // slow + throws
|
|
125
|
+
ctl.setPage(2); // fast + wins
|
|
126
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
127
|
+
const s = ctl.getState();
|
|
128
|
+
expect(s.error).toBe(null); // stale rejection did not land
|
|
129
|
+
expect(s.rows).toEqual([{ id: 20 }]);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('createServerDataSource - dispose', () => {
|
|
133
|
+
it('refresh after dispose does not call getRows', async () => {
|
|
134
|
+
const getRows = vi.fn(async () => ({ rows: [], rowCount: 0 }));
|
|
135
|
+
const ctl = createServerDataSource({ getRows }, { pageSize: 10, onChange: () => { } });
|
|
136
|
+
ctl.dispose();
|
|
137
|
+
ctl.refresh();
|
|
138
|
+
await flush();
|
|
139
|
+
expect(getRows).not.toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
it('a response resolving after dispose is ignored', async () => {
|
|
142
|
+
const onChange = vi.fn();
|
|
143
|
+
const ctl = createServerDataSource({
|
|
144
|
+
getRows: async () => {
|
|
145
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
146
|
+
return { rows: [{ id: 1 }], rowCount: 1 };
|
|
147
|
+
},
|
|
148
|
+
}, { pageSize: 10, onChange });
|
|
149
|
+
ctl.refresh();
|
|
150
|
+
ctl.dispose();
|
|
151
|
+
await new Promise((r) => setTimeout(r, 60));
|
|
152
|
+
expect(ctl.getState().rows).toEqual([]);
|
|
153
|
+
});
|
|
154
|
+
});
|
package/dist/spreadsheet.d.ts
CHANGED
|
@@ -42,6 +42,36 @@ export type MergeSpec = {
|
|
|
42
42
|
/** Default 1. */
|
|
43
43
|
colspan?: number;
|
|
44
44
|
};
|
|
45
|
+
/** A column with declarative spanning callbacks, as accepted by
|
|
46
|
+
* `spansToMerges`. Matches the relevant slice of `ColumnDef`. */
|
|
47
|
+
export type SpanColumn<TData = Record<string, unknown>> = {
|
|
48
|
+
id: string;
|
|
49
|
+
field?: string;
|
|
50
|
+
colSpan?: (params: {
|
|
51
|
+
data: TData;
|
|
52
|
+
rowIndex: number;
|
|
53
|
+
columnId: string;
|
|
54
|
+
value: unknown;
|
|
55
|
+
}) => number;
|
|
56
|
+
rowSpan?: (params: {
|
|
57
|
+
data: TData;
|
|
58
|
+
rowIndex: number;
|
|
59
|
+
columnId: string;
|
|
60
|
+
value: unknown;
|
|
61
|
+
}) => number;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Turn declarative per-column `colSpan` / `rowSpan` callbacks into a
|
|
65
|
+
* `MergeSpec[]` you can hand to `spreadsheetLayout` - so value-driven,
|
|
66
|
+
* AG-Grid-style spanning runs on the SAME real colspan/rowspan merge engine
|
|
67
|
+
* instead of a second code path. Recompute after sort/filter (indexes are
|
|
68
|
+
* display-row indexes). A common pattern is "merge runs of equal values":
|
|
69
|
+
*
|
|
70
|
+
* { field: 'region', rowSpan: ({ data, rowIndex }) =>
|
|
71
|
+
* rows.filter((r, i) => i >= rowIndex && r.region === data.region &&
|
|
72
|
+
* (i === rowIndex || rows[i-1].region === data.region)).length }
|
|
73
|
+
*/
|
|
74
|
+
export declare function spansToMerges<TData = Record<string, unknown>>(rows: ReadonlyArray<TData>, columns: ReadonlyArray<SpanColumn<TData>>, getValue?: (row: TData, columnId: string) => unknown): MergeSpec[];
|
|
45
75
|
/** Borders for one cell. Edges left unset render as the default
|
|
46
76
|
* cell border (i.e. no override). */
|
|
47
77
|
export type CellBorderSpec = {
|
package/dist/spreadsheet.js
CHANGED
|
@@ -19,6 +19,54 @@
|
|
|
19
19
|
* are actually rendered get the rowspan applied. Disable
|
|
20
20
|
* virtualisation on grids that need a continuous merge.
|
|
21
21
|
*/
|
|
22
|
+
/**
|
|
23
|
+
* Turn declarative per-column `colSpan` / `rowSpan` callbacks into a
|
|
24
|
+
* `MergeSpec[]` you can hand to `spreadsheetLayout` - so value-driven,
|
|
25
|
+
* AG-Grid-style spanning runs on the SAME real colspan/rowspan merge engine
|
|
26
|
+
* instead of a second code path. Recompute after sort/filter (indexes are
|
|
27
|
+
* display-row indexes). A common pattern is "merge runs of equal values":
|
|
28
|
+
*
|
|
29
|
+
* { field: 'region', rowSpan: ({ data, rowIndex }) =>
|
|
30
|
+
* rows.filter((r, i) => i >= rowIndex && r.region === data.region &&
|
|
31
|
+
* (i === rowIndex || rows[i-1].region === data.region)).length }
|
|
32
|
+
*/
|
|
33
|
+
export function spansToMerges(rows, columns, getValue) {
|
|
34
|
+
const merges = [];
|
|
35
|
+
const covered = new Set();
|
|
36
|
+
for (let r = 0; r < rows.length; r += 1) {
|
|
37
|
+
const row = rows[r];
|
|
38
|
+
for (let ci = 0; ci < columns.length; ci += 1) {
|
|
39
|
+
const col = columns[ci];
|
|
40
|
+
if (!col.colSpan && !col.rowSpan)
|
|
41
|
+
continue;
|
|
42
|
+
const key = `${r}:${ci}`;
|
|
43
|
+
if (covered.has(key))
|
|
44
|
+
continue;
|
|
45
|
+
const value = getValue
|
|
46
|
+
? getValue(row, col.id)
|
|
47
|
+
: row[col.field ?? col.id];
|
|
48
|
+
const params = { data: row, rowIndex: r, columnId: col.id, value };
|
|
49
|
+
const cs = Math.max(1, Math.floor(col.colSpan?.(params) ?? 1));
|
|
50
|
+
const rs = Math.max(1, Math.floor(col.rowSpan?.(params) ?? 1));
|
|
51
|
+
if (cs <= 1 && rs <= 1)
|
|
52
|
+
continue;
|
|
53
|
+
merges.push({
|
|
54
|
+
rowIndex: r,
|
|
55
|
+
columnId: col.id,
|
|
56
|
+
colspan: cs > 1 ? cs : undefined,
|
|
57
|
+
rowspan: rs > 1 ? rs : undefined,
|
|
58
|
+
});
|
|
59
|
+
for (let dr = 0; dr < rs; dr += 1) {
|
|
60
|
+
for (let dc = 0; dc < cs; dc += 1) {
|
|
61
|
+
if (dr === 0 && dc === 0)
|
|
62
|
+
continue;
|
|
63
|
+
covered.add(`${r + dr}:${ci + dc}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return merges;
|
|
69
|
+
}
|
|
22
70
|
const BORDER_KEYS = ['top', 'right', 'bottom', 'left'];
|
|
23
71
|
const MARK = 'data-svgrid-sheet';
|
|
24
72
|
function borderCss(spec) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|