@yuneta/gobj-ui 7.23.73 → 7.23.75

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.
@@ -27373,7 +27373,7 @@ function fold_reveal(model, state, node_key) {
27373
27373
  * Two layouts made for a treedb, and nothing else.
27374
27374
  *
27375
27375
  * What a treedb graph IS, once it is folded (treedb_fold_model.js):
27376
- * a forest read left to right. A main topic is a tree (places:
27376
+ * a forest read top to bottom. A main topic is a tree (places:
27377
27377
  * country, region, site, hall), the other topics hang from its
27378
27378
  * nodes through hooks (devices, users, controllers), a record may
27379
27379
  * hang from two parents (a device from its place AND its
@@ -27394,8 +27394,8 @@ function fold_reveal(model, state, node_key) {
27394
27394
  * - the force family scatters a tree, which is the pile with
27395
27395
  * physics.
27396
27396
  *
27397
- * So: a tidy tree (`tree`) and an outline (`outline`), both fed by
27398
- * the SAME spanning tree, chosen deterministically:
27397
+ * So: a tidy tree (`tree`) and a radial tree (`radial`), both fed
27398
+ * by the SAME spanning tree, chosen deterministically:
27399
27399
  *
27400
27400
  * - roots are the nodes with no incoming edge, in node order;
27401
27401
  * - a node belongs to the FIRST parent that reaches it in a
@@ -27407,16 +27407,21 @@ function fold_reveal(model, state, node_key) {
27407
27407
  * and then by node order (record order, the pages' order);
27408
27408
  * - a node nothing reaches (a cycle) is a root too.
27409
27409
  *
27410
- * `tree`: the classic tidy tree, left to right. Each depth is a
27411
- * column as wide as its widest card; a node is centred on the
27412
- * block of its children; siblings stack with `nodesep` between
27413
- * them. O(n), and opening a hook moves NOTHING that is not below
27414
- * or beside it -- the cards keep their order, the eye keeps its
27415
- * place.
27416
- *
27417
- * `outline`: one node per row, indented by depth -- what a JSON
27418
- * viewer draws, and what a treedb is when read as one. Tall, but
27419
- * nothing is ever beside anything: the reader scrolls a list.
27410
+ * `tree`: the classic tidy tree, top to bottom. Each depth is a
27411
+ * row as tall as its tallest card; a node is centred over the
27412
+ * block of its children; siblings sit side by side with `nodesep`
27413
+ * between them. O(n), and opening a hook moves NOTHING that is
27414
+ * not below or beside it -- the cards keep their order, the eye
27415
+ * keeps its place. It reads DOWN because that is the direction a
27416
+ * tree with many children has room in: a hall with a hundred
27417
+ * devices is a wide row, not a tall column beside a card. (The
27418
+ * left-to-right reading is `direction: "LR"`, kept for a host
27419
+ * that wants it; `dagre` reads that way in the graph.)
27420
+ *
27421
+ * `radial`: the root in the middle and a ring per depth, each
27422
+ * subtree owning a sector proportional to its leaves; the radius
27423
+ * of a ring grows until its neighbours sit side by side, so
27424
+ * nothing overlaps by construction (see `layout_radial`).
27420
27425
  *
27421
27426
  * Pure: takes plain nodes and edges, returns positions. The G6
27422
27427
  * classes in c_g6_nodes_tree.js are two thin adapters over it.
@@ -27426,8 +27431,8 @@ function fold_reveal(model, state, node_key) {
27426
27431
  ***********************************************************************/
27427
27432
  var DEFAULTS = {
27428
27433
  nodesep: 18,
27429
- ranksep: 110,
27430
- indent: 48
27434
+ ranksep: 90,
27435
+ direction: "TB"
27431
27436
  };
27432
27437
  /************************************************************
27433
27438
  * The spanning tree of a graph, deterministic.
@@ -27514,13 +27519,33 @@ function walk_order(t) {
27514
27519
  return order;
27515
27520
  }
27516
27521
  /************************************************************
27517
- * The tidy tree, left to right.
27522
+ * The tidy tree.
27518
27523
  *
27519
27524
  * Returns Map<id, {x, y}> with the CENTRE of each node, the way G6
27520
27525
  * positions a node.
27526
+ *
27527
+ * The algorithm is written ONCE, reading left to right (depths are
27528
+ * columns, siblings stack down). Top to bottom -- the default -- is
27529
+ * the same tree with the axes swapped: every card is fed in
27530
+ * transposed (its height as its width), and every position comes
27531
+ * back transposed. That is the whole difference, and it is the
27532
+ * reason the code is not duplicated for a second direction.
27521
27533
  ************************************************************/
27522
27534
  function layout_tree(nodes, edges, opts) {
27523
27535
  let o = Object.assign({}, DEFAULTS, opts || {});
27536
+ if (o.direction === "LR") return tidy_tree_lr(nodes, edges, o);
27537
+ let lr = tidy_tree_lr(nodes.map((n) => Object.assign({}, n, {
27538
+ w: n.h,
27539
+ h: n.w
27540
+ })), edges, o);
27541
+ let pos = /* @__PURE__ */ new Map();
27542
+ for (let [id, p] of lr) pos.set(id, {
27543
+ x: p.y,
27544
+ y: p.x
27545
+ });
27546
+ return pos;
27547
+ }
27548
+ function tidy_tree_lr(nodes, edges, o) {
27524
27549
  let t = spanning_tree(nodes, edges);
27525
27550
  let pos = /* @__PURE__ */ new Map();
27526
27551
  let col_w = [];
@@ -27686,37 +27711,6 @@ function layout_radial(nodes, edges, opts) {
27686
27711
  }
27687
27712
  return pos;
27688
27713
  }
27689
- /************************************************************
27690
- * The outline: one node per row, indented by depth, in the
27691
- * pre-order of the spanning tree. Rows are left-aligned on
27692
- * their indent, so a column of chips reads as a list.
27693
- ************************************************************/
27694
- function layout_outline(nodes, edges, opts) {
27695
- let o = Object.assign({}, DEFAULTS, opts || {});
27696
- let t = spanning_tree(nodes, edges);
27697
- let pos = /* @__PURE__ */ new Map();
27698
- let y = 0;
27699
- let visit = (root_id) => {
27700
- let stack = [root_id];
27701
- while (stack.length) {
27702
- let id = stack.pop();
27703
- let n = t.by_id.get(id);
27704
- let d = t.depth.get(id);
27705
- pos.set(id, {
27706
- x: d * o.indent + n.w / 2,
27707
- y: y + n.h / 2
27708
- });
27709
- y += n.h + o.nodesep;
27710
- let kids = t.children.get(id) || [];
27711
- for (let i = kids.length - 1; i >= 0; i--) stack.push(kids[i]);
27712
- }
27713
- };
27714
- for (let root of t.roots) {
27715
- visit(root);
27716
- y += o.nodesep;
27717
- }
27718
- return pos;
27719
- }
27720
27714
  //#endregion
27721
27715
  //#region node_modules/@babel/runtime/helpers/esm/typeof.js
27722
27716
  function _typeof(o) {
@@ -54533,6 +54527,14 @@ var GCLASS_NAME$4 = "C_G6_NODES_TREE";
54533
54527
  var HIGHLIGHT_COLOR = "#f0a020";
54534
54528
  var HIGHLIGHT_HALO = "rgba(240,160,32,0.35)";
54535
54529
  var SELECT_RING = "rgba(59,130,246,0.95)";
54530
+ var PORT_R_ENTITY = 10;
54531
+ var PORT_R_CHILD = 5;
54532
+ var PORT_LINE_WIDTH = 2;
54533
+ var COMPACT_SIZE = {
54534
+ hierarchical: 32,
54535
+ extended: 28,
54536
+ child: 22
54537
+ };
54536
54538
  /***************************************************************
54537
54539
  * Internal layout and operation mode definitions
54538
54540
  ***************************************************************/
@@ -54540,13 +54542,9 @@ var _layouts = {
54540
54542
  "manual": { type: "manual" },
54541
54543
  "treedb-tree": {
54542
54544
  type: "treedb-tree",
54545
+ direction: "TB",
54543
54546
  nodesep: 18,
54544
- ranksep: 110
54545
- },
54546
- "treedb-outline": {
54547
- type: "treedb-outline",
54548
- nodesep: 12,
54549
- indent: 48
54547
+ ranksep: 90
54550
54548
  },
54551
54549
  "dagre": {
54552
54550
  type: "dagre",
@@ -54617,6 +54615,8 @@ var attrs_table$4 = [
54617
54615
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "hidden_topics", 0, "[]", "Topics left out of the tree: no card, no pill counts them, no edge reaches them"),
54618
54616
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "main_topic", 0, "", "The topic the tree hangs from; empty = deduced (the one whose hooks reach the most others). Its parentless records are the roots; a parentless record of a topic linked to it is LOOSE and shown only on request"),
54619
54617
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "loose_topics", 0, "[]", "Topics whose loose records (no parent, in a topic the schema hangs from the main one) are shown as roots"),
54618
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "node_mode", 0, "expanded", "How a record is drawn: `expanded` = the card with its ports (the only shape a link can be edited on); `compact` = a rounded square of the topic's colour, no ports, no text -- the topology alone. One node can be toggled against it (EV_TOGGLE_NODE_MODE)"),
54619
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "node_labels", 0, true, "A compact node says its name under the square. Off, it is a coloured square and nothing else"),
54620
54620
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "confirm_delete_node", 0, true, "Ask confirmation before deleting a node"),
54621
54621
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "confirm_unlink_edge", 0, true, "Ask confirmation before unlinking an edge"),
54622
54622
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "wide", 0, "40px", "Height of header"),
@@ -54700,6 +54700,7 @@ var PRIVATE_DATA$4 = {
54700
54700
  _fold_state: null,
54701
54701
  _fold_listener: null,
54702
54702
  _pill_sig: null,
54703
+ _open_nodes: null,
54703
54704
  _fold_busy: false
54704
54705
  };
54705
54706
  var __gclass__$4 = null;
@@ -54845,7 +54846,6 @@ function register_layouts(gobj) {
54845
54846
  _g6_extensions_registered = true;
54846
54847
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "manual", ManualLayout);
54847
54848
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "treedb-tree", TreedbTreeLayout);
54848
- (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "treedb-outline", TreedbOutlineLayout);
54849
54849
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "treedb-radial", TreedbRadialLayout);
54850
54850
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.NODE, "light", LightNode);
54851
54851
  }
