From 632eef6424d8dc8d40c2906177892697679e7b85 Mon Sep 17 00:00:00 2001 From: Joris Date: Sat, 19 Apr 2025 12:36:38 +0200 Subject: Add ZIG server --- frontend/ts/src/ui/layout.ts | 34 ++++++++++++++++++++++ frontend/ts/src/ui/modal.ts | 67 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 frontend/ts/src/ui/layout.ts create mode 100644 frontend/ts/src/ui/modal.ts (limited to 'frontend/ts/src/ui') diff --git a/frontend/ts/src/ui/layout.ts b/frontend/ts/src/ui/layout.ts new file mode 100644 index 0000000..0c41223 --- /dev/null +++ b/frontend/ts/src/ui/layout.ts @@ -0,0 +1,34 @@ +import { h, Html } from 'lib/rx' +import * as icons from 'lib/icons' +import * as L from 'lib/loadable' + +export function columns(xs: Array): Html { + return h('div', { className: 'g-Columns' }, xs) +} + +export function loading(): Html { + return h('div', + { className: 'g-Loading' }, + icons.spinner() + ) +} + +export function error(message: string): Html { + return h('div', + { className: 'g-Error' }, + message + ) +} + +export function loadable(loadable: L.Loadable, view: (t: T) => Html): Html { + switch (loadable.key) { + case 'INIT': + return undefined + case 'LOADING': + return loading() + case 'LOADED': + return view(loadable.value) + case 'FAILURE': + return error(loadable.error) + } +} 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(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' + }) +} -- cgit v1.2.3