eleva 1.2.14-beta → 1.2.15-beta

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/package.json CHANGED
@@ -1,29 +1,104 @@
1
1
  {
2
2
  "name": "eleva",
3
- "version": "1.2.14-beta",
3
+ "version": "1.2.15-beta",
4
4
  "description": "A minimalist and lightweight, pure vanilla JavaScript frontend runtime framework.",
5
5
  "type": "module",
6
- "main": "dist/eleva.cjs.js",
7
- "module": "dist/eleva.esm.js",
8
- "umd": "dist/eleva.umd.js",
9
- "browser": "dist/eleva.min.js",
10
- "unpkg": "dist/eleva.min.js",
11
- "types": "dist/eleva.d.ts",
6
+ "private": false,
7
+ "license": "MIT",
8
+ "author": {
9
+ "name": "Tarek Raafat",
10
+ "email": "tarek.m.raafat@gmail.com",
11
+ "url": "https://www.tarekraafat.com"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/TarekRaafat/eleva.git"
16
+ },
17
+ "homepage": "https://elevajs.com",
18
+ "bugs": {
19
+ "url": "https://github.com/TarekRaafat/eleva/issues",
20
+ "email": "tarek.m.raafat@gmail.com"
21
+ },
22
+ "readme": "https://github.com/TarekRaafat/eleva#readme",
23
+ "main": "./dist/eleva.cjs.js",
24
+ "module": "./dist/eleva.esm.js",
25
+ "browser": "./dist/eleva.umd.min.js",
26
+ "types": "./dist/eleva.d.ts",
27
+ "unpkg": "./dist/eleva.umd.min.js",
28
+ "jsdelivr": "./dist/eleva.umd.min.js",
12
29
  "exports": {
13
30
  ".": {
14
31
  "types": "./dist/eleva.d.ts",
15
- "node": "./dist/eleva.cjs.js",
32
+ "import": "./dist/eleva.esm.js",
16
33
  "require": "./dist/eleva.cjs.js",
34
+ "node": "./dist/eleva.cjs.js",
35
+ "browser": "./dist/eleva.umd.min.js",
36
+ "default": "./dist/eleva.umd.min.js"
37
+ },
38
+ "./esm": {
39
+ "types": "./dist/eleva.d.ts",
17
40
  "import": "./dist/eleva.esm.js",
18
- "browser": "./dist/eleva.min.js",
19
- "umd": "./dist/eleva.umd.js",
20
- "default": "./dist/eleva.esm.js"
21
- }
41
+ "require": "./dist/eleva.esm.js",
42
+ "browser": "./dist/eleva.umd.min.js"
43
+ },
44
+ "./cjs": {
45
+ "types": "./dist/eleva.d.ts",
46
+ "require": "./dist/eleva.cjs.js"
47
+ },
48
+ "./umd": {
49
+ "types": "./dist/eleva.d.ts",
50
+ "import": "./dist/eleva.umd.js",
51
+ "browser": "./dist/eleva.umd.js"
52
+ },
53
+ "./browser": {
54
+ "types": "./dist/eleva.d.ts",
55
+ "import": "./dist/eleva.umd.min.js",
56
+ "browser": "./dist/eleva.umd.min.js"
57
+ },
58
+ "./package.json": "./package.json"
59
+ },
60
+ "files": [
61
+ "dist",
62
+ "types",
63
+ "src"
64
+ ],
65
+ "directories": {
66
+ "doc": "docs",
67
+ "example": "examples",
68
+ "test": "test",
69
+ "dist": "dist",
70
+ "src": "src",
71
+ "types": "types"
72
+ },
73
+ "sideEffects": false,
74
+ "devDependencies": {
75
+ "@babel/core": "^7.26.10",
76
+ "@babel/preset-env": "^7.26.9",
77
+ "@codecov/rollup-plugin": "^1.9.0",
78
+ "@rollup/plugin-babel": "^6.0.3",
79
+ "@rollup/plugin-node-resolve": "^16.0.0",
80
+ "@rollup/plugin-terser": "^0.4.4",
81
+ "babel-jest": "^29.7.0",
82
+ "eslint": "^9.0.0",
83
+ "eslint-plugin-prettier": "^5.2.5",
84
+ "husky": "^9.1.7",
85
+ "jest": "^29.6.1",
86
+ "jest-environment-jsdom": "^29.7.0",
87
+ "jest-html-reporters": "^3.1.7",
88
+ "jest-watch-typeahead": "^2.2.2",
89
+ "jsdoc": "^4.0.4",
90
+ "lint-staged": "^15.5.0",
91
+ "prettier": "^3.5.3",
92
+ "rimraf": "^6.0.1",
93
+ "rollup": "^4.34.8",
94
+ "rollup-plugin-dts": "^6.1.1",
95
+ "typescript": "^5.1.6",
96
+ "vitepress": "^1.6.3"
22
97
  },
