@yuneta/gobj-ui 7.23.23 → 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;
@@ -10924,7 +10941,11 @@ function build_graph$3(gobj) {
10924
10941
  }
10925
10942
  },
10926
10943
  layout: layout_cfg.g6_layout,
10927
- behaviors: ["drag-canvas", "zoom-canvas"]
10944
+ behaviors: [
10945
+ "drag-canvas",
10946
+ "zoom-canvas",
10947
+ "drag-element"
10948
+ ]
10928
10949
  });
10929
10950
  if (priv.theme) graph.setTheme(priv.theme);
10930
10951
  graph.on(_antv_g6.NodeEvent.CLICK, (evt) => {
@@ -11592,6 +11613,14 @@ function reanchor$1(gobj) {
11592
11613
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
11593
11614
  }
11594
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
+ /************************************************************
11595
11624
  * Arm the anchor, or let it go.
11596
11625
  *
11597
11626
  * Three states and one button, so a press ADVANCES: with no
@@ -11674,7 +11703,7 @@ function ac_node_click$4(gobj, event, kw, src) {
11674
11703
  priv.anchor_path = nodedata && nodedata.data ? nodedata.data.path : "";
11675
11704
  priv.anchor_state = "on";
11676
11705
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
11677
- yui_graph_center_on(graph, node_id);
11706
+ (0, _yuneta_gobj_js.gobj_post_event)(gobj, "EV_CENTER_ANCHOR", {}, gobj);
11678
11707
  return 0;
11679
11708
  }
11680
11709
  if (nodedata && nodedata.data) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_JSON_ITEM_CLICKED", {
@@ -11815,6 +11844,11 @@ function create_gclass$11(gclass_name) {
11815
11844
  ac_toggle_anchor$2,
11816
11845
  null
11817
11846
  ],
11847
+ [
11848
+ "EV_CENTER_ANCHOR",
11849
+ ac_center_anchor$2,
11850
+ null
11851
+ ],
11818
11852
  [
11819
11853
  "EV_RESIZE",
11820
11854
  ac_resize$2,
@@ -11846,6 +11880,7 @@ function create_gclass$11(gclass_name) {
11846
11880
  ["EV_CENTER", 0],
11847
11881
  ["EV_NODE_CLICK", 0],
11848
11882
  ["EV_TOGGLE_ANCHOR", 0],
11883
+ ["EV_CENTER_ANCHOR", 0],
11849
11884
  ["EV_RESIZE", 0],
11850
11885
  ["EV_SHOW", 0],
11851
11886
  ["EV_HIDE", 0],
@@ -13423,7 +13458,7 @@ function build_ui$8(gobj) {
13423
13458
  {
13424
13459
  id: priv.canvas_id,
13425
13460
  class: "gobj-tree-container",
13426
- style: "height:100%; min-height:0; border: 1px solid var(--bulma-border-weak); border-radius:0.2rem;"
13461
+ style: "height:100%; min-height:0; border: 1px solid var(--bulma-border-weak); border-radius:0.2rem; touch-action:none; -webkit-user-select:none; user-select:none;"
13427
13462
  },
13428
13463
  []
13429
13464
  ]]
@@ -13615,7 +13650,11 @@ function build_graph$2(gobj) {
13615
13650
  }
13616
13651
  },
13617
13652
  layout: layout_cfg.g6_layout,
13618
- behaviors: ["drag-canvas", "zoom-canvas"]
13653
+ behaviors: [
13654
+ "drag-canvas",
13655
+ "zoom-canvas",
13656
+ "drag-element"
13657
+ ]
13619
13658
  });
13620
13659
  if (priv.theme) graph.setTheme(priv.theme);
13621
13660
  graph.on("aftertransform", () => {
@@ -14479,6 +14518,14 @@ function reanchor(gobj) {
14479
14518
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
14480
14519
  }
14481
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
+ /************************************************************
14482
14529
  * Arm the anchor, or let it go.
14483
14530
  *
14484
14531
  * Three states and one button, so a press ADVANCES: with no
@@ -14563,7 +14610,7 @@ function ac_node_click$3(gobj, event, kw, src) {
14563
14610
  priv.anchor_name = node_data.full_name || "";
14564
14611
  priv.anchor_state = "on";
14565
14612
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
14566
- yui_graph_center_on(graph, node_id);
14613
+ (0, _yuneta_gobj_js.gobj_post_event)(gobj, "EV_CENTER_ANCHOR", {}, gobj);
14567
14614
  return 0;
14568
14615
  }
14569
14616
  let client_x = kw.evt && kw.evt.client ? kw.evt.client.x : 0;
@@ -14647,6 +14694,7 @@ function create_gclass$9(gclass_name) {
14647
14694
  ["EV_CENTER", 0],
14648
14695
  ["EV_NODE_CLICK", 0],
14649
14696
  ["EV_TOGGLE_ANCHOR", 0],
14697
+ ["EV_CENTER_ANCHOR", 0],
14650
14698
  ["EV_RESIZE", 0],
14651
14699
  ["EV_SHOW", 0],
14652
14700
  ["EV_HIDE", 0]
@@ -14711,6 +14759,11 @@ function create_gclass$9(gclass_name) {
14711
14759
  ac_toggle_anchor$1,
14712
14760
  null
14713
14761
  ],
14762
+ [
14763
+ "EV_CENTER_ANCHOR",
14764
+ ac_center_anchor$1,
14765
+ null
14766
+ ],
14714
14767
  [
14715
14768
  "EV_RESIZE",
14716
14769
  ac_resize$1,
@@ -52923,6 +52976,14 @@ function ac_zoom_reset(gobj, event, kw, src) {
52923
52976
  /************************************************************
52924
52977
  * What the anchor button says it will do next.
52925
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
+ ************************************************************/
52926
52987
  function anchor_title(state) {
52927
52988
  if (state === "arming") return (0, i18next.t)("click the element to centre on");
52928
52989
  if (state === "on") return (0, i18next.t)("centred: click to release");
@@ -53141,7 +53202,7 @@ function ac_node_click$2(gobj, event, kw, src) {
53141
53202
  priv.anchor_id = node_id;
53142
53203
  priv.anchor_state = "on";
53143
53204
  update_toolbar(gobj);
53144
- yui_graph_center_on(graph, node_id);
53205
+ (0, _yuneta_gobj_js.gobj_post_event)(gobj, "EV_CENTER_ANCHOR", {}, gobj);
53145
53206
  return 0;
53146
53207
  }
53147
53208
  try {
@@ -53410,6 +53471,11 @@ function create_gclass$4(gclass_name) {
53410
53471
  ac_toggle_anchor,
53411
53472
  null
53412
53473
  ],
53474
+ [
53475
+ "EV_CENTER_ANCHOR",
53476
+ ac_center_anchor,
53477
+ null
53478
+ ],
53413
53479
  [
53414
53480
  "EV_TOGGLE_TOOLBARS",
53415
53481
  ac_toggle_toolbars,
@@ -53524,6 +53590,7 @@ function create_gclass$4(gclass_name) {
53524
53590
  ["EV_ZOOM_OUT", 0],
53525
53591
  ["EV_ZOOM_RESET", 0],
53526
53592
  ["EV_TOGGLE_ANCHOR", 0],
53593
+ ["EV_CENTER_ANCHOR", 0],
53527
53594
  ["EV_TOGGLE_TOOLBARS", 0],
53528
53595
  ["EV_AUTO_FIT", 0],
53529
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;
@@ -10919,7 +10936,11 @@ function build_graph$3(gobj) {
10919
10936
  }
10920
10937
  },
10921
10938
  layout: layout_cfg.g6_layout,
10922
- behaviors: ["drag-canvas", "zoom-canvas"]
10939
+ behaviors: [
10940
+ "drag-canvas",
10941
+ "zoom-canvas",
10942
+ "drag-element"
10943
+ ]
10923
10944
  });
10924
10945
  if (priv.theme) graph.setTheme(priv.theme);
10925
10946
  graph.on(NodeEvent.CLICK, (evt) => {
@@ -11587,6 +11608,14 @@ function reanchor$1(gobj) {
11587
11608
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
11588
11609
  }
11589
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
+ /************************************************************
11590
11619
  * Arm the anchor, or let it go.
11591
11620
  *
11592
11621
  * Three states and one button, so a press ADVANCES: with no
@@ -11669,7 +11698,7 @@ function ac_node_click$4(gobj, event, kw, src) {
11669
11698
  priv.anchor_path = nodedata && nodedata.data ? nodedata.data.path : "";
11670
11699
  priv.anchor_state = "on";
11671
11700
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
11672
- yui_graph_center_on(graph, node_id);
11701
+ gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
11673
11702
  return 0;
11674
11703
  }
11675
11704
  if (nodedata && nodedata.data) gobj_publish_event(gobj, "EV_JSON_ITEM_CLICKED", {
@@ -11810,6 +11839,11 @@ function create_gclass$11(gclass_name) {
11810
11839
  ac_toggle_anchor$2,
11811
11840
  null
11812
11841
  ],
11842
+ [
11843
+ "EV_CENTER_ANCHOR",
11844
+ ac_center_anchor$2,
11845
+ null
11846
+ ],
11813
11847
  [
11814
11848
  "EV_RESIZE",
11815
11849
  ac_resize$2,
@@ -11841,6 +11875,7 @@ function create_gclass$11(gclass_name) {
11841
11875
  ["EV_CENTER", 0],
11842
11876
  ["EV_NODE_CLICK", 0],
11843
11877
  ["EV_TOGGLE_ANCHOR", 0],
11878
+ ["EV_CENTER_ANCHOR", 0],
11844
11879
  ["EV_RESIZE", 0],
11845
11880
  ["EV_SHOW", 0],
11846
11881
  ["EV_HIDE", 0],
@@ -13418,7 +13453,7 @@ function build_ui$8(gobj) {
13418
13453
  {
13419
13454
  id: priv.canvas_id,
13420
13455
  class: "gobj-tree-container",
13421
- style: "height:100%; min-height:0; border: 1px solid var(--bulma-border-weak); border-radius:0.2rem;"
13456
+ style: "height:100%; min-height:0; border: 1px solid var(--bulma-border-weak); border-radius:0.2rem; touch-action:none; -webkit-user-select:none; user-select:none;"
13422
13457
  },
13423
13458
  []
13424
13459
  ]]
@@ -13610,7 +13645,11 @@ function build_graph$2(gobj) {
13610
13645
  }
13611
13646
  },
13612
13647
  layout: layout_cfg.g6_layout,
13613
- behaviors: ["drag-canvas", "zoom-canvas"]
13648
+ behaviors: [
13649
+ "drag-canvas",
13650
+ "zoom-canvas",
13651
+ "drag-element"
13652
+ ]
13614
13653
  });
13615
13654
  if (priv.theme) graph.setTheme(priv.theme);
13616
13655
  graph.on("aftertransform", () => {
@@ -14474,6 +14513,14 @@ function reanchor(gobj) {
14474
14513
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
14475
14514
  }
14476
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
+ /************************************************************
14477
14524
  * Arm the anchor, or let it go.
14478
14525
  *
14479
14526
  * Three states and one button, so a press ADVANCES: with no
@@ -14558,7 +14605,7 @@ function ac_node_click$3(gobj, event, kw, src) {
14558
14605
  priv.anchor_name = node_data.full_name || "";
14559
14606
  priv.anchor_state = "on";
14560
14607
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
14561
- yui_graph_center_on(graph, node_id);
14608
+ gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
14562
14609
  return 0;
14563
14610
  }
14564
14611
  let client_x = kw.evt && kw.evt.client ? kw.evt.client.x : 0;
@@ -14642,6 +14689,7 @@ function create_gclass$9(gclass_name) {
14642
14689
  ["EV_CENTER", 0],
14643
14690
  ["EV_NODE_CLICK", 0],
14644
14691
  ["EV_TOGGLE_ANCHOR", 0],
14692
+ ["EV_CENTER_ANCHOR", 0],
14645
14693
  ["EV_RESIZE", 0],
14646
14694
  ["EV_SHOW", 0],
14647
14695
  ["EV_HIDE", 0]
@@ -14706,6 +14754,11 @@ function create_gclass$9(gclass_name) {
14706
14754
  ac_toggle_anchor$1,
14707
14755
  null
14708
14756
  ],
14757
+ [
14758
+ "EV_CENTER_ANCHOR",
14759
+ ac_center_anchor$1,
14760
+ null
14761
+ ],
14709
14762
  [
14710
14763
  "EV_RESIZE",
14711
14764
  ac_resize$1,
@@ -52918,6 +52971,14 @@ function ac_zoom_reset(gobj, event, kw, src) {
52918
52971
  /************************************************************
52919
52972
  * What the anchor button says it will do next.
52920
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
+ ************************************************************/
52921
52982
  function anchor_title(state) {
52922
52983
  if (state === "arming") return t("click the element to centre on");
52923
52984
  if (state === "on") return t("centred: click to release");
@@ -53136,7 +53197,7 @@ function ac_node_click$2(gobj, event, kw, src) {
53136
53197
  priv.anchor_id = node_id;
53137
53198
  priv.anchor_state = "on";
53138
53199
  update_toolbar(gobj);
53139
- yui_graph_center_on(graph, node_id);
53200
+ gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
53140
53201
  return 0;
53141
53202
  }
53142
53203
  try {
@@ -53405,6 +53466,11 @@ function create_gclass$4(gclass_name) {
53405
53466
  ac_toggle_anchor,
53406
53467
  null
53407
53468
  ],
53469
+ [
53470
+ "EV_CENTER_ANCHOR",
53471
+ ac_center_anchor,
53472
+ null
53473
+ ],
53408
53474
  [
53409
53475
  "EV_TOGGLE_TOOLBARS",
53410
53476
  ac_toggle_toolbars,
@@ -53519,6 +53585,7 @@ function create_gclass$4(gclass_name) {
53519
53585
  ["EV_ZOOM_OUT", 0],
53520
53586
  ["EV_ZOOM_RESET", 0],
53521
53587
  ["EV_TOGGLE_ANCHOR", 0],
53588
+ ["EV_CENTER_ANCHOR", 0],
53522
53589
  ["EV_TOGGLE_TOOLBARS", 0],
53523
53590
  ["EV_AUTO_FIT", 0],
53524
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.23",
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,
@@ -407,7 +408,13 @@ function build_ui(gobj)
407
408
  ['div', {class: 'C_YUI_GOBJ_TREE_JS', style: 'height:100%; display:flex; flex-direction:column; position:relative;'}, [
408
409
  ['div', {class: 'is-flex-grow-0 is-flex toolbar_yui_gobj_tree'}, $toolbar],
409
410
  ['div', {class: 'is-flex-grow-1', style: 'height:100%; min-height:0; overflow:hidden;'}, [
410
- ['div', {id: priv.canvas_id, class: 'gobj-tree-container', style: 'height:100%; min-height:0; border: 1px solid var(--bulma-border-weak); border-radius:0.2rem;'}, [
411
+ /* `touch-action` and `user-select` inline because this gclass loads no
412
+ * stylesheet of its own: G6 puts `touch-action: none` on its canvas and
413
+ * nothing on the html nodes over it, so without this a finger dragging a
414
+ * card makes a page scroll and stops dead after ~20px; and a card is
415
+ * text, so a mouse drag would start a selection. Same two rules
416
+ * `lib_graph.css` carries for the treedb graph. */
417
+ ['div', {id: priv.canvas_id, class: 'gobj-tree-container', style: 'height:100%; min-height:0; border: 1px solid var(--bulma-border-weak); border-radius:0.2rem; touch-action:none; -webkit-user-select:none; user-select:none;'}, [
411
418
  ]]
412
419
  ]]
413
420
  ]]
@@ -650,6 +657,13 @@ function build_graph(gobj)
650
657
  behaviors: [
651
658
  'drag-canvas',
652
659
  'zoom-canvas',
660
+ /* Move a card by dragging it. The position is not kept --
661
+ * this tree is rebuilt from the live gobj tree on every
662
+ * refresh -- and it is still worth having: pulling two
663
+ * cards apart to read the lines between them is the whole
664
+ * use of it. `enable` REPLACES a behavior's default rather
665
+ * than adding to it, so it is not passed. */
666
+ 'drag-element',
653
667
  ],
654
668
  });
655
669
 
@@ -1987,6 +2001,19 @@ function reanchor(gobj)
1987
2001
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
1988
2002
  }
1989
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
+
1990
2017
  /************************************************************
1991
2018
  * Arm the anchor, or let it go.
1992
2019
  *
@@ -2112,7 +2139,11 @@ function ac_node_click(gobj, event, kw, src)
2112
2139
  priv.anchor_name = node_data.full_name || "";
2113
2140
  priv.anchor_state = "on";
2114
2141
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
2115
- 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);
2116
2147
  return 0;
2117
2148
  }
2118
2149
 
@@ -2242,6 +2273,7 @@ function create_gclass(gclass_name)
2242
2273
  ["EV_CENTER", ac_center, null],
2243
2274
  ["EV_NODE_CLICK", ac_node_click, null],
2244
2275
  ["EV_TOGGLE_ANCHOR", ac_toggle_anchor, null],
2276
+ ["EV_CENTER_ANCHOR", ac_center_anchor, null],
2245
2277
  ["EV_RESIZE", ac_resize, null],
2246
2278
  ["EV_SHOW", ac_show, null],
2247
2279
  ["EV_HIDE", ac_hide, null],
@@ -2264,6 +2296,7 @@ function create_gclass(gclass_name)
2264
2296
  ["EV_CENTER", 0],
2265
2297
  ["EV_NODE_CLICK", 0],
2266
2298
  ["EV_TOGGLE_ANCHOR", 0],
2299
+ ["EV_CENTER_ANCHOR", 0],
2267
2300
  ["EV_RESIZE", 0],
2268
2301
  ["EV_SHOW", 0],
2269
2302
  ["EV_HIDE", 0],
@@ -293,6 +293,33 @@
293
293
  z-index: 1;
294
294
  }
295
295
 
296
+ /*
297
+ * The graph mount, once its cards can be DRAGGED.
298
+ *
299
+ * Two problems, both of them the browser's rather than G6's, and both
300
+ * already paid for once in the treedb graph (`lib_graph.css`):
301
+ *
302
+ * G6 puts `touch-action: none` on its CANVAS and NOTHING on the html
303
+ * nodes, which are ordinary DIVs layered over it. So a finger landing
304
+ * on a card made a page scroll: two `pointermove`s through, then the
305
+ * browser decided the gesture was a scroll and killed the stream with
306
+ * `pointercancel` -- the card followed for about 20px and stopped dead
307
+ * while the page slid.
308
+ *
309
+ * And a card is text: dragging one starts a text selection, and the
310
+ * wash stays until the next click somewhere else. Nothing on this
311
+ * canvas is prose.
312
+ *
313
+ * On the CONTAINER and not the cards: every layer G6 stacks in here is
314
+ * a DIV over a canvas that already refuses the browser's gestures, and
315
+ * they have to agree.
316
+ */
317
+ .C_YUI_JSON .json-graph-container {
318
+ touch-action: none;
319
+ -webkit-user-select: none;
320
+ user-select: none;
321
+ }
322
+
296
323
  /*
297
324
  * Disable any transition a host stylesheet might inject.
298
325
  */
@@ -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,
@@ -694,6 +695,20 @@ function build_graph(gobj)
694
695
  behaviors: [
695
696
  'drag-canvas',
696
697
  'zoom-canvas',
698
+ /*
699
+ * Move a card by dragging it. The position is NOT kept:
700
+ * the graph is rebuilt from the document on every refresh,
701
+ * fold and layout change, and there is nothing here to
702
+ * save it to -- this is a viewer over a value somebody
703
+ * handed it, not a document of its own. Deliberate: being
704
+ * able to pull two cards apart to read the lines between
705
+ * them is worth having even when it lasts one session.
706
+ *
707
+ * No `enable`: a behavior's `enable` REPLACES the default,
708
+ * it does not add to it, and the default here is exactly
709
+ * what is wanted.
710
+ */
711
+ 'drag-element',
697
712
  ],
698
713
  });
699
714
 
@@ -1805,6 +1820,19 @@ function reanchor(gobj)
1805
1820
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
1806
1821
  }
1807
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
+
1808
1836
  /************************************************************
1809
1837
  * Arm the anchor, or let it go.
1810
1838
  *
@@ -1917,7 +1945,22 @@ function ac_node_click(gobj, event, kw, src)
1917
1945
  priv.anchor_path = (nodedata && nodedata.data)? nodedata.data.path: "";
1918
1946
  priv.anchor_state = "on";
1919
1947
  yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
1920
- 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);
1921
1964
  return 0;
1922
1965
  }
1923
1966
 
@@ -2043,6 +2086,7 @@ function create_gclass(gclass_name)
2043
2086
  ["EV_CENTER", ac_center, null],
2044
2087
  ["EV_NODE_CLICK", ac_node_click, null],
2045
2088
  ["EV_TOGGLE_ANCHOR", ac_toggle_anchor, null],
2089
+ ["EV_CENTER_ANCHOR", ac_center_anchor, null],
2046
2090
  ["EV_RESIZE", ac_resize, null],
2047
2091
  ["EV_SHOW", ac_show, null],
2048
2092
  ["EV_HIDE", ac_hide, null],
@@ -2067,6 +2111,7 @@ function create_gclass(gclass_name)
2067
2111
  ["EV_CENTER", 0],
2068
2112
  ["EV_NODE_CLICK", 0],
2069
2113
  ["EV_TOGGLE_ANCHOR", 0],
2114
+ ["EV_CENTER_ANCHOR", 0],
2070
2115
  ["EV_RESIZE", 0],
2071
2116
  ["EV_SHOW", 0],
2072
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 */