docs: migrate to @nuxthq/ui-kit (#405)

Co-authored-by: Sébastien Chopin <seb@nuxt.com>
This commit is contained in:
Benjamin Canac
2023-07-17 14:49:50 +02:00
committed by GitHub
parent 2ec28e7cbd
commit 31d571abb5
71 changed files with 2155 additions and 2638 deletions

View File

@@ -0,0 +1,23 @@
<template>
<footer class="flex items-center gap-1.5 mt-12">
<div class="flex-1 flex items-baseline gap-1.5 text-sm text-gray-600 dark:text-gray-300 leading-6">
Made by
<NuxtLink to="https://nuxtlabs.com" aria-label="NuxtLabs">
<LogoLabs class="text-gray-900 dark:text-white w-14 h-auto" />
</NuxtLink>
</div>
<NuxtLink :to="`https://github.com/nuxtlabs/ui/releases/tag/v${config.version}`" target="_blank" class="inline-flex">
<UBadge :label="`v${config.version}`" />
</NuxtLink>
<div class="flex-1 flex items-center justify-end gap-1.5 -my-1 lg:hidden">
<USocialButton to="https://twitter.com/nuxtlabs" icon="i-simple-icons-twitter" />
<USocialButton to="https://github.com/nuxtlabs/ui" icon="i-simple-icons-github" />
</div>
</footer>
</template>
<script setup lang="ts">
const config = useRuntimeConfig().public
</script>

View File

@@ -1,32 +0,0 @@
<template>
<header class="sticky top-0 z-50 w-full backdrop-blur flex-none border-b border-gray-200 dark:border-gray-800 bg-white/75 dark:bg-gray-900/75">
<UContainer>
<HeaderLinks v-model="isDialogOpen" :links="links" />
</UContainer>
<TransitionRoot :show="isDialogOpen" as="template">
<Dialog as="div" @close="isDialogOpen = false">
<DialogPanel class="fixed inset-0 z-50 overflow-y-auto bg-white dark:bg-gray-900 lg:hidden">
<div class="px-4 sm:px-6 sticky top-0 border-b border-gray-200 dark:border-gray-800 bg-white/75 dark:bg-gray-900/75 backdrop-blur z-10">
<HeaderLinks v-model="isDialogOpen" :links="links" />
</div>
<div class="px-4 sm:px-6 py-4 sm:py-6">
<DocsAsideLinks @click="isDialogOpen = false" />
</div>
</DialogPanel>
</Dialog>
</TransitionRoot>
</header>
</template>
<script setup lang="ts">
import { Dialog, DialogPanel, TransitionRoot } from '@headlessui/vue'
const isDialogOpen = ref(false)
const links = [
{ label: 'Documentation', to: '/getting-started' },
{ label: 'Components', to: '/elements/avatar' },
{ label: 'Examples', to: '/examples' }
]
</script>

View File

@@ -1,66 +0,0 @@
<template>
<div class="flex items-center justify-between gap-3 h-16">
<div class="flex items-center gap-6">
<div class="flex items-center gap-3">
<NuxtLink to="/getting-started" class="flex items-end gap-1.5 font-bold text-xl text-gray-900 dark:text-white">
<Logo class="w-8 h-8 text-primary-500 dark:text-primary-400" />
<span class="hidden sm:block">NuxtLabs</span><span class="sm:text-primary-500 dark:sm:text-primary-400">UI</span>
</NuxtLink>
</div>
</div>
<div class="flex items-center justify-end flex-1 -mr-1.5 gap-3">
<DocsSearchButton class="ml-1.5 flex-1 lg:flex-none lg:w-48" />
<div class="flex items-center lg:gap-1.5">
<UPopover>
<template #default="{ open }">
<UButton color="gray" variant="ghost" square :class="[open && 'bg-gray-50 dark:bg-gray-800']">
<UIcon name="i-heroicons-swatch-20-solid" class="w-5 h-5 text-primary-500 dark:text-primary-400" />
</UButton>
</template>
<template #panel>
<ColorPicker />
</template>
</UPopover>
<ColorModeButton />
<UButton
to="https://twitter.com/nuxtlabs"
target="_blank"
color="gray"
variant="ghost"
class="hidden lg:inline-flex"
icon="i-simple-icons-twitter"
/>
<UButton
to="https://github.com/nuxtlabs/ui"
target="_blank"
color="gray"
variant="ghost"
class="hidden lg:inline-flex"
icon="i-simple-icons-github"
/>
<UButton
color="gray"
variant="ghost"
class="lg:hidden"
:icon="isDialogOpen ? 'i-heroicons-x-mark-20-solid' : 'i-heroicons-bars-3-20-solid'"
@click="isDialogOpen = !isDialogOpen"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{ modelValue: boolean, links: { to: string, label: string }[] }>()
const emit = defineEmits(['update:modelValue'])
const isDialogOpen = useVModel(props, 'modelValue', emit)
</script>

View File

@@ -1,15 +1,25 @@
<template>
<div class="p-2">
<div class="grid grid-cols-5 gap-px">
<ColorPickerButton v-for="color in primaryColors" :key="color.value" :color="color" :selected="primary" @select="primary = color" />
</div>
<UPopover>
<template #default="{ open }">
<UButton color="gray" variant="ghost" square :class="[open && 'bg-gray-50 dark:bg-gray-800']">
<UIcon name="i-heroicons-swatch-20-solid" class="w-5 h-5 text-primary-500 dark:text-primary-400" />
</UButton>
</template>
<hr class="border-gray-200 dark:border-gray-800 my-2">
<template #panel>
<div class="p-2">
<div class="grid grid-cols-5 gap-px">
<ColorPickerPill v-for="color in primaryColors" :key="color.value" :color="color" :selected="primary" @select="primary = color" />
</div>
<div class="grid grid-cols-5 gap-px">
<ColorPickerButton v-for="color in grayColors" :key="color.value" :color="color" :selected="gray" @select="gray = color" />
</div>
</div>
<hr class="border-gray-200 dark:border-gray-800 my-2">
<div class="grid grid-cols-5 gap-px">
<ColorPickerPill v-for="color in grayColors" :key="color.value" :color="color" :selected="gray" @select="gray = color" />
</div>
</div>
</template>
</UPopover>
</template>
<script setup lang="ts">

View File

@@ -1,33 +0,0 @@
<template>
<component
:is="to ? NuxtLink : 'div'"
:to="to"
class="block pl-4 pr-6 py-3 rounded-md !border !border-gray-200 dark:!border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-sm leading-6 my-5 last:mb-0 font-normal group relative prose-code:bg-white dark:prose-code:bg-gray-900"
:class="[to ? 'hover:!border-primary-500 dark:hover:!border-primary-400 hover:text-primary-500 dark:hover:text-primary-400 border-dashed hover:text-gray-800 dark:hover:text-gray-200' : '']"
>
<UIcon v-if="!!to" name="i-heroicons-link-20-solid" class="w-3 h-3 absolute right-2 top-2 text-gray-400 dark:text-gray-500 group-hover:text-primary-500 dark:group-hover:text-primary-400" />
<UIcon v-if="icon" :name="icon" class="w-4 h-4 mr-2 inline-flex items-center align-text-top" :class="color" />
<ContentSlot :use="$slots.default" unwrap="p" />
</component>
</template>
<script setup lang="ts">
const NuxtLink = resolveComponent('NuxtLink')
defineProps({
icon: {
type: String,
default: null
},
color: {
type: String,
default: 'text-primary-500 dark:text-primary-400'
},
to: {
type: String,
default: null
}
})
</script>

View File

@@ -1,18 +0,0 @@
<template>
<UKbd class="!my-0 align-text-top">
{{ shortcut }}
</UKbd>
</template>
<script setup lang="ts">
const props = defineProps({
value: {
type: String,
required: true
}
})
const { metaSymbol } = useShortcuts()
const shortcut = computed(() => props.value === 'meta' ? metaSymbol.value : props.value)
</script>

View File

@@ -1,53 +0,0 @@
<template>
<div :selected-index="selectedIndex" @change="changeTab">
<div class="flex border border-gray-200 dark:border-gray-700 border-b-0 rounded-t-md overflow-hidden -mb-px">
<div
v-for="(tab, index) in tabs"
:key="index"
as="template"
@click="selectedIndex = index"
>
<button
class="px-4 py-2 focus:outline-none text-sm border-r border-r-gray-200 dark:border-r-gray-700 transition-colors"
tabindex="-1"
:class="[selectedIndex === index ? 'font-medium text-primary-500 dark:text-primary-400 bg-gray-50 dark:bg-gray-800' : 'hover:bg-gray-50 dark:hover:bg-gray-800']"
>
{{ tab.label }}
</button>
</div>
</div>
<div class="[&>div>pre]:!rounded-t-none">
<component :is="selectedTab.component" />
</div>
</div>
</template>
<script setup lang="ts">
const slots = useSlots()
const selectedIndex = ref(0)
// Computed
const tabs = computed(() => slots.default?.().map((slot, index) => {
return {
label: slot.props?.filename || slot.props?.label || `${index}`,
component: slot
}
}) || [])
const selectedTab = computed(() => tabs.value.find((_, index) => index === selectedIndex.value))
// Methods
function changeTab (index) {
selectedIndex.value = index
}
</script>
<script lang="ts">
export default {
inheritAttrs: false
}
</script>

View File

@@ -1,71 +0,0 @@
<script lang="ts">
import { defineComponent } from '#imports'
export default defineComponent({
props: {
code: {
type: String,
default: ''
},
language: {
type: String,
default: null
},
filename: {
type: String,
default: null
},
highlights: {
type: Array as () => number[],
default: () => []
},
meta: {
type: String,
default: null
}
},
setup (props) {
const clipboard = useCopyToClipboard({ timeout: 2000 })
const icon = ref('i-heroicons-clipboard-document')
function copy () {
clipboard.copy(props.code, { title: 'Copied to clipboard!' })
icon.value = 'i-heroicons-clipboard-document-check'
setTimeout(() => {
icon.value = 'i-heroicons-clipboard-document'
}, 2000)
}
return {
icon,
copy
}
}
})
</script>
<template>
<div class="group relative" :class="`language-${language}`">
<UButton
:icon="icon"
variant="solid"
class="absolute right-3 top-3 opacity-0 group-hover:opacity-100 transition-opacity z-[1]"
size="xs"
tabindex="-1"
@click="copy"
/>
<span v-if="filename" class="text-gray-400 dark:text-gray-500 absolute right-3 bottom-3 text-sm group-hover:opacity-0 transition-opacity">{{ filename }}</span>
<slot />
</div>
</template>
<style>
pre code .line {
display: block;
min-height: 1rem;
}
</style>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
defineProps<{ id: string }>()
</script>
<template>
<h2 :id="id" class="scroll-mt-[161px] lg:scroll-mt-[112px]">
<NuxtLink :href="`#${id}`" class="group">
<div class="-ml-6 pr-2 py-2 inline-flex opacity-0 group-hover:opacity-100 transition-opacity absolute">
<UIcon name="i-heroicons-hashtag-20-solid" class="w-4 h-4 text-primary-500 dark:text-primary-400" />
</div>
<slot />
</NuxtLink>
</h2>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
defineProps<{ id: string }>()
</script>
<template>
<h3 :id="id" class="scroll-mt-[145px] lg:scroll-mt-[96px]">
<NuxtLink :href="`#${id}`" class="group">
<div class="-ml-6 pr-2 py-2 inline-flex opacity-0 group-hover:opacity-100 transition-opacity absolute">
<UIcon name="i-heroicons-hashtag-20-solid" class="w-4 h-4 text-primary-500 dark:text-primary-400" />
</div>
<slot />
</NuxtLink>
</h3>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
defineProps<{ id: string }>()
</script>
<template>
<h3 :id="id" class="scroll-mt-[145px] lg:scroll-mt-[96px]">
<NuxtLink :href="`#${id}`" class="group">
<div class="-ml-6 pr-2 py-2 inline-flex opacity-0 group-hover:opacity-100 transition-opacity absolute">
<UIcon name="i-heroicons-hashtag-20-solid" class="w-4 h-4 text-primary-500 dark:text-primary-400" />
</div>
<slot />
</NuxtLink>
</h3>
</template>

View File

@@ -1,15 +0,0 @@
<template>
<aside class="hidden py-8 overflow-y-auto lg:block lg:self-start lg:top-16 lg:max-h-[calc(100vh-65px)] lg:sticky lg:pr-8 lg:pl-[2px]">
<div class="relative">
<!-- <div class="sticky top-0 pointer-events-none z-[1]">
<div class="h-8 bg-white dark:bg-gray-900" />
<div class="bg-white dark:bg-gray-900 relative pointer-events-auto">
<DocsSearchButton class="w-full" />
</div>
<div class="h-8 bg-gradient-to-b from-white dark:from-gray-900" />
</div> -->
<DocsAsideLinks />
</div>
</aside>
</template>

View File

@@ -1,40 +0,0 @@
<template>
<div class="space-y-8">
<div v-for="(group, index) in navigation" :key="index" class="space-y-3">
<p class="text-sm font-semibold text-gray-900 dark:text-gray-200 truncate leading-6">
{{ group.title }}
</p>
<UVerticalNavigation
:links="mapContentLinks(group.children)"
class="mt-1"
:ui="{
wrapper: 'border-l border-gray-200 dark:border-gray-800 space-y-2',
base: 'group block border-l -ml-px lg:leading-6 flex items-center gap-2',
padding: 'pl-4',
rounded: '',
font: '',
ring: '',
active: 'text-primary-500 dark:text-primary-400 border-current',
inactive: 'border-transparent hover:border-gray-400 dark:hover:border-gray-500 text-gray-700 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300'
}"
>
<template #badge="{ link }">
<UBadge v-if="link.badge" size="xs" :ui="{ rounded: 'rounded-full' }">
{{ link.badge }}
</UBadge>
</template>
</UVerticalNavigation>
</div>
</div>
</template>
<script setup lang="ts">
import type { NavItem } from '@nuxt/content/dist/runtime/types'
const navigation: Ref<NavItem[]> = inject('navigation')
function mapContentLinks (links: NavItem[]) {
return links?.map(link => ({ label: link.title, icon: link.icon, to: link._path, badge: link.badge })) || []
}
</script>

