@yuneta/gobj-ui 7.15.0 → 7.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53734,6 +53734,7 @@ ensure_drag_canvas_patch();
53734
53734
  var GCLASS_NAME$1 = "C_G6_NODES_TREE";
53735
53735
  var HIGHLIGHT_COLOR = "#f0a020";
53736
53736
  var HIGHLIGHT_HALO = "rgba(240,160,32,0.35)";
53737
+ var SELECT_RING = "rgba(59,130,246,0.95)";
53737
53738
  /***************************************************************
53738
53739
  * Internal layout and operation mode definitions
53739
53740
  ***************************************************************/
@@ -53845,6 +53846,7 @@ var PRIVATE_DATA$1 = {
53845
53846
  _link_saved_styles: [],
53846
53847
  _focus_topic: null,
53847
53848
  _focus_ids: [],
53849
+ _selected_paint_ids: [],
53848
53850
  _pending_focus_topic: null,
53849
53851
  _pending_find: null,
53850
53852
  _layout_asked: "",
@@ -54073,12 +54075,15 @@ function configure_events(gobj) {
54073
54075
  update_toolbar(gobj);
54074
54076
  };
54075
54077
  i18next.on("languageChanged", priv._on_language_changed);
54076
- if (gobj_read_bool_attr(gobj, "with_fullscreen")) graph.on("keydown", (evt) => {
54077
- const key = evt.key;
54078
- const fullscreen = graph_get_plugin(gobj, "fullscreen");
54079
- if (!fullscreen) return;
54080
- if (key === "F" || key === "f") fullscreen.request();
54081
- else if (key === "Escape") fullscreen.exit();
54078
+ graph.on("keydown", (evt) => {
54079
+ let ctrl = !!(evt.ctrlKey || evt.metaKey);
54080
+ if (ctrl && (evt.key === "a" || evt.key === "A")) {
54081
+ if (typeof evt.preventDefault === "function") evt.preventDefault();
54082
+ }
54083
+ gobj_send_event(gobj, "EV_KEY_DOWN", {
54084
+ key: evt.key,
54085
+ ctrl
54086
+ }, gobj);
54082
54087
  });
54083
54088
  }
54084
54089
  /************************************************************
@@ -54365,9 +54370,24 @@ function configure_behaviour(gobj) {
54365
54370
  case "edition":
54366
54371
  priv.edit_mode = true;
54367
54372
  behaviors = [
54368
- "drag-canvas",
54373
+ {
54374
+ type: "drag-canvas",
54375
+ key: "drag-canvas",
54376
+ enable: (event) => !event.shiftKey
54377
+ },
54369
54378
  "zoom-canvas",
54370
- "drag-element"
54379
+ "drag-element",
54380
+ {
54381
+ type: "brush-select",
54382
+ key: "brush-select",
54383
+ trigger: ["shift"],
54384
+ enableElements: ["node"],
54385
+ immediately: false,
54386
+ onSelect: (states) => {
54387
+ let ids = Object.keys(states || {}).filter((id) => (states[id] || []).includes("selected"));
54388
+ gobj_send_event(gobj, "EV_BRUSH_SELECT", { ids }, gobj);
54389
+ }
54390
+ }
54371
54391
  ];
54372
54392
  break;
54373
54393
  case "operation":
@@ -55440,10 +55460,17 @@ function create_form_button_row(parent, buttons) {
55440
55460
  function show_confirm_popover(gobj, target_el, message, confirm_text, confirm_color, onConfirm, priv_key) {
55441
55461
  hide_overlay(gobj, priv_key);
55442
55462
  let priv = gobj.priv;
55443
- if (!target_el) return;
55444
- let iconRect = target_el.getBoundingClientRect();
55445
55463
  let containerRect = priv.$container.getBoundingClientRect();
55446
- const popover = create_popover_base(iconRect.right - containerRect.left + 6, iconRect.top - containerRect.top - 4, "g6-confirm-popover", "#ff4d4f", 160);
55464
+ let left, top;
55465
+ if (target_el) {
55466
+ let iconRect = target_el.getBoundingClientRect();
55467
+ left = iconRect.right - containerRect.left + 6;
55468
+ top = iconRect.top - containerRect.top - 4;
55469
+ } else {
55470
+ left = Math.round(containerRect.width / 2) - 110;
55471
+ top = Math.round(containerRect.height / 4);
55472
+ }
55473
+ const popover = create_popover_base(left, top, "g6-confirm-popover", "#ff4d4f", 160);
55447
55474
  let msg = document.createElement("div");
55448
55475
  msg.style.cssText = "margin-bottom:10px;font-weight:500;white-space:pre-line;";
55449
55476
  msg.textContent = message;
@@ -55482,6 +55509,81 @@ function perform_history_op(gobj, is_redo) {
55482
55509
  sync_history_to_backend(gobj, cmd ? cmd.original : null);
55483
55510
  }
55484
55511
  }
55512
+ /************************************************************
55513
+ * The selection.
55514
+ *
55515
+ * G6's `selected` element state IS the selection: `drag-element`
55516
+ * reads it (`getElementDataByState`) to decide what a drag moves,
55517
+ * so keeping the set anywhere else would be a second truth the
55518
+ * drag does not consult. What this gclass keeps is what is
55519
+ * PAINTED -- an html node draws no state style at all (its key
55520
+ * shape is a DOM element), so the ring lives in the card's own
55521
+ * html, exactly like the find highlight, and the painted set
55522
+ * exists to diff the repaint.
55523
+ ************************************************************/
55524
+ function selected_node_ids(gobj) {
55525
+ let graph = gobj.priv.graph;
55526
+ if (!graph) return [];
55527
+ let data;
55528
+ try {
55529
+ data = graph.getElementDataByState("node", "selected") || [];
55530
+ } catch (e) {
55531
+ return [];
55532
+ }
55533
+ return data.map((nd) => nd.id);
55534
+ }
55535
+ /************************************************************
55536
+ * Move the ring to these nodes and off the ones that had it.
55537
+ ************************************************************/
55538
+ function paint_selection(gobj, ids) {
55539
+ let priv = gobj.priv;
55540
+ let prev = priv._selected_paint_ids || [];
55541
+ let next = ids || [];
55542
+ priv._selected_paint_ids = next;
55543
+ repaint_cards(gobj, /* @__PURE__ */ new Set([...prev, ...next]));
55544
+ }
55545
+ /************************************************************
55546
+ * Select a SET of nodes: what a marquee and a shift-click do.
55547
+ *
55548
+ * It leaves `_selected_node_id` null even for a set of one,
55549
+ * because that field is not "the selection", it is "the node
55550
+ * opened for editing" -- the resize handles, the ports and the
55551
+ * popovers hang off it, and none of them means anything over a
55552
+ * set. Clicking a node is what opens one (`select_node`); a
55553
+ * marquee selects, it does not open.
55554
+ ************************************************************/
55555
+ function set_selection(gobj, ids) {
55556
+ let graph = gobj.priv.graph;
55557
+ let next = [];
55558
+ for (let id of ids || []) try {
55559
+ if (graph.getNodeData(id)) next.push(id);
55560
+ } catch (e) {}
55561
+ deselect_node(gobj);
55562
+ if (!next.length) return;
55563
+ history_pause(gobj);
55564
+ try {
55565
+ let states = {};
55566
+ for (let id of next) states[id] = ["selected"];
55567
+ graph.setElementState(states);
55568
+ } catch (e) {
55569
+ log_error(`${gobj_short_name(gobj)}: cannot set the selection: ${e}`);
55570
+ }
55571
+ paint_selection(gobj, next);
55572
+ graph_draw(gobj).then(() => {
55573
+ history_resume(gobj);
55574
+ });
55575
+ }
55576
+ /************************************************************
55577
+ * Add a node to the selection, or take it out of it.
55578
+ ************************************************************/
55579
+ function toggle_in_selection(gobj, node_id) {
55580
+ let priv = gobj.priv;
55581
+ let current = new Set(selected_node_ids(gobj));
55582
+ for (let id of priv._selected_paint_ids || []) current.add(id);
55583
+ if (current.has(node_id)) current.delete(node_id);
55584
+ else current.add(node_id);
55585
+ set_selection(gobj, [...current]);
55586
+ }
55485
55587
  function select_node(gobj, node_id) {
55486
55588
  let priv = gobj.priv;
55487
55589
  let graph = priv.graph;
@@ -55491,6 +55593,7 @@ function select_node(gobj, node_id) {
55491
55593
  graph.setElementState(node_id, ["selected"]);
55492
55594
  } catch (e) {}
55493
55595
  priv._selected_node_id = node_id;
55596
+ paint_selection(gobj, [node_id]);
55494
55597
  show_resize_handles(gobj);
55495
55598
  show_node_icon(gobj);
55496
55599
  graph_draw(gobj).then(() => {
@@ -55506,12 +55609,17 @@ function deselect_node(gobj) {
55506
55609
  hide_node_popover(gobj);
55507
55610
  hide_delete_confirm(gobj);
55508
55611
  hide_unlink_confirm(gobj);
55509
- if (priv._selected_node_id) {
55612
+ let clearing = /* @__PURE__ */ new Set([...selected_node_ids(gobj), ...priv._selected_paint_ids || []]);
55613
+ if (priv._selected_node_id) clearing.add(priv._selected_node_id);
55614
+ if (clearing.size) {
55510
55615
  history_pause(gobj);
55511
55616
  try {
55512
- graph.setElementState(priv._selected_node_id, []);
55617
+ let states = {};
55618
+ for (let id of clearing) states[id] = [];
55619
+ graph.setElementState(states);
55513
55620
  } catch (e) {}
55514
55621
  priv._selected_node_id = null;
55622
+ paint_selection(gobj, []);
55515
55623
  graph_draw(gobj).then(() => {
55516
55624
  history_resume(gobj);
55517
55625
  });
@@ -56798,33 +56906,36 @@ function refresh_minimap(gobj) {
56798
56906
  * same three-way choice — it lived inline in the theme refresh and is
56799
56907
  * shared now. Returns null for a node that carries no desc.
56800
56908
  ************************************************************/
56801
- function node_innerHTML_of(nd, theme, highlight) {
56909
+ function node_innerHTML_of(nd, theme, highlight, selected) {
56802
56910
  if (!nd || !nd.data || !nd.data.desc) return null;
56803
56911
  let desc = nd.data.desc;
56804
56912
  let record = nd.data.record || {};
56805
56913
  let label = node_label(desc, record);
56806
56914
  switch (desc.node_treedb_type) {
56807
- case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight);
56808
- case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight);
56809
- case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight);
56915
+ case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight, selected);
56916
+ case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected);
56917
+ case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected);
56810
56918
  }
56811
56919
  return null;
56812
56920
  }
56813
56921
  /************************************************************
56814
- * Repaint the cards whose highlight state CHANGES: the ones that had it
56815
- * and lose it, plus the ones that gain it. Only those — a treedb graph
56816
- * is redrawn per keystroke of the find box otherwise.
56922
+ * Repaint these cards with the flags they carry RIGHT NOW.
56923
+ *
56924
+ * One place, because the flags are independent and each used to
56925
+ * be written by whoever repainted last: a find that repainted its
56926
+ * matches erased the selection ring off them, and a selection
56927
+ * repainted over a match erased the amber. Both are read here
56928
+ * from where they live.
56817
56929
  ************************************************************/
56818
- function apply_node_highlight(gobj, prev_ids, next_ids) {
56930
+ function repaint_cards(gobj, ids) {
56819
56931
  let priv = gobj.priv;
56820
56932
  let graph = priv.graph;
56821
- if (!graph) return;
56822
- let next = new Set(next_ids || []);
56823
- let touched = /* @__PURE__ */ new Set([...prev_ids || [], ...next]);
56824
- if (touched.size === 0) return;
56933
+ if (!graph || !ids || !ids.size) return;
56934
+ let focus = new Set(priv._focus_ids || []);
56935
+ let selected = new Set(priv._selected_paint_ids || []);
56825
56936
  let updates = [];
56826
- for (let id of touched) {
56827
- let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, next.has(id));
56937
+ for (let id of ids) {
56938
+ let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, focus.has(id), selected.has(id));
56828
56939
  if (html !== null) updates.push({
56829
56940
  id,
56830
56941
  style: { innerHTML: html }
@@ -56835,19 +56946,32 @@ function apply_node_highlight(gobj, prev_ids, next_ids) {
56835
56946
  graph.updateNodeData(updates);
56836
56947
  graph.draw();
56837
56948
  } catch (e) {
56838
- log_error(`${gobj_short_name(gobj)}: cannot repaint the highlight: ${e}`);
56949
+ log_error(`${gobj_short_name(gobj)}: cannot repaint the cards: ${e}`);
56839
56950
  }
56840
56951
  }
56952
+ /************************************************************
56953
+ * Repaint the cards whose highlight state CHANGES: the ones that had it
56954
+ * and lose it, plus the ones that gain it. Only those — a treedb graph
56955
+ * is redrawn per keystroke of the find box otherwise.
56956
+ ************************************************************/
56957
+ function apply_node_highlight(gobj, prev_ids, next_ids) {
56958
+ if (!gobj.priv.graph) return;
56959
+ let next = new Set(next_ids || []);
56960
+ let touched = /* @__PURE__ */ new Set([...prev_ids || [], ...next]);
56961
+ if (touched.size === 0) return;
56962
+ repaint_cards(gobj, touched);
56963
+ }
56841
56964
  function refresh_html_nodes_theme(gobj, theme) {
56842
56965
  let priv = gobj.priv;
56843
56966
  let graph = priv.graph;
56844
56967
  if (!graph) return;
56845
56968
  let nodes = graph.getData().nodes || [];
56846
56969
  let highlighted = new Set(priv._focus_ids || []);
56970
+ let selected = new Set(priv._selected_paint_ids || []);
56847
56971
  let updates = [];
56848
56972
  for (let i = 0; i < nodes.length; i++) {
56849
56973
  let id = nodes[i].id;
56850
- let html = node_innerHTML_of(graph.getNodeData(id), theme, highlighted.has(id));
56974
+ let html = node_innerHTML_of(graph.getNodeData(id), theme, highlighted.has(id), selected.has(id));
56851
56975
  if (html !== null) updates.push({
56852
56976
  id,
56853
56977
  style: { innerHTML: html }
@@ -56877,12 +57001,29 @@ function refresh_default_edges_theme(gobj, theme) {
56877
57001
  if (updates.length > 0) graph.updateEdgeData(updates);
56878
57002
  }
56879
57003
  /************************************************************
57004
+ * The rings a card can wear, composed into one `box-shadow`.
57005
+ *
57006
+ * A node can be a find match and be selected at the same time, so
57007
+ * neither ring may be written by overwriting the other: the amber
57008
+ * halo hugs the card and the blue selection ring is drawn outside
57009
+ * it, which is also the order that reads correctly when only one
57010
+ * of them is on.
57011
+ ************************************************************/
57012
+ function ring_shadow(highlight, selected, base_shadow) {
57013
+ let rings = [];
57014
+ if (highlight) rings.push(`0 0 0 4px ${HIGHLIGHT_HALO}`);
57015
+ if (selected) rings.push(`0 0 0 ${highlight ? "7px" : "3px"} ${SELECT_RING}`);
57016
+ if (base_shadow) rings.push(base_shadow);
57017
+ if (!rings.length) return "";
57018
+ return `box-shadow: ${rings.join(", ")};`;
57019
+ }
57020
+ /************************************************************
56880
57021
  * Build innerHTML for pure-child (leaf) nodes: a compact
56881
57022
  * chip-card. Same colour/typography family as the entity card
56882
57023
  * but lighter (1px border, no shadow, single line). The name is
56883
57024
  * always legible (ellipsis + native title tooltip on overflow).
56884
57025
  ************************************************************/
56885
- function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
57026
+ function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected) {
56886
57027
  let title = key || label;
56887
57028
  let dark = theme === "dark";
56888
57029
  let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
@@ -56901,7 +57042,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
56901
57042
  height: 100%;
56902
57043
  background: ${bg};
56903
57044
  border: ${highlight ? "3px" : "1px"} solid ${border};
56904
- ${highlight ? `box-shadow: 0 0 0 4px ${HIGHLIGHT_HALO};` : ""}
57045
+ ${ring_shadow(highlight, selected, "")}
56905
57046
  border-radius: 8px;
56906
57047
  color: ${text_color};
56907
57048
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
@@ -56927,7 +57068,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
56927
57068
  * colour is kept (per-topic differentiation) but softened via
56928
57069
  * color-mix instead of a harsh saturated fill. Theme-aware.
56929
57070
  ************************************************************/
56930
- function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight) {
57071
+ function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected) {
56931
57072
  let title = key || label;
56932
57073
  let dark = theme === "dark";
56933
57074
  let surface = dark ? "#1b2230" : "#ffffff";
@@ -56969,7 +57110,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
56969
57110
  background: ${bg};
56970
57111
  border: ${highlight ? "3px" : "1.5px"} ${border_style} ${border};
56971
57112
  border-radius: 10px;
56972
- box-shadow: ${highlight ? `0 0 0 4px ${HIGHLIGHT_HALO}, ${shadow}` : shadow};
57113
+ ${ring_shadow(highlight, selected, shadow)}
56973
57114
  color: ${title_color};
56974
57115
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
56975
57116
  display: flex;
@@ -57013,17 +57154,89 @@ function execute_delete_node(gobj, nodeData) {
57013
57154
  deselect_node(gobj);
57014
57155
  }
57015
57156
  /************************************************************
57157
+ * Every node in the graph.
57158
+ ************************************************************/
57159
+ function select_all_nodes(gobj) {
57160
+ let graph = gobj.priv.graph;
57161
+ if (!graph) return;
57162
+ set_selection(gobj, ((graph.getData() || {}).nodes || []).map((nd) => nd.id));
57163
+ }
57164
+ /************************************************************
57165
+ * The nodes currently selected, as their node data.
57166
+ ************************************************************/
57167
+ function selected_nodes_data(gobj) {
57168
+ let priv = gobj.priv;
57169
+ let graph = priv.graph;
57170
+ let ids = new Set(selected_node_ids(gobj));
57171
+ for (let id of priv._selected_paint_ids || []) ids.add(id);
57172
+ if (priv._selected_node_id) ids.add(priv._selected_node_id);
57173
+ let out = [];
57174
+ for (let id of ids) {
57175
+ let nd;
57176
+ try {
57177
+ nd = graph.getNodeData(id);
57178
+ } catch (e) {
57179
+ continue;
57180
+ }
57181
+ if (nd && nd.data && nd.data.desc) out.push(nd);
57182
+ }
57183
+ return out;
57184
+ }
57185
+ /************************************************************
57186
+ * What a delete takes with it, for ONE node or for a set.
57187
+ *
57188
+ * Same sentence in both cases and the same keys, because it is the
57189
+ * same warning: these views delete with `force`, which UNLINKS the
57190
+ * children (they survive, loose) and cleans the node off its
57191
+ * parents. Over a set the two numbers are the sums -- an operator
57192
+ * about to detach eleven records has to read eleven, not "are you
57193
+ * sure".
57194
+ ************************************************************/
57195
+ function delete_question(nodes) {
57196
+ let children = 0;
57197
+ let parents = 0;
57198
+ for (let nd of nodes) {
57199
+ let impact = delete_impact(nd.data.desc, nd.data.record || {});
57200
+ children += impact.children;
57201
+ parents += impact.parents;
57202
+ }
57203
+ let question;
57204
+ if (nodes.length === 1) {
57205
+ let nd = nodes[0];
57206
+ question = t("delete") + " " + nd.data.desc.topic_name + ": " + (nd.data.record || {}).id + "?";
57207
+ } else question = t("delete") + " " + nodes.length + " " + t("records") + "?";
57208
+ if (children > 0) question += "\n" + children + " " + t("children will be unlinked, not deleted", { count: children });
57209
+ if (parents > 0) question += "\n" + t("it will be detached from") + " " + parents + " " + t("parents", { count: parents });
57210
+ return question;
57211
+ }
57212
+ /************************************************************
57213
+ * Delete every selected node (the Delete key).
57214
+ ************************************************************/
57215
+ function request_delete_selection(gobj) {
57216
+ let nodes = selected_nodes_data(gobj);
57217
+ if (!nodes.length) return;
57218
+ if (!gobj_read_bool_attr(gobj, "confirm_delete_node")) {
57219
+ execute_delete_selection(gobj, nodes);
57220
+ return;
57221
+ }
57222
+ show_confirm_popover(gobj, null, delete_question(nodes), "delete", "#ff4d4f", () => execute_delete_selection(gobj, nodes), "_delete_confirm_el");
57223
+ }
57224
+ function execute_delete_selection(gobj, nodes) {
57225
+ let priv = gobj.priv;
57226
+ hide_delete_confirm(gobj);
57227
+ for (let nd of nodes) gobj_publish_event(gobj, "EV_DELETE_NODE", {
57228
+ treedb_name: priv.treedb_name,
57229
+ topic_name: nd.data.desc.topic_name,
57230
+ record: nd.data.record
57231
+ });
57232
+ deselect_node(gobj);
57233
+ }
57234
+ /************************************************************
57016
57235
  * Show a confirmation popover next to the delete icon.
57017
57236
  ************************************************************/
57018
57237
  function show_delete_confirm(gobj, nodeData) {
57019
57238
  let priv = gobj.priv;
57020
- let record = nodeData.data.record || {};
57021
- let topic_name = nodeData.data.desc.topic_name;
57022
- let impact = delete_impact(nodeData.data.desc, record);
57023
- let question = t("delete") + " " + topic_name + ": " + record.id + "?";
57024
- if (impact.children > 0) question += "\n" + impact.children + " " + t("children will be unlinked, not deleted", { count: impact.children });
57025
- if (impact.parents > 0) question += "\n" + t("it will be detached from") + " " + impact.parents + " " + t("parents", { count: impact.parents });
57026
- show_confirm_popover(gobj, priv._node_delete_el, question, "delete", "#ff4d4f", () => execute_delete_node(gobj, nodeData), "_delete_confirm_el");
57239
+ show_confirm_popover(gobj, priv._node_delete_el, delete_question([nodeData]), "delete", "#ff4d4f", () => execute_delete_node(gobj, nodeData), "_delete_confirm_el");
57027
57240
  }
57028
57241
  function hide_delete_confirm(gobj) {
57029
57242
  hide_overlay(gobj, "_delete_confirm_el");
@@ -57890,6 +58103,49 @@ function ac_node_drag_end(gobj, event, kw, src) {
57890
58103
  return 0;
57891
58104
  }
57892
58105
  /************************************************************
58106
+ * A key, while the graph has focus.
58107
+ *
58108
+ * Full screen keeps the two keys it always had. The rest belong to
58109
+ * the selection, so they only mean anything in edition -- outside
58110
+ * it there is nothing selected to clear, select or delete.
58111
+ ************************************************************/
58112
+ function ac_key_down(gobj, event, kw, src) {
58113
+ let priv = gobj.priv;
58114
+ let key = kw && kw.key || "";
58115
+ if (gobj_read_bool_attr(gobj, "with_fullscreen")) {
58116
+ let fullscreen = graph_get_plugin(gobj, "fullscreen");
58117
+ if (fullscreen) {
58118
+ if (key === "F" || key === "f") fullscreen.request();
58119
+ else if (key === "Escape") fullscreen.exit();
58120
+ }
58121
+ }
58122
+ if (!priv.edit_mode) return 0;
58123
+ if (key === "Escape") {
58124
+ deselect_node(gobj);
58125
+ return 0;
58126
+ }
58127
+ if (kw.ctrl && (key === "a" || key === "A")) {
58128
+ select_all_nodes(gobj);
58129
+ return 0;
58130
+ }
58131
+ if (key === "Delete" || key === "Backspace") {
58132
+ request_delete_selection(gobj);
58133
+ return 0;
58134
+ }
58135
+ return 0;
58136
+ }
58137
+ /************************************************************
58138
+ * The rubber band let go: these are the nodes it enclosed.
58139
+ *
58140
+ * They arrive in the kw because `onSelect` runs BEFORE G6 writes
58141
+ * the state, so asking the graph here would answer the previous
58142
+ * selection.
58143
+ ************************************************************/
58144
+ function ac_brush_select(gobj, event, kw, src) {
58145
+ set_selection(gobj, kw && kw.ids || []);
58146
+ return 0;
58147
+ }
58148
+ /************************************************************
57893
58149
  * Node click - publish vertex clicked event
57894
58150
  ************************************************************/
57895
58151
  function ac_node_click(gobj, event, kw, src) {
@@ -57904,7 +58160,8 @@ function ac_node_click(gobj, event, kw, src) {
57904
58160
  topic_name: nodedata.data.desc.topic_name,
57905
58161
  record: nodedata.data.record
57906
58162
  });
57907
- if (priv.edit_mode) {
58163
+ if (priv.edit_mode && kw.evt.shiftKey) toggle_in_selection(gobj, node_id);
58164
+ else if (priv.edit_mode) {
57908
58165
  let containerRect = priv.$container.getBoundingClientRect();
57909
58166
  let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
57910
58167
  let port_key = detect_port_click(gobj, node_id, canvasPoint[0], canvasPoint[1]);
@@ -58131,6 +58388,16 @@ function create_gclass$1(gclass_name) {
58131
58388
  ac_node_drag_end,
58132
58389
  null
58133
58390
  ],
58391
+ [
58392
+ "EV_BRUSH_SELECT",
58393
+ ac_brush_select,
58394
+ null
58395
+ ],
58396
+ [
58397
+ "EV_KEY_DOWN",
58398
+ ac_key_down,
58399
+ null
58400
+ ],
58134
58401
  [
58135
58402
  "EV_ZOOM_IN",
58136
58403
  ac_zoom_in,
@@ -58239,6 +58506,8 @@ function create_gclass$1(gclass_name) {
58239
58506
  ["EV_NODE_CONTEXT_MENU", 0],
58240
58507
  ["EV_CANVAS_CLICK", 0],
58241
58508
  ["EV_NODE_DRAG_END", 0],
58509
+ ["EV_BRUSH_SELECT", 0],
58510
+ ["EV_KEY_DOWN", 0],
58242
58511
  ["EV_ZOOM_IN", 0],
58243
58512
  ["EV_ZOOM_OUT", 0],
58244
58513
  ["EV_ZOOM_RESET", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.15.0",
3
+ "version": "7.17.0",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",