@yuneta/gobj-ui 5.5.0 → 5.6.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/README.md +40 -0
- package/dist/gobj-ui.cjs.js +87 -4
- package/dist/gobj-ui.es.js +87 -5
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/c_yui_nav.js +6 -1
- package/src/c_yui_shell.css +47 -0
- package/src/c_yui_shell.js +106 -2
package/README.md
CHANGED
|
@@ -464,6 +464,46 @@ host can use it to toggle. The tree is a **pure child of the window**, so every
|
|
|
464
464
|
teardown path (the ✕, or the host destroying the window to toggle the entry
|
|
465
465
|
off) takes it down too.
|
|
466
466
|
|
|
467
|
+
### Toolbar badge — a count pinned to an item's icon
|
|
468
|
+
|
|
469
|
+
```js
|
|
470
|
+
/* app_config.json — seeds the FIRST paint only */
|
|
471
|
+
{ "id": "alarms", "icon": "yi-triangle-exclamation", "align": "end",
|
|
472
|
+
"aria_label": "alarms", "badge": 0,
|
|
473
|
+
"action": {"type": "navigate", "route": "/alarms"} }
|
|
474
|
+
|
|
475
|
+
/* the interface that matters: a count is a RUNTIME fact */
|
|
476
|
+
yui_shell_set_toolbar_item_badge(shell, "alarms", 3);
|
|
477
|
+
yui_shell_set_toolbar_item_badge(shell, "alarms", 0); // clears it
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
An icon-only toolbar button is a link; the badge is what makes it a
|
|
481
|
+
**signal**. Without a number, an alarm bell cannot say whether anything needs
|
|
482
|
+
you — which is the reason to look at it at all.
|
|
483
|
+
|
|
484
|
+
Rules baked in, all for the same reason (a badge that lies costs more than no
|
|
485
|
+
badge):
|
|
486
|
+
|
|
487
|
+
- **`0`, `""`, `null` and `false` all clear it.** A badge reading "0" is worse
|
|
488
|
+
than none: it draws the eye to say nothing.
|
|
489
|
+
- **Over 99 renders `99+`.** The toolbar is a fixed-width row and a four-digit
|
|
490
|
+
pill pushes its neighbours off a phone screen.
|
|
491
|
+
- **A string passes through** for the states that are not counts (`"!"`, `"…"`).
|
|
492
|
+
- **Unknown item id is a silent no-op**, so an app whose toolbar has no such
|
|
493
|
+
item does not log an error on every tick of whatever feeds the number.
|
|
494
|
+
- Writing the **same** value again touches no DOM: the badge is a
|
|
495
|
+
`role="status"` live region, and rewriting it would have a screen reader
|
|
496
|
+
announce the same number on every tick.
|
|
497
|
+
|
|
498
|
+
`role="status"` and not `aria-hidden`, deliberately: the button carries an
|
|
499
|
+
explicit `aria-label`, and an explicit label **replaces** an element's content
|
|
500
|
+
for a screen reader — a badge inside it would otherwise be silent. As its own
|
|
501
|
+
live region it is both read and announced when it changes.
|
|
502
|
+
|
|
503
|
+
> `C_YUI_NAV` items do **not** have this. Its item contract listed `badge` for a
|
|
504
|
+
> long time and nothing ever rendered it; the claim is gone. Implement it there
|
|
505
|
+
> the day a menu entry needs one.
|
|
506
|
+
|
|
467
507
|
### Modals — `yui_shell_show_modal` and the `before_close` veto
|
|
468
508
|
|
|
469
509
|
`yui_shell_show_modal(shell, $box, opts)` is the standard popup: pass
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -1656,17 +1656,59 @@ function build_toolbar(gobj, config) {
|
|
|
1656
1656
|
$zone.appendChild($bar);
|
|
1657
1657
|
}
|
|
1658
1658
|
/************************************************************
|
|
1659
|
+
* The badge node of a toolbar item — the little count pinned to
|
|
1660
|
+
* the corner of its icon (unread notifications, active alarms).
|
|
1661
|
+
*
|
|
1662
|
+
* ALWAYS rendered, hidden when empty, so the runtime setter has a
|
|
1663
|
+
* node to write into and does not have to rebuild the button.
|
|
1664
|
+
*
|
|
1665
|
+
* EMPTY IS 0, "" AND null ALIKE: a badge reading "0" is worse than
|
|
1666
|
+
* no badge — it draws the eye to say nothing. A count is only
|
|
1667
|
+
* worth a pixel when there IS something.
|
|
1668
|
+
*
|
|
1669
|
+
* Capped at 99+: the toolbar is a fixed-width row, and a four
|
|
1670
|
+
* digit pill pushes the items next to it off a phone screen.
|
|
1671
|
+
*
|
|
1672
|
+
* role="status" and NOT aria-hidden, deliberately. The button
|
|
1673
|
+
* carries an explicit aria-label, and an explicit label REPLACES
|
|
1674
|
+
* the element's content for a screen reader — so a badge inside
|
|
1675
|
+
* it would be silent. As its own live region it is both read and
|
|
1676
|
+
* announced when the count changes, which is the whole point of a
|
|
1677
|
+
* badge that means "something needs you".
|
|
1678
|
+
************************************************************/
|
|
1679
|
+
function badge_text(value) {
|
|
1680
|
+
if (value === null || value === void 0 || value === false) return "";
|
|
1681
|
+
if (typeof value === "number") {
|
|
1682
|
+
if (!isFinite(value) || value <= 0) return "";
|
|
1683
|
+
return value > 99 ? "99+" : String(Math.floor(value));
|
|
1684
|
+
}
|
|
1685
|
+
return String(value).trim();
|
|
1686
|
+
}
|
|
1687
|
+
function badge_node(value) {
|
|
1688
|
+
let text = badge_text(value);
|
|
1689
|
+
let attrs = {
|
|
1690
|
+
class: "yui-toolbar-badge",
|
|
1691
|
+
role: "status"
|
|
1692
|
+
};
|
|
1693
|
+
if (!text) attrs.hidden = "hidden";
|
|
1694
|
+
return [
|
|
1695
|
+
"span",
|
|
1696
|
+
attrs,
|
|
1697
|
+
text
|
|
1698
|
+
];
|
|
1699
|
+
}
|
|
1700
|
+
/************************************************************
|
|
1659
1701
|
* Renderer for the default ("action") item kind.
|
|
1660
1702
|
************************************************************/
|
|
1661
1703
|
function build_toolbar_action_item(gobj, it) {
|
|
1662
1704
|
let children = [];
|
|
1663
1705
|
if (!(0, _yuneta_gobj_js.empty_string)(it.icon)) children.push([
|
|
1664
1706
|
"span",
|
|
1665
|
-
{ class: "icon" },
|
|
1666
|
-
["i", {
|
|
1707
|
+
{ class: "icon yui-toolbar-icon" },
|
|
1708
|
+
[["i", {
|
|
1667
1709
|
class: it.icon,
|
|
1668
1710
|
"aria-hidden": "true"
|
|
1669
|
-
}]
|
|
1711
|
+
}], badge_node(it.badge)]
|
|
1670
1712
|
]);
|
|
1671
1713
|
if (!(0, _yuneta_gobj_js.empty_string)(it.name)) children.push([
|
|
1672
1714
|
"span",
|
|
@@ -2677,6 +2719,41 @@ function yui_shell_set_toolbar_item_icon(shell_gobj, item_id, icon_class) {
|
|
|
2677
2719
|
if ($i) $i.className = icon_class;
|
|
2678
2720
|
}
|
|
2679
2721
|
/************************************************************
|
|
2722
|
+
* Set (or clear) a toolbar item's badge — the count pinned to
|
|
2723
|
+
* its icon.
|
|
2724
|
+
*
|
|
2725
|
+
* yui_shell_set_toolbar_item_badge(shell, "alarms", 3);
|
|
2726
|
+
* yui_shell_set_toolbar_item_badge(shell, "alarms", 0); // hidden
|
|
2727
|
+
*
|
|
2728
|
+
* This is the API that matters: a count is a RUNTIME fact. The
|
|
2729
|
+
* `badge` field of a toolbar item only seeds the first paint, and
|
|
2730
|
+
* an app whose number never changes did not need a badge.
|
|
2731
|
+
*
|
|
2732
|
+
* 0 / "" / null / false all clear it, and a number over 99 renders
|
|
2733
|
+
* "99+" — see badge_node(). Accepts a string for the states that
|
|
2734
|
+
* are not counts ("!", "…").
|
|
2735
|
+
*
|
|
2736
|
+
* Silent no-op on an unknown item id: a toolbar that does not
|
|
2737
|
+
* declare the item (an app without alarms) must not be an error
|
|
2738
|
+
* at every tick of whatever feeds the number.
|
|
2739
|
+
************************************************************/
|
|
2740
|
+
function yui_shell_set_toolbar_item_badge(shell_gobj, item_id, value) {
|
|
2741
|
+
if (!shell_gobj || !(0, _yuneta_gobj_js.is_gobj)(shell_gobj) || (0, _yuneta_gobj_js.empty_string)(item_id)) return;
|
|
2742
|
+
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(shell_gobj, "$container");
|
|
2743
|
+
if (!$container) return;
|
|
2744
|
+
let $badge = $container.querySelector(`[data-toolbar-item-id="${item_id}"] .yui-toolbar-badge`);
|
|
2745
|
+
if (!$badge) return;
|
|
2746
|
+
let text = badge_text(value);
|
|
2747
|
+
if (!text) {
|
|
2748
|
+
$badge.textContent = "";
|
|
2749
|
+
$badge.setAttribute("hidden", "hidden");
|
|
2750
|
+
return;
|
|
2751
|
+
}
|
|
2752
|
+
if ($badge.textContent === text && !$badge.hasAttribute("hidden")) return;
|
|
2753
|
+
$badge.textContent = text;
|
|
2754
|
+
$badge.removeAttribute("hidden");
|
|
2755
|
+
}
|
|
2756
|
+
/************************************************************
|
|
2680
2757
|
* Programmatic close of any open toolbar dropdown. Useful for
|
|
2681
2758
|
* external triggers (e.g. EV_LOGOUT firing from elsewhere) that
|
|
2682
2759
|
* want to dismiss whatever menu is on screen.
|
|
@@ -12943,7 +13020,12 @@ function cards_grid_descriptor(items, show_label) {
|
|
|
12943
13020
|
* container; build_ui re-applies them on every rebuild.
|
|
12944
13021
|
*
|
|
12945
13022
|
* Each item supports:
|
|
12946
|
-
* id, name, icon (CSS class or svg id), route,
|
|
13023
|
+
* id, name, icon (CSS class or svg id), route, disabled
|
|
13024
|
+
* (`badge` was listed here for a long time and NOTHING ever
|
|
13025
|
+
* rendered it — a menu item cannot carry a count today. The
|
|
13026
|
+
* toolbar can: `badge` on a toolbar item plus
|
|
13027
|
+
* yui_shell_set_toolbar_item_badge(). Implement it here the
|
|
13028
|
+
* day a menu entry needs one, rather than re-listing it.)
|
|
12947
13029
|
* class — extra CSS class(es) on the item (tabs layout), e.g.
|
|
12948
13030
|
* a per-item state colour like "yui-nav-disconnected"
|
|
12949
13031
|
* closable — render a trailing ✕ that emits EV_NAV_ITEM_CLOSE
|
|
@@ -53622,6 +53704,7 @@ exports.yui_shell_register_overlay = yui_shell_register_overlay;
|
|
|
53622
53704
|
exports.yui_shell_set_avatar_provider = yui_shell_set_avatar_provider;
|
|
53623
53705
|
exports.yui_shell_set_connection_state = yui_shell_set_connection_state;
|
|
53624
53706
|
exports.yui_shell_set_sub_routes = yui_shell_set_sub_routes;
|
|
53707
|
+
exports.yui_shell_set_toolbar_item_badge = yui_shell_set_toolbar_item_badge;
|
|
53625
53708
|
exports.yui_shell_set_toolbar_item_icon = yui_shell_set_toolbar_item_icon;
|
|
53626
53709
|
exports.yui_shell_set_translator = yui_shell_set_translator;
|
|
53627
53710
|
exports.yui_shell_show_error = yui_shell_show_error;
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -1651,17 +1651,59 @@ function build_toolbar(gobj, config) {
|
|
|
1651
1651
|
$zone.appendChild($bar);
|
|
1652
1652
|
}
|
|
1653
1653
|
/************************************************************
|
|
1654
|
+
* The badge node of a toolbar item — the little count pinned to
|
|
1655
|
+
* the corner of its icon (unread notifications, active alarms).
|
|
1656
|
+
*
|
|
1657
|
+
* ALWAYS rendered, hidden when empty, so the runtime setter has a
|
|
1658
|
+
* node to write into and does not have to rebuild the button.
|
|
1659
|
+
*
|
|
1660
|
+
* EMPTY IS 0, "" AND null ALIKE: a badge reading "0" is worse than
|
|
1661
|
+
* no badge — it draws the eye to say nothing. A count is only
|
|
1662
|
+
* worth a pixel when there IS something.
|
|
1663
|
+
*
|
|
1664
|
+
* Capped at 99+: the toolbar is a fixed-width row, and a four
|
|
1665
|
+
* digit pill pushes the items next to it off a phone screen.
|
|
1666
|
+
*
|
|
1667
|
+
* role="status" and NOT aria-hidden, deliberately. The button
|
|
1668
|
+
* carries an explicit aria-label, and an explicit label REPLACES
|
|
1669
|
+
* the element's content for a screen reader — so a badge inside
|
|
1670
|
+
* it would be silent. As its own live region it is both read and
|
|
1671
|
+
* announced when the count changes, which is the whole point of a
|
|
1672
|
+
* badge that means "something needs you".
|
|
1673
|
+
************************************************************/
|
|
1674
|
+
function badge_text(value) {
|
|
1675
|
+
if (value === null || value === void 0 || value === false) return "";
|
|
1676
|
+
if (typeof value === "number") {
|
|
1677
|
+
if (!isFinite(value) || value <= 0) return "";
|
|
1678
|
+
return value > 99 ? "99+" : String(Math.floor(value));
|
|
1679
|
+
}
|
|
1680
|
+
return String(value).trim();
|
|
1681
|
+
}
|
|
1682
|
+
function badge_node(value) {
|
|
1683
|
+
let text = badge_text(value);
|
|
1684
|
+
let attrs = {
|
|
1685
|
+
class: "yui-toolbar-badge",
|
|
1686
|
+
role: "status"
|
|
1687
|
+
};
|
|
1688
|
+
if (!text) attrs.hidden = "hidden";
|
|
1689
|
+
return [
|
|
1690
|
+
"span",
|
|
1691
|
+
attrs,
|
|
1692
|
+
text
|
|
1693
|
+
];
|
|
1694
|
+
}
|
|
1695
|
+
/************************************************************
|
|
1654
1696
|
* Renderer for the default ("action") item kind.
|
|
1655
1697
|
************************************************************/
|
|
1656
1698
|
function build_toolbar_action_item(gobj, it) {
|
|
1657
1699
|
let children = [];
|
|
1658
1700
|
if (!empty_string(it.icon)) children.push([
|
|
1659
1701
|
"span",
|
|
1660
|
-
{ class: "icon" },
|
|
1661
|
-
["i", {
|
|
1702
|
+
{ class: "icon yui-toolbar-icon" },
|
|
1703
|
+
[["i", {
|
|
1662
1704
|
class: it.icon,
|
|
1663
1705
|
"aria-hidden": "true"
|
|
1664
|
-
}]
|
|
1706
|
+
}], badge_node(it.badge)]
|
|
1665
1707
|
]);
|
|
1666
1708
|
if (!empty_string(it.name)) children.push([
|
|
1667
1709
|
"span",
|
|
@@ -2672,6 +2714,41 @@ function yui_shell_set_toolbar_item_icon(shell_gobj, item_id, icon_class) {
|
|
|
2672
2714
|
if ($i) $i.className = icon_class;
|
|
2673
2715
|
}
|
|
2674
2716
|
/************************************************************
|
|
2717
|
+
* Set (or clear) a toolbar item's badge — the count pinned to
|
|
2718
|
+
* its icon.
|
|
2719
|
+
*
|
|
2720
|
+
* yui_shell_set_toolbar_item_badge(shell, "alarms", 3);
|
|
2721
|
+
* yui_shell_set_toolbar_item_badge(shell, "alarms", 0); // hidden
|
|
2722
|
+
*
|
|
2723
|
+
* This is the API that matters: a count is a RUNTIME fact. The
|
|
2724
|
+
* `badge` field of a toolbar item only seeds the first paint, and
|
|
2725
|
+
* an app whose number never changes did not need a badge.
|
|
2726
|
+
*
|
|
2727
|
+
* 0 / "" / null / false all clear it, and a number over 99 renders
|
|
2728
|
+
* "99+" — see badge_node(). Accepts a string for the states that
|
|
2729
|
+
* are not counts ("!", "…").
|
|
2730
|
+
*
|
|
2731
|
+
* Silent no-op on an unknown item id: a toolbar that does not
|
|
2732
|
+
* declare the item (an app without alarms) must not be an error
|
|
2733
|
+
* at every tick of whatever feeds the number.
|
|
2734
|
+
************************************************************/
|
|
2735
|
+
function yui_shell_set_toolbar_item_badge(shell_gobj, item_id, value) {
|
|
2736
|
+
if (!shell_gobj || !is_gobj(shell_gobj) || empty_string(item_id)) return;
|
|
2737
|
+
let $container = gobj_read_attr(shell_gobj, "$container");
|
|
2738
|
+
if (!$container) return;
|
|
2739
|
+
let $badge = $container.querySelector(`[data-toolbar-item-id="${item_id}"] .yui-toolbar-badge`);
|
|
2740
|
+
if (!$badge) return;
|
|
2741
|
+
let text = badge_text(value);
|
|
2742
|
+
if (!text) {
|
|
2743
|
+
$badge.textContent = "";
|
|
2744
|
+
$badge.setAttribute("hidden", "hidden");
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
if ($badge.textContent === text && !$badge.hasAttribute("hidden")) return;
|
|
2748
|
+
$badge.textContent = text;
|
|
2749
|
+
$badge.removeAttribute("hidden");
|
|
2750
|
+
}
|
|
2751
|
+
/************************************************************
|
|
2675
2752
|
* Programmatic close of any open toolbar dropdown. Useful for
|
|
2676
2753
|
* external triggers (e.g. EV_LOGOUT firing from elsewhere) that
|
|
2677
2754
|
* want to dismiss whatever menu is on screen.
|
|
@@ -12938,7 +13015,12 @@ function cards_grid_descriptor(items, show_label) {
|
|
|
12938
13015
|
* container; build_ui re-applies them on every rebuild.
|
|
12939
13016
|
*
|
|
12940
13017
|
* Each item supports:
|
|
12941
|
-
* id, name, icon (CSS class or svg id), route,
|
|
13018
|
+
* id, name, icon (CSS class or svg id), route, disabled
|
|
13019
|
+
* (`badge` was listed here for a long time and NOTHING ever
|
|
13020
|
+
* rendered it — a menu item cannot carry a count today. The
|
|
13021
|
+
* toolbar can: `badge` on a toolbar item plus
|
|
13022
|
+
* yui_shell_set_toolbar_item_badge(). Implement it here the
|
|
13023
|
+
* day a menu entry needs one, rather than re-listing it.)
|
|
12942
13024
|
* class — extra CSS class(es) on the item (tabs layout), e.g.
|
|
12943
13025
|
* a per-item state colour like "yui-nav-disconnected"
|
|
12944
13026
|
* closable — render a trailing ✕ that emits EV_NAV_ITEM_CLOSE
|
|
@@ -53529,6 +53611,6 @@ function setup_frontend_view(self) {
|
|
|
53529
53611
|
return win;
|
|
53530
53612
|
}
|
|
53531
53613
|
//#endregion
|
|
53532
|
-
export { EditControl, MarkerControl, YUI_PERIODS, YUI_PERIODS_DEFAULT, YUI_ROLLING, addClasses, apply_dev_traces, attach_clear, build_dev_panel, dev_window_was_open, disableElements, enableElements, epoch_to_local_input, epoch_to_ms, expose_view_container, fmt_epoch, getStrokeColor, infer_period, info_traffic, inject_svg_icons, is_current_period, iso_week, local_input_to_epoch, ms_to_epoch, period_bounds, period_bounds_epoch, period_label, period_name, period_shift, period_spec, period_start, refresh_clear, register_c_g6_nodes_tree, register_c_yui_form, register_c_yui_gobj_tree_js, register_c_yui_json, register_c_yui_json_graph, register_c_yui_map, register_c_yui_nav, register_c_yui_pager, register_c_yui_period, register_c_yui_service_view, register_c_yui_shell, register_c_yui_treedb_graph, register_c_yui_treedb_schema, register_c_yui_treedb_topic_with_form, register_c_yui_treedb_topics, register_c_yui_uplot, register_c_yui_window, register_c_yui_window_manager, register_c_yui_wizard, removeChildElements, removeClasses, rolling_bounds, safe_locale, set_active_state, set_cancel_state, set_submit_state, setup_dev, setup_frontend_view, toggleClasses, yui_is_dark, yui_mount_service_view, yui_shell_close_drawer, yui_shell_close_dropdown, yui_shell_confirm_ok, yui_shell_confirm_yesno, yui_shell_confirm_yesnocancel, yui_shell_navigate, yui_shell_open_drawer, yui_shell_overlay_dismissed, yui_shell_pop_escape, yui_shell_push_escape, yui_shell_refresh_avatars, yui_shell_register_event_handler, yui_shell_register_overlay, yui_shell_set_avatar_provider, yui_shell_set_connection_state, yui_shell_set_sub_routes, yui_shell_set_toolbar_item_icon, yui_shell_set_translator, yui_shell_show_error, yui_shell_show_info, yui_shell_show_modal, yui_shell_show_route_map, yui_shell_show_warning, yui_shell_toggle_drawer, yui_shell_unpark_route, yui_theme_now, yui_toolbar, yui_watch_theme };
|
|
53614
|
+
export { EditControl, MarkerControl, YUI_PERIODS, YUI_PERIODS_DEFAULT, YUI_ROLLING, addClasses, apply_dev_traces, attach_clear, build_dev_panel, dev_window_was_open, disableElements, enableElements, epoch_to_local_input, epoch_to_ms, expose_view_container, fmt_epoch, getStrokeColor, infer_period, info_traffic, inject_svg_icons, is_current_period, iso_week, local_input_to_epoch, ms_to_epoch, period_bounds, period_bounds_epoch, period_label, period_name, period_shift, period_spec, period_start, refresh_clear, register_c_g6_nodes_tree, register_c_yui_form, register_c_yui_gobj_tree_js, register_c_yui_json, register_c_yui_json_graph, register_c_yui_map, register_c_yui_nav, register_c_yui_pager, register_c_yui_period, register_c_yui_service_view, register_c_yui_shell, register_c_yui_treedb_graph, register_c_yui_treedb_schema, register_c_yui_treedb_topic_with_form, register_c_yui_treedb_topics, register_c_yui_uplot, register_c_yui_window, register_c_yui_window_manager, register_c_yui_wizard, removeChildElements, removeClasses, rolling_bounds, safe_locale, set_active_state, set_cancel_state, set_submit_state, setup_dev, setup_frontend_view, toggleClasses, yui_is_dark, yui_mount_service_view, yui_shell_close_drawer, yui_shell_close_dropdown, yui_shell_confirm_ok, yui_shell_confirm_yesno, yui_shell_confirm_yesnocancel, yui_shell_navigate, yui_shell_open_drawer, yui_shell_overlay_dismissed, yui_shell_pop_escape, yui_shell_push_escape, yui_shell_refresh_avatars, yui_shell_register_event_handler, yui_shell_register_overlay, yui_shell_set_avatar_provider, yui_shell_set_connection_state, yui_shell_set_sub_routes, yui_shell_set_toolbar_item_badge, yui_shell_set_toolbar_item_icon, yui_shell_set_translator, yui_shell_show_error, yui_shell_show_info, yui_shell_show_modal, yui_shell_show_route_map, yui_shell_show_warning, yui_shell_toggle_drawer, yui_shell_unpark_route, yui_theme_now, yui_toolbar, yui_watch_theme };
|
|
53533
53615
|
|
|
53534
53616
|
//# sourceMappingURL=gobj-ui.es.js.map
|
package/index.js
CHANGED
package/package.json
CHANGED
package/src/c_yui_nav.js
CHANGED
|
@@ -32,7 +32,12 @@
|
|
|
32
32
|
* container; build_ui re-applies them on every rebuild.
|
|
33
33
|
*
|
|
34
34
|
* Each item supports:
|
|
35
|
-
* id, name, icon (CSS class or svg id), route,
|
|
35
|
+
* id, name, icon (CSS class or svg id), route, disabled
|
|
36
|
+
* (`badge` was listed here for a long time and NOTHING ever
|
|
37
|
+
* rendered it — a menu item cannot carry a count today. The
|
|
38
|
+
* toolbar can: `badge` on a toolbar item plus
|
|
39
|
+
* yui_shell_set_toolbar_item_badge(). Implement it here the
|
|
40
|
+
* day a menu entry needs one, rather than re-listing it.)
|
|
36
41
|
* class — extra CSS class(es) on the item (tabs layout), e.g.
|
|
37
42
|
* a per-item state colour like "yui-nav-disconnected"
|
|
38
43
|
* closable — render a trailing ✕ that emits EV_NAV_ITEM_CLOSE
|
package/src/c_yui_shell.css
CHANGED
|
@@ -449,6 +449,53 @@
|
|
|
449
449
|
background-color: var(--bulma-scheme-main-bis, rgba(0,0,0,0.04));
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
+
/*------------------------------------------------------------
|
|
453
|
+
* Toolbar badge — the count pinned to an item's icon
|
|
454
|
+
*
|
|
455
|
+
* Anchored to the ICON and not to the button: with its label on
|
|
456
|
+
* (desktop) the button is wide, and a badge in its corner would
|
|
457
|
+
* float away from the glyph it counts.
|
|
458
|
+
*
|
|
459
|
+
* `hidden` is honoured explicitly: the element is always in the
|
|
460
|
+
* DOM (so the runtime setter has somewhere to write) and
|
|
461
|
+
* [hidden] loses to `display:inline-flex` without this rule.
|
|
462
|
+
*
|
|
463
|
+
* Danger colour on purpose — a badge exists to pull the eye. It
|
|
464
|
+
* is its own colour pair in both schemes rather than a Bulma
|
|
465
|
+
* helper, so it keeps its contrast when the toolbar background
|
|
466
|
+
* changes with the theme. No transition: house rule.
|
|
467
|
+
*------------------------------------------------------------*/
|
|
468
|
+
.yui-toolbar-icon {
|
|
469
|
+
position: relative;
|
|
470
|
+
overflow: visible;
|
|
471
|
+
}
|
|
472
|
+
.yui-toolbar-badge {
|
|
473
|
+
position: absolute;
|
|
474
|
+
top: -0.35em;
|
|
475
|
+
right: -0.55em;
|
|
476
|
+
display: inline-flex;
|
|
477
|
+
align-items: center;
|
|
478
|
+
justify-content: center;
|
|
479
|
+
box-sizing: border-box;
|
|
480
|
+
/* A round dot for one digit, a pill from two on. */
|
|
481
|
+
min-width: 1.15em;
|
|
482
|
+
height: 1.15em;
|
|
483
|
+
padding: 0 0.3em;
|
|
484
|
+
border-radius: 999px;
|
|
485
|
+
font-size: 0.62em;
|
|
486
|
+
font-weight: 700;
|
|
487
|
+
line-height: 1;
|
|
488
|
+
font-family: inherit;
|
|
489
|
+
background-color: var(--bulma-danger, #f14668);
|
|
490
|
+
color: var(--bulma-danger-invert, #fff);
|
|
491
|
+
/* Separates the pill from the glyph when they overlap. */
|
|
492
|
+
box-shadow: 0 0 0 0.12em var(--bulma-scheme-main, #fff);
|
|
493
|
+
pointer-events: none;
|
|
494
|
+
}
|
|
495
|
+
.yui-toolbar-badge[hidden] {
|
|
496
|
+
display: none;
|
|
497
|
+
}
|
|
498
|
+
|
|
452
499
|
/*============================================================
|
|
453
500
|
* Toolbar — type:"brand"
|
|
454
501
|
* Logo image + wordmark text. Renders as <button> when
|
package/src/c_yui_shell.js
CHANGED
|
@@ -1641,6 +1641,51 @@ function build_toolbar(gobj, config)
|
|
|
1641
1641
|
$zone.appendChild($bar);
|
|
1642
1642
|
}
|
|
1643
1643
|
|
|
1644
|
+
/************************************************************
|
|
1645
|
+
* The badge node of a toolbar item — the little count pinned to
|
|
1646
|
+
* the corner of its icon (unread notifications, active alarms).
|
|
1647
|
+
*
|
|
1648
|
+
* ALWAYS rendered, hidden when empty, so the runtime setter has a
|
|
1649
|
+
* node to write into and does not have to rebuild the button.
|
|
1650
|
+
*
|
|
1651
|
+
* EMPTY IS 0, "" AND null ALIKE: a badge reading "0" is worse than
|
|
1652
|
+
* no badge — it draws the eye to say nothing. A count is only
|
|
1653
|
+
* worth a pixel when there IS something.
|
|
1654
|
+
*
|
|
1655
|
+
* Capped at 99+: the toolbar is a fixed-width row, and a four
|
|
1656
|
+
* digit pill pushes the items next to it off a phone screen.
|
|
1657
|
+
*
|
|
1658
|
+
* role="status" and NOT aria-hidden, deliberately. The button
|
|
1659
|
+
* carries an explicit aria-label, and an explicit label REPLACES
|
|
1660
|
+
* the element's content for a screen reader — so a badge inside
|
|
1661
|
+
* it would be silent. As its own live region it is both read and
|
|
1662
|
+
* announced when the count changes, which is the whole point of a
|
|
1663
|
+
* badge that means "something needs you".
|
|
1664
|
+
************************************************************/
|
|
1665
|
+
function badge_text(value)
|
|
1666
|
+
{
|
|
1667
|
+
if(value === null || value === undefined || value === false) {
|
|
1668
|
+
return "";
|
|
1669
|
+
}
|
|
1670
|
+
if(typeof value === "number") {
|
|
1671
|
+
if(!isFinite(value) || value <= 0) {
|
|
1672
|
+
return "";
|
|
1673
|
+
}
|
|
1674
|
+
return (value > 99) ? "99+" : String(Math.floor(value));
|
|
1675
|
+
}
|
|
1676
|
+
return String(value).trim();
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
function badge_node(value)
|
|
1680
|
+
{
|
|
1681
|
+
let text = badge_text(value);
|
|
1682
|
+
let attrs = {class: "yui-toolbar-badge", role: "status"};
|
|
1683
|
+
if(!text) {
|
|
1684
|
+
attrs.hidden = "hidden";
|
|
1685
|
+
}
|
|
1686
|
+
return ["span", attrs, text];
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1644
1689
|
/************************************************************
|
|
1645
1690
|
* Renderer for the default ("action") item kind.
|
|
1646
1691
|
************************************************************/
|
|
@@ -1648,8 +1693,15 @@ function build_toolbar_action_item(gobj, it)
|
|
|
1648
1693
|
{
|
|
1649
1694
|
let children = [];
|
|
1650
1695
|
if(!empty_string(it.icon)) {
|
|
1651
|
-
|
|
1652
|
-
|
|
1696
|
+
/* The badge is anchored to the ICON, not to the button: with a
|
|
1697
|
+
* label on (desktop) the button is wide, and a badge pinned to
|
|
1698
|
+
* its corner would float far from the glyph it counts. */
|
|
1699
|
+
children.push(["span", {class: "icon yui-toolbar-icon"},
|
|
1700
|
+
[
|
|
1701
|
+
["i", {class: it.icon, "aria-hidden": "true"}],
|
|
1702
|
+
badge_node(it.badge)
|
|
1703
|
+
]
|
|
1704
|
+
]);
|
|
1653
1705
|
}
|
|
1654
1706
|
if(!empty_string(it.name)) {
|
|
1655
1707
|
children.push(["span", {class: "yui-toolbar-item-label", i18n: it.name},
|
|
@@ -3368,6 +3420,57 @@ function yui_shell_set_toolbar_item_icon(shell_gobj, item_id, icon_class)
|
|
|
3368
3420
|
}
|
|
3369
3421
|
}
|
|
3370
3422
|
|
|
3423
|
+
/************************************************************
|
|
3424
|
+
* Set (or clear) a toolbar item's badge — the count pinned to
|
|
3425
|
+
* its icon.
|
|
3426
|
+
*
|
|
3427
|
+
* yui_shell_set_toolbar_item_badge(shell, "alarms", 3);
|
|
3428
|
+
* yui_shell_set_toolbar_item_badge(shell, "alarms", 0); // hidden
|
|
3429
|
+
*
|
|
3430
|
+
* This is the API that matters: a count is a RUNTIME fact. The
|
|
3431
|
+
* `badge` field of a toolbar item only seeds the first paint, and
|
|
3432
|
+
* an app whose number never changes did not need a badge.
|
|
3433
|
+
*
|
|
3434
|
+
* 0 / "" / null / false all clear it, and a number over 99 renders
|
|
3435
|
+
* "99+" — see badge_node(). Accepts a string for the states that
|
|
3436
|
+
* are not counts ("!", "…").
|
|
3437
|
+
*
|
|
3438
|
+
* Silent no-op on an unknown item id: a toolbar that does not
|
|
3439
|
+
* declare the item (an app without alarms) must not be an error
|
|
3440
|
+
* at every tick of whatever feeds the number.
|
|
3441
|
+
************************************************************/
|
|
3442
|
+
function yui_shell_set_toolbar_item_badge(shell_gobj, item_id, value)
|
|
3443
|
+
{
|
|
3444
|
+
if(!shell_gobj || !is_gobj(shell_gobj) || empty_string(item_id)) {
|
|
3445
|
+
return;
|
|
3446
|
+
}
|
|
3447
|
+
let $container = gobj_read_attr(shell_gobj, "$container");
|
|
3448
|
+
if(!$container) {
|
|
3449
|
+
return;
|
|
3450
|
+
}
|
|
3451
|
+
let $badge = $container.querySelector(
|
|
3452
|
+
`[data-toolbar-item-id="${item_id}"] .yui-toolbar-badge`
|
|
3453
|
+
);
|
|
3454
|
+
if(!$badge) {
|
|
3455
|
+
return;
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3458
|
+
let text = badge_text(value);
|
|
3459
|
+
if(!text) {
|
|
3460
|
+
$badge.textContent = "";
|
|
3461
|
+
$badge.setAttribute("hidden", "hidden");
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3464
|
+
/* Do not touch the DOM when nothing changed: role="status" is a
|
|
3465
|
+
* live region, and rewriting the same number would have a screen
|
|
3466
|
+
* reader announce it again on every tick. */
|
|
3467
|
+
if($badge.textContent === text && !$badge.hasAttribute("hidden")) {
|
|
3468
|
+
return;
|
|
3469
|
+
}
|
|
3470
|
+
$badge.textContent = text;
|
|
3471
|
+
$badge.removeAttribute("hidden");
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3371
3474
|
/************************************************************
|
|
3372
3475
|
* Programmatic close of any open toolbar dropdown. Useful for
|
|
3373
3476
|
* external triggers (e.g. EV_LOGOUT firing from elsewhere) that
|
|
@@ -3414,6 +3517,7 @@ export {
|
|
|
3414
3517
|
yui_shell_language_changed,
|
|
3415
3518
|
yui_shell_set_connection_state,
|
|
3416
3519
|
yui_shell_set_toolbar_item_icon,
|
|
3520
|
+
yui_shell_set_toolbar_item_badge,
|
|
3417
3521
|
yui_shell_close_dropdown,
|
|
3418
3522
|
yui_shell_set_submenu
|
|
3419
3523
|
};
|