ladrillosjs 1.0.0-rc.9 → 1.0.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 CHANGED
@@ -6,194 +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
65
+
66
+ # Start the development server
67
+ npm run dev
68
+ ```
13
69
 
14
- ## Usage
70
+ ## Installation
15
71
 
16
- ### Install & import
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
31
-
32
- A component in LadrillosJS is a reusable custom HTML element that bundles its own template, logic (bindings) and styles into a single file.
33
-
34
- To create your first component, follow these steps:
35
-
36
- 1. Create an HTML file that defines your component’s template, script bindings and CSS. For example:
37
-
38
- ```html
39
- <!-- hello.html -->
40
-
41
- <p>Hello, LadrillosJS!</p>
42
- <button onclick="increment">Clicked {count} times</button>
43
-
44
- <script>
45
- // declare a bound variable
46
- let count = 0;
47
-
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>
55
-
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
- ```
68
-
69
- 2. Import and register your component in the page where you want to use it:
70
-
71
- ```html
72
- <!-- import -->
73
- <script type="module">
74
- import { registerComponent } from "ladrillosjs";
75
-
76
- registerComponent("hello-world", "hello-world.html");
77
- </script>
78
-
79
- <!-- import multiple components -->
80
- <script type="module">
81
- import { registerComponents } from "ladrillosjs";
82
-
83
- await registerComponents(
84
- [
85
- { name: "my-widget", path: "/components/widget.html" },
86
- { name: "my-card", path: "/components/card.html" },
87
- // …
88
- ],
89
- 10 // sets 10 parallel fetches - defaults to 5
90
- );
91
- </script>
92
-
93
- <!-- CDN -->
94
- <script type="module">
95
- ladrillosjs.registerComponent("hello-world", "hello-world.html");
96
- </script>
97
-
98
- <!-- CDN multiple components -->
99
- <script type="module">
100
- await ladrillos.registerComponents(
101
- [
102
- { name: "my-widget", path: "/components/widget.html" },
103
- { name: "my-card", path: "/components/card.html" },
104
- // …
105
- ],
106
- 10 // sets 10 parallel fetches - defaults to 5
107
- );
108
- </script>
109
- ```
110
-
111
- ```html
112
- <!-- then use it in markup -->
113
- <hello-world></hello-world>
114
- ```
115
-
116
- Under the hood, LadrillosJS will fetch, parse and cache hello.html, then define a `<hello-world>`.
117
-
118
- - Template placeholders `{…}` automatically bind to this.state.
119
- - Top‐level `let/const/function` in your `<script>` block are hoisted and initialized into `this.state` if they appear in a template or event.
120
- - Event attributes like `onclick="increment"` register listeners under the hood.
121
-
122
- ## Binding Variables & Events
123
-
124
- - To bind data into your markup, wrap state keys in `{}`
84
+ ## Your First Component
85
+
86
+ A component in LadrillosJS is a reusable custom HTML element that bundles its own template, logic, and styles into a single file.
87
+
88
+ ### 1. Create a Component File
89
+
90
+ Create `hello-world.html`:
91
+
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>
99
+
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>
112
+
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
+ ```
128
+
129
+ ### 2. Register and Use the Component
125
130
 
126
131
  ```html
127
- <span>{user.name}</span>
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
+ ```
149
+
150
+ ## Core Concepts
151
+
152
+ ### Component Registration
153
+
154
+ Register single or multiple components:
155
+
156
+ ```javascript
157
+ // Single component
158
+ import { registerComponent } from "ladrillosjs";
159
+ registerComponent("my-component", "./my-component.html");
160
+
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)
171
+
172
+ // Using CDN
173
+ ladrillosjs.registerComponent("my-component", "./my-component.html");
128
174
  ```
129
175
 
130
- - To bind an event, prefix an attribute with `on`:
176
+ ### State Management
177
+
178
+ Components have reactive state that automatically triggers re-renders:
131
179
 
132
180
  ```html
133
- <button onclick="doSomething">Do it</button>
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>
134
201
  ```
135
202
 
