lego-dom 1.0.0 → 1.3.4
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/.legodom +87 -0
- package/CHANGELOG.md +87 -3
- package/cdn.html +10 -5
- package/docs/.vitepress/config.js +23 -7
- package/docs/api/config.md +95 -0
- package/docs/api/define.md +29 -2
- package/docs/api/directives.md +10 -2
- package/docs/api/index.md +1 -0
- package/docs/contributing/01-welcome.md +2 -0
- package/docs/contributing/02-registry.md +37 -3
- package/docs/contributing/06-init.md +13 -2
- package/docs/contributing/07-observer.md +3 -0
- package/docs/contributing/08-snap.md +15 -1
- package/docs/contributing/10-studs.md +3 -1
- package/docs/contributing/11-scanner.md +13 -0
- package/docs/contributing/12-render.md +32 -10
- package/docs/contributing/13-directives.md +19 -1
- package/docs/contributing/14-events.md +1 -1
- package/docs/contributing/15-router.md +49 -1
- package/docs/contributing/16-state.md +9 -10
- package/docs/contributing/17-legodom.md +1 -8
- package/docs/contributing/index.md +23 -4
- package/docs/examples/form.md +1 -1
- package/docs/examples/index.md +3 -3
- package/docs/examples/routing.md +10 -10
- package/docs/examples/sfc-showcase.md +1 -1
- package/docs/examples/todo-app.md +7 -7
- package/docs/guide/cdn-usage.md +44 -18
- package/docs/guide/components.md +18 -12
- package/docs/guide/directives.md +131 -22
- package/docs/guide/directory-structure.md +248 -0
- package/docs/guide/faq.md +210 -0
- package/docs/guide/getting-started.md +14 -10
- package/docs/guide/index.md +1 -1
- package/docs/guide/lifecycle.md +32 -0
- package/docs/guide/quick-start.md +4 -4
- package/docs/guide/reactivity.md +2 -2
- package/docs/guide/routing.md +69 -8
- package/docs/guide/server-side.md +134 -0
- package/docs/guide/sfc.md +96 -13
- package/docs/guide/templating.md +62 -57
- package/docs/index.md +9 -9
- package/docs/router/basic-routing.md +8 -8
- package/docs/router/cold-entry.md +2 -2
- package/docs/router/history.md +7 -7
- package/docs/router/index.md +1 -1
- package/docs/router/resolver.md +5 -5
- package/docs/router/surgical-swaps.md +5 -5
- package/docs/tutorial/01-project-setup.md +152 -0
- package/docs/tutorial/02-your-first-component.md +226 -0
- package/docs/tutorial/03-adding-routes.md +279 -0
- package/docs/tutorial/04-multi-page-app.md +329 -0
- package/docs/tutorial/05-state-and-globals.md +285 -0
- package/docs/tutorial/index.md +40 -0
- package/examples/vite-app/index.html +1 -0
- package/examples/vite-app/src/app.js +2 -2
- package/examples/vite-app/src/components/side-menu.lego +46 -0
- package/examples/vite-app/vite.config.js +2 -1
- package/main.js +261 -72
- package/main.min.js +7 -0
- package/monitoring-plugin.js +111 -0
- package/package.json +4 -2
- package/parse-lego.js +49 -22
- package/tests/error.test.js +74 -0
- package/tests/main.test.js +2 -2
- package/tests/memory.test.js +68 -0
- package/tests/monitoring.test.js +74 -0
- package/tests/naming.test.js +74 -0
- package/tests/parse-lego.test.js +2 -2
- package/tests/security.test.js +67 -0
- package/tests/server.test.js +114 -0
- package/tests/syntax.test.js +67 -0
- package/vite-plugin.js +3 -2
- package/docs/guide/contributing.md +0 -32
package/docs/guide/sfc.md
CHANGED
|
@@ -2,11 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
Single File Components (SFCs) let you define components in dedicated `.lego` files when using Vite as your build tool.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
::: tip 🚀 New to LegoDOM?
|
|
6
|
+
Start with our **[Step-by-Step Tutorial](/tutorial/)** to build a complete multi-page app with SFCs!
|
|
7
|
+
:::
|
|
8
|
+
|
|
9
|
+
## Where Does My Config Go?
|
|
10
|
+
|
|
11
|
+
The #1 question developers ask: **"I have a `.lego` file – now where do I put my routes?"**
|
|
12
|
+
|
|
13
|
+
**Answer: Everything goes in your entry file (`app.js` or `main.js`):**
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
// src/app.js – The control center of your app
|
|
17
|
+
|
|
18
|
+
import { Lego } from 'lego-dom';
|
|
19
|
+
import registerComponents from 'virtual:lego-components';
|
|
20
|
+
|
|
21
|
+
// 1. Register all .lego files automatically
|
|
22
|
+
registerComponents();
|
|
23
|
+
|
|
24
|
+
// 2. Define your routes
|
|
25
|
+
Lego.route('/', 'home-page'); // home-page.lego
|
|
26
|
+
Lego.route('/login', 'login-page'); // login-page.lego
|
|
27
|
+
Lego.route('/users/:id', 'user-profile'); // user-profile.lego
|
|
6
28
|
|
|
7
|
-
|
|
29
|
+
// 3. Initialize global state (optional)
|
|
30
|
+
Lego.globals.user = null;
|
|
8
31
|
|
|
9
|
-
|
|
32
|
+
// 4. Start the engine
|
|
33
|
+
await Lego.init();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Your `index.html` just needs:
|
|
37
|
+
```html
|
|
38
|
+
<lego-router></lego-router>
|
|
39
|
+
<script type="module" src="/src/app.js"></script>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
That's the complete pattern! 🎉
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Why SFCs?
|
|
47
|
+
|
|
48
|
+
When your project grows, keeping components in separate files makes your codebase more organized and maintainable.
|
|
10
49
|
|
|
11
50
|
### Benefits
|
|
12
51
|
|
|
@@ -43,12 +82,12 @@ Here's a complete example (`user-card.lego`):
|
|
|
43
82
|
|
|
44
83
|
```html
|
|
45
84
|
<template>
|
|
46
|
-
<img class="avatar" src="
|
|
47
|
-
<h2 class="name">
|
|
48
|
-
<p class="bio">
|
|
49
|
-
<p>Followers:
|
|
85
|
+
<img class="avatar" src="[[ avatarUrl ]]" alt="[[ name ]]">
|
|
86
|
+
<h2 class="name">[[ name ]]</h2>
|
|
87
|
+
<p class="bio">[[ bio ]]</p>
|
|
88
|
+
<p>Followers: [[ followers ]]</p>
|
|
50
89
|
<button @click="follow()">
|
|
51
|
-
|
|
90
|
+
[[ isFollowing ? 'Unfollow' : 'Follow' ]]
|
|
52
91
|
</button>
|
|
53
92
|
</template>
|
|
54
93
|
|
|
@@ -214,10 +253,10 @@ Contains your component's HTML markup with Lego directives:
|
|
|
214
253
|
|
|
215
254
|
```html
|
|
216
255
|
<template>
|
|
217
|
-
<h1>
|
|
218
|
-
<p b-show="showContent">
|
|
256
|
+
<h1>[[ title ]]</h1>
|
|
257
|
+
<p b-show="showContent">[[ content ]]</p>
|
|
219
258
|
<ul>
|
|
220
|
-
<li b-for="item in items">
|
|
259
|
+
<li b-for="item in items">[[ item ]]</li>
|
|
221
260
|
</ul>
|
|
222
261
|
</template>
|
|
223
262
|
```
|
|
@@ -271,6 +310,50 @@ Scoped styles using Shadow DOM. Use `self` to target the component root:
|
|
|
271
310
|
|
|
272
311
|
Styles are automatically scoped to your component—they won't affect other components or global styles.
|
|
273
312
|
|
|
313
|
+
## Dynamic Styles
|
|
314
|
+
|
|
315
|
+
A powerful feature of LegoDOM SFCs is that **interpolation works inside `<style>` tags too!**
|
|
316
|
+
|
|
317
|
+
You can use `[[ ]]` to bind CSS values directly to your component's state, props, or logic. Because styles are scoped (Shadow DOM), this is safe and won't leak.
|
|
318
|
+
|
|
319
|
+
```html
|
|
320
|
+
<template>
|
|
321
|
+
<button @click="toggleTheme()">Toggle Theme</button>
|
|
322
|
+
</template>
|
|
323
|
+
|
|
324
|
+
<style>
|
|
325
|
+
/* Use state variables directly in CSS */
|
|
326
|
+
self {
|
|
327
|
+
background-color: [[ theme === 'dark' ? '#333' : '#fff' ]];
|
|
328
|
+
color: [[ theme === 'dark' ? '#fff' : '#000' ]];
|
|
329
|
+
|
|
330
|
+
/* You can also bind strict values for calculation */
|
|
331
|
+
--padding: [[ basePadding + 'px' ]];
|
|
332
|
+
padding: var(--padding);
|
|
333
|
+
}
|
|
334
|
+
</style>
|
|
335
|
+
|
|
336
|
+
<script>
|
|
337
|
+
export default {
|
|
338
|
+
theme: 'light',
|
|
339
|
+
basePadding: 20,
|
|
340
|
+
|
|
341
|
+
toggleTheme() {
|
|
342
|
+
this.theme = this.theme === 'light' ? 'dark' : 'light';
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
</script>
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
::: tip Why this rocks 🤘
|
|
349
|
+
This eliminates the need for CSS-in-JS libraries. You get full reactivity in your CSS with standard syntax.
|
|
350
|
+
:::
|
|
351
|
+
|
|
352
|
+
::: warning Performance Note
|
|
353
|
+
Binding to CSS properties works great for themes, settings, and layout changes.
|
|
354
|
+
For high-frequency updates (like drag-and-drop coordinates or 60fps animations), prefer binding to **CSS Variables** on the host element (`style="--x: [[ x ]]"`) to avoid re-parsing the stylesheet on every frame.
|
|
355
|
+
:::
|
|
356
|
+
|
|
274
357
|
## Hot Module Replacement
|
|
275
358
|
|
|
276
359
|
During development, changes to `.lego` files trigger a full page reload. Your changes appear instantly!
|
|
@@ -340,7 +423,7 @@ components/
|
|
|
340
423
|
```html
|
|
341
424
|
<template b-id="my-component">
|
|
342
425
|
<style>self { padding: 1rem; }</style>
|
|
343
|
-
<h1>
|
|
426
|
+
<h1>[[ title ]]</h1>
|
|
344
427
|
</template>
|
|
345
428
|
|
|
346
429
|
<my-component b-data="{ title: 'Hello' }"></my-component>
|
|
@@ -351,7 +434,7 @@ components/
|
|
|
351
434
|
```html
|
|
352
435
|
<!-- my-component.lego -->
|
|
353
436
|
<template>
|
|
354
|
-
<h1>
|
|
437
|
+
<h1>[[ title ]]</h1>
|
|
355
438
|
</template>
|
|
356
439
|
|
|
357
440
|
<style>
|
package/docs/guide/templating.md
CHANGED
|
@@ -4,38 +4,38 @@ Learn about Lego templating features and syntax.
|
|
|
4
4
|
|
|
5
5
|
## Interpolation
|
|
6
6
|
|
|
7
|
-
Use `
|
|
7
|
+
Use `[[ ]]` to insert dynamic content:
|
|
8
8
|
|
|
9
9
|
### Simple Values
|
|
10
10
|
|
|
11
11
|
```html
|
|
12
|
-
<p>
|
|
13
|
-
<h1>
|
|
14
|
-
<span>
|
|
12
|
+
<p>[[ message ]]</p>
|
|
13
|
+
<h1>[[ title ]]</h1>
|
|
14
|
+
<span>[[ count ]]</span>
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
### Expressions
|
|
18
18
|
|
|
19
19
|
```html
|
|
20
|
-
<p>
|
|
21
|
-
<span>
|
|
22
|
-
<div>
|
|
20
|
+
<p>[[ count * 2 ]]</p>
|
|
21
|
+
<span>[[ price.toFixed(2) ]]</span>
|
|
22
|
+
<div>[[ firstName + ' ' + lastName ]]</div>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
### Method Calls
|
|
26
26
|
|
|
27
27
|
```html
|
|
28
|
-
<p>
|
|
29
|
-
<span>
|
|
30
|
-
<div>
|
|
28
|
+
<p>[[ formatDate(timestamp) ]]</p>
|
|
29
|
+
<span>[[ calculateTotal() ]]</span>
|
|
30
|
+
<div>[[ getUsername() ]]</div>
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
### Conditional (Ternary)
|
|
34
34
|
|
|
35
35
|
```html
|
|
36
|
-
<p>
|
|
37
|
-
<span>
|
|
38
|
-
<div>
|
|
36
|
+
<p>[[ age >= 18 ? 'Adult' : 'Minor' ]]</p>
|
|
37
|
+
<span>[[ isOnline ? '🟢 Online' : '🔴 Offline' ]]</span>
|
|
38
|
+
<div>[[ items.length > 0 ? items.length + ' items' : 'Empty' ]]</div>
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
## Attribute Binding
|
|
@@ -45,29 +45,29 @@ Interpolate in any attribute:
|
|
|
45
45
|
### Simple Attributes
|
|
46
46
|
|
|
47
47
|
```html
|
|
48
|
-
<img src="/avatars/
|
|
49
|
-
<a href="/user/
|
|
50
|
-
<input placeholder="
|
|
48
|
+
<img src="/avatars/[[ userId ]].png" alt="[[ username ]]">
|
|
49
|
+
<a href="/user/[[ userId ]]">[[ username ]]</a>
|
|
50
|
+
<input placeholder="[[ defaultText ]]">
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
### Class Names
|
|
54
54
|
|
|
55
55
|
```html
|
|
56
|
-
<div class="card
|
|
57
|
-
<button class="
|
|
58
|
-
<li class="item status-
|
|
56
|
+
<div class="card [[ isActive ? 'active' : '' ]]">...</div>
|
|
57
|
+
<button class="[[ isDisabled ? 'disabled' : 'enabled' ]]">...</button>
|
|
58
|
+
<li class="item status-[[ status ]]">...</li>
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
### Data Attributes
|
|
62
62
|
|
|
63
63
|
```html
|
|
64
|
-
<div data-id="
|
|
64
|
+
<div data-id="[[ itemId ]]" data-type="[[ itemType ]]">...</div>
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
### Style (Inline)
|
|
68
68
|
|
|
69
69
|
```html
|
|
70
|
-
<div style="color:
|
|
70
|
+
<div style="color: [[ textColor ]]; background: [[ bgColor ]]">...</div>
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
## Escaping
|
|
@@ -81,11 +81,16 @@ Lego automatically escapes HTML to prevent XSS:
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
```html
|
|
84
|
-
<p>
|
|
84
|
+
<p>[[ userInput ]]</p>
|
|
85
85
|
<!-- Renders as: <script>alert("XSS")</script> -->
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
**
|
|
88
|
+
**By default,## Raw HTML
|
|
89
|
+
|
|
90
|
+
To render raw HTML content, use the `b-html` directive.
|
|
91
|
+
|
|
92
|
+
> [!WARNING]
|
|
93
|
+
> Never use `b-html` on untrusted user input (e.g., comments, messages). It can lead to XSS attacks.
|
|
89
94
|
|
|
90
95
|
## Whitespace
|
|
91
96
|
|
|
@@ -93,7 +98,7 @@ Templates preserve whitespace:
|
|
|
93
98
|
|
|
94
99
|
```html
|
|
95
100
|
<p>
|
|
96
|
-
|
|
101
|
+
[[ message ]]
|
|
97
102
|
</p>
|
|
98
103
|
<!-- Renders with newlines and indentation -->
|
|
99
104
|
```
|
|
@@ -101,25 +106,25 @@ Templates preserve whitespace:
|
|
|
101
106
|
Trim manually if needed:
|
|
102
107
|
|
|
103
108
|
```html
|
|
104
|
-
<p>
|
|
109
|
+
<p>[[ message.trim() ]]</p>
|
|
105
110
|
```
|
|
106
111
|
|
|
107
112
|
## Context
|
|
108
113
|
|
|
109
|
-
Inside `
|
|
114
|
+
Inside `[[ ]]`, you have access to:
|
|
110
115
|
|
|
111
116
|
### Component State (`this`)
|
|
112
117
|
|
|
113
118
|
```html
|
|
114
|
-
<p>
|
|
115
|
-
<span>
|
|
119
|
+
<p>[[ count ]]</p> <!-- this.count -->
|
|
120
|
+
<span>[[ user.name ]]</span> <!-- this.user.name -->
|
|
116
121
|
```
|
|
117
122
|
|
|
118
123
|
### Methods
|
|
119
124
|
|
|
120
125
|
```html
|
|
121
|
-
<p>
|
|
122
|
-
<div>
|
|
126
|
+
<p>[[ formatDate(timestamp) ]]</p>
|
|
127
|
+
<div>[[ calculateTotal() ]]</div>
|
|
123
128
|
```
|
|
124
129
|
|
|
125
130
|
### Special Keywords
|
|
@@ -129,7 +134,7 @@ Inside `{{ }}`, you have access to:
|
|
|
129
134
|
- `self` - Reference to component element (rare)
|
|
130
135
|
|
|
131
136
|
```html
|
|
132
|
-
<p>
|
|
137
|
+
<p>[[ global.user.name ]]</p>
|
|
133
138
|
<button @click="console.log(event)">Click</button>
|
|
134
139
|
```
|
|
135
140
|
|
|
@@ -147,7 +152,7 @@ Inside `{{ }}`, you have access to:
|
|
|
147
152
|
```
|
|
148
153
|
|
|
149
154
|
```html
|
|
150
|
-
<p>Price:
|
|
155
|
+
<p>Price: [[ formatCurrency(price) ]]</p>
|
|
151
156
|
```
|
|
152
157
|
|
|
153
158
|
### Date Formatting
|
|
@@ -162,7 +167,7 @@ Inside `{{ }}`, you have access to:
|
|
|
162
167
|
```
|
|
163
168
|
|
|
164
169
|
```html
|
|
165
|
-
<time>
|
|
170
|
+
<time>[[ formatDate(timestamp) ]]</time>
|
|
166
171
|
```
|
|
167
172
|
|
|
168
173
|
### Pluralization
|
|
@@ -177,7 +182,7 @@ Inside `{{ }}`, you have access to:
|
|
|
177
182
|
```
|
|
178
183
|
|
|
179
184
|
```html
|
|
180
|
-
<p>
|
|
185
|
+
<p>[[ items.length ]] [[ plural(items.length, 'item', 'items') ]]</p>
|
|
181
186
|
```
|
|
182
187
|
|
|
183
188
|
### Truncation
|
|
@@ -194,7 +199,7 @@ Inside `{{ }}`, you have access to:
|
|
|
194
199
|
```
|
|
195
200
|
|
|
196
201
|
```html
|
|
197
|
-
<p>
|
|
202
|
+
<p>[[ truncate(description, 100) ]]</p>
|
|
198
203
|
```
|
|
199
204
|
|
|
200
205
|
## Limitations
|
|
@@ -205,12 +210,12 @@ Can't use statements—only expressions:
|
|
|
205
210
|
|
|
206
211
|
```html
|
|
207
212
|
<!-- ❌ Doesn't work -->
|
|
208
|
-
<p>
|
|
209
|
-
<p>
|
|
213
|
+
<p>[[ if (condition) { return 'yes'; } ]]</p>
|
|
214
|
+
<p>[[ for (let i = 0; i < 10; i++) { } ]]</p>
|
|
210
215
|
|
|
211
216
|
<!-- ✅ Use ternary or methods -->
|
|
212
|
-
<p>
|
|
213
|
-
<p>
|
|
217
|
+
<p>[[ condition ? 'yes' : 'no' ]]</p>
|
|
218
|
+
<p>[[ renderList() ]]</p>
|
|
214
219
|
```
|
|
215
220
|
|
|
216
221
|
### No Declarations
|
|
@@ -219,10 +224,10 @@ Can't declare variables:
|
|
|
219
224
|
|
|
220
225
|
```html
|
|
221
226
|
<!-- ❌ Doesn't work -->
|
|
222
|
-
<p>
|
|
227
|
+
<p>[[ const total = price * qty; total ]]</p>
|
|
223
228
|
|
|
224
229
|
<!-- ✅ Use methods -->
|
|
225
|
-
<p>
|
|
230
|
+
<p>[[ getTotal() ]]</p>
|
|
226
231
|
```
|
|
227
232
|
|
|
228
233
|
```js
|
|
@@ -242,10 +247,10 @@ If logic is complex, use methods:
|
|
|
242
247
|
|
|
243
248
|
```html
|
|
244
249
|
<!-- ❌ Too complex -->
|
|
245
|
-
<p>
|
|
250
|
+
<p>[[ items.filter(x => x.active).map(x => x.name).join(', ') ]]</p>
|
|
246
251
|
|
|
247
252
|
<!-- ✅ Better -->
|
|
248
|
-
<p>
|
|
253
|
+
<p>[[ getActiveNames() ]]</p>
|
|
249
254
|
```
|
|
250
255
|
|
|
251
256
|
```js
|
|
@@ -265,10 +270,10 @@ Don't put formatting logic in templates:
|
|
|
265
270
|
|
|
266
271
|
```html
|
|
267
272
|
<!-- ❌ Messy -->
|
|
268
|
-
<p>$
|
|
273
|
+
<p>$[[ (price * 1.2).toFixed(2) ]]</p>
|
|
269
274
|
|
|
270
275
|
<!-- ✅ Clean -->
|
|
271
|
-
<p>
|
|
276
|
+
<p>[[ formatPrice(price) ]]</p>
|
|
272
277
|
```
|
|
273
278
|
|
|
274
279
|
### 3. Avoid Side Effects
|
|
@@ -277,10 +282,10 @@ Don't mutate state in templates:
|
|
|
277
282
|
|
|
278
283
|
```html
|
|
279
284
|
<!-- ❌ Bad -->
|
|
280
|
-
<p>
|
|
285
|
+
<p>[[ count++ ]]</p>
|
|
281
286
|
|
|
282
287
|
<!-- ✅ Good -->
|
|
283
|
-
<p>
|
|
288
|
+
<p>[[ count ]]</p>
|
|
284
289
|
<button @click="count++">Increment</button>
|
|
285
290
|
```
|
|
286
291
|
|
|
@@ -323,10 +328,10 @@ If a calculation is expensive, cache it:
|
|
|
323
328
|
|
|
324
329
|
```html
|
|
325
330
|
<!-- ❌ Runs on every render -->
|
|
326
|
-
<p>
|
|
331
|
+
<p>[[ expensiveCalculation() ]]</p>
|
|
327
332
|
|
|
328
333
|
<!-- ✅ Calculate once, store result -->
|
|
329
|
-
<p>
|
|
334
|
+
<p>[[ cachedResult ]]</p>
|
|
330
335
|
```
|
|
331
336
|
|
|
332
337
|
```js
|
|
@@ -343,7 +348,7 @@ If a calculation is expensive, cache it:
|
|
|
343
348
|
### Show/Hide Based on Condition
|
|
344
349
|
|
|
345
350
|
```html
|
|
346
|
-
<p b-show="user">Welcome,
|
|
351
|
+
<p b-show="user">Welcome, [[ user.name ]]!</p>
|
|
347
352
|
<p b-show="!user">Please log in</p>
|
|
348
353
|
```
|
|
349
354
|
|
|
@@ -352,7 +357,7 @@ If a calculation is expensive, cache it:
|
|
|
352
357
|
```html
|
|
353
358
|
<ul>
|
|
354
359
|
<li b-for="item in items">
|
|
355
|
-
#
|
|
360
|
+
#[[ $index + 1 ]]: [[ item.name ]]
|
|
356
361
|
</li>
|
|
357
362
|
</ul>
|
|
358
363
|
```
|
|
@@ -360,8 +365,8 @@ If a calculation is expensive, cache it:
|
|
|
360
365
|
### Conditional Classes
|
|
361
366
|
|
|
362
367
|
```html
|
|
363
|
-
<div class="item
|
|
364
|
-
|
|
368
|
+
<div class="item [[ item.active ? 'active' : '' ]] [[ item.featured ? 'featured' : '' ]]">
|
|
369
|
+
[[ item.name ]]
|
|
365
370
|
</div>
|
|
366
371
|
```
|
|
367
372
|
|
|
@@ -369,10 +374,10 @@ If a calculation is expensive, cache it:
|
|
|
369
374
|
|
|
370
375
|
```html
|
|
371
376
|
<a
|
|
372
|
-
href="/product/
|
|
373
|
-
class="product-link
|
|
374
|
-
title="
|
|
375
|
-
|
|
377
|
+
href="/product/[[ product.id ]]"
|
|
378
|
+
class="product-link [[ product.inStock ? '' : 'out-of-stock' ]]"
|
|
379
|
+
title="[[ product.name ]] - $[[ product.price ]]">
|
|
380
|
+
[[ product.name ]]
|
|
376
381
|
</a>
|
|
377
382
|
```
|
|
378
383
|
|
package/docs/index.md
CHANGED
|
@@ -7,14 +7,14 @@ hero:
|
|
|
7
7
|
tagline: A tiny, zero-dependency Web library for creating reactive Web Components directly in the browser
|
|
8
8
|
image:
|
|
9
9
|
src: /logo.svg
|
|
10
|
-
alt:
|
|
10
|
+
alt: LegoDOM
|
|
11
11
|
actions:
|
|
12
12
|
- theme: brand
|
|
13
13
|
text: Get Started
|
|
14
14
|
link: /guide/getting-started
|
|
15
15
|
- theme: alt
|
|
16
16
|
text: View on GitHub
|
|
17
|
-
link: https://github.com/rayattack/
|
|
17
|
+
link: https://github.com/rayattack/LegoDOM
|
|
18
18
|
- theme: alt
|
|
19
19
|
text: Try Examples
|
|
20
20
|
link: /examples/
|
|
@@ -59,7 +59,7 @@ features:
|
|
|
59
59
|
|
|
60
60
|
## Components & Naming
|
|
61
61
|
|
|
62
|
-
How you name your components depends on how you use
|
|
62
|
+
How you name your components depends on how you use LegoDOM:
|
|
63
63
|
|
|
64
64
|
### 1. Vite / Build Tools (Recommended)
|
|
65
65
|
**Convention over Configuration.** The filename *is* the tag name.
|
|
@@ -109,21 +109,21 @@ Since there are no files, you must explicitly name your components using the `b-
|
|
|
109
109
|
<script src="https://unpkg.com/lego-dom/main.js"></script>
|
|
110
110
|
<script>
|
|
111
111
|
// Complete the initialization
|
|
112
|
-
|
|
112
|
+
LegoDOM.init();
|
|
113
113
|
</script>
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
That's it. No build step, no npm, no configuration.
|
|
117
117
|
|
|
118
118
|
> [!IMPORTANT]
|
|
119
|
-
> **Why call `
|
|
120
|
-
> While `
|
|
119
|
+
> **Why call `LegoDOM.init()`?**
|
|
120
|
+
> While `LegoDOM.define()` will "snap" your components into the page immediately, you must call `LegoDOM.init()` to start the background engine. Without it:
|
|
121
121
|
> - **Reactivity** to data changes won't work.
|
|
122
122
|
> - **Mustaches** (<code v-pre>{{...}}</code>) outside of components won't hydrate.
|
|
123
123
|
> - **Single Page Routing** won't be activated.
|
|
124
124
|
> - **New components** added to the DOM dynamically won't be auto-initialized.
|
|
125
125
|
|
|
126
|
-
## Why
|
|
126
|
+
## Why LegoDOM?
|
|
127
127
|
|
|
128
128
|
**For small projects**, you get reactive components without the overhead of a full framework.
|
|
129
129
|
|
|
@@ -133,7 +133,7 @@ That's it. No build step, no npm, no configuration.
|
|
|
133
133
|
|
|
134
134
|
## Comparison
|
|
135
135
|
|
|
136
|
-
| Feature |
|
|
136
|
+
| Feature | LegoDOM | Vue | React |
|
|
137
137
|
|---------|--------|-----|-------|
|
|
138
138
|
| Size | < 17KB | ~33KB | ~40KB |
|
|
139
139
|
| Dependencies | 0 | Many | Many |
|
|
@@ -146,7 +146,7 @@ That's it. No build step, no npm, no configuration.
|
|
|
146
146
|
|
|
147
147
|
## Browser Support
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
LegoDOM works in all modern browsers that support:
|
|
150
150
|
- Web Components
|
|
151
151
|
- Shadow DOM
|
|
152
152
|
- ES6 Proxy
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Basic Routing: The Global Outlet
|
|
2
2
|
|
|
3
|
-
Before we dive into surgical swaps, we must understand how
|
|
3
|
+
Before we dive into surgical swaps, we must understand how LegoDOM handles the initial entry into your application. Every application needs a primary gateway—a place where the main content lives. In LegoDOM, this is the `<lego-router>`.
|
|
4
4
|
|
|
5
5
|
## The Entry Point
|
|
6
6
|
|
|
@@ -15,7 +15,7 @@ In your `index.html`, you define a single custom element that acts as the "Maste
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
When the page loads, the
|
|
18
|
+
When the page loads, the LegoDOM router looks at the current browser URL and searches its internal "Route Map" for a match.
|
|
19
19
|
|
|
20
20
|
## Defining Your First Routes
|
|
21
21
|
|
|
@@ -32,16 +32,16 @@ Lego.route('/user/:id', 'profile-page');
|
|
|
32
32
|
|
|
33
33
|
### How the Matching Works
|
|
34
34
|
|
|
35
|
-
1. **The Match**:
|
|
35
|
+
1. **The Match**: LegoDOM uses Regex to compare the window location with your defined paths.
|
|
36
36
|
|
|
37
|
-
2. **The Injection**: If a match is found,
|
|
37
|
+
2. **The Injection**: If a match is found, LegoDOM creates an instance of the associated component (e.g., `<home-page>`) and injects it into the `<lego-router>` tag.
|
|
38
38
|
|
|
39
39
|
3. **The Hydration**: The framework then "snaps" the component to life, initializing its state and running its `mounted()` lifecycle hook.
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
## The Global Fallback
|
|
43
43
|
|
|
44
|
-
If no route matches the current URL,
|
|
44
|
+
If no route matches the current URL, LegoDOM looks for a special fallback route. This is essential for handling **404 Not Found** states gracefully.
|
|
45
45
|
|
|
46
46
|
```js
|
|
47
47
|
Lego.route('*', 'not-found-page');
|
|
@@ -50,14 +50,14 @@ Lego.route('*', 'not-found-page');
|
|
|
50
50
|
|
|
51
51
|
## Handling Parameters (`$params`)
|
|
52
52
|
|
|
53
|
-
When you use a dynamic path like `/user/:id`,
|
|
53
|
+
When you use a dynamic path like `/user/:id`, LegoDOM automatically parses the URL and makes the data available to the component via the global `$route` object.
|
|
54
54
|
|
|
55
55
|
In your component template:
|
|
56
56
|
|
|
57
57
|
```html
|
|
58
58
|
<template b-id="profile-page">
|
|
59
59
|
<h1>User Profile</h1>
|
|
60
|
-
<p>Viewing ID:
|
|
60
|
+
<p>Viewing ID: [[ $route.params.id ]]</p>
|
|
61
61
|
</template>
|
|
62
62
|
|
|
63
63
|
```
|
|
@@ -83,7 +83,7 @@ mounted() {
|
|
|
83
83
|
|
|
84
84
|
<template>
|
|
85
85
|
<h1>User Profile</h1>
|
|
86
|
-
<p>Viewing ID:
|
|
86
|
+
<p>Viewing ID: [[ $params.id ]]</p>
|
|
87
87
|
</template>
|
|
88
88
|
|
|
89
89
|
<script>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
In the previous section, we learned how to use `b-target` to surgically swap fragments while the app is running. But what happens when a user types `myapp.com/messaging/123` directly into the address bar or hits **Refresh (F5)**?
|
|
5
5
|
|
|
6
|
-
In a traditional nested router, the framework handles this automatically. In
|
|
6
|
+
In a traditional nested router, the framework handles this automatically. In LegoDOM, we use a more powerful, explicit pattern called **Self-Healing Layouts**.
|
|
7
7
|
|
|
8
8
|
## The Cold Entry/Start Challenge
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@ When a "Cold Start" occurs:
|
|
|
13
13
|
|
|
14
14
|
2. The server returns the base `index.html`.
|
|
15
15
|
|
|
16
|
-
3.
|
|
16
|
+
3. LegoDOM matches the route `/messaging/:id` to the `messaging-shell` component.
|
|
17
17
|
|
|
18
18
|
4. The `messaging-shell` mounts, but the `#chat-window` target is empty (or showing its default "Select a conversation" text).
|
|
19
19
|
|
package/docs/router/history.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
One of the biggest frustrations in modern web development is "breaking the back button." When you use JavaScript to update only part of a page, the browser often doesn't realize a navigation event occurred. If the user hits "Back," they might be booted out of your app entirely.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
LegoDOM solves this using a system called **Smart History**. It ensures that even surgical, fragment-level updates are recorded and reversible.
|
|
7
7
|
|
|
8
8
|
## How Traditional Routers Fail
|
|
9
9
|
|
|
@@ -18,9 +18,9 @@ In a standard SPA, the router usually manages a single "current view." When you
|
|
|
18
18
|
|
|
19
19
|
Because the whole page (or the main outlet) is replaced, the browser's history stack is simple: Page A -> Page B. But in a complex layout like LinkedIn, you might have changed the chat window 5 times while the sidebar stayed exactly the same. Users expect the "Back" button to cycle through those chats, not take them back to the login screen.
|
|
20
20
|
|
|
21
|
-
## The
|
|
21
|
+
## The LegoDOM Approach: Target Tracking
|
|
22
22
|
|
|
23
|
-
When you use `b-link` with a `b-target`,
|
|
23
|
+
When you use `b-link` with a `b-target`, LegoDOM does two things simultaneously:
|
|
24
24
|
|
|
25
25
|
1. It updates the browser URL using the History API.
|
|
26
26
|
|
|
@@ -29,7 +29,7 @@ When you use `b-link` with a `b-target`, LegoJS does two things simultaneously:
|
|
|
29
29
|
|
|
30
30
|
### Inside the History State
|
|
31
31
|
|
|
32
|
-
Every time a surgical swap happens,
|
|
32
|
+
Every time a surgical swap happens, LegoDOM saves the target selector in the `history.state`. It looks something like this:
|
|
33
33
|
|
|
34
34
|
```js
|
|
35
35
|
// Internal representation
|
|
@@ -41,9 +41,9 @@ history.pushState({
|
|
|
41
41
|
|
|
42
42
|
## The "Popstate" Magic
|
|
43
43
|
|
|
44
|
-
When a user hits the **Back** or **Forward** button, the browser triggers a `popstate` event.
|
|
44
|
+
When a user hits the **Back** or **Forward** button, the browser triggers a `popstate` event. LegoDOM intercepts this event and checks if the incoming state contains `legoTargets`.
|
|
45
45
|
|
|
46
|
-
- **If `legoTargets` exists:**
|
|
46
|
+
- **If `legoTargets` exists:** LegoDOM performs a surgical swap. It takes the URL the browser is moving to and renders it _only_ into the specified target (e.g., `#chat-window`).
|
|
47
47
|
|
|
48
48
|
- **If no target exists:** It performs a global swap in the `<lego-router>`.
|
|
49
49
|
|
|
@@ -66,4 +66,4 @@ Because the history state uses the same URLs as your standard links, a "Back" na
|
|
|
66
66
|
|
|
67
67
|
Smart History is the "glue" that makes a multi-fragment interface feel like a single, cohesive application. It respects the user's intent by making the browser's navigation tools work for specific parts of the page, not just the whole page.
|
|
68
68
|
|
|
69
|
-
Next, we'll dive into the mechanics of how
|
|
69
|
+
Next, we'll dive into the mechanics of how LegoDOM finds these targets in complex, nested DOM trees in **Target Resolver: Scoping and Logic**.
|
package/docs/router/index.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
Traditional Single Page Application (SPA) routers (like React Router, Vue Router, or Angular's Router) rely on a **Centralized Configuration Tree**. As your project grows, this JSON or JavaScript object becomes a "source of truth" that is fragile, hard to maintain, and forces a rigid hierarchy on your UI.
|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**LegoDOM breaks this pattern.**
|
|
7
7
|
|
|
8
8
|
In Lego, we don't nest routes in a config file. Instead, we use the **URL as the Data Source** and the **DOM as the Layout Engine**.
|
|
9
9
|
|