Remove decorators in favor of declarative initialization

This commit is contained in:
Tangent Wantwight 2020-03-24 23:47:04 -04:00
parent a1900a4daa
commit 4212952abc
5 changed files with 72 additions and 56 deletions

View file

@ -1,40 +1,48 @@
import { KeyControl } from "./Keyboard"; import { KeyControl } from "./Keyboard";
/**
* A class decorator for automatically constructing
* class instances around elements on page load.
*/
export function Bind(selector: string) {
return (appletType: AppletConstructor) => {
const elements = document.querySelectorAll(selector);
for(let i = 0; i < elements.length; i++) {
const element = elements[i] as HTMLElement;
new appletType(element);
}
}
};
export interface AppletConstructor {
new(element: HTMLElement): any
};
/**
* A class decorator for automatically constructing
* a KeyControl around a canvas on page load & fetching the render context.
*/
export function Game(selector: string, tabIndex = -1) {
return (gameType: GameConstructor) => {
const elements = document.querySelectorAll(selector);
for(let i = 0; i < elements.length; i++) {
const element = elements[i] as HTMLCanvasElement;
if(!(element instanceof HTMLCanvasElement)) continue;
const cx = element.getContext("2d") as CanvasRenderingContext2D;
const keys = new KeyControl(element, tabIndex);
new gameType(element, cx, keys);
}
}
};
export interface GameConstructor { export interface GameConstructor {
new(canvas: HTMLCanvasElement, cx: CanvasRenderingContext2D, keys: KeyControl): any new(canvas: HTMLCanvasElement, cx: CanvasRenderingContext2D, keys: KeyControl): any
}; };
class Selection {
constructor(private elements: HTMLElement[]) {}
/**
* Run a callback for every selected item.
*/
public forEach(callback: (e: HTMLElement) => {}) {
this.elements.forEach(callback);
}
/**
* Run a callback for selected canvases, with a 2d context & KeyControl established for use.
*/
public forEachCanvas(callback: (canvas: HTMLCanvasElement, cx: CanvasRenderingContext2D, keys: KeyControl) => {}, tabIndex = -1) {
this.elements.forEach(e => {
if(e instanceof HTMLCanvasElement) {
const cx = e.getContext("2d") as CanvasRenderingContext2D;
const keys = new KeyControl(e, tabIndex);
callback(e, cx, keys);
}
});
}
}
/**
* Wrap an HTML element or list of elements to bind them to behavior.
*
* @param selector A CSS selector or a literal Element
*/
export function Select(selector: string): Selection;
export function Select(selector: HTMLElement): Selection;
export function Select(selector: string | HTMLElement): Selection {
if(typeof selector === "string") {
const elementList = document.querySelectorAll(selector);
const elements: HTMLElement[] = Array.prototype.slice.call(elementList);
return new Selection(elements);
} else {
const elements = [selector];
return new Selection(elements);
}
}

View file

@ -1,9 +1,8 @@
import { Bind } from "./Init"; import { Select } from "./Init";
import { KeyControl, KeyHandler, KeyName } from "./Keyboard"; import { KeyControl, KeyHandler, KeyName } from "./Keyboard";
import { Loop } from "./Loop"; import { Loop } from "./Loop";
@Bind("#KeyTest") class Test implements KeyHandler {
export class Test implements KeyHandler {
private keys: KeyControl; private keys: KeyControl;
constructor(public div: HTMLElement) { constructor(public div: HTMLElement) {
@ -29,8 +28,7 @@ export class Test implements KeyHandler {
} }
} }
@Bind("#LoopTest") class LoopTest {
export class LoopTest {
frames: number = 0; frames: number = 0;
constructor(public div: HTMLElement) { constructor(public div: HTMLElement) {
@ -45,3 +43,8 @@ export class LoopTest {
loop.start(); loop.start();
} }
} }
export function BindDemos(): void {
Select("#KeyTest").forEach(e => new Test(e));
Select("#LoopTest").forEach(e => new LoopTest(e));
}

View file

@ -1,4 +1,4 @@
import { Bind, Game } from "../Applet/Init"; import { Select } from "../Applet/Init";
import { KeyControl } from "../Applet/Keyboard"; import { KeyControl } from "../Applet/Keyboard";
import { Loop } from "../Applet/Loop"; import { Loop } from "../Applet/Loop";
import { DrawSet, Layer } from "../Applet/Render"; import { DrawSet, Layer } from "../Applet/Render";
@ -45,8 +45,7 @@ class TestData extends Data {
}; };
} }
@Bind("#EcsJoinTest") class EcsJoinTest {
export class EcsJoinTest {
constructor(pre: HTMLElement) { constructor(pre: HTMLElement) {
const data = new TestData(); const data = new TestData();
pre.innerText = JSON.stringify({ pre.innerText = JSON.stringify({
@ -58,8 +57,7 @@ export class EcsJoinTest {
} }
} }
@Bind("#EcsLookupTest") class EcsLookupTest {
export class EcsLookupTest {
constructor(pre: HTMLElement) { constructor(pre: HTMLElement) {
const data = new TestData(); const data = new TestData();
const applesMaybeCarrots = Join(data, "apple", "id").map(([apple, id]) => ({ const applesMaybeCarrots = Join(data, "apple", "id").map(([apple, id]) => ({
@ -70,8 +68,7 @@ export class EcsLookupTest {
} }
} }
@Bind("#EcsRemoveTest") class EcsRemoveTest {
export class EcsRemoveTest {
constructor(pre: HTMLElement) { constructor(pre: HTMLElement) {
const data = new TestData(); const data = new TestData();
const beforeDelete = Join(data, "apple", "carrot", "id",); const beforeDelete = Join(data, "apple", "carrot", "id",);
@ -84,8 +81,7 @@ export class EcsRemoveTest {
} }
} }
@Bind("#EcsCreateTest") class EcsCreateTest {
export class EcsCreateTest {
constructor(pre: HTMLElement) { constructor(pre: HTMLElement) {
const data = new TestData(); const data = new TestData();
const beforeCreate = Join(data, "apple", "banana", "carrot", "id"); const beforeCreate = Join(data, "apple", "banana", "carrot", "id");
@ -103,8 +99,7 @@ export class EcsCreateTest {
} }
} }
@Game("#RenderTest") class LoopTest {
export class LoopTest {
data = new Data(); data = new Data();
constructor(public canvas: HTMLCanvasElement, cx: CanvasRenderingContext2D, keys: KeyControl) { constructor(public canvas: HTMLCanvasElement, cx: CanvasRenderingContext2D, keys: KeyControl) {
@ -174,3 +169,11 @@ export class LoopTest {
}); });
} }
} }
export function BindTests(): void {
Select("#EcsJoinTest").forEach(e => new EcsJoinTest(e));
Select("#EcsLookupTest").forEach(e => new EcsLookupTest(e));
Select("#EcsRemoveTest").forEach(e => new EcsRemoveTest(e));
Select("#EcsCreateTest").forEach(e => new EcsCreateTest(e));
Select("#RenderTest").forEachCanvas((c, cx, keys) => new LoopTest(c, cx, keys));
}

View file

@ -1,18 +1,15 @@
import subscribe from "callbag-subscribe"; import subscribe from "callbag-subscribe";
import { Game } from "../Applet/Init";
import { KeyControl, KeyName } from "../Applet/Keyboard"; import { KeyControl, KeyName } from "../Applet/Keyboard";
import { DrawSet } from "../Applet/Render"; import { DrawSet } from "../Applet/Render";
import { Location, Polygon, RenderBounds } from "../Ecs/Components"; import { Location, Polygon, RenderBounds } from "../Ecs/Components";
import { Create } from "../Ecs/Data"; import { Create } from "../Ecs/Data";
import {} from "../Ecs/Lockstep";
import { RunRenderBounds } from "../Ecs/Renderers"; import { RunRenderBounds } from "../Ecs/Renderers";
import { LockstepClient } from "../Net/LockstepClient"; import { LockstepClient } from "../Net/LockstepClient";
import { Loopback } from "../Net/LoopbackServer"; import { Loopback } from "../Net/LoopbackServer";
import { Data, Engine } from "./GameComponents"; import { Data, Engine } from "./GameComponents";
import { Buttons } from "./Input"; import { Buttons } from "./Input";
@Game("#GameCanvas")
export class Main extends LockstepClient<KeyName[], Data> { export class Main extends LockstepClient<KeyName[], Data> {
buttons = new Buttons(); buttons = new Buttons();

View file

@ -1,2 +1,7 @@
import "./Game/Main"; import { Select } from "./Applet/Init";
import "./Ecs/test"; import { BindTests } from "./Ecs/test";
import { Main } from "./Game/Main";
Select("#GameCanvas").forEachCanvas((c, cx, keys) => new Main(c, cx, keys));
BindTests();