mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
106 lines
3.7 KiB
TypeScript
106 lines
3.7 KiB
TypeScript
// The web entry of the browser tests (the base bundle): `startPage` with the tests' API modules and React, a core facet
|
|
// serving `Panels` and the counter, and a React app: the panels (`useRegistry`), a draft input (component state an
|
|
// in-place reload keeps), a counter held while shown (`useHold`, `useWatch`, `useRefState`), and a note in page state
|
|
// (`usePageState`) whose component the "remount" button replaces by another function, as a plugin reload does.
|
|
|
|
import * as React from "react";
|
|
import { createElement as h, useState } from "react";
|
|
import * as jsx from "react/jsx-runtime";
|
|
import { createRoot } from "react-dom/client";
|
|
import { BACKGROUND_CONTEXT } from "../../../../src/context/index.ts";
|
|
import * as chord from "../../../../src/index.ts";
|
|
import {
|
|
HostProvider,
|
|
useHold,
|
|
usePageState,
|
|
useRefState,
|
|
useRegistry,
|
|
useWatch,
|
|
} from "../../../../src/react/index.ts";
|
|
import { startPage } from "../../../../src/web/index.ts";
|
|
import * as webApi from "./api.ts";
|
|
|
|
const { defineFacet } = chord;
|
|
|
|
/** What the tests read from the page. */
|
|
const probe = globalThis as unknown as { loads: number; counterState?: () => string | undefined; connection?: string };
|
|
probe.loads = (probe.loads ?? 0) + 1;
|
|
|
|
const core = defineFacet((host) => {
|
|
host.serve(webApi.Panels, { compose: (table) => table.map(({ value }) => value) });
|
|
host.serve(webApi.Counter, (_params, self) => {
|
|
const count = self.writable(0);
|
|
return {
|
|
count: self.derive([count], ([value]) => value),
|
|
increment: async () => {
|
|
count.set(count.value + 1);
|
|
return null;
|
|
},
|
|
};
|
|
});
|
|
});
|
|
|
|
function Counter() {
|
|
const ref = useHold(webApi.Counter, {});
|
|
const count = useWatch(ref?.count);
|
|
const state = useRefState(ref);
|
|
probe.counterState = () => ref?.state.value.kind;
|
|
return h(
|
|
"button",
|
|
{
|
|
type: "button",
|
|
id: "count",
|
|
"data-state": state?.kind,
|
|
onClick: () => void ref?.increment(BACKGROUND_CONTEXT),
|
|
},
|
|
String(count ?? "…"),
|
|
);
|
|
}
|
|
|
|
/** An input of the page state at `key`. */
|
|
function noteInput(key: string) {
|
|
const [note, setNote] = usePageState(key, "");
|
|
return h("input", {
|
|
id: "note",
|
|
"data-key": key,
|
|
value: note,
|
|
onChange: (event: { target: { value: string } }) => setNote(event.target.value),
|
|
});
|
|
}
|
|
|
|
/** The note's component and the components replacing it: each a new function to React, as a reloaded plugin's. */
|
|
const NOTES = [() => noteInput("note"), () => noteInput("note"), () => noteInput("other")];
|
|
|
|
function App() {
|
|
const panels = useRegistry(webApi.Panels) ?? [];
|
|
const [draft, setDraft] = useState("");
|
|
const [shown, setShown] = useState(true);
|
|
const [note, setNote] = useState(0);
|
|
return h(
|
|
"div",
|
|
null,
|
|
h("ul", { id: "panels" }, ...panels.map((panel) => h("li", { key: panel, className: "panel" }, panel))),
|
|
h("input", {
|
|
id: "draft",
|
|
value: draft,
|
|
onChange: (event: { target: { value: string } }) => setDraft(event.target.value),
|
|
}),
|
|
h("button", { type: "button", id: "toggle", onClick: () => setShown((value) => !value) }, "toggle"),
|
|
shown ? h(Counter) : null,
|
|
h("button", { type: "button", id: "remount", onClick: () => setNote((value) => value + 1) }, "remount"),
|
|
h(NOTES[note % NOTES.length]!),
|
|
);
|
|
}
|
|
|
|
/** Starts the page (Chord's package is free of side effects for bundlers: the entry calls this). */
|
|
export function start(): void {
|
|
void startPage({
|
|
connect: () => new WebSocket(`${location.origin.replace(/^http/, "ws")}/chord`),
|
|
modules: { "@test/app/v1": chord, "@test/app/v1/web": webApi, react: React, "react/jsx-runtime": jsx },
|
|
core: [{ name: "@test/core", facet: core }],
|
|
onConnection: (state) => {
|
|
probe.connection = state;
|
|
},
|
|
render: (host) => createRoot(document.getElementById("root")!).render(h(HostProvider, { host }, h(App))),
|
|
});
|
|
}
|