lego-dom 0.0.5 β†’ 0.0.8

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 (143) hide show
  1. package/.github/workflows/deploy-docs.yml +56 -0
  2. package/LICENSE +21 -0
  3. package/README.md +298 -355
  4. package/docs/.vitepress/config.js +107 -0
  5. package/docs/.vitepress/dist/404.html +22 -0
  6. package/docs/.vitepress/dist/api/define.html +35 -0
  7. package/docs/.vitepress/dist/api/directives.html +32 -0
  8. package/docs/.vitepress/dist/api/globals.html +27 -0
  9. package/docs/.vitepress/dist/api/index.html +25 -0
  10. package/docs/.vitepress/dist/api/lifecycle.html +38 -0
  11. package/docs/.vitepress/dist/api/route.html +34 -0
  12. package/docs/.vitepress/dist/api/vite-plugin.html +37 -0
  13. package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.js +11 -0
  14. package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.lean.js +1 -0
  15. package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.js +8 -0
  16. package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.lean.js +1 -0
  17. package/docs/.vitepress/dist/assets/api_globals.md.DOjt7AV0.js +3 -0
  18. package/docs/.vitepress/dist/assets/api_globals.md.DOjt7AV0.lean.js +1 -0
  19. package/docs/.vitepress/dist/assets/api_index.md.OS6h01ct.js +1 -0
  20. package/docs/.vitepress/dist/assets/api_index.md.OS6h01ct.lean.js +1 -0
  21. package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.js +14 -0
  22. package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.lean.js +1 -0
  23. package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.js +10 -0
  24. package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.lean.js +1 -0
  25. package/docs/.vitepress/dist/assets/api_vite-plugin.md.DNn9VhL5.js +13 -0
  26. package/docs/.vitepress/dist/assets/api_vite-plugin.md.DNn9VhL5.lean.js +1 -0
  27. package/docs/.vitepress/dist/assets/app.BG5s3B0P.js +1 -0
  28. package/docs/.vitepress/dist/assets/chunks/@localSearchIndexroot.DQmuWC2Z.js +1 -0
  29. package/docs/.vitepress/dist/assets/chunks/VPLocalSearchBox.BO-PSxt1.js +9 -0
  30. package/docs/.vitepress/dist/assets/chunks/framework.B7OFBR9X.js +19 -0
  31. package/docs/.vitepress/dist/assets/chunks/theme.DA-iSa9B.js +2 -0
  32. package/docs/.vitepress/dist/assets/examples_form.md.B3stGKbu.js +34 -0
  33. package/docs/.vitepress/dist/assets/examples_form.md.B3stGKbu.lean.js +1 -0
  34. package/docs/.vitepress/dist/assets/examples_index.md.BDEG_D4J.js +30 -0
  35. package/docs/.vitepress/dist/assets/examples_index.md.BDEG_D4J.lean.js +1 -0
  36. package/docs/.vitepress/dist/assets/examples_routing.md.bqZ9DjDK.js +338 -0
  37. package/docs/.vitepress/dist/assets/examples_routing.md.bqZ9DjDK.lean.js +1 -0
  38. package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DLXaUiop.js +13 -0
  39. package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DLXaUiop.lean.js +1 -0
  40. package/docs/.vitepress/dist/assets/examples_todo-app.md.D5RhZoo5.js +297 -0
  41. package/docs/.vitepress/dist/assets/examples_todo-app.md.D5RhZoo5.lean.js +1 -0
  42. package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CAjf03Lr.js +182 -0
  43. package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CAjf03Lr.lean.js +1 -0
  44. package/docs/.vitepress/dist/assets/guide_components.md.BIFWF1Hc.js +174 -0
  45. package/docs/.vitepress/dist/assets/guide_components.md.BIFWF1Hc.lean.js +1 -0
  46. package/docs/.vitepress/dist/assets/guide_contributing.md.BgbUN-Mr.js +1 -0
  47. package/docs/.vitepress/dist/assets/guide_contributing.md.BgbUN-Mr.lean.js +1 -0
  48. package/docs/.vitepress/dist/assets/guide_directives.md.Bi3ynu1d.js +140 -0
  49. package/docs/.vitepress/dist/assets/guide_directives.md.Bi3ynu1d.lean.js +1 -0
  50. package/docs/.vitepress/dist/assets/guide_getting-started.md.2Nr1lp2z.js +107 -0
  51. package/docs/.vitepress/dist/assets/guide_getting-started.md.2Nr1lp2z.lean.js +1 -0
  52. package/docs/.vitepress/dist/assets/guide_index.md.GvZq_Yf2.js +2 -0
  53. package/docs/.vitepress/dist/assets/guide_index.md.GvZq_Yf2.lean.js +1 -0
  54. package/docs/.vitepress/dist/assets/guide_lifecycle.md.B28j1OzS.js +304 -0
  55. package/docs/.vitepress/dist/assets/guide_lifecycle.md.B28j1OzS.lean.js +1 -0
  56. package/docs/.vitepress/dist/assets/guide_quick-start.md.CNk3VGTF.js +33 -0
  57. package/docs/.vitepress/dist/assets/guide_quick-start.md.CNk3VGTF.lean.js +1 -0
  58. package/docs/.vitepress/dist/assets/guide_reactivity.md.CVsaMaPv.js +135 -0
  59. package/docs/.vitepress/dist/assets/guide_reactivity.md.CVsaMaPv.lean.js +1 -0
  60. package/docs/.vitepress/dist/assets/guide_routing.md.DSpDP25o.js +193 -0
  61. package/docs/.vitepress/dist/assets/guide_routing.md.DSpDP25o.lean.js +1 -0
  62. package/docs/.vitepress/dist/assets/guide_sfc.md.CVUP66tS.js +187 -0
  63. package/docs/.vitepress/dist/assets/guide_sfc.md.CVUP66tS.lean.js +1 -0
  64. package/docs/.vitepress/dist/assets/guide_templating.md.BgCGe4aa.js +119 -0
  65. package/docs/.vitepress/dist/assets/guide_templating.md.BgCGe4aa.lean.js +1 -0
  66. package/docs/.vitepress/dist/assets/index.md.xV1taCED.js +23 -0
  67. package/docs/.vitepress/dist/assets/index.md.xV1taCED.lean.js +1 -0
  68. package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  69. package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  70. package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  71. package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  72. package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  73. package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  74. package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  75. package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  76. package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  77. package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  78. package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  79. package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  80. package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  81. package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  82. package/docs/.vitepress/dist/assets/style.eycE2Jhw.css +1 -0
  83. package/docs/.vitepress/dist/examples/form.html +58 -0
  84. package/docs/.vitepress/dist/examples/index.html +368 -0
  85. package/docs/.vitepress/dist/examples/routing.html +362 -0
  86. package/docs/.vitepress/dist/examples/sfc-showcase.html +37 -0
  87. package/docs/.vitepress/dist/examples/todo-app.html +321 -0
  88. package/docs/.vitepress/dist/guide/cdn-usage.html +206 -0
  89. package/docs/.vitepress/dist/guide/components.html +198 -0
  90. package/docs/.vitepress/dist/guide/contributing.html +25 -0
  91. package/docs/.vitepress/dist/guide/directives.html +164 -0
  92. package/docs/.vitepress/dist/guide/getting-started.html +131 -0
  93. package/docs/.vitepress/dist/guide/index.html +26 -0
  94. package/docs/.vitepress/dist/guide/lifecycle.html +328 -0
  95. package/docs/.vitepress/dist/guide/quick-start.html +57 -0
  96. package/docs/.vitepress/dist/guide/reactivity.html +159 -0
  97. package/docs/.vitepress/dist/guide/routing.html +217 -0
  98. package/docs/.vitepress/dist/guide/sfc.html +211 -0
  99. package/docs/.vitepress/dist/guide/templating.html +143 -0
  100. package/docs/.vitepress/dist/hashmap.json +1 -0
  101. package/docs/.vitepress/dist/index.html +47 -0
  102. package/docs/.vitepress/dist/logo.svg +38 -0
  103. package/docs/.vitepress/dist/vp-icons.css +1 -0
  104. package/docs/api/define.md +31 -0
  105. package/docs/api/directives.md +42 -0
  106. package/docs/api/globals.md +29 -0
  107. package/docs/api/index.md +29 -0
  108. package/docs/api/lifecycle.md +40 -0
  109. package/docs/api/route.md +37 -0
  110. package/docs/api/vite-plugin.md +58 -0
  111. package/docs/examples/form.md +42 -0
  112. package/docs/examples/index.md +104 -0
  113. package/docs/examples/routing.md +409 -0
  114. package/docs/examples/sfc-showcase.md +34 -0
  115. package/docs/examples/todo-app.md +383 -0
  116. package/docs/guide/cdn-usage.md +320 -0
  117. package/docs/guide/components.md +394 -0
  118. package/docs/guide/contributing.md +32 -0
  119. package/docs/guide/directives.md +430 -0
  120. package/docs/guide/getting-started.md +233 -0
  121. package/docs/guide/index.md +88 -0
  122. package/docs/guide/lifecycle.md +493 -0
  123. package/docs/guide/quick-start.md +46 -0
  124. package/docs/guide/reactivity.md +394 -0
  125. package/docs/guide/routing.md +373 -0
  126. package/docs/guide/sfc.md +381 -0
  127. package/docs/guide/templating.md +383 -0
  128. package/docs/index.md +126 -0
  129. package/docs/public/logo.svg +38 -0
  130. package/examples/vite-app/README.md +71 -0
  131. package/examples/vite-app/index.html +45 -0
  132. package/examples/vite-app/package.json +16 -0
  133. package/examples/vite-app/src/components/greeting-card.lego +41 -0
  134. package/examples/vite-app/src/components/sample-component.lego +75 -0
  135. package/examples/vite-app/src/main.js +11 -0
  136. package/examples/vite-app/vite.config.js +16 -0
  137. package/examples.js +99 -0
  138. package/package.json +34 -7
  139. package/parse-lego.js +119 -0
  140. package/parse-lego.test.js +107 -0
  141. package/vite-plugin.js +133 -0
  142. package/.ignore/auto.html +0 -135
  143. package/.ignore/test.html +0 -73
