@svgrid/grid 2.2.31 → 2.2.32
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/README.md +1 -1
- package/dist/SvAvatar.svelte +1 -1
- package/dist/SvCheckBox.svelte +2 -1
- package/dist/SvCircularProgress.svelte +1 -1
- package/dist/SvGrid.controller.svelte.d.ts +20 -0
- package/dist/SvGrid.controller.svelte.js +358 -18
- package/dist/SvGrid.css +103 -3
- package/dist/SvGrid.svelte +162 -10
- package/dist/SvGrid.types.d.ts +81 -0
- package/dist/SvGridDropdown.svelte +38 -6
- package/dist/SvGridDropdown.svelte.d.ts +4 -0
- package/dist/SvGridSelect.svelte +12 -3
- package/dist/SvGridSelect.svelte.d.ts +6 -0
- package/dist/SvGroupCell.svelte +9 -2
- package/dist/SvMultiSelect.svelte +12 -3
- package/dist/SvMultiSelect.svelte.d.ts +6 -0
- package/dist/SvPasswordInput.svelte +1 -1
- package/dist/SvProgress.svelte +1 -1
- package/dist/SvRating.svelte +1 -1
- package/dist/SvResult.svelte +1 -1
- package/dist/SvRichText.svelte +31 -16
- package/dist/SvRichText.svelte.d.ts +9 -0
- package/dist/SvSwitchButton.svelte +2 -1
- package/dist/SvToaster.svelte +1 -1
- package/dist/SvTree.svelte +9 -2
- package/dist/SvTree.svelte.d.ts +5 -0
- package/dist/SvTreeSelect.svelte +6 -2
- package/dist/build-api.js +16 -0
- package/dist/cdn/{GridMenus-Ds3OpzZT.js → GridMenus-C6ixSGWW.js} +33 -33
- package/dist/cdn/{GridMenus-Da_p7SKS.js → GridMenus-Cyk3qpJ7.js} +33 -33
- package/dist/cdn/{SvDateTimePicker-B8GopjhC.js → SvDateTimePicker-CdqaP61H.js} +2 -1
- package/dist/cdn/{SvDateTimePicker-DB0aIEk8.js → SvDateTimePicker-ClqtVIj3.js} +2 -1
- package/dist/cdn/{SvGridDropdown-DjALc_4f.js → SvGridDropdown-D0VdjeR8.js} +129 -123
- package/dist/cdn/{SvGridDropdown-hpnHvOGt.js → SvGridDropdown-Dh-2ej8j.js} +92 -87
- package/dist/cdn/{src-BLJfdyL0.js → src-BgHjWF0Q.js} +4193 -3837
- package/dist/cdn/{src-DYDCiC0D.js → src-CgD-wiuS.js} +5956 -5602
- package/dist/cdn/svgrid.js +8 -8
- package/dist/cdn/svgrid.svelte-external.js +8 -8
- package/dist/cell-render.d.ts +1 -0
- package/dist/cell-render.js +115 -1
- package/dist/cell-values.js +5 -0
- package/dist/core.d.ts +80 -9
- package/dist/core.js +89 -0
- package/dist/editor-contract.d.ts +12 -5
- package/dist/editor-registry.d.ts +18 -7
- package/dist/editor-registry.js +10 -1
- package/dist/group-display.d.ts +39 -0
- package/dist/group-display.js +52 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/keyboard-handlers.js +16 -0
- package/dist/recurrence.js +3 -2
- package/dist/svgrid-wrapper.types.d.ts +8 -2
- package/dist/themes/index.d.ts +10 -0
- package/dist/themes/index.js +26 -0
- package/package.json +1 -1
- package/src/SvAccordion.test.ts +5 -5
- package/src/SvAvatar.svelte +1 -1
- package/src/SvButtonGroup.test.ts +6 -6
- package/src/SvCheckBox.svelte +2 -1
- package/src/SvCircularProgress.svelte +1 -1
- package/src/SvFileUpload.test.ts +1 -1
- package/src/SvGrid.controller.svelte.ts +367 -17
- package/src/SvGrid.css +103 -3
- package/src/SvGrid.svelte +162 -10
- package/src/SvGrid.types.ts +81 -0
- package/src/SvGridDropdown.svelte +38 -6
- package/src/SvGridSelect.svelte +12 -3
- package/src/SvGroupCell.svelte +9 -2
- package/src/SvMultiSelect.svelte +12 -3
- package/src/SvPasswordInput.svelte +1 -1
- package/src/SvProgress.svelte +1 -1
- package/src/SvRating.svelte +1 -1
- package/src/SvResult.svelte +1 -1
- package/src/SvRichText.svelte +31 -16
- package/src/SvSwitchButton.svelte +2 -1
- package/src/SvToaster.svelte +1 -1
- package/src/SvTree.svelte +9 -2
- package/src/SvTreeSelect.svelte +6 -2
- package/src/build-api.ts +15 -0
- package/src/builtin-editors.test.ts +3 -1
- package/src/cell-render.test.ts +27 -3
- package/src/cell-render.ts +122 -1
- package/src/cell-values.ts +4 -0
- package/src/chart-export.test.ts +2 -2
- package/src/clipboard.test.ts +0 -2
- package/src/core.ts +157 -8
- package/src/editing.test.ts +1 -1
- package/src/editor-contract.ts +12 -5
- package/src/editor-registry.grid.test.ts +48 -0
- package/src/editor-registry.test.ts +3 -1
- package/src/editor-registry.ts +23 -7
- package/src/facet-buckets.test.ts +33 -33
- package/src/group-display.test.ts +91 -1
- package/src/group-display.ts +83 -0
- package/src/index.ts +9 -0
- package/src/keyboard-handlers.ts +17 -0
- package/src/list-power.test.ts +2 -2
- package/src/menus.test.ts +4 -4
- package/src/named-views.coverage.test.ts +3 -3
- package/src/recurrence.ts +3 -2
- package/src/row-resize.test.ts +40 -40
- package/src/scheduler-model.ts +1 -1
- package/src/server-data-source.coverage.test.ts +1 -1
- package/src/server-group-model.test.ts +6 -6
- package/src/spreadsheet.test.ts +0 -1
- package/src/svgrid-wrapper.types.ts +11 -9
- package/src/svgrid.async-editor-options.test.ts +271 -0
- package/src/svgrid.auto-row-height.test.ts +204 -0
- package/src/svgrid.charting.test.ts +4 -4
- package/src/svgrid.comments-autocomplete.test.ts +8 -2
- package/src/svgrid.context-menu.test.ts +25 -9
- package/src/svgrid.filter-depth.test.ts +163 -0
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +16 -2
- package/src/svgrid.filter-menu-scroll.test.ts +10 -2
- package/src/svgrid.grand-total.test.ts +188 -0
- package/src/svgrid.group-display-mode.test.ts +171 -0
- package/src/svgrid.group-footers.test.ts +121 -0
- package/src/svgrid.group-pagination.test.ts +153 -0
- package/src/svgrid.tree-data.test.ts +186 -0
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -0
- package/src/themes/index.ts +42 -0
- package/src/tree-edit.test.ts +4 -4
- package/src/tree-row-model.test.ts +169 -0
- package/src/ui-localization.test.ts +113 -0
- package/themes/ag-alpine.css +22 -0
- package/themes/antd.css +22 -0
- package/themes/atlassian.css +22 -0
- package/themes/bootstrap.css +22 -0
- package/themes/carbon.css +22 -0
- package/themes/catppuccin.css +22 -0
- package/themes/dracula.css +22 -0
- package/themes/ember.css +22 -0
- package/themes/excel.css +22 -0
- package/themes/fluent.css +22 -0
- package/themes/github.css +22 -0
- package/themes/linear.css +22 -0
- package/themes/material.css +22 -0
- package/themes/nord.css +22 -0
- package/themes/notion.css +22 -0
- package/themes/salesforce.css +22 -0
- package/themes/sap.css +22 -0
- package/themes/shadcn.css +22 -0
- package/themes/tailwind.css +22 -0
- package/themes/vercel.css +22 -0
package/dist/core.d.ts
CHANGED
|
@@ -48,6 +48,12 @@ export type CellSpanParams<TData extends RowData = RowData> = {
|
|
|
48
48
|
/** The cell's base value for this column. */
|
|
49
49
|
value: unknown;
|
|
50
50
|
};
|
|
51
|
+
/** The raw option list a column's `editorOptions` can supply. */
|
|
52
|
+
export type EditorOptionSource = ReadonlyArray<string | number | {
|
|
53
|
+
value: string | number;
|
|
54
|
+
label?: string;
|
|
55
|
+
color?: string;
|
|
56
|
+
}>;
|
|
51
57
|
/** Params passed to a column's `valueParser(...)` on edit commit. */
|
|
52
58
|
export type ValueParserParams<TData extends RowData = RowData> = {
|
|
53
59
|
/** The value after built-in per-`editorType` coercion. */
|
|
@@ -126,6 +132,16 @@ export type ColumnDefTemplate<TContext> = string | ((context: TContext) => unkno
|
|
|
126
132
|
export type GroupAggregator<TData = any> = 'sum' | 'avg' | 'min' | 'max' | 'count' | 'countDistinct' | 'extent' | 'first' | ((values: number[], rows: Array<TData>) => unknown);
|
|
127
133
|
/** Apply a group aggregator over a bucket's leaf rows for one column. */
|
|
128
134
|
export declare function applyGroupAggregate<TData extends RowData>(agg: GroupAggregator<TData>, columnId: string, rows: ReadonlyArray<Row<TData>>): unknown;
|
|
135
|
+
/**
|
|
136
|
+
* A column definition.
|
|
137
|
+
*
|
|
138
|
+
* `TFeatures` is a phantom parameter - it is threaded through nested
|
|
139
|
+
* `columns` groups but no member depends on it, so `{}`, `TableFeatures` and
|
|
140
|
+
* `typeof features` are all interchangeable here. It is deliberately left
|
|
141
|
+
* WITHOUT a default: `ColumnDef<Row>` would otherwise bind `Row` to this slot
|
|
142
|
+
* and silently type your data as `RowData`, losing every field-name check.
|
|
143
|
+
* Prefer {@link GridColumns} / {@link GridColumnDef} for the common case.
|
|
144
|
+
*/
|
|
129
145
|
export type ColumnDef<TFeatures extends TableFeatures, TData extends RowData> = {
|
|
130
146
|
id?: string;
|
|
131
147
|
field?: keyof TData & string;
|
|
@@ -270,16 +286,17 @@ export type ColumnDef<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
270
286
|
*
|
|
271
287
|
* Pass a function `(row) => options` for row-dependent (cascading)
|
|
272
288
|
* options - e.g. City options that depend on Country in the same row.
|
|
289
|
+
*
|
|
290
|
+
* Either form may return a **Promise**, for options that come from the
|
|
291
|
+
* server. While it resolves, the editor shows a loading state and the cell
|
|
292
|
+
* renders its raw value.
|
|
293
|
+
*
|
|
294
|
+
* Results are cached so reopening an editor does not refetch: a static source
|
|
295
|
+
* per column, a per-row source per row AND per that row's data - so a cascade
|
|
296
|
+
* reloads by itself when the cell it depends on is edited. Call
|
|
297
|
+
* `api.refreshEditorOptions(columnId?)` when the list changes server-side.
|
|
273
298
|
*/
|
|
274
|
-
editorOptions?:
|
|
275
|
-
value: string | number;
|
|
276
|
-
label?: string;
|
|
277
|
-
color?: string;
|
|
278
|
-
}> | ((row: TData) => ReadonlyArray<string | number | {
|
|
279
|
-
value: string | number;
|
|
280
|
-
label?: string;
|
|
281
|
-
color?: string;
|
|
282
|
-
}>);
|
|
299
|
+
editorOptions?: EditorOptionSource | Promise<EditorOptionSource> | ((row: TData) => EditorOptionSource | Promise<EditorOptionSource>);
|
|
283
300
|
/** When true, list/chips allow multiple selections. Cell value becomes an array. */
|
|
284
301
|
editorMultiple?: boolean;
|
|
285
302
|
/** Separator used when joining array values for the readonly cell display. Defaults to ', '. */
|
|
@@ -339,6 +356,21 @@ export type ColumnDef<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
339
356
|
*/
|
|
340
357
|
cellClass?: string | ReadonlyArray<string> | ((ctx: CellContext<TData>) => string | ReadonlyArray<string> | Record<string, boolean> | undefined | null);
|
|
341
358
|
};
|
|
359
|
+
/**
|
|
360
|
+
* A column definition keyed only by your row type - the ergonomic form of
|
|
361
|
+
* {@link ColumnDef}, whose first parameter is a phantom feature bag that is
|
|
362
|
+
* almost always `{}`.
|
|
363
|
+
*
|
|
364
|
+
* ```ts
|
|
365
|
+
* const columns: GridColumns<Person> = [{ field: 'firstName', header: 'Name' }]
|
|
366
|
+
* ```
|
|
367
|
+
*
|
|
368
|
+
* Interchangeable with `ColumnDef<{}, TData>` and `ColumnDef<typeof features,
|
|
369
|
+
* TData>` in both directions, so it mixes freely with existing code.
|
|
370
|
+
*/
|
|
371
|
+
export type GridColumnDef<TData extends RowData = RowData> = ColumnDef<TableFeatures, TData>;
|
|
372
|
+
/** An array of {@link GridColumnDef} - what you pass to `<SvGrid columns={...}>`. */
|
|
373
|
+
export type GridColumns<TData extends RowData = RowData> = Array<GridColumnDef<TData>>;
|
|
342
374
|
export type Column<TData extends RowData> = {
|
|
343
375
|
id: string;
|
|
344
376
|
columnDef: ColumnDef<any, TData>;
|
|
@@ -427,6 +459,45 @@ export declare function createCoreRowModel<TData extends RowData>(): RowModelFac
|
|
|
427
459
|
export declare function createFilteredRowModel<TData extends RowData>(): RowModelFactory<TData>;
|
|
428
460
|
export declare function createPaginatedRowModel<TData extends RowData>(): RowModelFactory<TData>;
|
|
429
461
|
export declare function createGroupedRowModel<TData extends RowData>(): RowModelFactory<TData>;
|
|
462
|
+
export type TreeRowModelOptions = {
|
|
463
|
+
/** Field holding each row's parent id. Rows with no parent are roots. */
|
|
464
|
+
parentField: string;
|
|
465
|
+
/** Field holding the row's own id. Defaults to `'id'`. */
|
|
466
|
+
idField?: string;
|
|
467
|
+
};
|
|
468
|
+
/**
|
|
469
|
+
* Client-side tree data: nest the grid's own flat rows into a parent/child
|
|
470
|
+
* hierarchy that `createExpandedRowModel` then walks.
|
|
471
|
+
*
|
|
472
|
+
* This works on the rows the grid already built rather than on raw data, so
|
|
473
|
+
* tree rows keep their cells, editing, selection and formatting - they are real
|
|
474
|
+
* data rows that happen to have children, not synthetic banners like grouping's.
|
|
475
|
+
* That is also why the model is parent-id based: nested source arrays never
|
|
476
|
+
* become rows (the grid only builds rows for `data`), so nested input is
|
|
477
|
+
* flattened first with {@link flattenTreeData}. One code path, no duplicated
|
|
478
|
+
* row construction.
|
|
479
|
+
*
|
|
480
|
+
* Rows are tagged `__treeRow` so `isGroupRow` does not mistake an expandable
|
|
481
|
+
* data row for a full-width group banner.
|
|
482
|
+
*/
|
|
483
|
+
export declare function createTreeRowModel<TData extends RowData>(options: TreeRowModelOptions): RowModelFactory<TData>;
|
|
484
|
+
export type FlattenTreeOptions = {
|
|
485
|
+
/** Field holding an array of child objects. */
|
|
486
|
+
childrenField: string;
|
|
487
|
+
/** Field holding each object's id. Defaults to `'id'`. */
|
|
488
|
+
idField?: string;
|
|
489
|
+
/** Field to WRITE the resolved parent id onto. Defaults to `'__parentId'`. */
|
|
490
|
+
parentField?: string;
|
|
491
|
+
};
|
|
492
|
+
/**
|
|
493
|
+
* Flatten nested tree data into the flat parent-id shape `createTreeRowModel`
|
|
494
|
+
* consumes, stamping each child with its parent's id.
|
|
495
|
+
*
|
|
496
|
+
* Children are emitted directly after their parent so the natural order already
|
|
497
|
+
* matches the rendered tree. The `childrenField` array is left on the objects
|
|
498
|
+
* (harmless, and callers often still want it); only the parent link is added.
|
|
499
|
+
*/
|
|
500
|
+
export declare function flattenTreeData<T extends RowData>(data: ReadonlyArray<T>, options: FlattenTreeOptions): T[];
|
|
430
501
|
export declare function createExpandedRowModel<TData extends RowData>(): RowModelFactory<TData>;
|
|
431
502
|
export declare function createSortedRowModel<TData extends RowData>(localSortFns?: typeof sortFns): RowModelFactory<TData>;
|
|
432
503
|
export type SvGridOptions<TFeatures extends TableFeatures, TData extends RowData> = {
|
package/dist/core.js
CHANGED
|
@@ -193,6 +193,95 @@ export function createGroupedRowModel() {
|
|
|
193
193
|
return buildGroups(rows, 0, 0, 'group');
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Client-side tree data: nest the grid's own flat rows into a parent/child
|
|
198
|
+
* hierarchy that `createExpandedRowModel` then walks.
|
|
199
|
+
*
|
|
200
|
+
* This works on the rows the grid already built rather than on raw data, so
|
|
201
|
+
* tree rows keep their cells, editing, selection and formatting - they are real
|
|
202
|
+
* data rows that happen to have children, not synthetic banners like grouping's.
|
|
203
|
+
* That is also why the model is parent-id based: nested source arrays never
|
|
204
|
+
* become rows (the grid only builds rows for `data`), so nested input is
|
|
205
|
+
* flattened first with {@link flattenTreeData}. One code path, no duplicated
|
|
206
|
+
* row construction.
|
|
207
|
+
*
|
|
208
|
+
* Rows are tagged `__treeRow` so `isGroupRow` does not mistake an expandable
|
|
209
|
+
* data row for a full-width group banner.
|
|
210
|
+
*/
|
|
211
|
+
export function createTreeRowModel(options) {
|
|
212
|
+
const { parentField, idField = 'id' } = options;
|
|
213
|
+
return ({ table, rows }) => {
|
|
214
|
+
if (!rows.length)
|
|
215
|
+
return rows;
|
|
216
|
+
const keyOf = (row) => row.original?.[idField];
|
|
217
|
+
const parentOf = (row) => row.original?.[parentField];
|
|
218
|
+
const present = new Set();
|
|
219
|
+
for (const row of rows)
|
|
220
|
+
present.add(keyOf(row));
|
|
221
|
+
const childrenByParent = new Map();
|
|
222
|
+
const roots = [];
|
|
223
|
+
for (const row of rows) {
|
|
224
|
+
const parent = parentOf(row);
|
|
225
|
+
// A row whose parent is absent (filtered out, or never existed) becomes a
|
|
226
|
+
// root rather than disappearing - silently dropping rows is worse than a
|
|
227
|
+
// shallower tree. Self-parenting is treated the same way.
|
|
228
|
+
if (parent == null || parent === keyOf(row) || !present.has(parent)) {
|
|
229
|
+
roots.push(row);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const list = childrenByParent.get(parent) ?? [];
|
|
233
|
+
list.push(row);
|
|
234
|
+
childrenByParent.set(parent, list);
|
|
235
|
+
}
|
|
236
|
+
// Guards a cycle in the parent chain from recursing forever.
|
|
237
|
+
const seen = new Set();
|
|
238
|
+
const build = (row, depth) => {
|
|
239
|
+
const key = keyOf(row);
|
|
240
|
+
const id = row.id;
|
|
241
|
+
if (seen.has(key)) {
|
|
242
|
+
return { ...row, depth, subRows: [], getCanExpand: () => false };
|
|
243
|
+
}
|
|
244
|
+
seen.add(key);
|
|
245
|
+
const subRows = (childrenByParent.get(key) ?? []).map((child) => build(child, depth + 1));
|
|
246
|
+
return {
|
|
247
|
+
...row,
|
|
248
|
+
depth,
|
|
249
|
+
subRows,
|
|
250
|
+
leafCount: subRows.reduce((n, sub) => n + 1 + (sub.leafCount ?? 0), 0),
|
|
251
|
+
__treeRow: true,
|
|
252
|
+
getCanExpand: () => subRows.length > 0,
|
|
253
|
+
getIsExpanded: () => Boolean((table.getState().expanded ?? {})[id]),
|
|
254
|
+
toggleExpanded: () => {
|
|
255
|
+
table.setExpanded((prev) => ({ ...prev, [id]: !prev[id] }));
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
return roots.map((root) => build(root, 0));
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Flatten nested tree data into the flat parent-id shape `createTreeRowModel`
|
|
264
|
+
* consumes, stamping each child with its parent's id.
|
|
265
|
+
*
|
|
266
|
+
* Children are emitted directly after their parent so the natural order already
|
|
267
|
+
* matches the rendered tree. The `childrenField` array is left on the objects
|
|
268
|
+
* (harmless, and callers often still want it); only the parent link is added.
|
|
269
|
+
*/
|
|
270
|
+
export function flattenTreeData(data, options) {
|
|
271
|
+
const { childrenField, idField = 'id', parentField = '__parentId' } = options;
|
|
272
|
+
const out = [];
|
|
273
|
+
const walk = (nodes, parentId) => {
|
|
274
|
+
for (const node of nodes) {
|
|
275
|
+
const flat = { ...node, [parentField]: parentId };
|
|
276
|
+
out.push(flat);
|
|
277
|
+
const kids = node[childrenField];
|
|
278
|
+
if (Array.isArray(kids) && kids.length)
|
|
279
|
+
walk(kids, node[idField]);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
walk(data, null);
|
|
283
|
+
return out;
|
|
284
|
+
}
|
|
196
285
|
export function createExpandedRowModel() {
|
|
197
286
|
return ({ table, rows }) => {
|
|
198
287
|
const expanded = table.getState().expanded ?? {};
|
|
@@ -63,16 +63,23 @@ export type SvEditorProps = {
|
|
|
63
63
|
* The interaction surface an editor exposes when embedded as a grid cell editor.
|
|
64
64
|
* Standalone usage ignores these; the grid supplies them so every editor shares
|
|
65
65
|
* one commit / cancel / move contract (Enter commits, Escape cancels, Tab moves)
|
|
66
|
-
* instead of each cell-editor re-implementing key handling.
|
|
67
|
-
*
|
|
66
|
+
* instead of each cell-editor re-implementing key handling.
|
|
67
|
+
*
|
|
68
|
+
* `CellEditorContext` (editor-registry.ts) extends this type, so what the grid
|
|
69
|
+
* hands a registered editor and what this contract promises are the same thing
|
|
70
|
+
* by construction rather than by convention.
|
|
68
71
|
*/
|
|
69
72
|
export type EditorInteraction<V = unknown> = {
|
|
70
|
-
/**
|
|
71
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Commit the value and stop editing (Enter, option pick, blur-commit).
|
|
75
|
+
* The argument is optional: an editor that has been reporting through
|
|
76
|
+
* `onChange` can just call `onCommit()` to commit what the grid already has.
|
|
77
|
+
*/
|
|
78
|
+
onCommit?: (value?: V) => void;
|
|
72
79
|
/** Abandon the edit and restore the previous value (Escape). */
|
|
73
80
|
onCancel?: () => void;
|
|
74
81
|
/** Commit, then move to the adjacent cell (Tab = 1, Shift+Tab = -1). */
|
|
75
|
-
onCommitAndMove?: (value: V, direction: 1 | -1) => void;
|
|
82
|
+
onCommitAndMove?: (value: V | undefined, direction: 1 | -1) => void;
|
|
76
83
|
/** Ask the surrounding popover/panel to close without committing. */
|
|
77
84
|
onRequestClose?: () => void;
|
|
78
85
|
/** True while mounted inside a grid cell (lets an editor tune autofocus etc.). */
|
|
@@ -20,8 +20,17 @@
|
|
|
20
20
|
* mounting.
|
|
21
21
|
*/
|
|
22
22
|
import type { Component } from 'svelte';
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
import type { EditorInteraction } from './editor-contract';
|
|
24
|
+
/**
|
|
25
|
+
* Context the grid hands a cell editor when it mounts one for an edit.
|
|
26
|
+
*
|
|
27
|
+
* Extends {@link EditorInteraction} - the shared commit / cancel / move contract
|
|
28
|
+
* the `Sv*` editors are written against - and narrows the parts the grid always
|
|
29
|
+
* supplies, so a registered component and a built-in editor speak the same
|
|
30
|
+
* language. `onCommitAndMove`, `onRequestClose` and `inCell` come from the
|
|
31
|
+
* contract and are always populated by the grid.
|
|
32
|
+
*/
|
|
33
|
+
export type CellEditorContext<T = unknown> = Required<Pick<EditorInteraction<T>, 'onCommit' | 'onCancel' | 'onCommitAndMove' | 'onRequestClose' | 'inCell'>> & {
|
|
25
34
|
/** The value currently being edited. */
|
|
26
35
|
value: T;
|
|
27
36
|
/** Id of the row being edited. */
|
|
@@ -30,10 +39,6 @@ export type CellEditorContext<T = unknown> = {
|
|
|
30
39
|
columnId: string;
|
|
31
40
|
/** Update the in-progress value WITHOUT ending the edit. */
|
|
32
41
|
onChange: (value: T) => void;
|
|
33
|
-
/** Commit the value and stop editing (optionally pass a final value). */
|
|
34
|
-
onCommit: (value?: T) => void;
|
|
35
|
-
/** Discard changes and stop editing (returns focus to the grid). */
|
|
36
|
-
onCancel: () => void;
|
|
37
42
|
};
|
|
38
43
|
/** How a registered component is mounted and wired for a cell edit. */
|
|
39
44
|
export type CellEditorRegistration = {
|
|
@@ -61,7 +66,13 @@ export declare function hasCellEditor(type: string): boolean;
|
|
|
61
66
|
export declare function unregisterCellEditor(type: string): void;
|
|
62
67
|
/** All registered custom editor type names. */
|
|
63
68
|
export declare function registeredCellEditorTypes(): string[];
|
|
64
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* The default context → props mapping used when a registration has no `props`.
|
|
71
|
+
* Passes the whole {@link EditorInteraction} surface, so an editor written
|
|
72
|
+
* against the shared contract works when registered with no mapping at all.
|
|
73
|
+
* Svelte ignores props a component doesn't declare, so the extra keys are inert
|
|
74
|
+
* for a simple editor that only wants `value` + `onCommit`.
|
|
75
|
+
*/
|
|
65
76
|
export declare function defaultEditorProps(ctx: CellEditorContext): Record<string, unknown>;
|
|
66
77
|
/** Resolve the props to spread onto a registered editor for a given context. */
|
|
67
78
|
export declare function resolveEditorProps(registration: CellEditorRegistration, ctx: CellEditorContext): Record<string, unknown>;
|
package/dist/editor-registry.js
CHANGED
|
@@ -23,13 +23,22 @@ export function unregisterCellEditor(type) {
|
|
|
23
23
|
export function registeredCellEditorTypes() {
|
|
24
24
|
return [...registry.keys()];
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The default context → props mapping used when a registration has no `props`.
|
|
28
|
+
* Passes the whole {@link EditorInteraction} surface, so an editor written
|
|
29
|
+
* against the shared contract works when registered with no mapping at all.
|
|
30
|
+
* Svelte ignores props a component doesn't declare, so the extra keys are inert
|
|
31
|
+
* for a simple editor that only wants `value` + `onCommit`.
|
|
32
|
+
*/
|
|
27
33
|
export function defaultEditorProps(ctx) {
|
|
28
34
|
return {
|
|
29
35
|
value: ctx.value,
|
|
30
36
|
onChange: ctx.onChange,
|
|
31
37
|
onCommit: ctx.onCommit,
|
|
32
38
|
onCancel: ctx.onCancel,
|
|
39
|
+
onCommitAndMove: ctx.onCommitAndMove,
|
|
40
|
+
onRequestClose: ctx.onRequestClose,
|
|
41
|
+
inCell: ctx.inCell,
|
|
33
42
|
};
|
|
34
43
|
}
|
|
35
44
|
/** Resolve the props to spread onto a registered editor for a given context. */
|
package/dist/group-display.d.ts
CHANGED
|
@@ -36,6 +36,45 @@ export type GroupFooterOptions<R> = {
|
|
|
36
36
|
* preserved. No-op when neither footer flag is set.
|
|
37
37
|
*/
|
|
38
38
|
export declare function insertGroupFooters<R>(rows: ReadonlyArray<R>, opts: GroupFooterOptions<R>): R[];
|
|
39
|
+
export type GroupPaginationOptions<R> = {
|
|
40
|
+
/** Depth of a row (group rows and their descendants). */
|
|
41
|
+
getDepth: (row: R) => number;
|
|
42
|
+
/** True when a row is a group (banner) row. */
|
|
43
|
+
isGroup: (row: R) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* True when a group row is expanded. Defaults to treating every group as
|
|
46
|
+
* expanded. This matters: an EXPANDED group is a header, reprinted above its
|
|
47
|
+
* children and costing no page budget, while a COLLAPSED group is the visible
|
|
48
|
+
* unit itself and must consume a slot - otherwise a fully collapsed grid has
|
|
49
|
+
* no budget-consuming rows at all and every page comes back empty.
|
|
50
|
+
*/
|
|
51
|
+
isExpanded?: (row: R) => boolean;
|
|
52
|
+
pageIndex: number;
|
|
53
|
+
pageSize: number;
|
|
54
|
+
};
|
|
55
|
+
export type GroupPaginationResult<R> = {
|
|
56
|
+
/** The rows to render for this page: the page's budget-consuming rows, each
|
|
57
|
+
* preceded by whichever ancestor banners have not been printed yet. */
|
|
58
|
+
rows: R[];
|
|
59
|
+
/** Budget-consuming rows across every page - what the pager divides by.
|
|
60
|
+
* Data rows plus collapsed group rows; expanded banners do not count. */
|
|
61
|
+
dataRowCount: number;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Paginate a grouped row list by DATA rows, repeating each page's ancestor
|
|
65
|
+
* group headers.
|
|
66
|
+
*
|
|
67
|
+
* Slicing the flat grouped list directly counts group banners against
|
|
68
|
+
* `pageSize`, so `pageSize: 10` over ten groups of two shows almost no data
|
|
69
|
+
* (#73). Here only non-group rows consume the page budget, and a page reprints
|
|
70
|
+
* the group chain its first rows sit under - the same thing a spreadsheet does
|
|
71
|
+
* when a grouped table breaks across pages.
|
|
72
|
+
*
|
|
73
|
+
* Ancestors are tracked with a depth stack rather than parent pointers, so this
|
|
74
|
+
* works on the already-flattened expanded model and needs nothing from the row
|
|
75
|
+
* objects but depth and group-ness.
|
|
76
|
+
*/
|
|
77
|
+
export declare function paginateGroupedRows<R>(rows: ReadonlyArray<R>, opts: GroupPaginationOptions<R>): GroupPaginationResult<R>;
|
|
39
78
|
export type AutoGroupColumnSpec = {
|
|
40
79
|
/** Synthetic column id (e.g. `__autoGroup` or `__group_<field>`). */
|
|
41
80
|
id: string;
|
package/dist/group-display.js
CHANGED
|
@@ -51,6 +51,58 @@ export function insertGroupFooters(rows, opts) {
|
|
|
51
51
|
}
|
|
52
52
|
return out;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Paginate a grouped row list by DATA rows, repeating each page's ancestor
|
|
56
|
+
* group headers.
|
|
57
|
+
*
|
|
58
|
+
* Slicing the flat grouped list directly counts group banners against
|
|
59
|
+
* `pageSize`, so `pageSize: 10` over ten groups of two shows almost no data
|
|
60
|
+
* (#73). Here only non-group rows consume the page budget, and a page reprints
|
|
61
|
+
* the group chain its first rows sit under - the same thing a spreadsheet does
|
|
62
|
+
* when a grouped table breaks across pages.
|
|
63
|
+
*
|
|
64
|
+
* Ancestors are tracked with a depth stack rather than parent pointers, so this
|
|
65
|
+
* works on the already-flattened expanded model and needs nothing from the row
|
|
66
|
+
* objects but depth and group-ness.
|
|
67
|
+
*/
|
|
68
|
+
export function paginateGroupedRows(rows, opts) {
|
|
69
|
+
const { getDepth, isGroup, isExpanded, pageIndex, pageSize } = opts;
|
|
70
|
+
const expanded = isExpanded ?? (() => true);
|
|
71
|
+
const start = pageIndex * pageSize;
|
|
72
|
+
const end = start + pageSize;
|
|
73
|
+
const out = [];
|
|
74
|
+
// Expanded banners currently open, outermost first. `printed` marks the ones
|
|
75
|
+
// this page has already emitted, so a group spanning rows 3-8 prints its
|
|
76
|
+
// banner once, not once per row.
|
|
77
|
+
const openGroups = [];
|
|
78
|
+
let unitsSeen = 0;
|
|
79
|
+
for (const row of rows) {
|
|
80
|
+
const depth = getDepth(row);
|
|
81
|
+
// A row at depth <= an open banner's depth closes it.
|
|
82
|
+
while (openGroups.length > 0 && getDepth(openGroups[openGroups.length - 1].row) >= depth) {
|
|
83
|
+
openGroups.pop();
|
|
84
|
+
}
|
|
85
|
+
if (isGroup(row) && expanded(row)) {
|
|
86
|
+
// A header for rows that follow: reprinted per page, costs no budget.
|
|
87
|
+
openGroups.push({ row, printed: false });
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
// Everything else is a visible unit: a data row, or a collapsed group
|
|
91
|
+
// standing in for its whole subtree.
|
|
92
|
+
const index = unitsSeen;
|
|
93
|
+
unitsSeen += 1;
|
|
94
|
+
if (index < start || index >= end)
|
|
95
|
+
continue;
|
|
96
|
+
for (const g of openGroups) {
|
|
97
|
+
if (g.printed)
|
|
98
|
+
continue;
|
|
99
|
+
g.printed = true;
|
|
100
|
+
out.push(g.row);
|
|
101
|
+
}
|
|
102
|
+
out.push(row);
|
|
103
|
+
}
|
|
104
|
+
return { rows: out, dataRowCount: unitsSeen };
|
|
105
|
+
}
|
|
54
106
|
/**
|
|
55
107
|
* Decide the synthetic auto-group columns for a grouping display mode:
|
|
56
108
|
* - `groupRows`: none (the banner path handles display) - `{ [], {} }`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { formatNumericWithConfig, resolveDatePattern } from './cell-formatting';
|
|
2
2
|
export { coerceExportDate, formatValueForExport, projectGridRows, toExcelNumFmt, valueForExcel, serializeDelimited, serializeJson, serializeMarkdown, serializeHtml, serializeXml, downloadTextFile, downloadBlobFile, copyTextToClipboard, type GridExportOptions, type GridExportScope, type GridExportColumn, type GridClipboardOptions, type GridClipboardFormat, type SerializeProgress, type SerializeOptions, type CsvOptions, } from './export-format';
|
|
3
|
-
export { columnFilteringFeature, columnGroupingFeature, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createPaginatedRowModel, createSortedRowModel, applyGroupAggregate, filterFns, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, type Cell, type CellContext, type CellSpanParams, type ValueParserParams, type EditorContext, type CellData, type ActiveCellState, type CellFormatConfig, type CellFormatter, type Column, type ColumnDef, type ColumnDefTemplate, type GroupAggregator, type Header, type HeaderContext, type HeaderGroup, type Row, type RowData, type SortingState, type SvGrid as SvGridInstance, type SvGridOptions, type TableFeatures, type Updater, } from './core';
|
|
3
|
+
export { columnFilteringFeature, columnGroupingFeature, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createPaginatedRowModel, createSortedRowModel, createTreeRowModel, flattenTreeData, applyGroupAggregate, filterFns, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, type Cell, type CellContext, type CellSpanParams, type ValueParserParams, type EditorContext, type CellData, type ActiveCellState, type CellFormatConfig, type CellFormatter, type Column, type ColumnDef, type ColumnDefTemplate, type GridColumnDef, type GridColumns, type GroupAggregator, type Header, type HeaderContext, type HeaderGroup, type Row, type RowData, type SortingState, type SvGrid as SvGridInstance, type SvGridOptions, type TableFeatures, type TreeRowModelOptions, type FlattenTreeOptions, type Updater, } from './core';
|
|
4
4
|
export { createGrid, createSvGrid, type SvelteGrid } from './createGrid.svelte';
|
|
5
5
|
export { createGridState, createSvGridState } from './createGridState.svelte';
|
|
6
6
|
export { subscribeGrid, subscribeSvGrid } from './subscribe';
|
|
@@ -206,7 +206,7 @@ export { default as SvRowGroupPanel } from './SvRowGroupPanel.svelte';
|
|
|
206
206
|
export { createNamedViews, memoryViews, localStorageViews, attachAutoSavedView, type SavedView, type ViewStorage, type NamedViews, type AutoSavedViewOptions, } from './named-views';
|
|
207
207
|
export { resolveCellFormat, computeColumnStat, lerpColor, rgba, contrastText, formatNeedsStats, formatsNeedingStats, type ConditionalFormat, type ConditionalFormatSpec, type ColorScaleFormat, type ColorScaleStop, type ScaleBounds, type DataBarFormat, type IconSetFormat, type RuleFormat, type ResolvedCellFormat, } from './conditional-formatting';
|
|
208
208
|
export { defaultGridMessages, resolveGridMessages, type GridMessages, } from './grid-messages';
|
|
209
|
-
export { insertGroupFooters, buildAutoGroupColumns, type GroupDisplayType, type GroupFooterOptions, type AutoGroupColumnSpec, type AutoGroupResult, } from './group-display';
|
|
209
|
+
export { insertGroupFooters, buildAutoGroupColumns, paginateGroupedRows, type GroupDisplayType, type GroupFooterOptions, type AutoGroupColumnSpec, type AutoGroupResult, type GroupPaginationOptions, type GroupPaginationResult, } from './group-display';
|
|
210
210
|
export { getKeyboardIntent, getNextActiveCell, type GridKeyboardIntent } from './keyboard';
|
|
211
211
|
export { createVirtualizer } from './virtualization/virtualizer';
|
|
212
212
|
export { createSvelteVirtualizer } from './virtualization/svelte-virtualizer.svelte';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { formatNumericWithConfig, resolveDatePattern } from './cell-formatting';
|
|
2
2
|
export { coerceExportDate, formatValueForExport, projectGridRows, toExcelNumFmt, valueForExcel, serializeDelimited, serializeJson, serializeMarkdown, serializeHtml, serializeXml, downloadTextFile, downloadBlobFile, copyTextToClipboard, } from './export-format';
|
|
3
|
-
export { columnFilteringFeature, columnGroupingFeature, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createPaginatedRowModel, createSortedRowModel, applyGroupAggregate, filterFns, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, } from './core';
|
|
3
|
+
export { columnFilteringFeature, columnGroupingFeature, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createPaginatedRowModel, createSortedRowModel, createTreeRowModel, flattenTreeData, applyGroupAggregate, filterFns, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, } from './core';
|
|
4
4
|
export { createGrid, createSvGrid } from './createGrid.svelte';
|
|
5
5
|
export { createGridState, createSvGridState } from './createGridState.svelte';
|
|
6
6
|
export { subscribeGrid, subscribeSvGrid } from './subscribe';
|
|
@@ -243,7 +243,7 @@ export { default as SvRowGroupPanel } from './SvRowGroupPanel.svelte';
|
|
|
243
243
|
export { createNamedViews, memoryViews, localStorageViews, attachAutoSavedView, } from './named-views';
|
|
244
244
|
export { resolveCellFormat, computeColumnStat, lerpColor, rgba, contrastText, formatNeedsStats, formatsNeedingStats, } from './conditional-formatting';
|
|
245
245
|
export { defaultGridMessages, resolveGridMessages, } from './grid-messages';
|
|
246
|
-
export { insertGroupFooters, buildAutoGroupColumns, } from './group-display';
|
|
246
|
+
export { insertGroupFooters, buildAutoGroupColumns, paginateGroupedRows, } from './group-display';
|
|
247
247
|
export { getKeyboardIntent, getNextActiveCell } from './keyboard';
|
|
248
248
|
export { createVirtualizer } from './virtualization/virtualizer';
|
|
249
249
|
export { createSvelteVirtualizer } from './virtualization/svelte-virtualizer.svelte';
|
|
@@ -107,6 +107,22 @@ export function createKeyboard(ctx) {
|
|
|
107
107
|
// expands a collapsed group row, ArrowLeft collapses an expanded one,
|
|
108
108
|
// instead of moving the active cell (treegrid-style). Leaves fall through
|
|
109
109
|
// to normal navigation.
|
|
110
|
+
// Client tree data gets the same ArrowRight/ArrowLeft contract, driven off
|
|
111
|
+
// the row model rather than consumer callbacks.
|
|
112
|
+
if (ctx.props.treeData && (intent === "moveRight" || intent === "moveLeft")) {
|
|
113
|
+
const row = ctx.allRows[current.rowIndex];
|
|
114
|
+
if (row?.getCanExpand?.()) {
|
|
115
|
+
const expanded = row.getIsExpanded?.() ?? false;
|
|
116
|
+
if (intent === "moveRight" && !expanded) {
|
|
117
|
+
row.toggleExpanded?.();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (intent === "moveLeft" && expanded) {
|
|
121
|
+
row.toggleExpanded?.();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
110
126
|
const serverGroup = ctx.props.serverGroup;
|
|
111
127
|
if (serverGroup && (intent === "moveRight" || intent === "moveLeft")) {
|
|
112
128
|
const data = ctx.allRows[current.rowIndex]?.original;
|
package/dist/recurrence.js
CHANGED
|
@@ -149,8 +149,9 @@ const MO = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct'
|
|
|
149
149
|
const ORD = { 1: 'first', 2: 'second', 3: 'third', 4: 'fourth', 5: 'fifth', [-1]: 'last' };
|
|
150
150
|
// Where in the month a monthly / yearly rule lands, as a phrase.
|
|
151
151
|
function whereInMonth(r, wd, ord) {
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
const firstWeekday = r.weekdays?.[0];
|
|
153
|
+
if (r.weekOfMonth != null && firstWeekday != null) {
|
|
154
|
+
return `the ${ord[r.weekOfMonth] ?? r.weekOfMonth} ${wd[firstWeekday]}`;
|
|
154
155
|
}
|
|
155
156
|
if (r.day === -1)
|
|
156
157
|
return 'the last day';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { CellFormatConfig, ColumnDef, RowData, SvGridOptions, TableFeatures } from './core';
|
|
2
|
-
import type { Props } from './SvGrid.types';
|
|
2
|
+
import type { FilterOperator, Props } from './SvGrid.types';
|
|
3
3
|
import type { GridExportOptions, GridClipboardOptions } from './export-format';
|
|
4
4
|
import type { ChartSpec, ChartType } from './chart';
|
|
5
|
-
export type SvGridFilterOperator =
|
|
5
|
+
export type SvGridFilterOperator = FilterOperator;
|
|
6
6
|
/**
|
|
7
7
|
* A serializable snapshot of everything that makes up the current "view":
|
|
8
8
|
* sort, grouping, pagination, column layout (width / pinning / order /
|
|
@@ -211,6 +211,12 @@ export type SvGridApi<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
211
211
|
*/
|
|
212
212
|
setFacetFilter(columnId: string, values: ReadonlyArray<string> | null): void;
|
|
213
213
|
clearFilter(columnId: string): void;
|
|
214
|
+
/**
|
|
215
|
+
* Drop cached async `editorOptions` so the next render refetches them.
|
|
216
|
+
* Omit `columnId` to clear every column. Only affects columns whose
|
|
217
|
+
* `editorOptions` returns a Promise - static lists are never refetched.
|
|
218
|
+
*/
|
|
219
|
+
refreshEditorOptions(columnId?: string): void;
|
|
214
220
|
/**
|
|
215
221
|
* Clear every active column filter (menu, filter-row, set-list, and global).
|
|
216
222
|
* Resets the grid to "no filtering" in a single call.
|
package/dist/themes/index.d.ts
CHANGED
|
@@ -37,6 +37,16 @@ export type ThemePalette = {
|
|
|
37
37
|
pinnedBorder?: string;
|
|
38
38
|
/** Drop shadow cast by a pinned column into the scroll area. */
|
|
39
39
|
pinnedShadow?: string;
|
|
40
|
+
/** Errors, destructive actions, invalid cells. */
|
|
41
|
+
danger?: string;
|
|
42
|
+
/** Confirmations, positive deltas. */
|
|
43
|
+
success?: string;
|
|
44
|
+
/** Cautions, pending states, the rating star. */
|
|
45
|
+
warning?: string;
|
|
46
|
+
/** Neutral informational callouts. */
|
|
47
|
+
info?: string;
|
|
48
|
+
/** Focus outline color. Defaults to the preset's accent. */
|
|
49
|
+
focusRing?: string;
|
|
40
50
|
};
|
|
41
51
|
export type ThemePreset = {
|
|
42
52
|
/** Stable id, e.g. used as a CSS file name (`themes/<id>.css`). */
|
package/dist/themes/index.js
CHANGED
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* browser. `@svgrid/enterprise`'s Studio layer builds its `ProjectTheme`-aware
|
|
9
9
|
* wrapper on top of this module instead of keeping its own copy.
|
|
10
10
|
*/
|
|
11
|
+
/** Status-color defaults per mode - dark mode lifts them so they stay legible
|
|
12
|
+
* on a dark ground. Values match the components' own inline fallbacks. */
|
|
13
|
+
const STATUS_DEFAULTS = {
|
|
14
|
+
light: { danger: '#dc2626', success: '#16a34a', warning: '#d97706', info: '#0891b2' },
|
|
15
|
+
dark: { danger: '#f87171', success: '#4ade80', warning: '#fbbf24', info: '#22d3ee' },
|
|
16
|
+
};
|
|
11
17
|
/** The built-in theme presets, in gallery order (SvGrid's own theme first). */
|
|
12
18
|
export const themePresets = [
|
|
13
19
|
// Ember is SvGrid's signature look - the palette svgrid.com itself runs on, so
|
|
@@ -108,6 +114,7 @@ export function resolveThemeTokens(preset, mode, accentOverride) {
|
|
|
108
114
|
const resolved = preset ?? defaultThemePreset;
|
|
109
115
|
const p = resolved[mode];
|
|
110
116
|
const accent = accentOverride || p.accent;
|
|
117
|
+
const status = STATUS_DEFAULTS[mode];
|
|
111
118
|
return {
|
|
112
119
|
'--sg-bg': p.bg,
|
|
113
120
|
'--sg-fg': p.fg,
|
|
@@ -146,6 +153,25 @@ export function resolveThemeTokens(preset, mode, accentOverride) {
|
|
|
146
153
|
'--sg-on-accent': onAccent(accent),
|
|
147
154
|
'--sg-radius': `${resolved.radius}px`,
|
|
148
155
|
'--sg-font': resolved.font,
|
|
156
|
+
// Semantic status colors. Same reasoning as the scrollbar tokens below:
|
|
157
|
+
// the UI kit consumes these (destructive buttons, alerts, validation,
|
|
158
|
+
// badges, the rating star), so a preset that doesn't define them leaves
|
|
159
|
+
// every status color at a hard-coded red/green/amber that ignores the
|
|
160
|
+
// theme and, on dark presets, sits at the wrong lightness.
|
|
161
|
+
'--sg-danger': p.danger ?? status.danger,
|
|
162
|
+
'--sg-success': p.success ?? status.success,
|
|
163
|
+
'--sg-warning': p.warning ?? status.warning,
|
|
164
|
+
'--sg-info': p.info ?? status.info,
|
|
165
|
+
// A COLOR, not a shadow - consumed as `outline: 2px solid var(...)`.
|
|
166
|
+
'--sg-focus-ring': p.focusRing ?? accent,
|
|
167
|
+
// Derived surfaces + shape the kit reads. Deriving from the palette keeps
|
|
168
|
+
// them following the preset and the light/dark mode.
|
|
169
|
+
'--sg-muted-bg': p.rowHover,
|
|
170
|
+
'--sg-skeleton-bg': p.rowHover,
|
|
171
|
+
'--sg-radius-lg': `${resolved.radius * 2}px`,
|
|
172
|
+
'--sg-rating-on': p.warning ?? status.warning,
|
|
173
|
+
'--sg-rating-empty': p.border,
|
|
174
|
+
'--sg-invalid-fg': p.danger ?? status.danger,
|
|
149
175
|
// The grid's custom scrollbar is a shadow-DOM web component that reads its
|
|
150
176
|
// OWN --sg-scrollbar-* tokens (with fixed light-mode fallbacks). Without
|
|
151
177
|
// these it ignores the theme entirely - a light scrollbar left on a dark
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svgrid/grid",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.32",
|
|
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",
|
package/src/SvAccordion.test.ts
CHANGED
|
@@ -32,11 +32,11 @@ describe('SvAccordion', () => {
|
|
|
32
32
|
try {
|
|
33
33
|
const headers = [...target.querySelectorAll<HTMLButtonElement>('.sv-acc__header')]
|
|
34
34
|
expect(headers).toHaveLength(3)
|
|
35
|
-
expect(headers[0]
|
|
36
|
-
expect(headers[1]
|
|
35
|
+
expect(headers[0]!.getAttribute('aria-expanded')).toBe('true')
|
|
36
|
+
expect(headers[1]!.getAttribute('aria-expanded')).toBe('false')
|
|
37
37
|
const region = target.querySelector<HTMLElement>('.sv-acc__panel')!
|
|
38
38
|
expect(region.getAttribute('role')).toBe('region')
|
|
39
|
-
expect(region.getAttribute('aria-labelledby')).toBe(headers[0]
|
|
39
|
+
expect(region.getAttribute('aria-labelledby')).toBe(headers[0]!.id)
|
|
40
40
|
// Only one panel region is rendered (the open one).
|
|
41
41
|
expect(target.querySelectorAll('.sv-acc__panel')).toHaveLength(1)
|
|
42
42
|
} finally { destroy() }
|
|
@@ -47,7 +47,7 @@ describe('SvAccordion', () => {
|
|
|
47
47
|
const { target, destroy } = mountAcc({ items, expanded: ['a'], expandMode: 'single', onChange: (ids: string[]) => (got = ids) })
|
|
48
48
|
try {
|
|
49
49
|
const headers = target.querySelectorAll<HTMLButtonElement>('.sv-acc__header')
|
|
50
|
-
headers[1]
|
|
50
|
+
headers[1]!.click()
|
|
51
51
|
flushSync()
|
|
52
52
|
expect(got).toEqual(['b'])
|
|
53
53
|
} finally { destroy() }
|
|
@@ -58,7 +58,7 @@ describe('SvAccordion', () => {
|
|
|
58
58
|
const { target, destroy } = mountAcc({ items, expanded: ['a'], expandMode: 'multiple', onChange: (ids: string[]) => (got = ids) })
|
|
59
59
|
try {
|
|
60
60
|
const headers = target.querySelectorAll<HTMLButtonElement>('.sv-acc__header')
|
|
61
|
-
headers[2]
|
|
61
|
+
headers[2]!.click()
|
|
62
62
|
flushSync()
|
|
63
63
|
expect(got).toEqual(['a', 'c'])
|
|
64
64
|
} finally { destroy() }
|
package/src/SvAvatar.svelte
CHANGED
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
.sv-avatar__status.is-online { background: var(--sg-success, #16a34a); }
|
|
72
72
|
.sv-avatar__status.is-offline { background: var(--sg-muted, #94a3b8); }
|
|
73
73
|
.sv-avatar__status.is-busy { background: var(--sg-danger, #dc2626); }
|
|
74
|
-
.sv-avatar__status.is-away { background: var(--sg-warning, #
|
|
74
|
+
.sv-avatar__status.is-away { background: var(--sg-warning, #d97706); }
|
|
75
75
|
</style>
|