136
- ## Initializing from Attributes
203
+ ### Event Handling
204
+
205
+ Multiple ways to handle events:
206
+
207
+ ```html
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>
137
240
 
138
- Component attributes sync to `this.state` automatically and can be reference by using `this.state["some-prop"]` in your template or code.
241
+ <div data-else>
242
+ <p>You have many items!</p>
243
+ </div>
139
244
 
140
- ## Internal vs. External Scripts
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;
141
251
 
142
- - Inline scripts (no src) are parsed, top‑level bindings registered, then executed in component context.
143
- - External scripts with `bind` attribute are fetched, and processed exactly like inline scripts.
252
+ function login() {
253
+ isLoggedIn = true;
254
+ }
144
255
 
145
- ```js
146
- <script src="path_to_file.js" bind></script>
256
+ function logout() {
257
+ isLoggedIn = false;
258
+ }
259
+ </script>
147
260
  ```
148
261
 
149
- - External scripts without bind are injected via a `<script>` tag (for non‑module, or third‑party scripts).
150
- - `type="module"` scripts (inline or external) are added to the shadow/root as real modules.
262
+ ### Slots
263
+
264
+ Content projection using slots:
265
+
266
+ ```html
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>
287
+ ```
288
+
289
+ ## Advanced Features
290
+
291
+ ### External Scripts
292
+
293
+ Load external JavaScript with components:
151
294
 
152
- ## Managing State
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>
153
301
 
154
- 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.
302
+ <!-- Regular external script -->
303
+ <script src="https://cdn.example.com/library.js"></script>
304
+ ```
155
305
 
156
- When you call `this.setState({ key: value, … })`, LadrillosJS will automatically re-render the component with the updated state.
306
+ For modules with `bind`, export a default function:
157
307
 
158
- ## Emitting
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
+ };
159
315
 
160
- 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.
316
+ this.init = () => {
317
+ console.log("Component initialized");
318
+ };
161
319
 
162
- ```js
163
- this.emit("event-name", { some: "data" });
320
+ // Called automatically if defined
321
+ this.init();
322
+ }
164
323
  ```
165
324
 
166
- To listen to emitted events, you can use the `this.listen()` method on the component instance:
325
+ ### Global State Stores
326
+
327
+ Share state across components:
167
328
 
168
- ```js
169
- this.listen("event-name", (payload) => {
170
- console.log(payload); // Access the emitted data
329
+ ```javascript
330
+ // stores/userStore.js
331
+ import { createStore } from "ladrillosjs";
332
+
333
+ export const userStore = createStore({
334
+ user: null,
335
+ isAuthenticated: false,
171
336
  });
337
+
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
+ }
351
+ ```
352
+
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>
359
+
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
+ }
172
380
  ```
173
381
 
174
- ## Two‑Way Binding
382
+ ### Shadow DOM
383
+
384
+ Components use Shadow DOM by default for style encapsulation. To disable:
385
+
386
+ ```javascript
387
+ // Disable Shadow DOM for a component
388
+ registerComponent("my-component", "./my-component.html", false);
389
+
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 |
175
408
 
176
- LadrillosJS provides built‑in two‑way data binding for form controls and contenteditable elements via the `data-bind` attribute.
177
- When a component is initialized, any element with `data-bind="key"` will:
409
+ ### Store Methods
178
410
 
179
- - Push its initial value/content into `this.state.key`.
180
- - Listen for user input (e.g. `input` or `change` events) and update `this.state.key` automatically.
181
- - Re-render whenever you call `this.setState({ key: newValue })`, updating the element’s value or innerText.
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 |
182
418
 
183
- Usage example:
419
+ ## Examples
420
+
421
+ ### Component Communication
184
422
 
185
423
  ```html
186
- <!-- In your component HTML -->
187
- <input type="text" placeholder="Username" data-bind="username" />
188
- <div contenteditable="true" placeholder="About you…" data-bind="bio"></div>
189
- <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>
190
437
 
191
438
  <script>
192
- const submit = () => {
193
- console.log("Username:", this.state.username);
194
- console.log("Bio:", this.state.bio);
195
- // reset state
196
- this.setState({ username: "", bio: "" });
439
+ const sendMessage = () => {
440
+ this.emit("child-event", {
441
+ message: this.state["data-message"],
442
+ timestamp: Date.now(),
443
+ });
197
444
  };
198
445
  </script>
199
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;