View File

@@ -1,40 +0,0 @@
<template>
<footer class="flex items-center justify-between gap-1.5">
<div class="flex items-baseline gap-1.5 text-sm text-center text-gray-500 dark:text-gray-400">
Made by
<NuxtLink to="https://nuxtlabs.com" aria-label="NuxtLabs">
<LogoLabs class="text-primary-500 w-14 h-auto dark:text-primary-400" />
</NuxtLink>
</div>
<div class="flex items-center gap-3 -my-1">
<div class="flex lg:hidden items-center gap-1.5">
<UButton
to="https://twitter.com/nuxtlabs"
target="_blank"
color="gray"
size="2xs"
variant="ghost"
icon="i-simple-icons-twitter"
/>
<UButton
to="https://github.com/nuxtlabs/ui"
target="_blank"
color="gray"
size="2xs"
variant="ghost"
icon="i-simple-icons-github"
/>
</div>
<NuxtLink :to="`https://github.com/nuxtlabs/ui/releases/tag/v${config.version}`" target="_blank">
<UBadge :label="`v${config.version}`" />
</NuxtLink>
</div>
</footer>
</template>
<script setup lang="ts">
const config = useRuntimeConfig().public
</script>

View File

@@ -1,22 +0,0 @@
<template>
<div class="flex items-center justify-between gap-1.5">
<UButton
v-if="page"
:to="`https://github.com/nuxtlabs/ui/edit/dev/docs/content/${page._file}`"
label="Edit this page on GitHub"
color="primary"
variant="link"
:padded="false"
icon="i-heroicons-pencil-square"
/>
</div>
</template>
<script setup lang="ts">
defineProps({
page: {
type: Object,
default: null
}
})
</script>

