feat(design): the button families on the decided ladder, first half of the sweep (E28 S3b1)
Button is rebuilt to A1/B1/B3: explicit heights (xs 24 / sm 28 / md 32, lg 40 for a hero CTA), r8 (a pill on media), state layers instead of opacity and bg-card hovers, a `pressed` toggle, `overlay`, `media` and `link-muted` variants. The icon-only sizes move to the new IconButton (28 / 32, 48 only for the Play on a picture; a required label). Pressable (a card, row or tile that is the control) and DragHandle join them. The state-* utilities now stand down on a disabled, busy or pressed control, held by theme.contract.test.ts. Swept onto them: the Button icon-size callers and 16 files (Plex playlist, Plex detail, the YT player modal chrome, Welcome, Plex and video cards with AddToPlaylist and DownloadButton, the video editor, Feed, personal views, Scheduler, Users, the nav rail, Plex detail parts, Settings): 268 raw buttons are 153. Fixed on the way: FocusSafeButton overwrote a caller's aria-disabled (the "unavailable, here is why" state; adminInvites caught it), a season toggle and a Plex settings switch had no name/state, and the video card's overlay follows the C5 card (Resume pill, From the start pill, a round Play).
This commit is contained in:
@@ -13,6 +13,7 @@ import { formatDate } from "../lib/format";
|
||||
import { api, type DownloadJob, type ShareLink, type ShareRecipient } from "../lib/api";
|
||||
import { inputCls } from "./ui/form";
|
||||
import { Button } from "./ui/Button";
|
||||
import { IconButton } from "./ui/IconButton";
|
||||
import { FocusSafeButton } from "./ui/FocusSafeButton";
|
||||
const EXPIRY_DAYS = [null, 1, 7, 30] as const;
|
||||
|
||||
@@ -148,39 +149,35 @@ function LinkRow({ link, onChanged }: { link: ShareLink; onChanged: () => void }
|
||||
value={fullUrl}
|
||||
className="flex-1 min-w-0 bg-transparent text-xs text-muted truncate outline-hidden"
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
onClick={copy}
|
||||
aria-label={t("downloads.share.copy")}
|
||||
title={t("downloads.share.copy")}
|
||||
>
|
||||
<IconButton label={t("downloads.share.copy")} size="sm" variant="ghost" onClick={copy}>
|
||||
{copied ? <Check className="w-4 h-4 text-success" /> : <Copy className="w-4 h-4" />}
|
||||
</Button>
|
||||
</IconButton>
|
||||
{/* All three on the same rung. Only Copy was migrated in S1, which left one 2rem button
|
||||
between two 1.75rem ones with a different radius and hover fill — and, on a coarse
|
||||
pointer, `.touch-target` grew ONLY the migrated one to 2.75rem while the destructive
|
||||
Revoke stayed small (E10 review round 1). */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
onClick={() => {
|
||||
setNewPass("");
|
||||
setEditPass((v) => !v);
|
||||
}}
|
||||
<IconButton
|
||||
label={
|
||||
link.has_password
|
||||
? t("downloads.share.changePassword")
|
||||
: t("downloads.share.setPassword")
|
||||
}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
setNewPass("");
|
||||
setEditPass((v) => !v);
|
||||
}}
|
||||
// On the ICON, not the button: `clsx` does not merge Tailwind, so a colour className loses
|
||||
// to the variant's own `text-muted`/`hover:text-fg` by stylesheet order and the state was
|
||||
// simply invisible (E10 review round 2). The Copy button above already does it this way.
|
||||
icon={<Lock className={`w-4 h-4 ${link.has_password ? "text-accent" : ""}`} />}
|
||||
/>
|
||||
<Button
|
||||
>
|
||||
<Lock className={`w-4 h-4 ${link.has_password ? "text-accent" : ""}`} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
label={t("downloads.share.revoke")}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
onClick={async () => {
|
||||
if (
|
||||
await confirm({
|
||||
@@ -192,9 +189,9 @@ function LinkRow({ link, onChanged }: { link: ShareLink; onChanged: () => void }
|
||||
)
|
||||
revoke.mutate();
|
||||
}}
|
||||
label={t("downloads.share.revoke")}
|
||||
icon={<Trash2 className="w-4 h-4 text-danger" />}
|
||||
/>
|
||||
>
|
||||
<Trash2 className="w-4 h-4 text-danger" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-muted flex-wrap">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
|
||||
Reference in New Issue
Block a user