package/README.md CHANGED
@@ -1,523 +1,466 @@
1
- # Lego JS Library
1
+ # LegoJS
2
2
 
3
- Lego JS is a surgical, zero-dependency reactive library for building native Web Components. It skips the virtual DOM overhead, leveraging Proxies and Shadow DOM to provide a lightweight (<5KB) alternative to the heavy-duty framework status quo.
3
+ LegoJS is a tiny, zero-dependency JavaScript library for building reactive Web Components directly in the browser.
4
4
 
5
- ## πŸ— Core Concepts
5
+ The goal of LegoJS is **mental model simplicity**:
6
6
 
7
- ### 1. Reactive State (`studs`)
7
+ * No virtual DOM
8
+ * No compilation step required
9
+ * No JSX
10
+ * No framework-specific syntax to learn
8
11
 
9
- Lego components use a reactive state object called `studs`. State mutations trigger efficient, batched DOM updates via `requestAnimationFrame`β€”minimizing reflows without a complex reconciliation engine.
12
+ You write **HTML**, add a few **directives**, and LegoJS takes care of reactivity and updates.
10
13
 
11
- ### 2. Shadow DOM Encapsulation
14
+ This README is intentionally designed so that a developer can understand **everything they need** about LegoJS by reading this file alone.
12
15
 
