@yuneta/gobj-ui 7.23.80 → 7.23.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1388,6 +1388,29 @@ radius and per-port radius — per node and as topic defaults from `resize all`
1388
1388
  on the spot, and arm Save. Positions and edge styles are not touched.
1389
1389
  Consumer i18n keys: `reset sizes`, `reset topic sizes`.
1390
1390
 
1391
+ **A port has a shape, and its own properties popover** (since `7.23.81`).
1392
+ G6 draws every port as a circle — `drawPortShapes` upserts a `Circle` and
1393
+ nothing in the port style says otherwise — so a record's card is now a node
1394
+ of its own, `treedb-card` (G6's html node with `drawPortShapes` overridden),
1395
+ where a port's `shape` picks the G shape by the names G6 registers itself:
1396
+ `circle`, `square`, `diamond`, `triangle`. `r` stays the one size (the
1397
+ half-side of the square, the half-diagonal of the diamond), and the hit test,
1398
+ the resize handles and the edge's landing point read `r` and none of them care
1399
+ about the outline. A selected port shows a gear beside it, as the node does
1400
+ (under the link icon on an fkey); it opens the **port properties** popover —
1401
+ shape, radius, and the scope: *this port*, *the same port of every card of
1402
+ the topic* (the hook or fkey column), or *every port there is* — with a live
1403
+ preview on the port and a cancel that puts it back. The choice is remembered
1404
+ as the topic's default (`port_shapes[key]` / `port_shape`, beside
1405
+ `port_sizes` / `portR`), so a card that arrives later is born with it, and
1406
+ saved per node in `__graphs__` as `port_shapes`. On a touch screen the port's
1407
+ context menu has the same entry. The `+N` chip stays an `html` node.
1408
+ Consumer i18n keys: `port properties`, `shape`, `circle`, `square`, `diamond`,
1409
+ `triangle`, `radius`, `this port`, `same port in topic`, `all ports` — and
1410
+ the node and edge popovers' own labels, which had no key at all (`apply to`,
1411
+ `fill color`, `stroke color`, `line width`, `color`, `create`), so they
1412
+ rendered in English in every language.
1413
+
1391
1414
  ### The legend is the graph's layer control
1392
1415
 
1393
1416
  The legend strip under the toolbar is **always there** now (the *Legend*
@@ -43159,7 +43159,7 @@ var _excluded$5 = ["style"];
43159
43159
  * HTML container
43160
43160
  * @see https://github.com/pmndrs/drei#html
43161
43161
  */
43162
- var HTML = /*#__PURE__*/ function(_DisplayObject) {
43162
+ var HTML$1 = /*#__PURE__*/ function(_DisplayObject) {
43163
43163
  function HTML() {
43164
43164
  var _this;
43165
43165
  var _ref = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}, style = _ref.style, rest = _objectWithoutProperties(_ref, _excluded$5);
@@ -43207,7 +43207,7 @@ var HTML = /*#__PURE__*/ function(_DisplayObject) {
43207
43207
  }
43208
43208
  ]);
43209
43209
  }(DisplayObject);
