Skip to content

Commit

Permalink
feat: header nav
Browse files Browse the repository at this point in the history
Signed-off-by: ZTL-UwU <[email protected]>
  • Loading branch information
ZTL-UwU committed May 24, 2024
1 parent 52ac15d commit 0a309b1
Show file tree
Hide file tree
Showing 18 changed files with 405 additions and 21 deletions.
28 changes: 28 additions & 0 deletions app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,34 @@ export default defineAppConfig({
dark: '/logo-dark.svg',
},
darkModeToggle: true,
nav: [{
title: 'Credits',
links: [{
title: 'shadcn-ui',
to: 'https://ui.shadcn.com/',
description: 'For the beautiful component design & docs design',
target: '_blank',
}, {
title: 'shadcn-vue',
to: 'https://www.shadcn-vue.com/',
description: 'For the vue port of shadcn-ui & some docs component source',
target: '_blank',
}, {
title: 'Docus',
to: 'https://docus.dev/',
description: 'For inspiration & some docs component source',
target: '_blank',
}, {
title: 'Nuxt Content',
to: 'https://content.nuxt.com/',
description: 'Content made easy for Vue Developers',
target: '_blank',
}],
}, {
title: 'Use This Template',
to: 'https://nuxt.com/templates',
target: '_blank',
}],
links: [{
icon: 'lucide:github',
to: 'https://github.com/ZTL-UwU/shadcn-docs-nuxt',
Expand Down
4 changes: 4 additions & 0 deletions components/layout/Aside.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<template>
<UiScrollArea orientation="vertical" class="relative overflow-hidden h-full py-6 pr-6 text-sm" type="auto">
<LayoutHeaderNavMobile v-if="isMobile" class="border-b pb-2 mb-5" />
<ul v-if="useLevel" class="pb-4 border-b mb-1">
<li v-for="link in navigation" :key="link.id">
<NuxtLink
Expand All @@ -13,6 +14,7 @@
v-if="link.icon"
:name="link.icon"
class="self-center"
size="16"
/>
{{ link.title }}
</NuxtLink>
Expand All @@ -23,6 +25,8 @@
</template>

<script setup lang="ts">
defineProps<{ isMobile: boolean }>();
const { navDirFromPath } = useContentHelpers();
const { navigation } = useContent();
const { useLevel } = useConfig().value.aside;
Expand Down
32 changes: 16 additions & 16 deletions components/layout/Header.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
<template>
<header class="sticky z-40 top-0 bg-background/80 backdrop-blur-lg lg:border-b">
<div class="container px-4 md:px-8 flex h-14 max-w-screen-2xl items-center border-b lg:border-none">
<LayoutHeaderLogo class="hidden md:flex" />
<div class="container px-4 md:px-8 flex h-14 max-w-screen-2xl items-center border-b lg:border-none gap-2 justify-between">
<LayoutHeaderLogo class="hidden md:flex flex-1" />
<LayoutMobileNav />

<span class="flex-1" />

<DarkModeToggle v-if="config.header.darkModeToggle" />
<NuxtLink
v-for="(link, i) in config.header.links"
:key="i"
:to="link?.to"
:target="link?.target"
>
<UiButton variant="ghost" size="icon" class="flex gap-2">
<Icon v-if="link?.icon" :name="link.icon" size="18" />
</UiButton>
</NuxtLink>
<LayoutHeaderNav class="hidden lg:flex flex-1" />
<div class="flex flex-1 justify-end">
<DarkModeToggle v-if="config.header.darkModeToggle" />
<NuxtLink
v-for="(link, i) in config.header.links"
:key="i"
:to="link?.to"
:target="link?.target"
>
<UiButton variant="ghost" size="icon" class="flex gap-2">
<Icon v-if="link?.icon" :name="link.icon" size="18" />
</UiButton>
</NuxtLink>
</div>
</div>
<div v-if="config.toc.enable" class="lg:hidden">
<LayoutToc is-small />
Expand Down
43 changes: 43 additions & 0 deletions components/layout/HeaderNav.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<template>
<UiNavigationMenu>
<UiNavigationMenuList>
<UiNavigationMenuItem v-for="(item, i) in nav" :key="i">
<template v-if="item.links">
<UiNavigationMenuTrigger class="font-semibold bg-transparent">
{{ item.title }}
</UiNavigationMenuTrigger>
<UiNavigationMenuContent>
<ul class="w-[250px] p-2">
<li v-for="link in item.links" :key="link.title">
<NuxtLink
:to="link.to"
:target="link.to"
class="px-3 py-2 mb-1 hover:bg-muted rounded-md w-full block gap-2 transition-all"
>
<div class="font-semibold">
{{ link.title }}
</div>
<div class="text-muted-foreground text-sm">
{{ link.description }}
</div>
</NuxtLink>
</li>
</ul>
</UiNavigationMenuContent>
</template>
<NuxtLink v-else :to="item.to" :target="item.target" class="relative!">
<Icon name="lucide:arrow-up-right" class="absolute right-2 top-2 text-muted-foreground" size="13" />
<UiNavigationMenuLink class="pr-6 font-semibold bg-transparent" :class="navigationMenuTriggerStyle()">
{{ item.title }}
</UiNavigationMenuLink>
</NuxtLink>
</UiNavigationMenuItem>
</UiNavigationMenuList>
</UiNavigationMenu>
</template>

<script setup lang="ts">
import { navigationMenuTriggerStyle } from '@/components/ui/navigation-menu';
const { nav } = useConfig().value.header;
</script>
14 changes: 14 additions & 0 deletions components/layout/HeaderNavMobile.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<template>
<div>
<LayoutHeaderNavMobileItem
v-for="(item, i) in nav"
:key="i"
:item="item"
:index="i"
/>
</div>
</template>

<script setup lang="ts">
const { nav } = useConfig().value.header;
</script>
66 changes: 66 additions & 0 deletions components/layout/HeaderNavMobileItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<template>
<template v-if="item.links">
<UiCollapsible v-model:open="isOpen">
<UiCollapsibleTrigger class="w-full text-left p-2">
<div class="w-full flex gap-1">
{{ item.title }}
<Icon
:name="isOpen ? 'lucide:chevrons-down-up' : 'lucide:chevrons-up-down'"
size="12"
class="ml-auto self-center"
/>
</div>
</UiCollapsibleTrigger>
<UiCollapsibleContent>
<ul class="pl-2">
<li v-for="link in item.links" :key="link.title">
<NuxtLink
:to="link.to"
:target="link.to"
class="px-3 py-2 mb-1 hover:bg-muted rounded-md w-full block gap-2 transition-all"
>
<div class="font-semibold">
{{ link.title }}
</div>
<div class="text-muted-foreground text-sm">
{{ link.description }}
</div>
</NuxtLink>
</li>
</ul>
</UiCollapsibleContent>
</UiCollapsible>
</template>
<NuxtLink v-else :to="item.to" :target="item.target" class="w-full flex p-2">
{{ item.title }}
<Icon name="lucide:arrow-up-right" class="ml-1 text-muted-foreground" size="12" />
</NuxtLink>
</template>

<script setup lang="ts">
const props = defineProps<{
item: {
title: string;
links: {
title: string;
to: string;
description: string;
target: string;
}[];
to?: undefined;
target?: undefined;
} | {
title: string;
to: string;
target: string;
links?: undefined;
};
index: number;
}>();
const collapsed = useCollapsedMap();
const isOpen = ref(collapsed.value.get(`mobile-header-nav${props.index}`) || false);
watch(isOpen, (v) => {
collapsed.value.set(`mobile-header-nav${props.index}`, v);
});
</script>
2 changes: 1 addition & 1 deletion components/layout/MobileNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</UiSheetTrigger>
<UiSheetContent side="left" class="pr-0">
<LayoutHeaderLogo />
<LayoutAside />
<LayoutAside is-mobile />
</UiSheetContent>
</UiSheet>
</template>
Expand Down
6 changes: 3 additions & 3 deletions components/layout/Toc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
<UiCollapsible
v-else
v-model:open="isOpen"
class="block lg:hidden text-sm px-4 py-3 w-full border-b"
class="block lg:hidden text-sm w-full border-b"
>
<UiCollapsibleTrigger class="w-full flex text-left font-semibold">
<UiCollapsibleTrigger class="px-4 py-3 w-full flex text-left font-semibold">
On This Page
<Icon
name="lucide:chevron-right"
Expand All @@ -25,7 +25,7 @@
/>
</UiCollapsibleTrigger>
<UiCollapsibleContent>
<LayoutTocTree :links="toc.links" :level="0" class="text-sm pl-4 border-l my-3" />
<LayoutTocTree :links="toc.links" :level="0" class="text-sm pl-4 border-l mb-3 mx-4" />
</UiCollapsibleContent>
</UiCollapsible>
</template>
Expand Down
33 changes: 33 additions & 0 deletions components/ui/navigation-menu/NavigationMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<NavigationMenuRoot
v-bind="forwarded"
:class="cn('relative z-10 flex max-w-max flex-1 items-center justify-center', props.class)"
>
<slot />
<NavigationMenuViewport />
</NavigationMenuRoot>
</template>

<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue';
import {
NavigationMenuRoot,
type NavigationMenuRootEmits,
type NavigationMenuRootProps,
useForwardPropsEmits,
} from 'radix-vue';
import NavigationMenuViewport from './NavigationMenuViewport.vue';
import { cn } from '@/lib/utils';
const props = defineProps<NavigationMenuRootProps & { class?: HTMLAttributes['class'] }>();
const emits = defineEmits<NavigationMenuRootEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
34 changes: 34 additions & 0 deletions components/ui/navigation-menu/NavigationMenuContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<NavigationMenuContent
v-bind="forwarded"
:class="cn(
'left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto',
props.class,
)"
>
<slot />
</NavigationMenuContent>
</template>

<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue';
import {
NavigationMenuContent,
type NavigationMenuContentEmits,
type NavigationMenuContentProps,
useForwardPropsEmits,
} from 'radix-vue';
import { cn } from '@/lib/utils';
const props = defineProps<NavigationMenuContentProps & { class?: HTMLAttributes['class'] }>();
const emits = defineEmits<NavigationMenuContentEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
24 changes: 24 additions & 0 deletions components/ui/navigation-menu/NavigationMenuIndicator.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<NavigationMenuIndicator
v-bind="forwardedProps"
:class="cn('top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in', props.class)"
>
<div class="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
</NavigationMenuIndicator>
</template>

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

<script setup lang="ts">
import { NavigationMenuItem, type NavigationMenuItemProps } from 'radix-vue';
const props = defineProps<NavigationMenuItemProps>();
</script>
19 changes: 19 additions & 0 deletions components/ui/navigation-menu/NavigationMenuLink.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<NavigationMenuLink v-bind="forwarded">
<slot />
</NavigationMenuLink>
</template>

<script setup lang="ts">
import {
NavigationMenuLink,
type NavigationMenuLinkEmits,
type NavigationMenuLinkProps,
useForwardPropsEmits,
} from 'radix-vue';
const props = defineProps<NavigationMenuLinkProps>();
const emits = defineEmits<NavigationMenuLinkEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
Loading

0 comments on commit 0a309b1

Please sign in to comment.