@@ -54928,6 +54928,9 @@ function configure_events(gobj) {
54928
54928
  graph.on(_antv_g6.NodeEvent.CONTEXT_MENU, (evt) => {
54929
54929
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_NODE_CONTEXT_MENU", { evt }, gobj);
54930
54930
  });
54931
+ graph.on(_antv_g6.NodeEvent.DBLCLICK, (evt) => {
54932
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_NODE_DBLCLICK", { evt }, gobj);
54933
+ });
54931
54934
  graph.on(_antv_g6.EdgeEvent.CLICK, (evt) => {
54932
54935
  if (consume_long_press_click(graph)) return;
54933
54936
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_EDGE_CLICK", { evt }, gobj);
@@ -55352,7 +55355,7 @@ function configure_behaviour(gobj) {
55352
55355
  case "writing":
55353
55356
  case "reading":
55354
55357
  priv.edit_mode = false;
55355
- behaviors = ["drag-canvas", "zoom-canvas"];
55358
+ behaviors = ["drag-canvas", ...camera_behaviors()];
55356
55359
  break;
55357
55360
  case "edition":
55358
55361
  priv.edit_mode = true;
@@ -55366,7 +55369,7 @@ function configure_behaviour(gobj) {
55366
55369
  return true;
55367
55370
  }
55368
55371
  },
55369
- "zoom-canvas",
55372
+ ...camera_behaviors(),
55370
55373
  "drag-element",
55371
55374
  {
55372
55375
  type: BRUSH_SELECT_OWNED,
@@ -55384,7 +55387,7 @@ function configure_behaviour(gobj) {
55384
55387
  break;
55385
55388
  case "operation":
55386
55389
  priv.edit_mode = false;
55387
- behaviors = ["drag-canvas", "zoom-canvas"];
55390
+ behaviors = ["drag-canvas", ...camera_behaviors()];
55388
55391
  break;
55389
55392
  default: (0, _yuneta_gobj_js.log_error)(`operation mode unknown: ${operation_mode}`);
55390
55393
  }
@@ -55456,13 +55459,33 @@ function build_ports(gobj, desc) {
55456
55459
  * Which way the graph reads. `dagre` is the left-to-right
55457
55460
  * layout; everything else keeps the top-down reading, `manual`
55458
55461
  * included, because the saved arrangements were made with the
55459
- * ports on the top and bottom edges.
55460
- ************************************************************/
55461
- var LR_LAYOUTS = [
55462
- "dagre",
55463
- "treedb-tree",
55464
- "treedb-outline"
55465
- ];
55462
+ * ports on the top and bottom edges. The treedb tree reads down
55463
+ * too, since 7.23.75: read right it was dagre's twin.
55464
+ ************************************************************/
55465
+ var LR_LAYOUTS = ["dagre"];
55466
+ /************************************************************
55467
+ * The camera behaviors every operation mode shares.
55468
+ *
55469
+ * The wheel SCROLLS, as it does on a map and on every page: a
55470
+ * graph of many nodes is taller than the screen, and a wheel
55471
+ * that zoomed instead made it a thing to be looked at from afar
55472
+ * or read through a keyhole -- never scrolled. Zoom is Ctrl +
55473
+ * wheel (which is also what a trackpad pinch arrives as), the
55474
+ * toolbar's +/-, and the two-finger pinch of g6_touch_gestures.
55475
+ * `enable` on the scroll keeps the two apart: a Ctrl wheel is
55476
+ * the zoom's, so the scroll stands aside for it.
55477
+ ************************************************************/
55478
+ function camera_behaviors() {
55479
+ return [{
55480
+ type: "scroll-canvas",
55481
+ key: "scroll-canvas",
55482
+ enable: (event) => !(event && (event.ctrlKey || event.metaKey))
55483
+ }, {
55484
+ type: "zoom-canvas",
55485
+ key: "zoom-canvas",
55486
+ trigger: ["Control"]
55487
+ }];
55488
+ }
55466
55489
  function layout_direction(gobj) {
55467
55490
  return (0, _yuneta_gobj_js.str_in_list)(LR_LAYOUTS, gobj.priv.layout) ? "LR" : "TB";
55468
55491
  }
@@ -55530,83 +55553,101 @@ function apply_layout_direction(gobj) {
55530
55553
  * Create a topic node in the G6 graph
55531
55554
  ************************************************************/
55532
55555
  function create_topic_node(gobj, desc, record) {
55533
- let priv = gobj.priv;
55534
- const graph = priv.graph;
55556
+ const graph = gobj.priv.graph;
55535
55557
  let node_name = build_node_name(gobj, desc.topic_name, record.id);
55536
55558
  let xy = get_default_ne_xy(gobj);
55537
55559
  let geometry = get_node_graph_props(gobj, desc.topic_name, record.id, record);
55538
55560
  let x = (0, _yuneta_gobj_js.kw_get_int)(gobj, geometry, "x", xy, _yuneta_gobj_js.kw_flag_t.KW_CREATE);
55539
55561
  let y = (0, _yuneta_gobj_js.kw_get_int)(gobj, geometry, "y", xy, _yuneta_gobj_js.kw_flag_t.KW_CREATE);
55540
- let ports = build_ports(gobj, desc);
55541
- let node_graph_type = null;
55562
+ let shape = node_shape_of(gobj, node_name, desc, record, geometry, pills_html_of_key(gobj, fold_node_key(desc.topic_name, String(record.id))));
55563
+ let node_def = {
55564
+ id: node_name,
55565
+ type: shape.type,
55566
+ style: Object.assign({
55567
+ x,
55568
+ y
55569
+ }, shape.style),
55570
+ data: {
55571
+ topic_name: desc.topic_name,
55572
+ desc,
55573
+ record,
55574
+ graph_props: geometry,
55575
+ port_keys: build_ports(gobj, desc).map((p) => p.key)
55576
+ }
55577
+ };
55578
+ graph.addNodeData([node_def]);
55579
+ }
55580
+ /************************************************************
55581
+ * The shape of a record node: OPEN (the card, with its ports)
55582
+ * or CLOSED (a coloured square), by `node_mode` and the nodes
55583
+ * toggled against it.
55584
+ *
55585
+ * Both answer {type, style} WITHOUT x/y, which belong to the node
55586
+ * and not to its shape, and a node moves between the two on the
55587
+ * fly: G6 rebuilds the element when `type` changes, and the
55588
+ * style is MERGED, which is why each shape also writes the keys
55589
+ * the other one would leave behind (`labelText`, `ports`).
55590
+ ************************************************************/
55591
+ function node_shape_of(gobj, node_id, desc, record, geometry, pills_html) {
55592
+ if (node_is_compact(gobj, node_id)) return compact_shape_of(gobj, desc, record, {});
55593
+ return card_shape_of(gobj, desc, record, geometry, pills_html);
55594
+ }
55595
+ function node_is_compact(gobj, node_id) {
55596
+ let priv = gobj.priv;
55597
+ if (!priv._open_nodes) priv._open_nodes = /* @__PURE__ */ new Set();
55598
+ let compact = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "node_mode") === "compact";
55599
+ return priv._open_nodes.has(node_id) ? !compact : compact;
55600
+ }
55601
+ /************************************************************
55602
+ * The CARD: an html node, the tier's size, the ports, the
55603
+ * saved geometry on top. The flags (focus, selection, anchor)
55604
+ * are not painted here: repaint_cards() reads them where they
55605
+ * live and rebuilds the html.
55606
+ ************************************************************/
55607
+ function card_shape_of(gobj, desc, record, geometry, pills_html) {
55608
+ let priv = gobj.priv;
55542
55609
  let node_treedb_type = desc.node_treedb_type;
55610
+ let label = node_label(desc, record);
55611
+ let ports = build_ports(gobj, desc);
55543
55612
  let style = {
55544
- x,
55545
- y,
55546
55613
  fill: desc.color,
55547
55614
  stroke: getStrokeColor(desc.color),
55548
- lineWidth: 1
55615
+ lineWidth: 1,
55616
+ lineDash: [],
55617
+ halo: false,
55618
+ labelText: ""
55549
55619
  };
55550
- let pills_html = pills_html_of_key(gobj, fold_node_key(desc.topic_name, String(record.id)));
55551
55620
  let size = card_size_for(node_treedb_type, !!pills_html);
55552
- if (node_treedb_type === "child") {
55553
- node_graph_type = "html";
55554
- style.size = size;
55555
- style.dx = -size[0] / 2;
55556
- style.dy = -size[1] / 2;
55557
- style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id);
55558
- } else if (node_treedb_type === "extended") {
55559
- node_graph_type = "html";
55560
- style.size = size;
55561
- style.dx = -size[0] / 2;
55562
- style.dy = -size[1] / 2;
55563
- style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id, false, false, false, pills_html);
55564
- } else {
55565
- node_graph_type = "html";
55566
- style.size = size;
55567
- style.dx = -size[0] / 2;
55568
- style.dy = -size[1] / 2;
55569
- style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id, false, false, false, pills_html);
55570
- }
55621
+ style.size = size;
55622
+ style.dx = -size[0] / 2;
55623
+ style.dy = -size[1] / 2;
55624
+ if (node_treedb_type === "child") style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, label, record.id);
55625
+ else if (node_treedb_type === "extended") style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, label, desc.topic_name, true, record.id, false, false, false, pills_html);
55626
+ else style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, label, desc.topic_name, false, record.id, false, false, false, pills_html);
55571
55627
  let topic_props = priv._graph_properties[desc.topic_name];
