Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x | import { useState, type ReactNode, type JSX } from "react";
import { AlertTriangle, Check, Info, X } from "lucide-react";
import { type CalloutBuiltinProps, type CalloutVariant } from "@grackle-ai/common";
import styles from "./Callout.module.scss";
import { ICON_LG, ICON_MD } from "../../utils/iconSize.js";
// `variant` now lives in the built-in's zod schema (@grackle-ai/common);
// re-export the union so the package barrel keeps exposing CalloutVariant.
export type { CalloutVariant };
interface CalloutProps extends CalloutBuiltinProps {
children: ReactNode;
/** Optional extra class name for layout overrides. */
className?: string;
}
const VARIANT_ICONS: Record<CalloutVariant, ReactNode> = {
success: <Check size={ICON_LG} />,
error: <X size={ICON_LG} />,
warning: <AlertTriangle size={ICON_LG} />,
info: <Info size={ICON_LG} />,
};
/**
* Inline contextual alert for persistent, location-specific information.
* Use for things like blocked dependencies, validation errors, or
* status messages that belong within a specific panel rather than a toast.
*/
export function Callout({
variant = "info",
children,
dismissible = false,
className,
}: CalloutProps): JSX.Element {
const [dismissed, setDismissed] = useState(false);
return (
<>
{!dismissed && (
<div
className={[styles.callout, styles[variant], className].filter(Boolean).join(" ")}
role={variant === "error" || variant === "warning" ? "alert" : "status"}
>
<span className={styles.icon} aria-hidden="true">
{VARIANT_ICONS[variant]}
</span>
<span className={styles.content}>{children}</span>
{dismissible && (
<button
type="button"
className={styles.close}
onClick={() => setDismissed(true)}
aria-label="Dismiss"
>
<X size={ICON_MD} aria-hidden="true" />
</button>
)}
</div>
)}
</>
);
}
|