lego-dom 0.0.9 → 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/CHANGELOG.md +44 -0
- package/README.md +1 -0
- package/{go.html → cdn.html} +33 -26
- package/docs/.vitepress/config.js +39 -1
- package/docs/api/directives.md +3 -3
- package/docs/api/index.md +1 -1
- package/docs/contributing/01-welcome.md +36 -0
- package/docs/contributing/02-registry.md +99 -0
- package/docs/contributing/03-batcher.md +110 -0
- package/docs/contributing/04-reactivity.md +87 -0
- package/docs/contributing/05-caching.md +59 -0
- package/docs/contributing/06-init.md +125 -0
- package/docs/contributing/07-observer.md +69 -0
- package/docs/contributing/08-snap.md +126 -0
- package/docs/contributing/09-diffing.md +69 -0
- package/docs/contributing/10-studs.md +76 -0
- package/docs/contributing/11-scanner.md +104 -0
- package/docs/contributing/12-render.md +116 -0
- package/docs/contributing/13-directives.md +225 -0
- package/docs/contributing/14-events.md +57 -0
- package/docs/contributing/15-router.md +9 -0
- package/docs/contributing/16-state.md +48 -0
- package/docs/contributing/17-legodom.md +55 -0
- package/docs/contributing/index.md +5 -0
- package/docs/examples/form.md +1 -1
- package/docs/examples/index.md +1 -1
- package/docs/examples/routing.md +4 -4
- package/docs/examples/todo-app.md +1 -1
- package/docs/guide/cdn-usage.md +8 -0
- package/docs/guide/components.md +33 -15
- package/docs/guide/directives.md +22 -22
- package/docs/guide/getting-started.md +35 -10
- package/docs/guide/index.md +3 -3
- package/docs/guide/quick-start.md +4 -1
- package/docs/guide/reactivity.md +22 -1
- package/docs/guide/routing.md +189 -289
- package/docs/guide/sfc.md +1 -1
- package/docs/guide/templating.md +2 -2
- package/docs/index.md +41 -7
- package/docs/router/basic-routing.md +103 -0
- package/docs/router/cold-entry.md +91 -0
- package/docs/router/history.md +69 -0
- package/docs/router/index.md +73 -0
- package/docs/router/resolver.md +74 -0
- package/docs/router/surgical-swaps.md +134 -0
- package/examples/vite-app/index.html +4 -12
- package/examples/vite-app/package.json +4 -2
- package/examples/vite-app/src/app.css +3 -0
- package/examples/vite-app/src/app.js +29 -0
- package/examples/vite-app/src/components/app-navbar.lego +34 -0
- package/examples/vite-app/src/components/customers/customer-details.lego +24 -0
- package/examples/vite-app/src/components/customers/customer-orders.lego +21 -0
- package/examples/vite-app/src/components/customers/order-list.lego +55 -0
- package/examples/vite-app/src/components/greeting-card.lego +1 -1
- package/examples/vite-app/src/components/sample-component.lego +15 -15
- package/examples/vite-app/src/components/shells/customers-shell.lego +21 -0
- package/examples/vite-app/src/components/todo-list.lego +12 -15
- package/examples/vite-app/src/components/widgets/user-card.lego +27 -0
- package/examples/vite-app/vite.config.js +5 -1
- package/main.js +247 -56
- package/package.json +1 -1
- package/parse-lego.js +17 -8
- package/{main.test.js → tests/main.test.js} +34 -17
- package/tests/parse-lego.test.js +65 -0
- package/vite-plugin.js +60 -22
- package/docs/.vitepress/dist/404.html +0 -22
- package/docs/.vitepress/dist/api/define.html +0 -35
- package/docs/.vitepress/dist/api/directives.html +0 -32
- package/docs/.vitepress/dist/api/globals.html +0 -27
- package/docs/.vitepress/dist/api/index.html +0 -25
- package/docs/.vitepress/dist/api/lifecycle.html +0 -38
- package/docs/.vitepress/dist/api/route.html +0 -34
- package/docs/.vitepress/dist/api/vite-plugin.html +0 -37
- package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.js +0 -11
- package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.js +0 -8
- package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_globals.md.CEznyRAY.js +0 -3
- package/docs/.vitepress/dist/assets/api_globals.md.CEznyRAY.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_index.md.IEYUxUIr.js +0 -1
- package/docs/.vitepress/dist/assets/api_index.md.IEYUxUIr.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.js +0 -14
- package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.js +0 -10
- package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_vite-plugin.md.DC8Li09k.js +0 -13
- package/docs/.vitepress/dist/assets/api_vite-plugin.md.DC8Li09k.lean.js +0 -1
- package/docs/.vitepress/dist/assets/app.BfblNDJy.js +0 -1
- package/docs/.vitepress/dist/assets/chunks/@localSearchIndexroot.Crdp7-Zp.js +0 -1
- package/docs/.vitepress/dist/assets/chunks/VPLocalSearchBox.C18E44rY.js +0 -9
- package/docs/.vitepress/dist/assets/chunks/framework.B7OFBR9X.js +0 -19
- package/docs/.vitepress/dist/assets/chunks/theme.VX3itTW6.js +0 -2
- package/docs/.vitepress/dist/assets/examples_form.md.DQoAgbLR.js +0 -34
- package/docs/.vitepress/dist/assets/examples_form.md.DQoAgbLR.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_index.md.CVJJjXXE.js +0 -28
- package/docs/.vitepress/dist/assets/examples_index.md.CVJJjXXE.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_routing.md.sRnA5RXw.js +0 -338
- package/docs/.vitepress/dist/assets/examples_routing.md.sRnA5RXw.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DPf9Wm99.js +0 -13
- package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DPf9Wm99.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_todo-app.md.CqF4JaWn.js +0 -297
- package/docs/.vitepress/dist/assets/examples_todo-app.md.CqF4JaWn.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CjIjusre.js +0 -182
- package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CjIjusre.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_components.md.CMU3iM6R.js +0 -174
- package/docs/.vitepress/dist/assets/guide_components.md.CMU3iM6R.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_contributing.md.Crrv3T_0.js +0 -1
- package/docs/.vitepress/dist/assets/guide_contributing.md.Crrv3T_0.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_directives.md.DFwqvqOv.js +0 -140
- package/docs/.vitepress/dist/assets/guide_directives.md.DFwqvqOv.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_getting-started.md.DtaJPe0i.js +0 -107
- package/docs/.vitepress/dist/assets/guide_getting-started.md.DtaJPe0i.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_index.md.DtJVpLI9.js +0 -2
- package/docs/.vitepress/dist/assets/guide_index.md.DtJVpLI9.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_lifecycle.md.CfY3jlU1.js +0 -304
- package/docs/.vitepress/dist/assets/guide_lifecycle.md.CfY3jlU1.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_quick-start.md.CwdNNA21.js +0 -33
- package/docs/.vitepress/dist/assets/guide_quick-start.md.CwdNNA21.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_reactivity.md.DgTH0MTn.js +0 -135
- package/docs/.vitepress/dist/assets/guide_reactivity.md.DgTH0MTn.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_routing.md.nMB0QOBR.js +0 -193
- package/docs/.vitepress/dist/assets/guide_routing.md.nMB0QOBR.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_sfc.md.BUkWma1z.js +0 -187
- package/docs/.vitepress/dist/assets/guide_sfc.md.BUkWma1z.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_templating.md.XI3uUlYI.js +0 -119
- package/docs/.vitepress/dist/assets/guide_templating.md.XI3uUlYI.lean.js +0 -1
- package/docs/.vitepress/dist/assets/index.md.M4_o26kF.js +0 -23
- package/docs/.vitepress/dist/assets/index.md.M4_o26kF.lean.js +0 -1
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/.vitepress/dist/assets/style.eycE2Jhw.css +0 -1
- package/docs/.vitepress/dist/examples/form.html +0 -58
- package/docs/.vitepress/dist/examples/index.html +0 -52
- package/docs/.vitepress/dist/examples/routing.html +0 -362
- package/docs/.vitepress/dist/examples/sfc-showcase.html +0 -37
- package/docs/.vitepress/dist/examples/todo-app.html +0 -321
- package/docs/.vitepress/dist/guide/cdn-usage.html +0 -206
- package/docs/.vitepress/dist/guide/components.html +0 -198
- package/docs/.vitepress/dist/guide/contributing.html +0 -25
- package/docs/.vitepress/dist/guide/directives.html +0 -164
- package/docs/.vitepress/dist/guide/getting-started.html +0 -131
- package/docs/.vitepress/dist/guide/index.html +0 -26
- package/docs/.vitepress/dist/guide/lifecycle.html +0 -328
- package/docs/.vitepress/dist/guide/quick-start.html +0 -57
- package/docs/.vitepress/dist/guide/reactivity.html +0 -159
- package/docs/.vitepress/dist/guide/routing.html +0 -217
- package/docs/.vitepress/dist/guide/sfc.html +0 -211
- package/docs/.vitepress/dist/guide/templating.html +0 -143
- package/docs/.vitepress/dist/hashmap.json +0 -1
- package/docs/.vitepress/dist/index.html +0 -47
- package/docs/.vitepress/dist/logo.svg +0 -38
- package/docs/.vitepress/dist/vp-icons.css +0 -1
- package/examples/vite-app/src/main.js +0 -11
- package/examples.js +0 -99
package/docs/guide/routing.md
CHANGED
|
@@ -1,373 +1,273 @@
|
|
|
1
|
-
# Routing
|
|
1
|
+
# Surgical Routing
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
LegoDOM features a powerful "Surgical Router" that sets it apart from typical SPA routers. Instead of just replacing a single `<router-outlet>`, Lego allows you to swap **any** part of your page from **any** link, giving you the feel of a complex SPA with the simplicity of old-school HTML frames.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## The Concept
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
In traditional SPAs, you have one `RouterView` that swaps out entire pages.
|
|
8
|
+
In Lego, every link can be a router trigger, and every element can be a target.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```
|
|
10
|
+
- **`b-target`**: "Where should this content go?"
|
|
11
|
+
- **`b-link`**: "Should this update the URL history?"
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
---
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## 1. Declarative Routing
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
Lego.route('/', 'home-page');
|
|
19
|
-
Lego.route('/about', 'about-page');
|
|
20
|
-
Lego.route('/contact', 'contact-page');
|
|
21
|
-
```
|
|
17
|
+
The most common way to route is using standard `<a>` tags enriched with Lego attributes.
|
|
22
18
|
|
|
23
|
-
###
|
|
19
|
+
### `b-target`
|
|
20
|
+
Specifies the CSS selector of the element to replace.
|
|
24
21
|
|
|
25
22
|
```html
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
</template>
|
|
23
|
+
<!-- Swaps content into <div id="main-content"> -->
|
|
24
|
+
<a href="/profile" b-target="#main-content">Go to Profile</a>
|
|
25
|
+
|
|
26
|
+
<!-- Example of using route params in a template -->
|
|
27
|
+
<main>
|
|
28
|
+
<blog-posts b-show="$route.params.section === 'posts'"></blog-posts>
|
|
29
|
+
<blog-authors b-show="$route.params.section === 'authors'"></blog-authors>
|
|
30
|
+
</main>
|
|
35
31
|
```
|
|
36
32
|
|
|
37
|
-
###
|
|
33
|
+
### `b-link`
|
|
34
|
+
Controls browser history behavior.
|
|
35
|
+
- `b-link` (or just `b-target`): Defaults to `true` (updates URL, pushes history).
|
|
36
|
+
- `b-link="false"`: Does **not** update the URL. Great for tabs, modals, or side-panels.
|
|
38
37
|
|
|
39
38
|
```html
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<a href="/about" b-link>About</a>
|
|
43
|
-
<a href="/contact" b-link>Contact</a>
|
|
44
|
-
</nav>
|
|
39
|
+
<!-- Updates URL to /settings, swaps #main -->
|
|
40
|
+
<a href="/settings" b-target="#main">Settings</a>
|
|
45
41
|
|
|
46
|
-
|
|
42
|
+
<!-- Keeps URL same, just swaps the sidebar context -->
|
|
43
|
+
<a href="/sidebar/tools" b-target="#sidebar" b-link="false">Open Tools</a>
|
|
47
44
|
```
|
|
48
45
|
|
|
49
|
-
|
|
46
|
+
### Deep Linking & Defaults
|
|
47
|
+
If a user refreshes the page, surgical targets (like `#sidebar`) usually won't have content because the `b-target` click never happened.
|
|
50
48
|
|
|
51
|
-
|
|
49
|
+
**The Golden Rule:** Always have a `<lego-router>` as your default "Main" outlet.
|
|
50
|
+
When the page loads, Lego looks for `<lego-router>` to render the URL's matching component.
|
|
52
51
|
|
|
53
52
|
```html
|
|
54
|
-
<!DOCTYPE html>
|
|
55
|
-
<html>
|
|
56
|
-
<head>
|
|
57
|
-
<title>My SPA</title>
|
|
58
|
-
<style>
|
|
59
|
-
nav { padding: 1rem; background: #f0f0f0; }
|
|
60
|
-
nav a { margin-right: 1rem; text-decoration: none; }
|
|
61
|
-
nav a.active { font-weight: bold; }
|
|
62
|
-
</style>
|
|
63
|
-
</head>
|
|
64
53
|
<body>
|
|
65
|
-
<nav>
|
|
66
|
-
<a href="/" b-link>Home</a>
|
|
67
|
-
<a href="/blog" b-link>Blog</a>
|
|
68
|
-
<a href="/about" b-link>About</a>
|
|
69
|
-
</nav>
|
|
54
|
+
<nav>...</nav>
|
|
70
55
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<template b-id="home-page">
|
|
74
|
-
<h1>Welcome Home</h1>
|
|
75
|
-
<p>This is the homepage.</p>
|
|
76
|
-
</template>
|
|
56
|
+
<!-- Default Outlet: Renders /home, /about, etc. -->
|
|
57
|
+
<lego-router id="main-app"></lego-router>
|
|
77
58
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<ul>
|
|
81
|
-
<li><a href="/blog/1" b-link>First Post</a></li>
|
|
82
|
-
<li><a href="/blog/2" b-link>Second Post</a></li>
|
|
83
|
-
</ul>
|
|
84
|
-
</template>
|
|
85
|
-
|
|
86
|
-
<template b-id="about-page">
|
|
87
|
-
<h1>About Us</h1>
|
|
88
|
-
<p>We build awesome things.</p>
|
|
89
|
-
</template>
|
|
90
|
-
|
|
91
|
-
<script src="https://unpkg.com/lego-dom/main.js"></script>
|
|
92
|
-
<script>
|
|
93
|
-
Lego.route('/', 'home-page');
|
|
94
|
-
Lego.route('/blog', 'blog-page');
|
|
95
|
-
Lego.route('/about', 'about-page');
|
|
96
|
-
</script>
|
|
59
|
+
<!-- Surgical Outlet: Only updated when specifically targeted -->
|
|
60
|
+
<aside id="sidebar"></aside>
|
|
97
61
|
</body>
|
|
98
|
-
</html>
|
|
99
62
|
```
|
|
100
63
|
|
|
101
|
-
##
|
|
64
|
+
## 2. The `$go` API
|
|
102
65
|
|
|
103
|
-
|
|
66
|
+
For full programmatic control, use the globally available `$go` helper. It allows for surgical updates from your JavaScript logic.
|
|
104
67
|
|
|
105
|
-
|
|
106
|
-
Lego.
|
|
107
|
-
Lego.route('/blog/:slug', 'blog-post');
|
|
108
|
-
Lego.route('/category/:cat/item/:id', 'product-detail');
|
|
109
|
-
```
|
|
68
|
+
### Syntax
|
|
69
|
+
`Lego.globals.$go(path, ...targets)`
|
|
110
70
|
|
|
111
|
-
|
|
71
|
+
- **path**: The URL to navigate to (e.g., `/user/1`).
|
|
72
|
+
- **targets**: A list of selectors (e.g., `#main`, `#sidebar`). Passing nothing defaults to `lego-router`.
|
|
112
73
|
|
|
113
|
-
|
|
74
|
+
### Methods
|
|
75
|
+
The `$go` function returns an object with HTTP verb methods, primarily only `.get()` is relevant for routing, but others exist for consistency.
|
|
114
76
|
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
<p>User ID: {{ global.params.id }}</p>
|
|
119
|
-
<button @click="loadUser()">Load User</button>
|
|
120
|
-
</template>
|
|
121
|
-
|
|
122
|
-
<script>
|
|
123
|
-
Lego.define('user-profile',
|
|
124
|
-
Lego.registry['user-profile'].innerHTML, {
|
|
125
|
-
async loadUser() {
|
|
126
|
-
const userId = Lego.globals.params.id;
|
|
127
|
-
const user = await fetch(`/api/users/${userId}`).then(r => r.json());
|
|
128
|
-
this.username = user.name;
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
</script>
|
|
132
|
-
```
|
|
77
|
+
```javascript
|
|
78
|
+
// 1. Standard Navigation (pushes to history)
|
|
79
|
+
Lego.globals.$go('/profile').get();
|
|
133
80
|
|
|
134
|
-
|
|
81
|
+
// 2. Surgical Navigation (updates #sidebar, pushes to history)
|
|
82
|
+
Lego.globals.$go('/widgets/clock', '#sidebar').get();
|
|
135
83
|
|
|
136
|
-
|
|
84
|
+
// 3. Silent Update (updates #modal, NO history change)
|
|
85
|
+
// Pass `false` as the first argument to .get()
|
|
86
|
+
Lego.globals.$go('/modals/login', '#modal').get(false);
|
|
87
|
+
```
|
|
137
88
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
89
|
+
### Interactive Example: "The Shell"
|
|
90
|
+
You can update **multiple** targets at once (future feature) or chain them.
|
|
91
|
+
Commonly, you use `$go` inside your component logic:
|
|
142
92
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
93
|
+
```html
|
|
94
|
+
<script>
|
|
95
|
+
export default {
|
|
96
|
+
methods: {
|
|
97
|
+
async loadUser() {
|
|
98
|
+
const userId = Lego.globals.$route.params.id; // Access in JS logic
|
|
99
|
+
const user = await fetch(`/api/users/${userId}`).then(r => r.json());
|
|
100
|
+
this.username = user.name;
|
|
101
|
+
},
|
|
102
|
+
openSettings() {
|
|
103
|
+
// Open settings in the sidebar without losing the main page context
|
|
104
|
+
this.global.$go('/settings-panel', '#sidebar').get(false);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
148
107
|
}
|
|
149
|
-
|
|
108
|
+
</script>
|
|
150
109
|
```
|
|
151
110
|
|
|
152
|
-
|
|
111
|
+
---
|
|
153
112
|
|
|
154
|
-
|
|
113
|
+
## 3. Advanced Patterns
|
|
155
114
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const authMiddleware = (params, globals) => {
|
|
159
|
-
if (!globals.isLoggedIn) {
|
|
160
|
-
history.pushState({}, '', '/login');
|
|
161
|
-
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
162
|
-
return false; // Block navigation
|
|
163
|
-
}
|
|
164
|
-
return true; // Allow navigation
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
// Apply to routes
|
|
168
|
-
Lego.route('/dashboard', 'dashboard-page', authMiddleware);
|
|
169
|
-
Lego.route('/profile', 'profile-page', authMiddleware);
|
|
170
|
-
```
|
|
115
|
+
### The "Sidebar" Pattern
|
|
116
|
+
Keep a persistent Main Content while swapping sidebars.
|
|
171
117
|
|
|
172
|
-
|
|
118
|
+
```html
|
|
119
|
+
<nav>
|
|
120
|
+
<!-- Main Nav: Updates URL and main view -->
|
|
121
|
+
<a href="/dashboard" b-target="#main">Dashboard</a>
|
|
122
|
+
<a href="/files" b-target="#main">Files</a>
|
|
123
|
+
</nav>
|
|
173
124
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
console.log('Navigating to:', params);
|
|
178
|
-
return true;
|
|
179
|
-
};
|
|
125
|
+
<main id="main">
|
|
126
|
+
<!-- Dashboard or Files render here -->
|
|
127
|
+
</main>
|
|
180
128
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
129
|
+
<aside id="context-pane">
|
|
130
|
+
<!-- Context specific tools render here -->
|
|
131
|
+
<template b-id="user-profile">
|
|
132
|
+
<h1>User Profile</h1>
|
|
133
|
+
<p>User ID: {{ $route.params.id }}</p>
|
|
134
|
+
<button @click="loadUser()">Load User</button>
|
|
135
|
+
</template>
|
|
136
|
+
</aside>
|
|
188
137
|
|
|
189
|
-
|
|
190
|
-
Lego.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return true;
|
|
194
|
-
});
|
|
138
|
+
<!-- Inside Dashboard Component -->
|
|
139
|
+
<button onclick="Lego.globals.$go('/tools/chart-config', '#context-pane').get(false)">
|
|
140
|
+
Configure Chart
|
|
141
|
+
</button>
|
|
195
142
|
```
|
|
196
143
|
|
|
197
|
-
|
|
144
|
+
### The "Modal" Pattern
|
|
145
|
+
Render a route into a modal dialog container.
|
|
198
146
|
|
|
199
|
-
|
|
147
|
+
```html
|
|
148
|
+
<dialog id="modal-container"></dialog>
|
|
200
149
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const isActive = link.getAttribute('href') === window.location.pathname;
|
|
206
|
-
link.classList.toggle('active', isActive);
|
|
207
|
-
});
|
|
208
|
-
});
|
|
150
|
+
<a href="/login" b-target="#modal-container"
|
|
151
|
+
onclick="document.getElementById('modal-container').showModal()">
|
|
152
|
+
Login
|
|
153
|
+
</a>
|
|
209
154
|
```
|
|
210
155
|
|
|
211
|
-
|
|
156
|
+
### The "Persistent Layout" Pattern (The Holy Grail)
|
|
157
|
+
This is where LegoDOM outshines traditional routers. You can have static sidebars that **never** reload, while the center content changes dynamically.
|
|
212
158
|
|
|
213
|
-
|
|
159
|
+
```html
|
|
160
|
+
<body>
|
|
161
|
+
<!-- LEFT: Never reloads. Keeps scroll position & expanded folders. -->
|
|
162
|
+
<aside id="static-left">
|
|
163
|
+
<file-tree></file-tree>
|
|
164
|
+
</aside>
|
|
214
165
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
Lego.route('/', 'home-page');
|
|
218
|
-
Lego.route('/about', 'about-page');
|
|
166
|
+
<!-- CENTER: The main router outlet -->
|
|
167
|
+
<lego-router id="main-content"></lego-router>
|
|
219
168
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const routes = ['/', '/about']; // Your known routes
|
|
224
|
-
|
|
225
|
-
if (!routes.includes(path)) {
|
|
226
|
-
// Show 404
|
|
227
|
-
document.querySelector('lego-router').innerHTML = '<not-found></not-found>';
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
window.addEventListener('popstate', matchRoute);
|
|
232
|
-
matchRoute(); // Initial check
|
|
169
|
+
<!-- RIGHT: Context panel for tools/details -->
|
|
170
|
+
<aside id="static-right"></aside>
|
|
171
|
+
</body>
|
|
233
172
|
```
|
|
173
|
+
* **Main Links:** `<a href="/page" b-target="#main-content">`
|
|
174
|
+
* **Tool Links:** `<a href="/tool" b-target="#static-right">`
|
|
234
175
|
|
|
235
|
-
|
|
176
|
+
---
|
|
236
177
|
|
|
237
|
-
|
|
178
|
+
## 4. Deep Routing Strategies
|
|
238
179
|
|
|
239
|
-
|
|
240
|
-
<template b-id="blog-layout">
|
|
241
|
-
<aside>
|
|
242
|
-
<a href="/blog/posts" b-link>Posts</a>
|
|
243
|
-
<a href="/blog/authors" b-link>Authors</a>
|
|
244
|
-
</aside>
|
|
245
|
-
<main>
|
|
246
|
-
<blog-posts b-if="global.params.section === 'posts'"></blog-posts>
|
|
247
|
-
<blog-authors b-if="global.params.section === 'authors'"></blog-authors>
|
|
248
|
-
</main>
|
|
249
|
-
</template>
|
|
250
|
-
```
|
|
180
|
+
When handling deep routes like `/customers/:id/orders/:orderId`, you have two architectural choices.
|
|
251
181
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
```
|
|
182
|
+
### Option A: The Shell Strategy (Self-Healing)
|
|
183
|
+
Map everything to a single "Shell" component. The Shell determines what to show in its sub-outlets based on the URL params.
|
|
255
184
|
|
|
256
|
-
|
|
185
|
+
* **Pros:** Highly surgical. The Shell never re-renders, only its children do.
|
|
186
|
+
* **Cons:** Requires logic in `mounted()` to "heal" the state on page load.
|
|
257
187
|
|
|
258
|
-
|
|
188
|
+
```javascript
|
|
189
|
+
// Route Configuration
|
|
190
|
+
Lego.route('/customers/:id', 'customers-shell');
|
|
191
|
+
Lego.route('/customers/:id/orders/:orderId', 'customers-shell');
|
|
259
192
|
|
|
260
|
-
|
|
261
|
-
{
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
this.searchQuery = params.get('q') || '';
|
|
265
|
-
this.page = parseInt(params.get('page')) || 1;
|
|
193
|
+
// Component Logic (Self-Healing)
|
|
194
|
+
mounted() {
|
|
195
|
+
if (this.$route.params.orderId) {
|
|
196
|
+
this.$go(window.location.pathname, '#details-pane').get();
|
|
266
197
|
}
|
|
267
198
|
}
|
|
268
199
|
```
|
|
269
200
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
## Hash vs History Mode
|
|
201
|
+
### Option B: The Page Strategy (Component Nesting)
|
|
202
|
+
Map deep routes to specific "Page" components. Each page imports and wraps itself in a shared Layout.
|
|
273
203
|
|
|
274
|
-
|
|
204
|
+
* **Pros:** Simpler logic. No "healing" code required.
|
|
205
|
+
* **Cons:** The Layout is technically re-created on every route change (though diffing makes it cheap).
|
|
275
206
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
Not hash-based:
|
|
281
|
-
❌ `#/about`
|
|
282
|
-
❌ `#/user/123`
|
|
283
|
-
|
|
284
|
-
### Server Configuration
|
|
285
|
-
|
|
286
|
-
For clean URLs to work, configure your server to serve `index.html` for all routes:
|
|
287
|
-
|
|
288
|
-
**nginx:**
|
|
289
|
-
```nginx
|
|
290
|
-
location / {
|
|
291
|
-
try_files $uri $uri/ /index.html;
|
|
292
|
-
}
|
|
207
|
+
```javascript
|
|
208
|
+
// Route Configuration
|
|
209
|
+
Lego.route('/customers/:id/orders/:orderId', 'order-details-page');
|
|
293
210
|
```
|
|
294
211
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
212
|
+
```html
|
|
213
|
+
<!-- order-details-page.lego -->
|
|
214
|
+
<template>
|
|
215
|
+
<customers-layout>
|
|
216
|
+
<order-info id="{{ $route.params.orderId }}"></order-info>
|
|
217
|
+
</customers-layout>
|
|
218
|
+
</template>
|
|
301
219
|
```
|
|
302
220
|
|
|
303
|
-
|
|
304
|
-
```js
|
|
305
|
-
app.get('*', (req, res) => {
|
|
306
|
-
res.sendFile(path.join(__dirname, 'index.html'));
|
|
307
|
-
});
|
|
308
|
-
```
|
|
221
|
+
---
|
|
309
222
|
|
|
310
|
-
##
|
|
223
|
+
## 5. Middleware & Guards
|
|
311
224
|
|
|
312
|
-
|
|
225
|
+
Middleware runs **before** the surgical swap happens. It### Accessing Parameters
|
|
226
|
+
Route parameters are available directly via `$route.params` in templates.
|
|
313
227
|
|
|
314
|
-
|
|
315
|
-
// routes.js
|
|
316
|
-
const routes = {
|
|
317
|
-
'/': 'home-page',
|
|
318
|
-
'/blog': 'blog-page',
|
|
319
|
-
'/blog/:slug': 'blog-post',
|
|
320
|
-
'/user/:id': 'user-profile',
|
|
321
|
-
'/about': 'about-page'
|
|
322
|
-
};
|
|
228
|
+
> **Note:** `$route` is a global helper available in all templates.
|
|
323
229
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
230
|
+
```javascript
|
|
231
|
+
/*
|
|
232
|
+
* Middleware Signature:
|
|
233
|
+
* (params: Object, globals: Object) => boolean | Promise<boolean>
|
|
234
|
+
* Return `true` to allow navigation, `false` to block.
|
|
235
|
+
*/
|
|
328
236
|
|
|
329
|
-
|
|
237
|
+
// Example: Auth Guard
|
|
238
|
+
const requireAuth = (params, globals) => {
|
|
239
|
+
if (!globals.user) {
|
|
240
|
+
// Redirect to login using surgical routing!
|
|
241
|
+
globals.$go('/login', '#main').get();
|
|
242
|
+
return false; // Stop original navigation
|
|
243
|
+
}
|
|
244
|
+
return true;
|
|
245
|
+
};
|
|
330
246
|
|
|
331
|
-
|
|
332
|
-
<template b-id="user-profile">
|
|
333
|
-
<div b-if="loading">Loading...</div>
|
|
334
|
-
<div b-if="!loading">
|
|
335
|
-
<h1>{{ user.name }}</h1>
|
|
336
|
-
<p>{{ user.bio }}</p>
|
|
337
|
-
</div>
|
|
338
|
-
</template>
|
|
247
|
+
Lego.route('/admin', 'admin-panel', requireAuth);
|
|
339
248
|
```
|
|
340
249
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
```js
|
|
344
|
-
{
|
|
345
|
-
async loadData() {
|
|
346
|
-
this.loading = true;
|
|
347
|
-
this.error = null;
|
|
348
|
-
try {
|
|
349
|
-
const data = await fetch('/api/data').then(r => r.json());
|
|
350
|
-
this.data = data;
|
|
351
|
-
} catch (err) {
|
|
352
|
-
this.error = 'Failed to load data';
|
|
353
|
-
} finally {
|
|
354
|
-
this.loading = false;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
```
|
|
250
|
+
## 5. Smart History
|
|
359
251
|
|
|
360
|
-
|
|
252
|
+
Lego's router is "History Aware".
|
|
253
|
+
When you use `b-target`, Lego stores the target selectors in the browser's History State.
|
|
361
254
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
255
|
+
**What this means:**
|
|
256
|
+
1. You click "Open Sidebar" (Surgical update to `#sidebar`).
|
|
257
|
+
2. You click "Home" (Main update to `#main`).
|
|
258
|
+
3. You click **Back**.
|
|
259
|
+
4. Lego automatically knows to reverse the "Home" navigation.
|
|
260
|
+
5. You click **Back** again.
|
|
261
|
+
6. Lego knows the previous state was a surgical update to `#sidebar` and restores it correctly!
|
|
366
262
|
|
|
367
|
-
|
|
263
|
+
## Summary Table
|
|
368
264
|
|
|
369
|
-
|
|
265
|
+
| Feature | Code | Description |
|
|
266
|
+
| :--- | :--- | :--- |
|
|
267
|
+
| **Standard Link** | `<a href="/x">` | Standard browser navigation (full reload). |
|
|
268
|
+
| **SPA Link** | `<a href="/x" b-target>` | Default SPA nav. Swaps `<lego-router>`. |
|
|
269
|
+
| **Surgical Link** | `<a href="/x" b-target="#id">` | Swaps content of `#id`. Updates URL. |
|
|
270
|
+
| **Silent Link** | `... b-link="false">` | Swaps content. **No** URL update. |
|
|
271
|
+
| **JS Nav** | `$go('/x').get()` | Programmatic navigation. |
|
|
272
|
+
| **Silent JS** | `$go('/x').get(false)` | Programmatic silent swap. |
|
|
370
273
|
|
|
371
|
-
- See [routing examples](/examples/routing)
|
|
372
|
-
- Learn about [lifecycle hooks](/guide/lifecycle)
|
|
373
|
-
- Explore [state management patterns](/guide/reactivity)
|
package/docs/guide/sfc.md
CHANGED
|
@@ -215,7 +215,7 @@ Contains your component's HTML markup with Lego directives:
|
|
|
215
215
|
```html
|
|
216
216
|
<template>
|
|
217
217
|
<h1>{{ title }}</h1>
|
|
218
|
-
<p b-
|
|
218
|
+
<p b-show="showContent">{{ content }}</p>
|
|
219
219
|
<ul>
|
|
220
220
|
<li b-for="item in items">{{ item }}</li>
|
|
221
221
|
</ul>
|
package/docs/guide/templating.md
CHANGED
|
@@ -343,8 +343,8 @@ If a calculation is expensive, cache it:
|
|
|
343
343
|
### Show/Hide Based on Condition
|
|
344
344
|
|
|
345
345
|
```html
|
|
346
|
-
<p b-
|
|
347
|
-
<p b-
|
|
346
|
+
<p b-show="user">Welcome, {{ user.name }}!</p>
|
|
347
|
+
<p b-show="!user">Please log in</p>
|
|
348
348
|
```
|
|
349
349
|
|
|
350
350
|
### List with Index
|
package/docs/index.md
CHANGED
|
@@ -57,11 +57,35 @@ features:
|
|
|
57
57
|
details: Battle-tested patterns from Vue and React, adapted for pure Web Components. No framework lock-in.
|
|
58
58
|
---
|
|
59
59
|
|
|
60
|
-
##
|
|
60
|
+
## Components & Naming
|
|
61
|
+
|
|
62
|
+
How you name your components depends on how you use Lego:
|
|
63
|
+
|
|
64
|
+
### 1. Vite / Build Tools (Recommended)
|
|
65
|
+
**Convention over Configuration.** The filename *is* the tag name.
|
|
66
|
+
- `user-card.lego` → `<user-card></user-card>`
|
|
67
|
+
- `app-nav.lego` → `<app-nav></app-nav>`
|
|
68
|
+
|
|
69
|
+
You do not need `b-id` inside `.lego` files; the build system handles registration automatically.
|
|
70
|
+
|
|
71
|
+
### 2. CDN / Script Tags
|
|
72
|
+
Since there are no files, you must explicitly name your components using the `b-id` attribute on the `<template>` tag.
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<!-- Only needed for CDN usage -->
|
|
76
|
+
<template b-id="user-profile">
|
|
77
|
+
<h1>User Profile</h1>
|
|
78
|
+
</template>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Quick Start (CDN)
|
|
61
82
|
|
|
62
83
|
```html
|
|
63
84
|
<!-- Define a component -->
|
|
64
|
-
<template b-id="counter-button"
|
|
85
|
+
<template b-id="counter-button" b-data="{
|
|
86
|
+
title: 'My counter',
|
|
87
|
+
count: 0
|
|
88
|
+
}">
|
|
65
89
|
<style>
|
|
66
90
|
self {
|
|
67
91
|
display: block;
|
|
@@ -80,13 +104,25 @@ features:
|
|
|
80
104
|
</template>
|
|
81
105
|
|
|
82
106
|
<!-- Use it -->
|
|
83
|
-
<counter-button b-data="{ title: '
|
|
107
|
+
<counter-button b-data="{ title: 'Override b-data title' }"></counter-button>
|
|
84
108
|
|
|
85
109
|
<script src="https://unpkg.com/lego-dom/main.js"></script>
|
|
110
|
+
<script>
|
|
111
|
+
// Complete the initialization
|
|
112
|
+
Lego.init();
|
|
113
|
+
</script>
|
|
86
114
|
```
|
|
87
115
|
|
|
88
116
|
That's it. No build step, no npm, no configuration.
|
|
89
117
|
|
|
118
|
+
> [!IMPORTANT]
|
|
119
|
+
> **Why call `Lego.init()`?**
|
|
120
|
+
> While `Lego.define()` will "snap" your components into the page immediately, you must call `Lego.init()` to start the background engine. Without it:
|
|
121
|
+
> - **Reactivity** to data changes won't work.
|
|
122
|
+
> - **Mustaches** (<code v-pre>{{...}}</code>) outside of components won't hydrate.
|
|
123
|
+
> - **Single Page Routing** won't be activated.
|
|
124
|
+
> - **New components** added to the DOM dynamically won't be auto-initialized.
|
|
125
|
+
|
|
90
126
|
## Why Lego?
|
|
91
127
|
|
|
92
128
|
**For small projects**, you get reactive components without the overhead of a full framework.
|
|
@@ -119,8 +155,6 @@ Lego works in all modern browsers that support:
|
|
|
119
155
|
This includes Chrome 63+, Firefox 63+, Safari 11.1+, and Edge 79+.
|
|
120
156
|
|
|
121
157
|
## Community
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
- 💬 [Discussions](https://github.com/rayattack/Lego/discussions)
|
|
125
|
-
- 🐛 [Issue Tracker](https://github.com/rayattack/Lego/issues)
|
|
158
|
+
- 💬 [Discussions](https://github.com/rayattack/LegoDOM/discussions)
|
|
159
|
+
- 🐛 [Issue Tracker](https://github.com/rayattack/LegoDOM/issues)
|
|
126
160
|
- 📦 [npm Package](https://www.npmjs.com/package/lego-dom)
|