Files
siftlode/frontend/src/components/ui/Pressable.tsx
T
peter edf2f22ef9 feat(design): titles in click surfaces stay selectable (E28 S3e4b)
A button whose text is a title or a name is now `selectable` (FocusSafeButton,
under Pressable and Button): Chrome selects inside a button once it is
user-select: text (measured), and the click that ends a selection, or a press
dragged past the slop, opens nothing. 23 surfaces carry it (cards, rows, people,
channels, playlists, users, views, messages, log lines, description links); the
Plex caption div gets the same guard. A contract test pins every plain Pressable
with its reason, so a new one has to choose.
2026-09-26 18:46:24 +02:00

38 lines
1.6 KiB
TypeScript

import { forwardRef } from "react";
import type { ButtonHTMLAttributes } from "react";
import clsx from "clsx";
import { FocusSafeButton } from "./FocusSafeButton";
// A click SURFACE: a button whose content is the control — a card, a list row, a poster tile, a menu
// row, a thumbnail that opens its video. It is not an action button (that is `Button`, with a label and
// a rung) and not a glyph (`IconButton`); it owns what every such surface needs and kept forgetting:
// `type="button"` (never a stray form submit), a left-aligned body, the focus-safe disabled state, and,
// on request, the B3 state layer for a row that has no hover of its own. `className` lays the surface
// out; its paint is the surface's own (a card's glass, a row's layer).
export const Pressable = forwardRef<
HTMLButtonElement,
ButtonHTMLAttributes<HTMLButtonElement> & {
/** Paint the B3 hover/pressed layer (a plain row or menu item; a card has its own hover). */
layer?: boolean;
/** A tile with a caption under a picture reads centred; everything else left. */
align?: "left" | "center";
/** Working: unavailable and announced as busy. */
busy?: boolean;
/** Its text is a title or a name: it stays selectable (see FocusSafeButton). */
selectable?: boolean;
}
>(function Pressable({ type, layer = false, align = "left", className, ...rest }, ref) {
return (
<FocusSafeButton
ref={ref}
type={type ?? "button"}
className={clsx(
align === "center" ? "text-center" : "text-left",
layer && "state-layer",
className,
)}
{...rest}
/>
);
});