| 1 |
/** |
| 2 |
* Islands infrastructure. |
| 3 |
* |
| 4 |
* An island is a custom element that attaches behavior to server-rendered |
| 5 |
* markup. It never re-renders what the server sent, so a page keeps working |
| 6 |
* with the script blocked and the element only adds what needs a client. |
| 7 |
* |
| 8 |
* The payoff over the `DOMContentLoaded` + `htmx:afterSwap` re-init dance that |
| 9 |
* static/mt.js does by hand: the browser upgrades a custom element whenever it |
| 10 |
* is connected to the document, including markup htmx swapped in, so an island |
| 11 |
* needs no manual re-initialization. |
| 12 |
* |
| 13 |
* Ported from MNW/server/frontend/src/islands/base.ts, deliberately as a copy. |
| 14 |
* The two apps share no workspace and no registry, and 30 lines duplicated is |
| 15 |
* cheaper than the mechanism that would let them share it. See the Client |
| 16 |
* section of the livechat design note. |
| 17 |
* |
| 18 |
* Pure logic goes in a sibling `*.logic.ts` with no DOM imports, so |
| 19 |
* `node --test` exercises it without a DOM; the element file wires that logic |
| 20 |
* over the light-DOM children. |
| 21 |
*/ |
| 22 |
|
| 23 |
/** |
| 24 |
* Base class: runs `init()` exactly once, the first time the element is |
| 25 |
* connected. Moving the element in the document will not re-run it. |
| 26 |
*/ |
| 27 |
export abstract class MtElement extends HTMLElement { |
| 28 |
#initialized = false; |
| 29 |
|
| 30 |
connectedCallback(): void { |
| 31 |
if (this.#initialized) return; |
| 32 |
this.#initialized = true; |
| 33 |
this.init(); |
| 34 |
} |
| 35 |
|
| 36 |
/** Wire behavior over the already-rendered light-DOM children. */ |
| 37 |
protected abstract init(): void; |
| 38 |
} |
| 39 |
|
| 40 |
/** |
| 41 |
* Define a custom element once. Idempotent, so importing the module from more |
| 42 |
* than one entry point does not throw on the second definition. |
| 43 |
*/ |
| 44 |
export function define(name: string, ctor: CustomElementConstructor): void { |
| 45 |
if (!customElements.get(name)) customElements.define(name, ctor); |
| 46 |
} |
| 47 |
|