Add rules covering bug prevention (noLeakedRender, noFloatingPromises, noImportCycles, noReactForwardRef), security (noScriptUrl, noAlert), performance (noAwaitInLoops, useTopLevelRegex), and code style (noNestedTernary, useGlobalThis, useNullishCoalescing, useSortedClasses, plus ~40 more). Fix all violations: extract top-level regex constants, guard React && renders with boolean coercion, refactor nested ternaries, replace window with globalThis, sort Tailwind classes, and introduce expectDomainError test helper to eliminate conditional expects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
50 lines
1.2 KiB
TypeScript
50 lines
1.2 KiB
TypeScript
import { X } from "lucide-react";
|
|
import { useEffect } from "react";
|
|
import { createPortal } from "react-dom";
|
|
import { Button } from "./ui/button.js";
|
|
|
|
interface ToastProps {
|
|
message: string;
|
|
progress?: number;
|
|
onDismiss: () => void;
|
|
autoDismissMs?: number;
|
|
}
|
|
|
|
export function Toast({
|
|
message,
|
|
progress,
|
|
onDismiss,
|
|
autoDismissMs,
|
|
}: ToastProps) {
|
|
useEffect(() => {
|
|
if (autoDismissMs === undefined) return;
|
|
const timer = setTimeout(onDismiss, autoDismissMs);
|
|
return () => clearTimeout(timer);
|
|
}, [autoDismissMs, onDismiss]);
|
|
|
|
return createPortal(
|
|
<div className="fixed bottom-4 left-4 z-50">
|
|
<div className="flex items-center gap-3 rounded-lg border border-border bg-card px-4 py-3 shadow-lg">
|
|
<span className="text-foreground text-sm">{message}</span>
|
|
{progress !== undefined && (
|
|
<div className="h-2 w-24 overflow-hidden rounded-full bg-muted">
|
|
<div
|
|
className="h-full rounded-full bg-primary transition-all"
|
|
style={{ width: `${Math.round(progress * 100)}%` }}
|
|
/>
|
|
</div>
|
|
)}
|
|
<Button
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
onClick={onDismiss}
|
|
className="text-muted-foreground"
|
|
>
|
|
<X className="h-3 w-3" />
|
|
</Button>
|
|
</div>
|
|
</div>,
|
|
document.body,
|
|
);
|
|
}
|