@yuneta/gobj-ui 7.26.30 → 7.26.31
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 +9 -0
- package/dist/gobj-ui.cjs.js +42 -4
- package/dist/gobj-ui.es.js +42 -4
- package/package.json +1 -1
- package/src/c_yui_form.js +12 -0
- package/src/c_yui_window.js +32 -5
- package/src/window_layer.wiring.test.js +106 -0
- package/src/yui_tabulator_i18n.js +10 -2
- package/src/yui_tabulator_i18n.test.js +80 -0
package/README.md
CHANGED
|
@@ -78,6 +78,15 @@ Published as `@yuneta/gobj-ui`. Built on top of [`@yuneta/gobj-js`](https://gith
|
|
|
78
78
|
> maximize/restore + close. The self-contained **"shade"** fallback (roll up to
|
|
79
79
|
> the title bar in place) and its `is-shaded` CSS are **removed**; an app that
|
|
80
80
|
> relied on shading needs to register a manager.
|
|
81
|
+
>
|
|
82
|
+
> **Where a window lives (7.26.31):** a `C_YUI_WINDOW` whose host names no
|
|
83
|
+
> `$parent` mounts in the **overlay layer of the shell** -- above the content,
|
|
84
|
+
> below the toolbar's dropdowns, the dialogs and the toasts -- and on
|
|
85
|
+
> `document.body` only when there is no shell. It used to be the body always,
|
|
86
|
+
> and since `.yui-shell` is `position: fixed` (a stacking context of its own)
|
|
87
|
+
> a window at z-index 11 covered the WHOLE shell, modal layer included: the
|
|
88
|
+
> confirmation of a window's own close opened under it, out of reach. A
|
|
89
|
+
> `$parent` the host names still wins.
|
|
81
90
|
|
|
82
91
|
## Two maintained lines
|
|
83
92
|
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -4050,7 +4050,7 @@ var MOBILE_MAX = 768;
|
|
|
4050
4050
|
***************************************************************/
|
|
4051
4051
|
var attrs_table$21 = [
|
|
4052
4052
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
|
|
4053
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else to document.body"),
|
|
4053
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else to the overlay layer of the shell (document.body when there is none)"),
|
|
4054
4054
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "x", 0, "300", "X position"),
|
|
4055
4055
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "y", 0, "100", "Y position"),
|
|
4056
4056
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "width", 0, "700", "Width of the window"),
|
|
@@ -4545,8 +4545,8 @@ function build_ui$18(gobj) {
|
|
|
4545
4545
|
}
|
|
4546
4546
|
(0, _yuneta_gobj_js.refresh_language)($container, i18next.t);
|
|
4547
4547
|
let $parent = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$parent");
|
|
4548
|
-
if (
|
|
4549
|
-
|
|
4548
|
+
if (!$parent) $parent = default_parent(gobj);
|
|
4549
|
+
$parent.appendChild($container);
|
|
4550
4550
|
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "openMaximized")) max$2(gobj);
|
|
4551
4551
|
else if (!mobile) {
|
|
4552
4552
|
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "content_size")) $container.style.height = "auto";
|
|
@@ -4632,6 +4632,30 @@ function close_window(gobj) {
|
|
|
4632
4632
|
};
|
|
4633
4633
|
}
|
|
4634
4634
|
/************************************************************
|
|
4635
|
+
* Where a window lives when its host names no `$parent`: the
|
|
4636
|
+
* OVERLAY layer of the shell -- above the content, below the
|
|
4637
|
+
* toolbar's dropdowns, the dialogs and the toasts.
|
|
4638
|
+
*
|
|
4639
|
+
* It used to be document.body, and that put every window above
|
|
4640
|
+
* the WHOLE shell: `.yui-shell` is position:fixed, a stacking
|
|
4641
|
+
* context of its own, so the z-index of its modal layer (99)
|
|
4642
|
+
* counted only inside it, and a window at z-index 11 on the body
|
|
4643
|
+
* covered it. The confirmation a window's own close asks ("all
|
|
4644
|
+
* changes will be lost") opened UNDER the window that asked it,
|
|
4645
|
+
* where no click could reach it; a toast, a dropdown of the
|
|
4646
|
+
* toolbar, any dialog opened with a window on screen, the same.
|
|
4647
|
+
*
|
|
4648
|
+
* Inside the layer the dock's z-index still orders the windows
|
|
4649
|
+
* among themselves. No shell (a page with windows and no shell):
|
|
4650
|
+
* the body, as before.
|
|
4651
|
+
************************************************************/
|
|
4652
|
+
function default_parent(gobj) {
|
|
4653
|
+
let shell = yui_shell_of(gobj);
|
|
4654
|
+
let layers = shell && shell.priv && shell.priv.layers;
|
|
4655
|
+
if (layers && layers.overlay) return layers.overlay;
|
|
4656
|
+
return document.body;
|
|
4657
|
+
}
|
|
4658
|
+
/************************************************************
|
|
4635
4659
|
*
|
|
4636
4660
|
************************************************************/
|
|
4637
4661
|
function toggle(gobj) {
|
|
@@ -6734,7 +6758,9 @@ function yui_tabulator_name_filters(table, t) {
|
|
|
6734
6758
|
let $title = $el.querySelector(".tabulator-col-title");
|
|
6735
6759
|
if (!title && $title) title = $title.textContent.trim();
|
|
6736
6760
|
if (!title) continue;
|
|
6737
|
-
|
|
6761
|
+
let name = t("filter by column", { column: title });
|
|
6762
|
+
$input.setAttribute("aria-label", name);
|
|
6763
|
+
$input.setAttribute("title", name);
|
|
6738
6764
|
}
|
|
6739
6765
|
} catch (e) {}
|
|
6740
6766
|
}
|
|
@@ -6765,6 +6791,8 @@ function yui_tabulator_name_row_selects(table, t) {
|
|
|
6765
6791
|
for (let $box of boxes) {
|
|
6766
6792
|
$box.setAttribute("aria-label", t("select row"));
|
|
6767
6793
|
$box.setAttribute("data-i18n-aria-label", "select row");
|
|
6794
|
+
$box.setAttribute("title", t("select row"));
|
|
6795
|
+
$box.setAttribute("data-i18n-title", "select row");
|
|
6768
6796
|
}
|
|
6769
6797
|
} catch (e) {}
|
|
6770
6798
|
}
|
|
@@ -9163,11 +9191,19 @@ function name_form_control$1($field, conf) {
|
|
|
9163
9191
|
$control.setAttribute("aria-label", (0, i18next.t)(key));
|
|
9164
9192
|
$control.setAttribute("data-i18n-aria-label", key);
|
|
9165
9193
|
}
|
|
9194
|
+
if ($control && !$control.getAttribute("title")) {
|
|
9195
|
+
$control.setAttribute("title", (0, i18next.t)(key));
|
|
9196
|
+
$control.setAttribute("data-i18n-title", key);
|
|
9197
|
+
}
|
|
9166
9198
|
let $ts = $field.querySelector(".ts-control input");
|
|
9167
9199
|
if ($ts && !$ts.getAttribute("aria-label")) {
|
|
9168
9200
|
$ts.setAttribute("aria-label", (0, i18next.t)(key));
|
|
9169
9201
|
$ts.setAttribute("data-i18n-aria-label", key);
|
|
9170
9202
|
}
|
|
9203
|
+
if ($ts && !$ts.getAttribute("title")) {
|
|
9204
|
+
$ts.setAttribute("title", (0, i18next.t)(key));
|
|
9205
|
+
$ts.setAttribute("data-i18n-title", key);
|
|
9206
|
+
}
|
|
9171
9207
|
let $clear = $field.querySelector(".ts-wrapper .clear-button");
|
|
9172
9208
|
if ($clear) {
|
|
9173
9209
|
const CLEAR_KEY = "remove all selected options";
|
|
@@ -10257,6 +10293,8 @@ function create_coordinates(gobj, attrs) {
|
|
|
10257
10293
|
type: "text",
|
|
10258
10294
|
placeholder: (0, i18next.t)("coordinates..."),
|
|
10259
10295
|
"data-i18n-placeholder": "coordinates...",
|
|
10296
|
+
title: (0, i18next.t)("coordinates"),
|
|
10297
|
+
"data-i18n-title": "coordinates",
|
|
10260
10298
|
"aria-label": (0, i18next.t)("coordinates"),
|
|
10261
10299
|
"data-i18n-aria-label": "coordinates"
|
|
10262
10300
|
},
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -4045,7 +4045,7 @@ var MOBILE_MAX = 768;
|
|
|
4045
4045
|
***************************************************************/
|
|
4046
4046
|
var attrs_table$21 = [
|
|
4047
4047
|
SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
|
|
4048
|
-
SDATA(data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else to document.body"),
|
|
4048
|
+
SDATA(data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else to the overlay layer of the shell (document.body when there is none)"),
|
|
4049
4049
|
SDATA(data_type_t.DTP_INTEGER, "x", 0, "300", "X position"),
|
|
4050
4050
|
SDATA(data_type_t.DTP_INTEGER, "y", 0, "100", "Y position"),
|
|
4051
4051
|
SDATA(data_type_t.DTP_INTEGER, "width", 0, "700", "Width of the window"),
|
|
@@ -4540,8 +4540,8 @@ function build_ui$18(gobj) {
|
|
|
4540
4540
|
}
|
|
4541
4541
|
refresh_language($container, t);
|
|
4542
4542
|
let $parent = gobj_read_attr(gobj, "$parent");
|
|
4543
|
-
if (
|
|
4544
|
-
|
|
4543
|
+
if (!$parent) $parent = default_parent(gobj);
|
|
4544
|
+
$parent.appendChild($container);
|
|
4545
4545
|
if (gobj_read_bool_attr(gobj, "openMaximized")) max$2(gobj);
|
|
4546
4546
|
else if (!mobile) {
|
|
4547
4547
|
if (gobj_read_bool_attr(gobj, "content_size")) $container.style.height = "auto";
|
|
@@ -4627,6 +4627,30 @@ function close_window(gobj) {
|
|
|
4627
4627
|
};
|
|
4628
4628
|
}
|
|
4629
4629
|
/************************************************************
|
|
4630
|
+
* Where a window lives when its host names no `$parent`: the
|
|
4631
|
+
* OVERLAY layer of the shell -- above the content, below the
|
|
4632
|
+
* toolbar's dropdowns, the dialogs and the toasts.
|
|
4633
|
+
*
|
|
4634
|
+
* It used to be document.body, and that put every window above
|
|
4635
|
+
* the WHOLE shell: `.yui-shell` is position:fixed, a stacking
|
|
4636
|
+
* context of its own, so the z-index of its modal layer (99)
|
|
4637
|
+
* counted only inside it, and a window at z-index 11 on the body
|
|
4638
|
+
* covered it. The confirmation a window's own close asks ("all
|
|
4639
|
+
* changes will be lost") opened UNDER the window that asked it,
|
|
4640
|
+
* where no click could reach it; a toast, a dropdown of the
|
|
4641
|
+
* toolbar, any dialog opened with a window on screen, the same.
|
|
4642
|
+
*
|
|
4643
|
+
* Inside the layer the dock's z-index still orders the windows
|
|
4644
|
+
* among themselves. No shell (a page with windows and no shell):
|
|
4645
|
+
* the body, as before.
|
|
4646
|
+
************************************************************/
|
|
4647
|
+
function default_parent(gobj) {
|
|
4648
|
+
let shell = yui_shell_of(gobj);
|
|
4649
|
+
let layers = shell && shell.priv && shell.priv.layers;
|
|
4650
|
+
if (layers && layers.overlay) return layers.overlay;
|
|
4651
|
+
return document.body;
|
|
4652
|
+
}
|
|
4653
|
+
/************************************************************
|
|
4630
4654
|
*
|
|
4631
4655
|
************************************************************/
|
|
4632
4656
|
function toggle(gobj) {
|
|
@@ -6729,7 +6753,9 @@ function yui_tabulator_name_filters(table, t) {
|
|
|
6729
6753
|
let $title = $el.querySelector(".tabulator-col-title");
|
|
6730
6754
|
if (!title && $title) title = $title.textContent.trim();
|
|
6731
6755
|
if (!title) continue;
|
|
6732
|
-
|
|
6756
|
+
let name = t("filter by column", { column: title });
|
|
6757
|
+
$input.setAttribute("aria-label", name);
|
|
6758
|
+
$input.setAttribute("title", name);
|
|
6733
6759
|
}
|
|
6734
6760
|
} catch (e) {}
|
|
6735
6761
|
}
|
|
@@ -6760,6 +6786,8 @@ function yui_tabulator_name_row_selects(table, t) {
|
|
|
6760
6786
|
for (let $box of boxes) {
|
|
6761
6787
|
$box.setAttribute("aria-label", t("select row"));
|
|
6762
6788
|
$box.setAttribute("data-i18n-aria-label", "select row");
|
|
6789
|
+
$box.setAttribute("title", t("select row"));
|
|
6790
|
+
$box.setAttribute("data-i18n-title", "select row");
|
|
6763
6791
|
}
|
|
6764
6792
|
} catch (e) {}
|
|
6765
6793
|
}
|
|
@@ -9158,11 +9186,19 @@ function name_form_control$1($field, conf) {
|
|
|
9158
9186
|
$control.setAttribute("aria-label", t(key));
|
|
9159
9187
|
$control.setAttribute("data-i18n-aria-label", key);
|
|
9160
9188
|
}
|
|
9189
|
+
if ($control && !$control.getAttribute("title")) {
|
|
9190
|
+
$control.setAttribute("title", t(key));
|
|
9191
|
+
$control.setAttribute("data-i18n-title", key);
|
|
9192
|
+
}
|
|
9161
9193
|
let $ts = $field.querySelector(".ts-control input");
|
|
9162
9194
|
if ($ts && !$ts.getAttribute("aria-label")) {
|
|
9163
9195
|
$ts.setAttribute("aria-label", t(key));
|
|
9164
9196
|
$ts.setAttribute("data-i18n-aria-label", key);
|
|
9165
9197
|
}
|
|
9198
|
+
if ($ts && !$ts.getAttribute("title")) {
|
|
9199
|
+
$ts.setAttribute("title", t(key));
|
|
9200
|
+
$ts.setAttribute("data-i18n-title", key);
|
|
9201
|
+
}
|
|
9166
9202
|
let $clear = $field.querySelector(".ts-wrapper .clear-button");
|
|
9167
9203
|
if ($clear) {
|
|
9168
9204
|
const CLEAR_KEY = "remove all selected options";
|
|
@@ -10252,6 +10288,8 @@ function create_coordinates(gobj, attrs) {
|
|
|
10252
10288
|
type: "text",
|
|
10253
10289
|
placeholder: t("coordinates..."),
|
|
10254
10290
|
"data-i18n-placeholder": "coordinates...",
|
|
10291
|
+
title: t("coordinates"),
|
|
10292
|
+
"data-i18n-title": "coordinates",
|
|
10255
10293
|
"aria-label": t("coordinates"),
|
|
10256
10294
|
"data-i18n-aria-label": "coordinates"
|
|
10257
10295
|
},
|
package/package.json
CHANGED
package/src/c_yui_form.js
CHANGED
|
@@ -688,6 +688,12 @@ function name_form_control($field, conf)
|
|
|
688
688
|
$control.setAttribute("aria-label", t(key));
|
|
689
689
|
$control.setAttribute("data-i18n-aria-label", key);
|
|
690
690
|
}
|
|
691
|
+
/* And its title, as every control carries: the label sits apart
|
|
692
|
+
* from the box, and a narrow form takes it to another line. */
|
|
693
|
+
if($control && !$control.getAttribute("title")) {
|
|
694
|
+
$control.setAttribute("title", t(key));
|
|
695
|
+
$control.setAttribute("data-i18n-title", key);
|
|
696
|
+
}
|
|
691
697
|
|
|
692
698
|
/* Tom Select HIDES the <select> it is given (`ts-hidden-accessible`)
|
|
693
699
|
* and draws its own text box in front of it. Naming the original
|
|
@@ -700,6 +706,10 @@ function name_form_control($field, conf)
|
|
|
700
706
|
$ts.setAttribute("aria-label", t(key));
|
|
701
707
|
$ts.setAttribute("data-i18n-aria-label", key);
|
|
702
708
|
}
|
|
709
|
+
if($ts && !$ts.getAttribute("title")) {
|
|
710
|
+
$ts.setAttribute("title", t(key));
|
|
711
|
+
$ts.setAttribute("data-i18n-title", key);
|
|
712
|
+
}
|
|
703
713
|
|
|
704
714
|
/* The ✕ of the clear_button plugin is drawn by Tom Select too, with a
|
|
705
715
|
* title written once with t() and no key: named here, after the
|
|
@@ -2052,6 +2062,8 @@ function create_coordinates(gobj, attrs)
|
|
|
2052
2062
|
* froze in the language it was built in. */
|
|
2053
2063
|
placeholder: t('coordinates...'),
|
|
2054
2064
|
'data-i18n-placeholder': 'coordinates...',
|
|
2065
|
+
title: t('coordinates'),
|
|
2066
|
+
'data-i18n-title': 'coordinates',
|
|
2055
2067
|
'aria-label': t('coordinates'),
|
|
2056
2068
|
'data-i18n-aria-label': 'coordinates'},
|
|
2057
2069
|
'',
|
package/src/c_yui_window.js
CHANGED
|
@@ -81,7 +81,7 @@ const MOBILE_MAX = 768;
|
|
|
81
81
|
***************************************************************/
|
|
82
82
|
const attrs_table = [
|
|
83
83
|
SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
|
|
84
|
-
SDATA(data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else to document.body"),
|
|
84
|
+
SDATA(data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else to the overlay layer of the shell (document.body when there is none)"),
|
|
85
85
|
SDATA(data_type_t.DTP_INTEGER, "x", 0, "300", "X position"),
|
|
86
86
|
SDATA(data_type_t.DTP_INTEGER, "y", 0, "100", "Y position"),
|
|
87
87
|
SDATA(data_type_t.DTP_INTEGER, "width", 0, "700", "Width of the window"),
|
|
@@ -724,11 +724,10 @@ function build_ui(gobj)
|
|
|
724
724
|
refresh_language($container, t);
|
|
725
725
|
|
|
726
726
|
let $parent = gobj_read_attr(gobj, "$parent");
|
|
727
|
-
if(
|
|
728
|
-
$parent
|
|
729
|
-
} else {
|
|
730
|
-
document.body.appendChild($container);
|
|
727
|
+
if(!$parent) {
|
|
728
|
+
$parent = default_parent(gobj);
|
|
731
729
|
}
|
|
730
|
+
$parent.appendChild($container);
|
|
732
731
|
|
|
733
732
|
if (gobj_read_bool_attr(gobj, "openMaximized")) {
|
|
734
733
|
max(gobj);
|
|
@@ -860,6 +859,34 @@ function close_window(gobj)
|
|
|
860
859
|
return {state: "asking", answer: answer};
|
|
861
860
|
}
|
|
862
861
|
|
|
862
|
+
/************************************************************
|
|
863
|
+
* Where a window lives when its host names no `$parent`: the
|
|
864
|
+
* OVERLAY layer of the shell -- above the content, below the
|
|
865
|
+
* toolbar's dropdowns, the dialogs and the toasts.
|
|
866
|
+
*
|
|
867
|
+
* It used to be document.body, and that put every window above
|
|
868
|
+
* the WHOLE shell: `.yui-shell` is position:fixed, a stacking
|
|
869
|
+
* context of its own, so the z-index of its modal layer (99)
|
|
870
|
+
* counted only inside it, and a window at z-index 11 on the body
|
|
871
|
+
* covered it. The confirmation a window's own close asks ("all
|
|
872
|
+
* changes will be lost") opened UNDER the window that asked it,
|
|
873
|
+
* where no click could reach it; a toast, a dropdown of the
|
|
874
|
+
* toolbar, any dialog opened with a window on screen, the same.
|
|
875
|
+
*
|
|
876
|
+
* Inside the layer the dock's z-index still orders the windows
|
|
877
|
+
* among themselves. No shell (a page with windows and no shell):
|
|
878
|
+
* the body, as before.
|
|
879
|
+
************************************************************/
|
|
880
|
+
function default_parent(gobj)
|
|
881
|
+
{
|
|
882
|
+
let shell = yui_shell_of(gobj);
|
|
883
|
+
let layers = shell && shell.priv && shell.priv.layers;
|
|
884
|
+
if(layers && layers.overlay) {
|
|
885
|
+
return layers.overlay;
|
|
886
|
+
}
|
|
887
|
+
return document.body;
|
|
888
|
+
}
|
|
889
|
+
|
|
863
890
|
/************************************************************
|
|
864
891
|
*
|
|
865
892
|
************************************************************/
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* window_layer.wiring.test.js
|
|
3
|
+
*
|
|
4
|
+
* Where a C_YUI_WINDOW lives when its host names no `$parent`:
|
|
5
|
+
* the OVERLAY layer of the shell, below the layers of the
|
|
6
|
+
* dropdowns, the dialogs and the toasts -- not document.body,
|
|
7
|
+
* where it covered the whole shell (a stacking context of its
|
|
8
|
+
* own) and the dialog of its own close opened under it.
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2026, ArtGins.
|
|
11
|
+
* All Rights Reserved.
|
|
12
|
+
***********************************************************************/
|
|
13
|
+
import {describe, test, expect, beforeAll} from "vitest";
|
|
14
|
+
import {install_dom_double} from "../test/dom_double.js";
|
|
15
|
+
|
|
16
|
+
install_dom_double();
|
|
17
|
+
|
|
18
|
+
if(typeof globalThis.matchMedia === "undefined") {
|
|
19
|
+
globalThis.matchMedia = () => ({matches: false, addEventListener() {}, removeEventListener() {}});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
SDATA, SDATA_END, data_type_t, gclass_create, gobj_write_attr,
|
|
24
|
+
gobj_start_up, gobj_create_yuno, gobj_create, gobj_create_service,
|
|
25
|
+
gobj_start, gobj_stop, gobj_destroy, gobj_read_attr,
|
|
26
|
+
} = await import("@yuneta/gobj-js");
|
|
27
|
+
const {register_c_yui_shell} = await import("./c_yui_shell.js");
|
|
28
|
+
const {register_c_yui_nav} = await import("./c_yui_nav.js");
|
|
29
|
+
const {register_c_yui_window} = await import("./c_yui_window.js");
|
|
30
|
+
|
|
31
|
+
let yuno = null;
|
|
32
|
+
|
|
33
|
+
beforeAll(async () => {
|
|
34
|
+
const i18next = (await import("i18next")).default;
|
|
35
|
+
await i18next.init({lng: "en", resources: {}});
|
|
36
|
+
gobj_start_up(null, null, null, null, null, null, null);
|
|
37
|
+
gclass_create("C_TEST_LAYER_HOST", [], [["ST_IDLE", []]], {}, 0, [SDATA_END()], {}, 0, 0, 0, 0);
|
|
38
|
+
gclass_create(
|
|
39
|
+
"C_TEST_LAYER_VIEW", [], [["ST_IDLE", []]], {
|
|
40
|
+
mt_create: (gobj) => {
|
|
41
|
+
gobj_write_attr(gobj, "$container", document.createElement("div"));
|
|
42
|
+
}
|
|
43
|
+
}, 0, [
|
|
44
|
+
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "view root"),
|
|
45
|
+
SDATA_END()
|
|
46
|
+
], {}, 0, 0, 0, 0
|
|
47
|
+
);
|
|
48
|
+
register_c_yui_shell();
|
|
49
|
+
register_c_yui_nav();
|
|
50
|
+
register_c_yui_window();
|
|
51
|
+
yuno = gobj_create_yuno("layer_yuno", "C_TEST_LAYER_HOST", {});
|
|
52
|
+
gobj_start(yuno);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("the default parent of a window", () => {
|
|
56
|
+
test("with no shell: document.body", () => {
|
|
57
|
+
const w = gobj_create_service("layer_w0", "C_YUI_WINDOW", {header: "w", body: ["div", {}, "w"]}, yuno);
|
|
58
|
+
gobj_start(w);
|
|
59
|
+
expect(gobj_read_attr(w, "$container").parentNode).toBe(document.body);
|
|
60
|
+
gobj_stop(w);
|
|
61
|
+
gobj_destroy(w);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("with a shell: its overlay layer, also for a window that is not its child", () => {
|
|
65
|
+
const shell = gobj_create("layer_shell", "C_YUI_SHELL", {
|
|
66
|
+
config: {
|
|
67
|
+
shell: {
|
|
68
|
+
zones: {center: {host: "stage.main"}},
|
|
69
|
+
stages: {main: {zone: "center", default_route: "/a"}}
|
|
70
|
+
},
|
|
71
|
+
menu: {primary: {items: [
|
|
72
|
+
{id: "a", name: "a", route: "/a",
|
|
73
|
+
target: {stage: "main", gclass: "C_TEST_LAYER_VIEW", kw: {}}}
|
|
74
|
+
]}}
|
|
75
|
+
},
|
|
76
|
+
use_hash: false
|
|
77
|
+
}, yuno);
|
|
78
|
+
gobj_start(shell);
|
|
79
|
+
|
|
80
|
+
/* A child of the shell, and one parented to the app (the dev
|
|
81
|
+
* window, a device window of a view): both in the overlay. */
|
|
82
|
+
const inside = gobj_create_service("layer_w1", "C_YUI_WINDOW", {header: "a", body: ["div", {}, "a"]}, shell);
|
|
83
|
+
gobj_start(inside);
|
|
84
|
+
const outside = gobj_create_service("layer_w2", "C_YUI_WINDOW", {header: "b", body: ["div", {}, "b"]}, yuno);
|
|
85
|
+
gobj_start(outside);
|
|
86
|
+
|
|
87
|
+
const overlay = shell.priv.layers.overlay;
|
|
88
|
+
expect(overlay).toBeTruthy();
|
|
89
|
+
expect(gobj_read_attr(inside, "$container").parentNode).toBe(overlay);
|
|
90
|
+
expect(gobj_read_attr(outside, "$container").parentNode).toBe(overlay);
|
|
91
|
+
|
|
92
|
+
/* And a `$parent` named by the host still wins. */
|
|
93
|
+
const $mine = document.createElement("div");
|
|
94
|
+
const named = gobj_create_service("layer_w3", "C_YUI_WINDOW",
|
|
95
|
+
{header: "c", body: ["div", {}, "c"], $parent: $mine}, yuno);
|
|
96
|
+
gobj_start(named);
|
|
97
|
+
expect(gobj_read_attr(named, "$container").parentNode).toBe($mine);
|
|
98
|
+
|
|
99
|
+
for(const w of [inside, outside, named]) {
|
|
100
|
+
gobj_stop(w);
|
|
101
|
+
gobj_destroy(w);
|
|
102
|
+
}
|
|
103
|
+
gobj_stop(shell);
|
|
104
|
+
gobj_destroy(shell);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -134,8 +134,14 @@ function yui_tabulator_name_filters(table, t)
|
|
|
134
134
|
* already exists in two apps as the header filter's
|
|
135
135
|
* PLACEHOLDER ("filtrar columna...") and carries no
|
|
136
136
|
* interpolation, so reusing it named all five boxes the
|
|
137
|
-
* same -- which is where this started.
|
|
138
|
-
|
|
137
|
+
* same -- which is where this started.
|
|
138
|
+
*
|
|
139
|
+
* The `title` too: every control carries both, and a filter
|
|
140
|
+
* box shows only its placeholder, which goes as soon as
|
|
141
|
+
* something is typed in it. */
|
|
142
|
+
let name = t("filter by column", {column: title});
|
|
143
|
+
$input.setAttribute("aria-label", name);
|
|
144
|
+
$input.setAttribute("title", name);
|
|
139
145
|
}
|
|
140
146
|
} catch(e) {
|
|
141
147
|
/* a table between renders has no columns to name */
|
|
@@ -181,6 +187,8 @@ function yui_tabulator_name_row_selects(table, t)
|
|
|
181
187
|
for(let $box of boxes) {
|
|
182
188
|
$box.setAttribute("aria-label", t("select row"));
|
|
183
189
|
$box.setAttribute("data-i18n-aria-label", "select row");
|
|
190
|
+
$box.setAttribute("title", t("select row"));
|
|
191
|
+
$box.setAttribute("data-i18n-title", "select row");
|
|
184
192
|
}
|
|
185
193
|
} catch(e) {
|
|
186
194
|
/* a table between renders has no boxes to name */
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* yui_tabulator_i18n.test.js
|
|
3
|
+
*
|
|
4
|
+
* The names given AFTER the render to what Tabulator draws for
|
|
5
|
+
* itself: the header filters and the row-selection boxes carry
|
|
6
|
+
* an aria-label AND a title, like every control.
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) 2026, ArtGins.
|
|
9
|
+
* All Rights Reserved.
|
|
10
|
+
***********************************************************************/
|
|
11
|
+
import {describe, it, expect} from "vitest";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
yui_tabulator_name_filters,
|
|
15
|
+
yui_tabulator_name_row_selects,
|
|
16
|
+
} from "./yui_tabulator_i18n.js";
|
|
17
|
+
|
|
18
|
+
/* An element double: attributes, and the one child a selector finds. */
|
|
19
|
+
function element(child)
|
|
20
|
+
{
|
|
21
|
+
const attrs = {};
|
|
22
|
+
return {
|
|
23
|
+
attrs,
|
|
24
|
+
setAttribute: (k, v) => {
|
|
25
|
+
attrs[k] = String(v);
|
|
26
|
+
},
|
|
27
|
+
getAttribute: (k) => (k in attrs) ? attrs[k] : null,
|
|
28
|
+
querySelector: () => child || null,
|
|
29
|
+
querySelectorAll: () => (child ? [child] : []),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* A column double with its header filter input. */
|
|
34
|
+
function column(title, $input)
|
|
35
|
+
{
|
|
36
|
+
const $el = element($input);
|
|
37
|
+
return {
|
|
38
|
+
getElement: () => $el,
|
|
39
|
+
getDefinition: () => ({title: title}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const t = (key, opts) => (key === "filter by column")
|
|
44
|
+
? `Filtrar por ${opts.column}`
|
|
45
|
+
: (key === "select row" ? "Seleccionar fila" : key);
|
|
46
|
+
|
|
47
|
+
describe("yui_tabulator_name_filters", () => {
|
|
48
|
+
it("names each header filter with an aria-label and a title", () => {
|
|
49
|
+
const $a = element();
|
|
50
|
+
const $b = element();
|
|
51
|
+
const table = {getColumns: () => [column("Id", $a), column("Name", $b)]};
|
|
52
|
+
|
|
53
|
+
yui_tabulator_name_filters(table, t);
|
|
54
|
+
|
|
55
|
+
expect($a.attrs["aria-label"]).toBe("Filtrar por Id");
|
|
56
|
+
expect($a.attrs["title"]).toBe("Filtrar por Id");
|
|
57
|
+
expect($b.attrs["aria-label"]).toBe("Filtrar por Name");
|
|
58
|
+
expect($b.attrs["title"]).toBe("Filtrar por Name");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("leaves a column without a filter alone", () => {
|
|
62
|
+
const table = {getColumns: () => [column("Id", null)]};
|
|
63
|
+
expect(() => yui_tabulator_name_filters(table, t)).not.toThrow();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("yui_tabulator_name_row_selects", () => {
|
|
68
|
+
it("renames Tabulator's English box with an aria-label and a title, and their keys", () => {
|
|
69
|
+
const $box = element();
|
|
70
|
+
$box.setAttribute("aria-label", "Select Row");
|
|
71
|
+
const table = {element: element($box)};
|
|
72
|
+
|
|
73
|
+
yui_tabulator_name_row_selects(table, t);
|
|
74
|
+
|
|
75
|
+
expect($box.attrs["aria-label"]).toBe("Seleccionar fila");
|
|
76
|
+
expect($box.attrs["data-i18n-aria-label"]).toBe("select row");
|
|
77
|
+
expect($box.attrs["title"]).toBe("Seleccionar fila");
|
|
78
|
+
expect($box.attrs["data-i18n-title"]).toBe("select row");
|
|
79
|
+
});
|
|
80
|
+
});
|