@yuneta/gobj-ui 7.23.132 → 7.23.134
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/dist/gobj-ui.cjs.js +313 -89
- package/dist/gobj-ui.es.js +313 -89
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +428 -112
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -290,7 +290,7 @@ const PORT_LINE_WIDTH = 2;
|
|
|
290
290
|
* gobj tree view has: `expanded` is the card with its pills and
|
|
291
291
|
* its ports; `compact` is a one-line pill with the name inside
|
|
292
292
|
* and small ports, the half-way house; `shape` is a figure of the
|
|
293
|
-
* topic's colour -- a
|
|
293
|
+
* topic's colour -- a circle, a square, a diamond... chosen in the
|
|
294
294
|
* node's properties -- no ports, no text, topology and nothing
|
|
295
295
|
* else. The three tiers keep their order of size in every view,
|
|
296
296
|
* so a root is still bigger than a leaf when nothing says which
|
|
@@ -311,9 +311,11 @@ const SHAPE_SIZE = {
|
|
|
311
311
|
child: 22,
|
|
312
312
|
};
|
|
313
313
|
/* The figures a `shape` node can be, by G6's own node types
|
|
314
|
-
* (`square` is G6's `rect` with rounded corners).
|
|
314
|
+
* (`square` is G6's `rect` with rounded corners). A circle by
|
|
315
|
+
* default: a figure is a POINT of the topology, and a circle is
|
|
316
|
+
* the one figure that says nothing more than that. */
|
|
315
317
|
const NODE_SHAPES = ['square', 'circle', 'diamond', 'triangle', 'hexagon', 'star'];
|
|
316
|
-
const DEFAULT_NODE_SHAPE = '
|
|
318
|
+
const DEFAULT_NODE_SHAPE = 'circle';
|
|
317
319
|
|
|
318
320
|
/***************************************************************
|
|
319
321
|
* Internal layout and operation mode definitions
|
|
@@ -519,6 +521,7 @@ let PRIVATE_DATA = {
|
|
|
519
521
|
_focus_topic: null, // topic currently focused (EV_FOCUS_TOPIC)
|
|
520
522
|
_focus_ids: [], // node ids carrying the focus 'active' state
|
|
521
523
|
_on_pointerdown_focus: null, // listener keeping the keyboard on the canvas
|
|
524
|
+
_on_native_contextmenu: null, // listener keeping the browser's menu out
|
|
522
525
|
_uninstall_long_press: null, // touch door to the context menu
|
|
523
526
|
toolbar_collapsed: true, // floating toolbars folded (narrow only)
|
|
524
527
|
_toolbars_could_collapse: null, // last answer, to notice it changed
|
|
@@ -704,6 +707,13 @@ function mt_destroy(gobj)
|
|
|
704
707
|
priv._on_focusout_restore = null;
|
|
705
708
|
}
|
|
706
709
|
|
|
710
|
+
if(priv._on_native_contextmenu) {
|
|
711
|
+
priv.$container.removeEventListener(
|
|
712
|
+
"contextmenu", priv._on_native_contextmenu, true
|
|
713
|
+
);
|
|
714
|
+
priv._on_native_contextmenu = null;
|
|
715
|
+
}
|
|
716
|
+
|
|
707
717
|
if(priv.theme_observer) {
|
|
708
718
|
priv.theme_observer.disconnect();
|
|
709
719
|
priv.theme_observer = null;
|
|
@@ -1141,6 +1151,32 @@ function configure_events(gobj)
|
|
|
1141
1151
|
};
|
|
1142
1152
|
priv.$container.addEventListener("focusout", priv._on_focusout_restore);
|
|
1143
1153
|
|
|
1154
|
+
/* The browser's own menu never opens over the graph.
|
|
1155
|
+
*
|
|
1156
|
+
* The context-menu plugin calls preventDefault() on the event
|
|
1157
|
+
* it is handed, and that event is the one @antv/g SYNTHESISES
|
|
1158
|
+
* from `pointerdown` with `button === 2` -- not the DOM's
|
|
1159
|
+
* `contextmenu`, which nobody cancelled. So a right click opened
|
|
1160
|
+
* both menus, and where ours had nothing to show (an edge, the
|
|
1161
|
+
* empty canvas) it opened the browser's alone. A form field of a
|
|
1162
|
+
* popover keeps it: that is where cut and paste live. */
|
|
1163
|
+
if(priv._on_native_contextmenu) {
|
|
1164
|
+
priv.$container.removeEventListener(
|
|
1165
|
+
"contextmenu", priv._on_native_contextmenu, true
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
priv._on_native_contextmenu = (ev) => {
|
|
1169
|
+
let target = ev.target;
|
|
1170
|
+
if(target && typeof target.closest === "function" &&
|
|
1171
|
+
target.closest("input, textarea, select, [contenteditable]")) {
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
ev.preventDefault();
|
|
1175
|
+
};
|
|
1176
|
+
priv.$container.addEventListener(
|
|
1177
|
+
"contextmenu", priv._on_native_contextmenu, true
|
|
1178
|
+
);
|
|
1179
|
+
|
|
1144
1180
|
/* The canvas carries a `tabIndex` of its own, so a keydown reaches
|
|
1145
1181
|
* us only while the GRAPH has focus. That is what keeps Ctrl+A in
|
|
1146
1182
|
* the find box a text selection and not a selection of every node:
|
|
@@ -1205,7 +1241,12 @@ function configure_plugins(gobj)
|
|
|
1205
1241
|
* press below, so the gesture arms itself on exactly what the
|
|
1206
1242
|
* menu would agree to open on. */
|
|
1207
1243
|
let menu_enable = (e) => {
|
|
1208
|
-
|
|
1244
|
+
if(e.targetType === 'node') {
|
|
1245
|
+
return true;
|
|
1246
|
+
}
|
|
1247
|
+
/* An edge's commands (its properties, unlink) are edition
|
|
1248
|
+
* commands: in reading mode it has nothing to offer. */
|
|
1249
|
+
return e.targetType === 'edge' && !!priv.edit_mode;
|
|
1209
1250
|
};
|
|
1210
1251
|
|
|
1211
1252
|
graph_add_plugin(
|
|
@@ -2400,7 +2441,7 @@ function pill_innerHTML_of(gobj, nd, theme, highlight, selected, anchored)
|
|
|
2400
2441
|
|
|
2401
2442
|
/* The figure a `shape` node wears: the node's own, then the
|
|
2402
2443
|
* topic's default (the node properties popover writes both), then
|
|
2403
|
-
* a
|
|
2444
|
+
* a circle. */
|
|
2404
2445
|
function node_figure_of(gobj, desc, geometry)
|
|
2405
2446
|
{
|
|
2406
2447
|
let shape = geometry? geometry.node_shape : null;
|
|
@@ -2447,13 +2488,14 @@ function figure_shape_of(gobj, desc, record, geometry, flags)
|
|
|
2447
2488
|
let fill = dark? mix_colors(paint.fill, 0.25, "#2c3542")
|
|
2448
2489
|
: mix_colors(paint.fill, 0.10, "#ffffff");
|
|
2449
2490
|
let stroke = dark? mix_colors(paint.fill, 0.85, "#ffffff") : paint.fill;
|
|
2450
|
-
|
|
2491
|
+
/* The card's and the pill's line width, 1 unless somebody chose
|
|
2492
|
+
* another: the three views of a record wear the same outline. */
|
|
2493
|
+
let line_width = paint.lineWidth;
|
|
2451
2494
|
/* A paint somebody CHOSE (a node's or a topic's own, saved in
|
|
2452
2495
|
* `__graphs__`) is not ours to reinterpret: it comes through as
|
|
2453
2496
|
* it was chosen. */
|
|
2454
2497
|
if(paint.stroke !== getStrokeColor(paint.fill)) {
|
|
2455
2498
|
stroke = paint.stroke;
|
|
2456
|
-
line_width = Math.max(paint.lineWidth, 2);
|
|
2457
2499
|
}
|
|
2458
2500
|
if(f.selected) {
|
|
2459
2501
|
stroke = SELECT_RING;
|
|
@@ -2798,57 +2840,17 @@ function draw_link(
|
|
|
2798
2840
|
* Create the edge with independent id and semantic data
|
|
2799
2841
|
* HACK: source/target are interchanged so arrows point parent -> child
|
|
2800
2842
|
*/
|
|
2801
|
-
// Doc-style default: neutral grey edges, teal for the
|
|
2802
|
-
// containment/tree relation (parent and child same topic, i.e.
|
|
2803
|
-
// a self-hierarchy). Saturated topic colour is dropped.
|
|
2804
|
-
// `themed_default` marks edges still on the default colour so a
|
|
2805
|
-
// theme toggle can re-theme them without touching user-saved ones.
|
|
2806
|
-
let dark = (priv.theme === "dark");
|
|
2807
2843
|
let is_tree = (parent_topic === child_topic);
|
|
2808
|
-
let
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
:
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
let edge_props = topic_props.edges[edge_key];
|
|
2819
|
-
if(edge_props) {
|
|
2820
|
-
if(edge_props.lineWidth != null) {
|
|
2821
|
-
saved_lineWidth = edge_props.lineWidth;
|
|
2822
|
-
}
|
|
2823
|
-
if(edge_props.stroke) {
|
|
2824
|
-
saved_stroke = edge_props.stroke;
|
|
2825
|
-
themed_default = false;
|
|
2826
|
-
}
|
|
2827
|
-
}
|
|
2828
|
-
}
|
|
2829
|
-
// Fall back to topic defaults (only if not user-overridden)
|
|
2830
|
-
if(themed_default && is_object(topic_props.defaults)) {
|
|
2831
|
-
let defs = topic_props.defaults;
|
|
2832
|
-
if(defs.edge_styles && defs.edge_styles[hook_name]) {
|
|
2833
|
-
let hook_style = defs.edge_styles[hook_name];
|
|
2834
|
-
if(hook_style.lineWidth != null) {
|
|
2835
|
-
saved_lineWidth = hook_style.lineWidth;
|
|
2836
|
-
}
|
|
2837
|
-
if(hook_style.stroke) {
|
|
2838
|
-
saved_stroke = hook_style.stroke;
|
|
2839
|
-
themed_default = false;
|
|
2840
|
-
}
|
|
2841
|
-
} else if(defs.edge_style) {
|
|
2842
|
-
if(defs.edge_style.lineWidth != null) {
|
|
2843
|
-
saved_lineWidth = defs.edge_style.lineWidth;
|
|
2844
|
-
}
|
|
2845
|
-
if(defs.edge_style.stroke) {
|
|
2846
|
-
saved_stroke = defs.edge_style.stroke;
|
|
2847
|
-
themed_default = false;
|
|
2848
|
-
}
|
|
2849
|
-
}
|
|
2850
|
-
}
|
|
2851
|
-
}
|
|
2844
|
+
let edge_style = edge_style_of(gobj, {
|
|
2845
|
+
parent_topic: parent_topic,
|
|
2846
|
+
parent_id: parent_id,
|
|
2847
|
+
hook_name: hook_name,
|
|
2848
|
+
child_id: child_id,
|
|
2849
|
+
is_tree: is_tree,
|
|
2850
|
+
});
|
|
2851
|
+
let saved_lineWidth = edge_style.lineWidth;
|
|
2852
|
+
let saved_stroke = edge_style.stroke;
|
|
2853
|
+
let themed_default = edge_style.themed_default;
|
|
2852
2854
|
|
|
2853
2855
|
let edge = {
|
|
2854
2856
|
id: build_edge_id(gobj),
|
|
@@ -6804,6 +6806,7 @@ function show_node_popover(gobj)
|
|
|
6804
6806
|
shapeSelect.value = nodeData.data && nodeData.data.desc
|
|
6805
6807
|
? node_figure_of(gobj, nodeData.data.desc, nodeData.data.graph_props || {})
|
|
6806
6808
|
: DEFAULT_NODE_SHAPE;
|
|
6809
|
+
let orig_figure = shapeSelect.value;
|
|
6807
6810
|
|
|
6808
6811
|
// Apply-to scope
|
|
6809
6812
|
create_form_label(popover, 'apply to');
|
|
@@ -6832,7 +6835,10 @@ function show_node_popover(gobj)
|
|
|
6832
6835
|
strokeInput.value,
|
|
6833
6836
|
parseInt(lwInput.value) || 1,
|
|
6834
6837
|
scopeSelect.value,
|
|
6835
|
-
|
|
6838
|
+
/* Only a figure somebody CHOSE is written: the
|
|
6839
|
+
* one the select opened on is today's default,
|
|
6840
|
+
* and saving it would freeze it on the node. */
|
|
6841
|
+
(shapeSelect.value !== orig_figure)? shapeSelect.value : ""
|
|
6836
6842
|
);
|
|
6837
6843
|
},
|
|
6838
6844
|
},
|
|
@@ -8256,9 +8262,89 @@ function apply_edge_properties(gobj, edge_id, lineWidth, stroke, scope)
|
|
|
8256
8262
|
deselect_edge(gobj);
|
|
8257
8263
|
}
|
|
8258
8264
|
|
|
8265
|
+
/************************************************************
|
|
8266
|
+
* The style an edge INHERITS, before anything saved for the
|
|
8267
|
+
* edge itself: its hook's default in the parent topic, else the
|
|
8268
|
+
* parent topic's default for every edge, else the library's.
|
|
8269
|
+
*
|
|
8270
|
+
* The library's: neutral grey, teal for the containment/tree
|
|
8271
|
+
* relation (parent and child of the same topic, a
|
|
8272
|
+
* self-hierarchy). `themed_default` says the colour is still the
|
|
8273
|
+
* library's, so a theme toggle re-themes it and leaves a chosen
|
|
8274
|
+
* one alone.
|
|
8275
|
+
************************************************************/
|
|
8276
|
+
function edge_inherited_style_of(gobj, d)
|
|
8277
|
+
{
|
|
8278
|
+
let priv = gobj.priv;
|
|
8279
|
+
let dark = (priv.theme === "dark");
|
|
8280
|
+
let style = {
|
|
8281
|
+
lineWidth: d.is_tree ? 2 : 1.6,
|
|
8282
|
+
stroke: d.is_tree
|
|
8283
|
+
? (dark ? '#22a7c2' : '#0e7490')
|
|
8284
|
+
: (dark ? '#8b94a3' : '#6b7280'),
|
|
8285
|
+
themed_default: true,
|
|
8286
|
+
};
|
|
8287
|
+
|
|
8288
|
+
let defs = topic_defaults_of(gobj, d.parent_topic);
|
|
8289
|
+
if(!defs) {
|
|
8290
|
+
return style;
|
|
8291
|
+
}
|
|
8292
|
+
let chosen = null;
|
|
8293
|
+
if(is_object(defs.edge_styles) && is_object(defs.edge_styles[d.hook_name])) {
|
|
8294
|
+
chosen = defs.edge_styles[d.hook_name];
|
|
8295
|
+
} else if(is_object(defs.edge_style)) {
|
|
8296
|
+
chosen = defs.edge_style;
|
|
8297
|
+
}
|
|
8298
|
+
if(chosen) {
|
|
8299
|
+
if(chosen.lineWidth != null) {
|
|
8300
|
+
style.lineWidth = chosen.lineWidth;
|
|
8301
|
+
}
|
|
8302
|
+
if(chosen.stroke) {
|
|
8303
|
+
style.stroke = chosen.stroke;
|
|
8304
|
+
style.themed_default = false;
|
|
8305
|
+
}
|
|
8306
|
+
}
|
|
8307
|
+
return style;
|
|
8308
|
+
}
|
|
8309
|
+
|
|
8310
|
+
/* The key of an edge's own entry in its parent topic's `edges`. */
|
|
8311
|
+
function edge_key_of(d)
|
|
8312
|
+
{
|
|
8313
|
+
return d.hook_name + ":" + d.parent_id + ":" + d.child_id;
|
|
8314
|
+
}
|
|
8315
|
+
|
|
8316
|
+
/************************************************************
|
|
8317
|
+
* The style an edge WEARS: what it inherits, with what was saved
|
|
8318
|
+
* for the edge itself on top.
|
|
8319
|
+
************************************************************/
|
|
8320
|
+
function edge_style_of(gobj, d)
|
|
8321
|
+
{
|
|
8322
|
+
let style = edge_inherited_style_of(gobj, d);
|
|
8323
|
+
let topic_props = gobj.priv._graph_properties[d.parent_topic];
|
|
8324
|
+
let own = (is_object(topic_props) && is_object(topic_props.edges))?
|
|
8325
|
+
topic_props.edges[edge_key_of(d)] : null;
|
|
8326
|
+
if(is_object(own)) {
|
|
8327
|
+
if(own.lineWidth != null) {
|
|
8328
|
+
style.lineWidth = own.lineWidth;
|
|
8329
|
+
}
|
|
8330
|
+
if(own.stroke) {
|
|
8331
|
+
style.stroke = own.stroke;
|
|
8332
|
+
style.themed_default = false;
|
|
8333
|
+
}
|
|
8334
|
+
}
|
|
8335
|
+
return style;
|
|
8336
|
+
}
|
|
8337
|
+
|
|
8259
8338
|
/************************************************************
|
|
8260
8339
|
* Save edge styles into _graph_properties (called from
|
|
8261
8340
|
* save_geometry).
|
|
8341
|
+
*
|
|
8342
|
+
* Only what differs from what the edge INHERITS is written. It
|
|
8343
|
+
* used to be "any line width but 2", and the library's own width
|
|
8344
|
+
* for an edge between two topics is 1.6: every Save wrote every
|
|
8345
|
+
* such edge down with its width and its theme's colour, so the
|
|
8346
|
+
* default was frozen on it -- a theme toggle no longer re-themed
|
|
8347
|
+
* it after a reload, and a topic default never reached it.
|
|
8262
8348
|
************************************************************/
|
|
8263
8349
|
function update_edge_geometry(gobj, edge_id)
|
|
8264
8350
|
{
|
|
@@ -8271,16 +8357,20 @@ function update_edge_geometry(gobj, edge_id)
|
|
|
8271
8357
|
}
|
|
8272
8358
|
|
|
8273
8359
|
let style = edgeData.style || {};
|
|
8274
|
-
let
|
|
8275
|
-
let
|
|
8360
|
+
let d = edgeData.data;
|
|
8361
|
+
let inherited = edge_inherited_style_of(gobj, d);
|
|
8362
|
+
let parent_topic = d.parent_topic;
|
|
8363
|
+
let edge_key = edge_key_of(d);
|
|
8276
8364
|
|
|
8277
|
-
let
|
|
8278
|
-
|
|
8279
|
-
|
|
8365
|
+
let edge_props = {};
|
|
8366
|
+
if(style.lineWidth != null && style.lineWidth !== inherited.lineWidth) {
|
|
8367
|
+
edge_props.lineWidth = style.lineWidth;
|
|
8368
|
+
}
|
|
8369
|
+
if(style.stroke && style.stroke !== inherited.stroke) {
|
|
8370
|
+
edge_props.stroke = style.stroke;
|
|
8371
|
+
}
|
|
8280
8372
|
|
|
8281
|
-
|
|
8282
|
-
let hasCustom = (lineWidth != null && lineWidth !== 2);
|
|
8283
|
-
if(!hasCustom) {
|
|
8373
|
+
if(Object.keys(edge_props).length === 0) {
|
|
8284
8374
|
let topic_props = priv._graph_properties[parent_topic];
|
|
8285
8375
|
if(topic_props && is_object(topic_props.edges)) {
|
|
8286
8376
|
delete topic_props.edges[edge_key];
|
|
@@ -8298,11 +8388,6 @@ function update_edge_geometry(gobj, edge_id)
|
|
|
8298
8388
|
if(!is_object(topic_props.edges)) {
|
|
8299
8389
|
topic_props.edges = {};
|
|
8300
8390
|
}
|
|
8301
|
-
|
|
8302
|
-
let edge_props = { lineWidth: lineWidth };
|
|
8303
|
-
if(stroke) {
|
|
8304
|
-
edge_props.stroke = stroke;
|
|
8305
|
-
}
|
|
8306
8391
|
topic_props.edges[edge_key] = edge_props;
|
|
8307
8392
|
}
|
|
8308
8393
|
|
|
@@ -8341,7 +8426,9 @@ function build_context_menu_items(gobj, e)
|
|
|
8341
8426
|
gobj, node_id, canvasPoint[0], canvasPoint[1]
|
|
8342
8427
|
);
|
|
8343
8428
|
|
|
8344
|
-
|
|
8429
|
+
/* A port's commands are edition commands; out of edition a
|
|
8430
|
+
* port is part of its node, and gets the node's menu. */
|
|
8431
|
+
if(port_key && priv.edit_mode) {
|
|
8345
8432
|
priv._context_port_key = port_key;
|
|
8346
8433
|
return build_port_context_menu(gobj, node_id, port_key);
|
|
8347
8434
|
} else {
|
|
@@ -8387,11 +8474,14 @@ function build_node_context_menu(gobj, node_id)
|
|
|
8387
8474
|
if(gobj.priv.edit_mode && !node_is_compact(gobj, node_id)) {
|
|
8388
8475
|
items.push(ctx_item('g6-icon-resize', t('resize all'), 'resize_all_nodes'));
|
|
8389
8476
|
items.push(ctx_item('g6-icon-resize', t('resize topic nodes'), 'resize_topic_nodes'));
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8477
|
+
}
|
|
8478
|
+
if(gobj.priv.edit_mode) {
|
|
8479
|
+
/* The way back, in every view: what the node properties and
|
|
8480
|
+
* the resizes saved -- or a Save froze before 7.23.80 --
|
|
8481
|
+
* forgotten, the library's defaults back. */
|
|
8482
|
+
items.push(ctx_item('g6-icon-undo', t('reset node'), 'reset_node'));
|
|
8483
|
+
items.push(ctx_item('g6-icon-undo', t('reset topic nodes'), 'reset_topic_nodes'));
|
|
8484
|
+
items.push(ctx_item('g6-icon-undo', t('reset all nodes'), 'reset_all_nodes'));
|
|
8395
8485
|
}
|
|
8396
8486
|
|
|
8397
8487
|
return items;
|
|
@@ -8411,18 +8501,33 @@ function build_port_context_menu(gobj, node_id, port_key)
|
|
|
8411
8501
|
items.push(ctx_item('g6-icon-edit', t('port properties'), 'port_properties'));
|
|
8412
8502
|
items.push(ctx_item('g6-icon-resize', t('resize all ports'), 'resize_all_ports'));
|
|
8413
8503
|
items.push(ctx_item('g6-icon-resize', t('resize topic ports'), 'resize_topic_ports'));
|
|
8504
|
+
/* The way back from the popover and the two above: the
|
|
8505
|
+
* library's own shape and radius. */
|
|
8506
|
+
items.push(ctx_item('g6-icon-undo', t('reset port'), 'reset_port'));
|
|
8507
|
+
items.push(ctx_item('g6-icon-undo', t('reset topic ports'), 'reset_topic_ports'));
|
|
8508
|
+
items.push(ctx_item('g6-icon-undo', t('reset all ports'), 'reset_all_ports'));
|
|
8414
8509
|
}
|
|
8415
8510
|
|
|
8416
8511
|
return items;
|
|
8417
8512
|
}
|
|
8418
8513
|
|
|
8419
8514
|
/************************************************************
|
|
8420
|
-
* Build edge context menu items
|
|
8515
|
+
* Build edge context menu items: the two floating icons of a
|
|
8516
|
+
* selected edge, for a right click and for a finger.
|
|
8421
8517
|
************************************************************/
|
|
8422
8518
|
function build_edge_context_menu(gobj)
|
|
8423
8519
|
{
|
|
8424
8520
|
let items = [];
|
|
8425
|
-
|
|
8521
|
+
|
|
8522
|
+
if(gobj.priv.edit_mode) {
|
|
8523
|
+
inject_svg_icons();
|
|
8524
|
+
items.push(ctx_item('g6-icon-edit', t('edge properties'), 'edge_properties'));
|
|
8525
|
+
items.push(ctx_item('g6-icon-delete', t('unlink'), 'unlink_edge'));
|
|
8526
|
+
items.push(ctx_item('g6-icon-undo', t('reset edge'), 'reset_edge'));
|
|
8527
|
+
items.push(ctx_item('g6-icon-undo', t('reset same type edges'), 'reset_same_type_edges'));
|
|
8528
|
+
items.push(ctx_item('g6-icon-undo', t('reset all edges'), 'reset_all_edges'));
|
|
8529
|
+
}
|
|
8530
|
+
|
|
8426
8531
|
return items;
|
|
8427
8532
|
}
|
|
8428
8533
|
|
|
@@ -8445,11 +8550,23 @@ function handle_context_menu_click(gobj, value)
|
|
|
8445
8550
|
case 'resize_topic_nodes':
|
|
8446
8551
|
copy_size_to_nodes(gobj, true);
|
|
8447
8552
|
break;
|
|
8448
|
-
case '
|
|
8449
|
-
|
|
8553
|
+
case 'reset_node':
|
|
8554
|
+
reset_nodes(gobj, 'this');
|
|
8555
|
+
break;
|
|
8556
|
+
case 'reset_topic_nodes':
|
|
8557
|
+
reset_nodes(gobj, 'same_topic');
|
|
8558
|
+
break;
|
|
8559
|
+
case 'reset_all_nodes':
|
|
8560
|
+
reset_nodes(gobj, 'all');
|
|
8450
8561
|
break;
|
|
8451
|
-
case '
|
|
8452
|
-
|
|
8562
|
+
case 'reset_edge':
|
|
8563
|
+
reset_edges(gobj, 'this');
|
|
8564
|
+
break;
|
|
8565
|
+
case 'reset_same_type_edges':
|
|
8566
|
+
reset_edges(gobj, 'same_type');
|
|
8567
|
+
break;
|
|
8568
|
+
case 'reset_all_edges':
|
|
8569
|
+
reset_edges(gobj, 'all');
|
|
8453
8570
|
break;
|
|
8454
8571
|
case 'port_properties':
|
|
8455
8572
|
if(priv._context_node_id && priv._context_port_key) {
|
|
@@ -8463,6 +8580,32 @@ function handle_context_menu_click(gobj, value)
|
|
|
8463
8580
|
case 'resize_topic_ports':
|
|
8464
8581
|
copy_size_to_ports(gobj, true);
|
|
8465
8582
|
break;
|
|
8583
|
+
case 'reset_port':
|
|
8584
|
+
reset_ports(gobj, 'this');
|
|
8585
|
+
break;
|
|
8586
|
+
case 'reset_topic_ports':
|
|
8587
|
+
reset_ports(gobj, 'same_topic');
|
|
8588
|
+
break;
|
|
8589
|
+
case 'reset_all_ports':
|
|
8590
|
+
reset_ports(gobj, 'all');
|
|
8591
|
+
break;
|
|
8592
|
+
case 'edge_properties':
|
|
8593
|
+
if(priv._context_edge_id) {
|
|
8594
|
+
deselect_node(gobj);
|
|
8595
|
+
select_edge(gobj, priv._context_edge_id);
|
|
8596
|
+
show_edge_popover(gobj);
|
|
8597
|
+
}
|
|
8598
|
+
break;
|
|
8599
|
+
case 'unlink_edge':
|
|
8600
|
+
if(priv._context_edge_id) {
|
|
8601
|
+
deselect_node(gobj);
|
|
8602
|
+
select_edge(gobj, priv._context_edge_id);
|
|
8603
|
+
request_unlink_edge(gobj);
|
|
8604
|
+
}
|
|
8605
|
+
break;
|
|
8606
|
+
default:
|
|
8607
|
+
log_error(`${gobj_short_name(gobj)}: unknown context menu item: ${value}`);
|
|
8608
|
+
break;
|
|
8466
8609
|
}
|
|
8467
8610
|
}
|
|
8468
8611
|
|
|
@@ -8561,40 +8704,47 @@ function copy_size_to_nodes(gobj, same_topic_only)
|
|
|
8561
8704
|
}
|
|
8562
8705
|
}
|
|
8563
8706
|
|
|
8707
|
+
/*
|
|
8708
|
+
* What a reset forgets, by what it resets. A node's own look is
|
|
8709
|
+
* its size, its colours, its line width and its figure; its ports
|
|
8710
|
+
* have their own reset. The POSITION is not a property and no reset
|
|
8711
|
+
* touches it: it is where somebody put the node.
|
|
8712
|
+
*/
|
|
8713
|
+
const NODE_STYLE_KEYS = ["size", "fill", "stroke", "lineWidth", "node_shape"];
|
|
8714
|
+
const PORT_STYLE_KEYS = ["portR", "port_sizes", "port_shapes", "port_shape"];
|
|
8715
|
+
|
|
8564
8716
|
/************************************************************
|
|
8565
|
-
* Forget
|
|
8566
|
-
*
|
|
8567
|
-
*
|
|
8568
|
-
*
|
|
8569
|
-
*
|
|
8570
|
-
*
|
|
8571
|
-
* they are a different choice.
|
|
8717
|
+
* Forget these saved keys -- on the node of the menu alone
|
|
8718
|
+
* ('this'), or on every node of its topic and in the topic's
|
|
8719
|
+
* defaults ('same_topic'), or everywhere ('all'). Both copies of
|
|
8720
|
+
* an entry are stripped: the one in `_graph_properties` (which
|
|
8721
|
+
* Save writes, folded nodes included) and the one the node
|
|
8722
|
+
* carries. Returns the ids of the nodes on screen to reshape.
|
|
8572
8723
|
************************************************************/
|
|
8573
|
-
function
|
|
8724
|
+
function forget_saved_node_keys(gobj, nd, scope, keys)
|
|
8574
8725
|
{
|
|
8575
8726
|
let priv = gobj.priv;
|
|
8576
8727
|
let graph = priv.graph;
|
|
8577
|
-
|
|
8578
|
-
let topic = "";
|
|
8579
|
-
if(same_topic_only) {
|
|
8580
|
-
let nd = priv._context_node_id? graph.getNodeData(priv._context_node_id) : null;
|
|
8581
|
-
if(!nd || !nd.data || !nd.data.topic_name) {
|
|
8582
|
-
log_error(`${gobj_short_name(gobj)}: reset topic sizes without a node`);
|
|
8583
|
-
return;
|
|
8584
|
-
}
|
|
8585
|
-
topic = nd.data.topic_name;
|
|
8586
|
-
}
|
|
8587
|
-
const SIZE_KEYS = ["size", "portR", "port_sizes"];
|
|
8588
8728
|
let strip = (props) => {
|
|
8589
8729
|
if(!is_object(props)) {
|
|
8590
8730
|
return;
|
|
8591
8731
|
}
|
|
8592
|
-
for(let k of
|
|
8732
|
+
for(let k of keys) {
|
|
8593
8733
|
delete props[k];
|
|
8594
8734
|
}
|
|
8595
8735
|
};
|
|
8596
8736
|
|
|
8597
|
-
|
|
8737
|
+
if(scope === 'this') {
|
|
8738
|
+
strip(node_props_entry(gobj, nd));
|
|
8739
|
+
strip(nd.data.graph_props);
|
|
8740
|
+
if(nd.data.record) {
|
|
8741
|
+
strip(nd.data.record._geometry);
|
|
8742
|
+
}
|
|
8743
|
+
return [nd.id];
|
|
8744
|
+
}
|
|
8745
|
+
|
|
8746
|
+
let topic = (scope === 'same_topic')? nd.data.topic_name : "";
|
|
8747
|
+
|
|
8598
8748
|
for(const [topic_name, props] of Object.entries(priv._graph_properties || {})) {
|
|
8599
8749
|
if(topic && topic_name !== topic) {
|
|
8600
8750
|
continue;
|
|
@@ -8607,25 +8757,191 @@ function reset_sizes(gobj, same_topic_only)
|
|
|
8607
8757
|
strip(props.defaults);
|
|
8608
8758
|
}
|
|
8609
8759
|
|
|
8610
|
-
/* The nodes on screen: their own copy of the entry, then the shape. */
|
|
8611
8760
|
let ids = [];
|
|
8612
|
-
for(let
|
|
8613
|
-
if(!
|
|
8761
|
+
for(let n of (graph.getNodeData() || [])) {
|
|
8762
|
+
if(!n || !n.data || !n.data.desc || is_more_node(n)) {
|
|
8614
8763
|
continue;
|
|
8615
8764
|
}
|
|
8616
|
-
if(topic &&
|
|
8765
|
+
if(topic && n.data.topic_name !== topic) {
|
|
8617
8766
|
continue;
|
|
8618
8767
|
}
|
|
8619
|
-
strip(
|
|
8620
|
-
if(
|
|
8621
|
-
strip(
|
|
8768
|
+
strip(n.data.graph_props);
|
|
8769
|
+
if(n.data.record) {
|
|
8770
|
+
strip(n.data.record._geometry);
|
|
8622
8771
|
}
|
|
8623
|
-
ids.push(
|
|
8772
|
+
ids.push(n.id);
|
|
8624
8773
|
}
|
|
8774
|
+
return ids;
|
|
8775
|
+
}
|
|
8776
|
+
|
|
8777
|
+
/* The nodes are reshaped from what is left saved, and Save writes
|
|
8778
|
+
* the cleared entries: a value nobody chose is not written back
|
|
8779
|
+
* (see update_geometry). */
|
|
8780
|
+
function reshape_after_reset(gobj, ids)
|
|
8781
|
+
{
|
|
8782
|
+
let graph = gobj.priv.graph;
|
|
8625
8783
|
reshape_nodes(gobj, ids);
|
|
8626
8784
|
graph.draw().then(() => {
|
|
8627
8785
|
update_resize_handles_position(gobj);
|
|
8628
8786
|
update_port_resize_handles_position(gobj);
|
|
8787
|
+
update_port_icon_position(gobj);
|
|
8788
|
+
mark_graph_dirty(gobj);
|
|
8789
|
+
});
|
|
8790
|
+
}
|
|
8791
|
+
|
|
8792
|
+
/************************************************************
|
|
8793
|
+
* Forget the saved look of nodes (NODE_STYLE_KEYS), back to the
|
|
8794
|
+
* library's: this node, its topic's, or all of them. A figure
|
|
8795
|
+
* saved as a square before the circle became the default goes
|
|
8796
|
+
* back to the default this way.
|
|
8797
|
+
************************************************************/
|
|
8798
|
+
function reset_nodes(gobj, scope)
|
|
8799
|
+
{
|
|
8800
|
+
let priv = gobj.priv;
|
|
8801
|
+
let nd = priv._context_node_id? priv.graph.getNodeData(priv._context_node_id) : null;
|
|
8802
|
+
if(!nd || !nd.data || !nd.data.topic_name) {
|
|
8803
|
+
log_error(`${gobj_short_name(gobj)}: reset nodes without a node`);
|
|
8804
|
+
return;
|
|
8805
|
+
}
|
|
8806
|
+
reshape_after_reset(gobj, forget_saved_node_keys(gobj, nd, scope, NODE_STYLE_KEYS));
|
|
8807
|
+
}
|
|
8808
|
+
|
|
8809
|
+
/************************************************************
|
|
8810
|
+
* Forget the saved SHAPE and RADIUS of ports, back to the
|
|
8811
|
+
* library's own:
|
|
8812
|
+
* 'this' the port of the menu, on its node only
|
|
8813
|
+
* 'same_topic' every port of the node's topic, with the
|
|
8814
|
+
* topic's port defaults
|
|
8815
|
+
* 'all' every port of every topic, likewise
|
|
8816
|
+
* The node's own look is not touched: that is `reset node`.
|
|
8817
|
+
************************************************************/
|
|
8818
|
+
function reset_ports(gobj, scope)
|
|
8819
|
+
{
|
|
8820
|
+
let priv = gobj.priv;
|
|
8821
|
+
let graph = priv.graph;
|
|
8822
|
+
|
|
8823
|
+
let node_id = priv._context_node_id;
|
|
8824
|
+
let port_key = priv._context_port_key;
|
|
8825
|
+
let nd = node_id? graph.getNodeData(node_id) : null;
|
|
8826
|
+
if(!nd || !nd.data || !nd.data.topic_name || !port_key) {
|
|
8827
|
+
log_error(`${gobj_short_name(gobj)}: reset ports without a port`);
|
|
8828
|
+
return;
|
|
8829
|
+
}
|
|
8830
|
+
|
|
8831
|
+
if(scope !== 'this') {
|
|
8832
|
+
reshape_after_reset(gobj, forget_saved_node_keys(gobj, nd, scope, PORT_STYLE_KEYS));
|
|
8833
|
+
return;
|
|
8834
|
+
}
|
|
8835
|
+
|
|
8836
|
+
/* One port: its entries in the per-port maps, not the maps. */
|
|
8837
|
+
let strip_port = (props) => {
|
|
8838
|
+
if(!is_object(props)) {
|
|
8839
|
+
return;
|
|
8840
|
+
}
|
|
8841
|
+
for(let k of ["port_sizes", "port_shapes"]) {
|
|
8842
|
+
if(is_object(props[k])) {
|
|
8843
|
+
delete props[k][port_key];
|
|
8844
|
+
if(Object.keys(props[k]).length === 0) {
|
|
8845
|
+
delete props[k];
|
|
8846
|
+
}
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
};
|
|
8850
|
+
strip_port(node_props_entry(gobj, nd));
|
|
8851
|
+
strip_port(nd.data.graph_props);
|
|
8852
|
+
if(nd.data.record) {
|
|
8853
|
+
strip_port(nd.data.record._geometry);
|
|
8854
|
+
}
|
|
8855
|
+
reshape_after_reset(gobj, [node_id]);
|
|
8856
|
+
}
|
|
8857
|
+
|
|
8858
|
+
/************************************************************
|
|
8859
|
+
* Forget the saved style of edges, back to what they inherit:
|
|
8860
|
+
* 'this' the edge of the menu, its own entry
|
|
8861
|
+
* 'same_type' every edge of its hook in its parent topic,
|
|
8862
|
+
* with that hook's default
|
|
8863
|
+
* 'all' every edge, with every edge default
|
|
8864
|
+
* The same three scopes as the edge properties popover.
|
|
8865
|
+
************************************************************/
|
|
8866
|
+
function reset_edges(gobj, scope)
|
|
8867
|
+
{
|
|
8868
|
+
let priv = gobj.priv;
|
|
8869
|
+
let graph = priv.graph;
|
|
8870
|
+
|
|
8871
|
+
let ed = priv._context_edge_id? graph.getEdgeData(priv._context_edge_id) : null;
|
|
8872
|
+
if(!ed || !ed.data || !ed.data.parent_topic) {
|
|
8873
|
+
log_error(`${gobj_short_name(gobj)}: reset edges without an edge`);
|
|
8874
|
+
return;
|
|
8875
|
+
}
|
|
8876
|
+
let src = ed.data;
|
|
8877
|
+
let hits = (d) => {
|
|
8878
|
+
if(!d || !d.parent_topic) {
|
|
8879
|
+
return false; /* a `+N` chip's line */
|
|
8880
|
+
}
|
|
8881
|
+
if(scope === 'this') {
|
|
8882
|
+
return edge_key_of(d) === edge_key_of(src) && d.parent_topic === src.parent_topic;
|
|
8883
|
+
}
|
|
8884
|
+
if(scope === 'same_type') {
|
|
8885
|
+
return d.parent_topic === src.parent_topic && d.hook_name === src.hook_name;
|
|
8886
|
+
}
|
|
8887
|
+
return true;
|
|
8888
|
+
};
|
|
8889
|
+
|
|
8890
|
+
/* The saved entries, on screen or folded away. */
|
|
8891
|
+
for(const [topic_name, props] of Object.entries(priv._graph_properties || {})) {
|
|
8892
|
+
if(!is_object(props)) {
|
|
8893
|
+
continue;
|
|
8894
|
+
}
|
|
8895
|
+
if(scope !== 'all' && topic_name !== src.parent_topic) {
|
|
8896
|
+
continue;
|
|
8897
|
+
}
|
|
8898
|
+
if(is_object(props.edges)) {
|
|
8899
|
+
for(let key of Object.keys(props.edges)) {
|
|
8900
|
+
let hook_name = key.split(":")[0];
|
|
8901
|
+
if(scope === 'this' && key !== edge_key_of(src)) {
|
|
8902
|
+
continue;
|
|
8903
|
+
}
|
|
8904
|
+
if(scope === 'same_type' && hook_name !== src.hook_name) {
|
|
8905
|
+
continue;
|
|
8906
|
+
}
|
|
8907
|
+
delete props.edges[key];
|
|
8908
|
+
}
|
|
8909
|
+
if(Object.keys(props.edges).length === 0) {
|
|
8910
|
+
delete props.edges;
|
|
8911
|
+
}
|
|
8912
|
+
}
|
|
8913
|
+
if(is_object(props.defaults)) {
|
|
8914
|
+
if(scope === 'same_type' && is_object(props.defaults.edge_styles)) {
|
|
8915
|
+
delete props.defaults.edge_styles[src.hook_name];
|
|
8916
|
+
if(Object.keys(props.defaults.edge_styles).length === 0) {
|
|
8917
|
+
delete props.defaults.edge_styles;
|
|
8918
|
+
}
|
|
8919
|
+
} else if(scope === 'all') {
|
|
8920
|
+
delete props.defaults.edge_styles;
|
|
8921
|
+
delete props.defaults.edge_style;
|
|
8922
|
+
}
|
|
8923
|
+
}
|
|
8924
|
+
}
|
|
8925
|
+
|
|
8926
|
+
/* The edges on screen, restyled from what is left. */
|
|
8927
|
+
let updates = [];
|
|
8928
|
+
for(let e of (graph.getEdgeData() || [])) {
|
|
8929
|
+
if(!e || !hits(e.data)) {
|
|
8930
|
+
continue;
|
|
8931
|
+
}
|
|
8932
|
+
let style = edge_style_of(gobj, e.data);
|
|
8933
|
+
updates.push({
|
|
8934
|
+
id: e.id,
|
|
8935
|
+
style: {lineWidth: style.lineWidth, stroke: style.stroke},
|
|
8936
|
+
data: Object.assign({}, e.data, {themed_default: style.themed_default}),
|
|
8937
|
+
});
|
|
8938
|
+
}
|
|
8939
|
+
|
|
8940
|
+
deselect_edge(gobj);
|
|
8941
|
+
if(updates.length > 0) {
|
|
8942
|
+
graph.updateEdgeData(updates);
|
|
8943
|
+
}
|
|
8944
|
+
graph.draw().then(() => {
|
|
8629
8945
|
mark_graph_dirty(gobj);
|
|
8630
8946
|
});
|
|
8631
8947
|
}
|