13
- Styles and logic are scoped by default. Use the `self` selector inside a component's `<style>` tag to target the host element without worrying about CSS leakage or global collisions.
16
+ ---
14
17
 
15
- ### 3. Directives (`b-`)
18
+ ## Installation
16
19
 
17
- Native-feeling attributes (`b-if`, `b-for`, `b-sync`) bridge your data to the DOM. No JSX, no compilation required for core featuresβ€”just standard HTML.
18
-
19
- ## πŸš€ Quickstart
20
-
21
- ### πŸ“¦ Installation
22
-
23
- Drop the script and start building. No build step, no `npm install` fatigue.
20
+ The package name on npm is **`lego-dom`** (the name `legojs` was already taken).
24
21
 
22
+ ```bash
23
+ npm install lego-dom
25
24
  ```
26
- <script src="path/to/main.js"></script>
27
25
 
28
- ```
29
-
30
- ### πŸ›  Creating your first Component
31
-
32
- Define logic and layout in a standard `<template>`.
26
+ Or include it directly in the browser:
33
27
 
28
+ ```html
29
+ <script src="node_modules/lego-dom/main.js"></script>
34
30
  ```
35
- <!-- Define -->
36
- <template b-id="greeting-card">
37
- <style>
38
- self { display: block; padding: 20px; border-radius: 8px; background: #f0f4f8; }
39
- </style>
40
-
41
- <h2>Hello, {{ name }}!</h2>
42
- <button @click="showBio = !showBio">Toggle Bio</button>
43
- <p b-if="showBio">{{ bio }}</p>
44
- </template>
45
31
 
46
- <!-- Execute -->
47
- <greeting-card b-data="{ name: 'Alex', bio: 'Dev', showBio: false }"></greeting-card>
32
+ Once loaded, `Lego` is available globally.
48
33
 
49
- ```
50
-
51
- ## πŸ“– Directives Deep Dive
34
+ ---
52
35
 
53
- Directive
36
+ ## The Mental Model
54
37
 
55
- Type
38
+ Think of LegoJS like real Lego blocks:
56
39
 
57
- Description
40
+ * **Templates** define how a block looks
41
+ * **Studs** define the data attached to a block
42
+ * **Directives** snap data to the DOM
43
+ * **Changes to data automatically update the DOM**
58
44
 
59
- Example
45
+ There is no mounting, diffing, or reconciliation engine.
60
46
 
61
- `b-id`
47
+ You change JavaScript objects β†’ LegoJS updates the DOM.
62
48
 
63
- Definition
49
+ ---
64
50
 
65
- Registers the custom element tag name.
51
+ ## Defining a Component (Block)
66
52
 
67
- `<template b-id="my-tag">`
53
+ A component is defined using a standard HTML `<template>` with a `b-id`.
68
54
 
69
- `b-data`
55
+ ```html
56
+ <template b-id="hello-card">
57
+ <style>
58
+ self {
59
+ display: block;
60
+ padding: 1rem;
61
+ border: 1px solid #ccc;
62
+ }
63
+ </style>
70
64
 
71
- Initialization
65
+ <h2>Hello {{ name }}</h2>
66
+ <button @click="count++">Clicked {{ count }} times</button>
67
+ </template>
68
+ ```
72
69
 
