feat: add combobox, commands (#52)
* feat: add combobox, commands * chore: add new label
This commit is contained in:
parent
7abf5b29ef
commit
7dcc589a1d
4
.github/workflows/publish.yaml
vendored
4
.github/workflows/publish.yaml
vendored
|
|
@ -2,9 +2,13 @@ name: Publish www
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
paths:
|
paths:
|
||||||
- 'apps/www/**'
|
- 'apps/www/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
paths:
|
paths:
|
||||||
- 'apps/www/**'
|
- 'apps/www/**'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,18 +155,16 @@ export const docsConfig: DocsConfig = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Combobox',
|
title: 'Combobox',
|
||||||
disabled: true,
|
href: '/docs/components/combobox',
|
||||||
label: 'Soon',
|
label: 'New',
|
||||||
href: '#',
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Command',
|
||||||
|
href: '/docs/components/command',
|
||||||
|
label: 'New',
|
||||||
items: [],
|
items: [],
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// title: "Command",
|
|
||||||
// href: "#",
|
|
||||||
// label: "Soon",
|
|
||||||
// disabled: true,
|
|
||||||
// items: []
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
title: 'Context Menu',
|
title: 'Context Menu',
|
||||||
href: '/docs/components/context-menu',
|
href: '/docs/components/context-menu',
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
"@vue/compiler-dom": "^3.3.4",
|
"@vue/compiler-dom": "^3.3.4",
|
||||||
"autoprefixer": "^10.4.15",
|
"autoprefixer": "^10.4.15",
|
||||||
"lodash.template": "^4.5.0",
|
"lodash.template": "^4.5.0",
|
||||||
"radix-vue": "^0.1.34",
|
"radix-vue": "^0.2.2",
|
||||||
"rimraf": "^5.0.1",
|
"rimraf": "^5.0.1",
|
||||||
"tailwind-merge": "^1.14.0",
|
"tailwind-merge": "^1.14.0",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
|
|
|
||||||
89
apps/www/src/content/docs/components/combobox.md
Normal file
89
apps/www/src/content/docs/components/combobox.md
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
---
|
||||||
|
title: Combobox
|
||||||
|
description: Autocomplete input and command palette with a list of suggestions.
|
||||||
|
component: true
|
||||||
|
---
|
||||||
|
|
||||||
|
<ComponentPreview name="ComboboxDemo" />
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The Combobox is built using a composition of the `<Popover />` and the `<Command />` components.
|
||||||
|
|
||||||
|
See installation instructions for the [Popover](/docs/components/popover#installation) and the [Command](/docs/components/command#installation) components.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Check, ChevronsUpDown } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/popover'
|
||||||
|
|
||||||
|
const frameworks = [
|
||||||
|
{ value: 'next.js', label: 'Next.js' },
|
||||||
|
{ value: 'sveltekit', label: 'SvelteKit' },
|
||||||
|
{ value: 'nuxt.js', label: 'Nuxt.js' },
|
||||||
|
{ value: 'remix', label: 'Remix' },
|
||||||
|
{ value: 'astro', label: 'Astro' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const value = ref({})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Popover v-model:open="open">
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
:aria-expanded="open"
|
||||||
|
class="w-[200px] justify-between"
|
||||||
|
>
|
||||||
|
{{ value ? frameworks.find((framework) => framework.value === value)?.label : 'Select framework...' }}
|
||||||
|
|
||||||
|
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-[200px] p-0">
|
||||||
|
<Command v-model="value">
|
||||||
|
<CommandInput placeholder="Search framework..." />
|
||||||
|
<CommandEmpty>No framework found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
v-for="framework in frameworks"
|
||||||
|
:key="framework.value"
|
||||||
|
:value="framework"
|
||||||
|
@select="open = false"
|
||||||
|
>
|
||||||
|
<Check
|
||||||
|
:class="cn(
|
||||||
|
'mr-2 h-4 w-4',
|
||||||
|
value === framework.value ? 'opacity-100' : 'opacity-0',
|
||||||
|
)"
|
||||||
|
/>
|
||||||
|
{{ framework.label }}
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
65
apps/www/src/content/docs/components/command.md
Normal file
65
apps/www/src/content/docs/components/command.md
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
---
|
||||||
|
title: Command
|
||||||
|
description: Displays a list of options for the user to pick from—triggered by a button.
|
||||||
|
source: apps/www/src/lib/registry/default/ui/popover
|
||||||
|
primitive: https://www.radix-vue.com/components/popover.html
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
<ComponentPreview name="CommandDemo" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx shadcn-vue@latest add command
|
||||||
|
```
|
||||||
|
|
||||||
|
<ManualInstall>
|
||||||
|
|
||||||
|
1. Install `radix-vue`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install radix-vue
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Copy and paste the component source files linked at the top of this page into your project.
|
||||||
|
</ManualInstall>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandDialog,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
CommandSeparator,
|
||||||
|
CommandShortcut,
|
||||||
|
} from '@/components/ui/command'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup heading="Suggestions">
|
||||||
|
<CommandItem>Calendar</CommandItem>
|
||||||
|
<CommandItem>Search Emoji</CommandItem>
|
||||||
|
<CommandItem>Calculator</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandGroup heading="Settings">
|
||||||
|
<CommandItem>Profile</CommandItem>
|
||||||
|
<CommandItem>Billing</CommandItem>
|
||||||
|
<CommandItem>Settings</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</template>```
|
||||||
70
apps/www/src/lib/registry/default/example/ComboboxDemo.vue
Normal file
70
apps/www/src/lib/registry/default/example/ComboboxDemo.vue
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Check, ChevronsUpDown } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/popover'
|
||||||
|
|
||||||
|
const frameworks = [
|
||||||
|
{ value: 'next.js', label: 'Next.js' },
|
||||||
|
{ value: 'sveltekit', label: 'SvelteKit' },
|
||||||
|
{ value: 'nuxt.js', label: 'Nuxt.js' },
|
||||||
|
{ value: 'remix', label: 'Remix' },
|
||||||
|
{ value: 'astro', label: 'Astro' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const value = ref<typeof frameworks[number]>()
|
||||||
|
|
||||||
|
const filterFunction = (list: typeof frameworks, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Popover v-model:open="open">
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
:aria-expanded="open"
|
||||||
|
class="w-[200px] justify-between"
|
||||||
|
>
|
||||||
|
{{ value ? value.label : 'Select framework...' }}
|
||||||
|
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-[200px] p-0">
|
||||||
|
<Command v-model="value" :filter-function="filterFunction">
|
||||||
|
<CommandInput placeholder="Search framework..." />
|
||||||
|
<CommandEmpty>No framework found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
v-for="framework in frameworks"
|
||||||
|
:key="framework.value"
|
||||||
|
:value="framework"
|
||||||
|
@select="open = false"
|
||||||
|
>
|
||||||
|
<Check
|
||||||
|
:class="cn(
|
||||||
|
'mr-2 h-4 w-4',
|
||||||
|
value?.value === framework.value ? 'opacity-100' : 'opacity-0',
|
||||||
|
)"
|
||||||
|
/>
|
||||||
|
{{ framework.label }}
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
62
apps/www/src/lib/registry/default/example/CommandDemo.vue
Normal file
62
apps/www/src/lib/registry/default/example/CommandDemo.vue
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Calculator,
|
||||||
|
Calendar,
|
||||||
|
CreditCard,
|
||||||
|
Settings,
|
||||||
|
Smile,
|
||||||
|
User,
|
||||||
|
} from 'lucide-vue-next'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
CommandSeparator,
|
||||||
|
CommandShortcut,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Command class="rounded-lg border shadow-md max-w-[450px]">
|
||||||
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup heading="Suggestions">
|
||||||
|
<CommandItem value="Calendar">
|
||||||
|
<Calendar class="mr-2 h-4 w-4" />
|
||||||
|
<span>Calendar</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="Search Emoji">
|
||||||
|
<Smile class="mr-2 h-4 w-4" />
|
||||||
|
<span>Search Emoji</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="Calculator">
|
||||||
|
<Calculator class="mr-2 h-4 w-4" />
|
||||||
|
<span>Calculator</span>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandGroup heading="Settings">
|
||||||
|
<CommandItem value="Profile">
|
||||||
|
<User class="mr-2 h-4 w-4" />
|
||||||
|
<span>Profile</span>
|
||||||
|
<CommandShortcut>⌘P</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="Billing">
|
||||||
|
<CreditCard class="mr-2 h-4 w-4" />
|
||||||
|
<span>Billing</span>
|
||||||
|
<CommandShortcut>⌘B</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="Settings">
|
||||||
|
<Settings class="mr-2 h-4 w-4" />
|
||||||
|
<span>Settings</span>
|
||||||
|
<CommandShortcut>⌘S</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</template>
|
||||||
21
apps/www/src/lib/registry/default/ui/command/Command.vue
Normal file
21
apps/www/src/lib/registry/default/ui/command/Command.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxRootEmits, ComboboxRootProps } from 'radix-vue'
|
||||||
|
import { ComboboxRoot } from 'radix-vue'
|
||||||
|
import { cn, useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxRootProps>()
|
||||||
|
const emits = defineEmits<ComboboxRootEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxRoot
|
||||||
|
v-bind="{ ...props, ...emitsAsProps }"
|
||||||
|
:open="true"
|
||||||
|
:model-value="''"
|
||||||
|
:class="cn('flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ComboboxRoot>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { DialogRootEmits, DialogRootProps } from 'radix-vue'
|
||||||
|
import Command from './Command.vue'
|
||||||
|
import { Dialog, DialogContent } from '@/lib/registry/default/ui/dialog'
|
||||||
|
import { useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<DialogRootProps>()
|
||||||
|
const emits = defineEmits<DialogRootEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog v-bind="{ ...props, ...emitsAsProps }">
|
||||||
|
<DialogContent class="overflow-hidden p-0 shadow-lg">
|
||||||
|
<Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||||
|
<slot />
|
||||||
|
</Command>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxEmptyProps } from 'radix-vue'
|
||||||
|
import { ComboboxEmpty } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxEmptyProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxEmpty v-bind="props" :class="cn('py-6 text-center text-sm', $attrs.class ?? '')">
|
||||||
|
<slot />
|
||||||
|
</ComboboxEmpty>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxGroupProps } from 'radix-vue'
|
||||||
|
import { ComboboxGroup, ComboboxLabel } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxGroupProps & {
|
||||||
|
heading?: string
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxGroup
|
||||||
|
v-bind="props"
|
||||||
|
:class="cn('overflow-hidden p-1 text-foreground', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<ComboboxLabel v-if="heading" class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
|
||||||
|
{{ heading }}
|
||||||
|
</ComboboxLabel>
|
||||||
|
<slot />
|
||||||
|
</ComboboxGroup>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Search } from 'lucide-vue-next'
|
||||||
|
import { ComboboxInput, type ComboboxInputProps } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxInputProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
inheritAttrs: false,
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center border-b px-3" cmdk-input-wrapper>
|
||||||
|
<Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
<ComboboxInput
|
||||||
|
v-bind="{ ...props, ...$attrs }"
|
||||||
|
auto-focus
|
||||||
|
:class="cn('flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', $attrs.class ?? '')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
19
apps/www/src/lib/registry/default/ui/command/CommandItem.vue
Normal file
19
apps/www/src/lib/registry/default/ui/command/CommandItem.vue
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxItemEmits, ComboboxItemProps } from 'radix-vue'
|
||||||
|
import { ComboboxItem } from 'radix-vue'
|
||||||
|
import { cn, useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxItemProps>()
|
||||||
|
const emits = defineEmits<ComboboxItemEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxItem
|
||||||
|
v-bind="{ ...props, ...emitsAsProps }"
|
||||||
|
:class="cn('relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ComboboxItem>
|
||||||
|
</template>
|
||||||
16
apps/www/src/lib/registry/default/ui/command/CommandList.vue
Normal file
16
apps/www/src/lib/registry/default/ui/command/CommandList.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxContentEmits, ComboboxContentProps } from 'radix-vue'
|
||||||
|
import { ComboboxContent } from 'radix-vue'
|
||||||
|
import { cn, useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxContentProps>()
|
||||||
|
const emits = defineEmits<ComboboxContentEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxContent v-bind="{ ...props, ...emitsAsProps }" :class="cn('max-h-[300px] overflow-y-auto overflow-x-hidden', $attrs.class ?? '')">
|
||||||
|
<slot />
|
||||||
|
</ComboboxContent>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxSeparatorProps } from 'radix-vue'
|
||||||
|
import { ComboboxSeparator } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxSeparatorProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxSeparator
|
||||||
|
v-bind="props"
|
||||||
|
:class="cn('-mx-1 h-px bg-border', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ComboboxSeparator>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span :class="cn('ml-auto text-xs tracking-widest text-muted-foreground', $attrs.class ?? '')">
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
9
apps/www/src/lib/registry/default/ui/command/index.ts
Normal file
9
apps/www/src/lib/registry/default/ui/command/index.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
export { default as Command } from './Command.vue'
|
||||||
|
export { default as CommandDialog } from './CommandDialog.vue'
|
||||||
|
export { default as CommandEmpty } from './CommandEmpty.vue'
|
||||||
|
export { default as CommandGroup } from './CommandGroup.vue'
|
||||||
|
export { default as CommandInput } from './CommandInput.vue'
|
||||||
|
export { default as CommandItem } from './CommandItem.vue'
|
||||||
|
export { default as CommandList } from './CommandList.vue'
|
||||||
|
export { default as CommandSeparator } from './CommandSeparator.vue'
|
||||||
|
export { default as CommandShortcut } from './CommandShortcut.vue'
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PopoverRoot, type PopoverRootProps } from 'radix-vue'
|
import { PopoverRoot } from 'radix-vue'
|
||||||
|
import type { PopoverRootEmits, PopoverRootProps } from 'radix-vue'
|
||||||
|
import { useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<PopoverRootProps>()
|
const props = defineProps<PopoverRootProps>()
|
||||||
|
const emits = defineEmits<PopoverRootEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PopoverRoot v-bind="props">
|
<PopoverRoot v-bind="{ ...props, ...emitsAsProps }">
|
||||||
<slot />
|
<slot />
|
||||||
</PopoverRoot>
|
</PopoverRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
71
apps/www/src/lib/registry/new-york/example/ComboboxDemo.vue
Normal file
71
apps/www/src/lib/registry/new-york/example/ComboboxDemo.vue
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import Check from '~icons/radix-icons/check'
|
||||||
|
import CaretSort from '~icons/radix-icons/caret-sort'
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Button } from '@/lib/registry/default/ui/button'
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
} from '@/lib/registry/default/ui/command'
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/lib/registry/default/ui/popover'
|
||||||
|
|
||||||
|
const frameworks = [
|
||||||
|
{ value: 'next.js', label: 'Next.js' },
|
||||||
|
{ value: 'sveltekit', label: 'SvelteKit' },
|
||||||
|
{ value: 'nuxt.js', label: 'Nuxt.js' },
|
||||||
|
{ value: 'remix', label: 'Remix' },
|
||||||
|
{ value: 'astro', label: 'Astro' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const value = ref<typeof frameworks[number]>()
|
||||||
|
|
||||||
|
const filterFunction = (list: typeof frameworks, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Popover v-model:open="open">
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
:aria-expanded="open"
|
||||||
|
class="w-[200px] justify-between"
|
||||||
|
>
|
||||||
|
{{ value ? value.label : 'Select framework...' }}
|
||||||
|
<CaretSort class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-[200px] p-0">
|
||||||
|
<Command v-model="value" :filter-function="filterFunction">
|
||||||
|
<CommandInput class="h-9" placeholder="Search framework..." />
|
||||||
|
<CommandEmpty>No framework found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
v-for="framework in frameworks"
|
||||||
|
:key="framework.value"
|
||||||
|
:value="framework"
|
||||||
|
@select="open = false"
|
||||||
|
>
|
||||||
|
{{ framework.label }}
|
||||||
|
<Check
|
||||||
|
:class="cn(
|
||||||
|
'ml-auto h-4 w-4',
|
||||||
|
value?.value === framework.value ? 'opacity-100' : 'opacity-0',
|
||||||
|
)"
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
62
apps/www/src/lib/registry/new-york/example/CommandDemo.vue
Normal file
62
apps/www/src/lib/registry/new-york/example/CommandDemo.vue
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Calculator,
|
||||||
|
Calendar,
|
||||||
|
CreditCard,
|
||||||
|
Settings,
|
||||||
|
Smile,
|
||||||
|
User,
|
||||||
|
} from 'lucide-vue-next'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
CommandSeparator,
|
||||||
|
CommandShortcut,
|
||||||
|
} from '@/lib/registry/new-york/ui/command'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Command class="rounded-lg border shadow-md max-w-[450px]">
|
||||||
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup heading="Suggestions">
|
||||||
|
<CommandItem value="Calendar">
|
||||||
|
<Calendar class="mr-2 h-4 w-4" />
|
||||||
|
<span>Calendar</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="Search Emoji">
|
||||||
|
<Smile class="mr-2 h-4 w-4" />
|
||||||
|
<span>Search Emoji</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="Calculator">
|
||||||
|
<Calculator class="mr-2 h-4 w-4" />
|
||||||
|
<span>Calculator</span>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandGroup heading="Settings">
|
||||||
|
<CommandItem value="Profile">
|
||||||
|
<User class="mr-2 h-4 w-4" />
|
||||||
|
<span>Profile</span>
|
||||||
|
<CommandShortcut>⌘P</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="Billing">
|
||||||
|
<CreditCard class="mr-2 h-4 w-4" />
|
||||||
|
<span>Billing</span>
|
||||||
|
<CommandShortcut>⌘B</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem value="Settings">
|
||||||
|
<Settings class="mr-2 h-4 w-4" />
|
||||||
|
<span>Settings</span>
|
||||||
|
<CommandShortcut>⌘S</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</template>
|
||||||
20
apps/www/src/lib/registry/new-york/ui/command/Command.vue
Normal file
20
apps/www/src/lib/registry/new-york/ui/command/Command.vue
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxRootEmits, ComboboxRootProps } from 'radix-vue'
|
||||||
|
import { ComboboxRoot } from 'radix-vue'
|
||||||
|
import { cn, useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxRootProps>()
|
||||||
|
const emits = defineEmits<ComboboxRootEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxRoot
|
||||||
|
v-bind="{ ...props, ...emitsAsProps }"
|
||||||
|
:open="true"
|
||||||
|
:class="cn('flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ComboboxRoot>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { DialogRootEmits, DialogRootProps } from 'radix-vue'
|
||||||
|
import Command from './Command.vue'
|
||||||
|
import { Dialog, DialogContent } from '@/lib/registry/new-york/ui/dialog'
|
||||||
|
import { useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<DialogRootProps>()
|
||||||
|
const emits = defineEmits<DialogRootEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog v-bind="{ ...props, ...emitsAsProps }">
|
||||||
|
<DialogContent class="overflow-hidden p-0 shadow-lg">
|
||||||
|
<Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||||
|
<slot />
|
||||||
|
</Command>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxEmptyProps } from 'radix-vue'
|
||||||
|
import { ComboboxEmpty } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxEmptyProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxEmpty v-bind="props" :class="cn('py-6 text-center text-sm', $attrs.class ?? '')">
|
||||||
|
<slot />
|
||||||
|
</ComboboxEmpty>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxGroupProps } from 'radix-vue'
|
||||||
|
import { ComboboxGroup, ComboboxLabel } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxGroupProps & {
|
||||||
|
heading?: string
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxGroup
|
||||||
|
v-bind="props"
|
||||||
|
:class="cn('overflow-hidden p-1 text-foreground', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<ComboboxLabel class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
|
||||||
|
{{ heading }}
|
||||||
|
</ComboboxLabel>
|
||||||
|
<slot />
|
||||||
|
</ComboboxGroup>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Search } from 'lucide-vue-next'
|
||||||
|
import { ComboboxInput, type ComboboxInputProps } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxInputProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
inheritAttrs: false,
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center border-b px-3" cmdk-input-wrapper>
|
||||||
|
<Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
<ComboboxInput
|
||||||
|
v-bind="{ ...props, ...$attrs }"
|
||||||
|
auto-focus
|
||||||
|
:class="cn('flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', $attrs.class ?? '')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxItemEmits, ComboboxItemProps } from 'radix-vue'
|
||||||
|
import { ComboboxItem } from 'radix-vue'
|
||||||
|
import { cn, useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxItemProps>()
|
||||||
|
const emits = defineEmits<ComboboxItemEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxItem
|
||||||
|
v-bind="{ ...props, ...emitsAsProps }"
|
||||||
|
:class="cn('relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ComboboxItem>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxContentEmits, ComboboxContentProps } from 'radix-vue'
|
||||||
|
import { ComboboxContent } from 'radix-vue'
|
||||||
|
import { cn, useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxContentProps>()
|
||||||
|
const emits = defineEmits<ComboboxContentEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxContent v-bind="{ ...props, ...emitsAsProps }" :class="cn('max-h-[300px] overflow-y-auto overflow-x-hidden', $attrs.class ?? '')">
|
||||||
|
<slot />
|
||||||
|
</ComboboxContent>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ComboboxSeparatorProps } from 'radix-vue'
|
||||||
|
import { ComboboxSeparator } from 'radix-vue'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<ComboboxSeparatorProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ComboboxSeparator
|
||||||
|
v-bind="props"
|
||||||
|
:class="cn('-mx-1 h-px bg-border', $attrs.class ?? '')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ComboboxSeparator>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span :class="cn('ml-auto text-xs tracking-widest text-muted-foreground', $attrs.class ?? '')">
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
9
apps/www/src/lib/registry/new-york/ui/command/index.ts
Normal file
9
apps/www/src/lib/registry/new-york/ui/command/index.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
export { default as Command } from './Command.vue'
|
||||||
|
export { default as CommandDialog } from './CommandDialog.vue'
|
||||||
|
export { default as CommandEmpty } from './CommandEmpty.vue'
|
||||||
|
export { default as CommandGroup } from './CommandGroup.vue'
|
||||||
|
export { default as CommandInput } from './CommandInput.vue'
|
||||||
|
export { default as CommandItem } from './CommandItem.vue'
|
||||||
|
export { default as CommandList } from './CommandList.vue'
|
||||||
|
export { default as CommandSeparator } from './CommandSeparator.vue'
|
||||||
|
export { default as CommandShortcut } from './CommandShortcut.vue'
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PopoverRoot, type PopoverRootProps } from 'radix-vue'
|
import { PopoverRoot } from 'radix-vue'
|
||||||
|
import type { PopoverRootEmits, PopoverRootProps } from 'radix-vue'
|
||||||
|
import { useEmitAsProps } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<PopoverRootProps>()
|
const props = defineProps<PopoverRootProps>()
|
||||||
|
const emits = defineEmits<PopoverRootEmits>()
|
||||||
|
|
||||||
|
const emitsAsProps = useEmitAsProps(emits)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PopoverRoot v-bind="props">
|
<PopoverRoot v-bind="{ ...props, ...emitsAsProps }">
|
||||||
<slot />
|
<slot />
|
||||||
</PopoverRoot>
|
</PopoverRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,8 @@ importers:
|
||||||
specifier: ^4.5.0
|
specifier: ^4.5.0
|
||||||
version: 4.5.0
|
version: 4.5.0
|
||||||
radix-vue:
|
radix-vue:
|
||||||
specifier: ^0.1.34
|
specifier: ^0.2.2
|
||||||
version: 0.1.34(vue@3.3.4)
|
version: 0.2.2(vue@3.3.4)
|
||||||
rimraf:
|
rimraf:
|
||||||
specifier: ^5.0.1
|
specifier: ^5.0.1
|
||||||
version: 5.0.1
|
version: 5.0.1
|
||||||
|
|
@ -6393,8 +6393,8 @@ packages:
|
||||||
- vue
|
- vue
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/radix-vue@0.1.33(vue@3.3.4):
|
/radix-vue@0.2.2(vue@3.3.4):
|
||||||
resolution: {integrity: sha512-2nwcjrXNXML//iOxUeqyh2bBXQtQt3Xy1NqM2oHZJTNNEaL2QnMjlSN51DXMzx3QB2uex2IwqA1cFTHbJ34tyw==}
|
resolution: {integrity: sha512-eW1VpctGY1JPafNHzlelxt/7dx340PZgj078bLsdMQDqq9lfN7tKHaj+pSyH7tdT3OtFsxKAmO65rnlaTAUqAg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@floating-ui/dom': 1.5.1
|
'@floating-ui/dom': 1.5.1
|
||||||
'@floating-ui/vue': 1.0.2(vue@3.3.4)
|
'@floating-ui/vue': 1.0.2(vue@3.3.4)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user