ladrillosjs 1.0.1 โ 2.0.0-beta.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.
- package/README.md +533 -105
- package/dist/cache/functionCache.d.ts +15 -0
- package/dist/cache/index.d.ts +16 -0
- package/dist/core/componentParser.d.ts +29 -0
- package/dist/core/componentSource.d.ts +12 -0
- package/dist/core/css/cssParser.d.ts +3 -0
- package/dist/core/eventBus.d.ts +41 -0
- package/dist/core/html/htmlRenderer.d.ts +18 -0
- package/dist/core/html/htmlparser.d.ts +16 -0
- package/dist/core/js/scriptParser.d.ts +3 -0
- package/dist/core/main.d.ts +4 -26
- package/dist/core/webcomponent.d.ts +2 -2
- package/dist/index-CXHidyhO.js +8 -0
- package/dist/index-CXHidyhO.js.map +1 -0
- package/dist/index-VkDZJVOR.mjs +361 -0
- package/dist/index-VkDZJVOR.mjs.map +1 -0
- package/dist/index.d.ts +56 -6
- package/dist/ladrillosjs.cjs.js +2 -1
- package/dist/ladrillosjs.cjs.js.map +1 -0
- package/dist/ladrillosjs.es.js +12 -4
- package/dist/ladrillosjs.es.js.map +1 -0
- package/dist/ladrillosjs.umd.js +76 -18
- package/dist/ladrillosjs.umd.js.map +1 -0
- package/dist/types/LadrilloTypes.d.ts +47 -14
- package/dist/utils/logger.d.ts +14 -11
- package/dist/utils/regex.d.ts +2 -0
- package/dist/webcomponent-CJ3lZBZb.mjs +703 -0
- package/dist/webcomponent-CJ3lZBZb.mjs.map +1 -0
- package/dist/webcomponent-i9W7LUiv.js +70 -0
- package/dist/webcomponent-i9W7LUiv.js.map +1 -0
- package/package.json +8 -4
- package/dist/core/store.d.ts +0 -6
- package/dist/index-46umVYfZ.mjs +0 -215
- package/dist/index-dECyHgvF.js +0 -3
- package/dist/utils/stringify.d.ts +0 -7
- package/dist/webcomponent-Dntygkjn.js +0 -16
- package/dist/webcomponent-lvbLxIns.mjs +0 -757
package/README.md
CHANGED
|
@@ -4,12 +4,17 @@
|
|
|
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.
|
|
8
|
+
|
|
7
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."
|
|
8
10
|
|
|
9
11
|
## Table of Contents
|
|
10
12
|
|
|
11
13
|
- [Features](#features)
|
|
12
14
|
- [Getting Started](#getting-started)
|
|
15
|
+
- [Prerequisites](#prerequisites)
|
|
16
|
+
- [What's New in v2.0](#whats-new-in-v20)
|
|
17
|
+
- [Example Applications](#example-applications)
|
|
13
18
|
- [Installation](#installation)
|
|
14
19
|
- [Your First Component](#your-first-component)
|
|
15
20
|
- [Core Concepts](#core-concepts)
|
|
@@ -21,10 +26,15 @@ A lightweight, zero-dependency web component framework for building modular web
|
|
|
21
26
|
- [Slots](#slots)
|
|
22
27
|
- [Advanced Features](#advanced-features)
|
|
23
28
|
- [External Scripts](#external-scripts)
|
|
24
|
-
- [Global
|
|
29
|
+
- [Global Event Bus](#global-event-bus)
|
|
25
30
|
- [Shadow DOM](#shadow-dom)
|
|
31
|
+
- [Performance & Caching](#performance--caching)
|
|
26
32
|
- [API Reference](#api-reference)
|
|
27
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)
|
|
28
38
|
- [Contributing](#contributing)
|
|
29
39
|
- [License](#license)
|
|
30
40
|
|
|
@@ -32,25 +42,51 @@ A lightweight, zero-dependency web component framework for building modular web
|
|
|
32
42
|
|
|
33
43
|
- ๐ **Zero Dependencies** - Pure JavaScript, no build tools required
|
|
34
44
|
- ๐ฆ **Single-File Components** - HTML, CSS, and JavaScript in one file
|
|
35
|
-
- โก **Reactive State** - Automatic re-rendering on state changes
|
|
36
|
-
- ๐ฏ **Event System** - Built-in event emission and
|
|
37
|
-
- ๐ **Two-Way Data Binding** -
|
|
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`
|
|
38
48
|
- ๐จ **Scoped Styles** - Component styles with optional Shadow DOM
|
|
39
|
-
- ๐ช **
|
|
49
|
+
- ๐ช **Global Event Bus** - Cross-component communication without prop drilling
|
|
40
50
|
- ๐ **Slots Support** - Content projection with named and default slots
|
|
41
|
-
- ๐ **TypeScript Support** -
|
|
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
|
|
42
57
|
|
|
43
58
|
## Getting Started
|
|
44
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
|
+
|
|
45
80
|
The repository includes several example applications that demonstrate various features:
|
|
46
81
|
|
|
47
82
|
- **[Todo App](samples/apps/todo)** - Classic todo list with component composition
|
|
48
|
-
- **[Notes App](samples/apps/notes)** - Multi-component app with global
|
|
83
|
+
- **[Notes App](samples/apps/notes)** - Multi-component app with global event bus
|
|
49
84
|
- **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
|
|
50
85
|
- **[API Example](samples/apps/api)** - Fetching and displaying external data
|
|
51
|
-
- **[Business Card](samples/apps/biz)** - Editable form with two-way data binding
|
|
86
|
+
- **[Business Card](samples/apps/biz)** - Editable form with two-way data binding using `$bind`
|
|
52
87
|
- **[Button Game](samples/apps/button-game)** - Interactive game with component events
|
|
53
88
|
- **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation system
|
|
89
|
+
- **[Document Chat](samples/apps/document-chat)** - Chat interface with component communication
|
|
54
90
|
- **[Docs](samples/apps/docs)** - Documentation viewer with syntax highlighting
|
|
55
91
|
|
|
56
92
|
To run the examples:
|
|
@@ -60,11 +96,20 @@ To run the examples:
|
|
|
60
96
|
git clone https://github.com/drubiodev/LadrillosJS.git
|
|
61
97
|
cd LadrillosJS
|
|
62
98
|
|
|
63
|
-
# Install dependencies
|
|
99
|
+
# Install dependencies
|
|
64
100
|
npm install
|
|
65
101
|
|
|
66
|
-
# Start the development server
|
|
102
|
+
# Start the development server (Vite)
|
|
67
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
|
|
68
113
|
```
|
|
69
114
|
|
|
70
115
|
## Installation
|
|
@@ -78,7 +123,18 @@ npm install ladrillosjs
|
|
|
78
123
|
### CDN
|
|
79
124
|
|
|
80
125
|
```html
|
|
81
|
-
|
|
126
|
+
<!-- Latest version -->
|
|
127
|
+
<script type="module">
|
|
128
|
+
import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
|
|
129
|
+
registerComponent("my-component", "./my-component.html");
|
|
130
|
+
</script>
|
|
131
|
+
|
|
132
|
+
<!-- UMD (for legacy browsers) -->
|
|
133
|
+
<script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
|
|
134
|
+
<script>
|
|
135
|
+
// Access via global ladrillosjs object
|
|
136
|
+
ladrillosjs.registerComponent("my-component", "./my-component.html");
|
|
137
|
+
</script>
|
|
82
138
|
```
|
|
83
139
|
|
|
84
140
|
## Your First Component
|
|
@@ -156,50 +212,67 @@ Register single or multiple components:
|
|
|
156
212
|
```javascript
|
|
157
213
|
// Single component
|
|
158
214
|
import { registerComponent } from "ladrillosjs";
|
|
159
|
-
registerComponent("my-component", "./my-component.html");
|
|
215
|
+
await registerComponent("my-component", "./my-component.html");
|
|
160
216
|
|
|
161
|
-
// Multiple components
|
|
217
|
+
// Multiple components (v2.0 feature - currently in development)
|
|
162
218
|
import { registerComponents } from "ladrillosjs";
|
|
163
|
-
await registerComponents(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
],
|
|
169
|
-
10
|
|
170
|
-
); // Max 10 parallel fetches (default: 5)
|
|
219
|
+
await registerComponents([
|
|
220
|
+
{ name: "app-header", path: "./components/header.html" },
|
|
221
|
+
{ name: "app-footer", path: "./components/footer.html" },
|
|
222
|
+
{ name: "user-card", path: "./components/user-card.html" },
|
|
223
|
+
]);
|
|
171
224
|
|
|
172
225
|
// Using CDN
|
|
173
226
|
ladrillosjs.registerComponent("my-component", "./my-component.html");
|
|
174
227
|
```
|
|
175
228
|
|
|
229
|
+
**Note:** The `registerComponents` function is planned for v2.0 to enable bulk registration with concurrency control.
|
|
230
|
+
|
|
176
231
|
### State Management
|
|
177
232
|
|
|
178
|
-
Components have reactive state that automatically triggers re-renders:
|
|
233
|
+
Components have reactive state that automatically triggers re-renders. In v2.0, variable assignments are automatically tracked and trigger reactivity:
|
|
179
234
|
|
|
180
235
|
```html
|
|
181
236
|
<div>
|
|
182
237
|
<h2>User: {user.name}</h2>
|
|
183
238
|
<p>Score: {score}</p>
|
|
184
239
|
<button onclick="updateScore">Add Point</button>
|
|
240
|
+
<button onclick="increment">Count: {count}</button>
|
|
185
241
|
</div>
|
|
186
242
|
|
|
187
243
|
<script>
|
|
188
|
-
|
|
189
|
-
const formattedDate = date.toLocaleDateString("en-US"); // Format: MM/DD/YYYY
|
|
190
|
-
// Initial state
|
|
244
|
+
// Initial state - automatically tracked
|
|
191
245
|
let score = 0;
|
|
246
|
+
let count = 0;
|
|
192
247
|
let user = {
|
|
193
248
|
name: "Player 1",
|
|
194
249
|
};
|
|
195
250
|
|
|
196
251
|
const updateScore = () => {
|
|
197
|
-
//
|
|
252
|
+
// Direct assignment triggers re-render automatically
|
|
198
253
|
score++;
|
|
199
254
|
};
|
|
255
|
+
|
|
256
|
+
const increment = () => {
|
|
257
|
+
// All these automatically trigger re-renders in v2.0
|
|
258
|
+
count++; // Increment
|
|
259
|
+
count += 5; // Compound assignment
|
|
260
|
+
count = count * 2; // Direct assignment
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// You can also use the explicit setState method
|
|
264
|
+
const updateUser = () => {
|
|
265
|
+
$setState({ user: { name: "Jane", age: 30 } });
|
|
266
|
+
};
|
|
200
267
|
</script>
|
|
201
268
|
```
|
|
202
269
|
|
|
270
|
+
**New in v2.0:**
|
|
271
|
+
|
|
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)
|
|
275
|
+
|
|
203
276
|
### Event Handling
|
|
204
277
|
|
|
205
278
|
Multiple ways to handle events:
|
|
@@ -214,9 +287,12 @@ Multiple ways to handle events:
|
|
|
214
287
|
<!-- Inline arrow function -->
|
|
215
288
|
<button onclick="(e) => console.log(e.target)">Log Target</button>
|
|
216
289
|
|
|
290
|
+
<!-- Component communication with event bus (v2.0) -->
|
|
291
|
+
<button onclick="notifyOthers">Emit Event</button>
|
|
292
|
+
|
|
217
293
|
<script>
|
|
218
|
-
|
|
219
|
-
|
|
294
|
+
let count = 0;
|
|
295
|
+
let items = [];
|
|
220
296
|
|
|
221
297
|
const handleClick = (event) => {
|
|
222
298
|
console.log("Clicked!", event);
|
|
@@ -224,9 +300,92 @@ Multiple ways to handle events:
|
|
|
224
300
|
};
|
|
225
301
|
|
|
226
302
|
const addItem = (name, value) => {
|
|
227
|
-
|
|
303
|
+
items = [...items, { name, value }];
|
|
228
304
|
};
|
|
305
|
+
|
|
306
|
+
// v2.0: Use $emit to send events to other components
|
|
307
|
+
const notifyOthers = () => {
|
|
308
|
+
$emit("item-added", { count, timestamp: Date.now() });
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
// v2.0: Listen for events from other components
|
|
312
|
+
$listen("user-logged-in", (data) => {
|
|
313
|
+
console.log("User logged in:", data);
|
|
314
|
+
count = 0; // Reset count
|
|
315
|
+
});
|
|
316
|
+
</script>
|
|
317
|
+
```
|
|
318
|
+
|
|
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
|
+
### Data Binding
|
|
326
|
+
|
|
327
|
+
LadrillosJS supports both one-way and two-way data binding:
|
|
328
|
+
|
|
329
|
+
#### One-Way Binding (Template Interpolation)
|
|
330
|
+
|
|
331
|
+
```html
|
|
332
|
+
<div>
|
|
333
|
+
<h1>{title}</h1>
|
|
334
|
+
<p>{user.name} - {user.email}</p>
|
|
335
|
+
<span>Items: {items.length}</span>
|
|
336
|
+
</div>
|
|
337
|
+
|
|
338
|
+
<script>
|
|
339
|
+
let title = "My App";
|
|
340
|
+
let user = { name: "John", email: "john@example.com" };
|
|
341
|
+
let items = [1, 2, 3];
|
|
342
|
+
</script>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Two-Way Binding (v2.0 Enhanced)
|
|
346
|
+
|
|
347
|
+
Use the `$bind` prefix to create automatic two-way bindings with form inputs:
|
|
348
|
+
|
|
349
|
+
```html
|
|
350
|
+
<div>
|
|
351
|
+
<h2>Hello, {$name}!</h2>
|
|
352
|
+
<input type="text" $bind="name" placeholder="Enter your name" />
|
|
353
|
+
|
|
354
|
+
<p>Email: {$email}</p>
|
|
355
|
+
<input type="email" $bind="email" />
|
|
356
|
+
|
|
357
|
+
<p>Bio: {$bio}</p>
|
|
358
|
+
<textarea $bind="bio"></textarea>
|
|
359
|
+
|
|
360
|
+
<p>Country: {$country}</p>
|
|
361
|
+
<select $bind="country">
|
|
362
|
+
<option value="us">United States</option>
|
|
363
|
+
<option value="uk">United Kingdom</option>
|
|
364
|
+
<option value="ca">Canada</option>
|
|
365
|
+
</select>
|
|
366
|
+
</div>
|
|
367
|
+
|
|
368
|
+
<script>
|
|
369
|
+
// Variables with $bind are automatically synced with inputs
|
|
370
|
+
let $name = "World";
|
|
371
|
+
let $email = "";
|
|
372
|
+
let $bio = "";
|
|
373
|
+
let $country = "us";
|
|
229
374
|
</script>
|
|
375
|
+
```
|
|
376
|
+
|
|
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
|
+
### Conditional Rendering
|
|
385
|
+
|
|
386
|
+
Control element visibility with conditional directives:
|
|
387
|
+
|
|
388
|
+
```html
|
|
230
389
|
<div>
|
|
231
390
|
<h1>Shopping Cart ({items.length} items)</h1>
|
|
232
391
|
|
|
@@ -245,20 +404,27 @@ Multiple ways to handle events:
|
|
|
245
404
|
<button data-if="!isLoggedIn" onclick="login">Login</button>
|
|
246
405
|
<button data-else onclick="logout">Logout</button>
|
|
247
406
|
</div>
|
|
407
|
+
|
|
248
408
|
<script>
|
|
249
|
-
|
|
250
|
-
|
|
409
|
+
let items = ["apple", "banana"];
|
|
410
|
+
let isLoggedIn = false;
|
|
251
411
|
|
|
252
|
-
|
|
412
|
+
const login = () => {
|
|
253
413
|
isLoggedIn = true;
|
|
254
|
-
}
|
|
414
|
+
};
|
|
255
415
|
|
|
256
|
-
|
|
416
|
+
const logout = () => {
|
|
257
417
|
isLoggedIn = false;
|
|
258
|
-
}
|
|
418
|
+
};
|
|
259
419
|
</script>
|
|
260
420
|
```
|
|
261
421
|
|
|
422
|
+
**Conditional Directives:**
|
|
423
|
+
|
|
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
|
|
427
|
+
|
|
262
428
|
### Slots
|
|
263
429
|
|
|
264
430
|
Content projection using slots:
|
|
@@ -290,7 +456,7 @@ Content projection using slots:
|
|
|
290
456
|
|
|
291
457
|
### External Scripts
|
|
292
458
|
|
|
293
|
-
Load external JavaScript with components:
|
|
459
|
+
Load external JavaScript with components and bind them to the component context:
|
|
294
460
|
|
|
295
461
|
```html
|
|
296
462
|
<!-- With 'bind' attribute for component context -->
|
|
@@ -299,84 +465,156 @@ Load external JavaScript with components:
|
|
|
299
465
|
<!-- ES modules with bind -->
|
|
300
466
|
<script src="./component-logic.js" type="module" bind></script>
|
|
301
467
|
|
|
302
|
-
<!-- Regular external script -->
|
|
468
|
+
<!-- Regular external script (global scope) -->
|
|
303
469
|
<script src="https://cdn.example.com/library.js"></script>
|
|
304
470
|
```
|
|
305
471
|
|
|
306
|
-
For modules with `bind`, export a default function:
|
|
472
|
+
For modules with `bind`, export a default function that receives the component context:
|
|
307
473
|
|
|
308
474
|
```javascript
|
|
309
475
|
// component-logic.js
|
|
310
476
|
export default function () {
|
|
311
477
|
// 'this' refers to the component instance
|
|
478
|
+
// Access component utilities
|
|
479
|
+
const { $state, $setState, $emit, $listen } = this;
|
|
480
|
+
|
|
312
481
|
this.formatDate = (date) => {
|
|
313
482
|
return new Intl.DateTimeFormat("en-US").format(date);
|
|
314
483
|
};
|
|
315
484
|
|
|
316
|
-
this.
|
|
317
|
-
|
|
485
|
+
this.loadData = async () => {
|
|
486
|
+
const response = await fetch("/api/data");
|
|
487
|
+
const data = await response.json();
|
|
488
|
+
$setState({ data });
|
|
318
489
|
};
|
|
319
490
|
|
|
491
|
+
// Listen for events from other components
|
|
492
|
+
$listen("refresh-data", () => {
|
|
493
|
+
this.loadData();
|
|
494
|
+
});
|
|
495
|
+
|
|
320
496
|
// Called automatically if defined
|
|
321
|
-
this.init
|
|
497
|
+
if (this.init) {
|
|
498
|
+
this.init();
|
|
499
|
+
}
|
|
322
500
|
}
|
|
323
501
|
```
|
|
324
502
|
|
|
325
|
-
|
|
503
|
+
**New in v2.0:**
|
|
326
504
|
|
|
327
|
-
|
|
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
|
|
328
508
|
|
|
329
|
-
|
|
330
|
-
// stores/userStore.js
|
|
331
|
-
import { createStore } from "ladrillosjs";
|
|
332
|
-
|
|
333
|
-
export const userStore = createStore({
|
|
334
|
-
user: null,
|
|
335
|
-
isAuthenticated: false,
|
|
336
|
-
});
|
|
509
|
+
### Global Event Bus
|
|
337
510
|
|
|
338
|
-
|
|
339
|
-
userStore.setState({
|
|
340
|
-
user: userData,
|
|
341
|
-
isAuthenticated: true,
|
|
342
|
-
});
|
|
343
|
-
}
|
|
511
|
+
**New in v2.0:** The global event bus enables cross-component communication without prop drilling or shared state.
|
|
344
512
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
513
|
+
```javascript
|
|
514
|
+
// In any component script
|
|
515
|
+
// Emit an event
|
|
516
|
+
$emit("user-logged-in", { userId: 123, username: "john" });
|
|
517
|
+
|
|
518
|
+
// Listen for events
|
|
519
|
+
$listen("user-logged-in", (data) => {
|
|
520
|
+
console.log(`User ${data.username} logged in`);
|
|
521
|
+
// Update local state
|
|
522
|
+
isLoggedIn = true;
|
|
523
|
+
currentUser = data;
|
|
524
|
+
});
|
|
351
525
|
```
|
|
352
526
|
|
|
527
|
+
#### Example: Header & Login Components
|
|
528
|
+
|
|
353
529
|
```html
|
|
354
530
|
<!-- header.html -->
|
|
355
531
|
<header>
|
|
356
|
-
<span data-if="
|
|
532
|
+
<span data-if="isLoggedIn">Welcome, {username}!</span>
|
|
357
533
|
<button data-else onclick="showLogin">Login</button>
|
|
358
534
|
</header>
|
|
359
535
|
|
|
360
|
-
<script
|
|
536
|
+
<script>
|
|
537
|
+
let isLoggedIn = false;
|
|
538
|
+
let username = "";
|
|
539
|
+
|
|
540
|
+
// Listen for login event from other components
|
|
541
|
+
$listen("user-logged-in", (user) => {
|
|
542
|
+
isLoggedIn = true;
|
|
543
|
+
username = user.username;
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
const showLogin = () => {
|
|
547
|
+
$emit("show-login-modal");
|
|
548
|
+
};
|
|
549
|
+
</script>
|
|
361
550
|
```
|
|
362
551
|
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
|
|
552
|
+
```html
|
|
553
|
+
<!-- login-form.html -->
|
|
554
|
+
<form onsubmit="handleLogin">
|
|
555
|
+
<input type="text" $bind="username" placeholder="Username" />
|
|
556
|
+
<input type="password" $bind="password" placeholder="Password" />
|
|
557
|
+
<button type="submit">Login</button>
|
|
558
|
+
</form>
|
|
366
559
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
560
|
+
<script>
|
|
561
|
+
let $username = "";
|
|
562
|
+
let $password = "";
|
|
563
|
+
|
|
564
|
+
const handleLogin = (e) => {
|
|
565
|
+
e.preventDefault();
|
|
566
|
+
|
|
567
|
+
// Emit login success event
|
|
568
|
+
$emit("user-logged-in", {
|
|
569
|
+
userId: 123,
|
|
570
|
+
username: $username,
|
|
373
571
|
});
|
|
374
|
-
});
|
|
375
572
|
|
|
376
|
-
|
|
377
|
-
|
|
573
|
+
// Clear form
|
|
574
|
+
$username = "";
|
|
575
|
+
$password = "";
|
|
378
576
|
};
|
|
379
|
-
|
|
577
|
+
</script>
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**Event Bus Benefits:**
|
|
581
|
+
|
|
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
|
|
586
|
+
|
|
587
|
+
### Removed: Global State Stores
|
|
588
|
+
|
|
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:
|
|
590
|
+
|
|
591
|
+
**Before (v1.x with stores):**
|
|
592
|
+
|
|
593
|
+
```javascript
|
|
594
|
+
import { createStore } from "ladrillosjs";
|
|
595
|
+
|
|
596
|
+
export const userStore = createStore({
|
|
597
|
+
user: null,
|
|
598
|
+
isAuthenticated: false,
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
userStore.subscribe((state) => {
|
|
602
|
+
this.setState(state);
|
|
603
|
+
});
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
**After (v2.0 with event bus):**
|
|
607
|
+
|
|
608
|
+
```javascript
|
|
609
|
+
// Emit events to notify components of changes
|
|
610
|
+
$emit("user-updated", { user: userData, isAuthenticated: true });
|
|
611
|
+
|
|
612
|
+
// Listen for changes in components that need them
|
|
613
|
+
$listen("user-updated", ({ user, isAuthenticated }) => {
|
|
614
|
+
// Update local component state
|
|
615
|
+
currentUser = user;
|
|
616
|
+
loggedIn = isAuthenticated;
|
|
617
|
+
});
|
|
380
618
|
```
|
|
381
619
|
|
|
382
620
|
### Shadow DOM
|
|
@@ -385,60 +623,125 @@ Components use Shadow DOM by default for style encapsulation. To disable:
|
|
|
385
623
|
|
|
386
624
|
```javascript
|
|
387
625
|
// Disable Shadow DOM for a component
|
|
388
|
-
registerComponent("my-component", "./my-component.html", false);
|
|
626
|
+
await registerComponent("my-component", "./my-component.html", false);
|
|
389
627
|
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
{ name: "global-styles", path: "./global.html", useShadowDOM: false },
|
|
393
|
-
{ name: "isolated-widget", path: "./widget.html", useShadowDOM: true },
|
|
394
|
-
]);
|
|
628
|
+
// With Shadow DOM enabled (default)
|
|
629
|
+
await registerComponent("isolated-widget", "./widget.html", true);
|
|
395
630
|
```
|
|
396
631
|
|
|
632
|
+
**Shadow DOM Benefits:**
|
|
633
|
+
|
|
634
|
+
- **Style Isolation**: Component styles don't leak to global scope
|
|
635
|
+
- **Encapsulation**: Internal DOM structure is hidden from parent
|
|
636
|
+
- **Cleaner DOM**: Styles and scripts are scoped to component
|
|
637
|
+
|
|
638
|
+
**When to Disable:**
|
|
639
|
+
|
|
640
|
+
- Need global CSS styles to apply
|
|
641
|
+
- Using third-party CSS frameworks
|
|
642
|
+
- Debugging with browser dev tools (easier without shadow DOM)
|
|
643
|
+
|
|
644
|
+
### Performance & Caching
|
|
645
|
+
|
|
646
|
+
**New in v2.0:** LRU (Least Recently Used) caching for improved performance:
|
|
647
|
+
|
|
648
|
+
#### Component Caching
|
|
649
|
+
|
|
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
|
|
653
|
+
|
|
654
|
+
#### Function Caching
|
|
655
|
+
|
|
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
|
|
659
|
+
|
|
660
|
+
**Performance Improvements:**
|
|
661
|
+
|
|
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
|
|
666
|
+
|
|
397
667
|
## API Reference
|
|
398
668
|
|
|
399
669
|
### Component Methods
|
|
400
670
|
|
|
401
|
-
| Method
|
|
402
|
-
|
|
|
403
|
-
| `
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
671
|
+
| Method | Description |
|
|
672
|
+
| ------------------- | ------------------------------------------------------------------------- |
|
|
673
|
+
| `setState(partial)` | Update component state and trigger re-render (merges with existing state) |
|
|
674
|
+
|
|
675
|
+
### Framework Utilities (v2.0)
|
|
676
|
+
|
|
677
|
+
Available within component `<script>` tags:
|
|
678
|
+
|
|
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 |
|
|
687
|
+
|
|
688
|
+
### Component Attributes
|
|
408
689
|
|
|
409
|
-
|
|
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) |
|
|
410
697
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
|
414
|
-
|
|
|
415
|
-
| `
|
|
416
|
-
| `
|
|
417
|
-
| `store.reset()` | Reset to initial state |
|
|
698
|
+
### Registration Functions
|
|
699
|
+
|
|
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 |
|
|
418
704
|
|
|
419
705
|
## Examples
|
|
420
706
|
|
|
421
707
|
### Component Communication
|
|
422
708
|
|
|
709
|
+
**v2.0 uses the global event bus instead of custom events:**
|
|
710
|
+
|
|
423
711
|
```html
|
|
424
712
|
<!-- parent.html -->
|
|
425
713
|
<div>
|
|
426
|
-
<
|
|
714
|
+
<h2>Parent Component</h2>
|
|
715
|
+
<p>Messages received: {messageCount}</p>
|
|
716
|
+
<child-component></child-component>
|
|
427
717
|
</div>
|
|
428
718
|
|
|
429
719
|
<script>
|
|
430
|
-
|
|
720
|
+
let messageCount = 0;
|
|
721
|
+
|
|
722
|
+
// Listen for events from child
|
|
723
|
+
$listen("child-message", (data) => {
|
|
431
724
|
console.log("Received from child:", data);
|
|
725
|
+
messageCount++;
|
|
432
726
|
});
|
|
433
727
|
</script>
|
|
728
|
+
```
|
|
434
729
|
|
|
730
|
+
```html
|
|
435
731
|
<!-- child.html -->
|
|
436
|
-
<
|
|
732
|
+
<div>
|
|
733
|
+
<h3>Child Component</h3>
|
|
734
|
+
<button onclick="sendMessage">Send Message to Parent</button>
|
|
735
|
+
</div>
|
|
437
736
|
|
|
438
737
|
<script>
|
|
738
|
+
let count = 0;
|
|
739
|
+
|
|
439
740
|
const sendMessage = () => {
|
|
440
|
-
|
|
441
|
-
|
|
741
|
+
count++;
|
|
742
|
+
// Emit event that parent (or any component) can listen to
|
|
743
|
+
$emit("child-message", {
|
|
744
|
+
message: `Hello from child! (${count})`,
|
|
442
745
|
timestamp: Date.now(),
|
|
443
746
|
});
|
|
444
747
|
};
|
|
@@ -453,19 +756,144 @@ const createCard = (userData) => {
|
|
|
453
756
|
const card = document.createElement("user-card");
|
|
454
757
|
card.setAttribute("user-id", userData.id);
|
|
455
758
|
card.setAttribute("name", userData.name);
|
|
759
|
+
card.setAttribute("email", userData.email);
|
|
456
760
|
document.querySelector("#user-list").appendChild(card);
|
|
457
761
|
};
|
|
458
762
|
|
|
459
|
-
// Fetch and create
|
|
763
|
+
// Fetch and create multiple components
|
|
460
764
|
fetch("/api/users")
|
|
461
765
|
.then((res) => res.json())
|
|
462
766
|
.then((users) => users.forEach(createCard));
|
|
463
767
|
```
|
|
464
768
|
|
|
769
|
+
### Passing Complex Data
|
|
770
|
+
|
|
771
|
+
Use JSON.stringify for passing objects/arrays as attributes:
|
|
772
|
+
|
|
773
|
+
```html
|
|
774
|
+
<!-- In parent component -->
|
|
775
|
+
<script>
|
|
776
|
+
const user = { id: 1, name: "John", roles: ["admin", "user"] };
|
|
777
|
+
const items = [1, 2, 3, 4, 5];
|
|
778
|
+
|
|
779
|
+
// Create HTML with stringified data
|
|
780
|
+
const cardHtml = `
|
|
781
|
+
<user-card data-user='${JSON.stringify(user)}'></user-card>
|
|
782
|
+
<list-component data-items='${JSON.stringify(items)}'></list-component>
|
|
783
|
+
`;
|
|
784
|
+
|
|
785
|
+
// Or use the built-in stringify helper in v2.0
|
|
786
|
+
const cardHtml2 = `
|
|
787
|
+
<user-card data-user="${this.stringify(user)}"></user-card>
|
|
788
|
+
`;
|
|
789
|
+
</script>
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
```html
|
|
793
|
+
<!-- In child component (user-card.html) -->
|
|
794
|
+
<div>
|
|
795
|
+
<h3>{user.name}</h3>
|
|
796
|
+
<p>ID: {user.id}</p>
|
|
797
|
+
<p>Roles: {user.roles.join(", ")}</p>
|
|
798
|
+
</div>
|
|
799
|
+
|
|
800
|
+
<script>
|
|
801
|
+
// data-user is automatically parsed from JSON
|
|
802
|
+
let user = this.state["data-user"];
|
|
803
|
+
</script>
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
## Migration Guide (v1.x to v2.0)
|
|
807
|
+
|
|
808
|
+
### Breaking Changes
|
|
809
|
+
|
|
810
|
+
1. **Global State Stores Removed**
|
|
811
|
+
|
|
812
|
+
- **Before:** `createStore()` API
|
|
813
|
+
- **After:** Use global event bus with `$emit` and `$listen`
|
|
814
|
+
|
|
815
|
+
2. **Component Registration**
|
|
816
|
+
|
|
817
|
+
- **Before:** `registerComponent()` was synchronous
|
|
818
|
+
- **After:** Returns a Promise, use `await` or `.then()`
|
|
819
|
+
|
|
820
|
+
3. **Framework Utilities**
|
|
821
|
+
|
|
822
|
+
- **Before:** `this.emit()`, `this.listen()`, `this.setState()`
|
|
823
|
+
- **After:** Use `$emit()`, `$listen()`, `$setState()` in scripts (legacy methods still available for compatibility)
|
|
824
|
+
|
|
825
|
+
4. **Two-Way Binding**
|
|
826
|
+
- **Before:** No built-in support (manual implementation)
|
|
827
|
+
- **After:** Use `$bind` attribute for automatic two-way binding
|
|
828
|
+
|
|
829
|
+
### New Features to Adopt
|
|
830
|
+
|
|
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`
|
|
836
|
+
|
|
465
837
|
## Contributing
|
|
466
838
|
|
|
467
839
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
468
840
|
|
|
841
|
+
### Development
|
|
842
|
+
|
|
843
|
+
```bash
|
|
844
|
+
# Install dependencies
|
|
845
|
+
npm install
|
|
846
|
+
|
|
847
|
+
# Run development server with hot reload
|
|
848
|
+
npm run dev
|
|
849
|
+
|
|
850
|
+
# Run tests
|
|
851
|
+
npm test
|
|
852
|
+
|
|
853
|
+
# Run tests with coverage
|
|
854
|
+
npm run test:coverage
|
|
855
|
+
|
|
856
|
+
# Build the library
|
|
857
|
+
npm run build
|
|
858
|
+
|
|
859
|
+
# Build TypeScript types
|
|
860
|
+
npm run build:types
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
### Project Structure
|
|
864
|
+
|
|
865
|
+
```
|
|
866
|
+
src/
|
|
867
|
+
โโโ index.ts # Main entry point
|
|
868
|
+
โโโ core/
|
|
869
|
+
โ โโโ main.ts # Core Ladrillos class
|
|
870
|
+
โ โโโ webcomponent.ts # Web component definition
|
|
871
|
+
โ โโโ componentParser.ts # Component file parser
|
|
872
|
+
โ โโโ componentSource.ts # Component fetching with cache
|
|
873
|
+
โ โโโ eventBus.ts # Global event bus
|
|
874
|
+
โ โโโ css/
|
|
875
|
+
โ โ โโโ cssParser.ts
|
|
876
|
+
โ โโโ html/
|
|
877
|
+
โ โ โโโ htmlparser.ts
|
|
878
|
+
โ โ โโโ htmlRenderer.ts
|
|
879
|
+
โ โโโ js/
|
|
880
|
+
โ โโโ scriptParser.ts
|
|
881
|
+
โโโ cache/
|
|
882
|
+
โ โโโ index.ts # LRU cache for components
|
|
883
|
+
โ โโโ functionCache.ts # LRU cache for compiled functions
|
|
884
|
+
โโโ types/
|
|
885
|
+
โ โโโ LadrilloTypes.ts # TypeScript type definitions
|
|
886
|
+
โโโ utils/
|
|
887
|
+
โโโ logger.ts # Logging utilities
|
|
888
|
+
โโโ regex.ts # Regex patterns
|
|
889
|
+
```
|
|
890
|
+
|
|
469
891
|
## License
|
|
470
892
|
|
|
471
893
|
MIT License - see [LICENSE](LICENSE) file for details.
|
|
894
|
+
|
|
895
|
+
---
|
|
896
|
+
|
|
897
|
+
**LadrillosJS v2.0** - Built with โค๏ธ by [Daniel Rubio](https://github.com/drubiodev)
|
|
898
|
+
|
|
899
|
+
Rewritten in TypeScript for better performance, developer experience, and maintainability.
|