73
- Sets the initial reactive state.
70
+ Use the component in HTML:
74
71
 
75
- `<my-tag b-data="{ count: 0 }">`
72
+ ```html
73
+ <hello-card b-data="{ name: 'Ahmed', count: 0 }"></hello-card>
74
+ ```
76
75
 
77
- `b-sync`
76
+ ---
78
77
 
79
- Two-way
78
+ ## Reactive State (`studs`)
80
79
 
81
- Bi-directional binding for form inputs.
80
+ Each component has a reactive state object internally called **studs**.
82
81
 
83
- `<input b-sync="query">`
82
+ * Defined via `b-data` or component logic
83
+ * Implemented using JavaScript `Proxy`
84
+ * Any mutation automatically schedules a re-render
84
85
 
85
- `b-for`
86
+ ```html
87
+ <button @click="count++"></button>
88
+ ```
86
89
 
87
- Structural
90
+ No setters. No actions. No reducers.
88
91
 
89
- High-performance array looping.
92
+ Just mutate data.
90
93
 
91
- `<li b-for="u in users">{{ u.name }}</li>`
94
+ ---
92
95
 
93
- `b-if`
96
+ ## Templating (`{{ }}`)
94
97
 
95
- Structural
98
+ Text interpolation works in:
96
99
 
97
- Toggles presence based on truthiness.
100
+ * Text nodes
101
+ * Attributes
102
+ * Class names
98
103
 
99
- `<div b-if="isAdmin">Admin</div>`
104
+ ```html
105
+ <p>Hello {{ user.name }}</p>
106
+ <img src="/avatars/{{ user.id }}.png">
107
+ ```
100
108
 
101
- `b-text`
109
+ Expressions are plain JavaScript.
102
110
 
103
- Binding
111
+ ---
104
112
 
105
- Escaped text binding (one-way).
113
+ ## Event Handling (`@event`)
106
114
 
107
- `<span b-text="title"></span>`
115
+ Use `@` followed by any DOM event.
108
116
 
109
- `@event`
117
+ ```html
118
+ <button @click="submit()">Submit</button>
119
+ ```
110
120
 
111
- Event
121
+ The expression runs in the component’s state scope.
112
122
 
113
- Standard DOM event listeners.
123
+ You also have access to:
114
124
 
115
- `<button @click="run()">`
125
+ * `event` – the native DOM event
126
+ * `$emit(name, detail)` – dispatch custom events
127
+ * `$element` – the host custom element
116
128
 
117
- ## πŸ“ Advanced Example: Todo List
129
+ ---
118
130
 
119
- Demonstrating `b-for`, event handling, and two-way binding in a single block.
131
+ ## Conditional Rendering (`b-if`)
120
132
 
133
+ ```html
134
+ <p b-if="isLoggedIn">Welcome back</p>
121
135
  ```
122
- <template b-id="todo-app">
123
- <style>
124
- .done { text-decoration: line-through; opacity: 0.6; }
125
- ul { list-style: none; padding: 0; }
126
- </style>
127
136
 
128
- <h3>Tasks ({{ tasks.filter(t => !t.done).length }} left)</h3>
129
-
130
- <input b-sync="newTask" placeholder="Add task...">
131
- <button @click="tasks.push({text: newTask, done: false}); newTask=''">Add</button>
132
-
133
- <ul>
134
- <li b-for="task in tasks">
135
- <input type="checkbox" b-sync="task.done">
136
- <span class="{{ task.done ? 'done' : '' }}">{{ task.text }}</span>
137
- </li>
138
- </ul>
139
- </template>
137
+ When the expression is falsy, the element is hidden via `display: none`.
140
138
 
141
- <script>
142
- export default {
143
- tasks: [{ text: 'Native Components', done: true }, { text: 'Profit', done: false }],
144
- newTask: ''
145
- }
146
- </script>
139
+ ---
147
140
 