55572
55628
  let topic_defaults = (0, _yuneta_gobj_js.is_object)(topic_props) && (0, _yuneta_gobj_js.is_object)(topic_props.defaults) ? topic_props.defaults : null;
55573
55629
  if (topic_defaults) {
55574
55630
  let def_size = topic_defaults.size;
55575
55631
  if (!geometry.size && Array.isArray(def_size) && def_size.length > 0) {
55576
55632
  style.size = [...def_size];
55577
- if (node_graph_type === "html") {
55578
- style.dx = -def_size[0] / 2;
55579
- style.dy = -(def_size.length > 1 ? def_size[1] : def_size[0]) / 2;
55580
- }
55633
+ style.dx = -def_size[0] / 2;
55634
+ style.dy = -(def_size.length > 1 ? def_size[1] : def_size[0]) / 2;
55581
55635
  }
55582
55636
  if (!geometry.portR && topic_defaults.portR > 0) style.portR = topic_defaults.portR;
55583
55637
  }
55584
55638
  let saved_size = geometry.size;
55585
55639
  if (Array.isArray(saved_size) && saved_size.length > 0) {
55586
55640
  style.size = saved_size;
55587
- if (node_graph_type === "html") {
55588
- style.dx = -saved_size[0] / 2;
55589
- style.dy = -(saved_size.length > 1 ? saved_size[1] : saved_size[0]) / 2;
55590
- }
55641
+ style.dx = -saved_size[0] / 2;
55642
+ style.dy = -(saved_size.length > 1 ? saved_size[1] : saved_size[0]) / 2;
55591
55643
  }
