@yuneta/gobj-ui 5.8.0 → 5.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/gobj-ui.cjs.js +16 -6
- package/dist/gobj-ui.es.js +16 -6
- package/package.json +2 -2
- package/src/c_yui_shell.js +22 -0
- package/src/c_yui_treedb_topic_with_form.css +25 -0
- package/src/c_yui_treedb_topic_with_form.js +19 -6
package/README.md
CHANGED
|
@@ -520,6 +520,43 @@ keeps working afterwards. With no guard a modal closes exactly as it always
|
|
|
520
520
|
did, and the returned `close()` always closes **unconditionally** — the veto is
|
|
521
521
|
for the user's dismiss, not for the code's.
|
|
522
522
|
|
|
523
|
+
### Confirmations — and the red one, `yui_shell_confirm_danger`
|
|
524
|
+
|
|
525
|
+
`yui_shell_confirm_yesno(shell, message, opts)` asks a question and resolves to
|
|
526
|
+
a boolean. Its yes is `is-link`, the right colour for *"do you want to
|
|
527
|
+
continue"*.
|
|
528
|
+
|
|
529
|
+
**`yui_shell_confirm_danger(shell, message, opts)`** is the same call with a
|
|
530
|
+
**red** confirm button and the error icon (`type: "danger"` by default). Use it
|
|
531
|
+
whenever the yes destroys something — deleting an account, dropping a record.
|
|
532
|
+
The two must not look alike: the destructive one is precisely the one that must
|
|
533
|
+
not be clicked by reflex.
|
|
534
|
+
|
|
535
|
+
In both, the **safe answer is the last button**, so Escape, the backdrop and
|
|
536
|
+
the X all resolve to it.
|
|
537
|
+
|
|
538
|
+
```js
|
|
539
|
+
if(await yui_shell_confirm_danger(shell, t("delete account detail"))) {
|
|
540
|
+
/* only here has the red button been pressed */
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### `C_YUI_FORM` — choosing the bottom toolbar
|
|
545
|
+
|
|
546
|
+
By default the form shows **save + undo + clear + copy + paste**. The
|
|
547
|
+
`toolbar` attr takes the button names you want, in the order you want them:
|
|
548
|
+
|
|
549
|
+
```js
|
|
550
|
+
gobj_create("form", "C_YUI_FORM", {toolbar: ["save"]}, parent); // one action
|
|
551
|
+
gobj_create("form", "C_YUI_FORM", {toolbar: []}, parent); // no toolbar
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Save/undo/clear stay on the left of the bar and copy/paste on the right — the
|
|
555
|
+
split the layout has always drawn — so dropping a whole group leaves no hole in
|
|
556
|
+
the middle, and **a toolbar left with a single group is centred**. An unknown
|
|
557
|
+
name is reported, not silently dropped: a typo would otherwise remove the save
|
|
558
|
+
button with no trace of why.
|
|
559
|
+
|
|
523
560
|
## Conventions
|
|
524
561
|
|
|
525
562
|
### i18n: a string must be able to CHANGE language, not just be translated once
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -2003,6 +2003,16 @@ function open_toolbar_dropdown(gobj, item, action, $trigger) {
|
|
|
2003
2003
|
} else style_parts.push(`left:${Math.max(0, Math.round(rect.left))}px`);
|
|
2004
2004
|
$panel.setAttribute("style", style_parts.join(";"));
|
|
2005
2005
|
priv.layers.popup.appendChild($panel);
|
|
2006
|
+
let margin = 8;
|
|
2007
|
+
let pw = $panel.offsetWidth;
|
|
2008
|
+
let pos = $panel.getBoundingClientRect();
|
|
2009
|
+
let left = pos.left;
|
|
2010
|
+
if (left + pw > window.innerWidth - margin) left = window.innerWidth - pw - margin;
|
|
2011
|
+
if (left < margin) left = margin;
|
|
2012
|
+
if (Math.round(left) !== Math.round(pos.left)) {
|
|
2013
|
+
$panel.style.right = "auto";
|
|
2014
|
+
$panel.style.left = `${Math.round(left)}px`;
|
|
2015
|
+
}
|
|
2006
2016
|
yui_shell_translate(gobj, $panel);
|
|
2007
2017
|
let backdrop = (ev) => {
|
|
2008
2018
|
if ($panel.contains(ev.target)) return;
|
|
@@ -16198,7 +16208,7 @@ function build_ui$4(gobj) {
|
|
|
16198
16208
|
if (with_edition_mode) {
|
|
16199
16209
|
$table_toolbar = (0, _yuneta_gobj_js.createElement2)(["div", {
|
|
16200
16210
|
id: `${toolbar_id}`,
|
|
16201
|
-
class: "buttons mb-0"
|
|
16211
|
+
class: "TREEDB_TABLE_ACTIONS buttons mb-0"
|
|
16202
16212
|
}]);
|
|
16203
16213
|
let $edit_button = (0, _yuneta_gobj_js.createElement2)([
|
|
16204
16214
|
"button",
|
|
@@ -16322,19 +16332,19 @@ function build_ui$4(gobj) {
|
|
|
16322
16332
|
}
|
|
16323
16333
|
let $table_toolbar = create_table_toolbar();
|
|
16324
16334
|
let toolbar_id = (0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "toolbar_id");
|
|
16325
|
-
let $view_toolbar = (0, _yuneta_gobj_js.createElement2)(["div", { class: "is-flex is-align-items-center" }]);
|
|
16335
|
+
let $view_toolbar = (0, _yuneta_gobj_js.createElement2)(["div", { class: "TREEDB_TABLE_TOOLS is-flex is-align-items-center" }]);
|
|
16326
16336
|
let with_refresh_button = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_refresh_button");
|
|
16327
16337
|
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_search_button")) {
|
|
16328
16338
|
let search_id = `${toolbar_id}_search`;
|
|
16329
16339
|
let $search_box = (0, _yuneta_gobj_js.createElement2)([
|
|
16330
16340
|
"div",
|
|
16331
|
-
{ class: "control has-icons-left mr-1" },
|
|
16341
|
+
{ class: "TREEDB_TABLE_SEARCH control has-icons-left mr-1" },
|
|
16332
16342
|
[["input", {
|
|
16333
16343
|
id: search_id,
|
|
16334
16344
|
class: "input",
|
|
16335
16345
|
type: "text",
|
|
16336
|
-
placeholder: "search
|
|
16337
|
-
|
|
16346
|
+
placeholder: (0, i18next.t)("search"),
|
|
16347
|
+
"data-i18n-placeholder": "search"
|
|
16338
16348
|
}], [
|
|
16339
16349
|
"span",
|
|
16340
16350
|
{ class: "icon is-left" },
|
|
@@ -16389,7 +16399,7 @@ function build_ui$4(gobj) {
|
|
|
16389
16399
|
style: "height:100%;"
|
|
16390
16400
|
},
|
|
16391
16401
|
[["div", {
|
|
16392
|
-
class: "toolbar_tabulator_table m-1",
|
|
16402
|
+
class: "TREEDB_TABLE_TOOLBAR toolbar_tabulator_table m-1",
|
|
16393
16403
|
style: "display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap;"
|
|
16394
16404
|
}], ["div", {
|
|
16395
16405
|
id: `${(0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "table_id")}`,
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -1998,6 +1998,16 @@ function open_toolbar_dropdown(gobj, item, action, $trigger) {
|
|
|
1998
1998
|
} else style_parts.push(`left:${Math.max(0, Math.round(rect.left))}px`);
|
|
1999
1999
|
$panel.setAttribute("style", style_parts.join(";"));
|
|
2000
2000
|
priv.layers.popup.appendChild($panel);
|
|
2001
|
+
let margin = 8;
|
|
2002
|
+
let pw = $panel.offsetWidth;
|
|
2003
|
+
let pos = $panel.getBoundingClientRect();
|
|
2004
|
+
let left = pos.left;
|
|
2005
|
+
if (left + pw > window.innerWidth - margin) left = window.innerWidth - pw - margin;
|
|
2006
|
+
if (left < margin) left = margin;
|
|
2007
|
+
if (Math.round(left) !== Math.round(pos.left)) {
|
|
2008
|
+
$panel.style.right = "auto";
|
|
2009
|
+
$panel.style.left = `${Math.round(left)}px`;
|
|
2010
|
+
}
|
|
2001
2011
|
yui_shell_translate(gobj, $panel);
|
|
2002
2012
|
let backdrop = (ev) => {
|
|
2003
2013
|
if ($panel.contains(ev.target)) return;
|
|
@@ -16192,7 +16202,7 @@ function build_ui$4(gobj) {
|
|
|
16192
16202
|
if (with_edition_mode) {
|
|
16193
16203
|
$table_toolbar = createElement2(["div", {
|
|
16194
16204
|
id: `${toolbar_id}`,
|
|
16195
|
-
class: "buttons mb-0"
|
|
16205
|
+
class: "TREEDB_TABLE_ACTIONS buttons mb-0"
|
|
16196
16206
|
}]);
|
|
16197
16207
|
let $edit_button = createElement2([
|
|
16198
16208
|
"button",
|
|
@@ -16316,19 +16326,19 @@ function build_ui$4(gobj) {
|
|
|
16316
16326
|
}
|
|
16317
16327
|
let $table_toolbar = create_table_toolbar();
|
|
16318
16328
|
let toolbar_id = gobj_read_str_attr(gobj, "toolbar_id");
|
|
16319
|
-
let $view_toolbar = createElement2(["div", { class: "is-flex is-align-items-center" }]);
|
|
16329
|
+
let $view_toolbar = createElement2(["div", { class: "TREEDB_TABLE_TOOLS is-flex is-align-items-center" }]);
|
|
16320
16330
|
let with_refresh_button = gobj_read_bool_attr(gobj, "with_refresh_button");
|
|
16321
16331
|
if (gobj_read_bool_attr(gobj, "with_search_button")) {
|
|
16322
16332
|
let search_id = `${toolbar_id}_search`;
|
|
16323
16333
|
let $search_box = createElement2([
|
|
16324
16334
|
"div",
|
|
16325
|
-
{ class: "control has-icons-left mr-1" },
|
|
16335
|
+
{ class: "TREEDB_TABLE_SEARCH control has-icons-left mr-1" },
|
|
16326
16336
|
[["input", {
|
|
16327
16337
|
id: search_id,
|
|
16328
16338
|
class: "input",
|
|
16329
16339
|
type: "text",
|
|
16330
|
-
placeholder: "search
|
|
16331
|
-
|
|
16340
|
+
placeholder: t("search"),
|
|
16341
|
+
"data-i18n-placeholder": "search"
|
|
16332
16342
|
}], [
|
|
16333
16343
|
"span",
|
|
16334
16344
|
{ class: "icon is-left" },
|
|
@@ -16383,7 +16393,7 @@ function build_ui$4(gobj) {
|
|
|
16383
16393
|
style: "height:100%;"
|
|
16384
16394
|
},
|
|
16385
16395
|
[["div", {
|
|
16386
|
-
class: "toolbar_tabulator_table m-1",
|
|
16396
|
+
class: "TREEDB_TABLE_TOOLBAR toolbar_tabulator_table m-1",
|
|
16387
16397
|
style: "display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap;"
|
|
16388
16398
|
}], ["div", {
|
|
16389
16399
|
id: `${gobj_read_str_attr(gobj, "table_id")}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuneta/gobj-ui",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/gobj-ui.cjs.js",
|
|
6
6
|
"module": "dist/gobj-ui.es.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"src/",
|
|
21
21
|
"vite-plugin-yuneta-html.js"
|
|
22
22
|
],
|
|
23
|
-
"description": "Yuneta UI Library
|
|
23
|
+
"description": "Yuneta UI Library \u2014 the active line: declarative shell (C_YUI_SHELL+NAV+PAGER+WIZARD) + windows, TreeDB views, forms, charts and maps. Consumed by wattyzer and the yunetas JS yunos (gui_agent, gui_treedb). The frozen legacy GClass GUI stack (C_YUI_MAIN/TABS/ROUTING) lives on the v1 branch, npm dist-tag 'legacy' (consumed by estadodelaire and hidraulia).",
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "vite build",
|
|
26
26
|
"test": "vitest run",
|
package/src/c_yui_shell.js
CHANGED
|
@@ -2099,6 +2099,28 @@ function open_toolbar_dropdown(gobj, item, action, $trigger)
|
|
|
2099
2099
|
|
|
2100
2100
|
priv.layers.popup.appendChild($panel);
|
|
2101
2101
|
|
|
2102
|
+
/* Now that it HAS a width, keep it inside the viewport on both
|
|
2103
|
+
* edges. The anchoring above guards only the edge it aligns to,
|
|
2104
|
+
* so a right-aligned panel whose trigger sits near the LEFT of the
|
|
2105
|
+
* bar hangs off the left side — which is what every navbar-end
|
|
2106
|
+
* trigger does under dir="rtl": in Arabic the language menu opened
|
|
2107
|
+
* at left:-60px with its first characters unreachable. Measure,
|
|
2108
|
+
* clamp, and pin with `left` so there is one source of truth. */
|
|
2109
|
+
let margin = 8;
|
|
2110
|
+
let pw = $panel.offsetWidth;
|
|
2111
|
+
let pos = $panel.getBoundingClientRect();
|
|
2112
|
+
let left = pos.left;
|
|
2113
|
+
if(left + pw > window.innerWidth - margin) {
|
|
2114
|
+
left = window.innerWidth - pw - margin;
|
|
2115
|
+
}
|
|
2116
|
+
if(left < margin) {
|
|
2117
|
+
left = margin;
|
|
2118
|
+
}
|
|
2119
|
+
if(Math.round(left) !== Math.round(pos.left)) {
|
|
2120
|
+
$panel.style.right = "auto";
|
|
2121
|
+
$panel.style.left = `${Math.round(left)}px`;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2102
2124
|
/* Translate the lazily-built panel (see the note above). */
|
|
2103
2125
|
yui_shell_translate(gobj, $panel);
|
|
2104
2126
|
|
|
@@ -46,6 +46,31 @@
|
|
|
46
46
|
min-width: 0;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/* Search box width: a rule, not an inline style, so the mobile block
|
|
50
|
+
* below can widen it (an inline max-width would win over any class). */
|
|
51
|
+
.TREEDB_TABLE_TOOLS .TREEDB_TABLE_SEARCH .input {
|
|
52
|
+
max-width: 200px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Mobile: the tools of the list (search + refresh) take a row of their
|
|
56
|
+
* own, with the search stretched across it and the refresh pinned at
|
|
57
|
+
* the end. Without this the group keeps its natural width and leaves
|
|
58
|
+
* dead space, while the search -- the most used control of the two --
|
|
59
|
+
* stays the narrowest thing on screen. The refresh label is already
|
|
60
|
+
* dropped by is-hidden-mobile, decided in the markup: measuring at
|
|
61
|
+
* runtime would change the shape of the bar on a language switch. */
|
|
62
|
+
@media screen and (max-width: 768px) {
|
|
63
|
+
.TREEDB_TABLE_TOOLS {
|
|
64
|
+
width: 100%;
|
|
65
|
+
}
|
|
66
|
+
.TREEDB_TABLE_TOOLS .TREEDB_TABLE_SEARCH {
|
|
67
|
+
flex: 1 1 auto;
|
|
68
|
+
}
|
|
69
|
+
.TREEDB_TABLE_TOOLS .TREEDB_TABLE_SEARCH .input {
|
|
70
|
+
max-width: none;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
49
74
|
/* The edit/create form runs in the shell's standardized adaptive dialog
|
|
50
75
|
* (yui_shell_show_modal dialog:true, logical_class TREEDB_FORM_SHEET). The
|
|
51
76
|
* shell CSS already gives it the centred card / full-screen-mobile sheet;
|
|
@@ -343,8 +343,13 @@ function build_ui(gobj)
|
|
|
343
343
|
|
|
344
344
|
if(with_edition_mode) {
|
|
345
345
|
$table_toolbar = createElement2(
|
|
346
|
-
['div', {id: `${toolbar_id}`, class: 'buttons mb-0'}]
|
|
346
|
+
['div', {id: `${toolbar_id}`, class: 'TREEDB_TABLE_ACTIONS buttons mb-0'}]
|
|
347
347
|
);
|
|
348
|
+
/* Edit is a MODE TOGGLE, not one more action: it arms/disarms
|
|
349
|
+
* the buttons that modify the table (new/delete/copy/paste),
|
|
350
|
+
* which is why it comes first and why delete sits among them
|
|
351
|
+
* instead of being pushed away from the harmless ones. Do not
|
|
352
|
+
* reorder this group as if they were peer actions. */
|
|
348
353
|
let $edit_button = createElement2(
|
|
349
354
|
['button', {id: ``, class: 'button button-edit-record mr-1'}, [
|
|
350
355
|
['i', {class: 'yi-pen'}],
|
|
@@ -463,7 +468,12 @@ function build_ui(gobj)
|
|
|
463
468
|
*----------------------------------------------*/
|
|
464
469
|
let toolbar_id = gobj_read_str_attr(gobj, "toolbar_id");
|
|
465
470
|
|
|
466
|
-
|
|
471
|
+
/* The tools of the list (search, refresh) sit apart from the record
|
|
472
|
+
* actions: actions left, tools right, and on mobile the tools take a
|
|
473
|
+
* row of their own with the search stretched across it. */
|
|
474
|
+
let $view_toolbar = createElement2(
|
|
475
|
+
['div', {class: 'TREEDB_TABLE_TOOLS is-flex is-align-items-center'}]
|
|
476
|
+
);
|
|
467
477
|
|
|
468
478
|
let with_refresh_button = gobj_read_bool_attr(gobj, "with_refresh_button");
|
|
469
479
|
let with_search_button = gobj_read_bool_attr(gobj, "with_search_button");
|
|
@@ -471,13 +481,16 @@ function build_ui(gobj)
|
|
|
471
481
|
if(with_search_button) {
|
|
472
482
|
let search_id = `${toolbar_id}_search`;
|
|
473
483
|
let $search_box = createElement2(
|
|
474
|
-
['div', {class: 'control has-icons-left mr-1'}, [
|
|
484
|
+
['div', {class: 'TREEDB_TABLE_SEARCH control has-icons-left mr-1'}, [
|
|
475
485
|
['input', {
|
|
476
486
|
id: search_id,
|
|
477
487
|
class: 'input',
|
|
478
488
|
type: 'text',
|
|
479
|
-
placeholder
|
|
480
|
-
|
|
489
|
+
/* The placeholder is not a text node, so the
|
|
490
|
+
* data-i18n walk cannot reach it — it needs its own
|
|
491
|
+
* key or it stays English for good. */
|
|
492
|
+
placeholder: t('search'),
|
|
493
|
+
'data-i18n-placeholder': 'search'
|
|
481
494
|
}],
|
|
482
495
|
['span', {class: 'icon is-left'}, [
|
|
483
496
|
['i', {class: 'yi-magnifying-glass'}]
|
|
@@ -536,7 +549,7 @@ function build_ui(gobj)
|
|
|
536
549
|
let table_id = gobj_read_str_attr(gobj, "table_id");
|
|
537
550
|
let $container = createElement2(
|
|
538
551
|
['div', {class: 'C_YUI_TREEDB_TOPIC_WITH_FORM', style: 'height:100%;'}, [
|
|
539
|
-
['div', {class: 'toolbar_tabulator_table m-1', style: 'display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap;'}],
|
|
552
|
+
['div', {class: 'TREEDB_TABLE_TOOLBAR toolbar_tabulator_table m-1', style: 'display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap;'}],
|
|
540
553
|
['div',
|
|
541
554
|
{
|
|
542
555
|
id: `${table_id}`,
|