@yuneta/gobj-ui 7.23.0 → 7.23.2

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
@@ -439,15 +439,34 @@ the order of arrival: the viewer opens on the **tree** (`view_mode` default).
439
439
  | `tree` | *where is this value, and what is around it* | the lazy view; the only one that can drill; the default |
440
440
  | `graph` | *what shape is this* | a hosted `C_YUI_JSON_GRAPH` child (AntV/G6) |
441
441
 
442
- The graph carries **its own** toolbar, and since `7.22.0` that toolbar has the
443
- same two facilities the tree has: a **find box** (rate-limited, `EV_FIND_NODES`)
442
+ Since `7.23.2` the schema and cell-JSON popups of `C_YUI_TREEDB_TOPIC_WITH_FORM`
443
+ present that viewer as a floating `C_YUI_WINDOW` on a laptop — movable,
444
+ resizable, maximisable, position remembered — and keep the modal sheet on a
445
+ phone. A JSON document is read *while* looking at the table it came from, which
446
+ a modal cannot allow; a phone has no room to arrange anything, so the sheet
447
+ stays. Note the close contract: `C_YUI_WINDOW.close_window()` calls `on_close`
448
+ and THEN destroys itself, so a host must drop its reference rather than destroy
449
+ the window too.
450
+
451
+ The graph carries **its own** toolbar, a **layout picker**
452
+ (`EV_CHANGE_LAYOUT {layout}`: `vertical tree`, `dagre top-down`,
453
+ `dagre left-right` — the layout and the edge type move together), and since
454
+ `7.22.0` the same two facilities the tree has: a **find box** (rate-limited, `EV_FIND_NODES`)
444
455
  that highlights matching rows and outlines the cards they are in and says how
445
456
  many matched, and **expand-all / collapse-all** (`EV_EXPAND_ALL` /
446
457
  `EV_COLLAPSE_ALL`) that fold every card but the root, marking each cut with
447
458
  `▸ N` so the shape stays legible and you can see where the rest went. Since
448
459
  `7.23.0` each card that HAS a branch also carries its own handle in its header
449
- (`▾` open, `▸ N` folded, `EV_TOGGLE_FOLD {path}`); a leaf gets a spacer instead,
450
- because a handle that does nothing is worse than none.
460
+ (`EV_TOGGLE_FOLD {path}`); a leaf gets a spacer of the same width instead,
461
+ because a handle that does nothing is worse than none and the labels should
462
+ stay on one axis.
463
+
464
+ That handle is the **same filled chip the gobj tree draws** (`+N` folded, `−`
465
+ open — see `render_toggle_html` in `c_yui_gobj_tree_js.js`), and it is a chip
466
+ for a reason: `7.23.0` shipped it as a bare `▾` and on a phone it was
467
+ invisible. At the zoom that fits a document on screen a glyph is a couple of
468
+ pixels of ink; a filled chip is still a visible blob and reads as something you
469
+ press. `+N` also carries the count of what is hidden.
451
470
 
452
471
  The handle is delegated from the canvas mount in the **capture** phase, over
453
472
  `pointerdown`/`pointerup`/`mousedown`/`click`: the card is an `innerHTML`
