Initial commit: SIBU 2.0 MISSION
This commit is contained in:
148
frontend/src/components/BottomNav.vue
Normal file
148
frontend/src/components/BottomNav.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const { t } = useI18n()
|
||||
|
||||
const navItems = [
|
||||
{ name: 'map', path: '/map', icon: 'map' },
|
||||
{ name: 'schedules', path: '/schedules', icon: 'schedule' },
|
||||
{ name: 'discover', path: '/discover', icon: 'explore' },
|
||||
{ name: 'taxi', path: '/taxi', icon: 'directions_bus' } // Cambiado a ícono de transporte más general
|
||||
]
|
||||
|
||||
const navigateTo = (path: string) => {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
const isActive = (path: string) => {
|
||||
return route.path === path
|
||||
}
|
||||
|
||||
// Scroll detection logic
|
||||
const isVisible = ref(true)
|
||||
let lastScrollPosition = 0
|
||||
|
||||
const handleScroll = () => {
|
||||
const currentScrollPosition = window.pageYOffset || document.documentElement.scrollTop
|
||||
if (currentScrollPosition < 0) return // For iOS elastic scroll
|
||||
|
||||
if (Math.abs(currentScrollPosition - lastScrollPosition) < 10) return
|
||||
|
||||
isVisible.value = currentScrollPosition < lastScrollPosition || currentScrollPosition < 50
|
||||
lastScrollPosition = currentScrollPosition
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="bottom-nav" :class="{ 'nav-hidden': !isVisible }">
|
||||
<div
|
||||
v-for="item in navItems"
|
||||
:key="item.name"
|
||||
class="nav-item"
|
||||
:class="{ active: isActive(item.path) }"
|
||||
@click="navigateTo(item.path)"
|
||||
>
|
||||
<span class="material-icons">{{ item.icon }}</span>
|
||||
<span class="nav-label">{{ t('navigation.' + item.name) }}</span>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: calc(70px + var(--safe-area-bottom));
|
||||
background: var(--header-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-top: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
padding-bottom: var(--safe-area-bottom);
|
||||
z-index: 1000;
|
||||
box-shadow: 0 -10px 30px rgba(0,0,0,0.3);
|
||||
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.nav-hidden {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
padding: 8px 12px;
|
||||
border-radius: 16px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: var(--hover-bg);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
color: var(--active-color);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-size: 26px;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.nav-item.active .material-icons {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.bottom-nav {
|
||||
left: 50%;
|
||||
right: auto;
|
||||
width: 600px;
|
||||
bottom: 24px;
|
||||
border-radius: 24px;
|
||||
border: 1px solid var(--border-color);
|
||||
box-shadow: 0 20px 50px rgba(0,0,0,0.4);
|
||||
height: 80px;
|
||||
padding: 0 20px;
|
||||
/* En desktop no la ocultamos para mantener la UX de cursor */
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.nav-hidden {
|
||||
transform: translate(-50%, 150%);
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user