@yuneta/gobj-ui 2.5.0 → 2.6.0
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/dist/gobj-ui.cjs.js +10407 -10337
- package/dist/gobj-ui.es.js +10407 -10337
- package/package.json +1 -1
- package/src/c_yui_shell.js +34 -0
- package/src/c_yui_treedb_graph.js +3 -2
- package/src/c_yui_treedb_topic_with_form.js +81 -26
- package/src/c_yui_treedb_topics.js +3 -4
- package/src/c_yui_window.js +7 -3
- package/src/shell_modals.js +20 -0
package/package.json
CHANGED
package/src/c_yui_shell.js
CHANGED
|
@@ -34,6 +34,8 @@ import {
|
|
|
34
34
|
gclass_create, log_error, log_warning,
|
|
35
35
|
gobj_create, gobj_destroy,
|
|
36
36
|
gobj_start, gobj_stop,
|
|
37
|
+
gobj_parent,
|
|
38
|
+
gobj_gclass_name,
|
|
37
39
|
gobj_publish_event,
|
|
38
40
|
gobj_send_event,
|
|
39
41
|
gobj_subscribe_event,
|
|
@@ -68,6 +70,12 @@ import {
|
|
|
68
70
|
***************************************************************/
|
|
69
71
|
const GCLASS_NAME = "C_YUI_SHELL";
|
|
70
72
|
|
|
73
|
+
/* Last shell created on the page — fallback for yui_shell_of()
|
|
74
|
+
* when the caller is not a shell descendant (e.g. a floating
|
|
75
|
+
* C_YUI_WINDOW parented to the app gobj). Real apps have exactly
|
|
76
|
+
* one shell; tests that create several get the most recent. */
|
|
77
|
+
let __last_shell__ = null;
|
|
78
|
+
|
|
71
79
|
/* Zones rendered inside the base layer. */
|
|
72
80
|
const ZONE_IDS = ["top", "top-sub", "left", "center", "right", "bottom-sub", "bottom"];
|
|
73
81
|
|
|
@@ -166,6 +174,8 @@ function mt_create(gobj)
|
|
|
166
174
|
active_dropdown: null
|
|
167
175
|
});
|
|
168
176
|
|
|
177
|
+
__last_shell__ = gobj;
|
|
178
|
+
|
|
169
179
|
build_ui(gobj);
|
|
170
180
|
}
|
|
171
181
|
|
|
@@ -318,6 +328,10 @@ function mt_destroy(gobj)
|
|
|
318
328
|
}
|
|
319
329
|
gobj_write_attr(gobj, "$container", null);
|
|
320
330
|
gobj_write_attr(gobj, "priv", null);
|
|
331
|
+
|
|
332
|
+
if(__last_shell__ === gobj) {
|
|
333
|
+
__last_shell__ = null;
|
|
334
|
+
}
|
|
321
335
|
}
|
|
322
336
|
|
|
323
337
|
|
|
@@ -2408,6 +2422,25 @@ function register_c_yui_shell()
|
|
|
2408
2422
|
* bottom of the file to keep the skeleton layout intact.
|
|
2409
2423
|
***************************************************************/
|
|
2410
2424
|
|
|
2425
|
+
/************************************************************
|
|
2426
|
+
* Resolve the shell that governs `gobj`: the nearest
|
|
2427
|
+
* C_YUI_SHELL ancestor, else the last shell created on the
|
|
2428
|
+
* page (apps have exactly one). Null when no shell exists —
|
|
2429
|
+
* callers must degrade loudly (shell_modals logs a warning
|
|
2430
|
+
* and resolves the safe default).
|
|
2431
|
+
************************************************************/
|
|
2432
|
+
function yui_shell_of(gobj)
|
|
2433
|
+
{
|
|
2434
|
+
let g = gobj;
|
|
2435
|
+
while(g) {
|
|
2436
|
+
if(gobj_gclass_name(g) === GCLASS_NAME) {
|
|
2437
|
+
return g;
|
|
2438
|
+
}
|
|
2439
|
+
g = gobj_parent(g);
|
|
2440
|
+
}
|
|
2441
|
+
return __last_shell__;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2411
2444
|
/************************************************************
|
|
2412
2445
|
* Programmatic navigation (bypass hash).
|
|
2413
2446
|
************************************************************/
|
|
@@ -2552,6 +2585,7 @@ function yui_shell_close_dropdown(shell_gobj)
|
|
|
2552
2585
|
|
|
2553
2586
|
export {
|
|
2554
2587
|
register_c_yui_shell,
|
|
2588
|
+
yui_shell_of,
|
|
2555
2589
|
yui_shell_navigate,
|
|
2556
2590
|
yui_shell_open_drawer,
|
|
2557
2591
|
yui_shell_close_drawer,
|
|
@@ -80,7 +80,8 @@ import {
|
|
|
80
80
|
set_submit_state,
|
|
81
81
|
set_active_state,
|
|
82
82
|
} from "./lib_graph.js";
|
|
83
|
-
import {
|
|
83
|
+
import {yui_shell_show_error} from "./shell_modals.js";
|
|
84
|
+
import {yui_shell_of} from "./c_yui_shell.js";
|
|
84
85
|
|
|
85
86
|
import {t} from "i18next";
|
|
86
87
|
|
|
@@ -941,7 +942,7 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
941
942
|
* app-modal that wedges the whole SPA behind an empty tab. */
|
|
942
943
|
show_load_error(gobj, t(comment));
|
|
943
944
|
} else {
|
|
944
|
-
|
|
945
|
+
yui_shell_show_error(yui_shell_of(gobj), comment, {t: t});
|
|
945
946
|
}
|
|
946
947
|
// HACK don't return for non-descs, pass errors when need it.
|
|
947
948
|
}
|
|
@@ -57,9 +57,16 @@ import {
|
|
|
57
57
|
} from "@yuneta/gobj-js";
|
|
58
58
|
|
|
59
59
|
import {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
yui_shell_confirm_yesnocancel,
|
|
61
|
+
yui_shell_confirm_ok,
|
|
62
|
+
yui_shell_popup_layer,
|
|
63
|
+
} from "./shell_modals.js";
|
|
64
|
+
|
|
65
|
+
import {
|
|
66
|
+
yui_shell_of,
|
|
67
|
+
yui_shell_push_escape,
|
|
68
|
+
yui_shell_pop_escape,
|
|
69
|
+
} from "./c_yui_shell.js";
|
|
63
70
|
|
|
64
71
|
import {register_c_yui_form} from "./c_yui_form.js";
|
|
65
72
|
|
|
@@ -132,17 +139,26 @@ let PRIVATE_DATA = {
|
|
|
132
139
|
tabulator: null,
|
|
133
140
|
form: null, // hosted C_YUI_FORM child (while dialog open)
|
|
134
141
|
$popup: null, // dialog DOM element
|
|
135
|
-
on_keydown: null, // document Escape handler (
|
|
142
|
+
on_keydown: null, // document Escape handler (no-shell fallback)
|
|
143
|
+
escape_shell: null, // shell whose Escape chain holds escape_handler
|
|
144
|
+
escape_handler: null, // close handler pushed on the shell chain
|
|
136
145
|
};
|
|
137
146
|
|
|
138
147
|
let __gclass__ = null;
|
|
139
148
|
|
|
140
|
-
/* Where to mount the edit/delete modal.
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
|
|
149
|
+
/* Where to mount the edit/delete modal. Under a C_YUI_SHELL use
|
|
150
|
+
* its popup layer (z 20): the shell confirms live on the modal
|
|
151
|
+
* layer (z 99), so they always paint above this dialog — a
|
|
152
|
+
* body-mounted Bulma `.modal` would cover them (the shell is a
|
|
153
|
+
* stacking context below body-level modals). The legacy
|
|
154
|
+
* C_YUI_MAIN "#popup-layer" and document.body (Bulma `.modal` is
|
|
155
|
+
* position:fixed) remain the shell-less fallbacks. */
|
|
156
|
+
function popup_mount_layer(gobj)
|
|
145
157
|
{
|
|
158
|
+
let $layer = yui_shell_popup_layer(yui_shell_of(gobj));
|
|
159
|
+
if($layer) {
|
|
160
|
+
return $layer;
|
|
161
|
+
}
|
|
146
162
|
return document.getElementById("popup-layer") || document.body;
|
|
147
163
|
}
|
|
148
164
|
|
|
@@ -200,6 +216,11 @@ function mt_stop(gobj)
|
|
|
200
216
|
***************************************************************/
|
|
201
217
|
function mt_destroy(gobj)
|
|
202
218
|
{
|
|
219
|
+
/* A destroy with the edit dialog open (e.g. the hosted view is
|
|
220
|
+
* rebuilt on a transport rebind) must tear the dialog down too:
|
|
221
|
+
* it unhooks the Escape handler (shell chain or document
|
|
222
|
+
* listener) and removes the dialog DOM. */
|
|
223
|
+
close_form_dialog(gobj);
|
|
203
224
|
destroy_ui(gobj);
|
|
204
225
|
}
|
|
205
226
|
|
|
@@ -1181,17 +1202,30 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1181
1202
|
$form_container.style.minHeight = "0";
|
|
1182
1203
|
}
|
|
1183
1204
|
|
|
1184
|
-
popup_mount_layer().appendChild($element);
|
|
1205
|
+
popup_mount_layer(gobj).appendChild($element);
|
|
1185
1206
|
refresh_language($element, t);
|
|
1186
1207
|
|
|
1187
|
-
/* Escape closes through the same unsaved-changes guard as the X.
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1208
|
+
/* Escape closes through the same unsaved-changes guard as the X.
|
|
1209
|
+
* Under a shell, ride its Escape priority chain — LIFO with the
|
|
1210
|
+
* shell confirm dialogs, so Escape on an open confirm never
|
|
1211
|
+
* reaches this dialog and stacks a second one. Without a shell
|
|
1212
|
+
* (bare unit tests) fall back to a document listener. */
|
|
1213
|
+
let shell = yui_shell_of(gobj);
|
|
1214
|
+
if(shell) {
|
|
1215
|
+
priv.escape_shell = shell;
|
|
1216
|
+
priv.escape_handler = function() {
|
|
1191
1217
|
request_close_form_dialog(gobj);
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
|
|
1218
|
+
};
|
|
1219
|
+
yui_shell_push_escape(shell, "popup", priv.escape_handler);
|
|
1220
|
+
} else {
|
|
1221
|
+
priv.on_keydown = function(evt) {
|
|
1222
|
+
if(evt.key === "Escape") {
|
|
1223
|
+
evt.stopPropagation();
|
|
1224
|
+
request_close_form_dialog(gobj);
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1227
|
+
document.addEventListener("keydown", priv.on_keydown);
|
|
1228
|
+
}
|
|
1195
1229
|
|
|
1196
1230
|
gobj_start(form); // mt_start loads `record` into the form
|
|
1197
1231
|
|
|
@@ -1219,8 +1253,12 @@ function request_close_form_dialog(gobj)
|
|
|
1219
1253
|
let kw = {};
|
|
1220
1254
|
gobj_send_event(priv.form, "EV_WINDOW_TO_CLOSE", kw, gobj);
|
|
1221
1255
|
if(kw.abort_close) {
|
|
1222
|
-
|
|
1223
|
-
|
|
1256
|
+
yui_shell_confirm_yesnocancel(
|
|
1257
|
+
yui_shell_of(gobj),
|
|
1258
|
+
'All changes will be lost. Are you sure?',
|
|
1259
|
+
{t: t, yes_label: "yes", no_label: "no", cancel_label: "cancel"}
|
|
1260
|
+
).then(function(answer) {
|
|
1261
|
+
if(answer === "yes") {
|
|
1224
1262
|
close_form_dialog(gobj);
|
|
1225
1263
|
}
|
|
1226
1264
|
});
|
|
@@ -1236,6 +1274,11 @@ function request_close_form_dialog(gobj)
|
|
|
1236
1274
|
function close_form_dialog(gobj)
|
|
1237
1275
|
{
|
|
1238
1276
|
let priv = gobj.priv;
|
|
1277
|
+
if(priv.escape_handler) {
|
|
1278
|
+
yui_shell_pop_escape(priv.escape_shell, priv.escape_handler);
|
|
1279
|
+
priv.escape_shell = null;
|
|
1280
|
+
priv.escape_handler = null;
|
|
1281
|
+
}
|
|
1239
1282
|
if(priv.on_keydown) {
|
|
1240
1283
|
document.removeEventListener("keydown", priv.on_keydown);
|
|
1241
1284
|
priv.on_keydown = null;
|
|
@@ -1640,7 +1683,7 @@ function show_dropdown_popup_menu(gobj, x, y, items, callback)
|
|
|
1640
1683
|
/*
|
|
1641
1684
|
* Add to popup layer
|
|
1642
1685
|
*/
|
|
1643
|
-
popup_mount_layer().appendChild($element);
|
|
1686
|
+
popup_mount_layer(gobj).appendChild($element);
|
|
1644
1687
|
|
|
1645
1688
|
/*
|
|
1646
1689
|
* Set position
|
|
@@ -1929,12 +1972,18 @@ function ac_delete_rows(gobj, event, kw, src)
|
|
|
1929
1972
|
*----------------------------*/
|
|
1930
1973
|
let rows = tabulator.getSelectedData();
|
|
1931
1974
|
if (!rows.length) {
|
|
1932
|
-
|
|
1975
|
+
yui_shell_confirm_ok(
|
|
1976
|
+
yui_shell_of(gobj), 'please select some row',
|
|
1977
|
+
{t: t, ok_label: "accept"}
|
|
1978
|
+
);
|
|
1933
1979
|
return 0;
|
|
1934
1980
|
}
|
|
1935
1981
|
|
|
1936
|
-
|
|
1937
|
-
|
|
1982
|
+
yui_shell_confirm_yesnocancel(
|
|
1983
|
+
yui_shell_of(gobj), 'are you sure',
|
|
1984
|
+
{t: t, yes_label: "yes", no_label: "no", cancel_label: "cancel"}
|
|
1985
|
+
).then(function(answer) {
|
|
1986
|
+
if(answer === "yes") {
|
|
1938
1987
|
for(let row of rows) {
|
|
1939
1988
|
// TODO why don't send once EV_DELETE_RECORD(S)
|
|
1940
1989
|
gobj_publish_event(
|
|
@@ -1954,8 +2003,11 @@ function ac_delete_rows(gobj, event, kw, src)
|
|
|
1954
2003
|
* Delete one row
|
|
1955
2004
|
* {index: , row: }
|
|
1956
2005
|
*----------------------------*/
|
|
1957
|
-
|
|
1958
|
-
|
|
2006
|
+
yui_shell_confirm_yesnocancel(
|
|
2007
|
+
yui_shell_of(gobj), 'are you sure',
|
|
2008
|
+
{t: t, yes_label: "yes", no_label: "no", cancel_label: "cancel"}
|
|
2009
|
+
).then(function(answer) {
|
|
2010
|
+
if(answer === "yes") {
|
|
1959
2011
|
gobj_publish_event(
|
|
1960
2012
|
gobj,
|
|
1961
2013
|
"EV_DELETE_RECORD",
|
|
@@ -1983,7 +2035,10 @@ function ac_copy_rows(gobj, event, kw, src)
|
|
|
1983
2035
|
*----------------------------*/
|
|
1984
2036
|
let rows = tabulator.getSelectedData();
|
|
1985
2037
|
if (!rows.length) {
|
|
1986
|
-
|
|
2038
|
+
yui_shell_confirm_ok(
|
|
2039
|
+
yui_shell_of(gobj), 'please select some row',
|
|
2040
|
+
{t: t, ok_label: "accept"}
|
|
2041
|
+
);
|
|
1987
2042
|
return 0;
|
|
1988
2043
|
}
|
|
1989
2044
|
|
|
@@ -41,9 +41,8 @@ import {
|
|
|
41
41
|
refresh_language,
|
|
42
42
|
} from "@yuneta/gobj-js";
|
|
43
43
|
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
} from "./c_yui_main.js";
|
|
44
|
+
import {yui_shell_show_error} from "./shell_modals.js";
|
|
45
|
+
import {yui_shell_of} from "./c_yui_shell.js";
|
|
47
46
|
|
|
48
47
|
import {t} from "i18next";
|
|
49
48
|
|
|
@@ -819,7 +818,7 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
819
818
|
* app-modal that wedges the whole SPA behind an empty tab. */
|
|
820
819
|
show_load_error(gobj, t(comment));
|
|
821
820
|
} else {
|
|
822
|
-
|
|
821
|
+
yui_shell_show_error(yui_shell_of(gobj), comment, {t: t});
|
|
823
822
|
}
|
|
824
823
|
return 0;
|
|
825
824
|
}
|
package/src/c_yui_window.js
CHANGED
|
@@ -44,7 +44,8 @@ import {
|
|
|
44
44
|
|
|
45
45
|
import {t} from "i18next";
|
|
46
46
|
|
|
47
|
-
import {
|
|
47
|
+
import {yui_shell_confirm_yesnocancel} from "./shell_modals.js";
|
|
48
|
+
import {yui_shell_of} from "./c_yui_shell.js";
|
|
48
49
|
|
|
49
50
|
/***************************************************************
|
|
50
51
|
* Constants
|
|
@@ -672,8 +673,11 @@ function close_window(gobj)
|
|
|
672
673
|
gobj_stop_children(gobj);
|
|
673
674
|
gobj_destroy(gobj);
|
|
674
675
|
} else if(kw_close.warning) {
|
|
675
|
-
|
|
676
|
-
|
|
676
|
+
yui_shell_confirm_yesnocancel(
|
|
677
|
+
yui_shell_of(gobj), kw_close.warning,
|
|
678
|
+
{t: t, yes_label: "yes", no_label: "no", cancel_label: "cancel"}
|
|
679
|
+
).then(function(answer) {
|
|
680
|
+
if(answer === "yes") {
|
|
677
681
|
if(on_close) {
|
|
678
682
|
on_close();
|
|
679
683
|
}
|
package/src/shell_modals.js
CHANGED
|
@@ -49,16 +49,36 @@ import {
|
|
|
49
49
|
***************************************************************/
|
|
50
50
|
function notification_layer(shell)
|
|
51
51
|
{
|
|
52
|
+
if(!shell) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
52
55
|
let priv = gobj_read_attr(shell, "priv");
|
|
53
56
|
return priv && priv.layers && priv.layers.notification;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
function modal_layer(shell)
|
|
57
60
|
{
|
|
61
|
+
if(!shell) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
58
64
|
let priv = gobj_read_attr(shell, "priv");
|
|
59
65
|
return priv && priv.layers && priv.layers.modal;
|
|
60
66
|
}
|
|
61
67
|
|
|
68
|
+
/* Public: where a component should mount its own popup/dialog DOM
|
|
69
|
+
* (e.g. the treedb edit dialog). The popup layer (z 20) sits below
|
|
70
|
+
* the modal layer (z 99), so shell confirms always paint above the
|
|
71
|
+
* component's dialog — matching the Escape chain LIFO order. Null
|
|
72
|
+
* when there is no shell: the caller picks its legacy fallback. */
|
|
73
|
+
export function yui_shell_popup_layer(shell)
|
|
74
|
+
{
|
|
75
|
+
if(!shell) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
let priv = gobj_read_attr(shell, "priv");
|
|
79
|
+
return (priv && priv.layers && priv.layers.popup) || null;
|
|
80
|
+
}
|
|
81
|
+
|
|
62
82
|
|
|
63
83
|
/***************************************************************
|
|
64
84
|
* i18n bridge:
|