This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
The following commit(s) were added to refs/heads/main by this push:
new 5f16170 ui sidebar: group header click cascades to first layer's
first tab
5f16170 is described below
commit 5f161708d5ace2c42fcad578850689e8f5102554
Author: Wu Sheng <[email protected]>
AuthorDate: Sun May 17 11:19:01 2026 +0800
ui sidebar: group header click cascades to first layer's first tab
Group section headers ("Istio", "Self-Observability", "Platform
monitoring") were inert labels — operators had to click the layer
row first, then the tab, to reach a screen. Now clicking the
header cascades two levels: pick the first layer in the group,
expand it, navigate to its first tab. Matches the requested
"group menu only has the first sub menu select (nested twice),
then fire the real menu selection" behaviour.
Visual affordance: clickable section headers get cursor: pointer
+ a fg-1 hover lift so they read as actionable, without breaking
the L0 label voice (uppercase, fg-3).
---
apps/ui/src/shell/AppSidebar.vue | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/apps/ui/src/shell/AppSidebar.vue b/apps/ui/src/shell/AppSidebar.vue
index 94194ce..8bd763b 100644
--- a/apps/ui/src/shell/AppSidebar.vue
+++ b/apps/ui/src/shell/AppSidebar.vue
@@ -147,6 +147,24 @@ function toggleLayer(key: string): void {
}
}
+/**
+ * Group-header click — cascade two levels: expand the first layer in
+ * the group AND navigate to its first tab. Mirrors the behaviour
+ * described as "for group menu, only the first sub menu select
+ * (nested twice), then fire the real menu selection". Without this
+ * the section labels were inert and the operator had to drill in via
+ * two clicks (layer → tab) instead of one (group).
+ */
+function pickGroupFirst(layers: SidebarLayer[]): void {
+ const first = layers[0];
+ if (!first) return;
+ const nav = layerNavByKey.value.get(first.key);
+ if (!nav) return;
+ expandedLayer.value = first.key;
+ if (route.path === nav.primaryTo) return;
+ void router.push(nav.primaryTo);
+}
+
interface LayerGroup { kind: 'group'; label: string; layers: SidebarLayer[] }
interface LayerSingle { kind: 'single'; layer: SidebarLayer }
type SidebarEntry = LayerGroup | LayerSingle;
@@ -347,7 +365,10 @@ watch(
</div>
<template v-for="(E, ei) in sidebarEntries" :key="E.kind === 'group' ?
`g:${E.label}` : `s:${E.layer.key}:${ei}`">
<template v-if="E.kind === 'group'">
- <div class="sw-nav-section sw-nav-section--icon">
+ <div
+ class="sw-nav-section sw-nav-section--icon
sw-nav-section--clickable"
+ @click="pickGroupFirst(E.layers)"
+ >
<Icon :name="sectionIcon(E.label)" />
<span class="layer-group-name">{{ E.label }}</span>
</div>
@@ -438,7 +459,10 @@ watch(
</template>
<template v-if="operateLayers.length > 0">
- <div class="sw-nav-section sw-nav-section--icon">
+ <div
+ class="sw-nav-section sw-nav-section--icon sw-nav-section--clickable"
+ @click="pickGroupFirst(operateLayers)"
+ >
<Icon :name="sectionIcon('Platform monitoring')" />
<span>Platform monitoring</span>
</div>
@@ -758,6 +782,16 @@ watch(
color: var(--sw-fg-3);
opacity: 1;
}
+/* Clickable section header — cascades through the first child layer
+ * to its first tab. Hover lifts the text so the affordance reads. */
+.sw-nav-section--clickable {
+ cursor: pointer;
+ user-select: none;
+}
+.sw-nav-section--clickable:hover,
+.sw-nav-section--clickable:hover :deep(svg) {
+ color: var(--sw-fg-1);
+}
.layer-group-name { flex: 1; min-width: 0; }
/* Grouped and ungrouped layer rows sit at the same indent — the group
* header already delineates the section, so no extra tree-style nest. */