@yuneta/gobj-ui 7.26.28 → 7.26.30
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 +225 -62
- package/dist/gobj-ui.cjs.js +401 -122
- package/dist/gobj-ui.es.js +401 -123
- package/index.js +1 -0
- package/package.json +4 -2
- package/scripts/validate-locales.mjs +565 -177
- package/src/c_yui_form.js +5 -3
- package/src/c_yui_gobj_tree_js.js +1 -1
- package/src/c_yui_nav.js +164 -29
- package/src/c_yui_period.js +3 -3
- package/src/c_yui_schema_editor.js +22 -18
- package/src/c_yui_shell.css +84 -18
- package/src/c_yui_shell.js +149 -68
- package/src/c_yui_treedb_graph.js +3 -4
- package/src/c_yui_treedb_topic_with_form.js +15 -15
- package/src/c_yui_treedb_topics.js +8 -8
- package/src/c_yui_window_manager.js +5 -2
- package/src/i18n_key_functions.js +112 -23
- package/src/i18n_key_functions.test.js +329 -8
- package/src/nav_cards_helpers.js +21 -5
- package/src/nav_item_badge.wiring.test.js +564 -0
- package/src/shell_route_map.js +2 -2
- package/src/yui_badge.js +145 -0
- package/src/yui_badge.test.js +148 -0
- package/src/yui_gclass_view.js +1 -1
- package/src/yui_time.js +1 -1
package/src/c_yui_form.js
CHANGED
|
@@ -805,11 +805,13 @@ function build_html_form(gobj, $form, prefix, template)
|
|
|
805
805
|
}
|
|
806
806
|
|
|
807
807
|
/*
|
|
808
|
-
* Build the html field and add to form, if <tag> is not-null
|
|
808
|
+
* Build the html field and add to form, if <tag> is not-null.
|
|
809
|
+
* Its label and options are the schema's: column names and
|
|
810
|
+
* headers, data.
|
|
809
811
|
*/
|
|
810
812
|
if(html_field_conf.tag) {
|
|
811
|
-
let $field = create_form_field(gobj, $form, prefix, field_desc, html_field_conf);
|
|
812
|
-
name_form_control($field, html_field_conf);
|
|
813
|
+
let $field = create_form_field(gobj, $form, prefix, field_desc, /* i18n: data */ html_field_conf);
|
|
814
|
+
name_form_control($field, /* i18n: data */ html_field_conf);
|
|
813
815
|
$form.appendChild($field);
|
|
814
816
|
}
|
|
815
817
|
});
|
|
@@ -2862,7 +2862,7 @@ function ac_open_gclass(gobj, event, kw, src)
|
|
|
2862
2862
|
close_gclass_view(priv.gclass_view);
|
|
2863
2863
|
priv.gclass_view = null;
|
|
2864
2864
|
|
|
2865
|
-
priv.gclass_view = open_gclass_view(gobj, node_data.gclass, {
|
|
2865
|
+
priv.gclass_view = open_gclass_view(gobj, /* i18n: data */ node_data.gclass, {
|
|
2866
2866
|
title_prefix: gobj_yuno_name() || "",
|
|
2867
2867
|
/* Which state THIS gobj is in. The description describes the
|
|
2868
2868
|
* CLASS and cannot carry it, and the tree is the only party
|
package/src/c_yui_nav.js
CHANGED
|
@@ -33,11 +33,29 @@
|
|
|
33
33
|
*
|
|
34
34
|
* Each item supports:
|
|
35
35
|
* id, name, icon (CSS class or svg id), route, disabled
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
36
|
+
* badge — a count, or a short text, carried by the item:
|
|
37
|
+
* the toolbar badge's rules, helper and live
|
|
38
|
+
* region (yui_badge.js) -- 0 / "" / null show
|
|
39
|
+
* none, over 99 reads "99+". It sits on the
|
|
40
|
+
* corner of the icon where the item is an icon
|
|
41
|
+
* over its label (icon-bar, cards, a stacked
|
|
42
|
+
* item) or an icon alone, and after the label
|
|
43
|
+
* where the item reads as a line (vertical,
|
|
44
|
+
* drawer, submenu, tabs, an accordion head).
|
|
45
|
+
* The field only seeds the paint: a count is a
|
|
46
|
+
* RUNTIME fact, set by
|
|
47
|
+
* yui_shell_set_nav_item_badge(shell, id, value,
|
|
48
|
+
* tone), which reaches every nav drawing the id.
|
|
49
|
+
* Not drawn by "breadcrumb", deliberately: a
|
|
50
|
+
* crumb names a step of the way in, and the
|
|
51
|
+
* count belongs to the menu entry that leads
|
|
52
|
+
* there -- a trail with counts on it reads as a
|
|
53
|
+
* list of destinations, which is what a
|
|
54
|
+
* breadcrumb is not. ("backbar" has no items.)
|
|
55
|
+
* badge_tone — "danger" (the default look), "warning",
|
|
56
|
+
* "info", "success", "primary" or "link":
|
|
57
|
+
* Bulma's is-<tone> on the badge. An unknown
|
|
58
|
+
* one is logged and drawn with the default look.
|
|
41
59
|
* class — extra CSS class(es) on the item (tabs layout), e.g.
|
|
42
60
|
* a per-item state colour like "yui-nav-disconnected"
|
|
43
61
|
* closable — render a trailing ✕ that emits EV_NAV_ITEM_CLOSE
|
|
@@ -48,6 +66,13 @@
|
|
|
48
66
|
* active highlight restored. The app_config path is simply the
|
|
49
67
|
* first, startup caller of the same mechanism.
|
|
50
68
|
*
|
|
69
|
+
* One item's badge is set at runtime by sending EV_SET_ITEM_BADGE
|
|
70
|
+
* {item_id, badge, badge_tone}: painted in place, no rebuild, and
|
|
71
|
+
* KEPT in the `badges` attr, which wins over the item's own fields
|
|
72
|
+
* in every later rebuild. The shell sends it to all its navs
|
|
73
|
+
* (yui_shell_set_nav_item_badge) and hands `badges` to every nav
|
|
74
|
+
* it creates.
|
|
75
|
+
*
|
|
51
76
|
* In addition to navigable items, secondary navs accept two
|
|
52
77
|
* decorative item kinds for in-place visual grouping:
|
|
53
78
|
*
|
|
@@ -71,6 +96,7 @@ import {
|
|
|
71
96
|
gobj_parent,
|
|
72
97
|
gobj_read_attr, gobj_read_pointer_attr, gobj_write_attr,
|
|
73
98
|
gobj_publish_event,
|
|
99
|
+
gobj_short_name,
|
|
74
100
|
createElement2, empty_string, is_array, is_object, is_string,
|
|
75
101
|
} from "@yuneta/gobj-js";
|
|
76
102
|
|
|
@@ -83,6 +109,7 @@ import {
|
|
|
83
109
|
bulma_hidden_class,
|
|
84
110
|
} from "./shell_show_on.js";
|
|
85
111
|
import {text_node} from "./yui_text.js";
|
|
112
|
+
import {badge_node, badge_tone, paint_badge} from "./yui_badge.js";
|
|
86
113
|
|
|
87
114
|
/***************************************************************
|
|
88
115
|
* Constants
|
|
@@ -118,6 +145,7 @@ SDATA(data_type_t.DTP_BOOLEAN, "show_label", 0, true, "Show item la
|
|
|
118
145
|
SDATA(data_type_t.DTP_STRING, "level", 0, "primary", "primary|secondary"),
|
|
119
146
|
SDATA(data_type_t.DTP_STRING, "show_on", 0, "", "Breakpoint visibility expr (shell show_on syntax); empty = always"),
|
|
120
147
|
SDATA(data_type_t.DTP_STRING, "back_route", 0, "", "backbar layout: route of the section index the ← returns to"),
|
|
148
|
+
SDATA(data_type_t.DTP_DICT, "badges", 0, "{}", "Item id -> {badge, badge_tone} set at runtime (EV_SET_ITEM_BADGE); wins over the item's own fields"),
|
|
121
149
|
|
|
122
150
|
SDATA(data_type_t.DTP_POINTER, "shell", 0, null, "C_YUI_SHELL gobj (for navigation calls)"),
|
|
123
151
|
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "Root HTMLElement of this nav"),
|
|
@@ -230,17 +258,19 @@ function build_ui(gobj)
|
|
|
230
258
|
let menu_id = gobj_read_attr(gobj, "menu_id") || "nav";
|
|
231
259
|
let $container;
|
|
232
260
|
|
|
261
|
+
/* The items are the app's nav declaration: the keys of their names
|
|
262
|
+
* are read there (app_config.json), not here. */
|
|
233
263
|
switch(layout) {
|
|
234
|
-
case "vertical": $container = render_vertical(gobj, items); break;
|
|
235
|
-
case "icon-bar": $container = render_icon_bar(gobj, items); break;
|
|
236
|
-
case "tabs": $container = render_tabs(gobj, items); break;
|
|
237
|
-
case "drawer": $container = render_drawer(gobj, items); break;
|
|
238
|
-
case "submenu": $container = render_submenu(gobj, items); break;
|
|
239
|
-
case "accordion": $container = render_accordion(gobj, items); break;
|
|
240
|
-
case "cards": $container = render_cards(gobj, items); break;
|
|
241
|
-
case "backbar": $container = render_backbar(gobj);
|
|
242
|
-
case "breadcrumb": $container = render_breadcrumb(gobj, items); break;
|
|
243
|
-
default: $container = render_vertical(gobj, items);
|
|
264
|
+
case "vertical": $container = render_vertical(gobj, /* i18n: data */ items); break;
|
|
265
|
+
case "icon-bar": $container = render_icon_bar(gobj, /* i18n: data */ items); break;
|
|
266
|
+
case "tabs": $container = render_tabs(gobj, /* i18n: data */ items); break;
|
|
267
|
+
case "drawer": $container = render_drawer(gobj, /* i18n: data */ items); break;
|
|
268
|
+
case "submenu": $container = render_submenu(gobj, /* i18n: data */ items); break;
|
|
269
|
+
case "accordion": $container = render_accordion(gobj, /* i18n: data */ items); break;
|
|
270
|
+
case "cards": $container = render_cards(gobj, /* i18n: data */ items); break;
|
|
271
|
+
case "backbar": $container = render_backbar(gobj); break;
|
|
272
|
+
case "breadcrumb": $container = render_breadcrumb(gobj, /* i18n: data */ items); break;
|
|
273
|
+
default: $container = render_vertical(gobj, /* i18n: data */ items);
|
|
244
274
|
}
|
|
245
275
|
$container.setAttribute("data-nav-zone", zone);
|
|
246
276
|
$container.setAttribute("data-nav-layout", layout);
|
|
@@ -407,13 +437,20 @@ function render_tabs(gobj, items)
|
|
|
407
437
|
continue; /* tab strips have no room for section labels */
|
|
408
438
|
}
|
|
409
439
|
let children = [];
|
|
440
|
+
let has_label = show_label && !empty_string(it.name);
|
|
441
|
+
/* An icon-only tab carries its badge on the icon; a labelled
|
|
442
|
+
* one after the label, before the ✕. */
|
|
443
|
+
let pinned = !empty_string(it.icon) && !has_label;
|
|
444
|
+
let badge_el = item_badge_node(gobj, it, pinned);
|
|
410
445
|
if(!empty_string(it.icon)) {
|
|
411
|
-
children.push(
|
|
412
|
-
["i", {class: it.icon, "aria-hidden":"true"}]]);
|
|
446
|
+
children.push(icon_span(it.icon, "icon is-small", pinned ? badge_el : null));
|
|
413
447
|
}
|
|
414
|
-
if(
|
|
448
|
+
if(has_label) {
|
|
415
449
|
children.push(["span", {i18n: it.name}, text_node(it.name)]);
|
|
416
450
|
}
|
|
451
|
+
if(!pinned) {
|
|
452
|
+
children.push(badge_el);
|
|
453
|
+
}
|
|
417
454
|
/* Optional close affordance: a trailing ✕ that emits
|
|
418
455
|
* EV_NAV_ITEM_CLOSE instead of navigating (caught first in
|
|
419
456
|
* wire_clicks by its data-close-item marker). */
|
|
@@ -460,7 +497,8 @@ function render_tabs(gobj, items)
|
|
|
460
497
|
function render_cards(gobj, items)
|
|
461
498
|
{
|
|
462
499
|
let show_label = gobj_read_attr(gobj, "show_label");
|
|
463
|
-
return createElement2(cards_grid_descriptor(items, show_label
|
|
500
|
+
return createElement2(cards_grid_descriptor(items, show_label,
|
|
501
|
+
(it, pinned) => item_badge_node(gobj, it, pinned)));
|
|
464
502
|
}
|
|
465
503
|
|
|
466
504
|
/************************************************************
|
|
@@ -479,6 +517,9 @@ function render_cards(gobj, items)
|
|
|
479
517
|
* other layout — so the delegated click handler and the active
|
|
480
518
|
* highlight work unchanged, and the last crumb marks itself through
|
|
481
519
|
* `active_route` like a tab does.
|
|
520
|
+
*
|
|
521
|
+
* No badge, deliberately: a crumb names a step of the way in, and a
|
|
522
|
+
* count belongs to the menu entry that leads there.
|
|
482
523
|
************************************************************/
|
|
483
524
|
function render_breadcrumb(gobj, items)
|
|
484
525
|
{
|
|
@@ -642,7 +683,9 @@ function render_accordion(gobj, items)
|
|
|
642
683
|
head_attrs["aria-label"] = head_text;
|
|
643
684
|
head_attrs["data-i18n-aria-label"] = head_text;
|
|
644
685
|
}
|
|
645
|
-
|
|
686
|
+
/* A section head reads as a line: its badge follows the label */
|
|
687
|
+
let $hdr = createElement2(["button", head_attrs,
|
|
688
|
+
[text_node(head_text), item_badge_node(gobj, it, false)]]);
|
|
646
689
|
$container.appendChild($hdr);
|
|
647
690
|
|
|
648
691
|
let $ul = createElement2(
|
|
@@ -694,16 +737,21 @@ function item_li(gobj, it, opts)
|
|
|
694
737
|
let { icon_pos, show_label, stacked } = opts;
|
|
695
738
|
let children = [];
|
|
696
739
|
let label = it.name || "";
|
|
740
|
+
let is_stacked = !!(stacked || icon_pos === "top" || icon_pos === "bottom");
|
|
697
741
|
|
|
698
|
-
let icon_el = !empty_string(it.icon)
|
|
699
|
-
? ["span", {class: "icon"}, ["i", {class: it.icon, "aria-hidden":"true"}]]
|
|
700
|
-
: null;
|
|
701
742
|
let label_el = (show_label && !empty_string(label))
|
|
702
743
|
? ["span", {class: "yui-nav-label", i18n: label}, text_node(label)]
|
|
703
744
|
: null;
|
|
745
|
+
/* On the corner of the icon where the item is an icon over its
|
|
746
|
+
* label or an icon alone; after the label where it reads as a line. */
|
|
747
|
+
let pinned = !empty_string(it.icon) && (is_stacked || !label_el);
|
|
748
|
+
let badge_el = item_badge_node(gobj, it, pinned);
|
|
749
|
+
let icon_el = !empty_string(it.icon)
|
|
750
|
+
? icon_span(it.icon, "icon", pinned ? badge_el : null)
|
|
751
|
+
: null;
|
|
704
752
|
|
|
705
753
|
let a_class = "yui-nav-item";
|
|
706
|
-
if(
|
|
754
|
+
if(is_stacked) {
|
|
707
755
|
a_class += " yui-nav-stacked";
|
|
708
756
|
}
|
|
709
757
|
|
|
@@ -711,6 +759,9 @@ function item_li(gobj, it, opts)
|
|
|
711
759
|
if(label_el) {
|
|
712
760
|
children.push(label_el);
|
|
713
761
|
}
|
|
762
|
+
if(!pinned) {
|
|
763
|
+
children.push(badge_el);
|
|
764
|
+
}
|
|
714
765
|
if(icon_el) {
|
|
715
766
|
children.push(icon_el);
|
|
716
767
|
}
|
|
@@ -721,6 +772,9 @@ function item_li(gobj, it, opts)
|
|
|
721
772
|
if(label_el) {
|
|
722
773
|
children.push(label_el);
|
|
723
774
|
}
|
|
775
|
+
if(!pinned) {
|
|
776
|
+
children.push(badge_el);
|
|
777
|
+
}
|
|
724
778
|
}
|
|
725
779
|
|
|
726
780
|
let aria_key = label || it.id;
|
|
@@ -763,9 +817,12 @@ function item_iconbar(gobj, it, opts)
|
|
|
763
817
|
{
|
|
764
818
|
let { icon_pos, show_label } = opts;
|
|
765
819
|
let label = it.name || "";
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
820
|
+
/* An icon bar item is an icon over its label: the badge goes on
|
|
821
|
+
* the corner of the icon, or after the label of one that has none. */
|
|
822
|
+
let pinned = !empty_string(it.icon);
|
|
823
|
+
let badge_el = item_badge_node(gobj, it, pinned);
|
|
824
|
+
let icon_el = pinned
|
|
825
|
+
? icon_span(it.icon, "icon is-medium", badge_el)
|
|
769
826
|
: null;
|
|
770
827
|
let label_el = (show_label && !empty_string(label))
|
|
771
828
|
? ["span", {class: "yui-nav-label is-size-7", i18n: label}, text_node(label)]
|
|
@@ -787,6 +844,9 @@ function item_iconbar(gobj, it, opts)
|
|
|
787
844
|
children.push(label_el);
|
|
788
845
|
}
|
|
789
846
|
}
|
|
847
|
+
if(!pinned) {
|
|
848
|
+
children.push(badge_el);
|
|
849
|
+
}
|
|
790
850
|
|
|
791
851
|
let a_attrs = {
|
|
792
852
|
class: "yui-nav-item yui-nav-stacked",
|
|
@@ -808,6 +868,41 @@ function item_iconbar(gobj, it, opts)
|
|
|
808
868
|
];
|
|
809
869
|
}
|
|
810
870
|
|
|
871
|
+
/************************************************************
|
|
872
|
+
* The badge of an item (yui_badge.js), always drawn -- hidden
|
|
873
|
+
* when empty -- so EV_SET_ITEM_BADGE has a node to write into.
|
|
874
|
+
* What was set at runtime for its id (`badges`) wins over the
|
|
875
|
+
* item's own `badge` / `badge_tone`. `pinned`: on the corner of
|
|
876
|
+
* the icon instead of after the label.
|
|
877
|
+
************************************************************/
|
|
878
|
+
function item_badge_node(gobj, it, pinned)
|
|
879
|
+
{
|
|
880
|
+
let badges = gobj_read_attr(gobj, "badges");
|
|
881
|
+
let state = it;
|
|
882
|
+
if(is_object(badges) && is_string(it.id) &&
|
|
883
|
+
Object.prototype.hasOwnProperty.call(badges, it.id)) {
|
|
884
|
+
state = badges[it.id] || {};
|
|
885
|
+
}
|
|
886
|
+
let classes = "NAV_ITEM_BADGE yui-badge yui-nav-badge";
|
|
887
|
+
if(pinned) {
|
|
888
|
+
classes += " yui-nav-badge-pinned";
|
|
889
|
+
}
|
|
890
|
+
return badge_node(state.badge, state.badge_tone, classes,
|
|
891
|
+
`C_YUI_NAV '${gobj_read_attr(gobj, "menu_id") || ""}': item '${it.id}'`);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/************************************************************
|
|
895
|
+
* An item's icon, with its badge on the corner when given one.
|
|
896
|
+
************************************************************/
|
|
897
|
+
function icon_span(icon, classes, badge_el)
|
|
898
|
+
{
|
|
899
|
+
let glyph = ["i", {class: icon, "aria-hidden": "true"}];
|
|
900
|
+
if(!badge_el) {
|
|
901
|
+
return ["span", {class: classes}, glyph];
|
|
902
|
+
}
|
|
903
|
+
return ["span", {class: `${classes} yui-nav-icon`}, [glyph, badge_el]];
|
|
904
|
+
}
|
|
905
|
+
|
|
811
906
|
function wire_clicks(gobj, $container)
|
|
812
907
|
{
|
|
813
908
|
let priv = gobj.priv;
|
|
@@ -930,6 +1025,44 @@ function ac_set_items(gobj, event, kw, src)
|
|
|
930
1025
|
return 0;
|
|
931
1026
|
}
|
|
932
1027
|
|
|
1028
|
+
/************************************************************
|
|
1029
|
+
* EV_SET_ITEM_BADGE {item_id, badge, badge_tone} — the badge of
|
|
1030
|
+
* one item, set at runtime (yui_shell_set_nav_item_badge).
|
|
1031
|
+
*
|
|
1032
|
+
* Kept in `badges`, so every later rebuild draws it, and painted
|
|
1033
|
+
* in place: no rebuild for a count that moves on every tick. An
|
|
1034
|
+
* id this nav does not draw is kept all the same — a later
|
|
1035
|
+
* EV_SET_ITEMS may bring the item.
|
|
1036
|
+
************************************************************/
|
|
1037
|
+
function ac_set_item_badge(gobj, event, kw, src)
|
|
1038
|
+
{
|
|
1039
|
+
let item_id = kw.item_id;
|
|
1040
|
+
if(empty_string(item_id)) {
|
|
1041
|
+
log_error(`C_YUI_NAV: ${event} without item_id, from ${gobj_short_name(src)}`);
|
|
1042
|
+
return -1;
|
|
1043
|
+
}
|
|
1044
|
+
let menu_id = gobj_read_attr(gobj, "menu_id") || "";
|
|
1045
|
+
let tone = badge_tone(kw.badge_tone, `C_YUI_NAV '${menu_id}': item '${item_id}'`);
|
|
1046
|
+
let badge = (kw.badge === undefined) ? null : kw.badge;
|
|
1047
|
+
|
|
1048
|
+
/* A new dict: the one in the attr may be the creator's. */
|
|
1049
|
+
let badges = Object.assign({}, gobj_read_attr(gobj, "badges"));
|
|
1050
|
+
badges[item_id] = {badge: badge, badge_tone: tone};
|
|
1051
|
+
gobj_write_attr(gobj, "badges", badges);
|
|
1052
|
+
|
|
1053
|
+
let $c = gobj_read_attr(gobj, "$container");
|
|
1054
|
+
if(!$c) {
|
|
1055
|
+
return 0;
|
|
1056
|
+
}
|
|
1057
|
+
let $badges = $c.querySelectorAll(
|
|
1058
|
+
`[data-item-id="${css_escape(item_id)}"] .NAV_ITEM_BADGE`
|
|
1059
|
+
);
|
|
1060
|
+
for(let $badge of $badges) {
|
|
1061
|
+
paint_badge($badge, badge, tone);
|
|
1062
|
+
}
|
|
1063
|
+
return 0;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
933
1066
|
/************************************************************
|
|
934
1067
|
* EV_ROUTE_CHANGED from the shell — refresh active highlight
|
|
935
1068
|
************************************************************/
|
|
@@ -1041,14 +1174,16 @@ function create_gclass(gclass_name)
|
|
|
1041
1174
|
|
|
1042
1175
|
const states = [
|
|
1043
1176
|
["ST_IDLE", [
|
|
1044
|
-
["EV_ROUTE_CHANGED",
|
|
1045
|
-
["EV_SET_ITEMS",
|
|
1177
|
+
["EV_ROUTE_CHANGED", ac_route_changed, null],
|
|
1178
|
+
["EV_SET_ITEMS", ac_set_items, null],
|
|
1179
|
+
["EV_SET_ITEM_BADGE", ac_set_item_badge, null]
|
|
1046
1180
|
]]
|
|
1047
1181
|
];
|
|
1048
1182
|
|
|
1049
1183
|
const event_types = [
|
|
1050
1184
|
["EV_ROUTE_CHANGED", 0],
|
|
1051
1185
|
["EV_SET_ITEMS", 0],
|
|
1186
|
+
["EV_SET_ITEM_BADGE", 0],
|
|
1052
1187
|
["EV_NAV_CLICKED", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_PUBLIC_EVENT],
|
|
1053
1188
|
["EV_NAV_ITEM_CLOSE", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_PUBLIC_EVENT],
|
|
1054
1189
|
["EV_DRAWER_CLOSE_REQUESTED", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_PUBLIC_EVENT]
|
package/src/c_yui_period.js
CHANGED
|
@@ -386,7 +386,7 @@ function mode_name(mode)
|
|
|
386
386
|
return t(mode.roll.label || mode.id);
|
|
387
387
|
}
|
|
388
388
|
if(mode.kind === "bucket") {
|
|
389
|
-
return period_name(
|
|
389
|
+
return period_name(mode.spec, t);
|
|
390
390
|
}
|
|
391
391
|
return t(mode.id); /* span / custom */
|
|
392
392
|
}
|
|
@@ -482,7 +482,7 @@ function build_ui(gobj)
|
|
|
482
482
|
if(mode.overflow || mode.hidden) {
|
|
483
483
|
continue;
|
|
484
484
|
}
|
|
485
|
-
priv.$modes.appendChild(build_mode_button(gobj, mode));
|
|
485
|
+
priv.$modes.appendChild(build_mode_button(gobj, /* i18n: C_YUI_PERIOD_I18N_KEYS, YUI_TIME_I18N_KEYS */ mode));
|
|
486
486
|
}
|
|
487
487
|
|
|
488
488
|
/* Scroll plumbing, not an action: it changes nothing outside the strip.
|
|
@@ -572,7 +572,7 @@ function build_mode_button(gobj, mode)
|
|
|
572
572
|
{
|
|
573
573
|
/* The name is COMPOSED for a bucket (period_name), so it carries no
|
|
574
574
|
* key: ac_language_changed() rewrites the text and both names. */
|
|
575
|
-
let name = mode_name(
|
|
575
|
+
let name = mode_name(mode);
|
|
576
576
|
let $btn = createElement2(
|
|
577
577
|
["button", {class: `button YUI_PERIOD_MODE YUI_PERIOD_MODE_${mode.id.toUpperCase()}`,
|
|
578
578
|
type: "button", title: name, "aria-label": name},
|
|
@@ -383,7 +383,7 @@ function mt_destroy(gobj)
|
|
|
383
383
|
function refuse_bad_name(gobj, $message)
|
|
384
384
|
{
|
|
385
385
|
mark_name_field(gobj, true);
|
|
386
|
-
yui_shell_show_error(yui_shell_of(gobj),
|
|
386
|
+
yui_shell_show_error(yui_shell_of(gobj), $message, {t: t});
|
|
387
387
|
return -1;
|
|
388
388
|
}
|
|
389
389
|
|
|
@@ -1258,7 +1258,7 @@ function render(gobj)
|
|
|
1258
1258
|
}
|
|
1259
1259
|
if(state === "ST_IDLE") {
|
|
1260
1260
|
show_notice(gobj, priv.load_error ? "cannot load the schemas" : "not connected",
|
|
1261
|
-
priv.load_error, true);
|
|
1261
|
+
/* i18n: data */ priv.load_error, true);
|
|
1262
1262
|
destroy_diagram(gobj);
|
|
1263
1263
|
clear($body);
|
|
1264
1264
|
return;
|
|
@@ -1362,16 +1362,16 @@ function render_treedbs(gobj, $body)
|
|
|
1362
1362
|
let $cards = [];
|
|
1363
1363
|
|
|
1364
1364
|
for(let treedb of priv.model.treedbs) {
|
|
1365
|
-
|
|
1365
|
+
const from_c = treedb.c_schema_version !== undefined &&
|
|
1366
|
+
String(treedb.c_schema_version) !== String(treedb.schema_version);
|
|
1367
|
+
const tags = [
|
|
1366
1368
|
{i18n: "topics", text: treedb.topics.length},
|
|
1367
1369
|
{i18n: "version", text: treedb.schema_version === undefined
|
|
1368
|
-
? "?" : treedb.schema_version}
|
|
1370
|
+
? "?" : treedb.schema_version},
|
|
1371
|
+
...(from_c
|
|
1372
|
+
? [{i18n: "from c", text: treedb.c_schema_version, style: "is-warning is-light"}]
|
|
1373
|
+
: [])
|
|
1369
1374
|
];
|
|
1370
|
-
if(treedb.c_schema_version !== undefined &&
|
|
1371
|
-
String(treedb.c_schema_version) !== String(treedb.schema_version)
|
|
1372
|
-
) {
|
|
1373
|
-
tags.push({i18n: "from c", text: treedb.c_schema_version, style: "is-warning is-light"});
|
|
1374
|
-
}
|
|
1375
1375
|
$cards.push(open_card(gobj, "SCHEMA_TREEDB_CARD", treedb.id, "yi-database",
|
|
1376
1376
|
tags, "EV_SELECT_TREEDB", {treedb: treedb.id}));
|
|
1377
1377
|
}
|
|
@@ -1406,7 +1406,7 @@ function render_topics(gobj, $body)
|
|
|
1406
1406
|
let treedb = current_treedb(gobj);
|
|
1407
1407
|
|
|
1408
1408
|
if(!treedb) {
|
|
1409
|
-
show_notice(gobj, "that treedb is not here any more", priv.treedb_id);
|
|
1409
|
+
show_notice(gobj, "that treedb is not here any more", /* i18n: data */ priv.treedb_id);
|
|
1410
1410
|
return;
|
|
1411
1411
|
}
|
|
1412
1412
|
|
|
@@ -1554,7 +1554,7 @@ function render_columns(gobj, $body)
|
|
|
1554
1554
|
let topic = current_topic(gobj);
|
|
1555
1555
|
|
|
1556
1556
|
if(!topic) {
|
|
1557
|
-
show_notice(gobj, "that topic is not here any more", priv.topic_name);
|
|
1557
|
+
show_notice(gobj, "that topic is not here any more", /* i18n: data */ priv.topic_name);
|
|
1558
1558
|
return;
|
|
1559
1559
|
}
|
|
1560
1560
|
|
|
@@ -1815,7 +1815,7 @@ function render_diagram(gobj)
|
|
|
1815
1815
|
if(!treedb) {
|
|
1816
1816
|
destroy_diagram(gobj);
|
|
1817
1817
|
clear($body);
|
|
1818
|
-
show_notice(gobj, "that treedb is not here any more", priv.treedb_id);
|
|
1818
|
+
show_notice(gobj, "that treedb is not here any more", /* i18n: data */ priv.treedb_id);
|
|
1819
1819
|
return;
|
|
1820
1820
|
}
|
|
1821
1821
|
if(!gclass_find_by_name("C_YUI_TREEDB_SCHEMA")) {
|
|
@@ -1921,7 +1921,7 @@ function open_dialog(gobj, $content, title, logical, title_prefix)
|
|
|
1921
1921
|
priv.dialog_gen = priv.model_gen;
|
|
1922
1922
|
priv.dialog_kind = logical;
|
|
1923
1923
|
priv.dialog_content = $content;
|
|
1924
|
-
priv.dialog = yui_shell_show_modal(yui_shell_of(gobj),
|
|
1924
|
+
priv.dialog = yui_shell_show_modal(yui_shell_of(gobj), $content, {
|
|
1925
1925
|
dialog: true,
|
|
1926
1926
|
title: title,
|
|
1927
1927
|
title_prefix: title_prefix || "",
|
|
@@ -2435,8 +2435,8 @@ function open_report(gobj, findings, treedb_id)
|
|
|
2435
2435
|
return ["tr", {class: `SCHEMA_REPORT_ROW SCHEMA_REPORT_${finding.severity.toUpperCase()}`}, [
|
|
2436
2436
|
["td", {class: "SCHEMA_REPORT_SEVERITY"}, [
|
|
2437
2437
|
["span", {class: `tag ${finding.severity === "error" ? "is-danger" : "is-warning"}` +
|
|
2438
|
-
" is-light", i18n:
|
|
2439
|
-
t(
|
|
2438
|
+
" is-light", i18n: finding.severity},
|
|
2439
|
+
t(finding.severity)]
|
|
2440
2440
|
]],
|
|
2441
2441
|
["td", {class: "SCHEMA_REPORT_WHAT", i18n: finding.code}, t(finding.code)],
|
|
2442
2442
|
["td", {class: "SCHEMA_REPORT_TOPIC"}, text_node(finding.topic || "")],
|
|
@@ -4256,7 +4256,7 @@ function confirm_column_delete(gobj, verdict)
|
|
|
4256
4256
|
}
|
|
4257
4257
|
$warning = createElement2(["div", {class: "SCHEMA_COLUMN_DATA_WARNING"}, children]);
|
|
4258
4258
|
}
|
|
4259
|
-
confirm_then(gobj, $warning?
|
|
4259
|
+
confirm_then(gobj, $warning? $warning : message, `${c.topic}.${c.col}`,
|
|
4260
4260
|
{what: "column", topic: c.topic, col: c.col});
|
|
4261
4261
|
return 0;
|
|
4262
4262
|
}
|
|
@@ -4494,7 +4494,9 @@ function ac_validate(gobj, event, kw, src)
|
|
|
4494
4494
|
written_topics: Object.keys(priv.written),
|
|
4495
4495
|
baseline: priv.baseline
|
|
4496
4496
|
});
|
|
4497
|
-
|
|
4497
|
+
/* The severities are C_YUI_SCHEMA_EDITOR_I18N_KEYS; the codes are the
|
|
4498
|
+
* checks' own keys, read where schema_validate.js writes them. */
|
|
4499
|
+
open_report(gobj, /* i18n: C_YUI_SCHEMA_EDITOR_I18N_KEYS */ findings, treedb.id);
|
|
4498
4500
|
if(has_errors(findings)) {
|
|
4499
4501
|
log_warning(`${gobj_short_name(gobj)}: ${treedb.id} would be refused as it is`);
|
|
4500
4502
|
}
|
|
@@ -4578,7 +4580,9 @@ function ac_preview_import(gobj, event, kw, src)
|
|
|
4578
4580
|
|
|
4579
4581
|
let plan = plan_import(treedb, incoming, {prune: !!kw.prune});
|
|
4580
4582
|
priv.import_plan = plan;
|
|
4581
|
-
|
|
4583
|
+
/* Its writes are SCHEMA_IMPORT_I18N_KEYS; its conflicts are keys of
|
|
4584
|
+
* their own, read where schema_import.js writes them. */
|
|
4585
|
+
render_import_plan(gobj, /* i18n: SCHEMA_IMPORT_I18N_KEYS */ plan);
|
|
4582
4586
|
return 0;
|
|
4583
4587
|
}
|
|
4584
4588
|
|
package/src/c_yui_shell.css
CHANGED
|
@@ -459,29 +459,30 @@
|
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
/*------------------------------------------------------------
|
|
462
|
-
*
|
|
462
|
+
* Badge — the count of a toolbar item or a menu item
|
|
463
|
+
* (yui_badge.js)
|
|
463
464
|
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
465
|
+
* `.yui-badge` is the LOOK, one for both. Where it SITS is the
|
|
466
|
+
* host's: `.yui-toolbar-badge` on the corner of a toolbar icon,
|
|
467
|
+
* `.yui-nav-badge` after a menu item's label, or on the corner
|
|
468
|
+
* of its icon (`.yui-nav-badge-pinned`).
|
|
469
|
+
*
|
|
470
|
+
* Anchored to the ICON and not to the control: with its label on
|
|
471
|
+
* (desktop) a toolbar button is wide, and a badge in its corner
|
|
472
|
+
* would float away from the glyph it counts.
|
|
467
473
|
*
|
|
468
474
|
* `hidden` is honoured explicitly: the element is always in the
|
|
469
475
|
* DOM (so the runtime setter has somewhere to write) and
|
|
470
476
|
* [hidden] loses to `display:inline-flex` without this rule.
|
|
471
477
|
*
|
|
472
|
-
* Danger colour on purpose — a badge exists to pull
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
478
|
+
* Danger colour by default, on purpose — a badge exists to pull
|
|
479
|
+
* the eye. A TONE (`is-warning`, `is-info`, …) is a FILL: the
|
|
480
|
+
* raw Bulma token under its `-invert` ink, its own colour pair in
|
|
481
|
+
* both schemes rather than a Bulma helper, so the badge keeps its
|
|
482
|
+
* contrast when the bar under it changes with the theme (README,
|
|
483
|
+
* "Colour"). No transition: house rule.
|
|
476
484
|
*------------------------------------------------------------*/
|
|
477
|
-
.yui-
|
|
478
|
-
position: relative;
|
|
479
|
-
overflow: visible;
|
|
480
|
-
}
|
|
481
|
-
.yui-toolbar-badge {
|
|
482
|
-
position: absolute;
|
|
483
|
-
top: -0.35em;
|
|
484
|
-
right: -0.55em;
|
|
485
|
+
.yui-badge {
|
|
485
486
|
display: inline-flex;
|
|
486
487
|
align-items: center;
|
|
487
488
|
justify-content: center;
|
|
@@ -491,19 +492,84 @@
|
|
|
491
492
|
height: 1.15em;
|
|
492
493
|
padding: 0 0.3em;
|
|
493
494
|
border-radius: 999px;
|
|
494
|
-
font-size: 0.62em;
|
|
495
495
|
font-weight: 700;
|
|
496
496
|
line-height: 1;
|
|
497
497
|
font-family: inherit;
|
|
498
|
+
white-space: nowrap;
|
|
498
499
|
background-color: var(--bulma-danger, #f14668);
|
|
499
500
|
color: var(--bulma-danger-invert, #fff);
|
|
500
501
|
/* Separates the pill from the glyph when they overlap. */
|
|
501
502
|
box-shadow: 0 0 0 0.12em var(--bulma-scheme-main, #fff);
|
|
502
503
|
pointer-events: none;
|
|
503
504
|
}
|
|
504
|
-
.yui-
|
|
505
|
+
.yui-badge[hidden] {
|
|
505
506
|
display: none;
|
|
506
507
|
}
|
|
508
|
+
.yui-badge.is-danger {
|
|
509
|
+
background-color: var(--bulma-danger, #f14668);
|
|
510
|
+
color: var(--bulma-danger-invert, #fff);
|
|
511
|
+
}
|
|
512
|
+
.yui-badge.is-warning {
|
|
513
|
+
background-color: var(--bulma-warning, #ffb70f);
|
|
514
|
+
color: var(--bulma-warning-invert, rgba(0, 0, 0, 0.7));
|
|
515
|
+
}
|
|
516
|
+
.yui-badge.is-info {
|
|
517
|
+
background-color: var(--bulma-info, #66d1ff);
|
|
518
|
+
color: var(--bulma-info-invert, rgba(0, 0, 0, 0.7));
|
|
519
|
+
}
|
|
520
|
+
.yui-badge.is-success {
|
|
521
|
+
background-color: var(--bulma-success, #48c78e);
|
|
522
|
+
color: var(--bulma-success-invert, rgba(0, 0, 0, 0.7));
|
|
523
|
+
}
|
|
524
|
+
.yui-badge.is-primary {
|
|
525
|
+
background-color: var(--bulma-primary, #00d1b2);
|
|
526
|
+
color: var(--bulma-primary-invert, rgba(0, 0, 0, 0.7));
|
|
527
|
+
}
|
|
528
|
+
.yui-badge.is-link {
|
|
529
|
+
background-color: var(--bulma-link, #485fc7);
|
|
530
|
+
color: var(--bulma-link-invert, #fff);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/* Toolbar: on the corner of the icon, sized by the icon. */
|
|
534
|
+
.yui-toolbar-icon {
|
|
535
|
+
position: relative;
|
|
536
|
+
overflow: visible;
|
|
537
|
+
}
|
|
538
|
+
.yui-toolbar-badge {
|
|
539
|
+
position: absolute;
|
|
540
|
+
top: -0.35em;
|
|
541
|
+
right: -0.55em;
|
|
542
|
+
font-size: 0.62em;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/* Menu item: after the label. Sized in rem, so it is the same
|
|
546
|
+
* pill in every layout whatever the item's own font size. */
|
|
547
|
+
.yui-nav-badge {
|
|
548
|
+
flex: 0 0 auto;
|
|
549
|
+
margin-inline-start: 0.4rem;
|
|
550
|
+
font-size: 0.75rem;
|
|
551
|
+
vertical-align: middle;
|
|
552
|
+
}
|
|
553
|
+
/* Where the item already spaces its children (a flex row or column
|
|
554
|
+
* with a gap), the gap is the space. */
|
|
555
|
+
.yui-nav-vertical .menu-list a > .yui-nav-badge,
|
|
556
|
+
.yui-nav-stacked > .yui-nav-badge,
|
|
557
|
+
.yui-nav-cards .yui-nav-card > .yui-nav-badge {
|
|
558
|
+
margin-inline-start: 0;
|
|
559
|
+
}
|
|
560
|
+
/* ...or on the corner of the icon, where the item is an icon over
|
|
561
|
+
* its label (icon bar, cards, a stacked item) or an icon alone. */
|
|
562
|
+
.yui-nav-icon {
|
|
563
|
+
position: relative;
|
|
564
|
+
overflow: visible;
|
|
565
|
+
}
|
|
566
|
+
.yui-nav-badge.yui-nav-badge-pinned {
|
|
567
|
+
position: absolute;
|
|
568
|
+
top: -0.35em;
|
|
569
|
+
inset-inline-end: -0.55em;
|
|
570
|
+
margin: 0;
|
|
571
|
+
font-size: 0.7rem;
|
|
572
|
+
}
|
|
507
573
|
|
|
508
574
|
/*============================================================
|
|
509
575
|
* Toolbar — type:"brand"
|