View File

@@ -1,42 +0,0 @@
<template>
<header v-if="page" class="relative border-b border-gray-200 dark:border-gray-800 pb-8 mb-12">
<p class="mb-4 text-sm leading-6 font-semibold text-primary-500 dark:text-primary-400 capitalize">
{{ page._dir?.title ? page._dir.title : useLowerCase(page._dir) }}
</p>
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between">
<h1 class="text-3xl sm:text-4xl font-extrabold text-gray-900 tracking-tight dark:text-white">
{{ page.title }}
</h1>
<div v-if="page.headlessui || page.github" class="flex items-center gap-2 mt-4 lg:mt-0">
<UButton
v-if="page.headlessui"
:label="page.headlessui.label"
:to="page.headlessui.to"
icon="i-simple-icons-headlessui"
color="white"
/>
<UButton
v-if="page.github"
label="GitHub"
icon="i-simple-icons-github"
color="white"
:to="`https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/${page._dir}/${page.title.replace(' ', '')}${page.github.suffix || '.vue'}`"
/>
</div>
</div>
<p v-if="page.description" class="mt-4 text-lg text-gray-500 dark:text-gray-400">
{{ page.description }}
</p>
</header>
</template>
<script setup lang="ts">
defineProps({
page: {
type: Object,
default: null
}
})
</script>

