UI infrastructure and simple "Help" dialog (#3)

Sets up some WIP infrastructure for building out the UI, and adds a simple popup
to show the navigation controls for the app.
This commit is contained in:
Danny McGee
2023-10-09 22:19:58 -04:00
committed by GitHub
parent 57d64f08f0
commit c84fd31387
40 changed files with 1608 additions and 590 deletions
+1
View File
@@ -0,0 +1 @@
export * from "./lib/decorators/on";
+33
View File
@@ -0,0 +1,33 @@
import { LitElement } from "lit";
import { customElement, state } from "lit/decorators.js";
import { on } from "./on";
@customElement("test-counter")
class TestCounter extends LitElement {
@state() count = 0;
@on("click")
incrementCount(): void {
this.count += 1;
}
}
declare global {
interface HTMLElementTagNameMap {
"test-counter": TestCounter;
}
}
describe("@on(eventName)", () => {
it("binds a Custom Element method as an event handler", () => {
const counter = document.createElement("test-counter");
document.body.appendChild(counter);
counter.click();
counter.click();
counter.click();
expect(counter.count).toBe(3);
});
});
+51
View File
@@ -0,0 +1,51 @@
import { match } from "@storyteller/utility";
import type { LitElement } from "lit";
/**
* Adds the decorated method as an event listener for the given event name, and
* automatically cleans up the listener when the element is removed from the
* DOM.
*
* The event name can be prefixed with `window:` or `document:` to add the
* listener to one of those targets instead of the custom element itself.
*/
export function on(eventSelector: string) {
const [targetName, eventName] = eventSelector.includes(":")
? eventSelector.split(":")
: [null, eventSelector];
let target = match (targetName, {
"window": () => window as EventTarget,
"document": () => document as EventTarget,
_: () => null,
});
return <T extends LitElement>(proto: T, propName: keyof T, desc: PropertyDescriptor) => {
const handler = desc?.value ?? proto[propName] ?? (() => {});
const connectedCb = proto.connectedCallback;
const disconnectedCb = proto.disconnectedCallback;
Object.defineProperty(proto, "connectedCallback", {
value: function (this: HTMLElement) {
target ??= this;
target.addEventListener(eventName, handler);
connectedCb.call(this);
},
configurable: true,
writable: true,
});
Object.defineProperty(proto, "disconnectedCallback", {
value: function (this: HTMLElement) {
target ??= this;
target.removeEventListener(eventName, handler);
disconnectedCb.call(this);
},
configurable: true,
writable: true,
});
return desc;
}
}