Add custom intervention modals
This commit is contained in:
@@ -32,6 +32,10 @@ const CardStackContainer = memo<CardStackProps>(
|
||||
headerLockupSize,
|
||||
toggleAlignment = "center",
|
||||
className = "",
|
||||
showAddCard = false,
|
||||
addCardLabel = "",
|
||||
addCardAriaLabel = "",
|
||||
onAddCard,
|
||||
}) => {
|
||||
const [internalExpanded, setInternalExpanded] = useState(false);
|
||||
const [internalSelectedIds, setInternalSelectedIds] = useState<string[]>(
|
||||
@@ -90,6 +94,10 @@ const CardStackContainer = memo<CardStackProps>(
|
||||
headerLockupSize={headerLockupSize}
|
||||
toggleAlignment={toggleAlignment}
|
||||
className={className}
|
||||
showAddCard={showAddCard}
|
||||
addCardLabel={addCardLabel}
|
||||
addCardAriaLabel={addCardAriaLabel}
|
||||
onAddCard={onAddCard}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from "react";
|
||||
import type { HeaderLockupSizeValue } from "../../type/HeaderLockup/HeaderLockup.types";
|
||||
|
||||
export interface CardStackItem {
|
||||
@@ -18,7 +19,7 @@ export interface CardStackProps {
|
||||
toggleLabel?: string;
|
||||
showLessLabel?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
description?: ReactNode;
|
||||
/** "default" = compact grid/column + expanded grid; "singleStack" = always one column, expand shows more in same stack */
|
||||
layout?: "default" | "singleStack";
|
||||
/**
|
||||
@@ -45,6 +46,11 @@ export interface CardStackProps {
|
||||
/** Alignment of the expand/collapse control in `singleStack` layout (Figma right-rail: end). */
|
||||
toggleAlignment?: "center" | "end";
|
||||
className?: string;
|
||||
/** Optional “Add” entry (e.g. custom method card wizard). */
|
||||
showAddCard?: boolean;
|
||||
addCardLabel?: string;
|
||||
addCardAriaLabel?: string;
|
||||
onAddCard?: () => void;
|
||||
}
|
||||
|
||||
export interface CardStackViewProps {
|
||||
@@ -57,7 +63,7 @@ export interface CardStackViewProps {
|
||||
toggleLabel: string;
|
||||
showLessLabel: string;
|
||||
title: string;
|
||||
description: string;
|
||||
description: ReactNode;
|
||||
layout: "default" | "singleStack";
|
||||
compactRecommendedLimit: number;
|
||||
compactCardIds: string[] | undefined;
|
||||
@@ -65,4 +71,8 @@ export interface CardStackViewProps {
|
||||
headerLockupSize: HeaderLockupSizeValue | undefined;
|
||||
toggleAlignment: "center" | "end";
|
||||
className: string;
|
||||
showAddCard: boolean;
|
||||
addCardLabel: string;
|
||||
addCardAriaLabel: string;
|
||||
onAddCard?: () => void;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,77 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import HeaderLockup from "../../type/HeaderLockup";
|
||||
import type { HeaderLockupSizeValue } from "../../type/HeaderLockup/HeaderLockup.types";
|
||||
import Selection from "../Selection";
|
||||
import type { CardStackViewProps } from "./CardStack.types";
|
||||
|
||||
function CardStackHeaderLockup({
|
||||
title,
|
||||
description,
|
||||
justification,
|
||||
size,
|
||||
wrapperClassName,
|
||||
}: {
|
||||
title: string;
|
||||
description: ReactNode;
|
||||
justification: "center" | "left";
|
||||
size: HeaderLockupSizeValue;
|
||||
wrapperClassName?: string;
|
||||
}) {
|
||||
if (!title && !description) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={wrapperClassName ?? "min-w-0"}>
|
||||
<HeaderLockup
|
||||
title={title}
|
||||
description={description}
|
||||
justification={justification}
|
||||
size={size}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CardStackAddCardButton({
|
||||
label,
|
||||
ariaLabel,
|
||||
onClick,
|
||||
}: {
|
||||
label: string;
|
||||
ariaLabel: string;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
aria-label={ariaLabel}
|
||||
className="flex min-h-[88px] w-full shrink-0 items-center justify-center rounded-[var(--measures-radius-medium,8px)] bg-[var(--color-surface-default-secondary)] font-inter text-base font-medium text-[var(--color-content-default-primary)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-invert-primary)]"
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden
|
||||
>
|
||||
<path
|
||||
d="M12 5v14M5 12h14"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
{label}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function CardStackView({
|
||||
cards,
|
||||
selectedIds,
|
||||
@@ -22,7 +90,19 @@ export function CardStackView({
|
||||
headerLockupSize,
|
||||
toggleAlignment,
|
||||
className,
|
||||
showAddCard,
|
||||
addCardLabel,
|
||||
addCardAriaLabel,
|
||||
onAddCard,
|
||||
}: CardStackViewProps) {
|
||||
const addTile =
|
||||
showAddCard && onAddCard && addCardLabel.length > 0 ? (
|
||||
<CardStackAddCardButton
|
||||
label={addCardLabel}
|
||||
ariaLabel={addCardAriaLabel || addCardLabel}
|
||||
onClick={onAddCard}
|
||||
/>
|
||||
) : null;
|
||||
const lockupSize = headerLockupSize ?? "L";
|
||||
const isSelected = (id: string) => selectedIds.includes(id);
|
||||
// Compact: explicit `compactCardIds` (caller-driven, used by create-flow
|
||||
@@ -47,16 +127,13 @@ export function CardStackView({
|
||||
const displayedCards = expanded ? cards : compactCards;
|
||||
return (
|
||||
<div className={`flex w-full flex-col gap-6 min-w-0 ${className}`}>
|
||||
{title || description ? (
|
||||
<div className="min-w-0 shrink-0">
|
||||
<HeaderLockup
|
||||
title={title}
|
||||
description={description}
|
||||
justification="center"
|
||||
size={lockupSize}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<CardStackHeaderLockup
|
||||
title={title}
|
||||
description={description}
|
||||
justification="center"
|
||||
size={lockupSize}
|
||||
wrapperClassName="min-w-0 shrink-0"
|
||||
/>
|
||||
<div className="flex w-full min-w-0 flex-col gap-2">
|
||||
{displayedCards.map((item) => (
|
||||
<Selection
|
||||
@@ -71,6 +148,7 @@ export function CardStackView({
|
||||
onClick={() => onCardSelect(item.id)}
|
||||
/>
|
||||
))}
|
||||
{addTile}
|
||||
</div>
|
||||
{hasMore ? (
|
||||
<button
|
||||
@@ -89,16 +167,12 @@ export function CardStackView({
|
||||
|
||||
return (
|
||||
<div className={`flex w-full flex-col gap-6 min-w-0 ${className}`}>
|
||||
{title || description ? (
|
||||
<div className="min-w-0">
|
||||
<HeaderLockup
|
||||
title={title}
|
||||
description={description}
|
||||
justification="center"
|
||||
size={lockupSize}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<CardStackHeaderLockup
|
||||
title={title}
|
||||
description={description}
|
||||
justification="center"
|
||||
size={lockupSize}
|
||||
/>
|
||||
|
||||
{expanded ? (
|
||||
<div className="mx-auto grid w-full max-w-[min(100%,860px)] grid-cols-1 gap-x-4 gap-y-6 md:grid-cols-2">
|
||||
@@ -115,6 +189,9 @@ export function CardStackView({
|
||||
onClick={() => onCardSelect(item.id)}
|
||||
/>
|
||||
))}
|
||||
{addTile ? (
|
||||
<div className="min-w-0 md:col-span-2">{addTile}</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : compactDesktopLayout === "pyramidFive" ? (
|
||||
<>
|
||||
@@ -133,6 +210,7 @@ export function CardStackView({
|
||||
onClick={() => onCardSelect(item.id)}
|
||||
/>
|
||||
))}
|
||||
{addTile}
|
||||
</div>
|
||||
<div className="mx-auto hidden w-full max-w-[min(100%,860px)] md:block">
|
||||
{/*
|
||||
@@ -228,6 +306,11 @@ export function CardStackView({
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{addTile ? (
|
||||
<div className="mx-auto hidden w-full max-w-[min(100%,860px)] md:block">
|
||||
{addTile}
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
) : compactDesktopLayout === "flexWrap" ? (
|
||||
<>
|
||||
@@ -246,6 +329,7 @@ export function CardStackView({
|
||||
onClick={() => onCardSelect(item.id)}
|
||||
/>
|
||||
))}
|
||||
{addTile}
|
||||
</div>
|
||||
{/* md–lg: pyramid (2 + 1), each row centered; lg+: one centered row (not edge-to-edge in a 2-col grid) */}
|
||||
{compactCards.length === 3 ? (
|
||||
@@ -280,6 +364,9 @@ export function CardStackView({
|
||||
onClick={() => onCardSelect(compactCards[2].id)}
|
||||
/>
|
||||
</div>
|
||||
{addTile ? (
|
||||
<div className="flex w-full justify-center px-2">{addTile}</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mx-auto hidden w-full max-w-[min(100%,860px)] flex-wrap justify-center gap-2 lg:flex">
|
||||
{compactCards.map((item) => (
|
||||
@@ -297,6 +384,11 @@ export function CardStackView({
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{addTile ? (
|
||||
<div className="mx-auto hidden w-full max-w-[min(100%,860px)] lg:flex lg:justify-center">
|
||||
<div className="w-full min-w-[281px] max-w-[574px]">{addTile}</div>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<div className="mx-auto hidden w-full max-w-[min(100%,860px)] flex-wrap justify-center gap-2 md:flex">
|
||||
@@ -318,6 +410,11 @@ export function CardStackView({
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{addTile ? (
|
||||
<div className="flex w-full min-w-0 shrink-0 justify-center md:w-[281px] md:max-w-[574px] md:flex-[1_1_100%]">
|
||||
{addTile}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
@@ -338,6 +435,7 @@ export function CardStackView({
|
||||
onClick={() => onCardSelect(item.id)}
|
||||
/>
|
||||
))}
|
||||
{addTile}
|
||||
</div>
|
||||
{/* Compact 640+: 6-col grid so each card spans 2; second row centered (cols 2–3 and 4–5) */}
|
||||
<div className="hidden md:grid grid-cols-6 gap-x-4 gap-y-6 w-full">
|
||||
@@ -365,6 +463,9 @@ export function CardStackView({
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{addTile ? (
|
||||
<div className="col-span-6 min-w-0">{addTile}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user