55592
55644
  if (geometry.portR > 0) style.portR = geometry.portR;
55593
- let node_def = {
55594
- id: node_name,
55595
- type: node_graph_type,
55596
- style,
55597
- data: {
55598
- topic_name: desc.topic_name,
55599
- desc,
55600
- record,
55601
- graph_props: geometry
55602
- }
55603
- };
55604
55645
  if ((0, _yuneta_gobj_js.json_size)(ports)) {
55605
55646
  (0, _yuneta_gobj_js.json_object_update_missing)(style, {
55606
55647
  port: true,
55607
55648
  ports,
55608
- portR: node_treedb_type === "child" ? 2 : 6,
55609
- portLineWidth: 1
55649
+ portR: node_treedb_type === "child" ? PORT_R_CHILD : PORT_R_ENTITY,
55650
+ portLineWidth: PORT_LINE_WIDTH
55610
55651
  });
55611
55652
  let port_sizes = geometry.port_sizes;
55612
55653
  let default_port_sizes = topic_defaults ? topic_defaults.port_sizes : null;
@@ -55616,8 +55657,117 @@ function create_topic_node(gobj, desc, record) {
55616
55657
  if (r == null && (0, _yuneta_gobj_js.is_object)(default_port_sizes)) r = default_port_sizes[key];
55617
55658
  if (r != null) style.ports[i].r = r;
55618
55659
  }
55660
+ } else {
55661
+ style.port = false;
55662
+ style.ports = [];
55663
+ }
55664
+ return {
55665
+ type: "html",
55666
+ style
55667
+ };
55668
+ }
55669
+ /************************************************************
55670
+ * The SQUARE: a G6 `rect` of the topic's colour, no ports, the
55671
+ * name as a label under it when `node_labels` says so. A native
55672
+ * shape and not html on purpose: G6's own stroke and halo paint
55673
+ * on it, so the focus, the selection and the anchor are three
55674
+ * keys of the style instead of a rebuilt innerHTML.
55675
+ *
55676
+ * flags {highlight, selected, anchored}
55677
+ ************************************************************/
55678
+ function compact_shape_of(gobj, desc, record, flags) {
55679
+ let dark = gobj.priv.theme === "dark";
55680
+ let f = flags || {};
55681
+ let tier = desc.node_treedb_type;
55682
+ let side = COMPACT_SIZE[tier] || COMPACT_SIZE.hierarchical;
55683
+ let labels = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "node_labels");
55684
+ let stroke = getStrokeColor(desc.color);
55685
+ let line_width = 1.5;
55686
+ if (f.selected) {
55687
+ stroke = SELECT_RING;
55688
+ line_width = 3;
55689
+ }
55690
+ if (f.highlight) {
55691
+ stroke = HIGHLIGHT_COLOR;
55692
+ line_width = 3;
55693
+ }
55694
+ let dash = [];
55695
+ if (tier === "extended") dash = [4, 3];
55696
+ if (f.anchored) {
55697
+ dash = [2, 2];
55698
+ line_width = Math.max(line_width, 2.5);
55699
+ }
55700
+ return {
55701
+ type: "rect",
55702
+ style: {
55703
+ size: [side, side],
55704
+ radius: Math.round(side / 5),
55705
+ fill: desc.color,
55706
+ stroke,
55707
+ lineWidth: line_width,
55708
+ lineDash: dash,
55709
+ halo: !!f.highlight,
55710
+ haloStroke: HIGHLIGHT_HALO,
55711
+ haloLineWidth: 8,
55712
+ port: false,
55713
+ ports: [],
55714
+ labelText: labels ? node_label(desc, record) : "",
55715
+ labelPlacement: "bottom",
55716
+ labelFontSize: 11,
55717
+ labelFill: dark ? "#e6e9ef" : "#2e333d",
55718
+ labelMaxWidth: 150,
55719
+ labelWordWrap: true,
55720
+ labelMaxLines: 1,
55721
+ labelTextOverflow: "ellipsis",
55722
+ labelBackground: true,
55723
+ labelBackgroundFill: dark ? "#1b2230" : "#ffffff",
55724
+ labelBackgroundOpacity: .85,
55725
+ labelBackgroundRadius: 3,
55726
+ labelPadding: [1, 3]
55727
+ }
55728
+ };
55729
+ }
55730
+ /************************************************************
55731
+ * Give these record nodes the shape their mode says, then
55732
+ * repaint them with the flags they carry. A `+N` chip is not a
55733
+ * record and keeps its own shape.
55734
+ ************************************************************/
55735
+ function reshape_nodes(gobj, ids) {
55736
+ let graph = gobj.priv.graph;
55737
+ if (!graph) return;
55738
+ let updates = [];
55739
+ let touched = /* @__PURE__ */ new Set();
55740
+ for (let id of ids) {
55741
+ let nd;
55742
+ try {
55743
+ nd = graph.getNodeData(id);
55744
+ } catch (e) {
55745
+ continue;
55746
+ }
55747
+ if (!nd || !nd.data || !nd.data.desc || is_more_node(nd)) continue;
55748
+ let geometry = nd.data.graph_props || {};
55749
+ let shape = node_shape_of(gobj, id, nd.data.desc, nd.data.record || {}, geometry, pills_html_of(gobj, nd));
55750
+ updates.push({
55751
+ id,
55752
+ type: shape.type,
55753
+ style: shape.style
55754
+ });
55755
+ touched.add(id);
55756
+ }
55757
+ if (!updates.length) return;
55758
+ try {
55759
+ graph.updateNodeData(updates);
55760
+ } catch (e) {
55761
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot reshape the nodes: ${e}`);
55762
+ return;
55619
55763
  }
55620
- if (node_graph_type) graph.addNodeData([node_def]);
55764
+ repaint_cards(gobj, touched);
55765
+ }
55766
+ function all_record_node_ids(gobj) {
55767
+ let graph = gobj.priv.graph;
55768
+ let ids = [];
55769
+ for (let nd of graph && graph.getNodeData() || []) if (nd && nd.data && nd.data.desc && !is_more_node(nd)) ids.push(nd.id);
55770
+ return ids;
55621
55771
  }
55622
55772
  /************************************************************
55623
55773
  * Update topic node data
@@ -56464,7 +56614,7 @@ var ManualLayout = class extends _antv_g6.BaseLayout {
56464
56614
  }
56465
56615
  };
56466
56616
  /************************************************************
56467
- * Custom G6 layouts: TreedbTreeLayout / TreedbOutlineLayout.
56617
+ * Custom G6 layouts: TreedbTreeLayout / TreedbRadialLayout.
56468
56618
  *
56469
56619
  * Thin adapters over treedb_layout.js: the nodes with their sizes,
56470
56620
  * the edges with the RANK of the hook they leave by (the position
@@ -56482,7 +56632,8 @@ function treedb_layout_input(data) {
56482
56632
  let w = (0, _yuneta_gobj_js.is_array)(size) ? size[0] : size;
56483
56633
  let h = (0, _yuneta_gobj_js.is_array)(size) ? size.length > 1 ? size[1] : size[0] : size;
56484
56634
  let ports = node.style && (0, _yuneta_gobj_js.is_array)(node.style.ports) ? node.style.ports : [];
56485
- ports_of.set(node.id, ports.map((p) => p.key));
56635
+ let keys = node.data && (0, _yuneta_gobj_js.is_array)(node.data.port_keys) ? node.data.port_keys : ports.map((p) => p.key);
56636
+ ports_of.set(node.id, keys);
56486
56637
  return {
56487
56638
  id: node.id,
56488
56639
  w,
@@ -56518,12 +56669,6 @@ var TreedbTreeLayout = class extends _antv_g6.BaseLayout {
56518
56669
  return treedb_layout_output(layout_tree(input.nodes, input.edges, options));
56519
56670
  }
56520
56671
  };
56521
- var TreedbOutlineLayout = class extends _antv_g6.BaseLayout {
56522
- async execute(data, options) {
56523
- let input = treedb_layout_input(data);
56524
- return treedb_layout_output(layout_outline(input.nodes, input.edges, options));
56525
- }
56526
- };
56527
56672
  var TreedbRadialLayout = class extends _antv_g6.BaseLayout {
56528
56673
  async execute(data, options) {
56529
56674
  let input = treedb_layout_input(data);
@@ -57215,7 +57360,7 @@ function get_port_radius(gobj, node_id, port_key) {
57215
57360
  if (ports[i].r != null) return ports[i].r;
57216
57361
  break;
57217
57362
  }
57218
- return style.portR || 6;
57363
+ return style.portR || PORT_R_ENTITY;
57219
57364
  }
57220
57365
  /************************************************************
57221
57366
  * How much wider than the port itself its hit area is,
@@ -57247,7 +57392,7 @@ function detect_port_click(gobj, node_id, canvasX, canvasY) {
57247
57392
  const style = graph.getNodeData(node_id).style || {};
57248
57393
  const { w, h } = extract_size(style);
57249
57394
  const ports = style.ports || [];
57250
- const defaultR = style.portR || 6;
57395
+ const defaultR = style.portR || PORT_R_ENTITY;
57251
57396
  let best_key = null;
57252
57397
  let best_dist = Infinity;
57253
57398
  let slop = port_hit_slop_world(graph);
@@ -57404,7 +57549,7 @@ function enter_linking_mode(gobj) {
57404
57549
  p.stroke = "#52c41a";
57405
57550
  p.lineWidth = 3;
57406
57551
  if (p.r != null) p.r = p.r + 4;
57407
- else p.r = (style.portR || 6) + 4;
57552
+ else p.r = (style.portR || PORT_R_ENTITY) + 4;
57408
57553
  }
57409
57554
  graph.updateNodeData([{
57410
57555
  id: vh.node_id,
@@ -57556,7 +57701,7 @@ function find_hook_at_point(gobj, canvasX, canvasY) {
57556
57701
  let pl = p.placement || [.5, .5];
57557
57702
  let px = pos[0] + (pl[0] - .5) * w;
57558
57703
  let py = pos[1] + (pl[1] - .5) * h;
57559
- let r = p.r != null ? p.r : style.portR || 6;
57704
+ let r = p.r != null ? p.r : style.portR || PORT_R_ENTITY;
57560
57705
  let dx = canvasX - px;
57561
57706
  let dy = canvasY - py;
57562
57707
  if (Math.sqrt(dx * dx + dy * dy) <= r + 6) return {
@@ -58314,7 +58459,19 @@ function repaint_cards(gobj, ids) {
58314
58459
  } catch (e) {
58315
58460
  continue;
58316
58461
  }
58317
- let html = node_innerHTML_of(nd, priv.theme, focus.has(id), selected.has(id), priv.anchor_state === "on" && id === priv.anchor_id, pills_html_of(gobj, nd));
58462
+ let anchored = priv.anchor_state === "on" && id === priv.anchor_id;
58463
+ if (nd && nd.data && nd.data.desc && node_is_compact(gobj, id)) {
58464
+ updates.push({
58465
+ id,
58466
+ style: compact_shape_of(gobj, nd.data.desc, nd.data.record || {}, {
58467
+ highlight: focus.has(id),
58468
+ selected: selected.has(id),
58469
+ anchored
58470
+ }).style
58471
+ });
58472
+ continue;
58473
+ }
58474
+ let html = node_innerHTML_of(nd, priv.theme, focus.has(id), selected.has(id), anchored, pills_html_of(gobj, nd));
58318
58475
  if (html !== null) updates.push({
58319
58476
  id,
58320
58477
  style: { innerHTML: html }
@@ -58351,7 +58508,19 @@ function refresh_html_nodes_theme(gobj, theme) {
58351
58508
  for (let i = 0; i < nodes.length; i++) {
58352
58509
  let id = nodes[i].id;
58353
58510
  let nd = graph.getNodeData(id);
58354
- let html = node_innerHTML_of(nd, theme, highlighted.has(id), selected.has(id), priv.anchor_state === "on" && id === priv.anchor_id, pills_html_of(gobj, nd));
58511
+ let anchored = priv.anchor_state === "on" && id === priv.anchor_id;
58512
+ if (nd && nd.data && nd.data.desc && node_is_compact(gobj, id)) {
58513
+ updates.push({
58514
+ id,
58515
+ style: compact_shape_of(gobj, nd.data.desc, nd.data.record || {}, {
58516
+ highlight: highlighted.has(id),
58517
+ selected: selected.has(id),
58518
+ anchored
58519
+ }).style
58520
+ });
58521
+ continue;
58522
+ }
58523
+ let html = node_innerHTML_of(nd, theme, highlighted.has(id), selected.has(id), anchored, pills_html_of(gobj, nd));
58355
58524
  if (html === null) html = more_innerHTML_of(gobj, nd, theme);
58356
58525
  if (html !== null) updates.push({
58357
58526
  id,
@@ -58971,8 +59140,10 @@ function ctx_item(icon_id, label, value) {
58971
59140
  ************************************************************/
58972
59141
  function build_node_context_menu(gobj, node_id) {
58973
59142
  let items = [];
58974
- if (gobj.priv.edit_mode) {
58975
- inject_svg_icons();
59143
+ inject_svg_icons();
59144
+ if (node_is_compact(gobj, node_id)) items.push(ctx_item("g6-icon-fullscreen", (0, i18next.t)("open node"), "toggle_node_mode"));
59145
+ else items.push(ctx_item("g6-icon-fullscreen-exit", (0, i18next.t)("close node"), "toggle_node_mode"));
59146
+ if (gobj.priv.edit_mode && !node_is_compact(gobj, node_id)) {
58976
59147
  items.push(ctx_item("g6-icon-resize", (0, i18next.t)("resize all"), "resize_all_nodes"));
58977
59148
  items.push(ctx_item("g6-icon-resize", (0, i18next.t)("resize topic nodes"), "resize_topic_nodes"));
58978
59149
  }
@@ -59000,8 +59171,11 @@ function build_edge_context_menu(gobj) {
59000
59171
  * Handle context menu click: dispatch by value.
59001
59172
  ************************************************************/
59002
59173
  function handle_context_menu_click(gobj, value) {
59003
- gobj.priv;
59174
+ let priv = gobj.priv;
59004
59175
  switch (value) {
59176
+ case "toggle_node_mode":
59177
+ if (priv._context_node_id) (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_NODE_MODE", { id: priv._context_node_id }, gobj);
59178
+ break;
59005
59179
  case "resize_all_nodes":
59006
59180
  copy_size_to_nodes(gobj, false);
59007
59181
  break;
@@ -59532,7 +59706,7 @@ async function reconcile_fold_apply(gobj, opts, visible) {
59532
59706
  let nd = graph.getNodeData(id);
59533
59707
  let desc = nd && nd.data && nd.data.desc;
59534
59708
  let geometry = nd && nd.data && nd.data.graph_props || {};
59535
- if (desc && !(0, _yuneta_gobj_js.is_array)(geometry.size)) {
59709
+ if (desc && !(0, _yuneta_gobj_js.is_array)(geometry.size) && !node_is_compact(gobj, id)) {
59536
59710
  let size = card_size_for(desc.node_treedb_type, !!sig);
59537
59711
  resize.push({
59538
59712
  id,
@@ -59617,6 +59791,7 @@ function ac_clear_data(gobj, event, kw, src) {
59617
59791
  priv._fold_model = null;
59618
59792
  priv._fold_state = null;
59619
59793
  priv._pill_sig = null;
59794
+ priv._open_nodes = null;
59620
59795
  graph_remove_plugin(gobj, "history");
59621
59796
  update_history_buttons(gobj);
59622
59797
  graph_clear(gobj);
@@ -60115,6 +60290,79 @@ function ac_set_loose_topics(gobj, event, kw, src) {
60115
60290
  return 0;
60116
60291
  }
60117
60292
  /************************************************************
60293
+ * The shape of every record: open cards or closed squares.
60294
+ * A change of mode forgets the nodes toggled against the old
60295
+ * one -- they were exceptions to a rule that no longer holds --
60296
+ * and lays the graph out again, because a square and a card
60297
+ * do not occupy the same room.
60298
+ ************************************************************/
60299
+ function ac_set_node_mode(gobj, event, kw, src) {
60300
+ let priv = gobj.priv;
60301
+ let mode = kw && kw.node_mode === "compact" ? "compact" : "expanded";
60302
+ (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "node_mode", mode);
60303
+ priv._open_nodes = /* @__PURE__ */ new Set();
60304
+ if (!priv.graph || !priv._fold_model) return 0;
60305
+ reshape_nodes(gobj, all_record_node_ids(gobj));
60306
+ reconcile_fold(gobj, {
60307
+ relayout: true,
60308
+ fit: true
60309
+ });
60310
+ return 0;
60311
+ }
60312
+ /************************************************************
60313
+ * The name under a closed square, on or off. Only the closed
60314
+ * nodes change, and nothing moves: the label hangs outside the
60315
+ * square and the layout measures the square.
60316
+ ************************************************************/
60317
+ function ac_set_node_labels(gobj, event, kw, src) {
60318
+ let priv = gobj.priv;
60319
+ (0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "node_labels", !!(kw && kw.node_labels));
60320
+ if (!priv.graph) return 0;
60321
+ reshape_nodes(gobj, all_record_node_ids(gobj).filter((id) => node_is_compact(gobj, id)));
60322
+ return 0;
60323
+ }
60324
+ /************************************************************
60325
+ * ONE node opened or closed against the mode (a double click,
60326
+ * or the context menu). It holds still on screen while the
60327
+ * rest of the graph makes room, as a fold does.
60328
+ ************************************************************/
60329
+ function ac_toggle_node_mode$1(gobj, event, kw, src) {
60330
+ let priv = gobj.priv;
60331
+ let node_id = kw && kw.id || "";
60332
+ if (!priv.graph || !node_id) {
60333
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: toggle node mode without a node`);
60334
+ return -1;
60335
+ }
60336
+ if (!priv._open_nodes) priv._open_nodes = /* @__PURE__ */ new Set();
60337
+ if (priv._open_nodes.has(node_id)) priv._open_nodes.delete(node_id);
60338
+ else priv._open_nodes.add(node_id);
60339
+ let vp = yui_graph_viewport_of(priv.graph, node_id);
60340
+ reshape_nodes(gobj, [node_id]);
60341
+ if (priv._fold_model) reconcile_fold(gobj, {
60342
+ relayout: true,
60343
+ keep: node_id,
60344
+ keep_vp: vp
60345
+ });
60346
+ return 0;
60347
+ }
60348
+ /************************************************************
60349
+ * A double click on a node: open it if closed, close it if open.
60350
+ * A `+N` chip is not a record and has no shape to toggle.
60351
+ ************************************************************/
60352
+ function ac_node_dblclick(gobj, event, kw, src) {
60353
+ let priv = gobj.priv;
60354
+ let node_id = kw && kw.evt && kw.evt.target ? kw.evt.target.id : "";
60355
+ let nd = null;
60356
+ try {
60357
+ nd = priv.graph.getNodeData(node_id);
60358
+ } catch (e) {
60359
+ nd = null;
60360
+ }
60361
+ if (!nd || !nd.data || !nd.data.desc || is_more_node(nd)) return 0;
60362
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_NODE_MODE", { id: node_id }, gobj);
60363
+ return 0;
60364
+ }
60365
+ /************************************************************
60118
60366
  * Tell whoever asked how many nodes the term matched. The count is
60119
60367
  * the answer to the question the box asks; without it "nothing moved"
60120
60368
  * and "nothing matched" look the same.
@@ -60604,6 +60852,26 @@ function create_gclass$4(gclass_name) {
60604
60852
  ac_set_loose_topics,
60605
60853
  null
60606
60854
  ],
60855
+ [
60856
+ "EV_SET_NODE_MODE",
60857
+ ac_set_node_mode,
60858
+ null
60859
+ ],
60860
+ [
60861
+ "EV_SET_NODE_LABELS",
60862
+ ac_set_node_labels,
60863
+ null
60864
+ ],
60865
+ [
60866
+ "EV_TOGGLE_NODE_MODE",
60867
+ ac_toggle_node_mode$1,
60868
+ null
60869
+ ],
60870
+ [
60871
+ "EV_NODE_DBLCLICK",
60872
+ ac_node_dblclick,
60873
+ null
60874
+ ],
60607
60875
  [
60608
60876
  "EV_CENTER",
60609
60877
  ac_center,
@@ -60708,6 +60976,10 @@ function create_gclass$4(gclass_name) {
60708
60976
  ["EV_SET_HIDDEN_TOPICS", 0],
60709
60977
  ["EV_SET_MAIN_TOPIC", 0],
60710
60978
  ["EV_SET_LOOSE_TOPICS", 0],
60979
+ ["EV_SET_NODE_MODE", 0],
60980
+ ["EV_SET_NODE_LABELS", 0],
60981
+ ["EV_TOGGLE_NODE_MODE", 0],
60982
+ ["EV_NODE_DBLCLICK", 0],
60711
60983
  ["EV_LEGEND_STATE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
60712
60984
  ["EV_CENTER", 0],
60713
60985
  ["EV_FULLSCREEN", 0],
@@ -60773,6 +61045,8 @@ var attrs_table$3 = [
60773
61045
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "hidden_topics", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "[]", "Topics hidden from the graph (legend). User preference, per treedb"),
60774
61046
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "main_topic", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "", "The topic the tree hangs from; empty = deduced by the graph. User preference, per treedb"),
60775
61047
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_LIST, "loose_topics", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "[]", "Topics whose LOOSE records (no parent) are shown. User preference, per treedb"),
61048
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "node_mode", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, "expanded", "How the records are drawn: `expanded` = the cards with their ports, `compact` = coloured squares, the topology alone. User preference"),
61049
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "node_labels", _yuneta_gobj_js.sdata_flag_t.SDF_PERSIST, true, "A compact node says its name under its square. User preference"),
60776
61050
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote Yuno to request data"),
60777
61051
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote service treedb name"),
60778
61052
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_DICT, "descs", 0, null, "Descriptions of topics obtained"),
@@ -60850,7 +61124,9 @@ function mt_create$3(gobj) {
60850
61124
  fold_page_size: (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "fold_page_size"),
60851
61125
  hidden_topics: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "hidden_topics") || [],
60852
61126
  main_topic: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "main_topic") || "",
60853
- loose_topics: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "loose_topics") || []
61127
+ loose_topics: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "loose_topics") || [],
61128
+ node_mode: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "node_mode") || "expanded",
61129
+ node_labels: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "node_labels")
60854
61130
  }, gobj);
60855
61131
  populate_nodes_tree_options(gobj);
60856
61132
  if (priv.with_treedb_tables) {}
@@ -60901,6 +61177,7 @@ function build_ui$3(gobj) {
60901
61177
  let priv = gobj.priv;
60902
61178
  let padding = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "padding");
60903
61179
  let $toolbar = make_toolbar(gobj);
61180
+ refresh_node_shape_buttons(gobj, $toolbar);
60904
61181
  let $back = make_back_to_topics(gobj);
60905
61182
  let $container = (0, _yuneta_gobj_js.createElement2)([
60906
61183
  "div",
@@ -61042,6 +61319,57 @@ function option_label(name) {
61042
61319
  /************************************************************
61043
61320
  *
61044
61321
  ************************************************************/
