feat: add Dropdown Menu component
This commit is contained in:
parent
71bff69c95
commit
2cf2dca134
|
|
@ -4,6 +4,7 @@ import PopoverDemo from '@/registry/default/examples/PopoverDemo.vue'
|
||||||
import DialogDemo from '@/registry/default/examples/DialogDemo.vue'
|
import DialogDemo from '@/registry/default/examples/DialogDemo.vue'
|
||||||
import AlertDialogDemo from '@/registry/default/examples/AlertDialogDemo.vue'
|
import AlertDialogDemo from '@/registry/default/examples/AlertDialogDemo.vue'
|
||||||
import SelectDemo from '@/registry/default/examples/SelectDemo.vue'
|
import SelectDemo from '@/registry/default/examples/SelectDemo.vue'
|
||||||
|
import DropdownMenuDemo from '@/registry/default/examples/DropdownMenuDemo.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -17,5 +18,7 @@ import SelectDemo from '@/registry/default/examples/SelectDemo.vue'
|
||||||
<AlertDialogDemo />
|
<AlertDialogDemo />
|
||||||
|
|
||||||
<SelectDemo />
|
<SelectDemo />
|
||||||
|
|
||||||
|
<DropdownMenuDemo />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
110
apps/www/src/lib/registry/default/examples/DropdownMenuDemo.vue
Normal file
110
apps/www/src/lib/registry/default/examples/DropdownMenuDemo.vue
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Button } from "@/registry/default/ui/button";
|
||||||
|
import { DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuPortal, DropdownMenuSubContent, DropdownMenu
|
||||||
|
} from "@/registry/default/ui/dropdown-menu";
|
||||||
|
import {
|
||||||
|
Cloud,
|
||||||
|
CreditCard,
|
||||||
|
Github,
|
||||||
|
Keyboard,
|
||||||
|
LifeBuoy,
|
||||||
|
LogOut,
|
||||||
|
Mail,
|
||||||
|
MessageSquare,
|
||||||
|
Plus,
|
||||||
|
PlusCircle,
|
||||||
|
Settings,
|
||||||
|
User,
|
||||||
|
UserPlus,
|
||||||
|
Users,
|
||||||
|
} from "lucide-vue-next"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline">Open</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent class="w-56">
|
||||||
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<User class="mr-2 h-4 w-4" />
|
||||||
|
<span>Profile</span>
|
||||||
|
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<CreditCard class="mr-2 h-4 w-4" />
|
||||||
|
<span>Billing</span>
|
||||||
|
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<Settings class="mr-2 h-4 w-4" />
|
||||||
|
<span>Settings</span>
|
||||||
|
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<Keyboard class="mr-2 h-4 w-4" />
|
||||||
|
<span>Keyboard shortcuts</span>
|
||||||
|
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<Users class="mr-2 h-4 w-4" />
|
||||||
|
<span>Team</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger>
|
||||||
|
<UserPlus class="mr-2 h-4 w-4" />
|
||||||
|
<span>Invite users</span>
|
||||||
|
</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuPortal>
|
||||||
|
<DropdownMenuSubContent>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<Mail class="mr-2 h-4 w-4" />
|
||||||
|
<span>Email</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<MessageSquare class="mr-2 h-4 w-4" />
|
||||||
|
<span>Message</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<PlusCircle class="mr-2 h-4 w-4" />
|
||||||
|
<span>More...</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuPortal>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<Plus class="mr-2 h-4 w-4" />
|
||||||
|
<span>New Team</span>
|
||||||
|
<DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<Github class="mr-2 h-4 w-4" />
|
||||||
|
<span>GitHub</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<LifeBuoy class="mr-2 h-4 w-4" />
|
||||||
|
<span>Support</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem disabled>
|
||||||
|
<Cloud class="mr-2 h-4 w-4" />
|
||||||
|
<span>API</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<LogOut class="mr-2 h-4 w-4" />
|
||||||
|
<span>Log out</span>
|
||||||
|
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</template>
|
||||||
256
apps/www/src/lib/registry/default/ui/dropdown-menu.tsx
Normal file
256
apps/www/src/lib/registry/default/ui/dropdown-menu.tsx
Normal file
|
|
@ -0,0 +1,256 @@
|
||||||
|
import {
|
||||||
|
DropdownMenuCheckboxItem as DropdownMenuCheckboxItemPrimitive, type DropdownMenuCheckboxItemProps,
|
||||||
|
DropdownMenuContent as DropdownMenuContentPrimitive, type DropdownMenuContentProps,
|
||||||
|
DropdownMenuGroup as DropdownMenuGroupPrimitive,
|
||||||
|
DropdownMenuItemIndicator as DropdownMenuItemIndicatorPrimitive,
|
||||||
|
DropdownMenuItem as DropdownMenuItemPrimitive, type DropdownMenuItemProps,
|
||||||
|
DropdownMenuLabel as DropdownMenuLabelPrimitive, type DropdownMenuLabelProps,
|
||||||
|
DropdownMenuPortal as DropdownMenuPortalPrimitive,
|
||||||
|
DropdownMenuRadioGroup as DropdownMenuRadioGroupPrimitive,
|
||||||
|
DropdownMenuRadioItem as DropdownMenuRadioItemPrimitive, type DropdownMenuRadioItemProps,
|
||||||
|
DropdownMenuRoot as DropdownMenuRootPrimitive,
|
||||||
|
DropdownMenuSeparator as DropdownMenuSeparatorPrimitive, type DropdownMenuSeparatorProps,
|
||||||
|
DropdownMenuSubContent as DropdownMenuSubContentPrimitive, type DropdownMenuSubContentProps,
|
||||||
|
DropdownMenuSub as DropdownMenuSubPrimitive,
|
||||||
|
DropdownMenuSubTrigger as DropdownMenuSubTriggerPrimitive, type DropdownMenuSubTriggerProps,
|
||||||
|
DropdownMenuTrigger as DropdownMenuTriggerPrimitive,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import { Check, ChevronRight, Circle } from 'lucide-vue-next'
|
||||||
|
import { cn, convertToComponent, useEmitAsProps } from '@/utils'
|
||||||
|
|
||||||
|
const ChevronRightIcon = convertToComponent(ChevronRight)
|
||||||
|
const CheckIcon = convertToComponent(Check)
|
||||||
|
const CircleIcon = convertToComponent(Circle)
|
||||||
|
|
||||||
|
const DropdownMenu = DropdownMenuRootPrimitive
|
||||||
|
const DropdownMenuTrigger = DropdownMenuTriggerPrimitive
|
||||||
|
const DropdownMenuGroup = DropdownMenuGroupPrimitive
|
||||||
|
const DropdownMenuPortal = DropdownMenuPortalPrimitive
|
||||||
|
const DropdownMenuSub = DropdownMenuSubPrimitive
|
||||||
|
const DropdownMenuRadioGroup = DropdownMenuRadioGroupPrimitive
|
||||||
|
|
||||||
|
const DropdownMenuSubTrigger = defineComponent<
|
||||||
|
DropdownMenuSubTriggerProps & {
|
||||||
|
inset?: boolean
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
(props, { attrs, slots }) => {
|
||||||
|
return () => (
|
||||||
|
<DropdownMenuSubTriggerPrimitive
|
||||||
|
class={cn(
|
||||||
|
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
|
||||||
|
props.inset && 'pl-8',
|
||||||
|
attrs.class ?? '',
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{slots.default?.()}
|
||||||
|
<ChevronRightIcon class="ml-auto w-4 h-4" />
|
||||||
|
</DropdownMenuSubTriggerPrimitive>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DropdownMenuSubTrigger',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const DropdownMenuSubContent = defineComponent<DropdownMenuSubContentProps>(
|
||||||
|
(props, { attrs, emit, slots }) => {
|
||||||
|
const emitsAsProps = useEmitAsProps(emit)
|
||||||
|
return () => (
|
||||||
|
<DropdownMenuSubContentPrimitive
|
||||||
|
class={cn(
|
||||||
|
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
|
attrs.class ?? '',
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
{...emitsAsProps}
|
||||||
|
>
|
||||||
|
{slots.default?.()}
|
||||||
|
</DropdownMenuSubContentPrimitive>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DropdownMenuSubContent',
|
||||||
|
emits: DropdownMenuSubContentPrimitive.emits,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const DropdownMenuContent = defineComponent<DropdownMenuContentProps>(
|
||||||
|
(props, { attrs, emit, slots }) => {
|
||||||
|
const emitsAsProps = useEmitAsProps(emit)
|
||||||
|
return () => (
|
||||||
|
<DropdownMenuPortalPrimitive>
|
||||||
|
<DropdownMenuContentPrimitive
|
||||||
|
sideOffset={props.sideOffset ?? 4}
|
||||||
|
class={cn(
|
||||||
|
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
|
||||||
|
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
|
attrs.class ?? '',
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
{...emitsAsProps}
|
||||||
|
>
|
||||||
|
{slots.default?.()}
|
||||||
|
</DropdownMenuContentPrimitive>
|
||||||
|
</DropdownMenuPortalPrimitive>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DropdownMenuContent',
|
||||||
|
emits: DropdownMenuContentPrimitive.emits,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const DropdownMenuItem = defineComponent<
|
||||||
|
DropdownMenuItemProps & {
|
||||||
|
inset?: boolean
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
(props, { attrs, emit, slots }) => {
|
||||||
|
const emitsAsProps = useEmitAsProps(emit)
|
||||||
|
return () => (
|
||||||
|
<DropdownMenuItemPrimitive
|
||||||
|
class={cn(
|
||||||
|
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
|
props.inset && 'pl-8',
|
||||||
|
attrs.class ?? '',
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
{...emitsAsProps}
|
||||||
|
>
|
||||||
|
{slots.default?.()}
|
||||||
|
</DropdownMenuItemPrimitive>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DropdownMenuItem',
|
||||||
|
emits: DropdownMenuItemPrimitive.emits,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const DropdownMenuCheckboxItem = defineComponent<DropdownMenuCheckboxItemProps>(
|
||||||
|
(props, { attrs, slots, emit }) => {
|
||||||
|
const emitsAsProps = useEmitAsProps(emit)
|
||||||
|
return () => (
|
||||||
|
<DropdownMenuCheckboxItemPrimitive
|
||||||
|
class={cn(
|
||||||
|
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
|
attrs.class ?? '',
|
||||||
|
)}
|
||||||
|
checked={props.checked}
|
||||||
|
{...props}
|
||||||
|
{...emitsAsProps}
|
||||||
|
>
|
||||||
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<DropdownMenuItemIndicatorPrimitive>
|
||||||
|
<CheckIcon class="h-4 w-4" />
|
||||||
|
</DropdownMenuItemIndicatorPrimitive>
|
||||||
|
</span>
|
||||||
|
{slots.default?.()}
|
||||||
|
</DropdownMenuCheckboxItemPrimitive>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{ name: 'DropdownMenuCheckboxItem', emits: DropdownMenuItemPrimitive.emits },
|
||||||
|
)
|
||||||
|
|
||||||
|
const DropdownMenuRadioItem = defineComponent<DropdownMenuRadioItemProps>(
|
||||||
|
(props, { attrs, slots, emit }) => {
|
||||||
|
const emitsAsProps = useEmitAsProps(emit)
|
||||||
|
return () => (
|
||||||
|
<DropdownMenuRadioItemPrimitive
|
||||||
|
class={cn(
|
||||||
|
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
|
attrs.class ?? '',
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
{...emitsAsProps}
|
||||||
|
>
|
||||||
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<DropdownMenuItemIndicatorPrimitive>
|
||||||
|
<CircleIcon class="h-4 w-4 fill-current" />
|
||||||
|
</DropdownMenuItemIndicatorPrimitive>
|
||||||
|
</span>
|
||||||
|
{slots.default?.()}
|
||||||
|
</DropdownMenuRadioItemPrimitive>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DropdownMenuRadioItem',
|
||||||
|
emits: DropdownMenuRadioItemPrimitive.emits,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const DropdownMenuLabel = defineComponent<
|
||||||
|
DropdownMenuLabelProps & {
|
||||||
|
inset?: boolean
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
(props, { attrs, slots }) => {
|
||||||
|
return () => (
|
||||||
|
<DropdownMenuLabelPrimitive
|
||||||
|
class={cn(
|
||||||
|
'px-2 py-1.5 text-sm font-semibold',
|
||||||
|
props.inset && 'pl-8',
|
||||||
|
attrs.class ?? '',
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{slots.default?.()}
|
||||||
|
</DropdownMenuLabelPrimitive>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DropdownMenuLabel',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const DropdownMenuSeparator = defineComponent<DropdownMenuSeparatorProps>(
|
||||||
|
(props, { attrs }) => {
|
||||||
|
return () => (
|
||||||
|
<DropdownMenuSeparatorPrimitive
|
||||||
|
class={cn('mx-1 my-1 h-px bg-muted', attrs.class)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DropdownMenuSeparator',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const DropdownMenuShortcut = defineComponent(
|
||||||
|
(props, { attrs }) => {
|
||||||
|
return () => (
|
||||||
|
<span
|
||||||
|
class={cn(
|
||||||
|
'ml-auto text-xs tracking-widest opacity-60',
|
||||||
|
attrs.class ?? '',
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DropdownMenuShortcut',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuPortal,
|
||||||
|
DropdownMenuSub,
|
||||||
|
DropdownMenuSubContent,
|
||||||
|
DropdownMenuSubTrigger,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user