@yuneta/gobj-ui 7.23.28 → 7.23.29

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.
@@ -9711,6 +9711,42 @@ function yui_graph_update_anchor($container, state) {
9711
9711
  * nothing to centre right now".
9712
9712
  ************************************************************/
9713
9713
  var __centring__ = /* @__PURE__ */ new WeakMap();
9714
+ /************************************************************
9715
+ * Put `node_id` at a GIVEN point of the viewport.
9716
+ *
9717
+ * What "the graph does not jump when I fold something" is
9718
+ * made of: read where the card is, rebuild the layout, put
9719
+ * that card back where it was. Everything that did not move
9720
+ * in the layout stays put with it.
9721
+ ************************************************************/
9722
+ async function yui_graph_place_at(graph, node_id, viewport_xy) {
9723
+ if (!graph || !node_id || !viewport_xy) return false;
9724
+ try {
9725
+ if (typeof graph.getElementPosition !== "function" || typeof graph.getCanvasCenter !== "function" || typeof graph.translateTo !== "function") return false;
9726
+ let z = graph.getZoom() || 1;
9727
+ let c = graph.getCanvasCenter();
9728
+ let n = graph.getElementPosition(node_id);
9729
+ await graph.translateTo([(c[0] - n[0]) * z + (viewport_xy[0] - c[0]), (c[1] - n[1]) * z + (viewport_xy[1] - c[1])]);
9730
+ return true;
9731
+ } catch (e) {
9732
+ return false;
9733
+ }
9734
+ }
9735
+ /************************************************************
9736
+ * Where `node_id` is right now, in viewport pixels.
9737
+ *
9738
+ * The pair of the call above: read it before a rebuild, hand
9739
+ * it back after, and the node has not moved on screen even
9740
+ * though the layout under it changed.
9741
+ ************************************************************/
9742
+ function yui_graph_viewport_of(graph, node_id) {
9743
+ if (!graph || !node_id) return null;
9744
+ try {
9745
+ return graph.getViewportByCanvas(graph.getElementPosition(node_id));
9746
+ } catch (e) {
9747
+ return null;
9748
+ }
9749
+ }
9714
9750
  async function yui_graph_center_on(graph, node_id) {
9715
9751
  if (!graph || !node_id) return false;
9716
9752
  let st = __centring__.get(graph);
@@ -10581,6 +10617,7 @@ var PRIVATE_DATA$11 = {
10581
10617
  _resize_raf: 0,
10582
10618
  search: "",
10583
10619
  collapsed: null,
10620
+ reserved: null,
10584
10621
  match_count: 0,
10585
10622
  find_timer: null,
10586
10623
  fold_listener: null,
@@ -10592,7 +10629,8 @@ var PRIVATE_DATA$11 = {
10592
10629
  anchor_path: "",
10593
10630
  anchor_state: "off",
10594
10631
  last_zoom: 1,
10595
- center_posted: false
10632
+ center_posted: false,
10633
+ pending_keep: null
10596
10634
  };
10597
10635
  var __gclass__$11 = null;
10598
10636
  var __layout_registered__$1 = false;
@@ -10702,6 +10740,7 @@ function type_color(type, dark) {
10702
10740
  function mt_create$11(gobj) {
10703
10741
  let priv = gobj.priv;
10704
10742
  priv.collapsed = /* @__PURE__ */ new Set();
10743
+ priv.reserved = /* @__PURE__ */ new Map();
10705
10744
  priv.search = "";
10706
10745
  priv.canvas_id = "json-canvas-" + (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj));
10707
10746
  (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "canvas_id", priv.canvas_id);
@@ -10801,7 +10840,11 @@ function build_ui$10(gobj) {
10801
10840
  evt.stopPropagation();
10802
10841
  evt.preventDefault();
10803
10842
  if (evt.type !== "click") return;
10804
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_FOLD", { path: $handle.getAttribute("data-fold-path") }, gobj);
10843
+ let $card = $handle.closest ? $handle.closest(".JSON_CARD") : null;
10844
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_TOGGLE_FOLD", {
10845
+ path: $handle.getAttribute("data-fold-path"),
10846
+ keep: $card ? $card.getAttribute("data-json-path") : ""
10847
+ }, gobj);
10805
10848
  };
10806
10849
  for (let type of FOLD_SWALLOWED_EVENTS) $mount.addEventListener(type, priv.fold_listener, true);
10807
10850
  } else (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no canvas mount, fold handles are dead`);
@@ -11082,6 +11125,8 @@ var BEHAVIORS_READING$1 = [
11082
11125
  var BEHAVIORS_PICKING$1 = ["drag-canvas", ZOOM_CANVAS$1];
11083
11126
  var CARD_HEADER_H$1 = 24;
11084
11127
  var CARD_ROW_H$1 = 22;
11128
+ var H_GAP = 60;
11129
+ var V_GAP = 50;
11085
11130
  function fold_toggle_html(path, foldable, folded, branches, colors) {
11086
11131
  if (!foldable) return FOLD_SPACER;
11087
11132
  let label = folded ? "+" + branches : "−";
@@ -11203,7 +11248,10 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id, parent_port)
11203
11248
  lines.push(build_cell_html(key, value, type, dark, matched, fold));
11204
11249
  }
11205
11250
  if (parent_id && is_pure_collection(kw)) {
11206
- if (priv.collapsed.has(path)) return parent_id;
11251
+ if (priv.collapsed.has(path)) {
11252
+ push_fold_phantom(gobj, path, parent_id, parent_port, nodes, edges);
11253
+ return parent_id;
11254
+ }
11207
11255
  for (let pending of pending_complex) build_json_nodes(gobj, add_segment(path, pending.key), pending.value, nodes, edges, parent_id, parent_port);
11208
11256
  return parent_id;
11209
11257
  }
@@ -11289,7 +11337,10 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id, parent_port)
11289
11337
  if (parent_port) edge.style.sourcePort = parent_port;
11290
11338
  edges.push(edge);
11291
11339
  }
11292
- if (folded) return group_id;
11340
+ if (folded) {
11341
+ push_fold_phantom(gobj, path, group_id, "", nodes, edges);
11342
+ return group_id;
11343
+ }
11293
11344
  for (let pending of pending_complex) build_json_nodes(gobj, add_segment(path, pending.key), pending.value, nodes, edges, group_id, pending.port);
11294
11345
  return group_id;
11295
11346
  }
@@ -11320,8 +11371,16 @@ function load_json(gobj) {
11320
11371
  });
11321
11372
  graph.render().then(() => {
11322
11373
  reanchor$1(gobj);
11323
- paint_anchor_mark(gobj);
11374
+ paint_anchor_mark$1(gobj);
11324
11375
  if (preserve_view) {
11376
+ let keep = priv.pending_keep;
11377
+ priv.pending_keep = null;
11378
+ if (keep) {
11379
+ for (let n of nodes) if (n.data && n.data.path === keep.path) {
11380
+ yui_graph_place_at(graph, n.id, [keep.x, keep.y]);
11381
+ break;
11382
+ }
11383
+ }
11325
11384
  yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
11326
11385
  return;
11327
11386
  }
@@ -11373,6 +11432,108 @@ function update_find_result(gobj) {
11373
11432
  priv.$find_result.classList.remove("is-hidden");
11374
11433
  }
11375
11434
  /************************************************************
11435
+ * The invisible child a fold leaves behind.
11436
+ *
11437
+ * It is the whole of "nothing moves when I fold": the layout
11438
+ * is a plain tree that centres a parent over its children, so
11439
+ * the only way to keep the survivors still is to leave the
11440
+ * space where it was, in the order it was, as a CHILD. A width
11441
+ * reserved as a number would still have to be placed, and
11442
+ * appending it moves every sibling by half of it.
11443
+ *
11444
+ * A `rect` and not an `html` node: nothing is drawn, and a
11445
+ * DOM element per fold would be a card nobody can see but the
11446
+ * Inspector would show.
11447
+ ************************************************************/
11448
+ function push_fold_phantom(gobj, path, parent_id, parent_port, nodes, edges) {
11449
+ let width = gobj.priv.reserved.get(path) || 0;
11450
+ if (!(width > 0) || !parent_id) return;
11451
+ let id = gen_node_id$1(gobj);
11452
+ nodes.push({
11453
+ id,
11454
+ type: "rect",
11455
+ data: {
11456
+ path,
11457
+ phantom: true
11458
+ },
11459
+ style: {
11460
+ size: [width, 1],
11461
+ fill: "transparent",
11462
+ stroke: "transparent",
11463
+ opacity: 0,
11464
+ pointerEvents: "none"
11465
+ }
11466
+ });
11467
+ edges.push({
11468
+ source: parent_id,
11469
+ target: id,
11470
+ style: {
11471
+ opacity: 0,
11472
+ endArrow: false,
11473
+ sourcePort: parent_port || void 0
11474
+ }
11475
+ });
11476
+ }
11477
+ /************************************************************
11478
+ * The width the children a fold is about to hide are taking
11479
+ * RIGHT NOW, measured with the layout's own numbers.
11480
+ *
11481
+ * `card_path` is the card that will lose them; `under` limits
11482
+ * it to the ones hanging from one key of that card, which is
11483
+ * the case of a container that has no card of its own -- its
11484
+ * children hang from the parent, so its fold is a hole in the
11485
+ * PARENT.
11486
+ ************************************************************/
11487
+ function group_width_of(gobj, card_path, under) {
11488
+ let priv = gobj.priv;
11489
+ if (!priv.graph) return 0;
11490
+ try {
11491
+ let by_id = {};
11492
+ let card_id = "";
11493
+ for (let node of priv.graph.getNodeData()) {
11494
+ if (!node || !node.data) continue;
11495
+ by_id[node.id] = node;
11496
+ if (node.data.path === card_path) card_id = node.id;
11497
+ }
11498
+ if (!card_id) return 0;
11499
+ let prefix = under ? under + "`" : "";
11500
+ let sum = 0;
11501
+ let n = 0;
11502
+ for (let edge of priv.graph.getEdgeData()) {
11503
+ if (!edge || edge.source !== card_id) continue;
11504
+ let kid = by_id[edge.target];
11505
+ if (!kid) continue;
11506
+ if (prefix && String(kid.data.path).indexOf(prefix) !== 0) continue;
11507
+ sum += kid.data.subtree_width || 0;
11508
+ n++;
11509
+ }
11510
+ return n ? sum + H_GAP * (n - 1) : 0;
11511
+ } catch (e) {
11512
+ return 0;
11513
+ }
11514
+ }
11515
+ /************************************************************
11516
+ * The screen spot of the card at `path`, to be given back
11517
+ * after the rebuild. Null when it has no card of its own --
11518
+ * a pure collection -- and then nothing anchors and the
11519
+ * camera simply stays where it was.
11520
+ ************************************************************/
11521
+ function keep_of(gobj, path) {
11522
+ let priv = gobj.priv;
11523
+ if ((0, _yuneta_gobj_js.empty_string)(path) || !priv.graph) return null;
11524
+ try {
11525
+ for (let node of priv.graph.getNodeData()) if (node && node.data && node.data.path === path) {
11526
+ let vp = yui_graph_viewport_of(priv.graph, node.id);
11527
+ return vp ? {
11528
+ path,
11529
+ x: vp[0],
11530
+ y: vp[1]
11531
+ } : null;
11532
+ }
11533
+ } catch (e) {}
11534
+ return null;
11535
+ }
11536
+ /************************************************************
11376
11537
  * Clear and reload.
11377
11538
  * {preserve_view: true}
11378
11539
  * keep the current zoom and translate (no fitView) —
@@ -11414,8 +11575,6 @@ var JsonTreeLayout = class extends _antv_g6.BaseLayout {
11414
11575
  h
11415
11576
  };
11416
11577
  }
11417
- const H_GAP = 60;
11418
- const V_GAP = 50;
11419
11578
  let subtree_widths = {};
11420
11579
  function calc_subtree_width(node_id) {
11421
11580
  let kids = children_map[node_id] || [];
@@ -11432,6 +11591,7 @@ var JsonTreeLayout = class extends _antv_g6.BaseLayout {
11432
11591
  return subtree_widths[node_id];
11433
11592
  }
11434
11593
  for (let root of roots) calc_subtree_width(root.id);
11594
+ for (let node of nodes) if (node.data) node.data.subtree_width = subtree_widths[node.id];
11435
11595
  let positions = {};
11436
11596
  function position_node(node_id, x, y) {
11437
11597
  positions[node_id] = {
@@ -11479,9 +11639,16 @@ var JsonTreeLayout = class extends _antv_g6.BaseLayout {
11479
11639
  /************************************************************
11480
11640
  *
11481
11641
  ************************************************************/
