refactor(ui): make an event row a single open target
The row already opened the event; the tick sat on top of it, so the two smallest targets on the page did different things a few pixels apart. "Done" was never the only thing a reader wants to say about an event either — status, effort and notes all live in the detail sheet. The tick becomes a chevron: decorative, aria-hidden, and leaning towards where the row is about to take you on hover. The full-bleed button is the only control, so there is no second stop for anyone tabbing or using a screen reader. Undo on a revealed ignored row stays a real button — it is a real action with nowhere better to sit. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b085087b05
commit
b1df7b2581
@@ -228,7 +228,6 @@ export function App() {
|
|||||||
status={prog.progress[row.event.id]?.status}
|
status={prog.progress[row.event.id]?.status}
|
||||||
effort={prog.progress[row.event.id]?.effort}
|
effort={prog.progress[row.event.id]?.effort}
|
||||||
ignored={ignored.marks[row.event.id] !== undefined}
|
ignored={ignored.marks[row.event.id] !== undefined}
|
||||||
onToggle={toggle}
|
|
||||||
onRestore={(id) => ignored.toggle(id)}
|
onRestore={(id) => ignored.toggle(id)}
|
||||||
onOpen={setOpenId}
|
onOpen={setOpenId}
|
||||||
/>
|
/>
|
||||||
@@ -246,7 +245,6 @@ export function App() {
|
|||||||
status={prog.progress[row.event.id]?.status}
|
status={prog.progress[row.event.id]?.status}
|
||||||
effort={prog.progress[row.event.id]?.effort}
|
effort={prog.progress[row.event.id]?.effort}
|
||||||
ignored={ignored.marks[row.event.id] !== undefined}
|
ignored={ignored.marks[row.event.id] !== undefined}
|
||||||
onToggle={toggle}
|
|
||||||
onRestore={(id) => ignored.toggle(id)}
|
onRestore={(id) => ignored.toggle(id)}
|
||||||
onOpen={setOpenId}
|
onOpen={setOpenId}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ interface EventRowProps {
|
|||||||
effort?: Effort | undefined;
|
effort?: Effort | undefined;
|
||||||
/** Only ever true when the reader has chosen to reveal ignored events. */
|
/** Only ever true when the reader has chosen to reveal ignored events. */
|
||||||
ignored?: boolean | undefined;
|
ignored?: boolean | undefined;
|
||||||
onToggle: (id: string) => void;
|
|
||||||
onRestore?: ((id: string) => void) | undefined;
|
onRestore?: ((id: string) => void) | undefined;
|
||||||
onOpen: (id: string) => void;
|
onOpen: (id: string) => void;
|
||||||
}
|
}
|
||||||
@@ -32,7 +31,6 @@ export function EventRow({
|
|||||||
status,
|
status,
|
||||||
effort,
|
effort,
|
||||||
ignored = false,
|
ignored = false,
|
||||||
onToggle,
|
|
||||||
onRestore,
|
onRestore,
|
||||||
onOpen,
|
onOpen,
|
||||||
}: EventRowProps) {
|
}: EventRowProps) {
|
||||||
@@ -156,9 +154,15 @@ export function EventRow({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sits above the row target so ticking done never opens the sheet.
|
{/* The trailing control used to tick an event done from the list. It
|
||||||
On a revealed ignored row this becomes the undo, which is the most
|
does not any more: "done" was never the only thing a reader wants
|
||||||
direct place to put it. */}
|
to say about an event, and a tick they can hit by accident on the
|
||||||
|
way to opening it is a bad trade. The row opens the sheet, where
|
||||||
|
status, effort and notes all live; this is just the affordance
|
||||||
|
saying so.
|
||||||
|
|
||||||
|
The one exception is a revealed ignored row, where undo is a real
|
||||||
|
action with nowhere better to sit. */}
|
||||||
{ignored && onRestore !== undefined ? (
|
{ignored && onRestore !== undefined ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -178,30 +182,39 @@ export function EventRow({
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
// Decorative: the full-bleed button behind the row is already the
|
||||||
type="button"
|
// control, and a second one with the same effect would just be an
|
||||||
onClick={() => onToggle(event.id)}
|
// extra stop for anyone tabbing or using a screen reader.
|
||||||
aria-pressed={completed}
|
<span
|
||||||
aria-label={
|
aria-hidden
|
||||||
completed ? `Mark ${event.title} not done` : `Mark ${event.title} done`
|
className={`row-check row-open grid size-7 shrink-0 place-items-center self-center rounded-md ${
|
||||||
}
|
completed ? "text-near" : "text-faint"
|
||||||
className={`row-check pointer-events-auto relative z-20 grid size-7 shrink-0 cursor-pointer place-items-center self-center rounded-md border ${
|
}`}
|
||||||
completed
|
>
|
||||||
? "border-transparent bg-near/20 text-near"
|
{completed ? (
|
||||||
: "border-hairline text-faint"
|
<svg viewBox="0 0 16 16" className="size-3.5">
|
||||||
}`}
|
<path
|
||||||
>
|
d="M2.5 8.5l3.5 3.5 7.5-8"
|
||||||
<svg viewBox="0 0 16 16" className="size-3.5" aria-hidden>
|
fill="none"
|
||||||
<path
|
stroke="currentColor"
|
||||||
d="M2.5 8.5l3.5 3.5 7.5-8"
|
strokeWidth="2"
|
||||||
fill="none"
|
strokeLinecap="round"
|
||||||
stroke="currentColor"
|
strokeLinejoin="round"
|
||||||
strokeWidth="2"
|
/>
|
||||||
strokeLinecap="round"
|
</svg>
|
||||||
strokeLinejoin="round"
|
) : (
|
||||||
/>
|
<svg viewBox="0 0 16 16" className="size-3.5">
|
||||||
</svg>
|
<path
|
||||||
</button>
|
d="M6 3l5 5-5 5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.8"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -176,6 +176,13 @@ body {
|
|||||||
border-color: var(--color-faint);
|
border-color: var(--color-faint);
|
||||||
color: var(--color-muted);
|
color: var(--color-muted);
|
||||||
}
|
}
|
||||||
|
/* The chevron leans towards where the row is about to take you. It is the
|
||||||
|
only thing left on the right of a row, so it has to carry "this opens"
|
||||||
|
on its own. */
|
||||||
|
.event-row:hover .row-open {
|
||||||
|
transform: translateX(2px);
|
||||||
|
color: var(--color-ink);
|
||||||
|
}
|
||||||
.row-check:hover {
|
.row-check:hover {
|
||||||
transform: scale(1.08);
|
transform: scale(1.08);
|
||||||
border-color: var(--color-muted);
|
border-color: var(--color-muted);
|
||||||
@@ -212,6 +219,7 @@ body {
|
|||||||
}
|
}
|
||||||
.row-check:hover,
|
.row-check:hover,
|
||||||
.row-check:active,
|
.row-check:active,
|
||||||
|
.event-row:hover .row-open,
|
||||||
.event-row:hover .row-rail {
|
.event-row:hover .row-rail {
|
||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user