Skip to content

Commit

Permalink
feat: header & footer
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 142b9c2 commit 52ac15d
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 60 deletions.
45 changes: 45 additions & 0 deletions app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export default defineAppConfig({
shadcnDocs: {
header: {
title: 'shadcn-docs',
showTitle: true,
logo: {
light: '/logo.svg',
dark: '/logo-dark.svg',
},
darkModeToggle: true,
links: [{
icon: 'lucide:github',
to: 'https://github.com/ZTL-UwU/shadcn-docs-nuxt',
target: '_blank',
}],
},
aside: {
useLevel: true,
collapse: false,
},
main: {
breadCrumb: true,
showTitle: true,
},
footer: {
credits: 'Copyright © 2024',
links: [
{
title: 'shadcn-vue',
to: 'https://www.shadcn-vue.com/',
target: '_blank',
},
{
icon: 'lucide:github',
to: 'https://github.com/ZTL-UwU/shadcn-docs-nuxt',
target: '_blank',
},
],
},
toc: {
enable: true,
title: 'On This Page',
},
},
});
4 changes: 2 additions & 2 deletions components/DarkModeToggle.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<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" />
<Icon name="lucide:sun" class="rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" size="18" />
<Icon name="lucide:moon" class="absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" size="18" />
<span class="sr-only">Toggle theme</span>
</UiButton>
</template>
Expand Down
13 changes: 9 additions & 4 deletions components/layout/Aside.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<UiScrollArea orientation="vertical" class="relative overflow-hidden h-full py-6 pr-6 text-sm" type="auto">
<ul class="pb-3 border-b">
<ul v-if="useLevel" class="pb-4 border-b mb-1">
<li v-for="link in navigation" :key="link.id">
<NuxtLink
:to="link._path"
Expand All @@ -25,14 +25,19 @@
<script setup lang="ts">
const { navDirFromPath } = useContentHelpers();
const { navigation } = useContent();
const { useLevel } = useConfig().value.aside;
const tree = computed(() => {
const route = useRoute();
const path = route.path.split('/');
const leveledPath = path.splice(0, 2).join('/');
if (useLevel) {
const leveledPath = path.splice(0, 2).join('/');
const dir = navDirFromPath(leveledPath, navigation.value);
return dir ?? [];
const dir = navDirFromPath(leveledPath, navigation.value);
return dir ?? [];
}
return navigation.value;
});
const path = useRoute().path;
Expand Down
2 changes: 1 addition & 1 deletion components/layout/AsideTree.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<ul :class="[level > 0 ? 'border-l' : 'py-2', level <= 1 && 'mt-2']">
<ul :class="[level > 0 ? 'border-l' : '[&:not(:first-child)]:py-2']">
<template v-for="link in links" :key="link._id">
<LayoutAsideTreeItem
:link="link"
Expand Down
84 changes: 42 additions & 42 deletions components/layout/AsideTreeItem.vue
Original file line number Diff line number Diff line change
@@ -1,46 +1,44 @@
<template>
<li class="border-l-red">
<div
class="rounded-md transition-all underline-offset-4"
:class="[level > 0 && 'pl-4']"
<li
class="rounded-md transition-all underline-offset-4"
:class="[level > 0 && 'pl-4']"
>
<UiCollapsible v-if="link.children" v-model:open="isOpen">
<UiCollapsibleTrigger class="w-full text-left py-1.5">
<div class="w-full flex gap-1">
<Icon
v-if="link.icon"
:name="link.icon"
class="self-center"
/>
{{ link.title }}
<Icon
:name="isOpen ? 'lucide:chevrons-down-up' : 'lucide:chevrons-up-down'"
size="12"
class="ml-auto self-center"
/>
</div>
</UiCollapsibleTrigger>
<UiCollapsibleContent>
<LayoutAsideTree :links="link.children" :level="level + 1" />
</UiCollapsibleContent>
</UiCollapsible>
<NuxtLink
v-else
:to="link._path"
class="w-full flex hover:underline text-muted-foreground gap-1"
:class="[
isActive && 'font-semibold text-primary',
level > 0 ? 'py-1.5' : 'pt-2',
]"
>
<UiCollapsible v-if="link.children" v-model:open="isOpen">
<UiCollapsibleTrigger class="w-full text-left" :class="[level > 0 ? 'py-1.5' : 'pt-2']">
<div class="w-full flex gap-1">
<Icon
v-if="link.icon"
:name="link.icon"
class="self-center"
/>
{{ link.title }}
<Icon
:name="isOpen ? 'lucide:chevrons-down-up' : 'lucide:chevrons-up-down'"
size="12"
class="ml-auto self-center"
/>
</div>
</UiCollapsibleTrigger>
<UiCollapsibleContent>
<LayoutAsideTree :links="link.children" :level="level + 1" />
</UiCollapsibleContent>
</UiCollapsible>
<NuxtLink
v-else
:to="link._path"
class="w-full flex hover:underline text-muted-foreground gap-1"
:class="[
isActive && 'font-semibold text-primary',
level > 0 ? 'py-1.5' : 'pt-2',
]"
>
<Icon
v-if="link.icon"
:name="link.icon"
class="self-center"
/>
{{ link.title }}
</NuxtLink>
</div>
<Icon
v-if="link.icon"
:name="link.icon"
class="self-center"
/>
{{ link.title }}
</NuxtLink>
</li>
</template>

