@yuneta/gobj-ui 7.21.1 → 7.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -5
- package/dist/gobj-ui.cjs.js +319 -14
- package/dist/gobj-ui.es.js +319 -14
- package/package.json +1 -1
- package/src/c_yui_json.css +22 -16
- package/src/c_yui_json.js +13 -4
- package/src/c_yui_json_graph.js +427 -9
package/README.md
CHANGED
|
@@ -439,6 +439,29 @@ the order of arrival: the viewer opens on the **tree** (`view_mode` default).
|
|
|
439
439
|
| `tree` | *where is this value, and what is around it* | the lazy view; the only one that can drill; the default |
|
|
440
440
|
| `graph` | *what shape is this* | a hosted `C_YUI_JSON_GRAPH` child (AntV/G6) |
|
|
441
441
|
|
|
442
|
+
The graph carries **its own** toolbar, and since `7.22.0` that toolbar has the
|
|
443
|
+
same two facilities the tree has: a **find box** (rate-limited, `EV_FIND_NODES`)
|
|
444
|
+
that highlights matching rows and outlines the cards they are in and says how
|
|
445
|
+
many matched, and **expand-all / collapse-all** (`EV_EXPAND_ALL` /
|
|
446
|
+
`EV_COLLAPSE_ALL`) that fold every card but the root, marking each cut with
|
|
447
|
+
`▸ N` so the shape stays legible and you can see where the rest went. Since
|
|
448
|
+
`7.23.0` each card that HAS a branch also carries its own handle in its header
|
|
449
|
+
(`▾` open, `▸ N` folded, `EV_TOGGLE_FOLD {path}`); a leaf gets a spacer instead,
|
|
450
|
+
because a handle that does nothing is worse than none.
|
|
451
|
+
|
|
452
|
+
The handle is delegated from the canvas mount in the **capture** phase, over
|
|
453
|
+
`pointerdown`/`pointerup`/`mousedown`/`click`: the card is an `innerHTML`
|
|
454
|
+
string so nothing can be bound to it directly, G6 binds on the node element
|
|
455
|
+
below, and G6 builds its click from the POINTER sequence — swallowing `click`
|
|
456
|
+
alone leaves the fold working and the card also reporting an item click. A find
|
|
457
|
+
does not move the camera — a viewport that jumps on every keystroke is
|
|
458
|
+
unusable — which is why the count is there.
|
|
459
|
+
|
|
460
|
+
Note **where the highlight lives**: baked into the card's own markup, never set
|
|
461
|
+
as a G6 node state. The key shape of an `html` node is a DOM element and G6
|
|
462
|
+
paints no state style on it, so setting `active` selects correctly and shows
|
|
463
|
+
nothing.
|
|
464
|
+
|
|
442
465
|
- **Neither text nor graph is lazy.** Both show what the client currently
|
|
443
466
|
holds, `__collapsed__` sentinels included, because that is honestly what it
|
|
444
467
|
has. Drill in the tree and they grow with it.
|
|
@@ -477,11 +500,13 @@ Two layout facts the browser taught this component, both worth keeping:
|
|
|
477
500
|
`"text"` / `"graph"`; **no mode advances** to the next view, which is what
|
|
478
501
|
the two-view toggle did when the list was two long), plus `EV_REFRESH` /
|
|
479
502
|
`EV_SHOW` / `EV_HIDE` / `EV_LANGUAGE_CHANGED`.
|
|
480
|
-
- Output
|
|
481
|
-
must declare it in its own FSM (CHILD subscription model)
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
503
|
+
- Output event: `EV_EXPAND_PATH {path, size}` (`EVF_OUTPUT_EVENT`) — the parent
|
|
504
|
+
must declare it in its own FSM (CHILD subscription model). That is the ONLY
|
|
505
|
+
one, on purpose: this viewer is a child of its host and subscribes it to
|
|
506
|
+
everything it publishes, so every output event is a mandatory declaration in
|
|
507
|
+
every host's FSM. The graph child's `EV_JSON_ITEM_CLICKED` stops here
|
|
508
|
+
(`7.21.0` forwarded it and broke exactly that way); a host that wants node
|
|
509
|
+
clicks mounts `C_YUI_JSON_GRAPH` itself.
|
|
485
510
|
- Internal (DOM → FSM): `EV_TOGGLE_NODE`, `EV_EXPAND_COLLAPSED`, `EV_SEARCH`,
|
|
486
511
|
`EV_EXPAND_ALL`, `EV_COLLAPSE_ALL`, `EV_COPY_ALL`. Every kw carries only a
|
|
487
512
|
`path` string — never a DOM node or gobj.
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -9473,6 +9473,12 @@ function ensure_drag_canvas_patch() {
|
|
|
9473
9473
|
* Constants
|
|
9474
9474
|
***************************************************************/
|
|
9475
9475
|
var GCLASS_NAME$11 = "C_YUI_JSON_GRAPH";
|
|
9476
|
+
var FOLD_SWALLOWED_EVENTS = [
|
|
9477
|
+
"pointerdown",
|
|
9478
|
+
"pointerup",
|
|
9479
|
+
"mousedown",
|
|
9480
|
+
"click"
|
|
9481
|
+
];
|
|
9476
9482
|
/***************************************************************
|
|
9477
9483
|
* Data
|
|
9478
9484
|
***************************************************************/
|
|
@@ -9492,7 +9498,15 @@ var PRIVATE_DATA$11 = {
|
|
|
9492
9498
|
theme: null,
|
|
9493
9499
|
theme_observer: null,
|
|
9494
9500
|
resize_observer: null,
|
|
9495
|
-
_resize_raf: 0
|
|
9501
|
+
_resize_raf: 0,
|
|
9502
|
+
search: "",
|
|
9503
|
+
collapsed: null,
|
|
9504
|
+
match_count: 0,
|
|
9505
|
+
find_timer: null,
|
|
9506
|
+
fold_listener: null,
|
|
9507
|
+
$find_input: null,
|
|
9508
|
+
$find_result: null,
|
|
9509
|
+
$find_count: null
|
|
9496
9510
|
};
|
|
9497
9511
|
var __gclass__$11 = null;
|
|
9498
9512
|
var __layout_registered__$1 = false;
|
|
@@ -9561,6 +9575,8 @@ function type_color(type, dark) {
|
|
|
9561
9575
|
***************************************************************/
|
|
9562
9576
|
function mt_create$11(gobj) {
|
|
9563
9577
|
let priv = gobj.priv;
|
|
9578
|
+
priv.collapsed = /* @__PURE__ */ new Set();
|
|
9579
|
+
priv.search = "";
|
|
9564
9580
|
priv.canvas_id = "json-canvas-" + (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj));
|
|
9565
9581
|
(0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "canvas_id", priv.canvas_id);
|
|
9566
9582
|
let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
|
|
@@ -9590,6 +9606,10 @@ function mt_stop$11(gobj) {}
|
|
|
9590
9606
|
* Framework Method: Destroy
|
|
9591
9607
|
***************************************************************/
|
|
9592
9608
|
function mt_destroy$11(gobj) {
|
|
9609
|
+
if (gobj.priv.find_timer) {
|
|
9610
|
+
clearTimeout(gobj.priv.find_timer);
|
|
9611
|
+
gobj.priv.find_timer = null;
|
|
9612
|
+
}
|
|
9593
9613
|
let priv = gobj.priv;
|
|
9594
9614
|
if (priv.theme_observer) {
|
|
9595
9615
|
priv.theme_observer.disconnect();
|
|
@@ -9646,25 +9666,118 @@ function build_ui$10(gobj) {
|
|
|
9646
9666
|
]]
|
|
9647
9667
|
]);
|
|
9648
9668
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $container);
|
|
9669
|
+
let $mount = $container.querySelector("#" + priv.canvas_id);
|
|
9670
|
+
if ($mount) {
|
|
9671
|
+
priv.fold_listener = function(evt) {
|
|
9672
|
+
let $handle = evt.target && evt.target.closest ? evt.target.closest("[data-fold-path]") : null;
|
|
9673
|
+
if (!$handle) return;
|
|
9674
|
+
evt.stopPropagation();
|
|
9675
|
+
evt.preventDefault();
|
|
9676
|
+
if (evt.type !== "click") return;
|
|
9677
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_FOLD", { path: $handle.getAttribute("data-fold-path") }, gobj);
|
|
9678
|
+
};
|
|
9679
|
+
for (let type of FOLD_SWALLOWED_EVENTS) $mount.addEventListener(type, priv.fold_listener, true);
|
|
9680
|
+
} else (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no canvas mount, fold handles are dead`);
|
|
9681
|
+
priv.$find_input = $container.querySelector(".JSON_GRAPH_FIND_INPUT");
|
|
9682
|
+
priv.$find_result = $container.querySelector(".JSON_GRAPH_FIND_RESULT");
|
|
9683
|
+
priv.$find_count = $container.querySelector(".JSON_GRAPH_FIND_COUNT");
|
|
9649
9684
|
(0, _yuneta_gobj_js.refresh_language)($container, i18next.t);
|
|
9650
9685
|
}
|
|
9651
9686
|
/************************************************************
|
|
9652
9687
|
* Destroy UI
|
|
9653
9688
|
************************************************************/
|
|
9654
9689
|
function destroy_ui$6(gobj) {
|
|
9690
|
+
let priv = gobj.priv;
|
|
9655
9691
|
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
9692
|
+
if (priv.fold_listener) {
|
|
9693
|
+
let $mount = $container ? $container.querySelector("#" + priv.canvas_id) : null;
|
|
9694
|
+
if ($mount) for (let type of FOLD_SWALLOWED_EVENTS) $mount.removeEventListener(type, priv.fold_listener, true);
|
|
9695
|
+
priv.fold_listener = null;
|
|
9696
|
+
}
|
|
9656
9697
|
if ($container) {
|
|
9657
9698
|
if ($container.parentNode) $container.parentNode.removeChild($container);
|
|
9658
9699
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", null);
|
|
9659
9700
|
}
|
|
9660
9701
|
}
|
|
9661
9702
|
/************************************************************
|
|
9703
|
+
* One fold button. `open` rotates the chevron to the open
|
|
9704
|
+
* state, mirroring the per-node toggle of the lazy tree.
|
|
9705
|
+
************************************************************/
|
|
9706
|
+
function graph_fold_button(gobj, event_name, label_key, open) {
|
|
9707
|
+
let icon_style = "font-size:1.5em; color:inherit;" + (open ? " transform: rotate(90deg);" : "");
|
|
9708
|
+
return [
|
|
9709
|
+
"button",
|
|
9710
|
+
{
|
|
9711
|
+
class: `button ${event_name}`,
|
|
9712
|
+
type: "button",
|
|
9713
|
+
style: {
|
|
9714
|
+
height: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "wide"),
|
|
9715
|
+
width: "2.5em"
|
|
9716
|
+
},
|
|
9717
|
+
title: (0, i18next.t)(label_key),
|
|
9718
|
+
"data-i18n-title": label_key,
|
|
9719
|
+
"aria-label": (0, i18next.t)(label_key),
|
|
9720
|
+
"data-i18n-aria-label": label_key
|
|
9721
|
+
},
|
|
9722
|
+
["i", {
|
|
9723
|
+
style: icon_style,
|
|
9724
|
+
class: "yi-chevron-right"
|
|
9725
|
+
}],
|
|
9726
|
+
{ click: (evt) => {
|
|
9727
|
+
evt.stopPropagation();
|
|
9728
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, event_name, {}, gobj);
|
|
9729
|
+
} }
|
|
9730
|
+
];
|
|
9731
|
+
}
|
|
9732
|
+
/************************************************************
|
|
9662
9733
|
* Toolbar
|
|
9663
9734
|
************************************************************/
|
|
9664
9735
|
function make_toolbar$3(gobj) {
|
|
9665
|
-
gobj.priv;
|
|
9736
|
+
let priv = gobj.priv;
|
|
9666
9737
|
let toolbar_wide = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "wide");
|
|
9667
|
-
let left_items = [
|
|
9738
|
+
let left_items = [[
|
|
9739
|
+
"div",
|
|
9740
|
+
{
|
|
9741
|
+
class: "JSON_GRAPH_FIND control has-icons-left",
|
|
9742
|
+
style: "margin-right:.5rem; max-width:12rem; min-width:7rem;"
|
|
9743
|
+
},
|
|
9744
|
+
[["input", {
|
|
9745
|
+
class: "JSON_GRAPH_FIND_INPUT input",
|
|
9746
|
+
type: "text",
|
|
9747
|
+
placeholder: (0, i18next.t)("search"),
|
|
9748
|
+
"data-i18n-placeholder": "search",
|
|
9749
|
+
"aria-label": (0, i18next.t)("search"),
|
|
9750
|
+
"data-i18n-aria-label": "search"
|
|
9751
|
+
}], [
|
|
9752
|
+
"span",
|
|
9753
|
+
{ class: "icon is-left" },
|
|
9754
|
+
[["i", { class: "yi-magnifying-glass" }]]
|
|
9755
|
+
]],
|
|
9756
|
+
{ input: (evt) => {
|
|
9757
|
+
evt.stopPropagation();
|
|
9758
|
+
let text = evt.target.value.trim();
|
|
9759
|
+
if (priv.find_timer) clearTimeout(priv.find_timer);
|
|
9760
|
+
priv.find_timer = setTimeout(function() {
|
|
9761
|
+
priv.find_timer = null;
|
|
9762
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_FIND_NODES", { text }, gobj);
|
|
9763
|
+
}, 250);
|
|
9764
|
+
} }
|
|
9765
|
+
], [
|
|
9766
|
+
"div",
|
|
9767
|
+
{
|
|
9768
|
+
class: "JSON_GRAPH_FIND_RESULT is-hidden",
|
|
9769
|
+
style: "display:flex; align-items:center; gap:.3rem; margin-right:.5rem; font-size:.85rem;"
|
|
9770
|
+
},
|
|
9771
|
+
[[
|
|
9772
|
+
"span",
|
|
9773
|
+
{ class: "JSON_GRAPH_FIND_COUNT" },
|
|
9774
|
+
""
|
|
9775
|
+
], [
|
|
9776
|
+
"span",
|
|
9777
|
+
{ i18n: "matches" },
|
|
9778
|
+
"matches"
|
|
9779
|
+
]]
|
|
9780
|
+
]];
|
|
9668
9781
|
let center_items = [];
|
|
9669
9782
|
for (let item of [
|
|
9670
9783
|
[
|
|
@@ -9722,6 +9835,7 @@ function make_toolbar$3(gobj) {
|
|
|
9722
9835
|
} }
|
|
9723
9836
|
]);
|
|
9724
9837
|
}
|
|
9838
|
+
let right_items = [graph_fold_button(gobj, "EV_EXPAND_ALL", "expand all", true), graph_fold_button(gobj, "EV_COLLAPSE_ALL", "collapse all", false)];
|
|
9725
9839
|
const $toolbar = yui_toolbar({}, [
|
|
9726
9840
|
[
|
|
9727
9841
|
"div",
|
|
@@ -9736,7 +9850,7 @@ function make_toolbar$3(gobj) {
|
|
|
9736
9850
|
[
|
|
9737
9851
|
"div",
|
|
9738
9852
|
{ class: "yui-horizontal-toolbar-section right" },
|
|
9739
|
-
|
|
9853
|
+
right_items
|
|
9740
9854
|
]
|
|
9741
9855
|
]);
|
|
9742
9856
|
(0, _yuneta_gobj_js.refresh_language)($toolbar, i18next.t);
|
|
@@ -9787,7 +9901,7 @@ function build_graph$3(gobj) {
|
|
|
9787
9901
|
/************************************************************
|
|
9788
9902
|
* Build cell value HTML (matching old mx_json_viewer colors)
|
|
9789
9903
|
************************************************************/
|
|
9790
|
-
function build_cell_html(key, value, type, dark) {
|
|
9904
|
+
function build_cell_html(key, value, type, dark, matched) {
|
|
9791
9905
|
let color = type_color(type, dark);
|
|
9792
9906
|
let key_color = json_card_style(GROUP_COLORS.dict, dark).key;
|
|
9793
9907
|
let display_value = "";
|
|
@@ -9812,7 +9926,48 @@ function build_cell_html(key, value, type, dark) {
|
|
|
9812
9926
|
break;
|
|
9813
9927
|
default: display_value = String(value);
|
|
9814
9928
|
}
|
|
9815
|
-
|
|
9929
|
+
let row = `<span style="color:${key_color}">• ${(0, _yuneta_gobj_js.escapeHtml)(String(key))}: </span><span style="color:${color}">${display_value}</span>`;
|
|
9930
|
+
if (matched) row = `<span style="background:${dark ? "#7a5d00" : "#ffe082"}; border-radius:2px; padding:0 2px;">${row}</span>`;
|
|
9931
|
+
return row;
|
|
9932
|
+
}
|
|
9933
|
+
/************************************************************
|
|
9934
|
+
* The per-node fold handle, drawn in the card's header.
|
|
9935
|
+
*
|
|
9936
|
+
* Only a card with a BRANCH gets one — a leaf has nothing to
|
|
9937
|
+
* fold, and a handle that does nothing is worse than none.
|
|
9938
|
+
* It carries the path in `data-fold-path`, which is what the
|
|
9939
|
+
* delegated listener in build_ui reads; the card is an
|
|
9940
|
+
* innerHTML string, so there is no other way to hang a
|
|
9941
|
+
* handler on it.
|
|
9942
|
+
*
|
|
9943
|
+
* Folded it shows the glyph AND the count of branches hidden
|
|
9944
|
+
* under it; open, just the glyph. No word, no title: a word
|
|
9945
|
+
* inside this string could carry no i18n key.
|
|
9946
|
+
************************************************************/
|
|
9947
|
+
function fold_toggle_html(path, foldable, folded, branches) {
|
|
9948
|
+
if (!foldable) return `<span style="width:1.1em; flex:0 0 auto;"></span>`;
|
|
9949
|
+
let glyph = folded ? "▸" : "▾";
|
|
9950
|
+
let text = folded ? `${glyph} ${branches}` : glyph;
|
|
9951
|
+
return `<span data-fold-path="${(0, _yuneta_gobj_js.escapeHtml)(path)}" style="flex:0 0 auto; cursor:pointer; user-select:none; padding:0 .2em; min-width:1.1em; text-align:left;">${text}</span>`;
|
|
9952
|
+
}
|
|
9953
|
+
/************************************************************
|
|
9954
|
+
* Does this key/value pair match the find term?
|
|
9955
|
+
*
|
|
9956
|
+
* Scalars are matched on what the card SHOWS. A complex value
|
|
9957
|
+
* is not: its contents are a card of their own and match there,
|
|
9958
|
+
* so matching the `{3}` summary too would light up every
|
|
9959
|
+
* ancestor of every hit.
|
|
9960
|
+
************************************************************/
|
|
9961
|
+
function cell_matches(key, value, type, needle) {
|
|
9962
|
+
if ((0, _yuneta_gobj_js.empty_string)(needle)) return false;
|
|
9963
|
+
if (String(key).toLowerCase().includes(needle)) return true;
|
|
9964
|
+
switch (type) {
|
|
9965
|
+
case "string":
|
|
9966
|
+
case "number":
|
|
9967
|
+
case "boolean": return String(value).toLowerCase().includes(needle);
|
|
9968
|
+
case "null": return "null".includes(needle);
|
|
9969
|
+
default: return false;
|
|
9970
|
+
}
|
|
9816
9971
|
}
|
|
9817
9972
|
/************************************************************
|
|
9818
9973
|
* Get JSON type string
|
|
@@ -9858,12 +10013,21 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
|
|
|
9858
10013
|
let is_dict = (0, _yuneta_gobj_js.is_object)(kw);
|
|
9859
10014
|
let is_list = (0, _yuneta_gobj_js.is_array)(kw);
|
|
9860
10015
|
if (!is_dict && !is_list) return null;
|
|
10016
|
+
let priv = gobj.priv;
|
|
10017
|
+
let needle = priv.search || "";
|
|
9861
10018
|
let dark = yui_is_dark();
|
|
9862
10019
|
let lines = [];
|
|
9863
10020
|
let pending_complex = [];
|
|
10021
|
+
let card_matched = false;
|
|
9864
10022
|
let entries = is_dict ? Object.entries(kw) : kw.map((v, i) => [i, v]);
|
|
9865
10023
|
for (let [key, value] of entries) {
|
|
9866
|
-
let
|
|
10024
|
+
let type = get_json_type(value);
|
|
10025
|
+
let matched = cell_matches(key, value, type, needle);
|
|
10026
|
+
if (matched) {
|
|
10027
|
+
card_matched = true;
|
|
10028
|
+
priv.match_count++;
|
|
10029
|
+
}
|
|
10030
|
+
let html = build_cell_html(key, value, type, dark, matched);
|
|
9867
10031
|
lines.push(html);
|
|
9868
10032
|
if ((0, _yuneta_gobj_js.is_object)(value) && (0, _yuneta_gobj_js.json_object_size)(value) > 0 || (0, _yuneta_gobj_js.is_array)(value) && value.length > 0) pending_complex.push({
|
|
9869
10033
|
key,
|
|
@@ -9871,14 +10035,18 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
|
|
|
9871
10035
|
});
|
|
9872
10036
|
}
|
|
9873
10037
|
let label = get_group_label(path) || (is_dict ? "{}" : "[]");
|
|
10038
|
+
let foldable = pending_complex.length > 0;
|
|
10039
|
+
let folded = foldable && priv.collapsed.has(path);
|
|
9874
10040
|
let colors = json_card_style(is_dict ? GROUP_COLORS.dict : GROUP_COLORS.list, dark);
|
|
10041
|
+
let border_color = card_matched ? dark ? "#ffb300" : "#ff8f00" : colors.border;
|
|
10042
|
+
let border_width = card_matched ? 2 : 1;
|
|
9875
10043
|
let content_html = lines.map((line) => `<div style="font-family:monospace; font-size:13px; padding:1px 6px; white-space:nowrap;">${line}</div>`).join("");
|
|
9876
10044
|
let min_width = Math.max(180, label.length * 9);
|
|
9877
10045
|
let node_html = `
|
|
9878
10046
|
<div style="
|
|
9879
10047
|
min-width: ${min_width}px;
|
|
9880
10048
|
background: ${colors.bg};
|
|
9881
|
-
border:
|
|
10049
|
+
border: ${border_width}px ${is_dict ? "dashed" : "solid"} ${border_color};
|
|
9882
10050
|
border-radius: ${is_dict ? "6px" : "0"};
|
|
9883
10051
|
opacity: 0.9;
|
|
9884
10052
|
overflow: hidden;
|
|
@@ -9889,8 +10057,10 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
|
|
|
9889
10057
|
font-weight: bold;
|
|
9890
10058
|
font-size: 12px;
|
|
9891
10059
|
padding: 3px 8px;
|
|
9892
|
-
|
|
9893
|
-
|
|
10060
|
+
display: flex;
|
|
10061
|
+
align-items: center;
|
|
10062
|
+
gap: 6px;
|
|
10063
|
+
">${fold_toggle_html(path, foldable, folded, pending_complex.length)}<span style="flex:1 1 auto; text-align:center;">${(0, _yuneta_gobj_js.escapeHtml)(label)}</span></div>
|
|
9894
10064
|
${content_html}
|
|
9895
10065
|
</div>`;
|
|
9896
10066
|
let node = {
|
|
@@ -9916,6 +10086,7 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
|
|
|
9916
10086
|
lineWidth: 1
|
|
9917
10087
|
}
|
|
9918
10088
|
});
|
|
10089
|
+
if (folded) return group_id;
|
|
9919
10090
|
for (let pending of pending_complex) build_json_nodes(gobj, add_segment(path, pending.key), pending.value, nodes, edges, group_id);
|
|
9920
10091
|
return group_id;
|
|
9921
10092
|
}
|
|
@@ -9932,6 +10103,7 @@ function load_json(gobj) {
|
|
|
9932
10103
|
let path = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "path") || "`";
|
|
9933
10104
|
if ((0, _yuneta_gobj_js.empty_string)(path)) path = "`";
|
|
9934
10105
|
priv.node_counter = 0;
|
|
10106
|
+
priv.match_count = 0;
|
|
9935
10107
|
let nodes = [];
|
|
9936
10108
|
let edges = [];
|
|
9937
10109
|
build_json_nodes(gobj, path, json, nodes, edges, null);
|
|
@@ -9948,6 +10120,42 @@ function load_json(gobj) {
|
|
|
9948
10120
|
}
|
|
9949
10121
|
}
|
|
9950
10122
|
/************************************************************
|
|
10123
|
+
* Every path that HAS a branch to fold, in the same recursion
|
|
10124
|
+
* build_json_nodes uses — so "collapse all" can never name a
|
|
10125
|
+
* card that does not exist. The root is excluded: folding it
|
|
10126
|
+
* would leave an empty canvas.
|
|
10127
|
+
************************************************************/
|
|
10128
|
+
function collect_foldable_paths(value, path, out, is_root) {
|
|
10129
|
+
let is_dict = (0, _yuneta_gobj_js.is_object)(value);
|
|
10130
|
+
let is_list = (0, _yuneta_gobj_js.is_array)(value);
|
|
10131
|
+
if (!is_dict && !is_list) return false;
|
|
10132
|
+
let has_branch = false;
|
|
10133
|
+
let entries = is_dict ? Object.entries(value) : value.map((v, i) => [i, v]);
|
|
10134
|
+
for (let [key, child] of entries) if ((0, _yuneta_gobj_js.is_object)(child) && (0, _yuneta_gobj_js.json_object_size)(child) > 0 || (0, _yuneta_gobj_js.is_array)(child) && child.length > 0) {
|
|
10135
|
+
has_branch = true;
|
|
10136
|
+
collect_foldable_paths(child, add_segment(path, key), out, false);
|
|
10137
|
+
}
|
|
10138
|
+
if (has_branch && !is_root) out.push(path);
|
|
10139
|
+
return has_branch;
|
|
10140
|
+
}
|
|
10141
|
+
/************************************************************
|
|
10142
|
+
* Show the find count, or hide the chip when there is no
|
|
10143
|
+
* term. Zero is a RESULT and must show: a graph that did not
|
|
10144
|
+
* move looks the same whether nothing matched or the match
|
|
10145
|
+
* was already on screen.
|
|
10146
|
+
************************************************************/
|
|
10147
|
+
function update_find_result(gobj) {
|
|
10148
|
+
let priv = gobj.priv;
|
|
10149
|
+
if (!priv.$find_result || !priv.$find_count) return;
|
|
10150
|
+
if ((0, _yuneta_gobj_js.empty_string)(priv.search)) {
|
|
10151
|
+
priv.$find_result.classList.add("is-hidden");
|
|
10152
|
+
priv.$find_count.textContent = "";
|
|
10153
|
+
return;
|
|
10154
|
+
}
|
|
10155
|
+
priv.$find_count.textContent = String(priv.match_count);
|
|
10156
|
+
priv.$find_result.classList.remove("is-hidden");
|
|
10157
|
+
}
|
|
10158
|
+
/************************************************************
|
|
9951
10159
|
* Clear and reload.
|
|
9952
10160
|
* {preserve_view: true}
|
|
9953
10161
|
* keep the current zoom and translate (no fitView) —
|
|
@@ -10069,6 +10277,70 @@ function ac_refresh$5(gobj, event, kw, src) {
|
|
|
10069
10277
|
}
|
|
10070
10278
|
/************************************************************
|
|
10071
10279
|
* {theme: "dark"|"light"} — the app switched theme.
|
|
10280
|
+
* EV_TOGGLE_FOLD { path } — fold or unfold ONE card.
|
|
10281
|
+
*
|
|
10282
|
+
* The camera does not move: folding one branch is a local
|
|
10283
|
+
* edit of the picture, and refitting the whole graph for it
|
|
10284
|
+
* would throw away where the reader was looking. The toolbar
|
|
10285
|
+
* pair, which changes everything at once, does refit.
|
|
10286
|
+
************************************************************/
|
|
10287
|
+
function ac_toggle_fold(gobj, event, kw, src) {
|
|
10288
|
+
let priv = gobj.priv;
|
|
10289
|
+
let path = kw && kw.path || "";
|
|
10290
|
+
if ((0, _yuneta_gobj_js.empty_string)(path)) {
|
|
10291
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: EV_TOGGLE_FOLD with no path`);
|
|
10292
|
+
return -1;
|
|
10293
|
+
}
|
|
10294
|
+
if (priv.collapsed.has(path)) priv.collapsed.delete(path);
|
|
10295
|
+
else priv.collapsed.add(path);
|
|
10296
|
+
refresh_json(gobj, { preserve_view: true });
|
|
10297
|
+
update_find_result(gobj);
|
|
10298
|
+
return 0;
|
|
10299
|
+
}
|
|
10300
|
+
/************************************************************
|
|
10301
|
+
* EV_FIND_NODES { text }
|
|
10302
|
+
*
|
|
10303
|
+
* The camera does NOT move: a find is a way of reading what is
|
|
10304
|
+
* already on screen, and a viewport that jumps on every
|
|
10305
|
+
* keystroke is unusable. The count says whether there is
|
|
10306
|
+
* anything to look for elsewhere.
|
|
10307
|
+
************************************************************/
|
|
10308
|
+
function ac_find_nodes$2(gobj, event, kw, src) {
|
|
10309
|
+
let priv = gobj.priv;
|
|
10310
|
+
priv.search = (kw && kw.text || "").trim().toLowerCase();
|
|
10311
|
+
refresh_json(gobj, { preserve_view: true });
|
|
10312
|
+
update_find_result(gobj);
|
|
10313
|
+
return 0;
|
|
10314
|
+
}
|
|
10315
|
+
/************************************************************
|
|
10316
|
+
* EV_EXPAND_ALL
|
|
10317
|
+
************************************************************/
|
|
10318
|
+
function ac_expand_all$2(gobj, event, kw, src) {
|
|
10319
|
+
let priv = gobj.priv;
|
|
10320
|
+
if (priv.collapsed.size === 0) return 0;
|
|
10321
|
+
priv.collapsed.clear();
|
|
10322
|
+
refresh_json(gobj);
|
|
10323
|
+
update_find_result(gobj);
|
|
10324
|
+
return 0;
|
|
10325
|
+
}
|
|
10326
|
+
/************************************************************
|
|
10327
|
+
* EV_COLLAPSE_ALL — fold every card but the root.
|
|
10328
|
+
************************************************************/
|
|
10329
|
+
function ac_collapse_all$2(gobj, event, kw, src) {
|
|
10330
|
+
let priv = gobj.priv;
|
|
10331
|
+
let json_data = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "json_data");
|
|
10332
|
+
if (!json_data) return 0;
|
|
10333
|
+
let paths = [];
|
|
10334
|
+
let path = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "path") || "`";
|
|
10335
|
+
if ((0, _yuneta_gobj_js.empty_string)(path)) path = "`";
|
|
10336
|
+
collect_foldable_paths(json_data, path, paths, true);
|
|
10337
|
+
priv.collapsed = new Set(paths);
|
|
10338
|
+
refresh_json(gobj);
|
|
10339
|
+
update_find_result(gobj);
|
|
10340
|
+
return 0;
|
|
10341
|
+
}
|
|
10342
|
+
/************************************************************
|
|
10343
|
+
*
|
|
10072
10344
|
************************************************************/
|
|
10073
10345
|
function ac_theme$3(gobj, event, kw, src) {
|
|
10074
10346
|
let priv = gobj.priv;
|
|
@@ -10209,6 +10481,26 @@ function create_gclass$11(gclass_name) {
|
|
|
10209
10481
|
ac_refresh$5,
|
|
10210
10482
|
null
|
|
10211
10483
|
],
|
|
10484
|
+
[
|
|
10485
|
+
"EV_FIND_NODES",
|
|
10486
|
+
ac_find_nodes$2,
|
|
10487
|
+
null
|
|
10488
|
+
],
|
|
10489
|
+
[
|
|
10490
|
+
"EV_TOGGLE_FOLD",
|
|
10491
|
+
ac_toggle_fold,
|
|
10492
|
+
null
|
|
10493
|
+
],
|
|
10494
|
+
[
|
|
10495
|
+
"EV_EXPAND_ALL",
|
|
10496
|
+
ac_expand_all$2,
|
|
10497
|
+
null
|
|
10498
|
+
],
|
|
10499
|
+
[
|
|
10500
|
+
"EV_COLLAPSE_ALL",
|
|
10501
|
+
ac_collapse_all$2,
|
|
10502
|
+
null
|
|
10503
|
+
],
|
|
10212
10504
|
[
|
|
10213
10505
|
"EV_THEME",
|
|
10214
10506
|
ac_theme$3,
|
|
@@ -10258,6 +10550,10 @@ function create_gclass$11(gclass_name) {
|
|
|
10258
10550
|
const event_types = [
|
|
10259
10551
|
["EV_LOAD_DATA", 0],
|
|
10260
10552
|
["EV_REFRESH", 0],
|
|
10553
|
+
["EV_FIND_NODES", 0],
|
|
10554
|
+
["EV_TOGGLE_FOLD", 0],
|
|
10555
|
+
["EV_EXPAND_ALL", 0],
|
|
10556
|
+
["EV_COLLAPSE_ALL", 0],
|
|
10261
10557
|
["EV_THEME", 0],
|
|
10262
10558
|
["EV_ZOOM_IN", 0],
|
|
10263
10559
|
["EV_ZOOM_OUT", 0],
|
|
@@ -11459,11 +11755,20 @@ function ac_set_view_mode(gobj, event, kw, src) {
|
|
|
11459
11755
|
/************************************************************
|
|
11460
11756
|
* EV_JSON_ITEM_CLICKED — from the hosted graph child.
|
|
11461
11757
|
*
|
|
11462
|
-
*
|
|
11463
|
-
*
|
|
11758
|
+
* It STOPS here, and that is deliberate. 7.21.0 republished
|
|
11759
|
+
* it "so the host has one contract", which got the framework
|
|
11760
|
+
* backwards: this viewer is a CHILD of its host and subscribes
|
|
11761
|
+
* it to everything it publishes, so a new output event is a
|
|
11762
|
+
* new MANDATORY declaration in every host's FSM. None of the
|
|
11763
|
+
* six that mount this viewer asked for node clicks, and every
|
|
11764
|
+
* one of them answered a click on a graph card with
|
|
11765
|
+
* "Event NOT DEFINED in state".
|
|
11766
|
+
*
|
|
11767
|
+
* A host that wants node clicks mounts C_YUI_JSON_GRAPH
|
|
11768
|
+
* itself — that gclass publishes them, and a host that mounts
|
|
11769
|
+
* it has declared them.
|
|
11464
11770
|
************************************************************/
|
|
11465
11771
|
function ac_json_item_clicked(gobj, event, kw, src) {
|
|
11466
|
-
(0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_JSON_ITEM_CLICKED", kw);
|
|
11467
11772
|
return 0;
|
|
11468
11773
|
}
|
|
11469
11774
|
/************************************************************
|
|
@@ -11636,7 +11941,7 @@ function create_gclass$10(gclass_name) {
|
|
|
11636
11941
|
["EV_COLLAPSE_ALL", 0],
|
|
11637
11942
|
["EV_COPY_ALL", 0],
|
|
11638
11943
|
["EV_SET_VIEW_MODE", 0],
|
|
11639
|
-
["EV_JSON_ITEM_CLICKED",
|
|
11944
|
+
["EV_JSON_ITEM_CLICKED", 0],
|
|
11640
11945
|
["EV_LANGUAGE_CHANGED", 0],
|
|
11641
11946
|
["EV_REFRESH", 0],
|
|
11642
11947
|
["EV_SHOW", 0],
|