eleva 1.2.5-alpha → 1.2.7-alpha

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/src/core/Eleva.js CHANGED
@@ -62,6 +62,8 @@ export class Eleva {
62
62
  this._isMounted = false;
63
63
  /** @private {Emitter} Instance of the event emitter for handling component events */
64
64
  this.emitter = new Emitter();
65
+ /** @private {Signal} Instance of the signal for handling plugin and component signals */
66
+ this.signal = Signal;
65
67
  /** @private {Renderer} Instance of the renderer for handling DOM updates and patching */
66
68
  this.renderer = new Renderer();
67
69
  }
@@ -133,7 +135,7 @@ export class Eleva {
133
135
  const context = {
134
136
  props,
135
137
  emitter: this.emitter,
136
- signal: (v) => new Signal(v),
138
+ signal: (v) => new this.signal(v),
137
139
  ...this._prepareLifecycleHooks(),
138
140
  };
139
141
 
@@ -22,15 +22,10 @@ export class Renderer {
22
22
  throw new Error("newHtml must be a string");
23
23
  }
24
24
 
25
- const tempContainer = document.createElement("div");
26
- try {
27
- tempContainer.innerHTML = newHtml;
28
- this.diff(container, tempContainer);
29
- } catch (error) {
30
- throw new Error(`Failed to patch DOM: ${error.message}`);
31
- } finally {
32
- tempContainer.innerHTML = "";
33
- }
25
+ const temp = document.createElement("div");
26
+ temp.innerHTML = newHtml;
27
+ this.diff(container, temp);
28
+ temp.innerHTML = "";
34
29
  }
35
30
 
