ladrillosjs 2.0.0-beta.1 → 2.0.0-beta.10
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/LICENSE +1 -1
- package/README.md +451 -678
- package/dist/core/builtins/lazyElement.d.ts +35 -0
- package/dist/core/cache/expressionCache.d.ts +15 -0
- package/dist/core/component/bindingParser.d.ts +2 -0
- package/dist/{cache/index.d.ts → core/component/cache.d.ts} +8 -2
- package/dist/core/component/extract.d.ts +2 -0
- package/dist/core/component/loader.d.ts +10 -0
- package/dist/core/component/webcomponent.d.ts +29 -0
- package/dist/core/configure.d.ts +28 -0
- package/dist/core/diff/listDiff.d.ts +99 -0
- package/dist/core/directives/directiveProcessor.d.ts +58 -0
- package/dist/core/events/eventBus.d.ts +136 -0
- package/dist/core/helpers/frameworkHelpers.d.ts +38 -0
- package/dist/core/html/htmlparser.d.ts +15 -12
- package/dist/core/js/moduleExecutor.d.ts +118 -0
- package/dist/core/js/reactivity.d.ts +52 -0
- package/dist/core/js/scriptParser.d.ts +52 -3
- package/dist/core/ladrillos.d.ts +68 -0
- package/dist/core/lazy/index.d.ts +6 -0
- package/dist/core/lazy/lazyLoader.d.ts +22 -0
- package/dist/core/lazy/lazyStrategies.d.ts +73 -0
- package/dist/core/scheduler/batchScheduler.d.ts +97 -0
- package/dist/core.d.ts +15 -0
- package/dist/core.js +2 -0
- package/dist/core.js.map +1 -0
- package/dist/events.d.ts +20 -0
- package/dist/events.js +1 -0
- package/dist/index.d.ts +28 -60
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/lazy.d.ts +17 -0
- package/dist/lazy.js +1 -0
- package/dist/shared-CCPr4_4R.js +3 -0
- package/dist/shared-CCPr4_4R.js.map +1 -0
- package/dist/shared-D-P0qKQY.js +2 -0
- package/dist/shared-D-P0qKQY.js.map +1 -0
- package/dist/shared-cwUZzSZ7.js +2 -0
- package/dist/shared-cwUZzSZ7.js.map +1 -0
- package/dist/types/index.d.ts +171 -0
- package/dist/utils/devWarnings.d.ts +133 -0
- package/dist/utils/directives.d.ts +149 -0
- package/dist/utils/jsevents.d.ts +4 -0
- package/dist/utils/keyModifiers.d.ts +106 -0
- package/dist/utils/regex.d.ts +4 -1
- package/dist/utils/sandbox.d.ts +25 -0
- package/package.json +97 -49
- package/dist/cache/functionCache.d.ts +0 -15
- package/dist/core/componentParser.d.ts +0 -29
- package/dist/core/componentSource.d.ts +0 -12
- package/dist/core/eventBus.d.ts +0 -41
- package/dist/core/html/htmlRenderer.d.ts +0 -18
- package/dist/core/main.d.ts +0 -9
- package/dist/core/webcomponent.d.ts +0 -2
- package/dist/index-CXHidyhO.js +0 -8
- package/dist/index-CXHidyhO.js.map +0 -1
- package/dist/index-VkDZJVOR.mjs +0 -361
- package/dist/index-VkDZJVOR.mjs.map +0 -1
- package/dist/ladrillosjs.cjs.js +0 -2
- package/dist/ladrillosjs.cjs.js.map +0 -1
- package/dist/ladrillosjs.es.js +0 -14
- package/dist/ladrillosjs.es.js.map +0 -1
- package/dist/ladrillosjs.umd.js +0 -76
- package/dist/ladrillosjs.umd.js.map +0 -1
- package/dist/types/LadrilloTypes.d.ts +0 -51
- package/dist/utils/logger.d.ts +0 -23
- 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/dist/core/css/{cssParser.d.ts → cssParser/cssParser.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,899 +1,672 @@
|
|
|
1
1
|
# LadrillosJS
|
|
2
2
|
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
**Version 2.0** - Now rewritten in TypeScript with enhanced performance, better developer experience, and powerful new features.
|
|
8
|
-
|
|
9
|
-
"I designed this framework to empower developers with the ability to componentize their code efficiently and effectively, without the need for a full-scale framework. By focusing on simplicity and leveraging core web fundamentals, my goal was to create a lightweight and accessible solution that enhances development while staying true to the basics."
|
|
10
|
-
|
|
11
|
-
## Table of Contents
|
|
12
|
-
|
|
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
|
-
- [Installation](#installation)
|
|
19
|
-
- [Your First Component](#your-first-component)
|
|
20
|
-
- [Core Concepts](#core-concepts)
|
|
21
|
-
- [Component Registration](#component-registration)
|
|
22
|
-
- [State Management](#state-management)
|
|
23
|
-
- [Event Handling](#event-handling)
|
|
24
|
-
- [Data Binding](#data-binding)
|
|
25
|
-
- [Conditional Rendering](#conditional-rendering)
|
|
26
|
-
- [Slots](#slots)
|
|
27
|
-
- [Advanced Features](#advanced-features)
|
|
28
|
-
- [External Scripts](#external-scripts)
|
|
29
|
-
- [Global Event Bus](#global-event-bus)
|
|
30
|
-
- [Shadow DOM](#shadow-dom)
|
|
31
|
-
- [Performance & Caching](#performance--caching)
|
|
32
|
-
- [API Reference](#api-reference)
|
|
33
|
-
- [Examples](#examples)
|
|
34
|
-
- [Component Communication](#component-communication)
|
|
35
|
-
- [Dynamic Component Creation](#dynamic-component-creation)
|
|
36
|
-
- [Passing Complex Data](#passing-complex-data)
|
|
37
|
-
- [Migration Guide (v1.x to v2.0)](#migration-guide-v1x-to-v20)
|
|
38
|
-
- [Contributing](#contributing)
|
|
39
|
-
- [License](#license)
|
|
40
|
-
|
|
41
|
-
## Features
|
|
42
|
-
|
|
43
|
-
- 🚀 **Zero Dependencies** - Pure JavaScript, no build tools required
|
|
44
|
-
- 📦 **Single-File Components** - HTML, CSS, and JavaScript in one file
|
|
45
|
-
- ⚡ **Reactive State** - Automatic re-rendering on state changes with optimized proxies
|
|
46
|
-
- 🎯 **Event System** - Built-in event emission and global event bus for component communication
|
|
47
|
-
- 🔄 **Two-Way Data Binding** - Seamless binding for form inputs with `$bind`
|
|
48
|
-
- 🎨 **Scoped Styles** - Component styles with optional Shadow DOM
|
|
49
|
-
- 🏪 **Global Event Bus** - Cross-component communication without prop drilling
|
|
50
|
-
- 🔌 **Slots Support** - Content projection with named and default slots
|
|
51
|
-
- 📝 **TypeScript Support** - Full type definitions and TypeScript source code
|
|
52
|
-
- 🎭 **Conditional Rendering** - `data-if`, `data-else-if`, and `data-else` directives
|
|
53
|
-
- 🚄 **Smart Caching** - LRU cache for components and compiled functions
|
|
54
|
-
- 🔧 **Framework Utilities** - `$state`, `$setState`, `$emit`, `$listen`, `$querySelector` helpers
|
|
55
|
-
- ⚙️ **Automatic Reactivity** - Variable assignments automatically trigger re-renders
|
|
56
|
-
- 🧩 **External Scripts** - Load and bind external JavaScript with your components
|
|
57
|
-
|
|
58
|
-
## Getting Started
|
|
59
|
-
|
|
60
|
-
### Prerequisites
|
|
61
|
-
|
|
62
|
-
- **Node.js**: Version 20.19+ or 22.12+ is required for development
|
|
63
|
-
- **TypeScript**: v5.8+ (included in devDependencies)
|
|
64
|
-
|
|
65
|
-
### What's New in v2.0
|
|
66
|
-
|
|
67
|
-
- **🔄 Complete TypeScript Rewrite** - Full type safety and improved IDE support
|
|
68
|
-
- **⚡ Performance Enhancements** - LRU caching for components and compiled functions
|
|
69
|
-
- **🎯 Global Event Bus** - New `$emit` and `$listen` for cross-component communication
|
|
70
|
-
- **🔧 Framework Utilities** - New `$state`, `$setState`, `$querySelector` helpers
|
|
71
|
-
- **🚀 Automatic Reactivity** - Variable assignments like `count++` automatically trigger re-renders
|
|
72
|
-
- **📦 Better Build System** - Vite-powered builds with sourcemaps and multiple output formats (ESM, UMD, CJS)
|
|
73
|
-
- **🧪 Testing** - Vitest with coverage reporting
|
|
74
|
-
- **🎭 Enhanced Conditionals** - More robust `data-if`, `data-else-if`, `data-else` rendering
|
|
75
|
-
- **🔄 Two-Way Binding** - Simplified with `$bind` prefix for automatic state synchronization
|
|
76
|
-
- **🧹 Memory Management** - Automatic cleanup of event listeners on component disconnect
|
|
77
|
-
|
|
78
|
-
### Example Applications
|
|
79
|
-
|
|
80
|
-
The repository includes several example applications that demonstrate various features:
|
|
81
|
-
|
|
82
|
-
- **[Todo App](samples/apps/todo)** - Classic todo list with component composition
|
|
83
|
-
- **[Notes App](samples/apps/notes)** - Multi-component app with global event bus
|
|
84
|
-
- **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
|
|
85
|
-
- **[API Example](samples/apps/api)** - Fetching and displaying external data
|
|
86
|
-
- **[Business Card](samples/apps/biz)** - Editable form with two-way data binding using `$bind`
|
|
87
|
-
- **[Button Game](samples/apps/button-game)** - Interactive game with component events
|
|
88
|
-
- **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation system
|
|
89
|
-
- **[Document Chat](samples/apps/document-chat)** - Chat interface with component communication
|
|
90
|
-
- **[Docs](samples/apps/docs)** - Documentation viewer with syntax highlighting
|
|
91
|
-
|
|
92
|
-
To run the examples:
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/drubiodev/LadrillosJS/refs/heads/main/LadrillosJS.jpg" alt="LadrillosJS" width="300"/>
|
|
5
|
+
</p>
|
|
93
6
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
cd LadrillosJS
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>A lightweight, zero-dependency web component framework.</strong>
|
|
9
|
+
</p>
|
|
98
10
|
|
|
99
|
-
|
|
100
|
-
npm
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/ladrillosjs"><img src="https://img.shields.io/npm/v/ladrillosjs.svg" alt="npm version"></a>
|
|
13
|
+
<a href="https://github.com/drubiodev/LadrillosJS/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/ladrillosjs.svg" alt="license"></a>
|
|
14
|
+
<a href="https://bundlephobia.com/package/ladrillosjs"><img src="https://img.shields.io/bundlephobia/minzip/ladrillosjs" alt="bundle size"></a>
|
|
15
|
+
<a href="https://github.com/drubiodev/LadrillosJS"><img src="https://img.shields.io/github/stars/drubiodev/LadrillosJS?style=social" alt="GitHub stars"></a>
|
|
16
|
+
</p>
|
|
101
17
|
|
|
102
|
-
|
|
103
|
-
|
|
18
|
+
<p align="center">
|
|
19
|
+
Build modular web apps with simple HTML components. No virtual DOM, no complex tooling required.
|
|
20
|
+
</p>
|
|
104
21
|
|
|
105
|
-
|
|
106
|
-
npm run build
|
|
107
|
-
|
|
108
|
-
# Run tests
|
|
109
|
-
npm test
|
|
110
|
-
|
|
111
|
-
# Run tests with coverage
|
|
112
|
-
npm run test:coverage
|
|
113
|
-
```
|
|
22
|
+
---
|
|
114
23
|
|
|
115
|
-
##
|
|
24
|
+
## 📑 Table of Contents
|
|
25
|
+
|
|
26
|
+
- [Quick Start](#-quick-start)
|
|
27
|
+
- [Installation](#-installation)
|
|
28
|
+
- [Core Concepts](#-core-concepts)
|
|
29
|
+
- [Directives](#-directives)
|
|
30
|
+
- [Event Bus](#-event-bus)
|
|
31
|
+
- [Element References](#-element-references)
|
|
32
|
+
- [Lazy Loading](#-lazy-loading)
|
|
33
|
+
- [API Reference](#-api-reference)
|
|
34
|
+
- [Using with Vite](#-using-with-vite)
|
|
35
|
+
- [Browser Support](#-browser-support)
|
|
36
|
+
- [Examples](#-examples)
|
|
37
|
+
- [Contributing](#-contributing)
|
|
38
|
+
- [License](#-license)
|
|
116
39
|
|
|
117
|
-
|
|
40
|
+
---
|
|
118
41
|
|
|
119
|
-
|
|
120
|
-
npm install ladrillosjs
|
|
121
|
-
```
|
|
42
|
+
## 🚀 Quick Start
|
|
122
43
|
|
|
123
|
-
###
|
|
44
|
+
### 1. Add the Script
|
|
124
45
|
|
|
125
46
|
```html
|
|
126
|
-
<!-- Latest version -->
|
|
127
47
|
<script type="module">
|
|
128
|
-
import
|
|
129
|
-
|
|
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");
|
|
48
|
+
import ladrillosjs from "https://cdn.jsdelivr.net/npm/ladrillosjs@2/dist/index.js";
|
|
49
|
+
window.ladrillosjs = ladrillosjs;
|
|
137
50
|
</script>
|
|
138
51
|
```
|
|
139
52
|
|
|
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.
|
|
53
|
+
### 2. Create a Component
|
|
143
54
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
Create `hello-world.html`:
|
|
55
|
+
Save this as `counter.html`:
|
|
147
56
|
|
|
148
57
|
```html
|
|
149
|
-
|
|
150
|
-
<div class="
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
58
|
+
<div class="counter">
|
|
59
|
+
<div class="count-display">{count}</div>
|
|
60
|
+
<div class="buttons">
|
|
61
|
+
<button onclick="count--">−</button>
|
|
62
|
+
<button onclick="count = 0">Reset</button>
|
|
63
|
+
<button onclick="count++">+</button>
|
|
64
|
+
</div>
|
|
65
|
+
<p>Double: {count * 2} | Squared: {count * count}</p>
|
|
154
66
|
</div>
|
|
155
67
|
|
|
156
68
|
<script>
|
|
157
|
-
// Component state
|
|
158
|
-
let title = "Welcome to LadrillosJS";
|
|
159
|
-
let name = "World";
|
|
160
69
|
let count = 0;
|
|
161
|
-
|
|
162
|
-
// Event handler
|
|
163
|
-
const greet = () => {
|
|
164
|
-
count++;
|
|
165
|
-
name = prompt("What's your name?") || "World";
|
|
166
|
-
};
|
|
167
70
|
</script>
|
|
168
71
|
|
|
169
72
|
<style>
|
|
170
|
-
.
|
|
73
|
+
.counter {
|
|
171
74
|
text-align: center;
|
|
172
75
|
padding: 2rem;
|
|
173
|
-
background: #f0f0f0;
|
|
174
|
-
border-radius: 8px;
|
|
175
76
|
}
|
|
176
|
-
|
|
77
|
+
.count-display {
|
|
78
|
+
font-size: 4rem;
|
|
79
|
+
font-weight: bold;
|
|
80
|
+
}
|
|
177
81
|
button {
|
|
178
82
|
padding: 0.5rem 1rem;
|
|
179
|
-
|
|
83
|
+
margin: 0.25rem;
|
|
180
84
|
cursor: pointer;
|
|
181
85
|
}
|
|
182
86
|
</style>
|
|
183
87
|
```
|
|
184
88
|
|
|
185
|
-
###
|
|
89
|
+
### 3. Use It
|
|
186
90
|
|
|
187
91
|
```html
|
|
188
92
|
<!DOCTYPE html>
|
|
189
93
|
<html>
|
|
190
94
|
<head>
|
|
191
|
-
<title>My App</title>
|
|
192
|
-
</head>
|
|
193
|
-
<body>
|
|
194
|
-
<!-- Use your component -->
|
|
195
|
-
<hello-world></hello-world>
|
|
196
|
-
|
|
197
|
-
<!-- Register component -->
|
|
198
95
|
<script type="module">
|
|
199
|
-
import { registerComponent } from "ladrillosjs";
|
|
200
|
-
registerComponent("
|
|
96
|
+
import { registerComponent } from "https://cdn.jsdelivr.net/npm/ladrillosjs@2/dist/index.js";
|
|
97
|
+
registerComponent("my-counter", "./counter.html");
|
|
201
98
|
</script>
|
|
99
|
+
</head>
|
|
100
|
+
<body>
|
|
101
|
+
<my-counter></my-counter>
|
|
202
102
|
</body>
|
|
203
103
|
</html>
|
|
204
104
|
```
|
|
205
105
|
|
|
206
|
-
|
|
106
|
+
That's it! Your reactive component is ready. 🎉
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 📦 Installation
|
|
207
111
|
|
|
208
|
-
###
|
|
112
|
+
### CDN (No Build Step)
|
|
209
113
|
|
|
210
|
-
|
|
114
|
+
LadrillosJS v2 is distributed as native ES modules. Import directly from a CDN:
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<!-- ES Module (recommended) -->
|
|
118
|
+
<script type="module">
|
|
119
|
+
import {
|
|
120
|
+
registerComponent,
|
|
121
|
+
registerComponents,
|
|
122
|
+
} from "https://cdn.jsdelivr.net/npm/ladrillosjs@2/dist/index.js";
|
|
123
|
+
|
|
124
|
+
registerComponent("my-component", "./component.html");
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<!-- Also available on unpkg -->
|
|
128
|
+
<script type="module">
|
|
129
|
+
import { registerComponent } from "https://unpkg.com/ladrillosjs@2/dist/index.js";
|
|
130
|
+
</script>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
> **Note:** LadrillosJS v2 is ESM-only. Legacy UMD/IIFE global builds are not published to npm.
|
|
134
|
+
|
|
135
|
+
### NPM (With Build Tools)
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npm install ladrillosjs
|
|
139
|
+
```
|
|
211
140
|
|
|
212
141
|
```javascript
|
|
142
|
+
import { registerComponent, registerComponents } from "ladrillosjs";
|
|
143
|
+
|
|
213
144
|
// Single component
|
|
214
|
-
|
|
215
|
-
await registerComponent("my-component", "./my-component.html");
|
|
145
|
+
registerComponent("my-counter", "./components/counter.html");
|
|
216
146
|
|
|
217
|
-
// Multiple components
|
|
218
|
-
import { registerComponents } from "ladrillosjs";
|
|
147
|
+
// Multiple components
|
|
219
148
|
await registerComponents([
|
|
220
149
|
{ name: "app-header", path: "./components/header.html" },
|
|
221
150
|
{ name: "app-footer", path: "./components/footer.html" },
|
|
222
|
-
{ name: "user-card", path: "./components/user-card.html" },
|
|
223
151
|
]);
|
|
224
|
-
|
|
225
|
-
// Using CDN
|
|
226
|
-
ladrillosjs.registerComponent("my-component", "./my-component.html");
|
|
227
152
|
```
|
|
228
153
|
|
|
229
|
-
|
|
154
|
+
### Granular Imports (Tree-Shaking)
|
|
230
155
|
|
|
231
|
-
|
|
156
|
+
```javascript
|
|
157
|
+
// Full API
|
|
158
|
+
import { registerComponent, $emit, $listen } from "ladrillosjs";
|
|
232
159
|
|
|
233
|
-
|
|
160
|
+
// Core only (no lazy loading, no event bus)
|
|
161
|
+
import { registerComponent } from "ladrillosjs/core";
|
|
234
162
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
<h2>User: {user.name}</h2>
|
|
238
|
-
<p>Score: {score}</p>
|
|
239
|
-
<button onclick="updateScore">Add Point</button>
|
|
240
|
-
<button onclick="increment">Count: {count}</button>
|
|
241
|
-
</div>
|
|
163
|
+
// Lazy loading strategies only
|
|
164
|
+
import { lazyOnVisible, lazyOnIdle } from "ladrillosjs/lazy";
|
|
242
165
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
let score = 0;
|
|
246
|
-
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
|
-
};
|
|
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
|
-
};
|
|
267
|
-
</script>
|
|
166
|
+
// Event bus only
|
|
167
|
+
import { $emit, $listen } from "ladrillosjs/events";
|
|
268
168
|
```
|
|
269
169
|
|
|
270
|
-
|
|
170
|
+
---
|
|
271
171
|
|
|
272
|
-
|
|
273
|
-
- **`$state`**: Direct access to component state within scripts
|
|
274
|
-
- **`$setState(updates)`**: Explicit state updates (merges with existing state)
|
|
172
|
+
## 📖 Core Concepts
|
|
275
173
|
|
|
276
|
-
###
|
|
174
|
+
### Template Bindings
|
|
277
175
|
|
|
278
|
-
|
|
176
|
+
Use `{expression}` to display reactive data. Any JavaScript expression works:
|
|
279
177
|
|
|
280
178
|
```html
|
|
281
|
-
|
|
282
|
-
<
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
179
|
+
<h1>{title}</h1>
|
|
180
|
+
<p>Hello, {user.name}!</p>
|
|
181
|
+
<span>Total: {items.length} items</span>
|
|
182
|
+
<p>Is adult: {age >= 18 ? 'Yes' : 'No'}</p>
|
|
183
|
+
```
|
|
286
184
|
|
|
287
|
-
|
|
288
|
-
<button onclick="(e) => console.log(e.target)">Log Target</button>
|
|
185
|
+
### Reactive State
|
|
289
186
|
|
|
290
|
-
|
|
291
|
-
<button onclick="notifyOthers">Emit Event</button>
|
|
187
|
+
Just declare variables with `let` — changes automatically update the DOM:
|
|
292
188
|
|
|
189
|
+
```html
|
|
293
190
|
<script>
|
|
294
191
|
let count = 0;
|
|
295
|
-
let
|
|
296
|
-
|
|
297
|
-
const handleClick = (event) => {
|
|
298
|
-
console.log("Clicked!", event);
|
|
299
|
-
count++;
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const addItem = (name, value) => {
|
|
303
|
-
items = [...items, { name, value }];
|
|
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
|
-
});
|
|
192
|
+
let user = { name: "Alice", role: "Developer" };
|
|
193
|
+
let items = ["Apple", "Banana", "Cherry"];
|
|
316
194
|
</script>
|
|
317
195
|
```
|
|
318
196
|
|
|
319
|
-
|
|
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
|
|
197
|
+
### Event Handlers
|
|
324
198
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
LadrillosJS supports both one-way and two-way data binding:
|
|
328
|
-
|
|
329
|
-
#### One-Way Binding (Template Interpolation)
|
|
199
|
+
Attach events directly in HTML with inline expressions or function calls:
|
|
330
200
|
|
|
331
201
|
```html
|
|
332
|
-
<
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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>
|
|
202
|
+
<button onclick="count++">Increment</button>
|
|
203
|
+
<button onclick="handleClick()">Click me</button>
|
|
204
|
+
<input onkeyup="search(event.target.value)" />
|
|
205
|
+
<form onsubmit="handleSubmit(event)">...</form>
|
|
343
206
|
```
|
|
344
207
|
|
|
345
|
-
|
|
208
|
+
### Two-Way Binding
|
|
346
209
|
|
|
347
|
-
Use
|
|
210
|
+
Use `$bind` to sync form inputs with state:
|
|
348
211
|
|
|
349
212
|
```html
|
|
350
|
-
<
|
|
351
|
-
|
|
352
|
-
<input type="text" $bind="name" placeholder="Enter your name" />
|
|
353
|
-
|
|
354
|
-
<p>Email: {$email}</p>
|
|
355
|
-
<input type="email" $bind="email" />
|
|
213
|
+
<input type="text" $bind="username" placeholder="Enter name" />
|
|
214
|
+
<p>Hello, {username}!</p>
|
|
356
215
|
|
|
357
|
-
|
|
358
|
-
<textarea $bind="bio"></textarea>
|
|
216
|
+
<textarea $bind="bio"></textarea>
|
|
359
217
|
|
|
360
|
-
|
|
361
|
-
<
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
<option value="ca">Canada</option>
|
|
365
|
-
</select>
|
|
366
|
-
</div>
|
|
218
|
+
<select $bind="country">
|
|
219
|
+
<option value="us">United States</option>
|
|
220
|
+
<option value="uk">United Kingdom</option>
|
|
221
|
+
</select>
|
|
367
222
|
|
|
368
223
|
<script>
|
|
369
|
-
|
|
370
|
-
let
|
|
371
|
-
let
|
|
372
|
-
let $bio = "";
|
|
373
|
-
let $country = "us";
|
|
224
|
+
let username = "";
|
|
225
|
+
let bio = "";
|
|
226
|
+
let country = "us";
|
|
374
227
|
</script>
|
|
375
228
|
```
|
|
376
229
|
|
|
377
|
-
|
|
230
|
+
---
|
|
378
231
|
|
|
379
|
-
|
|
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"`
|
|
232
|
+
## 🧩 Built-in Elements & Directives
|
|
383
233
|
|
|
384
234
|
### Conditional Rendering
|
|
385
235
|
|
|
386
|
-
|
|
236
|
+
Use `<if>`, `<else-if>`, and `<else>` to conditionally render elements:
|
|
387
237
|
|
|
388
238
|
```html
|
|
389
|
-
<
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
<div data-if="items.length === 0">
|
|
393
|
-
<p>Your cart is empty</p>
|
|
394
|
-
</div>
|
|
239
|
+
<if condition="status === 'loading'">Loading...</if>
|
|
240
|
+
<else-if condition="status === 'error'">Something went wrong!</else-if>
|
|
241
|
+
<else>Content loaded successfully!</else>
|
|
395
242
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
243
|
+
<script>
|
|
244
|
+
let status = "loading";
|
|
245
|
+
</script>
|
|
246
|
+
```
|
|
399
247
|
|
|
400
|
-
|
|
401
|
-
<p>You have many items!</p>
|
|
402
|
-
</div>
|
|
248
|
+
### Show/Hide (CSS Toggle)
|
|
403
249
|
|
|
404
|
-
|
|
405
|
-
<button data-else onclick="logout">Logout</button>
|
|
406
|
-
</div>
|
|
250
|
+
Use `<show>` to toggle visibility without removing from DOM (uses `display: none`):
|
|
407
251
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
let isLoggedIn = false;
|
|
252
|
+
```html
|
|
253
|
+
<show condition="isVisible">I can be shown or hidden</show>
|
|
411
254
|
|
|
412
|
-
|
|
413
|
-
isLoggedIn = true;
|
|
414
|
-
};
|
|
255
|
+
<button onclick="isVisible = !isVisible">Toggle</button>
|
|
415
256
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
};
|
|
257
|
+
<script>
|
|
258
|
+
let isVisible = true;
|
|
419
259
|
</script>
|
|
420
260
|
```
|
|
421
261
|
|
|
422
|
-
|
|
262
|
+
> **`<show>` vs `<if>`:** `<show>` toggles CSS display (children stay in DOM), `<if>` adds/removes children entirely.
|
|
423
263
|
|
|
424
|
-
|
|
425
|
-
- **`data-else-if="expression"`**: Chain multiple conditions
|
|
426
|
-
- **`data-else`**: Fallback when all previous conditions are false
|
|
264
|
+
### List Rendering
|
|
427
265
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
Content projection using slots:
|
|
266
|
+
Use `<for>` to render lists with optional index and key:
|
|
431
267
|
|
|
432
268
|
```html
|
|
433
|
-
<!--
|
|
434
|
-
<
|
|
435
|
-
<
|
|
436
|
-
<
|
|
437
|
-
</
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
<div
|
|
443
|
-
|
|
269
|
+
<!-- Simple list -->
|
|
270
|
+
<ul>
|
|
271
|
+
<for each="fruit in fruits">
|
|
272
|
+
<li>🍎 {fruit}</li>
|
|
273
|
+
</for>
|
|
274
|
+
</ul>
|
|
275
|
+
|
|
276
|
+
<!-- With index -->
|
|
277
|
+
<for each="(item, index) in items">
|
|
278
|
+
<div>#{index + 1}: {item}</div>
|
|
279
|
+
</for>
|
|
280
|
+
|
|
281
|
+
<!-- Object array with key -->
|
|
282
|
+
<for each="user in users" key="user.id">
|
|
283
|
+
<div>
|
|
284
|
+
<span>{user.avatar}</span>
|
|
285
|
+
<span>{user.name}</span>
|
|
286
|
+
<span>{user.role}</span>
|
|
444
287
|
</div>
|
|
445
|
-
</
|
|
288
|
+
</for>
|
|
446
289
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
290
|
+
<script>
|
|
291
|
+
let fruits = ["Apple", "Banana", "Cherry"];
|
|
292
|
+
let items = ["First", "Second", "Third"];
|
|
293
|
+
let users = [
|
|
294
|
+
{ id: 1, name: "Alice", role: "Developer", avatar: "👩💻" },
|
|
295
|
+
{ id: 2, name: "Bob", role: "Designer", avatar: "👨🎨" },
|
|
296
|
+
];
|
|
297
|
+
</script>
|
|
453
298
|
```
|
|
454
299
|
|
|
455
|
-
|
|
300
|
+
### Lazy Loading
|
|
456
301
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
Load external JavaScript with components and bind them to the component context:
|
|
302
|
+
Use `<lazy>` to defer rendering until a trigger fires (viewport, idle, delay, interaction, media):
|
|
460
303
|
|
|
461
304
|
```html
|
|
462
|
-
|
|
463
|
-
<
|
|
464
|
-
|
|
465
|
-
<!-- ES modules with bind -->
|
|
466
|
-
<script src="./component-logic.js" type="module" bind></script>
|
|
305
|
+
<lazy margin="100px">
|
|
306
|
+
<heavy-chart></heavy-chart>
|
|
307
|
+
</lazy>
|
|
467
308
|
|
|
468
|
-
|
|
469
|
-
<
|
|
309
|
+
<lazy interaction="click,focus">
|
|
310
|
+
<support-chat></support-chat>
|
|
311
|
+
</lazy>
|
|
470
312
|
```
|
|
471
313
|
|
|
472
|
-
|
|
314
|
+
### Cheat Sheet
|
|
315
|
+
|
|
316
|
+
| Element / Directive | Purpose | Example |
|
|
317
|
+
| ------------------- | ------------------------ | ------------------------------------------------------ |
|
|
318
|
+
| `<if>` | Conditional render | `<if condition="isLoggedIn">Welcome!</if>` |
|
|
319
|
+
| `<else-if>` | Chained condition | `<else-if condition="isGuest">Hello Guest</else-if>` |
|
|
320
|
+
| `<else>` | Fallback | `<else>Please log in</else>` |
|
|
321
|
+
| `<show>` | CSS visibility toggle | `<show condition="isOpen">Menu</show>` |
|
|
322
|
+
| `<for>` | Loop rendering | `<for each="item in items"><li>{item}</li></for>` |
|
|
323
|
+
| `<for>` (indexed) | Loop with index | `<for each="(item, i) in items">…</for>` |
|
|
324
|
+
| `<for key="…">` | List optimization | `<for each="u in users" key="u.id">…</for>` |
|
|
325
|
+
| `<lazy>` | Defer rendering | `<lazy idle><analytics-pixel /></lazy>` |
|
|
326
|
+
| `$bind` | Two-way binding | `<input $bind="email" />` |
|
|
327
|
+
| `$ref` | Element reference | `<input $ref="inputEl" />` |
|
|
473
328
|
|
|
474
|
-
|
|
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
|
-
});
|
|
329
|
+
---
|
|
495
330
|
|
|
496
|
-
|
|
497
|
-
if (this.init) {
|
|
498
|
-
this.init();
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
```
|
|
331
|
+
## 📡 Event Bus
|
|
502
332
|
|
|
503
|
-
|
|
333
|
+
Communicate between components using `$emit` and `$listen`:
|
|
504
334
|
|
|
505
|
-
|
|
506
|
-
- **`$emit` and `$listen`**: Available in external scripts for event communication
|
|
507
|
-
- **Automatic Initialization**: Functions are auto-attached to component context
|
|
335
|
+
### Sender Component
|
|
508
336
|
|
|
509
|
-
|
|
337
|
+
```html
|
|
338
|
+
<button onclick="sendMessage()">Send Message</button>
|
|
510
339
|
|
|
511
|
-
|
|
340
|
+
<script>
|
|
341
|
+
let message = "Hello from sender!";
|
|
512
342
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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
|
-
});
|
|
343
|
+
function sendMessage() {
|
|
344
|
+
$emit("my-event", { text: message, time: new Date().toLocaleTimeString() });
|
|
345
|
+
}
|
|
346
|
+
</script>
|
|
525
347
|
```
|
|
526
348
|
|
|
527
|
-
|
|
349
|
+
### Receiver Component
|
|
528
350
|
|
|
529
351
|
```html
|
|
530
|
-
|
|
531
|
-
<
|
|
532
|
-
|
|
533
|
-
<button data-else onclick="showLogin">Login</button>
|
|
534
|
-
</header>
|
|
352
|
+
<div>
|
|
353
|
+
<p>Received: {receivedMessage}</p>
|
|
354
|
+
</div>
|
|
535
355
|
|
|
536
356
|
<script>
|
|
537
|
-
let
|
|
538
|
-
let username = "";
|
|
357
|
+
let receivedMessage = "Waiting...";
|
|
539
358
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
isLoggedIn = true;
|
|
543
|
-
username = user.username;
|
|
359
|
+
$listen("my-event", (data) => {
|
|
360
|
+
receivedMessage = data.text;
|
|
544
361
|
});
|
|
545
|
-
|
|
546
|
-
const showLogin = () => {
|
|
547
|
-
$emit("show-login-modal");
|
|
548
|
-
};
|
|
549
362
|
</script>
|
|
550
363
|
```
|
|
551
364
|
|
|
552
|
-
|
|
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>
|
|
365
|
+
---
|
|
559
366
|
|
|
560
|
-
|
|
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,
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
// Clear form
|
|
574
|
-
$username = "";
|
|
575
|
-
$password = "";
|
|
576
|
-
};
|
|
577
|
-
</script>
|
|
578
|
-
```
|
|
367
|
+
## 🏷️ Element References
|
|
579
368
|
|
|
580
|
-
|
|
369
|
+
Use `$ref` to get direct DOM access for advanced manipulation:
|
|
581
370
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
- **Promise Support**: `$emit` returns a promise when listeners are async
|
|
371
|
+
```html
|
|
372
|
+
<input type="text" $ref="inputEl" placeholder="Click button to focus" />
|
|
373
|
+
<button onclick="focusInput()">Focus Input</button>
|
|
586
374
|
|
|
587
|
-
|
|
375
|
+
<canvas $ref="canvas" width="200" height="100"></canvas>
|
|
376
|
+
<button onclick="draw()">Draw on Canvas</button>
|
|
588
377
|
|
|
589
|
-
|
|
378
|
+
<script>
|
|
379
|
+
function focusInput() {
|
|
380
|
+
$refs.inputEl.focus();
|
|
381
|
+
$refs.inputEl.select();
|
|
382
|
+
}
|
|
590
383
|
|
|
591
|
-
|
|
384
|
+
function draw() {
|
|
385
|
+
const ctx = $refs.canvas.getContext("2d");
|
|
386
|
+
ctx.fillStyle = "blue";
|
|
387
|
+
ctx.fillRect(10, 10, 100, 50);
|
|
388
|
+
}
|
|
389
|
+
</script>
|
|
390
|
+
```
|
|
592
391
|
|
|
593
|
-
|
|
594
|
-
import { createStore } from "ladrillosjs";
|
|
392
|
+
---
|
|
595
393
|
|
|
596
|
-
|
|
597
|
-
user: null,
|
|
598
|
-
isAuthenticated: false,
|
|
599
|
-
});
|
|
394
|
+
## ⏳ Lazy Loading
|
|
600
395
|
|
|
601
|
-
|
|
602
|
-
this.setState(state);
|
|
603
|
-
});
|
|
604
|
-
```
|
|
396
|
+
Load components only when needed to improve initial page load:
|
|
605
397
|
|
|
606
|
-
|
|
398
|
+
### Lazy Loading Strategies
|
|
607
399
|
|
|
608
400
|
```javascript
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
401
|
+
import {
|
|
402
|
+
registerComponents,
|
|
403
|
+
lazyOnVisible,
|
|
404
|
+
lazyOnIdle,
|
|
405
|
+
lazyOnInteraction,
|
|
406
|
+
lazyOnMedia,
|
|
407
|
+
lazyOnDelay,
|
|
408
|
+
} from "ladrillosjs";
|
|
409
|
+
|
|
410
|
+
await registerComponents([
|
|
411
|
+
// Load when visible in viewport
|
|
412
|
+
{
|
|
413
|
+
name: "lazy-footer",
|
|
414
|
+
path: "./footer.html",
|
|
415
|
+
lazy: lazyOnVisible({ rootMargin: "100px" }),
|
|
416
|
+
},
|
|
417
|
+
|
|
418
|
+
// Load when browser is idle
|
|
419
|
+
{
|
|
420
|
+
name: "analytics-widget",
|
|
421
|
+
path: "./analytics.html",
|
|
422
|
+
lazy: lazyOnIdle(5000), // timeout: 5s max wait
|
|
423
|
+
},
|
|
424
|
+
|
|
425
|
+
// Load on user interaction
|
|
426
|
+
{
|
|
427
|
+
name: "modal-dialog",
|
|
428
|
+
path: "./modal.html",
|
|
429
|
+
lazy: lazyOnInteraction(["click", "focusin"]),
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
// Load based on media query
|
|
433
|
+
{
|
|
434
|
+
name: "mobile-nav",
|
|
435
|
+
path: "./mobile-nav.html",
|
|
436
|
+
lazy: lazyOnMedia("(max-width: 768px)"),
|
|
437
|
+
},
|
|
438
|
+
|
|
439
|
+
// Load after delay
|
|
440
|
+
{
|
|
441
|
+
name: "chat-widget",
|
|
442
|
+
path: "./chat.html",
|
|
443
|
+
lazy: lazyOnDelay(3000), // 3 second delay
|
|
444
|
+
},
|
|
445
|
+
]);
|
|
618
446
|
```
|
|
619
447
|
|
|
620
|
-
|
|
448
|
+
| Strategy | Use Case |
|
|
449
|
+
| ------------------- | -------------------------------------------- |
|
|
450
|
+
| `lazyOnVisible` | Below-fold content, footers, image galleries |
|
|
451
|
+
| `lazyOnIdle` | Non-critical features, analytics |
|
|
452
|
+
| `lazyOnInteraction` | Modals, dropdowns, tooltips |
|
|
453
|
+
| `lazyOnMedia` | Mobile/desktop specific components |
|
|
454
|
+
| `lazyOnDelay` | Chat widgets, notifications |
|
|
621
455
|
|
|
622
|
-
|
|
456
|
+
### Eager Override
|
|
623
457
|
|
|
624
|
-
|
|
625
|
-
// Disable Shadow DOM for a component
|
|
626
|
-
await registerComponent("my-component", "./my-component.html", false);
|
|
458
|
+
Force a lazy component to load immediately by adding the `eager` attribute:
|
|
627
459
|
|
|
628
|
-
|
|
629
|
-
|
|
460
|
+
```html
|
|
461
|
+
<lazy-footer eager></lazy-footer>
|
|
630
462
|
```
|
|
631
463
|
|
|
632
|
-
|
|
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:**
|
|
464
|
+
---
|
|
639
465
|
|
|
640
|
-
|
|
641
|
-
- Using third-party CSS frameworks
|
|
642
|
-
- Debugging with browser dev tools (easier without shadow DOM)
|
|
466
|
+
## 📋 API Reference
|
|
643
467
|
|
|
644
|
-
###
|
|
468
|
+
### `registerComponent`
|
|
645
469
|
|
|
646
|
-
|
|
470
|
+
```javascript
|
|
471
|
+
registerComponent(name, path, useShadowDOM?, lazy?)
|
|
472
|
+
```
|
|
647
473
|
|
|
648
|
-
|
|
474
|
+
| Parameter | Type | Default | Description |
|
|
475
|
+
| -------------- | ----------------------- | -------- | ------------------------------- |
|
|
476
|
+
| `name` | string | required | Tag name (must include hyphen) |
|
|
477
|
+
| `path` | string | required | Path to `.html` component file |
|
|
478
|
+
| `useShadowDOM` | boolean | `true` | Enable Shadow DOM encapsulation |
|
|
479
|
+
| `lazy` | boolean \| LazyStrategy | `false` | Lazy loading configuration |
|
|
649
480
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
-
|
|
481
|
+
```javascript
|
|
482
|
+
// Basic usage
|
|
483
|
+
registerComponent("my-button", "./button.html");
|
|
653
484
|
|
|
654
|
-
|
|
485
|
+
// Without Shadow DOM (for global CSS)
|
|
486
|
+
registerComponent("my-nav", "./nav.html", false);
|
|
655
487
|
|
|
656
|
-
|
|
657
|
-
-
|
|
658
|
-
|
|
488
|
+
// With lazy loading
|
|
489
|
+
registerComponent("my-footer", "./footer.html", true, lazyOnVisible());
|
|
490
|
+
```
|
|
659
491
|
|
|
660
|
-
|
|
492
|
+
### `registerComponents`
|
|
661
493
|
|
|
662
|
-
|
|
663
|
-
- Faster template rendering with cached functions
|
|
664
|
-
- Optimized state updates with change detection
|
|
665
|
-
- Efficient re-rendering with minimal DOM updates
|
|
494
|
+
Register multiple components with parallel fetching:
|
|
666
495
|
|
|
667
|
-
|
|
496
|
+
```javascript
|
|
497
|
+
const result = await registerComponents([
|
|
498
|
+
{ name: "app-header", path: "./header.html" },
|
|
499
|
+
{ name: "app-footer", path: "./footer.html", lazy: lazyOnVisible() },
|
|
500
|
+
{ name: "user-card", path: "./user-card.html", useShadowDOM: false },
|
|
501
|
+
]);
|
|
668
502
|
|
|
669
|
-
|
|
503
|
+
// Returns: { success: [...], failed: [...], skipped: [...] }
|
|
504
|
+
```
|
|
670
505
|
|
|
671
|
-
|
|
672
|
-
| ------------------- | ------------------------------------------------------------------------- |
|
|
673
|
-
| `setState(partial)` | Update component state and trigger re-render (merges with existing state) |
|
|
506
|
+
### `$use`
|
|
674
507
|
|
|
675
|
-
|
|
508
|
+
Infer the component tag name from the file path:
|
|
676
509
|
|
|
677
|
-
|
|
510
|
+
```javascript
|
|
511
|
+
await $use("./components/user-card.html"); // Registers as <user-card>
|
|
512
|
+
```
|
|
678
513
|
|
|
679
|
-
|
|
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 |
|
|
514
|
+
### `loadLazyComponent`
|
|
687
515
|
|
|
688
|
-
|
|
516
|
+
Force a lazy component to load immediately from JavaScript:
|
|
689
517
|
|
|
690
|
-
|
|
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) |
|
|
518
|
+
```javascript
|
|
519
|
+
import { loadLazyComponent } from "ladrillosjs";
|
|
697
520
|
|
|
698
|
-
|
|
521
|
+
await loadLazyComponent("my-lazy-footer");
|
|
522
|
+
```
|
|
699
523
|
|
|
700
|
-
|
|
701
|
-
| ---------------------------------------------- | ------------------------------------------------------ |
|
|
702
|
-
| `registerComponent(name, path, useShadowDOM?)` | Register a single component (returns Promise) |
|
|
703
|
-
| `registerComponents(components)` | **Coming soon** - Register multiple components at once |
|
|
524
|
+
### `configure`
|
|
704
525
|
|
|
705
|
-
|
|
526
|
+
Configure framework-level options (optional):
|
|
706
527
|
|
|
707
|
-
|
|
528
|
+
```javascript
|
|
529
|
+
import { configure } from "ladrillosjs";
|
|
708
530
|
|
|
709
|
-
|
|
531
|
+
configure({
|
|
532
|
+
cacheSize: 50, // Component LRU cache size (default: 25)
|
|
533
|
+
onError: (err) => telemetry.capture(err), // Custom error handler
|
|
534
|
+
});
|
|
535
|
+
```
|
|
710
536
|
|
|
711
|
-
|
|
712
|
-
<!-- parent.html -->
|
|
713
|
-
<div>
|
|
714
|
-
<h2>Parent Component</h2>
|
|
715
|
-
<p>Messages received: {messageCount}</p>
|
|
716
|
-
<child-component></child-component>
|
|
717
|
-
</div>
|
|
537
|
+
### Event Bus
|
|
718
538
|
|
|
719
|
-
|
|
720
|
-
|
|
539
|
+
| Function | Description |
|
|
540
|
+
| -------------------------- | ----------------------------------- |
|
|
541
|
+
| `$emit(event, data)` | Broadcast an event to all listeners |
|
|
542
|
+
| `$listen(event, callback)` | Subscribe to an event |
|
|
721
543
|
|
|
722
|
-
|
|
723
|
-
$listen("child-message", (data) => {
|
|
724
|
-
console.log("Received from child:", data);
|
|
725
|
-
messageCount++;
|
|
726
|
-
});
|
|
727
|
-
</script>
|
|
728
|
-
```
|
|
544
|
+
---
|
|
729
545
|
|
|
730
|
-
|
|
731
|
-
<!-- child.html -->
|
|
732
|
-
<div>
|
|
733
|
-
<h3>Child Component</h3>
|
|
734
|
-
<button onclick="sendMessage">Send Message to Parent</button>
|
|
735
|
-
</div>
|
|
546
|
+
## 🛠️ Using with Vite
|
|
736
547
|
|
|
737
|
-
|
|
738
|
-
let count = 0;
|
|
548
|
+
LadrillosJS works seamlessly with Vite for production builds:
|
|
739
549
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
// Emit event that parent (or any component) can listen to
|
|
743
|
-
$emit("child-message", {
|
|
744
|
-
message: `Hello from child! (${count})`,
|
|
745
|
-
timestamp: Date.now(),
|
|
746
|
-
});
|
|
747
|
-
};
|
|
748
|
-
</script>
|
|
550
|
+
```bash
|
|
551
|
+
npm install ladrillosjs vite
|
|
749
552
|
```
|
|
750
553
|
|
|
751
|
-
### Dynamic Component Creation
|
|
752
|
-
|
|
753
554
|
```javascript
|
|
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
|
-
};
|
|
762
|
-
|
|
763
|
-
// Fetch and create multiple components
|
|
764
|
-
fetch("/api/users")
|
|
765
|
-
.then((res) => res.json())
|
|
766
|
-
.then((users) => users.forEach(createCard));
|
|
767
|
-
```
|
|
555
|
+
// vite.config.js
|
|
556
|
+
import { defineConfig } from "vite";
|
|
768
557
|
|
|
769
|
-
|
|
558
|
+
export default defineConfig({
|
|
559
|
+
// LadrillosJS components work out of the box!
|
|
560
|
+
});
|
|
561
|
+
```
|
|
770
562
|
|
|
771
|
-
|
|
563
|
+
```javascript
|
|
564
|
+
// main.js
|
|
565
|
+
import { registerComponent } from "ladrillosjs";
|
|
772
566
|
|
|
773
|
-
|
|
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>
|
|
567
|
+
registerComponent("my-counter", "./components/counter.html", false);
|
|
790
568
|
```
|
|
791
569
|
|
|
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>
|
|
570
|
+
See the [samples/](samples/) directory for complete examples:
|
|
799
571
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
</script>
|
|
804
|
-
```
|
|
572
|
+
- `samples/vite-sample/` — Basic Vite setup
|
|
573
|
+
- `samples/vite-basic-site/` — Multi-component site
|
|
574
|
+
- `samples/ladrillos-demo/` — Full feature showcase
|
|
805
575
|
|
|
806
|
-
|
|
576
|
+
---
|
|
807
577
|
|
|
808
|
-
|
|
578
|
+
## 📚 Examples
|
|
809
579
|
|
|
810
|
-
|
|
580
|
+
### Todo List
|
|
811
581
|
|
|
812
|
-
|
|
813
|
-
- **After:** Use global event bus with `$emit` and `$listen`
|
|
582
|
+
A complete CRUD example combining all directives:
|
|
814
583
|
|
|
815
|
-
|
|
584
|
+
```html
|
|
585
|
+
<div class="todo-app">
|
|
586
|
+
<form onsubmit="addTodo(event)">
|
|
587
|
+
<input type="text" $bind="newTodo" placeholder="What needs to be done?" />
|
|
588
|
+
<button type="submit">Add</button>
|
|
589
|
+
</form>
|
|
590
|
+
|
|
591
|
+
<ul>
|
|
592
|
+
<for each="todo in todos" key="todo.id">
|
|
593
|
+
<li>
|
|
594
|
+
<input type="checkbox" onclick="toggleTodo({todo.id})" />
|
|
595
|
+
<span class="{todo.completed ? 'done' : ''}">{todo.text}</span>
|
|
596
|
+
<button onclick="removeTodo({todo.id})">🗑️</button>
|
|
597
|
+
</li>
|
|
598
|
+
</for>
|
|
599
|
+
</ul>
|
|
600
|
+
|
|
601
|
+
<if condition="todos.length === 0"><p>No todos yet!</p></if>
|
|
602
|
+
</div>
|
|
816
603
|
|
|
817
|
-
|
|
818
|
-
|
|
604
|
+
<script>
|
|
605
|
+
let todos = [
|
|
606
|
+
{ id: 1, text: "Learn LadrillosJS", completed: false },
|
|
607
|
+
{ id: 2, text: "Build something awesome", completed: false },
|
|
608
|
+
];
|
|
609
|
+
let newTodo = "";
|
|
610
|
+
let nextId = 3;
|
|
611
|
+
|
|
612
|
+
function addTodo(event) {
|
|
613
|
+
event.preventDefault();
|
|
614
|
+
if (newTodo.trim()) {
|
|
615
|
+
todos = [...todos, { id: nextId++, text: newTodo, completed: false }];
|
|
616
|
+
newTodo = "";
|
|
617
|
+
}
|
|
618
|
+
}
|
|
819
619
|
|
|
820
|
-
|
|
620
|
+
function toggleTodo(id) {
|
|
621
|
+
todos = todos.map((t) =>
|
|
622
|
+
t.id === id ? { ...t, completed: !t.completed } : t,
|
|
623
|
+
);
|
|
624
|
+
}
|
|
821
625
|
|
|
822
|
-
|
|
823
|
-
|
|
626
|
+
function removeTodo(id) {
|
|
627
|
+
todos = todos.filter((t) => t.id !== id);
|
|
628
|
+
}
|
|
629
|
+
</script>
|
|
630
|
+
```
|
|
824
631
|
|
|
825
|
-
|
|
826
|
-
- **Before:** No built-in support (manual implementation)
|
|
827
|
-
- **After:** Use `$bind` attribute for automatic two-way binding
|
|
632
|
+
> 💡 Check the [samples/](samples/) folder for more examples including lazy loading, event bus communication, and real-world patterns.
|
|
828
633
|
|
|
829
|
-
|
|
634
|
+
---
|
|
830
635
|
|
|
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`
|
|
636
|
+
## 🤝 Contributing
|
|
836
637
|
|
|
837
|
-
|
|
638
|
+
Contributions are welcome! Here's how you can help:
|
|
838
639
|
|
|
839
|
-
|
|
640
|
+
1. **Fork** the repository
|
|
641
|
+
2. **Create** a feature branch: `git checkout -b feature/amazing-feature`
|
|
642
|
+
3. **Commit** your changes: `git commit -m 'Add amazing feature'`
|
|
643
|
+
4. **Push** to the branch: `git push origin feature/amazing-feature`
|
|
644
|
+
5. **Open** a Pull Request
|
|
840
645
|
|
|
841
|
-
### Development
|
|
646
|
+
### Development Setup
|
|
842
647
|
|
|
843
648
|
```bash
|
|
844
|
-
|
|
649
|
+
git clone https://github.com/drubiodev/LadrillosJS.git
|
|
650
|
+
cd LadrillosJS
|
|
845
651
|
npm install
|
|
846
|
-
|
|
847
|
-
|
|
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
|
|
652
|
+
npm run dev # Watch mode for development
|
|
653
|
+
npm run build # Build for production
|
|
861
654
|
```
|
|
862
655
|
|
|
863
|
-
|
|
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
|
-
```
|
|
656
|
+
---
|
|
890
657
|
|
|
891
|
-
## License
|
|
658
|
+
## 📄 License
|
|
892
659
|
|
|
893
|
-
MIT License
|
|
660
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
894
661
|
|
|
895
662
|
---
|
|
896
663
|
|
|
897
|
-
|
|
664
|
+
<p align="center">
|
|
665
|
+
<strong>Built with ❤️ by <a href="https://github.com/drubiodev">Daniel Rubio</a></strong>
|
|
666
|
+
</p>
|
|
898
667
|
|
|
899
|
-
|
|
668
|
+
<p align="center">
|
|
669
|
+
<a href="https://github.com/drubiodev/LadrillosJS">GitHub</a> •
|
|
670
|
+
<a href="https://www.npmjs.com/package/ladrillosjs">NPM</a> •
|
|
671
|
+
<a href="https://github.com/drubiodev/LadrillosJS/issues">Issues</a>
|
|
672
|
+
</p>
|