virtual-tree-canvas 0.7.0 → 0.7.2
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 +15 -0
- package/README.md +12 -1
- package/docs/icon-catalog.html +68 -66
- package/docs/icon-catalog.md +111 -111
- package/docs/performance/dynamic-updates-0.7.1.md +38 -0
- package/package.json +3 -2
- package/src/assets/icons.js +10 -4
- package/src/core/dynamic-state.js +19 -29
- package/src/core/patch-batcher.js +4 -0
- package/src/core/theme-manager.js +238 -64
- package/src/core/tree-cell-layout.js +4 -2
- package/src/core/tree-column-model.js +72 -24
- package/src/core/tree-model.js +4 -2
- package/src/core/view-options.js +98 -25
- package/src/core/visible-row-model.js +50 -20
- package/src/index.d.ts +22 -0
- package/src/input/row-actions.js +224 -83
- package/src/input/row-reorder-input.js +112 -39
- package/src/input/tree-tooltip.js +39 -5
- package/src/input/tree-view-input-controller.js +33 -10
- package/src/inspector/cell-editor-manager.js +110 -31
- package/src/inspector/pane-layout.js +11 -3
- package/src/renderers/checkbox.js +12 -13
- package/src/renderers/tree-row-renderer.js +325 -68
- package/src/tree-view-controller.js +919 -213
- package/src/tree-view.js +215 -53
- package/src/view/styles.js +83 -12
package/docs/icon-catalog.md
CHANGED
|
@@ -1,171 +1,171 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Icon catalog
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The 65 bundled icons are available in `resources/icons/`. Open the
|
|
4
|
+
[rendered visual catalog](https://htmlpreview.github.io/?https://github.com/mploscos/virtual-tree-canvas/blob/main/docs/icon-catalog.html)
|
|
5
|
+
to compare them at different sizes on light and dark backgrounds. The
|
|
6
|
+
[HTML source](./icon-catalog.html) is also available. `builtinIconNames` lists
|
|
7
|
+
their IDs from JavaScript.
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## Usage
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Assign an ID to the node's `icon` property:
|
|
10
12
|
|
|
11
13
|
```js
|
|
12
|
-
view.setData([{ id: 'operator-1', label: '
|
|
14
|
+
view.setData([{ id: 'operator-1', label: 'Operator', icon: 'person' }]);
|
|
13
15
|
```
|
|
14
16
|
|
|
15
|
-
- `data-bus`
|
|
16
|
-
- `placeholder`
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
17
|
+
- `data-bus` represents a data bus; `bus-vehicle` represents a passenger bus.
|
|
18
|
+
- `placeholder` is used when no specific icon is available.
|
|
19
|
+
- Pair icons with labels. For states such as warnings or errors, also provide
|
|
20
|
+
text instead of relying on color alone.
|
|
21
|
+
- To add a custom icon, call `view.registerIcon('custom', svgStringOrUrl)` and
|
|
22
|
+
assign `icon: 'custom'` to the node.
|
|
23
|
+
- Set `iconsBaseUrl` when creating the view if assets are served from another URL.
|
|
24
|
+
SVGs are loaded when drawn or through `view.controller.iconRegistry.prepare()`.
|
|
23
25
|
|
|
24
|
-
##
|
|
26
|
+
## Available icons
|
|
25
27
|
|
|
26
|
-
###
|
|
28
|
+
### People and organization
|
|
27
29
|
|
|
28
|
-
| ID |
|
|
30
|
+
| ID | Meaning |
|
|
29
31
|
| --- | --- |
|
|
30
|
-
| `person` |
|
|
31
|
-
| `people` |
|
|
32
|
-
| `child` |
|
|
32
|
+
| `person` | Person |
|
|
33
|
+
| `people` | Group |
|
|
34
|
+
| `child` | Child |
|
|
33
35
|
|
|
34
|
-
###
|
|
36
|
+
### Animals
|
|
35
37
|
|
|
36
|
-
| ID |
|
|
38
|
+
| ID | Meaning |
|
|
37
39
|
| --- | --- |
|
|
38
|
-
| `dog` |
|
|
39
|
-
| `cat` |
|
|
40
|
-
| `bird` |
|
|
41
|
-
| `fish` |
|
|
42
|
-
| `insect` |
|
|
40
|
+
| `dog` | Dog |
|
|
41
|
+
| `cat` | Cat |
|
|
42
|
+
| `bird` | Bird |
|
|
43
|
+
| `fish` | Fish |
|
|
44
|
+
| `insect` | Insect |
|
|
43
45
|
|
|
44
|
-
###
|
|
46
|
+
### Plants and ecosystems
|
|
45
47
|
|
|
46
|
-
| ID |
|
|
48
|
+
| ID | Meaning |
|
|
47
49
|
| --- | --- |
|
|
48
|
-
| `tree` |
|
|
49
|
-
| `leaf` |
|
|
50
|
-
| `flower` |
|
|
51
|
-
| `seedling` |
|
|
50
|
+
| `tree` | Tree |
|
|
51
|
+
| `leaf` | Leaf |
|
|
52
|
+
| `flower` | Flower |
|
|
53
|
+
| `seedling` | Seedling |
|
|
52
54
|
|
|
53
|
-
###
|
|
55
|
+
### Weather
|
|
54
56
|
|
|
55
|
-
| ID |
|
|
57
|
+
| ID | Meaning |
|
|
56
58
|
| --- | --- |
|
|
57
|
-
| `sun` |
|
|
58
|
-
| `moon` |
|
|
59
|
-
| `cloud` |
|
|
60
|
-
| `rain` |
|
|
61
|
-
| `snow` |
|
|
62
|
-
| `wind` |
|
|
63
|
-
| `storm` |
|
|
64
|
-
| `fog` |
|
|
59
|
+
| `sun` | Sun |
|
|
60
|
+
| `moon` | Moon |
|
|
61
|
+
| `cloud` | Cloud |
|
|
62
|
+
| `rain` | Rain |
|
|
63
|
+
| `snow` | Snow |
|
|
64
|
+
| `wind` | Wind |
|
|
65
|
+
| `storm` | Storm |
|
|
66
|
+
| `fog` | Fog |
|
|
65
67
|
|
|
66
|
-
###
|
|
68
|
+
### Terrain and water
|
|
67
69
|
|
|
68
|
-
| ID |
|
|
70
|
+
| ID | Meaning |
|
|
69
71
|
| --- | --- |
|
|
70
|
-
| `ground` |
|
|
71
|
-
| `surface` |
|
|
72
|
-
| `subsurface` |
|
|
72
|
+
| `ground` | Ground domain |
|
|
73
|
+
| `surface` | Surface domain |
|
|
74
|
+
| `subsurface` | Subsurface domain |
|
|
73
75
|
|
|
74
|
-
###
|
|
76
|
+
### Ground transportation
|
|
75
77
|
|
|
76
|
-
| ID |
|
|
78
|
+
| ID | Meaning |
|
|
77
79
|
| --- | --- |
|
|
78
|
-
| `car` |
|
|
79
|
-
| `bicycle` |
|
|
80
|
-
| `bus-vehicle` |
|
|
80
|
+
| `car` | Car |
|
|
81
|
+
| `bicycle` | Bicycle |
|
|
82
|
+
| `bus-vehicle` | Passenger bus |
|
|
81
83
|
|
|
82
|
-
###
|
|
84
|
+
### Aviation and space
|
|
83
85
|
|
|
84
|
-
| ID |
|
|
86
|
+
| ID | Meaning |
|
|
85
87
|
| --- | --- |
|
|
86
|
-
| `aircraft` |
|
|
87
|
-
| `helicopter` |
|
|
88
|
-
| `drone` |
|
|
89
|
-
| `space` |
|
|
88
|
+
| `aircraft` | Aircraft |
|
|
89
|
+
| `helicopter` | Helicopter |
|
|
90
|
+
| `drone` | Drone |
|
|
91
|
+
| `space` | Space domain |
|
|
90
92
|
|
|
91
|
-
###
|
|
93
|
+
### Maritime environment
|
|
92
94
|
|
|
93
|
-
| ID |
|
|
95
|
+
| ID | Meaning |
|
|
94
96
|
| --- | --- |
|
|
95
|
-
| `ship` |
|
|
97
|
+
| `ship` | Ship |
|
|
96
98
|
|
|
97
|
-
###
|
|
99
|
+
### Objects and facilities
|
|
98
100
|
|
|
99
|
-
| ID |
|
|
101
|
+
| ID | Meaning |
|
|
100
102
|
| --- | --- |
|
|
101
|
-
| `box` |
|
|
102
|
-
| `building` |
|
|
103
|
+
| `box` | Box |
|
|
104
|
+
| `building` | Building |
|
|
103
105
|
|
|
104
|
-
###
|
|
106
|
+
### Data and networks
|
|
105
107
|
|
|
106
|
-
| ID |
|
|
108
|
+
| ID | Meaning |
|
|
107
109
|
| --- | --- |
|
|
108
|
-
| `data-bus` |
|
|
109
|
-
| `network` |
|
|
110
|
-
| `server` |
|
|
111
|
-
| `database` |
|
|
112
|
-
| `link` |
|
|
110
|
+
| `data-bus` | Data bus |
|
|
111
|
+
| `network` | Network |
|
|
112
|
+
| `server` | Server |
|
|
113
|
+
| `database` | Database |
|
|
114
|
+
| `link` | Link |
|
|
113
115
|
|
|
114
|
-
###
|
|
116
|
+
### Sensors and measurements
|
|
115
117
|
|
|
116
|
-
| ID |
|
|
118
|
+
| ID | Meaning |
|
|
117
119
|
| --- | --- |
|
|
118
|
-
| `sensor` |
|
|
120
|
+
| `sensor` | Generic sensor |
|
|
119
121
|
| `radar` | Radar |
|
|
120
|
-
| `thermometer` |
|
|
122
|
+
| `thermometer` | Thermometer |
|
|
121
123
|
|
|
122
|
-
###
|
|
124
|
+
### Geography and navigation
|
|
123
125
|
|
|
124
|
-
| ID |
|
|
126
|
+
| ID | Meaning |
|
|
125
127
|
| --- | --- |
|
|
126
|
-
| `globe` |
|
|
127
|
-
| `point` |
|
|
128
|
-
| `track` |
|
|
128
|
+
| `globe` | Globe |
|
|
129
|
+
| `point` | Point |
|
|
130
|
+
| `track` | Track |
|
|
129
131
|
|
|
130
|
-
### Inspector
|
|
132
|
+
### Inspector and documents
|
|
131
133
|
|
|
132
|
-
| ID |
|
|
134
|
+
| ID | Meaning |
|
|
133
135
|
| --- | --- |
|
|
134
|
-
| `folder` |
|
|
135
|
-
| `inspector-object` |
|
|
136
|
+
| `folder` | Folder |
|
|
137
|
+
| `inspector-object` | Data object |
|
|
136
138
|
| `inspector-array` | Array |
|
|
137
|
-
| `inspector-value` |
|
|
139
|
+
| `inspector-value` | Scalar value |
|
|
138
140
|
|
|
139
|
-
###
|
|
141
|
+
### States and alerts
|
|
140
142
|
|
|
141
|
-
| ID |
|
|
143
|
+
| ID | Meaning |
|
|
142
144
|
| --- | --- |
|
|
143
|
-
| `warning` |
|
|
145
|
+
| `warning` | Warning |
|
|
144
146
|
| `error` | Error |
|
|
145
|
-
| `placeholder` |
|
|
146
|
-
| `damage` |
|
|
147
|
-
| `situation` |
|
|
148
|
-
| `lock` |
|
|
147
|
+
| `placeholder` | Unknown type |
|
|
148
|
+
| `damage` | Damage |
|
|
149
|
+
| `situation` | Situation |
|
|
150
|
+
| `lock` | Locked |
|
|
149
151
|
|
|
150
|
-
###
|
|
152
|
+
### Actions and controls
|
|
151
153
|
|
|
152
|
-
| ID |
|
|
154
|
+
| ID | Meaning |
|
|
153
155
|
| --- | --- |
|
|
154
156
|
| `control` | Control |
|
|
155
|
-
| `task` |
|
|
156
|
-
| `star` |
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
157
|
+
| `task` | Task |
|
|
158
|
+
| `star` | Favorite |
|
|
159
|
+
| `star-filled` | Active favorite |
|
|
160
|
+
| `pin` | Pinned |
|
|
161
|
+
| `heart` | Favorite |
|
|
162
|
+
| `grip` | Drag handle |
|
|
163
|
+
| `trash` | Delete or remove |
|
|
160
164
|
|
|
161
|
-
###
|
|
165
|
+
### Time and simulation
|
|
162
166
|
|
|
163
|
-
| ID |
|
|
167
|
+
| ID | Meaning |
|
|
164
168
|
| --- | --- |
|
|
165
|
-
| `clock` |
|
|
166
|
-
| `calendar` |
|
|
167
|
-
| `munition` |
|
|
168
|
-
|
|
169
|
-
- `trash`: eliminar un elemento o quitarlo de una lista.
|
|
170
|
-
|
|
171
|
-
- `star-filled`: favorito activo; combina con `star` para el estado inactivo.
|
|
169
|
+
| `clock` | Clock |
|
|
170
|
+
| `calendar` | Calendar |
|
|
171
|
+
| `munition` | Munition |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Dynamic update benchmark: 0.7.0 → 0.7.1
|
|
2
|
+
|
|
3
|
+
Measured on 2026-09-13 in headless Chromium on the development host. Each case
|
|
4
|
+
uses 10,000 flat nodes, approximately 40 visible rows, 60 update ticks and 500
|
|
5
|
+
patches per tick (30,000 patches/case). Times are milliseconds. The reproducible
|
|
6
|
+
command is `npm run benchmark:dynamic`.
|
|
7
|
+
|
|
8
|
+
The 0.7.0 measurement used an extracted clean `HEAD` and the same benchmark file.
|
|
9
|
+
Icon-load invalidations and initial ResizeObserver work were settled before each
|
|
10
|
+
sample. “Total avg” is patch application plus render time per tick; a zero-render
|
|
11
|
+
case therefore contains only patch processing.
|
|
12
|
+
|
|
13
|
+
| Sorted | RowActions | Visible patches | Patch avg/p95 before → after | Render avg/p95 before → after | Renders before → after | Total avg before → after |
|
|
14
|
+
|---|---:|---:|---:|---:|---:|---:|
|
|
15
|
+
| no | no | 0% | 0.417/0.700 → 0.788/1.200 | 1.573/2.000 → 0/0 | 60 → 0 | 1.990 → 0.788 |
|
|
16
|
+
| no | no | 10% | 0.413/0.600 → 0.657/0.900 | 1.422/2.100 → 1.485/2.000 | 60 → 60 | 1.835 → 2.142 |
|
|
17
|
+
| no | no | 100% | 0.285/0.400 → 0.212/0.400 | 1.335/2.000 → 1.335/1.900 | 60 → 60 | 1.620 → 1.547 |
|
|
18
|
+
| no | yes | 0% | 0.303/0.500 → 0.635/0.900 | 2.408/3.600 → 0/0 | 60 → 0 | 2.712 → 0.635 |
|
|
19
|
+
| no | yes | 10% | 0.318/0.500 → 0.592/0.800 | 2.708/3.400 → 2.960/3.900 | 60 → 60 | 3.027 → 3.552 |
|
|
20
|
+
| no | yes | 100% | 0.203/0.300 → 0.153/0.300 | 2.535/3.600 → 2.552/3.700 | 60 → 60 | 2.738 → 2.705 |
|
|
21
|
+
| yes | no | 0% | 0.317/0.400 → 0.548/0.800 | 2.012/3.400 → 0/0 | 60 → 0 | 2.328 → 0.548 |
|
|
22
|
+
| yes | no | 10% | 0.345/0.500 → 0.582/0.800 | 1.945/2.600 → 1.343/2.300 | 60 → 60 | 2.290 → 1.925 |
|
|
23
|
+
| yes | no | 100% | 0.267/0.500 → 0.220/0.400 | 2.005/2.600 → 1.305/2.300 | 60 → 60 | 2.272 → 1.525 |
|
|
24
|
+
| yes | yes | 0% | 0.287/0.400 → 0.493/0.700 | 3.087/3.900 → 0/0 | 60 → 0 | 3.373 → 0.493 |
|
|
25
|
+
| yes | yes | 10% | 0.307/0.500 → 0.478/0.800 | 3.402/5.200 → 2.782/4.100 | 60 → 60 | 3.708 → 3.260 |
|
|
26
|
+
| yes | yes | 100% | 0.182/0.300 → 0.137/0.300 | 3.103/4.800 → 2.447/3.800 | 60 → 60 | 3.285 → 2.583 |
|
|
27
|
+
|
|
28
|
+
In every 0%-visible case, 0.7.1 recorded 60 renders avoided because the changes
|
|
29
|
+
were offscreen, with zero render requests and executions. In visible cases,
|
|
30
|
+
0.7.0 performed 60 full RowActions synchronizations; 0.7.1 performed 60
|
|
31
|
+
incremental and zero full synchronizations. At 100% visible, repeated patches for
|
|
32
|
+
the approximately 40 visible IDs coalesced 30,000 received patches into 2,400
|
|
33
|
+
unique changed nodes across the 60 frames.
|
|
34
|
+
|
|
35
|
+
The queue adds measurable bookkeeping when nearly all 500 IDs are unique and a
|
|
36
|
+
render is required anyway; this is visible in the two unsorted 10% cases. The
|
|
37
|
+
largest gains are the intended real-time-tree case (offscreen traffic), and active
|
|
38
|
+
sorting additionally benefits from removing the 10,000-entry scene copy.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "virtual-tree-canvas",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
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",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"scripts": {
|
|
35
35
|
"demo": "python3 -m http.server 4173 --bind localhost",
|
|
36
36
|
"test": "node --test test/*.test.js",
|
|
37
|
-
"test:browser": "node test/run-browser.mjs"
|
|
37
|
+
"test:browser": "node test/run-browser.mjs",
|
|
38
|
+
"benchmark:dynamic": "node scripts/diagnostics/profile-dynamic-updates.mjs"
|
|
38
39
|
},
|
|
39
40
|
"keywords": [
|
|
40
41
|
"tree",
|
package/src/assets/icons.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Resolve built-in SVG resources independently of the bundled JavaScript location.
|
|
2
3
|
* @param {string | URL} [iconsBaseUrl] Directory URL, with or without a trailing slash.
|
|
3
4
|
*/
|
|
4
5
|
export function createBuiltinIconUrls(iconsBaseUrl) {
|
|
5
|
-
const base = String(iconsBaseUrl ?? new URL('../../resources/icons/', import.meta.url)).replace(
|
|
6
|
-
|
|
6
|
+
const base = String(iconsBaseUrl ?? new URL('../../resources/icons/', import.meta.url)).replace(
|
|
7
|
+
/\/+$/,
|
|
8
|
+
''
|
|
9
|
+
);
|
|
10
|
+
return Object.freeze(
|
|
11
|
+
Object.fromEntries(builtinIconNames.map((name) => [name, `${base}/${name}.svg`]))
|
|
12
|
+
);
|
|
7
13
|
}
|
|
8
14
|
|
|
9
15
|
export const builtinIconNames = Object.freeze([
|
|
@@ -71,5 +77,5 @@ export const builtinIconNames = Object.freeze([
|
|
|
71
77
|
'track',
|
|
72
78
|
'tree',
|
|
73
79
|
'warning',
|
|
74
|
-
'wind'
|
|
80
|
+
'wind'
|
|
75
81
|
]);
|
|
@@ -1,44 +1,34 @@
|
|
|
1
1
|
const hasOwn = Object.prototype.hasOwnProperty;
|
|
2
2
|
|
|
3
|
-
const knownDynamicStateKeys = new Set([
|
|
4
|
-
'value',
|
|
5
|
-
'status',
|
|
6
|
-
'progress',
|
|
7
|
-
'pulse',
|
|
8
|
-
'color',
|
|
9
|
-
'selected',
|
|
10
|
-
'highlighted',
|
|
11
|
-
'visible',
|
|
12
|
-
'updated',
|
|
13
|
-
'updatedAt',
|
|
14
|
-
'icon',
|
|
15
|
-
]);
|
|
16
|
-
|
|
17
3
|
/**
|
|
18
4
|
* @param {import('./types.js').NodeDynamicState} target
|
|
19
5
|
* @param {import('./types.js').NodeDynamicState} state
|
|
20
6
|
*/
|
|
21
7
|
export function mergeDynamicState(target, state) {
|
|
22
|
-
|
|
8
|
+
mergeDynamicStateChanged(target, state);
|
|
9
|
+
return target;
|
|
10
|
+
}
|
|
23
11
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (
|
|
12
|
+
/**
|
|
13
|
+
* Merge a dynamic state patch and report whether at least one own property changed.
|
|
14
|
+
* Values are compared with Object.is so repeated NaN values and signed zero are
|
|
15
|
+
* handled consistently.
|
|
16
|
+
*
|
|
17
|
+
* @param {import('./types.js').NodeDynamicState} target
|
|
18
|
+
* @param {import('./types.js').NodeDynamicState} state
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
export function mergeDynamicStateChanged(target, state) {
|
|
22
|
+
if (!state) return false;
|
|
23
|
+
let changed = false;
|
|
35
24
|
|
|
36
25
|
for (const key in state) {
|
|
37
|
-
if (!hasOwn.call(state, key) ||
|
|
38
|
-
|
|
26
|
+
if (!hasOwn.call(state, key) || Object.is(target[key], state[key])) continue;
|
|
27
|
+
target[key] = state[key];
|
|
28
|
+
changed = true;
|
|
39
29
|
}
|
|
40
30
|
|
|
41
|
-
return
|
|
31
|
+
return changed;
|
|
42
32
|
}
|
|
43
33
|
|
|
44
34
|
/** @param {import('./types.js').NodeDynamicState} state */
|