diff --git a/src/backend/main.tsx b/src/backend/main.tsx index e76d7f4..366965a 100644 --- a/src/backend/main.tsx +++ b/src/backend/main.tsx @@ -35,7 +35,7 @@ export const mainPageHandler: RequestHandler = (req, res) => { type="submit" name={key} value={`${counters[key] - 1}`} - is="button-query-tracker" + is="reactive-button" track={key} delta="-1" > @@ -45,7 +45,7 @@ export const mainPageHandler: RequestHandler = (req, res) => { type="submit" name={key} value={`${counters[key] + 1}`} - is="button-query-tracker" + is="reactive-button" track={key} delta="+1" > @@ -53,7 +53,7 @@ export const mainPageHandler: RequestHandler = (req, res) => { {" "} Value of "{key}":{" "} - + {counters[key]} diff --git a/src/frontend/app.ts b/src/frontend/app.ts index d247df4..83b3e3c 100644 --- a/src/frontend/app.ts +++ b/src/frontend/app.ts @@ -1,13 +1,9 @@ import { computeAllSolutions } from "../shared/solver.ts"; import { rules } from "../shared/tictactoe-rules.ts"; -import { ProgressiveForm } from "./progressive-form.ts"; -import { ButtonTracker, SpanTracker } from "./query-tracker.ts"; - -customElements.define("progressive-form", ProgressiveForm, { extends: "form" }); -customElements.define("span-query-tracker", SpanTracker, { extends: "span" }); -customElements.define("button-query-tracker", ButtonTracker, { extends: "button" }); +import { initializeWebComponents } from "./components/index.ts"; document.addEventListener("DOMContentLoaded", function () { + initializeWebComponents(); const allSolutions = computeAllSolutions(3, 3, rules); console.log(allSolutions.size); console.log(allSolutions); diff --git a/src/frontend/components/index.ts b/src/frontend/components/index.ts new file mode 100644 index 0000000..2d00c7f --- /dev/null +++ b/src/frontend/components/index.ts @@ -0,0 +1,16 @@ +import { ProgressiveForm } from "./progressive-form.ts"; +import { ReactiveButton } from "./reactive-button.ts"; +import { ReactiveSpan } from "./reactive-span.ts"; + +export const initializeWebComponents = () => { + if ((window as Partial).customElements?.define) { + // We need to define customized built-in elements first, + // because WebKit (Safari) is not standard-compliant[1] and doesn't support them, + // so hopefully these calls will throw, leaving us with all custom elements or none, + // instead of defining autonomous custom elements and skipping customized built-in elements. + // [1]: https://github.com/WebKit/standards-positions/issues/97 + customElements.define("progressive-form", ProgressiveForm, { extends: "form" }); + customElements.define("reactive-button", ReactiveButton, { extends: "button" }); + customElements.define("reactive-span", ReactiveSpan, { extends: "span" }); + } +}; diff --git a/src/frontend/progressive-form.ts b/src/frontend/components/progressive-form.ts similarity index 100% rename from src/frontend/progressive-form.ts rename to src/frontend/components/progressive-form.ts diff --git a/src/frontend/components/reactive-button.ts b/src/frontend/components/reactive-button.ts new file mode 100644 index 0000000..da1f59b --- /dev/null +++ b/src/frontend/components/reactive-button.ts @@ -0,0 +1,22 @@ +import { TrackingTools } from "../lib/query-tracking-utils.ts"; + +export class ReactiveButton extends HTMLButtonElement { + private readonly trackingTools = new TrackingTools(this); + + handleTrackedValueUpdate(newValue: string | null) { + const delta = parseInt(this.getAttribute("delta") ?? "0", 10); + this.value = (parseInt(newValue ?? "0", 10) + delta).toString(); + } + + connectedCallback() { + this.trackingTools.connectedCallback(); + } + + attributeChangedCallback() { + this.trackingTools.attributeChangedCallback(); + } + + disconnectedCallback() { + this.trackingTools.disconnectedCallback(); + } +} diff --git a/src/frontend/components/reactive-span.ts b/src/frontend/components/reactive-span.ts new file mode 100644 index 0000000..8f68cb3 --- /dev/null +++ b/src/frontend/components/reactive-span.ts @@ -0,0 +1,21 @@ +import { TrackingTools } from "../lib/query-tracking-utils.ts"; + +export class ReactiveSpan extends HTMLSpanElement { + private readonly trackingTools = new TrackingTools(this); + + handleTrackedValueUpdate(newValue: string | null) { + this.innerText = newValue ?? "[null]"; + } + + connectedCallback() { + this.trackingTools.connectedCallback(); + } + + attributeChangedCallback() { + this.trackingTools.attributeChangedCallback(); + } + + disconnectedCallback() { + this.trackingTools.disconnectedCallback(); + } +} diff --git a/src/frontend/navigation-utils.ts b/src/frontend/lib/navigation-utils.ts similarity index 100% rename from src/frontend/navigation-utils.ts rename to src/frontend/lib/navigation-utils.ts diff --git a/src/frontend/query-tracker.ts b/src/frontend/lib/query-tracking-utils.ts similarity index 59% rename from src/frontend/query-tracker.ts rename to src/frontend/lib/query-tracking-utils.ts index 49750b9..eda9e12 100644 --- a/src/frontend/query-tracker.ts +++ b/src/frontend/lib/query-tracking-utils.ts @@ -16,7 +16,7 @@ const createQueryStringTracker = (key: string, onChange: (newValue: string | nul }; }; -class TrackingTools< +export class TrackingTools< TElement extends HTMLElement & { handleTrackedValueUpdate(this: TElement, newValue: string | null): void }, > { private currentTrackKey: string | null = null; @@ -56,44 +56,3 @@ class TrackingTools< } } } - -export class SpanTracker extends HTMLSpanElement { - private readonly trackingTools = new TrackingTools(this); - - handleTrackedValueUpdate(newValue: string | null) { - this.innerText = newValue ?? "[null]"; - } - - connectedCallback() { - this.trackingTools.connectedCallback(); - } - - attributeChangedCallback() { - this.trackingTools.attributeChangedCallback(); - } - - disconnectedCallback() { - this.trackingTools.disconnectedCallback(); - } -} - -export class ButtonTracker extends HTMLButtonElement { - private readonly trackingTools = new TrackingTools(this); - - handleTrackedValueUpdate(newValue: string | null) { - const delta = parseInt(this.getAttribute("delta") ?? "0", 10); - this.value = (parseInt(newValue ?? "0", 10) + delta).toString(); - } - - connectedCallback() { - this.trackingTools.connectedCallback(); - } - - attributeChangedCallback() { - this.trackingTools.attributeChangedCallback(); - } - - disconnectedCallback() { - this.trackingTools.disconnectedCallback(); - } -}