Skip to content

Commit

Permalink
feat: mobile navbar
Browse files Browse the repository at this point in the history
Signed-off-by: ZTL-UwU <zhangtianli2006@163.com>
  • Loading branch information
ZTL-UwU committed May 24, 2024
1 parent 3c9b934 commit 58c75cb
Show file tree
Hide file tree
Showing 16 changed files with 255 additions and 27 deletions.
2 changes: 1 addition & 1 deletion components/DarkModeToggle.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<UiButton variant="outline" size="sm" @click="switchMode">
<UiButton variant="ghost" size="icon" @click="switchMode">
<Icon name="lucide:moon" class="h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Icon name="lucide:sun" class="absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span class="sr-only">Toggle theme</span>
Expand Down
36 changes: 16 additions & 20 deletions components/layout/Aside.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
<template>
<aside
class="fixed top-16 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block overflow-y-auto"
>
<UiScrollArea orientation="vertical" class="relative overflow-hidden h-full py-6 pr-6 text-sm" type="auto">
<ul class="pb-3 border-b">
<li v-for="link in navigation" :key="link.id">
<NuxtLink
:to="link._path"
class="px-2 py-2 mb-1 hover:bg-muted rounded-md w-full block transition-all"
:class="[
path.startsWith(link._path) && 'bg-muted hover:bg-muted font-semibold',
]"
>
{{ link.title }}
</NuxtLink>
</li>
</ul>
<LayoutAsideTree :links="tree" :level="0" class="pl-2" />
</UiScrollArea>
</aside>
<UiScrollArea orientation="vertical" class="relative overflow-hidden h-full py-6 pr-6 text-sm" type="auto">
<ul class="pb-3 border-b">
<li v-for="link in navigation" :key="link.id">
<NuxtLink
:to="link._path"
class="px-2 py-2 mb-1 hover:bg-muted rounded-md w-full block transition-all"
:class="[
path.startsWith(link._path) && 'bg-muted hover:bg-muted font-semibold',
]"
>
{{ link.title }}
</NuxtLink>
</li>
</ul>
<LayoutAsideTree :links="tree" :level="0" class="pl-2" />
</UiScrollArea>
</template>

<script setup lang="ts">
Expand Down
5 changes: 2 additions & 3 deletions components/layout/AsideTreeItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
:class="[level > 0 && 'pl-4']"
>
<UiCollapsible v-if="link.children" v-model:open="isOpen">
<UiCollapsibleTrigger class="w-full text-left" :class="[level > 0 ? 'py-1.5 px-2' : 'pt-2']">
<UiCollapsibleTrigger class="w-full text-left" :class="[level > 0 ? 'py-1.5' : 'pt-2']">
<div class="w-full flex">
{{ link.title }}
<Icon
:name="isOpen ? 'lucide:chevrons-down-up' : 'lucide:chevrons-up-down'"
size="12"
class="ml-auto self-center"
:class="level === 0 && 'mr-2'"
/>
</div>
</UiCollapsibleTrigger>
Expand All @@ -26,7 +25,7 @@
class="w-full block hover:underline text-muted-foreground"
:class="[
isActive && 'font-semibold text-primary',
level > 0 ? 'py-1.5 px-2' : 'pt-2',
level > 0 ? 'py-1.5' : 'pt-2',
]"
>
{{ link.title }}
Expand Down
4 changes: 3 additions & 1 deletion components/layout/Header.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<template>
<header class="sticky z-40 top-0 bg-background/80 backdrop-blur-lg border-b border-border">
<div
class="container flex h-14 max-w-screen-2xl items-center"
class="container px-4 md:px-8 flex h-14 max-w-screen-2xl items-center"
>
<LayoutMobileNav />
<span class="flex-1" />
<DarkModeToggle />
</div>
</header>
Expand Down
20 changes: 20 additions & 0 deletions components/layout/MobileNav.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<UiSheet v-model:open="open">
<UiSheetTrigger as-child>
<UiButton
variant="ghost"
size="icon"
class="md:hidden"
>
<Icon name="lucide:menu" />
</UiButton>
</UiSheetTrigger>
<UiSheetContent side="left" class="pr-0">
<LayoutAside />
</UiSheetContent>
</UiSheet>
</template>

<script setup lang="ts">
const open = ref(false);
</script>
14 changes: 14 additions & 0 deletions components/ui/sheet/Sheet.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<template>
<DialogRoot v-bind="forwarded">
<slot />
</DialogRoot>
</template>

