optimizations

This commit is contained in:
2026-03-28 19:15:39 +01:00
parent 0b25d9570a
commit cab4fbd83e
509 changed files with 1016804 additions and 1605 deletions

View File

@@ -9,9 +9,29 @@ import { createPortal } from 'react-dom'
* 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 }) {
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) {
@@ -35,17 +55,19 @@ export default function ShareToast({ message = 'Link copied!', visible = false,
return createPortal(
<div
role="status"
aria-live="polite"
role={config.role}
aria-live={config.live}
className={[
'fixed bottom-24 left-1/2 z-[10001] -translate-x-1/2 rounded-full border border-white/[0.10] bg-nova-800/90 px-5 py-2.5 text-sm font-medium text-white shadow-xl backdrop-blur-md transition-all duration-200',
'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">
{/* Check icon */}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 text-emerald-400">
<path fillRule="evenodd" d="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" clipRule="evenodd" />
<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>

View File

@@ -1,4 +1,4 @@
import React, { forwardRef } from 'react'
import React, { forwardRef, useId } from 'react'
/**
* Nova TextInput
@@ -26,7 +26,11 @@ const TextInput = forwardRef(function TextInput(
},
ref,
) {
const inputId = id ?? (label ? label.toLowerCase().replace(/\s+/g, '-') : undefined)
const generatedId = useId()
const labelSlug = typeof label === 'string'
? label.toLowerCase().replace(/\s+/g, '-')
: null
const inputId = id ?? labelSlug ?? `text-input-${generatedId.replace(/[:]/g, '')}`
const sizeClass = {
sm: 'py-1.5 text-xs',

View File

@@ -1,4 +1,4 @@
import React, { forwardRef } from 'react'
import React, { forwardRef, useId } from 'react'
/**
* Nova Textarea
@@ -14,7 +14,11 @@ const Textarea = forwardRef(function Textarea(
{ label, error, hint, required, rows = 4, resize = false, id, className = '', ...rest },
ref,
) {
const inputId = id ?? (label ? label.toLowerCase().replace(/\s+/g, '-') : undefined)
const generatedId = useId()
const labelSlug = typeof label === 'string'
? label.toLowerCase().replace(/\s+/g, '-')
: null
const inputId = id ?? labelSlug ?? `textarea-${generatedId.replace(/[:]/g, '')}`
const inputClass = [
'block w-full rounded-xl border bg-white/[0.06] text-white text-sm',