@@ -3518,7 +3518,7 @@ function composed_title(gobj) {
3518
3518
  * Below the mobile breakpoint the window is a full-screen
3519
3519
  * sheet (no float / drag / resize).
3520
3520
  ************************************************************/
3521
- function is_mobile$2() {
3521
+ function is_mobile$3() {
3522
3522
  return (window.innerWidth || document.documentElement.offsetWidth) <= MOBILE_MAX;
3523
3523
  }
3524
3524
  /************************************************************
@@ -3551,7 +3551,7 @@ function do_minimize(gobj) {
3551
3551
  ************************************************************/
3552
3552
  function build_ui$15(gobj) {
3553
3553
  ensure_window_style();
3554
- let mobile = is_mobile$2();
3554
+ let mobile = is_mobile$3();
3555
3555
  let header = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "header");
3556
3556
  if ((0, _yuneta_gobj_js.is_gobj)(header)) header = (0, _yuneta_gobj_js.gobj_read_attr)(header, "$container");
3557
3557
  if (!header) header = build_default_header(gobj);
@@ -3889,7 +3889,7 @@ function min$1(gobj) {
3889
3889
  * handlers moving
3890
3890
  ************************************************************/
3891
3891
  function mvStart(gobj, evt) {
3892
- if (is_mobile$2()) return;
3892
+ if (is_mobile$3()) return;
3893
3893
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
3894
3894
  let window_rect = $container.getBoundingClientRect();
3895
3895
  let x = evt.screenX;
@@ -4048,7 +4048,7 @@ function do_center(gobj, x, y, width, height) {
4048
4048
  function handleResize(gobj) {
4049
4049
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
4050
4050
  if (!$container) return;
4051
- if (is_mobile$2()) {
4051
+ if (is_mobile$3()) {
4052
4052
  $container.classList.add("is-mobile-sheet");
4053
4053
  let vw = window.innerWidth || document.documentElement.offsetWidth;
4054
4054
  let vh = window.innerHeight || document.documentElement.offsetHeight;
@@ -9479,6 +9479,56 @@ var FOLD_SWALLOWED_EVENTS = [
9479
9479
  "mousedown",
9480
9480
  "click"
9481
9481
  ];
9482
+ var LAYOUTS$1 = {
9483
+ "json-tree": {
9484
+ label: "vertical tree",
9485
+ g6_layout: { type: "json-tree-layout" },
9486
+ edge_type: "cubic-vertical"
9487
+ },
9488
+ "dagre-tb": {
9489
+ label: "dagre top-down",
9490
+ g6_layout: {
9491
+ type: "antv-dagre",
9492
+ rankdir: "TB",
9493
+ nodesep: 20,
9494
+ ranksep: 40
9495
+ },
9496
+ edge_type: "polyline"
9497
+ },
9498
+ "dagre-lr": {
9499
+ label: "dagre left-right",
9500
+ g6_layout: {
9501
+ type: "antv-dagre",
9502
+ rankdir: "LR",
9503
+ nodesep: 20,
9504
+ ranksep: 40
9505
+ },
9506
+ edge_type: "polyline"
9507
+ }
9508
+ };
9509
+ var DEFAULT_LAYOUT = "json-tree";
9510
+ function get_layout_cfg$1(key) {
9511
+ return LAYOUTS$1[key] || LAYOUTS$1[DEFAULT_LAYOUT];
9512
+ }
9513
+ /************************************************************
9514
+ * A layout's label, with every key SPELLED OUT inside t().
9515
+ *
9516
+ * Never t(LAYOUTS[key].label) nor a `data-i18n` fed from that
9517
+ * table: the apps' validate-locales scans for t("literal"),
9518
+ * so a key reached through a variable is a key it cannot
9519
+ * demand — and i18next answers an undefined key with the key
9520
+ * itself, which renders in lower-case English and never
9521
+ * changes language. Twice already in this component's
9522
+ * history (7.20.0 `tree view`, and this table on its first
9523
+ * draft).
9524
+ ************************************************************/
9525
+ function layout_label(key) {
9526
+ switch (key) {
9527
+ case "dagre-tb": return (0, i18next.t)("dagre top-down");
9528
+ case "dagre-lr": return (0, i18next.t)("dagre left-right");
9529
+ default: return (0, i18next.t)("vertical tree");
9530
+ }
9531
+ }
9482
9532
  /***************************************************************
9483
9533
  * Data
9484
9534
  ***************************************************************/
@@ -9489,6 +9539,7 @@ var attrs_table$11 = [
9489
9539
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$container", 0, null, "Container element"),
9490
9540
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "canvas_id", 0, "", "Canvas ID"),
9491
9541
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "wide", 0, "36px", "Height of header"),
9542
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "layout", 0, "json-tree", "Layout key (see LAYOUTS)"),
9492
9543
  (0, _yuneta_gobj_js.SDATA_END)()
9493
9544
  ];
9494
9545
  var PRIVATE_DATA$11 = {
@@ -9504,6 +9555,7 @@ var PRIVATE_DATA$11 = {
9504
9555
  match_count: 0,
9505
9556
  find_timer: null,
9506
9557
  fold_listener: null,
9558
+ $layout_select: null,
9507
9559
  $find_input: null,
9508
9560
  $find_result: null,
9509
9561
  $find_count: null
@@ -9678,6 +9730,7 @@ function build_ui$10(gobj) {
9678
9730
  };
9679
9731
  for (let type of FOLD_SWALLOWED_EVENTS) $mount.addEventListener(type, priv.fold_listener, true);
9680
9732
  } else (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no canvas mount, fold handles are dead`);
9733
+ priv.$layout_select = $container.querySelector(".JSON_GRAPH_LAYOUT");
9681
9734
  priv.$find_input = $container.querySelector(".JSON_GRAPH_FIND_INPUT");
9682
9735
  priv.$find_result = $container.querySelector(".JSON_GRAPH_FIND_RESULT");
9683
9736
  priv.$find_count = $container.querySelector(".JSON_GRAPH_FIND_COUNT");
@@ -9835,7 +9888,42 @@ function make_toolbar$3(gobj) {
9835
9888
  } }
9836
9889
  ]);
9837
9890
  }
9838
- let right_items = [graph_fold_button(gobj, "EV_EXPAND_ALL", "expand all", true), graph_fold_button(gobj, "EV_COLLAPSE_ALL", "collapse all", false)];
9891
+ let current_layout = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout") || DEFAULT_LAYOUT;
9892
+ let options = Object.keys(LAYOUTS$1).map(function(key) {
9893
+ let attrs = {
9894
+ value: key,
9895
+ "data-i18n": LAYOUTS$1[key].label
9896
+ };
9897
+ if (key === current_layout) attrs.selected = "selected";
9898
+ return [
9899
+ "option",
9900
+ attrs,
9901
+ layout_label(key)
9902
+ ];
9903
+ });
9904
+ let right_items = [
9905
+ (0, _yuneta_gobj_js.createElement2)([
9906
+ "select",
9907
+ {
9908
+ class: "JSON_GRAPH_LAYOUT select",
9909
+ style: {
9910
+ height: toolbar_wide,
9911
+ "margin-left": "0.5em"
9912
+ },
9913
+ title: (0, i18next.t)("layout"),
9914
+ "data-i18n-title": "layout",
9915
+ "aria-label": (0, i18next.t)("layout"),
9916
+ "data-i18n-aria-label": "layout"
9917
+ },
9918
+ options,
9919
+ { change: (evt) => {
9920
+ evt.stopPropagation();
9921
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CHANGE_LAYOUT", { layout: evt.target.value }, gobj);
9922
+ } }
9923
+ ]),
9924
+ graph_fold_button(gobj, "EV_EXPAND_ALL", "expand all", true),
9925
+ graph_fold_button(gobj, "EV_COLLAPSE_ALL", "collapse all", false)
9926
+ ];
9839
9927
  const $toolbar = yui_toolbar({}, [
9840
9928
  [
9841
9929
  "div",
@@ -9861,6 +9949,7 @@ function make_toolbar$3(gobj) {
9861
9949
  ************************************************************/
9862
9950
  function build_graph$3(gobj) {
9863
9951
  let priv = gobj.priv;
9952
+ let layout_cfg = get_layout_cfg$1((0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout"));
9864
9953
  const graph = priv.graph = new _antv_g6.Graph({
9865
9954
  container: priv.canvas_id,
9866
9955
  animation: false,
@@ -9868,14 +9957,14 @@ function build_graph$3(gobj) {
9868
9957
  zoomRange: [.1, 4],
9869
9958
  node: { style: { labelPlacement: "center" } },
9870
9959
  edge: {
9871
- type: "cubic-vertical",
9960
+ type: layout_cfg.edge_type,
9872
9961
  style: {
9873
9962
  stroke: "#999",
9874
9963
  lineWidth: 1,
9875
9964
  endArrow: true
9876
9965
  }
9877
9966
  },
9878
- layout: { type: "json-tree-layout" },
9967
+ layout: layout_cfg.g6_layout,
9879
9968
  behaviors: ["drag-canvas", "zoom-canvas"]
9880
9969
  });
9881
9970
  if (priv.theme) graph.setTheme(priv.theme);
@@ -9940,15 +10029,40 @@ function build_cell_html(key, value, type, dark, matched) {
9940
10029
  * innerHTML string, so there is no other way to hang a
9941
10030
  * handler on it.
9942
10031
  *
9943
- * Folded it shows the glyph AND the count of branches hidden
9944
- * under it; open, just the glyph. No word, no title: a word
9945
- * inside this string could carry no i18n key.
9946
- ************************************************************/
9947
- function fold_toggle_html(path, foldable, folded, branches) {
9948
- if (!foldable) return `<span style="width:1.1em; flex:0 0 auto;"></span>`;
9949
- let glyph = folded ? "&#9656;" : "&#9662;";
9950
- let text = folded ? `${glyph} ${branches}` : glyph;
9951
- return `<span data-fold-path="${(0, _yuneta_gobj_js.escapeHtml)(path)}" style="flex:0 0 auto; cursor:pointer; user-select:none; padding:0 .2em; min-width:1.1em; text-align:left;">${text}</span>`;
10032
+ * A filled CHIP, the same one the gobj tree draws
10033
+ * (`c_yui_gobj_tree_js.js`, render_toggle_html): background,
10034
+ * border, radius, and `+N` / `−` rather than a triangle. It
10035
+ * shipped once as a bare `▾` and on a phone it was invisible —
10036
+ * a glyph is a couple of pixels of ink at the zoom that fits a
10037
+ * document on screen, while a filled chip stays a visible blob
10038
+ * and reads as something you press. `+N` also carries the
10039
+ * count of what is hidden, which the open state does not need.
10040
+ *
10041
+ * No `title`: it would be a word inside an innerHTML string,
10042
+ * where it could carry no i18n key and would sit in English
10043
+ * forever. `+3` and `−` need no language.
10044
+ ************************************************************/
10045
+ function fold_toggle_html(path, foldable, folded, branches, colors) {
10046
+ if (!foldable) return `<span style="width:24px; flex:0 0 auto;"></span>`;
10047
+ let label = folded ? "+" + branches : "&#8722;";
10048
+ return `<span data-fold-path="${(0, _yuneta_gobj_js.escapeHtml)(path)}" style="
10049
+ flex: 0 0 auto;
10050
+ display: inline-flex;
10051
+ align-items: center;
10052
+ justify-content: center;
10053
+ min-width: 22px;
10054
+ height: 16px;
10055
+ padding: 0 5px;
10056
+ background: ${colors.bg};
10057
+ color: ${colors.border};
10058
+ border: 1px solid ${colors.bg};
10059
+ border-radius: 6px;
10060
+ font-size: 11px;
10061
+ font-weight: 700;
10062
+ line-height: 1;
10063
+ cursor: pointer;
10064
+ user-select: none;
10065
+ ">${label}</span>`;
9952
10066
  }
9953
10067
  /************************************************************
9954
10068
  * Does this key/value pair match the find term?
@@ -10060,7 +10174,7 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id) {
10060
10174
  display: flex;
10061
10175
  align-items: center;
10062
10176
  gap: 6px;
10063
- ">${fold_toggle_html(path, foldable, folded, pending_complex.length)}<span style="flex:1 1 auto; text-align:center;">${(0, _yuneta_gobj_js.escapeHtml)(label)}</span></div>
10177
+ ">${fold_toggle_html(path, foldable, folded, pending_complex.length, colors)}<span style="flex:1 1 auto; text-align:center;">${(0, _yuneta_gobj_js.escapeHtml)(label)}</span></div>
10064
10178
  ${content_html}
10065
10179
  </div>`;
10066
10180
  let node = {
@@ -10277,6 +10391,34 @@ function ac_refresh$5(gobj, event, kw, src) {
10277
10391
  }
10278
10392
  /************************************************************
10279
10393
  * {theme: "dark"|"light"} — the app switched theme.
10394
+ * EV_CHANGE_LAYOUT { layout }
10395
+ *
10396
+ * The layout AND the edge type move together — a vertical
10397
+ * layout with horizontal cubics draws every edge the wrong way
10398
+ * round — and then the graph is rebuilt and refitted, because a
10399
+ * new layout is a new shape and the old camera framed the old
10400
+ * one.
10401
+ ************************************************************/
10402
+ function ac_change_layout$1(gobj, event, kw, src) {
10403
+ let priv = gobj.priv;
10404
+ let key = kw && kw.layout || "";
10405
+ if (!LAYOUTS$1[key]) {
10406
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: unknown layout '${key}'`);
10407
+ return -1;
10408
+ }
10409
+ if (key === (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout")) return 0;
10410
+ (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "layout", key);
10411
+ if (priv.$layout_select && priv.$layout_select.value !== key) priv.$layout_select.value = key;
10412
+ let cfg = get_layout_cfg$1(key);
10413
+ if (priv.graph) priv.graph.setOptions({
10414
+ layout: cfg.g6_layout,
10415
+ edge: { type: cfg.edge_type }
10416
+ });
10417
+ refresh_json(gobj);
10418
+ update_find_result(gobj);
10419
+ return 0;
10420
+ }
10421
+ /************************************************************
10280
10422
  * EV_TOGGLE_FOLD { path } — fold or unfold ONE card.
10281
10423
  *
10282
10424
  * The camera does not move: folding one branch is a local
@@ -10491,6 +10633,11 @@ function create_gclass$11(gclass_name) {
10491
10633
  ac_toggle_fold,
10492
10634
  null
10493
10635
  ],
10636
+ [
10637
+ "EV_CHANGE_LAYOUT",
10638
+ ac_change_layout$1,
10639
+ null
10640
+ ],
10494
10641
  [
10495
10642
  "EV_EXPAND_ALL",
10496
10643
  ac_expand_all$2,
@@ -10552,6 +10699,7 @@ function create_gclass$11(gclass_name) {
10552
10699
  ["EV_REFRESH", 0],
10553
10700
  ["EV_FIND_NODES", 0],
10554
10701
  ["EV_TOGGLE_FOLD", 0],
10702
+ ["EV_CHANGE_LAYOUT", 0],
10555
10703
  ["EV_EXPAND_ALL", 0],
10556
10704
  ["EV_COLLAPSE_ALL", 0],
10557
10705
  ["EV_THEME", 0],
@@ -10885,12 +11033,13 @@ function mt_start$10(gobj) {
10885
11033
  /***************************************************************
10886
11034
  * Framework Method: Stop
10887
11035
  ***************************************************************/
10888
- function mt_stop$10(gobj) {}
11036
+ function mt_stop$10(gobj) {
11037
+ teardown_graph_child(gobj);
11038
+ }
10889
11039
  /***************************************************************
10890
11040
  * Framework Method: Destroy
10891
11041
  ***************************************************************/
10892
11042
  function mt_destroy$10(gobj) {
10893
- teardown_graph_child(gobj);
10894
11043
  destroy_ui$5(gobj);
10895
11044
  }
10896
11045
  /***************************
@@ -16190,7 +16339,7 @@ function treedb_delete_node$1(gobj, treedb_name, topic_name, record, options) {
16190
16339
  /************************************************************
16191
16340
  * True on a phone-width viewport (Bulma's mobile breakpoint).
16192
16341
  ************************************************************/
16193
- function is_mobile$1() {
16342
+ function is_mobile$2() {
16194
16343
  return typeof window !== "undefined" && window.innerWidth <= 768;
16195
16344
  }
16196
16345
  /************************************************************
@@ -16209,7 +16358,7 @@ function open_json_viewer$1(gobj) {
16209
16358
  yui_shell_show_error(yui_shell_of(gobj), "raw json viewer unavailable", { t: i18next.t });
16210
16359
  return;
16211
16360
  }
16212
- let mobile = is_mobile$1();
16361
+ let mobile = is_mobile$2();
16213
16362
  let shell = yui_shell_of(gobj);
16214
16363
  let jv = (0, _yuneta_gobj_js.gobj_create_service)(`treedb-topics-json-${(0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj))}`, "C_YUI_JSON", { subscriber: gobj }, gobj);
16215
16364
  if (!jv) {
@@ -17422,10 +17571,102 @@ var PRIVATE_DATA$5 = {
17422
17571
  form_modal: null,
17423
17572
  schema_gobj: null,
17424
17573
  schema_modal: null,
17574
+ schema_win: null,
17425
17575
  cell_json_gobj: null,
17426
- cell_json_modal: null
17576
+ cell_json_modal: null,
17577
+ cell_json_win: null
17427
17578
  };
17428
17579
  var __gclass__$5 = null;
17580
+ function is_mobile$1() {
17581
+ return typeof window !== "undefined" && window.innerWidth <= 768;
17582
+ }
17583
+ /************************************************************
17584
+ * Present a JSON viewer's $container.
17585
+ *
17586
+ * On a LAPTOP a floating C_YUI_WINDOW: a JSON document is
17587
+ * something you read WHILE looking at the table behind it —
17588
+ * you move it, you resize it, you maximise it when the
17589
+ * document is big. A modal can do none of that, and it
17590
+ * darkens exactly the rows you opened the cell from.
17591
+ *
17592
+ * On a phone the modal sheet stays: there is no room to
17593
+ * arrange anything, and the sheet already carries the shell's
17594
+ * Escape / Back handling.
17595
+ *
17596
+ * Returns {win, modal} — exactly one of them is set.
17597
+ ************************************************************/
17598
+ function present_json_popup(gobj, $box, opts) {
17599
+ let shell = yui_shell_of(gobj);
17600
+ if (!shell) {
17601
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no shell, cannot present the json`);
17602
+ return null;
17603
+ }
17604
+ if (is_mobile$1()) return {
17605
+ modal: yui_shell_show_modal(shell, $box, {
17606
+ dialog: true,
17607
+ logical_class: opts.logical_class + "_SHEET",
17608
+ title_prefix: opts.title_prefix,
17609
+ title: opts.title,
17610
+ t: i18next.t,
17611
+ on_close: opts.on_close
17612
+ }),
17613
+ win: null
17614
+ };
17615
+ let win = (0, _yuneta_gobj_js.gobj_create_service)(opts.name, "C_YUI_WINDOW", {
17616
+ $parent: popup_mount_layer(gobj),
17617
+ subscriber: null,
17618
+ modal: false,
17619
+ showMax: true,
17620
+ showFooter: false,
17621
+ resizable: true,
17622
+ center: true,
17623
+ auto_save_size_and_position: true,
17624
+ width: 720,
17625
+ height: 620,
17626
+ logical_class: opts.logical_class + "_WINDOW",
17627
+ title_prefix: opts.title_prefix,
17628
+ title: opts.title,
17629
+ icon: opts.icon,
17630
+ body: $box,
17631
+ manager: null,
17632
+ on_close: function() {
17633
+ if ((0, _yuneta_gobj_js.gobj_is_destroying)(gobj)) return;
17634
+ opts.forget_window();
17635
+ opts.on_close();
17636
+ }
17637
+ }, gobj);
17638
+ if (!win) {
17639
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot create the json window`);
17640
+ return null;
17641
+ }
17642
+ (0, _yuneta_gobj_js.gobj_start)(win);
17643
+ return {
17644
+ win,
17645
+ modal: null
17646
+ };
17647
+ }
17648
+ /************************************************************
17649
+ * Retire a presenting window: reference cleared FIRST, then
17650
+ * stop, then destroy.
17651
+ *
17652
+ * The order is the whole content of this function. The ✕ of
17653
+ * the window calls back into the teardown that calls this, so
17654
+ * clearing the reference first is what stops the recursion.
17655
+ * And it is STOP then destroy because the window was started
17656
+ * on open: handing a running gobj to gobj_destroy() makes the
17657
+ * framework rescue it and say so — "Destroying a RUNNING
17658
+ * gobj" — which is a caller's bug, not a framework message.
17659
+ * Same lesson `c_tranger_view.close_json_viewer` carries.
17660
+ ************************************************************/
17661
+ function retire_popup_window(win) {
17662
+ if (!win) return;
17663
+ try {
17664
+ if ((0, _yuneta_gobj_js.gobj_is_running)(win)) (0, _yuneta_gobj_js.gobj_stop)(win);
17665
+ (0, _yuneta_gobj_js.gobj_destroy)(win);
17666
+ } catch (e) {
17667
+ (0, _yuneta_gobj_js.log_warning)(`${GCLASS_NAME$5}: json window already gone: ${e}`);
17668
+ }
17669
+ }
17429
17670
  function popup_mount_layer(gobj) {
17430
17671
  let $layer = yui_shell_popup_layer(yui_shell_of(gobj));
17431
17672
  if ($layer) return $layer;
@@ -18596,8 +18837,7 @@ function open_schema_dialog(gobj) {
18596
18837
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no desc, no schema to show`);
18597
18838
  return;
18598
18839
  }
18599
- let shell = yui_shell_of(gobj);
18600
- if (!shell) {
18840
+ if (!yui_shell_of(gobj)) {
18601
18841
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no shell, cannot open the schema view`);
18602
18842
  return;
18603
18843
  }
@@ -18614,16 +18854,25 @@ function open_schema_dialog(gobj) {
18614
18854
  teardown_schema_child(gobj);
18615
18855
  return;
18616
18856
  }
18617
- priv.schema_modal = yui_shell_show_modal(shell, $box, {
18618
- dialog: true,
18619
- logical_class: "TREEDB_SCHEMA_SHEET",
18857
+ let presented = present_json_popup(gobj, $box, {
18858
+ name: "schemawin_" + (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj)),
18859
+ logical_class: "TREEDB_SCHEMA",
18620
18860
  title_prefix: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name"),
18621
18861
  title: "schema",
18622
- t: i18next.t,
18862
+ icon: "yi-table",
18863
+ forget_window: function() {
18864
+ priv.schema_win = null;
18865
+ },
18623
18866
  on_close: function() {
18624
18867
  teardown_schema_child(gobj);
18625
18868
  }
18626
18869
  });
18870
+ if (!presented) {
18871
+ teardown_schema_child(gobj);
18872
+ return;
18873
+ }
18874
+ priv.schema_modal = presented.modal;
18875
+ priv.schema_win = presented.win;
18627
18876
  (0, _yuneta_gobj_js.gobj_send_event)(json_view, "EV_SET_JSON", { json: desc }, gobj);
18628
18877
  }
18629
18878
  /************************************************************
@@ -18638,6 +18887,9 @@ function teardown_schema_child(gobj) {
18638
18887
  (0, _yuneta_gobj_js.gobj_destroy)(priv.schema_gobj);
18639
18888
  priv.schema_gobj = null;
18640
18889
  }
18890
+ let win = priv.schema_win;
18891
+ priv.schema_win = null;
18892
+ retire_popup_window(win);
18641
18893
  priv.schema_modal = null;
18642
18894
  }
18643
18895
  /************************************************************
@@ -18684,8 +18936,7 @@ function open_cell_json_dialog(gobj, row_id, col_id) {
18684
18936
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: row '${row_id}' has no col '${col_id}'`);
18685
18937
  return;
18686
18938
  }
18687
- let shell = yui_shell_of(gobj);
18688
- if (!shell) {
18939
+ if (!yui_shell_of(gobj)) {
18689
18940
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no shell, cannot open the cell json`);
18690
18941
  return;
18691
18942
  }
@@ -18702,16 +18953,25 @@ function open_cell_json_dialog(gobj, row_id, col_id) {
18702
18953
  teardown_cell_json_child(gobj);
18703
18954
  return;
18704
18955
  }
18705
- priv.cell_json_modal = yui_shell_show_modal(shell, $box, {
18706
- dialog: true,
18707
- logical_class: "TREEDB_CELL_JSON_SHEET",
18956
+ let presented = present_json_popup(gobj, $box, {
18957
+ name: "cellwin_" + (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj)),
18958
+ logical_class: "TREEDB_CELL_JSON",
18708
18959
  title_prefix: String(row_id),
18709
18960
  title: col_id,
18710
- t: i18next.t,
18961
+ icon: "yi-js-square",
18962
+ forget_window: function() {
18963
+ priv.cell_json_win = null;
18964
+ },
18711
18965
  on_close: function() {
18712
18966
  teardown_cell_json_child(gobj);
18713
18967
  }
18714
18968
  });
18969
+ if (!presented) {
18970
+ teardown_cell_json_child(gobj);
18971
+ return;
18972
+ }
18973
+ priv.cell_json_modal = presented.modal;
18974
+ priv.cell_json_win = presented.win;
18715
18975
  (0, _yuneta_gobj_js.gobj_send_event)(json_view, "EV_SET_JSON", { json: value }, gobj);
18716
18976
  }
18717
18977
  /************************************************************
@@ -18726,6 +18986,9 @@ function teardown_cell_json_child(gobj) {
18726
18986
  (0, _yuneta_gobj_js.gobj_destroy)(priv.cell_json_gobj);
18727
18987
  priv.cell_json_gobj = null;
18728
18988
  }
18989
+ let win = priv.cell_json_win;
18990
+ priv.cell_json_win = null;
18991
+ retire_popup_window(win);
18729
18992
  priv.cell_json_modal = null;
18730
18993
  }
18731
18994
  /************************************************************