virtual-tree-canvas 0.6.0 → 0.7.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/CHANGELOG.md +32 -0
- package/README.md +90 -3
- package/docs/icon-catalog.html +68 -66
- package/docs/icon-catalog.json +12 -0
- package/docs/icon-catalog.md +111 -107
- package/docs/performance/dynamic-updates-0.7.1.md +38 -0
- package/package.json +3 -2
- package/resources/icons/star-filled.svg +1 -0
- package/resources/icons/trash.svg +1 -0
- package/src/assets/icons.js +12 -4
- package/src/core/dynamic-state.js +19 -29
- package/src/core/patch-batcher.js +4 -0
- package/src/core/theme-manager.js +238 -61
- package/src/core/tree-cell-layout.js +11 -0
- package/src/core/tree-column-model.js +81 -22
- package/src/core/tree-model.js +4 -2
- package/src/core/types.js +1 -0
- package/src/core/view-options.js +98 -18
- package/src/core/visible-row-model.js +51 -21
- package/src/index.d.ts +70 -1
- package/src/input/row-actions.js +248 -0
- package/src/input/row-reorder-input.js +134 -36
- package/src/input/tree-tooltip.js +39 -5
- package/src/input/tree-view-input-controller.js +35 -10
- package/src/inspector/cell-editor-manager.js +128 -35
- package/src/inspector/pane-layout.js +20 -0
- package/src/renderers/checkbox.js +13 -0
- package/src/renderers/tree-row-renderer.js +356 -107
- package/src/tree-view-controller.js +968 -236
- package/src/tree-view.js +218 -54
- package/src/view/styles.js +83 -12
package/src/core/view-options.js
CHANGED
|
@@ -1,30 +1,110 @@
|
|
|
1
1
|
import { resolveTheme } from './theme-manager.js';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Supported TreeView configuration options and defaults.
|
|
5
|
+
*/
|
|
3
6
|
export const treeViewDefaults = Object.freeze({
|
|
4
|
-
mode: 'tree',
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
mode: 'tree',
|
|
8
|
+
presentation: 'pane',
|
|
9
|
+
nodes: Object.freeze([]),
|
|
10
|
+
model: null,
|
|
11
|
+
meta: null,
|
|
12
|
+
columns: null,
|
|
13
|
+
theme: 'dark',
|
|
14
|
+
flatRoot: true,
|
|
15
|
+
enforceMeta: false,
|
|
16
|
+
filter: true,
|
|
17
|
+
filterPlacement: 'auto',
|
|
18
|
+
markUpdated: true,
|
|
19
|
+
editable: true,
|
|
20
|
+
rowActions: Object.freeze([]),
|
|
21
|
+
rowDrag: null,
|
|
22
|
+
rowReorder: false,
|
|
23
|
+
initialExpandDepth: 1,
|
|
24
|
+
rowHeight: undefined,
|
|
25
|
+
indentWidth: undefined,
|
|
26
|
+
showHeader: true,
|
|
27
|
+
headerHeight: 28,
|
|
28
|
+
iconResolver: null,
|
|
29
|
+
fontFamily: null
|
|
9
30
|
});
|
|
10
31
|
export const treeViewOptionNames = Object.freeze(Object.keys(treeViewDefaults));
|
|
11
32
|
|
|
12
33
|
export function validateTreeViewOptions(options) {
|
|
13
|
-
if (!options || typeof options !== 'object' || Array.isArray(options))
|
|
34
|
+
if (!options || typeof options !== 'object' || Array.isArray(options))
|
|
35
|
+
throw new TypeError('Options must be an object');
|
|
14
36
|
for (const [key, value] of Object.entries(options)) {
|
|
37
|
+
if (
|
|
38
|
+
key === 'rowActions' &&
|
|
39
|
+
(!Array.isArray(value) ||
|
|
40
|
+
value.some(
|
|
41
|
+
(action) =>
|
|
42
|
+
!action ||
|
|
43
|
+
typeof action.id !== 'string' ||
|
|
44
|
+
!action.id ||
|
|
45
|
+
typeof action.label !== 'string'
|
|
46
|
+
) ||
|
|
47
|
+
new Set(value.map((action) => action.id)).size !== value.length)
|
|
48
|
+
)
|
|
49
|
+
throw new TypeError('rowActions requires unique ids and labels');
|
|
50
|
+
if (key === 'rowActions')
|
|
51
|
+
for (const action of value) {
|
|
52
|
+
if (action.kind !== undefined && !['button', 'checkbox'].includes(action.kind))
|
|
53
|
+
throw new TypeError('row action kind must be button or checkbox');
|
|
54
|
+
for (const property of ['visible', 'disabled', 'pressed', 'checked'])
|
|
55
|
+
if (
|
|
56
|
+
action[property] !== undefined &&
|
|
57
|
+
!['boolean', 'function'].includes(typeof action[property])
|
|
58
|
+
)
|
|
59
|
+
throw new TypeError(`${property} must be boolean or a function`);
|
|
60
|
+
if (action.icon !== undefined && !['string', 'function'].includes(typeof action.icon))
|
|
61
|
+
throw new TypeError('action icon must be a name or a function');
|
|
62
|
+
}
|
|
63
|
+
if (key === 'rowDrag' && value !== null && typeof value !== 'function')
|
|
64
|
+
throw new TypeError('rowDrag must be a function or null');
|
|
15
65
|
if (key === 'theme') resolveTheme(value);
|
|
16
|
-
if (key === 'mode' && !['tree', 'inspector'].includes(value))
|
|
17
|
-
|
|
18
|
-
if (key === '
|
|
66
|
+
if (key === 'mode' && !['tree', 'inspector'].includes(value))
|
|
67
|
+
throw new TypeError('mode must be tree or inspector');
|
|
68
|
+
if (key === 'presentation' && !['pane', 'table'].includes(value))
|
|
69
|
+
throw new TypeError('presentation must be pane or table');
|
|
70
|
+
if (key === 'filterPlacement' && !['auto', 'bar', 'header'].includes(value))
|
|
71
|
+
throw new TypeError('filterPlacement must be auto, bar or header');
|
|
19
72
|
if (key === 'nodes' && !Array.isArray(value)) throw new TypeError('nodes must be an array');
|
|
20
|
-
if (key === 'columns' && value !== null && !Array.isArray(value))
|
|
73
|
+
if (key === 'columns' && value !== null && !Array.isArray(value))
|
|
74
|
+
throw new TypeError('columns must be an array or null');
|
|
21
75
|
if (key === 'model' && value === undefined) throw new TypeError('model must not be undefined');
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (
|
|
76
|
+
if (
|
|
77
|
+
key === 'meta' &&
|
|
78
|
+
value !== null &&
|
|
79
|
+
(!value || typeof value !== 'object' || Array.isArray(value))
|
|
80
|
+
)
|
|
81
|
+
throw new TypeError('meta must be an object or null');
|
|
82
|
+
if (
|
|
83
|
+
['rowHeight', 'indentWidth'].includes(key) &&
|
|
84
|
+
value !== undefined &&
|
|
85
|
+
(!Number.isFinite(value) || value <= 0)
|
|
86
|
+
)
|
|
87
|
+
throw new TypeError(`${key} must be a positive number`);
|
|
88
|
+
if (key === 'headerHeight' && (!Number.isFinite(value) || value < 0))
|
|
89
|
+
throw new TypeError('headerHeight must be non-negative');
|
|
90
|
+
if (key === 'initialExpandDepth' && (!Number.isInteger(value) || value < 0))
|
|
91
|
+
throw new TypeError('initialExpandDepth must be a non-negative integer');
|
|
92
|
+
if (key === 'iconResolver' && value !== null && typeof value !== 'function')
|
|
93
|
+
throw new TypeError('iconResolver must be a function or null');
|
|
94
|
+
if (key === 'fontFamily' && value !== null && typeof value !== 'string')
|
|
95
|
+
throw new TypeError('fontFamily must be a string or null');
|
|
96
|
+
if (
|
|
97
|
+
[
|
|
98
|
+
'flatRoot',
|
|
99
|
+
'enforceMeta',
|
|
100
|
+
'filter',
|
|
101
|
+
'markUpdated',
|
|
102
|
+
'editable',
|
|
103
|
+
'rowReorder',
|
|
104
|
+
'showHeader'
|
|
105
|
+
].includes(key) &&
|
|
106
|
+
typeof value !== 'boolean'
|
|
107
|
+
)
|
|
108
|
+
throw new TypeError(`${key} must be boolean`);
|
|
29
109
|
}
|
|
30
110
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
export class VisibleRowModel extends EventTarget {
|
|
15
|
+
|
|
15
16
|
/**
|
|
16
17
|
* @param {{
|
|
17
18
|
* model: import('./tree-model.js').TreeModel,
|
|
@@ -26,9 +27,15 @@ export class VisibleRowModel extends EventTarget {
|
|
|
26
27
|
this.expansion = expansion;
|
|
27
28
|
this.rowHeight = rowHeight;
|
|
28
29
|
this.indentWidth = indentWidth;
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @type {TreeRow[]}
|
|
33
|
+
*/
|
|
30
34
|
this.rows = [];
|
|
31
|
-
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @type {Map<string, number>}
|
|
38
|
+
*/
|
|
32
39
|
this.rowIndexById = new Map();
|
|
33
40
|
this.contentWidth = 0;
|
|
34
41
|
this.contentHeight = 0;
|
|
@@ -75,14 +82,18 @@ export class VisibleRowModel extends EventTarget {
|
|
|
75
82
|
const node = this.model.index.getNode(id);
|
|
76
83
|
const state = this.model.dynamicState.get(id) ?? {};
|
|
77
84
|
const ownMatch = node ? this.filterPredicate(node, state) : false;
|
|
78
|
-
const childMatch = this.model.index
|
|
85
|
+
const childMatch = this.model.index
|
|
86
|
+
.getChildren(id)
|
|
87
|
+
.some((childId) => subtreeIncluded(childId));
|
|
79
88
|
const included = ownMatch || childMatch;
|
|
80
89
|
includeCache.set(id, included);
|
|
81
90
|
return included;
|
|
82
91
|
};
|
|
83
92
|
|
|
84
93
|
const sortedChildren = (id) => {
|
|
85
|
-
const children = this.model.index
|
|
94
|
+
const children = this.model.index
|
|
95
|
+
.getChildren(id)
|
|
96
|
+
.filter((childId) => subtreeIncluded(childId));
|
|
86
97
|
if (!this.sortComparator) return children;
|
|
87
98
|
return children.slice().sort((aId, bId) => {
|
|
88
99
|
const a = this.model.index.getNode(aId);
|
|
@@ -98,7 +109,8 @@ export class VisibleRowModel extends EventTarget {
|
|
|
98
109
|
const children = sortedChildren(id);
|
|
99
110
|
const hasChildren = this.expansion.hasChildren(id);
|
|
100
111
|
const autoExpanded = Boolean(this.filterPredicate && children.length > 0);
|
|
101
|
-
const expanded =
|
|
112
|
+
const expanded =
|
|
113
|
+
(this.expansion.isExpanded(id) || autoExpanded) && !this.filterCollapsed.has(id);
|
|
102
114
|
const rowIndex = this.rows.length;
|
|
103
115
|
this.rows.push({
|
|
104
116
|
nodeId: id,
|
|
@@ -108,7 +120,7 @@ export class VisibleRowModel extends EventTarget {
|
|
|
108
120
|
y: rowIndex * this.rowHeight,
|
|
109
121
|
height: this.rowHeight,
|
|
110
122
|
expanded,
|
|
111
|
-
hasChildren
|
|
123
|
+
hasChildren
|
|
112
124
|
});
|
|
113
125
|
this.rowIndexById.set(id, rowIndex);
|
|
114
126
|
maxDepth = Math.max(maxDepth, depth);
|
|
@@ -118,7 +130,9 @@ export class VisibleRowModel extends EventTarget {
|
|
|
118
130
|
|
|
119
131
|
const roots = this.model.index.roots.filter((rootId) => subtreeIncluded(rootId));
|
|
120
132
|
if (this.sortComparator) {
|
|
121
|
-
roots.sort((aId, bId) =>
|
|
133
|
+
roots.sort((aId, bId) =>
|
|
134
|
+
this.sortComparator(this.model.index.getNode(aId), this.model.index.getNode(bId), aId, bId)
|
|
135
|
+
);
|
|
122
136
|
}
|
|
123
137
|
for (const rootId of roots) visit(rootId, 0);
|
|
124
138
|
this.contentHeight = this.rows.length * this.rowHeight;
|
|
@@ -126,13 +140,17 @@ export class VisibleRowModel extends EventTarget {
|
|
|
126
140
|
this.dispatchEvent(new Event('change'));
|
|
127
141
|
}
|
|
128
142
|
|
|
129
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* @param {string} id
|
|
145
|
+
*/
|
|
130
146
|
getRowById(id) {
|
|
131
147
|
const rowIndex = this.rowIndexById.get(id);
|
|
132
148
|
return rowIndex === undefined ? null : this.rows[rowIndex];
|
|
133
149
|
}
|
|
134
150
|
|
|
135
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* @param {number} rowIndex
|
|
153
|
+
*/
|
|
136
154
|
getRow(rowIndex) {
|
|
137
155
|
return this.rows[rowIndex] ?? null;
|
|
138
156
|
}
|
|
@@ -144,14 +162,23 @@ export class VisibleRowModel extends EventTarget {
|
|
|
144
162
|
getVisibleRange(viewport, overscan = 6) {
|
|
145
163
|
const rowViewportHeight = viewport.rowViewportHeight ?? viewport.viewportHeight;
|
|
146
164
|
const first = Math.max(0, Math.floor(viewport.scrollY / this.rowHeight) - overscan);
|
|
147
|
-
const last = Math.min(
|
|
148
|
-
|
|
165
|
+
const last = Math.min(
|
|
166
|
+
this.rows.length - 1,
|
|
167
|
+
Math.ceil((viewport.scrollY + rowViewportHeight) / this.rowHeight) + overscan
|
|
168
|
+
);
|
|
169
|
+
return {
|
|
170
|
+
first,
|
|
171
|
+
last,
|
|
172
|
+
count: last >= first ? last - first + 1 : 0
|
|
173
|
+
};
|
|
149
174
|
}
|
|
150
175
|
|
|
151
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* @param {import('./tree-view-viewport.js').TreeViewViewport} viewport
|
|
178
|
+
*/
|
|
152
179
|
getStickyRows(viewport) {
|
|
153
180
|
const rowViewportHeight = viewport.rowViewportHeight ?? viewport.viewportHeight;
|
|
154
|
-
if (rowViewportHeight < this.rowHeight * 2) return [];
|
|
181
|
+
if (viewport.scrollY <= 0 || rowViewportHeight < this.rowHeight * 2) return [];
|
|
155
182
|
const max = Math.max(0, Math.min(6, Math.floor(rowViewportHeight / this.rowHeight) - 1));
|
|
156
183
|
const first = Math.max(0, Math.floor(viewport.scrollY / this.rowHeight));
|
|
157
184
|
let row = this.getRow(first);
|
|
@@ -169,20 +196,23 @@ export class VisibleRowModel extends EventTarget {
|
|
|
169
196
|
sticky = [...sticky.slice(0, replaceAt), candidate];
|
|
170
197
|
}
|
|
171
198
|
}
|
|
172
|
-
return sticky
|
|
173
|
-
|
|
174
|
-
.
|
|
175
|
-
|
|
176
|
-
stickyY: Math.max(ancestor.y - viewport.scrollY, index * this.rowHeight),
|
|
177
|
-
}));
|
|
199
|
+
return sticky.slice(-max).map((ancestor, index) => ({
|
|
200
|
+
...ancestor,
|
|
201
|
+
stickyY: Math.max(ancestor.y - viewport.scrollY, index * this.rowHeight)
|
|
202
|
+
}));
|
|
178
203
|
}
|
|
179
204
|
|
|
180
|
-
/**
|
|
205
|
+
/**
|
|
206
|
+
* @param {TreeRow} row
|
|
207
|
+
*/
|
|
181
208
|
#getStickyPath(row) {
|
|
182
|
-
const ancestors = this.model.index
|
|
209
|
+
const ancestors = this.model.index
|
|
210
|
+
.getAncestors(row.nodeId)
|
|
211
|
+
.reverse()
|
|
183
212
|
.map((id) => this.getRowById(id))
|
|
184
213
|
.filter((ancestor) => ancestor && ancestor.rowIndex < row.rowIndex);
|
|
185
214
|
if (row.hasChildren) ancestors.push(row);
|
|
186
215
|
return ancestors;
|
|
187
216
|
}
|
|
217
|
+
|
|
188
218
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
export interface RowAction {
|
|
2
|
+
kind?: 'button' | 'checkbox';
|
|
3
|
+
checked?: boolean | ((node: TreeNode, state: Record<string, any>) => boolean);
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
icon?: string | ((node: TreeNode, state: Record<string, any>) => string);
|
|
7
|
+
/** Icon variants to rasterize before the first action. */
|
|
8
|
+
preloadIcons?: string[];
|
|
9
|
+
visible?: boolean | ((node: TreeNode, state: Record<string, any>) => boolean);
|
|
10
|
+
disabled?: boolean | ((node: TreeNode, state: Record<string, any>) => boolean);
|
|
11
|
+
pressed?: boolean | ((node: TreeNode, state: Record<string, any>) => boolean);
|
|
12
|
+
}
|
|
13
|
+
export type RowDragResolver = ((node: TreeNode, state: Record<string, any>) => any | null) | null;
|
|
1
14
|
export const builtinIconNames: readonly string[];
|
|
2
15
|
export interface RowReorderDetail {
|
|
3
16
|
nodeId: string;
|
|
@@ -14,6 +27,8 @@ export interface TreeViewOptions {
|
|
|
14
27
|
iconsBaseUrl?: string | URL;
|
|
15
28
|
iconRegistry?: IconRegistry;
|
|
16
29
|
rowReorder?: boolean;
|
|
30
|
+
rowActions?: RowAction[];
|
|
31
|
+
rowDrag?: RowDragResolver;
|
|
17
32
|
autoRender?: boolean;
|
|
18
33
|
tooltip?: boolean;
|
|
19
34
|
canvas?: HTMLCanvasElement;
|
|
@@ -49,6 +64,7 @@ export type TreeNode = {
|
|
|
49
64
|
icon?: string;
|
|
50
65
|
image?: string;
|
|
51
66
|
tags?: string[];
|
|
67
|
+
reorderable?: boolean;
|
|
52
68
|
data?: any;
|
|
53
69
|
};
|
|
54
70
|
|
|
@@ -89,8 +105,12 @@ export type Column = {
|
|
|
89
105
|
minWidth?: number;
|
|
90
106
|
align?: 'left' | 'center' | 'right';
|
|
91
107
|
kind?: string;
|
|
108
|
+
/** Optional semantic type for value-column coloring; defaults to the raw value type. */
|
|
109
|
+
valueType?: string | ((value: any, node: TreeNode, state: Record<string, any>) => string);
|
|
92
110
|
sortable?: boolean;
|
|
93
111
|
value?: (node: TreeNode, state: Record<string, any>) => string | number | boolean;
|
|
112
|
+
/** Formats displayed text and tooltips without changing sorting values. */
|
|
113
|
+
format?: (value: any, node: TreeNode, state: Record<string, any>) => string;
|
|
94
114
|
render?: (ctx: CanvasRenderingContext2D, cell: any) => void;
|
|
95
115
|
};
|
|
96
116
|
|
|
@@ -128,6 +148,10 @@ export class TreeViewController {
|
|
|
128
148
|
model: any;
|
|
129
149
|
inspector: any;
|
|
130
150
|
columnModel: any;
|
|
151
|
+
setRowActions(actions: RowAction[]): void;
|
|
152
|
+
setRowDrag(resolver: RowDragResolver): void;
|
|
153
|
+
sortBy(id: string, direction?: string): boolean;
|
|
154
|
+
clearSort(): void;
|
|
131
155
|
setEditable(enabled: boolean): void;
|
|
132
156
|
setInitialExpandDepth(depth: number): void;
|
|
133
157
|
setIconResolver(resolver: TreeViewConfiguration['iconResolver']): void;
|
|
@@ -141,7 +165,7 @@ export class TreeViewController {
|
|
|
141
165
|
nextSearchResult(): string | null;
|
|
142
166
|
previousSearchResult(): string | null;
|
|
143
167
|
setRowReorder(enabled: boolean): void;
|
|
144
|
-
canReorderRows(): boolean;
|
|
168
|
+
canReorderRows(nodeId?: string): boolean;
|
|
145
169
|
getRowOrder(parentId?: string | null): string[];
|
|
146
170
|
moveRow(nodeId: string, targetIndex: number, options?: {source?: RowReorderDetail["source"]}): boolean;
|
|
147
171
|
moveRowBy(nodeId: string, offset: number, options?: {source?: RowReorderDetail["source"]}): boolean;
|
|
@@ -159,12 +183,14 @@ export class TreeViewController {
|
|
|
159
183
|
setModel(model: any, meta?: Record<string, MetaRule>, options?: Record<string, any>): void;
|
|
160
184
|
setColumns(columns: Column[] | null): void;
|
|
161
185
|
setDynamicState(patches: DynamicPatch[]): void;
|
|
186
|
+
flushDynamicState(): Set<string>;
|
|
162
187
|
setTheme(theme: any): void;
|
|
163
188
|
setLayoutMetrics(options?: { rowHeight?: number; indentWidth?: number; headerHeight?: number }): void;
|
|
164
189
|
registerIcon(name: string, icon: IconSource): any;
|
|
165
190
|
resize(width: number, height: number): void;
|
|
166
191
|
render(time?: number): void;
|
|
167
192
|
renderMeasured(time?: number): any;
|
|
193
|
+
getStats(): TreeViewStats;
|
|
168
194
|
hitTest(clientX: number, clientY: number): any;
|
|
169
195
|
getTooltipForHit(hit: any): any;
|
|
170
196
|
search(query: string, options?: Record<string, any> & { caseSensitive?: boolean; wholeWord?: boolean }): any;
|
|
@@ -176,10 +202,31 @@ export class TreeViewController {
|
|
|
176
202
|
getSelection(): string[];
|
|
177
203
|
setSelection(ids: string[]): void;
|
|
178
204
|
clearSelection(): void;
|
|
205
|
+
toggle(nodeId: string): boolean;
|
|
179
206
|
expandAll(): void;
|
|
180
207
|
collapseAll(): void;
|
|
181
208
|
}
|
|
182
209
|
|
|
210
|
+
export interface TreeViewStats {
|
|
211
|
+
totalNodes: number;
|
|
212
|
+
visibleRows: number;
|
|
213
|
+
renderedRows: number;
|
|
214
|
+
patchesFrame: number;
|
|
215
|
+
dirtyNodes: number;
|
|
216
|
+
selectedCount: number;
|
|
217
|
+
rebuildCount: number;
|
|
218
|
+
setDynamicStateCalls: number;
|
|
219
|
+
patchesReceived: number;
|
|
220
|
+
uniqueNodesReceived: number;
|
|
221
|
+
nodesChanged: number;
|
|
222
|
+
rendersRequested: number;
|
|
223
|
+
rendersExecuted: number;
|
|
224
|
+
rendersAvoidedNoChanges: number;
|
|
225
|
+
rendersAvoidedOffscreen: number;
|
|
226
|
+
rowActionsFullUpdates: number;
|
|
227
|
+
rowActionsIncrementalUpdates: number;
|
|
228
|
+
}
|
|
229
|
+
|
|
183
230
|
export class TreeViewInputController {
|
|
184
231
|
constructor(options: { controller: TreeViewController; cellEditor?: CellEditorManager | null });
|
|
185
232
|
destroy(): void;
|
|
@@ -208,9 +255,13 @@ export interface TreeViewConfiguration {
|
|
|
208
255
|
markUpdated?: boolean;
|
|
209
256
|
editable?: boolean;
|
|
210
257
|
rowReorder?: boolean;
|
|
258
|
+
rowActions?: RowAction[];
|
|
259
|
+
rowDrag?: RowDragResolver;
|
|
211
260
|
initialExpandDepth?: number;
|
|
212
261
|
rowHeight?: number;
|
|
213
262
|
indentWidth?: number;
|
|
263
|
+
/** Hide column headings without hiding the filter bar. Defaults to true; inspector panes may hide headings automatically. */
|
|
264
|
+
showHeader?: boolean;
|
|
214
265
|
headerHeight?: number;
|
|
215
266
|
iconResolver?: ((node: TreeNode) => string | Partial<TreeNode> | null | undefined) | null;
|
|
216
267
|
fontFamily?: string | null;
|
|
@@ -273,7 +324,18 @@ export interface TreeSearchState {
|
|
|
273
324
|
current: string | null;
|
|
274
325
|
count: number;
|
|
275
326
|
}
|
|
327
|
+
export interface RowDragDetail {
|
|
328
|
+
nodeId: string;
|
|
329
|
+
payload: any;
|
|
330
|
+
label: string;
|
|
331
|
+
originalEvent: PointerEvent;
|
|
332
|
+
}
|
|
276
333
|
export interface TreeViewEvents {
|
|
334
|
+
rowaction: {checked?: boolean; actionId: string; nodeId: string; node: TreeNode; originalEvent: MouseEvent};
|
|
335
|
+
rowdragstart: RowDragDetail;
|
|
336
|
+
rowdragmove: RowDragDetail;
|
|
337
|
+
rowdragend: RowDragDetail;
|
|
338
|
+
rowdragcancel: Omit<RowDragDetail, 'originalEvent'>;
|
|
277
339
|
valuechange: InspectorValueChange;
|
|
278
340
|
modelchange: {model: any; meta?: Record<string, MetaRule>; path?: string; structural?: boolean; action?: string; value?: any} & Partial<InspectorValueChange>;
|
|
279
341
|
action: {path: string; label: string; nodeId: string; model: any; source: string};
|
|
@@ -288,3 +350,10 @@ export interface TreeViewEvents {
|
|
|
288
350
|
rowreorderchange: {enabled: boolean};
|
|
289
351
|
}
|
|
290
352
|
export type TreeViewEvent<K extends keyof TreeViewEvents> = {type: K; detail: TreeViewEvents[K]};
|
|
353
|
+
|
|
354
|
+
/** Formats an inspector value using its options and precision. */
|
|
355
|
+
export function formatInspectorValue(value: any, meta?: MetaRule): string;
|
|
356
|
+
|
|
357
|
+
export class ModelInspectorBuilder {
|
|
358
|
+
build(model: any, meta?: Record<string, MetaRule>, options?: {flatRoot?: boolean; enforceMeta?: boolean}): TreeNode[];
|
|
359
|
+
}
|