monacousa-portal/components/SidebarLink.vue

111 lines
2.6 KiB
Vue
Raw Normal View History

<template>
<NuxtLink
:to="to"
:class="[
'flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all duration-200 group relative',
isActive
? 'bg-glass-monaco text-monaco-600 font-medium shadow-soft'
: 'text-gray-700 hover:bg-glass-monaco-soft hover:text-monaco-600 hover:translate-x-0.5'
]"
>
<!-- Active Indicator -->
<div
v-if="isActive"
class="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-8 bg-gradient-monaco rounded-r-full"
></div>
<!-- Icon -->
<component
:is="icon"
:class="[
'flex-shrink-0 transition-colors',
collapsed ? 'w-6 h-6' : 'w-5 h-5',
isActive ? 'text-monaco-600' : 'text-gray-500 group-hover:text-monaco-600'
]"
/>
<!-- Label and Badge -->
<transition name="slide">
<div v-if="!collapsed" class="flex-1 flex items-center justify-between">
<span class="text-sm font-medium">{{ label }}</span>
<!-- Badge -->
<span
v-if="badge"
:class="[
'px-2 py-0.5 text-xs rounded-full font-medium transition-colors',
isActive
? 'bg-monaco-600 text-white'
: 'bg-glass-monaco-soft text-monaco-700 group-hover:bg-monaco-100'
]"
>
{{ badge }}
</span>
</div>
</transition>
<!-- Tooltip for collapsed state -->
<div
v-if="collapsed"
class="absolute left-full ml-2 px-3 py-2 bg-gray-900 text-white text-sm rounded-lg
opacity-0 invisible group-hover:opacity-100 group-hover:visible
transition-all duration-200 whitespace-nowrap z-50"
>
{{ label }}
<div class="absolute left-0 top-1/2 -translate-x-1 -translate-y-1/2
w-0 h-0 border-4 border-transparent border-r-gray-900"></div>
</div>
</NuxtLink>
</template>
<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
const props = defineProps({
to: {
type: String,
required: true
},
icon: {
type: Object,
required: true
},
label: {
type: String,
required: true
},
badge: {
type: [String, Number],
default: null
},
collapsed: {
type: Boolean,
default: false
}
})
const route = useRoute()
const isActive = computed(() => {
return route.path === props.to || route.path.startsWith(props.to + '/')
})
</script>
<style scoped>
/* Slide transition for label */
.slide-enter-active,
.slide-leave-active {
transition: all 0.2s ease;
}
.slide-enter-from {
opacity: 0;
transform: translateX(-10px);
}
.slide-leave-to {
opacity: 0;
transform: translateX(-10px);
}
</style>