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/README.md +7 -4
- package/dist/eleva.cjs.js +42 -67
- package/dist/eleva.cjs.js.map +1 -1
- package/dist/eleva.d.ts +9 -32
- package/dist/eleva.esm.js +42 -67
- package/dist/eleva.esm.js.map +1 -1
- package/dist/eleva.umd.js +42 -67
- package/dist/eleva.umd.js.map +1 -1
- package/dist/eleva.umd.min.js +3 -0
- package/dist/eleva.umd.min.js.map +1 -0
- package/package.json +98 -75
- package/src/core/Eleva.js +23 -54
- package/src/modules/Emitter.js +1 -1
- package/src/modules/Renderer.js +20 -22
- package/src/modules/Signal.js +1 -0
- package/types/core/Eleva.d.ts +0 -24
- package/types/core/Eleva.d.ts.map +1 -1
- package/types/modules/Emitter.d.ts +2 -2
- package/types/modules/Emitter.d.ts.map +1 -1
- package/types/modules/Renderer.d.ts +5 -5
- package/types/modules/Renderer.d.ts.map +1 -1
- package/types/modules/Signal.d.ts +2 -1
- package/types/modules/Signal.d.ts.map +1 -1
- package/dist/eleva.min.js +0 -3
- package/dist/eleva.min.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,29 +1,104 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eleva",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.15-beta",
|
|
4
4
|
"description": "A minimalist and lightweight, pure vanilla JavaScript frontend runtime framework.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
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
|
|
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
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
"
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
}
|
package/src/modules/Emitter.js
CHANGED
|
@@ -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():
|
|
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...
|
package/src/modules/Renderer.js
CHANGED
|
@@ -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
|
-
*
|
|
19
|
-
*
|
|
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
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
|
package/src/modules/Signal.js
CHANGED
package/types/core/Eleva.d.ts
CHANGED
|
@@ -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,
|
|
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():
|
|
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): () =>
|
|
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,
|
|
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
|
-
*
|
|
17
|
-
*
|
|
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
|
|
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;
|
|
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
|
|
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
|