61322
+ /************************************************************
61323
+ * The two toggles of the nodes' shape.
61324
+ *
61325
+ * Closed nodes: every record a rounded square of its topic's
61326
+ * colour, no ports, no text -- the topology of a graph of many
61327
+ * nodes, which the cards hide behind their own size. Labels:
61328
+ * the name under each square, when the squares alone do not say
61329
+ * enough. Both persist, both are forwarded to the G6 child, and
61330
+ * one node can be opened against the rule with a double click or
61331
+ * its context menu (the child's business).
61332
+ ************************************************************/
61333
+ function node_shape_items(gobj, wide) {
61334
+ let toggle = (cls, icon, event_name, label_key) => {
61335
+ return [
61336
+ "button",
61337
+ {
61338
+ class: `${cls} button`,
61339
+ type: "button",
61340
+ style: {
61341
+ height: wide,
61342
+ width: "2.5em"
61343
+ },
61344
+ title: (0, i18next.t)(label_key),
61345
+ "data-i18n-title": label_key,
61346
+ "aria-label": (0, i18next.t)(label_key),
61347
+ "data-i18n-aria-label": label_key,
61348
+ "aria-pressed": "false"
61349
+ },
61350
+ ["i", {
61351
+ style: "font-size:1.5em; color:inherit;",
61352
+ class: icon
61353
+ }],
61354
+ { click: (evt) => {
61355
+ evt.stopPropagation();
61356
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, event_name, {}, gobj);
61357
+ } }
61358
+ ];
61359
+ };
61360
+ return [toggle("GRAPH_NODE_MODE", "yi-hexagon-nodes", "EV_TOGGLE_NODE_MODE", "closed nodes"), toggle("GRAPH_NODE_LABELS", "yi-font", "EV_TOGGLE_NODE_LABELS", "node labels")];
61361
+ }
61362
+ function refresh_node_shape_buttons(gobj, $root) {
61363
+ if (!$root) return;
61364
+ let compact = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "node_mode") === "compact";
61365
+ let labels = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "node_labels");
61366
+ set_pressed_state($root, ".GRAPH_NODE_MODE", compact);
61367
+ set_pressed_state($root, ".GRAPH_NODE_LABELS", labels);
61368
+ let $mode = $root.querySelector(".GRAPH_NODE_MODE");
61369
+ if ($mode) $mode.setAttribute("aria-pressed", compact ? "true" : "false");
61370
+ let $labels = $root.querySelector(".GRAPH_NODE_LABELS");
61371
+ if ($labels) $labels.setAttribute("aria-pressed", labels ? "true" : "false");
61372
+ }
61045
61373
  function make_toolbar(gobj) {
61046
61374
  let priv = gobj.priv;
61047
61375
  let modes = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "operation_modes");
