@yuneta/gobj-ui 7.23.39 → 7.23.41
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 +79 -25
- package/dist/gobj-ui.es.js +79 -25
- package/package.json +1 -1
- package/src/bulma_is_light.test.js +160 -0
- package/src/bulma_tint.js +71 -0
- package/src/c_yui_gobj_tree_js.js +20 -8
- package/src/c_yui_json_graph.js +17 -9
- package/src/c_yui_schema_editor.js +27 -3
- package/src/c_yui_treedb_graph.js +10 -6
- package/src/shell_modals.js +2 -1
- package/src/shell_route_map.js +13 -3
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -44,6 +44,75 @@ maplibre_gl = __toESM(maplibre_gl, 1);
|
|
|
44
44
|
let uplot = require("uplot");
|
|
45
45
|
uplot = __toESM(uplot, 1);
|
|
46
46
|
let _antv_g6 = require("@antv/g6");
|
|
47
|
+
//#region src/bulma_tint.js
|
|
48
|
+
/***********************************************************************
|
|
49
|
+
* bulma_tint.js
|
|
50
|
+
*
|
|
51
|
+
* Bulma's "soften this colour" modifier, applied so that it cannot
|
|
52
|
+
* be applied alone.
|
|
53
|
+
*
|
|
54
|
+
* WHY THIS IS A FUNCTION AND NOT A STRING. 'light' is one of
|
|
55
|
+
* Bulma's colours, so `is-light` is BOTH a colour and the modifier
|
|
56
|
+
* that softens another one. A compound selector may match the same
|
|
57
|
+
* class twice, so `.tag.is-light.is-light` matches an element that
|
|
58
|
+
* carries the class ONCE -- and that rule sets the text to
|
|
59
|
+
* var(--bulma-light-light-invert-l), WHICH BULMA NEVER DEFINES:
|
|
60
|
+
* referenced four times in bulma.css, declared zero times. The
|
|
61
|
+
* colour declaration is then invalid while the background still
|
|
62
|
+
* resolves, so what is left on screen is a pill with no readable
|
|
63
|
+
* label.
|
|
64
|
+
*
|
|
65
|
+
* Two selectors of Bulma 1 reach that undefined variable:
|
|
66
|
+
*
|
|
67
|
+
* .tag.is-light.is-light
|
|
68
|
+
* .notification.is-light.is-light
|
|
69
|
+
*
|
|
70
|
+
* It cost three unreadable chips in the schema editor -- the topic
|
|
71
|
+
* count, the schema version, and every column flag with no colour of
|
|
72
|
+
* its own ('writable', 'image', 'wild'...). They read as empty
|
|
73
|
+
* pills, which looks like a bug in the DATA and is a missing CSS
|
|
74
|
+
* variable.
|
|
75
|
+
*
|
|
76
|
+
* The neutral chip is a bare element with no modifier at all: it
|
|
77
|
+
* takes --bulma-background-l and --bulma-text-l, follows the theme
|
|
78
|
+
* and stays legible in both.
|
|
79
|
+
*
|
|
80
|
+
* Copyright (c) 2026, ArtGins.
|
|
81
|
+
* All Rights Reserved.
|
|
82
|
+
***********************************************************************/
|
|
83
|
+
var TINTABLE = [
|
|
84
|
+
"primary",
|
|
85
|
+
"link",
|
|
86
|
+
"info",
|
|
87
|
+
"success",
|
|
88
|
+
"warning",
|
|
89
|
+
"danger",
|
|
90
|
+
"black",
|
|
91
|
+
"dark",
|
|
92
|
+
"white",
|
|
93
|
+
"text",
|
|
94
|
+
"ghost"
|
|
95
|
+
];
|
|
96
|
+
/***************************************************************
|
|
97
|
+
* A softened colour, or nothing.
|
|
98
|
+
*
|
|
99
|
+
* yui_tint("warning") -> "is-warning is-light"
|
|
100
|
+
* yui_tint("is-warning") -> "is-warning is-light"
|
|
101
|
+
* yui_tint("light") -> "" (there is nothing to soften)
|
|
102
|
+
* yui_tint("") -> ""
|
|
103
|
+
* yui_tint(undefined) -> ""
|
|
104
|
+
*
|
|
105
|
+
* Anything it does not recognise comes back empty rather than being
|
|
106
|
+
* passed through: a class this function cannot vouch for is exactly the
|
|
107
|
+
* case it exists to stop.
|
|
108
|
+
***************************************************************/
|
|
109
|
+
function yui_tint(colour) {
|
|
110
|
+
if (typeof colour !== "string") return "";
|
|
111
|
+
const name = colour.trim().replace(/^is-/, "");
|
|
112
|
+
if (TINTABLE.indexOf(name) < 0) return "";
|
|
113
|
+
return `is-${name} is-light`;
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
47
116
|
//#region src/shell_focus_trap.js
|
|
48
117
|
/***********************************************************************
|
|
49
118
|
* shell_focus_trap.js
|
|
@@ -2858,7 +2927,7 @@ function show_notification(shell, kind, message, opts) {
|
|
|
2858
2927
|
let $note = (0, _yuneta_gobj_js.createElement2)([
|
|
2859
2928
|
"div",
|
|
2860
2929
|
{
|
|
2861
|
-
class: `TOAST notification yui-notification
|
|
2930
|
+
class: `TOAST notification yui-notification ${yui_tint(kind)}`,
|
|
2862
2931
|
role: kind === "danger" ? "alert" : "status"
|
|
2863
2932
|
},
|
|
2864
2933
|
[["button", {
|
|
@@ -10921,10 +10990,6 @@ function make_toolbar$5(gobj) {
|
|
|
10921
10990
|
"span",
|
|
10922
10991
|
{ class: "JSON_GRAPH_FIND_COUNT" },
|
|
10923
10992
|
""
|
|
10924
|
-
], [
|
|
10925
|
-
"span",
|
|
10926
|
-
{ i18n: "matches" },
|
|
10927
|
-
"matches"
|
|
10928
10993
|
]]
|
|
10929
10994
|
]]);
|
|
10930
10995
|
let center_items = [];
|
|
@@ -11450,7 +11515,7 @@ function update_find_result$1(gobj) {
|
|
|
11450
11515
|
priv.$find_count.textContent = "";
|
|
11451
11516
|
return;
|
|
11452
11517
|
}
|
|
11453
|
-
priv.$find_count.textContent =
|
|
11518
|
+
priv.$find_count.textContent = (0, i18next.t)("matches", { count: priv.match_count });
|
|
11454
11519
|
priv.$find_result.classList.remove("is-hidden");
|
|
11455
11520
|
}
|
|
11456
11521
|
/************************************************************
|
|
@@ -17368,10 +17433,6 @@ function make_find_items(gobj) {
|
|
|
17368
17433
|
"span",
|
|
17369
17434
|
{ class: "GOBJ_TREE_FIND_COUNT" },
|
|
17370
17435
|
""
|
|
17371
|
-
], [
|
|
17372
|
-
"span",
|
|
17373
|
-
{ i18n: "matches" },
|
|
17374
|
-
(0, i18next.t)("matches")
|
|
17375
17436
|
]]
|
|
17376
17437
|
]];
|
|
17377
17438
|
}
|
|
@@ -17404,7 +17465,7 @@ function update_find_result(gobj) {
|
|
|
17404
17465
|
priv.$find_count.textContent = "";
|
|
17405
17466
|
return;
|
|
17406
17467
|
}
|
|
17407
|
-
priv.$find_count.textContent =
|
|
17468
|
+
priv.$find_count.textContent = (0, i18next.t)("matches", { count: priv.match_count });
|
|
17408
17469
|
priv.$find_result.classList.remove("is-hidden");
|
|
17409
17470
|
}
|
|
17410
17471
|
function make_toolbar$1(gobj) {
|
|
@@ -18650,6 +18711,7 @@ function ac_language_changed$3(gobj, event, kw, src) {
|
|
|
18650
18711
|
if ($container) (0, _yuneta_gobj_js.refresh_language)($container, i18next.t);
|
|
18651
18712
|
hide_popover(gobj);
|
|
18652
18713
|
refresh_tree(gobj);
|
|
18714
|
+
update_find_result(gobj);
|
|
18653
18715
|
return 0;
|
|
18654
18716
|
}
|
|
18655
18717
|
/***************************
|
|
@@ -20164,11 +20226,7 @@ function build_body(shell, t) {
|
|
|
20164
20226
|
let $count = (0, _yuneta_gobj_js.createElement2)([
|
|
20165
20227
|
"span",
|
|
20166
20228
|
{ class: "ROUTEMAP_COUNT is-size-7 has-text-grey is-hidden" },
|
|
20167
|
-
[$count_n
|
|
20168
|
-
"span",
|
|
20169
|
-
{ i18n: "matches" },
|
|
20170
|
-
"matches"
|
|
20171
|
-
]]
|
|
20229
|
+
[$count_n]
|
|
20172
20230
|
]);
|
|
20173
20231
|
let $refs_input = (0, _yuneta_gobj_js.createElement2)(["input", {
|
|
20174
20232
|
class: "ROUTEMAP_REFS_INPUT",
|
|
@@ -20206,7 +20264,7 @@ function build_body(shell, t) {
|
|
|
20206
20264
|
if ($root_li) filter_li($root_li, q, false, __show_refs__);
|
|
20207
20265
|
if (q === "") $count.classList.add("is-hidden");
|
|
20208
20266
|
else {
|
|
20209
|
-
$count_n.textContent = $tree.querySelectorAll(".ROUTEMAP_MATCH").length;
|
|
20267
|
+
$count_n.textContent = t("matches", { count: $tree.querySelectorAll(".ROUTEMAP_MATCH").length });
|
|
20210
20268
|
$count.classList.remove("is-hidden");
|
|
20211
20269
|
}
|
|
20212
20270
|
};
|
|
@@ -58140,10 +58198,6 @@ function make_toolbar(gobj) {
|
|
|
58140
58198
|
"span",
|
|
58141
58199
|
{ class: "GRAPH_FIND_COUNT" },
|
|
58142
58200
|
""
|
|
58143
|
-
], [
|
|
58144
|
-
"span",
|
|
58145
|
-
{ i18n: "matches" },
|
|
58146
|
-
"matches"
|
|
58147
58201
|
]]
|
|
58148
58202
|
]]
|
|
58149
58203
|
],
|
|
@@ -59199,7 +59253,7 @@ function ac_find_result(gobj, event, kw, src) {
|
|
|
59199
59253
|
$count.textContent = "";
|
|
59200
59254
|
return 0;
|
|
59201
59255
|
}
|
|
59202
|
-
$count.textContent =
|
|
59256
|
+
$count.textContent = (0, i18next.t)("matches", { count: kw && kw.matches || 0 });
|
|
59203
59257
|
$result.classList.remove("is-hidden");
|
|
59204
59258
|
return 0;
|
|
59205
59259
|
}
|
|
@@ -62428,7 +62482,7 @@ function open_card(gobj, logical, title, icon, tags, event, kw) {
|
|
|
62428
62482
|
let $tags = tags.map((tag) => {
|
|
62429
62483
|
return [
|
|
62430
62484
|
"span",
|
|
62431
|
-
{ class: `${logical}_TAG tag ${tag.style || "
|
|
62485
|
+
{ class: `${logical}_TAG tag ${tag.style || ""}`.trimEnd() },
|
|
62432
62486
|
tag.i18n ? [[
|
|
62433
62487
|
"span",
|
|
62434
62488
|
{ i18n: tag.i18n },
|
|
@@ -62575,7 +62629,7 @@ function render_topics(gobj, $body) {
|
|
|
62575
62629
|
topic.system_topic ? [
|
|
62576
62630
|
"span",
|
|
62577
62631
|
{
|
|
62578
|
-
class: "SCHEMA_TOPIC_SYSTEM tag
|
|
62632
|
+
class: "SCHEMA_TOPIC_SYSTEM tag ml-2",
|
|
62579
62633
|
i18n: "system"
|
|
62580
62634
|
},
|
|
62581
62635
|
(0, i18next.t)("system")
|
|
@@ -62924,7 +62978,7 @@ function column_row(gobj, topic, col, index, readonly, twice) {
|
|
|
62924
62978
|
let $flags = flags.map((flag) => {
|
|
62925
62979
|
return [
|
|
62926
62980
|
"span",
|
|
62927
|
-
{ class: `SCHEMA_COL_FLAG tag
|
|
62981
|
+
{ class: `SCHEMA_COL_FLAG tag ${yui_tint(flag_style(flag))}`.trimEnd() },
|
|
62928
62982
|
`${flag}`
|
|
62929
62983
|
];
|
|
62930
62984
|
});
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -39,6 +39,75 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
enumerable: true
|
|
40
40
|
}) : target, mod));
|
|
41
41
|
//#endregion
|
|
42
|
+
//#region src/bulma_tint.js
|
|
43
|
+
/***********************************************************************
|
|
44
|
+
* bulma_tint.js
|
|
45
|
+
*
|
|
46
|
+
* Bulma's "soften this colour" modifier, applied so that it cannot
|
|
47
|
+
* be applied alone.
|
|
48
|
+
*
|
|
49
|
+
* WHY THIS IS A FUNCTION AND NOT A STRING. 'light' is one of
|
|
50
|
+
* Bulma's colours, so `is-light` is BOTH a colour and the modifier
|
|
51
|
+
* that softens another one. A compound selector may match the same
|
|
52
|
+
* class twice, so `.tag.is-light.is-light` matches an element that
|
|
53
|
+
* carries the class ONCE -- and that rule sets the text to
|
|
54
|
+
* var(--bulma-light-light-invert-l), WHICH BULMA NEVER DEFINES:
|
|
55
|
+
* referenced four times in bulma.css, declared zero times. The
|
|
56
|
+
* colour declaration is then invalid while the background still
|
|
57
|
+
* resolves, so what is left on screen is a pill with no readable
|
|
58
|
+
* label.
|
|
59
|
+
*
|
|
60
|
+
* Two selectors of Bulma 1 reach that undefined variable:
|
|
61
|
+
*
|
|
62
|
+
* .tag.is-light.is-light
|
|
63
|
+
* .notification.is-light.is-light
|
|
64
|
+
*
|
|
65
|
+
* It cost three unreadable chips in the schema editor -- the topic
|
|
66
|
+
* count, the schema version, and every column flag with no colour of
|
|
67
|
+
* its own ('writable', 'image', 'wild'...). They read as empty
|
|
68
|
+
* pills, which looks like a bug in the DATA and is a missing CSS
|
|
69
|
+
* variable.
|
|
70
|
+
*
|
|
71
|
+
* The neutral chip is a bare element with no modifier at all: it
|
|
72
|
+
* takes --bulma-background-l and --bulma-text-l, follows the theme
|
|
73
|
+
* and stays legible in both.
|
|
74
|
+
*
|
|
75
|
+
* Copyright (c) 2026, ArtGins.
|
|
76
|
+
* All Rights Reserved.
|
|
77
|
+
***********************************************************************/
|
|
78
|
+
var TINTABLE = [
|
|
79
|
+
"primary",
|
|
80
|
+
"link",
|
|
81
|
+
"info",
|
|
82
|
+
"success",
|
|
83
|
+
"warning",
|
|
84
|
+
"danger",
|
|
85
|
+
"black",
|
|
86
|
+
"dark",
|
|
87
|
+
"white",
|
|
88
|
+
"text",
|
|
89
|
+
"ghost"
|
|
90
|
+
];
|
|
91
|
+
/***************************************************************
|
|
92
|
+
* A softened colour, or nothing.
|
|
93
|
+
*
|
|
94
|
+
* yui_tint("warning") -> "is-warning is-light"
|
|
95
|
+
* yui_tint("is-warning") -> "is-warning is-light"
|
|
96
|
+
* yui_tint("light") -> "" (there is nothing to soften)
|
|
97
|
+
* yui_tint("") -> ""
|
|
98
|
+
* yui_tint(undefined) -> ""
|
|
99
|
+
*
|
|
100
|
+
* Anything it does not recognise comes back empty rather than being
|
|
101
|
+
* passed through: a class this function cannot vouch for is exactly the
|
|
102
|
+
* case it exists to stop.
|
|
103
|
+
***************************************************************/
|
|
104
|
+
function yui_tint(colour) {
|
|
105
|
+
if (typeof colour !== "string") return "";
|
|
106
|
+
const name = colour.trim().replace(/^is-/, "");
|
|
107
|
+
if (TINTABLE.indexOf(name) < 0) return "";
|
|
108
|
+
return `is-${name} is-light`;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
42
111
|
//#region src/shell_focus_trap.js
|
|
43
112
|
/***********************************************************************
|
|
44
113
|
* shell_focus_trap.js
|
|
@@ -2853,7 +2922,7 @@ function show_notification(shell, kind, message, opts) {
|
|
|
2853
2922
|
let $note = createElement2([
|
|
2854
2923
|
"div",
|
|
2855
2924
|
{
|
|
2856
|
-
class: `TOAST notification yui-notification
|
|
2925
|
+
class: `TOAST notification yui-notification ${yui_tint(kind)}`,
|
|
2857
2926
|
role: kind === "danger" ? "alert" : "status"
|
|
2858
2927
|
},
|
|
2859
2928
|
[["button", {
|
|
@@ -10916,10 +10985,6 @@ function make_toolbar$5(gobj) {
|
|
|
10916
10985
|
"span",
|
|
10917
10986
|
{ class: "JSON_GRAPH_FIND_COUNT" },
|
|
10918
10987
|
""
|
|
10919
|
-
], [
|
|
10920
|
-
"span",
|
|
10921
|
-
{ i18n: "matches" },
|
|
10922
|
-
"matches"
|
|
10923
10988
|
]]
|
|
10924
10989
|
]]);
|
|
10925
10990
|
let center_items = [];
|
|
@@ -11445,7 +11510,7 @@ function update_find_result$1(gobj) {
|
|
|
11445
11510
|
priv.$find_count.textContent = "";
|
|
11446
11511
|
return;
|
|
11447
11512
|
}
|
|
11448
|
-
priv.$find_count.textContent =
|
|
11513
|
+
priv.$find_count.textContent = t("matches", { count: priv.match_count });
|
|
11449
11514
|
priv.$find_result.classList.remove("is-hidden");
|
|
11450
11515
|
}
|
|
11451
11516
|
/************************************************************
|
|
@@ -17363,10 +17428,6 @@ function make_find_items(gobj) {
|
|
|
17363
17428
|
"span",
|
|
17364
17429
|
{ class: "GOBJ_TREE_FIND_COUNT" },
|
|
17365
17430
|
""
|
|
17366
|
-
], [
|
|
17367
|
-
"span",
|
|
17368
|
-
{ i18n: "matches" },
|
|
17369
|
-
t("matches")
|
|
17370
17431
|
]]
|
|
17371
17432
|
]];
|
|
17372
17433
|
}
|
|
@@ -17399,7 +17460,7 @@ function update_find_result(gobj) {
|
|
|
17399
17460
|
priv.$find_count.textContent = "";
|
|
17400
17461
|
return;
|
|
17401
17462
|
}
|
|
17402
|
-
priv.$find_count.textContent =
|
|
17463
|
+
priv.$find_count.textContent = t("matches", { count: priv.match_count });
|
|
17403
17464
|
priv.$find_result.classList.remove("is-hidden");
|
|
17404
17465
|
}
|
|
17405
17466
|
function make_toolbar$1(gobj) {
|
|
@@ -18645,6 +18706,7 @@ function ac_language_changed$3(gobj, event, kw, src) {
|
|
|
18645
18706
|
if ($container) refresh_language($container, t);
|
|
18646
18707
|
hide_popover(gobj);
|
|
18647
18708
|
refresh_tree(gobj);
|
|
18709
|
+
update_find_result(gobj);
|
|
18648
18710
|
return 0;
|
|
18649
18711
|
}
|
|
18650
18712
|
/***************************
|
|
@@ -20159,11 +20221,7 @@ function build_body(shell, t) {
|
|
|
20159
20221
|
let $count = createElement2([
|
|
20160
20222
|
"span",
|
|
20161
20223
|
{ class: "ROUTEMAP_COUNT is-size-7 has-text-grey is-hidden" },
|
|
20162
|
-
[$count_n
|
|
20163
|
-
"span",
|
|
20164
|
-
{ i18n: "matches" },
|
|
20165
|
-
"matches"
|
|
20166
|
-
]]
|
|
20224
|
+
[$count_n]
|
|
20167
20225
|
]);
|
|
20168
20226
|
let $refs_input = createElement2(["input", {
|
|
20169
20227
|
class: "ROUTEMAP_REFS_INPUT",
|
|
@@ -20201,7 +20259,7 @@ function build_body(shell, t) {
|
|
|
20201
20259
|
if ($root_li) filter_li($root_li, q, false, __show_refs__);
|
|
20202
20260
|
if (q === "") $count.classList.add("is-hidden");
|
|
20203
20261
|
else {
|
|
20204
|
-
$count_n.textContent = $tree.querySelectorAll(".ROUTEMAP_MATCH").length;
|
|
20262
|
+
$count_n.textContent = t("matches", { count: $tree.querySelectorAll(".ROUTEMAP_MATCH").length });
|
|
20205
20263
|
$count.classList.remove("is-hidden");
|
|
20206
20264
|
}
|
|
20207
20265
|
};
|
|
@@ -58135,10 +58193,6 @@ function make_toolbar(gobj) {
|
|
|
58135
58193
|
"span",
|
|
58136
58194
|
{ class: "GRAPH_FIND_COUNT" },
|
|
58137
58195
|
""
|
|
58138
|
-
], [
|
|
58139
|
-
"span",
|
|
58140
|
-
{ i18n: "matches" },
|
|
58141
|
-
"matches"
|
|
58142
58196
|
]]
|
|
58143
58197
|
]]
|
|
58144
58198
|
],
|
|
@@ -59194,7 +59248,7 @@ function ac_find_result(gobj, event, kw, src) {
|
|
|
59194
59248
|
$count.textContent = "";
|
|
59195
59249
|
return 0;
|
|
59196
59250
|
}
|
|
59197
|
-
$count.textContent =
|
|
59251
|
+
$count.textContent = t("matches", { count: kw && kw.matches || 0 });
|
|
59198
59252
|
$result.classList.remove("is-hidden");
|
|
59199
59253
|
return 0;
|
|
59200
59254
|
}
|
|
@@ -62423,7 +62477,7 @@ function open_card(gobj, logical, title, icon, tags, event, kw) {
|
|
|
62423
62477
|
let $tags = tags.map((tag) => {
|
|
62424
62478
|
return [
|
|
62425
62479
|
"span",
|
|
62426
|
-
{ class: `${logical}_TAG tag ${tag.style || "
|
|
62480
|
+
{ class: `${logical}_TAG tag ${tag.style || ""}`.trimEnd() },
|
|
62427
62481
|
tag.i18n ? [[
|
|
62428
62482
|
"span",
|
|
62429
62483
|
{ i18n: tag.i18n },
|
|
@@ -62570,7 +62624,7 @@ function render_topics(gobj, $body) {
|
|
|
62570
62624
|
topic.system_topic ? [
|
|
62571
62625
|
"span",
|
|
62572
62626
|
{
|
|
62573
|
-
class: "SCHEMA_TOPIC_SYSTEM tag
|
|
62627
|
+
class: "SCHEMA_TOPIC_SYSTEM tag ml-2",
|
|
62574
62628
|
i18n: "system"
|
|
62575
62629
|
},
|
|
62576
62630
|
t("system")
|
|
@@ -62919,7 +62973,7 @@ function column_row(gobj, topic, col, index, readonly, twice) {
|
|
|
62919
62973
|
let $flags = flags.map((flag) => {
|
|
62920
62974
|
return [
|
|
62921
62975
|
"span",
|
|
62922
|
-
{ class: `SCHEMA_COL_FLAG tag
|
|
62976
|
+
{ class: `SCHEMA_COL_FLAG tag ${yui_tint(flag_style(flag))}`.trimEnd() },
|
|
62923
62977
|
`${flag}`
|
|
62924
62978
|
];
|
|
62925
62979
|
});
|
package/package.json
CHANGED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* bulma_is_light.test.js
|
|
3
|
+
*
|
|
4
|
+
* `is-light` MUST NEVER BE THE ONLY BULMA COLOUR ON AN ELEMENT.
|
|
5
|
+
*
|
|
6
|
+
* 'light' is one of Bulma's colours, so `is-light` is both a colour
|
|
7
|
+
* and the "soften it" modifier. A compound selector may match the
|
|
8
|
+
* same class twice, so `.tag.is-light.is-light` matches an element
|
|
9
|
+
* that carries the class ONCE -- and that rule sets the text to
|
|
10
|
+
* var(--bulma-light-light-invert-l), WHICH BULMA NEVER DEFINES:
|
|
11
|
+
* referenced four times in bulma.css, declared zero times. The
|
|
12
|
+
* colour declaration is then invalid while the background still
|
|
13
|
+
* resolves, and what is left on screen is a pill with no readable
|
|
14
|
+
* label.
|
|
15
|
+
*
|
|
16
|
+
* Two selectors in Bulma 1 reach that undefined variable:
|
|
17
|
+
*
|
|
18
|
+
* .tag.is-light.is-light
|
|
19
|
+
* .notification.is-light.is-light
|
|
20
|
+
*
|
|
21
|
+
* It cost three unreadable chips in the schema editor -- the topic
|
|
22
|
+
* count, the schema version, and every column flag without a colour
|
|
23
|
+
* of its own ('writable', 'image', 'wild'...). They looked like
|
|
24
|
+
* empty pills, which reads as a rendering bug in the data and is a
|
|
25
|
+
* missing CSS variable.
|
|
26
|
+
*
|
|
27
|
+
* THIS IS A SOURCE SCAN AND NOT A RENDER TEST ON PURPOSE: jsdom does
|
|
28
|
+
* not load Bulma, so no mounted test can see the contrast. What can
|
|
29
|
+
* be checked is the rule that produces it.
|
|
30
|
+
*
|
|
31
|
+
* Copyright (c) 2026, ArtGins.
|
|
32
|
+
* All Rights Reserved.
|
|
33
|
+
***********************************************************************/
|
|
34
|
+
import {describe, it, expect} from "vitest";
|
|
35
|
+
import {readdirSync, readFileSync} from "node:fs";
|
|
36
|
+
import {dirname, join} from "node:path";
|
|
37
|
+
import {fileURLToPath} from "node:url";
|
|
38
|
+
|
|
39
|
+
import {yui_tint, TINTABLE} from "./bulma_tint.js";
|
|
40
|
+
|
|
41
|
+
const SRC = dirname(fileURLToPath(import.meta.url));
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
* The ONE file allowed to write the literal: yui_tint() emits it only
|
|
45
|
+
* next to a colour it has already checked against TINTABLE, and the
|
|
46
|
+
* colour is interpolated, so no source scan can see it. What guards
|
|
47
|
+
* that file is the behaviour suite at the bottom, not the scan.
|
|
48
|
+
*/
|
|
49
|
+
const EMITTER = "bulma_tint.js";
|
|
50
|
+
|
|
51
|
+
/* Every Bulma colour except 'light' itself. */
|
|
52
|
+
const COLOURS = [
|
|
53
|
+
"is-primary", "is-link", "is-info", "is-success", "is-warning",
|
|
54
|
+
"is-danger", "is-black", "is-dark", "is-white", "is-text", "is-ghost"
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* How far around an occurrence to look for its colour. The colour does
|
|
59
|
+
* not have to sit on the same line: it may come from a ternary, from a
|
|
60
|
+
* string concatenation split across two lines, or from a variable
|
|
61
|
+
* interpolated just before. A window is what covers all three without
|
|
62
|
+
* parsing javascript.
|
|
63
|
+
*/
|
|
64
|
+
const WINDOW = 260;
|
|
65
|
+
|
|
66
|
+
function offenders(source)
|
|
67
|
+
{
|
|
68
|
+
let out = [];
|
|
69
|
+
let at = source.indexOf("is-light");
|
|
70
|
+
while(at >= 0) {
|
|
71
|
+
const from = Math.max(0, at - WINDOW);
|
|
72
|
+
const to = Math.min(source.length, at + WINDOW);
|
|
73
|
+
const around = source.slice(from, to);
|
|
74
|
+
|
|
75
|
+
/* A comment ABOUT the rule is not a use of it. */
|
|
76
|
+
const line_start = source.lastIndexOf("\n", at) + 1;
|
|
77
|
+
const line = source.slice(line_start, source.indexOf("\n", at));
|
|
78
|
+
const is_comment = /^\s*(\*|\/\/|\/\*)/.test(line);
|
|
79
|
+
|
|
80
|
+
if(!is_comment && !COLOURS.some((c) => around.includes(c))) {
|
|
81
|
+
out.push(line.trim());
|
|
82
|
+
}
|
|
83
|
+
at = source.indexOf("is-light", at + 1);
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
describe("bulma: is-light never travels alone", () => {
|
|
89
|
+
const files = readdirSync(SRC).filter(
|
|
90
|
+
(f) => f.endsWith(".js") && !f.endsWith(".test.js") && f !== EMITTER
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
it("scans every source file of the library", () => {
|
|
94
|
+
expect(files.length).toBeGreaterThan(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
for(let file of files) {
|
|
98
|
+
it(`${file} carries no colourless is-light`, () => {
|
|
99
|
+
const source = readFileSync(join(SRC, file), "utf-8");
|
|
100
|
+
expect(offenders(source)).toEqual([]);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("the scan itself", () => {
|
|
106
|
+
it("catches a colourless is-light", () => {
|
|
107
|
+
const bad = 'return ["span", {class: `SOMETHING tag is-light`}, "x"];';
|
|
108
|
+
expect(offenders(bad)).toHaveLength(1);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("accepts one that travels with a colour", () => {
|
|
112
|
+
const good = 'return ["span", {class: `SOMETHING tag is-warning is-light`}, "x"];';
|
|
113
|
+
expect(offenders(good)).toEqual([]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("accepts a colour that arrives split across lines", () => {
|
|
117
|
+
const good = 'const c = `tag ${x ? "is-danger" : "is-warning"}`\n'
|
|
118
|
+
+ ' + " is-light";';
|
|
119
|
+
expect(offenders(good)).toEqual([]);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe("yui_tint", () => {
|
|
124
|
+
it("softens every colour that CAN be softened", () => {
|
|
125
|
+
for(let name of TINTABLE) {
|
|
126
|
+
expect(yui_tint(name)).toBe(`is-${name} is-light`);
|
|
127
|
+
expect(yui_tint(`is-${name}`)).toBe(`is-${name} is-light`);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("refuses to soften 'light' with itself", () => {
|
|
132
|
+
expect(yui_tint("light")).toBe("");
|
|
133
|
+
expect(yui_tint("is-light")).toBe("");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("answers nothing for nothing", () => {
|
|
137
|
+
expect(yui_tint("")).toBe("");
|
|
138
|
+
expect(yui_tint(undefined)).toBe("");
|
|
139
|
+
expect(yui_tint(null)).toBe("");
|
|
140
|
+
expect(yui_tint(7)).toBe("");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("does not pass through what it cannot vouch for", () => {
|
|
144
|
+
/* A class this function cannot recognise is exactly the case it
|
|
145
|
+
* exists to stop, so it comes back empty instead of forwarded. */
|
|
146
|
+
expect(yui_tint("is-whatever")).toBe("");
|
|
147
|
+
expect(yui_tint("has-text-danger")).toBe("");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("never emits is-light on its own", () => {
|
|
151
|
+
for(let input of TINTABLE.concat(
|
|
152
|
+
["light", "is-light", "", "nope", "is-nope"]
|
|
153
|
+
)) {
|
|
154
|
+
const out = yui_tint(input);
|
|
155
|
+
if(out.includes("is-light")) {
|
|
156
|
+
expect(out).toMatch(/is-(primary|link|info|success|warning|danger|black|dark|white|text|ghost) is-light/);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* bulma_tint.js
|
|
3
|
+
*
|
|
4
|
+
* Bulma's "soften this colour" modifier, applied so that it cannot
|
|
5
|
+
* be applied alone.
|
|
6
|
+
*
|
|
7
|
+
* WHY THIS IS A FUNCTION AND NOT A STRING. 'light' is one of
|
|
8
|
+
* Bulma's colours, so `is-light` is BOTH a colour and the modifier
|
|
9
|
+
* that softens another one. A compound selector may match the same
|
|
10
|
+
* class twice, so `.tag.is-light.is-light` matches an element that
|
|
11
|
+
* carries the class ONCE -- and that rule sets the text to
|
|
12
|
+
* var(--bulma-light-light-invert-l), WHICH BULMA NEVER DEFINES:
|
|
13
|
+
* referenced four times in bulma.css, declared zero times. The
|
|
14
|
+
* colour declaration is then invalid while the background still
|
|
15
|
+
* resolves, so what is left on screen is a pill with no readable
|
|
16
|
+
* label.
|
|
17
|
+
*
|
|
18
|
+
* Two selectors of Bulma 1 reach that undefined variable:
|
|
19
|
+
*
|
|
20
|
+
* .tag.is-light.is-light
|
|
21
|
+
* .notification.is-light.is-light
|
|
22
|
+
*
|
|
23
|
+
* It cost three unreadable chips in the schema editor -- the topic
|
|
24
|
+
* count, the schema version, and every column flag with no colour of
|
|
25
|
+
* its own ('writable', 'image', 'wild'...). They read as empty
|
|
26
|
+
* pills, which looks like a bug in the DATA and is a missing CSS
|
|
27
|
+
* variable.
|
|
28
|
+
*
|
|
29
|
+
* The neutral chip is a bare element with no modifier at all: it
|
|
30
|
+
* takes --bulma-background-l and --bulma-text-l, follows the theme
|
|
31
|
+
* and stays legible in both.
|
|
32
|
+
*
|
|
33
|
+
* Copyright (c) 2026, ArtGins.
|
|
34
|
+
* All Rights Reserved.
|
|
35
|
+
***********************************************************************/
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Bulma's colours, minus 'light' -- which is exactly the one that must
|
|
39
|
+
* never be softened with itself.
|
|
40
|
+
*/
|
|
41
|
+
const TINTABLE = [
|
|
42
|
+
"primary", "link", "info", "success", "warning",
|
|
43
|
+
"danger", "black", "dark", "white", "text", "ghost"
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/***************************************************************
|
|
47
|
+
* A softened colour, or nothing.
|
|
48
|
+
*
|
|
49
|
+
* yui_tint("warning") -> "is-warning is-light"
|
|
50
|
+
* yui_tint("is-warning") -> "is-warning is-light"
|
|
51
|
+
* yui_tint("light") -> "" (there is nothing to soften)
|
|
52
|
+
* yui_tint("") -> ""
|
|
53
|
+
* yui_tint(undefined) -> ""
|
|
54
|
+
*
|
|
55
|
+
* Anything it does not recognise comes back empty rather than being
|
|
56
|
+
* passed through: a class this function cannot vouch for is exactly the
|
|
57
|
+
* case it exists to stop.
|
|
58
|
+
***************************************************************/
|
|
59
|
+
function yui_tint(colour)
|
|
60
|
+
{
|
|
61
|
+
if(typeof colour !== "string") {
|
|
62
|
+
return "";
|
|
63
|
+
}
|
|
64
|
+
const name = colour.trim().replace(/^is-/, "");
|
|
65
|
+
if(TINTABLE.indexOf(name) < 0) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
return `is-${name} is-light`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {yui_tint, TINTABLE};
|
|
@@ -914,16 +914,25 @@ function make_find_items(gobj)
|
|
|
914
914
|
|
|
915
915
|
return [
|
|
916
916
|
$find_control,
|
|
917
|
-
/*
|
|
918
|
-
* is
|
|
919
|
-
*
|
|
920
|
-
*
|
|
921
|
-
*
|
|
917
|
+
/* ONE counted span, not a number beside a word: "1 matches"
|
|
918
|
+
* is what two spans always produce, because nothing there can
|
|
919
|
+
* see the count. `t(key, {count})` picks `matches_one` for
|
|
920
|
+
* one and falls back to the base key for the rest, in both
|
|
921
|
+
* languages.
|
|
922
|
+
*
|
|
923
|
+
* And NO `i18n` attribute on it: refresh_language()
|
|
924
|
+
* re-translates whatever carries one by calling t() WITHOUT
|
|
925
|
+
* the count, which would put the plural back over the
|
|
926
|
+
* singular. A counted word is re-rendered by its owner
|
|
927
|
+
* instead -- which is why the language action below repaints
|
|
928
|
+
* this chip. */
|
|
929
|
+
/* `display:flex` inline and NOT the `is-flex` helper -- both
|
|
930
|
+
* Bulma helpers carry !important, so `is-hidden is-flex` on one
|
|
931
|
+
* element is decided by stylesheet order. */
|
|
922
932
|
['div', {class: 'GOBJ_TREE_FIND_RESULT is-hidden',
|
|
923
933
|
style: 'display:flex; align-items:center; gap:.3rem; ' +
|
|
924
934
|
'margin-right:.5rem; font-size:.85rem;'}, [
|
|
925
|
-
['span', {class: 'GOBJ_TREE_FIND_COUNT'}, '']
|
|
926
|
-
['span', {i18n: 'matches'}, t('matches')]
|
|
935
|
+
['span', {class: 'GOBJ_TREE_FIND_COUNT'}, '']
|
|
927
936
|
]]
|
|
928
937
|
];
|
|
929
938
|
}
|
|
@@ -964,7 +973,7 @@ function update_find_result(gobj)
|
|
|
964
973
|
priv.$find_count.textContent = "";
|
|
965
974
|
return;
|
|
966
975
|
}
|
|
967
|
-
priv.$find_count.textContent =
|
|
976
|
+
priv.$find_count.textContent = t("matches", {count: priv.match_count});
|
|
968
977
|
priv.$find_result.classList.remove("is-hidden");
|
|
969
978
|
}
|
|
970
979
|
|
|
@@ -2834,6 +2843,9 @@ function ac_language_changed(gobj, event, kw, src)
|
|
|
2834
2843
|
}
|
|
2835
2844
|
hide_popover(gobj);
|
|
2836
2845
|
refresh_tree(gobj);
|
|
2846
|
+
/* The count chip carries a COUNTED word, which has no key for
|
|
2847
|
+
* refresh_language() to re-translate: its owner has to repaint it. */
|
|
2848
|
+
update_find_result(gobj);
|
|
2837
2849
|
return 0;
|
|
2838
2850
|
}
|
|
2839
2851
|
|
package/src/c_yui_json_graph.js
CHANGED
|
@@ -599,17 +599,25 @@ function make_toolbar(gobj)
|
|
|
599
599
|
*/
|
|
600
600
|
let left_items = yui_graph_fold_items(gobj, toolbar_wide).concat([
|
|
601
601
|
$find_control,
|
|
602
|
-
/*
|
|
603
|
-
* is
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
602
|
+
/* ONE counted span, not a number beside a word: "1 matches"
|
|
603
|
+
* is what two spans always produce, because nothing there can
|
|
604
|
+
* see the count. `t(key, {count})` picks `matches_one` for
|
|
605
|
+
* one and falls back to the base key for the rest, in both
|
|
606
|
+
* languages.
|
|
607
|
+
*
|
|
608
|
+
* And NO `i18n` attribute on it: refresh_language()
|
|
609
|
+
* re-translates whatever carries one by calling t() WITHOUT
|
|
610
|
+
* the count, which would put the plural back over the
|
|
611
|
+
* singular. Nothing re-translates it, so it is repainted on
|
|
612
|
+
* the next find -- which is the next keystroke, and it is
|
|
613
|
+
* hidden while the box is empty. */
|
|
614
|
+
/* `display:flex` inline and NOT the `is-flex` helper — both
|
|
615
|
+
* Bulma helpers carry !important, so `is-hidden is-flex` on one
|
|
616
|
+
* element is decided by stylesheet order. */
|
|
608
617
|
['div', {class: 'JSON_GRAPH_FIND_RESULT is-hidden',
|
|
609
618
|
style: 'display:flex; align-items:center; gap:.3rem; ' +
|
|
610
619
|
'margin-right:.5rem; font-size:.85rem;'}, [
|
|
611
|
-
['span', {class: 'JSON_GRAPH_FIND_COUNT'}, '']
|
|
612
|
-
['span', {i18n: 'matches'}, 'matches']
|
|
620
|
+
['span', {class: 'JSON_GRAPH_FIND_COUNT'}, '']
|
|
613
621
|
]]
|
|
614
622
|
]);
|
|
615
623
|
let center_items = [];
|
|
@@ -1537,7 +1545,7 @@ function update_find_result(gobj)
|
|
|
1537
1545
|
priv.$find_count.textContent = "";
|
|
1538
1546
|
return;
|
|
1539
1547
|
}
|
|
1540
|
-
priv.$find_count.textContent =
|
|
1548
|
+
priv.$find_count.textContent = t("matches", {count: priv.match_count});
|
|
1541
1549
|
priv.$find_result.classList.remove("is-hidden");
|
|
1542
1550
|
}
|
|
1543
1551
|
|
|
@@ -109,6 +109,7 @@ import {
|
|
|
109
109
|
is_object,
|
|
110
110
|
empty_string,
|
|
111
111
|
} from "@yuneta/gobj-js";
|
|
112
|
+
import {yui_tint} from "./bulma_tint.js";
|
|
112
113
|
|
|
113
114
|
import "./c_yui_schema_editor.css";
|
|
114
115
|
|
|
@@ -871,7 +872,24 @@ function render(gobj)
|
|
|
871
872
|
function open_card(gobj, logical, title, icon, tags, event, kw)
|
|
872
873
|
{
|
|
873
874
|
let $tags = tags.map((tag) => {
|
|
874
|
-
|
|
875
|
+
/*
|
|
876
|
+
* `is-light` ALONE IS NOT A NEUTRAL CHIP, IT IS AN UNREADABLE ONE.
|
|
877
|
+
*
|
|
878
|
+
* 'light' is one of Bulma's colours, so `is-light` is both the
|
|
879
|
+
* colour and the modifier -- and `.tag.is-light.is-light` matches
|
|
880
|
+
* an element carrying the class ONCE (a compound selector may
|
|
881
|
+
* match the same class twice). That rule sets the text to
|
|
882
|
+
* var(--bulma-light-light-invert-l), WHICH BULMA NEVER DEFINES:
|
|
883
|
+
* used four times in its stylesheet, declared zero. The colour
|
|
884
|
+
* declaration is then invalid, the background still resolves, and
|
|
885
|
+
* what is left is a pill with no readable label.
|
|
886
|
+
*
|
|
887
|
+
* A bare `.tag` is the neutral chip: it takes --bulma-background-l
|
|
888
|
+
* and --bulma-text-l, so it follows the theme and stays legible in
|
|
889
|
+
* both. `is-light` goes on only NEXT TO a real colour, where the
|
|
890
|
+
* variable it needs does exist.
|
|
891
|
+
*/
|
|
892
|
+
return ["span", {class: `${logical}_TAG tag ${tag.style || ""}`.trimEnd()},
|
|
875
893
|
tag.i18n
|
|
876
894
|
? [["span", {i18n: tag.i18n}, t(tag.i18n)],
|
|
877
895
|
["span", {class: "ml-1"}, `${tag.text}`]]
|
|
@@ -989,7 +1007,7 @@ function render_topics(gobj, $body)
|
|
|
989
1007
|
["td", {class: "SCHEMA_TOPIC_NAME"}, [
|
|
990
1008
|
["span", {class: "has-text-weight-semibold"}, `${topic.name}`],
|
|
991
1009
|
topic.system_topic
|
|
992
|
-
? ["span", {class: "SCHEMA_TOPIC_SYSTEM tag
|
|
1010
|
+
? ["span", {class: "SCHEMA_TOPIC_SYSTEM tag ml-2",
|
|
993
1011
|
i18n: "system"}, t("system")]
|
|
994
1012
|
: ["span", {}, ""],
|
|
995
1013
|
ambiguous
|
|
@@ -1222,7 +1240,13 @@ function column_row(gobj, topic, col, index, readonly, twice)
|
|
|
1222
1240
|
let is_pkey = (col.name === (topic.pkey || "id"));
|
|
1223
1241
|
|
|
1224
1242
|
let $flags = flags.map((flag) => {
|
|
1225
|
-
|
|
1243
|
+
/* The colour carries the meaning, and `is-light` softens it. With
|
|
1244
|
+
* no colour there is nothing to soften: a bare tag is the neutral
|
|
1245
|
+
* one, and `is-light` on its own would erase the label -- see
|
|
1246
|
+
* open_card(). It is why 'writable', 'image', 'wild' and every
|
|
1247
|
+
* other unstyled flag rendered as an empty pill. */
|
|
1248
|
+
return ["span", {class: `SCHEMA_COL_FLAG tag ${yui_tint(flag_style(flag))}`.trimEnd()},
|
|
1249
|
+
`${flag}`];
|
|
1226
1250
|
});
|
|
1227
1251
|
|
|
1228
1252
|
/* An unset `blob` column comes back as `{}`, which is an object and
|
|
@@ -649,9 +649,14 @@ function make_toolbar(gobj)
|
|
|
649
649
|
|
|
650
650
|
let center_items = [
|
|
651
651
|
$find_control,
|
|
652
|
-
/*
|
|
653
|
-
*
|
|
654
|
-
*
|
|
652
|
+
/* ONE counted span, not a number beside a word: "1 matches" is
|
|
653
|
+
* what two spans always produce, because nothing there can see
|
|
654
|
+
* the count. `t(key, {count})` picks `matches_one` for one and
|
|
655
|
+
* falls back to the base key for the rest, in both languages.
|
|
656
|
+
* No `i18n` attribute on it: refresh_language() would call t()
|
|
657
|
+
* WITHOUT the count and put the plural back over the singular.
|
|
658
|
+
* Nothing re-translates it, so it is repainted on the next
|
|
659
|
+
* find, and it is hidden while the box is empty. */
|
|
655
660
|
/* `display:flex` inline and NOT the `is-flex` helper: both Bulma
|
|
656
661
|
* helpers carry !important, so `is-hidden is-flex` on one element
|
|
657
662
|
* is decided by whichever lands later in the stylesheet — and the
|
|
@@ -661,8 +666,7 @@ function make_toolbar(gobj)
|
|
|
661
666
|
['div', {class: 'GRAPH_FIND_RESULT is-hidden',
|
|
662
667
|
style: 'display:flex; align-items:center; gap:.3rem; ' +
|
|
663
668
|
'margin-right:.5rem; font-size:.85rem;'}, [
|
|
664
|
-
['span', {class: 'GRAPH_FIND_COUNT'}, '']
|
|
665
|
-
['span', {i18n: 'matches'}, 'matches']
|
|
669
|
+
['span', {class: 'GRAPH_FIND_COUNT'}, '']
|
|
666
670
|
]]
|
|
667
671
|
];
|
|
668
672
|
|
|
@@ -2485,7 +2489,7 @@ function ac_find_result(gobj, event, kw, src)
|
|
|
2485
2489
|
return 0;
|
|
2486
2490
|
}
|
|
2487
2491
|
|
|
2488
|
-
$count.textContent =
|
|
2492
|
+
$count.textContent = t("matches", {count: (kw && kw.matches) || 0});
|
|
2489
2493
|
$result.classList.remove("is-hidden");
|
|
2490
2494
|
return 0;
|
|
2491
2495
|
}
|
package/src/shell_modals.js
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
empty_string,
|
|
34
34
|
log_warning,
|
|
35
35
|
} from "@yuneta/gobj-js";
|
|
36
|
+
import {yui_tint} from "./bulma_tint.js";
|
|
36
37
|
|
|
37
38
|
import {
|
|
38
39
|
activate_focus_trap_on,
|
|
@@ -121,7 +122,7 @@ function show_notification(shell, kind, message, opts)
|
|
|
121
122
|
? {i18n: message}
|
|
122
123
|
: {};
|
|
123
124
|
let $note = createElement2(
|
|
124
|
-
["div", {class: `TOAST notification yui-notification
|
|
125
|
+
["div", {class: `TOAST notification yui-notification ${yui_tint(kind)}`,
|
|
125
126
|
role: kind === "danger" ? "alert" : "status"},
|
|
126
127
|
[
|
|
127
128
|
["button", {class: "TOAST_CLOSE delete", "aria-label": "close"}],
|
package/src/shell_route_map.js
CHANGED
|
@@ -257,11 +257,19 @@ function build_body(shell, t)
|
|
|
257
257
|
);
|
|
258
258
|
attach_clear($search_ctrl, $search);
|
|
259
259
|
|
|
260
|
-
/* Live match counter next to the filter.
|
|
260
|
+
/* Live match counter next to the filter.
|
|
261
|
+
*
|
|
262
|
+
* ONE counted span, not a number beside a word: "1 matches" is what
|
|
263
|
+
* two spans always produce, because nothing there can see the
|
|
264
|
+
* count. `t(key, {count})` picks `matches_one` for one and falls
|
|
265
|
+
* back to the base key for the rest. No `i18n` attribute on it --
|
|
266
|
+
* refresh_language() would call t() WITHOUT the count and put the
|
|
267
|
+
* plural back over the singular -- so the filter, which runs on
|
|
268
|
+
* every keystroke, is what repaints it. */
|
|
261
269
|
let $count_n = createElement2(["span", {class: "ROUTEMAP_COUNT_N"}, ""]);
|
|
262
270
|
let $count = createElement2(
|
|
263
271
|
["span", {class: "ROUTEMAP_COUNT is-size-7 has-text-grey is-hidden"},
|
|
264
|
-
[$count_n
|
|
272
|
+
[$count_n]]
|
|
265
273
|
);
|
|
266
274
|
/* References are hidden by default and revealed by this toggle.
|
|
267
275
|
* Remembered for the page session (not localStorage): it is a
|
|
@@ -294,7 +302,9 @@ function build_body(shell, t)
|
|
|
294
302
|
if(q === "") {
|
|
295
303
|
$count.classList.add("is-hidden");
|
|
296
304
|
} else {
|
|
297
|
-
$count_n.textContent =
|
|
305
|
+
$count_n.textContent = t("matches", {
|
|
306
|
+
count: $tree.querySelectorAll(".ROUTEMAP_MATCH").length
|
|
307
|
+
});
|
|
298
308
|
$count.classList.remove("is-hidden");
|
|
299
309
|
}
|
|
300
310
|
};
|