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
@@ -0,0 +1,225 @@
1
+ # You know C++, I know Web Components++
2
+
3
+ Let's break down the two simplest yet most vital directives in the library: b-if and b-text. These are the "workhorses" that handle visibility and data display without you having to write manual DOM manipulation code.
4
+
5
+ ## Conditional Directives `b-if` & `b-show`
6
+
7
+ Directives like `b-if`, `b-text`, and `b-for` are the "Instructions" that bridge the gap between your JavaScript state and the DOM. Without them, your state would just be numbers and strings sitting in memory with no way to manifest on the screen.
8
+
9
+ ### 1. Conditional Visibility (`b-show`)
10
+
11
+ The `b-show` directive is used to show or hide elements based on a truthy or falsy value in your state.
12
+
13
+ - **How it works**: During the `render()` cycle, the library executes `safeEval(b.expr, { state, self: b.node })`.
14
+
15
+ - **The Implementation**:
16
+
17
+
18
+ ```js
19
+ if (b.type === 'b-show') b.node.style.display = safeEval(b.expr, { state, self: b.node }) ? '' : 'none';
20
+ ```
21
+
22
+ - LegoDOM does not physically remove the element from the DOM (which is expensive), this library uses `display: none`.
23
+
24
+ - This means **b-show** is incredibly fast because the browser doesn't have to recalculate the entire DOM tree.
25
+
26
+ - It however also means the element still exists in memory and its `mounted` hook remains active even when hidden.
27
+
28
+
29
+ ### 2. Alternating Visibility (`b-if`)
30
+
31
+ #### 1. The "Place in Line" Problem
32
+
33
+ When an element has `b-if="false"`, we want it to disappear. If we simply remove it (`node.remove()`), the browser "forgets" where that element was supposed to live.
34
+
35
+ - **The Risk:** If that element was originally between a `<h1>` and a `<footer>`, and the condition turns `true` later, how does the library know exactly where to put it back?
36
+
37
+ - **The Solution:** We leave a "bookmark" or an **Anchor** (a tiny, invisible `Comment` node) exactly where the element used to be.
38
+
39
+ ```js
40
+ if (node.hasAttribute('b-if')) {
41
+ const expr = node.getAttribute('b-if');
42
+ // Create an anchor point to keep track of where the element belongs in the DOM
43
+ const anchor = document.createComment(`b-if: ${expr}`);
44
+ const data = getPrivateData(node);
45
+ data.anchor = anchor;
46
+ bindings.push({ type: 'b-if', node, anchor, expr });
47
+ }
48
+ ```
49
+
50
+
51
+ ### 2. Why use a Comment Node?
52
+
53
+ We use `document.createComment()` because:
54
+
55
+ - It is a valid DOM node that occupies a specific position in the `childNodes` list.
56
+
57
+ - It is completely invisible to the user and doesn't affect CSS layouts or accessibility.
58
+
59
+ - It acts as a permanent reference point for the `replaceChild` operation.
60
+
61
+
62
+ ### 3. Why store it in `getPrivateData`?
63
+
64
+ LegoDOM was designed in a way that the `render()` function runs every time state changes.
65
+
66
+ - **Consistency:** By storing the anchor in the element's `privateData` (via `WeakMap`), we ensure that the same specific element is always paired with the same specific anchor.
67
+
68
+ - **The Swap Logic:**
69
+
70
+ - **Condition becomes `false`:** We find the element in the DOM and replace it with its stored anchor: `parent.replaceChild(anchor, node)`.
71
+
72
+ - **Condition becomes `true`:** We find the anchor in the DOM and replace it with the element: `parent.replaceChild(node, anchor)`.
73
+
74
+
75
+ ### 4. Memory Safety
76
+
77
+ By using `getPrivateData` (which is a `WeakMap`), we ensure that if the component is destroyed, the reference to the `anchor` is garbage collected. If we didn't store it here, we would have to scan the entire DOM or maintain complex external maps to find where to re-insert hidden elements, which would be a massive performance hit.
78
+
79
+ **In short:** The anchor is the "Reserved Seat" sign at a theater. `getPrivateData` is the list that remembers which seat belongs to which person while they are out in the lobby.
80
+
81
+
82
+ ## Simple Text Interpolation (`b-text`)
83
+
84
+ While you can use <code v-pre>{{mustaches}}</code>, `b-text` is the "cleaner" way to bind the entire content of an element to a single variable.
85
+
86
+ - **The Logic**: It uses the `resolve()` helper to walk through your state object based on a string path.
87
+
88
+ - Example: `<span b-text="user.profile.name"></span>`.
89
+
90
+ - **The Implementation**:
91
+
92
+ ```js
93
+ if (b.type === 'b-text') b.node.textContent = escapeHTML(resolve(b.path, state));
94
+ ```
95
+
96
+ - **Security**: Note the use of `escapeHTML()`. This is a critical security feature that prevents **XSS (Cross-Site Scripting) attacks** by turning characters like `<` into `&lt;`, ensuring that user-provided data cannot execute malicious scripts in your app.
97
+
98
+
99
+ ### 3. Efficiency in the Render Loop
100
+
101
+ Because both of these were "mapped" during the `scanForBindings` phase (Topic 11), the `render` engine holds a direct reference to the DOM `node`.
102
+
103
+ - It doesn't have to look for the element by ID or class.
104
+
105
+ - It just goes: "Variable X changed -> Go to memory address for Node Y -> Update `.style.display` or `.textContent`".
106
+
107
+
108
+ ## Iterative Directive: `b-for` & The Pool
109
+
110
+ The library looks for the pattern `item in list` (e.g., `user in users`).
111
+
112
+ - **Capture**: During the scanning phase, it saves the inner HTML of the element as a "template string" and then empties the element so it can be filled dynamically.
113
+
114
+
115
+ ### The Concept of "The Pool" (`forPools`)
116
+
117
+ To prevent "DOM Thrashing" (constantly creating and deleting elements), the library uses a `WeakMap` called `forPools`.
118
+
119
+ - **The Cache**: Each `b-for` node has its own `Map` inside the pool.
120
+
121
+ - **The Key**: It identifies each item in your array. If the item is an object, it assigns a hidden `__id`. If it's a primitive (like a string), it combines the index and the value.
122
+
123
+ - **Why?**: If you re-order a list of 100 items, the library doesn't create 100 new elements. it finds the existing elements in the "pool" by their key and simply moves them to the new position.
124
+
125
+
126
+ ### 3. The Local Scope Injection
127
+
128
+ This is a brilliant piece of JavaScript engineering. When rendering a list item, the library needs the item (e.g., `user`) to be available, but it also needs the parent component's data to be available.
129
+
130
+
131
+ ```js
132
+ const localScope = Object.assign(Object.create(state), { [b.itemName]: item });
133
+
134
+ ```
135
+
136
+ - **Prototype Inheritance**: It creates a new object where the **prototype** is the component's state (`_studs`).
137
+
138
+ - **The Result**: Inside the loop, if you reference <code v-pre>{{user.name}}</code>, it finds it on the `localScope`. If you reference <code v-pre>{{globalTitle}}</code> (defined in the parent), it doesn't find it on `localScope`, so it automatically looks up the prototype chain to find it in the parent `state`.
139
+
140
+
141
+ ### 4. Updating and Pruning
142
+
143
+ - **Update**: For every item in the current array, it calls `updateNodeBindings(child, localScope)` to fill in the mustaches for that specific row.
144
+
145
+ - **Surgical Sync**: It specifically looks for `b-sync` inputs inside the loop to ensure two-way binding works correctly for individual list items.
146
+
147
+ - **Prune**: After the loop finishes, any element left in the "pool" that wasn't used in the current render (meaning it was deleted from your data array) is physically removed from the DOM.
148
+
149
+ ## Directive: `b-sync` (Two-Way Binding)
150
+
151
+ The `b-sync` directive is designed to keep an `<input>`, `<textarea>`, or `<select>` element in perfect synchronization with a specific variable in your state.
152
+
153
+ ```js
154
+ if (child.hasAttribute('b-sync')) {
155
+ const prop = child.getAttribute('b-sync');
156
+ const updateState = () => {
157
+ let target, last;
158
+ if (loopCtx && prop.startsWith(loopCtx.name + '.')) {
159
+ const list = safeEval(loopCtx.listName, { state, global: Lego.globals, self: componentRoot });
160
+ const item = list[loopCtx.index];
161
+ if (!item) return;
162
+ const subPath = prop.split('.').slice(1);
163
+ last = subPath.pop();
164
+ target = subPath.reduce((o, k) => o[k], item);
165
+ } else {
166
+ const keys = prop.split('.');
167
+ last = keys.pop();
168
+ target = keys.reduce((o, k) => o[k], state);
169
+ }
170
+ const newVal = child.type === 'checkbox' ? child.checked : child.value;
171
+ if (target && target[last] !== newVal) target[last] = newVal;
172
+ };
173
+ child.addEventListener('input', updateState);
174
+ child.addEventListener('change', updateState);
175
+ }
176
+ ```
177
+
178
+ ### 1. The Setup: Listening for Changes
179
+
180
+ When the `scanForBindings` function encounters a `b-sync="somePath"` attribute, it doesn't just record it for rendering; it attaches an **event listener** to the element.
181
+
182
+ - **The Event**: It listens for the `input` event (which fires every time a character is typed).
183
+
184
+ - **The Logic**: When the event fires, the library captures the `event.target.value`.
185
+
186
+ - **The Update**: It uses the internal `set()` helper to reach into your component's `_studs` and update the value at the path you specified (e.g., `user.name`).
187
+
188
+
189
+ ### 2. The Implementation: Multi-Type Support
190
+
191
+ The library is smart enough to handle different types of inputs automatically:
192
+
193
+ - **Checkboxes**: It looks at `.checked` instead of `.value`.
194
+
195
+ - **Numbers**: If the input `type` is "number" or "range", it automatically converts the string from the DOM into a real JavaScript `Number` before saving it to your state.
196
+
197
+
198
+ ### 3. Preventing the "Echo" Effect
199
+
200
+ A common problem in two-way binding is the "Infinite Update Loop":
201
+
202
+ 1. You type "A".
203
+
204
+ 2. `b-sync` updates the state to "A".
205
+
206
+ 3. The state change triggers a `render()`.
207
+
208
+ 4. `render()` updates the input value to "A".
209
+
210
+ 5. The cursor jumps to the end of the input or triggers another event.
211
+
212
+
213
+ **How Lego solves it**: During the `render()` phase for a `b-sync` binding, the library checks if the element is currently the `document.activeElement` (the thing you are typing in). If it is, and the value hasn't changed from what's already there, it skips the update to avoid disturbing your typing flow.
214
+
215
+ ### 4. The `b-sync` inside `b-for` loops
216
+
217
+ As mentioned in Topic 14, `b-sync` works inside loops. If you have a list of inputs generated by a `b-for`, each input is synced to its specific item in the array. The library uses the `localScope` (with its prototype chain) to ensure that typing in the 3rd input only updates the 3rd item in your data list.
218
+
219
+ ----------
220
+
221
+ **Summary**: `b-if` manages visibility via CSS, and `b-text` manages safe text updates via property resolution.
222
+
223
+ `b-for` is a "Reconciliation Engine". It uses a memory pool to recycle DOM nodes and clever prototype inheritance to give each row access to both its own data and the parent's data.
224
+
225
+ `b-sync` automates the "Boilerplate" of web development. You no longer have to write `onchange` handlers for every input; you just name the variable, and the library handles the rest.
@@ -0,0 +1,57 @@
1
+ # Lights, Camera, Action
2
+
3
+ This is how LegoDOM makes your components interactive by connecting DOM events (clicks, keypresses, submits) directly to the methods you wrote in
4
+ your **SFC** (Single File Component) autodiscovered **.lego** file, `<template />` tag or Lego.define call.
5
+
6
+ ## Event Handling (The `@event` Syntax)
7
+
8
+ The library uses a shorthand syntax for event listeners: `@event-name="methodName"`. For example, `@click="increment"` or `@submit="saveUser"`.
9
+
10
+ ### 1. The `bind(container, el)` Phase
11
+
12
+ During the `snap()` process, after the Shadow DOM is attached, the library calls `bind(container, el)`.
13
+
14
+ - **Scanning for Attributes**: It looks through all elements in the Shadow DOM for any attribute starting with `@`.
15
+
16
+ - **The Match**: If it finds `@click="toggle"`, it identifies `click` as the event and `toggle` as the function name to look for in `_studs`.
17
+
18
+
19
+ ### 2. Context Preservation (`.bind`)
20
+
21
+ This is a critical "under the hood" step. When you click a button, the browser usually sets the keyword `this` to the button itself. However, in Lego, you want `this` to be your **reactive state**.
22
+
23
+ - **The Implementation**:
24
+
25
+ ```js
26
+ const handler = state[methodName].bind(state);
27
+ node.addEventListener(eventName, handler);
28
+ ```
29
+
30
+ - By using `.bind(state)`, the library ensures that when your `toggle()` function runs, `this.show = true` actually updates the proxy state, not the HTML button.
31
+
32
+
33
+ ### 3. Argument Support
34
+
35
+ The library is flexible with how you call these methods:
36
+
37
+ - **Standard Call**: `@click="doSomething"` passes the native `Event` object as the first argument.
38
+
39
+ - **Parameterized Call**: `@click="deleteItem(5)"`. The library uses a regex to check if there are parentheses. If it finds them, it parses the arguments (like `5`) and passes them to your function.
40
+
41
+ - **The "Magic" `$event`**: If you write `@click="move($event, 10)"`, the library intelligently injects the native browser event object into that specific slot.
42
+
43
+
44
+ ### 4. Event Modifiers
45
+
46
+ Lego includes built-in modifiers to handle common web patterns without writing extra JS:
47
+
48
+ - **`.prevent`**: Automatically calls `event.preventDefault()`. Great for `@submit.prevent="save"`.
49
+
50
+ - **`.stop`**: Calls `event.stopPropagation()` to stop the event from bubbling up to parent elements.
51
+
52
+ - **`.enter`**: Only triggers the method if the "Enter" key was pressed (perfect for search bars).
53
+
54
+
55
+ ----------
56
+
57
+ **Summary**: The `@` syntax automates `addEventListener`, ensures the correct `this` context for your methods, and provides powerful modifiers to keep your component logic clean.
@@ -0,0 +1,9 @@
1
+ # Where do you want to go?
2
+
3
+ The true power of the Lego router isn't just changing the URL; it's the **targeted DOM injection** that allows you to swap _any_ part of the page with _any_ component, without writing a single line of `fetch` or `innerHTML` logic.
4
+
5
+
6
+ ## LegoDOM Router
7
+
8
+
9
+
@@ -0,0 +1,48 @@
1
+ # Lego and the Brain
2
+ In Lego, the code is designed to allow a component in the footer to talk to a component in the header without them ever being "parents" or "children" of each other.
3
+
4
+ ## Global State (`Lego.globals`) & The Observer Pattern
5
+
6
+
7
+
8
+ ### 1. The "Why": Decoupling the Hierarchy
9
+
10
+ In most frameworks, data flows down like a waterfall. If a deeply nested component needs a piece of data, every parent above it must "pass it down." The code in `main.js` avoids this by creating a centralized, reactive hub.
11
+
12
+ ### 2. The Implementation: The "Universal Proxy"
13
+
14
+ When you define `Lego.globals`, the library doesn't just store your object. It wraps the entire thing in the same `reactive()` proxy used for individual components.
15
+
16
+ - **The Code**: `Lego.globals = reactive(userDefinedGlobals, null)`.
17
+
18
+ - **The Magic of `null`**: Notice that when a component's state is made reactive, we pass the `el` (the element) so it knows what to render. When we create `Lego.globals`, we pass `null`. This tells the proxy: "You don't belong to one element; you belong to everyone".
19
+
20
+
21
+ ### 3. The `$global` Prefix and Subscriptions
22
+
23
+ The library uses a specific naming convention to trigger its "Global Watcher" logic. Whenever the `render()` engine or a `b-sync` sees a variable starting with `$`, it knows to ignore the local component state (`_studs`) and look into `Lego.globals` instead.
24
+
25
+ - **The Subscription Logic**: In the `render()` function, if a global is accessed, the code automatically adds that component to a "Global Subscribers" list.
26
+
27
+ - **The Update Loop**: When you change a global (e.g., `Lego.globals.theme = 'dark'`), the Proxy's `set` trap fires. Because this proxy is global, it iterates through **every single component** currently on the page and tells them to check if they need a re-render.
28
+
29
+
30
+ ### 4. Why `Object.defineProperty` is avoided for Globals
31
+
32
+ The library sticks to **Proxy** for globals because it allows for **dynamic property addition**.
33
+
34
+ - In older libraries, you had to declare all your global variables upfront.
35
+
36
+ - Because Lego uses a Proxy, you can do `Lego.globals.newVar = 'surprise'` at runtime, and the library will immediately catch that "set" operation and notify all components, even though `newVar` didn't exist when the app started.
37
+
38
+
39
+ ### 5. The `$go` and Globals Synergy
40
+
41
+ This is where the code becomes a "system." When you use `$go` to swap a component (Topic 17), the new component is born, it scans its HTML, sees a `$user` variable, and "subscribes" to the global state.
42
+
43
+ - This allows for **Persistent Identity**: The user's name stays in the header because the header is subscribed to `Lego.globals.user`, even as the rest of the page is being torn down and rebuilt by the router.
44
+
45
+
46
+ ----------
47
+
48
+ **Summary**: The code uses a "Centralized Proxy" to bypass the DOM hierarchy, ensuring that data is shared via subscription rather than inheritance.
@@ -0,0 +1,55 @@
1
+ # Mama "We Made It!"
2
+ Everything we’ve discussed i.e. the Scanner, the Registry, the Router, and the Snap etc. "Everything" stays dormant until `Lego.init()` is called. This function is not just a starter; it’s an orchestrator that synchronizes the JavaScript environment with the existing HTML on the page.
3
+
4
+ ## The Lego Initializer
5
+
6
+ The code for `init()` is deceptively small because its primary job is to flip the switches on the systems we've already built.
7
+
8
+ ### 1. The Singleton Guard
9
+
10
+ The very first thing `init()` does is check a internal flag: `if (initialized) return;`.
11
+
12
+ - **The Why**: In a complex app, you might accidentally call `init()` multiple times. Without this guard, you would attach multiple `MutationObservers` to the body, causing every component to "snap" and "render" twice for every single change.
13
+
14
+
15
+ ### 2. Bootstrapping the Watchdog
16
+
17
+ The core of the initialization is setting up the `MutationObserver` we discussed in Topic 7.
18
+
19
+ - **The Target**: It targets `document.body`.
20
+
21
+ - **The Configuration**: It uses `{ childList: true, subtree: true }`.
22
+
23
+ - **The Why**: By starting the observer _before_ the first render, the library ensures that any elements it creates during the initial startup are also caught and "snapped" into life.
24
+
25
+
26
+ ### 3. The "Initial Snap" (The Recursive Wake-up)
27
+
28
+ Once the observer is live, the code calls `snap(document.body)`.
29
+
30
+ - **The Why**: The `MutationObserver` only sees _new_ changes. It cannot see the HTML that was already there when the page loaded.
31
+
32
+ - **The Logic**: By manually calling `snap` on the body, the library recursively walks through your entire server-rendered HTML. It finds every custom tag (e.g., `<user-card>`) and "upgrades" them into components.
33
+
34
+
35
+ ### 4. Activating the Global Listener
36
+
37
+ This is where the **Router Hijack** (Topic 17) is installed.
38
+
39
+ - The code adds a single click listener to the `window`.
40
+
41
+ - **The Why**: Instead of attaching listeners to every individual `<a>` tag (which would be slow and break when new links are added), it uses **Event Delegation**. It waits for clicks to bubble up to the window, checks if they are `b-link` clicks, and then decides whether to trigger the router.
42
+
43
+
44
+ ### 5. The First Route Check
45
+
46
+ Finally, `init()` calls `router()` manually for the first time.
47
+
48
+ - **The Why**: If a user navigates directly to `mysite.com/dashboard`, the browser loads the page, but the JavaScript needs to know which component to put into the `router-view` immediately. This manual call ensures the UI matches the URL on the very first frame.
49
+
50
+
51
+ ----------
52
+
53
+ **The Architecture "Why"**: Lego is designed to be **Zero-Config**. By putting all these steps into `init()`, the developer only has to care about one thing: "When is my DOM ready?" The code handles the complex timing of making sure the Watchdog is looking while the Router is switching and the Snap is initializing.
54
+
55
+ **Summary**: `Lego.init()` is the bridge. It turns a static document into a reactive application by starting the observer, snapping the existing HTML, and hijacking the navigation.
@@ -0,0 +1,5 @@
1
+ # Welcome to the LegoDOM Source Code Explainer Series ;-)
2
+
3
+ Here I will give a sneak peek into my brain and thinking when designing the LegoDOM library. Some
4
+ decisions might suck to you - I am happy to hear your honest feedback on the
5
+ [community chat](https://github.com/rayattack/lego-dom/discussions).
@@ -1,6 +1,6 @@
1
1
  # Form Example
2
2
 
3
- Handling forms in LegoJS.
3
+ Handling forms in Lego.
4
4
 
5
5
  ## Live Demo
6
6
 
@@ -17,7 +17,7 @@ Handling forms in LegoJS.
17
17
  <input type="password" b-sync="password">
18
18
  </div>
19
19
 
20
- <p b-if="error" style="color: red">{{ error }}</p>
20
+ <p b-show="error" style="color: red">{{ error }}</p>
21
21
 
22
22
  <button type="submit">Login</button>
23
23
  </form>
@@ -1,6 +1,6 @@
1
1
  # Examples
2
2
 
3
- Explore these hands-on examples to learn LegoJS patterns and best practices.
3
+ Explore these hands-on examples to learn Lego patterns and best practices.
4
4
 
5
5
  ## Quick Examples
6
6
 
@@ -27,7 +27,7 @@ Two-way data binding with `b-sync`.
27
27
  ```html
28
28
  <template b-id="name-input">
29
29
  <input b-sync="name" placeholder="Enter your name">
30
- <p b-if="name">Hello, {{ name }}!</p>
30
+ <p b-show="name">Hello, {{ name }}!</p>
31
31
  </template>
32
32
 
33
33
  <name-input b-data="{ name: '' }"></name-input>
@@ -49,7 +49,7 @@ Lists with `b-for`.
49
49
 
50
50
  <todo-list b-data="{
51
51
  todos: [
52
- { text: 'Learn LegoJS', done: true },
52
+ { text: 'Learn Lego', done: true },
53
53
  { text: 'Build an app', done: false }
54
54
  ]
55
55
  }"></todo-list>
@@ -101,4 +101,4 @@ Try these examples directly in your browser:
101
101
 
102
102
  - Check the [API Reference](/api/)
103
103
  - Read the [Guide](/guide/)
104
- - View the [source code](https://github.com/rayattack/LegoJS)
104
+ - View the [source code](https://github.com/rayattack/Lego)
@@ -10,7 +10,7 @@ A multi-page application demonstrating client-side routing.
10
10
  <head>
11
11
  <meta charset="UTF-8">
12
12
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
- <title>Routing Demo - LegoJS</title>
13
+ <title>Routing Demo - Lego</title>
14
14
  <style>
15
15
  body {
16
16
  font-family: system-ui, sans-serif;
@@ -87,7 +87,7 @@ A multi-page application demonstrating client-side routing.
87
87
  </style>
88
88
 
89
89
  <div class="hero">
90
- <h1>Welcome to LegoJS Routing Demo</h1>
90
+ <h1>Welcome to Lego Routing Demo</h1>
91
91
  <p>Navigate between pages using client-side routing</p>
92
92
  </div>
93
93
 
@@ -113,8 +113,8 @@ A multi-page application demonstrating client-side routing.
113
113
  self { display: block; }
114
114
  </style>
115
115
 
116
- <h1>About LegoJS</h1>
117
- <p>LegoJS is a tiny, zero-dependency JavaScript library for building reactive Web Components.</p>
116
+ <h1>About Lego</h1>
117
+ <p>Lego is a tiny, zero-dependency JavaScript library for building reactive Web Components.</p>
118
118
 
119
119
  <h2>Features</h2>
120
120
  <ul>
@@ -176,11 +176,11 @@ A multi-page application demonstrating client-side routing.
176
176
  }
177
177
  </style>
178
178
 
179
- <div b-if="loading" class="loading">
179
+ <div b-show="loading" class="loading">
180
180
  Loading user...
181
181
  </div>
182
182
 
183
- <div b-if="!loading && user" class="profile">
183
+ <div b-show="!loading && user" class="profile">
184
184
  <h1>{{ user.name }}</h1>
185
185
  <p><strong>Email:</strong> {{ user.email }}</p>
186
186
  <p><strong>Phone:</strong> {{ user.phone }}</p>
@@ -243,7 +243,7 @@ A multi-page application demonstrating client-side routing.
243
243
 
244
244
  <h1>Contact Us</h1>
245
245
 
246
- <form b-if="!submitted" @submit="handleSubmit(event)">
246
+ <form b-show="!submitted" @submit="handleSubmit(event)">
247
247
  <div class="form-group">
248
248
  <label>Name</label>
249
249
  <input b-sync="form.name" required>
@@ -262,7 +262,7 @@ A multi-page application demonstrating client-side routing.
262
262
  <button type="submit">Send Message</button>
263
263
  </form>
264
264
 
265
- <div b-if="submitted" class="success">
265
+ <div b-show="submitted" class="success">
266
266
  <h3>✅ Message Sent!</h3>
267
267
  <p>Thank you for contacting us, {{ form.name }}. We'll respond to {{ form.email }} soon.</p>
268
268
  <button @click="submitted = false">Send Another</button>
@@ -7,13 +7,13 @@ Using `.lego` files with Vite.
7
7
  **Counter.lego**
8
8
  ```html
9
9
  <template>
10
- <style>
11
- button { color: red; }
12
- </style>
13
-
14
10
  <button @click="count++">Count: {{ count }}</button>
15
11
  </template>
16
12
 
13
+ <style>
14
+ button { color: red; }
15
+ </style>
16
+
17
17
  <script>
18
18
  export default {
19
19
  count: 0
@@ -1,6 +1,6 @@
1
1
  # Todo App Example
2
2
 
3
- A complete todo application demonstrating LegoJS features.
3
+ A complete todo application demonstrating Lego features.
4
4
 
5
5
  ## Live Demo
6
6
 
@@ -14,7 +14,7 @@ A complete todo application demonstrating LegoJS features.
14
14
  <head>
15
15
  <meta charset="UTF-8">
16
16
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
17
- <title>Todo App - LegoJS</title>
17
+ <title>Todo App - Lego</title>
18
18
  <style>
19
19
  body {
20
20
  font-family: system-ui, -apple-system, sans-serif;
@@ -212,7 +212,7 @@ A complete todo application demonstrating LegoJS features.
212
212
  <span>{{ remaining() }} item{{ remaining() === 1 ? '' : 's' }} left</span>
213
213
  <button
214
214
  class="clear-completed"
215
- b-if="completedCount() > 0"
215
+ b-show="completedCount() > 0"
216
216
  @click="clearCompleted()">
217
217
  Clear completed ({{ completedCount() }})
218
218
  </button>
@@ -1,6 +1,6 @@
1
1
  # CDN Usage
2
2
 
3
- LegoJS works perfectly without any build tools. Just include it via CDN and start building!
3
+ Lego works perfectly without any build tools. Just include it via CDN and start building!
4
4
 
5
5
  ## Quick Start
6
6
 
@@ -19,8 +19,11 @@ LegoJS works perfectly without any build tools. Just include it via CDN and star
19
19
  <!-- Use it -->
20
20
  <hello-world b-data="{ message: 'Hello from CDN!' }"></hello-world>
21
21
 
22
- <!-- Include LegoJS -->
22
+ <!-- Include Lego -->
23
23
  <script src="https://unpkg.com/lego-dom/main.js"></script>
24
+ <script>
25
+ Lego.init();
26
+ </script>
24
27
  </body>
25
28
  </html>
26
29
  ```
@@ -65,7 +68,7 @@ Here's a full working application using only CDN:
65
68
  <head>
66
69
  <meta charset="UTF-8">
67
70
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
68
- <title>Todo App - LegoJS</title>
71
+ <title>Todo App - Lego</title>
69
72
  <style>
70
73
  body {
71
74
  font-family: system-ui, sans-serif;
@@ -146,7 +149,7 @@ Here's a full working application using only CDN:
146
149
  Lego.define('todo-app', Lego.registry['todo-app'].innerHTML, {
147
150
  newTodo: '',
148
151
  todos: [
149
- { text: 'Learn LegoJS', done: true },
152
+ { text: 'Learn Lego', done: true },
150
153
  { text: 'Build something awesome', done: false }
151
154
  ],
152
155
  addTodo() {
@@ -162,6 +165,9 @@ Here's a full working application using only CDN:
162
165
  return this.todos.filter(t => !t.done).length;
163
166
  }
164
167
  });
168
+
169
+ // Don't forget to init!
170
+ Lego.init();
165
171
  </script>
166
172
  </body>
167
173
  </html>
@@ -169,7 +175,7 @@ Here's a full working application using only CDN:
169
175
 
170
176
  ## Progressive Enhancement
171
177
 
172
- LegoJS is perfect for progressively enhancing existing sites:
178
+ Lego is perfect for progressively enhancing existing sites:
173
179
 
174
180
  ```html
175
181
  <!-- Your existing page -->
@@ -208,19 +214,21 @@ LegoJS is perfect for progressively enhancing existing sites:
208
214
  window.location.href = '/logout';
209
215
  }
210
216
  });
217
+
218
+ Lego.init();
211
219
  </script>
212
220
  ```
213
221
 
214
222
  ## Embedding in Existing Apps
215
223
 
216
- LegoJS components work alongside other frameworks:
224
+ Lego components work alongside other frameworks:
217
225
 
218
226
  ```html
219
227
  <!-- Works fine with jQuery, Bootstrap, etc. -->
220
228
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
221
229
  <script src="https://unpkg.com/lego-dom/main.js"></script>
222
230
 
223
- <!-- Your LegoJS component -->
231
+ <!-- Your Lego component -->
224
232
  <my-component></my-component>
225
233
 
226
234
  <!-- Your jQuery code -->
@@ -271,7 +279,7 @@ For maximum security:
271
279
 
272
280
  ## Browser Compatibility
273
281
 
274
- LegoJS works in all modern browsers:
282
+ Lego works in all modern browsers:
275
283
 
276
284
  - ✅ Chrome 63+
277
285
  - ✅ Firefox 63+