feat: add DropdownMenu component

feat: add DropdownMenu component

feat: add DropdownMenu component
This commit is contained in:
rev4324 2023-08-29 22:18:59 +02:00
parent f59d7727b2
commit edbdeca603
12 changed files with 482 additions and 0 deletions

View File

@ -6,6 +6,7 @@ import AlertDialogDemo from '@/registry/default/examples/AlertDialogDemo.vue'
import SelectDemo from '@/registry/default/examples/SelectDemo.vue'
import AvatarDemo from '@/registry/default/examples/AvatarDemo.vue'
import AlertDemo from '@/registry/default/examples/AlertDemo.vue'
import DropdownMenuDemo from '@/registry/default/examples/DropdownMenuDemo.vue'
</script>
<template>
@ -23,5 +24,7 @@ import AlertDemo from '@/registry/default/examples/AlertDemo.vue'
<AvatarDemo />
<AlertDemo />
<DropdownMenuDemo />
</div>
</template>

View 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 Dropdown Menu</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>

View File

@ -0,0 +1,42 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { DropdownMenuCheckboxItem as DropdownMenuCheckboxItemPrimitive, type DropdownMenuCheckboxItemProps, type DropdownMenuCheckboxItemEmits,
DropdownMenuItemIndicator as DropdownMenuItemIndicatorPrimitive } from 'radix-vue';
import { cn, useEmitAsProps } from '@/utils';
import { CheckIcon } from 'lucide-vue-next';
const props = defineProps<DropdownMenuCheckboxItemProps>();
const emits = defineEmits<DropdownMenuCheckboxItemEmits>();
const emitsAsProps = useEmitAsProps(emits);
defineOptions({
name: 'DropdownMenuCheckboxItem',
inheritAttrs: false,
});
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: className, ...rest } = allAttrs
return {
className,
rest: rest
}
})
</script>
<template>
<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.className ?? '',
)"
:checked="checked"
v-bind="{ ...attrs.rest, ...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>
<slot />
</DropdownMenuCheckboxItemPrimitive>
</template>

View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { DropdownMenuContent as DropdownMenuContentPrimitive, type DropdownMenuContentProps, type DropdownMenuContentEmits } from 'radix-vue';
import { cn, useEmitAsProps } from '@/utils';
import { DropdownMenuPortal } from '@/registry/default/ui/dropdown-menu';
const props = withDefaults(defineProps<DropdownMenuContentProps>(), {
sideOffset: 4,
});
const emits = defineEmits<DropdownMenuContentEmits>();
const emitsAsProps = useEmitAsProps(emits);
defineOptions({
name: 'DropdownMenuContent',
inheritAttrs: false,
});
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: className, ...rest } = allAttrs
return {
className,
rest: rest
}
})
</script>
<template>
<DropdownMenuPortal>
<DropdownMenuContentPrimitive
: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.className ?? '',
)"
v-bind="{ ...attrs.rest, ...props, ...emitsAsProps }">
<slot />
</DropdownMenuContentPrimitive>
</DropdownMenuPortal>
</template>

View File

@ -0,0 +1,37 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { DropdownMenuItem as DropdownMenuItemPrimitive, type DropdownMenuItemProps, type DropdownMenuItemEmits } from 'radix-vue';
import { cn, useEmitAsProps } from '@/utils';
const props = defineProps<DropdownMenuItemProps & {
inset?: boolean
}>();
const emits = defineEmits<DropdownMenuItemEmits>();
const emitsAsProps = useEmitAsProps(emits);
defineOptions({
name: 'DropdownMenuItem',
inheritAttrs: false,
});
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: className, ...rest } = allAttrs
return {
className,
rest: rest
}
})
</script>
<template>
<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',
inset && 'pl-8',
attrs.className ?? '',
)"
v-bind="{ ...attrs.rest, ...props, ...emitsAsProps }">
<slot />
</DropdownMenuItemPrimitive>
</template>

View File

@ -0,0 +1,35 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { DropdownMenuLabel as DropdownMenuLabelPrimitive, type DropdownMenuLabelProps } from 'radix-vue';
import { cn } from '@/utils';
const props = defineProps<DropdownMenuLabelProps & {
inset?: boolean
}>();
defineOptions({
name: 'DropdownMenuLabel',
inheritAttrs: false,
});
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: className, ...rest } = allAttrs
return {
className,
rest: rest
}
})
</script>
<template>
<DropdownMenuLabelPrimitive
:class="cn(
'px-2 py-1.5 text-sm font-semibold',
inset && 'pl-8',
attrs.className ?? '',
)"
v-bind="{ ...attrs.rest, ...props }">
<slot />
</DropdownMenuLabelPrimitive>
</template>

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { DropdownMenuRadioItem as DropdownMenuRadioItemPrimitive, type DropdownMenuRadioItemProps, type DropdownMenuRadioItemEmits,
DropdownMenuItemIndicator as DropdownMenuItemIndicatorPrimitive } from 'radix-vue';
import { cn, useEmitAsProps } from '@/utils';
import { CircleIcon } from 'lucide-vue-next';
const props = defineProps<DropdownMenuRadioItemProps>();
const emits = defineEmits<DropdownMenuRadioItemEmits>();
const emitsAsProps = useEmitAsProps(emits);
defineOptions({
name: 'DropdownMenuRadioItem',
inheritAttrs: false,
});
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: className, ...rest } = allAttrs
return {
className,
rest: rest
}
})
</script>
<template>
<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.className ?? '',
)"
v-bind="{ ...attrs.rest, ...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>
<slot />
</DropdownMenuRadioItemPrimitive>
</template>

