@radishland/runtime 0.1.1 → 0.2.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/client/boot.js CHANGED
@@ -2,147 +2,170 @@
2
2
  var spaces_sep_by_comma = /\s*,\s*/;
3
3
  var bindingConfig = {
4
4
  "checked": {
5
- element: ["input"],
6
5
  type: ["boolean"],
7
6
  event: "change"
8
7
  },
9
8
  "value": {
10
- element: ["input", "select", "textarea"],
11
9
  type: ["string", "number"],
12
10
  event: "input"
13
11
  }
14
12
  };
15
13
 
16
14
  // src/boot.ts
17
- var bindingsQueryString = Object.keys(bindingConfig).map(
18
- (property) => `[\\@bind\\:${property}]`
19
- ).join(",");
20
- setTimeout(() => {
21
- customElements?.whenDefined("handler-registry").then(() => {
22
- document.querySelectorAll(
23
- `[\\@on],[\\@use],[\\@attr],[\\@attr\\|client],[\\@prop],${bindingsQueryString},[\\@text],[\\@html]`
24
- ).forEach(
25
- (entry) => {
26
- const events = entry.getAttribute("@on")?.trim()?.split(spaces_sep_by_comma);
27
- if (events) {
28
- for (const event of events) {
29
- const [type, handler] = event.split(":");
30
- const onRequest = new CustomEvent("@on-request", {
31
- bubbles: true,
32
- cancelable: true,
33
- composed: true,
34
- detail: {
35
- type,
36
- handler: handler || type
37
- }
38
- });
39
- entry.dispatchEvent(onRequest);
40
- }
41
- }
42
- const hooks = entry.getAttribute("@use")?.trim()?.split(spaces_sep_by_comma);
43
- if (hooks) {
44
- for (const hook of hooks) {
45
- const useRequest = new CustomEvent("@use-request", {
46
- bubbles: true,
47
- cancelable: true,
48
- composed: true,
49
- detail: {
50
- hook
51
- }
52
- });
53
- entry.dispatchEvent(useRequest);
54
- }
15
+ var hydrateElement = (element) => {
16
+ const attributes = ["@attr", "@attr|client"].map((item) => element?.getAttribute(item)).filter((attr) => attr !== null && attr !== void 0).flatMap((attr) => attr.trim().split(spaces_sep_by_comma));
17
+ for (const attribute of attributes) {
18
+ const [key, value] = attribute.split(":");
19
+ const attrRequest = new CustomEvent("@attr-request", {
20
+ bubbles: true,
21
+ cancelable: true,
22
+ composed: true,
23
+ detail: {
24
+ attribute: key,
25
+ identifier: value || key,
26
+ target: element
27
+ }
28
+ });
29
+ element.dispatchEvent(attrRequest);
30
+ }
31
+ for (const property of Object.keys(bindingConfig)) {
32
+ if (element.hasAttribute(`@bind:${property}`)) {
33
+ const identifier = element.getAttribute(`@bind:${property}`)?.trim() || property;
34
+ const bindRequest = new CustomEvent("@bind-request", {
35
+ bubbles: true,
36
+ cancelable: true,
37
+ composed: true,
38
+ detail: {
39
+ property,
40
+ identifier,
41
+ target: element
55
42
  }
56
- const props = entry.getAttribute("@prop")?.trim().split(spaces_sep_by_comma);
57
- if (props) {
58
- for (const prop of props) {
59
- const [key, value] = prop.split(":");
60
- const propRequest = new CustomEvent("@prop-request", {
61
- bubbles: true,
62
- cancelable: true,
63
- composed: true,
64
- detail: {
65
- property: key,
66
- identifier: value || key
67
- }
68
- });
69
- entry.dispatchEvent(propRequest);
43
+ });
44
+ element.dispatchEvent(bindRequest);
45
+ }
46
+ }
47
+ const booleanAttributes = element.getAttribute("@bool")?.trim().split(spaces_sep_by_comma);
48
+ if (booleanAttributes) {
49
+ for (const bool of booleanAttributes) {
50
+ const [key, value] = bool.split(":");
51
+ element.dispatchEvent(
52
+ new CustomEvent("@bool-request", {
53
+ bubbles: true,
54
+ cancelable: true,
55
+ composed: true,
56
+ detail: {
57
+ attribute: key,
58
+ identifier: value || key,
59
+ target: element
70
60
  }
61
+ })
62
+ );
63
+ }
64
+ }
65
+ const classList = element.getAttribute("@class");
66
+ if (classList) {
67
+ const classRequest = new CustomEvent("@class-request", {
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("@html-request", {
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 = element.getAttribute("@on")?.trim().split(spaces_sep_by_comma);
92
+ if (events) {
93
+ for (const event of events) {
94
+ const [type, handler] = event.split(":");
95
+ const onRequest = new CustomEvent("@on-request", {
96
+ bubbles: true,
97
+ cancelable: true,
98
+ composed: true,
99
+ detail: {
100
+ type,
101
+ identifier: handler || type,
102
+ target: element
71
103
  }
72
- const text = entry.hasAttribute("@text");
73
- if (text) {
74
- const identifier = entry.getAttribute("@text") || "text";
75
- const textRequest = new CustomEvent("@text-request", {
76
- bubbles: true,
77
- cancelable: true,
78
- composed: true,
79
- detail: {
80
- identifier
81
- }
82
- });
83
- entry.dispatchEvent(textRequest);
84
- }
85
- const html = entry.hasAttribute("@html");
86
- if (html) {
87
- const identifier = entry.getAttribute("@html") || "html";
88
- const htmlRequest = new CustomEvent("@html-request", {
89
- bubbles: true,
90
- cancelable: true,
91
- composed: true,
92
- detail: {
93
- identifier
94
- }
95
- });
96
- entry.dispatchEvent(htmlRequest);
97
- }
98
- const classList = entry.hasAttribute("@class");
99
- if (classList) {
100
- const identifier = entry.getAttribute("@class") || "class";
101
- const classRequest = new CustomEvent("@class-request", {
102
- bubbles: true,
103
- cancelable: true,
104
- composed: true,
105
- detail: {
106
- identifier
107
- }
108
- });
109
- entry.dispatchEvent(classRequest);
110
- }
111
- const attributes = [
112
- ...entry.getAttribute("@attr")?.trim().split(spaces_sep_by_comma) ?? [],
113
- ...entry.getAttribute("@attr|client")?.trim().split(spaces_sep_by_comma) ?? []
114
- ];
115
- if (attributes.length > 0) {
116
- for (const attribute of attributes) {
117
- const [key, value] = attribute.split(":");
118
- const attrRequest = new CustomEvent("@attr-request", {
119
- bubbles: true,
120
- cancelable: true,
121
- composed: true,
122
- detail: {
123
- attribute: key,
124
- identifier: value || key
125
- }
126
- });
127
- entry.dispatchEvent(attrRequest);
128
- }
104
+ });
105
+ element.dispatchEvent(onRequest);
106
+ }
107
+ }
108
+ const props = element.getAttribute("@prop")?.trim().split(spaces_sep_by_comma);
109
+ if (props) {
110
+ for (const prop of props) {
111
+ const [key, value] = prop.split(":");
112
+ const propRequest = new CustomEvent("@prop-request", {
113
+ bubbles: true,
114
+ cancelable: true,
115
+ composed: true,
116
+ detail: {
117
+ property: key,
118
+ identifier: value || key,
119
+ target: element
129
120
  }
130
- for (const property of Object.keys(bindingConfig)) {
131
- if (entry.hasAttribute(`@bind:${property}`)) {
132
- const identifier = entry.getAttribute(`@bind:${property}`)?.trim() || property;
133
- const bindRequest = new CustomEvent("@bind-request", {
134
- bubbles: true,
135
- cancelable: true,
136
- composed: true,
137
- detail: {
138
- property,
139
- identifier
140
- }
141
- });
142
- entry.dispatchEvent(bindRequest);
143
- }
121
+ });
122
+ element.dispatchEvent(propRequest);
123
+ }
124
+ }
125
+ const text = element.getAttribute("@text");
126
+ if (text) {
127
+ const textRequest = new CustomEvent("@text-request", {
128
+ bubbles: true,
129
+ cancelable: true,
130
+ composed: true,
131
+ detail: {
132
+ identifier: text,
133
+ target: element
134
+ }
135
+ });
136
+ element.dispatchEvent(textRequest);
137
+ }
138
+ const hooks = element.getAttribute("@use")?.trim().split(spaces_sep_by_comma);
139
+ if (hooks) {
140
+ for (const hook of hooks) {
141
+ const useRequest = new CustomEvent("@use-request", {
142
+ bubbles: true,
143
+ cancelable: true,
144
+ composed: true,
145
+ detail: {
146
+ identifier: hook,
147
+ target: element
144
148
  }
149
+ });
150
+ element.dispatchEvent(useRequest);
151
+ }
152
+ }
153
+ };
154
+ var hydrate = (root) => {
155
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
156
+ let node = walker.currentNode;
157
+ do {
158
+ if (node instanceof Element) {
159
+ console.log(node.tagName);
160
+ hydrateElement(node);
161
+ if (node.shadowRoot) {
162
+ console.log("entering shadow root");
163
+ hydrate(node.shadowRoot);
164
+ console.log("exiting shadow root");
145
165
  }
146
- );
147
- });
148
- }, 100);
166
+ }
167
+ } while (node = walker.nextNode());
168
+ };
169
+ customElements?.whenDefined("handler-registry").then(() => {
170
+ hydrate(document.body);
171
+ });
package/client/index.js CHANGED
@@ -79,12 +79,10 @@ var booleanAttributes = [
79
79
  ];
80
80
  var bindingConfig = {
81
81
  "checked": {
82
- element: ["input"],
83
82
  type: ["boolean"],
84
83
  event: "change"
85
84
  },
86
85
  "value": {
87
- element: ["input", "select", "textarea"],
88
86
  type: ["string", "number"],
89
87
  event: "input"
90
88
  }
@@ -139,10 +137,9 @@ var HandlerRegistry = class extends HTMLElement {
139
137
  *
140
138
  * The `abort` method of the controller is called in the `disconnectedCallback` method. It allows to cleanup event handlers and other abortable operations
141
139
  */
142
- abortController;
140
+ abortController = new AbortController();
143
141
  constructor() {
144
142
  super();
145
- this.abortController = new AbortController();
146
143
  }
147
144
  /**
148
145
  * Creates an effect that is automatically cleaned up when the component is disconnected
@@ -150,7 +147,7 @@ var HandlerRegistry = class extends HTMLElement {
150
147
  * An optional AbortSignal can be provided to abort the effect prematurely
151
148
  */
152
149
  effect(callback) {
153
- effect(callback, { signal: this.abortController.signal });
150
+ effect(callback, this.abortController);
154
151
  }
155
152
  /**
156
153
  * Looks up an identifier on the instance
@@ -158,19 +155,46 @@ var HandlerRegistry = class extends HTMLElement {
158
155
  lookup(identifier) {
159
156
  return this[identifier];
160
157
  }
158
+ #handleAttr(e) {
159
+ if (e instanceof CustomEvent) {
160
+ const { identifier, attribute, target } = e.detail;
161
+ if (identifier in this && target instanceof HTMLElement && attribute in target) {
162
+ const ref = this.lookup(identifier);
163
+ this.effect(() => {
164
+ if (booleanAttributes.includes(attribute)) {
165
+ target.toggleAttribute(attribute, ref.valueOf());
166
+ } else {
167
+ target.setAttribute(attribute, `${ref}`);
168
+ }
169
+ });
170
+ e.stopPropagation();
171
+ }
172
+ }
173
+ }
174
+ #handleBool(e) {
175
+ if (e instanceof CustomEvent) {
176
+ const { identifier, attribute, target } = e.detail;
177
+ if (identifier in this && target instanceof HTMLElement) {
178
+ const ref = this.lookup(identifier);
179
+ this.effect(() => {
180
+ target.toggleAttribute(attribute, ref.valueOf());
181
+ });
182
+ e.stopPropagation();
183
+ }
184
+ }
185
+ }
161
186
  #handleOn(e) {
162
187
  if (e instanceof CustomEvent) {
163
- const { handler, type: type2 } = e.detail;
164
- if (handler in this && typeof this.lookup(handler) === "function") {
165
- e.target?.addEventListener(type2, this.lookup(handler).bind(this));
188
+ const { identifier, type: type2, target } = e.detail;
189
+ if (identifier in this && typeof this.lookup(identifier) === "function") {
190
+ target.addEventListener(type2, this.lookup(identifier).bind(this));
166
191
  e.stopPropagation();
167
192
  }
168
193
  }
169
194
  }
170
195
  #handleClass(e) {
171
- const target = e.target;
172
- if (e instanceof CustomEvent && target) {
173
- const { identifier } = e.detail;
196
+ if (e instanceof CustomEvent) {
197
+ const { identifier, target } = e.detail;
174
198
  if (identifier in this) {
175
199
  this.effect(() => {
176
200
  const classList = this.lookup(identifier)?.valueOf();
@@ -192,9 +216,9 @@ var HandlerRegistry = class extends HTMLElement {
192
216
  }
193
217
  #handleUse(e) {
194
218
  if (e instanceof CustomEvent) {
195
- const { hook } = e.detail;
196
- if (hook in this && typeof this.lookup(hook) === "function") {
197
- const cleanup = this.lookup(hook).bind(this)(e.target);
219
+ const { identifier, target } = e.detail;
220
+ if (identifier in this && typeof this.lookup(identifier) === "function") {
221
+ const cleanup = this.lookup(identifier).bind(this)(target);
198
222
  if (typeof cleanup === "function") {
199
223
  this.#cleanup.push(cleanup);
200
224
  }
@@ -202,28 +226,10 @@ var HandlerRegistry = class extends HTMLElement {
202
226
  }
203
227
  }
204
228
  }
205
- #handleAttr(e) {
206
- if (e instanceof CustomEvent) {
207
- const { identifier, attribute } = e.detail;
208
- const target = e.target;
209
- if (identifier in this && target instanceof HTMLElement && attribute in target) {
210
- const ref = this.lookup(identifier);
211
- this.effect(() => {
212
- if (booleanAttributes.includes(attribute)) {
213
- ref.valueOf() ? target.setAttribute(attribute, "") : target.removeAttribute(attribute);
214
- } else {
215
- target.setAttribute(attribute, `${ref}`);
216
- }
217
- });
218
- e.stopPropagation();
219
- }
220
- }
221
- }
222
229
  #handleProp(e) {
223
230
  if (e instanceof CustomEvent) {
224
- const { identifier, property } = e.detail;
225
- const target = e.target;
226
- if (identifier in this && target && property in target) {
231
+ const { identifier, property, target } = e.detail;
232
+ if (identifier in this && property in target) {
227
233
  const ref = this.lookup(identifier);
228
234
  this.effect(() => {
229
235
  target[property] = ref.valueOf();
@@ -234,8 +240,7 @@ var HandlerRegistry = class extends HTMLElement {
234
240
  }
235
241
  #handleText(e) {
236
242
  if (e instanceof CustomEvent) {
237
- const target = e.target;
238
- const { identifier } = e.detail;
243
+ const { identifier, target } = e.detail;
239
244
  if (identifier in this && target instanceof HTMLElement) {
240
245
  const ref = this.lookup(identifier);
241
246
  this.effect(() => {
@@ -247,8 +252,7 @@ var HandlerRegistry = class extends HTMLElement {
247
252
  }
248
253
  #handleHTML(e) {
249
254
  if (e instanceof CustomEvent) {
250
- const { identifier } = e.detail;
251
- const target = e.target;
255
+ const { identifier, target } = e.detail;
252
256
  if (identifier in this && target instanceof HTMLElement) {
253
257
  const ref = this.lookup(identifier);
254
258
  this.effect(() => {
@@ -260,8 +264,7 @@ var HandlerRegistry = class extends HTMLElement {
260
264
  }
261
265
  #handleBind(e) {
262
266
  if (e instanceof CustomEvent) {
263
- const { identifier, property } = e.detail;
264
- const target = e.target;
267
+ const { identifier, property, target } = e.detail;
265
268
  if (identifier in this && target instanceof HTMLElement && property in target) {
266
269
  const state = this.lookup(identifier);
267
270
  if (isState(state)) {
@@ -278,8 +281,10 @@ var HandlerRegistry = class extends HTMLElement {
278
281
  }
279
282
  }
280
283
  connectedCallback() {
284
+ console.log(`${this.tagName} connected`);
281
285
  const { signal: signal2 } = this.abortController;
282
286
  this.addEventListener("@attr-request", this.#handleAttr, { signal: signal2 });
287
+ this.addEventListener("@bool-request", this.#handleBool, { signal: signal2 });
283
288
  this.addEventListener("@class-request", this.#handleClass, { signal: signal2 });
284
289
  this.addEventListener("@on-request", this.#handleOn, { signal: signal2 });
285
290
  this.addEventListener("@use-request", this.#handleUse, { signal: signal2 });
package/client/utils.d.ts CHANGED
@@ -16,12 +16,10 @@ declare function type(value: unknown): Types;
16
16
  declare const booleanAttributes: string[];
17
17
  declare const bindingConfig: {
18
18
  checked: {
19
- element: string[];
20
19
  type: string[];
21
20
  event: string;
22
21
  };
23
22
  value: {
24
- element: string[];
25
23
  type: string[];
26
24
  event: string;
27
25
  };
package/client/utils.js CHANGED
@@ -109,12 +109,10 @@ var booleanAttributes = [
109
109
  ];
110
110
  var bindingConfig = {
111
111
  "checked": {
112
- element: ["input"],
113
112
  type: ["boolean"],
114
113
  event: "change"
115
114
  },
116
115
  "value": {
117
- element: ["input", "select", "textarea"],
118
116
  type: ["string", "number"],
119
117
  event: "input"
120
118
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radishland/runtime",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "The Radish runtime",
6
6
  "author": "Frédéric Crozatier",