ladrillosjs 2.0.0-beta.1 → 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 +427 -504
- package/dist/{index-CXHidyhO.js → index-CPSzLUwq.js} +4 -4
- package/dist/index-CPSzLUwq.js.map +1 -0
- package/dist/{index-VkDZJVOR.mjs → index-I-w3IPex.mjs} +4 -4
- package/dist/index-I-w3IPex.mjs.map +1 -0
- package/dist/ladrillosjs.cjs.js +1 -1
- package/dist/ladrillosjs.es.js +1 -1
- package/dist/ladrillosjs.umd.js +23 -19
- package/dist/ladrillosjs.umd.js.map +1 -1
- package/dist/types/LadrilloTypes.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 +1 -1
- package/dist/index-CXHidyhO.js.map +0 -1
- package/dist/index-VkDZJVOR.mjs.map +0 -1
- package/dist/webcomponent-CJ3lZBZb.mjs +0 -703
- package/dist/webcomponent-CJ3lZBZb.mjs.map +0 -1
- package/dist/webcomponent-i9W7LUiv.js +0 -70
- package/dist/webcomponent-i9W7LUiv.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,19 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
A lightweight, zero-dependency web component framework for building modular web applications.
|
|
6
6
|
|
|
7
|
-
**Version 2.0** -
|
|
7
|
+
**Version 2.0** - Rewritten in TypeScript with enhanced performance, improved developer experience, and powerful new features.
|
|
8
8
|
|
|
9
|
-
"
|
|
9
|
+
> "Empower developers to componentize code efficiently without the complexity of a full-scale framework. Focus on simplicity while leveraging core web fundamentals."
|
|
10
10
|
|
|
11
11
|
## Table of Contents
|
|
12
12
|
|
|
13
13
|
- [Features](#features)
|
|
14
|
-
- [Getting Started](#getting-started)
|
|
15
|
-
- [Prerequisites](#prerequisites)
|
|
16
|
-
- [What's New in v2.0](#whats-new-in-v20)
|
|
17
|
-
- [Example Applications](#example-applications)
|
|
18
14
|
- [Installation](#installation)
|
|
19
|
-
- [
|
|
15
|
+
- [Quick Start](#quick-start)
|
|
20
16
|
- [Core Concepts](#core-concepts)
|
|
21
17
|
- [Component Registration](#component-registration)
|
|
22
18
|
- [State Management](#state-management)
|
|
@@ -25,92 +21,29 @@ A lightweight, zero-dependency web component framework for building modular web
|
|
|
25
21
|
- [Conditional Rendering](#conditional-rendering)
|
|
26
22
|
- [Slots](#slots)
|
|
27
23
|
- [Advanced Features](#advanced-features)
|
|
28
|
-
- [External Scripts](#external-scripts)
|
|
29
24
|
- [Global Event Bus](#global-event-bus)
|
|
25
|
+
- [External Scripts](#external-scripts)
|
|
30
26
|
- [Shadow DOM](#shadow-dom)
|
|
31
27
|
- [Performance & Caching](#performance--caching)
|
|
32
28
|
- [API Reference](#api-reference)
|
|
33
29
|
- [Examples](#examples)
|
|
34
|
-
|
|
35
|
-
- [Dynamic Component Creation](#dynamic-component-creation)
|
|
36
|
-
- [Passing Complex Data](#passing-complex-data)
|
|
37
|
-
- [Migration Guide (v1.x to v2.0)](#migration-guide-v1x-to-v20)
|
|
38
|
-
- [Contributing](#contributing)
|
|
30
|
+
- [Development](#development)
|
|
39
31
|
- [License](#license)
|
|
40
32
|
|
|
41
33
|
## Features
|
|
42
34
|
|
|
43
35
|
- 🚀 **Zero Dependencies** - Pure JavaScript, no build tools required
|
|
44
36
|
- 📦 **Single-File Components** - HTML, CSS, and JavaScript in one file
|
|
45
|
-
- ⚡ **Reactive State** - Automatic re-rendering on state changes
|
|
46
|
-
- 🎯 **Event System** -
|
|
47
|
-
- 🔄 **Two-Way Data Binding** - Seamless
|
|
37
|
+
- ⚡ **Reactive State** - Automatic re-rendering on state changes
|
|
38
|
+
- 🎯 **Event System** - Global event bus for component communication
|
|
39
|
+
- 🔄 **Two-Way Data Binding** - Seamless form input binding with `$bind`
|
|
48
40
|
- 🎨 **Scoped Styles** - Component styles with optional Shadow DOM
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
- 🎭 **Conditional Rendering** - `data-if`, `data-else-if`, and `data-else` directives
|
|
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
|
|
53
44
|
- 🚄 **Smart Caching** - LRU cache for components and compiled functions
|
|
54
|
-
- 🔧 **Framework Utilities** -
|
|
55
|
-
-
|
|
56
|
-
- 🧩 **External Scripts** - Load and bind external JavaScript with your components
|
|
57
|
-
|
|
58
|
-
## Getting Started
|
|
59
|
-
|
|
60
|
-
### Prerequisites
|
|
61
|
-
|
|
62
|
-
- **Node.js**: Version 20.19+ or 22.12+ is required for development
|
|
63
|
-
- **TypeScript**: v5.8+ (included in devDependencies)
|
|
64
|
-
|
|
65
|
-
### What's New in v2.0
|
|
66
|
-
|
|
67
|
-
- **🔄 Complete TypeScript Rewrite** - Full type safety and improved IDE support
|
|
68
|
-
- **⚡ Performance Enhancements** - LRU caching for components and compiled functions
|
|
69
|
-
- **🎯 Global Event Bus** - New `$emit` and `$listen` for cross-component communication
|
|
70
|
-
- **🔧 Framework Utilities** - New `$state`, `$setState`, `$querySelector` helpers
|
|
71
|
-
- **🚀 Automatic Reactivity** - Variable assignments like `count++` automatically trigger re-renders
|
|
72
|
-
- **📦 Better Build System** - Vite-powered builds with sourcemaps and multiple output formats (ESM, UMD, CJS)
|
|
73
|
-
- **🧪 Testing** - Vitest with coverage reporting
|
|
74
|
-
- **🎭 Enhanced Conditionals** - More robust `data-if`, `data-else-if`, `data-else` rendering
|
|
75
|
-
- **🔄 Two-Way Binding** - Simplified with `$bind` prefix for automatic state synchronization
|
|
76
|
-
- **🧹 Memory Management** - Automatic cleanup of event listeners on component disconnect
|
|
77
|
-
|
|
78
|
-
### Example Applications
|
|
79
|
-
|
|
80
|
-
The repository includes several example applications that demonstrate various features:
|
|
81
|
-
|
|
82
|
-
- **[Todo App](samples/apps/todo)** - Classic todo list with component composition
|
|
83
|
-
- **[Notes App](samples/apps/notes)** - Multi-component app with global event bus
|
|
84
|
-
- **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
|
|
85
|
-
- **[API Example](samples/apps/api)** - Fetching and displaying external data
|
|
86
|
-
- **[Business Card](samples/apps/biz)** - Editable form with two-way data binding using `$bind`
|
|
87
|
-
- **[Button Game](samples/apps/button-game)** - Interactive game with component events
|
|
88
|
-
- **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation system
|
|
89
|
-
- **[Document Chat](samples/apps/document-chat)** - Chat interface with component communication
|
|
90
|
-
- **[Docs](samples/apps/docs)** - Documentation viewer with syntax highlighting
|
|
91
|
-
|
|
92
|
-
To run the examples:
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
# Clone the repository
|
|
96
|
-
git clone https://github.com/drubiodev/LadrillosJS.git
|
|
97
|
-
cd LadrillosJS
|
|
98
|
-
|
|
99
|
-
# Install dependencies
|
|
100
|
-
npm install
|
|
101
|
-
|
|
102
|
-
# Start the development server (Vite)
|
|
103
|
-
npm run dev
|
|
104
|
-
|
|
105
|
-
# Build the library
|
|
106
|
-
npm run build
|
|
107
|
-
|
|
108
|
-
# Run tests
|
|
109
|
-
npm test
|
|
110
|
-
|
|
111
|
-
# Run tests with coverage
|
|
112
|
-
npm run test:coverage
|
|
113
|
-
```
|
|
45
|
+
- 🔧 **Framework Utilities** - Helper functions for common tasks
|
|
46
|
+
- 🧩 **External Scripts** - Load and bind external JavaScript modules
|
|
114
47
|
|
|
115
48
|
## Installation
|
|
116
49
|
|
|
@@ -123,45 +56,42 @@ npm install ladrillosjs
|
|
|
123
56
|
### CDN
|
|
124
57
|
|
|
125
58
|
```html
|
|
126
|
-
<!--
|
|
59
|
+
<!-- ES Module (Recommended) -->
|
|
127
60
|
<script type="module">
|
|
128
61
|
import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.es.js";
|
|
129
62
|
registerComponent("my-component", "./my-component.html");
|
|
130
63
|
</script>
|
|
131
64
|
|
|
132
|
-
<!-- UMD (
|
|
65
|
+
<!-- UMD (Browser Global) -->
|
|
133
66
|
<script src="https://cdn.jsdelivr.net/npm/ladrillosjs/dist/ladrillosjs.umd.js"></script>
|
|
134
67
|
<script>
|
|
135
|
-
// Access via global ladrillosjs object
|
|
136
68
|
ladrillosjs.registerComponent("my-component", "./my-component.html");
|
|
137
69
|
</script>
|
|
138
70
|
```
|
|
139
71
|
|
|
140
|
-
##
|
|
141
|
-
|
|
142
|
-
A component in LadrillosJS is a reusable custom HTML element that bundles its own template, logic, and styles into a single file.
|
|
72
|
+
## Quick Start
|
|
143
73
|
|
|
144
|
-
### 1. Create
|
|
74
|
+
### 1. Create Your First Component
|
|
145
75
|
|
|
146
|
-
Create `hello-world.html`:
|
|
76
|
+
Create a file called `hello-world.html`:
|
|
147
77
|
|
|
148
78
|
```html
|
|
149
79
|
<!-- hello-world.html -->
|
|
150
80
|
<div class="greeting">
|
|
151
81
|
<h1>{title}</h1>
|
|
152
82
|
<p>Hello, {name}!</p>
|
|
153
|
-
<button onclick="greet">
|
|
83
|
+
<button onclick="greet()">Greet ({count})</button>
|
|
154
84
|
</div>
|
|
155
85
|
|
|
156
86
|
<script>
|
|
157
|
-
// Component state
|
|
87
|
+
// Component state - automatically reactive
|
|
158
88
|
let title = "Welcome to LadrillosJS";
|
|
159
89
|
let name = "World";
|
|
160
90
|
let count = 0;
|
|
161
91
|
|
|
162
92
|
// Event handler
|
|
163
93
|
const greet = () => {
|
|
164
|
-
count++;
|
|
94
|
+
count++; // Automatically triggers re-render
|
|
165
95
|
name = prompt("What's your name?") || "World";
|
|
166
96
|
};
|
|
167
97
|
</script>
|
|
@@ -170,14 +100,24 @@ Create `hello-world.html`:
|
|
|
170
100
|
.greeting {
|
|
171
101
|
text-align: center;
|
|
172
102
|
padding: 2rem;
|
|
173
|
-
background: #
|
|
103
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
104
|
+
color: white;
|
|
174
105
|
border-radius: 8px;
|
|
175
106
|
}
|
|
176
107
|
|
|
177
108
|
button {
|
|
178
|
-
padding: 0.5rem
|
|
109
|
+
padding: 0.75rem 1.5rem;
|
|
179
110
|
font-size: 1rem;
|
|
111
|
+
background: white;
|
|
112
|
+
color: #667eea;
|
|
113
|
+
border: none;
|
|
114
|
+
border-radius: 4px;
|
|
180
115
|
cursor: pointer;
|
|
116
|
+
transition: transform 0.2s;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
button:hover {
|
|
120
|
+
transform: scale(1.05);
|
|
181
121
|
}
|
|
182
122
|
</style>
|
|
183
123
|
```
|
|
@@ -186,15 +126,17 @@ Create `hello-world.html`:
|
|
|
186
126
|
|
|
187
127
|
```html
|
|
188
128
|
<!DOCTYPE html>
|
|
189
|
-
<html>
|
|
129
|
+
<html lang="en">
|
|
190
130
|
<head>
|
|
191
|
-
<
|
|
131
|
+
<meta charset="UTF-8" />
|
|
132
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
133
|
+
<title>My LadrillosJS App</title>
|
|
192
134
|
</head>
|
|
193
135
|
<body>
|
|
194
136
|
<!-- Use your component -->
|
|
195
137
|
<hello-world></hello-world>
|
|
196
138
|
|
|
197
|
-
<!-- Register component -->
|
|
139
|
+
<!-- Register the component -->
|
|
198
140
|
<script type="module">
|
|
199
141
|
import { registerComponent } from "ladrillosjs";
|
|
200
142
|
registerComponent("hello-world", "./hello-world.html");
|
|
@@ -203,231 +145,267 @@ Create `hello-world.html`:
|
|
|
203
145
|
</html>
|
|
204
146
|
```
|
|
205
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
|
+
|
|
206
166
|
## Core Concepts
|
|
207
167
|
|
|
208
168
|
### Component Registration
|
|
209
169
|
|
|
210
|
-
Register
|
|
170
|
+
Register components to make them available as custom HTML elements:
|
|
211
171
|
|
|
212
172
|
```javascript
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
173
|
+
import { registerComponent, registerComponents } from "ladrillosjs";
|
|
174
|
+
|
|
175
|
+
// Register a single component
|
|
176
|
+
await registerComponent("my-button", "./components/my-button.html");
|
|
216
177
|
|
|
217
|
-
//
|
|
218
|
-
import { registerComponents } from "ladrillosjs";
|
|
178
|
+
// Register multiple components
|
|
219
179
|
await registerComponents([
|
|
220
180
|
{ name: "app-header", path: "./components/header.html" },
|
|
221
181
|
{ name: "app-footer", path: "./components/footer.html" },
|
|
222
182
|
{ name: "user-card", path: "./components/user-card.html" },
|
|
223
183
|
]);
|
|
224
184
|
|
|
225
|
-
//
|
|
226
|
-
|
|
185
|
+
// Disable Shadow DOM for a component
|
|
186
|
+
await registerComponent("global-styles", "./components/global.html", false);
|
|
227
187
|
```
|
|
228
188
|
|
|
229
|
-
**Note:** The `registerComponents` function is planned for v2.0 to enable bulk registration with concurrency control.
|
|
230
|
-
|
|
231
189
|
### State Management
|
|
232
190
|
|
|
233
|
-
Components have reactive state that automatically triggers re-renders
|
|
191
|
+
Components have reactive state that automatically triggers re-renders when changed:
|
|
234
192
|
|
|
235
193
|
```html
|
|
236
194
|
<div>
|
|
237
|
-
<h2>
|
|
238
|
-
<p>
|
|
239
|
-
<button onclick="
|
|
240
|
-
<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>
|
|
241
200
|
</div>
|
|
242
201
|
|
|
243
202
|
<script>
|
|
244
|
-
//
|
|
245
|
-
let score = 0;
|
|
203
|
+
// State variables - automatically reactive
|
|
246
204
|
let count = 0;
|
|
247
|
-
let user = {
|
|
248
|
-
name: "Player 1",
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
const updateScore = () => {
|
|
252
|
-
// Direct assignment triggers re-render automatically
|
|
253
|
-
score++;
|
|
254
|
-
};
|
|
205
|
+
let user = { name: "John", age: 25 };
|
|
255
206
|
|
|
256
207
|
const increment = () => {
|
|
257
|
-
//
|
|
258
|
-
count++; // Increment
|
|
259
|
-
count += 5; // Compound assignment
|
|
260
|
-
count = count * 2; // Direct assignment
|
|
208
|
+
count++; // Automatically triggers re-render
|
|
261
209
|
};
|
|
262
210
|
|
|
263
|
-
// You can also use the explicit setState method
|
|
264
211
|
const updateUser = () => {
|
|
265
|
-
|
|
212
|
+
user.name = "Jane"; // Direct mutation triggers re-render
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// You can also use $setState for explicit updates
|
|
216
|
+
const reset = () => {
|
|
217
|
+
$setState({ count: 0, user: { name: "Anonymous", age: 0 } });
|
|
266
218
|
};
|
|
267
219
|
</script>
|
|
268
220
|
```
|
|
269
221
|
|
|
270
|
-
**
|
|
222
|
+
**Available State Utilities:**
|
|
271
223
|
|
|
272
|
-
-
|
|
273
|
-
-
|
|
274
|
-
-
|
|
224
|
+
- Direct assignment: `count++`, `name = "New"`
|
|
225
|
+
- `$setState(updates)`: Merge updates into state
|
|
226
|
+
- `$getState()`: Access state in external modules
|
|
275
227
|
|
|
276
228
|
### Event Handling
|
|
277
229
|
|
|
278
|
-
|
|
230
|
+
Attach event handlers directly to elements:
|
|
279
231
|
|
|
280
232
|
```html
|
|
281
233
|
<!-- Method reference -->
|
|
282
|
-
<button onclick="handleClick">Click me
|
|
234
|
+
<button onclick="handleClick(event)">Click me</button>
|
|
283
235
|
|
|
284
236
|
<!-- Function with arguments -->
|
|
285
|
-
<button onclick="addItem('
|
|
237
|
+
<button onclick="addItem('apple', 5)">Add Item</button>
|
|
286
238
|
|
|
287
239
|
<!-- Inline arrow function -->
|
|
288
|
-
<button onclick="
|
|
289
|
-
|
|
290
|
-
<!--
|
|
291
|
-
<
|
|
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
|
+
/>
|
|
292
251
|
|
|
293
252
|
<script>
|
|
294
|
-
let count = 0;
|
|
295
253
|
let items = [];
|
|
254
|
+
let isValid = false;
|
|
296
255
|
|
|
297
256
|
const handleClick = (event) => {
|
|
298
|
-
console.log("
|
|
299
|
-
count++;
|
|
257
|
+
console.log("Button clicked!", event);
|
|
300
258
|
};
|
|
301
259
|
|
|
302
|
-
const addItem = (name,
|
|
303
|
-
items = [...items, { name,
|
|
260
|
+
const addItem = (name, quantity) => {
|
|
261
|
+
items = [...items, { name, quantity }];
|
|
262
|
+
console.log("Items:", items);
|
|
304
263
|
};
|
|
305
264
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
265
|
+
const validateInput = (e) => {
|
|
266
|
+
const value = e.target.value;
|
|
267
|
+
isValid = value.length >= 3;
|
|
309
268
|
};
|
|
310
269
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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);
|
|
277
|
+
};
|
|
316
278
|
</script>
|
|
317
279
|
```
|
|
318
280
|
|
|
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
281
|
### Data Binding
|
|
326
282
|
|
|
327
|
-
|
|
283
|
+
#### One-Way Binding (Display Data)
|
|
328
284
|
|
|
329
|
-
|
|
285
|
+
Use curly braces `{}` to display state in your template:
|
|
330
286
|
|
|
331
287
|
```html
|
|
332
288
|
<div>
|
|
333
289
|
<h1>{title}</h1>
|
|
334
290
|
<p>{user.name} - {user.email}</p>
|
|
335
|
-
<span>
|
|
291
|
+
<span>Total: {items.length} items</span>
|
|
292
|
+
<p>Formatted: {formatPrice(price)}</p>
|
|
336
293
|
</div>
|
|
337
294
|
|
|
338
295
|
<script>
|
|
339
296
|
let title = "My App";
|
|
340
297
|
let user = { name: "John", email: "john@example.com" };
|
|
341
|
-
let items = [
|
|
298
|
+
let items = ["apple", "banana", "orange"];
|
|
299
|
+
let price = 29.99;
|
|
300
|
+
|
|
301
|
+
const formatPrice = (value) => `$${value.toFixed(2)}`;
|
|
342
302
|
</script>
|
|
343
303
|
```
|
|
344
304
|
|
|
345
|
-
#### Two-Way Binding (
|
|
305
|
+
#### Two-Way Binding (Form Inputs)
|
|
346
306
|
|
|
347
|
-
Use the `$bind`
|
|
307
|
+
Use the `$bind` attribute for automatic synchronization between inputs and state:
|
|
348
308
|
|
|
349
309
|
```html
|
|
350
310
|
<div>
|
|
351
|
-
<h2>Hello, {
|
|
352
|
-
<input type="text" $bind="name" placeholder="
|
|
311
|
+
<h2>Hello, {name}!</h2>
|
|
312
|
+
<input type="text" $bind="name" placeholder="Your name" />
|
|
353
313
|
|
|
354
|
-
<p>Email: {
|
|
314
|
+
<p>Email: {email}</p>
|
|
355
315
|
<input type="email" $bind="email" />
|
|
356
316
|
|
|
357
|
-
<p>Bio: {
|
|
317
|
+
<p>Bio: {bio}</p>
|
|
358
318
|
<textarea $bind="bio"></textarea>
|
|
359
319
|
|
|
360
|
-
<p>Country: {
|
|
320
|
+
<p>Country: {country}</p>
|
|
361
321
|
<select $bind="country">
|
|
362
322
|
<option value="us">United States</option>
|
|
363
323
|
<option value="uk">United Kingdom</option>
|
|
364
324
|
<option value="ca">Canada</option>
|
|
365
325
|
</select>
|
|
326
|
+
|
|
327
|
+
<label>
|
|
328
|
+
<input type="checkbox" $bind="subscribe" />
|
|
329
|
+
Subscribe to newsletter: {subscribe}
|
|
330
|
+
</label>
|
|
366
331
|
</div>
|
|
367
332
|
|
|
368
333
|
<script>
|
|
369
|
-
// Variables
|
|
370
|
-
let
|
|
371
|
-
let
|
|
372
|
-
let
|
|
373
|
-
let
|
|
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;
|
|
374
340
|
</script>
|
|
375
341
|
```
|
|
376
342
|
|
|
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
343
|
### Conditional Rendering
|
|
385
344
|
|
|
386
|
-
|
|
345
|
+
Show or hide elements based on conditions:
|
|
387
346
|
|
|
388
347
|
```html
|
|
389
348
|
<div>
|
|
390
|
-
<h1>Shopping Cart
|
|
349
|
+
<h1>Shopping Cart</h1>
|
|
391
350
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
</div>
|
|
351
|
+
<!-- Simple condition -->
|
|
352
|
+
<p $if="{items.length === 0}">Your cart is empty</p>
|
|
395
353
|
|
|
396
|
-
|
|
397
|
-
|
|
354
|
+
<!-- Multiple conditions -->
|
|
355
|
+
<div $if="{items.length > 0 && items.length < 5}">
|
|
356
|
+
<p>You have {items.length} items</p>
|
|
398
357
|
</div>
|
|
399
358
|
|
|
400
|
-
<div
|
|
401
|
-
<p>
|
|
359
|
+
<div $else-if="{items.length >= 5}">
|
|
360
|
+
<p>Your cart is full! ({items.length} items)</p>
|
|
402
361
|
</div>
|
|
403
362
|
|
|
404
|
-
|
|
405
|
-
<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>
|
|
406
373
|
</div>
|
|
407
374
|
|
|
408
375
|
<script>
|
|
409
|
-
let items = [
|
|
376
|
+
let items = [];
|
|
410
377
|
let isLoggedIn = false;
|
|
378
|
+
let user = null;
|
|
411
379
|
|
|
412
380
|
const login = () => {
|
|
413
381
|
isLoggedIn = true;
|
|
382
|
+
user = { name: "John", role: "Admin" };
|
|
414
383
|
};
|
|
415
384
|
|
|
416
385
|
const logout = () => {
|
|
417
386
|
isLoggedIn = false;
|
|
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
|
+
}
|
|
418
396
|
};
|
|
419
397
|
</script>
|
|
420
398
|
```
|
|
421
399
|
|
|
422
400
|
**Conditional Directives:**
|
|
423
401
|
|
|
424
|
-
-
|
|
425
|
-
-
|
|
426
|
-
-
|
|
402
|
+
- `$if="{expression}"`: Show if expression is truthy
|
|
403
|
+
- `$else-if="{expression}"`: Chain multiple conditions
|
|
404
|
+
- `$else`: Fallback when previous conditions are false
|
|
427
405
|
|
|
428
406
|
### Slots
|
|
429
407
|
|
|
430
|
-
|
|
408
|
+
Project content from parent to child components:
|
|
431
409
|
|
|
432
410
|
```html
|
|
433
411
|
<!-- card.html -->
|
|
@@ -444,448 +422,393 @@ Content projection using slots:
|
|
|
444
422
|
</div>
|
|
445
423
|
</div>
|
|
446
424
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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>
|
|
453
441
|
```
|
|
454
442
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
### External Scripts
|
|
458
|
-
|
|
459
|
-
Load external JavaScript with components and bind them to the component context:
|
|
443
|
+
**Usage:**
|
|
460
444
|
|
|
461
445
|
```html
|
|
462
|
-
|
|
463
|
-
<
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
<
|
|
467
|
-
|
|
468
|
-
<!-- Regular external script (global scope) -->
|
|
469
|
-
<script src="https://cdn.example.com/library.js"></script>
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
For modules with `bind`, export a default function that receives the component context:
|
|
473
|
-
|
|
474
|
-
```javascript
|
|
475
|
-
// component-logic.js
|
|
476
|
-
export default function () {
|
|
477
|
-
// 'this' refers to the component instance
|
|
478
|
-
// Access component utilities
|
|
479
|
-
const { $state, $setState, $emit, $listen } = this;
|
|
480
|
-
|
|
481
|
-
this.formatDate = (date) => {
|
|
482
|
-
return new Intl.DateTimeFormat("en-US").format(date);
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
this.loadData = async () => {
|
|
486
|
-
const response = await fetch("/api/data");
|
|
487
|
-
const data = await response.json();
|
|
488
|
-
$setState({ data });
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
// Listen for events from other components
|
|
492
|
-
$listen("refresh-data", () => {
|
|
493
|
-
this.loadData();
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
// Called automatically if defined
|
|
497
|
-
if (this.init) {
|
|
498
|
-
this.init();
|
|
499
|
-
}
|
|
500
|
-
}
|
|
446
|
+
<my-card>
|
|
447
|
+
<h2 slot="header">User Profile</h2>
|
|
448
|
+
<p>Name: John Doe</p>
|
|
449
|
+
<p>Email: john@example.com</p>
|
|
450
|
+
<button slot="footer">Save Changes</button>
|
|
451
|
+
</my-card>
|
|
501
452
|
```
|
|
502
453
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
- **Better Context Binding**: External scripts get full access to component utilities
|
|
506
|
-
- **`$emit` and `$listen`**: Available in external scripts for event communication
|
|
507
|
-
- **Automatic Initialization**: Functions are auto-attached to component context
|
|
454
|
+
## Advanced Features
|
|
508
455
|
|
|
509
456
|
### Global Event Bus
|
|
510
457
|
|
|
511
|
-
|
|
458
|
+
The global event bus enables communication between components without prop drilling:
|
|
512
459
|
|
|
513
460
|
```javascript
|
|
514
|
-
//
|
|
515
|
-
// Emit an event
|
|
461
|
+
// Emit events to other components
|
|
516
462
|
$emit("user-logged-in", { userId: 123, username: "john" });
|
|
517
463
|
|
|
518
|
-
// Listen for events
|
|
464
|
+
// Listen for events from any component
|
|
519
465
|
$listen("user-logged-in", (data) => {
|
|
520
466
|
console.log(`User ${data.username} logged in`);
|
|
521
|
-
// Update local state
|
|
522
467
|
isLoggedIn = true;
|
|
523
468
|
currentUser = data;
|
|
524
469
|
});
|
|
525
470
|
```
|
|
526
471
|
|
|
527
|
-
|
|
472
|
+
**Example: Cross-Component Communication**
|
|
528
473
|
|
|
529
474
|
```html
|
|
530
475
|
<!-- header.html -->
|
|
531
476
|
<header>
|
|
532
|
-
<span
|
|
533
|
-
<button
|
|
477
|
+
<span $if="{isLoggedIn}">Welcome, {username}!</span>
|
|
478
|
+
<button $else onclick="requestLogin()">Login</button>
|
|
534
479
|
</header>
|
|
535
480
|
|
|
536
481
|
<script>
|
|
537
482
|
let isLoggedIn = false;
|
|
538
483
|
let username = "";
|
|
539
484
|
|
|
540
|
-
// Listen for login event from other components
|
|
541
485
|
$listen("user-logged-in", (user) => {
|
|
486
|
+
console.log("User logged in:", user);
|
|
542
487
|
isLoggedIn = true;
|
|
543
488
|
username = user.username;
|
|
544
489
|
});
|
|
545
490
|
|
|
546
|
-
const
|
|
547
|
-
$emit("show-login-
|
|
491
|
+
const requestLogin = () => {
|
|
492
|
+
$emit("show-login-dialog");
|
|
548
493
|
};
|
|
549
494
|
</script>
|
|
550
495
|
```
|
|
551
496
|
|
|
552
497
|
```html
|
|
553
498
|
<!-- login-form.html -->
|
|
554
|
-
<form onsubmit="handleLogin">
|
|
499
|
+
<form onsubmit="handleLogin(event)" $if="{showLoginDialog}">
|
|
555
500
|
<input type="text" $bind="username" placeholder="Username" />
|
|
556
501
|
<input type="password" $bind="password" placeholder="Password" />
|
|
557
502
|
<button type="submit">Login</button>
|
|
558
503
|
</form>
|
|
559
504
|
|
|
560
505
|
<script>
|
|
561
|
-
let
|
|
562
|
-
|
|
506
|
+
let showLoginDialog = false;
|
|
507
|
+
|
|
508
|
+
$listen("show-login-dialog", () => {
|
|
509
|
+
showLoginDialog = true;
|
|
510
|
+
});
|
|
563
511
|
|
|
564
512
|
const handleLogin = (e) => {
|
|
565
513
|
e.preventDefault();
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
username: $username,
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
// Clear form
|
|
574
|
-
$username = "";
|
|
575
|
-
$password = "";
|
|
514
|
+
$emit("user-logged-in", { userId: 123, username });
|
|
515
|
+
username = "";
|
|
516
|
+
password = "";
|
|
517
|
+
showLoginDialog = false;
|
|
576
518
|
};
|
|
577
519
|
</script>
|
|
578
520
|
```
|
|
579
521
|
|
|
580
|
-
|
|
522
|
+
### External Scripts
|
|
523
|
+
|
|
524
|
+
LadrillosJS supports three ways to include external JavaScript:
|
|
525
|
+
|
|
526
|
+
#### 1. Component-Scoped Scripts (Default)
|
|
581
527
|
|
|
582
|
-
|
|
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
|
|
528
|
+
Regular script tags execute within the component's context and have access to component state and utilities:
|
|
586
529
|
|
|
587
|
-
|
|
530
|
+
```html
|
|
531
|
+
<!-- alert-button.html -->
|
|
532
|
+
<button onclick="increaseCount()">{title}: {count}</button>
|
|
588
533
|
|
|
589
|
-
|
|
534
|
+
<!-- This script runs in the component context -->
|
|
535
|
+
<script src="./alert.js"></script>
|
|
536
|
+
```
|
|
590
537
|
|
|
591
|
-
**
|
|
538
|
+
**alert.js:**
|
|
592
539
|
|
|
593
540
|
```javascript
|
|
594
|
-
|
|
541
|
+
// Variables and functions are available to the component
|
|
542
|
+
let count = 0;
|
|
543
|
+
const title = "Button Count";
|
|
595
544
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
545
|
+
const increaseCount = () => {
|
|
546
|
+
count++; // Updates component state
|
|
547
|
+
};
|
|
548
|
+
```
|
|
600
549
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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>
|
|
604
564
|
```
|
|
605
565
|
|
|
606
|
-
**
|
|
566
|
+
**side.js:**
|
|
607
567
|
|
|
608
568
|
```javascript
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
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
|
+
}
|
|
617
582
|
});
|
|
618
583
|
```
|
|
619
584
|
|
|
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>
|
|
610
|
+
```
|
|
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
|
+
|
|
620
618
|
### Shadow DOM
|
|
621
619
|
|
|
622
|
-
Components use Shadow DOM by default for style encapsulation
|
|
620
|
+
Components use Shadow DOM by default for style encapsulation:
|
|
623
621
|
|
|
624
622
|
```javascript
|
|
625
|
-
//
|
|
626
|
-
await registerComponent("
|
|
627
|
-
|
|
628
|
-
// With Shadow DOM enabled (default)
|
|
623
|
+
// Shadow DOM enabled (default)
|
|
624
|
+
await registerComponent("isolated-widget", "./widget.html");
|
|
629
625
|
await registerComponent("isolated-widget", "./widget.html", true);
|
|
626
|
+
|
|
627
|
+
// Shadow DOM disabled
|
|
628
|
+
await registerComponent("global-styles", "./global.html", false);
|
|
630
629
|
```
|
|
631
630
|
|
|
632
631
|
**Shadow DOM Benefits:**
|
|
633
632
|
|
|
634
633
|
- **Style Isolation**: Component styles don't leak to global scope
|
|
635
|
-
- **Encapsulation**: Internal DOM
|
|
636
|
-
- **
|
|
634
|
+
- **Encapsulation**: Internal DOM is hidden from parent
|
|
635
|
+
- **Clean Separation**: Each component has its own styling context
|
|
637
636
|
|
|
638
637
|
**When to Disable:**
|
|
639
638
|
|
|
640
|
-
- Need global CSS
|
|
641
|
-
- Using third-party
|
|
642
|
-
-
|
|
639
|
+
- Need to apply global CSS frameworks (Bootstrap, Tailwind)
|
|
640
|
+
- Using third-party libraries that expect normal DOM
|
|
641
|
+
- Easier debugging without shadow boundaries
|
|
643
642
|
|
|
644
643
|
### Performance & Caching
|
|
645
644
|
|
|
646
|
-
|
|
645
|
+
LadrillosJS includes built-in performance optimizations:
|
|
647
646
|
|
|
648
647
|
#### Component Caching
|
|
649
648
|
|
|
650
|
-
- **
|
|
651
|
-
- **
|
|
652
|
-
- **
|
|
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
|
|
653
652
|
|
|
654
653
|
#### Function Caching
|
|
655
654
|
|
|
656
|
-
- **
|
|
657
|
-
- **
|
|
658
|
-
- **
|
|
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
|
|
659
658
|
|
|
660
|
-
**Performance
|
|
659
|
+
**Performance Tips:**
|
|
661
660
|
|
|
662
|
-
-
|
|
663
|
-
-
|
|
664
|
-
-
|
|
665
|
-
-
|
|
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
|
|
666
665
|
|
|
667
666
|
## API Reference
|
|
668
667
|
|
|
669
|
-
### Component Methods
|
|
670
|
-
|
|
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
|
|
689
|
-
|
|
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) |
|
|
697
|
-
|
|
698
668
|
### Registration Functions
|
|
699
669
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
| `registerComponents(components)` | **Coming soon** - Register multiple components at once |
|
|
704
|
-
|
|
705
|
-
## Examples
|
|
706
|
-
|
|
707
|
-
### Component Communication
|
|
708
|
-
|
|
709
|
-
**v2.0 uses the global event bus instead of custom events:**
|
|
710
|
-
|
|
711
|
-
```html
|
|
712
|
-
<!-- parent.html -->
|
|
713
|
-
<div>
|
|
714
|
-
<h2>Parent Component</h2>
|
|
715
|
-
<p>Messages received: {messageCount}</p>
|
|
716
|
-
<child-component></child-component>
|
|
717
|
-
</div>
|
|
718
|
-
|
|
719
|
-
<script>
|
|
720
|
-
let messageCount = 0;
|
|
721
|
-
|
|
722
|
-
// Listen for events from child
|
|
723
|
-
$listen("child-message", (data) => {
|
|
724
|
-
console.log("Received from child:", data);
|
|
725
|
-
messageCount++;
|
|
726
|
-
});
|
|
727
|
-
</script>
|
|
670
|
+
```typescript
|
|
671
|
+
registerComponent(name: string, path: string, useShadowDOM?: boolean): Promise<void>
|
|
672
|
+
registerComponents(components: Array<{name, path, useShadowDOM?}>): Promise<void>
|
|
728
673
|
```
|
|
729
674
|
|
|
730
|
-
|
|
731
|
-
<!-- child.html -->
|
|
732
|
-
<div>
|
|
733
|
-
<h3>Child Component</h3>
|
|
734
|
-
<button onclick="sendMessage">Send Message to Parent</button>
|
|
735
|
-
</div>
|
|
675
|
+
### Component Utilities
|
|
736
676
|
|
|
737
|
-
|
|
738
|
-
let count = 0;
|
|
677
|
+
Available within component `<script>` tags:
|
|
739
678
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
message: `Hello from child! (${count})`,
|
|
745
|
-
timestamp: Date.now(),
|
|
746
|
-
});
|
|
747
|
-
};
|
|
748
|
-
</script>
|
|
749
|
-
```
|
|
679
|
+
```typescript
|
|
680
|
+
// State management
|
|
681
|
+
$getState(): object // Access component state
|
|
682
|
+
$setState(updates: object): void // Update state and re-render
|
|
750
683
|
|
|
751
|
-
|
|
684
|
+
// Event bus
|
|
685
|
+
$emit(eventName: string, data?: any): void
|
|
686
|
+
$listen(eventName: string, callback: (data?) => void): () => void
|
|
752
687
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
const card = document.createElement("user-card");
|
|
757
|
-
card.setAttribute("user-id", userData.id);
|
|
758
|
-
card.setAttribute("name", userData.name);
|
|
759
|
-
card.setAttribute("email", userData.email);
|
|
760
|
-
document.querySelector("#user-list").appendChild(card);
|
|
761
|
-
};
|
|
688
|
+
// DOM queries (respects Shadow DOM boundaries)
|
|
689
|
+
$querySelector(selector: string): Element | null
|
|
690
|
+
$querySelectorAll(selector: string): NodeListOf<Element>
|
|
762
691
|
|
|
763
|
-
//
|
|
764
|
-
|
|
765
|
-
.then((res) => res.json())
|
|
766
|
-
.then((users) => users.forEach(createCard));
|
|
692
|
+
// Reactive variables (for external modules)
|
|
693
|
+
$reactive(name: string, initialValue: any): (value: any) => void
|
|
767
694
|
```
|
|
768
695
|
|
|
769
|
-
###
|
|
770
|
-
|
|
771
|
-
Use JSON.stringify for passing objects/arrays as attributes:
|
|
696
|
+
### Component Attributes
|
|
772
697
|
|
|
773
698
|
```html
|
|
774
|
-
<!--
|
|
775
|
-
<
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
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 -->
|
|
790
717
|
```
|
|
791
718
|
|
|
792
|
-
|
|
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
|
-
```
|
|
719
|
+
### Template Syntax
|
|
805
720
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
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)
|
|
721
|
+
```html
|
|
722
|
+
<!-- Variable interpolation -->
|
|
723
|
+
{variableName} {object.property} {array[0]}
|
|
824
724
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
- **After:** Use `$bind` attribute for automatic two-way binding
|
|
725
|
+
<!-- Function calls -->
|
|
726
|
+
{functionName(arg1, arg2)} {object.method()}
|
|
828
727
|
|
|
829
|
-
|
|
728
|
+
<!-- Expressions -->
|
|
729
|
+
{count + 1} {isActive ? 'Yes' : 'No'} {items.length > 0 ? 'Has items' : 'Empty'}
|
|
730
|
+
```
|
|
830
731
|
|
|
831
|
-
|
|
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`
|
|
732
|
+
## Development
|
|
836
733
|
|
|
837
|
-
|
|
734
|
+
### Prerequisites
|
|
838
735
|
|
|
839
|
-
|
|
736
|
+
- **Node.js**: v20.19+ or v22.12+
|
|
737
|
+
- **npm**: v9+ (comes with Node.js)
|
|
840
738
|
|
|
841
|
-
###
|
|
739
|
+
### Setup
|
|
842
740
|
|
|
843
741
|
```bash
|
|
742
|
+
# Clone the repository
|
|
743
|
+
git clone https://github.com/drubiodev/LadrillosJS.git
|
|
744
|
+
cd LadrillosJS
|
|
745
|
+
|
|
844
746
|
# Install dependencies
|
|
845
747
|
npm install
|
|
846
748
|
|
|
847
|
-
#
|
|
749
|
+
# Start development server
|
|
848
750
|
npm run dev
|
|
849
751
|
|
|
752
|
+
# Build the library
|
|
753
|
+
npm run build
|
|
754
|
+
|
|
850
755
|
# Run tests
|
|
851
756
|
npm test
|
|
852
757
|
|
|
853
758
|
# Run tests with coverage
|
|
854
759
|
npm run test:coverage
|
|
855
|
-
|
|
856
|
-
# Build the library
|
|
857
|
-
npm run build
|
|
858
|
-
|
|
859
|
-
# Build TypeScript types
|
|
860
|
-
npm run build:types
|
|
861
760
|
```
|
|
862
761
|
|
|
863
762
|
### Project Structure
|
|
864
763
|
|
|
865
764
|
```
|
|
866
|
-
|
|
867
|
-
├──
|
|
868
|
-
├──
|
|
869
|
-
│ ├──
|
|
870
|
-
│ ├──
|
|
871
|
-
│ ├──
|
|
872
|
-
│ ├──
|
|
873
|
-
│ ├──
|
|
874
|
-
│ ├──
|
|
875
|
-
│ │
|
|
876
|
-
│
|
|
877
|
-
│ │ ├──
|
|
878
|
-
│ │
|
|
879
|
-
│ └──
|
|
880
|
-
│
|
|
881
|
-
|
|
882
|
-
│ ├──
|
|
883
|
-
│
|
|
884
|
-
|
|
885
|
-
│
|
|
886
|
-
└──
|
|
887
|
-
|
|
888
|
-
|
|
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
|
+
```
|
|
802
|
+
|
|
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
|
|
889
812
|
```
|
|
890
813
|
|
|
891
814
|
## License
|
|
@@ -896,4 +819,4 @@ MIT License - see [LICENSE](LICENSE) file for details.
|
|
|
896
819
|
|
|
897
820
|
**LadrillosJS v2.0** - Built with ❤️ by [Daniel Rubio](https://github.com/drubiodev)
|
|
898
821
|
|
|
899
|
-
|
|
822
|
+
🌟 [GitHub](https://github.com/drubiodev/LadrillosJS) • 📦 [NPM](https://www.npmjs.com/package/ladrillosjs) • 📖 [Documentation](https://github.com/drubiodev/LadrillosJS/blob/main/README.md)
|