@svgrid/grid 2.6.16 → 2.6.18
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/DockNodeView.svelte.d.ts +1 -1
- package/dist/DockPopoutHost.svelte.d.ts +1 -1
- package/dist/FlexRender.svelte.d.ts +1 -1
- package/dist/GridFooter.svelte +6 -3
- package/dist/GridFooter.svelte.d.ts +3 -3
- package/dist/GridMenus.svelte.d.ts +3 -3
- package/dist/SvAccordion.svelte.d.ts +3 -3
- package/dist/SvAutoComplete.svelte.d.ts +2 -2
- package/dist/SvBreadcrumb.svelte.d.ts +2 -2
- package/dist/SvButtonGroup.svelte.d.ts +3 -3
- package/dist/SvCalendar.svelte.d.ts +7 -7
- package/dist/SvCarousel.svelte.d.ts +1 -1
- package/dist/SvCheckBox.svelte.d.ts +1 -1
- package/dist/SvColorInput.svelte.d.ts +1 -1
- package/dist/SvComboBox.svelte.d.ts +2 -2
- package/dist/SvCommand.svelte.d.ts +2 -2
- package/dist/SvCountryInput.svelte.d.ts +1 -1
- package/dist/SvDateRangeInput.svelte.d.ts +3 -3
- package/dist/SvDateTimePicker.svelte.d.ts +4 -4
- package/dist/SvDockLayout.svelte.d.ts +2 -2
- package/dist/SvDockManager.svelte.d.ts +2 -2
- package/dist/SvDropDownList.svelte.d.ts +2 -2
- package/dist/SvDurationInput.svelte.d.ts +1 -1
- package/dist/SvField.svelte.d.ts +1 -1
- package/dist/SvFileUpload.svelte.d.ts +3 -3
- package/dist/SvForm.svelte.d.ts +4 -4
- package/dist/SvGauge.svelte.d.ts +1 -1
- package/dist/SvGrid.controller.svelte.d.ts +28 -28
- package/dist/SvGrid.controller.svelte.js +117 -45
- package/dist/SvGrid.css +42 -2
- package/dist/SvGrid.helpers.d.ts +2 -2
- package/dist/SvGrid.svelte.d.ts +3 -3
- package/dist/SvGrid.types.d.ts +28 -7
- package/dist/SvGridCellEditor.svelte.d.ts +2 -2
- package/dist/SvGridChart.svelte.d.ts +1 -1
- package/dist/SvGridChartPanel.svelte.d.ts +2 -2
- package/dist/SvGridChartView.svelte.d.ts +1 -1
- package/dist/SvGridDropdown.svelte.d.ts +1 -1
- package/dist/SvGridSelect.svelte.d.ts +4 -4
- package/dist/SvGroupCell.svelte.d.ts +1 -1
- package/dist/SvHoverCard.svelte.d.ts +1 -1
- package/dist/SvListBox.svelte.d.ts +2 -2
- package/dist/SvMaskedInput.svelte.d.ts +1 -1
- package/dist/SvMenu.svelte.d.ts +1 -1
- package/dist/SvMenuList.svelte.d.ts +3 -3
- package/dist/SvMenubar.svelte.d.ts +3 -3
- package/dist/SvMultiSelect.svelte.d.ts +3 -3
- package/dist/SvNavPane.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte.d.ts +1 -1
- package/dist/SvOtpInput.svelte.d.ts +1 -1
- package/dist/SvPasswordInput.svelte.d.ts +1 -1
- package/dist/SvPhoneInput.svelte.d.ts +2 -2
- package/dist/SvPopconfirm.svelte.d.ts +1 -1
- package/dist/SvPopover.svelte.d.ts +1 -1
- package/dist/SvRadioGroup.svelte.d.ts +3 -3
- package/dist/SvRating.svelte.d.ts +1 -1
- package/dist/SvSegmented.svelte.d.ts +3 -3
- package/dist/SvSlider.svelte.d.ts +1 -1
- package/dist/SvSparkline.svelte.d.ts +1 -1
- package/dist/SvSplitter.svelte.d.ts +3 -3
- package/dist/SvStepper.svelte.d.ts +2 -2
- package/dist/SvSwitchButton.svelte.d.ts +1 -1
- package/dist/SvTabs.svelte.d.ts +3 -3
- package/dist/SvTagsInput.svelte.d.ts +1 -1
- package/dist/SvTextArea.svelte.d.ts +1 -1
- package/dist/SvTextInput.svelte.d.ts +1 -1
- package/dist/SvTimePicker.svelte.d.ts +3 -3
- package/dist/SvTimeline.svelte.d.ts +2 -2
- package/dist/SvToggleButton.svelte.d.ts +1 -1
- package/dist/SvTooltip.svelte.d.ts +1 -1
- package/dist/SvTour.svelte.d.ts +3 -3
- package/dist/SvTree.svelte.d.ts +3 -3
- package/dist/SvTreeSelect.svelte.d.ts +3 -3
- package/dist/advanced-filter.svelte.d.ts +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/ai.js +1 -1
- package/dist/build-api.d.ts +2 -2
- package/dist/build-api.js +8 -8
- package/dist/builtin-editors.js +1 -1
- package/dist/cdn/{GridMenus-CPdy5dxw.js → GridMenus-B0F9iBrG.js} +1 -1
- package/dist/cdn/{GridMenus-BLajq_RP.js → GridMenus-IHK_l7m6.js} +1 -1
- package/dist/cdn/{src-CKpl94Cx.js → src-B1TdiyS8.js} +272 -270
- package/dist/cdn/{src-ogpR-lHu.js → src-Cd0tearp.js} +272 -270
- package/dist/cdn/svgrid.js +2 -2
- package/dist/cdn/svgrid.svelte-external.js +2 -2
- package/dist/cell-render.d.ts +3 -3
- package/dist/cell-render.js +6 -6
- package/dist/cell-values.d.ts +1 -1
- package/dist/cell-values.js +1 -1
- package/dist/chart-export.d.ts +1 -1
- package/dist/clipboard.d.ts +2 -2
- package/dist/clipboard.js +5 -5
- package/dist/column-groups.js +1 -1
- package/dist/columns.d.ts +2 -2
- package/dist/columns.js +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/createAutocomplete.svelte.d.ts +1 -1
- package/dist/createAutocomplete.svelte.js +2 -2
- package/dist/createButtonGroup.svelte.js +1 -1
- package/dist/createCalendar.svelte.d.ts +4 -4
- package/dist/createCalendar.svelte.js +4 -4
- package/dist/createCarousel.svelte.d.ts +1 -1
- package/dist/createCheckbox.svelte.js +1 -1
- package/dist/createColorInput.svelte.js +1 -1
- package/dist/createCombobox.svelte.d.ts +1 -1
- package/dist/createCombobox.svelte.js +2 -2
- package/dist/createCommand.svelte.d.ts +1 -1
- package/dist/createCommand.svelte.js +2 -2
- package/dist/createCountryInput.svelte.d.ts +1 -1
- package/dist/createCountryInput.svelte.js +2 -2
- package/dist/createDateTimePicker.svelte.d.ts +1 -1
- package/dist/createDateTimePicker.svelte.js +3 -3
- package/dist/createDropdownList.svelte.d.ts +1 -1
- package/dist/createDropdownList.svelte.js +2 -2
- package/dist/createFileUpload.svelte.js +1 -1
- package/dist/createForm.svelte.d.ts +1 -1
- package/dist/createForm.svelte.js +2 -2
- package/dist/createGrid.svelte.d.ts +1 -1
- package/dist/createGrid.svelte.js +1 -1
- package/dist/createGridState.svelte.d.ts +1 -1
- package/dist/createListbox.svelte.d.ts +1 -1
- package/dist/createListbox.svelte.js +2 -2
- package/dist/createMaskedInput.svelte.js +2 -2
- package/dist/createMenu.svelte.d.ts +2 -2
- package/dist/createMenu.svelte.js +2 -2
- package/dist/createNumberInput.svelte.js +1 -1
- package/dist/createOverlay.svelte.js +3 -3
- package/dist/createPagination.d.ts +1 -1
- package/dist/createPagination.js +1 -1
- package/dist/createPasswordInput.svelte.js +1 -1
- package/dist/createPhoneInput.svelte.js +3 -3
- package/dist/createPopoverSelect.svelte.d.ts +1 -1
- package/dist/createPopoverSelect.svelte.js +3 -3
- package/dist/createRadioGroup.svelte.js +1 -1
- package/dist/createRating.svelte.js +1 -1
- package/dist/createSlider.svelte.js +1 -1
- package/dist/createSwitch.svelte.js +1 -1
- package/dist/createTabs.svelte.d.ts +1 -1
- package/dist/createTagsInput.svelte.js +1 -1
- package/dist/createToggle.svelte.js +1 -1
- package/dist/createTooltip.svelte.js +1 -1
- package/dist/createTree.svelte.d.ts +1 -1
- package/dist/datetime/date-restrict.d.ts +1 -1
- package/dist/datetime/date-restrict.js +1 -1
- package/dist/datetime/date-selection.js +1 -1
- package/dist/dock-context.d.ts +1 -1
- package/dist/dock-manager-model.d.ts +1 -1
- package/dist/dock-manager-model.js +1 -1
- package/dist/dock-popout.d.ts +1 -1
- package/dist/dock-popout.js +1 -1
- package/dist/editing.d.ts +2 -2
- package/dist/editing.js +5 -5
- package/dist/editor-registry.d.ts +1 -1
- package/dist/editors/cell-editors.js +1 -1
- package/dist/export-format.d.ts +2 -2
- package/dist/export-format.js +2 -2
- package/dist/export-provider.d.ts +1 -1
- package/dist/facet-buckets.d.ts +1 -1
- package/dist/facet-buckets.js +1 -1
- package/dist/features.d.ts +2 -2
- package/dist/features.js +2 -2
- package/dist/filter-operators.d.ts +2 -2
- package/dist/filter-operators.js +1 -1
- package/dist/form-field.d.ts +1 -1
- package/dist/grid-messages.js +1 -1
- package/dist/headless.d.ts +11 -4
- package/dist/headless.js +11 -4
- package/dist/index.d.ts +120 -120
- package/dist/index.js +111 -111
- package/dist/keyboard-handlers.d.ts +2 -2
- package/dist/keyboard-handlers.js +2 -2
- package/dist/keyboard.d.ts +1 -1
- package/dist/menus.d.ts +3 -3
- package/dist/menus.js +3 -3
- package/dist/named-views.d.ts +1 -1
- package/dist/pivot-view.svelte.d.ts +1 -1
- package/dist/recurrence.d.ts +1 -1
- package/dist/recurrence.js +1 -1
- package/dist/row-drag.js +1 -1
- package/dist/scheduler-ical.d.ts +1 -1
- package/dist/scheduler-model.d.ts +3 -3
- package/dist/scheduler-model.js +2 -2
- package/dist/scroll-sync.d.ts +2 -2
- package/dist/scroll-sync.js +1 -1
- package/dist/selection.d.ts +2 -2
- package/dist/selection.js +3 -3
- package/dist/server-data-source.d.ts +1 -1
- package/dist/server-group-model.d.ts +1 -1
- package/dist/static-functions.d.ts +1 -1
- package/dist/static-functions.js +1 -1
- package/dist/subscribe.d.ts +1 -1
- package/dist/summaries.d.ts +2 -2
- package/dist/summaries.js +3 -3
- package/dist/svgrid-wrapper.types.d.ts +5 -5
- package/dist/themes/index.d.ts +3 -1
- package/dist/themes/index.js +4 -2
- package/dist/toast-store.svelte.js +1 -1
- package/dist/tree-select.d.ts +1 -1
- package/dist/virtualization/column-virtualizer.d.ts +3 -3
- package/dist/virtualization/column-virtualizer.js +1 -1
- package/dist/virtualization/svelte-virtualizer.svelte.d.ts +3 -3
- package/dist/virtualization/svelte-virtualizer.svelte.js +1 -1
- package/dist/virtualization/virtualizer.d.ts +1 -1
- package/package.json +14 -2
- package/src/GridFooter.svelte +6 -3
- package/src/SvGrid.controller.svelte.ts +90 -14
- package/src/SvGrid.css +42 -2
- package/src/SvGrid.types.ts +21 -0
- package/src/headless.ts +7 -0
- package/src/svgrid.behavior.test.ts +208 -0
- package/src/themes/index.ts +4 -2
package/dist/themes/index.d.ts
CHANGED
|
@@ -69,7 +69,9 @@ export type ThemePreset = {
|
|
|
69
69
|
};
|
|
70
70
|
/** The built-in theme presets, in gallery order (SvGrid's own theme first). */
|
|
71
71
|
export declare const themePresets: ThemePreset[];
|
|
72
|
-
/** The default preset (used when nothing is chosen)
|
|
72
|
+
/** The default preset (used when nothing is chosen): Ember, the house theme the
|
|
73
|
+
* demo gallery and svgrid.com open on, so a scaffolded or Studio-generated app
|
|
74
|
+
* looks like the demos unless a preset is picked. */
|
|
73
75
|
export declare const defaultThemePreset: ThemePreset;
|
|
74
76
|
/** Look up a preset by id. */
|
|
75
77
|
export declare const getThemePreset: (id?: string) => ThemePreset | undefined;
|
package/dist/themes/index.js
CHANGED
|
@@ -101,8 +101,10 @@ export const themePresets = [
|
|
|
101
101
|
light: { bg: '#eff1f5', fg: '#4c4f69', muted: '#5c5f77', border: '#ccd0da', headerBg: '#e6e9ef', headerFg: '#4c4f69', accent: '#8839ef', rowAlt: '#e6e9ef', rowHover: '#dce0e8', selectionBg: '#dce0e8' },
|
|
102
102
|
dark: { bg: '#1e1e2e', fg: '#cdd6f4', muted: '#a6adc8', border: '#313244', headerBg: '#181825', headerFg: '#cdd6f4', accent: '#cba6f7', rowAlt: '#1a1a28', rowHover: '#313244', selectionBg: '#45475a' } },
|
|
103
103
|
];
|
|
104
|
-
/** The default preset (used when nothing is chosen)
|
|
105
|
-
|
|
104
|
+
/** The default preset (used when nothing is chosen): Ember, the house theme the
|
|
105
|
+
* demo gallery and svgrid.com open on, so a scaffolded or Studio-generated app
|
|
106
|
+
* looks like the demos unless a preset is picked. */
|
|
107
|
+
export const defaultThemePreset = themePresets.find((t) => t.id === 'ember') ?? themePresets[0];
|
|
106
108
|
/** Look up a preset by id. */
|
|
107
109
|
export const getThemePreset = (id) => id ? themePresets.find((t) => t.id === id) : undefined;
|
|
108
110
|
/** WCAG 2.1 relative luminance. */
|
package/dist/tree-select.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* resolve the path from root to a value (for the trigger label). Framework-free
|
|
5
5
|
* + pure so they are unit-tested directly.
|
|
6
6
|
*/
|
|
7
|
-
import type { TreeSelectNode } from './ui-app.types';
|
|
7
|
+
import type { TreeSelectNode } from './ui-app.types.js';
|
|
8
8
|
export type FlatRow = {
|
|
9
9
|
node: TreeSelectNode;
|
|
10
10
|
depth: number;
|
|
@@ -9,14 +9,14 @@ export declare function createColumnVirtualizer(input: {
|
|
|
9
9
|
}): {
|
|
10
10
|
setViewportWidth(viewportWidth: number): void;
|
|
11
11
|
setHorizontalOffset(scrollOffset: number): void;
|
|
12
|
-
setOptions(next: Partial<import("./types").VirtualizerOptions>): void;
|
|
12
|
+
setOptions(next: Partial<import("./types.js").VirtualizerOptions>): void;
|
|
13
13
|
setScrollOffset(scrollOffset: number): void;
|
|
14
14
|
setViewportHeight(viewportHeight: number): void;
|
|
15
15
|
scrollToIndex(index: number): void;
|
|
16
|
-
getVirtualItems(): import("./types").VirtualItem[];
|
|
16
|
+
getVirtualItems(): import("./types.js").VirtualItem[];
|
|
17
17
|
getTotalSize(): number;
|
|
18
18
|
getOffsetForIndex(index: number): number;
|
|
19
19
|
getSizeForIndex(index: number): number;
|
|
20
|
-
getState(): import("./types").VirtualizerState;
|
|
20
|
+
getState(): import("./types.js").VirtualizerState;
|
|
21
21
|
subscribe(listener: () => void): () => boolean;
|
|
22
22
|
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type { VirtualizerOptions } from './types';
|
|
1
|
+
import type { VirtualizerOptions } from './types.js';
|
|
2
2
|
export declare function createSvelteVirtualizer(options: VirtualizerOptions): {
|
|
3
3
|
readonly version: number;
|
|
4
4
|
setOptions: (next: Partial<VirtualizerOptions>) => void;
|
|
5
5
|
setScrollOffset: (scrollOffset: number) => void;
|
|
6
6
|
setViewportHeight: (viewportHeight: number) => void;
|
|
7
7
|
scrollToIndex: (index: number) => void;
|
|
8
|
-
getVirtualItems: () => import("./types").VirtualItem[];
|
|
8
|
+
getVirtualItems: () => import("./types.js").VirtualItem[];
|
|
9
9
|
getTotalSize: () => number;
|
|
10
10
|
getOffsetForIndex: (index: number) => number;
|
|
11
11
|
getSizeForIndex: (index: number) => number;
|
|
12
|
-
getState: () => import("./types").VirtualizerState;
|
|
12
|
+
getState: () => import("./types.js").VirtualizerState;
|
|
13
13
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { VirtualItem, VirtualizerOptions, VirtualizerState } from './types';
|
|
1
|
+
import type { VirtualItem, VirtualizerOptions, VirtualizerState } from './types.js';
|
|
2
2
|
type VirtualizerListener = () => void;
|
|
3
3
|
/**
|
|
4
4
|
* The window to render when no DOM measurement exists yet.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svgrid/grid",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.18",
|
|
4
4
|
"description": "Svelte 5-native data grid and data table. Headless-first engine with virtual scrolling for 100k+ rows, Excel-style filtering, inline editing, grouping, tree data, pivot, and server-side data. Drop-in <SvGrid> component. TypeScript, SSR-ready.",
|
|
5
5
|
"author": "jQWidgets <boikom@jqwidgets.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,19 @@
|
|
|
34
34
|
"headless",
|
|
35
35
|
"headless-table",
|
|
36
36
|
"virtual-scroll",
|
|
37
|
+
"virtual-scrolling",
|
|
38
|
+
"virtualization",
|
|
39
|
+
"sortable",
|
|
40
|
+
"filterable",
|
|
41
|
+
"pagination",
|
|
42
|
+
"tree-grid",
|
|
43
|
+
"pivot-table",
|
|
37
44
|
"spreadsheet",
|
|
45
|
+
"ag-grid-alternative",
|
|
46
|
+
"tanstack-table",
|
|
47
|
+
"tanstack-table-alternative",
|
|
48
|
+
"svelte-headless-table",
|
|
49
|
+
"svelte-headless-table-alternative",
|
|
38
50
|
"typescript",
|
|
39
51
|
"svgrid"
|
|
40
52
|
],
|
|
@@ -110,7 +122,7 @@
|
|
|
110
122
|
"test:types": "svelte-check --tsconfig ./tsconfig.json",
|
|
111
123
|
"test:lib": "vitest run --coverage",
|
|
112
124
|
"test:build": "publint --strict",
|
|
113
|
-
"build": "svelte-package --input ./src --output ./dist && node ../../tools/strip-dist-tests.mjs ./dist && node ./scripts/build-cdn.mjs && node ./scripts/build-themes.mjs",
|
|
125
|
+
"build": "svelte-package --input ./src --output ./dist && node ../../tools/strip-dist-tests.mjs ./dist && node ../../tools/add-dist-extensions.mjs ./dist && node ./scripts/build-cdn.mjs && node ./scripts/build-themes.mjs",
|
|
114
126
|
"lint": "eslint ./src"
|
|
115
127
|
},
|
|
116
128
|
"devDependencies": {
|
package/src/GridFooter.svelte
CHANGED
|
@@ -117,9 +117,12 @@
|
|
|
117
117
|
onChange={(v) => setPageSize(Number(v))}
|
|
118
118
|
/>
|
|
119
119
|
{:else}
|
|
120
|
-
<!-- Mirrors SvGridDropdown's closed trigger
|
|
121
|
-
|
|
122
|
-
|
|
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. -->
|
|
123
126
|
<div class="sv-grid-dropdown">
|
|
124
127
|
<button
|
|
125
128
|
type="button"
|
|
@@ -595,8 +595,13 @@ export function createSvGridController<
|
|
|
595
595
|
((props.selectionMode ?? "both") === "row" ||
|
|
596
596
|
(props.selectionMode ?? "both") === "both"),
|
|
597
597
|
);
|
|
598
|
+
// `selectable` is the shortcut alias, so it wins over the fine-grained prop,
|
|
599
|
+
// which in turn wins over `selectionMode` - the same precedence the other
|
|
600
|
+
// shortcuts use. Unlike them it starts ON (selectionMode defaults to 'both'),
|
|
601
|
+
// so its real work is `selectable={false}`.
|
|
598
602
|
const enableCellSelectionEffective = $derived(
|
|
599
|
-
props.
|
|
603
|
+
props.selectable ??
|
|
604
|
+
props.enableCellSelection ??
|
|
600
605
|
((props.selectionMode ?? "both") === "cell" ||
|
|
601
606
|
(props.selectionMode ?? "both") === "both"),
|
|
602
607
|
);
|
|
@@ -2543,7 +2548,53 @@ export function createSvGridController<
|
|
|
2543
2548
|
// footer is correct on the first frame (no flicker).
|
|
2544
2549
|
const SUMMARY_DEFER_CELL_LIMIT = 50_000;
|
|
2545
2550
|
|
|
2546
|
-
|
|
2551
|
+
/** Deferred summaries for a grid too big to total inline. A grid under the
|
|
2552
|
+
* cell limit is handled by `eagerSummaries` below - synchronously, so the
|
|
2553
|
+
* totals are in the SERVER-rendered footer too. */
|
|
2554
|
+
let deferredSummaries = $state<Record<string, string>>({});
|
|
2555
|
+
|
|
2556
|
+
/**
|
|
2557
|
+
* The aggregator, handed over once `createSummaries(ctx)` has run - it needs
|
|
2558
|
+
* `ctx`, which cannot exist this early. Held in `$state` so `eagerSummaries`
|
|
2559
|
+
* recomputes when it lands.
|
|
2560
|
+
*
|
|
2561
|
+
* The whole dance is to keep the declaration ABOVE `ctx`: a `$derived`
|
|
2562
|
+
* referenced before its declaration is mis-compiled (the binding is dropped
|
|
2563
|
+
* and the `ctx` getter comes out with an empty body, so the footer read
|
|
2564
|
+
* `undefined` and threw on the first column id).
|
|
2565
|
+
*/
|
|
2566
|
+
let summarize = $state<
|
|
2567
|
+
| null
|
|
2568
|
+
| ((rows: ReadonlyArray<Row<TData>>, columns: ReadonlyArray<Column<TData>>) => Record<string, string>)
|
|
2569
|
+
>(null);
|
|
2570
|
+
|
|
2571
|
+
/**
|
|
2572
|
+
* Footer totals for a grid small enough to aggregate inline, derived rather
|
|
2573
|
+
* than assigned from an `$effect`.
|
|
2574
|
+
*
|
|
2575
|
+
* Effects never run during SSR, so when the totals lived only in the effect
|
|
2576
|
+
* the server emitted a summary row of EMPTY cells: the numbers appeared only
|
|
2577
|
+
* after hydration (a visible pop-in), and never at all for a reader with
|
|
2578
|
+
* JavaScript off - while the row still took up space and drew its border.
|
|
2579
|
+
* A `$derived` runs on the server too, so the totals ship with the HTML.
|
|
2580
|
+
*
|
|
2581
|
+
* `null` above the cell limit - that case stays on the rAF-deferred effect,
|
|
2582
|
+
* which keeps a huge grid painting before it totals.
|
|
2583
|
+
*/
|
|
2584
|
+
const eagerSummaries = $derived.by(() => {
|
|
2585
|
+
if (!summarize) return null;
|
|
2586
|
+
if (!(props.enableRowSummaries ?? true)) return null;
|
|
2587
|
+
const rows = allRows;
|
|
2588
|
+
const columns = allColumns;
|
|
2589
|
+
if (rows.length * columns.length > SUMMARY_DEFER_CELL_LIMIT) return null;
|
|
2590
|
+
// `computeSummaries` reads `editedCellValues` through `ctx`, so an inline
|
|
2591
|
+
// edit re-runs this too.
|
|
2592
|
+
return summarize(rows, columns);
|
|
2593
|
+
});
|
|
2594
|
+
|
|
2595
|
+
/** What the footer renders: the inline totals when the grid is small enough,
|
|
2596
|
+
* otherwise the rAF-deferred ones. */
|
|
2597
|
+
const summaryByColumn = $derived(eagerSummaries ?? deferredSummaries);
|
|
2547
2598
|
$effect(() => {
|
|
2548
2599
|
// Re-aggregate whenever the data / columns / edits change. We depend on
|
|
2549
2600
|
// `allRows` / `allColumns` / `editedCellValues` DIRECTLY - not the
|
|
@@ -2557,20 +2608,21 @@ export function createSvGridController<
|
|
|
2557
2608
|
const rows = allRows;
|
|
2558
2609
|
const columns = allColumns;
|
|
2559
2610
|
if (!(props.enableRowSummaries ?? true)) {
|
|
2560
|
-
|
|
2611
|
+
deferredSummaries = {};
|
|
2561
2612
|
return;
|
|
2562
2613
|
}
|
|
2563
2614
|
if (
|
|
2564
2615
|
rows.length * columns.length <= SUMMARY_DEFER_CELL_LIMIT ||
|
|
2565
2616
|
typeof requestAnimationFrame === "undefined"
|
|
2566
2617
|
) {
|
|
2567
|
-
|
|
2618
|
+
// Small enough to total inline - `eagerSummaries` already did, and it
|
|
2619
|
+
// wins in the getter, so there is nothing to recompute here.
|
|
2568
2620
|
return;
|
|
2569
2621
|
}
|
|
2570
2622
|
// Large grid: paint first, total a frame later.
|
|
2571
2623
|
let cancelled = false;
|
|
2572
2624
|
const handle = requestAnimationFrame(() => {
|
|
2573
|
-
if (!cancelled)
|
|
2625
|
+
if (!cancelled) deferredSummaries = computeSummaries(rows, columns);
|
|
2574
2626
|
});
|
|
2575
2627
|
return () => {
|
|
2576
2628
|
cancelled = true;
|
|
@@ -2778,18 +2830,39 @@ export function createSvGridController<
|
|
|
2778
2830
|
// width/height stays in sync. Always attached (window resize / parent
|
|
2779
2831
|
// layout shift / sidebar collapse can change the size whether the
|
|
2780
2832
|
// consumer passed a numeric or "100%" containerHeight).
|
|
2781
|
-
/** True
|
|
2782
|
-
*
|
|
2783
|
-
*
|
|
2784
|
-
*
|
|
2785
|
-
* scrollbar for ONE frame (based on the base column widths summing
|
|
2786
|
-
*
|
|
2787
|
-
*
|
|
2788
|
-
*
|
|
2833
|
+
/** True once the grid has measured its real container size and `fitColumns`
|
|
2834
|
+
* has had a chance to scale the columns to that width. Gates the scrollbar
|
|
2835
|
+
* visibility, the scrollbar gutter on a trailing right-aligned column and
|
|
2836
|
+
* the top pager: rendering those before this flips paints a horizontal
|
|
2837
|
+
* scrollbar for ONE frame (based on the base column widths summing larger
|
|
2838
|
+
* than the viewport), then immediately hides it once fit scaling kicks in -
|
|
2839
|
+
* visible as a "flashing horizontal scrollbar" every time a demo first
|
|
2840
|
+
* loads. */
|
|
2789
2841
|
let hasMeasured = $state(false);
|
|
2790
2842
|
|
|
2791
2843
|
$effect(() => {
|
|
2792
2844
|
if (!scrollContainer) return;
|
|
2845
|
+
// Measure SYNCHRONOUSLY here, not just from the observer below. Effects run
|
|
2846
|
+
// after the DOM is in place but before the browser paints, so reading the
|
|
2847
|
+
// container now lets the very first painted frame already carry the
|
|
2848
|
+
// scrollbars, the fit-scaled column widths, the 16px gutter on a trailing
|
|
2849
|
+
// right-aligned column and the top pager (which has a `border-top`).
|
|
2850
|
+
// `observeSizeRaf` has to defer its callback by a frame (or the
|
|
2851
|
+
// ResizeObserver re-notifies within one delivery cycle), so relying on it
|
|
2852
|
+
// alone guarantees one painted frame in the unmeasured state - a visible
|
|
2853
|
+
// flash on EVERY mount, and therefore on every navigation in an app that
|
|
2854
|
+
// recreates the grid (e.g. a `{#key}` around it, or a route that remounts).
|
|
2855
|
+
// `untrack` keeps the writes below from making this effect depend on
|
|
2856
|
+
// `viewportVersion` (it would then re-run and re-attach the observer).
|
|
2857
|
+
untrack(() => {
|
|
2858
|
+
// A zero-height container means the grid is not laid out yet (hidden
|
|
2859
|
+
// ancestor, `display: none`); leave it unmeasured and let the observer
|
|
2860
|
+
// flip it when the box becomes real.
|
|
2861
|
+
if (scrollContainer && scrollContainer.clientHeight > 0) {
|
|
2862
|
+
viewportVersion += 1;
|
|
2863
|
+
hasMeasured = true;
|
|
2864
|
+
}
|
|
2865
|
+
});
|
|
2793
2866
|
return observeSizeRaf(scrollContainer, () => {
|
|
2794
2867
|
viewportVersion += 1;
|
|
2795
2868
|
if (!hasMeasured) hasMeasured = true;
|
|
@@ -3705,7 +3778,6 @@ export function createSvGridController<
|
|
|
3705
3778
|
get computeSummaries() { return computeSummaries; },
|
|
3706
3779
|
get SUMMARY_DEFER_CELL_LIMIT() { return SUMMARY_DEFER_CELL_LIMIT; },
|
|
3707
3780
|
get summaryByColumn() { return summaryByColumn; },
|
|
3708
|
-
set summaryByColumn(v) { summaryByColumn = v as never; },
|
|
3709
3781
|
get hasMeasured() { return hasMeasured; },
|
|
3710
3782
|
set hasMeasured(v) { hasMeasured = v as never; },
|
|
3711
3783
|
get scrollBottomArmed() { return scrollBottomArmed; },
|
|
@@ -3859,6 +3931,10 @@ export function createSvGridController<
|
|
|
3859
3931
|
const { showTooltipFor, hideTooltip, flushScheduledScrollSync, scheduleScrollSync, onBodyScroll } = createScrollSync<TFeatures, TData>(ctx);
|
|
3860
3932
|
const { onGridKeyDown, onWindowKeydown, onHeaderSortClick } = createKeyboard<TFeatures, TData>(ctx);
|
|
3861
3933
|
const { computeSummaries, hasRenderedColumn } = createSummaries<TFeatures, TData>(ctx);
|
|
3934
|
+
// Hand the aggregator to `eagerSummaries` (declared above `ctx`, which this
|
|
3935
|
+
// needs). Plain assignment during init - no effect, so SSR gets it too.
|
|
3936
|
+
summarize = computeSummaries;
|
|
3937
|
+
|
|
3862
3938
|
const { updateFilterRow, updateFilterOperator, updateFilterMenuValue, updateFilterMenuValueTo, addFilterToken, removeFilterToken, toggleFilterToken, toggleCheckboxWithKeyboard, isColumnFiltered, closeMenus, openInSuggest, closeInSuggest, openChooseColumns, openColumnMenu, openFilterMenu, openOperatorMenu, sortColumnFromMenu, clearColumnSort, groupByColumnFromMenu, clearGroupingFromMenu, isFacetChecked, toggleFacetValue, setFacetSelection, setFilterTokens, isAllFacetsChecked, toggleAllFacets, clearColumnFilter, changePage, goToPage, setPageSize, openContextMenu, closeContextMenu, contextMenuItems, saveComment, removeComment, closeCommentEditor } = createMenus<TFeatures, TData>(ctx);
|
|
3863
3939
|
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellValidity, computeCellNote, getColumnEditorOptions, areEditorOptionsLoading, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender<TFeatures, TData>(ctx);
|
|
3864
3940
|
const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, startEditing, stopEditing, startFullRowEdit, setFullRowDraft, commitFullRowEdit, cancelFullRowEdit, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, commitAndMoveByTab, focusOnMount, onCellDoubleClick, pasteFromClipboard, onGridPaste } = createEditing<TFeatures, TData>(ctx);
|
package/src/SvGrid.css
CHANGED
|
@@ -2495,13 +2495,53 @@ select.sv-grid-fr-editor {
|
|
|
2495
2495
|
border-radius: 5px;
|
|
2496
2496
|
background: var(--sg-input-bg, #fff);
|
|
2497
2497
|
}
|
|
2498
|
-
|
|
2499
|
-
.
|
|
2498
|
+
/* The closed page-size trigger is plain markup until the real SvGridDropdown
|
|
2499
|
+
chunk loads on the first click (GridFooter.svelte keeps that component lazy).
|
|
2500
|
+
SvGridDropdown's own trigger rules are scoped to the component, so they never
|
|
2501
|
+
reach the placeholder - it needs the same box here, or the pager shows a
|
|
2502
|
+
stock <button> (UA border + padding, hugging the left) until the first click.
|
|
2503
|
+
Keep these in step with the .sv-grid-dropdown* rules in SvGridDropdown.svelte. */
|
|
2504
|
+
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown {
|
|
2505
|
+
position: absolute;
|
|
2506
|
+
inset: 0;
|
|
2507
|
+
height: 100%;
|
|
2500
2508
|
background: transparent;
|
|
2501
2509
|
border-radius: 5px;
|
|
2502
2510
|
font-size: 13px;
|
|
2503
2511
|
color: var(--sg-fg, #0f172a);
|
|
2504
2512
|
}
|
|
2513
|
+
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown-trigger {
|
|
2514
|
+
display: flex;
|
|
2515
|
+
align-items: center;
|
|
2516
|
+
gap: 6px;
|
|
2517
|
+
width: 100%;
|
|
2518
|
+
height: 100%;
|
|
2519
|
+
min-height: 28px;
|
|
2520
|
+
padding: 0 8px;
|
|
2521
|
+
border: 0;
|
|
2522
|
+
outline: none;
|
|
2523
|
+
border-radius: 5px;
|
|
2524
|
+
background: transparent;
|
|
2525
|
+
color: var(--sg-fg, #0f172a);
|
|
2526
|
+
font: inherit;
|
|
2527
|
+
font-size: 13px;
|
|
2528
|
+
text-align: start;
|
|
2529
|
+
cursor: pointer;
|
|
2530
|
+
box-sizing: border-box;
|
|
2531
|
+
}
|
|
2532
|
+
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown-label {
|
|
2533
|
+
flex: 1 1 auto;
|
|
2534
|
+
min-width: 0;
|
|
2535
|
+
overflow: hidden;
|
|
2536
|
+
text-overflow: ellipsis;
|
|
2537
|
+
white-space: nowrap;
|
|
2538
|
+
}
|
|
2539
|
+
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown-caret {
|
|
2540
|
+
flex: 0 0 auto;
|
|
2541
|
+
font-size: 10px;
|
|
2542
|
+
opacity: 0.7;
|
|
2543
|
+
line-height: 1;
|
|
2544
|
+
}
|
|
2505
2545
|
.sv-grid-pagination-pagesize-dd:focus-within {
|
|
2506
2546
|
border-color: var(--sg-accent, #2563eb);
|
|
2507
2547
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 30%, transparent);
|
package/src/SvGrid.types.ts
CHANGED
|
@@ -1020,6 +1020,7 @@ export type Props<TFeatures extends TableFeatures = TableFeatures, TData extends
|
|
|
1020
1020
|
* `sortable` - column sorting (injects `rowSortingFeature`)
|
|
1021
1021
|
* `filterable` - column filtering (injects `columnFilteringFeature`)
|
|
1022
1022
|
* `editable` - inline cell editing (alias of `enableInlineEditing`)
|
|
1023
|
+
* `selectable` - cell selection (alias of `enableCellSelection`)
|
|
1023
1024
|
* `groupable` - row grouping controls(alias of `showGroupingControls`,
|
|
1024
1025
|
* also injects `columnGroupingFeature`)
|
|
1025
1026
|
* `pageable` - pagination footer (alias of `showPagination`)
|
|
@@ -1030,6 +1031,26 @@ export type Props<TFeatures extends TableFeatures = TableFeatures, TData extends
|
|
|
1030
1031
|
sortable?: boolean;
|
|
1031
1032
|
filterable?: boolean;
|
|
1032
1033
|
editable?: boolean;
|
|
1034
|
+
/**
|
|
1035
|
+
* Cell selection - click a cell to select it, drag or shift-click to extend
|
|
1036
|
+
* the selection to a range. Alias of `enableCellSelection`, and the highest
|
|
1037
|
+
* priority of the three inputs that decide it:
|
|
1038
|
+
* `selectable` -> `enableCellSelection` -> `selectionMode`.
|
|
1039
|
+
*
|
|
1040
|
+
* NOTE the asymmetry with its siblings, which are all off until you opt in:
|
|
1041
|
+
* this one is **ON by default**, because `selectionMode` defaults to
|
|
1042
|
+
* `'both'`. It is named here so the capability is discoverable next to the
|
|
1043
|
+
* other shortcuts, and so `selectable={false}` is a one-word way to turn
|
|
1044
|
+
* cell selection off (the longhand being `selectionMode="none"`, which also
|
|
1045
|
+
* removes the row-selection surface).
|
|
1046
|
+
*
|
|
1047
|
+
* This is the CELL surface only. The selection **checkbox column** is the
|
|
1048
|
+
* other half of `selectionMode`, controlled by `showRowSelection` - also on
|
|
1049
|
+
* by default, and it needs no feature import (verified: the column renders
|
|
1050
|
+
* and toggles rows with an empty `features` set). Use `selectionMode` when
|
|
1051
|
+
* you want to move both surfaces at once.
|
|
1052
|
+
*/
|
|
1053
|
+
selectable?: boolean;
|
|
1033
1054
|
/**
|
|
1034
1055
|
* Client-side tree data: nest rows into a hierarchy by parent id.
|
|
1035
1056
|
*
|
package/src/headless.ts
CHANGED
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
* your own renderer, running the engine under SSR, or unit-testing row models
|
|
8
8
|
* without a browser.
|
|
9
9
|
*
|
|
10
|
+
* Two entry functions, and the difference matters outside a bundler:
|
|
11
|
+
* `createSvGrid` is the Svelte 5 wrapper - it holds its state in runes, so it
|
|
12
|
+
* needs the Svelte compiler in the pipeline (Vite, SvelteKit, vitest). Bare
|
|
13
|
+
* `node script.mjs` cannot run it. `createSvGridCore` is the same engine with
|
|
14
|
+
* no runes and no reactivity, so it runs anywhere Node runs: a service, a
|
|
15
|
+
* worker, a CLI, a plain unit test.
|
|
16
|
+
*
|
|
10
17
|
* Every runtime symbol here is also reachable from the main `@svgrid/grid`
|
|
11
18
|
* barrel, so the two paths never disagree. This entry additionally exposes the
|
|
12
19
|
* low-level types a custom renderer needs and the barrel does not re-export
|
|
@@ -703,3 +703,211 @@ describe('SvGrid - fitColumns', () => {
|
|
|
703
703
|
}
|
|
704
704
|
})
|
|
705
705
|
})
|
|
706
|
+
|
|
707
|
+
describe('SvGrid - selectable shortcut (cell selection)', () => {
|
|
708
|
+
// The gated path a user actually takes: pointerdown on a cell runs through
|
|
709
|
+
// createSelection.setSelection, which bails on !enableCellSelectionEffective.
|
|
710
|
+
// Asserting through it (rather than api.selectCells, which writes the range
|
|
711
|
+
// directly and bypasses the gate) is what makes these tests meaningful.
|
|
712
|
+
// Deliberately skips `.sv-grid-selection-cell`: the checkbox column is a
|
|
713
|
+
// different surface and a pointerdown there starts no range, so clicking it
|
|
714
|
+
// would make every assertion below pass vacuously.
|
|
715
|
+
async function clickCell(target: HTMLElement, index: number) {
|
|
716
|
+
const cells = target.querySelectorAll(
|
|
717
|
+
'.sv-grid-cell:not(.sv-grid-selection-cell)',
|
|
718
|
+
)
|
|
719
|
+
const cell = cells[index]
|
|
720
|
+
if (!cell) throw new Error(`no data cell at index ${index} (found ${cells.length})`)
|
|
721
|
+
cell.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true, button: 0 }))
|
|
722
|
+
await tick()
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
it('is ON by default - selectionMode defaults to "both"', async () => {
|
|
726
|
+
const { api, target, destroy } = await mountGrid()
|
|
727
|
+
try {
|
|
728
|
+
await tick()
|
|
729
|
+
await clickCell(target, 0)
|
|
730
|
+
expect(api.getSelected().length).toBe(1)
|
|
731
|
+
} finally {
|
|
732
|
+
destroy()
|
|
733
|
+
}
|
|
734
|
+
})
|
|
735
|
+
|
|
736
|
+
it('selectable={false} turns cell selection off', async () => {
|
|
737
|
+
const { api, target, destroy } = await mountGrid({ selectable: false })
|
|
738
|
+
try {
|
|
739
|
+
await tick()
|
|
740
|
+
await clickCell(target, 0)
|
|
741
|
+
expect(api.getSelected()).toEqual([])
|
|
742
|
+
} finally {
|
|
743
|
+
destroy()
|
|
744
|
+
}
|
|
745
|
+
})
|
|
746
|
+
|
|
747
|
+
it('selectable={true} wins over selectionMode="none"', async () => {
|
|
748
|
+
const { api, target, destroy } = await mountGrid({
|
|
749
|
+
selectable: true,
|
|
750
|
+
selectionMode: 'none',
|
|
751
|
+
})
|
|
752
|
+
try {
|
|
753
|
+
await tick()
|
|
754
|
+
await clickCell(target, 0)
|
|
755
|
+
expect(api.getSelected().length).toBe(1)
|
|
756
|
+
} finally {
|
|
757
|
+
destroy()
|
|
758
|
+
}
|
|
759
|
+
})
|
|
760
|
+
|
|
761
|
+
it('selectable={false} wins over enableCellSelection={true}', async () => {
|
|
762
|
+
const { api, target, destroy } = await mountGrid({
|
|
763
|
+
selectable: false,
|
|
764
|
+
enableCellSelection: true,
|
|
765
|
+
})
|
|
766
|
+
try {
|
|
767
|
+
await tick()
|
|
768
|
+
await clickCell(target, 0)
|
|
769
|
+
expect(api.getSelected()).toEqual([])
|
|
770
|
+
} finally {
|
|
771
|
+
destroy()
|
|
772
|
+
}
|
|
773
|
+
})
|
|
774
|
+
|
|
775
|
+
it('falls back to enableCellSelection when selectable is omitted', async () => {
|
|
776
|
+
const { api, target, destroy } = await mountGrid({ enableCellSelection: false })
|
|
777
|
+
try {
|
|
778
|
+
await tick()
|
|
779
|
+
await clickCell(target, 0)
|
|
780
|
+
expect(api.getSelected()).toEqual([])
|
|
781
|
+
} finally {
|
|
782
|
+
destroy()
|
|
783
|
+
}
|
|
784
|
+
})
|
|
785
|
+
|
|
786
|
+
it('api.setOption("selectable", false) turns it off at runtime', async () => {
|
|
787
|
+
const { api, target, destroy } = await mountGrid()
|
|
788
|
+
try {
|
|
789
|
+
await tick()
|
|
790
|
+
await clickCell(target, 0)
|
|
791
|
+
expect(api.getSelected().length).toBe(1)
|
|
792
|
+
|
|
793
|
+
api.setOption('selectable', false)
|
|
794
|
+
await tick()
|
|
795
|
+
api.selectCells([])
|
|
796
|
+
await tick()
|
|
797
|
+
await clickCell(target, 0)
|
|
798
|
+
expect(api.getSelected()).toEqual([])
|
|
799
|
+
} finally {
|
|
800
|
+
destroy()
|
|
801
|
+
}
|
|
802
|
+
})
|
|
803
|
+
})
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Guards the promise the homepage "A real grid in ~15 lines" section makes:
|
|
807
|
+
* the snippet shown there is shortcuts-only - no `features` import, no
|
|
808
|
+
* `editorType` - so every capability it advertises must work from exactly
|
|
809
|
+
* those props. The section drifted from the component once already (it showed
|
|
810
|
+
* a bare grid beside a preview captioned "sort, filter, select, edit ... are
|
|
811
|
+
* all live"), which is what this test exists to catch.
|
|
812
|
+
*/
|
|
813
|
+
describe('SvGrid - homepage snippet (shortcuts only, no features import)', () => {
|
|
814
|
+
const snippetRows = [
|
|
815
|
+
{ firstName: 'Ada', age: 36, status: 'active' },
|
|
816
|
+
{ firstName: 'Linus', age: 54, status: 'active' },
|
|
817
|
+
{ firstName: 'Grace', age: 85, status: 'inactive' },
|
|
818
|
+
]
|
|
819
|
+
const snippetColumns = [
|
|
820
|
+
{ field: 'firstName', header: 'First name' },
|
|
821
|
+
{ field: 'age', header: 'Age' },
|
|
822
|
+
{ field: 'status', header: 'Status' },
|
|
823
|
+
] as unknown as typeof personColumns
|
|
824
|
+
|
|
825
|
+
function mountSnippet(): Promise<MountResult> {
|
|
826
|
+
return new Promise((resolveApi, rejectApi) => {
|
|
827
|
+
const target = document.createElement('div')
|
|
828
|
+
document.body.appendChild(target)
|
|
829
|
+
const app = mount(SvGrid, {
|
|
830
|
+
target,
|
|
831
|
+
props: {
|
|
832
|
+
data: snippetRows,
|
|
833
|
+
columns: snippetColumns,
|
|
834
|
+
// The four shortcuts the snippet passes - and nothing else.
|
|
835
|
+
sortable: true,
|
|
836
|
+
filterable: true,
|
|
837
|
+
editable: true,
|
|
838
|
+
selectable: true,
|
|
839
|
+
getRowId: (r: { firstName: string }) => r.firstName,
|
|
840
|
+
rowHeight: 36,
|
|
841
|
+
containerHeight: 300,
|
|
842
|
+
virtualization: false,
|
|
843
|
+
onApiReady(api: SvGridApi<any, any>) {
|
|
844
|
+
resolveApi({
|
|
845
|
+
api,
|
|
846
|
+
target,
|
|
847
|
+
destroy: () => {
|
|
848
|
+
unmount(app)
|
|
849
|
+
target.remove()
|
|
850
|
+
},
|
|
851
|
+
} as unknown as MountResult)
|
|
852
|
+
},
|
|
853
|
+
} as never,
|
|
854
|
+
})
|
|
855
|
+
if (!app) rejectApi(new Error('mount failed'))
|
|
856
|
+
})
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
it('sortable works without importing rowSortingFeature', async () => {
|
|
860
|
+
const { api, destroy } = await mountSnippet()
|
|
861
|
+
try {
|
|
862
|
+
await tick()
|
|
863
|
+
api.setSort('age', 'asc')
|
|
864
|
+
await tick()
|
|
865
|
+
expect(api.getState().sorting).toEqual([{ id: 'age', desc: false }])
|
|
866
|
+
} finally {
|
|
867
|
+
destroy()
|
|
868
|
+
}
|
|
869
|
+
})
|
|
870
|
+
|
|
871
|
+
it('editable opens an editor on a column with no editorType', async () => {
|
|
872
|
+
const { api, target, destroy } = await mountSnippet()
|
|
873
|
+
try {
|
|
874
|
+
await tick()
|
|
875
|
+
api.startEditing(0, 'firstName')
|
|
876
|
+
await tick()
|
|
877
|
+
expect(target.querySelectorAll('.sv-grid-cell-editing').length).toBe(1)
|
|
878
|
+
} finally {
|
|
879
|
+
destroy()
|
|
880
|
+
}
|
|
881
|
+
})
|
|
882
|
+
|
|
883
|
+
it('selectable gives cell selection', async () => {
|
|
884
|
+
const { api, target, destroy } = await mountSnippet()
|
|
885
|
+
try {
|
|
886
|
+
await tick()
|
|
887
|
+
const cell = target.querySelector('.sv-grid-cell:not(.sv-grid-selection-cell)')
|
|
888
|
+
cell?.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true, button: 0 }))
|
|
889
|
+
await tick()
|
|
890
|
+
expect(api.getSelected().length).toBe(1)
|
|
891
|
+
} finally {
|
|
892
|
+
destroy()
|
|
893
|
+
}
|
|
894
|
+
})
|
|
895
|
+
|
|
896
|
+
it('the checkbox column renders and toggles a row - no feature import needed', async () => {
|
|
897
|
+
const { api, target, destroy } = await mountSnippet()
|
|
898
|
+
try {
|
|
899
|
+
await tick()
|
|
900
|
+
// [0] is the header select-all; [1] is the first row.
|
|
901
|
+
const boxes = target.querySelectorAll<HTMLElement>('.sv-grid-checkbox')
|
|
902
|
+
expect(boxes.length).toBeGreaterThan(1)
|
|
903
|
+
boxes[1]?.click()
|
|
904
|
+
await tick()
|
|
905
|
+
const names = (api.getSelectedRows() as unknown as { firstName: string }[]).map(
|
|
906
|
+
(r) => r.firstName,
|
|
907
|
+
)
|
|
908
|
+
expect(names).toEqual(['Ada'])
|
|
909
|
+
} finally {
|
|
910
|
+
destroy()
|
|
911
|
+
}
|
|
912
|
+
})
|
|
913
|
+
})
|
package/src/themes/index.ts
CHANGED
|
@@ -162,8 +162,10 @@ export const themePresets: ThemePreset[] = [
|
|
|
162
162
|
dark: { bg: '#1e1e2e', fg: '#cdd6f4', muted: '#a6adc8', border: '#313244', headerBg: '#181825', headerFg: '#cdd6f4', accent: '#cba6f7', rowAlt: '#1a1a28', rowHover: '#313244', selectionBg: '#45475a' } },
|
|
163
163
|
]
|
|
164
164
|
|
|
165
|
-
/** The default preset (used when nothing is chosen)
|
|
166
|
-
|
|
165
|
+
/** The default preset (used when nothing is chosen): Ember, the house theme the
|
|
166
|
+
* demo gallery and svgrid.com open on, so a scaffolded or Studio-generated app
|
|
167
|
+
* looks like the demos unless a preset is picked. */
|
|
168
|
+
export const defaultThemePreset: ThemePreset = themePresets.find((t) => t.id === 'ember') ?? themePresets[0]!
|
|
167
169
|
|
|
168
170
|
/** Look up a preset by id. */
|
|
169
171
|
export const getThemePreset = (id?: string): ThemePreset | undefined =>
|