ladrillosjs 2.0.0-beta.4.4 → 2.0.0-beta.5.1

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 (40) hide show
  1. package/README.md +405 -1723
  2. package/dist/index.d.ts +26 -58
  3. package/dist/index.js +1 -0
  4. package/package.json +49 -55
  5. package/LICENSE +0 -21
  6. package/dist/cache/functionCache.d.ts +0 -15
  7. package/dist/cache/index.d.ts +0 -16
  8. package/dist/core/componentParser.d.ts +0 -36
  9. package/dist/core/componentSource.d.ts +0 -12
  10. package/dist/core/css/cssParser.d.ts +0 -3
  11. package/dist/core/eventBus.d.ts +0 -41
  12. package/dist/core/html/htmlRenderer.d.ts +0 -23
  13. package/dist/core/html/htmlparser.d.ts +0 -22
  14. package/dist/core/js/scriptParser.d.ts +0 -3
  15. package/dist/core/main.d.ts +0 -13
  16. package/dist/core/webcomponent.d.ts +0 -2
  17. package/dist/index-6X7blelu.js +0 -74
  18. package/dist/index-6X7blelu.js.map +0 -1
  19. package/dist/index-DE-ur4y8.mjs +0 -758
  20. package/dist/index-DE-ur4y8.mjs.map +0 -1
  21. package/dist/ladrillosjs.cjs.js +0 -2
  22. package/dist/ladrillosjs.cjs.js.map +0 -1
  23. package/dist/ladrillosjs.es.js +0 -15
  24. package/dist/ladrillosjs.es.js.map +0 -1
  25. package/dist/ladrillosjs.umd.js +0 -183
  26. package/dist/ladrillosjs.umd.js.map +0 -1
  27. package/dist/types/LadrilloTypes.d.ts +0 -157
  28. package/dist/utils/devErrors.d.ts +0 -60
  29. package/dist/utils/logger.d.ts +0 -23
  30. package/dist/utils/regex.d.ts +0 -20
  31. package/dist/vite/copyComponentsPlugin.d.ts +0 -45
  32. package/dist/vite/index.d.ts +0 -4
  33. package/dist/vite.cjs +0 -51
  34. package/dist/vite.cjs.map +0 -7
  35. package/dist/vite.js +0 -51
  36. package/dist/vite.js.map +0 -7
  37. package/dist/webcomponent-BvS1JL3O.js +0 -111
  38. package/dist/webcomponent-BvS1JL3O.js.map +0 -1
  39. package/dist/webcomponent-DawPaCV3.mjs +0 -1299
  40. package/dist/webcomponent-DawPaCV3.mjs.map +0 -1
package/README.md CHANGED
@@ -1,1902 +1,584 @@
1
1
  # LadrillosJS
2
2
 
