@yuneta/gobj-ui 7.23.148 → 7.23.150
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 +24 -0
- package/dist/gobj-ui.cjs.js +249 -52
- package/dist/gobj-ui.es.js +250 -53
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +114 -1
- package/src/c_yui_treedb_graph.js +48 -0
- package/src/treedb_elbow.js +102 -0
- package/src/treedb_elbow.test.js +105 -0
- package/src/yui_icons.css +6 -0
package/dist/gobj-ui.es.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createJSONEditor } from "vanilla-jsoneditor";
|
|
|
6
6
|
import { TabulatorFull } from "tabulator-tables";
|
|
7
7
|
import * as maplibregl from "maplibre-gl";
|
|
8
8
|
import uPlot from "uplot";
|
|
9
|
-
import { BaseLayout, BrushSelect, CanvasEvent, Circle, CommonEvent, DragCanvas, EdgeEvent, ExtensionCategory, Graph, HTML, HistoryEvent, NodeEvent, Toolbar, ZoomCanvas, register } from "@antv/g6";
|
|
9
|
+
import { BaseLayout, BrushSelect, CanvasEvent, Circle, CommonEvent, DragCanvas, EdgeEvent, ExtensionCategory, Graph, HTML, HistoryEvent, NodeEvent, Polyline, Toolbar, ZoomCanvas, register } from "@antv/g6";
|
|
10
10
|
//#region \0rolldown/runtime.js
|
|
11
11
|
var __create = Object.create;
|
|
12
12
|
var __defProp = Object.defineProperty;
|
|
@@ -29625,6 +29625,54 @@ function layout_radial(nodes, edges, opts) {
|
|
|
29625
29625
|
}
|
|
29626
29626
|
return pos;
|
|
29627
29627
|
}
|
|
29628
|
+
/************************************************************
|
|
29629
|
+
* The lane of `id` among `ids`, the edges joining the same two
|
|
29630
|
+
* cards: 0, then +1, -1, +2, -2... in the order they were made.
|
|
29631
|
+
* Edge ids end in a counter, so a numeric sort is that order.
|
|
29632
|
+
************************************************************/
|
|
29633
|
+
function elbow_lane(ids, id) {
|
|
29634
|
+
let k = ids.slice().sort((a, b) => String(a).localeCompare(String(b), void 0, { numeric: true })).indexOf(id);
|
|
29635
|
+
if (k <= 0) return 0;
|
|
29636
|
+
return (k % 2 ? 1 : -1) * Math.ceil(k / 2);
|
|
29637
|
+
}
|
|
29638
|
+
function swap(p) {
|
|
29639
|
+
return [p[1], p[0]];
|
|
29640
|
+
}
|
|
29641
|
+
function swap_box(b) {
|
|
29642
|
+
return {
|
|
29643
|
+
x1: b.y1,
|
|
29644
|
+
y1: b.x1,
|
|
29645
|
+
x2: b.y2,
|
|
29646
|
+
y2: b.x2
|
|
29647
|
+
};
|
|
29648
|
+
}
|
|
29649
|
+
/************************************************************
|
|
29650
|
+
* The control points of an elbow from the port `s` to the port
|
|
29651
|
+
* `t` ([x, y] each), knowing the boxes of the two cards
|
|
29652
|
+
* ({x1, y1, x2, y2}) and the edge's lane.
|
|
29653
|
+
*
|
|
29654
|
+
* vertical the rows are stacked (top to bottom); false
|
|
29655
|
+
* when they are side by side (left to right)
|
|
29656
|
+
************************************************************/
|
|
29657
|
+
function elbow_points(s, t, box_s, box_t, lane, vertical) {
|
|
29658
|
+
if (!vertical) return elbow_points(swap(s), swap(t), swap_box(box_s), swap_box(box_t), lane, true).map(swap);
|
|
29659
|
+
let shift = lane * 10;
|
|
29660
|
+
if (t[1] - s[1] > 20) {
|
|
29661
|
+
let mid = (s[1] + t[1]) / 2 + shift;
|
|
29662
|
+
mid = Math.min(Math.max(mid, s[1] + 10), t[1] - 10);
|
|
29663
|
+
return [[s[0], mid], [t[0], mid]];
|
|
29664
|
+
}
|
|
29665
|
+
let out = 16 + Math.abs(shift);
|
|
29666
|
+
let side = lane < 0 ? Math.min(box_s.x1, box_t.x1) - 24 - Math.abs(shift) : Math.max(box_s.x2, box_t.x2) + 24 + Math.abs(shift);
|
|
29667
|
+
let y1 = s[1] + out;
|
|
29668
|
+
let y2 = t[1] - out;
|
|
29669
|
+
return [
|
|
29670
|
+
[s[0], y1],
|
|
29671
|
+
[side, y1],
|
|
29672
|
+
[side, y2],
|
|
29673
|
+
[t[0], y2]
|
|
29674
|
+
];
|
|
29675
|
+
}
|
|
29628
29676
|
//#endregion
|
|
29629
29677
|
//#region node_modules/@babel/runtime/helpers/esm/typeof.js
|
|
29630
29678
|
function _typeof(o) {
|
|
@@ -45639,7 +45687,7 @@ var _excluded$1 = ["style"];
|
|
|
45639
45687
|
/**
|
|
45640
45688
|
* Polyline inherits the marker-related capabilities of Polygon.
|
|
45641
45689
|
*/
|
|
45642
|
-
var Polyline = /*#__PURE__*/ function(_Polygon) {
|
|
45690
|
+
var Polyline$1 = /*#__PURE__*/ function(_Polygon) {
|
|
45643
45691
|
function Polyline() {
|
|
45644
45692
|
var _ref = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}, style = _ref.style, rest = _objectWithoutProperties(_ref, _excluded$1);
|
|
45645
45693
|
_classCallCheck(this, Polyline);
|
|
@@ -45731,7 +45779,7 @@ var Polyline = /*#__PURE__*/ function(_Polygon) {
|
|
|
45731
45779
|
}
|
|
45732
45780
|
]);
|
|
45733
45781
|
}(Polygon);
|
|
45734
|
-
Polyline.PARSED_STYLE_LIST = new Set([].concat(_toConsumableArray(Polygon.PARSED_STYLE_LIST), [
|
|
45782
|
+
Polyline$1.PARSED_STYLE_LIST = new Set([].concat(_toConsumableArray(Polygon.PARSED_STYLE_LIST), [
|
|
45735
45783
|
"points",
|
|
45736
45784
|
"markerStart",
|
|
45737
45785
|
"markerMid",
|
|
@@ -56566,6 +56614,12 @@ var attrs_table$4 = [
|
|
|
56566
56614
|
SDATA(data_type_t.DTP_STRING, "main_topic", 0, "", "The topic the tree hangs from; empty = deduced (the one whose hooks reach the most others). Its parentless records are the roots; a parentless record of a topic linked to it is LOOSE and shown only on request"),
|
|
56567
56615
|
SDATA(data_type_t.DTP_LIST, "loose_topics", 0, "[]", "Topics whose loose records (no parent, in a topic the schema hangs from the main one) are shown as roots"),
|
|
56568
56616
|
SDATA(data_type_t.DTP_STRING, "node_mode", 0, "expanded", "How a record is drawn: `expanded` = the card with its pills and ports; `compact` = a one-line pill with the name and small ports; `shape` = a figure of the topic's colour (square, circle, diamond... per node or topic, from the node properties), no ports, no text -- the topology alone. One node can be toggled against it (EV_TOGGLE_NODE_MODE)"),
|
|
56617
|
+
SDATA(data_type_t.DTP_STRING, "edge_shape", 0, "curved", "How an edge is drawn: `curved` (the default) or `elbow` -- straight out of its port to the channel between the two rows, along it, and straight into the other port: the bus of an org chart, as mxGraph drew its trees. Layouts with no rows (`rowless_layouts`) keep the curve"),
|
|
56618
|
+
SDATA(data_type_t.DTP_LIST, "rowless_layouts", sdata_flag_t.SDF_RD, JSON.stringify([
|
|
56619
|
+
"radial",
|
|
56620
|
+
"d3-force",
|
|
56621
|
+
"force-atlas2"
|
|
56622
|
+
]), "Layouts with no rows for an elbow to run between: their edges stay curved whatever `edge_shape` says (read-only, for the parent to query)"),
|
|
56569
56623
|
SDATA(data_type_t.DTP_BOOLEAN, "node_labels", 0, true, "A `shape` node says its name under the figure. Off, it is a coloured figure and nothing else"),
|
|
56570
56624
|
SDATA(data_type_t.DTP_BOOLEAN, "confirm_delete_node", 0, true, "Ask confirmation before deleting a node"),
|
|
56571
56625
|
SDATA(data_type_t.DTP_BOOLEAN, "confirm_unlink_edge", 0, true, "Ask confirmation before unlinking an edge"),
|
|
@@ -56817,6 +56871,8 @@ function register_layouts(gobj) {
|
|
|
56817
56871
|
register(ExtensionCategory.LAYOUT, "manual", ManualLayout);
|
|
56818
56872
|
register(ExtensionCategory.LAYOUT, "treedb-tree", TreedbTreeLayout);
|
|
56819
56873
|
register(ExtensionCategory.LAYOUT, "treedb-compact", TreedbCompactLayout);
|
|
56874
|
+
register(ExtensionCategory.EDGE, "treedb-elbow-v", TreedbElbowV);
|
|
56875
|
+
register(ExtensionCategory.EDGE, "treedb-elbow-h", TreedbElbowH);
|
|
56820
56876
|
register(ExtensionCategory.LAYOUT, "treedb-radial", TreedbRadialLayout);
|
|
56821
56877
|
register(ExtensionCategory.NODE, "light", LightNode);
|
|
56822
56878
|
register(ExtensionCategory.NODE, "treedb-card", TreedbCard);
|
|
@@ -57509,7 +57565,10 @@ function layout_direction(gobj) {
|
|
|
57509
57565
|
return str_in_list(LR_LAYOUTS, gobj.priv.layout) ? "LR" : "TB";
|
|
57510
57566
|
}
|
|
57511
57567
|
function edge_type_for(gobj) {
|
|
57512
|
-
|
|
57568
|
+
let lr = layout_direction(gobj) === "LR";
|
|
57569
|
+
let rowless = gobj_read_attr(gobj, "rowless_layouts") || [];
|
|
57570
|
+
if (gobj_read_str_attr(gobj, "edge_shape") === "elbow" && !str_in_list(rowless, gobj.priv.layout)) return lr ? "treedb-elbow-h" : "treedb-elbow-v";
|
|
57571
|
+
return lr ? "cubic-horizontal" : "cubic";
|
|
57513
57572
|
}
|
|
57514
57573
|
/************************************************************
|
|
57515
57574
|
* Where the ports sit, by direction: fkeys (the way in from a
|
|
@@ -58984,6 +59043,70 @@ var TreedbTreeLayout = class extends BaseLayout {
|
|
|
58984
59043
|
return treedb_layout_output(layout_tree(input.nodes, input.edges, options));
|
|
58985
59044
|
}
|
|
58986
59045
|
};
|
|
59046
|
+
/************************************************************
|
|
59047
|
+
* An ELBOW edge, the way mxGraph drew a tree: straight out of
|
|
59048
|
+
* the parent's port to the channel half way to the child, along
|
|
59049
|
+
* it, and straight into the child's port. The children of one
|
|
59050
|
+
* hook sit on one row and leave by one port, so they share the
|
|
59051
|
+
* channel and the lines read as one bus.
|
|
59052
|
+
*
|
|
59053
|
+
* Computed on every draw from where the two ends ARE, so a
|
|
59054
|
+
* dragged card, a fold or a layout needs nothing else. `-v` runs
|
|
59055
|
+
* between rows (top to bottom), `-h` between columns. An edge that
|
|
59056
|
+
* does not run forward goes round the two cards, and the edges
|
|
59057
|
+
* joining the same two cards take lanes (treedb_elbow.js).
|
|
59058
|
+
************************************************************/
|
|
59059
|
+
var ELBOW_RADIUS = 6;
|
|
59060
|
+
function elbow_box(node) {
|
|
59061
|
+
try {
|
|
59062
|
+
let b = node.getShape("key").getBounds();
|
|
59063
|
+
return {
|
|
59064
|
+
x1: b.min[0],
|
|
59065
|
+
y1: b.min[1],
|
|
59066
|
+
x2: b.max[0],
|
|
59067
|
+
y2: b.max[1]
|
|
59068
|
+
};
|
|
59069
|
+
} catch (e) {
|
|
59070
|
+
log_error(`elbow edge: no bounds for node ${node && node.id}: ${e}`);
|
|
59071
|
+
let c = node.getCenter();
|
|
59072
|
+
return {
|
|
59073
|
+
x1: c[0],
|
|
59074
|
+
y1: c[1],
|
|
59075
|
+
x2: c[0],
|
|
59076
|
+
y2: c[1]
|
|
59077
|
+
};
|
|
59078
|
+
}
|
|
59079
|
+
}
|
|
59080
|
+
function elbow_lane_of(edge) {
|
|
59081
|
+
let graph = edge.context && edge.context.graph;
|
|
59082
|
+
let s = edge.sourceNode && edge.sourceNode.id;
|
|
59083
|
+
let t = edge.targetNode && edge.targetNode.id;
|
|
59084
|
+
if (!graph || s === void 0 || t === void 0 || s === t) return 0;
|
|
59085
|
+
try {
|
|
59086
|
+
return elbow_lane(graph.getRelatedEdgesData(s).filter((d) => d.source === s && d.target === t || d.source === t && d.target === s).map((d) => d.id), edge.id);
|
|
59087
|
+
} catch (e) {
|
|
59088
|
+
log_error(`elbow edge ${edge.id}: cannot read the edges of ${s}: ${e}`);
|
|
59089
|
+
return 0;
|
|
59090
|
+
}
|
|
59091
|
+
}
|
|
59092
|
+
var TreedbElbowV = class extends Polyline {
|
|
59093
|
+
getControlPoints(attributes) {
|
|
59094
|
+
let [s, t] = this.getEndpoints(attributes, false);
|
|
59095
|
+
return elbow_points(s, t, elbow_box(this.sourceNode), elbow_box(this.targetNode), elbow_lane_of(this), true);
|
|
59096
|
+
}
|
|
59097
|
+
getKeyPath(attributes) {
|
|
59098
|
+
return super.getKeyPath(Object.assign({}, attributes, { radius: attributes.radius || ELBOW_RADIUS }));
|
|
59099
|
+
}
|
|
59100
|
+
};
|
|
59101
|
+
var TreedbElbowH = class extends Polyline {
|
|
59102
|
+
getControlPoints(attributes) {
|
|
59103
|
+
let [s, t] = this.getEndpoints(attributes, false);
|
|
59104
|
+
return elbow_points(s, t, elbow_box(this.sourceNode), elbow_box(this.targetNode), elbow_lane_of(this), false);
|
|
59105
|
+
}
|
|
59106
|
+
getKeyPath(attributes) {
|
|
59107
|
+
return super.getKeyPath(Object.assign({}, attributes, { radius: attributes.radius || ELBOW_RADIUS }));
|
|
59108
|
+
}
|
|
59109
|
+
};
|
|
58987
59110
|
var TreedbCompactLayout = class extends BaseLayout {
|
|
58988
59111
|
async execute(data, options) {
|
|
58989
59112
|
let input = treedb_layout_input(data);
|
|
@@ -63427,6 +63550,20 @@ function ac_set_node_labels(gobj, event, kw, src) {
|
|
|
63427
63550
|
return 0;
|
|
63428
63551
|
}
|
|
63429
63552
|
/************************************************************
|
|
63553
|
+
* Curved or elbow edges (the host's toggle). Nothing moves: every
|
|
63554
|
+
* edge takes the type that goes with the shape and the layout,
|
|
63555
|
+
* and draws again.
|
|
63556
|
+
************************************************************/
|
|
63557
|
+
function ac_set_edge_shape(gobj, event, kw, src) {
|
|
63558
|
+
let priv = gobj.priv;
|
|
63559
|
+
let shape = kw && kw.edge_shape === "elbow" ? "elbow" : "curved";
|
|
63560
|
+
gobj_write_str_attr(gobj, "edge_shape", shape);
|
|
63561
|
+
if (!priv.graph || !priv.graph_rendered) return 0;
|
|
63562
|
+
apply_layout_direction(gobj);
|
|
63563
|
+
graph_draw(gobj);
|
|
63564
|
+
return 0;
|
|
63565
|
+
}
|
|
63566
|
+
/************************************************************
|
|
63430
63567
|
* ONE node opened or closed against the mode (a double click,
|
|
63431
63568
|
* or the context menu). It holds still on screen while the
|
|
63432
63569
|
* rest of the graph makes room, as a fold does.
|
|
@@ -63986,6 +64123,11 @@ function create_gclass$4(gclass_name) {
|
|
|
63986
64123
|
ac_set_node_labels,
|
|
63987
64124
|
null
|
|
63988
64125
|
],
|
|
64126
|
+
[
|
|
64127
|
+
"EV_SET_EDGE_SHAPE",
|
|
64128
|
+
ac_set_edge_shape,
|
|
64129
|
+
null
|
|
64130
|
+
],
|
|
63989
64131
|
[
|
|
63990
64132
|
"EV_TOGGLE_NODE_MODE",
|
|
63991
64133
|
ac_toggle_node_mode,
|
|
@@ -64107,6 +64249,7 @@ function create_gclass$4(gclass_name) {
|
|
|
64107
64249
|
["EV_SET_LOOSE_TOPICS", 0],
|
|
64108
64250
|
["EV_SET_NODE_MODE", 0],
|
|
64109
64251
|
["EV_SET_NODE_LABELS", 0],
|
|
64252
|
+
["EV_SET_EDGE_SHAPE", 0],
|
|
64110
64253
|
["EV_TOGGLE_NODE_MODE", 0],
|
|
64111
64254
|
["EV_NODE_DBLCLICK", 0],
|
|
64112
64255
|
["EV_LEGEND_STATE", event_flag_t.EVF_OUTPUT_EVENT],
|
|
@@ -64183,6 +64326,7 @@ var attrs_table$3 = [
|
|
|
64183
64326
|
SDATA(data_type_t.DTP_STRING, "main_topic", sdata_flag_t.SDF_PERSIST, "", "The topic the tree hangs from; empty = deduced by the graph. User preference, per treedb"),
|
|
64184
64327
|
SDATA(data_type_t.DTP_LIST, "loose_topics", sdata_flag_t.SDF_PERSIST, "[]", "Topics whose LOOSE records (no parent) are shown. User preference, per treedb"),
|
|
64185
64328
|
SDATA(data_type_t.DTP_STRING, "node_mode", sdata_flag_t.SDF_PERSIST, "expanded", "How the records are drawn: `expanded` = the cards with their pills and ports, `compact` = one-line pills with the name, `shape` = coloured figures, the topology alone. User preference"),
|
|
64329
|
+
SDATA(data_type_t.DTP_STRING, "edge_shape", sdata_flag_t.SDF_PERSIST, "curved", "How the edges are drawn: `curved` or `elbow` (the bus of an org chart). User preference"),
|
|
64186
64330
|
SDATA(data_type_t.DTP_BOOLEAN, "node_labels", sdata_flag_t.SDF_PERSIST, true, "A `shape` node says its name under its figure. User preference"),
|
|
64187
64331
|
SDATA(data_type_t.DTP_DICT, "camera", sdata_flag_t.SDF_PERSIST, "{}", "Where the reader left the viewport: {zoom, x, y}. Handed to the engine on creation and written back when a move settles, so the graph opens where it was left. User preference, per treedb"),
|
|
64188
64332
|
SDATA(data_type_t.DTP_INTEGER, "fold_level", sdata_flag_t.SDF_PERSIST, 0, "The level of the MAIN tree the toolbar's stepper stands on: 1 = the main topic's roots, N = N-1 levels under them; 0 = never stepped, `expand_depth`. Handed to the engine on creation, so the graph opens at the level it was left. User preference, per treedb"),
|
|
@@ -64275,6 +64419,7 @@ function mt_create$3(gobj) {
|
|
|
64275
64419
|
loose_topics: gobj_read_attr(gobj, "loose_topics") || [],
|
|
64276
64420
|
node_mode: gobj_read_str_attr(gobj, "node_mode") || "expanded",
|
|
64277
64421
|
node_labels: gobj_read_bool_attr(gobj, "node_labels"),
|
|
64422
|
+
edge_shape: gobj_read_str_attr(gobj, "edge_shape") || "curved",
|
|
64278
64423
|
camera: gobj_read_attr(gobj, "camera") || {}
|
|
64279
64424
|
}, gobj);
|
|
64280
64425
|
populate_nodes_tree_options(gobj);
|
|
@@ -64485,58 +64630,82 @@ function option_label(name) {
|
|
|
64485
64630
|
* its context menu (the child's business).
|
|
64486
64631
|
************************************************************/
|
|
64487
64632
|
function node_shape_items(gobj, wide) {
|
|
64488
|
-
return [
|
|
64489
|
-
|
|
64490
|
-
|
|
64491
|
-
|
|
64492
|
-
|
|
64493
|
-
|
|
64494
|
-
|
|
64495
|
-
|
|
64496
|
-
|
|
64497
|
-
|
|
64498
|
-
|
|
64499
|
-
|
|
64500
|
-
|
|
64501
|
-
|
|
64502
|
-
|
|
64503
|
-
|
|
64504
|
-
|
|
64633
|
+
return [
|
|
64634
|
+
[
|
|
64635
|
+
"div",
|
|
64636
|
+
{
|
|
64637
|
+
class: "GRAPH_NODE_MODE buttons has-addons",
|
|
64638
|
+
style: "margin:0 0 0 .5rem; flex:0 0 auto; flex-wrap:nowrap;"
|
|
64639
|
+
},
|
|
64640
|
+
NODE_MODES.map((mode) => {
|
|
64641
|
+
let key = NODE_MODE_LABELS[mode];
|
|
64642
|
+
return [
|
|
64643
|
+
"button",
|
|
64644
|
+
{
|
|
64645
|
+
class: "GRAPH_NODE_MODE_BTN button",
|
|
64646
|
+
type: "button",
|
|
64647
|
+
"data-mode": mode,
|
|
64648
|
+
style: {
|
|
64649
|
+
height: wide,
|
|
64650
|
+
width: "2.5em"
|
|
64651
|
+
},
|
|
64652
|
+
title: t(key),
|
|
64653
|
+
"data-i18n-title": key,
|
|
64654
|
+
"aria-label": t(key),
|
|
64655
|
+
"data-i18n-aria-label": key,
|
|
64656
|
+
"aria-pressed": "false"
|
|
64505
64657
|
},
|
|
64506
|
-
|
|
64507
|
-
|
|
64508
|
-
|
|
64509
|
-
|
|
64510
|
-
|
|
64658
|
+
yui_toolbar_icon(NODE_MODE_ICONS[mode]),
|
|
64659
|
+
{ click: (evt) => {
|
|
64660
|
+
evt.stopPropagation();
|
|
64661
|
+
gobj_send_event(gobj, "EV_SET_NODE_MODE", { node_mode: mode }, gobj);
|
|
64662
|
+
} }
|
|
64663
|
+
];
|
|
64664
|
+
})
|
|
64665
|
+
],
|
|
64666
|
+
[
|
|
64667
|
+
"button",
|
|
64668
|
+
{
|
|
64669
|
+
class: "GRAPH_NODE_LABELS button",
|
|
64670
|
+
type: "button",
|
|
64671
|
+
style: {
|
|
64672
|
+
height: wide,
|
|
64673
|
+
width: "2.5em"
|
|
64511
64674
|
},
|
|
64512
|
-
|
|
64513
|
-
|
|
64514
|
-
|
|
64515
|
-
|
|
64516
|
-
|
|
64517
|
-
];
|
|
64518
|
-
})
|
|
64519
|
-
], [
|
|
64520
|
-
"button",
|
|
64521
|
-
{
|
|
64522
|
-
class: "GRAPH_NODE_LABELS button",
|
|
64523
|
-
type: "button",
|
|
64524
|
-
style: {
|
|
64525
|
-
height: wide,
|
|
64526
|
-
width: "2.5em"
|
|
64675
|
+
title: t("node labels"),
|
|
64676
|
+
"data-i18n-title": "node labels",
|
|
64677
|
+
"aria-label": t("node labels"),
|
|
64678
|
+
"data-i18n-aria-label": "node labels",
|
|
64679
|
+
"aria-pressed": "false"
|
|
64527
64680
|
},
|
|
64528
|
-
|
|
64529
|
-
|
|
64530
|
-
|
|
64531
|
-
|
|
64532
|
-
|
|
64533
|
-
|
|
64534
|
-
|
|
64535
|
-
|
|
64536
|
-
|
|
64537
|
-
|
|
64538
|
-
|
|
64539
|
-
|
|
64681
|
+
yui_toolbar_icon("yi-font"),
|
|
64682
|
+
{ click: (evt) => {
|
|
64683
|
+
evt.stopPropagation();
|
|
64684
|
+
gobj_send_event(gobj, "EV_TOGGLE_NODE_LABELS", {}, gobj);
|
|
64685
|
+
} }
|
|
64686
|
+
],
|
|
64687
|
+
[
|
|
64688
|
+
"button",
|
|
64689
|
+
{
|
|
64690
|
+
class: "GRAPH_EDGE_ELBOW button",
|
|
64691
|
+
type: "button",
|
|
64692
|
+
style: {
|
|
64693
|
+
height: wide,
|
|
64694
|
+
width: "2.5em"
|
|
64695
|
+
},
|
|
64696
|
+
title: t("elbow edges"),
|
|
64697
|
+
"data-i18n-title": "elbow edges",
|
|
64698
|
+
"aria-label": t("elbow edges"),
|
|
64699
|
+
"data-i18n-aria-label": "elbow edges",
|
|
64700
|
+
"aria-pressed": "false"
|
|
64701
|
+
},
|
|
64702
|
+
yui_toolbar_icon("yi-diagram-project"),
|
|
64703
|
+
{ click: (evt) => {
|
|
64704
|
+
evt.stopPropagation();
|
|
64705
|
+
gobj_send_event(gobj, "EV_TOGGLE_EDGE_SHAPE", {}, gobj);
|
|
64706
|
+
} }
|
|
64707
|
+
]
|
|
64708
|
+
];
|
|
64540
64709
|
}
|
|
64541
64710
|
var NODE_MODES = [
|
|
64542
64711
|
"expanded",
|
|
@@ -64569,6 +64738,16 @@ function refresh_node_shape_buttons(gobj, $root) {
|
|
|
64569
64738
|
$labels.setAttribute("aria-pressed", labels ? "true" : "false");
|
|
64570
64739
|
$labels.disabled = mode !== "shape";
|
|
64571
64740
|
}
|
|
64741
|
+
let elbow = gobj_read_str_attr(gobj, "edge_shape") === "elbow";
|
|
64742
|
+
set_pressed_state($root, ".GRAPH_EDGE_ELBOW", elbow);
|
|
64743
|
+
let $elbow = $root.querySelector(".GRAPH_EDGE_ELBOW");
|
|
64744
|
+
if ($elbow) {
|
|
64745
|
+
$elbow.setAttribute("aria-pressed", elbow ? "true" : "false");
|
|
64746
|
+
let child = gobj.priv.gobj_nodes_tree;
|
|
64747
|
+
let rowless = child ? gobj_read_attr(child, "rowless_layouts") || [] : [];
|
|
64748
|
+
let layout = child ? gobj_read_str_attr(child, "layout") : "";
|
|
64749
|
+
$elbow.disabled = rowless.includes(layout);
|
|
64750
|
+
}
|
|
64572
64751
|
}
|
|
64573
64752
|
/************************************************************
|
|
64574
64753
|
* The fold STEPPER: one level less, where the tree stands, one
|
|
@@ -65117,6 +65296,7 @@ function populate_nodes_tree_options(gobj) {
|
|
|
65117
65296
|
let current_layout = gobj_read_str_attr(priv.gobj_nodes_tree, "layout");
|
|
65118
65297
|
if (current_layout) $layout_select.value = current_layout;
|
|
65119
65298
|
refresh_layout_stepper(gobj);
|
|
65299
|
+
refresh_node_shape_buttons(gobj, $container);
|
|
65120
65300
|
}
|
|
65121
65301
|
let $operation_mode_select = $container.querySelector(".GRAPH_MODE_SELECT");
|
|
65122
65302
|
$operation_mode_select.value = priv.operation_mode;
|
|
@@ -65810,6 +65990,7 @@ function ac_set_layout(gobj, event, kw, src) {
|
|
|
65810
65990
|
$layout_select.value = priv.layout;
|
|
65811
65991
|
gobj_send_event(priv.gobj_nodes_tree, "EV_SET_LAYOUT", { layout }, gobj);
|
|
65812
65992
|
refresh_layout_stepper(gobj);
|
|
65993
|
+
refresh_node_shape_buttons(gobj, priv.$container);
|
|
65813
65994
|
return 0;
|
|
65814
65995
|
}
|
|
65815
65996
|
/************************************************************
|
|
@@ -66161,6 +66342,7 @@ function ac_layout_autoset(gobj, event, kw, src) {
|
|
|
66161
66342
|
if (!$select || !layout) return 0;
|
|
66162
66343
|
$select.value = layout;
|
|
66163
66344
|
refresh_layout_stepper(gobj);
|
|
66345
|
+
refresh_node_shape_buttons(gobj, $container);
|
|
66164
66346
|
return 0;
|
|
66165
66347
|
}
|
|
66166
66348
|
/************************************************************
|
|
@@ -66233,6 +66415,15 @@ function ac_set_node_mode(gobj, event, kw, src) {
|
|
|
66233
66415
|
if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_SET_NODE_MODE", { node_mode: mode }, gobj);
|
|
66234
66416
|
return 0;
|
|
66235
66417
|
}
|
|
66418
|
+
function ac_toggle_edge_shape(gobj, event, kw, src) {
|
|
66419
|
+
let priv = gobj.priv;
|
|
66420
|
+
let shape = gobj_read_str_attr(gobj, "edge_shape") === "elbow" ? "curved" : "elbow";
|
|
66421
|
+
gobj_write_str_attr(gobj, "edge_shape", shape);
|
|
66422
|
+
gobj_save_persistent_attrs(gobj, "edge_shape");
|
|
66423
|
+
refresh_node_shape_buttons(gobj, priv.$container);
|
|
66424
|
+
if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_SET_EDGE_SHAPE", { edge_shape: shape }, gobj);
|
|
66425
|
+
return 0;
|
|
66426
|
+
}
|
|
66236
66427
|
function ac_toggle_node_labels(gobj, event, kw, src) {
|
|
66237
66428
|
let priv = gobj.priv;
|
|
66238
66429
|
let labels = !gobj_read_bool_attr(gobj, "node_labels");
|
|
@@ -66477,6 +66668,11 @@ function create_gclass$3(gclass_name) {
|
|
|
66477
66668
|
ac_toggle_node_labels,
|
|
66478
66669
|
null
|
|
66479
66670
|
],
|
|
66671
|
+
[
|
|
66672
|
+
"EV_TOGGLE_EDGE_SHAPE",
|
|
66673
|
+
ac_toggle_edge_shape,
|
|
66674
|
+
null
|
|
66675
|
+
],
|
|
66480
66676
|
[
|
|
66481
66677
|
"EV_LAYOUT_AUTOSET",
|
|
66482
66678
|
ac_layout_autoset,
|
|
@@ -66554,6 +66750,7 @@ function create_gclass$3(gclass_name) {
|
|
|
66554
66750
|
["EV_COLLAPSE_LEVEL", 0],
|
|
66555
66751
|
["EV_SET_NODE_MODE", 0],
|
|
66556
66752
|
["EV_TOGGLE_NODE_LABELS", 0],
|
|
66753
|
+
["EV_TOGGLE_EDGE_SHAPE", 0],
|
|
66557
66754
|
["EV_FIND_RESULT", 0],
|
|
66558
66755
|
["EV_LAYOUT_AUTOSET", 0],
|
|
66559
66756
|
["EV_CAMERA_CHANGED", 0],
|
package/package.json
CHANGED
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -133,6 +133,7 @@ import {
|
|
|
133
133
|
fold_root_group_key,
|
|
134
134
|
} from "./treedb_fold_model.js";
|
|
135
135
|
import {layout_tree, layout_radial, layout_compact} from "./treedb_layout.js";
|
|
136
|
+
import {elbow_lane, elbow_points} from "./treedb_elbow.js";
|
|
136
137
|
|
|
137
138
|
import {
|
|
138
139
|
BaseLayout,
|
|
@@ -146,6 +147,7 @@ import {
|
|
|
146
147
|
HTML,
|
|
147
148
|
Toolbar,
|
|
148
149
|
register,
|
|
150
|
+
Polyline,
|
|
149
151
|
} from '@antv/g6';
|
|
150
152
|
|
|
151
153
|
import {Circle as CircleGeometry, Rect as RectGeometry} from '@antv/g';
|
|
@@ -456,6 +458,8 @@ SDATA(data_type_t.DTP_LIST, "loose_topics", 0, "[]", "Topics whos
|
|
|
456
458
|
|
|
457
459
|
/*---------------- Node shape ----------------*/
|
|
458
460
|
SDATA(data_type_t.DTP_STRING, "node_mode", 0, "expanded", "How a record is drawn: `expanded` = the card with its pills and ports; `compact` = a one-line pill with the name and small ports; `shape` = a figure of the topic's colour (square, circle, diamond... per node or topic, from the node properties), no ports, no text -- the topology alone. One node can be toggled against it (EV_TOGGLE_NODE_MODE)"),
|
|
461
|
+
SDATA(data_type_t.DTP_STRING, "edge_shape", 0, "curved", "How an edge is drawn: `curved` (the default) or `elbow` -- straight out of its port to the channel between the two rows, along it, and straight into the other port: the bus of an org chart, as mxGraph drew its trees. Layouts with no rows (`rowless_layouts`) keep the curve"),
|
|
462
|
+
SDATA(data_type_t.DTP_LIST, "rowless_layouts", sdata_flag_t.SDF_RD, JSON.stringify(["radial", "d3-force", "force-atlas2"]), "Layouts with no rows for an elbow to run between: their edges stay curved whatever `edge_shape` says (read-only, for the parent to query)"),
|
|
459
463
|
SDATA(data_type_t.DTP_BOOLEAN, "node_labels", 0, true, "A `shape` node says its name under the figure. Off, it is a coloured figure and nothing else"),
|
|
460
464
|
SDATA(data_type_t.DTP_BOOLEAN, "confirm_delete_node", 0, true, "Ask confirmation before deleting a node"),
|
|
461
465
|
SDATA(data_type_t.DTP_BOOLEAN, "confirm_unlink_edge", 0, true, "Ask confirmation before unlinking an edge"),
|
|
@@ -936,6 +940,8 @@ function register_layouts(gobj)
|
|
|
936
940
|
register(ExtensionCategory.LAYOUT, 'manual', ManualLayout);
|
|
937
941
|
register(ExtensionCategory.LAYOUT, 'treedb-tree', TreedbTreeLayout);
|
|
938
942
|
register(ExtensionCategory.LAYOUT, 'treedb-compact', TreedbCompactLayout);
|
|
943
|
+
register(ExtensionCategory.EDGE, 'treedb-elbow-v', TreedbElbowV);
|
|
944
|
+
register(ExtensionCategory.EDGE, 'treedb-elbow-h', TreedbElbowH);
|
|
939
945
|
register(ExtensionCategory.LAYOUT, 'treedb-radial', TreedbRadialLayout);
|
|
940
946
|
register(ExtensionCategory.NODE, 'light', LightNode);
|
|
941
947
|
register(ExtensionCategory.NODE, 'treedb-card', TreedbCard);
|
|
@@ -2036,9 +2042,17 @@ function layout_direction(gobj)
|
|
|
2036
2042
|
return str_in_list(LR_LAYOUTS, gobj.priv.layout)? "LR" : "TB";
|
|
2037
2043
|
}
|
|
2038
2044
|
|
|
2045
|
+
/* A curve, or an elbow when the reader asked for one and the layout
|
|
2046
|
+
* has rows to run it between. */
|
|
2039
2047
|
function edge_type_for(gobj)
|
|
2040
2048
|
{
|
|
2041
|
-
|
|
2049
|
+
let lr = (layout_direction(gobj) === "LR");
|
|
2050
|
+
let rowless = gobj_read_attr(gobj, "rowless_layouts") || [];
|
|
2051
|
+
if(gobj_read_str_attr(gobj, "edge_shape") === "elbow" &&
|
|
2052
|
+
!str_in_list(rowless, gobj.priv.layout)) {
|
|
2053
|
+
return lr? "treedb-elbow-h" : "treedb-elbow-v";
|
|
2054
|
+
}
|
|
2055
|
+
return lr? "cubic-horizontal" : "cubic";
|
|
2042
2056
|
}
|
|
2043
2057
|
|
|
2044
2058
|
/************************************************************
|
|
@@ -4199,6 +4213,84 @@ class TreedbTreeLayout extends BaseLayout
|
|
|
4199
4213
|
}
|
|
4200
4214
|
}
|
|
4201
4215
|
|
|
4216
|
+
/************************************************************
|
|
4217
|
+
* An ELBOW edge, the way mxGraph drew a tree: straight out of
|
|
4218
|
+
* the parent's port to the channel half way to the child, along
|
|
4219
|
+
* it, and straight into the child's port. The children of one
|
|
4220
|
+
* hook sit on one row and leave by one port, so they share the
|
|
4221
|
+
* channel and the lines read as one bus.
|
|
4222
|
+
*
|
|
4223
|
+
* Computed on every draw from where the two ends ARE, so a
|
|
4224
|
+
* dragged card, a fold or a layout needs nothing else. `-v` runs
|
|
4225
|
+
* between rows (top to bottom), `-h` between columns. An edge that
|
|
4226
|
+
* does not run forward goes round the two cards, and the edges
|
|
4227
|
+
* joining the same two cards take lanes (treedb_elbow.js).
|
|
4228
|
+
************************************************************/
|
|
4229
|
+
const ELBOW_RADIUS = 6;
|
|
4230
|
+
|
|
4231
|
+
/* The box of a card on the canvas, for a detour to go round it. */
|
|
4232
|
+
function elbow_box(node)
|
|
4233
|
+
{
|
|
4234
|
+
try {
|
|
4235
|
+
let b = node.getShape('key').getBounds();
|
|
4236
|
+
return {x1: b.min[0], y1: b.min[1], x2: b.max[0], y2: b.max[1]};
|
|
4237
|
+
} catch(e) {
|
|
4238
|
+
log_error(`elbow edge: no bounds for node ${node && node.id}: ${e}`);
|
|
4239
|
+
let c = node.getCenter();
|
|
4240
|
+
return {x1: c[0], y1: c[1], x2: c[0], y2: c[1]};
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
|
|
4244
|
+
/* The lane of an edge among the edges joining the same two cards,
|
|
4245
|
+
* either way round (treedb_elbow.js). */
|
|
4246
|
+
function elbow_lane_of(edge)
|
|
4247
|
+
{
|
|
4248
|
+
let graph = edge.context && edge.context.graph;
|
|
4249
|
+
let s = edge.sourceNode && edge.sourceNode.id;
|
|
4250
|
+
let t = edge.targetNode && edge.targetNode.id;
|
|
4251
|
+
if(!graph || s === undefined || t === undefined || s === t) {
|
|
4252
|
+
return 0;
|
|
4253
|
+
}
|
|
4254
|
+
try {
|
|
4255
|
+
let ids = graph.getRelatedEdgesData(s)
|
|
4256
|
+
.filter((d) => (d.source === s && d.target === t) ||
|
|
4257
|
+
(d.source === t && d.target === s))
|
|
4258
|
+
.map((d) => d.id);
|
|
4259
|
+
return elbow_lane(ids, edge.id);
|
|
4260
|
+
} catch(e) {
|
|
4261
|
+
log_error(`elbow edge ${edge.id}: cannot read the edges of ${s}: ${e}`);
|
|
4262
|
+
return 0;
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
|
|
4266
|
+
class TreedbElbowV extends Polyline
|
|
4267
|
+
{
|
|
4268
|
+
getControlPoints(attributes) {
|
|
4269
|
+
let [s, t] = this.getEndpoints(attributes, false);
|
|
4270
|
+
return elbow_points(s, t, elbow_box(this.sourceNode), elbow_box(this.targetNode),
|
|
4271
|
+
elbow_lane_of(this), true);
|
|
4272
|
+
}
|
|
4273
|
+
|
|
4274
|
+
getKeyPath(attributes) {
|
|
4275
|
+
return super.getKeyPath(Object.assign({}, attributes,
|
|
4276
|
+
{radius: attributes.radius || ELBOW_RADIUS}));
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
|
|
4280
|
+
class TreedbElbowH extends Polyline
|
|
4281
|
+
{
|
|
4282
|
+
getControlPoints(attributes) {
|
|
4283
|
+
let [s, t] = this.getEndpoints(attributes, false);
|
|
4284
|
+
return elbow_points(s, t, elbow_box(this.sourceNode), elbow_box(this.targetNode),
|
|
4285
|
+
elbow_lane_of(this), false);
|
|
4286
|
+
}
|
|
4287
|
+
|
|
4288
|
+
getKeyPath(attributes) {
|
|
4289
|
+
return super.getKeyPath(Object.assign({}, attributes,
|
|
4290
|
+
{radius: attributes.radius || ELBOW_RADIUS}));
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
|
|
4202
4294
|
class TreedbCompactLayout extends BaseLayout
|
|
4203
4295
|
{
|
|
4204
4296
|
async execute(data, options) {
|
|
@@ -10875,6 +10967,25 @@ function ac_set_node_labels(gobj, event, kw, src)
|
|
|
10875
10967
|
return 0;
|
|
10876
10968
|
}
|
|
10877
10969
|
|
|
10970
|
+
/************************************************************
|
|
10971
|
+
* Curved or elbow edges (the host's toggle). Nothing moves: every
|
|
10972
|
+
* edge takes the type that goes with the shape and the layout,
|
|
10973
|
+
* and draws again.
|
|
10974
|
+
************************************************************/
|
|
10975
|
+
function ac_set_edge_shape(gobj, event, kw, src)
|
|
10976
|
+
{
|
|
10977
|
+
let priv = gobj.priv;
|
|
10978
|
+
|
|
10979
|
+
let shape = (kw && kw.edge_shape === "elbow")? "elbow" : "curved";
|
|
10980
|
+
gobj_write_str_attr(gobj, "edge_shape", shape);
|
|
10981
|
+
if(!priv.graph || !priv.graph_rendered) {
|
|
10982
|
+
return 0; /* every edge is typed as it is drawn */
|
|
10983
|
+
}
|
|
10984
|
+
apply_layout_direction(gobj);
|
|
10985
|
+
graph_draw(gobj);
|
|
10986
|
+
return 0;
|
|
10987
|
+
}
|
|
10988
|
+
|
|
10878
10989
|
/************************************************************
|
|
10879
10990
|
* ONE node opened or closed against the mode (a double click,
|
|
10880
10991
|
* or the context menu). It holds still on screen while the
|
|
@@ -11518,6 +11629,7 @@ function create_gclass(gclass_name)
|
|
|
11518
11629
|
["EV_SET_LOOSE_TOPICS", ac_set_loose_topics, null],
|
|
11519
11630
|
["EV_SET_NODE_MODE", ac_set_node_mode, null],
|
|
11520
11631
|
["EV_SET_NODE_LABELS", ac_set_node_labels, null],
|
|
11632
|
+
["EV_SET_EDGE_SHAPE", ac_set_edge_shape, null],
|
|
11521
11633
|
["EV_TOGGLE_NODE_MODE", ac_toggle_node_mode, null],
|
|
11522
11634
|
["EV_NODE_DBLCLICK", ac_node_dblclick, null],
|
|
11523
11635
|
["EV_CENTER", ac_center, null],
|
|
@@ -11585,6 +11697,7 @@ function create_gclass(gclass_name)
|
|
|
11585
11697
|
["EV_SET_LOOSE_TOPICS", 0],
|
|
11586
11698
|
["EV_SET_NODE_MODE", 0],
|
|
11587
11699
|
["EV_SET_NODE_LABELS", 0],
|
|
11700
|
+
["EV_SET_EDGE_SHAPE", 0],
|
|
11588
11701
|
["EV_TOGGLE_NODE_MODE", 0],
|
|
11589
11702
|
["EV_NODE_DBLCLICK", 0],
|
|
11590
11703
|
["EV_LEGEND_STATE", event_flag_t.EVF_OUTPUT_EVENT],
|