@yuneta/gobj-ui 7.23.10 → 7.23.12

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,24 @@ 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 looks **pressed** while it is on (`pressed_state`, an inverted
888
+ neutral chip) rather than taking one of the palette's colours: each of those
889
+ names a KIND of action, and `7.23.11` borrowed undo/redo's violet for it —
890
+ which put the same colour on two neighbouring buttons for two different
891
+ reasons, and carried the whole state change in the hairline of an outline
892
+ glyph. It is deliberately a button and not a heuristic — the toolbar says which
893
+ of the two the graph is listening for, and turning it off gives the camera
894
+ back. It is not device-specific: the same button spares a desktop reader the
895
+ key. Leaving edition turns it off, and it is not persisted.
896
+
897
+ **New key for consumers: `selection mode`.**
885
898
 
886
899
  ### Tabs opened at runtime, and the two decisions their url costs
887
900
 
@@ -928,6 +941,7 @@ clicked":
928
941
  | click a node | selects it **and opens it**: resize handles, ports, popovers |
929
942
  | **shift + click** | adds that node to the selection, or takes it out |
930
943
  | **shift + drag on the canvas** | rubber band: the selection becomes what it enclosed |
944
+ | 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
945
  | **ctrl/cmd + A** | every node |
932
946
  | the **fit-to-selection** button | puts the viewport on what is selected |
933
947
  | drag any selected node | **moves the whole selection**, as one undo |
@@ -20597,6 +20597,19 @@ function set_active_state($container, selector, set) {
20597
20597
  if (set) addClasses($container, selector, "color_active_state");
20598
20598
  else removeClasses($container, selector, "color_active_state");
20599
20599
  }
20600
+ /************************************************************
20601
+ * Set or reset the 'pressed' state of a TOGGLE.
20602
+ *
20603
+ * Not one of the colours above: each of those names a KIND of
20604
+ * action (create, pending, history, destructive), and a toggle
20605
+ * is not an action -- it is a state the control is IN. So it
20606
+ * looks pressed instead of changing category, which also keeps
20607
+ * it out of the way of a neighbour wearing the same hue.
20608
+ ************************************************************/
20609
+ function set_pressed_state($container, selector, set) {
20610
+ if (set) addClasses($container, selector, "pressed_state");
20611
+ else removeClasses($container, selector, "pressed_state");
20612
+ }
20600
20613
  /**
20601
20614
  * Returns a smart stroke color in `rgba()` format based on:
20602
20615
  * - The given fill color (hex or rgb[a])
@@ -47493,6 +47506,19 @@ var ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
47493
47506
  <symbol id="g6-icon-toolbar-hide" viewBox="0 0 384 512">
47494
47507
  <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
47508
  </symbol>
47509
+ <!-- dashed marquee (selection mode).
47510
+ The band ITSELF, because that is what the button turns on: a
47511
+ drag on the canvas draws one and a tap picks a card. Not the
47512
+ fit-to-selection glyph next to it in the other toolbar -- that
47513
+ one is solid brackets around a solid object and means "put the
47514
+ camera on what is picked", which is the opposite end of the
47515
+ gesture. Dashes: 16 segments of 59 with gaps of 36 over a
47516
+ perimeter of 1520, so they land on the corners. -->
47517
+ <symbol id="g6-icon-select-mode" viewBox="0 0 512 512">
47518
+ <rect x="46" y="86" width="420" height="340" fill="none"
47519
+ stroke="currentColor" stroke-width="40"
47520
+ stroke-dasharray="59 36"/>
47521
+ </symbol>
47496
47522
  <!-- floppy-disk (save) -->
47497
47523
  <symbol id="g6-icon-save" viewBox="0 0 448 512">
47498
47524
  <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 +47768,7 @@ var PRIVATE_DATA$4 = {
47742
47768
  toolbar_collapsed: true,
47743
47769
  _toolbars_could_collapse: null,
47744
47770
  _on_focusout_restore: null,
47771
+ selection_mode: false,
47745
47772
  _selected_paint_ids: [],
47746
47773
  _pending_focus_topic: null,
47747
47774
  _pending_find: null,
@@ -48065,6 +48092,7 @@ function update_toolbar(gobj) {
48065
48092
  configure_toolbar_edit(gobj);
48066
48093
  if (graph_get_plugin(gobj, "toolbar-edit")) graph.updatePlugin({ key: "toolbar-edit" });
48067
48094
  update_zoom_selection_button(gobj);
48095
+ update_selection_mode_button(gobj);
48068
48096
  }
48069
48097
  /************************************************************
48070
48098
  * The floating toolbars follow the theme.
@@ -48279,7 +48307,8 @@ function configure_toolbar(gobj) {
48279
48307
  * Contains edit-mode buttons: Undo, Redo, Save
48280
48308
  ************************************************************/
