trigo / trigo-web /app /src /components /SidebarMenu.vue
k-l-lambda's picture
updated
502af73
<template>
<nav
class="sidebar-menu"
:class="{ expanded: isExpanded }"
@mouseenter="isExpanded = true"
@mouseleave="isExpanded = false"
>
<div
class="logo-section"
@click="handleLogoClick"
title="Click to test WebSocket connection"
>
<img src="@/assets/logo.png" alt="Trigo" class="logo clickable" />
<h1 class="app-title">Trigo</h1>
</div>
<div class="menu-tabs">
<router-link
v-for="tab in tabs"
:key="tab.path"
:to="tab.path"
class="tab-button"
:class="{ active: isActiveTab(tab.path) }"
>
<span class="tab-icon">{{ tab.icon }}</span>
<span class="tab-label">{{ tab.label }}</span>
</router-link>
</div>
<div class="sidebar-footer">
<p class="version">v1.0.0</p>
</div>
</nav>
</template>
<script setup lang="ts">
import { ref, computed } from "vue";
import { useRoute } from "vue-router";
import { useSocket } from "@/composables/useSocket";
interface Tab {
path: string;
label: string;
icon: string;
}
const tabs: Tab[] = [
{ path: "/", label: "Single Game", icon: "🎮" },
{ path: "/vs-ai", label: "VS AI", icon: "🤖" },
{ path: "/vs-people", label: "VS People", icon: "👥" },
{ path: "/library", label: "Game Library", icon: "📚" }
];
const route = useRoute();
const isExpanded = ref(false);
const isActiveTab = (path: string): boolean => {
return route.path === path;
};
// WebSocket echo test
const { sendEcho, connected: socketConnected } = useSocket();
const handleLogoClick = async () => {
try {
console.log("[Echo Test] Sending echo request...");
console.log("[Echo Test] Socket connected:", socketConnected.value);
const response = await sendEcho("Echo test from Trigo sidebar!");
console.log("[Echo Test] Response:", response);
// Show alert with the response
alert(`WebSocket Echo Test\n\n${response}`);
} catch (error) {
console.error("[Echo Test] Error:", error);
alert(
`WebSocket Echo Test Failed\n\n${error instanceof Error ? error.message : String(error)}`
);
}
};
</script>
<style lang="scss" scoped>
.sidebar-menu {
width: 70px; // Collapsed width (icon only)
background: #2a2a2a;
display: flex;
flex-direction: column;
border-right: 1px solid #404040;
padding: 20px 0;
flex-shrink: 0;
transition: width 0.3s ease;
overflow: hidden;
position: relative;
&.expanded {
width: 220px; // Expanded width (icon + text)
}
}
.logo-section {
display: flex;
flex-direction: column;
align-items: center;
padding: 0 10px 30px;
border-bottom: 1px solid #404040;
margin-bottom: 20px;
min-height: 110px; // Prevent layout shift
cursor: pointer;
transition: background-color 0.2s ease;
&:hover {
background-color: rgba(255, 255, 255, 0.05);
}
&:active {
background-color: rgba(255, 255, 255, 0.1);
}
}
.logo {
width: 50px;
height: 50px;
margin-bottom: 12px;
border-radius: 8px;
flex-shrink: 0;
transition:
transform 0.2s ease,
opacity 0.2s ease;
&.clickable {
.logo-section:hover & {
transform: scale(1.1);
opacity: 0.9;
}
.logo-section:active & {
transform: scale(0.95);
}
}
}
.app-title {
font-size: 20px;
font-weight: 600;
color: #e0e0e0;
margin: 0;
white-space: nowrap;
opacity: 0;
transition: opacity 0.2s ease 0.1s;
.expanded & {
opacity: 1;
}
}
.menu-tabs {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
padding: 0 10px;
}
.tab-button {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 12px;
border-radius: 8px;
background: transparent;
color: #a0a0a0;
text-decoration: none;
font-size: 15px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
border-left: 3px solid transparent;
position: relative;
white-space: nowrap;
&:hover {
background: #353535;
color: #e0e0e0;
}
&.active {
background: #3a3a3a;
color: #e94560;
border-left-color: #e94560;
.tab-icon {
transform: scale(1.1);
}
}
}
.tab-icon {
font-size: 22px;
line-height: 1;
transition: transform 0.2s ease;
min-width: 28px;
text-align: center;
flex-shrink: 0;
}
.tab-label {
flex: 1;
opacity: 0;
transition: opacity 0.2s ease 0.1s;
.expanded & {
opacity: 1;
}
}
.sidebar-footer {
padding: 20px 10px;
border-top: 1px solid #404040;
text-align: center;
}
.version {
font-size: 11px;
color: #606060;
margin: 0;
white-space: nowrap;
opacity: 0;
transition: opacity 0.2s ease 0.1s;
.expanded & {
opacity: 1;
}
}
</style>