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.
- package/CHANGELOG.md +44 -0
- package/README.md +49 -432
- package/cdn.html +124 -0
- package/docs/.vitepress/config.js +43 -5
- package/docs/api/directives.md +3 -3
- package/docs/api/globals.md +1 -1
- package/docs/api/index.md +3 -3
- package/docs/api/vite-plugin.md +1 -1
- package/docs/contributing/01-welcome.md +36 -0
- package/docs/contributing/02-registry.md +99 -0
- package/docs/contributing/03-batcher.md +110 -0
- package/docs/contributing/04-reactivity.md +87 -0
- package/docs/contributing/05-caching.md +59 -0
- package/docs/contributing/06-init.md +125 -0
- package/docs/contributing/07-observer.md +69 -0
- package/docs/contributing/08-snap.md +126 -0
- package/docs/contributing/09-diffing.md +69 -0
- package/docs/contributing/10-studs.md +76 -0
- package/docs/contributing/11-scanner.md +104 -0
- package/docs/contributing/12-render.md +116 -0
- package/docs/contributing/13-directives.md +225 -0
- package/docs/contributing/14-events.md +57 -0
- package/docs/contributing/15-router.md +9 -0
- package/docs/contributing/16-state.md +48 -0
- package/docs/contributing/17-legodom.md +55 -0
- package/docs/contributing/index.md +5 -0
- package/docs/examples/form.md +2 -2
- package/docs/examples/index.md +4 -4
- package/docs/examples/routing.md +8 -8
- package/docs/examples/sfc-showcase.md +4 -4
- package/docs/examples/todo-app.md +3 -3
- package/docs/guide/cdn-usage.md +16 -8
- package/docs/guide/components.md +34 -16
- package/docs/guide/contributing.md +2 -2
- package/docs/guide/directives.md +23 -23
- package/docs/guide/getting-started.md +41 -16
- package/docs/guide/index.md +12 -12
- package/docs/guide/lifecycle.md +1 -1
- package/docs/guide/quick-start.md +8 -5
- package/docs/guide/reactivity.md +30 -9
- package/docs/guide/routing.md +189 -289
- package/docs/guide/sfc.md +40 -40
- package/docs/guide/templating.md +4 -4
- package/docs/index.md +48 -14
- package/docs/public/logo.svg +17 -38
- package/docs/router/basic-routing.md +103 -0
- package/docs/router/cold-entry.md +91 -0
- package/docs/router/history.md +69 -0
- package/docs/router/index.md +73 -0
- package/docs/router/resolver.md +74 -0
- package/docs/router/surgical-swaps.md +134 -0
- package/examples/vite-app/README.md +2 -2
- package/examples/vite-app/index.html +9 -13
- package/examples/vite-app/package.json +4 -2
- package/examples/vite-app/src/app.css +3 -0
- package/examples/vite-app/src/app.js +29 -0
- package/examples/vite-app/src/components/app-navbar.lego +34 -0
- package/examples/vite-app/src/components/customers/customer-details.lego +24 -0
- package/examples/vite-app/src/components/customers/customer-orders.lego +21 -0
- package/examples/vite-app/src/components/customers/order-list.lego +55 -0
- package/examples/vite-app/src/components/greeting-card.lego +26 -26
- package/examples/vite-app/src/components/sample-component.lego +58 -58
- package/examples/vite-app/src/components/shells/customers-shell.lego +21 -0
- package/examples/vite-app/src/components/todo-list.lego +239 -0
- package/examples/vite-app/src/components/widgets/user-card.lego +27 -0
- package/examples/vite-app/vite.config.js +7 -2
- package/lego.js +2 -0
- package/main.js +280 -83
- package/package.json +8 -3
- package/parse-lego.js +17 -8
- package/parse-lego.test.js +1 -1
- package/{main.test.js β tests/main.test.js} +34 -17
- package/tests/parse-lego.test.js +65 -0
- package/vite-plugin.js +62 -24
- package/docs/.vitepress/dist/404.html +0 -22
- package/docs/.vitepress/dist/api/define.html +0 -35
- package/docs/.vitepress/dist/api/directives.html +0 -32
- package/docs/.vitepress/dist/api/globals.html +0 -27
- package/docs/.vitepress/dist/api/index.html +0 -25
- package/docs/.vitepress/dist/api/lifecycle.html +0 -38
- package/docs/.vitepress/dist/api/route.html +0 -34
- package/docs/.vitepress/dist/api/vite-plugin.html +0 -37
- package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.js +0 -11
- package/docs/.vitepress/dist/assets/api_define.md.UA-ygUnQ.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.js +0 -8
- package/docs/.vitepress/dist/assets/api_directives.md.BV-D251p.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_globals.md.DOjt7AV0.js +0 -3
- package/docs/.vitepress/dist/assets/api_globals.md.DOjt7AV0.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_index.md.OS6h01ct.js +0 -1
- package/docs/.vitepress/dist/assets/api_index.md.OS6h01ct.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.js +0 -14
- package/docs/.vitepress/dist/assets/api_lifecycle.md.Ccm5xw6-.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.js +0 -10
- package/docs/.vitepress/dist/assets/api_route.md.CAHf_KNp.lean.js +0 -1
- package/docs/.vitepress/dist/assets/api_vite-plugin.md.DNn9VhL5.js +0 -13
- package/docs/.vitepress/dist/assets/api_vite-plugin.md.DNn9VhL5.lean.js +0 -1
- package/docs/.vitepress/dist/assets/app.BG5s3B0P.js +0 -1
- package/docs/.vitepress/dist/assets/chunks/@localSearchIndexroot.DQmuWC2Z.js +0 -1
- package/docs/.vitepress/dist/assets/chunks/VPLocalSearchBox.BO-PSxt1.js +0 -9
- package/docs/.vitepress/dist/assets/chunks/framework.B7OFBR9X.js +0 -19
- package/docs/.vitepress/dist/assets/chunks/theme.DA-iSa9B.js +0 -2
- package/docs/.vitepress/dist/assets/examples_form.md.B3stGKbu.js +0 -34
- package/docs/.vitepress/dist/assets/examples_form.md.B3stGKbu.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_index.md.BDEG_D4J.js +0 -30
- package/docs/.vitepress/dist/assets/examples_index.md.BDEG_D4J.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_routing.md.bqZ9DjDK.js +0 -338
- package/docs/.vitepress/dist/assets/examples_routing.md.bqZ9DjDK.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DLXaUiop.js +0 -13
- package/docs/.vitepress/dist/assets/examples_sfc-showcase.md.DLXaUiop.lean.js +0 -1
- package/docs/.vitepress/dist/assets/examples_todo-app.md.D5RhZoo5.js +0 -297
- package/docs/.vitepress/dist/assets/examples_todo-app.md.D5RhZoo5.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CAjf03Lr.js +0 -182
- package/docs/.vitepress/dist/assets/guide_cdn-usage.md.CAjf03Lr.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_components.md.BIFWF1Hc.js +0 -174
- package/docs/.vitepress/dist/assets/guide_components.md.BIFWF1Hc.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_contributing.md.BgbUN-Mr.js +0 -1
- package/docs/.vitepress/dist/assets/guide_contributing.md.BgbUN-Mr.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_directives.md.Bi3ynu1d.js +0 -140
- package/docs/.vitepress/dist/assets/guide_directives.md.Bi3ynu1d.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_getting-started.md.2Nr1lp2z.js +0 -107
- package/docs/.vitepress/dist/assets/guide_getting-started.md.2Nr1lp2z.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_index.md.GvZq_Yf2.js +0 -2
- package/docs/.vitepress/dist/assets/guide_index.md.GvZq_Yf2.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_lifecycle.md.B28j1OzS.js +0 -304
- package/docs/.vitepress/dist/assets/guide_lifecycle.md.B28j1OzS.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_quick-start.md.CNk3VGTF.js +0 -33
- package/docs/.vitepress/dist/assets/guide_quick-start.md.CNk3VGTF.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_reactivity.md.CVsaMaPv.js +0 -135
- package/docs/.vitepress/dist/assets/guide_reactivity.md.CVsaMaPv.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_routing.md.DSpDP25o.js +0 -193
- package/docs/.vitepress/dist/assets/guide_routing.md.DSpDP25o.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_sfc.md.CVUP66tS.js +0 -187
- package/docs/.vitepress/dist/assets/guide_sfc.md.CVUP66tS.lean.js +0 -1
- package/docs/.vitepress/dist/assets/guide_templating.md.BgCGe4aa.js +0 -119
- package/docs/.vitepress/dist/assets/guide_templating.md.BgCGe4aa.lean.js +0 -1
- package/docs/.vitepress/dist/assets/index.md.xV1taCED.js +0 -23
- package/docs/.vitepress/dist/assets/index.md.xV1taCED.lean.js +0 -1
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/.vitepress/dist/assets/style.eycE2Jhw.css +0 -1
- package/docs/.vitepress/dist/examples/form.html +0 -58
- package/docs/.vitepress/dist/examples/index.html +0 -368
- package/docs/.vitepress/dist/examples/routing.html +0 -362
- package/docs/.vitepress/dist/examples/sfc-showcase.html +0 -37
- package/docs/.vitepress/dist/examples/todo-app.html +0 -321
- package/docs/.vitepress/dist/guide/cdn-usage.html +0 -206
- package/docs/.vitepress/dist/guide/components.html +0 -198
- package/docs/.vitepress/dist/guide/contributing.html +0 -25
- package/docs/.vitepress/dist/guide/directives.html +0 -164
- package/docs/.vitepress/dist/guide/getting-started.html +0 -131
- package/docs/.vitepress/dist/guide/index.html +0 -26
- package/docs/.vitepress/dist/guide/lifecycle.html +0 -328
- package/docs/.vitepress/dist/guide/quick-start.html +0 -57
- package/docs/.vitepress/dist/guide/reactivity.html +0 -159
- package/docs/.vitepress/dist/guide/routing.html +0 -217
- package/docs/.vitepress/dist/guide/sfc.html +0 -211
- package/docs/.vitepress/dist/guide/templating.html +0 -143
- package/docs/.vitepress/dist/hashmap.json +0 -1
- package/docs/.vitepress/dist/index.html +0 -47
- package/docs/.vitepress/dist/logo.svg +0 -38
- package/docs/.vitepress/dist/vp-icons.css +0 -1
- package/examples/vite-app/src/main.js +0 -11
- package/examples.js +0 -99
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.0.0] - 2026-01-10
|
|
6
|
+
|
|
7
|
+
**The Launch Release!** π
|
|
8
|
+
LegoDOM moves out of beta with a finalized API, robust routing, and a hybrid rendering engine.
|
|
9
|
+
|
|
10
|
+
### π Major features
|
|
11
|
+
|
|
12
|
+
- **Surgical Routing:** Introduced a groundbreaking `b-target` attribute that allows any link to update any part of the page without a full reload.
|
|
13
|
+
- **Smart History:** The router now tracks surgical updates in `history.state`, correctly restoring "fragment" states when using the Back/Forward buttons.
|
|
14
|
+
- **Persistent Layouts:** Support for "Holy Grail" layouts where sidebars never reload or lose state (scroll position, inputs) while main content changes.
|
|
15
|
+
- **Hybrid Rendering Engine:**
|
|
16
|
+
- **Light DOM Support:** The engine now hydrates `{{mustaches}}` in `index.html` and Light DOM slots, not just inside Shadow Roots.
|
|
17
|
+
- **Global Reactivity:** Components now automatically broadcast global state changes (like URL params) to all active subscribers.
|
|
18
|
+
- **Optimized Updates:** A dependency tracking system ensures only components that *use* global state are re-rendered.
|
|
19
|
+
- **Developer Experience (DX):**
|
|
20
|
+
- **Automatic Injection:** `$route` and `$go` are now injected into every component's script scope (`this.$route`, `this.$go`), removing the need for `Lego.globals.xxx`.
|
|
21
|
+
- **Cleaner Templates:** Template expressions now support `$route` directly (`{{ $route.params.id }}`) without `global.` prefix.
|
|
22
|
+
- **HMR 2.0:** The Vite plugin now correctly handles adding/deleting `.lego` files and performs smarter hot updates.
|
|
23
|
+
|
|
24
|
+
### β‘ Improvements
|
|
25
|
+
|
|
26
|
+
- **Router:**
|
|
27
|
+
- Exposed `$go(path, ...targets).get()` for programmatic surgical navigation.
|
|
28
|
+
- Fixed `TypeError` where `$go` was not found in component scopes.
|
|
29
|
+
- Support for multiple URL parameters (e.g., `/customers/:id/orders/:orderId`).
|
|
30
|
+
- Defaults `b-link` to `true` (always push history) unless explicitly set to `false`.
|
|
31
|
+
- **Core:**
|
|
32
|
+
- `Lego.init()` is now required to start the engine, allowing for explicit control over startup timing.
|
|
33
|
+
- `e.composedPath()` is used for event delegation, allowing links inside Shadow DOM to trigger the global router.
|
|
34
|
+
- Fixed `b-sync` losing reactivity inside `b-for` loops.
|
|
35
|
+
- Fixed hydration of attributes like `href="/user/{{id}}"`.
|
|
36
|
+
- **Docs:**
|
|
37
|
+
- Complete overhaul of Routing guide with "Surgical Swaps", "Deep Linking", and "Self-Healing" patterns.
|
|
38
|
+
- Clarified component naming conventions (Filename for Vite vs. `b-id` for CDN).
|
|
39
|
+
|
|
40
|
+
### π Bug Fixes
|
|
41
|
+
|
|
42
|
+
- Fixed "Literal Mustaches" appearing in `href` and text content on initial load.
|
|
43
|
+
- Fixed Deep Linking where hitting Refresh on a sub-route would render an empty shell (addressed via Self-Healing pattern).
|
|
44
|
+
- Fixed an issue where `Lego.globals` changes triggered a full re-render of unrelated components.
|
package/README.md
CHANGED
|
@@ -1,466 +1,83 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Lego π§±
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The tiny, zero-dependency library for building reactive Web Components.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Lego embraces the web platform. It turns standard HTML `<template>` tags into reactive, encapsulated custom elements with zero build steps required.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
* No compilation step required
|
|
9
|
-
* No JSX
|
|
10
|
-
* No framework-specific syntax to learn
|
|
11
|
-
|
|
12
|
-
You write **HTML**, add a few **directives**, and LegoJS takes care of reactivity and updates.
|
|
13
|
-
|
|
14
|
-
This README is intentionally designed so that a developer can understand **everything they need** about LegoJS by reading this file alone.
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## Installation
|
|
19
|
-
|
|
20
|
-
The package name on npm is **`lego-dom`** (the name `legojs` was already taken).
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
npm install lego-dom
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Or include it directly in the browser:
|
|
27
|
-
|
|
28
|
-
```html
|
|
29
|
-
<script src="node_modules/lego-dom/main.js"></script>
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Once loaded, `Lego` is available globally.
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## The Mental Model
|
|
37
|
-
|
|
38
|
-
Think of LegoJS like real Lego blocks:
|
|
39
|
-
|
|
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**
|
|
44
|
-
|
|
45
|
-
There is no mounting, diffing, or reconciliation engine.
|
|
46
|
-
|
|
47
|
-
You change JavaScript objects β LegoJS updates the DOM.
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## Defining a Component (Block)
|
|
52
|
-
|
|
53
|
-
A component is defined using a standard HTML `<template>` with a `b-id`.
|
|
54
|
-
|
|
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>
|
|
64
|
-
|
|
65
|
-
<h2>Hello {{ name }}</h2>
|
|
66
|
-
<button @click="count++">Clicked {{ count }} times</button>
|
|
67
|
-
</template>
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Use the component in HTML:
|
|
71
|
-
|
|
72
|
-
```html
|
|
73
|
-
<hello-card b-data="{ name: 'Ahmed', count: 0 }"></hello-card>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Reactive State (`studs`)
|
|
79
|
-
|
|
80
|
-
Each component has a reactive state object internally called **studs**.
|
|
81
|
-
|
|
82
|
-
* Defined via `b-data` or component logic
|
|
83
|
-
* Implemented using JavaScript `Proxy`
|
|
84
|
-
* Any mutation automatically schedules a re-render
|
|
85
|
-
|
|
86
|
-
```html
|
|
87
|
-
<button @click="count++"></button>
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
No setters. No actions. No reducers.
|
|
91
|
-
|
|
92
|
-
Just mutate data.
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
## Templating (`{{ }}`)
|
|
97
|
-
|
|
98
|
-
Text interpolation works in:
|
|
99
|
-
|
|
100
|
-
* Text nodes
|
|
101
|
-
* Attributes
|
|
102
|
-
* Class names
|
|
103
|
-
|
|
104
|
-
```html
|
|
105
|
-
<p>Hello {{ user.name }}</p>
|
|
106
|
-
<img src="/avatars/{{ user.id }}.png">
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Expressions are plain JavaScript.
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## Event Handling (`@event`)
|
|
114
|
-
|
|
115
|
-
Use `@` followed by any DOM event.
|
|
116
|
-
|
|
117
|
-
```html
|
|
118
|
-
<button @click="submit()">Submit</button>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
The expression runs in the componentβs state scope.
|
|
122
|
-
|
|
123
|
-
You also have access to:
|
|
124
|
-
|
|
125
|
-
* `event` β the native DOM event
|
|
126
|
-
* `$emit(name, detail)` β dispatch custom events
|
|
127
|
-
* `$element` β the host custom element
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
## Conditional Rendering (`b-if`)
|
|
132
|
-
|
|
133
|
-
```html
|
|
134
|
-
<p b-if="isLoggedIn">Welcome back</p>
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
When the expression is falsy, the element is hidden via `display: none`.
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## Lists (`b-for`)
|
|
142
|
-
|
|
143
|
-
Render lists using `b-for`:
|
|
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
|
-
```
|
|
153
|
-
|
|
154
|
-
* DOM nodes are reused
|
|
155
|
-
* Items are tracked internally
|
|
156
|
-
* Updates are efficient without a virtual DOM
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## Two-Way Binding (`b-sync`)
|
|
161
|
-
|
|
162
|
-
`b-sync` keeps inputs and state in sync.
|
|
163
|
-
|
|
164
|
-
```html
|
|
165
|
-
<input b-sync="username">
|
|
166
|
-
<input type="checkbox" b-sync="settings.enabled">
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
Works with:
|
|
170
|
-
|
|
171
|
-
* text inputs
|
|
172
|
-
* checkboxes
|
|
173
|
-
* nested objects
|
|
174
|
-
* items inside `b-for`
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## Styling and Shadow DOM
|
|
179
|
-
|
|
180
|
-
Every component uses **Shadow DOM** automatically.
|
|
181
|
-
|
|
182
|
-
Inside `<style>` blocks:
|
|
183
|
-
|
|
184
|
-
* Use `self` to target the component root
|
|
185
|
-
* `self` is converted to `:host`
|
|
186
|
-
|
|
187
|
-
```css
|
|
188
|
-
self {
|
|
189
|
-
display: block;
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Styles never leak in or out.
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## Lifecycle Hooks
|
|
198
|
-
|
|
199
|
-
Define lifecycle methods directly on the component state:
|
|
200
|
-
|
|
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
|
-
```
|
|
7
|
+
[**Explore the Docs**](https://rayattack.github.io/legodom/) | [**Examples**](https://rayattack.github.io/legodom/examples/) | [**GitHub**](https://github.com/rayattack/legodom)
|
|
214
8
|
|
|
215
9
|
---
|
|
216
10
|
|
|
217
|
-
##
|
|
218
|
-
|
|
219
|
-
Child components communicate upward using events.
|
|
11
|
+
## Why Lego?
|
|
220
12
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
* bubble
|
|
228
|
-
* cross Shadow DOM boundaries
|
|
229
|
-
* are standard `CustomEvent`s
|
|
13
|
+
- β‘ **Extremely Fast** β No virtual DOM. No reconciliation. Direct DOM updates.
|
|
14
|
+
- π¦ **Zero Dependencies** β Weighs less than 4kb gzipped.
|
|
15
|
+
- π οΈ **No Build Step** β Works directly in the browser with standard `<script>` tags.
|
|
16
|
+
- π§© **Native Web Components** β Real Custom Elements, real Shadow DOM.
|
|
17
|
+
- π **Built-in Routing** β Lego Router included for client-side routing.
|
|
18
|
+
- π **Familiar Mentals** β Plain JavaScript objects for state, plain HTML for templates.
|
|
230
19
|
|
|
231
20
|
---
|
|
232
21
|
|
|
233
|
-
##
|
|
234
|
-
|
|
235
|
-
Read state from the nearest ancestor component:
|
|
236
|
-
|
|
237
|
-
```html
|
|
238
|
-
<p>{{ $ancestors('app-shell').user.name }}</p>
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
This is intended for **reading**, not mutation.
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## Shared State (`$registry`)
|
|
246
|
-
|
|
247
|
-
Components defined via `Lego.define` get a shared singleton state.
|
|
248
|
-
|
|
249
|
-
```js
|
|
250
|
-
$registry('settings').theme
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
Useful for global configuration or app-wide state.
|
|
254
|
-
|
|
255
|
-
---
|
|
256
|
-
|
|
257
|
-
## Router
|
|
258
|
-
|
|
259
|
-
LegoJS includes a minimal client-side router.
|
|
260
|
-
|
|
261
|
-
Add a router outlet:
|
|
262
|
-
|
|
263
|
-
```html
|
|
264
|
-
<lego-router></lego-router>
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
Define routes:
|
|
268
|
-
|
|
269
|
-
```js
|
|
270
|
-
Lego.route('/', 'home-page');
|
|
271
|
-
Lego.route('/user/:id', 'user-page');
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
Access route params:
|
|
22
|
+
## Quick Start (No Build Required)
|
|
275
23
|
|
|
276
24
|
```html
|
|
277
|
-
|
|
278
|
-
|
|
25
|
+
<!DOCTYPE html>
|
|
26
|
+
<html>
|
|
27
|
+
<body>
|
|
28
|
+
<hello-world></hello-world>
|
|
279
29
|
|
|
280
|
-
|
|
30
|
+
<template b-id="hello-world">
|
|
31
|
+
<style>
|
|
32
|
+
h1 { color: #ffca28; font-family: sans-serif; }
|
|
33
|
+
</style>
|
|
34
|
+
<h1>Hello, {{ name }}!</h1>
|
|
35
|
+
<button @click="toggle()">Toggle Name</button>
|
|
36
|
+
</template>
|
|
281
37
|
|
|
282
|
-
|
|
283
|
-
<
|
|
38
|
+
<script src="https://unpkg.com/lego-dom/main.js"></script>
|
|
39
|
+
<script>
|
|
40
|
+
document.querySelector('hello-world').state = {
|
|
41
|
+
name: 'World',
|
|
42
|
+
toggle() {
|
|
43
|
+
this.name = this.name === 'World' ? 'Lego' : 'World';
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
</script>
|
|
47
|
+
</body>
|
|
48
|
+
</html>
|
|
284
49
|
```
|
|
285
50
|
|
|
286
51
|
---
|
|
287
52
|
|
|
288
|
-
##
|
|
289
|
-
|
|
290
|
-
```js
|
|
291
|
-
history.pushState({}, '', '/success');
|
|
292
|
-
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
---
|
|
296
|
-
|
|
297
|
-
## Defining Components in JavaScript
|
|
298
|
-
|
|
299
|
-
You can also define components programmatically:
|
|
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
|
-
);
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
---
|
|
313
|
-
|
|
314
|
-
## Initialization
|
|
315
|
-
|
|
316
|
-
LegoJS initializes automatically on `DOMContentLoaded`.
|
|
317
|
-
|
|
318
|
-
You usually do **not** need to call anything manually.
|
|
319
|
-
|
|
320
|
-
---
|
|
321
|
-
|
|
322
|
-
## Design Philosophy
|
|
323
|
-
|
|
324
|
-
LegoJS is intentionally small and opinionated:
|
|
325
|
-
|
|
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
|
|
330
|
-
|
|
331
|
-
If you can explain your UI with plain objects and markup, LegoJS will feel natural.
|
|
332
|
-
|
|
333
|
-
---
|
|
334
|
-
|
|
335
|
-
## Single File Components (SFC)
|
|
53
|
+
## Also Supports Modern Toolchains
|
|
336
54
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
### .lego File Format
|
|
340
|
-
|
|
341
|
-
A `.lego` file contains three optional sections:
|
|
55
|
+
Lego includes a **Vite plugin** for developers who prefer **Single File Components (.lego)**:
|
|
342
56
|
|
|
343
57
|
```html
|
|
58
|
+
<!-- user-card.lego -->
|
|
344
59
|
<template>
|
|
345
|
-
|
|
346
|
-
<h1>{{ title }}</h1>
|
|
347
|
-
<button @click="count++">{{ count }}</button>
|
|
60
|
+
<h1>{{ name }}</h1>
|
|
348
61
|
</template>
|
|
349
62
|
|
|
350
|
-
<script>
|
|
351
|
-
export default {
|
|
352
|
-
// Your component logic/state
|
|
353
|
-
title: 'Hello',
|
|
354
|
-
count: 0
|
|
355
|
-
}
|
|
356
|
-
</script>
|
|
357
|
-
|
|
358
63
|
<style>
|
|
359
|
-
|
|
360
|
-
self {
|
|
361
|
-
display: block;
|
|
362
|
-
padding: 1rem;
|
|
363
|
-
}
|
|
64
|
+
self { display: block; padding: 20px; }
|
|
364
65
|
</style>
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
The component name is automatically derived from the filename (e.g., `sample-component.lego` β `<sample-component>`).
|
|
368
|
-
|
|
369
|
-
---
|
|
370
|
-
|
|
371
|
-
## Vite Plugin Setup
|
|
372
|
-
|
|
373
|
-
### Installation
|
|
374
|
-
|
|
375
|
-
```bash
|
|
376
|
-
npm install lego-dom vite
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
### Configuration
|
|
380
|
-
|
|
381
|
-
Create `vite.config.js`:
|
|
382
|
-
|
|
383
|
-
```js
|
|
384
|
-
import { defineConfig } from 'vite';
|
|
385
|
-
import legoPlugin from 'lego-dom/vite-plugin';
|
|
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
|
-
});
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
### Usage
|
|
398
|
-
|
|
399
|
-
Create your components in `.lego` files:
|
|
400
|
-
|
|
401
|
-
```
|
|
402
|
-
src/
|
|
403
|
-
components/
|
|
404
|
-
my-button.lego
|
|
405
|
-
user-card.lego
|
|
406
|
-
main.js
|
|
407
|
-
index.html
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
In your `main.js`:
|
|
411
|
-
|
|
412
|
-
```js
|
|
413
|
-
import { Lego } from 'lego-dom/main.js';
|
|
414
|
-
import registerComponents from 'virtual:lego-components';
|
|
415
|
-
|
|
416
|
-
registerComponents();
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
In your HTML:
|
|
420
66
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
67
|
+
<script>
|
|
68
|
+
export default { name: 'John Doe' }
|
|
69
|
+
</script>
|
|
424
70
|
```
|
|
425
71
|
|
|
426
|
-
**Auto-discovery**: The Vite plugin automatically finds all `.lego` files and registers them with LegoJS!
|
|
427
|
-
|
|
428
72
|
---
|
|
429
73
|
|
|
430
|
-
##
|
|
431
|
-
|
|
432
|
-
LegoJS works in **two modes**:
|
|
433
|
-
|
|
434
|
-
### 1. Without Build Tooling
|
|
435
|
-
|
|
436
|
-
Include `main.js` directly and use `<template b-id>` or `Lego.define()`:
|
|
437
|
-
|
|
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
|
-
```
|
|
444
|
-
|
|
445
|
-
### 2. With Vite (SFC)
|
|
446
|
-
|
|
447
|
-
Use `.lego` files that are auto-discovered and compiled:
|
|
448
|
-
|
|
449
|
-
```bash
|
|
450
|
-
npm run dev
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
Both modes use the same LegoJS runtime and support all the same features!
|
|
454
|
-
|
|
455
|
-
---
|
|
74
|
+
## π Links
|
|
456
75
|
|
|
457
|
-
|
|
76
|
+
- π [Full Documentation](https://rayattack.github.io/legodom/)
|
|
77
|
+
- π [Quick Start Guide](https://rayattack.github.io/legodom/guide/quick-start)
|
|
78
|
+
- π [Vite Plugin Setup](https://rayattack.github.io/legodom/api/vite-plugin)
|
|
79
|
+
- π§ͺ [Example Showcase](https://rayattack.github.io/legodom/examples/)
|
|
458
80
|
|
|
459
|
-
|
|
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
|
|
81
|
+
## License
|
|
465
82
|
|
|
466
|
-
|
|
83
|
+
MIT Β© [Tersoo Ortserga](https://github.com/rayattack)
|
package/cdn.html
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<html lang="en">
|
|
2
|
+
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Lego Test</title>
|
|
7
|
+
<script src="./main.js"></script>
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body @todo-added="() => {console.log('Todo added!')}">
|
|
11
|
+
<template b-id="todo-list" b-data="{
|
|
12
|
+
title: 'My Todo List',
|
|
13
|
+
items: [],
|
|
14
|
+
newItem: '',
|
|
15
|
+
mounted() {
|
|
16
|
+
console.log('Todo list mounted!');
|
|
17
|
+
}
|
|
18
|
+
}">
|
|
19
|
+
<style>
|
|
20
|
+
:host {
|
|
21
|
+
display: block;
|
|
22
|
+
font-family: system-ui, sans-serif;
|
|
23
|
+
padding: 20px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.done {
|
|
27
|
+
text-decoration: line-through;
|
|
28
|
+
color: #888;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.input {
|
|
32
|
+
padding: .4em;
|
|
33
|
+
border: 1px solid #ccc;
|
|
34
|
+
border-radius: 0.1em;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.item-row {
|
|
38
|
+
margin-bottom: 8px;
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: 10px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
button {
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
border: none;
|
|
47
|
+
padding: 5px 10px;
|
|
48
|
+
border-radius: 0px;
|
|
49
|
+
background-color: #404;
|
|
50
|
+
color: #fff;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
h3 {
|
|
54
|
+
color: #318d8d;
|
|
55
|
+
margin-top: 0;
|
|
56
|
+
}
|
|
57
|
+
</style>
|
|
58
|
+
|
|
59
|
+
<h3>{{title}}</h3>
|
|
60
|
+
<p>{{ items.filter(t => !t.done).length }} of {{ items.length }} items remaining</p>
|
|
61
|
+
<div style="margin-bottom: 20px;">
|
|
62
|
+
<input b-sync="newItem" placeholder="Add task..." class="input">
|
|
63
|
+
<button @click="() => {
|
|
64
|
+
if(!newItem.trim()) return;
|
|
65
|
+
items.push({text: `${newItem} - ${items.length + 1}`, done: false});
|
|
66
|
+
newItem = '';
|
|
67
|
+
$emit('todo-added', { item: newItem });
|
|
68
|
+
}">Add</button>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div b-for="todo in items">
|
|
72
|
+
<div class="item-row {{todo.done ? 'done' : ''}}">
|
|
73
|
+
<input type="checkbox" b-sync="todo.done">
|
|
74
|
+
<span>{{todo.text}}</span>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<template b-id="user-card" b-data="{
|
|
80
|
+
name: 'Anonymous',
|
|
81
|
+
bio: 'No bio provided'
|
|
82
|
+
}">
|
|
83
|
+
<style>
|
|
84
|
+
:host {
|
|
85
|
+
display: block;
|
|
86
|
+
font-family: system-ui, sans-serif;
|
|
87
|
+
padding: 20px;
|
|
88
|
+
border: 1px solid #ccc;
|
|
89
|
+
border-radius: 8px;
|
|
90
|
+
margin-bottom: 20px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
h3 {
|
|
94
|
+
color: #62318d;
|
|
95
|
+
margin-top: 0;
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
98
|
+
|
|
99
|
+
<h3>{{name}}</h3>
|
|
100
|
+
<p>{{bio}}</p>
|
|
101
|
+
<slot></slot>
|
|
102
|
+
</template>
|
|
103
|
+
|
|
104
|
+
<!-- Using defaults -->
|
|
105
|
+
<user-card>
|
|
106
|
+
<p>This user uses default data from the template.</p>
|
|
107
|
+
</user-card>
|
|
108
|
+
|
|
109
|
+
<!-- Overriding defaults -->
|
|
110
|
+
<user-card b-data="{
|
|
111
|
+
name: 'John Doe',
|
|
112
|
+
bio: 'Software Engineer'
|
|
113
|
+
}">
|
|
114
|
+
<p>This user overrides the defaults.</p>
|
|
115
|
+
|
|
116
|
+
<todo-list b-data="{
|
|
117
|
+
title: 'Work Tasks',
|
|
118
|
+
items: [{text: 'Implement template defaults', done: true}]
|
|
119
|
+
}">
|
|
120
|
+
</todo-list>
|
|
121
|
+
</user-card>
|
|
122
|
+
</body>
|
|
123
|
+
|
|
124
|
+
</html>
|