148
- ```
141
+ ## Lists (`b-for`)
149
142
 
150
- ## πŸ— Tooling & Ecosystem
143
+ Render lists using `b-for`:
151
144
 
145
+ ```html
146
+ <ul>
147
+ <li b-for="todo in todos">
148
+ <input type="checkbox" b-sync="todo.done">
149
+ <span class="{{ todo.done ? 'done' : '' }}">{{ todo.text }}</span>
150
+ </li>
151
+ </ul>
152
+ ```
152
153
 
153
- # πŸ“¦ Lego Single File Components (.lego)
154
+ * DOM nodes are reused
155
+ * Items are tracked internally
156
+ * Updates are efficient without a virtual DOM
154
157
 
155
- Single File Components (SFCs) are the professional standard for building with Lego JS. They allow you to co-locate your markup, scoped CSS, and reactive logic in a single `.lego` file, providing a clean separation of concerns without the mental overhead of switching between multiple files.
158
+ ---
156
159
 
157
- ## πŸ— Anatomy of a .lego File
160
+ ## Two-Way Binding (`b-sync`)
158
161
 
159
- A `.lego` file is composed of three top-level blocks: `<template>`, `<style>`, and `<script>`.
162
+ `b-sync` keeps inputs and state in sync.
160
163
 
164
+ ```html
165
+ <input b-sync="username">
166
+ <input type="checkbox" b-sync="settings.enabled">
161
167
  ```
162
- <!-- user-profile.lego -->
163
- <template>
164
- <div class="profile-card">
165
- <img src="{{ avatar }}" alt="{{ name }}">
166
- <h2>{{ name }}</h2>
167
- <p>{{ bio }}</p>
168
- <button @click="poke">Poke {{ name }}</button>
169
- </div>
170
- </template>
171
168
 
172
- <style>
173
- self {
174
- display: block;
175
- border: 1px solid #ddd;
176
- border-radius: 8px;
177
- padding: 1rem;
178
- text-align: center;
179
- }
180
- img {
181
- width: 80px;
182
- height: 80px;
183
- border-radius: 50%;
184
- }
185
- h2 { color: #333; }
186
- </style>
169
+ Works with:
187
170
 
188
- <script>
189
- export default {
190
- // Initial data state
191
- avatar: 'default.png',
192
- name: 'Anonymous',
193
- bio: 'No bio provided.',
194
-
195
- // Logic methods
196
- poke() {
197
- console.log(`${this.name} was poked!`);
198
- this.$emit('poked', { name: this.name });
199
- },
200
-
201
- // Lifecycle hooks
202
- mounted() {
203
- console.log('Profile component is now in the DOM');
204
- }
205
- }
206
- </script>
207
-
208
- ```
171
+ * text inputs
172
+ * checkboxes
173
+ * nested objects
174
+ * items inside `b-for`
209
175
 
210
- ## 🎨 Scoped Styling
176
+ ---
211
177
 
212
- Styles defined inside a `.lego` file are automatically scoped to that component using the **Shadow DOM**.
178
+ ## Styling and Shadow DOM
213
179
 
214
- - **The `self` selector**: Use `self` to target the host element of the component itself (the custom tag). In the final build, this is converted to the `:host` CSS selector.
215
-
216
- - **No Leakage**: Styles defined here will not leak out to the parent page, and global styles (except CSS variables) will not leak into the component.
217
-
180
+ Every component uses **Shadow DOM** automatically.
218
181
 
219
- ## βš™οΈ Component Logic (`export default`)
182
+ Inside `<style>` blocks:
220
183
 
221
- The `<script>` block must `export default` a plain JavaScript object. This object defines the component's state and behavior.
184
+ * Use `self` to target the component root
185
+ * `self` is converted to `:host`
222
186
 
223
- ### 1. Reactive Data
224
-
225
- Any property defined in the exported object becomes part of the reactive `data` proxy. When you change `this.username = 'NewName'`, the template updates automatically.
226
-
227
- ### 2. Lifecycle Hooks
228
-
229
- Lego JS provides specific hooks to manage the component's existence:
187
+ ```css
188
+ self {
189
+ display: block;
190
+ }
191
+ ```
230
192
 
231
- Hook
193
+ Styles never leak in or out.
232
194
 
233
- Description
195
+ ---
234
196
 
235
- `mounted()`
197
+ ## Lifecycle Hooks
236
198
 
237
- Called after the component is attached to the DOM and its shadow root is initialized.
199
+ Define lifecycle methods directly on the component state:
238
200
 
239
- `updated()`
201
+ ```js
202
+ {
203
+ mounted() {
204
+ console.log('Component attached');
205
+ },
206
+ updated() {
207
+ console.log('State changed');
208
+ },
209
+ unmounted() {
210
+ console.log('Component removed');
211
+ }
212
+ }
213
+ ```
240
214
 
241
- Called after the state changes and the DOM has been patched via the batcher.
215
+ ---
242
216
 
243
- `unmounted()`
217
+ ## Custom Events (`$emit`)
244
218
 
245
- Called just before the component is removed from the DOM. Use for cleanup (e.g., clearing timers).
219
+ Child components communicate upward using events.
246
220
 
247
- ## ✨ Magic Helpers ($)
221
+ ```html
222
+ <button @click="$emit('save', data)">Save</button>
223
+ ```
248
224
 
249
- Lego JS provides several "magic" variables available directly in your templates and event handlers to speed up development.
225
+ Events:
250
226
 
251
- ### `$element`
227
+ * bubble
228
+ * cross Shadow DOM boundaries
229
+ * are standard `CustomEvent`s
252
230
 
253
- Refers to the host custom element itself (the root of the component).
231
+ ---
254
232
 
