# Dropdown Menu — Sub-component Reference

All exports from `@loke/ui/dropdown-menu`.

## Root components

### DropdownMenu

```ts
interface DropdownMenuProps {
  children?: ReactNode;
  defaultOpen?: boolean;
  dir?: "ltr" | "rtl";
  modal?: boolean;          // default: true
  onOpenChange?: (open: boolean) => void;
  open?: boolean;
}
```

Root context provider. `modal={true}` disables pointer events on the background when open. Wrap `DropdownMenuTrigger` and `DropdownMenuPortal` inside.

---

### DropdownMenuTrigger

```ts
interface DropdownMenuTriggerProps extends PrimitiveButtonProps {
  disabled?: boolean;
}
```

Renders a `<button>` that toggles the menu. Sets `aria-haspopup="menu"`, `aria-expanded`, and `aria-controls`. `Enter`, `Space`, `ArrowDown` open the menu. `PointerDown` toggles.

---

### DropdownMenuPortal

```ts
interface DropdownMenuPortalProps {
  children?: ReactNode;
  container?: HTMLElement | null;  // default: document.body
  forceMount?: true;
}
```

Portals content into the DOM. Wrap `DropdownMenuContent` inside. Required to avoid z-index and overflow clipping issues.

---

## Content

### DropdownMenuContent

```ts
interface DropdownMenuContentProps
  extends Omit<MenuContentProps, "onEntryFocus"> {
  // All PopperContent positioning props:
  side?: "top" | "right" | "bottom" | "left";  // default: "bottom"
  sideOffset?: number;                           // default: 0
  align?: "start" | "center" | "end";           // default: "center"
  alignOffset?: number;
  avoidCollisions?: boolean;                     // default: true
  collisionBoundary?: Element | Element[] | null;
  collisionPadding?: number | Partial<Record<Side, number>>;
  arrowPadding?: number;
  sticky?: "partial" | "always";
  hideWhenDetached?: boolean;
  // DismissableLayer props:
  onEscapeKeyDown?: (event: KeyboardEvent) => void;
  onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;
  onFocusOutside?: (event: FocusOutsideEvent) => void;
  onInteractOutside?: (event: PointerDownOutsideEvent | FocusOutsideEvent) => void;
  onCloseAutoFocus?: (event: Event) => void;
  // Menu-specific:
  loop?: boolean;  // default: false — whether keyboard nav wraps
}
```

CSS custom properties set on this element:

| Property | Description |
|---|---|
| `--loke-dropdown-menu-content-available-height` | Viewport space above/below trigger |
| `--loke-dropdown-menu-content-available-width` | Viewport space left/right of trigger |
| `--loke-dropdown-menu-content-transform-origin` | Correct origin for scale animations |
| `--loke-dropdown-menu-trigger-height` | Height of the trigger element |
| `--loke-dropdown-menu-trigger-width` | Width of the trigger element |

---

## Items

### DropdownMenuItem

```ts
interface DropdownMenuItemProps extends PrimitiveDivProps {
  disabled?: boolean;
  onSelect?: (event: Event) => void;
  textValue?: string;  // used for typeahead; defaults to textContent
}
```

Standard menu item. Fires `onSelect` on pointer-up or Enter/Space, then auto-closes the menu. Call `event.preventDefault()` in `onSelect` to prevent auto-close.

---

### DropdownMenuCheckboxItem

```ts
interface DropdownMenuCheckboxItemProps extends PrimitiveDivProps {
  checked?: boolean | "indeterminate";
  disabled?: boolean;
  onCheckedChange?: (checked: boolean) => void;
  onSelect?: (event: Event) => void;
  textValue?: string;
}
```

Toggle item. `data-state="checked"` | `"unchecked"` | `"indeterminate"`. Does not auto-close by default when `onSelect` is not prevented — call `event.preventDefault()` inside `onSelect` to keep menu open after toggle.

---

### DropdownMenuRadioGroup

```ts
interface DropdownMenuRadioGroupProps extends PrimitiveDivProps {
  onValueChange?: (value: string) => void;
  value?: string;
}
```

Groups `DropdownMenuRadioItem` elements. Manages a single selected value.

---

### DropdownMenuRadioItem

```ts
interface DropdownMenuRadioItemProps extends PrimitiveDivProps {
  disabled?: boolean;
  onSelect?: (event: Event) => void;
  textValue?: string;
  value: string;  // required
}
```

Radio-style item. `data-state="checked"` | `"unchecked"`. Must be inside `DropdownMenuRadioGroup`. Fires `onValueChange` on the group when selected.

---

### DropdownMenuItemIndicator

```ts
interface DropdownMenuItemIndicatorProps extends PrimitiveSpanProps {
  forceMount?: true;
}
```

Renders only when the parent `CheckboxItem` or `RadioItem` is checked. By default unmounts when unchecked. Use `forceMount` to keep it mounted for exit animations. Style with `data-state="checked"` / `"unchecked"`.

---

## Structure

### DropdownMenuGroup

```ts
interface DropdownMenuGroupProps extends PrimitiveDivProps {}
```

Semantic grouping container. Renders a `<div>` with no additional behavior. Use alongside `DropdownMenuLabel` to label a group.

---

### DropdownMenuLabel

```ts
interface DropdownMenuLabelProps extends PrimitiveDivProps {}
```

Non-interactive label for a group. Not focusable, not selectable.

---

### DropdownMenuSeparator

```ts
interface DropdownMenuSeparatorProps extends PrimitiveDivProps {}
```

Visual and semantic separator. Renders with `role="separator"`.

---

### DropdownMenuArrow

```ts
interface DropdownMenuArrowProps extends PrimitiveSVGProps {
  height?: number;  // default: 5
  width?: number;   // default: 10
}
```

SVG arrow pointing toward the trigger. Must be inside `DropdownMenuContent`.

---

## Submenus

### DropdownMenuSub

```ts
interface DropdownMenuSubProps {
  children?: ReactNode;
  defaultOpen?: boolean;
  onOpenChange?: (open: boolean) => void;
  open?: boolean;
}
```

Submenu root. Manages open state for the nested `DropdownMenuSubTrigger` + `DropdownMenuSubContent` pair.

---

### DropdownMenuSubTrigger

```ts
interface DropdownMenuSubTriggerProps extends PrimitiveDivProps {
  disabled?: boolean;
  textValue?: string;
}
```

Item that opens the submenu on pointer move or Enter/ArrowRight. Renders an arrow indicator automatically. Must be inside `DropdownMenuSub`.

---

### DropdownMenuSubContent

```ts
interface DropdownMenuSubContentProps {
  // Same positioning/dismissal props as DropdownMenuContent
  avoidCollisions?: boolean;
  collisionBoundary?: Element | Element[] | null;
  collisionPadding?: number | Partial<Record<Side, number>>;
  forceMount?: true;
  hideWhenDetached?: boolean;
  loop?: boolean;
  onEscapeKeyDown?: (event: KeyboardEvent) => void;
  onFocusOutside?: (event: FocusOutsideEvent) => void;
  onInteractOutside?: (event: PointerDownOutsideEvent | FocusOutsideEvent) => void;
  onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;
  sticky?: "partial" | "always";
}
```

Content panel for the submenu. Wrap in `DropdownMenuPortal`. Sets the same `--loke-dropdown-menu-*` CSS custom properties as `DropdownMenuContent`.

---

## Scope utility

### createDropdownMenuScope

```ts
function createDropdownMenuScope(): CreateScope;
```

For composing `DropdownMenu` inside other compound components. See the [context-and-collection](../../context-and-collection/SKILL.md) skill for scope threading patterns.
