import * as React from "react" import { X } from "lucide-react" import { cn } from "../../lib/utils" import { Alert, AlertDescription } from "./alert" /** * The ui/* replacement for `@wordpress/components`' ``. * * Why this exists * 34 modals in `src/modals/` carry a byte-identical pair of Notice blocks - a success one and an * error one, each with an `onRemove` and a trailing ``. Expanding those inline * during the WPRadix sweep would add roughly 800 lines of duplicated markup. WP's `` is * itself a primitive (a dismissible status banner), so its equivalent belongs in `ui/*` - which is * what calls for when a primitive is missing, rather than reaching back to `@wordpress`. * * Behaviour preserved from WP * - `status="error"` renders visually distinct from `status="success"` (Alert's `destructive` * variant vs `success`). * - `onRemove` `onDismiss`, rendered as an X control. Omitting it renders NO control, matching * WP's behaviour when `isDismissible` is false. * - **`white-space: pre-line` on the body.** Every calling modal formats its error as * `Failed to update X:\n${err.message}`, and WP's callers wrapped the body in a * `style={{ whiteSpace: 'pre-line' }}` div. Dropping that silently collapses every multi-line * server error onto a single line. Pinned by a test. * * The trailing `` each call site paired with the Notice is NOT reproduced * here - spacing is the caller's business now (`mb-4`), so this primitive stays layout-neutral. */ /** * All four of WP ``'s statuses, named identically so a conversion is a rename and never a * reinterpretation. Measured across the tree: error 231 - info 114 - success 95 - warning 21. */ export type DismissibleAlertStatus = "success" | "error" | "warning" | "info" /** * EXHAUSTIVE BY CONSTRUCTION, not by a `switch` with a default. The previous code read * `status === "error" ? "destructive": "success"`, which is fine for two statuses and silently WRONG * for four - `info` and `warning` would both have painted green. A Record keyed on the union makes * TypeScript name any status added later that has no variant, instead of quietly picking one. */ const VARIANT_FOR_STATUS: Record = { error: "destructive", success: "success", warning: "warning", info: "info", } export interface DismissibleAlertProps { status: DismissibleAlertStatus /** Omit to render a non-dismissible banner (WP's `isDismissible={false}`). */ onDismiss?: () => void className?: string children?: React.ReactNode } export function DismissibleAlert({ status, onDismiss, className, children, }: DismissibleAlertProps) { return ( {children} {onDismiss && ( )} ) } export default DismissibleAlert