sdxc

Type to search, or start from one of these:

Menu

A menu surface anchored to whatever invoker opens it, riding the same native Popover API Popover does, sized to its own content and padded around a compact stack of Menu.Item rows, Menu.Separator dividers.

View code
<Button commandfor="issue-menu" command="toggle-popover" variant="outline">
	Issue actions
	<ChevronDownIcon />
</Button>

<Menu id="issue-menu" aria-label="Issue actions" placement="bottom" mix={[menuKeys()]}>
	<Section aria-labelledby="issue-menu-edit">
		<Header id="issue-menu-edit">Edit</Header>
		<Menu.Item href="/issues/482/edit">
			<PencilIcon />
			<span mix={[grow()]}>Rename</span>
			<Keyboard>R</Keyboard>
		</Menu.Item>
		<Menu.Item>
			<UserPlusIcon />
			<span mix={[grow()]}>Assign to teammate</span>
			<Keyboard>A</Keyboard>
		</Menu.Item>
	</Section>

	<Menu.Separator />

	<Section aria-labelledby="issue-menu-share">
		<Header id="issue-menu-share">Share</Header>
		<Menu.Item>
			<CopyIcon />
			<span mix={[grow()]}>Copy link</span>
			<Keyboard>⌘C</Keyboard>
		</Menu.Item>
		<Menu.Item href="/issues/482" target="_blank" rel="noreferrer">
			<ExternalLinkIcon />
			<span mix={[grow()]}>Open in new tab</span>
		</Menu.Item>
		<Menu.Item disabled>
			<ShareIcon />
			<span mix={[grow()]}>Share externally</span>
		</Menu.Item>
	</Section>

	<Menu.Separator />

	<Menu.Item danger>
		<Trash2Icon />
		<span mix={[grow()]}>Delete issue</span>
		<Keyboard></Keyboard>
	</Menu.Item>
</Menu>

Installation

An ordinary dependency: install it, import the theme once, and import the component where it is used.

npm add @sdxc/ui
import "@sdxc/ui/theme.css";

Usage

import { Menu } from "@sdxc/ui";

Renders the menu's own host: a Popover defaulting to "bottom-start" placement and "menu" role, sized to its content. Point a row's commandfor at another <Menu>'s id to open it as a nested menu.

Composition

The parts the component publishes, each a static property of the host.

  • Menu
  • Menu.ItemRenders a native <a> when href is set, else a <button>, defaulting role to "menuitem"; only omitting or neutralizing href stops the <a> variant's navigation, since aria-disabled mutes it visually only.
  • Menu.SeparatorRenders a hairline divider between two runs of Menu.Items: the shared Separator component with the small block-axis margin a menu's compact rows need on either side of it.

Examples

<Menu id="row-menu" placement="left-start" aria-label={t("table.rowActions")}>
	<Section aria-labelledby="row-menu-view-heading">
		<Header id="row-menu-view-heading">{t("menu.view")}</Header>
		<Menu.Item href={viewUrl}>{t("actions.view")}</Menu.Item>
		<Menu.Item href={editUrl}>{t("actions.edit")}</Menu.Item>
	</Section>
	<Menu.Separator />
	<Menu.Item danger commandfor="confirm-delete" command="show-modal">{t("actions.delete")}</Menu.Item>
</Menu>

Props

Read from the component's own types, so every prop, every default and every allowed value is listed.

PropTypeDescription
childrenRemixNodeThe menu's rows: Menu.Item, Menu.Separator, or a Section/Header pair grouping related items together.

Also accepts everything in Popover.Props.

Menu.Item

PropTypeDescription
href?stringDestination the row navigates to. Renders the row as a native <a> instead of a <button> when set.
target?TagProps<"a">["target"]target for the link variant, applied only once href is set.
rel?TagProps<"a">["rel"]rel for the link variant, applied only once href is set.
danger?booleanStyles the row in the semantic danger tone, for a destructive action like deleting a resource.
mix?MixinDescriptor<HTMLAnchorElement | HTMLButtonElement, any, ElementProps>[]Extra mixin(s) applied to the row's host element, whichever of <a>/<button> it renders as depending on href.

Also accepts everything in TagProps<"button">.

Menu.Separator

Also accepts everything in Separator.Props.