mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
refactor: ♻️ Improve mobile timeline switching
This commit is contained in:
parent
466c1eaaac
commit
3ff674017e
|
|
@ -1,12 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="fixed md:hidden bottom-0 inset-x-0 border-t h-20 bg-background z-10 flex items-center justify-around *:p-7 *:w-full gap-6 p-6">
|
class="fixed md:hidden bottom-0 inset-x-0 border-t h-20 bg-background z-10 flex items-center justify-around *:h-full *:w-full gap-6 px-4 py-2">
|
||||||
<Timelines>
|
<Button :as="NuxtLink" href="/" variant="ghost" size="icon">
|
||||||
<Button variant="ghost" size="icon">
|
<Home class="!size-6" />
|
||||||
<Home class="!size-6" />
|
</Button>
|
||||||
</Button>
|
<Button :as="NuxtLink" href="/notifications" variant="ghost" size="icon">
|
||||||
</Timelines>
|
|
||||||
<Button v-if="identity" :as="NuxtLink" href="/notifications" variant="ghost" size="icon">
|
|
||||||
<Bell class="!size-6" />
|
<Bell class="!size-6" />
|
||||||
</Button>
|
</Button>
|
||||||
<AccountSwitcher>
|
<AccountSwitcher>
|
||||||
|
|
@ -14,7 +12,7 @@
|
||||||
<User class="!size-6" />
|
<User class="!size-6" />
|
||||||
</Button>
|
</Button>
|
||||||
</AccountSwitcher>
|
</AccountSwitcher>
|
||||||
<Button v-if="identity" variant="default" size="icon" :title="m.salty_aloof_turkey_nudge()"
|
<Button variant="default" size="icon" :title="m.salty_aloof_turkey_nudge()"
|
||||||
@click="useEvent('composer:open')">
|
@click="useEvent('composer:open')">
|
||||||
<Pen class="!size-6" />
|
<Pen class="!size-6" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -27,5 +25,4 @@ import * as m from "~/paraglide/messages.js";
|
||||||
import { NuxtLink } from "#components";
|
import { NuxtLink } from "#components";
|
||||||
import AccountSwitcher from "../sidebars/account-switcher.vue";
|
import AccountSwitcher from "../sidebars/account-switcher.vue";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import Timelines from "./timelines.vue";
|
</script>
|
||||||
</script>
|
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,61 @@
|
||||||
<template>
|
<template>
|
||||||
<Drawer>
|
<Tabs v-model:model-value="current">
|
||||||
<DrawerTrigger :as-child="true">
|
<TabsList>
|
||||||
<slot />
|
<TabsTrigger v-for="timeline in timelines.filter(
|
||||||
</DrawerTrigger>
|
|
||||||
<DrawerContent>
|
|
||||||
<DrawerClose v-for="item in timelines.filter(
|
|
||||||
i => i.requiresLogin ? !!identity : true,
|
i => i.requiresLogin ? !!identity : true,
|
||||||
)" :key="item.name" :as-child="true">
|
)" :key="timeline.value" :value="timeline.value" :as="NuxtLink" :href="timeline.url">
|
||||||
<Button :as="NuxtLink" :href="item.url" variant="outline" size="lg" class="w-full">
|
{{ timeline.name }}
|
||||||
<component :is="item.icon" />
|
</TabsTrigger>
|
||||||
{{ item.name }}
|
</TabsList>
|
||||||
</Button>
|
</Tabs>
|
||||||
</DrawerClose>
|
|
||||||
<DialogTitle class="sr-only">{{ m.trite_real_sawfish_drum() }}</DialogTitle>
|
|
||||||
<DialogDescription class="sr-only">{{ m.trite_real_sawfish_drum() }}</DialogDescription>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { BedSingle, Globe, House, MapIcon } from "lucide-vue-next";
|
import { BedSingle, Globe, House, MapIcon } from "lucide-vue-next";
|
||||||
|
import { Tabs, TabsList, TabsTrigger } from "~/components/ui/tabs";
|
||||||
import * as m from "~/paraglide/messages.js";
|
import * as m from "~/paraglide/messages.js";
|
||||||
import { NuxtLink } from "#components";
|
import { NuxtLink } from "#components";
|
||||||
import DrawerContent from "../modals/drawer-content.vue";
|
|
||||||
import { Button } from "../ui/button";
|
|
||||||
import { Drawer, DrawerTrigger } from "../ui/drawer";
|
|
||||||
|
|
||||||
const timelines = [
|
const timelines = [
|
||||||
{
|
{
|
||||||
name: m.bland_chunky_sparrow_propel(),
|
name: m.bland_chunky_sparrow_propel(),
|
||||||
|
value: "home",
|
||||||
url: "/home",
|
url: "/home",
|
||||||
icon: House,
|
icon: House,
|
||||||
requiresLogin: true,
|
requiresLogin: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: m.lost_trick_dog_grace(),
|
name: m.lost_trick_dog_grace(),
|
||||||
|
value: "public",
|
||||||
url: "/public",
|
url: "/public",
|
||||||
icon: MapIcon,
|
icon: MapIcon,
|
||||||
requiresLogin: false,
|
requiresLogin: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: m.crazy_game_parrot_pave(),
|
name: m.crazy_game_parrot_pave(),
|
||||||
|
value: "local",
|
||||||
url: "/local",
|
url: "/local",
|
||||||
icon: BedSingle,
|
icon: BedSingle,
|
||||||
requiresLogin: false,
|
requiresLogin: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: m.real_tame_moose_greet(),
|
name: m.real_tame_moose_greet(),
|
||||||
|
value: "global",
|
||||||
url: "/global",
|
url: "/global",
|
||||||
icon: Globe,
|
icon: Globe,
|
||||||
requiresLogin: false,
|
requiresLogin: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
|
||||||
|
const { beforeEach } = useRouter();
|
||||||
|
const { path } = useRoute();
|
||||||
|
|
||||||
|
const current = computed(() => {
|
||||||
|
if (path === "/") {
|
||||||
|
return identity.value ? "home" : "public";
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeline = timelines.find((i) => i.url === path);
|
||||||
|
return timeline ? timeline.value : "public";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ChevronDownIcon } from "lucide-vue-next";
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
|
|
@ -14,12 +15,19 @@ import {
|
||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
} from "~/components/ui/sidebar";
|
} from "~/components/ui/sidebar";
|
||||||
import { SettingIds } from "~/settings";
|
import { SettingIds } from "~/settings";
|
||||||
|
import Timelines from "../navigation/timelines.vue";
|
||||||
import LeftSidebar from "./left-sidebar.vue";
|
import LeftSidebar from "./left-sidebar.vue";
|
||||||
import RightSidebar from "./right-sidebar.vue";
|
import RightSidebar from "./right-sidebar.vue";
|
||||||
|
|
||||||
const showRightSidebar = useSetting(SettingIds.NotificationsSidebar);
|
const showRightSidebar = useSetting(SettingIds.NotificationsSidebar);
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const isMd = useMediaQuery("(max-width: 768px)");
|
||||||
|
const showTimelines = computed(
|
||||||
|
() =>
|
||||||
|
["/", "/home", "/local", "/public", "/global"].includes(route.path) &&
|
||||||
|
isMd.value,
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -28,22 +36,36 @@ const route = useRoute();
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
<header
|
<header
|
||||||
class="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12 overflow-hidden bg-background">
|
class="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12 overflow-hidden bg-background">
|
||||||
<div class="flex items-center gap-2 px-4">
|
<div :key="route.path" class="flex items-center gap-2 px-4 max-w-full">
|
||||||
<SidebarTrigger class="-ml-1" />
|
<SidebarTrigger class="-ml-1 shrink-0" />
|
||||||
<Separator orientation="vertical" class="mr-2 h-4" />
|
<Separator v-if="route.meta.breadcrumbs" orientation="vertical" class="mr-2 h-4" />
|
||||||
<Breadcrumb v-if="route.meta.breadcrumbs">
|
<Breadcrumb v-if="route.meta.breadcrumbs">
|
||||||
<BreadcrumbList>
|
<BreadcrumbList>
|
||||||
<template v-for="(breadcrumb, index) of route.meta.breadcrumbs()">
|
<template v-for="(breadcrumb, index) of route.meta.breadcrumbs()">
|
||||||
<BreadcrumbItem class="hidden md:block">
|
<BreadcrumbItem>
|
||||||
<component :is="breadcrumb.href ? BreadcrumbLink : BreadcrumbPage" :href="breadcrumb.href">
|
<component v-if="!breadcrumb.links"
|
||||||
|
:is="breadcrumb.href ? BreadcrumbLink : BreadcrumbPage" :href="breadcrumb.href">
|
||||||
{{ breadcrumb.text }}
|
{{ breadcrumb.text }}
|
||||||
</component>
|
</component>
|
||||||
|
<DropdownMenu v-else>
|
||||||
|
<DropdownMenuTrigger class="flex items-center gap-1">
|
||||||
|
{{ breadcrumb.text }}
|
||||||
|
<ChevronDownIcon class="size-4" />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start">
|
||||||
|
<DropdownMenuItem v-for="link of breadcrumb.links" :key="link.href"
|
||||||
|
:as="BreadcrumbLink" :href="link.href">
|
||||||
|
{{ link.text }}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
<BreadcrumbSeparator v-if="index !== (route.meta.breadcrumbs().length - 1)"
|
<BreadcrumbSeparator v-if="index !== (route.meta.breadcrumbs().length - 1)" />
|
||||||
class="hidden md:block" />
|
|
||||||
</template>
|
</template>
|
||||||
</BreadcrumbList>
|
</BreadcrumbList>
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
|
<Separator v-if="showTimelines" orientation="vertical" class="h-4" />
|
||||||
|
<Timelines v-if="showTimelines" />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="flex flex-1 flex-col gap-4 md:p-1 overflow-auto *:z-10">
|
<div class="flex flex-1 flex-col gap-4 md:p-1 overflow-auto *:z-10">
|
||||||
|
|
|
||||||
10
index.d.ts
vendored
10
index.d.ts
vendored
|
|
@ -1,12 +1,16 @@
|
||||||
|
import type { JSX } from "vue/jsx-runtime";
|
||||||
|
|
||||||
declare module "#app" {
|
declare module "#app" {
|
||||||
interface PageMeta {
|
interface PageMeta {
|
||||||
breadcrumbs?: () => {
|
breadcrumbs?: () => {
|
||||||
text: string;
|
text: string;
|
||||||
href?: string;
|
href?: string;
|
||||||
|
list?: {
|
||||||
|
text: string;
|
||||||
|
href: string;
|
||||||
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
header?: JSX.Element;
|
||||||
requiresAuth?: boolean;
|
requiresAuth?: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is always important to import/export something when augmenting a type
|
|
||||||
export {};
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<MobileNavbar />
|
<MobileNavbar v-if="identity" />
|
||||||
<ComposerDialog />
|
<ComposerDialog />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="tsx">
|
||||||
import Global from "~/components/timelines/global.vue";
|
import Global from "~/components/timelines/global.vue";
|
||||||
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
||||||
import * as m from "~/paraglide/messages.js";
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
@ -17,14 +17,5 @@ useHead({
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: "app",
|
layout: "app",
|
||||||
breadcrumbs: () => [
|
|
||||||
{
|
|
||||||
text: m.steep_aqua_fox_harbor(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: m.real_tame_moose_greet(),
|
|
||||||
href: "/global",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -6,26 +6,17 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="tsx">
|
||||||
import Home from "~/components/timelines/home.vue";
|
import Home from "~/components/timelines/home.vue";
|
||||||
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
||||||
import * as m from "~/paraglide/messages.js";
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: "Home",
|
title: m.bland_chunky_sparrow_propel(),
|
||||||
});
|
});
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: "app",
|
layout: "app",
|
||||||
breadcrumbs: () => [
|
|
||||||
{
|
|
||||||
text: m.steep_aqua_fox_harbor(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: m.bland_chunky_sparrow_propel(),
|
|
||||||
href: "/home",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="tsx">
|
||||||
import Home from "~/components/timelines/home.vue";
|
import Home from "~/components/timelines/home.vue";
|
||||||
import Public from "~/components/timelines/public.vue";
|
import Public from "~/components/timelines/public.vue";
|
||||||
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
||||||
|
|
@ -22,19 +22,5 @@ useHead({
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: "app",
|
layout: "app",
|
||||||
breadcrumbs: () => [
|
|
||||||
{
|
|
||||||
text: m.steep_aqua_fox_harbor(),
|
|
||||||
},
|
|
||||||
identity.value
|
|
||||||
? {
|
|
||||||
text: m.bland_chunky_sparrow_propel(),
|
|
||||||
href: "/home",
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
text: m.lost_trick_dog_grace(),
|
|
||||||
href: "/public",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="tsx" setup>
|
||||||
import Local from "~/components/timelines/local.vue";
|
import Local from "~/components/timelines/local.vue";
|
||||||
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
||||||
import * as m from "~/paraglide/messages.js";
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
@ -18,14 +18,5 @@ useHead({
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: "app",
|
layout: "app",
|
||||||
breadcrumbs: () => [
|
|
||||||
{
|
|
||||||
text: m.steep_aqua_fox_harbor(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: m.crazy_game_parrot_pave(),
|
|
||||||
href: "/local",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -6,25 +6,16 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="tsx">
|
||||||
import Public from "~/components/timelines/public.vue";
|
import Public from "~/components/timelines/public.vue";
|
||||||
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
import TimelineScroller from "~/components/timelines/timeline-scroller.vue";
|
||||||
import * as m from "~/paraglide/messages.js";
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: "Public",
|
title: m.lost_trick_dog_grace(),
|
||||||
});
|
});
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: "app",
|
layout: "app",
|
||||||
breadcrumbs: () => [
|
|
||||||
{
|
|
||||||
text: m.steep_aqua_fox_harbor(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: m.lost_trick_dog_grace(),
|
|
||||||
href: "/public",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue