docs: block preview and codes

This commit is contained in:
zernonia 2024-11-23 00:49:59 +08:00
parent 097830a15c
commit bb5d5b2a71
467 changed files with 4179 additions and 2924 deletions

View File

@ -1,31 +1,33 @@
<script setup lang="ts">
import { Badge } from '@/registry/new-york/ui/badge'
import { Popover, PopoverContent, PopoverTrigger } from '@/registry/new-york/ui/popover'
import type { Block } from '@/registry/schema'
import Button from '@/registry/new-york/ui/button/Button.vue'
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/registry/new-york/ui/resizable'
import { Separator } from '@/registry/new-york/ui/separator'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/registry/new-york/ui/tabs'
import { ToggleGroup, ToggleGroupItem } from '@/registry/new-york/ui/toggle-group'
import { useConfigStore } from '@/stores/config'
import { CircleHelp, Info, Monitor, Smartphone, Tablet } from 'lucide-vue-next'
import MagicString from 'magic-string'
import { reactive, ref, watch } from 'vue'
import { useClipboard } from '@vueuse/core'
import { Check, Fullscreen, Monitor, Smartphone, Tablet, Terminal } from 'lucide-vue-next'
import MagicString from 'magic-string'
import { computed, reactive, ref, watch } from 'vue'
import { compileScript, parse, walk } from 'vue/compiler-sfc'
import { Index } from '../../../__registry__'
import { Index } from '../../../__registry__/block'
import { highlight } from '../config/shiki'
import BlockCopyButton from './BlockCopyButton.vue'
import BlockPreview from './BlockPreview.vue'
import StyleSwitcher from './StyleSwitcher.vue'
import BlockViewerCode from './BlockViewerCode.vue'
const props = defineProps<{
name: string
}>()
const { style, codeConfig } = useConfigStore()
const { copied, copy } = useClipboard()
const isLoading = ref(true)
const tabValue = ref('preview')
const resizableRef = ref<InstanceType<typeof ResizablePanel>>()
const componentRegistry = ref<Block>()
const rawString = ref('')
const codeHtml = ref('')
@ -35,6 +37,19 @@ const metadata = reactive({
containerClass: null as string | null,
})
const iframeURL = computed(() => {
// @ts-expect-error env available in import.meta
if (import.meta.env.SSR)
return ''
const url = new URL(`${window.location.origin}/blocks/renderer`)
url.searchParams.append('name', props.name)
url.searchParams.append('styles', 'new-york')
url.searchParams.append('containerClass', metadata.containerClass ?? '')
return url.href
})
function removeScript(code: string) {
const s = new MagicString(code)
const scriptTagRegex = /<script\s+lang="ts"\s*>[\s\S]+?<\/script>/g
@ -58,9 +73,11 @@ function transformImportPath(code: string) {
watch([style, codeConfig], async () => {
try {
const styleIndex = Index[style.value]
const componentRegistry = styleIndex[props.name]
componentRegistry.value = styleIndex[props.name]
if (!componentRegistry.value)
return
const rawString = await componentRegistry.raw()
const rawString = await componentRegistry.value.raw()
if (!metadata.description) {
const { descriptor } = parse(rawString)
const ast = compileScript(descriptor, { id: '' })
@ -91,48 +108,47 @@ watch([style, codeConfig], async () => {
<Tabs
:id="name"
v-model="tabValue"
class="relative grid w-full scroll-m-20 gap-4"
class="group/block-view-wrapper flex min-w-0 flex-col items-stretch gap-4"
:style=" {
'--container-height': metadata.iframeHeight ?? '600px',
'--height': metadata.iframeHeight ?? '600px',
}"
>
<div class="flex flex-col items-center gap-4 sm:flex-row">
<div class="flex items-center gap-2">
<TabsList class="hidden sm:flex">
<TabsTrigger value="preview">
<div class="hidden items-center gap-2 sm:flex">
<TabsList class="h-7 items-center rounded-md p-0 px-[calc(theme(spacing.1)_-_2px)] py-[theme(spacing.1)]">
<TabsTrigger class="h-[1.45rem] rounded-sm px-2 text-xs" value="preview">
Preview
</TabsTrigger>
<TabsTrigger value="code">
<TabsTrigger class="h-[1.45rem] rounded-sm px-2 text-xs" value="code">
Code
</TabsTrigger>
</TabsList>
<div class="hidden items-center gap-2 sm:flex">
<Separator
orientation="vertical"
class="mx-2 hidden h-4 md:flex"
/>
<div class="flex items-center gap-2">
<a :href="`#${name}`">
<Badge variant="outline">{{ name }}</Badge>
</a>
<Popover>
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
<Info class="h-3.5 w-3.5" />
<span class="sr-only">Block description</span>
</PopoverTrigger>
<PopoverContent
side="right"
:side-offset="10"
class="text-sm"
>
{{ metadata.description }}
</PopoverContent>
</Popover>
</div>
<div class="text-sm font-medium underline-offset-2 hover:underline">
<a :href="`#${name}`">{{ metadata.description }}</a>
</div>
</div>
<div class="flex items-center gap-2 pr-[14px] sm:ml-auto">
<div class="hidden h-[28px] items-center gap-1.5 rounded-md border p-[2px] shadow-sm md:flex">
<Button
variant="ghost"
class="hidden h-7 w-7 rounded-md border bg-transparent shadow-none md:flex lg:w-auto"
size="sm"
@click="copy(`npx shadcn-vue@latest add ${name}`)"
>
<Check v-if="copied" />
<Terminal v-else />
<span class="hidden lg:inline">npx shadcn-vue add {{ name }}</span>
</Button>
<Separator
orientation="vertical"
class="mx-2 hidden h-4 md:flex"
/>
<div class="hidden h-7 items-center gap-1.5 rounded-md border p-[2px] shadow-none lg:flex">
<ToggleGroup
type="single"
default-value="100"
@ -158,44 +174,22 @@ watch([style, codeConfig], async () => {
>
<Smartphone class="h-3.5 w-3.5" />
</ToggleGroupItem>
<Separator orientation="vertical" class="h-4" />
<Button
size="icon"
variant="ghost"
class="h-[22px] w-[22px] rounded-sm p-0"
as-child
title="Open in New Tab"
>
<a :href="iframeURL" target="_blank">
<span class="sr-only">Open in New Tab</span>
<Fullscreen class="h-3.5 w-3.5" />
</a>
</Button>
</ToggleGroup>
</div>
<Separator
orientation="vertical"
class="mx-2 hidden h-4 md:flex"
/>
<StyleSwitcher class="h-7" />
<Popover>
<PopoverTrigger class="hidden text-muted-foreground hover:text-foreground sm:flex">
<CircleHelp class="h-3.5 w-3.5" />
<span class="sr-only">Block description</span>
</PopoverTrigger>
<PopoverContent
side="top"
:side-offset="20"
class="space-y-3 rounded-[0.5rem] text-sm"
>
<p class="font-medium">
What is the difference between the New York and Default style?
</p>
<p>
A style comes with its own set of components, animations,
icons and more.
</p>
<p>
The <span class="font-medium">Default</span> style has
larger inputs, uses lucide-vue-next for icons and
tailwindcss-animate for animations.
</p>
<p>
The <span class="font-medium">New York</span> style ships
with smaller buttons and inputs. It also uses shadows on cards
and buttons.
</p>
</PopoverContent>
</Popover>
<Separator orientation="vertical" class="mx-2 h-4" />
<BlockCopyButton :code="rawString" />
<!-- <BlockCopyButton :code="rawString" /> -->
<!-- <V0Button
name="{block.name}"
description="{block.description" || "Edit in v0"}
@ -208,7 +202,7 @@ watch([style, codeConfig], async () => {
v-show="tabValue === 'preview'"
force-mount
value="preview"
class="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted h-[--container-height] px-0"
class="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted h-[--height] px-0"
>
<ResizablePanelGroup id="block-resizable" direction="horizontal" class="relative z-10">
<ResizablePanel
@ -218,17 +212,18 @@ watch([style, codeConfig], async () => {
:min-size="30"
:as-child="true"
>
<BlockPreview :name="name" styles="default" :container-class="metadata.containerClass ?? ''" container />
<BlockPreview :url="iframeURL" container />
</ResizablePanel>
<ResizableHandle id="block-resizable-handle" class="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block" />
<ResizablePanel id="block-resizable-panel-2" :default-size="0" :min-size="0" />
</ResizablePanelGroup>
</TabsContent>
<TabsContent value="code" class="h-[--container-height]">
<div
<TabsContent value="code" class="h-[--height]">
<BlockViewerCode v-if="componentRegistry" :item="componentRegistry" />
<!-- <div
class="language-vue !h-full !max-h-[none] !mt-0"
v-html="codeHtml"
/>
/> -->
</TabsContent>
</Tabs>
</template>

View File

@ -1,28 +1,13 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { ref } from 'vue'
import Spinner from './Spinner.vue'
const props = defineProps<{
name: string
styles?: string
containerClass?: string
container?: boolean
url: string
}>()
const isLoading = ref(true)
const iframeURL = computed(() => {
// @ts-expect-error env available in import.meta
if (import.meta.env.SSR)
return ''
const url = new URL(`${window.location.origin}/blocks/renderer`)
Object.entries(props).forEach(([key, value]) => {
if (value)
url.searchParams.append(key, value as string)
})
return url.href
})
</script>
<template>
@ -35,8 +20,8 @@ const iframeURL = computed(() => {
>
<iframe
v-show="!isLoading"
:src="iframeURL"
class="relative z-20 w-full bg-background" :class="[container ? 'h-[--container-height]' : 'size-full']"
:src="url"
class="relative z-20 w-full bg-background" :class="[container ? 'h-[--height]' : 'size-full']"
@load="isLoading = false"
/>
</div>

View File

@ -0,0 +1,70 @@
<script setup lang="ts">
import type { Block } from '@/registry/schema'
import { File } from 'lucide-vue-next'
import { computed, onBeforeMount, ref } from 'vue'
import { highlight } from '../config/shiki'
import BlockCopyCodeButton from './BlockCopyCodeButton.vue'
import BlockViewerFileTree, { type FileTree } from './BlockViewerFileTree.vue'
const props = defineProps<{
item: Block
}>()
const activeFile = ref<FileTree>()
const cacheCodes = ref<Map<string, string>>(new Map<string, string>())
const activeCode = computed(() => cacheCodes.value.get(activeFile.value?.path ?? ''))
onBeforeMount(async () => {
for (const file of (props.item.files ?? [])) {
const raw = await file.raw()
const highlighted = highlight(raw, 'vue')
cacheCodes.value.set(file.target || file.path.split(`${props.item.name}/`)[1], highlighted)
}
})
</script>
<template>
<div class="mr-[14px] flex h-[--height] overflow-hidden rounded-xl bg-zinc-950 text-white group-data-[view=preview]/block-view-wrapper:hidden">
<div class="w-[280px]">
<BlockViewerFileTree v-model="activeFile" :item />
</div>
<div class="flex min-w-0 flex-1 flex-col">
<div class="flex h-12 flex-shrink-0 items-center gap-2 border-b border-zinc-700 bg-zinc-900 px-4 text-sm font-medium">
<File class="size-4" />
{{ activeFile?.path }}
<div class="ml-auto flex items-center gap-2">
<BlockCopyCodeButton :code="activeCode" />
</div>
</div>
<div :key="activeFile?.path" data-line-codeblock class="relative flex-1 overflow-hidden after:absolute after:inset-y-0 after:left-0 after:w-10 after:bg-zinc-950 [&_.line:before]:sticky [&_.line:before]:left-2 [&_.line:before]:z-10 [&_.line:before]:translate-y-[-1px] [&_.line:before]:pr-1 [&_pre]:h-[--height] [&_pre]:overflow-auto [&_pre]:!bg-transparent [&_pre]:pb-20 [&_pre]:pt-4 [&_pre]:font-mono [&_pre]:text-sm [&_pre]:leading-relaxed" v-html="activeCode" />
</div>
</div>
</template>
<style>
[data-line-codeblock] code {
display: grid;
min-width: 100%;
overflow-wrap: break-word;
border-radius: 0;
border-width: 0;
background-color: transparent;
padding: 0;
counter-reset: line;
-webkit-box-decoration-break: clone;
box-decoration-break: clone;
}
[data-line-codeblock] .line:before {
font-size: .75rem;
line-height: 1rem;
color: hsla(0, 0%, 98%, .4);
counter-increment: line;
content: counter(line);
display: inline-block;
width: 1.8rem;
margin-right: 1.4rem;
text-align: right;
}
</style>

View File

@ -0,0 +1,150 @@
<script lang="ts">
export interface FileTree {
name: string
path?: string
children?: FileTree[]
}
</script>
<script setup lang="ts">
import type { Block } from '@/registry/schema'
import Button from '@/registry/new-york/ui/button/Button.vue'
import { ChevronRight, File, Folder } from 'lucide-vue-next'
import { TreeItem, TreeRoot } from 'reka-ui'
import { computed, ref, watch } from 'vue'
const props = defineProps<{
item: Block
}>()
const activeFile = defineModel<FileTree>()
// TODO: Improve the getter logics
const flattenFiles = computed(() => {
const root: FileTree[] = []
for (const file of props.item.files ?? []) {
const path = file.target || file.path.split(`${props.item.name}/`)[1]
const parts = path.split('/')
for (let i = 0; i < parts.length; i++) {
const part = parts[i]
const isFile = i === parts.length - 1
const newNode: FileTree = isFile
? { name: part, path }
: { name: part, children: [] }
root.push(newNode)
}
}
return root
})
const treeItem = computed(() => {
return createFileTreeForRegistryItemFiles([...(props.item.files ?? [])])
})
const expandedKeys = ref<string[]>([])
function createFileTreeForRegistryItemFiles(
files: Array<{ path: string, target?: string }>,
) {
const root: FileTree[] = []
for (const file of files) {
const path = file.target || file.path.split(`${props.item.name}/`)[1]
const parts = path.split('/')
let currentLevel = root
for (let i = 0; i < parts.length; i++) {
const part = parts[i]
const isFile = i === parts.length - 1
const existingNode = currentLevel.find(node => node.name === part)
if (existingNode) {
if (isFile) {
// Update existing file node with full path
existingNode.path = path
}
else {
// Move to next level in the tree
currentLevel = existingNode.children!
}
}
else {
const newNode: FileTree = isFile
? { name: part, path }
: { name: part, children: [] }
currentLevel.push(newNode)
if (!isFile) {
currentLevel = newNode.children!
}
}
}
}
return root
}
watch(flattenFiles, (n) => {
activeFile.value = n.filter(i => i.path)[0]
expandedKeys.value = n.map(i => i.name)
}, { immediate: true })
</script>
<template>
<div class="min-h-full w-full has-[[data-variant=inset]]:bg-sidebar flex flex-col">
<div class="flex h-full flex-col w-full flex-1 border-r border-zinc-700 bg-zinc-900 text-white">
<div class="duration-200 flex shrink-0 items-center font-medium outline-none ease-linear h-12 rounded-none border-b border-zinc-700 px-4 text-sm text-white">
Files
</div>
<TreeRoot
v-slot="{ flattenItems }"
v-model="activeFile"
v-model:expanded="expandedKeys"
class="list-none select-none"
:items="treeItem"
:get-key="(item) => item.name"
>
<TreeItem
v-for="item in flattenItems"
:key="item._id"
v-slot="{ isSelected, isExpanded }"
v-bind="item.bind"
as-child
@select="(ev) => {
if (item.hasChildren)
ev.preventDefault()
}"
>
<Button
variant="ghost"
:data-active="isSelected"
class="flex w-full justify-start whitespace-nowrap rounded-none pl-[--index] hover:bg-zinc-700 hover:text-white focus-visible:bg-zinc-700 focus-visible:text-white active:bg-zinc-700 active:text-white data-[active=true]:bg-zinc-700 data-[active=true]:text-white"
:style="{ 'padding-left': `${(item.level - 0.25) * 1.5}rem` }"
>
<template v-if="item.hasChildren">
<ChevronRight
icon="lucide:folder"
class="h-4 w-4 transition-transform"
:class="{ 'rotate-90': isExpanded } "
/>
<Folder class="h-4 w-4" />
</template>
<template v-else>
<ChevronRight
icon="lucide:folder"
class="invisible"
/>
<File class="h-4 w-4" />
</template>
<div>
{{ item.value.name }}
</div>
</Button>
</TreeItem>
</TreeRoot>
</div>
</div>
</template>

View File

@ -12,6 +12,7 @@ import PageHeaderDescription from '../components/PageHeaderDescription.vue'
import PageHeaderHeading from '../components/PageHeaderHeading.vue'
import BlockContainer from './BlockContainer.vue'
// const blocks = ['Sidebar01', 'Sidebar02', 'Sidebar03', 'Sidebar04', 'Sidebar05', 'Sidebar06', 'Sidebar07', 'Sidebar08', 'Sidebar09', 'Sidebar10', 'Sidebar11', 'Sidebar12', 'Sidebar13', 'Sidebar14', 'Sidebar15', 'Login01']
const blocks = Object.values(Index['new-york']).filter((i: any) => i.type === 'registry:block').map((i: any) => i.name)
</script>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -303,7 +303,6 @@ export const Index: Record<string, any> = {
}`
})}],
component: () => import("${componentPath}").then((m) => m.default),
raw: () => import("${componentPath}?raw").then((m) => m.default),
source: "${sourceFilename}",
category: "${item.category ?? ''}",
subcategory: "${item.subcategory ?? ''}"
@ -357,6 +356,91 @@ export const Index: Record<string, any> = {
await writeFile(path.join(process.cwd(), '__registry__/index.ts'), index)
}
// ----------------------------------------------------------------------------
// Build __registry__/block.ts.
// ----------------------------------------------------------------------------
async function buildBlockRegistry(registry: Registry) {
let index = `// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
export const Index: Record<string, any> = {
`
for (const style of styles) {
index += ` "${style.name}": {`
// Build style index.
for (const item of registry) {
if (item.type !== 'registry:block')
continue
const resolveFiles = item.files?.map(
file =>
`registry/${style.name}/${
typeof file === 'string' ? file : file.path
}`,
)
if (!resolveFiles) {
continue
}
const type = item.type.split(':')[1]
let componentPath = `@/registry/${style.name}/${type}/${item.name}`
if (item.files) {
const files = item.files.map(file =>
typeof file === 'string'
? { type: 'registry:page', path: file }
: file,
)
if (files?.length) {
componentPath = `@/registry/${style.name}/${files[0].path}`
}
}
index += `
"${item.name}": {
name: "${item.name}",
description: "${item.description ?? ''}",
type: "${item.type}",
registryDependencies: ${JSON.stringify(item.registryDependencies)},
files: [${item.files?.map((file) => {
const filePath = `registry/${style.name}/${
typeof file === 'string' ? file : file.path
}`
const resolvedFilePath = path.resolve(filePath)
return typeof file === 'string'
? `"${resolvedFilePath}"`
: `{
path: "${filePath}",
type: "${file.type}",
target: "${file.target ?? ''}",
raw: () => import("@/${filePath}?raw").then((m) => m.default)
}`
})}],
component: () => import("${componentPath}").then((m) => m.default),
raw: () => import("${componentPath}?raw").then((m) => m.default),
source: "",
category: "${item.category ?? ''}",
subcategory: "${item.subcategory ?? ''}"
},`
}
index += `
},`
}
index += `
}
`
// Write style block.
rimraf.sync(path.join(process.cwd(), '__registry__/block.ts'))
await writeFile(path.join(process.cwd(), '__registry__/block.ts'), index)
}
// ----------------------------------------------------------------------------
// Build registry/styles/[style]/[name].json.
// ----------------------------------------------------------------------------
@ -886,6 +970,7 @@ try {
}
await buildRegistry(result.data)
await buildBlockRegistry(result.data)
await buildStyles(result.data)
await buildStylesIndex()
await buildThemes()

View File

@ -3,7 +3,7 @@ title: Sidebar
description: A composable, themeable and customizable sidebar component.
---
<BlockPreview name="Sidebar07" ></BlockPreview>
<BlockPreview url="/blocks/renderer?name=Sidebar07&styles=new-york" ></BlockPreview>
## Installation

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 B

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -10,7 +10,7 @@
"path": "example/AccordionDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/registry/default/ui/accordion'\n\nconst defaultValue = 'item-1'\n\nconst accordionItems = [\n { value: 'item-1', title: 'Is it accessible?', content: 'Yes. It adheres to the WAI-ARIA design pattern.' },\n { value: 'item-2', title: 'Is it unstyled?', content: 'Yes. It\\'s unstyled by default, giving you freedom over the look and feel.' },\n { value: 'item-3', title: 'Can it be animated?', content: 'Yes! You can use the transition prop to configure the animation.' },\n]\n</script>\n\n<template>\n <Accordion type=\"single\" class=\"w-full\" collapsible :default-value=\"defaultValue\">\n <AccordionItem v-for=\"item in accordionItems\" :key=\"item.value\" :value=\"item.value\">\n <AccordionTrigger>{{ item.title }}</AccordionTrigger>\n <AccordionContent>\n {{ item.content }}\n </AccordionContent>\n </AccordionItem>\n </Accordion>\n</template>\n",
"type": "registry:example",
"target": "AccordionDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/AlertDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Alert, AlertDescription, AlertTitle } from '@/registry/default/ui/alert'\nimport { Terminal } from 'lucide-vue-next'\n</script>\n\n<template>\n <Alert>\n <Terminal class=\"h-4 w-4\" />\n <AlertTitle>Heads up!</AlertTitle>\n <AlertDescription>\n You can add components to your app using the cli.\n </AlertDescription>\n </Alert>\n</template>\n",
"type": "registry:example",
"target": "AlertDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/AlertDestructiveDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Alert, AlertDescription, AlertTitle } from '@/registry/default/ui/alert'\nimport { AlertCircle } from 'lucide-vue-next'\n</script>\n\n<template>\n <Alert variant=\"destructive\">\n <AlertCircle class=\"w-4 h-4\" />\n <AlertTitle>Error</AlertTitle>\n <AlertDescription>\n Your session has expired. Please log in again.\n </AlertDescription>\n </Alert>\n</template>\n",
"type": "registry:example",
"target": "AlertDestructiveDemo.vue"
"target": ""
}
]
}

View File

@ -11,7 +11,7 @@
"path": "example/AlertDialogDemo.vue",
"content": "<script setup lang=\"ts\">\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n} from '@/registry/default/ui/alert-dialog'\nimport { Button } from '@/registry/default/ui/button'\n</script>\n\n<template>\n <AlertDialog>\n <AlertDialogTrigger as-child>\n <Button variant=\"outline\">\n Show Dialog\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>\n <AlertDialogDescription>\n This action cannot be undone. This will permanently delete your\n account and remove your data from our servers.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction>Continue</AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n</template>\n",
"type": "registry:example",
"target": "AlertDialogDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/AreaChartCustomTooltip.vue",
"content": "<script setup lang=\"ts\">\nimport { AreaChart } from '@/registry/default/ui/chart-area'\nimport CustomChartTooltip from './CustomChartTooltip.vue'\n\nconst data = [\n { name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n]\n</script>\n\n<template>\n <AreaChart\n index=\"name\"\n :data=\"data\"\n :categories=\"['total', 'predicted']\"\n :custom-tooltip=\"CustomChartTooltip\"\n />\n</template>\n",
"type": "registry:example",
"target": "AreaChartCustomTooltip.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/AreaChartDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { AreaChart } from '@/registry/default/ui/chart-area'\n\nconst data = [\n { name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n]\n</script>\n\n<template>\n <AreaChart :data=\"data\" index=\"name\" :categories=\"['total', 'predicted']\" />\n</template>\n",
"type": "registry:example",
"target": "AreaChartDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/AreaChartSparkline.vue",
"content": "<script setup lang=\"ts\">\nimport { AreaChart } from '@/registry/default/ui/chart-area'\nimport { CurveType } from '@unovis/ts'\n\nconst data = [\n { name: 'Jan', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Feb', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Mar', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Apr', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'May', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Jun', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Jul', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Aug', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Sep', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Oct', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Nov', total: Math.floor(Math.random() * 2000) + 1000 },\n { name: 'Dec', total: Math.floor(Math.random() * 2000) + 1000 },\n]\n</script>\n\n<template>\n <AreaChart\n class=\"h-[100px] w-[400px]\"\n index=\"name\"\n :data=\"data\"\n :categories=\"['total']\"\n :show-grid-line=\"false\"\n :show-legend=\"false\"\n :show-x-axis=\"false\"\n :show-y-axis=\"false\"\n :curve-type=\"CurveType.Linear\"\n />\n</template>\n",
"type": "registry:example",
"target": "AreaChartSparkline.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/AspectRatioDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { AspectRatio } from '@/registry/default/ui/aspect-ratio'\n</script>\n\n<template>\n <AspectRatio :ratio=\"16 / 9\" class=\"bg-muted\">\n <img\n src=\"https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80\"\n alt=\"Photo by Drew Beamer\"\n class=\"rounded-md object-cover w-full h-full\"\n >\n </AspectRatio>\n</template>\n",
"type": "registry:example",
"target": "AspectRatioDemo.vue"
"target": ""
}
]
}

View File

@ -13,7 +13,7 @@
"path": "block/Authentication01.vue",
"content": "<script lang=\"ts\">\nexport const description\n = 'A simple login form with email and password. The submit button says \\'Sign in\\'.'\nexport const iframeHeight = '600px'\nexport const containerClass = 'w-full h-screen flex items-center justify-center px-4'\n</script>\n\n<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/registry/default/ui/card'\nimport { Input } from '@/registry/default/ui/input'\nimport { Label } from '@/registry/default/ui/label'\n</script>\n\n<template>\n <Card class=\"w-full max-w-sm\">\n <CardHeader>\n <CardTitle class=\"text-2xl\">\n Login\n </CardTitle>\n <CardDescription>\n Enter your email below to login to your account.\n </CardDescription>\n </CardHeader>\n <CardContent class=\"grid gap-4\">\n <div class=\"grid gap-2\">\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"m@example.com\" required />\n </div>\n <div class=\"grid gap-2\">\n <Label for=\"password\">Password</Label>\n <Input id=\"password\" type=\"password\" required />\n </div>\n </CardContent>\n <CardFooter>\n <Button class=\"w-full\">\n Sign in\n </Button>\n </CardFooter>\n </Card>\n</template>\n",
"type": "registry:block",
"target": "Authentication01.vue"
"target": "pages/dashboard/index.vue"
}
]
}

View File

@ -13,7 +13,7 @@
"path": "block/Authentication02.vue",
"content": "<script lang=\"ts\">\nexport const description\n = 'A login form with email and password. There\\'s an option to login with Google and a link to sign up if you don\\'t have an account.'\nexport const iframeHeight = '600px'\nexport const containerClass = 'w-full h-screen flex items-center justify-center px-4'\n</script>\n\n<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/registry/default/ui/card'\nimport { Input } from '@/registry/default/ui/input'\nimport { Label } from '@/registry/default/ui/label'\n</script>\n\n<template>\n <Card class=\"mx-auto max-w-sm\">\n <CardHeader>\n <CardTitle class=\"text-2xl\">\n Login\n </CardTitle>\n <CardDescription>\n Enter your email below to login to your account\n </CardDescription>\n </CardHeader>\n <CardContent>\n <div class=\"grid gap-4\">\n <div class=\"grid gap-2\">\n <Label for=\"email\">Email</Label>\n <Input\n id=\"email\"\n type=\"email\"\n placeholder=\"m@example.com\"\n required\n />\n </div>\n <div class=\"grid gap-2\">\n <div class=\"flex items-center\">\n <Label for=\"password\">Password</Label>\n <a href=\"#\" class=\"ml-auto inline-block text-sm underline\">\n Forgot your password?\n </a>\n </div>\n <Input id=\"password\" type=\"password\" required />\n </div>\n <Button type=\"submit\" class=\"w-full\">\n Login\n </Button>\n <Button variant=\"outline\" class=\"w-full\">\n Login with Google\n </Button>\n </div>\n <div class=\"mt-4 text-center text-sm\">\n Don't have an account?\n <a href=\"#\" class=\"underline\">\n Sign up\n </a>\n </div>\n </CardContent>\n </Card>\n</template>\n",
"type": "registry:block",
"target": "Authentication02.vue"
"target": "pages/dashboard/index.vue"
}
]
}

View File

@ -13,7 +13,7 @@
"path": "block/Authentication03.vue",
"content": "<script lang=\"ts\">\nexport const description\n = 'A sign up form with first name, last name, email and password inside a card. There\\'s an option to sign up with GitHub and a link to login if you already have an account'\nexport const iframeHeight = '600px'\nexport const containerClass = 'w-full h-screen flex items-center justify-center px-4'\n</script>\n\n<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/registry/default/ui/card'\nimport { Input } from '@/registry/default/ui/input'\nimport { Label } from '@/registry/default/ui/label'\n</script>\n\n<template>\n <Card class=\"mx-auto max-w-sm\">\n <CardHeader>\n <CardTitle class=\"text-xl\">\n Sign Up\n </CardTitle>\n <CardDescription>\n Enter your information to create an account\n </CardDescription>\n </CardHeader>\n <CardContent>\n <div class=\"grid gap-4\">\n <div class=\"grid grid-cols-2 gap-4\">\n <div class=\"grid gap-2\">\n <Label for=\"first-name\">First name</Label>\n <Input id=\"first-name\" placeholder=\"Max\" required />\n </div>\n <div class=\"grid gap-2\">\n <Label for=\"last-name\">Last name</Label>\n <Input id=\"last-name\" placeholder=\"Robinson\" required />\n </div>\n </div>\n <div class=\"grid gap-2\">\n <Label for=\"email\">Email</Label>\n <Input\n id=\"email\"\n type=\"email\"\n placeholder=\"m@example.com\"\n required\n />\n </div>\n <div class=\"grid gap-2\">\n <Label for=\"password\">Password</Label>\n <Input id=\"password\" type=\"password\" />\n </div>\n <Button type=\"submit\" class=\"w-full\">\n Create an account\n </Button>\n <Button variant=\"outline\" class=\"w-full\">\n Sign up with GitHub\n </Button>\n </div>\n <div class=\"mt-4 text-center text-sm\">\n Already have an account?\n <a href=\"#\" class=\"underline\">\n Sign in\n </a>\n </div>\n </CardContent>\n </Card>\n</template>\n",
"type": "registry:block",
"target": "Authentication03.vue"
"target": "pages/dashboard/index.vue"
}
]
}

View File

@ -12,7 +12,7 @@
"path": "block/Authentication04.vue",
"content": "<script lang=\"ts\">\nexport const description\n = 'A login page with two columns. The first column has the login form with email and password. There\\'s a Forgot your passwork link and a link to sign up if you do not have an account. The second column has a cover image.'\nexport const iframeHeight = '800px'\nexport const containerClass = 'w-full h-full p-4 lg:p-0'\n</script>\n\n<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport { Input } from '@/registry/default/ui/input'\nimport { Label } from '@/registry/default/ui/label'\n</script>\n\n<template>\n <div class=\"w-full lg:grid lg:min-h-[600px] lg:grid-cols-2 xl:min-h-[800px]\">\n <div class=\"flex items-center justify-center py-12\">\n <div class=\"mx-auto grid w-[350px] gap-6\">\n <div class=\"grid gap-2 text-center\">\n <h1 class=\"text-3xl font-bold\">\n Login\n </h1>\n <p class=\"text-balance text-muted-foreground\">\n Enter your email below to login to your account\n </p>\n </div>\n <div class=\"grid gap-4\">\n <div class=\"grid gap-2\">\n <Label for=\"email\">Email</Label>\n <Input\n id=\"email\"\n type=\"email\"\n placeholder=\"m@example.com\"\n required\n />\n </div>\n <div class=\"grid gap-2\">\n <div class=\"flex items-center\">\n <Label for=\"password\">Password</Label>\n <a\n href=\"/forgot-password\"\n class=\"ml-auto inline-block text-sm underline\"\n >\n Forgot your password?\n </a>\n </div>\n <Input id=\"password\" type=\"password\" required />\n </div>\n <Button type=\"submit\" class=\"w-full\">\n Login\n </Button>\n <Button variant=\"outline\" class=\"w-full\">\n Login with Google\n </Button>\n </div>\n <div class=\"mt-4 text-center text-sm\">\n Don't have an account?\n <a href=\"#\" class=\"underline\">\n Sign up\n </a>\n </div>\n </div>\n </div>\n <div class=\"hidden bg-muted lg:block\">\n <img\n src=\"/placeholder.svg\"\n alt=\"Image\"\n width=\"1920\"\n height=\"1080\"\n class=\"h-full w-full object-cover dark:brightness-[0.2] dark:grayscale\"\n >\n </div>\n </div>\n</template>\n",
"type": "registry:block",
"target": "Authentication04.vue"
"target": "pages/dashboard/index.vue"
}
]
}

View File

@ -12,7 +12,7 @@
"path": "example/AutoFormApi.vue",
"content": "<script setup lang=\"ts\">\nimport { AutoForm } from '@/registry/default/ui/auto-form'\nimport { Button } from '@/registry/default/ui/button'\nimport { toast } from '@/registry/default/ui/toast'\nimport { h, onMounted, shallowRef } from 'vue'\nimport * as z from 'zod'\n\nconst schema = shallowRef<z.ZodObject< any, any, any > | null>(null)\n\nonMounted(() => {\n fetch('https://jsonplaceholder.typicode.com/users')\n .then(response => response.json())\n .then((data) => {\n schema.value = z.object({\n user: z.enum(data.map((user: any) => user.name)),\n })\n })\n})\n\nfunction onSubmit(values: Record<string, any>) {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n}\n</script>\n\n<template>\n <div class=\"flex justify-center w-full\">\n <AutoForm\n v-if=\"schema\"\n class=\"w-2/3 space-y-6\"\n :schema=\"schema\"\n @submit=\"onSubmit\"\n >\n <Button type=\"submit\">\n Submit\n </Button>\n </AutoForm>\n\n <div v-else>\n Loading...\n </div>\n </div>\n</template>\n",
"type": "registry:example",
"target": "AutoFormApi.vue"
"target": ""
}
]
}

View File

@ -12,7 +12,7 @@
"path": "example/AutoFormArray.vue",
"content": "<script setup lang=\"ts\">\nimport { AutoForm } from '@/registry/default/ui/auto-form'\nimport { Button } from '@/registry/default/ui/button'\nimport { toast } from '@/registry/default/ui/toast'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst schema = z.object({\n guestListName: z.string(),\n invitedGuests: z\n .array(\n z.object({\n name: z.string(),\n age: z.coerce.number(),\n }),\n )\n .describe('Guests invited to the party'),\n})\n\nfunction onSubmit(values: Record<string, any>) {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n}\n</script>\n\n<template>\n <AutoForm\n class=\"w-2/3 space-y-6\"\n :schema=\"schema\"\n @submit=\"onSubmit\"\n >\n <Button type=\"submit\">\n Submit\n </Button>\n </AutoForm>\n</template>\n",
"type": "registry:example",
"target": "AutoFormArray.vue"
"target": ""
}
]
}

View File

@ -12,7 +12,7 @@
"path": "example/AutoFormBasic.vue",
"content": "<script setup lang=\"ts\">\nimport { AutoForm, AutoFormField } from '@/registry/default/ui/auto-form'\nimport { Button } from '@/registry/default/ui/button'\nimport { toast } from '@/registry/default/ui/toast'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nenum Sports {\n Football = 'Football/Soccer',\n Basketball = 'Basketball',\n Baseball = 'Baseball',\n Hockey = 'Hockey (Ice)',\n None = 'I don\\'t like sports',\n}\n\nconst schema = z.object({\n username: z\n .string({\n required_error: 'Username is required.',\n })\n .min(2, {\n message: 'Username must be at least 2 characters.',\n }),\n\n password: z\n .string({\n required_error: 'Password is required.',\n })\n .min(8, {\n message: 'Password must be at least 8 characters.',\n }),\n\n favouriteNumber: z.coerce\n .number({\n invalid_type_error: 'Favourite number must be a number.',\n })\n .min(1, {\n message: 'Favourite number must be at least 1.',\n })\n .max(10, {\n message: 'Favourite number must be at most 10.',\n })\n .default(1)\n .optional(),\n\n acceptTerms: z\n .boolean()\n .refine(value => value, {\n message: 'You must accept the terms and conditions.',\n path: ['acceptTerms'],\n }),\n\n sendMeMails: z.boolean().optional(),\n\n birthday: z.coerce.date().optional(),\n\n color: z.enum(['red', 'green', 'blue']).optional(),\n\n // Another enum example\n marshmallows: z\n .enum(['not many', 'a few', 'a lot', 'too many']),\n\n // Native enum example\n sports: z.nativeEnum(Sports).describe('What is your favourite sport?'),\n\n bio: z\n .string()\n .min(10, {\n message: 'Bio must be at least 10 characters.',\n })\n .max(160, {\n message: 'Bio must not be longer than 30 characters.',\n })\n .optional(),\n\n customParent: z.string().optional(),\n\n file: z.string().optional(),\n})\n\nfunction onSubmit(values: Record<string, any>) {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n}\n</script>\n\n<template>\n <AutoForm\n class=\"w-2/3 space-y-6\"\n :schema=\"schema\"\n :field-config=\"{\n password: {\n label: 'Your secure password',\n inputProps: {\n type: 'password',\n placeholder: '••••••••',\n },\n },\n favouriteNumber: {\n description: 'Your favourite number between 1 and 10.',\n },\n acceptTerms: {\n label: 'Accept terms and conditions.',\n inputProps: {\n required: true,\n },\n },\n\n birthday: {\n description: 'We need your birthday to send you a gift.',\n },\n\n sendMeMails: {\n component: 'switch',\n },\n\n bio: {\n component: 'textarea',\n },\n\n marshmallows: {\n label: 'How many marshmallows fit in your mouth?',\n component: 'radio',\n },\n\n file: {\n label: 'Text file',\n component: 'file',\n },\n }\"\n @submit=\"onSubmit\"\n >\n <template #acceptTerms=\"slotProps\">\n <AutoFormField v-bind=\"slotProps\" />\n <div class=\"!mt-2 text-sm\">\n I agree to the <button class=\"text-primary underline\">\n terms and conditions\n </button>.\n </div>\n </template>\n\n <template #customParent=\"slotProps\">\n <div class=\"flex items-end space-x-2\">\n <AutoFormField v-bind=\"slotProps\" class=\"w-full\" />\n <Button type=\"button\">\n Check\n </Button>\n </div>\n </template>\n\n <Button type=\"submit\">\n Submit\n </Button>\n </AutoForm>\n</template>\n",
"type": "registry:example",
"target": "AutoFormBasic.vue"
"target": ""
}
]
}

View File

@ -12,7 +12,7 @@
"path": "example/AutoFormConfirmPassword.vue",
"content": "<script setup lang=\"ts\">\nimport { AutoForm } from '@/registry/default/ui/auto-form'\nimport { Button } from '@/registry/default/ui/button'\nimport { toast } from '@/registry/default/ui/toast'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst schema = z\n .object({\n password: z.string(),\n confirm: z.string(),\n })\n .refine(data => data.password === data.confirm, {\n message: 'Passwords must match.',\n path: ['confirm'],\n })\n\nfunction onSubmit(values: Record<string, any>) {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n}\n</script>\n\n<template>\n <AutoForm\n class=\"w-2/3 space-y-6\"\n :schema=\"schema\"\n @submit=\"onSubmit\"\n >\n <Button type=\"submit\">\n Submit\n </Button>\n </AutoForm>\n</template>\n",
"type": "registry:example",
"target": "AutoFormConfirmPassword.vue"
"target": ""
}
]
}

View File

@ -16,7 +16,7 @@
"path": "example/AutoFormControlled.vue",
"content": "<script setup lang=\"ts\">\nimport { AutoForm } from '@/registry/default/ui/auto-form'\nimport { Button } from '@/registry/default/ui/button'\nimport { toast } from '@/registry/default/ui/toast'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst schema = z.object({\n username: z.string(),\n})\n\nconst form = useForm({\n validationSchema: toTypedSchema(schema),\n})\n\nfunction onSubmit(values: Record<string, any>) {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n}\n</script>\n\n<template>\n <AutoForm\n class=\"w-2/3 space-y-6\"\n :schema=\"schema\"\n :form=\"form\"\n @submit=\"onSubmit\"\n >\n <Button type=\"submit\">\n Submit\n </Button>\n </AutoForm>\n</template>\n",
"type": "registry:example",
"target": "AutoFormControlled.vue"
"target": ""
}
]
}

View File

@ -12,7 +12,7 @@
"path": "example/AutoFormDependencies.vue",
"content": "<script setup lang=\"ts\">\nimport { AutoForm } from '@/registry/default/ui/auto-form'\nimport { Button } from '@/registry/default/ui/button'\nimport { toast } from '@/registry/default/ui/toast'\nimport { h } from 'vue'\nimport * as z from 'zod'\nimport { DependencyType } from '../ui/auto-form/interface'\n\nconst schema = z.object({\n age: z.number(),\n parentsAllowed: z.boolean().optional(),\n vegetarian: z.boolean().optional(),\n mealOptions: z.enum(['Pasta', 'Salad', 'Beef Wellington']).optional(),\n})\n\nfunction onSubmit(values: Record<string, any>) {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n}\n</script>\n\n<template>\n <AutoForm\n class=\"w-2/3 space-y-6\"\n :schema=\"schema\"\n :field-config=\"{\n age: {\n description:\n 'Setting this below 18 will require parents consent.',\n },\n parentsAllowed: {\n label: 'Did your parents allow you to register?',\n },\n vegetarian: {\n label: 'Are you a vegetarian?',\n description:\n 'Setting this to true will remove non-vegetarian food options.',\n },\n mealOptions: {\n component: 'radio',\n },\n }\"\n :dependencies=\"[\n {\n sourceField: 'age',\n type: DependencyType.HIDES,\n targetField: 'parentsAllowed',\n when: (age) => age >= 18,\n },\n {\n sourceField: 'age',\n type: DependencyType.REQUIRES,\n targetField: 'parentsAllowed',\n when: (age) => age < 18,\n },\n {\n sourceField: 'vegetarian',\n type: DependencyType.SETS_OPTIONS,\n targetField: 'mealOptions',\n when: (vegetarian) => vegetarian,\n options: ['Pasta', 'Salad'],\n },\n ]\"\n @submit=\"onSubmit\"\n >\n <Button type=\"submit\">\n Submit\n </Button>\n </AutoForm>\n</template>\n",
"type": "registry:example",
"target": "AutoFormDependencies.vue"
"target": ""
}
]
}

View File

@ -12,7 +12,7 @@
"path": "example/AutoFormInputWithoutLabel.vue",
"content": "<script setup lang=\"ts\">\nimport { AutoForm, AutoFormField } from '@/registry/default/ui/auto-form'\nimport { Button } from '@/registry/default/ui/button'\nimport { toast } from '@/registry/default/ui/toast'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst schema = z.object({\n username: z.string(),\n})\n\nfunction onSubmit(values: Record<string, any>) {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n}\n</script>\n\n<template>\n <AutoForm\n class=\"w-2/3 space-y-6\"\n :schema=\"schema\"\n :field-config=\"{\n username: {\n hideLabel: true,\n },\n }\"\n @submit=\"onSubmit\"\n >\n <template #username=\"slotProps\">\n <div class=\"flex items-start gap-3\">\n <div class=\"flex-1\">\n <AutoFormField v-bind=\"slotProps\" />\n </div>\n <div>\n <Button type=\"submit\">\n Update\n </Button>\n </div>\n </div>\n </template>\n </AutoForm>\n</template>\n",
"type": "registry:example",
"target": "AutoFormInputWithoutLabel.vue"
"target": ""
}
]
}

View File

@ -12,7 +12,7 @@
"path": "example/AutoFormSubObject.vue",
"content": "<script setup lang=\"ts\">\nimport { AutoForm } from '@/registry/default/ui/auto-form'\nimport { Button } from '@/registry/default/ui/button'\nimport { toast } from '@/registry/default/ui/toast'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst schema = z.object({\n subObject: z.object({\n subField: z.string().optional().default('Sub Field'),\n numberField: z.number().optional().default(1),\n\n subSubObject: z\n .object({\n subSubField: z.string().default('Sub Sub Field'),\n })\n .describe('Sub Sub Object Description'),\n }),\n optionalSubObject: z\n .object({\n optionalSubField: z.string(),\n otherOptionalSubField: z.string(),\n })\n .optional(),\n})\n\nfunction onSubmit(values: Record<string, any>) {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n}\n</script>\n\n<template>\n <AutoForm\n class=\"w-2/3 space-y-6\"\n :schema=\"schema\"\n :field-config=\"{\n subObject: {\n numberField: {\n inputProps: {\n type: 'number',\n },\n },\n },\n }\"\n @submit=\"onSubmit\"\n >\n <Button type=\"submit\">\n Submit\n </Button>\n </AutoForm>\n</template>\n",
"type": "registry:example",
"target": "AutoFormSubObject.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/AvatarDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Avatar, AvatarFallback, AvatarImage } from '@/registry/default/ui/avatar'\n</script>\n\n<template>\n <Avatar>\n <AvatarImage src=\"https://github.com/unovue.png\" alt=\"@unovue\" />\n <AvatarFallback>CN</AvatarFallback>\n </Avatar>\n</template>\n",
"type": "registry:example",
"target": "AvatarDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BadgeDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Badge } from '@/registry/default/ui/badge'\n</script>\n\n<template>\n <Badge>Badge</Badge>\n</template>\n",
"type": "registry:example",
"target": "BadgeDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BadgeDestructiveDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Badge } from '@/registry/default/ui/badge'\n</script>\n\n<template>\n <Badge variant=\"destructive\">\n Destructive\n </Badge>\n</template>\n",
"type": "registry:example",
"target": "BadgeDestructiveDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BadgeOutlineDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Badge } from '@/registry/default/ui/badge'\n</script>\n\n<template>\n <Badge variant=\"outline\">\n Outline\n </Badge>\n</template>\n",
"type": "registry:example",
"target": "BadgeOutlineDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BadgeSecondaryDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Badge } from '@/registry/default/ui/badge'\n</script>\n\n<template>\n <Badge variant=\"secondary\">\n Secondary\n </Badge>\n</template>\n",
"type": "registry:example",
"target": "BadgeSecondaryDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BarChartCustomTooltip.vue",
"content": "<script setup lang=\"ts\">\nimport { BarChart } from '@/registry/default/ui/chart-bar'\nimport CustomChartTooltip from './CustomChartTooltip.vue'\n\nconst data = [\n { name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n]\n</script>\n\n<template>\n <BarChart\n :data=\"data\"\n index=\"name\"\n :categories=\"['total', 'predicted']\"\n :y-formatter=\"(tick, i) => {\n return typeof tick === 'number'\n ? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`\n : ''\n }\"\n :custom-tooltip=\"CustomChartTooltip\"\n />\n</template>\n",
"type": "registry:example",
"target": "BarChartCustomTooltip.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BarChartDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { BarChart } from '@/registry/default/ui/chart-bar'\n\nconst data = [\n { name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n]\n</script>\n\n<template>\n <BarChart\n :data=\"data\"\n index=\"name\"\n :categories=\"['total', 'predicted']\"\n :y-formatter=\"(tick, i) => {\n return typeof tick === 'number'\n ? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`\n : ''\n }\"\n />\n</template>\n",
"type": "registry:example",
"target": "BarChartDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BarChartRounded.vue",
"content": "<script setup lang=\"ts\">\nimport { BarChart } from '@/registry/default/ui/chart-bar'\n\nconst data = [\n { name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n]\n</script>\n\n<template>\n <BarChart\n index=\"name\"\n :data=\"data\"\n :categories=\"['total', 'predicted']\"\n :y-formatter=\"(tick, i) => {\n return typeof tick === 'number'\n ? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`\n : ''\n }\"\n :rounded-corners=\"4\"\n />\n</template>\n",
"type": "registry:example",
"target": "BarChartRounded.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BarChartStacked.vue",
"content": "<script setup lang=\"ts\">\nimport { BarChart } from '@/registry/default/ui/chart-bar'\n\nconst data = [\n { name: 'Jan', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Feb', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Mar', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Apr', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'May', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jun', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n { name: 'Jul', total: Math.floor(Math.random() * 2000) + 500, predicted: Math.floor(Math.random() * 2000) + 500 },\n]\n</script>\n\n<template>\n <BarChart\n index=\"name\"\n :data=\"data\"\n :categories=\"['total', 'predicted']\"\n :y-formatter=\"(tick, i) => {\n return typeof tick === 'number'\n ? `$ ${new Intl.NumberFormat('us').format(tick).toString()}`\n : ''\n }\"\n :type=\"'stacked'\"\n />\n</template>\n",
"type": "registry:example",
"target": "BarChartStacked.vue"
"target": ""
}
]
}

View File

@ -11,7 +11,7 @@
"path": "example/BreadcrumbDemo.vue",
"content": "<script lang=\"ts\" setup>\nimport {\n Breadcrumb,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '@/registry/default/ui/breadcrumb'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@/registry/default/ui/dropdown-menu'\n</script>\n\n<template>\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"/\">\n Home\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <DropdownMenu>\n <DropdownMenuTrigger class=\"flex items-center gap-1\">\n <BreadcrumbEllipsis class=\"h-4 w-4\" />\n <span class=\"sr-only\">Toggle menu</span>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuItem>Documentation</DropdownMenuItem>\n <DropdownMenuItem>Themes</DropdownMenuItem>\n <DropdownMenuItem>GitHub</DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbLink href=\"/docs/components/accordion.html\">\n Components\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage>Breadcrumb</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n</template>\n",
"type": "registry:example",
"target": "BreadcrumbDemo.vue"
"target": ""
}
]
}

View File

@ -11,7 +11,7 @@
"path": "example/BreadcrumbDropdown.vue",
"content": "<script lang=\"ts\" setup>\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '@/registry/default/ui/breadcrumb'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@/registry/default/ui/dropdown-menu'\nimport { ChevronDown, Slash } from 'lucide-vue-next'\n</script>\n\n<template>\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"/\">\n Home\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\n <Slash />\n </BreadcrumbSeparator>\n <BreadcrumbItem>\n <DropdownMenu>\n <DropdownMenuTrigger class=\"flex items-center gap-1\">\n Components\n <ChevronDown class=\"h-4 w-4\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuItem>Documentation</DropdownMenuItem>\n <DropdownMenuItem>Themes</DropdownMenuItem>\n <DropdownMenuItem>GitHub</DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\n <Slash />\n </BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbPage>Breadcrumb</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n</template>\n",
"type": "registry:example",
"target": "BreadcrumbDropdown.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BreadcrumbEllipsisDemo.vue",
"content": "<script lang=\"ts\" setup>\nimport {\n Breadcrumb,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '@/registry/default/ui/breadcrumb'\n</script>\n\n<template>\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink as-child>\n <a href=\"/\">\n Home\n </a>\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbEllipsis />\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbLink as-child>\n <a href=\"/docs/components/accordion.html\">\n Components\n </a>\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage>Breadcrumb</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n</template>\n",
"type": "registry:example",
"target": "BreadcrumbEllipsisDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BreadcrumbLinkDemo.vue",
"content": "<script lang=\"ts\" setup>\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '@/registry/default/ui/breadcrumb'\n</script>\n\n<template>\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink>\n <a href=\"/\">\n Home\n </a>\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbLink>\n <a href=\"/docs/components/accordion.html\">\n Components\n </a>\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage>Breadcrumb</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n</template>\n",
"type": "registry:example",
"target": "BreadcrumbLinkDemo.vue"
"target": ""
}
]
}

View File

@ -15,7 +15,7 @@
"path": "example/BreadcrumbResponsive.vue",
"content": "<script lang=\"ts\" setup>\nimport {\n Breadcrumb,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '@/registry/default/ui/breadcrumb'\nimport { Button } from '@/registry/default/ui/button'\nimport {\n Drawer,\n DrawerClose,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n DrawerTrigger,\n} from '@/registry/default/ui/drawer'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@/registry/default/ui/dropdown-menu'\nimport { useMediaQuery } from '@vueuse/core'\nimport { computed, ref } from 'vue'\n\nconst isDesktop = useMediaQuery('(min-width: 768px)')\nconst isOpen = ref(false)\nconst items = ref([\n { href: '#', label: 'Home' },\n { href: '#', label: 'Documentation' },\n { href: '#', label: 'Building Your Application' },\n { href: '#', label: 'Data Fetching' },\n { label: 'Caching and Revalidating' },\n])\n\nconst itemsToDisplay = 3\nconst firstLabel = computed(() => items.value[0]?.label)\n\nconst allButLastTwoItems = computed(() => items.value.slice(1, -2))\nconst remainingItems = computed(() => items.value.slice(-itemsToDisplay + 1))\n</script>\n\n<template>\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"{items[0].href}\">\n {{ firstLabel }}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <template v-if=\"items.length > itemsToDisplay\">\n <BreadcrumbItem>\n <DropdownMenu v-if=\"isDesktop\" v-model:open=\"isOpen\">\n <DropdownMenuTrigger\n class=\"flex items-center gap-1\"\n aria-label=\"Toggle menu\"\n >\n <BreadcrumbEllipsis class=\"h-4 w-4\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuItem v-for=\"item of allButLastTwoItems\" :key=\"item.label\">\n <a :href=\"item.href || '#'\">\n {{ item.label }}\n </a>\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n <Drawer v-else v-model:open=\"isOpen\">\n <DrawerTrigger aria-label=\"Toggle Menu\">\n <BreadcrumbEllipsis class=\"h-4 w-4\" />\n </DrawerTrigger>\n <DrawerContent>\n <DrawerHeader class=\"text-left\">\n <DrawerTitle>Navigate to</DrawerTitle>\n <DrawerDescription>\n Select a page to navigate to.\n </DrawerDescription>\n </DrawerHeader>\n <div class=\"grid gap-1 px-4\">\n <a\n v-for=\"item of allButLastTwoItems\"\n :key=\"item.label\"\n :href=\"item.href\"\n class=\"py-1 text-sm\"\n >\n {{ item.label }}\n </a>\n </div>\n <DrawerFooter class=\"pt-4\">\n <DrawerClose as-child>\n <Button variant=\"outline\">\n Close\n </Button>\n </DrawerClose>\n </DrawerFooter>\n </DrawerContent>\n </Drawer>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n </template>\n <BreadcrumbItem v-for=\" item of remainingItems\" :key=\"item.label\">\n <template v-if=\"item.href\">\n <BreadcrumbLink\n as-child\n class=\"max-w-20 truncate md:max-w-none\"\n >\n <a :href=\"item.href\">\n {{ item.label }}\n </a>\n </BreadcrumbLink>\n <BreadcrumbSeparator />\n </template>\n <BreadcrumbPage v-else class=\"max-w-20 truncate md:max-w-none\">\n {{ item.label }}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n</template>\n",
"type": "registry:example",
"target": "BreadcrumbResponsive.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/BreadcrumbSeparatorDemo.vue",
"content": "<script lang=\"ts\" setup>\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '@/registry/default/ui/breadcrumb'\nimport { Slash } from 'lucide-vue-next'\n</script>\n\n<template>\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"/\">\n Home\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\n <Slash />\n </BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"/docs/components/accordion.html\">\n Components\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\n <Slash />\n </BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbPage>Breadcrumb</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n</template>\n",
"type": "registry:example",
"target": "BreadcrumbSeparatorDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonAsChildDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n</script>\n\n<template>\n <Button as-child>\n <a href=\"/login\">\n Login\n </a>\n </Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonAsChildDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n</script>\n\n<template>\n <Button>Button</Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonDestructiveDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n</script>\n\n<template>\n <Button variant=\"destructive\">\n Destructive\n </Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonDestructiveDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonGhostDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n</script>\n\n<template>\n <Button variant=\"ghost\">\n Ghost\n </Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonGhostDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonIconDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport { ChevronRight } from 'lucide-vue-next'\n</script>\n\n<template>\n <Button variant=\"outline\" size=\"icon\">\n <ChevronRight class=\"w-4 h-4\" />\n </Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonIconDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonLinkDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n</script>\n\n<template>\n <Button variant=\"link\">\n Link\n </Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonLinkDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonLoadingDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport { Loader2 } from 'lucide-vue-next'\n</script>\n\n<template>\n <Button disabled>\n <Loader2 class=\"w-4 h-4 mr-2 animate-spin\" />\n Please wait\n </Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonLoadingDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonOutlineDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n</script>\n\n<template>\n <Button variant=\"outline\">\n Outline\n </Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonOutlineDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonSecondaryDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n</script>\n\n<template>\n <Button variant=\"secondary\">\n Secondary\n </Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonSecondaryDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ButtonWithIconDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport { Mail } from 'lucide-vue-next'\n</script>\n\n<template>\n <Button>\n <Mail class=\"w-4 h-4 mr-2\" /> Login with Email\n </Button>\n</template>\n",
"type": "registry:example",
"target": "ButtonWithIconDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/CalendarDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Calendar } from '@/registry/default/ui/calendar'\nimport { type DateValue, getLocalTimeZone, today } from '@internationalized/date'\nimport { type Ref, ref } from 'vue'\n\nconst value = ref(today(getLocalTimeZone())) as Ref<DateValue>\n</script>\n\n<template>\n <Calendar v-model=\"value\" :weekday-format=\"'short'\" class=\"rounded-md border\" />\n</template>\n",
"type": "registry:example",
"target": "CalendarDemo.vue"
"target": ""
}
]
}

