diff options
Diffstat (limited to 'frontend/ts/src/ui/modal.ts')
-rw-r--r-- | frontend/ts/src/ui/modal.ts | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/frontend/ts/src/ui/modal.ts b/frontend/ts/src/ui/modal.ts new file mode 100644 index 0000000..2d5e275 --- /dev/null +++ b/frontend/ts/src/ui/modal.ts @@ -0,0 +1,67 @@ +import { h, Html } from 'lib/rx' +import * as rx from 'lib/rx' + +interface Params { + header: Html + body: Html + onClose: () => void + className?: string + onmount?: (element: Element) => void + onunmount?: (element: Element) => void +} + +export function view({ header, body, onClose, className, onmount, onunmount }: Params): Html { + return rx.withState<boolean>(false, closingVar => { + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') onClose() + } + + return h('div', + { className: closingVar.map(isClosing => `g-Modal ${className ?? ''} ${isClosing ? 'g-Modal--Fade' : ''}`), + onclick: onClose, + onmount: (element: Element) => { + document.addEventListener('keydown', onKeyDown) + if (onmount) onmount(element) + }, + onunmount: (element: Element) => { + document.removeEventListener('keydown', onKeyDown) + if (onunmount) onunmount(element) + } + }, + h('div', + { className: 'g-Modal__Content', + onclick: (e: Event) => e.stopPropagation() + }, + h('div', + { className: 'g-Modal__Header' }, + header, + h('button', + { className: 'g-Modal__Close', + onclick: onClose + }, + '✕' + ) + ), + h('div', + { className: 'g-Modal__Body' }, + body + ) + ) + ) + }) +} + +interface ErrorParams { + header: string + message: string + onClose: () => void +} + +export function error({ header, message, onClose }: ErrorParams): Html { + return view({ + header, + body: h('div', { className: 'g-Modal__ContentError' }, message), + onClose, + className: 'g-Modal--Danger' + }) +} |