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,279 @@
|
|
|
1
|
+
# Step 3: Adding Routes
|
|
2
|
+
|
|
3
|
+
Now let's make your app navigable. By the end of this page, you'll understand exactly **where routes go**, **how to define them**, and **how to navigate between pages**.
|
|
4
|
+
|
|
5
|
+
## The Golden Question: Where Do Routes Go?
|
|
6
|
+
|
|
7
|
+
**Answer: In your entry file (`app.js`), before `Lego.init()`.**
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
// src/app.js
|
|
11
|
+
|
|
12
|
+
import { Lego } from 'lego-dom';
|
|
13
|
+
import registerComponents from 'virtual:lego-components';
|
|
14
|
+
|
|
15
|
+
// 1. Register components
|
|
16
|
+
registerComponents();
|
|
17
|
+
|
|
18
|
+
// 2. Define ALL your routes here ⭐
|
|
19
|
+
Lego.route('/', 'home-page');
|
|
20
|
+
Lego.route('/login', 'login-page');
|
|
21
|
+
Lego.route('/welcome', 'welcome-page');
|
|
22
|
+
Lego.route('/users/:id', 'user-profile'); // Dynamic route
|
|
23
|
+
|
|
24
|
+
// 3. Initialize (must come AFTER routes)
|
|
25
|
+
await Lego.init();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
::: tip Why Before init()?
|
|
29
|
+
`Lego.init()` starts the router. If routes aren't defined yet, the router has nothing to match. Always define routes **before** calling `init()`.
|
|
30
|
+
:::
|
|
31
|
+
|
|
32
|
+
## Route Syntax
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
Lego.route(path, componentName, middleware?)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
| Argument | Description | Example |
|
|
39
|
+
|----------|-------------|---------|
|
|
40
|
+
| `path` | URL pattern | `'/'`, `'/login'`, `'/users/:id'` |
|
|
41
|
+
| `componentName` | The component to render (filename minus `.lego`) | `'home-page'`, `'login-page'` |
|
|
42
|
+
| `middleware` | Optional guard function | `(params, globals) => boolean` |
|
|
43
|
+
|
|
44
|
+
### Dynamic Routes
|
|
45
|
+
|
|
46
|
+
Use `:param` for URL parameters:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
Lego.route('/users/:id', 'user-profile');
|
|
50
|
+
Lego.route('/posts/:postId/comments/:commentId', 'comment-view');
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Access them in your component:
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
// In your .lego file's <script>
|
|
57
|
+
mounted() {
|
|
58
|
+
const userId = this.$route.params.id;
|
|
59
|
+
console.log('User ID:', userId);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Navigating Between Pages
|
|
64
|
+
|
|
65
|
+
### Method 1: The `b-link` Attribute (Declarative)
|
|
66
|
+
|
|
67
|
+
```html
|
|
68
|
+
<!-- In any component template -->
|
|
69
|
+
<a href="/login" b-link>Go to Login</a>
|
|
70
|
+
<a href="/users/42" b-link>View User 42</a>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The `b-link` attribute turns a regular `<a>` tag into a SPA link. No page reload!
|
|
74
|
+
|
|
75
|
+
### Method 2: The `$go()` Helper (Programmatic)
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
// In your component methods
|
|
79
|
+
handleLoginClick() {
|
|
80
|
+
this.$go('/login').get();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Or with the global reference
|
|
84
|
+
Lego.globals.$go('/welcome').get();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Method 3: Using `b-target` (Surgical Updates)
|
|
88
|
+
|
|
89
|
+
Want to update only a specific part of the page?
|
|
90
|
+
|
|
91
|
+
```html
|
|
92
|
+
<!-- Update just the sidebar, not the whole page -->
|
|
93
|
+
<a href="/sidebar/settings" b-target="#sidebar" b-link="false">Settings</a>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Create a Login Page
|
|
97
|
+
|
|
98
|
+
Let's add a login page to navigate to.
|
|
99
|
+
|
|
100
|
+
Create `src/components/login-page.lego`:
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<template>
|
|
104
|
+
<div class="login-container">
|
|
105
|
+
<h1>Login</h1>
|
|
106
|
+
<form @submit="handleSubmit(event)">
|
|
107
|
+
<div class="form-group">
|
|
108
|
+
<label>Email</label>
|
|
109
|
+
<input type="email" b-sync="email" placeholder="you@example.com" required>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="form-group">
|
|
112
|
+
<label>Password</label>
|
|
113
|
+
<input type="password" b-sync="password" placeholder="••••••••" required>
|
|
114
|
+
</div>
|
|
115
|
+
<button type="submit">Sign In</button>
|
|
116
|
+
</form>
|
|
117
|
+
<p class="back-link">
|
|
118
|
+
<a href="/" b-link>← Back to Home</a>
|
|
119
|
+
</p>
|
|
120
|
+
</div>
|
|
121
|
+
</template>
|
|
122
|
+
|
|
123
|
+
<style>
|
|
124
|
+
self {
|
|
125
|
+
display: flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
justify-content: center;
|
|
128
|
+
min-height: 100vh;
|
|
129
|
+
background: #f5f5f5;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.login-container {
|
|
133
|
+
background: white;
|
|
134
|
+
padding: 3rem;
|
|
135
|
+
border-radius: 16px;
|
|
136
|
+
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
|
|
137
|
+
width: 100%;
|
|
138
|
+
max-width: 400px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
h1 {
|
|
142
|
+
text-align: center;
|
|
143
|
+
margin-bottom: 2rem;
|
|
144
|
+
color: #333;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.form-group {
|
|
148
|
+
margin-bottom: 1.5rem;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
label {
|
|
152
|
+
display: block;
|
|
153
|
+
margin-bottom: 0.5rem;
|
|
154
|
+
font-weight: 600;
|
|
155
|
+
color: #555;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
input {
|
|
159
|
+
width: 100%;
|
|
160
|
+
padding: 0.75rem 1rem;
|
|
161
|
+
border: 2px solid #e0e0e0;
|
|
162
|
+
border-radius: 8px;
|
|
163
|
+
font-size: 1rem;
|
|
164
|
+
transition: border-color 0.2s;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
input:focus {
|
|
168
|
+
outline: none;
|
|
169
|
+
border-color: #667eea;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
button {
|
|
173
|
+
width: 100%;
|
|
174
|
+
padding: 1rem;
|
|
175
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
176
|
+
color: white;
|
|
177
|
+
border: none;
|
|
178
|
+
border-radius: 8px;
|
|
179
|
+
font-size: 1rem;
|
|
180
|
+
font-weight: 600;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
transition: transform 0.2s;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
button:hover {
|
|
186
|
+
transform: translateY(-2px);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.back-link {
|
|
190
|
+
text-align: center;
|
|
191
|
+
margin-top: 1.5rem;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.back-link a {
|
|
195
|
+
color: #667eea;
|
|
196
|
+
text-decoration: none;
|
|
197
|
+
}
|
|
198
|
+
</style>
|
|
199
|
+
|
|
200
|
+
<script>
|
|
201
|
+
export default {
|
|
202
|
+
email: '',
|
|
203
|
+
password: '',
|
|
204
|
+
|
|
205
|
+
handleSubmit(event) {
|
|
206
|
+
event.preventDefault();
|
|
207
|
+
console.log('Login attempt:', this.email);
|
|
208
|
+
|
|
209
|
+
// Navigate to welcome page
|
|
210
|
+
this.$go('/welcome').get();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
</script>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Update Your Routes
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
// src/app.js
|
|
220
|
+
import { Lego } from 'lego-dom';
|
|
221
|
+
import registerComponents from 'virtual:lego-components';
|
|
222
|
+
|
|
223
|
+
registerComponents();
|
|
224
|
+
|
|
225
|
+
Lego.route('/', 'home-page');
|
|
226
|
+
Lego.route('/login', 'login-page'); // Add this!
|
|
227
|
+
|
|
228
|
+
await Lego.init();
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Wire Up Navigation from Home
|
|
232
|
+
|
|
233
|
+
Update `home-page.lego`'s button to navigate:
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
// In the <script> section
|
|
237
|
+
handleClick() {
|
|
238
|
+
this.$go('/login').get();
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Test It!
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
npm run dev
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
1. Open `http://localhost:5173` – see your home page
|
|
249
|
+
2. Click "Get Started" – navigates to `/login`
|
|
250
|
+
3. Click "← Back to Home" – navigates back to `/`
|
|
251
|
+
4. Use browser back/forward – it just works!
|
|
252
|
+
|
|
253
|
+
## What You've Learned
|
|
254
|
+
|
|
255
|
+
✅ Routes go in `app.js`, before `Lego.init()`
|
|
256
|
+
✅ `Lego.route(path, component)` maps URLs to components
|
|
257
|
+
✅ `b-link` on `<a>` tags for declarative navigation
|
|
258
|
+
✅ `$go(path).get()` for programmatic navigation
|
|
259
|
+
✅ Dynamic routes with `:param` syntax
|
|
260
|
+
|
|
261
|
+
## Quick Reference
|
|
262
|
+
|
|
263
|
+
| I want to... | Use this |
|
|
264
|
+
|--------------|----------|
|
|
265
|
+
| Define a route | `Lego.route('/path', 'component-name')` |
|
|
266
|
+
| Navigate via link | `<a href="/path" b-link>Click</a>` |
|
|
267
|
+
| Navigate via JS | `this.$go('/path').get()` |
|
|
268
|
+
| Get route params | `this.$route.params.id` |
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
<div style="display: flex; justify-content: space-between; margin-top: 3rem;">
|
|
273
|
+
<a href="./02-your-first-component" style="display: inline-block; background: #eee; color: #333; padding: 0.75rem 1.5rem; border-radius: 6px; text-decoration: none; font-weight: 600;">
|
|
274
|
+
← Previous: Your First Component
|
|
275
|
+
</a>
|
|
276
|
+
<a href="./04-multi-page-app" style="display: inline-block; background: #4CAF50; color: white; padding: 0.75rem 1.5rem; border-radius: 6px; text-decoration: none; font-weight: 600;">
|
|
277
|
+
Next: Multi-Page App →
|
|
278
|
+
</a>
|
|
279
|
+
</div>
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# Step 4: Building a Multi-Page App
|
|
2
|
+
|
|
3
|
+
Let's bring it all together! In this step, we'll complete the **index → login → welcome** flow that every real app needs. You'll see exactly how data flows between pages.
|
|
4
|
+
|
|
5
|
+
## The Complete App Structure
|
|
6
|
+
|
|
7
|
+
By the end of this page, you'll have:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
src/components/
|
|
11
|
+
├── home-page.lego ← Landing page (/)
|
|
12
|
+
├── login-page.lego ← Login form (/login)
|
|
13
|
+
└── welcome-page.lego ← Dashboard (/welcome)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Step 1: Create the Welcome Page
|
|
17
|
+
|
|
18
|
+
Create `src/components/welcome-page.lego`:
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<template>
|
|
22
|
+
<div class="dashboard">
|
|
23
|
+
<header>
|
|
24
|
+
<h1>Welcome, [[ username ]]! 🎉</h1>
|
|
25
|
+
<button @click="handleLogout()">Logout</button>
|
|
26
|
+
</header>
|
|
27
|
+
|
|
28
|
+
<main>
|
|
29
|
+
<div class="card">
|
|
30
|
+
<h2>You're In!</h2>
|
|
31
|
+
<p>You've successfully navigated through a complete authentication flow built with LegoDOM.</p>
|
|
32
|
+
<p>This page received your username from the login form using global state.</p>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div class="stats">
|
|
36
|
+
<div class="stat">
|
|
37
|
+
<span class="value">[[ visitCount ]]</span>
|
|
38
|
+
<span class="label">Page Views</span>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="stat">
|
|
41
|
+
<span class="value">[[ formatTime() ]]</span>
|
|
42
|
+
<span class="label">Current Time</span>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="stat">
|
|
45
|
+
<span class="value">✓</span>
|
|
46
|
+
<span class="label">Logged In</span>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="actions">
|
|
51
|
+
<a href="/" b-link class="btn">← Back to Home</a>
|
|
52
|
+
</div>
|
|
53
|
+
</main>
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<style>
|
|
58
|
+
self {
|
|
59
|
+
display: block;
|
|
60
|
+
min-height: 100vh;
|
|
61
|
+
background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%);
|
|
62
|
+
color: white;
|
|
63
|
+
padding: 2rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
header {
|
|
67
|
+
display: flex;
|
|
68
|
+
justify-content: space-between;
|
|
69
|
+
align-items: center;
|
|
70
|
+
max-width: 800px;
|
|
71
|
+
margin: 0 auto 3rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
header h1 {
|
|
75
|
+
font-size: 1.75rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
header button {
|
|
79
|
+
background: rgba(255,255,255,0.1);
|
|
80
|
+
color: white;
|
|
81
|
+
border: 1px solid rgba(255,255,255,0.2);
|
|
82
|
+
padding: 0.5rem 1rem;
|
|
83
|
+
border-radius: 6px;
|
|
84
|
+
cursor: pointer;
|
|
85
|
+
transition: background 0.2s;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
header button:hover {
|
|
89
|
+
background: rgba(255,255,255,0.2);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
main {
|
|
93
|
+
max-width: 800px;
|
|
94
|
+
margin: 0 auto;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.card {
|
|
98
|
+
background: rgba(255,255,255,0.05);
|
|
99
|
+
padding: 2rem;
|
|
100
|
+
border-radius: 16px;
|
|
101
|
+
margin-bottom: 2rem;
|
|
102
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.card h2 {
|
|
106
|
+
color: #4ecca3;
|
|
107
|
+
margin-bottom: 1rem;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.card p {
|
|
111
|
+
opacity: 0.8;
|
|
112
|
+
line-height: 1.6;
|
|
113
|
+
margin-bottom: 0.5rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.stats {
|
|
117
|
+
display: grid;
|
|
118
|
+
grid-template-columns: repeat(3, 1fr);
|
|
119
|
+
gap: 1rem;
|
|
120
|
+
margin-bottom: 2rem;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.stat {
|
|
124
|
+
background: rgba(255,255,255,0.05);
|
|
125
|
+
padding: 1.5rem;
|
|
126
|
+
border-radius: 12px;
|
|
127
|
+
text-align: center;
|
|
128
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.stat .value {
|
|
132
|
+
display: block;
|
|
133
|
+
font-size: 2rem;
|
|
134
|
+
font-weight: bold;
|
|
135
|
+
color: #4ecca3;
|
|
136
|
+
margin-bottom: 0.5rem;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.stat .label {
|
|
140
|
+
font-size: 0.85rem;
|
|
141
|
+
opacity: 0.7;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.actions {
|
|
145
|
+
text-align: center;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.btn {
|
|
149
|
+
display: inline-block;
|
|
150
|
+
padding: 0.75rem 1.5rem;
|
|
151
|
+
background: #4ecca3;
|
|
152
|
+
color: #1a1a2e;
|
|
153
|
+
text-decoration: none;
|
|
154
|
+
border-radius: 8px;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
}
|
|
157
|
+
</style>
|
|
158
|
+
|
|
159
|
+
<script>
|
|
160
|
+
export default {
|
|
161
|
+
visitCount: 0,
|
|
162
|
+
|
|
163
|
+
get username() {
|
|
164
|
+
// Get username from global state (set by login page)
|
|
165
|
+
return Lego.globals.user?.name || 'Guest';
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
mounted() {
|
|
169
|
+
this.visitCount++;
|
|
170
|
+
|
|
171
|
+
// Redirect to login if not authenticated
|
|
172
|
+
if (!Lego.globals.user) {
|
|
173
|
+
console.log('Not logged in, redirecting...');
|
|
174
|
+
this.$go('/login').get();
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
formatTime() {
|
|
179
|
+
return new Date().toLocaleTimeString();
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
handleLogout() {
|
|
183
|
+
// Clear global user state
|
|
184
|
+
Lego.globals.user = null;
|
|
185
|
+
this.$go('/').get();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
</script>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Step 2: Update Login to Set Global State
|
|
192
|
+
|
|
193
|
+
The login page needs to store the user in global state. Update `src/components/login-page.lego`'s script section:
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
<script>
|
|
197
|
+
export default {
|
|
198
|
+
email: '',
|
|
199
|
+
password: '',
|
|
200
|
+
|
|
201
|
+
handleSubmit(event) {
|
|
202
|
+
event.preventDefault();
|
|
203
|
+
|
|
204
|
+
// Store user in GLOBAL state (accessible from any component!)
|
|
205
|
+
Lego.globals.user = {
|
|
206
|
+
name: this.email.split('@')[0], // Use email prefix as name
|
|
207
|
+
email: this.email,
|
|
208
|
+
loggedInAt: new Date()
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
console.log('User logged in:', Lego.globals.user);
|
|
212
|
+
|
|
213
|
+
// Navigate to welcome page
|
|
214
|
+
this.$go('/welcome').get();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
</script>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Step 3: Update Home Page Navigation
|
|
221
|
+
|
|
222
|
+
Update `src/components/home-page.lego`'s `handleClick` method:
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
handleClick() {
|
|
226
|
+
this.$go('/login').get();
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Step 4: Complete `app.js`
|
|
231
|
+
|
|
232
|
+
Here's the final `src/app.js`:
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
import { Lego } from 'lego-dom';
|
|
236
|
+
import registerComponents from 'virtual:lego-components';
|
|
237
|
+
|
|
238
|
+
// Register all .lego components
|
|
239
|
+
registerComponents();
|
|
240
|
+
|
|
241
|
+
// Define routes
|
|
242
|
+
Lego.route('/', 'home-page');
|
|
243
|
+
Lego.route('/login', 'login-page');
|
|
244
|
+
Lego.route('/welcome', 'welcome-page');
|
|
245
|
+
|
|
246
|
+
// Initialize with default user state
|
|
247
|
+
Lego.globals.user = null;
|
|
248
|
+
|
|
249
|
+
// Start the engine
|
|
250
|
+
await Lego.init();
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Test the Complete Flow
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
npm run dev
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
1. **Home Page** (`/`) – Click "Get Started"
|
|
260
|
+
2. **Login Page** (`/login`) – Enter an email, click "Sign In"
|
|
261
|
+
3. **Welcome Page** (`/welcome`) – See your username displayed!
|
|
262
|
+
4. Click "Logout" – Clears state, returns to home
|
|
263
|
+
5. Try navigating directly to `/welcome` – Redirects to login!
|
|
264
|
+
|
|
265
|
+
## The Data Flow
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
269
|
+
│ Lego.globals │
|
|
270
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
271
|
+
│ │ user: { name: 'john', email: 'john@test.com' } │ │
|
|
272
|
+
│ └─────────────────────────────────────────────────────┘ │
|
|
273
|
+
└─────────────────────────────────────────────────────────────┘
|
|
274
|
+
▲ │
|
|
275
|
+
│ writes │ reads
|
|
276
|
+
│ ▼
|
|
277
|
+
┌──────────┐ ┌──────────────┐
|
|
278
|
+
│ login │ │ welcome │
|
|
279
|
+
│ page │ │ page │
|
|
280
|
+
└──────────┘ └──────────────┘
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Auth Guard Pattern
|
|
284
|
+
|
|
285
|
+
Want to protect routes? Add middleware:
|
|
286
|
+
|
|
287
|
+
```javascript
|
|
288
|
+
// In app.js
|
|
289
|
+
|
|
290
|
+
const requireAuth = (params, globals) => {
|
|
291
|
+
if (!globals.user) {
|
|
292
|
+
globals.$go('/login').get();
|
|
293
|
+
return false; // Block navigation
|
|
294
|
+
}
|
|
295
|
+
return true; // Allow navigation
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
Lego.route('/welcome', 'welcome-page', requireAuth);
|
|
299
|
+
Lego.route('/dashboard', 'dashboard-page', requireAuth);
|
|
300
|
+
Lego.route('/settings', 'settings-page', requireAuth);
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## What You've Built
|
|
304
|
+
|
|
305
|
+
✅ A complete login flow: home → login → welcome
|
|
306
|
+
✅ Global state shared across components
|
|
307
|
+
✅ Logout functionality that clears state
|
|
308
|
+
✅ Automatic redirect when not authenticated
|
|
309
|
+
✅ Route protection with middleware
|
|
310
|
+
|
|
311
|
+
## The Full Picture
|
|
312
|
+
|
|
313
|
+
| File | Purpose |
|
|
314
|
+
|------|---------|
|
|
315
|
+
| `index.html` | Entry HTML with `<lego-router>` |
|
|
316
|
+
| `src/app.js` | Routes, globals, initialization |
|
|
317
|
+
| `src/components/*.lego` | Your page components |
|
|
318
|
+
| `vite.config.js` | Vite + LegoDOM plugin setup |
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
<div style="display: flex; justify-content: space-between; margin-top: 3rem;">
|
|
323
|
+
<a href="./03-adding-routes" style="display: inline-block; background: #eee; color: #333; padding: 0.75rem 1.5rem; border-radius: 6px; text-decoration: none; font-weight: 600;">
|
|
324
|
+
← Previous: Adding Routes
|
|
325
|
+
</a>
|
|
326
|
+
<a href="./05-state-and-globals" style="display: inline-block; background: #4CAF50; color: white; padding: 0.75rem 1.5rem; border-radius: 6px; text-decoration: none; font-weight: 600;">
|
|
327
|
+
Next: State & Globals →
|
|
328
|
+
</a>
|
|
329
|
+
</div>
|