refactor: ♻️ Improve mobile timeline switching

This commit is contained in:
Jesse Wierzbinski 2024-12-26 14:34:59 +01:00
parent 466c1eaaac
commit 3ff674017e
No known key found for this signature in database
10 changed files with 82 additions and 103 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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
View file

@ -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 {};

View file

@ -16,7 +16,7 @@
</CardFooter> </CardFooter>
</Card> </Card>
</Sidebar> </Sidebar>
<MobileNavbar /> <MobileNavbar v-if="identity" />
<ComposerDialog /> <ComposerDialog />
</template> </template>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>