@yuneta/gobj-ui 7.22.0 → 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 +17 -6
- package/dist/gobj-ui.cjs.js +91 -8
- package/dist/gobj-ui.es.js +91 -8
- package/package.json +1 -1
- package/src/c_yui_json.js +13 -4
- package/src/c_yui_json_graph.js +128 -10
package/README.md
CHANGED
|
@@ -444,7 +444,16 @@ same two facilities the tree has: a **find box** (rate-limited, `EV_FIND_NODES`)
|
|
|
444
444
|
that highlights matching rows and outlines the cards they are in and says how
|
|
445
445
|
many matched, and **expand-all / collapse-all** (`EV_EXPAND_ALL` /
|
|
446
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.
|
|
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
|
|
448
457
|
does not move the camera — a viewport that jumps on every keystroke is
|
|
449
458
|
unusable — which is why the count is there.
|
|
450
459
|
|
|
@@ -491,11 +500,13 @@ Two layout facts the browser taught this component, both worth keeping:
|
|
|
491
500
|
`"text"` / `"graph"`; **no mode advances** to the next view, which is what
|
|
492
501
|
the two-view toggle did when the list was two long), plus `EV_REFRESH` /
|
|
493
502
|
`EV_SHOW` / `EV_HIDE` / `EV_LANGUAGE_CHANGED`.
|
|
494
|
-
- Output
|
|
495
|
-
must declare it in its own FSM (CHILD subscription model)
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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.
|
|
499
510
|
- Internal (DOM → FSM): `EV_TOGGLE_NODE`, `EV_EXPAND_COLLAPSED`, `EV_SEARCH`,
|
|
500
511
|
`EV_EXPAND_ALL`, `EV_COLLAPSE_ALL`, `EV_COPY_ALL`. Every kw carries only a
|
|
501
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
|
***************************************************************/
|
|
@@ -9497,6 +9503,7 @@ var PRIVATE_DATA$11 = {
|
|
|
9497
9503
|
collapsed: null,
|
|
9498
9504
|
match_count: 0,
|
|
9499
9505
|
find_timer: null,
|
|
9506
|
+
fold_listener: null,
|
|
9500
9507
|
$find_input: null,
|
|
9501
9508
|
$find_result: null,
|
|
9502
9509
|
$find_count: null
|
|
@@ -9659,6 +9666,18 @@ function build_ui$10(gobj) {
|
|
|
9659
9666
|
]]
|
|
9660
9667
|
]);
|
|
9661
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`);
|
|
9662
9681
|
priv.$find_input = $container.querySelector(".JSON_GRAPH_FIND_INPUT");
|
|
9663
9682
|
priv.$find_result = $container.querySelector(".JSON_GRAPH_FIND_RESULT");
|
|
9664
9683
|
priv.$find_count = $container.querySelector(".JSON_GRAPH_FIND_COUNT");
|
|
@@ -9668,7 +9687,13 @@ function build_ui$10(gobj) {
|
|
|
9668
9687
|
* Destroy UI
|
|
9669
9688
|
************************************************************/
|
|
9670
9689
|
function destroy_ui$6(gobj) {
|
|
9690
|
+
let priv = gobj.priv;
|
|
9671
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
|
+
}
|
|
9672
9697
|
if ($container) {
|
|
9673
9698
|
if ($container.parentNode) $container.parentNode.removeChild($container);
|
|
9674
9699
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", null);
|
|
@@ -9906,6 +9931,26 @@ function build_cell_html(key, value, type, dark, matched) {
|
|
|
9906
9931
|
return row;
|
|
9907
9932
|
}
|
|
9908
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
|
+
/************************************************************
|
|
9909
9954
|
* Does this key/value pair match the find term?
|
|
9910
9955
|
*
|
|
9911
9956
|
* Scalars are matched on what the card SHOWS. A complex value
|
|
@@ -9990,8 +10035,8 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
|
|
|
9990
10035
|
});
|
|
9991
10036
|
}
|
|
9992
10037
|
let label = get_group_label(path) || (is_dict ? "{}" : "[]");
|
|
9993
|
-
let
|
|
9994
|
-
|
|
10038
|
+
let foldable = pending_complex.length > 0;
|
|
10039
|
+
let folded = foldable && priv.collapsed.has(path);
|
|
9995
10040
|
let colors = json_card_style(is_dict ? GROUP_COLORS.dict : GROUP_COLORS.list, dark);
|
|
9996
10041
|
let border_color = card_matched ? dark ? "#ffb300" : "#ff8f00" : colors.border;
|
|
9997
10042
|
let border_width = card_matched ? 2 : 1;
|
|
@@ -10012,8 +10057,10 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
|
|
|
10012
10057
|
font-weight: bold;
|
|
10013
10058
|
font-size: 12px;
|
|
10014
10059
|
padding: 3px 8px;
|
|
10015
|
-
|
|
10016
|
-
|
|
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>
|
|
10017
10064
|
${content_html}
|
|
10018
10065
|
</div>`;
|
|
10019
10066
|
let node = {
|
|
@@ -10230,6 +10277,27 @@ function ac_refresh$5(gobj, event, kw, src) {
|
|
|
10230
10277
|
}
|
|
10231
10278
|
/************************************************************
|
|
10232
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
|
+
/************************************************************
|
|
10233
10301
|
* EV_FIND_NODES { text }
|
|
10234
10302
|
*
|
|
10235
10303
|
* The camera does NOT move: a find is a way of reading what is
|
|
@@ -10418,6 +10486,11 @@ function create_gclass$11(gclass_name) {
|
|
|
10418
10486
|
ac_find_nodes$2,
|
|
10419
10487
|
null
|
|
10420
10488
|
],
|
|
10489
|
+
[
|
|
10490
|
+
"EV_TOGGLE_FOLD",
|
|
10491
|
+
ac_toggle_fold,
|
|
10492
|
+
null
|
|
10493
|
+
],
|
|
10421
10494
|
[
|
|
10422
10495
|
"EV_EXPAND_ALL",
|
|
10423
10496
|
ac_expand_all$2,
|
|
@@ -10478,6 +10551,7 @@ function create_gclass$11(gclass_name) {
|
|
|
10478
10551
|
["EV_LOAD_DATA", 0],
|
|
10479
10552
|
["EV_REFRESH", 0],
|
|
10480
10553
|
["EV_FIND_NODES", 0],
|
|
10554
|
+
["EV_TOGGLE_FOLD", 0],
|
|
10481
10555
|
["EV_EXPAND_ALL", 0],
|
|
10482
10556
|
["EV_COLLAPSE_ALL", 0],
|
|
10483
10557
|
["EV_THEME", 0],
|
|
@@ -11681,11 +11755,20 @@ function ac_set_view_mode(gobj, event, kw, src) {
|
|
|
11681
11755
|
/************************************************************
|
|
11682
11756
|
* EV_JSON_ITEM_CLICKED — from the hosted graph child.
|
|
11683
11757
|
*
|
|
11684
|
-
*
|
|
11685
|
-
*
|
|
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.
|
|
11686
11770
|
************************************************************/
|
|
11687
11771
|
function ac_json_item_clicked(gobj, event, kw, src) {
|
|
11688
|
-
(0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_JSON_ITEM_CLICKED", kw);
|
|
11689
11772
|
return 0;
|
|
11690
11773
|
}
|
|
11691
11774
|
/************************************************************
|
|
@@ -11858,7 +11941,7 @@ function create_gclass$10(gclass_name) {
|
|
|
11858
11941
|
["EV_COLLAPSE_ALL", 0],
|
|
11859
11942
|
["EV_COPY_ALL", 0],
|
|
11860
11943
|
["EV_SET_VIEW_MODE", 0],
|
|
11861
|
-
["EV_JSON_ITEM_CLICKED",
|
|
11944
|
+
["EV_JSON_ITEM_CLICKED", 0],
|
|
11862
11945
|
["EV_LANGUAGE_CHANGED", 0],
|
|
11863
11946
|
["EV_REFRESH", 0],
|
|
11864
11947
|
["EV_SHOW", 0],
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -9468,6 +9468,12 @@ function ensure_drag_canvas_patch() {
|
|
|
9468
9468
|
* Constants
|
|
9469
9469
|
***************************************************************/
|
|
9470
9470
|
var GCLASS_NAME$11 = "C_YUI_JSON_GRAPH";
|
|
9471
|
+
var FOLD_SWALLOWED_EVENTS = [
|
|
9472
|
+
"pointerdown",
|
|
9473
|
+
"pointerup",
|
|
9474
|
+
"mousedown",
|
|
9475
|
+
"click"
|
|
9476
|
+
];
|
|
9471
9477
|
/***************************************************************
|
|
9472
9478
|
* Data
|
|
9473
9479
|
***************************************************************/
|
|
@@ -9492,6 +9498,7 @@ var PRIVATE_DATA$11 = {
|
|
|
9492
9498
|
collapsed: null,
|
|
9493
9499
|
match_count: 0,
|
|
9494
9500
|
find_timer: null,
|
|
9501
|
+
fold_listener: null,
|
|
9495
9502
|
$find_input: null,
|
|
9496
9503
|
$find_result: null,
|
|
9497
9504
|
$find_count: null
|
|
@@ -9654,6 +9661,18 @@ function build_ui$10(gobj) {
|
|
|
9654
9661
|
]]
|
|
9655
9662
|
]);
|
|
9656
9663
|
gobj_write_attr(gobj, "$container", $container);
|
|
9664
|
+
let $mount = $container.querySelector("#" + priv.canvas_id);
|
|
9665
|
+
if ($mount) {
|
|
9666
|
+
priv.fold_listener = function(evt) {
|
|
9667
|
+
let $handle = evt.target && evt.target.closest ? evt.target.closest("[data-fold-path]") : null;
|
|
9668
|
+
if (!$handle) return;
|
|
9669
|
+
evt.stopPropagation();
|
|
9670
|
+
evt.preventDefault();
|
|
9671
|
+
if (evt.type !== "click") return;
|
|
9672
|
+
gobj_send_event(gobj, "EV_TOGGLE_FOLD", { path: $handle.getAttribute("data-fold-path") }, gobj);
|
|
9673
|
+
};
|
|
9674
|
+
for (let type of FOLD_SWALLOWED_EVENTS) $mount.addEventListener(type, priv.fold_listener, true);
|
|
9675
|
+
} else log_error(`${gobj_short_name(gobj)}: no canvas mount, fold handles are dead`);
|
|
9657
9676
|
priv.$find_input = $container.querySelector(".JSON_GRAPH_FIND_INPUT");
|
|
9658
9677
|
priv.$find_result = $container.querySelector(".JSON_GRAPH_FIND_RESULT");
|
|
9659
9678
|
priv.$find_count = $container.querySelector(".JSON_GRAPH_FIND_COUNT");
|
|
@@ -9663,7 +9682,13 @@ function build_ui$10(gobj) {
|
|
|
9663
9682
|
* Destroy UI
|
|
9664
9683
|
************************************************************/
|
|
9665
9684
|
function destroy_ui$6(gobj) {
|
|
9685
|
+
let priv = gobj.priv;
|
|
9666
9686
|
let $container = gobj_read_attr(gobj, "$container");
|
|
9687
|
+
if (priv.fold_listener) {
|
|
9688
|
+
let $mount = $container ? $container.querySelector("#" + priv.canvas_id) : null;
|
|
9689
|
+
if ($mount) for (let type of FOLD_SWALLOWED_EVENTS) $mount.removeEventListener(type, priv.fold_listener, true);
|
|
9690
|
+
priv.fold_listener = null;
|
|
9691
|
+
}
|
|
9667
9692
|
if ($container) {
|
|
9668
9693
|
if ($container.parentNode) $container.parentNode.removeChild($container);
|
|
9669
9694
|
gobj_write_attr(gobj, "$container", null);
|
|
@@ -9901,6 +9926,26 @@ function build_cell_html(key, value, type, dark, matched) {
|
|
|
9901
9926
|
return row;
|
|
9902
9927
|
}
|
|
9903
9928
|
/************************************************************
|
|
9929
|
+
* The per-node fold handle, drawn in the card's header.
|
|
9930
|
+
*
|
|
9931
|
+
* Only a card with a BRANCH gets one — a leaf has nothing to
|
|
9932
|
+
* fold, and a handle that does nothing is worse than none.
|
|
9933
|
+
* It carries the path in `data-fold-path`, which is what the
|
|
9934
|
+
* delegated listener in build_ui reads; the card is an
|
|
9935
|
+
* innerHTML string, so there is no other way to hang a
|
|
9936
|
+
* handler on it.
|
|
9937
|
+
*
|
|
9938
|
+
* Folded it shows the glyph AND the count of branches hidden
|
|
9939
|
+
* under it; open, just the glyph. No word, no title: a word
|
|
9940
|
+
* inside this string could carry no i18n key.
|
|
9941
|
+
************************************************************/
|
|
9942
|
+
function fold_toggle_html(path, foldable, folded, branches) {
|
|
9943
|
+
if (!foldable) return `<span style="width:1.1em; flex:0 0 auto;"></span>`;
|
|
9944
|
+
let glyph = folded ? "▸" : "▾";
|
|
9945
|
+
let text = folded ? `${glyph} ${branches}` : glyph;
|
|
9946
|
+
return `<span data-fold-path="${escapeHtml(path)}" style="flex:0 0 auto; cursor:pointer; user-select:none; padding:0 .2em; min-width:1.1em; text-align:left;">${text}</span>`;
|
|
9947
|
+
}
|
|
9948
|
+
/************************************************************
|
|
9904
9949
|
* Does this key/value pair match the find term?
|
|
9905
9950
|
*
|
|
9906
9951
|
* Scalars are matched on what the card SHOWS. A complex value
|
|
@@ -9985,8 +10030,8 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
|
|
|
9985
10030
|
});
|
|
9986
10031
|
}
|
|
9987
10032
|
let label = get_group_label(path) || (is_dict ? "{}" : "[]");
|
|
9988
|
-
let
|
|
9989
|
-
|
|
10033
|
+
let foldable = pending_complex.length > 0;
|
|
10034
|
+
let folded = foldable && priv.collapsed.has(path);
|
|
9990
10035
|
let colors = json_card_style(is_dict ? GROUP_COLORS.dict : GROUP_COLORS.list, dark);
|
|
9991
10036
|
let border_color = card_matched ? dark ? "#ffb300" : "#ff8f00" : colors.border;
|
|
9992
10037
|
let border_width = card_matched ? 2 : 1;
|
|
@@ -10007,8 +10052,10 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
|
|
|
10007
10052
|
font-weight: bold;
|
|
10008
10053
|
font-size: 12px;
|
|
10009
10054
|
padding: 3px 8px;
|
|
10010
|
-
|
|
10011
|
-
|
|
10055
|
+
display: flex;
|
|
10056
|
+
align-items: center;
|
|
10057
|
+
gap: 6px;
|
|
10058
|
+
">${fold_toggle_html(path, foldable, folded, pending_complex.length)}<span style="flex:1 1 auto; text-align:center;">${escapeHtml(label)}</span></div>
|
|
10012
10059
|
${content_html}
|
|
10013
10060
|
</div>`;
|
|
10014
10061
|
let node = {
|
|
@@ -10225,6 +10272,27 @@ function ac_refresh$5(gobj, event, kw, src) {
|
|
|
10225
10272
|
}
|
|
10226
10273
|
/************************************************************
|
|
10227
10274
|
* {theme: "dark"|"light"} — the app switched theme.
|
|
10275
|
+
* EV_TOGGLE_FOLD { path } — fold or unfold ONE card.
|
|
10276
|
+
*
|
|
10277
|
+
* The camera does not move: folding one branch is a local
|
|
10278
|
+
* edit of the picture, and refitting the whole graph for it
|
|
10279
|
+
* would throw away where the reader was looking. The toolbar
|
|
10280
|
+
* pair, which changes everything at once, does refit.
|
|
10281
|
+
************************************************************/
|
|
10282
|
+
function ac_toggle_fold(gobj, event, kw, src) {
|
|
10283
|
+
let priv = gobj.priv;
|
|
10284
|
+
let path = kw && kw.path || "";
|
|
10285
|
+
if (empty_string(path)) {
|
|
10286
|
+
log_error(`${gobj_short_name(gobj)}: EV_TOGGLE_FOLD with no path`);
|
|
10287
|
+
return -1;
|
|
10288
|
+
}
|
|
10289
|
+
if (priv.collapsed.has(path)) priv.collapsed.delete(path);
|
|
10290
|
+
else priv.collapsed.add(path);
|
|
10291
|
+
refresh_json(gobj, { preserve_view: true });
|
|
10292
|
+
update_find_result(gobj);
|
|
10293
|
+
return 0;
|
|
10294
|
+
}
|
|
10295
|
+
/************************************************************
|
|
10228
10296
|
* EV_FIND_NODES { text }
|
|
10229
10297
|
*
|
|
10230
10298
|
* The camera does NOT move: a find is a way of reading what is
|
|
@@ -10413,6 +10481,11 @@ function create_gclass$11(gclass_name) {
|
|
|
10413
10481
|
ac_find_nodes$2,
|
|
10414
10482
|
null
|
|
10415
10483
|
],
|
|
10484
|
+
[
|
|
10485
|
+
"EV_TOGGLE_FOLD",
|
|
10486
|
+
ac_toggle_fold,
|
|
10487
|
+
null
|
|
10488
|
+
],
|
|
10416
10489
|
[
|
|
10417
10490
|
"EV_EXPAND_ALL",
|
|
10418
10491
|
ac_expand_all$2,
|
|
@@ -10473,6 +10546,7 @@ function create_gclass$11(gclass_name) {
|
|
|
10473
10546
|
["EV_LOAD_DATA", 0],
|
|
10474
10547
|
["EV_REFRESH", 0],
|
|
10475
10548
|
["EV_FIND_NODES", 0],
|
|
10549
|
+
["EV_TOGGLE_FOLD", 0],
|
|
10476
10550
|
["EV_EXPAND_ALL", 0],
|
|
10477
10551
|
["EV_COLLAPSE_ALL", 0],
|
|
10478
10552
|
["EV_THEME", 0],
|
|
@@ -11676,11 +11750,20 @@ function ac_set_view_mode(gobj, event, kw, src) {
|
|
|
11676
11750
|
/************************************************************
|
|
11677
11751
|
* EV_JSON_ITEM_CLICKED — from the hosted graph child.
|
|
11678
11752
|
*
|
|
11679
|
-
*
|
|
11680
|
-
*
|
|
11753
|
+
* It STOPS here, and that is deliberate. 7.21.0 republished
|
|
11754
|
+
* it "so the host has one contract", which got the framework
|
|
11755
|
+
* backwards: this viewer is a CHILD of its host and subscribes
|
|
11756
|
+
* it to everything it publishes, so a new output event is a
|
|
11757
|
+
* new MANDATORY declaration in every host's FSM. None of the
|
|
11758
|
+
* six that mount this viewer asked for node clicks, and every
|
|
11759
|
+
* one of them answered a click on a graph card with
|
|
11760
|
+
* "Event NOT DEFINED in state".
|
|
11761
|
+
*
|
|
11762
|
+
* A host that wants node clicks mounts C_YUI_JSON_GRAPH
|
|
11763
|
+
* itself — that gclass publishes them, and a host that mounts
|
|
11764
|
+
* it has declared them.
|
|
11681
11765
|
************************************************************/
|
|
11682
11766
|
function ac_json_item_clicked(gobj, event, kw, src) {
|
|
11683
|
-
gobj_publish_event(gobj, "EV_JSON_ITEM_CLICKED", kw);
|
|
11684
11767
|
return 0;
|
|
11685
11768
|
}
|
|
11686
11769
|
/************************************************************
|
|
@@ -11853,7 +11936,7 @@ function create_gclass$10(gclass_name) {
|
|
|
11853
11936
|
["EV_COLLAPSE_ALL", 0],
|
|
11854
11937
|
["EV_COPY_ALL", 0],
|
|
11855
11938
|
["EV_SET_VIEW_MODE", 0],
|
|
11856
|
-
["EV_JSON_ITEM_CLICKED",
|
|
11939
|
+
["EV_JSON_ITEM_CLICKED", 0],
|
|
11857
11940
|
["EV_LANGUAGE_CHANGED", 0],
|
|
11858
11941
|
["EV_REFRESH", 0],
|
|
11859
11942
|
["EV_SHOW", 0],
|
package/package.json
CHANGED
package/src/c_yui_json.js
CHANGED
|
@@ -1175,12 +1175,21 @@ function ac_set_view_mode(gobj, event, kw, src)
|
|
|
1175
1175
|
/************************************************************
|
|
1176
1176
|
* EV_JSON_ITEM_CLICKED — from the hosted graph child.
|
|
1177
1177
|
*
|
|
1178
|
-
*
|
|
1179
|
-
*
|
|
1178
|
+
* It STOPS here, and that is deliberate. 7.21.0 republished
|
|
1179
|
+
* it "so the host has one contract", which got the framework
|
|
1180
|
+
* backwards: this viewer is a CHILD of its host and subscribes
|
|
1181
|
+
* it to everything it publishes, so a new output event is a
|
|
1182
|
+
* new MANDATORY declaration in every host's FSM. None of the
|
|
1183
|
+
* six that mount this viewer asked for node clicks, and every
|
|
1184
|
+
* one of them answered a click on a graph card with
|
|
1185
|
+
* "Event NOT DEFINED in state".
|
|
1186
|
+
*
|
|
1187
|
+
* A host that wants node clicks mounts C_YUI_JSON_GRAPH
|
|
1188
|
+
* itself — that gclass publishes them, and a host that mounts
|
|
1189
|
+
* it has declared them.
|
|
1180
1190
|
************************************************************/
|
|
1181
1191
|
function ac_json_item_clicked(gobj, event, kw, src)
|
|
1182
1192
|
{
|
|
1183
|
-
gobj_publish_event(gobj, "EV_JSON_ITEM_CLICKED", kw);
|
|
1184
1193
|
return 0;
|
|
1185
1194
|
}
|
|
1186
1195
|
|
|
@@ -1307,7 +1316,7 @@ function create_gclass(gclass_name)
|
|
|
1307
1316
|
["EV_COLLAPSE_ALL", 0],
|
|
1308
1317
|
["EV_COPY_ALL", 0],
|
|
1309
1318
|
["EV_SET_VIEW_MODE", 0],
|
|
1310
|
-
["EV_JSON_ITEM_CLICKED",
|
|
1319
|
+
["EV_JSON_ITEM_CLICKED", 0],
|
|
1311
1320
|
["EV_LANGUAGE_CHANGED", 0],
|
|
1312
1321
|
["EV_REFRESH", 0],
|
|
1313
1322
|
["EV_SHOW", 0],
|
package/src/c_yui_json_graph.js
CHANGED
|
@@ -67,6 +67,13 @@ import { yui_is_dark, yui_theme_now, yui_watch_theme } from "./yui_theme.js";
|
|
|
67
67
|
***************************************************************/
|
|
68
68
|
const GCLASS_NAME = "C_YUI_JSON_GRAPH";
|
|
69
69
|
|
|
70
|
+
/*
|
|
71
|
+
* What a click on a fold handle has to swallow so G6 never turns it
|
|
72
|
+
* into a node click. `click` is the one that ACTS; the rest are cut
|
|
73
|
+
* because G6 assembles its own click from the pointer sequence.
|
|
74
|
+
*/
|
|
75
|
+
const FOLD_SWALLOWED_EVENTS = ["pointerdown", "pointerup", "mousedown", "click"];
|
|
76
|
+
|
|
70
77
|
/***************************************************************
|
|
71
78
|
* Data
|
|
72
79
|
***************************************************************/
|
|
@@ -102,6 +109,7 @@ let PRIVATE_DATA = {
|
|
|
102
109
|
collapsed: null, // Set<string> of paths whose children are hidden
|
|
103
110
|
match_count: 0, // matches of the last find, for the count chip
|
|
104
111
|
find_timer: null, // rate-limits the find box
|
|
112
|
+
fold_listener: null, // delegated click on the card fold handles
|
|
105
113
|
$find_input: null,
|
|
106
114
|
$find_result: null,
|
|
107
115
|
$find_count: null,
|
|
@@ -303,6 +311,46 @@ function build_ui(gobj)
|
|
|
303
311
|
|
|
304
312
|
gobj_write_attr(gobj, "$container", $container);
|
|
305
313
|
|
|
314
|
+
/*
|
|
315
|
+
* The fold handles live inside an `innerHTML` string, so no handler
|
|
316
|
+
* can be attached to them: the click is delegated from the canvas
|
|
317
|
+
* mount, in the CAPTURE phase, over FOUR event types.
|
|
318
|
+
*
|
|
319
|
+
* Capture because G6 binds on the html node ELEMENT, a descendant
|
|
320
|
+
* of this one: in the bubble phase G6 has already run by the time
|
|
321
|
+
* this listener sees the event, so stopping it there stops nothing.
|
|
322
|
+
*
|
|
323
|
+
* Four types because G6 does not use the DOM `click` at all — it
|
|
324
|
+
* builds its own from the POINTER sequence. Swallowing `click`
|
|
325
|
+
* alone left the fold working and the card ALSO reporting an item
|
|
326
|
+
* click; `pointerdown`/`pointerup` are the pair that has to be cut,
|
|
327
|
+
* and `mousedown` goes with them for a browser that still sends it.
|
|
328
|
+
* Measured by instrumenting the mount, not guessed.
|
|
329
|
+
*/
|
|
330
|
+
let $mount = $container.querySelector('#' + priv.canvas_id);
|
|
331
|
+
if($mount) {
|
|
332
|
+
priv.fold_listener = function(evt) {
|
|
333
|
+
let $handle = evt.target && evt.target.closest
|
|
334
|
+
? evt.target.closest('[data-fold-path]')
|
|
335
|
+
: null;
|
|
336
|
+
if(!$handle) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
evt.stopPropagation();
|
|
340
|
+
evt.preventDefault();
|
|
341
|
+
if(evt.type !== "click") {
|
|
342
|
+
return; /* swallowed so G6 never sees a node click */
|
|
343
|
+
}
|
|
344
|
+
gobj_send_event(gobj, "EV_TOGGLE_FOLD",
|
|
345
|
+
{path: $handle.getAttribute('data-fold-path')}, gobj);
|
|
346
|
+
};
|
|
347
|
+
for(let type of FOLD_SWALLOWED_EVENTS) {
|
|
348
|
+
$mount.addEventListener(type, priv.fold_listener, true);
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
log_error(`${gobj_short_name(gobj)}: no canvas mount, fold handles are dead`);
|
|
352
|
+
}
|
|
353
|
+
|
|
306
354
|
priv.$find_input = $container.querySelector('.JSON_GRAPH_FIND_INPUT');
|
|
307
355
|
priv.$find_result = $container.querySelector('.JSON_GRAPH_FIND_RESULT');
|
|
308
356
|
priv.$find_count = $container.querySelector('.JSON_GRAPH_FIND_COUNT');
|
|
@@ -315,7 +363,19 @@ function build_ui(gobj)
|
|
|
315
363
|
************************************************************/
|
|
316
364
|
function destroy_ui(gobj)
|
|
317
365
|
{
|
|
366
|
+
let priv = gobj.priv;
|
|
318
367
|
let $container = gobj_read_attr(gobj, "$container");
|
|
368
|
+
|
|
369
|
+
if(priv.fold_listener) {
|
|
370
|
+
let $mount = $container? $container.querySelector('#' + priv.canvas_id): null;
|
|
371
|
+
if($mount) {
|
|
372
|
+
for(let type of FOLD_SWALLOWED_EVENTS) {
|
|
373
|
+
$mount.removeEventListener(type, priv.fold_listener, true);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
priv.fold_listener = null;
|
|
377
|
+
}
|
|
378
|
+
|
|
319
379
|
if($container) {
|
|
320
380
|
if($container.parentNode) {
|
|
321
381
|
$container.parentNode.removeChild($container);
|
|
@@ -598,6 +658,36 @@ function build_cell_html(key, value, type, dark, matched)
|
|
|
598
658
|
return row;
|
|
599
659
|
}
|
|
600
660
|
|
|
661
|
+
/************************************************************
|
|
662
|
+
* The per-node fold handle, drawn in the card's header.
|
|
663
|
+
*
|
|
664
|
+
* Only a card with a BRANCH gets one — a leaf has nothing to
|
|
665
|
+
* fold, and a handle that does nothing is worse than none.
|
|
666
|
+
* It carries the path in `data-fold-path`, which is what the
|
|
667
|
+
* delegated listener in build_ui reads; the card is an
|
|
668
|
+
* innerHTML string, so there is no other way to hang a
|
|
669
|
+
* handler on it.
|
|
670
|
+
*
|
|
671
|
+
* Folded it shows the glyph AND the count of branches hidden
|
|
672
|
+
* under it; open, just the glyph. No word, no title: a word
|
|
673
|
+
* inside this string could carry no i18n key.
|
|
674
|
+
************************************************************/
|
|
675
|
+
function fold_toggle_html(path, foldable, folded, branches)
|
|
676
|
+
{
|
|
677
|
+
if(!foldable) {
|
|
678
|
+
/* A spacer, so the label of a leaf card stays centred on the
|
|
679
|
+
* same axis as the label of a foldable one. */
|
|
680
|
+
return `<span style="width:1.1em; flex:0 0 auto;"></span>`;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
let glyph = folded? "▸": "▾"; /* right / down */
|
|
684
|
+
let text = folded? `${glyph} ${branches}`: glyph;
|
|
685
|
+
|
|
686
|
+
return `<span data-fold-path="${escapeHtml(path)}" ` +
|
|
687
|
+
`style="flex:0 0 auto; cursor:pointer; user-select:none; ` +
|
|
688
|
+
`padding:0 .2em; min-width:1.1em; text-align:left;">${text}</span>`;
|
|
689
|
+
}
|
|
690
|
+
|
|
601
691
|
/************************************************************
|
|
602
692
|
* Does this key/value pair match the find term?
|
|
603
693
|
*
|
|
@@ -725,17 +815,12 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id)
|
|
|
725
815
|
* Folded: keep the card, drop the branch, and SAY how much went
|
|
726
816
|
* with it. A cut that is not marked reads as a document that ends
|
|
727
817
|
* there — the same rule the lazy tree follows for a `__collapsed__`
|
|
728
|
-
* sentinel. The
|
|
818
|
+
* sentinel. The mark is a glyph and a number on purpose: the card
|
|
729
819
|
* is an innerHTML string, where a word could carry no i18n key and
|
|
730
820
|
* would sit there in English forever.
|
|
731
821
|
*/
|
|
732
|
-
let
|
|
733
|
-
|
|
734
|
-
lines.push(
|
|
735
|
-
`<span style="color:${type_color("number", dark)}; font-weight:bold;">` +
|
|
736
|
-
`▸ ${pending_complex.length}</span>`
|
|
737
|
-
);
|
|
738
|
-
}
|
|
822
|
+
let foldable = pending_complex.length > 0;
|
|
823
|
+
let folded = foldable && priv.collapsed.has(path);
|
|
739
824
|
let colors = json_card_style(
|
|
740
825
|
is_dict ? GROUP_COLORS.dict : GROUP_COLORS.list, dark
|
|
741
826
|
);
|
|
@@ -766,8 +851,10 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id)
|
|
|
766
851
|
font-weight: bold;
|
|
767
852
|
font-size: 12px;
|
|
768
853
|
padding: 3px 8px;
|
|
769
|
-
|
|
770
|
-
|
|
854
|
+
display: flex;
|
|
855
|
+
align-items: center;
|
|
856
|
+
gap: 6px;
|
|
857
|
+
">${fold_toggle_html(path, foldable, folded, pending_complex.length)}<span style="flex:1 1 auto; text-align:center;">${escapeHtml(label)}</span></div>
|
|
771
858
|
${content_html}
|
|
772
859
|
</div>`;
|
|
773
860
|
|
|
@@ -1097,6 +1184,35 @@ function ac_refresh(gobj, event, kw, src)
|
|
|
1097
1184
|
|
|
1098
1185
|
/************************************************************
|
|
1099
1186
|
* {theme: "dark"|"light"} — the app switched theme.
|
|
1187
|
+
* EV_TOGGLE_FOLD { path } — fold or unfold ONE card.
|
|
1188
|
+
*
|
|
1189
|
+
* The camera does not move: folding one branch is a local
|
|
1190
|
+
* edit of the picture, and refitting the whole graph for it
|
|
1191
|
+
* would throw away where the reader was looking. The toolbar
|
|
1192
|
+
* pair, which changes everything at once, does refit.
|
|
1193
|
+
************************************************************/
|
|
1194
|
+
function ac_toggle_fold(gobj, event, kw, src)
|
|
1195
|
+
{
|
|
1196
|
+
let priv = gobj.priv;
|
|
1197
|
+
let path = (kw && kw.path) || "";
|
|
1198
|
+
|
|
1199
|
+
if(empty_string(path)) {
|
|
1200
|
+
log_error(`${gobj_short_name(gobj)}: EV_TOGGLE_FOLD with no path`);
|
|
1201
|
+
return -1;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
if(priv.collapsed.has(path)) {
|
|
1205
|
+
priv.collapsed.delete(path);
|
|
1206
|
+
} else {
|
|
1207
|
+
priv.collapsed.add(path);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
refresh_json(gobj, {preserve_view: true});
|
|
1211
|
+
update_find_result(gobj);
|
|
1212
|
+
return 0;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/************************************************************
|
|
1100
1216
|
* EV_FIND_NODES { text }
|
|
1101
1217
|
*
|
|
1102
1218
|
* The camera does NOT move: a find is a way of reading what is
|
|
@@ -1349,6 +1465,7 @@ function create_gclass(gclass_name)
|
|
|
1349
1465
|
["EV_LOAD_DATA", ac_load_data, null],
|
|
1350
1466
|
["EV_REFRESH", ac_refresh, null],
|
|
1351
1467
|
["EV_FIND_NODES", ac_find_nodes, null],
|
|
1468
|
+
["EV_TOGGLE_FOLD", ac_toggle_fold, null],
|
|
1352
1469
|
["EV_EXPAND_ALL", ac_expand_all, null],
|
|
1353
1470
|
["EV_COLLAPSE_ALL", ac_collapse_all, null],
|
|
1354
1471
|
["EV_THEME", ac_theme, null],
|
|
@@ -1370,6 +1487,7 @@ function create_gclass(gclass_name)
|
|
|
1370
1487
|
["EV_LOAD_DATA", 0],
|
|
1371
1488
|
["EV_REFRESH", 0],
|
|
1372
1489
|
["EV_FIND_NODES", 0],
|
|
1490
|
+
["EV_TOGGLE_FOLD", 0],
|
|
1373
1491
|
["EV_EXPAND_ALL", 0],
|
|
1374
1492
|
["EV_COLLAPSE_ALL", 0],
|
|
1375
1493
|
["EV_THEME", 0],
|