1+ <template >
2+ <div class =" mobile-navbar" >
3+ <div class =" mobile-navbar__items" >
4+ <router-link
5+ v-for =" (item, index) in items"
6+ :key =" item"
7+ class =" mobile-navbar__item-link"
8+ :to =" routerPushTo(item)"
9+ @click =" onItemClick(item, index)"
10+ >
11+ <div
12+ class =" mobile-navbar__item"
13+ :class =" {
14+ 'mobile-navbar__item--active': activeItem?.label === item.label,
15+ [`mobile-navbar__item--active--${variant}`]: activeItem?.label === item.label,
16+ }"
17+ >
18+ <icon
19+ height =" 20"
20+ width =" 20"
21+ :name =" item.icon"
22+ />
23+
24+ <div
25+ v-if =" showLabel"
26+ class =" mobile-navbar__item-text"
27+ >
28+ {{ item.label }}
29+ </div >
30+ </div >
31+ </router-link >
32+
33+ <div
34+ :class =" computedClass"
35+ :style =" indicatorStyle"
36+ />
37+ </div >
38+ </div >
39+ </template >
40+
41+ <script setup>
42+ import { ref , computed } from ' vue' ;
43+ import Icon from ' ../components/Icon.vue' ;
44+
45+ const props = defineProps ({
46+ /**
47+ * Define os itens de menu da navbar.
48+ */
49+ items: {
50+ type: Array ,
51+ required: true ,
52+ validator : (value ) => {
53+ return value .length > 0 && value .lenght <= 5 && value .every (item => item .route .path && item .route .name );
54+ },
55+ },
56+ /**
57+ * Define a variante de cor dos detalhes do componente. São 9 variantes implementadas: 'green', 'teal',
58+ * 'blue', 'indigo', 'violet', 'pink', 'red', 'orange', 'amber', 'gray' e 'dark'.
59+ */
60+ variant: {
61+ type: String ,
62+ default: ' green' ,
63+ },
64+ /**
65+ * Remove as labels dos itens da navbar.
66+ */
67+ showLabel: {
68+ type: Boolean ,
69+ default: false ,
70+ },
71+ });
72+
73+ const emits = defineEmits ([' item-click' ]);
74+
75+ const activeItem = ref (props .items [0 ]);
76+ const activeIndex = ref (0 );
77+
78+ const indicatorStyle = computed (() => {
79+ const itemWidth = 100 / props .items .length ;
80+ return {
81+ width: ` ${ itemWidth} %` ,
82+ transform: ` translateX(${ activeIndex .value * 100 } %)` ,
83+ };
84+ });
85+
86+ const computedClass = computed (() => {
87+ let stringona = ' ' ;
88+
89+ switch (activeIndex .value ) {
90+ case 0 :
91+ stringona += ` mobile-navbar__indicator--first ` ;
92+ break ;
93+ case props .items .length - 1 :
94+ stringona += ` mobile-navbar__indicator--last ` ;
95+ break ;
96+ default :
97+ stringona += ` mobile-navbar__indicator ` ;
98+ break ;
99+ }
100+
101+ return stringona .concat (` mobile-navbar__indicator--${ props .variant } ` );
102+ });
103+
104+ function routerPushTo (item ) {
105+ if (item .route .name ) {
106+ return { name: item .route .name };
107+ }
108+
109+ return { path: item .route .path };
110+ }
111+
112+ function onItemClick (item , index ) {
113+ activeItem .value = item;
114+ activeIndex .value = index;
115+ emits (' item-click' , item);
116+ }
117+
118+ </script >
119+
120+ <style lang="scss" scoped>
121+ @import ' ../assets/sass/tokens.scss' ;
122+
123+ .mobile-navbar {
124+ position : absolute ;
125+ bottom : 0 ;
126+ left : 0 ;
127+ width : 100% ;
128+ display : flex ;
129+ align-items : center ;
130+ justify-content : space-between ;
131+ background-color : rgba (#fff , .85 );
132+ backdrop-filter : blur (10px );
133+ box-shadow : 0px -1px 4px rgba (0 , 0 , 0 , 0.05 );
134+ position : relative ;
135+
136+ & __items {
137+ display : flex ;
138+ width : 100% ;
139+ justify-content : space-between ;
140+ align-items : center ;
141+ position : relative ;
142+ }
143+
144+ & __item {
145+ display : flex ;
146+ flex-direction : column ;
147+ justify-content : center ;
148+ align-items : center ;
149+ gap : spacer (1 );
150+ flex : 1 ;
151+ min-width : 0 ;
152+ height : 100% ;
153+ min-height : 50px ;
154+ padding : pYX (2 , 1 );
155+ position : relative ;
156+ color : $n-600 ;
157+ z-index : 1 ;
158+
159+ & --active {
160+ @include variantResolver using ($color-name , $shade-50 , $shade-100 , $shade-200 , $shade-300 , $base-color , $shade-500 , $shade-600 ) {
161+ color : $shade-500 ;
162+ }
163+ }
164+ }
165+
166+ & __item-link {
167+ width : 100% ;
168+ display : flex ;
169+ flex-direction : column ;
170+ gap : spacer (1 );
171+ align-items : center ;
172+ justify-content : center ;
173+ cursor : pointer ;
174+ }
175+
176+ & __item-text {
177+ font-size : 9.5px ;
178+ font-weight : $font-weight-semibold ;
179+ overflow : hidden ;
180+ text-overflow : ellipsis ;
181+ white-space : nowrap ;
182+ text-align : center ;
183+ width : 100% ;
184+ }
185+
186+ & __indicator {
187+ position : absolute ;
188+ top : 0 ;
189+ left : 0 ;
190+ height : 100% ;
191+ transition : transform 0.3s ease ;
192+ border-radius : 8px 8px 0 0 ;
193+
194+ & --first {
195+ @extend .mobile-navbar__indicator ;
196+ border-radius : 0 8px 0 0 ;
197+ }
198+
199+ & --last {
200+ @extend .mobile-navbar__indicator ;
201+ border-radius : 8px 0 0 0 ;
202+ }
203+
204+ @include variantResolver using ($color-name , $shade-50 , $shade-100 , $shade-200 , $shade-300 , $base-color , $shade-500 , $shade-600 ) {
205+ background-color : $shade-50 ;
206+ color : $shade-500 ;
207+ }
208+
209+ & ::after {
210+ content : ' ' ;
211+ position : absolute ;
212+ bottom : 0 ;
213+ left : 0 ;
214+ width : 100% ;
215+ height : 2px ;
216+ background-color : currentColor ;
217+ }
218+ }
219+ }
220+ </style >
0 commit comments