36
31
  /**
@@ -48,76 +43,61 @@ export class Renderer {
48
43
  throw new Error("Both parents must be HTMLElements");
49
44
  }
50
45
 
51
- // Fast path for identical nodes
52
46
  if (oldParent.isEqualNode(newParent)) return;
53
47
 
54
- const oldNodes = Array.from(oldParent.childNodes);
55
- const newNodes = Array.from(newParent.childNodes);
56
- const max = Math.max(oldNodes.length, newNodes.length);
57
-
58
- // Batch DOM operations for better performance
48
+ const oldC = oldParent.childNodes;
49
+ const newC = newParent.childNodes;
50
+ const len = Math.max(oldC.length, newC.length);
59
51
  const operations = [];
60
52
 
61
- for (let i = 0; i < max; i++) {
62
- const oldNode = oldNodes[i];
63
- const newNode = newNodes[i];
53
+ for (let i = 0; i < len; i++) {
54
+ const oldNode = oldC[i];
55
+ const newNode = newC[i];
64
56
 
65
- // Case 1: Append new nodes that don't exist in the old tree.
66
57
  if (!oldNode && newNode) {
67
58
  operations.push(() => oldParent.appendChild(newNode.cloneNode(true)));
68
59
  continue;
69
60
  }
70
- // Case 2: Remove old nodes not present in the new tree.
71
61
  if (oldNode && !newNode) {
72
62
  operations.push(() => oldParent.removeChild(oldNode));
73
63
  continue;
74
64
  }
75
65
 
76
- // Case 3: For element nodes, compare keys if available.
77
- if (
78
- oldNode?.nodeType === Node.ELEMENT_NODE &&
79
- newNode?.nodeType === Node.ELEMENT_NODE
80
- ) {
81
- const oldKey = oldNode.getAttribute("key");
82
- const newKey = newNode.getAttribute("key");
83
- if (oldKey || newKey) {
84
- if (oldKey !== newKey) {
85
- operations.push(() =>
86
- oldParent.replaceChild(newNode.cloneNode(true), oldNode)
87
- );
88
- continue;
89
- }
90
- }
91
- }
66
+ const isSameType =
67
+ oldNode.nodeType === newNode.nodeType &&
68
+ oldNode.nodeName === newNode.nodeName;
92
69
 
93
- // Case 4: Replace nodes if types or tag names differ.
94
- if (
95
- oldNode?.nodeType !== newNode?.nodeType ||
96
- oldNode?.nodeName !== newNode?.nodeName
97
- ) {
70
+ if (!isSameType) {
98
71
  operations.push(() =>
99
72
  oldParent.replaceChild(newNode.cloneNode(true), oldNode)
100
73
  );
101
74
  continue;
102
75
  }
103
76
 
104
- // Case 5: For text nodes, update content if different.
105
- if (oldNode?.nodeType === Node.TEXT_NODE) {
106
- if (oldNode.nodeValue !== newNode.nodeValue) {
107
- oldNode.nodeValue = newNode.nodeValue;
77
+ if (oldNode.nodeType === Node.ELEMENT_NODE) {
78
+ const oldKey = oldNode.getAttribute("key");
79
+ const newKey = newNode.getAttribute("key");
80
+
81
+ if (oldKey !== newKey && (oldKey || newKey)) {
82
+ operations.push(() =>
83
+ oldParent.replaceChild(newNode.cloneNode(true), oldNode)
84
+ );
85
+ continue;
108
86
  }
109
- continue;
110
- }
111
87
 
112
- // Case 6: For element nodes, update attributes and then diff children.
113
- if (oldNode?.nodeType === Node.ELEMENT_NODE) {
114
88
  this.updateAttributes(oldNode, newNode);
115
89
  this.diff(oldNode, newNode);
90
+ } else if (
91
+ oldNode.nodeType === Node.TEXT_NODE &&
92
+ oldNode.nodeValue !== newNode.nodeValue
93
+ ) {
94
+ oldNode.nodeValue = newNode.nodeValue;
116
95
  }
117
96
  }
118
97
 
119
- // Execute batched operations
120
- operations.forEach((op) => op());
98
+ if (operations.length) {
99
+ operations.forEach((op) => op());
100
+ }
121
101
  }
122
102
 
123
103
  /**
@@ -132,67 +112,60 @@ export class Renderer {
132
112
  throw new Error("Both elements must be HTMLElements");
133
113
  }
134
114
 
135
- // Special cases for properties that don't map directly to attributes
136
- const specialProperties = {
137
- value: true,
138
- checked: true,
139
- selected: true,
140
- disabled: true,
141
- readOnly: true,
142
- multiple: true,
143
- };
144
-
145
- // Batch attribute operations for better performance
115
+ const oldAttrs = oldEl.attributes;
116
+ const newAttrs = newEl.attributes;
146
117
  const operations = [];
147
118
 
148
- // Remove old attributes that no longer exist
149
- Array.from(oldEl.attributes).forEach((attr) => {
150
- if (attr.name.startsWith("@")) return;
151
- if (!newEl.hasAttribute(attr.name)) {
152
- operations.push(() => oldEl.removeAttribute(attr.name));
119
+ // Remove old attributes
120
+ for (const { name } of oldAttrs) {
121
+ if (!name.startsWith("@") && !newEl.hasAttribute(name)) {
122
+ operations.push(() => oldEl.removeAttribute(name));
153
123
  }
154
- });
155
-
156
- // Add or update attributes from newEl
157
- Array.from(newEl.attributes).forEach((attr) => {
158
- if (attr.name.startsWith("@")) return;
159
- if (oldEl.getAttribute(attr.name) !== attr.value) {
160
- operations.push(() => {
161
- oldEl.setAttribute(attr.name, attr.value);
162
-
163
- // Convert kebab-case to camelCase for property names
164
- const propName = attr.name.replace(/-([a-z])/g, (_, letter) =>
165
- letter.toUpperCase()
166
- );
124
+ }
167
125
 
168
- // Handle special cases first
169
- if (specialProperties[propName]) {
170
- oldEl[propName] = attr.value === "" ? true : attr.value;
171
- }
172
- // Handle ARIA attributes
173
- else if (attr.name.startsWith("aria-")) {
174
- const ariaName =
175
- "aria" +
176
- attr.name
177
- .slice(5)
178
- .replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
179
- oldEl[ariaName] = attr.value;
180
- }
181
- // Handle data attributes
182
- else if (attr.name.startsWith("data-")) {
183
- // dataset handles the camelCase conversion automatically
184
- const dataName = attr.name.slice(5);
185
- oldEl.dataset[dataName] = attr.value;
186
- }
187
- // Handle standard properties
188
- else if (propName in oldEl) {
189
- oldEl[propName] = attr.value;
126
+ // Update/add new attributes
127
+ for (const attr of newAttrs) {
128
+ const { name, value } = attr;
129
+ if (name.startsWith("@")) continue;
130
+
131
+ if (oldEl.getAttribute(name) === value) continue;
132
+
133
+ operations.push(() => {
134
+ oldEl.setAttribute(name, value);
135
+
136
+ if (name.startsWith("aria-")) {
137
+ const prop =
138
+ "aria" +
139
+ name.slice(5).replace(/-([a-z])/g, (_, l) => l.toUpperCase());
140
+ oldEl[prop] = value;
141
+ } else if (name.startsWith("data-")) {
142
+ oldEl.dataset[name.slice(5)] = value;
143
+ } else {
144
+ const prop = name.replace(/-([a-z])/g, (_, l) => l.toUpperCase());
145
+ if (prop in oldEl) {
146
+ const descriptor = Object.getOwnPropertyDescriptor(
147
+ Object.getPrototypeOf(oldEl),
148
+ prop
149
+ );
150
+ const isBoolean =
151
+ typeof oldEl[prop] === "boolean" ||
152
+ (descriptor?.get &&
153
+ typeof descriptor.get.call(oldEl) === "boolean");
154
+
155
+ if (isBoolean) {
156
+ oldEl[prop] =
157
+ value !== "false" &&
158
+ (value === "" || value === prop || value === "true");
159
+ } else {
160
+ oldEl[prop] = value;
161
+ }
190
162
  }
191
- });
192
- }
193
- });
163
+ }
164
+ });
165
+ }
194
166
 
195
- // Execute batched operations
196
- operations.forEach((op) => op());
167
+ if (operations.length) {
168
+ operations.forEach((op) => op());
169
+ }
197
170
  }
198
171
  }
@@ -54,6 +54,8 @@ export class Eleva {
54
54
  private _isMounted;
55
55
  /** @private {Emitter} Instance of the event emitter for handling component events */
