@yuneta/gobj-ui 7.14.3 → 7.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/dist/gobj-ui.cjs.js +86 -22
- package/dist/gobj-ui.es.js +86 -22
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +137 -32
- package/src/lib_graph.css +70 -4
package/README.md
CHANGED
|
@@ -668,6 +668,46 @@ Two implementation notes worth keeping:
|
|
|
668
668
|
still arrive from a keyboard path or a form that outlived the flag, and an
|
|
669
669
|
ignored write is exactly the behaviour this whole change exists to stop.
|
|
670
670
|
|
|
671
|
+
### The graph's viewport toolbar
|
|
672
|
+
|
|
673
|
+
`C_G6_NODES_TREE` floats a vertical toolbar over the canvas:
|
|
674
|
+
|
|
675
|
+
| control | what it does |
|
|
676
|
+
|---|---|
|
|
677
|
+
| zoom in / zoom out | one step of scale |
|
|
678
|
+
| **the zoom level** | a readout, not a button — `85%` |
|
|
679
|
+
| ── | |
|
|
680
|
+
| fit | `fitView()`: the whole graph in the viewport |
|
|
681
|
+
| **`1:1`** | `zoomTo(1)`: actual size |
|
|
682
|
+
| ── | |
|
|
683
|
+
| full screen | the container, not the camera |
|
|
684
|
+
|
|
685
|
+
Two of those rows are the answer to a real complaint, and the reasoning
|
|
686
|
+
generalises:
|
|
687
|
+
|
|
688
|
+
- **`1:1` used to be a house**, and the house was the thing people reached for
|
|
689
|
+
when they wanted the graph back. It never gave it to them: the action is
|
|
690
|
+
`zoomTo(1)`, which sets the **scale** and leaves the camera where it was, so
|
|
691
|
+
from a corner of a large graph it answered with the same corner at 100%. A
|
|
692
|
+
house means *the initial extent* in a map and *the starting view* in an
|
|
693
|
+
editor — never a scale — and this one sat directly under `fit`, so the pair
|
|
694
|
+
read as two ways to do one thing. Actual size is **written** in every editor
|
|
695
|
+
that offers it, because there is no glyph anybody recognises for it.
|
|
696
|
+
- **the zoom level is shown** because `1:1` is a jump to a number, and a jump
|
|
697
|
+
to a number is only meaningful next to the number you are on.
|
|
698
|
+
|
|
699
|
+
The **separators are gaps, not lines**: every item already carries a hairline
|
|
700
|
+
against its neighbour, so one more line would not group anything. Full screen
|
|
701
|
+
is behind the second one because it is a window control that happens to live in
|
|
702
|
+
a camera toolbar.
|
|
703
|
+
|
|
704
|
+
Both toolbars (this one and the edit one) **follow the theme**. They used to be
|
|
705
|
+
pinned to a light background in both themes, with the icon colour pinned dark
|
|
706
|
+
so it survived that — two light islands over a dark canvas.
|
|
707
|
+
|
|
708
|
+
**New keys for consumers: `actual size`, `zoom level`** (both tooltips, so a
|
|
709
|
+
host that has not defined them shows the key on hover and nothing else breaks).
|
|
710
|
+
|
|
671
711
|
### Finding a node in the graph
|
|
672
712
|
|
|
673
713
|
`C_YUI_TREEDB_GRAPH` carries a find box in the middle of its toolbar. It
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -53702,11 +53702,22 @@ function inject_svg_icons() {
|
|
|
53702
53702
|
***********************************************************************/
|
|
53703
53703
|
/***************************************************************
|
|
53704
53704
|
* YuiToolbar — G6 Toolbar subclass that adds per-item className
|
|
53705
|
-
* and disabled support.
|
|
53705
|
+
* and disabled support, plus three item kinds G6 does not have.
|
|
53706
53706
|
*
|
|
53707
53707
|
* Each item in getItems() may carry:
|
|
53708
53708
|
* className — CSS class added to the div (e.g. 'EV_SAVE_GRAPH')
|
|
53709
53709
|
* disabled — boolean; sets the 'disabled' attribute initially
|
|
53710
|
+
* text — render this text instead of a sprite symbol; a
|
|
53711
|
+
* label is the only readable way to say `1:1`, and
|
|
53712
|
+
* every editor that offers actual-size writes it
|
|
53713
|
+
* readout — a text item that REPORTS instead of acting (the
|
|
53714
|
+
* zoom level); it is not a button
|
|
53715
|
+
* separator — a group divider, not an item
|
|
53716
|
+
*
|
|
53717
|
+
* The base class fires onClick only for elements whose class list
|
|
53718
|
+
* contains `g6-toolbar-item`, so a separator and a readout carry
|
|
53719
|
+
* their own class and are inert by construction, with no guard in
|
|
53720
|
+
* the click handler.
|
|
53710
53721
|
*
|
|
53711
53722
|
* With className set to the event name, the lib_graph.js state
|
|
53712
53723
|
* functions (set_submit_state, disableElements, …) work on toolbar
|
|
@@ -53716,9 +53727,15 @@ function inject_svg_icons() {
|
|
|
53716
53727
|
var YuiToolbar = class extends _antv_g6.Toolbar {
|
|
53717
53728
|
async getDOMContent() {
|
|
53718
53729
|
return (await this.options.getItems()).map((item) => {
|
|
53730
|
+
if (item.separator) return `<div class="g6-toolbar-sep" aria-hidden="true"></div>`;
|
|
53719
53731
|
const extra_class = item.className ? ` ${item.className}` : "";
|
|
53732
|
+
const title = item.title ?? "";
|
|
53733
|
+
if (item.readout) return `<div class="g6-toolbar-readout${extra_class}" title="${title}" aria-label="${title}">${item.text ?? ""}</div>`;
|
|
53720
53734
|
const disabled = item.disabled ? " disabled" : "";
|
|
53721
|
-
|
|
53735
|
+
const is_text = item.text !== void 0;
|
|
53736
|
+
const kind_class = is_text ? " g6-toolbar-item-text" : "";
|
|
53737
|
+
const body = is_text ? `<span class="g6-toolbar-text">${item.text}</span>` : `<svg aria-hidden="true" focusable="false"><use xlink:href="#${item.id}"></use></svg>`;
|
|
53738
|
+
return `<div class="g6-toolbar-item${kind_class}${extra_class}" value="${item.value}" title="${title}" aria-label="${title}"${disabled}>${body}</div>`;
|
|
53722
53739
|
}).join("");
|
|
53723
53740
|
}
|
|
53724
53741
|
};
|
|
@@ -54063,6 +54080,7 @@ function configure_events(gobj) {
|
|
|
54063
54080
|
update_edge_icon_position(gobj);
|
|
54064
54081
|
update_node_icon_position(gobj);
|
|
54065
54082
|
update_link_icon_position(gobj);
|
|
54083
|
+
update_zoom_readout(gobj);
|
|
54066
54084
|
});
|
|
54067
54085
|
priv._on_language_changed = () => {
|
|
54068
54086
|
update_toolbar(gobj);
|
|
@@ -54128,6 +54146,59 @@ function update_toolbar(gobj) {
|
|
|
54128
54146
|
configure_toolbar_edit(gobj);
|
|
54129
54147
|
if (graph_get_plugin(gobj, "toolbar-edit")) graph.updatePlugin({ key: "toolbar-edit" });
|
|
54130
54148
|
}
|
|
54149
|
+
/************************************************************
|
|
54150
|
+
* The floating toolbars follow the theme.
|
|
54151
|
+
*
|
|
54152
|
+
* They used to be pinned to a light background in BOTH themes,
|
|
54153
|
+
* with the icon colour pinned dark so it survived that -- two
|
|
54154
|
+
* light islands sitting over a dark canvas. The tokens do the
|
|
54155
|
+
* flipping now; the fallbacks are the old forced-light values, so
|
|
54156
|
+
* a host without Bulma gets exactly what it had.
|
|
54157
|
+
************************************************************/
|
|
54158
|
+
function toolbar_style(extra) {
|
|
54159
|
+
return Object.assign({
|
|
54160
|
+
backgroundColor: "var(--bulma-scheme-main, #f5f5f5)",
|
|
54161
|
+
color: "var(--bulma-text-strong, #333)",
|
|
54162
|
+
padding: "8px",
|
|
54163
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
54164
|
+
borderRadius: "8px",
|
|
54165
|
+
border: "1px solid var(--bulma-border-weak, #e8e8e8)",
|
|
54166
|
+
opacity: "0.85"
|
|
54167
|
+
}, extra || {});
|
|
54168
|
+
}
|
|
54169
|
+
/************************************************************
|
|
54170
|
+
* The zoom level as a percentage, the way every editor that
|
|
54171
|
+
* shows one writes it. Empty while the graph cannot be asked --
|
|
54172
|
+
* the toolbar is built before the first draw, and a viewport
|
|
54173
|
+
* that does not exist yet has no zoom to report.
|
|
54174
|
+
************************************************************/
|
|
54175
|
+
function zoom_percent_text(gobj) {
|
|
54176
|
+
let graph = gobj.priv.graph;
|
|
54177
|
+
if (!graph || typeof graph.getZoom !== "function") return "";
|
|
54178
|
+
let zoom;
|
|
54179
|
+
try {
|
|
54180
|
+
zoom = graph.getZoom();
|
|
54181
|
+
} catch (e) {
|
|
54182
|
+
return "";
|
|
54183
|
+
}
|
|
54184
|
+
if (!zoom) return "";
|
|
54185
|
+
return `${Math.round(zoom * 100)}%`;
|
|
54186
|
+
}
|
|
54187
|
+
/************************************************************
|
|
54188
|
+
* Repaint the zoom readout in place.
|
|
54189
|
+
*
|
|
54190
|
+
* The text node is patched instead of re-rendering the plugin:
|
|
54191
|
+
* the readout follows the wheel, and updatePlugin() rebuilds the
|
|
54192
|
+
* whole toolbar's innerHTML -- which would also drop the disabled
|
|
54193
|
+
* state of the edit buttons on every notch of the wheel.
|
|
54194
|
+
************************************************************/
|
|
54195
|
+
function update_zoom_readout(gobj) {
|
|
54196
|
+
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
54197
|
+
if (!$container) return;
|
|
54198
|
+
let $readout = $container.querySelector(".G6_ZOOM_LEVEL");
|
|
54199
|
+
if (!$readout) return;
|
|
54200
|
+
$readout.textContent = zoom_percent_text(gobj);
|
|
54201
|
+
}
|
|
54131
54202
|
function configure_toolbar(gobj) {
|
|
54132
54203
|
let priv = gobj.priv;
|
|
54133
54204
|
let toolbar_position = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "toolbar_position") || "top-left";
|
|
@@ -54136,17 +54207,10 @@ function configure_toolbar(gobj) {
|
|
|
54136
54207
|
type: "yui-toolbar",
|
|
54137
54208
|
className: "g6-toolbar-large",
|
|
54138
54209
|
position: toolbar_position,
|
|
54139
|
-
style: {
|
|
54140
|
-
backgroundColor: "#f5f5f5",
|
|
54141
|
-
color: "#333",
|
|
54142
|
-
padding: "8px",
|
|
54143
|
-
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
54144
|
-
borderRadius: "8px",
|
|
54145
|
-
border: "1px solid #e8e8e8",
|
|
54146
|
-
opacity: "0.85",
|
|
54210
|
+
style: toolbar_style({
|
|
54147
54211
|
marginTop: "12px",
|
|
54148
54212
|
marginLeft: "12px"
|
|
54149
|
-
},
|
|
54213
|
+
}),
|
|
54150
54214
|
getItems: () => {
|
|
54151
54215
|
let items = [
|
|
54152
54216
|
{
|
|
@@ -54161,6 +54225,13 @@ function configure_toolbar(gobj) {
|
|
|
54161
54225
|
className: "EV_ZOOM_OUT",
|
|
54162
54226
|
title: (0, i18next.t)("zoom out")
|
|
54163
54227
|
},
|
|
54228
|
+
{
|
|
54229
|
+
readout: true,
|
|
54230
|
+
className: "G6_ZOOM_LEVEL",
|
|
54231
|
+
text: zoom_percent_text(gobj),
|
|
54232
|
+
title: (0, i18next.t)("zoom level")
|
|
54233
|
+
},
|
|
54234
|
+
{ separator: true },
|
|
54164
54235
|
{
|
|
54165
54236
|
id: "g6-icon-fit",
|
|
54166
54237
|
value: "auto-fit",
|
|
@@ -54168,13 +54239,14 @@ function configure_toolbar(gobj) {
|
|
|
54168
54239
|
title: (0, i18next.t)("auto fit")
|
|
54169
54240
|
},
|
|
54170
54241
|
{
|
|
54171
|
-
|
|
54242
|
+
text: "1:1",
|
|
54172
54243
|
value: "reset",
|
|
54173
54244
|
className: "EV_ZOOM_RESET",
|
|
54174
|
-
title: (0, i18next.t)("
|
|
54245
|
+
title: (0, i18next.t)("actual size")
|
|
54175
54246
|
}
|
|
54176
54247
|
];
|
|
54177
54248
|
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_fullscreen")) {
|
|
54249
|
+
items.push({ separator: true });
|
|
54178
54250
|
if (priv.is_fullscreen) items.push({
|
|
54179
54251
|
id: "g6-icon-fullscreen-exit",
|
|
54180
54252
|
value: "exit-fullscreen",
|
|
@@ -54230,15 +54302,7 @@ function configure_toolbar_edit(gobj) {
|
|
|
54230
54302
|
type: "yui-toolbar",
|
|
54231
54303
|
className: "g6-toolbar-large",
|
|
54232
54304
|
position: "left-top",
|
|
54233
|
-
style:
|
|
54234
|
-
backgroundColor: "#f5f5f5",
|
|
54235
|
-
color: "#333",
|
|
54236
|
-
padding: "8px",
|
|
54237
|
-
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
54238
|
-
borderRadius: "8px",
|
|
54239
|
-
border: "1px solid #e8e8e8",
|
|
54240
|
-
opacity: "0.85"
|
|
54241
|
-
},
|
|
54305
|
+
style: toolbar_style(),
|
|
54242
54306
|
getItems: () => {
|
|
54243
54307
|
return [
|
|
54244
54308
|
{
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -53689,11 +53689,22 @@ function inject_svg_icons() {
|
|
|
53689
53689
|
***********************************************************************/
|
|
53690
53690
|
/***************************************************************
|
|
53691
53691
|
* YuiToolbar — G6 Toolbar subclass that adds per-item className
|
|
53692
|
-
* and disabled support.
|
|
53692
|
+
* and disabled support, plus three item kinds G6 does not have.
|
|
53693
53693
|
*
|
|
53694
53694
|
* Each item in getItems() may carry:
|
|
53695
53695
|
* className — CSS class added to the div (e.g. 'EV_SAVE_GRAPH')
|
|
53696
53696
|
* disabled — boolean; sets the 'disabled' attribute initially
|
|
53697
|
+
* text — render this text instead of a sprite symbol; a
|
|
53698
|
+
* label is the only readable way to say `1:1`, and
|
|
53699
|
+
* every editor that offers actual-size writes it
|
|
53700
|
+
* readout — a text item that REPORTS instead of acting (the
|
|
53701
|
+
* zoom level); it is not a button
|
|
53702
|
+
* separator — a group divider, not an item
|
|
53703
|
+
*
|
|
53704
|
+
* The base class fires onClick only for elements whose class list
|
|
53705
|
+
* contains `g6-toolbar-item`, so a separator and a readout carry
|
|
53706
|
+
* their own class and are inert by construction, with no guard in
|
|
53707
|
+
* the click handler.
|
|
53697
53708
|
*
|
|
53698
53709
|
* With className set to the event name, the lib_graph.js state
|
|
53699
53710
|
* functions (set_submit_state, disableElements, …) work on toolbar
|
|
@@ -53703,9 +53714,15 @@ function inject_svg_icons() {
|
|
|
53703
53714
|
var YuiToolbar = class extends Toolbar {
|
|
53704
53715
|
async getDOMContent() {
|
|
53705
53716
|
return (await this.options.getItems()).map((item) => {
|
|
53717
|
+
if (item.separator) return `<div class="g6-toolbar-sep" aria-hidden="true"></div>`;
|
|
53706
53718
|
const extra_class = item.className ? ` ${item.className}` : "";
|
|
53719
|
+
const title = item.title ?? "";
|
|
53720
|
+
if (item.readout) return `<div class="g6-toolbar-readout${extra_class}" title="${title}" aria-label="${title}">${item.text ?? ""}</div>`;
|
|
53707
53721
|
const disabled = item.disabled ? " disabled" : "";
|
|
53708
|
-
|
|
53722
|
+
const is_text = item.text !== void 0;
|
|
53723
|
+
const kind_class = is_text ? " g6-toolbar-item-text" : "";
|
|
53724
|
+
const body = is_text ? `<span class="g6-toolbar-text">${item.text}</span>` : `<svg aria-hidden="true" focusable="false"><use xlink:href="#${item.id}"></use></svg>`;
|
|
53725
|
+
return `<div class="g6-toolbar-item${kind_class}${extra_class}" value="${item.value}" title="${title}" aria-label="${title}"${disabled}>${body}</div>`;
|
|
53709
53726
|
}).join("");
|
|
53710
53727
|
}
|
|
53711
53728
|
};
|
|
@@ -54050,6 +54067,7 @@ function configure_events(gobj) {
|
|
|
54050
54067
|
update_edge_icon_position(gobj);
|
|
54051
54068
|
update_node_icon_position(gobj);
|
|
54052
54069
|
update_link_icon_position(gobj);
|
|
54070
|
+
update_zoom_readout(gobj);
|
|
54053
54071
|
});
|
|
54054
54072
|
priv._on_language_changed = () => {
|
|
54055
54073
|
update_toolbar(gobj);
|
|
@@ -54115,6 +54133,59 @@ function update_toolbar(gobj) {
|
|
|
54115
54133
|
configure_toolbar_edit(gobj);
|
|
54116
54134
|
if (graph_get_plugin(gobj, "toolbar-edit")) graph.updatePlugin({ key: "toolbar-edit" });
|
|
54117
54135
|
}
|
|
54136
|
+
/************************************************************
|
|
54137
|
+
* The floating toolbars follow the theme.
|
|
54138
|
+
*
|
|
54139
|
+
* They used to be pinned to a light background in BOTH themes,
|
|
54140
|
+
* with the icon colour pinned dark so it survived that -- two
|
|
54141
|
+
* light islands sitting over a dark canvas. The tokens do the
|
|
54142
|
+
* flipping now; the fallbacks are the old forced-light values, so
|
|
54143
|
+
* a host without Bulma gets exactly what it had.
|
|
54144
|
+
************************************************************/
|
|
54145
|
+
function toolbar_style(extra) {
|
|
54146
|
+
return Object.assign({
|
|
54147
|
+
backgroundColor: "var(--bulma-scheme-main, #f5f5f5)",
|
|
54148
|
+
color: "var(--bulma-text-strong, #333)",
|
|
54149
|
+
padding: "8px",
|
|
54150
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
54151
|
+
borderRadius: "8px",
|
|
54152
|
+
border: "1px solid var(--bulma-border-weak, #e8e8e8)",
|
|
54153
|
+
opacity: "0.85"
|
|
54154
|
+
}, extra || {});
|
|
54155
|
+
}
|
|
54156
|
+
/************************************************************
|
|
54157
|
+
* The zoom level as a percentage, the way every editor that
|
|
54158
|
+
* shows one writes it. Empty while the graph cannot be asked --
|
|
54159
|
+
* the toolbar is built before the first draw, and a viewport
|
|
54160
|
+
* that does not exist yet has no zoom to report.
|
|
54161
|
+
************************************************************/
|
|
54162
|
+
function zoom_percent_text(gobj) {
|
|
54163
|
+
let graph = gobj.priv.graph;
|
|
54164
|
+
if (!graph || typeof graph.getZoom !== "function") return "";
|
|
54165
|
+
let zoom;
|
|
54166
|
+
try {
|
|
54167
|
+
zoom = graph.getZoom();
|
|
54168
|
+
} catch (e) {
|
|
54169
|
+
return "";
|
|
54170
|
+
}
|
|
54171
|
+
if (!zoom) return "";
|
|
54172
|
+
return `${Math.round(zoom * 100)}%`;
|
|
54173
|
+
}
|
|
54174
|
+
/************************************************************
|
|
54175
|
+
* Repaint the zoom readout in place.
|
|
54176
|
+
*
|
|
54177
|
+
* The text node is patched instead of re-rendering the plugin:
|
|
54178
|
+
* the readout follows the wheel, and updatePlugin() rebuilds the
|
|
54179
|
+
* whole toolbar's innerHTML -- which would also drop the disabled
|
|
54180
|
+
* state of the edit buttons on every notch of the wheel.
|
|
54181
|
+
************************************************************/
|
|
54182
|
+
function update_zoom_readout(gobj) {
|
|
54183
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
54184
|
+
if (!$container) return;
|
|
54185
|
+
let $readout = $container.querySelector(".G6_ZOOM_LEVEL");
|
|
54186
|
+
if (!$readout) return;
|
|
54187
|
+
$readout.textContent = zoom_percent_text(gobj);
|
|
54188
|
+
}
|
|
54118
54189
|
function configure_toolbar(gobj) {
|
|
54119
54190
|
let priv = gobj.priv;
|
|
54120
54191
|
let toolbar_position = gobj_read_str_attr(gobj, "toolbar_position") || "top-left";
|
|
@@ -54123,17 +54194,10 @@ function configure_toolbar(gobj) {
|
|
|
54123
54194
|
type: "yui-toolbar",
|
|
54124
54195
|
className: "g6-toolbar-large",
|
|
54125
54196
|
position: toolbar_position,
|
|
54126
|
-
style: {
|
|
54127
|
-
backgroundColor: "#f5f5f5",
|
|
54128
|
-
color: "#333",
|
|
54129
|
-
padding: "8px",
|
|
54130
|
-
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
54131
|
-
borderRadius: "8px",
|
|
54132
|
-
border: "1px solid #e8e8e8",
|
|
54133
|
-
opacity: "0.85",
|
|
54197
|
+
style: toolbar_style({
|
|
54134
54198
|
marginTop: "12px",
|
|
54135
54199
|
marginLeft: "12px"
|
|
54136
|
-
},
|
|
54200
|
+
}),
|
|
54137
54201
|
getItems: () => {
|
|
54138
54202
|
let items = [
|
|
54139
54203
|
{
|
|
@@ -54148,6 +54212,13 @@ function configure_toolbar(gobj) {
|
|
|
54148
54212
|
className: "EV_ZOOM_OUT",
|
|
54149
54213
|
title: t("zoom out")
|
|
54150
54214
|
},
|
|
54215
|
+
{
|
|
54216
|
+
readout: true,
|
|
54217
|
+
className: "G6_ZOOM_LEVEL",
|
|
54218
|
+
text: zoom_percent_text(gobj),
|
|
54219
|
+
title: t("zoom level")
|
|
54220
|
+
},
|
|
54221
|
+
{ separator: true },
|
|
54151
54222
|
{
|
|
54152
54223
|
id: "g6-icon-fit",
|
|
54153
54224
|
value: "auto-fit",
|
|
@@ -54155,13 +54226,14 @@ function configure_toolbar(gobj) {
|
|
|
54155
54226
|
title: t("auto fit")
|
|
54156
54227
|
},
|
|
54157
54228
|
{
|
|
54158
|
-
|
|
54229
|
+
text: "1:1",
|
|
54159
54230
|
value: "reset",
|
|
54160
54231
|
className: "EV_ZOOM_RESET",
|
|
54161
|
-
title: t("
|
|
54232
|
+
title: t("actual size")
|
|
54162
54233
|
}
|
|
54163
54234
|
];
|
|
54164
54235
|
if (gobj_read_bool_attr(gobj, "with_fullscreen")) {
|
|
54236
|
+
items.push({ separator: true });
|
|
54165
54237
|
if (priv.is_fullscreen) items.push({
|
|
54166
54238
|
id: "g6-icon-fullscreen-exit",
|
|
54167
54239
|
value: "exit-fullscreen",
|
|
@@ -54217,15 +54289,7 @@ function configure_toolbar_edit(gobj) {
|
|
|
54217
54289
|
type: "yui-toolbar",
|
|
54218
54290
|
className: "g6-toolbar-large",
|
|
54219
54291
|
position: "left-top",
|
|
54220
|
-
style:
|
|
54221
|
-
backgroundColor: "#f5f5f5",
|
|
54222
|
-
color: "#333",
|
|
54223
|
-
padding: "8px",
|
|
54224
|
-
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
54225
|
-
borderRadius: "8px",
|
|
54226
|
-
border: "1px solid #e8e8e8",
|
|
54227
|
-
opacity: "0.85"
|
|
54228
|
-
},
|
|
54292
|
+
style: toolbar_style(),
|
|
54229
54293
|
getItems: () => {
|
|
54230
54294
|
return [
|
|
54231
54295
|
{
|
package/package.json
CHANGED
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -105,11 +105,22 @@ import {yui_theme_now, yui_watch_theme} from "./yui_theme.js";
|
|
|
105
105
|
|
|
106
106
|
/***************************************************************
|
|
107
107
|
* YuiToolbar — G6 Toolbar subclass that adds per-item className
|
|
108
|
-
* and disabled support.
|
|
108
|
+
* and disabled support, plus three item kinds G6 does not have.
|
|
109
109
|
*
|
|
110
110
|
* Each item in getItems() may carry:
|
|
111
111
|
* className — CSS class added to the div (e.g. 'EV_SAVE_GRAPH')
|
|
112
112
|
* disabled — boolean; sets the 'disabled' attribute initially
|
|
113
|
+
* text — render this text instead of a sprite symbol; a
|
|
114
|
+
* label is the only readable way to say `1:1`, and
|
|
115
|
+
* every editor that offers actual-size writes it
|
|
116
|
+
* readout — a text item that REPORTS instead of acting (the
|
|
117
|
+
* zoom level); it is not a button
|
|
118
|
+
* separator — a group divider, not an item
|
|
119
|
+
*
|
|
120
|
+
* The base class fires onClick only for elements whose class list
|
|
121
|
+
* contains `g6-toolbar-item`, so a separator and a readout carry
|
|
122
|
+
* their own class and are inert by construction, with no guard in
|
|
123
|
+
* the click handler.
|
|
113
124
|
*
|
|
114
125
|
* With className set to the event name, the lib_graph.js state
|
|
115
126
|
* functions (set_submit_state, disableElements, …) work on toolbar
|
|
@@ -122,14 +133,36 @@ class YuiToolbar extends Toolbar
|
|
|
122
133
|
{
|
|
123
134
|
const items = await this.options.getItems();
|
|
124
135
|
return items.map((item) => {
|
|
136
|
+
if(item.separator) {
|
|
137
|
+
return `<div class="g6-toolbar-sep" aria-hidden="true"></div>`;
|
|
138
|
+
}
|
|
139
|
+
|
|
125
140
|
const extra_class = item.className ? ` ${item.className}` : '';
|
|
126
|
-
const
|
|
141
|
+
const title = item.title ?? '';
|
|
142
|
+
|
|
143
|
+
if(item.readout) {
|
|
144
|
+
return (
|
|
145
|
+
`<div class="g6-toolbar-readout${extra_class}"` +
|
|
146
|
+
` title="${title}" aria-label="${title}">` +
|
|
147
|
+
`${item.text ?? ''}` +
|
|
148
|
+
`</div>`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const disabled = item.disabled ? ' disabled' : '';
|
|
153
|
+
const is_text = (item.text !== undefined);
|
|
154
|
+
const kind_class = is_text ? ' g6-toolbar-item-text' : '';
|
|
155
|
+
const body = is_text
|
|
156
|
+
? `<span class="g6-toolbar-text">${item.text}</span>`
|
|
157
|
+
: `<svg aria-hidden="true" focusable="false">` +
|
|
158
|
+
`<use xlink:href="#${item.id}"></use>` +
|
|
159
|
+
`</svg>`;
|
|
160
|
+
|
|
127
161
|
return (
|
|
128
|
-
`<div class="g6-toolbar-item${extra_class}"` +
|
|
129
|
-
` value="${item.value}" title="${
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
`</svg>` +
|
|
162
|
+
`<div class="g6-toolbar-item${kind_class}${extra_class}"` +
|
|
163
|
+
` value="${item.value}" title="${title}"` +
|
|
164
|
+
` aria-label="${title}"${disabled}>` +
|
|
165
|
+
`${body}` +
|
|
133
166
|
`</div>`
|
|
134
167
|
);
|
|
135
168
|
}).join('');
|
|
@@ -732,6 +765,7 @@ function configure_events(gobj)
|
|
|
732
765
|
update_edge_icon_position(gobj);
|
|
733
766
|
update_node_icon_position(gobj);
|
|
734
767
|
update_link_icon_position(gobj);
|
|
768
|
+
update_zoom_readout(gobj);
|
|
735
769
|
});
|
|
736
770
|
|
|
737
771
|
// Re-render G6 toolbars when language changes
|
|
@@ -863,6 +897,79 @@ function update_toolbar(gobj)
|
|
|
863
897
|
}
|
|
864
898
|
}
|
|
865
899
|
|
|
900
|
+
/************************************************************
|
|
901
|
+
* The floating toolbars follow the theme.
|
|
902
|
+
*
|
|
903
|
+
* They used to be pinned to a light background in BOTH themes,
|
|
904
|
+
* with the icon colour pinned dark so it survived that -- two
|
|
905
|
+
* light islands sitting over a dark canvas. The tokens do the
|
|
906
|
+
* flipping now; the fallbacks are the old forced-light values, so
|
|
907
|
+
* a host without Bulma gets exactly what it had.
|
|
908
|
+
************************************************************/
|
|
909
|
+
function toolbar_style(extra)
|
|
910
|
+
{
|
|
911
|
+
return Object.assign({
|
|
912
|
+
backgroundColor: 'var(--bulma-scheme-main, #f5f5f5)',
|
|
913
|
+
color: 'var(--bulma-text-strong, #333)',
|
|
914
|
+
padding: '8px',
|
|
915
|
+
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
|
|
916
|
+
borderRadius: '8px',
|
|
917
|
+
border: '1px solid var(--bulma-border-weak, #e8e8e8)',
|
|
918
|
+
opacity: '0.85',
|
|
919
|
+
}, extra || {});
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/************************************************************
|
|
923
|
+
* The zoom level as a percentage, the way every editor that
|
|
924
|
+
* shows one writes it. Empty while the graph cannot be asked --
|
|
925
|
+
* the toolbar is built before the first draw, and a viewport
|
|
926
|
+
* that does not exist yet has no zoom to report.
|
|
927
|
+
************************************************************/
|
|
928
|
+
function zoom_percent_text(gobj)
|
|
929
|
+
{
|
|
930
|
+
let graph = gobj.priv.graph;
|
|
931
|
+
|
|
932
|
+
if(!graph || typeof graph.getZoom !== "function") {
|
|
933
|
+
return "";
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
let zoom;
|
|
937
|
+
try {
|
|
938
|
+
zoom = graph.getZoom();
|
|
939
|
+
} catch(e) {
|
|
940
|
+
return "";
|
|
941
|
+
}
|
|
942
|
+
if(!zoom) {
|
|
943
|
+
return "";
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
return `${Math.round(zoom * 100)}%`;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/************************************************************
|
|
950
|
+
* Repaint the zoom readout in place.
|
|
951
|
+
*
|
|
952
|
+
* The text node is patched instead of re-rendering the plugin:
|
|
953
|
+
* the readout follows the wheel, and updatePlugin() rebuilds the
|
|
954
|
+
* whole toolbar's innerHTML -- which would also drop the disabled
|
|
955
|
+
* state of the edit buttons on every notch of the wheel.
|
|
956
|
+
************************************************************/
|
|
957
|
+
function update_zoom_readout(gobj)
|
|
958
|
+
{
|
|
959
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
960
|
+
|
|
961
|
+
if(!$container) {
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
let $readout = $container.querySelector(".G6_ZOOM_LEVEL");
|
|
966
|
+
if(!$readout) {
|
|
967
|
+
return; /* no toolbar (with_toolbar false): nobody to report to */
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
$readout.textContent = zoom_percent_text(gobj);
|
|
971
|
+
}
|
|
972
|
+
|
|
866
973
|
function configure_toolbar(gobj)
|
|
867
974
|
{
|
|
868
975
|
let priv = gobj.priv;
|
|
@@ -880,28 +987,37 @@ function configure_toolbar(gobj)
|
|
|
880
987
|
type: 'yui-toolbar',
|
|
881
988
|
className: 'g6-toolbar-large',
|
|
882
989
|
position: toolbar_position,
|
|
883
|
-
style: {
|
|
884
|
-
|
|
885
|
-
/* Forced-light bg in both themes → pin dark icon
|
|
886
|
-
* color (currentColor) so it stays visible in dark. */
|
|
887
|
-
color: '#333',
|
|
888
|
-
padding: '8px',
|
|
889
|
-
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
|
|
890
|
-
borderRadius: '8px',
|
|
891
|
-
border: '1px solid #e8e8e8',
|
|
892
|
-
opacity: '0.85',
|
|
893
|
-
marginTop: '12px',
|
|
990
|
+
style: toolbar_style({
|
|
991
|
+
marginTop: '12px',
|
|
894
992
|
marginLeft: '12px',
|
|
895
|
-
},
|
|
993
|
+
}),
|
|
896
994
|
getItems: () => {
|
|
897
995
|
let items = [
|
|
898
996
|
{ id: 'g6-icon-zoom-in', value: 'zoom-in', className: 'EV_ZOOM_IN', title: t('zoom in') },
|
|
899
997
|
{ id: 'g6-icon-zoom-out', value: 'zoom-out', className: 'EV_ZOOM_OUT', title: t('zoom out') },
|
|
998
|
+
/* What the two buttons above change. Every editor
|
|
999
|
+
* that offers a zoom shows the number; without it
|
|
1000
|
+
* `1:1` is a jump to a value nobody was told. */
|
|
1001
|
+
{ readout: true, className: 'G6_ZOOM_LEVEL',
|
|
1002
|
+
text: zoom_percent_text(gobj), title: t('zoom level') },
|
|
1003
|
+
{ separator: true },
|
|
900
1004
|
{ id: 'g6-icon-fit', value: 'auto-fit', className: 'EV_AUTO_FIT', title: t('auto fit') },
|
|
901
|
-
|
|
1005
|
+
/* `1:1`, not a house. The action is `zoomTo(1)`: it
|
|
1006
|
+
* sets the SCALE and leaves the camera where it was,
|
|
1007
|
+
* which is neither of the two things a house means
|
|
1008
|
+
* anywhere -- a map's initial extent, an editor's
|
|
1009
|
+
* starting view. Sitting under `fit`, the house read
|
|
1010
|
+
* as a second way to get the whole graph back, and
|
|
1011
|
+
* answered with the same corner of it at 100%.
|
|
1012
|
+
* Actual size is WRITTEN in every editor that offers
|
|
1013
|
+
* it, never drawn: there is no glyph for it. */
|
|
1014
|
+
{ text: '1:1', value: 'reset', className: 'EV_ZOOM_RESET', title: t('actual size') },
|
|
902
1015
|
];
|
|
903
1016
|
|
|
904
1017
|
if(gobj_read_bool_attr(gobj, "with_fullscreen")) {
|
|
1018
|
+
/* Full screen is a WINDOW control, not a camera one:
|
|
1019
|
+
* it goes in its own group. */
|
|
1020
|
+
items.push({ separator: true });
|
|
905
1021
|
if(priv.is_fullscreen) {
|
|
906
1022
|
items.push(
|
|
907
1023
|
{ id: 'g6-icon-fullscreen-exit', value: 'exit-fullscreen', className: 'EV_EXIT_FULLSCREEN', title: t('exit full screen') }
|
|
@@ -976,18 +1092,7 @@ function configure_toolbar_edit(gobj)
|
|
|
976
1092
|
type: 'yui-toolbar',
|
|
977
1093
|
className: 'g6-toolbar-large',
|
|
978
1094
|
position: 'left-top',
|
|
979
|
-
style:
|
|
980
|
-
backgroundColor: '#f5f5f5',
|
|
981
|
-
/* Toolbar bg is forced light in BOTH themes; icons
|
|
982
|
-
* use currentColor, so pin a dark color or in dark
|
|
983
|
-
* theme they'd be light-on-light (invisible). */
|
|
984
|
-
color: '#333',
|
|
985
|
-
padding: '8px',
|
|
986
|
-
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
|
|
987
|
-
borderRadius: '8px',
|
|
988
|
-
border: '1px solid #e8e8e8',
|
|
989
|
-
opacity: '0.85',
|
|
990
|
-
},
|
|
1095
|
+
style: toolbar_style(),
|
|
991
1096
|
getItems: () => {
|
|
992
1097
|
return [
|
|
993
1098
|
{ id: 'g6-icon-plus', value: 'create-node', className: 'EV_CREATE_NODE_BTN color_create_state', title: t('create node') },
|
package/src/lib_graph.css
CHANGED
|
@@ -55,13 +55,79 @@
|
|
|
55
55
|
opacity: 0.35;
|
|
56
56
|
pointer-events: none;
|
|
57
57
|
}
|
|
58
|
-
/* Vertical toolbar (column): horizontal
|
|
58
|
+
/* Vertical toolbar (column): horizontal hairline between items */
|
|
59
59
|
.g6-toolbar.g6-toolbar-large[style*="flex-direction: column"] .g6-toolbar-item + .g6-toolbar-item {
|
|
60
|
-
border-top: 1px solid #d9d9d9;
|
|
60
|
+
border-top: 1px solid var(--bulma-border-weak, #d9d9d9);
|
|
61
61
|
}
|
|
62
|
-
/* Horizontal toolbar (row): vertical
|
|
62
|
+
/* Horizontal toolbar (row): vertical hairline between items */
|
|
63
63
|
.g6-toolbar.g6-toolbar-large[style*="flex-direction: row"] .g6-toolbar-item + .g6-toolbar-item {
|
|
64
|
-
border-left: 1px solid #d9d9d9;
|
|
64
|
+
border-left: 1px solid var(--bulma-border-weak, #d9d9d9);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* Group divider. Every item already carries a hairline against its
|
|
69
|
+
* neighbour, so a group break has to be a GAP, not one more line --
|
|
70
|
+
* otherwise the camera controls and the window control read as one
|
|
71
|
+
* list of six equal things.
|
|
72
|
+
*/
|
|
73
|
+
.g6-toolbar.g6-toolbar-large .g6-toolbar-sep {
|
|
74
|
+
background-color: var(--bulma-border, #cfcfcf);
|
|
75
|
+
flex: none;
|
|
76
|
+
}
|
|
77
|
+
.g6-toolbar.g6-toolbar-large[style*="flex-direction: column"] .g6-toolbar-sep {
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 1px;
|
|
80
|
+
margin: 5px 0;
|
|
81
|
+
}
|
|
82
|
+
.g6-toolbar.g6-toolbar-large[style*="flex-direction: row"] .g6-toolbar-sep {
|
|
83
|
+
width: 1px;
|
|
84
|
+
align-self: stretch;
|
|
85
|
+
margin: 0 5px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* A toolbar item whose glyph is TEXT (`1:1`). There is no icon for
|
|
90
|
+
* actual size, so the item has to be as wide as its label instead
|
|
91
|
+
* of the square an icon gets.
|
|
92
|
+
*/
|
|
93
|
+
.g6-toolbar.g6-toolbar-large .g6-toolbar-item-text {
|
|
94
|
+
width: auto;
|
|
95
|
+
min-width: 20px;
|
|
96
|
+
}
|
|
97
|
+
.g6-toolbar.g6-toolbar-large .g6-toolbar-text {
|
|
98
|
+
display: block;
|
|
99
|
+
line-height: 20px;
|
|
100
|
+
font-size: 0.8125rem;
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
text-align: center;
|
|
103
|
+
white-space: nowrap;
|
|
104
|
+
font-variant-numeric: tabular-nums;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/*
|
|
108
|
+
* The zoom readout REPORTS, it does not act: no pointer, no hover,
|
|
109
|
+
* and quieter than the buttons it sits with. It carries no
|
|
110
|
+
* `g6-toolbar-item` class, which is also what keeps G6's own click
|
|
111
|
+
* handler from firing on it.
|
|
112
|
+
*/
|
|
113
|
+
.g6-toolbar.g6-toolbar-large .g6-toolbar-readout {
|
|
114
|
+
padding: 2px 6px;
|
|
115
|
+
font-size: 0.75rem;
|
|
116
|
+
text-align: center;
|
|
117
|
+
white-space: nowrap;
|
|
118
|
+
cursor: default;
|
|
119
|
+
opacity: 0.7;
|
|
120
|
+
font-variant-numeric: tabular-nums;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/*
|
|
124
|
+
* Hover follows the theme too. G6 injects `.g6-toolbar-item:hover
|
|
125
|
+
* { background-color: #f0f0f0 }` at runtime, AFTER our bundled CSS,
|
|
126
|
+
* so an equal-specificity rule would lose -- the extra class on the
|
|
127
|
+
* toolbar itself is what wins it, with no !important.
|
|
128
|
+
*/
|
|
129
|
+
.g6-toolbar.g6-toolbar-large .g6-toolbar-item:hover {
|
|
130
|
+
background-color: var(--bulma-background, #f0f0f0);
|
|
65
131
|
}
|
|
66
132
|
|
|
67
133
|
/* No transient effects: open/move popups immediately (user pref).
|