Files
SkinbaseNova/resources/js/components/ui/ShareToast.jsx
T
2026-03-28 19:15:39 +01:00

78 lines
2.7 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useState } from 'react'
import { createPortal } from 'react-dom'
/**
* ShareToast — a minimal, auto-dismissing toast notification.
*
* Props:
* message – text to display
* visible – whether the toast is currently shown
* onHide – callback when the toast finishes (auto-hidden after ~2 s)
* duration – ms before auto-dismiss (default 2000)
* variant – success or error tone (default success)
*/
export default function ShareToast({ message = 'Link copied!', visible = false, onHide, duration = 2000, variant = 'success' }) {
const [show, setShow] = useState(false)
const config = variant === 'error'
? {
border: 'border-rose-300/25',
background: 'bg-rose-950/90',
text: 'text-rose-50',
icon: 'text-rose-300',
role: 'alert',
live: 'assertive',
iconPath: 'M12 9v3.75m0 3.75h.007v.008H12v-.008ZM10.29 3.86 1.82 18a1.875 1.875 0 0 0 1.606 2.813h16.148A1.875 1.875 0 0 0 21.18 18L12.71 3.86a1.875 1.875 0 0 0-3.42 0Z',
}
: {
border: 'border-white/[0.10]',
background: 'bg-nova-800/90',
text: 'text-white',
icon: 'text-emerald-400',
role: 'status',
live: 'polite',
iconPath: 'M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z',
}
useEffect(() => {
if (visible) {
// Small delay so the enter transition plays
const enterTimer = requestAnimationFrame(() => setShow(true))
const hideTimer = setTimeout(() => {
setShow(false)
setTimeout(() => onHide?.(), 200) // let exit transition finish
}, duration)
return () => {
cancelAnimationFrame(enterTimer)
clearTimeout(hideTimer)
}
} else {
setShow(false)
}
}, [visible, duration, onHide])
if (!visible) return null
return createPortal(
<div
role={config.role}
aria-live={config.live}
className={[
'fixed bottom-24 left-1/2 z-[10001] -translate-x-1/2 rounded-full border px-5 py-2.5 text-sm font-medium shadow-xl backdrop-blur-md transition-all duration-200',
config.border,
config.background,
config.text,
show ? 'translate-y-0 opacity-100' : 'translate-y-3 opacity-0',
].join(' ')}
>
<span className="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className={`h-4 w-4 ${config.icon}`}>
<path fillRule="evenodd" d={config.iconPath} clipRule="evenodd" />
</svg>
{message}
</span>
</div>,
document.body,
)
}