23
98
  "scripts": {
24
99
  "dev": "rollup -c -w",
25
100
  "build": "rollup -c",
26
- "build:types": "tsc --emitDeclarationOnly",
101
+ "build:types": "tsc",
27
102
  "build:types:bundle": "rollup -c rollup.dts.config.js",
28
103
  "build:all": "npm run clean && npm run build && npm run build:types && npm run build:types:bundle",
29
104
  "docs:dev": "vitepress dev docs",
@@ -48,7 +123,6 @@
48
123
  "codecov": "npx codecov",
49
124
  "lint": "eslint src/**/*.js",
50
125
  "format": "npx prettier \"src/**/*\" \"types/**/*\" --write",
51
- "size": "size-limit",
52
126
  "clean": "rimraf dist types",
53
127
  "prepublishOnly": "npm run format && npm run lint && npm run test:source:coverage && npm run build:all",
54
128
  "prepare": "npm run build:all"
@@ -56,31 +130,16 @@
56
130
  "engines": {
57
131
  "node": ">=16.0.0"
58
132
  },
59
- "devDependencies": {
60
- "@babel/core": "^7.26.10",
61
- "@babel/preset-env": "^7.26.9",
62
- "@codecov/rollup-plugin": "^1.9.0",
63
- "@rollup/plugin-babel": "^6.0.3",
64
- "@rollup/plugin-commonjs": "^28.0.2",
65
- "@rollup/plugin-node-resolve": "^16.0.0",
66
- "@rollup/plugin-terser": "^0.4.4",
67
- "@size-limit/preset-app": "^11.2.0",
68
- "babel-jest": "^29.7.0",
69
- "eslint": "^9.0.0",
70
- "eslint-plugin-prettier": "^5.2.5",
71
- "husky": "^9.1.7",
72
- "jest": "^29.6.1",
73
- "jest-environment-jsdom": "^29.7.0",
74
- "jest-html-reporters": "^3.1.7",
75
- "jest-watch-typeahead": "^2.2.2",
76
- "jsdoc": "^4.0.4",
77
- "lint-staged": "^15.5.0",
78
- "prettier": "^3.5.3",
79
- "rimraf": "^6.0.1",
80
- "rollup": "^4.34.8",
81
- "rollup-plugin-dts": "^6.1.1",
82
- "typescript": "^5.1.6",
83
- "vitepress": "^1.6.3"
133
+ "browserslist": [
134
+ "> 0.25%",
135
+ "last 2 versions",
136
+ "not dead",
137
+ "not op_mini all",
138
+ "not ie 11"
139
+ ],
140
+ "publishConfig": {
141
+ "access": "public",
142
+ "registry": "https://registry.npmjs.org/"
84
143
  },
85
144
  "lint-staged": {
86
145
  "src/**/*.{js,ts}": [
@@ -140,35 +199,6 @@
140
199
  "web development tools",
141
200
  "micro frontends"
142
201
  ],
143
- "author": {
144
- "name": "Tarek Raafat",
145
- "email": "tarek.m.raafat@gmail.com",
146
- "url": "https://www.tarekraafat.com"
147
- },
148
- "license": "MIT",
149
- "homepage": "https://elevajs.com",
150
- "repository": {
151
- "type": "git",
152
- "url": "git+https://github.com/TarekRaafat/eleva.git"
153
- },
154
- "bugs": {
155
- "url": "https://github.com/TarekRaafat/eleva/issues",
156
- "email": "tarek.m.raafat@gmail.com"
157
- },
158
- "directories": {
159
- "doc": "docs",
160
- "example": "examples",
161
- "test": "test",
162
- "dist": "dist",
163
- "src": "src",
164
- "types": "types"
165
- },
166
- "files": [
167
- "dist",
168
- "types",
169
- "src"
170
- ],
171
- "sideEffects": false,
172
202
  "funding": [
173
203
  {
174
204
  "type": "github",
@@ -178,12 +208,5 @@
178
208
  "type": "buymeacoffee",
179
209
  "url": "https://www.buymeacoffee.com/tarekraafat"
180
210
  }
181
- ],
182
- "browserslist": [
183
- "> 0.25%",
184
- "last 2 versions",
185
- "not dead",
186
- "not op_mini all",
187
- "not ie 11"
188
211
  ]
189
212
  }
package/src/core/Eleva.js CHANGED
@@ -177,7 +177,7 @@ export class Eleva {
177
177
  const context = {
178
178
  props,
179
179
  emitter: this.emitter,
180
- /** @type {(v: any) => Signal} */
180
+ /** @type {(v: any) => Signal<any>} */
181
181
  signal: (v) => new this.signal(v),
182
182
  ...this._prepareLifecycleHooks(),
183
183
  };
@@ -191,7 +191,7 @@ export class Eleva {
191
191
  * 4. Managing component lifecycle
192
192
  *
193
193
  * @param {Object<string, any>} data - Data returned from the component's setup function
194
- * @returns {MountResult} An object containing:
194
+ * @returns {Promise<MountResult>} An object containing:
195
195
  * - container: The mounted component's container element
196
196
  * - data: The component's reactive state and context
197
197
  * - unmount: Function to clean up and unmount the component
@@ -301,14 +301,17 @@ export class Eleva {
301
301
  const attrs = el.attributes;
302
302
  for (let i = 0; i < attrs.length; i++) {
303
303
  const attr = attrs[i];
304
- if (attr.name.startsWith("@")) {
305
- const event = attr.name.slice(1);
306
- const handler = TemplateEngine.evaluate(attr.value, context);
307
- if (typeof handler === "function") {
308
- el.addEventListener(event, handler);
309
- el.removeAttribute(attr.name);
310
- cleanupListeners.push(() => el.removeEventListener(event, handler));
311
- }
304
+
305
+ if (!attr.name.startsWith("@")) continue;
306
+
307
+ const event = attr.name.slice(1);
308
+ const handlerName = attr.value;
309
+ const handler =
310
+ context[handlerName] || TemplateEngine.evaluate(handlerName, context);
311
+ if (typeof handler === "function") {
312
+ el.addEventListener(event, handler);
313
+ el.removeAttribute(attr.name);
314
+ cleanupListeners.push(() => el.removeEventListener(event, handler));
312
315
  }
313
316
  }
314
317
  }
@@ -353,6 +356,7 @@ export class Eleva {
353
356
  * // Returns: { name: "John", age: "25" }
354
357
  */
355
358
  _extractProps(element, prefix) {
359
+ /** @type {Record<string, string>} */
356
360
  const props = {};
357
361
  for (const { name, value } of element.attributes) {
358
362
  if (name.startsWith(prefix)) {
@@ -362,41 +366,6 @@ export class Eleva {
362
366
  return props;
363
367
  }
364
368
 
365
- /**
366
- * Mounts a single component instance to a container element.
367
- * This method handles the actual mounting of a component with its props.
368
- *
369
- * @private
370
- * @param {HTMLElement} container - The container element to mount the component to
371
- * @param {string|ComponentDefinition} component - The component to mount, either as a name or definition
372
- * @param {Object<string, any>} props - The props to pass to the component
373
- * @returns {Promise<MountResult>} A promise that resolves to the mounted component instance
374
- * @throws {Error} If the container is not a valid HTMLElement
375
- */
376
- async _mountComponentInstance(container, component, props) {
377
- if (!(container instanceof HTMLElement)) return null;
378
- return await this.mount(container, component, props);
379
- }
380
-
381
- /**
382
- * Mounts components found by a selector in the container.
383
- * This method handles mounting multiple instances of the same component type.
384
- *
385
- * @private
386
- * @param {HTMLElement} container - The container to search for components
387
- * @param {string} selector - The CSS selector to find components
388
- * @param {string|ComponentDefinition} component - The component to mount
389
- * @param {Array<MountResult>} instances - Array to store the mounted component instances
390
- * @returns {Promise<void>}
391
- */
392
- async _mountComponentsBySelector(container, selector, component, instances) {
393
- for (const el of container.querySelectorAll(selector)) {
394
- const props = this._extractProps(el, ":");
395
- const instance = await this._mountComponentInstance(el, component, props);
396
- if (instance) instances.push(instance);
397
- }
398
- }
399
-
400
369
  /**
401
370
  * Mounts all components within the parent component's container.
402
371
  * This method handles mounting of explicitly defined children components.
@@ -419,20 +388,20 @@ export class Eleva {
419
388
  * };
420
389
  */
421
390
  async _mountComponents(container, children, childInstances) {
391
+ if (!children) return;
392
+
422
393
  // Clean up existing instances
423
394
  for (const child of childInstances) child.unmount();
424
395
  childInstances.length = 0;
425
396
 
426
397
  // Mount explicitly defined children components
427
- if (children) {
428
- for (const [selector, component] of Object.entries(children)) {
429
- if (!selector) continue;
430
- await this._mountComponentsBySelector(
431
- container,
432
- selector,
433
- component,
434
- childInstances
435
- );
398
+ for (const [selector, component] of Object.entries(children)) {
399
+ if (!selector) continue;
400
+ for (const el of container.querySelectorAll(selector)) {
401
+ if (!(el instanceof HTMLElement)) continue;
402
+ const props = this._extractProps(el, ":");
403
+ const instance = await this.mount(el, component, props);
404
+ if (instance) childInstances.push(instance);
436
405
  }
437
406
  }
438
407
  }
@@ -29,7 +29,7 @@ export class Emitter {
29
29
  * @public
30
30
  * @param {string} event - The name of the event to listen for.
31
31
  * @param {function(any): void} handler - The callback function to invoke when the event occurs.
32
- * @returns {function(): boolean} A function to unsubscribe the event handler.
32
+ * @returns {function(): void} A function to unsubscribe the event handler.
33
33
  * @example
34
34
  * const unsubscribe = emitter.on('user:login', (user) => console.log(user));
35
35
  * // Later...
@@ -13,16 +13,25 @@
13
13
  * renderer.patchDOM(container, newHtml);
14
14
  */
15
15
  export class Renderer {
16
+ /**
17
+ * Creates a new Renderer instance with a reusable temporary container for parsing HTML.
18
+ * @public
19
+ */
20
+ constructor() {
21
+ /** @private {HTMLElement} Reusable temporary container for parsing new HTML */
22
+ this._tempContainer = document.createElement("div");
23
+ }
24
+
16
25
  /**
17
26
  * Patches the DOM of a container element with new HTML content.
18
- * This method efficiently updates the DOM by comparing the new content with the existing
19
- * content and applying only the necessary changes.
27
+ * Efficiently updates the DOM by parsing new HTML into a reusable container
28
+ * and applying only the necessary changes.
20
29
  *
21
30
  * @public
22
31
  * @param {HTMLElement} container - The container element to patch.
23
32
  * @param {string} newHtml - The new HTML content to apply.
24
33
  * @returns {void}
25
- * @throws {Error} If container is not an HTMLElement or newHtml is not a string.
34
+ * @throws {Error} If container is not an HTMLElement, newHtml is not a string, or patching fails.
26
35
  */
27
36
  patchDOM(container, newHtml) {
28
37
  if (!(container instanceof HTMLElement)) {
@@ -32,10 +41,14 @@ export class Renderer {
32
41
  throw new Error("newHtml must be a string");
33
42
  }
34
43
 
35
- const temp = document.createElement("div");
36
- temp.innerHTML = newHtml;
37
- this._diff(container, temp);
38
- temp.innerHTML = "";
44
+ try {
45
+ // Directly set new HTML, replacing any existing content
46
+ this._tempContainer.innerHTML = newHtml;
47
+
48
+ this._diff(container, this._tempContainer);
49
+ } catch {
50
+ throw new Error("Failed to patch DOM");
51
+ }
39
52
  }
40
53
 
41
54
  /**
@@ -47,16 +60,8 @@ export class Renderer {
47
60
  * @param {HTMLElement} oldParent - The original DOM element.
48
61
  * @param {HTMLElement} newParent - The new DOM element.
49
62
  * @returns {void}
50
- * @throws {Error} If either parent is not an HTMLElement.
51
63
  */
52
64
  _diff(oldParent, newParent) {
53
- if (
54
- !(oldParent instanceof HTMLElement) ||
55
- !(newParent instanceof HTMLElement)
56
- ) {
57
- throw new Error("Both parents must be HTMLElements");
58
- }
59
-
60
65
  if (oldParent.isEqualNode(newParent)) return;
61
66
 
62
67
  const oldChildren = oldParent.childNodes;
@@ -67,8 +72,6 @@ export class Renderer {
67
72
  const oldNode = oldChildren[i];
68
73
  const newNode = newChildren[i];
69
74
 
70
- if (!oldNode && !newNode) continue;
71
-
72
75
  if (!oldNode && newNode) {
73
76
  oldParent.appendChild(newNode.cloneNode(true));
74
77
  continue;
@@ -115,13 +118,8 @@ export class Renderer {
115
118
  * @param {HTMLElement} oldEl - The element to update.
116
119
  * @param {HTMLElement} newEl - The element providing the updated attributes.
117
120
  * @returns {void}
118
- * @throws {Error} If either element is not an HTMLElement.
119
121
  */
120
122
  _updateAttributes(oldEl, newEl) {
121
- if (!(oldEl instanceof HTMLElement) || !(newEl instanceof HTMLElement)) {
122
- throw new Error("Both elements must be HTMLElements");
123
- }
124
-
125
123
  const oldAttrs = oldEl.attributes;
126
124
  const newAttrs = newEl.attributes;
127
125
 
@@ -10,6 +10,7 @@
10
10
  * const count = new Signal(0);
11
11
  * count.watch((value) => console.log(`Count changed to: ${value}`));
12
12
  * count.value = 1; // Logs: "Count changed to: 1"
13
+ * @template T
13
14
  */
14
15
  export class Signal {
15
16
  /**
@@ -170,30 +170,6 @@ export class Eleva {
170
170
  * // Returns: { name: "John", age: "25" }
171
171
  */
172
172
  private _extractProps;
173
- /**
174
- * Mounts a single component instance to a container element.
175
- * This method handles the actual mounting of a component with its props.
176
- *
177
- * @private
178
- * @param {HTMLElement} container - The container element to mount the component to
179
- * @param {string|ComponentDefinition} component - The component to mount, either as a name or definition
180
- * @param {Object<string, any>} props - The props to pass to the component
181
- * @returns {Promise<MountResult>} A promise that resolves to the mounted component instance
182
- * @throws {Error} If the container is not a valid HTMLElement
183
- */
184
- private _mountComponentInstance;
185
- /**
186
- * Mounts components found by a selector in the container.
187
- * This method handles mounting multiple instances of the same component type.
188
- *
189
- * @private
190
- * @param {HTMLElement} container - The container to search for components
191
- * @param {string} selector - The CSS selector to find components
192
- * @param {string|ComponentDefinition} component - The component to mount
193
- * @param {Array<MountResult>} instances - Array to store the mounted component instances
194
- * @returns {Promise<void>}
195
- */
196
- private _mountComponentsBySelector;
197
173
  /**
198
174
  * Mounts all components within the parent component's container.
199
175
  * This method handles mounting of explicitly defined children components.
@@ -1 +1 @@
1
- {"version":3,"file":"Eleva.d.ts","sourceRoot":"","sources":["../../src/core/Eleva.js"],"names":[],"mappings":"AAOA;;;;;;;;;;GAUG;AAEH;;;;;;GAMG;AAEH;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;GAaG;AACH;IACE;;;;;;;OAOG;IACH,kBAJW,MAAM;;OA8BhB;IAzBC,0EAA0E;IAC1E,oBAAgB;IAChB,yFAAyF;IACzF;;MAAoB;IACpB,oFAAoF;IACpF,wBAA4B;IAC5B,+FAA+F;IAC/F,6BAAoB;IACpB,wFAAwF;IACxF,0BAA8B;IAE9B,gGAAgG;IAChG,oBAA4B;IAC5B,2FAA2F;IAC3F,iBAAyB;IACzB,gFAAgF;IAChF,wBAMC;IACD,oFAAoF;IACpF,mBAAuB;IAGzB;;;;;;;;;;;OAWG;IACH,mBANW,WAAW;;QAET,KAAK,CASjB;IAED;;;;;;;;;;;;;;OAcG;IACH,uBAVW,MAAM,cACN,mBAAmB,GACjB,KAAK,CAYjB;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,wBAdW,WAAW,YACX,MAAM,GAAC,mBAAmB;;QAExB,OAAO,CAAC,WAAW,CAAC,CAoIhC;IAED;;;;;;;OAOG;IACH,+BAOC;IAED;;;;;;;;;OASG;IACH,uBAiBC;IAED;;;;;;;;;;OAUG;IACH,sBAYC;IAED;;;;;;;;;;;;OAYG;IACH,sBAQC;IAED;;;;;;;;;;OAUG;IACH,gCAGC;IAED;;;;;;;;;;OAUG;IACH,mCAMC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,yBAiBC;CACF;;;;;;;UA7a4C,CAAC;YAAO,MAAM,GAAE,GAAG;KAAC,GAAC,OAAO,CAAC;YAAO,MAAM,GAAE,GAAG;KAAC,CAAC,CAAC;;;;cAEjF,CAAS,IAAmB,EAAnB;YAAO,MAAM,GAAE,GAAG;KAAC,KAAG,MAAM;;;;;;UAEN,MAAM;;;;;;;;;;;;aAQrC,CAAS,IAAK,EAAL,KAAK,EAAE,IAAmB,EAAnB;YAAO,MAAM,GAAE,GAAG;KAAC,KAAG,IAAI;;;;UAE1C,MAAM;;;;;;eAMN,WAAW;;;;;;;;;;aAIX,MAAY,IAAI;;wBA7BN,uBAAuB;uBADxB,sBAAsB;yBAEpB,wBAAwB"}
1
+ {"version":3,"file":"Eleva.d.ts","sourceRoot":"","sources":["../../src/core/Eleva.js"],"names":[],"mappings":"AAOA;;;;;;;;;;GAUG;AAEH;;;;;;GAMG;AAEH;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;GAaG;AACH;IACE;;;;;;;OAOG;IACH,kBAJW,MAAM;;OA8BhB;IAzBC,0EAA0E;IAC1E,oBAAgB;IAChB,yFAAyF;IACzF;;MAAoB;IACpB,oFAAoF;IACpF,wBAA4B;IAC5B,+FAA+F;IAC/F,6BAAoB;IACpB,wFAAwF;IACxF,0BAA8B;IAE9B,gGAAgG;IAChG,oBAA4B;IAC5B,2FAA2F;IAC3F,iBAAyB;IACzB,gFAAgF;IAChF,wBAMC;IACD,oFAAoF;IACpF,mBAAuB;IAGzB;;;;;;;;;;;OAWG;IACH,mBANW,WAAW;;QAET,KAAK,CASjB;IAED;;;;;;;;;;;;;;OAcG;IACH,uBAVW,MAAM,cACN,mBAAmB,GACjB,KAAK,CAYjB;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,wBAdW,WAAW,YACX,MAAM,GAAC,mBAAmB;;QAExB,OAAO,CAAC,WAAW,CAAC,CAoIhC;IAED;;;;;;;OAOG;IACH,+BAOC;IAED;;;;;;;;;OASG;IACH,uBAoBC;IAED;;;;;;;;;;OAUG;IACH,sBAYC;IAED;;;;;;;;;;;;OAYG;IACH,sBASC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,yBAiBC;CACF;;;;;;;UA9Y4C,CAAC;YAAO,MAAM,GAAE,GAAG;KAAC,GAAC,OAAO,CAAC;YAAO,MAAM,GAAE,GAAG;KAAC,CAAC,CAAC;;;;cAEjF,CAAS,IAAmB,EAAnB;YAAO,MAAM,GAAE,GAAG;KAAC,KAAG,MAAM;;;;;;UAEN,MAAM;;;;;;;;;;;;aAQrC,CAAS,IAAK,EAAL,KAAK,EAAE,IAAmB,EAAnB;YAAO,MAAM,GAAE,GAAG;KAAC,KAAG,IAAI;;;;UAE1C,MAAM;;;;;;eAMN,WAAW;;;;;;;;;;aAIX,MAAY,IAAI;;wBA7BN,uBAAuB;uBADxB,sBAAsB;yBAEpB,wBAAwB"}
@@ -19,13 +19,13 @@ export class Emitter {
19
19
  * @public
20
20
  * @param {string} event - The name of the event to listen for.
21
21
  * @param {function(any): void} handler - The callback function to invoke when the event occurs.
22
- * @returns {function(): boolean} A function to unsubscribe the event handler.
22
+ * @returns {function(): void} A function to unsubscribe the event handler.
23
23
  * @example
24
24
  * const unsubscribe = emitter.on('user:login', (user) => console.log(user));
25
25
  * // Later...
26
26
  * unsubscribe(); // Stops listening for the event
27
27
  */
28
- public on(event: string, handler: (arg0: any) => void): () => boolean;
28
+ public on(event: string, handler: (arg0: any) => void): () => void;
29
29
  /**
30
30
  * Removes an event handler for the specified event name.
31
31
  * If no handler is provided, all handlers for the event are removed.
@@ -1 +1 @@
1
- {"version":3,"file":"Emitter.d.ts","sourceRoot":"","sources":["../../src/modules/Emitter.js"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH;IAOI,gHAAgH;IAChH,gBAAwB;IAG1B;;;;;;;;;;;;OAYG;IACH,iBARW,MAAM,WACN,CAAS,IAAG,EAAH,GAAG,KAAG,IAAI,GACjB,MAAY,OAAO,CAW/B;IAED;;;;;;;;OAQG;IACH,kBAJW,MAAM,YACN,CAAS,IAAG,EAAH,GAAG,KAAG,IAAI,GACjB,IAAI,CAYhB;IAED;;;;;;;;OAQG;IACH,mBAJW,MAAM,WACH,GAAG,EAAA,GACJ,IAAI,CAKhB;CACF"}
1
+ {"version":3,"file":"Emitter.d.ts","sourceRoot":"","sources":["../../src/modules/Emitter.js"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH;IAOI,gHAAgH;IAChH,gBAAwB;IAG1B;;;;;;;;;;;;OAYG;IACH,iBARW,MAAM,WACN,CAAS,IAAG,EAAH,GAAG,KAAG,IAAI,GACjB,MAAY,IAAI,CAW5B;IAED;;;;;;;;OAQG;IACH,kBAJW,MAAM,YACN,CAAS,IAAG,EAAH,GAAG,KAAG,IAAI,GACjB,IAAI,CAYhB;IAED;;;;;;;;OAQG;IACH,mBAJW,MAAM,WACH,GAAG,EAAA,GACJ,IAAI,CAKhB;CACF"}
@@ -11,16 +11,18 @@
11
11
  * renderer.patchDOM(container, newHtml);
12
12
  */
13
13
  export class Renderer {
14
+ /** @private {HTMLElement} Reusable temporary container for parsing new HTML */
15
+ private _tempContainer;
14
16
  /**
15
17
  * Patches the DOM of a container element with new HTML content.
16
- * This method efficiently updates the DOM by comparing the new content with the existing
17
- * content and applying only the necessary changes.
18
+ * Efficiently updates the DOM by parsing new HTML into a reusable container
19
+ * and applying only the necessary changes.
18
20
  *
19
21
  * @public
20
22
  * @param {HTMLElement} container - The container element to patch.
21
23
  * @param {string} newHtml - The new HTML content to apply.
22
24
  * @returns {void}
23
- * @throws {Error} If container is not an HTMLElement or newHtml is not a string.
25
+ * @throws {Error} If container is not an HTMLElement, newHtml is not a string, or patching fails.
24
26
  */
25
27
  public patchDOM(container: HTMLElement, newHtml: string): void;
26
28
  /**
@@ -32,7 +34,6 @@ export class Renderer {
32
34
  * @param {HTMLElement} oldParent - The original DOM element.
33
35
  * @param {HTMLElement} newParent - The new DOM element.
34
36
  * @returns {void}
35
- * @throws {Error} If either parent is not an HTMLElement.
36
37
  */
37
38
  private _diff;
38
39
  /**
@@ -43,7 +44,6 @@ export class Renderer {
43
44
  * @param {HTMLElement} oldEl - The element to update.
44
45
  * @param {HTMLElement} newEl - The element providing the updated attributes.
45
46
  * @returns {void}
46
- * @throws {Error} If either element is not an HTMLElement.
47
47
  */
48
48
  private _updateAttributes;
49
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Renderer.d.ts","sourceRoot":"","sources":["../../src/modules/Renderer.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH;IACE;;;;;;;;;;OAUG;IACH,2BALW,WAAW,WACX,MAAM,GACJ,IAAI,CAehB;IAED;;;;;;;;;;OAUG;IACH,cAwDC;IAED;;;;;;;;;OASG;IACH,0BAqDC;CACF"}
1
+ {"version":3,"file":"Renderer.d.ts","sourceRoot":"","sources":["../../src/modules/Renderer.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH;IAMI,+EAA+E;IAC/E,uBAAmD;IAGrD;;;;;;;;;;OAUG;IACH,2BALW,WAAW,WACX,MAAM,GACJ,IAAI,CAmBhB;IAED;;;;;;;;;OASG;IACH,cA+CC;IAED;;;;;;;;OAQG;IACH,0BAiDC;CACF"}
@@ -8,8 +8,9 @@
8
8
  * const count = new Signal(0);
9
9
  * count.watch((value) => console.log(`Count changed to: ${value}`));
10
10
  * count.value = 1; // Logs: "Count changed to: 1"
11
+ * @template T
11
12
  */
12
- export class Signal {
13
+ export class Signal<T> {
13
14
  /**
14
15
  * Creates a new Signal instance with the specified initial value.
15
16
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Signal.d.ts","sourceRoot":"","sources":["../../src/modules/Signal.js"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH;IACE;;;;;OAKG;IACH,mBAFW,GAAC,EASX;IANC,6GAA6G;IAC7G,eAAmB;IACnB,sIAAsI;IACtI,kBAA0B;IAC1B,sHAAsH;IACtH,iBAAqB;IAavB;;;;;;;OAOG;IACH,yBAHW,CAAC,EAQX;IAvBD;;;;;OAKG;IACH,oBAFa,CAAC,CAIb;IAiBD;;;;;;;;;;;OAWG;IACH,iBAPW,CAAS,IAAC,EAAD,CAAC,KAAG,IAAI,GACf,MAAY,OAAO,CAS/B;IAED;;;;;;;OAOG;IACH,gBAQC;CACF"}
1
+ {"version":3,"file":"Signal.d.ts","sourceRoot":"","sources":["../../src/modules/Signal.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,oBAFa,CAAC;IAGZ;;;;;OAKG;IACH,mBAFW,GAAC,EASX;IANC,6GAA6G;IAC7G,eAAmB;IACnB,sIAAsI;IACtI,kBAA0B;IAC1B,sHAAsH;IACtH,iBAAqB;IAavB;;;;;;;OAOG;IACH,yBAHW,CAAC,EAQX;IAvBD;;;;;OAKG;IACH,oBAFa,CAAC,CAIb;IAiBD;;;;;;;;;;;OAWG;IACH,iBAPW,CAAS,IAAC,EAAD,CAAC,KAAG,IAAI,GACf,MAAY,OAAO,CAS/B;IAED;;;;;;;OAOG;IACH,gBAQC;CACF"}
package/dist/eleva.min.js DELETED
@@ -1,3 +0,0 @@
1
- /*! Eleva v1.2.14-beta | MIT License | https://elevajs.com */
2
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Eleva=e()}(this,(function(){"use strict";class t{static expressionPattern=/\{\{\s*(.*?)\s*\}\}/g;static parse(t,e){return"string"!=typeof t?t:t.replace(this.expressionPattern,((t,n)=>this.evaluate(n,e)))}static evaluate(t,e){if("string"!=typeof t)return t;try{return new Function("data",`with(data) { return ${t}; }`)(e)}catch{return""}}}class e{constructor(t){this._value=t,this._watchers=new Set,this._pending=!1}get value(){return this._value}set value(t){this._value!==t&&(this._value=t,this._notify())}watch(t){return this._watchers.add(t),()=>this._watchers.delete(t)}_notify(){this._pending||(this._pending=!0,queueMicrotask((()=>{this._watchers.forEach((t=>t(this._value))),this._pending=!1})))}}class n{constructor(){this._events=new Map}on(t,e){return this._events.has(t)||this._events.set(t,new Set),this._events.get(t).add(e),()=>this.off(t,e)}off(t,e){if(this._events.has(t))if(e){const n=this._events.get(t);n.delete(e),0===n.size&&this._events.delete(t)}else this._events.delete(t)}emit(t,...e){this._events.has(t)&&this._events.get(t).forEach((t=>t(...e)))}}class o{patchDOM(t,e){if(!(t instanceof HTMLElement))throw new Error("Container must be an HTMLElement");if("string"!=typeof e)throw new Error("newHtml must be a string");const n=document.createElement("div");n.innerHTML=e,this._diff(t,n),n.innerHTML=""}_diff(t,e){if(!(t instanceof HTMLElement&&e instanceof HTMLElement))throw new Error("Both parents must be HTMLElements");if(t.isEqualNode(e))return;const n=t.childNodes,o=e.childNodes,s=Math.max(n.length,o.length);for(let e=0;e<s;e++){const s=n[e],i=o[e];if(!s&&!i)continue;if(!s&&i){t.appendChild(i.cloneNode(!0));continue}if(s&&!i){t.removeChild(s);continue}if(s.nodeType===i.nodeType&&s.nodeName===i.nodeName)if(s.nodeType===Node.ELEMENT_NODE){const e=s.getAttribute("key"),n=i.getAttribute("key");if(e!==n&&(e||n)){t.replaceChild(i.cloneNode(!0),s);continue}this._updateAttributes(s,i),this._diff(s,i)}else s.nodeType===Node.TEXT_NODE&&s.nodeValue!==i.nodeValue&&(s.nodeValue=i.nodeValue);else t.replaceChild(i.cloneNode(!0),s)}}_updateAttributes(t,e){if(!(t instanceof HTMLElement&&e instanceof HTMLElement))throw new Error("Both elements must be HTMLElements");const n=t.attributes,o=e.attributes;for(const{name:o}of n)e.hasAttribute(o)||t.removeAttribute(o);for(const e of o){const{name:n,value:o}=e;if(!n.startsWith("@")&&t.getAttribute(n)!==o)if(t.setAttribute(n,o),n.startsWith("aria-")){t["aria"+n.slice(5).replace(/-([a-z])/g,((t,e)=>e.toUpperCase()))]=o}else if(n.startsWith("data-"))t.dataset[n.slice(5)]=o;else{const e=n.replace(/-([a-z])/g,((t,e)=>e.toUpperCase()));if(e in t){const n=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(t),e),s="boolean"==typeof t[e]||n?.get&&"boolean"==typeof n.get.call(t);t[e]=s?"false"!==o&&(""===o||o===e||"true"===o):o}}}}}return class{constructor(t,s={}){this.name=t,this.config=s,this.emitter=new n,this.signal=e,this.renderer=new o,this._components=new Map,this._plugins=new Map,this._lifecycleHooks=["onBeforeMount","onMount","onBeforeUpdate","onUpdate","onUnmount"],this._isMounted=!1}use(t,e={}){return t.install(this,e),this._plugins.set(t.name,t),this}component(t,e){return this._components.set(t,e),this}async mount(n,o,s={}){if(!n)throw new Error(`Container not found: ${n}`);const i="string"==typeof o?this._components.get(o):o;if(!i)throw new Error(`Component "${o}" not registered.`);if("function"!=typeof i.template)throw new Error("Component template must be a function");const{setup:r,template:a,style:c,children:l}=i,u={props:s,emitter:this.emitter,signal:t=>new this.signal(t),...this._prepareLifecycleHooks()},f="function"==typeof r?await r(u):{};return await(async s=>{const i={...u,...s},r=[],f=[],h=[];this._isMounted?i.onBeforeUpdate&&i.onBeforeUpdate():i.onBeforeMount&&i.onBeforeMount();const p=async()=>{const e=t.parse(a(i),i);this.renderer.patchDOM(n,e),this._processEvents(n,i,h),this._injectStyles(n,o,c,i),await this._mountComponents(n,l,f),this._isMounted?i.onUpdate&&i.onUpdate():(i.onMount&&i.onMount(),this._isMounted=!0)};for(const t of Object.values(s))t instanceof e&&r.push(t.watch(p));return await p(),{container:n,data:i,unmount:()=>{for(const t of r)t();for(const t of h)t();for(const t of f)t.unmount();i.onUnmount&&i.onUnmount(),n.innerHTML=""}}})(f)}_prepareLifecycleHooks(){const t={};for(const e of this._lifecycleHooks)t[e]=()=>{};return t}_processEvents(e,n,o){const s=e.querySelectorAll("*");for(const e of s){const s=e.attributes;for(let i=0;i<s.length;i++){const r=s[i];if(r.name.startsWith("@")){const s=r.name.slice(1),i=t.evaluate(r.value,n);"function"==typeof i&&(e.addEventListener(s,i),e.removeAttribute(r.name),o.push((()=>e.removeEventListener(s,i))))}}}}_injectStyles(e,n,o,s){if(!o)return;let i=e.querySelector(`style[data-eleva-style="${n}"]`);i||(i=document.createElement("style"),i.setAttribute("data-eleva-style",n),e.appendChild(i)),i.textContent=t.parse(o(s),s)}_extractProps(t,e){const n={};for(const{name:o,value:s}of t.attributes)o.startsWith(e)&&(n[o.replace(e,"")]=s);return n}async _mountComponentInstance(t,e,n){return t instanceof HTMLElement?await this.mount(t,e,n):null}async _mountComponentsBySelector(t,e,n,o){for(const s of t.querySelectorAll(e)){const t=this._extractProps(s,":"),e=await this._mountComponentInstance(s,n,t);e&&o.push(e)}}async _mountComponents(t,e,n){for(const t of n)t.unmount();if(n.length=0,e)for(const[o,s]of Object.entries(e))o&&await this._mountComponentsBySelector(t,o,s,n)}}}));
3
- //# sourceMappingURL=eleva.min.js.map