kopia lustrzana https://github.com/Tldraw/Tldraw
122 wiersze
3.1 KiB
TypeScript
122 wiersze
3.1 KiB
TypeScript
import * as T from '@radix-ui/react-toast'
|
|
import * as React from 'react'
|
|
import { AlertSeverity, TLUiToast, useToasts } from '../context/toasts'
|
|
import { useTranslation } from '../hooks/useTranslation/useTranslation'
|
|
import { TLUiIconType } from '../icon-types'
|
|
import { TldrawUiButton } from './primitives/Button/TldrawUiButton'
|
|
import { TldrawUiButtonLabel } from './primitives/Button/TldrawUiButtonLabel'
|
|
import { TldrawUiIcon } from './primitives/TldrawUiIcon'
|
|
|
|
const SEVERITY_TO_ICON: { [msg in AlertSeverity]: TLUiIconType } = {
|
|
success: 'check-circle',
|
|
warning: 'warning-triangle',
|
|
error: 'cross-circle',
|
|
info: 'info-circle',
|
|
}
|
|
|
|
function Toast({ toast }: { toast: TLUiToast }) {
|
|
const { removeToast } = useToasts()
|
|
const msg = useTranslation()
|
|
|
|
const onOpenChange = (isOpen: boolean) => {
|
|
if (!isOpen) {
|
|
removeToast(toast.id)
|
|
}
|
|
}
|
|
|
|
const hasActions = toast.actions && toast.actions.length > 0
|
|
|
|
const icon = toast.icon || (toast.severity && SEVERITY_TO_ICON[toast.severity])
|
|
|
|
return (
|
|
<T.Root
|
|
onOpenChange={onOpenChange}
|
|
className="tlui-toast__container"
|
|
duration={toast.keepOpen ? Infinity : 5000}
|
|
data-severity={toast.severity}
|
|
>
|
|
{icon && (
|
|
<div className="tlui-toast__icon">
|
|
<TldrawUiIcon icon={icon} />
|
|
</div>
|
|
)}
|
|
<div className="tlui-toast__main">
|
|
<div className="tlui-toast__content">
|
|
{toast.title && <T.Title className="tlui-toast__title">{toast.title}</T.Title>}
|
|
{toast.description && (
|
|
<T.Description className="tlui-toast__description">{toast.description}</T.Description>
|
|
)}
|
|
</div>
|
|
{toast.actions && (
|
|
<div className="tlui-toast__actions">
|
|
{toast.actions.map((action, i) => (
|
|
<T.Action key={i} altText={action.label} asChild onClick={action.onClick}>
|
|
<TldrawUiButton type={action.type}>
|
|
<TldrawUiButtonLabel>{action.label}</TldrawUiButtonLabel>
|
|
</TldrawUiButton>
|
|
</T.Action>
|
|
))}
|
|
<T.Close asChild>
|
|
<TldrawUiButton
|
|
type="normal"
|
|
className="tlui-toast__close"
|
|
style={{ marginLeft: 'auto' }}
|
|
>
|
|
<TldrawUiButtonLabel>{toast.closeLabel ?? msg('toast.close')}</TldrawUiButtonLabel>
|
|
</TldrawUiButton>
|
|
</T.Close>
|
|
</div>
|
|
)}
|
|
</div>
|
|
{!hasActions && (
|
|
<T.Close asChild>
|
|
<TldrawUiButton type="normal" className="tlui-toast__close">
|
|
<TldrawUiButtonLabel>{toast.closeLabel ?? msg('toast.close')}</TldrawUiButtonLabel>
|
|
</TldrawUiButton>
|
|
</T.Close>
|
|
)}
|
|
</T.Root>
|
|
)
|
|
}
|
|
|
|
function _Toasts() {
|
|
const { toasts } = useToasts()
|
|
|
|
return (
|
|
<>
|
|
{toasts.map((toast) => (
|
|
<Toast key={toast.id} toast={toast} />
|
|
))}
|
|
</>
|
|
)
|
|
}
|
|
|
|
export const Toasts = React.memo(_Toasts)
|
|
|
|
export function ToastViewport() {
|
|
const { toasts } = useToasts()
|
|
|
|
const [hasToasts, setHasToasts] = React.useState(false)
|
|
|
|
React.useEffect(() => {
|
|
let cancelled = false
|
|
if (toasts.length) {
|
|
setHasToasts(true)
|
|
} else {
|
|
setTimeout(() => {
|
|
if (!cancelled) {
|
|
setHasToasts(false)
|
|
}
|
|
}, 1000)
|
|
}
|
|
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [toasts.length, setHasToasts])
|
|
|
|
if (!hasToasts) return null
|
|
|
|
return <T.ToastViewport className="tlui-toast__viewport" />
|
|
}
|