ladrillosjs 2.0.0-beta.1 → 2.0.0-beta.10

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 (71) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +451 -678
  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/{cache/index.d.ts → core/component/cache.d.ts} +8 -2
  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/diff/listDiff.d.ts +99 -0
  12. package/dist/core/directives/directiveProcessor.d.ts +58 -0
  13. package/dist/core/events/eventBus.d.ts +136 -0
  14. package/dist/core/helpers/frameworkHelpers.d.ts +38 -0
  15. package/dist/core/html/htmlparser.d.ts +15 -12
  16. package/dist/core/js/moduleExecutor.d.ts +118 -0
  17. package/dist/core/js/reactivity.d.ts +52 -0
  18. package/dist/core/js/scriptParser.d.ts +52 -3
  19. package/dist/core/ladrillos.d.ts +68 -0
  20. package/dist/core/lazy/index.d.ts +6 -0
  21. package/dist/core/lazy/lazyLoader.d.ts +22 -0
  22. package/dist/core/lazy/lazyStrategies.d.ts +73 -0
  23. package/dist/core/scheduler/batchScheduler.d.ts +97 -0
  24. package/dist/core.d.ts +15 -0
  25. package/dist/core.js +2 -0
  26. package/dist/core.js.map +1 -0
  27. package/dist/events.d.ts +20 -0
  28. package/dist/events.js +1 -0
  29. package/dist/index.d.ts +28 -60
  30. package/dist/index.js +2 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/lazy.d.ts +17 -0
  33. package/dist/lazy.js +1 -0
  34. package/dist/shared-CCPr4_4R.js +3 -0
  35. package/dist/shared-CCPr4_4R.js.map +1 -0
  36. package/dist/shared-D-P0qKQY.js +2 -0
  37. package/dist/shared-D-P0qKQY.js.map +1 -0
  38. package/dist/shared-cwUZzSZ7.js +2 -0
  39. package/dist/shared-cwUZzSZ7.js.map +1 -0
  40. package/dist/types/index.d.ts +171 -0
  41. package/dist/utils/devWarnings.d.ts +133 -0
  42. package/dist/utils/directives.d.ts +149 -0
  43. package/dist/utils/jsevents.d.ts +4 -0
  44. package/dist/utils/keyModifiers.d.ts +106 -0
  45. package/dist/utils/regex.d.ts +4 -1
  46. package/dist/utils/sandbox.d.ts +25 -0
  47. package/package.json +97 -49
  48. package/dist/cache/functionCache.d.ts +0 -15
  49. package/dist/core/componentParser.d.ts +0 -29
  50. package/dist/core/componentSource.d.ts +0 -12
  51. package/dist/core/eventBus.d.ts +0 -41
  52. package/dist/core/html/htmlRenderer.d.ts +0 -18
  53. package/dist/core/main.d.ts +0 -9
  54. package/dist/core/webcomponent.d.ts +0 -2
  55. package/dist/index-CXHidyhO.js +0 -8
  56. package/dist/index-CXHidyhO.js.map +0 -1
  57. package/dist/index-VkDZJVOR.mjs +0 -361
  58. package/dist/index-VkDZJVOR.mjs.map +0 -1
  59. package/dist/ladrillosjs.cjs.js +0 -2
  60. package/dist/ladrillosjs.cjs.js.map +0 -1
  61. package/dist/ladrillosjs.es.js +0 -14
  62. package/dist/ladrillosjs.es.js.map +0 -1
  63. package/dist/ladrillosjs.umd.js +0 -76
  64. package/dist/ladrillosjs.umd.js.map +0 -1
  65. package/dist/types/LadrilloTypes.d.ts +0 -51
  66. package/dist/utils/logger.d.ts +0 -23
  67. package/dist/webcomponent-CJ3lZBZb.mjs +0 -703
  68. package/dist/webcomponent-CJ3lZBZb.mjs.map +0 -1
  69. package/dist/webcomponent-i9W7LUiv.js +0 -70
  70. package/dist/webcomponent-i9W7LUiv.js.map +0 -1
  71. /package/dist/core/css/{cssParser.d.ts → cssParser/cssParser.d.ts} +0 -0
package/README.md CHANGED
@@ -1,899 +1,672 @@
1
1
  # LadrillosJS
2
2
 