56
56
  private emitter;
57
+ /** @private {Signal} Instance of the signal for handling plugin and component signals */
58
+ private signal;
57
59
  /** @private {Renderer} Instance of the renderer for handling DOM updates and patching */
58
60
  private renderer;
59
61
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Eleva.d.ts","sourceRoot":"","sources":["../../src/core/Eleva.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;GAIG;AACH;IACE;;;;;OAKG;IACH,kBAHW,MAAM;;OA0BhB;IAtBC,wEAAwE;IACxE,MADW,MAAM,CACD;IAChB,uFAAuF;IACvF;;MAAoB;IACpB,0GAA0G;IAC1G;;MAAqB;IACrB,wEAAwE;IACxE,iBAAkB;IAClB,mFAAmF;IACnF,wBAMC;IACD,2EAA2E;IAC3E,mBAAuB;IACvB,qFAAqF;IACrF,gBAA4B;IAC5B,yFAAyF;IACzF,iBAA8B;IAGhC;;;;;;OAMG;IACH,YAJW,MAAM;;QAEJ,KAAK,CAQjB;IAED;;;;;;OAMG;IACH,gBAJW,MAAM,cACN,mBAAmB,GACjB,KAAK,CAKjB;IAED;;;;;;;;OAQG;IACH,iBANW,WAAW,YACX,MAAM,GAAC,mBAAmB;;QAExB,MAAM,GAAC,OAAO,CAAC,MAAM,CAAC,CAgHlC;IAED;;;;;OAKG;IACH,+BAKC;IAED;;;;;;;;OAQG;IACH,uBAiBC;IAED;;;;;;;;OAQG;IACH,sBAYC;IAED;;;;;;;OAOG;IACH,uBAgBC;CACF;;;;;;;;;;;;UAtS4C,CAAC;YAAO,MAAM,GAAE,GAAG;KAAC,GAAC,OAAO,CAAC;YAAO,MAAM,GAAE,GAAG;KAAC,CAAC,CAAC;;;;;;cAKjF,CAAS,IAAmB,EAAnB;YAAO,MAAM,GAAE,GAAG;KAAC,KAAG,MAAM;;;;;;;;UAKN,MAAM"}
1
+ {"version":3,"file":"Eleva.d.ts","sourceRoot":"","sources":["../../src/core/Eleva.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;GAIG;AACH;IACE;;;;;OAKG;IACH,kBAHW,MAAM;;OA4BhB;IAxBC,wEAAwE;IACxE,MADW,MAAM,CACD;IAChB,uFAAuF;IACvF;;MAAoB;IACpB,0GAA0G;IAC1G;;MAAqB;IACrB,wEAAwE;IACxE,iBAAkB;IAClB,mFAAmF;IACnF,wBAMC;IACD,2EAA2E;IAC3E,mBAAuB;IACvB,qFAAqF;IACrF,gBAA4B;IAC5B,yFAAyF;IACzF,eAAoB;IACpB,yFAAyF;IACzF,iBAA8B;IAGhC;;;;;;OAMG;IACH,YAJW,MAAM;;QAEJ,KAAK,CAQjB;IAED;;;;;;OAMG;IACH,gBAJW,MAAM,cACN,mBAAmB,GACjB,KAAK,CAKjB;IAED;;;;;;;;OAQG;IACH,iBANW,WAAW,YACX,MAAM,GAAC,mBAAmB;;QAExB,MAAM,GAAC,OAAO,CAAC,MAAM,CAAC,CAgHlC;IAED;;;;;OAKG;IACH,+BAKC;IAED;;;;;;;;OAQG;IACH,uBAiBC;IAED;;;;;;;;OAQG;IACH,sBAYC;IAED;;;;;;;OAOG;IACH,uBAgBC;CACF;;;;;;;;;;;;UAxS4C,CAAC;YAAO,MAAM,GAAE,GAAG;KAAC,GAAC,OAAO,CAAC;YAAO,MAAM,GAAE,GAAG;KAAC,CAAC,CAAC;;;;;;cAKjF,CAAS,IAAmB,EAAnB;YAAO,MAAM,GAAE,GAAG;KAAC,KAAG,MAAM;;;;;;;;UAKN,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"Renderer.d.ts","sourceRoot":"","sources":["../../src/modules/Renderer.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH;IACE;;;;;;OAMG;IACH,oBAJW,WAAW,WACX,MAAM,QAoBhB;IAED;;;;;;OAMG;IACH,gBAJW,WAAW,aACX,WAAW,QAiFrB;IAED;;;;;;OAMG;IACH,wBAJW,WAAW,SACX,WAAW,QAsErB;CACF"}
1
+ {"version":3,"file":"Renderer.d.ts","sourceRoot":"","sources":["../../src/modules/Renderer.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH;IACE;;;;;;OAMG;IACH,oBAJW,WAAW,WACX,MAAM,QAehB;IAED;;;;;;OAMG;IACH,gBAJW,WAAW,aACX,WAAW,QAkErB;IAED;;;;;;OAMG;IACH,wBAJW,WAAW,SACX,WAAW,QA+DrB;CACF"}