43210
- HTML.PARSED_STYLE_LIST = new Set([].concat(_toConsumableArray(DisplayObject.PARSED_STYLE_LIST), [
43210
+ HTML$1.PARSED_STYLE_LIST = new Set([].concat(_toConsumableArray(DisplayObject.PARSED_STYLE_LIST), [
43211
43211
  "x",
43212
43212
  "y",
43213
43213
  "$el",
@@ -54658,6 +54658,8 @@ var PRIVATE_DATA$4 = {
54658
54658
  _edge_icon_el: null,
54659
54659
  _edge_delete_el: null,
54660
54660
  _edge_popover_el: null,
54661
+ _port_icon_el: null,
54662
+ _port_popover_el: null,
54661
54663
  _node_icon_el: null,
54662
54664
  _node_delete_el: null,
54663
54665
  _node_popover_el: null,
@@ -54848,6 +54850,7 @@ function register_layouts(gobj) {
54848
54850
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "treedb-tree", TreedbTreeLayout);
54849
54851
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "treedb-radial", TreedbRadialLayout);
54850
54852
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.NODE, "light", LightNode);
54853
+ (0, _antv_g6.register)(_antv_g6.ExtensionCategory.NODE, "treedb-card", TreedbCard);
54851
54854
  }
54852
54855
  }
54853
54856
  /************************************************************
@@ -54917,6 +54920,7 @@ function configure_events(gobj) {
54917
54920
  update_edge_icon_position(gobj);
54918
54921
  update_node_icon_position(gobj);
54919
54922
  update_link_icon_position(gobj);
54923
+ update_port_icon_position(gobj);
54920
54924
  });
54921
54925
  graph.on(_antv_g6.NodeEvent.DRAG_END, (evt) => {
54922
54926
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_NODE_DRAG_END", { evt }, gobj);
@@ -54941,6 +54945,7 @@ function configure_events(gobj) {
54941
54945
  update_edge_icon_position(gobj);
54942
54946
  update_node_icon_position(gobj);
54943
54947
  update_link_icon_position(gobj);
54948
+ update_port_icon_position(gobj);
54944
54949
  update_zoom_readout(gobj);
54945
54950
  let z = priv.last_zoom;
54946
54951
  try {
@@ -55657,12 +55662,22 @@ function card_shape_of(gobj, desc, record, geometry, pills_html) {
55657
55662
  if (r == null && (0, _yuneta_gobj_js.is_object)(default_port_sizes)) r = default_port_sizes[key];
55658
55663
  if (r != null) style.ports[i].r = r;
55659
55664
  }
55665
+ let port_shapes = geometry.port_shapes;
55666
+ let default_port_shapes = topic_defaults ? topic_defaults.port_shapes : null;
55667
+ let default_shape = topic_defaults ? topic_defaults.port_shape : null;
55668
+ for (let i = 0; i < style.ports.length; i++) {
55669
+ let key = style.ports[i].key;
55670
+ let shape = (0, _yuneta_gobj_js.is_object)(port_shapes) ? port_shapes[key] : null;
55671
+ if (!shape && (0, _yuneta_gobj_js.is_object)(default_port_shapes)) shape = default_port_shapes[key];
55672
+ if (!shape) shape = default_shape;
55673
+ if (shape && (0, _yuneta_gobj_js.str_in_list)(PORT_SHAPES, shape)) style.ports[i].shape = shape;
55674
+ }
55660
55675
  } else {
55661
55676
  style.port = false;
55662
55677
  style.ports = [];
55663
55678
  }
55664
55679
  return {
55665
- type: "html",
55680
+ type: "treedb-card",
55666
55681
  style
55667
55682
  };
55668
55683
  }
@@ -56086,6 +56101,10 @@ function update_geometry(gobj, node_id) {
56086
56101
  for (let i = 0; i < ports.length; i++) if (ports[i].r != null) port_sizes[ports[i].key] = ports[i].r;
56087
56102
  if (Object.keys(port_sizes).length > 0) node_props.port_sizes = port_sizes;
56088
56103
  else delete node_props.port_sizes;
56104
+ let port_shapes = {};
56105
+ for (let i = 0; i < ports.length; i++) if (ports[i].shape && ports[i].shape !== "circle") port_shapes[ports[i].key] = ports[i].shape;
56106
+ if (Object.keys(port_shapes).length > 0) node_props.port_shapes = port_shapes;
56107
+ else delete node_props.port_shapes;
56089
56108
  }
56090
56109
  topic_props.nodes[record.id] = node_props;
56091
56110
  nodedata.data.graph_props = node_props;
@@ -56568,11 +56587,74 @@ function graph_resize(gobj, width, height) {
56568
56587
  update_port_resize_handles_position(gobj);
56569
56588
  update_node_icon_position(gobj);
56570
56589
  update_link_icon_position(gobj);
56590
+ update_port_icon_position(gobj);
56571
56591
  }
56572
56592
  function graph_write_behaviors(gobj, behaviors) {
56573
56593
  gobj.priv.graph.setOptions({ behaviors });
56574
56594
  }
56575
56595
  /************************************************************
56596
+ * Custom G6 node: TreedbCard -- G6's html node with ports that
56597
+ * have a SHAPE.
56598
+ *
56599
+ * G6 draws every port as a circle (`drawPortShapes` upserts a
56600
+ * `Circle`, nothing in the port style says otherwise). A port's
56601
+ * `shape` -- `circle`, `square`, `diamond`, `triangle` -- picks
56602
+ * the G shape here, by the names G6 registers itself, and `r`
56603
+ * stays the one size: the half-side of the square, the half-
56604
+ * diagonal of the diamond, the half-height of the triangle. The
56605
+ * hit test, the resize handles and the edge's landing point all
56606
+ * read `r` and none of them care about the outline.
56607
+ ************************************************************/
56608
+ var PORT_SHAPES = [
56609
+ "circle",
56610
+ "square",
56611
+ "diamond",
56612
+ "triangle"
56613
+ ];
56614
+ var TreedbCard = class extends _antv_g6.HTML {
56615
+ drawPortShapes(attributes, container) {
56616
+ const portsStyle = this.getPortsStyle(attributes);
56617
+ Object.keys(portsStyle).forEach((key) => {
56618
+ const style = portsStyle[key];
56619
+ const shapeKey = `port-${key}`;
56620
+ if (style === false) {
56621
+ this.upsert(shapeKey, "circle", false, container);
56622
+ return;
56623
+ }
56624
+ let r = Number(style.r) || 0;
56625
+ switch (style.shape) {
56626
+ case "square":
56627
+ this.upsert(shapeKey, "rect", Object.assign({}, style, {
56628
+ x: -r,
56629
+ y: -r,
56630
+ width: 2 * r,
56631
+ height: 2 * r
56632
+ }), container);
56633
+ break;
56634
+ case "diamond":
56635
+ this.upsert(shapeKey, "polygon", Object.assign({}, style, { points: [
56636
+ [0, -r],
56637
+ [r, 0],
56638
+ [0, r],
56639
+ [-r, 0]
56640
+ ] }), container);
56641
+ break;
56642
+ case "triangle":
56643
+ this.upsert(shapeKey, "polygon", Object.assign({}, style, { points: [
56644
+ [0, -r],
56645
+ [r, r],
56646
+ [-r, r]
56647
+ ] }), container);
56648
+ break;
56649
+ default: this.upsert(shapeKey, "circle", style, container);
56650
+ }
56651
+ });
56652
+ }
56653
+ };
56654
+ function is_card_type(type) {
56655
+ return type === "treedb-card" || type === "html";
56656
+ }
56657
+ /************************************************************
56576
56658
  * Custom G6 node: LightNode (circle with status indicator)
56577
56659
  ************************************************************/
56578
56660
  var LightNode = class extends _antv_g6.Circle {
@@ -57318,7 +57400,7 @@ function start_node_resize(gobj, e, mx, my) {
57318
57400
  };
57319
57401
  if (nodeType === "circle") updateStyle.size = [Math.max(newW, newH)];
57320
57402
  else updateStyle.size = [newW, newH];
57321
- if (nodeType === "html") {
57403
+ if (is_card_type(nodeType)) {
57322
57404
  updateStyle.dx = -newW / 2;
57323
57405
  updateStyle.dy = -newH / 2;
57324
57406
  }
@@ -57443,6 +57525,7 @@ function select_port(gobj, node_id, port_key) {
57443
57525
  priv._selected_port_key = port_key;
57444
57526
  show_port_resize_handles(gobj);
57445
57527
  show_link_icon_if_fkey(gobj);
57528
+ show_port_icon(gobj);
57446
57529
  }
57447
57530
  /************************************************************
57448
57531
  * Deselect port: clear port state, return to node selection.
@@ -57451,6 +57534,8 @@ function deselect_port(gobj) {
57451
57534
  let priv = gobj.priv;
57452
57535
  exit_linking_mode(gobj);
57453
57536
  hide_link_icon(gobj);
57537
+ hide_port_icon(gobj);
57538
+ hide_port_popover(gobj);
57454
57539
  hide_port_resize_handles(gobj);
57455
57540
  priv._selected_port_key = null;
57456
57541
  }
@@ -57503,6 +57588,217 @@ function update_link_icon_position(gobj) {
57503
57588
  }
57504
57589
  }
57505
57590
  /************************************************************
57591
+ * Port properties icon and popover: the gear beside a selected
57592
+ * port, as the node has its own. The popover has the two things
57593
+ * a port IS on screen -- its shape and its radius -- and the
57594
+ * scope: this port, the same port of every card of the topic
57595
+ * (the hook or fkey column), or every port there is.
57596
+ ************************************************************/
57597
+ function port_icon_xy(gobj) {
57598
+ let priv = gobj.priv;
57599
+ let graph = priv.graph;
57600
+ let canvasPos = get_port_canvas_position(gobj, priv._selected_node_id, priv._selected_port_key);
57601
+ if (!canvasPos) return null;
57602
+ let vp = graph.getViewportByCanvas([canvasPos.x, canvasPos.y]);
57603
+ let below = priv._link_icon_el ? floating_icon_step() : 0;
57604
+ return {
57605
+ x: vp[0] + floating_icon_size() / 2 + 4,
57606
+ y: vp[1] + floating_icon_dy() + below
57607
+ };
57608
+ }
57609
+ function show_port_icon(gobj) {
57610
+ hide_port_icon(gobj);
57611
+ let priv = gobj.priv;
57612
+ if (!priv._selected_node_id || !priv._selected_port_key) return;
57613
+ let xy = port_icon_xy(gobj);
57614
+ if (!xy) return;
57615
+ let icon = create_floating_icon("gear", "#1890ff", xy.x, xy.y, (0, i18next.t)("port properties"), () => toggle_port_popover(gobj));
57616
+ priv.$container.appendChild(icon);
57617
+ priv._port_icon_el = icon;
57618
+ }
57619
+ function hide_port_icon(gobj) {
57620
+ hide_overlay(gobj, "_port_icon_el");
57621
+ }
57622
+ function update_port_icon_position(gobj) {
57623
+ let priv = gobj.priv;
57624
+ if (!priv._port_icon_el || !priv._selected_node_id || !priv._selected_port_key) return;
57625
+ try {
57626
+ let xy = port_icon_xy(gobj);
57627
+ if (!xy) {
57628
+ hide_port_icon(gobj);
57629
+ hide_port_popover(gobj);
57630
+ return;
57631
+ }
57632
+ priv._port_icon_el.style.left = xy.x + "px";
57633
+ priv._port_icon_el.style.top = xy.y + "px";
57634
+ if (priv._port_popover_el) {
57635
+ priv._port_popover_el.style.left = xy.x + floating_popover_dx() + "px";
57636
+ priv._port_popover_el.style.top = xy.y + "px";
57637
+ clamp_popover_position(gobj, priv._port_popover_el);
57638
+ }
57639
+ } catch (e) {
57640
+ hide_port_icon(gobj);
57641
+ hide_port_popover(gobj);
57642
+ }
57643
+ }
57644
+ function toggle_port_popover(gobj) {
57645
+ if (gobj.priv._port_popover_el) hide_port_popover(gobj);
57646
+ else show_port_popover(gobj);
57647
+ }
57648
+ function hide_port_popover(gobj) {
57649
+ hide_overlay(gobj, "_port_popover_el");
57650
+ }
57651
+ function ports_with(ports, port_key, changes) {
57652
+ return (ports || []).map((p) => p.key === port_key ? Object.assign({}, p, changes) : p);
57653
+ }
57654
+ function show_port_popover(gobj) {
57655
+ hide_port_popover(gobj);
57656
+ let priv = gobj.priv;
57657
+ let graph = priv.graph;
57658
+ let node_id = priv._selected_node_id;
57659
+ let port_key = priv._selected_port_key;
57660
+ if (!node_id || !port_key) return;
57661
+ let nodeData = graph.getNodeData(node_id);
57662
+ if (!nodeData || !nodeData.style) return;
57663
+ let port = (nodeData.style.ports || []).find((p) => p.key === port_key);
57664
+ if (!port) return;
57665
+ let orig_shape = port.shape || "circle";
57666
+ let orig_r = get_port_radius(gobj, node_id, port_key);
57667
+ let xy = port_icon_xy(gobj);
57668
+ if (!xy) return;
57669
+ const popover = create_popover_base(xy.x + floating_popover_dx(), xy.y, "g6-port-popover", "#d9d9d9", 180);
57670
+ function preview_port() {
57671
+ let r = parseInt(rInput.value) || orig_r;
57672
+ graph.updateNodeData([{
57673
+ id: node_id,
57674
+ style: { ports: ports_with(nodeData.style.ports, port_key, {
57675
+ shape: shapeSelect.value,
57676
+ r
57677
+ }) }
57678
+ }]);
57679
+ graph.draw().then(() => {
57680
+ update_port_resize_handles_position(gobj);
57681
+ });
57682
+ }
57683
+ create_form_label(popover, "shape");
57684
+ let shapeSelect = create_form_select(popover, PORT_SHAPES.map((shape) => {
57685
+ return {
57686
+ value: shape,
57687
+ label: (0, i18next.t)(shape)
57688
+ };
57689
+ }));
57690
+ shapeSelect.value = orig_shape;
57691
+ shapeSelect.addEventListener("change", preview_port);
57692
+ create_form_label(popover, "radius");
57693
+ let rInput = create_form_number_input(popover, orig_r, 2, 40, preview_port);
57694
+ create_form_label(popover, "apply to");
57695
+ let scopeSelect = create_form_select(popover, [
57696
+ {
57697
+ value: "this",
57698
+ label: (0, i18next.t)("this port")
57699
+ },
57700
+ {
57701
+ value: "same_topic",
57702
+ label: (0, i18next.t)("same port in topic")
57703
+ },
57704
+ {
57705
+ value: "all",
57706
+ label: (0, i18next.t)("all ports")
57707
+ }
57708
+ ]);
57709
+ create_form_button_row(popover, [{
57710
+ text: "cancel",
57711
+ style: BTN_STYLE_CANCEL,
57712
+ onClick: () => {
57713
+ graph.updateNodeData([{
57714
+ id: node_id,
57715
+ style: { ports: ports_with(nodeData.style.ports, port_key, {
57716
+ shape: orig_shape,
57717
+ r: orig_r
57718
+ }) }
57719
+ }]);
57720
+ graph.draw().then(() => {
57721
+ update_port_resize_handles_position(gobj);
57722
+ });
57723
+ hide_port_popover(gobj);
57724
+ }
57725
+ }, {
57726
+ text: "apply",
57727
+ style: "flex:1;padding:6px;background:#1890ff;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;font-weight:500;",
57728
+ onClick: () => {
57729
+ apply_port_properties(gobj, node_id, port_key, shapeSelect.value, parseInt(rInput.value) || orig_r, scopeSelect.value);
57730
+ }
57731
+ }]);
57732
+ priv.$container.appendChild(popover);
57733
+ priv._port_popover_el = popover;
57734
+ clamp_popover_position(gobj, popover);
57735
+ }
57736
+ /************************************************************
57737
+ * Apply a port's shape and radius to one port, to the same port
57738
+ * of every card of the topic, or to every port -- and remember
57739
+ * the choice as the topic's default, the way `resize all ports`
57740
+ * does, so a card that arrives later is born with it.
57741
+ * scope: 'this', 'same_topic', 'all'
57742
+ ************************************************************/
57743
+ function apply_port_properties(gobj, node_id, port_key, shape, r, scope) {
57744
+ let priv = gobj.priv;
57745
+ let graph = priv.graph;
57746
+ let nodeData = graph.getNodeData(node_id);
57747
+ if (!nodeData || !nodeData.data) return;
57748
+ let source_topic = nodeData.data.topic_name;
57749
+ if (scope === "same_topic") {
57750
+ if (!(0, _yuneta_gobj_js.is_object)(priv._graph_properties[source_topic])) priv._graph_properties[source_topic] = {};
57751
+ let defaults = priv._graph_properties[source_topic].defaults || {};
57752
+ defaults.port_sizes = Object.assign({}, defaults.port_sizes, { [port_key]: r });
57753
+ defaults.port_shapes = Object.assign({}, defaults.port_shapes, { [port_key]: shape });
57754
+ priv._graph_properties[source_topic].defaults = defaults;
57755
+ } else if (scope === "all") for (const topic_name of Object.keys(priv.descs || {})) {
57756
+ if (!(0, _yuneta_gobj_js.is_object)(priv._graph_properties[topic_name])) priv._graph_properties[topic_name] = {};
57757
+ let defaults = priv._graph_properties[topic_name].defaults || {};
57758
+ defaults.portR = r;
57759
+ defaults.port_shape = shape;
57760
+ delete defaults.port_sizes;
57761
+ delete defaults.port_shapes;
57762
+ priv._graph_properties[topic_name].defaults = defaults;
57763
+ }
57764
+ let updates = [];
57765
+ for (let nd of graph.getNodeData() || []) {
57766
+ if (!nd || !nd.data || !nd.data.desc || !nd.style || !(0, _yuneta_gobj_js.is_array)(nd.style.ports)) continue;
57767
+ let new_ports;
57768
+ if (scope === "this") {
57769
+ if (nd.id !== node_id) continue;
57770
+ new_ports = ports_with(nd.style.ports, port_key, {
57771
+ shape,
57772
+ r
57773
+ });
57774
+ } else if (scope === "same_topic") {
57775
+ if (nd.data.topic_name !== source_topic) continue;
57776
+ new_ports = ports_with(nd.style.ports, port_key, {
57777
+ shape,
57778
+ r
57779
+ });
57780
+ } else new_ports = nd.style.ports.map((p) => Object.assign({}, p, {
57781
+ shape,
57782
+ r
57783
+ }));
57784
+ let upd = { ports: new_ports };
57785
+ if (scope === "all") upd.portR = r;
57786
+ updates.push({
57787
+ id: nd.id,
57788
+ style: upd
57789
+ });
57790
+ }
57791
+ if (updates.length > 0) {
57792
+ graph.updateNodeData(updates);
57793
+ graph.draw().then(() => {
57794
+ update_port_resize_handles_position(gobj);
57795
+ update_port_icon_position(gobj);
57796
+ mark_graph_dirty(gobj);
57797
+ });
57798
+ }
57799
+ hide_port_popover(gobj);
57800
+ }
57801
+ /************************************************************
57506
57802
  * Get a column descriptor by field id from a topic desc.
57507
57803
  ************************************************************/
57508
57804
  function get_col_by_id(desc, col_id) {
@@ -59177,6 +59473,7 @@ function build_port_context_menu(gobj, node_id, port_key) {
59177
59473
  let items = [];
59178
59474
  if (gobj.priv.edit_mode) {
59179
59475
  inject_svg_icons();
59476
+ items.push(ctx_item("g6-icon-edit", (0, i18next.t)("port properties"), "port_properties"));
59180
59477
  items.push(ctx_item("g6-icon-resize", (0, i18next.t)("resize all ports"), "resize_all_ports"));
59181
59478
  items.push(ctx_item("g6-icon-resize", (0, i18next.t)("resize topic ports"), "resize_topic_ports"));
59182
59479
  }
@@ -59209,6 +59506,12 @@ function handle_context_menu_click(gobj, value) {
59209
59506
  case "reset_topic_sizes":
59210
59507
  reset_sizes(gobj, true);
59211
59508
  break;
59509
+ case "port_properties":
59510
+ if (priv._context_node_id && priv._context_port_key) {
59511
+ select_port(gobj, priv._context_node_id, priv._context_port_key);
59512
+ show_port_popover(gobj);
59513
+ }
59514
+ break;
59212
59515
  case "resize_all_ports":
59213
59516
  copy_size_to_ports(gobj, false);
59214
59517
  break;
@@ -59252,7 +59555,7 @@ function copy_size_to_nodes(gobj, same_topic_only) {
59252
59555
  if (nodes[i].id === node_id) continue;
59253
59556
  let updateStyle = { size: [...source_size] };
59254
59557
  if (source_portR != null) updateStyle.portR = source_portR;
59255
- if (nd.type === "html") {
59558
+ if (is_card_type(nd.type)) {
59256
59559
  updateStyle.dx = -source_size[0] / 2;
59257
59560
  updateStyle.dy = -(source_size.length > 1 ? source_size[1] : source_size[0]) / 2;
59258
59561
  }
@@ -59852,6 +60155,7 @@ async function reconcile_fold_apply(gobj, opts, visible) {
59852
60155
  update_edge_icon_position(gobj);
59853
60156
  update_node_icon_position(gobj);
59854
60157
  update_link_icon_position(gobj);
60158
+ update_port_icon_position(gobj);
59855
60159
  return changed;
59856
60160
  }
59857
60161
  /************************************************************