deckhearth/components/scanner/ScannerToast.js

59 lines
1.8 KiB
JavaScript
Raw Permalink Normal View History

const ICON_MAP = {
success: { glyph: '✓', color: 'var(--color-success, #10B981)' },
error: { glyph: '✗', color: 'var(--color-error, #EF4444)' },
info: { glyph: '', color: 'var(--color-info, #3B82F6)' },
};
/**
* Floating toast overlay for the camera viewport.
*
* Always rendered in the DOM so CSS transitions work on enter and exit.
* The parent controls visibility via the `visible` prop and handles the
* auto-dismiss timer.
*/
export default function ScannerToast({ message, visible, type = 'success' }) {
const { glyph, color } = ICON_MAP[type] || ICON_MAP.success;
return (
<div
role="status"
aria-live="polite"
className="absolute left-1/2 z-20 pointer-events-none"
style={{
bottom: '33%',
transform: `translateX(-50%) translateY(${visible ? '0' : '12px'})`,
opacity: visible ? 1 : 0,
transition: 'opacity 200ms ease, transform 200ms ease',
visibility: visible ? 'visible' : 'hidden',
transitionProperty: 'opacity, transform, visibility',
transitionDelay: visible ? '0ms' : '0ms, 0ms, 200ms',
}}
>
<div
className="flex items-center gap-2 rounded-full px-4 py-3 shadow-lg pointer-events-auto"
style={{
backgroundColor: 'rgba(var(--bg-secondary-rgb), 0.9)',
backdropFilter: 'blur(12px)',
WebkitBackdropFilter: 'blur(12px)',
border: '1px solid var(--border)',
maxWidth: 280,
}}
>
<span
className="text-sm font-bold flex-shrink-0"
style={{ color }}
aria-hidden="true"
>
{glyph}
</span>
<span
className="text-sm font-medium truncate"
style={{ color: 'var(--text-primary)' }}
>
{message}
</span>
</div>
</div>
);
}