You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

84 lines
2.2 KiB
TypeScript

import type { Value } from './types';
import { valueToUI } from './valueToUI';
import { render, hitTest, hitTestTextInput, handleKeyboard } from './ui';
import { evaluate } from './interpreter';
export type App = {
init: Value;
update: Value; // State / Event / State
view: Value; // State / UI
}
export function runApp(app: App, canvas: HTMLCanvasElement) {
let state = app.init;
function rerender() {
if (app.view.kind !== 'closure')
throw new Error('view must be a function');
const callEnv = new Map(app.view.env);
callEnv.set(app.view.params[0], state);
const uiValue = evaluate(app.view.body, callEnv);
const ui = valueToUI(uiValue);
render(ui, canvas);
}
function handleEvent(event: Value) {
if (app.update.kind !== 'closure')
throw new Error('update must be a function');
if (app.update.params.length !== 2)
throw new Error('update must have 2 parameters');
const callEnv = new Map(app.update.env);
callEnv.set(app.update.params[0], state);
callEnv.set(app.update.params[1], event);
const newState = evaluate(app.update.body, callEnv);
state = newState;
rerender();
}
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// Text Inputs
const hitTextInput = hitTestTextInput(x, y);
if (hitTextInput) {
rerender();
return;
}
const eventName = hitTest(x, y);
if (eventName) {
const event: Value = {
kind: 'constructor',
name: eventName,
args: []
}
handleEvent(event);
}
});
window.addEventListener('keydown', (e) => {
const result = handleKeyboard(e.key);
if (result) {
const event: Value = {
kind: 'constructor',
name: result.event,
args: [{ kind: 'string', value: result.value }]
}
handleEvent(event);
e.preventDefault();
}
})
rerender();
}