@yuneta/gobj-ui 7.15.0 → 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 +50 -0
- package/dist/gobj-ui.cjs.js +174 -25
- package/dist/gobj-ui.es.js +174 -25
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +294 -26
package/README.md
CHANGED
|
@@ -708,6 +708,56 @@ so it survived that — two light islands over a dark canvas.
|
|
|
708
708
|
**New keys for consumers: `actual size`, `zoom level`** (both tooltips, so a
|
|
709
709
|
host that has not defined them shows the key on hover and nothing else breaks).
|
|
710
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
|
+
|
|
711
761
|
### Finding a node in the graph
|
|
712
762
|
|
|
713
763
|
`C_YUI_TREEDB_GRAPH` carries a find box in the middle of its toolbar. It
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -53747,6 +53747,7 @@ ensure_drag_canvas_patch();
|
|
|
53747
53747
|
var GCLASS_NAME$1 = "C_G6_NODES_TREE";
|
|
53748
53748
|
var HIGHLIGHT_COLOR = "#f0a020";
|
|
53749
53749
|
var HIGHLIGHT_HALO = "rgba(240,160,32,0.35)";
|
|
53750
|
+
var SELECT_RING = "rgba(59,130,246,0.95)";
|
|
53750
53751
|
/***************************************************************
|
|
53751
53752
|
* Internal layout and operation mode definitions
|
|
53752
53753
|
***************************************************************/
|
|
@@ -53858,6 +53859,7 @@ var PRIVATE_DATA$1 = {
|
|
|
53858
53859
|
_link_saved_styles: [],
|
|
53859
53860
|
_focus_topic: null,
|
|
53860
53861
|
_focus_ids: [],
|
|
53862
|
+
_selected_paint_ids: [],
|
|
53861
53863
|
_pending_focus_topic: null,
|
|
53862
53864
|
_pending_find: null,
|
|
53863
53865
|
_layout_asked: "",
|
|
@@ -54378,9 +54380,24 @@ function configure_behaviour(gobj) {
|
|
|
54378
54380
|
case "edition":
|
|
54379
54381
|
priv.edit_mode = true;
|
|
54380
54382
|
behaviors = [
|
|
54381
|
-
|
|
54383
|
+
{
|
|
54384
|
+
type: "drag-canvas",
|
|
54385
|
+
key: "drag-canvas",
|
|
54386
|
+
enable: (event) => !event.shiftKey
|
|
54387
|
+
},
|
|
54382
54388
|
"zoom-canvas",
|
|
54383
|
-
"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
|
+
}
|
|
54384
54401
|
];
|
|
54385
54402
|
break;
|
|
54386
54403
|
case "operation":
|
|
@@ -55495,6 +55512,81 @@ function perform_history_op(gobj, is_redo) {
|
|
|
55495
55512
|
sync_history_to_backend(gobj, cmd ? cmd.original : null);
|
|
55496
55513
|
}
|
|
55497
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
|
+
}
|
|
55498
55590
|
function select_node(gobj, node_id) {
|
|
55499
55591
|
let priv = gobj.priv;
|
|
55500
55592
|
let graph = priv.graph;
|
|
@@ -55504,6 +55596,7 @@ function select_node(gobj, node_id) {
|
|
|
55504
55596
|
graph.setElementState(node_id, ["selected"]);
|
|
55505
55597
|
} catch (e) {}
|
|
55506
55598
|
priv._selected_node_id = node_id;
|
|
55599
|
+
paint_selection(gobj, [node_id]);
|
|
55507
55600
|
show_resize_handles(gobj);
|
|
55508
55601
|
show_node_icon(gobj);
|
|
55509
55602
|
graph_draw(gobj).then(() => {
|
|
@@ -55519,12 +55612,17 @@ function deselect_node(gobj) {
|
|
|
55519
55612
|
hide_node_popover(gobj);
|
|
55520
55613
|
hide_delete_confirm(gobj);
|
|
55521
55614
|
hide_unlink_confirm(gobj);
|
|
55522
|
-
|
|
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) {
|
|
55523
55618
|
history_pause(gobj);
|
|
55524
55619
|
try {
|
|
55525
|
-
|
|
55620
|
+
let states = {};
|
|
55621
|
+
for (let id of clearing) states[id] = [];
|
|
55622
|
+
graph.setElementState(states);
|
|
55526
55623
|
} catch (e) {}
|
|
55527
55624
|
priv._selected_node_id = null;
|
|
55625
|
+
paint_selection(gobj, []);
|
|
55528
55626
|
graph_draw(gobj).then(() => {
|
|
55529
55627
|
history_resume(gobj);
|
|
55530
55628
|
});
|
|
@@ -56811,33 +56909,36 @@ function refresh_minimap(gobj) {
|
|
|
56811
56909
|
* same three-way choice — it lived inline in the theme refresh and is
|
|
56812
56910
|
* shared now. Returns null for a node that carries no desc.
|
|
56813
56911
|
************************************************************/
|
|
56814
|
-
function node_innerHTML_of(nd, theme, highlight) {
|
|
56912
|
+
function node_innerHTML_of(nd, theme, highlight, selected) {
|
|
56815
56913
|
if (!nd || !nd.data || !nd.data.desc) return null;
|
|
56816
56914
|
let desc = nd.data.desc;
|
|
56817
56915
|
let record = nd.data.record || {};
|
|
56818
56916
|
let label = node_label(desc, record);
|
|
56819
56917
|
switch (desc.node_treedb_type) {
|
|
56820
|
-
case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight);
|
|
56821
|
-
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight);
|
|
56822
|
-
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);
|
|
56823
56921
|
}
|
|
56824
56922
|
return null;
|
|
56825
56923
|
}
|
|
56826
56924
|
/************************************************************
|
|
56827
|
-
* Repaint
|
|
56828
|
-
*
|
|
56829
|
-
*
|
|
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.
|
|
56830
56932
|
************************************************************/
|
|
56831
|
-
function
|
|
56933
|
+
function repaint_cards(gobj, ids) {
|
|
56832
56934
|
let priv = gobj.priv;
|
|
56833
56935
|
let graph = priv.graph;
|
|
56834
|
-
if (!graph) return;
|
|
56835
|
-
let
|
|
56836
|
-
let
|
|
56837
|
-
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 || []);
|
|
56838
56939
|
let updates = [];
|
|
56839
|
-
for (let id of
|
|
56840
|
-
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));
|
|
56841
56942
|
if (html !== null) updates.push({
|
|
56842
56943
|
id,
|
|
56843
56944
|
style: { innerHTML: html }
|
|
@@ -56848,19 +56949,32 @@ function apply_node_highlight(gobj, prev_ids, next_ids) {
|
|
|
56848
56949
|
graph.updateNodeData(updates);
|
|
56849
56950
|
graph.draw();
|
|
56850
56951
|
} catch (e) {
|
|
56851
|
-
(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}`);
|
|
56852
56953
|
}
|
|
56853
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
|
+
}
|
|
56854
56967
|
function refresh_html_nodes_theme(gobj, theme) {
|
|
56855
56968
|
let priv = gobj.priv;
|
|
56856
56969
|
let graph = priv.graph;
|
|
56857
56970
|
if (!graph) return;
|
|
56858
56971
|
let nodes = graph.getData().nodes || [];
|
|
56859
56972
|
let highlighted = new Set(priv._focus_ids || []);
|
|
56973
|
+
let selected = new Set(priv._selected_paint_ids || []);
|
|
56860
56974
|
let updates = [];
|
|
56861
56975
|
for (let i = 0; i < nodes.length; i++) {
|
|
56862
56976
|
let id = nodes[i].id;
|
|
56863
|
-
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));
|
|
56864
56978
|
if (html !== null) updates.push({
|
|
56865
56979
|
id,
|
|
56866
56980
|
style: { innerHTML: html }
|
|
@@ -56890,12 +57004,29 @@ function refresh_default_edges_theme(gobj, theme) {
|
|
|
56890
57004
|
if (updates.length > 0) graph.updateEdgeData(updates);
|
|
56891
57005
|
}
|
|
56892
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
|
+
/************************************************************
|
|
56893
57024
|
* Build innerHTML for pure-child (leaf) nodes: a compact
|
|
56894
57025
|
* chip-card. Same colour/typography family as the entity card
|
|
56895
57026
|
* but lighter (1px border, no shadow, single line). The name is
|
|
56896
57027
|
* always legible (ellipsis + native title tooltip on overflow).
|
|
56897
57028
|
************************************************************/
|
|
56898
|
-
function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
57029
|
+
function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected) {
|
|
56899
57030
|
let title = key || label;
|
|
56900
57031
|
let dark = theme === "dark";
|
|
56901
57032
|
let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
|
|
@@ -56914,7 +57045,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
|
56914
57045
|
height: 100%;
|
|
56915
57046
|
background: ${bg};
|
|
56916
57047
|
border: ${highlight ? "3px" : "1px"} solid ${border};
|
|
56917
|
-
${highlight
|
|
57048
|
+
${ring_shadow(highlight, selected, "")}
|
|
56918
57049
|
border-radius: 8px;
|
|
56919
57050
|
color: ${text_color};
|
|
56920
57051
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
@@ -56940,7 +57071,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
|
56940
57071
|
* colour is kept (per-topic differentiation) but softened via
|
|
56941
57072
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
56942
57073
|
************************************************************/
|
|
56943
|
-
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) {
|
|
56944
57075
|
let title = key || label;
|
|
56945
57076
|
let dark = theme === "dark";
|
|
56946
57077
|
let surface = dark ? "#1b2230" : "#ffffff";
|
|
@@ -56982,7 +57113,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
|
|
|
56982
57113
|
background: ${bg};
|
|
56983
57114
|
border: ${highlight ? "3px" : "1.5px"} ${border_style} ${border};
|
|
56984
57115
|
border-radius: 10px;
|
|
56985
|
-
|
|
57116
|
+
${ring_shadow(highlight, selected, shadow)}
|
|
56986
57117
|
color: ${title_color};
|
|
56987
57118
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
56988
57119
|
display: flex;
|
|
@@ -57903,6 +58034,17 @@ function ac_node_drag_end(gobj, event, kw, src) {
|
|
|
57903
58034
|
return 0;
|
|
57904
58035
|
}
|
|
57905
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
|
+
/************************************************************
|
|
57906
58048
|
* Node click - publish vertex clicked event
|
|
57907
58049
|
************************************************************/
|
|
57908
58050
|
function ac_node_click(gobj, event, kw, src) {
|
|
@@ -57917,7 +58059,8 @@ function ac_node_click(gobj, event, kw, src) {
|
|
|
57917
58059
|
topic_name: nodedata.data.desc.topic_name,
|
|
57918
58060
|
record: nodedata.data.record
|
|
57919
58061
|
});
|
|
57920
|
-
if (priv.edit_mode)
|
|
58062
|
+
if (priv.edit_mode && kw.evt.shiftKey) toggle_in_selection(gobj, node_id);
|
|
58063
|
+
else if (priv.edit_mode) {
|
|
57921
58064
|
let containerRect = priv.$container.getBoundingClientRect();
|
|
57922
58065
|
let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
|
|
57923
58066
|
let port_key = detect_port_click(gobj, node_id, canvasPoint[0], canvasPoint[1]);
|
|
@@ -58144,6 +58287,11 @@ function create_gclass$1(gclass_name) {
|
|
|
58144
58287
|
ac_node_drag_end,
|
|
58145
58288
|
null
|
|
58146
58289
|
],
|
|
58290
|
+
[
|
|
58291
|
+
"EV_BRUSH_SELECT",
|
|
58292
|
+
ac_brush_select,
|
|
58293
|
+
null
|
|
58294
|
+
],
|
|
58147
58295
|
[
|
|
58148
58296
|
"EV_ZOOM_IN",
|
|
58149
58297
|
ac_zoom_in,
|
|
@@ -58252,6 +58400,7 @@ function create_gclass$1(gclass_name) {
|
|
|
58252
58400
|
["EV_NODE_CONTEXT_MENU", 0],
|
|
58253
58401
|
["EV_CANVAS_CLICK", 0],
|
|
58254
58402
|
["EV_NODE_DRAG_END", 0],
|
|
58403
|
+
["EV_BRUSH_SELECT", 0],
|
|
58255
58404
|
["EV_ZOOM_IN", 0],
|
|
58256
58405
|
["EV_ZOOM_OUT", 0],
|
|
58257
58406
|
["EV_ZOOM_RESET", 0],
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -53734,6 +53734,7 @@ ensure_drag_canvas_patch();
|
|
|
53734
53734
|
var GCLASS_NAME$1 = "C_G6_NODES_TREE";
|
|
53735
53735
|
var HIGHLIGHT_COLOR = "#f0a020";
|
|
53736
53736
|
var HIGHLIGHT_HALO = "rgba(240,160,32,0.35)";
|
|
53737
|
+
var SELECT_RING = "rgba(59,130,246,0.95)";
|
|
53737
53738
|
/***************************************************************
|
|
53738
53739
|
* Internal layout and operation mode definitions
|
|
53739
53740
|
***************************************************************/
|
|
@@ -53845,6 +53846,7 @@ var PRIVATE_DATA$1 = {
|
|
|
53845
53846
|
_link_saved_styles: [],
|
|
53846
53847
|
_focus_topic: null,
|
|
53847
53848
|
_focus_ids: [],
|
|
53849
|
+
_selected_paint_ids: [],
|
|
53848
53850
|
_pending_focus_topic: null,
|
|
53849
53851
|
_pending_find: null,
|
|
53850
53852
|
_layout_asked: "",
|
|
@@ -54365,9 +54367,24 @@ function configure_behaviour(gobj) {
|
|
|
54365
54367
|
case "edition":
|
|
54366
54368
|
priv.edit_mode = true;
|
|
54367
54369
|
behaviors = [
|
|
54368
|
-
|
|
54370
|
+
{
|
|
54371
|
+
type: "drag-canvas",
|
|
54372
|
+
key: "drag-canvas",
|
|
54373
|
+
enable: (event) => !event.shiftKey
|
|
54374
|
+
},
|
|
54369
54375
|
"zoom-canvas",
|
|
54370
|
-
"drag-element"
|
|
54376
|
+
"drag-element",
|
|
54377
|
+
{
|
|
54378
|
+
type: "brush-select",
|
|
54379
|
+
key: "brush-select",
|
|
54380
|
+
trigger: ["shift"],
|
|
54381
|
+
enableElements: ["node"],
|
|
54382
|
+
immediately: false,
|
|
54383
|
+
onSelect: (states) => {
|
|
54384
|
+
let ids = Object.keys(states || {}).filter((id) => (states[id] || []).includes("selected"));
|
|
54385
|
+
gobj_send_event(gobj, "EV_BRUSH_SELECT", { ids }, gobj);
|
|
54386
|
+
}
|
|
54387
|
+
}
|
|
54371
54388
|
];
|
|
54372
54389
|
break;
|
|
54373
54390
|
case "operation":
|
|
@@ -55482,6 +55499,81 @@ function perform_history_op(gobj, is_redo) {
|
|
|
55482
55499
|
sync_history_to_backend(gobj, cmd ? cmd.original : null);
|
|
55483
55500
|
}
|
|
55484
55501
|
}
|
|
55502
|
+
/************************************************************
|
|
55503
|
+
* The selection.
|
|
55504
|
+
*
|
|
55505
|
+
* G6's `selected` element state IS the selection: `drag-element`
|
|
55506
|
+
* reads it (`getElementDataByState`) to decide what a drag moves,
|
|
55507
|
+
* so keeping the set anywhere else would be a second truth the
|
|
55508
|
+
* drag does not consult. What this gclass keeps is what is
|
|
55509
|
+
* PAINTED -- an html node draws no state style at all (its key
|
|
55510
|
+
* shape is a DOM element), so the ring lives in the card's own
|
|
55511
|
+
* html, exactly like the find highlight, and the painted set
|
|
55512
|
+
* exists to diff the repaint.
|
|
55513
|
+
************************************************************/
|
|
55514
|
+
function selected_node_ids(gobj) {
|
|
55515
|
+
let graph = gobj.priv.graph;
|
|
55516
|
+
if (!graph) return [];
|
|
55517
|
+
let data;
|
|
55518
|
+
try {
|
|
55519
|
+
data = graph.getElementDataByState("node", "selected") || [];
|
|
55520
|
+
} catch (e) {
|
|
55521
|
+
return [];
|
|
55522
|
+
}
|
|
55523
|
+
return data.map((nd) => nd.id);
|
|
55524
|
+
}
|
|
55525
|
+
/************************************************************
|
|
55526
|
+
* Move the ring to these nodes and off the ones that had it.
|
|
55527
|
+
************************************************************/
|
|
55528
|
+
function paint_selection(gobj, ids) {
|
|
55529
|
+
let priv = gobj.priv;
|
|
55530
|
+
let prev = priv._selected_paint_ids || [];
|
|
55531
|
+
let next = ids || [];
|
|
55532
|
+
priv._selected_paint_ids = next;
|
|
55533
|
+
repaint_cards(gobj, /* @__PURE__ */ new Set([...prev, ...next]));
|
|
55534
|
+
}
|
|
55535
|
+
/************************************************************
|
|
55536
|
+
* Select a SET of nodes: what a marquee and a shift-click do.
|
|
55537
|
+
*
|
|
55538
|
+
* It leaves `_selected_node_id` null even for a set of one,
|
|
55539
|
+
* because that field is not "the selection", it is "the node
|
|
55540
|
+
* opened for editing" -- the resize handles, the ports and the
|
|
55541
|
+
* popovers hang off it, and none of them means anything over a
|
|
55542
|
+
* set. Clicking a node is what opens one (`select_node`); a
|
|
55543
|
+
* marquee selects, it does not open.
|
|
55544
|
+
************************************************************/
|
|
55545
|
+
function set_selection(gobj, ids) {
|
|
55546
|
+
let graph = gobj.priv.graph;
|
|
55547
|
+
let next = [];
|
|
55548
|
+
for (let id of ids || []) try {
|
|
55549
|
+
if (graph.getNodeData(id)) next.push(id);
|
|
55550
|
+
} catch (e) {}
|
|
55551
|
+
deselect_node(gobj);
|
|
55552
|
+
if (!next.length) return;
|
|
55553
|
+
history_pause(gobj);
|
|
55554
|
+
try {
|
|
55555
|
+
let states = {};
|
|
55556
|
+
for (let id of next) states[id] = ["selected"];
|
|
55557
|
+
graph.setElementState(states);
|
|
55558
|
+
} catch (e) {
|
|
55559
|
+
log_error(`${gobj_short_name(gobj)}: cannot set the selection: ${e}`);
|
|
55560
|
+
}
|
|
55561
|
+
paint_selection(gobj, next);
|
|
55562
|
+
graph_draw(gobj).then(() => {
|
|
55563
|
+
history_resume(gobj);
|
|
55564
|
+
});
|
|
55565
|
+
}
|
|
55566
|
+
/************************************************************
|
|
55567
|
+
* Add a node to the selection, or take it out of it.
|
|
55568
|
+
************************************************************/
|
|
55569
|
+
function toggle_in_selection(gobj, node_id) {
|
|
55570
|
+
let priv = gobj.priv;
|
|
55571
|
+
let current = new Set(selected_node_ids(gobj));
|
|
55572
|
+
for (let id of priv._selected_paint_ids || []) current.add(id);
|
|
55573
|
+
if (current.has(node_id)) current.delete(node_id);
|
|
55574
|
+
else current.add(node_id);
|
|
55575
|
+
set_selection(gobj, [...current]);
|
|
55576
|
+
}
|
|
55485
55577
|
function select_node(gobj, node_id) {
|
|
55486
55578
|
let priv = gobj.priv;
|
|
55487
55579
|
let graph = priv.graph;
|
|
@@ -55491,6 +55583,7 @@ function select_node(gobj, node_id) {
|
|
|
55491
55583
|
graph.setElementState(node_id, ["selected"]);
|
|
55492
55584
|
} catch (e) {}
|
|
55493
55585
|
priv._selected_node_id = node_id;
|
|
55586
|
+
paint_selection(gobj, [node_id]);
|
|
55494
55587
|
show_resize_handles(gobj);
|
|
55495
55588
|
show_node_icon(gobj);
|
|
55496
55589
|
graph_draw(gobj).then(() => {
|
|
@@ -55506,12 +55599,17 @@ function deselect_node(gobj) {
|
|
|
55506
55599
|
hide_node_popover(gobj);
|
|
55507
55600
|
hide_delete_confirm(gobj);
|
|
55508
55601
|
hide_unlink_confirm(gobj);
|
|
55509
|
-
|
|
55602
|
+
let clearing = /* @__PURE__ */ new Set([...selected_node_ids(gobj), ...priv._selected_paint_ids || []]);
|
|
55603
|
+
if (priv._selected_node_id) clearing.add(priv._selected_node_id);
|
|
55604
|
+
if (clearing.size) {
|
|
55510
55605
|
history_pause(gobj);
|
|
55511
55606
|
try {
|
|
55512
|
-
|
|
55607
|
+
let states = {};
|
|
55608
|
+
for (let id of clearing) states[id] = [];
|
|
55609
|
+
graph.setElementState(states);
|
|
55513
55610
|
} catch (e) {}
|
|
55514
55611
|
priv._selected_node_id = null;
|
|
55612
|
+
paint_selection(gobj, []);
|
|
55515
55613
|
graph_draw(gobj).then(() => {
|
|
55516
55614
|
history_resume(gobj);
|
|
55517
55615
|
});
|
|
@@ -56798,33 +56896,36 @@ function refresh_minimap(gobj) {
|
|
|
56798
56896
|
* same three-way choice — it lived inline in the theme refresh and is
|
|
56799
56897
|
* shared now. Returns null for a node that carries no desc.
|
|
56800
56898
|
************************************************************/
|
|
56801
|
-
function node_innerHTML_of(nd, theme, highlight) {
|
|
56899
|
+
function node_innerHTML_of(nd, theme, highlight, selected) {
|
|
56802
56900
|
if (!nd || !nd.data || !nd.data.desc) return null;
|
|
56803
56901
|
let desc = nd.data.desc;
|
|
56804
56902
|
let record = nd.data.record || {};
|
|
56805
56903
|
let label = node_label(desc, record);
|
|
56806
56904
|
switch (desc.node_treedb_type) {
|
|
56807
|
-
case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight);
|
|
56808
|
-
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight);
|
|
56809
|
-
case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight);
|
|
56905
|
+
case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight, selected);
|
|
56906
|
+
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected);
|
|
56907
|
+
case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected);
|
|
56810
56908
|
}
|
|
56811
56909
|
return null;
|
|
56812
56910
|
}
|
|
56813
56911
|
/************************************************************
|
|
56814
|
-
* Repaint
|
|
56815
|
-
*
|
|
56816
|
-
*
|
|
56912
|
+
* Repaint these cards with the flags they carry RIGHT NOW.
|
|
56913
|
+
*
|
|
56914
|
+
* One place, because the flags are independent and each used to
|
|
56915
|
+
* be written by whoever repainted last: a find that repainted its
|
|
56916
|
+
* matches erased the selection ring off them, and a selection
|
|
56917
|
+
* repainted over a match erased the amber. Both are read here
|
|
56918
|
+
* from where they live.
|
|
56817
56919
|
************************************************************/
|
|
56818
|
-
function
|
|
56920
|
+
function repaint_cards(gobj, ids) {
|
|
56819
56921
|
let priv = gobj.priv;
|
|
56820
56922
|
let graph = priv.graph;
|
|
56821
|
-
if (!graph) return;
|
|
56822
|
-
let
|
|
56823
|
-
let
|
|
56824
|
-
if (touched.size === 0) return;
|
|
56923
|
+
if (!graph || !ids || !ids.size) return;
|
|
56924
|
+
let focus = new Set(priv._focus_ids || []);
|
|
56925
|
+
let selected = new Set(priv._selected_paint_ids || []);
|
|
56825
56926
|
let updates = [];
|
|
56826
|
-
for (let id of
|
|
56827
|
-
let html = node_innerHTML_of(graph.getNodeData(id), priv.theme,
|
|
56927
|
+
for (let id of ids) {
|
|
56928
|
+
let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, focus.has(id), selected.has(id));
|
|
56828
56929
|
if (html !== null) updates.push({
|
|
56829
56930
|
id,
|
|
56830
56931
|
style: { innerHTML: html }
|
|
@@ -56835,19 +56936,32 @@ function apply_node_highlight(gobj, prev_ids, next_ids) {
|
|
|
56835
56936
|
graph.updateNodeData(updates);
|
|
56836
56937
|
graph.draw();
|
|
56837
56938
|
} catch (e) {
|
|
56838
|
-
log_error(`${gobj_short_name(gobj)}: cannot repaint the
|
|
56939
|
+
log_error(`${gobj_short_name(gobj)}: cannot repaint the cards: ${e}`);
|
|
56839
56940
|
}
|
|
56840
56941
|
}
|
|
56942
|
+
/************************************************************
|
|
56943
|
+
* Repaint the cards whose highlight state CHANGES: the ones that had it
|
|
56944
|
+
* and lose it, plus the ones that gain it. Only those — a treedb graph
|
|
56945
|
+
* is redrawn per keystroke of the find box otherwise.
|
|
56946
|
+
************************************************************/
|
|
56947
|
+
function apply_node_highlight(gobj, prev_ids, next_ids) {
|
|
56948
|
+
if (!gobj.priv.graph) return;
|
|
56949
|
+
let next = new Set(next_ids || []);
|
|
56950
|
+
let touched = /* @__PURE__ */ new Set([...prev_ids || [], ...next]);
|
|
56951
|
+
if (touched.size === 0) return;
|
|
56952
|
+
repaint_cards(gobj, touched);
|
|
56953
|
+
}
|
|
56841
56954
|
function refresh_html_nodes_theme(gobj, theme) {
|
|
56842
56955
|
let priv = gobj.priv;
|
|
56843
56956
|
let graph = priv.graph;
|
|
56844
56957
|
if (!graph) return;
|
|
56845
56958
|
let nodes = graph.getData().nodes || [];
|
|
56846
56959
|
let highlighted = new Set(priv._focus_ids || []);
|
|
56960
|
+
let selected = new Set(priv._selected_paint_ids || []);
|
|
56847
56961
|
let updates = [];
|
|
56848
56962
|
for (let i = 0; i < nodes.length; i++) {
|
|
56849
56963
|
let id = nodes[i].id;
|
|
56850
|
-
let html = node_innerHTML_of(graph.getNodeData(id), theme, highlighted.has(id));
|
|
56964
|
+
let html = node_innerHTML_of(graph.getNodeData(id), theme, highlighted.has(id), selected.has(id));
|
|
56851
56965
|
if (html !== null) updates.push({
|
|
56852
56966
|
id,
|
|
56853
56967
|
style: { innerHTML: html }
|
|
@@ -56877,12 +56991,29 @@ function refresh_default_edges_theme(gobj, theme) {
|
|
|
56877
56991
|
if (updates.length > 0) graph.updateEdgeData(updates);
|
|
56878
56992
|
}
|
|
56879
56993
|
/************************************************************
|
|
56994
|
+
* The rings a card can wear, composed into one `box-shadow`.
|
|
56995
|
+
*
|
|
56996
|
+
* A node can be a find match and be selected at the same time, so
|
|
56997
|
+
* neither ring may be written by overwriting the other: the amber
|
|
56998
|
+
* halo hugs the card and the blue selection ring is drawn outside
|
|
56999
|
+
* it, which is also the order that reads correctly when only one
|
|
57000
|
+
* of them is on.
|
|
57001
|
+
************************************************************/
|
|
57002
|
+
function ring_shadow(highlight, selected, base_shadow) {
|
|
57003
|
+
let rings = [];
|
|
57004
|
+
if (highlight) rings.push(`0 0 0 4px ${HIGHLIGHT_HALO}`);
|
|
57005
|
+
if (selected) rings.push(`0 0 0 ${highlight ? "7px" : "3px"} ${SELECT_RING}`);
|
|
57006
|
+
if (base_shadow) rings.push(base_shadow);
|
|
57007
|
+
if (!rings.length) return "";
|
|
57008
|
+
return `box-shadow: ${rings.join(", ")};`;
|
|
57009
|
+
}
|
|
57010
|
+
/************************************************************
|
|
56880
57011
|
* Build innerHTML for pure-child (leaf) nodes: a compact
|
|
56881
57012
|
* chip-card. Same colour/typography family as the entity card
|
|
56882
57013
|
* but lighter (1px border, no shadow, single line). The name is
|
|
56883
57014
|
* always legible (ellipsis + native title tooltip on overflow).
|
|
56884
57015
|
************************************************************/
|
|
56885
|
-
function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
57016
|
+
function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected) {
|
|
56886
57017
|
let title = key || label;
|
|
56887
57018
|
let dark = theme === "dark";
|
|
56888
57019
|
let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
|
|
@@ -56901,7 +57032,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
|
56901
57032
|
height: 100%;
|
|
56902
57033
|
background: ${bg};
|
|
56903
57034
|
border: ${highlight ? "3px" : "1px"} solid ${border};
|
|
56904
|
-
${highlight
|
|
57035
|
+
${ring_shadow(highlight, selected, "")}
|
|
56905
57036
|
border-radius: 8px;
|
|
56906
57037
|
color: ${text_color};
|
|
56907
57038
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
@@ -56927,7 +57058,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
|
56927
57058
|
* colour is kept (per-topic differentiation) but softened via
|
|
56928
57059
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
56929
57060
|
************************************************************/
|
|
56930
|
-
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight) {
|
|
57061
|
+
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected) {
|
|
56931
57062
|
let title = key || label;
|
|
56932
57063
|
let dark = theme === "dark";
|
|
56933
57064
|
let surface = dark ? "#1b2230" : "#ffffff";
|
|
@@ -56969,7 +57100,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
|
|
|
56969
57100
|
background: ${bg};
|
|
56970
57101
|
border: ${highlight ? "3px" : "1.5px"} ${border_style} ${border};
|
|
56971
57102
|
border-radius: 10px;
|
|
56972
|
-
|
|
57103
|
+
${ring_shadow(highlight, selected, shadow)}
|
|
56973
57104
|
color: ${title_color};
|
|
56974
57105
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
56975
57106
|
display: flex;
|
|
@@ -57890,6 +58021,17 @@ function ac_node_drag_end(gobj, event, kw, src) {
|
|
|
57890
58021
|
return 0;
|
|
57891
58022
|
}
|
|
57892
58023
|
/************************************************************
|
|
58024
|
+
* The rubber band let go: these are the nodes it enclosed.
|
|
58025
|
+
*
|
|
58026
|
+
* They arrive in the kw because `onSelect` runs BEFORE G6 writes
|
|
58027
|
+
* the state, so asking the graph here would answer the previous
|
|
58028
|
+
* selection.
|
|
58029
|
+
************************************************************/
|
|
58030
|
+
function ac_brush_select(gobj, event, kw, src) {
|
|
58031
|
+
set_selection(gobj, kw && kw.ids || []);
|
|
58032
|
+
return 0;
|
|
58033
|
+
}
|
|
58034
|
+
/************************************************************
|
|
57893
58035
|
* Node click - publish vertex clicked event
|
|
57894
58036
|
************************************************************/
|
|
57895
58037
|
function ac_node_click(gobj, event, kw, src) {
|
|
@@ -57904,7 +58046,8 @@ function ac_node_click(gobj, event, kw, src) {
|
|
|
57904
58046
|
topic_name: nodedata.data.desc.topic_name,
|
|
57905
58047
|
record: nodedata.data.record
|
|
57906
58048
|
});
|
|
57907
|
-
if (priv.edit_mode)
|
|
58049
|
+
if (priv.edit_mode && kw.evt.shiftKey) toggle_in_selection(gobj, node_id);
|
|
58050
|
+
else if (priv.edit_mode) {
|
|
57908
58051
|
let containerRect = priv.$container.getBoundingClientRect();
|
|
57909
58052
|
let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
|
|
57910
58053
|
let port_key = detect_port_click(gobj, node_id, canvasPoint[0], canvasPoint[1]);
|
|
@@ -58131,6 +58274,11 @@ function create_gclass$1(gclass_name) {
|
|
|
58131
58274
|
ac_node_drag_end,
|
|
58132
58275
|
null
|
|
58133
58276
|
],
|
|
58277
|
+
[
|
|
58278
|
+
"EV_BRUSH_SELECT",
|
|
58279
|
+
ac_brush_select,
|
|
58280
|
+
null
|
|
58281
|
+
],
|
|
58134
58282
|
[
|
|
58135
58283
|
"EV_ZOOM_IN",
|
|
58136
58284
|
ac_zoom_in,
|
|
@@ -58239,6 +58387,7 @@ function create_gclass$1(gclass_name) {
|
|
|
58239
58387
|
["EV_NODE_CONTEXT_MENU", 0],
|
|
58240
58388
|
["EV_CANVAS_CLICK", 0],
|
|
58241
58389
|
["EV_NODE_DRAG_END", 0],
|
|
58390
|
+
["EV_BRUSH_SELECT", 0],
|
|
58242
58391
|
["EV_ZOOM_IN", 0],
|
|
58243
58392
|
["EV_ZOOM_OUT", 0],
|
|
58244
58393
|
["EV_ZOOM_RESET", 0],
|
package/package.json
CHANGED
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -184,6 +184,15 @@ const GCLASS_NAME = "C_G6_NODES_TREE";
|
|
|
184
184
|
const HIGHLIGHT_COLOR = "#f0a020";
|
|
185
185
|
const HIGHLIGHT_HALO = "rgba(240,160,32,0.35)";
|
|
186
186
|
|
|
187
|
+
/*
|
|
188
|
+
* The selection ring. Deliberately NOT the amber of the highlight:
|
|
189
|
+
* a node can be a find match AND be selected, and two amber rings
|
|
190
|
+
* would say nothing about either. Blue is what a selection is in
|
|
191
|
+
* every editor, and the ring is drawn OUTSIDE the highlight's halo
|
|
192
|
+
* so the two compose instead of overwriting one another.
|
|
193
|
+
*/
|
|
194
|
+
const SELECT_RING = "rgba(59,130,246,0.95)";
|
|
195
|
+
|
|
187
196
|
/***************************************************************
|
|
188
197
|
* Internal layout and operation mode definitions
|
|
189
198
|
***************************************************************/
|
|
@@ -324,6 +333,10 @@ let PRIVATE_DATA = {
|
|
|
324
333
|
_link_saved_styles: [], // saved port styles to restore on cancel
|
|
325
334
|
_focus_topic: null, // topic currently focused (EV_FOCUS_TOPIC)
|
|
326
335
|
_focus_ids: [], // node ids carrying the focus 'active' state
|
|
336
|
+
_selected_paint_ids: [], // node ids whose card is PAINTED selected
|
|
337
|
+
// (G6's 'selected' state is the selection
|
|
338
|
+
// itself; this is what is on screen, and
|
|
339
|
+
// it exists to diff the repaint)
|
|
327
340
|
_pending_focus_topic: null, // focus requested before data was loaded
|
|
328
341
|
_pending_find: null, // find requested before data was loaded
|
|
329
342
|
_layout_asked: "", // layout the host asked for at create (see mt_create)
|
|
@@ -1182,9 +1195,46 @@ function configure_behaviour(gobj)
|
|
|
1182
1195
|
case "edition":
|
|
1183
1196
|
priv.edit_mode = true;
|
|
1184
1197
|
behaviors = [
|
|
1185
|
-
|
|
1198
|
+
/* Panning gives way while Shift is held: that is the
|
|
1199
|
+
* marquee's gesture, and G6 binds drag-canvas straight
|
|
1200
|
+
* to the drag events, so without this the canvas pans
|
|
1201
|
+
* under the rubber band. */
|
|
1202
|
+
{
|
|
1203
|
+
type: "drag-canvas",
|
|
1204
|
+
key: "drag-canvas",
|
|
1205
|
+
enable: (event) => !event.shiftKey,
|
|
1206
|
+
},
|
|
1186
1207
|
"zoom-canvas",
|
|
1208
|
+
/* Moves EVERY node in the `selected` state, not just
|
|
1209
|
+
* the one under the pointer, and wraps the whole move
|
|
1210
|
+
* in one history batch -- so a group move is one drag
|
|
1211
|
+
* and one undo. Nothing to configure: `selected` is
|
|
1212
|
+
* already its default `state`. */
|
|
1187
1213
|
"drag-element",
|
|
1214
|
+
/* Shift+drag on the canvas: the rubber band. The
|
|
1215
|
+
* GESTURE is G6's, its RESULT enters the machine --
|
|
1216
|
+
* `onSelect` fires before G6 writes the state, so the
|
|
1217
|
+
* ids travel with the event and the action does not
|
|
1218
|
+
* have to race it. */
|
|
1219
|
+
{
|
|
1220
|
+
type: "brush-select",
|
|
1221
|
+
key: "brush-select",
|
|
1222
|
+
trigger: ["shift"],
|
|
1223
|
+
enableElements: ["node"],
|
|
1224
|
+
/* Left at G6's default (false): the set is read at
|
|
1225
|
+
* pointerup, not on every pointermove. Each answer
|
|
1226
|
+
* repaints the cards it touches, and doing that per
|
|
1227
|
+
* frame of a rubber band over a hundred nodes is
|
|
1228
|
+
* paid for nothing -- the band already shows what
|
|
1229
|
+
* it covers. */
|
|
1230
|
+
immediately: false,
|
|
1231
|
+
onSelect: (states) => {
|
|
1232
|
+
let ids = Object.keys(states || {}).filter(
|
|
1233
|
+
(id) => (states[id] || []).includes("selected")
|
|
1234
|
+
);
|
|
1235
|
+
gobj_send_event(gobj, "EV_BRUSH_SELECT", {ids: ids}, gobj);
|
|
1236
|
+
},
|
|
1237
|
+
},
|
|
1188
1238
|
];
|
|
1189
1239
|
break;
|
|
1190
1240
|
case "operation":
|
|
@@ -2948,6 +2998,126 @@ function perform_history_op(gobj, is_redo)
|
|
|
2948
2998
|
}
|
|
2949
2999
|
}
|
|
2950
3000
|
|
|
3001
|
+
/************************************************************
|
|
3002
|
+
* The selection.
|
|
3003
|
+
*
|
|
3004
|
+
* G6's `selected` element state IS the selection: `drag-element`
|
|
3005
|
+
* reads it (`getElementDataByState`) to decide what a drag moves,
|
|
3006
|
+
* so keeping the set anywhere else would be a second truth the
|
|
3007
|
+
* drag does not consult. What this gclass keeps is what is
|
|
3008
|
+
* PAINTED -- an html node draws no state style at all (its key
|
|
3009
|
+
* shape is a DOM element), so the ring lives in the card's own
|
|
3010
|
+
* html, exactly like the find highlight, and the painted set
|
|
3011
|
+
* exists to diff the repaint.
|
|
3012
|
+
************************************************************/
|
|
3013
|
+
function selected_node_ids(gobj)
|
|
3014
|
+
{
|
|
3015
|
+
let graph = gobj.priv.graph;
|
|
3016
|
+
|
|
3017
|
+
if(!graph) {
|
|
3018
|
+
return [];
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
let data;
|
|
3022
|
+
try {
|
|
3023
|
+
data = graph.getElementDataByState('node', 'selected') || [];
|
|
3024
|
+
} catch(e) {
|
|
3025
|
+
return []; /* asked before there is a graph to ask */
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
return data.map((nd) => nd.id);
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
/************************************************************
|
|
3032
|
+
* Move the ring to these nodes and off the ones that had it.
|
|
3033
|
+
************************************************************/
|
|
3034
|
+
function paint_selection(gobj, ids)
|
|
3035
|
+
{
|
|
3036
|
+
let priv = gobj.priv;
|
|
3037
|
+
let prev = priv._selected_paint_ids || [];
|
|
3038
|
+
let next = ids || [];
|
|
3039
|
+
|
|
3040
|
+
priv._selected_paint_ids = next;
|
|
3041
|
+
|
|
3042
|
+
repaint_cards(gobj, new Set([...prev, ...next]));
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
/************************************************************
|
|
3046
|
+
* Select a SET of nodes: what a marquee and a shift-click do.
|
|
3047
|
+
*
|
|
3048
|
+
* It leaves `_selected_node_id` null even for a set of one,
|
|
3049
|
+
* because that field is not "the selection", it is "the node
|
|
3050
|
+
* opened for editing" -- the resize handles, the ports and the
|
|
3051
|
+
* popovers hang off it, and none of them means anything over a
|
|
3052
|
+
* set. Clicking a node is what opens one (`select_node`); a
|
|
3053
|
+
* marquee selects, it does not open.
|
|
3054
|
+
************************************************************/
|
|
3055
|
+
function set_selection(gobj, ids)
|
|
3056
|
+
{
|
|
3057
|
+
let priv = gobj.priv;
|
|
3058
|
+
let graph = priv.graph;
|
|
3059
|
+
|
|
3060
|
+
let next = [];
|
|
3061
|
+
for(let id of (ids || [])) {
|
|
3062
|
+
try {
|
|
3063
|
+
if(graph.getNodeData(id)) {
|
|
3064
|
+
next.push(id);
|
|
3065
|
+
}
|
|
3066
|
+
} catch(e) {
|
|
3067
|
+
/* An id the graph does not have: a brush answers with what
|
|
3068
|
+
* it enclosed, and a node can be gone by the time we ask. */
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
deselect_node(gobj); /* the state, the paint and the affordances */
|
|
3073
|
+
|
|
3074
|
+
if(!next.length) {
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
history_pause(gobj);
|
|
3079
|
+
try {
|
|
3080
|
+
let states = {};
|
|
3081
|
+
for(let id of next) {
|
|
3082
|
+
states[id] = ['selected'];
|
|
3083
|
+
}
|
|
3084
|
+
graph.setElementState(states);
|
|
3085
|
+
} catch(e) {
|
|
3086
|
+
log_error(`${gobj_short_name(gobj)}: cannot set the selection: ${e}`);
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
paint_selection(gobj, next);
|
|
3090
|
+
|
|
3091
|
+
graph_draw(gobj).then(() => {
|
|
3092
|
+
history_resume(gobj);
|
|
3093
|
+
});
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
/************************************************************
|
|
3097
|
+
* Add a node to the selection, or take it out of it.
|
|
3098
|
+
************************************************************/
|
|
3099
|
+
function toggle_in_selection(gobj, node_id)
|
|
3100
|
+
{
|
|
3101
|
+
let priv = gobj.priv;
|
|
3102
|
+
|
|
3103
|
+
/* The union of the two views of the same thing. A brush clears
|
|
3104
|
+
* G6's state on pointerdown without telling anybody, so reading
|
|
3105
|
+
* only the state could drop a card that is on screen wearing a
|
|
3106
|
+
* ring. */
|
|
3107
|
+
let current = new Set(selected_node_ids(gobj));
|
|
3108
|
+
for(let id of (priv._selected_paint_ids || [])) {
|
|
3109
|
+
current.add(id);
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
if(current.has(node_id)) {
|
|
3113
|
+
current.delete(node_id);
|
|
3114
|
+
} else {
|
|
3115
|
+
current.add(node_id);
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
set_selection(gobj, [...current]);
|
|
3119
|
+
}
|
|
3120
|
+
|
|
2951
3121
|
function select_node(gobj, node_id)
|
|
2952
3122
|
{
|
|
2953
3123
|
let priv = gobj.priv;
|
|
@@ -2962,6 +3132,7 @@ function select_node(gobj, node_id)
|
|
|
2962
3132
|
graph.setElementState(node_id, ['selected']);
|
|
2963
3133
|
} catch(e) {}
|
|
2964
3134
|
priv._selected_node_id = node_id;
|
|
3135
|
+
paint_selection(gobj, [node_id]);
|
|
2965
3136
|
|
|
2966
3137
|
// Show resize handles and properties icon
|
|
2967
3138
|
show_resize_handles(gobj);
|
|
@@ -2984,12 +3155,28 @@ function deselect_node(gobj)
|
|
|
2984
3155
|
hide_delete_confirm(gobj);
|
|
2985
3156
|
hide_unlink_confirm(gobj);
|
|
2986
3157
|
|
|
3158
|
+
/* Both views of the selection, because either can hold an id the
|
|
3159
|
+
* other lost: G6's state is what a drag moves, the painted set is
|
|
3160
|
+
* what the reader can see. */
|
|
3161
|
+
let clearing = new Set([
|
|
3162
|
+
...selected_node_ids(gobj),
|
|
3163
|
+
...(priv._selected_paint_ids || [])
|
|
3164
|
+
]);
|
|
2987
3165
|
if(priv._selected_node_id) {
|
|
3166
|
+
clearing.add(priv._selected_node_id);
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
if(clearing.size) {
|
|
2988
3170
|
history_pause(gobj);
|
|
2989
3171
|
try {
|
|
2990
|
-
|
|
3172
|
+
let states = {};
|
|
3173
|
+
for(let id of clearing) {
|
|
3174
|
+
states[id] = [];
|
|
3175
|
+
}
|
|
3176
|
+
graph.setElementState(states);
|
|
2991
3177
|
} catch(e) {}
|
|
2992
3178
|
priv._selected_node_id = null;
|
|
3179
|
+
paint_selection(gobj, []);
|
|
2993
3180
|
|
|
2994
3181
|
graph_draw(gobj).then(() => {
|
|
2995
3182
|
history_resume(gobj);
|
|
@@ -4793,7 +4980,7 @@ function refresh_minimap(gobj)
|
|
|
4793
4980
|
* same three-way choice — it lived inline in the theme refresh and is
|
|
4794
4981
|
* shared now. Returns null for a node that carries no desc.
|
|
4795
4982
|
************************************************************/
|
|
4796
|
-
function node_innerHTML_of(nd, theme, highlight)
|
|
4983
|
+
function node_innerHTML_of(nd, theme, highlight, selected)
|
|
4797
4984
|
{
|
|
4798
4985
|
if(!nd || !nd.data || !nd.data.desc) {
|
|
4799
4986
|
return null;
|
|
@@ -4805,45 +4992,49 @@ function node_innerHTML_of(nd, theme, highlight)
|
|
|
4805
4992
|
switch(desc.node_treedb_type) {
|
|
4806
4993
|
case 'child':
|
|
4807
4994
|
return build_chip_innerHTML(
|
|
4808
|
-
desc.color, theme, record.icon, label, record.id,
|
|
4995
|
+
desc.color, theme, record.icon, label, record.id,
|
|
4996
|
+
highlight, selected
|
|
4809
4997
|
);
|
|
4810
4998
|
case 'extended':
|
|
4811
4999
|
return build_node_innerHTML(
|
|
4812
5000
|
desc.color, theme, record.icon, label,
|
|
4813
|
-
desc.topic_name, true, record.id, highlight
|
|
5001
|
+
desc.topic_name, true, record.id, highlight, selected
|
|
4814
5002
|
);
|
|
4815
5003
|
case 'hierarchical':
|
|
4816
5004
|
return build_node_innerHTML(
|
|
4817
5005
|
desc.color, theme, record.icon, label,
|
|
4818
|
-
desc.topic_name, false, record.id, highlight
|
|
5006
|
+
desc.topic_name, false, record.id, highlight, selected
|
|
4819
5007
|
);
|
|
4820
5008
|
}
|
|
4821
5009
|
return null;
|
|
4822
5010
|
}
|
|
4823
5011
|
|
|
4824
5012
|
/************************************************************
|
|
4825
|
-
* Repaint
|
|
4826
|
-
*
|
|
4827
|
-
*
|
|
5013
|
+
* Repaint these cards with the flags they carry RIGHT NOW.
|
|
5014
|
+
*
|
|
5015
|
+
* One place, because the flags are independent and each used to
|
|
5016
|
+
* be written by whoever repainted last: a find that repainted its
|
|
5017
|
+
* matches erased the selection ring off them, and a selection
|
|
5018
|
+
* repainted over a match erased the amber. Both are read here
|
|
5019
|
+
* from where they live.
|
|
4828
5020
|
************************************************************/
|
|
4829
|
-
function
|
|
5021
|
+
function repaint_cards(gobj, ids)
|
|
4830
5022
|
{
|
|
4831
5023
|
let priv = gobj.priv;
|
|
4832
5024
|
let graph = priv.graph;
|
|
4833
|
-
if(!graph) {
|
|
4834
|
-
return;
|
|
4835
|
-
}
|
|
4836
5025
|
|
|
4837
|
-
|
|
4838
|
-
let touched = new Set([...(prev_ids || []), ...next]);
|
|
4839
|
-
if(touched.size === 0) {
|
|
5026
|
+
if(!graph || !ids || !ids.size) {
|
|
4840
5027
|
return;
|
|
4841
5028
|
}
|
|
4842
5029
|
|
|
5030
|
+
let focus = new Set(priv._focus_ids || []);
|
|
5031
|
+
let selected = new Set(priv._selected_paint_ids || []);
|
|
5032
|
+
|
|
4843
5033
|
let updates = [];
|
|
4844
|
-
for(let id of
|
|
4845
|
-
let
|
|
4846
|
-
|
|
5034
|
+
for(let id of ids) {
|
|
5035
|
+
let html = node_innerHTML_of(
|
|
5036
|
+
graph.getNodeData(id), priv.theme, focus.has(id), selected.has(id)
|
|
5037
|
+
);
|
|
4847
5038
|
if(html !== null) {
|
|
4848
5039
|
updates.push({id: id, style: {innerHTML: html}});
|
|
4849
5040
|
}
|
|
@@ -4851,14 +5042,37 @@ function apply_node_highlight(gobj, prev_ids, next_ids)
|
|
|
4851
5042
|
if(!updates.length) {
|
|
4852
5043
|
return;
|
|
4853
5044
|
}
|
|
5045
|
+
|
|
4854
5046
|
try {
|
|
4855
5047
|
graph.updateNodeData(updates);
|
|
4856
5048
|
graph.draw();
|
|
4857
5049
|
} catch(e) {
|
|
4858
|
-
log_error(`${gobj_short_name(gobj)}: cannot repaint the
|
|
5050
|
+
log_error(`${gobj_short_name(gobj)}: cannot repaint the cards: ${e}`);
|
|
4859
5051
|
}
|
|
4860
5052
|
}
|
|
4861
5053
|
|
|
5054
|
+
/************************************************************
|
|
5055
|
+
* Repaint the cards whose highlight state CHANGES: the ones that had it
|
|
5056
|
+
* and lose it, plus the ones that gain it. Only those — a treedb graph
|
|
5057
|
+
* is redrawn per keystroke of the find box otherwise.
|
|
5058
|
+
************************************************************/
|
|
5059
|
+
function apply_node_highlight(gobj, prev_ids, next_ids)
|
|
5060
|
+
{
|
|
5061
|
+
let priv = gobj.priv;
|
|
5062
|
+
let graph = priv.graph;
|
|
5063
|
+
if(!graph) {
|
|
5064
|
+
return;
|
|
5065
|
+
}
|
|
5066
|
+
|
|
5067
|
+
let next = new Set(next_ids || []);
|
|
5068
|
+
let touched = new Set([...(prev_ids || []), ...next]);
|
|
5069
|
+
if(touched.size === 0) {
|
|
5070
|
+
return;
|
|
5071
|
+
}
|
|
5072
|
+
|
|
5073
|
+
repaint_cards(gobj, touched);
|
|
5074
|
+
}
|
|
5075
|
+
|
|
4862
5076
|
function refresh_html_nodes_theme(gobj, theme)
|
|
4863
5077
|
{
|
|
4864
5078
|
let priv = gobj.priv;
|
|
@@ -4871,10 +5085,13 @@ function refresh_html_nodes_theme(gobj, theme)
|
|
|
4871
5085
|
* rebuilt every card without it would silently CLEAR the focus or the
|
|
4872
5086
|
* find that is on screen. Carry it across. */
|
|
4873
5087
|
let highlighted = new Set(priv._focus_ids || []);
|
|
5088
|
+
let selected = new Set(priv._selected_paint_ids || []);
|
|
4874
5089
|
let updates = [];
|
|
4875
5090
|
for(let i = 0; i < nodes.length; i++) {
|
|
4876
5091
|
let id = nodes[i].id;
|
|
4877
|
-
let html = node_innerHTML_of(
|
|
5092
|
+
let html = node_innerHTML_of(
|
|
5093
|
+
graph.getNodeData(id), theme, highlighted.has(id), selected.has(id)
|
|
5094
|
+
);
|
|
4878
5095
|
if(html !== null) {
|
|
4879
5096
|
updates.push({id: id, style: {innerHTML: html}});
|
|
4880
5097
|
}
|
|
@@ -4913,13 +5130,42 @@ function refresh_default_edges_theme(gobj, theme)
|
|
|
4913
5130
|
}
|
|
4914
5131
|
}
|
|
4915
5132
|
|
|
5133
|
+
/************************************************************
|
|
5134
|
+
* The rings a card can wear, composed into one `box-shadow`.
|
|
5135
|
+
*
|
|
5136
|
+
* A node can be a find match and be selected at the same time, so
|
|
5137
|
+
* neither ring may be written by overwriting the other: the amber
|
|
5138
|
+
* halo hugs the card and the blue selection ring is drawn outside
|
|
5139
|
+
* it, which is also the order that reads correctly when only one
|
|
5140
|
+
* of them is on.
|
|
5141
|
+
************************************************************/
|
|
5142
|
+
function ring_shadow(highlight, selected, base_shadow)
|
|
5143
|
+
{
|
|
5144
|
+
let rings = [];
|
|
5145
|
+
|
|
5146
|
+
if(highlight) {
|
|
5147
|
+
rings.push(`0 0 0 4px ${HIGHLIGHT_HALO}`);
|
|
5148
|
+
}
|
|
5149
|
+
if(selected) {
|
|
5150
|
+
rings.push(`0 0 0 ${highlight? "7px" : "3px"} ${SELECT_RING}`);
|
|
5151
|
+
}
|
|
5152
|
+
if(base_shadow) {
|
|
5153
|
+
rings.push(base_shadow);
|
|
5154
|
+
}
|
|
5155
|
+
if(!rings.length) {
|
|
5156
|
+
return "";
|
|
5157
|
+
}
|
|
5158
|
+
|
|
5159
|
+
return `box-shadow: ${rings.join(", ")};`;
|
|
5160
|
+
}
|
|
5161
|
+
|
|
4916
5162
|
/************************************************************
|
|
4917
5163
|
* Build innerHTML for pure-child (leaf) nodes: a compact
|
|
4918
5164
|
* chip-card. Same colour/typography family as the entity card
|
|
4919
5165
|
* but lighter (1px border, no shadow, single line). The name is
|
|
4920
5166
|
* always legible (ellipsis + native title tooltip on overflow).
|
|
4921
5167
|
************************************************************/
|
|
4922
|
-
function build_chip_innerHTML(color, theme, icon, label, key, highlight)
|
|
5168
|
+
function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected)
|
|
4923
5169
|
{
|
|
4924
5170
|
let title = key || label;
|
|
4925
5171
|
let dark = (theme === "dark");
|
|
@@ -4950,7 +5196,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight)
|
|
|
4950
5196
|
height: 100%;
|
|
4951
5197
|
background: ${bg};
|
|
4952
5198
|
border: ${highlight? "3px" : "1px"} solid ${border};
|
|
4953
|
-
${highlight
|
|
5199
|
+
${ring_shadow(highlight, selected, "")}
|
|
4954
5200
|
border-radius: 8px;
|
|
4955
5201
|
color: ${text_color};
|
|
4956
5202
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
@@ -4977,7 +5223,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight)
|
|
|
4977
5223
|
* colour is kept (per-topic differentiation) but softened via
|
|
4978
5224
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
4979
5225
|
************************************************************/
|
|
4980
|
-
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight)
|
|
5226
|
+
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected)
|
|
4981
5227
|
{
|
|
4982
5228
|
let title = key || label;
|
|
4983
5229
|
let dark = (theme === "dark");
|
|
@@ -5040,7 +5286,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
|
|
|
5040
5286
|
background: ${bg};
|
|
5041
5287
|
border: ${highlight? "3px" : "1.5px"} ${border_style} ${border};
|
|
5042
5288
|
border-radius: 10px;
|
|
5043
|
-
|
|
5289
|
+
${ring_shadow(highlight, selected, shadow)}
|
|
5044
5290
|
color: ${title_color};
|
|
5045
5291
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
5046
5292
|
display: flex;
|
|
@@ -6477,6 +6723,20 @@ function ac_node_drag_end(gobj, event, kw, src)
|
|
|
6477
6723
|
return 0;
|
|
6478
6724
|
}
|
|
6479
6725
|
|
|
6726
|
+
/************************************************************
|
|
6727
|
+
* The rubber band let go: these are the nodes it enclosed.
|
|
6728
|
+
*
|
|
6729
|
+
* They arrive in the kw because `onSelect` runs BEFORE G6 writes
|
|
6730
|
+
* the state, so asking the graph here would answer the previous
|
|
6731
|
+
* selection.
|
|
6732
|
+
************************************************************/
|
|
6733
|
+
function ac_brush_select(gobj, event, kw, src)
|
|
6734
|
+
{
|
|
6735
|
+
set_selection(gobj, (kw && kw.ids) || []);
|
|
6736
|
+
|
|
6737
|
+
return 0;
|
|
6738
|
+
}
|
|
6739
|
+
|
|
6480
6740
|
/************************************************************
|
|
6481
6741
|
* Node click - publish vertex clicked event
|
|
6482
6742
|
************************************************************/
|
|
@@ -6495,7 +6755,13 @@ function ac_node_click(gobj, event, kw, src)
|
|
|
6495
6755
|
record: nodedata.data.record
|
|
6496
6756
|
});
|
|
6497
6757
|
|
|
6498
|
-
if(priv.edit_mode) {
|
|
6758
|
+
if(priv.edit_mode && kw.evt.shiftKey) {
|
|
6759
|
+
/* Shift+click extends the selection, the way it does
|
|
6760
|
+
* everywhere. It never looks for a port: a port is a
|
|
6761
|
+
* one-node affordance, and this gesture is about the
|
|
6762
|
+
* set. */
|
|
6763
|
+
toggle_in_selection(gobj, node_id);
|
|
6764
|
+
} else if(priv.edit_mode) {
|
|
6499
6765
|
// Check if click hits a port
|
|
6500
6766
|
// Convert client coords to viewport (container-relative) then to canvas
|
|
6501
6767
|
let containerRect = priv.$container.getBoundingClientRect();
|
|
@@ -6782,6 +7048,7 @@ function create_gclass(gclass_name)
|
|
|
6782
7048
|
["EV_NODE_CONTEXT_MENU", ac_node_context_menu, null],
|
|
6783
7049
|
["EV_CANVAS_CLICK", ac_canvas_click, null],
|
|
6784
7050
|
["EV_NODE_DRAG_END", ac_node_drag_end, null],
|
|
7051
|
+
["EV_BRUSH_SELECT", ac_brush_select, null],
|
|
6785
7052
|
|
|
6786
7053
|
/*--- Toolbar events ---*/
|
|
6787
7054
|
["EV_ZOOM_IN", ac_zoom_in, null],
|
|
@@ -6826,6 +7093,7 @@ function create_gclass(gclass_name)
|
|
|
6826
7093
|
["EV_NODE_CONTEXT_MENU", 0],
|
|
6827
7094
|
["EV_CANVAS_CLICK", 0],
|
|
6828
7095
|
["EV_NODE_DRAG_END", 0],
|
|
7096
|
+
["EV_BRUSH_SELECT", 0],
|
|
6829
7097
|
|
|
6830
7098
|
/*--- Toolbar (internal) ---*/
|
|
6831
7099
|
["EV_ZOOM_IN", 0],
|