Dropdown
Holds the open state. Contains a DropdownMenuTrigger and a DropdownMenuContent.
import { CaretDownIcon, CopyIcon, PencilSimpleIcon, TrashIcon } from "@phosphor-icons/react";
import { Button } from "@etched/reticle/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@etched/reticle/dropdown";
export function Preview() {
return (
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button>
Actions
<CaretDownIcon />
</Button>
}
/>
<DropdownMenuContent align="start">
<DropdownMenuItem icon={<PencilSimpleIcon />}>Edit</DropdownMenuItem>
<DropdownMenuItem icon={<CopyIcon />}>Copy ID</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem icon={<TrashIcon />} variant="danger">
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}Installation
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuLinkItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "@etched/reticle/dropdown";Examples
Row actions
Run 1,284. Last action: none
import { ArrowBendUpRightIcon, ArrowSquareOutIcon, CopyIcon, DotsThreeIcon, PencilSimpleIcon, TrashIcon } from "@phosphor-icons/react";
import { useState } from "react";
import { Button } from "@etched/reticle/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLinkItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "@etched/reticle/dropdown";
export function RowActions() {
const [last, setLast] = useState("none");
return (
<div className="row">
<span>Run 1,284. Last action: {last}</span>
<DropdownMenu>
<DropdownMenuTrigger
render={<Button shape="square" variant="ghost" aria-label="Actions for run 1,284" icon={<DotsThreeIcon />} />}
/>
<DropdownMenuContent align="end">
<DropdownMenuItem icon={<PencilSimpleIcon />} onClick={() => setLast("Edit")}>
Edit
<DropdownMenuShortcut>E</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem icon={<CopyIcon />} onClick={() => setLast("Copy ID")}>
Copy ID
<DropdownMenuShortcut>⌘C</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuLinkItem icon={<ArrowSquareOutIcon />} href="#dropdown" target="_blank">
Open in new tab
</DropdownMenuLinkItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger icon={<ArrowBendUpRightIcon />}>Move to bench</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem disabled>A-03 (current)</DropdownMenuItem>
<DropdownMenuItem onClick={() => setLast("Move to B-14")}>B-14</DropdownMenuItem>
<DropdownMenuItem onClick={() => setLast("Move to B-15")}>B-15</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuItem disabled onClick={() => setLast("Retry")}>
Retry
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem icon={<TrashIcon />} variant="danger" onClick={() => setLast("Delete")}>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}Checkbox and radio items
The menu stays open while you check items.
import { SlidersHorizontalIcon } from "@phosphor-icons/react";
import { useState } from "react";
import { Button } from "@etched/reticle/button";
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@etched/reticle/dropdown";
const columns = { owner: "Owner", site: "Site", started: "Started" };
type Column = keyof typeof columns;
// SAFETY: columns is a literal object, so its keys are Column.
const columnKeys = Object.keys(columns) as Column[];
export function CheckboxAndRadioItems() {
const [shown, setShown] = useState<Record<Column, boolean>>({ owner: true, site: true, started: false });
const [sort, setSort] = useState("newest");
return (
<div className="row">
<DropdownMenu>
<DropdownMenuTrigger render={<Button icon={<SlidersHorizontalIcon />}>View</Button>} />
<DropdownMenuContent align="start">
<DropdownMenuGroup>
<DropdownMenuLabel>Columns</DropdownMenuLabel>
{columnKeys.map((key) => (
<DropdownMenuCheckboxItem
key={key}
checked={shown[key]}
onCheckedChange={(checked) => setShown({ ...shown, [key]: checked })}
>
{columns[key]}
</DropdownMenuCheckboxItem>
))}
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuRadioGroup value={sort} onValueChange={setSort}>
<DropdownMenuLabel>Sort by</DropdownMenuLabel>
<DropdownMenuRadioItem value="newest">Newest first</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="oldest">Oldest first</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="status">Status</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}Links and downloads
import { CaretDownIcon } from "@phosphor-icons/react";
import { Button } from "@etched/reticle/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuLinkItem, DropdownMenuTrigger } from "@etched/reticle/dropdown";
const csv = "data:text/csv,run,status%0A1284,passed";
export function LinksAndDownloads() {
return (
<div className="row">
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button>
Export
<CaretDownIcon />
</Button>
}
/>
<DropdownMenuContent align="start">
<DropdownMenuLinkItem href={csv} download="runs.csv">
Download all runs CSV
</DropdownMenuLinkItem>
<DropdownMenuLinkItem href={csv} download="my-runs.csv">
Download my runs CSV
</DropdownMenuLinkItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}