virtual-tree-canvas 0.4.0 → 0.6.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/CHANGELOG.md +42 -0
- package/README.md +115 -28
- package/docs/icon-catalog.html +67 -0
- package/docs/icon-catalog.json +380 -0
- package/docs/icon-catalog.md +167 -0
- package/examples/generate-tree.js +86 -0
- package/examples/reorder-table.html +33 -0
- package/examples/units.html +43 -0
- package/package.json +7 -3
- package/resources/icons/bicycle.svg +1 -0
- package/resources/icons/bird.svg +1 -0
- package/resources/icons/box.svg +1 -0
- package/resources/icons/building.svg +1 -0
- package/resources/icons/calendar.svg +1 -0
- package/resources/icons/car.svg +1 -0
- package/resources/icons/cat.svg +1 -0
- package/resources/icons/child.svg +1 -0
- package/resources/icons/clock.svg +1 -0
- package/resources/icons/cloud.svg +1 -0
- package/resources/icons/data-bus.svg +1 -0
- package/resources/icons/database.svg +1 -0
- package/resources/icons/dog.svg +1 -0
- package/resources/icons/drone.svg +1 -0
- package/resources/icons/fish.svg +1 -0
- package/resources/icons/flower.svg +1 -0
- package/resources/icons/fog.svg +1 -0
- package/resources/icons/globe.svg +1 -0
- package/resources/icons/grip.svg +1 -0
- package/resources/icons/heart.svg +1 -0
- package/resources/icons/helicopter.svg +1 -0
- package/resources/icons/insect.svg +1 -0
- package/resources/icons/leaf.svg +1 -0
- package/resources/icons/link.svg +1 -0
- package/resources/icons/lock.svg +1 -0
- package/resources/icons/moon.svg +1 -0
- package/resources/icons/network.svg +1 -0
- package/resources/icons/people.svg +1 -0
- package/resources/icons/person.svg +1 -0
- package/resources/icons/pin.svg +1 -0
- package/resources/icons/radar.svg +1 -0
- package/resources/icons/rain.svg +1 -0
- package/resources/icons/seedling.svg +1 -0
- package/resources/icons/sensor.svg +1 -0
- package/resources/icons/server.svg +1 -0
- package/resources/icons/ship.svg +1 -0
- package/resources/icons/snow.svg +1 -0
- package/resources/icons/star.svg +1 -0
- package/resources/icons/storm.svg +1 -0
- package/resources/icons/sun.svg +1 -0
- package/resources/icons/thermometer.svg +1 -0
- package/resources/icons/tree.svg +1 -0
- package/resources/icons/wind.svg +1 -0
- package/src/assets/icons.js +73 -25
- package/src/core/icon-registry.js +6 -8
- package/src/core/theme-manager.js +15 -4
- package/src/core/tree-column-model.js +16 -3
- package/src/core/tree-model.js +20 -0
- package/src/core/view-options.js +30 -0
- package/src/index.d.ts +166 -5
- package/src/index.js +3 -0
- package/src/input/index.js +1 -0
- package/src/input/row-reorder-input.js +143 -0
- package/src/input/tree-filter-bar.js +55 -0
- package/src/input/tree-tooltip.js +55 -0
- package/src/input/tree-view-input-controller.js +5 -0
- package/src/inspector/cell-editor-manager.js +42 -22
- package/src/inspector/editor-resolver.js +6 -1
- package/src/inspector/numeric-layout.js +16 -0
- package/src/renderers/tree-row-renderer.js +79 -13
- package/src/tree-view-controller.js +261 -34
- package/src/tree-view.js +181 -0
- package/src/view/styles.js +14 -0
- package/src/assets/icons/air.svg +0 -1
- package/src/assets/icons/radar.svg +0 -1
- /package/{src/assets → resources}/icons/aircraft.svg +0 -0
- /package/{src/assets/icons/bus.svg → resources/icons/bus-vehicle.svg} +0 -0
- /package/{src/assets → resources}/icons/control.svg +0 -0
- /package/{src/assets → resources}/icons/damage.svg +0 -0
- /package/{src/assets → resources}/icons/error.svg +0 -0
- /package/{src/assets → resources}/icons/folder.svg +0 -0
- /package/{src/assets → resources}/icons/ground.svg +0 -0
- /package/{src/assets → resources}/icons/inspector-array.svg +0 -0
- /package/{src/assets → resources}/icons/inspector-object.svg +0 -0
- /package/{src/assets → resources}/icons/inspector-value.svg +0 -0
- /package/{src/assets → resources}/icons/munition.svg +0 -0
- /package/{src/assets → resources}/icons/placeholder.svg +0 -0
- /package/{src/assets → resources}/icons/point.svg +0 -0
- /package/{src/assets → resources}/icons/situation.svg +0 -0
- /package/{src/assets → resources}/icons/space.svg +0 -0
- /package/{src/assets → resources}/icons/subsurface.svg +0 -0
- /package/{src/assets → resources}/icons/surface.svg +0 -0
- /package/{src/assets → resources}/icons/task.svg +0 -0
- /package/{src/assets → resources}/icons/track.svg +0 -0
- /package/{src/assets → resources}/icons/warning.svg +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en"><meta charset="utf-8"><title>Numeric units — virtual-tree-canvas</title>
|
|
3
|
+
<style>
|
|
4
|
+
body{margin:24px;background:#0a0f1c;color:#e8edf4;font:14px system-ui}h1{font-size:21px}p{color:#8ea0b5}.host{position:relative;height:230px;border:1px solid #2b3b52;border-radius:8px;overflow:hidden;margin:16px 0;max-width:1050px}canvas{display:block;width:100%;height:100%}#result{font-family:monospace}
|
|
5
|
+
</style>
|
|
6
|
+
<h1>Values with units</h1><p>Units remain visible while editing. Display precision never changes the stored numeric value.</p>
|
|
7
|
+
<div id="pane" class="host"></div><div id="table" class="host"></div><div id="result"></div>
|
|
8
|
+
<script type="module">
|
|
9
|
+
import { TreeView } from '../src/index.js';
|
|
10
|
+
const controllers=[], views=[];
|
|
11
|
+
for (const presentation of ['pane','table']) {
|
|
12
|
+
const host=document.getElementById(presentation);
|
|
13
|
+
const view=new TreeView(host,{mode:'inspector',presentation,filter:false,initialExpandDepth:3});
|
|
14
|
+
const c=view.controller;
|
|
15
|
+
view.setModel({speed:125.45678,altitude:1250.25,acceleration:-9.80665,temperature:21.125,tiny:0.00001234,legacy:12.34567},{
|
|
16
|
+
speed:{unit:'m/s',precision:2,min:0,max:300},altitude:{unit:'m',precision:1},acceleration:{unit:'m/s²',precision:3,readonly:true},temperature:{unit:'°C',precision:2},tiny:{unit:'rad',precision:8},legacy:{}
|
|
17
|
+
});controllers.push(c);views.push(view);
|
|
18
|
+
}
|
|
19
|
+
window.unitControllers=controllers;
|
|
20
|
+
window.addEventListener('pagehide',()=>views.forEach(view=>view.destroy()));
|
|
21
|
+
// Small browser integration check, activated explicitly for automated validation.
|
|
22
|
+
if (new URLSearchParams(location.search).has('test')) {
|
|
23
|
+
const assert=(v,m)=>{if(!v)throw Error(m)};
|
|
24
|
+
try {
|
|
25
|
+
for(const c of controllers){
|
|
26
|
+
const col=c.columnModel.columns.find(x=>x.kind==='inspectorPane'||x.kind==='inspectorValue');
|
|
27
|
+
const box=c.canvas.getBoundingClientRect();
|
|
28
|
+
const hit=c.hitTest(col.x+col.width-60,42);
|
|
29
|
+
assert(hit.part==='number','range numeric hit');
|
|
30
|
+
assert(c.cellEditor.handlePointerDown({},hit),'open numeric editor');
|
|
31
|
+
const input=c.cellEditor.overlay;
|
|
32
|
+
assert(input.value==='125.45678','editor retains full precision');
|
|
33
|
+
assert(input.getAttribute('aria-label').includes('m/s'),'accessible unit');
|
|
34
|
+
assert(input.getBoundingClientRect().right < box.left+col.x+col.width-30,'unit remains outside editor');
|
|
35
|
+
input.value='127.123456';input.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter'}));
|
|
36
|
+
assert(c.model.index.getNode('model:speed').data.value===127.123456,'numeric edit');
|
|
37
|
+
assert(c.model.index.getNode('model:speed').data.valueText==='127.12','display precision after edit');
|
|
38
|
+
c.render();
|
|
39
|
+
}
|
|
40
|
+
document.getElementById('result').textContent='PASS: pane/table editing, precision, units and geometry';
|
|
41
|
+
} catch(e){document.getElementById('result').textContent='FAIL: '+e.stack}
|
|
42
|
+
}
|
|
43
|
+
</script></html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "virtual-tree-canvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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,9 +20,12 @@
|
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"src",
|
|
23
|
+
"resources",
|
|
23
24
|
"docs",
|
|
24
25
|
"README.md",
|
|
25
|
-
"LICENSE"
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"examples",
|
|
28
|
+
"CHANGELOG.md"
|
|
26
29
|
],
|
|
27
30
|
"private": false,
|
|
28
31
|
"publishConfig": {
|
|
@@ -30,7 +33,8 @@
|
|
|
30
33
|
},
|
|
31
34
|
"scripts": {
|
|
32
35
|
"demo": "python3 -m http.server 4173 --bind localhost",
|
|
33
|
-
"test": "node --test test
|
|
36
|
+
"test": "node --test test/*.test.js",
|
|
37
|
+
"test:browser": "node test/run-browser.mjs"
|
|
34
38
|
},
|
|
35
39
|
"keywords": [
|
|
36
40
|
"tree",
|
|
@@ -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" stroke-linejoin="round"><circle cx="5" cy="16" r="4"/><circle cx="19" cy="16" r="4"/><path d="m5 16 5-9 5 9H5m10 0 3-12h-4M8 7h5m5-3h3"/></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" stroke-linejoin="round"><path d="M3 13q6 0 8-7 3-4 6 0l4 2-4 2q0 9-11 9l-3-6Zm5 2 5-4M10 19v3m4-4v4"/><circle cx="15" cy="6" r=".6" fill="currentColor"/></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" stroke-linejoin="round"><path d="m3 7 9-5 9 5v10l-9 5-9-5Zm0 0 9 5 9-5m-9 5v10M8 4l9 5"/></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" stroke-linejoin="round"><path d="M4 22V3h16v19M2 22h20M9 22v-5h6v5M8 7h1m6 0h1M8 12h1m6 0h1"/></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" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M3 10h18M8 2v5m8-5v5M7 14h2m3 0h2m3 0h1M7 18h2m3 0h2"/></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" stroke-linejoin="round"><path d="m4 10 2-6h12l2 6M3 10h18v8H3Zm2 8v3m14-3v3M6 14h2m8 0h2"/></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" stroke-linejoin="round"><path d="m5 9-1-6 6 4h4l6-4-1 6a7 7 0 1 1-14 0ZM2 13h5m10 0h5M9 11h.01M15 11h.01m-4 4 1 1 1-1"/></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" stroke-linejoin="round"><circle cx="12" cy="5" r="3"/><path d="M12 8v8m-6-5 6 2 6-2m-6 5-4 5m4-5 4 5"/></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" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 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="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 18a5 5 0 1 1 1-10 6 6 0 0 1 11 1 4.5 4.5 0 0 1 0 9Z"/></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" stroke-linejoin="round"><path d="M3 12h18M6 8v4m6 0v4m6-8v4"/><rect x="3" y="3" width="6" height="5" rx="1"/><rect x="15" y="3" width="6" height="5" rx="1"/><rect x="9" y="16" width="6" height="5" rx="1"/></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" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 4 18 4 18 0V5M3 12c0 4 18 4 18 0"/></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" stroke-linejoin="round"><path d="M8 6q4-3 8 0l2 10q-6 8-12 0ZM8 6 4 5 2 13l4 3M16 6l4-1 2 8-4 3M9 11h.01M15 11h.01m-4 4 1 1 1-1"/></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" stroke-linejoin="round"><rect x="9" y="9" width="6" height="6" rx="2"/><path d="m9 9-4-4m10 4 4-4M9 15l-4 4m10-4 4 4"/><circle cx="5" cy="5" r="3"/><circle cx="19" cy="5" r="3"/><circle cx="5" cy="19" r="3"/><circle cx="19" cy="19" r="3"/></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" stroke-linejoin="round"><path d="M3 12q7-12 15-2l4-5v14l-4-5Q10 24 3 12Zm10-7v14"/><circle cx="8" cy="11" r=".7" fill="currentColor"/></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" stroke-linejoin="round"><path d="M12 14v8m0-4q-7 1-7-4 5-1 7 4m0 2q7 0 7-4-5-1-7 4M9 6C5 0 1 7 7 9c-5 5 3 9 5 3 3 6 10 1 5-3 6-2 1-9-3-3 1-7-7-7-5 0Z"/><circle cx="12" cy="8" r="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="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 11a3.5 3.5 0 0 1-.6-6.95A5 5 0 0 1 15 5h2a3 3 0 0 1 0 6H6Z"/><path d="M4 15h16M2 19h7m4 0h9"/></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" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><ellipse cx="12" cy="12" rx="4" ry="10"/><path d="M2 12h20"/></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" stroke-linejoin="round"><circle cx="9" cy="5" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="19" r="1"/></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" stroke-linejoin="round"><path d="M12 21 3 12C-2 5 7-1 12 6c5-7 14-1 9 6Z"/></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" stroke-linejoin="round"><path d="M6 17V9h8q6 0 6 7l-5 2H6L2 8m4 5H2m9-4V4M3 4h17M7 18v3m9-3v3M4 21h16M14 9v5h5"/></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" stroke-linejoin="round"><ellipse cx="12" cy="14" rx="5" ry="7"/><path d="M9 7V5l-2-2m8 4V5l2-2M7 11 3 8m4 7H2m5 3-4 3m14-10 4-3m-4 7h5m-5 3 4 3M12 7v14"/></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" stroke-linejoin="round"><path d="M20 3Q3 1 3 13q0 8 8 8 12-1 9-18ZM3 21 16 8"/></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" stroke-linejoin="round"><path d="m9 15 6-6m-4-4 2-2a5 5 0 0 1 7 7l-3 3a5 5 0 0 1-6 1m2 5-2 2a5 5 0 0 1-7-7l3-3a5 5 0 0 1 6-1"/></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" stroke-linejoin="round"><rect x="4" y="10" width="16" height="12" rx="2"/><path d="M7 10V7a5 5 0 0 1 10 0v3M12 15v3"/></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" stroke-linejoin="round"><path d="M20 15A9 9 0 0 1 9 3a9 9 0 1 0 11 12Z"/></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" stroke-linejoin="round"><rect x="9" y="2" width="6" height="5" rx="1"/><rect x="2" y="17" width="6" height="5" rx="1"/><rect x="16" y="17" width="6" height="5" rx="1"/><path d="M12 7v5M5 17v-5h14v5"/></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" stroke-linejoin="round"><circle cx="9" cy="7" r="3"/><path d="M2 21v-3a7 7 0 0 1 14 0v3M17 4a3 3 0 0 1 0 6m2 4a6 6 0 0 1 3 5v2"/></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" stroke-linejoin="round"><circle cx="12" cy="7" r="4"/><path d="M4 21v-2a8 8 0 0 1 16 0v2"/></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" stroke-linejoin="round"><path d="m8 3 8 0-1 6 4 4v2H5v-2l4-4-1-6ZM12 15v7"/></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" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="4.5"/><path d="m12 12 6.4-6.4"/><g fill="currentColor" stroke="none"><circle cx="12" cy="12" r="1.1"/><circle cx="7" cy="15" r="1.1"/></g></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" stroke-linejoin="round"><path d="M5 15a4 4 0 1 1 1-8 5 5 0 0 1 10 1 3.5 3.5 0 0 1 2 7M7 18l-1 3m6-3-1 3m6-3-1 3"/></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" stroke-linejoin="round"><path d="M12 22V11M12 15C3 15 2 10 3 5c6-1 10 2 9 10Zm0-4c0-7 4-9 9-8 1 5-2 9-9 8Z"/></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" stroke-linejoin="round"><circle cx="12" cy="12" r="2"/><path d="M7 7a7 7 0 0 0 0 10m10-10a7 7 0 0 1 0 10M4 4a11 11 0 0 0 0 16M20 4a11 11 0 0 1 0 16"/></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" stroke-linejoin="round"><rect x="3" y="3" width="18" height="7" rx="2"/><rect x="3" y="14" width="18" height="7" rx="2"/><path d="M7 6.5h.01M7 17.5h.01M12 6.5h5m-5 11h5"/></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" stroke-linejoin="round"><path d="m3 13 9-4 9 4-3 6M6 19l-3-6m4-2V5h10v6M12 5V2M2 21q3-4 6 0 4-4 8 0 3-4 6 0"/></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" stroke-linejoin="round"><path d="M12 2v20M3 7l18 10M3 17 21 7M9 4l3 3 3-3m-6 16 3-3 3 3M3 10l4-1-1-4m12 14-1-4 4-1M3 14l4 1-1 4M18 5l-1 4 4 1"/></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" stroke-linejoin="round"><path d="m12 2 3 6 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1Z"/></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" stroke-linejoin="round"><path d="M6 15a4 4 0 1 1 0-8 5 5 0 0 1 10 1 3.5 3.5 0 0 1 2 7m-5-4-5 7h5l-2 5 6-8h-5Z"/></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" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M2 12h2m16 0h2M5 5l1 1m12 12 1 1M5 19l1-1M18 6l1-1"/></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" stroke-linejoin="round"><path d="M9 14V5a3 3 0 0 1 6 0v9a5 5 0 1 1-6 0ZM12 8v9"/></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" stroke-linejoin="round"><path d="m12 2-6 8h3l-6 7h7v5h4v-5h7l-6-7h3Z"/></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" stroke-linejoin="round"><path d="M3 8h12a3 3 0 1 0-3-3M2 12h17a3 3 0 1 1-3 3M4 16h5a3 3 0 1 1-3 3"/></svg>
|
package/src/assets/icons.js
CHANGED
|
@@ -1,25 +1,73 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
|
|
1
|
+
/** Resolve built-in SVG resources independently of the bundled JavaScript location.
|
|
2
|
+
* @param {string | URL} [iconsBaseUrl] Directory URL, with or without a trailing slash.
|
|
3
|
+
*/
|
|
4
|
+
export function createBuiltinIconUrls(iconsBaseUrl) {
|
|
5
|
+
const base = String(iconsBaseUrl ?? new URL('../../resources/icons/', import.meta.url)).replace(/\/+$/, '');
|
|
6
|
+
return Object.freeze(Object.fromEntries(builtinIconNames.map(name => [name, `${base}/${name}.svg`])));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const builtinIconNames = Object.freeze([
|
|
10
|
+
'aircraft',
|
|
11
|
+
'bicycle',
|
|
12
|
+
'bird',
|
|
13
|
+
'box',
|
|
14
|
+
'building',
|
|
15
|
+
'bus-vehicle',
|
|
16
|
+
'calendar',
|
|
17
|
+
'car',
|
|
18
|
+
'cat',
|
|
19
|
+
'child',
|
|
20
|
+
'clock',
|
|
21
|
+
'cloud',
|
|
22
|
+
'control',
|
|
23
|
+
'damage',
|
|
24
|
+
'data-bus',
|
|
25
|
+
'database',
|
|
26
|
+
'dog',
|
|
27
|
+
'drone',
|
|
28
|
+
'error',
|
|
29
|
+
'fish',
|
|
30
|
+
'flower',
|
|
31
|
+
'fog',
|
|
32
|
+
'folder',
|
|
33
|
+
'globe',
|
|
34
|
+
'grip',
|
|
35
|
+
'ground',
|
|
36
|
+
'heart',
|
|
37
|
+
'helicopter',
|
|
38
|
+
'insect',
|
|
39
|
+
'inspector-array',
|
|
40
|
+
'inspector-object',
|
|
41
|
+
'inspector-value',
|
|
42
|
+
'leaf',
|
|
43
|
+
'link',
|
|
44
|
+
'lock',
|
|
45
|
+
'moon',
|
|
46
|
+
'munition',
|
|
47
|
+
'network',
|
|
48
|
+
'people',
|
|
49
|
+
'person',
|
|
50
|
+
'pin',
|
|
51
|
+
'placeholder',
|
|
52
|
+
'point',
|
|
53
|
+
'radar',
|
|
54
|
+
'rain',
|
|
55
|
+
'seedling',
|
|
56
|
+
'sensor',
|
|
57
|
+
'server',
|
|
58
|
+
'ship',
|
|
59
|
+
'situation',
|
|
60
|
+
'snow',
|
|
61
|
+
'space',
|
|
62
|
+
'star',
|
|
63
|
+
'storm',
|
|
64
|
+
'subsurface',
|
|
65
|
+
'sun',
|
|
66
|
+
'surface',
|
|
67
|
+
'task',
|
|
68
|
+
'thermometer',
|
|
69
|
+
'track',
|
|
70
|
+
'tree',
|
|
71
|
+
'warning',
|
|
72
|
+
'wind',
|
|
73
|
+
]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createBuiltinIconUrls } from '../assets/icons.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Icon source registry for the Canvas2D tree renderer.
|
|
@@ -8,11 +8,11 @@ import { builtinIconUrls } from '../assets/icons.js';
|
|
|
8
8
|
* colour combination. The render hot path is therefore a single drawImage.
|
|
9
9
|
*/
|
|
10
10
|
export class IconRegistry {
|
|
11
|
-
constructor({ pixelRatio } = {}) {
|
|
11
|
+
constructor({ pixelRatio, iconsBaseUrl } = {}) {
|
|
12
12
|
this.icons = new Map();
|
|
13
13
|
this.listeners = new Set();
|
|
14
14
|
this.pixelRatio = pixelRatio ?? devicePixelRatio();
|
|
15
|
-
this.#registerBuiltIns();
|
|
15
|
+
this.#registerBuiltIns(iconsBaseUrl);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/** Register an SVG string/URL, an image URL, ImageBitmap, or legacy Canvas draw function. */
|
|
@@ -91,9 +91,7 @@ export class IconRegistry {
|
|
|
91
91
|
error: null,
|
|
92
92
|
};
|
|
93
93
|
this.icons.set(name, entry);
|
|
94
|
-
//
|
|
95
|
-
// Avoid file-URL fetch attempts while the package is exercised in Node.
|
|
96
|
-
if (typeof window !== 'undefined') this.#loadSvg(entry);
|
|
94
|
+
// Load on prepare()/draw(): unused catalog icons do not make network requests.
|
|
97
95
|
return entry;
|
|
98
96
|
}
|
|
99
97
|
|
|
@@ -162,8 +160,8 @@ export class IconRegistry {
|
|
|
162
160
|
for (const listener of this.listeners) listener();
|
|
163
161
|
}
|
|
164
162
|
|
|
165
|
-
#registerBuiltIns() {
|
|
166
|
-
for (const [name, url] of Object.entries(
|
|
163
|
+
#registerBuiltIns(iconsBaseUrl) {
|
|
164
|
+
for (const [name, url] of Object.entries(createBuiltinIconUrls(iconsBaseUrl))) this.register(name, url);
|
|
167
165
|
}
|
|
168
166
|
}
|
|
169
167
|
|
|
@@ -2,6 +2,7 @@ export const darkTheme = {
|
|
|
2
2
|
rowHeight: 28,
|
|
3
3
|
indentWidth: 18,
|
|
4
4
|
font: '12px Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
5
|
+
unitFont: '11px ui-monospace, SFMono-Regular, Consolas, monospace',
|
|
5
6
|
monoFont: '12px "JetBrains Mono", "Cascadia Mono", "Fira Code", ui-monospace, SFMono-Regular, Consolas, monospace',
|
|
6
7
|
colors: {
|
|
7
8
|
background: '#0a0f1c',
|
|
@@ -35,7 +36,7 @@ export const darkTheme = {
|
|
|
35
36
|
root: { icon: 'folder', color: '#38bdf8' },
|
|
36
37
|
system: { icon: 'folder', color: '#818cf8' },
|
|
37
38
|
platform: { icon: 'aircraft', color: '#60a5fa' },
|
|
38
|
-
air: { icon: '
|
|
39
|
+
air: { icon: 'aircraft', color: '#60a5fa' },
|
|
39
40
|
ground: { icon: 'ground', color: '#a3e635' },
|
|
40
41
|
surface: { icon: 'surface', color: '#22d3ee' },
|
|
41
42
|
subsurface: { icon: 'subsurface', color: '#38bdf8' },
|
|
@@ -50,7 +51,7 @@ export const darkTheme = {
|
|
|
50
51
|
warning: { icon: 'warning', color: '#facc15' },
|
|
51
52
|
error: { icon: 'error', color: '#ef4444' },
|
|
52
53
|
task: { icon: 'task', color: '#f97316' },
|
|
53
|
-
bus: { icon: 'bus', color: '#22d3ee' },
|
|
54
|
+
bus: { icon: 'data-bus', color: '#22d3ee' },
|
|
54
55
|
object: { icon: 'inspector-object', color: '#7dd3fc' },
|
|
55
56
|
array: { icon: 'inspector-array', color: '#a78bfa' },
|
|
56
57
|
string: { icon: 'inspector-value', color: '#94a3b8' },
|
|
@@ -128,7 +129,7 @@ export const tacticalTheme = {
|
|
|
128
129
|
...darkTheme.types,
|
|
129
130
|
root: { icon: 'folder', color: '#7ddc92' },
|
|
130
131
|
platform: { icon: 'aircraft', color: '#93c572' },
|
|
131
|
-
air: { icon: '
|
|
132
|
+
air: { icon: 'aircraft', color: '#93c572' },
|
|
132
133
|
ground: { icon: 'ground', color: '#c7f36f' },
|
|
133
134
|
surface: { icon: 'surface', color: '#67e8f9' },
|
|
134
135
|
subsurface: { icon: 'subsurface', color: '#7dd3fc' },
|
|
@@ -142,7 +143,7 @@ export const tacticalTheme = {
|
|
|
142
143
|
damage: { icon: 'damage', color: '#ff8fab' },
|
|
143
144
|
warning: { icon: 'warning', color: '#ffd166' },
|
|
144
145
|
error: { icon: 'error', color: '#ff5c5c' },
|
|
145
|
-
bus: { icon: 'bus', color: '#67e8f9' },
|
|
146
|
+
bus: { icon: 'data-bus', color: '#67e8f9' },
|
|
146
147
|
},
|
|
147
148
|
};
|
|
148
149
|
|
|
@@ -187,7 +188,17 @@ export class ThemeManager extends EventTarget {
|
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
|
|
191
|
+
export function resolveTheme(theme = {}) {
|
|
192
|
+
if (typeof theme === 'string') {
|
|
193
|
+
if (!Object.hasOwn(themes, theme)) throw new TypeError(`Unknown virtual tree theme: ${theme}`);
|
|
194
|
+
return themes[theme];
|
|
195
|
+
}
|
|
196
|
+
if (!theme || typeof theme !== 'object' || Array.isArray(theme)) throw new TypeError('theme must be a theme name or object');
|
|
197
|
+
return theme;
|
|
198
|
+
}
|
|
199
|
+
|
|
190
200
|
function normalizeTheme(theme = {}) {
|
|
201
|
+
theme = resolveTheme(theme);
|
|
191
202
|
const base = mergeTheme(darkTheme, theme);
|
|
192
203
|
return {
|
|
193
204
|
...base,
|
|
@@ -69,6 +69,17 @@ export class TreeColumnModel {
|
|
|
69
69
|
if (!this.columns.some((column) => column.kind === 'tree' || column.kind === 'inspectorPane')) {
|
|
70
70
|
this.columns.unshift(normalizeColumn(builtInColumns.tree, 0));
|
|
71
71
|
}
|
|
72
|
+
this.setRowReorder(this.rowReorder);
|
|
73
|
+
this.#layout();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
setRowReorder(enabled) {
|
|
77
|
+
this.rowReorder = Boolean(enabled);
|
|
78
|
+
this.columns = this.columns.filter(column => column.id !== '__vtc_row_order');
|
|
79
|
+
if (this.rowReorder) this.columns.unshift(normalizeColumn({
|
|
80
|
+
id: '__vtc_row_order', label: '', kind: 'rowOrder', width: 72, minWidth: 72,
|
|
81
|
+
sortable: false, resizable: false
|
|
82
|
+
}, 0));
|
|
72
83
|
this.#layout();
|
|
73
84
|
}
|
|
74
85
|
|
|
@@ -83,17 +94,18 @@ export class TreeColumnModel {
|
|
|
83
94
|
|
|
84
95
|
resizeColumn(id, width) {
|
|
85
96
|
const column = this.getColumn(id);
|
|
86
|
-
if (!column) return false;
|
|
97
|
+
if (!column || column.resizable === false) return false;
|
|
87
98
|
column.width = Math.max(column.minWidth, width);
|
|
88
99
|
this.#layout();
|
|
89
100
|
return true;
|
|
90
101
|
}
|
|
91
102
|
|
|
92
103
|
moveColumn(id, targetIndex) {
|
|
104
|
+
if (id === '__vtc_row_order') return false;
|
|
93
105
|
const currentIndex = this.columns.findIndex((column) => column.id === id);
|
|
94
106
|
if (currentIndex === -1) return false;
|
|
95
107
|
const [column] = this.columns.splice(currentIndex, 1);
|
|
96
|
-
const nextIndex = Math.max(0, Math.min(this.columns.length, targetIndex));
|
|
108
|
+
const nextIndex = Math.max(this.rowReorder ? 1 : 0, Math.min(this.columns.length, targetIndex));
|
|
97
109
|
this.columns.splice(nextIndex, 0, column);
|
|
98
110
|
if (!this.columns.some((item) => item.kind === 'tree')) this.columns.unshift(normalizeColumn(builtInColumns.tree, 0));
|
|
99
111
|
this.#layout();
|
|
@@ -107,7 +119,7 @@ export class TreeColumnModel {
|
|
|
107
119
|
}
|
|
108
120
|
|
|
109
121
|
getResizeHandleAt(x, tolerance = 5) {
|
|
110
|
-
return this.columns.find((column) => Math.abs(x - (column.x + column.width)) <= tolerance) ?? null;
|
|
122
|
+
return this.columns.find((column) => column.resizable !== false && Math.abs(x - (column.x + column.width)) <= tolerance) ?? null;
|
|
111
123
|
}
|
|
112
124
|
|
|
113
125
|
#layout() {
|
|
@@ -144,6 +156,7 @@ function normalizeColumn(column, index) {
|
|
|
144
156
|
align: source.align ?? 'left',
|
|
145
157
|
kind: source.kind ?? (index === 0 ? 'tree' : 'text'),
|
|
146
158
|
sortable: source.sortable ?? true,
|
|
159
|
+
resizable: source.resizable ?? true,
|
|
147
160
|
value: source.value ?? ((node) => node[source.id] ?? ''),
|
|
148
161
|
render: source.render,
|
|
149
162
|
x: 0,
|
package/src/core/tree-model.js
CHANGED
|
@@ -56,6 +56,26 @@ export class TreeModel extends EventTarget {
|
|
|
56
56
|
this.dispatchEvent(new Event('structurechange'));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/** Move one node among siblings without resetting expansion or live values. */
|
|
60
|
+
moveNode(id, targetIndex) {
|
|
61
|
+
const node = this.index.getNode(id);
|
|
62
|
+
if (!node || !Number.isInteger(targetIndex)) return null;
|
|
63
|
+
const parentId = node.parentId ?? null;
|
|
64
|
+
const siblings = this.index.getChildren(parentId);
|
|
65
|
+
const fromIndex = siblings.indexOf(id);
|
|
66
|
+
const toIndex = Math.max(0, Math.min(siblings.length - 1, targetIndex));
|
|
67
|
+
if (fromIndex === toIndex) return null;
|
|
68
|
+
const siblingOrder = siblings.slice();
|
|
69
|
+
siblingOrder.splice(fromIndex, 1);
|
|
70
|
+
siblingOrder.splice(toIndex, 0, id);
|
|
71
|
+
const orderedNodes = siblingOrder.map(nodeId => this.index.getNode(nodeId));
|
|
72
|
+
let position = 0;
|
|
73
|
+
this.nodes = this.nodes.map(item => (item.parentId ?? null) === parentId ? orderedNodes[position++] : item);
|
|
74
|
+
this.index.rebuild(this.nodes);
|
|
75
|
+
this.dispatchEvent(new Event('structurechange'));
|
|
76
|
+
return { nodeId: id, parentId, fromIndex, toIndex, siblingOrder, order: this.nodes.map(item => item.id) };
|
|
77
|
+
}
|
|
78
|
+
|
|
59
79
|
/** @param {string} id */
|
|
60
80
|
collapse(id) {
|
|
61
81
|
this.expanded.delete(id);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { resolveTheme } from './theme-manager.js';
|
|
2
|
+
/** Supported TreeView configuration options and defaults. */
|
|
3
|
+
export const treeViewDefaults = Object.freeze({
|
|
4
|
+
mode: 'tree', presentation: 'pane', nodes: Object.freeze([]), model: null, meta: null,
|
|
5
|
+
columns: null, theme: 'dark', flatRoot: true, enforceMeta: false,
|
|
6
|
+
filter: true, filterPlacement: 'auto', markUpdated: true, editable: true,
|
|
7
|
+
rowReorder: false, initialExpandDepth: 1, rowHeight: undefined,
|
|
8
|
+
indentWidth: undefined, headerHeight: 28, iconResolver: null, fontFamily: null,
|
|
9
|
+
});
|
|
10
|
+
export const treeViewOptionNames = Object.freeze(Object.keys(treeViewDefaults));
|
|
11
|
+
|
|
12
|
+
export function validateTreeViewOptions(options) {
|
|
13
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Options must be an object');
|
|
14
|
+
for (const [key, value] of Object.entries(options)) {
|
|
15
|
+
if (key === 'theme') resolveTheme(value);
|
|
16
|
+
if (key === 'mode' && !['tree', 'inspector'].includes(value)) throw new TypeError('mode must be tree or inspector');
|
|
17
|
+
if (key === 'presentation' && !['pane', 'table'].includes(value)) throw new TypeError('presentation must be pane or table');
|
|
18
|
+
if (key === 'filterPlacement' && !['auto', 'bar', 'header'].includes(value)) throw new TypeError('filterPlacement must be auto, bar or header');
|
|
19
|
+
if (key === 'nodes' && !Array.isArray(value)) throw new TypeError('nodes must be an array');
|
|
20
|
+
if (key === 'columns' && value !== null && !Array.isArray(value)) throw new TypeError('columns must be an array or null');
|
|
21
|
+
if (key === 'model' && value === undefined) throw new TypeError('model must not be undefined');
|
|
22
|
+
if (key === 'meta' && value !== null && (!value || typeof value !== 'object' || Array.isArray(value))) throw new TypeError('meta must be an object or null');
|
|
23
|
+
if (['rowHeight', 'indentWidth'].includes(key) && value !== undefined && (!Number.isFinite(value) || value <= 0)) throw new TypeError(`${key} must be a positive number`);
|
|
24
|
+
if (key === 'headerHeight' && (!Number.isFinite(value) || value < 0)) throw new TypeError('headerHeight must be non-negative');
|
|
25
|
+
if (key === 'initialExpandDepth' && (!Number.isInteger(value) || value < 0)) throw new TypeError('initialExpandDepth must be a non-negative integer');
|
|
26
|
+
if (key === 'iconResolver' && value !== null && typeof value !== 'function') throw new TypeError('iconResolver must be a function or null');
|
|
27
|
+
if (key === 'fontFamily' && value !== null && typeof value !== 'string') throw new TypeError('fontFamily must be a string or null');
|
|
28
|
+
if (['flatRoot', 'enforceMeta', 'filter', 'markUpdated', 'editable', 'rowReorder'].includes(key) && typeof value !== 'boolean') throw new TypeError(`${key} must be boolean`);
|
|
29
|
+
}
|
|
30
|
+
}
|