Inline confirm
StableA question asked in place of the buttons, never with window.confirm: “Abandon this session?” with Keep going first.
AbandonQuestion is the focus timer’s version: it says what would be lost (“The 12 minutes so far won’t be logged”), puts the safe choice first and treats Esc as keep going. It replaces the controls where they stood, so nothing jumps and no dialog covers the clock.
Updated View source
Abandon a focus session
import { useEffect, useRef, useState } from 'react'import { AbandonQuestion, PhaseChip } from '@rchursin/product/timer'import { Button } from '@rchursin/product/ui'
type View = 'asking' | 'running' | 'abandoned'
/** * The question replaces the controls in place: no window.confirm, no dialog. It opens on “Keep * going” (the safe choice), Esc means keep going, and focus always lands on the control that * replaced the one you pressed. */export default function InlineConfirmSession() { const [view, setView] = useState<View>('asking') const [paused, setPaused] = useState(false) const [focusNext, setFocusNext] = useState<'keep' | 'abandon' | 'start' | 'pause' | null>(null) const keepRef = useRef<HTMLButtonElement>(null) const abandonRef = useRef<HTMLButtonElement>(null) const startRef = useRef<HTMLButtonElement>(null) const pauseRef = useRef<HTMLButtonElement>(null)
Usage
import { AbandonQuestion } from '@rchursin/product/timer'{asking ? ( <AbandonQuestion spent="12 minutes" keepRef={keepRef} onKeep={keepGoing} onAbandon={abandon} />) : ( <Button variant="ghost" icon="x" onClick={ask}>Abandon</Button>)}API
AbandonQuestion props
| Prop | Type | Default | Description |
|---|---|---|---|
spentrequired | string | – | How much would be lost, in words: “12 minutes”. |
keepRef | RefObject<HTMLButtonElement | null> | – | Put on Keep going, so you can focus it when the question appears. |
onKeeprequired | () => void | – | Keep going, and Esc anywhere inside the question. |
onAbandonrequired | () => void | – | Abandon session. |
className | string | – | Classes on the outlined box. |
Accessibility
Keyboard
| Key | What it does |
|---|---|
| TabShiftTab | Between Keep going and Abandon session. |
| Esc | Anywhere inside the question: keep going. |
| EnterSpace | Presses the focused button. |
- It is a role="group" named by the question, so moving into it reads “Abandon this session? The 12 minutes so far won’t be logged.”
- It doesn’t move focus itself. Focus Keep going (keepRef) when it appears, and put focus on whatever replaces it afterwards; FocusSession and the example do both.
- The safe choice comes first and takes focus; the destructive one is named in full, “Abandon session”.
- The wording is fixed for focus sessions. For other questions, copy the pattern.
Guidelines
Do
- Ask in place, where the button was.
- Say exactly what will be lost.
- Start focus on the safe choice.
Don’t
- Don’t ask about things that can be undone; act and offer Undo in a toast.
- Don’t use window.confirm or a modal for a one-line question.
- Don’t leave focus behind when the question goes away.
In use
Related
- Focus sessionA Pomodoro session: choose a task, say what you’ll finish, then focus and break in phases, with overtime and an inline Abandon.
- DialogA modal on the native <dialog> element: the page behind goes inert, Esc closes it, and focus goes back to what opened it.
- 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.
/** * A focus session (Pomodoro): pick a task, say what you intend to finish, and work in phases: * Focus → short break → Focus … → long break after four. All lengths are adjustable. * * Past zero a phase keeps counting as overtime ("+02:13") until you finish it, unless "start the next phase by * itself" is on. Finish logs the time (toast with Undo); Abandon asks inline first and logs nothing. * * Keys (while focus is inside the session): Space start / pause · F finish · Esc closes the Abandon question. */import { useEffect, useId, useRef, useState, type CSSProperties, type RefObject } from "react"import { cx } from "../../components/cx"import { statusById } from "../data/sample"import { Icon } from "../icons/Icon"import { formatDuration, formatTime } from "../lib/dates"import { useHotkeys } from "../lib/hotkeys"import { Button, IconButton } from "../ui/button"import { Field, Switch, TextInput } from "../ui/fields"import { PickerButton } from "../ui/picker"import { Popover, usePopover } from "../ui/popover"import {