@yuneta/gobj-ui 7.23.79 → 7.23.81
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 +37 -0
- package/dist/gobj-ui.cjs.js +393 -12
- package/dist/gobj-ui.es.js +394 -13
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +499 -19
- package/src/lib_graph.css +2 -0
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -137,6 +137,7 @@ import {
|
|
|
137
137
|
HistoryEvent,
|
|
138
138
|
EdgeEvent,
|
|
139
139
|
Circle,
|
|
140
|
+
HTML,
|
|
140
141
|
Toolbar,
|
|
141
142
|
register,
|
|
142
143
|
} from '@antv/g6';
|
|
@@ -446,6 +447,8 @@ let PRIVATE_DATA = {
|
|
|
446
447
|
_edge_icon_el: null, // floating properties icon element
|
|
447
448
|
_edge_delete_el: null, // floating delete icon element for edge
|
|
448
449
|
_edge_popover_el: null, // edge properties popover element
|
|
450
|
+
_port_icon_el: null, // floating port properties icon element
|
|
451
|
+
_port_popover_el: null, // port properties popover element
|
|
449
452
|
_node_icon_el: null, // floating node properties icon element
|
|
450
453
|
_node_delete_el: null, // floating delete icon element for node
|
|
451
454
|
_node_popover_el: null, // node properties popover element
|
|
@@ -839,6 +842,7 @@ function register_layouts(gobj)
|
|
|
839
842
|
register(ExtensionCategory.LAYOUT, 'treedb-tree', TreedbTreeLayout);
|
|
840
843
|
register(ExtensionCategory.LAYOUT, 'treedb-radial', TreedbRadialLayout);
|
|
841
844
|
register(ExtensionCategory.NODE, 'light', LightNode);
|
|
845
|
+
register(ExtensionCategory.NODE, 'treedb-card', TreedbCard);
|
|
842
846
|
}
|
|
843
847
|
}
|
|
844
848
|
|
|
@@ -940,6 +944,7 @@ function configure_events(gobj)
|
|
|
940
944
|
update_edge_icon_position(gobj);
|
|
941
945
|
update_node_icon_position(gobj);
|
|
942
946
|
update_link_icon_position(gobj);
|
|
947
|
+
update_port_icon_position(gobj);
|
|
943
948
|
});
|
|
944
949
|
|
|
945
950
|
graph.on(NodeEvent.DRAG_END, (evt) => {
|
|
@@ -974,6 +979,7 @@ function configure_events(gobj)
|
|
|
974
979
|
update_edge_icon_position(gobj);
|
|
975
980
|
update_node_icon_position(gobj);
|
|
976
981
|
update_link_icon_position(gobj);
|
|
982
|
+
update_port_icon_position(gobj);
|
|
977
983
|
update_zoom_readout(gobj);
|
|
978
984
|
|
|
979
985
|
/*
|
|
@@ -2132,12 +2138,32 @@ function card_shape_of(gobj, desc, record, geometry, pills_html)
|
|
|
2132
2138
|
}
|
|
2133
2139
|
}
|
|
2134
2140
|
}
|
|
2141
|
+
|
|
2142
|
+
/* And the SHAPE of each port, the same way: the node's own,
|
|
2143
|
+
* then the topic's per-port default, then the topic's one
|
|
2144
|
+
* shape for every port (`all ports` in the port popover). */
|
|
2145
|
+
let port_shapes = geometry.port_shapes;
|
|
2146
|
+
let default_port_shapes = topic_defaults ? topic_defaults.port_shapes : null;
|
|
2147
|
+
let default_shape = topic_defaults ? topic_defaults.port_shape : null;
|
|
2148
|
+
for(let i = 0; i < style.ports.length; i++) {
|
|
2149
|
+
let key = style.ports[i].key;
|
|
2150
|
+
let shape = is_object(port_shapes) ? port_shapes[key] : null;
|
|
2151
|
+
if(!shape && is_object(default_port_shapes)) {
|
|
2152
|
+
shape = default_port_shapes[key];
|
|
2153
|
+
}
|
|
2154
|
+
if(!shape) {
|
|
2155
|
+
shape = default_shape;
|
|
2156
|
+
}
|
|
2157
|
+
if(shape && str_in_list(PORT_SHAPES, shape)) {
|
|
2158
|
+
style.ports[i].shape = shape;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2135
2161
|
} else {
|
|
2136
2162
|
style.port = false;
|
|
2137
2163
|
style.ports = [];
|
|
2138
2164
|
}
|
|
2139
2165
|
|
|
2140
|
-
return {type: '
|
|
2166
|
+
return {type: 'treedb-card', style: style};
|
|
2141
2167
|
}
|
|
2142
2168
|
|
|
2143
2169
|
/************************************************************
|
|
@@ -2732,26 +2758,62 @@ function update_geometry(gobj, node_id)
|
|
|
2732
2758
|
topic_props.nodes = {};
|
|
2733
2759
|
}
|
|
2734
2760
|
|
|
2735
|
-
|
|
2761
|
+
/* Extract geometry from render style -- the POSITION always, the
|
|
2762
|
+
* SIZE only when somebody chose it. A card on the tier's default
|
|
2763
|
+
* used to be saved with that default, so every Save froze the
|
|
2764
|
+
* library's size of the day into `__graphs__` and a later default
|
|
2765
|
+
* (bigger ports, 7.23.75/79) reached no saved treedb: the same
|
|
2766
|
+
* trap as the invented cascade coordinates, for the size. And a
|
|
2767
|
+
* CLOSED node is a square: its render size is not the card's and
|
|
2768
|
+
* is never saved as one -- the entry keeps what it had. */
|
|
2736
2769
|
let node_props = topic_props.nodes[record.id] || {};
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2770
|
+
let picked = kw_clone_by_keys(gobj, style, ["x", "y", "size", "portR"]);
|
|
2771
|
+
if(node_is_compact(gobj, node_id)) {
|
|
2772
|
+
delete picked.size;
|
|
2773
|
+
delete picked.portR;
|
|
2774
|
+
} else {
|
|
2775
|
+
let desc = nodedata.data.desc;
|
|
2776
|
+
let def_size = card_size_for(desc.node_treedb_type, !!pills_html_of(gobj, nodedata));
|
|
2777
|
+
if(is_array(picked.size) && picked.size[0] === def_size[0] &&
|
|
2778
|
+
picked.size[1] === def_size[1]) {
|
|
2779
|
+
delete picked.size;
|
|
2780
|
+
delete node_props.size;
|
|
2781
|
+
}
|
|
2782
|
+
let def_r = (desc.node_treedb_type === 'child')? PORT_R_CHILD : PORT_R_ENTITY;
|
|
2783
|
+
if(picked.portR === def_r) {
|
|
2784
|
+
delete picked.portR;
|
|
2785
|
+
delete node_props.portR;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
json_object_update(node_props, picked);
|
|
2741
2789
|
|
|
2742
2790
|
// Save per-port radius values (only ports with custom r)
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2791
|
+
if(!node_is_compact(gobj, node_id)) {
|
|
2792
|
+
let node_style = nodedata.style || {};
|
|
2793
|
+
let ports = node_style.ports || [];
|
|
2794
|
+
let port_sizes = {};
|
|
2795
|
+
for(let i = 0; i < ports.length; i++) {
|
|
2796
|
+
if(ports[i].r != null) {
|
|
2797
|
+
port_sizes[ports[i].key] = ports[i].r;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
if(Object.keys(port_sizes).length > 0) {
|
|
2801
|
+
node_props.port_sizes = port_sizes;
|
|
2802
|
+
} else {
|
|
2803
|
+
delete node_props.port_sizes;
|
|
2804
|
+
}
|
|
2805
|
+
// And the shape of each port that is not a circle
|
|
2806
|
+
let port_shapes = {};
|
|
2807
|
+
for(let i = 0; i < ports.length; i++) {
|
|
2808
|
+
if(ports[i].shape && ports[i].shape !== 'circle') {
|
|
2809
|
+
port_shapes[ports[i].key] = ports[i].shape;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
if(Object.keys(port_shapes).length > 0) {
|
|
2813
|
+
node_props.port_shapes = port_shapes;
|
|
2814
|
+
} else {
|
|
2815
|
+
delete node_props.port_shapes;
|
|
2749
2816
|
}
|
|
2750
|
-
}
|
|
2751
|
-
if(Object.keys(port_sizes).length > 0) {
|
|
2752
|
-
node_props.port_sizes = port_sizes;
|
|
2753
|
-
} else {
|
|
2754
|
-
delete node_props.port_sizes;
|
|
2755
2817
|
}
|
|
2756
2818
|
|
|
2757
2819
|
topic_props.nodes[record.id] = node_props;
|
|
@@ -3461,6 +3523,7 @@ function graph_resize(gobj, width, height)
|
|
|
3461
3523
|
update_port_resize_handles_position(gobj);
|
|
3462
3524
|
update_node_icon_position(gobj);
|
|
3463
3525
|
update_link_icon_position(gobj);
|
|
3526
|
+
update_port_icon_position(gobj);
|
|
3464
3527
|
}
|
|
3465
3528
|
|
|
3466
3529
|
function graph_write_behaviors(gobj, behaviors)
|
|
@@ -3496,6 +3559,64 @@ function graph_set_behavior(gobj, behavior, set)
|
|
|
3496
3559
|
});
|
|
3497
3560
|
}
|
|
3498
3561
|
|
|
3562
|
+
/************************************************************
|
|
3563
|
+
* Custom G6 node: TreedbCard -- G6's html node with ports that
|
|
3564
|
+
* have a SHAPE.
|
|
3565
|
+
*
|
|
3566
|
+
* G6 draws every port as a circle (`drawPortShapes` upserts a
|
|
3567
|
+
* `Circle`, nothing in the port style says otherwise). A port's
|
|
3568
|
+
* `shape` -- `circle`, `square`, `diamond`, `triangle` -- picks
|
|
3569
|
+
* the G shape here, by the names G6 registers itself, and `r`
|
|
3570
|
+
* stays the one size: the half-side of the square, the half-
|
|
3571
|
+
* diagonal of the diamond, the half-height of the triangle. The
|
|
3572
|
+
* hit test, the resize handles and the edge's landing point all
|
|
3573
|
+
* read `r` and none of them care about the outline.
|
|
3574
|
+
************************************************************/
|
|
3575
|
+
const PORT_SHAPES = ['circle', 'square', 'diamond', 'triangle'];
|
|
3576
|
+
|
|
3577
|
+
class TreedbCard extends HTML
|
|
3578
|
+
{
|
|
3579
|
+
drawPortShapes(attributes, container) {
|
|
3580
|
+
const portsStyle = this.getPortsStyle(attributes);
|
|
3581
|
+
Object.keys(portsStyle).forEach((key) => {
|
|
3582
|
+
const style = portsStyle[key];
|
|
3583
|
+
const shapeKey = `port-${key}`;
|
|
3584
|
+
if(style === false) {
|
|
3585
|
+
this.upsert(shapeKey, 'circle', false, container);
|
|
3586
|
+
return;
|
|
3587
|
+
}
|
|
3588
|
+
let r = Number(style.r) || 0;
|
|
3589
|
+
switch(style.shape) {
|
|
3590
|
+
case 'square':
|
|
3591
|
+
this.upsert(shapeKey, 'rect', Object.assign({}, style, {
|
|
3592
|
+
x: -r, y: -r, width: 2 * r, height: 2 * r,
|
|
3593
|
+
}), container);
|
|
3594
|
+
break;
|
|
3595
|
+
case 'diamond':
|
|
3596
|
+
this.upsert(shapeKey, 'polygon', Object.assign({}, style, {
|
|
3597
|
+
points: [[0, -r], [r, 0], [0, r], [-r, 0]],
|
|
3598
|
+
}), container);
|
|
3599
|
+
break;
|
|
3600
|
+
case 'triangle':
|
|
3601
|
+
this.upsert(shapeKey, 'polygon', Object.assign({}, style, {
|
|
3602
|
+
points: [[0, -r], [r, r], [-r, r]],
|
|
3603
|
+
}), container);
|
|
3604
|
+
break;
|
|
3605
|
+
default:
|
|
3606
|
+
this.upsert(shapeKey, 'circle', style, container);
|
|
3607
|
+
break;
|
|
3608
|
+
}
|
|
3609
|
+
});
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
/* A record's card is `treedb-card`; `html` is what it was until
|
|
3614
|
+
* 7.23.81 and what a `+N` chip still is. */
|
|
3615
|
+
function is_card_type(type)
|
|
3616
|
+
{
|
|
3617
|
+
return type === 'treedb-card' || type === 'html';
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3499
3620
|
/************************************************************
|
|
3500
3621
|
* Custom G6 node: LightNode (circle with status indicator)
|
|
3501
3622
|
************************************************************/
|
|
@@ -4637,7 +4758,7 @@ function start_node_resize(gobj, e, mx, my)
|
|
|
4637
4758
|
}
|
|
4638
4759
|
|
|
4639
4760
|
// HTML nodes: update dx, dy to keep content centered
|
|
4640
|
-
if(nodeType
|
|
4761
|
+
if(is_card_type(nodeType)) {
|
|
4641
4762
|
updateStyle.dx = -newW / 2;
|
|
4642
4763
|
updateStyle.dy = -newH / 2;
|
|
4643
4764
|
}
|
|
@@ -4818,6 +4939,7 @@ function select_port(gobj, node_id, port_key)
|
|
|
4818
4939
|
|
|
4819
4940
|
show_port_resize_handles(gobj);
|
|
4820
4941
|
show_link_icon_if_fkey(gobj);
|
|
4942
|
+
show_port_icon(gobj);
|
|
4821
4943
|
}
|
|
4822
4944
|
|
|
4823
4945
|
/************************************************************
|
|
@@ -4829,6 +4951,8 @@ function deselect_port(gobj)
|
|
|
4829
4951
|
|
|
4830
4952
|
exit_linking_mode(gobj);
|
|
4831
4953
|
hide_link_icon(gobj);
|
|
4954
|
+
hide_port_icon(gobj);
|
|
4955
|
+
hide_port_popover(gobj);
|
|
4832
4956
|
hide_port_resize_handles(gobj);
|
|
4833
4957
|
priv._selected_port_key = null;
|
|
4834
4958
|
}
|
|
@@ -4916,6 +5040,272 @@ function update_link_icon_position(gobj)
|
|
|
4916
5040
|
}
|
|
4917
5041
|
}
|
|
4918
5042
|
|
|
5043
|
+
/************************************************************
|
|
5044
|
+
* Port properties icon and popover: the gear beside a selected
|
|
5045
|
+
* port, as the node has its own. The popover has the two things
|
|
5046
|
+
* a port IS on screen -- its shape and its radius -- and the
|
|
5047
|
+
* scope: this port, the same port of every card of the topic
|
|
5048
|
+
* (the hook or fkey column), or every port there is.
|
|
5049
|
+
************************************************************/
|
|
5050
|
+
function port_icon_xy(gobj)
|
|
5051
|
+
{
|
|
5052
|
+
let priv = gobj.priv;
|
|
5053
|
+
let graph = priv.graph;
|
|
5054
|
+
let canvasPos = get_port_canvas_position(
|
|
5055
|
+
gobj, priv._selected_node_id, priv._selected_port_key
|
|
5056
|
+
);
|
|
5057
|
+
if(!canvasPos) {
|
|
5058
|
+
return null;
|
|
5059
|
+
}
|
|
5060
|
+
let vp = graph.getViewportByCanvas([canvasPos.x, canvasPos.y]);
|
|
5061
|
+
/* Under the link icon when there is one (an fkey), in its
|
|
5062
|
+
* place otherwise. */
|
|
5063
|
+
let below = priv._link_icon_el? floating_icon_step() : 0;
|
|
5064
|
+
return {
|
|
5065
|
+
x: vp[0] + floating_icon_size() / 2 + 4,
|
|
5066
|
+
y: vp[1] + floating_icon_dy() + below,
|
|
5067
|
+
};
|
|
5068
|
+
}
|
|
5069
|
+
|
|
5070
|
+
function show_port_icon(gobj)
|
|
5071
|
+
{
|
|
5072
|
+
hide_port_icon(gobj);
|
|
5073
|
+
|
|
5074
|
+
let priv = gobj.priv;
|
|
5075
|
+
if(!priv._selected_node_id || !priv._selected_port_key) {
|
|
5076
|
+
return;
|
|
5077
|
+
}
|
|
5078
|
+
let xy = port_icon_xy(gobj);
|
|
5079
|
+
if(!xy) {
|
|
5080
|
+
return;
|
|
5081
|
+
}
|
|
5082
|
+
let icon = create_floating_icon(
|
|
5083
|
+
'gear', '#1890ff', xy.x, xy.y,
|
|
5084
|
+
t('port properties'), () => toggle_port_popover(gobj)
|
|
5085
|
+
);
|
|
5086
|
+
priv.$container.appendChild(icon);
|
|
5087
|
+
priv._port_icon_el = icon;
|
|
5088
|
+
}
|
|
5089
|
+
|
|
5090
|
+
function hide_port_icon(gobj)
|
|
5091
|
+
{
|
|
5092
|
+
hide_overlay(gobj, '_port_icon_el');
|
|
5093
|
+
}
|
|
5094
|
+
|
|
5095
|
+
function update_port_icon_position(gobj)
|
|
5096
|
+
{
|
|
5097
|
+
let priv = gobj.priv;
|
|
5098
|
+
if(!priv._port_icon_el || !priv._selected_node_id || !priv._selected_port_key) {
|
|
5099
|
+
return;
|
|
5100
|
+
}
|
|
5101
|
+
try {
|
|
5102
|
+
let xy = port_icon_xy(gobj);
|
|
5103
|
+
if(!xy) {
|
|
5104
|
+
hide_port_icon(gobj);
|
|
5105
|
+
hide_port_popover(gobj);
|
|
5106
|
+
return;
|
|
5107
|
+
}
|
|
5108
|
+
priv._port_icon_el.style.left = xy.x + 'px';
|
|
5109
|
+
priv._port_icon_el.style.top = xy.y + 'px';
|
|
5110
|
+
if(priv._port_popover_el) {
|
|
5111
|
+
priv._port_popover_el.style.left = (xy.x + floating_popover_dx()) + 'px';
|
|
5112
|
+
priv._port_popover_el.style.top = xy.y + 'px';
|
|
5113
|
+
clamp_popover_position(gobj, priv._port_popover_el);
|
|
5114
|
+
}
|
|
5115
|
+
} catch(e) {
|
|
5116
|
+
hide_port_icon(gobj);
|
|
5117
|
+
hide_port_popover(gobj);
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
|
|
5121
|
+
function toggle_port_popover(gobj)
|
|
5122
|
+
{
|
|
5123
|
+
let priv = gobj.priv;
|
|
5124
|
+
if(priv._port_popover_el) {
|
|
5125
|
+
hide_port_popover(gobj);
|
|
5126
|
+
} else {
|
|
5127
|
+
show_port_popover(gobj);
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
|
|
5131
|
+
function hide_port_popover(gobj)
|
|
5132
|
+
{
|
|
5133
|
+
hide_overlay(gobj, '_port_popover_el');
|
|
5134
|
+
}
|
|
5135
|
+
|
|
5136
|
+
/* The ports of a node with ONE of them rewritten. */
|
|
5137
|
+
function ports_with(ports, port_key, changes)
|
|
5138
|
+
{
|
|
5139
|
+
return (ports || []).map((p) => (p.key === port_key)? Object.assign({}, p, changes) : p);
|
|
5140
|
+
}
|
|
5141
|
+
|
|
5142
|
+
function show_port_popover(gobj)
|
|
5143
|
+
{
|
|
5144
|
+
hide_port_popover(gobj);
|
|
5145
|
+
|
|
5146
|
+
let priv = gobj.priv;
|
|
5147
|
+
let graph = priv.graph;
|
|
5148
|
+
let node_id = priv._selected_node_id;
|
|
5149
|
+
let port_key = priv._selected_port_key;
|
|
5150
|
+
if(!node_id || !port_key) {
|
|
5151
|
+
return;
|
|
5152
|
+
}
|
|
5153
|
+
let nodeData = graph.getNodeData(node_id);
|
|
5154
|
+
if(!nodeData || !nodeData.style) {
|
|
5155
|
+
return;
|
|
5156
|
+
}
|
|
5157
|
+
let ports = nodeData.style.ports || [];
|
|
5158
|
+
let port = ports.find((p) => p.key === port_key);
|
|
5159
|
+
if(!port) {
|
|
5160
|
+
return;
|
|
5161
|
+
}
|
|
5162
|
+
let orig_shape = port.shape || 'circle';
|
|
5163
|
+
let orig_r = get_port_radius(gobj, node_id, port_key);
|
|
5164
|
+
|
|
5165
|
+
let xy = port_icon_xy(gobj);
|
|
5166
|
+
if(!xy) {
|
|
5167
|
+
return;
|
|
5168
|
+
}
|
|
5169
|
+
const popover = create_popover_base(
|
|
5170
|
+
xy.x + floating_popover_dx(), xy.y, 'g6-port-popover', '#d9d9d9', 180
|
|
5171
|
+
);
|
|
5172
|
+
|
|
5173
|
+
/* Live preview on THIS port; the others wait for apply. */
|
|
5174
|
+
function preview_port() {
|
|
5175
|
+
let r = parseInt(rInput.value) || orig_r;
|
|
5176
|
+
graph.updateNodeData([{id: node_id, style: {
|
|
5177
|
+
ports: ports_with(nodeData.style.ports, port_key, {shape: shapeSelect.value, r: r}),
|
|
5178
|
+
}}]);
|
|
5179
|
+
graph.draw().then(() => {
|
|
5180
|
+
update_port_resize_handles_position(gobj);
|
|
5181
|
+
});
|
|
5182
|
+
}
|
|
5183
|
+
|
|
5184
|
+
create_form_label(popover, 'shape');
|
|
5185
|
+
let shapeSelect = create_form_select(popover, PORT_SHAPES.map((shape) => {
|
|
5186
|
+
return {value: shape, label: t(shape)};
|
|
5187
|
+
}));
|
|
5188
|
+
shapeSelect.value = orig_shape;
|
|
5189
|
+
shapeSelect.addEventListener('change', preview_port);
|
|
5190
|
+
|
|
5191
|
+
create_form_label(popover, 'radius');
|
|
5192
|
+
let rInput = create_form_number_input(popover, orig_r, 2, 40, preview_port);
|
|
5193
|
+
|
|
5194
|
+
create_form_label(popover, 'apply to');
|
|
5195
|
+
let scopeSelect = create_form_select(popover, [
|
|
5196
|
+
{value: 'this', label: t('this port')},
|
|
5197
|
+
{value: 'same_topic', label: t('same port in topic')},
|
|
5198
|
+
{value: 'all', label: t('all ports')},
|
|
5199
|
+
]);
|
|
5200
|
+
|
|
5201
|
+
create_form_button_row(popover, [
|
|
5202
|
+
{
|
|
5203
|
+
text: 'cancel',
|
|
5204
|
+
style: BTN_STYLE_CANCEL,
|
|
5205
|
+
onClick: () => {
|
|
5206
|
+
graph.updateNodeData([{id: node_id, style: {
|
|
5207
|
+
ports: ports_with(nodeData.style.ports, port_key, {shape: orig_shape, r: orig_r}),
|
|
5208
|
+
}}]);
|
|
5209
|
+
graph.draw().then(() => {
|
|
5210
|
+
update_port_resize_handles_position(gobj);
|
|
5211
|
+
});
|
|
5212
|
+
hide_port_popover(gobj);
|
|
5213
|
+
},
|
|
5214
|
+
},
|
|
5215
|
+
{
|
|
5216
|
+
text: 'apply',
|
|
5217
|
+
style: 'flex:1;padding:6px;background:#1890ff;color:#fff;border:none;' +
|
|
5218
|
+
'border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;',
|
|
5219
|
+
onClick: () => {
|
|
5220
|
+
apply_port_properties(gobj, node_id, port_key,
|
|
5221
|
+
shapeSelect.value, parseInt(rInput.value) || orig_r, scopeSelect.value
|
|
5222
|
+
);
|
|
5223
|
+
},
|
|
5224
|
+
},
|
|
5225
|
+
]);
|
|
5226
|
+
|
|
5227
|
+
priv.$container.appendChild(popover);
|
|
5228
|
+
priv._port_popover_el = popover;
|
|
5229
|
+
clamp_popover_position(gobj, popover);
|
|
5230
|
+
}
|
|
5231
|
+
|
|
5232
|
+
/************************************************************
|
|
5233
|
+
* Apply a port's shape and radius to one port, to the same port
|
|
5234
|
+
* of every card of the topic, or to every port -- and remember
|
|
5235
|
+
* the choice as the topic's default, the way `resize all ports`
|
|
5236
|
+
* does, so a card that arrives later is born with it.
|
|
5237
|
+
* scope: 'this', 'same_topic', 'all'
|
|
5238
|
+
************************************************************/
|
|
5239
|
+
function apply_port_properties(gobj, node_id, port_key, shape, r, scope)
|
|
5240
|
+
{
|
|
5241
|
+
let priv = gobj.priv;
|
|
5242
|
+
let graph = priv.graph;
|
|
5243
|
+
|
|
5244
|
+
let nodeData = graph.getNodeData(node_id);
|
|
5245
|
+
if(!nodeData || !nodeData.data) {
|
|
5246
|
+
return;
|
|
5247
|
+
}
|
|
5248
|
+
let source_topic = nodeData.data.topic_name;
|
|
5249
|
+
|
|
5250
|
+
if(scope === 'same_topic') {
|
|
5251
|
+
if(!is_object(priv._graph_properties[source_topic])) {
|
|
5252
|
+
priv._graph_properties[source_topic] = {};
|
|
5253
|
+
}
|
|
5254
|
+
let defaults = priv._graph_properties[source_topic].defaults || {};
|
|
5255
|
+
defaults.port_sizes = Object.assign({}, defaults.port_sizes, {[port_key]: r});
|
|
5256
|
+
defaults.port_shapes = Object.assign({}, defaults.port_shapes, {[port_key]: shape});
|
|
5257
|
+
priv._graph_properties[source_topic].defaults = defaults;
|
|
5258
|
+
} else if(scope === 'all') {
|
|
5259
|
+
for(const topic_name of Object.keys(priv.descs || {})) {
|
|
5260
|
+
if(!is_object(priv._graph_properties[topic_name])) {
|
|
5261
|
+
priv._graph_properties[topic_name] = {};
|
|
5262
|
+
}
|
|
5263
|
+
let defaults = priv._graph_properties[topic_name].defaults || {};
|
|
5264
|
+
defaults.portR = r;
|
|
5265
|
+
defaults.port_shape = shape;
|
|
5266
|
+
delete defaults.port_sizes;
|
|
5267
|
+
delete defaults.port_shapes;
|
|
5268
|
+
priv._graph_properties[topic_name].defaults = defaults;
|
|
5269
|
+
}
|
|
5270
|
+
}
|
|
5271
|
+
|
|
5272
|
+
let updates = [];
|
|
5273
|
+
for(let nd of (graph.getNodeData() || [])) {
|
|
5274
|
+
if(!nd || !nd.data || !nd.data.desc || !nd.style || !is_array(nd.style.ports)) {
|
|
5275
|
+
continue;
|
|
5276
|
+
}
|
|
5277
|
+
let new_ports;
|
|
5278
|
+
if(scope === 'this') {
|
|
5279
|
+
if(nd.id !== node_id) {
|
|
5280
|
+
continue;
|
|
5281
|
+
}
|
|
5282
|
+
new_ports = ports_with(nd.style.ports, port_key, {shape: shape, r: r});
|
|
5283
|
+
} else if(scope === 'same_topic') {
|
|
5284
|
+
if(nd.data.topic_name !== source_topic) {
|
|
5285
|
+
continue;
|
|
5286
|
+
}
|
|
5287
|
+
new_ports = ports_with(nd.style.ports, port_key, {shape: shape, r: r});
|
|
5288
|
+
} else {
|
|
5289
|
+
new_ports = nd.style.ports.map((p) => Object.assign({}, p, {shape: shape, r: r}));
|
|
5290
|
+
}
|
|
5291
|
+
let upd = {ports: new_ports};
|
|
5292
|
+
if(scope === 'all') {
|
|
5293
|
+
upd.portR = r;
|
|
5294
|
+
}
|
|
5295
|
+
updates.push({id: nd.id, style: upd});
|
|
5296
|
+
}
|
|
5297
|
+
|
|
5298
|
+
if(updates.length > 0) {
|
|
5299
|
+
graph.updateNodeData(updates);
|
|
5300
|
+
graph.draw().then(() => {
|
|
5301
|
+
update_port_resize_handles_position(gobj);
|
|
5302
|
+
update_port_icon_position(gobj);
|
|
5303
|
+
mark_graph_dirty(gobj);
|
|
5304
|
+
});
|
|
5305
|
+
}
|
|
5306
|
+
hide_port_popover(gobj);
|
|
5307
|
+
}
|
|
5308
|
+
|
|
4919
5309
|
/************************************************************
|
|
4920
5310
|
* Get a column descriptor by field id from a topic desc.
|
|
4921
5311
|
************************************************************/
|
|
@@ -7410,6 +7800,11 @@ function build_node_context_menu(gobj, node_id)
|
|
|
7410
7800
|
if(gobj.priv.edit_mode && !node_is_compact(gobj, node_id)) {
|
|
7411
7801
|
items.push(ctx_item('g6-icon-resize', t('resize all'), 'resize_all_nodes'));
|
|
7412
7802
|
items.push(ctx_item('g6-icon-resize', t('resize topic nodes'), 'resize_topic_nodes'));
|
|
7803
|
+
/* The way back: the sizes somebody saved -- or that a Save
|
|
7804
|
+
* froze before 7.23.80 -- forgotten, the library's defaults
|
|
7805
|
+
* back on every card. */
|
|
7806
|
+
items.push(ctx_item('g6-icon-undo', t('reset sizes'), 'reset_all_sizes'));
|
|
7807
|
+
items.push(ctx_item('g6-icon-undo', t('reset topic sizes'), 'reset_topic_sizes'));
|
|
7413
7808
|
}
|
|
7414
7809
|
|
|
7415
7810
|
return items;
|
|
@@ -7424,6 +7819,9 @@ function build_port_context_menu(gobj, node_id, port_key)
|
|
|
7424
7819
|
|
|
7425
7820
|
if(gobj.priv.edit_mode) {
|
|
7426
7821
|
inject_svg_icons();
|
|
7822
|
+
/* The popover, for a finger: the gear beside the port needs
|
|
7823
|
+
* the port selected first, and a long press is the menu. */
|
|
7824
|
+
items.push(ctx_item('g6-icon-edit', t('port properties'), 'port_properties'));
|
|
7427
7825
|
items.push(ctx_item('g6-icon-resize', t('resize all ports'), 'resize_all_ports'));
|
|
7428
7826
|
items.push(ctx_item('g6-icon-resize', t('resize topic ports'), 'resize_topic_ports'));
|
|
7429
7827
|
}
|
|
@@ -7460,6 +7858,18 @@ function handle_context_menu_click(gobj, value)
|
|
|
7460
7858
|
case 'resize_topic_nodes':
|
|
7461
7859
|
copy_size_to_nodes(gobj, true);
|
|
7462
7860
|
break;
|
|
7861
|
+
case 'reset_all_sizes':
|
|
7862
|
+
reset_sizes(gobj, false);
|
|
7863
|
+
break;
|
|
7864
|
+
case 'reset_topic_sizes':
|
|
7865
|
+
reset_sizes(gobj, true);
|
|
7866
|
+
break;
|
|
7867
|
+
case 'port_properties':
|
|
7868
|
+
if(priv._context_node_id && priv._context_port_key) {
|
|
7869
|
+
select_port(gobj, priv._context_node_id, priv._context_port_key);
|
|
7870
|
+
show_port_popover(gobj);
|
|
7871
|
+
}
|
|
7872
|
+
break;
|
|
7463
7873
|
case 'resize_all_ports':
|
|
7464
7874
|
copy_size_to_ports(gobj, false);
|
|
7465
7875
|
break;
|
|
@@ -7546,7 +7956,7 @@ function copy_size_to_nodes(gobj, same_topic_only)
|
|
|
7546
7956
|
}
|
|
7547
7957
|
|
|
7548
7958
|
// Recalculate dx/dy for HTML nodes
|
|
7549
|
-
if(nd.type
|
|
7959
|
+
if(is_card_type(nd.type)) {
|
|
7550
7960
|
updateStyle.dx = -source_size[0] / 2;
|
|
7551
7961
|
let h = source_size.length > 1 ? source_size[1] : source_size[0];
|
|
7552
7962
|
updateStyle.dy = -h / 2;
|
|
@@ -7564,6 +7974,75 @@ function copy_size_to_nodes(gobj, same_topic_only)
|
|
|
7564
7974
|
}
|
|
7565
7975
|
}
|
|
7566
7976
|
|
|
7977
|
+
/************************************************************
|
|
7978
|
+
* Forget every SAVED size: the card sizes, the port radii and the
|
|
7979
|
+
* per-port radii, per node and as topic defaults (`resize all`),
|
|
7980
|
+
* for one topic or for all of them. The cards go back to the
|
|
7981
|
+
* library's defaults on the spot, and Save writes the cleared
|
|
7982
|
+
* entries -- a size nobody chose is not written back (see
|
|
7983
|
+
* update_geometry). Positions and edge styles are not touched:
|
|
7984
|
+
* they are a different choice.
|
|
7985
|
+
************************************************************/
|
|
7986
|
+
function reset_sizes(gobj, same_topic_only)
|
|
7987
|
+
{
|
|
7988
|
+
let priv = gobj.priv;
|
|
7989
|
+
let graph = priv.graph;
|
|
7990
|
+
|
|
7991
|
+
let topic = "";
|
|
7992
|
+
if(same_topic_only) {
|
|
7993
|
+
let nd = priv._context_node_id? graph.getNodeData(priv._context_node_id) : null;
|
|
7994
|
+
if(!nd || !nd.data || !nd.data.topic_name) {
|
|
7995
|
+
log_error(`${gobj_short_name(gobj)}: reset topic sizes without a node`);
|
|
7996
|
+
return;
|
|
7997
|
+
}
|
|
7998
|
+
topic = nd.data.topic_name;
|
|
7999
|
+
}
|
|
8000
|
+
const SIZE_KEYS = ["size", "portR", "port_sizes"];
|
|
8001
|
+
let strip = (props) => {
|
|
8002
|
+
if(!is_object(props)) {
|
|
8003
|
+
return;
|
|
8004
|
+
}
|
|
8005
|
+
for(let k of SIZE_KEYS) {
|
|
8006
|
+
delete props[k];
|
|
8007
|
+
}
|
|
8008
|
+
};
|
|
8009
|
+
|
|
8010
|
+
/* The saved entries, on screen or folded away. */
|
|
8011
|
+
for(const [topic_name, props] of Object.entries(priv._graph_properties || {})) {
|
|
8012
|
+
if(topic && topic_name !== topic) {
|
|
8013
|
+
continue;
|
|
8014
|
+
}
|
|
8015
|
+
if(is_object(props.nodes)) {
|
|
8016
|
+
for(let node_props of Object.values(props.nodes)) {
|
|
8017
|
+
strip(node_props);
|
|
8018
|
+
}
|
|
8019
|
+
}
|
|
8020
|
+
strip(props.defaults);
|
|
8021
|
+
}
|
|
8022
|
+
|
|
8023
|
+
/* The nodes on screen: their own copy of the entry, then the shape. */
|
|
8024
|
+
let ids = [];
|
|
8025
|
+
for(let nd of (graph.getNodeData() || [])) {
|
|
8026
|
+
if(!nd || !nd.data || !nd.data.desc || is_more_node(nd)) {
|
|
8027
|
+
continue;
|
|
8028
|
+
}
|
|
8029
|
+
if(topic && nd.data.topic_name !== topic) {
|
|
8030
|
+
continue;
|
|
8031
|
+
}
|
|
8032
|
+
strip(nd.data.graph_props);
|
|
8033
|
+
if(nd.data.record) {
|
|
8034
|
+
strip(nd.data.record._geometry);
|
|
8035
|
+
}
|
|
8036
|
+
ids.push(nd.id);
|
|
8037
|
+
}
|
|
8038
|
+
reshape_nodes(gobj, ids);
|
|
8039
|
+
graph.draw().then(() => {
|
|
8040
|
+
update_resize_handles_position(gobj);
|
|
8041
|
+
update_port_resize_handles_position(gobj);
|
|
8042
|
+
mark_graph_dirty(gobj);
|
|
8043
|
+
});
|
|
8044
|
+
}
|
|
8045
|
+
|
|
7567
8046
|
/************************************************************
|
|
7568
8047
|
* Copy the selected port's radius to matching ports.
|
|
7569
8048
|
* If same_topic_only=true, only ports in same-topic nodes.
|
|
@@ -8314,6 +8793,7 @@ async function reconcile_fold_apply(gobj, opts, visible)
|
|
|
8314
8793
|
update_edge_icon_position(gobj);
|
|
8315
8794
|
update_node_icon_position(gobj);
|
|
8316
8795
|
update_link_icon_position(gobj);
|
|
8796
|
+
update_port_icon_position(gobj);
|
|
8317
8797
|
|
|
8318
8798
|
return changed;
|
|
8319
8799
|
}
|
package/src/lib_graph.css
CHANGED
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
.graph-container .g6-create-popover,
|
|
87
87
|
.graph-container .g6-node-popover,
|
|
88
88
|
.graph-container .g6-edge-popover,
|
|
89
|
+
.graph-container .g6-port-popover,
|
|
89
90
|
.graph-container .g6-node-detail {
|
|
90
91
|
-webkit-user-select: text;
|
|
91
92
|
user-select: text;
|
|
@@ -99,6 +100,7 @@
|
|
|
99
100
|
.graph-container .g6-create-popover,
|
|
100
101
|
.graph-container .g6-node-popover,
|
|
101
102
|
.graph-container .g6-edge-popover,
|
|
103
|
+
.graph-container .g6-port-popover,
|
|
102
104
|
.graph-container .g6-node-detail,
|
|
103
105
|
.graph-container .g6-contextmenu {
|
|
104
106
|
touch-action: auto;
|