Stelle docs-site auf Nuxt-UI-Docs-Template-Stil um
This commit is contained in:
57
docs-site/app/app.config.ts
Normal file
57
docs-site/app/app.config.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
export default defineAppConfig({
|
||||
ui: {
|
||||
colors: {
|
||||
primary: 'green',
|
||||
neutral: 'slate'
|
||||
},
|
||||
footer: {
|
||||
slots: {
|
||||
root: 'border-t border-default',
|
||||
left: 'text-sm text-muted'
|
||||
}
|
||||
}
|
||||
},
|
||||
seo: {
|
||||
siteName: 'FEDEO Docs'
|
||||
},
|
||||
header: {
|
||||
title: 'FEDEO Docs',
|
||||
to: '/',
|
||||
search: true,
|
||||
colorMode: true,
|
||||
links: [
|
||||
{
|
||||
icon: 'i-simple-icons-github',
|
||||
to: 'https://git.federspiel.tech/flfeders/FEDEO',
|
||||
target: '_blank',
|
||||
'aria-label': 'Repository'
|
||||
}
|
||||
]
|
||||
},
|
||||
footer: {
|
||||
credits: `Built with Nuxt UI • © ${new Date().getFullYear()} FEDEO`,
|
||||
colorMode: false,
|
||||
links: [
|
||||
{
|
||||
icon: 'i-simple-icons-github',
|
||||
to: 'https://git.federspiel.tech/flfeders/FEDEO',
|
||||
target: '_blank',
|
||||
'aria-label': 'Repository'
|
||||
}
|
||||
]
|
||||
},
|
||||
toc: {
|
||||
title: 'Inhaltsverzeichnis',
|
||||
bottom: {
|
||||
title: 'Links',
|
||||
links: [
|
||||
{
|
||||
icon: 'i-lucide-book-open',
|
||||
label: 'FEDEO Projekt',
|
||||
to: 'https://git.federspiel.tech/flfeders/FEDEO',
|
||||
target: '_blank'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
44
docs-site/app/app.vue
Normal file
44
docs-site/app/app.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
const { seo } = useAppConfig()
|
||||
|
||||
const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'))
|
||||
const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('docs'), {
|
||||
server: false
|
||||
})
|
||||
|
||||
useHead({
|
||||
meta: [{ name: 'viewport', content: 'width=device-width, initial-scale=1' }],
|
||||
htmlAttrs: { lang: 'de' }
|
||||
})
|
||||
|
||||
useSeoMeta({
|
||||
titleTemplate: `%s - ${seo?.siteName}`,
|
||||
ogSiteName: seo?.siteName,
|
||||
twitterCard: 'summary_large_image'
|
||||
})
|
||||
|
||||
provide('navigation', navigation)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp>
|
||||
<NuxtLoadingIndicator />
|
||||
|
||||
<AppHeader />
|
||||
|
||||
<UMain>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</UMain>
|
||||
|
||||
<AppFooter />
|
||||
|
||||
<ClientOnly>
|
||||
<LazyUContentSearch
|
||||
:files="files"
|
||||
:navigation="navigation"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</UApp>
|
||||
</template>
|
||||
25
docs-site/app/assets/css/main.css
Normal file
25
docs-site/app/assets/css/main.css
Normal file
@@ -0,0 +1,25 @@
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@source "../../../content/**/*";
|
||||
|
||||
@theme static {
|
||||
--container-8xl: 90rem;
|
||||
--font-sans: 'Public Sans', sans-serif;
|
||||
|
||||
--color-green-50: #EFFDF5;
|
||||
--color-green-100: #D9FBE8;
|
||||
--color-green-200: #B3F5D1;
|
||||
--color-green-300: #75EDAE;
|
||||
--color-green-400: #00DC82;
|
||||
--color-green-500: #00C16A;
|
||||
--color-green-600: #00A155;
|
||||
--color-green-700: #007F45;
|
||||
--color-green-800: #016538;
|
||||
--color-green-900: #0A5331;
|
||||
--color-green-950: #052E16;
|
||||
}
|
||||
|
||||
:root {
|
||||
--ui-container: var(--container-8xl);
|
||||
}
|
||||
23
docs-site/app/components/AppFooter.vue
Normal file
23
docs-site/app/components/AppFooter.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
const { footer } = useAppConfig()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UFooter>
|
||||
<template #left>
|
||||
{{ footer.credits }}
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<UColorModeButton v-if="footer?.colorMode" />
|
||||
|
||||
<template v-if="footer?.links">
|
||||
<UButton
|
||||
v-for="(link, index) of footer?.links"
|
||||
:key="index"
|
||||
v-bind="{ color: 'neutral', variant: 'ghost', ...link }"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</UFooter>
|
||||
</template>
|
||||
49
docs-site/app/components/AppHeader.vue
Normal file
49
docs-site/app/components/AppHeader.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContentNavigationItem } from '@nuxt/content'
|
||||
|
||||
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
const { header } = useAppConfig()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UHeader
|
||||
:ui="{ center: 'flex-1' }"
|
||||
:to="header?.to || '/'"
|
||||
>
|
||||
<UContentSearchButton
|
||||
v-if="header?.search"
|
||||
:collapsed="false"
|
||||
class="w-full"
|
||||
/>
|
||||
|
||||
<template #left>
|
||||
<NuxtLink :to="header?.to || '/'">
|
||||
<AppLogo class="w-auto h-6 shrink-0" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<UContentSearchButton
|
||||
v-if="header?.search"
|
||||
class="lg:hidden"
|
||||
/>
|
||||
|
||||
<UColorModeButton v-if="header?.colorMode" />
|
||||
|
||||
<template v-if="header?.links">
|
||||
<UButton
|
||||
v-for="(link, index) of header.links"
|
||||
:key="index"
|
||||
v-bind="{ color: 'neutral', variant: 'ghost', ...link }"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<UContentNavigation
|
||||
highlight
|
||||
:navigation="navigation"
|
||||
/>
|
||||
</template>
|
||||
</UHeader>
|
||||
</template>
|
||||
3
docs-site/app/components/AppLogo.vue
Normal file
3
docs-site/app/components/AppLogo.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<span class="font-semibold text-primary">FEDEO Docs</span>
|
||||
</template>
|
||||
31
docs-site/app/error.vue
Normal file
31
docs-site/app/error.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import type { NuxtError } from '#app'
|
||||
|
||||
defineProps<{
|
||||
error: NuxtError
|
||||
}>()
|
||||
|
||||
const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'))
|
||||
const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('docs'), {
|
||||
server: false
|
||||
})
|
||||
|
||||
provide('navigation', navigation)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp>
|
||||
<AppHeader />
|
||||
|
||||
<UError :error="error" />
|
||||
|
||||
<AppFooter />
|
||||
|
||||
<ClientOnly>
|
||||
<LazyUContentSearch
|
||||
:files="files"
|
||||
:navigation="navigation"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</UApp>
|
||||
</template>
|
||||
22
docs-site/app/layouts/docs.vue
Normal file
22
docs-site/app/layouts/docs.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContentNavigationItem } from '@nuxt/content'
|
||||
|
||||
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UContainer>
|
||||
<UPage>
|
||||
<template #left>
|
||||
<UPageAside>
|
||||
<UContentNavigation
|
||||
highlight
|
||||
:navigation="navigation"
|
||||
/>
|
||||
</UPageAside>
|
||||
</template>
|
||||
|
||||
<slot />
|
||||
</UPage>
|
||||
</UContainer>
|
||||
</template>
|
||||
90
docs-site/app/pages/[...slug].vue
Normal file
90
docs-site/app/pages/[...slug].vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<script setup lang="ts">
|
||||
import type { ContentNavigationItem } from '@nuxt/content'
|
||||
import { findPageHeadline } from '@nuxt/content/utils'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'docs'
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const { toc } = useAppConfig()
|
||||
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
|
||||
|
||||
const { data: page } = await useAsyncData(route.path, () => queryCollection('docs').path(route.path).first())
|
||||
if (!page.value) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Seite nicht gefunden', fatal: true })
|
||||
}
|
||||
|
||||
const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
||||
return queryCollectionItemSurroundings('docs', route.path, {
|
||||
fields: ['description']
|
||||
})
|
||||
})
|
||||
|
||||
const title = page.value.seo?.title || page.value.title
|
||||
const description = page.value.seo?.description || page.value.description
|
||||
|
||||
useSeoMeta({
|
||||
title,
|
||||
ogTitle: title,
|
||||
description,
|
||||
ogDescription: description
|
||||
})
|
||||
|
||||
const headline = computed(() => findPageHeadline(navigation?.value, page.value?.path))
|
||||
|
||||
const links = computed(() => {
|
||||
return [...(toc?.bottom?.links || [])].filter(Boolean)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPage v-if="page">
|
||||
<UPageHeader
|
||||
:title="page.title"
|
||||
:description="page.description"
|
||||
:headline="headline"
|
||||
/>
|
||||
|
||||
<UPageBody>
|
||||
<ContentRenderer
|
||||
v-if="page"
|
||||
:value="page"
|
||||
/>
|
||||
|
||||
<USeparator v-if="surround?.length" />
|
||||
|
||||
<UContentSurround :surround="surround" />
|
||||
</UPageBody>
|
||||
|
||||
<template
|
||||
v-if="page?.body?.toc?.links?.length"
|
||||
#right
|
||||
>
|
||||
<UContentToc
|
||||
:title="toc?.title"
|
||||
:links="page.body?.toc?.links"
|
||||
>
|
||||
<template
|
||||
v-if="toc?.bottom"
|
||||
#bottom
|
||||
>
|
||||
<div
|
||||
class="hidden lg:block space-y-6"
|
||||
:class="{ 'mt-6!': page.body?.toc?.links?.length }"
|
||||
>
|
||||
<USeparator
|
||||
v-if="page.body?.toc?.links?.length"
|
||||
type="dashed"
|
||||
/>
|
||||
|
||||
<UPageLinks
|
||||
:title="toc.bottom.title"
|
||||
:links="links"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</UContentToc>
|
||||
</template>
|
||||
</UPage>
|
||||
</template>
|
||||
25
docs-site/app/pages/index.vue
Normal file
25
docs-site/app/pages/index.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
const { data: page } = await useAsyncData('index', () => queryCollection('landing').path('/').first())
|
||||
if (!page.value) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Seite nicht gefunden', fatal: true })
|
||||
}
|
||||
|
||||
const title = page.value.seo?.title || page.value.title
|
||||
const description = page.value.seo?.description || page.value.description
|
||||
|
||||
useSeoMeta({
|
||||
titleTemplate: '',
|
||||
title,
|
||||
ogTitle: title,
|
||||
description,
|
||||
ogDescription: description
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentRenderer
|
||||
v-if="page"
|
||||
:value="page"
|
||||
:prose="false"
|
||||
/>
|
||||
</template>
|
||||
Reference in New Issue
Block a user