63 lines
2.2 KiB
JavaScript
63 lines
2.2 KiB
JavaScript
/**
|
|
* The light popup stack: the frame-wide overlay entry this package owns.
|
|
*
|
|
* @module @dsh-plugin/session-notify/client/ui/ToastLayer
|
|
*/
|
|
|
|
import * as React from 'react'
|
|
import { KindIcon, CloseIcon } from './icons.js'
|
|
import { seatTranslator, useOwnStore } from './hooks.js'
|
|
import { CSS } from './styles.js'
|
|
|
|
const h = React.createElement
|
|
|
|
export function ToastLayer(props) {
|
|
const snapshot = useOwnStore(props.store)
|
|
const toasts = snapshot.toasts
|
|
const tr = seatTranslator(props, props.fallbackT)
|
|
React.useEffect(() => {
|
|
if (toasts.length === 0) return undefined
|
|
const onKeyDown = (event) => {
|
|
if (event.key !== 'Escape' || event.defaultPrevented) return
|
|
const top = toasts[0]
|
|
if (top === undefined) return
|
|
event.preventDefault()
|
|
props.dismiss(top.id)
|
|
}
|
|
document.addEventListener('keydown', onKeyDown)
|
|
return () => {
|
|
document.removeEventListener('keydown', onKeyDown)
|
|
}
|
|
}, [toasts, props])
|
|
if (toasts.length === 0) return null
|
|
return h(React.Fragment, null,
|
|
h('style', null, CSS),
|
|
h('div', { className: 'dsn-stack', role: 'region', 'aria-live': 'polite' },
|
|
toasts.map((toast) => h('div', {
|
|
key: toast.id,
|
|
className: 'dsn-toast',
|
|
role: toast.kind === 'completion' ? 'status' : 'alert',
|
|
onMouseEnter: () => props.hold(toast.id),
|
|
onMouseLeave: () => props.arm(toast.id),
|
|
},
|
|
h('span', { className: `dsn-toast-icon is-${toast.kind}` }, h(KindIcon, { kind: toast.kind, size: 16 })),
|
|
h('div', { className: 'dsn-toast-text' },
|
|
h('div', { className: 'dsn-toast-title' }, toast.title),
|
|
h('div', { className: 'dsn-toast-desc' }, toast.body)),
|
|
h('div', { className: 'dsn-toast-actions' },
|
|
h('button', {
|
|
type: 'button',
|
|
className: 'dsn-toast-action',
|
|
onClick: () => {
|
|
props.dismiss(toast.id)
|
|
props.open(toast.sessionId)
|
|
},
|
|
}, tr('toast.view')),
|
|
h('button', {
|
|
type: 'button',
|
|
className: 'dsn-toast-close',
|
|
'aria-label': tr('toast.dismiss'),
|
|
onClick: () => props.dismiss(toast.id),
|
|
}, h(CloseIcon, { size: 14 })))))))
|
|
}
|