Save workspace changes

This commit is contained in:
2026-04-18 17:02:56 +02:00
parent f02ea9a711
commit 87d60af5a9
4220 changed files with 1388603 additions and 1554 deletions

View File

@@ -0,0 +1,77 @@
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,
)
}