ladrillosjs 1.0.2 → 2.0.0-beta.1.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 +588 -237
- 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-CPSzLUwq.js +8 -0
- package/dist/index-CPSzLUwq.js.map +1 -0
- package/dist/index-I-w3IPex.mjs +361 -0
- package/dist/index-I-w3IPex.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 +80 -18
- package/dist/ladrillosjs.umd.js.map +1 -0
- package/dist/types/LadrilloTypes.d.ts +49 -14
- package/dist/utils/logger.d.ts +14 -11
- package/dist/utils/regex.d.ts +2 -0
- package/dist/webcomponent-Vt40CdPC.mjs +818 -0
- package/dist/webcomponent-Vt40CdPC.mjs.map +1 -0
- package/dist/webcomponent-oNIeezqN.js +74 -0
- package/dist/webcomponent-oNIeezqN.js.map +1 -0
- package/package.json +8 -4
- package/dist/core/store.d.ts +0 -6
- package/dist/index-D_dHFObN.mjs +0 -215
- package/dist/index-X9YN_DbT.js +0 -3
- package/dist/utils/stringify.d.ts +0 -7
- package/dist/webcomponent-Cga3h8cx.js +0 -16
- package/dist/webcomponent-UTcwAakf.mjs +0 -769
package/README.md
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
A lightweight, zero-dependency web component framework for building modular web applications.
|
|
6
6
|
|
|
7
|
-
|
|
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."
|
|
8
10
|
|
|
9
11
|
## Table of Contents
|
|
10
12
|
|
|
11
13
|
- [Features](#features)
|
|
12
|
-
- [Getting Started](#getting-started)
|
|
13
14
|
- [Installation](#installation)
|
|
14
|
-
- [
|
|
15
|
+
- [Quick Start](#quick-start)
|
|
15
16
|
- [Core Concepts](#core-concepts)
|
|
16
17
|
- [Component Registration](#component-registration)
|
|
17
18
|
- [State Management](#state-management)
|
|
@@ -20,12 +21,13 @@ A lightweight, zero-dependency web component framework for building modular web
|
|
|
20
21
|
- [Conditional Rendering](#conditional-rendering)
|
|
21
22
|
- [Slots](#slots)
|
|
22
23
|
- [Advanced Features](#advanced-features)
|
|
24
|
+
- [Global Event Bus](#global-event-bus)
|
|
23
25
|
- [External Scripts](#external-scripts)
|
|
24
|
-
- [Global State Stores](#global-state-stores)
|
|
25
26
|
- [Shadow DOM](#shadow-dom)
|
|
27
|
+
- [Performance & Caching](#performance--caching)
|
|
26
28
|
- [API Reference](#api-reference)
|
|
27
29
|
- [Examples](#examples)
|
|
28
|
-
- [
|
|
30
|
+
- [Development](#development)
|
|
29
31
|
- [License](#license)
|
|
30
32
|
|
|
31
33
|
## Features
|
|
@@ -33,39 +35,15 @@ A lightweight, zero-dependency web component framework for building modular web
|
|
|
33
35
|
- 🚀 **Zero Dependencies** - Pure JavaScript, no build tools required
|
|
34
36
|
- 📦 **Single-File Components** - HTML, CSS, and JavaScript in one file
|
|
35
37
|
- ⚡ **Reactive State** - Automatic re-rendering on state changes
|
|
36
|
-
- 🎯 **Event System** -
|
|
37
|
-
- 🔄 **Two-Way Data Binding** -
|
|
38
|
+
- 🎯 **Event System** - Global event bus for component communication
|
|
39
|
+
- 🔄 **Two-Way Data Binding** - Seamless form input binding with `$bind`
|
|
38
40
|
- 🎨 **Scoped Styles** - Component styles with optional Shadow DOM
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
The repository includes several example applications that demonstrate various features:
|
|
46
|
-
|
|
47
|
-
- **[Todo App](samples/apps/todo)** - Classic todo list with component composition
|
|
48
|
-
- **[Notes App](samples/apps/notes)** - Multi-component app with global state management
|
|
49
|
-
- **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
|
|
50
|
-
- **[API Example](samples/apps/api)** - Fetching and displaying external data
|
|
51
|
-
- **[Business Card](samples/apps/biz)** - Editable form with two-way data binding
|
|
52
|
-
- **[Button Game](samples/apps/button-game)** - Interactive game with component events
|
|
53
|
-
- **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation system
|
|
54
|
-
- **[Docs](samples/apps/docs)** - Documentation viewer with syntax highlighting
|
|
55
|
-
|
|
56
|
-
To run the examples:
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
# Clone the repository
|
|
60
|
-
git clone https://github.com/drubiodev/LadrillosJS.git
|
|
61
|
-
cd LadrillosJS
|
|
62
|
-
|
|
63
|
-
# Install dependencies (for dev server only)
|
|
64
|
-
npm install
|
|
65
|
-
|
|
66
|
-
# Start the development server
|
|
67
|
-
npm run dev
|
|
68
|
-
```
|
|
41
|
+
- 🔌 **Slots** - Content projection with named and default slots
|
|
42
|
+
- 📝 **TypeScript** - Full type definitions and TypeScript source code
|
|
43
|
+
- 🎭 **Conditional Rendering** - `$if`, `$else-if`, and `$else` directives
|
|
44
|
+
- 🚄 **Smart Caching** - LRU cache for components and compiled functions
|
|
45
|
+
- 🔧 **Framework Utilities** - Helper functions for common tasks
|
|
46
|
+
- 🧩 **External Scripts** - Load and bind external JavaScript modules
|
|
69
47
|
|
|
70
48
|
## Installation
|
|
71
49
|
|
|
@@ -78,34 +56,42 @@ npm install ladrillosjs
|
|
|
78
56
|
### CDN
|
|
79
57
|
|
|
80
58
|
```html
|
|
81
|
-
|
|
82
|
-
|
|
59
|
+
<!-- ES Module (Recommended) -->
|
|
60
|
+
<script type="module">
|
|
61
|
+
import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
|
|
62
|
+
registerComponent("my-component", "./my-component.html");
|
|
63
|
+
</script>
|
|
83
64
|
|
|
84
|
-
|
|
65
|
+
<!-- UMD (Browser Global) -->
|
|
66
|
+
<script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
|
|
67
|
+
<script>
|
|
68
|
+
ladrillosjs.registerComponent("my-component", "./my-component.html");
|
|
69
|
+
</script>
|
|
70
|
+
```
|
|
85
71
|
|
|
86
|
-
|
|
72
|
+
## Quick Start
|
|
87
73
|
|
|
88
|
-
### 1. Create
|
|
74
|
+
### 1. Create Your First Component
|
|
89
75
|
|
|
90
|
-
Create `hello-world.html`:
|
|
76
|
+
Create a file called `hello-world.html`:
|
|
91
77
|
|
|
92
78
|
```html
|
|
93
79
|
<!-- hello-world.html -->
|
|
94
80
|
<div class="greeting">
|
|
95
81
|
<h1>{title}</h1>
|
|
96
82
|
<p>Hello, {name}!</p>
|
|
97
|
-
<button onclick="greet">
|
|
83
|
+
<button onclick="greet()">Greet ({count})</button>
|
|
98
84
|
</div>
|
|
99
85
|
|
|
100
86
|
<script>
|
|
101
|
-
// Component state
|
|
87
|
+
// Component state - automatically reactive
|
|
102
88
|
let title = "Welcome to LadrillosJS";
|
|
103
89
|
let name = "World";
|
|
104
90
|
let count = 0;
|
|
105
91
|
|
|
106
92
|
// Event handler
|
|
107
93
|
const greet = () => {
|
|
108
|
-
count++;
|
|
94
|
+
count++; // Automatically triggers re-render
|
|
109
95
|
name = prompt("What's your name?") || "World";
|
|
110
96
|
};
|
|
111
97
|
</script>
|
|
@@ -114,14 +100,24 @@ Create `hello-world.html`:
|
|
|
114
100
|
.greeting {
|
|
115
101
|
text-align: center;
|
|
116
102
|
padding: 2rem;
|
|
117
|
-
background: #
|
|
103
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
104
|
+
color: white;
|
|
118
105
|
border-radius: 8px;
|
|
119
106
|
}
|
|
120
107
|
|
|
121
108
|
button {
|
|
122
|
-
padding: 0.5rem
|
|
109
|
+
padding: 0.75rem 1.5rem;
|
|
123
110
|
font-size: 1rem;
|
|
111
|
+
background: white;
|
|
112
|
+
color: #667eea;
|
|
113
|
+
border: none;
|
|
114
|
+
border-radius: 4px;
|
|
124
115
|
cursor: pointer;
|
|
116
|
+
transition: transform 0.2s;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
button:hover {
|
|
120
|
+
transform: scale(1.05);
|
|
125
121
|
}
|
|
126
122
|
</style>
|
|
127
123
|
```
|
|
@@ -130,15 +126,17 @@ Create `hello-world.html`:
|
|
|
130
126
|
|
|
131
127
|
```html
|
|
132
128
|
<!DOCTYPE html>
|
|
133
|
-
<html>
|
|
129
|
+
<html lang="en">
|
|
134
130
|
<head>
|
|
135
|
-
<
|
|
131
|
+
<meta charset="UTF-8" />
|
|
132
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
133
|
+
<title>My LadrillosJS App</title>
|
|
136
134
|
</head>
|
|
137
135
|
<body>
|
|
138
136
|
<!-- Use your component -->
|
|
139
137
|
<hello-world></hello-world>
|
|
140
138
|
|
|
141
|
-
<!-- Register component -->
|
|
139
|
+
<!-- Register the component -->
|
|
142
140
|
<script type="module">
|
|
143
141
|
import { registerComponent } from "ladrillosjs";
|
|
144
142
|
registerComponent("hello-world", "./hello-world.html");
|
|
@@ -147,121 +145,267 @@ Create `hello-world.html`:
|
|
|
147
145
|
</html>
|
|
148
146
|
```
|
|
149
147
|
|
|
148
|
+
### 3. Explore Example Apps
|
|
149
|
+
|
|
150
|
+
Check out the `samples/apps/` directory for complete examples:
|
|
151
|
+
|
|
152
|
+
- **[Todo App](samples/apps/todo)** - Classic todo list with component composition
|
|
153
|
+
- **[Notes App](samples/apps/notes)** - Multi-component app with event bus
|
|
154
|
+
- **[Simple Button](samples/apps/simple-button)** - Basic interactive component
|
|
155
|
+
- **[Business Card](samples/apps/biz)** - Form with two-way data binding
|
|
156
|
+
- **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation
|
|
157
|
+
- **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
|
|
158
|
+
|
|
159
|
+
Run the development server to view examples:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm install
|
|
163
|
+
npm run dev
|
|
164
|
+
```
|
|
165
|
+
|
|
150
166
|
## Core Concepts
|
|
151
167
|
|
|
152
168
|
### Component Registration
|
|
153
169
|
|
|
154
|
-
Register
|
|
170
|
+
Register components to make them available as custom HTML elements:
|
|
155
171
|
|
|
156
172
|
```javascript
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
await registerComponents(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// Using CDN
|
|
173
|
-
ladrillosjs.registerComponent("my-component", "./my-component.html");
|
|
173
|
+
import { registerComponent, registerComponents } from "ladrillosjs";
|
|
174
|
+
|
|
175
|
+
// Register a single component
|
|
176
|
+
await registerComponent("my-button", "./components/my-button.html");
|
|
177
|
+
|
|
178
|
+
// Register multiple components
|
|
179
|
+
await registerComponents([
|
|
180
|
+
{ name: "app-header", path: "./components/header.html" },
|
|
181
|
+
{ name: "app-footer", path: "./components/footer.html" },
|
|
182
|
+
{ name: "user-card", path: "./components/user-card.html" },
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
// Disable Shadow DOM for a component
|
|
186
|
+
await registerComponent("global-styles", "./components/global.html", false);
|
|
174
187
|
```
|
|
175
188
|
|
|
176
189
|
### State Management
|
|
177
190
|
|
|
178
|
-
Components have reactive state that automatically triggers re-renders:
|
|
191
|
+
Components have reactive state that automatically triggers re-renders when changed:
|
|
179
192
|
|
|
180
193
|
```html
|
|
181
194
|
<div>
|
|
182
|
-
<h2>
|
|
183
|
-
<p>
|
|
184
|
-
<button onclick="
|
|
195
|
+
<h2>Counter: {count}</h2>
|
|
196
|
+
<p>User: {user.name}</p>
|
|
197
|
+
<button onclick="increment()">Add</button>
|
|
198
|
+
<button onclick="updateUser()">Change User</button>
|
|
199
|
+
<button onclick="reset()">Reset</button>
|
|
185
200
|
</div>
|
|
186
201
|
|
|
187
202
|
<script>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
203
|
+
// State variables - automatically reactive
|
|
204
|
+
let count = 0;
|
|
205
|
+
let user = { name: "John", age: 25 };
|
|
206
|
+
|
|
207
|
+
const increment = () => {
|
|
208
|
+
count++; // Automatically triggers re-render
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const updateUser = () => {
|
|
212
|
+
user.name = "Jane"; // Direct mutation triggers re-render
|
|
194
213
|
};
|
|
195
214
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
215
|
+
// You can also use $setState for explicit updates
|
|
216
|
+
const reset = () => {
|
|
217
|
+
$setState({ count: 0, user: { name: "Anonymous", age: 0 } });
|
|
199
218
|
};
|
|
200
219
|
</script>
|
|
201
220
|
```
|
|
202
221
|
|
|
222
|
+
**Available State Utilities:**
|
|
223
|
+
|
|
224
|
+
- Direct assignment: `count++`, `name = "New"`
|
|
225
|
+
- `$setState(updates)`: Merge updates into state
|
|
226
|
+
- `$getState()`: Access state in external modules
|
|
227
|
+
|
|
203
228
|
### Event Handling
|
|
204
229
|
|
|
205
|
-
|
|
230
|
+
Attach event handlers directly to elements:
|
|
206
231
|
|
|
207
232
|
```html
|
|
208
233
|
<!-- Method reference -->
|
|
209
|
-
<button onclick="handleClick">Click me
|
|
234
|
+
<button onclick="handleClick(event)">Click me</button>
|
|
210
235
|
|
|
211
236
|
<!-- Function with arguments -->
|
|
212
|
-
<button onclick="addItem('
|
|
237
|
+
<button onclick="addItem('apple', 5)">Add Item</button>
|
|
213
238
|
|
|
214
239
|
<!-- Inline arrow function -->
|
|
215
|
-
<button onclick="
|
|
240
|
+
<button onclick="console.log(event.target)">Log Event</button>
|
|
241
|
+
|
|
242
|
+
<!-- Multiple events -->
|
|
243
|
+
<label $if="isValid">Valid</label>
|
|
244
|
+
<input
|
|
245
|
+
type="text"
|
|
246
|
+
placeholder="Enter text (min 3 characters)"
|
|
247
|
+
onkeyup="validateInput(event)"
|
|
248
|
+
onfocus="highlightField(event)"
|
|
249
|
+
onblur="saveField(event)"
|
|
250
|
+
/>
|
|
216
251
|
|
|
217
252
|
<script>
|
|
218
|
-
|
|
219
|
-
|
|
253
|
+
let items = [];
|
|
254
|
+
let isValid = false;
|
|
220
255
|
|
|
221
256
|
const handleClick = (event) => {
|
|
222
|
-
console.log("
|
|
223
|
-
|
|
257
|
+
console.log("Button clicked!", event);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const addItem = (name, quantity) => {
|
|
261
|
+
items = [...items, { name, quantity }];
|
|
262
|
+
console.log("Items:", items);
|
|
224
263
|
};
|
|
225
264
|
|
|
226
|
-
const
|
|
227
|
-
|
|
265
|
+
const validateInput = (e) => {
|
|
266
|
+
const value = e.target.value;
|
|
267
|
+
isValid = value.length >= 3;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const highlightField = (e) => {
|
|
271
|
+
e.target.style.backgroundColor = "lightyellow";
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const saveField = (e) => {
|
|
275
|
+
e.target.style.backgroundColor = "";
|
|
276
|
+
console.log("Field saved:", e.target.value);
|
|
228
277
|
};
|
|
229
278
|
</script>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Data Binding
|
|
282
|
+
|
|
283
|
+
#### One-Way Binding (Display Data)
|
|
284
|
+
|
|
285
|
+
Use curly braces `{}` to display state in your template:
|
|
286
|
+
|
|
287
|
+
```html
|
|
230
288
|
<div>
|
|
231
|
-
<h1>
|
|
289
|
+
<h1>{title}</h1>
|
|
290
|
+
<p>{user.name} - {user.email}</p>
|
|
291
|
+
<span>Total: {items.length} items</span>
|
|
292
|
+
<p>Formatted: {formatPrice(price)}</p>
|
|
293
|
+
</div>
|
|
232
294
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
295
|
+
<script>
|
|
296
|
+
let title = "My App";
|
|
297
|
+
let user = { name: "John", email: "john@example.com" };
|
|
298
|
+
let items = ["apple", "banana", "orange"];
|
|
299
|
+
let price = 29.99;
|
|
300
|
+
|
|
301
|
+
const formatPrice = (value) => `$${value.toFixed(2)}`;
|
|
302
|
+
</script>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### Two-Way Binding (Form Inputs)
|
|
306
|
+
|
|
307
|
+
Use the `$bind` attribute for automatic synchronization between inputs and state:
|
|
308
|
+
|
|
309
|
+
```html
|
|
310
|
+
<div>
|
|
311
|
+
<h2>Hello, {name}!</h2>
|
|
312
|
+
<input type="text" $bind="name" placeholder="Your name" />
|
|
313
|
+
|
|
314
|
+
<p>Email: {email}</p>
|
|
315
|
+
<input type="email" $bind="email" />
|
|
316
|
+
|
|
317
|
+
<p>Bio: {bio}</p>
|
|
318
|
+
<textarea $bind="bio"></textarea>
|
|
319
|
+
|
|
320
|
+
<p>Country: {country}</p>
|
|
321
|
+
<select $bind="country">
|
|
322
|
+
<option value="us">United States</option>
|
|
323
|
+
<option value="uk">United Kingdom</option>
|
|
324
|
+
<option value="ca">Canada</option>
|
|
325
|
+
</select>
|
|
326
|
+
|
|
327
|
+
<label>
|
|
328
|
+
<input type="checkbox" $bind="subscribe" />
|
|
329
|
+
Subscribe to newsletter: {subscribe}
|
|
330
|
+
</label>
|
|
331
|
+
</div>
|
|
332
|
+
|
|
333
|
+
<script>
|
|
334
|
+
// Variables are automatically synced with inputs
|
|
335
|
+
let name = "World";
|
|
336
|
+
let email = "";
|
|
337
|
+
let bio = "";
|
|
338
|
+
let country = "us";
|
|
339
|
+
let subscribe = false;
|
|
340
|
+
</script>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Conditional Rendering
|
|
344
|
+
|
|
345
|
+
Show or hide elements based on conditions:
|
|
346
|
+
|
|
347
|
+
```html
|
|
348
|
+
<div>
|
|
349
|
+
<h1>Shopping Cart</h1>
|
|
236
350
|
|
|
237
|
-
|
|
238
|
-
|
|
351
|
+
<!-- Simple condition -->
|
|
352
|
+
<p $if="{items.length === 0}">Your cart is empty</p>
|
|
353
|
+
|
|
354
|
+
<!-- Multiple conditions -->
|
|
355
|
+
<div $if="{items.length > 0 && items.length < 5}">
|
|
356
|
+
<p>You have {items.length} items</p>
|
|
239
357
|
</div>
|
|
240
358
|
|
|
241
|
-
<div
|
|
242
|
-
<p>
|
|
359
|
+
<div $else-if="{items.length >= 5}">
|
|
360
|
+
<p>Your cart is full! ({items.length} items)</p>
|
|
243
361
|
</div>
|
|
244
362
|
|
|
245
|
-
|
|
246
|
-
<button
|
|
363
|
+
<!-- Login/Logout example -->
|
|
364
|
+
<button $if="{!isLoggedIn}" onclick="login()">Login</button>
|
|
365
|
+
<button $else onclick="logout()">Logout</button>
|
|
366
|
+
|
|
367
|
+
<!-- Complex conditions -->
|
|
368
|
+
<div $if="{user && user.role.toLowerCase() === 'admin'}">
|
|
369
|
+
<p>{user.role} Panel</p>
|
|
370
|
+
Hello {user.name}
|
|
371
|
+
<button onclick="addToCart()">🛒 Add To Cart</button>
|
|
372
|
+
</div>
|
|
247
373
|
</div>
|
|
374
|
+
|
|
248
375
|
<script>
|
|
249
|
-
|
|
250
|
-
|
|
376
|
+
let items = [];
|
|
377
|
+
let isLoggedIn = false;
|
|
378
|
+
let user = null;
|
|
251
379
|
|
|
252
|
-
|
|
380
|
+
const login = () => {
|
|
253
381
|
isLoggedIn = true;
|
|
254
|
-
|
|
382
|
+
user = { name: "John", role: "Admin" };
|
|
383
|
+
};
|
|
255
384
|
|
|
256
|
-
|
|
385
|
+
const logout = () => {
|
|
257
386
|
isLoggedIn = false;
|
|
258
|
-
|
|
387
|
+
user = null;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const addToCart = () => {
|
|
391
|
+
if (items.length < 5) {
|
|
392
|
+
items.push(`Item ${items.length + 1}`);
|
|
393
|
+
} else {
|
|
394
|
+
alert("Cart is full!");
|
|
395
|
+
}
|
|
396
|
+
};
|
|
259
397
|
</script>
|
|
260
398
|
```
|
|
261
399
|
|
|
400
|
+
**Conditional Directives:**
|
|
401
|
+
|
|
402
|
+
- `$if="{expression}"`: Show if expression is truthy
|
|
403
|
+
- `$else-if="{expression}"`: Chain multiple conditions
|
|
404
|
+
- `$else`: Fallback when previous conditions are false
|
|
405
|
+
|
|
262
406
|
### Slots
|
|
263
407
|
|
|
264
|
-
|
|
408
|
+
Project content from parent to child components:
|
|
265
409
|
|
|
266
410
|
```html
|
|
267
411
|
<!-- card.html -->
|
|
@@ -278,194 +422,401 @@ Content projection using slots:
|
|
|
278
422
|
</div>
|
|
279
423
|
</div>
|
|
280
424
|
|
|
281
|
-
|
|
425
|
+
<style>
|
|
426
|
+
.card {
|
|
427
|
+
border: 1px solid #ddd;
|
|
428
|
+
border-radius: 8px;
|
|
429
|
+
padding: 1rem;
|
|
430
|
+
}
|
|
431
|
+
.card-header {
|
|
432
|
+
font-weight: bold;
|
|
433
|
+
margin-bottom: 1rem;
|
|
434
|
+
}
|
|
435
|
+
.card-footer {
|
|
436
|
+
margin-top: 1rem;
|
|
437
|
+
border-top: 1px solid #eee;
|
|
438
|
+
padding-top: 1rem;
|
|
439
|
+
}
|
|
440
|
+
</style>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Usage:**
|
|
444
|
+
|
|
445
|
+
```html
|
|
282
446
|
<my-card>
|
|
283
447
|
<h2 slot="header">User Profile</h2>
|
|
284
|
-
<p>
|
|
285
|
-
<
|
|
448
|
+
<p>Name: John Doe</p>
|
|
449
|
+
<p>Email: john@example.com</p>
|
|
450
|
+
<button slot="footer">Save Changes</button>
|
|
286
451
|
</my-card>
|
|
287
452
|
```
|
|
288
453
|
|
|
289
454
|
## Advanced Features
|
|
290
455
|
|
|
291
|
-
###
|
|
456
|
+
### Global Event Bus
|
|
292
457
|
|
|
293
|
-
|
|
458
|
+
The global event bus enables communication between components without prop drilling:
|
|
459
|
+
|
|
460
|
+
```javascript
|
|
461
|
+
// Emit events to other components
|
|
462
|
+
$emit("user-logged-in", { userId: 123, username: "john" });
|
|
463
|
+
|
|
464
|
+
// Listen for events from any component
|
|
465
|
+
$listen("user-logged-in", (data) => {
|
|
466
|
+
console.log(`User ${data.username} logged in`);
|
|
467
|
+
isLoggedIn = true;
|
|
468
|
+
currentUser = data;
|
|
469
|
+
});
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**Example: Cross-Component Communication**
|
|
294
473
|
|
|
295
474
|
```html
|
|
296
|
-
<!--
|
|
297
|
-
<
|
|
475
|
+
<!-- header.html -->
|
|
476
|
+
<header>
|
|
477
|
+
<span $if="{isLoggedIn}">Welcome, {username}!</span>
|
|
478
|
+
<button $else onclick="requestLogin()">Login</button>
|
|
479
|
+
</header>
|
|
480
|
+
|
|
481
|
+
<script>
|
|
482
|
+
let isLoggedIn = false;
|
|
483
|
+
let username = "";
|
|
298
484
|
|
|
299
|
-
|
|
300
|
-
|
|
485
|
+
$listen("user-logged-in", (user) => {
|
|
486
|
+
console.log("User logged in:", user);
|
|
487
|
+
isLoggedIn = true;
|
|
488
|
+
username = user.username;
|
|
489
|
+
});
|
|
301
490
|
|
|
302
|
-
|
|
303
|
-
|
|
491
|
+
const requestLogin = () => {
|
|
492
|
+
$emit("show-login-dialog");
|
|
493
|
+
};
|
|
494
|
+
</script>
|
|
304
495
|
```
|
|
305
496
|
|
|
306
|
-
|
|
497
|
+
```html
|
|
498
|
+
<!-- login-form.html -->
|
|
499
|
+
<form onsubmit="handleLogin(event)" $if="{showLoginDialog}">
|
|
500
|
+
<input type="text" $bind="username" placeholder="Username" />
|
|
501
|
+
<input type="password" $bind="password" placeholder="Password" />
|
|
502
|
+
<button type="submit">Login</button>
|
|
503
|
+
</form>
|
|
307
504
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
export default function () {
|
|
311
|
-
// 'this' refers to the component instance
|
|
312
|
-
this.formatDate = (date) => {
|
|
313
|
-
return new Intl.DateTimeFormat("en-US").format(date);
|
|
314
|
-
};
|
|
505
|
+
<script>
|
|
506
|
+
let showLoginDialog = false;
|
|
315
507
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
};
|
|
508
|
+
$listen("show-login-dialog", () => {
|
|
509
|
+
showLoginDialog = true;
|
|
510
|
+
});
|
|
319
511
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
512
|
+
const handleLogin = (e) => {
|
|
513
|
+
e.preventDefault();
|
|
514
|
+
$emit("user-logged-in", { userId: 123, username });
|
|
515
|
+
username = "";
|
|
516
|
+
password = "";
|
|
517
|
+
showLoginDialog = false;
|
|
518
|
+
};
|
|
519
|
+
</script>
|
|
323
520
|
```
|
|
324
521
|
|
|
325
|
-
###
|
|
522
|
+
### External Scripts
|
|
326
523
|
|
|
327
|
-
|
|
524
|
+
LadrillosJS supports three ways to include external JavaScript:
|
|
328
525
|
|
|
329
|
-
|
|
330
|
-
// stores/userStore.js
|
|
331
|
-
import { createStore } from "ladrillosjs";
|
|
526
|
+
#### 1. Component-Scoped Scripts (Default)
|
|
332
527
|
|
|
333
|
-
|
|
334
|
-
user: null,
|
|
335
|
-
isAuthenticated: false,
|
|
336
|
-
});
|
|
528
|
+
Regular script tags execute within the component's context and have access to component state and utilities:
|
|
337
529
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
isAuthenticated: true,
|
|
342
|
-
});
|
|
343
|
-
}
|
|
530
|
+
```html
|
|
531
|
+
<!-- alert-button.html -->
|
|
532
|
+
<button onclick="increaseCount()">{title}: {count}</button>
|
|
344
533
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
user: null,
|
|
348
|
-
isAuthenticated: false,
|
|
349
|
-
});
|
|
350
|
-
}
|
|
534
|
+
<!-- This script runs in the component context -->
|
|
535
|
+
<script src="./alert.js"></script>
|
|
351
536
|
```
|
|
352
537
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
538
|
+
**alert.js:**
|
|
539
|
+
|
|
540
|
+
```javascript
|
|
541
|
+
// Variables and functions are available to the component
|
|
542
|
+
let count = 0;
|
|
543
|
+
const title = "Button Count";
|
|
359
544
|
|
|
360
|
-
|
|
545
|
+
const increaseCount = () => {
|
|
546
|
+
count++; // Updates component state
|
|
547
|
+
};
|
|
361
548
|
```
|
|
362
549
|
|
|
550
|
+
#### 2. ES Modules
|
|
551
|
+
|
|
552
|
+
Use `type="module"` for standard ES module imports:
|
|
553
|
+
|
|
554
|
+
```html
|
|
555
|
+
<!-- side-nav.html -->
|
|
556
|
+
<nav>
|
|
557
|
+
<h1><Note App/></h1>
|
|
558
|
+
<button onclick="createNote()">Create Note</button>
|
|
559
|
+
<ul></ul>
|
|
560
|
+
</nav>
|
|
561
|
+
|
|
562
|
+
<!-- Load as ES module -->
|
|
563
|
+
<script type="module" src="../js/side.js"></script>
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**side.js:**
|
|
567
|
+
|
|
363
568
|
```javascript
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
569
|
+
import { registerComponent, $listen, $querySelector } from "ladrillosjs";
|
|
570
|
+
|
|
571
|
+
const notes = [];
|
|
572
|
+
registerComponent("note-item", "./components/note-item.html");
|
|
573
|
+
|
|
574
|
+
$listen("note_saved", (data) => {
|
|
575
|
+
notes.push({ ...data });
|
|
576
|
+
const ul = $querySelector("ul");
|
|
577
|
+
if (ul) {
|
|
578
|
+
ul.innerHTML = notes
|
|
579
|
+
.map((n) => `<note-item data-note='${JSON.stringify(n)}'></note-item>`)
|
|
580
|
+
.join("");
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
```
|
|
375
584
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
585
|
+
#### 3. External Libraries
|
|
586
|
+
|
|
587
|
+
Use the `external` attribute for third-party libraries that shouldn't be bound to component context:
|
|
588
|
+
|
|
589
|
+
```html
|
|
590
|
+
<!-- codeblock.html -->
|
|
591
|
+
<link
|
|
592
|
+
rel="stylesheet"
|
|
593
|
+
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"
|
|
594
|
+
/>
|
|
595
|
+
<script
|
|
596
|
+
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"
|
|
597
|
+
external
|
|
598
|
+
></script>
|
|
599
|
+
|
|
600
|
+
<div class="code-container">
|
|
601
|
+
<pre><code id="code" class="language-html"></code></pre>
|
|
602
|
+
</div>
|
|
603
|
+
|
|
604
|
+
<script>
|
|
605
|
+
// Access the external library (hljs is global)
|
|
606
|
+
const codeElement = $querySelector("pre code");
|
|
607
|
+
codeElement.textContent = "console.log('Hello')";
|
|
608
|
+
hljs.highlightElement(codeElement); // Use external library
|
|
609
|
+
</script>
|
|
380
610
|
```
|
|
381
611
|
|
|
612
|
+
**When to use `external`:**
|
|
613
|
+
|
|
614
|
+
- Third-party CDN libraries (highlight.js, Chart.js, etc.)
|
|
615
|
+
- Libraries that need to be loaded globally
|
|
616
|
+
- Scripts that don't need component context or utilities
|
|
617
|
+
|
|
382
618
|
### Shadow DOM
|
|
383
619
|
|
|
384
|
-
Components use Shadow DOM by default for style encapsulation
|
|
620
|
+
Components use Shadow DOM by default for style encapsulation:
|
|
385
621
|
|
|
386
622
|
```javascript
|
|
387
|
-
//
|
|
388
|
-
registerComponent("
|
|
623
|
+
// Shadow DOM enabled (default)
|
|
624
|
+
await registerComponent("isolated-widget", "./widget.html");
|
|
625
|
+
await registerComponent("isolated-widget", "./widget.html", true);
|
|
389
626
|
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
{ name: "global-styles", path: "./global.html", useShadowDOM: false },
|
|
393
|
-
{ name: "isolated-widget", path: "./widget.html", useShadowDOM: true },
|
|
394
|
-
]);
|
|
627
|
+
// Shadow DOM disabled
|
|
628
|
+
await registerComponent("global-styles", "./global.html", false);
|
|
395
629
|
```
|
|
396
630
|
|
|
631
|
+
**Shadow DOM Benefits:**
|
|
632
|
+
|
|
633
|
+
- **Style Isolation**: Component styles don't leak to global scope
|
|
634
|
+
- **Encapsulation**: Internal DOM is hidden from parent
|
|
635
|
+
- **Clean Separation**: Each component has its own styling context
|
|
636
|
+
|
|
637
|
+
**When to Disable:**
|
|
638
|
+
|
|
639
|
+
- Need to apply global CSS frameworks (Bootstrap, Tailwind)
|
|
640
|
+
- Using third-party libraries that expect normal DOM
|
|
641
|
+
- Easier debugging without shadow boundaries
|
|
642
|
+
|
|
643
|
+
### Performance & Caching
|
|
644
|
+
|
|
645
|
+
LadrillosJS includes built-in performance optimizations:
|
|
646
|
+
|
|
647
|
+
#### Component Caching
|
|
648
|
+
|
|
649
|
+
- **LRU Cache**: Stores up to 25 most recently used components
|
|
650
|
+
- **Instant Loading**: Cached components load immediately
|
|
651
|
+
- **Reduced Network**: No repeated HTTP requests for components
|
|
652
|
+
|
|
653
|
+
#### Function Caching
|
|
654
|
+
|
|
655
|
+
- **Template Compilation**: Caches up to 100 compiled expressions
|
|
656
|
+
- **Memory Efficient**: Reuses Function objects for identical expressions
|
|
657
|
+
- **Faster Renders**: Expressions like `{formatName(user)}` compile once
|
|
658
|
+
|
|
659
|
+
**Performance Tips:**
|
|
660
|
+
|
|
661
|
+
- Component files are cached after first load
|
|
662
|
+
- State updates trigger minimal DOM changes
|
|
663
|
+
- Event listeners are automatically cleaned up
|
|
664
|
+
- Conditional rendering skips hidden elements
|
|
665
|
+
|
|
397
666
|
## API Reference
|
|
398
667
|
|
|
399
|
-
###
|
|
668
|
+
### Registration Functions
|
|
400
669
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
| `this.listen(eventName, handler)` | Listen for custom events |
|
|
406
|
-
| `this.querySelector(selector)` | Query element within component |
|
|
407
|
-
| `this.querySelectorAll(selector)` | Query all elements within component |
|
|
670
|
+
```typescript
|
|
671
|
+
registerComponent(name: string, path: string, useShadowDOM?: boolean): Promise<void>
|
|
672
|
+
registerComponents(components: Array<{name, path, useShadowDOM?}>): Promise<void>
|
|
673
|
+
```
|
|
408
674
|
|
|
409
|
-
###
|
|
675
|
+
### Component Utilities
|
|
410
676
|
|
|
411
|
-
|
|
412
|
-
| --------------------------- | -------------------------- |
|
|
413
|
-
| `createStore(initialState)` | Create a new store |
|
|
414
|
-
| `store.getState()` | Get current store state |
|
|
415
|
-
| `store.setState(partial)` | Update store state |
|
|
416
|
-
| `store.subscribe(callback)` | Subscribe to state changes |
|
|
417
|
-
| `store.reset()` | Reset to initial state |
|
|
677
|
+
Available within component `<script>` tags:
|
|
418
678
|
|
|
419
|
-
|
|
679
|
+
```typescript
|
|
680
|
+
// State management
|
|
681
|
+
$getState(): object // Access component state
|
|
682
|
+
$setState(updates: object): void // Update state and re-render
|
|
420
683
|
|
|
421
|
-
|
|
684
|
+
// Event bus
|
|
685
|
+
$emit(eventName: string, data?: any): void
|
|
686
|
+
$listen(eventName: string, callback: (data?) => void): () => void
|
|
687
|
+
|
|
688
|
+
// DOM queries (respects Shadow DOM boundaries)
|
|
689
|
+
$querySelector(selector: string): Element | null
|
|
690
|
+
$querySelectorAll(selector: string): NodeListOf<Element>
|
|
691
|
+
|
|
692
|
+
// Reactive variables (for external modules)
|
|
693
|
+
$reactive(name: string, initialValue: any): (value: any) => void
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### Component Attributes
|
|
422
697
|
|
|
423
698
|
```html
|
|
424
|
-
<!--
|
|
425
|
-
<
|
|
426
|
-
|
|
427
|
-
|
|
699
|
+
<!-- Two-way data binding -->
|
|
700
|
+
<input $bind="variableName" />
|
|
701
|
+
|
|
702
|
+
<!-- Conditional rendering -->
|
|
703
|
+
<div $if="{condition}">...</div>
|
|
704
|
+
<div $else-if="{anotherCondition}">...</div>
|
|
705
|
+
<div $else>...</div>
|
|
706
|
+
|
|
707
|
+
<!-- Event handlers -->
|
|
708
|
+
<button onclick="methodName()">Click</button>
|
|
709
|
+
<button onclick="method(arg1, arg2)">Call with args</button>
|
|
710
|
+
<button onclick="console.log(event)">Inline function</button>
|
|
711
|
+
|
|
712
|
+
<!-- Slots -->
|
|
713
|
+
<slot></slot>
|
|
714
|
+
<!-- Default slot -->
|
|
715
|
+
<slot name="header"></slot>
|
|
716
|
+
<!-- Named slot -->
|
|
717
|
+
```
|
|
428
718
|
|
|
429
|
-
|
|
430
|
-
this.listen("child-event", (data) => {
|
|
431
|
-
console.log("Received from child:", data);
|
|
432
|
-
});
|
|
433
|
-
</script>
|
|
719
|
+
### Template Syntax
|
|
434
720
|
|
|
435
|
-
|
|
436
|
-
|
|
721
|
+
```html
|
|
722
|
+
<!-- Variable interpolation -->
|
|
723
|
+
{variableName} {object.property} {array[0]}
|
|
437
724
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
});
|
|
444
|
-
};
|
|
445
|
-
</script>
|
|
725
|
+
<!-- Function calls -->
|
|
726
|
+
{functionName(arg1, arg2)} {object.method()}
|
|
727
|
+
|
|
728
|
+
<!-- Expressions -->
|
|
729
|
+
{count + 1} {isActive ? 'Yes' : 'No'} {items.length > 0 ? 'Has items' : 'Empty'}
|
|
446
730
|
```
|
|
447
731
|
|
|
448
|
-
|
|
732
|
+
## Development
|
|
449
733
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
734
|
+
### Prerequisites
|
|
735
|
+
|
|
736
|
+
- **Node.js**: v20.19+ or v22.12+
|
|
737
|
+
- **npm**: v9+ (comes with Node.js)
|
|
738
|
+
|
|
739
|
+
### Setup
|
|
740
|
+
|
|
741
|
+
```bash
|
|
742
|
+
# Clone the repository
|
|
743
|
+
git clone https://github.com/drubiodev/LadrillosJS.git
|
|
744
|
+
cd LadrillosJS
|
|
745
|
+
|
|
746
|
+
# Install dependencies
|
|
747
|
+
npm install
|
|
458
748
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
749
|
+
# Start development server
|
|
750
|
+
npm run dev
|
|
751
|
+
|
|
752
|
+
# Build the library
|
|
753
|
+
npm run build
|
|
754
|
+
|
|
755
|
+
# Run tests
|
|
756
|
+
npm test
|
|
757
|
+
|
|
758
|
+
# Run tests with coverage
|
|
759
|
+
npm run test:coverage
|
|
463
760
|
```
|
|
464
761
|
|
|
465
|
-
|
|
762
|
+
### Project Structure
|
|
763
|
+
|
|
764
|
+
```
|
|
765
|
+
LadrillosJS/
|
|
766
|
+
├── src/
|
|
767
|
+
│ ├── index.ts # Main entry point
|
|
768
|
+
│ ├── core/
|
|
769
|
+
│ │ ├── main.ts # Core Ladrillos class
|
|
770
|
+
│ │ ├── webcomponent.ts # Web component wrapper
|
|
771
|
+
│ │ ├── componentParser.ts # Parse component files
|
|
772
|
+
│ │ ├── componentSource.ts # Fetch with caching
|
|
773
|
+
│ │ ├── eventBus.ts # Global event bus
|
|
774
|
+
│ │ ├── css/
|
|
775
|
+
│ │ │ └── cssParser.ts
|
|
776
|
+
│ │ ├── html/
|
|
777
|
+
│ │ │ ├── htmlparser.ts
|
|
778
|
+
│ │ │ └── htmlRenderer.ts
|
|
779
|
+
│ │ └── js/
|
|
780
|
+
│ │ └── scriptParser.ts
|
|
781
|
+
│ ├── cache/
|
|
782
|
+
│ │ ├── index.ts # LRU component cache
|
|
783
|
+
│ │ └── functionCache.ts # LRU function cache
|
|
784
|
+
│ ├── types/
|
|
785
|
+
│ │ └── LadrilloTypes.ts
|
|
786
|
+
│ └── utils/
|
|
787
|
+
│ ├── logger.ts
|
|
788
|
+
│ └── regex.ts
|
|
789
|
+
├── samples/ # Example applications
|
|
790
|
+
│ └── apps/
|
|
791
|
+
│ ├── todo/
|
|
792
|
+
│ ├── notes/
|
|
793
|
+
│ ├── simple-button/
|
|
794
|
+
│ └── ...
|
|
795
|
+
├── test/ # Test files
|
|
796
|
+
├── dist/ # Built files
|
|
797
|
+
├── package.json
|
|
798
|
+
├── tsconfig.json
|
|
799
|
+
├── vite.config.js
|
|
800
|
+
└── vitest.config.js
|
|
801
|
+
```
|
|
466
802
|
|
|
467
|
-
|
|
803
|
+
### NPM Scripts
|
|
804
|
+
|
|
805
|
+
```bash
|
|
806
|
+
npm run dev # Start Vite dev server
|
|
807
|
+
npm run build # Build library (ESM, UMD, CJS)
|
|
808
|
+
npm run build:types # Generate TypeScript declarations
|
|
809
|
+
npm test # Run tests with Vitest
|
|
810
|
+
npm run test:coverage # Run tests with coverage report
|
|
811
|
+
npm run preview # Preview production build
|
|
812
|
+
```
|
|
468
813
|
|
|
469
814
|
## License
|
|
470
815
|
|
|
471
816
|
MIT License - see [LICENSE](LICENSE) file for details.
|
|
817
|
+
|
|
818
|
+
---
|
|
819
|
+
|
|
820
|
+
**LadrillosJS v2.0** - Built with ❤️ by [Daniel Rubio](https://github.com/drubiodev)
|
|
821
|
+
|
|
822
|
+
🌟 [GitHub](https://github.com/drubiodev/LadrillosJS) • 📦 [NPM](https://www.npmjs.com/package/ladrillosjs) • 📖 [Documentation](https://github.com/drubiodev/LadrillosJS/blob/main/README.md)
|