View File

@ -0,0 +1,32 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { DropdownMenuSeparator as DropdownMenuSeparatorPrimitive, type DropdownMenuSeparatorProps } from 'radix-vue';
import { cn } from '@/utils';
const props = defineProps<DropdownMenuSeparatorProps>();
defineOptions({
name: 'DropdownMenuSeparator',
inheritAttrs: false,
});
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: className, ...rest } = allAttrs
return {
className,
rest: rest
}
})
</script>
<template>
<DropdownMenuSeparatorPrimitive
:class="cn(
'mx-1 my-1 h-px bg-muted',
attrs.className ?? '',
)"
v-bind="{ ...attrs.rest, ...props }">
<slot />
</DropdownMenuSeparatorPrimitive>
</template>

View File

@ -0,0 +1,29 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cn } from '@/utils';
defineOptions({
name: 'DropdownMenuShortcut',
inheritAttrs: false,
});
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: className, ...rest } = allAttrs
return {
className,
rest: rest
}
})
</script>
<template>
<span
:class="cn(
'ml-auto text-xs tracking-widest opacity-60',
attrs.className ?? '',
)"
v-bind="{ ...attrs.rest }">
<slot />
</span>
</template>

View File

@ -0,0 +1,34 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { DropdownMenuSubContent as DropdownMenuSubContentPrimitive, type DropdownMenuSubContentProps, type DropdownMenuSubContentEmits } from 'radix-vue';
import { cn, useEmitAsProps } from '@/utils';
const props = defineProps<DropdownMenuSubContentProps>();
const emits = defineEmits<DropdownMenuSubContentEmits>();
const emitsAsProps = useEmitAsProps(emits);
defineOptions({
name: 'DropdownMenuSubContent',
inheritAttrs: false,
});
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: className, ...rest } = allAttrs
return {
className,
rest: rest
}
})
</script>
<template>
<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.className ?? '',
)"
v-bind="{ ...attrs.rest, ...props, ...emitsAsProps }">
<slot />
</DropdownMenuSubContentPrimitive>
</template>

View File

@ -0,0 +1,37 @@
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { DropdownMenuSubTrigger as DropdownMenuSubTriggerPrimitive, type DropdownMenuSubTriggerProps } from 'radix-vue';
import { cn } from '@/utils';
import { ChevronRight } from 'lucide-vue-next';
const props = defineProps<DropdownMenuSubTriggerProps & {
inset?: boolean;
}>();
defineOptions({
name: 'DropdownMenuSubTrigger',
inheritAttrs: false,
});
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: className, ...rest } = allAttrs
return {
className,
rest: rest
}
})
</script>
<template>
<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',
inset && 'pl-8',
attrs.className ?? '',
)"
v-bind="{ ...attrs.rest, ...props }">
<slot />
<ChevronRight class="ml-auto w-4 h-4" />
</DropdownMenuSubTriggerPrimitive>
</template>

View File

@ -0,0 +1,42 @@
import {
DropdownMenuRoot as DropdownMenuRootPrimitive,
DropdownMenuTrigger as DropdownMenuTriggerPrimitive,
DropdownMenuGroup as DropdownMenuGroupPrimitive,
DropdownMenuPortal as DropdownMenuPortalPrimitive,
DropdownMenuSub as DropdownMenuSubPrimitive,
DropdownMenuRadioGroup as DropdownMenuRadioGroupPrimitive,
} from "radix-vue"
import DropdownMenuSubTrigger from "./DropdownMenuSubTrigger.vue"
import DropdownMenuSubContent from "./DropdownMenuSubContent.vue"
import DropdownMenuContent from "./DropdownMenuContent.vue"
import DropdownMenuItem from "./DropdownMenuItem.vue"
import DropdownMenuCheckboxItem from "./DropdownMenuCheckboxItem.vue"
import DropdownMenuRadioItem from "./DropdownMenuRadioItem.vue"
import DropdownMenuLabel from "./DropdownMenuLabel.vue"
import DropdownMenuSeparator from "./DropdownMenuSeparator.vue"
import DropdownMenuShortcut from "./DropdownMenuShortcut.vue"
const DropdownMenu = DropdownMenuRootPrimitive
const DropdownMenuTrigger = DropdownMenuTriggerPrimitive
const DropdownMenuGroup = DropdownMenuGroupPrimitive
const DropdownMenuPortal = DropdownMenuPortalPrimitive
const DropdownMenuSub = DropdownMenuSubPrimitive
const DropdownMenuRadioGroup = DropdownMenuRadioGroupPrimitive
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuRadioGroup,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
}