virtual-tree-canvas 0.3.9 → 0.4.0
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 +164 -225
- package/docs/inspector-demo.png +0 -0
- package/docs/tree-demo.png +0 -0
- package/package.json +2 -1
- package/src/assets/icons/air.svg +1 -0
- package/src/assets/icons/aircraft.svg +1 -0
- package/src/assets/icons/bus.svg +1 -0
- package/src/assets/icons/control.svg +1 -0
- package/src/assets/icons/damage.svg +1 -0
- package/src/assets/icons/error.svg +1 -0
- package/src/assets/icons/folder.svg +1 -0
- package/src/assets/icons/ground.svg +1 -0
- package/src/assets/icons/inspector-array.svg +1 -0
- package/src/assets/icons/inspector-object.svg +1 -0
- package/src/assets/icons/inspector-value.svg +1 -0
- package/src/assets/icons/munition.svg +1 -0
- package/src/assets/icons/placeholder.svg +1 -0
- package/src/assets/icons/point.svg +1 -0
- package/src/assets/icons/radar.svg +1 -0
- package/src/assets/icons/situation.svg +1 -0
- package/src/assets/icons/space.svg +1 -0
- package/src/assets/icons/subsurface.svg +1 -0
- package/src/assets/icons/surface.svg +1 -0
- package/src/assets/icons/task.svg +1 -0
- package/src/assets/icons/track.svg +1 -0
- package/src/assets/icons/warning.svg +1 -0
- package/src/assets/icons.js +25 -0
- package/src/core/icon-registry.js +173 -495
- package/src/core/tree-worker-client.js +17 -0
- package/src/index.d.ts +13 -0
- package/src/renderers/tree-row-renderer.js +35 -0
- package/src/tree-view-controller.js +9 -2
package/README.md
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
# virtual-tree-canvas
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**A fast, framework-agnostic tree and tree-table for large hierarchical data.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`virtual-tree-canvas` gives applications the interactions users expect from a
|
|
6
|
+
native tree view—expandable branches, columns, selection, search, filtering and
|
|
7
|
+
keyboard navigation—without creating a DOM node for every row. It renders only
|
|
8
|
+
what is visible with Canvas2D, so it stays responsive as data and live updates
|
|
9
|
+
grow.
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+

