@yuneta/gobj-ui 7.23.1 → 7.23.3

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,8 +439,26 @@ 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
+ Its camera speaks the **same vocabulary as the treedb graph** (`7.23.3`): zoom
452
+ in / zoom out, a live **zoom readout**, `yi-fit` for fit — the same corner
453
+ brackets the SVG sprite draws for `g6-icon-fit` — and the WRITTEN `1:1` for
454
+ actual size, which is written in every editor that offers it and never drawn.
455
+ The readout hangs off G6's `aftertransform`, the one hook that also covers the
456
+ wheel.
457
+
458
+ The graph carries **its own** toolbar, a **layout picker**
459
+ (`EV_CHANGE_LAYOUT {layout}`: `vertical tree`, `dagre top-down`,
460
+ `dagre left-right` — the layout and the edge type move together), and since
461
+ `7.22.0` the same two facilities the tree has: a **find box** (rate-limited, `EV_FIND_NODES`)
444
462
  that highlights matching rows and outlines the cards they are in and says how
445
463
  many matched, and **expand-all / collapse-all** (`EV_EXPAND_ALL` /
446
464
  `EV_COLLAPSE_ALL`) that fold every card but the root, marking each cut with
@@ -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,8 @@ var PRIVATE_DATA$11 = {
9504
9555
  match_count: 0,
9505
9556
  find_timer: null,
9506
9557
  fold_listener: null,
9558
+ $layout_select: null,
9559
+ $zoom_readout: null,
9507
9560
  $find_input: null,
9508
9561
  $find_result: null,
9509
9562
  $find_count: null
@@ -9678,6 +9731,7 @@ function build_ui$10(gobj) {
9678
9731
  };
9679
9732
  for (let type of FOLD_SWALLOWED_EVENTS) $mount.addEventListener(type, priv.fold_listener, true);
9680
9733
  } else (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no canvas mount, fold handles are dead`);
9734
+ priv.$layout_select = $container.querySelector(".JSON_GRAPH_LAYOUT");
9681
9735
  priv.$find_input = $container.querySelector(".JSON_GRAPH_FIND_INPUT");
9682
9736
  priv.$find_result = $container.querySelector(".JSON_GRAPH_FIND_RESULT");
9683
9737
  priv.$find_count = $container.querySelector(".JSON_GRAPH_FIND_COUNT");
@@ -9700,6 +9754,54 @@ function destroy_ui$6(gobj) {
9700
9754
  }
9701
9755
  }
9702
9756
  /************************************************************
9757
+ * One camera button.
9758
+ ************************************************************/
9759
+ function camera_button(gobj, icon, event_name, label_key, wide) {
9760
+ return [
9761
+ "button",
9762
+ {
9763
+ class: `button ${event_name}`,
9764
+ type: "button",
9765
+ style: {
9766
+ height: wide,
9767
+ width: "2.5em"
9768
+ },
9769
+ title: (0, i18next.t)(label_key),
9770
+ "data-i18n-title": label_key,
9771
+ "aria-label": (0, i18next.t)(label_key),
9772
+ "data-i18n-aria-label": label_key
9773
+ },
9774
+ ["i", {
9775
+ style: "font-size:1.5em; color:inherit;",
9776
+ class: icon
9777
+ }],
9778
+ { click: (evt) => {
9779
+ evt.stopPropagation();
9780
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, event_name, { evt }, gobj);
9781
+ } }
9782
+ ];
9783
+ }
9784
+ /************************************************************
9785
+ * The zoom, as a percentage. What the two magnifiers
9786
+ * change: an editor that offers a zoom shows the number.
9787
+ ************************************************************/
9788
+ function zoom_percent_text$1(gobj) {
9789
+ let graph = gobj.priv.graph;
9790
+ if (!graph) return "100%";
9791
+ try {
9792
+ return Math.round(graph.getZoom() * 100) + "%";
9793
+ } catch (e) {
9794
+ return "100%";
9795
+ }
9796
+ }
9797
+ /************************************************************
9798
+ * Repaint the readout after anything that moves the camera.
9799
+ ************************************************************/
9800
+ function update_zoom_readout$1(gobj) {
9801
+ let priv = gobj.priv;
9802
+ if (priv.$zoom_readout) priv.$zoom_readout.textContent = zoom_percent_text$1(gobj);
9803
+ }
9804
+ /************************************************************
9703
9805
  * One fold button. `open` rotates the chevron to the open
9704
9806
  * state, mirroring the per-node toggle of the lazy tree.
9705
9807
  ************************************************************/
@@ -9779,63 +9881,88 @@ function make_toolbar$3(gobj) {
9779
9881
  ]]
9780
9882
  ]];
9781
9883
  let center_items = [];
9782
- for (let item of [
9783
- [
9784
- "yi-magnifying-glass-plus",
9785
- "EV_ZOOM_IN",
9786
- false,
9787
- "i"
9788
- ],
9789
- [
9790
- "yi-magnifying-glass",
9791
- "EV_ZOOM_RESET",
9792
- false,
9793
- "i"
9794
- ],
9795
- [
9796
- "yi-magnifying-glass-minus",
9797
- "EV_ZOOM_OUT",
9798
- false,
9799
- "i"
9800
- ],
9801
- [
9802
- "yi-arrows-to-eye",
9803
- "EV_CENTER",
9804
- false,
9805
- "i"
9806
- ],
9807
- [
9808
- "yi-arrows-rotate",
9809
- "EV_REFRESH",
9810
- false,
9811
- "i"
9812
- ]
9813
- ]) {
9814
- let icon_name = item[0];
9815
- let event_name = item[1];
9816
- let disabled = item[2];
9817
- let button = {
9818
- class: `button ${event_name}`,
9884
+ for (let item of [[
9885
+ "yi-magnifying-glass-plus",
9886
+ "EV_ZOOM_IN",
9887
+ "zoom in"
9888
+ ], [
9889
+ "yi-magnifying-glass-minus",
9890
+ "EV_ZOOM_OUT",
9891
+ "zoom out"
9892
+ ]]) center_items.push(camera_button(gobj, item[0], item[1], item[2], toolbar_wide));
9893
+ priv.$zoom_readout = (0, _yuneta_gobj_js.createElement2)([
9894
+ "span",
9895
+ {
9896
+ class: "JSON_GRAPH_ZOOM_LEVEL is-flex is-align-items-center px-2 has-text-grey",
9897
+ style: "font-size:.85rem; min-width:3.5em; justify-content:center;",
9898
+ title: (0, i18next.t)("zoom level"),
9899
+ "data-i18n-title": "zoom level"
9900
+ },
9901
+ zoom_percent_text$1(gobj)
9902
+ ]);
9903
+ center_items.push(priv.$zoom_readout);
9904
+ center_items.push(camera_button(gobj, "yi-fit", "EV_CENTER", "auto fit", toolbar_wide));
9905
+ center_items.push([
9906
+ "button",
9907
+ {
9908
+ class: "button EV_ZOOM_RESET",
9909
+ type: "button",
9819
9910
  style: {
9820
9911
  height: toolbar_wide,
9821
9912
  width: "2.5em"
9822
- }
9823
- };
9824
- if (disabled) button.disabled = true;
9825
- center_items.push([
9826
- "button",
9827
- button,
9828
- ["i", {
9829
- style: "font-size:1.5em; color:inherit;",
9830
- class: icon_name
9831
- }],
9832
- { click: (evt) => {
9913
+ },
9914
+ title: (0, i18next.t)("actual size"),
9915
+ "data-i18n-title": "actual size",
9916
+ "aria-label": (0, i18next.t)("actual size"),
9917
+ "data-i18n-aria-label": "actual size"
9918
+ },
9919
+ [
9920
+ "span",
9921
+ { style: "font-weight:700;" },
9922
+ "1:1"
9923
+ ],
9924
+ { click: (evt) => {
9925
+ evt.stopPropagation();
9926
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_ZOOM_RESET", { evt }, gobj);
9927
+ } }
9928
+ ]);
9929
+ center_items.push(camera_button(gobj, "yi-arrows-rotate", "EV_REFRESH", "refresh", toolbar_wide));
9930
+ let current_layout = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout") || DEFAULT_LAYOUT;
9931
+ let options = Object.keys(LAYOUTS$1).map(function(key) {
9932
+ let attrs = {
9933
+ value: key,
9934
+ "data-i18n": LAYOUTS$1[key].label
9935
+ };
9936
+ if (key === current_layout) attrs.selected = "selected";
9937
+ return [
9938
+ "option",
9939
+ attrs,
9940
+ layout_label(key)
9941
+ ];
9942
+ });
9943
+ let right_items = [
9944
+ (0, _yuneta_gobj_js.createElement2)([
9945
+ "select",
9946
+ {
9947
+ class: "JSON_GRAPH_LAYOUT select",
9948
+ style: {
9949
+ height: toolbar_wide,
9950
+ "margin-left": "0.5em"
9951
+ },
9952
+ title: (0, i18next.t)("layout"),
9953
+ "data-i18n-title": "layout",
9954
+ "aria-label": (0, i18next.t)("layout"),
9955
+ "data-i18n-aria-label": "layout"
9956
+ },
9957
+ options,
9958
+ { change: (evt) => {
9833
9959
  evt.stopPropagation();
9834
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, event_name, { evt }, gobj);
9960
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CHANGE_LAYOUT", { layout: evt.target.value }, gobj);
9835
9961
  } }
9836
- ]);
9837
- }
9838
- let right_items = [graph_fold_button(gobj, "EV_EXPAND_ALL", "expand all", true), graph_fold_button(gobj, "EV_COLLAPSE_ALL", "collapse all", false)];
9962
+ ]),
9963
+ graph_fold_button(gobj, "EV_EXPAND_ALL", "expand all", true),
9964
+ graph_fold_button(gobj, "EV_COLLAPSE_ALL", "collapse all", false)
9965
+ ];
9839
9966
  const $toolbar = yui_toolbar({}, [
9840
9967
  [
9841
9968
  "div",
@@ -9861,6 +9988,7 @@ function make_toolbar$3(gobj) {
9861
9988
  ************************************************************/
9862
9989
  function build_graph$3(gobj) {
9863
9990
  let priv = gobj.priv;
9991
+ let layout_cfg = get_layout_cfg$1((0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout"));
9864
9992
  const graph = priv.graph = new _antv_g6.Graph({
9865
9993
  container: priv.canvas_id,
9866
9994
  animation: false,
@@ -9868,20 +9996,23 @@ function build_graph$3(gobj) {
9868
9996
  zoomRange: [.1, 4],
9869
9997
  node: { style: { labelPlacement: "center" } },
9870
9998
  edge: {
9871
- type: "cubic-vertical",
9999
+ type: layout_cfg.edge_type,
9872
10000
  style: {
9873
10001
  stroke: "#999",
9874
10002
  lineWidth: 1,
9875
10003
  endArrow: true
9876
10004
  }
9877
10005
  },
9878
- layout: { type: "json-tree-layout" },
10006
+ layout: layout_cfg.g6_layout,
9879
10007
  behaviors: ["drag-canvas", "zoom-canvas"]
9880
10008
  });
9881
10009
  if (priv.theme) graph.setTheme(priv.theme);
9882
10010
  graph.on(_antv_g6.NodeEvent.CLICK, (evt) => {
9883
10011
  (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_NODE_CLICK", { evt }, gobj);
9884
10012
  });
10013
+ graph.on("aftertransform", () => {
10014
+ update_zoom_readout$1(gobj);
10015
+ });
9885
10016
  graph.on(_antv_g6.CanvasEvent.CLICK, (evt) => {});
9886
10017
  if (typeof ResizeObserver !== "undefined") {
9887
10018
  let $canvas = document.getElementById(priv.canvas_id);
@@ -10141,6 +10272,7 @@ function load_json(gobj) {
10141
10272
  });
10142
10273
  graph.render().then(() => {
10143
10274
  if (!preserve_view) graph.fitView();
10275
+ update_zoom_readout$1(gobj);
10144
10276
  });
10145
10277
  }
10146
10278
  }
@@ -10302,6 +10434,34 @@ function ac_refresh$5(gobj, event, kw, src) {
10302
10434
  }
10303
10435
  /************************************************************
10304
10436
  * {theme: "dark"|"light"} — the app switched theme.
10437
+ * EV_CHANGE_LAYOUT { layout }
10438
+ *
10439
+ * The layout AND the edge type move together — a vertical
10440
+ * layout with horizontal cubics draws every edge the wrong way
10441
+ * round — and then the graph is rebuilt and refitted, because a
10442
+ * new layout is a new shape and the old camera framed the old
10443
+ * one.
10444
+ ************************************************************/
10445
+ function ac_change_layout$1(gobj, event, kw, src) {
10446
+ let priv = gobj.priv;
10447
+ let key = kw && kw.layout || "";
10448
+ if (!LAYOUTS$1[key]) {
10449
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: unknown layout '${key}'`);
10450
+ return -1;
10451
+ }
10452
+ if (key === (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout")) return 0;
10453
+ (0, _yuneta_gobj_js.gobj_write_str_attr)(gobj, "layout", key);
10454
+ if (priv.$layout_select && priv.$layout_select.value !== key) priv.$layout_select.value = key;
10455
+ let cfg = get_layout_cfg$1(key);
10456
+ if (priv.graph) priv.graph.setOptions({
10457
+ layout: cfg.g6_layout,
10458
+ edge: { type: cfg.edge_type }
10459
+ });
10460
+ refresh_json(gobj);
10461
+ update_find_result(gobj);
10462
+ return 0;
10463
+ }
10464
+ /************************************************************
10305
10465
  * EV_TOGGLE_FOLD { path } — fold or unfold ONE card.
10306
10466
  *
10307
10467
  * The camera does not move: folding one branch is a local
@@ -10516,6 +10676,11 @@ function create_gclass$11(gclass_name) {
10516
10676
  ac_toggle_fold,
10517
10677
  null
10518
10678
  ],
10679
+ [
10680
+ "EV_CHANGE_LAYOUT",
10681
+ ac_change_layout$1,
10682
+ null
10683
+ ],
10519
10684
  [
10520
10685
  "EV_EXPAND_ALL",
10521
10686
  ac_expand_all$2,
@@ -10577,6 +10742,7 @@ function create_gclass$11(gclass_name) {
10577
10742
  ["EV_REFRESH", 0],
10578
10743
  ["EV_FIND_NODES", 0],
10579
10744
  ["EV_TOGGLE_FOLD", 0],
10745
+ ["EV_CHANGE_LAYOUT", 0],
10580
10746
  ["EV_EXPAND_ALL", 0],
10581
10747
  ["EV_COLLAPSE_ALL", 0],
10582
10748
  ["EV_THEME", 0],
@@ -10910,12 +11076,13 @@ function mt_start$10(gobj) {
10910
11076
  /***************************************************************
10911
11077
  * Framework Method: Stop
10912
11078
  ***************************************************************/
10913
- function mt_stop$10(gobj) {}
11079
+ function mt_stop$10(gobj) {
11080
+ teardown_graph_child(gobj);
11081
+ }
10914
11082
  /***************************************************************
10915
11083
  * Framework Method: Destroy
10916
11084
  ***************************************************************/
10917
11085
  function mt_destroy$10(gobj) {
10918
- teardown_graph_child(gobj);
10919
11086
  destroy_ui$5(gobj);
10920
11087
  }
10921
11088
  /***************************
@@ -16215,7 +16382,7 @@ function treedb_delete_node$1(gobj, treedb_name, topic_name, record, options) {
16215
16382
  /************************************************************
16216
16383
  * True on a phone-width viewport (Bulma's mobile breakpoint).
16217
16384
  ************************************************************/
16218
- function is_mobile$1() {
16385
+ function is_mobile$2() {
16219
16386
  return typeof window !== "undefined" && window.innerWidth <= 768;
16220
16387
  }
16221
16388
  /************************************************************
@@ -16234,7 +16401,7 @@ function open_json_viewer$1(gobj) {
16234
16401
  yui_shell_show_error(yui_shell_of(gobj), "raw json viewer unavailable", { t: i18next.t });
16235
16402
  return;
16236
16403
  }
16237
- let mobile = is_mobile$1();
16404
+ let mobile = is_mobile$2();
16238
16405
  let shell = yui_shell_of(gobj);
16239
16406
  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);
16240
16407
  if (!jv) {
@@ -17447,10 +17614,102 @@ var PRIVATE_DATA$5 = {
17447
17614
  form_modal: null,
17448
17615
  schema_gobj: null,
17449
17616
  schema_modal: null,
17617
+ schema_win: null,
17450
17618
  cell_json_gobj: null,
17451
- cell_json_modal: null
17619
+ cell_json_modal: null,
17620
+ cell_json_win: null
17452
17621
  };
17453
17622
  var __gclass__$5 = null;
17623
+ function is_mobile$1() {
17624
+ return typeof window !== "undefined" && window.innerWidth <= 768;
17625
+ }
17626
+ /************************************************************
17627
+ * Present a JSON viewer's $container.
17628
+ *
17629
+ * On a LAPTOP a floating C_YUI_WINDOW: a JSON document is
17630
+ * something you read WHILE looking at the table behind it —
17631
+ * you move it, you resize it, you maximise it when the
17632
+ * document is big. A modal can do none of that, and it
17633
+ * darkens exactly the rows you opened the cell from.
17634
+ *
17635
+ * On a phone the modal sheet stays: there is no room to
17636
+ * arrange anything, and the sheet already carries the shell's
17637
+ * Escape / Back handling.
17638
+ *
17639
+ * Returns {win, modal} — exactly one of them is set.
17640
+ ************************************************************/
17641
+ function present_json_popup(gobj, $box, opts) {
17642
+ let shell = yui_shell_of(gobj);
17643
+ if (!shell) {
17644
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no shell, cannot present the json`);
17645
+ return null;
17646
+ }
17647
+ if (is_mobile$1()) return {
17648
+ modal: yui_shell_show_modal(shell, $box, {
17649
+ dialog: true,
17650
+ logical_class: opts.logical_class + "_SHEET",
17651
+ title_prefix: opts.title_prefix,
17652
+ title: opts.title,
17653
+ t: i18next.t,
17654
+ on_close: opts.on_close
17655
+ }),
17656
+ win: null
17657
+ };
17658
+ let win = (0, _yuneta_gobj_js.gobj_create_service)(opts.name, "C_YUI_WINDOW", {
17659
+ $parent: popup_mount_layer(gobj),
17660
+ subscriber: null,
17661
+ modal: false,
17662
+ showMax: true,
17663
+ showFooter: false,
17664
+ resizable: true,
17665
+ center: true,
17666
+ auto_save_size_and_position: true,
17667
+ width: 720,
17668
+ height: 620,
17669
+ logical_class: opts.logical_class + "_WINDOW",
17670
+ title_prefix: opts.title_prefix,
17671
+ title: opts.title,
17672
+ icon: opts.icon,
17673
+ body: $box,
17674
+ manager: null,
17675
+ on_close: function() {
17676
+ if ((0, _yuneta_gobj_js.gobj_is_destroying)(gobj)) return;
17677
+ opts.forget_window();
17678
+ opts.on_close();
17679
+ }
17680
+ }, gobj);
17681
+ if (!win) {
17682
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: cannot create the json window`);
17683
+ return null;
17684
+ }
17685
+ (0, _yuneta_gobj_js.gobj_start)(win);
17686
+ return {
17687
+ win,
17688
+ modal: null
17689
+ };
17690
+ }
17691
+ /************************************************************
17692
+ * Retire a presenting window: reference cleared FIRST, then
17693
+ * stop, then destroy.
17694
+ *
17695
+ * The order is the whole content of this function. The ✕ of
17696
+ * the window calls back into the teardown that calls this, so
17697
+ * clearing the reference first is what stops the recursion.
17698
+ * And it is STOP then destroy because the window was started
17699
+ * on open: handing a running gobj to gobj_destroy() makes the
17700
+ * framework rescue it and say so — "Destroying a RUNNING
17701
+ * gobj" — which is a caller's bug, not a framework message.
17702
+ * Same lesson `c_tranger_view.close_json_viewer` carries.
17703
+ ************************************************************/
17704
+ function retire_popup_window(win) {
17705
+ if (!win) return;
17706
+ try {
17707
+ if ((0, _yuneta_gobj_js.gobj_is_running)(win)) (0, _yuneta_gobj_js.gobj_stop)(win);
17708
+ (0, _yuneta_gobj_js.gobj_destroy)(win);
17709
+ } catch (e) {
17710
+ (0, _yuneta_gobj_js.log_warning)(`${GCLASS_NAME$5}: json window already gone: ${e}`);
17711
+ }
17712
+ }
17454
17713
  function popup_mount_layer(gobj) {
17455
17714
  let $layer = yui_shell_popup_layer(yui_shell_of(gobj));
17456
17715
  if ($layer) return $layer;
@@ -18621,8 +18880,7 @@ function open_schema_dialog(gobj) {
18621
18880
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no desc, no schema to show`);
18622
18881
  return;
18623
18882
  }
18624
- let shell = yui_shell_of(gobj);
18625
- if (!shell) {
18883
+ if (!yui_shell_of(gobj)) {
18626
18884
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no shell, cannot open the schema view`);
18627
18885
  return;
18628
18886
  }
@@ -18639,16 +18897,25 @@ function open_schema_dialog(gobj) {
18639
18897
  teardown_schema_child(gobj);
18640
18898
  return;
18641
18899
  }
18642
- priv.schema_modal = yui_shell_show_modal(shell, $box, {
18643
- dialog: true,
18644
- logical_class: "TREEDB_SCHEMA_SHEET",
18900
+ let presented = present_json_popup(gobj, $box, {
18901
+ name: "schemawin_" + (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj)),
18902
+ logical_class: "TREEDB_SCHEMA",
18645
18903
  title_prefix: (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name"),
18646
18904
  title: "schema",
18647
- t: i18next.t,
18905
+ icon: "yi-table",
18906
+ forget_window: function() {
18907
+ priv.schema_win = null;
18908
+ },
18648
18909
  on_close: function() {
18649
18910
  teardown_schema_child(gobj);
18650
18911
  }
18651
18912
  });
18913
+ if (!presented) {
18914
+ teardown_schema_child(gobj);
18915
+ return;
18916
+ }
18917
+ priv.schema_modal = presented.modal;
18918
+ priv.schema_win = presented.win;
18652
18919
  (0, _yuneta_gobj_js.gobj_send_event)(json_view, "EV_SET_JSON", { json: desc }, gobj);
18653
18920
  }
18654
18921
  /************************************************************
@@ -18663,6 +18930,9 @@ function teardown_schema_child(gobj) {
18663
18930
  (0, _yuneta_gobj_js.gobj_destroy)(priv.schema_gobj);
18664
18931
  priv.schema_gobj = null;
18665
18932
  }
18933
+ let win = priv.schema_win;
18934
+ priv.schema_win = null;
18935
+ retire_popup_window(win);
18666
18936
  priv.schema_modal = null;
18667
18937
  }
18668
18938
  /************************************************************
@@ -18709,8 +18979,7 @@ function open_cell_json_dialog(gobj, row_id, col_id) {
18709
18979
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: row '${row_id}' has no col '${col_id}'`);
18710
18980
  return;
18711
18981
  }
18712
- let shell = yui_shell_of(gobj);
18713
- if (!shell) {
18982
+ if (!yui_shell_of(gobj)) {
18714
18983
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no shell, cannot open the cell json`);
18715
18984
  return;
18716
18985
  }
@@ -18727,16 +18996,25 @@ function open_cell_json_dialog(gobj, row_id, col_id) {
18727
18996
  teardown_cell_json_child(gobj);
18728
18997
  return;
18729
18998
  }
18730
- priv.cell_json_modal = yui_shell_show_modal(shell, $box, {
18731
- dialog: true,
18732
- logical_class: "TREEDB_CELL_JSON_SHEET",
18999
+ let presented = present_json_popup(gobj, $box, {
19000
+ name: "cellwin_" + (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj)),
19001
+ logical_class: "TREEDB_CELL_JSON",
18733
19002
  title_prefix: String(row_id),
18734
19003
  title: col_id,
18735
- t: i18next.t,
19004
+ icon: "yi-js-square",
19005
+ forget_window: function() {
19006
+ priv.cell_json_win = null;
19007
+ },
18736
19008
  on_close: function() {
18737
19009
  teardown_cell_json_child(gobj);
18738
19010
  }
18739
19011
  });
19012
+ if (!presented) {
19013
+ teardown_cell_json_child(gobj);
19014
+ return;
19015
+ }
19016
+ priv.cell_json_modal = presented.modal;
19017
+ priv.cell_json_win = presented.win;
18740
19018
  (0, _yuneta_gobj_js.gobj_send_event)(json_view, "EV_SET_JSON", { json: value }, gobj);
18741
19019
  }
18742
19020
  /************************************************************
@@ -18751,6 +19029,9 @@ function teardown_cell_json_child(gobj) {
18751
19029
  (0, _yuneta_gobj_js.gobj_destroy)(priv.cell_json_gobj);
18752
19030
  priv.cell_json_gobj = null;
18753
19031
  }
19032
+ let win = priv.cell_json_win;
19033
+ priv.cell_json_win = null;
19034
+ retire_popup_window(win);
18754
19035
  priv.cell_json_modal = null;
18755
19036
  }
18756
19037
  /************************************************************