ladrillosjs 2.0.0-beta.1 → 2.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,116 +1,57 @@
1
1
  # LadrillosJS
2
2
 
3
- <img src="https://raw.githubusercontent.com/drubiodev/LadrillosJS/refs/heads/main/LadrillosJS.png" alt="LadrillosJS" width="400"/>
3
+ <img src="https://raw.githubusercontent.com/drubiodev/LadrillosJS/refs/heads/main/LadrillosJS.jpg" alt="LadrillosJS" width="400"/>
4
4
 
5
5
  A lightweight, zero-dependency web component framework for building modular web applications.
6
6
 
7
- **Version 2.0** - Now rewritten in TypeScript with enhanced performance, better developer experience, and powerful new features.
7
+ **Version 2.0** - Rewritten in TypeScript with enhanced performance, improved developer experience, and powerful new features.
8
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."
9
+ > "Empower developers to componentize code efficiently without the complexity of a full-scale framework. Focus on simplicity while leveraging core web fundamentals."
10
10
 
11
11
  ## Table of Contents
12
12
 
13
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
14
  - [Installation](#installation)
19
- - [Your First Component](#your-first-component)
15
+ - [Quick Start](#quick-start)
20
16
  - [Core Concepts](#core-concepts)
21
17
  - [Component Registration](#component-registration)
22
18
  - [State Management](#state-management)
23
19
  - [Event Handling](#event-handling)
24
20
  - [Data Binding](#data-binding)
25
21
  - [Conditional Rendering](#conditional-rendering)
22
+ - [List Rendering](#list-rendering)
26
23
  - [Slots](#slots)
24
+ - [Component Props](#component-props)
27
25
  - [Advanced Features](#advanced-features)
28
- - [External Scripts](#external-scripts)
29
26
  - [Global Event Bus](#global-event-bus)
27
+ - [External Scripts](#external-scripts)
30
28
  - [Shadow DOM](#shadow-dom)
29
+ - [Styling Components](#styling-components)
31
30
  - [Performance & Caching](#performance--caching)
31
+ - [Common Patterns](#common-patterns)
32
+ - [Keyboard Events](#keyboard-events)
33
+ - [Form Validation](#form-validation)
34
+ - [Loading States](#loading-states)
32
35
  - [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)
36
+ - [Development](#development)
37
+ - [Attribution](#attribution)
39
38
  - [License](#license)
40
39
 
41
40
  ## Features
42
41
 
43
42
  - 🚀 **Zero Dependencies** - Pure JavaScript, no build tools required
44
43
  - 📦 **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`
44
+ - ⚡ **Reactive State** - Automatic re-rendering on state changes
45
+ - 🎯 **Event System** - Global event bus for component communication
46
+ - 🔄 **Two-Way Data Binding** - Seamless form input binding with `$bind`
48
47
  - 🎨 **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:
93
-
94
- ```bash
95
- # Clone the repository
96
- git clone https://github.com/drubiodev/LadrillosJS.git
97
- cd LadrillosJS
98
-
99
- # Install dependencies
100
- npm install
101
-
102
- # Start the development server (Vite)
103
- npm run dev
104
-
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
- ```
48
+ - 🔌 **Slots** - Content projection with named and default slots
49
+ - 📝 **TypeScript** - Full type definitions and TypeScript source code
50
+ - 🎭 **Conditional Rendering** - `$if`, `$else-if`, and `$else` directives
51
+ - **List Rendering** - `$for` directive for rendering arrays
52
+ - �🚄 **Smart Caching** - LRU cache for components and compiled functions
53
+ - 🔧 **Framework Utilities** - Helper functions for common tasks
54
+ - 🧩 **External Scripts** - Load and bind external JavaScript modules
114
55
 
115
56
  ## Installation
116
57
 
@@ -123,45 +64,42 @@ npm install ladrillosjs
123
64
  ### CDN
124
65
 
125
66
  ```html
126
- <!-- Latest version -->
67
+ <!-- ES Module (Recommended) -->
127
68
  <script type="module">
128
69
  import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
129
70
  registerComponent("my-component", "./my-component.html");
130
71
  </script>
131
72
 
132
- <!-- UMD (for legacy browsers) -->
73
+ <!-- UMD (Browser Global) -->
133
74
  <script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
134
75
  <script>
135
- // Access via global ladrillosjs object
136
76
  ladrillosjs.registerComponent("my-component", "./my-component.html");
137
77
  </script>
138
78
  ```
139
79
 
140
- ## Your First Component
80
+ ## Quick Start
141
81
 
142
- A component in LadrillosJS is a reusable custom HTML element that bundles its own template, logic, and styles into a single file.
82
+ ### 1. Create Your First Component
143
83
 
144
- ### 1. Create a Component File
145
-
146
- Create `hello-world.html`:
84
+ Create a file called `hello-world.html`:
147
85
 
148
86
  ```html
149
87
  <!-- hello-world.html -->
150
88
  <div class="greeting">
151
89
  <h1>{title}</h1>
152
90
  <p>Hello, {name}!</p>
153
- <button onclick="greet">Click me ({count})</button>
91
+ <button onclick="greet()">Greet ({count})</button>
154
92
  </div>
155
93
 
156
94
  <script>
157
- // Component state
95
+ // Component state - automatically reactive
158
96
  let title = "Welcome to LadrillosJS";
159
97
  let name = "World";
160
98
  let count = 0;
161
99
 
162
100
  // Event handler
163
101
  const greet = () => {
164
- count++;
102
+ count++; // Automatically triggers re-render
165
103
  name = prompt("What's your name?") || "World";
166
104
  };
167
105
  </script>
@@ -170,14 +108,24 @@ Create `hello-world.html`:
170
108
  .greeting {
171
109
  text-align: center;
172
110
  padding: 2rem;
173
- background: #f0f0f0;
111
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
112
+ color: white;
174
113
  border-radius: 8px;
175
114
  }
176
115
 
177
116
  button {
178
- padding: 0.5rem 1rem;
117
+ padding: 0.75rem 1.5rem;
179
118
  font-size: 1rem;
119
+ background: white;
120
+ color: #667eea;
121
+ border: none;
122
+ border-radius: 4px;
180
123
  cursor: pointer;
124
+ transition: transform 0.2s;
125
+ }
126
+
127
+ button:hover {
128
+ transform: scale(1.05);
181
129
  }
182
130
  </style>
183
131
  ```
@@ -186,15 +134,17 @@ Create `hello-world.html`:
186
134
 
187
135
  ```html
188
136
  <!DOCTYPE html>
189
- <html>
137
+ <html lang="en">
190
138
  <head>
191
- <title>My App</title>
139
+ <meta charset="UTF-8" />
140
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
141
+ <title>My LadrillosJS App</title>
192
142
  </head>
193
143
  <body>
194
144
  <!-- Use your component -->
195
145
  <hello-world></hello-world>
196
146
 
197
- <!-- Register component -->
147
+ <!-- Register the component -->
198
148
  <script type="module">
199
149
  import { registerComponent } from "ladrillosjs";
200
150
  registerComponent("hello-world", "./hello-world.html");
@@ -203,231 +153,349 @@ Create `hello-world.html`:
203
153
  </html>
204
154
  ```
205
155
 
156
+ ### 3. Explore Example Apps
157
+
158
+ Check out the `samples/apps/` directory for complete examples:
159
+
160
+ - **[Todo App](samples/apps/todo)** - Classic todo list with component composition
161
+ - **[Notes App](samples/apps/notes)** - Multi-component app with event bus
162
+ - **[Simple Button](samples/apps/simple-button)** - Basic interactive component
163
+ - **[Business Card](samples/apps/biz)** - Form with two-way data binding
164
+ - **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation
165
+ - **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
166
+ - **[List Rendering](samples/apps/list-test)** - Dynamic lists with `$for` directive
167
+
168
+ Run the development server to view examples:
169
+
170
+ ```bash
171
+ npm install
172
+ npm run dev
173
+ ```
174
+
206
175
  ## Core Concepts
207
176
 
208
177
  ### Component Registration
209
178
 
210
- Register single or multiple components:
179
+ Register components to make them available as custom HTML elements:
211
180
 
212
181
  ```javascript
213
- // Single component
214
- import { registerComponent } from "ladrillosjs";
215
- await registerComponent("my-component", "./my-component.html");
182
+ import { registerComponent, registerComponents } from "ladrillosjs";
183
+
184
+ // Register a single component
185
+ await registerComponent("my-button", "./components/my-button.html");
216
186
 
217
- // Multiple components (v2.0 feature - currently in development)
218
- import { registerComponents } from "ladrillosjs";
187
+ // Register multiple components
219
188
  await registerComponents([
220
189
  { name: "app-header", path: "./components/header.html" },
221
190
  { name: "app-footer", path: "./components/footer.html" },
222
191
  { name: "user-card", path: "./components/user-card.html" },
223
192
  ]);
224
193
 
225
- // Using CDN
226
- ladrillosjs.registerComponent("my-component", "./my-component.html");
194
+ // Disable Shadow DOM for a component
195
+ await registerComponent("global-styles", "./components/global.html", false);
227
196
  ```
228
197
 
229
- **Note:** The `registerComponents` function is planned for v2.0 to enable bulk registration with concurrency control.
230
-
231
198
  ### State Management
232
199
 
233
- Components have reactive state that automatically triggers re-renders. In v2.0, variable assignments are automatically tracked and trigger reactivity:
200
+ Components have reactive state that automatically triggers re-renders when changed:
234
201
 
235
202
  ```html
236
203
  <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>
204
+ <h2>Counter: {count}</h2>
205
+ <p>User: {user.name}</p>
206
+ <button onclick="increment()">Add</button>
207
+ <button onclick="updateUser()">Change User</button>
208
+ <button onclick="reset()">Reset</button>
241
209
  </div>
242
210
 
243
211
  <script>
244
- // Initial state - automatically tracked
245
- let score = 0;
212
+ // State variables - automatically reactive
246
213
  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
- };
214
+ let user = { name: "John", age: 25 };
255
215
 
256
216
  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
217
+ count++; // Automatically triggers re-render
261
218
  };
262
219
 
263
- // You can also use the explicit setState method
264
220
  const updateUser = () => {
265
- $setState({ user: { name: "Jane", age: 30 } });
221
+ user.name = "Jane"; // Direct mutation triggers re-render
222
+ };
223
+
224
+ // You can also use $setState for explicit updates
225
+ const reset = () => {
226
+ $setState({ count: 0, user: { name: "Anonymous", age: 0 } });
266
227
  };
267
228
  </script>
268
229
  ```
269
230
 
270
- **New in v2.0:**
231
+ **Available State Utilities:**
271
232
 
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)
233
+ - Direct assignment: `count++`, `name = "New"`
234
+ - `$setState(updates)`: Merge updates into state
235
+ - `$getState()`: Access state in external modules
275
236
 
276
237
  ### Event Handling
277
238
 
278
- Multiple ways to handle events:
239
+ Attach event handlers directly to elements:
279
240
 
280
241
  ```html
281
242
  <!-- Method reference -->
282
- <button onclick="handleClick">Click me: {count}</button>
243
+ <button onclick="handleClick(event)">Click me</button>
283
244
 
284
245
  <!-- Function with arguments -->
285
- <button onclick="addItem('Hello', 123)">Add Item</button>
246
+ <button onclick="addItem('apple', 5)">Add Item</button>
286
247
 
287
248
  <!-- Inline arrow function -->
288
- <button onclick="(e) => console.log(e.target)">Log Target</button>
289
-
290
- <!-- Component communication with event bus (v2.0) -->
291
- <button onclick="notifyOthers">Emit Event</button>
249
+ <button onclick="console.log(event.target)">Log Event</button>
250
+
251
+ <!-- Multiple events -->
252
+ <label $if="isValid">Valid</label>
253
+ <input
254
+ type="text"
255
+ placeholder="Enter text (min 3 characters)"
256
+ onkeyup="validateInput(event)"
257
+ onfocus="highlightField(event)"
258
+ onblur="saveField(event)"
259
+ />
292
260
 
293
261
  <script>
294
- let count = 0;
295
262
  let items = [];
263
+ let isValid = false;
296
264
 
297
265
  const handleClick = (event) => {
298
- console.log("Clicked!", event);
299
- count++;
266
+ console.log("Button clicked!", event);
300
267
  };
301
268
 
302
- const addItem = (name, value) => {
303
- items = [...items, { name, value }];
269
+ const addItem = (name, quantity) => {
270
+ items = [...items, { name, quantity }];
271
+ console.log("Items:", items);
304
272
  };
305
273
 
306
- // v2.0: Use $emit to send events to other components
307
- const notifyOthers = () => {
308
- $emit("item-added", { count, timestamp: Date.now() });
274
+ const validateInput = (e) => {
275
+ const value = e.target.value;
276
+ isValid = value.length >= 3;
309
277
  };
310
278
 
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
- });
279
+ const highlightField = (e) => {
280
+ e.target.style.backgroundColor = "lightyellow";
281
+ };
282
+
283
+ const saveField = (e) => {
284
+ e.target.style.backgroundColor = "";
285
+ console.log("Field saved:", e.target.value);
286
+ };
316
287
  </script>
317
288
  ```
318
289
 
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
324
-
325
290
  ### Data Binding
326
291
 
327
- LadrillosJS supports both one-way and two-way data binding:
292
+ #### One-Way Binding (Display Data)
328
293
 
329
- #### One-Way Binding (Template Interpolation)
294
+ Use curly braces `{}` to display state in your template:
330
295
 
331
296
  ```html
332
297
  <div>
333
298
  <h1>{title}</h1>
334
299
  <p>{user.name} - {user.email}</p>
335
- <span>Items: {items.length}</span>
300
+ <span>Total: {items.length} items</span>
301
+ <p>Formatted: {formatPrice(price)}</p>
336
302
  </div>
337
303
 
338
304
  <script>
339
305
  let title = "My App";
340
306
  let user = { name: "John", email: "john@example.com" };
341
- let items = [1, 2, 3];
307
+ let items = ["apple", "banana", "orange"];
308
+ let price = 29.99;
309
+
310
+ const formatPrice = (value) => `$${value.toFixed(2)}`;
342
311
  </script>
343
312
  ```
344
313
 
345
- #### Two-Way Binding (v2.0 Enhanced)
314
+ #### Two-Way Binding (Form Inputs)
346
315
 
347
- Use the `$bind` prefix to create automatic two-way bindings with form inputs:
316
+ Use the `$bind` attribute for automatic synchronization between inputs and state:
348
317
 
349
318
  ```html
350
319
  <div>
351
- <h2>Hello, {$name}!</h2>
352
- <input type="text" $bind="name" placeholder="Enter your name" />
320
+ <h2>Hello, {name}!</h2>
321
+ <input type="text" $bind="name" placeholder="Your name" />
353
322
 
354
- <p>Email: {$email}</p>
323
+ <p>Email: {email}</p>
355
324
  <input type="email" $bind="email" />
356
325
 
357
- <p>Bio: {$bio}</p>
326
+ <p>Bio: {bio}</p>
358
327
  <textarea $bind="bio"></textarea>
359
328
 
360
- <p>Country: {$country}</p>
329
+ <p>Country: {country}</p>
361
330
  <select $bind="country">
362
331
  <option value="us">United States</option>
363
332
  <option value="uk">United Kingdom</option>
364
333
  <option value="ca">Canada</option>
365
334
  </select>
335
+
336
+ <label>
337
+ <input type="checkbox" $bind="subscribe" />
338
+ Subscribe to newsletter: {subscribe}
339
+ </label>
366
340
  </div>
367
341
 
368
342
  <script>
369
- // Variables with $bind are automatically synced with inputs
370
- let $name = "World";
371
- let $email = "";
372
- let $bio = "";
373
- let $country = "us";
343
+ // Variables are automatically synced with inputs
344
+ let name = "World";
345
+ let email = "";
346
+ let bio = "";
347
+ let country = "us";
348
+ let subscribe = false;
374
349
  </script>
375
350
  ```
376
351
 
377
- **New in v2.0:**
378
-
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"`
383
-
384
352
  ### Conditional Rendering
385
353
 
386
- Control element visibility with conditional directives:
354
+ Show or hide elements based on conditions:
387
355
 
388
356
  ```html
389
357
  <div>
390
- <h1>Shopping Cart ({items.length} items)</h1>
358
+ <h1>Shopping Cart</h1>
391
359
 
392
- <div data-if="items.length === 0">
393
- <p>Your cart is empty</p>
394
- </div>
360
+ <!-- Simple condition -->
361
+ <p $if="{items.length === 0}">Your cart is empty</p>
395
362
 
396
- <div data-else-if="items.length < 3">
397
- <p>You have a few items</p>
363
+ <!-- Multiple conditions -->
364
+ <div $if="{items.length > 0 && items.length < 5}">
365
+ <p>You have {items.length} items</p>
398
366
  </div>
399
367
 
400
- <div data-else>
401
- <p>You have many items!</p>
368
+ <div $else-if="{items.length >= 5}">
369
+ <p>Your cart is full! ({items.length} items)</p>
402
370
  </div>
403
371
 
404
- <button data-if="!isLoggedIn" onclick="login">Login</button>
405
- <button data-else onclick="logout">Logout</button>
372
+ <!-- Login/Logout example -->
373
+ <button $if="{!isLoggedIn}" onclick="login()">Login</button>
374
+ <button $else onclick="logout()">Logout</button>
375
+
376
+ <!-- Complex conditions -->
377
+ <div $if="{user && user.role.toLowerCase() === 'admin'}">
378
+ <p>{user.role} Panel</p>
379
+ Hello {user.name}
380
+ <button onclick="addToCart()">🛒 Add To Cart</button>
381
+ </div>
406
382
  </div>
407
383
 
408
384
  <script>
409
- let items = ["apple", "banana"];
385
+ let items = [];
410
386
  let isLoggedIn = false;
387
+ let user = null;
411
388
 
412
389
  const login = () => {
413
390
  isLoggedIn = true;
391
+ user = { name: "John", role: "Admin" };
414
392
  };
415
393
 
416
394
  const logout = () => {
417
395
  isLoggedIn = false;
396
+ user = null;
397
+ };
398
+
399
+ const addToCart = () => {
400
+ if (items.length < 5) {
401
+ items.push(`Item ${items.length + 1}`);
402
+ } else {
403
+ alert("Cart is full!");
404
+ }
418
405
  };
419
406
  </script>
420
407
  ```
421
408
 
422
409
  **Conditional Directives:**
423
410
 
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
411
+ - `$if="{expression}"`: Show if expression is truthy
412
+ - `$else-if="{expression}"`: Chain multiple conditions
413
+ - `$else`: Fallback when previous conditions are false
414
+
415
+ ### List Rendering
416
+
417
+ Render lists of items using the `$for` directive:
418
+
419
+ ```html
420
+ <div>
421
+ <h2>My Fruits</h2>
422
+ <ul>
423
+ <li $for="fruit in fruits">{fruit}</li>
424
+ </ul>
425
+ <button onclick="addFruit()">Add Fruit</button>
426
+ </div>
427
+
428
+ <script>
429
+ let fruits = ["Apple", "Banana", "Orange"];
430
+
431
+ const addFruit = () => {
432
+ const newFruit = prompt("Enter a fruit name:");
433
+ if (newFruit) {
434
+ fruits = [...fruits, newFruit]; // Triggers re-render
435
+ }
436
+ };
437
+ </script>
438
+ ```
439
+
440
+ **List Rendering with Objects:**
441
+
442
+ ```html
443
+ <div>
444
+ <h2>User List</h2>
445
+ <div class="user-card" $for="user in users">
446
+ <h3>{user.name}</h3>
447
+ <p>Email: {user.email}</p>
448
+ </div>
449
+ </div>
450
+
451
+ <script>
452
+ let users = [
453
+ { name: "John Doe", email: "john@example.com" },
454
+ { name: "Jane Smith", email: "jane@example.com" },
455
+ ];
456
+ </script>
457
+ ```
458
+
459
+ **With Index:**
460
+
461
+ ```html
462
+ <ul>
463
+ <li $for="(item, index) in items">{index + 1}. {item}</li>
464
+ </ul>
465
+
466
+ <script>
467
+ let items = ["First", "Second", "Third"];
468
+ </script>
469
+ ```
470
+
471
+ **Performance Optimization with `$key`:**
472
+
473
+ Use the `$key` attribute to help LadrillosJS track items efficiently:
474
+
475
+ ```html
476
+ <div>
477
+ <div class="user-card" $for="user in users" $key="user.id">
478
+ <h3>{user.name}</h3>
479
+ <p>Email: {user.email}</p>
480
+ <button onclick="removeUser(user.id)">Remove</button>
481
+ </div>
482
+ </div>
483
+
484
+ <script>
485
+ let users = [
486
+ { id: 1, name: "John", email: "john@example.com" },
487
+ { id: 2, name: "Jane", email: "jane@example.com" },
488
+ ];
489
+
490
+ const removeUser = (id) => {
491
+ users = users.filter((u) => u.id !== id);
492
+ };
493
+ </script>
494
+ ```
427
495
 
428
496
  ### Slots
429
497
 
430
- Content projection using slots:
498
+ Project content from parent to child components:
431
499
 
432
500
  ```html
433
501
  <!-- card.html -->
@@ -444,450 +512,589 @@ Content projection using slots:
444
512
  </div>
445
513
  </div>
446
514
 
447
- <!-- Usage -->
515
+ <style>
516
+ .card {
517
+ border: 1px solid #ddd;
518
+ border-radius: 8px;
519
+ padding: 1rem;
520
+ }
521
+ .card-header {
522
+ font-weight: bold;
523
+ margin-bottom: 1rem;
524
+ }
525
+ .card-footer {
526
+ margin-top: 1rem;
527
+ border-top: 1px solid #eee;
528
+ padding-top: 1rem;
529
+ }
530
+ </style>
531
+ ```
532
+
533
+ **Usage:**
534
+
535
+ ```html
448
536
  <my-card>
449
537
  <h2 slot="header">User Profile</h2>
450
- <p>This goes in the default slot</p>
451
- <button slot="footer">Save</button>
538
+ <p>Name: John Doe</p>
539
+ <p>Email: john@example.com</p>
540
+ <button slot="footer">Save Changes</button>
452
541
  </my-card>
453
542
  ```
454
543
 
455
- ## Advanced Features
544
+ ### Component Props
456
545
 
457
- ### External Scripts
458
-
459
- Load external JavaScript with components and bind them to the component context:
546
+ Pass data to components using HTML attributes:
460
547
 
461
548
  ```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>
549
+ <!-- greeting.html -->
550
+ <div>
551
+ <h1>Hello, {name}!</h1>
552
+ <p>Age: {age}</p>
553
+ </div>
467
554
 
468
- <!-- Regular external script (global scope) -->
469
- <script src="https://cdn.example.com/library.js"></script>
555
+ <script>
556
+ // Access attributes passed to the component
557
+ let name = this.getAttribute("name") || "Guest";
558
+ let age = this.getAttribute("age") || "unknown";
559
+ </script>
470
560
  ```
471
561
 
472
- For modules with `bind`, export a default function that receives the component context:
473
-
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
- };
562
+ **Usage:**
484
563
 
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
- });
495
-
496
- // Called automatically if defined
497
- if (this.init) {
498
- this.init();
499
- }
500
- }
564
+ ```html
565
+ <my-greeting name="John" age="25"></my-greeting>
566
+ <my-greeting name="Jane" age="30"></my-greeting>
501
567
  ```
502
568
 
503
- **New in v2.0:**
504
-
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
569
+ ## Advanced Features
508
570
 
509
571
  ### Global Event Bus
510
572
 
511
- **New in v2.0:** The global event bus enables cross-component communication without prop drilling or shared state.
573
+ The global event bus enables communication between components without prop drilling:
512
574
 
513
575
  ```javascript
514
- // In any component script
515
- // Emit an event
576
+ // Emit events to other components
516
577
  $emit("user-logged-in", { userId: 123, username: "john" });
517
578
 
518
- // Listen for events
579
+ // Listen for events from any component
519
580
  $listen("user-logged-in", (data) => {
520
581
  console.log(`User ${data.username} logged in`);
521
- // Update local state
522
582
  isLoggedIn = true;
523
583
  currentUser = data;
524
584
  });
525
585
  ```
526
586
 
527
- #### Example: Header & Login Components
587
+ **Example: Cross-Component Communication**
528
588
 
529
589
  ```html
530
590
  <!-- header.html -->
531
591
  <header>
532
- <span data-if="isLoggedIn">Welcome, {username}!</span>
533
- <button data-else onclick="showLogin">Login</button>
592
+ <span $if="{isLoggedIn}">Welcome, {username}!</span>
593
+ <button $else onclick="requestLogin()">Login</button>
534
594
  </header>
535
595
 
536
596
  <script>
537
597
  let isLoggedIn = false;
538
598
  let username = "";
539
599
 
540
- // Listen for login event from other components
541
600
  $listen("user-logged-in", (user) => {
601
+ console.log("User logged in:", user);
542
602
  isLoggedIn = true;
543
603
  username = user.username;
544
604
  });
545
605
 
546
- const showLogin = () => {
547
- $emit("show-login-modal");
606
+ const requestLogin = () => {
607
+ $emit("show-login-dialog");
548
608
  };
549
609
  </script>
550
610
  ```
551
611
 
552
612
  ```html
553
613
  <!-- login-form.html -->
554
- <form onsubmit="handleLogin">
614
+ <form onsubmit="handleLogin(event)" $if="{showLoginDialog}">
555
615
  <input type="text" $bind="username" placeholder="Username" />
556
616
  <input type="password" $bind="password" placeholder="Password" />
557
617
  <button type="submit">Login</button>
558
618
  </form>
559
619
 
560
620
  <script>
561
- let $username = "";
562
- let $password = "";
621
+ let showLoginDialog = false;
622
+
623
+ $listen("show-login-dialog", () => {
624
+ showLoginDialog = true;
625
+ });
563
626
 
564
627
  const handleLogin = (e) => {
565
628
  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 = "";
629
+ $emit("user-logged-in", { userId: 123, username });
630
+ username = "";
631
+ password = "";
632
+ showLoginDialog = false;
576
633
  };
577
634
  </script>
578
635
  ```
579
636
 
580
- **Event Bus Benefits:**
637
+ ### External Scripts
638
+
639
+ LadrillosJS supports three ways to include external JavaScript:
640
+
641
+ #### 1. Component-Scoped Scripts (Default)
581
642
 
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
643
+ Regular script tags execute within the component's context and have access to component state and utilities:
586
644
 
587
- ### Removed: Global State Stores
645
+ ```html
646
+ <!-- alert-button.html -->
647
+ <button onclick="increaseCount()">{title}: {count}</button>
588
648
 
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:
649
+ <!-- This script runs in the component context -->
650
+ <script src="./alert.js"></script>
651
+ ```
590
652
 
591
- **Before (v1.x with stores):**
653
+ **alert.js:**
592
654
 
593
655
  ```javascript
594
- import { createStore } from "ladrillosjs";
656
+ // Variables and functions are available to the component
657
+ let count = 0;
658
+ const title = "Button Count";
595
659
 
596
- export const userStore = createStore({
597
- user: null,
598
- isAuthenticated: false,
599
- });
660
+ const increaseCount = () => {
661
+ count++; // Updates component state
662
+ };
663
+ ```
600
664
 
601
- userStore.subscribe((state) => {
602
- this.setState(state);
603
- });
665
+ #### 2. ES Modules
666
+
667
+ Use `type="module"` for standard ES module imports:
668
+
669
+ ```html
670
+ <!-- side-nav.html -->
671
+ <nav>
672
+ <h1>&lt;Note App/&gt;</h1>
673
+ <button onclick="createNote()">Create Note</button>
674
+ <ul></ul>
675
+ </nav>
676
+
677
+ <!-- Load as ES module -->
678
+ <script type="module" src="../js/side.js"></script>
604
679
  ```
605
680
 
606
- **After (v2.0 with event bus):**
681
+ **side.js:**
607
682
 
608
683
  ```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;
684
+ import { registerComponent, $listen, $querySelector } from "ladrillosjs";
685
+
686
+ const notes = [];
687
+ registerComponent("note-item", "./components/note-item.html");
688
+
689
+ $listen("note_saved", (data) => {
690
+ notes.push({ ...data });
691
+ const ul = $querySelector("ul");
692
+ if (ul) {
693
+ ul.innerHTML = notes
694
+ .map((n) => `<note-item data-note='${JSON.stringify(n)}'></note-item>`)
695
+ .join("");
696
+ }
617
697
  });
618
698
  ```
619
699
 
700
+ #### 3. External Libraries
701
+
702
+ Use the `external` attribute for third-party libraries that shouldn't be bound to component context:
703
+
704
+ ```html
705
+ <!-- codeblock.html -->
706
+ <link
707
+ rel="stylesheet"
708
+ href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"
709
+ />
710
+ <script
711
+ src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"
712
+ external
713
+ ></script>
714
+
715
+ <div class="code-container">
716
+ <pre><code id="code" class="language-html"></code></pre>
717
+ </div>
718
+
719
+ <script>
720
+ // Access the external library (hljs is global)
721
+ const codeElement = $querySelector("pre code");
722
+ codeElement.textContent = "console.log('Hello')";
723
+ hljs.highlightElement(codeElement); // Use external library
724
+ </script>
725
+ ```
726
+
727
+ **When to use `external`:**
728
+
729
+ - Third-party CDN libraries (highlight.js, Chart.js, etc.)
730
+ - Libraries that need to be loaded globally
731
+ - Scripts that don't need component context or utilities
732
+
620
733
  ### Shadow DOM
621
734
 
622
- Components use Shadow DOM by default for style encapsulation. To disable:
735
+ Components use Shadow DOM by default for style encapsulation:
623
736
 
624
737
  ```javascript
625
- // Disable Shadow DOM for a component
626
- await registerComponent("my-component", "./my-component.html", false);
627
-
628
- // With Shadow DOM enabled (default)
738
+ // Shadow DOM enabled (default)
739
+ await registerComponent("isolated-widget", "./widget.html");
629
740
  await registerComponent("isolated-widget", "./widget.html", true);
741
+
742
+ // Shadow DOM disabled
743
+ await registerComponent("global-styles", "./global.html", false);
630
744
  ```
631
745
 
632
746
  **Shadow DOM Benefits:**
633
747
 
634
748
  - **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
749
+ - **Encapsulation**: Internal DOM is hidden from parent
750
+ - **Clean Separation**: Each component has its own styling context
637
751
 
638
752
  **When to Disable:**
639
753
 
640
- - Need global CSS styles to apply
641
- - Using third-party CSS frameworks
642
- - Debugging with browser dev tools (easier without shadow DOM)
754
+ - Need to apply global CSS frameworks (Bootstrap, Tailwind)
755
+ - Using third-party libraries that expect normal DOM
756
+ - Easier debugging without shadow boundaries
643
757
 
644
- ### Performance & Caching
758
+ ### Styling Components
645
759
 
646
- **New in v2.0:** LRU (Least Recently Used) caching for improved performance:
760
+ LadrillosJS supports multiple ways to style your components:
647
761
 
648
- #### Component Caching
762
+ #### 1. Inline Styles (Scoped)
649
763
 
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
764
+ ```html
765
+ <div class="button">{label}</div>
653
766
 
654
- #### Function Caching
767
+ <style>
768
+ .button {
769
+ padding: 10px 20px;
770
+ background: #007bff;
771
+ color: white;
772
+ }
773
+ </style>
655
774
 
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
775
+ <script>
776
+ let label = "Click me";
777
+ </script>
778
+ ```
659
779
 
660
- **Performance Improvements:**
780
+ #### 2. External Stylesheets
661
781
 
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
782
+ ```html
783
+ <link rel="stylesheet" href="./styles.css" />
784
+ <div class="container">Content</div>
785
+ ```
666
786
 
667
- ## API Reference
787
+ #### 3. Import Fonts and External CSS
668
788
 
669
- ### Component Methods
789
+ ```html
790
+ <style>
791
+ @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
670
792
 
671
- | Method | Description |
672
- | ------------------- | ------------------------------------------------------------------------- |
673
- | `setState(partial)` | Update component state and trigger re-render (merges with existing state) |
793
+ body {
794
+ font-family: "Roboto", sans-serif;
795
+ }
796
+ </style>
797
+ ```
674
798
 
675
- ### Framework Utilities (v2.0)
799
+ #### 4. Using `:host` Selector (Shadow DOM)
676
800
 
677
- Available within component `<script>` tags:
801
+ When using Shadow DOM, style the component's container with `:host`:
678
802
 
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 |
803
+ ```html
804
+ <style>
805
+ :host {
806
+ display: block;
807
+ padding: 1rem;
808
+ background: white;
809
+ }
687
810
 
688
- ### Component Attributes
811
+ :host(.highlighted) {
812
+ border: 2px solid gold;
813
+ }
814
+ </style>
815
+ ```
689
816
 
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) |
817
+ **Usage:**
697
818
 
698
- ### Registration Functions
819
+ ```html
820
+ <my-component class="highlighted"></my-component>
821
+ ```
699
822
 
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 |
823
+ ### Performance & Caching
704
824
 
705
- ## Examples
825
+ LadrillosJS includes built-in performance optimizations:
706
826
 
707
- ### Component Communication
827
+ #### Component Caching
708
828
 
709
- **v2.0 uses the global event bus instead of custom events:**
829
+ - **LRU Cache**: Stores up to 25 most recently used components
830
+ - **Instant Loading**: Cached components load immediately
831
+ - **Reduced Network**: No repeated HTTP requests for components
710
832
 
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>
833
+ #### Function Caching
718
834
 
719
- <script>
720
- let messageCount = 0;
835
+ - **Template Compilation**: Caches up to 100 compiled expressions
836
+ - **Memory Efficient**: Reuses Function objects for identical expressions
837
+ - **Faster Renders**: Expressions like `{formatName(user)}` compile once
721
838
 
722
- // Listen for events from child
723
- $listen("child-message", (data) => {
724
- console.log("Received from child:", data);
725
- messageCount++;
726
- });
727
- </script>
728
- ```
839
+ **Performance Tips:**
840
+
841
+ - Component files are cached after first load
842
+ - State updates trigger minimal DOM changes
843
+ - Event listeners are automatically cleaned up
844
+ - Conditional rendering skips hidden elements
845
+
846
+ ## Common Patterns
847
+
848
+ ### Keyboard Events
849
+
850
+ Handle keyboard input for interactive features:
729
851
 
730
852
  ```html
731
- <!-- child.html -->
732
853
  <div>
733
- <h3>Child Component</h3>
734
- <button onclick="sendMessage">Send Message to Parent</button>
854
+ <p>Press arrow keys to navigate. Current: {direction}</p>
855
+ <p>Press Enter to submit</p>
735
856
  </div>
736
857
 
737
858
  <script>
738
- let count = 0;
739
-
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
- };
859
+ let direction = "none";
860
+
861
+ document.addEventListener("keyup", (event) => {
862
+ if (event.key === "ArrowRight") {
863
+ direction = "right";
864
+ } else if (event.key === "ArrowLeft") {
865
+ direction = "left";
866
+ } else if (event.key === "ArrowUp") {
867
+ direction = "up";
868
+ } else if (event.key === "ArrowDown") {
869
+ direction = "down";
870
+ } else if (event.key === "Enter") {
871
+ direction = "submitted!";
872
+ }
873
+ });
748
874
  </script>
749
875
  ```
750
876
 
751
- ### Dynamic Component Creation
877
+ ### Form Validation
752
878
 
753
- ```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
- };
879
+ Real-time form validation with reactive state:
762
880
 
763
- // Fetch and create multiple components
764
- fetch("/api/users")
765
- .then((res) => res.json())
766
- .then((users) => users.forEach(createCard));
767
- ```
881
+ ```html
882
+ <form onsubmit="handleSubmit(event)">
883
+ <input type="email" $bind="email" placeholder="Email" onkeyup="validate()" />
884
+ <p $if="{!isValid}" style="color: red">Please enter a valid email</p>
885
+ <button type="submit" $if="{isValid}">Submit</button>
886
+ </form>
768
887
 
769
- ### Passing Complex Data
888
+ <script>
889
+ let email = "";
890
+ let isValid = false;
770
891
 
771
- Use JSON.stringify for passing objects/arrays as attributes:
892
+ const validate = () => {
893
+ isValid = email.includes("@") && email.length > 5;
894
+ };
772
895
 
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
- `;
896
+ const handleSubmit = (e) => {
897
+ e.preventDefault();
898
+ if (isValid) {
899
+ alert(`Submitted: ${email}`);
900
+ }
901
+ };
789
902
  </script>
790
903
  ```
791
904
 
905
+ ### Loading States
906
+
907
+ Show loading indicators while fetching data:
908
+
792
909
  ```html
793
- <!-- In child component (user-card.html) -->
794
910
  <div>
795
- <h3>{user.name}</h3>
796
- <p>ID: {user.id}</p>
797
- <p>Roles: {user.roles.join(", ")}</p>
911
+ <div $if="{isLoading}">Loading...</div>
912
+ <div $else-if="{error}">Error: {error}</div>
913
+ <div $else>
914
+ <h2>{data.title}</h2>
915
+ <p>{data.description}</p>
916
+ </div>
917
+ <button onclick="fetchData()">Refresh</button>
798
918
  </div>
799
919
 
800
920
  <script>
801
- // data-user is automatically parsed from JSON
802
- let user = this.state["data-user"];
921
+ let isLoading = false;
922
+ let error = null;
923
+ let data = { title: "", description: "" };
924
+
925
+ const fetchData = async () => {
926
+ isLoading = true;
927
+ error = null;
928
+
929
+ try {
930
+ const response = await fetch("https://api.example.com/data");
931
+ data = await response.json();
932
+ } catch (e) {
933
+ error = e.message;
934
+ } finally {
935
+ isLoading = false;
936
+ }
937
+ };
803
938
  </script>
804
939
  ```
805
940
 
806
- ## Migration Guide (v1.x to v2.0)
941
+ ## API Reference
807
942
 
808
- ### Breaking Changes
943
+ ### Registration Functions
809
944
 
810
- 1. **Global State Stores Removed**
945
+ ```typescript
946
+ registerComponent(name: string, path: string, useShadowDOM?: boolean): Promise<void>
947
+ registerComponents(components: Array<{name, path, useShadowDOM?}>): Promise<void>
948
+ ```
811
949
 
812
- - **Before:** `createStore()` API
813
- - **After:** Use global event bus with `$emit` and `$listen`
950
+ ### Component Utilities
814
951
 
815
- 2. **Component Registration**
952
+ Available within component `<script>` tags:
816
953
 
817
- - **Before:** `registerComponent()` was synchronous
818
- - **After:** Returns a Promise, use `await` or `.then()`
954
+ ```typescript
955
+ // State management
956
+ $getState(): object // Access component state
957
+ $setState(updates: object): void // Update state and re-render
819
958
 
820
- 3. **Framework Utilities**
959
+ // Event bus
960
+ $emit(eventName: string, data?: any): void
961
+ $listen(eventName: string, callback: (data?) => void): () => void
821
962
 
822
- - **Before:** `this.emit()`, `this.listen()`, `this.setState()`
823
- - **After:** Use `$emit()`, `$listen()`, `$setState()` in scripts (legacy methods still available for compatibility)
963
+ // DOM queries (respects Shadow DOM boundaries)
964
+ $querySelector(selector: string): Element | null
965
+ $querySelectorAll(selector: string): NodeListOf<Element>
824
966
 
825
- 4. **Two-Way Binding**
826
- - **Before:** No built-in support (manual implementation)
827
- - **After:** Use `$bind` attribute for automatic two-way binding
967
+ // Reactive variables (for external modules)
968
+ $reactive(name: string, initialValue: any): (value: any) => void
969
+ ```
828
970
 
829
- ### New Features to Adopt
971
+ ### Component Attributes
830
972
 
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`
973
+ ```html
974
+ <!-- Two-way data binding -->
975
+ <input $bind="variableName" />
976
+
977
+ <!-- Conditional rendering -->
978
+ <div $if="{condition}">...</div>
979
+ <div $else-if="{anotherCondition}">...</div>
980
+ <div $else>...</div>
981
+
982
+ <!-- List rendering -->
983
+ <li $for="item in items">{item}</li>
984
+ <li $for="(item, index) in items">{index}: {item}</li>
985
+ <div $for="user in users" $key="user.id">{user.name}</div>
986
+
987
+ <!-- Event handlers -->
988
+ <button onclick="methodName()">Click</button>
989
+ <button onclick="method(arg1, arg2)">Call with args</button>
990
+ <button onclick="console.log(event)">Inline function</button>
991
+
992
+ <!-- Slots -->
993
+ <slot></slot>
994
+ <!-- Default slot -->
995
+ <slot name="header"></slot>
996
+ <!-- Named slot -->
997
+ ```
836
998
 
837
- ## Contributing
999
+ ### Template Syntax
1000
+
1001
+ ```html
1002
+ <!-- Variable interpolation -->
1003
+ {variableName} {object.property} {array[0]}
1004
+
1005
+ <!-- Function calls -->
1006
+ {functionName(arg1, arg2)} {object.method()}
1007
+
1008
+ <!-- Expressions -->
1009
+ {count + 1} {isActive ? 'Yes' : 'No'} {items.length > 0 ? 'Has items' : 'Empty'}
1010
+ ```
1011
+
1012
+ ## Development
1013
+
1014
+ ### Prerequisites
838
1015
 
839
- Contributions are welcome! Please feel free to submit a Pull Request.
1016
+ - **Node.js**: v20.19+ or v22.12+
1017
+ - **npm**: v9+ (comes with Node.js)
840
1018
 
841
- ### Development
1019
+ ### Setup
842
1020
 
843
1021
  ```bash
1022
+ # Clone the repository
1023
+ git clone https://github.com/drubiodev/LadrillosJS.git
1024
+ cd LadrillosJS
1025
+
844
1026
  # Install dependencies
845
1027
  npm install
846
1028
 
847
- # Run development server with hot reload
1029
+ # Start development server
848
1030
  npm run dev
849
1031
 
1032
+ # Build the library
1033
+ npm run build
1034
+
850
1035
  # Run tests
851
1036
  npm test
852
1037
 
853
1038
  # Run tests with coverage
854
1039
  npm run test:coverage
855
-
856
- # Build the library
857
- npm run build
858
-
859
- # Build TypeScript types
860
- npm run build:types
861
1040
  ```
862
1041
 
863
1042
  ### Project Structure
864
1043
 
865
1044
  ```
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
1045
+ LadrillosJS/
1046
+ ├── src/
1047
+ ├── index.ts # Main entry point
1048
+ │ ├── core/
1049
+ ├── main.ts # Core Ladrillos class
1050
+ ├── webcomponent.ts # Web component wrapper
1051
+ ├── componentParser.ts # Parse component files
1052
+ ├── componentSource.ts # Fetch with caching
1053
+ ├── eventBus.ts # Global event bus
1054
+ │ │ ├── css/
1055
+ │ │ └── cssParser.ts
1056
+ │ │ ├── html/
1057
+ │ │ │ ├── htmlparser.ts
1058
+ │ │ └── htmlRenderer.ts
1059
+ └── js/
1060
+ │ │ └── scriptParser.ts
1061
+ │ ├── cache/
1062
+ │ ├── index.ts # LRU component cache
1063
+ │ │ └── functionCache.ts # LRU function cache
1064
+ ├── types/
1065
+ │ │ └── LadrilloTypes.ts
1066
+ │ └── utils/
1067
+ │ ├── logger.ts
1068
+ │ └── regex.ts
1069
+ ├── samples/ # Example applications
1070
+ │ └── apps/
1071
+ │ ├── todo/
1072
+ │ ├── notes/
1073
+ │ ├── simple-button/
1074
+ │ └── ...
1075
+ ├── test/ # Test files
1076
+ ├── dist/ # Built files
1077
+ ├── package.json
1078
+ ├── tsconfig.json
1079
+ ├── vite.config.js
1080
+ └── vitest.config.js
1081
+ ```
1082
+
1083
+ ### NPM Scripts
1084
+
1085
+ ```bash
1086
+ npm run dev # Start Vite dev server
1087
+ npm run build # Build library (ESM, UMD, CJS)
1088
+ npm run build:types # Generate TypeScript declarations
1089
+ npm test # Run tests with Vitest
1090
+ npm run test:coverage # Run tests with coverage report
1091
+ npm run preview # Preview production build
889
1092
  ```
890
1093
 
1094
+ ## Attribution
1095
+
1096
+ If you use LadrillosJS in your project, I'd appreciate a mention or link back to this repository, though it's not required. It helps others discover the framework and motivates continued development!
1097
+
891
1098
  ## License
892
1099
 
893
1100
  MIT License - see [LICENSE](LICENSE) file for details.
@@ -896,4 +1103,4 @@ MIT License - see [LICENSE](LICENSE) file for details.
896
1103
 
897
1104
  **LadrillosJS v2.0** - Built with ❤️ by [Daniel Rubio](https://github.com/drubiodev)
898
1105
 
899
- Rewritten in TypeScript for better performance, developer experience, and maintainability.
1106
+ 🌟 [GitHub](https://github.com/drubiodev/LadrillosJS) 📦 [NPM](https://www.npmjs.com/package/ladrillosjs) 📖 [Documentation](https://github.com/drubiodev/LadrillosJS/blob/main/README.md)