ladrillosjs 2.0.0-beta.5.1 → 2.0.0-beta.7

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -46
  3. package/dist/core/builtins/lazyElement.d.ts +35 -0
  4. package/dist/core/cache/expressionCache.d.ts +15 -0
  5. package/dist/core/component/bindingParser.d.ts +2 -0
  6. package/dist/core/component/cache.d.ts +22 -0
  7. package/dist/core/component/extract.d.ts +2 -0
  8. package/dist/core/component/loader.d.ts +10 -0
  9. package/dist/core/component/webcomponent.d.ts +29 -0
  10. package/dist/core/configure.d.ts +28 -0
  11. package/dist/core/css/cssParser/cssParser.d.ts +3 -0
  12. package/dist/core/diff/listDiff.d.ts +99 -0
  13. package/dist/core/directives/directiveProcessor.d.ts +58 -0
  14. package/dist/core/events/eventBus.d.ts +136 -0
  15. package/dist/core/helpers/frameworkHelpers.d.ts +38 -0
  16. package/dist/core/html/htmlparser.d.ts +19 -0
  17. package/dist/core/js/moduleExecutor.d.ts +118 -0
  18. package/dist/core/js/reactivity.d.ts +52 -0
  19. package/dist/core/js/scriptParser.d.ts +52 -0
  20. package/dist/core/ladrillos.d.ts +68 -0
  21. package/dist/core/lazy/index.d.ts +6 -0
  22. package/dist/core/lazy/lazyLoader.d.ts +22 -0
  23. package/dist/core/lazy/lazyStrategies.d.ts +73 -0
  24. package/dist/core/scheduler/batchScheduler.d.ts +97 -0
  25. package/dist/core.d.ts +15 -0
  26. package/dist/core.js +2 -0
  27. package/dist/core.js.map +1 -0
  28. package/dist/events.d.ts +20 -0
  29. package/dist/events.js +1 -0
  30. package/dist/index.d.ts +11 -9
  31. package/dist/index.js +2 -1
  32. package/dist/index.js.map +1 -0
  33. package/dist/lazy.d.ts +17 -0
  34. package/dist/lazy.js +1 -0
  35. package/dist/shared-7u414ZMv.js +3 -0
  36. package/dist/shared-7u414ZMv.js.map +1 -0
  37. package/dist/shared-BA0RyagV.js +2 -0
  38. package/dist/shared-BA0RyagV.js.map +1 -0
  39. package/dist/shared-D-P0qKQY.js +2 -0
  40. package/dist/shared-D-P0qKQY.js.map +1 -0
  41. package/dist/types/index.d.ts +171 -0
  42. package/dist/utils/devWarnings.d.ts +133 -0
  43. package/dist/utils/directives.d.ts +149 -0
  44. package/dist/utils/jsevents.d.ts +4 -0
  45. package/dist/utils/keyModifiers.d.ts +106 -0
  46. package/dist/utils/regex.d.ts +5 -0
  47. package/dist/utils/sandbox.d.ts +25 -0
  48. package/package.json +62 -14
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 src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></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
- ladrillosjs.registerComponent("my-counter", "./counter.html");
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
- <!-- Global (UMD) -->
113
- <script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
117
+ <!-- ES Module (recommended) -->
114
118
  <script type="module">
115
- ladrillosjs.registerComponent("my-component", "./component.html");
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
- <!-- ES Module -->
127
+ <!-- Also available on unpkg -->
119
128
  <script type="module">
120
- import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
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 `$if`, `$else-if`, and `$else` to conditionally render elements:
236
+ Use `<if>`, `<else-if>`, and `<else>` to conditionally render elements:
211
237
 
212
238
  ```html
213
- <div $if="{status === 'loading'}">Loading...</div>
214
- <div $else-if="{status === 'error'}">Something went wrong!</div>
215
- <div $else>Content loaded successfully!</div>
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 `$show` to toggle visibility without removing from DOM (uses `display: none`):
250
+ Use `<show>` to toggle visibility without removing from DOM (uses `display: none`):
225
251
 
226
252
  ```html
227
- <div $show="{isVisible}">I can be shown or hidden</div>
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
- > **`$show` vs `$if`:** `$show` toggles CSS display (element stays in DOM), `$if` adds/removes from DOM entirely.
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 `$for` to render lists with optional index and key:
266
+ Use `<for>` to render lists with optional index and key:
241
267
 
