@ixfx/components 0.10.7 → 0.11.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/bundle/index.d.ts +552 -1172
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +2113 -1402
- package/bundle/index.js.map +1 -1
- package/dist/crumbs.d.ts +2 -2
- package/dist/{data-grid-BDNjQv3Q.d.ts → data-grid-BWrtE8H9.d.ts} +4 -4
- package/dist/{data-grid-BDNjQv3Q.d.ts.map → data-grid-BWrtE8H9.d.ts.map} +1 -1
- package/dist/data-grid.d.ts +2 -2
- package/dist/{detail-list-BdLuOwQj.d.ts → detail-list-DxQZkK-C.d.ts} +4 -4
- package/dist/{detail-list-BdLuOwQj.d.ts.map → detail-list-DxQZkK-C.d.ts.map} +1 -1
- package/dist/detail-list.d.ts +3 -3
- package/dist/{grid-list-DyphX9Fv.d.ts → grid-list-ChgYpUUq.d.ts} +4 -4
- package/dist/{grid-list-DyphX9Fv.d.ts.map → grid-list-ChgYpUUq.d.ts.map} +1 -1
- package/dist/grid-list.d.ts +2 -2
- package/dist/incr-search.d.ts +1 -1
- package/dist/{index-BXafRK-_.d.ts → index-CHbuPyLL.d.ts} +3 -3
- package/dist/{index-BXafRK-_.d.ts.map → index-CHbuPyLL.d.ts.map} +1 -1
- package/dist/{index-C59-JEPl.d.ts → index-PjiaGpA5.d.ts} +2 -2
- package/dist/{index-C59-JEPl.d.ts.map → index-PjiaGpA5.d.ts.map} +1 -1
- package/dist/index.d.ts +405 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1299 -40
- package/dist/index.js.map +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.d.ts +1 -1
- package/dist/miller.d.ts +3 -3
- package/dist/split-layout-C5SYbDrq.d.ts.map +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.js +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-R25WqAVO.d.ts} +3 -3
- package/dist/{vertical-list-C03PN6lx.d.ts.map → vertical-list-R25WqAVO.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 +160 -4
- package/dist/split-layout-BJEU1W0d.js.map +0 -1
- package/dist/types-Bc23SI94.d.ts.map +0 -1
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Hyperbolic Tree
|
|
2
|
+
|
|
3
|
+
`<ixfx-hyperbolictree-list>` — a hyperbolic (Poincaré disk) browser over tree data. The focused node sits at the disk's center; its neighbourhood fans outward, shrinking toward the edge with tree-graph distance. Clicking a node re-centers the disk on it with an animated Möbius transform — the underlying layout never changes, only the projection.
|
|
4
|
+
|
|
5
|
+
It implements the shared `TreeComponent` interface (see [`src/tree/README.md`](../tree/README.md)), so `TreeDataModel`, `IncrSearchTreeController`, and any `TreeComponent`-typed code work unmodified.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Contents
|
|
10
|
+
|
|
11
|
+
1. [Differences from `ixfx-tree-list`](#differences-from-ixfx-tree-list)
|
|
12
|
+
2. [Data model](#data-model)
|
|
13
|
+
3. [Properties](#properties)
|
|
14
|
+
4. [Zoom vs. curvature](#zoom-vs-curvature)
|
|
15
|
+
5. [Weighting](#weighting)
|
|
16
|
+
6. [Targeting a specific node or edge](#targeting-a-specific-node-or-edge)
|
|
17
|
+
7. [Custom edge rendering](#custom-edge-rendering)
|
|
18
|
+
8. [Focus, selection, navigation](#focus-selection-navigation)
|
|
19
|
+
9. [Events](#events)
|
|
20
|
+
10. [Keyboard / pointer interaction](#keyboard--pointer-interaction)
|
|
21
|
+
11. [CSS variables and parts](#css-variables-and-parts)
|
|
22
|
+
12. [Types reference](#types-reference)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Differences from `ixfx-tree-list`
|
|
27
|
+
|
|
28
|
+
- **Expand/collapse is reinterpreted as focus change.** Every loaded node is always laid out; there's no expanded/collapsed state. `expand`/`collapse` still fire — when the new focus is a descendant/ancestor of the previous one — for interop with existing `TreeComponent` listeners.
|
|
29
|
+
- **`interactionMode` drops `'checked'`** — no checkbox affordance at disk-edge node scale.
|
|
30
|
+
- **`exclusivity` is accepted but ignored** — nothing is independently open.
|
|
31
|
+
- **`filterPredicate` dims** non-matching nodes instead of hiding them.
|
|
32
|
+
- Dragging empty disk space **pans** the view without changing focus or selection.
|
|
33
|
+
- Drag-and-drop and `checkboxRenderer` are out of scope.
|
|
34
|
+
|
|
35
|
+
## Data model
|
|
36
|
+
|
|
37
|
+
Same `TreeNode` / `TreeItem` shapes as the rest of the tree family (see [`src/tree/README.md`](../tree/README.md#data-model)), including `root`, `model` (`TreeDataModel`), and `loadChildren` for lazy loading.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
el.root = {
|
|
41
|
+
item: { key: 'root', label: 'My Files' },
|
|
42
|
+
children: [
|
|
43
|
+
{ item: { key: 'docs', label: 'Documents' }, children: [] },
|
|
44
|
+
{ item: { key: 'img', label: 'Images', isLeaf: true } },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Properties
|
|
50
|
+
|
|
51
|
+
| Property (attribute) | Type | Default | Description |
|
|
52
|
+
|---|---|---|---|
|
|
53
|
+
| `root` | `TreeNode \| undefined` | `undefined` | Root node of the tree |
|
|
54
|
+
| `model` | `TreeDataModel \| undefined` | `undefined` | Shared data model; takes precedence over `loadChildren` |
|
|
55
|
+
| `loadChildren` | `LoadChildrenCallback \| undefined` | `undefined` | Callback for lazy child loading |
|
|
56
|
+
| `interactionMode` (`interaction-mode`) | `'implicit' \| 'standard' \| 'manual'` | `'standard'` | Gesture mapping — see [`src/tree/README.md`](../tree/README.md#interactionmode-ixfx-tree-list--ixfx-miller-list) (`'checked'` is unsupported here) |
|
|
57
|
+
| `selectionFilter` (`selection-filter`) | `'none' \| 'leaf' \| 'branch' \| 'all'` | `'all'` | Which node kinds can be selected |
|
|
58
|
+
| `exclusivity` | `TreeExclusivity` | `'none'` | Accepted for interface compatibility, ignored |
|
|
59
|
+
| `filterPredicate` | `TreeFilterPredicate \| undefined` | `undefined` | Non-matching nodes (and branches with no matching descendant) are dimmed, not hidden |
|
|
60
|
+
| `maxRadius` (`max-radius`) | `number` | `4` | Tree-graph steps from the focus to lay out and render |
|
|
61
|
+
| `nodeRadius` (`node-radius`) | `number` | `0.05` | Base node dot radius, as a fraction of the disk radius |
|
|
62
|
+
| `minNodeRadius` (`min-node-radius`) | `number` | `0.015` | Nodes whose scaled radius falls below this render without a label |
|
|
63
|
+
| `minWeightScale` (`min-weight-scale`) | `number` | `0.6` | Radius multiplier for a node with `weight` at the low end of the currently-visible range |
|
|
64
|
+
| `maxWeightScale` (`max-weight-scale`) | `number` | `1.6` | Radius multiplier for a node with `weight` at the high end of the currently-visible range |
|
|
65
|
+
| `zoom` | `number` | `1` | Post-layout SVG magnification, centered on the focus. See [Zoom vs. curvature](#zoom-vs-curvature) |
|
|
66
|
+
| `curvature` | `number` | `1` | Hyperbolic distance placed between generations — how curved the space itself is. See [Zoom vs. curvature](#zoom-vs-curvature) |
|
|
67
|
+
| `focusAnimationDuration` (`focus-animation-duration`) | `number` | `400` | Duration (ms) of the animated re-center on focus change; `0` snaps instantly. Respects `prefers-reduced-motion` |
|
|
68
|
+
| `edgeRenderer` | `HyperbolicEdgeRenderer \| undefined` | `undefined` | Custom renderer for edges, in place of the default geodesic-arc path |
|
|
69
|
+
| `disableKeyboardNavigation` (`disable-keyboard-navigation`) | `boolean` | `false` | Disables the keyboard handlers below |
|
|
70
|
+
| `searchModes` (`search-modes`) | `string \| undefined` | `undefined` | Space-separated subset of `navigate select filter` — see [`src/tree/README.md`](../tree/README.md#incremental-search) |
|
|
71
|
+
| `selectedNode` | `TreeNode \| undefined` | `undefined` | Primary (most recent) selected node |
|
|
72
|
+
| `selectedNodes` | `ReadonlySet<TreeNode>` | `{}` | Full selection set (read-only) |
|
|
73
|
+
| `focusedNode` | `TreeNode \| undefined` (read-only) | true root | The node currently centered on the disk. Pure view-state — `root` is never mutated by focus changes |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Zoom vs. curvature
|
|
78
|
+
|
|
79
|
+
Two independent knobs affect how much of the tree is visible and how it's magnified — neither mutates `root`:
|
|
80
|
+
|
|
81
|
+
| | What it changes | Effect |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `zoom` | A post-layout SVG `scale()`, applied after everything else | Uniformly magnifies/shrinks the rendered disk around the focus. Node positions relative to each other are unchanged — it's a camera zoom, not a re-layout. |
|
|
84
|
+
| `curvature` | The hyperbolic distance placed between each generation (default `1`, matching the standard Poincaré-disk layout) | Raising it pushes each generation further out in hyperbolic terms, so the disk's own `tanh` compression crowds distant generations toward the boundary faster — a stronger fisheye. This *reshapes* the layout (relative sizes/spacing of near vs. far nodes change), which is why increasing it reads as "zooming in" on the focus even at `zoom: 1`. Lowering it spreads generations out more evenly, flattening the view toward a Euclidean-feeling layout. |
|
|
85
|
+
| `maxRadius` | How many tree-graph rings are laid out at all | Independent of both — a window over the tree, not a projection change. |
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<ixfx-hyperbolictree-list zoom="1.5" curvature="1.8"></ixfx-hyperbolictree-list>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Weighting
|
|
92
|
+
|
|
93
|
+
Set `TreeItem.weight` (a plain `number`, any range) on nodes to influence their visuals:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
el.root = {
|
|
97
|
+
item: { key: 'root', label: 'Root', weight: 10 },
|
|
98
|
+
children: [
|
|
99
|
+
{ item: { key: 'big', label: 'Big folder', weight: 8 } },
|
|
100
|
+
{ item: { key: 'small', label: 'Small file', weight: 1 } },
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Weight is normalized to `0..1` **against whichever nodes are currently visible** (the ring window `maxRadius` selects) — not the whole tree — and exposed as CSS custom properties:
|
|
106
|
+
|
|
107
|
+
| Custom property | Where | Value |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `--node-weight` | Each node's `<g part="node">` | Normalized `0..1` (defaults to `0.5` if the node, or the whole visible set, has no weight) |
|
|
110
|
+
| `--node-weight-raw` | Each node's `<g part="node">` | The raw `item.weight` value, when set (otherwise unset) |
|
|
111
|
+
| `--edge-weight` | Each edge's `<path part="edge">` | Average of the two endpoint nodes' `--node-weight` |
|
|
112
|
+
|
|
113
|
+
By default this:
|
|
114
|
+
|
|
115
|
+
- Scales node radius between `minWeightScale`× and `maxWeightScale`× (default `0.6`–`1.6`).
|
|
116
|
+
- Tints node fill toward `--accent` as weight increases (`color-mix` on `--node-weight`).
|
|
117
|
+
- Thickens edge strokes as `--edge-weight` increases.
|
|
118
|
+
|
|
119
|
+
Override any of it with plain CSS — `::part(dot)`, `::part(edge)`, or the custom properties themselves:
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
ixfx-hyperbolictree-list::part(dot) {
|
|
123
|
+
fill: color-mix(in oklch, gray, red calc(var(--node-weight, 0.5) * 100%));
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
> **Gotcha:** because normalization is per-*visible-window*, a node whose weight is small relative to something merely nearby in the tree (e.g. a giant sibling subtree, or the root itself, both often in view) can still normalize close to `0` even if it's "big" in absolute terms. If your weight metric has a wide dynamic range (e.g. raw descendant counts across a deep tree), compress it — `Math.log2(x + 1)` rather than `x` — before assigning it to `item.weight`, or the visible spread of sizes/colours will collapse toward one end.
|
|
128
|
+
|
|
129
|
+
## Targeting a specific node or edge
|
|
130
|
+
|
|
131
|
+
Each node and edge exports extra `part` tokens keyed by node identity, so a single one can be styled from outside the shadow root:
|
|
132
|
+
|
|
133
|
+
| Element | Exported parts |
|
|
134
|
+
|---|---|
|
|
135
|
+
| Node `<g>` | `node`, `node-<key>` |
|
|
136
|
+
| Edge `<path>` | `edge`, `edge-to-<key>`, `edge-from-<key>` |
|
|
137
|
+
|
|
138
|
+
(Non-identifier characters in a key are replaced with `-` to keep the token CSS-selectable.)
|
|
139
|
+
|
|
140
|
+
```css
|
|
141
|
+
/* Give the edge leading to "library" a distinct dashed line. */
|
|
142
|
+
ixfx-hyperbolictree-list::part(edge-to-library) {
|
|
143
|
+
stroke: var(--accent);
|
|
144
|
+
stroke-dasharray: 8 4;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
A plain attribute selector — `[data-to="library"]` — **cannot** reach across the shadow boundary from page-level CSS; only `::part()` can. Each edge does carry `data-from`/`data-to` attributes (and each node a `data-key` attribute), but those are for JS introspection (`el.shadowRoot.querySelectorAll(...)`), not CSS.
|
|
149
|
+
|
|
150
|
+
## Custom edge rendering
|
|
151
|
+
|
|
152
|
+
For styling `::part()`/CSS can't express (arbitrary shapes, gradients per edge, etc.), replace the default geodesic-arc path entirely:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { svg } from 'lit';
|
|
156
|
+
import type { HyperbolicEdgeRenderContext } from '@ixfx/components';
|
|
157
|
+
|
|
158
|
+
el.edgeRenderer = ({ edge, fromNode, toNode }: HyperbolicEdgeRenderContext) => {
|
|
159
|
+
// Authored against the layout's native -1..1 disk coordinates —
|
|
160
|
+
// the component scales this up to match its internal render scale.
|
|
161
|
+
return svg`<line x1=${edge.from.x} y1=${edge.from.y} x2=${edge.to.x} y2=${edge.to.y} stroke="red" />`;
|
|
162
|
+
};
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`edge.weightNormalized` is available on `edge` for weight-driven custom rendering.
|
|
166
|
+
|
|
167
|
+
## Focus, selection, navigation
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Re-center the disk on a node (animated Möbius transform; fires `focus`, plus
|
|
171
|
+
// `expand`/`collapse` when relevant)
|
|
172
|
+
el.focusOn(node);
|
|
173
|
+
|
|
174
|
+
// Selection — same contract as ixfx-tree-list
|
|
175
|
+
el.select(node);
|
|
176
|
+
el.deselect(node);
|
|
177
|
+
el.clearSelection();
|
|
178
|
+
el.selectAll();
|
|
179
|
+
el.deselectAll();
|
|
180
|
+
|
|
181
|
+
// Navigate by key path or predicate — focuses + selects the match
|
|
182
|
+
el.navigateTo(['root', 'documents', 'readme.md']);
|
|
183
|
+
el.navigateToNode(n => n.item.key === 'readme.md');
|
|
184
|
+
|
|
185
|
+
// Snapshot of every currently laid-out node (within maxRadius of the focus),
|
|
186
|
+
// including the focused node itself, in ring-major order
|
|
187
|
+
const nodes = el.getNodes(); // ReadonlyArray<{ node: TreeNode; depth: number }>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Events
|
|
191
|
+
|
|
192
|
+
All events **bubble** and are **composed**.
|
|
193
|
+
|
|
194
|
+
| Event | Detail type | When fired |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| `focus` | `HyperbolicFocusDetail` — `{ node: TreeNode; previous: TreeNode \| undefined }` | The focused (centered) node changes |
|
|
197
|
+
| `select` | `TreeSelectDetail` | Selection set changes |
|
|
198
|
+
| `tickle` | `TreeTickleDetail` | An item enters cursor focus (pointer hover or keyboard cursor) |
|
|
199
|
+
| `activate` | `TreeActivateDetail` | Enter key or double-click |
|
|
200
|
+
| `item-click` | `{ node: TreeNode; depth: number }` | Raw pointer click on any item |
|
|
201
|
+
| `item-dblclick` | `TreeItemDblClickDetail` | Double-click (also fires `activate`) |
|
|
202
|
+
| `expand` | `{ node: TreeNode; depth: number }` | New focus is a descendant of the previous one |
|
|
203
|
+
| `collapse` | `{ node: TreeNode; depth: number }` | New focus is an ancestor of the previous one |
|
|
204
|
+
|
|
205
|
+
## Keyboard / pointer interaction
|
|
206
|
+
|
|
207
|
+
The element must be focused before keyboard navigation works (`disableKeyboardNavigation` turns it off entirely).
|
|
208
|
+
|
|
209
|
+
| Key | Action |
|
|
210
|
+
|---|---|
|
|
211
|
+
| `Arrow Up` | Focus the child closest to screen-"up" |
|
|
212
|
+
| `Arrow Down` | Focus the parent |
|
|
213
|
+
| `Arrow Left` / `Arrow Right` | Cycle siblings of the focused node |
|
|
214
|
+
| `Enter` | Select (unless `interactionMode="manual"`) and activate the focused node |
|
|
215
|
+
| `Escape` | Return focus to the root |
|
|
216
|
+
|
|
217
|
+
Pointer: click a node to focus (and select, per `interactionMode`) it; drag empty disk space to pan.
|
|
218
|
+
|
|
219
|
+
## CSS variables and parts
|
|
220
|
+
|
|
221
|
+
| Variable | Default | Description |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| `--hyperbolictree-node-color` | `var(--surface-4)` | Base node fill |
|
|
224
|
+
| `--hyperbolictree-node-text` | `var(--surface-3-text)` | Label text colour |
|
|
225
|
+
| `--hyperbolictree-edge-color` | `var(--surface-6)` | Edge stroke colour |
|
|
226
|
+
| `--hyperbolictree-boundary-color` | `var(--border-subtle)` | Disk boundary stroke colour |
|
|
227
|
+
| `--hyperbolictree-font-size` | `25px` | Label font size |
|
|
228
|
+
| `--node-weight` / `--node-weight-raw` | — | See [Weighting](#weighting) |
|
|
229
|
+
| `--edge-weight` | — | See [Weighting](#weighting) |
|
|
230
|
+
|
|
231
|
+
| Part | Element | Notes |
|
|
232
|
+
|---|---|---|
|
|
233
|
+
| `boundary` | Disk-edge circle | |
|
|
234
|
+
| `edges` / `nodes` | Group wrappers | |
|
|
235
|
+
| `edge`, `edge-to-<key>`, `edge-from-<key>` | Each edge `<path>` | See [Targeting a specific node or edge](#targeting-a-specific-node-or-edge) |
|
|
236
|
+
| `node`, `node-<key>` | Each node `<g>` | |
|
|
237
|
+
| `hit` | Invisible larger hit-target circle per node | |
|
|
238
|
+
| `dot` | The visible node circle | |
|
|
239
|
+
| `label` | Node label `<text>` | |
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Types reference
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
type HyperbolicInteractionMode = 'implicit' | 'standard' | 'manual';
|
|
247
|
+
|
|
248
|
+
type HyperbolicFocusDetail = {
|
|
249
|
+
readonly node: TreeNode;
|
|
250
|
+
readonly previous: TreeNode | undefined;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
type HyperbolicEdgeRenderContext = {
|
|
254
|
+
readonly edge: HyperbolicLayoutEdge;
|
|
255
|
+
readonly fromNode: TreeNode;
|
|
256
|
+
readonly toNode: TreeNode;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
type HyperbolicEdgeRenderer = (context: HyperbolicEdgeRenderContext) => unknown;
|
|
260
|
+
|
|
261
|
+
type HyperbolicLayoutNode = {
|
|
262
|
+
readonly node: TreeNode;
|
|
263
|
+
readonly key: string;
|
|
264
|
+
readonly depth: number; // absolute depth from the true root
|
|
265
|
+
readonly ring: number; // tree-graph steps from the focus (0 = focus itself)
|
|
266
|
+
readonly parentKey: string | undefined;
|
|
267
|
+
readonly position: Point; // Poincaré-disk position, focus projected to the origin
|
|
268
|
+
readonly angularShare: number; // angular width (radians) allocated by the parent's partition
|
|
269
|
+
readonly weight: number | undefined;
|
|
270
|
+
readonly weightNormalized: number; // 0..1, normalized across the visible node set
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
type HyperbolicLayoutEdge = {
|
|
274
|
+
readonly fromKey: string;
|
|
275
|
+
readonly toKey: string;
|
|
276
|
+
readonly from: Point;
|
|
277
|
+
readonly to: Point;
|
|
278
|
+
readonly weightNormalized: number; // average of the two endpoints'
|
|
279
|
+
};
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
See [`src/tree/README.md`](../tree/README.md) for `TreeNode`, `TreeItem` (including `weight`), `TreeDataModel`, `TreeComponent`, and incremental search — all shared with the rest of the tree family.
|
package/docs-user/index.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ixfx/components",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"generated": "2026-09-
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"generated": "2026-09-19T12:04:31.116Z",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
7
7
|
"name": "ac-text",
|
|
@@ -83,6 +83,11 @@
|
|
|
83
83
|
"summary": "A meta-component that renders a typed item list using any sub-component you supply. Items can be displayed in a single sub-component (_ungrouped_ mode) or split into labelled groups, each with its own",
|
|
84
84
|
"path": "docs-user/grouped-item-lister.md"
|
|
85
85
|
},
|
|
86
|
+
{
|
|
87
|
+
"name": "hyperbolic-tree",
|
|
88
|
+
"summary": "`<ixfx-hyperbolictree-list>` — a hyperbolic (Poincaré disk) browser over tree data. The focused node sits at the disk's center; its neighbourhood fans outward, shrinking toward the edge with tree-grap",
|
|
89
|
+
"path": "docs-user/hyperbolic-tree.md"
|
|
90
|
+
},
|
|
86
91
|
{
|
|
87
92
|
"name": "icons",
|
|
88
93
|
"summary": "A centralized SVG icon system for `@ixfx/components`. Icons are stored by name, can be overridden globally, and all components that use a given icon re-render automatically when it changes.",
|
package/docs-user/llms.txt
CHANGED
|
@@ -27,6 +27,7 @@ import '@ixfx/components/data-grid';
|
|
|
27
27
|
- [form](docs-user/form.md): Primitives for typical form layout with responsive label positioning and data binding.
|
|
28
28
|
- [grid-list](docs-user/grid-list.md): A virtualised grid of items — thumbnails, cards, or arbitrary slotted markup —
|
|
29
29
|
- [grouped-item-lister](docs-user/grouped-item-lister.md): A meta-component that renders a typed item list using any sub-component you supply. Items can be displayed in a single sub-component (_ungrouped_ mode) or split into labelled groups, each with its own
|
|
30
|
+
- [hyperbolic-tree](docs-user/hyperbolic-tree.md): `<ixfx-hyperbolictree-list>` — a hyperbolic (Poincaré disk) browser over tree data. The focused node sits at the disk's center; its neighbourhood fans outward, shrinking toward the edge with tree-grap
|
|
30
31
|
- [icons](docs-user/icons.md): A centralized SVG icon system for `@ixfx/components`. Icons are stored by name, can be overridden globally, and all components that use a given icon re-render automatically when it changes.
|
|
31
32
|
- [incr-search](docs-user/incr-search.md): Utilities for incremental (type-ahead) search over data collections or DOM-backed lists.
|
|
32
33
|
- [labelled-radial-input](docs-user/labelled-radial-input.md): A composite radial input that pairs an editable label (`ixfx-editable-number`) with a rotary dial (`ixfx-radial-input`). The two controls are always in sync — turning the dial updates the number, and
|
package/docs-user/tree.md
CHANGED
|
@@ -34,6 +34,7 @@ type TreeItem = {
|
|
|
34
34
|
icon?: string; // Icon name (passed to <ixfx-icon>)
|
|
35
35
|
tooltip?: string; // Tooltip content
|
|
36
36
|
value?: string; // Optional machine-readable value
|
|
37
|
+
weight?: number; // Relative importance/magnitude; only ixfx-hyperbolictree-list uses it today
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
type TreeNode = {
|
package/llms.txt
CHANGED
|
@@ -27,6 +27,7 @@ import '@ixfx/components/data-grid';
|
|
|
27
27
|
- [form](docs-user/form.md): Primitives for typical form layout with responsive label positioning and data binding.
|
|
28
28
|
- [grid-list](docs-user/grid-list.md): A virtualised grid of items — thumbnails, cards, or arbitrary slotted markup —
|
|
29
29
|
- [grouped-item-lister](docs-user/grouped-item-lister.md): A meta-component that renders a typed item list using any sub-component you supply. Items can be displayed in a single sub-component (_ungrouped_ mode) or split into labelled groups, each with its own
|
|
30
|
+
- [hyperbolic-tree](docs-user/hyperbolic-tree.md): `<ixfx-hyperbolictree-list>` — a hyperbolic (Poincaré disk) browser over tree data. The focused node sits at the disk's center; its neighbourhood fans outward, shrinking toward the edge with tree-grap
|
|
30
31
|
- [icons](docs-user/icons.md): A centralized SVG icon system for `@ixfx/components`. Icons are stored by name, can be overridden globally, and all components that use a given icon re-render automatically when it changes.
|
|
31
32
|
- [incr-search](docs-user/incr-search.md): Utilities for incremental (type-ahead) search over data collections or DOM-backed lists.
|
|
32
33
|
- [labelled-radial-input](docs-user/labelled-radial-input.md): A composite radial input that pairs an editable label (`ixfx-editable-number`) with a rotary dial (`ixfx-radial-input`). The two controls are always in sync — turning the dial updates the number, and
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ixfx/components",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.0",
|
|
5
5
|
"packageManager": "pnpm@10.0.0",
|
|
6
6
|
"description": "Web components for ixfx — Lit-based UI toolkit",
|
|
7
7
|
"author": "Clint Heyer",
|
|
@@ -16,18 +16,174 @@
|
|
|
16
16
|
"types": "./bundle/index.d.ts",
|
|
17
17
|
"import": "./bundle/index.js"
|
|
18
18
|
},
|
|
19
|
+
"./ac-text": {
|
|
20
|
+
"types": "./dist/ac-text.d.ts",
|
|
21
|
+
"import": "./dist/ac-text.js"
|
|
22
|
+
},
|
|
23
|
+
"./button": {
|
|
24
|
+
"types": "./dist/button.d.ts",
|
|
25
|
+
"import": "./dist/button.js"
|
|
26
|
+
},
|
|
27
|
+
"./checkbox": {
|
|
28
|
+
"types": "./dist/checkbox.d.ts",
|
|
29
|
+
"import": "./dist/checkbox.js"
|
|
30
|
+
},
|
|
31
|
+
"./colour-picker": {
|
|
32
|
+
"types": "./dist/colour-picker.d.ts",
|
|
33
|
+
"import": "./dist/colour-picker.js"
|
|
34
|
+
},
|
|
35
|
+
"./crumbs": {
|
|
36
|
+
"types": "./dist/crumbs.d.ts",
|
|
37
|
+
"import": "./dist/crumbs.js"
|
|
38
|
+
},
|
|
39
|
+
"./data-display": {
|
|
40
|
+
"types": "./dist/data-display.d.ts",
|
|
41
|
+
"import": "./dist/data-display.js"
|
|
42
|
+
},
|
|
43
|
+
"./editable-label": {
|
|
44
|
+
"types": "./dist/editable-label.d.ts",
|
|
45
|
+
"import": "./dist/editable-label.js"
|
|
46
|
+
},
|
|
47
|
+
"./hex": {
|
|
48
|
+
"types": "./dist/hex.d.ts",
|
|
49
|
+
"import": "./dist/hex.js"
|
|
50
|
+
},
|
|
51
|
+
"./icons": {
|
|
52
|
+
"types": "./dist/icons.d.ts",
|
|
53
|
+
"import": "./dist/icons.js"
|
|
54
|
+
},
|
|
19
55
|
"./incr-search": {
|
|
20
56
|
"types": "./dist/incr-search.d.ts",
|
|
21
57
|
"import": "./dist/incr-search.js"
|
|
22
58
|
},
|
|
23
|
-
"./
|
|
24
|
-
"types": "./dist/
|
|
25
|
-
"import": "./dist/
|
|
59
|
+
"./labelled-radial-input": {
|
|
60
|
+
"types": "./dist/labelled-radial-input.d.ts",
|
|
61
|
+
"import": "./dist/labelled-radial-input.js"
|
|
62
|
+
},
|
|
63
|
+
"./labelled-range-input": {
|
|
64
|
+
"types": "./dist/labelled-range-input.d.ts",
|
|
65
|
+
"import": "./dist/labelled-range-input.js"
|
|
66
|
+
},
|
|
67
|
+
"./labelled-slider-input": {
|
|
68
|
+
"types": "./dist/labelled-slider-input.d.ts",
|
|
69
|
+
"import": "./dist/labelled-slider-input.js"
|
|
70
|
+
},
|
|
71
|
+
"./led": {
|
|
72
|
+
"types": "./dist/led.d.ts",
|
|
73
|
+
"import": "./dist/led.js"
|
|
74
|
+
},
|
|
75
|
+
"./menu": {
|
|
76
|
+
"types": "./dist/menu.d.ts",
|
|
77
|
+
"import": "./dist/menu.js"
|
|
78
|
+
},
|
|
79
|
+
"./miller": {
|
|
80
|
+
"types": "./dist/miller.d.ts",
|
|
81
|
+
"import": "./dist/miller.js"
|
|
82
|
+
},
|
|
83
|
+
"./narrowed-text": {
|
|
84
|
+
"types": "./dist/narrowed-text.d.ts",
|
|
85
|
+
"import": "./dist/narrowed-text.js"
|
|
86
|
+
},
|
|
87
|
+
"./panel": {
|
|
88
|
+
"types": "./dist/panel.d.ts",
|
|
89
|
+
"import": "./dist/panel.js"
|
|
90
|
+
},
|
|
91
|
+
"./plots": {
|
|
92
|
+
"types": "./dist/plots.d.ts",
|
|
93
|
+
"import": "./dist/plots.js"
|
|
94
|
+
},
|
|
95
|
+
"./polar-pad": {
|
|
96
|
+
"types": "./dist/polar-pad.d.ts",
|
|
97
|
+
"import": "./dist/polar-pad.js"
|
|
98
|
+
},
|
|
99
|
+
"./radial-input": {
|
|
100
|
+
"types": "./dist/radial-input.d.ts",
|
|
101
|
+
"import": "./dist/radial-input.js"
|
|
102
|
+
},
|
|
103
|
+
"./range-input": {
|
|
104
|
+
"types": "./dist/range-input.d.ts",
|
|
105
|
+
"import": "./dist/range-input.js"
|
|
106
|
+
},
|
|
107
|
+
"./range": {
|
|
108
|
+
"types": "./dist/range.d.ts",
|
|
109
|
+
"import": "./dist/range.js"
|
|
110
|
+
},
|
|
111
|
+
"./select-horiz": {
|
|
112
|
+
"types": "./dist/select-horiz.d.ts",
|
|
113
|
+
"import": "./dist/select-horiz.js"
|
|
114
|
+
},
|
|
115
|
+
"./slider-input": {
|
|
116
|
+
"types": "./dist/slider-input.d.ts",
|
|
117
|
+
"import": "./dist/slider-input.js"
|
|
118
|
+
},
|
|
119
|
+
"./snap-container": {
|
|
120
|
+
"types": "./dist/snap-container.d.ts",
|
|
121
|
+
"import": "./dist/snap-container.js"
|
|
122
|
+
},
|
|
123
|
+
"./split-layout": {
|
|
124
|
+
"types": "./dist/split-layout.d.ts",
|
|
125
|
+
"import": "./dist/split-layout.js"
|
|
126
|
+
},
|
|
127
|
+
"./swipe": {
|
|
128
|
+
"types": "./dist/swipe.d.ts",
|
|
129
|
+
"import": "./dist/swipe.js"
|
|
130
|
+
},
|
|
131
|
+
"./tabs": {
|
|
132
|
+
"types": "./dist/tabs.d.ts",
|
|
133
|
+
"import": "./dist/tabs.js"
|
|
134
|
+
},
|
|
135
|
+
"./timeline": {
|
|
136
|
+
"types": "./dist/timeline.d.ts",
|
|
137
|
+
"import": "./dist/timeline.js"
|
|
138
|
+
},
|
|
139
|
+
"./titlebar": {
|
|
140
|
+
"types": "./dist/titlebar.d.ts",
|
|
141
|
+
"import": "./dist/titlebar.js"
|
|
142
|
+
},
|
|
143
|
+
"./tree": {
|
|
144
|
+
"types": "./dist/tree.d.ts",
|
|
145
|
+
"import": "./dist/tree.js"
|
|
146
|
+
},
|
|
147
|
+
"./xy-pad": {
|
|
148
|
+
"types": "./dist/xy-pad.d.ts",
|
|
149
|
+
"import": "./dist/xy-pad.js"
|
|
150
|
+
},
|
|
151
|
+
"./ac-token": {
|
|
152
|
+
"types": "./dist/ac-token.d.ts",
|
|
153
|
+
"import": "./dist/ac-token.js"
|
|
154
|
+
},
|
|
155
|
+
"./data-grid": {
|
|
156
|
+
"types": "./dist/data-grid.d.ts",
|
|
157
|
+
"import": "./dist/data-grid.js"
|
|
26
158
|
},
|
|
27
159
|
"./detail-list": {
|
|
28
160
|
"types": "./dist/detail-list.d.ts",
|
|
29
161
|
"import": "./dist/detail-list.js"
|
|
30
162
|
},
|
|
163
|
+
"./grid-list": {
|
|
164
|
+
"types": "./dist/grid-list.d.ts",
|
|
165
|
+
"import": "./dist/grid-list.js"
|
|
166
|
+
},
|
|
167
|
+
"./form": {
|
|
168
|
+
"types": "./dist/form.d.ts",
|
|
169
|
+
"import": "./dist/form.js"
|
|
170
|
+
},
|
|
171
|
+
"./grouped-item-lister": {
|
|
172
|
+
"types": "./dist/grouped-item-lister.d.ts",
|
|
173
|
+
"import": "./dist/grouped-item-lister.js"
|
|
174
|
+
},
|
|
175
|
+
"./notifier": {
|
|
176
|
+
"types": "./dist/notifier.d.ts",
|
|
177
|
+
"import": "./dist/notifier.js"
|
|
178
|
+
},
|
|
179
|
+
"./snackbar": {
|
|
180
|
+
"types": "./dist/snackbar.d.ts",
|
|
181
|
+
"import": "./dist/snackbar.js"
|
|
182
|
+
},
|
|
183
|
+
"./transitory-label": {
|
|
184
|
+
"types": "./dist/transitory-label.d.ts",
|
|
185
|
+
"import": "./dist/transitory-label.js"
|
|
186
|
+
},
|
|
31
187
|
"./vertical-list": {
|
|
32
188
|
"types": "./dist/vertical-list.d.ts",
|
|
33
189
|
"import": "./dist/vertical-list.js"
|