@yuneta/gobj-ui 5.1.0 → 5.2.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 +23 -3
- package/dist/gobj-ui.es.js +23 -3
- package/package.json +1 -1
- package/src/c_yui_shell.css +34 -0
- package/src/c_yui_shell.js +23 -3
- package/src/shell_toolbar_helpers.js +13 -0
- package/src/shell_toolbar_helpers.test.js +41 -0
- package/src/yui_icons.css +10 -0
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -307,6 +307,8 @@ function validate_dropdown_action(action, owner_id) {
|
|
|
307
307
|
warnings.push(`dropdown item ${where} has action.type:dropdown — nested dropdowns are not supported`);
|
|
308
308
|
continue;
|
|
309
309
|
}
|
|
310
|
+
if (it.icon && it.image) warnings.push(`dropdown item ${where} declares both icon and image — image wins, drop one`);
|
|
311
|
+
if (it.selected !== void 0 && typeof it.selected !== "boolean") warnings.push(`dropdown item ${where} has non-boolean 'selected'`);
|
|
310
312
|
let nested = validate_action(it.action, where);
|
|
311
313
|
for (let w of nested) warnings.push(w);
|
|
312
314
|
}
|
|
@@ -1914,7 +1916,16 @@ function build_dropdown_row(gobj, sub, idx) {
|
|
|
1914
1916
|
return null;
|
|
1915
1917
|
}
|
|
1916
1918
|
let children = [];
|
|
1917
|
-
if (!(0, _yuneta_gobj_js.empty_string)(sub.
|
|
1919
|
+
if (!(0, _yuneta_gobj_js.empty_string)(sub.image)) children.push([
|
|
1920
|
+
"span",
|
|
1921
|
+
{ class: "yui-toolbar-dropdown-glyph" },
|
|
1922
|
+
["img", {
|
|
1923
|
+
src: sub.image,
|
|
1924
|
+
alt: "",
|
|
1925
|
+
"aria-hidden": "true"
|
|
1926
|
+
}]
|
|
1927
|
+
]);
|
|
1928
|
+
else if (!(0, _yuneta_gobj_js.empty_string)(sub.icon)) children.push([
|
|
1918
1929
|
"span",
|
|
1919
1930
|
{ class: "icon" },
|
|
1920
1931
|
["i", {
|
|
@@ -1930,12 +1941,21 @@ function build_dropdown_row(gobj, sub, idx) {
|
|
|
1930
1941
|
},
|
|
1931
1942
|
sub.name
|
|
1932
1943
|
]);
|
|
1944
|
+
children.push([
|
|
1945
|
+
"span",
|
|
1946
|
+
{ class: "yui-toolbar-dropdown-check" },
|
|
1947
|
+
["i", {
|
|
1948
|
+
class: "yi-check",
|
|
1949
|
+
"aria-hidden": "true"
|
|
1950
|
+
}]
|
|
1951
|
+
]);
|
|
1933
1952
|
let aria_key = sub.aria_label || sub.name || sub.id || "";
|
|
1934
1953
|
let i18n_aria = sub.aria_label || sub.name;
|
|
1935
1954
|
let attrs = {
|
|
1936
|
-
class: "yui-toolbar-dropdown-item",
|
|
1955
|
+
class: "yui-toolbar-dropdown-item" + (sub.selected ? " is-selected" : ""),
|
|
1937
1956
|
type: "button",
|
|
1938
|
-
role: "
|
|
1957
|
+
role: "menuitemradio",
|
|
1958
|
+
"aria-checked": sub.selected ? "true" : "false",
|
|
1939
1959
|
"data-dropdown-item-id": sub.id || "",
|
|
1940
1960
|
"aria-label": aria_key
|
|
1941
1961
|
};
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -302,6 +302,8 @@ function validate_dropdown_action(action, owner_id) {
|
|
|
302
302
|
warnings.push(`dropdown item ${where} has action.type:dropdown — nested dropdowns are not supported`);
|
|
303
303
|
continue;
|
|
304
304
|
}
|
|
305
|
+
if (it.icon && it.image) warnings.push(`dropdown item ${where} declares both icon and image — image wins, drop one`);
|
|
306
|
+
if (it.selected !== void 0 && typeof it.selected !== "boolean") warnings.push(`dropdown item ${where} has non-boolean 'selected'`);
|
|
305
307
|
let nested = validate_action(it.action, where);
|
|
306
308
|
for (let w of nested) warnings.push(w);
|
|
307
309
|
}
|
|
@@ -1909,7 +1911,16 @@ function build_dropdown_row(gobj, sub, idx) {
|
|
|
1909
1911
|
return null;
|
|
1910
1912
|
}
|
|
1911
1913
|
let children = [];
|
|
1912
|
-
if (!empty_string(sub.
|
|
1914
|
+
if (!empty_string(sub.image)) children.push([
|
|
1915
|
+
"span",
|
|
1916
|
+
{ class: "yui-toolbar-dropdown-glyph" },
|
|
1917
|
+
["img", {
|
|
1918
|
+
src: sub.image,
|
|
1919
|
+
alt: "",
|
|
1920
|
+
"aria-hidden": "true"
|
|
1921
|
+
}]
|
|
1922
|
+
]);
|
|
1923
|
+
else if (!empty_string(sub.icon)) children.push([
|
|
1913
1924
|
"span",
|
|
1914
1925
|
{ class: "icon" },
|
|
1915
1926
|
["i", {
|
|
@@ -1925,12 +1936,21 @@ function build_dropdown_row(gobj, sub, idx) {
|
|
|
1925
1936
|
},
|
|
1926
1937
|
sub.name
|
|
1927
1938
|
]);
|
|
1939
|
+
children.push([
|
|
1940
|
+
"span",
|
|
1941
|
+
{ class: "yui-toolbar-dropdown-check" },
|
|
1942
|
+
["i", {
|
|
1943
|
+
class: "yi-check",
|
|
1944
|
+
"aria-hidden": "true"
|
|
1945
|
+
}]
|
|
1946
|
+
]);
|
|
1928
1947
|
let aria_key = sub.aria_label || sub.name || sub.id || "";
|
|
1929
1948
|
let i18n_aria = sub.aria_label || sub.name;
|
|
1930
1949
|
let attrs = {
|
|
1931
|
-
class: "yui-toolbar-dropdown-item",
|
|
1950
|
+
class: "yui-toolbar-dropdown-item" + (sub.selected ? " is-selected" : ""),
|
|
1932
1951
|
type: "button",
|
|
1933
|
-
role: "
|
|
1952
|
+
role: "menuitemradio",
|
|
1953
|
+
"aria-checked": sub.selected ? "true" : "false",
|
|
1934
1954
|
"data-dropdown-item-id": sub.id || "",
|
|
1935
1955
|
"aria-label": aria_key
|
|
1936
1956
|
};
|
package/package.json
CHANGED
package/src/c_yui_shell.css
CHANGED
|
@@ -528,6 +528,40 @@
|
|
|
528
528
|
.yui-toolbar-dropdown-item:hover {
|
|
529
529
|
background-color: var(--bulma-scheme-main-bis, rgba(0, 0, 0, 0.06));
|
|
530
530
|
}
|
|
531
|
+
/* Label takes the slack so the trailing check sits flush right. */
|
|
532
|
+
.yui-toolbar-dropdown-label {
|
|
533
|
+
flex: 1;
|
|
534
|
+
}
|
|
535
|
+
/* Image glyph (flags): boxed to the icon size so rows stay aligned
|
|
536
|
+
* whether an item uses an icon class or an image. */
|
|
537
|
+
.yui-toolbar-dropdown-glyph {
|
|
538
|
+
display: inline-flex;
|
|
539
|
+
align-items: center;
|
|
540
|
+
justify-content: center;
|
|
541
|
+
width: 1.5rem;
|
|
542
|
+
flex: 0 0 auto;
|
|
543
|
+
}
|
|
544
|
+
.yui-toolbar-dropdown-glyph img {
|
|
545
|
+
width: 1.15rem;
|
|
546
|
+
height: auto;
|
|
547
|
+
display: block;
|
|
548
|
+
border-radius: 2px;
|
|
549
|
+
}
|
|
550
|
+
/* The check is always in the DOM and only hidden, so selecting a
|
|
551
|
+
* different option does not change any row's width. */
|
|
552
|
+
.yui-toolbar-dropdown-check {
|
|
553
|
+
display: inline-flex;
|
|
554
|
+
align-items: center;
|
|
555
|
+
flex: 0 0 auto;
|
|
556
|
+
visibility: hidden;
|
|
557
|
+
color: var(--bulma-link, #485fc7);
|
|
558
|
+
}
|
|
559
|
+
.yui-toolbar-dropdown-item.is-selected .yui-toolbar-dropdown-check {
|
|
560
|
+
visibility: visible;
|
|
561
|
+
}
|
|
562
|
+
.yui-toolbar-dropdown-item.is-selected .yui-toolbar-dropdown-label {
|
|
563
|
+
font-weight: 600;
|
|
564
|
+
}
|
|
531
565
|
.yui-toolbar-dropdown-item:focus-visible {
|
|
532
566
|
outline: 2px solid var(--bulma-link, #485fc7);
|
|
533
567
|
outline-offset: -2px;
|
package/src/c_yui_shell.js
CHANGED
|
@@ -2068,7 +2068,20 @@ function build_dropdown_row(gobj, sub, idx)
|
|
|
2068
2068
|
return null;
|
|
2069
2069
|
}
|
|
2070
2070
|
let children = [];
|
|
2071
|
-
|
|
2071
|
+
/* Leading glyph: either an icon CLASS or an IMAGE.
|
|
2072
|
+
*
|
|
2073
|
+
* `image` (a URL or a data: URI) exists for what the icon set
|
|
2074
|
+
* cannot express: the icons are CSS masks, so they are strictly
|
|
2075
|
+
* MONOCHROME, and a flag per language — the motivating case — is
|
|
2076
|
+
* not. It is deliberately an <img src> and NOT raw markup: an
|
|
2077
|
+
* SVG loaded through <img> cannot run script, so this stays safe
|
|
2078
|
+
* even if a caller ever feeds it a value that came from a
|
|
2079
|
+
* backend. An API that took markup would be one refactor away
|
|
2080
|
+
* from an XSS. */
|
|
2081
|
+
if(!empty_string(sub.image)) {
|
|
2082
|
+
children.push(["span", {class: "yui-toolbar-dropdown-glyph"},
|
|
2083
|
+
["img", {src: sub.image, alt: "", "aria-hidden": "true"}]]);
|
|
2084
|
+
} else if(!empty_string(sub.icon)) {
|
|
2072
2085
|
children.push(["span", {class: "icon"},
|
|
2073
2086
|
["i", {class: sub.icon, "aria-hidden": "true"}]]);
|
|
2074
2087
|
}
|
|
@@ -2076,12 +2089,19 @@ function build_dropdown_row(gobj, sub, idx)
|
|
|
2076
2089
|
children.push(["span", {class: "yui-toolbar-dropdown-label",
|
|
2077
2090
|
i18n: sub.name}, sub.name]);
|
|
2078
2091
|
}
|
|
2092
|
+
/* Trailing check for the ACTIVE option. The element is always
|
|
2093
|
+
* rendered and hidden by CSS when not selected, so every row keeps
|
|
2094
|
+
* the same width and the list does not jump as the choice moves. */
|
|
2095
|
+
children.push(["span", {class: "yui-toolbar-dropdown-check"},
|
|
2096
|
+
["i", {class: "yi-check", "aria-hidden": "true"}]]);
|
|
2097
|
+
|
|
2079
2098
|
let aria_key = sub.aria_label || sub.name || sub.id || "";
|
|
2080
2099
|
let i18n_aria = sub.aria_label || sub.name;
|
|
2081
2100
|
let attrs = {
|
|
2082
|
-
class: "yui-toolbar-dropdown-item",
|
|
2101
|
+
class: "yui-toolbar-dropdown-item" + (sub.selected ? " is-selected" : ""),
|
|
2083
2102
|
type: "button",
|
|
2084
|
-
role: "
|
|
2103
|
+
role: "menuitemradio",
|
|
2104
|
+
"aria-checked": sub.selected ? "true" : "false",
|
|
2085
2105
|
"data-dropdown-item-id": sub.id || "",
|
|
2086
2106
|
"aria-label": aria_key
|
|
2087
2107
|
};
|
|
@@ -202,6 +202,19 @@ export function validate_dropdown_action(action, owner_id)
|
|
|
202
202
|
);
|
|
203
203
|
continue;
|
|
204
204
|
}
|
|
205
|
+
/* A row draws ONE leading glyph. Declaring both is not fatal
|
|
206
|
+
* (image wins) but it is always a mistake worth surfacing. */
|
|
207
|
+
if(it.icon && it.image) {
|
|
208
|
+
warnings.push(
|
|
209
|
+
`dropdown item ${where} declares both icon and image — ` +
|
|
210
|
+
`image wins, drop one`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
if(it.selected !== undefined && typeof it.selected !== "boolean") {
|
|
214
|
+
warnings.push(
|
|
215
|
+
`dropdown item ${where} has non-boolean 'selected'`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
205
218
|
let nested = validate_action(it.action, where);
|
|
206
219
|
for(let w of nested) {
|
|
207
220
|
warnings.push(w);
|
|
@@ -205,3 +205,44 @@ test("kinds + action-type constants are exposed", () => {
|
|
|
205
205
|
expect(TOOLBAR_ITEM_KINDS).toEqual(["brand", "avatar", "connection", "action"]);
|
|
206
206
|
expect(TOOLBAR_ACTION_TYPES).toEqual(["navigate", "drawer", "event", "dropdown"]);
|
|
207
207
|
});
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
/*============================================================
|
|
211
|
+
* dropdown items: selected + image (5.2.0)
|
|
212
|
+
*============================================================*/
|
|
213
|
+
test("dropdown: 'selected' and 'image' are accepted", () => {
|
|
214
|
+
let r = validate_dropdown_action({
|
|
215
|
+
type: "dropdown",
|
|
216
|
+
items: [
|
|
217
|
+
{ id: "es", name: "spanish", image: "data:image/svg+xml,%3Csvg/%3E",
|
|
218
|
+
selected: true, action: { type: "navigate", route: "/language/set/es" } },
|
|
219
|
+
{ id: "en", name: "english", image: "data:image/svg+xml,%3Csvg/%3E",
|
|
220
|
+
selected: false, action: { type: "navigate", route: "/language/set/en" } },
|
|
221
|
+
],
|
|
222
|
+
}, "language");
|
|
223
|
+
expect(r).toEqual([]);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("dropdown: declaring both icon and image warns", () => {
|
|
227
|
+
let r = validate_dropdown_action({
|
|
228
|
+
type: "dropdown",
|
|
229
|
+
items: [
|
|
230
|
+
{ id: "es", name: "spanish", icon: "yi-language", image: "/es.svg",
|
|
231
|
+
action: { type: "navigate", route: "/language/set/es" } },
|
|
232
|
+
],
|
|
233
|
+
}, "language");
|
|
234
|
+
expect(r.length).toBe(1);
|
|
235
|
+
expect(r[0]).toContain("both icon and image");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("dropdown: non-boolean 'selected' warns", () => {
|
|
239
|
+
let r = validate_dropdown_action({
|
|
240
|
+
type: "dropdown",
|
|
241
|
+
items: [
|
|
242
|
+
{ id: "es", name: "spanish", selected: "yes",
|
|
243
|
+
action: { type: "navigate", route: "/language/set/es" } },
|
|
244
|
+
],
|
|
245
|
+
}, "language");
|
|
246
|
+
expect(r.length).toBe(1);
|
|
247
|
+
expect(r[0]).toContain("non-boolean");
|
|
248
|
+
});
|
package/src/yui_icons.css
CHANGED
|
@@ -113,6 +113,11 @@
|
|
|
113
113
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath d='M9.4 86.6C-3.1 74.1-3.1 53.9 9.4 41.4s32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L178.7 256 9.4 86.6zM256 416l288 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-288 0c-17.7 0-32-14.3-32-32s14.3-32 32-32z'/%3E%3C/svg%3E");
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
.yi-upload::before {
|
|
117
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M246.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 109.3 192 320c0 17.7 14.3 32 32 32s32-14.3 32-32l0-210.7 73.4 73.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-128-128zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64c0 53 43 96 96 96l320 0c53 0 96-43 96-96l0-64c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64c0 17.7-14.3 32-32 32L96 448c-17.7 0-32-14.3-32-32l0-64z'/%3E%3C/svg%3E");
|
|
118
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M246.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 109.3 192 320c0 17.7 14.3 32 32 32s32-14.3 32-32l0-210.7 73.4 73.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-128-128zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64c0 53 43 96 96 96l320 0c53 0 96-43 96-96l0-64c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64c0 17.7-14.3 32-32 32L96 448c-17.7 0-32-14.3-32-32l0-64z'/%3E%3C/svg%3E");
|
|
119
|
+
}
|
|
120
|
+
|
|
116
121
|
.yi-user::before {
|
|
117
122
|
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M224 248a120 120 0 1 0 0-240 120 120 0 1 0 0 240zm-29.7 56C95.8 304 16 383.8 16 482.3 16 498.7 29.3 512 45.7 512l356.6 0c16.4 0 29.7-13.3 29.7-29.7 0-98.5-79.8-178.3-178.3-178.3l-59.4 0z'/%3E%3C/svg%3E");
|
|
118
123
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M224 248a120 120 0 1 0 0-240 120 120 0 1 0 0 240zm-29.7 56C95.8 304 16 383.8 16 482.3 16 498.7 29.3 512 45.7 512l356.6 0c16.4 0 29.7-13.3 29.7-29.7 0-98.5-79.8-178.3-178.3-178.3l-59.4 0z'/%3E%3C/svg%3E");
|
|
@@ -129,6 +134,11 @@
|
|
|
129
134
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M384 80c8.8 0 16 7.2 16 16l0 320c0 8.8-7.2 16-16 16L64 432c-8.8 0-16-7.2-16-16L48 96c0-8.8 7.2-16 16-16l320 0zM64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64L64 32z'/%3E%3C/svg%3E");
|
|
130
135
|
}
|
|
131
136
|
|
|
137
|
+
.yi-check::before {
|
|
138
|
+
-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 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z'/%3E%3C/svg%3E");
|
|
139
|
+
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 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z'/%3E%3C/svg%3E");
|
|
140
|
+
}
|
|
141
|
+
|
|
132
142
|
.yi-square-check::before {
|
|
133
143
|
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M384 32c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96C0 60.7 28.7 32 64 32l320 0zM64 80c-8.8 0-16 7.2-16 16l0 320c0 8.8 7.2 16 16 16l320 0c8.8 0 16-7.2 16-16l0-320c0-8.8-7.2-16-16-16L64 80zm230.7 89.9c7.8-10.7 22.8-13.1 33.5-5.3 10.7 7.8 13.1 22.8 5.3 33.5L211.4 366.1c-4.1 5.7-10.5 9.3-17.5 9.8-7 .5-13.9-2-18.8-6.9l-55.9-55.9c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l36 36 105.6-145.2z'/%3E%3C/svg%3E");
|
|
134
144
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M384 32c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96C0 60.7 28.7 32 64 32l320 0zM64 80c-8.8 0-16 7.2-16 16l0 320c0 8.8 7.2 16 16 16l320 0c8.8 0 16-7.2 16-16l0-320c0-8.8-7.2-16-16-16L64 80zm230.7 89.9c7.8-10.7 22.8-13.1 33.5-5.3 10.7 7.8 13.1 22.8 5.3 33.5L211.4 366.1c-4.1 5.7-10.5 9.3-17.5 9.8-7 .5-13.9-2-18.8-6.9l-55.9-55.9c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l36 36 105.6-145.2z'/%3E%3C/svg%3E");
|