Merge branch 'dev' into refactor/update-tailwind-classes

This commit is contained in:
Sadegh Barati 2024-02-02 22:47:30 +03:30
commit ec2b606dec
428 changed files with 4403 additions and 2358 deletions

View File

@ -6,62 +6,27 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
**Before You Start...** Thanks for taking the time to fill out this bug report!
This form is only for submitting bug reports. If you have a usage question This form is only for submitting bug reports. If you have a usage question
or are unsure if this is really a bug, make sure to: or are unsure if this is really a bug, make sure to:
- Read the [docs](https://radix-vue.com/) - Read the [docs](https://radix-vue.com/)
- Ask on [Discord Chat](https://chat.radix-vue.com/) - Ask on [Discord Chat](https://chat.radix-vue.com/)
- Ask on [GitHub Discussions](https://github.com/shadcn-vue/shadcn-vue/discussions) - Ask on [GitHub Discussions](https://github.com/shadcn-vue/shadcn-vue/discussions)
Also try to search for your issue - it may have already been answered or even fixed.
However, if you find that an old, closed issue still persists in the latest version,
you should open a new issue using the form below instead of commenting on the old issue.
- type: textarea
id: bug-env
attributes:
label: Environment
description: Please provide the following information about your environment.
value: |
Developement/Production OS: Windows 10 19043.1110
Node version: 16.0.0
Package manager: pnpm@8.6.0
Radix Vue version: 1.0.0
Shadcn Vue version: 1.0.0
Vue version: 3.0.0
Nuxt version: 3.0.0
Nuxt mode: universal
Nuxt target: server
CSS framework: tailwindcss@3.3.3
Client OS: Windows 10 19043.1110
Browser: Chrome 90.0.4430.212
render: bash
validations:
required: true
- type: input - type: input
id: reproduction-link id: reproduction
attributes: attributes:
label: Link to minimal reproduction label: Reproduction
description: | description: |
Please provide a link to a minimal reproduction of the bug. A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is **required**, otherwise the issue might be closed without further notice. [**Why?**](https://antfu.me/posts/why-reproductions-are-required)
A minimal reproduction is a CodeSandbox, CodePen, or a StackBlitz that contains the bare minimum amount of code needed to show the bug.
A minimal reproduction is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem
This is **required** for us to be able to triage your issue in a timely manner. To get started, you can use the StackBlitz and CodeSandbox playgrounds in shadcn-vue demos:
https://www.shadcn-vue.com/docs/components/accordion.html
Please do not just fill in a random link. The issue will be closed if no valid reproduction is provided. or use template presets
placeholder: Reproduction Link https://vite.new
validations: https://nuxt.new
required: true placeholder: Reproduction
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to reproduce
description: |
How do you trigger this bug? Please walk us through it step by step.
Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format lists and code.
placeholder: Steps to reproduce
validations: validations:
required: true required: true
- type: textarea - type: textarea
@ -73,14 +38,18 @@ body:
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: expected-behavior id: system-info
attributes: attributes:
label: Expected behavior label: System Info
description: A clear and concise description of what you expected to happen. description: Output of `npx envinfo --system --npmPackages vue,@vueuse/core,radix-vue,nuxt,shadcn-vue,shadcn-nuxt,unplugin-auto-import --binaries --browsers`
- type: textarea render: bash
id: screenshots placeholder: System, Binaries, Browsers
validations:
required: true
- type: checkboxes
id: contributes
attributes: attributes:
label: Conext & Screenshots (if applicable) label: Contributes
description: | options:
If applicable, provide any additional context or screenshots of the bug. - label: I am willing to submit a PR to fix this issue
You can drag and drop images here to add them to the issue. - label: I am willing to submit a PR with failing tests

View File

@ -3,15 +3,15 @@ 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 'shikiji' import { createCssVariablesTheme } from 'shiki'
// 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'
const cssVariables = createCssVariablesTheme({ const cssVariables = createCssVariablesTheme({
name: 'css-variables',
variablePrefix: '--shiki-', variablePrefix: '--shiki-',
variableDefaults: {}, variableDefaults: {},
fontStyle: true,
}) })
// https://vitepress.dev/reference/site-config // https://vitepress.dev/reference/site-config
@ -59,6 +59,10 @@ export default defineConfig({
srcDir: path.resolve(__dirname, '../src'), srcDir: path.resolve(__dirname, '../src'),
markdown: { markdown: {
theme: cssVariables, theme: cssVariables,
codeTransformers: [
// transformerMetaWordHighlight(),
// transformerNotationWordHighlight(),
],
config(md) { config(md) {
md.use(ComponentPreviewPlugin) md.use(ComponentPreviewPlugin)
}, },

View File

@ -299,6 +299,12 @@ export const docsConfig: DocsConfig = {
href: '/docs/components/slider', href: '/docs/components/slider',
items: [], items: [],
}, },
{
title: 'Sonner',
href: '/docs/components/sonner',
label: 'New',
items: [],
},
{ {
title: 'Switch', title: 'Switch',
href: '/docs/components/switch', href: '/docs/components/switch',
@ -322,7 +328,6 @@ export const docsConfig: DocsConfig = {
{ {
title: 'Toast', title: 'Toast',
href: '/docs/components/toast', href: '/docs/components/toast',
label: 'New',
items: [], items: [],
}, },
{ {

View File

@ -6,6 +6,7 @@ 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 { 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 ChevronRightIcon from '~icons/lucide/chevron-right' import ChevronRightIcon from '~icons/lucide/chevron-right'
const $route = useRoute() const $route = useRoute()
@ -20,7 +21,7 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
<aside <aside
class="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block overflow-y-auto" class="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block overflow-y-auto"
> >
<ScrollArea orientation="vertical" class="h-full py-6 pl-8 pr-6 lg:py-8" :type="'auto'"> <ScrollArea orientation="vertical" class="relative overflow-hidden h-full py-6 pr-6 lg:py-8" :type="'auto'">
<div class="w-full"> <div class="w-full">
<div v-for="docsGroup in docsConfig.sidebarNav" :key="docsGroup.title" class="pb-4"> <div v-for="docsGroup in docsConfig.sidebarNav" :key="docsGroup.title" class="pb-4">
<h4 <h4
@ -45,9 +46,9 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
> >
{{ doc.title }} {{ doc.title }}
<Badge v-if="doc.label" class="ml-2" :variant="'secondary'"> <span v-if="doc.label" class="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
{{ doc.label }} {{ doc.label }}
</Badge> </span>
</a> </a>
</div> </div>
</div> </div>
@ -81,6 +82,10 @@ const sourceLink = 'https://github.com/radix-vue/shadcn-vue/tree/dev/'
</div> </div>
<div class="flex items-center space-x-2 pt-4"> <div class="flex items-center space-x-2 pt-4">
<a v-if="frontmatter.docs" :href="frontmatter.docs" target="_blank" class="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 select-none border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80">
<RadixIconsExternalLink class="mr-1" />
Docs
</a>
<a v-if="frontmatter.source" :href="sourceLink + frontmatter.source" target="_blank" class="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 select-none border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80"> <a v-if="frontmatter.source" :href="sourceLink + frontmatter.source" target="_blank" class="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 select-none border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80">
<RadixIconsCode class="mr-1" /> <RadixIconsCode class="mr-1" />
Component Source Component Source

View File

@ -17,6 +17,7 @@ import RadixIconsSun from '~icons/radix-icons/sun'
import { useConfigStore } from '@/stores/config' import { useConfigStore } from '@/stores/config'
import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog' import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
import { Toaster as DefaultToaster } from '@/lib/registry/default/ui/toast' import { Toaster as DefaultToaster } from '@/lib/registry/default/ui/toast'
import { Toaster as NewYorkSonner } from '@/lib/registry/new-york/ui/sonner'
import { Toaster as NewYorkToaster } from '@/lib/registry/new-york/ui/toast' import { Toaster as NewYorkToaster } from '@/lib/registry/new-york/ui/toast'
import File from '~icons/radix-icons/file' import File from '~icons/radix-icons/file'
@ -86,7 +87,7 @@ watch(() => $route.path, (n) => {
<div class="flex min-h-screen flex-col bg-background"> <div class="flex min-h-screen flex-col bg-background">
<header class="sticky z-40 top-0 bg-background/80 backdrop-blur-lg border-b border-border"> <header class="sticky z-40 top-0 bg-background/80 backdrop-blur-lg border-b border-border">
<div <div
class="container flex justify-between h-14 items-center" class="container flex justify-between h-14 max-w-screen-2xl items-center"
> >
<MobileNav /> <MobileNav />
@ -287,6 +288,9 @@ watch(() => $route.path, (n) => {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
<DefaultToaster /> <DefaultToaster />
<ClientOnly>
<NewYorkSonner :theme="isDark ? 'dark' : 'light'" />
</ClientOnly>
<NewYorkToaster /> <NewYorkToaster />
</div> </div>
</template> </template>

View File

@ -5,49 +5,49 @@
@layer base { @layer base {
:root { :root {
--background: 0 0% 100%; --background: 0 0% 100%;
--foreground: 240 10% 3.9%; --foreground: 240 10% 3.9%;
--card: 0 0% 100%; --card: 0 0% 100%;
--card-foreground: 240 10% 3.9%; --card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%; --popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%; --popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%; --primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%; --primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%; --secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%; --secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%; --muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%; --muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%; --accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%; --accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%; --destructive: 0 72.22% 50.59%;
--destructive-foreground: 0 0% 98%; --destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%; --border: 240 5.9% 90%;
--input: 240 5.9% 90%; --input: 240 5.9% 90%;
--ring: 240 5% 64.9%; --ring: 240 5% 64.9%;
--radius: 0.5rem; --radius: 0.5rem;
} }
.dark { .dark {
--background: 240 10% 3.9%; --background: 240 10% 3.9%;
--foreground: 0 0% 98%; --foreground: 0 0% 98%;
--card: 240 10% 3.9%; --card: 240 10% 3.9%;
--card-foreground: 0 0% 98%; --card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%; --popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%; --popover-foreground: 0 0% 98%;
--primary: 0 0% 98%; --primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%; --primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%; --secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%; --secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%; --muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%; --muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%; --accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%; --accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%; --destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%; --destructive-foreground: 0 85.7% 97.3%;
--border: 240 3.7% 15.9%; --border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%; --input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%; --ring: 240 4.9% 83.9%;
} }
* { * {

View File

@ -1,5 +1,5 @@
:root { :root {
--shiki-foreground: #EEEEEE; --shiki-foreground: #FFF8;
--shiki-color-background: #ffffff; --shiki-color-background: #ffffff;
--shiki-token-constant: #ffffff; --shiki-token-constant: #ffffff;
--shiki-token-string: #ffffff88; --shiki-token-string: #ffffff88;
@ -7,7 +7,14 @@
--shiki-token-keyword: #ffffff88; --shiki-token-keyword: #ffffff88;
--shiki-token-parameter: #AA0000; --shiki-token-parameter: #AA0000;
--shiki-token-function: #ffffff; --shiki-token-function: #ffffff;
--shiki-token-string-expression: #ffffff88; --shiki-token-string-expression: #ebebeb;
--shiki-token-punctuation: #ffffff; --shiki-token-punctuation: #ffffff;
--shiki-token-link: #EE0000; --shiki-token-link: #EE0000;
}
.shiki .highlighted-word {
border-radius: calc(var(--radius) - 2px);
border-color: rgba(63,63,70,.7);
background-color: rgba(63,63,70,.5);
padding: 0.25rem;
} }

View File

@ -3,6 +3,7 @@
--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E"); --vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E");
--vp-code-bg: hsl(var(--muted)); --vp-code-bg: hsl(var(--muted));
--vp-c-divider: hsl(var(--muted)); --vp-c-divider: hsl(var(--muted));
--vp-code-block-color: #fff
} }
@ -414,7 +415,7 @@
.vp-doc div[class*='language-'].line-numbers-mode { .vp-doc div[class*='language-'].line-numbers-mode {
/*rtl:ignore*/ /*rtl:ignore*/
padding-left: 32px; padding-left: 0px;
} }
.vp-doc .line-numbers-wrapper { .vp-doc .line-numbers-wrapper {
@ -566,4 +567,12 @@
.vp-external-link-icon::after { .vp-external-link-icon::after {
content: ''; content: '';
}
.vp-doc [class*=language-] code {
color: var(--vp-code-block-color);
}
.line-numbers-mode pre code .line {
padding-left: 3rem !important;
} }

View File

@ -506,6 +506,13 @@ export const Index = {
component: () => import('../src/lib/registry/default/example/RadioGroupForm.vue').then(m => m.default), component: () => import('../src/lib/registry/default/example/RadioGroupForm.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/RadioGroupForm.vue'], files: ['../src/lib/registry/default/example/RadioGroupForm.vue'],
}, },
RangePickerWithSlot: {
name: 'RangePickerWithSlot',
type: 'components:example',
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
component: () => import('../src/lib/registry/default/example/RangePickerWithSlot.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/RangePickerWithSlot.vue'],
},
ScrollAreaDemo: { ScrollAreaDemo: {
name: 'ScrollAreaDemo', name: 'ScrollAreaDemo',
type: 'components:example', type: 'components:example',
@ -569,6 +576,13 @@ export const Index = {
component: () => import('../src/lib/registry/default/example/SliderDemo.vue').then(m => m.default), component: () => import('../src/lib/registry/default/example/SliderDemo.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/SliderDemo.vue'], files: ['../src/lib/registry/default/example/SliderDemo.vue'],
}, },
SonnerDemo: {
name: 'SonnerDemo',
type: 'components:example',
registryDependencies: ['button'],
component: () => import('../src/lib/registry/default/example/SonnerDemo.vue').then(m => m.default),
files: ['../src/lib/registry/default/example/SonnerDemo.vue'],
},
SwitchDemo: { SwitchDemo: {
name: 'SwitchDemo', name: 'SwitchDemo',
type: 'components:example', type: 'components:example',
@ -1390,6 +1404,13 @@ export const Index = {
component: () => import('../src/lib/registry/new-york/example/RadioGroupForm.vue').then(m => m.default), component: () => import('../src/lib/registry/new-york/example/RadioGroupForm.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/RadioGroupForm.vue'], files: ['../src/lib/registry/new-york/example/RadioGroupForm.vue'],
}, },
RangePickerWithSlot: {
name: 'RangePickerWithSlot',
type: 'components:example',
registryDependencies: ['utils', 'button', 'calendar', 'popover'],
component: () => import('../src/lib/registry/new-york/example/RangePickerWithSlot.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/RangePickerWithSlot.vue'],
},
ScrollAreaDemo: { ScrollAreaDemo: {
name: 'ScrollAreaDemo', name: 'ScrollAreaDemo',
type: 'components:example', type: 'components:example',
@ -1453,6 +1474,13 @@ export const Index = {
component: () => import('../src/lib/registry/new-york/example/SliderDemo.vue').then(m => m.default), component: () => import('../src/lib/registry/new-york/example/SliderDemo.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/SliderDemo.vue'], files: ['../src/lib/registry/new-york/example/SliderDemo.vue'],
}, },
SonnerDemo: {
name: 'SonnerDemo',
type: 'components:example',
registryDependencies: ['button'],
component: () => import('../src/lib/registry/new-york/example/SonnerDemo.vue').then(m => m.default),
files: ['../src/lib/registry/new-york/example/SonnerDemo.vue'],
},
SwitchDemo: { SwitchDemo: {
name: 'SwitchDemo', name: 'SwitchDemo',
type: 'components:example', type: 'components:example',

View File

@ -19,24 +19,25 @@
"@morev/vue-transitions": "^2.3.6", "@morev/vue-transitions": "^2.3.6",
"@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.11.6", "@tanstack/vue-table": "^8.11.8",
"@unovis/ts": "^1.3.1", "@unovis/ts": "^1.3.3",
"@unovis/vue": "^1.3.1", "@unovis/vue": "^1.3.3",
"@vee-validate/zod": "^4.12.4", "@vee-validate/zod": "^4.12.5",
"@vueuse/core": "^10.7.2", "@vueuse/core": "^10.7.2",
"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": "^2.30.0", "date-fns": "^2.30.0",
"embla-carousel": "8.0.0-rc19", "embla-carousel": "^8.0.0-rc22",
"embla-carousel-autoplay": "8.0.0-rc19", "embla-carousel-autoplay": "^8.0.0-rc22",
"embla-carousel-vue": "8.0.0-rc19", "embla-carousel-vue": "^8.0.0-rc22",
"lucide-vue-next": "^0.276.0", "lucide-vue-next": "^0.276.0",
"radix-vue": "^1.3.2", "radix-vue": "^1.4.0",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2", "v-calendar": "^3.1.2",
"vee-validate": "4.12.4", "vee-validate": "4.12.5",
"vue": "^3.4.14", "vue": "^3.4.15",
"vue-sonner": "^1.0.3",
"vue-wrap-balancer": "^1.1.3", "vue-wrap-balancer": "^1.1.3",
"zod": "^3.22.4" "zod": "^3.22.4"
}, },
@ -45,26 +46,27 @@
"@iconify-json/tabler": "^1.1.89", "@iconify-json/tabler": "^1.1.89",
"@iconify/json": "^2.2.108", "@iconify/json": "^2.2.108",
"@iconify/vue": "^4.1.1", "@iconify/vue": "^4.1.1",
"@shikijs/transformers": "^1.0.0-beta.3",
"@types/lodash.template": "^4.5.2", "@types/lodash.template": "^4.5.2",
"@types/node": "^20.8.10", "@types/node": "^20.8.10",
"@vitejs/plugin-vue": "^5.0.3", "@vitejs/plugin-vue": "^5.0.3",
"@vitejs/plugin-vue-jsx": "^3.1.0", "@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/compiler-core": "^3.4.14", "@vue/compiler-core": "^3.4.15",
"@vue/compiler-dom": "^3.4.14", "@vue/compiler-dom": "^3.4.15",
"@vue/tsconfig": "^0.5.1", "@vue/tsconfig": "^0.5.1",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.17",
"lodash.template": "^4.5.0", "lodash.template": "^4.5.0",
"oxc-parser": "^0.2.0", "oxc-parser": "^0.2.0",
"pathe": "^1.1.2", "pathe": "^1.1.2",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"shikiji": "^0.10.0-beta.2", "shiki": "^1.0.0-beta.3",
"tailwind-merge": "^2.2.0", "tailwind-merge": "^2.2.1",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"tsx": "^4.7.0", "tsx": "^4.7.0",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"unplugin-icons": "^0.17.1", "unplugin-icons": "^0.18.3",
"vite": "^5.0.11", "vite": "^5.0.12",
"vitepress": "^1.0.0-rc.37", "vitepress": "^1.0.0-rc.41",
"vue-tsc": "^1.8.27" "vue-tsc": "^1.8.27"
} }
} }

View File

@ -56,5 +56,40 @@ import { Calendar } from '@/components/ui/calendar'
</template> </template>
``` ```
See the [VCalendar](https://vcalendar.io/getting-started/installation.html) documentation for more information. The API is essentially the same, i.e. props and slots. See the [VCalendar](https://vcalendar.io/getting-started/installation.html) documentation for more information.
### Slots
The slots available are [those currently supported](https://github.com/nathanreyes/v-calendar/blob/v3.1.2/src/components/Calendar/CalendarSlot.vue#L16-L28) by VCalendar, namely :
- `day-content`
- `day-popover`
- `dp-footer`
- `footer`
- `header-title-wrapper`
- `header-title`
- `header-prev-button`
- `header-next-button`
- `nav`
- `nav-prev-button`
- `nav-next-button`
- `page`
- `time-header`
Example using the `day-content` slot:
```vue
<script setup lang="ts">
import { Calendar } from '@/components/ui/calendar'
</script>
<template>
<Calendar>
<template #day-content="{ day, dayProps, dayEvents }">
<div v-bind="dayProps" v-on="dayEvents">
{{ day.label }}
</div>
</template>
</Calendar>
</template>
```

View File

@ -57,7 +57,7 @@ To set the size of the items, you can use the `basis` utility class on the `<Car
Example Example
```vue title="Example" showLineNumbers {4-6} ```vue:line-numbers title="Example" {4-6}
// 33% of the carousel width. // 33% of the carousel width.
<Carousel> <Carousel>
<CarouselContent> <CarouselContent>
@ -71,7 +71,7 @@ Example
Responsive Responsive
```vue title="Responsive" showLineNumbers {4-6} ```vue:line-numbers title="Responsive" {4-6}
// 50% on small screens and 33% on larger screens. // 50% on small screens and 33% on larger screens.
<Carousel> <Carousel>
<CarouselContent> <CarouselContent>
@ -101,7 +101,7 @@ You can always adjust this in your own project if you need to.
Example Example
```vue showLineNumbers /-ml-4/ /pl-4/ ```vue:line-numbers /-ml-4/ /pl-4/
<template> <template>
<Carousel> <Carousel>
<CarouselContent class="-ml-4"> <CarouselContent class="-ml-4">
@ -121,7 +121,7 @@ Example
Responsive Responsive
```vue showLineNumbers /-ml-2/ /pl-2/ /md:-ml-4/ /md:pl-4/ ```vue:line-numbers /-ml-2/ /pl-2/ /md:-ml-4/ /md:pl-4/
<template> <template>
<Carousel> <Carousel>
<CarouselContent class="-ml-2 md:-ml-4"> <CarouselContent class="-ml-2 md:-ml-4">
@ -155,7 +155,7 @@ Use the `orientation` prop to set the orientation of the carousel.
You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information. You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information.
```vue showLineNumbers {3-6} ```vue:line-numbers {3-6}
<template> <template>
<Carousel <Carousel
:opts="{ :opts="{
@ -184,7 +184,7 @@ Use the `@init-api` emit method on `<Carousel />` component to set the instance
You can access it through setting a template ref on the `<Carousel />` component. You can access it through setting a template ref on the `<Carousel />` component.
```vue showLineNumbers {2,5,9} ```vue:line-numbers {2,5,9}
<script setup> <script setup>
const carouselContainerRef = ref<InstanceType<typeof Carousel> | null>(null) const carouselContainerRef = ref<InstanceType<typeof Carousel> | null>(null)
@ -204,7 +204,7 @@ function accessApi() {
You can listen to events using the API. To get the API instance use the `@init-api` emit method on the `<Carousel />` component You can listen to events using the API. To get the API instance use the `@init-api` emit method on the `<Carousel />` component
```vue showLineNumbers {5,7-9,25} ```vue:line-numbers {5,7-9,25}
<script setup> <script setup>
import { nextTick, ref, watch } from 'vue' import { nextTick, ref, watch } from 'vue'
import { useCarousel } from '@/components/ui/carousel' import { useCarousel } from '@/components/ui/carousel'
@ -241,7 +241,7 @@ See the [Embla Carousel docs](https://www.embla-carousel.com/api/events/) for mo
You can get the reactive slot props like `carouselRef, canScrollNext..Prev, scrollNext..Prev` using the `v-slot` directive in the `<Carousel v-slot="slotProps" />` component to extend the functionality. You can get the reactive slot props like `carouselRef, canScrollNext..Prev, scrollNext..Prev` using the `v-slot` directive in the `<Carousel v-slot="slotProps" />` component to extend the functionality.
```vue showLineNumbers {2} ```vue:line-numbers {2}
<template> <template>
<Carousel v-slot="{ canScrollNext, canScrollPrev }"> <Carousel v-slot="{ canScrollNext, canScrollPrev }">
... ...
@ -260,7 +260,7 @@ npm i embla-carousel-autoplay
``` ```
```vue showLineNumbers {2,8-10} ```vue:line-numbers {2,8-10}
<script setup lang="ts"> <script setup lang="ts">
import Autoplay from 'embla-carousel-autoplay' import Autoplay from 'embla-carousel-autoplay'
</script> </script>

View File

@ -25,7 +25,7 @@ We'll start with the basic `<Table />` component and build a complex data table
## Table of Contents ## Table of Contents
This guide will show you how to use [TanStack Table](https://tanstack.com/table/v8) and the <Table /> component to build your own custom data table. We'll cover the following topics: This guide will show you how to use [TanStack Table](https://tanstack.com/table/v8) and the `<Table />` component to build your own custom data table. We'll cover the following topics:
- [Basic Table](#basic-table) - [Basic Table](#basic-table)
- [Row Actions](#row-actions) - [Row Actions](#row-actions)
@ -109,31 +109,23 @@ Let's start by building a basic table.
First, we'll define our columns in the `columns.ts` file. First, we'll define our columns in the `columns.ts` file.
```ts:line-numbers title="components/payments/columns.ts" {1,12-27} ```ts:line-numbers {1,12-27}
import type { ColumnDef } from '@tanstack/vue-table' import { h } from 'vue'
// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export interface Payment {
id: string
amount: number
status: 'pending' | 'processing' | 'success' | 'failed'
email: string
}
export const columns: ColumnDef<Payment>[] = [ export const columns: ColumnDef<Payment>[] = [
{
accessorKey: 'status',
header: 'Status',
},
{
accessorKey: 'email',
header: 'Email',
},
{ {
accessorKey: 'amount', accessorKey: 'amount',
header: 'Amount', header: () => h('div', { class: 'text-right' }, 'Amount'),
}, cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(amount)
return h('div', { class: 'text-right font-medium' }, formatted)
},
}
] ]
``` ```
@ -149,7 +141,7 @@ formatted, sorted and filtered.
Next, we'll create a `<DataTable />` component to render our table. Next, we'll create a `<DataTable />` component to render our table.
```ts:line-numbers ```vue:line-numbers
<script setup lang="ts" generic="TData, TValue"> <script setup lang="ts" generic="TData, TValue">
import type { ColumnDef } from '@tanstack/vue-table' import type { ColumnDef } from '@tanstack/vue-table'
import { import {
@ -225,7 +217,7 @@ const table = useVueTable({
Finally, we'll render our table in our index component. Finally, we'll render our table in our index component.
```ts:line-numbers showLineNumbers{28} ```vue:line-numbers {28}
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { columns } from "./components/columns" import { columns } from "./components/columns"
@ -272,7 +264,7 @@ Let's format the amount cell to display the dollar amount. We'll also align the
Update the `header` and `cell` definitions for amount as follows: Update the `header` and `cell` definitions for amount as follows:
```ts:line-numbers showLineNumbers title="components/payments/columns.ts" {5-17} ```ts:line-numbers title="components/payments/columns.ts" {5-17}
import { h } from 'vue' import { h } from 'vue'
export const columns: ColumnDef<Payment>[] = [ export const columns: ColumnDef<Payment>[] = [
@ -302,7 +294,7 @@ Let's add row actions to our table. We'll use a `<Dropdown />` component for thi
### Add the following into your `DataTableDropDown.vue` component: ### Add the following into your `DataTableDropDown.vue` component:
```ts:line-numbers ```vue:line-numbers
// DataTableDropDown.vue // DataTableDropDown.vue
<script setup lang="ts"> <script setup lang="ts">
import { MoreHorizontal } from 'lucide-vue-next' import { MoreHorizontal } from 'lucide-vue-next'
@ -380,7 +372,7 @@ Next, we'll add pagination to our table.
### Update `<DataTable>` ### Update `<DataTable>`
```ts:line-numbers showLineNumbers{4,12} ```ts:line-numbers {4,12}
import { import {
FlexRender, FlexRender,
getCoreRowModel, getCoreRowModel,
@ -402,8 +394,7 @@ This will automatically paginate your rows into pages of 10. See the [pagination
We can add pagination controls to our table using the `<Button />` component and the `table.previousPage()`, `table.nextPage()` API methods. We can add pagination controls to our table using the `<Button />` component and the `table.previousPage()`, `table.nextPage()` API methods.
```ts:line-numbers showLineNumbers{3,15,21-39} ```vue:line-numbers {3,15,21-39}
// components/payments/DataTable.vue
<script lang="ts" generic="TData, TValue"> <script lang="ts" generic="TData, TValue">
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
@ -458,7 +449,7 @@ Let's make the email column sortable.
### Add the following into your `utils` file: ### Add the following into your `utils` file:
```ts:line-numbers showLineNumbers{5,6,12-17} ```ts:line-numbers {5,6,12-17}
import { type ClassValue, clsx } from 'clsx' import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { camelize, getCurrentInstance, toHandlerKey } from 'vue' import { camelize, getCurrentInstance, toHandlerKey } from 'vue'
@ -482,7 +473,7 @@ The `valueUpdater` function updates a Vue `ref` object's value. It handles both
### Update `<DataTable>` ### Update `<DataTable>`
```ts:line-numbers showLineNumbers{4,7,16,34,41-44} ```vue:line-numbers {4,7,16,34,41-44}
<script setup lang="ts" generic="TData, TValue"> <script setup lang="ts" generic="TData, TValue">
import type { import type {
ColumnDef, ColumnDef,
@ -545,7 +536,7 @@ const table = useVueTable({
We can now update the `email` header cell to add sorting controls. We can now update the `email` header cell to add sorting controls.
```ts:line-numbers showLineNumbers{5,10-17} ```ts:line-numbers {5,10-17}
// components/payments/columns.ts // components/payments/columns.ts
import type { import type {
ColumnDef, ColumnDef,
@ -579,7 +570,7 @@ Let's add a search input to filter emails in our table.
### Update `<DataTable>` ### Update `<DataTable>`
```ts:line-numbers showLineNumbers{4,11,19,39,48-49,52,60-64} ```vue:line-numbers {4,11,19,39,48-49,52,60-64}
<script setup lang="ts" generic="TData, TValue"> <script setup lang="ts" generic="TData, TValue">
import type { import type {
ColumnDef, ColumnDef,
@ -664,7 +655,7 @@ Adding column visibility is fairly simple using `@tanstack/vue-table` visibility
### Update `<DataTable>` ### Update `<DataTable>`
```ts:line-numbers showLineNumbers{6,9-14,48,59,63,75-91} ```vue:line-numbers {6,9-14,48,59,63,75-91}
<script setup lang="ts" generic="TData, TValue"> <script setup lang="ts" generic="TData, TValue">
import type { import type {
ColumnDef, ColumnDef,
@ -803,7 +794,7 @@ Next, we're going to add row selection to our table.
### Update column definitions ### Update column definitions
```ts:line-numbers showLineNumbers{3,6-20} ```ts:line-numbers {3,6-20}
import type { ColumnDef } from '@tanstack/vue-table' import type { ColumnDef } from '@tanstack/vue-table'
import { Checkbox } from '@/components/ui/checkbox' import { Checkbox } from '@/components/ui/checkbox'
@ -829,7 +820,7 @@ export const columns: ColumnDef<Payment>[] = [
### Update `<DataTable>` ### Update `<DataTable>`
```ts:line-numbers showLineNumbers{10,22,27} ```vue:line-numbers {10,22,27}
<script setup lang="ts" generic="TData, TValue"> <script setup lang="ts" generic="TData, TValue">
const props = defineProps<{ const props = defineProps<{
columns: ColumnDef<TData, TValue>[] columns: ColumnDef<TData, TValue>[]
@ -895,7 +886,7 @@ Here are some components you can use to build your data tables. This is from the
Make any column header sortable and hideable. Make any column header sortable and hideable.
```ts:line-numbers ```vue:line-numbers
<script setup lang="ts"> <script setup lang="ts">
import type { Column } from '@tanstack/vue-table' import type { Column } from '@tanstack/vue-table'
import { type Task } from '../data/schema' import { type Task } from '../data/schema'
@ -988,7 +979,7 @@ export const columns = [
Add pagination controls to your table including page size and selection count. Add pagination controls to your table including page size and selection count.
```ts:line-numbers ```vue:line-numbers
<script setup lang="ts"> <script setup lang="ts">
import { type Table } from '@tanstack/vue-table' import { type Table } from '@tanstack/vue-table'
import { type Task } from '../data/schema' import { type Task } from '../data/schema'
@ -1093,8 +1084,7 @@ defineProps<DataTablePaginationProps>()
A component to toggle column visibility. A component to toggle column visibility.
```ts:line-numbers ```vue:line-numbers
<script setup lang="ts"> <script setup lang="ts">
import type { Table } from '@tanstack/vue-table' import type { Table } from '@tanstack/vue-table'
import { computed } from 'vue' import { computed } from 'vue'

View File

@ -72,6 +72,10 @@ const date = ref<Date>()
<ComponentPreview name="DatePickerWithPresets" /> <ComponentPreview name="DatePickerWithPresets" />
### With Slot
<ComponentPreview name="RangePickerWithSlot" />
### Form ### Form
<ComponentPreview name="DatePickerForm" /> <ComponentPreview name="DatePickerForm" />

View File

@ -159,7 +159,7 @@ Use `@vee-validate/zod` to integrate Zod schema validation with `vee-validate`
`toTypedSchema` also makes the form values and submitted values typed automatically and caters for both input and output types of that schema. `toTypedSchema` also makes the form values and submitted values typed automatically and caters for both input and output types of that schema.
```vue showLineNumbers {2-3,5-7} ```vue:line-numbers {2-3,5-7}
<script setup lang="ts"> <script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod' import * as z from 'zod'
@ -179,7 +179,7 @@ Use the `useForm` composable from `vee-validate` or use `<Form />` component to
<TabPreview name="Composition" :names="['Composition', 'Component']"> <TabPreview name="Composition" :names="['Composition', 'Component']">
<template #Composition> <template #Composition>
```vue showLineNumbers {2,19-21} ```vue:line-numbers {2,19-21}
<script setup lang="ts"> <script setup lang="ts">
import { useForm } from 'vee-validate' import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
@ -218,7 +218,7 @@ const onSubmit = form.handleSubmit((values) => {
<template #Component> <template #Component>
```vue showLineNumbers {5,24-26} ```vue:line-numbers {5,24-26}
<script setup lang="ts"> <script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod' import * as z from 'zod'
@ -256,7 +256,7 @@ function onSubmit(values) {
Based on last step we can either use `<Form />` component or `useForm` composable Based on last step we can either use `<Form />` component or `useForm` composable
`useForm` is recommended cause values are typed automatically `useForm` is recommended cause values are typed automatically
```vue showLineNumbers {2} ```vue:line-numbers {2}
<script setup lang="ts"> <script setup lang="ts">
import { useForm } from 'vee-validate' import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'

View File

@ -47,7 +47,7 @@ import {
### Link Component ### Link Component
When using the Nuxt.js <NuxtLink /> component, you can use `navigationMenuTriggerStyle()` to apply the correct styles to the trigger. When using the Nuxt.js `<NuxtLink />` component, you can use `navigationMenuTriggerStyle()` to apply the correct styles to the trigger.
```ts ```ts
import { navigationMenuTriggerStyle } from '@/components/ui/navigation-menu' import { navigationMenuTriggerStyle } from '@/components/ui/navigation-menu'

View File

@ -57,7 +57,7 @@ Use the `side` property to `<SheetContent />` to indicate the edge of the screen
You can adjust the size of the sheet using CSS classes: You can adjust the size of the sheet using CSS classes:
```vue:line-numbers showLineNumbers{4} ```vue:line-numbers {4}
<template> <template>
<Sheet> <Sheet>
<SheetTrigger>Open</SheetTrigger> <SheetTrigger>Open</SheetTrigger>

View File

@ -0,0 +1,63 @@
---
title: Sonner
description: An opinionated toast component for Vue.
docs: https://vue-sonner.vercel.app
source: apps/www/src/lib/registry/default/ui/sonner
---
<ComponentPreview name="SonnerDemo" />
## About
The Sonner component is provided by [vue-sonner](https://vue-sonner.vercel.app/), which is a Vue port of Sonner, originally created by [Emil Kowalski](https://twitter.com/emilkowalski_) for React.
## Installation
<Steps>
### Run the following command
```bash
npx shadcn-vue@latest add sonner
```
### Add the Toaster component
Add the following `Toaster` component to your `App.vue` file:
```vue title="App.vue" {2,6}
<script setup lang="ts">
import { Toaster } from '@/components/ui/sonner'
</script>
<template>
<Toaster />
</template>
```
</Steps>
## Usage
```vue
<script setup lang="ts">
import { toast } from 'vue-sonner'
import { Button } from '@/components/ui/button'
</script>
<template>
<Button
variant="outline" @click="() => {
toast('Event has been created', {
description: 'Sunday, December 03, 2023 at 9:00 AM',
action: {
label: 'Undo',
onClick: () => console.log('Undo'),
},
})
}"
>
Add to calander
</Button>
</template>
```

View File

@ -17,7 +17,7 @@ npm create astro@latest
You will be asked a few questions to configure your project: You will be asked a few questions to configure your project:
```txt showLineNumbers ```txt:line-numbers
- Where should we create your new project? - Where should we create your new project?
./your-app-name ./your-app-name
- How would you like to start your new project? - How would you like to start your new project?
@ -76,7 +76,7 @@ This will install `tailwindcss` and `@astrojs/tailwind` as dependencies and set
Add the code below to the tsconfig.json file to resolve paths: Add the code below to the tsconfig.json file to resolve paths:
```json {2-7} showLineNumbers ```json:line-numbers {2-7}
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
@ -99,7 +99,7 @@ npx shadcn-vue@latest init
You will be asked a few questions to configure `components.json`: You will be asked a few questions to configure `components.json`:
```txt showLineNumbers ```txt:line-numbers
Would you like to use TypeScript (recommended)? no / yes Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Astro Which framework are you using? Astro
Which style would you like to use? Default Which style would you like to use? Default
@ -116,15 +116,17 @@ Write configuration to components.json. Proceed? > Y/n
Import the `globals.css` file in the `src/index.astro` file: Import the `globals.css` file in the `src/index.astro` file:
```ts {2} showLineNumbers ```ts:line-numbers {2}
---
import '@/styles/globals.css' import '@/styles/globals.css'
---
``` ```
### Update astro tailwind config ### Update astro tailwind config
To prevent serving the Tailwind base styles twice, we need to tell Astro not to apply the base styles, since we already include them in our own `globals.css` file. To do this, set the `applyBaseStyles` config option for the tailwind plugin in `astro.config.mjs` to `false`. To prevent serving the Tailwind base styles twice, we need to tell Astro not to apply the base styles, since we already include them in our own `globals.css` file. To do this, set the `applyBaseStyles` config option for the tailwind plugin in `astro.config.mjs` to `false`.
```ts {3-5} showLineNumbers ```ts:line-numbers {3-5}
export default defineConfig({ export default defineConfig({
integrations: [ integrations: [
tailwind({ tailwind({
@ -144,7 +146,7 @@ npx shadcn-vue@latest add button
The command above will add the `Button` component to your project. You can then import it like this: The command above will add the `Button` component to your project. You can then import it like this:
```astro {2,10} showLineNumbers ```astro:line-numbers {2,10}
--- ---
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
--- ---

View File

@ -25,7 +25,7 @@ npx shadcn-vue@latest init
You will be asked a few questions to configure `components.json`: You will be asked a few questions to configure `components.json`:
```txt showLineNumbers ```txt:line-numbers
Would you like to use TypeScript (recommended)? no / yes Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default Which style would you like to use? Default

View File

@ -180,7 +180,7 @@ npx shadcn-vue@latest init
You will be asked a few questions to configure `components.json`: You will be asked a few questions to configure `components.json`:
```txt showLineNumbers ```txt:line-numbers
Would you like to use TypeScript (recommended)? no / yes Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default Which style would you like to use? Default

View File

@ -19,13 +19,63 @@ npm create vite@latest my-vue-app -- --template vue-ts
### Add Tailwind and its configuration ### Add Tailwind and its configuration
Install `tailwindcss` and its peer dependencies, then generate your `tailwind.config.js` and `postcss.config.js` files: Install `tailwindcss` and its peer dependencies, then generate your `tailwind.config.js` and configure `postcss` plugins
```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
``` <TabsMarkdown>
<TabMarkdown title="vite.config.ts">
Vite already has [`postcss`](https://github.com/vitejs/vite/blob/main/packages/vite/package.json#L78) dependency so you don't have to install it again in your package.json
```bash
npm install -D tailwindcss autoprefixer
```
#### `vite.config`
```typescript {5,6,10-14}
import path from "path"
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import tailwind from "tailwindcss"
import autoprefixer from "autoprefixer"
export default defineConfig({
plugins: [vue()],
css: {
postcss: {
plugins: [tailwind(), autoprefixer()],
},
},
resolve: {...}
})
```
</TabMarkdown>
<TabMarkdown title="postcss.config.js">
```bash
npm install -D tailwindcss autoprefixer postcss
```
#### `postcss.config.js`
```js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
```
</TabMarkdown>
</TabsMarkdown>
### Edit tsconfig.json ### Edit tsconfig.json
@ -42,6 +92,11 @@ Add the code below to the compilerOptions of your tsconfig.json so your app can
Add the code below to the vite.config.ts so your app can resolve paths without error Add the code below to the vite.config.ts so your app can resolve paths without error
```bash
# (so you can import "path" without error)
npm i -D @types/node
```
```typescript ```typescript
import path from "path" import path from "path"
import vue from "@vitejs/plugin-vue" import vue from "@vitejs/plugin-vue"
@ -69,7 +124,7 @@ npx shadcn-vue@latest init
You will be asked a few questions to configure `components.json`: You will be asked a few questions to configure `components.json`:
```txt showLineNumbers ```txt:line-numbers
Would you like to use TypeScript (recommended)? no / yes Would you like to use TypeScript (recommended)? no / yes
Which framework are you using? Vite / Nuxt / Laravel Which framework are you using? Vite / Nuxt / Laravel
Which style would you like to use? Default Which style would you like to use? Default

View File

@ -40,7 +40,7 @@ const date = ref({
</span> </span>
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent class="w-auto p-0" :align="'end'" :avoid-collisions="true"> <PopoverContent class="w-auto p-0" :align="'end'">
<Calendar <Calendar
v-model.range="date" v-model.range="date"
:columns="2" :columns="2"

View File

@ -95,7 +95,7 @@ async function onSubmit(values: any) {
</FormField> </FormField>
<FormField v-slot="{ componentField, value }" name="dob"> <FormField v-slot="{ componentField, value }" name="dob">
<FormItem> <FormItem class="flex flex-col">
<FormLabel>Date of birth</FormLabel> <FormLabel>Date of birth</FormLabel>
<Popover> <Popover>
<PopoverTrigger as-child> <PopoverTrigger as-child>
@ -123,7 +123,7 @@ async function onSubmit(values: any) {
</FormField> </FormField>
<FormField v-slot="{ value }" name="language"> <FormField v-slot="{ value }" name="language">
<FormItem> <FormItem class="flex flex-col">
<FormLabel>Language</FormLabel> <FormLabel>Language</FormLabel>
<Popover v-model:open="open"> <Popover v-model:open="open">

View File

@ -1,5 +1,12 @@
<script setup lang='ts'> <script setup lang='ts'>
import { Card, CardContent, CardHeader, CardTitle } from '@/lib/registry/default/ui/card' import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/lib/registry/default/ui/card'
import { import {
Select, Select,
SelectContent, SelectContent,

View File

@ -14,7 +14,7 @@ import {
useVueTable, useVueTable,
} from '@tanstack/vue-table' } from '@tanstack/vue-table'
import { h, ref } from 'vue' import { h, ref } from 'vue'
import { CaretSortIcon, ChevronDownIcon } from '@radix-icons/vue' import { ChevronDown, ChevronsUpDown } from 'lucide-vue-next'
import DropdownAction from '../DataTableDemoColumn.vue' import DropdownAction from '../DataTableDemoColumn.vue'
import { Button } from '@/lib/registry/default/ui/button' import { Button } from '@/lib/registry/default/ui/button'
@ -104,7 +104,7 @@ const columns: ColumnDef<Payment>[] = [
return h(Button, { return h(Button, {
variant: 'ghost', variant: 'ghost',
onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'), onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
}, ['Email', h(CaretSortIcon, { class: 'ml-2 h-4 w-4' })]) }, ['Email', h(ChevronsUpDown, { class: 'ml-2 h-4 w-4' })])
}, },
cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')), cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
}, },
@ -179,7 +179,7 @@ const table = useVueTable({
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger as-child> <DropdownMenuTrigger as-child>
<Button variant="outline" class="ml-auto"> <Button variant="outline" class="ml-auto">
Columns <ChevronDownIcon class="ml-2 h-4 w-4" /> Columns <ChevronDown class="ml-2 h-4 w-4" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { CaretSortIcon, CheckIcon } from '@radix-icons/vue' import { Check, ChevronsUpDown } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Button } from '@/lib/registry/default/ui/button' import { Button } from '@/lib/registry/default/ui/button'
@ -44,7 +44,7 @@ const value = ref<string>('')
{{ value {{ value
? frameworks.find((framework) => framework.value === value)?.label ? frameworks.find((framework) => framework.value === value)?.label
: "Select framework..." }} : "Select framework..." }}
<CaretSortIcon class="ml-2 h-4 w-4 shrink-0 opacity-50" /> <ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent class="w-[200px] p-0"> <PopoverContent class="w-[200px] p-0">
@ -65,7 +65,7 @@ const value = ref<string>('')
}" }"
> >
{{ framework.label }} {{ framework.label }}
<CheckIcon <Check
:class="cn( :class="cn(
'ml-auto h-4 w-4', 'ml-auto h-4 w-4',
value === framework.value ? 'opacity-100' : 'opacity-0', value === framework.value ? 'opacity-100' : 'opacity-0',

View File

@ -40,7 +40,7 @@ const date = ref({
</span> </span>
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent class="w-auto p-0" align="start" :avoid-collisions="true"> <PopoverContent class="w-auto p-0" align="start">
<Calendar <Calendar
v-model.range="date" v-model.range="date"
:columns="2" :columns="2"

View File

@ -0,0 +1,69 @@
<script setup lang="ts">
import { addDays, format } from 'date-fns'
import { Calendar as CalendarIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import { cn } from '@/lib/utils'
import { Button } from '@/lib/registry/default/ui/button'
import { Calendar } from '@/lib/registry/default/ui/calendar'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/lib/registry/default/ui/popover'
const date = ref({
start: new Date(2022, 0, 20),
end: addDays(new Date(2022, 0, 20), 20),
})
</script>
<template>
<div :class="cn('grid gap-2', $attrs.class ?? '')">
<Popover>
<PopoverTrigger as-child>
<Button
id="date"
:variant="'outline'"
:class="cn(
'w-[300px] justify-start text-left font-normal',
!date && 'text-muted-foreground',
)"
>
<CalendarIcon class="mr-2 h-4 w-4" />
<span>
{{ date.start ? (
date.end ? `${format(date.start, 'LLL dd, y')} - ${format(date.end, 'LLL dd, y')}`
: format(date.start, 'LLL dd, y')
) : 'Pick a date' }}
</span>
</Button>
</PopoverTrigger>
<PopoverContent class="w-auto p-0" align="start">
<Calendar
v-model.range="date"
mode="date"
:columns="2"
>
<template #footer>
<div class="w-full px-3 pb-3">
Entry time
<Calendar
v-model="date.start"
mode="time"
hide-time-header
/>
Exit time
<Calendar
v-model="date.end"
mode="time"
hide-time-header
/>
</div>
</template>
</Calendar>
</PopoverContent>
</Popover>
</div>
</template>

View File

@ -0,0 +1,20 @@
<script setup lang="ts">
import { toast } from 'vue-sonner'
import { Button } from '@/lib/registry/default/ui/button'
</script>
<template>
<Button
variant="outline" @click="() => {
toast('Event has been created', {
description: 'Sunday, December 03, 2023 at 9:00 AM',
action: {
label: 'Undo',
onClick: () => console.log('Undo'),
},
})
}"
>
Add to calander
</Button>
</template>

View File

@ -3,15 +3,17 @@ import {
AccordionRoot, AccordionRoot,
type AccordionRootEmits, type AccordionRootEmits,
type AccordionRootProps, type AccordionRootProps,
useEmitAsProps, useForwardPropsEmits,
} from 'radix-vue' } from 'radix-vue'
const props = defineProps<AccordionRootProps>() const props = defineProps<AccordionRootProps>()
const emits = defineEmits<AccordionRootEmits>() const emits = defineEmits<AccordionRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script> </script>
<template> <template>
<AccordionRoot v-bind="{ ...props, ...useEmitAsProps(emits) }"> <AccordionRoot v-bind="forwarded">
<slot /> <slot />
</AccordionRoot> </AccordionRoot>
</template> </template>

View File

@ -1,21 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AccordionContent, type AccordionContentProps } from 'radix-vue' import { AccordionContent, type AccordionContentProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<AccordionContentProps & { class?: string }>() const props = defineProps<AccordionContentProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<AccordionContent <AccordionContent
v-bind="props" v-bind="delegatedProps"
:class=" class="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
cn(
'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
props.class,
)
"
> >
<div class="pb-4 pt-0"> <div :class="cn('pb-4 pt-0', props.class)">
<slot /> <slot />
</div> </div>
</AccordionContent> </AccordionContent>

View File

@ -1,14 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { AccordionItem, type AccordionItemProps } from 'radix-vue' import { type HTMLAttributes, computed } from 'vue'
import { AccordionItem, type AccordionItemProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<AccordionItemProps & { class?: string }>() const props = defineProps<AccordionItemProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script> </script>
<template> <template>
<AccordionItem <AccordionItem
v-bind="props" v-bind="forwardedProps"
:class="cn('border-b', props.class ?? '')" :class="cn('border-b', props.class)"
> >
<slot /> <slot />
</AccordionItem> </AccordionItem>

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
AccordionHeader, AccordionHeader,
AccordionTrigger, AccordionTrigger,
@ -7,13 +8,19 @@ import {
import { ChevronDown } from 'lucide-vue-next' import { ChevronDown } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<AccordionTriggerProps & { class?: string }>() const props = defineProps<AccordionTriggerProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<AccordionHeader class="flex" as="div"> <AccordionHeader class="flex">
<AccordionTrigger <AccordionTrigger
v-bind="props" v-bind="delegatedProps"
:class=" :class="
cn( cn(
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180', 'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
@ -22,9 +29,11 @@ const props = defineProps<AccordionTriggerProps & { class?: string }>()
" "
> >
<slot /> <slot />
<ChevronDown <slot name="icon">
class="h-4 w-4 shrink-0 transition-transform duration-200" <ChevronDown
/> class="h-4 w-4 shrink-0 transition-transform duration-200"
/>
</slot>
</AccordionTrigger> </AccordionTrigger>
</AccordionHeader> </AccordionHeader>
</template> </template>

View File

@ -1,13 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AlertDialogAction, type AlertDialogActionProps } from 'radix-vue' import { AlertDialogAction, type AlertDialogActionProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { buttonVariants } from '@/lib/registry/default/ui/button' import { buttonVariants } from '@/lib/registry/default/ui/button'
const props = defineProps<AlertDialogActionProps>() const props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<AlertDialogAction v-bind="props" :class="cn(buttonVariants(), $attrs.class ?? '')"> <AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
<slot /> <slot />
</AlertDialogAction> </AlertDialogAction>
</template> </template>

View File

@ -1,13 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AlertDialogCancel, type AlertDialogCancelProps } from 'radix-vue' import { AlertDialogCancel, type AlertDialogCancelProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { buttonVariants } from '@/lib/registry/default/ui/button' import { buttonVariants } from '@/lib/registry/default/ui/button'
const props = defineProps<AlertDialogCancelProps>() const props = defineProps<AlertDialogCancelProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<AlertDialogCancel v-bind="props" :class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', $attrs.class ?? '')"> <AlertDialogCancel v-bind="delegatedProps" :class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', props.class)">
<slot /> <slot />
</AlertDialogCancel> </AlertDialogCancel>
</template> </template>

View File

@ -1,30 +1,37 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
AlertDialogContent, AlertDialogContent,
type AlertDialogContentEmits, type AlertDialogContentEmits,
type AlertDialogContentProps, type AlertDialogContentProps,
AlertDialogOverlay, AlertDialogOverlay,
AlertDialogPortal, AlertDialogPortal,
useEmitAsProps, useForwardPropsEmits,
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<AlertDialogContentProps & { class?: string }>() const props = defineProps<AlertDialogContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<AlertDialogContentEmits>() const emits = defineEmits<AlertDialogContentEmits>()
const emitsAsProps = useEmitAsProps(emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<AlertDialogPortal> <AlertDialogPortal>
<AlertDialogOverlay <AlertDialogOverlay
class="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/> />
<AlertDialogContent <AlertDialogContent
v-bind="{ ...props, ...emitsAsProps }" v-bind="forwarded"
:class=" :class="
cn( cn(
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full', 'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
props.class, props.class,
) )
" "

View File

@ -1,17 +1,24 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
AlertDialogDescription, AlertDialogDescription,
type AlertDialogDescriptionProps, type AlertDialogDescriptionProps,
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<AlertDialogDescriptionProps & { class?: string }>() const props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<AlertDialogDescription <AlertDialogDescription
:class="cn('text-muted-foreground text-sm', props.class)" v-bind="delegatedProps"
:as-child="props.asChild" :class="cn('text-sm text-muted-foreground', props.class)"
> >
<slot /> <slot />
</AlertDialogDescription> </AlertDialogDescription>

View File

@ -1,19 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps({ const props = defineProps<{
class: { class?: HTMLAttributes['class']
type: String, }>()
default: '',
},
})
</script> </script>
<template> <template>
<div <div
:class=" :class="
cn( cn(
'flex flex-col space-y-2 sm:space-y-0 mt-3.5 sm:flex-row sm:justify-end sm:space-x-2', 'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
props.class, props.class,
) )
" "

View File

@ -1,17 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps({ const props = defineProps<{
class: { class?: HTMLAttributes['class']
type: String, }>()
default: '',
},
})
</script> </script>
<template> <template>
<div <div
:class="cn('flex flex-col space-y-2 text-center sm:text-left', props.class)" :class="cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)"
> >
<slot /> <slot />
</div> </div>

View File

@ -1,14 +1,21 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AlertDialogTitle, type AlertDialogTitleProps } from 'radix-vue' import { AlertDialogTitle, type AlertDialogTitleProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<AlertDialogTitleProps & { class?: string }>() const props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<AlertDialogTitle <AlertDialogTitle
:as-child="props.asChild" v-bind="delegatedProps"
:class="cn('text-lg text-foreground font-semibold', props.class)" :class="cn('text-lg font-semibold', props.class)"
> >
<slot /> <slot />
</AlertDialogTitle> </AlertDialogTitle>

View File

@ -1,17 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { alertVariants } from '.' import type { HTMLAttributes } from 'vue'
import { type AlertVariants, alertVariants } from '.'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
interface Props { const props = defineProps<{
variant?: NonNullable<Parameters<typeof alertVariants>[0]>['variant'] class?: HTMLAttributes['class']
class?: string variant?: AlertVariants['variant']
} }>()
const props = defineProps<Props>()
</script> </script>
<template> <template>
<div :class="cn(alertVariants({ variant }), props.class ?? '')"> <div :class="cn(alertVariants({ variant }), props.class)" role="alert">
<slot /> <slot />
</div> </div>
</template> </template>

View File

@ -1,9 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps({ const props = defineProps<{
class: String, class?: HTMLAttributes['class']
}) }>()
</script> </script>
<template> <template>

View File

@ -1,9 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script> </script>
<template> <template>
<h5 :class="cn('mb-1 font-medium leading-none tracking-tight', $attrs.class ?? '')"> <h5 :class="cn('mb-1 font-medium leading-none tracking-tight', props.class)">
<slot /> <slot />
</h5> </h5>
</template> </template>

View File

@ -1,4 +1,4 @@
import { cva } from 'class-variance-authority' import { type VariantProps, cva } from 'class-variance-authority'
export { default as Alert } from './Alert.vue' export { default as Alert } from './Alert.vue'
export { default as AlertTitle } from './AlertTitle.vue' export { default as AlertTitle } from './AlertTitle.vue'
@ -19,3 +19,5 @@ export const alertVariants = cva(
}, },
}, },
) )
export type AlertVariants = VariantProps<typeof alertVariants>

View File

@ -1,15 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { AvatarRoot } from 'radix-vue' import { AvatarRoot } from 'radix-vue'
import { avatarVariant } from '.' import { type AvatarVariants, avatarVariant } from '.'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
interface Props { const props = withDefaults(defineProps<{
size?: NonNullable<Parameters<typeof avatarVariant>[0]>['size'] class?: HTMLAttributes['class']
shape?: NonNullable<Parameters<typeof avatarVariant>[0]>['shape'] size?: AvatarVariants['size']
class?: string shape?: AvatarVariants['shape']
} }>(), {
const props = withDefaults(defineProps<Props>(), {
size: 'sm', size: 'sm',
shape: 'circle', shape: 'circle',
}) })

View File

@ -1,4 +1,4 @@
import { cva } from 'class-variance-authority' import { type VariantProps, cva } from 'class-variance-authority'
export { default as Avatar } from './Avatar.vue' export { default as Avatar } from './Avatar.vue'
export { default as AvatarImage } from './AvatarImage.vue' export { default as AvatarImage } from './AvatarImage.vue'
@ -20,3 +20,5 @@ export const avatarVariant = cva(
}, },
}, },
) )
export type AvatarVariants = VariantProps<typeof avatarVariant>

View File

@ -1,18 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import type { VariantProps } from 'class-variance-authority' import type { HTMLAttributes } from 'vue'
import { badgeVariants } from '.' import { type BadgeVariants, badgeVariants } from '.'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
interface BadgeVariantProps extends VariantProps<typeof badgeVariants> {} const props = defineProps<{
variant?: BadgeVariants['variant']
interface Props { class?: HTMLAttributes['class']
variant?: BadgeVariantProps['variant'] }>()
}
defineProps<Props>()
</script> </script>
<template> <template>
<div :class="cn(badgeVariants({ variant }), $attrs.class ?? '')"> <div :class="cn(badgeVariants({ variant }), props.class)">
<slot /> <slot />
</div> </div>
</template> </template>

View File

@ -1,4 +1,4 @@
import { cva } from 'class-variance-authority' import { type VariantProps, cva } from 'class-variance-authority'
export { default as Badge } from './Badge.vue' export { default as Badge } from './Badge.vue'
@ -21,3 +21,5 @@ export const badgeVariants = cva(
}, },
}, },
) )
export type BadgeVariants = VariantProps<typeof badgeVariants>

View File

@ -1,20 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import type { VariantProps } from 'class-variance-authority' import type { HTMLAttributes } from 'vue'
import { Primitive, type PrimitiveProps } from 'radix-vue' import { Primitive, type PrimitiveProps } from 'radix-vue'
import { buttonVariants } from '.' import { type ButtonVariants, buttonVariants } from '.'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
interface ButtonVariantProps extends VariantProps<typeof buttonVariants> {}
interface Props extends PrimitiveProps { interface Props extends PrimitiveProps {
variant?: ButtonVariantProps['variant'] variant?: ButtonVariants['variant']
size?: ButtonVariantProps['size'] size?: ButtonVariants['size']
as?: string as?: string
class?: HTMLAttributes['class']
} }
withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
variant: 'default',
size: 'default',
as: 'button', as: 'button',
}) })
</script> </script>
@ -23,7 +20,7 @@ withDefaults(defineProps<Props>(), {
<Primitive <Primitive
:as="as" :as="as"
:as-child="asChild" :as-child="asChild"
:class="cn(buttonVariants({ variant, size }), $attrs.class ?? '')" :class="cn(buttonVariants({ variant, size }), props.class)"
> >
<slot /> <slot />
</Primitive> </Primitive>

View File

@ -1,9 +1,9 @@
import { cva } from 'class-variance-authority' import { type VariantProps, cva } from 'class-variance-authority'
export { default as Button } from './Button.vue' export { default as Button } from './Button.vue'
export const buttonVariants = cva( export const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{ {
variants: { variants: {
variant: { variant: {
@ -30,3 +30,5 @@ export const buttonVariants = cva(
}, },
}, },
) )
export type ButtonVariants = VariantProps<typeof buttonVariants>

View File

@ -1,11 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { ChevronLeft, ChevronRight } from 'lucide-vue-next'
import type { Calendar } from 'v-calendar' import type { Calendar } from 'v-calendar'
import { DatePicker } from 'v-calendar' import { DatePicker } from 'v-calendar'
import { ChevronLeft, ChevronRight } from 'lucide-vue-next' import { computed, nextTick, onMounted, ref, useSlots } from 'vue'
import { computed, nextTick, onMounted, ref } from 'vue' import { isVCalendarSlot } from '.'
import { buttonVariants } from '@/lib/registry/default/ui/button'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { buttonVariants } from '@/lib/registry/default/ui/button'
/* Extracted from v-calendar */ /* Extracted from v-calendar */
type DatePickerModel = DatePickerDate | DatePickerRangeObject type DatePickerModel = DatePickerDate | DatePickerRangeObject
@ -28,8 +29,7 @@ interface SimpleDateParts {
defineOptions({ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}) })
const props = withDefaults(defineProps< {
const props = withDefaults(defineProps<{
modelValue?: string | number | Date | DatePickerModel modelValue?: string | number | Date | DatePickerModel
modelModifiers?: object modelModifiers?: object
columns?: number columns?: number
@ -64,11 +64,21 @@ onMounted(async () => {
if (modelValue.value instanceof Date && calendarRef.value) if (modelValue.value instanceof Date && calendarRef.value)
calendarRef.value.focusDate(modelValue.value) calendarRef.value.focusDate(modelValue.value)
}) })
const $slots = useSlots()
const vCalendarSlots = computed(() => {
return Object.keys($slots)
.filter(name => isVCalendarSlot(name))
.reduce((obj: Record<string, any>, key: string) => {
obj[key] = $slots[key]
return obj
}, {})
})
</script> </script>
<template> <template>
<div class="relative"> <div class="relative">
<div class="absolute flex justify-between w-full px-4 top-3 z-[1]"> <div v-if="$attrs.mode !== 'time'" class="absolute flex justify-between w-full px-4 top-3 z-[1]">
<button :class="cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')" @click="handleNav('prev')"> <button :class="cn(buttonVariants({ variant: 'outline' }), 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100')" @click="handleNav('prev')">
<ChevronLeft class="w-4 h-4" /> <ChevronLeft class="w-4 h-4" />
</button> </button>
@ -79,18 +89,30 @@ onMounted(async () => {
<DatePicker <DatePicker
ref="datePicker" ref="datePicker"
v-model="modelValue"
v-bind="$attrs" v-bind="$attrs"
v-model="modelValue"
:model-modifiers="modelModifiers" :model-modifiers="modelModifiers"
class="calendar" class="calendar"
trim-weeks trim-weeks
:transition="'none'" :transition="'none'"
:columns="columns" :columns="columns"
/> >
<template v-for="(_, slot) of vCalendarSlots" #[slot]="scope">
<slot :name="slot" v-bind="scope" />
</template>
<template #nav-prev-button>
<ChevronLeft />
</template>
<template #nav-next-button>
<ChevronRight />
</template>
</DatePicker>
</div> </div>
</template> </template>
<style lang="postcss"> <style lang="css">
.calendar { .calendar {
@apply p-3 text-center; @apply p-3 text-center;
} }
@ -98,7 +120,25 @@ onMounted(async () => {
@apply grid gap-4; @apply grid gap-4;
} }
.calendar .vc-title { .calendar .vc-title {
@apply text-sm font-medium pointer-events-none; @apply text-sm font-medium relative z-20;
}
.vc-popover-content-wrapper .vc-popover-content {
@apply mt-3 rounded-md max-w-xs border bg-background;
}
.vc-popover-content-wrapper .vc-nav-header {
@apply flex justify-between items-center p-2;
}
.vc-popover-content-wrapper .vc-nav-items {
@apply grid grid-cols-4 gap-2 p-2;
}
.vc-popover-content-wrapper .vc-nav-items .vc-nav-item {
@apply rounded-md px-2 py-1;
}
.vc-popover-content-wrapper .vc-nav-items .vc-nav-item:hover {
@apply text-muted-foreground bg-muted;
}
.vc-popover-content-wrapper .vc-nav-items .vc-nav-item.is-active {
@apply bg-primary text-primary-foreground;
} }
.calendar .vc-pane-header-wrapper { .calendar .vc-pane-header-wrapper {
@apply hidden; @apply hidden;
@ -110,16 +150,22 @@ onMounted(async () => {
@apply flex; @apply flex;
} }
.calendar .vc-weekday { .calendar .vc-weekday {
@apply text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]; @apply text-muted-foreground rounded-md w-full font-normal text-[0.8rem];
}
.calendar .vc-weekday-1 {
@apply pr-3;
}
.calendar .vc-weekday-7 {
@apply pl-3;
} }
.calendar .vc-weeks { .calendar .vc-weeks {
@apply w-full space-y-2 flex flex-col [&>_div]:grid [&>_div]:grid-cols-7; @apply w-full space-y-2 flex flex-col [&>_div]:grid [&>_div]:grid-cols-7;
} }
.calendar .vc-day:has(.vc-highlights) { .calendar .vc-day:has(.vc-highlights) {
@apply bg-accent first:rounded-l-md last:rounded-r-md overflow-hidden; @apply first:rounded-l-md last:rounded-r-md;
} }
.calendar .vc-day.is-today:not(:has(.vc-day-layer)) { .calendar .vc-day.is-today:not(:has(.vc-day-layer)) .vc-day-content {
@apply bg-secondary rounded-md; @apply bg-secondary text-primary rounded-md;
} }
.calendar .vc-day:has(.vc-highlight-base-start) { .calendar .vc-day:has(.vc-highlight-base-start) {
@apply rounded-l-md; @apply rounded-l-md;
@ -131,7 +177,7 @@ onMounted(async () => {
@apply rounded-md; @apply rounded-md;
} }
.calendar .vc-day-content { .calendar .vc-day-content {
@apply text-center text-sm p-0 relative focus-within:relative focus-within:z-20 inline-flex items-center justify-center ring-offset-background hover:transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 hover:bg-accent hover:text-accent-foreground h-9 w-9 font-normal aria-selected:opacity-100 select-none; @apply text-center text-sm p-0 relative focus-within:relative focus-within:z-20 inline-flex items-center justify-center ring-offset-background hover:transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 hover:bg-accent hover:text-accent-foreground h-9 w-9 font-normal aria-selected:opacity-100 select-none;
} }
.calendar .vc-day-content:not(.vc-highlight-content-light) { .calendar .vc-day-content:not(.vc-highlight-content-light) {
@apply rounded-md; @apply rounded-md;
@ -157,6 +203,7 @@ onMounted(async () => {
--vc-slide-duration: 0.15s; --vc-slide-duration: 0.15s;
--vc-slide-timing: ease; --vc-slide-timing: ease;
} }
.calendar .vc-fade-enter-active, .calendar .vc-fade-enter-active,
.calendar .vc-fade-leave-active, .calendar .vc-fade-leave-active,
.calendar .vc-slide-left-enter-active, .calendar .vc-slide-left-enter-active,
@ -183,6 +230,7 @@ onMounted(async () => {
backface-visibility: hidden; backface-visibility: hidden;
pointer-events: none; pointer-events: none;
} }
.calendar .vc-none-leave-active, .calendar .vc-none-leave-active,
.calendar .vc-fade-leave-active, .calendar .vc-fade-leave-active,
.calendar .vc-slide-left-leave-active, .calendar .vc-slide-left-leave-active,
@ -192,6 +240,7 @@ onMounted(async () => {
position: absolute !important; position: absolute !important;
width: 100%; width: 100%;
} }
.calendar .vc-none-enter-from, .calendar .vc-none-enter-from,
.calendar .vc-none-leave-to, .calendar .vc-none-leave-to,
.calendar .vc-fade-enter-from, .calendar .vc-fade-enter-from,
@ -208,6 +257,7 @@ onMounted(async () => {
.calendar .vc-slide-fade-leave-to { .calendar .vc-slide-fade-leave-to {
opacity: 0; opacity: 0;
} }
.calendar .vc-slide-left-enter-from, .calendar .vc-slide-left-enter-from,
.calendar .vc-slide-right-leave-to, .calendar .vc-slide-right-leave-to,
.calendar .vc-slide-fade-enter-from.direction-left, .calendar .vc-slide-fade-enter-from.direction-left,
@ -215,6 +265,7 @@ onMounted(async () => {
-webkit-transform: translateX(var(--vc-slide-translate)); -webkit-transform: translateX(var(--vc-slide-translate));
transform: translateX(var(--vc-slide-translate)); transform: translateX(var(--vc-slide-translate));
} }
.calendar .vc-slide-right-enter-from, .calendar .vc-slide-right-enter-from,
.calendar .vc-slide-left-leave-to, .calendar .vc-slide-left-leave-to,
.calendar .vc-slide-fade-enter-from.direction-right, .calendar .vc-slide-fade-enter-from.direction-right,
@ -222,6 +273,7 @@ onMounted(async () => {
-webkit-transform: translateX(calc(-1 * var(--vc-slide-translate))); -webkit-transform: translateX(calc(-1 * var(--vc-slide-translate)));
transform: translateX(calc(-1 * var(--vc-slide-translate))); transform: translateX(calc(-1 * var(--vc-slide-translate)));
} }
.calendar .vc-slide-up-enter-from, .calendar .vc-slide-up-enter-from,
.calendar .vc-slide-down-leave-to, .calendar .vc-slide-down-leave-to,
.calendar .vc-slide-fade-enter-from.direction-top, .calendar .vc-slide-fade-enter-from.direction-top,
@ -229,6 +281,7 @@ onMounted(async () => {
-webkit-transform: translateY(var(--vc-slide-translate)); -webkit-transform: translateY(var(--vc-slide-translate));
transform: translateY(var(--vc-slide-translate)); transform: translateY(var(--vc-slide-translate));
} }
.calendar .vc-slide-down-enter-from, .calendar .vc-slide-down-enter-from,
.calendar .vc-slide-up-leave-to, .calendar .vc-slide-up-leave-to,
.calendar .vc-slide-fade-enter-from.direction-bottom, .calendar .vc-slide-fade-enter-from.direction-bottom,

View File

@ -1 +1,22 @@
export { default as Calendar } from './Calendar.vue' export { default as Calendar } from './Calendar.vue'
import type { CalendarSlotName } from 'v-calendar/dist/types/src/components/Calendar/CalendarSlot.vue.d.ts'
export function isVCalendarSlot(slotName: string): slotName is CalendarSlotName {
const validSlots: CalendarSlotName[] = [
'day-content',
'day-popover',
'dp-footer',
'footer',
'header-title-wrapper',
'header-title',
'header-prev-button',
'header-next-button',
'nav',
'nav-prev-button',
'nav-next-button',
'page',
'time-header',
]
return validSlots.includes(slotName as CalendarSlotName)
}

View File

@ -1,12 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps({ const props = defineProps<{
class: { class?: HTMLAttributes['class']
type: String, }>()
default: '',
},
})
</script> </script>
<template> <template>

View File

@ -1,12 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps({ const props = defineProps<{
class: { class?: HTMLAttributes['class']
type: String, }>()
default: '',
},
})
</script> </script>
<template> <template>

View File

@ -1,12 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps({ const props = defineProps<{
class: { class?: HTMLAttributes['class']
type: String, }>()
default: '',
},
})
</script> </script>
<template> <template>

View File

@ -1,16 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps({ const props = defineProps<{
class: { class?: HTMLAttributes['class']
type: String, }>()
default: '',
},
})
</script> </script>
<template> <template>
<div :class="cn('p-6 pt-0', props.class)"> <div :class="cn('flex items-center p-6 pt-0', props.class)">
<slot /> <slot />
</div> </div>
</template> </template>

View File

@ -1,16 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps({ const props = defineProps<{
class: { class?: HTMLAttributes['class']
type: String, }>()
default: '',
},
})
</script> </script>
<template> <template>
<div :class="cn('flex flex-col space-y-1.5 p-6', props.class)"> <div :class="cn('flex flex-col gap-y-1.5 p-6', props.class)">
<slot /> <slot />
</div> </div>
</template> </template>

View File

@ -1,18 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps({ const props = defineProps<{
class: { class?: HTMLAttributes['class']
type: String, }>()
default: '',
},
})
</script> </script>
<template> <template>
<h3 <h3
:class=" :class="
cn('text-2xl font-semibold leading-none tracking-tighter', props.class) cn('text-2xl font-semibold leading-none tracking-tight', props.class)
" "
> >
<slot /> <slot />

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { WithClassAsProps } from './interface'
import { useCarousel } from './useCarousel' import { useCarousel } from './useCarousel'
import type { WithClassAsProps } from './interface'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
defineOptions({ defineOptions({

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { WithClassAsProps } from './interface'
import { useCarousel } from './useCarousel' import { useCarousel } from './useCarousel'
import type { WithClassAsProps } from './interface'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<WithClassAsProps>() const props = defineProps<WithClassAsProps>()

View File

@ -1,13 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue' import type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue'
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'radix-vue' import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from 'radix-vue'
import { Check } from 'lucide-vue-next' import { Check } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<CheckboxRootProps>() const props = defineProps<CheckboxRootProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<CheckboxRootEmits>() const emits = defineEmits<CheckboxRootEmits>()
const forwarded = useForwardPropsEmits(props, emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
@ -15,10 +22,12 @@ const forwarded = useForwardPropsEmits(props, emits)
v-bind="forwarded" v-bind="forwarded"
:class=" :class="
cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground', cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
$attrs.class ?? '')" props.class)"
> >
<CheckboxIndicator class="flex h-full w-full items-center justify-center text-current"> <CheckboxIndicator class="flex h-full w-full items-center justify-center text-current">
<Check class="h-4 w-4" /> <slot>
<Check class="h-4 w-4" />
</slot>
</CheckboxIndicator> </CheckboxIndicator>
</CheckboxRoot> </CheckboxRoot>
</template> </template>

View File

@ -1,13 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { CollapsibleRoot, useEmitAsProps } from 'radix-vue' import { CollapsibleRoot, useForwardPropsEmits } from 'radix-vue'
import type { CollapsibleRootEmits, CollapsibleRootProps } from 'radix-vue' import type { CollapsibleRootEmits, CollapsibleRootProps } from 'radix-vue'
const props = defineProps<CollapsibleRootProps>() const props = defineProps<CollapsibleRootProps>()
const emits = defineEmits<CollapsibleRootEmits>() const emits = defineEmits<CollapsibleRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script> </script>
<template> <template>
<CollapsibleRoot v-slot="{ open }" v-bind="{ ...props, ...useEmitAsProps(emits) }"> <CollapsibleRoot v-slot="{ open }" v-bind="forwarded">
<slot :open="open" /> <slot :open="open" />
</CollapsibleRoot> </CollapsibleRoot>
</template> </template>

View File

@ -1,20 +1,29 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import type { ComboboxRootEmits, ComboboxRootProps } from 'radix-vue' import type { ComboboxRootEmits, ComboboxRootProps } from 'radix-vue'
import { ComboboxRoot, useForwardPropsEmits } from 'radix-vue' import { ComboboxRoot, useForwardPropsEmits } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ComboboxRootProps>() const props = withDefaults(defineProps<ComboboxRootProps & { class?: HTMLAttributes['class'] }>(), {
open: true,
modelValue: '',
})
const emits = defineEmits<ComboboxRootEmits>() const emits = defineEmits<ComboboxRootEmits>()
const forwarded = useForwardPropsEmits(props, emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<ComboboxRoot <ComboboxRoot
v-bind="forwarded" v-bind="forwarded"
:open="true" :class="cn('flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground', props.class)"
:model-value="''"
:class="cn('flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground', $attrs.class ?? '')"
> >
<slot /> <slot />
</ComboboxRoot> </ComboboxRoot>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { useEmitAsProps } from 'radix-vue' import { useForwardPropsEmits } from 'radix-vue'
import type { DialogRootEmits, DialogRootProps } from 'radix-vue' import type { DialogRootEmits, DialogRootProps } from 'radix-vue'
import Command from './Command.vue' import Command from './Command.vue'
import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog' import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
@ -7,12 +7,12 @@ import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
const props = defineProps<DialogRootProps>() const props = defineProps<DialogRootProps>()
const emits = defineEmits<DialogRootEmits>() const emits = defineEmits<DialogRootEmits>()
const emitsAsProps = useEmitAsProps(emits) const forwarded = useForwardPropsEmits(props, emits)
</script> </script>
<template> <template>
<Dialog v-bind="{ ...props, ...emitsAsProps }"> <Dialog v-bind="forwarded">
<DialogContent class="p-0 overflow-hidden shadow-lg"> <DialogContent class="overflow-hidden p-0 shadow-lg">
<Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> <Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
<slot /> <slot />
</Command> </Command>

View File

@ -1,13 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import type { ComboboxEmptyProps } from 'radix-vue' import type { ComboboxEmptyProps } from 'radix-vue'
import { ComboboxEmpty } from 'radix-vue' import { ComboboxEmpty } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ComboboxEmptyProps>() const props = defineProps<ComboboxEmptyProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<ComboboxEmpty v-bind="props" :class="cn('py-6 text-center text-sm', $attrs.class ?? '')"> <ComboboxEmpty v-bind="delegatedProps" :class="cn('py-6 text-center text-sm', props.class)">
<slot /> <slot />
</ComboboxEmpty> </ComboboxEmpty>
</template> </template>

View File

@ -1,17 +1,25 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import type { ComboboxGroupProps } from 'radix-vue' import type { ComboboxGroupProps } from 'radix-vue'
import { ComboboxGroup, ComboboxLabel } from 'radix-vue' import { ComboboxGroup, ComboboxLabel } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ComboboxGroupProps & { const props = defineProps<ComboboxGroupProps & {
class?: HTMLAttributes['class']
heading?: string heading?: string
}>() }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<ComboboxGroup <ComboboxGroup
v-bind="props" v-bind="delegatedProps"
:class="cn('overflow-hidden p-1 text-foreground', $attrs.class ?? '')" :class="cn('overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground', props.class)"
> >
<ComboboxLabel v-if="heading" class="px-2 py-1.5 text-xs font-medium text-muted-foreground"> <ComboboxLabel v-if="heading" class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
{{ heading }} {{ heading }}

View File

@ -1,24 +1,33 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { Search } from 'lucide-vue-next' import { Search } from 'lucide-vue-next'
import { ComboboxInput, type ComboboxInputProps } from 'radix-vue' import { ComboboxInput, type ComboboxInputProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ComboboxInputProps>() defineOptions({
</script>
<script lang="ts">
export default {
inheritAttrs: false, inheritAttrs: false,
} })
const props = defineProps<ComboboxInputProps & {
class?: HTMLAttributes['class']
}>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script> </script>
<template> <template>
<div class="flex items-center border-b px-3" cmdk-input-wrapper> <div class="flex items-center border-b px-3" cmdk-input-wrapper>
<Search class="mr-2 h-4 w-4 shrink-0 opacity-50" /> <Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
<ComboboxInput <ComboboxInput
v-bind="{ ...props, ...$attrs }" v-bind="{ ...forwardedProps, ...$attrs }"
auto-focus auto-focus
:class="cn('flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', $attrs.class ?? '')" :class="cn('flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', props.class)"
/> />
</div> </div>
</template> </template>

View File

@ -1,18 +1,25 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import type { ComboboxItemEmits, ComboboxItemProps } from 'radix-vue' import type { ComboboxItemEmits, ComboboxItemProps } from 'radix-vue'
import { ComboboxItem, useEmitAsProps } from 'radix-vue' import { ComboboxItem, useForwardPropsEmits } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ComboboxItemProps>() const props = defineProps<ComboboxItemProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ComboboxItemEmits>() const emits = defineEmits<ComboboxItemEmits>()
const emitsAsProps = useEmitAsProps(emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<ComboboxItem <ComboboxItem
v-bind="{ ...props, ...emitsAsProps }" v-bind="forwarded"
:class="cn('relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', $attrs.class ?? '')" :class="cn('relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', props.class)"
@select.prevent @select.prevent
> >
<slot /> <slot />

View File

@ -1,16 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import type { ComboboxContentEmits, ComboboxContentProps } from 'radix-vue' import type { ComboboxContentEmits, ComboboxContentProps } from 'radix-vue'
import { ComboboxContent, useForwardPropsEmits } from 'radix-vue' import { ComboboxContent, useForwardPropsEmits } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ComboboxContentProps>() const props = defineProps<ComboboxContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ComboboxContentEmits>() const emits = defineEmits<ComboboxContentEmits>()
const forwarded = useForwardPropsEmits(props, emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<ComboboxContent v-bind="forwarded" :class="cn('max-h-[300px] overflow-y-auto overflow-x-hidden', $attrs.class ?? '')"> <ComboboxContent v-bind="forwarded" :class="cn('max-h-[300px] overflow-y-auto overflow-x-hidden', props.class)">
<div role="presentation"> <div role="presentation">
<slot /> <slot />
</div> </div>

View File

@ -1,15 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import type { ComboboxSeparatorProps } from 'radix-vue' import type { ComboboxSeparatorProps } from 'radix-vue'
import { ComboboxSeparator } from 'radix-vue' import { ComboboxSeparator } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ComboboxSeparatorProps>() const props = defineProps<ComboboxSeparatorProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<ComboboxSeparator <ComboboxSeparator
v-bind="props" v-bind="delegatedProps"
:class="cn('-mx-1 h-px bg-border', $attrs.class ?? '')" :class="cn('-mx-1 h-px bg-border', props.class)"
> >
<slot /> <slot />
</ComboboxSeparator> </ComboboxSeparator>

View File

@ -1,9 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script> </script>
<template> <template>
<span :class="cn('ml-auto text-xs tracking-widest text-muted-foreground', $attrs.class ?? '')"> <span :class="cn('ml-auto text-xs tracking-widest text-muted-foreground', props.class)">
<slot /> <slot />
</span> </span>
</template> </template>

View File

@ -1,33 +1,38 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
ContextMenuCheckboxItem, ContextMenuCheckboxItem,
type ContextMenuCheckboxItemEmits, type ContextMenuCheckboxItemEmits,
type ContextMenuCheckboxItemProps, type ContextMenuCheckboxItemProps,
ContextMenuItemIndicator, ContextMenuItemIndicator,
useEmitAsProps, useForwardPropsEmits,
} from 'radix-vue' } from 'radix-vue'
import { Check } from 'lucide-vue-next' import { Check } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ContextMenuCheckboxItemProps & { class?: string }>() const props = defineProps<ContextMenuCheckboxItemProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ContextMenuCheckboxItemEmits>() const emits = defineEmits<ContextMenuCheckboxItemEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<ContextMenuCheckboxItem <ContextMenuCheckboxItem
v-bind="{ ...props, ...useEmitAsProps(emits) }" v-bind="forwarded"
:class="[ :class="cn(
cn( 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', props.class,
props.class, )"
),
]"
> >
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<ContextMenuItemIndicator <ContextMenuItemIndicator>
class="absolute left-1.5 inline-flex w-4 h-4 items-center justify-center" <Check class="h-4 w-4" />
>
<Check class="h-4 h-w" />
</ContextMenuItemIndicator> </ContextMenuItemIndicator>
</span> </span>
<slot /> <slot />

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
ContextMenuContent, ContextMenuContent,
type ContextMenuContentEmits, type ContextMenuContentEmits,
@ -8,23 +9,26 @@ import {
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ContextMenuContentProps & { class?: string }>() const props = defineProps<ContextMenuContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ContextMenuContentEmits>() const emits = defineEmits<ContextMenuContentEmits>()
const forwarded = useForwardPropsEmits(props, emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<ContextMenuPortal> <ContextMenuPortal>
<ContextMenuContent <ContextMenuContent
:align-offset="props.alignOffset"
:class="[
cn(
'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
),
]"
v-bind="forwarded" v-bind="forwarded"
:class="cn(
'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
)"
> >
<slot /> <slot />
</ContextMenuContent> </ContextMenuContent>

View File

@ -1,26 +1,33 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
ContextMenuItem, ContextMenuItem,
type ContextMenuItemEmits, type ContextMenuItemEmits,
type ContextMenuItemProps, type ContextMenuItemProps,
useEmitAsProps, useForwardPropsEmits,
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ContextMenuItemProps & { class?: string; inset?: boolean }>() const props = defineProps<ContextMenuItemProps & { class?: HTMLAttributes['class']; inset?: boolean }>()
const emits = defineEmits<ContextMenuItemEmits>() const emits = defineEmits<ContextMenuItemEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<ContextMenuItem <ContextMenuItem
v-bind="{ ...props, ...useEmitAsProps(emits) }" v-bind="forwarded"
:class="[ :class="cn(
cn( 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', inset && 'pl-8',
inset && 'pl-8', props.class,
props.class, )"
),
]"
> >
<slot /> <slot />
</ContextMenuItem> </ContextMenuItem>

View File

@ -1,16 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
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?: string; inset?: boolean }>() const props = defineProps<ContextMenuLabelProps & { class?: HTMLAttributes['class']; inset?: boolean }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<ContextMenuLabel <ContextMenuLabel
v-bind="props" v-bind="delegatedProps"
:class=" :class="
cn('px-2 py-1.5 text-sm font-semibold text-foreground', cn('px-2 py-1.5 text-sm font-semibold text-foreground',
inset && 'pl-8', props.class ?? '', inset && 'pl-8', props.class,
)" )"
> >
<slot /> <slot />

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
ContextMenuItemIndicator, ContextMenuItemIndicator,
ContextMenuRadioItem, ContextMenuRadioItem,
@ -9,21 +10,25 @@ import {
import { Circle } from 'lucide-vue-next' import { Circle } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ContextMenuRadioItemProps & { class?: string }>() const props = defineProps<ContextMenuRadioItemProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ContextMenuRadioItemEmits>() const emits = defineEmits<ContextMenuRadioItemEmits>()
const forwarded = useForwardPropsEmits(props, emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<ContextMenuRadioItem <ContextMenuRadioItem
v-bind="forwarded" v-bind="forwarded"
:class="[ :class="cn(
cn( 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', props.class,
props.class, )"
),
]"
> >
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<ContextMenuItemIndicator> <ContextMenuItemIndicator>

View File

@ -1,13 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
ContextMenuSeparator, ContextMenuSeparator,
type ContextMenuSeparatorProps, type ContextMenuSeparatorProps,
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<ContextMenuSeparatorProps>() const props = defineProps<ContextMenuSeparatorProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<ContextMenuSeparator v-bind="props" :class="cn('-mx-1 my-1 h-px bg-border', $attrs.class ?? '')" /> <ContextMenuSeparator v-bind="delegatedProps" :class="cn('-mx-1 my-1 h-px bg-border', props.class)" />
</template> </template>

View File

@ -1,9 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script> </script>
<template> <template>
<span :class="cn('ml-auto text-xs tracking-widest text-muted-foreground', $attrs.class ?? '')"> <span :class="cn('ml-auto text-xs tracking-widest text-muted-foreground', props.class)">
<slot /> <slot />
</span> </span>
</template> </template>

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
ContextMenuSubContent, ContextMenuSubContent,
type DropdownMenuSubContentEmits, type DropdownMenuSubContentEmits,
@ -7,10 +8,16 @@ import {
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuSubContentProps & { class?: string }>() const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<DropdownMenuSubContentEmits>() const emits = defineEmits<DropdownMenuSubContentEmits>()
const forwarded = useForwardPropsEmits(props, emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>

View File

@ -1,24 +1,32 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
ContextMenuSubTrigger, ContextMenuSubTrigger,
type ContextMenuSubTriggerProps, type ContextMenuSubTriggerProps,
useForwardProps,
} from 'radix-vue' } from 'radix-vue'
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?: string; inset?: boolean }>() const props = defineProps<ContextMenuSubTriggerProps & { class?: HTMLAttributes['class']; inset?: boolean }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script> </script>
<template> <template>
<ContextMenuSubTrigger <ContextMenuSubTrigger
v-bind="props" v-bind="forwardedProps"
:class="[ :class="cn(
cn( 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground', inset && 'pl-8',
inset && 'pl-8', props.class,
props.class, )"
),
]"
> >
<slot /> <slot />
<ChevronRight class="ml-auto h-4 w-4" /> <ChevronRight class="ml-auto h-4 w-4" />

View File

@ -1,11 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { ContextMenuTrigger, type ContextMenuTriggerProps } from 'radix-vue' import { ContextMenuTrigger, type ContextMenuTriggerProps, useForwardProps } from 'radix-vue'
const props = defineProps<ContextMenuTriggerProps>() const props = defineProps<ContextMenuTriggerProps>()
const forwardedProps = useForwardProps(props)
</script> </script>
<template> <template>
<ContextMenuTrigger v-bind="props"> <ContextMenuTrigger v-bind="forwardedProps">
<slot /> <slot />
</ContextMenuTrigger> </ContextMenuTrigger>
</template> </template>

View File

@ -1,14 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { DialogRoot, type DialogRootEmits, type DialogRootProps, useForwardPropsEmits } from 'radix-vue' import { DialogRoot, type DialogRootEmits, type DialogRootProps, useForwardPropsEmits } from 'radix-vue'
const props = defineProps<DialogRootProps>() const props = defineProps<DialogRootProps>()
const emits = defineEmits<DialogRootEmits>() const emits = defineEmits<DialogRootEmits>()
const forwarded = useForwardPropsEmits(props, emits) const forwarded = useForwardPropsEmits(props, emits)
</script> </script>
<template> <template>
<DialogRoot v-bind="forwarded"> <DialogRoot v-bind="forwarded">
<slot /> <slot />
</DialogRoot> </DialogRoot>
</template> </template>

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
DialogClose, DialogClose,
DialogContent, DialogContent,
@ -6,35 +7,40 @@ import {
type DialogContentProps, type DialogContentProps,
DialogOverlay, DialogOverlay,
DialogPortal, DialogPortal,
useEmitAsProps, useForwardPropsEmits,
} from 'radix-vue' } from 'radix-vue'
import { X } from 'lucide-vue-next' import { X } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DialogContentProps & { class?: string }>() const props = defineProps<DialogContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<DialogContentEmits>() const emits = defineEmits<DialogContentEmits>()
const emitsAsProps = useEmitAsProps(emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<DialogPortal> <DialogPortal>
<DialogOverlay <DialogOverlay
class="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/> />
<DialogContent <DialogContent
v-bind="forwarded"
:class=" :class="
cn( cn(
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full', 'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
props.class, props.class,
) )"
"
v-bind="{ ...props, ...emitsAsProps }"
> >
<slot /> <slot />
<DialogClose <DialogClose
class="absolute top-3 right-3 p-0.5 transition-colors rounded-md hover:bg-secondary" class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
> >
<X class="w-4 h-4" /> <X class="w-4 h-4" />
<span class="sr-only">Close</span> <span class="sr-only">Close</span>

View File

@ -1,14 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { DialogDescription, type DialogDescriptionProps } from 'radix-vue' import { type HTMLAttributes, computed } from 'vue'
import { DialogDescription, type DialogDescriptionProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DialogDescriptionProps & { class?: string }>() const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script> </script>
<template> <template>
<DialogDescription <DialogDescription
v-bind="props" v-bind="forwardedProps"
:class="cn('text-muted-foreground text-sm', props.class)" :class="cn('text-sm text-muted-foreground', props.class)"
> >
<slot /> <slot />
</DialogDescription> </DialogDescription>

View File

@ -1,18 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
interface DialogFooterProps { const props = defineProps<{ class?: HTMLAttributes['class'] }>()
class?: string
}
const props = defineProps<DialogFooterProps>()
</script> </script>
<template> <template>
<div <div
:class=" :class="
cn( cn(
'flex flex-col space-y-2 sm:space-y-0 mt-1.5 sm:flex-row sm:justify-end sm:space-x-2', 'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
props.class, props.class,
) )
" "

View File

@ -1,16 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
interface DialogHeaderProps { const props = defineProps<{
class?: string class?: HTMLAttributes['class']
} }>()
const props = defineProps<DialogHeaderProps>()
</script> </script>
<template> <template>
<div <div
:class="cn('flex flex-col space-y-2 text-center sm:text-left', props.class)" :class="cn('flex flex-col gap-y-1.5 text-center sm:text-left', props.class)"
> >
<slot /> <slot />
</div> </div>

View File

@ -1,16 +1,25 @@
<script setup lang="ts"> <script setup lang="ts">
import { DialogTitle, type DialogTitleProps } from 'radix-vue' import { type HTMLAttributes, computed } from 'vue'
import { DialogTitle, type DialogTitleProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DialogTitleProps & { class?: string }>() const props = defineProps<DialogTitleProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script> </script>
<template> <template>
<DialogTitle <DialogTitle
v-bind="props" v-bind="forwardedProps"
:class=" :class="
cn( cn(
'text-lg text-foreground font-semibold leading-none tracking-tight', 'text-lg font-semibold leading-none tracking-tight',
props.class, props.class,
) )
" "

View File

@ -1,21 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
DropdownMenuCheckboxItem, DropdownMenuCheckboxItem,
type DropdownMenuCheckboxItemEmits, type DropdownMenuCheckboxItemEmits,
type DropdownMenuCheckboxItemProps, type DropdownMenuCheckboxItemProps,
DropdownMenuItemIndicator, DropdownMenuItemIndicator,
useEmitAsProps, useForwardPropsEmits,
} from 'radix-vue' } from 'radix-vue'
import { Check } from 'lucide-vue-next' import { Check } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuCheckboxItemProps & { class?: string }>() const props = defineProps<DropdownMenuCheckboxItemProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<DropdownMenuCheckboxItemEmits>() const emits = defineEmits<DropdownMenuCheckboxItemEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
v-bind="{ ...props, ...useEmitAsProps(emits) }" v-bind="forwarded"
:class=" cn( :class=" cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
props.class, props.class,

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
DropdownMenuContent, DropdownMenuContent,
type DropdownMenuContentEmits, type DropdownMenuContentEmits,
@ -9,26 +10,27 @@ import {
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = withDefaults( const props = withDefaults(
defineProps<DropdownMenuContentProps & { class?: string }>(), defineProps<DropdownMenuContentProps & { class?: HTMLAttributes['class'] }>(),
{ {
sideOffset: 4, sideOffset: 4,
}, },
) )
const emits = defineEmits<DropdownMenuContentEmits>() const emits = defineEmits<DropdownMenuContentEmits>()
const forwarded = useForwardPropsEmits(props, emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<DropdownMenuPortal> <DropdownMenuPortal>
<DropdownMenuContent <DropdownMenuContent
:class="[
cn(
'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
),
]"
v-bind="forwarded" v-bind="forwarded"
:class="cn('z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', props.class)"
> >
<slot /> <slot />
</DropdownMenuContent> </DropdownMenuContent>

View File

@ -1,20 +1,27 @@
<script setup lang="ts"> <script setup lang="ts">
import { DropdownMenuItem, type DropdownMenuItemProps } from 'radix-vue' import { type HTMLAttributes, computed } from 'vue'
import { DropdownMenuItem, type DropdownMenuItemProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuItemProps & { inset?: boolean; class?: string }>() const props = defineProps<DropdownMenuItemProps & { class?: HTMLAttributes['class']; inset?: boolean }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script> </script>
<template> <template>
<DropdownMenuItem <DropdownMenuItem
v-bind="props" v-bind="forwardedProps"
:class="[ :class="cn(
cn( 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', inset && 'pl-8',
inset && 'pl-8', props.class,
props.class, )"
),
]"
> >
<slot /> <slot />
</DropdownMenuItem> </DropdownMenuItem>

View File

@ -1,19 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { DropdownMenuLabel, type DropdownMenuLabelProps } from 'radix-vue' import { type HTMLAttributes, computed } from 'vue'
import { DropdownMenuLabel, type DropdownMenuLabelProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuLabelProps & { const props = defineProps<DropdownMenuLabelProps & { class?: HTMLAttributes['class']; inset?: boolean }>()
inset?: boolean
class?: string const delegatedProps = computed(() => {
}>() const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script> </script>
<template> <template>
<DropdownMenuLabel <DropdownMenuLabel
v-bind="props" v-bind="forwardedProps"
:class=" :class="cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', props.class)"
cn('px-2 py-1.5 text-sm font-semibold',
inset && 'pl-8', props.class)"
> >
<slot /> <slot />
</DropdownMenuLabel> </DropdownMenuLabel>

View File

@ -3,18 +3,17 @@ import {
DropdownMenuRadioGroup, DropdownMenuRadioGroup,
type DropdownMenuRadioGroupEmits, type DropdownMenuRadioGroupEmits,
type DropdownMenuRadioGroupProps, type DropdownMenuRadioGroupProps,
useForwardPropsEmits,
} from 'radix-vue' } from 'radix-vue'
const props = defineProps<DropdownMenuRadioGroupProps>() const props = defineProps<DropdownMenuRadioGroupProps>()
const emits = defineEmits<DropdownMenuRadioGroupEmits>() const emits = defineEmits<DropdownMenuRadioGroupEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script> </script>
<template> <template>
<DropdownMenuRadioGroup <DropdownMenuRadioGroup v-bind="forwarded">
v-bind="props"
@update:model-value="emits('update:modelValue', $event)"
>
<slot /> <slot />
</DropdownMenuRadioGroup> </DropdownMenuRadioGroup>
</template> </template>

View File

@ -1,29 +1,37 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
DropdownMenuItemIndicator, DropdownMenuItemIndicator,
DropdownMenuRadioItem, DropdownMenuRadioItem,
type DropdownMenuRadioItemEmits, type DropdownMenuRadioItemEmits,
type DropdownMenuRadioItemProps, type DropdownMenuRadioItemProps,
useEmitAsProps, useForwardPropsEmits,
} from 'radix-vue' } from 'radix-vue'
import { Circle } from 'lucide-vue-next' import { Circle } from 'lucide-vue-next'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuRadioItemProps & { class?: string }>() const props = defineProps<DropdownMenuRadioItemProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<DropdownMenuRadioItemEmits>() const emits = defineEmits<DropdownMenuRadioItemEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<DropdownMenuRadioItem <DropdownMenuRadioItem
v-bind="{ ...props, ...useEmitAsProps(emits) }" v-bind="forwarded"
:class="cn( :class="cn(
'flex relative items-center rounded-md transition-colors data-[disabled]:opacity-50 data-[disabled]:pointer-events-none data-[highlighted]:bg-outline-hover pl-7 py-1.5 text-sm outline-none select-none cursor-default', 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
props.class, props.class,
)" )"
> >
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuItemIndicator> <DropdownMenuItemIndicator>
<Circle class="h-2 w-2 fill-current" /> <Circle class="h-2 w-2 fill-current" />
</DropdownMenuItemIndicator> </DropdownMenuItemIndicator>

View File

@ -1,12 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
DropdownMenuSeparator, DropdownMenuSeparator,
type DropdownMenuSeparatorProps, type DropdownMenuSeparatorProps,
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuSeparatorProps>() const props = defineProps<DropdownMenuSeparatorProps & {
class?: HTMLAttributes['class']
}>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script> </script>
<template> <template>
<DropdownMenuSeparator v-bind="props" class="-mx-1 my-1 h-px bg-muted" /> <DropdownMenuSeparator v-bind="delegatedProps" :class="cn('-mx-1 my-1 h-px bg-muted', props.class)" />
</template> </template>

View File

@ -1,9 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script> </script>
<template> <template>
<span :class="cn('ml-auto text-xs tracking-widest opacity-60', $attrs.class ?? '')"> <span :class="cn('ml-auto text-xs tracking-widest opacity-60', props.class)">
<slot /> <slot />
</span> </span>
</template> </template>

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
DropdownMenuSubContent, DropdownMenuSubContent,
type DropdownMenuSubContentEmits, type DropdownMenuSubContentEmits,
@ -7,16 +8,22 @@ import {
} from 'radix-vue' } from 'radix-vue'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const props = defineProps<DropdownMenuSubContentProps>() const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<DropdownMenuSubContentEmits>() const emits = defineEmits<DropdownMenuSubContentEmits>()
const forwarded = useForwardPropsEmits(props, emits) const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script> </script>
<template> <template>
<DropdownMenuSubContent <DropdownMenuSubContent
v-bind="forwarded" v-bind="forwarded"
:class="cn('z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', $attrs.class ?? '')" :class="cn('z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', props.class)"
> >
<slot /> <slot />
</DropdownMenuSubContent> </DropdownMenuSubContent>

View File

@ -1,23 +1,31 @@
<script setup lang="ts"> <script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { import {
DropdownMenuSubTrigger, DropdownMenuSubTrigger,
type DropdownMenuSubTriggerProps, type DropdownMenuSubTriggerProps,
useForwardProps,
} from 'radix-vue' } from 'radix-vue'
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<DropdownMenuSubTriggerProps & { class?: string }>() const props = defineProps<DropdownMenuSubTriggerProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script> </script>
<template> <template>
<DropdownMenuSubTrigger <DropdownMenuSubTrigger
v-bind="props" v-bind="forwardedProps"
:class="[ :class="cn(
cn( 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent', props.class,
props.class, )"
),
]"
> >
<slot /> <slot />
<ChevronRight class="ml-auto h-4 w-4" /> <ChevronRight class="ml-auto h-4 w-4" />

View File

@ -1,11 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { DropdownMenuTrigger, type DropdownMenuTriggerProps } from 'radix-vue' import { DropdownMenuTrigger, type DropdownMenuTriggerProps, useForwardProps } from 'radix-vue'
const props = defineProps<DropdownMenuTriggerProps>() const props = defineProps<DropdownMenuTriggerProps>()
const forwardedProps = useForwardProps(props)
</script> </script>
<template> <template>
<DropdownMenuTrigger class="outline-none" v-bind="props"> <DropdownMenuTrigger class="outline-none" v-bind="forwardedProps">
<slot /> <slot />
</DropdownMenuTrigger> </DropdownMenuTrigger>
</template> </template>

View File

@ -1,21 +1,19 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useAttrs } from 'vue' import type { HTMLAttributes } from 'vue'
import { useFormField } from './useFormField' import { useFormField } from './useFormField'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
defineOptions({ const props = defineProps<{
inheritAttrs: false, class?: HTMLAttributes['class']
}) }>()
const { formDescriptionId } = useFormField() const { formDescriptionId } = useFormField()
const { class: className, ...rest } = useAttrs()
</script> </script>
<template> <template>
<p <p
:id="formDescriptionId" :id="formDescriptionId"
:class="cn('text-sm text-muted-foreground', className ?? '')" :class="cn('text-sm text-muted-foreground', props.class)"
v-bind="rest"
> >
<slot /> <slot />
</p> </p>

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