@yuneta/gobj-ui 7.23.131 → 7.23.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -0
- package/dist/gobj-ui.cjs.js +123 -12
- package/dist/gobj-ui.es.js +123 -12
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +190 -11
package/README.md
CHANGED
|
@@ -2290,6 +2290,56 @@ too; the shell's stylesheet raises Bulma's 30% alpha to 70% (2.47:1 → 7.96 in
|
|
|
2290
2290
|
dark) where Bulma declares the variable, since a `:root` override does not
|
|
2291
2291
|
reach it.
|
|
2292
2292
|
|
|
2293
|
+
### A control without a name is a bug: `title` + `aria-label`, always
|
|
2294
|
+
|
|
2295
|
+
**Every control this library draws — and every control an app draws with it —
|
|
2296
|
+
carries a `title` AND an `aria-label`, and both are translatable.** A control
|
|
2297
|
+
is any `input`, `select`, `textarea`, `button`, or anything that behaves as
|
|
2298
|
+
one. All four attributes are written where the control is built:
|
|
2299
|
+
|
|
2300
|
+
```js
|
|
2301
|
+
['button', {class: 'FOO_SAVE button',
|
|
2302
|
+
title: t('save'), 'data-i18n-title': 'save',
|
|
2303
|
+
'aria-label': t('save'), 'data-i18n-aria-label': 'save'}, …]
|
|
2304
|
+
```
|
|
2305
|
+
|
|
2306
|
+
It is a floor, not a preference. The ecosystem was swept against it — the
|
|
2307
|
+
deployed page of six SPAs, read control by control — and the target is **zero
|
|
2308
|
+
controls without a name**. Three things it exists to stop, each of which
|
|
2309
|
+
shipped:
|
|
2310
|
+
|
|
2311
|
+
| What looks like a name | Why it is not |
|
|
2312
|
+
|---|---|
|
|
2313
|
+
| A `<label>` **beside** the control (Bulma's `field`) | No `for`, no wrapping: it names the box for the eye and for nothing else |
|
|
2314
|
+
| `<label for="x">` over a control carrying only `name="x"` | `for` matches an **`id`**, which the control does not have — correct-looking source, no association |
|
|
2315
|
+
| A `placeholder` | Gone the moment something is typed; a reader need not announce it |
|
|
2316
|
+
| The visible text | Disappears with `is-hidden-mobile`; and where the text is the STATE (`on`/`off`), it does not say what the control DOES |
|
|
2317
|
+
|
|
2318
|
+
A `<label>` that **wraps** its control is the one shape that needs no
|
|
2319
|
+
`aria-label` — the label IS the accessible name. And a **literal**
|
|
2320
|
+
`aria-label` next to a visible `i18n` label is worse than none: it OVERRIDES
|
|
2321
|
+
the translated text for a reader.
|
|
2322
|
+
|
|
2323
|
+
Two things carry no attribute anyone can set, and are named after the render
|
|
2324
|
+
instead — and again on every rebuild, because a name written onto a widget's
|
|
2325
|
+
DOM is a race with the next one:
|
|
2326
|
+
|
|
2327
|
+
- **What a widget draws for itself**: Tabulator's header filters and its
|
|
2328
|
+
row-selection checkbox (`yui_tabulator_name_filters` /
|
|
2329
|
+
`yui_tabulator_name_row_selects`, hung off `columnsLoaded` and
|
|
2330
|
+
`renderComplete`), Tom Select's box in front of the `<select>` it hides
|
|
2331
|
+
(`name_form_control`).
|
|
2332
|
+
- **An `<option>`**, which is text like any other: it carries `data-i18n`,
|
|
2333
|
+
with `value` kept explicit — a translated option with no value tells the FSM
|
|
2334
|
+
to enter a mode that does not exist.
|
|
2335
|
+
|
|
2336
|
+
**How to check it.** Not with a grep: dump `title`/`aria-label` from the
|
|
2337
|
+
DEPLOYED DOM, resolve each control's name the way a reader does (`aria-label`
|
|
2338
|
+
→ `label[for]` → a wrapping `<label>` → the text → `title` → `placeholder`),
|
|
2339
|
+
then switch language and diff. A key that arrives as a VARIABLE — from a data
|
|
2340
|
+
table, a helper's argument, a local alias of `t()` — is invisible to
|
|
2341
|
+
`validate-locales` and shows up only there.
|
|
2342
|
+
|
|
2293
2343
|
### i18n: a string must be able to CHANGE language, not just be translated once
|
|
2294
2344
|
|
|
2295
2345
|
Passing a string through `t()` is **not** enough. `refresh_language()` only
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -55080,7 +55080,7 @@ var NODE_SHAPES = [
|
|
|
55080
55080
|
"hexagon",
|
|
55081
55081
|
"star"
|
|
55082
55082
|
];
|
|
55083
|
-
var DEFAULT_NODE_SHAPE = "
|
|
55083
|
+
var DEFAULT_NODE_SHAPE = "circle";
|
|
55084
55084
|
/***************************************************************
|
|
55085
55085
|
* Internal layout and operation mode definitions
|
|
55086
55086
|
***************************************************************/
|
|
@@ -55233,6 +55233,7 @@ var PRIVATE_DATA$4 = {
|
|
|
55233
55233
|
_focus_topic: null,
|
|
55234
55234
|
_focus_ids: [],
|
|
55235
55235
|
_on_pointerdown_focus: null,
|
|
55236
|
+
_on_native_contextmenu: null,
|
|
55236
55237
|
_uninstall_long_press: null,
|
|
55237
55238
|
toolbar_collapsed: true,
|
|
55238
55239
|
_toolbars_could_collapse: null,
|
|
@@ -55330,6 +55331,10 @@ function mt_destroy$4(gobj) {
|
|
|
55330
55331
|
priv.$container.removeEventListener("focusout", priv._on_focusout_restore);
|
|
55331
55332
|
priv._on_focusout_restore = null;
|
|
55332
55333
|
}
|
|
55334
|
+
if (priv._on_native_contextmenu) {
|
|
55335
|
+
priv.$container.removeEventListener("contextmenu", priv._on_native_contextmenu, true);
|
|
55336
|
+
priv._on_native_contextmenu = null;
|
|
55337
|
+
}
|
|
55333
55338
|
if (priv.theme_observer) {
|
|
55334
55339
|
priv.theme_observer.disconnect();
|
|
55335
55340
|
priv.theme_observer = null;
|
|
@@ -55533,6 +55538,13 @@ function configure_events(gobj) {
|
|
|
55533
55538
|
if ($canvas && $canvas.isConnected && document.activeElement !== $canvas) $canvas.focus({ preventScroll: true });
|
|
55534
55539
|
};
|
|
55535
55540
|
priv.$container.addEventListener("focusout", priv._on_focusout_restore);
|
|
55541
|
+
if (priv._on_native_contextmenu) priv.$container.removeEventListener("contextmenu", priv._on_native_contextmenu, true);
|
|
55542
|
+
priv._on_native_contextmenu = (ev) => {
|
|
55543
|
+
let target = ev.target;
|
|
55544
|
+
if (target && typeof target.closest === "function" && target.closest("input, textarea, select, [contenteditable]")) return;
|
|
55545
|
+
ev.preventDefault();
|
|
55546
|
+
};
|
|
55547
|
+
priv.$container.addEventListener("contextmenu", priv._on_native_contextmenu, true);
|
|
55536
55548
|
graph.on("keydown", (evt) => {
|
|
55537
55549
|
let ctrl = !!(evt.ctrlKey || evt.metaKey);
|
|
55538
55550
|
if (ctrl && (evt.key === "a" || evt.key === "A")) {
|
|
@@ -55566,7 +55578,8 @@ function configure_plugins(gobj) {
|
|
|
55566
55578
|
}
|
|
55567
55579
|
});
|
|
55568
55580
|
let menu_enable = (e) => {
|
|
55569
|
-
|
|
55581
|
+
if (e.targetType === "node") return true;
|
|
55582
|
+
return e.targetType === "edge" && !!priv.edit_mode;
|
|
55570
55583
|
};
|
|
55571
55584
|
graph_add_plugin(gobj, "contextmenu", {
|
|
55572
55585
|
trigger: "contextmenu",
|
|
@@ -56402,11 +56415,8 @@ function figure_shape_of(gobj, desc, record, geometry, flags) {
|
|
|
56402
56415
|
let paint = node_paint_of(gobj, desc, geometry);
|
|
56403
56416
|
let fill = dark ? mix_colors(paint.fill, .25, "#2c3542") : mix_colors(paint.fill, .1, "#ffffff");
|
|
56404
56417
|
let stroke = dark ? mix_colors(paint.fill, .85, "#ffffff") : paint.fill;
|
|
56405
|
-
let line_width =
|
|
56406
|
-
if (paint.stroke !== getStrokeColor(paint.fill))
|
|
56407
|
-
stroke = paint.stroke;
|
|
56408
|
-
line_width = Math.max(paint.lineWidth, 2);
|
|
56409
|
-
}
|
|
56418
|
+
let line_width = paint.lineWidth;
|
|
56419
|
+
if (paint.stroke !== getStrokeColor(paint.fill)) stroke = paint.stroke;
|
|
56410
56420
|
if (f.selected) {
|
|
56411
56421
|
stroke = SELECT_RING;
|
|
56412
56422
|
line_width = 3;
|
|
@@ -59358,6 +59368,7 @@ function show_node_popover(gobj) {
|
|
|
59358
59368
|
};
|
|
59359
59369
|
}));
|
|
59360
59370
|
shapeSelect.value = nodeData.data && nodeData.data.desc ? node_figure_of(gobj, nodeData.data.desc, nodeData.data.graph_props || {}) : DEFAULT_NODE_SHAPE;
|
|
59371
|
+
let orig_figure = shapeSelect.value;
|
|
59361
59372
|
create_form_label(popover, "apply to");
|
|
59362
59373
|
let scopeSelect = create_form_select(popover, [
|
|
59363
59374
|
{
|
|
@@ -59384,7 +59395,7 @@ function show_node_popover(gobj) {
|
|
|
59384
59395
|
style: "flex:1;padding:6px;background:#1890ff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
|
|
59385
59396
|
onClick: () => {
|
|
59386
59397
|
drop_preview_undo(gobj, "_node_preview_undo");
|
|
59387
|
-
apply_node_properties(gobj, node_id, fillInput.value, strokeInput.value, parseInt(lwInput.value) || 1, scopeSelect.value, shapeSelect.value);
|
|
59398
|
+
apply_node_properties(gobj, node_id, fillInput.value, strokeInput.value, parseInt(lwInput.value) || 1, scopeSelect.value, shapeSelect.value !== orig_figure ? shapeSelect.value : "");
|
|
59388
59399
|
}
|
|
59389
59400
|
}]);
|
|
59390
59401
|
priv.$container.appendChild(popover);
|
|
@@ -60317,7 +60328,7 @@ function build_context_menu_items(gobj, e) {
|
|
|
60317
60328
|
let containerRect = priv.$container.getBoundingClientRect();
|
|
60318
60329
|
let canvasPoint = graph.getCanvasByViewport([e.client.x - containerRect.left, e.client.y - containerRect.top]);
|
|
60319
60330
|
let port_key = detect_port_click(gobj, node_id, canvasPoint[0], canvasPoint[1]);
|
|
60320
|
-
if (port_key) {
|
|
60331
|
+
if (port_key && priv.edit_mode) {
|
|
60321
60332
|
priv._context_port_key = port_key;
|
|
60322
60333
|
return build_port_context_menu(gobj, node_id, port_key);
|
|
60323
60334
|
} else return build_node_context_menu(gobj, node_id);
|
|
@@ -60362,14 +60373,24 @@ function build_port_context_menu(gobj, node_id, port_key) {
|
|
|
60362
60373
|
items.push(ctx_item("g6-icon-edit", (0, i18next.t)("port properties"), "port_properties"));
|
|
60363
60374
|
items.push(ctx_item("g6-icon-resize", (0, i18next.t)("resize all ports"), "resize_all_ports"));
|
|
60364
60375
|
items.push(ctx_item("g6-icon-resize", (0, i18next.t)("resize topic ports"), "resize_topic_ports"));
|
|
60376
|
+
items.push(ctx_item("g6-icon-undo", (0, i18next.t)("reset port"), "reset_port"));
|
|
60377
|
+
items.push(ctx_item("g6-icon-undo", (0, i18next.t)("reset topic ports"), "reset_topic_ports"));
|
|
60378
|
+
items.push(ctx_item("g6-icon-undo", (0, i18next.t)("reset all ports"), "reset_all_ports"));
|
|
60365
60379
|
}
|
|
60366
60380
|
return items;
|
|
60367
60381
|
}
|
|
60368
60382
|
/************************************************************
|
|
60369
|
-
* Build edge context menu items
|
|
60383
|
+
* Build edge context menu items: the two floating icons of a
|
|
60384
|
+
* selected edge, for a right click and for a finger.
|
|
60370
60385
|
************************************************************/
|
|
60371
60386
|
function build_edge_context_menu(gobj) {
|
|
60372
|
-
|
|
60387
|
+
let items = [];
|
|
60388
|
+
if (gobj.priv.edit_mode) {
|
|
60389
|
+
inject_svg_icons();
|
|
60390
|
+
items.push(ctx_item("g6-icon-edit", (0, i18next.t)("edge properties"), "edge_properties"));
|
|
60391
|
+
items.push(ctx_item("g6-icon-delete", (0, i18next.t)("unlink"), "unlink_edge"));
|
|
60392
|
+
}
|
|
60393
|
+
return items;
|
|
60373
60394
|
}
|
|
60374
60395
|
/************************************************************
|
|
60375
60396
|
* Handle context menu click: dispatch by value.
|
|
@@ -60401,7 +60422,33 @@ function handle_context_menu_click(gobj, value) {
|
|
|
60401
60422
|
case "resize_all_ports":
|
|
60402
60423
|
copy_size_to_ports(gobj, false);
|
|
60403
60424
|
break;
|
|
60404
|
-
case "resize_topic_ports":
|
|
60425
|
+
case "resize_topic_ports":
|
|
60426
|
+
copy_size_to_ports(gobj, true);
|
|
60427
|
+
break;
|
|
60428
|
+
case "reset_port":
|
|
60429
|
+
reset_ports(gobj, "this");
|
|
60430
|
+
break;
|
|
60431
|
+
case "reset_topic_ports":
|
|
60432
|
+
reset_ports(gobj, "same_topic");
|
|
60433
|
+
break;
|
|
60434
|
+
case "reset_all_ports":
|
|
60435
|
+
reset_ports(gobj, "all");
|
|
60436
|
+
break;
|
|
60437
|
+
case "edge_properties":
|
|
60438
|
+
if (priv._context_edge_id) {
|
|
60439
|
+
deselect_node(gobj);
|
|
60440
|
+
select_edge(gobj, priv._context_edge_id);
|
|
60441
|
+
show_edge_popover(gobj);
|
|
60442
|
+
}
|
|
60443
|
+
break;
|
|
60444
|
+
case "unlink_edge":
|
|
60445
|
+
if (priv._context_edge_id) {
|
|
60446
|
+
deselect_node(gobj);
|
|
60447
|
+
select_edge(gobj, priv._context_edge_id);
|
|
60448
|
+
request_unlink_edge(gobj);
|
|
60449
|
+
}
|
|
60450
|
+
break;
|
|
60451
|
+
default: (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: unknown context menu item: ${value}`);
|
|
60405
60452
|
}
|
|
60406
60453
|
}
|
|
60407
60454
|
/************************************************************
|
|
@@ -60509,6 +60556,70 @@ function reset_sizes(gobj, same_topic_only) {
|
|
|
60509
60556
|
});
|
|
60510
60557
|
}
|
|
60511
60558
|
/************************************************************
|
|
60559
|
+
* Forget the saved SHAPE and RADIUS of ports, back to the
|
|
60560
|
+
* library's own:
|
|
60561
|
+
* 'this' the port of the menu, on its node only
|
|
60562
|
+
* 'same_topic' every port of the node's topic, with the
|
|
60563
|
+
* topic's port defaults
|
|
60564
|
+
* 'all' every port of every topic, likewise
|
|
60565
|
+
* The node sizes are not touched: that is `reset sizes`.
|
|
60566
|
+
************************************************************/
|
|
60567
|
+
function reset_ports(gobj, scope) {
|
|
60568
|
+
let priv = gobj.priv;
|
|
60569
|
+
let graph = priv.graph;
|
|
60570
|
+
let node_id = priv._context_node_id;
|
|
60571
|
+
let port_key = priv._context_port_key;
|
|
60572
|
+
let nd = node_id ? graph.getNodeData(node_id) : null;
|
|
60573
|
+
if (!nd || !nd.data || !nd.data.topic_name || !port_key) {
|
|
60574
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: reset ports without a port`);
|
|
60575
|
+
return;
|
|
60576
|
+
}
|
|
60577
|
+
const PORT_KEYS = [
|
|
60578
|
+
"portR",
|
|
60579
|
+
"port_sizes",
|
|
60580
|
+
"port_shapes",
|
|
60581
|
+
"port_shape"
|
|
60582
|
+
];
|
|
60583
|
+
let strip_port = (props) => {
|
|
60584
|
+
if (!(0, _yuneta_gobj_js.is_object)(props)) return;
|
|
60585
|
+
for (let k of ["port_sizes", "port_shapes"]) if ((0, _yuneta_gobj_js.is_object)(props[k])) {
|
|
60586
|
+
delete props[k][port_key];
|
|
60587
|
+
if (Object.keys(props[k]).length === 0) delete props[k];
|
|
60588
|
+
}
|
|
60589
|
+
};
|
|
60590
|
+
let strip_all = (props) => {
|
|
60591
|
+
if (!(0, _yuneta_gobj_js.is_object)(props)) return;
|
|
60592
|
+
for (let k of PORT_KEYS) delete props[k];
|
|
60593
|
+
};
|
|
60594
|
+
let ids = [];
|
|
60595
|
+
if (scope === "this") {
|
|
60596
|
+
strip_port(node_props_entry(gobj, nd));
|
|
60597
|
+
strip_port(nd.data.graph_props);
|
|
60598
|
+
if (nd.data.record) strip_port(nd.data.record._geometry);
|
|
60599
|
+
ids.push(node_id);
|
|
60600
|
+
} else {
|
|
60601
|
+
let topic = scope === "same_topic" ? nd.data.topic_name : "";
|
|
60602
|
+
for (const [topic_name, props] of Object.entries(priv._graph_properties || {})) {
|
|
60603
|
+
if (topic && topic_name !== topic) continue;
|
|
60604
|
+
if ((0, _yuneta_gobj_js.is_object)(props.nodes)) for (let node_props of Object.values(props.nodes)) strip_all(node_props);
|
|
60605
|
+
strip_all(props.defaults);
|
|
60606
|
+
}
|
|
60607
|
+
for (let n of graph.getNodeData() || []) {
|
|
60608
|
+
if (!n || !n.data || !n.data.desc || is_more_node(n)) continue;
|
|
60609
|
+
if (topic && n.data.topic_name !== topic) continue;
|
|
60610
|
+
strip_all(n.data.graph_props);
|
|
60611
|
+
if (n.data.record) strip_all(n.data.record._geometry);
|
|
60612
|
+
ids.push(n.id);
|
|
60613
|
+
}
|
|
60614
|
+
}
|
|
60615
|
+
reshape_nodes(gobj, ids);
|
|
60616
|
+
graph.draw().then(() => {
|
|
60617
|
+
update_port_resize_handles_position(gobj);
|
|
60618
|
+
update_port_icon_position(gobj);
|
|
60619
|
+
mark_graph_dirty(gobj);
|
|
60620
|
+
});
|
|
60621
|
+
}
|
|
60622
|
+
/************************************************************
|
|
60512
60623
|
* Copy the selected port's radius to matching ports.
|
|
60513
60624
|
* If same_topic_only=true, only ports in same-topic nodes.
|
|
60514
60625
|
* If same_topic_only=false, matching ports in all nodes.
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -55075,7 +55075,7 @@ var NODE_SHAPES = [
|
|
|
55075
55075
|
"hexagon",
|
|
55076
55076
|
"star"
|
|
55077
55077
|
];
|
|
55078
|
-
var DEFAULT_NODE_SHAPE = "
|
|
55078
|
+
var DEFAULT_NODE_SHAPE = "circle";
|
|
55079
55079
|
/***************************************************************
|
|
55080
55080
|
* Internal layout and operation mode definitions
|
|
55081
55081
|
***************************************************************/
|
|
@@ -55228,6 +55228,7 @@ var PRIVATE_DATA$4 = {
|
|
|
55228
55228
|
_focus_topic: null,
|
|
55229
55229
|
_focus_ids: [],
|
|
55230
55230
|
_on_pointerdown_focus: null,
|
|
55231
|
+
_on_native_contextmenu: null,
|
|
55231
55232
|
_uninstall_long_press: null,
|
|
55232
55233
|
toolbar_collapsed: true,
|
|
55233
55234
|
_toolbars_could_collapse: null,
|
|
@@ -55325,6 +55326,10 @@ function mt_destroy$4(gobj) {
|
|
|
55325
55326
|
priv.$container.removeEventListener("focusout", priv._on_focusout_restore);
|
|
55326
55327
|
priv._on_focusout_restore = null;
|
|
55327
55328
|
}
|
|
55329
|
+
if (priv._on_native_contextmenu) {
|
|
55330
|
+
priv.$container.removeEventListener("contextmenu", priv._on_native_contextmenu, true);
|
|
55331
|
+
priv._on_native_contextmenu = null;
|
|
55332
|
+
}
|
|
55328
55333
|
if (priv.theme_observer) {
|
|
55329
55334
|
priv.theme_observer.disconnect();
|
|
55330
55335
|
priv.theme_observer = null;
|
|
@@ -55528,6 +55533,13 @@ function configure_events(gobj) {
|
|
|
55528
55533
|
if ($canvas && $canvas.isConnected && document.activeElement !== $canvas) $canvas.focus({ preventScroll: true });
|
|
55529
55534
|
};
|
|
55530
55535
|
priv.$container.addEventListener("focusout", priv._on_focusout_restore);
|
|
55536
|
+
if (priv._on_native_contextmenu) priv.$container.removeEventListener("contextmenu", priv._on_native_contextmenu, true);
|
|
55537
|
+
priv._on_native_contextmenu = (ev) => {
|
|
55538
|
+
let target = ev.target;
|
|
55539
|
+
if (target && typeof target.closest === "function" && target.closest("input, textarea, select, [contenteditable]")) return;
|
|
55540
|
+
ev.preventDefault();
|
|
55541
|
+
};
|
|
55542
|
+
priv.$container.addEventListener("contextmenu", priv._on_native_contextmenu, true);
|
|
55531
55543
|
graph.on("keydown", (evt) => {
|
|
55532
55544
|
let ctrl = !!(evt.ctrlKey || evt.metaKey);
|
|
55533
55545
|
if (ctrl && (evt.key === "a" || evt.key === "A")) {
|
|
@@ -55561,7 +55573,8 @@ function configure_plugins(gobj) {
|
|
|
55561
55573
|
}
|
|
55562
55574
|
});
|
|
55563
55575
|
let menu_enable = (e) => {
|
|
55564
|
-
|
|
55576
|
+
if (e.targetType === "node") return true;
|
|
55577
|
+
return e.targetType === "edge" && !!priv.edit_mode;
|
|
55565
55578
|
};
|
|
55566
55579
|
graph_add_plugin(gobj, "contextmenu", {
|
|
55567
55580
|
trigger: "contextmenu",
|
|
@@ -56397,11 +56410,8 @@ function figure_shape_of(gobj, desc, record, geometry, flags) {
|
|
|
56397
56410
|
let paint = node_paint_of(gobj, desc, geometry);
|
|
56398
56411
|
let fill = dark ? mix_colors(paint.fill, .25, "#2c3542") : mix_colors(paint.fill, .1, "#ffffff");
|
|
56399
56412
|
let stroke = dark ? mix_colors(paint.fill, .85, "#ffffff") : paint.fill;
|
|
56400
|
-
let line_width =
|
|
56401
|
-
if (paint.stroke !== getStrokeColor(paint.fill))
|
|
56402
|
-
stroke = paint.stroke;
|
|
56403
|
-
line_width = Math.max(paint.lineWidth, 2);
|
|
56404
|
-
}
|
|
56413
|
+
let line_width = paint.lineWidth;
|
|
56414
|
+
if (paint.stroke !== getStrokeColor(paint.fill)) stroke = paint.stroke;
|
|
56405
56415
|
if (f.selected) {
|
|
56406
56416
|
stroke = SELECT_RING;
|
|
56407
56417
|
line_width = 3;
|
|
@@ -59353,6 +59363,7 @@ function show_node_popover(gobj) {
|
|
|
59353
59363
|
};
|
|
59354
59364
|
}));
|
|
59355
59365
|
shapeSelect.value = nodeData.data && nodeData.data.desc ? node_figure_of(gobj, nodeData.data.desc, nodeData.data.graph_props || {}) : DEFAULT_NODE_SHAPE;
|
|
59366
|
+
let orig_figure = shapeSelect.value;
|
|
59356
59367
|
create_form_label(popover, "apply to");
|
|
59357
59368
|
let scopeSelect = create_form_select(popover, [
|
|
59358
59369
|
{
|
|
@@ -59379,7 +59390,7 @@ function show_node_popover(gobj) {
|
|
|
59379
59390
|
style: "flex:1;padding:6px;background:#1890ff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
|
|
59380
59391
|
onClick: () => {
|
|
59381
59392
|
drop_preview_undo(gobj, "_node_preview_undo");
|
|
59382
|
-
apply_node_properties(gobj, node_id, fillInput.value, strokeInput.value, parseInt(lwInput.value) || 1, scopeSelect.value, shapeSelect.value);
|
|
59393
|
+
apply_node_properties(gobj, node_id, fillInput.value, strokeInput.value, parseInt(lwInput.value) || 1, scopeSelect.value, shapeSelect.value !== orig_figure ? shapeSelect.value : "");
|
|
59383
59394
|
}
|
|
59384
59395
|
}]);
|
|
59385
59396
|
priv.$container.appendChild(popover);
|
|
@@ -60312,7 +60323,7 @@ function build_context_menu_items(gobj, e) {
|
|
|
60312
60323
|
let containerRect = priv.$container.getBoundingClientRect();
|
|
60313
60324
|
let canvasPoint = graph.getCanvasByViewport([e.client.x - containerRect.left, e.client.y - containerRect.top]);
|
|
60314
60325
|
let port_key = detect_port_click(gobj, node_id, canvasPoint[0], canvasPoint[1]);
|
|
60315
|
-
if (port_key) {
|
|
60326
|
+
if (port_key && priv.edit_mode) {
|
|
60316
60327
|
priv._context_port_key = port_key;
|
|
60317
60328
|
return build_port_context_menu(gobj, node_id, port_key);
|
|
60318
60329
|
} else return build_node_context_menu(gobj, node_id);
|
|
@@ -60357,14 +60368,24 @@ function build_port_context_menu(gobj, node_id, port_key) {
|
|
|
60357
60368
|
items.push(ctx_item("g6-icon-edit", t("port properties"), "port_properties"));
|
|
60358
60369
|
items.push(ctx_item("g6-icon-resize", t("resize all ports"), "resize_all_ports"));
|
|
60359
60370
|
items.push(ctx_item("g6-icon-resize", t("resize topic ports"), "resize_topic_ports"));
|
|
60371
|
+
items.push(ctx_item("g6-icon-undo", t("reset port"), "reset_port"));
|
|
60372
|
+
items.push(ctx_item("g6-icon-undo", t("reset topic ports"), "reset_topic_ports"));
|
|
60373
|
+
items.push(ctx_item("g6-icon-undo", t("reset all ports"), "reset_all_ports"));
|
|
60360
60374
|
}
|
|
60361
60375
|
return items;
|
|
60362
60376
|
}
|
|
60363
60377
|
/************************************************************
|
|
60364
|
-
* Build edge context menu items
|
|
60378
|
+
* Build edge context menu items: the two floating icons of a
|
|
60379
|
+
* selected edge, for a right click and for a finger.
|
|
60365
60380
|
************************************************************/
|
|
60366
60381
|
function build_edge_context_menu(gobj) {
|
|
60367
|
-
|
|
60382
|
+
let items = [];
|
|
60383
|
+
if (gobj.priv.edit_mode) {
|
|
60384
|
+
inject_svg_icons();
|
|
60385
|
+
items.push(ctx_item("g6-icon-edit", t("edge properties"), "edge_properties"));
|
|
60386
|
+
items.push(ctx_item("g6-icon-delete", t("unlink"), "unlink_edge"));
|
|
60387
|
+
}
|
|
60388
|
+
return items;
|
|
60368
60389
|
}
|
|
60369
60390
|
/************************************************************
|
|
60370
60391
|
* Handle context menu click: dispatch by value.
|
|
@@ -60396,7 +60417,33 @@ function handle_context_menu_click(gobj, value) {
|
|
|
60396
60417
|
case "resize_all_ports":
|
|
60397
60418
|
copy_size_to_ports(gobj, false);
|
|
60398
60419
|
break;
|
|
60399
|
-
case "resize_topic_ports":
|
|
60420
|
+
case "resize_topic_ports":
|
|
60421
|
+
copy_size_to_ports(gobj, true);
|
|
60422
|
+
break;
|
|
60423
|
+
case "reset_port":
|
|
60424
|
+
reset_ports(gobj, "this");
|
|
60425
|
+
break;
|
|
60426
|
+
case "reset_topic_ports":
|
|
60427
|
+
reset_ports(gobj, "same_topic");
|
|
60428
|
+
break;
|
|
60429
|
+
case "reset_all_ports":
|
|
60430
|
+
reset_ports(gobj, "all");
|
|
60431
|
+
break;
|
|
60432
|
+
case "edge_properties":
|
|
60433
|
+
if (priv._context_edge_id) {
|
|
60434
|
+
deselect_node(gobj);
|
|
60435
|
+
select_edge(gobj, priv._context_edge_id);
|
|
60436
|
+
show_edge_popover(gobj);
|
|
60437
|
+
}
|
|
60438
|
+
break;
|
|
60439
|
+
case "unlink_edge":
|
|
60440
|
+
if (priv._context_edge_id) {
|
|
60441
|
+
deselect_node(gobj);
|
|
60442
|
+
select_edge(gobj, priv._context_edge_id);
|
|
60443
|
+
request_unlink_edge(gobj);
|
|
60444
|
+
}
|
|
60445
|
+
break;
|
|
60446
|
+
default: log_error(`${gobj_short_name(gobj)}: unknown context menu item: ${value}`);
|
|
60400
60447
|
}
|
|
60401
60448
|
}
|
|
60402
60449
|
/************************************************************
|
|
@@ -60504,6 +60551,70 @@ function reset_sizes(gobj, same_topic_only) {
|
|
|
60504
60551
|
});
|
|
60505
60552
|
}
|
|
60506
60553
|
/************************************************************
|
|
60554
|
+
* Forget the saved SHAPE and RADIUS of ports, back to the
|
|
60555
|
+
* library's own:
|
|
60556
|
+
* 'this' the port of the menu, on its node only
|
|
60557
|
+
* 'same_topic' every port of the node's topic, with the
|
|
60558
|
+
* topic's port defaults
|
|
60559
|
+
* 'all' every port of every topic, likewise
|
|
60560
|
+
* The node sizes are not touched: that is `reset sizes`.
|
|
60561
|
+
************************************************************/
|
|
60562
|
+
function reset_ports(gobj, scope) {
|
|
60563
|
+
let priv = gobj.priv;
|
|
60564
|
+
let graph = priv.graph;
|
|
60565
|
+
let node_id = priv._context_node_id;
|
|
60566
|
+
let port_key = priv._context_port_key;
|
|
60567
|
+
let nd = node_id ? graph.getNodeData(node_id) : null;
|
|
60568
|
+
if (!nd || !nd.data || !nd.data.topic_name || !port_key) {
|
|
60569
|
+
log_error(`${gobj_short_name(gobj)}: reset ports without a port`);
|
|
60570
|
+
return;
|
|
60571
|
+
}
|
|
60572
|
+
const PORT_KEYS = [
|
|
60573
|
+
"portR",
|
|
60574
|
+
"port_sizes",
|
|
60575
|
+
"port_shapes",
|
|
60576
|
+
"port_shape"
|
|
60577
|
+
];
|
|
60578
|
+
let strip_port = (props) => {
|
|
60579
|
+
if (!is_object(props)) return;
|
|
60580
|
+
for (let k of ["port_sizes", "port_shapes"]) if (is_object(props[k])) {
|
|
60581
|
+
delete props[k][port_key];
|
|
60582
|
+
if (Object.keys(props[k]).length === 0) delete props[k];
|
|
60583
|
+
}
|
|
60584
|
+
};
|
|
60585
|
+
let strip_all = (props) => {
|
|
60586
|
+
if (!is_object(props)) return;
|
|
60587
|
+
for (let k of PORT_KEYS) delete props[k];
|
|
60588
|
+
};
|
|
60589
|
+
let ids = [];
|
|
60590
|
+
if (scope === "this") {
|
|
60591
|
+
strip_port(node_props_entry(gobj, nd));
|
|
60592
|
+
strip_port(nd.data.graph_props);
|
|
60593
|
+
if (nd.data.record) strip_port(nd.data.record._geometry);
|
|
60594
|
+
ids.push(node_id);
|
|
60595
|
+
} else {
|
|
60596
|
+
let topic = scope === "same_topic" ? nd.data.topic_name : "";
|
|
60597
|
+
for (const [topic_name, props] of Object.entries(priv._graph_properties || {})) {
|
|
60598
|
+
if (topic && topic_name !== topic) continue;
|
|
60599
|
+
if (is_object(props.nodes)) for (let node_props of Object.values(props.nodes)) strip_all(node_props);
|
|
60600
|
+
strip_all(props.defaults);
|
|
60601
|
+
}
|
|
60602
|
+
for (let n of graph.getNodeData() || []) {
|
|
60603
|
+
if (!n || !n.data || !n.data.desc || is_more_node(n)) continue;
|
|
60604
|
+
if (topic && n.data.topic_name !== topic) continue;
|
|
60605
|
+
strip_all(n.data.graph_props);
|
|
60606
|
+
if (n.data.record) strip_all(n.data.record._geometry);
|
|
60607
|
+
ids.push(n.id);
|
|
60608
|
+
}
|
|
60609
|
+
}
|
|
60610
|
+
reshape_nodes(gobj, ids);
|
|
60611
|
+
graph.draw().then(() => {
|
|
60612
|
+
update_port_resize_handles_position(gobj);
|
|
60613
|
+
update_port_icon_position(gobj);
|
|
60614
|
+
mark_graph_dirty(gobj);
|
|
60615
|
+
});
|
|
60616
|
+
}
|
|
60617
|
+
/************************************************************
|
|
60507
60618
|
* Copy the selected port's radius to matching ports.
|
|
60508
60619
|
* If same_topic_only=true, only ports in same-topic nodes.
|
|
60509
60620
|
* If same_topic_only=false, matching ports in all nodes.
|
package/package.json
CHANGED
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;
|
|
@@ -6804,6 +6846,7 @@ function show_node_popover(gobj)
|
|
|
6804
6846
|
shapeSelect.value = nodeData.data && nodeData.data.desc
|
|
6805
6847
|
? node_figure_of(gobj, nodeData.data.desc, nodeData.data.graph_props || {})
|
|
6806
6848
|
: DEFAULT_NODE_SHAPE;
|
|
6849
|
+
let orig_figure = shapeSelect.value;
|
|
6807
6850
|
|
|
6808
6851
|
// Apply-to scope
|
|
6809
6852
|
create_form_label(popover, 'apply to');
|
|
@@ -6832,7 +6875,10 @@ function show_node_popover(gobj)
|
|
|
6832
6875
|
strokeInput.value,
|
|
6833
6876
|
parseInt(lwInput.value) || 1,
|
|
6834
6877
|
scopeSelect.value,
|
|
6835
|
-
|
|
6878
|
+
/* Only a figure somebody CHOSE is written: the
|
|
6879
|
+
* one the select opened on is today's default,
|
|
6880
|
+
* and saving it would freeze it on the node. */
|
|
6881
|
+
(shapeSelect.value !== orig_figure)? shapeSelect.value : ""
|
|
6836
6882
|
);
|
|
6837
6883
|
},
|
|
6838
6884
|
},
|
|
@@ -8341,7 +8387,9 @@ function build_context_menu_items(gobj, e)
|
|
|
8341
8387
|
gobj, node_id, canvasPoint[0], canvasPoint[1]
|
|
8342
8388
|
);
|
|
8343
8389
|
|
|
8344
|
-
|
|
8390
|
+
/* A port's commands are edition commands; out of edition a
|
|
8391
|
+
* port is part of its node, and gets the node's menu. */
|
|
8392
|
+
if(port_key && priv.edit_mode) {
|
|
8345
8393
|
priv._context_port_key = port_key;
|
|
8346
8394
|
return build_port_context_menu(gobj, node_id, port_key);
|
|
8347
8395
|
} else {
|
|
@@ -8411,18 +8459,30 @@ function build_port_context_menu(gobj, node_id, port_key)
|
|
|
8411
8459
|
items.push(ctx_item('g6-icon-edit', t('port properties'), 'port_properties'));
|
|
8412
8460
|
items.push(ctx_item('g6-icon-resize', t('resize all ports'), 'resize_all_ports'));
|
|
8413
8461
|
items.push(ctx_item('g6-icon-resize', t('resize topic ports'), 'resize_topic_ports'));
|
|
8462
|
+
/* The way back from the popover and the two above: the
|
|
8463
|
+
* library's own shape and radius. */
|
|
8464
|
+
items.push(ctx_item('g6-icon-undo', t('reset port'), 'reset_port'));
|
|
8465
|
+
items.push(ctx_item('g6-icon-undo', t('reset topic ports'), 'reset_topic_ports'));
|
|
8466
|
+
items.push(ctx_item('g6-icon-undo', t('reset all ports'), 'reset_all_ports'));
|
|
8414
8467
|
}
|
|
8415
8468
|
|
|
8416
8469
|
return items;
|
|
8417
8470
|
}
|
|
8418
8471
|
|
|
8419
8472
|
/************************************************************
|
|
8420
|
-
* Build edge context menu items
|
|
8473
|
+
* Build edge context menu items: the two floating icons of a
|
|
8474
|
+
* selected edge, for a right click and for a finger.
|
|
8421
8475
|
************************************************************/
|
|
8422
8476
|
function build_edge_context_menu(gobj)
|
|
8423
8477
|
{
|
|
8424
8478
|
let items = [];
|
|
8425
|
-
|
|
8479
|
+
|
|
8480
|
+
if(gobj.priv.edit_mode) {
|
|
8481
|
+
inject_svg_icons();
|
|
8482
|
+
items.push(ctx_item('g6-icon-edit', t('edge properties'), 'edge_properties'));
|
|
8483
|
+
items.push(ctx_item('g6-icon-delete', t('unlink'), 'unlink_edge'));
|
|
8484
|
+
}
|
|
8485
|
+
|
|
8426
8486
|
return items;
|
|
8427
8487
|
}
|
|
8428
8488
|
|
|
@@ -8463,6 +8523,32 @@ function handle_context_menu_click(gobj, value)
|
|
|
8463
8523
|
case 'resize_topic_ports':
|
|
8464
8524
|
copy_size_to_ports(gobj, true);
|
|
8465
8525
|
break;
|
|
8526
|
+
case 'reset_port':
|
|
8527
|
+
reset_ports(gobj, 'this');
|
|
8528
|
+
break;
|
|
8529
|
+
case 'reset_topic_ports':
|
|
8530
|
+
reset_ports(gobj, 'same_topic');
|
|
8531
|
+
break;
|
|
8532
|
+
case 'reset_all_ports':
|
|
8533
|
+
reset_ports(gobj, 'all');
|
|
8534
|
+
break;
|
|
8535
|
+
case 'edge_properties':
|
|
8536
|
+
if(priv._context_edge_id) {
|
|
8537
|
+
deselect_node(gobj);
|
|
8538
|
+
select_edge(gobj, priv._context_edge_id);
|
|
8539
|
+
show_edge_popover(gobj);
|
|
8540
|
+
}
|
|
8541
|
+
break;
|
|
8542
|
+
case 'unlink_edge':
|
|
8543
|
+
if(priv._context_edge_id) {
|
|
8544
|
+
deselect_node(gobj);
|
|
8545
|
+
select_edge(gobj, priv._context_edge_id);
|
|
8546
|
+
request_unlink_edge(gobj);
|
|
8547
|
+
}
|
|
8548
|
+
break;
|
|
8549
|
+
default:
|
|
8550
|
+
log_error(`${gobj_short_name(gobj)}: unknown context menu item: ${value}`);
|
|
8551
|
+
break;
|
|
8466
8552
|
}
|
|
8467
8553
|
}
|
|
8468
8554
|
|
|
@@ -8630,6 +8716,99 @@ function reset_sizes(gobj, same_topic_only)
|
|
|
8630
8716
|
});
|
|
8631
8717
|
}
|
|
8632
8718
|
|
|
8719
|
+
/************************************************************
|
|
8720
|
+
* Forget the saved SHAPE and RADIUS of ports, back to the
|
|
8721
|
+
* library's own:
|
|
8722
|
+
* 'this' the port of the menu, on its node only
|
|
8723
|
+
* 'same_topic' every port of the node's topic, with the
|
|
8724
|
+
* topic's port defaults
|
|
8725
|
+
* 'all' every port of every topic, likewise
|
|
8726
|
+
* The node sizes are not touched: that is `reset sizes`.
|
|
8727
|
+
************************************************************/
|
|
8728
|
+
function reset_ports(gobj, scope)
|
|
8729
|
+
{
|
|
8730
|
+
let priv = gobj.priv;
|
|
8731
|
+
let graph = priv.graph;
|
|
8732
|
+
|
|
8733
|
+
let node_id = priv._context_node_id;
|
|
8734
|
+
let port_key = priv._context_port_key;
|
|
8735
|
+
let nd = node_id? graph.getNodeData(node_id) : null;
|
|
8736
|
+
if(!nd || !nd.data || !nd.data.topic_name || !port_key) {
|
|
8737
|
+
log_error(`${gobj_short_name(gobj)}: reset ports without a port`);
|
|
8738
|
+
return;
|
|
8739
|
+
}
|
|
8740
|
+
|
|
8741
|
+
const PORT_KEYS = ["portR", "port_sizes", "port_shapes", "port_shape"];
|
|
8742
|
+
let strip_port = (props) => {
|
|
8743
|
+
if(!is_object(props)) {
|
|
8744
|
+
return;
|
|
8745
|
+
}
|
|
8746
|
+
for(let k of ["port_sizes", "port_shapes"]) {
|
|
8747
|
+
if(is_object(props[k])) {
|
|
8748
|
+
delete props[k][port_key];
|
|
8749
|
+
if(Object.keys(props[k]).length === 0) {
|
|
8750
|
+
delete props[k];
|
|
8751
|
+
}
|
|
8752
|
+
}
|
|
8753
|
+
}
|
|
8754
|
+
};
|
|
8755
|
+
let strip_all = (props) => {
|
|
8756
|
+
if(!is_object(props)) {
|
|
8757
|
+
return;
|
|
8758
|
+
}
|
|
8759
|
+
for(let k of PORT_KEYS) {
|
|
8760
|
+
delete props[k];
|
|
8761
|
+
}
|
|
8762
|
+
};
|
|
8763
|
+
|
|
8764
|
+
let ids = [];
|
|
8765
|
+
if(scope === 'this') {
|
|
8766
|
+
strip_port(node_props_entry(gobj, nd));
|
|
8767
|
+
strip_port(nd.data.graph_props);
|
|
8768
|
+
if(nd.data.record) {
|
|
8769
|
+
strip_port(nd.data.record._geometry);
|
|
8770
|
+
}
|
|
8771
|
+
ids.push(node_id);
|
|
8772
|
+
} else {
|
|
8773
|
+
let topic = (scope === 'same_topic')? nd.data.topic_name : "";
|
|
8774
|
+
|
|
8775
|
+
/* The saved entries, on screen or folded away. */
|
|
8776
|
+
for(const [topic_name, props] of Object.entries(priv._graph_properties || {})) {
|
|
8777
|
+
if(topic && topic_name !== topic) {
|
|
8778
|
+
continue;
|
|
8779
|
+
}
|
|
8780
|
+
if(is_object(props.nodes)) {
|
|
8781
|
+
for(let node_props of Object.values(props.nodes)) {
|
|
8782
|
+
strip_all(node_props);
|
|
8783
|
+
}
|
|
8784
|
+
}
|
|
8785
|
+
strip_all(props.defaults);
|
|
8786
|
+
}
|
|
8787
|
+
|
|
8788
|
+
/* The nodes on screen: their own copy of the entry. */
|
|
8789
|
+
for(let n of (graph.getNodeData() || [])) {
|
|
8790
|
+
if(!n || !n.data || !n.data.desc || is_more_node(n)) {
|
|
8791
|
+
continue;
|
|
8792
|
+
}
|
|
8793
|
+
if(topic && n.data.topic_name !== topic) {
|
|
8794
|
+
continue;
|
|
8795
|
+
}
|
|
8796
|
+
strip_all(n.data.graph_props);
|
|
8797
|
+
if(n.data.record) {
|
|
8798
|
+
strip_all(n.data.record._geometry);
|
|
8799
|
+
}
|
|
8800
|
+
ids.push(n.id);
|
|
8801
|
+
}
|
|
8802
|
+
}
|
|
8803
|
+
|
|
8804
|
+
reshape_nodes(gobj, ids);
|
|
8805
|
+
graph.draw().then(() => {
|
|
8806
|
+
update_port_resize_handles_position(gobj);
|
|
8807
|
+
update_port_icon_position(gobj);
|
|
8808
|
+
mark_graph_dirty(gobj);
|
|
8809
|
+
});
|
|
8810
|
+
}
|
|
8811
|
+
|
|
8633
8812
|
/************************************************************
|
|
8634
8813
|
* Copy the selected port's radius to matching ports.
|
|
8635
8814
|
* If same_topic_only=true, only ports in same-topic nodes.
|