Create task
StableThe New task dialog, with a draft that survives Esc, and the dialog that lists every Tasks shortcut.
CreateTaskDialog asks for a title (the only required field), a description and six properties, then creates the task in the shared store and says so in a toast. What you type is saved as a draft in this browser, so Esc or a click outside never loses it, and “Create more” keeps the dialog open for the next one. ShortcutsDialog lists the Tasks shortcuts from shortcutGroups, the same list the kit page’s key tables read.
New task
import { useState } from 'react'import { CreateTaskDialog, StatusIcon, useTaskState } from '@rchursin/product/tasks'import { Button } from '@rchursin/product/ui'
const number = (id: string) => Number(id.split('-')[1]) || 0
/** * Creates a real task in the shared sample workspace (with Undo in the toast). Only the title * is required. Esc keeps what you typed as a draft; Ctrl+Enter (⌘Enter) creates. */export default function CreateTaskExample() { const { tasks } = useTaskState() const [open, setOpen] = useState(false) const newest = [...tasks].sort((a, b) => number(b.id) - number(a.id)).slice(0, 3)
return ( <div className="flex w-full max-w-sm flex-col gap-5"> <Button icon="plus" className="self-start" onClick={() => setOpen(true)}> New task </Button> <div className="flex flex-col gap-2"> <p className="font-mono text-[11px] tracking-[0.12em] text-fg-3 uppercase">Newest in the sample workspace</p> <ul className="flex flex-col"> {newest.map((t) => ( <li key={t.id} className="flex h-9 min-w-0 items-center gap-2 border-b border-line text-[13px] last:border-0"> <StatusIcon status={t.status} /> <span className="w-12 shrink-0 font-mono text-[11px] text-fg-3 tabular-nums">{t.id}</span> <span className="truncate text-fg">{t.title}</span> </li> ))} </ul> </div> <CreateTaskDialog open={open} onClose={() => setOpen(false)} draftKey="tasks:draft-docs" /> </div> )}Usage
import { CreateTaskDialog, ShortcutsDialog } from '@rchursin/product/tasks'<CreateTaskDialog open={open} onClose={() => setOpen(false)} preset={{ status: 'in_progress' }} /><ShortcutsDialog open={help} onClose={() => setHelp(false)} />Examples
Shortcuts dialog
import { useState } from 'react'import { shortcutGroups, ShortcutsDialog } from '@rchursin/product/tasks'import { Button } from '@rchursin/product/ui'
/** The “?” dialog from the Tasks app. It reads shortcutGroups, the same list the kit page’s key tables use. */export default function ShortcutsExample() { const [open, setOpen] = useState(false) const count = shortcutGroups.reduce((n, g) => n + g.rows.length, 0)
return ( <div className="flex flex-col items-center gap-3"> <Button variant="secondary" icon="keyboard" onClick={() => setOpen(true)}> Keyboard shortcuts </Button> <p className="text-[12px] text-fg-3"> {count} shortcuts in {shortcutGroups.length} groups </p> <ShortcutsDialog open={open} onClose={() => setOpen(false)} /> </div> )}API
CreateTaskDialog props
| Prop | Type | Default | Description |
|---|---|---|---|
openrequired | boolean | – | Shows the dialog. |
onCloserequired | () => void | – | Esc, a click outside, the close button, or a create without “Create more”. |
preset | CreatePreset | – | { status?, priority?, assignee? } to start from, e.g. the group whose “+” was pressed. |
onOpenTask | (id: string) => void | – | Offers “Open” in the success toast. Without it, the toast offers “Undo”. |
draftKey | string | 'tasks:draft' | Where the draft is saved. Give each dialog on a page its own key. |
ShortcutsDialog props
| Prop | Type | Default | Description |
|---|---|---|---|
openrequired | boolean | – | Shows the dialog. |
onCloserequired | () => void | – | Esc, a click outside or the close button. |
shortcutGroups
| Prop | Type | Default | Description |
|---|---|---|---|
titlerequired | string | – | Group heading: “Move around”, “Change”, “Select”, “Move a card (on its grip)”. |
rowsrequired | { keys: string; alt?: string; action: string }[] | – | Each shortcut in combo syntax (“mod+b”), an optional second key, and what it does. |
Accessibility
Keyboard
| Key | What it does |
|---|---|
| Enter | In the title: move on to the description. |
| CtrlEnter | Create the task from any field. |
| Esc | Close the dialog and keep the draft. |
| Tab | Moves through the fields, the six property pickers, “Create more” and Create task. |
- Both are native modal <dialog>s: the page behind is inert, and focus goes back to the button that opened them.
- Focus starts in the title. Creating without one shows “Add a title to create the task.”, tied to the field with aria-describedby and aria-invalid, and puts focus back in it.
- When a draft comes back, a line says “Draft restored.” next to a Discard button.
- The six pickers sit in a group named “Properties”; each button names its current value.
- The shortcuts dialog starts focus on its list, so a screen reader begins with the content. Each shortcut is a term and description pair with <kbd> keys.
Guidelines
Do
- Pass a preset when people create from a group or a column, so the task lands where they expect.
- Give each CreateTaskDialog on a page its own draftKey.
- Build help screens and docs from shortcutGroups, so they never drift from the app.
Don’t
- Don’t disable Create task while the title is empty; let the press say what is missing.
- Don’t clear the draft on Esc; people close dialogs by accident.
In use
Related
- TasksThe whole task tracker: a keyboard-first list and board, a detail view, bulk edits, and undo for every change.
- DialogA modal on the native <dialog> element: the page behind goes inert, Esc closes it, and focus goes back to what opened it.
- Property pickersThe Tasks tool’s pickers for status, priority, assignee, labels and estimate, as a chip, a row or an icon.
- Key capsKeyboard keys cut from the same metal as the buttons, and shortcuts written as key caps.
- ToastShort past-tense results with at most one action, like “Run archived · Undo”. Read out politely; they wait while hovered or focused.
Source
The component’s own files, exactly as they run on this site.
/** * Create task: a modal dialog with a title, a description and a row of property pickers. * ⌘Enter / Ctrl+Enter creates. "Create more" keeps the dialog open (and the properties) for the next one. * Esc or a click outside closes it and keeps what you typed as a draft, restored next time. * Only the title is required; the error says so next to the field. */import { useEffect, useId, useRef, useState } from "react"import { ME, type PriorityId, type StatusId } from "../data/sample"import { Icon } from "../icons/Icon"import { isApple } from "../lib/hotkeys"import { Button } from "../ui/button"import { Dialog } from "../ui/dialog"import { ErrorText, Switch, Textarea } from "../ui/fields"import { useToast } from "../ui/toast"import { useTaskActions, type TaskDraft } from "./actions"import { labelIndex } from "./model"import { AssigneePicker, DueDatePicker, EstimatePicker,