@yuneta/gobj-ui 7.15.0 → 7.17.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 +69 -0
- package/dist/gobj-ui.cjs.js +310 -41
- package/dist/gobj-ui.es.js +310 -41
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +508 -62
package/README.md
CHANGED
|
@@ -708,6 +708,75 @@ 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
|
+
| **ctrl/cmd + A** | every node |
|
|
722
|
+
| drag any selected node | **moves the whole selection**, as one undo |
|
|
723
|
+
| **Delete** / **Backspace** | deletes the selection, after a confirmation that counts what it takes |
|
|
724
|
+
| **Esc**, or a click on the canvas | clears it |
|
|
725
|
+
|
|
726
|
+
The keys reach the graph only while the **graph has focus** — G6 gives its
|
|
727
|
+
canvas a `tabIndex` of its own — which is what keeps `ctrl+A` inside the find
|
|
728
|
+
box a selection of the TEXT and not of every node: the focus is in the input,
|
|
729
|
+
and the input is not inside the canvas. They arrive as `EV_KEY_DOWN` and the
|
|
730
|
+
action decides, so a key is as visible in the `machine` trace as a click.
|
|
731
|
+
|
|
732
|
+
Three decisions are worth knowing, because each one is where this could have
|
|
733
|
+
gone wrong:
|
|
734
|
+
|
|
735
|
+
- **G6's `selected` element state IS the selection.** `drag-element` decides
|
|
736
|
+
what a drag moves by asking the graph for it
|
|
737
|
+
(`getElementDataByState('node', 'selected')`), so a set kept anywhere else
|
|
738
|
+
would be a second truth the drag never consults — the ring would say five and
|
|
739
|
+
one would move. It also batches the move, so a group drag is one history
|
|
740
|
+
entry rather than one per node.
|
|
741
|
+
|
|
742
|
+
- **The ring is painted into the card's own html**, and it had to be. A state
|
|
743
|
+
style paints on a node's KEY SHAPE, and every node here is an `html` node
|
|
744
|
+
whose key shape is a DOM element — the same reason the amber highlight had
|
|
745
|
+
never appeared before `7.3.0`. Selecting with `brush-select` and nothing else
|
|
746
|
+
would have selected correctly and shown **nothing**. The ring is blue and
|
|
747
|
+
drawn OUTSIDE the amber halo, so a node that is both a find match and
|
|
748
|
+
selected wears both; one function composes them (`ring_shadow`), because
|
|
749
|
+
before it each repaint wrote its own flag and erased the other's.
|
|
750
|
+
|
|
751
|
+
- **The gesture is G6's, the result is an event.** `brush-select` gets an
|
|
752
|
+
`onSelect` that sends `EV_BRUSH_SELECT` with the ids, and the action does the
|
|
753
|
+
work — so a marquee shows up in the `machine` trace like every other action.
|
|
754
|
+
Shift+click is not G6's `click-select` at all: this gclass already owns
|
|
755
|
+
`EV_NODE_CLICK`, and adding a second selection owner outside the FSM is how
|
|
756
|
+
the two end up disagreeing.
|
|
757
|
+
|
|
758
|
+
Panning gives way while **Shift** is held (`drag-canvas` takes an `enable`
|
|
759
|
+
predicate), or the canvas would pan under the rubber band — G6 binds
|
|
760
|
+
`drag-canvas` straight to the drag events, and its own docs warn that the two
|
|
761
|
+
gestures cannot both be a plain drag.
|
|
762
|
+
|
|
763
|
+
**A delete says what it takes, whether it is one or twenty.** The Delete key
|
|
764
|
+
asks the same question the per-node delete icon asks, built by the same
|
|
765
|
+
function: the record's key when there is one, the count when there are more,
|
|
766
|
+
and then the two lines that are actually at stake — *N children will be
|
|
767
|
+
UNLINKED, not deleted* and *it will be detached from M parents* — where over a
|
|
768
|
+
set the numbers are the sums. These views delete with `force`, so an operator
|
|
769
|
+
pressing Delete over twelve cards has to read eleven detached children, not
|
|
770
|
+
"are you sure". It needs no new keys: the sentence is the one `7.10.0` already
|
|
771
|
+
defined. The question has no icon to hang off, so it is asked in the middle of
|
|
772
|
+
the graph it is about.
|
|
773
|
+
|
|
774
|
+
**A marquee selects, it does not open.** Even when it encloses exactly one
|
|
775
|
+
node, the handles and ports stay away: `_selected_node_id` means *the node
|
|
776
|
+
opened for editing*, and only a click sets it. Everything that hangs off a
|
|
777
|
+
single node reads that field, so a multiple selection puts all of it away by
|
|
778
|
+
construction rather than by a check in twenty places.
|
|
779
|
+
|
|
711
780
|
### Finding a node in the graph
|
|
712
781
|
|
|
713
782
|
`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: "",
|
|
@@ -54086,12 +54088,15 @@ function configure_events(gobj) {
|
|
|
54086
54088
|
update_toolbar(gobj);
|
|
54087
54089
|
};
|
|
54088
54090
|
i18next.default.on("languageChanged", priv._on_language_changed);
|
|
54089
|
-
|
|
54090
|
-
|
|
54091
|
-
|
|
54092
|
-
|
|
54093
|
-
|
|
54094
|
-
|
|
54091
|
+
graph.on("keydown", (evt) => {
|
|
54092
|
+
let ctrl = !!(evt.ctrlKey || evt.metaKey);
|
|
54093
|
+
if (ctrl && (evt.key === "a" || evt.key === "A")) {
|
|
54094
|
+
if (typeof evt.preventDefault === "function") evt.preventDefault();
|
|
54095
|
+
}
|
|
54096
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_KEY_DOWN", {
|
|
54097
|
+
key: evt.key,
|
|
54098
|
+
ctrl
|
|
54099
|
+
}, gobj);
|
|
54095
54100
|
});
|
|
54096
54101
|
}
|
|
54097
54102
|
/************************************************************
|
|
@@ -54378,9 +54383,24 @@ function configure_behaviour(gobj) {
|
|
|
54378
54383
|
case "edition":
|
|
54379
54384
|
priv.edit_mode = true;
|
|
54380
54385
|
behaviors = [
|
|
54381
|
-
|
|
54386
|
+
{
|
|
54387
|
+
type: "drag-canvas",
|
|
54388
|
+
key: "drag-canvas",
|
|
54389
|
+
enable: (event) => !event.shiftKey
|
|
54390
|
+
},
|
|
54382
54391
|
"zoom-canvas",
|
|
54383
|
-
"drag-element"
|
|
54392
|
+
"drag-element",
|
|
54393
|
+
{
|
|
54394
|
+
type: "brush-select",
|
|
54395
|
+
key: "brush-select",
|
|
54396
|
+
trigger: ["shift"],
|
|
54397
|
+
enableElements: ["node"],
|
|
54398
|
+
immediately: false,
|
|
54399
|
+
onSelect: (states) => {
|
|
54400
|
+
let ids = Object.keys(states || {}).filter((id) => (states[id] || []).includes("selected"));
|
|
54401
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_BRUSH_SELECT", { ids }, gobj);
|
|
54402
|
+
}
|
|
54403
|
+
}
|
|
54384
54404
|
];
|
|
54385
54405
|
break;
|
|
54386
54406
|
case "operation":
|
|
@@ -55453,10 +55473,17 @@ function create_form_button_row(parent, buttons) {
|
|
|
55453
55473
|
function show_confirm_popover(gobj, target_el, message, confirm_text, confirm_color, onConfirm, priv_key) {
|
|
55454
55474
|
hide_overlay(gobj, priv_key);
|
|
55455
55475
|
let priv = gobj.priv;
|
|
55456
|
-
if (!target_el) return;
|
|
55457
|
-
let iconRect = target_el.getBoundingClientRect();
|
|
55458
55476
|
let containerRect = priv.$container.getBoundingClientRect();
|
|
55459
|
-
|
|
55477
|
+
let left, top;
|
|
55478
|
+
if (target_el) {
|
|
55479
|
+
let iconRect = target_el.getBoundingClientRect();
|
|
55480
|
+
left = iconRect.right - containerRect.left + 6;
|
|
55481
|
+
top = iconRect.top - containerRect.top - 4;
|
|
55482
|
+
} else {
|
|
55483
|
+
left = Math.round(containerRect.width / 2) - 110;
|
|
55484
|
+
top = Math.round(containerRect.height / 4);
|
|
55485
|
+
}
|
|
55486
|
+
const popover = create_popover_base(left, top, "g6-confirm-popover", "#ff4d4f", 160);
|
|
55460
55487
|
let msg = document.createElement("div");
|
|
55461
55488
|
msg.style.cssText = "margin-bottom:10px;font-weight:500;white-space:pre-line;";
|
|
55462
55489
|
msg.textContent = message;
|
|
@@ -55495,6 +55522,81 @@ function perform_history_op(gobj, is_redo) {
|
|
|
55495
55522
|
sync_history_to_backend(gobj, cmd ? cmd.original : null);
|
|
55496
55523
|
}
|
|
55497
55524
|
}
|
|
55525
|
+
/************************************************************
|
|
55526
|
+
* The selection.
|
|
55527
|
+
*
|
|
55528
|
+
* G6's `selected` element state IS the selection: `drag-element`
|
|
55529
|
+
* reads it (`getElementDataByState`) to decide what a drag moves,
|
|
55530
|
+
* so keeping the set anywhere else would be a second truth the
|
|
55531
|
+
* drag does not consult. What this gclass keeps is what is
|
|
55532
|
+
* PAINTED -- an html node draws no state style at all (its key
|
|
55533
|
+
* shape is a DOM element), so the ring lives in the card's own
|
|
55534
|
+
* html, exactly like the find highlight, and the painted set
|
|
55535
|
+
* exists to diff the repaint.
|
|
55536
|
+
************************************************************/
|
|
55537
|
+
function selected_node_ids(gobj) {
|
|
55538
|
+
let graph = gobj.priv.graph;
|
|
55539
|
+
if (!graph) return [];
|
|
55540
|
+
let data;
|
|
55541
|
+
try {
|
|
55542
|
+
data = graph.getElementDataByState("node", "selected") || [];
|
|
55543
|
+
} catch (e) {
|
|
55544
|
+
return [];
|
|
55545
|
+
}
|
|
55546
|
+
return data.map((nd) => nd.id);
|
|
55547
|
+
}
|
|
55548
|
+
/************************************************************
|
|
55549
|
+
* Move the ring to these nodes and off the ones that had it.
|
|
55550
|
+
************************************************************/
|
|
55551
|
+
function paint_selection(gobj, ids) {
|
|
55552
|
+
let priv = gobj.priv;
|
|
55553
|
+
let prev = priv._selected_paint_ids || [];
|
|
55554
|
+
let next = ids || [];
|
|
55555
|
+
priv._selected_paint_ids = next;
|
|
55556
|
+
repaint_cards(gobj, /* @__PURE__ */ new Set([...prev, ...next]));
|
|
55557
|
+
}
|
|
55558
|
+
/************************************************************
|
|
55559
|
+
* Select a SET of nodes: what a marquee and a shift-click do.
|
|
55560
|
+
*
|
|
55561
|
+
* It leaves `_selected_node_id` null even for a set of one,
|
|
55562
|
+
* because that field is not "the selection", it is "the node
|
|
55563
|
+
* opened for editing" -- the resize handles, the ports and the
|
|
55564
|
+
* popovers hang off it, and none of them means anything over a
|
|
55565
|
+
* set. Clicking a node is what opens one (`select_node`); a
|
|
55566
|
+
* marquee selects, it does not open.
|
|
55567
|
+
************************************************************/
|
|
55568
|
+
function set_selection(gobj, ids) {
|
|
55569
|
+
let graph = gobj.priv.graph;
|
|
55570
|
+
let next = [];
|
|
55571
|
+
for (let id of ids || []) try {
|
|
55572
|
+
if (graph.getNodeData(id)) next.push(id);
|
|
55573
|
+
} catch (e) {}
|
|
55574
|
+
deselect_node(gobj);
|
|
55575
|
+
if (!next.length) return;
|
|
55576
|
+
history_pause(gobj);
|
|
55577
|
+
try {
|
|
55578
|
+
let states = {};
|
|
55579
|
+
for (let id of next) states[id] = ["selected"];
|
|
55580
|
+
graph.setElementState(states);
|
|
55581
|
+
} catch (e) {
|
|
55582
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot set the selection: ${e}`);
|
|
55583
|
+
}
|
|
55584
|
+
paint_selection(gobj, next);
|
|
55585
|
+
graph_draw(gobj).then(() => {
|
|
55586
|
+
history_resume(gobj);
|
|
55587
|
+
});
|
|
55588
|
+
}
|
|
55589
|
+
/************************************************************
|
|
55590
|
+
* Add a node to the selection, or take it out of it.
|
|
55591
|
+
************************************************************/
|
|
55592
|
+
function toggle_in_selection(gobj, node_id) {
|
|
55593
|
+
let priv = gobj.priv;
|
|
55594
|
+
let current = new Set(selected_node_ids(gobj));
|
|
55595
|
+
for (let id of priv._selected_paint_ids || []) current.add(id);
|
|
55596
|
+
if (current.has(node_id)) current.delete(node_id);
|
|
55597
|
+
else current.add(node_id);
|
|
55598
|
+
set_selection(gobj, [...current]);
|
|
55599
|
+
}
|
|
55498
55600
|
function select_node(gobj, node_id) {
|
|
55499
55601
|
let priv = gobj.priv;
|
|
55500
55602
|
let graph = priv.graph;
|
|
@@ -55504,6 +55606,7 @@ function select_node(gobj, node_id) {
|
|
|
55504
55606
|
graph.setElementState(node_id, ["selected"]);
|
|
55505
55607
|
} catch (e) {}
|
|
55506
55608
|
priv._selected_node_id = node_id;
|
|
55609
|
+
paint_selection(gobj, [node_id]);
|
|
55507
55610
|
show_resize_handles(gobj);
|
|
55508
55611
|
show_node_icon(gobj);
|
|
55509
55612
|
graph_draw(gobj).then(() => {
|
|
@@ -55519,12 +55622,17 @@ function deselect_node(gobj) {
|
|
|
55519
55622
|
hide_node_popover(gobj);
|
|
55520
55623
|
hide_delete_confirm(gobj);
|
|
55521
55624
|
hide_unlink_confirm(gobj);
|
|
55522
|
-
|
|
55625
|
+
let clearing = /* @__PURE__ */ new Set([...selected_node_ids(gobj), ...priv._selected_paint_ids || []]);
|
|
55626
|
+
if (priv._selected_node_id) clearing.add(priv._selected_node_id);
|
|
55627
|
+
if (clearing.size) {
|
|
55523
55628
|
history_pause(gobj);
|
|
55524
55629
|
try {
|
|
55525
|
-
|
|
55630
|
+
let states = {};
|
|
55631
|
+
for (let id of clearing) states[id] = [];
|
|
55632
|
+
graph.setElementState(states);
|
|
55526
55633
|
} catch (e) {}
|
|
55527
55634
|
priv._selected_node_id = null;
|
|
55635
|
+
paint_selection(gobj, []);
|
|
55528
55636
|
graph_draw(gobj).then(() => {
|
|
55529
55637
|
history_resume(gobj);
|
|
55530
55638
|
});
|
|
@@ -56811,33 +56919,36 @@ function refresh_minimap(gobj) {
|
|
|
56811
56919
|
* same three-way choice — it lived inline in the theme refresh and is
|
|
56812
56920
|
* shared now. Returns null for a node that carries no desc.
|
|
56813
56921
|
************************************************************/
|
|
56814
|
-
function node_innerHTML_of(nd, theme, highlight) {
|
|
56922
|
+
function node_innerHTML_of(nd, theme, highlight, selected) {
|
|
56815
56923
|
if (!nd || !nd.data || !nd.data.desc) return null;
|
|
56816
56924
|
let desc = nd.data.desc;
|
|
56817
56925
|
let record = nd.data.record || {};
|
|
56818
56926
|
let label = node_label(desc, record);
|
|
56819
56927
|
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);
|
|
56928
|
+
case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight, selected);
|
|
56929
|
+
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected);
|
|
56930
|
+
case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected);
|
|
56823
56931
|
}
|
|
56824
56932
|
return null;
|
|
56825
56933
|
}
|
|
56826
56934
|
/************************************************************
|
|
56827
|
-
* Repaint
|
|
56828
|
-
*
|
|
56829
|
-
*
|
|
56935
|
+
* Repaint these cards with the flags they carry RIGHT NOW.
|
|
56936
|
+
*
|
|
56937
|
+
* One place, because the flags are independent and each used to
|
|
56938
|
+
* be written by whoever repainted last: a find that repainted its
|
|
56939
|
+
* matches erased the selection ring off them, and a selection
|
|
56940
|
+
* repainted over a match erased the amber. Both are read here
|
|
56941
|
+
* from where they live.
|
|
56830
56942
|
************************************************************/
|
|
56831
|
-
function
|
|
56943
|
+
function repaint_cards(gobj, ids) {
|
|
56832
56944
|
let priv = gobj.priv;
|
|
56833
56945
|
let graph = priv.graph;
|
|
56834
|
-
if (!graph) return;
|
|
56835
|
-
let
|
|
56836
|
-
let
|
|
56837
|
-
if (touched.size === 0) return;
|
|
56946
|
+
if (!graph || !ids || !ids.size) return;
|
|
56947
|
+
let focus = new Set(priv._focus_ids || []);
|
|
56948
|
+
let selected = new Set(priv._selected_paint_ids || []);
|
|
56838
56949
|
let updates = [];
|
|
56839
|
-
for (let id of
|
|
56840
|
-
let html = node_innerHTML_of(graph.getNodeData(id), priv.theme,
|
|
56950
|
+
for (let id of ids) {
|
|
56951
|
+
let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, focus.has(id), selected.has(id));
|
|
56841
56952
|
if (html !== null) updates.push({
|
|
56842
56953
|
id,
|
|
56843
56954
|
style: { innerHTML: html }
|
|
@@ -56848,19 +56959,32 @@ function apply_node_highlight(gobj, prev_ids, next_ids) {
|
|
|
56848
56959
|
graph.updateNodeData(updates);
|
|
56849
56960
|
graph.draw();
|
|
56850
56961
|
} catch (e) {
|
|
56851
|
-
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot repaint the
|
|
56962
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot repaint the cards: ${e}`);
|
|
56852
56963
|
}
|
|
56853
56964
|
}
|
|
56965
|
+
/************************************************************
|
|
56966
|
+
* Repaint the cards whose highlight state CHANGES: the ones that had it
|
|
56967
|
+
* and lose it, plus the ones that gain it. Only those — a treedb graph
|
|
56968
|
+
* is redrawn per keystroke of the find box otherwise.
|
|
56969
|
+
************************************************************/
|
|
56970
|
+
function apply_node_highlight(gobj, prev_ids, next_ids) {
|
|
56971
|
+
if (!gobj.priv.graph) return;
|
|
56972
|
+
let next = new Set(next_ids || []);
|
|
56973
|
+
let touched = /* @__PURE__ */ new Set([...prev_ids || [], ...next]);
|
|
56974
|
+
if (touched.size === 0) return;
|
|
56975
|
+
repaint_cards(gobj, touched);
|
|
56976
|
+
}
|
|
56854
56977
|
function refresh_html_nodes_theme(gobj, theme) {
|
|
56855
56978
|
let priv = gobj.priv;
|
|
56856
56979
|
let graph = priv.graph;
|
|
56857
56980
|
if (!graph) return;
|
|
56858
56981
|
let nodes = graph.getData().nodes || [];
|
|
56859
56982
|
let highlighted = new Set(priv._focus_ids || []);
|
|
56983
|
+
let selected = new Set(priv._selected_paint_ids || []);
|
|
56860
56984
|
let updates = [];
|
|
56861
56985
|
for (let i = 0; i < nodes.length; i++) {
|
|
56862
56986
|
let id = nodes[i].id;
|
|
56863
|
-
let html = node_innerHTML_of(graph.getNodeData(id), theme, highlighted.has(id));
|
|
56987
|
+
let html = node_innerHTML_of(graph.getNodeData(id), theme, highlighted.has(id), selected.has(id));
|
|
56864
56988
|
if (html !== null) updates.push({
|
|
56865
56989
|
id,
|
|
56866
56990
|
style: { innerHTML: html }
|
|
@@ -56890,12 +57014,29 @@ function refresh_default_edges_theme(gobj, theme) {
|
|
|
56890
57014
|
if (updates.length > 0) graph.updateEdgeData(updates);
|
|
56891
57015
|
}
|
|
56892
57016
|
/************************************************************
|
|
57017
|
+
* The rings a card can wear, composed into one `box-shadow`.
|
|
57018
|
+
*
|
|
57019
|
+
* A node can be a find match and be selected at the same time, so
|
|
57020
|
+
* neither ring may be written by overwriting the other: the amber
|
|
57021
|
+
* halo hugs the card and the blue selection ring is drawn outside
|
|
57022
|
+
* it, which is also the order that reads correctly when only one
|
|
57023
|
+
* of them is on.
|
|
57024
|
+
************************************************************/
|
|
57025
|
+
function ring_shadow(highlight, selected, base_shadow) {
|
|
57026
|
+
let rings = [];
|
|
57027
|
+
if (highlight) rings.push(`0 0 0 4px ${HIGHLIGHT_HALO}`);
|
|
57028
|
+
if (selected) rings.push(`0 0 0 ${highlight ? "7px" : "3px"} ${SELECT_RING}`);
|
|
57029
|
+
if (base_shadow) rings.push(base_shadow);
|
|
57030
|
+
if (!rings.length) return "";
|
|
57031
|
+
return `box-shadow: ${rings.join(", ")};`;
|
|
57032
|
+
}
|
|
57033
|
+
/************************************************************
|
|
56893
57034
|
* Build innerHTML for pure-child (leaf) nodes: a compact
|
|
56894
57035
|
* chip-card. Same colour/typography family as the entity card
|
|
56895
57036
|
* but lighter (1px border, no shadow, single line). The name is
|
|
56896
57037
|
* always legible (ellipsis + native title tooltip on overflow).
|
|
56897
57038
|
************************************************************/
|
|
56898
|
-
function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
57039
|
+
function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected) {
|
|
56899
57040
|
let title = key || label;
|
|
56900
57041
|
let dark = theme === "dark";
|
|
56901
57042
|
let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
|
|
@@ -56914,7 +57055,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
|
56914
57055
|
height: 100%;
|
|
56915
57056
|
background: ${bg};
|
|
56916
57057
|
border: ${highlight ? "3px" : "1px"} solid ${border};
|
|
56917
|
-
${highlight
|
|
57058
|
+
${ring_shadow(highlight, selected, "")}
|
|
56918
57059
|
border-radius: 8px;
|
|
56919
57060
|
color: ${text_color};
|
|
56920
57061
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
@@ -56940,7 +57081,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
|
|
|
56940
57081
|
* colour is kept (per-topic differentiation) but softened via
|
|
56941
57082
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
56942
57083
|
************************************************************/
|
|
56943
|
-
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight) {
|
|
57084
|
+
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected) {
|
|
56944
57085
|
let title = key || label;
|
|
56945
57086
|
let dark = theme === "dark";
|
|
56946
57087
|
let surface = dark ? "#1b2230" : "#ffffff";
|
|
@@ -56982,7 +57123,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
|
|
|
56982
57123
|
background: ${bg};
|
|
56983
57124
|
border: ${highlight ? "3px" : "1.5px"} ${border_style} ${border};
|
|
56984
57125
|
border-radius: 10px;
|
|
56985
|
-
|
|
57126
|
+
${ring_shadow(highlight, selected, shadow)}
|
|
56986
57127
|
color: ${title_color};
|
|
56987
57128
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
56988
57129
|
display: flex;
|
|
@@ -57026,17 +57167,89 @@ function execute_delete_node(gobj, nodeData) {
|
|
|
57026
57167
|
deselect_node(gobj);
|
|
57027
57168
|
}
|
|
57028
57169
|
/************************************************************
|
|
57170
|
+
* Every node in the graph.
|
|
57171
|
+
************************************************************/
|
|
57172
|
+
function select_all_nodes(gobj) {
|
|
57173
|
+
let graph = gobj.priv.graph;
|
|
57174
|
+
if (!graph) return;
|
|
57175
|
+
set_selection(gobj, ((graph.getData() || {}).nodes || []).map((nd) => nd.id));
|
|
57176
|
+
}
|
|
57177
|
+
/************************************************************
|
|
57178
|
+
* The nodes currently selected, as their node data.
|
|
57179
|
+
************************************************************/
|
|
57180
|
+
function selected_nodes_data(gobj) {
|
|
57181
|
+
let priv = gobj.priv;
|
|
57182
|
+
let graph = priv.graph;
|
|
57183
|
+
let ids = new Set(selected_node_ids(gobj));
|
|
57184
|
+
for (let id of priv._selected_paint_ids || []) ids.add(id);
|
|
57185
|
+
if (priv._selected_node_id) ids.add(priv._selected_node_id);
|
|
57186
|
+
let out = [];
|
|
57187
|
+
for (let id of ids) {
|
|
57188
|
+
let nd;
|
|
57189
|
+
try {
|
|
57190
|
+
nd = graph.getNodeData(id);
|
|
57191
|
+
} catch (e) {
|
|
57192
|
+
continue;
|
|
57193
|
+
}
|
|
57194
|
+
if (nd && nd.data && nd.data.desc) out.push(nd);
|
|
57195
|
+
}
|
|
57196
|
+
return out;
|
|
57197
|
+
}
|
|
57198
|
+
/************************************************************
|
|
57199
|
+
* What a delete takes with it, for ONE node or for a set.
|
|
57200
|
+
*
|
|
57201
|
+
* Same sentence in both cases and the same keys, because it is the
|
|
57202
|
+
* same warning: these views delete with `force`, which UNLINKS the
|
|
57203
|
+
* children (they survive, loose) and cleans the node off its
|
|
57204
|
+
* parents. Over a set the two numbers are the sums -- an operator
|
|
57205
|
+
* about to detach eleven records has to read eleven, not "are you
|
|
57206
|
+
* sure".
|
|
57207
|
+
************************************************************/
|
|
57208
|
+
function delete_question(nodes) {
|
|
57209
|
+
let children = 0;
|
|
57210
|
+
let parents = 0;
|
|
57211
|
+
for (let nd of nodes) {
|
|
57212
|
+
let impact = delete_impact(nd.data.desc, nd.data.record || {});
|
|
57213
|
+
children += impact.children;
|
|
57214
|
+
parents += impact.parents;
|
|
57215
|
+
}
|
|
57216
|
+
let question;
|
|
57217
|
+
if (nodes.length === 1) {
|
|
57218
|
+
let nd = nodes[0];
|
|
57219
|
+
question = (0, i18next.t)("delete") + " " + nd.data.desc.topic_name + ": " + (nd.data.record || {}).id + "?";
|
|
57220
|
+
} else question = (0, i18next.t)("delete") + " " + nodes.length + " " + (0, i18next.t)("records") + "?";
|
|
57221
|
+
if (children > 0) question += "\n" + children + " " + (0, i18next.t)("children will be unlinked, not deleted", { count: children });
|
|
57222
|
+
if (parents > 0) question += "\n" + (0, i18next.t)("it will be detached from") + " " + parents + " " + (0, i18next.t)("parents", { count: parents });
|
|
57223
|
+
return question;
|
|
57224
|
+
}
|
|
57225
|
+
/************************************************************
|
|
57226
|
+
* Delete every selected node (the Delete key).
|
|
57227
|
+
************************************************************/
|
|
57228
|
+
function request_delete_selection(gobj) {
|
|
57229
|
+
let nodes = selected_nodes_data(gobj);
|
|
57230
|
+
if (!nodes.length) return;
|
|
57231
|
+
if (!(0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "confirm_delete_node")) {
|
|
57232
|
+
execute_delete_selection(gobj, nodes);
|
|
57233
|
+
return;
|
|
57234
|
+
}
|
|
57235
|
+
show_confirm_popover(gobj, null, delete_question(nodes), "delete", "#ff4d4f", () => execute_delete_selection(gobj, nodes), "_delete_confirm_el");
|
|
57236
|
+
}
|
|
57237
|
+
function execute_delete_selection(gobj, nodes) {
|
|
57238
|
+
let priv = gobj.priv;
|
|
57239
|
+
hide_delete_confirm(gobj);
|
|
57240
|
+
for (let nd of nodes) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_DELETE_NODE", {
|
|
57241
|
+
treedb_name: priv.treedb_name,
|
|
57242
|
+
topic_name: nd.data.desc.topic_name,
|
|
57243
|
+
record: nd.data.record
|
|
57244
|
+
});
|
|
57245
|
+
deselect_node(gobj);
|
|
57246
|
+
}
|
|
57247
|
+
/************************************************************
|
|
57029
57248
|
* Show a confirmation popover next to the delete icon.
|
|
57030
57249
|
************************************************************/
|
|
57031
57250
|
function show_delete_confirm(gobj, nodeData) {
|
|
57032
57251
|
let priv = gobj.priv;
|
|
57033
|
-
|
|
57034
|
-
let topic_name = nodeData.data.desc.topic_name;
|
|
57035
|
-
let impact = delete_impact(nodeData.data.desc, record);
|
|
57036
|
-
let question = (0, i18next.t)("delete") + " " + topic_name + ": " + record.id + "?";
|
|
57037
|
-
if (impact.children > 0) question += "\n" + impact.children + " " + (0, i18next.t)("children will be unlinked, not deleted", { count: impact.children });
|
|
57038
|
-
if (impact.parents > 0) question += "\n" + (0, i18next.t)("it will be detached from") + " " + impact.parents + " " + (0, i18next.t)("parents", { count: impact.parents });
|
|
57039
|
-
show_confirm_popover(gobj, priv._node_delete_el, question, "delete", "#ff4d4f", () => execute_delete_node(gobj, nodeData), "_delete_confirm_el");
|
|
57252
|
+
show_confirm_popover(gobj, priv._node_delete_el, delete_question([nodeData]), "delete", "#ff4d4f", () => execute_delete_node(gobj, nodeData), "_delete_confirm_el");
|
|
57040
57253
|
}
|
|
57041
57254
|
function hide_delete_confirm(gobj) {
|
|
57042
57255
|
hide_overlay(gobj, "_delete_confirm_el");
|
|
@@ -57903,6 +58116,49 @@ function ac_node_drag_end(gobj, event, kw, src) {
|
|
|
57903
58116
|
return 0;
|
|
57904
58117
|
}
|
|
57905
58118
|
/************************************************************
|
|
58119
|
+
* A key, while the graph has focus.
|
|
58120
|
+
*
|
|
58121
|
+
* Full screen keeps the two keys it always had. The rest belong to
|
|
58122
|
+
* the selection, so they only mean anything in edition -- outside
|
|
58123
|
+
* it there is nothing selected to clear, select or delete.
|
|
58124
|
+
************************************************************/
|
|
58125
|
+
function ac_key_down(gobj, event, kw, src) {
|
|
58126
|
+
let priv = gobj.priv;
|
|
58127
|
+
let key = kw && kw.key || "";
|
|
58128
|
+
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_fullscreen")) {
|
|
58129
|
+
let fullscreen = graph_get_plugin(gobj, "fullscreen");
|
|
58130
|
+
if (fullscreen) {
|
|
58131
|
+
if (key === "F" || key === "f") fullscreen.request();
|
|
58132
|
+
else if (key === "Escape") fullscreen.exit();
|
|
58133
|
+
}
|
|
58134
|
+
}
|
|
58135
|
+
if (!priv.edit_mode) return 0;
|
|
58136
|
+
if (key === "Escape") {
|
|
58137
|
+
deselect_node(gobj);
|
|
58138
|
+
return 0;
|
|
58139
|
+
}
|
|
58140
|
+
if (kw.ctrl && (key === "a" || key === "A")) {
|
|
58141
|
+
select_all_nodes(gobj);
|
|
58142
|
+
return 0;
|
|
58143
|
+
}
|
|
58144
|
+
if (key === "Delete" || key === "Backspace") {
|
|
58145
|
+
request_delete_selection(gobj);
|
|
58146
|
+
return 0;
|
|
58147
|
+
}
|
|
58148
|
+
return 0;
|
|
58149
|
+
}
|
|
58150
|
+
/************************************************************
|
|
58151
|
+
* The rubber band let go: these are the nodes it enclosed.
|
|
58152
|
+
*
|
|
58153
|
+
* They arrive in the kw because `onSelect` runs BEFORE G6 writes
|
|
58154
|
+
* the state, so asking the graph here would answer the previous
|
|
58155
|
+
* selection.
|
|
58156
|
+
************************************************************/
|
|
58157
|
+
function ac_brush_select(gobj, event, kw, src) {
|
|
58158
|
+
set_selection(gobj, kw && kw.ids || []);
|
|
58159
|
+
return 0;
|
|
58160
|
+
}
|
|
58161
|
+
/************************************************************
|
|
57906
58162
|
* Node click - publish vertex clicked event
|
|
57907
58163
|
************************************************************/
|
|
57908
58164
|
function ac_node_click(gobj, event, kw, src) {
|
|
@@ -57917,7 +58173,8 @@ function ac_node_click(gobj, event, kw, src) {
|
|
|
57917
58173
|
topic_name: nodedata.data.desc.topic_name,
|
|
57918
58174
|
record: nodedata.data.record
|
|
57919
58175
|
});
|
|
57920
|
-
if (priv.edit_mode)
|
|
58176
|
+
if (priv.edit_mode && kw.evt.shiftKey) toggle_in_selection(gobj, node_id);
|
|
58177
|
+
else if (priv.edit_mode) {
|
|
57921
58178
|
let containerRect = priv.$container.getBoundingClientRect();
|
|
57922
58179
|
let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
|
|
57923
58180
|
let port_key = detect_port_click(gobj, node_id, canvasPoint[0], canvasPoint[1]);
|
|
@@ -58144,6 +58401,16 @@ function create_gclass$1(gclass_name) {
|
|
|
58144
58401
|
ac_node_drag_end,
|
|
58145
58402
|
null
|
|
58146
58403
|
],
|
|
58404
|
+
[
|
|
58405
|
+
"EV_BRUSH_SELECT",
|
|
58406
|
+
ac_brush_select,
|
|
58407
|
+
null
|
|
58408
|
+
],
|
|
58409
|
+
[
|
|
58410
|
+
"EV_KEY_DOWN",
|
|
58411
|
+
ac_key_down,
|
|
58412
|
+
null
|
|
58413
|
+
],
|
|
58147
58414
|
[
|
|
58148
58415
|
"EV_ZOOM_IN",
|
|
58149
58416
|
ac_zoom_in,
|
|
@@ -58252,6 +58519,8 @@ function create_gclass$1(gclass_name) {
|
|
|
58252
58519
|
["EV_NODE_CONTEXT_MENU", 0],
|
|
58253
58520
|
["EV_CANVAS_CLICK", 0],
|
|
58254
58521
|
["EV_NODE_DRAG_END", 0],
|
|
58522
|
+
["EV_BRUSH_SELECT", 0],
|
|
58523
|
+
["EV_KEY_DOWN", 0],
|
|
58255
58524
|
["EV_ZOOM_IN", 0],
|
|
58256
58525
|
["EV_ZOOM_OUT", 0],
|
|
58257
58526
|
["EV_ZOOM_RESET", 0],
|