242
268
  ```html
243
269
  <!-- Simple list -->
244
270
  <ul>
245
- <li $for="fruit in fruits">🍎 {fruit}</li>
271
+ <for each="fruit in fruits">
272
+ <li>🍎 {fruit}</li>
273
+ </for>
246
274
  </ul>
247
275
 
248
276
  <!-- With index -->
249
- <div $for="(item, index) in items">#{index + 1}: {item}</div>
277
+ <for each="(item, index) in items">
278
+ <div>#{index + 1}: {item}</div>
279
+ </for>
250
280
 
251
281
  <!-- Object array with key -->
252
- <div $for="user in users" $key="user.id">
253
- <span>{user.avatar}</span>
254
- <span>{user.name}</span>
255
- <span>{user.role}</span>
256
- </div>
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
- ### Directives Cheat Sheet
300
+ ### Lazy Loading
269
301
 
270
- | Directive | Purpose | Example |
271
- | ---------------- | --------------------- | ------------------------------------------------ |
272
- | `$if` | Conditional render | `<div $if="{isLoggedIn}">Welcome!</div>` |
273
- | `$else-if` | Chained condition | `<div $else-if="{isGuest}">Hello Guest</div>` |
274
- | `$else` | Fallback | `<div $else>Please log in</div>` |
275
- | `$show` | CSS visibility toggle | `<div $show="{isOpen}">Menu</div>` |
276
- | `$for` | Loop rendering | `<li $for="item in items">{item}</li>` |
277
- | `$for` (indexed) | Loop with index | `<li $for="(item, i) in items">{i}: {item}</li>` |
278
- | `$bind` | Two-way binding | `<input $bind="email" />` |
279
- | `$key` | List optimization | `<div $for="user in users" $key="user.id">` |
280
- | `$ref` | Element reference | `<input $ref="inputEl" />` |
302
+ Use `<lazy>` to defer rendering until a trigger fires (viewport, idle, delay, interaction, media):
303
+
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
 
@@ -406,11 +453,19 @@ await registerComponents([
406
453
  | `lazyOnMedia` | Mobile/desktop specific components |
407
454
  | `lazyOnDelay` | Chat widgets, notifications |
408
455
 
456
+ ### Eager Override
457
+
458
+ Force a lazy component to load immediately by adding the `eager` attribute:
459
+
460
+ ```html
461
+ <lazy-footer eager></lazy-footer>
462
+ ```
463
+
409
464
  ---
410
465
 
411
466
  ## 📋 API Reference
412
467
 
413
- ### registerComponent
468
+ ### `registerComponent`
414
469
 
415
470
  ```javascript
416
471
  registerComponent(name, path, useShadowDOM?, lazy?)
@@ -434,7 +489,7 @@ registerComponent("my-nav", "./nav.html", false);
434
489
  registerComponent("my-footer", "./footer.html", true, lazyOnVisible());
435
490
  ```
436
491
 
437
- ### registerComponents
492
+ ### `registerComponents`
438
493
 
439
494
  Register multiple components with parallel fetching:
440
495
 
@@ -448,6 +503,37 @@ const result = await registerComponents([
448
503
  // Returns: { success: [...], failed: [...], skipped: [...] }
449
504
  ```
450
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
+
451
537
  ### Event Bus
452
538
 
453
539
  | Function | Description |
@@ -503,14 +589,16 @@ A complete CRUD example combining all directives:
503
589
  </form>
504
590
 
505
591
  <ul>
506
- <li $for="todo in todos" $key="todo.id">
507
- <input type="checkbox" onclick="toggleTodo({todo.id})" />
508
- <span class="{todo.completed ? 'done' : ''}">{todo.text}</span>
509
- <button onclick="removeTodo({todo.id})">🗑️</button>
510
- </li>
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>
511
599
  </ul>
512
600
 
513
- <p $if="{todos.length === 0}">No todos yet!</p>
601
+ <if condition="todos.length === 0"><p>No todos yet!</p></if>
514
602
  </div>
515
603
 
516
604
  <script>
@@ -531,7 +619,7 @@ A complete CRUD example combining all directives:
531
619
 
532
620
  function toggleTodo(id) {
533
621
  todos = todos.map((t) =>
534
- t.id === id ? { ...t, completed: !t.completed } : t
622
+ t.id === id ? { ...t, completed: !t.completed } : t,
535
623
  );
536
624
  }
537
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,2 @@
1
+ import { BindingDescriptor } from '../../types';
2
+ export declare function analyzeBinding(raw: string): BindingDescriptor["bindings"][number];
@@ -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,2 @@
1
+ import { LadrillosComponent } from '../../types';
2
+ export declare function parseComponent(source: string, name: string, componentUrl?: string): Promise<LadrillosComponent>;
@@ -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,3 @@
1
+ type StyleTarget = HTMLElement | ShadowRoot;
2
+ export declare const loadStyles: (target: StyleTarget, cssText: string | undefined, useShadowDOM: boolean) => void;
3
+ export {};
@@ -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;