|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
- Tree-table columns
|
|
11
|
-
- Expand/collapse
|
|
12
|
-
- Single and multi-selection
|
|
13
|
-
- Search and focus
|
|
14
|
-
- Keyboard navigation
|
|
15
|
-
- Horizontal and vertical scrolling
|
|
16
|
-
- Themes and type-based styles
|
|
17
|
-
- Canvas vector icons and image icons
|
|
18
|
-
- Batched dynamic state updates
|
|
19
|
-
- Benchmark/demo mode
|
|
13
|
+
## Why use it?
|
|
20
14
|
|
|
21
|
-
|
|
15
|
+
- **Built for large trees.** Only visible rows plus a small overscan range are painted.
|
|
16
|
+
- **Interactive by default.** Tree-table columns, sorting, resizing, selection, focus, tooltips and keyboard navigation are included.
|
|
17
|
+
- **Good for live data.** Batched state patches update status, progress and values without rebuilding the tree.
|
|
18
|
+
- **Framework-free.** Use it directly from browser JavaScript; no React or Web Component runtime is required.
|
|
19
|
+
- **Easy to style.** Built-in dark, light and tactical themes, type-based styling, and editable SVG icons.
|
|
20
|
+
- **Ready for inspectors.** Turn plain JavaScript objects into compact editable forms or property tables.
|
|
22
21
|
|
|
23
22
|
## Install
|
|
24
23
|
|
|
@@ -26,165 +25,85 @@ No React, Web Components, or frontend framework required.
|
|
|
26
25
|
npm install virtual-tree-canvas
|
|
27
26
|
```
|
|
28
27
|
|
|
29
|
-
##
|
|
28
|
+
## Quick start
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Dynamic updates are handled as patches:
|
|
34
|
-
|
|
35
|
-
```js
|
|
36
|
-
tree.setDynamicState([
|
|
37
|
-
{ id: 'node-1', state: { status: 1, progress: 0.7, value: 42 } }
|
|
38
|
-
]);
|
|
30
|
+
```html
|
|
31
|
+
<canvas id="assets-tree"></canvas>
|
|
39
32
|
```
|
|
40
33
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Cold-path operations such as `setData()`, expand/collapse, and search may rebuild the visible row list.
|
|
49
|
-
|
|
50
|
-
For large datasets, `enableWorkers()` moves search and filtered row rebuilds off the main thread when browser Workers are available. The synchronous APIs remain available for small datasets, tests, and custom integrations.
|
|
51
|
-
|
|
52
|
-
## Basic Usage
|
|
34
|
+
```css
|
|
35
|
+
#assets-tree {
|
|
36
|
+
display: block;
|
|
37
|
+
width: 100%;
|
|
38
|
+
height: 480px;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
53
41
|
|
|
54
42
|
```js
|
|
55
43
|
import { TreeViewController } from 'virtual-tree-canvas';
|
|
56
44
|
|
|
57
|
-
const
|
|
58
|
-
|
|
45
|
+
const tree = new TreeViewController({
|
|
46
|
+
canvas: document.querySelector('#assets-tree'),
|
|
47
|
+
initialExpandDepth: 2,
|
|
48
|
+
});
|
|
59
49
|
|
|
60
50
|
tree.setData([
|
|
61
|
-
{ id: '
|
|
62
|
-
{ id: '
|
|
51
|
+
{ id: 'fleet', label: 'Fleet', type: 'root' },
|
|
52
|
+
{ id: 'radar-1', parentId: 'fleet', label: 'Forward radar', type: 'sensor' },
|
|
53
|
+
{ id: 'track-100', parentId: 'radar-1', label: 'Track T-100', type: 'track' },
|
|
63
54
|
]);
|
|
64
|
-
```
|
|
65
55
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
tree.setModel(model, meta, { presentation: 'pane' });
|
|
71
|
-
tree.setDynamicState(patches);
|
|
72
|
-
|
|
73
|
-
tree.expand(nodeId);
|
|
74
|
-
tree.collapse(nodeId);
|
|
75
|
-
tree.toggle(nodeId);
|
|
76
|
-
tree.expandAll();
|
|
77
|
-
tree.collapseAll();
|
|
78
|
-
|
|
79
|
-
tree.search(query);
|
|
80
|
-
tree.searchAsync(query);
|
|
81
|
-
tree.clearSearch();
|
|
82
|
-
tree.getSearchState();
|
|
83
|
-
tree.nextSearchResult();
|
|
84
|
-
tree.previousSearchResult();
|
|
85
|
-
tree.setFilter(queryOrPredicate);
|
|
86
|
-
tree.setFilterAsync(query);
|
|
87
|
-
tree.clearFilter();
|
|
88
|
-
|
|
89
|
-
tree.focusNode(nodeId);
|
|
90
|
-
tree.scrollToNode(nodeId, 'center');
|
|
91
|
-
|
|
92
|
-
tree.getSelection();
|
|
93
|
-
tree.setSelection(['node-1', 'node-2']);
|
|
94
|
-
tree.clearSelection();
|
|
95
|
-
|
|
96
|
-
tree.setTheme(theme);
|
|
97
|
-
tree.setColumns(columns);
|
|
98
|
-
tree.resizeColumn(columnId, width);
|
|
99
|
-
tree.moveColumn(columnId, targetIndex);
|
|
100
|
-
tree.sortBy(columnId, 'asc');
|
|
101
|
-
tree.clearSort();
|
|
102
|
-
tree.registerIcon('custom', imageOrUrlOrDrawFunction);
|
|
103
|
-
|
|
104
|
-
tree.enableWorkers();
|
|
105
|
-
tree.disableWorkers();
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Model Inspector
|
|
109
|
-
|
|
110
|
-
`setModel(model, meta, options)` renders plain JSON-like objects as an editable inspector.
|
|
56
|
+
tree.setDynamicState([
|
|
57
|
+
{ id: 'radar-1', state: { status: 0, progress: 0.72 } },
|
|
58
|
+
{ id: 'track-100', state: { status: 1, value: 430 } },
|
|
59
|
+
]);
|
|
111
60
|
|
|
112
|
-
|
|
113
|
-
tree.setModel(
|
|
114
|
-
{
|
|
115
|
-
sensor: { enabled: true, range: 72, mode: 'track' },
|
|
116
|
-
tracks: [{ id: 'T-100', speed: 430 }]
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
'sensor.range': { min: 0, max: 120, step: 1, integer: true },
|
|
120
|
-
'sensor.mode': { options: { Search: 'search', Track: 'track' } },
|
|
121
|
-
'tracks.*.speed': { min: 0, max: 900, step: 5 },
|
|
122
|
-
'tracks.*.id': { readonly: true }
|
|
123
|
-
}
|
|
124
|
-
);
|
|
61
|
+
tree.render();
|
|
125
62
|
```
|
|
126
63
|
|
|
127
|
-
|
|
64
|
+
The controller observes the canvas size. Call `tree.render()` from your own
|
|
65
|
+
animation loop when the data is live, or after changing data in a static view.
|
|
128
66
|
|
|
129
|
-
|
|
130
|
-
tree.setModel(model, meta, {
|
|
131
|
-
presentation: 'pane',
|
|
132
|
-
flatRoot: true, // render root properties directly
|
|
133
|
-
enforceMeta: true, // fields without metadata are readonly/disabled
|
|
134
|
-
filter: true, // use the header as a filter input
|
|
135
|
-
markUpdated: false // do not show update dots for local user edits
|
|
136
|
-
});
|
|
137
|
-
```
|
|
67
|
+

|
|
138
68
|
|
|
139
|
-
|
|
69
|
+
## Common tasks
|
|
140
70
|
|
|
141
71
|
```js
|
|
142
|
-
|
|
143
|
-
tree.
|
|
144
|
-
|
|
72
|
+
// Navigate and select
|
|
73
|
+
tree.expand('radar-1');
|
|
74
|
+
tree.focusNode('track-100', { align: 'center', select: true });
|
|
75
|
+
tree.setSelection(['track-100']);
|
|
76
|
+
|
|
77
|
+
// Search and filter
|
|
78
|
+
tree.search('forward radar');
|
|
79
|
+
tree.setFilter('track');
|
|
80
|
+
tree.clearFilter();
|
|
145
81
|
|
|
146
|
-
|
|
82
|
+
// Configure the tree-table
|
|
83
|
+
tree.setColumns(columns);
|
|
84
|
+
tree.sortBy('status', 'asc');
|
|
85
|
+
tree.resizeColumn('name', 320);
|
|
147
86
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
tracks.0.speed
|
|
151
|
-
tracks.*.speed
|
|
87
|
+
// Use a worker for expensive search/filter work
|
|
88
|
+
await tree.enableWorkers();
|
|
152
89
|
```
|
|
153
90
|
|
|
154
|
-
|
|
91
|
+
## Live updates and performance
|
|
155
92
|
|
|
156
|
-
|
|
93
|
+
Use `setDynamicState()` for values that change frequently. It updates the
|
|
94
|
+
dynamic state of affected nodes while retaining indexes, expansion state and
|
|
95
|
+
visible rows.
|
|
157
96
|
|
|
158
97
|
```js
|
|
159
|
-
tree.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
`scrollToNode()` supports:
|
|
165
|
-
|
|
166
|
-
```text
|
|
167
|
-
start | center | end | nearest
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Events
|
|
171
|
-
|
|
172
|
-
```js
|
|
173
|
-
tree.on('nodehover', (event) => {});
|
|
174
|
-
tree.on('nodeclick', (event) => {});
|
|
175
|
-
tree.on('nodedblclick', (event) => {});
|
|
176
|
-
tree.on('selectionchange', (event) => {});
|
|
177
|
-
tree.on('expand', (event) => {});
|
|
178
|
-
tree.on('collapse', (event) => {});
|
|
179
|
-
tree.on('focuschange', (event) => {});
|
|
180
|
-
tree.on('searchchange', (event) => {});
|
|
181
|
-
tree.on('filterchange', (event) => {});
|
|
182
|
-
tree.on('sortchange', (event) => {});
|
|
183
|
-
tree.on('columnschange', (event) => {});
|
|
184
|
-
tree.on('viewportchange', (event) => {});
|
|
98
|
+
tree.setDynamicState([
|
|
99
|
+
{ id: 'track-100', state: { status: 1, progress: 0.7, value: 42 } },
|
|
100
|
+
]);
|
|
185
101
|
```
|
|
186
102
|
|
|
187
|
-
|
|
103
|
+
`setData()`, expand/collapse, sorting and filtering are structural operations
|
|
104
|
+
and may rebuild the visible row list. For very large datasets,
|
|
105
|
+
`enableWorkers()` moves search and filtered row rebuilds off the main thread
|
|
106
|
+
when Workers are available.
|
|
188
107
|
|
|
189
108
|
## Columns
|
|
190
109
|
|
|
@@ -195,9 +114,8 @@ tree.setColumns([
|
|
|
195
114
|
label: 'Name',
|
|
196
115
|
width: 340,
|
|
197
116
|
minWidth: 160,
|
|
198
|
-
align: 'left',
|
|
199
117
|
kind: 'tree',
|
|
200
|
-
value: (node) => node.label ?? node.id
|
|
118
|
+
value: (node) => node.label ?? node.id,
|
|
201
119
|
},
|
|
202
120
|
{
|
|
203
121
|
id: 'status',
|
|
@@ -206,128 +124,149 @@ tree.setColumns([
|
|
|
206
124
|
minWidth: 64,
|
|
207
125
|
align: 'center',
|
|
208
126
|
kind: 'status',
|
|
209
|
-
value: (_node, state) => state.status
|
|
210
|
-
}
|
|
127
|
+
value: (_node, state) => state.status,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: 'progress',
|
|
131
|
+
label: 'Progress',
|
|
132
|
+
width: 140,
|
|
133
|
+
kind: 'progress',
|
|
134
|
+
value: (_node, state) => state.progress,
|
|
135
|
+
},
|
|
211
136
|
]);
|
|
212
137
|
```
|
|
213
138
|
|
|
214
|
-
|
|
139
|
+
Available column kinds are `tree`, `status`, `value`, `progress`, `type`,
|
|
140
|
+
`updated` and `text`. A column can also provide `render(ctx, cell)` for custom
|
|
141
|
+
Canvas2D content. Import `builtInColumns` or `defaultTreeTableColumns` to start
|
|
142
|
+
from the default set.
|
|
215
143
|
|
|
216
|
-
|
|
217
|
-
{
|
|
218
|
-
id: 'status',
|
|
219
|
-
label: 'Status',
|
|
220
|
-
width: 80,
|
|
221
|
-
minWidth: 40,
|
|
222
|
-
align: 'left' | 'center' | 'right',
|
|
223
|
-
kind: 'tree' | 'status' | 'value' | 'progress' | 'type' | 'updated' | 'text',
|
|
224
|
-
sortable: true,
|
|
225
|
-
value: (node, state) => string | number,
|
|
226
|
-
render: (ctx, cell) => {}
|
|
227
|
-
}
|
|
228
|
-
```
|
|
144
|
+
## Themes and icons
|
|
229
145
|
|
|
230
|
-
|
|
146
|
+
Three themes are included:
|
|
231
147
|
|
|
232
148
|
```js
|
|
233
|
-
import {
|
|
234
|
-
```
|
|
149
|
+
import { darkTheme, lightTheme, tacticalTheme } from 'virtual-tree-canvas';
|
|
235
150
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
Columns can be resized, reordered, and sorted through the controller API. The demo also supports header click sorting and drag-to-resize on column edges.
|
|
151
|
+
tree.setTheme(tacticalTheme);
|
|
152
|
+
```
|
|
239
153
|
|
|
240
|
-
|
|
154
|
+
Themes can map domain types to a colour and icon:
|
|
241
155
|
|
|
242
156
|
```js
|
|
243
157
|
tree.setTheme({
|
|
244
158
|
rowHeight: 28,
|
|
245
|
-
indentWidth: 18,
|
|
246
159
|
font: '12px system-ui',
|
|
247
|
-
colors: {
|
|
248
|
-
background: '#0b1020',
|
|
249
|
-
row: '#0b1020',
|
|
250
|
-
rowHover: '#111827',
|
|
251
|
-
rowSelected: '#1e3a8a',
|
|
252
|
-
rowHighlighted: '#3b0764',
|
|
253
|
-
text: '#e5e7eb',
|
|
254
|
-
textMuted: '#94a3b8',
|
|
255
|
-
guide: '#1f2937',
|
|
256
|
-
chevron: '#94a3b8',
|
|
257
|
-
focus: '#38bdf8',
|
|
258
|
-
progressTrack: '#1f2937',
|
|
259
|
-
progressFill: '#22c55e',
|
|
260
|
-
badgeText: '#ffffff'
|
|
261
|
-
},
|
|
262
160
|
types: {
|
|
263
161
|
root: { icon: 'folder', color: '#38bdf8' },
|
|
264
162
|
platform: { icon: 'aircraft', color: '#60a5fa' },
|
|
265
163
|
sensor: { icon: 'radar', color: '#34d399' },
|
|
266
164
|
warning: { icon: 'warning', color: '#facc15' },
|
|
267
|
-
error: { icon: 'error', color: '#ef4444' }
|
|
268
165
|
},
|
|
269
|
-
statuses: {
|
|
270
|
-
0: { label: 'OK', color: '#22c55e' },
|
|
271
|
-
1: { label: 'WARN', color: '#facc15' },
|
|
272
|
-
2: { label: 'ERR', color: '#ef4444' }
|
|
273
|
-
}
|
|
274
166
|
});
|
|
275
167
|
```
|
|
276
168
|
|
|
277
|
-
Built-in
|
|
169
|
+
Built-in icons are editable SVG files in [`src/assets/icons`](./src/assets/icons).
|
|
170
|
+
They are preloaded and rasterized once per icon, colour, CSS size and device
|
|
171
|
+
pixel ratio. Rendering rows then uses a cached `drawImage`, not SVG parsing or
|
|
172
|
+
path drawing.
|
|
173
|
+
|
|
174
|
+
Register application icons from an SVG URL, inline SVG or an image:
|
|
278
175
|
|
|
279
176
|
```js
|
|
280
|
-
|
|
177
|
+
tree.registerIcon('camera', '/icons/camera.svg');
|
|
178
|
+
tree.registerIcon('satellite', '<svg viewBox="0 0 24 24"><path fill="currentColor" d="..."/></svg>');
|
|
179
|
+
tree.registerIcon('logo', imageElement);
|
|
281
180
|
```
|
|
282
181
|
|
|
283
|
-
|
|
182
|
+
Use `currentColor` in an SVG to inherit the type or dynamic-state colour.
|
|
183
|
+
`tree.iconRegistry.prepare({ icons, size, color, pixelRatio })` is also
|
|
184
|
+
available when an application wants to warm a known icon set before display.
|
|
284
185
|
|
|
285
|
-
|
|
286
|
-
2. Node type rule, such as `theme.types.sensor`
|
|
287
|
-
3. Default theme color
|
|
186
|
+
## Model inspector
|
|
288
187
|
|
|
289
|
-
|
|
188
|
+
`setModel()` renders JSON-like data as an editable inspector. Metadata controls
|
|
189
|
+
the editor, bounds, choices, labels and descriptions.
|
|
290
190
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
191
|
+
```js
|
|
192
|
+
tree.setModel(
|
|
193
|
+
{
|
|
194
|
+
sensor: { enabled: true, range: 72, mode: 'track' },
|
|
195
|
+
tracks: [{ id: 'T-100', speed: 430 }],
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
'sensor.range': { min: 0, max: 120, step: 1, integer: true },
|
|
199
|
+
'sensor.mode': { options: { Search: 'search', Track: 'track' } },
|
|
200
|
+
'tracks.*.speed': { min: 0, max: 900, step: 5 },
|
|
201
|
+
'tracks.*.id': { readonly: true },
|
|
202
|
+
},
|
|
203
|
+
{ presentation: 'pane', flatRoot: true },
|
|
204
|
+
);
|
|
295
205
|
```
|
|
296
206
|
|
|
297
|
-
|
|
207
|
+
Choose `presentation: 'pane'` for a compact folder-and-controls view, or
|
|
208
|
+
`presentation: 'table'` for Property / Value / Type / Description columns.
|
|
209
|
+
Editors are inferred from data and metadata: checkbox, range, number, text,
|
|
210
|
+
select, colour, button, object and array.
|
|
211
|
+
|
|
212
|
+
Listen for user edits and actions with `valuechange`, `modelchange` and
|
|
213
|
+
`action` events.
|
|
214
|
+
|
|
215
|
+
## Events
|
|
298
216
|
|
|
299
217
|
```js
|
|
300
|
-
tree.
|
|
301
|
-
|
|
302
|
-
tree.registerIcon('custom-vector', (ctx, x, y, size, color) => {
|
|
303
|
-
ctx.fillStyle = color;
|
|
304
|
-
ctx.fillRect(x, y, size, size);
|
|
218
|
+
tree.on('nodeclick', (event) => {
|
|
219
|
+
console.log(event.detail.nodeId);
|
|
305
220
|
});
|
|
221
|
+
tree.on('selectionchange', (event) => {});
|
|
222
|
+
tree.on('expand', (event) => {});
|
|
223
|
+
tree.on('searchchange', (event) => {});
|
|
224
|
+
tree.on('filterchange', (event) => {});
|
|
225
|
+
tree.on('valuechange', (event) => {});
|
|
306
226
|
```
|
|
307
227
|
|
|
308
|
-
|
|
228
|
+
Other events include `nodehover`, `nodedblclick`, `collapse`, `focuschange`,
|
|
229
|
+
`sortchange`, `columnschange`, `viewportchange`, `modelchange` and `action`.
|
|
230
|
+
The payload is always available as `event.detail`.
|
|
309
231
|
|
|
310
|
-
##
|
|
232
|
+
## API overview
|
|
311
233
|
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
|
|
234
|
+
```js
|
|
235
|
+
tree.setData(nodes);
|
|
236
|
+
tree.setDynamicState(patches);
|
|
237
|
+
tree.setTheme(theme);
|
|
238
|
+
tree.setColumns(columns);
|
|
239
|
+
|
|
240
|
+
tree.expand(nodeId);
|
|
241
|
+
tree.collapse(nodeId);
|
|
242
|
+
tree.toggle(nodeId);
|
|
243
|
+
tree.expandAll();
|
|
244
|
+
tree.collapseAll();
|
|
245
|
+
|
|
246
|
+
tree.search(query, { caseSensitive, wholeWord });
|
|
247
|
+
tree.setFilter(queryOrPredicate, { caseSensitive, wholeWord });
|
|
248
|
+
tree.clearSearch();
|
|
249
|
+
tree.clearFilter();
|
|
315
250
|
|
|
316
|
-
|
|
251
|
+
tree.focusNode(nodeId, { align: 'start' | 'center' | 'end' | 'nearest' });
|
|
252
|
+
tree.scrollToNode(nodeId, 'center');
|
|
253
|
+
tree.scrollTo(x, y);
|
|
317
254
|
|
|
318
|
-
|
|
319
|
-
|
|
255
|
+
tree.setSelection(ids);
|
|
256
|
+
tree.getSelection();
|
|
257
|
+
tree.registerIcon(name, svgOrImage);
|
|
258
|
+
tree.enableWorkers();
|
|
259
|
+
tree.disableWorkers();
|
|
320
260
|
```
|
|
321
261
|
|
|
322
|
-
|
|
262
|
+
## Demo
|
|
323
263
|
|
|
324
|
-
```
|
|
325
|
-
|
|
264
|
+
```bash
|
|
265
|
+
npm run demo
|
|
326
266
|
```
|
|
327
267
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
Benchmark stats separate frame, patch, scene, render, search, filter, and worker timings. Use "Copy JSON" to export the current sample.
|
|
268
|
+
Open <http://localhost:4173/demo/> for the large-tree benchmark, or
|
|
269
|
+
<http://localhost:4173/demo/inspector.html> for the editable inspector demo.
|
|
331
270
|
|
|
332
271
|
## Tests
|
|
333
272
|
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "virtual-tree-canvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "High-performance Canvas2D virtual tree/table widget for very large hierarchical datasets.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"src",
|
|
23
|
+
"docs",
|
|
23
24
|
"README.md",
|
|
24
25
|
"LICENSE"
|
|
25
26
|
],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2.5l1.4 8.1 7.2 4.2-7.9-1.3L12 21.5l-.7-8-7.9 1.3 7.2-4.2z"/><path d="M9.8 18.8L6.5 21M14.2 18.8l3.3 2.2" fill="none"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2.5l1.4 8.1 7.2 4.2-7.9-1.3L12 21.5l-.7-8-7.9 1.3 7.2-4.2z"/><path d="M9.8 18.8L6.5 21M14.2 18.8l3.3 2.2" fill="none"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 4h12v13H6zM4 8h2m12 0h2M6 17l-2 3m14-3 2 3"/><path d="M8 7h3v4H8zm5 0h3v4h-3zM8 15h.01M16 15h.01"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"><path d="M4 17h16l2 3H2zM12 17l-2-8"/><circle cx="9.5" cy="7" r="2" fill="currentColor"/><circle cx="16" cy="19" r=".8" fill="currentColor"/><circle cx="19" cy="19" r=".8" fill="currentColor"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-linejoin="round"><path d="m12 2 2.5 5L21 5l-3 5 4 3-5.5 1L17 21l-5-3-5 3 .5-7L2 13l4-3-3-5 6.5 2z"/><circle cx="12" cy="12" r="2.2" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"><circle cx="12" cy="12" r="8.5"/><path d="m9 9 6 6m0-6-6 6"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 6.5A2.5 2.5 0 0 1 5.5 4H10l2.1 2H18.5A2.5 2.5 0 0 1 21 8.5v8A2.5 2.5 0 0 1 18.5 19h-13A2.5 2.5 0 0 1 3 16.5z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4 10h12l3 3v5H4zm4-3h7v3H8zM6.5 17a2 2 0 1 0 0 4 2 2 0 0 0 0-4m10 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="8" cy="12" r="4"/><circle cx="16" cy="12" r="4"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><rect x="5" y="5" width="14" height="14" rx="1"/><path d="M10 5v14m4-14v14"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="7"/><circle cx="12" cy="12" r="2.5" fill="currentColor" stroke="none"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-linejoin="round"><path d="m12 2 3 5v10l-3 5-3-5V7z"/><path d="m9 14-6 5 6-1m6-4 6 5-6-1" fill="none" stroke-width="2" stroke-linecap="round"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="3" fill="currentColor"/><circle cx="12" cy="12" r="8"/><path d="M12 2v3m0 14v3M2 12h3m14 0h3"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 12l6.5-5M12 12a7 7 0 1 0 7 7"/><path d="M12 5a7 7 0 0 1 7 7"/><circle cx="12" cy="12" r="1.5" fill="currentColor" stroke="none"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="4" opacity=".55"/><path d="M3 12h18M12 3v18m0-9 6-5"/><circle cx="16" cy="8.5" r="1" fill="currentColor" stroke="none"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"><circle cx="12" cy="12" r="3" fill="currentColor"/><path d="M3 9h5v6H3zm13 0h5v6h-5zM8 12h8M4 18c4-3 12-3 16 0"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"><path d="M3 14c2-3 16-3 18 0-2 3-16 3-18 0M10 8h4v5h-4zM9 7h6"/><path d="M4 20c2-1.5 4-1.5 6 0s4 1.5 6 0" fill="none" stroke-linecap="round"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"><path d="M3 14h14l4 3-16 2zM9 8h5v6H9zM12 4v4"/><path d="M4 21c2-1.5 4-1.5 6 0s4 1.5 6 0" fill="none" stroke-linecap="round"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="4" width="14" height="16" rx="2"/><path d="M9 4.5h6M8.5 11.5l2 2 4-4M9 16h6"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="7"/><path d="M12 3v18M3 12h18"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M10.27 3.3a2 2 0 0 1 3.46 0l8.02 13.9A2 2 0 0 1 20.02 20H3.98a2 2 0 0 1-1.73-2.8zM11 9v5h2V9zm0 7v2h2v-2z"/></svg>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Built-in SVG sources. Use registerIcon(name, urlOrSvg) to add application icons. */
|
|
2
|
+
export const builtinIconUrls = Object.freeze({
|
|
3
|
+
placeholder: new URL('./icons/placeholder.svg', import.meta.url).href,
|
|
4
|
+
folder: new URL('./icons/folder.svg', import.meta.url).href,
|
|
5
|
+
aircraft: new URL('./icons/aircraft.svg', import.meta.url).href,
|
|
6
|
+
radar: new URL('./icons/radar.svg', import.meta.url).href,
|
|
7
|
+
warning: new URL('./icons/warning.svg', import.meta.url).href,
|
|
8
|
+
error: new URL('./icons/error.svg', import.meta.url).href,
|
|
9
|
+
task: new URL('./icons/task.svg', import.meta.url).href,
|
|
10
|
+
bus: new URL('./icons/bus.svg', import.meta.url).href,
|
|
11
|
+
track: new URL('./icons/track.svg', import.meta.url).href,
|
|
12
|
+
point: new URL('./icons/point.svg', import.meta.url).href,
|
|
13
|
+
munition: new URL('./icons/munition.svg', import.meta.url).href,
|
|
14
|
+
air: new URL('./icons/air.svg', import.meta.url).href,
|
|
15
|
+
ground: new URL('./icons/ground.svg', import.meta.url).href,
|
|
16
|
+
surface: new URL('./icons/surface.svg', import.meta.url).href,
|
|
17
|
+
subsurface: new URL('./icons/subsurface.svg', import.meta.url).href,
|
|
18
|
+
space: new URL('./icons/space.svg', import.meta.url).href,
|
|
19
|
+
control: new URL('./icons/control.svg', import.meta.url).href,
|
|
20
|
+
situation: new URL('./icons/situation.svg', import.meta.url).href,
|
|
21
|
+
damage: new URL('./icons/damage.svg', import.meta.url).href,
|
|
22
|
+
'inspector-object': new URL('./icons/inspector-object.svg', import.meta.url).href,
|
|
23
|
+
'inspector-array': new URL('./icons/inspector-array.svg', import.meta.url).href,
|
|
24
|
+
'inspector-value': new URL('./icons/inspector-value.svg', import.meta.url).href,
|
|
25
|
+
});
|