Dropdown menu
Preview
<script setup lang="ts">
import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuGroup,
  DropdownMenuSubTrigger,
  DropdownMenuSub,
  DropdownMenuSeparator,
  DropdownMenuGroupLabel,
} from "~/components/ui/dropdown-menu";
import { Button } from "~/components/ui/button";
import {
  User2,
  CreditCard,
  Settings,
  Keyboard,
  Settings2,
  Plus,
  UserPlus,
} from "lucide-vue-next";
import { Mail, MessageSquare } from "lucide-vue-next";
import { PlusCircle } from "lucide-vue-next";
</script>
<template>
  <DropdownMenu :close-on-select="true">
    <DropdownMenuTrigger>
      <Button variant="outline">Menu</Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent>
      <DropdownMenuGroup id="account">
        <DropdownMenuGroupLabel for="account"> Account </DropdownMenuGroupLabel>
        <DropdownMenuItem id="profile">
          <User2 class="mr-2 h-4 w-4" />
          Profile
        </DropdownMenuItem>
        <DropdownMenuItem id="billing">
          <CreditCard class="mr-2 h-4 w-4" />
          Billing
        </DropdownMenuItem>
        <DropdownMenuItem id="settings">
          <Settings class="mr-2 h-4 w-4" />
          Settings
        </DropdownMenuItem>
        <DropdownMenuItem id="shortcuts">
          <Keyboard class="mr-2 h-4 w-4" />
          Keyboard shortcuts
        </DropdownMenuItem>
      </DropdownMenuGroup>
      <!-- <DropdownMenuSeparator /> -->
      <DropdownMenuGroup id="team">
        <DropdownMenuGroupLabel for="team"> Team </DropdownMenuGroupLabel>
        <DropdownMenuItem id="team-settings">
          <Settings2 class="mr-2 h-4 w-4" />
          Team settings
        </DropdownMenuItem>
        <DropdownMenu>
          <DropdownMenuSubTrigger>
            <UserPlus class="mr-2 h-4 w-4" />
            Invite users
          </DropdownMenuSubTrigger>
          <DropdownMenuContent>
            <DropdownMenuItem id="invite-email">
              <Mail class="mr-2 h-4 w-4" />
              Email
            </DropdownMenuItem>
            <DropdownMenuItem id="invite-chat">
              <MessageSquare class="mr-2 h-4 w-4" />
              Chat
            </DropdownMenuItem>
            <DropdownMenuSeparator />
            <DropdownMenuItem id="invite-more">
              <PlusCircle class="mr-2 h-4 w-4" />
              More
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </DropdownMenuGroup>
    </DropdownMenuContent>
  </DropdownMenu>
</template>Installation
Copy and paste this into your project
// ~/components/ui/dropdown-menu.tsx
import {
  Menu,
  MenuContent,
  MenuItem,
  MenuItemGroup,
  MenuItemGroupLabel,
  MenuPositioner,
  MenuProps,
  MenuSeparator,
  MenuTrigger,
  MenuTriggerItem,
} from "@ark-ui/vue";
import { ExtendProps, cn } from "~/lib/utils";
import { Teleport, defineComponent, onMounted, ref, h } from "vue";
import { ChevronRight } from "lucide-vue-next";
const Chevron = defineComponent({
  setup() {
    return () =>
      h(ChevronRight, {
        class: "w-4 h-4 ml-auto",
      });
  },
});
const DropdownMenu = defineComponent({
  props: {} as ExtendProps<MenuProps>,
  setup(_, { slots, attrs }) {
    const key = ref("ssr");
    onMounted(() => {
      key.value = "csr";
    });
    return () => (
      <Menu key={key.value} {...attrs}>
        {slots.default?.()}
      </Menu>
    );
  },
});
const DropdownMenuTrigger = MenuTrigger;
const DropdownMenuGroup = MenuItemGroup;
const DropdownMenuSub = Menu;
const DropdownMenuContent = defineComponent({
  setup(_, { slots, attrs }) {
    return () => (
      <Teleport to="body">
        <MenuPositioner>
          <MenuContent
            class={cn(
              "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none",
              attrs.class ?? ""
            )}
          >
            {slots.default?.()}
          </MenuContent>
        </MenuPositioner>
      </Teleport>
    );
  },
});
const DropdownMenuItem = defineComponent({
  props: {
    id: {
      type: String,
      required: true,
    },
    inset: {
      type: Boolean,
      default: false,
    },
  },
  setup(props, { slots, attrs, emit }) {
    return () => (
      <MenuItem
        id={props.id}
        {...attrs}
        class={cn(
          "w-full relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[focus]:bg-accent data-[focus]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
          props.inset && "pl-8",
          attrs.class ?? ""
        )}
      >
        {slots.default?.()}
      </MenuItem>
    );
  },
});
const DropdownMenuSubTrigger = defineComponent({
  props: {
    inset: {
      type: Boolean,
      default: false,
    },
  },
  setup(props, { slots, attrs }) {
    return () => (
      <MenuTriggerItem
        class={cn(
          "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[focus]:bg-accent data-[state=open]:bg-accent",
          props.inset && "pl-8",
          attrs.class ?? ""
        )}
      >
        {slots.default?.()}
        <Chevron />
      </MenuTriggerItem>
    );
  },
});
const DropdownMenuSeparator = defineComponent({
  setup(_, { attrs }) {
    return () => (
      <MenuSeparator
        class={cn("-mx-1 my-1 h-px bg-muted", attrs.class ?? "")}
      />
    );
  },
});
const DropdownMenuGroupLabel = defineComponent({
  props: {
    for: {
      type: String,
      required: true,
    },
  },
  setup(props, { attrs, slots }) {
    return () => (
      <MenuItemGroupLabel
        htmlFor={props.for}
        class={cn(
          "block px-2 py-1.5 text-xs font-medium text-muted-foreground",
          attrs.class ?? ""
        )}
      >
        {slots.default?.()}
      </MenuItemGroupLabel>
    );
  },
});
export {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuGroup,
  DropdownMenuSub,
  DropdownMenuSubTrigger,
  DropdownMenuSeparator,
  DropdownMenuGroupLabel,
};Usage
import {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSubTrigger,
  DropdownMenuSub,
} from "~/components/ui/dropdown-menu";Note: All dropdown menu items must have an id attribute. This is used to identify item is clicked through the @select event.
<DropdownMenu @select="(...)">
  <DropdownMenuTrigger>Open</DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuItem id="profile">Profile</DropdownMenuItem>
     <DropdownMenuSub>
      <DropdownMenuSubTrigger>
        <UserPlus class="mr-2 h-4 w-4" />
        Invite users
      </DropdownMenuSubTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem id="email">
          Email
        </DropdownMenuItem>
        <DropdownMenuItem id="chat">
          Chat
        </DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem id="more">
          More
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenuSub>
  </DropdownMenuContent>
</DropdownMenu>