Skip to content

Commit b43ae04

Browse files
committed
feat: implement new sidebar menu with expand and collapse functionality, plus improvements in component styling and layout
1 parent b97d71d commit b43ae04

File tree

39 files changed

+357
-31
lines changed

39 files changed

+357
-31
lines changed

docs/app/components/AsideMenu.vue

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
11
<template>
2-
<div class="border-2 border-primary-light-500 rounded-lg p-4 h-full w-56 px-8">
3-
<AsideMenuItems :items="items" />
2+
<div class="aside-menu-container">
3+
<div class="aside-menu-content">
4+
<div class="menu-header">
5+
<div class="menu-title">
6+
Menu
7+
</div>
8+
<div class="menu-controls">
9+
<button
10+
class="expand-all-btn"
11+
@click="toggleAllSections"
12+
:title="isAllExpanded ? 'Colapsar tudo' : 'Expandir tudo'"
13+
>
14+
{{ isAllExpanded ? '⊖' : '⊕' }}
15+
</button>
16+
</div>
17+
</div>
18+
<AsideMenuItems :items="items" />
19+
</div>
420
</div>
521
</template>
622

723
<script lang="ts" setup>
24+
import { computed } from 'vue'
25+
import { useAccordion } from '../composables/useAccordion'
826
import AsideMenuItems from './AsideMenuItems.vue'
927
1028
interface Items {
@@ -86,8 +104,86 @@ const items: Items[] = [
86104
],
87105
},
88106
]
107+
108+
const { openSections, openAllSections, closeAllSections } = useAccordion()
109+
110+
const isAllExpanded = computed(() => {
111+
return openSections.value.size === items.length
112+
})
113+
114+
const toggleAllSections = () => {
115+
if (isAllExpanded.value) {
116+
closeAllSections()
117+
}
118+
else {
119+
openAllSections()
120+
}
121+
}
89122
</script>
90123