255
- ### `$refs`
233
+ ## Accessing Ancestors (`$ancestors`)
256
234
 
257
- Provides quick access to DOM elements within the component's Shadow DOM that have a `ref` attribute.
235
+ Read state from the nearest ancestor component:
258
236
 
259
- ### `$emit(eventName, detail)`
237
+ ```html
238
+ <p>{{ $ancestors('app-shell').user.name }}</p>
239
+ ```
260
240
 
261
- A shorthand for dispatching custom events. By default, these events have `bubbles: true` and `composed: true`, allowing them to pass through multiple levels of Shadow DOM.
241
+ This is intended for **reading**, not mutation.
262
242
 
263
- ### `$ancestors(id)`
243
+ ---
264
244
 
265
- Searches up the DOM tree for the nearest component matching the provided tag name or `b-id`.
245
+ ## Shared State (`$registry`)
266
246
 
267
- ### `$event` and `$self`
247
+ Components defined via `Lego.define` get a shared singleton state.
268
248
 
269
- - `$event`: The native DOM event object.
270
-
271
- - `$self`: The specific element that triggered the event (alias for `event.target`).
272
-
249
+ ```js
250
+ $registry('settings').theme
251
+ ```
273
252
 
274
- ## πŸ“‘ The "One Way" Pattern (Data Flow)
253
+ Useful for global configuration or app-wide state.
275
254
 
276
- While Lego JS allows deep tree access, the **only** recommended way to handle state updates in large apps is **Data Down, Events Up**. This prevents "mutant state" where you don't know which child changed a value.
255
+ ---
277
256
 
278
- ### ❌ The "Bad" Way (Direct Mutation)
257
+ ## Router
279
258
 
280
- Avoid having children directly change an ancestor's data. It makes debugging impossible.
259
+ LegoJS includes a minimal client-side router.
281
260
 
282
- ```
283
- <!-- child.lego: DON'T DO THIS -->
284
- <button @click="$ancestors('app-shell').user.age++">Update Age</button>
261
+ Add a router outlet:
285
262
 
263
+ ```html
264
+ <lego-router></lego-router>
286
265
  ```
287
266
 
288
- ### βœ… The "Lego" Way (Generic Update Pattern)
289
-
290
- If you have many fields, you don't need unique event handlers for each. Use a single generic update event.
291
-
292
- 1. **Child** sends the field name and the new value:
293
-
294
- ```
295
- <!-- settings-child.lego -->
296
- <input type="text"
297
- value="{{ $ancestors('app-shell').user.name }}"
298
- @input="$emit('update-field', { key: 'name', value: $self.value })">
299
-
300
- <input type="number"
301
- value="{{ $ancestors('app-shell').user.age }}"
302
- @input="$emit('update-field', { key: 'age', value: $self.value })">
303
-
304
- ```
305
-
306
- 2. **Ancestor** handles all updates in one function:
307
-
308
- ```
309
- <!-- grand-parent.lego -->
310
- <template>
311
- <div @update-field="handleFieldUpdate">
312
- <settings-child></settings-child>
313
- </div>
314
- </template>
315
-
316
- <script>
317
- export default {
318
- user: { name: 'John', age: 30 },
319
- handleFieldUpdate(event) {
320
- const { key, value } = event.detail;
321
- // One place to validate everything
322
- if (this.user.hasOwnProperty(key)) {
323
- this.user[key] = value;
324
- }
325
- }
326
- }
327
- </script>
328
-
329
- ```
330
-
331
-
332
- ## πŸš€ The Build Process (Vite)
333
-
334
- To use `.lego` files, you must use the `Lego.vitePlugin()`. This plugin transforms your HTML-like file into a standard JavaScript module that registers the component with the `Lego.define` API.
335
-
336
- ### Configuration
337
-
338
- In your `vite.config.js`:
267
+ Define routes:
339
268
 
269
+ ```js
270
+ Lego.route('/', 'home-page');
271
+ Lego.route('/user/:id', 'user-page');
340
272
  ```
341
- import { defineConfig } from 'vite';
342
- import { Lego } from './path/to/lego.js';
343
273
 
344
- export default defineConfig({
345
- plugins: [Lego.vitePlugin()]
346
- });
274
+ Access route params:
347
275
 
276
+ ```html
277
+ <p>User ID: {{ global.params.id }}</p>
348
278
  ```
349
279
 
350
- ## πŸ’‘ Best Practices
280
+ Navigation:
351
281
 
352
- 1. **Naming**: Use multi-word names for components to avoid collisions.
353
-
354
- 2. **Read-Only Ancestors**: Use `$ancestors()` primarily for **reading** values.
355
-
356
- 3. **Intent-based Events**: For complex logic (e.g., `submit-order`), use a specific event. For simple data syncing, use a generic `update-field` event.
282
+ ```html
283
+ <a href="/dashboard" b-link>Dashboard</a>
284
+ ```
357
285
 
286
+ ---
358
287
 
288
+ ## Programmatic Navigation
359
289
 
360
- # πŸ›£ Lego JS Routing Guide
290
+ ```js
291
+ history.pushState({}, '', '/success');
292
+ window.dispatchEvent(new PopStateEvent('popstate'));
293
+ ```
361
294
 
362
- The Lego JS router is a lightweight client-side routing solution that leverages the browser's History API. It allows you to build Single Page Applications (SPAs) by mapping URL paths to specific Web Components (Blocks).
295
+ ---
363
296
 
364
- ## 🧩 The Router Viewport
297
+ ## Defining Components in JavaScript
365
298
 
366
- To use routing, you must place the `<lego-router>` element in your HTML. This acts as the container where the matched component will be rendered.
299
+ You can also define components programmatically:
367
300
 
301
+ ```js
302
+ Lego.define(
303
+ 'counter-box',
304
+ `
305
+ <style>self { display:block }</style>
306
+ <button @click="count++">{{ count }}</button>
307
+ `,
308
+ { count: 0 }
309
+ );
368
310
  ```
369
- <body>
370
- <nav>
371
- <a href="/" b-link>Home</a>
372
- <a href="/profile/123" b-link>Profile</a>
373
- </nav>
374
311
 
375
- <!-- Routed components appear here -->
376
- <lego-router></lego-router>
377
- </body>
312
+ ---
378
313
 
379
- ```
314
+ ## Initialization
380
315
 
