import { useId, type ReactNode } from "react"; // Shared MATCH / segmented primitive — a rounded-full pill track where exactly ONE option is active // (radio-like). Backs the feed any/all + or/and "Match" toggles and the feed Mine/Library scope bar, // and the Plex any/all combine toggles. The active paint comes entirely from the // `.pill[aria-pressed="true"]` recipe (index.css) — do NOT add bg/text/border-colour utilities on the // buttons here or a utility-layer class would beat the pressed fill. export interface SegmentedOption { value: T; label: string; icon?: ReactNode; title?: string; // optional per-option tooltip } export default function SegmentedToggle({ options, value, onChange, ariaLabel, label, title, fullWidth = false, size = "md", className, }: { options: readonly SegmentedOption[]; value: T; onChange: (v: T) => void; ariaLabel: string; // Optional left-hand caption; when set the control renders as a justify-between row (the feed/Plex // "Match" toggles). Omit for a standalone bar (the Mine/Library scope switch). label?: ReactNode; // What the choice means, beyond its name: the row's tooltip for the pointer, and the group's // accessible description for the keyboard and a screen reader — a `title` on a non-focusable box // alone reaches neither (it had only ever been the pointer's). title?: string; // Full-width track with flex-1 buttons (the scope bar); default is intrinsic width. fullWidth?: boolean; // md (28, text-xs) is the toolbar rung: it lines up with the search pill, the triggers and an `sm` // Button (DESIGN B1). sm (24, text-3xs) is only a side panel's captioned match toggle. md is the // default because the old sm default slid a pager and a toolbar count onto 10px type. size?: "sm" | "md"; className?: string; }) { const hintId = useId(); const track = (
{title && ( )} {options.map((o) => ( ))}
); if (label == null) { return className ?
{track}
: track; } return (
{label} {track}
); }