91-
<style>
124+
<style scoped>
125+
.aside-menu-container {
126+
@apply relative w-72 h-full;
127+
}
128+
129+
.aside-menu-content {
130+
@apply relative h-full bg-white dark:bg-primary-light-800 rounded-2xl border-2 border-primary-light-500 shadow-lg;
131+
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
132+
box-shadow:
133+
0 8px 32px rgba(0, 0, 0, 0.1),
134+
0 4px 16px rgba(0, 0, 0, 0.05),
135+
inset 0 1px 0 rgba(255, 255, 255, 0.8);
136+
transform: rotate(-0.5deg);
137+
transition: all 0.3s ease;
138+
margin: 8px;
139+
}
140+
141+
.aside-menu-content:hover {
142+
transform: rotate(0deg) scale(1.02);
143+
box-shadow:
144+
0 12px 40px rgba(0, 0, 0, 0.15),
145+
0 6px 20px rgba(0, 0, 0, 0.08),
146+
inset 0 1px 0 rgba(255, 255, 255, 0.9);
147+
}
148+
149+
.menu-header {
150+
@apply relative p-6 pb-4 border-b-2 border-primary-light-500/20 flex justify-between items-center;
151+
border-radius: 14px 14px 0 0;
152+
}
92153
154+
.menu-title {
155+
@apply text-2xl font-bold text-primary-light-600 dark:text-primary-dark-400;
156+
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.8);
157+
transform: rotate(-1deg);
158+
}
159+
160+
.menu-controls {
161+
@apply flex items-center;
162+
}
163+
164+
.expand-all-btn {
165+
@apply w-8 h-8 rounded-full bg-primary-light-500 text-white text-lg font-bold flex items-center justify-center transition-all duration-200;
166+
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
167+
transform: rotate(0deg);
168+
}
169+
170+
.expand-all-btn:hover {
171+
@apply bg-primary-light-600;
172+
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
173+
transform: scale(1.1) rotate(5deg);
174+
}
175+
176+
@media (prefers-color-scheme: dark) {
177+
.aside-menu-content {
178+
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
179+
box-shadow:
180+
0 8px 32px rgba(0, 0, 0, 0.3),
181+
0 4px 16px rgba(0, 0, 0, 0.2),
182+
inset 0 1px 0 rgba(255, 255, 255, 0.1);
183+
}
184+
185+
.menu-title {
186+
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
187+
}
188+
}
93189
</style>
Lines changed: 229 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,253 @@
11
<template>
2-
<div class="font-patrick flex flex-col h-full overflow-y-auto">
2+
<div class="menu-items-container">
33
<div
44
v-for="list, index in items"
55
:key="index"
6-
class="space-y-2"
6+
class="menu-section"
77
>
8-
<div class="font-bold mb-2">
9-
{{ list.title }}
10-
</div>
118
<div
12-
v-for="item in list.links"
13-
:key="item.name"
14-
class="w-full flex justify-between items-center font-sans text-sm"
9+
class="section-header"
10+
@click="toggleSection(list.title)"
1511
>
16-
<a
17-
:href="item.link"
18-
>{{ item.name }}</a>
19-
<PUBadge
20-
v-if="item.new"
21-
label="new"
22-
/>
12+
<div class="section-title">
13+
{{ list.title }}
14+
</div>
15+
<div class="section-toggle">
16+
<div
17+
class="toggle-icon"
18+
:class="{ rotated: isSectionOpen(list.title) }"
19+
>
20+
<PUIcon
21+
name="chevron-down"
22+
size="small"
23+
/>
24+
</div>
25+
</div>
2326
</div>
27+
<Transition
28+
name="accordion"
29+
@enter="onEnter"
30+
@leave="onLeave"
31+
>
32+
<div
33+
v-show="isSectionOpen(list.title)"
34+
class="section-content"
35+
>
36+
<div class="section-links">
37+
<div
38+
v-for="item in list.links"
39+
:key="item.name"
40+
class="menu-link-item group"
41+
>
42+
<a
43+
:href="item.link"
44+
class="menu-link"
45+
>{{ item.name }}</a>
46+
<PUBadge
47+
v-if="item.new"
48+
label="new"
49+
class="new-badge"
50+
/>
51+
</div>
52+
</div>
53+
</div>
54+
</Transition>
2455
<div
2556
v-if="index !== items.length - 1"
26-
class="separator py-2"
57+
class="section-separator"
2758
>
28-
<div class="border-[1px] border-primary-light-500/10 " />
59+
<div class="separator-line"></div>
2960
</div>
3061
</div>
3162
</div>
3263
</template>
3364

