ActionMenu
The gesture-driven menu behind the Create picker and album options.
import ActionMenuContextMenu from "swshd/ActionMenu/Context";For a single action, use Button.
Each shell is a default export from its own entry point. The Context popover is self-contained on both platforms; the Create menu's web card becomes a bottom sheet below sheetBelow, and on native Create is only the trigger's gesture host — the app draws the sheet under a shared provider and overlay.
import ActionMenuContextMenu from "swshd/ActionMenu/Context";
import ActionMenuCreateMenu from "swshd/ActionMenu/Create";Open each menu, then pick a row, press Escape, or click the backdrop.
The parts compose inside a shell's children:
Group
A section of rows with an optional uppercase header.
Label
The group's title row, joining the entrance cascade.
Item
One selectable row. The create variant draws a disc, title, and subtitle; the context variant draws a big-text action row.
ScrollArea
Web only. Wraps a run of rows inside the card: those rows scroll while rows before or after it stay pinned, and its own maxHeight caps the run. On native it renders its children inline.
Data-shaped menus map their key to the row's id:
{group.items.map(({ key, ...item }) => (
<ActionMenuItem key={key} id={key} {...item} />
))}On web a click opens the card; on native a press-and-drag lands on the row. Selection is deferred until the exit animation completes, so an action that re-renders the menu cannot cut the close short. On web both cards enter over 220 ms; the Context card exits on the same transition, and the Create card exits faster, over 140 ms. Native cards use springs. On web, maxHeight scrolls the rows inside the card.
A few props are internal-facing: the shell's surface supplies paddingLeft/paddingRight, discSize, index, and stagger. backgroundColor, textColor, headerColor, iconColor, and highlightColor are legacy palette overrides for the app's themed menus. Leave them unset.
<ActionMenuContextMenu
renderChildren={({ isOpen, toggle, gestureControlled }) => (
<ButtonRoot iconOnly variant="ghost"
accessibilityLabel="Options" onPress={gestureControlled ? undefined : toggle}>
<ButtonIcon Icon={isOpen ? IconClose : IconEllipsis} />
</ButtonRoot>
)}
>
<ActionMenuGroup>
<ActionMenuLabel>Album settings</ActionMenuLabel>
<ActionMenuItem id="joiners" label="Joiners" Icon={IconPeople} onSelect={onJoiners} />
</ActionMenuGroup>
</ActionMenuContextMenu>Roles
The card is role="menu"; rows are menuitem, or menuitemradio with aria-checked when selected is set.
Keyboard
Escape closes an open menu.
Trigger
The trigger's accessible name is the consumer's — the render prop owns its button, so pass an accessibilityLabel.
Each shell's props:
ActionMenuContextMenu
| Prop | Type | Default | Description |
|---|---|---|---|
renderChildren | (state: ActionMenuRenderState) => ReactNode | — | The trigger. Receives isOpen, toggle, close, and gestureControlled (native drag). |
anchor | ActionMenuAnchor | { horizontal: "right", vertical: "below" } | Corner the popover pins to and grows from. |
align | "left" | "right" | — | Row content and group header alignment. Defaults to the anchor's horizontal side. |
width | number | — | Explicit menu width. Omitted, the card hugs its content plus edgePadding on the side opposite align. |
edgePadding | number | ACTION_MENU_DEFAULT_EDGE_PADDING | Padding on the side opposite align when width is unset. |
edgeDetection | boolean | true | Flip the requested anchor axis when the card would overflow the viewport. |
maxHeight | number | — | Web only. Caps the card's height; past it the rows scroll inside the card. |
onOpen | () => void | — | Fires on every open, including the native drag. |
containerStyle | StyleProp<ViewStyle> | — | The host element. |
surfaceStyle | StyleProp<ViewStyle> | — | The popover surface. |
children | ReactNode | — | The menu's rows, composed of Group / Label / Item. |
ActionMenuCreateMenu
| Prop | Type | Default | Description |
|---|---|---|---|
renderChildren | (state: ActionMenuRenderState) => ReactNode | — | The trigger. On native gestureControlled is true and the Pan owns press/drag. |
placement | "above" | "below" | — | Web only. Which side of the trigger the card opens on. |
align | "left" | "right" | "right" | Web only. Which trigger edge the card pins to. |
sheetBelow | number | — | Web only. Below this viewport width the card renders as a bottom sheet. Off when omitted. |
maxHeight | number | — | Web only. Caps the card's height. |
containerStyle | StyleProp<ViewStyle> | — | The host element. |
children | ReactNode | — | The rows. Ignored on native — the app draws the sheet. |
ActionMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | Row identity for drag hit-testing and selection (key is React-reserved). |
label | string | — | The row's text. |
Icon | IconComponent | — | A glyph from swshd/icons. |
onSelect | () => void | — | Fired on selection — deferred until the menu's exit animation completes. |
subtitle | string | — | Secondary line — only the create variant's row body renders it. |
leading | ReactElement | — | Rendered in the create row's disc slot instead of Icon. |
accessory | ReactElement | — | Rendered under the title in the subtitle slot when there is no subtitle. |
selected | boolean | — | Marks the current choice: a trailing check and aria-checked. |
destructive | boolean | — | Danger ink for delete/leave actions. |
disabled | boolean | — | Blocks selection. |
variant | "create" | "context" | — | Which row body to draw. Defaults to the surface's variant, then "context". |
align | "left" | "right" | — | Row content alignment. Context body only; defaults to "right". |