Simplify habit creation with editable starters and compact colors

This commit is contained in:
syntaxbullet
2026-09-05 07:51:22 +02:00
parent a2f5616b96
commit 9f8154e412
3 changed files with 130 additions and 7 deletions

View File

@@ -7,6 +7,12 @@ import { HabitColorPicker } from "./design-system/HabitColorPicker";
import { habitInput, type Schedule } from "../habits/contracts";
import { habitRequest } from "../lib/dashboard";
const STARTERS = [
{ label: "Reading", name: "Read", method: "count", target: 10, unit: "pages", tasks: [""], color: "#977344" },
{ label: "Get outside", name: "Get outside", method: "manual", target: 1, unit: "times", tasks: [""], color: "#58765b" },
{ label: "Evening routine", name: "Wind down", method: "tasks", target: 1, unit: "times", tasks: ["Put things away", "Prepare for tomorrow"], color: "#79618d" },
] as const;
/** Account behavior composed from the design system's existing form components. */
export function HabitForm({
date,
@@ -23,14 +29,26 @@ export function HabitForm({
const saving = useRef(false);
const [name, setName] = useState("");
const [method, setMethod] = useState<"manual" | "count" | "tasks">("manual");
const [target, setTarget] = useState(8);
const [unit, setUnit] = useState("glasses");
const [target, setTarget] = useState(10);
const [unit, setUnit] = useState("pages");
const [tasks, setTasks] = useState([""]);
const [schedule, setSchedule] = useState<Schedule>({ type: "daily" });
const [color, setColor] = useState("#58765b");
const [busy, setBusy] = useState(false);
const [error, setError] = useState("");
function applyStarter(starter: (typeof STARTERS)[number] | null) {
setName(starter?.name ?? "");
setMethod(starter?.method ?? "manual");
setTarget(starter?.target ?? 10);
setUnit(starter?.unit ?? "pages");
setTasks(starter ? [...starter.tasks] : [""]);
setColor(starter?.color ?? "#58765b");
setSchedule({ type: "daily" });
setError("");
form.current?.querySelector<HTMLInputElement>("input")?.focus();
}
async function submit(event: FormEvent) {
event.preventDefault();
if (saving.current) return;
@@ -78,6 +96,15 @@ export function HabitForm({
<Modal id="new-habit" eyebrow="A NEW HABIT" title={<>Make it <em>yours.</em></>} description="Choose what counts as complete. Your schedule follows your account’s timezone." onClose={onCancel} closeLabel="Close new habit" busy={busy} size="compact" initialFocus="input" returnFocus={() => document.getElementById("add-habit")}>
<form ref={form} className="ds-edit-content" onSubmit={submit}>
<fieldset className="ds-task-editor-fieldset" disabled={busy}>
<div className="ds-habit-starters" role="group" aria-label="Start with an idea">
<p className="ds-footnote">Start with an idea, then make it your own.</p>
<div className="ds-habit-starter-options">
{STARTERS.map((starter) => (
<Button key={starter.label} variant="secondary" onClick={() => applyStarter(starter)}>{starter.label}</Button>
))}
<Button variant="text" onClick={() => applyStarter(null)}>Start from scratch</Button>
</div>
</div>
<Field label="Habit name">
{(id) => (
<input
@@ -101,13 +128,13 @@ export function HabitForm({
>
<option value="manual">A simple check-in</option>
<option value="count">A count target</option>
<option value="tasks">A list of tasks</option>
<option value="tasks">A routine checklist</option>
</select>
)}
</Field>
{method === "count" && (
<div className="ds-form-grid">
<Field label="Daily target">
<Field label="Target per scheduled day">
{(id) => (
<input
id={id}
@@ -186,7 +213,7 @@ export function HabitForm({
onChange={setSchedule}
anchorDate={date}
/>
<HabitColorPicker value={color} onChange={setColor} mode="create" />
<HabitColorPicker value={color} onChange={setColor} mode="create" compact />
</fieldset>
{error && (
<p className="ds-form-feedback" role="alert">