@@ -61112,6 +61440,7 @@ function make_toolbar(gobj) {
61112
61440
  } }
61113
61441
  ],
61114
61442
  ...yui_graph_fold_items(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "wide")),
61443
+ ...node_shape_items(gobj, (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "wide")),
61115
61444
  [
61116
61445
  "button",
61117
61446
  {
@@ -62175,9 +62504,9 @@ function refresh_legend(gobj) {
62175
62504
  hidden ? `${entry.total}` : `${entry.visible}/${entry.total}`
62176
62505
  ]);
62177
62506
  let body_attrs = {
62178
- class: "GRAPH_LEGEND_ITEM button is-small" + (focused ? " pressed_state" : "") + (is_main ? " GRAPH_LEGEND_MAIN" : ""),
62507
+ class: "GRAPH_LEGEND_ITEM button is-small" + (focused ? " GRAPH_LEGEND_FOCUSED" : "") + (is_main ? " GRAPH_LEGEND_MAIN" : ""),
62179
62508
  type: "button",
62180
- style: "gap:0; flex:0 0 auto;",
62509
+ style: "gap:0; flex:0 0 auto;" + (focused ? " box-shadow: inset 0 0 0 2px var(--bulma-text, #2e333d);" : ""),
62181
62510
  title: is_main ? (0, i18next.t)("main topic") : hidden ? (0, i18next.t)("show topic") : (0, i18next.t)("hide topic"),
62182
62511
  "aria-label": topic_name,
62183
62512
  "aria-pressed": hidden ? "false" : "true"
@@ -62426,6 +62755,29 @@ function ac_collapse_all(gobj, event, kw, src) {
62426
62755
  return 0;
62427
62756
  }
62428
62757
  /************************************************************
62758
+ * The shape pair of the toolbar: the attr flips, persists,
62759
+ * shows on the button, and reaches the G6 child, which owns
62760
+ * the nodes.
62761
+ ************************************************************/
62762
+ function ac_toggle_node_mode(gobj, event, kw, src) {
62763
+ let priv = gobj.priv;
62764
+ let mode = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "node_mode") === "compact" ? "expanded" : "compact";
62765
+ (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "node_mode", mode);
62766
+ (0, _yuneta_gobj_js.gobj_save_persistent_attrs)(gobj, "node_mode");
62767
+ refresh_node_shape_buttons(gobj, priv.$container);
62768
+ if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_SET_NODE_MODE", { node_mode: mode }, gobj);
62769
+ return 0;
62770
+ }
62771
+ function ac_toggle_node_labels(gobj, event, kw, src) {
62772
+ let priv = gobj.priv;
62773
+ let labels = !(0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "node_labels");
62774
+ (0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "node_labels", labels);
62775
+ (0, _yuneta_gobj_js.gobj_save_persistent_attrs)(gobj, "node_labels");
62776
+ refresh_node_shape_buttons(gobj, priv.$container);
62777
+ if (priv.gobj_nodes_tree) (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_SET_NODE_LABELS", { node_labels: labels }, gobj);
62778
+ return 0;
62779
+ }
62780
+ /************************************************************
62429
62781
  * How many nodes the term matched, from the graph child. Zero with a
62430
62782
  * term typed is an ANSWER and is shown; an empty box shows nothing.
62431
62783
  ************************************************************/
@@ -62639,6 +62991,16 @@ function create_gclass$3(gclass_name) {
62639
62991
  ac_collapse_all,
62640
62992
  null
62641
62993
  ],
62994
+ [
62995
+ "EV_TOGGLE_NODE_MODE",
62996
+ ac_toggle_node_mode,
62997
+ null
62998
+ ],
62999
+ [
63000
+ "EV_TOGGLE_NODE_LABELS",
63001
+ ac_toggle_node_labels,
63002
+ null
63003
+ ],
62642
63004
  [
62643
63005
  "EV_LAYOUT_AUTOSET",
62644
63006
  ac_layout_autoset,
@@ -62703,6 +63065,8 @@ function create_gclass$3(gclass_name) {
62703
63065
  ["EV_FIND_NODES", 0],
62704
63066
  ["EV_EXPAND_ALL", 0],
62705
63067
  ["EV_COLLAPSE_ALL", 0],
63068
+ ["EV_TOGGLE_NODE_MODE", 0],
63069
+ ["EV_TOGGLE_NODE_LABELS", 0],
62706
63070
  ["EV_FIND_RESULT", 0],
62707
63071
  ["EV_LAYOUT_AUTOSET", 0],
62708
63072
  ["EV_LEGEND_TOPIC", 0],