@yuneta/gobj-ui 7.23.69 → 7.23.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/dist/gobj-ui.cjs.js +470 -3
- package/dist/gobj-ui.es.js +470 -3
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +103 -2
- package/src/c_yui_form.js +8 -2
- package/src/c_yui_treedb_graph.js +6 -0
- package/src/form_field_readonly.js +61 -0
- package/src/form_field_readonly.test.js +50 -0
- package/src/treedb_layout.js +380 -0
- package/src/treedb_layout.test.js +172 -0
package/README.md
CHANGED
|
@@ -1266,6 +1266,55 @@ a level or two, with a count on every cut.
|
|
|
1266
1266
|
|
|
1267
1267
|
One consumer key came with it: `show more`, the chip's tooltip.
|
|
1268
1268
|
|
|
1269
|
+
### Layouts: what G6 offers, and the two made for a treedb
|
|
1270
|
+
|
|
1271
|
+
What a treedb graph IS, once folded: a **forest read left to right**. A main
|
|
1272
|
+
topic is a tree (places: country, region, site, hall), the other topics hang
|
|
1273
|
+
from its nodes through hooks, a record may hang from two parents (a device
|
|
1274
|
+
from its place AND its controller), and every visible node was reached from a
|
|
1275
|
+
root. Tens to a few hundred cards on screen, never the store.
|
|
1276
|
+
|
|
1277
|
+
G6 5.1 registers these layouts, and this is how each meets that shape:
|
|
1278
|
+
|
|
1279
|
+
| layout | what it does | for a treedb |
|
|
1280
|
+
|---|---|---|
|
|
1281
|
+
| `dagre` / `antv-dagre` | layered DAG: longest-path ranks, crossing minimisation, one direction | the right reading (LR); but the crossing heuristic **reorders the siblings on every run** — open one hook and the column next to it shuffles — and it is the one thing that takes time on a big expansion |
|
|
1282
|
+
| `compact-box`, `indented`, `dendrogram`, `mindmap` | @antv/hierarchy tree layouts | the shape wanted, but G6 runs them on a tree it **builds from the edges** (`createTreeStructure`): a node with two parents lands under whichever parent the builder met first, unstably |
|
|
1283
|
+
| `radial` | MDS on graph distances from a focus node, a ring per depth, overlaps pushed apart afterwards | the right idea — a hall with a hundred devices is a **fan**, not a column — but the ring radius is one it is TOLD: twenty cards of 172px on a ring of radius 200 have 52px each and pile up. Tried, and replaced by our own (below) |
|
|
1284
|
+
| `force`, `d3-force`, `force-atlas2`, `fruchterman` | physics | scatter a tree: the pile with springs. `d3-force` and `force-atlas2` stay in the picker for the rare flat treedb |
|
|
1285
|
+
| `circular`, `concentric`, `grid`, `mds`, `random` | rings, rings by degree, a grid, projection, noise | none knows a parent from a child |
|
|
1286
|
+
| `combo-combined`, `fishbone` | combos / cause-effect | not this data |
|
|
1287
|
+
|
|
1288
|
+
So three of our own, in `treedb_layout.js` (pure, tested), registered as G6
|
|
1289
|
+
layouts by three thin adapters in `c_g6_nodes_tree.js`:
|
|
1290
|
+
|
|
1291
|
+
- **`treedb-tree`** — the classic tidy tree, left to right. A column per
|
|
1292
|
+
depth as wide as its widest card, a node centred on the block of its
|
|
1293
|
+
children, siblings stacked with `nodesep`. **The default** for a treedb
|
|
1294
|
+
nobody has arranged (was `dagre`).
|
|
1295
|
+
- **`treedb-outline`** — one node per row, indented by depth: what a JSON
|
|
1296
|
+
viewer draws, and what a treedb is when read as one. Tall, but nothing is
|
|
1297
|
+
ever beside anything.
|
|
1298
|
+
- **`radial`** (`treedb-radial`) — the root in the middle, a ring per depth,
|
|
1299
|
+
every subtree an angular **sector** proportional to its leaves, and the
|
|
1300
|
+
radius of each ring the larger of one step out from the ring before and
|
|
1301
|
+
the length its cards need side by side. A fan that cannot overlap by
|
|
1302
|
+
construction; several roots share the circle around an empty centre.
|
|
1303
|
+
|
|
1304
|
+
Both take the **same spanning tree**, chosen deterministically: roots are the
|
|
1305
|
+
nodes with no incoming edge, in node order; a node belongs to the **first
|
|
1306
|
+
parent that reaches it** in a breadth-first walk (the place, not the
|
|
1307
|
+
controller, because the place's column came first) and its other links are
|
|
1308
|
+
drawn as edges across the tree; the children of a node are ordered by the
|
|
1309
|
+
**hook they hang from** (the parent's port order — the schema's column order)
|
|
1310
|
+
and then by node order (record order, the pages' order); a cycle nothing
|
|
1311
|
+
reaches gets a root. Consequences that dagre cannot give: O(n), no crossing
|
|
1312
|
+
heuristic, and **opening a hook moves nothing that is not under or beside
|
|
1313
|
+
it** — the cards keep their order, the eye keeps its place, and the `+N` chip
|
|
1314
|
+
sits at the end of its own hook's children. The hook rank comes from the
|
|
1315
|
+
edge's `sourcePort` looked up in the parent's ports, so the adapter needs no
|
|
1316
|
+
model, only the G6 data.
|
|
1317
|
+
|
|
1269
1318
|
### The legend is the graph's layer control
|
|
1270
1319
|
|
|
1271
1320
|
The legend strip under the toolbar is **always there** now (the *Legend*
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -6403,6 +6403,57 @@ function plan_toolbar(wanted) {
|
|
|
6403
6403
|
};
|
|
6404
6404
|
}
|
|
6405
6405
|
//#endregion
|
|
6406
|
+
//#region src/form_field_readonly.js
|
|
6407
|
+
/***********************************************************************
|
|
6408
|
+
* form_field_readonly.js
|
|
6409
|
+
*
|
|
6410
|
+
* Is this field of the form read-only?
|
|
6411
|
+
*
|
|
6412
|
+
* Pure, so the rule can be tested without a DOM: the gclass turns
|
|
6413
|
+
* the answer into `readonly`/`disabled` on a control, this decides
|
|
6414
|
+
* the answer. Same split as form_toolbar_plan.js and
|
|
6415
|
+
* treedb_write_plan.js.
|
|
6416
|
+
*
|
|
6417
|
+
* The rule it exists to state ONCE: **`writable` governs the WRITE
|
|
6418
|
+
* of a column's VALUE, and an fkey is not written — it is LINKED.**
|
|
6419
|
+
* An fkey column is normally declared with no `writable` flag at
|
|
6420
|
+
* all (`treedb_authzs`'s `users.roles` is `['fkey']`, and so is
|
|
6421
|
+
* almost every fkey in the tree), so reading "not writable" as "not
|
|
6422
|
+
* editable" takes away the only way to link a record to its parent
|
|
6423
|
+
* — with `users.roles`, the only way to give a person a role.
|
|
6424
|
+
*
|
|
6425
|
+
* The other half of this rule is already written down in
|
|
6426
|
+
* c_yui_treedb_topic_with_form.js, where the record travels back:
|
|
6427
|
+
* the form sends "the writable cols, the fkeys (a link is edited by
|
|
6428
|
+
* linking) and the pkey". The two halves have to agree, and when
|
|
6429
|
+
* they did not, the form disabled the control whose value the save
|
|
6430
|
+
* path was still waiting for.
|
|
6431
|
+
*
|
|
6432
|
+
* A `file` column is an fkey too (flagged `['fkey','file']`) and is
|
|
6433
|
+
* NOT covered: it answers `type: "file"`, its bytes travel beside
|
|
6434
|
+
* the record, and the SDK declares as legal a `file` column that
|
|
6435
|
+
* only a load fills.
|
|
6436
|
+
*
|
|
6437
|
+
* Copyright (c) 2026, ArtGins.
|
|
6438
|
+
* All Rights Reserved.
|
|
6439
|
+
***********************************************************************/
|
|
6440
|
+
/***************************************************************
|
|
6441
|
+
* field_is_readonly(form_readonly, field_desc) -> boolean
|
|
6442
|
+
*
|
|
6443
|
+
* form_readonly the FORM is read-only: a record opened to be
|
|
6444
|
+
* looked at, or a store nobody may write. It is
|
|
6445
|
+
* a property of the opening, not of the schema,
|
|
6446
|
+
* and it wins over everything.
|
|
6447
|
+
* field_desc as treedb_get_field_desc() answers it
|
|
6448
|
+
* (`type`, `is_writable`).
|
|
6449
|
+
***************************************************************/
|
|
6450
|
+
function field_is_readonly(form_readonly, field_desc) {
|
|
6451
|
+
if (form_readonly) return true;
|
|
6452
|
+
let desc = field_desc || {};
|
|
6453
|
+
if (desc.type === "fkey") return false;
|
|
6454
|
+
return !desc.is_writable;
|
|
6455
|
+
}
|
|
6456
|
+
//#endregion
|
|
6406
6457
|
//#region src/yui_theme.js
|
|
6407
6458
|
/***********************************************************************
|
|
6408
6459
|
* yui_theme.js
|
|
@@ -7215,7 +7266,7 @@ function build_form_field_conf(gobj, field_desc) {
|
|
|
7215
7266
|
placeholder: "",
|
|
7216
7267
|
options: [],
|
|
7217
7268
|
extras: {},
|
|
7218
|
-
readonly: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly")
|
|
7269
|
+
readonly: field_is_readonly((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "readonly"), field_desc),
|
|
7219
7270
|
required: field_desc.is_required,
|
|
7220
7271
|
hidden: field_desc.is_hidden,
|
|
7221
7272
|
default_value: field_desc.default_value
|
|
@@ -27314,6 +27365,329 @@ function fold_reveal(model, state, node_key) {
|
|
|
27314
27365
|
return true;
|
|
27315
27366
|
}
|
|
27316
27367
|
//#endregion
|
|
27368
|
+
//#region src/treedb_layout.js
|
|
27369
|
+
/***********************************************************************
|
|
27370
|
+
* treedb_layout.js
|
|
27371
|
+
*
|
|
27372
|
+
* Two layouts made for a treedb, and nothing else.
|
|
27373
|
+
*
|
|
27374
|
+
* What a treedb graph IS, once it is folded (treedb_fold_model.js):
|
|
27375
|
+
* a forest read left to right. A main topic is a tree (places:
|
|
27376
|
+
* country, region, site, hall), the other topics hang from its
|
|
27377
|
+
* nodes through hooks (devices, users, controllers), a record may
|
|
27378
|
+
* hang from two parents (a device from its place AND its
|
|
27379
|
+
* controller), and every visible node was reached from a root.
|
|
27380
|
+
* Tens to a few hundred cards on screen, never the whole store.
|
|
27381
|
+
*
|
|
27382
|
+
* G6's own layouts either do not know that or pay for not knowing:
|
|
27383
|
+
*
|
|
27384
|
+
* - `dagre` gets the reading direction right and then ranks by
|
|
27385
|
+
* longest path and minimises crossings with a heuristic that
|
|
27386
|
+
* REORDERS the siblings on every run -- open one hook and the
|
|
27387
|
+
* column next to it shuffles. It is O(n·m) per sweep and it is
|
|
27388
|
+
* the one thing that takes time on a big expansion.
|
|
27389
|
+
* - the tree layouts (`compact-box`, `indented`, `dendrogram`,
|
|
27390
|
+
* `mindmap`) draw exactly the shape wanted, but G6 runs them on
|
|
27391
|
+
* a TREE it builds from the edges: a node with two parents is
|
|
27392
|
+
* placed by whichever parent the builder met first, unstably.
|
|
27393
|
+
* - the force family scatters a tree, which is the pile with
|
|
27394
|
+
* physics.
|
|
27395
|
+
*
|
|
27396
|
+
* So: a tidy tree (`tree`) and an outline (`outline`), both fed by
|
|
27397
|
+
* the SAME spanning tree, chosen deterministically:
|
|
27398
|
+
*
|
|
27399
|
+
* - roots are the nodes with no incoming edge, in node order;
|
|
27400
|
+
* - a node belongs to the FIRST parent that reaches it in a
|
|
27401
|
+
* breadth-first walk from the roots -- the place, not the
|
|
27402
|
+
* controller, because the place's column came first -- and the
|
|
27403
|
+
* other links are drawn as edges across the tree;
|
|
27404
|
+
* - the children of a node are ordered by the HOOK they hang from
|
|
27405
|
+
* (the parent's port order, which is the schema's column order)
|
|
27406
|
+
* and then by node order (record order, the pages' order);
|
|
27407
|
+
* - a node nothing reaches (a cycle) is a root too.
|
|
27408
|
+
*
|
|
27409
|
+
* `tree`: the classic tidy tree, left to right. Each depth is a
|
|
27410
|
+
* column as wide as its widest card; a node is centred on the
|
|
27411
|
+
* block of its children; siblings stack with `nodesep` between
|
|
27412
|
+
* them. O(n), and opening a hook moves NOTHING that is not below
|
|
27413
|
+
* or beside it -- the cards keep their order, the eye keeps its
|
|
27414
|
+
* place.
|
|
27415
|
+
*
|
|
27416
|
+
* `outline`: one node per row, indented by depth -- what a JSON
|
|
27417
|
+
* viewer draws, and what a treedb is when read as one. Tall, but
|
|
27418
|
+
* nothing is ever beside anything: the reader scrolls a list.
|
|
27419
|
+
*
|
|
27420
|
+
* Pure: takes plain nodes and edges, returns positions. The G6
|
|
27421
|
+
* classes in c_g6_nodes_tree.js are two thin adapters over it.
|
|
27422
|
+
*
|
|
27423
|
+
* Copyright (c) 2026, ArtGins.
|
|
27424
|
+
* All Rights Reserved.
|
|
27425
|
+
***********************************************************************/
|
|
27426
|
+
var DEFAULTS = {
|
|
27427
|
+
nodesep: 18,
|
|
27428
|
+
ranksep: 110,
|
|
27429
|
+
indent: 48
|
|
27430
|
+
};
|
|
27431
|
+
/************************************************************
|
|
27432
|
+
* The spanning tree of a graph, deterministic.
|
|
27433
|
+
*
|
|
27434
|
+
* nodes [{id, w, h, order}] order: creation index
|
|
27435
|
+
* edges [{source, target, rank}] rank: the hook's position in
|
|
27436
|
+
* the parent's ports (0 = first)
|
|
27437
|
+
*
|
|
27438
|
+
* Returns {roots: [id], children: Map<id, [id]>, depth: Map<id, n>,
|
|
27439
|
+
* by_id: Map<id, node>}
|
|
27440
|
+
************************************************************/
|
|
27441
|
+
function spanning_tree(nodes, edges) {
|
|
27442
|
+
let by_id = /* @__PURE__ */ new Map();
|
|
27443
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
27444
|
+
let n = nodes[i];
|
|
27445
|
+
by_id.set(n.id, Object.assign({ order: i }, n));
|
|
27446
|
+
}
|
|
27447
|
+
let out = /* @__PURE__ */ new Map();
|
|
27448
|
+
let has_parent = /* @__PURE__ */ new Set();
|
|
27449
|
+
for (let e of edges) {
|
|
27450
|
+
if (!by_id.has(e.source) || !by_id.has(e.target) || e.source === e.target) continue;
|
|
27451
|
+
if (!out.has(e.source)) out.set(e.source, []);
|
|
27452
|
+
out.get(e.source).push({
|
|
27453
|
+
id: e.target,
|
|
27454
|
+
rank: e.rank > 0 ? e.rank : 0
|
|
27455
|
+
});
|
|
27456
|
+
has_parent.add(e.target);
|
|
27457
|
+
}
|
|
27458
|
+
for (let list of out.values()) list.sort((a, b) => {
|
|
27459
|
+
if (a.rank !== b.rank) return a.rank - b.rank;
|
|
27460
|
+
return by_id.get(a.id).order - by_id.get(b.id).order;
|
|
27461
|
+
});
|
|
27462
|
+
let roots = [];
|
|
27463
|
+
for (let n of by_id.values()) if (!has_parent.has(n.id)) roots.push(n.id);
|
|
27464
|
+
let children = /* @__PURE__ */ new Map();
|
|
27465
|
+
let depth = /* @__PURE__ */ new Map();
|
|
27466
|
+
let assign = (start) => {
|
|
27467
|
+
let queue = [start];
|
|
27468
|
+
depth.set(start, 0);
|
|
27469
|
+
while (queue.length) {
|
|
27470
|
+
let id = queue.shift();
|
|
27471
|
+
let kids = [];
|
|
27472
|
+
for (let link of out.get(id) || []) {
|
|
27473
|
+
if (depth.has(link.id)) continue;
|
|
27474
|
+
depth.set(link.id, depth.get(id) + 1);
|
|
27475
|
+
kids.push(link.id);
|
|
27476
|
+
queue.push(link.id);
|
|
27477
|
+
}
|
|
27478
|
+
children.set(id, kids);
|
|
27479
|
+
}
|
|
27480
|
+
};
|
|
27481
|
+
for (let root of roots) assign(root);
|
|
27482
|
+
for (let n of by_id.values()) if (!depth.has(n.id)) {
|
|
27483
|
+
roots.push(n.id);
|
|
27484
|
+
assign(n.id);
|
|
27485
|
+
}
|
|
27486
|
+
return {
|
|
27487
|
+
roots,
|
|
27488
|
+
children,
|
|
27489
|
+
depth,
|
|
27490
|
+
by_id
|
|
27491
|
+
};
|
|
27492
|
+
}
|
|
27493
|
+
/************************************************************
|
|
27494
|
+
* The tidy tree, left to right.
|
|
27495
|
+
*
|
|
27496
|
+
* Returns Map<id, {x, y}> with the CENTRE of each node, the way G6
|
|
27497
|
+
* positions a node.
|
|
27498
|
+
************************************************************/
|
|
27499
|
+
function layout_tree(nodes, edges, opts) {
|
|
27500
|
+
let o = Object.assign({}, DEFAULTS, opts || {});
|
|
27501
|
+
let t = spanning_tree(nodes, edges);
|
|
27502
|
+
let pos = /* @__PURE__ */ new Map();
|
|
27503
|
+
let col_w = [];
|
|
27504
|
+
for (let [id, d] of t.depth) {
|
|
27505
|
+
let w = t.by_id.get(id).w;
|
|
27506
|
+
col_w[d] = Math.max(col_w[d] || 0, w);
|
|
27507
|
+
}
|
|
27508
|
+
let col_x = [];
|
|
27509
|
+
let x = 0;
|
|
27510
|
+
for (let d = 0; d < col_w.length; d++) {
|
|
27511
|
+
col_x[d] = x;
|
|
27512
|
+
x += (col_w[d] || 0) + o.ranksep;
|
|
27513
|
+
}
|
|
27514
|
+
let height = /* @__PURE__ */ new Map();
|
|
27515
|
+
let measure = (id) => {
|
|
27516
|
+
let kids = t.children.get(id) || [];
|
|
27517
|
+
let own = t.by_id.get(id).h;
|
|
27518
|
+
if (!kids.length) {
|
|
27519
|
+
height.set(id, own);
|
|
27520
|
+
return own;
|
|
27521
|
+
}
|
|
27522
|
+
let sum = 0;
|
|
27523
|
+
for (let i = 0; i < kids.length; i++) {
|
|
27524
|
+
sum += measure(kids[i]);
|
|
27525
|
+
if (i > 0) sum += o.nodesep;
|
|
27526
|
+
}
|
|
27527
|
+
let h = Math.max(own, sum);
|
|
27528
|
+
height.set(id, h);
|
|
27529
|
+
return h;
|
|
27530
|
+
};
|
|
27531
|
+
for (let root of t.roots) measure(root);
|
|
27532
|
+
let place = (id, top) => {
|
|
27533
|
+
let n = t.by_id.get(id);
|
|
27534
|
+
let d = t.depth.get(id);
|
|
27535
|
+
let kids = t.children.get(id) || [];
|
|
27536
|
+
let block = height.get(id);
|
|
27537
|
+
let cx = col_x[d] + (col_w[d] - n.w) / 2 + n.w / 2;
|
|
27538
|
+
if (!kids.length) {
|
|
27539
|
+
pos.set(id, {
|
|
27540
|
+
x: cx,
|
|
27541
|
+
y: top + block / 2
|
|
27542
|
+
});
|
|
27543
|
+
return;
|
|
27544
|
+
}
|
|
27545
|
+
let kids_h = 0;
|
|
27546
|
+
for (let i = 0; i < kids.length; i++) {
|
|
27547
|
+
kids_h += height.get(kids[i]);
|
|
27548
|
+
if (i > 0) kids_h += o.nodesep;
|
|
27549
|
+
}
|
|
27550
|
+
let cursor = top + (block - kids_h) / 2;
|
|
27551
|
+
for (let kid of kids) {
|
|
27552
|
+
place(kid, cursor);
|
|
27553
|
+
cursor += height.get(kid) + o.nodesep;
|
|
27554
|
+
}
|
|
27555
|
+
pos.set(id, {
|
|
27556
|
+
x: cx,
|
|
27557
|
+
y: top + block / 2
|
|
27558
|
+
});
|
|
27559
|
+
};
|
|
27560
|
+
let top = 0;
|
|
27561
|
+
for (let root of t.roots) {
|
|
27562
|
+
place(root, top);
|
|
27563
|
+
top += height.get(root) + o.nodesep * 2;
|
|
27564
|
+
}
|
|
27565
|
+
return pos;
|
|
27566
|
+
}
|
|
27567
|
+
/************************************************************
|
|
27568
|
+
* The radial tree: the root in the middle, a ring per depth.
|
|
27569
|
+
*
|
|
27570
|
+
* G6's `radial` places by MDS on the graph distances and pushes
|
|
27571
|
+
* overlaps apart afterwards, with a ring radius it is TOLD; twenty
|
|
27572
|
+
* cards of 172px on a ring of radius 200 have 52px each and pile
|
|
27573
|
+
* up. This one is the radial form of the tidy tree above: every
|
|
27574
|
+
* subtree gets an angular SECTOR proportional to its leaves, a node
|
|
27575
|
+
* sits in the middle of its sector, and the radius of each ring is
|
|
27576
|
+
* the larger of "one step further out than the ring before" and
|
|
27577
|
+
* "long enough for its nodes to sit side by side" -- so a hall
|
|
27578
|
+
* with a hundred devices is a wide, sparse fan, and nothing
|
|
27579
|
+
* overlaps by construction. Several roots share the circle around
|
|
27580
|
+
* a virtual centre.
|
|
27581
|
+
*
|
|
27582
|
+
* ranksep the step between rings
|
|
27583
|
+
* nodesep the gap between neighbours on a ring
|
|
27584
|
+
************************************************************/
|
|
27585
|
+
function layout_radial(nodes, edges, opts) {
|
|
27586
|
+
let o = Object.assign({}, DEFAULTS, opts || {});
|
|
27587
|
+
let t = spanning_tree(nodes, edges);
|
|
27588
|
+
let pos = /* @__PURE__ */ new Map();
|
|
27589
|
+
if (!t.roots.length) return pos;
|
|
27590
|
+
let leaves = /* @__PURE__ */ new Map();
|
|
27591
|
+
let count = (id) => {
|
|
27592
|
+
let kids = t.children.get(id) || [];
|
|
27593
|
+
if (!kids.length) {
|
|
27594
|
+
leaves.set(id, 1);
|
|
27595
|
+
return 1;
|
|
27596
|
+
}
|
|
27597
|
+
let n = 0;
|
|
27598
|
+
for (let kid of kids) n += count(kid);
|
|
27599
|
+
leaves.set(id, n);
|
|
27600
|
+
return n;
|
|
27601
|
+
};
|
|
27602
|
+
let total = 0;
|
|
27603
|
+
for (let root of t.roots) total += count(root);
|
|
27604
|
+
let single = t.roots.length === 1;
|
|
27605
|
+
let ring_of = (id) => t.depth.get(id) + (single ? 0 : 1);
|
|
27606
|
+
let angle = /* @__PURE__ */ new Map();
|
|
27607
|
+
let rings = [];
|
|
27608
|
+
let sector = (id, from, span) => {
|
|
27609
|
+
let a = from + span / 2;
|
|
27610
|
+
angle.set(id, a);
|
|
27611
|
+
let ring = ring_of(id);
|
|
27612
|
+
if (!rings[ring]) rings[ring] = [];
|
|
27613
|
+
let n = t.by_id.get(id);
|
|
27614
|
+
rings[ring].push({
|
|
27615
|
+
id,
|
|
27616
|
+
angle: a,
|
|
27617
|
+
diag: Math.hypot(n.w, n.h)
|
|
27618
|
+
});
|
|
27619
|
+
let cursor = from;
|
|
27620
|
+
for (let kid of t.children.get(id) || []) {
|
|
27621
|
+
let part = span * leaves.get(kid) / leaves.get(id);
|
|
27622
|
+
sector(kid, cursor, part);
|
|
27623
|
+
cursor += part;
|
|
27624
|
+
}
|
|
27625
|
+
};
|
|
27626
|
+
if (single) {
|
|
27627
|
+
let root = t.roots[0];
|
|
27628
|
+
angle.set(root, 0);
|
|
27629
|
+
let cursor = -Math.PI / 2;
|
|
27630
|
+
for (let kid of t.children.get(root) || []) {
|
|
27631
|
+
let part = 2 * Math.PI * leaves.get(kid) / leaves.get(root);
|
|
27632
|
+
sector(kid, cursor, part);
|
|
27633
|
+
cursor += part;
|
|
27634
|
+
}
|
|
27635
|
+
} else {
|
|
27636
|
+
let cursor = -Math.PI / 2;
|
|
27637
|
+
for (let root of t.roots) {
|
|
27638
|
+
let part = 2 * Math.PI * leaves.get(root) / total;
|
|
27639
|
+
sector(root, cursor, part);
|
|
27640
|
+
cursor += part;
|
|
27641
|
+
}
|
|
27642
|
+
}
|
|
27643
|
+
let radius = [0];
|
|
27644
|
+
for (let ring = 1; ring < rings.length; ring++) {
|
|
27645
|
+
let r = radius[ring - 1] + o.ranksep;
|
|
27646
|
+
let list = (rings[ring] || []).slice().sort((a, b) => a.angle - b.angle);
|
|
27647
|
+
for (let i = 0; i < list.length && list.length > 1; i++) {
|
|
27648
|
+
let a = list[i];
|
|
27649
|
+
let b = list[(i + 1) % list.length];
|
|
27650
|
+
let gap = i + 1 < list.length ? b.angle - a.angle : b.angle + 2 * Math.PI - a.angle;
|
|
27651
|
+
if (gap > 1e-9) r = Math.max(r, (a.diag / 2 + b.diag / 2 + o.nodesep) / gap);
|
|
27652
|
+
}
|
|
27653
|
+
radius[ring] = r;
|
|
27654
|
+
}
|
|
27655
|
+
for (let [id, a] of angle) {
|
|
27656
|
+
let r = radius[ring_of(id)] || 0;
|
|
27657
|
+
pos.set(id, {
|
|
27658
|
+
x: r * Math.cos(a),
|
|
27659
|
+
y: r * Math.sin(a)
|
|
27660
|
+
});
|
|
27661
|
+
}
|
|
27662
|
+
return pos;
|
|
27663
|
+
}
|
|
27664
|
+
/************************************************************
|
|
27665
|
+
* The outline: one node per row, indented by depth, in the
|
|
27666
|
+
* pre-order of the spanning tree. Rows are left-aligned on
|
|
27667
|
+
* their indent, so a column of chips reads as a list.
|
|
27668
|
+
************************************************************/
|
|
27669
|
+
function layout_outline(nodes, edges, opts) {
|
|
27670
|
+
let o = Object.assign({}, DEFAULTS, opts || {});
|
|
27671
|
+
let t = spanning_tree(nodes, edges);
|
|
27672
|
+
let pos = /* @__PURE__ */ new Map();
|
|
27673
|
+
let y = 0;
|
|
27674
|
+
let visit = (id) => {
|
|
27675
|
+
let n = t.by_id.get(id);
|
|
27676
|
+
let d = t.depth.get(id);
|
|
27677
|
+
pos.set(id, {
|
|
27678
|
+
x: d * o.indent + n.w / 2,
|
|
27679
|
+
y: y + n.h / 2
|
|
27680
|
+
});
|
|
27681
|
+
y += n.h + o.nodesep;
|
|
27682
|
+
for (let kid of t.children.get(id) || []) visit(kid);
|
|
27683
|
+
};
|
|
27684
|
+
for (let root of t.roots) {
|
|
27685
|
+
visit(root);
|
|
27686
|
+
y += o.nodesep;
|
|
27687
|
+
}
|
|
27688
|
+
return pos;
|
|
27689
|
+
}
|
|
27690
|
+
//#endregion
|
|
27317
27691
|
//#region node_modules/@babel/runtime/helpers/esm/typeof.js
|
|
27318
27692
|
function _typeof(o) {
|
|
27319
27693
|
"@babel/helpers - typeof";
|
|
@@ -54134,6 +54508,16 @@ var SELECT_RING = "rgba(59,130,246,0.95)";
|
|
|
54134
54508
|
***************************************************************/
|
|
54135
54509
|
var _layouts = {
|
|
54136
54510
|
"manual": { type: "manual" },
|
|
54511
|
+
"treedb-tree": {
|
|
54512
|
+
type: "treedb-tree",
|
|
54513
|
+
nodesep: 18,
|
|
54514
|
+
ranksep: 110
|
|
54515
|
+
},
|
|
54516
|
+
"treedb-outline": {
|
|
54517
|
+
type: "treedb-outline",
|
|
54518
|
+
nodesep: 12,
|
|
54519
|
+
indent: 48
|
|
54520
|
+
},
|
|
54137
54521
|
"dagre": {
|
|
54138
54522
|
type: "dagre",
|
|
54139
54523
|
rankdir: "LR",
|
|
@@ -54146,6 +54530,11 @@ var _layouts = {
|
|
|
54146
54530
|
nodesep: 30,
|
|
54147
54531
|
ranksep: 80
|
|
54148
54532
|
},
|
|
54533
|
+
"radial": {
|
|
54534
|
+
type: "treedb-radial",
|
|
54535
|
+
nodesep: 24,
|
|
54536
|
+
ranksep: 180
|
|
54537
|
+
},
|
|
54149
54538
|
"d3-force": {
|
|
54150
54539
|
type: "d3-force",
|
|
54151
54540
|
link: {
|
|
@@ -54425,6 +54814,9 @@ function register_layouts(gobj) {
|
|
|
54425
54814
|
if (!_g6_extensions_registered) {
|
|
54426
54815
|
_g6_extensions_registered = true;
|
|
54427
54816
|
(0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "manual", ManualLayout);
|
|
54817
|
+
(0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "treedb-tree", TreedbTreeLayout);
|
|
54818
|
+
(0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "treedb-outline", TreedbOutlineLayout);
|
|
54819
|
+
(0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "treedb-radial", TreedbRadialLayout);
|
|
54428
54820
|
(0, _antv_g6.register)(_antv_g6.ExtensionCategory.NODE, "light", LightNode);
|
|
54429
54821
|
}
|
|
54430
54822
|
}
|
|
@@ -55036,8 +55428,13 @@ function build_ports(gobj, desc) {
|
|
|
55036
55428
|
* included, because the saved arrangements were made with the
|
|
55037
55429
|
* ports on the top and bottom edges.
|
|
55038
55430
|
************************************************************/
|
|
55431
|
+
var LR_LAYOUTS = [
|
|
55432
|
+
"dagre",
|
|
55433
|
+
"treedb-tree",
|
|
55434
|
+
"treedb-outline"
|
|
55435
|
+
];
|
|
55039
55436
|
function layout_direction(gobj) {
|
|
55040
|
-
return gobj.priv.layout
|
|
55437
|
+
return (0, _yuneta_gobj_js.str_in_list)(LR_LAYOUTS, gobj.priv.layout) ? "LR" : "TB";
|
|
55041
55438
|
}
|
|
55042
55439
|
function edge_type_for(gobj) {
|
|
55043
55440
|
return layout_direction(gobj) === "LR" ? "cubic-horizontal" : "cubic";
|
|
@@ -56037,6 +56434,73 @@ var ManualLayout = class extends _antv_g6.BaseLayout {
|
|
|
56037
56434
|
}
|
|
56038
56435
|
};
|
|
56039
56436
|
/************************************************************
|
|
56437
|
+
* Custom G6 layouts: TreedbTreeLayout / TreedbOutlineLayout.
|
|
56438
|
+
*
|
|
56439
|
+
* Thin adapters over treedb_layout.js: the nodes with their sizes,
|
|
56440
|
+
* the edges with the RANK of the hook they leave by (the position
|
|
56441
|
+
* of the source port in the parent's ports, which is the schema's
|
|
56442
|
+
* column order), and the positions back. A `+N` chip's edge has a
|
|
56443
|
+
* source port too, so the chip sits at the end of its hook's
|
|
56444
|
+
* children, where the page ends.
|
|
56445
|
+
************************************************************/
|
|
56446
|
+
function treedb_layout_input(data) {
|
|
56447
|
+
const { nodes = [], edges = [] } = data;
|
|
56448
|
+
let ports_of = /* @__PURE__ */ new Map();
|
|
56449
|
+
return {
|
|
56450
|
+
nodes: nodes.map((node) => {
|
|
56451
|
+
let size = node.style && node.style.size || [172, 96];
|
|
56452
|
+
let w = (0, _yuneta_gobj_js.is_array)(size) ? size[0] : size;
|
|
56453
|
+
let h = (0, _yuneta_gobj_js.is_array)(size) ? size.length > 1 ? size[1] : size[0] : size;
|
|
56454
|
+
let ports = node.style && (0, _yuneta_gobj_js.is_array)(node.style.ports) ? node.style.ports : [];
|
|
56455
|
+
ports_of.set(node.id, ports.map((p) => p.key));
|
|
56456
|
+
return {
|
|
56457
|
+
id: node.id,
|
|
56458
|
+
w,
|
|
56459
|
+
h
|
|
56460
|
+
};
|
|
56461
|
+
}),
|
|
56462
|
+
edges: edges.map((edge) => {
|
|
56463
|
+
let port = edge.style ? edge.style.sourcePort : void 0;
|
|
56464
|
+
let keys = ports_of.get(edge.source) || [];
|
|
56465
|
+
let rank = port ? keys.indexOf(port) : -1;
|
|
56466
|
+
return {
|
|
56467
|
+
source: edge.source,
|
|
56468
|
+
target: edge.target,
|
|
56469
|
+
rank: rank >= 0 ? rank : 0
|
|
56470
|
+
};
|
|
56471
|
+
})
|
|
56472
|
+
};
|
|
56473
|
+
}
|
|
56474
|
+
function treedb_layout_output(pos) {
|
|
56475
|
+
let out = [];
|
|
56476
|
+
for (let [id, p] of pos) out.push({
|
|
56477
|
+
id,
|
|
56478
|
+
style: {
|
|
56479
|
+
x: p.x,
|
|
56480
|
+
y: p.y
|
|
56481
|
+
}
|
|
56482
|
+
});
|
|
56483
|
+
return { nodes: out };
|
|
56484
|
+
}
|
|
56485
|
+
var TreedbTreeLayout = class extends _antv_g6.BaseLayout {
|
|
56486
|
+
async execute(data, options) {
|
|
56487
|
+
let input = treedb_layout_input(data);
|
|
56488
|
+
return treedb_layout_output(layout_tree(input.nodes, input.edges, options));
|
|
56489
|
+
}
|
|
56490
|
+
};
|
|
56491
|
+
var TreedbOutlineLayout = class extends _antv_g6.BaseLayout {
|
|
56492
|
+
async execute(data, options) {
|
|
56493
|
+
let input = treedb_layout_input(data);
|
|
56494
|
+
return treedb_layout_output(layout_outline(input.nodes, input.edges, options));
|
|
56495
|
+
}
|
|
56496
|
+
};
|
|
56497
|
+
var TreedbRadialLayout = class extends _antv_g6.BaseLayout {
|
|
56498
|
+
async execute(data, options) {
|
|
56499
|
+
let input = treedb_layout_input(data);
|
|
56500
|
+
return treedb_layout_output(layout_radial(input.nodes, input.edges, options));
|
|
56501
|
+
}
|
|
56502
|
+
};
|
|
56503
|
+
/************************************************************
|
|
56040
56504
|
* Node selection and resize handles
|
|
56041
56505
|
************************************************************/
|
|
56042
56506
|
var RESIZE_HANDLE_SIZE = 8;
|
|
@@ -57698,7 +58162,7 @@ function auto_layout(gobj) {
|
|
|
57698
58162
|
if (priv._layout_asked) return false;
|
|
57699
58163
|
if (priv._nodes_total > 0 && priv._nodes_placed * 2 > priv._nodes_total) return false;
|
|
57700
58164
|
if (!priv.graph) return false;
|
|
57701
|
-
let name = "
|
|
58165
|
+
let name = "treedb-tree";
|
|
57702
58166
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "layout", name);
|
|
57703
58167
|
try {
|
|
57704
58168
|
priv.graph.setLayout(_layouts[name]);
|
|
@@ -60535,6 +60999,9 @@ function option_label(name) {
|
|
|
60535
60999
|
case "writing": return (0, i18next.t)("writing");
|
|
60536
61000
|
case "edition": return (0, i18next.t)("edition");
|
|
60537
61001
|
case "manual": return (0, i18next.t)("manual");
|
|
61002
|
+
case "treedb-tree": return (0, i18next.t)("treedb-tree");
|
|
61003
|
+
case "treedb-outline": return (0, i18next.t)("treedb-outline");
|
|
61004
|
+
case "radial": return (0, i18next.t)("radial");
|
|
60538
61005
|
case "dagre": return (0, i18next.t)("dagre");
|
|
60539
61006
|
case "antv-dagre": return (0, i18next.t)("antv-dagre");
|
|
60540
61007
|
case "d3-force": return (0, i18next.t)("d3-force");
|