refactor(Command, Combobox): adapt to respective component

This commit is contained in:
zernonia 2024-12-11 15:05:55 +08:00
parent d7c4f34bab
commit 18040192c8
56 changed files with 2911 additions and 282 deletions

View File

@ -1,11 +1,11 @@
import type { RegistryStyle } from '@/registry/registry-styles' import type { RegistryStyle } from '@/registry/registry-styles'
import sdk from '@stackblitz/sdk' import sdk from '@stackblitz/sdk'
import { getParameters } from 'codesandbox/lib/api/define' import { getParameters } from 'codesandbox/lib/api/define'
// @ts-expect-error ?raw
import cssRaw from '../../../../../packages/cli/test/fixtures/frameworks/nuxt/assets/css/tailwind.css?raw'
import { Index as demoIndex } from '../../../../www/__registry__' import { Index as demoIndex } from '../../../../www/__registry__'
// @ts-expect-error ?raw // @ts-expect-error ?raw
import tailwindConfigRaw from '../../../tailwind.config?raw' import tailwindConfigRaw from '../../../tailwind.config?raw'
// @ts-expect-error ?raw
import cssRaw from '../../../../../packages/cli/test/fixtures/frameworks/nuxt/assets/css/tailwind.css?raw'
export function makeCodeSandboxParams(componentName: string, style: RegistryStyle, sources: Record<string, string>) { export function makeCodeSandboxParams(componentName: string, style: RegistryStyle, sources: Record<string, string>) {
let files: Record<string, any> = {} let files: Record<string, any> = {}
@ -132,7 +132,7 @@ function constructFiles(componentName: string, style: RegistryStyle, sources: Re
}) })
// @ts-expect-error componentName might not exist in Index // @ts-expect-error componentName might not exist in Index
const registryDependencies = demoIndex[style][componentName as any]?.registryDependencies?.filter(i => i !== 'utils') const registryDependencies = demoIndex[style][componentName as any]?.registryDependencies?.filter(i => i !== 'utils') ?? []
const files = { const files = {
'package.json': { 'package.json': {

View File

@ -632,6 +632,57 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"combobox": {
name: "combobox",
description: "",
type: "registry:ui",
registryDependencies: ["utils"],
files: [{
path: "registry/new-york/ui/combobox/Combobox.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxAnchor.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxEmpty.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxGroup.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxInput.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxItem.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxList.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxSeparator.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxTrigger.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/index.ts",
type: "registry:ui",
target: ""
}],
component: () => import("@/registry/new-york/ui/combobox/Combobox.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"command": { "command": {
name: "command", name: "command",
description: "", description: "",
@ -2985,7 +3036,7 @@ export const Index: Record<string, any> = {
name: "ComboboxDemo", name: "ComboboxDemo",
description: "", description: "",
type: "registry:example", type: "registry:example",
registryDependencies: ["utils","button","command","popover"], registryDependencies: ["utils","combobox"],
files: [{ files: [{
path: "registry/new-york/example/ComboboxDemo.vue", path: "registry/new-york/example/ComboboxDemo.vue",
type: "registry:example", type: "registry:example",
@ -3015,7 +3066,7 @@ export const Index: Record<string, any> = {
name: "ComboboxForm", name: "ComboboxForm",
description: "", description: "",
type: "registry:example", type: "registry:example",
registryDependencies: ["utils","button","command","form","popover","toast"], registryDependencies: ["utils","button","combobox","form","toast"],
files: [{ files: [{
path: "registry/new-york/example/ComboboxForm.vue", path: "registry/new-york/example/ComboboxForm.vue",
type: "registry:example", type: "registry:example",
@ -3056,6 +3107,21 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"ComboboxTrigger": {
name: "ComboboxTrigger",
description: "",
type: "registry:example",
registryDependencies: ["utils","button","combobox"],
files: [{
path: "registry/new-york/example/ComboboxTrigger.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/ComboboxTrigger.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandDemo": { "CommandDemo": {
name: "CommandDemo", name: "CommandDemo",
description: "", description: "",
@ -3086,6 +3152,66 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"CommandDropdownMenu": {
name: "CommandDropdownMenu",
description: "",
type: "registry:example",
registryDependencies: ["button","command","dropdown-menu"],
files: [{
path: "registry/new-york/example/CommandDropdownMenu.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/CommandDropdownMenu.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandForm": {
name: "CommandForm",
description: "",
type: "registry:example",
registryDependencies: ["utils","button","command","form","popover","toast"],
files: [{
path: "registry/new-york/example/CommandForm.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/CommandForm.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandPopover": {
name: "CommandPopover",
description: "",
type: "registry:example",
registryDependencies: ["button","command","popover"],
files: [{
path: "registry/new-york/example/CommandPopover.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/CommandPopover.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandResponsive": {
name: "CommandResponsive",
description: "",
type: "registry:example",
registryDependencies: ["button","command","drawer","popover"],
files: [{
path: "registry/new-york/example/CommandResponsive.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/CommandResponsive.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"ContextMenuDemo": { "ContextMenuDemo": {
name: "ContextMenuDemo", name: "ContextMenuDemo",
description: "", description: "",
@ -6352,6 +6478,57 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"combobox": {
name: "combobox",
description: "",
type: "registry:ui",
registryDependencies: ["utils"],
files: [{
path: "registry/new-york/ui/combobox/Combobox.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxAnchor.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxEmpty.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxGroup.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxInput.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxItem.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxList.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxSeparator.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/ComboboxTrigger.vue",
type: "registry:ui",
target: ""
},{
path: "registry/new-york/ui/combobox/index.ts",
type: "registry:ui",
target: ""
}],
component: () => import("@/registry/new-york/ui/combobox/Combobox.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"command": { "command": {
name: "command", name: "command",
description: "", description: "",
@ -8705,7 +8882,7 @@ export const Index: Record<string, any> = {
name: "ComboboxDemo", name: "ComboboxDemo",
description: "", description: "",
type: "registry:example", type: "registry:example",
registryDependencies: ["utils","button","command","popover"], registryDependencies: ["utils","combobox"],
files: [{ files: [{
path: "registry/new-york/example/ComboboxDemo.vue", path: "registry/new-york/example/ComboboxDemo.vue",
type: "registry:example", type: "registry:example",
@ -8735,7 +8912,7 @@ export const Index: Record<string, any> = {
name: "ComboboxForm", name: "ComboboxForm",
description: "", description: "",
type: "registry:example", type: "registry:example",
registryDependencies: ["utils","button","command","form","popover","toast"], registryDependencies: ["utils","button","combobox","form","toast"],
files: [{ files: [{
path: "registry/new-york/example/ComboboxForm.vue", path: "registry/new-york/example/ComboboxForm.vue",
type: "registry:example", type: "registry:example",
@ -8776,6 +8953,21 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"ComboboxTrigger": {
name: "ComboboxTrigger",
description: "",
type: "registry:example",
registryDependencies: ["utils","button","combobox"],
files: [{
path: "registry/new-york/example/ComboboxTrigger.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/ComboboxTrigger.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandDemo": { "CommandDemo": {
name: "CommandDemo", name: "CommandDemo",
description: "", description: "",
@ -8806,6 +8998,66 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"CommandDropdownMenu": {
name: "CommandDropdownMenu",
description: "",
type: "registry:example",
registryDependencies: ["button","command","dropdown-menu"],
files: [{
path: "registry/new-york/example/CommandDropdownMenu.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/CommandDropdownMenu.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandForm": {
name: "CommandForm",
description: "",
type: "registry:example",
registryDependencies: ["utils","button","command","form","popover","toast"],
files: [{
path: "registry/new-york/example/CommandForm.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/CommandForm.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandPopover": {
name: "CommandPopover",
description: "",
type: "registry:example",
registryDependencies: ["button","command","popover"],
files: [{
path: "registry/new-york/example/CommandPopover.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/CommandPopover.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandResponsive": {
name: "CommandResponsive",
description: "",
type: "registry:example",
registryDependencies: ["button","command","drawer","popover"],
files: [{
path: "registry/new-york/example/CommandResponsive.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/new-york/example/CommandResponsive.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"ContextMenuDemo": { "ContextMenuDemo": {
name: "ContextMenuDemo", name: "ContextMenuDemo",
description: "", description: "",
@ -12088,6 +12340,57 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"combobox": {
name: "combobox",
description: "",
type: "registry:ui",
registryDependencies: ["utils"],
files: [{
path: "registry/default/ui/combobox/Combobox.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxAnchor.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxEmpty.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxGroup.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxInput.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxItem.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxList.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxSeparator.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxTrigger.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/index.ts",
type: "registry:ui",
target: ""
}],
component: () => import("@/registry/default/ui/combobox/Combobox.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"command": { "command": {
name: "command", name: "command",
description: "", description: "",
@ -14441,7 +14744,7 @@ export const Index: Record<string, any> = {
name: "ComboboxDemo", name: "ComboboxDemo",
description: "", description: "",
type: "registry:example", type: "registry:example",
registryDependencies: ["utils","button","command","popover"], registryDependencies: ["utils","combobox"],
files: [{ files: [{
path: "registry/default/example/ComboboxDemo.vue", path: "registry/default/example/ComboboxDemo.vue",
type: "registry:example", type: "registry:example",
@ -14471,7 +14774,7 @@ export const Index: Record<string, any> = {
name: "ComboboxForm", name: "ComboboxForm",
description: "", description: "",
type: "registry:example", type: "registry:example",
registryDependencies: ["utils","button","command","form","popover","toast"], registryDependencies: ["utils","button","combobox","form","toast"],
files: [{ files: [{
path: "registry/default/example/ComboboxForm.vue", path: "registry/default/example/ComboboxForm.vue",
type: "registry:example", type: "registry:example",
@ -14512,6 +14815,21 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"ComboboxTrigger": {
name: "ComboboxTrigger",
description: "",
type: "registry:example",
registryDependencies: ["utils","button","combobox"],
files: [{
path: "registry/default/example/ComboboxTrigger.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/ComboboxTrigger.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandDemo": { "CommandDemo": {
name: "CommandDemo", name: "CommandDemo",
description: "", description: "",
@ -14542,6 +14860,66 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"CommandDropdownMenu": {
name: "CommandDropdownMenu",
description: "",
type: "registry:example",
registryDependencies: ["button","command","dropdown-menu"],
files: [{
path: "registry/default/example/CommandDropdownMenu.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/CommandDropdownMenu.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandForm": {
name: "CommandForm",
description: "",
type: "registry:example",
registryDependencies: ["utils","button","command","form","popover","toast"],
files: [{
path: "registry/default/example/CommandForm.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/CommandForm.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandPopover": {
name: "CommandPopover",
description: "",
type: "registry:example",
registryDependencies: ["button","command","popover"],
files: [{
path: "registry/default/example/CommandPopover.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/CommandPopover.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandResponsive": {
name: "CommandResponsive",
description: "",
type: "registry:example",
registryDependencies: ["button","command","drawer","popover"],
files: [{
path: "registry/default/example/CommandResponsive.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/CommandResponsive.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"ContextMenuDemo": { "ContextMenuDemo": {
name: "ContextMenuDemo", name: "ContextMenuDemo",
description: "", description: "",
@ -17808,6 +18186,57 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"combobox": {
name: "combobox",
description: "",
type: "registry:ui",
registryDependencies: ["utils"],
files: [{
path: "registry/default/ui/combobox/Combobox.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxAnchor.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxEmpty.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxGroup.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxInput.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxItem.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxList.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxSeparator.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/ComboboxTrigger.vue",
type: "registry:ui",
target: ""
},{
path: "registry/default/ui/combobox/index.ts",
type: "registry:ui",
target: ""
}],
component: () => import("@/registry/default/ui/combobox/Combobox.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"command": { "command": {
name: "command", name: "command",
description: "", description: "",
@ -20161,7 +20590,7 @@ export const Index: Record<string, any> = {
name: "ComboboxDemo", name: "ComboboxDemo",
description: "", description: "",
type: "registry:example", type: "registry:example",
registryDependencies: ["utils","button","command","popover"], registryDependencies: ["utils","combobox"],
files: [{ files: [{
path: "registry/default/example/ComboboxDemo.vue", path: "registry/default/example/ComboboxDemo.vue",
type: "registry:example", type: "registry:example",
@ -20191,7 +20620,7 @@ export const Index: Record<string, any> = {
name: "ComboboxForm", name: "ComboboxForm",
description: "", description: "",
type: "registry:example", type: "registry:example",
registryDependencies: ["utils","button","command","form","popover","toast"], registryDependencies: ["utils","button","combobox","form","toast"],
files: [{ files: [{
path: "registry/default/example/ComboboxForm.vue", path: "registry/default/example/ComboboxForm.vue",
type: "registry:example", type: "registry:example",
@ -20232,6 +20661,21 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"ComboboxTrigger": {
name: "ComboboxTrigger",
description: "",
type: "registry:example",
registryDependencies: ["utils","button","combobox"],
files: [{
path: "registry/default/example/ComboboxTrigger.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/ComboboxTrigger.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandDemo": { "CommandDemo": {
name: "CommandDemo", name: "CommandDemo",
description: "", description: "",
@ -20262,6 +20706,66 @@ export const Index: Record<string, any> = {
category: "", category: "",
subcategory: "" subcategory: ""
}, },
"CommandDropdownMenu": {
name: "CommandDropdownMenu",
description: "",
type: "registry:example",
registryDependencies: ["button","command","dropdown-menu"],
files: [{
path: "registry/default/example/CommandDropdownMenu.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/CommandDropdownMenu.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandForm": {
name: "CommandForm",
description: "",
type: "registry:example",
registryDependencies: ["utils","button","command","form","popover","toast"],
files: [{
path: "registry/default/example/CommandForm.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/CommandForm.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandPopover": {
name: "CommandPopover",
description: "",
type: "registry:example",
registryDependencies: ["button","command","popover"],
files: [{
path: "registry/default/example/CommandPopover.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/CommandPopover.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"CommandResponsive": {
name: "CommandResponsive",
description: "",
type: "registry:example",
registryDependencies: ["button","command","drawer","popover"],
files: [{
path: "registry/default/example/CommandResponsive.vue",
type: "registry:example",
target: ""
}],
component: () => import("@/registry/default/example/CommandResponsive.vue").then((m) => m.default),
source: "",
category: "",
subcategory: ""
},
"ContextMenuDemo": { "ContextMenuDemo": {
name: "ContextMenuDemo", name: "ContextMenuDemo",
description: "", description: "",

View File

@ -1,6 +1,8 @@
--- ---
title: Combobox title: Combobox
description: Autocomplete input and command palette with a list of suggestions. description: Autocomplete input and command palette with a list of suggestions.
source: apps/www/registry/default/ui/combobox
primitive: https://www.reka-ui.com/docs/components/combobox.html
--- ---
<ComponentPreview name="ComboboxDemo" /> <ComponentPreview name="ComboboxDemo" />
@ -9,9 +11,9 @@ description: Autocomplete input and command palette with a list of suggestions.
## Installation ## Installation
The Combobox is built using a composition of the `<Popover />` and the `<Command />` components. ```bash
npx shadcn-vue@latest add combobox
See installation instructions for the [Popover](/docs/components/popover#installation) and the [Command](/docs/components/command#installation) components. ```
## Usage ## Usage
@ -92,11 +94,11 @@ const value = ref('')
## Examples ## Examples
### Combobox ### Combobox Trigger
<ComponentPreview name="ComboboxDemo" /> <ComponentPreview name="ComboboxTrigger" />
### Popover <!-- ### Popover
<ComponentPreview name="ComboboxPopover" /> <ComponentPreview name="ComboboxPopover" />
@ -108,7 +110,7 @@ const value = ref('')
You can create a responsive combobox by using the `<Popover />` on desktop and the `<Drawer />` components on mobile. You can create a responsive combobox by using the `<Popover />` on desktop and the `<Drawer />` components on mobile.
<ComponentPreview name="ComboboxResponsive" /> <ComponentPreview name="ComboboxResponsive" /> -->
### Form ### Form

View File

@ -2,7 +2,7 @@
title: Command title: Command
description: Fast, composable, unstyled command menu. description: Fast, composable, unstyled command menu.
source: apps/www/registry/default/ui/command source: apps/www/registry/default/ui/command
primitive: https://www.reka-ui.com/docs/components/combobox.html primitive: https://www.reka-ui.com/docs/components/listbox.html
--- ---
<ComponentPreview name="CommandDemo" /> <ComponentPreview name="CommandDemo" />
@ -134,6 +134,28 @@ watch(CmdJ, (v) => {
</template> </template>
``` ```
### Combobox <br></br>
You can use the `<Command />` component as a combobox. See the [Combobox](/docs/components/combobox) page for more information. <Callout>
You can use the `<Command />` component like a combobox.
</Callout>
### Popover
<ComponentPreview name="CommandPopover" />
### Dropdown menu
<ComponentPreview name="CommandDropdownMenu" />
### Responsive
You can create a responsive combobox by using the `<Popover />` on desktop and the `<Drawer />` components on mobile.
<ComponentPreview name="CommandResponsive" />
### Form
<ComponentPreview name="CommandForm" />

View File

@ -656,6 +656,58 @@
} }
] ]
}, },
{
"name": "combobox",
"type": "registry:ui",
"dependencies": [
"reka-ui"
],
"registryDependencies": [
"utils"
],
"files": [
{
"path": "ui/combobox/Combobox.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxAnchor.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxEmpty.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxGroup.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxInput.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxItem.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxList.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxSeparator.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxTrigger.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/index.ts",
"type": "registry:ui"
}
]
},
{ {
"name": "command", "name": "command",
"type": "registry:ui", "type": "registry:ui",
@ -2780,6 +2832,58 @@
} }
] ]
}, },
{
"name": "combobox",
"type": "registry:ui",
"dependencies": [
"reka-ui"
],
"registryDependencies": [
"utils"
],
"files": [
{
"path": "ui/combobox/Combobox.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxAnchor.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxEmpty.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxGroup.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxInput.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxItem.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxList.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxSeparator.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/ComboboxTrigger.vue",
"type": "registry:ui"
},
{
"path": "ui/combobox/index.ts",
"type": "registry:ui"
}
]
},
{ {
"name": "command", "name": "command",
"type": "registry:ui", "type": "registry:ui",

View File

@ -4,14 +4,12 @@
"dependencies": [], "dependencies": [],
"registryDependencies": [ "registryDependencies": [
"utils", "utils",
"button", "combobox"
"command",
"popover"
], ],
"files": [ "files": [
{ {
"path": "example/ComboboxDemo.vue", "path": "example/ComboboxDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/default/ui/popover'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n\nconst open = ref(false)\nconst value = ref('')\n</script>\n\n<template>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n class=\"w-[200px] justify-between\"\n >\n {{ value\n ? frameworks.find((framework) => framework.value === value)?.label\n : \"Select framework...\" }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput class=\"h-9\" placeholder=\"Search framework...\" />\n <CommandEmpty>No framework found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework.value\"\n @select=\"(ev) => {\n if (typeof ev.detail.value === 'string') {\n value = ev.detail.value\n }\n open = false\n }\"\n >\n {{ framework.label }}\n <Check\n :class=\"cn(\n 'ml-auto h-4 w-4',\n value === framework.value ? 'opacity-100' : 'opacity-0',\n )\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n</template>\n", "content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from '@/registry/default/ui/combobox'\nimport { Check, Search } from 'lucide-vue-next'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n</script>\n\n<template>\n <Combobox by=\"label\">\n <ComboboxAnchor>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput class=\"pl-9\" :display-value=\"(val) => val?.label ?? ''\" placeholder=\"Select framework...\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-3\">\n <Search class=\"size-4 text-muted-foreground\" />\n </span>\n </div>\n </ComboboxAnchor>\n\n <ComboboxList>\n <ComboboxEmpty>\n No framework found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework\"\n >\n {{ framework.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n",
"type": "registry:example", "type": "registry:example",
"target": "" "target": ""
} }

View File

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

View File

@ -0,0 +1,18 @@
{
"name": "ComboboxTrigger",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"utils",
"button",
"combobox"
],
"files": [
{
"path": "example/ComboboxTrigger.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/default/ui/combobox'\nimport { Check, ChevronsUpDown, Search } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n\nconst value = ref<typeof frameworks[0]>()\n</script>\n\n<template>\n <Combobox v-model=\"value\" by=\"label\">\n <ComboboxAnchor as-child>\n <ComboboxTrigger as-child>\n <Button variant=\"outline\" class=\"justify-between\">\n {{ value?.label ?? 'Select framework' }}\n\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </ComboboxTrigger>\n </ComboboxAnchor>\n\n <ComboboxList>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput class=\"pl-9 focus-visible:ring-0 border-0 border-b rounded-none h-10\" placeholder=\"Select framework...\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-3\">\n <Search class=\"size-4 text-muted-foreground\" />\n </span>\n </div>\n\n <ComboboxEmpty>\n No framework found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework\"\n >\n {{ framework.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
}

View File

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

View File

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

View File

@ -0,0 +1,18 @@
{
"name": "CommandPopover",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"button",
"command",
"popover"
],
"files": [
{
"path": "example/CommandPopover.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/default/ui/popover'\nimport { ref } from 'vue'\n\ninterface Status {\n value: string\n label: string\n}\n\nconst statuses: Status[] = [\n {\n value: 'backlog',\n label: 'Backlog',\n },\n {\n value: 'todo',\n label: 'Todo',\n },\n {\n value: 'in progress',\n label: 'In Progress',\n },\n {\n value: 'done',\n label: 'Done',\n },\n {\n value: 'canceled',\n label: 'Canceled',\n },\n]\n\nconst open = ref(false)\nconst selectedStatus = ref<Status>()\n</script>\n\n<template>\n <div class=\"flex items-center space-x-4\">\n <p class=\"text-sm text-muted-foreground\">\n Status\n </p>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n size=\"sm\"\n class=\"w-[150px] justify-start\"\n >\n <template v-if=\"selectedStatus\">\n {{ selectedStatus?.label }}\n </template>\n <template v-else>\n + Set status\n </template>\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"p-0\" side=\"right\" align=\"start\">\n <Command>\n <CommandInput placeholder=\"Change status...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"status in statuses\"\n :key=\"status.value\"\n :value=\"status.value\"\n @select=\"() => {\n selectedStatus = status\n open = false\n }\"\n >\n {{ status.label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n </div>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
}

View File

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

View File

@ -0,0 +1,72 @@
{
"name": "combobox",
"type": "registry:ui",
"dependencies": [
"reka-ui"
],
"registryDependencies": [
"utils"
],
"files": [
{
"path": "ui/combobox/Combobox.vue",
"content": "<script setup lang=\"ts\">\nimport { ComboboxRoot, type ComboboxRootEmits, type ComboboxRootProps, useForwardPropsEmits } from 'reka-ui'\n\nconst props = defineProps<ComboboxRootProps>()\nconst emits = defineEmits<ComboboxRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <ComboboxRoot v-bind=\"forwarded\">\n <slot />\n </ComboboxRoot>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxAnchor.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxAnchorProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxAnchor, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxAnchorProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxAnchor\n v-bind=\"forwarded\"\n :class=\"cn('w-[200px]', props.class)\"\n >\n <slot />\n </ComboboxAnchor>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxEmpty.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxEmptyProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxEmpty } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxEmptyProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxEmpty v-bind=\"delegatedProps\" :class=\"cn('py-6 text-center text-sm', props.class)\">\n <slot />\n </ComboboxEmpty>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxGroup.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxGroupProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxGroup, ComboboxLabel } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxGroupProps & {\n class?: HTMLAttributes['class']\n heading?: string\n}>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxGroup\n v-bind=\"delegatedProps\"\n :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)\"\n >\n <ComboboxLabel v-if=\"heading\" class=\"px-2 py-1.5 text-xs font-medium text-muted-foreground\">\n {{ heading }}\n </ComboboxLabel>\n <slot />\n </ComboboxGroup>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxInput.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { ComboboxInput, type ComboboxInputProps, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxInputProps & {\n class?: HTMLAttributes['class']\n}>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxInput\n v-bind=\"forwardedProps\"\n :class=\"cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)\"\n >\n <slot />\n </ComboboxInput>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxItem.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxItemEmits, ComboboxItemProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxItem, useForwardPropsEmits } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxItemProps & { class?: HTMLAttributes['class'] }>()\nconst emits = defineEmits<ComboboxItemEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <ComboboxItem\n v-bind=\"forwarded\"\n :class=\"cn('relative flex cursor-default gap-2 select-none justify-between 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 [&_svg]:size-4 [&_svg]:shrink-0', props.class)\"\n >\n <slot />\n </ComboboxItem>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxList.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxContentEmits, ComboboxContentProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxContent, ComboboxPortal, ComboboxViewport, useForwardPropsEmits } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = withDefaults(defineProps<ComboboxContentProps & { class?: HTMLAttributes['class'] }>(), {\n position: 'popper',\n align: 'center',\n sideOffset: 4,\n})\nconst emits = defineEmits<ComboboxContentEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <ComboboxPortal>\n <ComboboxContent\n v-bind=\"forwarded\"\n :class=\"cn('z-50 w-[200px] rounded-md border bg-popover text-popover-foreground shadow-md outline-none 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)\"\n >\n <ComboboxViewport>\n <slot />\n </ComboboxViewport>\n </ComboboxContent>\n </ComboboxPortal>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxSeparator.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxSeparatorProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxSeparator } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxSeparatorProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxSeparator\n v-bind=\"delegatedProps\"\n :class=\"cn('-mx-1 h-px bg-border', props.class)\"\n >\n <slot />\n </ComboboxSeparator>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxTrigger.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxTriggerProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxTrigger, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxTriggerProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxTrigger\n v-bind=\"forwarded\"\n :class=\"cn('', props.class)\"\n tabindex=\"0\"\n >\n <slot />\n </ComboboxTrigger>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/index.ts",
"content": "export { default as Combobox } from './Combobox.vue'\nexport { default as ComboboxAnchor } from './ComboboxAnchor.vue'\nexport { default as ComboboxEmpty } from './ComboboxEmpty.vue'\nexport { default as ComboboxGroup } from './ComboboxGroup.vue'\nexport { default as ComboboxInput } from './ComboboxInput.vue'\nexport { default as ComboboxItem } from './ComboboxItem.vue'\nexport { default as ComboboxList } from './ComboboxList.vue'\nexport { default as ComboboxSeparator } from './ComboboxSeparator.vue'\n\nexport { ComboboxCancel, ComboboxItemIndicator, ComboboxTrigger } from 'reka-ui'\n",
"type": "registry:ui",
"target": ""
}
]
}

View File

@ -4,14 +4,12 @@
"dependencies": [], "dependencies": [],
"registryDependencies": [ "registryDependencies": [
"utils", "utils",
"button", "combobox"
"command",
"popover"
], ],
"files": [ "files": [
{ {
"path": "example/ComboboxDemo.vue", "path": "example/ComboboxDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/new-york/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/new-york/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/new-york/ui/popover'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n\nconst open = ref(false)\nconst value = ref('')\n</script>\n\n<template>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n class=\"w-[200px] justify-between\"\n >\n {{ value\n ? frameworks.find((framework) => framework.value === value)?.label\n : \"Select framework...\" }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput class=\"h-9\" placeholder=\"Search framework...\" />\n <CommandEmpty>No framework found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework.value\"\n @select=\"(ev) => {\n if (typeof ev.detail.value === 'string') {\n value = ev.detail.value\n }\n open = false\n }\"\n >\n {{ framework.label }}\n <Check\n :class=\"cn(\n 'ml-auto h-4 w-4',\n value === framework.value ? 'opacity-100' : 'opacity-0',\n )\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n</template>\n", "content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from '@/registry/new-york/ui/combobox'\nimport { Check, Search } from 'lucide-vue-next'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n</script>\n\n<template>\n <Combobox by=\"label\">\n <ComboboxAnchor>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput class=\"pl-9\" :display-value=\"(val) => val?.label ?? ''\" placeholder=\"Select framework...\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-3\">\n <Search class=\"size-4 text-muted-foreground\" />\n </span>\n </div>\n </ComboboxAnchor>\n\n <ComboboxList>\n <ComboboxEmpty>\n No framework found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework\"\n >\n {{ framework.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n",
"type": "registry:example", "type": "registry:example",
"target": "" "target": ""
} }

View File

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

View File

@ -0,0 +1,18 @@
{
"name": "ComboboxTrigger",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"utils",
"button",
"combobox"
],
"files": [
{
"path": "example/ComboboxTrigger.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/new-york/ui/button'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/new-york/ui/combobox'\nimport { Check, ChevronsUpDown, Search } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n\nconst value = ref<typeof frameworks[0]>()\n</script>\n\n<template>\n <Combobox v-model=\"value\" by=\"label\">\n <ComboboxAnchor as-child>\n <ComboboxTrigger as-child>\n <Button variant=\"outline\" class=\"justify-between\">\n {{ value?.label ?? 'Select framework' }}\n\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </ComboboxTrigger>\n </ComboboxAnchor>\n\n <ComboboxList>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput class=\"pl-9 focus-visible:ring-0 border-0 border-b rounded-none h-10\" placeholder=\"Select framework...\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-3\">\n <Search class=\"size-4 text-muted-foreground\" />\n </span>\n </div>\n\n <ComboboxEmpty>\n No framework found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework\"\n >\n {{ framework.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
}

View File

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

View File

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

View File

@ -0,0 +1,18 @@
{
"name": "CommandPopover",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"button",
"command",
"popover"
],
"files": [
{
"path": "example/CommandPopover.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/new-york/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/new-york/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/new-york/ui/popover'\nimport { ref } from 'vue'\n\ninterface Status {\n value: string\n label: string\n}\n\nconst statuses: Status[] = [\n {\n value: 'backlog',\n label: 'Backlog',\n },\n {\n value: 'todo',\n label: 'Todo',\n },\n {\n value: 'in progress',\n label: 'In Progress',\n },\n {\n value: 'done',\n label: 'Done',\n },\n {\n value: 'canceled',\n label: 'Canceled',\n },\n]\n\nconst open = ref(false)\nconst selectedStatus = ref<Status>()\n</script>\n\n<template>\n <div class=\"flex items-center space-x-4\">\n <p class=\"text-sm text-muted-foreground\">\n Status\n </p>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n size=\"sm\"\n class=\"w-[150px] justify-start\"\n >\n <template v-if=\"selectedStatus\">\n {{ selectedStatus?.label }}\n </template>\n <template v-else>\n + Set status\n </template>\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"p-0\" side=\"right\" align=\"start\">\n <Command>\n <CommandInput placeholder=\"Change status...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"status in statuses\"\n :key=\"status.value\"\n :value=\"status.value\"\n @select=\"() => {\n selectedStatus = status\n open = false\n }\"\n >\n {{ status.label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n </div>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
}

View File

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

View File

@ -0,0 +1,72 @@
{
"name": "combobox",
"type": "registry:ui",
"dependencies": [
"reka-ui"
],
"registryDependencies": [
"utils"
],
"files": [
{
"path": "ui/combobox/Combobox.vue",
"content": "<script setup lang=\"ts\">\nimport { ComboboxRoot, type ComboboxRootEmits, type ComboboxRootProps, useForwardPropsEmits } from 'reka-ui'\n\nconst props = defineProps<ComboboxRootProps>()\nconst emits = defineEmits<ComboboxRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <ComboboxRoot v-bind=\"forwarded\">\n <slot />\n </ComboboxRoot>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxAnchor.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxAnchorProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxAnchor, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxAnchorProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxAnchor\n v-bind=\"forwarded\"\n :class=\"cn('w-[200px]', props.class)\"\n >\n <slot />\n </ComboboxAnchor>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxEmpty.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxEmptyProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxEmpty } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxEmptyProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxEmpty v-bind=\"delegatedProps\" :class=\"cn('py-6 text-center text-sm', props.class)\">\n <slot />\n </ComboboxEmpty>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxGroup.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxGroupProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxGroup, ComboboxLabel } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxGroupProps & {\n class?: HTMLAttributes['class']\n heading?: string\n}>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxGroup\n v-bind=\"delegatedProps\"\n :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)\"\n >\n <ComboboxLabel v-if=\"heading\" class=\"px-2 py-1.5 text-xs font-medium text-muted-foreground\">\n {{ heading }}\n </ComboboxLabel>\n <slot />\n </ComboboxGroup>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxInput.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { ComboboxInput, type ComboboxInputProps, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxInputProps & {\n class?: HTMLAttributes['class']\n}>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxInput\n v-bind=\"forwardedProps\"\n :class=\"cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)\"\n >\n <slot />\n </ComboboxInput>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxItem.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxItemEmits, ComboboxItemProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxItem, useForwardPropsEmits } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxItemProps & { class?: HTMLAttributes['class'] }>()\nconst emits = defineEmits<ComboboxItemEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <ComboboxItem\n v-bind=\"forwarded\"\n :class=\"cn('relative flex cursor-default gap-2 select-none justify-between 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 [&_svg]:size-4 [&_svg]:shrink-0', props.class)\"\n >\n <slot />\n </ComboboxItem>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxList.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxContentEmits, ComboboxContentProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxContent, ComboboxPortal, ComboboxViewport, useForwardPropsEmits } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = withDefaults(defineProps<ComboboxContentProps & { class?: HTMLAttributes['class'] }>(), {\n position: 'popper',\n align: 'center',\n sideOffset: 4,\n})\nconst emits = defineEmits<ComboboxContentEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <ComboboxPortal>\n <ComboboxContent\n v-bind=\"forwarded\"\n :class=\"cn('z-50 w-[200px] rounded-md border bg-popover text-popover-foreground shadow-md outline-none 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)\"\n >\n <ComboboxViewport>\n <slot />\n </ComboboxViewport>\n </ComboboxContent>\n </ComboboxPortal>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxSeparator.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxSeparatorProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxSeparator } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxSeparatorProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxSeparator\n v-bind=\"delegatedProps\"\n :class=\"cn('-mx-1 h-px bg-border', props.class)\"\n >\n <slot />\n </ComboboxSeparator>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxTrigger.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxTriggerProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxTrigger, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxTriggerProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxTrigger\n v-bind=\"forwarded\"\n :class=\"cn('', props.class)\"\n tabindex=\"0\"\n >\n <slot />\n </ComboboxTrigger>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/index.ts",
"content": "export { default as Combobox } from './Combobox.vue'\nexport { default as ComboboxAnchor } from './ComboboxAnchor.vue'\nexport { default as ComboboxEmpty } from './ComboboxEmpty.vue'\nexport { default as ComboboxGroup } from './ComboboxGroup.vue'\nexport { default as ComboboxInput } from './ComboboxInput.vue'\nexport { default as ComboboxItem } from './ComboboxItem.vue'\nexport { default as ComboboxList } from './ComboboxList.vue'\nexport { default as ComboboxSeparator } from './ComboboxSeparator.vue'\n\nexport { ComboboxCancel, ComboboxItemIndicator, ComboboxTrigger } from 'reka-ui'\n",
"type": "registry:ui",
"target": ""
}
]
}

View File

@ -1046,6 +1046,78 @@
} }
] ]
}, },
{
"name": "combobox",
"type": "registry:ui",
"dependencies": [
"reka-ui"
],
"registryDependencies": [
"utils"
],
"files": [
{
"path": "ui/combobox/Combobox.vue",
"content": "<script setup lang=\"ts\">\nimport { ComboboxRoot, type ComboboxRootEmits, type ComboboxRootProps, useForwardPropsEmits } from 'reka-ui'\n\nconst props = defineProps<ComboboxRootProps>()\nconst emits = defineEmits<ComboboxRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <ComboboxRoot v-bind=\"forwarded\">\n <slot />\n </ComboboxRoot>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxAnchor.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxAnchorProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxAnchor, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxAnchorProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxAnchor\n v-bind=\"forwarded\"\n :class=\"cn('w-[200px]', props.class)\"\n >\n <slot />\n </ComboboxAnchor>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxEmpty.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxEmptyProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxEmpty } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxEmptyProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxEmpty v-bind=\"delegatedProps\" :class=\"cn('py-6 text-center text-sm', props.class)\">\n <slot />\n </ComboboxEmpty>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxGroup.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxGroupProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxGroup, ComboboxLabel } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxGroupProps & {\n class?: HTMLAttributes['class']\n heading?: string\n}>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxGroup\n v-bind=\"delegatedProps\"\n :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)\"\n >\n <ComboboxLabel v-if=\"heading\" class=\"px-2 py-1.5 text-xs font-medium text-muted-foreground\">\n {{ heading }}\n </ComboboxLabel>\n <slot />\n </ComboboxGroup>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxInput.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { ComboboxInput, type ComboboxInputProps, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxInputProps & {\n class?: HTMLAttributes['class']\n}>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxInput\n v-bind=\"forwardedProps\"\n :class=\"cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)\"\n >\n <slot />\n </ComboboxInput>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxItem.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxItemEmits, ComboboxItemProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxItem, useForwardPropsEmits } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxItemProps & { class?: HTMLAttributes['class'] }>()\nconst emits = defineEmits<ComboboxItemEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <ComboboxItem\n v-bind=\"forwarded\"\n :class=\"cn('relative flex cursor-default gap-2 select-none justify-between 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 [&_svg]:size-4 [&_svg]:shrink-0', props.class)\"\n >\n <slot />\n </ComboboxItem>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxList.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxContentEmits, ComboboxContentProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxContent, ComboboxPortal, ComboboxViewport, useForwardPropsEmits } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = withDefaults(defineProps<ComboboxContentProps & { class?: HTMLAttributes['class'] }>(), {\n position: 'popper',\n align: 'center',\n sideOffset: 4,\n})\nconst emits = defineEmits<ComboboxContentEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <ComboboxPortal>\n <ComboboxContent\n v-bind=\"forwarded\"\n :class=\"cn('z-50 w-[200px] rounded-md border bg-popover text-popover-foreground shadow-md outline-none 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)\"\n >\n <ComboboxViewport>\n <slot />\n </ComboboxViewport>\n </ComboboxContent>\n </ComboboxPortal>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxSeparator.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxSeparatorProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxSeparator } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxSeparatorProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxSeparator\n v-bind=\"delegatedProps\"\n :class=\"cn('-mx-1 h-px bg-border', props.class)\"\n >\n <slot />\n </ComboboxSeparator>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxTrigger.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxTriggerProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxTrigger, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxTriggerProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxTrigger\n v-bind=\"forwarded\"\n :class=\"cn('', props.class)\"\n tabindex=\"0\"\n >\n <slot />\n </ComboboxTrigger>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/index.ts",
"content": "export { default as Combobox } from './Combobox.vue'\nexport { default as ComboboxAnchor } from './ComboboxAnchor.vue'\nexport { default as ComboboxEmpty } from './ComboboxEmpty.vue'\nexport { default as ComboboxGroup } from './ComboboxGroup.vue'\nexport { default as ComboboxInput } from './ComboboxInput.vue'\nexport { default as ComboboxItem } from './ComboboxItem.vue'\nexport { default as ComboboxList } from './ComboboxList.vue'\nexport { default as ComboboxSeparator } from './ComboboxSeparator.vue'\n\nexport { ComboboxCancel, ComboboxItemIndicator, ComboboxTrigger } from 'reka-ui'\n",
"type": "registry:ui",
"target": ""
}
]
},
{ {
"name": "command", "name": "command",
"type": "registry:ui", "type": "registry:ui",
@ -4108,14 +4180,12 @@
"dependencies": [], "dependencies": [],
"registryDependencies": [ "registryDependencies": [
"utils", "utils",
"button", "combobox"
"command",
"popover"
], ],
"files": [ "files": [
{ {
"path": "example/ComboboxDemo.vue", "path": "example/ComboboxDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/new-york/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/new-york/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/new-york/ui/popover'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n\nconst open = ref(false)\nconst value = ref('')\n</script>\n\n<template>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n class=\"w-[200px] justify-between\"\n >\n {{ value\n ? frameworks.find((framework) => framework.value === value)?.label\n : \"Select framework...\" }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput class=\"h-9\" placeholder=\"Search framework...\" />\n <CommandEmpty>No framework found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework.value\"\n @select=\"(ev) => {\n if (typeof ev.detail.value === 'string') {\n value = ev.detail.value\n }\n open = false\n }\"\n >\n {{ framework.label }}\n <Check\n :class=\"cn(\n 'ml-auto h-4 w-4',\n value === framework.value ? 'opacity-100' : 'opacity-0',\n )\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n</template>\n", "content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from '@/registry/new-york/ui/combobox'\nimport { Check, Search } from 'lucide-vue-next'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n</script>\n\n<template>\n <Combobox by=\"label\">\n <ComboboxAnchor>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput class=\"pl-9\" :display-value=\"(val) => val?.label ?? ''\" placeholder=\"Select framework...\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-3\">\n <Search class=\"size-4 text-muted-foreground\" />\n </span>\n </div>\n </ComboboxAnchor>\n\n <ComboboxList>\n <ComboboxEmpty>\n No framework found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework\"\n >\n {{ framework.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n",
"type": "registry:example", "type": "registry:example",
"target": "" "target": ""
} }
@ -4150,15 +4220,14 @@
"registryDependencies": [ "registryDependencies": [
"utils", "utils",
"button", "button",
"command", "combobox",
"form", "form",
"popover",
"toast" "toast"
], ],
"files": [ "files": [
{ {
"path": "example/ComboboxForm.vue", "path": "example/ComboboxForm.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/new-york/ui/button'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/new-york/ui/command'\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/new-york/ui/form'\n\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/new-york/ui/popover'\nimport { toast } from '@/registry/new-york/ui/toast'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst languages = [\n { label: 'English', value: 'en' },\n { label: 'French', value: 'fr' },\n { label: 'German', value: 'de' },\n { label: 'Spanish', value: 'es' },\n { label: 'Portuguese', value: 'pt' },\n { label: 'Russian', value: 'ru' },\n { label: 'Japanese', value: 'ja' },\n { label: 'Korean', value: 'ko' },\n { label: 'Chinese', value: 'zh' },\n] as const\n\nconst formSchema = toTypedSchema(z.object({\n language: z.string({\n required_error: 'Please select a language.',\n }),\n}))\n\nconst { handleSubmit, setFieldValue, values } = useForm({\n validationSchema: formSchema,\n initialValues: {\n language: '',\n },\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form class=\"space-y-6\" @submit=\"onSubmit\">\n <FormField name=\"language\">\n <FormItem class=\"flex flex-col\">\n <FormLabel>Language</FormLabel>\n <Popover>\n <PopoverTrigger as-child>\n <FormControl>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :class=\"cn('w-[200px] justify-between', !values.language && 'text-muted-foreground')\"\n >\n {{ values.language ? languages.find(\n (language) => language.value === values.language,\n )?.label : 'Select language...' }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput placeholder=\"Search language...\" />\n <CommandEmpty>Nothing found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"language in languages\"\n :key=\"language.value\"\n :value=\"language.label\"\n @select=\"() => {\n setFieldValue('language', language.value)\n }\"\n >\n {{ language.label }}\n <Check\n :class=\"cn('ml-auto h-4 w-4', language.value === values.language ? 'opacity-100' : 'opacity-0')\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n <FormDescription>\n This is the language that will be used in the dashboard.\n </FormDescription>\n <FormMessage />\n </FormItem>\n </FormField>\n\n <Button type=\"submit\">\n Submit\n </Button>\n </form>\n</template>\n", "content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/new-york/ui/button'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/new-york/ui/combobox'\n\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/new-york/ui/form'\n\nimport { toast } from '@/registry/new-york/ui/toast'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst languages = [\n { label: 'English', value: 'en' },\n { label: 'French', value: 'fr' },\n { label: 'German', value: 'de' },\n { label: 'Spanish', value: 'es' },\n { label: 'Portuguese', value: 'pt' },\n { label: 'Russian', value: 'ru' },\n { label: 'Japanese', value: 'ja' },\n { label: 'Korean', value: 'ko' },\n { label: 'Chinese', value: 'zh' },\n] as const\n\nconst formSchema = toTypedSchema(z.object({\n language: z.string({\n required_error: 'Please select a language.',\n }),\n}))\n\nconst { handleSubmit, setFieldValue } = useForm({\n validationSchema: formSchema,\n initialValues: {\n language: '',\n },\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form class=\"space-y-6\" @submit=\"onSubmit\">\n <FormField name=\"language\">\n <FormItem class=\"flex flex-col\">\n <FormLabel>Language</FormLabel>\n\n <Combobox by=\"label\">\n <FormControl>\n <ComboboxAnchor>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput :display-value=\"(val) => val?.label ?? ''\" placeholder=\"Select framework...\" />\n <ComboboxTrigger class=\"absolute end-0 inset-y-0 flex items-center justify-center px-3\">\n <ChevronsUpDown class=\"size-4 text-muted-foreground\" />\n </ComboboxTrigger>\n </div>\n </ComboboxAnchor>\n </FormControl>\n\n <ComboboxList>\n <ComboboxEmpty>\n Nothing found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"language in languages\"\n :key=\"language.value\"\n :value=\"language\"\n @select=\"() => {\n setFieldValue('language', language.value)\n }\"\n >\n {{ language.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n\n <FormDescription>\n This is the language that will be used in the dashboard.\n </FormDescription>\n <FormMessage />\n </FormItem>\n </FormField>\n\n <Button type=\"submit\">\n Submit\n </Button>\n </form>\n</template>\n",
"type": "registry:example", "type": "registry:example",
"target": "" "target": ""
} }
@ -4203,6 +4272,24 @@
} }
] ]
}, },
{
"name": "ComboboxTrigger",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"utils",
"button",
"combobox"
],
"files": [
{
"path": "example/ComboboxTrigger.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/new-york/ui/button'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/new-york/ui/combobox'\nimport { Check, ChevronsUpDown, Search } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n\nconst value = ref<typeof frameworks[0]>()\n</script>\n\n<template>\n <Combobox v-model=\"value\" by=\"label\">\n <ComboboxAnchor as-child>\n <ComboboxTrigger as-child>\n <Button variant=\"outline\" class=\"justify-between\">\n {{ value?.label ?? 'Select framework' }}\n\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </ComboboxTrigger>\n </ComboboxAnchor>\n\n <ComboboxList>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput class=\"pl-9 focus-visible:ring-0 border-0 border-b rounded-none h-10\" placeholder=\"Select framework...\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-3\">\n <Search class=\"size-4 text-muted-foreground\" />\n </span>\n </div>\n\n <ComboboxEmpty>\n No framework found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework\"\n >\n {{ framework.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{ {
"name": "CommandDemo", "name": "CommandDemo",
"type": "registry:example", "type": "registry:example",
@ -4237,6 +4324,88 @@
} }
] ]
}, },
{
"name": "CommandDropdownMenu",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"button",
"command",
"dropdown-menu"
],
"files": [
{
"path": "example/CommandDropdownMenu.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/new-york/ui/button'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/new-york/ui/command'\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from '@/registry/new-york/ui/dropdown-menu'\nimport { MoreHorizontal } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst labels = [\n 'feature',\n 'bug',\n 'enhancement',\n 'documentation',\n 'design',\n 'question',\n 'maintenance',\n]\n\nconst labelRef = ref('feature')\nconst open = ref(false)\n</script>\n\n<template>\n <div class=\"flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center\">\n <p class=\"text-sm font-medium leading-none\">\n <span class=\"mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground\">\n {{ labelRef }}\n </span>\n <span class=\"text-muted-foreground\">Create a new project</span>\n </p>\n <DropdownMenu v-model:open=\"open\">\n <DropdownMenuTrigger as-child>\n <Button variant=\"ghost\" size=\"sm\">\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" class=\"w-[200px]\">\n <DropdownMenuLabel>Actions</DropdownMenuLabel>\n <DropdownMenuGroup>\n <DropdownMenuItem>\n Assign to...\n </DropdownMenuItem>\n <DropdownMenuItem>\n Set due date...\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n Apply label\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent class=\"p-0\">\n <Command>\n <CommandInput\n placeholder=\"Filter label...\"\n auto-focus\n />\n <CommandList>\n <CommandEmpty>No label found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"label in labels\"\n :key=\"label\"\n :value=\"label\"\n @select=\"(ev) => {\n labelRef = ev.detail.value as string\n open = false\n }\"\n >\n {{ label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n <DropdownMenuSeparator />\n <DropdownMenuItem class=\"text-red-600\">\n Delete\n <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>\n </DropdownMenuItem>\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{
"name": "CommandForm",
"type": "registry:example",
"dependencies": [
"vee-validate",
"@vee-validate/zod",
"zod"
],
"registryDependencies": [
"utils",
"button",
"command",
"form",
"popover",
"toast"
],
"files": [
{
"path": "example/CommandForm.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/new-york/ui/button'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/new-york/ui/command'\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/new-york/ui/form'\n\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/new-york/ui/popover'\nimport { toast } from '@/registry/new-york/ui/toast'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst languages = [\n { label: 'English', value: 'en' },\n { label: 'French', value: 'fr' },\n { label: 'German', value: 'de' },\n { label: 'Spanish', value: 'es' },\n { label: 'Portuguese', value: 'pt' },\n { label: 'Russian', value: 'ru' },\n { label: 'Japanese', value: 'ja' },\n { label: 'Korean', value: 'ko' },\n { label: 'Chinese', value: 'zh' },\n] as const\n\nconst formSchema = toTypedSchema(z.object({\n language: z.string({\n required_error: 'Please select a language.',\n }),\n}))\n\nconst { handleSubmit, setFieldValue, values } = useForm({\n validationSchema: formSchema,\n initialValues: {\n language: '',\n },\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form class=\"space-y-6\" @submit=\"onSubmit\">\n <FormField name=\"language\">\n <FormItem class=\"flex flex-col\">\n <FormLabel>Language</FormLabel>\n <Popover>\n <PopoverTrigger as-child>\n <FormControl>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :class=\"cn('w-[200px] justify-between', !values.language && 'text-muted-foreground')\"\n >\n {{ values.language ? languages.find(\n (language) => language.value === values.language,\n )?.label : 'Select language...' }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput placeholder=\"Search language...\" />\n <CommandEmpty>Nothing found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"language in languages\"\n :key=\"language.value\"\n :value=\"language.label\"\n @select=\"() => {\n setFieldValue('language', language.value)\n }\"\n >\n {{ language.label }}\n <Check\n :class=\"cn('ml-auto h-4 w-4', language.value === values.language ? 'opacity-100' : 'opacity-0')\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n <FormDescription>\n This is the language that will be used in the dashboard.\n </FormDescription>\n <FormMessage />\n </FormItem>\n </FormField>\n\n <Button type=\"submit\">\n Submit\n </Button>\n </form>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{
"name": "CommandPopover",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"button",
"command",
"popover"
],
"files": [
{
"path": "example/CommandPopover.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/new-york/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/new-york/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/new-york/ui/popover'\nimport { ref } from 'vue'\n\ninterface Status {\n value: string\n label: string\n}\n\nconst statuses: Status[] = [\n {\n value: 'backlog',\n label: 'Backlog',\n },\n {\n value: 'todo',\n label: 'Todo',\n },\n {\n value: 'in progress',\n label: 'In Progress',\n },\n {\n value: 'done',\n label: 'Done',\n },\n {\n value: 'canceled',\n label: 'Canceled',\n },\n]\n\nconst open = ref(false)\nconst selectedStatus = ref<Status>()\n</script>\n\n<template>\n <div class=\"flex items-center space-x-4\">\n <p class=\"text-sm text-muted-foreground\">\n Status\n </p>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n size=\"sm\"\n class=\"w-[150px] justify-start\"\n >\n <template v-if=\"selectedStatus\">\n {{ selectedStatus?.label }}\n </template>\n <template v-else>\n + Set status\n </template>\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"p-0\" side=\"right\" align=\"start\">\n <Command>\n <CommandInput placeholder=\"Change status...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"status in statuses\"\n :key=\"status.value\"\n :value=\"status.value\"\n @select=\"() => {\n selectedStatus = status\n open = false\n }\"\n >\n {{ status.label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n </div>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{
"name": "CommandResponsive",
"type": "registry:example",
"dependencies": [
"@vueuse/core"
],
"registryDependencies": [
"button",
"command",
"drawer",
"popover"
],
"files": [
{
"path": "example/CommandResponsive.vue",
"content": "<script lang=\"ts\" setup>\nimport { Button } from '@/registry/new-york/ui/button'\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/registry/new-york/ui/command'\nimport { Drawer, DrawerContent, DrawerTrigger } from '@/registry/new-york/ui/drawer'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/registry/new-york/ui/popover'\nimport { createReusableTemplate, useMediaQuery } from '@vueuse/core'\nimport { ref } from 'vue'\n\ninterface Status {\n value: string\n label: string\n}\n\nconst statuses: Status[] = [\n {\n value: 'backlog',\n label: 'Backlog',\n },\n {\n value: 'todo',\n label: 'Todo',\n },\n {\n value: 'in progress',\n label: 'In Progress',\n },\n {\n value: 'done',\n label: 'Done',\n },\n {\n value: 'canceled',\n label: 'Canceled',\n },\n]\n\nconst [UseTemplate, StatusList] = createReusableTemplate()\nconst isDesktop = useMediaQuery('(min-width: 768px)')\n\nconst isOpen = ref(false)\nconst selectedStatus = ref<Status | null>(null)\n\nfunction onStatusSelect(status: Status) {\n selectedStatus.value = status\n isOpen.value = false\n}\n</script>\n\n<template>\n <div>\n <UseTemplate>\n <Command>\n <CommandInput placeholder=\"Filter status...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"status of statuses\"\n :key=\"status.value\"\n :value=\"status.value\"\n @select=\"onStatusSelect(status)\"\n >\n {{ status.label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </UseTemplate>\n\n <Popover v-if=\"isDesktop\" v-model:open=\"isOpen\">\n <PopoverTrigger as-child>\n <Button variant=\"outline\" class=\"w-[150px] justify-start\">\n {{ selectedStatus ? selectedStatus.label : \"+ Set status\" }}\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\" align=\"start\">\n <StatusList />\n </PopoverContent>\n </Popover>\n\n <Drawer v-else v-model:open=\"isOpen\">\n <DrawerTrigger as-child>\n <Button variant=\"outline\" class=\"w-[150px] justify-start\">\n {{ selectedStatus ? selectedStatus.label : \"+ Set status\" }}\n </Button>\n </DrawerTrigger>\n <DrawerContent>\n <div class=\"mt-4 border-t\">\n <StatusList />\n </div>\n </DrawerContent>\n </Drawer>\n </div>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{ {
"name": "ContextMenuDemo", "name": "ContextMenuDemo",
"type": "registry:example", "type": "registry:example",
@ -8339,6 +8508,78 @@
} }
] ]
}, },
{
"name": "combobox",
"type": "registry:ui",
"dependencies": [
"reka-ui"
],
"registryDependencies": [
"utils"
],
"files": [
{
"path": "ui/combobox/Combobox.vue",
"content": "<script setup lang=\"ts\">\nimport { ComboboxRoot, type ComboboxRootEmits, type ComboboxRootProps, useForwardPropsEmits } from 'reka-ui'\n\nconst props = defineProps<ComboboxRootProps>()\nconst emits = defineEmits<ComboboxRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n <ComboboxRoot v-bind=\"forwarded\">\n <slot />\n </ComboboxRoot>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxAnchor.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxAnchorProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxAnchor, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxAnchorProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxAnchor\n v-bind=\"forwarded\"\n :class=\"cn('w-[200px]', props.class)\"\n >\n <slot />\n </ComboboxAnchor>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxEmpty.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxEmptyProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxEmpty } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxEmptyProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxEmpty v-bind=\"delegatedProps\" :class=\"cn('py-6 text-center text-sm', props.class)\">\n <slot />\n </ComboboxEmpty>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxGroup.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxGroupProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxGroup, ComboboxLabel } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxGroupProps & {\n class?: HTMLAttributes['class']\n heading?: string\n}>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxGroup\n v-bind=\"delegatedProps\"\n :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)\"\n >\n <ComboboxLabel v-if=\"heading\" class=\"px-2 py-1.5 text-xs font-medium text-muted-foreground\">\n {{ heading }}\n </ComboboxLabel>\n <slot />\n </ComboboxGroup>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxInput.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { ComboboxInput, type ComboboxInputProps, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxInputProps & {\n class?: HTMLAttributes['class']\n}>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwardedProps = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxInput\n v-bind=\"forwardedProps\"\n :class=\"cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)\"\n >\n <slot />\n </ComboboxInput>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxItem.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxItemEmits, ComboboxItemProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxItem, useForwardPropsEmits } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxItemProps & { class?: HTMLAttributes['class'] }>()\nconst emits = defineEmits<ComboboxItemEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <ComboboxItem\n v-bind=\"forwarded\"\n :class=\"cn('relative flex cursor-default gap-2 select-none justify-between 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 [&_svg]:size-4 [&_svg]:shrink-0', props.class)\"\n >\n <slot />\n </ComboboxItem>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxList.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxContentEmits, ComboboxContentProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxContent, ComboboxPortal, ComboboxViewport, useForwardPropsEmits } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = withDefaults(defineProps<ComboboxContentProps & { class?: HTMLAttributes['class'] }>(), {\n position: 'popper',\n align: 'center',\n sideOffset: 4,\n})\nconst emits = defineEmits<ComboboxContentEmits>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n <ComboboxPortal>\n <ComboboxContent\n v-bind=\"forwarded\"\n :class=\"cn('z-50 w-[200px] rounded-md border bg-popover text-popover-foreground shadow-md outline-none 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)\"\n >\n <ComboboxViewport>\n <slot />\n </ComboboxViewport>\n </ComboboxContent>\n </ComboboxPortal>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxSeparator.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxSeparatorProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxSeparator } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxSeparatorProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n</script>\n\n<template>\n <ComboboxSeparator\n v-bind=\"delegatedProps\"\n :class=\"cn('-mx-1 h-px bg-border', props.class)\"\n >\n <slot />\n </ComboboxSeparator>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/ComboboxTrigger.vue",
"content": "<script setup lang=\"ts\">\nimport type { ComboboxTriggerProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { ComboboxTrigger, useForwardProps } from 'reka-ui'\nimport { computed, type HTMLAttributes } from 'vue'\n\nconst props = defineProps<ComboboxTriggerProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n const { class: _, ...delegated } = props\n\n return delegated\n})\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n <ComboboxTrigger\n v-bind=\"forwarded\"\n :class=\"cn('', props.class)\"\n tabindex=\"0\"\n >\n <slot />\n </ComboboxTrigger>\n</template>\n",
"type": "registry:ui",
"target": ""
},
{
"path": "ui/combobox/index.ts",
"content": "export { default as Combobox } from './Combobox.vue'\nexport { default as ComboboxAnchor } from './ComboboxAnchor.vue'\nexport { default as ComboboxEmpty } from './ComboboxEmpty.vue'\nexport { default as ComboboxGroup } from './ComboboxGroup.vue'\nexport { default as ComboboxInput } from './ComboboxInput.vue'\nexport { default as ComboboxItem } from './ComboboxItem.vue'\nexport { default as ComboboxList } from './ComboboxList.vue'\nexport { default as ComboboxSeparator } from './ComboboxSeparator.vue'\n\nexport { ComboboxCancel, ComboboxItemIndicator, ComboboxTrigger } from 'reka-ui'\n",
"type": "registry:ui",
"target": ""
}
]
},
{ {
"name": "command", "name": "command",
"type": "registry:ui", "type": "registry:ui",
@ -11401,14 +11642,12 @@
"dependencies": [], "dependencies": [],
"registryDependencies": [ "registryDependencies": [
"utils", "utils",
"button", "combobox"
"command",
"popover"
], ],
"files": [ "files": [
{ {
"path": "example/ComboboxDemo.vue", "path": "example/ComboboxDemo.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/default/ui/popover'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n\nconst open = ref(false)\nconst value = ref('')\n</script>\n\n<template>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n class=\"w-[200px] justify-between\"\n >\n {{ value\n ? frameworks.find((framework) => framework.value === value)?.label\n : \"Select framework...\" }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput class=\"h-9\" placeholder=\"Search framework...\" />\n <CommandEmpty>No framework found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework.value\"\n @select=\"(ev) => {\n if (typeof ev.detail.value === 'string') {\n value = ev.detail.value\n }\n open = false\n }\"\n >\n {{ framework.label }}\n <Check\n :class=\"cn(\n 'ml-auto h-4 w-4',\n value === framework.value ? 'opacity-100' : 'opacity-0',\n )\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n</template>\n", "content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from '@/registry/default/ui/combobox'\nimport { Check, Search } from 'lucide-vue-next'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n</script>\n\n<template>\n <Combobox by=\"label\">\n <ComboboxAnchor>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput class=\"pl-9\" :display-value=\"(val) => val?.label ?? ''\" placeholder=\"Select framework...\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-3\">\n <Search class=\"size-4 text-muted-foreground\" />\n </span>\n </div>\n </ComboboxAnchor>\n\n <ComboboxList>\n <ComboboxEmpty>\n No framework found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework\"\n >\n {{ framework.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n",
"type": "registry:example", "type": "registry:example",
"target": "" "target": ""
} }
@ -11443,15 +11682,14 @@
"registryDependencies": [ "registryDependencies": [
"utils", "utils",
"button", "button",
"command", "combobox",
"form", "form",
"popover",
"toast" "toast"
], ],
"files": [ "files": [
{ {
"path": "example/ComboboxForm.vue", "path": "example/ComboboxForm.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/default/ui/form'\n\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/default/ui/popover'\nimport { toast } from '@/registry/default/ui/toast'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst languages = [\n { label: 'English', value: 'en' },\n { label: 'French', value: 'fr' },\n { label: 'German', value: 'de' },\n { label: 'Spanish', value: 'es' },\n { label: 'Portuguese', value: 'pt' },\n { label: 'Russian', value: 'ru' },\n { label: 'Japanese', value: 'ja' },\n { label: 'Korean', value: 'ko' },\n { label: 'Chinese', value: 'zh' },\n]\n\nconst formSchema = toTypedSchema(z.object({\n language: z.string({\n required_error: 'Please select a language.',\n }),\n}))\n\nconst { handleSubmit, setFieldValue, values } = useForm({\n validationSchema: formSchema,\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form class=\"space-y-6\" @submit=\"onSubmit\">\n <FormField name=\"language\">\n <FormItem class=\"flex flex-col\">\n <FormLabel>Language</FormLabel>\n <Popover>\n <PopoverTrigger as-child>\n <FormControl>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :class=\"cn('w-[200px] justify-between', !values.language && 'text-muted-foreground')\"\n >\n {{ values.language ? languages.find(\n (language) => language.value === values.language,\n )?.label : 'Select language...' }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput placeholder=\"Search language...\" />\n <CommandEmpty>Nothing found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"language in languages\"\n :key=\"language.value\"\n :value=\"language.label\"\n @select=\"() => {\n setFieldValue('language', language.value)\n }\"\n >\n <Check\n :class=\"cn('mr-2 h-4 w-4', language.value === values.language ? 'opacity-100' : 'opacity-0')\"\n />\n {{ language.label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n <FormDescription>\n This is the language that will be used in the dashboard.\n </FormDescription>\n <FormMessage />\n </FormItem>\n </FormField>\n\n <Button type=\"submit\">\n Submit\n </Button>\n </form>\n</template>\n", "content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/default/ui/combobox'\n\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/default/ui/form'\n\nimport { toast } from '@/registry/default/ui/toast'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst languages = [\n { label: 'English', value: 'en' },\n { label: 'French', value: 'fr' },\n { label: 'German', value: 'de' },\n { label: 'Spanish', value: 'es' },\n { label: 'Portuguese', value: 'pt' },\n { label: 'Russian', value: 'ru' },\n { label: 'Japanese', value: 'ja' },\n { label: 'Korean', value: 'ko' },\n { label: 'Chinese', value: 'zh' },\n] as const\n\nconst formSchema = toTypedSchema(z.object({\n language: z.string({\n required_error: 'Please select a language.',\n }),\n}))\n\nconst { handleSubmit, setFieldValue } = useForm({\n validationSchema: formSchema,\n initialValues: {\n language: '',\n },\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form class=\"space-y-6\" @submit=\"onSubmit\">\n <FormField name=\"language\">\n <FormItem class=\"flex flex-col\">\n <FormLabel>Language</FormLabel>\n\n <Combobox by=\"label\">\n <FormControl>\n <ComboboxAnchor>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput :display-value=\"(val) => val?.label ?? ''\" placeholder=\"Select framework...\" />\n <ComboboxTrigger class=\"absolute end-0 inset-y-0 flex items-center justify-center px-3\">\n <ChevronsUpDown class=\"size-4 text-muted-foreground\" />\n </ComboboxTrigger>\n </div>\n </ComboboxAnchor>\n </FormControl>\n\n <ComboboxList>\n <ComboboxEmpty>\n Nothing found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"language in languages\"\n :key=\"language.value\"\n :value=\"language\"\n @select=\"() => {\n setFieldValue('language', language.value)\n }\"\n >\n {{ language.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n\n <FormDescription>\n This is the language that will be used in the dashboard.\n </FormDescription>\n <FormMessage />\n </FormItem>\n </FormField>\n\n <Button type=\"submit\">\n Submit\n </Button>\n </form>\n</template>\n",
"type": "registry:example", "type": "registry:example",
"target": "" "target": ""
} }
@ -11497,6 +11735,24 @@
} }
] ]
}, },
{
"name": "ComboboxTrigger",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"utils",
"button",
"combobox"
],
"files": [
{
"path": "example/ComboboxTrigger.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\nimport { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/default/ui/combobox'\nimport { Check, ChevronsUpDown, Search } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst frameworks = [\n { value: 'next.js', label: 'Next.js' },\n { value: 'sveltekit', label: 'SvelteKit' },\n { value: 'nuxt', label: 'Nuxt' },\n { value: 'remix', label: 'Remix' },\n { value: 'astro', label: 'Astro' },\n]\n\nconst value = ref<typeof frameworks[0]>()\n</script>\n\n<template>\n <Combobox v-model=\"value\" by=\"label\">\n <ComboboxAnchor as-child>\n <ComboboxTrigger as-child>\n <Button variant=\"outline\" class=\"justify-between\">\n {{ value?.label ?? 'Select framework' }}\n\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </ComboboxTrigger>\n </ComboboxAnchor>\n\n <ComboboxList>\n <div class=\"relative w-full max-w-sm items-center\">\n <ComboboxInput class=\"pl-9 focus-visible:ring-0 border-0 border-b rounded-none h-10\" placeholder=\"Select framework...\" />\n <span class=\"absolute start-0 inset-y-0 flex items-center justify-center px-3\">\n <Search class=\"size-4 text-muted-foreground\" />\n </span>\n </div>\n\n <ComboboxEmpty>\n No framework found.\n </ComboboxEmpty>\n\n <ComboboxGroup>\n <ComboboxItem\n v-for=\"framework in frameworks\"\n :key=\"framework.value\"\n :value=\"framework\"\n >\n {{ framework.label }}\n\n <ComboboxItemIndicator>\n <Check :class=\"cn('ml-auto h-4 w-4')\" />\n </ComboboxItemIndicator>\n </ComboboxItem>\n </ComboboxGroup>\n </ComboboxList>\n </Combobox>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{ {
"name": "CommandDemo", "name": "CommandDemo",
"type": "registry:example", "type": "registry:example",
@ -11531,6 +11787,88 @@
} }
] ]
}, },
{
"name": "CommandDropdownMenu",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"button",
"command",
"dropdown-menu"
],
"files": [
{
"path": "example/CommandDropdownMenu.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from '@/registry/default/ui/dropdown-menu'\nimport { MoreHorizontal } from 'lucide-vue-next'\nimport { ref } from 'vue'\n\nconst labels = [\n 'feature',\n 'bug',\n 'enhancement',\n 'documentation',\n 'design',\n 'question',\n 'maintenance',\n]\n\nconst labelRef = ref('feature')\nconst open = ref(false)\n</script>\n\n<template>\n <div class=\"flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center\">\n <p class=\"text-sm font-medium leading-none\">\n <span class=\"mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground\">\n {{ labelRef }}\n </span>\n <span class=\"text-muted-foreground\">Create a new project</span>\n </p>\n <DropdownMenu v-model:open=\"open\">\n <DropdownMenuTrigger as-child>\n <Button variant=\"ghost\" size=\"sm\">\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" class=\"w-[200px]\">\n <DropdownMenuLabel>Actions</DropdownMenuLabel>\n <DropdownMenuGroup>\n <DropdownMenuItem>\n Assign to...\n </DropdownMenuItem>\n <DropdownMenuItem>\n Set due date...\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuSub>\n <DropdownMenuSubTrigger>\n Apply label\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent class=\"p-0\">\n <Command>\n <CommandInput\n placeholder=\"Filter label...\"\n auto-focus\n />\n <CommandList>\n <CommandEmpty>No label found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"label in labels\"\n :key=\"label\"\n :value=\"label\"\n @select=\"(ev) => {\n labelRef = ev.detail.value as string\n open = false\n }\"\n >\n {{ label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n <DropdownMenuSeparator />\n <DropdownMenuItem class=\"text-red-600\">\n Delete\n <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>\n </DropdownMenuItem>\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{
"name": "CommandForm",
"type": "registry:example",
"dependencies": [
"vee-validate",
"@vee-validate/zod",
"zod"
],
"registryDependencies": [
"utils",
"button",
"command",
"form",
"popover",
"toast"
],
"files": [
{
"path": "example/CommandForm.vue",
"content": "<script setup lang=\"ts\">\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/registry/default/ui/button'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from '@/registry/default/ui/form'\n\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/default/ui/popover'\nimport { toast } from '@/registry/default/ui/toast'\nimport { toTypedSchema } from '@vee-validate/zod'\nimport { Check, ChevronsUpDown } from 'lucide-vue-next'\nimport { useForm } from 'vee-validate'\nimport { h } from 'vue'\nimport * as z from 'zod'\n\nconst languages = [\n { label: 'English', value: 'en' },\n { label: 'French', value: 'fr' },\n { label: 'German', value: 'de' },\n { label: 'Spanish', value: 'es' },\n { label: 'Portuguese', value: 'pt' },\n { label: 'Russian', value: 'ru' },\n { label: 'Japanese', value: 'ja' },\n { label: 'Korean', value: 'ko' },\n { label: 'Chinese', value: 'zh' },\n] as const\n\nconst formSchema = toTypedSchema(z.object({\n language: z.string({\n required_error: 'Please select a language.',\n }),\n}))\n\nconst { handleSubmit, setFieldValue, values } = useForm({\n validationSchema: formSchema,\n initialValues: {\n language: '',\n },\n})\n\nconst onSubmit = handleSubmit((values) => {\n toast({\n title: 'You submitted the following values:',\n description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),\n })\n})\n</script>\n\n<template>\n <form class=\"space-y-6\" @submit=\"onSubmit\">\n <FormField name=\"language\">\n <FormItem class=\"flex flex-col\">\n <FormLabel>Language</FormLabel>\n <Popover>\n <PopoverTrigger as-child>\n <FormControl>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n :class=\"cn('w-[200px] justify-between', !values.language && 'text-muted-foreground')\"\n >\n {{ values.language ? languages.find(\n (language) => language.value === values.language,\n )?.label : 'Select language...' }}\n <ChevronsUpDown class=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\">\n <Command>\n <CommandInput placeholder=\"Search language...\" />\n <CommandEmpty>Nothing found.</CommandEmpty>\n <CommandList>\n <CommandGroup>\n <CommandItem\n v-for=\"language in languages\"\n :key=\"language.value\"\n :value=\"language.label\"\n @select=\"() => {\n setFieldValue('language', language.value)\n }\"\n >\n {{ language.label }}\n <Check\n :class=\"cn('ml-auto h-4 w-4', language.value === values.language ? 'opacity-100' : 'opacity-0')\"\n />\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n <FormDescription>\n This is the language that will be used in the dashboard.\n </FormDescription>\n <FormMessage />\n </FormItem>\n </FormField>\n\n <Button type=\"submit\">\n Submit\n </Button>\n </form>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{
"name": "CommandPopover",
"type": "registry:example",
"dependencies": [],
"registryDependencies": [
"button",
"command",
"popover"
],
"files": [
{
"path": "example/CommandPopover.vue",
"content": "<script setup lang=\"ts\">\nimport { Button } from '@/registry/default/ui/button'\n\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/registry/default/ui/command'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@/registry/default/ui/popover'\nimport { ref } from 'vue'\n\ninterface Status {\n value: string\n label: string\n}\n\nconst statuses: Status[] = [\n {\n value: 'backlog',\n label: 'Backlog',\n },\n {\n value: 'todo',\n label: 'Todo',\n },\n {\n value: 'in progress',\n label: 'In Progress',\n },\n {\n value: 'done',\n label: 'Done',\n },\n {\n value: 'canceled',\n label: 'Canceled',\n },\n]\n\nconst open = ref(false)\nconst selectedStatus = ref<Status>()\n</script>\n\n<template>\n <div class=\"flex items-center space-x-4\">\n <p class=\"text-sm text-muted-foreground\">\n Status\n </p>\n <Popover v-model:open=\"open\">\n <PopoverTrigger as-child>\n <Button\n variant=\"outline\"\n size=\"sm\"\n class=\"w-[150px] justify-start\"\n >\n <template v-if=\"selectedStatus\">\n {{ selectedStatus?.label }}\n </template>\n <template v-else>\n + Set status\n </template>\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"p-0\" side=\"right\" align=\"start\">\n <Command>\n <CommandInput placeholder=\"Change status...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"status in statuses\"\n :key=\"status.value\"\n :value=\"status.value\"\n @select=\"() => {\n selectedStatus = status\n open = false\n }\"\n >\n {{ status.label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n </div>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{
"name": "CommandResponsive",
"type": "registry:example",
"dependencies": [
"@vueuse/core"
],
"registryDependencies": [
"button",
"command",
"drawer",
"popover"
],
"files": [
{
"path": "example/CommandResponsive.vue",
"content": "<script lang=\"ts\" setup>\nimport { Button } from '@/registry/default/ui/button'\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/registry/default/ui/command'\nimport { Drawer, DrawerContent, DrawerTrigger } from '@/registry/default/ui/drawer'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/registry/default/ui/popover'\nimport { createReusableTemplate, useMediaQuery } from '@vueuse/core'\nimport { ref } from 'vue'\n\ninterface Status {\n value: string\n label: string\n}\n\nconst statuses: Status[] = [\n {\n value: 'backlog',\n label: 'Backlog',\n },\n {\n value: 'todo',\n label: 'Todo',\n },\n {\n value: 'in progress',\n label: 'In Progress',\n },\n {\n value: 'done',\n label: 'Done',\n },\n {\n value: 'canceled',\n label: 'Canceled',\n },\n]\n\nconst [UseTemplate, StatusList] = createReusableTemplate()\nconst isDesktop = useMediaQuery('(min-width: 768px)')\n\nconst isOpen = ref(false)\nconst selectedStatus = ref<Status | null>(null)\n\nfunction onStatusSelect(status: Status) {\n selectedStatus.value = status\n isOpen.value = false\n}\n</script>\n\n<template>\n <div>\n <UseTemplate>\n <Command>\n <CommandInput placeholder=\"Filter status...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n <CommandGroup>\n <CommandItem\n v-for=\"status of statuses\"\n :key=\"status.value\"\n :value=\"status.value\"\n @select=\"onStatusSelect(status)\"\n >\n {{ status.label }}\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n </UseTemplate>\n\n <Popover v-if=\"isDesktop\" v-model:open=\"isOpen\">\n <PopoverTrigger as-child>\n <Button variant=\"outline\" class=\"w-[150px] justify-start\">\n {{ selectedStatus ? selectedStatus.label : \"+ Set status\" }}\n </Button>\n </PopoverTrigger>\n <PopoverContent class=\"w-[200px] p-0\" align=\"start\">\n <StatusList />\n </PopoverContent>\n </Popover>\n\n <Drawer v-else :open=\"isOpen\" @update:open=\"(newOpenValue) => isOpen = newOpenValue\">\n <DrawerTrigger as-child>\n <Button variant=\"outline\" class=\"w-[150px] justify-start\">\n {{ selectedStatus ? selectedStatus.label : \"+ Set status\" }}\n </Button>\n </DrawerTrigger>\n <DrawerContent>\n <div class=\"mt-4 border-t\">\n <StatusList />\n </div>\n </DrawerContent>\n </Drawer>\n </div>\n</template>\n",
"type": "registry:example",
"target": ""
}
]
},
{ {
"name": "ContextMenuDemo", "name": "ContextMenuDemo",
"type": "registry:example", "type": "registry:example",

View File

@ -1,22 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Button } from '@/registry/default/ui/button' import { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from '@/registry/default/ui/combobox'
import { Check, Search } from 'lucide-vue-next'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/registry/default/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/registry/default/ui/popover'
import { Check, ChevronsUpDown } from 'lucide-vue-next'
import { ref } from 'vue'
const frameworks = [ const frameworks = [
{ value: 'next.js', label: 'Next.js' }, { value: 'next.js', label: 'Next.js' },
@ -25,54 +10,37 @@ const frameworks = [
{ value: 'remix', label: 'Remix' }, { value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' }, { value: 'astro', label: 'Astro' },
] ]
const open = ref(false)
const value = ref('')
</script> </script>
<template> <template>
<Popover v-model:open="open"> <Combobox by="label">
<PopoverTrigger as-child> <ComboboxAnchor>
<Button <div class="relative w-full max-w-sm items-center">
variant="outline" <ComboboxInput class="pl-9" :display-value="(val) => val?.label ?? ''" placeholder="Select framework..." />
role="combobox" <span class="absolute start-0 inset-y-0 flex items-center justify-center px-3">
:aria-expanded="open" <Search class="size-4 text-muted-foreground" />
class="w-[200px] justify-between" </span>
> </div>
{{ value </ComboboxAnchor>
? frameworks.find((framework) => framework.value === value)?.label
: "Select framework..." }} <ComboboxList>
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" /> <ComboboxEmpty>
</Button> No framework found.
</PopoverTrigger> </ComboboxEmpty>
<PopoverContent class="w-[200px] p-0">
<Command> <ComboboxGroup>
<CommandInput class="h-9" placeholder="Search framework..." /> <ComboboxItem
<CommandEmpty>No framework found.</CommandEmpty>
<CommandList>
<CommandGroup>
<CommandItem
v-for="framework in frameworks" v-for="framework in frameworks"
:key="framework.value" :key="framework.value"
:value="framework.value" :value="framework"
@select="(ev) => {
if (typeof ev.detail.value === 'string') {
value = ev.detail.value
}
open = false
}"
> >
{{ framework.label }} {{ framework.label }}
<Check
:class="cn( <ComboboxItemIndicator>
'ml-auto h-4 w-4', <Check :class="cn('ml-auto h-4 w-4')" />
value === framework.value ? 'opacity-100' : 'opacity-0', </ComboboxItemIndicator>
)" </ComboboxItem>
/> </ComboboxGroup>
</CommandItem> </ComboboxList>
</CommandGroup> </Combobox>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</template> </template>

View File

@ -1,14 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Button } from '@/registry/default/ui/button' import { Button } from '@/registry/default/ui/button'
import { import { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/default/ui/combobox'
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/registry/default/ui/command'
import { import {
FormControl, FormControl,
FormDescription, FormDescription,
@ -18,11 +12,6 @@ import {
FormMessage, FormMessage,
} from '@/registry/default/ui/form' } from '@/registry/default/ui/form'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/registry/default/ui/popover'
import { toast } from '@/registry/default/ui/toast' import { toast } from '@/registry/default/ui/toast'
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import { Check, ChevronsUpDown } from 'lucide-vue-next' import { Check, ChevronsUpDown } from 'lucide-vue-next'
@ -40,7 +29,7 @@ const languages = [
{ label: 'Japanese', value: 'ja' }, { label: 'Japanese', value: 'ja' },
{ label: 'Korean', value: 'ko' }, { label: 'Korean', value: 'ko' },
{ label: 'Chinese', value: 'zh' }, { label: 'Chinese', value: 'zh' },
] ] as const
const formSchema = toTypedSchema(z.object({ const formSchema = toTypedSchema(z.object({
language: z.string({ language: z.string({
@ -48,8 +37,11 @@ const formSchema = toTypedSchema(z.object({
}), }),
})) }))
const { handleSubmit, setFieldValue, values } = useForm({ const { handleSubmit, setFieldValue } = useForm({
validationSchema: formSchema, validationSchema: formSchema,
initialValues: {
language: '',
},
}) })
const onSubmit = handleSubmit((values) => { const onSubmit = handleSubmit((values) => {
@ -65,45 +57,43 @@ const onSubmit = handleSubmit((values) => {
<FormField name="language"> <FormField name="language">
<FormItem class="flex flex-col"> <FormItem class="flex flex-col">
<FormLabel>Language</FormLabel> <FormLabel>Language</FormLabel>
<Popover>
<PopoverTrigger as-child> <Combobox by="label">
<FormControl> <FormControl>
<Button <ComboboxAnchor>
variant="outline" <div class="relative w-full max-w-sm items-center">
role="combobox" <ComboboxInput :display-value="(val) => val?.label ?? ''" placeholder="Select framework..." />
:class="cn('w-[200px] justify-between', !values.language && 'text-muted-foreground')" <ComboboxTrigger class="absolute end-0 inset-y-0 flex items-center justify-center px-3">
> <ChevronsUpDown class="size-4 text-muted-foreground" />
{{ values.language ? languages.find( </ComboboxTrigger>
(language) => language.value === values.language, </div>
)?.label : 'Select language...' }} </ComboboxAnchor>
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl> </FormControl>
</PopoverTrigger>
<PopoverContent class="w-[200px] p-0"> <ComboboxList>
<Command> <ComboboxEmpty>
<CommandInput placeholder="Search language..." /> Nothing found.
<CommandEmpty>Nothing found.</CommandEmpty> </ComboboxEmpty>
<CommandList>
<CommandGroup> <ComboboxGroup>
<CommandItem <ComboboxItem
v-for="language in languages" v-for="language in languages"
:key="language.value" :key="language.value"
:value="language.label" :value="language"
@select="() => { @select="() => {
setFieldValue('language', language.value) setFieldValue('language', language.value)
}" }"
> >
<Check
:class="cn('mr-2 h-4 w-4', language.value === values.language ? 'opacity-100' : 'opacity-0')"
/>
{{ language.label }} {{ language.label }}
</CommandItem>
</CommandGroup> <ComboboxItemIndicator>
</CommandList> <Check :class="cn('ml-auto h-4 w-4')" />
</Command> </ComboboxItemIndicator>
</PopoverContent> </ComboboxItem>
</Popover> </ComboboxGroup>
</ComboboxList>
</Combobox>
<FormDescription> <FormDescription>
This is the language that will be used in the dashboard. This is the language that will be used in the dashboard.
</FormDescription> </FormDescription>

View File

@ -0,0 +1,58 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
import { Button } from '@/registry/default/ui/button'
import { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/default/ui/combobox'
import { Check, ChevronsUpDown, Search } from 'lucide-vue-next'
import { ref } from 'vue'
const frameworks = [
{ value: 'next.js', label: 'Next.js' },
{ value: 'sveltekit', label: 'SvelteKit' },
{ value: 'nuxt', label: 'Nuxt' },
{ value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' },
]
const value = ref<typeof frameworks[0]>()
</script>
<template>
<Combobox v-model="value" by="label">
<ComboboxAnchor as-child>
<ComboboxTrigger as-child>
<Button variant="outline" class="justify-between">
{{ value?.label ?? 'Select framework' }}
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</ComboboxTrigger>
</ComboboxAnchor>
<ComboboxList>
<div class="relative w-full max-w-sm items-center">
<ComboboxInput class="pl-9 focus-visible:ring-0 border-0 border-b rounded-none h-10" placeholder="Select framework..." />
<span class="absolute start-0 inset-y-0 flex items-center justify-center px-3">
<Search class="size-4 text-muted-foreground" />
</span>
</div>
<ComboboxEmpty>
No framework found.
</ComboboxEmpty>
<ComboboxGroup>
<ComboboxItem
v-for="framework in frameworks"
:key="framework.value"
:value="framework"
>
{{ framework.label }}
<ComboboxItemIndicator>
<Check :class="cn('ml-auto h-4 w-4')" />
</ComboboxItemIndicator>
</ComboboxItem>
</ComboboxGroup>
</ComboboxList>
</Combobox>
</template>

View File

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

View File

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

View File

@ -0,0 +1,94 @@
<script setup lang="ts">
import { Button } from '@/registry/default/ui/button'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/registry/default/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/registry/default/ui/popover'
import { ref } from 'vue'
interface Status {
value: string
label: string
}
const statuses: Status[] = [
{
value: 'backlog',
label: 'Backlog',
},
{
value: 'todo',
label: 'Todo',
},
{
value: 'in progress',
label: 'In Progress',
},
{
value: 'done',
label: 'Done',
},
{
value: 'canceled',
label: 'Canceled',
},
]
const open = ref(false)
const selectedStatus = ref<Status>()
</script>
<template>
<div class="flex items-center space-x-4">
<p class="text-sm text-muted-foreground">
Status
</p>
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button
variant="outline"
size="sm"
class="w-[150px] justify-start"
>
<template v-if="selectedStatus">
{{ selectedStatus?.label }}
</template>
<template v-else>
+ Set status
</template>
</Button>
</PopoverTrigger>
<PopoverContent class="p-0" side="right" align="start">
<Command>
<CommandInput placeholder="Change status..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup>
<CommandItem
v-for="status in statuses"
:key="status.value"
:value="status.value"
@select="() => {
selectedStatus = status
open = false
}"
>
{{ status.label }}
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
</template>

View File

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

View File

@ -0,0 +1,14 @@
<script setup lang="ts">
import { ComboboxRoot, type ComboboxRootEmits, type ComboboxRootProps, useForwardPropsEmits } from 'reka-ui'
const props = defineProps<ComboboxRootProps>()
const emits = defineEmits<ComboboxRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<ComboboxRoot v-bind="forwarded">
<slot />
</ComboboxRoot>
</template>

View File

@ -0,0 +1,25 @@
<script setup lang="ts">
import type { ComboboxAnchorProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxAnchor, useForwardProps } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxAnchorProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardProps(delegatedProps)
</script>
<template>
<ComboboxAnchor
v-bind="forwarded"
:class="cn('w-[200px]', props.class)"
>
<slot />
</ComboboxAnchor>
</template>

View File

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

View File

@ -0,0 +1,29 @@
<script setup lang="ts">
import type { ComboboxGroupProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxGroup, ComboboxLabel } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxGroupProps & {
class?: HTMLAttributes['class']
heading?: string
}>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<ComboboxGroup
v-bind="delegatedProps"
: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">
{{ heading }}
</ComboboxLabel>
<slot />
</ComboboxGroup>
</template>

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
import { ComboboxInput, type ComboboxInputProps, useForwardProps } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxInputProps & {
class?: HTMLAttributes['class']
}>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<ComboboxInput
v-bind="forwardedProps"
:class="cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)"
>
<slot />
</ComboboxInput>
</template>

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import type { ComboboxItemEmits, ComboboxItemProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxItem, useForwardPropsEmits } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxItemProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ComboboxItemEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<ComboboxItem
v-bind="forwarded"
:class="cn('relative flex cursor-default gap-2 select-none justify-between 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 [&_svg]:size-4 [&_svg]:shrink-0', props.class)"
>
<slot />
</ComboboxItem>
</template>

View File

@ -0,0 +1,34 @@
<script setup lang="ts">
import type { ComboboxContentEmits, ComboboxContentProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxContent, ComboboxPortal, ComboboxViewport, useForwardPropsEmits } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = withDefaults(defineProps<ComboboxContentProps & { class?: HTMLAttributes['class'] }>(), {
position: 'popper',
align: 'center',
sideOffset: 4,
})
const emits = defineEmits<ComboboxContentEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<ComboboxPortal>
<ComboboxContent
v-bind="forwarded"
:class="cn('z-50 w-[200px] rounded-md border bg-popover text-popover-foreground shadow-md outline-none 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)"
>
<ComboboxViewport>
<slot />
</ComboboxViewport>
</ComboboxContent>
</ComboboxPortal>
</template>

View File

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

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import type { ComboboxTriggerProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxTrigger, useForwardProps } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxTriggerProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardProps(delegatedProps)
</script>
<template>
<ComboboxTrigger
v-bind="forwarded"
:class="cn('', props.class)"
tabindex="0"
>
<slot />
</ComboboxTrigger>
</template>

View File

@ -0,0 +1,10 @@
export { default as Combobox } from './Combobox.vue'
export { default as ComboboxAnchor } from './ComboboxAnchor.vue'
export { default as ComboboxEmpty } from './ComboboxEmpty.vue'
export { default as ComboboxGroup } from './ComboboxGroup.vue'
export { default as ComboboxInput } from './ComboboxInput.vue'
export { default as ComboboxItem } from './ComboboxItem.vue'
export { default as ComboboxList } from './ComboboxList.vue'
export { default as ComboboxSeparator } from './ComboboxSeparator.vue'
export { ComboboxCancel, ComboboxItemIndicator, ComboboxTrigger } from 'reka-ui'

View File

@ -1,22 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Button } from '@/registry/new-york/ui/button' import { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from '@/registry/new-york/ui/combobox'
import { Check, Search } from 'lucide-vue-next'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/registry/new-york/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/registry/new-york/ui/popover'
import { Check, ChevronsUpDown } from 'lucide-vue-next'
import { ref } from 'vue'
const frameworks = [ const frameworks = [
{ value: 'next.js', label: 'Next.js' }, { value: 'next.js', label: 'Next.js' },
@ -25,54 +10,37 @@ const frameworks = [
{ value: 'remix', label: 'Remix' }, { value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' }, { value: 'astro', label: 'Astro' },
] ]
const open = ref(false)
const value = ref('')
</script> </script>
<template> <template>
<Popover v-model:open="open"> <Combobox by="label">
<PopoverTrigger as-child> <ComboboxAnchor>
<Button <div class="relative w-full max-w-sm items-center">
variant="outline" <ComboboxInput class="pl-9" :display-value="(val) => val?.label ?? ''" placeholder="Select framework..." />
role="combobox" <span class="absolute start-0 inset-y-0 flex items-center justify-center px-3">
:aria-expanded="open" <Search class="size-4 text-muted-foreground" />
class="w-[200px] justify-between" </span>
> </div>
{{ value </ComboboxAnchor>
? frameworks.find((framework) => framework.value === value)?.label
: "Select framework..." }} <ComboboxList>
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" /> <ComboboxEmpty>
</Button> No framework found.
</PopoverTrigger> </ComboboxEmpty>
<PopoverContent class="w-[200px] p-0">
<Command> <ComboboxGroup>
<CommandInput class="h-9" placeholder="Search framework..." /> <ComboboxItem
<CommandEmpty>No framework found.</CommandEmpty>
<CommandList>
<CommandGroup>
<CommandItem
v-for="framework in frameworks" v-for="framework in frameworks"
:key="framework.value" :key="framework.value"
:value="framework.value" :value="framework"
@select="(ev) => {
if (typeof ev.detail.value === 'string') {
value = ev.detail.value
}
open = false
}"
> >
{{ framework.label }} {{ framework.label }}
<Check
:class="cn( <ComboboxItemIndicator>
'ml-auto h-4 w-4', <Check :class="cn('ml-auto h-4 w-4')" />
value === framework.value ? 'opacity-100' : 'opacity-0', </ComboboxItemIndicator>
)" </ComboboxItem>
/> </ComboboxGroup>
</CommandItem> </ComboboxList>
</CommandGroup> </Combobox>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</template> </template>

View File

@ -1,14 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { Button } from '@/registry/new-york/ui/button' import { Button } from '@/registry/new-york/ui/button'
import { import { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/new-york/ui/combobox'
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/registry/new-york/ui/command'
import { import {
FormControl, FormControl,
FormDescription, FormDescription,
@ -18,11 +12,6 @@ import {
FormMessage, FormMessage,
} from '@/registry/new-york/ui/form' } from '@/registry/new-york/ui/form'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/registry/new-york/ui/popover'
import { toast } from '@/registry/new-york/ui/toast' import { toast } from '@/registry/new-york/ui/toast'
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import { Check, ChevronsUpDown } from 'lucide-vue-next' import { Check, ChevronsUpDown } from 'lucide-vue-next'
@ -48,7 +37,7 @@ const formSchema = toTypedSchema(z.object({
}), }),
})) }))
const { handleSubmit, setFieldValue, values } = useForm({ const { handleSubmit, setFieldValue } = useForm({
validationSchema: formSchema, validationSchema: formSchema,
initialValues: { initialValues: {
language: '', language: '',
@ -68,45 +57,43 @@ const onSubmit = handleSubmit((values) => {
<FormField name="language"> <FormField name="language">
<FormItem class="flex flex-col"> <FormItem class="flex flex-col">
<FormLabel>Language</FormLabel> <FormLabel>Language</FormLabel>
<Popover>
<PopoverTrigger as-child> <Combobox by="label">
<FormControl> <FormControl>
<Button <ComboboxAnchor>
variant="outline" <div class="relative w-full max-w-sm items-center">
role="combobox" <ComboboxInput :display-value="(val) => val?.label ?? ''" placeholder="Select framework..." />
:class="cn('w-[200px] justify-between', !values.language && 'text-muted-foreground')" <ComboboxTrigger class="absolute end-0 inset-y-0 flex items-center justify-center px-3">
> <ChevronsUpDown class="size-4 text-muted-foreground" />
{{ values.language ? languages.find( </ComboboxTrigger>
(language) => language.value === values.language, </div>
)?.label : 'Select language...' }} </ComboboxAnchor>
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl> </FormControl>
</PopoverTrigger>
<PopoverContent class="w-[200px] p-0"> <ComboboxList>
<Command> <ComboboxEmpty>
<CommandInput placeholder="Search language..." /> Nothing found.
<CommandEmpty>Nothing found.</CommandEmpty> </ComboboxEmpty>
<CommandList>
<CommandGroup> <ComboboxGroup>
<CommandItem <ComboboxItem
v-for="language in languages" v-for="language in languages"
:key="language.value" :key="language.value"
:value="language.label" :value="language"
@select="() => { @select="() => {
setFieldValue('language', language.value) setFieldValue('language', language.value)
}" }"
> >
{{ language.label }} {{ language.label }}
<Check
:class="cn('ml-auto h-4 w-4', language.value === values.language ? 'opacity-100' : 'opacity-0')" <ComboboxItemIndicator>
/> <Check :class="cn('ml-auto h-4 w-4')" />
</CommandItem> </ComboboxItemIndicator>
</CommandGroup> </ComboboxItem>
</CommandList> </ComboboxGroup>
</Command> </ComboboxList>
</PopoverContent> </Combobox>
</Popover>
<FormDescription> <FormDescription>
This is the language that will be used in the dashboard. This is the language that will be used in the dashboard.
</FormDescription> </FormDescription>

View File

@ -0,0 +1,58 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
import { Button } from '@/registry/new-york/ui/button'
import { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxTrigger } from '@/registry/new-york/ui/combobox'
import { Check, ChevronsUpDown, Search } from 'lucide-vue-next'
import { ref } from 'vue'
const frameworks = [
{ value: 'next.js', label: 'Next.js' },
{ value: 'sveltekit', label: 'SvelteKit' },
{ value: 'nuxt', label: 'Nuxt' },
{ value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' },
]
const value = ref<typeof frameworks[0]>()
</script>
<template>
<Combobox v-model="value" by="label">
<ComboboxAnchor as-child>
<ComboboxTrigger as-child>
<Button variant="outline" class="justify-between">
{{ value?.label ?? 'Select framework' }}
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</ComboboxTrigger>
</ComboboxAnchor>
<ComboboxList>
<div class="relative w-full max-w-sm items-center">
<ComboboxInput class="pl-9 focus-visible:ring-0 border-0 border-b rounded-none h-10" placeholder="Select framework..." />
<span class="absolute start-0 inset-y-0 flex items-center justify-center px-3">
<Search class="size-4 text-muted-foreground" />
</span>
</div>
<ComboboxEmpty>
No framework found.
</ComboboxEmpty>
<ComboboxGroup>
<ComboboxItem
v-for="framework in frameworks"
:key="framework.value"
:value="framework"
>
{{ framework.label }}
<ComboboxItemIndicator>
<Check :class="cn('ml-auto h-4 w-4')" />
</ComboboxItemIndicator>
</ComboboxItem>
</ComboboxGroup>
</ComboboxList>
</Combobox>
</template>

View File

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

View File

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

View File

@ -0,0 +1,94 @@
<script setup lang="ts">
import { Button } from '@/registry/new-york/ui/button'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/registry/new-york/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/registry/new-york/ui/popover'
import { ref } from 'vue'
interface Status {
value: string
label: string
}
const statuses: Status[] = [
{
value: 'backlog',
label: 'Backlog',
},
{
value: 'todo',
label: 'Todo',
},
{
value: 'in progress',
label: 'In Progress',
},
{
value: 'done',
label: 'Done',
},
{
value: 'canceled',
label: 'Canceled',
},
]
const open = ref(false)
const selectedStatus = ref<Status>()
</script>
<template>
<div class="flex items-center space-x-4">
<p class="text-sm text-muted-foreground">
Status
</p>
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button
variant="outline"
size="sm"
class="w-[150px] justify-start"
>
<template v-if="selectedStatus">
{{ selectedStatus?.label }}
</template>
<template v-else>
+ Set status
</template>
</Button>
</PopoverTrigger>
<PopoverContent class="p-0" side="right" align="start">
<Command>
<CommandInput placeholder="Change status..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup>
<CommandItem
v-for="status in statuses"
:key="status.value"
:value="status.value"
@select="() => {
selectedStatus = status
open = false
}"
>
{{ status.label }}
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
</template>

View File

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

View File

@ -0,0 +1,14 @@
<script setup lang="ts">
import { ComboboxRoot, type ComboboxRootEmits, type ComboboxRootProps, useForwardPropsEmits } from 'reka-ui'
const props = defineProps<ComboboxRootProps>()
const emits = defineEmits<ComboboxRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<ComboboxRoot v-bind="forwarded">
<slot />
</ComboboxRoot>
</template>

View File

@ -0,0 +1,25 @@
<script setup lang="ts">
import type { ComboboxAnchorProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxAnchor, useForwardProps } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxAnchorProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardProps(delegatedProps)
</script>
<template>
<ComboboxAnchor
v-bind="forwarded"
:class="cn('w-[200px]', props.class)"
>
<slot />
</ComboboxAnchor>
</template>

View File

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

View File

@ -0,0 +1,29 @@
<script setup lang="ts">
import type { ComboboxGroupProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxGroup, ComboboxLabel } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxGroupProps & {
class?: HTMLAttributes['class']
heading?: string
}>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<ComboboxGroup
v-bind="delegatedProps"
: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">
{{ heading }}
</ComboboxLabel>
<slot />
</ComboboxGroup>
</template>

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
import { ComboboxInput, type ComboboxInputProps, useForwardProps } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxInputProps & {
class?: HTMLAttributes['class']
}>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<ComboboxInput
v-bind="forwardedProps"
:class="cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)"
>
<slot />
</ComboboxInput>
</template>

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import type { ComboboxItemEmits, ComboboxItemProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxItem, useForwardPropsEmits } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxItemProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<ComboboxItemEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<ComboboxItem
v-bind="forwarded"
:class="cn('relative flex cursor-default gap-2 select-none justify-between 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 [&_svg]:size-4 [&_svg]:shrink-0', props.class)"
>
<slot />
</ComboboxItem>
</template>

View File

@ -0,0 +1,34 @@
<script setup lang="ts">
import type { ComboboxContentEmits, ComboboxContentProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxContent, ComboboxPortal, ComboboxViewport, useForwardPropsEmits } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = withDefaults(defineProps<ComboboxContentProps & { class?: HTMLAttributes['class'] }>(), {
position: 'popper',
align: 'center',
sideOffset: 4,
})
const emits = defineEmits<ComboboxContentEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<ComboboxPortal>
<ComboboxContent
v-bind="forwarded"
:class="cn('z-50 w-[200px] rounded-md border bg-popover text-popover-foreground shadow-md outline-none 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)"
>
<ComboboxViewport>
<slot />
</ComboboxViewport>
</ComboboxContent>
</ComboboxPortal>
</template>

View File

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

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import type { ComboboxTriggerProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { ComboboxTrigger, useForwardProps } from 'reka-ui'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<ComboboxTriggerProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardProps(delegatedProps)
</script>
<template>
<ComboboxTrigger
v-bind="forwarded"
:class="cn('', props.class)"
tabindex="0"
>
<slot />
</ComboboxTrigger>
</template>

View File

@ -0,0 +1,10 @@
export { default as Combobox } from './Combobox.vue'
export { default as ComboboxAnchor } from './ComboboxAnchor.vue'
export { default as ComboboxEmpty } from './ComboboxEmpty.vue'
export { default as ComboboxGroup } from './ComboboxGroup.vue'
export { default as ComboboxInput } from './ComboboxInput.vue'
export { default as ComboboxItem } from './ComboboxItem.vue'
export { default as ComboboxList } from './ComboboxList.vue'
export { default as ComboboxSeparator } from './ComboboxSeparator.vue'
export { ComboboxCancel, ComboboxItemIndicator, ComboboxTrigger } from 'reka-ui'