11642
+ function priv_reset_folds(gobj) {
11643
+ gobj.priv.collapsed.clear();
11644
+ gobj.priv.reserved.clear();
11645
+ }
11482
11646
  function ac_load_data$1(gobj, event, kw, src) {
11483
11647
  if (kw.path !== void 0) (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "path", kw.path);
11484
- if (kw.data !== void 0) (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "json_data", kw.data);
11648
+ if (kw.data !== void 0) {
11649
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "json_data", kw.data);
11650
+ priv_reset_folds(gobj);
11651
+ }
11485
11652
  refresh_json(gobj);
11486
11653
  return 0;
11487
11654
  }
@@ -11536,8 +11703,16 @@ function ac_toggle_fold(gobj, event, kw, src) {
11536
11703
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: EV_TOGGLE_FOLD with no path`);
11537
11704
  return -1;
11538
11705
  }
11539
- if (priv.collapsed.has(path)) priv.collapsed.delete(path);
11540
- else priv.collapsed.add(path);
11706
+ let card = kw && kw.keep || path;
11707
+ if (priv.collapsed.has(path)) {
11708
+ priv.collapsed.delete(path);
11709
+ priv.reserved.delete(path);
11710
+ } else {
11711
+ let w = group_width_of(gobj, card, card === path ? "" : path);
11712
+ priv.collapsed.add(path);
11713
+ if (w > 0) priv.reserved.set(path, w);
11714
+ }
11715
+ priv.pending_keep = keep_of(gobj, kw && kw.keep || path);
11541
11716
  refresh_json(gobj, { preserve_view: true });
11542
11717
  update_find_result(gobj);
11543
11718
  return 0;
@@ -11564,6 +11739,7 @@ function ac_expand_all$2(gobj, event, kw, src) {
11564
11739
  let priv = gobj.priv;
11565
11740
  if (priv.collapsed.size === 0) return 0;
11566
11741
  priv.collapsed.clear();
11742
+ priv.reserved.clear();
11567
11743
  refresh_json(gobj);
11568
11744
  update_find_result(gobj);
11569
11745
  return 0;
@@ -11580,6 +11756,7 @@ function ac_collapse_all$2(gobj, event, kw, src) {
11580
11756
  if ((0, _yuneta_gobj_js.empty_string)(path)) path = "`";
11581
11757
  collect_foldable_paths(json_data, path, paths, true);
11582
11758
  priv.collapsed = new Set(paths);
11759
+ priv.reserved.clear();
11583
11760
  refresh_json(gobj);
11584
11761
  update_find_result(gobj);
11585
11762
  return 0;
@@ -11669,7 +11846,7 @@ function set_picking_mode$1(gobj, picking) {
11669
11846
  * each time and the mark would otherwise survive only until the
11670
11847
  * next fold.
11671
11848
  ************************************************************/
11672
- function paint_anchor_mark(gobj) {
11849
+ function paint_anchor_mark$1(gobj) {
11673
11850
  let priv = gobj.priv;
11674
11851
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
11675
11852
  if (!$container) return;
@@ -11684,7 +11861,7 @@ function paint_anchor_mark(gobj) {
11684
11861
  function ac_center_anchor$2(gobj, event, kw, src) {
11685
11862
  let priv = gobj.priv;
11686
11863
  priv.center_posted = false;
11687
- paint_anchor_mark(gobj);
11864
+ paint_anchor_mark$1(gobj);
11688
11865
  if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
11689
11866
  return 0;
11690
11867
  }
@@ -11705,7 +11882,7 @@ function ac_toggle_anchor$2(gobj, event, kw, src) {
11705
11882
  priv.anchor_path = "";
11706
11883
  }
11707
11884
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
11708
- paint_anchor_mark(gobj);
11885
+ paint_anchor_mark$1(gobj);
11709
11886
  set_picking_mode$1(gobj, priv.anchor_state === "arming");
11710
11887
  return 0;
11711
11888
  }
@@ -13250,6 +13427,71 @@ var BEHAVIORS_READING = [
13250
13427
  "drag-element"
13251
13428
  ];
13252
13429
  var BEHAVIORS_PICKING = ["drag-canvas", ZOOM_CANVAS];
13430
+ var VIEW_STORE_PREFIX = "yui_gobj_tree_view:";
13431
+ function view_store_key(gobj) {
13432
+ return VIEW_STORE_PREFIX + (0, _yuneta_gobj_js.gobj_name)(gobj);
13433
+ }
13434
+ function restore_view_shape(gobj) {
13435
+ let priv = gobj.priv;
13436
+ let state = load_view_state(gobj);
13437
+ if (!state) return;
13438
+ if (state.layout && LAYOUTS[state.layout]) {
13439
+ (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "layout", state.layout);
13440
+ if (priv.$layout_select) priv.$layout_select.value = state.layout;
13441
+ }
13442
+ if (state.collapsed && typeof state.collapsed === "object") priv.collapse_state = Object.assign({}, state.collapsed);
13443
+ }
13444
+ function restore_view_state(gobj) {
13445
+ let priv = gobj.priv;
13446
+ let graph = priv.graph;
13447
+ let state = load_view_state(gobj);
13448
+ if (!graph || !state) return false;
13449
+ let done = false;
13450
+ priv.restoring = true;
13451
+ try {
13452
+ if (typeof state.zoom === "number" && state.zoom > 0) {
13453
+ graph.zoomTo(state.zoom);
13454
+ priv.last_zoom = state.zoom;
13455
+ done = true;
13456
+ }
13457
+ if (Array.isArray(state.position) && state.position.length >= 2) {
13458
+ graph.translateTo([state.position[0], state.position[1]]);
13459
+ done = true;
13460
+ }
13461
+ if (state.anchor) {
13462
+ priv.anchor_name = state.anchor;
13463
+ priv.anchor_state = "on";
13464
+ reanchor(gobj);
13465
+ yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
13466
+ paint_anchor_mark(gobj);
13467
+ }
13468
+ } catch (e) {
13469
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot restore the saved view: ${e}`);
13470
+ } finally {
13471
+ priv.restoring = false;
13472
+ }
13473
+ yui_graph_update_zoom((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.graph);
13474
+ return done;
13475
+ }
13476
+ function save_view_state(gobj) {
13477
+ let priv = gobj.priv;
13478
+ let graph = priv.graph;
13479
+ if (!graph || priv.restoring) return;
13480
+ let state = {
13481
+ layout: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout"),
13482
+ collapsed: priv.collapse_state || {},
13483
+ anchor: priv.anchor_state === "on" ? priv.anchor_name : ""
13484
+ };
13485
+ try {
13486
+ state.zoom = graph.getZoom();
13487
+ state.position = graph.getPosition();
13488
+ } catch (e) {}
13489
+ (0, _yuneta_gobj_js.kw_set_local_storage_value)(view_store_key(gobj), state);
13490
+ }
13491
+ function load_view_state(gobj) {
13492
+ let state = (0, _yuneta_gobj_js.kw_get_local_storage_value)(view_store_key(gobj), null, false);
13493
+ return state && typeof state === "object" ? state : null;
13494
+ }
13253
13495
  var PRIVATE_DATA$9 = {
13254
13496
  canvas_id: "",
13255
13497
  graph: null,
@@ -13267,6 +13509,8 @@ var PRIVATE_DATA$9 = {
13267
13509
  anchor_id: "",
13268
13510
  anchor_name: "",
13269
13511
  anchor_state: "off",
13512
+ restoring: false,
13513
+ restored: false,
13270
13514
  last_zoom: 1
13271
13515
  };
13272
13516
  var __gclass__$9 = null;
@@ -13473,6 +13717,7 @@ function mt_create$9(gobj) {
13473
13717
  * Framework Method: Start
13474
13718
  ***************************************************************/
13475
13719
  function mt_start$9(gobj) {
13720
+ restore_view_shape(gobj);
13476
13721
  build_graph$2(gobj);
13477
13722
  load_tree(gobj);
13478
13723
  }
@@ -13610,8 +13855,12 @@ function build_popover(gobj, $container) {
13610
13855
  $header.appendChild($title);
13611
13856
  let $close = document.createElement("button");
13612
13857
  $close.type = "button";
13858
+ $close.className = "GOBJ_TREE_POPOVER_CLOSE";
13613
13859
  $close.textContent = "×";
13614
13860
  $close.title = (0, i18next.t)("close");
13861
+ $close.setAttribute("data-i18n-title", "close");
13862
+ $close.setAttribute("aria-label", (0, i18next.t)("close"));
13863
+ $close.setAttribute("data-i18n-aria-label", "close");
13615
13864
  $close.style.cssText = `
13616
13865
  background: transparent;
13617
13866
  border: none;
@@ -13745,6 +13994,7 @@ function build_graph$2(gobj) {
13745
13994
  if (z === priv.last_zoom) return;
13746
13995
  priv.last_zoom = z;
13747
13996
  if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
13997
+ save_view_state(gobj);
13748
13998
  });
13749
13999
  graph.on(_antv_g6.NodeEvent.CLICK, (evt) => {
13750
14000
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_NODE_CLICK", { evt }, gobj);
@@ -13894,7 +14144,7 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
13894
14144
  if (compact) {
13895
14145
  let compact_label = (0, _yuneta_gobj_js.escapeHtml)(instance_name ? gclass_name + "^" + instance_name : gclass_name);
13896
14146
  node_html = `
13897
- <div style="
14147
+ <div class="GOBJ_CARD" data-gobj-name="${(0, _yuneta_gobj_js.escapeHtml)(full_name)}" style="
13898
14148
  width: ${width}px;
13899
14149
  height: ${height}px;
13900
14150
  background: ${cs.bg};
@@ -13931,7 +14181,7 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
13931
14181
  }
13932
14182
  let badges_html = badges.map((b) => `<span style="font-size:10px; padding:0 5px; margin-right:4px; background:${cs.tagbg}; color:${cs.border}; border:1px solid ${cs.border}; border-radius:6px; white-space:nowrap;">${b}</span>`).join("");
13933
14183
  node_html = `
13934
- <div style="
14184
+ <div class="GOBJ_CARD" data-gobj-name="${(0, _yuneta_gobj_js.escapeHtml)(full_name)}" style="
13935
14185
  width: ${width}px;
13936
14186
  height: ${height}px;
13937
14187
  background: ${cs.bg};
@@ -14039,11 +14289,16 @@ function load_tree(gobj) {
14039
14289
  });
14040
14290
  graph.render().then(() => {
14041
14291
  reanchor(gobj);
14292
+ paint_anchor_mark(gobj);
14042
14293
  if (anchor) {
14043
14294
  restore_view_anchored(gobj, anchor);
14044
14295
  return;
14045
14296
  }
14046
14297
  if (preserve_view) return;
14298
+ if (!priv.restored) {
14299
+ priv.restored = true;
14300
+ if (restore_view_state(gobj)) return;
14301
+ }
14047
14302
  Promise.resolve(graph.zoomTo(1)).then(() => {
14048
14303
  if (!yui_graph_center_on(graph, priv.anchor_id)) yui_graph_center_on(graph, root_id);
14049
14304
  priv.last_zoom = 1;
@@ -14474,6 +14729,7 @@ var GobjLanesHLayout = class extends _antv_g6.BaseLayout {
14474
14729
  *
14475
14730
  ************************************************************/
14476
14731
  function ac_refresh$3(gobj, event, kw, src) {
14732
+ save_view_state(gobj);
14477
14733
  refresh_tree(gobj);
14478
14734
  return 0;
14479
14735
  }
@@ -14501,6 +14757,8 @@ function ac_change_layout(gobj, event, kw, src) {
14501
14757
  (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "layout", new_key);
14502
14758
  if (priv.$layout_select && priv.$layout_select.value !== new_key) priv.$layout_select.value = new_key;
14503
14759
  apply_layout(gobj);
14760
+ save_view_state(gobj);
14761
+ save_view_state(gobj);
14504
14762
  refresh_tree(gobj);
14505
14763
  return 0;
14506
14764
  }
@@ -14520,6 +14778,7 @@ function ac_toggle_collapse(gobj, event, kw, src) {
14520
14778
  priv.collapse_state[node_data.full_name] = "expanded";
14521
14779
  for (let child_info of node_data.direct_children || []) if (child_info.has_grandchildren) priv.collapse_state[child_info.full_name] = "collapsed";
14522
14780
  } else priv.collapse_state[node_data.full_name] = "collapsed";
14781
+ save_view_state(gobj);
14523
14782
  refresh_tree(gobj, { anchor });
14524
14783
  return 0;
14525
14784
  }
@@ -14599,10 +14858,31 @@ function reanchor(gobj) {
14599
14858
  ************************************************************/
14600
14859
  function ac_center_anchor$1(gobj, event, kw, src) {
14601
14860
  let priv = gobj.priv;
14861
+ paint_anchor_mark(gobj);
14862
+ save_view_state(gobj);
14602
14863
  if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
14603
14864
  return 0;
14604
14865
  }
14605
14866
  /************************************************************
14867
+ * Mark the card the camera is holding.
14868
+ *
14869
+ * A class on the card's own element and not a G6 node state:
14870
+ * G6 paints no state style on an html node.
14871
+ *
14872
+ * Re-applied after every rebuild, because the cards are new
14873
+ * DOM each time and the mark would otherwise last until the
14874
+ * next expand.
14875
+ ************************************************************/
14876
+ function paint_anchor_mark(gobj) {
14877
+ let priv = gobj.priv;
14878
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
14879
+ if (!$container) return;
14880
+ for (let $card of $container.querySelectorAll(".GOBJ_CARD_ANCHORED")) $card.classList.remove("GOBJ_CARD_ANCHORED");
14881
+ if (priv.anchor_state !== "on" || !priv.anchor_name) return;
14882
+ let $it = $container.querySelector(".GOBJ_CARD[data-gobj-name=\"" + CSS.escape(priv.anchor_name) + "\"]");
14883
+ if ($it) $it.classList.add("GOBJ_CARD_ANCHORED");
14884
+ }
14885
+ /************************************************************
14606
14886
  * Reading, or picking the node to anchor.
14607
14887
  ************************************************************/
14608
14888
  function set_picking_mode(gobj, picking) {
@@ -14630,6 +14910,8 @@ function ac_toggle_anchor$1(gobj, event, kw, src) {
14630
14910
  }
14631
14911
  yui_graph_update_anchor((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container"), priv.anchor_state);
14632
14912
  set_picking_mode(gobj, priv.anchor_state === "arming");
14913
+ paint_anchor_mark(gobj);
14914
+ save_view_state(gobj);
14633
14915
  return 0;
14634
14916
  }
14635
14917
  /************************************************************
@@ -14713,6 +14995,7 @@ function ac_node_click$3(gobj, event, kw, src) {
14713
14995
  function ac_show$6(gobj, event, kw, src) {
14714
14996
  let priv = gobj.priv;
14715
14997
  let graph = priv.graph;
14998
+ save_view_state(gobj);
14716
14999
  refresh_tree(gobj);
14717
15000
  if (graph) {
14718
15001
  let $canvas = document.getElementById(priv.canvas_id);
@@ -49031,7 +49314,7 @@ function configure_toolbar(gobj) {
49031
49314
  {
49032
49315
  id: "g6-icon-anchor",
49033
49316
  value: "anchor",
49034
- className: "EV_TOGGLE_ANCHOR" + (priv.anchor_state !== "off" ? " pressed_state" : "") + (priv.anchor_state === "arming" ? " color_pending_state" : ""),
49317
+ className: "EV_TOGGLE_ANCHOR" + (priv.anchor_state === "on" ? " pressed_state" : "") + (priv.anchor_state === "arming" ? " color_pending_state" : ""),
49035
49318
  title: anchor_title(priv.anchor_state)
49036
49319
  }
49037
49320
  ];
@@ -51940,15 +52223,15 @@ function refresh_minimap(gobj) {
51940
52223
  * same three-way choice — it lived inline in the theme refresh and is
51941
52224
  * shared now. Returns null for a node that carries no desc.
51942
52225
  ************************************************************/
51943
- function node_innerHTML_of(nd, theme, highlight, selected) {
52226
+ function node_innerHTML_of(nd, theme, highlight, selected, anchored) {
51944
52227
  if (!nd || !nd.data || !nd.data.desc) return null;
51945
52228
  let desc = nd.data.desc;
51946
52229
  let record = nd.data.record || {};
51947
52230
  let label = node_label(desc, record);
51948
52231
  switch (desc.node_treedb_type) {
51949
- case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight, selected);
51950
- case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected);
51951
- case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected);
52232
+ case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight, selected, anchored);
52233
+ case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected, anchored);
52234
+ case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected, anchored);
51952
52235
  }
51953
52236
  return null;
51954
52237
  }
@@ -51969,7 +52252,7 @@ function repaint_cards(gobj, ids) {
51969
52252
  let selected = new Set(priv._selected_paint_ids || []);
51970
52253
  let updates = [];
51971
52254
  for (let id of ids) {
51972
- let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, focus.has(id), selected.has(id));
52255
+ let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, focus.has(id), selected.has(id), priv.anchor_state === "on" && id === priv.anchor_id);
51973
52256
  if (html !== null) updates.push({
51974
52257
  id,
51975
52258
  style: { innerHTML: html }
@@ -52043,6 +52326,23 @@ function refresh_default_edges_theme(gobj, theme) {
52043
52326
  * it, which is also the order that reads correctly when only one
52044
52327
  * of them is on.
52045
52328
  ************************************************************/
52329
+ /************************************************************
52330
+ * The mark of the card the CAMERA is holding.
52331
+ *
52332
+ * An outline and not another ring, and DASHED: the two rings
52333
+ * already mean things here -- amber is a find match or a
52334
+ * focused topic, blue is the selection -- and a third solid
52335
+ * ring would be a third thing to learn in the same channel.
52336
+ * A dash is a different channel, so it reads beside either of
52337
+ * them, and it says "provisional" the way a camera state is.
52338
+ *
52339
+ * The same dash the other two viewers draw: this is one
52340
+ * feature with one control, and it should not be three
52341
+ * drawings.
52342
+ ************************************************************/
52343
+ function anchor_outline(anchored) {
52344
+ return anchored ? "outline: 3px dashed #fa8c16; outline-offset: 2px;" : "";
52345
+ }
52046
52346
  function ring_shadow(highlight, selected, base_shadow) {
52047
52347
  let rings = [];
52048
52348
  if (highlight) rings.push(`0 0 0 4px ${HIGHLIGHT_HALO}`);
@@ -52057,7 +52357,7 @@ function ring_shadow(highlight, selected, base_shadow) {
52057
52357
  * but lighter (1px border, no shadow, single line). The name is
52058
52358
  * always legible (ellipsis + native title tooltip on overflow).
52059
52359
  ************************************************************/
52060
- function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected) {
52360
+ function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected, anchored) {
52061
52361
  let title = key || label;
52062
52362
  let dark = theme === "dark";
52063
52363
  let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
@@ -52070,13 +52370,14 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight, selecte
52070
52370
  margin-right: 6px; flex: 0 0 auto;
52071
52371
  "/>`;
52072
52372
  return `
52073
- <div title="${(0, _yuneta_gobj_js.escapeHtml)(title)}" style="
52373
+ <div class="TREEDB_CARD" title="${(0, _yuneta_gobj_js.escapeHtml)(title)}" style="
52074
52374
  box-sizing: border-box;
52075
52375
  width: 100%;
52076
52376
  height: 100%;
52077
52377
  background: ${bg};
52078
52378
  border: ${highlight ? "3px" : "1px"} solid ${border};
52079
52379
  ${ring_shadow(highlight, selected, "")}
52380
+ ${anchor_outline(anchored)}
52080
52381
  border-radius: 8px;
52081
52382
  color: ${text_color};
52082
52383
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
@@ -52102,7 +52403,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight, selecte
52102
52403
  * colour is kept (per-topic differentiation) but softened via
52103
52404
  * color-mix instead of a harsh saturated fill. Theme-aware.
52104
52405
  ************************************************************/
52105
- function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected) {
52406
+ function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected, anchored) {
52106
52407
  let title = key || label;
52107
52408
  let dark = theme === "dark";
52108
52409
  let surface = dark ? "#1b2230" : "#ffffff";
@@ -52137,7 +52438,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
52137
52438
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
52138
52439
  ">${(0, _yuneta_gobj_js.escapeHtml)(topic_name)}</div>`;
52139
52440
  return `
52140
- <div title="${(0, _yuneta_gobj_js.escapeHtml)(title)}" style="
52441
+ <div class="TREEDB_CARD" title="${(0, _yuneta_gobj_js.escapeHtml)(title)}" style="
52141
52442
  box-sizing: border-box;
52142
52443
  width: 100%;
52143
52444
  height: 100%;
@@ -52145,6 +52446,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
52145
52446
  border: ${highlight ? "3px" : "1.5px"} ${border_style} ${border};
52146
52447
  border-radius: 10px;
52147
52448
  ${ring_shadow(highlight, selected, shadow)}
52449
+ ${anchor_outline(anchored)}
52148
52450
  color: ${title_color};
52149
52451
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
52150
52452
  display: flex;
@@ -53128,12 +53430,14 @@ function after_camera(gobj, moved) {
53128
53430
  ************************************************************/
53129
53431
  function ac_toggle_anchor(gobj, event, kw, src) {
53130
53432
  let priv = gobj.priv;
53433
+ let was = priv.anchor_id;
53131
53434
  if (priv.anchor_state === "off") priv.anchor_state = "arming";
53132
53435
  else {
53133
53436
  priv.anchor_state = "off";
53134
53437
  priv.anchor_id = "";
53135
53438
  }
53136
53439
  update_toolbar(gobj);
53440
+ if (was) repaint_cards(gobj, /* @__PURE__ */ new Set([was]));
53137
53441
  return 0;
53138
53442
  }
53139
53443
  /************************************************************
@@ -53311,6 +53615,7 @@ function ac_node_click$2(gobj, event, kw, src) {
53311
53615
  priv.anchor_id = node_id;
53312
53616
  priv.anchor_state = "on";
53313
53617
  update_toolbar(gobj);
53618
+ repaint_cards(gobj, /* @__PURE__ */ new Set([node_id]));
53314
53619
  (0, _yuneta_gobj_js.gobj_post_event)(gobj, "EV_CENTER_ANCHOR", {}, gobj);
53315
53620
  return 0;
53316
53621
  }