@yuneta/gobj-ui 5.5.0 → 5.7.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 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
@@ -890,7 +890,7 @@ function mt_start$15(gobj) {
890
890
  if (typeof shell_cfg.remember_section_position === "boolean") (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "remember_section_position", shell_cfg.remember_section_position);
891
891
  build_item_index(gobj, config);
892
892
  instantiate_menus(gobj, config);
893
- build_toolbar(gobj, config);
893
+ build_toolbar$1(gobj, config);
894
894
  preinstantiate_eager_views(gobj);
895
895
  if ((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "use_hash")) {
896
896
  priv.hash_handler = () => {
@@ -1616,7 +1616,7 @@ function safe_id(s) {
1616
1616
  * Per-item `show_on` is honoured: each rendered item is wrapped with
1617
1617
  * the same Bulma-helper logic as zones.
1618
1618
  ************************************************************/
1619
- function build_toolbar(gobj, config) {
1619
+ function build_toolbar$1(gobj, config) {
1620
1620
  let tb = config && config.toolbar;
1621
1621
  if (!tb || !(0, _yuneta_gobj_js.is_array)(tb.items)) return;
1622
1622
  let priv = gobj.priv;
@@ -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.
@@ -5683,6 +5760,59 @@ function refresh_clear($input) {
5683
5760
  $btn.classList.toggle("is-visible", clear_visible($input));
5684
5761
  }
5685
5762
  //#endregion
5763
+ //#region src/form_toolbar_plan.js
5764
+ /***********************************************************************
5765
+ * form_toolbar_plan.js
5766
+ *
5767
+ * Which buttons the C_YUI_FORM toolbar shows, and where.
5768
+ *
5769
+ * Pure, so it can be tested without a DOM: the gclass turns the
5770
+ * plan into elements, this decides the plan.
5771
+ *
5772
+ * Copyright (c) 2026, ArtGins.
5773
+ * All Rights Reserved.
5774
+ ***********************************************************************/
5775
+ var LEFT_BUTTONS = [
5776
+ "save",
5777
+ "undo",
5778
+ "clear"
5779
+ ];
5780
+ var RIGHT_BUTTONS = ["copy", "paste"];
5781
+ var DEFAULT_TOOLBAR = [
5782
+ "save",
5783
+ "undo",
5784
+ "clear",
5785
+ "copy",
5786
+ "paste"
5787
+ ];
5788
+ /***************************************************************
5789
+ * plan_toolbar(wanted) -> {left, right, unknown}
5790
+ *
5791
+ * wanted an array of button names, in the order asked for.
5792
+ * Anything that is not an array means "the default
5793
+ * five", so a caller that sets nothing keeps exactly
5794
+ * the toolbar this gclass always had.
5795
+ * [] means no toolbar at all.
5796
+ *
5797
+ * unknown names that match no button. They are reported and
5798
+ * NOT silently dropped: a typo in the list would
5799
+ * otherwise remove the save button with no trace.
5800
+ ***************************************************************/
5801
+ function plan_toolbar(wanted) {
5802
+ if (!Array.isArray(wanted)) wanted = DEFAULT_TOOLBAR;
5803
+ let left = [];
5804
+ let right = [];
5805
+ let unknown = [];
5806
+ for (let name of wanted) if (LEFT_BUTTONS.indexOf(name) >= 0) left.push(name);
5807
+ else if (RIGHT_BUTTONS.indexOf(name) >= 0) right.push(name);
5808
+ else unknown.push(name);
5809
+ return {
5810
+ left,
5811
+ right,
5812
+ unknown
5813
+ };
5814
+ }
5815
+ //#endregion
5686
5816
  //#region src/yui_theme.js
5687
5817
  /***********************************************************************
5688
5818
  * yui_theme.js
@@ -5814,6 +5944,13 @@ var attrs_table$13 = [
5814
5944
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_checkbox", 0, false, "Show a first column with checkbox to select rows"),
5815
5945
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_rownum", 0, false, "Print first column with row number"),
5816
5946
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_delete_row_btn", 0, false, "Print last column with delete row button"),
5947
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "toolbar", 0, [
5948
+ "save",
5949
+ "undo",
5950
+ "clear",
5951
+ "copy",
5952
+ "paste"
5953
+ ], "Toolbar buttons to show, in order: save, undo, clear, copy, paste. [] hides the toolbar"),
5817
5954
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "tabulator_settings", 0, {
5818
5955
  layout: "fitDataFill",
5819
5956
  validationMode: "highlight",
@@ -5924,6 +6061,175 @@ async function readClipboard$1(gobj) {
5924
6061
  }
5925
6062
  }
5926
6063
  /************************************************************
6064
+ * Bottom toolbar, from the `toolbar` attr.
6065
+ *
6066
+ * The five buttons it can show, and their order, are the
6067
+ * caller's: a dialog with a single action asks for ["save"],
6068
+ * and [] leaves no toolbar at all. Default is the five it
6069
+ * always had, so nothing moves for a caller that says nothing.
6070
+ ************************************************************/
6071
+ function toolbar_button(gobj, name) {
6072
+ switch (name) {
6073
+ case "save": return [
6074
+ "button",
6075
+ {
6076
+ class: "button p-1 button-save",
6077
+ title: "save",
6078
+ "aria-label": "save",
6079
+ disabled: true
6080
+ },
6081
+ [[
6082
+ "span",
6083
+ { class: "icon m-0" },
6084
+ "<i class=\"yi-floppy-disk\"></i>"
6085
+ ], [
6086
+ "span",
6087
+ {
6088
+ class: "is-hidden-mobile pl-1 pr-1",
6089
+ i18n: "save"
6090
+ },
6091
+ "save"
6092
+ ]],
6093
+ { click: function(evt) {
6094
+ evt.stopPropagation();
6095
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SAVE_RECORD", {}, gobj);
6096
+ } }
6097
+ ];
6098
+ case "undo": return [
6099
+ "button",
6100
+ {
6101
+ class: "button p-1 button-undo",
6102
+ title: "undo",
6103
+ "aria-label": "undo",
6104
+ disabled: true
6105
+ },
6106
+ [[
6107
+ "span",
6108
+ { class: "icon m-0" },
6109
+ "<i class=\"yi-arrow-rotate-left\"></i>"
6110
+ ], [
6111
+ "span",
6112
+ {
6113
+ class: "is-hidden-mobile pl-1 pr-1",
6114
+ i18n: "undo"
6115
+ },
6116
+ "undo"
6117
+ ]],
6118
+ { click: function(evt) {
6119
+ evt.stopPropagation();
6120
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_UNDO_RECORD", {}, gobj);
6121
+ } }
6122
+ ];
6123
+ case "clear": return [
6124
+ "button",
6125
+ {
6126
+ class: "button p-1",
6127
+ title: "clear",
6128
+ "aria-label": "clear"
6129
+ },
6130
+ [[
6131
+ "span",
6132
+ { class: "icon m-0" },
6133
+ "<i class=\"yi-broom-wide\"></i>"
6134
+ ], [
6135
+ "span",
6136
+ {
6137
+ class: "is-hidden-mobile pl-1 pr-1",
6138
+ i18n: "clear"
6139
+ },
6140
+ "clear"
6141
+ ]],
6142
+ { click: function(evt) {
6143
+ evt.stopPropagation();
6144
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CLEAR_RECORD", {}, gobj);
6145
+ } }
6146
+ ];
6147
+ case "copy": return [
6148
+ "button",
6149
+ {
6150
+ class: "button p-1",
6151
+ title: "copy",
6152
+ "aria-label": "copy"
6153
+ },
6154
+ [[
6155
+ "span",
6156
+ { class: "icon m-0" },
6157
+ "<i class=\"yi-copy\"></i>"
6158
+ ], [
6159
+ "span",
6160
+ {
6161
+ class: "is-hidden-mobile pl-1 pr-1",
6162
+ i18n: "copy"
6163
+ },
6164
+ "copy"
6165
+ ]],
6166
+ { click: function(evt) {
6167
+ evt.stopPropagation();
6168
+ (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_COPY_RECORD", {}, gobj);
6169
+ } }
6170
+ ];
6171
+ case "paste": return [
6172
+ "button",
6173
+ {
6174
+ class: "button p-1",
6175
+ title: "paste",
6176
+ "aria-label": "paste"
6177
+ },
6178
+ [[
6179
+ "span",
6180
+ { class: "icon m-0" },
6181
+ "<i class=\"yi-paste\"></i>"
6182
+ ], [
6183
+ "span",
6184
+ {
6185
+ class: "is-hidden-mobile pl-1 pr-1",
6186
+ i18n: "paste"
6187
+ },
6188
+ "paste"
6189
+ ]],
6190
+ { click: async function(evt) {
6191
+ evt.stopPropagation();
6192
+ await readClipboard$1(gobj);
6193
+ } }
6194
+ ];
6195
+ default: return null;
6196
+ }
6197
+ }
6198
+ function build_toolbar(gobj) {
6199
+ const plan = plan_toolbar((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "toolbar"));
6200
+ for (let name of plan.unknown) (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: unknown toolbar button '${name}'`);
6201
+ if (!plan.left.length && !plan.right.length) return [
6202
+ "span",
6203
+ {
6204
+ class: "yui-toolbar-form-empty",
6205
+ style: "display:none;"
6206
+ },
6207
+ ""
6208
+ ];
6209
+ return [
6210
+ "div",
6211
+ {
6212
+ class: "yui-toolbar-form is-flex-shrink-0 is-flex is-flex-direction-row is-justify-content-space-between is-align-items-center",
6213
+ style: "width:100%; flex-wrap:wrap; row-gap:6px;"
6214
+ },
6215
+ [[
6216
+ "div",
6217
+ {
6218
+ class: "p-1 is-flex is-flex-direction-row is-align-items-center",
6219
+ style: "column-gap:6px;"
6220
+ },
6221
+ plan.left.map((n) => toolbar_button(gobj, n))
6222
+ ], [
6223
+ "div",
6224
+ {
6225
+ class: "p-1 is-flex is-flex-direction-row is-align-items-center",
6226
+ style: "column-gap:6px;"
6227
+ },
6228
+ plan.right.map((n) => toolbar_button(gobj, n))
6229
+ ]]
6230
+ ];
6231
+ }
6232
+ /************************************************************
5927
6233
  * Build UI
5928
6234
  ************************************************************/
5929
6235
  function build_ui$11(gobj) {
@@ -5940,149 +6246,7 @@ function build_ui$11(gobj) {
5940
6246
  style: "overflow-y:auto; min-height:0;"
5941
6247
  },
5942
6248
  build_form(gobj)
5943
- ], [
5944
- "div",
5945
- {
5946
- class: "yui-toolbar-form is-flex-shrink-0 is-flex is-flex-direction-row is-justify-content-space-between is-align-items-center",
5947
- style: "width:100%; flex-wrap:wrap; row-gap:6px;"
5948
- },
5949
- [[
5950
- "div",
5951
- {
5952
- class: "p-1 is-flex is-flex-direction-row is-align-items-center",
5953
- style: "column-gap:6px;"
5954
- },
5955
- [
5956
- [
5957
- "button",
5958
- {
5959
- class: "button p-1 button-save",
5960
- title: "save",
5961
- "aria-label": "save",
5962
- disabled: true
5963
- },
5964
- [[
5965
- "span",
5966
- { class: "icon m-0" },
5967
- "<i class=\"yi-floppy-disk\"></i>"
5968
- ], [
5969
- "span",
5970
- {
5971
- class: "is-hidden-mobile pl-1 pr-1",
5972
- i18n: "save"
5973
- },
5974
- "save"
5975
- ]],
5976
- { click: function(evt) {
5977
- evt.stopPropagation();
5978
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SAVE_RECORD", {}, gobj);
5979
- } }
5980
- ],
5981
- [
5982
- "button",
5983
- {
5984
- class: "button p-1 button-undo",
5985
- title: "undo",
5986
- "aria-label": "undo",
5987
- disabled: true
5988
- },
5989
- [[
5990
- "span",
5991
- { class: "icon m-0" },
5992
- "<i class=\"yi-arrow-rotate-left\"></i>"
5993
- ], [
5994
- "span",
5995
- {
5996
- class: "is-hidden-mobile pl-1 pr-1",
5997
- i18n: "undo"
5998
- },
5999
- "undo"
6000
- ]],
6001
- { click: function(evt) {
6002
- evt.stopPropagation();
6003
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_UNDO_RECORD", {}, gobj);
6004
- } }
6005
- ],
6006
- [
6007
- "button",
6008
- {
6009
- class: "button p-1",
6010
- title: "clear",
6011
- "aria-label": "clear"
6012
- },
6013
- [[
6014
- "span",
6015
- { class: "icon m-0" },
6016
- "<i class=\"yi-broom-wide\"></i>"
6017
- ], [
6018
- "span",
6019
- {
6020
- class: "is-hidden-mobile pl-1 pr-1",
6021
- i18n: "clear"
6022
- },
6023
- "clear"
6024
- ]],
6025
- { click: function(evt) {
6026
- evt.stopPropagation();
6027
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CLEAR_RECORD", {}, gobj);
6028
- } }
6029
- ]
6030
- ]
6031
- ], [
6032
- "div",
6033
- {
6034
- class: "p-1 is-flex is-flex-direction-row is-align-items-center",
6035
- style: "column-gap:6px;"
6036
- },
6037
- [[
6038
- "button",
6039
- {
6040
- class: "button p-1",
6041
- title: "copy",
6042
- "aria-label": "copy"
6043
- },
6044
- [[
6045
- "span",
6046
- { class: "icon m-0" },
6047
- "<i class=\"yi-copy\"></i>"
6048
- ], [
6049
- "span",
6050
- {
6051
- class: "is-hidden-mobile pl-1 pr-1",
6052
- i18n: "copy"
6053
- },
6054
- "copy"
6055
- ]],
6056
- { click: function(evt) {
6057
- evt.stopPropagation();
6058
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_COPY_RECORD", {}, gobj);
6059
- } }
6060
- ], [
6061
- "button",
6062
- {
6063
- class: "button p-1",
6064
- title: "paste",
6065
- "aria-label": "paste"
6066
- },
6067
- [[
6068
- "span",
6069
- { class: "icon m-0" },
6070
- "<i class=\"yi-paste\"></i>"
6071
- ], [
6072
- "span",
6073
- {
6074
- class: "is-hidden-mobile pl-1 pr-1",
6075
- i18n: "paste"
6076
- },
6077
- "paste"
6078
- ]],
6079
- { click: async function(evt) {
6080
- evt.stopPropagation();
6081
- await readClipboard$1(gobj);
6082
- } }
6083
- ]]
6084
- ]]
6085
- ]]
6249
+ ], build_toolbar(gobj)]
6086
6250
  ]);
6087
6251
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $container);
6088
6252
  apply_form_mode(gobj, $container.querySelector("form"));
@@ -12943,7 +13107,12 @@ function cards_grid_descriptor(items, show_label) {
12943
13107
  * container; build_ui re-applies them on every rebuild.
12944
13108
  *
12945
13109
  * Each item supports:
12946
- * id, name, icon (CSS class or svg id), route, badge, disabled
13110
+ * id, name, icon (CSS class or svg id), route, disabled
13111
+ * (`badge` was listed here for a long time and NOTHING ever
13112
+ * rendered it — a menu item cannot carry a count today. The
13113
+ * toolbar can: `badge` on a toolbar item plus
13114
+ * yui_shell_set_toolbar_item_badge(). Implement it here the
13115
+ * day a menu entry needs one, rather than re-listing it.)
12947
13116
  * class — extra CSS class(es) on the item (tabs layout), e.g.
12948
13117
  * a per-item state colour like "yui-nav-disconnected"
12949
13118
  * closable — render a trailing ✕ that emits EV_NAV_ITEM_CLOSE
@@ -53622,6 +53791,7 @@ exports.yui_shell_register_overlay = yui_shell_register_overlay;
53622
53791
  exports.yui_shell_set_avatar_provider = yui_shell_set_avatar_provider;
53623
53792
  exports.yui_shell_set_connection_state = yui_shell_set_connection_state;
53624
53793
  exports.yui_shell_set_sub_routes = yui_shell_set_sub_routes;
53794
+ exports.yui_shell_set_toolbar_item_badge = yui_shell_set_toolbar_item_badge;
53625
53795
  exports.yui_shell_set_toolbar_item_icon = yui_shell_set_toolbar_item_icon;
53626
53796
  exports.yui_shell_set_translator = yui_shell_set_translator;
53627
53797
  exports.yui_shell_show_error = yui_shell_show_error;