48281
48309
  function configure_toolbar_edit(gobj) {
48282
- if (!gobj.priv.edit_mode || toolbars_collapsed(gobj)) {
48310
+ let priv = gobj.priv;
48311
+ if (!priv.edit_mode || toolbars_collapsed(gobj)) {
48283
48312
  graph_remove_plugin(gobj, "toolbar-edit");
48284
48313
  return;
48285
48314
  }
@@ -48298,6 +48327,12 @@ function configure_toolbar_edit(gobj) {
48298
48327
  className: "EV_CREATE_NODE_BTN color_create_state",
48299
48328
  title: (0, i18next.t)("create node")
48300
48329
  },
48330
+ {
48331
+ id: "g6-icon-select-mode",
48332
+ value: "selection-mode",
48333
+ className: "EV_TOGGLE_SELECTION_MODE" + (priv.selection_mode ? " pressed_state" : ""),
48334
+ title: (0, i18next.t)("selection mode")
48335
+ },
48301
48336
  {
48302
48337
  id: "g6-icon-save",
48303
48338
  value: "save",
@@ -48326,6 +48361,9 @@ function configure_toolbar_edit(gobj) {
48326
48361
  case "create-node":
48327
48362
  toggle_create_popover(gobj);
48328
48363
  break;
48364
+ case "selection-mode":
48365
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_SELECTION_MODE", {}, gobj);
48366
+ break;
48329
48367
  case "undo":
48330
48368
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_HISTORY_UNDO", {}, gobj);
48331
48369
  break;
@@ -48369,7 +48407,7 @@ function configure_behaviour(gobj) {
48369
48407
  type: "drag-canvas",
48370
48408
  key: "drag-canvas",
48371
48409
  enable: (event) => {
48372
- if (event.shiftKey) return false;
48410
+ if (event.shiftKey || priv.selection_mode) return false;
48373
48411
  if ("targetType" in event) return event.targetType === "canvas";
48374
48412
  return true;
48375
48413
  }
@@ -48379,7 +48417,8 @@ function configure_behaviour(gobj) {
48379
48417
  {
48380
48418
  type: BRUSH_SELECT_OWNED,
48381
48419
  key: "brush-select",
48382
- trigger: ["shift"],
48420
+ trigger: priv.selection_mode ? [] : ["shift"],
48421
+ enable: (event) => event.targetType === "canvas",
48383
48422
  enableElements: ["node"],
48384
48423
  immediately: false,
48385
48424
  onSelect: (states) => {
@@ -48395,7 +48434,10 @@ function configure_behaviour(gobj) {
48395
48434
  break;
48396
48435
  default: (0, _yuneta_gobj_js.log_error)(`operation mode unknown: ${operation_mode}`);
48397
48436
  }
48398
- if (!priv.edit_mode) deselect_node(gobj);
48437
+ if (!priv.edit_mode) {
48438
+ priv.selection_mode = false;
48439
+ deselect_node(gobj);
48440
+ }
48399
48441
  graph_write_behaviors(gobj, behaviors);
48400
48442
  }
48401
48443
  /************************************************************
@@ -49721,6 +49763,23 @@ function update_zoom_selection_button(gobj) {
49721
49763
  else disableElements($container, ".EV_ZOOM_SELECTION");
49722
49764
  }
49723
49765
  /************************************************************
49766
+ * The selection mode is a state, so it has to LOOK like one:
49767
+ * a PRESSED toggle, not a button wearing one of the palette's
49768
+ * action colours (see `pressed_state` in lib_graph.css).
49769
+ *
49770
+ * Toggled on the element rather than re-rendered with the
49771
+ * toolbar, for the same reason as the button above: a
49772
+ * re-render rebuilds the innerHTML and drops what every OTHER
49773
+ * button was told.
49774
+ ************************************************************/
49775
+ function update_selection_mode_button(gobj) {
49776
+ let priv = gobj.priv;
49777
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
49778
+ if (!$container) return;
49779
+ if (!$container.querySelector(".EV_TOGGLE_SELECTION_MODE")) return;
49780
+ set_pressed_state($container, ".EV_TOGGLE_SELECTION_MODE", priv.selection_mode);
49781
+ }
49782
+ /************************************************************
49724
49783
  * Select a SET of nodes: what a marquee and a shift-click do.
49725
49784
  *
49726
49785
  * It leaves `_selected_node_id` null even for a set of one,
@@ -52323,6 +52382,40 @@ function ac_set_operation_mode$1(gobj, event, kw, src) {
52323
52382
  return 0;
52324
52383
  }
52325
52384
  /************************************************************
52385
+ * Selection mode on/off, from the edit toolbar.
52386
+ *
52387
+ * Shift is what the selection is built on, and a telephone has
52388
+ * no Shift: the marquee and the add-to-selection click were a
52389
+ * desktop feature by construction. This makes that key a MODE
52390
+ * -- while it is on, a tap picks a card and a drag on the
52391
+ * background draws the band, so the same two gestures are
52392
+ * reachable with one finger.
52393
+ *
52394
+ * It costs panning while it is on, and deliberately: G6 binds
52395
+ * both to a plain drag, and a gesture cannot be two things.
52396
+ * That is why it is a button and not a heuristic -- the reader
52397
+ * turns it off to move the camera, and the toolbar says which
52398
+ * of the two the graph is listening for.
52399
+ ************************************************************/
52400
+ function ac_toggle_selection_mode(gobj, event, kw, src) {
52401
+ let priv = gobj.priv;
52402
+ if (!priv.edit_mode) {
52403
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: selection mode asked outside edition`);
52404
+ return 0;
52405
+ }
52406
+ priv.selection_mode = !priv.selection_mode;
52407
+ if (priv.selection_mode) {
52408
+ deselect_port(gobj);
52409
+ hide_node_icon(gobj);
52410
+ hide_node_popover(gobj);
52411
+ hide_resize_handles(gobj);
52412
+ priv._selected_node_id = null;
52413
+ }
52414
+ configure_behaviour(gobj);
52415
+ update_selection_mode_button(gobj);
52416
+ return 0;
52417
+ }
52418
+ /************************************************************
52326
52419
  * Node drag end
52327
52420
  ************************************************************/
52328
52421
  function ac_node_drag_end(gobj, event, kw, src) {
@@ -52387,7 +52480,7 @@ function ac_node_click$2(gobj, event, kw, src) {
52387
52480
  topic_name: nodedata.data.desc.topic_name,
52388
52481
  record: nodedata.data.record
52389
52482
  });
52390
- if (priv.edit_mode && kw.evt.shiftKey) toggle_in_selection(gobj, node_id);
52483
+ if (priv.edit_mode && (kw.evt.shiftKey || priv.selection_mode)) toggle_in_selection(gobj, node_id);
52391
52484
  else if (priv.edit_mode) {
52392
52485
  let containerRect = priv.$container.getBoundingClientRect();
52393
52486
  let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
@@ -52685,6 +52778,11 @@ function create_gclass$4(gclass_name) {
52685
52778
  ac_set_operation_mode$1,
52686
52779
  null
52687
52780
  ],
52781
+ [
52782
+ "EV_TOGGLE_SELECTION_MODE",
52783
+ ac_toggle_selection_mode,
52784
+ null
52785
+ ],
52688
52786
  [
52689
52787
  "EV_SAVE_GRAPH",
52690
52788
  ac_save_graph,
@@ -52759,6 +52857,7 @@ function create_gclass$4(gclass_name) {
52759
52857
  ["EV_FULLSCREEN", 0],
52760
52858
  ["EV_SET_LAYOUT", 0],
52761
52859
  ["EV_SET_OPERATION_MODE", 0],
52860
+ ["EV_TOGGLE_SELECTION_MODE", 0],
52762
52861
  ["EV_HISTORY_UNDO", 0],
52763
52862
  ["EV_HISTORY_REDO", 0],
52764
52863
  ["EV_SAVE_GRAPH", 0],
@@ -63610,6 +63709,7 @@ exports.rolling_bounds = rolling_bounds;
63610
63709
  exports.safe_locale = safe_locale;
63611
63710
  exports.set_active_state = set_active_state;
63612
63711
  exports.set_cancel_state = set_cancel_state;
63712
+ exports.set_pressed_state = set_pressed_state;
63613
63713
  exports.set_submit_state = set_submit_state;
63614
63714
  exports.setup_dev = setup_dev;
63615
63715
  exports.setup_frontend_view = setup_frontend_view;
@@ -20584,6 +20584,19 @@ function set_active_state($container, selector, set) {
20584
20584
  if (set) addClasses($container, selector, "color_active_state");
20585
20585
  else removeClasses($container, selector, "color_active_state");
20586
20586
  }
20587
+ /************************************************************
20588
+ * Set or reset the 'pressed' state of a TOGGLE.
20589
+ *
20590
+ * Not one of the colours above: each of those names a KIND of
20591
+ * action (create, pending, history, destructive), and a toggle
20592
+ * is not an action -- it is a state the control is IN. So it
20593
+ * looks pressed instead of changing category, which also keeps
20594
+ * it out of the way of a neighbour wearing the same hue.
20595
+ ************************************************************/
20596
+ function set_pressed_state($container, selector, set) {
20597
+ if (set) addClasses($container, selector, "pressed_state");
20598
+ else removeClasses($container, selector, "pressed_state");
20599
+ }
20587
20600
  /**
20588
20601
  * Returns a smart stroke color in `rgba()` format based on:
20589
20602
  * - The given fill color (hex or rgb[a])
@@ -47480,6 +47493,19 @@ var ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
47480
47493
  <symbol id="g6-icon-toolbar-hide" viewBox="0 0 384 512">
47481
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"/>
47482
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>
47483
47509
  <!-- floppy-disk (save) -->
47484
47510
  <symbol id="g6-icon-save" viewBox="0 0 448 512">
47485
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"/>
@@ -47729,6 +47755,7 @@ var PRIVATE_DATA$4 = {
47729
47755
  toolbar_collapsed: true,
47730
47756
  _toolbars_could_collapse: null,
47731
47757
  _on_focusout_restore: null,
47758
+ selection_mode: false,
47732
47759
  _selected_paint_ids: [],
47733
47760
  _pending_focus_topic: null,
47734
47761
  _pending_find: null,
@@ -48052,6 +48079,7 @@ function update_toolbar(gobj) {
48052
48079
  configure_toolbar_edit(gobj);
48053
48080
  if (graph_get_plugin(gobj, "toolbar-edit")) graph.updatePlugin({ key: "toolbar-edit" });
48054
48081
  update_zoom_selection_button(gobj);
48082
+ update_selection_mode_button(gobj);
48055
48083
  }
48056
48084
  /************************************************************
48057
48085
  * The floating toolbars follow the theme.
@@ -48266,7 +48294,8 @@ function configure_toolbar(gobj) {
48266
48294
  * Contains edit-mode buttons: Undo, Redo, Save
48267
48295
  ************************************************************/
48268
48296
  function configure_toolbar_edit(gobj) {
48269
- if (!gobj.priv.edit_mode || toolbars_collapsed(gobj)) {
48297
+ let priv = gobj.priv;
48298
+ if (!priv.edit_mode || toolbars_collapsed(gobj)) {
48270
48299
  graph_remove_plugin(gobj, "toolbar-edit");
48271
48300
  return;
48272
48301
  }
@@ -48285,6 +48314,12 @@ function configure_toolbar_edit(gobj) {
48285
48314
  className: "EV_CREATE_NODE_BTN color_create_state",
48286
48315
  title: t("create node")
48287
48316
  },
48317
+ {
48318
+ id: "g6-icon-select-mode",
48319
+ value: "selection-mode",
48320
+ className: "EV_TOGGLE_SELECTION_MODE" + (priv.selection_mode ? " pressed_state" : ""),
48321
+ title: t("selection mode")
48322
+ },
48288
48323
  {
48289
48324
  id: "g6-icon-save",
48290
48325
  value: "save",
@@ -48313,6 +48348,9 @@ function configure_toolbar_edit(gobj) {
48313
48348
  case "create-node":
48314
48349
  toggle_create_popover(gobj);
48315
48350
  break;
48351
+ case "selection-mode":
48352
+ gobj_send_event(gobj, "EV_TOGGLE_SELECTION_MODE", {}, gobj);
48353
+ break;
48316
48354
  case "undo":
48317
48355
  gobj_send_event(gobj, "EV_HISTORY_UNDO", {}, gobj);
48318
48356
  break;
@@ -48356,7 +48394,7 @@ function configure_behaviour(gobj) {
48356
48394
  type: "drag-canvas",
48357
48395
  key: "drag-canvas",
48358
48396
  enable: (event) => {
48359
- if (event.shiftKey) return false;
48397
+ if (event.shiftKey || priv.selection_mode) return false;
48360
48398
  if ("targetType" in event) return event.targetType === "canvas";
48361
48399
  return true;
48362
48400
  }
@@ -48366,7 +48404,8 @@ function configure_behaviour(gobj) {
48366
48404
  {
48367
48405
  type: BRUSH_SELECT_OWNED,
48368
48406
  key: "brush-select",
48369
- trigger: ["shift"],
48407
+ trigger: priv.selection_mode ? [] : ["shift"],
48408
+ enable: (event) => event.targetType === "canvas",
48370
48409
  enableElements: ["node"],
48371
48410
  immediately: false,
48372
48411
  onSelect: (states) => {
@@ -48382,7 +48421,10 @@ function configure_behaviour(gobj) {
48382
48421
  break;
48383
48422
  default: log_error(`operation mode unknown: ${operation_mode}`);
48384
48423
  }
48385
- if (!priv.edit_mode) deselect_node(gobj);
48424
+ if (!priv.edit_mode) {
48425
+ priv.selection_mode = false;
48426
+ deselect_node(gobj);
48427
+ }
48386
48428
  graph_write_behaviors(gobj, behaviors);
48387
48429
  }
48388
48430
  /************************************************************
@@ -49708,6 +49750,23 @@ function update_zoom_selection_button(gobj) {
49708
49750
  else disableElements($container, ".EV_ZOOM_SELECTION");
49709
49751
  }
49710
49752
  /************************************************************
49753
+ * The selection mode is a state, so it has to LOOK like one:
49754
+ * a PRESSED toggle, not a button wearing one of the palette's
49755
+ * action colours (see `pressed_state` in lib_graph.css).
49756
+ *
49757
+ * Toggled on the element rather than re-rendered with the
49758
+ * toolbar, for the same reason as the button above: a
49759
+ * re-render rebuilds the innerHTML and drops what every OTHER
49760
+ * button was told.
49761
+ ************************************************************/
49762
+ function update_selection_mode_button(gobj) {
49763
+ let priv = gobj.priv;
49764
+ let $container = gobj_read_attr(gobj, "$container");
49765
+ if (!$container) return;
49766
+ if (!$container.querySelector(".EV_TOGGLE_SELECTION_MODE")) return;
49767
+ set_pressed_state($container, ".EV_TOGGLE_SELECTION_MODE", priv.selection_mode);
49768
+ }
49769
+ /************************************************************
49711
49770
  * Select a SET of nodes: what a marquee and a shift-click do.
49712
49771
  *
49713
49772
  * It leaves `_selected_node_id` null even for a set of one,
@@ -52310,6 +52369,40 @@ function ac_set_operation_mode$1(gobj, event, kw, src) {
52310
52369
  return 0;
52311
52370
  }
52312
52371
  /************************************************************
52372
+ * Selection mode on/off, from the edit toolbar.
52373
+ *
52374
+ * Shift is what the selection is built on, and a telephone has
52375
+ * no Shift: the marquee and the add-to-selection click were a
52376
+ * desktop feature by construction. This makes that key a MODE
52377
+ * -- while it is on, a tap picks a card and a drag on the
52378
+ * background draws the band, so the same two gestures are
52379
+ * reachable with one finger.
52380
+ *
52381
+ * It costs panning while it is on, and deliberately: G6 binds
52382
+ * both to a plain drag, and a gesture cannot be two things.
52383
+ * That is why it is a button and not a heuristic -- the reader
52384
+ * turns it off to move the camera, and the toolbar says which
52385
+ * of the two the graph is listening for.
52386
+ ************************************************************/
52387
+ function ac_toggle_selection_mode(gobj, event, kw, src) {
52388
+ let priv = gobj.priv;
52389
+ if (!priv.edit_mode) {
52390
+ log_error(`${gobj_short_name(gobj)}: selection mode asked outside edition`);
52391
+ return 0;
52392
+ }
52393
+ priv.selection_mode = !priv.selection_mode;
52394
+ if (priv.selection_mode) {
52395
+ deselect_port(gobj);
52396
+ hide_node_icon(gobj);
52397
+ hide_node_popover(gobj);
52398
+ hide_resize_handles(gobj);
52399
+ priv._selected_node_id = null;
52400
+ }
52401
+ configure_behaviour(gobj);
52402
+ update_selection_mode_button(gobj);
52403
+ return 0;
52404
+ }
52405
+ /************************************************************
52313
52406
  * Node drag end
52314
52407
  ************************************************************/
52315
52408
  function ac_node_drag_end(gobj, event, kw, src) {
@@ -52374,7 +52467,7 @@ function ac_node_click$2(gobj, event, kw, src) {
52374
52467
  topic_name: nodedata.data.desc.topic_name,
52375
52468
  record: nodedata.data.record
52376
52469
  });
52377
- if (priv.edit_mode && kw.evt.shiftKey) toggle_in_selection(gobj, node_id);
52470
+ if (priv.edit_mode && (kw.evt.shiftKey || priv.selection_mode)) toggle_in_selection(gobj, node_id);
52378
52471
  else if (priv.edit_mode) {
52379
52472
  let containerRect = priv.$container.getBoundingClientRect();
52380
52473
  let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
@@ -52672,6 +52765,11 @@ function create_gclass$4(gclass_name) {
52672
52765
  ac_set_operation_mode$1,
52673
52766
  null
52674
52767
  ],
52768
+ [
52769
+ "EV_TOGGLE_SELECTION_MODE",
52770
+ ac_toggle_selection_mode,
52771
+ null
52772
+ ],
52675
52773
  [
52676
52774
  "EV_SAVE_GRAPH",
52677
52775
  ac_save_graph,
@@ -52746,6 +52844,7 @@ function create_gclass$4(gclass_name) {
52746
52844
  ["EV_FULLSCREEN", 0],
52747
52845
  ["EV_SET_LAYOUT", 0],
52748
52846
  ["EV_SET_OPERATION_MODE", 0],
52847
+ ["EV_TOGGLE_SELECTION_MODE", 0],
52749
52848
  ["EV_HISTORY_UNDO", 0],
52750
52849
  ["EV_HISTORY_REDO", 0],
52751
52850
  ["EV_SAVE_GRAPH", 0],
@@ -63539,6 +63638,6 @@ function setup_frontend_view(self) {
63539
63638
  return win;
63540
63639
  }
63541
63640
  //#endregion
63542
- export { EditControl, MarkerControl, YUI_PERIODS, YUI_PERIODS_DEFAULT, YUI_ROLLING, addClasses, apply_dev_traces, attach_clear, build_dev_panel, dev_window_was_open, disableElements, enableElements, epoch_to_local_input, epoch_to_ms, expose_view_container, fmt_epoch, getStrokeColor, infer_period, info_traffic, inject_svg_icons, is_current_period, iso_week, local_input_to_epoch, ms_to_epoch, period_bounds, period_bounds_epoch, period_label, period_name, period_shift, period_spec, period_start, refresh_clear, register_c_g6_nodes_tree, register_c_yui_form, register_c_yui_gobj_tree_js, register_c_yui_json, register_c_yui_json_graph, register_c_yui_map, register_c_yui_nav, register_c_yui_pager, register_c_yui_period, register_c_yui_schema_editor, register_c_yui_service_view, register_c_yui_shell, register_c_yui_treedb_graph, register_c_yui_treedb_schema, register_c_yui_treedb_topic_with_form, register_c_yui_treedb_topics, register_c_yui_uplot, register_c_yui_window, register_c_yui_window_manager, register_c_yui_wizard, removeChildElements, removeClasses, rolling_bounds, safe_locale, set_active_state, set_cancel_state, set_submit_state, setup_dev, setup_frontend_view, toggleClasses, yui_is_dark, yui_mount_service_view, yui_shell_close_drawer, yui_shell_close_dropdown, yui_shell_confirm_danger, yui_shell_confirm_ok, yui_shell_confirm_yesno, yui_shell_confirm_yesnocancel, yui_shell_navigate, yui_shell_open_drawer, yui_shell_overlay_dismissed, yui_shell_pop_escape, yui_shell_push_escape, yui_shell_refresh_avatars, yui_shell_register_event_handler, yui_shell_register_overlay, yui_shell_set_avatar_provider, yui_shell_set_connection_state, yui_shell_set_sub_routes, yui_shell_set_toolbar_item_badge, yui_shell_set_toolbar_item_icon, yui_shell_set_translator, yui_shell_show_error, yui_shell_show_info, yui_shell_show_modal, yui_shell_show_route_map, yui_shell_show_warning, yui_shell_toggle_drawer, yui_shell_unpark_route, yui_theme_now, yui_toolbar, yui_watch_theme };
63641
+ export { EditControl, MarkerControl, YUI_PERIODS, YUI_PERIODS_DEFAULT, YUI_ROLLING, addClasses, apply_dev_traces, attach_clear, build_dev_panel, dev_window_was_open, disableElements, enableElements, epoch_to_local_input, epoch_to_ms, expose_view_container, fmt_epoch, getStrokeColor, infer_period, info_traffic, inject_svg_icons, is_current_period, iso_week, local_input_to_epoch, ms_to_epoch, period_bounds, period_bounds_epoch, period_label, period_name, period_shift, period_spec, period_start, refresh_clear, register_c_g6_nodes_tree, register_c_yui_form, register_c_yui_gobj_tree_js, register_c_yui_json, register_c_yui_json_graph, register_c_yui_map, register_c_yui_nav, register_c_yui_pager, register_c_yui_period, register_c_yui_schema_editor, register_c_yui_service_view, register_c_yui_shell, register_c_yui_treedb_graph, register_c_yui_treedb_schema, register_c_yui_treedb_topic_with_form, register_c_yui_treedb_topics, register_c_yui_uplot, register_c_yui_window, register_c_yui_window_manager, register_c_yui_wizard, removeChildElements, removeClasses, rolling_bounds, safe_locale, set_active_state, set_cancel_state, set_pressed_state, set_submit_state, setup_dev, setup_frontend_view, toggleClasses, yui_is_dark, yui_mount_service_view, yui_shell_close_drawer, yui_shell_close_dropdown, yui_shell_confirm_danger, yui_shell_confirm_ok, yui_shell_confirm_yesno, yui_shell_confirm_yesnocancel, yui_shell_navigate, yui_shell_open_drawer, yui_shell_overlay_dismissed, yui_shell_pop_escape, yui_shell_push_escape, yui_shell_refresh_avatars, yui_shell_register_event_handler, yui_shell_register_overlay, yui_shell_set_avatar_provider, yui_shell_set_connection_state, yui_shell_set_sub_routes, yui_shell_set_toolbar_item_badge, yui_shell_set_toolbar_item_icon, yui_shell_set_translator, yui_shell_show_error, yui_shell_show_info, yui_shell_show_modal, yui_shell_show_route_map, yui_shell_show_warning, yui_shell_toggle_drawer, yui_shell_unpark_route, yui_theme_now, yui_toolbar, yui_watch_theme };
63543
63642
 
63544
63643
  //# sourceMappingURL=gobj-ui.es.js.map
package/index.js CHANGED
@@ -114,7 +114,7 @@ export {
114
114
  /*
115
115
  * Libraries and utilities
116
116
  */
117
- export { addClasses, removeClasses, toggleClasses, removeChildElements, disableElements, enableElements, set_submit_state, set_cancel_state, set_active_state, getStrokeColor } from "./src/lib_graph.js";
117
+ export { addClasses, removeClasses, toggleClasses, removeChildElements, disableElements, enableElements, set_submit_state, set_cancel_state, set_active_state, set_pressed_state, getStrokeColor } from "./src/lib_graph.js";
118
118
  export { inject_svg_icons } from "./src/lib_icons.js";
119
119
  export { EditControl, MarkerControl } from "./src/lib_maplibre.js";
120
120
  export { yui_toolbar } from "./src/yui_toolbar.js";
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.12",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -79,6 +79,7 @@ import {
79
79
  set_submit_state,
80
80
  set_cancel_state,
81
81
  set_active_state,
82
+ set_pressed_state,
82
83
  getStrokeColor,
83
84
  } from "./lib_graph.js";
84
85
 
@@ -350,6 +351,9 @@ let PRIVATE_DATA = {
350
351
  toolbar_collapsed: true, // floating toolbars folded (narrow only)
351
352
  _toolbars_could_collapse: null, // last answer, to notice it changed
352
353
  _on_focusout_restore: null, // ...and putting it back when it goes nowhere
354
+ selection_mode: false, // taps pick cards, a canvas drag is the band
355
+ // (edition only, transient: a mode nobody
356
+ // can see is a mode nobody can leave)
353
357
  _selected_paint_ids: [], // node ids whose card is PAINTED selected
354
358
  // (G6's 'selected' state is the selection
355
359
  // itself; this is what is on screen, and
@@ -1026,6 +1030,7 @@ function update_toolbar(gobj)
1026
1030
  /* A re-render rebuilds the innerHTML, so a button whose enabled
1027
1031
  * state is not part of getItems() has to be told again. */
1028
1032
  update_zoom_selection_button(gobj);
1033
+ update_selection_mode_button(gobj);
1029
1034
  }
1030
1035
 
1031
1036
  /************************************************************
@@ -1326,6 +1331,19 @@ function configure_toolbar_edit(gobj)
1326
1331
  getItems: () => {
1327
1332
  return [
1328
1333
  { id: 'g6-icon-plus', value: 'create-node', className: 'EV_CREATE_NODE_BTN color_create_state', title: t('create node') },
1334
+ /* The mode is carried in the class getItems()
1335
+ * returns, not only pushed onto the element after
1336
+ * the fact: this toolbar is rebuilt whole (a fold,
1337
+ * a language change), and the rebuilt DOM is not
1338
+ * there yet when the rebuild returns -- so a
1339
+ * button told afterwards came back OFF while the
1340
+ * graph was still listening for a selection. */
1341
+ {
1342
+ id: 'g6-icon-select-mode', value: 'selection-mode',
1343
+ className: 'EV_TOGGLE_SELECTION_MODE' +
1344
+ (priv.selection_mode? ' pressed_state' : ''),
1345
+ title: t('selection mode')
1346
+ },
1329
1347
  { id: 'g6-icon-save', value: 'save', className: 'EV_SAVE_GRAPH', title: t('save'), disabled: true },
1330
1348
  { id: 'g6-icon-undo', value: 'undo', className: 'EV_HISTORY_UNDO', title: t('undo'), disabled: true },
1331
1349
  { id: 'g6-icon-redo', value: 'redo', className: 'EV_HISTORY_REDO', title: t('redo'), disabled: true },
@@ -1336,6 +1354,9 @@ function configure_toolbar_edit(gobj)
1336
1354
  case 'create-node':
1337
1355
  toggle_create_popover(gobj);
1338
1356
  break;
1357
+ case 'selection-mode':
1358
+ gobj_send_event(gobj, "EV_TOGGLE_SELECTION_MODE", {}, gobj);
1359
+ break;
1339
1360
  case 'undo':
1340
1361
  gobj_send_event(gobj, "EV_HISTORY_UNDO", {}, gobj);
1341
1362
  break;
@@ -1429,7 +1450,7 @@ function configure_behaviour(gobj)
1429
1450
  type: "drag-canvas",
1430
1451
  key: "drag-canvas",
1431
1452
  enable: (event) => {
1432
- if(event.shiftKey) {
1453
+ if(event.shiftKey || priv.selection_mode) {
1433
1454
  return false;
1434
1455
  }
1435
1456
  if('targetType' in event) {
@@ -1458,7 +1479,18 @@ function configure_behaviour(gobj)
1458
1479
  * button lit on a graph nobody had touched. */
1459
1480
  type: BRUSH_SELECT_OWNED,
1460
1481
  key: "brush-select",
1461
- trigger: ["shift"],
1482
+ /* There is no Shift on a phone. The toolbar's
1483
+ * selection mode is that key made into a state:
1484
+ * with it on, the band needs no modifier -- and
1485
+ * panning stands aside above, because the two
1486
+ * cannot both be a plain drag. */
1487
+ trigger: priv.selection_mode ? [] : ["shift"],
1488
+ /* A band starts on the BACKGROUND. G6's default
1489
+ * (`enable: true`) does not look at what is under
1490
+ * the pointer, so a drag that begins on a card
1491
+ * drew a band and moved the card at the same
1492
+ * time. */
1493
+ enable: (event) => event.targetType === 'canvas',
1462
1494
  enableElements: ["node"],
1463
1495
  /* Left at G6's default (false): the set is read at
1464
1496
  * pointerup, not on every pointermove. Each answer
@@ -1494,6 +1526,9 @@ function configure_behaviour(gobj)
1494
1526
  break;
1495
1527
  }
1496
1528
  if(!priv.edit_mode) {
1529
+ /* The selection and the mode that picks it both belong to
1530
+ * edition, and neither survives leaving it. */
1531
+ priv.selection_mode = false;
1497
1532
  deselect_node(gobj);
1498
1533
  }
1499
1534
  graph_write_behaviors(gobj, behaviors);
@@ -3573,6 +3608,32 @@ function update_zoom_selection_button(gobj)
3573
3608
  }
3574
3609
  }
3575
3610
 
3611
+ /************************************************************
3612
+ * The selection mode is a state, so it has to LOOK like one:
3613
+ * a PRESSED toggle, not a button wearing one of the palette's
3614
+ * action colours (see `pressed_state` in lib_graph.css).
3615
+ *
3616
+ * Toggled on the element rather than re-rendered with the
3617
+ * toolbar, for the same reason as the button above: a
3618
+ * re-render rebuilds the innerHTML and drops what every OTHER
3619
+ * button was told.
3620
+ ************************************************************/
3621
+ function update_selection_mode_button(gobj)
3622
+ {
3623
+ let priv = gobj.priv;
3624
+ let $container = gobj_read_attr(gobj, "$container");
3625
+
3626
+ if(!$container) {
3627
+ return;
3628
+ }
3629
+
3630
+ if(!$container.querySelector(".EV_TOGGLE_SELECTION_MODE")) {
3631
+ return; /* no edit toolbar: not in edition, or folded away */
3632
+ }
3633
+
3634
+ set_pressed_state($container, ".EV_TOGGLE_SELECTION_MODE", priv.selection_mode);
3635
+ }
3636
+
3576
3637
  /************************************************************
3577
3638
  * Select a SET of nodes: what a marquee and a shift-click do.
3578
3639
  *
@@ -7533,6 +7594,51 @@ function ac_set_operation_mode(gobj, event, kw, src)
7533
7594
  return 0;
7534
7595
  }
7535
7596
 
7597
+ /************************************************************
7598
+ * Selection mode on/off, from the edit toolbar.
7599
+ *
7600
+ * Shift is what the selection is built on, and a telephone has
7601
+ * no Shift: the marquee and the add-to-selection click were a
7602
+ * desktop feature by construction. This makes that key a MODE
7603
+ * -- while it is on, a tap picks a card and a drag on the
7604
+ * background draws the band, so the same two gestures are
7605
+ * reachable with one finger.
7606
+ *
7607
+ * It costs panning while it is on, and deliberately: G6 binds
7608
+ * both to a plain drag, and a gesture cannot be two things.
7609
+ * That is why it is a button and not a heuristic -- the reader
7610
+ * turns it off to move the camera, and the toolbar says which
7611
+ * of the two the graph is listening for.
7612
+ ************************************************************/
7613
+ function ac_toggle_selection_mode(gobj, event, kw, src)
7614
+ {
7615
+ let priv = gobj.priv;
7616
+
7617
+ if(!priv.edit_mode) {
7618
+ log_error(`${gobj_short_name(gobj)}: selection mode asked outside edition`);
7619
+ return 0;
7620
+ }
7621
+
7622
+ priv.selection_mode = !priv.selection_mode;
7623
+
7624
+ if(priv.selection_mode) {
7625
+ /* A set has no handles, no ports and no popover: those hang
7626
+ * off `_selected_node_id`, the node OPENED for editing, and
7627
+ * in this mode a tap no longer opens one. The selection
7628
+ * itself stays -- entering the mode is not a way to lose it. */
7629
+ deselect_port(gobj);
7630
+ hide_node_icon(gobj);
7631
+ hide_node_popover(gobj);
7632
+ hide_resize_handles(gobj);
7633
+ priv._selected_node_id = null;
7634
+ }
7635
+
7636
+ configure_behaviour(gobj);
7637
+ update_selection_mode_button(gobj);
7638
+
7639
+ return 0;
7640
+ }
7641
+
7536
7642
  /************************************************************
7537
7643
  * Node drag end
7538
7644
  ************************************************************/
@@ -7632,11 +7738,13 @@ function ac_node_click(gobj, event, kw, src)
7632
7738
  record: nodedata.data.record
7633
7739
  });
7634
7740
 
7635
- if(priv.edit_mode && kw.evt.shiftKey) {
7741
+ if(priv.edit_mode && (kw.evt.shiftKey || priv.selection_mode)) {
7636
7742
  /* 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. */
7743
+ * everywhere -- and so does a plain tap while the
7744
+ * toolbar's selection mode is on, which is that key
7745
+ * for a finger. It never looks for a port: a port is
7746
+ * a one-node affordance, and this gesture is about
7747
+ * the set. */
7640
7748
  toggle_in_selection(gobj, node_id);
7641
7749
  } else if(priv.edit_mode) {
7642
7750
  // Check if click hits a port
@@ -7941,6 +8049,7 @@ function create_gclass(gclass_name)
7941
8049
  ["EV_FULLSCREEN", ac_fullscreen, null],
7942
8050
  ["EV_SET_LAYOUT", ac_set_layout, null],
7943
8051
  ["EV_SET_OPERATION_MODE", ac_set_operation_mode, null],
8052
+ ["EV_TOGGLE_SELECTION_MODE", ac_toggle_selection_mode, null],
7944
8053
  ["EV_SAVE_GRAPH", ac_save_graph, null],
7945
8054
  ["EV_HISTORY_UNDO", ac_history_undo, null],
7946
8055
  ["EV_HISTORY_REDO", ac_history_redo, null],
@@ -7991,6 +8100,7 @@ function create_gclass(gclass_name)
7991
8100
  ["EV_FULLSCREEN", 0],
7992
8101
  ["EV_SET_LAYOUT", 0],
7993
8102
  ["EV_SET_OPERATION_MODE", 0],
8103
+ ["EV_TOGGLE_SELECTION_MODE", 0],
7994
8104
  ["EV_HISTORY_UNDO", 0],
7995
8105
  ["EV_HISTORY_REDO", 0],
7996
8106
  ["EV_SAVE_GRAPH", 0],
package/src/lib_graph.css CHANGED
@@ -177,6 +177,32 @@
177
177
  background-color: var(--bulma-background, #f0f0f0);
178
178
  }
179
179
 
180
+ /*
181
+ * A TOGGLE that is on looks PRESSED, it does not take a colour from
182
+ * the palette at the top of this file: each of those names a kind of
183
+ * ACTION, and "the graph is listening for a selection" is not one --
184
+ * borrowing undo/redo's violet for it put the same colour on two
185
+ * neighbouring buttons for two different reasons.
186
+ *
187
+ * Inverted neutral, and deliberately hue-free: it cannot be mistaken
188
+ * for the hover tint declared just above (a much lighter wash of the
189
+ * same surface), it reads at a glance even when the glyph is a thin
190
+ * outline rather than a solid silhouette, and it says what it says
191
+ * without a hue -- which is the same reason the palette above is
192
+ * documented as colour-blind safe.
193
+ *
194
+ * The `:hover` half is not decoration: without it, hovering a pressed
195
+ * button would hand it back to the rule above (equal specificity,
196
+ * declared earlier, so this one wins only where both match) and the
197
+ * toggle would appear to switch off under the pointer.
198
+ */
199
+ .g6-toolbar.g6-toolbar-large .g6-toolbar-item.pressed_state,
200
+ .g6-toolbar.g6-toolbar-large .g6-toolbar-item.pressed_state:hover {
201
+ background-color: var(--bulma-text, #2e333d);
202
+ color: var(--bulma-background, #ffffff);
203
+ border-radius: 4px;
204
+ }
205
+
180
206
  /*
181
207
  * Touch: the context menu is a LIST OF COMMANDS, and G6 sizes its
182
208
  * rows with `padding: 8px 12px` -- 34px, which is a mouse row. The
package/src/lib_graph.js CHANGED
@@ -109,6 +109,24 @@ function set_active_state($container, selector, set)
109
109
  }
110
110
  }
111
111
 
112
+ /************************************************************
113
+ * Set or reset the 'pressed' state of a TOGGLE.
114
+ *
115
+ * Not one of the colours above: each of those names a KIND of
116
+ * action (create, pending, history, destructive), and a toggle
117
+ * is not an action -- it is a state the control is IN. So it
118
+ * looks pressed instead of changing category, which also keeps
119
+ * it out of the way of a neighbour wearing the same hue.
120
+ ************************************************************/
121
+ function set_pressed_state($container, selector, set)
122
+ {
123
+ if(set) {
124
+ addClasses($container, selector, "pressed_state");
125
+ } else {
126
+ removeClasses($container, selector, "pressed_state");
127
+ }
128
+ }
129
+
112
130
  /**
113
131
  * Returns a smart stroke color in `rgba()` format based on:
114
132
  * - The given fill color (hex or rgb[a])
@@ -187,5 +205,6 @@ export {
187
205
  set_submit_state,
188
206
  set_cancel_state,
189
207
  set_active_state,
208
+ set_pressed_state,
190
209
  getStrokeColor,
191
210
  };
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"/>