381
- ## πŸ“ Basic Route Definition
316
+ LegoJS initializes automatically on `DOMContentLoaded`.
382
317
 
383
- Routes are defined using `Lego.route(path, componentName)`.
318
+ You usually do **not** need to call anything manually.
384
319
 
385
- ```
386
- // Map the root path to 'home-page' block
387
- Lego.route('/', 'home-page');
320
+ ---
388
321
 
389
- // Map /about to 'about-page' block
390
- Lego.route('/about', 'about-page');
322
+ ## Design Philosophy
391
323
 
392
- ```
324
+ LegoJS is intentionally small and opinionated:
393
325
 
394
- ## εŠ¨ζ€ Dynamic Route Parameters
326
+ * The DOM is the source of truth
327
+ * JavaScript objects are the state
328
+ * HTML stays HTML
329
+ * Complexity is avoided unless absolutely necessary
395
330
 
396
- You can define dynamic segments in your paths using the `:` prefix. These parameters are automatically parsed and injected into `Lego.globals.params`.
331
+ If you can explain your UI with plain objects and markup, LegoJS will feel natural.
397
332
 
398
- ### Definition
333
+ ---
399
334
 
400
- ```
401
- Lego.route('/user/:id', 'user-profile');
402
- Lego.route('/post/:category/:slug', 'blog-post');
335
+ ## Single File Components (SFC)
403
336
 
404
- ```
337
+ LegoJS supports **Single File Components** using `.lego` files when using a build tool like Vite.
405
338
 
406
- ### Usage inside a Component
339
+ ### .lego File Format
407
340
 
408
- Any component can access these parameters via the `global` object.
341
+ A `.lego` file contains three optional sections:
409
342
 
410
- ```
411
- <template b-id="user-profile">
412
- <div>
413
- <h2>User Profile</h2>
414
- <p>Viewing ID: {{ global.params.id }}</p>
415
- </div>
343
+ ```html
344
+ <template>
345
+ <!-- Your HTML markup with directives -->
346
+ <h1>{{ title }}</h1>
347
+ <button @click="count++">{{ count }}</button>
416
348
  </template>
417
349
 
350
+ <script>
351
+ export default {
352
+ // Your component logic/state
353
+ title: 'Hello',
354
+ count: 0
355
+ }
356
+ </script>
357
+
358
+ <style>
359
+ /* Scoped CSS using self keyword */
360
+ self {
361
+ display: block;
362
+ padding: 1rem;
363
+ }
364
+ </style>
418
365
  ```
419
366
 
420
- ## πŸ›‘ Navigation Middleware (Authentication)
367
+ The component name is automatically derived from the filename (e.g., `sample-component.lego` β†’ `<sample-component>`).
421
368
 
422
- Middleware allows you to guard routes. The middleware function is executed before the component is rendered. If it returns `false`, the navigation is cancelled.
369
+ ---
423
370
 
424
- ### Example: Auth Guard
371
+ ## Vite Plugin Setup
425
372
 
426
- ```
427
- Lego.route('/admin', 'admin-dashboard', async (params, globals) => {
428
- const isAuthorized = globals.user && globals.user.role === 'admin';
429
-
430
- if (!isAuthorized) {
431
- // Redirect to login or home
432
- history.pushState({}, '', '/login');
433
- // We must manually trigger the router check after pushState
434
- Lego.init();
435
- return false; // Prevent 'admin-dashboard' from loading
436
- }
437
-
438
- return true; // Proceed to load the component
439
- });
373
+ ### Installation
440
374
 
