Create button
The button that opens a grid of what can be created, such as a maintenance record, a log entry or a checklist.
On this page
Code
import { CreateButton, type CreateOption } from "@bosunkit/components/create-button";
import { BookOpen, ListChecks, Wrench } from "lucide-react";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"The button a vessel's records page is made for: it opens a grid of what can be created, each with a mark, its name and " +
"a line about it.";
const OPTIONS: readonly CreateOption[] = [
{
id: "maintenance",
label: "Maintenance record",
description: "A job on a system, with its due date.",
icon: <Wrench size="1.25em" />,
},
{ id: "log", label: "Log entry", description: "A line in the ship's log.", icon: <BookOpen size="1.25em" /> },
{
id: "checklist",
label: "Checklist",
description: "Steps to tick off before departure.",
icon: <ListChecks size="1.25em" />,
},
];
export default function Showcase(): ReactElement {
const [chosen, setChosen]: [string, Dispatch<SetStateAction<string>>] = useState<string>("Nothing chosen yet");
return (
<div style={{ display: "grid", justifyItems: "start", gap: "var(--space-md)" }}>
<CreateButton
label="Create"
menuLabel="What do you want to create?"
options={OPTIONS}
onSelect={(id: string): void => {
setChosen(`Chose ${id}`);
}}
/>
<span style={{ color: "var(--text-dim)", fontSize: "0.85em" }}>{chosen}.</span>
</div>
);
}
A create button is the action a list page exists for. When there is more than one thing to create, it opens a grid of tiles in place of a long menu: each tile has a mark, a name and one line saying what it is for, so the crew choose by reading, not by remembering. The grid grows out of the button while it fades in, and the plus turns into a cross while it is open. It is built on Base UI's menu: the arrows move through the tiles as a grid, Left and Right along a row and Up and Down a column, skipping a disabled tile; the first letters of a name jump to it, Enter chooses, and Escape closes and returns focus to the button.
columns sets how many tiles sit side by side, and the grid keeps fewer where the window is too narrow, down to one on a
phone. An option that does not apply stays in the grid, dimmed and announced as disabled, so the crew can see it exists.
The caller translates every label, and onSelect receives the id of the option chosen after the grid closes.
Examples
One column
Code
import { CreateButton } from "@bosunkit/components/create-button";
import type { ReactElement } from "react";
export const description: string =
"`columns` of 1 for a short list of options, with a secondary button where creating is not the page's main action.";
export default function OneColumn(): ReactElement {
return (
<CreateButton
label="New"
menuLabel="New"
variant="default"
columns={1}
options={[
{ id: "crew", label: "Crew member", description: "Someone aboard, with their documents." },
{ id: "guest", label: "Guest", description: "A visitor for the passage." },
]}
onSelect={(): void => undefined}
/>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| labelrequired | string | – | The button's label ("Create"), translated by the caller. |
| menuLabelrequired | string | – | The grid's name for a screen reader ("What do you want to create?"), translated by the caller. |
| optionsrequired | readonly CreateOption[] | – | What can be created, in the order the grid shows them. |
| onSelectrequired | (id: string) => void | – | Called with the id of the option the crew choose, after the grid closes. |
| columns | 1 | 2 | 3 | undefined | 2 | How many options sit side by side in the grid on a wide screen; 2 by default. A narrow screen shows one column. |
| variant | ButtonVariant | undefined | "primary" | The button's emphasis; primary by default, since creating is the action a page is for. |
| motion | boolean | undefined | true | Whether the grid grows out of the button while it fades, and the plus turns into a cross; on by default. Under reduced motion the grid only fades and the plus does not turn, whatever this says. |
| open | boolean | undefined | – | Whether the grid is open, for a caller that holds the state; pair it with onOpenChange. |
| onOpenChange | BaseMenu.Root.Props["onOpenChange"] | – | Called with the open state a press, Escape or a choice asks for. |
| className | string | undefined | – | Added to the button's classes. |
| testId | string | undefined | – | Becomes data-testid on the button; the grid is ${testId}--menu. |
| ref | Ref<HTMLButtonElement> | undefined | – | The button element. |
Related
- Button: the button the create button is built on, with its emphases.
- Context menu: a menu of commands on a row, opened by a right click or a long press.
- Task dialog: the dialog a created record's form often opens in.