@yuneta/gobj-ui 2.4.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 +10520 -10445
- package/dist/gobj-ui.es.js +10520 -10445
- package/package.json +1 -1
- package/src/c_yui_main.css +78 -0
- package/src/c_yui_main.js +66 -39
- 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/src/yui_icons.css +15 -0
package/package.json
CHANGED
package/src/c_yui_main.css
CHANGED
|
@@ -421,3 +421,81 @@ a.flex-horizontal-section { /* To make flex any <a> */
|
|
|
421
421
|
color: var(--bulma-link-invert);
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
|
+
|
|
425
|
+
/*============================================================
|
|
426
|
+
* Volatil modals (display_volatil_modal):
|
|
427
|
+
* get_yesnocancel / get_yesno / get_ok and the typed
|
|
428
|
+
* info/warning/error messages. Icon-centric layout: a
|
|
429
|
+
* narrow rounded card with a tinted round icon of the
|
|
430
|
+
* type; everything maps to Bulma vars so one rule set
|
|
431
|
+
* follows light and dark.
|
|
432
|
+
*============================================================*/
|
|
433
|
+
.modal.yui-volatil .modal-background {
|
|
434
|
+
background-color: color-mix(in srgb, var(--bulma-scheme-invert, #000) 45%, transparent);
|
|
435
|
+
}
|
|
436
|
+
.modal.yui-volatil .modal-card {
|
|
437
|
+
position: relative;
|
|
438
|
+
width: min(26rem, calc(100vw - 2rem));
|
|
439
|
+
border-radius: 0.75rem;
|
|
440
|
+
background: var(--bulma-scheme-main, #fff);
|
|
441
|
+
box-shadow: 0 12px 40px color-mix(in srgb, var(--bulma-scheme-invert, #000) 30%, transparent);
|
|
442
|
+
padding: 1.5rem 1.5rem 1.25rem;
|
|
443
|
+
}
|
|
444
|
+
.modal.yui-volatil .modal-card-body {
|
|
445
|
+
background: transparent;
|
|
446
|
+
padding: 0;
|
|
447
|
+
}
|
|
448
|
+
.modal.yui-volatil .modal-card-foot {
|
|
449
|
+
background: transparent;
|
|
450
|
+
border-top: none;
|
|
451
|
+
padding: 1rem 0 0;
|
|
452
|
+
}
|
|
453
|
+
.yui-volatil-icon {
|
|
454
|
+
width: 3.25rem;
|
|
455
|
+
height: 3.25rem;
|
|
456
|
+
border-radius: 9999px;
|
|
457
|
+
display: flex;
|
|
458
|
+
align-items: center;
|
|
459
|
+
justify-content: center;
|
|
460
|
+
margin: 0 auto 0.75rem;
|
|
461
|
+
font-size: 1.5rem;
|
|
462
|
+
background: color-mix(in srgb, var(--yui-volatil-base, var(--bulma-info, #3e8ed0)) 15%, transparent);
|
|
463
|
+
color: var(--yui-volatil-ink, var(--bulma-info, #3e8ed0));
|
|
464
|
+
}
|
|
465
|
+
.modal.yui-volatil.is-info,
|
|
466
|
+
.modal.yui-volatil.is-question {
|
|
467
|
+
--yui-volatil-base: var(--bulma-info, #3e8ed0);
|
|
468
|
+
--yui-volatil-ink: var(--bulma-info-on-scheme, #3e8ed0);
|
|
469
|
+
}
|
|
470
|
+
.modal.yui-volatil.is-success {
|
|
471
|
+
--yui-volatil-base: var(--bulma-success, #48c78e);
|
|
472
|
+
--yui-volatil-ink: var(--bulma-success-on-scheme, #257953);
|
|
473
|
+
}
|
|
474
|
+
.modal.yui-volatil.is-warning {
|
|
475
|
+
--yui-volatil-base: var(--bulma-warning, #ffe08a);
|
|
476
|
+
--yui-volatil-ink: var(--bulma-warning-on-scheme, #946c00);
|
|
477
|
+
}
|
|
478
|
+
.modal.yui-volatil.is-error {
|
|
479
|
+
--yui-volatil-base: var(--bulma-danger, #f14668);
|
|
480
|
+
--yui-volatil-ink: var(--bulma-danger-on-scheme, #cc0f35);
|
|
481
|
+
}
|
|
482
|
+
.yui-volatil-title {
|
|
483
|
+
font-size: 1.15rem;
|
|
484
|
+
font-weight: 600;
|
|
485
|
+
text-transform: capitalize;
|
|
486
|
+
margin-bottom: 0.25rem;
|
|
487
|
+
color: var(--bulma-text-strong, inherit);
|
|
488
|
+
}
|
|
489
|
+
.yui-volatil-x {
|
|
490
|
+
position: absolute;
|
|
491
|
+
top: 0.75rem;
|
|
492
|
+
right: 0.75rem;
|
|
493
|
+
}
|
|
494
|
+
.yui-volatil-msg {
|
|
495
|
+
font-size: 1.05rem;
|
|
496
|
+
color: var(--bulma-text, inherit);
|
|
497
|
+
}
|
|
498
|
+
.modal.yui-volatil .modal-card-body .buttons {
|
|
499
|
+
margin-top: 1.25rem;
|
|
500
|
+
margin-bottom: 0;
|
|
501
|
+
}
|
package/src/c_yui_main.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* The tabs in a content-layer to manage sub-containers can be managed by yui_tabs.js
|
|
28
28
|
* or be integrated in each gobj.
|
|
29
29
|
*
|
|
30
|
-
* Copyright (c) 2025, ArtGins.
|
|
30
|
+
* Copyright (c) 2025-2026, ArtGins.
|
|
31
31
|
* All Rights Reserved.
|
|
32
32
|
***********************************************************************/
|
|
33
33
|
/* global ResizeObserver, window, document */
|
|
@@ -879,50 +879,77 @@ function display_volatil_modal(
|
|
|
879
879
|
{title, msg, callback, type, x_close, buttons}
|
|
880
880
|
) {
|
|
881
881
|
const callback_ = callback; // ✅ Assign to a constant
|
|
882
|
+
|
|
883
|
+
/* Normalize the type: it picks the tinted icon and the accent
|
|
884
|
+
* color (see the .yui-volatil rules in c_yui_main.css). */
|
|
885
|
+
let type_ = (type || "info").toLowerCase();
|
|
886
|
+
if(type_ === "danger") {
|
|
887
|
+
type_ = "error";
|
|
888
|
+
}
|
|
889
|
+
const TYPE_ICONS = {
|
|
890
|
+
"info": "yi-circle-info",
|
|
891
|
+
"question": "yi-question",
|
|
892
|
+
"success": "yi-square-check",
|
|
893
|
+
"warning": "yi-triangle-exclamation",
|
|
894
|
+
"error": "yi-circle-exclamation",
|
|
895
|
+
};
|
|
896
|
+
let icon = TYPE_ICONS[type_] || TYPE_ICONS["info"];
|
|
897
|
+
|
|
882
898
|
let $element = createElement2([
|
|
883
|
-
'div', {class:
|
|
899
|
+
'div', {class: `modal yui-volatil is-${type_}`}, [
|
|
884
900
|
['div', {class: 'modal-background'}, ''],
|
|
885
901
|
['div', {class: 'modal-card'}, []]
|
|
886
902
|
]
|
|
887
903
|
]);
|
|
888
904
|
|
|
889
905
|
const destroyModal = ()=> {
|
|
906
|
+
document.removeEventListener('keydown', on_keydown, true);
|
|
890
907
|
$element.classList.remove('is-active');
|
|
891
908
|
$element.parentNode.removeChild($element);
|
|
892
909
|
};
|
|
893
910
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
911
|
+
/* Escape = cancel. Capture phase on document, so Escape handlers
|
|
912
|
+
* beneath the modal (e.g. the treedb edit dialog's own) don't also
|
|
913
|
+
* fire and stack a second confirm. Only the top-most open modal
|
|
914
|
+
* reacts; the cancel/x click path carries the callback semantics,
|
|
915
|
+
* a buttonless modal just dismisses. */
|
|
916
|
+
const on_keydown = (evt)=> {
|
|
917
|
+
if(evt.key !== "Escape") {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
let $modals = document.querySelectorAll('.modal.is-active');
|
|
921
|
+
if($modals.length && $modals[$modals.length - 1] !== $element) {
|
|
922
|
+
return;
|
|
898
923
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
break;
|
|
907
|
-
case "warning":
|
|
908
|
-
background_color = 'has-background-warning';
|
|
909
|
-
break;
|
|
910
|
-
case "error":
|
|
911
|
-
case "danger":
|
|
912
|
-
background_color = 'has-background-danger';
|
|
913
|
-
break;
|
|
924
|
+
evt.stopPropagation();
|
|
925
|
+
evt.preventDefault();
|
|
926
|
+
let $cancel = $element.querySelector('.cancelButton, .delete');
|
|
927
|
+
if($cancel) {
|
|
928
|
+
$cancel.click();
|
|
929
|
+
} else {
|
|
930
|
+
destroyModal();
|
|
914
931
|
}
|
|
932
|
+
};
|
|
933
|
+
document.addEventListener('keydown', on_keydown, true);
|
|
915
934
|
|
|
916
|
-
|
|
917
|
-
let x_close_ = !x_close ? [] : ['button', {class: 'delete is-large', 'aria-label': 'close'}];
|
|
935
|
+
let $modal_card = $element.querySelector('.modal-card');
|
|
918
936
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
]
|
|
924
|
-
|
|
925
|
-
|
|
937
|
+
/* Icon-centric layout (no colored header bar): a tinted round
|
|
938
|
+
* icon of the type, an optional title, an optional top-right x. */
|
|
939
|
+
$modal_card.appendChild(createElement2(
|
|
940
|
+
['div', {class: 'yui-volatil-icon'}, [
|
|
941
|
+
['i', {class: icon, 'aria-hidden': 'true'}]
|
|
942
|
+
]]
|
|
943
|
+
));
|
|
944
|
+
if(title) {
|
|
945
|
+
$modal_card.appendChild(createElement2(
|
|
946
|
+
['p', {class: 'yui-volatil-title has-text-centered'}, title]
|
|
947
|
+
));
|
|
948
|
+
}
|
|
949
|
+
if(x_close) {
|
|
950
|
+
$modal_card.appendChild(createElement2(
|
|
951
|
+
['button', {class: 'delete yui-volatil-x', 'aria-label': 'close'}]
|
|
952
|
+
));
|
|
926
953
|
}
|
|
927
954
|
|
|
928
955
|
let $section = createElement2(
|
|
@@ -1017,7 +1044,7 @@ function display_info_message(title, msg, callback)
|
|
|
1017
1044
|
type: 'info',
|
|
1018
1045
|
buttons: [{
|
|
1019
1046
|
content:
|
|
1020
|
-
`<button class="button is-
|
|
1047
|
+
`<button class="button is-info px-6 with-focus">${t('accept')}</button>`,
|
|
1021
1048
|
}]
|
|
1022
1049
|
});
|
|
1023
1050
|
}
|
|
@@ -1037,7 +1064,7 @@ function display_warning_message(title, msg, callback)
|
|
|
1037
1064
|
type: 'warning',
|
|
1038
1065
|
buttons: [{
|
|
1039
1066
|
content:
|
|
1040
|
-
`<button class="button is-
|
|
1067
|
+
`<button class="button is-warning px-6 with-focus">${t('accept')}</button>`,
|
|
1041
1068
|
}]
|
|
1042
1069
|
});
|
|
1043
1070
|
}
|
|
@@ -1057,7 +1084,7 @@ function display_error_message(title, msg, callback, without_buttons)
|
|
|
1057
1084
|
type: 'error',
|
|
1058
1085
|
buttons: without_buttons? []: [{
|
|
1059
1086
|
content:
|
|
1060
|
-
`<button class="button is-
|
|
1087
|
+
`<button class="button is-danger px-6 with-focus">${t('accept')}</button>`,
|
|
1061
1088
|
}]
|
|
1062
1089
|
});
|
|
1063
1090
|
}
|
|
@@ -1069,7 +1096,7 @@ function get_yesnocancel(msg, callback)
|
|
|
1069
1096
|
{
|
|
1070
1097
|
let content =
|
|
1071
1098
|
`<div class="">
|
|
1072
|
-
<p class="
|
|
1099
|
+
<p class="yui-volatil-msg">${t(msg)}</p>
|
|
1073
1100
|
<div class="buttons is-centered">
|
|
1074
1101
|
<button class="button to-close is-success px-6 with-focus yesButton">${t('yes')}</button>
|
|
1075
1102
|
<button class="button to-close is-danger px-6 noButton">${t('no')}</button>
|
|
@@ -1091,7 +1118,7 @@ function get_yesnocancel(msg, callback)
|
|
|
1091
1118
|
title: null,
|
|
1092
1119
|
msg: content,
|
|
1093
1120
|
callback: callback_,
|
|
1094
|
-
type: '
|
|
1121
|
+
type: 'question'
|
|
1095
1122
|
});
|
|
1096
1123
|
}
|
|
1097
1124
|
|
|
@@ -1102,7 +1129,7 @@ function get_yesno(msg, callback)
|
|
|
1102
1129
|
{
|
|
1103
1130
|
let content =
|
|
1104
1131
|
`<div class="">
|
|
1105
|
-
<p class="
|
|
1132
|
+
<p class="yui-volatil-msg">${msg}</p>
|
|
1106
1133
|
<div class="buttons is-centered">
|
|
1107
1134
|
<button class="button to-close is-success px-6 with-focus yesButton">${t('yes')}</button>
|
|
1108
1135
|
<button class="button to-close is-danger px-6 noButton">${t('no')}</button>
|
|
@@ -1123,7 +1150,7 @@ function get_yesno(msg, callback)
|
|
|
1123
1150
|
title: null,
|
|
1124
1151
|
msg: content,
|
|
1125
1152
|
callback: callback_,
|
|
1126
|
-
type: '
|
|
1153
|
+
type: 'question'
|
|
1127
1154
|
});
|
|
1128
1155
|
}
|
|
1129
1156
|
|
|
@@ -1134,7 +1161,7 @@ function get_ok(msg, callback)
|
|
|
1134
1161
|
{
|
|
1135
1162
|
let content =
|
|
1136
1163
|
`<div class="">
|
|
1137
|
-
<p class="
|
|
1164
|
+
<p class="yui-volatil-msg">${msg}</p>
|
|
1138
1165
|
<div class="buttons is-centered">
|
|
1139
1166
|
<button class="button to-close is-success px-6 with-focus yesButton">${t('accept')}</button>
|
|
1140
1167
|
</div>
|
|
@@ -1154,7 +1181,7 @@ function get_ok(msg, callback)
|
|
|
1154
1181
|
title: null,
|
|
1155
1182
|
msg: content,
|
|
1156
1183
|
callback: callback_,
|
|
1157
|
-
type: '
|
|
1184
|
+
type: 'success'
|
|
1158
1185
|
});
|
|
1159
1186
|
}
|
|
1160
1187
|
|
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:
|
package/src/yui_icons.css
CHANGED
|
@@ -239,3 +239,18 @@
|
|
|
239
239
|
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M438.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-160-160c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L338.7 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l306.7 0L233.4 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l160-160z'/%3E%3C/svg%3E");
|
|
240
240
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M438.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-160-160c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L338.7 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l306.7 0L233.4 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l160-160z'/%3E%3C/svg%3E");
|
|
241
241
|
}
|
|
242
|
+
|
|
243
|
+
.yi-circle-info::before {
|
|
244
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z'/%3E%3C/svg%3E");
|
|
245
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z'/%3E%3C/svg%3E");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.yi-triangle-exclamation::before {
|
|
249
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z'/%3E%3C/svg%3E");
|
|
250
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z'/%3E%3C/svg%3E");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.yi-circle-exclamation::before {
|
|
254
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zm32 224a32 32 0 1 1 -64 0 32 32 0 1 1 64 0z'/%3E%3C/svg%3E");
|
|
255
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zm32 224a32 32 0 1 1 -64 0 32 32 0 1 1 64 0z'/%3E%3C/svg%3E");
|
|
256
|
+
}
|