@yuneta/gobj-ui 7.23.18 → 7.23.20
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/dist/gobj-ui.cjs.js +819 -380
- package/dist/gobj-ui.es.js +811 -372
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +135 -3
- package/src/c_yui_gobj_tree_js.js +185 -7
- package/src/c_yui_json_graph.js +269 -24
- package/src/json_view_helpers.js +67 -0
- package/src/json_view_helpers.test.js +67 -0
- package/src/lib_icons.js +7 -0
- package/src/yui_graph_camera.js +95 -0
package/package.json
CHANGED
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -85,6 +85,7 @@ import {
|
|
|
85
85
|
|
|
86
86
|
import {node_label} from "./treedb_node_label.js";
|
|
87
87
|
import {delete_impact} from "./delete_impact.js";
|
|
88
|
+
import {yui_graph_center_on} from "./yui_graph_camera.js";
|
|
88
89
|
|
|
89
90
|
import {
|
|
90
91
|
BaseLayout,
|
|
@@ -295,6 +296,11 @@ SDATA_END()
|
|
|
295
296
|
];
|
|
296
297
|
|
|
297
298
|
let PRIVATE_DATA = {
|
|
299
|
+
/*---------------- camera anchor ----------------*/
|
|
300
|
+
anchor_id: "", // the node the camera keeps in the middle
|
|
301
|
+
anchor_state: "off", // "off" | "arming" | "on"
|
|
302
|
+
last_zoom: 1, // tells a wheel zoom from a drag in aftertransform
|
|
303
|
+
|
|
298
304
|
_xy: 100,
|
|
299
305
|
_edge_seq: 0,
|
|
300
306
|
_history_paused: false,
|
|
@@ -833,6 +839,27 @@ function configure_events(gobj)
|
|
|
833
839
|
update_node_icon_position(gobj);
|
|
834
840
|
update_link_icon_position(gobj);
|
|
835
841
|
update_zoom_readout(gobj);
|
|
842
|
+
|
|
843
|
+
/*
|
|
844
|
+
* A ZOOM re-centres on the anchor; a PAN does not.
|
|
845
|
+
* `aftertransform` fires for both, and an anchor that also
|
|
846
|
+
* answered a drag would make the graph impossible to move
|
|
847
|
+
* while it is set. The zoom LEVEL is what tells them apart:
|
|
848
|
+
* it changes on a wheel notch and never on a drag.
|
|
849
|
+
*/
|
|
850
|
+
let z = priv.last_zoom;
|
|
851
|
+
try {
|
|
852
|
+
z = priv.graph.getZoom();
|
|
853
|
+
} catch(e) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if(z === priv.last_zoom) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
priv.last_zoom = z;
|
|
860
|
+
if(priv.anchor_state === "on" && priv.anchor_id) {
|
|
861
|
+
yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
862
|
+
}
|
|
836
863
|
});
|
|
837
864
|
|
|
838
865
|
// Re-render G6 toolbars when language changes
|
|
@@ -1236,6 +1263,19 @@ function configure_toolbar(gobj)
|
|
|
1236
1263
|
* Actual size is WRITTEN in every editor that offers
|
|
1237
1264
|
* it, never drawn: there is no glyph for it. */
|
|
1238
1265
|
{ text: '1:1', value: 'reset', className: 'EV_ZOOM_RESET', title: t('actual size') },
|
|
1266
|
+
/* Pick one node and every zoom leaves it in the
|
|
1267
|
+
* middle. The pressed classes are baked in HERE and
|
|
1268
|
+
* not set afterwards, for the same reason the
|
|
1269
|
+
* selection-mode button bakes its own: this list is
|
|
1270
|
+
* rebuilt asynchronously, and a button told after
|
|
1271
|
+
* the rebuild came back OFF. */
|
|
1272
|
+
{
|
|
1273
|
+
id: 'g6-icon-anchor', value: 'anchor',
|
|
1274
|
+
className: 'EV_TOGGLE_ANCHOR' +
|
|
1275
|
+
(priv.anchor_state !== "off"? ' pressed_state' : '') +
|
|
1276
|
+
(priv.anchor_state === "arming"? ' color_pending_state' : ''),
|
|
1277
|
+
title: anchor_title(priv.anchor_state)
|
|
1278
|
+
},
|
|
1239
1279
|
];
|
|
1240
1280
|
|
|
1241
1281
|
/* Only in edition, because that is where a selection
|
|
@@ -1288,6 +1328,9 @@ function configure_toolbar(gobj)
|
|
|
1288
1328
|
case 'reset':
|
|
1289
1329
|
gobj_send_event(gobj, "EV_ZOOM_RESET", {}, gobj);
|
|
1290
1330
|
break;
|
|
1331
|
+
case 'anchor':
|
|
1332
|
+
gobj_send_event(gobj, "EV_TOGGLE_ANCHOR", {}, gobj);
|
|
1333
|
+
break;
|
|
1291
1334
|
case 'auto-fit':
|
|
1292
1335
|
gobj_send_event(gobj, "EV_AUTO_FIT", {}, gobj);
|
|
1293
1336
|
break;
|
|
@@ -2839,7 +2882,7 @@ async function graph_zoom_in(gobj)
|
|
|
2839
2882
|
let graph = priv.graph;
|
|
2840
2883
|
|
|
2841
2884
|
const z = graph.getZoom();
|
|
2842
|
-
|
|
2885
|
+
after_camera(gobj, graph.zoomTo(z * 1.1));
|
|
2843
2886
|
}
|
|
2844
2887
|
|
|
2845
2888
|
async function graph_zoom_out(gobj)
|
|
@@ -2848,7 +2891,7 @@ async function graph_zoom_out(gobj)
|
|
|
2848
2891
|
let graph = priv.graph;
|
|
2849
2892
|
|
|
2850
2893
|
const z = graph.getZoom();
|
|
2851
|
-
|
|
2894
|
+
after_camera(gobj, graph.zoomTo(z * 0.9));
|
|
2852
2895
|
}
|
|
2853
2896
|
|
|
2854
2897
|
async function graph_render(gobj)
|
|
@@ -7502,7 +7545,79 @@ function ac_zoom_reset(gobj, event, kw, src)
|
|
|
7502
7545
|
let priv = gobj.priv;
|
|
7503
7546
|
let graph = priv.graph;
|
|
7504
7547
|
|
|
7505
|
-
|
|
7548
|
+
/*
|
|
7549
|
+
* With an anchor, actual size means "this node, life size";
|
|
7550
|
+
* without one it leaves the camera where it was, as before.
|
|
7551
|
+
*/
|
|
7552
|
+
if(priv.anchor_state === "on" && priv.anchor_id) {
|
|
7553
|
+
after_camera(gobj, graph.zoomTo(1));
|
|
7554
|
+
} else {
|
|
7555
|
+
graph.zoomTo(1);
|
|
7556
|
+
}
|
|
7557
|
+
return 0;
|
|
7558
|
+
}
|
|
7559
|
+
|
|
7560
|
+
/************************************************************
|
|
7561
|
+
* What the anchor button says it will do next.
|
|
7562
|
+
************************************************************/
|
|
7563
|
+
function anchor_title(state)
|
|
7564
|
+
{
|
|
7565
|
+
if(state === "arming") {
|
|
7566
|
+
return t('click the element to centre on');
|
|
7567
|
+
}
|
|
7568
|
+
if(state === "on") {
|
|
7569
|
+
return t('centred: click to release');
|
|
7570
|
+
}
|
|
7571
|
+
return t('anchor view');
|
|
7572
|
+
}
|
|
7573
|
+
|
|
7574
|
+
/************************************************************
|
|
7575
|
+
* Every camera action ends the same way once an anchor is
|
|
7576
|
+
* set: the thing you were reading goes back to the middle.
|
|
7577
|
+
*
|
|
7578
|
+
* Chained on the promise G6 returns rather than run beside it
|
|
7579
|
+
* -- centring before the zoom has finished centres the OLD
|
|
7580
|
+
* view, and the reader watches the node slide away and come
|
|
7581
|
+
* back.
|
|
7582
|
+
************************************************************/
|
|
7583
|
+
function after_camera(gobj, moved)
|
|
7584
|
+
{
|
|
7585
|
+
let priv = gobj.priv;
|
|
7586
|
+
|
|
7587
|
+
if(priv.anchor_state !== "on" || !priv.anchor_id) {
|
|
7588
|
+
return;
|
|
7589
|
+
}
|
|
7590
|
+
Promise.resolve(moved).then(function() {
|
|
7591
|
+
yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
7592
|
+
}).catch(function() {
|
|
7593
|
+
/* a camera move that never finished has nothing to centre */
|
|
7594
|
+
});
|
|
7595
|
+
}
|
|
7596
|
+
|
|
7597
|
+
/************************************************************
|
|
7598
|
+
* Arm the anchor, or let it go.
|
|
7599
|
+
*
|
|
7600
|
+
* Three states and one button, so a press ADVANCES: with no
|
|
7601
|
+
* target it starts waiting for one, and with a target it
|
|
7602
|
+
* releases. Pressing it while it waits is a cancel, which is
|
|
7603
|
+
* what somebody who pressed it by mistake will try.
|
|
7604
|
+
*
|
|
7605
|
+
* Unlike the DOM toolbars, the button is not repainted in
|
|
7606
|
+
* place: this toolbar is a G6 plugin that rebuilds its whole
|
|
7607
|
+
* item list, and the pressed classes are baked into that list.
|
|
7608
|
+
************************************************************/
|
|
7609
|
+
function ac_toggle_anchor(gobj, event, kw, src)
|
|
7610
|
+
{
|
|
7611
|
+
let priv = gobj.priv;
|
|
7612
|
+
|
|
7613
|
+
if(priv.anchor_state === "off") {
|
|
7614
|
+
priv.anchor_state = "arming";
|
|
7615
|
+
} else {
|
|
7616
|
+
priv.anchor_state = "off";
|
|
7617
|
+
priv.anchor_id = "";
|
|
7618
|
+
}
|
|
7619
|
+
|
|
7620
|
+
update_toolbar(gobj);
|
|
7506
7621
|
return 0;
|
|
7507
7622
|
}
|
|
7508
7623
|
|
|
@@ -7750,6 +7865,21 @@ function ac_node_click(gobj, event, kw, src)
|
|
|
7750
7865
|
let graph = priv.graph;
|
|
7751
7866
|
let node_id = kw.evt.target.id;
|
|
7752
7867
|
|
|
7868
|
+
/*
|
|
7869
|
+
* A waiting anchor TAKES the click, before selection, before
|
|
7870
|
+
* ports, before linking: it is what the reader armed the button
|
|
7871
|
+
* for, and letting the same press also do the graph's own thing
|
|
7872
|
+
* would make picking a target a side effect of doing something
|
|
7873
|
+
* else.
|
|
7874
|
+
*/
|
|
7875
|
+
if(priv.anchor_state === "arming") {
|
|
7876
|
+
priv.anchor_id = node_id;
|
|
7877
|
+
priv.anchor_state = "on";
|
|
7878
|
+
update_toolbar(gobj);
|
|
7879
|
+
yui_graph_center_on(graph, node_id);
|
|
7880
|
+
return 0;
|
|
7881
|
+
}
|
|
7882
|
+
|
|
7753
7883
|
try {
|
|
7754
7884
|
let nodedata = graph.getNodeData(node_id);
|
|
7755
7885
|
if(nodedata && nodedata.data && nodedata.data.desc) {
|
|
@@ -8061,6 +8191,7 @@ function create_gclass(gclass_name)
|
|
|
8061
8191
|
["EV_ZOOM_IN", ac_zoom_in, null],
|
|
8062
8192
|
["EV_ZOOM_OUT", ac_zoom_out, null],
|
|
8063
8193
|
["EV_ZOOM_RESET", ac_zoom_reset, null],
|
|
8194
|
+
["EV_TOGGLE_ANCHOR", ac_toggle_anchor, null],
|
|
8064
8195
|
["EV_TOGGLE_TOOLBARS", ac_toggle_toolbars, null],
|
|
8065
8196
|
["EV_AUTO_FIT", ac_auto_fit, null],
|
|
8066
8197
|
["EV_ZOOM_SELECTION", ac_zoom_selection, null],
|
|
@@ -8110,6 +8241,7 @@ function create_gclass(gclass_name)
|
|
|
8110
8241
|
["EV_ZOOM_IN", 0],
|
|
8111
8242
|
["EV_ZOOM_OUT", 0],
|
|
8112
8243
|
["EV_ZOOM_RESET", 0],
|
|
8244
|
+
["EV_TOGGLE_ANCHOR", 0],
|
|
8113
8245
|
["EV_TOGGLE_TOOLBARS", 0],
|
|
8114
8246
|
["EV_AUTO_FIT", 0],
|
|
8115
8247
|
["EV_ZOOM_SELECTION", 0],
|
|
@@ -42,6 +42,9 @@ import {
|
|
|
42
42
|
import {yui_toolbar} from "./yui_toolbar.js";
|
|
43
43
|
import {
|
|
44
44
|
yui_graph_camera_items,
|
|
45
|
+
yui_graph_anchor_item,
|
|
46
|
+
yui_graph_update_anchor,
|
|
47
|
+
yui_graph_center_on,
|
|
45
48
|
yui_graph_fold_items,
|
|
46
49
|
yui_graph_refresh_item,
|
|
47
50
|
yui_graph_update_zoom,
|
|
@@ -100,6 +103,19 @@ let PRIVATE_DATA = {
|
|
|
100
103
|
collapse_state: null, // map of full_name -> "collapsed" | "expanded"
|
|
101
104
|
resize_observer: null, // ResizeObserver on the canvas mount element
|
|
102
105
|
_resize_raf: 0, // rAF id debouncing resize -> EV_RESIZE
|
|
106
|
+
|
|
107
|
+
/*---------------- camera anchor ----------------*/
|
|
108
|
+
/*
|
|
109
|
+
* NOT `pending_anchor`, which is a different job with the same
|
|
110
|
+
* word: that one keeps a node at the SAME SCREEN SPOT across a
|
|
111
|
+
* re-render, so expanding a branch does not throw the reader off
|
|
112
|
+
* the node they expanded. This one keeps a node in the MIDDLE
|
|
113
|
+
* across a ZOOM, and the reader chooses it from the toolbar.
|
|
114
|
+
*/
|
|
115
|
+
anchor_id: "", // the node the camera keeps in the middle
|
|
116
|
+
anchor_name: "", // its full_name, which survives a rebuild
|
|
117
|
+
anchor_state: "off", // "off" | "arming" | "on"
|
|
118
|
+
last_zoom: 1, // tells a wheel zoom from a drag in aftertransform
|
|
103
119
|
};
|
|
104
120
|
|
|
105
121
|
let __gclass__ = null;
|
|
@@ -557,6 +573,7 @@ function make_toolbar(gobj)
|
|
|
557
573
|
* drawings have to be the same drawings.
|
|
558
574
|
*/
|
|
559
575
|
center_items = yui_graph_camera_items(gobj, priv.graph, toolbar_wide);
|
|
576
|
+
center_items.push(yui_graph_anchor_item(gobj, toolbar_wide));
|
|
560
577
|
center_items.push(yui_graph_refresh_item(gobj, toolbar_wide));
|
|
561
578
|
|
|
562
579
|
/*
|
|
@@ -644,6 +661,27 @@ function build_graph(gobj)
|
|
|
644
661
|
* wheel notch passes through no action of ours. */
|
|
645
662
|
graph.on('aftertransform', () => {
|
|
646
663
|
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
664
|
+
|
|
665
|
+
/*
|
|
666
|
+
* A ZOOM re-centres on the anchor; a PAN does not.
|
|
667
|
+
* `aftertransform` fires for both, and an anchor that also
|
|
668
|
+
* answered a drag would make the graph impossible to move
|
|
669
|
+
* while it is set. The zoom LEVEL is what tells them apart:
|
|
670
|
+
* it changes on a wheel notch and never on a drag.
|
|
671
|
+
*/
|
|
672
|
+
let z = priv.last_zoom;
|
|
673
|
+
try {
|
|
674
|
+
z = priv.graph.getZoom();
|
|
675
|
+
} catch(e) {
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
if(z === priv.last_zoom) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
priv.last_zoom = z;
|
|
682
|
+
if(priv.anchor_state === "on" && priv.anchor_id) {
|
|
683
|
+
yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
684
|
+
}
|
|
647
685
|
});
|
|
648
686
|
|
|
649
687
|
graph.on(NodeEvent.CLICK, (evt) => {
|
|
@@ -1071,13 +1109,40 @@ function load_tree(gobj)
|
|
|
1071
1109
|
build_gobj_nodes(gobj, yuno, nodes, edges, null, true, layout_cfg.compact);
|
|
1072
1110
|
|
|
1073
1111
|
if(nodes.length > 0) {
|
|
1112
|
+
let root_id = nodes[0].id; /* built before its children */
|
|
1074
1113
|
graph.setData({nodes: nodes, edges: edges});
|
|
1075
1114
|
graph.render().then(() => {
|
|
1115
|
+
/* Ids are generated per build: point the camera anchor at
|
|
1116
|
+
* its gobj again before anybody asks the camera to go
|
|
1117
|
+
* there. (`anchor` below is the OTHER one -- see the
|
|
1118
|
+
* note in PRIVATE_DATA.) */
|
|
1119
|
+
reanchor(gobj);
|
|
1120
|
+
|
|
1076
1121
|
if(anchor) {
|
|
1077
1122
|
restore_view_anchored(gobj, anchor);
|
|
1078
|
-
|
|
1079
|
-
graph.fitView();
|
|
1123
|
+
return;
|
|
1080
1124
|
}
|
|
1125
|
+
if(preserve_view) {
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/*
|
|
1130
|
+
* ACTUAL SIZE, not fit. A tree of anything real does not
|
|
1131
|
+
* fit at a zoom anybody can read, so fitting answered
|
|
1132
|
+
* "where is everything" when the question on opening is
|
|
1133
|
+
* "what does this say".
|
|
1134
|
+
*
|
|
1135
|
+
* Centred on the ANCHOR if there is one, else on the ROOT
|
|
1136
|
+
* -- never parked at the layout's origin, which is a
|
|
1137
|
+
* corner with nothing in it.
|
|
1138
|
+
*/
|
|
1139
|
+
Promise.resolve(graph.zoomTo(1)).then(() => {
|
|
1140
|
+
if(!yui_graph_center_on(graph, priv.anchor_id)) {
|
|
1141
|
+
yui_graph_center_on(graph, root_id);
|
|
1142
|
+
}
|
|
1143
|
+
priv.last_zoom = 1;
|
|
1144
|
+
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
1145
|
+
});
|
|
1081
1146
|
});
|
|
1082
1147
|
}
|
|
1083
1148
|
}
|
|
@@ -1861,6 +1926,91 @@ function ac_collapse_all(gobj, event, kw, src)
|
|
|
1861
1926
|
return 0;
|
|
1862
1927
|
}
|
|
1863
1928
|
|
|
1929
|
+
/************************************************************
|
|
1930
|
+
* Every camera action ends the same way once an anchor is
|
|
1931
|
+
* set: the thing you were reading goes back to the middle.
|
|
1932
|
+
*
|
|
1933
|
+
* Chained on the promise G6 returns rather than run beside it
|
|
1934
|
+
* -- centring before the zoom has finished centres the OLD
|
|
1935
|
+
* view, and the reader watches the node slide away and come
|
|
1936
|
+
* back.
|
|
1937
|
+
************************************************************/
|
|
1938
|
+
function after_camera(gobj, moved)
|
|
1939
|
+
{
|
|
1940
|
+
let priv = gobj.priv;
|
|
1941
|
+
|
|
1942
|
+
if(priv.anchor_state !== "on" || !priv.anchor_id) {
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
Promise.resolve(moved).then(function() {
|
|
1946
|
+
yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
1947
|
+
}).catch(function() {
|
|
1948
|
+
/* a camera move that never finished has nothing to centre */
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
/************************************************************
|
|
1953
|
+
* Point the anchor back at its node after a rebuild.
|
|
1954
|
+
*
|
|
1955
|
+
* Node ids are generated per build, so the id the reader chose
|
|
1956
|
+
* does not survive an expand, a collapse or a refresh -- the
|
|
1957
|
+
* `full_name` does, and it is what the anchor really means:
|
|
1958
|
+
* "this gobj", not "this card".
|
|
1959
|
+
*
|
|
1960
|
+
* A gobj no longer drawn (its branch got collapsed, or it went
|
|
1961
|
+
* away) releases the anchor rather than leaving a button that
|
|
1962
|
+
* claims to be locked onto something invisible.
|
|
1963
|
+
************************************************************/
|
|
1964
|
+
function reanchor(gobj)
|
|
1965
|
+
{
|
|
1966
|
+
let priv = gobj.priv;
|
|
1967
|
+
|
|
1968
|
+
if(priv.anchor_state !== "on" || !priv.anchor_name) {
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
let found = "";
|
|
1973
|
+
if(priv.node_by_id) {
|
|
1974
|
+
for(let id of Object.keys(priv.node_by_id)) {
|
|
1975
|
+
if(priv.node_by_id[id].full_name === priv.anchor_name) {
|
|
1976
|
+
found = id;
|
|
1977
|
+
break;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
priv.anchor_id = found;
|
|
1983
|
+
if(!found) {
|
|
1984
|
+
priv.anchor_state = "off";
|
|
1985
|
+
priv.anchor_name = "";
|
|
1986
|
+
}
|
|
1987
|
+
yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
/************************************************************
|
|
1991
|
+
* Arm the anchor, or let it go.
|
|
1992
|
+
*
|
|
1993
|
+
* Three states and one button, so a press ADVANCES: with no
|
|
1994
|
+
* target it starts waiting for one, and with a target it
|
|
1995
|
+
* releases. Pressing it while it waits is a cancel, which is
|
|
1996
|
+
* what somebody who pressed it by mistake will try.
|
|
1997
|
+
************************************************************/
|
|
1998
|
+
function ac_toggle_anchor(gobj, event, kw, src)
|
|
1999
|
+
{
|
|
2000
|
+
let priv = gobj.priv;
|
|
2001
|
+
|
|
2002
|
+
if(priv.anchor_state === "off") {
|
|
2003
|
+
priv.anchor_state = "arming";
|
|
2004
|
+
} else {
|
|
2005
|
+
priv.anchor_state = "off";
|
|
2006
|
+
priv.anchor_id = "";
|
|
2007
|
+
priv.anchor_name = "";
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
|
|
2011
|
+
return 0;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
1864
2014
|
/************************************************************
|
|
1865
2015
|
*
|
|
1866
2016
|
************************************************************/
|
|
@@ -1869,7 +2019,7 @@ function ac_zoom_in(gobj, event, kw, src)
|
|
|
1869
2019
|
let graph = gobj.priv.graph;
|
|
1870
2020
|
if(graph) {
|
|
1871
2021
|
let z = graph.getZoom();
|
|
1872
|
-
graph.zoomTo(z * 1.2);
|
|
2022
|
+
after_camera(gobj, graph.zoomTo(z * 1.2));
|
|
1873
2023
|
}
|
|
1874
2024
|
return 0;
|
|
1875
2025
|
}
|
|
@@ -1882,7 +2032,7 @@ function ac_zoom_out(gobj, event, kw, src)
|
|
|
1882
2032
|
let graph = gobj.priv.graph;
|
|
1883
2033
|
if(graph) {
|
|
1884
2034
|
let z = graph.getZoom();
|
|
1885
|
-
graph.zoomTo(z * 0.8);
|
|
2035
|
+
after_camera(gobj, graph.zoomTo(z * 0.8));
|
|
1886
2036
|
}
|
|
1887
2037
|
return 0;
|
|
1888
2038
|
}
|
|
@@ -1892,10 +2042,21 @@ function ac_zoom_out(gobj, event, kw, src)
|
|
|
1892
2042
|
************************************************************/
|
|
1893
2043
|
function ac_zoom_reset(gobj, event, kw, src)
|
|
1894
2044
|
{
|
|
1895
|
-
let
|
|
2045
|
+
let priv = gobj.priv;
|
|
2046
|
+
let graph = priv.graph;
|
|
2047
|
+
|
|
1896
2048
|
if(graph && graph.rendered) {
|
|
1897
|
-
|
|
1898
|
-
|
|
2049
|
+
/*
|
|
2050
|
+
* With an anchor, actual size means "this node, life size".
|
|
2051
|
+
* Without one it means the layout's origin, which is where
|
|
2052
|
+
* `1:1` always landed -- a corner nobody was looking at.
|
|
2053
|
+
*/
|
|
2054
|
+
if(priv.anchor_state === "on" && priv.anchor_id) {
|
|
2055
|
+
after_camera(gobj, graph.zoomTo(1));
|
|
2056
|
+
} else {
|
|
2057
|
+
graph.zoomTo(1);
|
|
2058
|
+
graph.translateTo([0, 0]);
|
|
2059
|
+
}
|
|
1899
2060
|
}
|
|
1900
2061
|
return 0;
|
|
1901
2062
|
}
|
|
@@ -1940,6 +2101,21 @@ function ac_node_click(gobj, event, kw, src)
|
|
|
1940
2101
|
return 0;
|
|
1941
2102
|
}
|
|
1942
2103
|
|
|
2104
|
+
/*
|
|
2105
|
+
* A waiting anchor TAKES the click: it is what the reader armed
|
|
2106
|
+
* the button for, and letting the same press also open the
|
|
2107
|
+
* popover would make picking a target a side effect of doing
|
|
2108
|
+
* something else.
|
|
2109
|
+
*/
|
|
2110
|
+
if(priv.anchor_state === "arming") {
|
|
2111
|
+
priv.anchor_id = node_id;
|
|
2112
|
+
priv.anchor_name = node_data.full_name || "";
|
|
2113
|
+
priv.anchor_state = "on";
|
|
2114
|
+
yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
|
|
2115
|
+
yui_graph_center_on(graph, node_id);
|
|
2116
|
+
return 0;
|
|
2117
|
+
}
|
|
2118
|
+
|
|
1943
2119
|
/*
|
|
1944
2120
|
* Show popover at click position
|
|
1945
2121
|
*/
|
|
@@ -2065,6 +2241,7 @@ function create_gclass(gclass_name)
|
|
|
2065
2241
|
["EV_ZOOM_RESET", ac_zoom_reset, null],
|
|
2066
2242
|
["EV_CENTER", ac_center, null],
|
|
2067
2243
|
["EV_NODE_CLICK", ac_node_click, null],
|
|
2244
|
+
["EV_TOGGLE_ANCHOR", ac_toggle_anchor, null],
|
|
2068
2245
|
["EV_RESIZE", ac_resize, null],
|
|
2069
2246
|
["EV_SHOW", ac_show, null],
|
|
2070
2247
|
["EV_HIDE", ac_hide, null],
|
|
@@ -2086,6 +2263,7 @@ function create_gclass(gclass_name)
|
|
|
2086
2263
|
["EV_ZOOM_RESET", 0],
|
|
2087
2264
|
["EV_CENTER", 0],
|
|
2088
2265
|
["EV_NODE_CLICK", 0],
|
|
2266
|
+
["EV_TOGGLE_ANCHOR", 0],
|
|
2089
2267
|
["EV_RESIZE", 0],
|
|
2090
2268
|
["EV_SHOW", 0],
|
|
2091
2269
|
["EV_HIDE", 0],
|