View File

@ -19,7 +19,7 @@
"path": "example/CalendarForm.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\nimport { Calendar } from '@/registry/default/ui/calendar'\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/default/ui/form'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/registry/default/ui/popover'\nimport { toast } from '@/registry/default/ui/toast'\nimport { CalendarDate, DateFormatter, getLocalTimeZone, parseDate, today } from '@internationalized/date'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { Calendar as CalendarIcon } from 'lucide-vue-next'\nimport { toDate } from 'reka-ui/date'\nimport { useForm } from 'vee-validate'\nimport { computed, h, ref } from 'vue'\nimport { z } from 'zod'\n\nconst df = new DateFormatter('en-US', {\n dateStyle: 'long',\n})\n\nconst formSchema = toTypedSchema(z.object({\n dob: z\n .string()\n .refine(v => v, { message: 'A date of birth is required.' }),\n}))\n\nconst placeholder = ref()\n\nconst { handleSubmit, setFieldValue, values } = useForm({\n validationSchema: formSchema,\n})\n\nconst value = computed({\n get: () => values.dob ? parseDate(values.dob) : undefined,\n set: val => val,\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form class=\"space-y-8\" @submit=\"onSubmit\">\n <FormField name=\"dob\">\n <FormItem class=\"flex flex-col\">\n <FormLabel>Date of birth</FormLabel>\n <Popover>\n <PopoverTrigger as-child>\n <FormControl>\n <Button\n variant=\"outline\" :class=\"cn(\n 'w-[240px] ps-3 text-start font-normal',\n !value && 'text-muted-foreground',\n )\"\n >\n <span>{{ value ? df.format(toDate(value)) : \"Pick a date\" }}</span>\n <CalendarIcon class=\"ms-auto h-4 w-4 opacity-50\" />\n </Button>\n <input hidden>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent class=\"w-auto p-0\">\n <Calendar\n v-model:placeholder=\"placeholder\"\n v-model=\"value\"\n calendar-label=\"Date of birth\"\n initial-focus\n :min-value=\"new CalendarDate(1900, 1, 1)\"\n :max-value=\"today(getLocalTimeZone())\"\n @update:model-value=\"(v) => {\n if (v) {\n setFieldValue('dob', v.toString())\n }\n else {\n setFieldValue('dob', undefined)\n }\n }\"\n />\n </PopoverContent>\n </Popover>\n <FormDescription>\n Your date of birth is used to calculate your age.\n </FormDescription>\n <FormMessage />\n </FormItem>\n </FormField>\n <Button type=\"submit\">\n Submit\n </Button>\n </Form>\n</template>\n",
"type": "registry:example",
"target": "CalendarForm.vue"
"target": ""
}
]
}

View File

@ -14,7 +14,7 @@
"path": "example/CalendarWithSelect.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading } from '@/registry/default/ui/calendar'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/registry/default/ui/select'\nimport { type DateValue, getLocalTimeZone, today } from '@internationalized/date'\nimport { useVModel } from '@vueuse/core'\nimport { CalendarRoot, type CalendarRootEmits, type CalendarRootProps, useDateFormatter, useForwardPropsEmits } from 'reka-ui'\nimport { createDecade, createYear, toDate } from 'reka-ui/date'\nimport { computed, type HTMLAttributes, type Ref } from 'vue'\n\nconst props = withDefaults(defineProps<CalendarRootProps & { class?: HTMLAttributes['class'] }>(), {\n modelValue: undefined,\n placeholder() {\n return today(getLocalTimeZone())\n },\n weekdayFormat: 'short',\n})\nconst emits = defineEmits<CalendarRootEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, placeholder: __, ...delegated } = props\n\n return delegated\n})\n\nconst placeholder = useVModel(props, 'modelValue', emits, {\n passive: true,\n defaultValue: today(getLocalTimeZone()),\n}) as Ref<DateValue>\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n\nconst formatter = useDateFormatter('en')\n</script>\n\n<template>\n <CalendarRoot\n v-slot=\"{ date, grid, weekDays }\"\n v-model:placeholder=\"placeholder\"\n v-bind=\"forwarded\"\n :class=\"cn('rounded-md border p-3', props.class)\"\n >\n <CalendarHeader>\n <CalendarHeading class=\"flex w-full items-center justify-between gap-2\">\n <Select\n :default-value=\"placeholder.month.toString()\"\n @update:model-value=\"(v) => {\n if (!v || !placeholder) return;\n if (Number(v) === placeholder?.month) return;\n placeholder = placeholder.set({\n month: Number(v),\n })\n }\"\n >\n <SelectTrigger aria-label=\"Select month\" class=\"w-[60%]\">\n <SelectValue placeholder=\"Select month\" />\n </SelectTrigger>\n <SelectContent class=\"max-h-[200px]\">\n <SelectItem\n v-for=\"month in createYear({ dateObj: date })\"\n :key=\"month.toString()\" :value=\"month.month.toString()\"\n >\n {{ formatter.custom(toDate(month), { month: 'long' }) }}\n </SelectItem>\n </SelectContent>\n </Select>\n\n <Select\n :default-value=\"placeholder.year.toString()\"\n @update:model-value=\"(v) => {\n if (!v || !placeholder) return;\n if (Number(v) === placeholder?.year) return;\n placeholder = placeholder.set({\n year: Number(v),\n })\n }\"\n >\n <SelectTrigger aria-label=\"Select year\" class=\"w-[40%]\">\n <SelectValue placeholder=\"Select year\" />\n </SelectTrigger>\n <SelectContent class=\"max-h-[200px]\">\n <SelectItem\n v-for=\"yearValue in createDecade({ dateObj: date, startIndex: -10, endIndex: 10 })\"\n :key=\"yearValue.toString()\" :value=\"yearValue.year.toString()\"\n >\n {{ yearValue.year }}\n </SelectItem>\n </SelectContent>\n </Select>\n </CalendarHeading>\n </CalendarHeader>\n\n <div class=\"flex flex-col space-y-4 pt-4 sm:flex-row sm:gap-x-4 sm:gap-y-0\">\n <CalendarGrid v-for=\"month in grid\" :key=\"month.value.toString()\">\n <CalendarGridHead>\n <CalendarGridRow>\n <CalendarHeadCell\n v-for=\"day in weekDays\" :key=\"day\"\n >\n {{ day }}\n </CalendarHeadCell>\n </CalendarGridRow>\n </CalendarGridHead>\n <CalendarGridBody class=\"grid\">\n <CalendarGridRow v-for=\"(weekDates, index) in month.rows\" :key=\"`weekDate-${index}`\" class=\"mt-2 w-full\">\n <CalendarCell\n v-for=\"weekDate in weekDates\"\n :key=\"weekDate.toString()\"\n :date=\"weekDate\"\n >\n <CalendarCellTrigger\n :day=\"weekDate\"\n :month=\"month.value\"\n />\n </CalendarCell>\n </CalendarGridRow>\n </CalendarGridBody>\n </CalendarGrid>\n </div>\n </CalendarRoot>\n</template>\n",
"type": "registry:example",
"target": "CalendarWithSelect.vue"
"target": ""
}
]
}

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@
"path": "example/CardDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\n\nimport { Button } from '@/registry/default/ui/button'\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from '@/registry/default/ui/card'\nimport { Switch } from '@/registry/default/ui/switch'\nimport { BellRing, Check } from 'lucide-vue-next'\n\nconst notifications = [\n {\n title: 'Your call has been confirmed.',\n description: '1 hour ago',\n },\n {\n title: 'You have a new message!',\n description: '1 hour ago',\n },\n {\n title: 'Your subscription is expiring soon!',\n description: '2 hours ago',\n },\n]\n</script>\n\n<template>\n <Card :class=\"cn('w-[380px]', $attrs.class ?? '')\">\n <CardHeader>\n <CardTitle>Notifications</CardTitle>\n <CardDescription>You have 3 unread messages.</CardDescription>\n </CardHeader>\n <CardContent class=\"grid gap-4\">\n <div class=\" flex items-center space-x-4 rounded-md border p-4\">\n <BellRing />\n <div class=\"flex-1 space-y-1\">\n <p class=\"text-sm font-medium leading-none\">\n Push Notifications\n </p>\n <p class=\"text-sm text-muted-foreground\">\n Send notifications to device.\n </p>\n </div>\n <Switch />\n </div>\n <div>\n <div\n v-for=\"(notification, index) in notifications\" :key=\"index\"\n class=\"mb-4 grid grid-cols-[25px_minmax(0,1fr)] items-start pb-4 last:mb-0 last:pb-0\"\n >\n <span class=\"flex h-2 w-2 translate-y-1 rounded-full bg-sky-500\" />\n <div class=\"space-y-1\">\n <p class=\"text-sm font-medium leading-none\">\n {{ notification.title }}\n </p>\n <p class=\"text-sm text-muted-foreground\">\n {{ notification.description }}\n </p>\n </div>\n </div>\n </div>\n </CardContent>\n <CardFooter>\n <Button class=\"w-full\">\n <Check class=\"mr-2 h-4 w-4\" /> Mark all as read\n </Button>\n </CardFooter>\n </Card>\n</template>\n",
"type": "registry:example",
"target": "CardDemo.vue"
"target": ""
}
]
}

View File

@ -14,7 +14,7 @@
"path": "example/CardFormDemo.vue",
"content": "<script setup lang='ts'>\nimport { Button } from '@/registry/default/ui/button'\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from '@/registry/default/ui/card'\nimport { Input } from '@/registry/default/ui/input'\nimport { Label } from '@/registry/default/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/registry/default/ui/select'\n</script>\n\n<template>\n <Card class=\"w-[350px]\">\n <CardHeader>\n <CardTitle>Create project</CardTitle>\n <CardDescription>Deploy your new project in one-click.</CardDescription>\n </CardHeader>\n <CardContent>\n <form>\n <div class=\"grid items-center w-full gap-4\">\n <div class=\"flex flex-col space-y-1.5\">\n <Label for=\"name\">Name</Label>\n <Input id=\"name\" placeholder=\"Name of your project\" />\n </div>\n <div class=\"flex flex-col space-y-1.5\">\n <Label for=\"framework\">Framework</Label>\n <Select>\n <SelectTrigger id=\"framework\">\n <SelectValue placeholder=\"Select\" />\n </SelectTrigger>\n <SelectContent position=\"popper\">\n <SelectItem value=\"nuxt\">\n Nuxt\n </SelectItem>\n <SelectItem value=\"next\">\n Next.js\n </SelectItem>\n <SelectItem value=\"sveltekit\">\n SvelteKit\n </SelectItem>\n <SelectItem value=\"astro\">\n Astro\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n </form>\n </CardContent>\n <CardFooter class=\"flex justify-between px-6 pb-6\">\n <Button variant=\"outline\">\n Cancel\n </Button>\n <Button>Deploy</Button>\n </CardFooter>\n </Card>\n</template>\n",
"type": "registry:example",
"target": "CardFormDemo.vue"
"target": ""
}
]
}

View File

@ -15,7 +15,7 @@
"path": "example/CardStats.vue",
"content": "<script setup lang=\"ts\">\nimport { Card, CardContent, CardHeader, CardTitle } from '@/registry/default/ui/card'\nimport { themes } from '@/registry/registry-themes'\nimport { useConfigStore } from '@/stores/config'\nimport { VisLine, VisScatter, VisStackedBar, VisXYContainer } from '@unovis/vue'\nimport { useData } from 'vitepress'\nimport { computed } from 'vue'\n\ntype Data = typeof data[number]\nconst data = [\n { revenue: 10400, subscription: 240 },\n { revenue: 14405, subscription: 300 },\n { revenue: 9400, subscription: 200 },\n { revenue: 8200, subscription: 278 },\n { revenue: 7000, subscription: 189 },\n { revenue: 9600, subscription: 239 },\n { revenue: 11244, subscription: 278 },\n { revenue: 26475, subscription: 189 },\n]\n\nconst cfg = useConfigStore()\n\nconst { isDark } = useData()\nconst theme = computed(() => themes.find(theme => theme.name === cfg.config.value.theme))\n\nconst lineX = (d: Data, i: number) => i\nconst lineY = (d: Data) => d.revenue\n</script>\n\n<template>\n <div class=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-2\">\n <Card>\n <CardHeader class=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle class=\"text-sm font-normal\">\n Total Revenue\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div class=\"text-2xl font-bold\">\n $15,231.89\n </div>\n <p class=\"text-xs text-muted-foreground\">\n +20.1% from last month\n </p>\n\n <div class=\"h-20\">\n <VisXYContainer\n height=\"80px\"\n :data=\"data\" :margin=\"{\n top: 5,\n right: 10,\n left: 10,\n bottom: 0,\n }\"\n >\n <VisLine :x=\"lineX\" :y=\"lineY\" color=\"hsl(var(--primary))\" />\n <VisScatter :x=\"lineX\" :y=\"lineY\" :size=\"6\" stroke-color=\"hsl(var(--primary))\" :stroke-width=\"2\" color=\"white\" />\n </VisXYContainer>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader class=\"pb-2\">\n <CardTitle class=\"text-lg\">\n Subscriptions\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div class=\"text-2xl font-bold\">\n +2,350\n </div>\n <p class=\"text-xs text-muted-foreground\">\n +54.8% from last month\n </p>\n\n <div class=\"mt-4 h-20\">\n <VisXYContainer\n height=\"80px\" :data=\"data\" :style=\"{\n '--theme-primary': `hsl(${\n theme?.cssVars?.[isDark ? 'dark' : 'light']?.primary\n })`,\n }\"\n >\n <VisStackedBar\n :x=\"lineX\"\n :y=\"(d: Data) => d.subscription\"\n :bar-padding=\"0.1\"\n :rounded-corners=\"0\" color=\"hsl(var(--primary))\"\n />\n </VisXYContainer>\n </div>\n </CardContent>\n </Card>\n </div>\n</template>\n",
"type": "registry:example",
"target": "CardStats.vue"
"target": ""
}
]
}

View File

@ -14,7 +14,7 @@
"path": "example/CardWithForm.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from '@/registry/default/ui/card'\nimport { Input } from '@/registry/default/ui/input'\nimport { Label } from '@/registry/default/ui/label'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/registry/default/ui/select'\n</script>\n\n<template>\n <Card class=\"w-[350px]\">\n <CardHeader>\n <CardTitle>Create project</CardTitle>\n <CardDescription>Deploy your new project in one-click.</CardDescription>\n </CardHeader>\n <CardContent>\n <form>\n <div class=\"grid w-full items-center gap-4\">\n <div class=\"flex flex-col space-y-1.5\">\n <Label for=\"name\">Name</Label>\n <Input id=\"name\" placeholder=\"Name of your project\" />\n </div>\n <div class=\"flex flex-col space-y-1.5\">\n <Label for=\"framework\">Framework</Label>\n <Select>\n <SelectTrigger id=\"framework\">\n <SelectValue placeholder=\"Select\" />\n </SelectTrigger>\n <SelectContent position=\"popper\">\n <SelectItem value=\"next\">\n Next.js\n </SelectItem>\n <SelectItem value=\"sveltekit\">\n SvelteKit\n </SelectItem>\n <SelectItem value=\"astro\">\n Astro\n </SelectItem>\n <SelectItem value=\"nuxt\">\n Nuxt\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n </form>\n </CardContent>\n <CardFooter class=\"flex justify-between\">\n <Button variant=\"outline\">\n Cancel\n </Button>\n <Button>Deploy</Button>\n </CardFooter>\n </Card>\n</template>\n",
"type": "registry:example",
"target": "CardWithForm.vue"
"target": ""
}
]
}

View File

@ -13,7 +13,7 @@
"path": "example/CarouselApi.vue",
"content": "<script setup lang=\"ts\">\nimport type { CarouselApi } from '@/registry/default/ui/carousel'\nimport { Card, CardContent } from '@/registry/default/ui/card'\nimport { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/registry/default/ui/carousel'\nimport { watchOnce } from '@vueuse/core'\nimport { ref } from 'vue'\n\nconst api = ref<CarouselApi>()\nconst totalCount = ref(0)\nconst current = ref(0)\n\nfunction setApi(val: CarouselApi) {\n api.value = val\n}\n\nwatchOnce(api, (api) => {\n if (!api)\n return\n\n totalCount.value = api.scrollSnapList().length\n current.value = api.selectedScrollSnap() + 1\n\n api.on('select', () => {\n current.value = api.selectedScrollSnap() + 1\n })\n})\n</script>\n\n<template>\n <div class=\"w-full sm:w-auto\">\n <Carousel class=\"relative w-full max-w-xs\" @init-api=\"setApi\">\n <CarouselContent>\n <CarouselItem v-for=\"(_, index) in 5\" :key=\"index\">\n <div class=\"p-1\">\n <Card>\n <CardContent class=\"flex aspect-square items-center justify-center p-6\">\n <span class=\"text-4xl font-semibold\">{{ index + 1 }}</span>\n </CardContent>\n </Card>\n </div>\n </CarouselItem>\n </CarouselContent>\n <CarouselPrevious />\n <CarouselNext />\n </Carousel>\n\n <div class=\"py-2 text-center text-sm text-muted-foreground\">\n Slide {{ current }} of {{ totalCount }}\n </div>\n </div>\n</template>\n",
"type": "registry:example",
"target": "CarouselApi.vue"
"target": ""
}
]
}

View File

@ -11,7 +11,7 @@
"path": "example/CarouselDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Card, CardContent } from '@/registry/default/ui/card'\nimport { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/registry/default/ui/carousel'\n</script>\n\n<template>\n <Carousel v-slot=\"{ canScrollNext }\" class=\"relative w-full max-w-xs\">\n <CarouselContent>\n <CarouselItem v-for=\"(_, index) in 5\" :key=\"index\">\n <div class=\"p-1\">\n <Card>\n <CardContent class=\"flex aspect-square items-center justify-center p-6\">\n <span class=\"text-4xl font-semibold\">{{ index + 1 }}</span>\n </CardContent>\n </Card>\n </div>\n </CarouselItem>\n </CarouselContent>\n <CarouselPrevious />\n <CarouselNext v-if=\"canScrollNext\" />\n </Carousel>\n</template>\n",
"type": "registry:example",
"target": "CarouselDemo.vue"
"target": ""
}
]
}

View File

@ -11,7 +11,7 @@
"path": "example/CarouselOrientation.vue",
"content": "<script setup lang=\"ts\">\nimport { Card, CardContent } from '@/registry/default/ui/card'\nimport { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/registry/default/ui/carousel'\n</script>\n\n<template>\n <Carousel\n orientation=\"vertical\"\n class=\"relative w-full max-w-xsw-full max-w-xs\"\n :opts=\"{\n align: 'start',\n }\"\n >\n <CarouselContent class=\"-mt-1 h-[200px]\">\n <CarouselItem v-for=\"(_, index) in 5\" :key=\"index\" class=\"p-1 md:basis-1/2\">\n <div class=\"p-1\">\n <Card>\n <CardContent class=\"flex items-center justify-center p-6\">\n <span class=\"text-3xl font-semibold\">{{ index + 1 }}</span>\n </CardContent>\n </Card>\n </div>\n </CarouselItem>\n </CarouselContent>\n <CarouselPrevious />\n <CarouselNext />\n </Carousel>\n</template>\n",
"type": "registry:example",
"target": "CarouselOrientation.vue"
"target": ""
}
]
}

View File

@ -11,7 +11,7 @@
"path": "example/CarouselPlugin.vue",
"content": "<script setup lang=\"ts\">\nimport { Card, CardContent } from '@/registry/default/ui/card'\nimport { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/registry/default/ui/carousel'\nimport Autoplay from 'embla-carousel-autoplay'\n\nconst plugin = Autoplay({\n delay: 2000,\n stopOnMouseEnter: true,\n stopOnInteraction: false,\n})\n</script>\n\n<template>\n <Carousel\n class=\"relative w-full max-w-xs\"\n :plugins=\"[plugin]\"\n @mouseenter=\"plugin.stop\"\n @mouseleave=\"[plugin.reset(), plugin.play(), console.log('Running')];\"\n >\n <CarouselContent>\n <CarouselItem v-for=\"(_, index) in 5\" :key=\"index\">\n <div class=\"p-1\">\n <Card>\n <CardContent class=\"flex aspect-square items-center justify-center p-6\">\n <span class=\"text-4xl font-semibold\">{{ index + 1 }}</span>\n </CardContent>\n </Card>\n </div>\n </CarouselItem>\n </CarouselContent>\n <CarouselPrevious />\n <CarouselNext />\n </Carousel>\n</template>\n",
"type": "registry:example",
"target": "CarouselPlugin.vue"
"target": ""
}
]
}

View File

@ -11,7 +11,7 @@
"path": "example/CarouselSize.vue",
"content": "<script setup lang=\"ts\">\nimport { Card, CardContent } from '@/registry/default/ui/card'\nimport { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/registry/default/ui/carousel'\n</script>\n\n<template>\n <Carousel\n class=\"relative w-full max-w-sm\"\n :opts=\"{\n align: 'start',\n }\"\n >\n <CarouselContent>\n <CarouselItem v-for=\"(_, index) in 5\" :key=\"index\" class=\"md:basis-1/2 lg:basis-1/3\">\n <div class=\"p-1\">\n <Card>\n <CardContent class=\"flex aspect-square items-center justify-center p-6\">\n <span class=\"text-3xl font-semibold\">{{ index + 1 }}</span>\n </CardContent>\n </Card>\n </div>\n </CarouselItem>\n </CarouselContent>\n <CarouselPrevious />\n <CarouselNext />\n </Carousel>\n</template>\n",
"type": "registry:example",
"target": "CarouselSize.vue"
"target": ""
}
]
}

View File

@ -11,7 +11,7 @@
"path": "example/CarouselSpacing.vue",
"content": "<script setup lang=\"ts\">\nimport { Card, CardContent } from '@/registry/default/ui/card'\nimport { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/registry/default/ui/carousel'\n</script>\n\n<template>\n <Carousel\n class=\"relative w-full max-w-sm\"\n :opts=\"{\n align: 'start',\n }\"\n >\n <CarouselContent class=\"-ml-1\">\n <CarouselItem v-for=\"(_, index) in 5\" :key=\"index\" class=\"pl-1 md:basis-1/2 lg:basis-1/3\">\n <div class=\"p-1\">\n <Card>\n <CardContent class=\"flex aspect-square items-center justify-center p-6\">\n <span class=\"text-2xl font-semibold\">{{ index + 1 }}</span>\n </CardContent>\n </Card>\n </div>\n </CarouselItem>\n </CarouselContent>\n <CarouselPrevious />\n <CarouselNext />\n </Carousel>\n</template>\n",
"type": "registry:example",
"target": "CarouselSpacing.vue"
"target": ""
}
]
}

View File

@ -13,7 +13,7 @@
"path": "example/CarouselThumbnails.vue",
"content": "<script setup lang=\"ts\">\nimport { Card, CardContent } from '@/registry/default/ui/card'\nimport { Carousel, type CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/registry/default/ui/carousel'\nimport { watchOnce } from '@vueuse/core'\nimport { ref } from 'vue'\n\nconst emblaMainApi = ref<CarouselApi>()\nconst emblaThumbnailApi = ref<CarouselApi>()\nconst selectedIndex = ref(0)\n\nfunction onSelect() {\n if (!emblaMainApi.value || !emblaThumbnailApi.value)\n return\n selectedIndex.value = emblaMainApi.value.selectedScrollSnap()\n emblaThumbnailApi.value.scrollTo(emblaMainApi.value.selectedScrollSnap())\n}\n\nfunction onThumbClick(index: number) {\n if (!emblaMainApi.value || !emblaThumbnailApi.value)\n return\n emblaMainApi.value.scrollTo(index)\n}\n\nwatchOnce(emblaMainApi, (emblaMainApi) => {\n if (!emblaMainApi)\n return\n\n onSelect()\n emblaMainApi.on('select', onSelect)\n emblaMainApi.on('reInit', onSelect)\n})\n</script>\n\n<template>\n <div class=\"w-full sm:w-auto\">\n <Carousel\n class=\"relative w-full max-w-xs\"\n @init-api=\"(val) => emblaMainApi = val\"\n >\n <CarouselContent>\n <CarouselItem v-for=\"(_, index) in 10\" :key=\"index\">\n <div class=\"p-1\">\n <Card>\n <CardContent class=\"flex aspect-square items-center justify-center p-6\">\n <span class=\"text-4xl font-semibold\">{{ index + 1 }}</span>\n </CardContent>\n </Card>\n </div>\n </CarouselItem>\n </CarouselContent>\n <CarouselPrevious />\n <CarouselNext />\n </Carousel>\n\n <Carousel\n class=\"relative w-full max-w-xs\"\n @init-api=\"(val) => emblaThumbnailApi = val\"\n >\n <CarouselContent class=\"flex gap-1 ml-0\">\n <CarouselItem v-for=\"(_, index) in 10\" :key=\"index\" class=\"pl-0 basis-1/4 cursor-pointer\" @click=\"onThumbClick(index)\">\n <div class=\"p-1\" :class=\"index === selectedIndex ? '' : 'opacity-50'\">\n <Card>\n <CardContent class=\"flex aspect-square items-center justify-center p-6\">\n <span class=\"text-4xl font-semibold\">{{ index + 1 }}</span>\n </CardContent>\n </Card>\n </div>\n </CarouselItem>\n </CarouselContent>\n </Carousel>\n </div>\n</template>\n",
"type": "registry:example",
"target": "CarouselThumbnails.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/CheckboxDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Checkbox } from '@/registry/default/ui/checkbox'\n</script>\n\n<template>\n <div class=\"flex items-center space-x-2\">\n <Checkbox id=\"terms\" />\n <label\n for=\"terms\"\n class=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n >\n Accept terms and conditions\n </label>\n </div>\n</template>\n",
"type": "registry:example",
"target": "CheckboxDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/CheckboxDisabled.vue",
"content": "<script setup lang=\"ts\">\nimport { Checkbox } from '@/registry/default/ui/checkbox'\n</script>\n\n<template>\n <div class=\"items-top flex space-x-2\">\n <Checkbox id=\"terms1\" disabled />\n <label\n for=\"terms2\"\n class=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n >\n Accept terms and conditions\n </label>\n </div>\n</template>\n",
"type": "registry:example",
"target": "CheckboxDisabled.vue"
"target": ""
}
]
}

View File

@ -17,7 +17,7 @@
"path": "example/CheckboxFormMultiple.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport { Checkbox } from '@/registry/default/ui/checkbox'\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/default/ui/form'\nimport { toast } from '@/registry/default/ui/toast'\n\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst items = [\n {\n id: 'recents',\n label: 'Recents',\n },\n {\n id: 'home',\n label: 'Home',\n },\n {\n id: 'applications',\n label: 'Applications',\n },\n {\n id: 'desktop',\n label: 'Desktop',\n },\n {\n id: 'downloads',\n label: 'Downloads',\n },\n {\n id: 'documents',\n label: 'Documents',\n },\n] as const\n\nconst formSchema = toTypedSchema(z.object({\n items: z.array(z.string()).refine(value => value.some(item => item), {\n message: 'You have to select at least one item.',\n }),\n}))\n\nconst { handleSubmit } = useForm({\n validationSchema: formSchema,\n initialValues: {\n items: ['recents', 'home'],\n },\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form @submit=\"onSubmit\">\n <FormField name=\"items\">\n <FormItem>\n <div class=\"mb-4\">\n <FormLabel class=\"text-base\">\n Sidebar\n </FormLabel>\n <FormDescription>\n Select the items you want to display in the sidebar.\n </FormDescription>\n </div>\n\n <FormField v-for=\"item in items\" v-slot=\"{ value, handleChange }\" :key=\"item.id\" type=\"checkbox\" :value=\"item.id\" :unchecked-value=\"false\" name=\"items\">\n <FormItem class=\"flex flex-row items-start space-x-3 space-y-0\">\n <FormControl>\n <Checkbox\n :model-value=\"value.includes(item.id)\"\n @update:model-value=\"handleChange\"\n />\n </FormControl>\n <FormLabel class=\"font-normal\">\n {{ item.label }}\n </FormLabel>\n </FormItem>\n </FormField>\n <FormMessage />\n </FormItem>\n </FormField>\n\n <div class=\"flex justify-start mt-4\">\n <Button type=\"submit\">\n Submit\n </Button>\n </div>\n </form>\n</template>\n",
"type": "registry:example",
"target": "CheckboxFormMultiple.vue"
"target": ""
}
]
}

View File

@ -17,7 +17,7 @@
"path": "example/CheckboxFormSingle.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport { Checkbox } from '@/registry/default/ui/checkbox'\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/default/ui/form'\nimport { toast } from '@/registry/default/ui/toast'\n\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst formSchema = toTypedSchema(z.object({\n mobile: z.boolean().default(false).optional(),\n}))\n\nconst { handleSubmit } = useForm({\n validationSchema: formSchema,\n initialValues: {\n mobile: true,\n },\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form class=\"space-y-6\" @submit=\"onSubmit\">\n <FormField v-slot=\"{ value, handleChange }\" type=\"checkbox\" name=\"mobile\">\n <FormItem class=\"flex flex-row items-start gap-x-3 space-y-0 rounded-md border p-4\">\n <FormControl>\n <Checkbox :model-value=\"value\" @update:model-value=\"handleChange\" />\n </FormControl>\n <div class=\"space-y-1 leading-none\">\n <FormLabel>Use different settings for my mobile devices</FormLabel>\n <FormDescription>\n You can manage your mobile notifications in the\n <a href=\"/examples/forms\">mobile settings</a> page.\n </FormDescription>\n <FormMessage />\n </div>\n </FormItem>\n </FormField>\n <Button type=\"submit\">\n Submit\n </Button>\n </Form>\n</template>\n",
"type": "registry:example",
"target": "CheckboxFormSingle.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/CheckboxWithText.vue",
"content": "<script setup lang=\"ts\">\nimport { Checkbox } from '@/registry/default/ui/checkbox'\n</script>\n\n<template>\n <div class=\"items-top flex gap-x-2\">\n <Checkbox id=\"terms1\" />\n <div class=\"grid gap-1.5 leading-none\">\n <label\n for=\"terms1\"\n class=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n >\n Accept terms and conditions\n </label>\n <p class=\"text-sm text-muted-foreground\">\n You agree to our Terms of Service and Privacy Policy.\n </p>\n </div>\n </div>\n</template>\n",
"type": "registry:example",
"target": "CheckboxWithText.vue"
"target": ""
}
]
}

View File

@ -11,7 +11,7 @@
"path": "example/CollapsibleDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from '@/registry/default/ui/collapsible'\nimport { ChevronsUpDown } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst isOpen = ref(false)\n</script>\n\n<template>\n <Collapsible\n v-model:open=\"isOpen\"\n class=\"w-[350px] space-y-2\"\n >\n <div class=\"flex items-center justify-between space-x-4 px-4\">\n <h4 class=\"text-sm font-semibold\">\n @peduarte starred 3 repositories\n </h4>\n <CollapsibleTrigger as-child>\n <Button variant=\"ghost\" size=\"sm\" class=\"w-9 p-0\">\n <ChevronsUpDown class=\"h-4 w-4\" />\n <span class=\"sr-only\">Toggle</span>\n </Button>\n </CollapsibleTrigger>\n </div>\n <div class=\"rounded-md border px-4 py-3 font-mono text-sm\">\n @radix-ui/primitives\n </div>\n <CollapsibleContent class=\"space-y-2\">\n <div class=\"rounded-md border px-4 py-3 font-mono text-sm\">\n @radix-ui/colors\n </div>\n <div class=\"rounded-md border px-4 py-3 font-mono text-sm\">\n @stitches/react\n </div>\n </CollapsibleContent>\n </Collapsible>\n</template>\n",
"type": "registry:example",
"target": "CollapsibleDemo.vue"
"target": ""
}
]
}

View File

@ -13,7 +13,7 @@
"path": "example/ComboboxDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/default/ui/popover'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n\nconst open = ref(false)\nconst value = ref('')\n</script>\n\n<template>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n class=\"w-[200px] justify-between\"\n >\n {{ value\n ? frameworks.find((framework) => framework.value === value)?.label\n : \"Select framework...\" }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput class=\"h-9\" placeholder=\"Search framework...\" />\n <CommandEmpty>No framework found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework.value\"\n @select=\"(ev) => {\n if (typeof ev.detail.value === 'string') {\n value = ev.detail.value\n }\n open = false\n }\"\n >\n {{ framework.label }}\n <Check\n :class=\"cn(\n 'ml-auto h-4 w-4',\n value === framework.value ? 'opacity-100' : 'opacity-0',\n )\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n</template>\n",
"type": "registry:example",
"target": "ComboboxDemo.vue"
"target": ""
}
]
}

View File

@ -12,7 +12,7 @@
"path": "example/ComboboxDropdownMenu.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from '@/registry/default/ui/dropdown-menu'\nimport { Calendar, MoreHorizontal, Tags, Trash, User } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst labels = [\n 'feature',\n 'bug',\n 'enhancement',\n 'documentation',\n 'design',\n 'question',\n 'maintenance',\n]\n\nconst labelRef = ref('feature')\nconst open = ref(false)\n</script>\n\n<template>\n <div class=\"flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center\">\n <p class=\"text-sm font-medium leading-none\">\n <span class=\"mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground\">\n {{ labelRef }}\n </span>\n <span class=\"text-muted-foreground\">Create a new project</span>\n </p>\n <DropdownMenu v-model:open=\"open\">\n <DropdownMenuTrigger as-child>\n <Button variant=\"ghost\" size=\"sm\">\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" class=\"w-[200px]\">\n <DropdownMenuLabel>Actions</DropdownMenuLabel>\n <DropdownMenuGroup>\n <DropdownMenuItem>\n <User class=\"mr-2 h-4 w-4\" />\n Assign to...\n </DropdownMenuItem>\n <DropdownMenuItem>\n <Calendar class=\"mr-2 h-4 w-4\" />\n Set due date...\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n <Tags class=\"mr-2 h-4 w-4\" />\n Apply label\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent class=\"p-0\">\n <Command>\n <CommandInput\n placeholder=\"Filter label...\"\n auto-focus\n />\n <CommandList>\n <CommandEmpty>No label found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"label in labels\"\n :key=\"label\"\n :value=\"label\"\n @select=\"(ev) => {\n labelRef = ev.detail.value as string\n open = false\n }\"\n >\n {{ label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n <DropdownMenuSeparator />\n <DropdownMenuItem class=\"text-red-600\">\n <Trash class=\"mr-2 h-4 w-4\" />\n Delete\n <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>\n </DropdownMenuItem>\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n</template>\n",
"type": "registry:example",
"target": "ComboboxDropdownMenu.vue"
"target": ""
}
]
}

View File

