@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 +23 -4
- package/dist/gobj-ui.cjs.js +297 -34
- package/dist/gobj-ui.es.js +297 -34
- package/package.json +1 -1
- package/src/c_yui_json.js +15 -1
- package/src/c_yui_json_graph.js +183 -17
- package/src/c_yui_treedb_topic_with_form.js +152 -10
package/src/c_yui_json_graph.js
CHANGED
|
@@ -74,6 +74,73 @@ const GCLASS_NAME = "C_YUI_JSON_GRAPH";
|
|
|
74
74
|
*/
|
|
75
75
|
const FOLD_SWALLOWED_EVENTS = ["pointerdown", "pointerup", "mousedown", "click"];
|
|
76
76
|
|
|
77
|
+
/*
|
|
78
|
+
* Layouts, same shape the gobj tree's registry uses
|
|
79
|
+
* (`c_yui_gobj_tree_js.js`): a G6 layout config plus the edge type
|
|
80
|
+
* that matches its direction — a vertical layout with horizontal
|
|
81
|
+
* cubics draws every edge going the wrong way round.
|
|
82
|
+
*
|
|
83
|
+
* `json-tree` is this gclass's own layout, the one it always had, and
|
|
84
|
+
* it stays the default: it sizes each level from the real card widths,
|
|
85
|
+
* which a generic layout cannot do with `html` nodes. The dagre pair
|
|
86
|
+
* is there for the documents it does badly — a wide, shallow object
|
|
87
|
+
* reads better left→right, which is also the house direction for a
|
|
88
|
+
* topology.
|
|
89
|
+
*/
|
|
90
|
+
const LAYOUTS = {
|
|
91
|
+
"json-tree": {
|
|
92
|
+
label: "vertical tree",
|
|
93
|
+
g6_layout: {type: 'json-tree-layout'},
|
|
94
|
+
edge_type: 'cubic-vertical',
|
|
95
|
+
},
|
|
96
|
+
"dagre-tb": {
|
|
97
|
+
label: "dagre top-down",
|
|
98
|
+
g6_layout: {type: 'antv-dagre', rankdir: 'TB', nodesep: 20, ranksep: 40},
|
|
99
|
+
edge_type: 'polyline',
|
|
100
|
+
},
|
|
101
|
+
"dagre-lr": {
|
|
102
|
+
label: "dagre left-right",
|
|
103
|
+
g6_layout: {type: 'antv-dagre', rankdir: 'LR', nodesep: 20, ranksep: 40},
|
|
104
|
+
edge_type: 'polyline',
|
|
105
|
+
},
|
|
106
|
+
/* `compact-box` is NOT here on purpose: it lays out from a fixed
|
|
107
|
+
* node size, and an `html` card is nothing like it — every card
|
|
108
|
+
* landed on top of the next. A layout that draws the document
|
|
109
|
+
* unreadable is worse than one option fewer. dagre reads the real
|
|
110
|
+
* sizes, which is why both of its directions are here. */
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const DEFAULT_LAYOUT = "json-tree";
|
|
114
|
+
|
|
115
|
+
function get_layout_cfg(key)
|
|
116
|
+
{
|
|
117
|
+
return LAYOUTS[key] || LAYOUTS[DEFAULT_LAYOUT];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/************************************************************
|
|
121
|
+
* A layout's label, with every key SPELLED OUT inside t().
|
|
122
|
+
*
|
|
123
|
+
* Never t(LAYOUTS[key].label) nor a `data-i18n` fed from that
|
|
124
|
+
* table: the apps' validate-locales scans for t("literal"),
|
|
125
|
+
* so a key reached through a variable is a key it cannot
|
|
126
|
+
* demand — and i18next answers an undefined key with the key
|
|
127
|
+
* itself, which renders in lower-case English and never
|
|
128
|
+
* changes language. Twice already in this component's
|
|
129
|
+
* history (7.20.0 `tree view`, and this table on its first
|
|
130
|
+
* draft).
|
|
131
|
+
************************************************************/
|
|
132
|
+
function layout_label(key)
|
|
133
|
+
{
|
|
134
|
+
switch(key) {
|
|
135
|
+
case "dagre-tb":
|
|
136
|
+
return t("dagre top-down");
|
|
137
|
+
case "dagre-lr":
|
|
138
|
+
return t("dagre left-right");
|
|
139
|
+
default:
|
|
140
|
+
return t("vertical tree");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
77
144
|
/***************************************************************
|
|
78
145
|
* Data
|
|
79
146
|
***************************************************************/
|
|
@@ -91,6 +158,7 @@ SDATA(data_type_t.DTP_STRING, "canvas_id", 0, "", "Canvas ID"),
|
|
|
91
158
|
|
|
92
159
|
/*---------------- Graph Settings ----------------*/
|
|
93
160
|
SDATA(data_type_t.DTP_STRING, "wide", 0, "36px", "Height of header"),
|
|
161
|
+
SDATA(data_type_t.DTP_STRING, "layout", 0, "json-tree", "Layout key (see LAYOUTS)"),
|
|
94
162
|
|
|
95
163
|
SDATA_END()
|
|
96
164
|
];
|
|
@@ -110,6 +178,7 @@ let PRIVATE_DATA = {
|
|
|
110
178
|
match_count: 0, // matches of the last find, for the count chip
|
|
111
179
|
find_timer: null, // rate-limits the find box
|
|
112
180
|
fold_listener: null, // delegated click on the card fold handles
|
|
181
|
+
$layout_select: null, // the layout picker
|
|
113
182
|
$find_input: null,
|
|
114
183
|
$find_result: null,
|
|
115
184
|
$find_count: null,
|
|
@@ -351,6 +420,7 @@ function build_ui(gobj)
|
|
|
351
420
|
log_error(`${gobj_short_name(gobj)}: no canvas mount, fold handles are dead`);
|
|
352
421
|
}
|
|
353
422
|
|
|
423
|
+
priv.$layout_select = $container.querySelector('.JSON_GRAPH_LAYOUT');
|
|
354
424
|
priv.$find_input = $container.querySelector('.JSON_GRAPH_FIND_INPUT');
|
|
355
425
|
priv.$find_result = $container.querySelector('.JSON_GRAPH_FIND_RESULT');
|
|
356
426
|
priv.$find_count = $container.querySelector('.JSON_GRAPH_FIND_COUNT');
|
|
@@ -516,7 +586,38 @@ function make_toolbar(gobj)
|
|
|
516
586
|
* went; "expand all" puts it back. Same chevron as the lazy tree,
|
|
517
587
|
* rotated to point down for the open state.
|
|
518
588
|
*/
|
|
589
|
+
/*
|
|
590
|
+
* A layout picker, same shape as the gobj tree's. One layout
|
|
591
|
+
* cannot serve every document: the built-in one sizes each level
|
|
592
|
+
* from the real card widths, dagre packs a wide shallow object
|
|
593
|
+
* far better, and left→right is the house direction for reading a
|
|
594
|
+
* topology.
|
|
595
|
+
*/
|
|
596
|
+
let current_layout = gobj_read_str_attr(gobj, "layout") || DEFAULT_LAYOUT;
|
|
597
|
+
let options = Object.keys(LAYOUTS).map(function(key) {
|
|
598
|
+
let attrs = {value: key, 'data-i18n': LAYOUTS[key].label};
|
|
599
|
+
if(key === current_layout) {
|
|
600
|
+
attrs.selected = "selected";
|
|
601
|
+
}
|
|
602
|
+
return ['option', attrs, layout_label(key)];
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
let $layout_select = createElement2(
|
|
606
|
+
['select', {
|
|
607
|
+
class: 'JSON_GRAPH_LAYOUT select',
|
|
608
|
+
style: {height: toolbar_wide, "margin-left": "0.5em"},
|
|
609
|
+
title: t("layout"), 'data-i18n-title': "layout",
|
|
610
|
+
'aria-label': t("layout"), 'data-i18n-aria-label': "layout"
|
|
611
|
+
}, options, {
|
|
612
|
+
change: (evt) => {
|
|
613
|
+
evt.stopPropagation();
|
|
614
|
+
gobj_send_event(gobj, "EV_CHANGE_LAYOUT", {layout: evt.target.value}, gobj);
|
|
615
|
+
}
|
|
616
|
+
}]
|
|
617
|
+
);
|
|
618
|
+
|
|
519
619
|
let right_items = [
|
|
620
|
+
$layout_select,
|
|
520
621
|
graph_fold_button(gobj, "EV_EXPAND_ALL", "expand all", true),
|
|
521
622
|
graph_fold_button(gobj, "EV_COLLAPSE_ALL", "collapse all", false),
|
|
522
623
|
];
|
|
@@ -537,6 +638,7 @@ function make_toolbar(gobj)
|
|
|
537
638
|
function build_graph(gobj)
|
|
538
639
|
{
|
|
539
640
|
let priv = gobj.priv;
|
|
641
|
+
let layout_cfg = get_layout_cfg(gobj_read_str_attr(gobj, "layout"));
|
|
540
642
|
|
|
541
643
|
const graph = priv.graph = new Graph({
|
|
542
644
|
container: priv.canvas_id,
|
|
@@ -551,7 +653,7 @@ function build_graph(gobj)
|
|
|
551
653
|
},
|
|
552
654
|
|
|
553
655
|
edge: {
|
|
554
|
-
type:
|
|
656
|
+
type: layout_cfg.edge_type,
|
|
555
657
|
style: {
|
|
556
658
|
stroke: '#999',
|
|
557
659
|
lineWidth: 1,
|
|
@@ -559,9 +661,7 @@ function build_graph(gobj)
|
|
|
559
661
|
},
|
|
560
662
|
},
|
|
561
663
|
|
|
562
|
-
layout:
|
|
563
|
-
type: 'json-tree-layout',
|
|
564
|
-
},
|
|
664
|
+
layout: layout_cfg.g6_layout,
|
|
565
665
|
|
|
566
666
|
behaviors: [
|
|
567
667
|
'drag-canvas',
|
|
@@ -668,24 +768,47 @@ function build_cell_html(key, value, type, dark, matched)
|
|
|
668
768
|
* innerHTML string, so there is no other way to hang a
|
|
669
769
|
* handler on it.
|
|
670
770
|
*
|
|
671
|
-
*
|
|
672
|
-
*
|
|
673
|
-
*
|
|
771
|
+
* A filled CHIP, the same one the gobj tree draws
|
|
772
|
+
* (`c_yui_gobj_tree_js.js`, render_toggle_html): background,
|
|
773
|
+
* border, radius, and `+N` / `−` rather than a triangle. It
|
|
774
|
+
* shipped once as a bare `▾` and on a phone it was invisible —
|
|
775
|
+
* a glyph is a couple of pixels of ink at the zoom that fits a
|
|
776
|
+
* document on screen, while a filled chip stays a visible blob
|
|
777
|
+
* and reads as something you press. `+N` also carries the
|
|
778
|
+
* count of what is hidden, which the open state does not need.
|
|
779
|
+
*
|
|
780
|
+
* No `title`: it would be a word inside an innerHTML string,
|
|
781
|
+
* where it could carry no i18n key and would sit in English
|
|
782
|
+
* forever. `+3` and `−` need no language.
|
|
674
783
|
************************************************************/
|
|
675
|
-
function fold_toggle_html(path, foldable, folded, branches)
|
|
784
|
+
function fold_toggle_html(path, foldable, folded, branches, colors)
|
|
676
785
|
{
|
|
677
786
|
if(!foldable) {
|
|
678
|
-
/* A spacer, so the label of a leaf card
|
|
679
|
-
* same axis as the label of a foldable one. */
|
|
680
|
-
return `<span style="width:
|
|
787
|
+
/* A spacer the width of the chip, so the label of a leaf card
|
|
788
|
+
* stays on the same axis as the label of a foldable one. */
|
|
789
|
+
return `<span style="width:24px; flex:0 0 auto;"></span>`;
|
|
681
790
|
}
|
|
682
791
|
|
|
683
|
-
let
|
|
684
|
-
let text = folded? `${glyph} ${branches}`: glyph;
|
|
792
|
+
let label = folded? ("+" + branches): "−"; /* +N / − */
|
|
685
793
|
|
|
686
|
-
return `<span data-fold-path="${escapeHtml(path)}"
|
|
687
|
-
|
|
688
|
-
|
|
794
|
+
return `<span data-fold-path="${escapeHtml(path)}" style="
|
|
795
|
+
flex: 0 0 auto;
|
|
796
|
+
display: inline-flex;
|
|
797
|
+
align-items: center;
|
|
798
|
+
justify-content: center;
|
|
799
|
+
min-width: 22px;
|
|
800
|
+
height: 16px;
|
|
801
|
+
padding: 0 5px;
|
|
802
|
+
background: ${colors.bg};
|
|
803
|
+
color: ${colors.border};
|
|
804
|
+
border: 1px solid ${colors.bg};
|
|
805
|
+
border-radius: 6px;
|
|
806
|
+
font-size: 11px;
|
|
807
|
+
font-weight: 700;
|
|
808
|
+
line-height: 1;
|
|
809
|
+
cursor: pointer;
|
|
810
|
+
user-select: none;
|
|
811
|
+
">${label}</span>`;
|
|
689
812
|
}
|
|
690
813
|
|
|
691
814
|
/************************************************************
|
|
@@ -854,7 +977,7 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id)
|
|
|
854
977
|
display: flex;
|
|
855
978
|
align-items: center;
|
|
856
979
|
gap: 6px;
|
|
857
|
-
">${fold_toggle_html(path, foldable, folded, pending_complex.length)}<span style="flex:1 1 auto; text-align:center;">${escapeHtml(label)}</span></div>
|
|
980
|
+
">${fold_toggle_html(path, foldable, folded, pending_complex.length, colors)}<span style="flex:1 1 auto; text-align:center;">${escapeHtml(label)}</span></div>
|
|
858
981
|
${content_html}
|
|
859
982
|
</div>`;
|
|
860
983
|
|
|
@@ -1184,6 +1307,47 @@ function ac_refresh(gobj, event, kw, src)
|
|
|
1184
1307
|
|
|
1185
1308
|
/************************************************************
|
|
1186
1309
|
* {theme: "dark"|"light"} — the app switched theme.
|
|
1310
|
+
* EV_CHANGE_LAYOUT { layout }
|
|
1311
|
+
*
|
|
1312
|
+
* The layout AND the edge type move together — a vertical
|
|
1313
|
+
* layout with horizontal cubics draws every edge the wrong way
|
|
1314
|
+
* round — and then the graph is rebuilt and refitted, because a
|
|
1315
|
+
* new layout is a new shape and the old camera framed the old
|
|
1316
|
+
* one.
|
|
1317
|
+
************************************************************/
|
|
1318
|
+
function ac_change_layout(gobj, event, kw, src)
|
|
1319
|
+
{
|
|
1320
|
+
let priv = gobj.priv;
|
|
1321
|
+
let key = (kw && kw.layout) || "";
|
|
1322
|
+
|
|
1323
|
+
if(!LAYOUTS[key]) {
|
|
1324
|
+
log_error(`${gobj_short_name(gobj)}: unknown layout '${key}'`);
|
|
1325
|
+
return -1;
|
|
1326
|
+
}
|
|
1327
|
+
if(key === gobj_read_str_attr(gobj, "layout")) {
|
|
1328
|
+
return 0;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
gobj_write_str_attr(gobj, "layout", key);
|
|
1332
|
+
|
|
1333
|
+
/* Keep the picker in step when the event came from elsewhere. */
|
|
1334
|
+
if(priv.$layout_select && priv.$layout_select.value !== key) {
|
|
1335
|
+
priv.$layout_select.value = key;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
let cfg = get_layout_cfg(key);
|
|
1339
|
+
if(priv.graph) {
|
|
1340
|
+
priv.graph.setOptions({
|
|
1341
|
+
layout: cfg.g6_layout,
|
|
1342
|
+
edge: {type: cfg.edge_type},
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
refresh_json(gobj);
|
|
1346
|
+
update_find_result(gobj);
|
|
1347
|
+
return 0;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/************************************************************
|
|
1187
1351
|
* EV_TOGGLE_FOLD { path } — fold or unfold ONE card.
|
|
1188
1352
|
*
|
|
1189
1353
|
* The camera does not move: folding one branch is a local
|
|
@@ -1466,6 +1630,7 @@ function create_gclass(gclass_name)
|
|
|
1466
1630
|
["EV_REFRESH", ac_refresh, null],
|
|
1467
1631
|
["EV_FIND_NODES", ac_find_nodes, null],
|
|
1468
1632
|
["EV_TOGGLE_FOLD", ac_toggle_fold, null],
|
|
1633
|
+
["EV_CHANGE_LAYOUT", ac_change_layout, null],
|
|
1469
1634
|
["EV_EXPAND_ALL", ac_expand_all, null],
|
|
1470
1635
|
["EV_COLLAPSE_ALL", ac_collapse_all, null],
|
|
1471
1636
|
["EV_THEME", ac_theme, null],
|
|
@@ -1488,6 +1653,7 @@ function create_gclass(gclass_name)
|
|
|
1488
1653
|
["EV_REFRESH", 0],
|
|
1489
1654
|
["EV_FIND_NODES", 0],
|
|
1490
1655
|
["EV_TOGGLE_FOLD", 0],
|
|
1656
|
+
["EV_CHANGE_LAYOUT", 0],
|
|
1491
1657
|
["EV_EXPAND_ALL", 0],
|
|
1492
1658
|
["EV_COLLAPSE_ALL", 0],
|
|
1493
1659
|
["EV_THEME", 0],
|
|
@@ -42,6 +42,8 @@ import {
|
|
|
42
42
|
kwid_get_ids,
|
|
43
43
|
gobj_create_pure_child,
|
|
44
44
|
gobj_start,
|
|
45
|
+
gobj_create_service,
|
|
46
|
+
gobj_is_destroying,
|
|
45
47
|
gobj_stop,
|
|
46
48
|
gobj_destroy,
|
|
47
49
|
gobj_is_running,
|
|
@@ -170,9 +172,11 @@ let PRIVATE_DATA = {
|
|
|
170
172
|
form: null, // hosted C_YUI_FORM child (while dialog open)
|
|
171
173
|
form_modal: null, // { close } handle of the adaptive dialog
|
|
172
174
|
schema_gobj: null, // hosted C_YUI_JSON child (while schema open)
|
|
173
|
-
schema_modal: null, // { close } handle of the schema dialog
|
|
175
|
+
schema_modal: null, // { close } handle of the schema dialog (phone)
|
|
176
|
+
schema_win: null, // C_YUI_WINDOW presenting it (laptop)
|
|
174
177
|
cell_json_gobj: null, // hosted C_YUI_JSON child (while a cell is open)
|
|
175
|
-
cell_json_modal: null, // { close } handle of the cell dialog
|
|
178
|
+
cell_json_modal: null, // { close } handle of the cell dialog (phone)
|
|
179
|
+
cell_json_win: null, // C_YUI_WINDOW presenting it (laptop)
|
|
176
180
|
};
|
|
177
181
|
|
|
178
182
|
let __gclass__ = null;
|
|
@@ -184,6 +188,120 @@ let __gclass__ = null;
|
|
|
184
188
|
* stacking context below body-level modals). The legacy
|
|
185
189
|
* C_YUI_MAIN "#popup-layer" and document.body (Bulma `.modal` is
|
|
186
190
|
* position:fixed) remain the shell-less fallbacks. */
|
|
191
|
+
/* True on a phone-width viewport (Bulma's mobile breakpoint). */
|
|
192
|
+
function is_mobile()
|
|
193
|
+
{
|
|
194
|
+
return typeof window !== "undefined" && window.innerWidth <= 768;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/************************************************************
|
|
198
|
+
* Present a JSON viewer's $container.
|
|
199
|
+
*
|
|
200
|
+
* On a LAPTOP a floating C_YUI_WINDOW: a JSON document is
|
|
201
|
+
* something you read WHILE looking at the table behind it —
|
|
202
|
+
* you move it, you resize it, you maximise it when the
|
|
203
|
+
* document is big. A modal can do none of that, and it
|
|
204
|
+
* darkens exactly the rows you opened the cell from.
|
|
205
|
+
*
|
|
206
|
+
* On a phone the modal sheet stays: there is no room to
|
|
207
|
+
* arrange anything, and the sheet already carries the shell's
|
|
208
|
+
* Escape / Back handling.
|
|
209
|
+
*
|
|
210
|
+
* Returns {win, modal} — exactly one of them is set.
|
|
211
|
+
************************************************************/
|
|
212
|
+
function present_json_popup(gobj, $box, opts)
|
|
213
|
+
{
|
|
214
|
+
let shell = yui_shell_of(gobj);
|
|
215
|
+
if(!shell) {
|
|
216
|
+
log_error(`${gobj_short_name(gobj)}: no shell, cannot present the json`);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if(is_mobile()) {
|
|
221
|
+
return {
|
|
222
|
+
modal: yui_shell_show_modal(shell, $box, {
|
|
223
|
+
dialog: true,
|
|
224
|
+
logical_class: opts.logical_class + "_SHEET",
|
|
225
|
+
title_prefix: opts.title_prefix,
|
|
226
|
+
title: opts.title,
|
|
227
|
+
t: t,
|
|
228
|
+
on_close: opts.on_close
|
|
229
|
+
}),
|
|
230
|
+
win: null
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let win = gobj_create_service(
|
|
235
|
+
opts.name,
|
|
236
|
+
"C_YUI_WINDOW",
|
|
237
|
+
{
|
|
238
|
+
$parent: popup_mount_layer(gobj),
|
|
239
|
+
subscriber: null,
|
|
240
|
+
modal: false,
|
|
241
|
+
showMax: true,
|
|
242
|
+
showFooter: false,
|
|
243
|
+
resizable: true,
|
|
244
|
+
center: true,
|
|
245
|
+
auto_save_size_and_position: true,
|
|
246
|
+
width: 720,
|
|
247
|
+
height: 620,
|
|
248
|
+
logical_class: opts.logical_class + "_WINDOW",
|
|
249
|
+
title_prefix: opts.title_prefix,
|
|
250
|
+
title: opts.title,
|
|
251
|
+
icon: opts.icon,
|
|
252
|
+
body: $box,
|
|
253
|
+
manager: null,
|
|
254
|
+
/* C_YUI_WINDOW.close_window() calls this and THEN stops and
|
|
255
|
+
* destroys itself, so a teardown that also destroys the
|
|
256
|
+
* window destroys it twice — two "gobj NULL or DESTROYED".
|
|
257
|
+
* `opts.forget_window()` drops the host's reference first;
|
|
258
|
+
* the ✕ path is the window retiring ITSELF. */
|
|
259
|
+
on_close: function() {
|
|
260
|
+
if(gobj_is_destroying(gobj)) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
opts.forget_window();
|
|
264
|
+
opts.on_close();
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
gobj
|
|
268
|
+
);
|
|
269
|
+
if(!win) {
|
|
270
|
+
log_error(`${gobj_short_name(gobj)}: cannot create the json window`);
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
gobj_start(win);
|
|
274
|
+
return {win: win, modal: null};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/************************************************************
|
|
278
|
+
* Retire a presenting window: reference cleared FIRST, then
|
|
279
|
+
* stop, then destroy.
|
|
280
|
+
*
|
|
281
|
+
* The order is the whole content of this function. The ✕ of
|
|
282
|
+
* the window calls back into the teardown that calls this, so
|
|
283
|
+
* clearing the reference first is what stops the recursion.
|
|
284
|
+
* And it is STOP then destroy because the window was started
|
|
285
|
+
* on open: handing a running gobj to gobj_destroy() makes the
|
|
286
|
+
* framework rescue it and say so — "Destroying a RUNNING
|
|
287
|
+
* gobj" — which is a caller's bug, not a framework message.
|
|
288
|
+
* Same lesson `c_tranger_view.close_json_viewer` carries.
|
|
289
|
+
************************************************************/
|
|
290
|
+
function retire_popup_window(win)
|
|
291
|
+
{
|
|
292
|
+
if(!win) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
if(gobj_is_running(win)) {
|
|
297
|
+
gobj_stop(win);
|
|
298
|
+
}
|
|
299
|
+
gobj_destroy(win);
|
|
300
|
+
} catch(e) {
|
|
301
|
+
log_warning(`${GCLASS_NAME}: json window already gone: ${e}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
187
305
|
function popup_mount_layer(gobj)
|
|
188
306
|
{
|
|
189
307
|
let $layer = yui_shell_popup_layer(yui_shell_of(gobj));
|
|
@@ -1864,16 +1982,25 @@ function open_schema_dialog(gobj)
|
|
|
1864
1982
|
/* Title split in two halves: the topic name is DATA (never
|
|
1865
1983
|
* translated) and "schema" is the kind (carries its i18n key), so
|
|
1866
1984
|
* the header re-translates on a language switch. */
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
logical_class: "
|
|
1985
|
+
let presented = present_json_popup(gobj, $box, {
|
|
1986
|
+
name: "schemawin_" + clean_name(gobj_name(gobj)),
|
|
1987
|
+
logical_class: "TREEDB_SCHEMA",
|
|
1870
1988
|
title_prefix: gobj_read_str_attr(gobj, "topic_name"),
|
|
1871
1989
|
title: "schema",
|
|
1872
|
-
|
|
1990
|
+
icon: "yi-table",
|
|
1991
|
+
forget_window: function() {
|
|
1992
|
+
priv.schema_win = null;
|
|
1993
|
+
},
|
|
1873
1994
|
on_close: function() {
|
|
1874
1995
|
teardown_schema_child(gobj);
|
|
1875
1996
|
}
|
|
1876
1997
|
});
|
|
1998
|
+
if(!presented) {
|
|
1999
|
+
teardown_schema_child(gobj); // Error already logged
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
priv.schema_modal = presented.modal;
|
|
2003
|
+
priv.schema_win = presented.win;
|
|
1877
2004
|
|
|
1878
2005
|
/* EV_SET_JSON and not the `json_data` attr: the attr renders the
|
|
1879
2006
|
* tree but leaves the viewer in ST_IDLE, where a click to expand a
|
|
@@ -1896,6 +2023,9 @@ function teardown_schema_child(gobj)
|
|
|
1896
2023
|
gobj_destroy(priv.schema_gobj);
|
|
1897
2024
|
priv.schema_gobj = null;
|
|
1898
2025
|
}
|
|
2026
|
+
let win = priv.schema_win;
|
|
2027
|
+
priv.schema_win = null;
|
|
2028
|
+
retire_popup_window(win);
|
|
1899
2029
|
priv.schema_modal = null;
|
|
1900
2030
|
}
|
|
1901
2031
|
|
|
@@ -1982,16 +2112,25 @@ function open_cell_json_dialog(gobj, row_id, col_id)
|
|
|
1982
2112
|
* i18n key — the shared `col.id` one, so the header re-translates on a
|
|
1983
2113
|
* language switch and reads as the raw id where the app defines no key
|
|
1984
2114
|
* (exactly what an untranslated column header already shows). */
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
logical_class: "
|
|
2115
|
+
let presented = present_json_popup(gobj, $box, {
|
|
2116
|
+
name: "cellwin_" + clean_name(gobj_name(gobj)),
|
|
2117
|
+
logical_class: "TREEDB_CELL_JSON",
|
|
1988
2118
|
title_prefix: String(row_id),
|
|
1989
2119
|
title: col_id,
|
|
1990
|
-
|
|
2120
|
+
icon: "yi-js-square",
|
|
2121
|
+
forget_window: function() {
|
|
2122
|
+
priv.cell_json_win = null;
|
|
2123
|
+
},
|
|
1991
2124
|
on_close: function() {
|
|
1992
2125
|
teardown_cell_json_child(gobj);
|
|
1993
2126
|
}
|
|
1994
2127
|
});
|
|
2128
|
+
if(!presented) {
|
|
2129
|
+
teardown_cell_json_child(gobj); // Error already logged
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
priv.cell_json_modal = presented.modal;
|
|
2133
|
+
priv.cell_json_win = presented.win;
|
|
1995
2134
|
|
|
1996
2135
|
/* EV_SET_JSON and not the `json_data` attr: the attr renders the
|
|
1997
2136
|
* tree but leaves the viewer in ST_IDLE, where a click to expand a
|
|
@@ -2014,6 +2153,9 @@ function teardown_cell_json_child(gobj)
|
|
|
2014
2153
|
gobj_destroy(priv.cell_json_gobj);
|
|
2015
2154
|
priv.cell_json_gobj = null;
|
|
2016
2155
|
}
|
|
2156
|
+
let win = priv.cell_json_win;
|
|
2157
|
+
priv.cell_json_win = null;
|
|
2158
|
+
retire_popup_window(win);
|
|
2017
2159
|
priv.cell_json_modal = null;
|
|
2018
2160
|
}
|
|
2019
2161
|
|