mirror of
https://github.com/versia-pub/frontend.git
synced 2026-03-13 11:39:16 +01:00
chore: ⬆️ Upgrade to the latest Shadcn-Vue version
Some checks failed
Some checks failed
This commit is contained in:
parent
7649ecfb80
commit
092bce0f24
169 changed files with 1860 additions and 1088 deletions
|
|
@ -1,128 +0,0 @@
|
|||
<template>
|
||||
<Drawer v-if="isMobile">
|
||||
<DrawerTrigger :as-child="true">
|
||||
<slot />
|
||||
</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<Button @click="switchAccount(identity.account.id)" variant="outline" size="lg"
|
||||
:href="`/@${identity.account.username}`" v-for="identity of identities"
|
||||
class="flex w-full items-center gap-2 px-4 text-left h-20">
|
||||
<Avatar class="size-12" :src="identity.account.avatar" :name="identity.account.display_name" />
|
||||
<div class="grid flex-1 text-left leading-tight">
|
||||
<span class="truncate font-semibold" v-render-emojis="identity.account.emojis">{{
|
||||
identity.account.display_name
|
||||
}}</span>
|
||||
<span class="truncate text-sm">@{{
|
||||
identity.account.acct
|
||||
}}</span>
|
||||
</div>
|
||||
</Button>
|
||||
<Button variant="secondary" size="lg" class="w-full" @click="signInAction">
|
||||
<UserPlus />
|
||||
{{ m.sunny_pink_hyena_walk() }}
|
||||
</Button>
|
||||
<Button variant="secondary" size="lg" @click="signOut(appData, identity)" v-if="identity">
|
||||
<LogOut />
|
||||
{{ m.sharp_big_mallard_reap() }}
|
||||
</Button>
|
||||
<Button variant="secondary" size="lg" :as="NuxtLink" href="/register" v-else>
|
||||
<LogIn />
|
||||
{{ m.honest_few_baboon_pop() }}
|
||||
</Button>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
<DropdownMenu v-else>
|
||||
<DropdownMenuTrigger :as-child="true">
|
||||
<slot />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent class="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded" side="bottom"
|
||||
align="end" :side-offset="4">
|
||||
<DropdownMenuLabel class="p-0 font-normal">
|
||||
<Button @click="switchAccount(identity.account.id)" variant="ghost" size="lg"
|
||||
:href="`/@${identity.account.username}`" v-for="identity of identities"
|
||||
class="flex w-full items-center gap-2 px-1 text-left text-sm">
|
||||
<Avatar class="size-8" :src="identity.account.avatar" :name="identity.account.display_name" />
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-semibold" v-render-emojis="identity.account.emojis">{{
|
||||
identity.account.display_name
|
||||
}}</span>
|
||||
<span class="truncate text-xs">@{{
|
||||
identity.account.username
|
||||
}}@{{
|
||||
identity.instance.domain
|
||||
}}</span>
|
||||
</div>
|
||||
</Button>
|
||||
<DropdownMenuItem @click="signInAction">
|
||||
<UserPlus />
|
||||
{{ m.sunny_pink_hyena_walk() }}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator v-if="identity" />
|
||||
<DropdownMenuGroup v-if="identity">
|
||||
<DropdownMenuItem :as="NuxtLink" :href="`/@${identity.account.username}`">
|
||||
<BadgeCheck />
|
||||
{{ m.factual_arable_jurgen_endure() }}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem @click="signOut(appData, identity)" v-if="identity">
|
||||
<LogOut />
|
||||
{{ m.sharp_big_mallard_reap() }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem :as="NuxtLink" href="/register" v-else>
|
||||
<LogIn />
|
||||
{{ m.honest_few_baboon_pop() }}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BadgeCheck, LogIn, LogOut, UserPlus } from "lucide-vue-next";
|
||||
import { toast } from "vue-sonner";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import { NuxtLink } from "#components";
|
||||
import DrawerContent from "../modals/drawer-content.vue";
|
||||
import Avatar from "../profiles/avatar.vue";
|
||||
import { Button } from "../ui/button";
|
||||
import { Drawer, DrawerTrigger } from "../ui/drawer";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu";
|
||||
|
||||
const appData = useAppData();
|
||||
const isMobile = useMediaQuery("(max-width: 768px)");
|
||||
|
||||
const signInAction = async () => signIn(appData, await askForInstance());
|
||||
|
||||
const switchAccount = async (userId: string) => {
|
||||
if (userId === identity.value?.account.id) {
|
||||
return await navigateTo(`/@${identity.value.account.username}`);
|
||||
}
|
||||
|
||||
const id = toast.loading("Switching account...");
|
||||
|
||||
const identityToSwitch = identities.value.find(
|
||||
(i) => i.account.id === userId,
|
||||
);
|
||||
|
||||
if (!identityToSwitch) {
|
||||
toast.dismiss(id);
|
||||
toast.error("No identity to switch to");
|
||||
return;
|
||||
}
|
||||
|
||||
identity.value = identityToSwitch;
|
||||
toast.dismiss(id);
|
||||
toast.success("Switched account");
|
||||
|
||||
window.location.href = "/";
|
||||
};
|
||||
</script>
|
||||
92
components/sidebars/account/account-switcher.vue
Normal file
92
components/sidebars/account/account-switcher.vue
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<script setup lang="ts">
|
||||
import { BadgeCheck, LogIn, LogOut, UserPlus } from "lucide-vue-next";
|
||||
import { toast } from "vue-sonner";
|
||||
import TinyCard from "~/components/profiles/tiny-card.vue";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "~/components/ui/dropdown-menu";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import { NuxtLink } from "#components";
|
||||
|
||||
const appData = useAppData();
|
||||
|
||||
const signInAction = async () => signIn(appData, await askForInstance());
|
||||
|
||||
const switchAccount = async (userId: string) => {
|
||||
if (userId === identity.value?.account.id) {
|
||||
return await navigateTo(`/@${identity.value.account.username}`);
|
||||
}
|
||||
|
||||
const id = toast.loading("Switching account...");
|
||||
|
||||
const identityToSwitch = identities.value.find(
|
||||
(i) => i.account.id === userId,
|
||||
);
|
||||
|
||||
if (!identityToSwitch) {
|
||||
toast.dismiss(id);
|
||||
toast.error("No identity to switch to");
|
||||
return;
|
||||
}
|
||||
|
||||
identity.value = identityToSwitch;
|
||||
toast.dismiss(id);
|
||||
toast.success("Switched account");
|
||||
|
||||
window.location.href = "/";
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger :as-child="true">
|
||||
<slot />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuLabel> Your accounts </DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem
|
||||
v-for="identity of identities"
|
||||
:key="identity.account.id"
|
||||
@click="switchAccount(identity.account.id)"
|
||||
:href="`/@${identity.account.username}`"
|
||||
>
|
||||
<TinyCard
|
||||
:account="identity.account"
|
||||
:domain="identity.instance.domain"
|
||||
naked
|
||||
/>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="signInAction">
|
||||
<UserPlus />
|
||||
{{ m.sunny_pink_hyena_walk() }}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator v-if="identity" />
|
||||
<DropdownMenuGroup v-if="identity">
|
||||
<DropdownMenuItem
|
||||
:as="NuxtLink"
|
||||
:href="`/@${identity.account.username}`"
|
||||
>
|
||||
<BadgeCheck />
|
||||
{{ m.factual_arable_jurgen_endure() }}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="signOut(appData, identity)">
|
||||
<LogOut />
|
||||
{{ m.sharp_big_mallard_reap() }}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuItem v-else :as="NuxtLink" href="/register">
|
||||
<LogIn />
|
||||
{{ m.honest_few_baboon_pop() }}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
60
components/sidebars/footer/footer-actions.vue
Normal file
60
components/sidebars/footer/footer-actions.vue
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<script setup lang="ts">
|
||||
import { ChevronsUpDown, DownloadCloud, Pen } from "lucide-vue-next";
|
||||
import TinyCard from "~/components/profiles/tiny-card.vue";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import {
|
||||
SidebarFooter,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "~/components/ui/sidebar";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import AccountSwitcher from "../account/account-switcher.vue";
|
||||
const { $pwa } = useNuxtApp();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SidebarFooter>
|
||||
<SidebarMenu class="gap-3">
|
||||
<SidebarMenuItem>
|
||||
<AccountSwitcher>
|
||||
<SidebarMenuButton size="lg">
|
||||
<TinyCard
|
||||
v-if="identity"
|
||||
:account="identity.account"
|
||||
:domain="identity.instance.domain"
|
||||
naked
|
||||
/>
|
||||
<ChevronsUpDown class="ml-auto size-4" />
|
||||
</SidebarMenuButton>
|
||||
</AccountSwitcher>
|
||||
</SidebarMenuItem>
|
||||
<SidebarMenuItem class="flex flex-col gap-2">
|
||||
<Button
|
||||
v-if="identity"
|
||||
variant="default"
|
||||
size="lg"
|
||||
class="w-full group-data-[collapsible=icon]:px-4"
|
||||
@click="useEvent('composer:open')"
|
||||
>
|
||||
<Pen />
|
||||
<span class="group-data-[collapsible=icon]:hidden">
|
||||
{{ m.salty_aloof_turkey_nudge() }}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
v-if="$pwa?.needRefresh"
|
||||
variant="destructive"
|
||||
size="lg"
|
||||
class="w-full group-data-[collapsible=icon]:px-4"
|
||||
@click="$pwa?.updateServiceWorker(true)"
|
||||
>
|
||||
<DownloadCloud />
|
||||
<span class="group-data-[collapsible=icon]:hidden">
|
||||
{{ m.quaint_low_felix_pave() }}
|
||||
</span>
|
||||
</Button>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarFooter>
|
||||
</template>
|
||||
49
components/sidebars/instance/instance-header.vue
Normal file
49
components/sidebars/instance/instance-header.vue
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<script setup lang="ts">
|
||||
import Avatar from "~/components/profiles/avatar.vue";
|
||||
import {
|
||||
SidebarHeader,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "~/components/ui/sidebar";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
|
||||
const instance = useInstance();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SidebarHeader>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<NuxtLink href="/">
|
||||
<SidebarMenuButton size="lg">
|
||||
<Avatar
|
||||
class="size-8"
|
||||
:src="
|
||||
instance?.thumbnail?.url ??
|
||||
'https://cdn.versia.pub/branding/icon.svg'
|
||||
"
|
||||
:name="instance?.title"
|
||||
/>
|
||||
<div
|
||||
class="grid flex-1 text-left text-sm leading-tight"
|
||||
>
|
||||
<span class="truncate font-semibold">
|
||||
{{
|
||||
instance?.title ??
|
||||
m.short_zippy_felix_kick()
|
||||
}}
|
||||
</span>
|
||||
<span class="truncate text-xs">
|
||||
{{
|
||||
instance?.description ??
|
||||
m.top_active_ocelot_cure()
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</SidebarMenuButton>
|
||||
</NuxtLink>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarHeader>
|
||||
</template>
|
||||
|
|
@ -1,209 +1,43 @@
|
|||
<template>
|
||||
<Sidebar collapsible="none" class="hidden md:flex">
|
||||
<SidebarHeader>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<NuxtLink href="/">
|
||||
<SidebarMenuButton size="lg"
|
||||
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground">
|
||||
<Avatar class="size-8" :src="instance?.thumbnail.url ??
|
||||
'https://cdn.versia.pub/branding/icon.svg'
|
||||
" :name="instance?.title" />
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-semibold">{{ instance?.title ?? m.short_zippy_felix_kick()
|
||||
}}</span>
|
||||
<span class="truncate text-xs">{{ instance?.description ?? m.top_active_ocelot_cure() }}</span>
|
||||
</div>
|
||||
<!-- <ChevronsUpDown class="ml-auto" /> -->
|
||||
</SidebarMenuButton>
|
||||
</NuxtLink>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarHeader>
|
||||
<Sidebar collapsible="offcanvas">
|
||||
<InstanceHeader />
|
||||
<SidebarContent>
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>{{ m.trite_real_sawfish_drum() }}</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem v-for="item in data.other.filter(
|
||||
i => i.requiresLogin ? !!identity : true,
|
||||
)" :key="item.name">
|
||||
<SidebarMenuButton as-child>
|
||||
<NuxtLink :href="item.url">
|
||||
<component :is="item.icon" />
|
||||
<span>{{ item.name }}</span>
|
||||
</NuxtLink>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
<SidebarGroupLabel>{{
|
||||
m.trite_real_sawfish_drum()
|
||||
}}</SidebarGroupLabel>
|
||||
<NavItems
|
||||
:items="
|
||||
sidebarConfig.other.filter((i) =>
|
||||
i.requiresLogin ? !!identity : true
|
||||
)
|
||||
"
|
||||
/>
|
||||
</SidebarGroup>
|
||||
<SidebarGroup v-if="identity" class="mt-auto">
|
||||
<SidebarGroupLabel>{{ m.close_short_kitten_coax() }}</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
<Collapsible v-for="item in data.navMain" :key="item.title" as-child class="group/collapsible">
|
||||
<SidebarMenuItem>
|
||||
<CollapsibleTrigger as-child>
|
||||
<SidebarMenuButton :tooltip="item.title">
|
||||
<component :is="item.icon" />
|
||||
<span>{{ item.title }}</span>
|
||||
<ChevronRight
|
||||
class="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
|
||||
</SidebarMenuButton>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<SidebarMenuSub>
|
||||
<SidebarMenuSubItem v-for="subItem in item.items" :key="subItem.title">
|
||||
<SidebarMenuSubButton as-child>
|
||||
<NuxtLink :href="subItem.url">
|
||||
<span>{{ subItem.title }}</span>
|
||||
</NuxtLink>
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
</SidebarMenuSub>
|
||||
</CollapsibleContent>
|
||||
</SidebarMenuItem>
|
||||
</Collapsible>
|
||||
</SidebarMenu>
|
||||
<SidebarGroupLabel>{{
|
||||
m.close_short_kitten_coax()
|
||||
}}</SidebarGroupLabel>
|
||||
<NavGroup :items="sidebarConfig.navMain" />
|
||||
</SidebarGroup>
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
<SidebarMenu class="gap-3">
|
||||
<SidebarMenuItem>
|
||||
<AccountSwitcher>
|
||||
<SidebarMenuButton size="lg"
|
||||
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground">
|
||||
<Avatar v-if="identity" class="size-8" :src="identity.account.avatar"
|
||||
:name="identity.account.display_name" />
|
||||
<Avatar v-else class="size-8" name="AB" />
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-semibold" v-render-emojis="identity?.account.emojis">{{
|
||||
identity?.account.display_name ?? "Not signed in"
|
||||
}}</span>
|
||||
<span class="truncate text-xs" v-if="identity">@{{ identity?.account.acct }}</span>
|
||||
</div>
|
||||
<ChevronsUpDown class="ml-auto size-4" />
|
||||
</SidebarMenuButton>
|
||||
</AccountSwitcher>
|
||||
</SidebarMenuItem>
|
||||
<SidebarMenuItem class="flex flex-col gap-2">
|
||||
<Button variant="default" size="lg" class="w-full group-data-[collapsible=icon]:px-4"
|
||||
v-if="identity" @click="useEvent('composer:open')">
|
||||
<Pen />
|
||||
<span class="group-data-[collapsible=icon]:hidden">{{ m.salty_aloof_turkey_nudge() }}</span>
|
||||
</Button>
|
||||
<Button variant="destructive" size="lg" class="w-full group-data-[collapsible=icon]:px-4"
|
||||
v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker(true)">
|
||||
<DownloadCloud />
|
||||
<span class="group-data-[collapsible=icon]:hidden">{{ m.quaint_low_felix_pave() }}</span>
|
||||
</Button>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarFooter>
|
||||
<FooterActions />
|
||||
<SidebarRail />
|
||||
</Sidebar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
BedSingle,
|
||||
Bell,
|
||||
ChevronRight,
|
||||
ChevronsUpDown,
|
||||
DownloadCloud,
|
||||
Globe,
|
||||
House,
|
||||
MapIcon,
|
||||
Pen,
|
||||
Settings2,
|
||||
} from "lucide-vue-next";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "~/components/ui/collapsible";
|
||||
import { sidebarConfig } from "~/components/sidebars/sidebar";
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarFooter,
|
||||
SidebarGroup,
|
||||
SidebarGroupLabel,
|
||||
SidebarHeader,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuSub,
|
||||
SidebarMenuSubButton,
|
||||
SidebarMenuSubItem,
|
||||
SidebarRail,
|
||||
} from "~/components/ui/sidebar";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import Avatar from "../profiles/avatar.vue";
|
||||
import { Button } from "../ui/button";
|
||||
import AccountSwitcher from "./account-switcher.vue";
|
||||
|
||||
const data = {
|
||||
navMain: [
|
||||
{
|
||||
title: m.patchy_seemly_hound_grace(),
|
||||
url: "/preferences",
|
||||
icon: Settings2,
|
||||
items: [
|
||||
{
|
||||
title: m.factual_arable_jurgen_endure(),
|
||||
url: "/preferences/account",
|
||||
},
|
||||
{
|
||||
title: m.tough_clean_wolf_gleam(),
|
||||
url: "/preferences/appearance",
|
||||
},
|
||||
{
|
||||
title: m.legal_best_tadpole_rise(),
|
||||
url: "/preferences/behaviour",
|
||||
},
|
||||
{
|
||||
title: m.novel_trite_sloth_adapt(),
|
||||
url: "/preferences/emojis",
|
||||
},
|
||||
{
|
||||
title: m.safe_green_mink_cook(),
|
||||
url: "/preferences/roles",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
other: [
|
||||
{
|
||||
name: m.bland_chunky_sparrow_propel(),
|
||||
url: "/home",
|
||||
icon: House,
|
||||
requiresLogin: true,
|
||||
},
|
||||
{
|
||||
name: m.lost_trick_dog_grace(),
|
||||
url: "/public",
|
||||
icon: MapIcon,
|
||||
requiresLogin: false,
|
||||
},
|
||||
{
|
||||
name: m.crazy_game_parrot_pave(),
|
||||
url: "/local",
|
||||
icon: BedSingle,
|
||||
requiresLogin: false,
|
||||
},
|
||||
{
|
||||
name: m.real_tame_moose_greet(),
|
||||
url: "/global",
|
||||
icon: Globe,
|
||||
requiresLogin: false,
|
||||
},
|
||||
{
|
||||
name: m.that_patchy_mare_snip(),
|
||||
url: "/notifications",
|
||||
icon: Bell,
|
||||
requiresLogin: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const instance = useInstance();
|
||||
const { $pwa } = useNuxtApp();
|
||||
import FooterActions from "./footer/footer-actions.vue";
|
||||
import InstanceHeader from "./instance/instance-header.vue";
|
||||
import NavGroup from "./navigation/nav-group.vue";
|
||||
import NavItems from "./navigation/nav-items.vue";
|
||||
</script>
|
||||
|
|
|
|||
59
components/sidebars/navigation/nav-group.vue
Normal file
59
components/sidebars/navigation/nav-group.vue
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<script setup lang="ts">
|
||||
import { ChevronRight } from "lucide-vue-next";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "~/components/ui/collapsible";
|
||||
import {
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuSub,
|
||||
SidebarMenuSubButton,
|
||||
SidebarMenuSubItem,
|
||||
} from "~/components/ui/sidebar";
|
||||
import type { SidebarNavMainItem } from "~/types/sidebar";
|
||||
|
||||
defineProps<{
|
||||
items: SidebarNavMainItem[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SidebarMenu>
|
||||
<Collapsible
|
||||
v-for="item in items"
|
||||
:key="item.title"
|
||||
as-child
|
||||
default-open
|
||||
class="group/collapsible"
|
||||
>
|
||||
<SidebarMenuItem>
|
||||
<CollapsibleTrigger as-child>
|
||||
<SidebarMenuButton :tooltip="item.title">
|
||||
<component :is="item.icon" />
|
||||
{{ item.title }}
|
||||
<ChevronRight
|
||||
class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180"
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<SidebarMenuSub>
|
||||
<SidebarMenuSubItem
|
||||
v-for="subItem in item.items"
|
||||
:key="subItem.title"
|
||||
>
|
||||
<SidebarMenuSubButton as-child>
|
||||
<NuxtLink :href="subItem.url">
|
||||
<span>{{ subItem.title }}</span>
|
||||
</NuxtLink>
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
</SidebarMenuSub>
|
||||
</CollapsibleContent>
|
||||
</SidebarMenuItem>
|
||||
</Collapsible>
|
||||
</SidebarMenu>
|
||||
</template>
|
||||
25
components/sidebars/navigation/nav-items.vue
Normal file
25
components/sidebars/navigation/nav-items.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import {
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "~/components/ui/sidebar";
|
||||
import type { SidebarNavItem } from "~/types/sidebar";
|
||||
|
||||
defineProps<{
|
||||
items: SidebarNavItem[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem v-for="item in items" :key="item.title">
|
||||
<SidebarMenuButton as-child>
|
||||
<NuxtLink :href="item.url">
|
||||
<component :is="item.icon" />
|
||||
<span>{{ item.title }}</span>
|
||||
</NuxtLink>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</template>
|
||||
|
|
@ -1,13 +1,17 @@
|
|||
<template>
|
||||
<Sidebar side="right" collapsible="none" class="w-96 hidden lg:flex">
|
||||
<SidebarContent class="p-2 overflow-y-auto">
|
||||
<Sidebar
|
||||
side="right"
|
||||
collapsible="none"
|
||||
class="hidden md:flex"
|
||||
style="--sidebar-width: 24rem; --sidebar-width-mobile: 18rem"
|
||||
>
|
||||
<SidebarContent class="overflow-y-auto *:p-2 *:gap-2">
|
||||
<NotificationsTimeline />
|
||||
</SidebarContent>
|
||||
<SidebarRail />
|
||||
</Sidebar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import NotificationsTimeline from "../timelines/notifications.vue";
|
||||
import { Sidebar, SidebarContent, SidebarRail } from "../ui/sidebar";
|
||||
<script setup lang="ts">
|
||||
import NotificationsTimeline from "~/components/timelines/notifications.vue";
|
||||
import { Sidebar, SidebarContent } from "~/components/ui/sidebar";
|
||||
</script>
|
||||
|
|
|
|||
74
components/sidebars/sidebar.ts
Normal file
74
components/sidebars/sidebar.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import {
|
||||
BedSingle,
|
||||
Bell,
|
||||
Globe,
|
||||
House,
|
||||
MapIcon,
|
||||
Settings2,
|
||||
} from "lucide-vue-next";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import type { SidebarConfig } from "~/types/sidebar";
|
||||
|
||||
export const sidebarConfig: SidebarConfig = {
|
||||
navMain: [
|
||||
{
|
||||
title: m.patchy_seemly_hound_grace(),
|
||||
url: "/preferences",
|
||||
icon: Settings2,
|
||||
items: [
|
||||
{
|
||||
title: m.factual_arable_jurgen_endure(),
|
||||
url: "/preferences/account",
|
||||
},
|
||||
{
|
||||
title: m.tough_clean_wolf_gleam(),
|
||||
url: "/preferences/appearance",
|
||||
},
|
||||
{
|
||||
title: m.legal_best_tadpole_rise(),
|
||||
url: "/preferences/behaviour",
|
||||
},
|
||||
{
|
||||
title: m.novel_trite_sloth_adapt(),
|
||||
url: "/preferences/emojis",
|
||||
},
|
||||
{
|
||||
title: m.safe_green_mink_cook(),
|
||||
url: "/preferences/roles",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
other: [
|
||||
{
|
||||
title: m.bland_chunky_sparrow_propel(),
|
||||
url: "/home",
|
||||
icon: House,
|
||||
requiresLogin: true,
|
||||
},
|
||||
{
|
||||
title: m.lost_trick_dog_grace(),
|
||||
url: "/public",
|
||||
icon: MapIcon,
|
||||
requiresLogin: false,
|
||||
},
|
||||
{
|
||||
title: m.crazy_game_parrot_pave(),
|
||||
url: "/local",
|
||||
icon: BedSingle,
|
||||
requiresLogin: false,
|
||||
},
|
||||
{
|
||||
title: m.real_tame_moose_greet(),
|
||||
url: "/global",
|
||||
icon: Globe,
|
||||
requiresLogin: false,
|
||||
},
|
||||
{
|
||||
title: m.that_patchy_mare_snip(),
|
||||
url: "/notifications",
|
||||
icon: Bell,
|
||||
requiresLogin: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SidebarInset, SidebarProvider } from "~/components/ui/sidebar";
|
||||
import Timelines from "~/components/navigation/timelines.vue";
|
||||
import { SidebarInset } from "~/components/ui/sidebar";
|
||||
import { SettingIds } from "~/settings";
|
||||
import Timelines from "../navigation/timelines.vue";
|
||||
import LeftSidebar from "./left-sidebar.vue";
|
||||
import RightSidebar from "./right-sidebar.vue";
|
||||
|
||||
|
|
@ -18,14 +18,15 @@ const showTimelines = computed(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<SidebarProvider>
|
||||
<LeftSidebar />
|
||||
<SidebarInset :class="cn('relative overflow-y-auto overflow-x-hidden', !isMd && 'pt-4')">
|
||||
<header v-if="showTimelines" class="flex h-16 items-center bg-background/80 backdrop-blur-2xl sticky top-0 inset-x-0 z-10 p-4">
|
||||
<Timelines />
|
||||
</header>
|
||||
<slot />
|
||||
</SidebarInset>
|
||||
<RightSidebar v-if="identity" v-show="showRightSidebar.value" />
|
||||
</SidebarProvider>
|
||||
<LeftSidebar />
|
||||
<main class="grow h-dvh overflow-y-auto">
|
||||
<header
|
||||
v-if="showTimelines"
|
||||
class="flex h-16 items-center bg-background/80 backdrop-blur-2xl sticky top-0 inset-x-0 z-10 p-4"
|
||||
>
|
||||
<Timelines />
|
||||
</header>
|
||||
<slot />
|
||||
</main>
|
||||
<RightSidebar v-if="identity" v-show="showRightSidebar.value" />
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue