Merge remote-tracking branch 'origin/dev' into charting

This commit is contained in:
zernonia 2024-03-21 10:50:00 +08:00
commit 26e3ea917c
272 changed files with 4845 additions and 1490 deletions

View File

@ -1,4 +1,5 @@
{ {
"vue.server.hybridMode": true,
"eslint.experimental.useFlatConfig": true, "eslint.experimental.useFlatConfig": true,
"prettier.enable": false, "prettier.enable": false,
"editor.formatOnSave": false, "editor.formatOnSave": false,

View File

@ -32,12 +32,12 @@ packages
└── cli └── cli
``` ```
| Path | Description | | Path | Description |
| --------------------- | ---------------------------------------- | | ----------------------------| -------------------------------------------|
| `apps/www/app` | The Next.js application for the website. | | `apps/www/.vitepress` | The Vitepress application for the website. |
| `apps/www/content` | The content for the website. | | `apps/www/src/content` | The content for the website. |
| `apps/www/registry` | The registry for the components. | | `apps/www/src/lib/registry` | The registry for the components. |
| `packages/cli` | The `shadcn-vue` package. | | `packages/cli` | The `shadcn-vue` package. |
## Development ## Development
@ -79,22 +79,24 @@ The documentation for this project is located in the `www` workspace. You can ru
pnpm dev pnpm dev
``` ```
Documentation is written using [md](https://vitepress.dev/guide/markdown). You can find the documentation files in the `apps/www/content/docs` directory. Documentation is written using [md](https://vitepress.dev/guide/markdown). You can find the documentation files in the `apps/www/src/content` directory.
## Components ## Components
We use a registry system for developing components. You can find the source code for the components under `apps/www/registry`. The components are organized by styles. We use a registry system for developing components. You can find the source code for the components under `apps/www/src/lib/registry`. The components are organized by styles.
```bash ```bash
apps apps
└── www └── www
└── registry └── src
├── default └── lib
│ ├── example └── registry
│ └── ui ├── default
└── new-york │ ├── example
├── example │ └── ui
└── ui └── new-york
├── example
└── ui
``` ```
When adding or modifying components, please ensure that: When adding or modifying components, please ensure that:
@ -130,13 +132,10 @@ the following categories:
e.g. `feat(components): add new prop to the avatar component` e.g. `feat(components): add new prop to the avatar component`
If you are interested in the detailed specification you can visit If you are interested in the detailed specification you can visit
https://www.conventionalcommits.org/ or check out the https://www.conventionalcommits.org/ or check out the
[Angular Commit Message Guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines). [Angular Commit Message Guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines).
## Requests for new components ## Requests for new components
If you have a request for a new component, please open a discussion on GitHub. We'll be happy to help you out. If you have a request for a new component, please open a discussion on GitHub. We'll be happy to help you out.

View File

@ -3,16 +3,12 @@ import { defineConfig } from 'vitepress'
import Icons from 'unplugin-icons/vite' import Icons from 'unplugin-icons/vite'
import tailwind from 'tailwindcss' import tailwind from 'tailwindcss'
import autoprefixer from 'autoprefixer' import autoprefixer from 'autoprefixer'
import { createCssVariablesTheme } from 'shiki' import { cssVariables } from './theme/config/shiki'
// import { transformerMetaWordHighlight, transformerNotationWordHighlight } from '@shikijs/transformers' // import { transformerMetaWordHighlight, transformerNotationWordHighlight } from '@shikijs/transformers'
import { siteConfig } from './theme/config/site' import { siteConfig } from './theme/config/site'
import ComponentPreviewPlugin from './theme/plugins/previewer' import ComponentPreviewPlugin from './theme/plugins/previewer'
import CodeWrapperPlugin from './theme/plugins/codewrapper'
const cssVariables = createCssVariablesTheme({
variablePrefix: '--shiki-',
variableDefaults: {},
})
// https://vitepress.dev/reference/site-config // https://vitepress.dev/reference/site-config
export default defineConfig({ export default defineConfig({
@ -65,6 +61,7 @@ export default defineConfig({
], ],
config(md) { config(md) {
md.use(ComponentPreviewPlugin) md.use(ComponentPreviewPlugin)
md.use(CodeWrapperPlugin)
}, },
}, },
rewrites: { rewrites: {

View File

@ -0,0 +1,98 @@
<script lang="ts" setup>
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod'
import { useConfigStore } from '@/stores/config'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Input } from '@/lib/registry/new-york/ui/input'
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/new-york/ui/form'
import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from '@/lib/registry/new-york/ui/sheet'
import RadixIconsGear from '~icons/radix-icons/gear'
const { codeConfig, setCodeConfig } = useConfigStore()
const formSchema = toTypedSchema(z.object({
prefix: z.string().default(''),
componentsPath: z.string().default('@/components'),
utilsPath: z.string().default('@/utils'),
}))
const { handleSubmit, setValues } = useForm({
validationSchema: formSchema,
initialValues: codeConfig.value,
})
const onSubmit = handleSubmit((values) => {
setCodeConfig(values)
setValues(values)
})
</script>
<template>
<Sheet
@update:open="(open) => {
if (open) setValues(codeConfig)
}"
>
<SheetTrigger as-child>
<Button
class="w-9 h-9"
:variant="'ghost'"
:size="'icon'"
>
<RadixIconsGear class="w-5 h-5" />
</Button>
</SheetTrigger>
<SheetContent>
<form @submit="onSubmit">
<SheetHeader>
<SheetTitle>Edit code config</SheetTitle>
<SheetDescription>
Configure how the CodeBlock should render on the site.
</SheetDescription>
</SheetHeader>
<div class="my-4">
<!-- <FormField v-slot="{ componentField }" name="prefix">
<FormItem>
<FormLabel>Prefix</FormLabel>
<FormControl>
<Input placeholder="" v-bind="componentField" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField> -->
<FormField v-slot="{ componentField }" name="componentsPath">
<FormItem>
<FormLabel>Components Path</FormLabel>
<FormControl>
<Input placeholder="@/components" v-bind="componentField" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="utilsPath">
<FormItem>
<FormLabel>Utils Path</FormLabel>
<FormControl>
<Input placeholder="@/utils" v-bind="componentField" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField>
</div>
<SheetFooter>
<SheetClose as-child>
<Button type="submit">
Save changes
</Button>
</SheetClose>
</SheetFooter>
</form>
</SheetContent>
</Sheet>
</template>

View File

@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue' import { ref, toRefs, watch } from 'vue'
import { Icon } from '@iconify/vue' import { Icon } from '@iconify/vue'
import { makeCodeSandboxParams } from '../utils/codeeditor' import { makeCodeSandboxParams } from '../utils/codeeditor'
import Tooltip from './Tooltip.vue' import Tooltip from './Tooltip.vue'
import { Button } from '@/lib/registry/new-york/ui/button' import { Button } from '@/lib/registry/new-york/ui/button'
import { type Style } from '@/lib/registry/styles' import type { Style } from '@/lib/registry/styles'
const props = defineProps<{ const props = defineProps<{
name: string name: string
@ -12,11 +12,12 @@ const props = defineProps<{
style: Style style: Style
}>() }>()
const { code } = toRefs(props)
const sources = ref<Record<string, string>>({}) const sources = ref<Record<string, string>>({})
onMounted(() => { watch(code, () => {
sources.value['App.vue'] = props.code sources.value['App.vue'] = code.value
}) }, { immediate: true })
</script> </script>
<template> <template>

View File

@ -0,0 +1,46 @@
import { type VNode, type VNodeArrayChildren, cloneVNode, defineComponent } from 'vue'
import { useConfigStore } from '@/stores/config'
function crawlSpan(children: VNodeArrayChildren, cb: (vnode: VNode) => void) {
children.forEach((childNode) => {
if (!Array.isArray(childNode) && typeof childNode === 'object') {
if (typeof childNode?.children === 'string')
cb(childNode)
else
crawlSpan(childNode?.children as VNodeArrayChildren ?? [], cb)
}
})
}
export default defineComponent(
(props, { slots }) => {
const { codeConfig } = useConfigStore()
return () => {
const clonedVNode = slots.default?.()?.[0]
? cloneVNode(slots.default?.()?.[0], {
key: JSON.stringify(codeConfig.value),
})
: undefined
// @ts-expect-error cloneVNode
const preVNode = [...clonedVNode?.children].find((node: VNode) => node.type === 'pre') as VNode
// @ts-expect-error cloneVNode
const codeVNode = preVNode.children?.at(0) as VNode
if (codeVNode) {
crawlSpan(codeVNode.children as VNodeArrayChildren, (vnode) => {
if (typeof vnode.children === 'string') {
vnode.children = vnode.children.replaceAll('@/components', codeConfig.value.componentsPath)
vnode.children = vnode.children.replaceAll('@/libs', codeConfig.value.utilsPath)
}
})
return clonedVNode
}
else {
return slots.default?.()
}
}
},
)

View File

@ -1,4 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue'
import { codeToHtml } from 'shiki'
import MagicString from 'magic-string'
import { cssVariables } from '../config/shiki'
import StyleSwitcher from './StyleSwitcher.vue' import StyleSwitcher from './StyleSwitcher.vue'
import ComponentLoader from './ComponentLoader.vue' import ComponentLoader from './ComponentLoader.vue'
import Stackblitz from './Stackblitz.vue' import Stackblitz from './Stackblitz.vue'
@ -11,14 +15,35 @@ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}) })
withDefaults(defineProps<{ const props = withDefaults(defineProps<{
name: string name: string
align?: 'center' | 'start' | 'end' align?: 'center' | 'start' | 'end'
sfcTsCode?: string
sfcTsHtml?: string
}>(), { align: 'center' }) }>(), { align: 'center' })
const { style } = useConfigStore() const { style, codeConfig } = useConfigStore()
const rawString = ref('')
const codeHtml = ref('')
function transformImportPath(code: string) {
const s = new MagicString(code)
s.replaceAll(`@/lib/registry/${style.value}`, codeConfig.value.componentsPath)
s.replaceAll(`@/lib/utils`, codeConfig.value.utilsPath)
return s.toString()
}
watch([style, codeConfig], async () => {
try {
rawString.value = await import(`../../../src/lib/registry/${style.value}/example/${props.name}.vue?raw`).then(res => res.default.trim())
codeHtml.value = await codeToHtml(transformImportPath(rawString.value), {
lang: 'vue',
theme: cssVariables,
})
}
catch (err) {
console.error(err)
}
}, { immediate: true, deep: true })
</script> </script>
<template> <template>
@ -47,8 +72,8 @@ const { style } = useConfigStore()
<StyleSwitcher /> <StyleSwitcher />
<div class="flex items-center gap-x-1"> <div class="flex items-center gap-x-1">
<Stackblitz :key="style" :style="style" :name="name" :code="decodeURIComponent(sfcTsCode ?? '')" /> <Stackblitz :key="style" :style="style" :name="name" :code="rawString" />
<CodeSandbox :key="style" :style="style" :name="name" :code="decodeURIComponent(sfcTsCode ?? '')" /> <CodeSandbox :key="style" :style="style" :name="name" :code="rawString" />
</div> </div>
</div> </div>
<div <div
@ -62,7 +87,7 @@ const { style } = useConfigStore()
</div> </div>
</TabsContent> </TabsContent>
<TabsContent value="code"> <TabsContent value="code">
<div v-if="sfcTsHtml" class="language-vue" style="flex: 1;" v-html="decodeURIComponent(sfcTsHtml)" /> <div v-if="codeHtml" class="language-vue" style="flex: 1;" v-html="codeHtml" />
<slot v-else /> <slot v-else />
</TabsContent> </TabsContent>
</Tabs> </Tabs>

View File

@ -2,7 +2,7 @@
</script> </script>
<template> <template>
<a href="/" class="mr-4 md:mr-2 xl:mr-6 flex items-center lg:space-x1 xl:space-x-2"> <a href="/" class="mr-4 md:mr-2 lg:mr-6 flex items-center lg:space-x1 xl:space-x-2">
<svg class="h-6 w-6" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="h-6 w-6" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_102_1338)"> <g clip-path="url(#clip0_102_1338)">
<path d="M208 128L128 208" stroke="#41B883" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" /> <path d="M208 128L128 208" stroke="#41B883" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" />

View File

@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue' import { ref, toRefs, watch } from 'vue'
import { Icon } from '@iconify/vue' import { Icon } from '@iconify/vue'
import { makeStackblitzParams } from '../utils/codeeditor' import { makeStackblitzParams } from '../utils/codeeditor'
import Tooltip from './Tooltip.vue' import Tooltip from './Tooltip.vue'
import { Button } from '@/lib/registry/new-york/ui/button' import { Button } from '@/lib/registry/new-york/ui/button'
import { type Style } from '@/lib/registry/styles' import type { Style } from '@/lib/registry/styles'
const props = defineProps<{ const props = defineProps<{
name: string name: string
@ -12,11 +12,12 @@ const props = defineProps<{
style: Style style: Style
}>() }>()
const { code } = toRefs(props)
const sources = ref<Record<string, string>>({}) const sources = ref<Record<string, string>>({})
onMounted(() => { watch(code, () => {
sources.value['App.vue'] = props.code sources.value['App.vue'] = code.value
}) }, { immediate: true })
function handleClick() { function handleClick() {
makeStackblitzParams(props.name, props.style, sources.value) makeStackblitzParams(props.name, props.style, sources.value)

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type SelectTriggerProps } from 'radix-vue' import type { SelectTriggerProps } from 'radix-vue'
import { useConfigStore } from '@/stores/config' import { useConfigStore } from '@/stores/config'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'

View File

@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { useSlots } from 'vue' import { TabsContent } from '@/lib/registry/default/ui/tabs'
import { TabsContent, TabsTrigger } from '@/lib/registry/default/ui/tabs'
withDefaults(defineProps<{ withDefaults(defineProps<{
title?: string title?: string

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, useSlots } from 'vue' import { computed, useSlots } from 'vue'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/lib/registry/default/ui/tabs' import { Tabs, TabsList, TabsTrigger } from '@/lib/registry/default/ui/tabs'
const slots = useSlots() const slots = useSlots()

View File

@ -1,3 +1,4 @@
export { default as CodeWrapper } from './CodeWrapper'
export { default as ComponentPreview } from './ComponentPreview.vue' export { default as ComponentPreview } from './ComponentPreview.vue'
export { default as TabPreview } from './TabPreview.vue' export { default as TabPreview } from './TabPreview.vue'
export { default as TabMarkdown } from './TabMarkdown.vue' export { default as TabMarkdown } from './TabMarkdown.vue'

View File

@ -89,6 +89,12 @@ export const docsConfig: DocsConfig = {
title: 'About', title: 'About',
href: '/docs/about', href: '/docs/about',
}, },
{
title: 'Contribution',
href: '/docs/contribution',
items: [],
label: 'New',
},
], ],
}, },
{ {
@ -161,6 +167,12 @@ export const docsConfig: DocsConfig = {
title: 'Badge', title: 'Badge',
href: '/docs/components/badge', href: '/docs/components/badge',
}, },
{
title: 'Breadcrumb',
href: '/docs/components/breadcrumb',
items: [],
label: 'New',
},
{ {
title: 'Button', title: 'Button',
href: '/docs/components/button', href: '/docs/components/button',

View File

@ -0,0 +1,6 @@
import { createCssVariablesTheme } from 'shiki'
export const cssVariables = createCssVariablesTheme({
variablePrefix: '--shiki-',
variableDefaults: {},
})

View File

@ -4,7 +4,6 @@ import { docsConfig } from '../config/docs'
import TableOfContentVue from '../components/TableOfContent.vue' import TableOfContentVue from '../components/TableOfContent.vue'
import EditLink from '../components/EditLink.vue' import EditLink from '../components/EditLink.vue'
import { ScrollArea } from '@/lib/registry/default/ui/scroll-area' import { ScrollArea } from '@/lib/registry/default/ui/scroll-area'
import { Badge } from '@/lib/registry/default/ui/badge'
import RadixIconsCode from '~icons/radix-icons/code' import RadixIconsCode from '~icons/radix-icons/code'
import RadixIconsExternalLink from '~icons/radix-icons/external-link' import RadixIconsExternalLink from '~icons/radix-icons/external-link'
import ChevronRightIcon from '~icons/lucide/chevron-right' import ChevronRightIcon from '~icons/lucide/chevron-right'

View File

@ -2,10 +2,10 @@
import { useMagicKeys, useToggle } from '@vueuse/core' import { useMagicKeys, useToggle } from '@vueuse/core'
import { onMounted, ref, watch } from 'vue' import { onMounted, ref, watch } from 'vue'
import { Content, useData, useRoute, useRouter } from 'vitepress' import { Content, useData, useRoute, useRouter } from 'vitepress'
import { SearchIcon } from 'lucide-vue-next'
import { type NavItem, docsConfig } from '../config/docs' import { type NavItem, docsConfig } from '../config/docs'
import Logo from '../components/Logo.vue' import Logo from '../components/Logo.vue'
import MobileNav from '../components/MobileNav.vue' import MobileNav from '../components/MobileNav.vue'
import CodeConfigCustomizer from '../components/CodeConfigCustomizer.vue'
import Kbd from '../components/Kbd.vue' import Kbd from '../components/Kbd.vue'
import ThemePopover from '../components/ThemePopover.vue' import ThemePopover from '../components/ThemePopover.vue'
@ -94,7 +94,7 @@ watch(() => $route.path, (n) => {
<Logo /> <Logo />
<nav <nav
class="flex items-center space-x-6 text-sm font-medium" class="flex items-center max-lg:space-x-4 space-x-6 text-sm font-medium"
> >
<a <a
v-for="route in docsConfig.mainNav" v-for="route in docsConfig.mainNav"
@ -120,38 +120,42 @@ watch(() => $route.path, (n) => {
class="relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-64" class="relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-64"
@click="isOpen = true" @click="isOpen = true"
> >
<span className="hidden lg:inline-flex">Search documentation...</span> <span class="hidden lg:inline-flex">Search documentation...</span>
<span className="inline-flex lg:hidden">Search...</span> <span class="inline-flex lg:hidden">Search...</span>
<kbd className="pointer-events-none absolute right-[0.3rem] top-[0.3rem] hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex"> <Kbd class="pointer-events-none absolute right-[0.3rem] top-[0.3rem] hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
<span className="text-xs"></span>K <span class="text-xs"></span>K
</kbd> </Kbd>
</Button> </Button>
</div> </div>
<nav class="flex items-center gap-x-1"> <nav class="flex items-center">
<ThemePopover /> <ThemePopover />
<CodeConfigCustomizer />
<Button <Button
v-for="link in links" v-for="link in links"
:key="link.name" :key="link.name"
as="a" as="a"
class="w-9 h-9"
:href="link.href" target="_blank" :href="link.href" target="_blank"
:variant="'ghost'" :variant="'ghost'"
:size="'sm'" :size="'icon'"
> >
<component :is="link.icon" class="w-[20px] h-5" /> <component :is="link.icon" class="w-5 h-5" />
</Button> </Button>
<ClientOnly> <ClientOnly>
<Button <Button
class="flex items-center justify-center" class="w-9 h-9"
aria-label="Toggle dark mode" aria-label="Toggle dark mode"
:variant="'ghost'" :variant="'ghost'"
:size="'icon'" @click="toggleDark()" :size="'icon'"
@click="toggleDark()"
> >
<component <component
:is="isDark ? RadixIconsSun : RadixIconsMoon" :is="isDark ? RadixIconsSun : RadixIconsMoon"
class="w-[20px] h-5 text-foreground" class="w-5 h-5 text-foreground"
/> />
</Button> </Button>
</ClientOnly> </ClientOnly>
@ -297,4 +301,4 @@ watch(() => $route.path, (n) => {
<NewYorkSonner :theme="'system'" /> <NewYorkSonner :theme="'system'" />
<NewYorkToaster /> <NewYorkToaster />
</div> </div>
</template> </template>../components/CodeConfigCustomizer.vue

View File

@ -0,0 +1,20 @@
import type { MarkdownRenderer } from 'vitepress'
export default function (md: MarkdownRenderer) {
const defaultFenceRenderer = md.renderer.rules.fence
if (!defaultFenceRenderer)
return
md.renderer.rules.fence = function (tokens, idx, options, env, self) {
// Check if this is a code block
const token = tokens[idx]
const isAllowedExtension = (token.info.includes('vue') || token.info.includes('astro') || token.info.includes('ts'))
if (token && token.tag === 'code' && isAllowedExtension) {
// Wrap the code block in CodeWrapper
return `<CodeWrapper>${defaultFenceRenderer(tokens, idx, options, env, self)}</CodeWrapper>`
}
// If not a code block, return the default rendering
return defaultFenceRenderer(tokens, idx, options, env, self)
}
}

View File

@ -1,7 +1,5 @@
import { dirname, resolve } from 'node:path' import type { MarkdownRenderer } from 'vitepress'
import fs from 'node:fs' import { parseProps } from './utils'
import type { MarkdownEnv, MarkdownRenderer } from 'vitepress'
import { generateDemoComponent, parseProps } from './utils'
export default function (md: MarkdownRenderer) { export default function (md: MarkdownRenderer) {
function addRenderRule(type: string) { function addRenderRule(type: string) {
@ -12,31 +10,9 @@ export default function (md: MarkdownRenderer) {
if (!content.match(/^<ComponentPreview\s/) || !content.endsWith('/>')) if (!content.match(/^<ComponentPreview\s/) || !content.endsWith('/>'))
return defaultRender!(tokens, idx, options, env, self) return defaultRender!(tokens, idx, options, env, self)
const { path } = env as MarkdownEnv
const props = parseProps(content) const props = parseProps(content)
const { attrs } = props
const { name, attrs } = props const demoScripts = `<ComponentPreview ${attrs ?? ''} v-bind='${JSON.stringify(props)}'></ComponentPreview>`.trim()
const pluginPath = dirname(__dirname)
const srcPath = resolve(pluginPath, '../../src/lib/registry/default/example/', `${name}.vue`).replace(/\\/g, '/')
if (!fs.existsSync(srcPath)) {
console.error(`rendering ${path}: ${srcPath} does not exist`)
return defaultRender!(tokens, idx, options, env, self)
}
let code = fs.readFileSync(srcPath, 'utf-8')
code = code.replaceAll(
'@/lib/registry/default/',
'@/components/',
)
const demoScripts = generateDemoComponent(md, env, {
attrs,
props,
code,
path: srcPath,
})
return demoScripts return demoScripts
} }
} }

View File

@ -1,6 +1,5 @@
// Credit to @hairyf https://github.com/hairyf/markdown-it-vitepress-demo // Credit to @hairyf https://github.com/hairyf/markdown-it-vitepress-demo
import type { MarkdownEnv, MarkdownRenderer } from 'vitepress'
import { baseParse } from '@vue/compiler-core' import { baseParse } from '@vue/compiler-core'
import type { AttributeNode, ElementNode } from '@vue/compiler-core' import type { AttributeNode, ElementNode } from '@vue/compiler-core'
@ -11,36 +10,6 @@ export interface GenerateOptions {
code: string code: string
} }
export function parse(
md: MarkdownRenderer,
env: MarkdownEnv,
{ code, attrs: _attrs, props }: GenerateOptions,
) {
const highlightedHtml = md.options.highlight!(code, 'vue', _attrs || '')
const sfcTsHtml = highlightedHtml
const attrs
= `sfcTsCode="${encodeURIComponent(code)}"\n`
+ `sfcTsHtml="${encodeURIComponent(sfcTsHtml)}"\n`
+ `v-bind='${JSON.stringify(props)}'\n`
return {
attrs,
highlightedHtml,
sfcTsCode: code,
sfcTsHtml,
}
}
export function generateDemoComponent(
md: MarkdownRenderer,
env: MarkdownEnv,
options: GenerateOptions,
) {
const { attrs } = parse(md, env, options)
return `<ComponentPreview \n${attrs}></ComponentPreview>`.trim()
}
export function isUndefined(v: any): v is undefined { export function isUndefined(v: any): v is undefined {
return v === undefined || v === null return v === undefined || v === null
} }

View File

@ -18,6 +18,7 @@ export function makeCodeSandboxParams(componentName: string, style: Style, sourc
export function makeStackblitzParams(componentName: string, style: Style, sources: Record<string, string>) { export function makeStackblitzParams(componentName: string, style: Style, sources: Record<string, string>) {
const files: Record<string, string> = {} const files: Record<string, string> = {}
Object.entries(constructFiles(componentName, style, sources)).forEach(([k, v]) => (files[`${k}`] = typeof v.content === 'object' ? JSON.stringify(v.content, null, 2) : v.content)) Object.entries(constructFiles(componentName, style, sources)).forEach(([k, v]) => (files[`${k}`] = typeof v.content === 'object' ? JSON.stringify(v.content, null, 2) : v.content))
return sdk.openProject({ return sdk.openProject({
title: `${componentName} - Radix Vue`, title: `${componentName} - Radix Vue`,
files, files,
@ -91,6 +92,7 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
'shadcn-vue': 'latest', 'shadcn-vue': 'latest',
'typescript': 'latest', 'typescript': 'latest',
'vaul-vue': 'latest', 'vaul-vue': 'latest',
'vue-sonner': 'latest',
'@unovis/vue': 'latest', '@unovis/vue': 'latest',
'@unovis/ts': 'latest', '@unovis/ts': 'latest',
} }
@ -104,6 +106,7 @@ function constructFiles(componentName: string, style: Style, sources: Record<str
'autoprefixer': 'latest', 'autoprefixer': 'latest',
} }
// We have static replace here as this is only showing for code reproduction, doesn't need dynamic codeConfig
const transformImportPath = (code: string) => { const transformImportPath = (code: string) => {
let parsed = code let parsed = code
parsed = parsed.replaceAll(`@/lib/registry/${style}`, '@/components') parsed = parsed.replaceAll(`@/lib/registry/${style}`, '@/components')

View File

@ -80,12 +80,47 @@ export const Index = {
component: () => import("../src/lib/registry/default/example/BadgeSecondaryDemo.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/example/BadgeSecondaryDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BadgeSecondaryDemo.vue"], files: ["../src/lib/registry/default/example/BadgeSecondaryDemo.vue"],
}, },
"BarChartDemo": { "BreadcrumbDemo": {
name: "BarChartDemo", name: "BreadcrumbDemo",
type: "components:example", type: "components:example",
registryDependencies: ["chart-bar"], registryDependencies: ["breadcrumb","dropdown-menu"],
component: () => import("../src/lib/registry/default/example/BarChartDemo.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/example/BreadcrumbDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BarChartDemo.vue"], files: ["../src/lib/registry/default/example/BreadcrumbDemo.vue"],
},
"BreadcrumbDropdown": {
name: "BreadcrumbDropdown",
type: "components:example",
registryDependencies: ["breadcrumb","dropdown-menu"],
component: () => import("../src/lib/registry/default/example/BreadcrumbDropdown.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbDropdown.vue"],
},
"BreadcrumbEllipsisDemo": {
name: "BreadcrumbEllipsisDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/default/example/BreadcrumbEllipsisDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbEllipsisDemo.vue"],
},
"BreadcrumbLinkDemo": {
name: "BreadcrumbLinkDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/default/example/BreadcrumbLinkDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbLinkDemo.vue"],
},
"BreadcrumbResponsive": {
name: "BreadcrumbResponsive",
type: "components:example",
registryDependencies: ["breadcrumb","button","drawer","dropdown-menu"],
component: () => import("../src/lib/registry/default/example/BreadcrumbResponsive.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbResponsive.vue"],
},
"BreadcrumbSeparatorDemo": {
name: "BreadcrumbSeparatorDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/default/example/BreadcrumbSeparatorDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/default/example/BreadcrumbSeparatorDemo.vue"],
}, },
"ButtonAsChildDemo": { "ButtonAsChildDemo": {
name: "ButtonAsChildDemo", name: "ButtonAsChildDemo",
@ -1159,12 +1194,47 @@ export const Index = {
component: () => import("../src/lib/registry/new-york/example/BadgeSecondaryDemo.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/example/BadgeSecondaryDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BadgeSecondaryDemo.vue"], files: ["../src/lib/registry/new-york/example/BadgeSecondaryDemo.vue"],
}, },
"BarChartDemo": { "BreadcrumbDemo": {
name: "BarChartDemo", name: "BreadcrumbDemo",
type: "components:example", type: "components:example",
registryDependencies: ["chart-bar"], registryDependencies: ["breadcrumb","dropdown-menu"],
component: () => import("../src/lib/registry/new-york/example/BarChartDemo.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/example/BreadcrumbDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BarChartDemo.vue"], files: ["../src/lib/registry/new-york/example/BreadcrumbDemo.vue"],
},
"BreadcrumbDropdown": {
name: "BreadcrumbDropdown",
type: "components:example",
registryDependencies: ["breadcrumb","dropdown-menu"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbDropdown.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbDropdown.vue"],
},
"BreadcrumbEllipsisDemo": {
name: "BreadcrumbEllipsisDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbEllipsisDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbEllipsisDemo.vue"],
},
"BreadcrumbLinkDemo": {
name: "BreadcrumbLinkDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbLinkDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbLinkDemo.vue"],
},
"BreadcrumbResponsive": {
name: "BreadcrumbResponsive",
type: "components:example",
registryDependencies: ["breadcrumb","button","drawer","dropdown-menu"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbResponsive.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbResponsive.vue"],
},
"BreadcrumbSeparatorDemo": {
name: "BreadcrumbSeparatorDemo",
type: "components:example",
registryDependencies: ["breadcrumb"],
component: () => import("../src/lib/registry/new-york/example/BreadcrumbSeparatorDemo.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/example/BreadcrumbSeparatorDemo.vue"],
}, },
"ButtonAsChildDemo": { "ButtonAsChildDemo": {
name: "ButtonAsChildDemo", name: "ButtonAsChildDemo",

View File

@ -1,7 +1,7 @@
{ {
"name": "www", "name": "www",
"type": "module", "type": "module",
"version": "0.10.1", "version": "0.10.2",
"files": [ "files": [
"dist" "dist"
], ],
@ -18,20 +18,21 @@
"@formkit/auto-animate": "^0.8.1", "@formkit/auto-animate": "^0.8.1",
"@radix-icons/vue": "^1.0.0", "@radix-icons/vue": "^1.0.0",
"@stackblitz/sdk": "^1.9.0", "@stackblitz/sdk": "^1.9.0",
"@tanstack/vue-table": "^8.13.2", "@tanstack/vue-table": "^8.14.0",
"@unovis/ts": "^1.3.5", "@unovis/ts": "^1.3.5",
"@unovis/vue": "^1.3.5", "@unovis/vue": "^1.3.5",
"@vee-validate/zod": "^4.12.5", "@vee-validate/zod": "^4.12.6",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"codesandbox": "^2.2.3", "codesandbox": "^2.2.3",
"date-fns": "^3.3.1", "date-fns": "^3.6.0",
"embla-carousel": "^8.0.0", "embla-carousel": "^8.0.0",
"embla-carousel-autoplay": "^8.0.0", "embla-carousel-autoplay": "^8.0.0",
"embla-carousel-vue": "^8.0.0", "embla-carousel-vue": "^8.0.0",
"lucide-vue-next": "^0.350.0", "lucide-vue-next": "^0.359.0",
"radix-vue": "^1.5.0", "magic-string": "^0.30.8",
"radix-vue": "^1.5.3",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2", "v-calendar": "^3.1.2",
"vaul-vue": "^0.1.0", "vaul-vue": "^0.1.0",
@ -47,9 +48,9 @@
"@iconify-json/tabler": "^1.1.106", "@iconify-json/tabler": "^1.1.106",
"@iconify/json": "^2.2.189", "@iconify/json": "^2.2.189",
"@iconify/vue": "^4.1.1", "@iconify/vue": "^4.1.1",
"@shikijs/transformers": "^1.1.7", "@shikijs/transformers": "^1.2.0",
"@types/lodash.template": "^4.5.3", "@types/lodash.template": "^4.5.3",
"@types/node": "^20.11.25", "@types/node": "^20.11.30",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0", "@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/compiler-core": "^3.4.21", "@vue/compiler-core": "^3.4.21",
@ -60,14 +61,14 @@
"oxc-parser": "^0.8.0", "oxc-parser": "^0.8.0",
"pathe": "^1.1.2", "pathe": "^1.1.2",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"shiki": "^1.1.7", "shiki": "^1.2.0",
"tailwind-merge": "^2.2.1", "tailwind-merge": "^2.2.2",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"tsx": "^4.7.1", "tsx": "^4.7.1",
"typescript": "^5.4.2", "typescript": "^5.4.2",
"unplugin-icons": "^0.18.5", "unplugin-icons": "^0.18.5",
"vite": "^5.1.5", "vite": "^5.2.2",
"vitepress": "^1.0.0-rc.45", "vitepress": "^1.0.0-rc.45",
"vue-tsc": "^2.0.6" "vue-tsc": "^2.0.7"
} }
} }

View File

@ -0,0 +1,176 @@
<mxfile host="app.diagrams.net" modified="2024-03-15T08:14:00.888Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0" etag="eUNOuIh_rCPXdI6LG0BE" version="24.0.6" type="device">
<diagram name="Page-1" id="10a91c8b-09ff-31b1-d368-03940ed4cc9e">
<mxGraphModel dx="1636" dy="971" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="PaMXV6_IjdSjTMUUNi7L-41" value="" style="rounded=0;whiteSpace=wrap;html=1;fontColor=none;noLabel=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="30" y="70" width="1010" height="680" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-1" value="Shadcn/Vue" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="380" y="130" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-2" value="Packages" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="160" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-3" value="Apps/www" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="630" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-4" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="PaMXV6_IjdSjTMUUNi7L-27" target="62893188c0fa7362-3" edge="1">
<mxGeometry x="-0.3002" y="13" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-5" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-1" target="62893188c0fa7362-2" edge="1">
<mxGeometry x="-0.359" y="-11" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-8" value="Module" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="80" y="360" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-9" value="CLI" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" parent="1" vertex="1">
<mxGeometry x="220" y="360" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="62893188c0fa7362-14" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-2" target="62893188c0fa7362-8" edge="1">
<mxGeometry x="-0.2" y="-14" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-15" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-2" target="62893188c0fa7362-9" edge="1">
<mxGeometry x="-0.2" y="14" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-16" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.75;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-3" target="PaMXV6_IjdSjTMUUNi7L-2" edge="1">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="644.5454545454545" y="360" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="62893188c0fa7362-17" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.25;entryY=0;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" parent="1" source="62893188c0fa7362-3" target="PaMXV6_IjdSjTMUUNi7L-1" edge="1">
<mxGeometry x="-0.1294" y="17" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="782.7272727272725" y="360" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-1" value="Registry" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="720" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-2" value=".vitepress" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="420" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-3" value="Scripts" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="870" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-4" value="Src" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="570" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-7" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.333;entryY=0.017;entryDx=0;entryDy=0;exitX=1;exitY=1;exitDx=0;exitDy=0;entryPerimeter=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="62893188c0fa7362-3" target="PaMXV6_IjdSjTMUUNi7L-3">
<mxGeometry x="-0.1294" y="17" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="841" y="270" as="sourcePoint" />
<mxPoint x="910" y="320" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-9" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.358;exitY=1.017;exitDx=0;exitDy=0;exitPerimeter=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="62893188c0fa7362-3" target="PaMXV6_IjdSjTMUUNi7L-4">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="720" y="540" as="sourcePoint" />
<mxPoint x="613" y="600" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-11" value="Content" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="420" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-12" value="Examples" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="570" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-13" value="Lib/Registry" style="whiteSpace=wrap;html=1;rounded=1;shadow=0;labelBackgroundColor=none;strokeWidth=1;fontFamily=Garamond;fontSize=17;align=center;sketch=1;curveFitting=1;jiggle=2;" vertex="1" parent="1">
<mxGeometry x="720" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-14" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.308;exitY=1.033;exitDx=0;exitDy=0;exitPerimeter=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-4" target="PaMXV6_IjdSjTMUUNi7L-11">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="560" y="470" as="sourcePoint" />
<mxPoint x="507" y="529" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-15" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-4" target="PaMXV6_IjdSjTMUUNi7L-12">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="657" y="442" as="sourcePoint" />
<mxPoint x="540" y="560" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-16" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.75;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-4" target="PaMXV6_IjdSjTMUUNi7L-13">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="660" y="435" as="sourcePoint" />
<mxPoint x="660" y="555" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-20" value="Default" style="rounded=1;whiteSpace=wrap;html=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" vertex="1" parent="1">
<mxGeometry x="650" y="630" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-21" value="New York" style="rounded=1;whiteSpace=wrap;html=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" vertex="1" parent="1">
<mxGeometry x="800" y="630" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-22" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-13" target="PaMXV6_IjdSjTMUUNi7L-20">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="730" y="670" as="sourcePoint" />
<mxPoint x="530" y="818" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-23" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="PaMXV6_IjdSjTMUUNi7L-13" target="PaMXV6_IjdSjTMUUNi7L-21">
<mxGeometry x="-0.2614" y="-13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="790" y="670" as="sourcePoint" />
<mxPoint x="750" y="820" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-26" value="1" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="140" y="230" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-28" value="3" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="400" y="350" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-29" value="4" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="550" y="350" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-30" value="5" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="700" y="350" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-31" value="6" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="850" y="350" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-32" value="7" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="400" y="480" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-33" value="8" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="550" y="480" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-34" value="9" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="700" y="480" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-36" value="10" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="630" y="610" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-38" value="11" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="780" y="610" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-39" value="" style="rounded=0;html=1;labelBackgroundColor=none;startArrow=none;startFill=0;startSize=5;endArrow=none;endFill=0;endSize=5;jettySize=auto;orthogonalLoop=1;strokeWidth=1;fontFamily=Garamond;fontSize=17;sketch=1;curveFitting=1;jiggle=2;shadow=0;" edge="1" parent="1" source="62893188c0fa7362-1" target="PaMXV6_IjdSjTMUUNi7L-27">
<mxGeometry x="-0.3002" y="13" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="500" y="189" as="sourcePoint" />
<mxPoint x="630" y="251" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="PaMXV6_IjdSjTMUUNi7L-27" value="2" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;sketch=1;curveFitting=1;jiggle=2;fontFamily=Garamond;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="610" y="230" width="40" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -89,7 +89,6 @@ This is used to generate the default color palette for your components. **This c
} }
``` ```
### tailwind.cssVariables ### tailwind.cssVariables
You can choose between using CSS variables or Tailwind CSS utility classes for theming. You can choose between using CSS variables or Tailwind CSS utility classes for theming.
@ -109,7 +108,6 @@ For more information, see the [theming docs](/docs/theming).
**This cannot be changed after initialization.** To switch between CSS variables and utility classes, you'll have to delete and re-install your components. **This cannot be changed after initialization.** To switch between CSS variables and utility classes, you'll have to delete and re-install your components.
## aliases ## aliases
The CLI uses these values and the `paths` config from your `tsconfig.json` or `jsconfig.json` file to place generated components in the correct location. The CLI uses these values and the `paths` config from your `tsconfig.json` or `jsconfig.json` file to place generated components in the correct location.
@ -118,7 +116,6 @@ Path aliases have to be set up in your `tsconfig.json` or `jsconfig.json` file.
> A fallback to `tsconfig.app.json` if no `paths` were found in `tsconfig.json` > A fallback to `tsconfig.app.json` if no `paths` were found in `tsconfig.json`
<Callout class="mt-6"> <Callout class="mt-6">
**Important:** If you're using the `src` directory, make sure it is included **Important:** If you're using the `src` directory, make sure it is included
@ -126,7 +123,6 @@ Path aliases have to be set up in your `tsconfig.json` or `jsconfig.json` file.
</Callout> </Callout>
### aliases.utils ### aliases.utils
Import alias for your utility functions. Import alias for your utility functions.

View File

@ -5,12 +5,10 @@ source: apps/www/src/lib/registry/default/ui/accordion
primitive: https://www.radix-vue.com/components/accordion.html primitive: https://www.radix-vue.com/components/accordion.html
--- ---
<ComponentPreview name="AccordionDemo" class="sm:max-w-[70%]" /> <ComponentPreview name="AccordionDemo" class="sm:max-w-[70%]" />
## Installation ## Installation
<Steps> <Steps>
### Run the following command ### Run the following command
@ -49,7 +47,6 @@ module.exports = {
</Steps> </Steps>
## Usage ## Usage
```vue ```vue
@ -68,4 +65,3 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/
</Accordion> </Accordion>
</template> </template>
``` ```

View File

@ -5,13 +5,10 @@ source: apps/www/src/lib/registry/default/ui/alert-dialog
primitive: https://www.radix-vue.com/components/alert-dialog.html primitive: https://www.radix-vue.com/components/alert-dialog.html
--- ---
<ComponentPreview name="AlertDialogDemo" /> <ComponentPreview name="AlertDialogDemo" />
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add alert-dialog npx shadcn-vue@latest add alert-dialog
``` ```

View File

@ -3,12 +3,10 @@ title: Alert
description: Displays a callout for user attention. description: Displays a callout for user attention.
--- ---
<ComponentPreview name="AlertDemo" /> <ComponentPreview name="AlertDemo" />
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add alert npx shadcn-vue@latest add alert
``` ```
@ -36,9 +34,6 @@ import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
<ComponentPreview name="AlertDemo" /> <ComponentPreview name="AlertDemo" />
### Destructive ### Destructive
<ComponentPreview name="AlertDestructiveDemo" /> <ComponentPreview name="AlertDestructiveDemo" />

View File

@ -5,7 +5,6 @@ source: apps/www/src/lib/registry/default/ui/aspect-ratio
primitive: https://www.radix-vue.com/components/aspect-ratio.html primitive: https://www.radix-vue.com/components/aspect-ratio.html
--- ---
<ComponentPreview name="AspectRatioDemo" /> <ComponentPreview name="AspectRatioDemo" />
## Installation ## Installation

View File

@ -5,13 +5,10 @@ source: apps/www/src/lib/registry/default/ui/avatar
primitive: https://www.radix-vue.com/components/avatar.html primitive: https://www.radix-vue.com/components/avatar.html
--- ---
<ComponentPreview name="AvatarDemo" /> <ComponentPreview name="AvatarDemo" />
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add avatar npx shadcn-vue@latest add avatar
``` ```

View File

@ -3,7 +3,6 @@ title: Badge
description: Displays a badge or a component that looks like a badge. description: Displays a badge or a component that looks like a badge.
--- ---
<ComponentPreview name="BadgeDemo" /> <ComponentPreview name="BadgeDemo" />
## Installation ## Installation
@ -80,14 +79,12 @@ import { Badge } from '@/components/ui/badge'
</template> </template>
``` ```
## Examples ## Examples
### Default ### Default
<ComponentPreview name="BadgeDemo" /> <ComponentPreview name="BadgeDemo" />
### Secondary ### Secondary
<ComponentPreview name="BadgeSecondaryDemo" /> <ComponentPreview name="BadgeSecondaryDemo" />

View File

@ -0,0 +1,205 @@
---
title: Breadcrumb
description: Displays the path to the current resource using a hierarchy of links.
---
<ComponentPreview name="BreadcrumbDemo" class="[&_.preview]:p-2" />
## Installation
```bash
npx shadcn-vue@latest add breadcrumb
```
## Usage
```vue
<script setup lang="ts">
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/lib/components/ui/breadcrumb'
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">
Home
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink href="/components">
Components
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</template>
```
## Examples
### Custom separator
Use a custom component as `slot` for `<BreadcrumbSeparator />` to create a custom separator.
<ComponentPreview name="BreadcrumbSeparatorDemo" />
```vue showLineNumbers {2,20-22}
<script setup lang="ts">
import { Slash } from 'lucide-react'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
} from '@/lib/components/ui/breadcrumb'
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">
Home
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator>
<Slash />
</BreadcrumbSeparator>
<BreadcrumbItem>
<BreadcrumbLink href="/components">
Components
</BreadcrumbLink>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</template>
```
---
### Dropdown
You can compose `<BreadcrumbItem />` with a `<DropdownMenu />` to create a dropdown in the breadcrumb.
<ComponentPreview name="BreadcrumbDropdown" class="[&_.preview]:p-2" />
```vue showLineNumbers {2-7,16-26}
<script setup lang="ts">
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/lib/components/ui/dropdown-menu'
import { BreadcrumbItem } from '@/lib/components/ui/breadcrumb'
import ChevronDownIcon from '~icons/radix-icons/chevron-down'
</script>
<template>
<BreadcrumbItem>
<DropdownMenu>
<DropdownMenuTrigger class="flex items-center gap-1">
Components
<ChevronDownIcon />
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem>Documentation</DropdownMenuItem>
<DropdownMenuItem>Themes</DropdownMenuItem>
<DropdownMenuItem>GitHub</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</BreadcrumbItem>
</template>
```
---
### Collapsed
We provide a `<BreadcrumbEllipsis />` component to show a collapsed state when the breadcrumb is too long.
<ComponentPreview name="BreadcrumbEllipsisDemo" class="[&_.preview]:p-2" />
```vue showLineNumbers {3,15}
<script setup lang="ts">
import {
Breadcrumb,
BreadcrumbEllipsis,
BreadcrumbItem,
BreadcrumbList,
} from '@/lib/components/ui/breadcrumb'
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<!-- ... -->
<BreadcrumbItem>
<BreadcrumbEllipsis />
</BreadcrumbItem>
<!-- ... -->
</BreadcrumbList>
</Breadcrumb>
</template>
```
---
### Link component
To use a custom link component from your routing library, you can use the `asChild` prop on `<BreadcrumbLink />`.
<ComponentPreview name="BreadcrumbLinkDemo" />
```vue showLineNumbers {15-19}
<script setup lang="ts">
import { RouterLink } from 'vue-router'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
} from '@/lib/components/ui/breadcrumb'
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink as-child>
<RouterLink to="/">
Home
</RouterLink>
</BreadcrumbLink>
</BreadcrumbItem>
<!-- -->
</BreadcrumbList>
</Breadcrumb>
</template>
```
---
### Responsive
Here's an example of a responsive breadcrumb that composes `<BreadcrumbItem />` with `<BreadcrumbEllipsis />`, `<DropdownMenu />`, and `<Drawer />`.
It displays a dropdown on desktop and a drawer on mobile.
<ComponentPreview name="BreadcrumbResponsive" class="[&_.preview]:p-2" />

View File

@ -3,7 +3,6 @@ title: Button
description: Displays a button or a component that looks like a button. description: Displays a button or a component that looks like a button.
--- ---
<ComponentPreview name="ButtonDemo" /> <ComponentPreview name="ButtonDemo" />
## Installation ## Installation
@ -94,24 +93,20 @@ import { Button } from '@/components/ui/button'
</template> </template>
``` ```
## Examples ## Examples
### Primary ### Primary
<ComponentPreview name="ButtonDemo" /> <ComponentPreview name="ButtonDemo" />
### Secondary ### Secondary
<ComponentPreview name="ButtonSecondaryDemo" /> <ComponentPreview name="ButtonSecondaryDemo" />
### Destructive ### Destructive
<ComponentPreview name="ButtonDestructiveDemo" /> <ComponentPreview name="ButtonDestructiveDemo" />
### Outline ### Outline
<ComponentPreview name="ButtonOutlineDemo" /> <ComponentPreview name="ButtonOutlineDemo" />

View File

@ -5,7 +5,6 @@ source: apps/www/src/lib/registry/default/ui/calendar
primitive: https://vcalendar.io/ primitive: https://vcalendar.io/
--- ---
<ComponentPreview name="CalendarDemo" /> <ComponentPreview name="CalendarDemo" />
## About ## About
@ -34,13 +33,10 @@ npm install v-calendar
### Copy and paste the following code into your project ### Copy and paste the following code into your project
<<< @/lib/registry/default/ui/calendar/Calendar.vue <<< @/lib/registry/default/ui/calendar/Calendar.vue
</Steps> </Steps>
</template> </template>
</TabPreview> </TabPreview>

View File

@ -3,13 +3,10 @@ title: Card
description: Displays a card with header, content, and footer. description: Displays a card with header, content, and footer.
--- ---
<ComponentPreview name="CardFormDemo" /> <ComponentPreview name="CardFormDemo" />
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add card npx shadcn-vue@latest add card
``` ```

View File

@ -5,13 +5,10 @@ source: apps/www/src/lib/registry/default/ui/checkbox
primitive: https://www.radix-vue.com/components/checkbox.html primitive: https://www.radix-vue.com/components/checkbox.html
--- ---
<ComponentPreview name="CheckboxDemo" /> <ComponentPreview name="CheckboxDemo" />
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add checkbox npx shadcn-vue@latest add checkbox
``` ```

View File

@ -5,12 +5,10 @@ source: apps/www/src/lib/registry/default/ui/collapsible
primitive: https://www.radix-vue.com/components/collapsible.html primitive: https://www.radix-vue.com/components/collapsible.html
--- ---
<ComponentPreview name="CollapsibleDemo" /> <ComponentPreview name="CollapsibleDemo" />
## Installation ## Installation
<Steps> <Steps>
### Run the following command ### Run the following command
@ -49,7 +47,6 @@ module.exports = {
</Steps> </Steps>
## Usage ## Usage
```vue ```vue

View File

@ -5,14 +5,10 @@ source: apps/www/src/lib/registry/default/ui/command
primitive: https://www.radix-vue.com/components/combobox.html primitive: https://www.radix-vue.com/components/combobox.html
--- ---
<ComponentPreview name="CommandDemo" /> <ComponentPreview name="CommandDemo" />
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add command npx shadcn-vue@latest add command
``` ```

View File

@ -5,7 +5,6 @@ source: apps/www/src/lib/registry/default/ui/context-menu
primitive: https://www.radix-vue.com/components/context-menu.html primitive: https://www.radix-vue.com/components/context-menu.html
--- ---
<ComponentPreview name="ContextMenuDemo" /> <ComponentPreview name="ContextMenuDemo" />
## Installation ## Installation

View File

@ -3,7 +3,6 @@ title: Date Picker
description: A date picker component with range and presets. description: A date picker component with range and presets.
--- ---
<ComponentPreview name="DatePickerDemo" /> <ComponentPreview name="DatePickerDemo" />
## Installation ## Installation
@ -53,7 +52,6 @@ const date = ref<Date>()
</template> </template>
``` ```
## Examples ## Examples
### Date Picker ### Date Picker

View File

@ -5,7 +5,6 @@ source: apps/www/src/lib/registry/default/ui/dialog
primitive: https://www.radix-vue.com/components/dialog.html primitive: https://www.radix-vue.com/components/dialog.html
--- ---
<ComponentPreview name="DialogDemo" /> <ComponentPreview name="DialogDemo" />
## Installation ## Installation
@ -67,7 +66,6 @@ import {
To activate the `Dialog` component from within a `Context Menu` or `Dropdown Menu`, you must encase the `Context Menu` or `Dropdown Menu` component in the `Dialog` component. For more information, refer to the linked issue [here](https://github.com/radix-ui/primitives/issues/1836). To activate the `Dialog` component from within a `Context Menu` or `Dropdown Menu`, you must encase the `Context Menu` or `Dropdown Menu` component in the `Dialog` component. For more information, refer to the linked issue [here](https://github.com/radix-ui/primitives/issues/1836).
```js:line-numbers showLineNumber{14-25} ```js:line-numbers showLineNumber{14-25}
<Dialog> <Dialog>
<ContextMenu> <ContextMenu>

View File

@ -5,7 +5,6 @@ source: apps/www/src/lib/registry/default/ui/hover-card
primitive: https://www.radix-vue.com/components/hover-card.html primitive: https://www.radix-vue.com/components/hover-card.html
--- ---
<ComponentPreview name="HoverCardDemo" /> <ComponentPreview name="HoverCardDemo" />
## Installation ## Installation

View File

@ -7,7 +7,6 @@ primitive: https://www.radix-vue.com/components/label.html
<ComponentPreview name="LabelDemo" /> <ComponentPreview name="LabelDemo" />
## Installation ## Installation
<TabPreview name="CLI"> <TabPreview name="CLI">

View File

@ -9,7 +9,6 @@ primitive: https://www.radix-vue.com/components/menubar.html
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add menubar npx shadcn-vue@latest add menubar
``` ```

View File

@ -64,4 +64,3 @@ import { navigationMenuTriggerStyle } from '@/components/ui/navigation-menu'
</NavigationMenuItem> </NavigationMenuItem>
</template> </template>
``` ```

View File

@ -9,7 +9,6 @@ primitive: https://www.radix-vue.com/components/pagination.html
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add pagination npx shadcn-vue@latest add pagination
``` ```

View File

@ -5,12 +5,10 @@ source: apps/www/src/lib/registry/default/ui/popover
primitive: https://www.radix-vue.com/components/popover.html primitive: https://www.radix-vue.com/components/popover.html
--- ---
<ComponentPreview name="PopoverDemo" /> <ComponentPreview name="PopoverDemo" />
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add popover npx shadcn-vue@latest add popover
``` ```

View File

@ -7,8 +7,6 @@ primitive: https://www.radix-vue.com/components/progress.html
<ComponentPreview name="ProgressDemo" /> <ComponentPreview name="ProgressDemo" />
## Installation ## Installation
<TabPreview name="CLI"> <TabPreview name="CLI">

View File

@ -9,7 +9,6 @@ primitive: https://www.radix-vue.com/components/radio-group.html
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add radio-group npx shadcn-vue@latest add radio-group
``` ```

View File

@ -84,7 +84,7 @@ import {
</script> </script>
<template> <template>
<ResizablePanelGroup direction="horizontal"> <ResizablePanelGroup direction="vertical">
<ResizablePanel>One</ResizablePanel> <ResizablePanel>One</ResizablePanel>
<ResizableHandle /> <ResizableHandle />
<ResizablePanel>Two</ResizablePanel> <ResizablePanel>Two</ResizablePanel>

View File

@ -9,7 +9,6 @@ primitive: https://www.radix-vue.com/components/scroll-area.html
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add scroll-area npx shadcn-vue@latest add scroll-area
``` ```
@ -37,4 +36,3 @@ import { ScrollArea } from '@/components/ui/scroll-area'
### Horizontal Scrolling ### Horizontal Scrolling
<ComponentPreview name="ScrollAreaHorizontalDemo" /> <ComponentPreview name="ScrollAreaHorizontalDemo" />

View File

@ -7,7 +7,6 @@ primitive: https://www.radix-vue.com/components/separator.html
<ComponentPreview name="SeparatorDemo" /> <ComponentPreview name="SeparatorDemo" />
## Installation ## Installation
<TabPreview name="CLI"> <TabPreview name="CLI">
@ -32,7 +31,6 @@ npm install radix-vue
<<< @/lib/registry/default/ui/separator/Separator.vue <<< @/lib/registry/default/ui/separator/Separator.vue
</Steps> </Steps>
</template> </template>

View File

@ -7,7 +7,6 @@ primitive: https://www.radix-vue.com/components/dialog.html
<ComponentPreview name="SheetDemo" /> <ComponentPreview name="SheetDemo" />
## Installation ## Installation
```bash ```bash
@ -52,7 +51,6 @@ Use the `side` property to `<SheetContent />` to indicate the edge of the screen
<ComponentPreview name="SheetSideDemo" /> <ComponentPreview name="SheetSideDemo" />
### Size ### Size
You can adjust the size of the sheet using CSS classes: You can adjust the size of the sheet using CSS classes:

View File

@ -7,7 +7,6 @@ primitive: https://www.radix-vue.com/components/switch.html
<ComponentPreview name="SwitchDemo" /> <ComponentPreview name="SwitchDemo" />
## Installation ## Installation
<TabPreview name="CLI"> <TabPreview name="CLI">

View File

@ -5,7 +5,6 @@ description: A responsive table component.
<ComponentPreview name="TableDemo" /> <ComponentPreview name="TableDemo" />
## Installation ## Installation
```bash ```bash

View File

@ -7,11 +7,8 @@ primitive: https://www.radix-vue.com/components/tabs.html
<ComponentPreview name="TabsDemo" /> <ComponentPreview name="TabsDemo" />
## Installation ## Installation
```bash ```bash
npx shadcn-vue@latest add tabs npx shadcn-vue@latest add tabs
``` ```

View File

@ -13,7 +13,6 @@ primitive: https://www.radix-vue.com/components/tags-input.html
npx shadcn-vue@latest add tags-input npx shadcn-vue@latest add tags-input
``` ```
## Usage ## Usage
### Tags with Combobox ### Tags with Combobox

View File

@ -5,7 +5,6 @@ description: Displays a form textarea or a component that looks like a textarea.
<ComponentPreview name="TextareaDemo" /> <ComponentPreview name="TextareaDemo" />
## Installation ## Installation
<TabPreview name="CLI"> <TabPreview name="CLI">

View File

@ -5,12 +5,10 @@ source: apps/www/src/lib/registry/default/ui/toast
primitive: https://www.radix-vue.com/components/toast.html primitive: https://www.radix-vue.com/components/toast.html
--- ---
<ComponentPreview name="ToastDemo" /> <ComponentPreview name="ToastDemo" />
## Installation ## Installation
<Steps> <Steps>
### Run the following command ### Run the following command
@ -35,7 +33,6 @@ import Toaster from '@/components/ui/toast/Toaster.vue'
</Steps> </Steps>
## Usage ## Usage
The `useToast` hook returns a `toast` function that you can use to display a toast. The `useToast` hook returns a `toast` function that you can use to display a toast.

View File

@ -64,30 +64,22 @@ import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
<ComponentPreview name="ToggleGroupDemo" /> <ComponentPreview name="ToggleGroupDemo" />
### Outline ### Outline
<ComponentPreview name="ToggleGroupOutlineDemo" /> <ComponentPreview name="ToggleGroupOutlineDemo" />
### Single ### Single
<ComponentPreview name="ToggleGroupSingleDemo" /> <ComponentPreview name="ToggleGroupSingleDemo" />
### Small ### Small
<ComponentPreview name="ToggleGroupSmallDemo" /> <ComponentPreview name="ToggleGroupSmallDemo" />
### Large ### Large
<ComponentPreview name="ToggleGroupLargeDemo" /> <ComponentPreview name="ToggleGroupLargeDemo" />
### Disabled ### Disabled
<ComponentPreview name="ToggleGroupDisabledDemo" /> <ComponentPreview name="ToggleGroupDisabledDemo" />

View File

@ -7,8 +7,6 @@ primitive: https://www.radix-vue.com/components/toggle.html
<ComponentPreview name="ToggleDemo" /> <ComponentPreview name="ToggleDemo" />
## Installation ## Installation
<TabPreview name="CLI"> <TabPreview name="CLI">
@ -56,30 +54,22 @@ import { Toggle } from '@/components/ui/toggle'
<ComponentPreview name="ToggleDemo" /> <ComponentPreview name="ToggleDemo" />
### Outline ### Outline
<ComponentPreview name="ToggleItalicDemo" /> <ComponentPreview name="ToggleItalicDemo" />
### With Text ### With Text
<ComponentPreview name="ToggleItalicWithTextDemo" /> <ComponentPreview name="ToggleItalicWithTextDemo" />
### Small ### Small
<ComponentPreview name="ToggleSmallDemo" /> <ComponentPreview name="ToggleSmallDemo" />
### Large ### Large
<ComponentPreview name="ToggleLargeDemo" /> <ComponentPreview name="ToggleLargeDemo" />
### Disabled ### Disabled
<ComponentPreview name="ToggleDisabledDemo" /> <ComponentPreview name="ToggleDisabledDemo" />

View File

@ -7,7 +7,6 @@ primitive: https://www.radix-vue.com/components/tooltip.html
<ComponentPreview name="TooltipDemo" /> <ComponentPreview name="TooltipDemo" />
## Installation ## Installation
```bash ```bash

View File

@ -0,0 +1,341 @@
---
title: Contribution
description: Learn on how to contribute to shadcn/vue.
---
## Introduction
Thanks for your interest in contributing to shadcn-vue.com. We're happy to have you here.
Please take a moment to review this document before submitting your first pull request. We also strongly recommend that you check for open issues and pull requests to see if someone else is working on something similar.
If you need any help, feel free to reach out to the core team on [Discord](https://chat.radix-vue.com/).
This guide provides detailed information to help new contributors.
## About this repository
This repository is a monorepo.
- We use [pnpm](https://pnpm.io) and [`workspaces`](https://pnpm.io/workspaces) for development.
## Project Structure
The GitHub repository consists of the several folders. here's a quick view.
<VPImage
alt="folder-structure"
class="block" :image="{
dark: '/diagrams/structure-dark.svg',
light: '/diagrams/structure-light.svg',
}"
/>
1. **packages** -
Contains source code for supporting `nuxt` as a module and the `cli` to add new components.
2. **apps/www** -
The main folder that holds the source code for the website and every `shadcn/vue` component. This folder contains important sub-folders and is a subproject with its own `package.json`.
3. **.vitepress** -
Contains the configuration and source code for `vitepress` and the `shadcn/vue` website.
4. **src** -
Hosts the main source code for every `shadcn/vue` component or demo and their documentation on the website.
5. **\_\_registry\_\_** -
Holds the registry file generated by `scripts/build-registry.ts` to serve components for the `cli`. This folder's content is auto-generated and should not be edited manually.
6. **scripts** -
Contains various helper scripts, such as `build-registry.ts`, which automatically generates the `__registry__` folder.
7. **content** -
This folder holds all the documentation for the `/docs` route. Each component has one `.md` file documenting the installation and usage of the component.
8. **examples** -
Holds all examples not part of `/docs`, like the [main page](/).
9. **lib/registry** -
The main folder hosts the source code for different styles of every component. This is likely the main folder you'll be changing.
We support two different styles for every component in `shadcn/vue`:
1. Default
2. New York
Every component added to the repository must support both versions, including the main source code and associated demos.
When adding or modifying components, please ensure that:
1. You make the changes for every style.
2. You update the documentation.
3. You run `pnpm build:registry` to update the registry.
## Development
Start by cloning the repository:
```bash
git clone git@github.com:radix-vue/shadcn-vue.git
```
### Install dependencies
```bash
pnpm install
```
### Run a workspace
You can use the `pnpm --filter=[WORKSPACE]` command to start the development process for a workspace or some of the shortcut command that we have setup.
#### Examples
1. To run the `shadcn-vue.com` website:
```
pnpm dev
```
2. To run the `shadcn-vue` cli package:
```
pnpm dev:cli
```
## Documentation
The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command:
```bash
pnpm dev
```
Documentation is written using [md](https://vitepress.dev/guide/markdown). You can find the documentation files in the `apps/www/src/content` directory.
## CLI
The `shadcn-vue` package is a CLI for adding components to your project. You can find the documentation for the CLI [here](https://shadcn-vue.com/docs/cli).
Any changes to the CLI should be made in the `packages/cli` directory. If you can, it would be great if you could add tests for your changes.
## Testing
Tests are written using [Vitest](https://vitest.dev). You can run all the tests from the root of the repository.
```bash
pnpm test
```
Please ensure that the tests are passing when submitting a pull request. If you're adding new features, please include tests.
## Commit Convention
Before you create a Pull Request, please check whether your commits comply with
the commit conventions used in this repository.
When you create a commit we kindly ask you to follow the convention
`category(scope or module): message` in your commit message while using one of
the following categories:
- `feat / feature`: all changes that introduce completely new code or new
features
- `fix`: changes that fix a bug (ideally you will additionally reference an
issue if present)
- `refactor`: any code related change that is not a fix nor a feature
- `docs`: changing existing or creating new documentation (i.e. README, docs for
usage of a lib or cli usage)
- `build`: all changes regarding the build of the software, changes to
dependencies or the addition of new dependencies
- `test`: all changes regarding tests (adding new tests or changing existing
ones)
- `ci`: all changes regarding the configuration of continuous integration (i.e.
github actions, ci system)
- `chore`: all changes to the repository that do not fit into any of the above
categories
e.g. `feat(components): add new prop to the avatar component`
If you are interested in the detailed specification you can visit [Conventional Commits](https://www.conventionalcommits.org/).
## SFC - Single File Components
Multiple components are integrated into one file in `shadcn/ui` - the React version of `shadcn` - while Vue only supports one component per file, hence the name Single File Component (SFC). In such cases, you need to create separate files for each component part and then export them all in an `index.ts` file.
See the [`Accordion`](https://github.com/radix-vue/shadcn-vue/tree/v0.10.2/apps/www/src/lib/registry/default/ui/accordion) source code as an example.
## Wrapping Radix-Vue Components
[Radix-Vue](https://www.radix-vue.com) hosts many low-level UI components that are used to build reusable components.
There are many cases that you need to wrap `Radix-Vue` components.
### Props & Events
All of the `Radix-Vue` compoennts expose their prop and emit types. We need to forward any props/events that are coming from outside to the `Radix-Vue` component.
To do so, we have a helper function named [`useForwardPropsEmits`](https://www.radix-vue.com/utilities/use-forward-props-emits.html) that combines props and events that must be binded to the child radix component.
To be more clear, the function `useForwardPropsEmits` takes in props and an optional emit function, and returns a
computed object that combines the parsed props and emits as props.
Here's an example from `Accordian` root component.
```vue
<script setup lang="ts">
import {
AccordionRoot,
type AccordionRootEmits,
type AccordionRootProps,
useForwardPropsEmits,
} from 'radix-vue'
const props = defineProps<AccordionRootProps>()
const emits = defineEmits<AccordionRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<AccordionRoot v-bind="forwarded">
<slot />
</AccordionRoot>
</template>
```
As you can see, `AccordionRootEmits` and `AccordionRootProps` types are imported from radix, combined with `useForwardPropsEmits` and then are binded using `v-bind` syntaxt.
### CSS Classes
There are cases when we want to accept `class` as a prop in our `shadcn/vue` component and then combine it with a default tailwind class on our `radix-vue` component via `cn` utility function.
In these cases, we can not use `v-bind`, because this would lead in [double class binding](https://github.com/radix-vue/shadcn-vue/pull/241).
Take a look at `DrawerDescription.vue`.
```vue
<script lang="ts" setup>
import type { DrawerDescriptionProps } from 'vaul-vue'
import { DrawerDescription } from 'vaul-vue'
import { type HtmlHTMLAttributes, computed } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<DrawerDescriptionProps & { class?: HtmlHTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<DrawerDescription v-bind="delegatedProps" :class="cn('text-sm text-muted-foreground', props.class)">
<slot />
</DrawerDescription>
</template>
```
As you can see, we have created a computed property named `delegatedProps` to remove `class` from props, and only then bind
the returned value to our radix component (`DrawerDescription` in this case).
As for our class, we first declared it as type of `HtmlHTMLAttributes['class']` and used `cn` to merge tailwind classes from `class` prop and our own classes.
This pattern only needs to be applied when the `cn` utility is required. For instances where there are no default Tailwind classes that need to be merged with user-provided classes, this pattern is not necessary. A good example of this is the `SelectValue.vue` component.
```vue
<script setup lang="ts">
import { SelectValue, type SelectValueProps } from 'radix-vue'
const props = defineProps<SelectValueProps>()
</script>
<template>
<SelectValue v-bind="props">
<slot />
</SelectValue>
</template>
```
### Boolean Props
When you are building a wrapper for a component, in some cases you want to ignore Vue [Props Boolean Casting](https://vuejs.org/guide/components/props.html#boolean-casting).
You can either set default value as undefined for all the boolean field, or you can use [`useForwardProps`](https://www.radix-vue.com/utilities/use-forward-props.html) composable.
Take a look at `AccordionItem.vue`
```vue
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AccordionItem, type AccordionItemProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AccordionItemProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<AccordionItem
v-bind="forwardedProps"
:class="cn('border-b', props.class)"
>
<slot />
</AccordionItem>
</template>
```
Since `AccordionItemProps` type has atleast one boolean property, we need to use `useForwardProps` on the entire props object.
Note that `useForwardPropsEmits` use `useForwardProps` under the hood.
### Component as Root
Whenever your root component is a `Component` Primitive from vue, it's easier to use [`Primitive`](https://www.radix-vue.com/utilities/primitive.html) instead.
Let's take a look at `Button.vue`
```vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { Primitive, type PrimitiveProps } from 'radix-vue'
import { type ButtonVariants, buttonVariants } from '.'
import { cn } from '@/lib/utils'
interface Props extends PrimitiveProps {
variant?: ButtonVariants['variant']
size?: ButtonVariants['size']
class?: HTMLAttributes['class']
}
const props = withDefaults(defineProps<Props>(), {
as: 'button',
})
</script>
<template>
<Primitive
:as="as"
:as-child="asChild"
:class="cn(buttonVariants({ variant, size }), props.class)"
>
<slot />
</Primitive>
</template>
```
You'll need to extend `PrimitiveProps` in your props to support `Primitive` component. In most cases you would also need a default value for [`as`](https://www.radix-vue.com/utilities/primitive.html#changing-as-value) property.
## Debugging
Here are some tools and techniques that can help you debug more effectively while contributing to `shadcn/vue` or developing your own projects.
### Install Vue Dev Tools
To easily inspect component props, attributes, events, and more, you can leverage the [`Vue DevTools`](https://devtools.vuejs.org/) extension for browsers. This extension provides a user-friendly interface for debugging Vue components and can improve your development experience.
### Enable Custom Formmaters
Vue wraps values stored in a `ref` in a way that, when logged, results in a nested object and requires manual inspection to access the value stored in the ref.
You can enable Custom Formatters in your browser to automate this process.
- [Firefox](https://firefox-source-docs.mozilla.org/devtools-user/custom_formatters/index.html)
- Chrome, Edge, Brave and other Chromium based [browsers](https://www.google.com/search?q=how+to+enable+custom++formatter+chrome)

View File

@ -56,8 +56,6 @@ description: How to install dependencies and structure your app.
</LinkedCard> </LinkedCard>
</div> </div>
## TypeScript ## TypeScript
This project and the components are written in TypeScript. We recommend using TypeScript for your project as well. This project and the components are written in TypeScript. We recommend using TypeScript for your project as well.

View File

@ -41,7 +41,6 @@ npm install -D @nuxtjs/tailwindcss
</TabMarkdown> </TabMarkdown>
<TabMarkdown title="manual"> <TabMarkdown title="manual">
Add the following code to `modules/shadcn.ts`. Add the following code to `modules/shadcn.ts`.
@ -148,7 +147,6 @@ declare module '@nuxt/schema' {
</TabMarkdown> </TabMarkdown>
</TabsMarkdown> </TabsMarkdown>
### Configure `nuxt.config.ts` ### Configure `nuxt.config.ts`
```ts ```ts

View File

@ -21,7 +21,6 @@ _Use this as a reference to build your own component libraries._
## FAQ ## FAQ
<Accordion type="multiple"> <Accordion type="multiple">
<AccordionItem value="faq-1"> <AccordionItem value="faq-1">

View File

@ -3,7 +3,6 @@ title: Theming
description: Use CSS Variables to customize the look and feel of your application. description: Use CSS Variables to customize the look and feel of your application.
--- ---
You can choose between using CSS variables or Tailwind CSS utility classes for theming. You can choose between using CSS variables or Tailwind CSS utility classes for theming.
## Utility classes ## Utility classes
@ -38,7 +37,6 @@ To use utility classes for theming set `tailwind.cssVariables` to `false` in you
To use CSS variables for theming set `tailwind.cssVariables` to `true` in your `components.json` file. To use CSS variables for theming set `tailwind.cssVariables` to `true` in your `components.json` file.
```json {7} title="components.json" ```json {7} title="components.json"
{ {
"style": "default", "style": "default",
@ -201,9 +199,6 @@ I recommend using [HSL colors](https://www.smashingmagazine.com/2021/07/hsl-colo
See the [Tailwind CSS documentation](https://tailwindcss.com/docs/customizing-colors#using-css-variables) for more information on using `rgb`, `rgba` or `hsl` colors. See the [Tailwind CSS documentation](https://tailwindcss.com/docs/customizing-colors#using-css-variables) for more information on using `rgb`, `rgba` or `hsl` colors.
## Hex -> Color Channel ## Hex -> Color Channel
You can use this tool to convert your HEX color to HSL without the color space function. Simply add your color in hex format, copy one of the generated values, then add them to the CSS variable. You can use this tool to convert your HEX color to HSL without the color space function. Simply add your color in hex format, copy one of the generated values, then add them to the CSS variable.

View File

@ -46,8 +46,8 @@ const debouncedSearch = refDebounced(searchValue, 250)
const filteredMailList = computed(() => { const filteredMailList = computed(() => {
let output: Mail[] = [] let output: Mail[] = []
const serachValue = debouncedSearch.value?.trim() const searchValue = debouncedSearch.value?.trim()
if (!serachValue) { if (!searchValue) {
output = props.mails output = props.mails
} }

View File

@ -91,7 +91,7 @@ const table = useVueTable({
<TableRow v-else> <TableRow v-else>
<TableCell <TableCell
col-span="{columns.length}" :colspan="columns.length"
class="h-24 text-center" class="h-24 text-center"
> >
No results. No results.

View File

@ -0,0 +1,53 @@
<script lang="ts" setup>
import {
Breadcrumb,
BreadcrumbEllipsis,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/lib/registry/default/ui/breadcrumb'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/lib/registry/default/ui/dropdown-menu'
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">
Home
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<DropdownMenu>
<DropdownMenuTrigger class="flex items-center gap-1">
<BreadcrumbEllipsis class="h-4 w-4" />
<span class="sr-only">Toggle menu</span>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem>Documentation</DropdownMenuItem>
<DropdownMenuItem>Themes</DropdownMenuItem>
<DropdownMenuItem>GitHub</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink href="/docs/components/accordion.html">
Components
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</template>

View File

@ -0,0 +1,51 @@
<script lang="ts" setup>
import { ChevronDown, Slash } from 'lucide-vue-next'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/lib/registry/default/ui/breadcrumb'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/lib/registry/default/ui/dropdown-menu'
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">
Home
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator>
<Slash />
</BreadcrumbSeparator>
<BreadcrumbItem>
<DropdownMenu>
<DropdownMenuTrigger class="flex items-center gap-1">
Components
<ChevronDown class="h-4 w-4" />
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem>Documentation</DropdownMenuItem>
<DropdownMenuItem>Themes</DropdownMenuItem>
<DropdownMenuItem>GitHub</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</BreadcrumbItem>
<BreadcrumbSeparator>
<Slash />
</BreadcrumbSeparator>
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</template>

View File

@ -0,0 +1,41 @@
<script lang="ts" setup>
import {
Breadcrumb,
BreadcrumbEllipsis,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/lib/registry/default/ui/breadcrumb'
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink as-child>
<a href="/">
Home
</a>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbEllipsis />
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink as-child>
<a href="/docs/components/accordion.html">
Components
</a>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</template>

View File

@ -0,0 +1,36 @@
<script lang="ts" setup>
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/lib/registry/default/ui/breadcrumb'
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink>
<a href="/">
Home
</a>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink>
<a href="/docs/components/accordion.html">
Components
</a>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</template>

View File

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

View File

@ -0,0 +1,37 @@
<script lang="ts" setup>
import { Slash } from 'lucide-vue-next'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/lib/registry/default/ui/breadcrumb'
</script>
<template>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">
Home
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator>
<Slash />
</BreadcrumbSeparator>
<BreadcrumbItem>
<BreadcrumbLink href="/docs/components/accordion.html">
Components
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator>
<Slash />
</BreadcrumbSeparator>
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</template>

View File

@ -221,7 +221,7 @@ const table = useVueTable({
<TableRow v-else> <TableRow v-else>
<TableCell <TableCell
:col-span="columns.length" :colspan="columns.length"
class="h-24 text-center" class="h-24 text-center"
> >
No results. No results.

View File

@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { import type {
ColumnDef,
ColumnFiltersState, ColumnFiltersState,
SortingState, SortingState,
VisibilityState, VisibilityState,
@ -233,7 +232,7 @@ const table = useVueTable({
<TableRow v-else> <TableRow v-else>
<TableCell <TableCell
col-span="{columns.length}" :colspan="columns.length"
class="h-24 text-center" class="h-24 text-center"
> >
No results. No results.

View File

@ -214,7 +214,7 @@ const table = useVueTable({
<TableRow v-else> <TableRow v-else>
<TableCell <TableCell
col-span="{columns.length}" :colspan="columns.length"
class="h-24 text-center" class="h-24 text-center"
> >
No results. No results.

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { MagnifyingGlassIcon } from '@radix-icons/vue' import { Search } from 'lucide-vue-next'
import { Input } from '@/lib/registry/default/ui/input' import { Input } from '@/lib/registry/default/ui/input'
</script> </script>
@ -7,7 +7,7 @@ import { Input } from '@/lib/registry/default/ui/input'
<div class="relative w-full max-w-sm items-center"> <div class="relative w-full max-w-sm items-center">
<Input id="search" type="text" placeholder="Search..." class="pl-10" /> <Input id="search" type="text" placeholder="Search..." class="pl-10" />
<span class="absolute start-0 inset-y-0 flex items-center justify-center px-2"> <span class="absolute start-0 inset-y-0 flex items-center justify-center px-2">
<MagnifyingGlassIcon class="size-6 text-muted-foreground" /> <Search class="size-6 text-muted-foreground" />
</span> </span>
</div> </div>
</template> </template>

View File

@ -10,7 +10,7 @@ import {
navigationMenuTriggerStyle, navigationMenuTriggerStyle,
} from '@/lib/registry/default/ui/navigation-menu' } from '@/lib/registry/default/ui/navigation-menu'
const components: { title: string; href: string; description: string }[] = [ const components: { title: string, href: string, description: string }[] = [
{ {
title: 'Alert Dialog', title: 'Alert Dialog',
href: '/docs/primitives/alert-dialog', href: '/docs/primitives/alert-dialog',

View File

@ -4,7 +4,7 @@ import {
NavigationMenuLink, NavigationMenuLink,
} from '@/lib/registry/default/ui/navigation-menu' } from '@/lib/registry/default/ui/navigation-menu'
defineProps<{ title?: string; href?: string }>() defineProps<{ title?: string, href?: string }>()
</script> </script>
<template> <template>

View File

@ -42,12 +42,13 @@ const handleComplete = (e: string[]) => console.log(e.join(''))
<template> <template>
<form class="w-2/3 space-y-6 mx-auto" @submit="onSubmit"> <form class="w-2/3 space-y-6 mx-auto" @submit="onSubmit">
<FormField v-slot="{ componentField }" name="pin"> <FormField v-slot="{ componentField, value }" name="pin">
<FormItem> <FormItem>
<FormLabel>OTP</FormLabel> <FormLabel>OTP</FormLabel>
<FormControl> <FormControl>
<PinInput <PinInput
id="pin-input" id="pin-input"
v-model="value!"
placeholder="○" placeholder="○"
class="flex gap-2 items-center mt-1" class="flex gap-2 items-center mt-1"
otp otp

View File

@ -0,0 +1,13 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<nav aria-label="breadcrumb" :class="props.class">
<slot />
</nav>
</template>

View File

@ -0,0 +1,22 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { MoreHorizontal } from 'lucide-vue-next'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<span
role="presentation"
aria-hidden="true"
:class="cn('flex h-9 w-9 items-center justify-center', props.class)"
>
<slot>
<MoreHorizontal class="h-4 w-4" />
</slot>
<span class="sr-only">More</span>
</span>
</template>

View File

@ -0,0 +1,16 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<li
:class="cn('inline-flex items-center gap-1.5', props.class)"
>
<slot />
</li>
</template>

View File

@ -0,0 +1,19 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { Primitive, type PrimitiveProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<PrimitiveProps & { class?: HTMLAttributes['class'] }>(), {
as: 'a',
})
</script>
<template>
<Primitive
:as="as"
:as-child="asChild"
:class="cn('transition-colors hover:text-foreground', props.class)"
>
<slot />
</Primitive>
</template>

View File

@ -0,0 +1,16 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<ol
:class="cn('flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5', props.class)"
>
<slot />
</ol>
</template>

View File

@ -0,0 +1,19 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<span
role="link"
aria-disabled="true"
aria-current="page"
:class="cn('font-normal text-foreground', props.class)"
>
<slot />
</span>
</template>

View File

@ -0,0 +1,21 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { ChevronRight } from 'lucide-vue-next'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<li
role="presentation"
aria-hidden="true"
:class="cn('[&>svg]:size-3.5', props.class)"
>
<slot>
<ChevronRight />
</slot>
</li>
</template>

View File

@ -0,0 +1,7 @@
export { default as Breadcrumb } from './Breadcrumb.vue'
export { default as BreadcrumbEllipsis } from './BreadcrumbEllipsis.vue'
export { default as BreadcrumbItem } from './BreadcrumbItem.vue'
export { default as BreadcrumbLink } from './BreadcrumbLink.vue'
export { default as BreadcrumbList } from './BreadcrumbList.vue'
export { default as BreadcrumbPage } from './BreadcrumbPage.vue'
export { default as BreadcrumbSeparator } from './BreadcrumbSeparator.vue'

View File

@ -7,7 +7,6 @@ import { cn } from '@/lib/utils'
interface Props extends PrimitiveProps { interface Props extends PrimitiveProps {
variant?: ButtonVariants['variant'] variant?: ButtonVariants['variant']
size?: ButtonVariants['size'] size?: ButtonVariants['size']
as?: string
class?: HTMLAttributes['class'] class?: HTMLAttributes['class']
} }

View File

@ -8,7 +8,9 @@ import type { CarouselEmits, CarouselProps } from './interface'
const [useProvideCarousel, useInjectCarousel] = createInjectionState( const [useProvideCarousel, useInjectCarousel] = createInjectionState(
({ ({
opts, orientation, plugins, opts,
orientation,
plugins,
}: CarouselProps, emits: CarouselEmits) => { }: CarouselProps, emits: CarouselEmits) => {
const [emblaNode, emblaApi] = emblaCarouselVue({ const [emblaNode, emblaApi] = emblaCarouselVue({
...opts, ...opts,

View File

@ -8,7 +8,7 @@ import {
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ContextMenuItemProps & { class?: HTMLAttributes['class']; inset?: boolean }>() const props = defineProps<ContextMenuItemProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
const emits = defineEmits<ContextMenuItemEmits>() const emits = defineEmits<ContextMenuItemEmits>()
const delegatedProps = computed(() => { const delegatedProps = computed(() => {

View File

@ -3,7 +3,7 @@ import { type HTMLAttributes, computed } from 'vue'
import { ContextMenuLabel, type ContextMenuLabelProps } from 'radix-vue' import { ContextMenuLabel, type ContextMenuLabelProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ContextMenuLabelProps & { class?: HTMLAttributes['class']; inset?: boolean }>() const props = defineProps<ContextMenuLabelProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
const delegatedProps = computed(() => { const delegatedProps = computed(() => {
const { class: _, ...delegated } = props const { class: _, ...delegated } = props

View File

@ -8,7 +8,7 @@ import {
import { ChevronRight } from 'lucide-vue-next' import { ChevronRight } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ContextMenuSubTriggerProps & { class?: HTMLAttributes['class']; inset?: boolean }>() const props = defineProps<ContextMenuSubTriggerProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
const delegatedProps = computed(() => { const delegatedProps = computed(() => {
const { class: _, ...delegated } = props const { class: _, ...delegated } = props

View File

@ -3,7 +3,7 @@ import { type HTMLAttributes, computed } from 'vue'
import { DropdownMenuItem, type DropdownMenuItemProps, useForwardProps } from 'radix-vue' import { DropdownMenuItem, type DropdownMenuItemProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuItemProps & { class?: HTMLAttributes['class']; inset?: boolean }>() const props = defineProps<DropdownMenuItemProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
const delegatedProps = computed(() => { const delegatedProps = computed(() => {
const { class: _, ...delegated } = props const { class: _, ...delegated } = props

View File

@ -3,7 +3,7 @@ import { type HTMLAttributes, computed } from 'vue'
import { DropdownMenuLabel, type DropdownMenuLabelProps, useForwardProps } from 'radix-vue' import { DropdownMenuLabel, type DropdownMenuLabelProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuLabelProps & { class?: HTMLAttributes['class']; inset?: boolean }>() const props = defineProps<DropdownMenuLabelProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
const delegatedProps = computed(() => { const delegatedProps = computed(() => {
const { class: _, ...delegated } = props const { class: _, ...delegated } = props

View File

@ -8,7 +8,7 @@ import {
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<MenubarItemProps & { class?: HTMLAttributes['class']; inset?: boolean }>() const props = defineProps<MenubarItemProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
const emits = defineEmits<MenubarItemEmits>() const emits = defineEmits<MenubarItemEmits>()

View File

@ -3,7 +3,7 @@ import type { HTMLAttributes } from 'vue'
import { MenubarLabel, type MenubarLabelProps } from 'radix-vue' import { MenubarLabel, type MenubarLabelProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<MenubarLabelProps & { class?: HTMLAttributes['class']; inset?: boolean }>() const props = defineProps<MenubarLabelProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
</script> </script>
<template> <template>

View File

@ -4,7 +4,7 @@ import { MenubarSubTrigger, type MenubarSubTriggerProps, useForwardProps } from
import { ChevronRight } from 'lucide-vue-next' import { ChevronRight } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<MenubarSubTriggerProps & { class?: HTMLAttributes['class']; inset?: boolean }>() const props = defineProps<MenubarSubTriggerProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
const delegatedProps = computed(() => { const delegatedProps = computed(() => {
const { class: _, ...delegated } = props const { class: _, ...delegated } = props

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