lego-dom 0.0.8 → 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.
Files changed (175) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +49 -432
  3. package/cdn.html +124 -0
  4. package/docs/.vitepress/config.js +43 -5
  5. package/docs/api/directives.md +3 -3
  6. package/docs/api/globals.md +1 -1
  7. package/docs/api/index.md +3 -3
  8. package/docs/api/vite-plugin.md +1 -1
  9. package/docs/contributing/01-welcome.md +36 -0
  10. package/docs/contributing/02-registry.md +99 -0
  11. package/docs/contributing/03-batcher.md +110 -0
  12. package/docs/contributing/04-reactivity.md +87 -0
  13. package/docs/contributing/05-caching.md +59 -0
  14. package/docs/contributing/06-init.md +125 -0
  15. package/docs/contributing/07-observer.md +69 -0
  16. package/docs/contributing/08-snap.md +126 -0
  17. package/docs/contributing/09-diffing.md +69 -0
  18. package/docs/contributing/10-studs.md +76 -0
  19. package/docs/contributing/11-scanner.md +104 -0
  20. package/docs/contributing/12-render.md +116 -0
  21. package/docs/contributing/13-directives.md +225 -0
  22. package/docs/contributing/14-events.md +57 -0
  23. package/docs/contributing/15-router.md +9 -0
  24. package/docs/contributing/16-state.md +48 -0
  25. package/docs/contributing/17-legodom.md +55 -0
  26. package/docs/contributing/index.md +5 -0
  27. package/docs/examples/form.md +2 -2
  28. package/docs/examples/index.md +4 -4
  29. package/docs/examples/routing.md +8 -8
  30. package/docs/examples/sfc-showcase.md +4 -4
  31. package/docs/examples/todo-app.md +3 -3
  32. package/docs/guide/cdn-usage.md +16 -8
  33. package/docs/guide/components.md +34 -16
  34. package/docs/guide/contributing.md +2 -2
  35. package/docs/guide/directives.md +23 -23
  36. package/docs/guide/getting-started.md +41 -16
  37. package/docs/guide/index.md +12 -12
  38. package/docs/guide/lifecycle.md +1 -1
  39. package/docs/guide/quick-start.md +8 -5
  40. package/docs/guide/reactivity.md +30 -9
  41. package/docs/guide/routing.md +189 -289
  42. package/docs/guide/sfc.md +40 -40
  43. package/docs/guide/templating.md +4 -4
  44. package/docs/index.md +48 -14
  45. package/docs/public/logo.svg +17 -38
  46. package/docs/router/basic-routing.md +103 -0
  47. package/docs/router/cold-entry.md +91 -0
  48. package/docs/router/history.md +69 -0
  49. package/docs/router/index.md +73 -0
  50. package/docs/router/resolver.md +74 -0
  51. package/docs/router/surgical-swaps.md +134 -0
  52. package/examples/vite-app/README.md +2 -2
  53. package/examples/vite-app/index.html +9 -13
  54. package/examples/vite-app/package.json +4 -2
  55. package/examples/vite-app/src/app.css +3 -0
  56. package/examples/vite-app/src/app.js +29 -0
  57. package/examples/vite-app/src/components/app-navbar.lego +34 -0
  58. package/examples/vite-app/src/components/customers/customer-details.lego +24 -0
  59. package/examples/vite-app/src/components/customers/customer-orders.lego +21 -0
  60. package/examples/vite-app/src/components/customers/order-list.lego +55 -0
  61. package/examples/vite-app/src/components/greeting-card.lego +26 -26
  62. package/examples/vite-app/src/components/sample-component.lego +58 -58
  63. package/examples/vite-app/src/components/shells/customers-shell.lego +21 -0
  64. package/examples/vite-app/src/components/todo-list.lego +239 -0
  65. package/examples/vite-app/src/components/widgets/user-card.lego +27 -0
  66. package/examples/vite-app/vite.config.js +7 -2
  67. package/lego.js +2 -0
  68. package/main.js +280 -83
  69. package/package.json +8 -3
  70. package/parse-lego.js +17 -8
  71. package/parse-lego.test.js +1 -1
  72. package/{main.test.js → tests/main.test.js} +34 -17
  73. package/tests/parse-lego.test.js +65 -0
  74. package/vite-plugin.js +62 -24
  75. package/docs/.vitepress/dist/404.html +0 -22
  76. package/docs/.vitepress/dist/api/define.html +0 -35
  77. package/docs/.vitepress/dist/api/directives.html +0 -32
  78. package/docs/.vitepress/dist/api/globals.html +0 -27
  79. package/docs/.vitepress/dist/api/index.html +0 -25
  80. package/docs/.vitepress/dist/api/lifecycle.html +0 -38
  81. package/docs/.vitepress/dist/api/route.html +0 -34
  82. package/docs/.vitepress/dist/api/vite-plugin.html +0 -37
  83. package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.js +0 -11
  84. package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.lean.js +0 -1
  85. package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.js +0 -8
  86. package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.lean.js +0 -1
  87. package/docs/.vitepress/dist/assets/api_globals.md.DOjt7AV0.js +0 -3
  88. package/docs/.vitepress/dist/assets/api_globals.md.DOjt7AV0.lean.js +0 -1
  89. package/docs/.vitepress/dist/assets/api_index.md.OS6h01ct.js +0 -1
  90. package/docs/.vitepress/dist/assets/api_index.md.OS6h01ct.lean.js +0 -1
  91. package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.js +0 -14
  92. package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.lean.js +0 -1
  93. package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.js +0 -10
  94. package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.lean.js +0 -1
  95. package/docs/.vitepress/dist/assets/api_vite-plugin.md.DNn9VhL5.js +0 -13
  96. package/docs/.vitepress/dist/assets/api_vite-plugin.md.DNn9VhL5.lean.js +0 -1
  97. package/docs/.vitepress/dist/assets/app.BG5s3B0P.js +0 -1
  98. package/docs/.vitepress/dist/assets/chunks/@localSearchIndexroot.DQmuWC2Z.js +0 -1
  99. package/docs/.vitepress/dist/assets/chunks/VPLocalSearchBox.BO-PSxt1.js +0 -9
  100. package/docs/.vitepress/dist/assets/chunks/framework.B7OFBR9X.js +0 -19
  101. package/docs/.vitepress/dist/assets/chunks/theme.DA-iSa9B.js +0 -2
  102. package/docs/.vitepress/dist/assets/examples_form.md.B3stGKbu.js +0 -34
  103. package/docs/.vitepress/dist/assets/examples_form.md.B3stGKbu.lean.js +0 -1
  104. package/docs/.vitepress/dist/assets/examples_index.md.BDEG_D4J.js +0 -30
  105. package/docs/.vitepress/dist/assets/examples_index.md.BDEG_D4J.lean.js +0 -1
  106. package/docs/.vitepress/dist/assets/examples_routing.md.bqZ9DjDK.js +0 -338
  107. package/docs/.vitepress/dist/assets/examples_routing.md.bqZ9DjDK.lean.js +0 -1
  108. package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DLXaUiop.js +0 -13
  109. package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DLXaUiop.lean.js +0 -1
  110. package/docs/.vitepress/dist/assets/examples_todo-app.md.D5RhZoo5.js +0 -297
  111. package/docs/.vitepress/dist/assets/examples_todo-app.md.D5RhZoo5.lean.js +0 -1
  112. package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CAjf03Lr.js +0 -182
  113. package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CAjf03Lr.lean.js +0 -1
  114. package/docs/.vitepress/dist/assets/guide_components.md.BIFWF1Hc.js +0 -174
  115. package/docs/.vitepress/dist/assets/guide_components.md.BIFWF1Hc.lean.js +0 -1
  116. package/docs/.vitepress/dist/assets/guide_contributing.md.BgbUN-Mr.js +0 -1
  117. package/docs/.vitepress/dist/assets/guide_contributing.md.BgbUN-Mr.lean.js +0 -1
  118. package/docs/.vitepress/dist/assets/guide_directives.md.Bi3ynu1d.js +0 -140
  119. package/docs/.vitepress/dist/assets/guide_directives.md.Bi3ynu1d.lean.js +0 -1
  120. package/docs/.vitepress/dist/assets/guide_getting-started.md.2Nr1lp2z.js +0 -107
  121. package/docs/.vitepress/dist/assets/guide_getting-started.md.2Nr1lp2z.lean.js +0 -1
  122. package/docs/.vitepress/dist/assets/guide_index.md.GvZq_Yf2.js +0 -2
  123. package/docs/.vitepress/dist/assets/guide_index.md.GvZq_Yf2.lean.js +0 -1
  124. package/docs/.vitepress/dist/assets/guide_lifecycle.md.B28j1OzS.js +0 -304
  125. package/docs/.vitepress/dist/assets/guide_lifecycle.md.B28j1OzS.lean.js +0 -1
  126. package/docs/.vitepress/dist/assets/guide_quick-start.md.CNk3VGTF.js +0 -33
  127. package/docs/.vitepress/dist/assets/guide_quick-start.md.CNk3VGTF.lean.js +0 -1
  128. package/docs/.vitepress/dist/assets/guide_reactivity.md.CVsaMaPv.js +0 -135
  129. package/docs/.vitepress/dist/assets/guide_reactivity.md.CVsaMaPv.lean.js +0 -1
  130. package/docs/.vitepress/dist/assets/guide_routing.md.DSpDP25o.js +0 -193
  131. package/docs/.vitepress/dist/assets/guide_routing.md.DSpDP25o.lean.js +0 -1
  132. package/docs/.vitepress/dist/assets/guide_sfc.md.CVUP66tS.js +0 -187
  133. package/docs/.vitepress/dist/assets/guide_sfc.md.CVUP66tS.lean.js +0 -1
  134. package/docs/.vitepress/dist/assets/guide_templating.md.BgCGe4aa.js +0 -119
  135. package/docs/.vitepress/dist/assets/guide_templating.md.BgCGe4aa.lean.js +0 -1
  136. package/docs/.vitepress/dist/assets/index.md.xV1taCED.js +0 -23
  137. package/docs/.vitepress/dist/assets/index.md.xV1taCED.lean.js +0 -1
  138. package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  139. package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  140. package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  141. package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  142. package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  143. package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  144. package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  145. package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  146. package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  147. package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  148. package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  149. package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  150. package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  151. package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  152. package/docs/.vitepress/dist/assets/style.eycE2Jhw.css +0 -1
  153. package/docs/.vitepress/dist/examples/form.html +0 -58
  154. package/docs/.vitepress/dist/examples/index.html +0 -368
  155. package/docs/.vitepress/dist/examples/routing.html +0 -362
  156. package/docs/.vitepress/dist/examples/sfc-showcase.html +0 -37
  157. package/docs/.vitepress/dist/examples/todo-app.html +0 -321
  158. package/docs/.vitepress/dist/guide/cdn-usage.html +0 -206
  159. package/docs/.vitepress/dist/guide/components.html +0 -198
  160. package/docs/.vitepress/dist/guide/contributing.html +0 -25
  161. package/docs/.vitepress/dist/guide/directives.html +0 -164
  162. package/docs/.vitepress/dist/guide/getting-started.html +0 -131
  163. package/docs/.vitepress/dist/guide/index.html +0 -26
  164. package/docs/.vitepress/dist/guide/lifecycle.html +0 -328
  165. package/docs/.vitepress/dist/guide/quick-start.html +0 -57
  166. package/docs/.vitepress/dist/guide/reactivity.html +0 -159
  167. package/docs/.vitepress/dist/guide/routing.html +0 -217
  168. package/docs/.vitepress/dist/guide/sfc.html +0 -211
  169. package/docs/.vitepress/dist/guide/templating.html +0 -143
  170. package/docs/.vitepress/dist/hashmap.json +0 -1
  171. package/docs/.vitepress/dist/index.html +0 -47
  172. package/docs/.vitepress/dist/logo.svg +0 -38
  173. package/docs/.vitepress/dist/vp-icons.css +0 -1
  174. package/examples/vite-app/src/main.js +0 -11
  175. package/examples.js +0 -99
