Skip to content

Commit 31f85d7

Browse files
committed
doc: nuxt content v3
1 parent 20ab4ae commit 31f85d7

23 files changed

+952
-1295
lines changed

docs/app.vue

+45-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,51 @@
11
<script setup lang="ts">
2-
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
2+
const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'), {
3+
transform(data) {
4+
return data?.[0]?.children.map((nav) => {
5+
return {
6+
...nav,
7+
children: nav.children.map((item) => {
8+
return {
9+
...item,
10+
to: item.path,
11+
}
12+
}),
13+
}
14+
})
15+
},
16+
})
17+
const { data: scriptsNavigation } = await useAsyncData('script-navigation', () => queryCollectionNavigation('scripts'), {
18+
transform(data) {
19+
return data?.[0]?.children.map((nav) => {
20+
return {
21+
...nav,
22+
children: nav.children.map((item) => {
23+
return {
24+
...item,
25+
to: item.path,
26+
}
27+
}),
28+
}
29+
})
30+
},
31+
})
332
4-
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation())
5-
const { data: files } = useLazyFetch<ParsedContent[]>('/api/search.json', {
6-
default: () => [],
33+
const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('docs'), {
34+
server: false,
35+
})
36+
const { data: scriptFiles } = useLazyAsyncData('search-scripts', () => queryCollectionSearchSections('scripts'), {
737
server: false,
838
})
939
10-
const route = useRoute()
1140
provide('navigation', computed(() => {
12-
return route.path.startsWith('/docs') ? navigation.value?.[0]?.children : navigation.value?.[1]?.children || []
41+
return navigation.value || []
1342
}))
1443
provide('topGuides', computed(() => {
15-
return navigation.value?.[0]?.children.find(nav => nav.title === 'Guides')?.children || []
44+
return (navigation.value || []).find(nav => nav.title === 'Guides')?.children || []
45+
}))
46+
47+
provide('scripts-navigation', computed(() => {
48+
return scriptsNavigation.value || []
1649
}))
1750
</script>
1851

@@ -29,7 +62,11 @@ provide('topGuides', computed(() => {
2962
<Footer />
3063

3164
<ClientOnly>
32-
<LazyUContentSearch :files="files" :navigation="navigation" />
65+
<LazyUContentSearch
66+
:files="[files, scriptFiles].flat()"
67+
:navigation="[navigation, scriptsNavigation].flat()"
68+
:fuse="{ resultLimit: 42 }"
69+
/>
3370
</ClientOnly>
3471

3572
<UNotifications />

docs/components/Header.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import type { NavItem } from '@nuxt/content/dist/runtime/types'
2+
import type { NavItem } from '@nuxt/contents'
33
44
const navigation = inject<NavItem[]>('navigation', [])
55
const { metaSymbol } = useShortcuts()

docs/components/content/CodeGroup.vue

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<script setup lang="ts">
2+
import type { PropType } from 'vue'
3+
4+
defineOptions({
5+
inheritAttrs: false,
6+
})
7+
8+
defineProps({
9+
class: {
10+
type: [String, Object, Array] as PropType<any>,
11+
default: undefined,
12+
},
13+
})
14+
15+
const ui = {
16+
wrapper: 'relative [&>div:last-child]:!my-0 [&>div:last-child]:!static my-5',
17+
header: 'flex items-center gap-1 border border-gray-200 dark:border-gray-700 border-b-0 rounded-t-md overflow-hidden p-2',
18+
tab: {
19+
base: 'px-2 py-1.5 focus:outline-none text-gray-700 dark:text-gray-200 text-sm rounded-md flex items-center gap-1.5',
20+
active: 'bg-gray-100 dark:bg-gray-800',
21+
inactive: 'hover:bg-gray-50 dark:hover:bg-gray-800/50',
22+
icon: {
23+
base: '',
24+
},
25+
},
26+
}
27+
28+
const slots = useSlots()
29+
30+
const selectedIndex = ref(0)
31+
defineExpose({ selectedIndex })
32+
33+
const rerenderCounter = ref(1)
34+
35+
function transformSlot(slot: any, index: number) {
36+
if (typeof slot.type === 'symbol') {
37+
return slot.children?.map(transformSlot)
38+
}
39+
40+
return {
41+
label: slot.props?.filename || slot.props?.label || `${index}`,
42+
icon: slot.props?.icon,
43+
component: slot,
44+
}
45+
}
46+
47+
// Computed
48+
49+
const tabs = computed(() => slots.default?.()?.flatMap(transformSlot).filter(Boolean) || [])
50+
51+
const selectedTab = computed(() => tabs.value.find((_, index) => index === selectedIndex.value))
52+
53+
onBeforeUpdate(() => {
54+
rerenderCounter.value += 1
55+
})
56+
</script>
57+
58+
<template>
59+
<div :class="ui.wrapper">
60+
<div :class="ui.header">
61+
<button
62+
v-for="(tab, index) in tabs"
63+
:key="index"
64+
tabindex="-1"
65+
:class="[ui.tab.base, selectedIndex === index ? ui.tab.active : ui.tab.inactive]"
66+
@click="selectedIndex = index"
67+
>
68+
<ProseCodeIcon :icon="tab.icon" :filename="tab.label" :class="ui.tab.icon.base" />
69+
<span>{{ tab.label }}</span>
70+
</button>
71+
</div>
72+
73+
<component :is="selectedTab?.component" :key="selectedIndex" hide-header />
74+
</div>
75+
</template>
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<script lang="ts">
2+
export default {
3+
inheritAttrs: false,
4+
}
5+
</script>
6+
7+
<script lang="ts" setup>
8+
const props = withDefaults(defineProps<{
9+
alt: string
10+
src: string
11+
lazy?: boolean | 'false' | 'true'
12+
width?: number
13+
figureClass?: string
14+
}>(), {
15+
lazy: true,
16+
})
17+
18+
const loadingType = computed(() => {
19+
return props.lazy ? 'lazy' : 'eager'
20+
})
21+
</script>
22+
23+
<template>
24+
<figure class="figure-image flex flex-col items-center justify-center mx-auto max-w-full my-2">
25+
<img
26+
v-bind="$attrs"
27+
height="700"
28+
:alt="alt"
29+
:width="width"
30+
:src="src"
31+
:loading="loadingType"
32+
>
33+
<figcaption v-if="alt" class="text-center">
34+
{{ alt }}
35+
</figcaption>
36+
</figure>
37+
</template>
38+
39+
<style>
40+
@media(max-width: 1024px) {
41+
.figure-image {
42+
transform: translateX(0) !important;
43+
}
44+
}
45+
46+
.figure-image :deep(img:not([src$=".svg"])) {
47+
width: auto;
48+
border-radius: 0.5em;
49+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 1px 10px 0 rgba(0, 0, 0, 0.05);
50+
max-height: min(65vh, 700px);
51+
margin: 0 auto;
52+
}
53+
</style>

docs/components/content/ProseCode.vue

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script setup lang="ts">
2+
import ProseCode from '@nuxtjs/mdc/runtime/components/prose/ProseCode.vue'
3+
</script>
4+
5+
<template>
6+
<ProseCode v-bind="$attrs">
7+
<slot />
8+
</ProseCode>
9+
</template>
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<script setup lang="ts">
2+
import type { PropType } from 'vue'
3+
4+
defineOptions({
5+
inheritAttrs: false,
6+
})
7+
8+
defineProps({
9+
class: {
10+
type: [String, Object, Array] as PropType<any>,
11+
default: undefined,
12+
},
13+
})
14+
15+
const ui = {
16+
wrapper: 'relative [&>div:last-child]:!my-0 [&>div:last-child]:!static my-5',
17+
header: 'flex items-center gap-1 border border-gray-200 dark:border-gray-700 border-b-0 rounded-t-md overflow-hidden p-2',
18+
tab: {
19+
base: 'px-2 py-1.5 focus:outline-none text-gray-700 dark:text-gray-200 text-sm rounded-md flex items-center gap-1.5',
20+
active: 'bg-gray-100 dark:bg-gray-800',
21+
inactive: 'hover:bg-gray-50 dark:hover:bg-gray-800/50',
22+
icon: {
23+
base: '',
24+
},
25+
},
26+
}
27+
28+
const slots = useSlots()
29+
30+
const rerenderCounter = ref(1)
31+
32+
function transformSlot(slot: any, index: number) {
33+
if (typeof slot.type === 'symbol') {
34+
return slot.children?.map(transformSlot)
35+
}
36+
37+
return {
38+
label: slot.props?.filename || slot.props?.label || `${index}`,
39+
icon: slot.props?.icon,
40+
component: slot,
41+
}
42+
}
43+
44+
// Computed
45+
46+
const tabs = computed(() => slots.default?.()?.flatMap(transformSlot).filter(Boolean) || [])
47+
48+
const { selectedFramework } = useFrameworkSelector()
49+
const selectedIndex = ref(tabs.value.findIndex(tab => tab.label.toLowerCase() === selectedFramework.value.slug))
50+
defineExpose({ selectedIndex })
51+
52+
const selectedTab = computed(() => tabs.value.find((_, index) => index === selectedIndex.value))
53+
54+
onBeforeUpdate(() => {
55+
rerenderCounter.value += 1
56+
})
57+
</script>
58+
59+
<template>
60+
<div :class="ui.wrapper" class="relative group [&>.group]:rounded-t-none [&>pre]:my-0 my-5">
61+
<div :class="ui.header">
62+
<button
63+
v-for="(tab, index) in tabs"
64+
:key="index"
65+
tabindex="-1"
66+
:class="[ui.tab.base, selectedIndex === index ? ui.tab.active : ui.tab.inactive]"
67+
@click="selectedIndex = index"
68+
>
69+
<ProseCodeIcon :icon="tab.icon" :filename="tab.label" :class="ui.tab.icon.base" />
70+
<span>{{ tab.label }}</span>
71+
</button>
72+
</div>
73+
74+
<div class="group text-sm/6 border border-[var(--ui-border-muted)] bg-[var(--ui-bg)] rounded-[calc(var(--ui-radius)*1.5)] px-4 py-3 overflow-x-auto focus:outline-none">
75+
<component :is="selectedTab?.component" :key="selectedIndex" hide-header />
76+
</div>
77+
</div>
78+
</template>

docs/content.config.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { defineCollection, defineContentConfig } from '@nuxt/content'
2+
import { asSeoCollection } from '@nuxtjs/seo/content'
3+
import { resolve } from 'pathe'
4+
5+
export default defineContentConfig({
6+
collections: {
7+
docs: defineCollection(asSeoCollection({
8+
type: 'page', // partial
9+
source: {
10+
include: '**/*.md',
11+
cwd: resolve('./content/docs'),
12+
prefix: `/docs`,
13+
},
14+
})),
15+
scripts: defineCollection(asSeoCollection({
16+
type: 'page', // partial
17+
source: {
18+
include: '**/*.md',
19+
cwd: resolve('./content/scripts'),
20+
prefix: `/scripts`,
21+
},
22+
})),
23+
snippets: defineCollection({
24+
type: 'page', // partial
25+
source: {
26+
include: '**/*.md',
27+
cwd: resolve('./content/snippets'),
28+
},
29+
}),
30+
},
31+
})
File renamed without changes.
File renamed without changes.
File renamed without changes.

docs/layouts/docs.vue

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
<script setup lang="ts">
2-
import type { NavItem } from '@nuxt/content/dist/runtime/types'
2+
import type { NavItem } from '@nuxt/content'
33
44
const navigation = inject<Ref<NavItem[]>>('navigation')
5+
const scriptsNavigation = inject<Ref<NavItem[]>>('scripts-navigation')
56
</script>
67

78
<template>
89
<UContainer>
910
<UPage>
1011
<template #left>
1112
<UAside>
12-
<UNavigationTree :links="mapContentNavigation(navigation)" />
13+
<UNavigationTree :links="mapContentNavigation(($route.path.startsWith('/docs') ? navigation : scriptsNavigation) || [])" />
1314
</UAside>
1415
</template>
1516

0 commit comments

Comments
 (0)