61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
import { h, Fragment } from 'preact';
|
|
import { useState, useEffect } from 'preact/hooks';
|
|
import { wsClient } from './lib/ws';
|
|
import { controlDialogs, dismissControlDialog, type ControlDialog } from './lib/controldialogs';
|
|
|
|
// Renders dialogs pushed by server-side control-logic action.dialog nodes over
|
|
// the WebSocket. Mounted once globally (App) since these are addressed to the
|
|
// connected user, independent of which panel is open. info/error dialogs are
|
|
// acknowledgements; input dialogs send a numeric response back to the server.
|
|
export default function ControlDialogs() {
|
|
const [reqs, setReqs] = useState<ControlDialog[]>([]);
|
|
useEffect(() => controlDialogs.subscribe(setReqs), []);
|
|
if (reqs.length === 0) return null;
|
|
return <Fragment>{reqs.map(r => <ControlDialogModal key={r.id} req={r} />)}</Fragment>;
|
|
}
|
|
|
|
function ControlDialogModal({ req }: { req: ControlDialog; key?: string }) {
|
|
const isInput = req.kind === 'input';
|
|
const [val, setVal] = useState('');
|
|
|
|
function ok() {
|
|
if (isInput) {
|
|
const n = parseFloat(val);
|
|
wsClient.sendDialogResponse(req.id, Number.isFinite(n) ? n : null);
|
|
}
|
|
dismissControlDialog(req.id);
|
|
}
|
|
function cancel() {
|
|
if (isInput) wsClient.sendDialogResponse(req.id, null);
|
|
dismissControlDialog(req.id);
|
|
}
|
|
|
|
function onKey(e: KeyboardEvent) {
|
|
if (e.key === 'Enter') { e.preventDefault(); ok(); }
|
|
if (e.key === 'Escape') { e.preventDefault(); cancel(); }
|
|
}
|
|
|
|
return (
|
|
<div class="logic-dialog-backdrop" onKeyDown={onKey}>
|
|
<div class={`logic-dialog logic-dialog-${req.kind}`} role="dialog">
|
|
<div class="logic-dialog-header">
|
|
{req.title || (req.kind === 'error' ? 'Error' : isInput ? 'Input required' : 'Info')}
|
|
</div>
|
|
{req.message && <div class="logic-dialog-message">{req.message}</div>}
|
|
{isInput && (
|
|
<div class="logic-dialog-field">
|
|
<input class="prop-input logic-dialog-input" type="number"
|
|
autoFocus value={val}
|
|
onInput={(e) => setVal((e.target as HTMLInputElement).value)}
|
|
onKeyDown={onKey} />
|
|
</div>
|
|
)}
|
|
<div class="logic-dialog-actions">
|
|
{isInput && <button class="panel-btn" onClick={cancel}>Cancel</button>}
|
|
<button class="panel-btn panel-btn-primary" onClick={ok}>OK</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|