@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 +1 -1
- package/client/index.d.ts +2 -0
- package/client/index.js +164 -3
- package/package.json +1 -5
- package/client/boot.d.ts +0 -2
- package/client/boot.js +0 -169
package/README.md
CHANGED
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(
|
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
|
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
|
+
"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
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
|
-
});
|