3465
<script setup lang="ts">
66+
import { useAccordion } from '../composables/useAccordion'
67+
3568
defineProps<{
3669
items: {
3770
title: string
3871
links: { name: string, link: string, new?: boolean }[]
3972
}[]
4073
}>()
74+
75+
const { toggleSection, isSectionOpen } = useAccordion()
76+
77+
const onEnter = (el: Element) => {
78+
const target = el as HTMLElement
79+
target.style.height = '0px'
80+
target.style.height = `${target.scrollHeight}px`
81+
}
82+
83+
const onLeave = (el: Element) => {
84+
const target = el as HTMLElement
85+
target.style.height = `${target.scrollHeight}px`
86+
target.style.height = '0px'
87+
}
4188
</script>
89+
90+
<style scoped>
91+
.menu-items-container {
92+
@apply flex flex-col h-full overflow-y-auto p-6 space-y-4;
93+
scrollbar-width: thin;
94+
scrollbar-color: #cbd5e1 transparent;
95+
}
96+
97+
.menu-items-container::-webkit-scrollbar {
98+
width: 6px;
99+
}
100+
101+
.menu-items-container::-webkit-scrollbar-track {
102+
background: transparent;
103+
}
104+
105+
.menu-items-container::-webkit-scrollbar-thumb {
106+
background: #cbd5e1;
107+
border-radius: 3px;
108+
}
109+
110+
.menu-items-container::-webkit-scrollbar-thumb:hover {
111+
background: #94a3b8;
112+
}
113+
114+
.menu-section {
115+
@apply space-y-2;
116+
}
117+
118+
.section-header {
119+
@apply flex justify-between items-center cursor-pointer p-2 rounded-lg transition-all duration-200;
120+
background: linear-gradient(135deg, rgba(241, 245, 249, 0.5) 0%, rgba(226, 232, 240, 0.5) 100%);
121+
}
122+
123+
.section-header:hover {
124+
background: linear-gradient(135deg, rgba(241, 245, 249, 0.8) 0%, rgba(226, 232, 240, 0.8) 100%);
125+
transform: translateX(2px);
126+
}
127+
128+
.section-title {
129+
@apply font-bold text-lg text-primary-light-600 dark:text-primary-dark-400;
130+
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
131+
transform: rotate(-0.5deg);
132+
position: relative;
133+
}
134+
135+
136+
.section-toggle {
137+
@apply flex items-center justify-center w-6 h-6;
138+
}
139+
140+
.toggle-icon {
141+
@apply text-primary-light-500 text-sm font-bold transition-transform duration-300;
142+
transform: rotate(0deg);
143+
}
144+
145+
.toggle-icon.rotated {
146+
transform: rotate(180deg);
147+
}
148+
149+
.section-content {
150+
@apply overflow-hidden;
151+
transition: height 0.3s ease;
152+
}
153+
154+
.section-links {
155+
@apply space-y-2 ml-2 mt-2;
156+
}
157+
158+
.menu-link-item {
159+
@apply flex justify-between items-center;
160+
transition: all 0.2s ease;
161+
}
162+
163+
.menu-link-item:hover {
164+
transform: translateX(4px);
165+
}
166+
167+
.menu-link {
168+
@apply text-sm text-primary-light-700 dark:text-primary-dark-300 transition-all duration-200;
169+
position: relative;
170+
padding: 4px 8px;
171+
border-radius: 6px;
172+
background: transparent;
173+
transition: all 0.2s ease;
174+
}
175+
176+
.menu-link:hover {
177+
@apply text-primary-light-500 dark:text-primary-dark-200;
178+
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(147, 197, 253, 0.1) 100%);
179+
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.15);
180+
transform: scale(1.02);
181+
}
182+
183+
.menu-link::before {
184+
content: '';
185+
@apply absolute left-0 top-1/2 w-0 h-0.5 bg-primary-light-500 rounded-full;
186+
transform: translateY(-50%);
187+
transition: width 0.2s ease;
188+
}
189+
190+
.menu-link:hover::before {
191+
width: 100%;
192+
}
193+
194+
.new-badge {
195+
transform: scale(0.8) rotate(5deg);
196+
transition: all 0.2s ease;
197+
}
198+
199+
.menu-link-item:hover .new-badge {
200+
transform: scale(0.9) rotate(0deg);
201+
}
202+
203+
.section-separator {
204+
@apply py-2;
205+
}
206+
207+
.separator-line {
208+
@apply h-px bg-gradient-to-r from-transparent via-primary-light-500/30 to-transparent;
209+
position: relative;
210+
}
211+
212+
.separator-line::before {
213+
content: '';
214+
@apply absolute top-0 left-1/2 w-2 h-px bg-primary-light-500;
215+
transform: translateX(-50%);
216+
}
217+
218+
.separator-line::after {
219+
content: '';
220+
@apply absolute top-0 left-1/2 w-1 h-px bg-primary-light-500;
221+
transform: translateX(-50%);
222+
}
223+
224+
.accordion-enter-active,
225+
.accordion-leave-active {
226+
transition: all 0.3s ease;
227+
}
228+
229+
.accordion-enter-from,
230+
.accordion-leave-to {
231+
opacity: 0;
232+
transform: translateY(-10px);
233+
}
234+
235+
@media (prefers-color-scheme: dark) {
236+
.section-header {
237+
background: linear-gradient(135deg, rgba(51, 65, 85, 0.5) 0%, rgba(71, 85, 105, 0.5) 100%);
238+
}
239+
240+
.section-header:hover {
241+
background: linear-gradient(135deg, rgba(51, 65, 85, 0.8) 0%, rgba(71, 85, 105, 0.8) 100%);
242+
}
243+
244+
.section-title {
245+
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
246+
}
247+
248+
.menu-link:hover {
249+
background: linear-gradient(135deg, rgba(59, 130, 246, 0.2) 0%, rgba(147, 197, 253, 0.2) 100%);
250+
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.25);
251+
}
252+
}
253+
</style>

0 commit comments

Comments
 (0)