ladrillosjs 1.0.0-rc.8 → 1.0.0

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 CHANGED
@@ -6,168 +6,466 @@ A lightweight, zero-dependency web component framework for building modular web
6
6
 
7
7
  "I designed this framework to empower developers with the ability to componentize their code efficiently and effectively, without the need for a full-scale framework. By focusing on simplicity and leveraging core web fundamentals, my goal was to create a lightweight and accessible solution that enhances development while staying true to the basics."
8
8
 
9
- ## Getting Starting with samples
9
+ ## Table of Contents
10
+
11
+ - [Features](#features)
12
+ - [Getting Started](#getting-started)
13
+ - [Installation](#installation)
14
+ - [Your First Component](#your-first-component)
15
+ - [Core Concepts](#core-concepts)
16
+ - [Component Registration](#component-registration)
17
+ - [State Management](#state-management)
18
+ - [Event Handling](#event-handling)
19
+ - [Data Binding](#data-binding)
20
+ - [Conditional Rendering](#conditional-rendering)
21
+ - [Slots](#slots)
22
+ - [Advanced Features](#advanced-features)
23
+ - [External Scripts](#external-scripts)
24
+ - [Global State Stores](#global-state-stores)
25
+ - [Shadow DOM](#shadow-dom)
26
+ - [API Reference](#api-reference)
27
+ - [Examples](#examples)
28
+ - [Contributing](#contributing)
29
+ - [License](#license)
30
+
31
+ ## Features
32
+
33
+ - 🚀 **Zero Dependencies** - Pure JavaScript, no build tools required
34
+ - 📦 **Single-File Components** - HTML, CSS, and JavaScript in one file
35
+ - ⚡ **Reactive State** - Automatic re-rendering on state changes
36
+ - 🎯 **Event System** - Built-in event emission and listening
37
+ - 🔄 **Two-Way Data Binding** - For form inputs and contenteditable elements
38
+ - 🎨 **Scoped Styles** - Component styles with optional Shadow DOM
39
+ - 🏪 **State Management** - Simple store implementation for shared state
40
+ - 🔌 **Slots Support** - Content projection with named and default slots
41
+ - 📝 **TypeScript Support** - Includes type definitions
42
+
43
+ ## Getting Started
44
+
45
+ The repository includes several example applications that demonstrate various features:
46
+
47
+ - **[Todo App](samples/apps/todo)** - Classic todo list with component composition
48
+ - **[Notes App](samples/apps/notes)** - Multi-component app with global state management
49
+ - **[Markdown Editor](samples/apps/markdown)** - Real-time markdown preview
50
+ - **[API Example](samples/apps/api)** - Fetching and displaying external data
51
+ - **[Business Card](samples/apps/biz)** - Editable form with two-way data binding
52
+ - **[Button Game](samples/apps/button-game)** - Interactive game with component events
53
+ - **[Slideshow](samples/apps/slideshow)** - Multi-slide presentation system
54
+ - **[Docs](samples/apps/docs)** - Documentation viewer with syntax highlighting
55
+
56
+ To run the examples:
10
57
 
11
- 1. `npm install`
12
- 2. `npm run dev`
58
+ ```bash
59
+ # Clone the repository
60
+ git clone https://github.com/drubiodev/LadrillosJS.git
61
+ cd LadrillosJS
62
+
63
+ # Install dependencies (for dev server only)
64
+ npm install
13
65
 
14
- ## Usage
66
+ # Start the development server
67
+ npm run dev
68
+ ```
15
69
 
16
- ### Install & import
70
+ ## Installation
71
+
72
+ ### NPM
17
73
 
18
74
  ```bash
19
75
  npm install ladrillosjs
20
76
  ```
21
77
 
22
- This will spin up Vite with the `samples` folder as the web root.
23
-
24
- ### cdn
78
+ ### CDN
25
79
 
26
- ```js
80
+ ```html
27
81
  <script defer src="https://cdn.jsdelivr.net/npm/ladrillosjs"></script>
28
82
  ```
29
83
 
30
- ## First Component
84
+ ## Your First Component
31
85
 
32
- A component in LadrillosJS is a reusable custom HTML element that bundles its own template, logic (bindings) and styles into a single file.
86
+ A component in LadrillosJS is a reusable custom HTML element that bundles its own template, logic, and styles into a single file.
33
87
 
34
- To create your first component, follow these steps:
88
+ ### 1. Create a Component File
35
89
 
36
- 1. Create an HTML file that defines your component’s template, script bindings and CSS. For example:
90
+ Create `hello-world.html`:
37
91
 
38
- ```html
39
- <!-- hello.html -->
92
+ ```html
93
+ <!-- hello-world.html -->
94
+ <div class="greeting">
95
+ <h1>{title}</h1>
96
+ <p>Hello, {name}!</p>
97
+ <button onclick="greet">Click me ({count})</button>
98
+ </div>
40
99
 
41
- <p>Hello, LadrillosJS!</p>
42
- <button onclick="increment">Clicked {count} times</button>
100
+ <script>
101
+ // Component state
102
+ let title = "Welcome to LadrillosJS";
103
+ let name = "World";
104
+ let count = 0;
105
+
106
+ // Event handler
107
+ const greet = () => {
108
+ count++;
109
+ name = prompt("What's your name?") || "World";
110
+ };
111
+ </script>
43
112
 
44
- <script>
45
- // declare a bound variable
46
- let count = 0;
113
+ <style>
114
+ .greeting {
115
+ text-align: center;
116
+ padding: 2rem;
117
+ background: #f0f0f0;
118
+ border-radius: 8px;
119
+ }
120
+
121
+ button {
122
+ padding: 0.5rem 1rem;
123
+ font-size: 1rem;
124
+ cursor: pointer;
125
+ }
126
+ </style>
127
+ ```
47
128
 
48
- // declare a handler for the button click
49
- const increment = () => {
50
- count++;
51
- // update component state and re-render
52
- this.setState({ count });
53
- };
54
- </script>
129
+ ### 2. Register and Use the Component
55
130
 
56
- <style>
57
- /* component‐scoped CSS */
58
- p {
59
- font-size: 1.25rem;
60
- color: #1a73e8;
61
- }
62
- button {
63
- margin-top: 0.5rem;
64
- padding: 0.5rem 1rem;
65
- }
66
- </style>
67
- ```
131
+ ```html
132
+ <!DOCTYPE html>
133
+ <html>
134
+ <head>
135
+ <title>My App</title>
136
+ </head>
137
+ <body>
138
+ <!-- Use your component -->
139
+ <hello-world></hello-world>
140
+
141
+ <!-- Register component -->
142
+ <script type="module">
143
+ import { registerComponent } from "ladrillosjs";
144
+ registerComponent("hello-world", "./hello-world.html");
145
+ </script>
146
+ </body>
147
+ </html>
148
+ ```
68
149
 
69
- 2. Import and register your component in the page where you want to use it:
150
+ ## Core Concepts
70
151
 
71
- ```html
72
- <!-- import -->
73
- <script type="module">
74
- import { registerComponent } from "ladrillosjs";
152
+ ### Component Registration
75
153
 
76
- registerComponent("hello-world", "hello-world.html");
77
- </script>
154
+ Register single or multiple components:
78
155
 
79
- <!-- CDN -->
80
- <script type="module">
81
- ladrillosjs.registerComponent("hello-world", "hello-world.html");
82
- </script>
83
- ```
156
+ ```javascript
157
+ // Single component
158
+ import { registerComponent } from "ladrillosjs";
159
+ registerComponent("my-component", "./my-component.html");
84
160
 
85
- ```html
86
- <!-- then use it in markup -->
87
- <hello-world></hello-world>
88
- ```
161
+ // Multiple components with concurrency control
162
+ import { registerComponents } from "ladrillosjs";
163
+ await registerComponents(
164
+ [
165
+ { name: "app-header", path: "./components/header.html" },
166
+ { name: "app-footer", path: "./components/footer.html" },
167
+ { name: "user-card", path: "./components/user-card.html" },
168
+ ],
169
+ 10
170
+ ); // Max 10 parallel fetches (default: 5)
89
171
 
90
- Under the hood, LadrillosJS will fetch, parse and cache hello.html, then define a `<hello-world>`.
172
+ // Using CDN
173
+ ladrillosjs.registerComponent("my-component", "./my-component.html");
174
+ ```
91
175
 
92
- - Template placeholders `{…}` automatically bind to this.state.
93
- - Top‐level `let/const/function` in your `<script>` block are hoisted and initialized into `this.state` if they appear in a template or event.
94
- - Event attributes like `onclick="increment"` register listeners under the hood.
176
+ ### State Management
95
177
 
96
- ## Binding Variables & Events
178
+ Components have reactive state that automatically triggers re-renders:
97
179
 
98
- - To bind data into your markup, wrap state keys in `{}`
180
+ ```html
181
+ <div>
182
+ <h2>User: {user.name}</h2>
183
+ <p>Score: {score}</p>
184
+ <button onclick="updateScore">Add Point</button>
185
+ </div>
186
+
187
+ <script>
188
+ const date = new Date(Date.now());
189
+ const formattedDate = date.toLocaleDateString("en-US"); // Format: MM/DD/YYYY
190
+ // Initial state
191
+ let score = 0;
192
+ let user = {
193
+ name: "Player 1",
194
+ };
195
+
196
+ const updateScore = () => {
197
+ // Update state and trigger re-render
198
+ score++;
199
+ };
200
+ </script>
201
+ ```
202
+
203
+ ### Event Handling
204
+
205
+ Multiple ways to handle events:
99
206
 
100
207
  ```html
101
- <span>{user.name}</span>
208
+ <!-- Method reference -->
209
+ <button onclick="handleClick">Click me: {count}</button>
210
+
211
+ <!-- Function with arguments -->
212
+ <button onclick="addItem('Hello', 123)">Add Item</button>
213
+
214
+ <!-- Inline arrow function -->
215
+ <button onclick="(e) => console.log(e.target)">Log Target</button>
216
+
217
+ <script>
218
+ const count = 0;
219
+ this.setState({ items: [] });
220
+
221
+ const handleClick = (event) => {
222
+ console.log("Clicked!", event);
223
+ count++;
224
+ };
225
+
226
+ const addItem = (name, value) => {
227
+ this.setState({ items: [...this.state.items, { name, value }] });
228
+ };
229
+ </script>
230
+ <div>
231
+ <h1>Shopping Cart ({items.length} items)</h1>
232
+
233
+ <div data-if="items.length === 0">
234
+ <p>Your cart is empty</p>
235
+ </div>
236
+
237
+ <div data-else-if="items.length < 3">
238
+ <p>You have a few items</p>
239
+ </div>
240
+
241
+ <div data-else>
242
+ <p>You have many items!</p>
243
+ </div>
244
+
245
+ <button data-if="!isLoggedIn" onclick="login">Login</button>
246
+ <button data-else onclick="logout">Logout</button>
247
+ </div>
248
+ <script>
249
+ const items = ["apple", "banana", "orange"];
250
+ const isLoggedIn = false;
251
+
252
+ function login() {
253
+ isLoggedIn = true;
254
+ }
255
+
256
+ function logout() {
257
+ isLoggedIn = false;
258
+ }
259
+ </script>
102
260
  ```
103
261
 
104
- - To bind an event, prefix an attribute with `on`:
262
+ ### Slots
263
+
264
+ Content projection using slots:
105
265
 
106
266
  ```html
107
- <button onclick="doSomething">Do it</button>
267
+ <!-- card.html -->
268
+ <div class="card">
269
+ <div class="card-header">
270
+ <slot name="header">Default Header</slot>
271
+ </div>
272
+ <div class="card-body">
273
+ <slot></slot>
274
+ <!-- Default slot -->
275
+ </div>
276
+ <div class="card-footer">
277
+ <slot name="footer"></slot>
278
+ </div>
279
+ </div>
280
+
281
+ <!-- Usage -->
282
+ <my-card>
283
+ <h2 slot="header">User Profile</h2>
284
+ <p>This goes in the default slot</p>
285
+ <button slot="footer">Save</button>
286
+ </my-card>
108
287
  ```
109
288
 
110
- ## Initializing from Attributes
289
+ ## Advanced Features
111
290
 
112
- Component attributes sync to `this.state` automatically and can be reference by using `this.state["some-prop"]` in your template or code.
291
+ ### External Scripts
113
292
 
114
- ## Internal vs. External Scripts
293
+ Load external JavaScript with components:
115
294
 
116
- - Inline scripts (no src) are parsed, top‑level bindings registered, then executed in component context.
117
- - External scripts with `bind` attribute are fetched, and processed exactly like inline scripts.
295
+ ```html
296
+ <!-- With 'bind' attribute for component context -->
297
+ <script src="./helpers.js" bind></script>
298
+
299
+ <!-- ES modules with bind -->
300
+ <script src="./component-logic.js" type="module" bind></script>
118
301
 
119
- ```js
120
- <script src="path_to_file.js" bind></script>
302
+ <!-- Regular external script -->
303
+ <script src="https://cdn.example.com/library.js"></script>
121
304
  ```
122
305
 
123
- - External scripts without bind are injected via a `<script>` tag (for non‑module, or third‑party scripts).
124
- - `type="module"` scripts (inline or external) are added to the shadow/root as real modules.
306
+ For modules with `bind`, export a default function:
307
+
308
+ ```javascript
309
+ // component-logic.js
310
+ export default function () {
311
+ // 'this' refers to the component instance
312
+ this.formatDate = (date) => {
313
+ return new Intl.DateTimeFormat("en-US").format(date);
314
+ };
315
+
316
+ this.init = () => {
317
+ console.log("Component initialized");
318
+ };
125
319
 
126
- ## Managing State
320
+ // Called automatically if defined
321
+ this.init();
322
+ }
323
+ ```
127
324
 
128
- To manage state in LadrillosJS, you can use the `this.setState()` method to update the component's state. The `this.state` object holds the component's state variables, and you can modify them as needed.
325
+ ### Global State Stores
129
326
 
130
- When you call `this.setState({ key: value, … })`, LadrillosJS will automatically re-render the component with the updated state.
327
+ Share state across components:
131
328
 
132
- ## Emitting
329
+ ```javascript
330
+ // stores/userStore.js
331
+ import { createStore } from "ladrillosjs";
133
332
 
134
- To emit events from your component, you can use the `this.emit()` method. This allows you to trigger custom events that can be listened to by parent components or other parts of your application. If you don't pass a second argument, the event will be emitted with the component's state as the data.
333
+ export const userStore = createStore({
334
+ user: null,
335
+ isAuthenticated: false,
336
+ });
135
337
 
136
- ```js
137
- this.emit("event-name", { some: "data" });
338
+ export function login(userData) {
339
+ userStore.setState({
340
+ user: userData,
341
+ isAuthenticated: true,
342
+ });
343
+ }
344
+
345
+ export function logout() {
346
+ userStore.setState({
347
+ user: null,
348
+ isAuthenticated: false,
349
+ });
350
+ }
138
351
  ```
139
352
 
140
- To listen to emitted events, you can use the `this.listen()` method on the component instance:
353
+ ```html
354
+ <!-- header.html -->
355
+ <header>
356
+ <span data-if="isAuthenticated">Welcome, {user.name}!</span>
357
+ <button data-else onclick="showLogin">Login</button>
358
+ </header>
141
359
 
142
- ```js
143
- this.listen("event-name", (payload) => {
144
- console.log(payload); // Access the emitted data
145
- });
360
+ <script type="module" src="./header-logic.js" bind></script>
361
+ ```
362
+
363
+ ```javascript
364
+ // header-logic.js
365
+ import { userStore } from "../stores/userStore.js";
366
+
367
+ export default function () {
368
+ // Subscribe to store changes
369
+ userStore.subscribe((state) => {
370
+ this.setState({
371
+ user: state.user,
372
+ isAuthenticated: state.isAuthenticated,
373
+ });
374
+ });
375
+
376
+ this.showLogin = () => {
377
+ this.emit("show-login");
378
+ };
379
+ }
146
380
  ```
147
381
 
148
- ## Two‑Way Binding
382
+ ### Shadow DOM
149
383
 
150
- LadrillosJS provides built‑in two‑way data binding for form controls and contenteditable elements via the `data-bind` attribute.
151
- When a component is initialized, any element with `data-bind="key"` will:
384
+ Components use Shadow DOM by default for style encapsulation. To disable:
152
385
 
153
- - Push its initial value/content into `this.state.key`.
154
- - Listen for user input (e.g. `input` or `change` events) and update `this.state.key` automatically.
155
- - Re-render whenever you call `this.setState({ key: newValue })`, updating the element’s value or innerText.
386
+ ```javascript
387
+ // Disable Shadow DOM for a component
388
+ registerComponent("my-component", "./my-component.html", false);
156
389
 
157
- Usage example:
390
+ // Multiple components
391
+ registerComponents([
392
+ { name: "global-styles", path: "./global.html", useShadowDOM: false },
393
+ { name: "isolated-widget", path: "./widget.html", useShadowDOM: true },
394
+ ]);
395
+ ```
396
+
397
+ ## API Reference
398
+
399
+ ### Component Methods
400
+
401
+ | Method | Description |
402
+ | --------------------------------- | -------------------------------------------- |
403
+ | `this.setState(partial)` | Update component state and trigger re-render |
404
+ | `this.emit(eventName, data?)` | Dispatch a custom event |
405
+ | `this.listen(eventName, handler)` | Listen for custom events |
406
+ | `this.querySelector(selector)` | Query element within component |
407
+ | `this.querySelectorAll(selector)` | Query all elements within component |
408
+
409
+ ### Store Methods
410
+
411
+ | Method | Description |
412
+ | --------------------------- | -------------------------- |
413
+ | `createStore(initialState)` | Create a new store |
414
+ | `store.getState()` | Get current store state |
415
+ | `store.setState(partial)` | Update store state |
416
+ | `store.subscribe(callback)` | Subscribe to state changes |
417
+ | `store.reset()` | Reset to initial state |
418
+
419
+ ## Examples
420
+
421
+ ### Component Communication
158
422
 
159
423
  ```html
160
- <!-- In your component HTML -->
161
- <input type="text" placeholder="Username" data-bind="username" />
162
- <div contenteditable="true" placeholder="About you…" data-bind="bio"></div>
163
- <button onclick="submit">Submit</button>
424
+ <!-- parent.html -->
425
+ <div>
426
+ <child-component data-message="Hello"></child-component>
427
+ </div>
428
+
429
+ <script>
430
+ this.listen("child-event", (data) => {
431
+ console.log("Received from child:", data);
432
+ });
433
+ </script>
434
+
435
+ <!-- child.html -->
436
+ <button onclick="sendMessage">{data-message}</button>
164
437
 
165
438
  <script>
166
- const submit = () => {
167
- console.log("Username:", this.state.username);
168
- console.log("Bio:", this.state.bio);
169
- // reset state
170
- this.setState({ username: "", bio: "" });
439
+ const sendMessage = () => {
440
+ this.emit("child-event", {
441
+ message: this.state["data-message"],
442
+ timestamp: Date.now(),
443
+ });
171
444
  };
172
445
  </script>
173
446
  ```
447
+
448
+ ### Dynamic Component Creation
449
+
450
+ ```javascript
451
+ // Create components programmatically
452
+ const createCard = (userData) => {
453
+ const card = document.createElement("user-card");
454
+ card.setAttribute("user-id", userData.id);
455
+ card.setAttribute("name", userData.name);
456
+ document.querySelector("#user-list").appendChild(card);
457
+ };
458
+
459
+ // Fetch and create
460
+ fetch("/api/users")
461
+ .then((res) => res.json())
462
+ .then((users) => users.forEach(createCard));
463
+ ```
464
+
465
+ ## Contributing
466
+
467
+ Contributions are welcome! Please feel free to submit a Pull Request.
468
+
469
+ ## License
470
+
471
+ MIT License - see [LICENSE](LICENSE) file for details.
@@ -0,0 +1,31 @@
1
+ export const ladrillos: Ladrillos;
2
+ export type LadrillosComponent = import("../types/LadrilloTypes").LadrillosComponent;
3
+ /** @typedef {import('../types/LadrilloTypes').LadrillosComponent} LadrillosComponent */
4
+ declare class Ladrillos {
5
+ static "__#2@#safeFetch"(url: any): Promise<string>;
6
+ /** @type {Record<string, LadrillosComponent>} */
7
+ components: Record<string, LadrillosComponent>;
8
+ /**
9
+ * Registers a web‐component by fetching its HTML, scripts, and styles.
10
+ * @param {string} name
11
+ * @param {string} path
12
+ * @param {boolean} [useShadowDOM=true]
13
+ */
14
+ registerComponent(name: string, path: string, useShadowDOM?: boolean): Promise<void>;
15
+ /**
16
+ * Registers multiple components with optional concurrency throttling.
17
+ * @param {{name: string, path: string,useShadowDOM:boolean}[]} components
18
+ * @param {number} [concurrency=5] max simultaneous registrations
19
+ */
20
+ registerComponents(components: {
21
+ name: string;
22
+ path: string;
23
+ useShadowDOM: boolean;
24
+ }[], concurrency?: number): Promise<any>;
25
+ /** @private */
26
+ private _runWithConcurrency;
27
+ /** @private */
28
+ private _defineWebComponent;
29
+ #private;
30
+ }
31
+ export {};
@@ -0,0 +1,6 @@
1
+ export function createStore(initialState?: {}): {
2
+ getState(): {};
3
+ setState(partial: any): void;
4
+ subscribe(fn: any): () => boolean;
5
+ reset(): void;
6
+ };
@@ -0,0 +1,2 @@
1
+ export function defineWebComponent(component: LadrillosComponent, useShadowDOM: boolean): void;
2
+ export type LadrillosComponent = import("../types/LadrilloTypes").LadrillosComponent;