3
- <img src="https://raw.githubusercontent.com/drubiodev/LadrillosJS/refs/heads/main/LadrillosJS.jpg" alt="LadrillosJS" width="400"/>
4
-
5
- A lightweight, zero-dependency web component framework for building modular web applications.
6
-
7
- **Version 2.0** - Rewritten in TypeScript with enhanced performance, improved developer experience, and powerful new features.
8
-
9
- > "Empower developers to componentize code efficiently without the complexity of a full-scale framework. Focus on simplicity while leveraging core web fundamentals."
10
-
11
- ## Table of Contents
12
-
13
- - [Features](#features)
14
- - [Installation](#installation)
15
- - [Using with Vite](#using-with-vite)
16
- - [Quick Start](#quick-start)
17
- - [Core Concepts](#core-concepts)
18
- - [Component Registration](#component-registration)
19
- - [State Management](#state-management)
20
- - [Event Handling](#event-handling)
21
- - [Data Binding](#data-binding)
22
- - [Conditional Rendering](#conditional-rendering)
23
- - [List Rendering](#list-rendering)
24
- - [Slots](#slots)
25
- - [Component Props](#component-props)
26
- - [Lifecycle Hooks](#lifecycle-hooks)
27
- - [Advanced Features](#advanced-features)
28
- - [Lazy Loading](#lazy-loading)
29
- - [Global Event Bus](#global-event-bus)
30
- - [External Scripts](#external-scripts)
31
- - [Shadow DOM](#shadow-dom)
32
- - [Styling Components](#styling-components)
33
- - [Performance & Caching](#performance--caching)
34
- - [Common Patterns](#common-patterns)
35
- - [Keyboard Events](#keyboard-events)
36
- - [Form Validation](#form-validation)
37
- - [Loading States](#loading-states)
38
- - [API Reference](#api-reference)
39
- - [Development](#development)
40
- - [Attribution](#attribution)
41
- - [License](#license)
42
-
43
- ## Features
44
-
45
- - 🚀 **Zero Dependencies** - Pure JavaScript, no build tools required
46
- - 📦 **Single-File Components** - HTML, CSS, and JavaScript in one file
47
- - ⚡ **Reactive State** - Automatic re-rendering on state changes
48
- - 🎯 **Event System** - Global event bus for component communication
49
- - 🔄 **Two-Way Data Binding** - Seamless form input binding with `$bind`
50
- - 🎨 **Scoped Styles** - Component styles with optional Shadow DOM
51
- - 🔌 **Slots** - Content projection with named and default slots
52
- - 📝 **TypeScript** - Full type definitions and TypeScript source code
53
- - 🎭 **Conditional Rendering** - `$if`, `$else-if`, and `$else` directives
54
- - 🔁 **List Rendering** - `$for` directive for rendering arrays
55
- - ⚡ **Lazy Loading** - Load components on-demand with Intersection Observer
56
- - 🚄 **Smart Caching** - LRU cache for components and compiled functions
57
- - 🔧 **Framework Utilities** - Helper functions for common tasks
58
- - 🧩 **External Scripts** - Load and bind external JavaScript modules
59
-
60
- ## Installation
61
-
62
- ### NPM
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/drubiodev/LadrillosJS/refs/heads/main/LadrillosJS.jpg" alt="LadrillosJS" width="300"/>
5
+ </p>
63
6
 
64
- ```bash
65
- npm install ladrillosjs
66
- ```
67
-
68
- ### CDN
69
-
70
- ```html
71
- <!-- ES Module (Recommended) -->
72
- <script type="module">
73
- import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
74
- registerComponent("my-component", "./my-component.html");
75
- </script>
76
-
77
- <!-- UMD (Browser Global) -->
78
- <script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
79
- <script>
80
- ladrillosjs.registerComponent("my-component", "./my-component.html");
81
- </script>
82
- ```
83
-
84
- ## Using with Vite
85
-
86
- LadrillosJS includes a Vite plugin to automatically copy your component files to the distribution folder during build. This is useful when you have static component files that need to be served with your application.
87
-
88
- ### Installation
89
-
90
- If you haven't already:
91
-
92
- ```bash
93
- npm install --save-dev vite
94
- npm install ladrillosjs
95
- ```
96
-
97
- ### Setup
98
-
99
- Create or update your `vite.config.js`:
100
-
101
- ```javascript
102
- import { defineConfig } from "vite";
103
- import { copyComponentsPlugin } from "ladrillosjs/vite";
104
-
105
- export default defineConfig({
106
- plugins: [
107
- copyComponentsPlugin({
108
- src: "components", // Source directory with your components
109
- dest: "components", // Destination in dist/ folder
110
- copyOnDev: false, // Only copy during build (not dev server)
111
- }),
112
- ],
113
- });
114
- ```
115
-
116
- ### Basic Usage
117
-
118
- ```javascript
119
- // vite.config.js with default settings
120
- import { defineConfig } from "vite";
121
- import { copyComponentsPlugin } from "ladrillosjs/vite";
122
-
123
- export default defineConfig({
124
- plugins: [copyComponentsPlugin()],
125
- });
126
- ```
127
-
128
- The plugin will:
129
-
130
- - 📁 Copy your `components/` folder to `dist/components/` during build
131
- - 🛡️ Handle errors gracefully with helpful logging
132
- - ⚙️ Use sensible defaults (src: 'components', dest: 'components')
133
-
134
- ### Plugin Options
135
-
136
- ```typescript
137
- interface CopyComponentsOptions {
138
- /** Source directory containing components (default: 'components') */
139
- src?: string;
140
-
141
- /** Destination directory in dist/ (default: 'components') */
142
- dest?: string;
143
-
144
- /** Copy during development server (default: false) */
145
- copyOnDev?: boolean;
146
-
147
- /** Process component module scripts (default: true) */
148
- processScripts?: boolean;
149
- }
150
- ```
151
-
152
- ### Processing Component Module Scripts
153
-
154
- When `processScripts` is enabled (default), the plugin automatically transforms component scripts using `type="module"` to work with the window scope. This is essential when building with Vite and using ES modules in your components.
155
-
156
- **What It Does:**
157
-
158
- - Converts ES module imports to window references
159
- - Wraps scripts in an async IIFE that waits for the library to load
160
- - Handles multiple components efficiently with a shared loading promise
161
-
162
- **Example:**
163
-
164
- Before build (in your component):
165
-
166
- ```html
167
- <script type="module">
168
- import { registerComponent } from "ladrillosjs";
169
-
170
- let count = 0;
171
-
172
- export const increment = () => {
173
- count++;
174
- };
175
- </script>
176
- ```
177
-
178
- After build (automatically transformed):
179
-
180
- ```html
181
- <script>
182
- (async () => {
183
- try {
184
- if (!window.__ladrillosPromise__) {
185
- window.__ladrillosPromise__ = new Promise((resolve) => {
186
- // Wait for LadrillosJS to load...
187
- });
188
- }
189
-
190
- await window.__ladrillosPromise__;
191
-
192
- (async () => {
193
- const { registerComponent } = window.ladrillosjs;
194
-
195
- let count = 0;
196
-
197
- // Component code...
198
- })();
199
- } catch (error) {
200
- console.error("Failed to execute component module:", error);
201
- }
202
- })();
203
- </script>
204
- ```
205
-
206
- **Disable Processing (if needed):**
207
-
208
- ```javascript
209
- import { defineConfig } from "vite";
210
- import { copyComponentsPlugin } from "ladrillosjs/vite";
211
-
212
- export default defineConfig({
213
- plugins: [
214
- copyComponentsPlugin({
215
- src: "components",
216
- dest: "components",
217
- processScripts: false, // Disable script processing
218
- }),
219
- ],
220
- });
221
- ```
222
-
223
- ### Complete Example
224
-
225
- Project structure:
226
-
227
- ```
228
- my-app/
229
- ├── components/
230
- │ ├── header.html
231
- │ ├── footer.html
232
- │ └── card.html
233
- ├── src/
234
- │ └── main.js
235
- ├── index.html
236
- └── vite.config.js
237
- ```
238
-
239
- Configuration:
240
-
241
- ```javascript
242
- import { defineConfig } from "vite";
243
- import { copyComponentsPlugin } from "ladrillosjs/vite";
244
-
245
- export default defineConfig({
246
- plugins: [
247
- copyComponentsPlugin({
248
- src: "components",
249
- dest: "components",
250
- }),
251
- ],
252
- });
253
- ```
254
-
255
- Usage in your app:
256
-
257
- ```javascript
258
- // src/main.js
259
- import { registerComponents } from "ladrillosjs";
260
-
261
- await registerComponents([
262
- { name: "app-header", path: "./components/header.html" },
263
- { name: "app-footer", path: "./components/footer.html" },
264
- { name: "app-card", path: "./components/card.html" },
265
- ]);
266
- ```
267
-
268
- After running `npm run build`, your components will be copied to `dist/components/` and be ready for production deployment.
269
-
270
- ### Development Workflow
271
-
272
- ```bash
273
- # Start dev server (components served from source)
274
- npm run dev
275
-
276
- # Build for production (components copied to dist)
277
- npm run build
278
-
279
- # Preview production build
280
- npm run preview
281
- ```
282
-
283
- ## Quick Start
284
-
285
- ### 1. Create Your First Component
286
-
287
- Create a file called `hello-world.html`:
288
-
289
- ```html
290
- <!-- hello-world.html -->
291
- <div class="greeting">
292
- <h1>{title}</h1>
293
- <p>Hello, {name}!</p>
294
- <button onclick="greet()">Greet ({count})</button>
295
- </div>
296
-
297
- <script>
298
- // Component state - automatically reactive
299
- let title = "Welcome to LadrillosJS";
300
- let name = "World";
301
- let count = 0;
302
-
303
- // Event handler
304
- const greet = () => {
305
- count++; // Automatically triggers re-render
306
- name = prompt("What's your name?") || "World";
307
- };
308
- </script>
309
-
310
- <style>
311
- .greeting {
312
- text-align: center;
313
- padding: 2rem;
314
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
315
- color: white;
316
- border-radius: 8px;
317
- }
318
-
319
- button {
320
- padding: 0.75rem 1.5rem;
321
- font-size: 1rem;
322
- background: white;
323
- color: #667eea;
324
- border: none;
325
- border-radius: 4px;
326
- cursor: pointer;
327
- transition: transform 0.2s;
328
- }
329
-
330
- button:hover {
331
- transform: scale(1.05);
332
- }
333
- </style>
334
- ```
335
-
336
- ### 2. Register and Use the Component
337
-
338
- ```html
339
- <!DOCTYPE html>
340
- <html lang="en">
341
- <head>
342
- <meta charset="UTF-8" />
343
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
344
- <title>My LadrillosJS App</title>
345
- </head>
346
- <body>
347
- <!-- Use your component -->
348
- <hello-world></hello-world>
349
-
350
- <!-- Register the component -->
351
- <script type="module">
352
- import { registerComponent } from "ladrillosjs";
353
- registerComponent("hello-world", "./hello-world.html");
354
- </script>
355
- </body>
356
- </html>
357
- ```
358
-
359
- ### 3. Explore Example Apps
360
-
361
- Check out the `samples/apps/` directory for complete examples:
362
-
363
- - **[Todo App](samples/apps/todo)** - Classic todo list with component composition
364
- - **[Notes App](samples/apps/notes)** - Multi-component app with event bus
365
- - **[Simple Button](samples/apps/simple-button)** - Basic interactive component
366
- - **[Business Card](samples/apps/biz)** - Form with two-way data binding
367
- - **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation
368
- - **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
369
- - **[List Rendering](samples/apps/list-test)** - Dynamic lists with `$for` directive
370
-
371
- Run the development server to view examples:
372
-
373
- ```bash
374
- npm install
375
- npm run dev
376
- ```
377
-
378
- ## Core Concepts
379
-
380
- ### Component Registration
381
-
382
- Register components to make them available as custom HTML elements:
383
-
384
- ```javascript
385
- import { registerComponent, registerComponents } from "ladrillosjs";
386
-
387
- // Register a single component
388
- await registerComponent("my-button", "./components/my-button.html");
389
-
390
- // Register multiple components
391
- await registerComponents([
392
- { name: "app-header", path: "./components/header.html" },
393
- { name: "app-footer", path: "./components/footer.html" },
394
- { name: "user-card", path: "./components/user-card.html" },
395
- ]);
396
-
397
- // Disable Shadow DOM for a component
398
- await registerComponent("global-styles", "./components/global.html", false);
399
-
400
- // Enable lazy loading for a component
401
- await registerComponent(
402
- "footer-section",
403
- "./components/footer.html",
404
- true,
405
- true
406
- );
407
-
408
- // Register multiple components with lazy loading
409
- await registerComponents([
410
- { name: "hero-section", path: "./components/hero.html" },
411
- { name: "feature-section", path: "./components/features.html", lazy: true },
412
- { name: "footer-section", path: "./components/footer.html", lazy: true },
413
- ]);
414
- ```
415
-
416
- ### State Management
417
-
418
- Components have reactive state that automatically triggers re-renders when changed:
419
-
420
- ```html
421
- <div>
422
- <h2>Counter: {count}</h2>
423
- <p>User: {user.name}</p>
424
- <button onclick="increment()">Add</button>
425
- <button onclick="updateUser()">Change User</button>
426
- <button onclick="reset()">Reset</button>
427
- </div>
428
-
429
- <script>
430
- // State variables - automatically reactive
431
- let count = 0;
432
- let user = { name: "John", age: 25 };
433
-
434
- const increment = () => {
435
- count++; // Automatically triggers re-render
436
- };
437
-
438
- const updateUser = () => {
439
- user.name = "Jane"; // Direct mutation triggers re-render
440
- };
441
-
442
- // You can also use $setState for explicit updates
443
- const reset = () => {
444
- $setState({ count: 0, user: { name: "Anonymous", age: 0 } });
445
- };
446
- </script>
447
- ```
448
-
449
- **Available State Utilities:**
450
-
451
- - Direct assignment: `count++`, `name = "New"`
452
- - `$setState(updates)`: Merge updates into state
453
- - `$getState()`: Access state in external modules
454
-
455
- ### Event Handling
456
-
457
- Attach event handlers directly to elements:
458
-
459
- ```html
460
- <!-- Method reference -->
461
- <button onclick="handleClick(event)">Click me</button>
462
-
463
- <!-- Function with arguments -->
464
- <button onclick="addItem('apple', 5)">Add Item</button>
465
-
466
- <!-- Inline arrow function -->
467
- <button onclick="console.log(event.target)">Log Event</button>
468
-
469
- <!-- Multiple events -->
470
- <label $if="isValid">Valid</label>
471
- <input
472
- type="text"
473
- placeholder="Enter text (min 3 characters)"
474
- onkeyup="validateInput(event)"
475
- onfocus="highlightField(event)"
476
- onblur="saveField(event)"
477
- />
478
-
479
- <script>
480
- let items = [];
481
- let isValid = false;
482
-
483
- const handleClick = (event) => {
484
- console.log("Button clicked!", event);
485
- };
486
-
487
- const addItem = (name, quantity) => {
488
- items = [...items, { name, quantity }];
489
- console.log("Items:", items);
490
- };
491
-
492
- const validateInput = (e) => {
493
- const value = e.target.value;
494
- isValid = value.length >= 3;
495
- };
496
-
497
- const highlightField = (e) => {
498
- e.target.style.backgroundColor = "lightyellow";
499
- };
500
-
501
- const saveField = (e) => {
502
- e.target.style.backgroundColor = "";
503
- console.log("Field saved:", e.target.value);
504
- };
505
- </script>
506
- ```
507
-
508
- ### Data Binding
509
-
510
- #### One-Way Binding (Display Data)
511
-
512
- Use curly braces `{}` to display state in your template:
513
-
514
- ```html
515
- <div>
516
- <h1>{title}</h1>
517
- <p>{user.name} - {user.email}</p>
518
- <span>Total: {items.length} items</span>
519
- <p>Formatted: {formatPrice(price)}</p>
520
- </div>
521
-
522
- <script>
523
- let title = "My App";
524
- let user = { name: "John", email: "john@example.com" };
525
- let items = ["apple", "banana", "orange"];
526
- let price = 29.99;
527
-
528
- const formatPrice = (value) => `$${value.toFixed(2)}`;
529
- </script>
530
- ```
531
-
532
- #### Two-Way Binding (Form Inputs)
533
-
534
- Use the `$bind` attribute for automatic synchronization between inputs and state:
535
-
536
- ```html
537
- <div>
538
- <h2>Hello, {name}!</h2>
539
- <input type="text" $bind="name" placeholder="Your name" />
540
-
541
- <p>Email: {email}</p>
542
- <input type="email" $bind="email" />
543
-
544
- <p>Bio: {bio}</p>
545
- <textarea $bind="bio"></textarea>
546
-
547
- <p>Country: {country}</p>
548
- <select $bind="country">
549
- <option value="us">United States</option>
550
- <option value="uk">United Kingdom</option>
551
- <option value="ca">Canada</option>
552
- </select>
553
-
554
- <label>
555
- <input type="checkbox" $bind="subscribe" />
556
- Subscribe to newsletter: {subscribe}
557
- </label>
558
- </div>
559
-
560
- <script>
561
- // Variables are automatically synced with inputs
562
- let name = "World";
563
- let email = "";
564
- let bio = "";
565
- let country = "us";
566
- let subscribe = false;
567
- </script>
568
- ```
569
-
570
- ### Conditional Rendering
571
-
572
- Show or hide elements based on conditions:
573
-
574
- ```html
575
- <div>
576
- <h1>Shopping Cart</h1>
577
-
578
- <!-- Simple condition -->
579
- <p $if="{items.length === 0}">Your cart is empty</p>
580
-
581
- <!-- Multiple conditions -->
582
- <div $if="{items.length > 0 && items.length < 5}">
583
- <p>You have {items.length} items</p>
584
- </div>
585
-
586
- <div $else-if="{items.length >= 5}">
587
- <p>Your cart is full! ({items.length} items)</p>
588
- </div>
589
-
590
- <!-- Login/Logout example -->
591
- <button $if="{!isLoggedIn}" onclick="login()">Login</button>
592
- <button $else onclick="logout()">Logout</button>
593
-
594
- <!-- Complex conditions -->
595
- <div $if="{user && user.role.toLowerCase() === 'admin'}">
596
- <p>{user.role} Panel</p>
597
- Hello {user.name}
598
- <button onclick="addToCart()">🛒 Add To Cart</button>
599
- </div>
600
- </div>
601
-
602
- <script>
603
- let items = [];
604
- let isLoggedIn = false;
605
- let user = null;
606
-
607
- const login = () => {
608
- isLoggedIn = true;
609
- user = { name: "John", role: "Admin" };
610
- };
611
-
612
- const logout = () => {
613
- isLoggedIn = false;
614
- user = null;
615
- };
616
-
617
- const addToCart = () => {
618
- if (items.length < 5) {
619
- items.push(`Item ${items.length + 1}`);
620
- } else {
621
- alert("Cart is full!");
622
- }
623
- };
624
- </script>
625
- ```
626
-
627
- **Conditional Directives:**
628
-
629
- - `$if="{expression}"`: Show if expression is truthy
630
- - `$else-if="{expression}"`: Chain multiple conditions
631
- - `$else`: Fallback when previous conditions are false
632
-
633
- ### List Rendering
634
-
635
- Render lists of items using the `$for` directive:
636
-
637
- ```html
638
- <div>
639
- <h2>My Fruits</h2>
640
- <ul>
641
- <li $for="fruit in fruits">{fruit}</li>
642
- </ul>
643
- <button onclick="addFruit()">Add Fruit</button>
644
- </div>
645
-
646
- <script>
647
- let fruits = ["Apple", "Banana", "Orange"];
648
-
649
- const addFruit = () => {
650
- const newFruit = prompt("Enter a fruit name:");
651
- if (newFruit) {
652
- fruits = [...fruits, newFruit]; // Triggers re-render
653
- }
654
- };
655
- </script>
656
- ```
657
-
658
- **List Rendering with Objects:**
659
-
660
- ```html
661
- <div>
662
- <h2>User List</h2>
663
- <div class="user-card" $for="user in users">
664
- <h3>{user.name}</h3>
665
- <p>Email: {user.email}</p>
666
- </div>
667
- </div>
668
-
669
- <script>
670
- let users = [
671
- { name: "John Doe", email: "john@example.com" },
672
- { name: "Jane Smith", email: "jane@example.com" },
673
- ];
674
- </script>
675
- ```
676
-
677
- **With Index:**
678
-
679
- ```html
680
- <ul>
681
- <li $for="(item, index) in items">{index + 1}. {item}</li>
682
- </ul>
683
-
684
- <script>
685
- let items = ["First", "Second", "Third"];
686
- </script>
687
- ```
688
-
689
- **Performance Optimization with `$key`:**
690
-
691
- Use the `$key` attribute to help LadrillosJS track items efficiently:
692
-
693
- ```html
694
- <div>
695
- <div class="user-card" $for="user in users" $key="user.id">
696
- <h3>{user.name}</h3>
697
- <p>Email: {user.email}</p>
698
- <button onclick="removeUser(user.id)">Remove</button>
699
- </div>
700
- </div>
701
-
702
- <script>
703
- let users = [
704
- { id: 1, name: "John", email: "john@example.com" },
705
- { id: 2, name: "Jane", email: "jane@example.com" },
706
- ];
707
-
708
- const removeUser = (id) => {
709
- users = users.filter((u) => u.id !== id);
710
- };
711
- </script>
712
- ```
713
-
714
- ### Slots
715
-
716
- Project content from parent to child components:
717
-
718
- ```html
719
- <!-- card.html -->
720
- <div class="card">
721
- <div class="card-header">
722
- <slot name="header">Default Header</slot>
723
- </div>
724
- <div class="card-body">
725
- <slot></slot>
726
- <!-- Default slot -->
727
- </div>
728
- <div class="card-footer">
729
- <slot name="footer"></slot>
730
- </div>
731
- </div>
732
-
733
- <style>
734
- .card {
735
- border: 1px solid #ddd;
736
- border-radius: 8px;
737
- padding: 1rem;
738
- }
739
- .card-header {
740
- font-weight: bold;
741
- margin-bottom: 1rem;
742
- }
743
- .card-footer {
744
- margin-top: 1rem;
745
- border-top: 1px solid #eee;
746
- padding-top: 1rem;
747
- }
748
- </style>
749
- ```
750
-
751
- **Usage:**
752
-
753
- ```html
754
- <my-card>
755
- <h2 slot="header">User Profile</h2>
756
- <p>Name: John Doe</p>
757
- <p>Email: john@example.com</p>
758
- <button slot="footer">Save Changes</button>
759
- </my-card>
760
- ```
761
-
762
- ### Component Props
763
-
764
- Pass data to components using HTML attributes:
765
-
766
- ```html
767
- <!-- greeting.html -->
768
- <div>
769
- <h1>Hello, {name}!</h1>
770
- <p>Age: {age}</p>
771
- </div>
772
-
773
- <script>
774
- // Access attributes passed to the component
775
- let name = this.getAttribute("name") || "Guest";
776
- let age = this.getAttribute("age") || "unknown";
777
- </script>
778
- ```
779
-
780
- **Usage:**
781
-
782
- ```html
783
- <my-greeting name="John" age="25"></my-greeting>
784
- <my-greeting name="Jane" age="30"></my-greeting>
785
- ```
786
-
787
- ### Lifecycle Hooks
788
-
789
- LadrillosJS provides Vue.js-style lifecycle hooks so you can run code at specific times in a component's lifecycle. These hooks are tied directly to Web Component lifecycle methods for maximum control.
790
-
791
- #### Available Hooks
792
-
793
- | Hook | Called | Purpose |
794
- | ------------ | ------------------------------------------------ | ---------------------------------- |
795
- | `$onMount` | After component is mounted and fully initialized | Setup, initialization, API calls |
796
- | `$onUpdate` | After component state changes and re-renders | React to state changes |
797
- | `$onUnmount` | Before component is removed from DOM | Cleanup, unsubscribe, clear timers |
798
-
799
- #### `$onMount(callback)`
800
-
801
- Called once when the component is fully mounted and ready. Perfect for initialization, API calls, and setup logic.
802
-
803
- ```html
804
- <div>
805
- <h2>User Profile</h2>
806
- <p $if="{loading}">Loading...</p>
807
- <div $else>
808
- <h3>{user.name}</h3>
809
- <p>{user.email}</p>
810
- </div>
811
- </div>
812
-
813
- <script>
814
- let user = { name: "", email: "" };
815
- let loading = true;
816
-
817
- // Called after component mounts with state ready
818
- $onMount(() => {
819
- console.log("✅ Component mounted!");
820
-
821
- // Perfect for API calls, initialization, etc
822
- fetch("/api/user/123")
823
- .then((res) => res.json())
824
- .then((data) => {
825
- user = data;
826
- loading = false;
827
- });
828
- });
829
- </script>
830
- ```
831
-
832
- **When to use `$onMount`:**
833
-
834
- - ✅ Fetch data from APIs
835
- - ✅ Initialize timers or subscriptions
836
- - ✅ Set up event listeners
837
- - ✅ Run component setup logic
838
- - ✅ Validate initial state
839
-
840
- #### `$onUpdate(callback)`
841
-
842
- Called after component state changes and the component re-renders. Perfect for reacting to state changes.
843
-
844
- ```html
845
- <div>
846
- <p>Count: {count}</p>
847
- <button onclick="count++">Increment</button>
848
- </div>
849
-
850
- <script>
851
- let count = 0;
852
-
853
- $onUpdate(() => {
854
- console.log("🔄 Component updated! New count:", count);
855
-
856
- // React to state changes
857
- if (count === 10) {
858
- console.log("Count reached 10!");
859
- }
860
- });
861
- </script>
862
- ```
863
-
864
- **When to use `$onUpdate`:**
865
-
866
- - ✅ Log state changes for debugging
867
- - ✅ Trigger side effects when specific properties change
868
- - ✅ Sync with external systems
869
- - ✅ Perform validations on updated state
870
-
871
- #### `$onUnmount(callback)`
872
-
873
- Called before the component is removed from the DOM. Perfect for cleanup operations.
874
-
875
- ```html
876
- <div>
877
- <p>Timer: {seconds}</p>
878
- </div>
879
-
880
- <script>
881
- let seconds = 0;
882
- let interval = null;
883
-
884
- $onMount(() => {
885
- // Start timer
886
- interval = setInterval(() => {
887
- seconds++;
888
- }, 1000);
889
- });
890
-
891
- $onUnmount(() => {
892
- console.log("❌ Component unmounting, cleaning up...");
893
-
894
- // Clear timer to prevent memory leaks
895
- if (interval) {
896
- clearInterval(interval);
897
- }
898
- });
899
- </script>
900
- ```
901
-
902
- **When to use `$onUnmount`:**
903
-
904
- - ✅ Clear timers and intervals
905
- - ✅ Remove event listeners
906
- - ✅ Unsubscribe from observables
907
- - ✅ Cleanup resources
908
- - ✅ Cancel pending API requests
909
-
910
- #### Complete Lifecycle Example
911
-
912
- ```html
913
- <div>
914
- <h3>Lifecycle Demo</h3>
915
- <p>Status: {status}</p>
916
- <button onclick="status = 'active'">Activate</button>
917
- <button onclick="status = 'inactive'">Deactivate</button>
918
- </div>
919
-
920
- <script>
921
- let status = "initializing";
922
-
923
- $onMount(() => {
924
- console.log("✅ $onMount - Component ready to go!");
925
- status = "ready";
926
- });
927
-
928
- $onUpdate(() => {
929
- console.log("🔄 $onUpdate - Status changed to:", status);
930
- });
931
-
932
- $onUnmount(() => {
933
- console.log("❌ $onUnmount - Cleaning up before removal");
934
- });
935
- </script>
936
- ```
937
-
938
- #### Lifecycle Timing Diagram
939
-
940
- ```
941
- ┌─────────────────────────────────────────────────────────┐
942
- │ Element added to DOM │
943
- │ ↓ │
944
- │ connectedCallback() starts │
945
- │ ├─ Parse template │
946
- │ ├─ Load styles │
947
- │ ├─ Execute scripts & initialize state │
948
- │ ├─ Render bindings, conditionals, loops │
949
- │ │ │
950
- │ ├─→ $onMount() called ← Component ready! │
951
- │ │ │
952
- │ State changes (click, setState, etc) │
953
- │ ├─ Proxy detects change │
954
- │ ├─ Schedule update (requestAnimationFrame) │
955
- │ ├─ Re-render bindings, conditionals, loops │
956
- │ │ │
957
- │ ├─→ $onUpdate() called ← After each update │
958
- │ │ ... (can happen many times) │
959
- │ │ │
960
- │ Element removed from DOM │
961
- │ ├─→ $onUnmount() called ← Before cleanup │
962
- │ │ │
963
- │ disconnectedCallback() cleanup │
964
- │ ├─ Remove event listeners │
965
- │ ├─ Clear subscriptions │
966
- │ └─ Complete │
967
- └─────────────────────────────────────────────────────────┘
968
- ```
969
-
970
- ## Advanced Features
971
-
972
- ### Lazy Loading
973
-
974
- LadrillosJS supports lazy loading components using the Intersection Observer API. Components are loaded only when they enter or are about to enter the viewport, improving initial page load performance.
975
-
976
- #### Basic Usage
977
-
978
- ```javascript
979
- import { registerComponents } from "ladrillosjs";
980
-
981
- await registerComponents([
982
- // Eager loading (default) - loads immediately
983
- { name: "hero-section", path: "./components/hero.html" },
984
- { name: "navbar", path: "./components/navbar.html" },
985
-
986
- // Lazy loading - loads when scrolled into view
987
- { name: "feature-section", path: "./components/features.html", lazy: true },
988
- { name: "testimonials", path: "./components/testimonials.html", lazy: true },
989
- { name: "footer-section", path: "./components/footer.html", lazy: true },
990
- ]);
991
- ```
992
-
993
- ```html
994
- <body>
995
- <!-- Loads immediately -->
996
- <navbar></navbar>
997
- <hero-section></hero-section>
998
-
999
- <!-- Loads when user scrolls near these components -->
1000
- <feature-section></feature-section>
1001
- <testimonials></testimonials>
1002
- <footer-section></footer-section>
1003
- </body>
1004
- ```
1005
-
1006
- #### Override Lazy Loading with `eager` Attribute
1007
-
1008
- You can force a lazy component to load immediately using the `eager` attribute:
1009
-
1010
- ```html
1011
- <!-- This lazy component loads immediately despite lazy registration -->
1012
- <footer-section eager></footer-section>
1013
- ```
1014
-
1015
- #### Best Practices for Lazy Loading
1016
-
1017
- **✅ Good candidates for lazy loading:**
1018
-
1019
- - Below-the-fold content (footers, testimonials)
1020
- - Large components with heavy resources
1021
- - Components that may not be viewed by all users
1022
- - Third-party widgets or embeds
1023
-
1024
- **❌ Avoid lazy loading for:**
1025
-
1026
- - Above-the-fold content (heroes, navigation)
1027
- - Critical interactive elements
1028
- - Small, lightweight components
1029
- - Content needed for SEO
7
+ <p align="center">
8
+ <strong>A lightweight, zero-dependency web component framework.</strong>
9
+ </p>
1030
10
 
1031
- **Performance Tips:**
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>
1032
17
 
1033
- 1. **Reserve space for lazy components** to prevent layout shift:
18
+ <p align="center">
19
+ Build modular web apps with simple HTML components. No virtual DOM, no complex tooling required.
20
+ </p>
1034
21
 
1035
- ```css
1036
- /* In your global CSS */
1037
- feature-section,
1038
- testimonials,
1039
- footer-section {
1040
- display: block;
1041
- min-height: 400px; /* Approximate height */
1042
- }
1043
- ```
1044
-
1045
- 2. **Load strategy**: Lazy components load **100px before** entering the viewport for smooth user experience and to account for network latency.
1046
-
1047
- 3. **Multiple instances**: If you use the same lazy component multiple times on a page, only one network request is made. All instances share the loaded component definition.
1048
-
1049
- 4. **Caching**: Once loaded, lazy components are cached and reused across page navigation.
1050
-
1051
- #### How It Works
1052
-
1053
- - **Placeholder**: Lazy components initially render as minimal placeholders
1054
- - **Intersection Observer**: Monitors when placeholders approach the viewport
1055
- - **Automatic Loading**: Component fetches and upgrades when visible
1056
- - **Seamless Swap**: Placeholder is replaced with the real component
1057
- - **Shared Loading**: Multiple instances coordinate to load only once
1058
-
1059
- ### Global Event Bus
1060
-
1061
- The global event bus enables communication between components without prop drilling:
1062
-
1063
- ```javascript
1064
- // Emit events to other components
1065
- $emit("user-logged-in", { userId: 123, username: "john" });
1066
-
1067
- // Listen for events from any component
1068
- $listen("user-logged-in", (data) => {
1069
- console.log(`User ${data.username} logged in`);
1070
- isLoggedIn = true;
1071
- currentUser = data;
1072
- });
1073
- ```
1074
-
1075
- **Example: Cross-Component Communication**
1076
-
1077
- ```html
1078
- <!-- header.html -->
1079
- <header>
1080
- <span $if="{isLoggedIn}">Welcome, {username}!</span>
1081
- <button $else onclick="requestLogin()">Login</button>
1082
- </header>
1083
-
1084
- <script>
1085
- let isLoggedIn = false;
1086
- let username = "";
1087
-
1088
- $listen("user-logged-in", (user) => {
1089
- console.log("User logged in:", user);
1090
- isLoggedIn = true;
1091
- username = user.username;
1092
- });
1093
-
1094
- const requestLogin = () => {
1095
- $emit("show-login-dialog");
1096
- };
1097
- </script>
1098
- ```
1099
-
1100
- ```html
1101
- <!-- login-form.html -->
1102
- <form onsubmit="handleLogin(event)" $if="{showLoginDialog}">
1103
- <input type="text" $bind="username" placeholder="Username" />
1104
- <input type="password" $bind="password" placeholder="Password" />
1105
- <button type="submit">Login</button>
1106
- </form>
1107
-
1108
- <script>
1109
- let showLoginDialog = false;
1110
-
1111
- $listen("show-login-dialog", () => {
1112
- showLoginDialog = true;
1113
- });
1114
-
1115
- const handleLogin = (e) => {
1116
- e.preventDefault();
1117
- $emit("user-logged-in", { userId: 123, username });
1118
- username = "";
1119
- password = "";
1120
- showLoginDialog = false;
1121
- };
1122
- </script>
1123
- ```
22
+ ---
1124
23
 
1125
- ### External Scripts
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)
1126
39
 
1127
- LadrillosJS supports three ways to include external JavaScript:
40
+ ---
1128
41
 
1129
- #### 1. ES Modules (Recommended for Complex Logic)
42
+ ## 🚀 Quick Start
1130
43
 
1131
- Use `type="module"` for standard ES modules with automatic state synchronization. Variables declared in modules are automatically initialized in component state and mutations are automatically synced:
44
+ ### 1. Add the Script
1132
45
 
1133
46
  ```html
1134
- <!-- side-nav.html -->
1135
- <nav>
1136
- <h1>&lt;Note App/&gt;</h1>
1137
- <button onclick="createNote()">Create Note</button>
1138
- <ul></ul>
1139
- </nav>
1140
-
1141
- <!-- Load as ES module - variables and exports are auto-synced to state -->
1142
- <script type="module" src="../js/side.js"></script>
1143
- ```
1144
-
1145
- **side.js:**
1146
-
1147
- ```javascript
1148
- import { registerComponent, $listen, $querySelector } from "ladrillosjs";
1149
-
1150
- // Declare variables that are used in template bindings
1151
- // They are automatically initialized in component state
1152
- const notes = [];
1153
-
1154
- registerComponent("note-item", "./components/note-item.html");
1155
-
1156
- // Export functions so they're available to event handlers
1157
- export const createNote = () => {
1158
- const title = prompt("Note title:");
1159
- if (title) {
1160
- notes.push({ title, content: "" });
1161
- }
1162
- };
1163
-
1164
- // Listen to global events
1165
- $listen("note_saved", (data) => {
1166
- notes.push({ ...data });
1167
- const ul = $querySelector("ul");
1168
- if (ul) {
1169
- ul.innerHTML = notes
1170
- .map((n) => `<note-item data-note='${JSON.stringify(n)}'></note-item>`)
1171
- .join("");
1172
- }
1173
- });
1174
- ```
1175
-
1176
- **How Module State Sync Works:**
1177
-
1178
- When a module script is loaded, LadrillosJS:
1179
-
1180
- 1. **Detects variables used in template bindings** (e.g., variables in `{}` expressions)
1181
- 2. **Initializes them in component state** automatically when declared
1182
- 3. **Syncs mutations to state** automatically when assigned
1183
- 4. **Auto-attaches exports** to the component so event handlers can access them
1184
-
1185
- This means you write natural JavaScript without boilerplate:
1186
-
1187
- ```javascript
1188
- // ✨ This is all you need!
1189
- let count = 0; // Auto-initialized in state
1190
- let user = { name: "" }; // Works with objects too
1191
-
1192
- export const increment = () => {
1193
- count++; // Auto-synced to state!
1194
- };
1195
-
1196
- export const updateUser = (name) => {
1197
- user.name = name; // Auto-synced to state!
1198
- };
47
+ <script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
1199
48
  ```
1200
49
 
1201
- **ES Module Benefits:**
50
+ ### 2. Create a Component
1202
51
 
1203
- - Import external libraries and utilities
1204
- - ✅ Organize code across multiple files
1205
- - ✅ Automatic state initialization for binding variables
1206
- - ✅ Automatic state sync on variable mutations
1207
- - ✅ Clean separation of concerns
1208
- - ✅ Standard JavaScript module syntax
1209
-
1210
- **Example: Counter Component**
52
+ Save this as `counter.html`:
1211
53
 
1212
54
  ```html
1213
- <!-- counter.html -->
1214
- <div>
1215
- <h2>Counter: {count}</h2>
1216
- <button onclick="increment()">+1</button>
1217
- <button onclick="decrement()">-1</button>
1218
- <button onclick="reset()">Reset</button>
55
+ <div class="counter">
56
+ <div class="count-display">{count}</div>
57
+ <div class="buttons">
58
+ <button onclick="count--">−</button>
59
+ <button onclick="count = 0">Reset</button>
60
+ <button onclick="count++">+</button>
61
+ </div>
62
+ <p>Double: {count * 2} | Squared: {count * count}</p>
1219
63
  </div>
1220
64
 
1221
- <script type="module" src="./counter.js"></script>
65
+ <script>
66
+ let count = 0;
67
+ </script>
1222
68
 
1223
69
  <style>
1224
- div {
70
+ .counter {
1225
71
  text-align: center;
1226
72
  padding: 2rem;
1227
73
  }
74
+ .count-display {
75
+ font-size: 4rem;
76
+ font-weight: bold;
77
+ }
1228
78
  button {
1229
79
  padding: 0.5rem 1rem;
1230
- margin: 0.5rem;
80
+ margin: 0.25rem;
1231
81
  cursor: pointer;
1232
82
  }
1233
83
  </style>
1234
84
  ```
1235
85
 
1236
- **counter.js:**
1237
-
1238
- ```javascript
1239
- // Variables used in template are auto-initialized
1240
- let count = 0;
1241
-
1242
- // Exports are auto-attached to component for event handlers
1243
- export const increment = () => {
1244
- count++;
1245
- };
1246
-
1247
- export const decrement = () => {
1248
- count--;
1249
- };
1250
-
1251
- export const reset = () => {
1252
- count = 0;
1253
- };
1254
- ```
1255
-
1256
- #### 2. Component-Scoped Scripts
1257
-
1258
- Regular script tags execute within the component's context and have access to component state and utilities:
86
+ ### 3. Use It
1259
87
 
1260
88
  ```html
1261
- <!-- alert-button.html -->
1262
- <button onclick="increaseCount()">{title}: {count}</button>
1263
-
1264
- <!-- This script runs in the component context -->
1265
- <script src="./alert.js"></script>
89
+ <!DOCTYPE html>
90
+ <html>
91
+ <head>
92
+ <script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
93
+ <script type="module">
94
+ ladrillosjs.registerComponent("my-counter", "./counter.html");
95
+ </script>
96
+ </head>
97
+ <body>
98
+ <my-counter></my-counter>
99
+ </body>
100
+ </html>
1266
101
  ```
1267
102
 
1268
- **alert.js:**
1269
-
1270
- ```javascript
1271
- // Variables and functions are available to the component
1272
- let count = 0;
1273
- const title = "Button Count";
103
+ That's it! Your reactive component is ready. 🎉
1274
104
 
1275
- const increaseCount = () => {
1276
- count++; // Updates component state
1277
- };
1278
- ```
105
+ ---
1279
106
 
1280
- #### 3. External Libraries
107
+ ## 📦 Installation
1281
108
 
1282
- Use the `external` attribute for third-party libraries that shouldn't be bound to component context:
109
+ ### CDN (No Build Step)
1283
110
 
1284
111
  ```html
1285
- <!-- codeblock.html -->
1286
- <link
1287
- rel="stylesheet"
1288
- href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"
1289
- />
1290
- <script
1291
- src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"
1292
- external
1293
- ></script>
1294
-
1295
- <div class="code-container">
1296
- <pre><code id="code" class="language-html"></code></pre>
1297
- </div>
112
+ <!-- Global (UMD) -->
113
+ <script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
114
+ <script type="module">
115
+ ladrillosjs.registerComponent("my-component", "./component.html");
116
+ </script>
1298
117
 
1299
- <script>
1300
- // Access the external library (hljs is global)
1301
- const codeElement = $querySelector("pre code");
1302
- codeElement.textContent = "console.log('Hello')";
1303
- hljs.highlightElement(codeElement); // Use external library
118
+ <!-- ES Module -->
119
+ <script type="module">
120
+ import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
121
+ registerComponent("my-component", "./component.html");
1304
122
  </script>
1305
123
  ```
1306
124
 
1307
- **When to use `external`:**
1308
-
1309
- - Third-party CDN libraries (highlight.js, Chart.js, etc.)
1310
- - Libraries that need to be loaded globally
1311
- - Scripts that don't need component context or utilities
1312
-
1313
- #### Module Script Best Practices
1314
-
1315
- **✅ Use modules for:**
1316
-
1317
- - Component-specific logic and functions
1318
- - Local state management
1319
- - Importing utilities and helpers
1320
- - Organizing complex components
1321
-
1322
- **❌ Don't use modules for:**
1323
-
1324
- - Simple inline event handlers (use inline scripts)
1325
- - Global libraries (use `external` attribute)
1326
- - Scripts that need global scope
125
+ ### NPM (With Build Tools)
1327
126
 
1328
- **Relative Imports:**
1329
-
1330
- Modules automatically resolve relative imports to their proper URLs:
1331
-
1332
- ```javascript
1333
- // These work automatically with modules
1334
- import { helper } from "./utils.js";
1335
- import { Component } from "../components/component.js";
1336
- import { api } from "../../api/client.js";
127
+ ```bash
128
+ npm install ladrillosjs
1337
129
  ```
1338
130
 
1339
- #### Legacy: Using `$reactive` (Optional)
1340
-
1341
- The `$reactive` function is still available for explicit state initialization in edge cases:
1342
-
1343
131
  ```javascript
1344
- import { $reactive } from "ladrillosjs";
132
+ import { registerComponent, registerComponents } from "ladrillosjs";
1345
133
 
1346
- let count = 0;
1347
- const setCount = $reactive("count", count); // Manual initialization
134
+ // Single component
135
+ registerComponent("my-counter", "./components/counter.html");
1348
136
 
1349
- // Later updates
1350
- setCount(newValue);
137
+ // Multiple components
138
+ await registerComponents([
139
+ { name: "app-header", path: "./components/header.html" },
140
+ { name: "app-footer", path: "./components/footer.html" },
141
+ ]);
1351
142
  ```
1352
143
 
1353
- However, this is **no longer necessary for module scripts** since variables are automatically initialized. Use it only if you need:
144
+ ---
1354
145
 
1355
- - Explicit control over initialization timing
1356
- - Manual setter functions
1357
- - Backward compatibility with older code
146
+ ## 📖 Core Concepts
1358
147
 
1359
- #### Advanced Module Patterns
148
+ ### Template Bindings
1360
149
 
1361
- **Pattern 1: Async Data Loading**
150
+ Use `{expression}` to display reactive data. Any JavaScript expression works:
1362
151
 
1363
152
  ```html
1364
- <!-- user-profile.html -->
1365
- <div $if="{loading}">Loading user...</div>
1366
- <div $else-if="{error}">{error}</div>
1367
- <div $else>
1368
- <h1>{user.name}</h1>
1369
- <p>Email: {user.email}</p>
1370
- <p>Bio: {user.bio}</p>
1371
- </div>
1372
-
1373
- <script type="module" src="./user-profile.js"></script>
153
+ <h1>{title}</h1>
154
+ <p>Hello, {user.name}!</p>
155
+ <span>Total: {items.length} items</span>
156
+ <p>Is adult: {age >= 18 ? 'Yes' : 'No'}</p>
1374
157
  ```
1375
158
 
1376
- **user-profile.js:**
1377
-
1378
- ```javascript
1379
- // Variables auto-initialized in component state
1380
- let user = { name: "", email: "", bio: "" };
1381
- let loading = true;
1382
- let error = null;
1383
-
1384
- // Auto-exported so it's available as event handler
1385
- export const loadUser = async (userId) => {
1386
- loading = true;
1387
- error = null;
1388
-
1389
- try {
1390
- const response = await fetch(`/api/users/${userId}`);
1391
- user = await response.json();
1392
- } catch (e) {
1393
- error = e.message;
1394
- } finally {
1395
- loading = false;
1396
- }
1397
- };
1398
-
1399
- // Load user on component mount
1400
- loadUser(1);
1401
- ```
159
+ ### Reactive State
1402
160
 
1403
- **Pattern 2: Composable Logic**
161
+ Just declare variables with `let` — changes automatically update the DOM:
1404
162
 
1405
- ```javascript
1406
- // utils/counter.js
1407
- export const createCounter = (initial = 0) => {
1408
- let count = initial;
1409
-
1410
- return {
1411
- increment: () => count++,
1412
- decrement: () => count--,
1413
- reset: () => (count = initial),
1414
- get value() {
1415
- return count;
1416
- },
1417
- };
1418
- };
1419
-
1420
- // counter.js (module script)
1421
- import { createCounter } from "./utils/counter.js";
1422
-
1423
- const counter = createCounter(0);
1424
-
1425
- export const increment = () => counter.increment();
1426
- export const decrement = () => counter.decrement();
1427
- export const reset = () => counter.reset();
163
+ ```html
164
+ <script>
165
+ let count = 0;
166
+ let user = { name: "Alice", role: "Developer" };
167
+ let items = ["Apple", "Banana", "Cherry"];
168
+ </script>
1428
169
  ```
1429
170
 
1430
- **Pattern 3: Shared State Between Components**
1431
-
1432
- ```javascript
1433
- // store.js - shared module
1434
- export let globalState = {
1435
- user: null,
1436
- notifications: [],
1437
- theme: "light",
1438
- };
1439
-
1440
- export const updateUser = (user) => {
1441
- globalState.user = user;
1442
- };
171
+ ### Event Handlers
1443
172
 
1444
- export const addNotification = (msg) => {
1445
- globalState.notifications.push(msg);
1446
- };
173
+ Attach events directly in HTML with inline expressions or function calls:
1447
174
 
1448
- // In component module script:
1449
- import { globalState, updateUser } from "./store.js";
1450
-
1451
- let user = globalState.user;
1452
-
1453
- export const switchTheme = () => {
1454
- globalState.theme = globalState.theme === "light" ? "dark" : "light";
1455
- };
175
+ ```html
176
+ <button onclick="count++">Increment</button>
177
+ <button onclick="handleClick()">Click me</button>
178
+ <input onkeyup="search(event.target.value)" />
179
+ <form onsubmit="handleSubmit(event)">...</form>
1456
180
  ```
1457
181
 
1458
- **Pattern 4: Event-Driven Updates**
1459
-
1460
- ```javascript
1461
- import { $listen, $emit } from "ladrillosjs";
1462
-
1463
- let items = [];
1464
- let selectedId = null;
1465
-
1466
- $listen("item:created", (item) => {
1467
- items = [...items, item];
1468
- });
182
+ ### Two-Way Binding
1469
183
 
1470
- $listen("item:deleted", (itemId) => {
1471
- items = items.filter((i) => i.id !== itemId);
1472
- });
1473
-
1474
- export const selectItem = (id) => {
1475
- selectedId = id;
1476
- $emit("item:selected", { id, item: items.find((i) => i.id === id) });
1477
- };
1478
-
1479
- export const deleteItem = (id) => {
1480
- $emit("item:delete-request", { id });
1481
- };
1482
- ```
184
+ Use `$bind` to sync form inputs with state:
1483
185
 
1484
- ### Shadow DOM
186
+ ```html
187
+ <input type="text" $bind="username" placeholder="Enter name" />
188
+ <p>Hello, {username}!</p>
1485
189
 
1486
- Components use Shadow DOM by default for style encapsulation:
190
+ <textarea $bind="bio"></textarea>
1487
191
 
1488
- ```javascript
1489
- // Shadow DOM enabled (default)
1490
- await registerComponent("isolated-widget", "./widget.html");
1491
- await registerComponent("isolated-widget", "./widget.html", true);
192
+ <select $bind="country">
193
+ <option value="us">United States</option>
194
+ <option value="uk">United Kingdom</option>
195
+ </select>
1492
196
 
1493
- // Shadow DOM disabled
1494
- await registerComponent("global-styles", "./global.html", false);
197
+ <script>
198
+ let username = "";
199
+ let bio = "";
200
+ let country = "us";
201
+ </script>
1495
202
  ```
1496
203
 
1497
- **Shadow DOM Benefits:**
1498
-
1499
- - **Style Isolation**: Component styles don't leak to global scope
1500
- - **Encapsulation**: Internal DOM is hidden from parent
1501
- - **Clean Separation**: Each component has its own styling context
1502
-
1503
- **When to Disable:**
1504
-
1505
- - Need to apply global CSS frameworks (Bootstrap, Tailwind)
1506
- - Using third-party libraries that expect normal DOM
1507
- - Easier debugging without shadow boundaries
204
+ ---
1508
205
 
1509
- ### Styling Components
206
+ ## 🧩 Directives
1510
207
 
1511
- LadrillosJS supports multiple ways to style your components:
208
+ ### Conditional Rendering
1512
209
 
1513
- #### 1. Inline Styles (Scoped)
210
+ Use `$if`, `$else-if`, and `$else` to conditionally render elements:
1514
211
 
1515
212
  ```html
1516
- <div class="button">{label}</div>
1517
-
1518
- <style>
1519
- .button {
1520
- padding: 10px 20px;
1521
- background: #007bff;
1522
- color: white;
1523
- }
1524
- </style>
213
+ <div $if="{status === 'loading'}">Loading...</div>
214
+ <div $else-if="{status === 'error'}">Something went wrong!</div>
215
+ <div $else>Content loaded successfully!</div>
1525
216
 
1526
217
  <script>
1527
- let label = "Click me";
218
+ let status = "loading";
1528
219
  </script>
1529
220
  ```
1530
221
 
1531
- #### 2. External Stylesheets
222
+ ### Show/Hide (CSS Toggle)
1532
223
 
1533
- ```html
1534
- <link rel="stylesheet" href="./styles.css" />
1535
- <div class="container">Content</div>
1536
- ```
1537
-
1538
- #### 3. Import Fonts and External CSS
224
+ Use `$show` to toggle visibility without removing from DOM (uses `display: none`):
1539
225
 
1540
226
  ```html
1541
- <style>
1542
- @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
227
+ <div $show="{isVisible}">I can be shown or hidden</div>
1543
228
 
1544
- body {
1545
- font-family: "Roboto", sans-serif;
1546
- }
1547
- </style>
229
+ <button onclick="isVisible = !isVisible">Toggle</button>
230
+
231
+ <script>
232
+ let isVisible = true;
233
+ </script>
1548
234
  ```
1549
235
 
1550
- #### 4. Using `:host` Selector (Shadow DOM)
236
+ > **`$show` vs `$if`:** `$show` toggles CSS display (element stays in DOM), `$if` adds/removes from DOM entirely.
237
+
238
+ ### List Rendering
1551
239
 
1552
- When using Shadow DOM, style the component's container with `:host`:
240
+ Use `$for` to render lists with optional index and key:
1553
241
 
1554
242
  ```html
1555
- <style>
1556
- :host {
1557
- display: block;
1558
- padding: 1rem;
1559
- background: white;
1560
- }
243
+ <!-- Simple list -->
244
+ <ul>
245
+ <li $for="fruit in fruits">🍎 {fruit}</li>
246
+ </ul>
1561
247
 
1562
- :host(.highlighted) {
1563
- border: 2px solid gold;
1564
- }
1565
- </style>
1566
- ```
248
+ <!-- With index -->
249
+ <div $for="(item, index) in items">#{index + 1}: {item}</div>
1567
250
 
1568
- **Usage:**
251
+ <!-- Object array with key -->
252
+ <div $for="user in users" $key="user.id">
253
+ <span>{user.avatar}</span>
254
+ <span>{user.name}</span>
255
+ <span>{user.role}</span>
256
+ </div>
1569
257
 
1570
- ```html
1571
- <my-component class="highlighted"></my-component>
258
+ <script>
259
+ let fruits = ["Apple", "Banana", "Cherry"];
260
+ let items = ["First", "Second", "Third"];
261
+ let users = [
262
+ { id: 1, name: "Alice", role: "Developer", avatar: "👩‍💻" },
263
+ { id: 2, name: "Bob", role: "Designer", avatar: "👨‍🎨" },
264
+ ];
265
+ </script>
1572
266
  ```
1573
267
 
1574
- ### Performance & Caching
1575
-
1576
- LadrillosJS includes built-in performance optimizations:
268
+ ### Directives Cheat Sheet
1577
269
 
1578
- #### Component Caching
270
+ | Directive | Purpose | Example |
271
+ | ---------------- | --------------------- | ------------------------------------------------ |
272
+ | `$if` | Conditional render | `<div $if="{isLoggedIn}">Welcome!</div>` |
273
+ | `$else-if` | Chained condition | `<div $else-if="{isGuest}">Hello Guest</div>` |
274
+ | `$else` | Fallback | `<div $else>Please log in</div>` |
275
+ | `$show` | CSS visibility toggle | `<div $show="{isOpen}">Menu</div>` |
276
+ | `$for` | Loop rendering | `<li $for="item in items">{item}</li>` |
277
+ | `$for` (indexed) | Loop with index | `<li $for="(item, i) in items">{i}: {item}</li>` |
278
+ | `$bind` | Two-way binding | `<input $bind="email" />` |
279
+ | `$key` | List optimization | `<div $for="user in users" $key="user.id">` |
280
+ | `$ref` | Element reference | `<input $ref="inputEl" />` |
1579
281
 
1580
- - **LRU Cache**: Stores up to 25 most recently used components
1581
- - **Instant Loading**: Cached components load immediately
1582
- - **Reduced Network**: No repeated HTTP requests for components
282
+ ---
1583
283
 
1584
- #### Function Caching
284
+ ## 📡 Event Bus
1585
285
 
1586
- - **Template Compilation**: Caches up to 100 compiled expressions
1587
- - **Memory Efficient**: Reuses Function objects for identical expressions
1588
- - **Faster Renders**: Expressions like `{formatName(user)}` compile once
286
+ Communicate between components using `$emit` and `$listen`:
1589
287
 
1590
- **Performance Tips:**
288
+ ### Sender Component
1591
289
 
1592
- - Component files are cached after first load
1593
- - State updates trigger minimal DOM changes
1594
- - Event listeners are automatically cleaned up
1595
- - Conditional rendering skips hidden elements
290
+ ```html
291
+ <button onclick="sendMessage()">Send Message</button>
1596
292
 
1597
- ## Common Patterns
293
+ <script>
294
+ let message = "Hello from sender!";
1598
295
 
1599
- ### Keyboard Events
296
+ function sendMessage() {
297
+ $emit("my-event", { text: message, time: new Date().toLocaleTimeString() });
298
+ }
299
+ </script>
300
+ ```
1600
301
 
1601
- Handle keyboard input for interactive features:
302
+ ### Receiver Component
1602
303
 
1603
304
  ```html
1604
305
  <div>
1605
- <p>Press arrow keys to navigate. Current: {direction}</p>
1606
- <p>Press Enter to submit</p>
306
+ <p>Received: {receivedMessage}</p>
1607
307
  </div>
1608
308
 
1609
309
  <script>
1610
- let direction = "none";
1611
-
1612
- document.addEventListener("keyup", (event) => {
1613
- if (event.key === "ArrowRight") {
1614
- direction = "right";
1615
- } else if (event.key === "ArrowLeft") {
1616
- direction = "left";
1617
- } else if (event.key === "ArrowUp") {
1618
- direction = "up";
1619
- } else if (event.key === "ArrowDown") {
1620
- direction = "down";
1621
- } else if (event.key === "Enter") {
1622
- direction = "submitted!";
1623
- }
310
+ let receivedMessage = "Waiting...";
311
+
312
+ $listen("my-event", (data) => {
313
+ receivedMessage = data.text;
1624
314
  });
1625
315
  </script>
1626
316
  ```
1627
317
 
1628
- ### Form Validation
318
+ ---
319
+
320
+ ## 🏷️ Element References
1629
321
 
1630
- Real-time form validation with reactive state:
322
+ Use `$ref` to get direct DOM access for advanced manipulation:
1631
323
 
1632
324
  ```html
1633
- <form onsubmit="handleSubmit(event)">
1634
- <input type="email" $bind="email" placeholder="Email" onkeyup="validate()" />
1635
- <p $if="{!isValid}" style="color: red">Please enter a valid email</p>
1636
- <button type="submit" $if="{isValid}">Submit</button>
1637
- </form>
325
+ <input type="text" $ref="inputEl" placeholder="Click button to focus" />
326
+ <button onclick="focusInput()">Focus Input</button>
1638
327
 
1639
- <script>
1640
- let email = "";
1641
- let isValid = false;
328
+ <canvas $ref="canvas" width="200" height="100"></canvas>
329
+ <button onclick="draw()">Draw on Canvas</button>
1642
330
 
1643
- const validate = () => {
1644
- isValid = email.includes("@") && email.length > 5;
1645
- };
331
+ <script>
332
+ function focusInput() {
333
+ $refs.inputEl.focus();
334
+ $refs.inputEl.select();
335
+ }
1646
336
 
1647
- const handleSubmit = (e) => {
1648
- e.preventDefault();
1649
- if (isValid) {
1650
- alert(`Submitted: ${email}`);
1651
- }
1652
- };
337
+ function draw() {
338
+ const ctx = $refs.canvas.getContext("2d");
339
+ ctx.fillStyle = "blue";
340
+ ctx.fillRect(10, 10, 100, 50);
341
+ }
1653
342
  </script>
1654
343
  ```
1655
344
 
1656
- ### Loading States
345
+ ---
1657
346
 
1658
- Show loading indicators while fetching data:
347
+ ## Lazy Loading
1659
348
 
1660
- ```html
1661
- <div>
1662
- <div $if="{isLoading}">Loading...</div>
1663
- <div $else-if="{error}">Error: {error}</div>
1664
- <div $else>
1665
- <h2>{data.title}</h2>
1666
- <p>{data.description}</p>
1667
- </div>
1668
- <button onclick="fetchData()">Refresh</button>
1669
- </div>
349
+ Load components only when needed to improve initial page load:
1670
350
 
1671
- <script>
1672
- let isLoading = false;
1673
- let error = null;
1674
- let data = { title: "", description: "" };
1675
-
1676
- const fetchData = async () => {
1677
- isLoading = true;
1678
- error = null;
1679
-
1680
- try {
1681
- const response = await fetch("https://api.example.com/data");
1682
- data = await response.json();
1683
- } catch (e) {
1684
- error = e.message;
1685
- } finally {
1686
- isLoading = false;
1687
- }
1688
- };
1689
- </script>
1690
- ```
351
+ ### Lazy Loading Strategies
352
+
353
+ ```javascript
354
+ import {
355
+ registerComponents,
356
+ lazyOnVisible,
357
+ lazyOnIdle,
358
+ lazyOnInteraction,
359
+ lazyOnMedia,
360
+ lazyOnDelay,
361
+ } from "ladrillosjs";
1691
362
 
1692
- ## API Reference
1693
-
1694
- ### Registration Functions
1695
-
1696
- ```typescript
1697
- registerComponent(
1698
- name: string,
1699
- path: string,
1700
- useShadowDOM?: boolean,
1701
- lazy?: boolean
1702
- ): Promise<void>
1703
-
1704
- registerComponents(
1705
- components: Array<{
1706
- name: string,
1707
- path: string,
1708
- useShadowDOM?: boolean,
1709
- lazy?: boolean
1710
- }>
1711
- ): Promise<void>
363
+ await registerComponents([
364
+ // Load when visible in viewport
365
+ {
366
+ name: "lazy-footer",
367
+ path: "./footer.html",
368
+ lazy: lazyOnVisible({ rootMargin: "100px" }),
369
+ },
370
+
371
+ // Load when browser is idle
372
+ {
373
+ name: "analytics-widget",
374
+ path: "./analytics.html",
375
+ lazy: lazyOnIdle(5000), // timeout: 5s max wait
376
+ },
377
+
378
+ // Load on user interaction
379
+ {
380
+ name: "modal-dialog",
381
+ path: "./modal.html",
382
+ lazy: lazyOnInteraction(["click", "focusin"]),
383
+ },
384
+
385
+ // Load based on media query
386
+ {
387
+ name: "mobile-nav",
388
+ path: "./mobile-nav.html",
389
+ lazy: lazyOnMedia("(max-width: 768px)"),
390
+ },
391
+
392
+ // Load after delay
393
+ {
394
+ name: "chat-widget",
395
+ path: "./chat.html",
396
+ lazy: lazyOnDelay(3000), // 3 second delay
397
+ },
398
+ ]);
1712
399
  ```
1713
400
 
1714
- **Parameters:**
401
+ | Strategy | Use Case |
402
+ | ------------------- | -------------------------------------------- |
403
+ | `lazyOnVisible` | Below-fold content, footers, image galleries |
404
+ | `lazyOnIdle` | Non-critical features, analytics |
405
+ | `lazyOnInteraction` | Modals, dropdowns, tooltips |
406
+ | `lazyOnMedia` | Mobile/desktop specific components |
407
+ | `lazyOnDelay` | Chat widgets, notifications |
408
+
409
+ ---
410
+
411
+ ## 📋 API Reference
412
+
413
+ ### registerComponent
1715
414
 
1716
- - `name`: Component tag name (must include a hyphen)
1717
- - `path`: Path to the component file
1718
- - `useShadowDOM`: Use Shadow DOM for style encapsulation (default: `true`)
1719
- - `lazy`: Enable lazy loading with Intersection Observer (default: `false`)
415
+ ```javascript
416
+ registerComponent(name, path, useShadowDOM?, lazy?)
417
+ ```
1720
418
 
1721
- **Examples:**
419
+ | Parameter | Type | Default | Description |
420
+ | -------------- | ----------------------- | -------- | ------------------------------- |
421
+ | `name` | string | required | Tag name (must include hyphen) |
422
+ | `path` | string | required | Path to `.html` component file |
423
+ | `useShadowDOM` | boolean | `true` | Enable Shadow DOM encapsulation |
424
+ | `lazy` | boolean \| LazyStrategy | `false` | Lazy loading configuration |
1722
425
 
1723
426
  ```javascript
1724
- // Basic registration
1725
- await registerComponent("my-button", "./button.html");
427
+ // Basic usage
428
+ registerComponent("my-button", "./button.html");
1726
429
 
1727
- // Disable Shadow DOM
1728
- await registerComponent("global-nav", "./nav.html", false);
430
+ // Without Shadow DOM (for global CSS)
431
+ registerComponent("my-nav", "./nav.html", false);
1729
432
 
1730
- // Enable lazy loading
1731
- await registerComponent("footer", "./footer.html", true, true);
433
+ // With lazy loading
434
+ registerComponent("my-footer", "./footer.html", true, lazyOnVisible());
435
+ ```
1732
436
 
1733
- // Batch registration
1734
- await registerComponents([
1735
- { name: "hero", path: "./hero.html" },
1736
- { name: "features", path: "./features.html", lazy: true },
1737
- { name: "footer", path: "./footer.html", useShadowDOM: false, lazy: true },
437
+ ### registerComponents
438
+
439
+ Register multiple components with parallel fetching:
440
+
441
+ ```javascript
442
+ const result = await registerComponents([
443
+ { name: "app-header", path: "./header.html" },
444
+ { name: "app-footer", path: "./footer.html", lazy: lazyOnVisible() },
445
+ { name: "user-card", path: "./user-card.html", useShadowDOM: false },
1738
446
  ]);
447
+
448
+ // Returns: { success: [...], failed: [...], skipped: [...] }
1739
449
  ```
1740
450
 
1741
- ### Component Utilities
451
+ ### Event Bus
1742
452
 
1743
- Available within component `<script>` tags:
453
+ | Function | Description |
454
+ | -------------------------- | ----------------------------------- |
455
+ | `$emit(event, data)` | Broadcast an event to all listeners |
456
+ | `$listen(event, callback)` | Subscribe to an event |
1744
457
 
1745
- ```typescript
1746
- // State management
1747
- $getState(): object // Access component state
1748
- $setState(updates: object): void // Update state and re-render
458
+ ---
1749
459
 
1750
- // Event bus
1751
- $emit(eventName: string, data?: any): void
1752
- $listen(eventName: string, callback: (data?) => void): () => void
460
+ ## 🛠️ Using with Vite
1753
461
 
1754
- // DOM queries (respects Shadow DOM boundaries)
1755
- $querySelector(selector: string): Element | null
1756
- $querySelectorAll(selector: string): NodeListOf<Element>
462
+ LadrillosJS works seamlessly with Vite for production builds:
1757
463
 
1758
- // Manual reactive initialization (optional - mainly for backward compatibility)
1759
- // Module scripts automatically initialize and sync variables
1760
- $reactive(name: string, initialValue: any): (value: any) => void
464
+ ```bash
465
+ npm install ladrillosjs vite
1761
466
  ```
1762
467
 
1763
- ### Component Attributes
468
+ ```javascript
469
+ // vite.config.js
470
+ import { defineConfig } from "vite";
1764
471
 
1765
- ```html
1766
- <!-- Two-way data binding -->
1767
- <input $bind="variableName" />
1768
-
1769
- <!-- Conditional rendering -->
1770
- <div $if="{condition}">...</div>
1771
- <div $else-if="{anotherCondition}">...</div>
1772
- <div $else>...</div>
1773
-
1774
- <!-- List rendering -->
1775
- <li $for="item in items">{item}</li>
1776
- <li $for="(item, index) in items">{index}: {item}</li>
1777
- <div $for="user in users" $key="user.id">{user.name}</div>
1778
-
1779
- <!-- Lazy loading override -->
1780
- <footer-section eager></footer-section>
1781
- <!-- Force immediate load -->
1782
-
1783
- <!-- Event handlers -->
1784
- <button onclick="methodName()">Click</button>
1785
- <button onclick="method(arg1, arg2)">Call with args</button>
1786
- <button onclick="console.log(event)">Inline function</button>
1787
-
1788
- <!-- Slots -->
1789
- <slot></slot>
1790
- <!-- Default slot -->
1791
- <slot name="header"></slot>
1792
- <!-- Named slot -->
472
+ export default defineConfig({
473
+ // LadrillosJS components work out of the box!
474
+ });
1793
475
  ```
1794
476
 
1795
- ### Template Syntax
477
+ ```javascript
478
+ // main.js
479
+ import { registerComponent } from "ladrillosjs";
1796
480
 
1797
- ```html
1798
- <!-- Variable interpolation -->
1799
- {variableName} {object.property} {array[0]}
481
+ registerComponent("my-counter", "./components/counter.html", false);
482
+ ```
1800
483
 
1801
- <!-- Function calls -->
1802
- {functionName(arg1, arg2)} {object.method()}
484
+ See the [samples/](samples/) directory for complete examples:
1803
485
 
1804
- <!-- Expressions -->
1805
- {count + 1} {isActive ? 'Yes' : 'No'} {items.length > 0 ? 'Has items' : 'Empty'}
1806
- ```
486
+ - `samples/vite-sample/` — Basic Vite setup
487
+ - `samples/vite-basic-site/` Multi-component site
488
+ - `samples/ladrillos-demo/` — Full feature showcase
1807
489
 
1808
- ## Development
490
+ ---
1809
491
 
1810
- ### Prerequisites
492
+ ## 📚 Examples
1811
493
 
1812
- - **Node.js**: v20.19+ or v22.12+
1813
- - **npm**: v9+ (comes with Node.js)
494
+ ### Todo List
1814
495
 
1815
- ### Setup
496
+ A complete CRUD example combining all directives:
1816
497
 
1817
- ```bash
1818
- # Clone the repository
1819
- git clone https://github.com/drubiodev/LadrillosJS.git
1820
- cd LadrillosJS
498
+ ```html
499
+ <div class="todo-app">
500
+ <form onsubmit="addTodo(event)">
501
+ <input type="text" $bind="newTodo" placeholder="What needs to be done?" />
502
+ <button type="submit">Add</button>
503
+ </form>
1821
504
 
1822
- # Install dependencies
1823
- npm install
505
+ <ul>
506
+ <li $for="todo in todos" $key="todo.id">
507
+ <input type="checkbox" onclick="toggleTodo({todo.id})" />
508
+ <span class="{todo.completed ? 'done' : ''}">{todo.text}</span>
509
+ <button onclick="removeTodo({todo.id})">🗑️</button>
510
+ </li>
511
+ </ul>
1824
512
 
1825
- # Start development server
1826
- npm run dev
513
+ <p $if="{todos.length === 0}">No todos yet!</p>
514
+ </div>
1827
515
 
1828
- # Build the library
1829
- npm run build
516
+ <script>
517
+ let todos = [
518
+ { id: 1, text: "Learn LadrillosJS", completed: false },
519
+ { id: 2, text: "Build something awesome", completed: false },
520
+ ];
521
+ let newTodo = "";
522
+ let nextId = 3;
523
+
524
+ function addTodo(event) {
525
+ event.preventDefault();
526
+ if (newTodo.trim()) {
527
+ todos = [...todos, { id: nextId++, text: newTodo, completed: false }];
528
+ newTodo = "";
529
+ }
530
+ }
1830
531
 
1831
- # Run tests
1832
- npm test
532
+ function toggleTodo(id) {
533
+ todos = todos.map((t) =>
534
+ t.id === id ? { ...t, completed: !t.completed } : t
535
+ );
536
+ }
1833
537
 
1834
- # Run tests with coverage
1835
- npm run test:coverage
538
+ function removeTodo(id) {
539
+ todos = todos.filter((t) => t.id !== id);
540
+ }
541
+ </script>
1836
542
  ```
1837
543
 
1838
- ### Project Structure
544
+ > 💡 Check the [samples/](samples/) folder for more examples including lazy loading, event bus communication, and real-world patterns.
1839
545
 
1840
- ```
1841
- LadrillosJS/
1842
- ├── src/
1843
- │ ├── index.ts # Main entry point
1844
- │ ├── core/
1845
- │ │ ├── main.ts # Core Ladrillos class
1846
- │ │ ├── webcomponent.ts # Web component wrapper
1847
- │ │ ├── componentParser.ts # Parse component files
1848
- │ │ ├── componentSource.ts # Fetch with caching
1849
- │ │ ├── eventBus.ts # Global event bus
1850
- │ │ ├── css/
1851
- │ │ │ └── cssParser.ts
1852
- │ │ ├── html/
1853
- │ │ │ ├── htmlparser.ts
1854
- │ │ │ └── htmlRenderer.ts
1855
- │ │ └── js/
1856
- │ │ └── scriptParser.ts
1857
- │ ├── cache/
1858
- │ │ ├── index.ts # LRU component cache
1859
- │ │ └── functionCache.ts # LRU function cache
1860
- │ ├── types/
1861
- │ │ └── LadrilloTypes.ts
1862
- │ └── utils/
1863
- │ ├── logger.ts
1864
- │ └── regex.ts
1865
- ├── samples/ # Example applications
1866
- │ └── apps/
1867
- │ ├── todo/
1868
- │ ├── notes/
1869
- │ ├── simple-button/
1870
- │ └── ...
1871
- ├── test/ # Test files
1872
- ├── dist/ # Built files
1873
- ├── package.json
1874
- ├── tsconfig.json
1875
- ├── vite.config.js
1876
- └── vitest.config.js
1877
- ```
546
+ ---
1878
547
 
1879
- ### NPM Scripts
548
+ ## 🤝 Contributing
549
+
550
+ Contributions are welcome! Here's how you can help:
551
+
552
+ 1. **Fork** the repository
553
+ 2. **Create** a feature branch: `git checkout -b feature/amazing-feature`
554
+ 3. **Commit** your changes: `git commit -m 'Add amazing feature'`
555
+ 4. **Push** to the branch: `git push origin feature/amazing-feature`
556
+ 5. **Open** a Pull Request
557
+
558
+ ### Development Setup
1880
559
 
1881
560
  ```bash
1882
- npm run dev # Start Vite dev server
1883
- npm run build # Build library (ESM, UMD, CJS)
1884
- npm run build:types # Generate TypeScript declarations
1885
- npm test # Run tests with Vitest
1886
- npm run test:coverage # Run tests with coverage report
1887
- npm run preview # Preview production build
561
+ git clone https://github.com/drubiodev/LadrillosJS.git
562
+ cd LadrillosJS
563
+ npm install
564
+ npm run dev # Watch mode for development
565
+ npm run build # Build for production
1888
566
  ```
1889
567
 
1890
- ## Attribution
1891
-
1892
- 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!
568
+ ---
1893
569
 
1894
- ## License
570
+ ## 📄 License
1895
571
 
1896
- MIT License - see [LICENSE](LICENSE) file for details.
572
+ MIT License see [LICENSE](LICENSE) for details.
1897
573
 
1898
574
  ---
1899
575
 
1900
- **LadrillosJS v2.0** - Built with ❤️ by [Daniel Rubio](https://github.com/drubiodev)
576
+ <p align="center">
577
+ <strong>Built with ❤️ by <a href="https://github.com/drubiodev">Daniel Rubio</a></strong>
578
+ </p>
1901
579
 
1902
- 🌟 [GitHub](https://github.com/drubiodev/LadrillosJS) • 📦 [NPM](https://www.npmjs.com/package/ladrillosjs) • 📖 [Documentation](https://github.com/drubiodev/LadrillosJS/blob/main/README.md)
580
+ <p align="center">
581
+ <a href="https://github.com/drubiodev/LadrillosJS">GitHub</a> •
582
+ <a href="https://www.npmjs.com/package/ladrillosjs">NPM</a> •
583
+ <a href="https://github.com/drubiodev/LadrillosJS/issues">Issues</a>
584
+ </p>