3
- <img src="https://raw.githubusercontent.com/drubiodev/LadrillosJS/refs/heads/main/LadrillosJS.png" alt="LadrillosJS" width="400"/>
4
-
5
- A lightweight, zero-dependency web component framework for building modular web applications.
6
-
7
- **Version 2.0** - Now rewritten in TypeScript with enhanced performance, better developer experience, and powerful new features.
8
-
9
- "I designed this framework to empower developers with the ability to componentize their code efficiently and effectively, without the need for a full-scale framework. By focusing on simplicity and leveraging core web fundamentals, my goal was to create a lightweight and accessible solution that enhances development while staying true to the basics."
10
-
11
- ## Table of Contents
12
-
13
- - [Features](#features)
14
- - [Getting Started](#getting-started)
15
- - [Prerequisites](#prerequisites)
16
- - [What's New in v2.0](#whats-new-in-v20)
17
- - [Example Applications](#example-applications)
18
- - [Installation](#installation)
19
- - [Your First Component](#your-first-component)
20
- - [Core Concepts](#core-concepts)
21
- - [Component Registration](#component-registration)
22
- - [State Management](#state-management)
23
- - [Event Handling](#event-handling)
24
- - [Data Binding](#data-binding)
25
- - [Conditional Rendering](#conditional-rendering)
26
- - [Slots](#slots)
27
- - [Advanced Features](#advanced-features)
28
- - [External Scripts](#external-scripts)
29
- - [Global Event Bus](#global-event-bus)
30
- - [Shadow DOM](#shadow-dom)
31
- - [Performance & Caching](#performance--caching)
32
- - [API Reference](#api-reference)
33
- - [Examples](#examples)
34
- - [Component Communication](#component-communication)
35
- - [Dynamic Component Creation](#dynamic-component-creation)
36
- - [Passing Complex Data](#passing-complex-data)
37
- - [Migration Guide (v1.x to v2.0)](#migration-guide-v1x-to-v20)
38
- - [Contributing](#contributing)
39
- - [License](#license)
40
-
41
- ## Features
42
-
43
- - 🚀 **Zero Dependencies** - Pure JavaScript, no build tools required
44
- - 📦 **Single-File Components** - HTML, CSS, and JavaScript in one file
45
- - ⚡ **Reactive State** - Automatic re-rendering on state changes with optimized proxies
46
- - 🎯 **Event System** - Built-in event emission and global event bus for component communication
47
- - 🔄 **Two-Way Data Binding** - Seamless binding for form inputs with `$bind`
48
- - 🎨 **Scoped Styles** - Component styles with optional Shadow DOM
49
- - 🏪 **Global Event Bus** - Cross-component communication without prop drilling
50
- - 🔌 **Slots Support** - Content projection with named and default slots
51
- - 📝 **TypeScript Support** - Full type definitions and TypeScript source code
52
- - 🎭 **Conditional Rendering** - `data-if`, `data-else-if`, and `data-else` directives
53
- - 🚄 **Smart Caching** - LRU cache for components and compiled functions
54
- - 🔧 **Framework Utilities** - `$state`, `$setState`, `$emit`, `$listen`, `$querySelector` helpers
55
- - ⚙️ **Automatic Reactivity** - Variable assignments automatically trigger re-renders
56
- - 🧩 **External Scripts** - Load and bind external JavaScript with your components
57
-
58
- ## Getting Started
59
-
60
- ### Prerequisites
61
-
62
- - **Node.js**: Version 20.19+ or 22.12+ is required for development
63
- - **TypeScript**: v5.8+ (included in devDependencies)
64
-
65
- ### What's New in v2.0
66
-
67
- - **🔄 Complete TypeScript Rewrite** - Full type safety and improved IDE support
68
- - **⚡ Performance Enhancements** - LRU caching for components and compiled functions
69
- - **🎯 Global Event Bus** - New `$emit` and `$listen` for cross-component communication
70
- - **🔧 Framework Utilities** - New `$state`, `$setState`, `$querySelector` helpers
71
- - **🚀 Automatic Reactivity** - Variable assignments like `count++` automatically trigger re-renders
72
- - **📦 Better Build System** - Vite-powered builds with sourcemaps and multiple output formats (ESM, UMD, CJS)
73
- - **🧪 Testing** - Vitest with coverage reporting
74
- - **🎭 Enhanced Conditionals** - More robust `data-if`, `data-else-if`, `data-else` rendering
75
- - **🔄 Two-Way Binding** - Simplified with `$bind` prefix for automatic state synchronization
76
- - **🧹 Memory Management** - Automatic cleanup of event listeners on component disconnect
77
-
78
- ### Example Applications
79
-
80
- The repository includes several example applications that demonstrate various features:
81
-
82
- - **[Todo App](samples/apps/todo)** - Classic todo list with component composition
83
- - **[Notes App](samples/apps/notes)** - Multi-component app with global event bus
84
- - **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
85
- - **[API Example](samples/apps/api)** - Fetching and displaying external data
86
- - **[Business Card](samples/apps/biz)** - Editable form with two-way data binding using `$bind`
87
- - **[Button Game](samples/apps/button-game)** - Interactive game with component events
88
- - **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation system
89
- - **[Document Chat](samples/apps/document-chat)** - Chat interface with component communication
90
- - **[Docs](samples/apps/docs)** - Documentation viewer with syntax highlighting
91
-
92
- To run the examples:
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/drubiodev/LadrillosJS/refs/heads/main/LadrillosJS.jpg" alt="LadrillosJS" width="300"/>
5
+ </p>
93
6
 
94
- ```bash
95
- # Clone the repository
96
- git clone https://github.com/drubiodev/LadrillosJS.git
97
- cd LadrillosJS
7
+ <p align="center">
8
+ <strong>A lightweight, zero-dependency web component framework.</strong>
9
+ </p>
98
10
 
99
- # Install dependencies
100
- npm install
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/ladrillosjs"><img src="https://img.shields.io/npm/v/ladrillosjs.svg" alt="npm version"></a>
13
+ <a href="https://github.com/drubiodev/LadrillosJS/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/ladrillosjs.svg" alt="license"></a>
14
+ <a href="https://bundlephobia.com/package/ladrillosjs"><img src="https://img.shields.io/bundlephobia/minzip/ladrillosjs" alt="bundle size"></a>
15
+ <a href="https://github.com/drubiodev/LadrillosJS"><img src="https://img.shields.io/github/stars/drubiodev/LadrillosJS?style=social" alt="GitHub stars"></a>
16
+ </p>
101
17
 
102
- # Start the development server (Vite)
103
- npm run dev
18
+ <p align="center">
19
+ Build modular web apps with simple HTML components. No virtual DOM, no complex tooling required.
20
+ </p>
104
21
 
105
- # Build the library
106
- npm run build
107
-
108
- # Run tests
109
- npm test
110
-
111
- # Run tests with coverage
112
- npm run test:coverage
113
- ```
22
+ ---
114
23
 
115
- ## Installation
24
+ ## 📑 Table of Contents
25
+
26
+ - [Quick Start](#-quick-start)
27
+ - [Installation](#-installation)
28
+ - [Core Concepts](#-core-concepts)
29
+ - [Directives](#-directives)
30
+ - [Event Bus](#-event-bus)
31
+ - [Element References](#-element-references)
32
+ - [Lazy Loading](#-lazy-loading)
33
+ - [API Reference](#-api-reference)
34
+ - [Using with Vite](#-using-with-vite)
35
+ - [Browser Support](#-browser-support)
36
+ - [Examples](#-examples)
37
+ - [Contributing](#-contributing)
38
+ - [License](#-license)
116
39
 
117
- ### NPM
40
+ ---
118
41
 
119
- ```bash
120
- npm install ladrillosjs
121
- ```
42
+ ## 🚀 Quick Start
122
43
 
123
- ### CDN
44
+ ### 1. Add the Script
124
45
 
125
46
  ```html
126
- <!-- Latest version -->
127
47
  <script type="module">
128
- import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
129
- registerComponent("my-component", "./my-component.html");
130
- </script>
131
-
132
- <!-- UMD (for legacy browsers) -->
133
- <script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
134
- <script>
135
- // Access via global ladrillosjs object
136
- ladrillosjs.registerComponent("my-component", "./my-component.html");
48
+ import ladrillosjs from "https://cdn.jsdelivr.net/npm/ladrillosjs@2/dist/index.js";
49
+ window.ladrillosjs = ladrillosjs;
137
50
  </script>
138
51
  ```
139
52
 
140
- ## Your First Component
141
-
142
- A component in LadrillosJS is a reusable custom HTML element that bundles its own template, logic, and styles into a single file.
53
+ ### 2. Create a Component
143
54
 
144
- ### 1. Create a Component File
145
-
146
- Create `hello-world.html`:
55
+ Save this as `counter.html`:
147
56
 
148
57
  ```html
149
- <!-- hello-world.html -->
150
- <div class="greeting">
151
- <h1>{title}</h1>
152
- <p>Hello, {name}!</p>
153
- <button onclick="greet">Click me ({count})</button>
58
+ <div class="counter">
59
+ <div class="count-display">{count}</div>
60
+ <div class="buttons">
61
+ <button onclick="count--">−</button>
62
+ <button onclick="count = 0">Reset</button>
63
+ <button onclick="count++">+</button>
64
+ </div>
65
+ <p>Double: {count * 2} | Squared: {count * count}</p>
154
66
  </div>
155
67
 
156
68
  <script>
157
- // Component state
158
- let title = "Welcome to LadrillosJS";
159
- let name = "World";
160
69
  let count = 0;
161
-
162
- // Event handler
163
- const greet = () => {
164
- count++;
165
- name = prompt("What's your name?") || "World";
166
- };
167
70
  </script>
168
71
 
169
72
  <style>
170
- .greeting {
73
+ .counter {
171
74
  text-align: center;
172
75
  padding: 2rem;
173
- background: #f0f0f0;
174
- border-radius: 8px;
175
76
  }
176
-
77
+ .count-display {
78
+ font-size: 4rem;
79
+ font-weight: bold;
80
+ }
177
81
  button {
178
82
  padding: 0.5rem 1rem;
179
- font-size: 1rem;
83
+ margin: 0.25rem;
180
84
  cursor: pointer;
181
85
  }
182
86
  </style>
183
87
  ```
184
88
 
185
- ### 2. Register and Use the Component
89
+ ### 3. Use It
186
90
 
187
91
  ```html
188
92
  <!DOCTYPE html>
189
93
  <html>
190
94
  <head>
191
- <title>My App</title>
192
- </head>
193
- <body>
194
- <!-- Use your component -->
195
- <hello-world></hello-world>
196
-
197
- <!-- Register component -->
198
95
  <script type="module">
199
- import { registerComponent } from "ladrillosjs";
200
- registerComponent("hello-world", "./hello-world.html");
96
+ import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs@2/dist/index.js";
97
+ registerComponent("my-counter", "./counter.html");
201
98
  </script>
99
+ </head>
100
+ <body>
101
+ <my-counter></my-counter>
202
102
  </body>
203
103
  </html>
204
104
  ```
205
105
 
206
- ## Core Concepts
106
+ That's it! Your reactive component is ready. 🎉
107
+
108
+ ---
109
+
110
+ ## 📦 Installation
207
111
 
208
- ### Component Registration
112
+ ### CDN (No Build Step)
209
113
 
210
- Register single or multiple components:
114
+ LadrillosJS v2 is distributed as native ES modules. Import directly from a CDN:
115
+
116
+ ```html
117
+ <!-- ES Module (recommended) -->
118
+ <script type="module">
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");
125
+ </script>
126
+
127
+ <!-- Also available on unpkg -->
128
+ <script type="module">
129
+ import { registerComponent } from "https://unpkg.com/ladrillosjs@2/dist/index.js";
130
+ </script>
131
+ ```
132
+
133
+ > **Note:** LadrillosJS v2 is ESM-only. Legacy UMD/IIFE global builds are not published to npm.
134
+
135
+ ### NPM (With Build Tools)
136
+
137
+ ```bash
138
+ npm install ladrillosjs
139
+ ```
211
140
 
212
141
  ```javascript
142
+ import { registerComponent, registerComponents } from "ladrillosjs";
143
+
213
144
  // Single component
214
- import { registerComponent } from "ladrillosjs";
215
- await registerComponent("my-component", "./my-component.html");
145
+ registerComponent("my-counter", "./components/counter.html");
216
146
 
217
- // Multiple components (v2.0 feature - currently in development)
218
- import { registerComponents } from "ladrillosjs";
147
+ // Multiple components
219
148
  await registerComponents([
220
149
  { name: "app-header", path: "./components/header.html" },
221
150
  { name: "app-footer", path: "./components/footer.html" },
222
- { name: "user-card", path: "./components/user-card.html" },
223
151
  ]);
224
-
225
- // Using CDN
226
- ladrillosjs.registerComponent("my-component", "./my-component.html");
227
152
  ```
228
153
 
229
- **Note:** The `registerComponents` function is planned for v2.0 to enable bulk registration with concurrency control.
154
+ ### Granular Imports (Tree-Shaking)
230
155
 
231
- ### State Management
156
+ ```javascript
157
+ // Full API
158
+ import { registerComponent, $emit, $listen } from "ladrillosjs";
232
159
 
233
- Components have reactive state that automatically triggers re-renders. In v2.0, variable assignments are automatically tracked and trigger reactivity:
160
+ // Core only (no lazy loading, no event bus)
161
+ import { registerComponent } from "ladrillosjs/core";
234
162
 
235
- ```html
236
- <div>
237
- <h2>User: {user.name}</h2>
238
- <p>Score: {score}</p>
239
- <button onclick="updateScore">Add Point</button>
240
- <button onclick="increment">Count: {count}</button>
241
- </div>
163
+ // Lazy loading strategies only
164
+ import { lazyOnVisible, lazyOnIdle } from "ladrillosjs/lazy";
242
165
 
243
- <script>
244
- // Initial state - automatically tracked
245
- let score = 0;
246
- let count = 0;
247
- let user = {
248
- name: "Player 1",
249
- };
250
-
251
- const updateScore = () => {
252
- // Direct assignment triggers re-render automatically
253
- score++;
254
- };
255
-
256
- const increment = () => {
257
- // All these automatically trigger re-renders in v2.0
258
- count++; // Increment
259
- count += 5; // Compound assignment
260
- count = count * 2; // Direct assignment
261
- };
262
-
263
- // You can also use the explicit setState method
264
- const updateUser = () => {
265
- $setState({ user: { name: "Jane", age: 30 } });
266
- };
267
- </script>
166
+ // Event bus only
167
+ import { $emit, $listen } from "ladrillosjs/events";
268
168
  ```
269
169
 
270
- **New in v2.0:**
170
+ ---
271
171
 
272
- - **Automatic Reactivity**: `count++`, `count += 5`, and direct assignments automatically trigger re-renders
273
- - **`$state`**: Direct access to component state within scripts
274
- - **`$setState(updates)`**: Explicit state updates (merges with existing state)
172
+ ## 📖 Core Concepts
275
173
 
276
- ### Event Handling
174
+ ### Template Bindings
277
175
 
278
- Multiple ways to handle events:
176
+ Use `{expression}` to display reactive data. Any JavaScript expression works:
279
177
 
280
178
  ```html
281
- <!-- Method reference -->
282
- <button onclick="handleClick">Click me: {count}</button>
283
-
284
- <!-- Function with arguments -->
285
- <button onclick="addItem('Hello', 123)">Add Item</button>
179
+ <h1>{title}</h1>
180
+ <p>Hello, {user.name}!</p>
181
+ <span>Total: {items.length} items</span>
182
+ <p>Is adult: {age >= 18 ? 'Yes' : 'No'}</p>
183
+ ```
286
184
 
287
- <!-- Inline arrow function -->
288
- <button onclick="(e) => console.log(e.target)">Log Target</button>
185
+ ### Reactive State
289
186
 
290
- <!-- Component communication with event bus (v2.0) -->
291
- <button onclick="notifyOthers">Emit Event</button>
187
+ Just declare variables with `let` changes automatically update the DOM:
292
188
 
189
+ ```html
293
190
  <script>
294
191
  let count = 0;
295
- let items = [];
296
-
297
- const handleClick = (event) => {
298
- console.log("Clicked!", event);
299
- count++;
300
- };
301
-
302
- const addItem = (name, value) => {
303
- items = [...items, { name, value }];
304
- };
305
-
306
- // v2.0: Use $emit to send events to other components
307
- const notifyOthers = () => {
308
- $emit("item-added", { count, timestamp: Date.now() });
309
- };
310
-
311
- // v2.0: Listen for events from other components
312
- $listen("user-logged-in", (data) => {
313
- console.log("User logged in:", data);
314
- count = 0; // Reset count
315
- });
192
+ let user = { name: "Alice", role: "Developer" };
193
+ let items = ["Apple", "Banana", "Cherry"];
316
194
  </script>
317
195
  ```
318
196
 
319
- **New in v2.0:**
320
-
321
- - **`$emit(eventName, data)`**: Send events to other components via global event bus
322
- - **`$listen(eventName, callback)`**: Listen for events from any component
323
- - **Automatic Cleanup**: Event listeners are automatically removed when component is disconnected
197
+ ### Event Handlers
324
198
 
325
- ### Data Binding
326
-
327
- LadrillosJS supports both one-way and two-way data binding:
328
-
329
- #### One-Way Binding (Template Interpolation)
199
+ Attach events directly in HTML with inline expressions or function calls:
330
200
 
331
201
  ```html
332
- <div>
333
- <h1>{title}</h1>
334
- <p>{user.name} - {user.email}</p>
335
- <span>Items: {items.length}</span>
336
- </div>
337
-
338
- <script>
339
- let title = "My App";
340
- let user = { name: "John", email: "john@example.com" };
341
- let items = [1, 2, 3];
342
- </script>
202
+ <button onclick="count++">Increment</button>
203
+ <button onclick="handleClick()">Click me</button>
204
+ <input onkeyup="search(event.target.value)" />
205
+ <form onsubmit="handleSubmit(event)">...</form>
343
206
  ```
344
207
 
345
- #### Two-Way Binding (v2.0 Enhanced)
208
+ ### Two-Way Binding
346
209
 
347
- Use the `$bind` prefix to create automatic two-way bindings with form inputs:
210
+ Use `$bind` to sync form inputs with state:
348
211
 
349
212
  ```html
350
- <div>
351
- <h2>Hello, {$name}!</h2>
352
- <input type="text" $bind="name" placeholder="Enter your name" />
353
-
354
- <p>Email: {$email}</p>
355
- <input type="email" $bind="email" />
213
+ <input type="text" $bind="username" placeholder="Enter name" />
214
+ <p>Hello, {username}!</p>
356
215
 
357
- <p>Bio: {$bio}</p>
358
- <textarea $bind="bio"></textarea>
216
+ <textarea $bind="bio"></textarea>
359
217
 
360
- <p>Country: {$country}</p>
361
- <select $bind="country">
362
- <option value="us">United States</option>
363
- <option value="uk">United Kingdom</option>
364
- <option value="ca">Canada</option>
365
- </select>
366
- </div>
218
+ <select $bind="country">
219
+ <option value="us">United States</option>
220
+ <option value="uk">United Kingdom</option>
221
+ </select>
367
222
 
368
223
  <script>
369
- // Variables with $bind are automatically synced with inputs
370
- let $name = "World";
371
- let $email = "";
372
- let $bio = "";
373
- let $country = "us";
224
+ let username = "";
225
+ let bio = "";
226
+ let country = "us";
374
227
  </script>
375
228
  ```
376
229
 
377
- **New in v2.0:**
230
+ ---
378
231
 
379
- - **`$bind` attribute**: Simplified two-way binding syntax
380
- - **Automatic State Sync**: Input changes automatically update component state
381
- - **All Input Types**: Works with text inputs, textareas, selects, checkboxes, and radio buttons
382
- - **Nested Paths**: Supports nested object bindings like `$bind="user.email"`
232
+ ## 🧩 Built-in Elements & Directives
383
233
 
384
234
  ### Conditional Rendering
385
235
 
386
- Control element visibility with conditional directives:
236
+ Use `<if>`, `<else-if>`, and `<else>` to conditionally render elements:
387
237
 
388
238
  ```html
389
- <div>
390
- <h1>Shopping Cart ({items.length} items)</h1>
391
-
392
- <div data-if="items.length === 0">
393
- <p>Your cart is empty</p>
394
- </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>
395
242
 
396
- <div data-else-if="items.length < 3">
397
- <p>You have a few items</p>
398
- </div>
243
+ <script>
244
+ let status = "loading";
245
+ </script>
246
+ ```
399
247
 
400
- <div data-else>
401
- <p>You have many items!</p>
402
- </div>
248
+ ### Show/Hide (CSS Toggle)
403
249
 
404
- <button data-if="!isLoggedIn" onclick="login">Login</button>
405
- <button data-else onclick="logout">Logout</button>
406
- </div>
250
+ Use `<show>` to toggle visibility without removing from DOM (uses `display: none`):
407
251
 
408
- <script>
409
- let items = ["apple", "banana"];
410
- let isLoggedIn = false;
252
+ ```html
253
+ <show condition="isVisible">I can be shown or hidden</show>
411
254
 
412
- const login = () => {
413
- isLoggedIn = true;
414
- };
255
+ <button onclick="isVisible = !isVisible">Toggle</button>
415
256
 
416
- const logout = () => {
417
- isLoggedIn = false;
418
- };
257
+ <script>
258
+ let isVisible = true;
419
259
  </script>
420
260
  ```
421
261
 
422
- **Conditional Directives:**
262
+ > **`<show>` vs `<if>`:** `<show>` toggles CSS display (children stay in DOM), `<if>` adds/removes children entirely.
423
263
 
424
- - **`data-if="expression"`**: Show element if expression is truthy
425
- - **`data-else-if="expression"`**: Chain multiple conditions
426
- - **`data-else`**: Fallback when all previous conditions are false
264
+ ### List Rendering
427
265
 
428
- ### Slots
429
-
430
- Content projection using slots:
266
+ Use `<for>` to render lists with optional index and key:
431
267
 
432
268
  ```html
433
- <!-- card.html -->
434
- <div class="card">
435
- <div class="card-header">
436
- <slot name="header">Default Header</slot>
437
- </div>
438
- <div class="card-body">
439
- <slot></slot>
440
- <!-- Default slot -->
441
- </div>
442
- <div class="card-footer">
443
- <slot name="footer"></slot>
269
+ <!-- Simple list -->
270
+ <ul>
271
+ <for each="fruit in fruits">
272
+ <li>🍎 {fruit}</li>
273
+ </for>
274
+ </ul>
275
+
276
+ <!-- With index -->
277
+ <for each="(item, index) in items">
278
+ <div>#{index + 1}: {item}</div>
279
+ </for>
280
+
281
+ <!-- Object array with key -->
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>
444
287
  </div>
445
- </div>
288
+ </for>
446
289
 
447
- <!-- Usage -->
448
- <my-card>
449
- <h2 slot="header">User Profile</h2>
450
- <p>This goes in the default slot</p>
451
- <button slot="footer">Save</button>
452
- </my-card>
290
+ <script>
291
+ let fruits = ["Apple", "Banana", "Cherry"];
292
+ let items = ["First", "Second", "Third"];
293
+ let users = [
294
+ { id: 1, name: "Alice", role: "Developer", avatar: "👩‍💻" },
295
+ { id: 2, name: "Bob", role: "Designer", avatar: "👨‍🎨" },
296
+ ];
297
+ </script>
453
298
  ```
454
299
 
455
- ## Advanced Features
300
+ ### Lazy Loading
456
301
 
457
- ### External Scripts
458
-
459
- Load external JavaScript with components and bind them to the component context:
302
+ Use `<lazy>` to defer rendering until a trigger fires (viewport, idle, delay, interaction, media):
460
303
 
461
304
  ```html
462
- <!-- With 'bind' attribute for component context -->
463
- <script src="./helpers.js" bind></script>
464
-
465
- <!-- ES modules with bind -->
466
- <script src="./component-logic.js" type="module" bind></script>
305
+ <lazy margin="100px">
306
+ <heavy-chart></heavy-chart>
307
+ </lazy>
467
308
 
468
- <!-- Regular external script (global scope) -->
469
- <script src="https://cdn.example.com/library.js"></script>
309
+ <lazy interaction="click,focus">
310
+ <support-chat></support-chat>
311
+ </lazy>
470
312
  ```
471
313
 
472
- For modules with `bind`, export a default function that receives the component context:
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" />` |
473
328
 
474
- ```javascript
475
- // component-logic.js
476
- export default function () {
477
- // 'this' refers to the component instance
478
- // Access component utilities
479
- const { $state, $setState, $emit, $listen } = this;
480
-
481
- this.formatDate = (date) => {
482
- return new Intl.DateTimeFormat("en-US").format(date);
483
- };
484
-
485
- this.loadData = async () => {
486
- const response = await fetch("/api/data");
487
- const data = await response.json();
488
- $setState({ data });
489
- };
490
-
491
- // Listen for events from other components
492
- $listen("refresh-data", () => {
493
- this.loadData();
494
- });
329
+ ---
495
330
 
496
- // Called automatically if defined
497
- if (this.init) {
498
- this.init();
499
- }
500
- }
501
- ```
331
+ ## 📡 Event Bus
502
332
 
503
- **New in v2.0:**
333
+ Communicate between components using `$emit` and `$listen`:
504
334
 
505
- - **Better Context Binding**: External scripts get full access to component utilities
506
- - **`$emit` and `$listen`**: Available in external scripts for event communication
507
- - **Automatic Initialization**: Functions are auto-attached to component context
335
+ ### Sender Component
508
336
 
509
- ### Global Event Bus
337
+ ```html
338
+ <button onclick="sendMessage()">Send Message</button>
510
339
 
511
- **New in v2.0:** The global event bus enables cross-component communication without prop drilling or shared state.
340
+ <script>
341
+ let message = "Hello from sender!";
512
342
 
513
- ```javascript
514
- // In any component script
515
- // Emit an event
516
- $emit("user-logged-in", { userId: 123, username: "john" });
517
-
518
- // Listen for events
519
- $listen("user-logged-in", (data) => {
520
- console.log(`User ${data.username} logged in`);
521
- // Update local state
522
- isLoggedIn = true;
523
- currentUser = data;
524
- });
343
+ function sendMessage() {
344
+ $emit("my-event", { text: message, time: new Date().toLocaleTimeString() });
345
+ }
346
+ </script>
525
347
  ```
526
348
 
527
- #### Example: Header & Login Components
349
+ ### Receiver Component
528
350
 
529
351
  ```html
530
- <!-- header.html -->
531
- <header>
532
- <span data-if="isLoggedIn">Welcome, {username}!</span>
533
- <button data-else onclick="showLogin">Login</button>
534
- </header>
352
+ <div>
353
+ <p>Received: {receivedMessage}</p>
354
+ </div>
535
355
 
536
356
  <script>
537
- let isLoggedIn = false;
538
- let username = "";
357
+ let receivedMessage = "Waiting...";
539
358
 
540
- // Listen for login event from other components
541
- $listen("user-logged-in", (user) => {
542
- isLoggedIn = true;
543
- username = user.username;
359
+ $listen("my-event", (data) => {
360
+ receivedMessage = data.text;
544
361
  });
545
-
546
- const showLogin = () => {
547
- $emit("show-login-modal");
548
- };
549
362
  </script>
550
363
  ```
551
364
 
552
- ```html
553
- <!-- login-form.html -->
554
- <form onsubmit="handleLogin">
555
- <input type="text" $bind="username" placeholder="Username" />
556
- <input type="password" $bind="password" placeholder="Password" />
557
- <button type="submit">Login</button>
558
- </form>
365
+ ---
559
366
 
560
- <script>
561
- let $username = "";
562
- let $password = "";
563
-
564
- const handleLogin = (e) => {
565
- e.preventDefault();
566
-
567
- // Emit login success event
568
- $emit("user-logged-in", {
569
- userId: 123,
570
- username: $username,
571
- });
572
-
573
- // Clear form
574
- $username = "";
575
- $password = "";
576
- };
577
- </script>
578
- ```
367
+ ## 🏷️ Element References
579
368
 
580
- **Event Bus Benefits:**
369
+ Use `$ref` to get direct DOM access for advanced manipulation:
581
370
 
582
- - **No Prop Drilling**: Components can communicate directly
583
- - **Decoupled Architecture**: Components don't need to know about each other
584
- - **Automatic Cleanup**: Listeners are removed when components disconnect
585
- - **Promise Support**: `$emit` returns a promise when listeners are async
371
+ ```html
372
+ <input type="text" $ref="inputEl" placeholder="Click button to focus" />
373
+ <button onclick="focusInput()">Focus Input</button>
586
374
 
587
- ### Removed: Global State Stores
375
+ <canvas $ref="canvas" width="200" height="100"></canvas>
376
+ <button onclick="draw()">Draw on Canvas</button>
588
377
 
589
- **Breaking Change in v2.0:** The `createStore` API has been removed in favor of the more powerful global event bus pattern. Instead of shared stores, use the event bus for cross-component communication:
378
+ <script>
379
+ function focusInput() {
380
+ $refs.inputEl.focus();
381
+ $refs.inputEl.select();
382
+ }
590
383
 
591
- **Before (v1.x with stores):**
384
+ function draw() {
385
+ const ctx = $refs.canvas.getContext("2d");
386
+ ctx.fillStyle = "blue";
387
+ ctx.fillRect(10, 10, 100, 50);
388
+ }
389
+ </script>
390
+ ```
592
391
 
593
- ```javascript
594
- import { createStore } from "ladrillosjs";
392
+ ---
595
393
 
596
- export const userStore = createStore({
597
- user: null,
598
- isAuthenticated: false,
599
- });
394
+ ## Lazy Loading
600
395
 
601
- userStore.subscribe((state) => {
602
- this.setState(state);
603
- });
604
- ```
396
+ Load components only when needed to improve initial page load:
605
397
 
606
- **After (v2.0 with event bus):**
398
+ ### Lazy Loading Strategies
607
399
 
608
400
  ```javascript
609
- // Emit events to notify components of changes
610
- $emit("user-updated", { user: userData, isAuthenticated: true });
611
-
612
- // Listen for changes in components that need them
613
- $listen("user-updated", ({ user, isAuthenticated }) => {
614
- // Update local component state
615
- currentUser = user;
616
- loggedIn = isAuthenticated;
617
- });
401
+ import {
402
+ registerComponents,
403
+ lazyOnVisible,
404
+ lazyOnIdle,
405
+ lazyOnInteraction,
406
+ lazyOnMedia,
407
+ lazyOnDelay,
408
+ } from "ladrillosjs";
409
+
410
+ await registerComponents([
411
+ // Load when visible in viewport
412
+ {
413
+ name: "lazy-footer",
414
+ path: "./footer.html",
415
+ lazy: lazyOnVisible({ rootMargin: "100px" }),
416
+ },
417
+
418
+ // Load when browser is idle
419
+ {
420
+ name: "analytics-widget",
421
+ path: "./analytics.html",
422
+ lazy: lazyOnIdle(5000), // timeout: 5s max wait
423
+ },
424
+
425
+ // Load on user interaction
426
+ {
427
+ name: "modal-dialog",
428
+ path: "./modal.html",
429
+ lazy: lazyOnInteraction(["click", "focusin"]),
430
+ },
431
+
432
+ // Load based on media query
433
+ {
434
+ name: "mobile-nav",
435
+ path: "./mobile-nav.html",
436
+ lazy: lazyOnMedia("(max-width: 768px)"),
437
+ },
438
+
439
+ // Load after delay
440
+ {
441
+ name: "chat-widget",
442
+ path: "./chat.html",
443
+ lazy: lazyOnDelay(3000), // 3 second delay
444
+ },
445
+ ]);
618
446
  ```
619
447
 
620
- ### Shadow DOM
448
+ | Strategy | Use Case |
449
+ | ------------------- | -------------------------------------------- |
450
+ | `lazyOnVisible` | Below-fold content, footers, image galleries |
451
+ | `lazyOnIdle` | Non-critical features, analytics |
452
+ | `lazyOnInteraction` | Modals, dropdowns, tooltips |
453
+ | `lazyOnMedia` | Mobile/desktop specific components |
454
+ | `lazyOnDelay` | Chat widgets, notifications |
621
455
 
622
- Components use Shadow DOM by default for style encapsulation. To disable:
456
+ ### Eager Override
623
457
 
624
- ```javascript
625
- // Disable Shadow DOM for a component
626
- await registerComponent("my-component", "./my-component.html", false);
458
+ Force a lazy component to load immediately by adding the `eager` attribute:
627
459
 
628
- // With Shadow DOM enabled (default)
629
- await registerComponent("isolated-widget", "./widget.html", true);
460
+ ```html
461
+ <lazy-footer eager></lazy-footer>
630
462
  ```
631
463
 
632
- **Shadow DOM Benefits:**
633
-
634
- - **Style Isolation**: Component styles don't leak to global scope
635
- - **Encapsulation**: Internal DOM structure is hidden from parent
636
- - **Cleaner DOM**: Styles and scripts are scoped to component
637
-
638
- **When to Disable:**
464
+ ---
639
465
 
640
- - Need global CSS styles to apply
641
- - Using third-party CSS frameworks
642
- - Debugging with browser dev tools (easier without shadow DOM)
466
+ ## 📋 API Reference
643
467
 
644
- ### Performance & Caching
468
+ ### `registerComponent`
645
469
 
646
- **New in v2.0:** LRU (Least Recently Used) caching for improved performance:
470
+ ```javascript
471
+ registerComponent(name, path, useShadowDOM?, lazy?)
472
+ ```
647
473
 
648
- #### Component Caching
474
+ | Parameter | Type | Default | Description |
475
+ | -------------- | ----------------------- | -------- | ------------------------------- |
476
+ | `name` | string | required | Tag name (must include hyphen) |
477
+ | `path` | string | required | Path to `.html` component file |
478
+ | `useShadowDOM` | boolean | `true` | Enable Shadow DOM encapsulation |
479
+ | `lazy` | boolean \| LazyStrategy | `false` | Lazy loading configuration |
649
480
 
650
- - **25 Components**: Automatically caches up to 25 component HTML files
651
- - **LRU Eviction**: Least recently used components are removed when cache is full
652
- - **Faster Re-renders**: Cached components load instantly on re-use
481
+ ```javascript
482
+ // Basic usage
483
+ registerComponent("my-button", "./button.html");
653
484
 
654
- #### Function Caching
485
+ // Without Shadow DOM (for global CSS)
486
+ registerComponent("my-nav", "./nav.html", false);
655
487
 
656
- - **100 Functions**: Caches up to 100 compiled template expressions
657
- - **Prevents Memory Leaks**: Reuses Function objects for identical expressions
658
- - **Example**: `{formatName("John")}` compiles once and is reused on every render
488
+ // With lazy loading
489
+ registerComponent("my-footer", "./footer.html", true, lazyOnVisible());
490
+ ```
659
491
 
660
- **Performance Improvements:**
492
+ ### `registerComponents`
661
493
 
662
- - Reduced HTTP requests for components
663
- - Faster template rendering with cached functions
664
- - Optimized state updates with change detection
665
- - Efficient re-rendering with minimal DOM updates
494
+ Register multiple components with parallel fetching:
666
495
 
667
- ## API Reference
496
+ ```javascript
497
+ const result = await registerComponents([
498
+ { name: "app-header", path: "./header.html" },
499
+ { name: "app-footer", path: "./footer.html", lazy: lazyOnVisible() },
500
+ { name: "user-card", path: "./user-card.html", useShadowDOM: false },
501
+ ]);
668
502
 
669
- ### Component Methods
503
+ // Returns: { success: [...], failed: [...], skipped: [...] }
504
+ ```
670
505
 
671
- | Method | Description |
672
- | ------------------- | ------------------------------------------------------------------------- |
673
- | `setState(partial)` | Update component state and trigger re-render (merges with existing state) |
506
+ ### `$use`
674
507
 
675
- ### Framework Utilities (v2.0)
508
+ Infer the component tag name from the file path:
676
509
 
677
- Available within component `<script>` tags:
510
+ ```javascript
511
+ await $use("./components/user-card.html"); // Registers as <user-card>
512
+ ```
678
513
 
679
- | Utility | Description |
680
- | ------------------------------ | -------------------------------------------------------------------- |
681
- | `$state` | Direct access to component state object |
682
- | `$setState(updates)` | Update state explicitly (alternative to direct assignments) |
683
- | `$emit(eventName, data?)` | Emit event to other components via global event bus |
684
- | `$listen(eventName, callback)` | Listen for events from other components (auto-cleanup on disconnect) |
685
- | `$querySelector(selector)` | Query element within component's DOM (respects Shadow DOM) |
686
- | `$querySelectorAll(selector)` | Query all matching elements within component |
514
+ ### `loadLazyComponent`
687
515
 
688
- ### Component Attributes
516
+ Force a lazy component to load immediately from JavaScript:
689
517
 
690
- | Attribute | Description |
691
- | --------------------------- | ----------------------------------------------------------------------------------- |
692
- | `$bind="variableName"` | Create two-way data binding with form inputs |
693
- | `data-if="expression"` | Conditionally render element if expression is truthy |
694
- | `data-else-if="expression"` | Chain multiple conditional expressions |
695
- | `data-else` | Render when all previous conditions are false |
696
- | `onclick="handler"` (etc.) | Attach event handlers (supports method names, inline functions, or arrow functions) |
518
+ ```javascript
519
+ import { loadLazyComponent } from "ladrillosjs";
697
520
 
698
- ### Registration Functions
521
+ await loadLazyComponent("my-lazy-footer");
522
+ ```
699
523
 
700
- | Function | Description |
701
- | ---------------------------------------------- | ------------------------------------------------------ |
702
- | `registerComponent(name, path, useShadowDOM?)` | Register a single component (returns Promise) |
703
- | `registerComponents(components)` | **Coming soon** - Register multiple components at once |
524
+ ### `configure`
704
525
 
705
- ## Examples
526
+ Configure framework-level options (optional):
706
527
 
707
- ### Component Communication
528
+ ```javascript
529
+ import { configure } from "ladrillosjs";
708
530
 
709
- **v2.0 uses the global event bus instead of custom events:**
531
+ configure({
532
+ cacheSize: 50, // Component LRU cache size (default: 25)
533
+ onError: (err) => telemetry.capture(err), // Custom error handler
534
+ });
535
+ ```
710
536
 
711
- ```html
712
- <!-- parent.html -->
713
- <div>
714
- <h2>Parent Component</h2>
715
- <p>Messages received: {messageCount}</p>
716
- <child-component></child-component>
717
- </div>
537
+ ### Event Bus
718
538
 
719
- <script>
720
- let messageCount = 0;
539
+ | Function | Description |
540
+ | -------------------------- | ----------------------------------- |
541
+ | `$emit(event, data)` | Broadcast an event to all listeners |
542
+ | `$listen(event, callback)` | Subscribe to an event |
721
543
 
722
- // Listen for events from child
723
- $listen("child-message", (data) => {
724
- console.log("Received from child:", data);
725
- messageCount++;
726
- });
727
- </script>
728
- ```
544
+ ---
729
545
 
730
- ```html
731
- <!-- child.html -->
732
- <div>
733
- <h3>Child Component</h3>
734
- <button onclick="sendMessage">Send Message to Parent</button>
735
- </div>
546
+ ## 🛠️ Using with Vite
736
547
 
737
- <script>
738
- let count = 0;
548
+ LadrillosJS works seamlessly with Vite for production builds:
739
549
 
740
- const sendMessage = () => {
741
- count++;
742
- // Emit event that parent (or any component) can listen to
743
- $emit("child-message", {
744
- message: `Hello from child! (${count})`,
745
- timestamp: Date.now(),
746
- });
747
- };
748
- </script>
550
+ ```bash
551
+ npm install ladrillosjs vite
749
552
  ```
750
553
 
751
- ### Dynamic Component Creation
752
-
753
554
  ```javascript
754
- // Create components programmatically
755
- const createCard = (userData) => {
756
- const card = document.createElement("user-card");
757
- card.setAttribute("user-id", userData.id);
758
- card.setAttribute("name", userData.name);
759
- card.setAttribute("email", userData.email);
760
- document.querySelector("#user-list").appendChild(card);
761
- };
762
-
763
- // Fetch and create multiple components
764
- fetch("/api/users")
765
- .then((res) => res.json())
766
- .then((users) => users.forEach(createCard));
767
- ```
555
+ // vite.config.js
556
+ import { defineConfig } from "vite";
768
557
 
769
- ### Passing Complex Data
558
+ export default defineConfig({
559
+ // LadrillosJS components work out of the box!
560
+ });
561
+ ```
770
562
 
771
- Use JSON.stringify for passing objects/arrays as attributes:
563
+ ```javascript
564
+ // main.js
565
+ import { registerComponent } from "ladrillosjs";
772
566
 
773
- ```html
774
- <!-- In parent component -->
775
- <script>
776
- const user = { id: 1, name: "John", roles: ["admin", "user"] };
777
- const items = [1, 2, 3, 4, 5];
778
-
779
- // Create HTML with stringified data
780
- const cardHtml = `
781
- <user-card data-user='${JSON.stringify(user)}'></user-card>
782
- <list-component data-items='${JSON.stringify(items)}'></list-component>
783
- `;
784
-
785
- // Or use the built-in stringify helper in v2.0
786
- const cardHtml2 = `
787
- <user-card data-user="${this.stringify(user)}"></user-card>
788
- `;
789
- </script>
567
+ registerComponent("my-counter", "./components/counter.html", false);
790
568
  ```
791
569
 
792
- ```html
793
- <!-- In child component (user-card.html) -->
794
- <div>
795
- <h3>{user.name}</h3>
796
- <p>ID: {user.id}</p>
797
- <p>Roles: {user.roles.join(", ")}</p>
798
- </div>
570
+ See the [samples/](samples/) directory for complete examples:
799
571
 
800
- <script>
801
- // data-user is automatically parsed from JSON
802
- let user = this.state["data-user"];
803
- </script>
804
- ```
572
+ - `samples/vite-sample/` — Basic Vite setup
573
+ - `samples/vite-basic-site/` Multi-component site
574
+ - `samples/ladrillos-demo/` Full feature showcase
805
575
 
806
- ## Migration Guide (v1.x to v2.0)
576
+ ---
807
577
 
808
- ### Breaking Changes
578
+ ## 📚 Examples
809
579
 
810
- 1. **Global State Stores Removed**
580
+ ### Todo List
811
581
 
812
- - **Before:** `createStore()` API
813
- - **After:** Use global event bus with `$emit` and `$listen`
582
+ A complete CRUD example combining all directives:
814
583
 
815
- 2. **Component Registration**
584
+ ```html
585
+ <div class="todo-app">
586
+ <form onsubmit="addTodo(event)">
587
+ <input type="text" $bind="newTodo" placeholder="What needs to be done?" />
588
+ <button type="submit">Add</button>
589
+ </form>
590
+
591
+ <ul>
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>
599
+ </ul>
600
+
601
+ <if condition="todos.length === 0"><p>No todos yet!</p></if>
602
+ </div>
816
603
 
817
- - **Before:** `registerComponent()` was synchronous
818
- - **After:** Returns a Promise, use `await` or `.then()`
604
+ <script>
605
+ let todos = [
606
+ { id: 1, text: "Learn LadrillosJS", completed: false },
607
+ { id: 2, text: "Build something awesome", completed: false },
608
+ ];
609
+ let newTodo = "";
610
+ let nextId = 3;
611
+
612
+ function addTodo(event) {
613
+ event.preventDefault();
614
+ if (newTodo.trim()) {
615
+ todos = [...todos, { id: nextId++, text: newTodo, completed: false }];
616
+ newTodo = "";
617
+ }
618
+ }
819
619
 
820
- 3. **Framework Utilities**
620
+ function toggleTodo(id) {
621
+ todos = todos.map((t) =>
622
+ t.id === id ? { ...t, completed: !t.completed } : t,
623
+ );
624
+ }
821
625
 
822
- - **Before:** `this.emit()`, `this.listen()`, `this.setState()`
823
- - **After:** Use `$emit()`, `$listen()`, `$setState()` in scripts (legacy methods still available for compatibility)
626
+ function removeTodo(id) {
627
+ todos = todos.filter((t) => t.id !== id);
628
+ }
629
+ </script>
630
+ ```
824
631
 
825
- 4. **Two-Way Binding**
826
- - **Before:** No built-in support (manual implementation)
827
- - **After:** Use `$bind` attribute for automatic two-way binding
632
+ > 💡 Check the [samples/](samples/) folder for more examples including lazy loading, event bus communication, and real-world patterns.
828
633
 
829
- ### New Features to Adopt
634
+ ---
830
635
 
831
- - Use `$bind` for two-way data binding instead of manual input handling
832
- - ✅ Replace store subscriptions with `$emit`/`$listen` event patterns
833
- - ✅ Direct variable assignments now trigger reactivity (`count++`)
834
- - ✅ Use `$state` for direct state access in scripts
835
- - ✅ Leverage conditional directives: `data-if`, `data-else-if`, `data-else`
636
+ ## 🤝 Contributing
836
637
 
837
- ## Contributing
638
+ Contributions are welcome! Here's how you can help:
838
639
 
839
- Contributions are welcome! Please feel free to submit a Pull Request.
640
+ 1. **Fork** the repository
641
+ 2. **Create** a feature branch: `git checkout -b feature/amazing-feature`
642
+ 3. **Commit** your changes: `git commit -m 'Add amazing feature'`
643
+ 4. **Push** to the branch: `git push origin feature/amazing-feature`
644
+ 5. **Open** a Pull Request
840
645
 
841
- ### Development
646
+ ### Development Setup
842
647
 
843
648
  ```bash
844
- # Install dependencies
649
+ git clone https://github.com/drubiodev/LadrillosJS.git
650
+ cd LadrillosJS
845
651
  npm install
846
-
847
- # Run development server with hot reload
848
- npm run dev
849
-
850
- # Run tests
851
- npm test
852
-
853
- # Run tests with coverage
854
- npm run test:coverage
855
-
856
- # Build the library
857
- npm run build
858
-
859
- # Build TypeScript types
860
- npm run build:types
652
+ npm run dev # Watch mode for development
653
+ npm run build # Build for production
861
654
  ```
862
655
 
863
- ### Project Structure
864
-
865
- ```
866
- src/
867
- ├── index.ts # Main entry point
868
- ├── core/
869
- │ ├── main.ts # Core Ladrillos class
870
- │ ├── webcomponent.ts # Web component definition
871
- │ ├── componentParser.ts # Component file parser
872
- │ ├── componentSource.ts # Component fetching with cache
873
- │ ├── eventBus.ts # Global event bus
874
- │ ├── css/
875
- │ │ └── cssParser.ts
876
- │ ├── html/
877
- │ │ ├── htmlparser.ts
878
- │ │ └── htmlRenderer.ts
879
- │ └── js/
880
- │ └── scriptParser.ts
881
- ├── cache/
882
- │ ├── index.ts # LRU cache for components
883
- │ └── functionCache.ts # LRU cache for compiled functions
884
- ├── types/
885
- │ └── LadrilloTypes.ts # TypeScript type definitions
886
- └── utils/
887
- ├── logger.ts # Logging utilities
888
- └── regex.ts # Regex patterns
889
- ```
656
+ ---
890
657
 
891
- ## License
658
+ ## 📄 License
892
659
 
893
- MIT License - see [LICENSE](LICENSE) file for details.
660
+ MIT License see [LICENSE](LICENSE) for details.
894
661
 
895
662
  ---
896
663
 
897
- **LadrillosJS v2.0** - Built with ❤️ by [Daniel Rubio](https://github.com/drubiodev)
664
+ <p align="center">
665
+ <strong>Built with ❤️ by <a href="https://github.com/drubiodev">Daniel Rubio</a></strong>
666
+ </p>
898
667
 
899
- Rewritten in TypeScript for better performance, developer experience, and maintainability.
668
+ <p align="center">
669
+ <a href="https://github.com/drubiodev/LadrillosJS">GitHub</a> •
670
+ <a href="https://www.npmjs.com/package/ladrillosjs">NPM</a> •
671
+ <a href="https://github.com/drubiodev/LadrillosJS/issues">Issues</a>
672
+ </p>