Skip to content

Commit f64d42a

Browse files
authored
Merge pull request #550 from sboldyreva/claude/collapsible-ecosystems-sidebar-Ho5IR
Collapsible sidebar (ecosystems)
2 parents af29e51 + 10a67ee commit f64d42a

2 files changed

Lines changed: 112 additions & 12 deletions

File tree

docs/.vuepress/theme/sidebar/SidebarGroup.vue

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,46 @@
99
class="sidebar-group-items"
1010
v-if="open || !collapsable"
1111
>
12-
<li v-for="child in item?.children">
13-
<SidebarSectionHeader
14-
v-if="child?.type === 'section-header'"
15-
:item="child"
16-
/>
17-
<SidebarLink
18-
v-else
19-
:closeSidebarDrawer="closeSidebarDrawer"
20-
:item="child"
21-
/>
22-
</li>
12+
<template v-for="(section, sIdx) in sections" :key="sIdx">
13+
<template v-if="section.header">
14+
<li>
15+
<SidebarSectionHeader
16+
:item="section.header"
17+
:collapsible="true"
18+
:expanded="!collapsedSections[section.key]"
19+
@toggle="toggleSection(section.key)"
20+
/>
21+
<DropdownTransition>
22+
<ul
23+
v-if="!collapsedSections[section.key]"
24+
class="sidebar-section-items"
25+
>
26+
<li v-for="(child, cIdx) in section.items" :key="cIdx">
27+
<SidebarLink
28+
:closeSidebarDrawer="closeSidebarDrawer"
29+
:item="child"
30+
/>
31+
</li>
32+
</ul>
33+
</DropdownTransition>
34+
</li>
35+
</template>
36+
<template v-else>
37+
<li v-for="(child, cIdx) in section.items" :key="cIdx">
38+
<SidebarLink
39+
:closeSidebarDrawer="closeSidebarDrawer"
40+
:item="child"
41+
/>
42+
</li>
43+
</template>
44+
</template>
2345
</ul>
2446
</DropdownTransition>
2547
</div>
2648
</template>
2749

2850
<script setup>
51+
import { computed, reactive } from 'vue'
2952
import SidebarLink from './SidebarLink.vue'
3053
import SidebarSectionHeader from './SidebarSectionHeader.vue'
3154
import DropdownTransition from "../components/DropdownTransition.vue";
@@ -52,6 +75,28 @@ const props = defineProps({
5275
default: () => {}
5376
}
5477
})
78+
79+
const sections = computed(() => {
80+
const children = props.item?.children || []
81+
const result = []
82+
let current = { header: null, items: [], key: '__leading__' }
83+
for (const child of children) {
84+
if (child?.type === 'section-header') {
85+
if (current.header || current.items.length) result.push(current)
86+
current = { header: child, items: [], key: child.title || `section-${result.length}` }
87+
} else {
88+
current.items.push(child)
89+
}
90+
}
91+
if (current.header || current.items.length) result.push(current)
92+
return result
93+
})
94+
95+
const collapsedSections = reactive({})
96+
97+
const toggleSection = (key) => {
98+
collapsedSections[key] = !collapsedSections[key]
99+
}
55100
</script>
56101
57102
<style lang="stylus">
@@ -95,4 +140,10 @@ const props = defineProps({
95140
.sidebar-group-items
96141
transition height .1s ease-out
97142
overflow hidden
143+
144+
.sidebar-section-items
145+
padding 0
146+
margin 0
147+
list-style-type none
148+
overflow hidden
98149
</style>

docs/.vuepress/theme/sidebar/SidebarSectionHeader.vue

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,32 @@ defineProps({
33
item: {
44
type: Object,
55
required: true
6+
},
7+
collapsible: {
8+
type: Boolean,
9+
default: false
10+
},
11+
expanded: {
12+
type: Boolean,
13+
default: true
614
}
715
});
16+
17+
defineEmits(['toggle']);
818
</script>
919

1020
<template>
11-
<div class="sidebar-section-header">
21+
<div
22+
class="sidebar-section-header"
23+
:class="{ collapsible, expanded }"
24+
:role="collapsible ? 'button' : undefined"
25+
:tabindex="collapsible ? 0 : undefined"
26+
:aria-expanded="collapsible ? expanded : undefined"
27+
@click="collapsible && $emit('toggle')"
28+
@keydown.enter.prevent="collapsible && $emit('toggle')"
29+
@keydown.space.prevent="collapsible && $emit('toggle')"
30+
>
31+
<span v-if="collapsible" class="section-arrow" aria-hidden="true"></span>
1232
<img :src="item.icon" class="language-logo" alt="language-logo" loading="lazy" />
1333
<span class="language-title">{{ item.title }}</span>
1434
</div>
@@ -24,6 +44,21 @@ defineProps({
2444
left: -1rem;
2545
}
2646
47+
.sidebar-section-header.collapsible {
48+
cursor: pointer;
49+
user-select: none;
50+
}
51+
52+
.sidebar-section-header.collapsible:hover .language-title {
53+
color: #1a73e8;
54+
}
55+
56+
.sidebar-section-header.collapsible:focus-visible {
57+
outline: 2px solid #1a73e8;
58+
outline-offset: 2px;
59+
border-radius: 4px;
60+
}
61+
2762
.language-logo {
2863
max-width: 2rem;
2964
height: auto;
@@ -35,4 +70,18 @@ defineProps({
3570
color: #2c3e50;
3671
letter-spacing: 0.01em;
3772
}
73+
74+
.section-arrow {
75+
flex-shrink: 0;
76+
width: 1rem;
77+
height: 0.5625rem;
78+
background-image: url("../../public/expand-more.svg");
79+
background-repeat: no-repeat;
80+
background-position: center;
81+
background-size: 1rem 0.5625rem;
82+
}
83+
84+
.sidebar-section-header.expanded .section-arrow {
85+
background-image: url("../../public/expand-more-down.svg");
86+
}
3887
</style>

0 commit comments

Comments
 (0)