@@ -1,373 +1,273 @@
1
- # Routing
1
+ # Surgical Routing
2
2
 
3
- LegoJS includes a built-in client-side router for building single-page applications.
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
- ## Basic Setup
5
+ ## The Concept
6
6
 
7
- ### 1. Add Router Outlet
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
- ```html
10
- <lego-router></lego-router>
11
- ```
10
+ - **`b-target`**: "Where should this content go?"
11
+ - **`b-link`**: "Should this update the URL history?"
12
12
 
13
- This is where your routed components will render.
13
+ ---
14
14
 
15
- ### 2. Define Routes
15
+ ## 1. Declarative Routing
16
16
 
17
- ```js
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
- ### 3. Create Page Components
19
+ ### `b-target`
20
+ Specifies the CSS selector of the element to replace.
24
21
 
25
22
  ```html
26
- <template b-id="home-page">
27
- <h1>Home</h1>
28
- <p>Welcome to the homepage!</p>
29
- </template>
30
-
31
- <template b-id="about-page">
32
- <h1>About</h1>
33
- <p>Learn more about us.</p>
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
- ### 4. Add Navigation
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
- <nav>
41
- <a href="/" b-link>Home</a>
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
- <lego-router></lego-router>
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
- The `b-link` attribute hijacks clicks to prevent page reloads.
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
- ## Complete Example
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
- <lego-router></lego-router>
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
- <template b-id="blog-page">
79
- <h1>Blog</h1>
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
- ## Dynamic Routes
64
+ ## 2. The `$go` API
102
65
 