Expand All @@ -52,8 +50,10 @@ const props = defineProps<{
level: number;
}>();
const { collapse } = useConfig().value.aside;
const collapsed = useCollapsedMap();
const isOpen = ref(props.level < 1 || collapsed.value.get(props.link._path) || true);
const isOpen = ref(collapsed.value.get(props.link._path) || (props.level < 1 && !collapse));
watch(isOpen, (v) => {
collapsed.value.set(props.link._path, v);
});
Expand Down
24 changes: 21 additions & 3 deletions components/layout/Footer.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
<template>
<footer class="py-6 md:px-8 md:py-0">
<div class="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
Footer
<footer class="text-muted-foreground py-6 md:px-8 md:py-0">
<div class="container flex flex-col items-center justify-between gap-2 md:h-24 md:flex-row">
<span class="text-sm">
{{ footer.credits }}
</span>
<span class="flex-1" />
<NuxtLink
v-for="(link, i) in footer.links"
:key="i"
:to="link?.to"
:target="link?.target"
>
<UiButton variant="ghost" :size="link?.icon && !link?.title ? 'icon' : 'default'" class="flex gap-2">
<Icon v-if="link?.icon" :name="link.icon" size="20" />
<span v-if="link?.title">{{ link.title }}</span>
</UiButton>
</NuxtLink>
</div>
</footer>
</template>

<script setup lang="ts">
const { footer } = useConfig().value;
</script>
21 changes: 19 additions & 2 deletions components/layout/Header.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
<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" />
<LayoutMobileNav />

<span class="flex-1" />
<DarkModeToggle />

<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 class="lg:hidden">
<div v-if="config.toc.enable" class="lg:hidden">
<LayoutToc is-small />
</div>
</header>
</template>

<script setup lang="ts">
const config = useConfig();
</script>
13 changes: 13 additions & 0 deletions components/layout/HeaderLogo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<NuxtLink v-if="logo.light && logo.dark" to="/" class="flex">
<img :src="logo.light" class="dark:hidden h-7">
<img :src="logo.dark" class="hidden dark:block h-7">
<span v-if="showTitle && title" class="self-center font-bold ml-3">
{{ title }}
</span>
</NuxtLink>
</template>

<script setup lang="ts">
const { logo, title, showTitle } = useConfig().value.header;
</script>
3 changes: 2 additions & 1 deletion components/layout/MobileNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
size="icon"
class="md:hidden"
>
<Icon name="lucide:menu" />
<Icon name="lucide:menu" size="18" />
</UiButton>
</UiSheetTrigger>
<UiSheetContent side="left" class="pr-0">
<LayoutHeaderLogo />
<LayoutAside />
</UiSheetContent>
</UiSheet>
Expand Down
3 changes: 2 additions & 1 deletion components/layout/Toc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
type="hover"
>
<p class="mb-2 text-base font-semibold">
On This Page
{{ title }}
</p>
<LayoutTocTree :links="toc.links" :level="0" />
</UiScrollArea>
Expand Down Expand Up @@ -35,5 +35,6 @@
defineProps<{ isSmall: boolean }>();
const { toc } = useContent();
const { title } = useConfig().value.toc;
const isOpen = ref(false);
</script>
40 changes: 40 additions & 0 deletions composables/useConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export function useConfig() {
const appConfig = computed(() => useAppConfig()?.shadcnDocs || {});

const { navKeyFromPath } = useContentHelpers();
const { navigation, page } = useContent();
const route = useRoute();

return computed(
() => {
const header = appConfig?.value?.header || {};
const main = appConfig?.value?.main || {};
const aside = appConfig?.value?.aside || {};
const footer = appConfig?.value?.footer || {};

return {
...appConfig.value,
header: {
...header,
...navKeyFromPath(route.path, 'header', navigation.value || []),
...page.value?.header,
} as typeof header,
main: {
...main,
...navKeyFromPath(route.path, 'main', navigation.value || []),
...page.value?.main,
} as typeof main,
aside: {
...aside,
...navKeyFromPath(route.path, 'aside', navigation.value || []),
...page.value?.aside,
} as typeof aside,
footer: {
...footer,
...navKeyFromPath(route.path, 'footer', navigation.value || []),
...page.value?.footer,
} as typeof footer,
};
},
);
}
9 changes: 5 additions & 4 deletions pages/[...slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
<aside class="fixed top-[102px] lg:top-[60px] 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 lg:grid lg:grid-cols-[1fr_200px]">
<main class="relative py-6" :class="[config.toc.enable && 'lg:gap-10 lg:py-8 lg:grid lg:grid-cols-[1fr_200px]']">
<div class="mx-auto w-full min-w-0">
<LayoutBreadcrumb class="mb-4" />
<LayoutBreadcrumb v-if="config.main.breadCrumb" class="mb-4" />

<div class="space-y-2 mb-6">
<div v-if="config.main.showTitle" class="space-y-2 mb-6">
<ProseH1>
{{ page?.title }}
</ProseH1>
Expand All @@ -35,7 +35,7 @@

<LayoutPrevNext />
</div>
<div class="hidden text-sm lg:block">
<div v-if="config.toc.enable" class="hidden text-sm lg:block">
<div class="sticky top-[90px] h-[calc(100vh-3.5rem)] overflow-hidden">
<LayoutToc :is-small="false" />
</div>
Expand All @@ -48,4 +48,5 @@

<script setup lang="ts">
const { page } = useContent();
const config = useConfig();
</script>
Binary file modified public/favicon.ico
Binary file not shown.
17 changes: 17 additions & 0 deletions public/logo-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 52ac15d

Please sign in to comment.