@yuneta/gobj-ui 2.2.4 → 2.3.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 +31 -1
- package/dist/gobj-ui.cjs.js +702 -1024
- package/dist/gobj-ui.es.js +703 -1025
- package/package.json +1 -1
- package/src/c_yui_form.js +322 -51
- package/src/c_yui_map.js +7 -4
- package/src/c_yui_nav.js +90 -1
- package/src/c_yui_shell.css +68 -2
- package/src/c_yui_shell.js +103 -18
- package/src/c_yui_treedb_topic_with_form.js +216 -1171
- package/src/nav_cards_helpers.js +71 -0
- package/src/nav_cards_helpers.test.js +122 -0
- package/src/shell_section_index.js +106 -0
- package/src/shell_section_index.test.js +151 -0
package/src/c_yui_map.js
CHANGED
|
@@ -559,10 +559,13 @@ function onClick(gobj, e)
|
|
|
559
559
|
const coordinates = e.features[0].geometry.coordinates.slice();
|
|
560
560
|
const properties = e.features[0].properties;
|
|
561
561
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
562
|
+
/* A marker may or may not be backed by a gobj service. Only look it up
|
|
563
|
+
* when the feature carries a name (verbose=false: a marker without a
|
|
564
|
+
* service is a normal case handled by the popup branch below, not an
|
|
565
|
+
* error). gobj_find_service() would otherwise crash on undefined. */
|
|
566
|
+
const gobj_service = properties.gobj_service_name
|
|
567
|
+
? gobj_find_service(properties.gobj_service_name, false)
|
|
568
|
+
: null;
|
|
566
569
|
if(gobj_service) {
|
|
567
570
|
let name = clean_name(gobj_name(gobj_service));
|
|
568
571
|
let window_service_name = `window-map-${name}`;
|
package/src/c_yui_nav.js
CHANGED
|
@@ -12,6 +12,17 @@
|
|
|
12
12
|
* "drawer" — off-canvas vertical (toggled by hamburger)
|
|
13
13
|
* "submenu" — Bulma .menu nested under a heading
|
|
14
14
|
* "accordion" — Bulma .menu list with collapsible group
|
|
15
|
+
* "cards" — grid of tappable cards (section-index landing;
|
|
16
|
+
* mounted as a stage view by C_YUI_SHELL when a
|
|
17
|
+
* primary item declares submenu.index)
|
|
18
|
+
* "backbar" — single "← <section>" link back to the section
|
|
19
|
+
* index; the mobile replacement of the tab strip
|
|
20
|
+
* for submenu.index sections (the shell collapses
|
|
21
|
+
* the secondary zone while the index is on stage)
|
|
22
|
+
*
|
|
23
|
+
* The `show_on` attr (same expressions as the shell's zone
|
|
24
|
+
* `show_on`) applies breakpoint visibility classes to the nav
|
|
25
|
+
* container; build_ui re-applies them on every rebuild.
|
|
15
26
|
*
|
|
16
27
|
* Each item supports:
|
|
17
28
|
* id, name, icon (CSS class or svg id), route, badge, disabled
|
|
@@ -51,13 +62,21 @@ import {
|
|
|
51
62
|
createElement2, empty_string, is_array, is_object, is_string,
|
|
52
63
|
} from "@yuneta/gobj-js";
|
|
53
64
|
|
|
65
|
+
import { cards_grid_descriptor } from "./nav_cards_helpers.js";
|
|
66
|
+
import {
|
|
67
|
+
BULMA_BP_ORDER,
|
|
68
|
+
breakpoints_from_expr,
|
|
69
|
+
bulma_hidden_class,
|
|
70
|
+
} from "./shell_show_on.js";
|
|
71
|
+
|
|
54
72
|
/***************************************************************
|
|
55
73
|
* Constants
|
|
56
74
|
***************************************************************/
|
|
57
75
|
const GCLASS_NAME = "C_YUI_NAV";
|
|
58
76
|
|
|
59
77
|
const SUPPORTED_LAYOUTS = [
|
|
60
|
-
"vertical", "icon-bar", "tabs", "drawer", "submenu", "accordion"
|
|
78
|
+
"vertical", "icon-bar", "tabs", "drawer", "submenu", "accordion", "cards",
|
|
79
|
+
"backbar"
|
|
61
80
|
];
|
|
62
81
|
|
|
63
82
|
/* Decorative items have no `route` and never participate in
|
|
@@ -82,6 +101,8 @@ SDATA(data_type_t.DTP_STRING, "layout", 0, "vertical", "vertical|ico
|
|
|
82
101
|
SDATA(data_type_t.DTP_STRING, "icon_pos", 0, "left", "left|right|top|bottom"),
|
|
83
102
|
SDATA(data_type_t.DTP_BOOLEAN, "show_label", 0, true, "Show item label next to/under the icon"),
|
|
84
103
|
SDATA(data_type_t.DTP_STRING, "level", 0, "primary", "primary|secondary"),
|
|
104
|
+
SDATA(data_type_t.DTP_STRING, "show_on", 0, "", "Breakpoint visibility expr (shell show_on syntax); empty = always"),
|
|
105
|
+
SDATA(data_type_t.DTP_STRING, "back_route", 0, "", "backbar layout: route of the section index the ← returns to"),
|
|
85
106
|
|
|
86
107
|
SDATA(data_type_t.DTP_POINTER, "shell", 0, null, "C_YUI_SHELL gobj (for navigation calls)"),
|
|
87
108
|
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "Root HTMLElement of this nav"),
|
|
@@ -200,12 +221,29 @@ function build_ui(gobj)
|
|
|
200
221
|
case "drawer": $container = render_drawer(gobj, items); break;
|
|
201
222
|
case "submenu": $container = render_submenu(gobj, items); break;
|
|
202
223
|
case "accordion": $container = render_accordion(gobj, items); break;
|
|
224
|
+
case "cards": $container = render_cards(gobj, items); break;
|
|
225
|
+
case "backbar": $container = render_backbar(gobj); break;
|
|
203
226
|
default: $container = render_vertical(gobj, items);
|
|
204
227
|
}
|
|
205
228
|
$container.setAttribute("data-nav-zone", zone);
|
|
206
229
|
$container.setAttribute("data-nav-layout", layout);
|
|
207
230
|
$container.classList.add("C_YUI_NAV", "yui-nav", `yui-nav-${layout}`);
|
|
208
231
|
|
|
232
|
+
/* Breakpoint visibility (same expressions as the shell's zone
|
|
233
|
+
* show_on). Applied here, attr-driven, so a rebuild via
|
|
234
|
+
* EV_SET_ITEMS re-applies it — the shell sets it only once at
|
|
235
|
+
* creation time. */
|
|
236
|
+
let show_on = gobj_read_attr(gobj, "show_on") || "";
|
|
237
|
+
if(!empty_string(show_on) && show_on !== "*") {
|
|
238
|
+
let visible = breakpoints_from_expr(show_on);
|
|
239
|
+
for(let bp of BULMA_BP_ORDER) {
|
|
240
|
+
if(!visible[bp]) {
|
|
241
|
+
$container.classList.add(bulma_hidden_class(bp));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
$container.setAttribute("data-show-on", show_on);
|
|
245
|
+
}
|
|
246
|
+
|
|
209
247
|
/* Accessibility: every nav root is a landmark. For the drawer we
|
|
210
248
|
* tag the wrapper as role=dialog and the panel as role=navigation. */
|
|
211
249
|
if(layout !== "drawer") {
|
|
@@ -383,6 +421,50 @@ function render_tabs(gobj, items)
|
|
|
383
421
|
);
|
|
384
422
|
}
|
|
385
423
|
|
|
424
|
+
function render_cards(gobj, items)
|
|
425
|
+
{
|
|
426
|
+
let show_label = gobj_read_attr(gobj, "show_label");
|
|
427
|
+
return createElement2(cards_grid_descriptor(items, show_label));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/************************************************************
|
|
431
|
+
* "backbar": a single "← <section>" link back to the section
|
|
432
|
+
* index route. Static DOM — the active child view names itself
|
|
433
|
+
* in its own content, so the bar only carries the way back plus
|
|
434
|
+
* section context. While the index itself is on stage the shell
|
|
435
|
+
* collapses the whole secondary zone (tabs and cards never
|
|
436
|
+
* coexist), so the bar needs no self-hiding logic.
|
|
437
|
+
************************************************************/
|
|
438
|
+
function render_backbar(gobj)
|
|
439
|
+
{
|
|
440
|
+
let back_route = gobj_read_attr(gobj, "back_route") || "";
|
|
441
|
+
let label = gobj_read_attr(gobj, "nav_label") || "";
|
|
442
|
+
|
|
443
|
+
let label_attrs = {class: "yui-nav-label"};
|
|
444
|
+
if(label) {
|
|
445
|
+
label_attrs.i18n = label;
|
|
446
|
+
}
|
|
447
|
+
let a_attrs = {
|
|
448
|
+
class: "yui-nav-item yui-nav-back",
|
|
449
|
+
href: back_route ? "#" + back_route : "#",
|
|
450
|
+
"data-item-id": "__index__",
|
|
451
|
+
"data-route": back_route,
|
|
452
|
+
"aria-label": label || "back"
|
|
453
|
+
};
|
|
454
|
+
if(label) {
|
|
455
|
+
a_attrs["data-i18n-aria-label"] = label;
|
|
456
|
+
}
|
|
457
|
+
return createElement2(
|
|
458
|
+
["div", {},
|
|
459
|
+
["a", a_attrs, [
|
|
460
|
+
["span", {class: "icon is-small"},
|
|
461
|
+
["i", {class: "yi-arrow-left", "aria-hidden": "true"}]],
|
|
462
|
+
["span", label_attrs, label]
|
|
463
|
+
]]
|
|
464
|
+
]
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
386
468
|
function render_drawer(gobj, items)
|
|
387
469
|
{
|
|
388
470
|
/* Off-canvas drawer: initially hidden; toggled via .is-active on the
|
|
@@ -757,6 +839,13 @@ function ac_route_changed(gobj, event, kw, src)
|
|
|
757
839
|
return 0;
|
|
758
840
|
}
|
|
759
841
|
|
|
842
|
+
/* backbar: no per-item highlight. Its visibility while the index
|
|
843
|
+
* itself is on stage is the shell's job (the whole secondary zone
|
|
844
|
+
* collapses at the index route — tabs and cards never coexist). */
|
|
845
|
+
if(gobj_read_attr(gobj, "layout") === "backbar") {
|
|
846
|
+
return 0;
|
|
847
|
+
}
|
|
848
|
+
|
|
760
849
|
/* Remove prior active class on any children */
|
|
761
850
|
let previouslyActive = $c.querySelectorAll(".is-active");
|
|
762
851
|
previouslyActive.forEach(n => n.classList.remove("is-active"));
|
package/src/c_yui_shell.css
CHANGED
|
@@ -167,13 +167,21 @@
|
|
|
167
167
|
}
|
|
168
168
|
.yui-nav-iconbar .yui-nav-item.is-active,
|
|
169
169
|
.yui-nav-iconbar li.is-active > .yui-nav-item {
|
|
170
|
-
color: var(--bulma-link, #485fc7);
|
|
171
|
-
|
|
170
|
+
background-color: var(--bulma-link, #485fc7);
|
|
171
|
+
color: var(--bulma-link-invert, #fff);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
/*--- tabs (Bulma .tabs) ---*/
|
|
175
175
|
.yui-nav-tabs .tabs ul { flex-wrap: wrap; }
|
|
176
176
|
|
|
177
|
+
/* Secondary-nav tab strip (top-sub zone): Bulma gives `.tabs:not(:last-child)`
|
|
178
|
+
* a 1.5rem block-spacing margin-bottom, which steals content height on every
|
|
179
|
+
* view (worst on mobile). The zone already has its own bottom border for
|
|
180
|
+
* separation, so drop the margin. Specificity (3 classes) beats Bulma's rule. */
|
|
181
|
+
.yui-zone-top-sub .yui-nav-tabs.tabs {
|
|
182
|
+
margin-bottom: 0.25rem;
|
|
183
|
+
}
|
|
184
|
+
|
|
177
185
|
/*--- closable tab: trailing ✕ affordance (item.closable) ---*/
|
|
178
186
|
.yui-nav-close {
|
|
179
187
|
opacity: 0.55;
|
|
@@ -189,6 +197,64 @@
|
|
|
189
197
|
/*--- per-item state class (item.class) — generic disconnected tint ---*/
|
|
190
198
|
.yui-nav-tabs .tabs li.yui-nav-disconnected a { color: var(--bulma-danger, #f14668); }
|
|
191
199
|
|
|
200
|
+
/*--- cards (section-index landing grid, mounted as a stage view) ---*/
|
|
201
|
+
.yui-nav-cards {
|
|
202
|
+
display: grid;
|
|
203
|
+
grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
|
|
204
|
+
gap: 0.75rem;
|
|
205
|
+
padding: 1rem;
|
|
206
|
+
width: 100%;
|
|
207
|
+
align-content: start;
|
|
208
|
+
}
|
|
209
|
+
.yui-nav-cards .yui-nav-card {
|
|
210
|
+
display: flex;
|
|
211
|
+
flex-direction: column;
|
|
212
|
+
align-items: center;
|
|
213
|
+
justify-content: center;
|
|
214
|
+
gap: 0.5rem;
|
|
215
|
+
padding: 1.25rem 0.75rem;
|
|
216
|
+
border: 1px solid var(--bulma-border-weak, #dbdbdb);
|
|
217
|
+
border-radius: 0.5rem;
|
|
218
|
+
background: var(--bulma-scheme-main, #fff);
|
|
219
|
+
color: var(--bulma-text, inherit);
|
|
220
|
+
text-decoration: none;
|
|
221
|
+
text-align: center;
|
|
222
|
+
line-height: 1.2;
|
|
223
|
+
word-break: break-word;
|
|
224
|
+
}
|
|
225
|
+
/* yui_icons are 1em CSS masks: font-size scales them. */
|
|
226
|
+
.yui-nav-cards .yui-nav-card .icon {
|
|
227
|
+
font-size: 1.5rem;
|
|
228
|
+
}
|
|
229
|
+
.yui-nav-cards .yui-nav-card:hover,
|
|
230
|
+
.yui-nav-cards .yui-nav-card:focus-visible {
|
|
231
|
+
border-color: var(--bulma-link, #485fc7);
|
|
232
|
+
color: var(--bulma-link, #485fc7);
|
|
233
|
+
}
|
|
234
|
+
.yui-nav-cards .yui-nav-card.is-active {
|
|
235
|
+
border-color: var(--bulma-link, #485fc7);
|
|
236
|
+
}
|
|
237
|
+
.yui-nav-cards .yui-nav-card[data-disabled="1"] {
|
|
238
|
+
opacity: 0.5;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/*--- backbar (mobile "← <section>" back-to-index, submenu.index) ---*/
|
|
242
|
+
.yui-nav-backbar {
|
|
243
|
+
display: flex;
|
|
244
|
+
align-items: center;
|
|
245
|
+
padding: 0.25rem 0.5rem;
|
|
246
|
+
}
|
|
247
|
+
.yui-nav-backbar .yui-nav-back {
|
|
248
|
+
display: inline-flex;
|
|
249
|
+
align-items: center;
|
|
250
|
+
gap: 0.4rem;
|
|
251
|
+
padding: 0.25rem 0.5rem;
|
|
252
|
+
border-radius: 0.375rem;
|
|
253
|
+
color: var(--bulma-text, inherit);
|
|
254
|
+
text-decoration: none;
|
|
255
|
+
font-weight: 600;
|
|
256
|
+
}
|
|
257
|
+
|
|
192
258
|
/*--- stacked item (icon above label) ---*/
|
|
193
259
|
.yui-nav-stacked {
|
|
194
260
|
display: inline-flex;
|
package/src/c_yui_shell.js
CHANGED
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
* $container in its stage, hide the previous one. Lifecycle per item
|
|
17
17
|
* decides when the gobj is created/destroyed.
|
|
18
18
|
*
|
|
19
|
+
* A primary item with `submenu.index` gets a section-index landing:
|
|
20
|
+
* its own route mounts the submenu as a "cards" C_YUI_NAV in the
|
|
21
|
+
* stage instead of redirecting to the default child (see
|
|
22
|
+
* shell_section_index.js for the contract).
|
|
23
|
+
*
|
|
19
24
|
* Hash-based 2-level routing (#/primary/secondary). No dependency on
|
|
20
25
|
* C_YUI_ROUTING.
|
|
21
26
|
*
|
|
@@ -53,6 +58,10 @@ import {
|
|
|
53
58
|
} from "./shell_toolbar_helpers.js";
|
|
54
59
|
|
|
55
60
|
import { resolve_route } from "./route_resolver.js";
|
|
61
|
+
import {
|
|
62
|
+
section_index_target,
|
|
63
|
+
secondary_nav_renders,
|
|
64
|
+
} from "./shell_section_index.js";
|
|
56
65
|
|
|
57
66
|
/***************************************************************
|
|
58
67
|
* Constants
|
|
@@ -621,18 +630,28 @@ function build_item_index(gobj, config)
|
|
|
621
630
|
let items = (menu && menu.items) || [];
|
|
622
631
|
for(let item of items) {
|
|
623
632
|
if(item.route) {
|
|
633
|
+
/* Section-index landing (submenu.index): the section
|
|
634
|
+
* route gets a synthesized target — the submenu itself
|
|
635
|
+
* rendered as a "cards" C_YUI_NAV in the stage — so it
|
|
636
|
+
* becomes a real resting, deep-linkable route instead
|
|
637
|
+
* of redirecting to the default child (navigate_to()
|
|
638
|
+
* only redirects while the entry has NO target).
|
|
639
|
+
* Explicit inline targets keep precedence (the helper
|
|
640
|
+
* returns null then). */
|
|
641
|
+
let target = item.target || section_index_target(menu_id, item);
|
|
642
|
+
|
|
624
643
|
/* A later menu must NOT clobber an earlier entry that
|
|
625
644
|
* has a valid target with one that has none. This is
|
|
626
645
|
* the common case where a `quick` drawer just reuses
|
|
627
646
|
* routes declared (with target) in `primary.submenu`.
|
|
628
647
|
* Rule: prefer the first entry with a target. */
|
|
629
648
|
let prev = priv.item_index[item.route];
|
|
630
|
-
if(!prev || (!prev.target &&
|
|
649
|
+
if(!prev || (!prev.target && target)) {
|
|
631
650
|
priv.item_index[item.route] = {
|
|
632
651
|
item: item,
|
|
633
652
|
parent_item: null,
|
|
634
|
-
stage:
|
|
635
|
-
target:
|
|
653
|
+
stage: target && target.stage || null,
|
|
654
|
+
target: target,
|
|
636
655
|
menu_id: menu_id
|
|
637
656
|
};
|
|
638
657
|
}
|
|
@@ -761,19 +780,27 @@ function instantiate_menus(gobj, config)
|
|
|
761
780
|
);
|
|
762
781
|
continue;
|
|
763
782
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
783
|
+
/* Index sections (submenu.index) get a render PLAN:
|
|
784
|
+
* the declared nav constrained to ">=tablet" plus a
|
|
785
|
+
* mobile "backbar" (← <section>) — tabs and cards
|
|
786
|
+
* never coexist, and a child view on mobile offers
|
|
787
|
+
* the way back instead of a duplicated tab strip. */
|
|
788
|
+
let renders = secondary_nav_renders(item, render_to_obj(layout));
|
|
768
789
|
let sub_menu_id = `secondary.${menu_id}.${item.id}`;
|
|
769
|
-
let
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
790
|
+
for(let render_cfg of renders) {
|
|
791
|
+
let submenu_def = {
|
|
792
|
+
items: sub.items,
|
|
793
|
+
render: { [zone_id]: render_cfg }
|
|
794
|
+
};
|
|
795
|
+
let nav = instantiate_nav_in_zone(
|
|
796
|
+
gobj, submenu_def, sub_menu_id, zone_id, "secondary",
|
|
797
|
+
item.name || ""
|
|
798
|
+
);
|
|
799
|
+
/* Hidden until owning primary is active. */
|
|
800
|
+
let $c = gobj_read_attr(nav, "$container");
|
|
801
|
+
if($c) {
|
|
802
|
+
$c.classList.add("is-hidden");
|
|
803
|
+
}
|
|
777
804
|
}
|
|
778
805
|
}
|
|
779
806
|
}
|
|
@@ -801,8 +828,11 @@ function instantiate_nav_in_zone(gobj, menu, menu_id, zone_id, level, nav_label)
|
|
|
801
828
|
render_cfg = { layout: render_cfg };
|
|
802
829
|
}
|
|
803
830
|
|
|
804
|
-
let nav_name = `nav_${menu_id.replace(/\./g, "_")}_${zone_id}`;
|
|
805
831
|
let layout = render_cfg.layout || "vertical";
|
|
832
|
+
/* A backbar shares menu_id and zone with the nav it complements —
|
|
833
|
+
* suffix the gobj name to avoid a sibling-name collision. */
|
|
834
|
+
let nav_name = `nav_${menu_id.replace(/\./g, "_")}_${zone_id}` +
|
|
835
|
+
(layout === "backbar" ? "_backbar" : "");
|
|
806
836
|
let nav = gobj_create(
|
|
807
837
|
nav_name,
|
|
808
838
|
"C_YUI_NAV",
|
|
@@ -815,6 +845,8 @@ function instantiate_nav_in_zone(gobj, menu, menu_id, zone_id, level, nav_label)
|
|
|
815
845
|
icon_pos: render_cfg.icon_pos || default_icon_pos(zone_id),
|
|
816
846
|
show_label: render_cfg.show_label !== false,
|
|
817
847
|
level: level,
|
|
848
|
+
show_on: render_cfg.show_on || "",
|
|
849
|
+
back_route: render_cfg.back_route || "",
|
|
818
850
|
shell: gobj
|
|
819
851
|
},
|
|
820
852
|
gobj
|
|
@@ -1174,6 +1206,16 @@ function build_view_gobj(gobj, entry, route, stage)
|
|
|
1174
1206
|
}
|
|
1175
1207
|
stage.el.appendChild($view);
|
|
1176
1208
|
gobj_start(view);
|
|
1209
|
+
|
|
1210
|
+
/* App view gclasses translate their own DOM; a section-index view
|
|
1211
|
+
* (synthesized "cards" C_YUI_NAV) is SHELL-owned DOM built after
|
|
1212
|
+
* the host's one-shot refresh_language — apply the registered
|
|
1213
|
+
* translator, same policy as lazily-built dropdown panels. */
|
|
1214
|
+
let priv = gobj_read_attr(gobj, "priv");
|
|
1215
|
+
if(priv && typeof priv.translator === "function" &&
|
|
1216
|
+
target.gclass === "C_YUI_NAV") {
|
|
1217
|
+
refresh_language($view, priv.translator);
|
|
1218
|
+
}
|
|
1177
1219
|
return view;
|
|
1178
1220
|
}
|
|
1179
1221
|
|
|
@@ -2002,7 +2044,19 @@ function update_secondary_nav_visibility(gobj, entry)
|
|
|
2002
2044
|
* secondary navs and a nav-derived check would never collapse
|
|
2003
2045
|
* the zone (empty white strip under the toolbar). */
|
|
2004
2046
|
let active_primary = entry.parent_item || entry.item || null;
|
|
2005
|
-
|
|
2047
|
+
|
|
2048
|
+
/* At the section-index route itself (a level-1 item with
|
|
2049
|
+
* submenu.index) the cards ARE the navigation: showing the tab
|
|
2050
|
+
* strip too would duplicate it (confusing — either tabs or
|
|
2051
|
+
* cards, never both). Collapse the secondary zone there. */
|
|
2052
|
+
let at_section_index = !!(
|
|
2053
|
+
!entry.parent_item &&
|
|
2054
|
+
active_primary &&
|
|
2055
|
+
active_primary.submenu &&
|
|
2056
|
+
active_primary.submenu.index
|
|
2057
|
+
);
|
|
2058
|
+
|
|
2059
|
+
let has_secondary = !at_section_index && !!(
|
|
2006
2060
|
active_primary &&
|
|
2007
2061
|
active_primary.submenu &&
|
|
2008
2062
|
Array.isArray(active_primary.submenu.items) &&
|
|
@@ -2037,7 +2091,7 @@ function update_secondary_nav_visibility(gobj, entry)
|
|
|
2037
2091
|
if(!$c) {
|
|
2038
2092
|
continue;
|
|
2039
2093
|
}
|
|
2040
|
-
if(nav_menu_id === target_secondary_id) {
|
|
2094
|
+
if(nav_menu_id === target_secondary_id && has_secondary) {
|
|
2041
2095
|
$c.classList.remove("is-hidden");
|
|
2042
2096
|
} else {
|
|
2043
2097
|
$c.classList.add("is-hidden");
|
|
@@ -2130,6 +2184,11 @@ function find_secondary_nav(priv, parent_item_id)
|
|
|
2130
2184
|
for(let nav of priv.navs) {
|
|
2131
2185
|
let menu_id = gobj_read_attr(nav, "menu_id") || "";
|
|
2132
2186
|
if(menu_id.startsWith("secondary.") && menu_id.endsWith(suffix)) {
|
|
2187
|
+
/* A backbar shares the menu_id but renders no items —
|
|
2188
|
+
* EV_SET_ITEMS must reach the item-based nav. */
|
|
2189
|
+
if(gobj_read_attr(nav, "layout") === "backbar") {
|
|
2190
|
+
continue;
|
|
2191
|
+
}
|
|
2133
2192
|
return nav;
|
|
2134
2193
|
}
|
|
2135
2194
|
}
|
|
@@ -2237,6 +2296,32 @@ function yui_shell_set_submenu(shell_gobj, parent_item_id, items)
|
|
|
2237
2296
|
|
|
2238
2297
|
/* Push the new items into the nav (rebuilds its DOM in place). */
|
|
2239
2298
|
gobj_send_event(nav, "EV_SET_ITEMS", {items: items}, shell_gobj);
|
|
2299
|
+
|
|
2300
|
+
/* Section-index landing (submenu.index): keep the synthesized
|
|
2301
|
+
* target and any mounted index view (a "cards" C_YUI_NAV in the
|
|
2302
|
+
* stage) in sync with the new items — otherwise a later mount,
|
|
2303
|
+
* or the already mounted view, would render the stale set. */
|
|
2304
|
+
if(parent_item && parent_item.route &&
|
|
2305
|
+
parent_item.submenu && parent_item.submenu.index) {
|
|
2306
|
+
let entry = priv.item_index[parent_item.route];
|
|
2307
|
+
if(entry && entry.target &&
|
|
2308
|
+
entry.target.gclass === "C_YUI_NAV" && entry.target.kw) {
|
|
2309
|
+
entry.target.kw.menu_items = items;
|
|
2310
|
+
}
|
|
2311
|
+
for(let stage_name in priv.stages) {
|
|
2312
|
+
let stage = priv.stages[stage_name];
|
|
2313
|
+
let view = stage.items && stage.items[parent_item.route];
|
|
2314
|
+
if(view) {
|
|
2315
|
+
gobj_send_event(view, "EV_SET_ITEMS", {items: items}, shell_gobj);
|
|
2316
|
+
/* EV_SET_ITEMS rebuilt the DOM: re-apply the translator
|
|
2317
|
+
* (shell-owned DOM, same policy as build_view_gobj). */
|
|
2318
|
+
let $view = gobj_read_attr(view, "$container");
|
|
2319
|
+
if($view && typeof priv.translator === "function") {
|
|
2320
|
+
refresh_language($view, priv.translator);
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2240
2325
|
return 0;
|
|
2241
2326
|
}
|
|
2242
2327
|
|