Combines Header and SideBar into the SideBar

This commit is contained in:
2024-10-06 02:57:38 -04:00
parent 74fe943410
commit 6e0b3449b7
3 changed files with 97 additions and 369 deletions

View File

@@ -1,23 +1,16 @@
<template>
<v-app>
<div class="flex flex-row">
<Header class=" w-full z-50 p-2 h-16"></Header>
<div class="fixed h-full w-60 border-r-2 z-30 ">
<side-bar></side-bar>
</div>
<div class="flex flex-row ">
<transition name="slide-fade">
<div v-show="sideBarStore.visible"
class="fixed h-full min-w-60 border-r-2 z-30 ">
<side-bar></side-bar>
</div>
</transition>
<div class="flex flex-col w-full min-h-screen ">
<div class="flex flex-col pl-60 w-full min-h-screen ">
<RouterView></RouterView>
</div>
</div>
</v-app>
<size-indicator></size-indicator>
@@ -25,69 +18,9 @@
</template>
<script async setup>
import Header from "@/views/main/Header.vue";
import Footer from "@/views/main/Footer.vue";
import SideBar from "@/views/main/SideBar.vue";
import {ref, onMounted, onUnmounted} from 'vue';
import {useSideBarStore} from "@/stores/sideBarStore.js";
import SizeIndicator from "@/views/tools/SizeIndicator.vue";
const showPopup = ref(false);
const popup = ref(null);
const popupButton = ref(null);
let closeSidebarTimer = null;
const sideBarStore = useSideBarStore()
const openSidebar = () => {
clearCloseSidebarTimer();
sideBarStore.show()
};
const startCloseSidebarTimer = () => {
closeSidebarTimer = setTimeout(() => {
sideBarStore.hide()
}, 500);
};
const clearCloseSidebarTimer = () => {
clearTimeout(closeSidebarTimer);
};
const handleClickOutside = (event) => {
if (
popup.value &&
!popup.value.contains(event.target) &&
!popupButton.value.contains(event.target) &&
!event.target.closest('.bg-purple')
) {
showPopup.value = false;
}
if (
!event.target.closest('.w-48') &&
!event.target.closest('.v-app-bar-nav-icon')
) {
sideBarStore.hide()
}
};
onMounted(() => {
document.addEventListener('click', handleClickOutside);
});
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside);
});
</script>
<style scoped>
/* Do not remove. Used for animation */
.slide-fade-enter-active, .slide-fade-leave-active {
transition: transform 0.7s ease, opacity 0.7s ease;
}
.slide-fade-enter, .slide-fade-leave-to {
transform: translateX(-100%);
opacity: 0;
}
</style>

View File