View File

@@ -1,33 +0,0 @@
<template>
<div class="grid gap-6 sm:grid-cols-2">
<DocsPrevNextCard
v-if="prev"
:title="prev.title"
:description="prev.description"
:to="prev._path"
icon="i-heroicons-arrow-left-20-solid"
/>
<span v-else class="hidden sm:block">&nbsp;</span>
<DocsPrevNextCard
v-if="next"
:title="next.title"
:description="next.description"
:to="next._path"
icon="i-heroicons-arrow-right-20-solid"
class="text-right"
/>
</div>
</template>
<script setup lang="ts">
defineProps({
prev: {
type: Object,
default: null
},
next: {
type: Object,
default: null
}
})
</script>

View File

@@ -1,36 +0,0 @@
<template>
<NuxtLink :to="to" class="block px-5 py-8 border not-prose rounded-lg border-gray-200 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800/50 group">
<div v-if="icon" class="inline-flex items-center rounded-full p-1.5 bg-gray-50 dark:bg-gray-800 group-hover:bg-primary-50 dark:group-hover:bg-primary-400/10 ring-1 ring-gray-300 dark:ring-gray-700 mb-4 group-hover:ring-primary-500/50 dark:group-hover:ring-primary-400/50">
<UIcon :name="icon" class="w-5 h-5 text-gray-900 dark:text-gray-100 group-hover:text-primary-500 dark:group-hover:text-primary-400" />
</div>
<p class="text-gray-900 dark:text-gray-50 font-medium text-[15px] mb-1">
{{ title }}
</p>
<p class="text-sm font-normal text-gray-500 dark:text-gray-400">
{{ description }}
</p>
</NuxtLink>
</template>
<script setup lang="ts">
defineProps({
icon: {
type: String,
default: null
},
title: {
type: String,
default: ''
},
description: {
type: String,
default: ''
},
to: {
type: String,
required: true
}
})
</script>

