Method cards, chips, and the right rail now share the two-column shell; last options clear the footer fade, See all stays under the stack, and the left heading no longer jumps when the list expands. Co-authored-by: Cursor <cursoragent@cursor.com>
128 lines
3.8 KiB
TypeScript
128 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import Chip from "../../controls/Chip";
|
|
import Tag from "../../utility/Tag";
|
|
import type { SelectionViewProps } from "./Selection.types";
|
|
|
|
function InfoIcon() {
|
|
return (
|
|
<span
|
|
className="flex h-[var(--spacing-scale-016)] w-[var(--spacing-scale-016)] shrink-0 items-center justify-center rounded-full border border-[var(--color-content-invert-brand-secondary)] bg-transparent text-[10px] font-medium leading-none text-[var(--color-content-invert-brand-secondary)]"
|
|
aria-hidden
|
|
>
|
|
?
|
|
</span>
|
|
);
|
|
}
|
|
|
|
function SelectionTag({
|
|
recommended,
|
|
selected,
|
|
}: {
|
|
recommended: boolean;
|
|
selected: boolean;
|
|
}) {
|
|
if (selected) return <Tag variant="selected" />;
|
|
if (recommended) return <Tag variant="recommended" />;
|
|
return null;
|
|
}
|
|
|
|
function optionAriaLabel(label: string, supportText: string): string {
|
|
return supportText ? `${label}: ${supportText}` : label;
|
|
}
|
|
|
|
export function SelectionView({
|
|
label,
|
|
supportText,
|
|
recommended,
|
|
selected,
|
|
size,
|
|
showInfoIcon,
|
|
id: selectionId,
|
|
className,
|
|
onClick,
|
|
onKeyDown,
|
|
}: SelectionViewProps) {
|
|
const ariaLabel = optionAriaLabel(label, supportText);
|
|
|
|
if (size === "s") {
|
|
return (
|
|
<Chip
|
|
label={label}
|
|
state={selected ? "selected" : "unselected"}
|
|
size="m"
|
|
ariaLabel={ariaLabel}
|
|
className={className}
|
|
onClick={() => onClick()}
|
|
/>
|
|
);
|
|
}
|
|
|
|
const borderClass = "border border-[var(--color-border-default-primary)]";
|
|
const selectedBorder = selected
|
|
? "outline outline-4 outline-dashed outline-black outline-offset-[-4px]"
|
|
: "";
|
|
const cardIdProp = selectionId ? { "data-card-id": selectionId } : {};
|
|
|
|
if (size === "m") {
|
|
const baseClasses = `select-none rounded-[var(--measures-radius-200,8px)] bg-[var(--color-gray-000)] px-4 py-3 transition-[border-color,box-shadow,outline] duration-200 cursor-pointer ${borderClass} ${selectedBorder} ${className}`;
|
|
|
|
return (
|
|
<div
|
|
{...cardIdProp}
|
|
role="button"
|
|
tabIndex={0}
|
|
aria-label={ariaLabel}
|
|
aria-pressed={selected}
|
|
data-selection-size="m"
|
|
className={`${baseClasses} flex min-h-0 w-full flex-col items-start justify-center gap-1`}
|
|
onClick={onClick}
|
|
onKeyDown={onKeyDown}
|
|
>
|
|
<SelectionTag recommended={recommended} selected={selected} />
|
|
<span className="w-full text-medium-label text-[var(--color-content-invert-secondary)]">
|
|
{label}
|
|
</span>
|
|
{supportText ? (
|
|
<p className="w-full text-x-small-paragraph text-[var(--color-content-invert-tertiary)]">
|
|
{supportText}
|
|
</p>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const baseClasses = `select-none rounded-[var(--measures-radius-200,8px)] bg-[var(--color-gray-000)] p-4 transition-[border-color,box-shadow,outline] duration-200 cursor-pointer ${borderClass} ${selectedBorder} ${className}`;
|
|
|
|
return (
|
|
<div
|
|
{...cardIdProp}
|
|
role="button"
|
|
tabIndex={0}
|
|
aria-label={ariaLabel}
|
|
aria-pressed={selected}
|
|
data-selection-size="l"
|
|
className={`${baseClasses} flex flex-row items-center justify-between gap-4`}
|
|
onClick={onClick}
|
|
onKeyDown={onKeyDown}
|
|
>
|
|
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
<div className="flex min-w-0 items-center gap-1">
|
|
<span className="min-w-0 break-words text-medium-label text-[var(--color-content-invert-secondary)]">
|
|
{label}
|
|
</span>
|
|
{showInfoIcon ? <InfoIcon /> : null}
|
|
</div>
|
|
{supportText ? (
|
|
<p className="min-w-0 break-words text-x-small-paragraph text-[var(--color-content-invert-tertiary)]">
|
|
{supportText}
|
|
</p>
|
|
) : null}
|
|
</div>
|
|
<div className="shrink-0">
|
|
<SelectionTag recommended={recommended} selected={selected} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|