103
- Use `:param` syntax for URL parameters:
66
+ For full programmatic control, use the globally available `$go` helper. It allows for surgical updates from your JavaScript logic.
104
67
 
105
- ```js
106
- Lego.route('/user/:id', 'user-profile');
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
- ### Accessing Parameters
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
- Route parameters are available in `global.params`:
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
- ```html
116
- <template b-id="user-profile">
117
- <h1>User Profile</h1>
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
- ## Programmatic Navigation
81
+ // 2. Surgical Navigation (updates #sidebar, pushes to history)
82
+ Lego.globals.$go('/widgets/clock', '#sidebar').get();
135
83
 
136
- Navigate programmatically using the History API:
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
- ```js
139
- // Navigate to a new route
140
- history.pushState({}, '', '/about');
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
- // Or in a component method:
144
- {
145
- goToAbout() {
146
- history.pushState({}, '', '/about');
147
- window.dispatchEvent(new PopStateEvent('popstate'));
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
- ## Route Middleware
111
+ ---
153
112
 
154
- Add middleware for authentication, logging, etc:
113
+ ## 3. Advanced Patterns
155
114
 
156
- ```js
157
- // Define middleware function
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
- ### Middleware Example
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
- ```js
175
- // Logging middleware
176
- const logger = (params) => {
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
- // Analytics middleware
182
- const analytics = (params) => {
183
- if (window.gtag) {
184
- gtag('event', 'page_view', { page_path: window.location.pathname });
185
- }
186
- return true;
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
- // Apply
190
- Lego.route('/products/:id', 'product-page', (params, globals) => {
191
- logger(params);
192
- analytics(params);
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
- ## Active Link Styling
144
+ ### The "Modal" Pattern
145
+ Render a route into a modal dialog container.
198
146
 
199
- Style active navigation links:
147
+ ```html
148
+ <dialog id="modal-container"></dialog>
200
149
 
