@yuneta/gobj-ui 7.23.2 → 7.23.4
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 +11 -0
- package/dist/gobj-ui.cjs.js +183 -176
- package/dist/gobj-ui.es.js +183 -176
- package/package.json +1 -1
- package/src/c_yui_gobj_tree_js.js +24 -34
- package/src/c_yui_json_graph.js +25 -61
- package/src/yui_graph_camera.js +150 -0
- package/src/yui_icons.css +10 -0
package/README.md
CHANGED
|
@@ -448,6 +448,17 @@ stays. Note the close contract: `C_YUI_WINDOW.close_window()` calls `on_close`
|
|
|
448
448
|
and THEN destroys itself, so a host must drop its reference rather than destroy
|
|
449
449
|
the window too.
|
|
450
450
|
|
|
451
|
+
Its camera comes from **`yui_graph_camera.js`** (`7.23.4`), the one place every
|
|
452
|
+
DOM-toolbar graph asks for one — this gclass and the gobj tree both, so a
|
|
453
|
+
cloned toolbar cannot drift apart again. The vocabulary is the treedb graph's
|
|
454
|
+
(`c_g6_nodes_tree.js`, which draws its toolbar as a G6 plugin over an SVG
|
|
455
|
+
sprite and so cannot share the code, only the decisions): zoom in / zoom out, a
|
|
456
|
+
live **zoom readout**, `yi-fit` for fit — the same corner brackets the sprite
|
|
457
|
+
draws for `g6-icon-fit` — and the WRITTEN `1:1` for actual size, which is
|
|
458
|
+
written in every editor that offers it and never drawn. Fold is the chevron
|
|
459
|
+
pair, from the lazy tree viewer's own toolbar. The readout hangs off G6's
|
|
460
|
+
`aftertransform`, the one hook that also covers the wheel.
|
|
461
|
+
|
|
451
462
|
The graph carries **its own** toolbar, a **layout picker**
|
|
452
463
|
(`EV_CHANGE_LAYOUT {layout}`: `vertical tree`, `dagre top-down`,
|
|
453
464
|
`dagre left-right` — the layout and the edge type move together), and since
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -9350,6 +9350,160 @@ function yui_toolbar(attrs = {}, items = []) {
|
|
|
9350
9350
|
return $toolbar;
|
|
9351
9351
|
}
|
|
9352
9352
|
//#endregion
|
|
9353
|
+
//#region src/yui_graph_camera.js
|
|
9354
|
+
/***********************************************************************
|
|
9355
|
+
* yui_graph_camera.js
|
|
9356
|
+
*
|
|
9357
|
+
* The camera and fold controls a G6 graph puts in a DOM toolbar,
|
|
9358
|
+
* built in ONE place.
|
|
9359
|
+
*
|
|
9360
|
+
* This module exists because the same action was drawn three
|
|
9361
|
+
* different ways across sibling views — actual size as a bare
|
|
9362
|
+
* magnifier here and as `1:1` there, fit as `arrows-to-eye` here and
|
|
9363
|
+
* as corner brackets there, fold as chevrons here and as an eye
|
|
9364
|
+
* there. A user sees two of these graphs side by side in the same
|
|
9365
|
+
* console, so a copied-and-drifted toolbar is not a detail.
|
|
9366
|
+
*
|
|
9367
|
+
* The VOCABULARY is not invented here. It is the one
|
|
9368
|
+
* `c_g6_nodes_tree.js` settled on for the treedb graph, which draws
|
|
9369
|
+
* its toolbar as a G6 plugin over an SVG sprite and therefore cannot
|
|
9370
|
+
* share this code — only its decisions:
|
|
9371
|
+
*
|
|
9372
|
+
* zoom in / zoom out magnifiers
|
|
9373
|
+
* zoom level a READOUT, because without it "1:1" is
|
|
9374
|
+
* a jump to a value nobody was told
|
|
9375
|
+
* fit corner brackets (`yi-fit`, the same
|
|
9376
|
+
* path as the sprite's `g6-icon-fit`)
|
|
9377
|
+
* actual size the WRITTEN `1:1` — the action is
|
|
9378
|
+
* `zoomTo(1)`, and actual size is written
|
|
9379
|
+
* in every editor that offers it, never
|
|
9380
|
+
* drawn, because there is no glyph for it
|
|
9381
|
+
*
|
|
9382
|
+
* Fold is the one the treedb graph does not have, so its vocabulary
|
|
9383
|
+
* comes from the other side of the family: the lazy tree viewer's own
|
|
9384
|
+
* toolbar (`c_yui_json.js`) uses chevrons — open pointing down, closed
|
|
9385
|
+
* pointing right — which is also the direction the per-node handles
|
|
9386
|
+
* speak. An eye means show/hide, which is a different idea.
|
|
9387
|
+
*
|
|
9388
|
+
* Copyright (c) 2026, ArtGins.
|
|
9389
|
+
* All Rights Reserved.
|
|
9390
|
+
***********************************************************************/
|
|
9391
|
+
/************************************************************
|
|
9392
|
+
* One icon button that fires `event_name`.
|
|
9393
|
+
************************************************************/
|
|
9394
|
+
function camera_button(gobj, icon, event_name, label_key, wide, extra_style) {
|
|
9395
|
+
return [
|
|
9396
|
+
"button",
|
|
9397
|
+
{
|
|
9398
|
+
class: `button ${event_name}`,
|
|
9399
|
+
type: "button",
|
|
9400
|
+
style: {
|
|
9401
|
+
height: wide,
|
|
9402
|
+
width: "2.5em"
|
|
9403
|
+
},
|
|
9404
|
+
title: (0, i18next.t)(label_key),
|
|
9405
|
+
"data-i18n-title": label_key,
|
|
9406
|
+
"aria-label": (0, i18next.t)(label_key),
|
|
9407
|
+
"data-i18n-aria-label": label_key
|
|
9408
|
+
},
|
|
9409
|
+
["i", {
|
|
9410
|
+
style: "font-size:1.5em; color:inherit;" + (extra_style || ""),
|
|
9411
|
+
class: icon
|
|
9412
|
+
}],
|
|
9413
|
+
{ click: (evt) => {
|
|
9414
|
+
evt.stopPropagation();
|
|
9415
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, event_name, { evt }, gobj);
|
|
9416
|
+
} }
|
|
9417
|
+
];
|
|
9418
|
+
}
|
|
9419
|
+
/************************************************************
|
|
9420
|
+
* The current zoom as a percentage.
|
|
9421
|
+
************************************************************/
|
|
9422
|
+
function yui_graph_zoom_text(graph) {
|
|
9423
|
+
if (!graph) return "100%";
|
|
9424
|
+
try {
|
|
9425
|
+
return Math.round(graph.getZoom() * 100) + "%";
|
|
9426
|
+
} catch (e) {
|
|
9427
|
+
return "100%";
|
|
9428
|
+
}
|
|
9429
|
+
}
|
|
9430
|
+
/************************************************************
|
|
9431
|
+
* Repaint the readout of a container that carries one.
|
|
9432
|
+
*
|
|
9433
|
+
* Call it from G6's `aftertransform`, which is the ONE hook
|
|
9434
|
+
* that also covers the WHEEL — a camera change that passes
|
|
9435
|
+
* through no action, so repainting from each zoom action
|
|
9436
|
+
* leaves the number lying after every notch.
|
|
9437
|
+
************************************************************/
|
|
9438
|
+
function yui_graph_update_zoom($container, graph) {
|
|
9439
|
+
if (!$container) return;
|
|
9440
|
+
let $readout = $container.querySelector(".GRAPH_ZOOM_LEVEL");
|
|
9441
|
+
if (!$readout) return;
|
|
9442
|
+
$readout.textContent = yui_graph_zoom_text(graph);
|
|
9443
|
+
}
|
|
9444
|
+
/************************************************************
|
|
9445
|
+
* The camera cluster: zoom in, zoom out, readout, fit,
|
|
9446
|
+
* actual size. Returns createElement2 specs, in order.
|
|
9447
|
+
*
|
|
9448
|
+
* `wide` is the host toolbar's button height.
|
|
9449
|
+
************************************************************/
|
|
9450
|
+
function yui_graph_camera_items(gobj, graph, wide) {
|
|
9451
|
+
return [
|
|
9452
|
+
camera_button(gobj, "yi-magnifying-glass-plus", "EV_ZOOM_IN", "zoom in", wide),
|
|
9453
|
+
camera_button(gobj, "yi-magnifying-glass-minus", "EV_ZOOM_OUT", "zoom out", wide),
|
|
9454
|
+
[
|
|
9455
|
+
"span",
|
|
9456
|
+
{
|
|
9457
|
+
class: "GRAPH_ZOOM_LEVEL is-flex is-align-items-center px-2 has-text-grey",
|
|
9458
|
+
style: "font-size:.85rem; min-width:3.5em; justify-content:center;",
|
|
9459
|
+
title: (0, i18next.t)("zoom level"),
|
|
9460
|
+
"data-i18n-title": "zoom level"
|
|
9461
|
+
},
|
|
9462
|
+
yui_graph_zoom_text(graph)
|
|
9463
|
+
],
|
|
9464
|
+
camera_button(gobj, "yi-fit", "EV_CENTER", "auto fit", wide),
|
|
9465
|
+
[
|
|
9466
|
+
"button",
|
|
9467
|
+
{
|
|
9468
|
+
class: "button EV_ZOOM_RESET",
|
|
9469
|
+
type: "button",
|
|
9470
|
+
style: {
|
|
9471
|
+
height: wide,
|
|
9472
|
+
width: "2.5em"
|
|
9473
|
+
},
|
|
9474
|
+
title: (0, i18next.t)("actual size"),
|
|
9475
|
+
"data-i18n-title": "actual size",
|
|
9476
|
+
"aria-label": (0, i18next.t)("actual size"),
|
|
9477
|
+
"data-i18n-aria-label": "actual size"
|
|
9478
|
+
},
|
|
9479
|
+
[
|
|
9480
|
+
"span",
|
|
9481
|
+
{ style: "font-weight:700;" },
|
|
9482
|
+
"1:1"
|
|
9483
|
+
],
|
|
9484
|
+
{ click: (evt) => {
|
|
9485
|
+
evt.stopPropagation();
|
|
9486
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_ZOOM_RESET", { evt }, gobj);
|
|
9487
|
+
} }
|
|
9488
|
+
]
|
|
9489
|
+
];
|
|
9490
|
+
}
|
|
9491
|
+
/************************************************************
|
|
9492
|
+
* The fold pair: expand all, collapse all. Chevrons, the
|
|
9493
|
+
* open one rotated down — the same pair the lazy tree
|
|
9494
|
+
* viewer's toolbar uses, and the same direction the
|
|
9495
|
+
* per-node handles speak.
|
|
9496
|
+
************************************************************/
|
|
9497
|
+
function yui_graph_fold_items(gobj, wide) {
|
|
9498
|
+
return [camera_button(gobj, "yi-chevron-right", "EV_EXPAND_ALL", "expand all", wide, " transform: rotate(90deg);"), camera_button(gobj, "yi-chevron-right", "EV_COLLAPSE_ALL", "collapse all", wide)];
|
|
9499
|
+
}
|
|
9500
|
+
/************************************************************
|
|
9501
|
+
* A refresh button, for the graphs that offer one.
|
|
9502
|
+
************************************************************/
|
|
9503
|
+
function yui_graph_refresh_item(gobj, wide) {
|
|
9504
|
+
return camera_button(gobj, "yi-arrows-rotate", "EV_REFRESH", "refresh", wide);
|
|
9505
|
+
}
|
|
9506
|
+
//#endregion
|
|
9353
9507
|
//#region src/g6_drag_canvas_touch.js
|
|
9354
9508
|
/***********************************************************************
|
|
9355
9509
|
* g6_drag_canvas_touch.js
|
|
@@ -9753,36 +9907,6 @@ function destroy_ui$6(gobj) {
|
|
|
9753
9907
|
}
|
|
9754
9908
|
}
|
|
9755
9909
|
/************************************************************
|
|
9756
|
-
* One fold button. `open` rotates the chevron to the open
|
|
9757
|
-
* state, mirroring the per-node toggle of the lazy tree.
|
|
9758
|
-
************************************************************/
|
|
9759
|
-
function graph_fold_button(gobj, event_name, label_key, open) {
|
|
9760
|
-
let icon_style = "font-size:1.5em; color:inherit;" + (open ? " transform: rotate(90deg);" : "");
|
|
9761
|
-
return [
|
|
9762
|
-
"button",
|
|
9763
|
-
{
|
|
9764
|
-
class: `button ${event_name}`,
|
|
9765
|
-
type: "button",
|
|
9766
|
-
style: {
|
|
9767
|
-
height: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "wide"),
|
|
9768
|
-
width: "2.5em"
|
|
9769
|
-
},
|
|
9770
|
-
title: (0, i18next.t)(label_key),
|
|
9771
|
-
"data-i18n-title": label_key,
|
|
9772
|
-
"aria-label": (0, i18next.t)(label_key),
|
|
9773
|
-
"data-i18n-aria-label": label_key
|
|
9774
|
-
},
|
|
9775
|
-
["i", {
|
|
9776
|
-
style: icon_style,
|
|
9777
|
-
class: "yi-chevron-right"
|
|
9778
|
-
}],
|
|
9779
|
-
{ click: (evt) => {
|
|
9780
|
-
evt.stopPropagation();
|
|
9781
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, event_name, {}, gobj);
|
|
9782
|
-
} }
|
|
9783
|
-
];
|
|
9784
|
-
}
|
|
9785
|
-
/************************************************************
|
|
9786
9910
|
* Toolbar
|
|
9787
9911
|
************************************************************/
|
|
9788
9912
|
function make_toolbar$3(gobj) {
|
|
@@ -9832,62 +9956,8 @@ function make_toolbar$3(gobj) {
|
|
|
9832
9956
|
]]
|
|
9833
9957
|
]];
|
|
9834
9958
|
let center_items = [];
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
"yi-magnifying-glass-plus",
|
|
9838
|
-
"EV_ZOOM_IN",
|
|
9839
|
-
false,
|
|
9840
|
-
"i"
|
|
9841
|
-
],
|
|
9842
|
-
[
|
|
9843
|
-
"yi-magnifying-glass",
|
|
9844
|
-
"EV_ZOOM_RESET",
|
|
9845
|
-
false,
|
|
9846
|
-
"i"
|
|
9847
|
-
],
|
|
9848
|
-
[
|
|
9849
|
-
"yi-magnifying-glass-minus",
|
|
9850
|
-
"EV_ZOOM_OUT",
|
|
9851
|
-
false,
|
|
9852
|
-
"i"
|
|
9853
|
-
],
|
|
9854
|
-
[
|
|
9855
|
-
"yi-arrows-to-eye",
|
|
9856
|
-
"EV_CENTER",
|
|
9857
|
-
false,
|
|
9858
|
-
"i"
|
|
9859
|
-
],
|
|
9860
|
-
[
|
|
9861
|
-
"yi-arrows-rotate",
|
|
9862
|
-
"EV_REFRESH",
|
|
9863
|
-
false,
|
|
9864
|
-
"i"
|
|
9865
|
-
]
|
|
9866
|
-
]) {
|
|
9867
|
-
let icon_name = item[0];
|
|
9868
|
-
let event_name = item[1];
|
|
9869
|
-
let disabled = item[2];
|
|
9870
|
-
let button = {
|
|
9871
|
-
class: `button ${event_name}`,
|
|
9872
|
-
style: {
|
|
9873
|
-
height: toolbar_wide,
|
|
9874
|
-
width: "2.5em"
|
|
9875
|
-
}
|
|
9876
|
-
};
|
|
9877
|
-
if (disabled) button.disabled = true;
|
|
9878
|
-
center_items.push([
|
|
9879
|
-
"button",
|
|
9880
|
-
button,
|
|
9881
|
-
["i", {
|
|
9882
|
-
style: "font-size:1.5em; color:inherit;",
|
|
9883
|
-
class: icon_name
|
|
9884
|
-
}],
|
|
9885
|
-
{ click: (evt) => {
|
|
9886
|
-
evt.stopPropagation();
|
|
9887
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, event_name, { evt }, gobj);
|
|
9888
|
-
} }
|
|
9889
|
-
]);
|
|
9890
|
-
}
|
|
9959
|
+
center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide);
|
|
9960
|
+
center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
|
|
9891
9961
|
let current_layout = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout") || DEFAULT_LAYOUT;
|
|
9892
9962
|
let options = Object.keys(LAYOUTS$1).map(function(key) {
|
|
9893
9963
|
let attrs = {
|
|
@@ -9901,29 +9971,25 @@ function make_toolbar$3(gobj) {
|
|
|
9901
9971
|
layout_label(key)
|
|
9902
9972
|
];
|
|
9903
9973
|
});
|
|
9904
|
-
let right_items = [
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
"margin-left": "0.5em"
|
|
9912
|
-
},
|
|
9913
|
-
title: (0, i18next.t)("layout"),
|
|
9914
|
-
"data-i18n-title": "layout",
|
|
9915
|
-
"aria-label": (0, i18next.t)("layout"),
|
|
9916
|
-
"data-i18n-aria-label": "layout"
|
|
9974
|
+
let right_items = [(0, _yuneta_gobj_js.createElement2)([
|
|
9975
|
+
"select",
|
|
9976
|
+
{
|
|
9977
|
+
class: "JSON_GRAPH_LAYOUT select",
|
|
9978
|
+
style: {
|
|
9979
|
+
height: toolbar_wide,
|
|
9980
|
+
"margin-left": "0.5em"
|
|
9917
9981
|
},
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9982
|
+
title: (0, i18next.t)("layout"),
|
|
9983
|
+
"data-i18n-title": "layout",
|
|
9984
|
+
"aria-label": (0, i18next.t)("layout"),
|
|
9985
|
+
"data-i18n-aria-label": "layout"
|
|
9986
|
+
},
|
|
9987
|
+
options,
|
|
9988
|
+
{ change: (evt) => {
|
|
9989
|
+
evt.stopPropagation();
|
|
9990
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CHANGE_LAYOUT", { layout: evt.target.value }, gobj);
|
|
9991
|
+
} }
|
|
9992
|
+
])].concat(yui_graph_fold_items(gobj, toolbar_wide));
|
|
9927
9993
|
const $toolbar = yui_toolbar({}, [
|
|
9928
9994
|
[
|
|
9929
9995
|
"div",
|
|
@@ -9971,6 +10037,9 @@ function build_graph$3(gobj) {
|
|
|
9971
10037
|
graph.on(_antv_g6.NodeEvent.CLICK, (evt) => {
|
|
9972
10038
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_NODE_CLICK", { evt }, gobj);
|
|
9973
10039
|
});
|
|
10040
|
+
graph.on("aftertransform", () => {
|
|
10041
|
+
yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
|
|
10042
|
+
});
|
|
9974
10043
|
graph.on(_antv_g6.CanvasEvent.CLICK, (evt) => {});
|
|
9975
10044
|
if (typeof ResizeObserver !== "undefined") {
|
|
9976
10045
|
let $canvas = document.getElementById(priv.canvas_id);
|
|
@@ -10230,6 +10299,7 @@ function load_json(gobj) {
|
|
|
10230
10299
|
});
|
|
10231
10300
|
graph.render().then(() => {
|
|
10232
10301
|
if (!preserve_view) graph.fitView();
|
|
10302
|
+
yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
|
|
10233
10303
|
});
|
|
10234
10304
|
}
|
|
10235
10305
|
}
|
|
@@ -12539,74 +12609,8 @@ function make_toolbar$1(gobj) {
|
|
|
12539
12609
|
let left_items = [];
|
|
12540
12610
|
let center_items = [];
|
|
12541
12611
|
let right_items = [];
|
|
12542
|
-
|
|
12543
|
-
|
|
12544
|
-
"yi-magnifying-glass-plus",
|
|
12545
|
-
"EV_ZOOM_IN",
|
|
12546
|
-
false,
|
|
12547
|
-
"i"
|
|
12548
|
-
],
|
|
12549
|
-
[
|
|
12550
|
-
"yi-magnifying-glass",
|
|
12551
|
-
"EV_ZOOM_RESET",
|
|
12552
|
-
false,
|
|
12553
|
-
"i"
|
|
12554
|
-
],
|
|
12555
|
-
[
|
|
12556
|
-
"yi-magnifying-glass-minus",
|
|
12557
|
-
"EV_ZOOM_OUT",
|
|
12558
|
-
false,
|
|
12559
|
-
"i"
|
|
12560
|
-
],
|
|
12561
|
-
[
|
|
12562
|
-
"yi-arrows-to-eye",
|
|
12563
|
-
"EV_CENTER",
|
|
12564
|
-
false,
|
|
12565
|
-
"i"
|
|
12566
|
-
],
|
|
12567
|
-
[
|
|
12568
|
-
"yi-eye",
|
|
12569
|
-
"EV_EXPAND_ALL",
|
|
12570
|
-
false,
|
|
12571
|
-
"i"
|
|
12572
|
-
],
|
|
12573
|
-
[
|
|
12574
|
-
"yi-eye-slash",
|
|
12575
|
-
"EV_COLLAPSE_ALL",
|
|
12576
|
-
false,
|
|
12577
|
-
"i"
|
|
12578
|
-
],
|
|
12579
|
-
[
|
|
12580
|
-
"yi-arrows-rotate",
|
|
12581
|
-
"EV_REFRESH",
|
|
12582
|
-
false,
|
|
12583
|
-
"i"
|
|
12584
|
-
]
|
|
12585
|
-
]) {
|
|
12586
|
-
let icon_name = item[0];
|
|
12587
|
-
let event_name = item[1];
|
|
12588
|
-
let disabled = item[2];
|
|
12589
|
-
let button = {
|
|
12590
|
-
class: `button ${event_name}`,
|
|
12591
|
-
style: {
|
|
12592
|
-
height: toolbar_wide,
|
|
12593
|
-
width: "2.5em"
|
|
12594
|
-
}
|
|
12595
|
-
};
|
|
12596
|
-
if (disabled) button.disabled = true;
|
|
12597
|
-
center_items.push([
|
|
12598
|
-
"button",
|
|
12599
|
-
button,
|
|
12600
|
-
["i", {
|
|
12601
|
-
style: "font-size:1.5em; color:inherit;",
|
|
12602
|
-
class: icon_name
|
|
12603
|
-
}],
|
|
12604
|
-
{ click: (evt) => {
|
|
12605
|
-
evt.stopPropagation();
|
|
12606
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, event_name, { evt }, gobj);
|
|
12607
|
-
} }
|
|
12608
|
-
]);
|
|
12609
|
-
}
|
|
12612
|
+
center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide).concat(yui_graph_fold_items(gobj, toolbar_wide));
|
|
12613
|
+
center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
|
|
12610
12614
|
let options = [];
|
|
12611
12615
|
for (let key of Object.keys(LAYOUTS)) {
|
|
12612
12616
|
let opt_attrs = { value: key };
|
|
@@ -12679,6 +12683,9 @@ function build_graph$2(gobj) {
|
|
|
12679
12683
|
behaviors: ["drag-canvas", "zoom-canvas"]
|
|
12680
12684
|
});
|
|
12681
12685
|
if (priv.theme) graph.setTheme(priv.theme);
|
|
12686
|
+
graph.on("aftertransform", () => {
|
|
12687
|
+
yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
|
|
12688
|
+
});
|
|
12682
12689
|
graph.on(_antv_g6.NodeEvent.CLICK, (evt) => {
|
|
12683
12690
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_NODE_CLICK", { evt }, gobj);
|
|
12684
12691
|
});
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -9345,6 +9345,160 @@ function yui_toolbar(attrs = {}, items = []) {
|
|
|
9345
9345
|
return $toolbar;
|
|
9346
9346
|
}
|
|
9347
9347
|
//#endregion
|
|
9348
|
+
//#region src/yui_graph_camera.js
|
|
9349
|
+
/***********************************************************************
|
|
9350
|
+
* yui_graph_camera.js
|
|
9351
|
+
*
|
|
9352
|
+
* The camera and fold controls a G6 graph puts in a DOM toolbar,
|
|
9353
|
+
* built in ONE place.
|
|
9354
|
+
*
|
|
9355
|
+
* This module exists because the same action was drawn three
|
|
9356
|
+
* different ways across sibling views — actual size as a bare
|
|
9357
|
+
* magnifier here and as `1:1` there, fit as `arrows-to-eye` here and
|
|
9358
|
+
* as corner brackets there, fold as chevrons here and as an eye
|
|
9359
|
+
* there. A user sees two of these graphs side by side in the same
|
|
9360
|
+
* console, so a copied-and-drifted toolbar is not a detail.
|
|
9361
|
+
*
|
|
9362
|
+
* The VOCABULARY is not invented here. It is the one
|
|
9363
|
+
* `c_g6_nodes_tree.js` settled on for the treedb graph, which draws
|
|
9364
|
+
* its toolbar as a G6 plugin over an SVG sprite and therefore cannot
|
|
9365
|
+
* share this code — only its decisions:
|
|
9366
|
+
*
|
|
9367
|
+
* zoom in / zoom out magnifiers
|
|
9368
|
+
* zoom level a READOUT, because without it "1:1" is
|
|
9369
|
+
* a jump to a value nobody was told
|
|
9370
|
+
* fit corner brackets (`yi-fit`, the same
|
|
9371
|
+
* path as the sprite's `g6-icon-fit`)
|
|
9372
|
+
* actual size the WRITTEN `1:1` — the action is
|
|
9373
|
+
* `zoomTo(1)`, and actual size is written
|
|
9374
|
+
* in every editor that offers it, never
|
|
9375
|
+
* drawn, because there is no glyph for it
|
|
9376
|
+
*
|
|
9377
|
+
* Fold is the one the treedb graph does not have, so its vocabulary
|
|
9378
|
+
* comes from the other side of the family: the lazy tree viewer's own
|
|
9379
|
+
* toolbar (`c_yui_json.js`) uses chevrons — open pointing down, closed
|
|
9380
|
+
* pointing right — which is also the direction the per-node handles
|
|
9381
|
+
* speak. An eye means show/hide, which is a different idea.
|
|
9382
|
+
*
|
|
9383
|
+
* Copyright (c) 2026, ArtGins.
|
|
9384
|
+
* All Rights Reserved.
|
|
9385
|
+
***********************************************************************/
|
|
9386
|
+
/************************************************************
|
|
9387
|
+
* One icon button that fires `event_name`.
|
|
9388
|
+
************************************************************/
|
|
9389
|
+
function camera_button(gobj, icon, event_name, label_key, wide, extra_style) {
|
|
9390
|
+
return [
|
|
9391
|
+
"button",
|
|
9392
|
+
{
|
|
9393
|
+
class: `button ${event_name}`,
|
|
9394
|
+
type: "button",
|
|
9395
|
+
style: {
|
|
9396
|
+
height: wide,
|
|
9397
|
+
width: "2.5em"
|
|
9398
|
+
},
|
|
9399
|
+
title: t(label_key),
|
|
9400
|
+
"data-i18n-title": label_key,
|
|
9401
|
+
"aria-label": t(label_key),
|
|
9402
|
+
"data-i18n-aria-label": label_key
|
|
9403
|
+
},
|
|
9404
|
+
["i", {
|
|
9405
|
+
style: "font-size:1.5em; color:inherit;" + (extra_style || ""),
|
|
9406
|
+
class: icon
|
|
9407
|
+
}],
|
|
9408
|
+
{ click: (evt) => {
|
|
9409
|
+
evt.stopPropagation();
|
|
9410
|
+
gobj_send_event(gobj, event_name, { evt }, gobj);
|
|
9411
|
+
} }
|
|
9412
|
+
];
|
|
9413
|
+
}
|
|
9414
|
+
/************************************************************
|
|
9415
|
+
* The current zoom as a percentage.
|
|
9416
|
+
************************************************************/
|
|
9417
|
+
function yui_graph_zoom_text(graph) {
|
|
9418
|
+
if (!graph) return "100%";
|
|
9419
|
+
try {
|
|
9420
|
+
return Math.round(graph.getZoom() * 100) + "%";
|
|
9421
|
+
} catch (e) {
|
|
9422
|
+
return "100%";
|
|
9423
|
+
}
|
|
9424
|
+
}
|
|
9425
|
+
/************************************************************
|
|
9426
|
+
* Repaint the readout of a container that carries one.
|
|
9427
|
+
*
|
|
9428
|
+
* Call it from G6's `aftertransform`, which is the ONE hook
|
|
9429
|
+
* that also covers the WHEEL — a camera change that passes
|
|
9430
|
+
* through no action, so repainting from each zoom action
|
|
9431
|
+
* leaves the number lying after every notch.
|
|
9432
|
+
************************************************************/
|
|
9433
|
+
function yui_graph_update_zoom($container, graph) {
|
|
9434
|
+
if (!$container) return;
|
|
9435
|
+
let $readout = $container.querySelector(".GRAPH_ZOOM_LEVEL");
|
|
9436
|
+
if (!$readout) return;
|
|
9437
|
+
$readout.textContent = yui_graph_zoom_text(graph);
|
|
9438
|
+
}
|
|
9439
|
+
/************************************************************
|
|
9440
|
+
* The camera cluster: zoom in, zoom out, readout, fit,
|
|
9441
|
+
* actual size. Returns createElement2 specs, in order.
|
|
9442
|
+
*
|
|
9443
|
+
* `wide` is the host toolbar's button height.
|
|
9444
|
+
************************************************************/
|
|
9445
|
+
function yui_graph_camera_items(gobj, graph, wide) {
|
|
9446
|
+
return [
|
|
9447
|
+
camera_button(gobj, "yi-magnifying-glass-plus", "EV_ZOOM_IN", "zoom in", wide),
|
|
9448
|
+
camera_button(gobj, "yi-magnifying-glass-minus", "EV_ZOOM_OUT", "zoom out", wide),
|
|
9449
|
+
[
|
|
9450
|
+
"span",
|
|
9451
|
+
{
|
|
9452
|
+
class: "GRAPH_ZOOM_LEVEL is-flex is-align-items-center px-2 has-text-grey",
|
|
9453
|
+
style: "font-size:.85rem; min-width:3.5em; justify-content:center;",
|
|
9454
|
+
title: t("zoom level"),
|
|
9455
|
+
"data-i18n-title": "zoom level"
|
|
9456
|
+
},
|
|
9457
|
+
yui_graph_zoom_text(graph)
|
|
9458
|
+
],
|
|
9459
|
+
camera_button(gobj, "yi-fit", "EV_CENTER", "auto fit", wide),
|
|
9460
|
+
[
|
|
9461
|
+
"button",
|
|
9462
|
+
{
|
|
9463
|
+
class: "button EV_ZOOM_RESET",
|
|
9464
|
+
type: "button",
|
|
9465
|
+
style: {
|
|
9466
|
+
height: wide,
|
|
9467
|
+
width: "2.5em"
|
|
9468
|
+
},
|
|
9469
|
+
title: t("actual size"),
|
|
9470
|
+
"data-i18n-title": "actual size",
|
|
9471
|
+
"aria-label": t("actual size"),
|
|
9472
|
+
"data-i18n-aria-label": "actual size"
|
|
9473
|
+
},
|
|
9474
|
+
[
|
|
9475
|
+
"span",
|
|
9476
|
+
{ style: "font-weight:700;" },
|
|
9477
|
+
"1:1"
|
|
9478
|
+
],
|
|
9479
|
+
{ click: (evt) => {
|
|
9480
|
+
evt.stopPropagation();
|
|
9481
|
+
gobj_send_event(gobj, "EV_ZOOM_RESET", { evt }, gobj);
|
|
9482
|
+
} }
|
|
9483
|
+
]
|
|
9484
|
+
];
|
|
9485
|
+
}
|
|
9486
|
+
/************************************************************
|
|
9487
|
+
* The fold pair: expand all, collapse all. Chevrons, the
|
|
9488
|
+
* open one rotated down — the same pair the lazy tree
|
|
9489
|
+
* viewer's toolbar uses, and the same direction the
|
|
9490
|
+
* per-node handles speak.
|
|
9491
|
+
************************************************************/
|
|
9492
|
+
function yui_graph_fold_items(gobj, wide) {
|
|
9493
|
+
return [camera_button(gobj, "yi-chevron-right", "EV_EXPAND_ALL", "expand all", wide, " transform: rotate(90deg);"), camera_button(gobj, "yi-chevron-right", "EV_COLLAPSE_ALL", "collapse all", wide)];
|
|
9494
|
+
}
|
|
9495
|
+
/************************************************************
|
|
9496
|
+
* A refresh button, for the graphs that offer one.
|
|
9497
|
+
************************************************************/
|
|
9498
|
+
function yui_graph_refresh_item(gobj, wide) {
|
|
9499
|
+
return camera_button(gobj, "yi-arrows-rotate", "EV_REFRESH", "refresh", wide);
|
|
9500
|
+
}
|
|
9501
|
+
//#endregion
|
|
9348
9502
|
//#region src/g6_drag_canvas_touch.js
|
|
9349
9503
|
/***********************************************************************
|
|
9350
9504
|
* g6_drag_canvas_touch.js
|
|
@@ -9748,36 +9902,6 @@ function destroy_ui$6(gobj) {
|
|
|
9748
9902
|
}
|
|
9749
9903
|
}
|
|
9750
9904
|
/************************************************************
|
|
9751
|
-
* One fold button. `open` rotates the chevron to the open
|
|
9752
|
-
* state, mirroring the per-node toggle of the lazy tree.
|
|
9753
|
-
************************************************************/
|
|
9754
|
-
function graph_fold_button(gobj, event_name, label_key, open) {
|
|
9755
|
-
let icon_style = "font-size:1.5em; color:inherit;" + (open ? " transform: rotate(90deg);" : "");
|
|
9756
|
-
return [
|
|
9757
|
-
"button",
|
|
9758
|
-
{
|
|
9759
|
-
class: `button ${event_name}`,
|
|
9760
|
-
type: "button",
|
|
9761
|
-
style: {
|
|
9762
|
-
height: gobj_read_attr(gobj, "wide"),
|
|
9763
|
-
width: "2.5em"
|
|
9764
|
-
},
|
|
9765
|
-
title: t(label_key),
|
|
9766
|
-
"data-i18n-title": label_key,
|
|
9767
|
-
"aria-label": t(label_key),
|
|
9768
|
-
"data-i18n-aria-label": label_key
|
|
9769
|
-
},
|
|
9770
|
-
["i", {
|
|
9771
|
-
style: icon_style,
|
|
9772
|
-
class: "yi-chevron-right"
|
|
9773
|
-
}],
|
|
9774
|
-
{ click: (evt) => {
|
|
9775
|
-
evt.stopPropagation();
|
|
9776
|
-
gobj_send_event(gobj, event_name, {}, gobj);
|
|
9777
|
-
} }
|
|
9778
|
-
];
|
|
9779
|
-
}
|
|
9780
|
-
/************************************************************
|
|
9781
9905
|
* Toolbar
|
|
9782
9906
|
************************************************************/
|
|
9783
9907
|
function make_toolbar$3(gobj) {
|
|
@@ -9827,62 +9951,8 @@ function make_toolbar$3(gobj) {
|
|
|
9827
9951
|
]]
|
|
9828
9952
|
]];
|
|
9829
9953
|
let center_items = [];
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
"yi-magnifying-glass-plus",
|
|
9833
|
-
"EV_ZOOM_IN",
|
|
9834
|
-
false,
|
|
9835
|
-
"i"
|
|
9836
|
-
],
|
|
9837
|
-
[
|
|
9838
|
-
"yi-magnifying-glass",
|
|
9839
|
-
"EV_ZOOM_RESET",
|
|
9840
|
-
false,
|
|
9841
|
-
"i"
|
|
9842
|
-
],
|
|
9843
|
-
[
|
|
9844
|
-
"yi-magnifying-glass-minus",
|
|
9845
|
-
"EV_ZOOM_OUT",
|
|
9846
|
-
false,
|
|
9847
|
-
"i"
|
|
9848
|
-
],
|
|
9849
|
-
[
|
|
9850
|
-
"yi-arrows-to-eye",
|
|
9851
|
-
"EV_CENTER",
|
|
9852
|
-
false,
|
|
9853
|
-
"i"
|
|
9854
|
-
],
|
|
9855
|
-
[
|
|
9856
|
-
"yi-arrows-rotate",
|
|
9857
|
-
"EV_REFRESH",
|
|
9858
|
-
false,
|
|
9859
|
-
"i"
|
|
9860
|
-
]
|
|
9861
|
-
]) {
|
|
9862
|
-
let icon_name = item[0];
|
|
9863
|
-
let event_name = item[1];
|
|
9864
|
-
let disabled = item[2];
|
|
9865
|
-
let button = {
|
|
9866
|
-
class: `button ${event_name}`,
|
|
9867
|
-
style: {
|
|
9868
|
-
height: toolbar_wide,
|
|
9869
|
-
width: "2.5em"
|
|
9870
|
-
}
|
|
9871
|
-
};
|
|
9872
|
-
if (disabled) button.disabled = true;
|
|
9873
|
-
center_items.push([
|
|
9874
|
-
"button",
|
|
9875
|
-
button,
|
|
9876
|
-
["i", {
|
|
9877
|
-
style: "font-size:1.5em; color:inherit;",
|
|
9878
|
-
class: icon_name
|
|
9879
|
-
}],
|
|
9880
|
-
{ click: (evt) => {
|
|
9881
|
-
evt.stopPropagation();
|
|
9882
|
-
gobj_send_event(gobj, event_name, { evt }, gobj);
|
|
9883
|
-
} }
|
|
9884
|
-
]);
|
|
9885
|
-
}
|
|
9954
|
+
center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide);
|
|
9955
|
+
center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
|
|
9886
9956
|
let current_layout = gobj_read_str_attr(gobj, "layout") || DEFAULT_LAYOUT;
|
|
9887
9957
|
let options = Object.keys(LAYOUTS$1).map(function(key) {
|
|
9888
9958
|
let attrs = {
|
|
@@ -9896,29 +9966,25 @@ function make_toolbar$3(gobj) {
|
|
|
9896
9966
|
layout_label(key)
|
|
9897
9967
|
];
|
|
9898
9968
|
});
|
|
9899
|
-
let right_items = [
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
"margin-left": "0.5em"
|
|
9907
|
-
},
|
|
9908
|
-
title: t("layout"),
|
|
9909
|
-
"data-i18n-title": "layout",
|
|
9910
|
-
"aria-label": t("layout"),
|
|
9911
|
-
"data-i18n-aria-label": "layout"
|
|
9969
|
+
let right_items = [createElement2([
|
|
9970
|
+
"select",
|
|
9971
|
+
{
|
|
9972
|
+
class: "JSON_GRAPH_LAYOUT select",
|
|
9973
|
+
style: {
|
|
9974
|
+
height: toolbar_wide,
|
|
9975
|
+
"margin-left": "0.5em"
|
|
9912
9976
|
},
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9977
|
+
title: t("layout"),
|
|
9978
|
+
"data-i18n-title": "layout",
|
|
9979
|
+
"aria-label": t("layout"),
|
|
9980
|
+
"data-i18n-aria-label": "layout"
|
|
9981
|
+
},
|
|
9982
|
+
options,
|
|
9983
|
+
{ change: (evt) => {
|
|
9984
|
+
evt.stopPropagation();
|
|
9985
|
+
gobj_send_event(gobj, "EV_CHANGE_LAYOUT", { layout: evt.target.value }, gobj);
|
|
9986
|
+
} }
|
|
9987
|
+
])].concat(yui_graph_fold_items(gobj, toolbar_wide));
|
|
9922
9988
|
const $toolbar = yui_toolbar({}, [
|
|
9923
9989
|
[
|
|
9924
9990
|
"div",
|
|
@@ -9966,6 +10032,9 @@ function build_graph$3(gobj) {
|
|
|
9966
10032
|
graph.on(NodeEvent.CLICK, (evt) => {
|
|
9967
10033
|
gobj_send_event(gobj, "EV_NODE_CLICK", { evt }, gobj);
|
|
9968
10034
|
});
|
|
10035
|
+
graph.on("aftertransform", () => {
|
|
10036
|
+
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
10037
|
+
});
|
|
9969
10038
|
graph.on(CanvasEvent.CLICK, (evt) => {});
|
|
9970
10039
|
if (typeof ResizeObserver !== "undefined") {
|
|
9971
10040
|
let $canvas = document.getElementById(priv.canvas_id);
|
|
@@ -10225,6 +10294,7 @@ function load_json(gobj) {
|
|
|
10225
10294
|
});
|
|
10226
10295
|
graph.render().then(() => {
|
|
10227
10296
|
if (!preserve_view) graph.fitView();
|
|
10297
|
+
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
10228
10298
|
});
|
|
10229
10299
|
}
|
|
10230
10300
|
}
|
|
@@ -12534,74 +12604,8 @@ function make_toolbar$1(gobj) {
|
|
|
12534
12604
|
let left_items = [];
|
|
12535
12605
|
let center_items = [];
|
|
12536
12606
|
let right_items = [];
|
|
12537
|
-
|
|
12538
|
-
|
|
12539
|
-
"yi-magnifying-glass-plus",
|
|
12540
|
-
"EV_ZOOM_IN",
|
|
12541
|
-
false,
|
|
12542
|
-
"i"
|
|
12543
|
-
],
|
|
12544
|
-
[
|
|
12545
|
-
"yi-magnifying-glass",
|
|
12546
|
-
"EV_ZOOM_RESET",
|
|
12547
|
-
false,
|
|
12548
|
-
"i"
|
|
12549
|
-
],
|
|
12550
|
-
[
|
|
12551
|
-
"yi-magnifying-glass-minus",
|
|
12552
|
-
"EV_ZOOM_OUT",
|
|
12553
|
-
false,
|
|
12554
|
-
"i"
|
|
12555
|
-
],
|
|
12556
|
-
[
|
|
12557
|
-
"yi-arrows-to-eye",
|
|
12558
|
-
"EV_CENTER",
|
|
12559
|
-
false,
|
|
12560
|
-
"i"
|
|
12561
|
-
],
|
|
12562
|
-
[
|
|
12563
|
-
"yi-eye",
|
|
12564
|
-
"EV_EXPAND_ALL",
|
|
12565
|
-
false,
|
|
12566
|
-
"i"
|
|
12567
|
-
],
|
|
12568
|
-
[
|
|
12569
|
-
"yi-eye-slash",
|
|
12570
|
-
"EV_COLLAPSE_ALL",
|
|
12571
|
-
false,
|
|
12572
|
-
"i"
|
|
12573
|
-
],
|
|
12574
|
-
[
|
|
12575
|
-
"yi-arrows-rotate",
|
|
12576
|
-
"EV_REFRESH",
|
|
12577
|
-
false,
|
|
12578
|
-
"i"
|
|
12579
|
-
]
|
|
12580
|
-
]) {
|
|
12581
|
-
let icon_name = item[0];
|
|
12582
|
-
let event_name = item[1];
|
|
12583
|
-
let disabled = item[2];
|
|
12584
|
-
let button = {
|
|
12585
|
-
class: `button ${event_name}`,
|
|
12586
|
-
style: {
|
|
12587
|
-
height: toolbar_wide,
|
|
12588
|
-
width: "2.5em"
|
|
12589
|
-
}
|
|
12590
|
-
};
|
|
12591
|
-
if (disabled) button.disabled = true;
|
|
12592
|
-
center_items.push([
|
|
12593
|
-
"button",
|
|
12594
|
-
button,
|
|
12595
|
-
["i", {
|
|
12596
|
-
style: "font-size:1.5em; color:inherit;",
|
|
12597
|
-
class: icon_name
|
|
12598
|
-
}],
|
|
12599
|
-
{ click: (evt) => {
|
|
12600
|
-
evt.stopPropagation();
|
|
12601
|
-
gobj_send_event(gobj, event_name, { evt }, gobj);
|
|
12602
|
-
} }
|
|
12603
|
-
]);
|
|
12604
|
-
}
|
|
12607
|
+
center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide).concat(yui_graph_fold_items(gobj, toolbar_wide));
|
|
12608
|
+
center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
|
|
12605
12609
|
let options = [];
|
|
12606
12610
|
for (let key of Object.keys(LAYOUTS)) {
|
|
12607
12611
|
let opt_attrs = { value: key };
|
|
@@ -12674,6 +12678,9 @@ function build_graph$2(gobj) {
|
|
|
12674
12678
|
behaviors: ["drag-canvas", "zoom-canvas"]
|
|
12675
12679
|
});
|
|
12676
12680
|
if (priv.theme) graph.setTheme(priv.theme);
|
|
12681
|
+
graph.on("aftertransform", () => {
|
|
12682
|
+
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
12683
|
+
});
|
|
12677
12684
|
graph.on(NodeEvent.CLICK, (evt) => {
|
|
12678
12685
|
gobj_send_event(gobj, "EV_NODE_CLICK", { evt }, gobj);
|
|
12679
12686
|
});
|
package/package.json
CHANGED
|
@@ -40,6 +40,12 @@ import {
|
|
|
40
40
|
} from "@yuneta/gobj-js";
|
|
41
41
|
|
|
42
42
|
import {yui_toolbar} from "./yui_toolbar.js";
|
|
43
|
+
import {
|
|
44
|
+
yui_graph_camera_items,
|
|
45
|
+
yui_graph_fold_items,
|
|
46
|
+
yui_graph_refresh_item,
|
|
47
|
+
yui_graph_update_zoom,
|
|
48
|
+
} from "./yui_graph_camera.js";
|
|
43
49
|
|
|
44
50
|
import {t} from "i18next";
|
|
45
51
|
|
|
@@ -535,40 +541,18 @@ function make_toolbar(gobj)
|
|
|
535
541
|
let center_items = [];
|
|
536
542
|
let right_items = [];
|
|
537
543
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
let event_name = item[1];
|
|
551
|
-
let disabled = item[2];
|
|
552
|
-
|
|
553
|
-
let button = {
|
|
554
|
-
class: `button ${event_name}`,
|
|
555
|
-
style: {height: toolbar_wide, width: '2.5em'}
|
|
556
|
-
};
|
|
557
|
-
if(disabled) {
|
|
558
|
-
button.disabled = true;
|
|
559
|
-
}
|
|
560
|
-
center_items.push(
|
|
561
|
-
['button', button,
|
|
562
|
-
['i', {style: 'font-size:1.5em; color:inherit;', class: icon_name}],
|
|
563
|
-
{
|
|
564
|
-
click: (evt) => {
|
|
565
|
-
evt.stopPropagation();
|
|
566
|
-
gobj_send_event(gobj, event_name, {evt: evt}, gobj);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
]
|
|
570
|
-
);
|
|
571
|
-
}
|
|
544
|
+
/*
|
|
545
|
+
* The camera and the fold pair come from yui_graph_camera.js.
|
|
546
|
+
*
|
|
547
|
+
* This toolbar used to draw two of these its own way — a bare
|
|
548
|
+
* magnifier for actual size, `arrows-to-eye` for fit — and fold as
|
|
549
|
+
* an eye, which means show/hide and not open/closed. The same
|
|
550
|
+
* console shows this graph and the JSON one side by side, so the
|
|
551
|
+
* drawings have to be the same drawings.
|
|
552
|
+
*/
|
|
553
|
+
center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide)
|
|
554
|
+
.concat(yui_graph_fold_items(gobj, toolbar_wide));
|
|
555
|
+
center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
|
|
572
556
|
|
|
573
557
|
/*
|
|
574
558
|
* Layout selector
|
|
@@ -651,6 +635,12 @@ function build_graph(gobj)
|
|
|
651
635
|
graph.setTheme(priv.theme);
|
|
652
636
|
}
|
|
653
637
|
|
|
638
|
+
/* The readout follows ANY camera change, the wheel included — a
|
|
639
|
+
* wheel notch passes through no action of ours. */
|
|
640
|
+
graph.on('aftertransform', () => {
|
|
641
|
+
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
642
|
+
});
|
|
643
|
+
|
|
654
644
|
graph.on(NodeEvent.CLICK, (evt) => {
|
|
655
645
|
gobj_send_event(gobj, "EV_NODE_CLICK", {evt: evt}, gobj);
|
|
656
646
|
});
|
package/src/c_yui_json_graph.js
CHANGED
|
@@ -47,6 +47,12 @@ import {
|
|
|
47
47
|
} from "@yuneta/gobj-js";
|
|
48
48
|
|
|
49
49
|
import {yui_toolbar} from "./yui_toolbar.js";
|
|
50
|
+
import {
|
|
51
|
+
yui_graph_camera_items,
|
|
52
|
+
yui_graph_fold_items,
|
|
53
|
+
yui_graph_refresh_item,
|
|
54
|
+
yui_graph_update_zoom,
|
|
55
|
+
} from "./yui_graph_camera.js";
|
|
50
56
|
|
|
51
57
|
import {t} from "i18next";
|
|
52
58
|
|
|
@@ -454,30 +460,6 @@ function destroy_ui(gobj)
|
|
|
454
460
|
}
|
|
455
461
|
}
|
|
456
462
|
|
|
457
|
-
/************************************************************
|
|
458
|
-
* One fold button. `open` rotates the chevron to the open
|
|
459
|
-
* state, mirroring the per-node toggle of the lazy tree.
|
|
460
|
-
************************************************************/
|
|
461
|
-
function graph_fold_button(gobj, event_name, label_key, open)
|
|
462
|
-
{
|
|
463
|
-
let icon_style = 'font-size:1.5em; color:inherit;' +
|
|
464
|
-
(open? ' transform: rotate(90deg);': '');
|
|
465
|
-
|
|
466
|
-
return ['button', {class: `button ${event_name}`,
|
|
467
|
-
type: 'button',
|
|
468
|
-
style: {height: gobj_read_attr(gobj, "wide"), width: '2.5em'},
|
|
469
|
-
title: t(label_key), 'data-i18n-title': label_key,
|
|
470
|
-
'aria-label': t(label_key), 'data-i18n-aria-label': label_key},
|
|
471
|
-
['i', {style: icon_style, class: 'yi-chevron-right'}],
|
|
472
|
-
{
|
|
473
|
-
click: (evt) => {
|
|
474
|
-
evt.stopPropagation();
|
|
475
|
-
gobj_send_event(gobj, event_name, {}, gobj);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
];
|
|
479
|
-
}
|
|
480
|
-
|
|
481
463
|
/************************************************************
|
|
482
464
|
* Toolbar
|
|
483
465
|
************************************************************/
|
|
@@ -544,38 +526,13 @@ function make_toolbar(gobj)
|
|
|
544
526
|
];
|
|
545
527
|
let center_items = [];
|
|
546
528
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
for(let item of c_icons) {
|
|
556
|
-
let icon_name = item[0];
|
|
557
|
-
let event_name = item[1];
|
|
558
|
-
let disabled = item[2];
|
|
559
|
-
|
|
560
|
-
let button = {
|
|
561
|
-
class: `button ${event_name}`,
|
|
562
|
-
style: {height: toolbar_wide, width: '2.5em'}
|
|
563
|
-
};
|
|
564
|
-
if(disabled) {
|
|
565
|
-
button.disabled = true;
|
|
566
|
-
}
|
|
567
|
-
center_items.push(
|
|
568
|
-
['button', button,
|
|
569
|
-
['i', {style: 'font-size:1.5em; color:inherit;', class: icon_name}],
|
|
570
|
-
{
|
|
571
|
-
click: (evt) => {
|
|
572
|
-
evt.stopPropagation();
|
|
573
|
-
gobj_send_event(gobj, event_name, {evt: evt}, gobj);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
]
|
|
577
|
-
);
|
|
578
|
-
}
|
|
529
|
+
/*
|
|
530
|
+
* The camera and the fold pair come from yui_graph_camera.js: the
|
|
531
|
+
* same drawings the gobj tree uses, because the two graphs sit in
|
|
532
|
+
* the same console and a copied toolbar drifts.
|
|
533
|
+
*/
|
|
534
|
+
center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide);
|
|
535
|
+
center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
|
|
579
536
|
|
|
580
537
|
/*
|
|
581
538
|
* Right: fold the tree.
|
|
@@ -616,11 +573,8 @@ function make_toolbar(gobj)
|
|
|
616
573
|
}]
|
|
617
574
|
);
|
|
618
575
|
|
|
619
|
-
let right_items = [
|
|
620
|
-
|
|
621
|
-
graph_fold_button(gobj, "EV_EXPAND_ALL", "expand all", true),
|
|
622
|
-
graph_fold_button(gobj, "EV_COLLAPSE_ALL", "collapse all", false),
|
|
623
|
-
];
|
|
576
|
+
let right_items = [$layout_select].concat(
|
|
577
|
+
yui_graph_fold_items(gobj, toolbar_wide));
|
|
624
578
|
|
|
625
579
|
const $toolbar = yui_toolbar({}, [
|
|
626
580
|
['div', {class: 'yui-horizontal-toolbar-section left'}, left_items],
|
|
@@ -677,6 +631,15 @@ function build_graph(gobj)
|
|
|
677
631
|
gobj_send_event(gobj, "EV_NODE_CLICK", {evt: evt}, gobj);
|
|
678
632
|
});
|
|
679
633
|
|
|
634
|
+
/* ONE hook for the readout, the same one the treedb graph uses:
|
|
635
|
+
* G6 fires it on any camera change, and the WHEEL is a camera
|
|
636
|
+
* change that passes through no action of ours — patching the text
|
|
637
|
+
* node from each zoom action would have left the readout lying
|
|
638
|
+
* after every notch. */
|
|
639
|
+
graph.on('aftertransform', () => {
|
|
640
|
+
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
641
|
+
});
|
|
642
|
+
|
|
680
643
|
graph.on(CanvasEvent.CLICK, (evt) => {
|
|
681
644
|
// Canvas click
|
|
682
645
|
});
|
|
@@ -1071,6 +1034,7 @@ function load_json(gobj)
|
|
|
1071
1034
|
if(!preserve_view) {
|
|
1072
1035
|
graph.fitView();
|
|
1073
1036
|
}
|
|
1037
|
+
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
1074
1038
|
});
|
|
1075
1039
|
}
|
|
1076
1040
|
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* yui_graph_camera.js
|
|
3
|
+
*
|
|
4
|
+
* The camera and fold controls a G6 graph puts in a DOM toolbar,
|
|
5
|
+
* built in ONE place.
|
|
6
|
+
*
|
|
7
|
+
* This module exists because the same action was drawn three
|
|
8
|
+
* different ways across sibling views — actual size as a bare
|
|
9
|
+
* magnifier here and as `1:1` there, fit as `arrows-to-eye` here and
|
|
10
|
+
* as corner brackets there, fold as chevrons here and as an eye
|
|
11
|
+
* there. A user sees two of these graphs side by side in the same
|
|
12
|
+
* console, so a copied-and-drifted toolbar is not a detail.
|
|
13
|
+
*
|
|
14
|
+
* The VOCABULARY is not invented here. It is the one
|
|
15
|
+
* `c_g6_nodes_tree.js` settled on for the treedb graph, which draws
|
|
16
|
+
* its toolbar as a G6 plugin over an SVG sprite and therefore cannot
|
|
17
|
+
* share this code — only its decisions:
|
|
18
|
+
*
|
|
19
|
+
* zoom in / zoom out magnifiers
|
|
20
|
+
* zoom level a READOUT, because without it "1:1" is
|
|
21
|
+
* a jump to a value nobody was told
|
|
22
|
+
* fit corner brackets (`yi-fit`, the same
|
|
23
|
+
* path as the sprite's `g6-icon-fit`)
|
|
24
|
+
* actual size the WRITTEN `1:1` — the action is
|
|
25
|
+
* `zoomTo(1)`, and actual size is written
|
|
26
|
+
* in every editor that offers it, never
|
|
27
|
+
* drawn, because there is no glyph for it
|
|
28
|
+
*
|
|
29
|
+
* Fold is the one the treedb graph does not have, so its vocabulary
|
|
30
|
+
* comes from the other side of the family: the lazy tree viewer's own
|
|
31
|
+
* toolbar (`c_yui_json.js`) uses chevrons — open pointing down, closed
|
|
32
|
+
* pointing right — which is also the direction the per-node handles
|
|
33
|
+
* speak. An eye means show/hide, which is a different idea.
|
|
34
|
+
*
|
|
35
|
+
* Copyright (c) 2026, ArtGins.
|
|
36
|
+
* All Rights Reserved.
|
|
37
|
+
***********************************************************************/
|
|
38
|
+
import {createElement2, gobj_send_event} from "@yuneta/gobj-js";
|
|
39
|
+
|
|
40
|
+
import {t} from "i18next";
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
/************************************************************
|
|
44
|
+
* One icon button that fires `event_name`.
|
|
45
|
+
************************************************************/
|
|
46
|
+
function camera_button(gobj, icon, event_name, label_key, wide, extra_style)
|
|
47
|
+
{
|
|
48
|
+
return ['button', {class: `button ${event_name}`, type: 'button',
|
|
49
|
+
style: {height: wide, width: '2.5em'},
|
|
50
|
+
title: t(label_key), 'data-i18n-title': label_key,
|
|
51
|
+
'aria-label': t(label_key), 'data-i18n-aria-label': label_key},
|
|
52
|
+
['i', {style: 'font-size:1.5em; color:inherit;' + (extra_style || ''),
|
|
53
|
+
class: icon}],
|
|
54
|
+
{
|
|
55
|
+
click: (evt) => {
|
|
56
|
+
evt.stopPropagation();
|
|
57
|
+
gobj_send_event(gobj, event_name, {evt: evt}, gobj);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/************************************************************
|
|
64
|
+
* The current zoom as a percentage.
|
|
65
|
+
************************************************************/
|
|
66
|
+
export function yui_graph_zoom_text(graph)
|
|
67
|
+
{
|
|
68
|
+
if(!graph) {
|
|
69
|
+
return "100%";
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
return Math.round(graph.getZoom() * 100) + "%";
|
|
73
|
+
} catch(e) {
|
|
74
|
+
return "100%";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/************************************************************
|
|
79
|
+
* Repaint the readout of a container that carries one.
|
|
80
|
+
*
|
|
81
|
+
* Call it from G6's `aftertransform`, which is the ONE hook
|
|
82
|
+
* that also covers the WHEEL — a camera change that passes
|
|
83
|
+
* through no action, so repainting from each zoom action
|
|
84
|
+
* leaves the number lying after every notch.
|
|
85
|
+
************************************************************/
|
|
86
|
+
export function yui_graph_update_zoom($container, graph)
|
|
87
|
+
{
|
|
88
|
+
if(!$container) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
let $readout = $container.querySelector('.GRAPH_ZOOM_LEVEL');
|
|
92
|
+
if(!$readout) {
|
|
93
|
+
return; /* a toolbar without a readout: nobody to report to */
|
|
94
|
+
}
|
|
95
|
+
$readout.textContent = yui_graph_zoom_text(graph);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/************************************************************
|
|
99
|
+
* The camera cluster: zoom in, zoom out, readout, fit,
|
|
100
|
+
* actual size. Returns createElement2 specs, in order.
|
|
101
|
+
*
|
|
102
|
+
* `wide` is the host toolbar's button height.
|
|
103
|
+
************************************************************/
|
|
104
|
+
export function yui_graph_camera_items(gobj, graph, wide)
|
|
105
|
+
{
|
|
106
|
+
return [
|
|
107
|
+
camera_button(gobj, "yi-magnifying-glass-plus", "EV_ZOOM_IN", "zoom in", wide),
|
|
108
|
+
camera_button(gobj, "yi-magnifying-glass-minus", "EV_ZOOM_OUT", "zoom out", wide),
|
|
109
|
+
['span', {class: 'GRAPH_ZOOM_LEVEL is-flex is-align-items-center px-2 has-text-grey',
|
|
110
|
+
style: 'font-size:.85rem; min-width:3.5em; justify-content:center;',
|
|
111
|
+
title: t('zoom level'), 'data-i18n-title': 'zoom level'},
|
|
112
|
+
yui_graph_zoom_text(graph)],
|
|
113
|
+
camera_button(gobj, "yi-fit", "EV_CENTER", "auto fit", wide),
|
|
114
|
+
/* Written, not drawn — see the header. */
|
|
115
|
+
['button', {class: 'button EV_ZOOM_RESET', type: 'button',
|
|
116
|
+
style: {height: wide, width: '2.5em'},
|
|
117
|
+
title: t('actual size'), 'data-i18n-title': 'actual size',
|
|
118
|
+
'aria-label': t('actual size'), 'data-i18n-aria-label': 'actual size'},
|
|
119
|
+
['span', {style: 'font-weight:700;'}, '1:1'],
|
|
120
|
+
{
|
|
121
|
+
click: (evt) => {
|
|
122
|
+
evt.stopPropagation();
|
|
123
|
+
gobj_send_event(gobj, "EV_ZOOM_RESET", {evt: evt}, gobj);
|
|
124
|
+
}
|
|
125
|
+
}]
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/************************************************************
|
|
130
|
+
* The fold pair: expand all, collapse all. Chevrons, the
|
|
131
|
+
* open one rotated down — the same pair the lazy tree
|
|
132
|
+
* viewer's toolbar uses, and the same direction the
|
|
133
|
+
* per-node handles speak.
|
|
134
|
+
************************************************************/
|
|
135
|
+
export function yui_graph_fold_items(gobj, wide)
|
|
136
|
+
{
|
|
137
|
+
return [
|
|
138
|
+
camera_button(gobj, "yi-chevron-right", "EV_EXPAND_ALL", "expand all", wide,
|
|
139
|
+
' transform: rotate(90deg);'),
|
|
140
|
+
camera_button(gobj, "yi-chevron-right", "EV_COLLAPSE_ALL", "collapse all", wide),
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/************************************************************
|
|
145
|
+
* A refresh button, for the graphs that offer one.
|
|
146
|
+
************************************************************/
|
|
147
|
+
export function yui_graph_refresh_item(gobj, wide)
|
|
148
|
+
{
|
|
149
|
+
return camera_button(gobj, "yi-arrows-rotate", "EV_REFRESH", "refresh", wide);
|
|
150
|
+
}
|
package/src/yui_icons.css
CHANGED
|
@@ -376,3 +376,13 @@
|
|
|
376
376
|
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath d='M208 80c0-26.5 21.5-48 48-48l64 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-8 0 0 40 152 0c30.9 0 56 25.1 56 56l0 32 8 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-64 0c-26.5 0-48-21.5-48-48l0-64c0-26.5 21.5-48 48-48l8 0 0-32c0-4.4-3.6-8-8-8l-152 0 0 40 8 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-64 0c-26.5 0-48-21.5-48-48l0-64c0-26.5 21.5-48 48-48l8 0 0-40-152 0c-4.4 0-8 3.6-8 8l0 32 8 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-64 0c-26.5 0-48-21.5-48-48l0-64c0-26.5 21.5-48 48-48l8 0 0-32c0-30.9 25.1-56 56-56l152 0 0-40-8 0c-26.5 0-48-21.5-48-48l0-64z'/%3E%3C/svg%3E");
|
|
377
377
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath d='M208 80c0-26.5 21.5-48 48-48l64 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-8 0 0 40 152 0c30.9 0 56 25.1 56 56l0 32 8 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-64 0c-26.5 0-48-21.5-48-48l0-64c0-26.5 21.5-48 48-48l8 0 0-32c0-4.4-3.6-8-8-8l-152 0 0 40 8 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-64 0c-26.5 0-48-21.5-48-48l0-64c0-26.5 21.5-48 48-48l8 0 0-40-152 0c-4.4 0-8 3.6-8 8l0 32 8 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-64 0c-26.5 0-48-21.5-48-48l0-64c0-26.5 21.5-48 48-48l8 0 0-32c0-30.9 25.1-56 56-56l152 0 0-40-8 0c-26.5 0-48-21.5-48-48l0-64z'/%3E%3C/svg%3E");
|
|
378
378
|
}
|
|
379
|
+
|
|
380
|
+
/* The camera's "fit" bracket, the SAME glyph the treedb graph's SVG
|
|
381
|
+
* sprite uses for the same action (`g6-icon-fit` in lib_icons.js).
|
|
382
|
+
* One action, one drawing: the JSON graph used to say it with
|
|
383
|
+
* `yi-arrows-to-eye`, which is a different picture for the same
|
|
384
|
+
* button in a sibling view. */
|
|
385
|
+
.yi-fit::before {
|
|
386
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32S0 334.3 0 352l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z'/%3E%3C/svg%3E");
|
|
387
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32S0 334.3 0 352l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z'/%3E%3C/svg%3E");
|
|
388
|
+
}
|