ladrillosjs 2.0.0-beta.6 → 2.0.0-beta.8
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/LICENSE +21 -0
- package/README.md +126 -46
- package/dist/core/builtins/lazyElement.d.ts +35 -0
- package/dist/core/cache/expressionCache.d.ts +15 -0
- package/dist/core/component/bindingParser.d.ts +2 -0
- package/dist/core/component/cache.d.ts +22 -0
- package/dist/core/component/extract.d.ts +2 -0
- package/dist/core/component/loader.d.ts +10 -0
- package/dist/core/component/webcomponent.d.ts +29 -0
- package/dist/core/configure.d.ts +28 -0
- package/dist/core/css/cssParser/cssParser.d.ts +3 -0
- package/dist/core/diff/listDiff.d.ts +99 -0
- package/dist/core/directives/directiveProcessor.d.ts +58 -0
- package/dist/core/events/eventBus.d.ts +136 -0
- package/dist/core/helpers/frameworkHelpers.d.ts +38 -0
- package/dist/core/html/htmlparser.d.ts +19 -0
- package/dist/core/js/moduleExecutor.d.ts +118 -0
- package/dist/core/js/reactivity.d.ts +52 -0
- package/dist/core/js/scriptParser.d.ts +52 -0
- package/dist/core/ladrillos.d.ts +68 -0
- package/dist/core/lazy/index.d.ts +6 -0
- package/dist/core/lazy/lazyLoader.d.ts +22 -0
- package/dist/core/lazy/lazyStrategies.d.ts +73 -0
- package/dist/core/scheduler/batchScheduler.d.ts +97 -0
- package/dist/core.d.ts +10 -9
- package/dist/core.js +2 -1
- package/dist/core.js.map +1 -0
- package/dist/events.js +1 -1
- package/dist/index.d.ts +11 -9
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -0
- package/dist/lazy.d.ts +1 -1
- package/dist/lazy.js +1 -1
- package/dist/{shared-DGGk2qBc.js → shared-D-P0qKQY.js} +2 -1
- package/dist/shared-D-P0qKQY.js.map +1 -0
- package/dist/shared-DmVkrquj.js +2 -0
- package/dist/shared-DmVkrquj.js.map +1 -0
- package/dist/shared-yJteVv_p.js +3 -0
- package/dist/shared-yJteVv_p.js.map +1 -0
- package/dist/types/index.d.ts +171 -0
- package/dist/utils/devWarnings.d.ts +133 -0
- package/dist/utils/directives.d.ts +149 -0
- package/dist/utils/jsevents.d.ts +4 -0
- package/dist/utils/keyModifiers.d.ts +106 -0
- package/dist/utils/regex.d.ts +5 -0
- package/dist/utils/sandbox.d.ts +25 -0
- package/package.json +46 -15
- package/dist/shared-Dru_qCE_.js +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Daniel Rubio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -44,7 +44,10 @@
|
|
|
44
44
|
### 1. Add the Script
|
|
45
45
|
|
|
46
46
|
```html
|
|
47
|
-
<script
|
|
47
|
+
<script type="module">
|
|
48
|
+
import ladrillosjs from "https://cdn.jsdelivr.net/npm/ladrillosjs@2/dist/index.js";
|
|
49
|
+
window.ladrillosjs = ladrillosjs;
|
|
50
|
+
</script>
|
|
48
51
|
```
|
|
49
52
|
|
|
50
53
|
### 2. Create a Component
|
|
@@ -89,9 +92,9 @@ Save this as `counter.html`:
|
|
|
89
92
|
<!DOCTYPE html>
|
|
90
93
|
<html>
|
|
91
94
|
<head>
|
|
92
|
-
<script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
|
|
93
95
|
<script type="module">
|
|
94
|
-
|
|
96
|
+
import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs@2/dist/index.js";
|
|
97
|
+
registerComponent("my-counter", "./counter.html");
|
|
95
98
|
</script>
|
|
96
99
|
</head>
|
|
97
100
|
<body>
|
|
@@ -108,20 +111,27 @@ That's it! Your reactive component is ready. 🎉
|
|
|
108
111
|
|
|
109
112
|
### CDN (No Build Step)
|
|
110
113
|
|
|
114
|
+
LadrillosJS v2 is distributed as native ES modules. Import directly from a CDN:
|
|
115
|
+
|
|
111
116
|
```html
|
|
112
|
-
<!--
|
|
113
|
-
<script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
|
|
117
|
+
<!-- ES Module (recommended) -->
|
|
114
118
|
<script type="module">
|
|
115
|
-
|
|
119
|
+
import {
|
|
120
|
+
registerComponent,
|
|
121
|
+
registerComponents,
|
|
122
|
+
} from "https://cdn.jsdelivr.net/npm/ladrillosjs@2/dist/index.js";
|
|
123
|
+
|
|
124
|
+
registerComponent("my-component", "./component.html");
|
|
116
125
|
</script>
|
|
117
126
|
|
|
118
|
-
<!--
|
|
127
|
+
<!-- Also available on unpkg -->
|
|
119
128
|
<script type="module">
|
|
120
|
-
import { registerComponent } from "https://
|
|
121
|
-
registerComponent("my-component", "./component.html");
|
|
129
|
+
import { registerComponent } from "https://unpkg.com/ladrillosjs@2/dist/index.js";
|
|
122
130
|
</script>
|
|
123
131
|
```
|
|
124
132
|
|
|
133
|
+
> **Note:** LadrillosJS v2 is ESM-only. Legacy UMD/IIFE global builds are not published to npm.
|
|
134
|
+
|
|
125
135
|
### NPM (With Build Tools)
|
|
126
136
|
|
|
127
137
|
```bash
|
|
@@ -141,6 +151,22 @@ await registerComponents([
|
|
|
141
151
|
]);
|
|
142
152
|
```
|
|
143
153
|
|
|
154
|
+
### Granular Imports (Tree-Shaking)
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
// Full API
|
|
158
|
+
import { registerComponent, $emit, $listen } from "ladrillosjs";
|
|
159
|
+
|
|
160
|
+
// Core only (no lazy loading, no event bus)
|
|
161
|
+
import { registerComponent } from "ladrillosjs/core";
|
|
162
|
+
|
|
163
|
+
// Lazy loading strategies only
|
|
164
|
+
import { lazyOnVisible, lazyOnIdle } from "ladrillosjs/lazy";
|
|
165
|
+
|
|
166
|
+
// Event bus only
|
|
167
|
+
import { $emit, $listen } from "ladrillosjs/events";
|
|
168
|
+
```
|
|
169
|
+
|
|
144
170
|
---
|
|
145
171
|
|
|
146
172
|
## 📖 Core Concepts
|
|
@@ -203,16 +229,16 @@ Use `$bind` to sync form inputs with state:
|
|
|
203
229
|
|
|
204
230
|
---
|
|
205
231
|
|
|
206
|
-
## 🧩 Directives
|
|
232
|
+
## 🧩 Built-in Elements & Directives
|
|
207
233
|
|
|
208
234
|
### Conditional Rendering
|
|
209
235
|
|
|
210
|
-
Use
|
|
236
|
+
Use `<if>`, `<else-if>`, and `<else>` to conditionally render elements:
|
|
211
237
|
|
|
212
238
|
```html
|
|
213
|
-
<
|
|
214
|
-
<
|
|
215
|
-
<
|
|
239
|
+
<if condition="status === 'loading'">Loading...</if>
|
|
240
|
+
<else-if condition="status === 'error'">Something went wrong!</else-if>
|
|
241
|
+
<else>Content loaded successfully!</else>
|
|
216
242
|
|
|
217
243
|
<script>
|
|
218
244
|
let status = "loading";
|
|
@@ -221,10 +247,10 @@ Use `$if`, `$else-if`, and `$else` to conditionally render elements:
|
|
|
221
247
|
|
|
222
248
|
### Show/Hide (CSS Toggle)
|
|
223
249
|
|
|
224
|
-
Use
|
|
250
|
+
Use `<show>` to toggle visibility without removing from DOM (uses `display: none`):
|
|
225
251
|
|
|
226
252
|
```html
|
|
227
|
-
<
|
|
253
|
+
<show condition="isVisible">I can be shown or hidden</show>
|
|
228
254
|
|
|
229
255
|
<button onclick="isVisible = !isVisible">Toggle</button>
|
|
230
256
|
|
|
@@ -233,27 +259,33 @@ Use `$show` to toggle visibility without removing from DOM (uses `display: none`
|
|
|
233
259
|
</script>
|
|
234
260
|
```
|
|
235
261
|
|
|
236
|
-
>
|
|
262
|
+
> **`<show>` vs `<if>`:** `<show>` toggles CSS display (children stay in DOM), `<if>` adds/removes children entirely.
|
|
237
263
|
|
|
238
264
|
### List Rendering
|
|
239
265
|
|
|
240
|
-
Use
|
|
266
|
+
Use `<for>` to render lists with optional index and key:
|
|
241
267
|
|
|
242
268
|
```html
|
|
243
269
|
<!-- Simple list -->
|
|
244
270
|
<ul>
|
|
245
|
-
<
|
|
271
|
+
<for each="fruit in fruits">
|
|
272
|
+
<li>🍎 {fruit}</li>
|
|
273
|
+
</for>
|
|
246
274
|
</ul>
|
|
247
275
|
|
|
248
276
|
<!-- With index -->
|
|
249
|
-
<
|
|
277
|
+
<for each="(item, index) in items">
|
|
278
|
+
<div>#{index + 1}: {item}</div>
|
|
279
|
+
</for>
|
|
250
280
|
|
|
251
281
|
<!-- Object array with key -->
|
|
252
|
-
<
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
</
|
|
282
|
+
<for each="user in users" key="user.id">
|
|
283
|
+
<div>
|
|
284
|
+
<span>{user.avatar}</span>
|
|
285
|
+
<span>{user.name}</span>
|
|
286
|
+
<span>{user.role}</span>
|
|
287
|
+
</div>
|
|
288
|
+
</for>
|
|
257
289
|
|
|
258
290
|
<script>
|
|
259
291
|
let fruits = ["Apple", "Banana", "Cherry"];
|
|
@@ -265,19 +297,34 @@ Use `$for` to render lists with optional index and key:
|
|
|
265
297
|
</script>
|
|
266
298
|
```
|
|
267
299
|
|
|
268
|
-
###
|
|
300
|
+
### Lazy Loading
|
|
301
|
+
|
|
302
|
+
Use `<lazy>` to defer rendering until a trigger fires (viewport, idle, delay, interaction, media):
|
|
269
303
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
304
|
+
```html
|
|
305
|
+
<lazy margin="100px">
|
|
306
|
+
<heavy-chart></heavy-chart>
|
|
307
|
+
</lazy>
|
|
308
|
+
|
|
309
|
+
<lazy interaction="click,focus">
|
|
310
|
+
<support-chat></support-chat>
|
|
311
|
+
</lazy>
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Cheat Sheet
|
|
315
|
+
|
|
316
|
+
| Element / Directive | Purpose | Example |
|
|
317
|
+
| ------------------- | ------------------------ | ------------------------------------------------------ |
|
|
318
|
+
| `<if>` | Conditional render | `<if condition="isLoggedIn">Welcome!</if>` |
|
|
319
|
+
| `<else-if>` | Chained condition | `<else-if condition="isGuest">Hello Guest</else-if>` |
|
|
320
|
+
| `<else>` | Fallback | `<else>Please log in</else>` |
|
|
321
|
+
| `<show>` | CSS visibility toggle | `<show condition="isOpen">Menu</show>` |
|
|
322
|
+
| `<for>` | Loop rendering | `<for each="item in items"><li>{item}</li></for>` |
|
|
323
|
+
| `<for>` (indexed) | Loop with index | `<for each="(item, i) in items">…</for>` |
|
|
324
|
+
| `<for key="…">` | List optimization | `<for each="u in users" key="u.id">…</for>` |
|
|
325
|
+
| `<lazy>` | Defer rendering | `<lazy idle><analytics-pixel /></lazy>` |
|
|
326
|
+
| `$bind` | Two-way binding | `<input $bind="email" />` |
|
|
327
|
+
| `$ref` | Element reference | `<input $ref="inputEl" />` |
|
|
281
328
|
|
|
282
329
|
---
|
|
283
330
|
|
|
@@ -418,7 +465,7 @@ Force a lazy component to load immediately by adding the `eager` attribute:
|
|
|
418
465
|
|
|
419
466
|
## 📋 API Reference
|
|
420
467
|
|
|
421
|
-
### registerComponent
|
|
468
|
+
### `registerComponent`
|
|
422
469
|
|
|
423
470
|
```javascript
|
|
424
471
|
registerComponent(name, path, useShadowDOM?, lazy?)
|
|
@@ -442,7 +489,7 @@ registerComponent("my-nav", "./nav.html", false);
|
|
|
442
489
|
registerComponent("my-footer", "./footer.html", true, lazyOnVisible());
|
|
443
490
|
```
|
|
444
491
|
|
|
445
|
-
### registerComponents
|
|
492
|
+
### `registerComponents`
|
|
446
493
|
|
|
447
494
|
Register multiple components with parallel fetching:
|
|
448
495
|
|
|
@@ -456,6 +503,37 @@ const result = await registerComponents([
|
|
|
456
503
|
// Returns: { success: [...], failed: [...], skipped: [...] }
|
|
457
504
|
```
|
|
458
505
|
|
|
506
|
+
### `$use`
|
|
507
|
+
|
|
508
|
+
Infer the component tag name from the file path:
|
|
509
|
+
|
|
510
|
+
```javascript
|
|
511
|
+
await $use("./components/user-card.html"); // Registers as <user-card>
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### `loadLazyComponent`
|
|
515
|
+
|
|
516
|
+
Force a lazy component to load immediately from JavaScript:
|
|
517
|
+
|
|
518
|
+
```javascript
|
|
519
|
+
import { loadLazyComponent } from "ladrillosjs";
|
|
520
|
+
|
|
521
|
+
await loadLazyComponent("my-lazy-footer");
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### `configure`
|
|
525
|
+
|
|
526
|
+
Configure framework-level options (optional):
|
|
527
|
+
|
|
528
|
+
```javascript
|
|
529
|
+
import { configure } from "ladrillosjs";
|
|
530
|
+
|
|
531
|
+
configure({
|
|
532
|
+
cacheSize: 50, // Component LRU cache size (default: 25)
|
|
533
|
+
onError: (err) => telemetry.capture(err), // Custom error handler
|
|
534
|
+
});
|
|
535
|
+
```
|
|
536
|
+
|
|
459
537
|
### Event Bus
|
|
460
538
|
|
|
461
539
|
| Function | Description |
|
|
@@ -511,14 +589,16 @@ A complete CRUD example combining all directives:
|
|
|
511
589
|
</form>
|
|
512
590
|
|
|
513
591
|
<ul>
|
|
514
|
-
<
|
|
515
|
-
<
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
592
|
+
<for each="todo in todos" key="todo.id">
|
|
593
|
+
<li>
|
|
594
|
+
<input type="checkbox" onclick="toggleTodo({todo.id})" />
|
|
595
|
+
<span class="{todo.completed ? 'done' : ''}">{todo.text}</span>
|
|
596
|
+
<button onclick="removeTodo({todo.id})">🗑️</button>
|
|
597
|
+
</li>
|
|
598
|
+
</for>
|
|
519
599
|
</ul>
|
|
520
600
|
|
|
521
|
-
<
|
|
601
|
+
<if condition="todos.length === 0"><p>No todos yet!</p></if>
|
|
522
602
|
</div>
|
|
523
603
|
|
|
524
604
|
<script>
|
|
@@ -539,7 +619,7 @@ A complete CRUD example combining all directives:
|
|
|
539
619
|
|
|
540
620
|
function toggleTodo(id) {
|
|
541
621
|
todos = todos.map((t) =>
|
|
542
|
-
t.id === id ? { ...t, completed: !t.completed } : t
|
|
622
|
+
t.id === id ? { ...t, completed: !t.completed } : t,
|
|
543
623
|
);
|
|
544
624
|
}
|
|
545
625
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { LazyStrategy } from '../lazy/lazyStrategies';
|
|
2
|
+
/**
|
|
3
|
+
* Pick a LazyStrategy from the attributes on a <lazy> element.
|
|
4
|
+
* Returns `null` for `eager` (= load immediately, no observation needed).
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveLazyStrategy(el: Element): LazyStrategy | null;
|
|
7
|
+
/**
|
|
8
|
+
* Process a single <lazy> element. Removes it from the DOM and replaces it
|
|
9
|
+
* with a comment placeholder + (optional) placeholder content. Schedules the
|
|
10
|
+
* actual content to swap in when the chosen strategy fires.
|
|
11
|
+
*/
|
|
12
|
+
export declare function processLazyElement(lazyEl: Element): void;
|
|
13
|
+
/**
|
|
14
|
+
* Find and process all top-level <lazy> elements in `host`. <lazy> elements
|
|
15
|
+
* inside <for> templates are skipped; they get processed when the loop
|
|
16
|
+
* renders each iteration via processLazyElement on the cloned content.
|
|
17
|
+
*
|
|
18
|
+
* Accepts a connected host OR a detached DocumentFragment — the latter is
|
|
19
|
+
* used by `loadTemplate` to preprocess <lazy> before any custom-element
|
|
20
|
+
* children get a chance to fire connectedCallback (and drain their light
|
|
21
|
+
* DOM into __originalChildren). Without this preprocessing, lazy's
|
|
22
|
+
* detach-then-reattach cycle would re-run children's connectedCallback with
|
|
23
|
+
* an empty innerHTML, breaking components that read $host.__originalHTML.
|
|
24
|
+
*/
|
|
25
|
+
export declare function scanLazyElements(host: HTMLElement | ShadowRoot | DocumentFragment): void;
|
|
26
|
+
/**
|
|
27
|
+
* Returns all detached DocumentFragments held by pending `<lazy>` placeholders
|
|
28
|
+
* inside `host`. Used by binding/event-handler/directive scanners to wire up
|
|
29
|
+
* the children of `<lazy>` elements while they are still detached from the
|
|
30
|
+
* document. Wiring done on these fragments survives the later move into the
|
|
31
|
+
* host tree when the lazy strategy fires.
|
|
32
|
+
*
|
|
33
|
+
* Returns an empty array for hosts that contain no pending lazy fragments.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getPendingLazyContent(host: HTMLElement | ShadowRoot | DocumentFragment): DocumentFragment[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variable Regex Cache
|
|
3
|
+
*
|
|
4
|
+
* Building a `RegExp` is comparatively expensive, and the reactivity layer
|
|
5
|
+
* checks the same variable names against many binding expressions when working
|
|
6
|
+
* out which bindings depend on which state keys. Caching the compiled regex per
|
|
7
|
+
* variable name avoids recreating it on every check.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Gets or creates a cached regex for variable boundary matching.
|
|
11
|
+
*
|
|
12
|
+
* @param variableName - The variable name to match
|
|
13
|
+
* @returns A regex that matches the variable as a whole word
|
|
14
|
+
*/
|
|
15
|
+
export declare function getCachedVariableRegex(variableName: string): RegExp;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set the maximum number of component sources retained in the LRU cache.
|
|
3
|
+
* When the new size is smaller than the current cache, the least-recently
|
|
4
|
+
* used entries are evicted until the limit is satisfied.
|
|
5
|
+
*/
|
|
6
|
+
export declare const setCacheSize: (size: number) => void;
|
|
7
|
+
/**
|
|
8
|
+
* LRU Cache: Gets cached content and marks it as recently used
|
|
9
|
+
* Moves the accessed item to the end of the Map (most recently used position)
|
|
10
|
+
* This ensures frequently accessed components stay in cache longer
|
|
11
|
+
* @param path - The file path to retrieve from cache
|
|
12
|
+
* @returns The cached content or undefined if not found
|
|
13
|
+
*/
|
|
14
|
+
export declare const getCachedComponentSource: (path: string) => string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* LRU Cache: Stores content with automatic eviction of least recently used items
|
|
17
|
+
* Maintains cache size limit by removing oldest items when full
|
|
18
|
+
* Updates existing items without affecting cache size
|
|
19
|
+
* @param path - The file path to cache
|
|
20
|
+
* @param content - The content to store
|
|
21
|
+
*/
|
|
22
|
+
export declare const setCachedComponentSource: (path: string, content: string) => void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of fetching a component source
|
|
3
|
+
*/
|
|
4
|
+
export interface FetchComponentResult {
|
|
5
|
+
/** The HTML source content */
|
|
6
|
+
source: string;
|
|
7
|
+
/** The actual resolved path (may differ from input for folder-as-component) */
|
|
8
|
+
resolvedPath: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function fetchComponentSource(path: string): Promise<FetchComponentResult | undefined>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { LadrillosComponent } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a Web Component class from a Ladrillos component definition.
|
|
4
|
+
*
|
|
5
|
+
* This function creates the class but does NOT register it with customElements.
|
|
6
|
+
* Use createWebComponent() if you want to both create and register.
|
|
7
|
+
*
|
|
8
|
+
* Follows the Web Components specification:
|
|
9
|
+
* - Proper lifecycle callbacks (connectedCallback, disconnectedCallback, etc.)
|
|
10
|
+
* - Observed attributes with attributeChangedCallback
|
|
11
|
+
* - Shadow DOM encapsulation (optional)
|
|
12
|
+
* - Reactive state that syncs with the DOM
|
|
13
|
+
*
|
|
14
|
+
* - Attributes from HTML OVERRIDE script variable defaults
|
|
15
|
+
* - Script variables serve as DEFAULT values when no attribute is provided
|
|
16
|
+
*
|
|
17
|
+
* Example:
|
|
18
|
+
* <my-counter count="5"></my-counter> <!-- count = 5, not the default -->
|
|
19
|
+
* <my-counter></my-counter> <!-- count = 0 (script default) -->
|
|
20
|
+
*/
|
|
21
|
+
export declare function createWebComponentClass(component: LadrillosComponent, useShadowDOM: boolean): typeof HTMLElement;
|
|
22
|
+
/**
|
|
23
|
+
* Creates and registers a Web Component from a Ladrillos component.
|
|
24
|
+
*
|
|
25
|
+
* This is the main entry point that:
|
|
26
|
+
* 1. Creates the component class
|
|
27
|
+
* 2. Registers it with customElements.define
|
|
28
|
+
*/
|
|
29
|
+
export declare function createWebComponent(component: LadrillosComponent, useShadowDOM: boolean): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { LadrillosErrorHandler } from '../utils/devWarnings';
|
|
2
|
+
/**
|
|
3
|
+
* Options accepted by `configure()`.
|
|
4
|
+
*/
|
|
5
|
+
export interface LadrillosConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Maximum number of component source files retained in the LRU cache.
|
|
8
|
+
* Defaults to 25. Must be a positive integer.
|
|
9
|
+
*/
|
|
10
|
+
cacheSize?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Custom error handler. Called in addition to the framework's built-in
|
|
13
|
+
* console logging so embedders can route framework errors to telemetry.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* configure({
|
|
17
|
+
* onError: (err) => telemetry.capture(err),
|
|
18
|
+
* });
|
|
19
|
+
*/
|
|
20
|
+
onError?: LadrillosErrorHandler | null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Configure framework-level options.
|
|
24
|
+
*
|
|
25
|
+
* Safe to call at any time; subsequent calls override prior values. Pass
|
|
26
|
+
* `onError: null` to clear a previously registered handler.
|
|
27
|
+
*/
|
|
28
|
+
export declare function configure(config: LadrillosConfig): void;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyed List Diffing Algorithm
|
|
3
|
+
*
|
|
4
|
+
* Uses a simplified LIS (Longest Increasing Subsequence) approach
|
|
5
|
+
* for optimal DOM operations.
|
|
6
|
+
*
|
|
7
|
+
* Benefits:
|
|
8
|
+
* - Minimizes DOM operations (moves instead of recreate)
|
|
9
|
+
* - Preserves element state (focus, scroll, animations)
|
|
10
|
+
* - O(n) best case, O(n log n) worst case
|
|
11
|
+
*
|
|
12
|
+
* Usage with $for:
|
|
13
|
+
* $for="item in items track by item.id"
|
|
14
|
+
* ^^^^^^^^^^^^^^
|
|
15
|
+
* Key expression for efficient diffing
|
|
16
|
+
*/
|
|
17
|
+
export interface DiffOperation {
|
|
18
|
+
type: "insert" | "remove" | "move" | "update";
|
|
19
|
+
/** Index in the old array (for remove/move/update) */
|
|
20
|
+
oldIndex?: number;
|
|
21
|
+
/** Index in the new array (for insert/move/update) */
|
|
22
|
+
newIndex?: number;
|
|
23
|
+
/** The item data */
|
|
24
|
+
item?: unknown;
|
|
25
|
+
/** Key for keyed operations */
|
|
26
|
+
key?: unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Computes the minimal set of DOM operations to transform oldItems into newItems.
|
|
30
|
+
* Uses keys for identity matching - items with the same key are considered the same.
|
|
31
|
+
*
|
|
32
|
+
* @param oldItems - Previous array items
|
|
33
|
+
* @param newItems - New array items
|
|
34
|
+
* @param getKey - Function to extract a unique key from an item
|
|
35
|
+
* @returns Array of operations to perform
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const ops = diffKeyed(
|
|
39
|
+
* [{ id: 1, name: 'A' }, { id: 2, name: 'B' }],
|
|
40
|
+
* [{ id: 2, name: 'B' }, { id: 1, name: 'A' }, { id: 3, name: 'C' }],
|
|
41
|
+
* item => item.id
|
|
42
|
+
* );
|
|
43
|
+
* // ops = [
|
|
44
|
+
* // { type: 'move', oldIndex: 1, newIndex: 0, key: 2 },
|
|
45
|
+
* // { type: 'move', oldIndex: 0, newIndex: 1, key: 1 },
|
|
46
|
+
* // { type: 'insert', newIndex: 2, key: 3, item: { id: 3, name: 'C' } }
|
|
47
|
+
* // ]
|
|
48
|
+
*/
|
|
49
|
+
export declare function diffKeyed<T>(oldItems: T[], newItems: T[], getKey: (item: T, index: number) => unknown): DiffOperation[];
|
|
50
|
+
/**
|
|
51
|
+
* Simpler diff for non-keyed lists.
|
|
52
|
+
* Less efficient but works when items don't have stable identity.
|
|
53
|
+
*
|
|
54
|
+
* @param oldLength - Previous array length
|
|
55
|
+
* @param newLength - New array length
|
|
56
|
+
* @param newItems - New array items
|
|
57
|
+
* @returns Array of operations
|
|
58
|
+
*/
|
|
59
|
+
export declare function diffUnkeyed<T>(oldLength: number, newLength: number, newItems: T[]): DiffOperation[];
|
|
60
|
+
/**
|
|
61
|
+
* Computes the set of positions that form the longest increasing subsequence
|
|
62
|
+
* of `source`, ignoring entries whose value is < 0 (used to mark brand-new
|
|
63
|
+
* items that must always be (re)inserted).
|
|
64
|
+
*
|
|
65
|
+
* The loop renderer uses this to decide which reused elements are already in
|
|
66
|
+
* correct relative DOM order and can therefore stay put — only the remaining
|
|
67
|
+
* elements need to be moved. This turns an in-order content update into zero
|
|
68
|
+
* DOM moves and minimizes moves for partial reorders.
|
|
69
|
+
*
|
|
70
|
+
* Runs in O(n log n) using patience sorting with predecessor links.
|
|
71
|
+
*
|
|
72
|
+
* @param source - For each new position, the element's previous index, or -1 if new
|
|
73
|
+
* @returns Set of new-position indices that should NOT be moved
|
|
74
|
+
*/
|
|
75
|
+
export declare function getStableIndices(source: number[]): Set<number>;
|
|
76
|
+
/**
|
|
77
|
+
* Creates a key getter function from a key expression.
|
|
78
|
+
*
|
|
79
|
+
* @param keyExpr - Key expression (e.g., "item.id" or just "id" if item is scope)
|
|
80
|
+
* @param itemName - The loop variable name (e.g., "item")
|
|
81
|
+
* @returns A function that extracts the key from an item
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* const getKey = createKeyGetter("item.id", "item");
|
|
85
|
+
* getKey({ id: 123, name: "foo" }) // 123
|
|
86
|
+
*/
|
|
87
|
+
export declare function createKeyGetter<T>(keyExpr: string | undefined, itemName: string): (item: T, index: number) => unknown;
|
|
88
|
+
/**
|
|
89
|
+
* Applies diff operations to a list of DOM elements.
|
|
90
|
+
* This is the main integration point with the loop renderer.
|
|
91
|
+
*
|
|
92
|
+
* @param container - Parent element containing the list
|
|
93
|
+
* @param elements - Current rendered elements
|
|
94
|
+
* @param operations - Diff operations to apply
|
|
95
|
+
* @param createFn - Function to create a new element for an item
|
|
96
|
+
* @param updateFn - Function to update an existing element
|
|
97
|
+
* @returns The new array of elements
|
|
98
|
+
*/
|
|
99
|
+
export declare function applyDiffOperations<T>(container: Element | ShadowRoot, elements: Element[], operations: DiffOperation[], createFn: (item: T, index: number) => Element, updateFn: (element: Element, item: T, index: number) => void): Element[];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ConditionalDescriptor, LoopDescriptor, TwoWayBindingDescriptor } from '../../types';
|
|
2
|
+
export type RefMap = Map<string, HTMLElement>;
|
|
3
|
+
export type DirectiveContext = {
|
|
4
|
+
loops: LoopDescriptor[];
|
|
5
|
+
conditionals: ConditionalDescriptor[][];
|
|
6
|
+
twoWayBindings: TwoWayBindingDescriptor[];
|
|
7
|
+
refs: RefMap;
|
|
8
|
+
showElements: ShowDescriptor[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Registry for two-way bindings.
|
|
12
|
+
* Maps state keys to the elements bound to them.
|
|
13
|
+
*/
|
|
14
|
+
export type TwoWayBindingRegistry = Map<string, Array<{
|
|
15
|
+
element: HTMLElement;
|
|
16
|
+
path: string[];
|
|
17
|
+
isContentEditable?: boolean;
|
|
18
|
+
}>>;
|
|
19
|
+
export type ShowDescriptor = {
|
|
20
|
+
element: HTMLElement;
|
|
21
|
+
expression: string;
|
|
22
|
+
originalDisplay: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Scans the template for all directives and returns descriptors for each.
|
|
26
|
+
* This should be called after the template HTML is injected into the DOM.
|
|
27
|
+
*/
|
|
28
|
+
export declare function scanDirectives(host: HTMLElement | ShadowRoot): DirectiveContext;
|
|
29
|
+
/**
|
|
30
|
+
* Scans the template for all directives and returns descriptors for each.
|
|
31
|
+
* This version accepts an existing refs Map to populate (used when refs
|
|
32
|
+
* need to be available before scripts run).
|
|
33
|
+
*/
|
|
34
|
+
export declare function scanDirectivesWithRefs(host: HTMLElement | ShadowRoot, existingRefs: RefMap): DirectiveContext;
|
|
35
|
+
/**
|
|
36
|
+
* Scans for $ref directives only and populates the refs Map.
|
|
37
|
+
* This can be called early (before scripts run) to make refs available.
|
|
38
|
+
*/
|
|
39
|
+
export declare function scanRefsOnly(host: HTMLElement | ShadowRoot | DocumentFragment, refs: RefMap): void;
|
|
40
|
+
/**
|
|
41
|
+
* Renders all loops with the current state.
|
|
42
|
+
*/
|
|
43
|
+
export declare function renderLoops(loops: LoopDescriptor[], state: Record<string, unknown>, evaluateExpression: (expr: string, context: Record<string, unknown>) => unknown): void;
|
|
44
|
+
/**
|
|
45
|
+
* Updates all conditionals with the current state.
|
|
46
|
+
*/
|
|
47
|
+
export declare function updateConditionals(conditionals: ConditionalDescriptor[][], state: Record<string, unknown>, evaluateExpression: (expr: string, context: Record<string, unknown>) => unknown): void;
|
|
48
|
+
/**
|
|
49
|
+
* Updates all $show elements with the current state.
|
|
50
|
+
*/
|
|
51
|
+
export declare function updateShowElements(showElements: ShowDescriptor[], state: Record<string, unknown>, evaluateExpression: (expr: string, context: Record<string, unknown>) => unknown): void;
|
|
52
|
+
/**
|
|
53
|
+
* Sets up two-way bindings and returns a registry for state→input sync.
|
|
54
|
+
*
|
|
55
|
+
* Returns a function that should be called when state changes to update
|
|
56
|
+
* all bound input elements with the new state values.
|
|
57
|
+
*/
|
|
58
|
+
export declare function setupTwoWayBindings(bindings: TwoWayBindingDescriptor[], state: Record<string, unknown>, evaluateExpression: (expr: string, context: Record<string, unknown>) => unknown): (changedKey?: string) => void;
|