109 lines
6.0 KiB
Vue
109 lines
6.0 KiB
Vue
<template>
|
|
<CollapsibleRoot class="flex flex-1 flex-col" v-model:open="open">
|
|
<div class="z-50 flex w-full items-center justify-between border-b border-light-35 dark:border-dark-35 px-2">
|
|
<div class="flex items-center px-2 gap-4">
|
|
<CollapsibleTrigger asChild>
|
|
<Button icon class="!bg-transparent group md:hidden">
|
|
<Icon class="group-data-[state=open]:hidden" icon="radix-icons:hamburger-menu" />
|
|
<Icon class="group-data-[state=closed]:hidden" icon="radix-icons:cross-1" />
|
|
</Button>
|
|
</CollapsibleTrigger>
|
|
<NuxtLink class="text-light-100 dark:text-dark-100 hover:text-opacity-70 m-2 flex items-center gap-4" aria-label="Accueil" :to="{ path: '/', force: true }">
|
|
<Avatar src="/logo.dark.svg" class="dark:block hidden" />
|
|
<Avatar src="/logo.light.svg" class="block dark:hidden" />
|
|
<span class="text-xl max-md:hidden">d[any]</span>
|
|
</NuxtLink>
|
|
</div>
|
|
<NavigationMenuRoot class="relative">
|
|
<NavigationMenuList class="flex items-center gap-8 max-md:hidden">
|
|
<NavigationMenuItem>
|
|
<NavigationMenuTrigger>
|
|
<NuxtLink :href="{ name: 'character' }" class="text-light-70 dark:text-dark-70" active-class="!text-accent-blue"><span class="pl-3 py-1 flex-1 truncate">Personnages</span></NuxtLink>
|
|
</NavigationMenuTrigger>
|
|
<NavigationMenuContent class="absolute top-0 left-0 w-full sm:w-auto bg-light-0 dark:bg-dark-0 border border-light-30 dark:border-dark-30">
|
|
<NuxtLink :href="{ name: 'character-list' }" class="text-light-70 dark:text-dark-70" active-class="!text-accent-blue"><span class="py-2 px-3 flex-1 truncate">Tous les personnages</span></NuxtLink>
|
|
</NavigationMenuContent>
|
|
</NavigationMenuItem>
|
|
</NavigationMenuList>
|
|
<div class="absolute top-full left-0 flex w-full justify-center my-4">
|
|
<NavigationMenuViewport class="h-[var(--radix-navigation-menu-viewport-height)] w-full origin-[top_center] overflow-hidden rounded-[10px] bg-white transition-[width,_height] duration-300 sm:w-[var(--radix-navigation-menu-viewport-width)]" />
|
|
</div>
|
|
</NavigationMenuRoot>
|
|
<div class="flex items-center px-2 gap-4">
|
|
<template v-if="!loggedIn">
|
|
<NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 dark:hover:text-dark-70" :to="{ name: 'user-login' }">Se connecter</NuxtLink>
|
|
<NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 dark:hover:text-dark-70 max-md:hidden" :to="{ name: 'user-register' }">Créer un compte</NuxtLink>
|
|
</template>
|
|
<template v-else>
|
|
<NuxtLink class="text-light-100 dark:text-dark-100 hover:text-light-70 dark:hover:text-dark-70" :to="{ name: 'user-login' }">{{ user!.username }}</NuxtLink>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<div class="flex flex-1 flex-row relative h-screen w-screen overflow-hidden">
|
|
<CollapsibleContent asChild forceMount>
|
|
<div class="bg-light-0 dark:bg-dark-0 z-40 w-screen md:w-[18rem] border-r border-light-30 dark:border-dark-30 flex flex-col justify-between my-2 max-md:data-[state=closed]:hidden">
|
|
<div class="flex-1 px-2 max-w-full max-h-full overflow-y-auto overflow-x-hidden">
|
|
<div v-if="user" class="flex flex-1 py-4 px-2 flex-row flex-1 justify-between items-center">
|
|
<NuxtLink v-if="hasPermissions(user.permissions, ['admin', 'editor'])" :to="{ name: 'explore-edit' }"><Button icon><Icon icon="radix-icons:pencil-2" /></Button></NuxtLink>
|
|
</div>
|
|
<Tree v-if="pages" v-model="pages" :getKey="(item) => item.path" class="ps-4">
|
|
<template #default="{ item, isExpanded }">
|
|
<NuxtLink :href="item.value.path && !item.hasChildren ? { name: 'explore-path', params: { path: item.value.path } } : undefined" class="flex flex-1 items-center hover:border-accent-blue hover:text-accent-purple max-w-full" :class="{ 'font-medium': item.hasChildren }" active-class="text-accent-blue" :data-private="item.value.private">
|
|
<Icon v-if="item.hasChildren" icon="radix-icons:chevron-right" :class="{ 'rotate-90': isExpanded }" class="h-4 w-4 transition-transform absolute" :style="{ 'left': `${item.level / 2 - 1.5}em` }" />
|
|
<Icon v-else-if="iconByType[item.value.type]" :icon="iconByType[item.value.type]" class="w-5 h-5" />
|
|
<div class="pl-1.5 py-1.5 flex-1 truncate">
|
|
{{ item.value.title }}
|
|
</div>
|
|
<Tooltip message="Privé" side="right"><Icon v-show="item.value.private" class="mx-1" icon="radix-icons:lock-closed" /></Tooltip>
|
|
</NuxtLink>
|
|
</template>
|
|
</Tree>
|
|
</div>
|
|
<div class="xl:px-12 px-6 pt-4 pb-2 text-center text-xs text-light-60 dark:text-dark-60">
|
|
<NuxtLink class="hover:underline italic" :to="{ name: 'roadmap' }">Roadmap</NuxtLink> - <NuxtLink class="hover:underline italic" :to="{ name: 'legal' }">Mentions légales</NuxtLink>
|
|
<p>Copyright Peaceultime - 2025</p>
|
|
</div>
|
|
</div>
|
|
</CollapsibleContent>
|
|
<slot></slot>
|
|
</div>
|
|
</CollapsibleRoot>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { Icon } from '@iconify/vue/dist/iconify.js';
|
|
import { iconByType } from '#shared/general.util';
|
|
import type { DropdownOption } from '~/components/base/DropdownMenu.vue';
|
|
import { hasPermissions } from '~/shared/auth.util';
|
|
import type { TreeItem } from '~/types/content';
|
|
|
|
const options = ref<DropdownOption[]>([{
|
|
type: 'item',
|
|
label: 'Mon profil',
|
|
select: () => useRouter().push({ name: 'user-profile' }),
|
|
}, {
|
|
type: 'item',
|
|
label: 'Deconnexion',
|
|
select: () => clear(),
|
|
}]);
|
|
|
|
const open = ref(false);
|
|
const { loggedIn, user, clear } = useUserSession();
|
|
const { fetch } = useContent();
|
|
|
|
await fetch(false);
|
|
|
|
const route = useRouter().currentRoute;
|
|
const path = computed(() => route.value.params.path ? Array.isArray(route.value.params.path) ? route.value.params.path[0] : route.value.params.path : undefined);
|
|
|
|
watch(route, () => {
|
|
open.value = false;
|
|
});
|
|
|
|
const { tree } = useContent();
|
|
const pages = computed(() => transform(tree.value));
|
|
function transform(list: TreeItem[] | undefined): TreeItem[] | undefined
|
|
{
|
|
return list?.filter(e => e.navigable)?.map(e => ({ ...e, open: path.value?.startsWith(e.path), children: transform(e.children) }));
|
|
}
|
|
</script> |