@yuneta/gobj-ui 7.14.3 → 7.16.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 +90 -0
- package/dist/gobj-ui.cjs.js +260 -47
- package/dist/gobj-ui.es.js +260 -47
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +431 -58
- package/src/lib_graph.css +70 -4
package/README.md
CHANGED
|
@@ -668,6 +668,96 @@ 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
|
+
|
|
711
|
+
### Selecting several nodes, and moving them together
|
|
712
|
+
|
|
713
|
+
In **edition** mode the graph has a real selection, not just "the node you
|
|
714
|
+
clicked":
|
|
715
|
+
|
|
716
|
+
| gesture | what it does |
|
|
717
|
+
|---|---|
|
|
718
|
+
| click a node | selects it **and opens it**: resize handles, ports, popovers |
|
|
719
|
+
| **shift + click** | adds that node to the selection, or takes it out |
|
|
720
|
+
| **shift + drag on the canvas** | rubber band: the selection becomes what it enclosed |
|
|
721
|
+
| drag any selected node | **moves the whole selection**, as one undo |
|
|
722
|
+
| click the canvas | clears it |
|
|
723
|
+
|
|
724
|
+
Three decisions are worth knowing, because each one is where this could have
|
|
725
|
+
gone wrong:
|
|
726
|
+
|
|
727
|
+
- **G6's `selected` element state IS the selection.** `drag-element` decides
|
|
728
|
+
what a drag moves by asking the graph for it
|
|
729
|
+
(`getElementDataByState('node', 'selected')`), so a set kept anywhere else
|
|
730
|
+
would be a second truth the drag never consults — the ring would say five and
|
|
731
|
+
one would move. It also batches the move, so a group drag is one history
|
|
732
|
+
entry rather than one per node.
|
|
733
|
+
|
|
734
|
+
- **The ring is painted into the card's own html**, and it had to be. A state
|
|
735
|
+
style paints on a node's KEY SHAPE, and every node here is an `html` node
|
|
736
|
+
whose key shape is a DOM element — the same reason the amber highlight had
|
|
737
|
+
never appeared before `7.3.0`. Selecting with `brush-select` and nothing else
|
|
738
|
+
would have selected correctly and shown **nothing**. The ring is blue and
|
|
739
|
+
drawn OUTSIDE the amber halo, so a node that is both a find match and
|
|
740
|
+
selected wears both; one function composes them (`ring_shadow`), because
|
|
741
|
+
before it each repaint wrote its own flag and erased the other's.
|
|
742
|
+
|
|
743
|
+
- **The gesture is G6's, the result is an event.** `brush-select` gets an
|
|
744
|
+
`onSelect` that sends `EV_BRUSH_SELECT` with the ids, and the action does the
|
|
745
|
+
work — so a marquee shows up in the `machine` trace like every other action.
|
|
746
|
+
Shift+click is not G6's `click-select` at all: this gclass already owns
|
|
747
|
+
`EV_NODE_CLICK`, and adding a second selection owner outside the FSM is how
|
|
748
|
+
the two end up disagreeing.
|
|
749
|
+
|
|
750
|
+
Panning gives way while **Shift** is held (`drag-canvas` takes an `enable`
|
|
751
|
+
predicate), or the canvas would pan under the rubber band — G6 binds
|
|
752
|
+
`drag-canvas` straight to the drag events, and its own docs warn that the two
|
|
753
|
+
gestures cannot both be a plain drag.
|
|
754
|
+
|
|
755
|
+
**A marquee selects, it does not open.** Even when it encloses exactly one
|
|
756
|
+
node, the handles and ports stay away: `_selected_node_id` means *the node
|
|
757
|
+
opened for editing*, and only a click sets it. Everything that hangs off a
|
|
758
|
+
single node reads that field, so a multiple selection puts all of it away by
|
|
759
|
+
construction rather than by a check in twenty places.
|
|
760
|
+
|
|
671
761
|
### Finding a node in the graph
|
|
672
762
|
|
|
673
763
|
`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
|
};
|
|
@@ -53730,6 +53747,7 @@ ensure_drag_canvas_patch();
|
|
|
53730
53747
|
var GCLASS_NAME$1 = "C_G6_NODES_TREE";
|
|
53731
53748
|
var HIGHLIGHT_COLOR = "#f0a020";
|
|
53732
53749
|
var HIGHLIGHT_HALO = "rgba(240,160,32,0.35)";
|
|
53750
|
+
var SELECT_RING = "rgba(59,130,246,0.95)";
|
|
53733
53751
|
/***************************************************************
|
|
53734
53752
|
* Internal layout and operation mode definitions
|
|
53735
53753
|
***************************************************************/
|
|
@@ -53841,6 +53859,7 @@ var PRIVATE_DATA$1 = {
|
|
|
53841
53859
|
_link_saved_styles: [],
|
|
53842
53860
|
_focus_topic: null,
|
|
53843
53861
|
_focus_ids: [],
|
|
53862
|
+
_selected_paint_ids: [],
|
|
53844
53863
|
_pending_focus_topic: null,
|
|
53845
53864
|
_pending_find: null,
|
|
53846
53865
|
_layout_asked: "",
|
|
@@ -54063,6 +54082,7 @@ function configure_events(gobj) {
|
|
|
54063
54082
|
update_edge_icon_position(gobj);
|
|
54064
54083
|
update_node_icon_position(gobj);
|
|
54065
54084
|
update_link_icon_position(gobj);
|
|
54085
|
+
update_zoom_readout(gobj);
|
|
54066
54086
|
});
|
|
54067
54087
|
priv._on_language_changed = () => {
|
|
54068
54088
|
update_toolbar(gobj);
|
|
@@ -54128,6 +54148,59 @@ function update_toolbar(gobj) {
|
|
|
54128
54148
|
configure_toolbar_edit(gobj);
|
|
54129
54149
|
if (graph_get_plugin(gobj, "toolbar-edit")) graph.updatePlugin({ key: "toolbar-edit" });
|
|
54130
54150
|
}
|
|
54151
|
+
/************************************************************
|
|
54152
|
+
* The floating toolbars follow the theme.
|
|
54153
|
+
*
|
|
54154
|
+
* They used to be pinned to a light background in BOTH themes,
|
|
54155
|
+
* with the icon colour pinned dark so it survived that -- two
|
|
54156
|
+
* light islands sitting over a dark canvas. The tokens do the
|
|
54157
|
+
* flipping now; the fallbacks are the old forced-light values, so
|
|
54158
|
+
* a host without Bulma gets exactly what it had.
|
|
54159
|
+
************************************************************/
|
|
54160
|
+
function toolbar_style(extra) {
|
|
54161
|
+
return Object.assign({
|
|
54162
|
+
backgroundColor: "var(--bulma-scheme-main, #f5f5f5)",
|
|
54163
|
+
color: "var(--bulma-text-strong, #333)",
|
|
54164
|
+
padding: "8px",
|
|
54165
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
54166
|
+
borderRadius: "8px",
|
|
54167
|
+
border: "1px solid var(--bulma-border-weak, #e8e8e8)",
|
|
54168
|
+
opacity: "0.85"
|
|
54169
|
+
}, extra || {});
|
|
54170
|
+
}
|
|
54171
|
+
/************************************************************
|
|
54172
|
+
* The zoom level as a percentage, the way every editor that
|
|
54173
|
+
* shows one writes it. Empty while the graph cannot be asked --
|
|
54174
|
+
* the toolbar is built before the first draw, and a viewport
|
|
54175
|
+
* that does not exist yet has no zoom to report.
|
|
54176
|
+
************************************************************/
|
|
54177
|
+
function zoom_percent_text(gobj) {
|
|
54178
|
+
let graph = gobj.priv.graph;
|
|
54179
|
+
if (!graph || typeof graph.getZoom !== "function") return "";
|
|
54180
|
+
let zoom;
|
|
54181
|
+
try {
|
|
54182
|
+
zoom = graph.getZoom();
|
|
54183
|
+
} catch (e) {
|
|
54184
|
+
return "";
|
|
54185
|
+
}
|
|
54186
|
+
if (!zoom) return "";
|
|
54187
|
+
return `${Math.round(zoom * 100)}%`;
|
|
54188
|
+
}
|
|
54189
|
+
/************************************************************
|
|
54190
|
+
* Repaint the zoom readout in place.
|
|
54191
|
+
*
|
|
54192
|
+
* The text node is patched instead of re-rendering the plugin:
|
|
54193
|
+
* the readout follows the wheel, and updatePlugin() rebuilds the
|
|
54194
|
+
* whole toolbar's innerHTML -- which would also drop the disabled
|
|
54195
|
+
* state of the edit buttons on every notch of the wheel.
|
|
54196
|
+
************************************************************/
|
|
54197
|
+
function update_zoom_readout(gobj) {
|
|
54198
|
+
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
54199
|
+
if (!$container) return;
|
|
54200
|
+
let $readout = $container.querySelector(".G6_ZOOM_LEVEL");
|
|
54201
|
+
if (!$readout) return;
|
|
54202
|
+
$readout.textContent = zoom_percent_text(gobj);
|
|
54203
|
+
}
|
|
54131
54204
|
function configure_toolbar(gobj) {
|
|
54132
54205
|
let priv = gobj.priv;
|
|
54133
54206
|
let toolbar_position = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "toolbar_position") || "top-left";
|
|
@@ -54136,17 +54209,10 @@ function configure_toolbar(gobj) {
|
|
|
54136
54209
|
type: "yui-toolbar",
|
|
54137
54210
|
className: "g6-toolbar-large",
|
|
54138
54211
|
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",
|
|
54212
|
+
style: toolbar_style({
|
|
54147
54213
|
marginTop: "12px",
|
|
54148
54214
|
marginLeft: "12px"
|
|
54149
|
-
},
|
|
54215
|
+
}),
|
|
54150
54216
|
getItems: () => {
|
|
54151
54217
|
let items = [
|
|
54152
54218
|
{
|
|
@@ -54161,6 +54227,13 @@ function configure_toolbar(gobj) {
|
|
|
54161
54227
|
className: "EV_ZOOM_OUT",
|
|
54162
54228
|
title: (0, i18next.t)("zoom out")
|
|
54163
54229
|
},
|
|
54230
|
+
{
|
|
54231
|
+
readout: true,
|
|
54232
|
+
className: "G6_ZOOM_LEVEL",
|
|
54233
|
+
text: zoom_percent_text(gobj),
|
|
54234
|
+
title: (0, i18next.t)("zoom level")
|
|
54235
|
+
},
|
|
54236
|
+
{ separator: true },
|
|
54164
54237
|
{
|
|
54165
54238
|
id: "g6-icon-fit",
|
|
54166
54239
|
value: "auto-fit",
|
|
@@ -54168,13 +54241,14 @@ function configure_toolbar(gobj) {
|
|
|
54168
54241
|
title: (0, i18next.t)("auto fit")
|
|
54169
54242
|
},
|
|
54170
54243
|
{
|
|
54171
|
-
|
|
54244
|
+
text: "1:1",
|
|
54172
54245
|
value: "reset",
|
|
54173
54246
|
className: "EV_ZOOM_RESET",
|
|
54174
|
-
title: (0, i18next.t)("
|
|
54247
|
+
title: (0, i18next.t)("actual size")
|
|
54175
54248
|
}
|
|
54176
54249
|
];
|
|
54177
54250
|
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_fullscreen")) {
|
|
54251
|
+
items.push({ separator: true });
|
|
54178
54252
|
if (priv.is_fullscreen) items.push({
|
|
54179
54253
|
id: "g6-icon-fullscreen-exit",
|
|
54180
54254
|
value: "exit-fullscreen",
|
|
@@ -54230,15 +54304,7 @@ function configure_toolbar_edit(gobj) {
|
|
|
54230
54304
|
type: "yui-toolbar",
|
|
54231
54305
|
className: "g6-toolbar-large",
|
|
54232
54306
|
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
|
-
},
|
|
54307
|
+
style: toolbar_style(),
|
|
54242
54308
|
getItems: () => {
|
|
54243
54309
|
return [
|
|
54244
54310
|
{
|
|
@@ -54314,9 +54380,24 @@ function configure_behaviour(gobj) {
|
|
|
54314
54380
|
case "edition":
|
|
54315
54381
|
priv.edit_mode = true;
|
|
54316
54382
|
behaviors = [
|
|
54317
|
-
|
|
54383
|
+
{
|
|
54384
|
+
type: "drag-canvas",
|
|
54385
|
+
key: "drag-canvas",
|
|
54386
|
+
enable: (event) => !event.shiftKey
|
|
54387
|
+
},
|
|
54318
54388
|
"zoom-canvas",
|
|
54319
|
-
"drag-element"
|
|
54389
|
+
"drag-element",
|
|
54390
|
+
{
|
|
54391
|
+
type: "brush-select",
|
|
54392
|
+
key: "brush-select",
|
|
54393
|
+
trigger: ["shift"],
|
|
54394
|
+
enableElements: ["node"],
|
|
54395
|
+
immediately: false,
|
|
54396
|
+
onSelect: (states) => {
|
|
54397
|
+
let ids = Object.keys(states || {}).filter((id) => (states[id] || []).includes("selected"));
|
|
54398
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_BRUSH_SELECT", { ids }, gobj);
|
|
54399
|
+
}
|
|
54400
|
+
}
|
|
54320
54401
|
];
|
|
54321
54402
|
break;
|
|
54322
54403
|
case "operation":
|
|
@@ -55431,6 +55512,81 @@ function perform_history_op(gobj, is_redo) {
|
|
|
55431
55512
|
sync_history_to_backend(gobj, cmd ? cmd.original : null);
|
|
55432
55513
|
}
|
|
55433
55514
|
}
|
|
55515
|
+
/************************************************************
|
|
55516
|
+
* The selection.
|
|
55517
|
+
*
|
|
55518
|
+
* G6's `selected` element state IS the selection: `drag-element`
|
|
55519
|
+
* reads it (`getElementDataByState`) to decide what a drag moves,
|
|
55520
|
+
* so keeping the set anywhere else would be a second truth the
|
|
55521
|
+
* drag does not consult. What this gclass keeps is what is
|
|
55522
|
+
* PAINTED -- an html node draws no state style at all (its key
|
|
55523
|
+
* shape is a DOM element), so the ring lives in the card's own
|
|
55524
|
+
* html, exactly like the find highlight, and the painted set
|
|
55525
|
+
* exists to diff the repaint.
|
|
55526
|
+
************************************************************/
|
|
55527
|
+
function selected_node_ids(gobj) {
|
|
55528
|
+
let graph = gobj.priv.graph;
|
|
55529
|
+
if (!graph) return [];
|
|
55530
|
+
let data;
|
|
55531
|
+
try {
|
|
55532
|
+
data = graph.getElementDataByState("node", "selected") || [];
|
|
55533
|
+
} catch (e) {
|
|
55534
|
+
return [];
|
|
55535
|
+
}
|
|
55536
|
+
return data.map((nd) => nd.id);
|
|
55537
|
+
}
|
|
55538
|
+
/************************************************************
|
|
55539
|
+
* Move the ring to these nodes and off the ones that had it.
|
|
55540
|
+
************************************************************/
|
|
55541
|
+
function paint_selection(gobj, ids) {
|
|
55542
|
+
let priv = gobj.priv;
|
|
55543
|
+
let prev = priv._selected_paint_ids || [];
|
|
55544
|
+
let next = ids || [];
|
|
55545
|
+
priv._selected_paint_ids = next;
|
|
55546
|
+
repaint_cards(gobj, /* @__PURE__ */ new Set([...prev, ...next]));
|
|
55547
|
+
}
|
|
55548
|
+
/************************************************************
|
|
55549
|
+
* Select a SET of nodes: what a marquee and a shift-click do.
|
|
55550
|
+
*
|
|
55551
|
+
* It leaves `_selected_node_id` null even for a set of one,
|
|
55552
|
+
* because that field is not "the selection", it is "the node
|
|
55553
|
+
* opened for editing" -- the resize handles, the ports and the
|
|
55554
|
+
* popovers hang off it, and none of them means anything over a
|
|
55555
|
+
* set. Clicking a node is what opens one (`select_node`); a
|
|
55556
|
+
* marquee selects, it does not open.
|
|
55557
|
+
************************************************************/
|
|
55558
|
+
function set_selection(gobj, ids) {
|
|
55559
|
+
let graph = gobj.priv.graph;
|
|
55560
|
+
let next = [];
|
|
55561
|
+
for (let id of ids || []) try {
|
|
55562
|
+
if (graph.getNodeData(id)) next.push(id);
|
|
55563
|
+
} catch (e) {}
|
|
55564
|
+
deselect_node(gobj);
|
|
55565
|
+
if (!next.length) return;
|
|
55566
|
+
history_pause(gobj);
|
|
55567
|
+
try {
|
|
55568
|
+
let states = {};
|
|
55569
|
+
for (let id of next) states[id] = ["selected"];
|
|
55570
|
+
graph.setElementState(states);
|
|
55571
|
+
} catch (e) {
|
|
55572
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot set the selection: ${e}`);
|
|
55573
|
+
}
|
|
55574
|
+
paint_selection(gobj, next);
|
|
55575
|
+
graph_draw(gobj).then(() => {
|
|
55576
|
+
history_resume(gobj);
|
|
55577
|
+
});
|
|
55578
|
+
}
|
|
55579
|
+
/************************************************************
|
|
55580
|
+
* Add a node to the selection, or take it out of it.
|
|
55581
|
+
************************************************************/
|
|
55582
|
+
function toggle_in_selection(gobj, node_id) {
|
|
55583
|
+
let priv = gobj.priv;
|
|
55584
|
+
let current = new Set(selected_node_ids(gobj));
|
|
55585
|
+
for (let id of priv._selected_paint_ids || []) current.add(id);
|
|
55586
|
+
if (current.has(node_id)) current.delete(node_id);
|
|
55587
|
+
else current.add(node_id);
|
|
55588
|
+
set_selection(gobj, [...current]);
|
|
55589
|
+
}
|
|
55434
55590
|
function select_node(gobj, node_id) {
|
|
55435
55591
|
let priv = gobj.priv;
|
|
55436
55592
|
let graph = priv.graph;
|
|
@@ -55440,6 +55596,7 @@ function select_node(gobj, node_id) {
|
|
|
55440
55596
|
graph.setElementState(node_id, ["selected"]);
|
|
55441
55597
|
} catch (e) {}
|
|
55442
55598
|
priv._selected_node_id = node_id;
|
|
55599
|
+
paint_selection(gobj, [node_id]);
|
|
55443
55600
|
show_resize_handles(gobj);
|
|
55444
55601
|
show_node_icon(gobj);
|
|
55445
55602
|
graph_draw(gobj).then(() => {
|
|
@@ -55455,12 +55612,17 @@ function deselect_node(gobj) {
|
|
|
55455
55612
|
hide_node_popover(gobj);
|
|
55456
55613
|
hide_delete_confirm(gobj);
|
|
55457
55614
|
hide_unlink_confirm(gobj);
|
|
55458
|
-
|
|
55615
|
+
let clearing = /* @__PURE__ */ new Set([...selected_node_ids(gobj), ...priv._selected_paint_ids || []]);
|
|
55616
|
+
if (priv._selected_node_id) clearing.add(priv._selected_node_id);
|
|
55617
|
+
if (clearing.size) {
|
|
55459
55618
|
history_pause(gobj);
|
|
55460
55619
|
try {
|
|
55461
|
-
|
|
55620
|
+
let states = {};
|
|
55621
|
+
for (let id of clearing) states[id] = [];
|
|
55622
|
+
graph.setElementState(states);
|
|
55462
55623
|
} catch (e) {}
|
|
55463
55624
|
priv._selected_node_id = null;
|
|
55625
|
+
paint_selection(gobj, []);
|
|
55464
55626
|
graph_draw(gobj).then(() => {
|
|
55465
55627
|
history_resume(gobj);
|
|
55466
55628
|
});
|
|
@@ -56747,33 +56909,36 @@ function refresh_minimap(gobj) {
|
|
|
56747
56909
|
* same three-way choice — it lived inline in the theme refresh and is
|
|
56748
56910
|
* shared now. Returns null for a node that carries no desc.
|
|
56749
56911
|
************************************************************/
|
|
56750
|
-
function node_innerHTML_of(nd, theme, highlight) {
|
|
56912
|
+
function node_innerHTML_of(nd, theme, highlight, selected) {
|
|
56751
56913
|
if (!nd || !nd.data || !nd.data.desc) return null;
|
|
56752
56914
|
let desc = nd.data.desc;
|
|
56753
56915
|
let record = nd.data.record || {};
|
|
56754
56916
|
let label = node_label(desc, record);
|
|
56755
56917
|
switch (desc.node_treedb_type) {
|
|
56756
|
-
case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight);
|
|
56757
|
-
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight);
|
|
56758
|
-
case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight);
|
|
56918
|
+
case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight, selected);
|
|
56919
|
+
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected);
|
|
56920
|
+
case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected);
|
|
56759
56921
|
}
|
|
56760
56922
|
return null;
|
|
56761
56923
|
}
|
|
56762
56924
|
/************************************************************
|
|
56763
|
-
* Repaint
|
|
56764
|
-
*
|
|
56765
|
-
*
|
|
56925
|
+
* Repaint these cards with the flags they carry RIGHT NOW.
|
|
56926
|
+
*
|
|
56927
|
+
* One place, because the flags are independent and each used to
|
|
56928
|
+
* be written by whoever repainted last: a find that repainted its
|
|
56929
|
+
* matches erased the selection ring off them, and a selection
|
|
56930
|
+
* repainted over a match erased the amber. Both are read here
|
|
56931
|
+
* from where they live.
|
|
56766
56932
|
************************************************************/
|
|
56767
|
-
function
|
|
56933
|
+
function repaint_cards(gobj, ids) {
|
|
56768
56934
|
let priv = gobj.priv;
|
|
56769
56935
|
let graph = priv.graph;
|
|
56770
|
-
if (!graph) return;
|
|
56771
|
-
let
|
|
56772
|
-
let
|
|
56773
|
-
if (touched.size === 0) return;
|
|
56936
|
+
if (!graph || !ids || !ids.size) return;
|
|
56937
|
+
let focus = new Set(priv._focus_ids || []);
|
|
56938
|
+
let selected = new Set(priv._selected_paint_ids || []);
|
|
56774
56939
|
let updates = [];
|
|
56775
|
-
for (let id of
|
|
56776
|
-
let html = node_innerHTML_of(graph.getNodeData(id), priv.theme,
|
|
56940
|
+
for (let id of ids) {
|
|
56941
|
+
let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, focus.has(id), selected.has(id));
|
|
56777
56942
|
if (html !== null) updates.push({
|
|
56778
56943
|
id,
|
|
56779
56944
|
style: { innerHTML: html }
|
|
@@ -56784,19 +56949,32 @@ function apply_node_highlight(gobj, prev_ids, next_ids) {
|
|
|
56784
56949
|
graph.updateNodeData(updates);
|
|
56785
56950
|
graph.draw();
|
|
56786
56951
|
} catch (e) {
|
|
56787
|
-
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot repaint the
|
|
56952
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot repaint the cards: ${e}`);
|
|
56788
56953
|
}
|
|
56789
56954
|
}
|
|
56955
|
+
/************************************************************
|
|
56956
|
+
* Repaint the cards whose highlight state CHANGES: the ones that had it
|
|
56957
|
+
* and lose it, plus the ones that gain it. Only those — a treedb graph
|
|
56958
|
+
* is redrawn per keystroke of the find box otherwise.
|
|
56959
|
+
************************************************************/
|
|
56960
|
+
function apply_node_highlight(gobj, prev_ids, next_ids) {
|
|
56961
|
+
if (!gobj.priv.graph) return;
|
|
56962
|
+
let next = new Set(next_ids || []);
|
|
56963
|
+
let touched = /* @__PURE__ */ new Set([...prev_ids || [], ...next]);
|
|
56964
|
+
if (touched.size === 0) return;
|
|
56965
|
+
repaint_cards(gobj, touched);
|
|
56966
|
+
}
|
|
56790
56967
|
function refresh_html_nodes_theme(gobj, theme) {
|
|
56791
56968
|
let priv = gobj.priv;
|
|
56792
56969
|
let graph = priv.graph;
|
|
56793
56970
|
if (!graph) return;
|
|
56794
56971
|
let nodes = graph.getData().nodes || [];
|
|
56795
56972
|
let highlighted = new Set(priv._focus_ids || []);
|
|
56973
|
+
let selected = new Set(priv._selected_paint_ids || []);
|
|
56796
56974
|
let updates = [];
|
|
56797
56975
|
for (let i = 0; i < nodes.length; i++) {
|
|
56798
56976
|
let id = nodes[i].id;
|
|
56799
|
-
let html = node_innerHTML_of(graph.getNodeData(id), theme, highlighted.has(id));
|
|
56977
|
+
let html = node_innerHTML_of(graph.getNodeData(id), theme, highlighted.has(id), selected.has(id));
|
|
56800
56978
|
if (html !== null) updates.push({
|
|
56801
56979
|
id,
|
|
56802
56980
|
style: { innerHTML: html }
|
|
@@ -56826,12 +57004,29 @@ function refresh_default_edges_theme(gobj, theme) {
|
|
|
56826
57004
|
if (updates.length > 0) graph.updateEdgeData(updates);
|
|
56827
57005
|
}
|
|
56828
57006
|
/************************************************************
|
|
57007
|
+
* The rings a card can wear, composed into one `box-shadow`.
|
|
57008
|
+
*
|
|
57009
|
+
* A node can be a find match and be selected at the same time, so
|
|
57010
|
+
* neither ring may be written by overwriting the other: the amber
|
|
57011
|
+
* halo hugs the card and the blue selection ring is drawn outside
|
|
57012
|
+
* it, which is also the order that reads correctly when only one
|
|
57013
|
+
* of them is on.
|
|
57014
|
+
************************************************************/
|
|
57015
|
+
function ring_shadow(highlight, selected, base_shadow) {
|
|
57016
|
+
let rings = [];
|
|
57017
|
+
if (highlight) rings.push(`0 0 0 4px ${HIGHLIGHT_HALO}`);
|
|
57018
|
+
if (selected) rings.push(`0 0 0 ${highlight ? "7px" : "3px"} ${SELECT_RING}`);
|
|
57019
|
+
if (base_shadow) rings.push(base_shadow);
|
|
57020
|
+
if (!rings.length) return "";
|
|
57021
|
+
return `box-shadow: ${rings.join(", ")};`;
|
|
57022
|
+
}
|
|
57023
|
+
/************************************************************
|
|
56829
57024
|
* Build innerHTML for pure-child (leaf) nodes: a compact
|
|
56830
57025
|
* chip-card. Same colour/typography family as the entity card
|
|
56831
57026
|
* but lighter (1px border, no shadow, single line). The name is
|
|
56832
57027
|
* always legible (ellipsis + native title tooltip on overflow).
|
|
56833
57028
|
************************************************************/
|
|
56834
|
-
function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
57029
|
+
function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected) {
|
|
56835
57030
|
let title = key || label;
|
|
56836
57031
|
let dark = theme === "dark";
|
|
56837
57032
|
let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
|
|
@@ -56850,7 +57045,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
|
56850
57045
|
height: 100%;
|
|
56851
57046
|
background: ${bg};
|
|
56852
57047
|
border: ${highlight ? "3px" : "1px"} solid ${border};
|
|
56853
|
-
${highlight
|
|
57048
|
+
${ring_shadow(highlight, selected, "")}
|
|
56854
57049
|
border-radius: 8px;
|
|
56855
57050
|
color: ${text_color};
|
|
56856
57051
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
@@ -56876,7 +57071,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
|
56876
57071
|
* colour is kept (per-topic differentiation) but softened via
|
|
56877
57072
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
56878
57073
|
************************************************************/
|
|
56879
|
-
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight) {
|
|
57074
|
+
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected) {
|
|
56880
57075
|
let title = key || label;
|
|
56881
57076
|
let dark = theme === "dark";
|
|
56882
57077
|
let surface = dark ? "#1b2230" : "#ffffff";
|
|
@@ -56918,7 +57113,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
|
|
|
56918
57113
|
background: ${bg};
|
|
56919
57114
|
border: ${highlight ? "3px" : "1.5px"} ${border_style} ${border};
|
|
56920
57115
|
border-radius: 10px;
|
|
56921
|
-
|
|
57116
|
+
${ring_shadow(highlight, selected, shadow)}
|
|
56922
57117
|
color: ${title_color};
|
|
56923
57118
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
56924
57119
|
display: flex;
|
|
@@ -57839,6 +58034,17 @@ function ac_node_drag_end(gobj, event, kw, src) {
|
|
|
57839
58034
|
return 0;
|
|
57840
58035
|
}
|
|
57841
58036
|
/************************************************************
|
|
58037
|
+
* The rubber band let go: these are the nodes it enclosed.
|
|
58038
|
+
*
|
|
58039
|
+
* They arrive in the kw because `onSelect` runs BEFORE G6 writes
|
|
58040
|
+
* the state, so asking the graph here would answer the previous
|
|
58041
|
+
* selection.
|
|
58042
|
+
************************************************************/
|
|
58043
|
+
function ac_brush_select(gobj, event, kw, src) {
|
|
58044
|
+
set_selection(gobj, kw && kw.ids || []);
|
|
58045
|
+
return 0;
|
|
58046
|
+
}
|
|
58047
|
+
/************************************************************
|
|
57842
58048
|
* Node click - publish vertex clicked event
|
|
57843
58049
|
************************************************************/
|
|
57844
58050
|
function ac_node_click(gobj, event, kw, src) {
|
|
@@ -57853,7 +58059,8 @@ function ac_node_click(gobj, event, kw, src) {
|
|
|
57853
58059
|
topic_name: nodedata.data.desc.topic_name,
|
|
57854
58060
|
record: nodedata.data.record
|
|
57855
58061
|
});
|
|
57856
|
-
if (priv.edit_mode)
|
|
58062
|
+
if (priv.edit_mode && kw.evt.shiftKey) toggle_in_selection(gobj, node_id);
|
|
58063
|
+
else if (priv.edit_mode) {
|
|
57857
58064
|
let containerRect = priv.$container.getBoundingClientRect();
|
|
57858
58065
|
let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
|
|
57859
58066
|
let port_key = detect_port_click(gobj, node_id, canvasPoint[0], canvasPoint[1]);
|
|
@@ -58080,6 +58287,11 @@ function create_gclass$1(gclass_name) {
|
|
|
58080
58287
|
ac_node_drag_end,
|
|
58081
58288
|
null
|
|
58082
58289
|
],
|
|
58290
|
+
[
|
|
58291
|
+
"EV_BRUSH_SELECT",
|
|
58292
|
+
ac_brush_select,
|
|
58293
|
+
null
|
|
58294
|
+
],
|
|
58083
58295
|
[
|
|
58084
58296
|
"EV_ZOOM_IN",
|
|
58085
58297
|
ac_zoom_in,
|
|
@@ -58188,6 +58400,7 @@ function create_gclass$1(gclass_name) {
|
|
|
58188
58400
|
["EV_NODE_CONTEXT_MENU", 0],
|
|
58189
58401
|
["EV_CANVAS_CLICK", 0],
|
|
58190
58402
|
["EV_NODE_DRAG_END", 0],
|
|
58403
|
+
["EV_BRUSH_SELECT", 0],
|
|
58191
58404
|
["EV_ZOOM_IN", 0],
|
|
58192
58405
|
["EV_ZOOM_OUT", 0],
|
|
58193
58406
|
["EV_ZOOM_RESET", 0],
|