48 lines
1.5 KiB
TypeScript
48 lines
1.5 KiB
TypeScript
import * as React from 'react';
|
|
import { AlertCircle, CheckCircle2, Info, X } from 'lucide-react';
|
|
import { cn } from '../../lib/utils';
|
|
import { Button } from './button';
|
|
|
|
export type ScreenMessageVariant = 'info' | 'success' | 'error';
|
|
|
|
export interface ScreenMessageProps {
|
|
className?: string;
|
|
duration?: number;
|
|
message: string;
|
|
open?: boolean;
|
|
variant?: ScreenMessageVariant;
|
|
onClose?: () => void;
|
|
}
|
|
|
|
const iconMap: Record<ScreenMessageVariant, React.ReactNode> = {
|
|
error: <AlertCircle size={16} />,
|
|
info: <Info size={16} />,
|
|
success: <CheckCircle2 size={16} />,
|
|
};
|
|
|
|
export function ScreenMessage(props: ScreenMessageProps) {
|
|
const { duration = 3600, message, onClose, open = Boolean(message), variant = 'info' } = props;
|
|
|
|
React.useEffect(() => {
|
|
if (!open || !message || duration <= 0 || !onClose) return undefined;
|
|
const timer = window.setTimeout(onClose, duration);
|
|
return () => window.clearTimeout(timer);
|
|
}, [duration, message, onClose, open]);
|
|
|
|
if (!open || !message) return null;
|
|
|
|
return (
|
|
<div className="screenMessageViewport" role="presentation">
|
|
<div className={cn('screenMessage', `screenMessage-${variant}`, props.className)} role="alert">
|
|
<span className="screenMessageIcon">{iconMap[variant]}</span>
|
|
<span className="screenMessageText">{message}</span>
|
|
{onClose && (
|
|
<Button type="button" variant="ghost" size="icon" className="screenMessageClose" aria-label="关闭提示" onClick={onClose}>
|
|
<X size={14} />
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|