<script setup lang="ts">
import { DialogRoot, type DialogRootEmits, type DialogRootProps, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<DialogRootProps>();
const emits = defineEmits<DialogRootEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
11 changes: 11 additions & 0 deletions components/ui/sheet/SheetClose.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<DialogClose v-bind="props">
<slot />
</DialogClose>
</template>

<script setup lang="ts">
import { DialogClose, type DialogCloseProps } from 'radix-vue';
const props = defineProps<DialogCloseProps>();
</script>
56 changes: 56 additions & 0 deletions components/ui/sheet/SheetContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<template>
<DialogPortal>
<DialogOverlay
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/>
<DialogContent
:class="cn(sheetVariants({ side }), props.class)"
v-bind="{ ...forwarded, ...$attrs }"
>
<slot />

<DialogClose
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"
>
<X class="w-4 h-4 text-muted-foreground" />
</DialogClose>
</DialogContent>
</DialogPortal>
</template>

<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue';
import {
DialogClose,
DialogContent,
type DialogContentEmits,
type DialogContentProps,
DialogOverlay,
DialogPortal,
useForwardPropsEmits,
} from 'radix-vue';
import { X } from 'lucide-vue-next';
import { type SheetVariants, sheetVariants } from '.';
import { cn } from '@/lib/utils';
interface SheetContentProps extends DialogContentProps {
class?: HTMLAttributes['class'];
side?: SheetVariants['side'];
}
defineOptions({
inheritAttrs: false,
});
const props = defineProps<SheetContentProps>();
const emits = defineEmits<DialogContentEmits>();
const delegatedProps = computed(() => {
const { class: _, side, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
22 changes: 22 additions & 0 deletions components/ui/sheet/SheetDescription.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<DialogDescription
:class="cn('text-sm text-muted-foreground', props.class)"
v-bind="delegatedProps"
>
<slot />
</DialogDescription>
</template>

<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue';
import { DialogDescription, type DialogDescriptionProps } from 'radix-vue';
import { cn } from '@/lib/utils';
const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes['class'] }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
19 changes: 19 additions & 0 deletions components/ui/sheet/SheetFooter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div
:class="
cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
props.class,
)
"
>
<slot />
</div>
</template>

<script setup lang="ts">
import type { HTMLAttributes } from 'vue';
import { cn } from '@/lib/utils';
const props = defineProps<{ class?: HTMLAttributes['class'] }>();
</script>
16 changes: 16 additions & 0 deletions components/ui/sheet/SheetHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<div
:class="
cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)
"
>
<slot />
</div>
</template>

<script setup lang="ts">
import type { HTMLAttributes } from 'vue';
import { cn } from '@/lib/utils';
const props = defineProps<{ class?: HTMLAttributes['class'] }>();
</script>
22 changes: 22 additions & 0 deletions components/ui/sheet/SheetTitle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<DialogTitle
:class="cn('text-lg font-semibold text-foreground', props.class)"
v-bind="delegatedProps"
>
<slot />
</DialogTitle>
</template>

<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue';
import { DialogTitle, type DialogTitleProps } from 'radix-vue';
import { cn } from '@/lib/utils';
const props = defineProps<DialogTitleProps & { class?: HTMLAttributes['class'] }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
11 changes: 11 additions & 0 deletions components/ui/sheet/SheetTrigger.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<DialogTrigger v-bind="props">
<slot />
</DialogTrigger>
</template>

<script setup lang="ts">
import { DialogTrigger, type DialogTriggerProps } from 'radix-vue';
const props = defineProps<DialogTriggerProps>();
</script>
31 changes: 31 additions & 0 deletions components/ui/sheet/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { type VariantProps, cva } from 'class-variance-authority';

export { default as Sheet } from './Sheet.vue';
export { default as SheetTrigger } from './SheetTrigger.vue';
export { default as SheetClose } from './SheetClose.vue';
export { default as SheetContent } from './SheetContent.vue';
export { default as SheetHeader } from './SheetHeader.vue';
export { default as SheetTitle } from './SheetTitle.vue';
export { default as SheetDescription } from './SheetDescription.vue';
export { default as SheetFooter } from './SheetFooter.vue';

export const sheetVariants = cva(
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
{
variants: {
side: {
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
bottom:
'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
right:
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
},
},
defaultVariants: {
side: 'right',
},
},
);

export type SheetVariants = VariantProps<typeof sheetVariants>;
7 changes: 7 additions & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,11 @@ export default defineNuxtConfig({
},
},
},
typescript: {
tsConfig: {
compilerOptions: {
baseUrl: '.',
},
},
},
});
6 changes: 4 additions & 2 deletions pages/[...slug].vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<template>
<LayoutHeader />
<div class="border-b">
<div class="container flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10">
<LayoutAside />
<div class="container px-4 md:px-8 flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10">
<aside class="fixed top-16 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block overflow-y-auto">
<LayoutAside />
</aside>
<main class="relative py-6 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_250px]">
<div class="mx-auto w-full min-w-0">
<div class="xl:hidden">
Expand Down

0 comments on commit 58c75cb

Please sign in to comment.