@ -19,7 +19,7 @@
"path": "example/ComboboxForm.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/default/ui/form'\n\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/default/ui/popover'\nimport { toast } from '@/registry/default/ui/toast'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst languages = [\n { label: 'English', value: 'en' },\n { label: 'French', value: 'fr' },\n { label: 'German', value: 'de' },\n { label: 'Spanish', value: 'es' },\n { label: 'Portuguese', value: 'pt' },\n { label: 'Russian', value: 'ru' },\n { label: 'Japanese', value: 'ja' },\n { label: 'Korean', value: 'ko' },\n { label: 'Chinese', value: 'zh' },\n]\n\nconst formSchema = toTypedSchema(z.object({\n language: z.string({\n required_error: 'Please select a language.',\n }),\n}))\n\nconst { handleSubmit, setFieldValue, values } = useForm({\n validationSchema: formSchema,\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form class=\"space-y-6\" @submit=\"onSubmit\">\n <FormField name=\"language\">\n <FormItem class=\"flex flex-col\">\n <FormLabel>Language</FormLabel>\n <Popover>\n <PopoverTrigger as-child>\n <FormControl>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :class=\"cn('w-[200px] justify-between', !values.language && 'text-muted-foreground')\"\n >\n {{ values.language ? languages.find(\n (language) => language.value === values.language,\n )?.label : 'Select language...' }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput placeholder=\"Search language...\" />\n <CommandEmpty>Nothing found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"language in languages\"\n :key=\"language.value\"\n :value=\"language.label\"\n @select=\"() => {\n setFieldValue('language', language.value)\n }\"\n >\n <Check\n :class=\"cn('mr-2 h-4 w-4', language.value === values.language ? 'opacity-100' : 'opacity-0')\"\n />\n {{ language.label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n <FormDescription>\n This is the language that will be used in the dashboard.\n </FormDescription>\n <FormMessage />\n </FormItem>\n </FormField>\n\n <Button type=\"submit\">\n Submit\n </Button>\n </form>\n</template>\n",
"type": "registry:example",
"target": "ComboboxForm.vue"
"target": ""
}
]
}

View File

@ -13,7 +13,7 @@
"path": "example/ComboboxPopover.vue",
"content": "<script setup lang=\"ts\">\nimport type { Icon } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/default/ui/popover'\nimport {\n ArrowUpCircle,\n CheckCircle2,\n Circle,\n HelpCircle,\n XCircle,\n} from 'lucide-vue-next'\nimport { ref } from 'vue'\n\ninterface Status {\n value: string\n label: string\n icon: Icon\n}\n\nconst statuses: Status[] = [\n {\n value: 'backlog',\n label: 'Backlog',\n icon: HelpCircle,\n },\n {\n value: 'todo',\n label: 'Todo',\n icon: Circle,\n },\n {\n value: 'in progress',\n label: 'In Progress',\n icon: ArrowUpCircle,\n },\n {\n value: 'done',\n label: 'Done',\n icon: CheckCircle2,\n },\n {\n value: 'canceled',\n label: 'Canceled',\n icon: XCircle,\n },\n]\n\nconst open = ref(false)\n// const value = ref<typeof statuses[number]>()\n\nconst selectedStatus = ref<Status>()\n</script>\n\n<template>\n <div class=\"flex items-center space-x-4\">\n <p class=\"text-sm text-muted-foreground\">\n Status\n </p>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n size=\"sm\"\n class=\"w-[150px] justify-start\"\n >\n <template v-if=\"selectedStatus\">\n <component :is=\"selectedStatus?.icon\" class=\"mr-2 h-4 w-4 shrink-0\" />\n {{ selectedStatus?.label }}\n </template>\n <template v-else>\n + Set status\n </template>\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"p-0\" side=\"right\" align=\"start\">\n <Command>\n <CommandInput placeholder=\"Change status...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"status in statuses\"\n :key=\"status.value\"\n :value=\"status.value\"\n @select=\"(value) => {\n selectedStatus = status\n open = false\n }\"\n >\n <component\n :is=\"status.icon\"\n :key=\"status.value\"\n :class=\"cn('mr-2 h-4 w-4', status.value === selectedStatus?.value ? 'opacity-100' : 'opacity-40',\n )\"\n />\n <span>{{ status.label }}</span>\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n </div>\n</template>\n",
"type": "registry:example",
"target": "ComboboxPopover.vue"
"target": ""
}
]
}

View File

@ -15,7 +15,7 @@
"path": "example/ComboboxResponsive.vue",
"content": "<script lang=\"ts\" setup>\nimport { Button } from '@/registry/default/ui/button'\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/registry/default/ui/command'\nimport { Drawer, DrawerContent, DrawerTrigger } from '@/registry/default/ui/drawer'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/registry/default/ui/popover'\nimport { createReusableTemplate, useMediaQuery } from '@vueuse/core'\nimport { ref } from 'vue'\n\ninterface Status {\n value: string\n label: string\n}\n\nconst statuses: Status[] = [\n {\n value: 'backlog',\n label: 'Backlog',\n },\n {\n value: 'todo',\n label: 'Todo',\n },\n {\n value: 'in progress',\n label: 'In Progress',\n },\n {\n value: 'done',\n label: 'Done',\n },\n {\n value: 'canceled',\n label: 'Canceled',\n },\n]\n\nconst [UseTemplate, StatusList] = createReusableTemplate()\nconst isDesktop = useMediaQuery('(min-width: 768px)')\n\nconst isOpen = ref(false)\nconst selectedStatus = ref<Status | null>(null)\n\nfunction onStatusSelect(status: Status) {\n selectedStatus.value = status\n isOpen.value = false\n}\n</script>\n\n<template>\n <div>\n <UseTemplate>\n <Command>\n <CommandInput placeholder=\"Filter status...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"status of statuses\"\n :key=\"status.value\"\n :value=\"status.value\"\n @select=\"onStatusSelect(status)\"\n >\n {{ status.label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </UseTemplate>\n\n <Popover v-if=\"isDesktop\" v-model:open=\"isOpen\">\n <PopoverTrigger as-child>\n <Button variant=\"outline\" class=\"w-[150px] justify-start\">\n {{ selectedStatus ? selectedStatus.label : \"+ Set status\" }}\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\" align=\"start\">\n <StatusList />\n </PopoverContent>\n </Popover>\n\n <Drawer v-else :open=\"isOpen\" @update:open=\"(newOpenValue) => isOpen = newOpenValue\">\n <DrawerTrigger as-child>\n <Button variant=\"outline\" class=\"w-[150px] justify-start\">\n {{ selectedStatus ? selectedStatus.label : \"+ Set status\" }}\n </Button>\n </DrawerTrigger>\n <DrawerContent>\n <div class=\"mt-4 border-t\">\n <StatusList />\n </div>\n </DrawerContent>\n </Drawer>\n </div>\n</template>\n",
"type": "registry:example",
"target": "ComboboxResponsive.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/CommandDemo.vue",
"content": "<script setup lang=\"ts\">\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut,\n} from '@/registry/default/ui/command'\n\nimport {\n Calculator,\n Calendar,\n CreditCard,\n Settings,\n Smile,\n User,\n} from 'lucide-vue-next'\n</script>\n\n<template>\n <Command class=\"rounded-lg border shadow-md max-w-[450px]\">\n <CommandInput placeholder=\"Type a command or search...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup heading=\"Suggestions\">\n <CommandItem value=\"Calendar\">\n <Calendar class=\"mr-2 h-4 w-4\" />\n <span>Calendar</span>\n </CommandItem>\n <CommandItem value=\"Search Emoji\">\n <Smile class=\"mr-2 h-4 w-4\" />\n <span>Search Emoji</span>\n </CommandItem>\n <CommandItem value=\"Calculator\">\n <Calculator class=\"mr-2 h-4 w-4\" />\n <span>Calculator</span>\n </CommandItem>\n </CommandGroup>\n <CommandSeparator />\n <CommandGroup heading=\"Settings\">\n <CommandItem value=\"Profile\">\n <User class=\"mr-2 h-4 w-4\" />\n <span>Profile</span>\n <CommandShortcut>⌘P</CommandShortcut>\n </CommandItem>\n <CommandItem value=\"Billing\">\n <CreditCard class=\"mr-2 h-4 w-4\" />\n <span>Billing</span>\n <CommandShortcut>⌘B</CommandShortcut>\n </CommandItem>\n <CommandItem value=\"Settings\">\n <Settings class=\"mr-2 h-4 w-4\" />\n <span>Settings</span>\n <CommandShortcut>⌘S</CommandShortcut>\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n</template>\n",
"type": "registry:example",
"target": "CommandDemo.vue"
"target": ""
}
]
}

View File

@ -12,7 +12,7 @@
"path": "example/CommandDialogDemo.vue",
"content": "<script setup lang=\"ts\">\nimport {\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n} from '@/registry/default/ui/command'\n\nimport { useMagicKeys } from '@vueuse/core'\nimport { ref, watch } from 'vue'\n\nconst open = ref(false)\n\nconst { Meta_J, Ctrl_J } = useMagicKeys({\n passive: false,\n onEventFired(e) {\n if (e.key === 'j' && (e.metaKey || e.ctrlKey))\n e.preventDefault()\n },\n})\n\nwatch([Meta_J, Ctrl_J], (v) => {\n if (v[0] || v[1])\n handleOpenChange()\n})\n\nfunction handleOpenChange() {\n open.value = !open.value\n}\n</script>\n\n<template>\n <div>\n <p class=\"text-sm text-muted-foreground\">\n Press\n <kbd\n class=\"pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100\"\n >\n <span class=\"text-xs\">⌘</span>J\n </kbd>\n </p>\n <CommandDialog v-model:open=\"open\">\n <CommandInput placeholder=\"Type a command or search...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup heading=\"Suggestions\">\n <CommandItem value=\"calendar\">\n Calendar\n </CommandItem>\n <CommandItem value=\"search-emoji\">\n Search Emoji\n </CommandItem>\n <CommandItem value=\"calculator\">\n Calculator\n </CommandItem>\n </CommandGroup>\n <CommandSeparator />\n <CommandGroup heading=\"Settings\">\n <CommandItem value=\"profile\">\n Profile\n </CommandItem>\n <CommandItem value=\"billing\">\n Billing\n </CommandItem>\n <CommandItem value=\"settings\">\n Settings\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </CommandDialog>\n </div>\n</template>\n",
"type": "registry:example",
"target": "CommandDialogDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/ContextMenuDemo.vue",
"content": "<script setup lang=\"ts\">\nimport {\n ContextMenu,\n ContextMenuCheckboxItem,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuLabel,\n ContextMenuRadioGroup,\n ContextMenuRadioItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuTrigger,\n} from '@/registry/default/ui/context-menu'\n</script>\n\n<template>\n <ContextMenu>\n <ContextMenuTrigger class=\"flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm\">\n Right click here\n </ContextMenuTrigger>\n <ContextMenuContent class=\"w-64\">\n <ContextMenuItem inset>\n Back\n <ContextMenuShortcut>⌘[</ContextMenuShortcut>\n </ContextMenuItem>\n <ContextMenuItem inset disabled>\n Forward\n <ContextMenuShortcut>⌘]</ContextMenuShortcut>\n </ContextMenuItem>\n <ContextMenuItem inset>\n Reload\n <ContextMenuShortcut>⌘R</ContextMenuShortcut>\n </ContextMenuItem>\n <ContextMenuSub>\n <ContextMenuSubTrigger inset>\n More Tools\n </ContextMenuSubTrigger>\n <ContextMenuSubContent class=\"w-48\">\n <ContextMenuItem>\n Save Page As...\n <ContextMenuShortcut>⇧⌘S</ContextMenuShortcut>\n </ContextMenuItem>\n <ContextMenuItem>Create Shortcut...</ContextMenuItem>\n <ContextMenuItem>Name Window...</ContextMenuItem>\n <ContextMenuSeparator />\n <ContextMenuItem>Developer Tools</ContextMenuItem>\n </ContextMenuSubContent>\n </ContextMenuSub>\n <ContextMenuSeparator />\n <ContextMenuCheckboxItem checked>\n Show Bookmarks Bar\n <ContextMenuShortcut>⌘⇧B</ContextMenuShortcut>\n </ContextMenuCheckboxItem>\n <ContextMenuCheckboxItem>Show Full URLs</ContextMenuCheckboxItem>\n <ContextMenuSeparator />\n <ContextMenuRadioGroup model-value=\"pedro\">\n <ContextMenuLabel inset>\n People\n </ContextMenuLabel>\n <ContextMenuSeparator />\n <ContextMenuRadioItem value=\"pedro\">\n Pedro Duarte\n </ContextMenuRadioItem>\n <ContextMenuRadioItem value=\"colm\">\n Colm Tuite\n </ContextMenuRadioItem>\n </ContextMenuRadioGroup>\n </ContextMenuContent>\n </ContextMenu>\n</template>\n",
"type": "registry:example",
"target": "ContextMenuDemo.vue"
"target": ""
}
]
}

View File

@ -10,7 +10,7 @@
"path": "example/CustomChartTooltip.vue",
"content": "<script setup lang=\"ts\">\nimport { Card, CardContent } from '@/registry/default/ui/card'\n\ndefineProps<{\n title?: string\n data: {\n name: string\n color: string\n value: any\n }[]\n}>()\n</script>\n\n<template>\n <Card class=\"text-sm\">\n <CardContent class=\"p-3 min-w-[180px] flex flex-col gap-2\">\n <div v-for=\"(item, key) in data\" :key=\"key\" class=\"flex justify-between items-center\">\n <div class=\"flex items-center\">\n <span class=\"w-1 h-7 mr-4 rounded-full\" :style=\"{ background: item.color }\" />\n <span>{{ item.name }}</span>\n </div>\n <span class=\"font-semibold ml-4\">{{ item.value }}</span>\n </div>\n </CardContent>\n </Card>\n</template>\n",
"type": "registry:example",
"target": "CustomChartTooltip.vue"
"target": ""
}
]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More