Dropdown
A toggleable menu displaying a list of options or actions.
---import Button from '@bejamas/ui/components/Button.astro';import Dropdown from '@bejamas/ui/components/Dropdown.astro';---
<Dropdown> <Dropdown part="trigger">Menu</Dropdown> <Dropdown part="content"> <ul class="p-1"> <li><Button as="a" href="#" variant="ghost" class="w-full justify-start">Item 1</Button></li> <li><Button as="a" href="#" variant="ghost" class="w-full justify-start">Item 2</Button></li> <li><Button as="a" href="#" variant="ghost" class="w-full justify-start">Item 3</Button></li> </ul> </Dropdown></Dropdown>Installation
Section titled “Installation” bunx bejamas add dropdown npx bejamas add dropdown pnpm dlx bejamas add dropdown yarn dlx bejamas add dropdown---/** * @component Dropdown * @title Dropdown * @description A toggleable menu displaying a list of options or actions. * * @preview * <Dropdown> * <Dropdown part="trigger">Menu</Dropdown> * <Dropdown part="content"> * <ul class="p-1"> * <li><Button as="a" href="#" variant="ghost" class="w-full justify-start">Item 1</Button></li> * <li><Button as="a" href="#" variant="ghost" class="w-full justify-start">Item 2</Button></li> * <li><Button as="a" href="#" variant="ghost" class="w-full justify-start">Item 3</Button></li> * </ul> * </Dropdown> * </Dropdown> */
import { cn } from "@bejamas/ui/lib/utils";import type { HTMLAttributes, HTMLTag } from "astro/types";import Button from "./Button.astro";
type DropdownPart = "root" | "trigger" | "content";
type RootProps = { part?: "root"; as?: HTMLTag; class?: string;} & HTMLAttributes<"div">;
type TriggerProps = { part: "trigger"; as?: HTMLTag; class?: string;} & HTMLAttributes<"button">;
type ContentProps = { part: "content"; as?: HTMLTag; class?: string;} & HTMLAttributes<"div">;
type Props = RootProps | TriggerProps | ContentProps;
const { part: rawPart, as: rawTag, class: className = "", ...rest} = Astro.props as Props;
const part: DropdownPart = (rawPart ?? "root") as DropdownPart;---
{ part === "root" && (() => { const Tag = (rawTag as HTMLTag) ?? "div"; return ( <Tag {...rest} data-slot="dropdown" class={cn("relative inline-block group peer", className)} > <slot name="trigger" /> <slot name="content" /> <slot /> </Tag> ); })()}
{ part === "trigger" && (() => { const Tag = (rawTag as HTMLTag) ?? undefined; if (Tag) { return ( <Tag {...rest} slot="trigger" role="button" tabindex="0" data-slot="dropdown-trigger" class={cn( "cursor-pointer select-none outline-none group-focus-within:pointer-events-none inline-flex items-center gap-1", className, )} > <slot /> <svg aria-hidden="true" viewBox="0 0 20 20" fill="currentColor" class="ml-1 -mr-1 h-4 w-4 shrink-0 transition-transform duration-150 ease-out group-focus-within:rotate-180 group-has-[:active]:rotate-180" > <path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 10.94l3.71-3.71a.75.75 0 111.06 1.06l-4.24 4.24a.75.75 0 01-1.06 0L5.25 8.29a.75.75 0 01-.02-1.08z" clip-rule="evenodd" /> </svg> </Tag> ); } return ( <Button slot="trigger" as="div" tabindex="0" role="button" variant="ghost" data-slot="dropdown-trigger" class={cn( "cursor-pointer select-none outline-none group-focus-within:pointer-events-none", className, )} {...(rest as any)} > <span class="inline-flex items-center gap-1"> <slot /> <svg aria-hidden="true" viewBox="0 0 20 20" fill="currentColor" class="ml-1 -mr-1 h-4 w-4 shrink-0 transition-transform duration-150 ease-out group-focus-within:rotate-180 group-has-[:active]:rotate-180" > <path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 10.94l3.71-3.71a.75.75 0 111.06 1.06l-4.24 4.24a.75.75 0 01-1.06 0L5.25 8.29a.75.75 0 01-.02-1.08z" clip-rule="evenodd" /> </svg> </span> </Button> ); })()}
{ part === "content" && (() => { const Tag = (rawTag as HTMLTag) ?? "div"; return ( <Tag {...rest} slot="content" tabindex="0" data-slot="dropdown-content" class={cn( "absolute origin-top-left transition-discrete p-1 left-0 mt-2 min-w-[10rem] bg-popover border border-border rounded-lg shadow-lg z-20 opacity-0 -translate-y-1 scale-98 pointer-events-none transition-all duration-100 ease-out select-none [&_*]:select-none group-focus-within:opacity-100 group-focus-within:translate-y-0 group-focus-within:scale-100 group-focus-within:pointer-events-auto group-focus-within:select-auto group-has-[:active]:opacity-100 group-has-[:active]:translate-y-0 group-has-[:active]:scale-100 group-has-[:active]:pointer-events-auto", className, )} > <slot /> </Tag> ); })()}