View File

@@ -1,181 +0,0 @@
<template>
<UModal
v-model="isSearchModalOpen"
:overlay="!isXs"
:transition="!isXs"
:ui="{
padding: 'sm:p-4',
rounded: 'sm:rounded-lg',
width: 'sm:max-w-3xl',
height: 'h-screen sm:h-[28rem]'
}"
>
<UCommandPalette
ref="commandPaletteRef"
:groups="groups"
command-attribute="title"
:close-button="{ icon: 'i-heroicons-x-mark-20-solid', color: 'gray', variant: 'ghost', size: 'sm', class: '-mr-1.5' }"
:ui="{ input: { height: 'h-16 sm:h-12', icon: { size: 'h-5 w-5', padding: 'pl-11' } } }"
:fuse="{
fuseOptions: { ignoreLocation: true, includeMatches: true, threshold: 0, keys: ['title', 'description', 'children.children.value', 'children.children.children.value'] },
resultLimit: 10
}"
@update:model-value="onSelect"
@close="isSearchModalOpen = false"
/>
</UModal>
</template>
<script setup lang="ts">
import type { NavItem } from '@nuxt/content/dist/runtime/types'
import type { Command } from '../../../src/runtime/types'
const navigation: Ref<NavItem[]> = inject('navigation')
const router = useRouter()
const { usingInput } = useShortcuts()
const { isSearchModalOpen } = useDocs()
const breakpoints = useBreakpoints({ mobile: 640 })
const isXs = breakpoints.smaller('mobile')
const commandPaletteRef = ref<HTMLElement & { query: Ref<string>, results: { item: Command }[] }>()
const { data: files } = await useLazyAsyncData('search', () => queryContent().where({ _type: 'markdown' }).find(), { default: () => [] })
// Computed
const defaultGroups = computed(() => navigation.value.map(item => ({
key: item._path,
label: item.title,
commands: files.value.filter(file => file._path.startsWith(item._path)).map(file => ({
id: file._id,
title: file.navigation?.title || file.title,
to: file._path,
suffix: file.description,
icon: file.icon
}))
})))
const queryGroups = computed(() => navigation.value.map(item => ({
key: item._path,
label: item.title,
commands: files.value.filter(file => file._path.startsWith(item._path)).flatMap((file) => {
return [{
id: file._id,
title: file.navigation?.title || file.title,
to: file._path,
description: file.description,
icon: file.icon
},
// @ts-ignore
...Object.entries(groupByHeading(file.body.children)).map(([hash, { title, children }]) => ({
id: `${file._path}${hash}`,
title,
prefix: `${file.navigation?.title || file.title} ->`,
prefixClass: 'text-gray-700 dark:text-gray-200',
to: `${file._path}${hash}`,
children: concatChildren(children),
icon: file.icon
}))]
})
})))
const groups = computed(() => commandPaletteRef.value?.query ? queryGroups.value : defaultGroups.value)
// avoid conflicts between multiple meta_k shortcuts
const canToggleModal = computed(() => isSearchModalOpen.value || !usingInput.value)
// Methods
function remapChildren (children: any[]) {
return children?.map((grandChild) => {
if (['code-inline', 'em', 'a', 'strong'].includes(grandChild.tag)) {
return { type: 'text', value: grandChild.children.find(child => child.type === 'text')?.value || '' }
}
return grandChild
})
}
function concatChildren (children: any[]) {
return children.map((child) => {
if (['alert'].includes(child.tag)) {
child.children = concatChildren(child.children)
}
if (child.tag === 'p') {
child.children = remapChildren(child.children)
child.children = child.children?.reduce((acc, grandChild) => {
if (grandChild.type === 'text') {
if (acc.length && acc[acc.length - 1].type === 'text') {
acc[acc.length - 1].value += grandChild.value
} else {
acc.push(grandChild)
}
} else {
acc.push(grandChild)
}
return acc
}, [])
}
if (['style'].includes(child.tag)) {
return null
}
return child
})
}
function groupByHeading (children: any[]) {
const groups = {} // grouped by path
let hash = '' // file.page with potential `#anchor` concat
let title: string | null
for (const node of children) {
// if heading found, udpate current path
if (['h2', 'h3'].includes(node.tag)) {
// find heading text value
title = node.children?.find(child => child.type === 'text')?.value
if (title) {
hash = `#${node.props.id}`
}
}
// push to existing/new group based on path
if (groups[hash]) {
groups[hash].children.push(node)
} else {
groups[hash] = { children: [node], title }
}
}
return groups
}
function onSelect (option) {
isSearchModalOpen.value = false
if (option.click) {
option.click()
} else if (option.to) {
router.push(option.to)
} else if (option.href) {
window.open(option.href, '_blank')
}
}
// Shortcuts
defineShortcuts({
meta_k: {
usingInput: true,
whenever: [canToggleModal],
handler: () => {
isSearchModalOpen.value = !isSearchModalOpen.value
}
},
escape: {
usingInput: true,
whenever: [isSearchModalOpen],
handler: () => { isSearchModalOpen.value = false }
}
})
</script>

