@yuneta/gobj-ui 7.23.132 → 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.
@@ -55080,7 +55080,7 @@ var NODE_SHAPES = [
55080
55080
  "hexagon",
55081
55081
  "star"
55082
55082
  ];
55083
- var DEFAULT_NODE_SHAPE = "square";
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
- return e.targetType === "node" || e.targetType === "edge";
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 = 2;
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 (prepared for expansion).
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
- return [];
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": copy_size_to_ports(gobj, true);
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.
@@ -55075,7 +55075,7 @@ var NODE_SHAPES = [
55075
55075
  "hexagon",
55076
55076
  "star"
55077
55077
  ];
55078
- var DEFAULT_NODE_SHAPE = "square";
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
- return e.targetType === "node" || e.targetType === "edge";
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 = 2;
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 (prepared for expansion).
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
- return [];
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": copy_size_to_ports(gobj, true);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.132",
3
+ "version": "7.23.133",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -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 square, a circle, a diamond... chosen in the
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 = 'square';
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
- return e.targetType === 'node' || e.targetType === 'edge';
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 square. */
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
- let line_width = 2;
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
- shapeSelect.value
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
- if(port_key) {
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 (prepared for expansion).
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
- // Future: add edge-specific actions here
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.