@yuneta/gobj-ui 7.23.10 → 7.23.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -877,11 +877,19 @@ a card with a border sitting on the graph). Above that width nothing changes.
877
877
 
878
878
  **New keys for consumers: `show toolbar`, `hide toolbar`.**
879
879
 
880
- What is still desktop-only, deliberately: **multi-selection**. `brush-select`
881
- is bound to `shift`, and `drag-canvas` stands aside for the same key — there
882
- is no shift on a phone, and giving the rubber band a touch gesture means
883
- taking one away from panning. Selecting nodes one at a time works on a phone;
884
- selecting a region does not.
880
+ **Multi-selection reaches a finger through a MODE, not through a gesture**
881
+ (`7.23.11`). Both halves of it — shift+click to add a card, shift+drag for the
882
+ rubber band — hang off a key a phone does not have, and there is no spare
883
+ gesture to give them: G6 binds panning and the band to the same plain drag, so
884
+ one of the two has to stand aside. The edit toolbar carries a **selection
885
+ mode** toggle (the dashed marquee, next to `+`): while it is on, a tap picks a
886
+ card and a drag on the background draws the band, and panning is what stands
887
+ aside. It is deliberately a button and not a heuristic — the toolbar says which
888
+ of the two the graph is listening for, and turning it off gives the camera
889
+ back. It is not device-specific: the same button spares a desktop reader the
890
+ key. Leaving edition turns it off, and it is not persisted.
891
+
892
+ **New key for consumers: `selection mode`.**
885
893
 
886
894
  ### Tabs opened at runtime, and the two decisions their url costs
887
895
 
@@ -928,6 +936,7 @@ clicked":
928
936
  | click a node | selects it **and opens it**: resize handles, ports, popovers |
929
937
  | **shift + click** | adds that node to the selection, or takes it out |
930
938
  | **shift + drag on the canvas** | rubber band: the selection becomes what it enclosed |
939
+ | the **selection mode** button | makes those two the PLAIN gestures — a tap picks, a canvas drag is the band — for as long as it is on (panning stands aside meanwhile) |
931
940
  | **ctrl/cmd + A** | every node |
932
941
  | the **fit-to-selection** button | puts the viewport on what is selected |
933
942
  | drag any selected node | **moves the whole selection**, as one undo |
@@ -47493,6 +47493,19 @@ var ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
47493
47493
  <symbol id="g6-icon-toolbar-hide" viewBox="0 0 384 512">
47494
47494
  <path fill="currentColor" d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/>
47495
47495
  </symbol>
47496
+ <!-- dashed marquee (selection mode).
47497
+ The band ITSELF, because that is what the button turns on: a
47498
+ drag on the canvas draws one and a tap picks a card. Not the
47499
+ fit-to-selection glyph next to it in the other toolbar -- that
47500
+ one is solid brackets around a solid object and means "put the
47501
+ camera on what is picked", which is the opposite end of the
47502
+ gesture. Dashes: 16 segments of 59 with gaps of 36 over a
47503
+ perimeter of 1520, so they land on the corners. -->
47504
+ <symbol id="g6-icon-select-mode" viewBox="0 0 512 512">
47505
+ <rect x="46" y="86" width="420" height="340" fill="none"
47506
+ stroke="currentColor" stroke-width="40"
47507
+ stroke-dasharray="59 36"/>
47508
+ </symbol>
47496
47509
  <!-- floppy-disk (save) -->
47497
47510
  <symbol id="g6-icon-save" viewBox="0 0 448 512">
47498
47511
  <path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-242.7c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32L64 32zm32 96c0-17.7 14.3-32 32-32l160 0c17.7 0 32 14.3 32 32l0 64c0 17.7-14.3 32-32 32l-160 0c-17.7 0-32-14.3-32-32l0-64zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/>