375
+ ```bash
376
+ npm install lego-dom vite
441
377
  ```
442
378
 
443
- ## πŸ”— Internal Navigation (`b-link`)
444
-
445
- To navigate without a full page refresh, use the `b-link` directive on anchor tags. This intercepts the click, updates the URL via `pushState`, and tells the Lego router to swap the view.
379
+ ### Configuration
446
380
 
447
- ```
448
- <!-- This will trigger the router logic -->
449
- <a href="/dashboard" b-link>Go to Dashboard</a>
381
+ Create `vite.config.js`:
450
382
 
451
- <!-- This will trigger a standard browser reload -->
452
- <a href="/external-site.com">External Link</a>
383
+ ```js
384
+ import { defineConfig } from 'vite';
385
+ import legoPlugin from 'lego-dom/vite-plugin';
453
386
 
387
+ export default defineConfig({
388
+ plugins: [
389
+ legoPlugin({
390
+ componentsDir: './src/components', // Where to look for .lego files
391
+ include: ['**/*.lego'] // Glob patterns to match
392
+ })
393
+ ]
394
+ });
454
395
  ```
455
396
 
456
- ## πŸ— Programmatic Navigation
397
+ ### Usage
457
398
 
458
- Sometimes you need to navigate via JavaScript (e.g., after a successful form submission). Use the standard `history.pushState` but remember that the library listens for the `popstate` event. To trigger a render manually, you can call the internal route matcher or simply use a helper:
399
+ Create your components in `.lego` files:
459
400
 
460
401
  ```
461
- // In your component logic
462
- submitForm() {
463
- // Save data...
464
- history.pushState({}, '', '/success');
465
- // Dispatch popstate so the router hears it
466
- window.dispatchEvent(new PopStateEvent('popstate'));
467
- }
468
-
402
+ src/
403
+ components/
404
+ my-button.lego
405
+ user-card.lego
406
+ main.js
407
+ index.html
469
408
  ```
470
409
 
471
- ## πŸ“‚ Large Scale Structure
410
+ In your `main.js`:
472
411
 
473
- For larger projects, define your routes in a dedicated initialization file and ensure your page components are registered before the router starts.
412
+ ```js
413
+ import { Lego } from 'lego-dom/main.js';
414
+ import registerComponents from 'virtual:lego-components';
474
415
 
416
+ registerComponents();
475
417
  ```
476
- // router-config.js
477
- import './views/home.lego';
478
- import './views/login.lego';
479
- import './views/profile.lego';
480
-
481
- export const setupRouter = () => {
482
- Lego.route('/', 'home-view');
483
- Lego.route('/login', 'login-view');
484
-
485
- // Nested logic for clean organization
486
- Lego.route('/profile/:username', 'profile-view', (params) => {
487
- return !!params.username; // Basic validation
488
- });
489
- };
490
418
 
491
- ```
419
+ In your HTML:
492
420
 
493
- ## πŸ“ Router State Summary
421
+ ```html
422
+ <my-button></my-button>
423
+ <user-card></user-card>
424
+ ```
494
425
 
495
- Feature
426
+ **Auto-discovery**: The Vite plugin automatically finds all `.lego` files and registers them with LegoJS!
496
427
 
497
- Syntax
428
+ ---
498
429
 
499
- Description
430
+ ## Two Usage Modes
500
431
 
501
- **Viewport**
432
+ LegoJS works in **two modes**:
502
433
 
503
- `<lego-router>`
434
+ ### 1. Without Build Tooling
504
435
 
505
- Where the content renders.
436
+ Include `main.js` directly and use `<template b-id>` or `Lego.define()`:
506
437
 
507
- **Directive**
438
+ ```html
439
+ <script src="node_modules/lego-dom/main.js"></script>
440
+ <template b-id="my-component">
441
+ <h1>Hello</h1>
442
+ </template>
443
+ ```
508
444
 
509
- `b-link`
445
+ ### 2. With Vite (SFC)
510
446
 
511
- Intercepts link clicks.
447
+ Use `.lego` files that are auto-discovered and compiled:
512
448
 
513
- **Globals**
449
+ ```bash
450
+ npm run dev
451
+ ```
514
452
 
515
- `global.params`
453
+ Both modes use the same LegoJS runtime and support all the same features!
516
454
 
517
- Object containing URL variables.
455
+ ---
518
456
 
519
- **Middleware**
457
+ ## Summary
520
458
 
521
- `(params, globals) => boolean`
459
+ * Install with `npm install lego-dom`
460
+ * Define components with `<template b-id>`
461
+ * Use `b-data` for state
462
+ * Use `{{ }}` for binding
463
+ * Use `@event` for logic
464
+ * Use `b-if`, `b-for`, and `b-sync` for structure
522
465
 
523
- Logic to permit/block access.
466
+ That’s it.