@yuneta/gobj-ui 7.23.24 → 7.23.25

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.
@@ -9679,7 +9679,7 @@ function yui_graph_update_anchor($container, state) {
9679
9679
  if (!$container) return;
9680
9680
  let $btn = $container.querySelector(".EV_TOGGLE_ANCHOR");
9681
9681
  if (!$btn) return;
9682
- set_pressed_state($container, ".EV_TOGGLE_ANCHOR", state !== "off");
9682
+ set_pressed_state($container, ".EV_TOGGLE_ANCHOR", state === "on");
9683
9683
  $btn.classList.toggle("color_pending_state", state === "arming");
9684
9684
  $btn.setAttribute("aria-pressed", state === "on" ? "true" : "false");
9685
9685
  let key = state === "arming" ? "click the element to centre on" : state === "on" ? "centred: click to release" : "anchor view";
@@ -9695,18 +9695,35 @@ function yui_graph_update_anchor($container, state) {
9695
9695
  * the zoom is what the reader asked for, the centring is what
9696
9696
  * keeps the thing they are reading under their eyes.
9697
9697
  *
9698
+ * The translate is computed and applied HERE rather than
9699
+ * delegated to `graph.focusElement()`, which is the obvious
9700
+ * call and does NOT work on these graphs. Measured: it
9701
+ * resolves, having computed the very offset wanted -- `[0,
9702
+ * 306]` for a card 306px above centre -- and the camera does
9703
+ * not move. `getPosition()` reads the same before and after,
9704
+ * and so does the card's box on screen. Three public calls do
9705
+ * move it, so that is what is used.
9706
+ *
9698
9707
  * Guarded on every side because it runs on every wheel notch:
9699
9708
  * a graph mid-rebuild, an anchor whose node went away with a
9700
- * fold, a G6 version without `focusElement` -- none of those is
9709
+ * fold, a G6 missing one of these calls -- none of those is
9701
9710
  * an error worth a log line per notch, they are all "there is
9702
9711
  * nothing to centre right now".
9703
9712
  ************************************************************/
9704
- function yui_graph_center_on(graph, node_id) {
9713
+ async function yui_graph_center_on(graph, node_id) {
9705
9714
  if (!graph || !node_id) return false;
9706
9715
  try {
9707
9716
  if (typeof graph.getElementData === "function" && !graph.getElementData(node_id)) return false;
9708
- if (typeof graph.focusElement !== "function") return false;
9709
- graph.focusElement(node_id);
9717
+ if (typeof graph.getElementPosition !== "function" || typeof graph.getViewportByCanvas !== "function" || typeof graph.getCanvasCenter !== "function" || typeof graph.translateTo !== "function") return false;
9718
+ for (let i = 0; i < 3; i++) {
9719
+ let at = graph.getViewportByCanvas(graph.getElementPosition(node_id));
9720
+ let want = graph.getCanvasCenter();
9721
+ let dx = want[0] - at[0];
9722
+ let dy = want[1] - at[1];
9723
+ if (Math.abs(dx) < 1 && Math.abs(dy) < 1) break;
9724
+ let pos = graph.getPosition();
9725
+ await graph.translateTo([pos[0] + dx, pos[1] + dy]);
9726
+ }
9710
9727
  return true;
9711
9728
  } catch (e) {
9712
9729
  return false;
@@ -11596,6 +11613,14 @@ function reanchor$1(gobj) {
11596
11613
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
11597
11614
  }
11598
11615
  /************************************************************
11616
+ * Centre on the anchor, one cycle after it was chosen.
11617
+ ************************************************************/
11618
+ function ac_center_anchor$2(gobj, event, kw, src) {
11619
+ let priv = gobj.priv;
11620
+ if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
11621
+ return 0;
11622
+ }
11623
+ /************************************************************
11599
11624
  * Arm the anchor, or let it go.
11600
11625
  *
11601
11626
  * Three states and one button, so a press ADVANCES: with no
@@ -11678,7 +11703,7 @@ function ac_node_click$4(gobj, event, kw, src) {
11678
11703
  priv.anchor_path = nodedata && nodedata.data ? nodedata.data.path : "";
11679
11704
  priv.anchor_state = "on";
11680
11705
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
11681
- yui_graph_center_on(graph, node_id);
11706
+ (0, _yuneta_gobj_js.gobj_post_event)(gobj, "EV_CENTER_ANCHOR", {}, gobj);
11682
11707
  return 0;
11683
11708
  }
11684
11709
  if (nodedata && nodedata.data) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_JSON_ITEM_CLICKED", {
@@ -11819,6 +11844,11 @@ function create_gclass$11(gclass_name) {
11819
11844
  ac_toggle_anchor$2,
11820
11845
  null
11821
11846
  ],
11847
+ [
11848
+ "EV_CENTER_ANCHOR",
11849
+ ac_center_anchor$2,
11850
+ null
11851
+ ],
11822
11852
  [
11823
11853
  "EV_RESIZE",
11824
11854
  ac_resize$2,
@@ -11850,6 +11880,7 @@ function create_gclass$11(gclass_name) {
11850
11880
  ["EV_CENTER", 0],
11851
11881
  ["EV_NODE_CLICK", 0],
11852
11882
  ["EV_TOGGLE_ANCHOR", 0],
11883
+ ["EV_CENTER_ANCHOR", 0],
11853
11884
  ["EV_RESIZE", 0],
11854
11885
  ["EV_SHOW", 0],
11855
11886
  ["EV_HIDE", 0],
@@ -14487,6 +14518,14 @@ function reanchor(gobj) {
14487
14518
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
14488
14519
  }
14489
14520
  /************************************************************
14521
+ * Centre on the anchor, one cycle after it was chosen.
14522
+ ************************************************************/
14523
+ function ac_center_anchor$1(gobj, event, kw, src) {
14524
+ let priv = gobj.priv;
14525
+ if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
14526
+ return 0;
14527
+ }
14528
+ /************************************************************
14490
14529
  * Arm the anchor, or let it go.
14491
14530
  *
14492
14531
  * Three states and one button, so a press ADVANCES: with no
@@ -14571,7 +14610,7 @@ function ac_node_click$3(gobj, event, kw, src) {
14571
14610
  priv.anchor_name = node_data.full_name || "";
14572
14611
  priv.anchor_state = "on";
14573
14612
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
14574
- yui_graph_center_on(graph, node_id);
14613
+ (0, _yuneta_gobj_js.gobj_post_event)(gobj, "EV_CENTER_ANCHOR", {}, gobj);
14575
14614
  return 0;
14576
14615
  }
14577
14616
  let client_x = kw.evt && kw.evt.client ? kw.evt.client.x : 0;
@@ -14655,6 +14694,7 @@ function create_gclass$9(gclass_name) {
14655
14694
  ["EV_CENTER", 0],
14656
14695
  ["EV_NODE_CLICK", 0],
14657
14696
  ["EV_TOGGLE_ANCHOR", 0],
14697
+ ["EV_CENTER_ANCHOR", 0],
14658
14698
  ["EV_RESIZE", 0],
14659
14699
  ["EV_SHOW", 0],
14660
14700
  ["EV_HIDE", 0]
@@ -14719,6 +14759,11 @@ function create_gclass$9(gclass_name) {
14719
14759
  ac_toggle_anchor$1,
14720
14760
  null
14721
14761
  ],
14762
+ [
14763
+ "EV_CENTER_ANCHOR",
14764
+ ac_center_anchor$1,
14765
+ null
14766
+ ],
14722
14767
  [
14723
14768
  "EV_RESIZE",
14724
14769
  ac_resize$1,
@@ -52931,6 +52976,14 @@ function ac_zoom_reset(gobj, event, kw, src) {
52931
52976
  /************************************************************
52932
52977
  * What the anchor button says it will do next.
52933
52978
  ************************************************************/
52979
+ function ac_center_anchor(gobj, event, kw, src) {
52980
+ let priv = gobj.priv;
52981
+ if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
52982
+ return 0;
52983
+ }
52984
+ /************************************************************
52985
+ * What the anchor button says it will do next.
52986
+ ************************************************************/
52934
52987
  function anchor_title(state) {
52935
52988
  if (state === "arming") return (0, i18next.t)("click the element to centre on");
52936
52989
  if (state === "on") return (0, i18next.t)("centred: click to release");
@@ -53149,7 +53202,7 @@ function ac_node_click$2(gobj, event, kw, src) {
53149
53202
  priv.anchor_id = node_id;
53150
53203
  priv.anchor_state = "on";
53151
53204
  update_toolbar(gobj);
53152
- yui_graph_center_on(graph, node_id);
53205
+ (0, _yuneta_gobj_js.gobj_post_event)(gobj, "EV_CENTER_ANCHOR", {}, gobj);
53153
53206
  return 0;
53154
53207
  }
53155
53208
  try {
@@ -53418,6 +53471,11 @@ function create_gclass$4(gclass_name) {
53418
53471
  ac_toggle_anchor,
53419
53472
  null
53420
53473
  ],
53474
+ [
53475
+ "EV_CENTER_ANCHOR",
53476
+ ac_center_anchor,
53477
+ null
53478
+ ],
53421
53479
  [
53422
53480
  "EV_TOGGLE_TOOLBARS",
53423
53481
  ac_toggle_toolbars,
@@ -53532,6 +53590,7 @@ function create_gclass$4(gclass_name) {
53532
53590
  ["EV_ZOOM_OUT", 0],
53533
53591
  ["EV_ZOOM_RESET", 0],
53534
53592
  ["EV_TOGGLE_ANCHOR", 0],
53593
+ ["EV_CENTER_ANCHOR", 0],
53535
53594
  ["EV_TOGGLE_TOOLBARS", 0],
53536
53595
  ["EV_AUTO_FIT", 0],
53537
53596
  ["EV_ZOOM_SELECTION", 0],
@@ -1,4 +1,4 @@
1
- import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, duplicate_objects, empty_string, escapeHtml, event_flag_t, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_change_state, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_start_tree, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_get_ids, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_console_log_enabled, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
1
+ import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, duplicate_objects, empty_string, escapeHtml, event_flag_t, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_change_state, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_post_event, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_start_tree, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_get_ids, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_console_log_enabled, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
2
2
  import i18next, { t } from "i18next";
3
3
  import TomSelect from "tom-select";
4
4
  import { createJSONEditor } from "vanilla-jsoneditor";
@@ -9674,7 +9674,7 @@ function yui_graph_update_anchor($container, state) {
9674
9674
  if (!$container) return;
9675
9675
  let $btn = $container.querySelector(".EV_TOGGLE_ANCHOR");
9676
9676
  if (!$btn) return;
9677
- set_pressed_state($container, ".EV_TOGGLE_ANCHOR", state !== "off");
9677
+ set_pressed_state($container, ".EV_TOGGLE_ANCHOR", state === "on");
9678
9678
  $btn.classList.toggle("color_pending_state", state === "arming");
9679
9679
  $btn.setAttribute("aria-pressed", state === "on" ? "true" : "false");
9680
9680
  let key = state === "arming" ? "click the element to centre on" : state === "on" ? "centred: click to release" : "anchor view";
@@ -9690,18 +9690,35 @@ function yui_graph_update_anchor($container, state) {
9690
9690
  * the zoom is what the reader asked for, the centring is what
9691
9691
  * keeps the thing they are reading under their eyes.
9692
9692
  *
9693
+ * The translate is computed and applied HERE rather than
9694
+ * delegated to `graph.focusElement()`, which is the obvious
9695
+ * call and does NOT work on these graphs. Measured: it
9696
+ * resolves, having computed the very offset wanted -- `[0,
9697
+ * 306]` for a card 306px above centre -- and the camera does
9698
+ * not move. `getPosition()` reads the same before and after,
9699
+ * and so does the card's box on screen. Three public calls do
9700
+ * move it, so that is what is used.
9701
+ *
9693
9702
  * Guarded on every side because it runs on every wheel notch:
9694
9703
  * a graph mid-rebuild, an anchor whose node went away with a
9695
- * fold, a G6 version without `focusElement` -- none of those is
9704
+ * fold, a G6 missing one of these calls -- none of those is
9696
9705
  * an error worth a log line per notch, they are all "there is
9697
9706
  * nothing to centre right now".
9698
9707
  ************************************************************/
9699
- function yui_graph_center_on(graph, node_id) {
9708
+ async function yui_graph_center_on(graph, node_id) {
9700
9709
  if (!graph || !node_id) return false;
9701
9710
  try {
9702
9711
  if (typeof graph.getElementData === "function" && !graph.getElementData(node_id)) return false;
9703
- if (typeof graph.focusElement !== "function") return false;
9704
- graph.focusElement(node_id);
9712
+ if (typeof graph.getElementPosition !== "function" || typeof graph.getViewportByCanvas !== "function" || typeof graph.getCanvasCenter !== "function" || typeof graph.translateTo !== "function") return false;
9713
+ for (let i = 0; i < 3; i++) {
9714
+ let at = graph.getViewportByCanvas(graph.getElementPosition(node_id));
9715
+ let want = graph.getCanvasCenter();
9716
+ let dx = want[0] - at[0];
9717
+ let dy = want[1] - at[1];
9718
+ if (Math.abs(dx) < 1 && Math.abs(dy) < 1) break;
9719
+ let pos = graph.getPosition();
9720
+ await graph.translateTo([pos[0] + dx, pos[1] + dy]);
9721
+ }
9705
9722
  return true;
9706
9723
  } catch (e) {
9707
9724
  return false;
@@ -11591,6 +11608,14 @@ function reanchor$1(gobj) {
11591
11608
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
11592
11609
  }
11593
11610
  /************************************************************
11611
+ * Centre on the anchor, one cycle after it was chosen.
11612
+ ************************************************************/
11613
+ function ac_center_anchor$2(gobj, event, kw, src) {
11614
+ let priv = gobj.priv;
11615
+ if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
11616
+ return 0;
11617
+ }
11618
+ /************************************************************
11594
11619
  * Arm the anchor, or let it go.
11595
11620
  *
11596
11621
  * Three states and one button, so a press ADVANCES: with no
@@ -11673,7 +11698,7 @@ function ac_node_click$4(gobj, event, kw, src) {
11673
11698
  priv.anchor_path = nodedata && nodedata.data ? nodedata.data.path : "";
11674
11699
  priv.anchor_state = "on";
11675
11700
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
11676
- yui_graph_center_on(graph, node_id);
11701
+ gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
11677
11702
  return 0;
11678
11703
  }
11679
11704
  if (nodedata && nodedata.data) gobj_publish_event(gobj, "EV_JSON_ITEM_CLICKED", {
@@ -11814,6 +11839,11 @@ function create_gclass$11(gclass_name) {
11814
11839
  ac_toggle_anchor$2,
11815
11840
  null
11816
11841
  ],
11842
+ [
11843
+ "EV_CENTER_ANCHOR",
11844
+ ac_center_anchor$2,
11845
+ null
11846
+ ],
11817
11847
  [
11818
11848
  "EV_RESIZE",
11819
11849
  ac_resize$2,
@@ -11845,6 +11875,7 @@ function create_gclass$11(gclass_name) {
11845
11875
  ["EV_CENTER", 0],
11846
11876
  ["EV_NODE_CLICK", 0],
11847
11877
  ["EV_TOGGLE_ANCHOR", 0],
11878
+ ["EV_CENTER_ANCHOR", 0],
11848
11879
  ["EV_RESIZE", 0],
11849
11880
  ["EV_SHOW", 0],
11850
11881
  ["EV_HIDE", 0],
@@ -14482,6 +14513,14 @@ function reanchor(gobj) {
14482
14513
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
14483
14514
  }
14484
14515
  /************************************************************
14516
+ * Centre on the anchor, one cycle after it was chosen.
14517
+ ************************************************************/
14518
+ function ac_center_anchor$1(gobj, event, kw, src) {
14519
+ let priv = gobj.priv;
14520
+ if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
14521
+ return 0;
14522
+ }
14523
+ /************************************************************
14485
14524
  * Arm the anchor, or let it go.
14486
14525
  *
14487
14526
  * Three states and one button, so a press ADVANCES: with no
@@ -14566,7 +14605,7 @@ function ac_node_click$3(gobj, event, kw, src) {
14566
14605
  priv.anchor_name = node_data.full_name || "";
14567
14606
  priv.anchor_state = "on";
14568
14607
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
14569
- yui_graph_center_on(graph, node_id);
14608
+ gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
14570
14609
  return 0;
14571
14610
  }
14572
14611
  let client_x = kw.evt && kw.evt.client ? kw.evt.client.x : 0;
@@ -14650,6 +14689,7 @@ function create_gclass$9(gclass_name) {
14650
14689
  ["EV_CENTER", 0],
14651
14690
  ["EV_NODE_CLICK", 0],
14652
14691
  ["EV_TOGGLE_ANCHOR", 0],
14692
+ ["EV_CENTER_ANCHOR", 0],
14653
14693
  ["EV_RESIZE", 0],
14654
14694
  ["EV_SHOW", 0],
14655
14695
  ["EV_HIDE", 0]
@@ -14714,6 +14754,11 @@ function create_gclass$9(gclass_name) {
14714
14754
  ac_toggle_anchor$1,
14715
14755
  null
14716
14756
  ],
14757
+ [
14758
+ "EV_CENTER_ANCHOR",
14759
+ ac_center_anchor$1,
14760
+ null
14761
+ ],
14717
14762
  [
14718
14763
  "EV_RESIZE",
14719
14764
  ac_resize$1,
@@ -52926,6 +52971,14 @@ function ac_zoom_reset(gobj, event, kw, src) {
52926
52971
  /************************************************************
52927
52972
  * What the anchor button says it will do next.
52928
52973
  ************************************************************/
52974
+ function ac_center_anchor(gobj, event, kw, src) {
52975
+ let priv = gobj.priv;
52976
+ if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
52977
+ return 0;
52978
+ }
52979
+ /************************************************************
52980
+ * What the anchor button says it will do next.
52981
+ ************************************************************/
52929
52982
  function anchor_title(state) {
52930
52983
  if (state === "arming") return t("click the element to centre on");
52931
52984
  if (state === "on") return t("centred: click to release");
@@ -53144,7 +53197,7 @@ function ac_node_click$2(gobj, event, kw, src) {
53144
53197
  priv.anchor_id = node_id;
53145
53198
  priv.anchor_state = "on";
53146
53199
  update_toolbar(gobj);
53147
- yui_graph_center_on(graph, node_id);
53200
+ gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
53148
53201
  return 0;
53149
53202
  }
53150
53203
  try {
@@ -53413,6 +53466,11 @@ function create_gclass$4(gclass_name) {
53413
53466
  ac_toggle_anchor,
53414
53467
  null
53415
53468
  ],
53469
+ [
53470
+ "EV_CENTER_ANCHOR",
53471
+ ac_center_anchor,
53472
+ null
53473
+ ],
53416
53474
  [
53417
53475
  "EV_TOGGLE_TOOLBARS",
53418
53476
  ac_toggle_toolbars,
@@ -53527,6 +53585,7 @@ function create_gclass$4(gclass_name) {
53527
53585
  ["EV_ZOOM_OUT", 0],
53528
53586
  ["EV_ZOOM_RESET", 0],
53529
53587
  ["EV_TOGGLE_ANCHOR", 0],
53588
+ ["EV_CENTER_ANCHOR", 0],
53530
53589
  ["EV_TOGGLE_TOOLBARS", 0],
53531
53590
  ["EV_AUTO_FIT", 0],
53532
53591
  ["EV_ZOOM_SELECTION", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.24",
3
+ "version": "7.23.25",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -42,6 +42,7 @@ import {
42
42
  gobj_subscribe_event,
43
43
  gobj_publish_event,
44
44
  gobj_send_event,
45
+ gobj_post_event,
45
46
  gobj_save_persistent_attrs,
46
47
  clean_name,
47
48
  sprintf,
@@ -7505,6 +7506,19 @@ function ac_zoom_reset(gobj, event, kw, src)
7505
7506
  return 0;
7506
7507
  }
7507
7508
 
7509
+ /************************************************************
7510
+ * What the anchor button says it will do next.
7511
+ ************************************************************/
7512
+ function ac_center_anchor(gobj, event, kw, src)
7513
+ {
7514
+ let priv = gobj.priv;
7515
+
7516
+ if(priv.anchor_state === "on" && priv.anchor_id) {
7517
+ yui_graph_center_on(priv.graph, priv.anchor_id);
7518
+ }
7519
+ return 0;
7520
+ }
7521
+
7508
7522
  /************************************************************
7509
7523
  * What the anchor button says it will do next.
7510
7524
  ************************************************************/
@@ -7824,7 +7838,11 @@ function ac_node_click(gobj, event, kw, src)
7824
7838
  priv.anchor_id = node_id;
7825
7839
  priv.anchor_state = "on";
7826
7840
  update_toolbar(gobj);
7827
- yui_graph_center_on(graph, node_id);
7841
+
7842
+ /* POSTED, not made here: a camera move issued inside G6's click
7843
+ * dispatch is swallowed -- see the note in yui_graph_center_on.
7844
+ * An event to itself and not a timer: a deferral is not a TIME. */
7845
+ gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
7828
7846
  return 0;
7829
7847
  }
7830
7848
 
@@ -8140,6 +8158,7 @@ function create_gclass(gclass_name)
8140
8158
  ["EV_ZOOM_OUT", ac_zoom_out, null],
8141
8159
  ["EV_ZOOM_RESET", ac_zoom_reset, null],
8142
8160
  ["EV_TOGGLE_ANCHOR", ac_toggle_anchor, null],
8161
+ ["EV_CENTER_ANCHOR", ac_center_anchor, null],
8143
8162
  ["EV_TOGGLE_TOOLBARS", ac_toggle_toolbars, null],
8144
8163
  ["EV_AUTO_FIT", ac_auto_fit, null],
8145
8164
  ["EV_ZOOM_SELECTION", ac_zoom_selection, null],
@@ -8190,6 +8209,7 @@ function create_gclass(gclass_name)
8190
8209
  ["EV_ZOOM_OUT", 0],
8191
8210
  ["EV_ZOOM_RESET", 0],
8192
8211
  ["EV_TOGGLE_ANCHOR", 0],
8212
+ ["EV_CENTER_ANCHOR", 0],
8193
8213
  ["EV_TOGGLE_TOOLBARS", 0],
8194
8214
  ["EV_AUTO_FIT", 0],
8195
8215
  ["EV_ZOOM_SELECTION", 0],
@@ -21,6 +21,7 @@ import {
21
21
  gobj_read_attr,
22
22
  gobj_write_attr,
23
23
  gobj_send_event,
24
+ gobj_post_event,
24
25
  gobj_write_str_attr,
25
26
  gobj_read_str_attr,
26
27
  gobj_publish_event,
@@ -2000,6 +2001,19 @@ function reanchor(gobj)
2000
2001
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
2001
2002
  }
2002
2003
 
2004
+ /************************************************************
2005
+ * Centre on the anchor, one cycle after it was chosen.
2006
+ ************************************************************/
2007
+ function ac_center_anchor(gobj, event, kw, src)
2008
+ {
2009
+ let priv = gobj.priv;
2010
+
2011
+ if(priv.anchor_state === "on" && priv.anchor_id) {
2012
+ yui_graph_center_on(priv.graph, priv.anchor_id);
2013
+ }
2014
+ return 0;
2015
+ }
2016
+
2003
2017
  /************************************************************
2004
2018
  * Arm the anchor, or let it go.
2005
2019
  *
@@ -2125,7 +2139,11 @@ function ac_node_click(gobj, event, kw, src)
2125
2139
  priv.anchor_name = node_data.full_name || "";
2126
2140
  priv.anchor_state = "on";
2127
2141
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
2128
- yui_graph_center_on(graph, node_id);
2142
+
2143
+ /* POSTED, not made here: a camera move issued inside G6's click
2144
+ * dispatch is swallowed -- see the note in yui_graph_center_on.
2145
+ * An event to itself and not a timer: a deferral is not a TIME. */
2146
+ gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
2129
2147
  return 0;
2130
2148
  }
2131
2149
 
@@ -2255,6 +2273,7 @@ function create_gclass(gclass_name)
2255
2273
  ["EV_CENTER", ac_center, null],
2256
2274
  ["EV_NODE_CLICK", ac_node_click, null],
2257
2275
  ["EV_TOGGLE_ANCHOR", ac_toggle_anchor, null],
2276
+ ["EV_CENTER_ANCHOR", ac_center_anchor, null],
2258
2277
  ["EV_RESIZE", ac_resize, null],
2259
2278
  ["EV_SHOW", ac_show, null],
2260
2279
  ["EV_HIDE", ac_hide, null],
@@ -2277,6 +2296,7 @@ function create_gclass(gclass_name)
2277
2296
  ["EV_CENTER", 0],
2278
2297
  ["EV_NODE_CLICK", 0],
2279
2298
  ["EV_TOGGLE_ANCHOR", 0],
2299
+ ["EV_CENTER_ANCHOR", 0],
2280
2300
  ["EV_RESIZE", 0],
2281
2301
  ["EV_SHOW", 0],
2282
2302
  ["EV_HIDE", 0],
@@ -25,6 +25,7 @@ import {
25
25
  gobj_read_attr,
26
26
  gobj_write_attr,
27
27
  gobj_send_event,
28
+ gobj_post_event,
28
29
  gobj_find_service,
29
30
  gobj_short_name,
30
31
  gobj_read_str_attr,
@@ -1819,6 +1820,19 @@ function reanchor(gobj)
1819
1820
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
1820
1821
  }
1821
1822
 
1823
+ /************************************************************
1824
+ * Centre on the anchor, one cycle after it was chosen.
1825
+ ************************************************************/
1826
+ function ac_center_anchor(gobj, event, kw, src)
1827
+ {
1828
+ let priv = gobj.priv;
1829
+
1830
+ if(priv.anchor_state === "on" && priv.anchor_id) {
1831
+ yui_graph_center_on(priv.graph, priv.anchor_id);
1832
+ }
1833
+ return 0;
1834
+ }
1835
+
1822
1836
  /************************************************************
1823
1837
  * Arm the anchor, or let it go.
1824
1838
  *
@@ -1931,7 +1945,22 @@ function ac_node_click(gobj, event, kw, src)
1931
1945
  priv.anchor_path = (nodedata && nodedata.data)? nodedata.data.path: "";
1932
1946
  priv.anchor_state = "on";
1933
1947
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
1934
- yui_graph_center_on(graph, node_id);
1948
+
1949
+ /*
1950
+ * The camera move is POSTED, not made here.
1951
+ *
1952
+ * A translate issued inside G6's click dispatch is swallowed:
1953
+ * measured, the call computes the right offset, resolves, and
1954
+ * the camera reads the same before and after -- while the very
1955
+ * same call from a zoom action centres the node exactly. So the
1956
+ * first thing an anchor did was nothing, which is what "I don't
1957
+ * see how it works" looks like.
1958
+ *
1959
+ * An event to itself and not a timer: a deferral is not a TIME,
1960
+ * and writing it as one costs the name of what happens next in
1961
+ * the trace.
1962
+ */
1963
+ gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
1935
1964
  return 0;
1936
1965
  }
1937
1966
 
@@ -2057,6 +2086,7 @@ function create_gclass(gclass_name)
2057
2086
  ["EV_CENTER", ac_center, null],
2058
2087
  ["EV_NODE_CLICK", ac_node_click, null],
2059
2088
  ["EV_TOGGLE_ANCHOR", ac_toggle_anchor, null],
2089
+ ["EV_CENTER_ANCHOR", ac_center_anchor, null],
2060
2090
  ["EV_RESIZE", ac_resize, null],
2061
2091
  ["EV_SHOW", ac_show, null],
2062
2092
  ["EV_HIDE", ac_hide, null],
@@ -2081,6 +2111,7 @@ function create_gclass(gclass_name)
2081
2111
  ["EV_CENTER", 0],
2082
2112
  ["EV_NODE_CLICK", 0],
2083
2113
  ["EV_TOGGLE_ANCHOR", 0],
2114
+ ["EV_CENTER_ANCHOR", 0],
2084
2115
  ["EV_RESIZE", 0],
2085
2116
  ["EV_SHOW", 0],
2086
2117
  ["EV_HIDE", 0],
package/src/lib_graph.css CHANGED
@@ -14,7 +14,11 @@
14
14
  .color_create_state {
15
15
  color: #1890ff !important;
16
16
  }
17
- .color_submit_state {
17
+ /* Orange is "needs attention, waiting on you". A toggle that is armed
18
+ * and waiting for its target is exactly that and not a submit, so it
19
+ * takes the same colour under a name that says so. */
20
+ .color_submit_state,
21
+ .color_pending_state {
18
22
  color: #fa8c16 !important;
19
23
  }
20
24
  .color_active_state {
@@ -237,6 +241,28 @@
237
241
  border-radius: 4px;
238
242
  }
239
243
 
244
+ /*
245
+ * The same pressed look for a toggle in a DOM toolbar.
246
+ *
247
+ * `pressed_state` was written for the G6 plugin toolbar and styled ONLY
248
+ * there, so the two toolbars built by `yui_graph_camera.js` were given
249
+ * the class and no rule: pressing the button changed nothing on screen,
250
+ * which from the outside is indistinguishable from a button that does
251
+ * not work.
252
+ *
253
+ * `border-color` as well as the fill, or Bulma's own border stays and
254
+ * the button reads as outlined-and-filled, which is neither state.
255
+ * The `:hover` half is the same guard as above: without it, hovering a
256
+ * pressed button hands it back to Bulma's rule and the toggle appears
257
+ * to switch off under the pointer.
258
+ */
259
+ .button.pressed_state,
260
+ .button.pressed_state:hover {
261
+ background-color: var(--bulma-text, #2e333d);
262
+ color: var(--bulma-background, #ffffff);
263
+ border-color: var(--bulma-text, #2e333d);
264
+ }
265
+
240
266
  /*
241
267
  * Touch: the context menu is a LIST OF COMMANDS, and G6 sizes its
242
268
  * rows with `padding: 8px 12px` -- 34px, which is a mouse row. The
@@ -191,7 +191,14 @@ export function yui_graph_update_anchor($container, state)
191
191
  if(!$btn) {
192
192
  return; /* a toolbar without the control: nothing to paint */
193
193
  }
194
- set_pressed_state($container, '.EV_TOGGLE_ANCHOR', state !== "off");
194
+ /*
195
+ * PRESSED is "on", and only "on": pressed is the state the control
196
+ * is IN, and while it is arming it is not in that state yet, it is
197
+ * asking for something. That gets the attention colour instead --
198
+ * which also keeps the two off each other, since an orange glyph on
199
+ * the pressed fill is orange on near-white in dark and unreadable.
200
+ */
201
+ set_pressed_state($container, '.EV_TOGGLE_ANCHOR', state === "on");
195
202
  $btn.classList.toggle('color_pending_state', state === "arming");
196
203
  $btn.setAttribute("aria-pressed", (state === "on")? "true": "false");
197
204
 
@@ -211,13 +218,22 @@ export function yui_graph_update_anchor($container, state)
211
218
  * the zoom is what the reader asked for, the centring is what
212
219
  * keeps the thing they are reading under their eyes.
213
220
  *
221
+ * The translate is computed and applied HERE rather than
222
+ * delegated to `graph.focusElement()`, which is the obvious
223
+ * call and does NOT work on these graphs. Measured: it
224
+ * resolves, having computed the very offset wanted -- `[0,
225
+ * 306]` for a card 306px above centre -- and the camera does
226
+ * not move. `getPosition()` reads the same before and after,
227
+ * and so does the card's box on screen. Three public calls do
228
+ * move it, so that is what is used.
229
+ *
214
230
  * Guarded on every side because it runs on every wheel notch:
215
231
  * a graph mid-rebuild, an anchor whose node went away with a
216
- * fold, a G6 version without `focusElement` -- none of those is
232
+ * fold, a G6 missing one of these calls -- none of those is
217
233
  * an error worth a log line per notch, they are all "there is
218
234
  * nothing to centre right now".
219
235
  ************************************************************/
220
- export function yui_graph_center_on(graph, node_id)
236
+ export async function yui_graph_center_on(graph, node_id)
221
237
  {
222
238
  if(!graph || !node_id) {
223
239
  return false;
@@ -226,10 +242,50 @@ export function yui_graph_center_on(graph, node_id)
226
242
  if(typeof graph.getElementData === "function" && !graph.getElementData(node_id)) {
227
243
  return false;
228
244
  }
229
- if(typeof graph.focusElement !== "function") {
245
+ if(typeof graph.getElementPosition !== "function" ||
246
+ typeof graph.getViewportByCanvas !== "function" ||
247
+ typeof graph.getCanvasCenter !== "function" ||
248
+ typeof graph.translateTo !== "function") {
230
249
  return false;
231
250
  }
232
- graph.focusElement(node_id);
251
+
252
+ /*
253
+ * MEASURE, MOVE, MEASURE AGAIN -- up to three times.
254
+ *
255
+ * Not the arithmetic anybody would write first, and the reason
256
+ * is worth keeping. The obvious call is `graph.focusElement()`,
257
+ * which computes the very offset wanted and then does not move
258
+ * the camera at all: measured on the JSON graph it resolves
259
+ * with `getPosition()` reading identical before and after.
260
+ * `translateBy()` (a RELATIVE transform) behaves the same way.
261
+ * `translateTo()` (ABSOLUTE) does move it -- and the units of
262
+ * its argument are not viewport pixels at any zoom but 1, so
263
+ * one shot lands short or long by a factor nobody should have
264
+ * to name.
265
+ *
266
+ * Probing for that factor gives noise: the camera settles
267
+ * asynchronously, so a position read straight after a
268
+ * translate is a value halfway through. AWAITING each move and
269
+ * measuring again needs no factor at all -- the direction is
270
+ * right, so the residual falls to nothing in two or three
271
+ * passes, and a graph whose maths change under us keeps
272
+ * working.
273
+ *
274
+ * The 1px floor is what stops it converging forever on the
275
+ * rounding.
276
+ */
277
+ for(let i = 0; i < 3; i++) {
278
+ let at = graph.getViewportByCanvas(graph.getElementPosition(node_id));
279
+ let want = graph.getCanvasCenter();
280
+ let dx = want[0] - at[0];
281
+ let dy = want[1] - at[1];
282
+
283
+ if(Math.abs(dx) < 1 && Math.abs(dy) < 1) {
284
+ break;
285
+ }
286
+ let pos = graph.getPosition();
287
+ await graph.translateTo([pos[0] + dx, pos[1] + dy]);
288
+ }
233
289
  return true;
234
290
  } catch(e) {
235
291
  return false; /* the element is gone, or the graph is between renders */