mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 20:19:34 +01:00
docs: add showcase page (#3659)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
@@ -14,6 +14,7 @@ const props = withDefaults(defineProps<{
|
||||
color?: string
|
||||
size?: { min: number, max: number }
|
||||
speed?: 'slow' | 'normal' | 'fast'
|
||||
isIndex?: boolean
|
||||
}>(), {
|
||||
starCount: 50,
|
||||
color: 'var(--ui-primary)',
|
||||
@@ -21,7 +22,8 @@ const props = withDefaults(defineProps<{
|
||||
min: 1,
|
||||
max: 3
|
||||
}),
|
||||
speed: 'normal'
|
||||
speed: 'normal',
|
||||
isIndex: false
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
@@ -53,7 +55,7 @@ const twinkleDuration = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="absolute pointer-events-none z-[-1] inset-y-0 left-4 right-4 lg:right-[50%] overflow-hidden">
|
||||
<div class="absolute pointer-events-none z-[-1] overflow-hidden" :class="isIndex ? 'inset-y-0 left-4 right-4 lg:right-[50%]' : 'inset-0'">
|
||||
<div
|
||||
v-for="star in stars"
|
||||
:key="star.id"
|
||||
|
||||
@@ -84,15 +84,10 @@ export function useLinks() {
|
||||
label: 'Community',
|
||||
icon: 'i-lucide-users',
|
||||
children: [{
|
||||
label: 'Roadmap',
|
||||
description: 'Track our development progress in real-time.',
|
||||
icon: 'i-lucide-map',
|
||||
to: '/roadmap'
|
||||
}, {
|
||||
label: 'Contribution',
|
||||
description: 'Learn how to contribute to Nuxt UI.',
|
||||
icon: 'i-lucide-git-pull-request-arrow',
|
||||
to: '/getting-started/contribution'
|
||||
icon: 'i-lucide-presentation',
|
||||
label: 'Showcase',
|
||||
description: 'Check out some amazing projects built with Nuxt UI.',
|
||||
to: '/showcase'
|
||||
}, {
|
||||
label: 'Devtools Integration',
|
||||
description: 'Integrate Nuxt UI with Nuxt Devtools with Compodium.',
|
||||
@@ -111,11 +106,6 @@ export function useLinks() {
|
||||
icon: 'i-simple-icons-figma',
|
||||
to: 'https://github.com/Justineo/tempad-dev-plugin-nuxt-ui',
|
||||
target: '_blank'
|
||||
}, {
|
||||
label: 'Team',
|
||||
description: 'Meet the team behind Nuxt UI.',
|
||||
icon: 'i-lucide-users',
|
||||
to: '/team'
|
||||
}]
|
||||
}, {
|
||||
label: 'Releases',
|
||||
|
||||
@@ -31,6 +31,11 @@ export function useSearchLinks() {
|
||||
label: 'Figma',
|
||||
icon: 'i-simple-icons-figma',
|
||||
to: '/figma'
|
||||
}, {
|
||||
icon: 'i-lucide-presentation',
|
||||
label: 'Community > Showcase',
|
||||
description: 'Check out some of the amazing projects built with Nuxt UI.',
|
||||
to: '/showcase'
|
||||
}, {
|
||||
label: 'Community > Contribution',
|
||||
description: 'A comprehensive guide on contributing to Nuxt UI, including project structure, development workflow, and best practices.',
|
||||
|
||||
@@ -74,7 +74,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<LazySkyBg />
|
||||
<LazySkyBg is-index />
|
||||
|
||||
<div class="h-[344px] lg:h-full lg:relative w-full lg:min-h-[calc(100vh-var(--ui-header-height)-1px)] overflow-hidden">
|
||||
<UPageMarquee
|
||||
|
||||
69
docs/app/pages/showcase.vue
Normal file
69
docs/app/pages/showcase.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import { joinURL } from 'ufo'
|
||||
|
||||
const { data: page } = await useAsyncData('showcase', () => queryCollection('showcase').first())
|
||||
if (!page.value) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
|
||||
}
|
||||
|
||||
const { url } = useSiteConfig()
|
||||
|
||||
useSeoMeta({
|
||||
titleTemplate: `%s - Nuxt UI`,
|
||||
title: page.value.title,
|
||||
description: page.value.description,
|
||||
ogTitle: `${page.value.title} - Nuxt UI`,
|
||||
ogDescription: page.value.description,
|
||||
ogImage: joinURL(url, '/og-image.png')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UMain v-if="page">
|
||||
<UPageHero
|
||||
:title="page.hero.title"
|
||||
:description="page.hero.description"
|
||||
:links="page.hero.links"
|
||||
:ui="{
|
||||
wrapper: 'lg:px-12',
|
||||
container: 'relative'
|
||||
}"
|
||||
>
|
||||
<template #top>
|
||||
<div class="absolute z-[-1] rounded-full bg-(--ui-primary) blur-[300px] size-60 sm:size-80 transform -translate-x-1/2 left-1/2 -translate-y-80" />
|
||||
</template>
|
||||
|
||||
<LazyStarsBg />
|
||||
|
||||
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
|
||||
<div class="border border-(--ui-border) bg-(--ui-bg)">
|
||||
<ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 items-start justify-center">
|
||||
<li
|
||||
v-for="item in page.items"
|
||||
:key="item.name"
|
||||
class="relative flex flex-col gap-y-4 justify-start group h-full p-4 hover:bg-(--ui-bg-elevated)/50 border-(--ui-border) border-b max-sm:last:border-b-0 border-r max-sm:border-r-0 sm:even:border-r-0 lg:even:border-r lg:border-r lg:[&:nth-child(4n)]:border-r-0 lg:[&:nth-child(5n)]:border-b-0 lg:[&:nth-child(6n)]:border-b-0"
|
||||
>
|
||||
<NuxtLink class="inset-0 absolute" :to="item.url" target="_blank">
|
||||
<span class="sr-only">Go to {{ item.name }}</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtImg
|
||||
:src="`/assets/showcase/${item.name.toLowerCase().replace(/\s/g, '-')}.png`"
|
||||
:alt="`Screenshot of ${item.name}`"
|
||||
loading="lazy"
|
||||
class="rounded-[calc(var(--ui-radius)*1.5)]"
|
||||
/>
|
||||
|
||||
<div class="flex items-center gap-1 px-1">
|
||||
<span class="font-medium text-(--ui-text-highlighted)">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
<UIcon name="i-lucide-arrow-right" class="size-4 flex-shrink-0 opacity-0 group-hover:opacity-100 transition-all duration-200 -translate-x-1 group-hover:translate-x-0 text-(--ui-text-muted)" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</UPageHero>
|
||||
</UMain>
|
||||
</template>
|
||||
@@ -1,6 +1,18 @@
|
||||
import { defineCollection, z } from '@nuxt/content'
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
const Button = z.object({
|
||||
label: z.string(),
|
||||
icon: z.string().optional(),
|
||||
trailingIcon: z.string().optional(),
|
||||
to: z.string().optional(),
|
||||
color: z.enum(['primary', 'neutral', 'success', 'warning', 'error', 'info']).optional(),
|
||||
size: z.enum(['xs', 'sm', 'md', 'lg', 'xl']).optional(),
|
||||
variant: z.enum(['solid', 'outline', 'subtle', 'soft', 'ghost', 'link']).optional(),
|
||||
id: z.string().optional(),
|
||||
target: z.enum(['_blank', '_self']).optional()
|
||||
})
|
||||
|
||||
const schema = z.object({
|
||||
category: z.enum(['layout', 'form', 'element', 'navigation', 'data', 'overlay']).optional(),
|
||||
framework: z.string().optional(),
|
||||
@@ -42,5 +54,26 @@ export const collections = {
|
||||
include: '**/*'
|
||||
}, pro!].filter(Boolean),
|
||||
schema
|
||||
}),
|
||||
showcase: defineCollection({
|
||||
type: 'page',
|
||||
source: 'showcase.yml',
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
hero: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
links: z.array(Button)
|
||||
}),
|
||||
items: z.array(z.object({
|
||||
name: z.string(),
|
||||
url: z.string(),
|
||||
screenshotUrl: z.string().optional(),
|
||||
screenshotOptions: z.object({
|
||||
delay: z.number()
|
||||
})
|
||||
}))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
19
docs/content/showcase.yml
Normal file
19
docs/content/showcase.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
title: Showcase
|
||||
description: Check out some of the amazing projects built with Nuxt UI.
|
||||
navigation: false
|
||||
hero:
|
||||
title: Showcase
|
||||
description: Discover our selection of projects built with Nuxt UI.
|
||||
items:
|
||||
- name: Shelve
|
||||
url: https://shelve.cloud
|
||||
- name: Uneed
|
||||
url: https://uneed.best
|
||||
- name: Details
|
||||
url: https://details.team
|
||||
- name: Espace Asso by Benevolt
|
||||
url: https://asso.benevolt.fr
|
||||
- name: Directus Docs
|
||||
url: https://docs.directus.io
|
||||
- name: Super SaaS
|
||||
url: https://supersaas.dev/
|
||||
54
docs/modules/screenshot.ts
Normal file
54
docs/modules/screenshot.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { defineNuxtModule } from '@nuxt/kit'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { join } from 'pathe'
|
||||
import captureWebsite from 'capture-website'
|
||||
|
||||
interface ContentFile {
|
||||
id?: string
|
||||
items?: any
|
||||
}
|
||||
|
||||
interface TemplateItem {
|
||||
name: string
|
||||
url?: string
|
||||
screenshotUrl?: string
|
||||
screenshotOptions?: Record<string, any>
|
||||
}
|
||||
|
||||
export default defineNuxtModule((options, nuxt) => {
|
||||
nuxt.hook('content:file:afterParse', async ({ content: file }: { content: ContentFile }) => {
|
||||
if (file.id?.includes('showcase') && file.items) {
|
||||
const itemsArray: TemplateItem[] = Array.isArray(file.items)
|
||||
? file.items
|
||||
: Object.values(file.items)
|
||||
if (itemsArray.length === 0) {
|
||||
console.log('No items to process')
|
||||
return
|
||||
}
|
||||
console.log(`Processing ${itemsArray.length} template items`)
|
||||
for (const template of itemsArray) {
|
||||
const url = template.screenshotUrl || template.url
|
||||
if (!url) {
|
||||
console.error(`Template ${template.name} has no "url" or "screenshotUrl" to take a screenshot from`)
|
||||
continue
|
||||
}
|
||||
const name = template.name.toLowerCase().replace(/\s/g, '-')
|
||||
const filename = join(process.cwd(), 'docs/public/assets/showcase', `${name}.png`)
|
||||
if (existsSync(filename)) {
|
||||
console.log(`Screenshot for ${template.name} already exists, skipping`)
|
||||
continue
|
||||
}
|
||||
console.log(`Generating screenshot for Template ${template.name} hitting ${url}...`)
|
||||
try {
|
||||
await captureWebsite.file(url, filename, {
|
||||
...(template.screenshotOptions || {}),
|
||||
launchOptions: { headless: true }
|
||||
})
|
||||
console.log(`Screenshot for ${template.name} generated successfully`)
|
||||
} catch (error) {
|
||||
console.error(`Error generating screenshot for ${template.name}:`, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -16,6 +16,7 @@
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@rollup/plugin-yaml": "^4.1.2",
|
||||
"@vueuse/nuxt": "^13.0.0",
|
||||
"capture-website": "^4.2.0",
|
||||
"@vueuse/integrations": "^13.0.0",
|
||||
"sortablejs": "^1.15.6",
|
||||
"joi": "^17.13.3",
|
||||
|
||||
BIN
docs/public/assets/showcase/details.png
Normal file
BIN
docs/public/assets/showcase/details.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 796 KiB |
BIN
docs/public/assets/showcase/directus-docs.png
Normal file
BIN
docs/public/assets/showcase/directus-docs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 211 KiB |
BIN
docs/public/assets/showcase/espace-asso-by-benevolt.png
Normal file
BIN
docs/public/assets/showcase/espace-asso-by-benevolt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
docs/public/assets/showcase/shelve.png
Normal file
BIN
docs/public/assets/showcase/shelve.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 423 KiB |
BIN
docs/public/assets/showcase/super-saas.png
Normal file
BIN
docs/public/assets/showcase/super-saas.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 350 KiB |
BIN
docs/public/assets/showcase/uneed.png
Normal file
BIN
docs/public/assets/showcase/uneed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 331 KiB |
Reference in New Issue
Block a user