@@ -1,241 +0,0 @@
<template>
<header
class="py-1 flex items-center justify-between bg-white shadow-md shadow-gray-700 z-20"
@click.stop
>
<div class="flex items-center">
<v-app-bar-nav-icon @click.stop="sideBarStore.toggle()">
</v-app-bar-nav-icon>
<RouterLink class="d-sm-block d-md-block d-lg-none" to="/">
<v-img
src="/images/usersmedia/HutopyProfile/profilepictures/profileHutopyProfile01.png"
ref="popupButtonRef"
alt="Hutopy Logo"
class="w-10"
></v-img>
</RouterLink>
<RouterLink class="d-none d-lg-flex" to="/">
<v-img
src="/medias/hutopy.png"
ref="popupButtonRef"
alt="Hutopy Logo"
class="mr-2 h-10 w-20"
></v-img>
<div class="flex align-center text-sm">ALPHA</div>
</RouterLink>
</div>
<!-- SmallScreen Searchbar -->
<div class="flex-grow flex items-center justify-center relative">
<div v-if="showSearch && isSmallScreen" class="search-bar-wrapper">
<v-text-field
density="compact"
rounded
variant="outlined"
v-model="searchQuery"
placeholder="Recherche"
hide-details
clearable
append-inner-icon="mdi-magnify"
@click.stop
@click:append-inner="onSearch"
@keyup.enter="onSearch"
>
</v-text-field>
</div>
<!-- largeScreen Searchbar -->
<div v-if="showSearch && !isSmallScreen" class="search-bar-large-screen">
<v-text-field
density="compact"
rounded
variant="outlined"
v-model="searchQuery"
placeholder="Recherche"
hide-details
clearable
append-inner-icon="mdi-magnify"
@click.stop
@click:append-inner="onSearch"
@keyup.enter="onSearch"
>
</v-text-field>
</div>
</div>
<div class="flex items-center space-x-4 search-container">
<template v-if="!showSearch">
<v-icon class="mx-2 cursor-pointer" @click.stop="toggleSearch">mdi-magnify</v-icon>
</template>
<div class="text-center">
<v-menu open-on-click>
<template v-slot:activator="{ props }">
<div v-bind="props" class="flex align-center font-sans py-1 px-2 rounded-lg hover:bg-gray-100">
<span class="max-w-xs hidden md:block capitalize">
{{ userProfileStore.alias }}
</span>
<img
:src="userProfileStore.portraitUrl"
alt="Profile Image"
class="ml-2 rounded-full"
width="32"
height="32"
/>
</div>
</template>
<v-list min-width="200px" class="align-center mt-3 left-3">
<template v-if="!authStore.isAuthenticated">
<v-list-item class="nav-button">
<v-list-item-title>
<v-btn to="/login" class="w-100" variant="plain">Connexion</v-btn>
</v-list-item-title>
</v-list-item>
</template>
<template v-else>
<v-list-item v-if="creatorProfileStore.creator.name && Object.keys(creatorProfileStore.creator).length > 0" class="nav-button">
<router-link :to="`/@${creatorProfileStore.creator.name}`">
<v-btn class="w-100" variant="plain">{{ creatorProfileStore.creator.name }}</v-btn>
</router-link>
</v-list-item>
<v-list-item v-if="!creatorProfileStore.hasCreator" class="nav-button">
<router-link to="/profile?target=CreatorPage">
<v-btn class="w-100" variant="plain">Activer votre page</v-btn>
</router-link>
</v-list-item>
<v-list-item class="nav-button">
<v-list-item-title>
<v-btn to="/profile" class="w-100" variant="plain">{{ $t('header.myprofile') }}</v-btn>
</v-list-item-title>
</v-list-item>
<v-list-item class="nav-button">
<v-list-item-title>
<v-btn to="/wallet" class="w-100" variant="plain">{{ $t('header.wallet') }}</v-btn>
</v-list-item-title>
</v-list-item>
<v-list-item class="nav-button">
<v-list-item-title>
<v-btn @click="authStore.logout" class="w-100" variant="plain">{{ $t('header.Signout') }}</v-btn>
</v-list-item-title>
</v-list-item>
</template>
</v-list>
</v-menu>
</div>
</div>
</header>
</template>
<script setup>
import { ref, onBeforeUnmount, onBeforeMount, watch } from "vue";
import { useRouter } from 'vue-router';
import { useSideBarStore } from '@/stores/sideBarStore.js';
import { useAuthStore } from "@/stores/authStore.js";
import { useDisplay } from 'vuetify';
import {useUserProfileStore} from "@/stores/userProfileStore.js";
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
const authStore = useAuthStore();
const userProfileStore = useUserProfileStore();
const creatorProfileStore = useCreatorProfileStore();
const sideBarStore = useSideBarStore();
const { smAndDown } = useDisplay();
const router = useRouter();
const searchQuery = ref("");
const showSearch = ref(false);
const isSmallScreen = ref(smAndDown.value);
watch(smAndDown, (val) => {
isSmallScreen.value = val;
});
const onSearch = () => {
const query = searchQuery.value.trim();
if (!query) {
router.push("/browse");
} else {
const words = query.split(" ");
if (words.length === 1) {
router.push(`/@${words[0]}`);
} else {
router.push({ name: "browse", query: { q: query } });
}
}
};
const toggleSearch = () => {
showSearch.value = !showSearch.value;
};
const handleClickOutside = (event) => {
if (!event.target.closest('.search-container')) {
showSearch.value = false;
}
};
onBeforeMount(() => {
document.addEventListener('click', handleClickOutside)
});
onBeforeUnmount(() => {
document.removeEventListener('click', handleClickOutside);
});
</script>
<style scoped>
.search-container {
position: relative;
}
.nav-button:hover {
@apply bg-[#903175] text-gray-200;
}
.absolute-center {
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 6px;
align-items: center;
}
.search-bar-wrapper {
position: absolute;
width: 100%;
max-width: 700px;
z-index: 50;
margin-left: 20px;
}
.search-bar-large-screen {
display: flex;
width: 600px;
z-index: 50;
}
@media (min-width: 1024px) {
.search-bar-large-screen {
display: flex;
margin-left: auto;
width: 600px;
}
}
@media (max-width: 640px) {
.absolute-center {
left: 65%;
transform: translateX(-55%);
}
}
</style>

View File

@@ -4,11 +4,16 @@ import {useAuthStore} from "@/stores/authStore.js";
import {useRouter} from 'vue-router';
import {ref} from "vue";
import {useI18n} from 'vue-i18n';
import {useCreatorProfileStore} from "@/stores/creatorProfileStore.js";
import {useUserProfileStore} from "@/stores/userProfileStore.js";
const {locale} = useI18n();
const router = useRouter();
const selectedLanguage = ref(locale.value);
const userProfileStore = useUserProfileStore()
const creatorProfileStore = useCreatorProfileStore()
function initializeLocale() {
const preferredLocale = localStorage.getItem('preferredLocale');
selectedLanguage.value = preferredLocale === null ? locale.value : preferredLocale;
@@ -28,72 +33,103 @@ initializeLocale();
</script>
<template>
<nav class="flex flex-col h-full overflow-y-auto custom-scrollbar bg-white">
<div class="flex justify-center py-3">
<v-btn
variant="text"
class="p-0 mx-0 min-w-0"
@click="toggleLanguage"
>
{{ selectedLanguage === 'fr' ? 'Fr' : 'En' }}
</v-btn>
<nav class="flex flex-col h-full bg-white">
<!-- APP SECTION -->
<div class="px-4 mt-4">
<!-- LOGO HUTOPY -->
<RouterLink class="d-none d-lg-flex" to="/">
<v-img
src="/images/usersmedia/HutopyProfile/profilepictures/profileHutopyProfile01.png"
ref="popupButtonRef"
alt="Hutopy Logo"
class="mr-2 h-10 w-20"
></v-img>
</RouterLink>
<!-- YOUR PAGE -->
<router-link v-if="creatorProfileStore.hasCreator"
:to="`/@${creatorProfileStore.creator.name}`">
<v-btn class="w-full"
prepend-icon="mdi-home-account"
variant="tonal">
{{ creatorProfileStore.creator.name }}
</v-btn>
</router-link>
<router-link v-else-if="authStore.isAuthenticated"
class="nav-button"
to="/profile?target=CreatorPage">
<v-btn class="w-100" variant="plain">Activer votre page</v-btn>
</router-link>
<!-- CREATE CONTENT -->
</div>
<!-- SUBSCRIPTION SECTION -->
<div class="flex-grow px-4">
<template v-if="authStore.isAuthenticated">
<div class="font-bold"> {{ $t('sidebar.subscriptionTitle') }}</div>
<!-- SUBSCRIPTION LIST -->
<subscription-list>
</subscription-list>
</template>
</div>
<div class="border border-solid border-1"></div>
<!-- USER SECTION -->
<div class="px-4 mb-4">
<div class="flex justify-center p-2">
<img
:src="userProfileStore.portraitUrl"
alt="Profile Image"
class="rounded-full"
width="32"
height="32"
>
<span class="ml-2 text-lg font-sans capitalize">
{{ userProfileStore.alias }}
</span>
</div>
<div class="flex flex-column gap-4">
<!-- PROFILE -->
<v-btn to="/profile"
class="w-full"
prepend-icon="mdi-account"
variant="tonal">
{{ $t('header.myprofile') }}
</v-btn>
<div v-if="authStore.isAuthenticated" class="flex-grow px-5 py-4">
<div class="font-bold"> {{ $t('sidebar.subscriptionTitle') }}</div>
<div v-if="authStore.isAuthenticated" class="flex-grow px-5">
<!-- LANGUAGE -->
<v-btn
variant="tonal"
class="w-full"
prepend-icon="mdi-translate-variant"
@click="toggleLanguage"
>
{{ selectedLanguage === 'fr' ? 'Fr' : 'En' }}
</v-btn>
<!-- LOGIN / LOGOUT -->
<template v-if="!authStore.isAuthenticated">
<v-btn to="/login"
variant="tonal"
class="w-full"
prepend-icon="mdi-login">
Connexion
</v-btn>
</template>
<template v-else>
<v-btn @click="authStore.logout"
variant="tonal"
class="w-full"
prepend-icon="mdi-logout">
{{ $t('header.Signout') }}
</v-btn>
</template>
</div>
<subscription-list>
</subscription-list>
</div>
<div v-else class="flex-grow px-5 py-4 flex justify-center font-bold ">
<span>Connectez-vous</span>
</div>
</nav>
</template>
<style scoped>
nav {
overflow-y: auto;
min-height: 0;
}
/* Firefox */
.custom-scrollbar {
scrollbar-width: thin;
scrollbar-color: #903175 #f1f1f1;
}
/* Chrome, Safari) */
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #f1f1f1;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: #903175;
border-radius: 10px;
border: 2px solid #f1f1f1;
}
h2 {
@apply font-sans font-bold ml-2;
}
aside {
@apply relative;
}
</style>