@ixfx/components 0.10.7 → 0.10.8
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/bundle/index.d.ts +403 -1
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +1459 -158
- package/bundle/index.js.map +1 -1
- package/dist/ac-text.d.ts +1 -1
- package/dist/{ac-token-TCLAZeVg.d.ts → ac-token--_vCHXWw.d.ts} +2 -2
- package/dist/{ac-token-TCLAZeVg.d.ts.map → ac-token--_vCHXWw.d.ts.map} +1 -1
- package/dist/ac-token.d.ts +1 -1
- package/dist/{button-CTL45aBl.d.ts → button-BYuf72aR.d.ts} +2 -2
- package/dist/{button-CTL45aBl.d.ts.map → button-BYuf72aR.d.ts.map} +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/colour-picker.d.ts +1 -1
- package/dist/crumbs.d.ts +3 -3
- package/dist/{data-grid-BDNjQv3Q.d.ts → data-grid-3yIylIH-.d.ts} +5 -5
- package/dist/{data-grid-BDNjQv3Q.d.ts.map → data-grid-3yIylIH-.d.ts.map} +1 -1
- package/dist/data-grid.d.ts +2 -2
- package/dist/{detail-list-BdLuOwQj.d.ts → detail-list-ccWFyhEc.d.ts} +5 -5
- package/dist/{detail-list-BdLuOwQj.d.ts.map → detail-list-ccWFyhEc.d.ts.map} +1 -1
- package/dist/detail-list.d.ts +3 -3
- package/dist/{grid-list-DyphX9Fv.d.ts → grid-list-BnOVgtLI.d.ts} +4 -4
- package/dist/{grid-list-DyphX9Fv.d.ts.map → grid-list-BnOVgtLI.d.ts.map} +1 -1
- package/dist/grid-list.d.ts +2 -2
- package/dist/{grouped-item-lister-Bhyo-dkH.d.ts → grouped-item-lister-DETte0Dh.d.ts} +2 -2
- package/dist/{grouped-item-lister-Bhyo-dkH.d.ts.map → grouped-item-lister-DETte0Dh.d.ts.map} +1 -1
- package/dist/grouped-item-lister.d.ts +1 -1
- package/dist/{icon-DR_3rSyZ.d.ts → icon-CDGprSdE.d.ts} +2 -2
- package/dist/icon-CDGprSdE.d.ts.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/incr-search.d.ts +1 -1
- package/dist/{index-BXafRK-_.d.ts → index-5-cwYpmx.d.ts} +4 -4
- package/dist/{index-BXafRK-_.d.ts.map → index-5-cwYpmx.d.ts.map} +1 -1
- package/dist/{index-garxxqgV.d.ts → index-CYndvTWH.d.ts} +2 -2
- package/dist/{index-garxxqgV.d.ts.map → index-CYndvTWH.d.ts.map} +1 -1
- package/dist/{index-BUB5SICW.d.ts → index-CoqtMSd9.d.ts} +2 -2
- package/dist/{index-BUB5SICW.d.ts.map → index-CoqtMSd9.d.ts.map} +1 -1
- package/dist/{index-C59-JEPl.d.ts → index-RImMNo0r.d.ts} +4 -4
- package/dist/{index-C59-JEPl.d.ts.map → index-RImMNo0r.d.ts.map} +1 -1
- package/dist/{index-CJ5fshD3.d.ts → index-UvnoZUAo.d.ts} +2 -2
- package/dist/{index-CJ5fshD3.d.ts.map → index-UvnoZUAo.d.ts.map} +1 -1
- package/dist/{index-BOUaDIW-.d.ts → index-tq6f8007.d.ts} +2 -2
- package/dist/{index-BOUaDIW-.d.ts.map → index-tq6f8007.d.ts.map} +1 -1
- package/dist/index.d.ts +417 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1299 -40
- package/dist/index.js.map +1 -1
- package/dist/{labelled-input-base-Dp_9KP0G.d.ts → labelled-input-base-DiTme1JO.d.ts} +2 -2
- package/dist/{labelled-input-base-Dp_9KP0G.d.ts.map → labelled-input-base-DiTme1JO.d.ts.map} +1 -1
- package/dist/labelled-radial-input.d.ts +2 -2
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-slider-input.d.ts +1 -1
- package/dist/{list-native-drag-controller-BAuWAtyQ.d.ts → list-native-drag-controller-B9reY4uM.d.ts} +2 -2
- package/dist/{list-native-drag-controller-BAuWAtyQ.d.ts.map → list-native-drag-controller-B9reY4uM.d.ts.map} +1 -1
- package/dist/{menu-item-ZmkXNjek.d.ts → menu-item-CZi9vy5O.d.ts} +2 -2
- package/dist/{menu-item-ZmkXNjek.d.ts.map → menu-item-CZi9vy5O.d.ts.map} +1 -1
- package/dist/menu.d.ts +2 -2
- package/dist/miller.d.ts +3 -3
- package/dist/panel.d.ts +1 -1
- package/dist/{split-layout-BJEU1W0d.js → split-layout-CXotkdbx.js} +46 -4
- package/dist/split-layout-CXotkdbx.js.map +1 -0
- package/dist/{split-layout-C5SYbDrq.d.ts → split-layout-JK3xWVX2.d.ts} +2 -2
- package/dist/{split-layout-C5SYbDrq.d.ts.map → split-layout-JK3xWVX2.d.ts.map} +1 -1
- package/dist/split-layout.d.ts +1 -1
- package/dist/split-layout.js +1 -1
- package/dist/{tab-list-v8mKX7Fu.d.ts → tab-list-pqfKaVAE.d.ts} +2 -2
- package/dist/{tab-list-v8mKX7Fu.d.ts.map → tab-list-pqfKaVAE.d.ts.map} +1 -1
- package/dist/tabs.d.ts +1 -1
- package/dist/{titlebar-B5zSTZnF.d.ts → titlebar-Tqu-VvMU.d.ts} +2 -2
- package/dist/{titlebar-B5zSTZnF.d.ts.map → titlebar-Tqu-VvMU.d.ts.map} +1 -1
- package/dist/titlebar.d.ts +1 -1
- package/dist/{tree-component-DGmKBbpy.d.ts → tree-component-CMGeqyjZ.d.ts} +2 -2
- package/dist/{tree-component-DGmKBbpy.d.ts.map → tree-component-CMGeqyjZ.d.ts.map} +1 -1
- package/dist/tree.d.ts +4 -4
- package/dist/{types-Bc23SI94.d.ts → types-3Jfcaxvo.d.ts} +8 -1
- package/dist/types-3Jfcaxvo.d.ts.map +1 -0
- package/dist/{vertical-list-C03PN6lx.d.ts → vertical-list-9dJ1R1hU.d.ts} +3 -3
- package/dist/{vertical-list-C03PN6lx.d.ts.map → vertical-list-9dJ1R1hU.d.ts.map} +1 -1
- package/dist/vertical-list.d.ts +2 -2
- package/docs-user/README.md +1 -0
- package/docs-user/data-grid.md +11 -2
- package/docs-user/hyperbolic-tree.md +282 -0
- package/docs-user/index.json +7 -2
- package/docs-user/llms.txt +1 -0
- package/docs-user/tree.md +1 -0
- package/llms.txt +1 -0
- package/package.json +1 -1
- package/dist/icon-DR_3rSyZ.d.ts.map +0 -1
- package/dist/split-layout-BJEU1W0d.js.map +0 -1
- package/dist/types-Bc23SI94.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ import { a as menuFromTreeToElements, t as menu_exports } from "./menu-B2Qv3Oiz.
|
|
|
35
35
|
import { t as PanTextElement } from "./pan-text-CqtvjPyK.js";
|
|
36
36
|
import { DataGridElement } from "./data-grid.js";
|
|
37
37
|
import { DetailListElement } from "./detail-list.js";
|
|
38
|
-
import { a as distribute, c as toSnapshot, i as buildModel, l as
|
|
38
|
+
import { a as distribute, c as preserveRestoreWeights, d as toSnapshot, f as walk, i as buildModel, l as resolveTracks, n as SplitPanelElement, o as findPanelNode, r as applySnapshot, s as normalise, t as SplitLayoutElement, u as setPanelRestoreWeight } from "./split-layout-CXotkdbx.js";
|
|
39
39
|
import { TabListElement } from "./tabs.js";
|
|
40
40
|
import { EditableLabel } from "./editable-label.js";
|
|
41
41
|
import { t as resolveAxisStep } from "./keyboard-B5xD7Fz-.js";
|
|
@@ -3745,6 +3745,7 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
3745
3745
|
this._junctionsKey = ``;
|
|
3746
3746
|
this._junctionsRaf = null;
|
|
3747
3747
|
this._hostResizeObserver = null;
|
|
3748
|
+
this._loadGeneration = 0;
|
|
3748
3749
|
this._toolbarSlots = /* @__PURE__ */ new Set();
|
|
3749
3750
|
this._toolbarObserver = null;
|
|
3750
3751
|
this._panelObservers = /* @__PURE__ */ new Map();
|
|
@@ -4062,6 +4063,7 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
4062
4063
|
}
|
|
4063
4064
|
loadLayout(layout) {
|
|
4064
4065
|
this._returnAllToolbars();
|
|
4066
|
+
this._loadGeneration++;
|
|
4065
4067
|
this._model = normalize(cloneNode(layout.root));
|
|
4066
4068
|
this._mergePoolIntoModel();
|
|
4067
4069
|
this.requestUpdate();
|
|
@@ -4763,12 +4765,18 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
4763
4765
|
const splits = this.shadowRoot?.querySelectorAll(`ixfx-split-layout[data-dock-path]`);
|
|
4764
4766
|
if (splits === void 0) return;
|
|
4765
4767
|
for (const el of splits) {
|
|
4768
|
+
if (el.getAttribute(`data-dock-gen`) !== String(this._loadGeneration)) continue;
|
|
4766
4769
|
const pathStr = el.getAttribute(`data-dock-path`);
|
|
4767
4770
|
if (pathStr === null) continue;
|
|
4768
4771
|
const node = findNode(this._model, parsePath(pathStr));
|
|
4769
4772
|
if (node === null || !isSplit(node)) continue;
|
|
4770
4773
|
const live = Object.values(el.getSizes());
|
|
4771
|
-
if (live.length === node.children.length && !weightsEqual(live, node.sizes))
|
|
4774
|
+
if (live.length === node.children.length && !weightsEqual(live, node.sizes)) {
|
|
4775
|
+
node.sizes = live;
|
|
4776
|
+
Array.from(el.children).forEach((child, i) => {
|
|
4777
|
+
if (child instanceof HTMLElement && node.sizes[i] !== void 0) child.setAttribute(`size`, String(node.sizes[i]));
|
|
4778
|
+
});
|
|
4779
|
+
}
|
|
4772
4780
|
}
|
|
4773
4781
|
}
|
|
4774
4782
|
_teardownTabDragListeners() {
|
|
@@ -4897,6 +4905,7 @@ let DockHostElement = class DockHostElement extends LitElement {
|
|
|
4897
4905
|
size=${size === void 0 ? nothing : String(size)}
|
|
4898
4906
|
style=${`--split-gutter-size: var(--dock-split-bar-size, 2px); --split-collapsed-size: var(--dock-collapsed-size, 40px)`}
|
|
4899
4907
|
data-dock-path=${pathStr}
|
|
4908
|
+
data-dock-gen=${this._loadGeneration}
|
|
4900
4909
|
@ixfx-split-resize=${this._onSplitWeightsChanged}
|
|
4901
4910
|
@ixfx-split-change=${this._onSplitWeightsChanged}
|
|
4902
4911
|
@ixfx-split-collapse=${(e) => this._onSplitCollapsed(e, true)}
|
|
@@ -5518,47 +5527,1289 @@ function createVerticalListAdapter(renderItem, options) {
|
|
|
5518
5527
|
list.addItem(el);
|
|
5519
5528
|
}
|
|
5520
5529
|
}
|
|
5521
|
-
function mapEls(els) {
|
|
5522
|
-
const result = /* @__PURE__ */ new Set();
|
|
5523
|
-
for (const el of els) {
|
|
5524
|
-
const item = elToItem.get(el);
|
|
5525
|
-
if (item !== void 0) result.add(item);
|
|
5530
|
+
function mapEls(els) {
|
|
5531
|
+
const result = /* @__PURE__ */ new Set();
|
|
5532
|
+
for (const el of els) {
|
|
5533
|
+
const item = elToItem.get(el);
|
|
5534
|
+
if (item !== void 0) result.add(item);
|
|
5535
|
+
}
|
|
5536
|
+
return result;
|
|
5537
|
+
}
|
|
5538
|
+
const adapter = Object.assign(container, {
|
|
5539
|
+
setItems(items) {
|
|
5540
|
+
applyItems(items);
|
|
5541
|
+
},
|
|
5542
|
+
clearSelection() {
|
|
5543
|
+
listEl?.clearSelection();
|
|
5544
|
+
},
|
|
5545
|
+
dispatch(commandId, args) {
|
|
5546
|
+
options?.onDispatch?.(commandId, args, listEl);
|
|
5547
|
+
}
|
|
5548
|
+
});
|
|
5549
|
+
Object.defineProperties(adapter, {
|
|
5550
|
+
selectedItems: {
|
|
5551
|
+
get() {
|
|
5552
|
+
return listEl ? mapEls(listEl.selectedItems) : /* @__PURE__ */ new Set();
|
|
5553
|
+
},
|
|
5554
|
+
enumerable: true,
|
|
5555
|
+
configurable: true
|
|
5556
|
+
},
|
|
5557
|
+
selectionMode: {
|
|
5558
|
+
get() {
|
|
5559
|
+
return listEl?.selectionMode ?? selectionMode;
|
|
5560
|
+
},
|
|
5561
|
+
set(v) {
|
|
5562
|
+
selectionMode = v;
|
|
5563
|
+
if (listEl) listEl.selectionMode = v;
|
|
5564
|
+
},
|
|
5565
|
+
enumerable: true,
|
|
5566
|
+
configurable: true
|
|
5567
|
+
}
|
|
5568
|
+
});
|
|
5569
|
+
return adapter;
|
|
5570
|
+
}
|
|
5571
|
+
//#endregion
|
|
5572
|
+
//#region src/hyperbolic-tree/poincare-math.ts
|
|
5573
|
+
const EPSILON = 1e-9;
|
|
5574
|
+
function complexMul(a, b) {
|
|
5575
|
+
return {
|
|
5576
|
+
x: a.x * b.x - a.y * b.y,
|
|
5577
|
+
y: a.x * b.y + a.y * b.x
|
|
5578
|
+
};
|
|
5579
|
+
}
|
|
5580
|
+
function complexConjugate(a) {
|
|
5581
|
+
return {
|
|
5582
|
+
x: a.x,
|
|
5583
|
+
y: -a.y
|
|
5584
|
+
};
|
|
5585
|
+
}
|
|
5586
|
+
function complexAdd(a, b) {
|
|
5587
|
+
return {
|
|
5588
|
+
x: a.x + b.x,
|
|
5589
|
+
y: a.y + b.y
|
|
5590
|
+
};
|
|
5591
|
+
}
|
|
5592
|
+
function complexSub(a, b) {
|
|
5593
|
+
return {
|
|
5594
|
+
x: a.x - b.x,
|
|
5595
|
+
y: a.y - b.y
|
|
5596
|
+
};
|
|
5597
|
+
}
|
|
5598
|
+
function complexScale(a, s) {
|
|
5599
|
+
return {
|
|
5600
|
+
x: a.x * s,
|
|
5601
|
+
y: a.y * s
|
|
5602
|
+
};
|
|
5603
|
+
}
|
|
5604
|
+
function complexDiv(a, b) {
|
|
5605
|
+
const denominator = b.x * b.x + b.y * b.y;
|
|
5606
|
+
if (denominator < EPSILON) return {
|
|
5607
|
+
x: 0,
|
|
5608
|
+
y: 0
|
|
5609
|
+
};
|
|
5610
|
+
return complexScale(complexMul(a, complexConjugate(b)), 1 / denominator);
|
|
5611
|
+
}
|
|
5612
|
+
/** Euclidean norm (`|z|`) of a point. */
|
|
5613
|
+
function norm(a) {
|
|
5614
|
+
return Math.hypot(a.x, a.y);
|
|
5615
|
+
}
|
|
5616
|
+
/** Clamp a point to stay strictly inside the unit disk (the Poincaré model's boundary is never reached). */
|
|
5617
|
+
function clampToDisk(p, maxNorm = .999999) {
|
|
5618
|
+
const n = norm(p);
|
|
5619
|
+
if (n <= maxNorm) return p;
|
|
5620
|
+
return complexScale(p, maxNorm / n);
|
|
5621
|
+
}
|
|
5622
|
+
/**
|
|
5623
|
+
* Möbius transform that translates `a` to the origin: `(z - a) / (1 - conj(a) * z)`.
|
|
5624
|
+
* A hyperbolic isometry — preserves all pairwise Poincaré distances.
|
|
5625
|
+
*/
|
|
5626
|
+
function mobiusToOrigin(z, a) {
|
|
5627
|
+
return clampToDisk(complexDiv(complexSub(z, a), complexSub({
|
|
5628
|
+
x: 1,
|
|
5629
|
+
y: 0
|
|
5630
|
+
}, complexMul(complexConjugate(a), z))));
|
|
5631
|
+
}
|
|
5632
|
+
/**
|
|
5633
|
+
* Inverse of {@link mobiusToOrigin}: translates the origin back to `a`, i.e.
|
|
5634
|
+
* `(z + a) / (1 + conj(a) * z)`.
|
|
5635
|
+
*/
|
|
5636
|
+
function mobiusFromOrigin(z, a) {
|
|
5637
|
+
return clampToDisk(complexDiv(complexAdd(z, a), complexAdd({
|
|
5638
|
+
x: 1,
|
|
5639
|
+
y: 0
|
|
5640
|
+
}, complexMul(complexConjugate(a), z))));
|
|
5641
|
+
}
|
|
5642
|
+
/**
|
|
5643
|
+
* Möbius addition (gyrogroup composition) of two disk-translations: the
|
|
5644
|
+
* canonical-space point reached by translating the origin to `a`, then
|
|
5645
|
+
* translating that local frame's origin by `b`.
|
|
5646
|
+
*/
|
|
5647
|
+
function mobiusAdd(a, b) {
|
|
5648
|
+
return mobiusFromOrigin(b, a);
|
|
5649
|
+
}
|
|
5650
|
+
/** Euclidean Poincaré-disk radius corresponding to hyperbolic distance `d` from the origin. */
|
|
5651
|
+
function euclideanRadiusForHyperbolicDistance(d) {
|
|
5652
|
+
return Math.tanh(d / 2);
|
|
5653
|
+
}
|
|
5654
|
+
/** Hyperbolic distance from the origin corresponding to Euclidean disk radius `r`. */
|
|
5655
|
+
function hyperbolicDistanceForEuclideanRadius(r) {
|
|
5656
|
+
return 2 * Math.atanh(Math.min(Math.max(r, 0), .999999));
|
|
5657
|
+
}
|
|
5658
|
+
/** Hyperbolic (Poincaré) distance between two points in the unit disk. */
|
|
5659
|
+
function poincareDistance(p, q) {
|
|
5660
|
+
return hyperbolicDistanceForEuclideanRadius(norm(mobiusToOrigin(q, p)));
|
|
5661
|
+
}
|
|
5662
|
+
/**
|
|
5663
|
+
* Point at parameter `t` (0–1) along the hyperbolic geodesic from `a` to `b`.
|
|
5664
|
+
* `t=0` yields `a`, `t=1` yields `b`; intermediate values move at constant
|
|
5665
|
+
* hyperbolic speed along the geodesic (not the Euclidean straight line).
|
|
5666
|
+
*/
|
|
5667
|
+
function geodesicInterpolate(a, b, t) {
|
|
5668
|
+
if (t <= 0) return a;
|
|
5669
|
+
if (t >= 1) return b;
|
|
5670
|
+
const bLocal = mobiusToOrigin(b, a);
|
|
5671
|
+
const localNorm = norm(bLocal);
|
|
5672
|
+
if (localNorm < EPSILON) return a;
|
|
5673
|
+
return mobiusFromOrigin(complexScale(complexScale(bLocal, 1 / localNorm), euclideanRadiusForHyperbolicDistance(hyperbolicDistanceForEuclideanRadius(localNorm) * t)), a);
|
|
5674
|
+
}
|
|
5675
|
+
//#endregion
|
|
5676
|
+
//#region src/hyperbolic-tree/layout.ts
|
|
5677
|
+
/** Depth-first search for the node with `key`. Returns `undefined` when absent. */
|
|
5678
|
+
function findHyperbolicNode(root, key) {
|
|
5679
|
+
if (!root) return void 0;
|
|
5680
|
+
if (root.item.key === key) return root;
|
|
5681
|
+
for (const child of root.children ?? []) {
|
|
5682
|
+
const hit = findHyperbolicNode(child, key);
|
|
5683
|
+
if (hit) return hit;
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5686
|
+
/**
|
|
5687
|
+
* Chain of nodes from `root` down to (and including) the node with `key`.
|
|
5688
|
+
* Returns `undefined` when the key is absent. The first entry is always `root`.
|
|
5689
|
+
*/
|
|
5690
|
+
function findHyperbolicPath(root, key) {
|
|
5691
|
+
if (!root) return void 0;
|
|
5692
|
+
if (root.item.key === key) return [root];
|
|
5693
|
+
for (const child of root.children ?? []) {
|
|
5694
|
+
const hit = findHyperbolicPath(child, key);
|
|
5695
|
+
if (hit) return [root, ...hit];
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5698
|
+
/**
|
|
5699
|
+
* Number of nodes in `node`'s subtree, counting `node` itself, up to
|
|
5700
|
+
* `remainingDepth` further levels (matching the canonical-position recursion's
|
|
5701
|
+
* own `maxDepth` cutoff, so weighting stays consistent with what's actually
|
|
5702
|
+
* laid out).
|
|
5703
|
+
*/
|
|
5704
|
+
function subtreeSize(node, remainingDepth) {
|
|
5705
|
+
if (remainingDepth <= 0) return 1;
|
|
5706
|
+
const children = node.children ?? [];
|
|
5707
|
+
if (children.length === 0) return 1;
|
|
5708
|
+
let total = 1;
|
|
5709
|
+
for (const child of children) total += subtreeSize(child, remainingDepth - 1);
|
|
5710
|
+
return total;
|
|
5711
|
+
}
|
|
5712
|
+
/**
|
|
5713
|
+
* Angle (as seen from `localOrigin`, i.e. after translating `localOrigin` to
|
|
5714
|
+
* the disk's center) of the disk-boundary point that currently sits at angle
|
|
5715
|
+
* `theta` from the true origin. Möbius translation is conformal but not
|
|
5716
|
+
* angle-preserving in the naive sense — a wedge boundary doesn't just shift
|
|
5717
|
+
* by a constant offset when reparented to a new local frame, its exact
|
|
5718
|
+
* transformed angle depends on how far off-center `localOrigin` is. Used to
|
|
5719
|
+
* carry a node's *own* angular wedge (inherited from its parent) into the
|
|
5720
|
+
* local frame of each of its children, rather than approximating it as
|
|
5721
|
+
* "the opposite direction ± half the width" — the same technique used by
|
|
5722
|
+
* reference hyperbolic-tree-browser implementations (e.g. d3-hypertree's
|
|
5723
|
+
* `wedgeTranslate`) to keep the recursive layout accurate at any depth/width.
|
|
5724
|
+
*/
|
|
5725
|
+
function boundaryAngleInLocalFrame(theta, localOrigin) {
|
|
5726
|
+
const transformed = mobiusToOrigin({
|
|
5727
|
+
x: Math.cos(theta),
|
|
5728
|
+
y: Math.sin(theta)
|
|
5729
|
+
}, localOrigin);
|
|
5730
|
+
return Math.atan2(transformed.y, transformed.x);
|
|
5731
|
+
}
|
|
5732
|
+
/** Deterministic pseudo-random value in `[-1, 1]` derived from `key`, stable across renders. */
|
|
5733
|
+
function stableUnitJitter(key) {
|
|
5734
|
+
let hash = 0;
|
|
5735
|
+
for (let i = 0; i < key.length; i++) hash = Math.imul(hash, 31) + key.charCodeAt(i) | 0;
|
|
5736
|
+
return (hash >>> 0) % 1e3 / 1e3 * 2 - 1;
|
|
5737
|
+
}
|
|
5738
|
+
/**
|
|
5739
|
+
* A single unbranching child (no siblings to fan against) would otherwise sit
|
|
5740
|
+
* at the exact bisector of its inherited wedge — i.e. dead-center, continuing
|
|
5741
|
+
* in a perfectly straight line from its own parent's edge. For a chain of
|
|
5742
|
+
* such nodes (a deeply-nested path with no branching), that reads as one
|
|
5743
|
+
* unbroken line cutting across the disk rather than a legible sequence of
|
|
5744
|
+
* edges. Nudging by a small, key-derived (so stable across re-renders) angle
|
|
5745
|
+
* breaks that up — the same purpose served by the small per-node offsets in
|
|
5746
|
+
* reference hyperbolic-tree-browser implementations (e.g. d3-hypertree's
|
|
5747
|
+
* `layoutBergé`, which staggers leaf nodes rather than stacking them exactly
|
|
5748
|
+
* radially).
|
|
5749
|
+
*/
|
|
5750
|
+
const CHAIN_JITTER_RADIANS = .22;
|
|
5751
|
+
/**
|
|
5752
|
+
* Canonical (true-root-anchored) hyperbolic position of every currently-loaded
|
|
5753
|
+
* node, keyed by node key. Positions never depend on the current focus —
|
|
5754
|
+
* refocusing is purely a Möbius re-projection of these fixed coordinates, so
|
|
5755
|
+
* the tree never needs to be re-laid-out when the focus changes.
|
|
5756
|
+
*
|
|
5757
|
+
* Each node holds an angular wedge — inherited from its parent, expressed in
|
|
5758
|
+
* its *own* local frame (root starts with the full `[-π, π]`) — across which
|
|
5759
|
+
* its children are placed at hyperbolic distance `curvature` (default `1`).
|
|
5760
|
+
* That wedge is partitioned among the children, weighted by each child's own
|
|
5761
|
+
* subtree size rather than split evenly, and each child's own share is
|
|
5762
|
+
* carried into its local frame via {@link boundaryAngleInLocalFrame} before
|
|
5763
|
+
* recursing. This is a strict partition (slices never overlap and always sum
|
|
5764
|
+
* to the parent's own wedge), which is what guarantees sibling subtrees never
|
|
5765
|
+
* spatially cross regardless of how deep or bushy they are.
|
|
5766
|
+
*
|
|
5767
|
+
* `curvature` scales how far apart (in hyperbolic distance) each generation
|
|
5768
|
+
* sits. Raising it pushes each generation further out, so the Poincaré
|
|
5769
|
+
* metric's own `tanh` compression crowds distant generations toward the
|
|
5770
|
+
* boundary faster — a stronger fisheye, which is why it reads as "zooming
|
|
5771
|
+
* in" on the focus. Lowering it spreads generations out more evenly,
|
|
5772
|
+
* flattening the view toward a Euclidean-feeling layout.
|
|
5773
|
+
*/
|
|
5774
|
+
function computeCanonicalPositions(root, maxDepth = 24, curvature = 1) {
|
|
5775
|
+
const map = /* @__PURE__ */ new Map();
|
|
5776
|
+
if (!root) return map;
|
|
5777
|
+
const origin = {
|
|
5778
|
+
x: 0,
|
|
5779
|
+
y: 0
|
|
5780
|
+
};
|
|
5781
|
+
map.set(root.item.key, {
|
|
5782
|
+
node: root,
|
|
5783
|
+
depth: 0,
|
|
5784
|
+
parentKey: void 0,
|
|
5785
|
+
position: origin,
|
|
5786
|
+
angularShare: 2 * Math.PI
|
|
5787
|
+
});
|
|
5788
|
+
const place = (node, position, wedgeStart, wedgeEnd, depth) => {
|
|
5789
|
+
if (depth >= maxDepth) return;
|
|
5790
|
+
const children = node.children ?? [];
|
|
5791
|
+
const n = children.length;
|
|
5792
|
+
if (n === 0) return;
|
|
5793
|
+
const r0 = euclideanRadiusForHyperbolicDistance(curvature);
|
|
5794
|
+
const angularWidth = wedgeEnd - wedgeStart;
|
|
5795
|
+
const weights = children.map((child) => subtreeSize(child, maxDepth - depth - 1));
|
|
5796
|
+
const totalWeight = weights.reduce((a, b) => a + b, 0);
|
|
5797
|
+
let cursor = wedgeStart;
|
|
5798
|
+
children.forEach((child, index) => {
|
|
5799
|
+
const share = n === 1 ? angularWidth : angularWidth * weights[index] / totalWeight;
|
|
5800
|
+
const childStart = cursor;
|
|
5801
|
+
const childEnd = cursor + share;
|
|
5802
|
+
cursor = childEnd;
|
|
5803
|
+
const theta = (childStart + childEnd) / 2 + (n === 1 ? stableUnitJitter(child.item.key) * Math.min(CHAIN_JITTER_RADIANS, share * .4) : 0);
|
|
5804
|
+
const local = {
|
|
5805
|
+
x: r0 * Math.cos(theta),
|
|
5806
|
+
y: r0 * Math.sin(theta)
|
|
5807
|
+
};
|
|
5808
|
+
const canonicalPosition = mobiusFromOrigin(local, position);
|
|
5809
|
+
map.set(child.item.key, {
|
|
5810
|
+
node: child,
|
|
5811
|
+
depth: depth + 1,
|
|
5812
|
+
parentKey: node.item.key,
|
|
5813
|
+
position: canonicalPosition,
|
|
5814
|
+
angularShare: share
|
|
5815
|
+
});
|
|
5816
|
+
const localStart = boundaryAngleInLocalFrame(childStart, local);
|
|
5817
|
+
let localWidth = boundaryAngleInLocalFrame(childEnd, local) - localStart;
|
|
5818
|
+
if (localWidth <= 0) localWidth += 2 * Math.PI;
|
|
5819
|
+
place(child, canonicalPosition, localStart, localStart + localWidth, depth + 1);
|
|
5820
|
+
});
|
|
5821
|
+
};
|
|
5822
|
+
place(root, origin, -Math.PI, Math.PI, 0);
|
|
5823
|
+
return map;
|
|
5824
|
+
}
|
|
5825
|
+
/** Nodes within `maxRadius` tree-graph steps of `focusedKey`, mapped to their step distance. */
|
|
5826
|
+
function ringDistances(canonical, focusedKey, maxRadius) {
|
|
5827
|
+
const ring = /* @__PURE__ */ new Map();
|
|
5828
|
+
if (!canonical.has(focusedKey)) return ring;
|
|
5829
|
+
ring.set(focusedKey, 0);
|
|
5830
|
+
let frontier = [focusedKey];
|
|
5831
|
+
for (let distance = 1; distance <= maxRadius && frontier.length > 0; distance++) {
|
|
5832
|
+
const next = [];
|
|
5833
|
+
for (const key of frontier) {
|
|
5834
|
+
const entry = canonical.get(key);
|
|
5835
|
+
if (!entry) continue;
|
|
5836
|
+
const neighbours = [];
|
|
5837
|
+
if (entry.parentKey !== void 0) neighbours.push(entry.parentKey);
|
|
5838
|
+
for (const child of entry.node.children ?? []) neighbours.push(child.item.key);
|
|
5839
|
+
for (const neighbour of neighbours) if (!ring.has(neighbour) && canonical.has(neighbour)) {
|
|
5840
|
+
ring.set(neighbour, distance);
|
|
5841
|
+
next.push(neighbour);
|
|
5842
|
+
}
|
|
5843
|
+
}
|
|
5844
|
+
frontier = next;
|
|
5845
|
+
}
|
|
5846
|
+
return ring;
|
|
5847
|
+
}
|
|
5848
|
+
/**
|
|
5849
|
+
* Compute the visible nodes/edges of a hyperbolic tree view: every
|
|
5850
|
+
* currently-loaded node within `maxRadius` tree-graph steps of the focused
|
|
5851
|
+
* node, projected onto the Poincaré disk with the focus at its center.
|
|
5852
|
+
*
|
|
5853
|
+
* @param root True tree root. `undefined` yields an empty layout.
|
|
5854
|
+
* @param options `focusedKey` (default the true root; unknown keys fall back
|
|
5855
|
+
* to the true root), `maxRadius` (default unlimited), `maxDepth` (safety
|
|
5856
|
+
* cap on canonical-position recursion depth, default 24), `centerOverride`
|
|
5857
|
+
* (project around this canonical-space point instead of the focus node's
|
|
5858
|
+
* own position — used to animate smoothly between foci).
|
|
5859
|
+
*/
|
|
5860
|
+
function computeHyperbolicLayout(root, options = {}) {
|
|
5861
|
+
if (!root) return {
|
|
5862
|
+
nodes: [],
|
|
5863
|
+
edges: []
|
|
5864
|
+
};
|
|
5865
|
+
const { maxRadius = Number.POSITIVE_INFINITY, maxDepth = 24, curvature = 1 } = options;
|
|
5866
|
+
const canonical = computeCanonicalPositions(root, maxDepth, curvature);
|
|
5867
|
+
const focusedKey = options.focusedKey !== void 0 && canonical.has(options.focusedKey) ? options.focusedKey : root.item.key;
|
|
5868
|
+
const focusEntry = canonical.get(focusedKey);
|
|
5869
|
+
if (!focusEntry) return {
|
|
5870
|
+
nodes: [],
|
|
5871
|
+
edges: []
|
|
5872
|
+
};
|
|
5873
|
+
const projectionCenter = options.centerOverride ?? focusEntry.position;
|
|
5874
|
+
const ring = ringDistances(canonical, focusedKey, maxRadius);
|
|
5875
|
+
const rawWeights = [...ring.keys()].map((key) => canonical.get(key).node.item.weight).filter((w) => typeof w === `number`);
|
|
5876
|
+
const minWeight = rawWeights.length > 0 ? Math.min(...rawWeights) : void 0;
|
|
5877
|
+
const maxWeight = rawWeights.length > 0 ? Math.max(...rawWeights) : void 0;
|
|
5878
|
+
const normalizeWeight = (weight) => {
|
|
5879
|
+
if (weight === void 0 || minWeight === void 0 || maxWeight === void 0 || maxWeight === minWeight) return .5;
|
|
5880
|
+
return (weight - minWeight) / (maxWeight - minWeight);
|
|
5881
|
+
};
|
|
5882
|
+
const nodes = [...ring.entries()].map(([key, distance]) => {
|
|
5883
|
+
const entry = canonical.get(key);
|
|
5884
|
+
const weight = entry.node.item.weight;
|
|
5885
|
+
return {
|
|
5886
|
+
node: entry.node,
|
|
5887
|
+
key,
|
|
5888
|
+
depth: entry.depth,
|
|
5889
|
+
ring: distance,
|
|
5890
|
+
parentKey: entry.parentKey,
|
|
5891
|
+
position: mobiusToOrigin(entry.position, projectionCenter),
|
|
5892
|
+
angularShare: entry.angularShare,
|
|
5893
|
+
weight,
|
|
5894
|
+
weightNormalized: normalizeWeight(weight)
|
|
5895
|
+
};
|
|
5896
|
+
}).sort((a, b) => a.ring - b.ring);
|
|
5897
|
+
const byKey = new Map(nodes.map((n) => [n.key, n]));
|
|
5898
|
+
const edges = [];
|
|
5899
|
+
for (const n of nodes) {
|
|
5900
|
+
if (n.parentKey === void 0) continue;
|
|
5901
|
+
const parent = byKey.get(n.parentKey);
|
|
5902
|
+
if (!parent) continue;
|
|
5903
|
+
edges.push({
|
|
5904
|
+
fromKey: parent.key,
|
|
5905
|
+
toKey: n.key,
|
|
5906
|
+
from: parent.position,
|
|
5907
|
+
to: n.position,
|
|
5908
|
+
weightNormalized: (parent.weightNormalized + n.weightNormalized) / 2
|
|
5909
|
+
});
|
|
5910
|
+
}
|
|
5911
|
+
return {
|
|
5912
|
+
nodes,
|
|
5913
|
+
edges
|
|
5914
|
+
};
|
|
5915
|
+
}
|
|
5916
|
+
//#endregion
|
|
5917
|
+
//#region src/hyperbolic-tree/hyperbolictree-list.ts
|
|
5918
|
+
/**
|
|
5919
|
+
* The Poincaré disk's math operates in a `-1..1` unit radius, but rendering
|
|
5920
|
+
* an SVG `viewBox` at that literal scale (then letting CSS stretch it to a
|
|
5921
|
+
* few hundred device pixels) is an extreme, unusual upscale factor that has
|
|
5922
|
+
* been observed to trigger a Chromium GPU-rasterization glitch — a `fill:
|
|
5923
|
+
* none` shape painting as solid-filled, matching its computed style exactly
|
|
5924
|
+
* but not what's on screen. Rendering in a "normal"-magnitude SVG coordinate
|
|
5925
|
+
* system (viewBox radius {@link RENDER_SCALE} instead of `1`) avoids it.
|
|
5926
|
+
*/
|
|
5927
|
+
const RENDER_SCALE = 500;
|
|
5928
|
+
/**
|
|
5929
|
+
* Sanitizes a node key into a token safe to use inside a `part` attribute /
|
|
5930
|
+
* `::part()` selector — arbitrary key characters (spaces, quotes, etc.)
|
|
5931
|
+
* would otherwise produce a token CSS can't select without bespoke escaping.
|
|
5932
|
+
*/
|
|
5933
|
+
function partToken(key) {
|
|
5934
|
+
return key.replace(/[^\w-]/g, `-`);
|
|
5935
|
+
}
|
|
5936
|
+
/** Angular distance (radians, 0–π) of a layout node from screen-"up" (−π/2). */
|
|
5937
|
+
function angleDistanceFromUp(n) {
|
|
5938
|
+
const angle = Math.atan2(n.position.y, n.position.x);
|
|
5939
|
+
let diff = Math.abs(angle - -Math.PI / 2);
|
|
5940
|
+
if (diff > Math.PI) diff = 2 * Math.PI - diff;
|
|
5941
|
+
return diff;
|
|
5942
|
+
}
|
|
5943
|
+
let HyperbolicTreeListElement = class HyperbolicTreeListElement extends LitElement {
|
|
5944
|
+
constructor(..._args) {
|
|
5945
|
+
super(..._args);
|
|
5946
|
+
this.exclusivity = `none`;
|
|
5947
|
+
this.selectionFilter = `all`;
|
|
5948
|
+
this.interactionMode = `standard`;
|
|
5949
|
+
this.maxRadius = 4;
|
|
5950
|
+
this.nodeRadius = .05;
|
|
5951
|
+
this.minNodeRadius = .015;
|
|
5952
|
+
this.minWeightScale = .6;
|
|
5953
|
+
this.maxWeightScale = 1.6;
|
|
5954
|
+
this.zoom = 1;
|
|
5955
|
+
this.curvature = 1;
|
|
5956
|
+
this.focusAnimationDuration = 400;
|
|
5957
|
+
this._selectedNodes = /* @__PURE__ */ new Set();
|
|
5958
|
+
this.disableKeyboardNavigation = false;
|
|
5959
|
+
this.registry = void 0;
|
|
5960
|
+
this._focusKey = void 0;
|
|
5961
|
+
this._animCenter = void 0;
|
|
5962
|
+
this._panOffset = {
|
|
5963
|
+
x: 0,
|
|
5964
|
+
y: 0
|
|
5965
|
+
};
|
|
5966
|
+
this.search = new ListSearchController(this, { adapter: createTreeListSearchAdapter(this, {
|
|
5967
|
+
labelSelector: `.hyperbolictree-label`,
|
|
5968
|
+
highlightKey: `incr-search-highlight`
|
|
5969
|
+
}) });
|
|
5970
|
+
this.tickled = new TickledController(this, {
|
|
5971
|
+
getItems: () => this.#layout().nodes.map((n) => ({
|
|
5972
|
+
id: n.key,
|
|
5973
|
+
enabled: true
|
|
5974
|
+
})),
|
|
5975
|
+
onStateChange: () => {
|
|
5976
|
+
this.setAttribute(`data-input-mode`, this.tickled.inputMode);
|
|
5977
|
+
},
|
|
5978
|
+
onTickle: (id) => {
|
|
5979
|
+
if (id === void 0) return;
|
|
5980
|
+
const n = this.#layout().nodes.find((n) => n.key === id);
|
|
5981
|
+
if (!n) return;
|
|
5982
|
+
this.dispatchEvent(new CustomEvent(`tickle`, {
|
|
5983
|
+
detail: {
|
|
5984
|
+
node: n.node,
|
|
5985
|
+
depth: n.depth
|
|
5986
|
+
},
|
|
5987
|
+
bubbles: true,
|
|
5988
|
+
composed: true
|
|
5989
|
+
}));
|
|
5990
|
+
}
|
|
5991
|
+
});
|
|
5992
|
+
this.#pendingKeys = /* @__PURE__ */ new Set();
|
|
5993
|
+
this.#aborters = /* @__PURE__ */ new Map();
|
|
5994
|
+
}
|
|
5995
|
+
static {
|
|
5996
|
+
this.shadowRootOptions = {
|
|
5997
|
+
...LitElement.shadowRootOptions,
|
|
5998
|
+
delegatesFocus: true
|
|
5999
|
+
};
|
|
6000
|
+
}
|
|
6001
|
+
get selectedNodes() {
|
|
6002
|
+
return this._selectedNodes;
|
|
6003
|
+
}
|
|
6004
|
+
/** The node currently centered on the disk. Pure view-state — `root` is never mutated by focus changes. */
|
|
6005
|
+
get focusedNode() {
|
|
6006
|
+
if (this._focusKey === void 0) return this.#effectiveRoot;
|
|
6007
|
+
return findHyperbolicNode(this.#effectiveRoot, this._focusKey) ?? this.#effectiveRoot;
|
|
6008
|
+
}
|
|
6009
|
+
#modelUnsubscribe;
|
|
6010
|
+
#pendingKeys;
|
|
6011
|
+
#aborters;
|
|
6012
|
+
#ownedRegistry;
|
|
6013
|
+
#unregisterSearchCommands;
|
|
6014
|
+
#animFrame;
|
|
6015
|
+
#panStart;
|
|
6016
|
+
get #effectiveRoot() {
|
|
6017
|
+
return this.model?.root ?? this.root;
|
|
6018
|
+
}
|
|
6019
|
+
#layout() {
|
|
6020
|
+
return computeHyperbolicLayout(this.#effectiveRoot, {
|
|
6021
|
+
focusedKey: this._focusKey,
|
|
6022
|
+
maxRadius: this.maxRadius,
|
|
6023
|
+
curvature: this.curvature,
|
|
6024
|
+
centerOverride: this._panOffset.x === 0 && this._panOffset.y === 0 && this._animCenter === void 0 ? void 0 : this.#effectiveCenter()
|
|
6025
|
+
});
|
|
6026
|
+
}
|
|
6027
|
+
#effectiveCenter() {
|
|
6028
|
+
const canonical = computeCanonicalPositions(this.#effectiveRoot, 24, this.curvature);
|
|
6029
|
+
const focusKey = this._focusKey ?? this.#effectiveRoot?.item.key;
|
|
6030
|
+
const base = this._animCenter ?? (focusKey === void 0 ? void 0 : canonical.get(focusKey)?.position);
|
|
6031
|
+
if (base === void 0) return void 0;
|
|
6032
|
+
if (this._panOffset.x === 0 && this._panOffset.y === 0) return base;
|
|
6033
|
+
return clampToDisk({
|
|
6034
|
+
x: base.x + this._panOffset.x,
|
|
6035
|
+
y: base.y + this._panOffset.y
|
|
6036
|
+
}, .95);
|
|
6037
|
+
}
|
|
6038
|
+
connectedCallback() {
|
|
6039
|
+
super.connectedCallback();
|
|
6040
|
+
this.setAttribute(`data-input-mode`, `mouse`);
|
|
6041
|
+
const registry = this.#ownedRegistry = createCommandRegistry();
|
|
6042
|
+
registry.register({
|
|
6043
|
+
id: `select-all`,
|
|
6044
|
+
label: `Select All`,
|
|
6045
|
+
description: `Select all visible items`,
|
|
6046
|
+
execute: () => this.selectAll()
|
|
6047
|
+
});
|
|
6048
|
+
registry.register({
|
|
6049
|
+
id: `deselect-all`,
|
|
6050
|
+
label: `Deselect All`,
|
|
6051
|
+
description: `Clear all selection`,
|
|
6052
|
+
execute: () => this.deselectAll()
|
|
6053
|
+
});
|
|
6054
|
+
this.registry = registry;
|
|
6055
|
+
this.#syncSearchCommands();
|
|
6056
|
+
}
|
|
6057
|
+
disconnectedCallback() {
|
|
6058
|
+
super.disconnectedCallback();
|
|
6059
|
+
this.#modelUnsubscribe?.();
|
|
6060
|
+
this.#modelUnsubscribe = void 0;
|
|
6061
|
+
for (const aborter of this.#aborters.values()) aborter.abort();
|
|
6062
|
+
this.#aborters.clear();
|
|
6063
|
+
this.#pendingKeys.clear();
|
|
6064
|
+
if (this.#animFrame !== void 0) {
|
|
6065
|
+
cancelAnimationFrame(this.#animFrame);
|
|
6066
|
+
this.#animFrame = void 0;
|
|
6067
|
+
}
|
|
6068
|
+
if (this.#ownedRegistry) {
|
|
6069
|
+
this.#ownedRegistry.unregister(`select-all`);
|
|
6070
|
+
this.#ownedRegistry.unregister(`deselect-all`);
|
|
6071
|
+
this.#ownedRegistry = void 0;
|
|
6072
|
+
this.registry = void 0;
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
#effectiveSearchModes() {
|
|
6076
|
+
const raw = this.searchModes?.trim();
|
|
6077
|
+
if (raw === void 0) return [...LIST_SEARCH_MODES];
|
|
6078
|
+
const requested = new Set(raw.split(/\s+/));
|
|
6079
|
+
return LIST_SEARCH_MODES.filter((m) => requested.has(m));
|
|
6080
|
+
}
|
|
6081
|
+
#syncSearchCommands() {
|
|
6082
|
+
if (!this.#ownedRegistry) return;
|
|
6083
|
+
this.search.setModes(this.#effectiveSearchModes());
|
|
6084
|
+
this.#unregisterSearchCommands?.();
|
|
6085
|
+
this.#unregisterSearchCommands = registerListSearchCommands(this.#ownedRegistry, this.search);
|
|
6086
|
+
}
|
|
6087
|
+
updated(changedProps) {
|
|
6088
|
+
if (changedProps.has(`root`) && this.root) this.#resetViewState();
|
|
6089
|
+
if (changedProps.has(`model`)) {
|
|
6090
|
+
this.#modelUnsubscribe?.();
|
|
6091
|
+
this.#modelUnsubscribe = void 0;
|
|
6092
|
+
if (this.model) {
|
|
6093
|
+
this.#modelUnsubscribe = this.model._subscribe((event) => {
|
|
6094
|
+
if (event.type === `root`) {
|
|
6095
|
+
this.#resetViewState();
|
|
6096
|
+
this.requestUpdate();
|
|
6097
|
+
} else if (event.type === `invalidate`) this.requestUpdate();
|
|
6098
|
+
});
|
|
6099
|
+
this.#resetViewState();
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
if (changedProps.has(`interactionMode`)) this.clearSelection();
|
|
6103
|
+
if (changedProps.has(`searchModes`)) this.#syncSearchCommands();
|
|
6104
|
+
if (this._focusKey !== void 0 && !findHyperbolicNode(this.#effectiveRoot, this._focusKey)) {
|
|
6105
|
+
this._focusKey = void 0;
|
|
6106
|
+
this._animCenter = void 0;
|
|
6107
|
+
}
|
|
6108
|
+
this.#ensureVisibleChildren();
|
|
6109
|
+
}
|
|
6110
|
+
#resetViewState() {
|
|
6111
|
+
this.#clearSelectionInternal();
|
|
6112
|
+
this.tickled.clearTickled();
|
|
6113
|
+
this._focusKey = void 0;
|
|
6114
|
+
this._animCenter = void 0;
|
|
6115
|
+
this._panOffset = {
|
|
6116
|
+
x: 0,
|
|
6117
|
+
y: 0
|
|
6118
|
+
};
|
|
6119
|
+
if (this.#animFrame !== void 0) {
|
|
6120
|
+
cancelAnimationFrame(this.#animFrame);
|
|
6121
|
+
this.#animFrame = void 0;
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
6124
|
+
#ensureVisibleChildren() {
|
|
6125
|
+
if (!this.model && !this.loadChildren) return;
|
|
6126
|
+
for (const n of this.#layout().nodes) {
|
|
6127
|
+
const { node } = n;
|
|
6128
|
+
if (node.children !== void 0 || this.#pendingKeys.has(node.item.key)) continue;
|
|
6129
|
+
this.#loadChildren(node, n.depth);
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
async #loadChildren(node, depth) {
|
|
6133
|
+
const key = node.item.key;
|
|
6134
|
+
this.#pendingKeys.add(key);
|
|
6135
|
+
const controller = new AbortController();
|
|
6136
|
+
this.#aborters.set(key, controller);
|
|
6137
|
+
this.requestUpdate();
|
|
6138
|
+
try {
|
|
6139
|
+
let children;
|
|
6140
|
+
if (this.model) children = await this.model.getChildren(node, depth, controller.signal);
|
|
6141
|
+
else children = [...await collectDataResult(this.loadChildren({
|
|
6142
|
+
node,
|
|
6143
|
+
depth
|
|
6144
|
+
}, controller.signal), controller.signal)];
|
|
6145
|
+
if (controller.signal.aborted) return;
|
|
6146
|
+
node.children = children;
|
|
6147
|
+
} catch {
|
|
6148
|
+
if (controller.signal.aborted) return;
|
|
6149
|
+
node.children = [];
|
|
6150
|
+
} finally {
|
|
6151
|
+
this.#pendingKeys.delete(key);
|
|
6152
|
+
this.#aborters.delete(key);
|
|
6153
|
+
if (!controller.signal.aborted) this.requestUpdate();
|
|
6154
|
+
}
|
|
6155
|
+
}
|
|
6156
|
+
#canSelect(node) {
|
|
6157
|
+
if (this.selectionFilter === `none`) return false;
|
|
6158
|
+
if (this.selectionFilter === `leaf`) return node.item.isLeaf === true;
|
|
6159
|
+
if (this.selectionFilter === `branch`) return node.item.isLeaf === false || node.children !== void 0 && node.children.length > 0;
|
|
6160
|
+
return true;
|
|
6161
|
+
}
|
|
6162
|
+
#clearSelectionInternal() {
|
|
6163
|
+
this._selectedNodes.clear();
|
|
6164
|
+
this.selectedNode = void 0;
|
|
6165
|
+
}
|
|
6166
|
+
#dispatchSelect(previous) {
|
|
6167
|
+
this.dispatchEvent(new CustomEvent(`select`, {
|
|
6168
|
+
detail: {
|
|
6169
|
+
selected: new Set(this._selectedNodes),
|
|
6170
|
+
previous
|
|
6171
|
+
},
|
|
6172
|
+
bubbles: true,
|
|
6173
|
+
composed: true
|
|
6174
|
+
}));
|
|
6175
|
+
}
|
|
6176
|
+
select(node) {
|
|
6177
|
+
if (!this.#canSelect(node)) return;
|
|
6178
|
+
const prev = new Set(this._selectedNodes);
|
|
6179
|
+
this._selectedNodes.clear();
|
|
6180
|
+
this._selectedNodes.add(node);
|
|
6181
|
+
this.selectedNode = node;
|
|
6182
|
+
this.#dispatchSelect(prev);
|
|
6183
|
+
this.requestUpdate();
|
|
6184
|
+
}
|
|
6185
|
+
deselect(node) {
|
|
6186
|
+
if (!this._selectedNodes.has(node)) return;
|
|
6187
|
+
const prev = new Set(this._selectedNodes);
|
|
6188
|
+
this._selectedNodes.delete(node);
|
|
6189
|
+
if (this.selectedNode === node) this.selectedNode = [...this._selectedNodes].at(-1);
|
|
6190
|
+
this.#dispatchSelect(prev);
|
|
6191
|
+
this.requestUpdate();
|
|
6192
|
+
}
|
|
6193
|
+
clearSelection() {
|
|
6194
|
+
if (this._selectedNodes.size === 0) return;
|
|
6195
|
+
const prev = new Set(this._selectedNodes);
|
|
6196
|
+
this.#clearSelectionInternal();
|
|
6197
|
+
this.#dispatchSelect(prev);
|
|
6198
|
+
this.requestUpdate();
|
|
6199
|
+
}
|
|
6200
|
+
selectAll() {
|
|
6201
|
+
const prev = new Set(this._selectedNodes);
|
|
6202
|
+
for (const n of this.#layout().nodes) {
|
|
6203
|
+
if (!this.#canSelect(n.node)) continue;
|
|
6204
|
+
this._selectedNodes.add(n.node);
|
|
6205
|
+
}
|
|
6206
|
+
this.selectedNode = [...this._selectedNodes].at(-1);
|
|
6207
|
+
this.#dispatchSelect(prev);
|
|
6208
|
+
this.requestUpdate();
|
|
6209
|
+
}
|
|
6210
|
+
deselectAll() {
|
|
6211
|
+
const prev = new Set(this._selectedNodes);
|
|
6212
|
+
this.#clearSelectionInternal();
|
|
6213
|
+
this.#dispatchSelect(prev);
|
|
6214
|
+
this.requestUpdate();
|
|
6215
|
+
}
|
|
6216
|
+
/** Replace the selection with `nodes` (subject to `selectionFilter`) — search "select" mode. */
|
|
6217
|
+
selectMany(nodes) {
|
|
6218
|
+
const prev = new Set(this._selectedNodes);
|
|
6219
|
+
this._selectedNodes = new Set([...nodes].filter((n) => this.#canSelect(n)));
|
|
6220
|
+
this.selectedNode = [...this._selectedNodes].at(-1);
|
|
6221
|
+
this.#dispatchSelect(prev);
|
|
6222
|
+
this.requestUpdate();
|
|
6223
|
+
}
|
|
6224
|
+
/**
|
|
6225
|
+
* Re-center the disk on `node` (an animated Möbius transform of the fixed
|
|
6226
|
+
* layout — `root` is never mutated). Fires `focus`, plus the semantic
|
|
6227
|
+
* `expand`/`collapse` event when the new focus is a descendant/ancestor of
|
|
6228
|
+
* the previous one, for interop with generic `TreeComponent` listeners.
|
|
6229
|
+
*/
|
|
6230
|
+
focusOn(node) {
|
|
6231
|
+
const targetKey = node.item.key;
|
|
6232
|
+
const currentFocusKey = this._focusKey ?? this.#effectiveRoot?.item.key;
|
|
6233
|
+
if (currentFocusKey === targetKey) return;
|
|
6234
|
+
const previousNode = currentFocusKey === void 0 ? void 0 : findHyperbolicNode(this.#effectiveRoot, currentFocusKey);
|
|
6235
|
+
const canonical = computeCanonicalPositions(this.#effectiveRoot, 24, this.curvature);
|
|
6236
|
+
const fromPosition = (currentFocusKey === void 0 ? void 0 : canonical.get(currentFocusKey)?.position) ?? {
|
|
6237
|
+
x: 0,
|
|
6238
|
+
y: 0
|
|
6239
|
+
};
|
|
6240
|
+
const toPosition = canonical.get(targetKey)?.position ?? {
|
|
6241
|
+
x: 0,
|
|
6242
|
+
y: 0
|
|
6243
|
+
};
|
|
6244
|
+
this._focusKey = targetKey;
|
|
6245
|
+
this._panOffset = {
|
|
6246
|
+
x: 0,
|
|
6247
|
+
y: 0
|
|
6248
|
+
};
|
|
6249
|
+
this.#animateTo(fromPosition, toPosition);
|
|
6250
|
+
this.dispatchEvent(new CustomEvent(`focus`, {
|
|
6251
|
+
detail: {
|
|
6252
|
+
node,
|
|
6253
|
+
previous: previousNode
|
|
6254
|
+
},
|
|
6255
|
+
bubbles: true,
|
|
6256
|
+
composed: true
|
|
6257
|
+
}));
|
|
6258
|
+
if (previousNode) {
|
|
6259
|
+
if (findHyperbolicNode(previousNode, targetKey)) {
|
|
6260
|
+
const depth = (findHyperbolicPath(this.#effectiveRoot, targetKey)?.length ?? 1) - 1;
|
|
6261
|
+
this.dispatchEvent(new CustomEvent(`expand`, {
|
|
6262
|
+
detail: {
|
|
6263
|
+
node,
|
|
6264
|
+
depth
|
|
6265
|
+
},
|
|
6266
|
+
bubbles: true,
|
|
6267
|
+
composed: true
|
|
6268
|
+
}));
|
|
6269
|
+
} else if (findHyperbolicNode(node, previousNode.item.key)) {
|
|
6270
|
+
const depth = (findHyperbolicPath(this.#effectiveRoot, previousNode.item.key)?.length ?? 1) - 1;
|
|
6271
|
+
this.dispatchEvent(new CustomEvent(`collapse`, {
|
|
6272
|
+
detail: {
|
|
6273
|
+
node: previousNode,
|
|
6274
|
+
depth
|
|
6275
|
+
},
|
|
6276
|
+
bubbles: true,
|
|
6277
|
+
composed: true
|
|
6278
|
+
}));
|
|
6279
|
+
}
|
|
6280
|
+
}
|
|
6281
|
+
this.requestUpdate();
|
|
6282
|
+
}
|
|
6283
|
+
#prefersReducedMotion() {
|
|
6284
|
+
return typeof matchMedia === `function` && matchMedia(`(prefers-reduced-motion: reduce)`).matches;
|
|
6285
|
+
}
|
|
6286
|
+
#animateTo(from, to) {
|
|
6287
|
+
if (this.#animFrame !== void 0) {
|
|
6288
|
+
cancelAnimationFrame(this.#animFrame);
|
|
6289
|
+
this.#animFrame = void 0;
|
|
6290
|
+
}
|
|
6291
|
+
if (this.focusAnimationDuration <= 0 || this.#prefersReducedMotion() || typeof requestAnimationFrame !== `function`) {
|
|
6292
|
+
this._animCenter = void 0;
|
|
6293
|
+
return;
|
|
6294
|
+
}
|
|
6295
|
+
const start = performance.now();
|
|
6296
|
+
const duration = this.focusAnimationDuration;
|
|
6297
|
+
const step = (now) => {
|
|
6298
|
+
const t = Math.min(1, (now - start) / duration);
|
|
6299
|
+
this._animCenter = t >= 1 ? void 0 : geodesicInterpolate(from, to, t);
|
|
6300
|
+
this.requestUpdate();
|
|
6301
|
+
this.#animFrame = t < 1 ? requestAnimationFrame(step) : void 0;
|
|
6302
|
+
};
|
|
6303
|
+
this.#animFrame = requestAnimationFrame(step);
|
|
6304
|
+
}
|
|
6305
|
+
#focusAndMaybeSelect(node) {
|
|
6306
|
+
this.focusOn(node);
|
|
6307
|
+
this.tickled.setTickled(node.item.key);
|
|
6308
|
+
if (this.interactionMode === `implicit`) this.select(node);
|
|
6309
|
+
}
|
|
6310
|
+
/** Focus + select the node at `keys` (path from the root's children). Returns `true` if found. */
|
|
6311
|
+
navigateTo(keys) {
|
|
6312
|
+
const root = this.#effectiveRoot;
|
|
6313
|
+
if (!root?.children) return false;
|
|
6314
|
+
let current = root;
|
|
6315
|
+
for (const key of keys) {
|
|
6316
|
+
const found = current.children?.find((n) => n.item.key === key);
|
|
6317
|
+
if (!found) return false;
|
|
6318
|
+
current = found;
|
|
5526
6319
|
}
|
|
5527
|
-
|
|
6320
|
+
this.focusOn(current);
|
|
6321
|
+
this.select(current);
|
|
6322
|
+
this.tickled.setTickled(current.item.key);
|
|
6323
|
+
this.requestUpdate();
|
|
6324
|
+
return true;
|
|
5528
6325
|
}
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
6326
|
+
/** Focus + select the first node for which `predicate` returns `true`. Returns `true` if found. */
|
|
6327
|
+
navigateToNode(predicate) {
|
|
6328
|
+
const root = this.#effectiveRoot;
|
|
6329
|
+
if (!root?.children) return false;
|
|
6330
|
+
const keyPath = this.#findKeyPath(root.children, predicate);
|
|
6331
|
+
if (!keyPath) return false;
|
|
6332
|
+
return this.navigateTo(keyPath);
|
|
6333
|
+
}
|
|
6334
|
+
/**
|
|
6335
|
+
* Bring the node with `key` into the visible window and move the keyboard
|
|
6336
|
+
* cursor to it, without changing the selection. Used by incremental-search
|
|
6337
|
+
* "navigate" mode.
|
|
6338
|
+
*/
|
|
6339
|
+
revealKey(key) {
|
|
6340
|
+
const node = findHyperbolicNode(this.#effectiveRoot, key);
|
|
6341
|
+
if (!node) return false;
|
|
6342
|
+
if (!this.#layout().nodes.some((n) => n.key === key)) this.focusOn(node);
|
|
6343
|
+
this.tickled.setTickled(key);
|
|
6344
|
+
this.requestUpdate();
|
|
6345
|
+
return true;
|
|
6346
|
+
}
|
|
6347
|
+
#findKeyPath(nodes, predicate, prefix = []) {
|
|
6348
|
+
for (const node of nodes) {
|
|
6349
|
+
const path = [...prefix, node.item.key];
|
|
6350
|
+
if (predicate(node)) return path;
|
|
6351
|
+
if (node.children?.length) {
|
|
6352
|
+
const hit = this.#findKeyPath(node.children, predicate, path);
|
|
6353
|
+
if (hit) return hit;
|
|
6354
|
+
}
|
|
5538
6355
|
}
|
|
5539
|
-
}
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
6356
|
+
}
|
|
6357
|
+
/**
|
|
6358
|
+
* All nodes currently laid out on the disk (within `maxRadius` of the
|
|
6359
|
+
* focus), in render (ring-major) order. Unlike `ixfx-icicletree-list`, the
|
|
6360
|
+
* focused node itself is included — it is literally rendered, at the
|
|
6361
|
+
* center.
|
|
6362
|
+
*/
|
|
6363
|
+
getNodes() {
|
|
6364
|
+
return this.#layout().nodes.map((n) => ({
|
|
6365
|
+
node: n.node,
|
|
6366
|
+
depth: n.depth
|
|
6367
|
+
}));
|
|
6368
|
+
}
|
|
6369
|
+
#matchesFilter(node, depth) {
|
|
6370
|
+
if (!this.filterPredicate) return true;
|
|
6371
|
+
if (this.filterPredicate(node, depth)) return true;
|
|
6372
|
+
return node.children?.some((child) => this.#matchesFilter(child, depth + 1)) ?? false;
|
|
6373
|
+
}
|
|
6374
|
+
#handleItemClick(n, event) {
|
|
6375
|
+
this.dispatchEvent(new CustomEvent(`item-click`, {
|
|
6376
|
+
detail: {
|
|
6377
|
+
node: n.node,
|
|
6378
|
+
depth: n.depth
|
|
5544
6379
|
},
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
}
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
6380
|
+
bubbles: true,
|
|
6381
|
+
composed: true
|
|
6382
|
+
}));
|
|
6383
|
+
if (this.interactionMode === `standard` && (event.ctrlKey || event.metaKey)) {
|
|
6384
|
+
if (this._selectedNodes.has(n.node)) this.deselect(n.node);
|
|
6385
|
+
else if (this.#canSelect(n.node)) {
|
|
6386
|
+
const prev = new Set(this._selectedNodes);
|
|
6387
|
+
this._selectedNodes.add(n.node);
|
|
6388
|
+
this.selectedNode = n.node;
|
|
6389
|
+
this.#dispatchSelect(prev);
|
|
6390
|
+
this.requestUpdate();
|
|
6391
|
+
}
|
|
6392
|
+
} else if (this.interactionMode !== `manual`) this.select(n.node);
|
|
6393
|
+
this.tickled.setTickled(n.key);
|
|
6394
|
+
this.focusOn(n.node);
|
|
6395
|
+
}
|
|
6396
|
+
#handleItemDblClick(n) {
|
|
6397
|
+
this.dispatchEvent(new CustomEvent(`item-dblclick`, {
|
|
6398
|
+
detail: {
|
|
6399
|
+
node: n.node,
|
|
6400
|
+
depth: n.depth
|
|
5551
6401
|
},
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
6402
|
+
bubbles: true,
|
|
6403
|
+
composed: true
|
|
6404
|
+
}));
|
|
6405
|
+
this.dispatchEvent(new CustomEvent(`activate`, {
|
|
6406
|
+
detail: {
|
|
6407
|
+
node: n.node,
|
|
6408
|
+
depth: n.depth
|
|
5555
6409
|
},
|
|
5556
|
-
|
|
5557
|
-
|
|
6410
|
+
bubbles: true,
|
|
6411
|
+
composed: true
|
|
6412
|
+
}));
|
|
6413
|
+
}
|
|
6414
|
+
#handleKeyDown(event) {
|
|
6415
|
+
if (this.disableKeyboardNavigation) return;
|
|
6416
|
+
const root = this.#effectiveRoot;
|
|
6417
|
+
const { nodes } = this.#layout();
|
|
6418
|
+
if (!root || nodes.length === 0) return;
|
|
6419
|
+
const focusNode = findHyperbolicNode(root, this._focusKey ?? root.item.key);
|
|
6420
|
+
if (!focusNode) return;
|
|
6421
|
+
switch (event.key) {
|
|
6422
|
+
case `ArrowUp`: {
|
|
6423
|
+
event.preventDefault();
|
|
6424
|
+
const children = nodes.filter((n) => n.parentKey === focusNode.item.key);
|
|
6425
|
+
if (children.length === 0) return;
|
|
6426
|
+
const best = children.reduce((a, b) => angleDistanceFromUp(a) < angleDistanceFromUp(b) ? a : b);
|
|
6427
|
+
this.#focusAndMaybeSelect(best.node);
|
|
6428
|
+
return;
|
|
6429
|
+
}
|
|
6430
|
+
case `ArrowDown`: {
|
|
6431
|
+
event.preventDefault();
|
|
6432
|
+
const path = findHyperbolicPath(root, focusNode.item.key);
|
|
6433
|
+
const parent = path && path.length >= 2 ? path[path.length - 2] : void 0;
|
|
6434
|
+
if (parent) this.#focusAndMaybeSelect(parent);
|
|
6435
|
+
return;
|
|
6436
|
+
}
|
|
6437
|
+
case `ArrowLeft`:
|
|
6438
|
+
case `ArrowRight`: {
|
|
6439
|
+
event.preventDefault();
|
|
6440
|
+
const path = findHyperbolicPath(root, focusNode.item.key);
|
|
6441
|
+
const siblings = (path && path.length >= 2 ? path[path.length - 2] : void 0)?.children ?? (focusNode === root ? [focusNode] : []);
|
|
6442
|
+
const index = siblings.findIndex((n) => n.item.key === focusNode.item.key);
|
|
6443
|
+
if (index === -1 || siblings.length <= 1) return;
|
|
6444
|
+
const next = siblings[(index + (event.key === `ArrowRight` ? 1 : -1) + siblings.length) % siblings.length];
|
|
6445
|
+
this.#focusAndMaybeSelect(next);
|
|
6446
|
+
return;
|
|
6447
|
+
}
|
|
6448
|
+
case `Enter`: {
|
|
6449
|
+
event.preventDefault();
|
|
6450
|
+
if (this.interactionMode !== `manual`) this.select(focusNode);
|
|
6451
|
+
const depth = (findHyperbolicPath(root, focusNode.item.key)?.length ?? 1) - 1;
|
|
6452
|
+
this.dispatchEvent(new CustomEvent(`activate`, {
|
|
6453
|
+
detail: {
|
|
6454
|
+
node: focusNode,
|
|
6455
|
+
depth
|
|
6456
|
+
},
|
|
6457
|
+
bubbles: true,
|
|
6458
|
+
composed: true
|
|
6459
|
+
}));
|
|
6460
|
+
return;
|
|
6461
|
+
}
|
|
6462
|
+
case `Escape`: if (this._focusKey !== void 0 && this._focusKey !== root.item.key) {
|
|
6463
|
+
event.preventDefault();
|
|
6464
|
+
this.focusOn(root);
|
|
6465
|
+
}
|
|
5558
6466
|
}
|
|
5559
|
-
}
|
|
5560
|
-
|
|
5561
|
-
|
|
6467
|
+
}
|
|
6468
|
+
#isBackgroundTarget(event) {
|
|
6469
|
+
const target = event.target;
|
|
6470
|
+
return target === event.currentTarget || target?.classList.contains(`disk-boundary`) === true;
|
|
6471
|
+
}
|
|
6472
|
+
#handleBackgroundPointerDown(event) {
|
|
6473
|
+
if (!this.#isBackgroundTarget(event)) return;
|
|
6474
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
6475
|
+
this.#panStart = {
|
|
6476
|
+
pointerId: event.pointerId,
|
|
6477
|
+
startClient: {
|
|
6478
|
+
x: event.clientX,
|
|
6479
|
+
y: event.clientY
|
|
6480
|
+
},
|
|
6481
|
+
baseOffset: this._panOffset
|
|
6482
|
+
};
|
|
6483
|
+
}
|
|
6484
|
+
#handleBackgroundPointerMove(event) {
|
|
6485
|
+
if (!this.#panStart || event.pointerId !== this.#panStart.pointerId) return;
|
|
6486
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
6487
|
+
const scale = 2.1 / (Math.min(rect.width, rect.height) || 1);
|
|
6488
|
+
const dx = (event.clientX - this.#panStart.startClient.x) * scale;
|
|
6489
|
+
const dy = (event.clientY - this.#panStart.startClient.y) * scale;
|
|
6490
|
+
this._panOffset = clampToDisk({
|
|
6491
|
+
x: this.#panStart.baseOffset.x - dx,
|
|
6492
|
+
y: this.#panStart.baseOffset.y - dy
|
|
6493
|
+
}, .9);
|
|
6494
|
+
this.requestUpdate();
|
|
6495
|
+
}
|
|
6496
|
+
#handleBackgroundPointerUp(event) {
|
|
6497
|
+
if (this.#panStart?.pointerId === event.pointerId) this.#panStart = void 0;
|
|
6498
|
+
}
|
|
6499
|
+
#defaultEdgeRenderer(edge) {
|
|
6500
|
+
const steps = 8;
|
|
6501
|
+
let d = ``;
|
|
6502
|
+
for (let i = 0; i <= steps; i++) {
|
|
6503
|
+
const p = geodesicInterpolate(edge.from, edge.to, i / steps);
|
|
6504
|
+
d += `${i === 0 ? `M` : `L`}${(p.x * RENDER_SCALE).toFixed(2)},${(p.y * RENDER_SCALE).toFixed(2)} `;
|
|
6505
|
+
}
|
|
6506
|
+
return svg`<path
|
|
6507
|
+
class="edge"
|
|
6508
|
+
d=${d}
|
|
6509
|
+
part="edge edge-to-${partToken(edge.toKey)} edge-from-${partToken(edge.fromKey)}"
|
|
6510
|
+
data-from=${edge.fromKey}
|
|
6511
|
+
data-to=${edge.toKey}
|
|
6512
|
+
style="--edge-weight: ${edge.weightNormalized}"
|
|
6513
|
+
/>`;
|
|
6514
|
+
}
|
|
6515
|
+
#renderEdge(edge, byKey) {
|
|
6516
|
+
if (this.edgeRenderer) {
|
|
6517
|
+
const fromNode = byKey.get(edge.fromKey)?.node;
|
|
6518
|
+
const toNode = byKey.get(edge.toKey)?.node;
|
|
6519
|
+
if (fromNode && toNode) return svg`<g transform="scale(${RENDER_SCALE})">${this.edgeRenderer({
|
|
6520
|
+
edge,
|
|
6521
|
+
fromNode,
|
|
6522
|
+
toNode
|
|
6523
|
+
})}</g>`;
|
|
6524
|
+
}
|
|
6525
|
+
return this.#defaultEdgeRenderer(edge);
|
|
6526
|
+
}
|
|
6527
|
+
/**
|
|
6528
|
+
* A node's rendered dot radius: shrinks with distance from focus (existing
|
|
6529
|
+
* behaviour), but also caps itself to the angular room {@link
|
|
6530
|
+
* HyperbolicLayoutNode.angularShare} actually gives it at its radius —
|
|
6531
|
+
* without this, a cluster of siblings squeezed into a narrow wedge (a
|
|
6532
|
+
* bushy ancestor elsewhere in the tree, or many siblings at this level)
|
|
6533
|
+
* all render at the same distance-only size and physically overlap into
|
|
6534
|
+
* an unreadable blob, however small {@link minNodeRadius} suppresses their
|
|
6535
|
+
* labels. `angularShare * distance` approximates the Euclidean arc length
|
|
6536
|
+
* available to the node; capping the diameter to a fraction of that keeps
|
|
6537
|
+
* a visible gap between crowded neighbours.
|
|
6538
|
+
*/
|
|
6539
|
+
#nodeRenderRadius(n) {
|
|
6540
|
+
const distance = Math.hypot(n.position.x, n.position.y);
|
|
6541
|
+
const scale = Math.max(.2, 1 - distance);
|
|
6542
|
+
const weightScale = this.minWeightScale + (this.maxWeightScale - this.minWeightScale) * n.weightNormalized;
|
|
6543
|
+
const radius = this.nodeRadius * scale * weightScale;
|
|
6544
|
+
if (distance < 1e-6) return radius;
|
|
6545
|
+
const arcLength = n.angularShare * distance;
|
|
6546
|
+
return Math.min(radius, Math.max(arcLength * .35, .003));
|
|
6547
|
+
}
|
|
6548
|
+
/**
|
|
6549
|
+
* Which nodes get a rendered label, greedily thinned to avoid overlap: a
|
|
6550
|
+
* per-node radius threshold alone ({@link minNodeRadius}) still lets many
|
|
6551
|
+
* same-sized nodes crowded into a small angular wedge all show labels that
|
|
6552
|
+
* visually collide. Nodes are considered in priority order (closer ring
|
|
6553
|
+
* first, then larger on-screen radius) and a label is kept only if its
|
|
6554
|
+
* position isn't too close to an already-kept one.
|
|
6555
|
+
*/
|
|
6556
|
+
#computeLabelKeys(nodes) {
|
|
6557
|
+
const minSpacing = .07;
|
|
6558
|
+
const prioritised = [...nodes.filter((n) => this.#nodeRenderRadius(n) >= this.minNodeRadius)].sort((a, b) => {
|
|
6559
|
+
if (a.ring !== b.ring) return a.ring - b.ring;
|
|
6560
|
+
return Math.hypot(a.position.x, a.position.y) - Math.hypot(b.position.x, b.position.y);
|
|
6561
|
+
});
|
|
6562
|
+
const kept = [];
|
|
6563
|
+
const keys = /* @__PURE__ */ new Set();
|
|
6564
|
+
for (const n of prioritised) if (!kept.some((k) => Math.hypot(k.position.x - n.position.x, k.position.y - n.position.y) < minSpacing)) {
|
|
6565
|
+
kept.push(n);
|
|
6566
|
+
keys.add(n.key);
|
|
6567
|
+
}
|
|
6568
|
+
return keys;
|
|
6569
|
+
}
|
|
6570
|
+
#renderNode(n, siblingGroups, labelKeys) {
|
|
6571
|
+
const { node, key, position } = n;
|
|
6572
|
+
const selected = this._selectedNodes.has(node);
|
|
6573
|
+
const tickled = this.tickled.isTickled(key);
|
|
6574
|
+
const filtered = !this.#matchesFilter(node, n.depth);
|
|
6575
|
+
const pending = this.#pendingKeys.has(key);
|
|
6576
|
+
const isFocused = n.ring === 0;
|
|
6577
|
+
const radius = this.#nodeRenderRadius(n);
|
|
6578
|
+
const showLabel = labelKeys.has(key);
|
|
6579
|
+
const siblings = siblingGroups.get(n.parentKey) ?? [n];
|
|
6580
|
+
const index = siblings.findIndex((s) => s.key === key);
|
|
6581
|
+
const scaledRadius = radius * RENDER_SCALE;
|
|
6582
|
+
const weightStyle = `--node-weight: ${n.weightNormalized};${n.weight === void 0 ? `` : ` --node-weight-raw: ${n.weight};`}`;
|
|
6583
|
+
return svg`
|
|
6584
|
+
<g
|
|
6585
|
+
class="node ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${filtered ? `filtered` : ``} ${pending ? `loading` : ``} ${isFocused ? `focused` : ``}"
|
|
6586
|
+
data-key=${key}
|
|
6587
|
+
role="treeitem"
|
|
6588
|
+
tabindex="-1"
|
|
6589
|
+
aria-level=${n.depth + 1}
|
|
6590
|
+
aria-selected=${selected}
|
|
6591
|
+
aria-setsize=${siblings.length}
|
|
6592
|
+
aria-posinset=${index + 1}
|
|
6593
|
+
aria-label=${node.item.label}
|
|
6594
|
+
part="node node-${partToken(key)}"
|
|
6595
|
+
style=${weightStyle}
|
|
6596
|
+
transform="translate(${position.x * RENDER_SCALE} ${position.y * RENDER_SCALE})"
|
|
6597
|
+
@pointerdown=${(e) => e.stopPropagation()}
|
|
6598
|
+
@click=${(e) => {
|
|
6599
|
+
e.stopPropagation();
|
|
6600
|
+
this.#handleItemClick(n, e);
|
|
6601
|
+
}}
|
|
6602
|
+
@dblclick=${(e) => {
|
|
6603
|
+
e.stopPropagation();
|
|
6604
|
+
this.#handleItemDblClick(n);
|
|
6605
|
+
}}
|
|
6606
|
+
@pointerenter=${() => this.tickled.handlePointerEnter(key)}
|
|
6607
|
+
>
|
|
6608
|
+
<circle class="node-hit" r=${Math.max(scaledRadius, .035 * RENDER_SCALE)} part="hit" />
|
|
6609
|
+
<circle class="node-dot" r=${scaledRadius} part="dot" />
|
|
6610
|
+
${showLabel ? svg`<text class="hyperbolictree-label" part="label" x=${scaledRadius + .01 * RENDER_SCALE} y="0" dominant-baseline="middle">${node.item.label}</text>` : nothing}
|
|
6611
|
+
</g>
|
|
6612
|
+
`;
|
|
6613
|
+
}
|
|
6614
|
+
render() {
|
|
6615
|
+
const { nodes, edges } = this.#layout();
|
|
6616
|
+
const byKey = new Map(nodes.map((n) => [n.key, n]));
|
|
6617
|
+
const siblingGroups = /* @__PURE__ */ new Map();
|
|
6618
|
+
for (const n of nodes) {
|
|
6619
|
+
const group = siblingGroups.get(n.parentKey) ?? [];
|
|
6620
|
+
group.push(n);
|
|
6621
|
+
siblingGroups.set(n.parentKey, group);
|
|
6622
|
+
}
|
|
6623
|
+
const labelKeys = this.#computeLabelKeys(nodes);
|
|
6624
|
+
return html`
|
|
6625
|
+
<svg
|
|
6626
|
+
class="hyperbolictree"
|
|
6627
|
+
viewBox="${-1.05 * RENDER_SCALE} ${-1.05 * RENDER_SCALE} ${2.1 * RENDER_SCALE} ${2.1 * RENDER_SCALE}"
|
|
6628
|
+
preserveAspectRatio="xMidYMid meet"
|
|
6629
|
+
role="tree"
|
|
6630
|
+
tabindex="0"
|
|
6631
|
+
data-input-mode=${this.tickled.inputMode}
|
|
6632
|
+
@keydown=${(e) => this.#handleKeyDown(e)}
|
|
6633
|
+
@pointerdown=${(e) => this.#handleBackgroundPointerDown(e)}
|
|
6634
|
+
@pointermove=${(e) => this.#handleBackgroundPointerMove(e)}
|
|
6635
|
+
@pointerup=${(e) => this.#handleBackgroundPointerUp(e)}
|
|
6636
|
+
@pointerleave=${() => this.tickled.clearTickled()}
|
|
6637
|
+
>
|
|
6638
|
+
<g class="zoom" transform="scale(${this.zoom})">
|
|
6639
|
+
<circle class="disk-boundary" part="boundary" cx="0" cy="0" r=${RENDER_SCALE} />
|
|
6640
|
+
<g class="edges" part="edges">
|
|
6641
|
+
${repeat(edges, (e) => `${e.fromKey}->${e.toKey}`, (e) => this.#renderEdge(e, byKey))}
|
|
6642
|
+
</g>
|
|
6643
|
+
<g class="nodes" part="nodes">
|
|
6644
|
+
${repeat(nodes, (n) => n.key, (n) => this.#renderNode(n, siblingGroups, labelKeys))}
|
|
6645
|
+
</g>
|
|
6646
|
+
</g>
|
|
6647
|
+
</svg>
|
|
6648
|
+
${this.search.renderField()}
|
|
6649
|
+
`;
|
|
6650
|
+
}
|
|
6651
|
+
static {
|
|
6652
|
+
this.styles = [
|
|
6653
|
+
themeFallbacks,
|
|
6654
|
+
listSearchStyles,
|
|
6655
|
+
css`
|
|
6656
|
+
:host {
|
|
6657
|
+
--hyperbolictree-node-color: var(--surface-4);
|
|
6658
|
+
--hyperbolictree-node-text: var(--surface-3-text);
|
|
6659
|
+
--hyperbolictree-edge-color: var(--surface-6);
|
|
6660
|
+
--hyperbolictree-boundary-color: var(--border-subtle);
|
|
6661
|
+
--hyperbolictree-font-size: 25px;
|
|
6662
|
+
|
|
6663
|
+
display: block;
|
|
6664
|
+
overflow: hidden;
|
|
6665
|
+
outline: none;
|
|
6666
|
+
overscroll-behavior: contain;
|
|
6667
|
+
}
|
|
6668
|
+
|
|
6669
|
+
/*
|
|
6670
|
+
* delegatesFocus means the host itself reports as focused whenever
|
|
6671
|
+
* the shadow DOM's focused descendant (the svg) is — without this,
|
|
6672
|
+
* the host also picks up the browser's own default focus ring/fill
|
|
6673
|
+
* sized to the whole box.
|
|
6674
|
+
*/
|
|
6675
|
+
:host(:focus),
|
|
6676
|
+
:host(:focus-visible) {
|
|
6677
|
+
outline: none;
|
|
6678
|
+
}
|
|
6679
|
+
|
|
6680
|
+
.hyperbolictree {
|
|
6681
|
+
display: block;
|
|
6682
|
+
width: 100%;
|
|
6683
|
+
height: 100%;
|
|
6684
|
+
outline: none;
|
|
6685
|
+
touch-action: none;
|
|
6686
|
+
overscroll-behavior: contain;
|
|
6687
|
+
}
|
|
6688
|
+
|
|
6689
|
+
.disk-boundary {
|
|
6690
|
+
fill: none;
|
|
6691
|
+
stroke: var(--hyperbolictree-boundary-color);
|
|
6692
|
+
stroke-width: 1;
|
|
6693
|
+
}
|
|
6694
|
+
|
|
6695
|
+
.edge {
|
|
6696
|
+
fill: none;
|
|
6697
|
+
stroke: var(--hyperbolictree-edge-color);
|
|
6698
|
+
/* --edge-weight (0..1, average of the endpoint nodes' --node-weight) thickens heavier edges. */
|
|
6699
|
+
stroke-width: calc(1px + var(--edge-weight, 0.5) * 3px);
|
|
6700
|
+
}
|
|
6701
|
+
|
|
6702
|
+
.node {
|
|
6703
|
+
cursor: pointer;
|
|
6704
|
+
}
|
|
6705
|
+
|
|
6706
|
+
.node-hit {
|
|
6707
|
+
fill: transparent;
|
|
6708
|
+
pointer-events: all;
|
|
6709
|
+
}
|
|
6710
|
+
|
|
6711
|
+
.node-dot {
|
|
6712
|
+
/* --node-weight (0..1, normalized across the visible node set) tints toward --accent for heavier nodes. */
|
|
6713
|
+
fill: color-mix(in oklab, var(--hyperbolictree-node-color), var(--accent) calc(var(--node-weight, 0.5) * 50%));
|
|
6714
|
+
stroke: var(--surface-2);
|
|
6715
|
+
stroke-width: 1;
|
|
6716
|
+
pointer-events: none;
|
|
6717
|
+
}
|
|
6718
|
+
|
|
6719
|
+
.node.tickled .node-dot {
|
|
6720
|
+
fill: var(--item-bg-tickled);
|
|
6721
|
+
}
|
|
6722
|
+
|
|
6723
|
+
.node.selected .node-dot {
|
|
6724
|
+
fill: var(--item-bg-selected);
|
|
6725
|
+
}
|
|
6726
|
+
|
|
6727
|
+
.node.selected.tickled .node-dot {
|
|
6728
|
+
fill: var(--item-bg-selected-tickled);
|
|
6729
|
+
}
|
|
6730
|
+
|
|
6731
|
+
.node.focused .node-dot {
|
|
6732
|
+
stroke: var(--accent);
|
|
6733
|
+
stroke-width: 3;
|
|
6734
|
+
}
|
|
6735
|
+
|
|
6736
|
+
.node.filtered {
|
|
6737
|
+
opacity: 0.3;
|
|
6738
|
+
}
|
|
6739
|
+
|
|
6740
|
+
.node.loading .node-dot {
|
|
6741
|
+
opacity: 0.6;
|
|
6742
|
+
}
|
|
6743
|
+
|
|
6744
|
+
.hyperbolictree-label {
|
|
6745
|
+
font-size: var(--hyperbolictree-font-size);
|
|
6746
|
+
fill: var(--hyperbolictree-node-text);
|
|
6747
|
+
cursor: pointer;
|
|
6748
|
+
user-select: none;
|
|
6749
|
+
}
|
|
6750
|
+
`
|
|
6751
|
+
];
|
|
6752
|
+
}
|
|
6753
|
+
};
|
|
6754
|
+
__decorate([property({ type: Object })], HyperbolicTreeListElement.prototype, "root", void 0);
|
|
6755
|
+
__decorate([property({ attribute: false })], HyperbolicTreeListElement.prototype, "model", void 0);
|
|
6756
|
+
__decorate([property({ attribute: false })], HyperbolicTreeListElement.prototype, "loadChildren", void 0);
|
|
6757
|
+
__decorate([property({
|
|
6758
|
+
reflect: true,
|
|
6759
|
+
attribute: `exclusivity`
|
|
6760
|
+
})], HyperbolicTreeListElement.prototype, "exclusivity", void 0);
|
|
6761
|
+
__decorate([property({
|
|
6762
|
+
reflect: true,
|
|
6763
|
+
attribute: `selection-filter`
|
|
6764
|
+
})], HyperbolicTreeListElement.prototype, "selectionFilter", void 0);
|
|
6765
|
+
__decorate([property({
|
|
6766
|
+
type: String,
|
|
6767
|
+
attribute: `interaction-mode`
|
|
6768
|
+
})], HyperbolicTreeListElement.prototype, "interactionMode", void 0);
|
|
6769
|
+
__decorate([property({
|
|
6770
|
+
type: Number,
|
|
6771
|
+
attribute: `max-radius`
|
|
6772
|
+
})], HyperbolicTreeListElement.prototype, "maxRadius", void 0);
|
|
6773
|
+
__decorate([property({
|
|
6774
|
+
type: Number,
|
|
6775
|
+
attribute: `node-radius`
|
|
6776
|
+
})], HyperbolicTreeListElement.prototype, "nodeRadius", void 0);
|
|
6777
|
+
__decorate([property({
|
|
6778
|
+
type: Number,
|
|
6779
|
+
attribute: `min-node-radius`
|
|
6780
|
+
})], HyperbolicTreeListElement.prototype, "minNodeRadius", void 0);
|
|
6781
|
+
__decorate([property({
|
|
6782
|
+
type: Number,
|
|
6783
|
+
attribute: `min-weight-scale`
|
|
6784
|
+
})], HyperbolicTreeListElement.prototype, "minWeightScale", void 0);
|
|
6785
|
+
__decorate([property({
|
|
6786
|
+
type: Number,
|
|
6787
|
+
attribute: `max-weight-scale`
|
|
6788
|
+
})], HyperbolicTreeListElement.prototype, "maxWeightScale", void 0);
|
|
6789
|
+
__decorate([property({ type: Number })], HyperbolicTreeListElement.prototype, "zoom", void 0);
|
|
6790
|
+
__decorate([property({ type: Number })], HyperbolicTreeListElement.prototype, "curvature", void 0);
|
|
6791
|
+
__decorate([property({
|
|
6792
|
+
type: Number,
|
|
6793
|
+
attribute: `focus-animation-duration`
|
|
6794
|
+
})], HyperbolicTreeListElement.prototype, "focusAnimationDuration", void 0);
|
|
6795
|
+
__decorate([property({ attribute: false })], HyperbolicTreeListElement.prototype, "edgeRenderer", void 0);
|
|
6796
|
+
__decorate([property({ attribute: false })], HyperbolicTreeListElement.prototype, "selectedNode", void 0);
|
|
6797
|
+
__decorate([state()], HyperbolicTreeListElement.prototype, "_selectedNodes", void 0);
|
|
6798
|
+
__decorate([property({ attribute: false })], HyperbolicTreeListElement.prototype, "filterPredicate", void 0);
|
|
6799
|
+
__decorate([property({
|
|
6800
|
+
type: Boolean,
|
|
6801
|
+
attribute: `disable-keyboard-navigation`
|
|
6802
|
+
})], HyperbolicTreeListElement.prototype, "disableKeyboardNavigation", void 0);
|
|
6803
|
+
__decorate([property({ attribute: false })], HyperbolicTreeListElement.prototype, "registry", void 0);
|
|
6804
|
+
__decorate([property({
|
|
6805
|
+
type: String,
|
|
6806
|
+
attribute: `search-modes`,
|
|
6807
|
+
reflect: true
|
|
6808
|
+
})], HyperbolicTreeListElement.prototype, "searchModes", void 0);
|
|
6809
|
+
__decorate([state()], HyperbolicTreeListElement.prototype, "_focusKey", void 0);
|
|
6810
|
+
__decorate([state()], HyperbolicTreeListElement.prototype, "_animCenter", void 0);
|
|
6811
|
+
__decorate([state()], HyperbolicTreeListElement.prototype, "_panOffset", void 0);
|
|
6812
|
+
HyperbolicTreeListElement = __decorate([safeCustomElement(`ixfx-hyperbolictree-list`)], HyperbolicTreeListElement);
|
|
5562
6813
|
//#endregion
|
|
5563
6814
|
//#region src/icicle-tree/layout.ts
|
|
5564
6815
|
/** Coerce a raw accessor result to a non-negative finite weight. */
|
|
@@ -6923,8 +8174,7 @@ let MulticolListElement = class MulticolListElement extends LitElement {
|
|
|
6923
8174
|
display: flex;
|
|
6924
8175
|
flex-direction: row;
|
|
6925
8176
|
align-items: stretch;
|
|
6926
|
-
overflow
|
|
6927
|
-
overflow-y: hidden;
|
|
8177
|
+
overflow: hidden;
|
|
6928
8178
|
position: relative;
|
|
6929
8179
|
height: 100%;
|
|
6930
8180
|
}
|
|
@@ -7545,7 +8795,16 @@ let MulticolListElement = class MulticolListElement extends LitElement {
|
|
|
7545
8795
|
let headerWidth = 0;
|
|
7546
8796
|
if (headerEl) {
|
|
7547
8797
|
const labelBtn = headerEl.querySelector(`.column-label`);
|
|
7548
|
-
if (labelBtn)
|
|
8798
|
+
if (labelBtn) {
|
|
8799
|
+
const labelTextWidth = measure(labelBtn.textContent ?? ``);
|
|
8800
|
+
const btnStyle = getComputedStyle(labelBtn);
|
|
8801
|
+
const labelBtnPaddingH = Number.parseFloat(btnStyle.paddingLeft) + Number.parseFloat(btnStyle.paddingRight);
|
|
8802
|
+
const burgerBtn = headerEl.querySelector(`.burger-btn`);
|
|
8803
|
+
const burgerWidth = burgerBtn ? Number.parseFloat(getComputedStyle(burgerBtn).width) : 0;
|
|
8804
|
+
const headerStyle = getComputedStyle(headerEl);
|
|
8805
|
+
const gap = Number.parseFloat(headerStyle.gap) || 0;
|
|
8806
|
+
headerWidth = labelTextWidth + labelBtnPaddingH + burgerWidth + gap;
|
|
8807
|
+
}
|
|
7549
8808
|
}
|
|
7550
8809
|
const neededWidth = Math.max(headerWidth, contentWidth + 24 + 16);
|
|
7551
8810
|
const clampedWidth = Math.max(minWidth, Math.min(maxWidth, Math.ceil(neededWidth)));
|
|
@@ -12919,6 +14178,6 @@ function init() {
|
|
|
12919
14178
|
console.log(`Init`);
|
|
12920
14179
|
}
|
|
12921
14180
|
//#endregion
|
|
12922
|
-
export { AcTextElement, AcTokenElement, BottomDrawerElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DataTableElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GridListElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IcicleTreeListElement, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, KeyedElementIndex, LIST_ITEM_MOTION_MS, LIST_SEARCH_COMMAND_IDS, LIST_SEARCH_MODES, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListDragSelectController, ListNativeDragController, ListSearchController, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, MulticolListElement, NarrowedTextElement, NotificationManager, NotificationPillElement, PanTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, ShapefillValueElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TickledController, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeDropController, TreeKeyboardNavController, TreeListElement, TreemapListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, animateListItemIn, animateListItemOut, applyAgeModification, applyHighlights, applySnapshot, applyTreeDrop, attachTimelineInteractions, bottomDrawerCommandId, buildHighlightTargets, buildModel, captionForEntry, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeColour, computeIcicleLayout, computeLeafGeoms, computeTreemapLayout, createAcCompletionProvider, createDataListSearchAdapter, createDefaultListDragPreview, createElementIncrSearch, createElementListSearchAdapter, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createTreeListSearchAdapter, createVerticalListAdapter, distribute, dropTargetFor, extractStack, findIcicleNode, findIciclePath, findNode, findParentOf, findStackOf, findStackPathByName, findTreemapNode, findTreemapPath, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, listSearchStyles, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normaliseValue, normalize, panelGapIndex, parseColourScaleAttr, parseLayout, pointerOverAnyHost, prefersReducedMotion, pruneDraggedDescendants, readBaseColour, registerFontIcon, registerIcon, registerListSearchCommands, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, startListItemOutAnimation, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, toolbarGapIndex, walk, wrapRoot };
|
|
14181
|
+
export { AcTextElement, AcTokenElement, BottomDrawerElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DataTableElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GridListElement, GroupedItemListerElement, HyperbolicTreeListElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IcicleTreeListElement, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, KeyedElementIndex, LIST_ITEM_MOTION_MS, LIST_SEARCH_COMMAND_IDS, LIST_SEARCH_MODES, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, ListDragSelectController, ListNativeDragController, ListSearchController, ListSelectionController, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, MulticolListElement, NarrowedTextElement, NotificationManager, NotificationPillElement, PanTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, ShapefillValueElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TickledController, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeDropController, TreeKeyboardNavController, TreeListElement, TreemapListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, animateListItemIn, animateListItemOut, applyAgeModification, applyHighlights, applySnapshot, applyTreeDrop, attachTimelineInteractions, bottomDrawerCommandId, buildHighlightTargets, buildModel, captionForEntry, clampToDisk, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeCanonicalPositions, computeColour, computeHyperbolicLayout, computeIcicleLayout, computeLeafGeoms, computeTreemapLayout, createAcCompletionProvider, createDataListSearchAdapter, createDefaultListDragPreview, createElementIncrSearch, createElementListSearchAdapter, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createTreeListSearchAdapter, createVerticalListAdapter, distribute, dropTargetFor, euclideanRadiusForHyperbolicDistance, extractStack, findHyperbolicNode, findHyperbolicPath, findIcicleNode, findIciclePath, findNode, findPanelNode, findParentOf, findStackOf, findStackPathByName, findTreemapNode, findTreemapPath, geodesicInterpolate, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, hyperbolicDistanceForEuclideanRadius, iconBus, init, insertBeside, isDragging, isMultiMode, isSplit, isStack, keyForElement, listItemFadeKeyframes, listSearchStyles, mobiusAdd, mobiusFromOrigin, mobiusToOrigin, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, norm, normalise, normaliseValue, normalize, panelGapIndex, parseColourScaleAttr, parseLayout, poincareDistance, pointerOverAnyHost, prefersReducedMotion, preserveRestoreWeights, pruneDraggedDescendants, readBaseColour, registerFontIcon, registerIcon, registerListSearchCommands, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, setPanelRestoreWeight, startListItemOutAnimation, svgToDataUri, swapLeaves, tickledItemStyles, tickledStyles, timelineStyles, toSnapshot, toolbarGapIndex, walk, wrapRoot };
|
|
12923
14182
|
|
|
12924
14183
|
//# sourceMappingURL=index.js.map
|