@radishland/runtime 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # Radish runtime
2
2
 
3
- This package contains the runtime of the Radish WebComponents framework
3
+ This package contains the runtime of the Radish WebComponents framework
package/client/index.d.ts CHANGED
@@ -61,6 +61,8 @@ declare class HandlerRegistry extends HTMLElement implements AutonomousCustomEle
61
61
  * Looks up an identifier on the instance
62
62
  */
63
63
  lookup(identifier: string): any;
64
+ hydrateElement(element: Element): void;
65
+ walkDOM(node: Node): void;
64
66
  connectedCallback(): void;
65
67
  disconnectedCallback(): void;
66
68
  }
package/client/index.js CHANGED
@@ -128,7 +128,7 @@ var object = (init) => {
128
128
  };
129
129
 
130
130
  // src/handler-registry.ts
131
- var HandlerRegistry = class extends HTMLElement {
131
+ var HandlerRegistry = class _HandlerRegistry extends HTMLElement {
132
132
  #cleanup = [];
133
133
  /**
134
134
  * References the handler's `AbortController`.
@@ -278,8 +278,166 @@ var HandlerRegistry = class extends HTMLElement {
278
278
  }
279
279
  }
280
280
  }
281
+ hydrateElement(element) {
282
+ console.log(`hydrating element ${element.nodeName}`);
283
+ const attributes = [...element.attributes];
284
+ const attr = attributes.filter((a) => a.localName.startsWith("attr:"));
285
+ for (const attribute of attr) {
286
+ const [_, key] = attribute.localName.split(":");
287
+ if (!key) throw new Error("Missing <key> in attr:<key>");
288
+ const attrRequest = new CustomEvent("rad::attr", {
289
+ bubbles: true,
290
+ cancelable: true,
291
+ composed: true,
292
+ detail: {
293
+ attribute: key,
294
+ identifier: attribute.value || key,
295
+ target: element
296
+ }
297
+ });
298
+ element.dispatchEvent(attrRequest);
299
+ }
300
+ for (const property of Object.keys(bindingConfig)) {
301
+ if (element.hasAttribute(`bind:${property}`)) {
302
+ const identifier = element.getAttribute(`bind:${property}`)?.trim() || property;
303
+ const bindRequest = new CustomEvent("rad::bind", {
304
+ bubbles: true,
305
+ cancelable: true,
306
+ composed: true,
307
+ detail: {
308
+ property,
309
+ identifier,
310
+ target: element
311
+ }
312
+ });
313
+ element.dispatchEvent(bindRequest);
314
+ }
315
+ }
316
+ const bools = attributes.filter((a) => a.localName.startsWith("bool:"));
317
+ for (const bool of bools) {
318
+ const [_, key] = bool.localName.split(":");
319
+ if (!key) throw new Error("Missing <key> in bool:<key>");
320
+ element.dispatchEvent(
321
+ new CustomEvent("rad::bool", {
322
+ bubbles: true,
323
+ cancelable: true,
324
+ composed: true,
325
+ detail: {
326
+ attribute: key,
327
+ identifier: bool.value || key,
328
+ target: element
329
+ }
330
+ })
331
+ );
332
+ }
333
+ const classList = element.getAttribute("classlist");
334
+ if (classList) {
335
+ const classRequest = new CustomEvent("rad::classlist", {
336
+ bubbles: true,
337
+ cancelable: true,
338
+ composed: true,
339
+ detail: {
340
+ identifier: classList,
341
+ target: element
342
+ }
343
+ });
344
+ element.dispatchEvent(classRequest);
345
+ }
346
+ const html = element.getAttribute("html");
347
+ if (html) {
348
+ const htmlRequest = new CustomEvent("rad::html", {
349
+ bubbles: true,
350
+ cancelable: true,
351
+ composed: true,
352
+ detail: {
353
+ identifier: html,
354
+ target: element
355
+ }
356
+ });
357
+ element.dispatchEvent(htmlRequest);
358
+ }
359
+ const events = attributes.filter((a) => a.localName.startsWith("on:"));
360
+ for (const event of events) {
361
+ const [_, type2] = event.localName.split(":");
362
+ if (!type2) throw new Error("Missing <type> in on:<type>");
363
+ const onRequest = new CustomEvent("rad::on", {
364
+ bubbles: true,
365
+ cancelable: true,
366
+ composed: true,
367
+ detail: {
368
+ type: type2,
369
+ identifier: event.value || type2,
370
+ target: element
371
+ }
372
+ });
373
+ element.dispatchEvent(onRequest);
374
+ }
375
+ const props = attributes.filter((a) => a.localName.startsWith("prop:"));
376
+ for (const prop of props) {
377
+ const [_, key] = prop.localName.split(":");
378
+ if (!key) throw new Error("Missing <key> in prop:<key>");
379
+ const propRequest = new CustomEvent("rad::prop", {
380
+ bubbles: true,
381
+ cancelable: true,
382
+ composed: true,
383
+ detail: {
384
+ property: key,
385
+ identifier: prop.value || key,
386
+ target: element
387
+ }
388
+ });
389
+ element.dispatchEvent(propRequest);
390
+ }
391
+ const text = element.getAttribute("text");
392
+ if (text) {
393
+ const textRequest = new CustomEvent("rad::text", {
394
+ bubbles: true,
395
+ cancelable: true,
396
+ composed: true,
397
+ detail: {
398
+ identifier: text,
399
+ target: element
400
+ }
401
+ });
402
+ element.dispatchEvent(textRequest);
403
+ }
404
+ const hooks = attributes.filter((a) => a.localName.startsWith("use:"));
405
+ for (const hook of hooks) {
406
+ const [_, identifier] = hook.localName.split(":");
407
+ if (!identifier) throw new Error("Missing <id> in use:<id>");
408
+ const useRequest = new CustomEvent("rad::use", {
409
+ bubbles: true,
410
+ cancelable: true,
411
+ composed: true,
412
+ detail: {
413
+ identifier,
414
+ target: element
415
+ }
416
+ });
417
+ element.dispatchEvent(useRequest);
418
+ }
419
+ }
420
+ walkDOM(node) {
421
+ if (node instanceof Element) {
422
+ this.hydrateElement(node);
423
+ if (node.shadowRoot && node.shadowRoot.mode === "open") {
424
+ console.log("entering shadow root");
425
+ this.walkDOM(node.shadowRoot);
426
+ console.log("exiting shadow root");
427
+ }
428
+ let child = node.firstElementChild;
429
+ while (child) {
430
+ if (!(child instanceof _HandlerRegistry)) {
431
+ this.walkDOM(child);
432
+ } else {
433
+ console.log(`skipping handler registry ${child.tagName}`);
434
+ }
435
+ child = child.nextElementSibling;
436
+ }
437
+ }
438
+ }
281
439
  connectedCallback() {
282
- console.log(`${this.tagName} connected`);
440
+ console.log(`[connectedCallback]: ${this.tagName}`);
283
441
  const { signal: signal2 } = this.abortController;
284
442
  this.addEventListener("rad::attr", this.#handleAttr, { signal: signal2 });
285
443
  this.addEventListener("rad::bind", this.#handleBind, { signal: signal2 });
@@ -290,6 +448,7 @@ var HandlerRegistry = class extends HTMLElement {
290
448
  this.addEventListener("rad::prop", this.#handleProp, { signal: signal2 });
291
449
  this.addEventListener("rad::text", this.#handleText, { signal: signal2 });
292
450
  this.addEventListener("rad::use", this.#handleUse, { signal: signal2 });
451
+ this.walkDOM(this);
293
452
  }
294
453
  disconnectedCallback() {
295
454
  this.abortController.abort();
@@ -298,6 +457,8 @@ var HandlerRegistry = class extends HTMLElement {
298
457
  }
299
458
  }
300
459
  };
301
- customElements?.define("handler-registry", HandlerRegistry);
460
+ if (window && !customElements.get("handler-registry")) {
461
+ customElements?.define("handler-registry", HandlerRegistry);
462
+ }
302
463
 
303
464
  export { HandlerRegistry, computed, effect, isState, reactive, signal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radishland/runtime",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "The Radish runtime",
6
6
  "author": "Frédéric Crozatier",
@@ -17,10 +17,6 @@
17
17
  "import": "./client/index.js",
18
18
  "types": "./client/index.d.ts"
19
19
  },
20
- "./boot": {
21
- "import": "./client/boot.js",
22
- "types": "./client/boot.d.ts"
23
- },
24
20
  "./utils": {
25
21
  "import": "./client/utils.js",
26
22
  "types": "./client/utils.d.ts"
package/client/boot.d.ts DELETED
@@ -1,2 +0,0 @@
1
-
2
- export { }
package/client/boot.js DELETED
@@ -1,169 +0,0 @@
1
- // src/utils.ts
2
- var bindingConfig = {
3
- "checked": {
4
- type: ["boolean"],
5
- event: "change"
6
- },
7
- "value": {
8
- type: ["string", "number"],
9
- event: "input"
10
- }
11
- };
12
-
13
- // src/boot.ts
14
- var hydrateElement = (element) => {
15
- const attributes = [...element.attributes];
16
- const attr = attributes.filter((a) => a.localName.startsWith("attr:"));
17
- for (const attribute of attr) {
18
- const [_, key] = attribute.localName.split(":");
19
- if (!key) throw new Error("Missing <key> in attr:<key>");
20
- const attrRequest = new CustomEvent("rad::attr", {
21
- bubbles: true,
22
- cancelable: true,
23
- composed: true,
24
- detail: {
25
- attribute: key,
26
- identifier: attribute.value || key,
27
- target: element
28
- }
29
- });
30
- element.dispatchEvent(attrRequest);
31
- }
32
- for (const property of Object.keys(bindingConfig)) {
33
- if (element.hasAttribute(`bind:${property}`)) {
34
- const identifier = element.getAttribute(`bind:${property}`)?.trim() || property;
35
- const bindRequest = new CustomEvent("rad::bind", {
36
- bubbles: true,
37
- cancelable: true,
38
- composed: true,
39
- detail: {
40
- property,
41
- identifier,
42
- target: element
43
- }
44
- });
45
- element.dispatchEvent(bindRequest);
46
- }
47
- }
48
- const bools = attributes.filter((a) => a.localName.startsWith("bool:"));
49
- for (const bool of bools) {
50
- const [_, key] = bool.localName.split(":");
51
- if (!key) throw new Error("Missing <key> in bool:<key>");
52
- element.dispatchEvent(
53
- new CustomEvent("rad::bool", {
54
- bubbles: true,
55
- cancelable: true,
56
- composed: true,
57
- detail: {
58
- attribute: key,
59
- identifier: bool.value || key,
60
- target: element
61
- }
62
- })
63
- );
64
- }
65
- const classList = element.getAttribute("classlist");
66
- if (classList) {
67
- const classRequest = new CustomEvent("rad::classlist", {
68
- bubbles: true,
69
- cancelable: true,
70
- composed: true,
71
- detail: {
72
- identifier: classList,
73
- target: element
74
- }
75
- });
76
- element.dispatchEvent(classRequest);
77
- }
78
- const html = element.getAttribute("html");
79
- if (html) {
80
- const htmlRequest = new CustomEvent("rad::html", {
81
- bubbles: true,
82
- cancelable: true,
83
- composed: true,
84
- detail: {
85
- identifier: html,
86
- target: element
87
- }
88
- });
89
- element.dispatchEvent(htmlRequest);
90
- }
91
- const events = attributes.filter((a) => a.localName.startsWith("on:"));
92
- for (const event of events) {
93
- const [_, type] = event.localName.split(":");
94
- if (!type) throw new Error("Missing <type> in on:<type>");
95
- const onRequest = new CustomEvent("rad::on", {
96
- bubbles: true,
97
- cancelable: true,
98
- composed: true,
99
- detail: {
100
- type,
101
- identifier: event.value || type,
102
- target: element
103
- }
104
- });
105
- element.dispatchEvent(onRequest);
106
- }
107
- const props = attributes.filter((a) => a.localName.startsWith("prop:"));
108
- for (const prop of props) {
109
- const [_, key] = prop.localName.split(":");
110
- if (!key) throw new Error("Missing <key> in prop:<key>");
111
- const propRequest = new CustomEvent("rad::prop", {
112
- bubbles: true,
113
- cancelable: true,
114
- composed: true,
115
- detail: {
116
- property: key,
117
- identifier: prop.value || key,
118
- target: element
119
- }
120
- });
121
- element.dispatchEvent(propRequest);
122
- }
123
- const text = element.getAttribute("text");
124
- if (text) {
125
- const textRequest = new CustomEvent("rad::text", {
126
- bubbles: true,
127
- cancelable: true,
128
- composed: true,
129
- detail: {
130
- identifier: text,
131
- target: element
132
- }
133
- });
134
- element.dispatchEvent(textRequest);
135
- }
136
- const hooks = attributes.filter((a) => a.localName.startsWith("use:"));
137
- for (const hook of hooks) {
138
- const [_, identifier] = hook.localName.split(":");
139
- if (!identifier) throw new Error("Missing <id> in use:<id>");
140
- const useRequest = new CustomEvent("rad::use", {
141
- bubbles: true,
142
- cancelable: true,
143
- composed: true,
144
- detail: {
145
- identifier,
146
- target: element
147
- }
148
- });
149
- element.dispatchEvent(useRequest);
150
- }
151
- };
152
- var hydrate = (root) => {
153
- const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
154
- let node = walker.currentNode;
155
- do {
156
- if (node instanceof Element) {
157
- console.log(node.tagName);
158
- hydrateElement(node);
159
- if (node.shadowRoot) {
160
- console.log("entering shadow root");
161
- hydrate(node.shadowRoot);
162
- console.log("exiting shadow root");
163
- }
164
- }
165
- } while (node = walker.nextNode());
166
- };
167
- customElements?.whenDefined("handler-registry").then(() => {
168
- hydrate(document.body);
169
- });