54 lines
1.9 KiB
TypeScript
54 lines
1.9 KiB
TypeScript
import { XIcon } from "~/components/ui/icons/vendor";
|
|
import { ReactNode } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Link } from "react-router";
|
|
import { BannerAlert } from "~/ui/components/BannerAlert";
|
|
|
|
// Subframe migration (strangler shim):
|
|
// Keeps the original ErrorBanner API but renders the Subframe design-system BannerAlert
|
|
// (type="error"). BannerAlert has no separate title slot, so the title is composed into the
|
|
// content (rendered bold) alongside text/redirect/children — same as before, all ReactNode.
|
|
// `redirect` maps to BannerAlert's `actionLabel` (the underlined action). `onDismiss` is NOT
|
|
// mapped to BannerAlert's `dismissible` button because that button is a non-functional design
|
|
// mock (no onClick); the original functional dismiss button is overlaid to preserve behavior.
|
|
|
|
interface Props {
|
|
title?: ReactNode;
|
|
text?: ReactNode | string;
|
|
redirect?: string;
|
|
children?: ReactNode;
|
|
onDismiss?: () => void;
|
|
}
|
|
|
|
export default function ErrorBanner({ title, text = "", redirect, children, onDismiss }: Props) {
|
|
const { t } = useTranslation();
|
|
return (
|
|
<div className="not-prose relative">
|
|
<BannerAlert
|
|
type="error"
|
|
fillContainer
|
|
actionLabel={
|
|
redirect ? (
|
|
<Link to={redirect}>
|
|
{t("shared.goTo")} {redirect}
|
|
</Link>
|
|
) : undefined
|
|
}
|
|
>
|
|
<span className="font-medium">{title ?? t("shared.error")}</span>{" "}
|
|
{text}
|
|
{children}
|
|
</BannerAlert>
|
|
|
|
{onDismiss && (
|
|
<div className="absolute top-4 right-4">
|
|
<button type="button" onClick={onDismiss} className="text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-200">
|
|
<span className="sr-only">{t("shared.close")}</span>
|
|
<XIcon className="size-4" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|