@yuneta/gobj-ui 2.3.1 → 2.5.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 +95 -51
- package/dist/gobj-ui.es.js +95 -51
- package/package.json +1 -1
- package/src/c_yui_form.css +50 -0
- package/src/c_yui_form.js +20 -2
- package/src/c_yui_main.css +78 -0
- package/src/c_yui_main.js +66 -39
- package/src/c_yui_shell.css +10 -2
- package/src/c_yui_treedb_topic_with_form.css +23 -0
- package/src/c_yui_treedb_topic_with_form.js +47 -4
- package/src/yui_icons.css +15 -0
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -1562,7 +1562,7 @@ function yui_toolbar(attrs = {}, items = []) {
|
|
|
1562
1562
|
* The tabs in a content-layer to manage sub-containers can be managed by yui_tabs.js
|
|
1563
1563
|
* or be integrated in each gobj.
|
|
1564
1564
|
*
|
|
1565
|
-
* Copyright (c) 2025, ArtGins.
|
|
1565
|
+
* Copyright (c) 2025-2026, ArtGins.
|
|
1566
1566
|
* All Rights Reserved.
|
|
1567
1567
|
***********************************************************************/
|
|
1568
1568
|
/***************************************************************
|
|
@@ -2153,9 +2153,19 @@ console.log("GLOBAL callback");
|
|
|
2153
2153
|
************************************************************/
|
|
2154
2154
|
function display_volatil_modal({ title, msg, callback, type, x_close, buttons }) {
|
|
2155
2155
|
const callback_ = callback;
|
|
2156
|
+
let type_ = (type || "info").toLowerCase();
|
|
2157
|
+
if (type_ === "danger") type_ = "error";
|
|
2158
|
+
const TYPE_ICONS = {
|
|
2159
|
+
"info": "yi-circle-info",
|
|
2160
|
+
"question": "yi-question",
|
|
2161
|
+
"success": "yi-square-check",
|
|
2162
|
+
"warning": "yi-triangle-exclamation",
|
|
2163
|
+
"error": "yi-circle-exclamation"
|
|
2164
|
+
};
|
|
2165
|
+
let icon = TYPE_ICONS[type_] || TYPE_ICONS["info"];
|
|
2156
2166
|
let $element = (0, _yuneta_gobj_js.createElement2)([
|
|
2157
2167
|
"div",
|
|
2158
|
-
{ class:
|
|
2168
|
+
{ class: `modal yui-volatil is-${type_}` },
|
|
2159
2169
|
[[
|
|
2160
2170
|
"div",
|
|
2161
2171
|
{ class: "modal-background" },
|
|
@@ -2167,44 +2177,39 @@ function display_volatil_modal({ title, msg, callback, type, x_close, buttons })
|
|
|
2167
2177
|
]]
|
|
2168
2178
|
]);
|
|
2169
2179
|
const destroyModal = () => {
|
|
2180
|
+
document.removeEventListener("keydown", on_keydown, true);
|
|
2170
2181
|
$element.classList.remove("is-active");
|
|
2171
2182
|
$element.parentNode.removeChild($element);
|
|
2172
2183
|
};
|
|
2184
|
+
const on_keydown = (evt) => {
|
|
2185
|
+
if (evt.key !== "Escape") return;
|
|
2186
|
+
let $modals = document.querySelectorAll(".modal.is-active");
|
|
2187
|
+
if ($modals.length && $modals[$modals.length - 1] !== $element) return;
|
|
2188
|
+
evt.stopPropagation();
|
|
2189
|
+
evt.preventDefault();
|
|
2190
|
+
let $cancel = $element.querySelector(".cancelButton, .delete");
|
|
2191
|
+
if ($cancel) $cancel.click();
|
|
2192
|
+
else destroyModal();
|
|
2193
|
+
};
|
|
2194
|
+
document.addEventListener("keydown", on_keydown, true);
|
|
2173
2195
|
let $modal_card = $element.querySelector(".modal-card");
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
}
|
|
2192
|
-
let title_ = !title ? [] : [
|
|
2193
|
-
"p",
|
|
2194
|
-
{ class: "modal-card-title has-text-centered" },
|
|
2195
|
-
title
|
|
2196
|
-
];
|
|
2197
|
-
let x_close_ = !x_close ? [] : ["button", {
|
|
2198
|
-
class: "delete is-large",
|
|
2199
|
-
"aria-label": "close"
|
|
2200
|
-
}];
|
|
2201
|
-
let $header = (0, _yuneta_gobj_js.createElement2)([
|
|
2202
|
-
"header",
|
|
2203
|
-
{ class: `modal-card-head ${background_color} p-3` },
|
|
2204
|
-
[title_, x_close_]
|
|
2205
|
-
]);
|
|
2206
|
-
$modal_card.appendChild($header);
|
|
2207
|
-
}
|
|
2196
|
+
$modal_card.appendChild((0, _yuneta_gobj_js.createElement2)([
|
|
2197
|
+
"div",
|
|
2198
|
+
{ class: "yui-volatil-icon" },
|
|
2199
|
+
[["i", {
|
|
2200
|
+
class: icon,
|
|
2201
|
+
"aria-hidden": "true"
|
|
2202
|
+
}]]
|
|
2203
|
+
]));
|
|
2204
|
+
if (title) $modal_card.appendChild((0, _yuneta_gobj_js.createElement2)([
|
|
2205
|
+
"p",
|
|
2206
|
+
{ class: "yui-volatil-title has-text-centered" },
|
|
2207
|
+
title
|
|
2208
|
+
]));
|
|
2209
|
+
if (x_close) $modal_card.appendChild((0, _yuneta_gobj_js.createElement2)(["button", {
|
|
2210
|
+
class: "delete yui-volatil-x",
|
|
2211
|
+
"aria-label": "close"
|
|
2212
|
+
}]));
|
|
2208
2213
|
let $section = (0, _yuneta_gobj_js.createElement2)([
|
|
2209
2214
|
"section",
|
|
2210
2215
|
{ class: "modal-card-body has-text-centered" },
|
|
@@ -2265,7 +2270,7 @@ function display_info_message(title, msg, callback) {
|
|
|
2265
2270
|
msg,
|
|
2266
2271
|
callback,
|
|
2267
2272
|
type: "info",
|
|
2268
|
-
buttons: [{ content: `<button class="button is-
|
|
2273
|
+
buttons: [{ content: `<button class="button is-info px-6 with-focus">${(0, i18next.t)("accept")}</button>` }]
|
|
2269
2274
|
});
|
|
2270
2275
|
}
|
|
2271
2276
|
/************************************************************
|
|
@@ -2278,7 +2283,7 @@ function display_warning_message(title, msg, callback) {
|
|
|
2278
2283
|
msg,
|
|
2279
2284
|
callback,
|
|
2280
2285
|
type: "warning",
|
|
2281
|
-
buttons: [{ content: `<button class="button is-
|
|
2286
|
+
buttons: [{ content: `<button class="button is-warning px-6 with-focus">${(0, i18next.t)("accept")}</button>` }]
|
|
2282
2287
|
});
|
|
2283
2288
|
}
|
|
2284
2289
|
/************************************************************
|
|
@@ -2291,7 +2296,7 @@ function display_error_message(title, msg, callback, without_buttons) {
|
|
|
2291
2296
|
msg,
|
|
2292
2297
|
callback,
|
|
2293
2298
|
type: "error",
|
|
2294
|
-
buttons: without_buttons ? [] : [{ content: `<button class="button is-
|
|
2299
|
+
buttons: without_buttons ? [] : [{ content: `<button class="button is-danger px-6 with-focus">${(0, i18next.t)("accept")}</button>` }]
|
|
2295
2300
|
});
|
|
2296
2301
|
}
|
|
2297
2302
|
/************************************************************
|
|
@@ -2299,7 +2304,7 @@ function display_error_message(title, msg, callback, without_buttons) {
|
|
|
2299
2304
|
************************************************************/
|
|
2300
2305
|
function get_yesnocancel(msg, callback) {
|
|
2301
2306
|
let content = `<div class="">
|
|
2302
|
-
<p class="
|
|
2307
|
+
<p class="yui-volatil-msg">${(0, i18next.t)(msg)}</p>
|
|
2303
2308
|
<div class="buttons is-centered">
|
|
2304
2309
|
<button class="button to-close is-success px-6 with-focus yesButton">${(0, i18next.t)("yes")}</button>
|
|
2305
2310
|
<button class="button to-close is-danger px-6 noButton">${(0, i18next.t)("no")}</button>
|
|
@@ -2315,7 +2320,7 @@ function get_yesnocancel(msg, callback) {
|
|
|
2315
2320
|
title: null,
|
|
2316
2321
|
msg: content,
|
|
2317
2322
|
callback: callback_,
|
|
2318
|
-
type: "
|
|
2323
|
+
type: "question"
|
|
2319
2324
|
});
|
|
2320
2325
|
}
|
|
2321
2326
|
/************************************************************
|
|
@@ -2323,7 +2328,7 @@ function get_yesnocancel(msg, callback) {
|
|
|
2323
2328
|
************************************************************/
|
|
2324
2329
|
function get_yesno(msg, callback) {
|
|
2325
2330
|
let content = `<div class="">
|
|
2326
|
-
<p class="
|
|
2331
|
+
<p class="yui-volatil-msg">${msg}</p>
|
|
2327
2332
|
<div class="buttons is-centered">
|
|
2328
2333
|
<button class="button to-close is-success px-6 with-focus yesButton">${(0, i18next.t)("yes")}</button>
|
|
2329
2334
|
<button class="button to-close is-danger px-6 noButton">${(0, i18next.t)("no")}</button>
|
|
@@ -2338,7 +2343,7 @@ function get_yesno(msg, callback) {
|
|
|
2338
2343
|
title: null,
|
|
2339
2344
|
msg: content,
|
|
2340
2345
|
callback: callback_,
|
|
2341
|
-
type: "
|
|
2346
|
+
type: "question"
|
|
2342
2347
|
});
|
|
2343
2348
|
}
|
|
2344
2349
|
/************************************************************
|
|
@@ -2346,7 +2351,7 @@ function get_yesno(msg, callback) {
|
|
|
2346
2351
|
************************************************************/
|
|
2347
2352
|
function get_ok(msg, callback) {
|
|
2348
2353
|
let content = `<div class="">
|
|
2349
|
-
<p class="
|
|
2354
|
+
<p class="yui-volatil-msg">${msg}</p>
|
|
2350
2355
|
<div class="buttons is-centered">
|
|
2351
2356
|
<button class="button to-close is-success px-6 with-focus yesButton">${(0, i18next.t)("accept")}</button>
|
|
2352
2357
|
</div>
|
|
@@ -2360,7 +2365,7 @@ function get_ok(msg, callback) {
|
|
|
2360
2365
|
title: null,
|
|
2361
2366
|
msg: content,
|
|
2362
2367
|
callback: callback_,
|
|
2363
|
-
type: "
|
|
2368
|
+
type: "success"
|
|
2364
2369
|
});
|
|
2365
2370
|
}
|
|
2366
2371
|
/************************************************************
|
|
@@ -5383,7 +5388,7 @@ function register_c_yui_wizard() {
|
|
|
5383
5388
|
*
|
|
5384
5389
|
* Form (in Window or not)
|
|
5385
5390
|
*
|
|
5386
|
-
* Copyright (c) 2024-
|
|
5391
|
+
* Copyright (c) 2024-2026, ArtGins.
|
|
5387
5392
|
* All Rights Reserved.
|
|
5388
5393
|
***********************************************************************/
|
|
5389
5394
|
/***************************************************************
|
|
@@ -5998,6 +6003,17 @@ function apply_form_mode(gobj, $form) {
|
|
|
5998
6003
|
}
|
|
5999
6004
|
}
|
|
6000
6005
|
/******************************************************************
|
|
6006
|
+
* True when the app renders dark: explicit <html data-theme>, or
|
|
6007
|
+
* the OS scheme when the attribute is absent ("system" theme
|
|
6008
|
+
* removes it — see themes.js). Read at field-build time; the
|
|
6009
|
+
* hosting dialog rebuilds the form on every open.
|
|
6010
|
+
******************************************************************/
|
|
6011
|
+
function form_is_dark_theme() {
|
|
6012
|
+
let attr = document.documentElement.getAttribute("data-theme");
|
|
6013
|
+
if (attr) return attr === "dark";
|
|
6014
|
+
return typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
6015
|
+
}
|
|
6016
|
+
/******************************************************************
|
|
6001
6017
|
* With html field conf builds an html element,
|
|
6002
6018
|
* ready to add to the form
|
|
6003
6019
|
*
|
|
@@ -6438,7 +6454,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6438
6454
|
break;
|
|
6439
6455
|
case "jsoneditor": {
|
|
6440
6456
|
let attrs = {
|
|
6441
|
-
class: "jsoneditor jse-theme-dark",
|
|
6457
|
+
class: "jsoneditor" + (form_is_dark_theme() ? " jse-theme-dark" : ""),
|
|
6442
6458
|
name,
|
|
6443
6459
|
style: ""
|
|
6444
6460
|
};
|
|
@@ -15402,7 +15418,7 @@ function register_c_yui_treedb_topics() {
|
|
|
15402
15418
|
*
|
|
15403
15419
|
* Table of TreeDB topic with FORM for editing
|
|
15404
15420
|
*
|
|
15405
|
-
* Copyright (c) 2025, ArtGins.
|
|
15421
|
+
* Copyright (c) 2025-2026, ArtGins.
|
|
15406
15422
|
* All Rights Reserved.
|
|
15407
15423
|
***********************************************************************/
|
|
15408
15424
|
/***************************************************************
|
|
@@ -15457,7 +15473,8 @@ var PRIVATE_DATA$2 = {
|
|
|
15457
15473
|
topic_name: "",
|
|
15458
15474
|
tabulator: null,
|
|
15459
15475
|
form: null,
|
|
15460
|
-
$popup: null
|
|
15476
|
+
$popup: null,
|
|
15477
|
+
on_keydown: null
|
|
15461
15478
|
};
|
|
15462
15479
|
var __gclass__$2 = null;
|
|
15463
15480
|
function popup_mount_layer() {
|
|
@@ -16026,6 +16043,14 @@ function create_tabulator(gobj) {
|
|
|
16026
16043
|
n = 0;
|
|
16027
16044
|
}
|
|
16028
16045
|
$rc.textContent = `${n} ${(0, i18next.t)("rows")}`;
|
|
16046
|
+
let single_page = true;
|
|
16047
|
+
try {
|
|
16048
|
+
let max = tabulator.getPageMax();
|
|
16049
|
+
single_page = max === false || max <= 1;
|
|
16050
|
+
} catch (e) {
|
|
16051
|
+
single_page = true;
|
|
16052
|
+
}
|
|
16053
|
+
tabulator.element.classList.toggle("yui-single-page", single_page);
|
|
16029
16054
|
}
|
|
16030
16055
|
tabulator._ready = false;
|
|
16031
16056
|
tabulator.on("tableBuilt", function() {
|
|
@@ -16201,6 +16226,13 @@ function open_form_dialog(gobj, mode, record) {
|
|
|
16201
16226
|
let desc = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "desc");
|
|
16202
16227
|
let popup_id = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "popup_id");
|
|
16203
16228
|
let topic_name = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "topic_name");
|
|
16229
|
+
let pkey = desc.pkey || "id";
|
|
16230
|
+
let title;
|
|
16231
|
+
if (mode === "create") title = (0, i18next.t)("new") + " " + (0, i18next.t)(topic_name);
|
|
16232
|
+
else {
|
|
16233
|
+
let rid = record ? String(record[pkey] ?? "") : "";
|
|
16234
|
+
title = (0, i18next.t)(topic_name) + ((0, _yuneta_gobj_js.empty_string)(rid) ? "" : " — " + rid);
|
|
16235
|
+
}
|
|
16204
16236
|
let $element = (0, _yuneta_gobj_js.createElement2)([
|
|
16205
16237
|
"div",
|
|
16206
16238
|
{
|
|
@@ -16223,7 +16255,7 @@ function open_form_dialog(gobj, mode, record) {
|
|
|
16223
16255
|
class: "modal-card-title",
|
|
16224
16256
|
style: "text-align:center;"
|
|
16225
16257
|
},
|
|
16226
|
-
|
|
16258
|
+
title
|
|
16227
16259
|
], [
|
|
16228
16260
|
"button",
|
|
16229
16261
|
{
|
|
@@ -16269,8 +16301,16 @@ function open_form_dialog(gobj, mode, record) {
|
|
|
16269
16301
|
}
|
|
16270
16302
|
popup_mount_layer().appendChild($element);
|
|
16271
16303
|
(0, _yuneta_gobj_js.refresh_language)($element, i18next.t);
|
|
16304
|
+
priv.on_keydown = function(evt) {
|
|
16305
|
+
if (evt.key === "Escape") {
|
|
16306
|
+
evt.stopPropagation();
|
|
16307
|
+
request_close_form_dialog(gobj);
|
|
16308
|
+
}
|
|
16309
|
+
};
|
|
16310
|
+
document.addEventListener("keydown", priv.on_keydown);
|
|
16272
16311
|
(0, _yuneta_gobj_js.gobj_start)(form);
|
|
16273
|
-
let $with_focus = $element.querySelector(".with-focus");
|
|
16312
|
+
let $with_focus = $element.querySelector(".with-focus:not([readonly])");
|
|
16313
|
+
if (!$with_focus) $with_focus = $element.querySelector("input.input:not([readonly]), textarea, select");
|
|
16274
16314
|
if ($with_focus) $with_focus.focus();
|
|
16275
16315
|
}
|
|
16276
16316
|
/************************************************************
|
|
@@ -16296,6 +16336,10 @@ function request_close_form_dialog(gobj) {
|
|
|
16296
16336
|
************************************************************/
|
|
16297
16337
|
function close_form_dialog(gobj) {
|
|
16298
16338
|
let priv = gobj.priv;
|
|
16339
|
+
if (priv.on_keydown) {
|
|
16340
|
+
document.removeEventListener("keydown", priv.on_keydown);
|
|
16341
|
+
priv.on_keydown = null;
|
|
16342
|
+
}
|
|
16299
16343
|
if (priv.form) {
|
|
16300
16344
|
if ((0, _yuneta_gobj_js.gobj_is_running)(priv.form)) (0, _yuneta_gobj_js.gobj_stop)(priv.form);
|
|
16301
16345
|
(0, _yuneta_gobj_js.gobj_destroy)(priv.form);
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -1557,7 +1557,7 @@ function yui_toolbar(attrs = {}, items = []) {
|
|
|
1557
1557
|
* The tabs in a content-layer to manage sub-containers can be managed by yui_tabs.js
|
|
1558
1558
|
* or be integrated in each gobj.
|
|
1559
1559
|
*
|
|
1560
|
-
* Copyright (c) 2025, ArtGins.
|
|
1560
|
+
* Copyright (c) 2025-2026, ArtGins.
|
|
1561
1561
|
* All Rights Reserved.
|
|
1562
1562
|
***********************************************************************/
|
|
1563
1563
|
/***************************************************************
|
|
@@ -2148,9 +2148,19 @@ console.log("GLOBAL callback");
|
|
|
2148
2148
|
************************************************************/
|
|
2149
2149
|
function display_volatil_modal({ title, msg, callback, type, x_close, buttons }) {
|
|
2150
2150
|
const callback_ = callback;
|
|
2151
|
+
let type_ = (type || "info").toLowerCase();
|
|
2152
|
+
if (type_ === "danger") type_ = "error";
|
|
2153
|
+
const TYPE_ICONS = {
|
|
2154
|
+
"info": "yi-circle-info",
|
|
2155
|
+
"question": "yi-question",
|
|
2156
|
+
"success": "yi-square-check",
|
|
2157
|
+
"warning": "yi-triangle-exclamation",
|
|
2158
|
+
"error": "yi-circle-exclamation"
|
|
2159
|
+
};
|
|
2160
|
+
let icon = TYPE_ICONS[type_] || TYPE_ICONS["info"];
|
|
2151
2161
|
let $element = createElement2([
|
|
2152
2162
|
"div",
|
|
2153
|
-
{ class:
|
|
2163
|
+
{ class: `modal yui-volatil is-${type_}` },
|
|
2154
2164
|
[[
|
|
2155
2165
|
"div",
|
|
2156
2166
|
{ class: "modal-background" },
|
|
@@ -2162,44 +2172,39 @@ function display_volatil_modal({ title, msg, callback, type, x_close, buttons })
|
|
|
2162
2172
|
]]
|
|
2163
2173
|
]);
|
|
2164
2174
|
const destroyModal = () => {
|
|
2175
|
+
document.removeEventListener("keydown", on_keydown, true);
|
|
2165
2176
|
$element.classList.remove("is-active");
|
|
2166
2177
|
$element.parentNode.removeChild($element);
|
|
2167
2178
|
};
|
|
2179
|
+
const on_keydown = (evt) => {
|
|
2180
|
+
if (evt.key !== "Escape") return;
|
|
2181
|
+
let $modals = document.querySelectorAll(".modal.is-active");
|
|
2182
|
+
if ($modals.length && $modals[$modals.length - 1] !== $element) return;
|
|
2183
|
+
evt.stopPropagation();
|
|
2184
|
+
evt.preventDefault();
|
|
2185
|
+
let $cancel = $element.querySelector(".cancelButton, .delete");
|
|
2186
|
+
if ($cancel) $cancel.click();
|
|
2187
|
+
else destroyModal();
|
|
2188
|
+
};
|
|
2189
|
+
document.addEventListener("keydown", on_keydown, true);
|
|
2168
2190
|
let $modal_card = $element.querySelector(".modal-card");
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
}
|
|
2187
|
-
let title_ = !title ? [] : [
|
|
2188
|
-
"p",
|
|
2189
|
-
{ class: "modal-card-title has-text-centered" },
|
|
2190
|
-
title
|
|
2191
|
-
];
|
|
2192
|
-
let x_close_ = !x_close ? [] : ["button", {
|
|
2193
|
-
class: "delete is-large",
|
|
2194
|
-
"aria-label": "close"
|
|
2195
|
-
}];
|
|
2196
|
-
let $header = createElement2([
|
|
2197
|
-
"header",
|
|
2198
|
-
{ class: `modal-card-head ${background_color} p-3` },
|
|
2199
|
-
[title_, x_close_]
|
|
2200
|
-
]);
|
|
2201
|
-
$modal_card.appendChild($header);
|
|
2202
|
-
}
|
|
2191
|
+
$modal_card.appendChild(createElement2([
|
|
2192
|
+
"div",
|
|
2193
|
+
{ class: "yui-volatil-icon" },
|
|
2194
|
+
[["i", {
|
|
2195
|
+
class: icon,
|
|
2196
|
+
"aria-hidden": "true"
|
|
2197
|
+
}]]
|
|
2198
|
+
]));
|
|
2199
|
+
if (title) $modal_card.appendChild(createElement2([
|
|
2200
|
+
"p",
|
|
2201
|
+
{ class: "yui-volatil-title has-text-centered" },
|
|
2202
|
+
title
|
|
2203
|
+
]));
|
|
2204
|
+
if (x_close) $modal_card.appendChild(createElement2(["button", {
|
|
2205
|
+
class: "delete yui-volatil-x",
|
|
2206
|
+
"aria-label": "close"
|
|
2207
|
+
}]));
|
|
2203
2208
|
let $section = createElement2([
|
|
2204
2209
|
"section",
|
|
2205
2210
|
{ class: "modal-card-body has-text-centered" },
|
|
@@ -2260,7 +2265,7 @@ function display_info_message(title, msg, callback) {
|
|
|
2260
2265
|
msg,
|
|
2261
2266
|
callback,
|
|
2262
2267
|
type: "info",
|
|
2263
|
-
buttons: [{ content: `<button class="button is-
|
|
2268
|
+
buttons: [{ content: `<button class="button is-info px-6 with-focus">${t("accept")}</button>` }]
|
|
2264
2269
|
});
|
|
2265
2270
|
}
|
|
2266
2271
|
/************************************************************
|
|
@@ -2273,7 +2278,7 @@ function display_warning_message(title, msg, callback) {
|
|
|
2273
2278
|
msg,
|
|
2274
2279
|
callback,
|
|
2275
2280
|
type: "warning",
|
|
2276
|
-
buttons: [{ content: `<button class="button is-
|
|
2281
|
+
buttons: [{ content: `<button class="button is-warning px-6 with-focus">${t("accept")}</button>` }]
|
|
2277
2282
|
});
|
|
2278
2283
|
}
|
|
2279
2284
|
/************************************************************
|
|
@@ -2286,7 +2291,7 @@ function display_error_message(title, msg, callback, without_buttons) {
|
|
|
2286
2291
|
msg,
|
|
2287
2292
|
callback,
|
|
2288
2293
|
type: "error",
|
|
2289
|
-
buttons: without_buttons ? [] : [{ content: `<button class="button is-
|
|
2294
|
+
buttons: without_buttons ? [] : [{ content: `<button class="button is-danger px-6 with-focus">${t("accept")}</button>` }]
|
|
2290
2295
|
});
|
|
2291
2296
|
}
|
|
2292
2297
|
/************************************************************
|
|
@@ -2294,7 +2299,7 @@ function display_error_message(title, msg, callback, without_buttons) {
|
|
|
2294
2299
|
************************************************************/
|
|
2295
2300
|
function get_yesnocancel(msg, callback) {
|
|
2296
2301
|
let content = `<div class="">
|
|
2297
|
-
<p class="
|
|
2302
|
+
<p class="yui-volatil-msg">${t(msg)}</p>
|
|
2298
2303
|
<div class="buttons is-centered">
|
|
2299
2304
|
<button class="button to-close is-success px-6 with-focus yesButton">${t("yes")}</button>
|
|
2300
2305
|
<button class="button to-close is-danger px-6 noButton">${t("no")}</button>
|
|
@@ -2310,7 +2315,7 @@ function get_yesnocancel(msg, callback) {
|
|
|
2310
2315
|
title: null,
|
|
2311
2316
|
msg: content,
|
|
2312
2317
|
callback: callback_,
|
|
2313
|
-
type: "
|
|
2318
|
+
type: "question"
|
|
2314
2319
|
});
|
|
2315
2320
|
}
|
|
2316
2321
|
/************************************************************
|
|
@@ -2318,7 +2323,7 @@ function get_yesnocancel(msg, callback) {
|
|
|
2318
2323
|
************************************************************/
|
|
2319
2324
|
function get_yesno(msg, callback) {
|
|
2320
2325
|
let content = `<div class="">
|
|
2321
|
-
<p class="
|
|
2326
|
+
<p class="yui-volatil-msg">${msg}</p>
|
|
2322
2327
|
<div class="buttons is-centered">
|
|
2323
2328
|
<button class="button to-close is-success px-6 with-focus yesButton">${t("yes")}</button>
|
|
2324
2329
|
<button class="button to-close is-danger px-6 noButton">${t("no")}</button>
|
|
@@ -2333,7 +2338,7 @@ function get_yesno(msg, callback) {
|
|
|
2333
2338
|
title: null,
|
|
2334
2339
|
msg: content,
|
|
2335
2340
|
callback: callback_,
|
|
2336
|
-
type: "
|
|
2341
|
+
type: "question"
|
|
2337
2342
|
});
|
|
2338
2343
|
}
|
|
2339
2344
|
/************************************************************
|
|
@@ -2341,7 +2346,7 @@ function get_yesno(msg, callback) {
|
|
|
2341
2346
|
************************************************************/
|
|
2342
2347
|
function get_ok(msg, callback) {
|
|
2343
2348
|
let content = `<div class="">
|
|
2344
|
-
<p class="
|
|
2349
|
+
<p class="yui-volatil-msg">${msg}</p>
|
|
2345
2350
|
<div class="buttons is-centered">
|
|
2346
2351
|
<button class="button to-close is-success px-6 with-focus yesButton">${t("accept")}</button>
|
|
2347
2352
|
</div>
|
|
@@ -2355,7 +2360,7 @@ function get_ok(msg, callback) {
|
|
|
2355
2360
|
title: null,
|
|
2356
2361
|
msg: content,
|
|
2357
2362
|
callback: callback_,
|
|
2358
|
-
type: "
|
|
2363
|
+
type: "success"
|
|
2359
2364
|
});
|
|
2360
2365
|
}
|
|
2361
2366
|
/************************************************************
|
|
@@ -5378,7 +5383,7 @@ function register_c_yui_wizard() {
|
|
|
5378
5383
|
*
|
|
5379
5384
|
* Form (in Window or not)
|
|
5380
5385
|
*
|
|
5381
|
-
* Copyright (c) 2024-
|
|
5386
|
+
* Copyright (c) 2024-2026, ArtGins.
|
|
5382
5387
|
* All Rights Reserved.
|
|
5383
5388
|
***********************************************************************/
|
|
5384
5389
|
/***************************************************************
|
|
@@ -5993,6 +5998,17 @@ function apply_form_mode(gobj, $form) {
|
|
|
5993
5998
|
}
|
|
5994
5999
|
}
|
|
5995
6000
|
/******************************************************************
|
|
6001
|
+
* True when the app renders dark: explicit <html data-theme>, or
|
|
6002
|
+
* the OS scheme when the attribute is absent ("system" theme
|
|
6003
|
+
* removes it — see themes.js). Read at field-build time; the
|
|
6004
|
+
* hosting dialog rebuilds the form on every open.
|
|
6005
|
+
******************************************************************/
|
|
6006
|
+
function form_is_dark_theme() {
|
|
6007
|
+
let attr = document.documentElement.getAttribute("data-theme");
|
|
6008
|
+
if (attr) return attr === "dark";
|
|
6009
|
+
return typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
6010
|
+
}
|
|
6011
|
+
/******************************************************************
|
|
5996
6012
|
* With html field conf builds an html element,
|
|
5997
6013
|
* ready to add to the form
|
|
5998
6014
|
*
|
|
@@ -6433,7 +6449,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6433
6449
|
break;
|
|
6434
6450
|
case "jsoneditor": {
|
|
6435
6451
|
let attrs = {
|
|
6436
|
-
class: "jsoneditor jse-theme-dark",
|
|
6452
|
+
class: "jsoneditor" + (form_is_dark_theme() ? " jse-theme-dark" : ""),
|
|
6437
6453
|
name,
|
|
6438
6454
|
style: ""
|
|
6439
6455
|
};
|
|
@@ -15397,7 +15413,7 @@ function register_c_yui_treedb_topics() {
|
|
|
15397
15413
|
*
|
|
15398
15414
|
* Table of TreeDB topic with FORM for editing
|
|
15399
15415
|
*
|
|
15400
|
-
* Copyright (c) 2025, ArtGins.
|
|
15416
|
+
* Copyright (c) 2025-2026, ArtGins.
|
|
15401
15417
|
* All Rights Reserved.
|
|
15402
15418
|
***********************************************************************/
|
|
15403
15419
|
/***************************************************************
|
|
@@ -15452,7 +15468,8 @@ var PRIVATE_DATA$2 = {
|
|
|
15452
15468
|
topic_name: "",
|
|
15453
15469
|
tabulator: null,
|
|
15454
15470
|
form: null,
|
|
15455
|
-
$popup: null
|
|
15471
|
+
$popup: null,
|
|
15472
|
+
on_keydown: null
|
|
15456
15473
|
};
|
|
15457
15474
|
var __gclass__$2 = null;
|
|
15458
15475
|
function popup_mount_layer() {
|
|
@@ -16021,6 +16038,14 @@ function create_tabulator(gobj) {
|
|
|
16021
16038
|
n = 0;
|
|
16022
16039
|
}
|
|
16023
16040
|
$rc.textContent = `${n} ${t("rows")}`;
|
|
16041
|
+
let single_page = true;
|
|
16042
|
+
try {
|
|
16043
|
+
let max = tabulator.getPageMax();
|
|
16044
|
+
single_page = max === false || max <= 1;
|
|
16045
|
+
} catch (e) {
|
|
16046
|
+
single_page = true;
|
|
16047
|
+
}
|
|
16048
|
+
tabulator.element.classList.toggle("yui-single-page", single_page);
|
|
16024
16049
|
}
|
|
16025
16050
|
tabulator._ready = false;
|
|
16026
16051
|
tabulator.on("tableBuilt", function() {
|
|
@@ -16196,6 +16221,13 @@ function open_form_dialog(gobj, mode, record) {
|
|
|
16196
16221
|
let desc = gobj_read_attr(gobj, "desc");
|
|
16197
16222
|
let popup_id = gobj_read_str_attr(gobj, "popup_id");
|
|
16198
16223
|
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
16224
|
+
let pkey = desc.pkey || "id";
|
|
16225
|
+
let title;
|
|
16226
|
+
if (mode === "create") title = t("new") + " " + t(topic_name);
|
|
16227
|
+
else {
|
|
16228
|
+
let rid = record ? String(record[pkey] ?? "") : "";
|
|
16229
|
+
title = t(topic_name) + (empty_string(rid) ? "" : " — " + rid);
|
|
16230
|
+
}
|
|
16199
16231
|
let $element = createElement2([
|
|
16200
16232
|
"div",
|
|
16201
16233
|
{
|
|
@@ -16218,7 +16250,7 @@ function open_form_dialog(gobj, mode, record) {
|
|
|
16218
16250
|
class: "modal-card-title",
|
|
16219
16251
|
style: "text-align:center;"
|
|
16220
16252
|
},
|
|
16221
|
-
|
|
16253
|
+
title
|
|
16222
16254
|
], [
|
|
16223
16255
|
"button",
|
|
16224
16256
|
{
|
|
@@ -16264,8 +16296,16 @@ function open_form_dialog(gobj, mode, record) {
|
|
|
16264
16296
|
}
|
|
16265
16297
|
popup_mount_layer().appendChild($element);
|
|
16266
16298
|
refresh_language($element, t);
|
|
16299
|
+
priv.on_keydown = function(evt) {
|
|
16300
|
+
if (evt.key === "Escape") {
|
|
16301
|
+
evt.stopPropagation();
|
|
16302
|
+
request_close_form_dialog(gobj);
|
|
16303
|
+
}
|
|
16304
|
+
};
|
|
16305
|
+
document.addEventListener("keydown", priv.on_keydown);
|
|
16267
16306
|
gobj_start(form);
|
|
16268
|
-
let $with_focus = $element.querySelector(".with-focus");
|
|
16307
|
+
let $with_focus = $element.querySelector(".with-focus:not([readonly])");
|
|
16308
|
+
if (!$with_focus) $with_focus = $element.querySelector("input.input:not([readonly]), textarea, select");
|
|
16269
16309
|
if ($with_focus) $with_focus.focus();
|
|
16270
16310
|
}
|
|
16271
16311
|
/************************************************************
|
|
@@ -16291,6 +16331,10 @@ function request_close_form_dialog(gobj) {
|
|
|
16291
16331
|
************************************************************/
|
|
16292
16332
|
function close_form_dialog(gobj) {
|
|
16293
16333
|
let priv = gobj.priv;
|
|
16334
|
+
if (priv.on_keydown) {
|
|
16335
|
+
document.removeEventListener("keydown", priv.on_keydown);
|
|
16336
|
+
priv.on_keydown = null;
|
|
16337
|
+
}
|
|
16294
16338
|
if (priv.form) {
|
|
16295
16339
|
if (gobj_is_running(priv.form)) gobj_stop(priv.form);
|
|
16296
16340
|
gobj_destroy(priv.form);
|
package/package.json
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* c_yui_form.css
|
|
3
|
+
*
|
|
4
|
+
* Theme integration for the third-party widgets C_YUI_FORM hosts
|
|
5
|
+
* (tom-select, vanilla-jsoneditor) plus form-owned input states.
|
|
6
|
+
*
|
|
7
|
+
* Everything maps to Bulma CSS vars, so a single rule set follows
|
|
8
|
+
* light/dark automatically (explicit data-theme or system theme) —
|
|
9
|
+
* no per-theme selectors needed.
|
|
10
|
+
*
|
|
11
|
+
* Copyright (c) 2026, ArtGins.
|
|
12
|
+
* All Rights Reserved.
|
|
13
|
+
***********************************************************************/
|
|
14
|
+
|
|
15
|
+
/*--- tom-select (fkey fields): ships light-only colors ---*/
|
|
16
|
+
.C_YUI_FORM .ts-wrapper .ts-control {
|
|
17
|
+
background: var(--bulma-scheme-main, #fff);
|
|
18
|
+
color: var(--bulma-text, #4a4a4a);
|
|
19
|
+
border-color: var(--bulma-border, #dbdbdb);
|
|
20
|
+
}
|
|
21
|
+
.C_YUI_FORM .ts-control > input {
|
|
22
|
+
color: var(--bulma-text, #4a4a4a);
|
|
23
|
+
}
|
|
24
|
+
.C_YUI_FORM .ts-wrapper.multi .ts-control > div {
|
|
25
|
+
background: var(--bulma-background, #f2f2f2);
|
|
26
|
+
color: var(--bulma-text, #4a4a4a);
|
|
27
|
+
}
|
|
28
|
+
.C_YUI_FORM .ts-dropdown {
|
|
29
|
+
background: var(--bulma-scheme-main, #fff);
|
|
30
|
+
color: var(--bulma-text, #4a4a4a);
|
|
31
|
+
border-color: var(--bulma-border, #dbdbdb);
|
|
32
|
+
}
|
|
33
|
+
.C_YUI_FORM .ts-dropdown .option.active {
|
|
34
|
+
background: var(--bulma-link, #485fc7);
|
|
35
|
+
color: var(--bulma-link-invert, #fff);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/*--- vanilla-jsoneditor: align its accent with the app palette ---*/
|
|
39
|
+
.C_YUI_FORM .jsoneditor {
|
|
40
|
+
--jse-theme-color: var(--bulma-link, #485fc7);
|
|
41
|
+
--jse-theme-color-highlight: var(--bulma-link, #485fc7);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/*--- readonly fields (e.g. the pkey in update mode) look readonly ---*/
|
|
45
|
+
.C_YUI_FORM input.input[readonly] {
|
|
46
|
+
background-color: var(--bulma-background, #f5f5f5);
|
|
47
|
+
color: var(--bulma-text-weak, #7a7a7a);
|
|
48
|
+
border-color: var(--bulma-border, #dbdbdb);
|
|
49
|
+
cursor: default;
|
|
50
|
+
}
|
package/src/c_yui_form.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Form (in Window or not)
|
|
5
5
|
*
|
|
6
|
-
* Copyright (c) 2024-
|
|
6
|
+
* Copyright (c) 2024-2026, ArtGins.
|
|
7
7
|
* All Rights Reserved.
|
|
8
8
|
***********************************************************************/
|
|
9
9
|
import {
|
|
@@ -55,6 +55,7 @@ import TomSelect from "tom-select"; // Import Tom-Select JS
|
|
|
55
55
|
|
|
56
56
|
import { createJSONEditor } from 'vanilla-jsoneditor';
|
|
57
57
|
import "vanilla-jsoneditor/themes/jse-theme-dark.css";
|
|
58
|
+
import "./c_yui_form.css";
|
|
58
59
|
|
|
59
60
|
import "tabulator-tables/dist/css/tabulator.min.css"; // Import Tabulator CSS
|
|
60
61
|
import "tabulator-tables/dist/css/tabulator_bulma.css";
|
|
@@ -835,6 +836,23 @@ function apply_form_mode(gobj, $form)
|
|
|
835
836
|
}
|
|
836
837
|
}
|
|
837
838
|
|
|
839
|
+
/******************************************************************
|
|
840
|
+
* True when the app renders dark: explicit <html data-theme>, or
|
|
841
|
+
* the OS scheme when the attribute is absent ("system" theme
|
|
842
|
+
* removes it — see themes.js). Read at field-build time; the
|
|
843
|
+
* hosting dialog rebuilds the form on every open.
|
|
844
|
+
******************************************************************/
|
|
845
|
+
function form_is_dark_theme()
|
|
846
|
+
{
|
|
847
|
+
let attr = document.documentElement.getAttribute("data-theme");
|
|
848
|
+
if(attr) {
|
|
849
|
+
return attr === "dark";
|
|
850
|
+
}
|
|
851
|
+
return typeof window !== "undefined" &&
|
|
852
|
+
window.matchMedia &&
|
|
853
|
+
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
854
|
+
}
|
|
855
|
+
|
|
838
856
|
/******************************************************************
|
|
839
857
|
* With html field conf builds an html element,
|
|
840
858
|
* ready to add to the form
|
|
@@ -1236,7 +1254,7 @@ function create_form_field(
|
|
|
1236
1254
|
case "jsoneditor":
|
|
1237
1255
|
{
|
|
1238
1256
|
let attrs = {
|
|
1239
|
-
class: 'jsoneditor jse-theme-dark',
|
|
1257
|
+
class: 'jsoneditor' + (form_is_dark_theme() ? ' jse-theme-dark' : ''),
|
|
1240
1258
|
name: name,
|
|
1241
1259
|
style: ''
|
|
1242
1260
|
};
|
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.css
CHANGED
|
@@ -141,16 +141,24 @@
|
|
|
141
141
|
.yui-nav-vertical .yui-nav-label { white-space: nowrap; }
|
|
142
142
|
|
|
143
143
|
/*--- icon-bar (bottom/top icon bar) ---*/
|
|
144
|
+
/* Distribute-or-scroll: items grow to share spare width but never
|
|
145
|
+
* shrink below their content (flex: 1 0 auto); when they don't fit the
|
|
146
|
+
* bar scrolls horizontally. justify-content must stay flex-start —
|
|
147
|
+
* with space-around + overflow the leading items get clipped past the
|
|
148
|
+
* left edge and become unreachable. */
|
|
144
149
|
.yui-nav-iconbar {
|
|
145
150
|
display: flex;
|
|
146
|
-
justify-content:
|
|
151
|
+
justify-content: flex-start;
|
|
147
152
|
align-items: stretch;
|
|
148
153
|
margin: 0;
|
|
149
154
|
padding: 0.25rem 0;
|
|
150
155
|
width: 100%;
|
|
151
156
|
background: var(--bulma-scheme-main, #fff);
|
|
157
|
+
overflow-x: auto;
|
|
158
|
+
scrollbar-width: thin;
|
|
152
159
|
}
|
|
153
|
-
.yui-nav-iconbar .level-item { flex: 1
|
|
160
|
+
.yui-nav-iconbar .level-item { flex: 1 0 auto; justify-content: center; }
|
|
161
|
+
.yui-nav-iconbar .yui-nav-label { white-space: nowrap; }
|
|
154
162
|
.yui-nav-iconbar .yui-nav-item {
|
|
155
163
|
display: flex;
|
|
156
164
|
flex-direction: column;
|
|
@@ -22,3 +22,26 @@
|
|
|
22
22
|
.tabulator .tabulator-footer .yui-tabulator-rowcount {
|
|
23
23
|
line-height: 1.5;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/* All rows fit in one page: the page-size selector and the
|
|
27
|
+
* First/Prev/1/Next/Last strip are pure noise (worst on mobile) —
|
|
28
|
+
* update_rowcount() toggles the class on every data change. */
|
|
29
|
+
.tabulator.yui-single-page .tabulator-footer .tabulator-paginator {
|
|
30
|
+
display: none;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Toolbar wrapping discipline (mobile): the record-buttons group
|
|
34
|
+
* must never split across lines — Bulma .buttons wraps internally,
|
|
35
|
+
* which stacked the toolbar into 3 rows on narrow phones. Keep the
|
|
36
|
+
* group on one line and scroll it horizontally if it overflows
|
|
37
|
+
* (same distribute-or-scroll pattern as the nav icon-bar); the
|
|
38
|
+
* search+refresh block then wraps as a whole, so the toolbar is
|
|
39
|
+
* 1 row when it fits and exactly 2 when it doesn't. */
|
|
40
|
+
.toolbar_tabulator_table .buttons {
|
|
41
|
+
flex-wrap: nowrap;
|
|
42
|
+
overflow-x: auto;
|
|
43
|
+
scrollbar-width: thin;
|
|
44
|
+
}
|
|
45
|
+
.toolbar_tabulator_table .control.has-icons-left {
|
|
46
|
+
min-width: 0;
|
|
47
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Table of TreeDB topic with FORM for editing
|
|
5
5
|
*
|
|
6
|
-
* Copyright (c) 2025, ArtGins.
|
|
6
|
+
* Copyright (c) 2025-2026, ArtGins.
|
|
7
7
|
* All Rights Reserved.
|
|
8
8
|
***********************************************************************/
|
|
9
9
|
|
|
@@ -132,6 +132,7 @@ let PRIVATE_DATA = {
|
|
|
132
132
|
tabulator: null,
|
|
133
133
|
form: null, // hosted C_YUI_FORM child (while dialog open)
|
|
134
134
|
$popup: null, // dialog DOM element
|
|
135
|
+
on_keydown: null, // document Escape handler (while dialog open)
|
|
135
136
|
};
|
|
136
137
|
|
|
137
138
|
let __gclass__ = null;
|
|
@@ -827,7 +828,9 @@ function create_tabulator(gobj)
|
|
|
827
828
|
}
|
|
828
829
|
let tabulator = new Tabulator($table_el, tabulator_settings);
|
|
829
830
|
|
|
830
|
-
/* Keep the footer in sync with the visible (active) row count
|
|
831
|
+
/* Keep the footer in sync with the visible (active) row count,
|
|
832
|
+
* and hide the pagination chrome while everything fits in one
|
|
833
|
+
* page (it comes back as soon as a second page exists). */
|
|
831
834
|
function update_rowcount() {
|
|
832
835
|
let $rc = tabulator.element &&
|
|
833
836
|
tabulator.element.querySelector(".yui-tabulator-rowcount");
|
|
@@ -841,6 +844,15 @@ function create_tabulator(gobj)
|
|
|
841
844
|
n = 0;
|
|
842
845
|
}
|
|
843
846
|
$rc.textContent = `${n} ${t("rows")}`;
|
|
847
|
+
|
|
848
|
+
let single_page = true;
|
|
849
|
+
try {
|
|
850
|
+
let max = tabulator.getPageMax(); // false = pagination off
|
|
851
|
+
single_page = (max === false) || max <= 1;
|
|
852
|
+
} catch(e) {
|
|
853
|
+
single_page = true;
|
|
854
|
+
}
|
|
855
|
+
tabulator.element.classList.toggle("yui-single-page", single_page);
|
|
844
856
|
}
|
|
845
857
|
|
|
846
858
|
tabulator._ready = false;
|
|
@@ -1103,12 +1115,23 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1103
1115
|
let popup_id = gobj_read_str_attr(gobj, "popup_id");
|
|
1104
1116
|
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
1105
1117
|
|
|
1118
|
+
/* Title says what you are doing: "new <topic>" on create,
|
|
1119
|
+
* "<topic> — <pkey>" on update. */
|
|
1120
|
+
let pkey = desc.pkey || "id";
|
|
1121
|
+
let title;
|
|
1122
|
+
if(mode === "create") {
|
|
1123
|
+
title = t("new") + " " + t(topic_name);
|
|
1124
|
+
} else {
|
|
1125
|
+
let rid = record ? String(record[pkey] ?? "") : "";
|
|
1126
|
+
title = t(topic_name) + (empty_string(rid) ? "" : " — " + rid);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1106
1129
|
let $element = createElement2([
|
|
1107
1130
|
'div', {id: popup_id, class: 'modal is-active'}, [
|
|
1108
1131
|
['div', {class: 'modal-background'}, ''],
|
|
1109
1132
|
['div', {class: 'modal-card modal-is-responsive'}, [
|
|
1110
1133
|
['header', {class: 'modal-card-head p-3'}, [
|
|
1111
|
-
['p', {class: 'modal-card-title', style: 'text-align:center;'},
|
|
1134
|
+
['p', {class: 'modal-card-title', style: 'text-align:center;'}, title],
|
|
1112
1135
|
['button', {class: 'delete is-large', 'aria-label': 'close'}, '', {
|
|
1113
1136
|
click: function(evt) {
|
|
1114
1137
|
evt.stopPropagation();
|
|
@@ -1161,9 +1184,25 @@ function open_form_dialog(gobj, mode, record)
|
|
|
1161
1184
|
popup_mount_layer().appendChild($element);
|
|
1162
1185
|
refresh_language($element, t);
|
|
1163
1186
|
|
|
1187
|
+
/* Escape closes through the same unsaved-changes guard as the X. */
|
|
1188
|
+
priv.on_keydown = function(evt) {
|
|
1189
|
+
if(evt.key === "Escape") {
|
|
1190
|
+
evt.stopPropagation();
|
|
1191
|
+
request_close_form_dialog(gobj);
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
document.addEventListener("keydown", priv.on_keydown);
|
|
1195
|
+
|
|
1164
1196
|
gobj_start(form); // mt_start loads `record` into the form
|
|
1165
1197
|
|
|
1166
|
-
|
|
1198
|
+
/* Focus the pkey unless the mode made it readonly (update) —
|
|
1199
|
+
* then the first editable field. */
|
|
1200
|
+
let $with_focus = $element.querySelector('.with-focus:not([readonly])');
|
|
1201
|
+
if(!$with_focus) {
|
|
1202
|
+
$with_focus = $element.querySelector(
|
|
1203
|
+
'input.input:not([readonly]), textarea, select'
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1167
1206
|
if($with_focus) {
|
|
1168
1207
|
$with_focus.focus();
|
|
1169
1208
|
}
|
|
@@ -1197,6 +1236,10 @@ function request_close_form_dialog(gobj)
|
|
|
1197
1236
|
function close_form_dialog(gobj)
|
|
1198
1237
|
{
|
|
1199
1238
|
let priv = gobj.priv;
|
|
1239
|
+
if(priv.on_keydown) {
|
|
1240
|
+
document.removeEventListener("keydown", priv.on_keydown);
|
|
1241
|
+
priv.on_keydown = null;
|
|
1242
|
+
}
|
|
1200
1243
|
if(priv.form) {
|
|
1201
1244
|
if(gobj_is_running(priv.form)) {
|
|
1202
1245
|
gobj_stop(priv.form);
|
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
|
+
}
|