@@ -47742,6 +47755,7 @@ var PRIVATE_DATA$4 = {
47742
47755
  toolbar_collapsed: true,
47743
47756
  _toolbars_could_collapse: null,
47744
47757
  _on_focusout_restore: null,
47758
+ selection_mode: false,
47745
47759
  _selected_paint_ids: [],
47746
47760
  _pending_focus_topic: null,
47747
47761
  _pending_find: null,
@@ -48065,6 +48079,7 @@ function update_toolbar(gobj) {
48065
48079
  configure_toolbar_edit(gobj);
48066
48080
  if (graph_get_plugin(gobj, "toolbar-edit")) graph.updatePlugin({ key: "toolbar-edit" });
48067
48081
  update_zoom_selection_button(gobj);
48082
+ update_selection_mode_button(gobj);
48068
48083
  }
48069
48084
  /************************************************************
48070
48085
  * The floating toolbars follow the theme.
@@ -48279,7 +48294,8 @@ function configure_toolbar(gobj) {
48279
48294
  * Contains edit-mode buttons: Undo, Redo, Save
48280
48295
  ************************************************************/
48281
48296
  function configure_toolbar_edit(gobj) {
48282
- if (!gobj.priv.edit_mode || toolbars_collapsed(gobj)) {
48297
+ let priv = gobj.priv;
48298
+ if (!priv.edit_mode || toolbars_collapsed(gobj)) {
48283
48299
  graph_remove_plugin(gobj, "toolbar-edit");
48284
48300
  return;
48285
48301
  }
@@ -48298,6 +48314,12 @@ function configure_toolbar_edit(gobj) {
48298
48314
  className: "EV_CREATE_NODE_BTN color_create_state",
48299
48315
  title: (0, i18next.t)("create node")
48300
48316
  },
48317
+ {
48318
+ id: "g6-icon-select-mode",
48319
+ value: "selection-mode",
48320
+ className: "EV_TOGGLE_SELECTION_MODE" + (priv.selection_mode ? " color_active_state" : ""),
48321
+ title: (0, i18next.t)("selection mode")
48322
+ },
48301
48323
  {
48302
48324
  id: "g6-icon-save",
48303
48325
  value: "save",
@@ -48326,6 +48348,9 @@ function configure_toolbar_edit(gobj) {
48326
48348
  case "create-node":
48327
48349
  toggle_create_popover(gobj);
48328
48350
  break;
48351
+ case "selection-mode":
48352
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_SELECTION_MODE", {}, gobj);
48353
+ break;
48329
48354
  case "undo":
48330
48355
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_HISTORY_UNDO", {}, gobj);
48331
48356
  break;
@@ -48369,7 +48394,7 @@ function configure_behaviour(gobj) {
48369
48394
  type: "drag-canvas",
48370
48395
  key: "drag-canvas",
48371
48396
  enable: (event) => {
48372
- if (event.shiftKey) return false;
48397
+ if (event.shiftKey || priv.selection_mode) return false;
48373
48398
  if ("targetType" in event) return event.targetType === "canvas";
48374
48399
  return true;
48375
48400
  }
@@ -48379,7 +48404,8 @@ function configure_behaviour(gobj) {
48379
48404
  {
48380
48405
  type: BRUSH_SELECT_OWNED,
48381
48406
  key: "brush-select",
48382
- trigger: ["shift"],
48407
+ trigger: priv.selection_mode ? [] : ["shift"],
48408
+ enable: (event) => event.targetType === "canvas",
48383
48409
  enableElements: ["node"],
48384
48410
  immediately: false,
48385
48411
  onSelect: (states) => {
@@ -48395,7 +48421,10 @@ function configure_behaviour(gobj) {
48395
48421
  break;
48396
48422
  default: (0, _yuneta_gobj_js.log_error)(`operation mode unknown: ${operation_mode}`);
48397
48423
  }
48398
- if (!priv.edit_mode) deselect_node(gobj);
48424
+ if (!priv.edit_mode) {
48425
+ priv.selection_mode = false;
48426
+ deselect_node(gobj);
48427
+ }
48399
48428
  graph_write_behaviors(gobj, behaviors);
48400
48429
  }
48401
48430
  /************************************************************
@@ -49721,6 +49750,21 @@ function update_zoom_selection_button(gobj) {
49721
49750
  else disableElements($container, ".EV_ZOOM_SELECTION");
49722
49751
  }
49723
49752
  /************************************************************
49753
+ * The selection mode is a state, so it has to LOOK like one.
49754
+ *
49755
+ * Toggled on the element rather than re-rendered with the
49756
+ * toolbar, for the same reason as the button above: a
49757
+ * re-render rebuilds the innerHTML and drops what every OTHER
49758
+ * button was told.
49759
+ ************************************************************/
49760
+ function update_selection_mode_button(gobj) {
49761
+ let priv = gobj.priv;
49762
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
49763
+ if (!$container) return;
49764
+ if (!$container.querySelector(".EV_TOGGLE_SELECTION_MODE")) return;
49765
+ set_active_state($container, ".EV_TOGGLE_SELECTION_MODE", priv.selection_mode);
49766
+ }
49767
+ /************************************************************
49724
49768
  * Select a SET of nodes: what a marquee and a shift-click do.
49725
49769
  *
49726
49770
  * It leaves `_selected_node_id` null even for a set of one,
@@ -52323,6 +52367,40 @@ function ac_set_operation_mode$1(gobj, event, kw, src) {
52323
52367
  return 0;
52324
52368
  }
52325
52369
  /************************************************************
52370
+ * Selection mode on/off, from the edit toolbar.
52371
+ *
52372
+ * Shift is what the selection is built on, and a telephone has
52373
+ * no Shift: the marquee and the add-to-selection click were a
52374
+ * desktop feature by construction. This makes that key a MODE
52375
+ * -- while it is on, a tap picks a card and a drag on the
52376
+ * background draws the band, so the same two gestures are
52377
+ * reachable with one finger.
52378
+ *
52379
+ * It costs panning while it is on, and deliberately: G6 binds
52380
+ * both to a plain drag, and a gesture cannot be two things.
52381
+ * That is why it is a button and not a heuristic -- the reader
52382
+ * turns it off to move the camera, and the toolbar says which
52383
+ * of the two the graph is listening for.
52384
+ ************************************************************/
52385
+ function ac_toggle_selection_mode(gobj, event, kw, src) {
52386
+ let priv = gobj.priv;
52387
+ if (!priv.edit_mode) {
52388
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: selection mode asked outside edition`);
52389
+ return 0;
52390
+ }
52391
+ priv.selection_mode = !priv.selection_mode;
52392
+ if (priv.selection_mode) {
52393
+ deselect_port(gobj);
52394
+ hide_node_icon(gobj);
52395
+ hide_node_popover(gobj);
52396
+ hide_resize_handles(gobj);
52397
+ priv._selected_node_id = null;
52398
+ }
52399
+ configure_behaviour(gobj);
52400
+ update_selection_mode_button(gobj);
52401
+ return 0;
52402
+ }
52403
+ /************************************************************
52326
52404
  * Node drag end
52327
52405
  ************************************************************/
52328
52406
  function ac_node_drag_end(gobj, event, kw, src) {
@@ -52387,7 +52465,7 @@ function ac_node_click$2(gobj, event, kw, src) {
52387
52465
  topic_name: nodedata.data.desc.topic_name,
52388
52466
  record: nodedata.data.record
52389
52467
  });
52390
- if (priv.edit_mode && kw.evt.shiftKey) toggle_in_selection(gobj, node_id);
52468
+ if (priv.edit_mode && (kw.evt.shiftKey || priv.selection_mode)) toggle_in_selection(gobj, node_id);
52391
52469
  else if (priv.edit_mode) {
52392
52470
  let containerRect = priv.$container.getBoundingClientRect();
52393
52471
  let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
@@ -52685,6 +52763,11 @@ function create_gclass$4(gclass_name) {
52685
52763
  ac_set_operation_mode$1,
52686
52764
  null
52687
52765
  ],
52766
+ [
52767
+ "EV_TOGGLE_SELECTION_MODE",
52768
+ ac_toggle_selection_mode,
52769
+ null
52770
+ ],
52688
52771
  [
52689
52772
  "EV_SAVE_GRAPH",
52690
52773
  ac_save_graph,
@@ -52759,6 +52842,7 @@ function create_gclass$4(gclass_name) {
52759
52842
  ["EV_FULLSCREEN", 0],
52760
52843
  ["EV_SET_LAYOUT", 0],
52761
52844
  ["EV_SET_OPERATION_MODE", 0],
52845
+ ["EV_TOGGLE_SELECTION_MODE", 0],
52762
52846
  ["EV_HISTORY_UNDO", 0],
52763
52847
  ["EV_HISTORY_REDO", 0],
52764
52848
  ["EV_SAVE_GRAPH", 0],
@@ -47480,6 +47480,19 @@ var ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
47480
47480
  <symbol id="g6-icon-toolbar-hide" viewBox="0 0 384 512">
47481
47481
  <path fill="currentColor" d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/>
47482
47482
  </symbol>
47483
+ <!-- dashed marquee (selection mode).
47484
+ The band ITSELF, because that is what the button turns on: a
47485
+ drag on the canvas draws one and a tap picks a card. Not the
47486
+ fit-to-selection glyph next to it in the other toolbar -- that
47487
+ one is solid brackets around a solid object and means "put the
47488
+ camera on what is picked", which is the opposite end of the
47489
+ gesture. Dashes: 16 segments of 59 with gaps of 36 over a
47490
+ perimeter of 1520, so they land on the corners. -->
47491
+ <symbol id="g6-icon-select-mode" viewBox="0 0 512 512">
47492
+ <rect x="46" y="86" width="420" height="340" fill="none"
47493
+ stroke="currentColor" stroke-width="40"
47494
+ stroke-dasharray="59 36"/>
47495
+ </symbol>
47483
47496
  <!-- floppy-disk (save) -->
47484
47497
  <symbol id="g6-icon-save" viewBox="0 0 448 512">
47485
47498
  <path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-242.7c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32L64 32zm32 96c0-17.7 14.3-32 32-32l160 0c17.7 0 32 14.3 32 32l0 64c0 17.7-14.3 32-32 32l-160 0c-17.7 0-32-14.3-32-32l0-64zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/>
@@ -47729,6 +47742,7 @@ var PRIVATE_DATA$4 = {
47729
47742
  toolbar_collapsed: true,
47730
47743
  _toolbars_could_collapse: null,
47731
47744
  _on_focusout_restore: null,
47745
+ selection_mode: false,
47732
47746
  _selected_paint_ids: [],
47733
47747
  _pending_focus_topic: null,
47734
47748
  _pending_find: null,
@@ -48052,6 +48066,7 @@ function update_toolbar(gobj) {
48052
48066
  configure_toolbar_edit(gobj);
48053
48067
  if (graph_get_plugin(gobj, "toolbar-edit")) graph.updatePlugin({ key: "toolbar-edit" });
48054
48068
  update_zoom_selection_button(gobj);
48069
+ update_selection_mode_button(gobj);
48055
48070
  }
48056
48071
  /************************************************************
48057
48072
  * The floating toolbars follow the theme.
@@ -48266,7 +48281,8 @@ function configure_toolbar(gobj) {
48266
48281
  * Contains edit-mode buttons: Undo, Redo, Save
48267
48282
  ************************************************************/
48268
48283
  function configure_toolbar_edit(gobj) {
48269
- if (!gobj.priv.edit_mode || toolbars_collapsed(gobj)) {
48284
+ let priv = gobj.priv;
48285
+ if (!priv.edit_mode || toolbars_collapsed(gobj)) {
48270
48286
  graph_remove_plugin(gobj, "toolbar-edit");
48271
48287
  return;
48272
48288
  }
@@ -48285,6 +48301,12 @@ function configure_toolbar_edit(gobj) {
48285
48301
  className: "EV_CREATE_NODE_BTN color_create_state",
48286
48302
  title: t("create node")
48287
48303
  },
48304
+ {
48305
+ id: "g6-icon-select-mode",
48306
+ value: "selection-mode",
48307
+ className: "EV_TOGGLE_SELECTION_MODE" + (priv.selection_mode ? " color_active_state" : ""),
48308
+ title: t("selection mode")
48309
+ },
48288
48310
  {
48289
48311
  id: "g6-icon-save",
48290
48312
  value: "save",
@@ -48313,6 +48335,9 @@ function configure_toolbar_edit(gobj) {
48313
48335
  case "create-node":
48314
48336
  toggle_create_popover(gobj);
48315
48337
  break;
48338
+ case "selection-mode":
48339
+ gobj_send_event(gobj, "EV_TOGGLE_SELECTION_MODE", {}, gobj);
48340
+ break;
48316
48341
  case "undo":
48317
48342
  gobj_send_event(gobj, "EV_HISTORY_UNDO", {}, gobj);
48318
48343
  break;
@@ -48356,7 +48381,7 @@ function configure_behaviour(gobj) {
48356
48381
  type: "drag-canvas",
48357
48382
  key: "drag-canvas",
48358
48383
  enable: (event) => {
48359
- if (event.shiftKey) return false;
48384
+ if (event.shiftKey || priv.selection_mode) return false;
48360
48385
  if ("targetType" in event) return event.targetType === "canvas";
48361
48386
  return true;
48362
48387
  }
@@ -48366,7 +48391,8 @@ function configure_behaviour(gobj) {
48366
48391
  {
48367
48392
  type: BRUSH_SELECT_OWNED,
48368
48393
  key: "brush-select",
48369
- trigger: ["shift"],
48394
+ trigger: priv.selection_mode ? [] : ["shift"],
48395
+ enable: (event) => event.targetType === "canvas",
48370
48396
  enableElements: ["node"],
48371
48397
  immediately: false,
48372
48398
  onSelect: (states) => {
@@ -48382,7 +48408,10 @@ function configure_behaviour(gobj) {
48382
48408
  break;
48383
48409
  default: log_error(`operation mode unknown: ${operation_mode}`);
48384
48410
  }
48385
- if (!priv.edit_mode) deselect_node(gobj);
48411
+ if (!priv.edit_mode) {
48412
+ priv.selection_mode = false;
48413
+ deselect_node(gobj);
48414
+ }
48386
48415
  graph_write_behaviors(gobj, behaviors);
48387
48416
  }
48388
48417
  /************************************************************
@@ -49708,6 +49737,21 @@ function update_zoom_selection_button(gobj) {
49708
49737
  else disableElements($container, ".EV_ZOOM_SELECTION");
49709
49738
  }
49710
49739
  /************************************************************
49740
+ * The selection mode is a state, so it has to LOOK like one.
49741
+ *
49742
+ * Toggled on the element rather than re-rendered with the
49743
+ * toolbar, for the same reason as the button above: a
49744
+ * re-render rebuilds the innerHTML and drops what every OTHER
49745
+ * button was told.
49746
+ ************************************************************/
49747
+ function update_selection_mode_button(gobj) {
49748
+ let priv = gobj.priv;
49749
+ let $container = gobj_read_attr(gobj, "$container");
49750
+ if (!$container) return;
49751
+ if (!$container.querySelector(".EV_TOGGLE_SELECTION_MODE")) return;
49752
+ set_active_state($container, ".EV_TOGGLE_SELECTION_MODE", priv.selection_mode);
49753
+ }
49754
+ /************************************************************
49711
49755
  * Select a SET of nodes: what a marquee and a shift-click do.
49712
49756
  *
49713
49757
  * It leaves `_selected_node_id` null even for a set of one,
@@ -52310,6 +52354,40 @@ function ac_set_operation_mode$1(gobj, event, kw, src) {
52310
52354
  return 0;
52311
52355
  }
52312
52356
  /************************************************************
52357
+ * Selection mode on/off, from the edit toolbar.
52358
+ *
52359
+ * Shift is what the selection is built on, and a telephone has
52360
+ * no Shift: the marquee and the add-to-selection click were a
52361
+ * desktop feature by construction. This makes that key a MODE
52362
+ * -- while it is on, a tap picks a card and a drag on the
52363
+ * background draws the band, so the same two gestures are
52364
+ * reachable with one finger.
52365
+ *
52366
+ * It costs panning while it is on, and deliberately: G6 binds
52367
+ * both to a plain drag, and a gesture cannot be two things.
52368
+ * That is why it is a button and not a heuristic -- the reader
52369
+ * turns it off to move the camera, and the toolbar says which
52370
+ * of the two the graph is listening for.
52371
+ ************************************************************/
52372
+ function ac_toggle_selection_mode(gobj, event, kw, src) {
52373
+ let priv = gobj.priv;
52374
+ if (!priv.edit_mode) {
52375
+ log_error(`${gobj_short_name(gobj)}: selection mode asked outside edition`);
52376
+ return 0;
52377
+ }
52378
+ priv.selection_mode = !priv.selection_mode;
52379
+ if (priv.selection_mode) {
52380
+ deselect_port(gobj);
52381
+ hide_node_icon(gobj);
52382
+ hide_node_popover(gobj);
52383
+ hide_resize_handles(gobj);
52384
+ priv._selected_node_id = null;
52385
+ }
52386
+ configure_behaviour(gobj);
52387
+ update_selection_mode_button(gobj);
52388
+ return 0;
52389
+ }
52390
+ /************************************************************
52313
52391
  * Node drag end
52314
52392
  ************************************************************/
52315
52393
  function ac_node_drag_end(gobj, event, kw, src) {
@@ -52374,7 +52452,7 @@ function ac_node_click$2(gobj, event, kw, src) {
52374
52452
  topic_name: nodedata.data.desc.topic_name,
52375
52453
  record: nodedata.data.record
52376
52454
  });
52377
- if (priv.edit_mode && kw.evt.shiftKey) toggle_in_selection(gobj, node_id);
52455
+ if (priv.edit_mode && (kw.evt.shiftKey || priv.selection_mode)) toggle_in_selection(gobj, node_id);
52378
52456
  else if (priv.edit_mode) {
52379
52457
  let containerRect = priv.$container.getBoundingClientRect();
52380
52458
  let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
@@ -52672,6 +52750,11 @@ function create_gclass$4(gclass_name) {
52672
52750
  ac_set_operation_mode$1,
52673
52751
  null
52674
52752
  ],
52753
+ [
52754
+ "EV_TOGGLE_SELECTION_MODE",
52755
+ ac_toggle_selection_mode,
52756
+ null
52757
+ ],
52675
52758
  [
52676
52759
  "EV_SAVE_GRAPH",
52677
52760
  ac_save_graph,
@@ -52746,6 +52829,7 @@ function create_gclass$4(gclass_name) {
52746
52829
  ["EV_FULLSCREEN", 0],
52747
52830
  ["EV_SET_LAYOUT", 0],
52748
52831
  ["EV_SET_OPERATION_MODE", 0],
52832
+ ["EV_TOGGLE_SELECTION_MODE", 0],
52749
52833
  ["EV_HISTORY_UNDO", 0],
52750
52834
  ["EV_HISTORY_REDO", 0],
52751
52835
  ["EV_SAVE_GRAPH", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.10",
3
+ "version": "7.23.11",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -350,6 +350,9 @@ let PRIVATE_DATA = {
350
350
  toolbar_collapsed: true, // floating toolbars folded (narrow only)
351
351
  _toolbars_could_collapse: null, // last answer, to notice it changed
352
352
  _on_focusout_restore: null, // ...and putting it back when it goes nowhere
353
+ selection_mode: false, // taps pick cards, a canvas drag is the band
354
+ // (edition only, transient: a mode nobody
355
+ // can see is a mode nobody can leave)
353
356
  _selected_paint_ids: [], // node ids whose card is PAINTED selected
354
357
  // (G6's 'selected' state is the selection
355
358
  // itself; this is what is on screen, and
@@ -1026,6 +1029,7 @@ function update_toolbar(gobj)
1026
1029
  /* A re-render rebuilds the innerHTML, so a button whose enabled
1027
1030
  * state is not part of getItems() has to be told again. */
1028
1031
  update_zoom_selection_button(gobj);
1032
+ update_selection_mode_button(gobj);
1029
1033
  }
1030
1034
 
1031
1035
  /************************************************************
@@ -1326,6 +1330,19 @@ function configure_toolbar_edit(gobj)
1326
1330
  getItems: () => {
1327
1331
  return [
1328
1332
  { id: 'g6-icon-plus', value: 'create-node', className: 'EV_CREATE_NODE_BTN color_create_state', title: t('create node') },
1333
+ /* The mode is carried in the class getItems()
1334
+ * returns, not only pushed onto the element after
1335
+ * the fact: this toolbar is rebuilt whole (a fold,
1336
+ * a language change), and the rebuilt DOM is not
1337
+ * there yet when the rebuild returns -- so a
1338
+ * button told afterwards came back OFF while the
1339
+ * graph was still listening for a selection. */
1340
+ {
1341
+ id: 'g6-icon-select-mode', value: 'selection-mode',
1342
+ className: 'EV_TOGGLE_SELECTION_MODE' +
1343
+ (priv.selection_mode? ' color_active_state' : ''),
1344
+ title: t('selection mode')
1345
+ },
1329
1346
  { id: 'g6-icon-save', value: 'save', className: 'EV_SAVE_GRAPH', title: t('save'), disabled: true },
1330
1347
  { id: 'g6-icon-undo', value: 'undo', className: 'EV_HISTORY_UNDO', title: t('undo'), disabled: true },
1331
1348
  { id: 'g6-icon-redo', value: 'redo', className: 'EV_HISTORY_REDO', title: t('redo'), disabled: true },
@@ -1336,6 +1353,9 @@ function configure_toolbar_edit(gobj)
1336
1353
  case 'create-node':
1337
1354
  toggle_create_popover(gobj);
1338
1355
  break;
1356
+ case 'selection-mode':
1357
+ gobj_send_event(gobj, "EV_TOGGLE_SELECTION_MODE", {}, gobj);
1358
+ break;
1339
1359
  case 'undo':
1340
1360
  gobj_send_event(gobj, "EV_HISTORY_UNDO", {}, gobj);
1341
1361
  break;
@@ -1429,7 +1449,7 @@ function configure_behaviour(gobj)
1429
1449
  type: "drag-canvas",
1430
1450
  key: "drag-canvas",
1431
1451
  enable: (event) => {
1432
- if(event.shiftKey) {
1452
+ if(event.shiftKey || priv.selection_mode) {
1433
1453
  return false;
1434
1454
  }
1435
1455
  if('targetType' in event) {
@@ -1458,7 +1478,18 @@ function configure_behaviour(gobj)
1458
1478
  * button lit on a graph nobody had touched. */
1459
1479
  type: BRUSH_SELECT_OWNED,
1460
1480
  key: "brush-select",
1461
- trigger: ["shift"],
1481
+ /* There is no Shift on a phone. The toolbar's
1482
+ * selection mode is that key made into a state:
1483
+ * with it on, the band needs no modifier -- and
1484
+ * panning stands aside above, because the two
1485
+ * cannot both be a plain drag. */
1486
+ trigger: priv.selection_mode ? [] : ["shift"],
1487
+ /* A band starts on the BACKGROUND. G6's default
1488
+ * (`enable: true`) does not look at what is under
1489
+ * the pointer, so a drag that begins on a card
1490
+ * drew a band and moved the card at the same
1491
+ * time. */
1492
+ enable: (event) => event.targetType === 'canvas',
1462
1493
  enableElements: ["node"],
1463
1494
  /* Left at G6's default (false): the set is read at
1464
1495
  * pointerup, not on every pointermove. Each answer
@@ -1494,6 +1525,9 @@ function configure_behaviour(gobj)
1494
1525
  break;
1495
1526
  }
1496
1527
  if(!priv.edit_mode) {
1528
+ /* The selection and the mode that picks it both belong to
1529
+ * edition, and neither survives leaving it. */
1530
+ priv.selection_mode = false;
1497
1531
  deselect_node(gobj);
1498
1532
  }
1499
1533
  graph_write_behaviors(gobj, behaviors);
@@ -3573,6 +3607,30 @@ function update_zoom_selection_button(gobj)
3573
3607
  }
3574
3608
  }
3575
3609
 
3610
+ /************************************************************
3611
+ * The selection mode is a state, so it has to LOOK like one.
3612
+ *
3613
+ * Toggled on the element rather than re-rendered with the
3614
+ * toolbar, for the same reason as the button above: a
3615
+ * re-render rebuilds the innerHTML and drops what every OTHER
3616
+ * button was told.
3617
+ ************************************************************/
3618
+ function update_selection_mode_button(gobj)
3619
+ {
3620
+ let priv = gobj.priv;
3621
+ let $container = gobj_read_attr(gobj, "$container");
3622
+
3623
+ if(!$container) {
3624
+ return;
3625
+ }
3626
+
3627
+ if(!$container.querySelector(".EV_TOGGLE_SELECTION_MODE")) {
3628
+ return; /* no edit toolbar: not in edition, or folded away */
3629
+ }
3630
+
3631
+ set_active_state($container, ".EV_TOGGLE_SELECTION_MODE", priv.selection_mode);
3632
+ }
3633
+
3576
3634
  /************************************************************
3577
3635
  * Select a SET of nodes: what a marquee and a shift-click do.
3578
3636
  *
@@ -7533,6 +7591,51 @@ function ac_set_operation_mode(gobj, event, kw, src)
7533
7591
  return 0;
7534
7592
  }
7535
7593
 
7594
+ /************************************************************
7595
+ * Selection mode on/off, from the edit toolbar.
7596
+ *
7597
+ * Shift is what the selection is built on, and a telephone has
7598
+ * no Shift: the marquee and the add-to-selection click were a
7599
+ * desktop feature by construction. This makes that key a MODE
7600
+ * -- while it is on, a tap picks a card and a drag on the
7601
+ * background draws the band, so the same two gestures are
7602
+ * reachable with one finger.
7603
+ *
7604
+ * It costs panning while it is on, and deliberately: G6 binds
7605
+ * both to a plain drag, and a gesture cannot be two things.
7606
+ * That is why it is a button and not a heuristic -- the reader
7607
+ * turns it off to move the camera, and the toolbar says which
7608
+ * of the two the graph is listening for.
7609
+ ************************************************************/
7610
+ function ac_toggle_selection_mode(gobj, event, kw, src)
7611
+ {
7612
+ let priv = gobj.priv;
7613
+
7614
+ if(!priv.edit_mode) {
7615
+ log_error(`${gobj_short_name(gobj)}: selection mode asked outside edition`);
7616
+ return 0;
7617
+ }
7618
+
7619
+ priv.selection_mode = !priv.selection_mode;
7620
+
7621
+ if(priv.selection_mode) {
7622
+ /* A set has no handles, no ports and no popover: those hang
7623
+ * off `_selected_node_id`, the node OPENED for editing, and
7624
+ * in this mode a tap no longer opens one. The selection
7625
+ * itself stays -- entering the mode is not a way to lose it. */
7626
+ deselect_port(gobj);
7627
+ hide_node_icon(gobj);
7628
+ hide_node_popover(gobj);
7629
+ hide_resize_handles(gobj);
7630
+ priv._selected_node_id = null;
7631
+ }
7632
+
7633
+ configure_behaviour(gobj);
7634
+ update_selection_mode_button(gobj);
7635
+
7636
+ return 0;
7637
+ }
7638
+
7536
7639
  /************************************************************
7537
7640
  * Node drag end
7538
7641
  ************************************************************/
@@ -7632,11 +7735,13 @@ function ac_node_click(gobj, event, kw, src)
7632
7735
  record: nodedata.data.record
7633
7736
  });
7634
7737
 
7635
- if(priv.edit_mode && kw.evt.shiftKey) {
7738
+ if(priv.edit_mode && (kw.evt.shiftKey || priv.selection_mode)) {
7636
7739
  /* Shift+click extends the selection, the way it does
7637
- * everywhere. It never looks for a port: a port is a
7638
- * one-node affordance, and this gesture is about the
7639
- * set. */
7740
+ * everywhere -- and so does a plain tap while the
7741
+ * toolbar's selection mode is on, which is that key
7742
+ * for a finger. It never looks for a port: a port is
7743
+ * a one-node affordance, and this gesture is about
7744
+ * the set. */
7640
7745
  toggle_in_selection(gobj, node_id);
7641
7746
  } else if(priv.edit_mode) {
7642
7747
  // Check if click hits a port
@@ -7941,6 +8046,7 @@ function create_gclass(gclass_name)
7941
8046
  ["EV_FULLSCREEN", ac_fullscreen, null],
7942
8047
  ["EV_SET_LAYOUT", ac_set_layout, null],
7943
8048
  ["EV_SET_OPERATION_MODE", ac_set_operation_mode, null],
8049
+ ["EV_TOGGLE_SELECTION_MODE", ac_toggle_selection_mode, null],
7944
8050
  ["EV_SAVE_GRAPH", ac_save_graph, null],
7945
8051
  ["EV_HISTORY_UNDO", ac_history_undo, null],
7946
8052
  ["EV_HISTORY_REDO", ac_history_redo, null],
@@ -7991,6 +8097,7 @@ function create_gclass(gclass_name)
7991
8097
  ["EV_FULLSCREEN", 0],
7992
8098
  ["EV_SET_LAYOUT", 0],
7993
8099
  ["EV_SET_OPERATION_MODE", 0],
8100
+ ["EV_TOGGLE_SELECTION_MODE", 0],
7994
8101
  ["EV_HISTORY_UNDO", 0],
7995
8102
  ["EV_HISTORY_REDO", 0],
7996
8103
  ["EV_SAVE_GRAPH", 0],
package/src/lib_icons.js CHANGED
@@ -127,6 +127,19 @@ const ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
127
127
  <symbol id="g6-icon-toolbar-hide" viewBox="0 0 384 512">
128
128
  <path fill="currentColor" d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/>
129
129
  </symbol>
130
+ <!-- dashed marquee (selection mode).
131
+ The band ITSELF, because that is what the button turns on: a
132
+ drag on the canvas draws one and a tap picks a card. Not the
133
+ fit-to-selection glyph next to it in the other toolbar -- that
134
+ one is solid brackets around a solid object and means "put the
135
+ camera on what is picked", which is the opposite end of the
136
+ gesture. Dashes: 16 segments of 59 with gaps of 36 over a
137
+ perimeter of 1520, so they land on the corners. -->
138
+ <symbol id="g6-icon-select-mode" viewBox="0 0 512 512">
139
+ <rect x="46" y="86" width="420" height="340" fill="none"
140
+ stroke="currentColor" stroke-width="40"
141
+ stroke-dasharray="59 36"/>
142
+ </symbol>
130
143
  <!-- floppy-disk (save) -->
131
144
  <symbol id="g6-icon-save" viewBox="0 0 448 512">
132
145
  <path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-242.7c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32L64 32zm32 96c0-17.7 14.3-32 32-32l160 0c17.7 0 32 14.3 32 32l0 64c0 17.7-14.3 32-32 32l-160 0c-17.7 0-32-14.3-32-32l0-64zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/>