dubroy-user-input/src/index.ts

93 lines
4.0 KiB
TypeScript

import { Dataspace, Turn, Dataflow } from '@syndicate-lang/core';
import { boot as bootHtml, template, Anchor } from '@syndicate-lang/html';
message type Key(key: string);
message type Mouse(what: string, x: number, y: number);
assertion type Status(status: string);
Dataspace.boot(ds => {
bootHtml(ds);
at ds {
spawn named 'input-driver' {
const container = document.getElementById('container')!;
const facet = Turn.activeFacet;
facet.preventInertCheck();
const sendMouseEvent = (what: string) => (e: PointerEvent) => facet.turn(() => {
const { left, top } = container.getBoundingClientRect();
send message Mouse(what, e.clientX - left, e.clientY - top);
});
container.addEventListener('pointerdown', sendMouseEvent('down'));
container.addEventListener('pointermove', sendMouseEvent('move'));
container.addEventListener('pointerup', sendMouseEvent('up'));
container.addEventListener('keydown', (e) => facet.turn(() => {
send message Key(e.key);
}));
}
const WIDTH = 50;
const HEIGHT = 50;
function dragBehaviour(x: Dataflow.Field<number>, y: Dataflow.Field<number>)
{
const inBounds = (dx: number, dy: number) =>
dx >= 0 && dy >= 0 && dx < WIDTH && dy < HEIGHT;
function awaitClick(status: string) {
assert Status(status);
on message Mouse('down', $dX: number, $dY: number) => {
if (inBounds(dX - x.value, dY - y.value)) {
stop {
react {
assert Status('Intermediate');
const [oX, oY] = [x.value, y.value];
stop on message Mouse('up', _, _) => react {
awaitClick('Clicked!');
}
stop on message Key('Escape') => react {
awaitClick('Cancelled!');
}
stop on message Mouse('move', $nX: number, $nY: number) => react {
assert Status('Dragging');
stop on message Mouse('up', _, _) => react {
awaitClick('Dragged!');
}
stop on message Key('Escape') => react {
[x.value, y.value] = [oX, oY];
awaitClick('Cancelled!');
}
[x.value, y.value] = [oX + nX - dX, oY + nY - dY];
on message Mouse('move', $nX: number, $nY: number) => {
[x.value, y.value] = [oX + nX - dX, oY + nY - dY];
}
}
}
}
}
}
}
react {
awaitClick('Waiting');
}
}
spawn named 'model' {
const ui = new Anchor();
field x: number = 24;
field y: number = 24;
assert ui.html('#container',
template`<div class="square"
style="left: ${x.value}px;
top: ${y.value}px;
width: ${WIDTH}px;
height: ${HEIGHT}px;"></div>`);
dragBehaviour(x, y);
}
spawn named 'status' {
const ui = new Anchor();
during Status($s: string) => assert ui.html('#status', template`${s}`);
}
}
});