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

1
.npmrc
View File

@@ -1 +1,2 @@
shamefully-hoist=true shamefully-hoist=true
auto-install-peers=true

View File

@@ -1,17 +1,41 @@
<template> <template>
<div> <div>
<Header /> <UHeader>
<template #left>
<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" />
<UContainer class="grid lg:grid-cols-10 lg:gap-8"> <span class="hidden sm:block">NuxtLabs</span><span class="sm:text-primary-500 dark:sm:text-primary-400">UI</span>
<DocsAside class="lg:col-span-2" /> </NuxtLink>
</template>
<div class="lg:col-span-8 min-h-0 flex flex-col"> <template #center>
<UDocsSearchButton class="ml-1.5 flg:w-64 xl:w-96" />
</template>
<template #right>
<ColorPicker />
<UColorModeButton />
<USocialButton to="https://twitter.com/nuxtlabs" icon="i-simple-icons-twitter" class="hidden lg:inline-flex" />
<USocialButton to="https://github.com/nuxtlabs/ui" icon="i-simple-icons-github" class="hidden lg:inline-flex" />
</template>
<template #links>
<UDocsAsideAnchors :links="anchors" />
<UDocsAsideLinks :links="navigation" />
</template>
</UHeader>
<UContainer>
<UDocsLayout :links="navigation" :anchors="anchors">
<NuxtPage /> <NuxtPage />
</div> </UDocsLayout>
</UContainer> </UContainer>
<ClientOnly> <ClientOnly>
<DocsSearch /> <UDocsSearch :files="files" :links="navigation" />
</ClientOnly> </ClientOnly>
<UNotifications /> <UNotifications />
@@ -22,9 +46,24 @@
const colorMode = useColorMode() const colorMode = useColorMode()
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation()) const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation())
const { data: files } = await useLazyAsyncData('files', () => queryContent().where({ _type: 'markdown', navigation: { $ne: false } }).find(), { default: () => [] })
provide('navigation', navigation) provide('navigation', navigation)
const anchors = [{
label: 'Documentation',
icon: 'i-heroicons-book-open-solid',
to: '/getting-started'
}, {
label: 'Playground',
icon: 'i-simple-icons-stackblitz',
to: 'https://stackblitz.com/edit/nuxtlabs-ui?file=app.config.ts,app.vue'
}, {
label: 'Releases',
icon: 'i-heroicons-rocket-launch-solid',
to: 'https://github.com/nuxtlabs/ui/releases'
}]
// Computed // Computed
const color = computed(() => colorMode.value === 'dark' ? '#18181b' : 'white') const color = computed(() => colorMode.value === 'dark' ? '#18181b' : 'white')
@@ -38,7 +77,6 @@ useHead({
{ key: 'theme-color', name: 'theme-color', content: color } { key: 'theme-color', name: 'theme-color', content: color }
], ],
link: [ link: [
{ rel: 'stylesheet', href: 'https://rsms.me/inter/inter.css' },
{ rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' } { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }
], ],
htmlAttrs: { htmlAttrs: {

View File

@@ -1,48 +0,0 @@
import type { RouterConfig } from '@nuxt/schema'
function findHashPosition (hash): { el: any, behavior: ScrollBehavior, top: number } {
const el = document.querySelector(hash)
// vue-router does not incorporate scroll-margin-top on its own.
if (el) {
const top = parseFloat(getComputedStyle(el).scrollMarginTop)
return {
el: hash,
behavior: 'smooth',
top
}
}
}
// https://router.vuejs.org/api/#routeroptions
export default <RouterConfig>{
scrollBehavior (to, from, savedPosition) {
const nuxtApp = useNuxtApp()
// If history back
if (savedPosition) {
// Handle Suspense resolution
return new Promise((resolve) => {
nuxtApp.hooks.hookOnce('page:finish', () => {
setTimeout(() => resolve(savedPosition), 50)
})
})
}
// Scroll to heading on click
if (to.hash) {
return new Promise((resolve) => {
if (to.path === from.path) {
setTimeout(() => resolve(findHashPosition(to.hash)), 50)
} else {
nuxtApp.hooks.hookOnce('page:finish', () => {
setTimeout(() => resolve(findHashPosition(to.hash)), 50)
})
}
})
}
// Scroll to top of window
return { top: 0 }
}
}

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> <template>
<div class="p-2"> <UPopover>
<div class="grid grid-cols-5 gap-px"> <template #default="{ open }">
<ColorPickerButton v-for="color in primaryColors" :key="color.value" :color="color" :selected="primary" @select="primary = color" /> <UButton color="gray" variant="ghost" square :class="[open && 'bg-gray-50 dark:bg-gray-800']">
</div> <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"> <hr class="border-gray-200 dark:border-gray-800 my-2">
<ColorPickerButton v-for="color in grayColors" :key="color.value" :color="color" :selected="gray" @select="gray = color" />
</div> <div class="grid grid-cols-5 gap-px">
</div> <ColorPickerPill v-for="color in grayColors" :key="color.value" :color="color" :selected="gray" @select="gray = color" />
</div>
</div>
</template>
</UPopover>
</template> </template>
<script setup lang="ts"> <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>

View File

@@ -1,11 +0,0 @@
import { createSharedComposable } from '@vueuse/core'
const _useDocs = () => {
const isSearchModalOpen = ref(false)
return {
isSearchModalOpen
}
}
export const useDocs = createSharedComposable(_useDocs)

View File

@@ -1,37 +0,0 @@
/**
* Scrollspy allows you to watch visible headings in a specific page.
* Useful for table of contents live style updates.
*/
export const useScrollspy = () => {
const observer = ref() as Ref<IntersectionObserver>
const visibleHeadings = ref([]) as Ref<string[]>
const activeHeadings = ref([]) as Ref<string[]>
const observerCallback = (entries: IntersectionObserverEntry[]) =>
entries.forEach((entry) => {
const id = entry.target.id
if (entry.isIntersecting) { visibleHeadings.value.push(id) } else { visibleHeadings.value = visibleHeadings.value.filter(t => t !== id) }
})
const updateHeadings = (headings: Element[]) =>
headings.forEach((heading) => {
observer.value.observe(heading)
})
watch(visibleHeadings, (val, oldVal) => {
if (val.length === 0) { activeHeadings.value = oldVal } else { activeHeadings.value = val }
})
// Create intersection observer
onBeforeMount(() => (observer.value = new IntersectionObserver(observerCallback)))
// Destroy it
onBeforeUnmount(() => observer.value?.disconnect())
return {
visibleHeadings,
activeHeadings,
updateHeadings
}
}

View File

@@ -8,7 +8,7 @@ head:
This module has been developed by the [NuxtLabs](https://nuxtlabs.com/) team for [Volta](https://volta.net) and [Nuxt Studio](https://nuxt.studio/), its goal is to provide everything related to UI when building a Nuxt app. This includes components, icons, colors, dark mode but also keyboard shortcuts. This module has been developed by the [NuxtLabs](https://nuxtlabs.com/) team for [Volta](https://volta.net) and [Nuxt Studio](https://nuxt.studio/), its goal is to provide everything related to UI when building a Nuxt app. This includes components, icons, colors, dark mode but also keyboard shortcuts.
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
[Volta](https://volta.net/) entire UI is built with this module alongside the 50+ keyboard shortcuts defined. [Volta](https://volta.net/) entire UI is built with this module alongside the 50+ keyboard shortcuts defined.
:: ::
@@ -31,7 +31,3 @@ This module has been developed by the [NuxtLabs](https://nuxtlabs.com/) team for
- [tailwindlabs/headlessui](https://github.com/tailwindlabs/headlessui) - [tailwindlabs/headlessui](https://github.com/tailwindlabs/headlessui)
- [vueuse/vueuse](https://github.com/vueuse/vueuse) - [vueuse/vueuse](https://github.com/vueuse/vueuse)
- [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons) - [egoist/tailwindcss-icons](https://github.com/egoist/tailwindcss-icons)
::alert{icon="i-heroicons-exclamation-triangle"}
This documentation is still a work in progress and will be updated regularly.
::

View File

@@ -2,6 +2,8 @@
description: 'Learn how to install and configure the module in your Nuxt app.' description: 'Learn how to install and configure the module in your Nuxt app.'
--- ---
## Quick Start
1. Install `@nuxthq/ui` dependency to your project: 1. Install `@nuxthq/ui` dependency to your project:
::code-group ::code-group
@@ -30,14 +32,10 @@ export default defineNuxtConfig({
That's it! You can now use all the components and composables in your Nuxt app ✨ That's it! You can now use all the components and composables in your Nuxt app ✨
::alert{icon="i-heroicons-exclamation-triangle"} ::callout{icon="i-heroicons-exclamation-triangle"}
As this module installs [@nuxtjs/tailwindcss](https://tailwindcss.nuxtjs.org/) and [@nuxtjs/color-mode](https://color-mode.nuxtjs.org/) for you, you should remove them from your `modules` and `dependencies` if you've previously installed them manually. As this module installs [@nuxtjs/tailwindcss](https://tailwindcss.nuxtjs.org/) and [@nuxtjs/color-mode](https://color-mode.nuxtjs.org/) for you, you should remove them from your `modules` and `dependencies` if you've previously installed them manually.
:: ::
## Playground
:u-button{icon="i-simple-icons-stackblitz" label="Play on StackBlitz" size="lg" to="https://stackblitz.com/edit/nuxtlabs-ui?file=app.config.ts,app.vue" target="_blank"}
## IntelliSense ## IntelliSense
If you're using VSCode, you can install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension to get autocompletion for the classes. If you're using VSCode, you can install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension to get autocompletion for the classes.

View File

@@ -19,7 +19,7 @@ export default defineAppConfig({
}) })
``` ```
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
Try to change the `primary` and `gray` colors by clicking on the :u-icon{name="i-heroicons-swatch-20-solid" class="w-4 h-4 align-middle text-primary-500 dark:text-primary-400"} button in the header. Try to change the `primary` and `gray` colors by clicking on the :u-icon{name="i-heroicons-swatch-20-solid" class="w-4 h-4 align-middle text-primary-500 dark:text-primary-400"} button in the header.
:: ::
@@ -29,11 +29,11 @@ To provide dynamic colors that can be changed at runtime, this module uses CSS v
Likewise, you can't define a `primary` color in your `tailwind.config.ts` as it would conflict with the `primary` color defined by the module. Likewise, you can't define a `primary` color in your `tailwind.config.ts` as it would conflict with the `primary` color defined by the module.
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
We'd advise you to use those colors in your components and pages, e.g. `text-primary-500 dark:text-primary-400`, `bg-gray-100 dark:bg-gray-900`, etc. so your app automatically adapts when changing your `app.config.ts`. We'd advise you to use those colors in your components and pages, e.g. `text-primary-500 dark:text-primary-400`, `bg-gray-100 dark:bg-gray-900`, etc. so your app automatically adapts when changing your `app.config.ts`.
:: ::
Components having a `color` prop like [Avatar](/elements/avatar#chip), [Badge](/elements/badge#style), [Button](/elements/button#style), [Input](/elements/input#style) (inherited in [Select](/forms/select) and [SelectMenu](/forms/select-menu)), [Radio](/forms/radio), [Checkbox](/forms/checkbox), [Toggle](/forms/toggle), [Range](/forms/range) and [Notification](/overlays/notification#timeout) will use the `primary` color by default but will handle all the colors defined in your `tailwind.config.ts` or the default Tailwind CSS colors. Components having a `color` prop like [Avatar](/elements/avatar#chip), [Badge](/elements/badge#style), [Button](/elements/button#style), [Input](/forms/input#style) (inherited in [Select](/forms/select) and [SelectMenu](/forms/select-menu)), [Radio](/forms/radio), [Checkbox](/forms/checkbox), [Toggle](/forms/toggle), [Range](/forms/range) and [Notification](/overlays/notification#timeout) will use the `primary` color by default but will handle all the colors defined in your `tailwind.config.ts` or the default Tailwind CSS colors.
Variant classes of those components are defined with a syntax like `bg-{color}-500 dark:bg-{color}-400` so they can be used with any color. However, this means that Tailwind will not find those classes and therefore will not generate the corresponding CSS. Variant classes of those components are defined with a syntax like `bg-{color}-500 dark:bg-{color}-400` so they can be used with any color. However, this means that Tailwind will not find those classes and therefore will not generate the corresponding CSS.
@@ -73,7 +73,7 @@ All the components are styled with dark mode in mind.
Thanks to [Tailwind CSS dark mode](https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually) class strategy and the [@nuxtjs/color-mode](https://github.com/nuxt-modules/color-mode) module, you literally have nothing to do. Thanks to [Tailwind CSS dark mode](https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually) class strategy and the [@nuxtjs/color-mode](https://github.com/nuxt-modules/color-mode) module, you literally have nothing to do.
::alert{icon="i-heroicons-puzzle-piece"} ::callout{icon="i-heroicons-puzzle-piece"}
Learn how to build a color mode button in the [Examples](/getting-started/examples#color-mode-button) page. Learn how to build a color mode button in the [Examples](/getting-started/examples#color-mode-button) page.
:: ::
@@ -87,7 +87,7 @@ export default defineNuxtConfig({
}) })
``` ```
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
If you're stuck in dark mode even after changing this setting, you might need to remove the `nuxt-color-mode` entry from your browser's local storage. If you're stuck in dark mode even after changing this setting, you might need to remove the `nuxt-color-mode` entry from your browser's local storage.
:: ::
@@ -115,7 +115,7 @@ Each component has a `ui` prop that allows you to customize everything specifica
</template> </template>
``` ```
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
You can find the default classes for each component under the `Preset` section. You can find the default classes for each component under the `Preset` section.
:: ::
@@ -165,7 +165,7 @@ export default defineNuxtConfig({
}) })
``` ```
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
Search the icon you want to use on https://icones.js.org built by [@antfu](https://github.com/antfu). Search the icon you want to use on https://icones.js.org built by [@antfu](https://github.com/antfu).
:: ::

View File

@@ -21,7 +21,7 @@ Shortcuts are displayed and styled through the [Kbd](/elements/kbd) component.
</template> </template>
``` ```
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
You will have a preview of how shortcuts are rendered in each component page. You will have a preview of how shortcuts are rendered in each component page.
:: ::

View File

@@ -3,7 +3,7 @@ title: Examples
description: Discover some real-life examples of components you can build. description: Discover some real-life examples of components you can build.
--- ---
::alert{icon="i-heroicons-wrench-screwdriver"} ::callout{icon="i-heroicons-wrench-screwdriver"}
If you have any ideas of examples you'd like to see, please comment on [this issue](https://github.com/nuxtlabs/ui/issues/297). If you have any ideas of examples you'd like to see, please comment on [this issue](https://github.com/nuxtlabs/ui/issues/297).
:: ::
@@ -153,7 +153,7 @@ padding: false
:command-palette-theme-algolia{class="max-h-[480px] rounded-md"} :command-palette-theme-algolia{class="max-h-[480px] rounded-md"}
:: ::
::alert{icon="i-simple-icons-github" to="https://github.com/nuxtlabs/ui/blob/dev/docs/components/content/themes/CommandPaletteThemeAlgolia.vue#L23"} ::callout{icon="i-simple-icons-github" to="https://github.com/nuxtlabs/ui/blob/dev/docs/components/content/themes/CommandPaletteThemeAlgolia.vue#L23"}
Take a look at the component! Take a look at the component!
:: ::
@@ -168,7 +168,7 @@ padding: false
:command-palette-theme-raycast{class="max-h-[480px] rounded-md"} :command-palette-theme-raycast{class="max-h-[480px] rounded-md"}
:: ::
::alert{icon="i-simple-icons-github" to="https://github.com/nuxtlabs/ui/blob/dev/docs/components/content/themes/CommandPaletteThemeRaycast.vue#L30"} ::callout{icon="i-simple-icons-github" to="https://github.com/nuxtlabs/ui/blob/dev/docs/components/content/themes/CommandPaletteThemeRaycast.vue#L30"}
Take a look at the component! Take a look at the component!
:: ::
@@ -261,6 +261,6 @@ Here are some examples of how components look like in RTL mode.
:pagination-example-r-t-l :pagination-example-r-t-l
:: ::
::alert{icon="i-simple-icons-github" to="https://github.com/nuxtlabs/ui/blob/dev/docs/components/content/examples/PaginationExampleRTL.vue"} ::callout{icon="i-simple-icons-github" to="https://github.com/nuxtlabs/ui/blob/dev/docs/components/content/examples/PaginationExampleRTL.vue"}
Take a look at the component! Take a look at the component!
:: ::

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display an image that represents a resource or a group of resources. description: Display an image that represents a resource or a group of resources.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/elements/Avatar.vue
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a short text to represent a status or a category. description: Display a short text to represent a status or a category.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/elements/Badge.vue
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Create a button with icon or link capabilities. description: Create a button with icon or link capabilities.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/elements/Button.vue
--- ---
## Usage ## Usage

View File

@@ -1,9 +1,12 @@
--- ---
github: true
description: Display a list of actions in a dropdown menu. description: Display a list of actions in a dropdown menu.
headlessui: links:
label: 'Menu' - label: GitHub
to: 'https://headlessui.com/vue/menu' icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/elements/Dropdown.vue
- label: Menu
icon: i-simple-icons-headlessui
to: https://headlessui.com/vue/menu
--- ---
## Usage ## Usage

View File

@@ -1,9 +1,12 @@
--- ---
github: true
description: Display togglable accordion panels. description: Display togglable accordion panels.
headlessui: links:
label: 'Disclosure' - label: GitHub
to: 'https://headlessui.com/vue/disclosure' icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/elements/Accordion.vue
- label: Disclosure
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/disclosure'
navigation: navigation:
badge: 'Edge' badge: 'Edge'
--- ---

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display an icon from Iconify library. description: Display an icon from Iconify library.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/elements/Icon.vue
--- ---
## Usage ## Usage
@@ -12,7 +15,7 @@ props:
--- ---
:: ::
::alert{icon="i-heroicons-exclamation-triangle"} ::callout{icon="i-heroicons-exclamation-triangle"}
When playing with the `name` prop above, you won't be able to use any icon you want as icons are bundled on build as explained in the [theming section](/getting-started/theming#icons). When playing with the `name` prop above, you won't be able to use any icon you want as icons are bundled on build as explained in the [theming section](/getting-started/theming#icons).
:: ::

View File

@@ -1,7 +1,10 @@
--- ---
github: true
title: 'Keyboard Key' title: 'Keyboard Key'
description: Display a keyboard key in a text block. description: Display a keyboard key in a text block.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/elements/Kbd.vue
navigation: navigation:
title: 'Kbd' title: 'Kbd'
--- ---

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display an input field. description: Display an input field.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/Input.vue
--- ---
## Usage ## Usage
@@ -218,7 +221,7 @@ const q = ref('')
``` ```
:: ::
::alert{icon="i-heroicons-exclamation-triangle-20-solid"} ::callout{icon="i-heroicons-exclamation-triangle-20-solid"}
As leading and trailing icons are wrapped around a `pointer-events-none` class, if you inject a clickable element in the slot, you need to remove this class to make it clickable by adding `:ui="{ icon: { trailing: { pointer: '' } } }"` to the Input. As leading and trailing icons are wrapped around a `pointer-events-none` class, if you inject a clickable element in the slot, you need to remove this class to make it clickable by adding `:ui="{ icon: { trailing: { pointer: '' } } }"` to the Input.
:: ::

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a textarea field. description: Display a textarea field.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/Textarea.vue
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a select field. description: Display a select field.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/Select.vue
--- ---
## Usage ## Usage

View File

@@ -1,9 +1,12 @@
--- ---
github: true
description: Display a select menu with advanced features. description: Display a select menu with advanced features.
headlessui: links:
label: 'Listbox' - label: GitHub
to: 'https://headlessui.com/vue/listbox' icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/SelectMenu.vue
- label: 'Listbox'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/listbox'
--- ---
## Usage ## Usage
@@ -122,7 +125,7 @@ excludedProps:
--- ---
:: ::
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
Learn how to customize icons from the [Select](/forms/select#icon) component. Learn how to customize icons from the [Select](/forms/select#icon) component.
:: ::

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a checkbox field. description: Display a checkbox field.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/Checkbox.vue
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a radio field. description: Display a radio field.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/Radio.vue
--- ---
## Usage ## Usage

View File

@@ -1,9 +1,12 @@
--- ---
github: true
description: Display a toggle field. description: Display a toggle field.
headlessui: links:
label: 'Switch' - label: GitHub
to: 'https://headlessui.com/vue/switch' icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/Toggle.vue
- label: 'Switch'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/switch'
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a range field description: Display a range field
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/Range.vue
navigation: navigation:
badge: New badge: New
--- ---

View File

@@ -1,7 +1,9 @@
--- ---
github:
suffix: .ts
description: Display a label and additional informations around a form element. description: Display a label and additional informations around a form element.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/forms/FormGroup.ts
--- ---
@@ -128,7 +130,7 @@ code: >-
You can also use the `error` prop as a boolean to mark the form element as invalid. You can also use the `error` prop as a boolean to mark the form element as invalid.
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
The `error` prop will automatically set the `color` prop of the form element to `red`. The `error` prop will automatically set the `color` prop of the form element to `red`.
:: ::

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: 'Display data in a table.' description: 'Display data in a table.'
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/data/Table.vue
--- ---
## Usage ## Usage
@@ -210,7 +213,7 @@ You can specify a default sort for the table through the `sort` prop. It's an ob
- `column` - The column to sort by. - `column` - The column to sort by.
- `direction` - The sort direction. Can be either `asc` or `desc` and defaults to `asc`. - `direction` - The sort direction. Can be either `asc` or `desc` and defaults to `asc`.
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
This will set the default sort and will work even if no column is set as `sortable`. This will set the default sort and will work even if no column is set as `sortable`.
:: ::
@@ -289,7 +292,7 @@ Use the `sort-asc-icon` prop to set a different icon or change it globally in `u
Use the `sort-desc-icon` prop to set a different icon or change it globally in `ui.table.default.sortDescIcon`. Defaults to `i-heroicons-bars-arrow-down-20-solid`. Use the `sort-desc-icon` prop to set a different icon or change it globally in `ui.table.default.sortDescIcon`. Defaults to `i-heroicons-bars-arrow-down-20-solid`.
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
You can also customize the entire header cell, read more in the [Slots](#slots) section. You can also customize the entire header cell, read more in the [Slots](#slots) section.
:: ::
@@ -320,7 +323,7 @@ const selected = ref([people[1]])
``` ```
:: ::
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
You can use the `by` prop to compare objects by a field instead of comparing object instances. We've replicated the behavior of Headless UI [Combobox](https://headlessui.com/vue/combobox#binding-objects-as-values). You can use the `by` prop to compare objects by a field instead of comparing object instances. We've replicated the behavior of Headless UI [Combobox](https://headlessui.com/vue/combobox#binding-objects-as-values).
:: ::
@@ -500,7 +503,7 @@ The `sort` property is an object with the following properties:
The `on-sort` property is a function that you can call to sort the table and accepts the column as parameter. The `on-sort` property is a function that you can call to sort the table and accepts the column as parameter.
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
Even though you can customize the sort button as mentioned in the [Sortable](#sortable) section, you can use this slot to completely override its behavior, with a custom dropdown for example. Even though you can customize the sort button as mentioned in the [Sortable](#sortable) section, you can use this slot to completely override its behavior, with a custom dropdown for example.
:: ::

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a vertical navigation. description: Display a vertical navigation.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/navigation/VerticalNavigation.vue
--- ---
## Usage ## Usage

View File

@@ -1,9 +1,12 @@
--- ---
github: true
description: Add a customizable command palette to your app. description: Add a customizable command palette to your app.
headlessui: links:
label: 'Combobox' - label: GitHub
to: 'https://headlessui.com/vue/combobox' icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/navigation/CommandPalette.vue
- label: 'Combobox'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/combobox'
--- ---
## Usage ## Usage
@@ -278,8 +281,8 @@ You can also highlight the matches in the command by setting the `fuse.fuseOptio
</template> </template>
``` ```
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
Try it yourself in this documentation's search by pressing :badge-shortcut{value="meta"} :badge-shortcut{value="K" class="ml-1"}. Try it yourself in this documentation's search by pressing :kbd{value="meta"} :kbd{value="K" class="ml-1"}.
:: ::
## Async search ## Async search
@@ -319,7 +322,7 @@ const groups = computed(() => {
``` ```
:: ::
::alert{icon="i-heroicons-light-bulb"} ::callout{icon="i-heroicons-light-bulb"}
The `loading` state will automatically be enabled when a `search` function is loading. You can disable this behavior by setting the `loading-icon` prop to `null` or globally in `ui.commandPalette.default.loadingIcon`. The `loading` state will automatically be enabled when a `search` function is loading. You can disable this behavior by setting the `loading-icon` prop to `null` or globally in `ui.commandPalette.default.loadingIcon`.
:: ::

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Add a pagination to handle pages. description: Add a pagination to handle pages.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/navigation/Pagination.vue
--- ---
## Usage ## Usage

View File

@@ -1,9 +1,12 @@
--- ---
github: true
description: Display a modal within your application. description: Display a modal within your application.
headlessui: links:
label: 'Dialog' - label: GitHub
to: 'https://headlessui.com/vue/dialog' icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/overlays/Modal.vue
- label: 'Dialog'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/dialog'
--- ---
## Usage ## Usage

View File

@@ -1,9 +1,12 @@
--- ---
github: true
description: Display a dialog that slides in from the edge of the screen. description: Display a dialog that slides in from the edge of the screen.
headlessui: links:
label: 'Dialog' - label: GitHub
to: 'https://headlessui.com/vue/dialog' icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/overlays/Slideover.vue
- label: 'Dialog'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/dialog'
--- ---
## Usage ## Usage

View File

@@ -1,9 +1,12 @@
--- ---
github: true
description: Display a non-modal dialog that floats around a trigger element. description: Display a non-modal dialog that floats around a trigger element.
headlessui: links:
label: 'Popover' - label: GitHub
to: 'https://headlessui.com/vue/popover' icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/overlays/Popover.vue
- label: 'Popover'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/popover'
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display content that appears on hover next to an element. description: Display content that appears on hover next to an element.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/overlays/Tooltip.vue
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a menu that appears on right click. description: Display a menu that appears on right click.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/overlays/ContextMenu.vue
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a toast notification in your app. description: Display a toast notification in your app.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/overlays/Notification.vue
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a card for content with a header, body and footer. description: Display a card for content with a header, body and footer.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/layout/Card.vue
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: A container lets you center and constrain the width of your content. description: A container lets you center and constrain the width of your content.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/layout/Container.vue
--- ---
## Usage ## Usage

View File

@@ -1,6 +1,9 @@
--- ---
github: true
description: Display a placeholder while content is loading. description: Display a placeholder while content is loading.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxtlabs/ui/blob/dev/src/runtime/components/layout/Skeleton.vue
--- ---
## Usage ## Usage

View File

@@ -1,49 +1,43 @@
import ui from '../src/module'
import { excludeColors } from '../src/colors'
import colors from 'tailwindcss/colors' import colors from 'tailwindcss/colors'
import module from '../src/module'
import { excludeColors } from '../src/colors'
import pkg from '../package.json' import pkg from '../package.json'
export default defineNuxtConfig({ export default defineNuxtConfig({
// @ts-ignore extends: '@nuxthq/ui-kit',
modules: [ modules: [
ui,
'@vueuse/nuxt',
'@nuxt/content', '@nuxt/content',
'@nuxt/devtools', '@nuxt/devtools',
'@nuxthq/studio', '@nuxthq/studio',
module,
'@nuxtjs/google-fonts',
'@nuxtjs/plausible', '@nuxtjs/plausible',
'nuxt-lodash', '@vueuse/nuxt',
'nuxt-component-meta' 'nuxt-component-meta',
'nuxt-lodash'
], ],
runtimeConfig: { runtimeConfig: {
public: { public: {
version: pkg.version version: pkg.version
} }
}, },
content: {
highlight: {
theme: {
light: 'material-lighter',
default: 'material-default',
dark: 'material-palenight'
},
preload: ['json', 'js', 'ts', 'html', 'css', 'vue', 'diff', 'shell', 'markdown', 'yaml', 'bash', 'ini']
}
},
ui: { ui: {
global: true, global: true,
icons: ['heroicons', 'simple-icons'], icons: ['heroicons', 'simple-icons'],
safelistColors: excludeColors(colors) safelistColors: excludeColors(colors)
}, },
typescript: { googleFonts: {
strict: false, families: {
includeWorkspace: true Inter: [400, 500, 600, 700]
}
}, },
routeRules: { routeRules: {
'/': { redirect: '/getting-started' } '/': { redirect: '/getting-started', prerender: false }
}, },
generate: { nitro: {
routes: ['/getting-started'] prerender: {
routes: ['/getting-started']
}
}, },
componentMeta: { componentMeta: {
metaFields: { metaFields: {
@@ -52,5 +46,9 @@ export default defineNuxtConfig({
events: false, events: false,
exposed: false exposed: false
} }
},
typescript: {
strict: false,
includeWorkspace: true
} }
}) })

22
docs/package.json Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "@nuxthq/ui-docs",
"private": true,
"devDependencies": {
"@iconify-json/heroicons": "latest",
"@iconify-json/simple-icons": "latest",
"@nuxt/content": "npm:@nuxt/content-edge@latest",
"@nuxt/devtools": "^0.6.7",
"@nuxt/eslint-config": "^0.1.1",
"@nuxthq/studio": "^0.13.4",
"@nuxthq/ui-kit": "npm:@nuxthq/ui-kit-edge@0.0.1-28159938.cd087ba",
"@nuxtjs/google-fonts": "^3.0.1",
"@nuxtjs/plausible": "^0.2.1",
"@vueuse/nuxt": "^10.2.1",
"eslint": "^8.45.0",
"nuxt": "^3.6.2",
"nuxt-component-meta": "^0.5.3",
"nuxt-lodash": "^2.5.0",
"typescript": "^5.1.6",
"v-calendar": "^3.0.3"
}
}

View File

@@ -1,43 +1,17 @@
<template> <template>
<div v-if="page" class="grid lg:grid-cols-10 lg:gap-8"> <UDocsPage v-if="page" :page="page" :surround="(surround as ParsedContent[])">
<div class="pt-8 pb-16" :class="page.body?.toc ? 'lg:col-span-8' : 'lg:col-span-10'"> <template #footer>
<DocsPageHeader :page="page" /> <Footer />
</template>
<ContentRenderer v-if="page.body" :value="page" class="prose prose-primary dark:prose-invert max-w-none" /> </UDocsPage>
<div v-else class="pt-8">
<DocsPageFooter :page="page" class="mt-12" /> Page not found
<hr class="border-gray-200 dark:border-gray-800 my-6">
<DocsPrevNext :prev="prev" :next="next" />
<DocsFooter class="mt-16" />
</div>
<DocsToc v-if="page.body?.toc" :toc="page.body.toc" class="lg:col-span-2 order-first lg:order-last" />
</div>
<div v-else class="flex-1 flex flex-col items-center justify-center">
<div class="text-center">
<p class="text-base font-semibold text-primary-500 dark:text-primary-400">
404
</p>
<h1 class="mt-2 text-4xl tracking-tight font-extrabold u-text-gray-900 sm:text-5xl">
Page not found
</h1>
<p class="mt-2 text-base u-text-gray-500">
Sorry, we couldnt find the page youre looking for.
</p>
<div class="mt-6">
<NuxtLink to="/" class="text-base font-medium text-primary-500 dark:text-primary-400 hover:u-text-gray-900">
Go back home
<span aria-hidden="true"> &rarr;</span>
</NuxtLink>
</div>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
const route = useRoute() const route = useRoute()
const { data: page } = await useAsyncData(`docs-${route.path}`, () => queryContent(route.path).findOne()) const { data: page } = await useAsyncData(`docs-${route.path}`, () => queryContent(route.path).findOne())
@@ -47,7 +21,10 @@ const { data: surround } = await useAsyncData(`docs-${route.path}-surround`, ()
.findSurround(route.path.endsWith('/') ? route.path.slice(0, -1) : route.path) .findSurround(route.path.endsWith('/') ? route.path.slice(0, -1) : route.path)
) )
const [prev, next] = surround.value if (process.server && !page.value) {
const event = useRequestEvent()
setResponseStatus(event, 404)
}
useContentHead(page) useContentHead(page)
</script> </script>

View File

@@ -1,185 +1,11 @@
import type { Config } from 'tailwindcss' import type { Config } from 'tailwindcss'
import defaultTheme from 'tailwindcss/defaultTheme' import defaultTheme from 'tailwindcss/defaultTheme'
export default <Partial<Config>> { export default <Partial<Config>>{
content: [
'docs/content/**/*.md'
],
theme: { theme: {
extend: { extend: {
fontFamily: { fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans] sans: ['Inter', ...defaultTheme.fontFamily.sans]
},
maxWidth: {
'8xl': '90rem'
},
typography: (theme) => {
return {
DEFAULT: {
css: {
'h1, h2, h3, h4': {
fontWeight: theme('fontWeight.bold'),
'scroll-margin-top': 'var(--scroll-mt)'
},
'h1 a, h2 a, h3 a, h4 a': {
borderBottom: 'none !important',
color: 'inherit',
fontWeight: 'inherit'
},
a: {
fontWeight: theme('fontWeight.medium'),
textDecoration: 'none',
borderBottom: '1px solid transparent'
},
'a:hover': {
borderColor: 'var(--tw-prose-links)'
},
'a:has(> code)': {
borderColor: 'transparent !important'
},
'a code': {
color: 'var(--tw-prose-code)',
border: '1px dashed var(--tw-prose-pre-border)'
},
'a:hover code': {
color: 'var(--tw-prose-links)',
borderColor: 'var(--tw-prose-links)'
},
pre: {
margin: '0',
borderRadius: '0.375rem',
border: '1px solid var(--tw-prose-pre-border)',
whiteSpace: 'pre-wrap',
wordBreak: 'break-word'
},
code: {
backgroundColor: 'var(--tw-prose-pre-bg)',
padding: '0.25rem 0.375rem',
borderRadius: '0.375rem',
border: '1px solid var(--tw-prose-pre-border)'
},
'code::before': {
content: ''
},
'code::after': {
content: ''
},
'blockquote p:first-of-type::before': {
content: ''
},
'blockquote p:last-of-type::after': {
content: ''
},
'input[type="checkbox"]': {
color: 'rgb(var(--color-primary-500))',
borderRadius: theme('borderRadius.DEFAULT'),
borderColor: 'rgb(var(--color-gray-300))',
height: theme('spacing.4'),
width: theme('spacing.4'),
marginTop: '-3.5px !important',
marginBottom: '0 !important',
'&:focus': {
'--tw-ring-offset-width': 0
}
},
'input[type="checkbox"]:checked': {
borderColor: 'rgb(var(--color-primary-500))'
},
'input[type="checkbox"]:disabled': {
opacity: 0.5,
cursor: 'not-allowed'
},
'ul.contains-task-list': {
marginLeft: '-1.625em'
},
'ul ul': {
paddingLeft: theme('padding.6')
},
'ul ol': {
paddingLeft: theme('padding.6')
},
'ul > li.task-list-item': {
paddingLeft: '0 !important'
},
'ul > li.task-list-item input': {
marginRight: '7px'
},
'ul > li.task-list-item > ul.contains-task-list': {
marginLeft: 'initial'
},
'ul > li.task-list-item a': {
marginBottom: 0
},
'ul > li.task-list-item::marker': {
content: 'none'
},
'ul > li > p': {
margin: 0
},
'ul > li > span.issue-badge, p > span.issue-badge': {
verticalAlign: 'text-top',
margin: '0 !important'
},
'ul > li > button': {
verticalAlign: 'baseline !important'
},
table: {
wordBreak: 'break-all'
}
}
},
primary: {
css: {
'--tw-prose-body': 'rgb(var(--color-gray-700))',
'--tw-prose-headings': 'rgb(var(--color-gray-900))',
'--tw-prose-lead': 'rgb(var(--color-gray-600))',
'--tw-prose-links': 'rgb(var(--color-primary-500))',
'--tw-prose-bold': 'rgb(var(--color-gray-900))',
'--tw-prose-counters': 'rgb(var(--color-gray-500))',
'--tw-prose-bullets': 'rgb(var(--color-gray-300))',
'--tw-prose-hr': 'rgb(var(--color-gray-100))',
'--tw-prose-quotes': 'rgb(var(--color-gray-900))',
'--tw-prose-quote-borders': 'rgb(var(--color-gray-200))',
'--tw-prose-captions': 'rgb(var(--color-gray-500))',
'--tw-prose-code': 'rgb(var(--color-gray-900))',
'--tw-prose-pre-code': 'rgb(var(--color-gray-900))',
'--tw-prose-pre-bg': 'rgb(var(--color-gray-50))',
'--tw-prose-pre-border': 'rgb(var(--color-gray-200))',
'--tw-prose-th-borders': 'rgb(var(--color-gray-300))',
'--tw-prose-td-borders': 'rgb(var(--color-gray-200))',
'--tw-prose-invert-body': 'rgb(var(--color-gray-200))',
'--tw-prose-invert-headings': theme('colors.white'),
'--tw-prose-invert-lead': 'rgb(var(--color-gray-400))',
'--tw-prose-invert-links': 'rgb(var(--color-primary-400))',
'--tw-prose-invert-bold': theme('colors.white'),
'--tw-prose-invert-counters': 'rgb(var(--color-gray-400))',
'--tw-prose-invert-bullets': 'rgb(var(--color-gray-600))',
'--tw-prose-invert-hr': 'rgb(var(--color-gray-800))',
'--tw-prose-invert-quotes': 'rgb(var(--color-gray-100))',
'--tw-prose-invert-quote-borders': 'rgb(var(--color-gray-700))',
'--tw-prose-invert-captions': 'rgb(var(--color-gray-400))',
'--tw-prose-invert-code': theme('colors.white'),
'--tw-prose-invert-pre-code': theme('colors.white'),
'--tw-prose-invert-pre-bg': 'rgb(var(--color-gray-800))',
'--tw-prose-invert-pre-border': 'rgb(var(--color-gray-700))',
'--tw-prose-invert-th-borders': 'rgb(var(--color-gray-700))',
'--tw-prose-invert-td-borders': 'rgb(var(--color-gray-800))'
}
},
invert: {
css: {
'--tw-prose-pre-border': 'var(--tw-prose-invert-pre-border)',
'input[type="checkbox"]': {
backgroundColor: 'rgb(var(--color-gray-800))',
borderColor: 'rgb(var(--color-gray-700))'
},
'input[type="checkbox"]:checked': {
backgroundColor: 'rgb(var(--color-primary-400))',
borderColor: 'rgb(var(--color-primary-400))'
}
}
}
}
} }
} }
} }

View File

@@ -49,24 +49,14 @@
"tailwindcss": "^3.3.2" "tailwindcss": "^3.3.2"
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/simple-icons": "^1.1.59",
"@nuxt/content": "^2.7.0",
"@nuxt/devtools": "^0.6.7",
"@nuxt/eslint-config": "^0.1.1", "@nuxt/eslint-config": "^0.1.1",
"@nuxt/module-builder": "^0.4.0", "@nuxt/module-builder": "^0.4.0",
"@nuxthq/studio": "^0.13.3",
"@nuxtjs/plausible": "^0.2.1",
"@release-it/conventional-changelog": "^7.0.0", "@release-it/conventional-changelog": "^7.0.0",
"@types/lodash-es": "^4.17.7",
"@types/node": "^20.4.1",
"@vueuse/nuxt": "^10.2.1",
"eslint": "^8.44.0", "eslint": "^8.44.0",
"nuxt": "^3.6.2", "nuxt": "^3.6.2",
"nuxt-component-meta": "^0.5.3",
"nuxt-lodash": "^2.5.0",
"release-it": "^16.1.0", "release-it": "^16.1.0",
"unbuild": "^1.2.1", "unbuild": "^1.2.1",
"v-calendar": "^3.0.3", "typescript": "^5.1.6",
"vue-tsc": "^1.8.4" "vue-tsc": "^1.8.4"
} }
} }

3197
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

3
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,3 @@
packages:
- "docs"
- "./"

View File

@@ -5,6 +5,7 @@
<NuxtLink <NuxtLink
v-else v-else
v-slot="{ href, navigate, exact, isActive, isExactActive }" v-slot="{ href, navigate, exact, isActive, isExactActive }"
:to="$attrs.to"
custom custom
> >
<a <a