@yuneta/gobj-ui 7.17.0 → 7.18.1
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 +14 -2
- package/dist/gobj-ui.cjs.js +117 -1
- package/dist/gobj-ui.es.js +117 -1
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +180 -5
- package/src/lib_icons.js +9 -0
package/README.md
CHANGED
|
@@ -680,6 +680,8 @@ Two implementation notes worth keeping:
|
|
|
680
680
|
| fit | `fitView()`: the whole graph in the viewport |
|
|
681
681
|
| **`1:1`** | `zoomTo(1)`: actual size |
|
|
682
682
|
| ── | |
|
|
683
|
+
| **fit to selection** | `fit`, for the part that is selected (edition only, disabled while nothing is) |
|
|
684
|
+
| ── | |
|
|
683
685
|
| full screen | the container, not the camera |
|
|
684
686
|
|
|
685
687
|
Two of those rows are the answer to a real complaint, and the reasoning
|
|
@@ -695,6 +697,14 @@ generalises:
|
|
|
695
697
|
that offers it, because there is no glyph anybody recognises for it.
|
|
696
698
|
- **the zoom level is shown** because `1:1` is a jump to a number, and a jump
|
|
697
699
|
to a number is only meaningful next to the number you are on.
|
|
700
|
+
- **fit to selection** wears the `fit` icon with a marked object inside it,
|
|
701
|
+
drawn rather than borrowed: the two sit next to each other and are the same
|
|
702
|
+
action at two scopes, so they have to read as a family. It appears only in
|
|
703
|
+
edition — a button that can never be enabled is furniture, not a control —
|
|
704
|
+
and is disabled while nothing is selected. `fitView()` has no subset form,
|
|
705
|
+
so the bounds are measured off the elements and the zoom is clamped to the
|
|
706
|
+
graph's own `zoomRange`, the only limit that is not invented here. One card
|
|
707
|
+
filling the view is not a bug: that is what zooming to it means.
|
|
698
708
|
|
|
699
709
|
The **separators are gaps, not lines**: every item already carries a hairline
|
|
700
710
|
against its neighbour, so one more line would not group anything. Full screen
|
|
@@ -705,8 +715,9 @@ Both toolbars (this one and the edit one) **follow the theme**. They used to be
|
|
|
705
715
|
pinned to a light background in both themes, with the icon colour pinned dark
|
|
706
716
|
so it survived that — two light islands over a dark canvas.
|
|
707
717
|
|
|
708
|
-
**New keys for consumers: `actual size`, `zoom level
|
|
709
|
-
host that has not defined them shows the key on hover and
|
|
718
|
+
**New keys for consumers: `actual size`, `zoom level`, `zoom to selection`**
|
|
719
|
+
(all tooltips, so a host that has not defined them shows the key on hover and
|
|
720
|
+
nothing else breaks).
|
|
710
721
|
|
|
711
722
|
### Selecting several nodes, and moving them together
|
|
712
723
|
|
|
@@ -719,6 +730,7 @@ clicked":
|
|
|
719
730
|
| **shift + click** | adds that node to the selection, or takes it out |
|
|
720
731
|
| **shift + drag on the canvas** | rubber band: the selection becomes what it enclosed |
|
|
721
732
|
| **ctrl/cmd + A** | every node |
|
|
733
|
+
| the **fit-to-selection** button | puts the viewport on what is selected |
|
|
722
734
|
| drag any selected node | **moves the whole selection**, as one undo |
|
|
723
735
|
| **Delete** / **Backspace** | deletes the selection, after a confirmation that counts what it takes |
|
|
724
736
|
| **Esc**, or a click on the canvas | clears it |
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -53617,6 +53617,15 @@ var ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
|
|
|
53617
53617
|
<symbol id="g6-icon-fit" viewBox="0 0 448 512">
|
|
53618
53618
|
<path fill="currentColor" d="M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32S0 334.3 0 352l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z"/>
|
|
53619
53619
|
</symbol>
|
|
53620
|
+
<!-- fit to selection: the same corner brackets as the fit icon,
|
|
53621
|
+
around a marked object. The two sit side by side in the
|
|
53622
|
+
toolbar and have to read as a family, which a borrowed glyph
|
|
53623
|
+
would not. (No backticks in here: the sprite lives inside a
|
|
53624
|
+
template literal.) -->
|
|
53625
|
+
<symbol id="g6-icon-fit-selection" viewBox="0 0 448 512">
|
|
53626
|
+
<path fill="currentColor" d="M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32S0 334.3 0 352l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z"/>
|
|
53627
|
+
<rect fill="currentColor" x="144" y="176" width="160" height="160" rx="24"/>
|
|
53628
|
+
</symbol>
|
|
53620
53629
|
<!-- crosshairs (center) -->
|
|
53621
53630
|
<symbol id="g6-icon-center" viewBox="0 0 576 512">
|
|
53622
53631
|
<path fill="currentColor" d="M288-16c17.7 0 32 14.3 32 32l0 18.3c98.1 14 175.7 91.6 189.7 189.7l18.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-18.3 0c-14 98.1-91.6 175.7-189.7 189.7l0 18.3c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-18.3C157.9 463.7 80.3 386.1 66.3 288L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l18.3 0C80.3 125.9 157.9 48.3 256 34.3L256 16c0-17.7 14.3-32 32-32zM131.2 288c12.7 62.7 62.1 112.1 124.8 124.8l0-12.8c0-17.7 14.3-32 32-32s32 14.3 32 32l0 12.8c62.7-12.7 112.1-62.1 124.8-124.8L432 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l12.8 0C432.1 161.3 382.7 111.9 320 99.2l0 12.8c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-12.8C193.3 111.9 143.9 161.3 131.2 224l12.8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-12.8 0zM288 208a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"/>
|
|
@@ -53859,6 +53868,7 @@ var PRIVATE_DATA$1 = {
|
|
|
53859
53868
|
_link_saved_styles: [],
|
|
53860
53869
|
_focus_topic: null,
|
|
53861
53870
|
_focus_ids: [],
|
|
53871
|
+
_on_pointerdown_focus: null,
|
|
53862
53872
|
_selected_paint_ids: [],
|
|
53863
53873
|
_pending_focus_topic: null,
|
|
53864
53874
|
_pending_find: null,
|
|
@@ -53923,6 +53933,10 @@ function mt_destroy$1(gobj) {
|
|
|
53923
53933
|
i18next.default.off("languageChanged", priv._on_language_changed);
|
|
53924
53934
|
priv._on_language_changed = null;
|
|
53925
53935
|
}
|
|
53936
|
+
if (priv._on_pointerdown_focus) {
|
|
53937
|
+
priv.$container.removeEventListener("pointerdown", priv._on_pointerdown_focus);
|
|
53938
|
+
priv._on_pointerdown_focus = null;
|
|
53939
|
+
}
|
|
53926
53940
|
if (priv.theme_observer) {
|
|
53927
53941
|
priv.theme_observer.disconnect();
|
|
53928
53942
|
priv.theme_observer = null;
|
|
@@ -54088,6 +54102,16 @@ function configure_events(gobj) {
|
|
|
54088
54102
|
update_toolbar(gobj);
|
|
54089
54103
|
};
|
|
54090
54104
|
i18next.default.on("languageChanged", priv._on_language_changed);
|
|
54105
|
+
priv._on_pointerdown_focus = (ev) => {
|
|
54106
|
+
let target = ev.target;
|
|
54107
|
+
if (target && typeof target.closest === "function") {
|
|
54108
|
+
if (target.closest("input, textarea, select, [contenteditable]")) return;
|
|
54109
|
+
if (target.closest(".g6-confirm-popover, .g6-create-popover,.g6-node-popover, .g6-edge-popover")) return;
|
|
54110
|
+
}
|
|
54111
|
+
let $canvas = priv.$container.querySelector("canvas");
|
|
54112
|
+
if ($canvas && typeof $canvas.focus === "function") $canvas.focus({ preventScroll: true });
|
|
54113
|
+
};
|
|
54114
|
+
priv.$container.addEventListener("pointerdown", priv._on_pointerdown_focus);
|
|
54091
54115
|
graph.on("keydown", (evt) => {
|
|
54092
54116
|
let ctrl = !!(evt.ctrlKey || evt.metaKey);
|
|
54093
54117
|
if (ctrl && (evt.key === "a" || evt.key === "A")) {
|
|
@@ -54150,6 +54174,7 @@ function update_toolbar(gobj) {
|
|
|
54150
54174
|
if (graph_get_plugin(gobj, "toolbar")) graph.updatePlugin({ key: "toolbar" });
|
|
54151
54175
|
configure_toolbar_edit(gobj);
|
|
54152
54176
|
if (graph_get_plugin(gobj, "toolbar-edit")) graph.updatePlugin({ key: "toolbar-edit" });
|
|
54177
|
+
update_zoom_selection_button(gobj);
|
|
54153
54178
|
}
|
|
54154
54179
|
/************************************************************
|
|
54155
54180
|
* The floating toolbars follow the theme.
|
|
@@ -54250,6 +54275,13 @@ function configure_toolbar(gobj) {
|
|
|
54250
54275
|
title: (0, i18next.t)("actual size")
|
|
54251
54276
|
}
|
|
54252
54277
|
];
|
|
54278
|
+
if (priv.edit_mode) items.push({
|
|
54279
|
+
id: "g6-icon-fit-selection",
|
|
54280
|
+
value: "fit-selection",
|
|
54281
|
+
className: "EV_ZOOM_SELECTION",
|
|
54282
|
+
title: (0, i18next.t)("zoom to selection"),
|
|
54283
|
+
disabled: true
|
|
54284
|
+
});
|
|
54253
54285
|
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_fullscreen")) {
|
|
54254
54286
|
items.push({ separator: true });
|
|
54255
54287
|
if (priv.is_fullscreen) items.push({
|
|
@@ -54281,6 +54313,9 @@ function configure_toolbar(gobj) {
|
|
|
54281
54313
|
case "auto-fit":
|
|
54282
54314
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_AUTO_FIT", {}, gobj);
|
|
54283
54315
|
break;
|
|
54316
|
+
case "fit-selection":
|
|
54317
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_ZOOM_SELECTION", {}, gobj);
|
|
54318
|
+
break;
|
|
54284
54319
|
case "center":
|
|
54285
54320
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CENTER", {}, gobj);
|
|
54286
54321
|
break;
|
|
@@ -55086,6 +55121,52 @@ async function graph_fit_readable(gobj) {
|
|
|
55086
55121
|
}
|
|
55087
55122
|
}
|
|
55088
55123
|
/************************************************************
|
|
55124
|
+
* Put the viewport on the selection: what `fit` does, for a part.
|
|
55125
|
+
*
|
|
55126
|
+
* Computed rather than delegated because `fitView()` fits the whole
|
|
55127
|
+
* graph and there is no subset form of it. The bounds come from the
|
|
55128
|
+
* elements themselves, so a node's real card size is what is
|
|
55129
|
+
* measured, not an assumed one; the zoom is clamped to the graph's
|
|
55130
|
+
* own `zoomRange`, which is the only limit that is not invented
|
|
55131
|
+
* here. A selection of one card filling the view is not a bug --
|
|
55132
|
+
* that is what zooming to it means, and it is what every editor
|
|
55133
|
+
* that offers the action does.
|
|
55134
|
+
************************************************************/
|
|
55135
|
+
async function graph_fit_selection(gobj, ids) {
|
|
55136
|
+
let graph = gobj.priv.graph;
|
|
55137
|
+
if (!graph || !ids || !ids.length) return;
|
|
55138
|
+
let minx = Infinity, miny = Infinity, maxx = -Infinity, maxy = -Infinity;
|
|
55139
|
+
let found = 0;
|
|
55140
|
+
for (let id of ids) {
|
|
55141
|
+
let bounds;
|
|
55142
|
+
try {
|
|
55143
|
+
bounds = graph.getElementRenderBounds(id);
|
|
55144
|
+
} catch (e) {
|
|
55145
|
+
continue;
|
|
55146
|
+
}
|
|
55147
|
+
if (!bounds || !bounds.min || !bounds.max) continue;
|
|
55148
|
+
minx = Math.min(minx, bounds.min[0]);
|
|
55149
|
+
miny = Math.min(miny, bounds.min[1]);
|
|
55150
|
+
maxx = Math.max(maxx, bounds.max[0]);
|
|
55151
|
+
maxy = Math.max(maxy, bounds.max[1]);
|
|
55152
|
+
found++;
|
|
55153
|
+
}
|
|
55154
|
+
if (!found) {
|
|
55155
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the selection has no bounds to fit`);
|
|
55156
|
+
return;
|
|
55157
|
+
}
|
|
55158
|
+
let size = graph.getSize();
|
|
55159
|
+
let vw = Math.max(1, size[0] - 96);
|
|
55160
|
+
let vh = Math.max(1, size[1] - 96);
|
|
55161
|
+
let bw = Math.max(1, maxx - minx);
|
|
55162
|
+
let bh = Math.max(1, maxy - miny);
|
|
55163
|
+
let zoom = Math.min(vw / bw, vh / bh);
|
|
55164
|
+
let range = graph.getZoomRange() || [.2, 4];
|
|
55165
|
+
zoom = Math.max(range[0], Math.min(range[1], zoom));
|
|
55166
|
+
await graph.zoomTo(zoom);
|
|
55167
|
+
await graph.focusElement(ids);
|
|
55168
|
+
}
|
|
55169
|
+
/************************************************************
|
|
55089
55170
|
* Focus the graph on one topic: highlight (amber 'active' state)
|
|
55090
55171
|
* every node of that topic and centre the viewport on them. An
|
|
55091
55172
|
* empty topic clears the highlight. If the graph is not rendered
|
|
@@ -55554,6 +55635,23 @@ function paint_selection(gobj, ids) {
|
|
|
55554
55635
|
let next = ids || [];
|
|
55555
55636
|
priv._selected_paint_ids = next;
|
|
55556
55637
|
repaint_cards(gobj, /* @__PURE__ */ new Set([...prev, ...next]));
|
|
55638
|
+
update_zoom_selection_button(gobj);
|
|
55639
|
+
}
|
|
55640
|
+
/************************************************************
|
|
55641
|
+
* Zoom-to-selection can only act on a selection.
|
|
55642
|
+
*
|
|
55643
|
+
* The attribute is toggled on the element instead of re-rendering
|
|
55644
|
+
* the toolbar, which would rebuild its innerHTML -- and with it
|
|
55645
|
+
* drop the disabled state of every OTHER button -- each time a
|
|
55646
|
+
* card is ticked.
|
|
55647
|
+
************************************************************/
|
|
55648
|
+
function update_zoom_selection_button(gobj) {
|
|
55649
|
+
let priv = gobj.priv;
|
|
55650
|
+
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
55651
|
+
if (!$container) return;
|
|
55652
|
+
if (!$container.querySelector(".EV_ZOOM_SELECTION")) return;
|
|
55653
|
+
if ((priv._selected_paint_ids || []).length > 0) enableElements($container, ".EV_ZOOM_SELECTION");
|
|
55654
|
+
else disableElements($container, ".EV_ZOOM_SELECTION");
|
|
55557
55655
|
}
|
|
55558
55656
|
/************************************************************
|
|
55559
55657
|
* Select a SET of nodes: what a marquee and a shift-click do.
|
|
@@ -58058,6 +58156,18 @@ function ac_auto_fit(gobj, event, kw, src) {
|
|
|
58058
58156
|
graph_fitview(gobj);
|
|
58059
58157
|
return 0;
|
|
58060
58158
|
}
|
|
58159
|
+
/************************************************************
|
|
58160
|
+
* Zoom to what is selected.
|
|
58161
|
+
************************************************************/
|
|
58162
|
+
function ac_zoom_selection(gobj, event, kw, src) {
|
|
58163
|
+
let ids = gobj.priv._selected_paint_ids || [];
|
|
58164
|
+
if (!ids.length) {
|
|
58165
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: zoom to selection with nothing selected`);
|
|
58166
|
+
return -1;
|
|
58167
|
+
}
|
|
58168
|
+
graph_fit_selection(gobj, ids);
|
|
58169
|
+
return 0;
|
|
58170
|
+
}
|
|
58061
58171
|
function ac_focus_topic(gobj, event, kw, src) {
|
|
58062
58172
|
graph_focus_topic(gobj, kw && kw.topic);
|
|
58063
58173
|
return 0;
|
|
@@ -58129,7 +58239,7 @@ function ac_key_down(gobj, event, kw, src) {
|
|
|
58129
58239
|
let fullscreen = graph_get_plugin(gobj, "fullscreen");
|
|
58130
58240
|
if (fullscreen) {
|
|
58131
58241
|
if (key === "F" || key === "f") fullscreen.request();
|
|
58132
|
-
else if (key === "Escape") fullscreen.exit();
|
|
58242
|
+
else if (key === "Escape" && priv.is_fullscreen) fullscreen.exit();
|
|
58133
58243
|
}
|
|
58134
58244
|
}
|
|
58135
58245
|
if (!priv.edit_mode) return 0;
|
|
@@ -58431,6 +58541,11 @@ function create_gclass$1(gclass_name) {
|
|
|
58431
58541
|
ac_auto_fit,
|
|
58432
58542
|
null
|
|
58433
58543
|
],
|
|
58544
|
+
[
|
|
58545
|
+
"EV_ZOOM_SELECTION",
|
|
58546
|
+
ac_zoom_selection,
|
|
58547
|
+
null
|
|
58548
|
+
],
|
|
58434
58549
|
[
|
|
58435
58550
|
"EV_FOCUS_TOPIC",
|
|
58436
58551
|
ac_focus_topic,
|
|
@@ -58525,6 +58640,7 @@ function create_gclass$1(gclass_name) {
|
|
|
58525
58640
|
["EV_ZOOM_OUT", 0],
|
|
58526
58641
|
["EV_ZOOM_RESET", 0],
|
|
58527
58642
|
["EV_AUTO_FIT", 0],
|
|
58643
|
+
["EV_ZOOM_SELECTION", 0],
|
|
58528
58644
|
["EV_FOCUS_TOPIC", 0],
|
|
58529
58645
|
["EV_FIND_NODES", 0],
|
|
58530
58646
|
["EV_FIND_RESULT", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -53604,6 +53604,15 @@ var ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
|
|
|
53604
53604
|
<symbol id="g6-icon-fit" viewBox="0 0 448 512">
|
|
53605
53605
|
<path fill="currentColor" d="M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32S0 334.3 0 352l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z"/>
|
|
53606
53606
|
</symbol>
|
|
53607
|
+
<!-- fit to selection: the same corner brackets as the fit icon,
|
|
53608
|
+
around a marked object. The two sit side by side in the
|
|
53609
|
+
toolbar and have to read as a family, which a borrowed glyph
|
|
53610
|
+
would not. (No backticks in here: the sprite lives inside a
|
|
53611
|
+
template literal.) -->
|
|
53612
|
+
<symbol id="g6-icon-fit-selection" viewBox="0 0 448 512">
|
|
53613
|
+
<path fill="currentColor" d="M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32S0 334.3 0 352l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z"/>
|
|
53614
|
+
<rect fill="currentColor" x="144" y="176" width="160" height="160" rx="24"/>
|
|
53615
|
+
</symbol>
|
|
53607
53616
|
<!-- crosshairs (center) -->
|
|
53608
53617
|
<symbol id="g6-icon-center" viewBox="0 0 576 512">
|
|
53609
53618
|
<path fill="currentColor" d="M288-16c17.7 0 32 14.3 32 32l0 18.3c98.1 14 175.7 91.6 189.7 189.7l18.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-18.3 0c-14 98.1-91.6 175.7-189.7 189.7l0 18.3c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-18.3C157.9 463.7 80.3 386.1 66.3 288L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l18.3 0C80.3 125.9 157.9 48.3 256 34.3L256 16c0-17.7 14.3-32 32-32zM131.2 288c12.7 62.7 62.1 112.1 124.8 124.8l0-12.8c0-17.7 14.3-32 32-32s32 14.3 32 32l0 12.8c62.7-12.7 112.1-62.1 124.8-124.8L432 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l12.8 0C432.1 161.3 382.7 111.9 320 99.2l0 12.8c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-12.8C193.3 111.9 143.9 161.3 131.2 224l12.8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-12.8 0zM288 208a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"/>
|
|
@@ -53846,6 +53855,7 @@ var PRIVATE_DATA$1 = {
|
|
|
53846
53855
|
_link_saved_styles: [],
|
|
53847
53856
|
_focus_topic: null,
|
|
53848
53857
|
_focus_ids: [],
|
|
53858
|
+
_on_pointerdown_focus: null,
|
|
53849
53859
|
_selected_paint_ids: [],
|
|
53850
53860
|
_pending_focus_topic: null,
|
|
53851
53861
|
_pending_find: null,
|
|
@@ -53910,6 +53920,10 @@ function mt_destroy$1(gobj) {
|
|
|
53910
53920
|
i18next.off("languageChanged", priv._on_language_changed);
|
|
53911
53921
|
priv._on_language_changed = null;
|
|
53912
53922
|
}
|
|
53923
|
+
if (priv._on_pointerdown_focus) {
|
|
53924
|
+
priv.$container.removeEventListener("pointerdown", priv._on_pointerdown_focus);
|
|
53925
|
+
priv._on_pointerdown_focus = null;
|
|
53926
|
+
}
|
|
53913
53927
|
if (priv.theme_observer) {
|
|
53914
53928
|
priv.theme_observer.disconnect();
|
|
53915
53929
|
priv.theme_observer = null;
|
|
@@ -54075,6 +54089,16 @@ function configure_events(gobj) {
|
|
|
54075
54089
|
update_toolbar(gobj);
|
|
54076
54090
|
};
|
|
54077
54091
|
i18next.on("languageChanged", priv._on_language_changed);
|
|
54092
|
+
priv._on_pointerdown_focus = (ev) => {
|
|
54093
|
+
let target = ev.target;
|
|
54094
|
+
if (target && typeof target.closest === "function") {
|
|
54095
|
+
if (target.closest("input, textarea, select, [contenteditable]")) return;
|
|
54096
|
+
if (target.closest(".g6-confirm-popover, .g6-create-popover,.g6-node-popover, .g6-edge-popover")) return;
|
|
54097
|
+
}
|
|
54098
|
+
let $canvas = priv.$container.querySelector("canvas");
|
|
54099
|
+
if ($canvas && typeof $canvas.focus === "function") $canvas.focus({ preventScroll: true });
|
|
54100
|
+
};
|
|
54101
|
+
priv.$container.addEventListener("pointerdown", priv._on_pointerdown_focus);
|
|
54078
54102
|
graph.on("keydown", (evt) => {
|
|
54079
54103
|
let ctrl = !!(evt.ctrlKey || evt.metaKey);
|
|
54080
54104
|
if (ctrl && (evt.key === "a" || evt.key === "A")) {
|
|
@@ -54137,6 +54161,7 @@ function update_toolbar(gobj) {
|
|
|
54137
54161
|
if (graph_get_plugin(gobj, "toolbar")) graph.updatePlugin({ key: "toolbar" });
|
|
54138
54162
|
configure_toolbar_edit(gobj);
|
|
54139
54163
|
if (graph_get_plugin(gobj, "toolbar-edit")) graph.updatePlugin({ key: "toolbar-edit" });
|
|
54164
|
+
update_zoom_selection_button(gobj);
|
|
54140
54165
|
}
|
|
54141
54166
|
/************************************************************
|
|
54142
54167
|
* The floating toolbars follow the theme.
|
|
@@ -54237,6 +54262,13 @@ function configure_toolbar(gobj) {
|
|
|
54237
54262
|
title: t("actual size")
|
|
54238
54263
|
}
|
|
54239
54264
|
];
|
|
54265
|
+
if (priv.edit_mode) items.push({
|
|
54266
|
+
id: "g6-icon-fit-selection",
|
|
54267
|
+
value: "fit-selection",
|
|
54268
|
+
className: "EV_ZOOM_SELECTION",
|
|
54269
|
+
title: t("zoom to selection"),
|
|
54270
|
+
disabled: true
|
|
54271
|
+
});
|
|
54240
54272
|
if (gobj_read_bool_attr(gobj, "with_fullscreen")) {
|
|
54241
54273
|
items.push({ separator: true });
|
|
54242
54274
|
if (priv.is_fullscreen) items.push({
|
|
@@ -54268,6 +54300,9 @@ function configure_toolbar(gobj) {
|
|
|
54268
54300
|
case "auto-fit":
|
|
54269
54301
|
gobj_send_event(gobj, "EV_AUTO_FIT", {}, gobj);
|
|
54270
54302
|
break;
|
|
54303
|
+
case "fit-selection":
|
|
54304
|
+
gobj_send_event(gobj, "EV_ZOOM_SELECTION", {}, gobj);
|
|
54305
|
+
break;
|
|
54271
54306
|
case "center":
|
|
54272
54307
|
gobj_send_event(gobj, "EV_CENTER", {}, gobj);
|
|
54273
54308
|
break;
|
|
@@ -55073,6 +55108,52 @@ async function graph_fit_readable(gobj) {
|
|
|
55073
55108
|
}
|
|
55074
55109
|
}
|
|
55075
55110
|
/************************************************************
|
|
55111
|
+
* Put the viewport on the selection: what `fit` does, for a part.
|
|
55112
|
+
*
|
|
55113
|
+
* Computed rather than delegated because `fitView()` fits the whole
|
|
55114
|
+
* graph and there is no subset form of it. The bounds come from the
|
|
55115
|
+
* elements themselves, so a node's real card size is what is
|
|
55116
|
+
* measured, not an assumed one; the zoom is clamped to the graph's
|
|
55117
|
+
* own `zoomRange`, which is the only limit that is not invented
|
|
55118
|
+
* here. A selection of one card filling the view is not a bug --
|
|
55119
|
+
* that is what zooming to it means, and it is what every editor
|
|
55120
|
+
* that offers the action does.
|
|
55121
|
+
************************************************************/
|
|
55122
|
+
async function graph_fit_selection(gobj, ids) {
|
|
55123
|
+
let graph = gobj.priv.graph;
|
|
55124
|
+
if (!graph || !ids || !ids.length) return;
|
|
55125
|
+
let minx = Infinity, miny = Infinity, maxx = -Infinity, maxy = -Infinity;
|
|
55126
|
+
let found = 0;
|
|
55127
|
+
for (let id of ids) {
|
|
55128
|
+
let bounds;
|
|
55129
|
+
try {
|
|
55130
|
+
bounds = graph.getElementRenderBounds(id);
|
|
55131
|
+
} catch (e) {
|
|
55132
|
+
continue;
|
|
55133
|
+
}
|
|
55134
|
+
if (!bounds || !bounds.min || !bounds.max) continue;
|
|
55135
|
+
minx = Math.min(minx, bounds.min[0]);
|
|
55136
|
+
miny = Math.min(miny, bounds.min[1]);
|
|
55137
|
+
maxx = Math.max(maxx, bounds.max[0]);
|
|
55138
|
+
maxy = Math.max(maxy, bounds.max[1]);
|
|
55139
|
+
found++;
|
|
55140
|
+
}
|
|
55141
|
+
if (!found) {
|
|
55142
|
+
log_error(`${gobj_short_name(gobj)}: the selection has no bounds to fit`);
|
|
55143
|
+
return;
|
|
55144
|
+
}
|
|
55145
|
+
let size = graph.getSize();
|
|
55146
|
+
let vw = Math.max(1, size[0] - 96);
|
|
55147
|
+
let vh = Math.max(1, size[1] - 96);
|
|
55148
|
+
let bw = Math.max(1, maxx - minx);
|
|
55149
|
+
let bh = Math.max(1, maxy - miny);
|
|
55150
|
+
let zoom = Math.min(vw / bw, vh / bh);
|
|
55151
|
+
let range = graph.getZoomRange() || [.2, 4];
|
|
55152
|
+
zoom = Math.max(range[0], Math.min(range[1], zoom));
|
|
55153
|
+
await graph.zoomTo(zoom);
|
|
55154
|
+
await graph.focusElement(ids);
|
|
55155
|
+
}
|
|
55156
|
+
/************************************************************
|
|
55076
55157
|
* Focus the graph on one topic: highlight (amber 'active' state)
|
|
55077
55158
|
* every node of that topic and centre the viewport on them. An
|
|
55078
55159
|
* empty topic clears the highlight. If the graph is not rendered
|
|
@@ -55541,6 +55622,23 @@ function paint_selection(gobj, ids) {
|
|
|
55541
55622
|
let next = ids || [];
|
|
55542
55623
|
priv._selected_paint_ids = next;
|
|
55543
55624
|
repaint_cards(gobj, /* @__PURE__ */ new Set([...prev, ...next]));
|
|
55625
|
+
update_zoom_selection_button(gobj);
|
|
55626
|
+
}
|
|
55627
|
+
/************************************************************
|
|
55628
|
+
* Zoom-to-selection can only act on a selection.
|
|
55629
|
+
*
|
|
55630
|
+
* The attribute is toggled on the element instead of re-rendering
|
|
55631
|
+
* the toolbar, which would rebuild its innerHTML -- and with it
|
|
55632
|
+
* drop the disabled state of every OTHER button -- each time a
|
|
55633
|
+
* card is ticked.
|
|
55634
|
+
************************************************************/
|
|
55635
|
+
function update_zoom_selection_button(gobj) {
|
|
55636
|
+
let priv = gobj.priv;
|
|
55637
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
55638
|
+
if (!$container) return;
|
|
55639
|
+
if (!$container.querySelector(".EV_ZOOM_SELECTION")) return;
|
|
55640
|
+
if ((priv._selected_paint_ids || []).length > 0) enableElements($container, ".EV_ZOOM_SELECTION");
|
|
55641
|
+
else disableElements($container, ".EV_ZOOM_SELECTION");
|
|
55544
55642
|
}
|
|
55545
55643
|
/************************************************************
|
|
55546
55644
|
* Select a SET of nodes: what a marquee and a shift-click do.
|
|
@@ -58045,6 +58143,18 @@ function ac_auto_fit(gobj, event, kw, src) {
|
|
|
58045
58143
|
graph_fitview(gobj);
|
|
58046
58144
|
return 0;
|
|
58047
58145
|
}
|
|
58146
|
+
/************************************************************
|
|
58147
|
+
* Zoom to what is selected.
|
|
58148
|
+
************************************************************/
|
|
58149
|
+
function ac_zoom_selection(gobj, event, kw, src) {
|
|
58150
|
+
let ids = gobj.priv._selected_paint_ids || [];
|
|
58151
|
+
if (!ids.length) {
|
|
58152
|
+
log_error(`${gobj_short_name(gobj)}: zoom to selection with nothing selected`);
|
|
58153
|
+
return -1;
|
|
58154
|
+
}
|
|
58155
|
+
graph_fit_selection(gobj, ids);
|
|
58156
|
+
return 0;
|
|
58157
|
+
}
|
|
58048
58158
|
function ac_focus_topic(gobj, event, kw, src) {
|
|
58049
58159
|
graph_focus_topic(gobj, kw && kw.topic);
|
|
58050
58160
|
return 0;
|
|
@@ -58116,7 +58226,7 @@ function ac_key_down(gobj, event, kw, src) {
|
|
|
58116
58226
|
let fullscreen = graph_get_plugin(gobj, "fullscreen");
|
|
58117
58227
|
if (fullscreen) {
|
|
58118
58228
|
if (key === "F" || key === "f") fullscreen.request();
|
|
58119
|
-
else if (key === "Escape") fullscreen.exit();
|
|
58229
|
+
else if (key === "Escape" && priv.is_fullscreen) fullscreen.exit();
|
|
58120
58230
|
}
|
|
58121
58231
|
}
|
|
58122
58232
|
if (!priv.edit_mode) return 0;
|
|
@@ -58418,6 +58528,11 @@ function create_gclass$1(gclass_name) {
|
|
|
58418
58528
|
ac_auto_fit,
|
|
58419
58529
|
null
|
|
58420
58530
|
],
|
|
58531
|
+
[
|
|
58532
|
+
"EV_ZOOM_SELECTION",
|
|
58533
|
+
ac_zoom_selection,
|
|
58534
|
+
null
|
|
58535
|
+
],
|
|
58421
58536
|
[
|
|
58422
58537
|
"EV_FOCUS_TOPIC",
|
|
58423
58538
|
ac_focus_topic,
|
|
@@ -58512,6 +58627,7 @@ function create_gclass$1(gclass_name) {
|
|
|
58512
58627
|
["EV_ZOOM_OUT", 0],
|
|
58513
58628
|
["EV_ZOOM_RESET", 0],
|
|
58514
58629
|
["EV_AUTO_FIT", 0],
|
|
58630
|
+
["EV_ZOOM_SELECTION", 0],
|
|
58515
58631
|
["EV_FOCUS_TOPIC", 0],
|
|
58516
58632
|
["EV_FIND_NODES", 0],
|
|
58517
58633
|
["EV_FIND_RESULT", event_flag_t.EVF_OUTPUT_EVENT],
|
package/package.json
CHANGED
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -333,6 +333,7 @@ let PRIVATE_DATA = {
|
|
|
333
333
|
_link_saved_styles: [], // saved port styles to restore on cancel
|
|
334
334
|
_focus_topic: null, // topic currently focused (EV_FOCUS_TOPIC)
|
|
335
335
|
_focus_ids: [], // node ids carrying the focus 'active' state
|
|
336
|
+
_on_pointerdown_focus: null, // listener keeping the keyboard on the canvas
|
|
336
337
|
_selected_paint_ids: [], // node ids whose card is PAINTED selected
|
|
337
338
|
// (G6's 'selected' state is the selection
|
|
338
339
|
// itself; this is what is on screen, and
|
|
@@ -459,6 +460,13 @@ function mt_destroy(gobj)
|
|
|
459
460
|
priv._on_language_changed = null;
|
|
460
461
|
}
|
|
461
462
|
|
|
463
|
+
if(priv._on_pointerdown_focus) {
|
|
464
|
+
priv.$container.removeEventListener(
|
|
465
|
+
"pointerdown", priv._on_pointerdown_focus
|
|
466
|
+
);
|
|
467
|
+
priv._on_pointerdown_focus = null;
|
|
468
|
+
}
|
|
469
|
+
|
|
462
470
|
if(priv.theme_observer) {
|
|
463
471
|
priv.theme_observer.disconnect();
|
|
464
472
|
priv.theme_observer = null;
|
|
@@ -787,6 +795,36 @@ function configure_events(gobj)
|
|
|
787
795
|
};
|
|
788
796
|
i18next.on('languageChanged', priv._on_language_changed);
|
|
789
797
|
|
|
798
|
+
/* Clicking a CARD is clicking a DOM element inside the container,
|
|
799
|
+
* and that takes the keyboard away from G6's canvas -- the only
|
|
800
|
+
* element that receives keydown. Selecting two nodes by clicking
|
|
801
|
+
* them and then pressing Escape did nothing, which is the most
|
|
802
|
+
* ordinary way there is to reach for it. The focus goes back on
|
|
803
|
+
* every press inside the graph.
|
|
804
|
+
*
|
|
805
|
+
* Except on what is there to be typed into: the popovers this
|
|
806
|
+
* gclass appends to its own container carry inputs, and stealing
|
|
807
|
+
* their focus on pointerdown would make them impossible to fill
|
|
808
|
+
* in.
|
|
809
|
+
*/
|
|
810
|
+
priv._on_pointerdown_focus = (ev) => {
|
|
811
|
+
let target = ev.target;
|
|
812
|
+
if(target && typeof target.closest === "function") {
|
|
813
|
+
if(target.closest("input, textarea, select, [contenteditable]")) {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
if(target.closest(".g6-confirm-popover, .g6-create-popover," +
|
|
817
|
+
".g6-node-popover, .g6-edge-popover")) {
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
let $canvas = priv.$container.querySelector("canvas");
|
|
822
|
+
if($canvas && typeof $canvas.focus === "function") {
|
|
823
|
+
$canvas.focus({preventScroll: true});
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
priv.$container.addEventListener("pointerdown", priv._on_pointerdown_focus);
|
|
827
|
+
|
|
790
828
|
/* The canvas carries a `tabIndex` of its own, so a keydown reaches
|
|
791
829
|
* us only while the GRAPH has focus. That is what keeps Ctrl+A in
|
|
792
830
|
* the find box a text selection and not a selection of every node:
|
|
@@ -912,6 +950,10 @@ function update_toolbar(gobj)
|
|
|
912
950
|
key: 'toolbar-edit',
|
|
913
951
|
});
|
|
914
952
|
}
|
|
953
|
+
|
|
954
|
+
/* A re-render rebuilds the innerHTML, so a button whose enabled
|
|
955
|
+
* state is not part of getItems() has to be told again. */
|
|
956
|
+
update_zoom_selection_button(gobj);
|
|
915
957
|
}
|
|
916
958
|
|
|
917
959
|
/************************************************************
|
|
@@ -1031,6 +1073,20 @@ function configure_toolbar(gobj)
|
|
|
1031
1073
|
{ text: '1:1', value: 'reset', className: 'EV_ZOOM_RESET', title: t('actual size') },
|
|
1032
1074
|
];
|
|
1033
1075
|
|
|
1076
|
+
/* Only in edition, because that is where a selection
|
|
1077
|
+
* can exist: a button that is permanently disabled is
|
|
1078
|
+
* not a control, it is furniture. Disabled while the
|
|
1079
|
+
* selection is empty -- `paint_selection` is the one
|
|
1080
|
+
* funnel every change goes through, so it is the one
|
|
1081
|
+
* place that has to say so. */
|
|
1082
|
+
if(priv.edit_mode) {
|
|
1083
|
+
items.push({
|
|
1084
|
+
id: 'g6-icon-fit-selection', value: 'fit-selection',
|
|
1085
|
+
className: 'EV_ZOOM_SELECTION',
|
|
1086
|
+
title: t('zoom to selection'), disabled: true
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1034
1090
|
if(gobj_read_bool_attr(gobj, "with_fullscreen")) {
|
|
1035
1091
|
/* Full screen is a WINDOW control, not a camera one:
|
|
1036
1092
|
* it goes in its own group. */
|
|
@@ -1062,6 +1118,9 @@ function configure_toolbar(gobj)
|
|
|
1062
1118
|
case 'auto-fit':
|
|
1063
1119
|
gobj_send_event(gobj, "EV_AUTO_FIT", {}, gobj);
|
|
1064
1120
|
break;
|
|
1121
|
+
case 'fit-selection':
|
|
1122
|
+
gobj_send_event(gobj, "EV_ZOOM_SELECTION", {}, gobj);
|
|
1123
|
+
break;
|
|
1065
1124
|
case 'center':
|
|
1066
1125
|
gobj_send_event(gobj, "EV_CENTER", {}, gobj);
|
|
1067
1126
|
break;
|
|
@@ -2290,6 +2349,65 @@ async function graph_fit_readable(gobj)
|
|
|
2290
2349
|
}
|
|
2291
2350
|
}
|
|
2292
2351
|
|
|
2352
|
+
/************************************************************
|
|
2353
|
+
* Put the viewport on the selection: what `fit` does, for a part.
|
|
2354
|
+
*
|
|
2355
|
+
* Computed rather than delegated because `fitView()` fits the whole
|
|
2356
|
+
* graph and there is no subset form of it. The bounds come from the
|
|
2357
|
+
* elements themselves, so a node's real card size is what is
|
|
2358
|
+
* measured, not an assumed one; the zoom is clamped to the graph's
|
|
2359
|
+
* own `zoomRange`, which is the only limit that is not invented
|
|
2360
|
+
* here. A selection of one card filling the view is not a bug --
|
|
2361
|
+
* that is what zooming to it means, and it is what every editor
|
|
2362
|
+
* that offers the action does.
|
|
2363
|
+
************************************************************/
|
|
2364
|
+
async function graph_fit_selection(gobj, ids)
|
|
2365
|
+
{
|
|
2366
|
+
let priv = gobj.priv;
|
|
2367
|
+
let graph = priv.graph;
|
|
2368
|
+
|
|
2369
|
+
if(!graph || !ids || !ids.length) {
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
let minx = Infinity, miny = Infinity, maxx = -Infinity, maxy = -Infinity;
|
|
2374
|
+
let found = 0;
|
|
2375
|
+
for(let id of ids) {
|
|
2376
|
+
let bounds;
|
|
2377
|
+
try {
|
|
2378
|
+
bounds = graph.getElementRenderBounds(id);
|
|
2379
|
+
} catch(e) {
|
|
2380
|
+
continue; /* gone since it was selected */
|
|
2381
|
+
}
|
|
2382
|
+
if(!bounds || !bounds.min || !bounds.max) {
|
|
2383
|
+
continue;
|
|
2384
|
+
}
|
|
2385
|
+
minx = Math.min(minx, bounds.min[0]);
|
|
2386
|
+
miny = Math.min(miny, bounds.min[1]);
|
|
2387
|
+
maxx = Math.max(maxx, bounds.max[0]);
|
|
2388
|
+
maxy = Math.max(maxy, bounds.max[1]);
|
|
2389
|
+
found++;
|
|
2390
|
+
}
|
|
2391
|
+
if(!found) {
|
|
2392
|
+
log_error(`${gobj_short_name(gobj)}: the selection has no bounds to fit`);
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
const MARGIN = 48;
|
|
2397
|
+
let size = graph.getSize();
|
|
2398
|
+
let vw = Math.max(1, size[0] - MARGIN * 2);
|
|
2399
|
+
let vh = Math.max(1, size[1] - MARGIN * 2);
|
|
2400
|
+
let bw = Math.max(1, maxx - minx);
|
|
2401
|
+
let bh = Math.max(1, maxy - miny);
|
|
2402
|
+
|
|
2403
|
+
let zoom = Math.min(vw / bw, vh / bh);
|
|
2404
|
+
let range = graph.getZoomRange() || [0.2, 4];
|
|
2405
|
+
zoom = Math.max(range[0], Math.min(range[1], zoom));
|
|
2406
|
+
|
|
2407
|
+
await graph.zoomTo(zoom);
|
|
2408
|
+
await graph.focusElement(ids);
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2293
2411
|
/************************************************************
|
|
2294
2412
|
* Focus the graph on one topic: highlight (amber 'active' state)
|
|
2295
2413
|
* every node of that topic and centre the viewport on them. An
|
|
@@ -3049,6 +3167,36 @@ function paint_selection(gobj, ids)
|
|
|
3049
3167
|
priv._selected_paint_ids = next;
|
|
3050
3168
|
|
|
3051
3169
|
repaint_cards(gobj, new Set([...prev, ...next]));
|
|
3170
|
+
update_zoom_selection_button(gobj);
|
|
3171
|
+
}
|
|
3172
|
+
|
|
3173
|
+
/************************************************************
|
|
3174
|
+
* Zoom-to-selection can only act on a selection.
|
|
3175
|
+
*
|
|
3176
|
+
* The attribute is toggled on the element instead of re-rendering
|
|
3177
|
+
* the toolbar, which would rebuild its innerHTML -- and with it
|
|
3178
|
+
* drop the disabled state of every OTHER button -- each time a
|
|
3179
|
+
* card is ticked.
|
|
3180
|
+
************************************************************/
|
|
3181
|
+
function update_zoom_selection_button(gobj)
|
|
3182
|
+
{
|
|
3183
|
+
let priv = gobj.priv;
|
|
3184
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
3185
|
+
|
|
3186
|
+
if(!$container) {
|
|
3187
|
+
return;
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
let $btn = $container.querySelector(".EV_ZOOM_SELECTION");
|
|
3191
|
+
if(!$btn) {
|
|
3192
|
+
return; /* no toolbar, or not in edition: nothing to enable */
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
if((priv._selected_paint_ids || []).length > 0) {
|
|
3196
|
+
enableElements($container, ".EV_ZOOM_SELECTION");
|
|
3197
|
+
} else {
|
|
3198
|
+
disableElements($container, ".EV_ZOOM_SELECTION");
|
|
3199
|
+
}
|
|
3052
3200
|
}
|
|
3053
3201
|
|
|
3054
3202
|
/************************************************************
|
|
@@ -6774,6 +6922,25 @@ function ac_auto_fit(gobj, event, kw, src)
|
|
|
6774
6922
|
return 0;
|
|
6775
6923
|
}
|
|
6776
6924
|
|
|
6925
|
+
/************************************************************
|
|
6926
|
+
* Zoom to what is selected.
|
|
6927
|
+
************************************************************/
|
|
6928
|
+
function ac_zoom_selection(gobj, event, kw, src)
|
|
6929
|
+
{
|
|
6930
|
+
let ids = gobj.priv._selected_paint_ids || [];
|
|
6931
|
+
|
|
6932
|
+
if(!ids.length) {
|
|
6933
|
+
/* The button is disabled without a selection, so getting here
|
|
6934
|
+
* means the event came from somewhere else. */
|
|
6935
|
+
log_error(`${gobj_short_name(gobj)}: zoom to selection with nothing selected`);
|
|
6936
|
+
return -1;
|
|
6937
|
+
}
|
|
6938
|
+
|
|
6939
|
+
graph_fit_selection(gobj, ids);
|
|
6940
|
+
|
|
6941
|
+
return 0;
|
|
6942
|
+
}
|
|
6943
|
+
|
|
6777
6944
|
function ac_focus_topic(gobj, event, kw, src)
|
|
6778
6945
|
{
|
|
6779
6946
|
graph_focus_topic(gobj, kw && kw.topic);
|
|
@@ -6869,11 +7036,17 @@ function ac_key_down(gobj, event, kw, src)
|
|
|
6869
7036
|
if(fullscreen) {
|
|
6870
7037
|
if(key === "F" || key === "f") {
|
|
6871
7038
|
fullscreen.request();
|
|
6872
|
-
} else if(key === "Escape") {
|
|
6873
|
-
/*
|
|
6874
|
-
*
|
|
6875
|
-
*
|
|
6876
|
-
*
|
|
7039
|
+
} else if(key === "Escape" && priv.is_fullscreen) {
|
|
7040
|
+
/* Only while actually IN full screen. Asking to leave
|
|
7041
|
+
* a full screen nobody entered throws, and since this
|
|
7042
|
+
* is now an action rather than a listener, that throw
|
|
7043
|
+
* takes the rest of the key with it -- which is how
|
|
7044
|
+
* Escape stopped clearing the selection.
|
|
7045
|
+
*
|
|
7046
|
+
* Escape does both when both apply, and that is right:
|
|
7047
|
+
* the browser leaves full screen on Escape whatever we
|
|
7048
|
+
* do, so refusing to clear as well would only make the
|
|
7049
|
+
* key do less than it appears to. */
|
|
6877
7050
|
fullscreen.exit();
|
|
6878
7051
|
}
|
|
6879
7052
|
}
|
|
@@ -7232,6 +7405,7 @@ function create_gclass(gclass_name)
|
|
|
7232
7405
|
["EV_ZOOM_OUT", ac_zoom_out, null],
|
|
7233
7406
|
["EV_ZOOM_RESET", ac_zoom_reset, null],
|
|
7234
7407
|
["EV_AUTO_FIT", ac_auto_fit, null],
|
|
7408
|
+
["EV_ZOOM_SELECTION", ac_zoom_selection, null],
|
|
7235
7409
|
["EV_FOCUS_TOPIC", ac_focus_topic, null],
|
|
7236
7410
|
["EV_FIND_NODES", ac_find_nodes, null],
|
|
7237
7411
|
["EV_CENTER", ac_center, null],
|
|
@@ -7278,6 +7452,7 @@ function create_gclass(gclass_name)
|
|
|
7278
7452
|
["EV_ZOOM_OUT", 0],
|
|
7279
7453
|
["EV_ZOOM_RESET", 0],
|
|
7280
7454
|
["EV_AUTO_FIT", 0],
|
|
7455
|
+
["EV_ZOOM_SELECTION", 0],
|
|
7281
7456
|
["EV_FOCUS_TOPIC", 0],
|
|
7282
7457
|
["EV_FIND_NODES", 0],
|
|
7283
7458
|
["EV_FIND_RESULT", event_flag_t.EVF_OUTPUT_EVENT],
|
package/src/lib_icons.js
CHANGED
|
@@ -52,6 +52,15 @@ const ICONS = `<svg xmlns="http://www.w3.org/2000/svg">
|
|
|
52
52
|
<symbol id="g6-icon-fit" viewBox="0 0 448 512">
|
|
53
53
|
<path fill="currentColor" d="M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32S0 334.3 0 352l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z"/>
|
|
54
54
|
</symbol>
|
|
55
|
+
<!-- fit to selection: the same corner brackets as the fit icon,
|
|
56
|
+
around a marked object. The two sit side by side in the
|
|
57
|
+
toolbar and have to read as a family, which a borrowed glyph
|
|
58
|
+
would not. (No backticks in here: the sprite lives inside a
|
|
59
|
+
template literal.) -->
|
|
60
|
+
<symbol id="g6-icon-fit-selection" viewBox="0 0 448 512">
|
|
61
|
+
<path fill="currentColor" d="M32 32C14.3 32 0 46.3 0 64l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64 64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 32zM64 352c0-17.7-14.3-32-32-32S0 334.3 0 352l0 96c0 17.7 14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0 0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96c0-17.7-14.3-32-32-32l-96 0zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32l0-96z"/>
|
|
62
|
+
<rect fill="currentColor" x="144" y="176" width="160" height="160" rx="24"/>
|
|
63
|
+
</symbol>
|
|
55
64
|
<!-- crosshairs (center) -->
|
|
56
65
|
<symbol id="g6-icon-center" viewBox="0 0 576 512">
|
|
57
66
|
<path fill="currentColor" d="M288-16c17.7 0 32 14.3 32 32l0 18.3c98.1 14 175.7 91.6 189.7 189.7l18.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-18.3 0c-14 98.1-91.6 175.7-189.7 189.7l0 18.3c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-18.3C157.9 463.7 80.3 386.1 66.3 288L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l18.3 0C80.3 125.9 157.9 48.3 256 34.3L256 16c0-17.7 14.3-32 32-32zM131.2 288c12.7 62.7 62.1 112.1 124.8 124.8l0-12.8c0-17.7 14.3-32 32-32s32 14.3 32 32l0 12.8c62.7-12.7 112.1-62.1 124.8-124.8L432 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l12.8 0C432.1 161.3 382.7 111.9 320 99.2l0 12.8c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-12.8C193.3 111.9 143.9 161.3 131.2 224l12.8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-12.8 0zM288 208a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"/>
|