201
- ```js
202
- // Update active class on navigation
203
- window.addEventListener('popstate', () => {
204
- document.querySelectorAll('[b-link]').forEach(link => {
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
- ## 404 / Not Found
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
- Handle unknown routes:
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
- ```js
216
- // Define all your routes
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
- // Catch-all for 404
221
- const matchRoute = () => {
222
- const path = window.location.pathname;
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
- ## Nested Routes
176
+ ---
236
177
 
237
- While LegoJS doesn't have built-in nested routing, you can implement it:
178
+ ## 4. Deep Routing Strategies
238
179
 
239
- ```html
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
- ```js
253
- Lego.route('/blog/:section', 'blog-layout');
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
- ## Query Strings
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
- Access query parameters using URLSearchParams:
188
+ ```javascript
189
+ // Route Configuration
190
+ Lego.route('/customers/:id', 'customers-shell');
191
+ Lego.route('/customers/:id/orders/:orderId', 'customers-shell');
259
192
 
260
- ```js
261
- {
262
- mounted() {
263
- const params = new URLSearchParams(window.location.search);
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
- Example: `/search?q=legojs&page=2`
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
- LegoJS uses History API (pushState) by default, giving you clean URLs:
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
- ✅ `/about`
277
- `/user/123`
278
- `/blog/my-post`
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
- **Apache (.htaccess):**
296
- ```apache
297
- RewriteEngine On
298
- RewriteCond %{REQUEST_FILENAME} !-f
299
- RewriteCond %{REQUEST_FILENAME} !-d
300
- RewriteRule ^ index.html [L]
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
- **Node.js/Express:**
304
- ```js
305
- app.get('*', (req, res) => {
306
- res.sendFile(path.join(__dirname, 'index.html'));
307
- });
308
- ```
221
+ ---
309
222
 
310
- ## Best Practices
223
+ ## 5. Middleware & Guards
311
224
 
312
- ### 1. Centralize Route Definitions
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
- ```js
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
- Object.entries(routes).forEach(([path, component]) => {
325
- Lego.route(path, component);
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
- ### 2. Loading States
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
- ```html
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
- ### 3. Error Handling
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
- ## Limitations
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
- - No nested router outlets (single level only)
363
- - No route guards (use middleware instead)
364
- - No automatic scroll restoration
365
- - Routes must be defined upfront (not lazy-loaded)
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
- For complex routing needs, consider integrating a dedicated router library.
263
+ ## Summary Table
368
264
 
369
- ## Next Steps
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
@@ -43,43 +43,6 @@ Here's a complete example (`user-card.lego`):
43
43
 
44
44
  ```html
45
45
  <template>
46
- <style>
47
- self {
48
- display: block;
49
- padding: 1.5rem;
50
- border: 2px solid #ddd;
51
- border-radius: 8px;
52
- max-width: 300px;
53
- }
54
-
55
- .avatar {
56
- width: 80px;
57
- height: 80px;
58
- border-radius: 50%;
59
- object-fit: cover;
60
- }
61
-
62
- .name {
63
- font-size: 1.5rem;
64
- font-weight: bold;
65
- margin: 0.5rem 0;
66
- }
67
-
68
- .bio {
69
- color: #666;
70
- margin: 0.5rem 0;
71
- }
72
-
73
- button {
74
- background: #4CAF50;
75
- color: white;
76
- border: none;
77
- padding: 0.5rem 1rem;
78
- border-radius: 4px;
79
- cursor: pointer;
80
- }
81
- </style>
82
-
83
46
  <img class="avatar" src="{{ avatarUrl }}" alt="{{ name }}">
84
47
  <h2 class="name">{{ name }}</h2>
85
48
  <p class="bio">{{ bio }}</p>
@@ -89,6 +52,43 @@ Here's a complete example (`user-card.lego`):
89
52
  </button>
90
53
  </template>
91
54
 
55
+ <style>
56
+ self {
57
+ display: block;
58
+ padding: 1.5rem;
59
+ border: 2px solid #ddd;
60
+ border-radius: 8px;
61
+ max-width: 300px;
62
+ }
63
+
64
+ .avatar {
65
+ width: 80px;
66
+ height: 80px;
67
+ border-radius: 50%;
68
+ object-fit: cover;
69
+ }
70
+
71
+ .name {
72
+ font-size: 1.5rem;
73
+ font-weight: bold;
74
+ margin: 0.5rem 0;
75
+ }
76
+
77
+ .bio {
78
+ color: #666;
79
+ margin: 0.5rem 0;
80
+ }
81
+
82
+ button {
83
+ background: #4CAF50;
84
+ color: white;
85
+ border: none;
86
+ padding: 0.5rem 1rem;
87
+ border-radius: 4px;
88
+ cursor: pointer;
89
+ }
90
+ </style>
91
+
92
92
  <script>
93
93
  export default {
94
94
  name: 'John Doe',
@@ -173,7 +173,7 @@ In your `index.html`:
173
173
  <!DOCTYPE html>
174
174
  <html>
175
175
  <head>
176
- <title>My LegoJS App</title>
176
+ <title>My Lego App</title>
177
177
  </head>
178
178
  <body>
179
179
  <div id="app">
@@ -210,12 +210,12 @@ Component names **must**:
210
210
 
211
211
  ### Template Section
212
212
 
213
- Contains your component's HTML markup with LegoJS directives:
213
+ Contains your component's HTML markup with Lego directives:
214
214
 
215
215
  ```html
216
216
  <template>
217
217
  <h1>{{ title }}</h1>
218
- <p b-if="showContent">{{ content }}</p>
218
+ <p b-show="showContent">{{ content }}</p>
219
219
  <ul>
220
220
  <li b-for="item in items">{{ item }}</li>
221
221
  </ul>