View File

@@ -1,33 +0,0 @@
<template>
<UButton
color="white"
variant="outline"
icon="i-heroicons-magnifying-glass-20-solid"
label="Search..."
truncate
:ui="{
color: {
white: {
outline: 'ring-1 ring-inset ring-gray-200 dark:ring-gray-800 hover:ring-gray-300 dark:hover:ring-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800/50 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 focus-visible:ring-2 focus-visible:ring-primary-500 dark:focus-visible:ring-primary-400'
}
}
}"
@click="isSearchModalOpen = true"
>
<template #trailing>
<div class="hidden lg:flex items-center gap-0.5 ml-auto -my-1 flex-shrink-0">
<UKbd class="!text-inherit">
{{ metaSymbol }}
</UKbd>
<UKbd class="!text-inherit">
K
</UKbd>
</div>
</template>
</UButton>
</template>
<script setup lang="ts">
const { isSearchModalOpen } = useDocs()
const { metaSymbol } = useShortcuts()
</script>

View File

@@ -1,24 +0,0 @@
<template>
<div class="sticky top-16 bg-white/75 dark:bg-gray-900/75 backdrop-blur group lg:self-start -mx-4 sm:-mx-6 lg:mx-0 px-4 sm:px-6 lg:pl-8 lg:pr-0">
<div class="py-3 lg:py-8 border-b border-dashed border-gray-200 dark:border-gray-800 lg:border-0">
<button class="flex items-center gap-2" tabindex="-1" @click="isTocOpen = !isTocOpen">
<span class="text-sm text-slate-900 font-semibold text-sm leading-6 dark:text-slate-100 truncate">Table of Contents</span>
<UIcon name="i-heroicons-chevron-right-20-solid" class="lg:hidden w-4 h-4 transition-transform duration-100 transform text-gray-400 dark:text-gray-500" :class="[isTocOpen ? 'rotate-90' : 'rotate-0']" />
</button>
<DocsTocLinks class="mt-2 lg:mt-4" :links="toc.links" :class="[isTocOpen ? 'lg:block' : 'hidden lg:block']" />
</div>
</div>
</template>
<script setup lang="ts">
defineProps({
toc: {
type: Object,
default: null
}
})
const isTocOpen = ref(false)
</script>

View File

@@ -1,49 +0,0 @@
<template>
<ul>
<li v-for="link in links" :key="link.text" :class="{ 'ml-3': link.depth === 3 }">
<a
:href="`#${link.id}`"
class="block py-1 font-medium text-sm"
:class="[activeHeadings.includes(link.id) ? 'text-primary-500 dark:text-primary-400' : 'hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300']"
@click.prevent="scrollToHeading(link.id)"
>
{{ link.text }}
</a>
<DocsTocLinks v-if="link.children" :links="link.children" />
</li>
</ul>
</template>
<script setup lang="ts">
import type { TocLink } from '@nuxt/content/dist/runtime/types'
defineProps({
links: {
type: Array as PropType<TocLink[]>,
default: () => []
}
})
const emit = defineEmits(['move'])
const route = useRoute()
const router = useRouter()
const { activeHeadings, updateHeadings } = useScrollspy()
watch(() => route.path, () => {
setTimeout(() => {
if (process.client) {
updateHeadings([
...document.querySelectorAll('h2'),
...document.querySelectorAll('h3')
])
}
}, 300)
}, { immediate: true })
const scrollToHeading = (id: string) => {
router.push(`#${id}`)
emit('move', id)
}
</script>