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
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Step 5: State & Globals
|
|
2
|
+
|
|
3
|
+
You've built a complete app! Now let's understand how state management works in LegoDOM and explore advanced patterns.
|
|
4
|
+
|
|
5
|
+
## Two Levels of State
|
|
6
|
+
|
|
7
|
+
### 1. Component State (Local)
|
|
8
|
+
|
|
9
|
+
Each component has its own reactive state defined in the `<script>` section:
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
export default {
|
|
13
|
+
// This is local to THIS component instance
|
|
14
|
+
count: 0,
|
|
15
|
+
items: [],
|
|
16
|
+
|
|
17
|
+
increment() {
|
|
18
|
+
this.count++; // Triggers re-render of THIS component only
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 2. Global State (`Lego.globals`)
|
|
24
|
+
|
|
25
|
+
Shared across ALL components. Perfect for user sessions, themes, shopping carts, etc.
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// In app.js or any component
|
|
29
|
+
Lego.globals.user = { name: 'John' };
|
|
30
|
+
Lego.globals.theme = 'dark';
|
|
31
|
+
Lego.globals.cart = [];
|
|
32
|
+
|
|
33
|
+
// In any component
|
|
34
|
+
console.log(Lego.globals.user.name); // 'John'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Accessing Globals in Components
|
|
38
|
+
|
|
39
|
+
### In JavaScript (Methods)
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
export default {
|
|
43
|
+
mounted() {
|
|
44
|
+
// Direct access
|
|
45
|
+
console.log(Lego.globals.user);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
toggleTheme() {
|
|
49
|
+
Lego.globals.theme = Lego.globals.theme === 'dark' ? 'light' : 'dark';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### In Templates
|
|
55
|
+
|
|
56
|
+
Use the `global` keyword:
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<template>
|
|
60
|
+
<p>Hello, [[ global.user.name ]]!</p>
|
|
61
|
+
<div class="[[ global.theme ]]">
|
|
62
|
+
Content styled by theme
|
|
63
|
+
</div>
|
|
64
|
+
<button b-show="global.user">Logout</button>
|
|
65
|
+
</template>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Route State (`$route`)
|
|
69
|
+
|
|
70
|
+
Access current route information:
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
// Available in any component
|
|
74
|
+
this.$route.url // '/users/42?tab=posts'
|
|
75
|
+
this.$route.route // '/users/:id'
|
|
76
|
+
this.$route.params // { id: '42' }
|
|
77
|
+
this.$route.query // { tab: 'posts' }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<template>
|
|
82
|
+
<h1>User [[ $route.params.id ]]</h1>
|
|
83
|
+
<p>Current tab: [[ $route.query.tab ]]</p>
|
|
84
|
+
</template>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## State Patterns
|
|
88
|
+
|
|
89
|
+
### Pattern 1: User Authentication
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
// app.js
|
|
93
|
+
Lego.globals.user = null; // Initialize
|
|
94
|
+
|
|
95
|
+
// login-page.lego
|
|
96
|
+
handleLogin() {
|
|
97
|
+
Lego.globals.user = { name: 'John', token: 'abc123' };
|
|
98
|
+
this.$go('/dashboard').get();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Any protected component
|
|
102
|
+
mounted() {
|
|
103
|
+
if (!Lego.globals.user) {
|
|
104
|
+
this.$go('/login').get();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Pattern 2: Theme Switching
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
// app.js
|
|
113
|
+
Lego.globals.theme = localStorage.getItem('theme') || 'light';
|
|
114
|
+
|
|
115
|
+
// theme-toggle.lego
|
|
116
|
+
<template>
|
|
117
|
+
<button @click="toggle()">
|
|
118
|
+
[[ global.theme === 'dark' ? '☀️' : '🌙' ]]
|
|
119
|
+
</button>
|
|
120
|
+
</template>
|
|
121
|
+
|
|
122
|
+
<script>
|
|
123
|
+
export default {
|
|
124
|
+
toggle() {
|
|
125
|
+
const newTheme = Lego.globals.theme === 'dark' ? 'light' : 'dark';
|
|
126
|
+
Lego.globals.theme = newTheme;
|
|
127
|
+
localStorage.setItem('theme', newTheme);
|
|
128
|
+
document.body.className = newTheme;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
</script>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Pattern 3: Shopping Cart
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
// app.js
|
|
138
|
+
Lego.globals.cart = [];
|
|
139
|
+
|
|
140
|
+
// product-card.lego
|
|
141
|
+
addToCart() {
|
|
142
|
+
Lego.globals.cart.push({
|
|
143
|
+
id: this.product.id,
|
|
144
|
+
name: this.product.name,
|
|
145
|
+
price: this.product.price
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// cart-icon.lego
|
|
150
|
+
<template>
|
|
151
|
+
<span class="cart">
|
|
152
|
+
🛒 [[ global.cart.length ]]
|
|
153
|
+
</span>
|
|
154
|
+
</template>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Pattern 4: Fetching Data
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
export default {
|
|
161
|
+
users: [],
|
|
162
|
+
loading: true,
|
|
163
|
+
error: null,
|
|
164
|
+
|
|
165
|
+
async mounted() {
|
|
166
|
+
try {
|
|
167
|
+
const response = await fetch('/api/users');
|
|
168
|
+
this.users = await response.json();
|
|
169
|
+
} catch (e) {
|
|
170
|
+
this.error = e.message;
|
|
171
|
+
} finally {
|
|
172
|
+
this.loading = false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```html
|
|
179
|
+
<template>
|
|
180
|
+
<div b-show="loading">Loading...</div>
|
|
181
|
+
<div b-show="error" class="error">[[ error ]]</div>
|
|
182
|
+
<ul b-show="!loading && !error">
|
|
183
|
+
<li b-for="user in users">[[ user.name ]]</li>
|
|
184
|
+
</ul>
|
|
185
|
+
</template>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Cross-Component Communication
|
|
189
|
+
|
|
190
|
+
### Using `$emit` (Child to Parent)
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<!-- child-component.lego -->
|
|
194
|
+
<template>
|
|
195
|
+
<button @click="notifyParent()">Click Me</button>
|
|
196
|
+
</template>
|
|
197
|
+
|
|
198
|
+
<script>
|
|
199
|
+
export default {
|
|
200
|
+
notifyParent() {
|
|
201
|
+
this.$emit('custom-event', { message: 'Hello from child!' });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
</script>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```html
|
|
208
|
+
<!-- parent-component.lego -->
|
|
209
|
+
<template>
|
|
210
|
+
<child-component @custom-event="handleEvent(event)"></child-component>
|
|
211
|
+
</template>
|
|
212
|
+
|
|
213
|
+
<script>
|
|
214
|
+
export default {
|
|
215
|
+
handleEvent(event) {
|
|
216
|
+
console.log(event.detail.message); // 'Hello from child!'
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
</script>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Using `$ancestors` (Access Parent State)
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
// In a deeply nested component
|
|
226
|
+
mounted() {
|
|
227
|
+
// Get state from a specific ancestor by tag name
|
|
228
|
+
const parentState = this.$ancestors('user-profile');
|
|
229
|
+
console.log(parentState.userId);
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Best Practices
|
|
234
|
+
|
|
235
|
+
| Do | Don't |
|
|
236
|
+
|----|-------|
|
|
237
|
+
| ✅ Use `Lego.globals` for truly global state (user, theme) | ❌ Put everything in globals |
|
|
238
|
+
| ✅ Keep component state local when possible | ❌ Over-engineer state management |
|
|
239
|
+
| ✅ Initialize globals in `app.js` | ❌ Initialize globals in random components |
|
|
240
|
+
| ✅ Use `$emit` for child→parent communication | ❌ Reach into child component internals |
|
|
241
|
+
|
|
242
|
+
## Complete State Cheatsheet
|
|
243
|
+
|
|
244
|
+
| I want to... | Use this |
|
|
245
|
+
|--------------|----------|
|
|
246
|
+
| Store component-local data | `export default { myData: ... }` |
|
|
247
|
+
| Share data app-wide | `Lego.globals.myData = ...` |
|
|
248
|
+
| Read global in template | `[[ global.myData ]]` |
|
|
249
|
+
| Read global in JS | `Lego.globals.myData` |
|
|
250
|
+
| Get route params | `this.$route.params.id` |
|
|
251
|
+
| Get query string | `this.$route.query.tab` |
|
|
252
|
+
| Notify parent component | `this.$emit('event-name', data)` |
|
|
253
|
+
| Navigate with state | Set globals before `$go()` |
|
|
254
|
+
|
|
255
|
+
## What's Next?
|
|
256
|
+
|
|
257
|
+
🎉 **Congratulations!** You've completed the LegoDOM tutorial!
|
|
258
|
+
|
|
259
|
+
You now know how to:
|
|
260
|
+
- Set up a project from scratch
|
|
261
|
+
- Create beautiful components
|
|
262
|
+
- Navigate between pages
|
|
263
|
+
- Share state across your app
|
|
264
|
+
|
|
265
|
+
### Continue Learning
|
|
266
|
+
|
|
267
|
+
- [Components Deep Dive](/guide/components) – Advanced component patterns
|
|
268
|
+
- [Directives Reference](/guide/directives) – All `b-*` directives explained
|
|
269
|
+
- [Routing Guide](/guide/routing) – Surgical swaps and advanced routing
|
|
270
|
+
- [API Reference](/api/) – Complete API documentation
|
|
271
|
+
|
|
272
|
+
### Get Help
|
|
273
|
+
|
|
274
|
+
- 💬 [GitHub Discussions](https://github.com/rayattack/LegoDOM/discussions)
|
|
275
|
+
- 🐛 [Report Issues](https://github.com/rayattack/LegoDOM/issues)
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
<div style="text-align: center; margin-top: 3rem; padding: 2rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 16px; color: white;">
|
|
280
|
+
<h2 style="margin-bottom: 1rem;">You Did It! 🚀</h2>
|
|
281
|
+
<p style="opacity: 0.9; margin-bottom: 1.5rem;">You've built a complete multi-page app with LegoDOM.</p>
|
|
282
|
+
<a href="/guide/" style="display: inline-block; background: white; color: #667eea; padding: 0.75rem 1.5rem; border-radius: 8px; text-decoration: none; font-weight: 600;">
|
|
283
|
+
Explore More Guides →
|
|
284
|
+
</a>
|
|
285
|
+
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Your First LegoDOM App
|
|
2
|
+
|
|
3
|
+
Welcome! In the next 15 minutes, you'll build a complete multi-page application with beautiful components, smooth navigation, and a login flow. No prior framework experience needed.
|
|
4
|
+
|
|
5
|
+
## What You'll Build
|
|
6
|
+
|
|
7
|
+
A polished app with:
|
|
8
|
+
- 🏠 **Landing Page** – Your app's front door
|
|
9
|
+
- 🔐 **Login Page** – A form that validates and navigates
|
|
10
|
+
- 🎉 **Welcome Page** – Personalized dashboard after login
|
|
11
|
+
|
|
12
|
+
All using Single File Components (`.lego` files) and client-side routing.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- Node.js 18+ installed
|
|
17
|
+
- A code editor (VS Code recommended)
|
|
18
|
+
- Basic HTML/CSS/JavaScript knowledge
|
|
19
|
+
|
|
20
|
+
## Tutorial Path
|
|
21
|
+
|
|
22
|
+
| Step | You'll Learn |
|
|
23
|
+
|------|--------------|
|
|
24
|
+
| [1. Project Setup](./01-project-setup) | Create a new project with Vite |
|
|
25
|
+
| [2. Your First Component](./02-your-first-component) | Build a `.lego` file |
|
|
26
|
+
| [3. Adding Routes](./03-adding-routes) | Navigate between pages |
|
|
27
|
+
| [4. Multi-Page App](./04-multi-page-app) | Complete login flow |
|
|
28
|
+
| [5. State & Globals](./05-state-and-globals) | Share data across pages |
|
|
29
|
+
|
|
30
|
+
## Ready?
|
|
31
|
+
|
|
32
|
+
<div style="text-align: center; margin: 2rem 0;">
|
|
33
|
+
<a href="./01-project-setup" style="display: inline-block; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 1rem 2rem; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 1.2rem;">
|
|
34
|
+
Start Building →
|
|
35
|
+
</a>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
::: tip No Build Tools? No Problem!
|
|
39
|
+
If you want to skip the Vite setup and use LegoDOM via CDN, check out the [CDN Quick Start](/guide/cdn-usage). You can always come back to this tutorial later.
|
|
40
|
+
:::
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Import Tailwind CSS
|
|
2
2
|
import './app.css';
|
|
3
3
|
|
|
4
|
-
// Import Lego core
|
|
5
|
-
import { Lego } from 'lego
|
|
4
|
+
// Import Lego core (local for testing)
|
|
5
|
+
import { Lego } from '../../../lego.js';
|
|
6
6
|
|
|
7
7
|
// Import virtual module that auto-discovers and registers all .lego components
|
|
8
8
|
import registerComponents from 'virtual:lego-components';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
/* @import 'https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:wght@400;700&display=swap'; */
|
|
3
|
+
self {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
}
|
|
9
|
+
.tooltipped:hover .tooltip {
|
|
10
|
+
visibility: visible;
|
|
11
|
+
}
|
|
12
|
+
.tooltip {
|
|
13
|
+
visibility: hidden;
|
|
14
|
+
width: 120px;
|
|
15
|
+
background-color: black;
|
|
16
|
+
color: #fff;
|
|
17
|
+
text-align: center;
|
|
18
|
+
border-radius: 5px;
|
|
19
|
+
padding: 5px 0;
|
|
20
|
+
position: absolute;
|
|
21
|
+
z-index: 1;
|
|
22
|
+
bottom: 125%;
|
|
23
|
+
left: 50%;
|
|
24
|
+
margin-left: -60px;
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<div>
|
|
30
|
+
<span b-for="menu in [
|
|
31
|
+
{ icon: 'home', name: 'Home' },
|
|
32
|
+
{ icon: 'mail', name: 'Mail' },
|
|
33
|
+
{ icon: 'calendar', name: 'Calendar' },
|
|
34
|
+
{ icon: 'person', name: 'Profile' },
|
|
35
|
+
]">
|
|
36
|
+
<p>[[ menu.name ]]</p>
|
|
37
|
+
</span>
|
|
38
|
+
</div>
|
|
39
|
+
<p>[[ name ]]</p>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<script>
|
|
43
|
+
export default {
|
|
44
|
+
name: 'side-menu'
|
|
45
|
+
}
|
|
46
|
+
</script>
|