@yuneta/gobj-ui 7.23.64 → 7.23.66
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 +55 -0
- package/dist/gobj-ui.cjs.js +1372 -187
- package/dist/gobj-ui.es.js +1372 -187
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +1185 -254
- package/src/c_yui_shell.js +78 -7
- package/src/c_yui_treedb_graph.js +38 -0
- package/src/shell_toolbar_helpers.js +12 -2
- package/src/shell_toolbar_helpers.test.js +55 -1
- package/src/treedb_fold_model.js +592 -0
- package/src/treedb_fold_model.test.js +308 -0
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -268,7 +268,8 @@ var TOOLBAR_ACTION_TYPES = [
|
|
|
268
268
|
"navigate",
|
|
269
269
|
"drawer",
|
|
270
270
|
"event",
|
|
271
|
-
"dropdown"
|
|
271
|
+
"dropdown",
|
|
272
|
+
"link"
|
|
272
273
|
];
|
|
273
274
|
/************************************************************
|
|
274
275
|
* Decide which renderer to use for a toolbar item.
|
|
@@ -337,6 +338,7 @@ function validate_action(action, owner_id) {
|
|
|
337
338
|
}
|
|
338
339
|
if (action.type === "navigate" && !_is_non_empty_string(action.route)) warnings.push(`toolbar item '${id}' (action.type:navigate) requires 'route'`);
|
|
339
340
|
if (action.type === "event" && !_is_non_empty_string(action.event)) warnings.push(`toolbar item '${id}' (action.type:event) requires 'event' name`);
|
|
341
|
+
if (action.type === "link" && !_is_non_empty_string(action.url)) warnings.push(`toolbar item '${id}' (action.type:link) requires 'url'`);
|
|
340
342
|
if (action.type === "dropdown") {
|
|
341
343
|
let nested = validate_dropdown_action(action, id);
|
|
342
344
|
for (let w of nested) warnings.push(w);
|
|
@@ -1684,6 +1686,20 @@ function safe_id(s) {
|
|
|
1684
1686
|
* drawer { type:"drawer", op:"toggle"|"open"|"close", menu_id? }
|
|
1685
1687
|
* event { type:"event", event, kw? }
|
|
1686
1688
|
* dropdown { type:"dropdown", items[] } (toolbar-only)
|
|
1689
|
+
* link { type:"link", url, target? }
|
|
1690
|
+
*
|
|
1691
|
+
* `link` leaves the application, so on a toolbar item and on the brand
|
|
1692
|
+
* it is rendered as a real <a href> rather than a <button>:
|
|
1693
|
+
* middle-click, open-in-new-tab and the url in the status bar are what
|
|
1694
|
+
* make a link recognisable as one, and a button that assigns
|
|
1695
|
+
* location.href has none of them. It is also why it does not become
|
|
1696
|
+
* an FSM event -- there is no state of this application to transition;
|
|
1697
|
+
* the page is being left. A target of "_blank" gets
|
|
1698
|
+
* rel="noopener noreferrer" for free.
|
|
1699
|
+
*
|
|
1700
|
+
* INSIDE A DROPDOWN it stays a <button>, deliberately: those rows are
|
|
1701
|
+
* role="menuitemradio" in a menu, and a link is not a choice among
|
|
1702
|
+
* options. There the dispatcher navigates.
|
|
1687
1703
|
*
|
|
1688
1704
|
* Per-item `show_on` is honoured: each rendered item is wrapped with
|
|
1689
1705
|
* the same Bulma-helper logic as zones.
|
|
@@ -1770,6 +1786,26 @@ function badge_node(value) {
|
|
|
1770
1786
|
];
|
|
1771
1787
|
}
|
|
1772
1788
|
/************************************************************
|
|
1789
|
+
* The attributes that turn a toolbar item into a real link, or
|
|
1790
|
+
* null when this item is not one. Answering null is what keeps
|
|
1791
|
+
* every caller's default (a <button> that dispatches an action)
|
|
1792
|
+
* exactly as it was.
|
|
1793
|
+
************************************************************/
|
|
1794
|
+
function toolbar_link_attrs(it) {
|
|
1795
|
+
let action = it && it.action || {};
|
|
1796
|
+
if (action.type !== "link") return null;
|
|
1797
|
+
if ((0, _yuneta_gobj_js.empty_string)(action.url)) {
|
|
1798
|
+
(0, _yuneta_gobj_js.log_warning)(`C_YUI_SHELL: toolbar item '${it.id || "?"}' has action.type "link" without a url`);
|
|
1799
|
+
return null;
|
|
1800
|
+
}
|
|
1801
|
+
let attrs = { href: action.url };
|
|
1802
|
+
if (!(0, _yuneta_gobj_js.empty_string)(action.target)) {
|
|
1803
|
+
attrs.target = action.target;
|
|
1804
|
+
if (action.target === "_blank") attrs.rel = "noopener noreferrer";
|
|
1805
|
+
}
|
|
1806
|
+
return attrs;
|
|
1807
|
+
}
|
|
1808
|
+
/************************************************************
|
|
1773
1809
|
* Renderer for the default ("action") item kind.
|
|
1774
1810
|
************************************************************/
|
|
1775
1811
|
function build_toolbar_action_item(gobj, it) {
|
|
@@ -1808,12 +1844,17 @@ function build_toolbar_action_item(gobj, it) {
|
|
|
1808
1844
|
btn_attrs.title = tip;
|
|
1809
1845
|
btn_attrs["data-i18n-title"] = tip;
|
|
1810
1846
|
}
|
|
1847
|
+
let link = toolbar_link_attrs(it);
|
|
1848
|
+
if (link) {
|
|
1849
|
+
delete btn_attrs.type;
|
|
1850
|
+
Object.assign(btn_attrs, link);
|
|
1851
|
+
}
|
|
1811
1852
|
let $item = (0, _yuneta_gobj_js.createElement2)([
|
|
1812
|
-
"button",
|
|
1853
|
+
link ? "a" : "button",
|
|
1813
1854
|
btn_attrs,
|
|
1814
1855
|
children
|
|
1815
1856
|
]);
|
|
1816
|
-
$item.addEventListener("click", (ev) => {
|
|
1857
|
+
if (!link) $item.addEventListener("click", (ev) => {
|
|
1817
1858
|
ev.preventDefault();
|
|
1818
1859
|
handle_toolbar_action(gobj, it, $item);
|
|
1819
1860
|
});
|
|
@@ -1845,7 +1886,11 @@ function build_toolbar_brand_item(gobj, it) {
|
|
|
1845
1886
|
attrs["aria-haspopup"] = "menu";
|
|
1846
1887
|
attrs["aria-expanded"] = "false";
|
|
1847
1888
|
}
|
|
1848
|
-
|
|
1889
|
+
let link = toolbar_link_attrs(it);
|
|
1890
|
+
if (link) {
|
|
1891
|
+
tag = "a";
|
|
1892
|
+
Object.assign(attrs, link);
|
|
1893
|
+
} else if (action_type === "") tag = "div";
|
|
1849
1894
|
else attrs.type = "button";
|
|
1850
1895
|
let $item = (0, _yuneta_gobj_js.createElement2)([
|
|
1851
1896
|
tag,
|
|
@@ -1863,7 +1908,7 @@ function build_toolbar_brand_item(gobj, it) {
|
|
|
1863
1908
|
it.wordmark
|
|
1864
1909
|
]]
|
|
1865
1910
|
]);
|
|
1866
|
-
if (action_type !== "") $item.addEventListener("click", (ev) => {
|
|
1911
|
+
if (action_type !== "" && !link) $item.addEventListener("click", (ev) => {
|
|
1867
1912
|
ev.preventDefault();
|
|
1868
1913
|
handle_toolbar_action(gobj, it, $item);
|
|
1869
1914
|
});
|
|
@@ -2035,6 +2080,12 @@ function handle_toolbar_action(gobj, item, $trigger) {
|
|
|
2035
2080
|
case "dropdown":
|
|
2036
2081
|
toggle_toolbar_dropdown(gobj, item, action, $trigger);
|
|
2037
2082
|
break;
|
|
2083
|
+
case "link":
|
|
2084
|
+
if (!(0, _yuneta_gobj_js.empty_string)(action.url)) {
|
|
2085
|
+
if (action.target === "_blank") window.open(action.url, "_blank", "noopener,noreferrer");
|
|
2086
|
+
else window.location.assign(action.url);
|
|
2087
|
+
} else (0, _yuneta_gobj_js.log_warning)(`C_YUI_SHELL: toolbar item '${item.id || "?"}' has action.type "link" without a url`);
|
|
2088
|
+
break;
|
|
2038
2089
|
default: (0, _yuneta_gobj_js.log_warning)(`C_YUI_SHELL: toolbar item '${item.id || "?"}' has no/unknown action.type`);
|
|
2039
2090
|
}
|
|
2040
2091
|
}
|
|
@@ -11203,7 +11254,7 @@ function ensure_drag_canvas_patch() {
|
|
|
11203
11254
|
* Constants
|
|
11204
11255
|
***************************************************************/
|
|
11205
11256
|
var GCLASS_NAME$13 = "C_YUI_JSON_GRAPH";
|
|
11206
|
-
var FOLD_SWALLOWED_EVENTS = [
|
|
11257
|
+
var FOLD_SWALLOWED_EVENTS$1 = [
|
|
11207
11258
|
"pointerdown",
|
|
11208
11259
|
"pointerup",
|
|
11209
11260
|
"mousedown",
|
|
@@ -11511,7 +11562,7 @@ function build_ui$12(gobj) {
|
|
|
11511
11562
|
keep: $card ? $card.getAttribute("data-json-path") : ""
|
|
11512
11563
|
}, gobj);
|
|
11513
11564
|
};
|
|
11514
|
-
for (let type of FOLD_SWALLOWED_EVENTS) $mount.addEventListener(type, priv.fold_listener, true);
|
|
11565
|
+
for (let type of FOLD_SWALLOWED_EVENTS$1) $mount.addEventListener(type, priv.fold_listener, true);
|
|
11515
11566
|
} else (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no canvas mount, fold handles are dead`);
|
|
11516
11567
|
priv.$layout_select = $container.querySelector(".JSON_GRAPH_LAYOUT");
|
|
11517
11568
|
priv.$find_input = $container.querySelector(".JSON_GRAPH_FIND_INPUT");
|
|
@@ -11527,7 +11578,7 @@ function destroy_ui$8(gobj) {
|
|
|
11527
11578
|
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
11528
11579
|
if (priv.fold_listener) {
|
|
11529
11580
|
let $mount = $container ? $container.querySelector("#" + priv.canvas_id) : null;
|
|
11530
|
-
if ($mount) for (let type of FOLD_SWALLOWED_EVENTS) $mount.removeEventListener(type, priv.fold_listener, true);
|
|
11581
|
+
if ($mount) for (let type of FOLD_SWALLOWED_EVENTS$1) $mount.removeEventListener(type, priv.fold_listener, true);
|
|
11531
11582
|
priv.fold_listener = null;
|
|
11532
11583
|
}
|
|
11533
11584
|
if ($container) {
|
|
@@ -12378,7 +12429,7 @@ function ac_change_layout$2(gobj, event, kw, src) {
|
|
|
12378
12429
|
* would throw away where the reader was looking. The toolbar
|
|
12379
12430
|
* pair, which changes everything at once, does refit.
|
|
12380
12431
|
************************************************************/
|
|
12381
|
-
function ac_toggle_fold(gobj, event, kw, src) {
|
|
12432
|
+
function ac_toggle_fold$1(gobj, event, kw, src) {
|
|
12382
12433
|
let priv = gobj.priv;
|
|
12383
12434
|
let path = kw && kw.path || "";
|
|
12384
12435
|
if ((0, _yuneta_gobj_js.empty_string)(path)) {
|
|
@@ -12417,7 +12468,7 @@ function ac_find_nodes$3(gobj, event, kw, src) {
|
|
|
12417
12468
|
/************************************************************
|
|
12418
12469
|
* EV_EXPAND_ALL
|
|
12419
12470
|
************************************************************/
|
|
12420
|
-
function ac_expand_all$
|
|
12471
|
+
function ac_expand_all$4(gobj, event, kw, src) {
|
|
12421
12472
|
let priv = gobj.priv;
|
|
12422
12473
|
if (priv.collapsed.size === 0) return 0;
|
|
12423
12474
|
priv.collapsed.clear();
|
|
@@ -12429,7 +12480,7 @@ function ac_expand_all$2(gobj, event, kw, src) {
|
|
|
12429
12480
|
/************************************************************
|
|
12430
12481
|
* EV_COLLAPSE_ALL — fold every card but the root.
|
|
12431
12482
|
************************************************************/
|
|
12432
|
-
function ac_collapse_all$
|
|
12483
|
+
function ac_collapse_all$4(gobj, event, kw, src) {
|
|
12433
12484
|
let priv = gobj.priv;
|
|
12434
12485
|
let json_data = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "json_data");
|
|
12435
12486
|
if (!json_data) return 0;
|
|
@@ -12719,7 +12770,7 @@ function create_gclass$13(gclass_name) {
|
|
|
12719
12770
|
],
|
|
12720
12771
|
[
|
|
12721
12772
|
"EV_TOGGLE_FOLD",
|
|
12722
|
-
ac_toggle_fold,
|
|
12773
|
+
ac_toggle_fold$1,
|
|
12723
12774
|
null
|
|
12724
12775
|
],
|
|
12725
12776
|
[
|
|
@@ -12729,12 +12780,12 @@ function create_gclass$13(gclass_name) {
|
|
|
12729
12780
|
],
|
|
12730
12781
|
[
|
|
12731
12782
|
"EV_EXPAND_ALL",
|
|
12732
|
-
ac_expand_all$
|
|
12783
|
+
ac_expand_all$4,
|
|
12733
12784
|
null
|
|
12734
12785
|
],
|
|
12735
12786
|
[
|
|
12736
12787
|
"EV_COLLAPSE_ALL",
|
|
12737
|
-
ac_collapse_all$
|
|
12788
|
+
ac_collapse_all$4,
|
|
12738
12789
|
null
|
|
12739
12790
|
],
|
|
12740
12791
|
[
|
|
@@ -13799,7 +13850,7 @@ function ac_search$2(gobj, event, kw, src) {
|
|
|
13799
13850
|
/************************************************************
|
|
13800
13851
|
* EV_EXPAND_ALL — expand every already-loaded container
|
|
13801
13852
|
************************************************************/
|
|
13802
|
-
function ac_expand_all$
|
|
13853
|
+
function ac_expand_all$3(gobj, event, kw, src) {
|
|
13803
13854
|
let priv = gobj.priv;
|
|
13804
13855
|
if (priv.root === null || priv.root === void 0) return 0;
|
|
13805
13856
|
let paths = [];
|
|
@@ -13817,7 +13868,7 @@ function ac_expand_all$1(gobj, event, kw, src) {
|
|
|
13817
13868
|
/************************************************************
|
|
13818
13869
|
* EV_COLLAPSE_ALL
|
|
13819
13870
|
************************************************************/
|
|
13820
|
-
function ac_collapse_all$
|
|
13871
|
+
function ac_collapse_all$3(gobj, event, kw, src) {
|
|
13821
13872
|
gobj.priv.expanded.clear();
|
|
13822
13873
|
render_view$1(gobj);
|
|
13823
13874
|
return 0;
|
|
@@ -14002,12 +14053,12 @@ function create_gclass$12(gclass_name) {
|
|
|
14002
14053
|
],
|
|
14003
14054
|
[
|
|
14004
14055
|
"EV_EXPAND_ALL",
|
|
14005
|
-
ac_expand_all$
|
|
14056
|
+
ac_expand_all$3,
|
|
14006
14057
|
null
|
|
14007
14058
|
],
|
|
14008
14059
|
[
|
|
14009
14060
|
"EV_COLLAPSE_ALL",
|
|
14010
|
-
ac_collapse_all$
|
|
14061
|
+
ac_collapse_all$3,
|
|
14011
14062
|
null
|
|
14012
14063
|
],
|
|
14013
14064
|
[
|
|
@@ -19010,7 +19061,7 @@ function ac_toggle_collapse(gobj, event, kw, src) {
|
|
|
19010
19061
|
/************************************************************
|
|
19011
19062
|
* Force every node visible (overrides threshold).
|
|
19012
19063
|
************************************************************/
|
|
19013
|
-
function ac_expand_all(gobj, event, kw, src) {
|
|
19064
|
+
function ac_expand_all$2(gobj, event, kw, src) {
|
|
19014
19065
|
let priv = gobj.priv;
|
|
19015
19066
|
if (!priv.collapse_state) priv.collapse_state = {};
|
|
19016
19067
|
if (priv.node_by_id) for (let id of Object.keys(priv.node_by_id)) {
|
|
@@ -19023,7 +19074,7 @@ function ac_expand_all(gobj, event, kw, src) {
|
|
|
19023
19074
|
/************************************************************
|
|
19024
19075
|
* Collapse every node that has children.
|
|
19025
19076
|
************************************************************/
|
|
19026
|
-
function ac_collapse_all(gobj, event, kw, src) {
|
|
19077
|
+
function ac_collapse_all$2(gobj, event, kw, src) {
|
|
19027
19078
|
let priv = gobj.priv;
|
|
19028
19079
|
if (!priv.collapse_state) priv.collapse_state = {};
|
|
19029
19080
|
if (priv.node_by_id) for (let id of Object.keys(priv.node_by_id)) {
|
|
@@ -19375,12 +19426,12 @@ function create_gclass$9(gclass_name) {
|
|
|
19375
19426
|
],
|
|
19376
19427
|
[
|
|
19377
19428
|
"EV_EXPAND_ALL",
|
|
19378
|
-
ac_expand_all,
|
|
19429
|
+
ac_expand_all$2,
|
|
19379
19430
|
null
|
|
19380
19431
|
],
|
|
19381
19432
|
[
|
|
19382
19433
|
"EV_COLLAPSE_ALL",
|
|
19383
|
-
ac_collapse_all,
|
|
19434
|
+
ac_collapse_all$2,
|
|
19384
19435
|
null
|
|
19385
19436
|
],
|
|
19386
19437
|
[
|
|
@@ -26749,6 +26800,429 @@ function node_label(desc, record) {
|
|
|
26749
26800
|
return id;
|
|
26750
26801
|
}
|
|
26751
26802
|
//#endregion
|
|
26803
|
+
//#region src/treedb_fold_model.js
|
|
26804
|
+
/***********************************************************************
|
|
26805
|
+
* treedb_fold_model.js
|
|
26806
|
+
*
|
|
26807
|
+
* A treedb read as a TREE, and which part of it is on screen.
|
|
26808
|
+
*
|
|
26809
|
+
* A treedb is a graph: a record hangs from every parent one of its
|
|
26810
|
+
* fkeys names, and a parent reaches its children through a hook.
|
|
26811
|
+
* Drawn whole it is a pile -- a nave with a hundred and forty
|
|
26812
|
+
* devices is a row of a hundred and forty cards -- so the graph
|
|
26813
|
+
* opens FOLDED, the way a JSON viewer opens: the roots, a level or
|
|
26814
|
+
* two under them, and a count on every cut saying what went with it.
|
|
26815
|
+
*
|
|
26816
|
+
* This module is the arithmetic of that, and nothing else: no G6,
|
|
26817
|
+
* no DOM, no gobj. The graph engine (c_g6_nodes_tree.js) asks it
|
|
26818
|
+
* what is visible and draws exactly that.
|
|
26819
|
+
*
|
|
26820
|
+
* Vocabulary
|
|
26821
|
+
*
|
|
26822
|
+
* node key "topic^id" -- one record
|
|
26823
|
+
* group the children of ONE node through ONE hook,
|
|
26824
|
+
* keyed "topic^id|hook"; the roots of a topic are
|
|
26825
|
+
* a group of their own, keyed "|topic"
|
|
26826
|
+
* shown how many of a group's children are on screen:
|
|
26827
|
+
* absent means folded, a number means the first
|
|
26828
|
+
* N, in record order, a page at a time
|
|
26829
|
+
*
|
|
26830
|
+
* Two rules that are not obvious:
|
|
26831
|
+
*
|
|
26832
|
+
* - A node is visible when SOME visible parent shows it, or it is
|
|
26833
|
+
* a shown root. A record with two parents (a device hangs from
|
|
26834
|
+
* its place AND its controller) is drawn ONCE, under whichever
|
|
26835
|
+
* opened first, and folding one of the two leaves it where the
|
|
26836
|
+
* other one shows it. The union, never the intersection.
|
|
26837
|
+
*
|
|
26838
|
+
* - A parent that the engine does not draw an edge to is not a
|
|
26839
|
+
* parent here either: the `extended` tier (hooks and no fkeys)
|
|
26840
|
+
* gets no edges in draw_link(), so its children are roots. The
|
|
26841
|
+
* tree on screen and the tree in this model have to agree, or a
|
|
26842
|
+
* pill counts children that no line reaches.
|
|
26843
|
+
*
|
|
26844
|
+
* Copyright (c) 2026, ArtGins.
|
|
26845
|
+
* All Rights Reserved.
|
|
26846
|
+
***********************************************************************/
|
|
26847
|
+
var PAGE_SIZE_DEFAULT = 24;
|
|
26848
|
+
/************************************************************
|
|
26849
|
+
* Keys
|
|
26850
|
+
************************************************************/
|
|
26851
|
+
function fold_node_key(topic_name, id) {
|
|
26852
|
+
return `${topic_name}^${id}`;
|
|
26853
|
+
}
|
|
26854
|
+
function fold_group_key(node_key, hook_name) {
|
|
26855
|
+
return `${node_key}|${hook_name}`;
|
|
26856
|
+
}
|
|
26857
|
+
function fold_root_group_key(topic_name) {
|
|
26858
|
+
return `|${topic_name}`;
|
|
26859
|
+
}
|
|
26860
|
+
function fold_split_group_key(group_key) {
|
|
26861
|
+
let i = group_key.lastIndexOf("|");
|
|
26862
|
+
if (i < 0) return null;
|
|
26863
|
+
return {
|
|
26864
|
+
node_key: group_key.slice(0, i),
|
|
26865
|
+
hook: group_key.slice(i + 1)
|
|
26866
|
+
};
|
|
26867
|
+
}
|
|
26868
|
+
/************************************************************
|
|
26869
|
+
* Every parent a record names, decoded, in column order.
|
|
26870
|
+
*
|
|
26871
|
+
* Only the parents that will be DRAWN: a topic missing from
|
|
26872
|
+
* `descs` (a system topic such as `__assets__`, never fetched)
|
|
26873
|
+
* and an `extended` parent (no edge is drawn to one, see the
|
|
26874
|
+
* header) are left out, so a child of nothing else is a root.
|
|
26875
|
+
************************************************************/
|
|
26876
|
+
function parents_of(descs, desc, record) {
|
|
26877
|
+
let out = [];
|
|
26878
|
+
let cols = desc.cols || [];
|
|
26879
|
+
for (let i = 0; i < cols.length; i++) {
|
|
26880
|
+
let col = cols[i];
|
|
26881
|
+
if (!col.fkey) continue;
|
|
26882
|
+
let fkeys = record[col.id];
|
|
26883
|
+
if (!fkeys) continue;
|
|
26884
|
+
let list = (0, _yuneta_gobj_js.is_array)(fkeys) ? fkeys : [fkeys];
|
|
26885
|
+
for (let j = 0; j < list.length; j++) {
|
|
26886
|
+
let ref = (0, _yuneta_gobj_js.treedb_decoder_fkey)(col, list[j]);
|
|
26887
|
+
if (!ref || !ref.topic_name || !ref.hook_name) continue;
|
|
26888
|
+
let parent_desc = descs[ref.topic_name];
|
|
26889
|
+
if (!parent_desc) continue;
|
|
26890
|
+
if (parent_desc.node_treedb_type === "extended") continue;
|
|
26891
|
+
out.push({
|
|
26892
|
+
node_key: fold_node_key(ref.topic_name, String(ref.id)),
|
|
26893
|
+
hook: ref.hook_name,
|
|
26894
|
+
fkey_name: col.id
|
|
26895
|
+
});
|
|
26896
|
+
}
|
|
26897
|
+
}
|
|
26898
|
+
return out;
|
|
26899
|
+
}
|
|
26900
|
+
/************************************************************
|
|
26901
|
+
* Build the model from the topic descriptions and their records.
|
|
26902
|
+
*
|
|
26903
|
+
* descs {topic_name: desc}, system topics included (skipped)
|
|
26904
|
+
* records {topic_name: [record, ...]}, in the order the
|
|
26905
|
+
* backend returned them -- that order is the order of
|
|
26906
|
+
* the children on screen, and of the pages
|
|
26907
|
+
*
|
|
26908
|
+
* Returns
|
|
26909
|
+
* {
|
|
26910
|
+
* nodes: Map<node_key, {key, topic_name, id, record,
|
|
26911
|
+
* parents: [{node_key, hook, fkey_name}],
|
|
26912
|
+
* hooks: {hook: [child_key, ...]}}>
|
|
26913
|
+
* groups: Map<group_key, [child_key, ...]> (roots included)
|
|
26914
|
+
* topics: [topic_name, ...] topics with records, in desc order
|
|
26915
|
+
* }
|
|
26916
|
+
************************************************************/
|
|
26917
|
+
function fold_build_model(descs, records) {
|
|
26918
|
+
let nodes = /* @__PURE__ */ new Map();
|
|
26919
|
+
let groups = /* @__PURE__ */ new Map();
|
|
26920
|
+
let topics = [];
|
|
26921
|
+
descs = (0, _yuneta_gobj_js.is_object)(descs) ? descs : {};
|
|
26922
|
+
records = (0, _yuneta_gobj_js.is_object)(records) ? records : {};
|
|
26923
|
+
for (let topic_name of Object.keys(descs)) {
|
|
26924
|
+
if (topic_name.substring(0, 2) === "__") continue;
|
|
26925
|
+
descs[topic_name];
|
|
26926
|
+
let list = records[topic_name];
|
|
26927
|
+
if (!(0, _yuneta_gobj_js.is_array)(list)) continue;
|
|
26928
|
+
topics.push(topic_name);
|
|
26929
|
+
for (let i = 0; i < list.length; i++) {
|
|
26930
|
+
let record = list[i];
|
|
26931
|
+
if (!(0, _yuneta_gobj_js.is_object)(record) || record.id === void 0 || record.id === null) continue;
|
|
26932
|
+
let key = fold_node_key(topic_name, String(record.id));
|
|
26933
|
+
nodes.set(key, {
|
|
26934
|
+
key,
|
|
26935
|
+
topic_name,
|
|
26936
|
+
id: String(record.id),
|
|
26937
|
+
record,
|
|
26938
|
+
parents: [],
|
|
26939
|
+
hooks: {}
|
|
26940
|
+
});
|
|
26941
|
+
}
|
|
26942
|
+
}
|
|
26943
|
+
for (let node of nodes.values()) {
|
|
26944
|
+
let desc = descs[node.topic_name];
|
|
26945
|
+
let parents = parents_of(descs, desc, node.record);
|
|
26946
|
+
for (let p of parents) {
|
|
26947
|
+
let parent = nodes.get(p.node_key);
|
|
26948
|
+
if (!parent) continue;
|
|
26949
|
+
node.parents.push(p);
|
|
26950
|
+
if (!parent.hooks[p.hook]) parent.hooks[p.hook] = [];
|
|
26951
|
+
parent.hooks[p.hook].push(node.key);
|
|
26952
|
+
}
|
|
26953
|
+
}
|
|
26954
|
+
for (let node of nodes.values()) for (let hook of Object.keys(node.hooks)) groups.set(fold_group_key(node.key, hook), node.hooks[hook]);
|
|
26955
|
+
let roots = {};
|
|
26956
|
+
for (let topic_name of topics) roots[topic_name] = [];
|
|
26957
|
+
for (let node of nodes.values()) if (node.parents.length === 0) roots[node.topic_name].push(node.key);
|
|
26958
|
+
let reached = reach_from(nodes, [].concat(...Object.values(roots)));
|
|
26959
|
+
for (let node of nodes.values()) if (!reached.has(node.key)) {
|
|
26960
|
+
roots[node.topic_name].push(node.key);
|
|
26961
|
+
for (let k of reach_from(nodes, [node.key])) reached.add(k);
|
|
26962
|
+
}
|
|
26963
|
+
for (let topic_name of topics) if (roots[topic_name].length > 0) groups.set(fold_root_group_key(topic_name), roots[topic_name]);
|
|
26964
|
+
return {
|
|
26965
|
+
nodes,
|
|
26966
|
+
groups,
|
|
26967
|
+
topics
|
|
26968
|
+
};
|
|
26969
|
+
}
|
|
26970
|
+
/************************************************************
|
|
26971
|
+
* Everything reachable downwards from `start`, cycles included.
|
|
26972
|
+
************************************************************/
|
|
26973
|
+
function reach_from(nodes, start) {
|
|
26974
|
+
let seen = /* @__PURE__ */ new Set();
|
|
26975
|
+
let queue = start.slice();
|
|
26976
|
+
while (queue.length) {
|
|
26977
|
+
let key = queue.shift();
|
|
26978
|
+
if (seen.has(key)) continue;
|
|
26979
|
+
seen.add(key);
|
|
26980
|
+
let node = nodes.get(key);
|
|
26981
|
+
if (!node) continue;
|
|
26982
|
+
for (let hook of Object.keys(node.hooks)) for (let child of node.hooks[hook]) if (!seen.has(child)) queue.push(child);
|
|
26983
|
+
}
|
|
26984
|
+
return seen;
|
|
26985
|
+
}
|
|
26986
|
+
/************************************************************
|
|
26987
|
+
* Fold state: which groups are open, and how far.
|
|
26988
|
+
************************************************************/
|
|
26989
|
+
function fold_new_state(page_size) {
|
|
26990
|
+
return {
|
|
26991
|
+
page_size: page_size > 0 ? page_size : PAGE_SIZE_DEFAULT,
|
|
26992
|
+
shown: /* @__PURE__ */ new Map()
|
|
26993
|
+
};
|
|
26994
|
+
}
|
|
26995
|
+
function fold_group_total(model, group_key) {
|
|
26996
|
+
let list = model.groups.get(group_key);
|
|
26997
|
+
return list ? list.length : 0;
|
|
26998
|
+
}
|
|
26999
|
+
function fold_group_shown(model, state, group_key) {
|
|
27000
|
+
let total = fold_group_total(model, group_key);
|
|
27001
|
+
let n = state.shown.get(group_key) || 0;
|
|
27002
|
+
return Math.min(n, total);
|
|
27003
|
+
}
|
|
27004
|
+
function fold_is_open(state, group_key) {
|
|
27005
|
+
return (state.shown.get(group_key) || 0) > 0;
|
|
27006
|
+
}
|
|
27007
|
+
function page_ceiling(state, n) {
|
|
27008
|
+
let p = state.page_size;
|
|
27009
|
+
return Math.ceil(n / p) * p;
|
|
27010
|
+
}
|
|
27011
|
+
/************************************************************
|
|
27012
|
+
* Open a group on its first page, or close it.
|
|
27013
|
+
* Returns true when it ends OPEN.
|
|
27014
|
+
************************************************************/
|
|
27015
|
+
function fold_toggle(model, state, group_key) {
|
|
27016
|
+
if (fold_is_open(state, group_key)) {
|
|
27017
|
+
state.shown.delete(group_key);
|
|
27018
|
+
return false;
|
|
27019
|
+
}
|
|
27020
|
+
if (fold_group_total(model, group_key) === 0) return false;
|
|
27021
|
+
state.shown.set(group_key, state.page_size);
|
|
27022
|
+
return true;
|
|
27023
|
+
}
|
|
27024
|
+
/************************************************************
|
|
27025
|
+
* One more page of a group. Returns how many are shown now.
|
|
27026
|
+
************************************************************/
|
|
27027
|
+
function fold_show_more(model, state, group_key) {
|
|
27028
|
+
let total = fold_group_total(model, group_key);
|
|
27029
|
+
let n = fold_group_shown(model, state, group_key) + state.page_size;
|
|
27030
|
+
n = Math.min(n, total);
|
|
27031
|
+
if (n > 0) state.shown.set(group_key, n);
|
|
27032
|
+
return n;
|
|
27033
|
+
}
|
|
27034
|
+
/************************************************************
|
|
27035
|
+
* Every group open on ALL its children. This is the reader
|
|
27036
|
+
* asking for the whole thing; the pages are for the other way in.
|
|
27037
|
+
************************************************************/
|
|
27038
|
+
function fold_expand_all(model, state) {
|
|
27039
|
+
state.shown.clear();
|
|
27040
|
+
for (let [group_key, list] of model.groups) if (list.length > 0) state.shown.set(group_key, list.length);
|
|
27041
|
+
}
|
|
27042
|
+
/************************************************************
|
|
27043
|
+
* The roots and nothing else -- the first page of each topic's
|
|
27044
|
+
* roots, the way the graph looks before anything is opened.
|
|
27045
|
+
************************************************************/
|
|
27046
|
+
function fold_collapse_all(model, state) {
|
|
27047
|
+
state.shown.clear();
|
|
27048
|
+
for (let topic_name of model.topics) {
|
|
27049
|
+
let key = fold_root_group_key(topic_name);
|
|
27050
|
+
if (fold_group_total(model, key) > 0) state.shown.set(key, state.page_size);
|
|
27051
|
+
}
|
|
27052
|
+
}
|
|
27053
|
+
/************************************************************
|
|
27054
|
+
* Open the tree down to `depth` levels: the roots are level 1,
|
|
27055
|
+
* their children level 2. A node reached by two paths takes the
|
|
27056
|
+
* shallowest. Each group opens on its first page. `depth` of 1
|
|
27057
|
+
* is the roots alone; anything below that is treated as 1.
|
|
27058
|
+
************************************************************/
|
|
27059
|
+
function fold_expand_to_depth(model, state, depth) {
|
|
27060
|
+
fold_collapse_all(model, state);
|
|
27061
|
+
if (!(depth > 1)) return;
|
|
27062
|
+
let level = /* @__PURE__ */ new Map();
|
|
27063
|
+
let queue = [];
|
|
27064
|
+
for (let topic_name of model.topics) {
|
|
27065
|
+
let roots = model.groups.get(fold_root_group_key(topic_name)) || [];
|
|
27066
|
+
for (let key of roots) if (!level.has(key)) {
|
|
27067
|
+
level.set(key, 1);
|
|
27068
|
+
queue.push(key);
|
|
27069
|
+
}
|
|
27070
|
+
}
|
|
27071
|
+
while (queue.length) {
|
|
27072
|
+
let key = queue.shift();
|
|
27073
|
+
let lv = level.get(key);
|
|
27074
|
+
if (lv >= depth) continue;
|
|
27075
|
+
let node = model.nodes.get(key);
|
|
27076
|
+
if (!node) continue;
|
|
27077
|
+
for (let hook of Object.keys(node.hooks)) {
|
|
27078
|
+
let children = node.hooks[hook];
|
|
27079
|
+
if (children.length === 0) continue;
|
|
27080
|
+
state.shown.set(fold_group_key(key, hook), state.page_size);
|
|
27081
|
+
for (let child of children) if (!level.has(child)) {
|
|
27082
|
+
level.set(child, lv + 1);
|
|
27083
|
+
queue.push(child);
|
|
27084
|
+
}
|
|
27085
|
+
}
|
|
27086
|
+
}
|
|
27087
|
+
}
|
|
27088
|
+
/************************************************************
|
|
27089
|
+
* The set of node keys on screen, from the roots down through
|
|
27090
|
+
* every open group -- and only through open groups whose parent
|
|
27091
|
+
* is itself on screen: a group remembered open under a folded
|
|
27092
|
+
* ancestor stays remembered, not shown.
|
|
27093
|
+
************************************************************/
|
|
27094
|
+
function fold_visible_set(model, state) {
|
|
27095
|
+
let visible = /* @__PURE__ */ new Set();
|
|
27096
|
+
let queue = [];
|
|
27097
|
+
let push_group = (group_key, children) => {
|
|
27098
|
+
let n = fold_group_shown(model, state, group_key);
|
|
27099
|
+
for (let i = 0; i < n; i++) {
|
|
27100
|
+
let key = children[i];
|
|
27101
|
+
if (!visible.has(key)) {
|
|
27102
|
+
visible.add(key);
|
|
27103
|
+
queue.push(key);
|
|
27104
|
+
}
|
|
27105
|
+
}
|
|
27106
|
+
};
|
|
27107
|
+
for (let topic_name of model.topics) {
|
|
27108
|
+
let key = fold_root_group_key(topic_name);
|
|
27109
|
+
let roots = model.groups.get(key);
|
|
27110
|
+
if (roots) push_group(key, roots);
|
|
27111
|
+
}
|
|
27112
|
+
while (queue.length) {
|
|
27113
|
+
let key = queue.shift();
|
|
27114
|
+
let node = model.nodes.get(key);
|
|
27115
|
+
if (!node) continue;
|
|
27116
|
+
for (let hook of Object.keys(node.hooks)) push_group(fold_group_key(key, hook), node.hooks[hook]);
|
|
27117
|
+
}
|
|
27118
|
+
return visible;
|
|
27119
|
+
}
|
|
27120
|
+
/************************************************************
|
|
27121
|
+
* The open groups that still have children off screen, with the
|
|
27122
|
+
* parent that owns each -- what the "+N" chips are drawn for.
|
|
27123
|
+
* Only groups whose parent is visible (or root groups).
|
|
27124
|
+
*
|
|
27125
|
+
* Returns [{group_key, node_key, hook, shown, total, rest}]
|
|
27126
|
+
************************************************************/
|
|
27127
|
+
function fold_pending_groups(model, state, visible) {
|
|
27128
|
+
let out = [];
|
|
27129
|
+
for (let [group_key, list] of model.groups) {
|
|
27130
|
+
let shown = fold_group_shown(model, state, group_key);
|
|
27131
|
+
if (shown === 0 || shown >= list.length) continue;
|
|
27132
|
+
let g = fold_split_group_key(group_key);
|
|
27133
|
+
if (g.node_key && !visible.has(g.node_key)) continue;
|
|
27134
|
+
out.push({
|
|
27135
|
+
group_key,
|
|
27136
|
+
node_key: g.node_key,
|
|
27137
|
+
hook: g.hook,
|
|
27138
|
+
shown,
|
|
27139
|
+
total: list.length,
|
|
27140
|
+
rest: list.length - shown
|
|
27141
|
+
});
|
|
27142
|
+
}
|
|
27143
|
+
return out;
|
|
27144
|
+
}
|
|
27145
|
+
/************************************************************
|
|
27146
|
+
* The pills of one card: one per hook that has children.
|
|
27147
|
+
*
|
|
27148
|
+
* Returns [{hook, group_key, total, shown, open, child_topic}]
|
|
27149
|
+
* `child_topic` is the topic of the first child, which is what
|
|
27150
|
+
* the pill is coloured with (a hook can name several topics in
|
|
27151
|
+
* the schema, but the children it has are what it links).
|
|
27152
|
+
************************************************************/
|
|
27153
|
+
function fold_pills_of(model, state, node_key) {
|
|
27154
|
+
let node = model.nodes.get(node_key);
|
|
27155
|
+
if (!node) return [];
|
|
27156
|
+
let out = [];
|
|
27157
|
+
for (let hook of Object.keys(node.hooks)) {
|
|
27158
|
+
let children = node.hooks[hook];
|
|
27159
|
+
if (children.length === 0) continue;
|
|
27160
|
+
let group_key = fold_group_key(node_key, hook);
|
|
27161
|
+
let first = model.nodes.get(children[0]);
|
|
27162
|
+
out.push({
|
|
27163
|
+
hook,
|
|
27164
|
+
group_key,
|
|
27165
|
+
total: children.length,
|
|
27166
|
+
shown: fold_group_shown(model, state, group_key),
|
|
27167
|
+
open: fold_is_open(state, group_key),
|
|
27168
|
+
child_topic: first ? first.topic_name : ""
|
|
27169
|
+
});
|
|
27170
|
+
}
|
|
27171
|
+
return out;
|
|
27172
|
+
}
|
|
27173
|
+
/************************************************************
|
|
27174
|
+
* Make ONE node visible, opening the groups on a path from a
|
|
27175
|
+
* root to it, and as many pages of each as it takes for the
|
|
27176
|
+
* node to be inside the shown prefix. Prefers the shortest path
|
|
27177
|
+
* (breadth-first upwards). Returns false if no root reaches it,
|
|
27178
|
+
* which cannot happen with a model built here (see roots) but
|
|
27179
|
+
* is the honest answer for a key the model does not know.
|
|
27180
|
+
************************************************************/
|
|
27181
|
+
function fold_reveal(model, state, node_key) {
|
|
27182
|
+
if (!model.nodes.get(node_key)) return false;
|
|
27183
|
+
let root_group_of = (key) => {
|
|
27184
|
+
let rg = fold_root_group_key(model.nodes.get(key).topic_name);
|
|
27185
|
+
return (model.groups.get(rg) || []).indexOf(key) >= 0 ? rg : null;
|
|
27186
|
+
};
|
|
27187
|
+
let via = /* @__PURE__ */ new Map();
|
|
27188
|
+
let queue = [node_key];
|
|
27189
|
+
via.set(node_key, null);
|
|
27190
|
+
let root_key = null;
|
|
27191
|
+
while (queue.length) {
|
|
27192
|
+
let key = queue.shift();
|
|
27193
|
+
if (root_group_of(key)) {
|
|
27194
|
+
root_key = key;
|
|
27195
|
+
break;
|
|
27196
|
+
}
|
|
27197
|
+
let n = model.nodes.get(key);
|
|
27198
|
+
for (let p of n.parents) {
|
|
27199
|
+
if (!model.nodes.has(p.node_key) || via.has(p.node_key)) continue;
|
|
27200
|
+
via.set(p.node_key, {
|
|
27201
|
+
child_key: key,
|
|
27202
|
+
hook: p.hook
|
|
27203
|
+
});
|
|
27204
|
+
queue.push(p.node_key);
|
|
27205
|
+
}
|
|
27206
|
+
}
|
|
27207
|
+
if (root_key === null) return false;
|
|
27208
|
+
let open_up_to = (group_key, child_key) => {
|
|
27209
|
+
let list = model.groups.get(group_key) || [];
|
|
27210
|
+
let idx = list.indexOf(child_key);
|
|
27211
|
+
if (idx < 0) return;
|
|
27212
|
+
let need = page_ceiling(state, idx + 1);
|
|
27213
|
+
if (need > (state.shown.get(group_key) || 0)) state.shown.set(group_key, Math.min(need, list.length));
|
|
27214
|
+
};
|
|
27215
|
+
open_up_to(root_group_of(root_key), root_key);
|
|
27216
|
+
let key = root_key;
|
|
27217
|
+
while (true) {
|
|
27218
|
+
let step = via.get(key);
|
|
27219
|
+
if (!step) break;
|
|
27220
|
+
open_up_to(fold_group_key(key, step.hook), step.child_key);
|
|
27221
|
+
key = step.child_key;
|
|
27222
|
+
}
|
|
27223
|
+
return true;
|
|
27224
|
+
}
|
|
27225
|
+
//#endregion
|
|
26752
27226
|
//#region node_modules/@babel/runtime/helpers/esm/typeof.js
|
|
26753
27227
|
function _typeof(o) {
|
|
26754
27228
|
"@babel/helpers - typeof";
|
|
@@ -53493,6 +53967,23 @@ function ensure_brush_select_owned() {
|
|
|
53493
53967
|
* record: null, // Data of node
|
|
53494
53968
|
* }
|
|
53495
53969
|
*
|
|
53970
|
+
* The graph opens FOLDED, like a JSON viewer.
|
|
53971
|
+
*
|
|
53972
|
+
* Every record of every topic is fetched, but only the ones on
|
|
53973
|
+
* screen become G6 nodes: the roots, `expand_depth` levels under
|
|
53974
|
+
* them, one page (`fold_page_size`) of children per hook. Each
|
|
53975
|
+
* card carries a pill per hook that has children -- `▸ devices
|
|
53976
|
+
* 142` -- which opens or folds that hook; an open hook with more
|
|
53977
|
+
* children than its page ends in a `+N` chip that opens the next
|
|
53978
|
+
* page. The arithmetic lives in treedb_fold_model.js; this file
|
|
53979
|
+
* reconciles the G6 data with what that model says is visible
|
|
53980
|
+
* (see reconcile_fold) and lays it out again.
|
|
53981
|
+
*
|
|
53982
|
+
* `dagre` reads LEFT TO RIGHT: the children of a node are a
|
|
53983
|
+
* column beside it, the ports move to the sides with it, and
|
|
53984
|
+
* the edges are horizontal beziers. `antv-dagre` keeps the
|
|
53985
|
+
* top-down reading.
|
|
53986
|
+
*
|
|
53496
53987
|
* Copyright (c) 2020-2021 Niyamaka.
|
|
53497
53988
|
* Copyright (c) 2025-2026, ArtGins.
|
|
53498
53989
|
* All Rights Reserved.
|
|
@@ -53552,8 +54043,18 @@ var SELECT_RING = "rgba(59,130,246,0.95)";
|
|
|
53552
54043
|
***************************************************************/
|
|
53553
54044
|
var _layouts = {
|
|
53554
54045
|
"manual": { type: "manual" },
|
|
53555
|
-
"dagre": {
|
|
53556
|
-
|
|
54046
|
+
"dagre": {
|
|
54047
|
+
type: "dagre",
|
|
54048
|
+
rankdir: "LR",
|
|
54049
|
+
nodesep: 18,
|
|
54050
|
+
ranksep: 110
|
|
54051
|
+
},
|
|
54052
|
+
"antv-dagre": {
|
|
54053
|
+
type: "antv-dagre",
|
|
54054
|
+
rankdir: "TB",
|
|
54055
|
+
nodesep: 30,
|
|
54056
|
+
ranksep: 80
|
|
54057
|
+
},
|
|
53557
54058
|
"d3-force": {
|
|
53558
54059
|
type: "d3-force",
|
|
53559
54060
|
link: {
|
|
@@ -53601,6 +54102,8 @@ var attrs_table$4 = [
|
|
|
53601
54102
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "layout_names", _yuneta_gobj_js.sdata_flag_t.SDF_RD, JSON.stringify(Object.keys(_layouts)), "Available layout names (read-only, for parent to query)"),
|
|
53602
54103
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "hook_port_position", 0, "bottom", "Hook port position"),
|
|
53603
54104
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "fkey_port_position", 0, "top", "Fkey port position"),
|
|
54105
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "expand_depth", 0, 2, "Levels open when a treedb loads: 1 = the roots alone, 2 = the roots and their children. Every open hook shows its first page"),
|
|
54106
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "fold_page_size", 0, 24, "Children of one hook shown per page; the `+N` chip after the page opens the next one"),
|
|
53604
54107
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "confirm_delete_node", 0, true, "Ask confirmation before deleting a node"),
|
|
53605
54108
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "confirm_unlink_edge", 0, true, "Ask confirmation before unlinking an edge"),
|
|
53606
54109
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "wide", 0, "40px", "Height of header"),
|
|
@@ -53672,7 +54175,14 @@ var PRIVATE_DATA$4 = {
|
|
|
53672
54175
|
_pending_find: null,
|
|
53673
54176
|
_layout_asked: "",
|
|
53674
54177
|
_nodes_total: 0,
|
|
53675
|
-
_nodes_placed: 0
|
|
54178
|
+
_nodes_placed: 0,
|
|
54179
|
+
expand_depth: 2,
|
|
54180
|
+
fold_page_size: 24,
|
|
54181
|
+
_fold_model: null,
|
|
54182
|
+
_fold_state: null,
|
|
54183
|
+
_fold_listener: null,
|
|
54184
|
+
_pill_sig: null,
|
|
54185
|
+
_fold_busy: false
|
|
53676
54186
|
};
|
|
53677
54187
|
var __gclass__$4 = null;
|
|
53678
54188
|
/******************************
|
|
@@ -53692,6 +54202,7 @@ function mt_create$4(gobj) {
|
|
|
53692
54202
|
build_ui$4(gobj);
|
|
53693
54203
|
register_layouts(gobj);
|
|
53694
54204
|
build_graph$1(gobj);
|
|
54205
|
+
install_fold_listener(gobj);
|
|
53695
54206
|
if (typeof ResizeObserver !== "undefined" && priv.$container) {
|
|
53696
54207
|
let ro = new ResizeObserver(() => {
|
|
53697
54208
|
if (priv._resize_raf) cancelAnimationFrame(priv._resize_raf);
|
|
@@ -53731,6 +54242,7 @@ function mt_destroy$4(gobj) {
|
|
|
53731
54242
|
i18next.default.off("languageChanged", priv._on_language_changed);
|
|
53732
54243
|
priv._on_language_changed = null;
|
|
53733
54244
|
}
|
|
54245
|
+
uninstall_fold_listener(gobj);
|
|
53734
54246
|
if (priv._on_pointerdown_focus) {
|
|
53735
54247
|
priv.$container.removeEventListener("pointerdown", priv._on_pointerdown_focus, true);
|
|
53736
54248
|
priv._on_pointerdown_focus = null;
|
|
@@ -53918,6 +54430,7 @@ function configure_events(gobj) {
|
|
|
53918
54430
|
});
|
|
53919
54431
|
priv._on_language_changed = () => {
|
|
53920
54432
|
update_toolbar(gobj);
|
|
54433
|
+
repaint_more_chips(gobj);
|
|
53921
54434
|
};
|
|
53922
54435
|
i18next.default.on("languageChanged", priv._on_language_changed);
|
|
53923
54436
|
priv._on_pointerdown_focus = (ev) => {
|
|
@@ -54415,15 +54928,79 @@ function build_ports(gobj, desc) {
|
|
|
54415
54928
|
top_ports.push(port);
|
|
54416
54929
|
}
|
|
54417
54930
|
}
|
|
54418
|
-
|
|
54419
|
-
for (let i = 0; i < bottom_ports.length; i++) {
|
|
54420
|
-
let point = getPointPosition(bottom_ports.length, i);
|
|
54421
|
-
if (top_ports.length === 0) bottom_ports[i].placement = [0, .5];
|
|
54422
|
-
else bottom_ports[i].placement = [point, 1];
|
|
54423
|
-
}
|
|
54931
|
+
place_ports(gobj, top_ports, bottom_ports);
|
|
54424
54932
|
return [...top_ports, ...bottom_ports];
|
|
54425
54933
|
}
|
|
54426
54934
|
/************************************************************
|
|
54935
|
+
* Which way the graph reads. `dagre` is the left-to-right
|
|
54936
|
+
* layout; everything else keeps the top-down reading, `manual`
|
|
54937
|
+
* included, because the saved arrangements were made with the
|
|
54938
|
+
* ports on the top and bottom edges.
|
|
54939
|
+
************************************************************/
|
|
54940
|
+
function layout_direction(gobj) {
|
|
54941
|
+
return gobj.priv.layout === "dagre" ? "LR" : "TB";
|
|
54942
|
+
}
|
|
54943
|
+
function edge_type_for(gobj) {
|
|
54944
|
+
return layout_direction(gobj) === "LR" ? "cubic-horizontal" : "cubic";
|
|
54945
|
+
}
|
|
54946
|
+
/************************************************************
|
|
54947
|
+
* Where the ports sit, by direction: fkeys (the way in from a
|
|
54948
|
+
* parent) all on one point of the leading edge, hooks (the way
|
|
54949
|
+
* out to the children) spread along the trailing edge. Left and
|
|
54950
|
+
* right when the graph reads left to right; top and bottom
|
|
54951
|
+
* otherwise.
|
|
54952
|
+
************************************************************/
|
|
54953
|
+
function place_ports(gobj, fkey_ports, hook_ports) {
|
|
54954
|
+
let lr = layout_direction(gobj) === "LR";
|
|
54955
|
+
for (let i = 0; i < fkey_ports.length; i++) fkey_ports[i].placement = lr ? [0, .5] : [.5, 0];
|
|
54956
|
+
for (let i = 0; i < hook_ports.length; i++) {
|
|
54957
|
+
let point = getPointPosition(hook_ports.length, i);
|
|
54958
|
+
if (fkey_ports.length === 0 && !lr) hook_ports[i].placement = [0, .5];
|
|
54959
|
+
else if (lr) hook_ports[i].placement = [1, point];
|
|
54960
|
+
else hook_ports[i].placement = [point, 1];
|
|
54961
|
+
}
|
|
54962
|
+
}
|
|
54963
|
+
/************************************************************
|
|
54964
|
+
* The layout changed direction: move every port to the edge
|
|
54965
|
+
* the new reading needs, and give every edge the curve that
|
|
54966
|
+
* goes with it. Called before the layout runs, so the lines
|
|
54967
|
+
* are drawn once, in the right place.
|
|
54968
|
+
************************************************************/
|
|
54969
|
+
function apply_layout_direction(gobj) {
|
|
54970
|
+
let graph = gobj.priv.graph;
|
|
54971
|
+
if (!graph) return;
|
|
54972
|
+
let node_updates = [];
|
|
54973
|
+
for (let nd of graph.getNodeData() || []) {
|
|
54974
|
+
if (!nd || !nd.data || !nd.data.desc || !nd.style || !(0, _yuneta_gobj_js.is_array)(nd.style.ports)) continue;
|
|
54975
|
+
let desc = nd.data.desc;
|
|
54976
|
+
let fkey_ports = [];
|
|
54977
|
+
let hook_ports = [];
|
|
54978
|
+
for (let port of nd.style.ports) {
|
|
54979
|
+
let col = get_col_by_id(desc, port.key);
|
|
54980
|
+
let copy = Object.assign({}, port);
|
|
54981
|
+
if (col && col.fkey) fkey_ports.push(copy);
|
|
54982
|
+
else hook_ports.push(copy);
|
|
54983
|
+
}
|
|
54984
|
+
place_ports(gobj, fkey_ports, hook_ports);
|
|
54985
|
+
node_updates.push({
|
|
54986
|
+
id: nd.id,
|
|
54987
|
+
style: { ports: [...fkey_ports, ...hook_ports] }
|
|
54988
|
+
});
|
|
54989
|
+
}
|
|
54990
|
+
let type = edge_type_for(gobj);
|
|
54991
|
+
let edge_updates = [];
|
|
54992
|
+
for (let ed of graph.getEdgeData() || []) if (ed && ed.type !== type) edge_updates.push({
|
|
54993
|
+
id: ed.id,
|
|
54994
|
+
type
|
|
54995
|
+
});
|
|
54996
|
+
try {
|
|
54997
|
+
if (node_updates.length) graph.updateNodeData(node_updates);
|
|
54998
|
+
if (edge_updates.length) graph.updateEdgeData(edge_updates);
|
|
54999
|
+
} catch (e) {
|
|
55000
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot turn the ports: ${e}`);
|
|
55001
|
+
}
|
|
55002
|
+
}
|
|
55003
|
+
/************************************************************
|
|
54427
55004
|
* Create a topic node in the G6 graph
|
|
54428
55005
|
************************************************************/
|
|
54429
55006
|
function create_topic_node(gobj, desc, record) {
|
|
@@ -54432,8 +55009,6 @@ function create_topic_node(gobj, desc, record) {
|
|
|
54432
55009
|
let node_name = build_node_name(gobj, desc.topic_name, record.id);
|
|
54433
55010
|
let xy = get_default_ne_xy(gobj);
|
|
54434
55011
|
let geometry = get_node_graph_props(gobj, desc.topic_name, record.id, record);
|
|
54435
|
-
priv._nodes_total++;
|
|
54436
|
-
if (geometry_has_position(geometry)) priv._nodes_placed++;
|
|
54437
55012
|
let x = (0, _yuneta_gobj_js.kw_get_int)(gobj, geometry, "x", xy, _yuneta_gobj_js.kw_flag_t.KW_CREATE);
|
|
54438
55013
|
let y = (0, _yuneta_gobj_js.kw_get_int)(gobj, geometry, "y", xy, _yuneta_gobj_js.kw_flag_t.KW_CREATE);
|
|
54439
55014
|
let ports = build_ports(gobj, desc);
|
|
@@ -54446,24 +55021,26 @@ function create_topic_node(gobj, desc, record) {
|
|
|
54446
55021
|
stroke: getStrokeColor(desc.color),
|
|
54447
55022
|
lineWidth: 1
|
|
54448
55023
|
};
|
|
55024
|
+
let pills_html = pills_html_of_key(gobj, fold_node_key(desc.topic_name, String(record.id)));
|
|
55025
|
+
let size = card_size_for(node_treedb_type, !!pills_html);
|
|
54449
55026
|
if (node_treedb_type === "child") {
|
|
54450
55027
|
node_graph_type = "html";
|
|
54451
|
-
style.size =
|
|
54452
|
-
style.dx = -
|
|
54453
|
-
style.dy = -
|
|
55028
|
+
style.size = size;
|
|
55029
|
+
style.dx = -size[0] / 2;
|
|
55030
|
+
style.dy = -size[1] / 2;
|
|
54454
55031
|
style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id);
|
|
54455
55032
|
} else if (node_treedb_type === "extended") {
|
|
54456
55033
|
node_graph_type = "html";
|
|
54457
|
-
style.size =
|
|
54458
|
-
style.dx = -
|
|
54459
|
-
style.dy = -
|
|
54460
|
-
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id);
|
|
55034
|
+
style.size = size;
|
|
55035
|
+
style.dx = -size[0] / 2;
|
|
55036
|
+
style.dy = -size[1] / 2;
|
|
55037
|
+
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id, false, false, false, pills_html);
|
|
54461
55038
|
} else {
|
|
54462
55039
|
node_graph_type = "html";
|
|
54463
|
-
style.size =
|
|
54464
|
-
style.dx = -
|
|
54465
|
-
style.dy = -
|
|
54466
|
-
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id);
|
|
55040
|
+
style.size = size;
|
|
55041
|
+
style.dx = -size[0] / 2;
|
|
55042
|
+
style.dy = -size[1] / 2;
|
|
55043
|
+
style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id, false, false, false, pills_html);
|
|
54467
55044
|
}
|
|
54468
55045
|
let topic_props = priv._graph_properties[desc.topic_name];
|
|
54469
55046
|
let topic_defaults = (0, _yuneta_gobj_js.is_object)(topic_props) && (0, _yuneta_gobj_js.is_object)(topic_props.defaults) ? topic_props.defaults : null;
|
|
@@ -54520,8 +55097,7 @@ function create_topic_node(gobj, desc, record) {
|
|
|
54520
55097
|
* Update topic node data
|
|
54521
55098
|
************************************************************/
|
|
54522
55099
|
function update_topic_node(gobj, desc, node_name, record) {
|
|
54523
|
-
let
|
|
54524
|
-
let graph = priv.graph;
|
|
55100
|
+
let graph = gobj.priv.graph;
|
|
54525
55101
|
try {
|
|
54526
55102
|
let nodedata = graph.getNodeData(node_name);
|
|
54527
55103
|
if (!nodedata) {
|
|
@@ -54529,18 +55105,7 @@ function update_topic_node(gobj, desc, node_name, record) {
|
|
|
54529
55105
|
return;
|
|
54530
55106
|
}
|
|
54531
55107
|
nodedata.data.record = record;
|
|
54532
|
-
|
|
54533
|
-
id: node_name,
|
|
54534
|
-
style: { innerHTML: build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id) }
|
|
54535
|
-
}]);
|
|
54536
|
-
else if (desc.node_treedb_type === "extended") graph.updateNodeData([{
|
|
54537
|
-
id: node_name,
|
|
54538
|
-
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id) }
|
|
54539
|
-
}]);
|
|
54540
|
-
else if (desc.node_treedb_type === "hierarchical") graph.updateNodeData([{
|
|
54541
|
-
id: node_name,
|
|
54542
|
-
style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id) }
|
|
54543
|
-
}]);
|
|
55108
|
+
repaint_cards(gobj, /* @__PURE__ */ new Set([node_name]));
|
|
54544
55109
|
} catch (e) {
|
|
54545
55110
|
(0, _yuneta_gobj_js.log_error)(e.message);
|
|
54546
55111
|
}
|
|
@@ -54579,19 +55144,6 @@ function remove_local_node(gobj, topic_name, node) {
|
|
|
54579
55144
|
}
|
|
54580
55145
|
}
|
|
54581
55146
|
/************************************************************
|
|
54582
|
-
* Create all links from records, initial load from parent
|
|
54583
|
-
************************************************************/
|
|
54584
|
-
function create_links(gobj) {
|
|
54585
|
-
let priv = gobj.priv;
|
|
54586
|
-
for (const [topic_name, records] of Object.entries(priv.records)) {
|
|
54587
|
-
let desc = priv.descs[topic_name];
|
|
54588
|
-
for (let i = 0; i < records.length; i++) {
|
|
54589
|
-
let record = records[i];
|
|
54590
|
-
draw_links(gobj, desc, record, true);
|
|
54591
|
-
}
|
|
54592
|
-
}
|
|
54593
|
-
}
|
|
54594
|
-
/************************************************************
|
|
54595
55147
|
* Collect all fkey references from a record as a Set.
|
|
54596
55148
|
*
|
|
54597
55149
|
* Each entry is a string: "col_id\tcol_idx\tfkey_value"
|
|
@@ -54671,6 +55223,17 @@ function draw_link(gobj, child_topic, child_id, source_col, fkey, verbose) {
|
|
|
54671
55223
|
return;
|
|
54672
55224
|
}
|
|
54673
55225
|
let child_node = build_node_name(gobj, child_topic, child_id);
|
|
55226
|
+
let child_cell = null;
|
|
55227
|
+
try {
|
|
55228
|
+
child_cell = graph.getNodeData(child_node);
|
|
55229
|
+
} catch (e) {
|
|
55230
|
+
child_cell = null;
|
|
55231
|
+
}
|
|
55232
|
+
if (!child_cell) {
|
|
55233
|
+
if (verbose) (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: child node NOT FOUND: ${child_node}`);
|
|
55234
|
+
return;
|
|
55235
|
+
}
|
|
55236
|
+
if (get_edge_by_link(gobj, parent_topic, parent_id, hook_name, child_topic, child_id, fkey_name)) return;
|
|
54674
55237
|
let dark = priv.theme === "dark";
|
|
54675
55238
|
let is_tree = parent_topic === child_topic;
|
|
54676
55239
|
let saved_lineWidth = is_tree ? 2 : 1.6;
|
|
@@ -54709,7 +55272,7 @@ function draw_link(gobj, child_topic, child_id, source_col, fkey, verbose) {
|
|
|
54709
55272
|
}
|
|
54710
55273
|
let edge = {
|
|
54711
55274
|
id: build_edge_id(gobj),
|
|
54712
|
-
type:
|
|
55275
|
+
type: edge_type_for(gobj),
|
|
54713
55276
|
source: parent_node,
|
|
54714
55277
|
target: child_node,
|
|
54715
55278
|
style: {
|
|
@@ -54840,7 +55403,10 @@ function save_geometry(gobj) {
|
|
|
54840
55403
|
let priv = gobj.priv;
|
|
54841
55404
|
let graph = priv.graph;
|
|
54842
55405
|
const nodes = graph.getData().nodes;
|
|
54843
|
-
for (let i = 0; i < nodes.length; i++)
|
|
55406
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
55407
|
+
if (!nodes[i].data || !nodes[i].data.desc) continue;
|
|
55408
|
+
update_geometry(gobj, nodes[i].id);
|
|
55409
|
+
}
|
|
54844
55410
|
const edges = graph.getData().edges;
|
|
54845
55411
|
for (let i = 0; i < edges.length; i++) update_edge_geometry(gobj, edges[i].id);
|
|
54846
55412
|
let origin = (0, _yuneta_gobj_js.gobj_read_str_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "node_uuid");
|
|
@@ -55097,38 +55663,72 @@ async function graph_fit_selection(gobj, ids) {
|
|
|
55097
55663
|
************************************************************/
|
|
55098
55664
|
function graph_focus_topic(gobj, topic) {
|
|
55099
55665
|
let priv = gobj.priv;
|
|
55100
|
-
|
|
55101
|
-
if (!graph || !priv.graph_rendered) {
|
|
55666
|
+
if (!priv.graph || !priv.graph_rendered || !priv._fold_model) {
|
|
55102
55667
|
priv._pending_focus_topic = topic;
|
|
55103
55668
|
return;
|
|
55104
55669
|
}
|
|
55105
|
-
let
|
|
55106
|
-
|
|
55107
|
-
|
|
55108
|
-
|
|
55109
|
-
(
|
|
55110
|
-
|
|
55670
|
+
let model = priv._fold_model;
|
|
55671
|
+
let state = priv._fold_state;
|
|
55672
|
+
let keys = [];
|
|
55673
|
+
if (!(0, _yuneta_gobj_js.empty_string)(topic)) {
|
|
55674
|
+
for (let node of model.nodes.values()) if (node.topic_name === topic) keys.push(node.key);
|
|
55675
|
+
if (keys.length === 0) (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: focus topic '${topic}' has no nodes`);
|
|
55676
|
+
let visible = fold_visible_set(model, state);
|
|
55677
|
+
let revealed = 0;
|
|
55678
|
+
for (let key of keys) {
|
|
55679
|
+
if (visible.has(key)) continue;
|
|
55680
|
+
if (revealed >= state.page_size) break;
|
|
55681
|
+
fold_reveal(model, state, key);
|
|
55682
|
+
revealed++;
|
|
55683
|
+
}
|
|
55111
55684
|
}
|
|
55112
|
-
|
|
55685
|
+
priv._focus_topic = topic || null;
|
|
55686
|
+
reconcile_fold(gobj, {}).then(() => {
|
|
55687
|
+
apply_focus_ids(gobj, keys, "focus topic");
|
|
55688
|
+
});
|
|
55689
|
+
}
|
|
55690
|
+
/************************************************************
|
|
55691
|
+
* Paint the highlight on these records (by model key) and put
|
|
55692
|
+
* the first of them in the middle. Shared by the find and the
|
|
55693
|
+
* topic focus, after the reconcile that brought them on screen.
|
|
55694
|
+
* Keys that are still off screen are skipped: a match beyond the
|
|
55695
|
+
* page a reveal opened is counted, not painted.
|
|
55696
|
+
************************************************************/
|
|
55697
|
+
function apply_focus_ids(gobj, keys, what) {
|
|
55698
|
+
let priv = gobj.priv;
|
|
55699
|
+
let graph = priv.graph;
|
|
55700
|
+
if (!graph) return;
|
|
55113
55701
|
let states = {};
|
|
55114
55702
|
for (let id of priv._focus_ids || []) states[id] = [];
|
|
55115
55703
|
let ids = [];
|
|
55116
|
-
|
|
55117
|
-
|
|
55118
|
-
|
|
55119
|
-
|
|
55704
|
+
for (let key of keys) {
|
|
55705
|
+
let id = node_id_of_key(gobj, key);
|
|
55706
|
+
let nd = null;
|
|
55707
|
+
try {
|
|
55708
|
+
nd = graph.getNodeData(id);
|
|
55709
|
+
} catch (e) {
|
|
55710
|
+
nd = null;
|
|
55711
|
+
}
|
|
55712
|
+
if (nd) {
|
|
55713
|
+
ids.push(id);
|
|
55714
|
+
states[id] = ["active"];
|
|
55715
|
+
}
|
|
55120
55716
|
}
|
|
55121
55717
|
let prev_ids = priv._focus_ids || [];
|
|
55122
55718
|
priv._focus_ids = ids;
|
|
55123
|
-
priv._focus_topic = topic || null;
|
|
55124
55719
|
try {
|
|
55125
55720
|
if (typeof graph.setElementState === "function") graph.setElementState(states);
|
|
55126
55721
|
apply_node_highlight(gobj, prev_ids, ids);
|
|
55127
|
-
if (ids.length
|
|
55722
|
+
if (ids.length) yui_graph_center_on(graph, ids[0]);
|
|
55128
55723
|
} catch (e) {
|
|
55129
|
-
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}:
|
|
55724
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${what} apply failed: ${e}`);
|
|
55130
55725
|
}
|
|
55131
55726
|
}
|
|
55727
|
+
function node_id_of_key(gobj, key) {
|
|
55728
|
+
let i = key.indexOf("^");
|
|
55729
|
+
if (i < 0) return "";
|
|
55730
|
+
return build_node_name(gobj, key.slice(0, i), key.slice(i + 1));
|
|
55731
|
+
}
|
|
55132
55732
|
/************************************************************
|
|
55133
55733
|
* Find the nodes whose name, id or topic contains `term`, highlight
|
|
55134
55734
|
* them with the same amber 'active' state the topic focus uses, and
|
|
@@ -55145,52 +55745,43 @@ function graph_focus_topic(gobj, topic) {
|
|
|
55145
55745
|
************************************************************/
|
|
55146
55746
|
function graph_find_nodes(gobj, term) {
|
|
55147
55747
|
let priv = gobj.priv;
|
|
55148
|
-
|
|
55149
|
-
if (!graph || !priv.graph_rendered) {
|
|
55748
|
+
if (!priv.graph || !priv.graph_rendered || !priv._fold_model) {
|
|
55150
55749
|
priv._pending_find = term;
|
|
55151
55750
|
return 0;
|
|
55152
55751
|
}
|
|
55153
|
-
let
|
|
55154
|
-
|
|
55155
|
-
|
|
55156
|
-
} catch (e) {
|
|
55157
|
-
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: graph.getData() failed: ${e}`);
|
|
55158
|
-
return 0;
|
|
55159
|
-
}
|
|
55160
|
-
let nodes = (0, _yuneta_gobj_js.is_array)(data.nodes) ? data.nodes : [];
|
|
55161
|
-
let states = {};
|
|
55162
|
-
for (let id of priv._focus_ids || []) states[id] = [];
|
|
55163
|
-
let ids = [];
|
|
55752
|
+
let model = priv._fold_model;
|
|
55753
|
+
let state = priv._fold_state;
|
|
55754
|
+
let keys = [];
|
|
55164
55755
|
if (!(0, _yuneta_gobj_js.empty_string)(term)) {
|
|
55165
55756
|
let needle = String(term).toLowerCase();
|
|
55166
|
-
for (let
|
|
55167
|
-
|
|
55168
|
-
let record = n.data.record || {};
|
|
55757
|
+
for (let node of model.nodes.values()) {
|
|
55758
|
+
let record = node.record || {};
|
|
55169
55759
|
let label = "";
|
|
55170
55760
|
try {
|
|
55171
|
-
label = node_label(
|
|
55761
|
+
label = node_label(priv.descs[node.topic_name] || {}, record) || "";
|
|
55172
55762
|
} catch (e) {
|
|
55173
55763
|
label = "";
|
|
55174
55764
|
}
|
|
55175
55765
|
if ([
|
|
55176
55766
|
label,
|
|
55177
55767
|
record.id,
|
|
55178
|
-
|
|
55179
|
-
].filter((v) => typeof v === "string").join(" ").toLowerCase().includes(needle))
|
|
55768
|
+
node.topic_name
|
|
55769
|
+
].filter((v) => typeof v === "string").join(" ").toLowerCase().includes(needle)) keys.push(node.key);
|
|
55180
55770
|
}
|
|
55181
|
-
for (let id of ids) states[id] = ["active"];
|
|
55182
55771
|
}
|
|
55183
|
-
let
|
|
55184
|
-
|
|
55185
|
-
|
|
55186
|
-
|
|
55187
|
-
if (
|
|
55188
|
-
|
|
55189
|
-
|
|
55190
|
-
} catch (e) {
|
|
55191
|
-
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: find apply failed: ${e}`);
|
|
55772
|
+
let visible = fold_visible_set(model, state);
|
|
55773
|
+
let revealed = 0;
|
|
55774
|
+
for (let key of keys) {
|
|
55775
|
+
if (visible.has(key)) continue;
|
|
55776
|
+
if (revealed >= state.page_size) break;
|
|
55777
|
+
fold_reveal(model, state, key);
|
|
55778
|
+
revealed++;
|
|
55192
55779
|
}
|
|
55193
|
-
|
|
55780
|
+
priv._focus_topic = null;
|
|
55781
|
+
reconcile_fold(gobj, {}).then(() => {
|
|
55782
|
+
apply_focus_ids(gobj, keys, "find");
|
|
55783
|
+
});
|
|
55784
|
+
return keys.length;
|
|
55194
55785
|
}
|
|
55195
55786
|
async function graph_zoom_in(gobj) {
|
|
55196
55787
|
let graph = gobj.priv.graph;
|
|
@@ -55303,20 +55894,43 @@ var LightNode = class extends _antv_g6.Circle {
|
|
|
55303
55894
|
************************************************************/
|
|
55304
55895
|
var ManualLayout = class extends _antv_g6.BaseLayout {
|
|
55305
55896
|
async execute(data, options) {
|
|
55306
|
-
const { nodes = [] } = data;
|
|
55307
|
-
|
|
55308
|
-
|
|
55309
|
-
|
|
55897
|
+
const { nodes = [], edges = [] } = data;
|
|
55898
|
+
let placed = {};
|
|
55899
|
+
let out = [];
|
|
55900
|
+
let chips = [];
|
|
55901
|
+
for (let node of nodes) {
|
|
55902
|
+
let d = node.data || {};
|
|
55903
|
+
if (d.more) {
|
|
55904
|
+
chips.push(node);
|
|
55905
|
+
continue;
|
|
55906
|
+
}
|
|
55907
|
+
let gp = d.graph_props;
|
|
55908
|
+
let geo = d.record ? d.record._geometry : null;
|
|
55310
55909
|
let x = gp && gp.x != null ? gp.x : geo && geo.x != null ? geo.x : 0;
|
|
55311
55910
|
let y = gp && gp.y != null ? gp.y : geo && geo.y != null ? geo.y : 0;
|
|
55312
|
-
|
|
55911
|
+
placed[node.id] = [x, y];
|
|
55912
|
+
out.push({
|
|
55313
55913
|
id: node.id,
|
|
55314
55914
|
style: {
|
|
55315
55915
|
x,
|
|
55316
55916
|
y
|
|
55317
55917
|
}
|
|
55318
|
-
};
|
|
55319
|
-
}
|
|
55918
|
+
});
|
|
55919
|
+
}
|
|
55920
|
+
for (let node of chips) {
|
|
55921
|
+
let edge = edges.find((e) => e.target === node.id);
|
|
55922
|
+
let p = edge ? placed[edge.source] : null;
|
|
55923
|
+
let x = p ? p[0] : node.style && node.style.x || 0;
|
|
55924
|
+
let y = p ? p[1] + 80 : node.style && node.style.y || 0;
|
|
55925
|
+
out.push({
|
|
55926
|
+
id: node.id,
|
|
55927
|
+
style: {
|
|
55928
|
+
x,
|
|
55929
|
+
y
|
|
55930
|
+
}
|
|
55931
|
+
});
|
|
55932
|
+
}
|
|
55933
|
+
return { nodes: out };
|
|
55320
55934
|
}
|
|
55321
55935
|
};
|
|
55322
55936
|
/************************************************************
|
|
@@ -56820,7 +57434,7 @@ function show_node_popover(gobj) {
|
|
|
56820
57434
|
};
|
|
56821
57435
|
if (node_graph_type === "hierarchical") {
|
|
56822
57436
|
let record = nodeData.data.record || {};
|
|
56823
|
-
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id);
|
|
57437
|
+
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id, false, false, false, pills_html_of(gobj, nodeData));
|
|
56824
57438
|
}
|
|
56825
57439
|
graph.updateNodeData([{
|
|
56826
57440
|
id: node_id,
|
|
@@ -56860,7 +57474,7 @@ function show_node_popover(gobj) {
|
|
|
56860
57474
|
};
|
|
56861
57475
|
if (node_graph_type === "hierarchical") {
|
|
56862
57476
|
let record = nodeData.data.record || {};
|
|
56863
|
-
restoreStyle.innerHTML = build_node_innerHTML(origFill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id);
|
|
57477
|
+
restoreStyle.innerHTML = build_node_innerHTML(origFill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id, false, false, false, pills_html_of(gobj, nodeData));
|
|
56864
57478
|
}
|
|
56865
57479
|
graph.updateNodeData([{
|
|
56866
57480
|
id: node_id,
|
|
@@ -57045,6 +57659,7 @@ function refresh_minimap(gobj) {
|
|
|
57045
57659
|
let nd = graph.getNodeData(id);
|
|
57046
57660
|
let size = nd && nd.style && nd.style.size || [120, 60];
|
|
57047
57661
|
let color = nd && nd.data && nd.data.desc && nd.data.desc.color || "#94a3b8";
|
|
57662
|
+
if (is_more_node(nd)) color = "rgba(148, 163, 184, 0.35)";
|
|
57048
57663
|
return new Rect({ style: {
|
|
57049
57664
|
x: -size[0] / 2,
|
|
57050
57665
|
y: -size[1] / 2,
|
|
@@ -57067,15 +57682,15 @@ function refresh_minimap(gobj) {
|
|
|
57067
57682
|
* same three-way choice — it lived inline in the theme refresh and is
|
|
57068
57683
|
* shared now. Returns null for a node that carries no desc.
|
|
57069
57684
|
************************************************************/
|
|
57070
|
-
function node_innerHTML_of(nd, theme, highlight, selected, anchored) {
|
|
57685
|
+
function node_innerHTML_of(nd, theme, highlight, selected, anchored, pills_html) {
|
|
57071
57686
|
if (!nd || !nd.data || !nd.data.desc) return null;
|
|
57072
57687
|
let desc = nd.data.desc;
|
|
57073
57688
|
let record = nd.data.record || {};
|
|
57074
57689
|
let label = node_label(desc, record);
|
|
57075
57690
|
switch (desc.node_treedb_type) {
|
|
57076
57691
|
case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight, selected, anchored);
|
|
57077
|
-
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected, anchored);
|
|
57078
|
-
case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected, anchored);
|
|
57692
|
+
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected, anchored, pills_html);
|
|
57693
|
+
case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected, anchored, pills_html);
|
|
57079
57694
|
}
|
|
57080
57695
|
return null;
|
|
57081
57696
|
}
|
|
@@ -57096,7 +57711,13 @@ function repaint_cards(gobj, ids) {
|
|
|
57096
57711
|
let selected = new Set(priv._selected_paint_ids || []);
|
|
57097
57712
|
let updates = [];
|
|
57098
57713
|
for (let id of ids) {
|
|
57099
|
-
let
|
|
57714
|
+
let nd;
|
|
57715
|
+
try {
|
|
57716
|
+
nd = graph.getNodeData(id);
|
|
57717
|
+
} catch (e) {
|
|
57718
|
+
continue;
|
|
57719
|
+
}
|
|
57720
|
+
let html = node_innerHTML_of(nd, priv.theme, focus.has(id), selected.has(id), priv.anchor_state === "on" && id === priv.anchor_id, pills_html_of(gobj, nd));
|
|
57100
57721
|
if (html !== null) updates.push({
|
|
57101
57722
|
id,
|
|
57102
57723
|
style: { innerHTML: html }
|
|
@@ -57132,7 +57753,9 @@ function refresh_html_nodes_theme(gobj, theme) {
|
|
|
57132
57753
|
let updates = [];
|
|
57133
57754
|
for (let i = 0; i < nodes.length; i++) {
|
|
57134
57755
|
let id = nodes[i].id;
|
|
57135
|
-
let
|
|
57756
|
+
let nd = graph.getNodeData(id);
|
|
57757
|
+
let html = node_innerHTML_of(nd, theme, highlighted.has(id), selected.has(id), priv.anchor_state === "on" && id === priv.anchor_id, pills_html_of(gobj, nd));
|
|
57758
|
+
if (html === null) html = more_innerHTML_of(gobj, nd, theme);
|
|
57136
57759
|
if (html !== null) updates.push({
|
|
57137
57760
|
id,
|
|
57138
57761
|
style: { innerHTML: html }
|
|
@@ -57247,7 +57870,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight, selecte
|
|
|
57247
57870
|
* colour is kept (per-topic differentiation) but softened via
|
|
57248
57871
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
57249
57872
|
************************************************************/
|
|
57250
|
-
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected, anchored) {
|
|
57873
|
+
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected, anchored, pills_html) {
|
|
57251
57874
|
let title = key || label;
|
|
57252
57875
|
let dark = theme === "dark";
|
|
57253
57876
|
let surface = dark ? "#1b2230" : "#ffffff";
|
|
@@ -57281,6 +57904,12 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
|
|
|
57281
57904
|
margin-top: 2px; max-width: 100%;
|
|
57282
57905
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
57283
57906
|
">${(0, _yuneta_gobj_js.escapeHtml)(topic_name)}</div>`;
|
|
57907
|
+
let pills_row = "";
|
|
57908
|
+
if (pills_html) pills_row = `
|
|
57909
|
+
<div class="TREEDB_PILLS" style="
|
|
57910
|
+
display: flex; flex-wrap: nowrap; gap: 4px; justify-content: center;
|
|
57911
|
+
margin-top: 6px; max-width: 100%; overflow: hidden;
|
|
57912
|
+
">${pills_html}</div>`;
|
|
57284
57913
|
return `
|
|
57285
57914
|
<div class="TREEDB_CARD" title="${(0, _yuneta_gobj_js.escapeHtml)(title)}" style="
|
|
57286
57915
|
box-sizing: border-box;
|
|
@@ -57306,7 +57935,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
|
|
|
57306
57935
|
max-width: 100%; overflow: hidden; text-overflow: ellipsis;
|
|
57307
57936
|
display: -webkit-box; -webkit-line-clamp: 2;
|
|
57308
57937
|
-webkit-box-orient: vertical; word-break: break-word;
|
|
57309
|
-
">${(0, _yuneta_gobj_js.escapeHtml)(label)}</div>${sub_html}
|
|
57938
|
+
">${(0, _yuneta_gobj_js.escapeHtml)(label)}</div>${sub_html}${pills_row}
|
|
57310
57939
|
</div>
|
|
57311
57940
|
`;
|
|
57312
57941
|
}
|
|
@@ -57339,7 +57968,7 @@ function execute_delete_node(gobj, nodeData) {
|
|
|
57339
57968
|
function select_all_nodes(gobj) {
|
|
57340
57969
|
let graph = gobj.priv.graph;
|
|
57341
57970
|
if (!graph) return;
|
|
57342
|
-
set_selection(gobj, ((graph.getData() || {}).nodes || []).map((nd) => nd.id));
|
|
57971
|
+
set_selection(gobj, ((graph.getData() || {}).nodes || []).filter((nd) => nd.data && nd.data.desc).map((nd) => nd.id));
|
|
57343
57972
|
}
|
|
57344
57973
|
/************************************************************
|
|
57345
57974
|
* The nodes currently selected, as their node data.
|
|
@@ -57603,7 +58232,7 @@ function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope) {
|
|
|
57603
58232
|
};
|
|
57604
58233
|
if (nd.data.desc.node_treedb_type === "hierarchical") {
|
|
57605
58234
|
let record = nd.data.record || {};
|
|
57606
|
-
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nd.data.desc, record), nd.data.desc.topic_name, false, record.id);
|
|
58235
|
+
updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nd.data.desc, record), nd.data.desc.topic_name, false, record.id, false, false, false, pills_html_of(gobj, nd));
|
|
57607
58236
|
}
|
|
57608
58237
|
updates.push({
|
|
57609
58238
|
id: nodes[i].id,
|
|
@@ -57679,7 +58308,7 @@ function apply_edge_properties(gobj, edge_id, lineWidth, stroke, scope) {
|
|
|
57679
58308
|
function update_edge_geometry(gobj, edge_id) {
|
|
57680
58309
|
let priv = gobj.priv;
|
|
57681
58310
|
let edgeData = priv.graph.getEdgeData(edge_id);
|
|
57682
|
-
if (!edgeData || !edgeData.data) return;
|
|
58311
|
+
if (!edgeData || !edgeData.data || !edgeData.data.parent_topic) return;
|
|
57683
58312
|
let style = edgeData.style || {};
|
|
57684
58313
|
let lineWidth = style.lineWidth;
|
|
57685
58314
|
let stroke = style.stroke;
|
|
@@ -57820,7 +58449,7 @@ function copy_size_to_nodes(gobj, same_topic_only) {
|
|
|
57820
58449
|
const nodes = graph.getData().nodes;
|
|
57821
58450
|
for (let i = 0; i < nodes.length; i++) {
|
|
57822
58451
|
let nd = graph.getNodeData(nodes[i].id);
|
|
57823
|
-
if (!nd || !nd.data) continue;
|
|
58452
|
+
if (!nd || !nd.data || !nd.data.desc) continue;
|
|
57824
58453
|
if (same_topic_only && nd.data.topic_name !== source_topic) continue;
|
|
57825
58454
|
if (nodes[i].id === node_id) continue;
|
|
57826
58455
|
let updateStyle = { size: [...source_size] };
|
|
@@ -57908,6 +58537,415 @@ function copy_size_to_ports(gobj, same_topic_only) {
|
|
|
57908
58537
|
}
|
|
57909
58538
|
}
|
|
57910
58539
|
/***************************
|
|
58540
|
+
* Folding
|
|
58541
|
+
***************************/
|
|
58542
|
+
var PILL_ROW_HEIGHT = 24;
|
|
58543
|
+
var MORE_CHIP_SIZE = [76, 26];
|
|
58544
|
+
/************************************************************
|
|
58545
|
+
* The three card sizes, with room for the pill row when there
|
|
58546
|
+
* is one. A chip (pure child) never has pills: it has no hooks.
|
|
58547
|
+
************************************************************/
|
|
58548
|
+
function card_size_for(node_treedb_type, has_pills) {
|
|
58549
|
+
let extra = has_pills ? PILL_ROW_HEIGHT : 0;
|
|
58550
|
+
switch (node_treedb_type) {
|
|
58551
|
+
case "child": return [116, 40];
|
|
58552
|
+
case "extended": return [144, 66 + extra];
|
|
58553
|
+
default: return [172, 96 + extra];
|
|
58554
|
+
}
|
|
58555
|
+
}
|
|
58556
|
+
/************************************************************
|
|
58557
|
+
* The model's key of a G6 record node, or "" for anything else.
|
|
58558
|
+
************************************************************/
|
|
58559
|
+
function node_key_of(nd) {
|
|
58560
|
+
if (!nd || !nd.data || !nd.data.desc || !nd.data.record) return "";
|
|
58561
|
+
return fold_node_key(nd.data.desc.topic_name, String(nd.data.record.id));
|
|
58562
|
+
}
|
|
58563
|
+
function is_more_node(nd) {
|
|
58564
|
+
return !!(nd && nd.data && nd.data.more);
|
|
58565
|
+
}
|
|
58566
|
+
function more_node_id(gobj, group_key) {
|
|
58567
|
+
return (0, _yuneta_gobj_js.sprintf)("more-%s-%s", gobj.priv.treedb_name, group_key);
|
|
58568
|
+
}
|
|
58569
|
+
function more_edge_id(gobj, group_key) {
|
|
58570
|
+
return (0, _yuneta_gobj_js.sprintf)("more-edge-%s-%s", gobj.priv.treedb_name, group_key);
|
|
58571
|
+
}
|
|
58572
|
+
/************************************************************
|
|
58573
|
+
* The colours of a pill or a chip, from the topic colour it
|
|
58574
|
+
* stands for -- the CHILD topic, the same colour its hook port
|
|
58575
|
+
* wears, so the pill and the port say the same thing.
|
|
58576
|
+
************************************************************/
|
|
58577
|
+
function pill_colors(color, theme) {
|
|
58578
|
+
let dark = theme === "dark";
|
|
58579
|
+
return {
|
|
58580
|
+
bg: dark ? `color-mix(in srgb, ${color} 45%, #1b2230)` : `color-mix(in srgb, ${color} 22%, #ffffff)`,
|
|
58581
|
+
fg: dark ? `color-mix(in srgb, ${color} 35%, #ffffff)` : `color-mix(in srgb, ${color} 55%, #0f172a)`,
|
|
58582
|
+
border: dark ? `color-mix(in srgb, ${color} 80%, #ffffff)` : color
|
|
58583
|
+
};
|
|
58584
|
+
}
|
|
58585
|
+
/************************************************************
|
|
58586
|
+
* One pill: `▸ devices 142` folded, `▾ devices 24/142` open on a
|
|
58587
|
+
* page, `▾ devices 142` open whole. A glyph and numbers on
|
|
58588
|
+
* purpose: the card is an innerHTML string where a WORD could
|
|
58589
|
+
* carry no i18n key, and the hook name is a schema identifier,
|
|
58590
|
+
* like the topic subtitle above it.
|
|
58591
|
+
************************************************************/
|
|
58592
|
+
function build_pill_html(pill, color, theme) {
|
|
58593
|
+
let c = pill_colors(color, theme);
|
|
58594
|
+
let glyph = pill.open ? "▾" : "▸";
|
|
58595
|
+
let count = pill.open && pill.shown < pill.total ? `${pill.shown}/${pill.total}` : `${pill.total}`;
|
|
58596
|
+
return `<span class="TREEDB_PILL" data-fold-group="${(0, _yuneta_gobj_js.escapeHtml)(pill.group_key)}"
|
|
58597
|
+
title="${(0, _yuneta_gobj_js.escapeHtml)(pill.hook)}" style="
|
|
58598
|
+
display: inline-flex; align-items: center; gap: 3px; flex: 0 1 auto;
|
|
58599
|
+
height: 16px; padding: 0 6px; border-radius: 8px;
|
|
58600
|
+
background: ${c.bg}; color: ${c.fg}; border: 1px solid ${c.border};
|
|
58601
|
+
font-size: 10px; font-weight: 700; line-height: 1; white-space: nowrap;
|
|
58602
|
+
overflow: hidden; text-overflow: ellipsis;
|
|
58603
|
+
cursor: pointer; user-select: none;
|
|
58604
|
+
"><span style="font-size: 9px;">${glyph}</span>${(0, _yuneta_gobj_js.escapeHtml)(pill.hook)}<span style="opacity: .75; font-weight: 600;">${count}</span></span>`;
|
|
58605
|
+
}
|
|
58606
|
+
/************************************************************
|
|
58607
|
+
* The pill row of one node, as html; "" when it has nothing to
|
|
58608
|
+
* fold or the model is not built yet.
|
|
58609
|
+
************************************************************/
|
|
58610
|
+
function pills_html_of_key(gobj, node_key) {
|
|
58611
|
+
let priv = gobj.priv;
|
|
58612
|
+
if (!priv._fold_model || !priv._fold_state || !node_key) return "";
|
|
58613
|
+
let pills = fold_pills_of(priv._fold_model, priv._fold_state, node_key);
|
|
58614
|
+
if (!pills.length) return "";
|
|
58615
|
+
let out = [];
|
|
58616
|
+
for (let pill of pills) {
|
|
58617
|
+
let child_desc = priv.descs ? priv.descs[pill.child_topic] : null;
|
|
58618
|
+
let color = child_desc && child_desc.color || "#94a3b8";
|
|
58619
|
+
out.push(build_pill_html(pill, color, priv.theme));
|
|
58620
|
+
}
|
|
58621
|
+
return out.join("");
|
|
58622
|
+
}
|
|
58623
|
+
function pills_html_of(gobj, nd) {
|
|
58624
|
+
return pills_html_of_key(gobj, node_key_of(nd));
|
|
58625
|
+
}
|
|
58626
|
+
function pills_signature(gobj, node_key) {
|
|
58627
|
+
let priv = gobj.priv;
|
|
58628
|
+
if (!priv._fold_model || !priv._fold_state) return "";
|
|
58629
|
+
return fold_pills_of(priv._fold_model, priv._fold_state, node_key).map((p) => `${p.hook}:${p.open ? p.shown : "-"}/${p.total}`).join(",");
|
|
58630
|
+
}
|
|
58631
|
+
/************************************************************
|
|
58632
|
+
* The `+N` chip at the end of a page: the rest of the group,
|
|
58633
|
+
* in the child topic's colour, dashed like a cut. Its title is
|
|
58634
|
+
* the one WORD of the folding, and it is translated at build
|
|
58635
|
+
* time: the chips are rebuilt on a language change.
|
|
58636
|
+
************************************************************/
|
|
58637
|
+
function build_more_innerHTML(color, theme, rest) {
|
|
58638
|
+
let c = pill_colors(color, theme);
|
|
58639
|
+
return `
|
|
58640
|
+
<div class="TREEDB_MORE" title="${(0, _yuneta_gobj_js.escapeHtml)((0, i18next.t)("show more"))}" style="
|
|
58641
|
+
box-sizing: border-box; width: 100%; height: 100%;
|
|
58642
|
+
display: flex; align-items: center; justify-content: center; gap: 4px;
|
|
58643
|
+
background: ${c.bg}; color: ${c.fg}; border: 1px dashed ${c.border};
|
|
58644
|
+
border-radius: 13px;
|
|
58645
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
58646
|
+
font-size: 12px; font-weight: 700; line-height: 1;
|
|
58647
|
+
cursor: pointer; user-select: none;
|
|
58648
|
+
">+${rest}<span style="font-size: 10px; opacity: .8;">…</span></div>
|
|
58649
|
+
`;
|
|
58650
|
+
}
|
|
58651
|
+
function more_innerHTML_of(gobj, nd, theme) {
|
|
58652
|
+
if (!is_more_node(nd)) return null;
|
|
58653
|
+
let priv = gobj.priv;
|
|
58654
|
+
let child_desc = priv.descs ? priv.descs[nd.data.child_topic] : null;
|
|
58655
|
+
return build_more_innerHTML(child_desc && child_desc.color || "#94a3b8", theme, nd.data.rest);
|
|
58656
|
+
}
|
|
58657
|
+
/************************************************************
|
|
58658
|
+
* The pills live inside an `innerHTML` string, so no handler can
|
|
58659
|
+
* be attached to them: the click is delegated from the container
|
|
58660
|
+
* in the CAPTURE phase over four event types -- the same door the
|
|
58661
|
+
* JSON graph opens for its fold handles, for the same reasons: G6
|
|
58662
|
+
* binds on the html node element, a descendant, so only capture
|
|
58663
|
+
* runs first; and G6 never reads the DOM `click`, it builds its
|
|
58664
|
+
* own from the pointer pair, so `pointerdown`/`pointerup` are the
|
|
58665
|
+
* ones that have to be cut or the card is ALSO clicked.
|
|
58666
|
+
************************************************************/
|
|
58667
|
+
var FOLD_SWALLOWED_EVENTS = [
|
|
58668
|
+
"pointerdown",
|
|
58669
|
+
"pointerup",
|
|
58670
|
+
"mousedown",
|
|
58671
|
+
"click"
|
|
58672
|
+
];
|
|
58673
|
+
function install_fold_listener(gobj) {
|
|
58674
|
+
let priv = gobj.priv;
|
|
58675
|
+
if (!priv.$container) {
|
|
58676
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no container, the fold pills are dead`);
|
|
58677
|
+
return;
|
|
58678
|
+
}
|
|
58679
|
+
priv._fold_listener = function(evt) {
|
|
58680
|
+
let $pill = evt.target && evt.target.closest ? evt.target.closest("[data-fold-group]") : null;
|
|
58681
|
+
if (!$pill) return;
|
|
58682
|
+
evt.stopPropagation();
|
|
58683
|
+
evt.preventDefault();
|
|
58684
|
+
if (evt.type !== "click") return;
|
|
58685
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_FOLD", { group: $pill.getAttribute("data-fold-group") }, gobj);
|
|
58686
|
+
};
|
|
58687
|
+
for (let type of FOLD_SWALLOWED_EVENTS) priv.$container.addEventListener(type, priv._fold_listener, true);
|
|
58688
|
+
}
|
|
58689
|
+
function uninstall_fold_listener(gobj) {
|
|
58690
|
+
let priv = gobj.priv;
|
|
58691
|
+
if (priv._fold_listener && priv.$container) for (let type of FOLD_SWALLOWED_EVENTS) priv.$container.removeEventListener(type, priv._fold_listener, true);
|
|
58692
|
+
priv._fold_listener = null;
|
|
58693
|
+
}
|
|
58694
|
+
/************************************************************
|
|
58695
|
+
* (Re)build the tree of the records. Cheap -- one pass over the
|
|
58696
|
+
* records -- so it runs again on every create/update/delete
|
|
58697
|
+
* rather than being patched. The fold state survives: it is
|
|
58698
|
+
* keyed by group, and a group that is gone is simply never read.
|
|
58699
|
+
************************************************************/
|
|
58700
|
+
function rebuild_fold_model(gobj) {
|
|
58701
|
+
let priv = gobj.priv;
|
|
58702
|
+
priv._fold_model = fold_build_model(priv.descs || {}, priv.records || {});
|
|
58703
|
+
if (!priv._fold_state) priv._fold_state = fold_new_state((0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "fold_page_size"));
|
|
58704
|
+
if (!priv._pill_sig) priv._pill_sig = /* @__PURE__ */ new Map();
|
|
58705
|
+
}
|
|
58706
|
+
/************************************************************
|
|
58707
|
+
* How many records there are, and how many carry a saved
|
|
58708
|
+
* position -- what auto_layout decides on. Counted over the
|
|
58709
|
+
* RECORDS, because the nodes on screen are a fraction of them.
|
|
58710
|
+
************************************************************/
|
|
58711
|
+
function count_placed_records(gobj) {
|
|
58712
|
+
let priv = gobj.priv;
|
|
58713
|
+
priv._nodes_total = 0;
|
|
58714
|
+
priv._nodes_placed = 0;
|
|
58715
|
+
for (const [topic_name, records] of Object.entries(priv.records || {})) {
|
|
58716
|
+
if (topic_name.substring(0, 2) === "__" || !(0, _yuneta_gobj_js.is_array)(records)) continue;
|
|
58717
|
+
for (let record of records) {
|
|
58718
|
+
priv._nodes_total++;
|
|
58719
|
+
if (geometry_has_position(get_node_graph_props(gobj, topic_name, record.id, record))) priv._nodes_placed++;
|
|
58720
|
+
}
|
|
58721
|
+
}
|
|
58722
|
+
}
|
|
58723
|
+
/************************************************************
|
|
58724
|
+
* Make the G6 data say what the fold model says is visible.
|
|
58725
|
+
*
|
|
58726
|
+
* Record nodes not visible any more are removed (their edges go
|
|
58727
|
+
* with them), the visible ones missing are created, the links
|
|
58728
|
+
* between visible nodes are drawn, the `+N` chips are placed or
|
|
58729
|
+
* taken away, and every card whose pills changed is repainted.
|
|
58730
|
+
* Then a draw, a layout if the set of nodes changed, and the
|
|
58731
|
+
* camera: fit the whole thing on the first load, or put `keep`
|
|
58732
|
+
* back where it was on screen before the change.
|
|
58733
|
+
*
|
|
58734
|
+
* Serialised: one reconcile at a time, in order. Two pills tapped
|
|
58735
|
+
* quickly would otherwise interleave their draws.
|
|
58736
|
+
*
|
|
58737
|
+
* opts.relayout run the layout even if no node came or went
|
|
58738
|
+
* opts.fit fit the viewport afterwards
|
|
58739
|
+
* opts.keep node id to hold still on screen
|
|
58740
|
+
* opts.keep_vp where it was ([x, y] viewport px)
|
|
58741
|
+
************************************************************/
|
|
58742
|
+
function reconcile_fold(gobj, opts) {
|
|
58743
|
+
let priv = gobj.priv;
|
|
58744
|
+
let run = () => reconcile_fold_now(gobj, opts || {}).catch((e) => {
|
|
58745
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: reconcile failed: ${e}`);
|
|
58746
|
+
});
|
|
58747
|
+
priv._fold_chain = (priv._fold_chain || Promise.resolve()).then(run, run);
|
|
58748
|
+
return priv._fold_chain;
|
|
58749
|
+
}
|
|
58750
|
+
async function reconcile_fold_now(gobj, opts) {
|
|
58751
|
+
let priv = gobj.priv;
|
|
58752
|
+
let graph = priv.graph;
|
|
58753
|
+
let model = priv._fold_model;
|
|
58754
|
+
let state = priv._fold_state;
|
|
58755
|
+
if (!graph || !model || !state || (0, _yuneta_gobj_js.gobj_is_destroying)(gobj)) return;
|
|
58756
|
+
let visible = fold_visible_set(model, state);
|
|
58757
|
+
let changed = false;
|
|
58758
|
+
history_pause(gobj);
|
|
58759
|
+
try {
|
|
58760
|
+
changed = await reconcile_fold_apply(gobj, opts, visible);
|
|
58761
|
+
} finally {
|
|
58762
|
+
history_resume(gobj);
|
|
58763
|
+
}
|
|
58764
|
+
return changed;
|
|
58765
|
+
}
|
|
58766
|
+
async function reconcile_fold_apply(gobj, opts, visible) {
|
|
58767
|
+
let priv = gobj.priv;
|
|
58768
|
+
let graph = priv.graph;
|
|
58769
|
+
let model = priv._fold_model;
|
|
58770
|
+
let state = priv._fold_state;
|
|
58771
|
+
let changed = false;
|
|
58772
|
+
let present = /* @__PURE__ */ new Map();
|
|
58773
|
+
let more_present = /* @__PURE__ */ new Map();
|
|
58774
|
+
for (let nd of graph.getNodeData() || []) {
|
|
58775
|
+
if (is_more_node(nd)) {
|
|
58776
|
+
more_present.set(nd.data.more, nd.id);
|
|
58777
|
+
continue;
|
|
58778
|
+
}
|
|
58779
|
+
let key = node_key_of(nd);
|
|
58780
|
+
if (key) present.set(key, nd.id);
|
|
58781
|
+
}
|
|
58782
|
+
let to_remove = [];
|
|
58783
|
+
for (let [key, id] of present) if (!visible.has(key)) to_remove.push(id);
|
|
58784
|
+
if (to_remove.length) {
|
|
58785
|
+
let gone = new Set(to_remove);
|
|
58786
|
+
if (priv._selected_node_id && gone.has(priv._selected_node_id)) deselect_node(gobj);
|
|
58787
|
+
if (priv._detail_node_id && gone.has(priv._detail_node_id)) hide_node_detail(gobj);
|
|
58788
|
+
priv._focus_ids = (priv._focus_ids || []).filter((id) => !gone.has(id));
|
|
58789
|
+
priv._selected_paint_ids = (priv._selected_paint_ids || []).filter((id) => !gone.has(id));
|
|
58790
|
+
if (priv.anchor_state === "on" && gone.has(priv.anchor_id)) {
|
|
58791
|
+
priv.anchor_id = "";
|
|
58792
|
+
priv.anchor_state = "off";
|
|
58793
|
+
update_toolbar(gobj);
|
|
58794
|
+
}
|
|
58795
|
+
for (let id of to_remove) priv._pill_sig.delete(id);
|
|
58796
|
+
graph.removeNodeData(to_remove);
|
|
58797
|
+
changed = true;
|
|
58798
|
+
}
|
|
58799
|
+
for (let key of visible) {
|
|
58800
|
+
if (present.has(key)) continue;
|
|
58801
|
+
let node = model.nodes.get(key);
|
|
58802
|
+
let desc = priv.descs[node.topic_name];
|
|
58803
|
+
if (!desc) {
|
|
58804
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no desc for ${key}`);
|
|
58805
|
+
continue;
|
|
58806
|
+
}
|
|
58807
|
+
create_topic_node(gobj, desc, node.record);
|
|
58808
|
+
priv._pill_sig.set(build_node_name(gobj, node.topic_name, node.id), pills_signature(gobj, key));
|
|
58809
|
+
changed = true;
|
|
58810
|
+
}
|
|
58811
|
+
for (let key of visible) {
|
|
58812
|
+
let node = model.nodes.get(key);
|
|
58813
|
+
let desc = priv.descs[node.topic_name];
|
|
58814
|
+
if (desc) draw_links(gobj, desc, node.record, false);
|
|
58815
|
+
}
|
|
58816
|
+
let pending = fold_pending_groups(model, state, visible);
|
|
58817
|
+
let wanted = /* @__PURE__ */ new Map();
|
|
58818
|
+
for (let p of pending) wanted.set(p.group_key, p);
|
|
58819
|
+
let more_gone = [];
|
|
58820
|
+
for (let [group_key, id] of more_present) if (!wanted.has(group_key)) more_gone.push(id);
|
|
58821
|
+
if (more_gone.length) {
|
|
58822
|
+
graph.removeNodeData(more_gone);
|
|
58823
|
+
changed = true;
|
|
58824
|
+
}
|
|
58825
|
+
for (let p of pending) {
|
|
58826
|
+
let first = model.nodes.get((model.groups.get(p.group_key) || [])[0]);
|
|
58827
|
+
let child_topic = first ? first.topic_name : "";
|
|
58828
|
+
let child_desc = priv.descs[child_topic];
|
|
58829
|
+
let color = child_desc && child_desc.color || "#94a3b8";
|
|
58830
|
+
let id = more_node_id(gobj, p.group_key);
|
|
58831
|
+
if (more_present.has(p.group_key)) {
|
|
58832
|
+
let nd = graph.getNodeData(id);
|
|
58833
|
+
if (nd && nd.data.rest !== p.rest) {
|
|
58834
|
+
nd.data.rest = p.rest;
|
|
58835
|
+
graph.updateNodeData([{
|
|
58836
|
+
id,
|
|
58837
|
+
style: { innerHTML: build_more_innerHTML(color, priv.theme, p.rest) }
|
|
58838
|
+
}]);
|
|
58839
|
+
}
|
|
58840
|
+
continue;
|
|
58841
|
+
}
|
|
58842
|
+
let xy = get_default_ne_xy(gobj);
|
|
58843
|
+
graph.addNodeData([{
|
|
58844
|
+
id,
|
|
58845
|
+
type: "html",
|
|
58846
|
+
style: {
|
|
58847
|
+
x: xy,
|
|
58848
|
+
y: xy,
|
|
58849
|
+
size: [...MORE_CHIP_SIZE],
|
|
58850
|
+
dx: -MORE_CHIP_SIZE[0] / 2,
|
|
58851
|
+
dy: -MORE_CHIP_SIZE[1] / 2,
|
|
58852
|
+
innerHTML: build_more_innerHTML(color, priv.theme, p.rest)
|
|
58853
|
+
},
|
|
58854
|
+
data: {
|
|
58855
|
+
more: p.group_key,
|
|
58856
|
+
child_topic,
|
|
58857
|
+
rest: p.rest
|
|
58858
|
+
}
|
|
58859
|
+
}]);
|
|
58860
|
+
if (p.node_key) {
|
|
58861
|
+
let parent = model.nodes.get(p.node_key);
|
|
58862
|
+
let dark = priv.theme === "dark";
|
|
58863
|
+
graph.addEdgeData([{
|
|
58864
|
+
id: more_edge_id(gobj, p.group_key),
|
|
58865
|
+
type: edge_type_for(gobj),
|
|
58866
|
+
source: build_node_name(gobj, parent.topic_name, parent.id),
|
|
58867
|
+
target: id,
|
|
58868
|
+
style: {
|
|
58869
|
+
sourcePort: p.hook,
|
|
58870
|
+
lineWidth: 1,
|
|
58871
|
+
lineDash: [4, 3],
|
|
58872
|
+
stroke: dark ? "#8b94a3" : "#94a3b8",
|
|
58873
|
+
startArrow: false,
|
|
58874
|
+
endArrow: false
|
|
58875
|
+
},
|
|
58876
|
+
data: { more: p.group_key }
|
|
58877
|
+
}]);
|
|
58878
|
+
}
|
|
58879
|
+
changed = true;
|
|
58880
|
+
}
|
|
58881
|
+
let repaint = /* @__PURE__ */ new Set();
|
|
58882
|
+
let resize = [];
|
|
58883
|
+
for (let key of visible) {
|
|
58884
|
+
let node = model.nodes.get(key);
|
|
58885
|
+
let id = build_node_name(gobj, node.topic_name, node.id);
|
|
58886
|
+
let sig = pills_signature(gobj, key);
|
|
58887
|
+
if (priv._pill_sig.get(id) === sig) continue;
|
|
58888
|
+
let had = !!priv._pill_sig.get(id);
|
|
58889
|
+
priv._pill_sig.set(id, sig);
|
|
58890
|
+
repaint.add(id);
|
|
58891
|
+
if (had !== !!sig) {
|
|
58892
|
+
let nd = graph.getNodeData(id);
|
|
58893
|
+
let desc = nd && nd.data && nd.data.desc;
|
|
58894
|
+
let geometry = nd && nd.data && nd.data.graph_props || {};
|
|
58895
|
+
if (desc && !(0, _yuneta_gobj_js.is_array)(geometry.size)) {
|
|
58896
|
+
let size = card_size_for(desc.node_treedb_type, !!sig);
|
|
58897
|
+
resize.push({
|
|
58898
|
+
id,
|
|
58899
|
+
style: {
|
|
58900
|
+
size,
|
|
58901
|
+
dx: -size[0] / 2,
|
|
58902
|
+
dy: -size[1] / 2
|
|
58903
|
+
}
|
|
58904
|
+
});
|
|
58905
|
+
}
|
|
58906
|
+
}
|
|
58907
|
+
}
|
|
58908
|
+
if (resize.length) graph.updateNodeData(resize);
|
|
58909
|
+
if (repaint.size) repaint_cards(gobj, repaint);
|
|
58910
|
+
try {
|
|
58911
|
+
refresh_minimap(gobj);
|
|
58912
|
+
} catch (e) {
|
|
58913
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: refresh_minimap failed: ${e}`);
|
|
58914
|
+
}
|
|
58915
|
+
await graph_draw(gobj);
|
|
58916
|
+
if (changed || opts.relayout) await graph_layout(gobj);
|
|
58917
|
+
if (opts.fit) await graph_fit_readable(gobj);
|
|
58918
|
+
else if (opts.keep && opts.keep_vp) await yui_graph_place_at(graph, opts.keep, opts.keep_vp);
|
|
58919
|
+
update_resize_handles_position(gobj);
|
|
58920
|
+
update_port_resize_handles_position(gobj);
|
|
58921
|
+
update_edge_icon_position(gobj);
|
|
58922
|
+
update_node_icon_position(gobj);
|
|
58923
|
+
update_link_icon_position(gobj);
|
|
58924
|
+
return changed;
|
|
58925
|
+
}
|
|
58926
|
+
/************************************************************
|
|
58927
|
+
* Repaint the `+N` chips (theme or language changed).
|
|
58928
|
+
************************************************************/
|
|
58929
|
+
function repaint_more_chips(gobj) {
|
|
58930
|
+
let priv = gobj.priv;
|
|
58931
|
+
let graph = priv.graph;
|
|
58932
|
+
if (!graph) return;
|
|
58933
|
+
let updates = [];
|
|
58934
|
+
for (let nd of graph.getNodeData() || []) {
|
|
58935
|
+
let html = more_innerHTML_of(gobj, nd, priv.theme);
|
|
58936
|
+
if (html !== null) updates.push({
|
|
58937
|
+
id: nd.id,
|
|
58938
|
+
style: { innerHTML: html }
|
|
58939
|
+
});
|
|
58940
|
+
}
|
|
58941
|
+
if (updates.length) try {
|
|
58942
|
+
graph.updateNodeData(updates);
|
|
58943
|
+
graph.draw();
|
|
58944
|
+
} catch (e) {
|
|
58945
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot repaint the chips: ${e}`);
|
|
58946
|
+
}
|
|
58947
|
+
}
|
|
58948
|
+
/***************************
|
|
57911
58949
|
* Actions
|
|
57912
58950
|
***************************/
|
|
57913
58951
|
/************************************************************
|
|
@@ -57936,6 +58974,9 @@ function ac_clear_data(gobj, event, kw, src) {
|
|
|
57936
58974
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "records", {});
|
|
57937
58975
|
priv._nodes_total = 0;
|
|
57938
58976
|
priv._nodes_placed = 0;
|
|
58977
|
+
priv._fold_model = null;
|
|
58978
|
+
priv._fold_state = null;
|
|
58979
|
+
priv._pill_sig = null;
|
|
57939
58980
|
graph_remove_plugin(gobj, "history");
|
|
57940
58981
|
update_history_buttons(gobj);
|
|
57941
58982
|
graph_clear(gobj);
|
|
@@ -57966,53 +59007,43 @@ function ac_load_data(gobj, event, kw, src) {
|
|
|
57966
59007
|
}
|
|
57967
59008
|
return 0;
|
|
57968
59009
|
}
|
|
57969
|
-
|
|
59010
|
+
priv.descs[topic_name];
|
|
57970
59011
|
priv.records[topic_name] = data;
|
|
57971
59012
|
priv.descs[topic_name].loaded = true;
|
|
57972
|
-
|
|
57973
|
-
let record = data[i];
|
|
57974
|
-
create_topic_node(gobj, desc, record);
|
|
57975
|
-
}
|
|
57976
|
-
let do_links = true;
|
|
59013
|
+
let all_loaded = true;
|
|
57977
59014
|
for (const [topic_name, desc] of Object.entries(priv.descs)) {
|
|
57978
59015
|
if (topic_name.substring(0, 2) === "__") continue;
|
|
57979
59016
|
if (!desc.loaded) {
|
|
57980
|
-
|
|
59017
|
+
all_loaded = false;
|
|
57981
59018
|
break;
|
|
57982
59019
|
}
|
|
57983
59020
|
}
|
|
57984
|
-
if (
|
|
59021
|
+
if (all_loaded && priv.graph) {
|
|
59022
|
+
rebuild_fold_model(gobj);
|
|
59023
|
+
fold_expand_to_depth(priv._fold_model, priv._fold_state, (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "expand_depth"));
|
|
59024
|
+
count_placed_records(gobj);
|
|
59025
|
+
let auto_laid = false;
|
|
57985
59026
|
try {
|
|
57986
|
-
|
|
59027
|
+
auto_laid = auto_layout(gobj);
|
|
57987
59028
|
} catch (e) {
|
|
57988
|
-
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}:
|
|
57989
|
-
}
|
|
57990
|
-
|
|
57991
|
-
|
|
57992
|
-
|
|
57993
|
-
|
|
57994
|
-
|
|
57995
|
-
|
|
57996
|
-
|
|
57997
|
-
|
|
57998
|
-
|
|
57999
|
-
|
|
58000
|
-
|
|
58001
|
-
|
|
58002
|
-
|
|
58003
|
-
|
|
58004
|
-
|
|
58005
|
-
|
|
58006
|
-
priv._pending_focus_topic = null;
|
|
58007
|
-
graph_focus_topic(gobj, ft);
|
|
58008
|
-
}
|
|
58009
|
-
if (priv._pending_find !== null) {
|
|
58010
|
-
let term = priv._pending_find;
|
|
58011
|
-
priv._pending_find = null;
|
|
58012
|
-
publish_find_result(gobj, term, graph_find_nodes(gobj, term));
|
|
58013
|
-
}
|
|
58014
|
-
});
|
|
58015
|
-
});
|
|
59029
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: auto_layout failed: ${e}`);
|
|
59030
|
+
}
|
|
59031
|
+
apply_layout_direction(gobj);
|
|
59032
|
+
reconcile_fold(gobj, {
|
|
59033
|
+
relayout: true,
|
|
59034
|
+
fit: auto_laid
|
|
59035
|
+
}).then(() => {
|
|
59036
|
+
sync_history_plugin(gobj);
|
|
59037
|
+
if (priv._pending_focus_topic !== null) {
|
|
59038
|
+
let ft = priv._pending_focus_topic;
|
|
59039
|
+
priv._pending_focus_topic = null;
|
|
59040
|
+
graph_focus_topic(gobj, ft);
|
|
59041
|
+
}
|
|
59042
|
+
if (priv._pending_find !== null) {
|
|
59043
|
+
let term = priv._pending_find;
|
|
59044
|
+
priv._pending_find = null;
|
|
59045
|
+
publish_find_result(gobj, term, graph_find_nodes(gobj, term));
|
|
59046
|
+
}
|
|
58016
59047
|
});
|
|
58017
59048
|
}
|
|
58018
59049
|
return 0;
|
|
@@ -58048,19 +59079,15 @@ function ac_node_created(gobj, event, kw, src) {
|
|
|
58048
59079
|
build_graph_properties(gobj);
|
|
58049
59080
|
return 0;
|
|
58050
59081
|
}
|
|
58051
|
-
|
|
58052
|
-
if (!desc) {
|
|
59082
|
+
if (!priv.descs[topic_name]) {
|
|
58053
59083
|
(0, _yuneta_gobj_js.log_error)(`ac_node_created: unknown topic: ${topic_name}`);
|
|
58054
59084
|
return 0;
|
|
58055
59085
|
}
|
|
58056
59086
|
if (!priv.records[topic_name]) priv.records[topic_name] = [];
|
|
58057
59087
|
priv.records[topic_name].push(node);
|
|
58058
|
-
|
|
58059
|
-
|
|
58060
|
-
|
|
58061
|
-
graph_draw(gobj).then(() => {
|
|
58062
|
-
history_resume(gobj);
|
|
58063
|
-
});
|
|
59088
|
+
rebuild_fold_model(gobj);
|
|
59089
|
+
fold_reveal(priv._fold_model, priv._fold_state, fold_node_key(topic_name, String(node.id)));
|
|
59090
|
+
reconcile_fold(gobj, {});
|
|
58064
59091
|
return 0;
|
|
58065
59092
|
}
|
|
58066
59093
|
/************************************************************
|
|
@@ -58109,8 +59136,14 @@ function ac_node_updated(gobj, event, kw, src) {
|
|
|
58109
59136
|
let [col_idx, fkey_value] = ref.split(" ");
|
|
58110
59137
|
clear_link(gobj, topic_name, node.id, cols[parseInt(col_idx)], fkey_value, false);
|
|
58111
59138
|
}
|
|
58112
|
-
update_topic_node(gobj, desc, node_name, node);
|
|
58113
59139
|
update_local_node(gobj, topic_name, node);
|
|
59140
|
+
let on_screen = false;
|
|
59141
|
+
try {
|
|
59142
|
+
on_screen = !!graph.getNodeData(node_name);
|
|
59143
|
+
} catch (e) {
|
|
59144
|
+
on_screen = false;
|
|
59145
|
+
}
|
|
59146
|
+
if (on_screen) update_topic_node(gobj, desc, node_name, node);
|
|
58114
59147
|
for (let ref of new_refs) if (!old_refs.has(ref)) {
|
|
58115
59148
|
let [col_idx, fkey_value] = ref.split(" ");
|
|
58116
59149
|
draw_link(gobj, topic_name, node.id, cols[parseInt(col_idx)], fkey_value, false);
|
|
@@ -58118,6 +59151,8 @@ function ac_node_updated(gobj, event, kw, src) {
|
|
|
58118
59151
|
graph_draw(gobj).then(() => {
|
|
58119
59152
|
history_resume(gobj);
|
|
58120
59153
|
});
|
|
59154
|
+
rebuild_fold_model(gobj);
|
|
59155
|
+
reconcile_fold(gobj, {});
|
|
58121
59156
|
return 0;
|
|
58122
59157
|
}
|
|
58123
59158
|
/************************************************************
|
|
@@ -58143,9 +59178,12 @@ function ac_node_deleted(gobj, event, kw, src) {
|
|
|
58143
59178
|
clear_links(gobj, desc, node, false);
|
|
58144
59179
|
remove_topic_node(gobj, node_name);
|
|
58145
59180
|
remove_local_node(gobj, topic_name, node);
|
|
59181
|
+
if (priv._pill_sig) priv._pill_sig.delete(node_name);
|
|
58146
59182
|
graph_draw(gobj).then(() => {
|
|
58147
59183
|
history_resume(gobj);
|
|
58148
59184
|
});
|
|
59185
|
+
rebuild_fold_model(gobj);
|
|
59186
|
+
reconcile_fold(gobj, {});
|
|
58149
59187
|
return 0;
|
|
58150
59188
|
}
|
|
58151
59189
|
/************************************************************
|
|
@@ -58204,6 +59242,7 @@ function ac_theme$1(gobj, event, kw, src) {
|
|
|
58204
59242
|
});
|
|
58205
59243
|
refresh_html_nodes_theme(gobj, theme);
|
|
58206
59244
|
refresh_default_edges_theme(gobj, theme);
|
|
59245
|
+
repaint_more_chips(gobj);
|
|
58207
59246
|
graph_draw(gobj).then(() => {
|
|
58208
59247
|
update_history_buttons(gobj);
|
|
58209
59248
|
});
|
|
@@ -58318,6 +59357,85 @@ function ac_focus_topic(gobj, event, kw, src) {
|
|
|
58318
59357
|
return 0;
|
|
58319
59358
|
}
|
|
58320
59359
|
/************************************************************
|
|
59360
|
+
* A pill was tapped: open that hook on its first page, or fold
|
|
59361
|
+
* it. The card the pill is on stays where it was on screen --
|
|
59362
|
+
* the layout moves everything else around it.
|
|
59363
|
+
************************************************************/
|
|
59364
|
+
function ac_toggle_fold(gobj, event, kw, src) {
|
|
59365
|
+
let priv = gobj.priv;
|
|
59366
|
+
let group = kw && kw.group || "";
|
|
59367
|
+
if (!priv._fold_model || !group) {
|
|
59368
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: fold of '${group}' with no tree loaded`);
|
|
59369
|
+
return -1;
|
|
59370
|
+
}
|
|
59371
|
+
let g = fold_split_group_key(group);
|
|
59372
|
+
if (!g) {
|
|
59373
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: bad fold group '${group}'`);
|
|
59374
|
+
return -1;
|
|
59375
|
+
}
|
|
59376
|
+
let node_id = g.node_key ? node_id_of_key(gobj, g.node_key) : "";
|
|
59377
|
+
let vp = node_id ? yui_graph_viewport_of(priv.graph, node_id) : null;
|
|
59378
|
+
fold_toggle(priv._fold_model, priv._fold_state, group);
|
|
59379
|
+
reconcile_fold(gobj, {
|
|
59380
|
+
keep: node_id,
|
|
59381
|
+
keep_vp: vp
|
|
59382
|
+
});
|
|
59383
|
+
return 0;
|
|
59384
|
+
}
|
|
59385
|
+
/************************************************************
|
|
59386
|
+
* A `+N` chip was tapped: the next page of its group. The
|
|
59387
|
+
* parent holds still; the chip itself moves down the column.
|
|
59388
|
+
************************************************************/
|
|
59389
|
+
function ac_show_more(gobj, event, kw, src) {
|
|
59390
|
+
let priv = gobj.priv;
|
|
59391
|
+
let group = kw && kw.group || "";
|
|
59392
|
+
if (!priv._fold_model || !group) {
|
|
59393
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: show more of '${group}' with no tree loaded`);
|
|
59394
|
+
return -1;
|
|
59395
|
+
}
|
|
59396
|
+
let g = fold_split_group_key(group);
|
|
59397
|
+
if (!g) {
|
|
59398
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: bad fold group '${group}'`);
|
|
59399
|
+
return -1;
|
|
59400
|
+
}
|
|
59401
|
+
let node_id = g.node_key ? node_id_of_key(gobj, g.node_key) : "";
|
|
59402
|
+
let vp = node_id ? yui_graph_viewport_of(priv.graph, node_id) : null;
|
|
59403
|
+
fold_show_more(priv._fold_model, priv._fold_state, group);
|
|
59404
|
+
reconcile_fold(gobj, {
|
|
59405
|
+
keep: node_id,
|
|
59406
|
+
keep_vp: vp
|
|
59407
|
+
});
|
|
59408
|
+
return 0;
|
|
59409
|
+
}
|
|
59410
|
+
/************************************************************
|
|
59411
|
+
* The whole treedb, every hook open on all its children. This
|
|
59412
|
+
* is the reader asking for the pile on purpose; the pages are
|
|
59413
|
+
* the other way in.
|
|
59414
|
+
************************************************************/
|
|
59415
|
+
function ac_expand_all$1(gobj, event, kw, src) {
|
|
59416
|
+
let priv = gobj.priv;
|
|
59417
|
+
if (!priv._fold_model) {
|
|
59418
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: expand all with no tree loaded`);
|
|
59419
|
+
return -1;
|
|
59420
|
+
}
|
|
59421
|
+
fold_expand_all(priv._fold_model, priv._fold_state);
|
|
59422
|
+
reconcile_fold(gobj, { fit: true });
|
|
59423
|
+
return 0;
|
|
59424
|
+
}
|
|
59425
|
+
/************************************************************
|
|
59426
|
+
* The roots alone.
|
|
59427
|
+
************************************************************/
|
|
59428
|
+
function ac_collapse_all$1(gobj, event, kw, src) {
|
|
59429
|
+
let priv = gobj.priv;
|
|
59430
|
+
if (!priv._fold_model) {
|
|
59431
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: collapse all with no tree loaded`);
|
|
59432
|
+
return -1;
|
|
59433
|
+
}
|
|
59434
|
+
fold_collapse_all(priv._fold_model, priv._fold_state);
|
|
59435
|
+
reconcile_fold(gobj, { fit: true });
|
|
59436
|
+
return 0;
|
|
59437
|
+
}
|
|
59438
|
+
/************************************************************
|
|
58321
59439
|
* Tell whoever asked how many nodes the term matched. The count is
|
|
58322
59440
|
* the answer to the question the box asks; without it "nothing moved"
|
|
58323
59441
|
* and "nothing matched" look the same.
|
|
@@ -58348,7 +59466,9 @@ function ac_fullscreen(gobj, event, kw, src) {
|
|
|
58348
59466
|
************************************************************/
|
|
58349
59467
|
function ac_set_layout$1(gobj, event, kw, src) {
|
|
58350
59468
|
gobj.priv;
|
|
58351
|
-
|
|
59469
|
+
let layout = select_layout(gobj, kw.layout);
|
|
59470
|
+
apply_layout_direction(gobj);
|
|
59471
|
+
graph_set_layout(gobj, layout).then(() => {
|
|
58352
59472
|
configure_behaviour(gobj);
|
|
58353
59473
|
update_toolbar(gobj);
|
|
58354
59474
|
});
|
|
@@ -58455,6 +59575,16 @@ function ac_node_click$2(gobj, event, kw, src) {
|
|
|
58455
59575
|
let priv = gobj.priv;
|
|
58456
59576
|
let graph = priv.graph;
|
|
58457
59577
|
let node_id = kw.evt.target.id;
|
|
59578
|
+
let more_nd = null;
|
|
59579
|
+
try {
|
|
59580
|
+
more_nd = graph.getNodeData(node_id);
|
|
59581
|
+
} catch (e) {
|
|
59582
|
+
more_nd = null;
|
|
59583
|
+
}
|
|
59584
|
+
if (is_more_node(more_nd)) {
|
|
59585
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SHOW_MORE", { group: more_nd.data.more }, gobj);
|
|
59586
|
+
return 0;
|
|
59587
|
+
}
|
|
58458
59588
|
if (priv.anchor_state === "arming") {
|
|
58459
59589
|
priv.anchor_id = node_id;
|
|
58460
59590
|
priv.anchor_state = "on";
|
|
@@ -58759,6 +59889,26 @@ function create_gclass$4(gclass_name) {
|
|
|
58759
59889
|
ac_find_nodes$1,
|
|
58760
59890
|
null
|
|
58761
59891
|
],
|
|
59892
|
+
[
|
|
59893
|
+
"EV_TOGGLE_FOLD",
|
|
59894
|
+
ac_toggle_fold,
|
|
59895
|
+
null
|
|
59896
|
+
],
|
|
59897
|
+
[
|
|
59898
|
+
"EV_SHOW_MORE",
|
|
59899
|
+
ac_show_more,
|
|
59900
|
+
null
|
|
59901
|
+
],
|
|
59902
|
+
[
|
|
59903
|
+
"EV_EXPAND_ALL",
|
|
59904
|
+
ac_expand_all$1,
|
|
59905
|
+
null
|
|
59906
|
+
],
|
|
59907
|
+
[
|
|
59908
|
+
"EV_COLLAPSE_ALL",
|
|
59909
|
+
ac_collapse_all$1,
|
|
59910
|
+
null
|
|
59911
|
+
],
|
|
58762
59912
|
[
|
|
58763
59913
|
"EV_CENTER",
|
|
58764
59914
|
ac_center,
|
|
@@ -58856,6 +60006,10 @@ function create_gclass$4(gclass_name) {
|
|
|
58856
60006
|
["EV_FIND_NODES", 0],
|
|
58857
60007
|
["EV_FIND_RESULT", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
|
|
58858
60008
|
["EV_LAYOUT_AUTOSET", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
|
|
60009
|
+
["EV_TOGGLE_FOLD", 0],
|
|
60010
|
+
["EV_SHOW_MORE", 0],
|
|
60011
|
+
["EV_EXPAND_ALL", 0],
|
|
60012
|
+
["EV_COLLAPSE_ALL", 0],
|
|
58859
60013
|
["EV_CENTER", 0],
|
|
58860
60014
|
["EV_FULLSCREEN", 0],
|
|
58861
60015
|
["EV_SET_LAYOUT", 0],
|
|
@@ -58913,6 +60067,8 @@ var attrs_table$3 = [
|
|
|
58913
60067
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
|
|
58914
60068
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "operation_modes", 0, "[\"reading\", \"operation\", \"writing\", \"edition\"]", "Available **permission** or behaviour modes. These operation modes are required to be accomplish by the graph handler (G6 child). TODO permissions must match treedb permissions."),
|
|
58915
60069
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_treedb_tables", 0, false, "Include treedb tables"),
|
|
60070
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "expand_depth", 0, 2, "Levels of the treedb open when it loads: 1 = the roots alone, 2 = the roots and their children. Forwarded to the G6 child"),
|
|
60071
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "fold_page_size", 0, 24, "Children of one hook shown per page in the graph; a `+N` chip opens the next page. Forwarded to the G6 child"),
|
|
58916
60072
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "operation_mode", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "reading", "Current operation mode (internal behaviour or role). Changed by the user trough the gui."),
|
|
58917
60073
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "layout", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "", "Current graph layout. User preference. Changed by the user through the gui."),
|
|
58918
60074
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote Yuno to request data"),
|
|
@@ -58986,7 +60142,9 @@ function mt_create$3(gobj) {
|
|
|
58986
60142
|
layout: priv.layout,
|
|
58987
60143
|
with_treedb_tables: priv.with_treedb_tables,
|
|
58988
60144
|
hook_port_position: "bottom",
|
|
58989
|
-
fkey_port_position: "top"
|
|
60145
|
+
fkey_port_position: "top",
|
|
60146
|
+
expand_depth: (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "expand_depth"),
|
|
60147
|
+
fold_page_size: (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "fold_page_size")
|
|
58990
60148
|
}, gobj);
|
|
58991
60149
|
populate_nodes_tree_options(gobj);
|
|
58992
60150
|
if (priv.with_treedb_tables) {}
|
|
@@ -59244,6 +60402,7 @@ function make_toolbar(gobj) {
|
|
|
59244
60402
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TREEDB", { evt }, gobj);
|
|
59245
60403
|
} }
|
|
59246
60404
|
],
|
|
60405
|
+
...yui_graph_fold_items(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "wide")),
|
|
59247
60406
|
[
|
|
59248
60407
|
"button",
|
|
59249
60408
|
{
|
|
@@ -60385,6 +61544,20 @@ function ac_find_nodes(gobj, event, kw, src) {
|
|
|
60385
61544
|
return 0;
|
|
60386
61545
|
}
|
|
60387
61546
|
/************************************************************
|
|
61547
|
+
* The fold pair of the toolbar, forwarded to the G6 child,
|
|
61548
|
+
* which owns the tree.
|
|
61549
|
+
************************************************************/
|
|
61550
|
+
function ac_expand_all(gobj, event, kw, src) {
|
|
61551
|
+
let priv = gobj.priv;
|
|
61552
|
+
if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_EXPAND_ALL", {}, gobj);
|
|
61553
|
+
return 0;
|
|
61554
|
+
}
|
|
61555
|
+
function ac_collapse_all(gobj, event, kw, src) {
|
|
61556
|
+
let priv = gobj.priv;
|
|
61557
|
+
if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_COLLAPSE_ALL", {}, gobj);
|
|
61558
|
+
return 0;
|
|
61559
|
+
}
|
|
61560
|
+
/************************************************************
|
|
60388
61561
|
* How many nodes the term matched, from the graph child. Zero with a
|
|
60389
61562
|
* term typed is an ANSWER and is shown; an empty box shows nothing.
|
|
60390
61563
|
************************************************************/
|
|
@@ -60577,6 +61750,16 @@ function create_gclass$3(gclass_name) {
|
|
|
60577
61750
|
ac_find_nodes,
|
|
60578
61751
|
null
|
|
60579
61752
|
],
|
|
61753
|
+
[
|
|
61754
|
+
"EV_EXPAND_ALL",
|
|
61755
|
+
ac_expand_all,
|
|
61756
|
+
null
|
|
61757
|
+
],
|
|
61758
|
+
[
|
|
61759
|
+
"EV_COLLAPSE_ALL",
|
|
61760
|
+
ac_collapse_all,
|
|
61761
|
+
null
|
|
61762
|
+
],
|
|
60580
61763
|
[
|
|
60581
61764
|
"EV_LAYOUT_AUTOSET",
|
|
60582
61765
|
ac_layout_autoset,
|
|
@@ -60639,6 +61822,8 @@ function create_gclass$3(gclass_name) {
|
|
|
60639
61822
|
["EV_SET_OPERATION_MODE", 0],
|
|
60640
61823
|
["EV_SET_FOCUS_TOPIC", 0],
|
|
60641
61824
|
["EV_FIND_NODES", 0],
|
|
61825
|
+
["EV_EXPAND_ALL", 0],
|
|
61826
|
+
["EV_COLLAPSE_ALL", 0],
|
|
60642
61827
|
["EV_FIND_RESULT", 0],
|
|
60643
61828
|
["EV_LAYOUT_AUTOSET", 0],
|
|
60644
61829
|
["EV_TOGGLE_LEGEND", 0],
|