lightview 2.0.7 → 2.0.9

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 (140) hide show
  1. package/README.md +47 -1283
  2. package/components/actions/button.js +5 -5
  3. package/components/actions/dropdown.js +6 -6
  4. package/components/actions/modal.js +9 -9
  5. package/components/actions/swap.js +5 -5
  6. package/components/data-display/accordion.js +6 -6
  7. package/components/data-display/alert.js +6 -6
  8. package/components/data-display/avatar.js +7 -7
  9. package/components/data-display/badge.js +5 -5
  10. package/components/data-display/card.js +7 -7
  11. package/components/data-display/carousel.js +4 -4
  12. package/components/data-display/chart.js +8 -8
  13. package/components/data-display/chat.js +7 -7
  14. package/components/data-display/collapse.js +5 -5
  15. package/components/data-display/countdown.js +3 -3
  16. package/components/data-display/diff.js +6 -6
  17. package/components/data-display/kbd.js +5 -5
  18. package/components/data-display/loading.js +5 -5
  19. package/components/data-display/progress.js +5 -5
  20. package/components/data-display/radial-progress.js +5 -5
  21. package/components/data-display/skeleton.js +3 -3
  22. package/components/data-display/stats.js +9 -9
  23. package/components/data-display/table.js +9 -9
  24. package/components/data-display/timeline.js +8 -8
  25. package/components/data-display/toast.js +3 -3
  26. package/components/data-display/tooltip.js +3 -3
  27. package/components/data-input/checkbox.js +5 -5
  28. package/components/data-input/file-input.js +3 -3
  29. package/components/data-input/input.js +5 -5
  30. package/components/data-input/radio.js +9 -9
  31. package/components/data-input/range.js +3 -3
  32. package/components/data-input/rating.js +3 -3
  33. package/components/data-input/select.js +5 -5
  34. package/components/data-input/textarea.js +3 -3
  35. package/components/data-input/toggle.js +5 -5
  36. package/components/layout/divider.js +3 -3
  37. package/components/layout/drawer.js +7 -7
  38. package/components/layout/footer.js +5 -5
  39. package/components/layout/hero.js +5 -5
  40. package/components/layout/indicator.js +4 -4
  41. package/components/layout/join.js +4 -4
  42. package/components/layout/navbar.js +6 -6
  43. package/components/navigation/breadcrumbs.js +4 -4
  44. package/components/navigation/dock.js +5 -5
  45. package/components/navigation/menu.js +6 -6
  46. package/components/navigation/pagination.js +3 -3
  47. package/components/navigation/steps.js +4 -4
  48. package/components/navigation/tabs.js +5 -5
  49. package/components/theme/theme-switch.js +30 -30
  50. package/docs/about.html +142 -14
  51. package/docs/api/computed.html +1 -6
  52. package/docs/api/effects.html +1 -7
  53. package/docs/api/elements.html +6 -10
  54. package/docs/api/enhance.html +1 -6
  55. package/docs/api/hypermedia.html +154 -22
  56. package/docs/api/index.html +7 -12
  57. package/docs/api/nav.html +18 -1
  58. package/docs/api/signals.html +1 -6
  59. package/docs/api/state.html +1 -6
  60. package/docs/assets/js/examplify-sandbox.html +2 -2
  61. package/docs/assets/js/examplify.js +15 -15
  62. package/docs/components/accordion.html +4 -4
  63. package/docs/components/alert.html +4 -4
  64. package/docs/components/avatar.html +4 -4
  65. package/docs/components/badge.html +4 -4
  66. package/docs/components/breadcrumbs.html +3 -3
  67. package/docs/components/button.html +5 -5
  68. package/docs/components/card.html +4 -4
  69. package/docs/components/carousel.html +3 -3
  70. package/docs/components/chart-area.html +6 -6
  71. package/docs/components/chart-bar.html +6 -6
  72. package/docs/components/chart-column.html +6 -6
  73. package/docs/components/chart-line.html +6 -6
  74. package/docs/components/chart-pie.html +6 -6
  75. package/docs/components/chart.html +2 -2
  76. package/docs/components/chat.html +4 -4
  77. package/docs/components/checkbox.html +4 -4
  78. package/docs/components/collapse.html +4 -4
  79. package/docs/components/countdown.html +4 -4
  80. package/docs/components/diff.html +3 -3
  81. package/docs/components/divider.html +3 -3
  82. package/docs/components/dock.html +3 -3
  83. package/docs/components/drawer.html +4 -4
  84. package/docs/components/dropdown.html +4 -4
  85. package/docs/components/file-input.html +4 -4
  86. package/docs/components/footer.html +3 -3
  87. package/docs/components/gallery.html +2 -2
  88. package/docs/components/hero.html +3 -3
  89. package/docs/components/index.css +5 -3
  90. package/docs/components/index.html +4 -4
  91. package/docs/components/indicator.html +3 -3
  92. package/docs/components/input.html +4 -4
  93. package/docs/components/join.html +3 -3
  94. package/docs/components/kbd.html +3 -3
  95. package/docs/components/loading.html +4 -4
  96. package/docs/components/menu.html +4 -4
  97. package/docs/components/modal.html +4 -4
  98. package/docs/components/navbar.html +3 -3
  99. package/docs/components/pagination.html +3 -3
  100. package/docs/components/progress.html +4 -4
  101. package/docs/components/radial-progress.html +3 -3
  102. package/docs/components/radio.html +4 -4
  103. package/docs/components/range.html +4 -4
  104. package/docs/components/rating.html +4 -4
  105. package/docs/components/select.html +4 -4
  106. package/docs/components/sidebar-setup.js +1 -1
  107. package/docs/components/skeleton.html +4 -4
  108. package/docs/components/spinner.html +4 -4
  109. package/docs/components/stats.html +4 -4
  110. package/docs/components/steps.html +3 -3
  111. package/docs/components/swap.html +4 -4
  112. package/docs/components/switch.html +4 -4
  113. package/docs/components/table.html +4 -4
  114. package/docs/components/tabs.html +4 -4
  115. package/docs/components/text-input.html +4 -4
  116. package/docs/components/textarea.html +4 -4
  117. package/docs/components/timeline.html +4 -4
  118. package/docs/components/toast.html +4 -4
  119. package/docs/components/toggle.html +4 -4
  120. package/docs/components/tooltip.html +4 -4
  121. package/docs/examples/getting-started-example.html +1 -1
  122. package/docs/examples/index.html +1 -2
  123. package/docs/getting-started/index.html +105 -14
  124. package/docs/index.html +2 -11
  125. package/docs/router-nav.html +13 -0
  126. package/docs/router.html +60 -17
  127. package/docs/styles/index.html +2 -7
  128. package/docs/syntax.html +144 -0
  129. package/functions/_middleware.js +17 -10
  130. package/functions/processServerScripts.js +127 -0
  131. package/index.html +8 -8
  132. package/lightview-router.js +141 -297
  133. package/lightview-x.js +604 -573
  134. package/lightview.js +179 -157
  135. package/package.json +33 -26
  136. package/scripts/analysis/README.md +2 -0
  137. package/scripts/analysis/analyze.js +266 -0
  138. package/scripts/analysis/latest_metrics.md +185 -0
  139. package/wrangler.toml +6 -0
  140. package/docs/playground.html +0 -416
package/README.md CHANGED
@@ -1,1335 +1,99 @@
1
-
2
- # Lightview: README.md
1
+ # Lightview
3
2
 
4
3
  A lightweight reactive UI library with signal-based reactivity and a clean API. Build dynamic UIs with automatic DOM synchronization.
5
4
 
6
- Access the full documentaion at [lightview.dev](https://lightview.dev).
7
-
8
- This NPM package is both the library and the website supporting the library. The website is built using Lightview. The core library files are in the root directory. The Website entry point is index.html and the restr of the site is under ./docs. The site is served by a Cloudflare pages deployment.
9
-
10
- **Core**: ~8KB | **With Hypermedia Extensions and Component Library Support**: ~18KB total
11
-
12
- Fast: This [gallery of components](https://lightview.dev/docs/components/) loads in about 1 second:
5
+ Access the full documentation and interactive examples at [lightview.dev](https://lightview.dev).
13
6
 
14
7
  ## Modular Architecture
15
8
 
9
+ **Core Library**: ~7.75KB | **Extended (Hypermedia + Components)**: ~20KB | **Router**: ~3KB
10
+
16
11
  Lightview is split into three files:
17
12
 
18
13
  - **`lightview.js`** - Core reactivity (signals, state, effects, elements)
19
- - **`lightview-x.js`** - Hypermedia extension (src fetching, href navigation, template literals, named registries, Object DOM syntax, UI component library support)
20
- - **`lightview-router.js`** - Router (src fetching, href navigation, template literals, named registries, Object DOM syntax, UI component library support)
21
-
22
- ### API Behavior
23
-
24
- | Usage | Core Only | With `-x` |
25
- |-------|-----------|-----------|
26
- | `signal(5)` | ✅ Works | ✅ Works |
27
- | `signal(5, "count")` | ⚠️ Ignores name | ✅ Registers |
28
- | `signal.get("count")` | ❌ Undefined | ✅ Works |
29
- | `signal.get("count", 0)` | ❌ Undefined | ✅ Creates if missing |
30
- | `state({...})` | ✅ Works | ✅ Works |
31
- | `state({...}, "app")` | ⚠️ Ignores name | ✅ Registers |
32
- | `state.get("app")` | ❌ Undefined | ✅ Works |
33
- | `state.get("app", {...})` | ❌ Undefined | ✅ Creates if missing |
34
- | `"${signal.get('count').value}"` | ❌ No processing | ✅ Reactive template |
35
- | `<div src="page.html">` | ❌ No fetching | ✅ Loads content |
36
- | `<span href="other.html">` | ❌ No navigation | ✅ Reactive navigation |
37
- | `{ div: { class: "x" } }` | ❌ Not recognized | ✅ Object DOM syntax |
38
- | `enhance('#btn', {...})` | ❌ Undefined | ✅ Enhances existing DOM |
39
-
40
- ### Installation
41
-
42
- ```html
43
- <!-- Core only (reactivity) -->
44
- <script src="lightview.js"></script>
45
-
46
- <!-- Full features (hypermedia + templates) -->
47
- <script src="lightview.js"></script>
48
- <script src="lightview-x.js"></script>
49
-
50
- <!-- Full features (hypermedia + templates + router) -->
51
- <script src="lightview.js"></script>
52
- <script src="lightview-x.js"></script>
53
- <script src="lightview-router.js"></script>
54
- ```
14
+ - **`lightview-x.js`** - Hypermedia extension (src fetching, href navigation, template literals, named registries, UI component library support)
15
+ - **`lightview-router.js`** - Pipeline-based History API router with middleware support
55
16
 
56
- ## Core Concepts
17
+ ## One System, Four Syntaxes
57
18
 
58
- **Lightview** provides four ways to build UIs:
19
+ Lightview supports multiple ways to build UIs, allowing you to pick the style that fits your workflow:
59
20
 
60
- 1. **Tagged API** - Concise, Bau.js-style syntax: `tags.div(...)`
61
- 2. **vDOM Syntax** - JSON data structures: `{ tag: "div", attributes: {}, children: [] }`
62
- 3. **Object DOM Syntax** *(lightview-x)* - Compact: `{ div: { class: "foo", children: [] } }`
63
- 4. **HTML** *(lightview-x)* - Custom HTML elements.
21
+ 1. **Tagged API**: Concise, JavaScript-first syntax (e.g., `tags.div(...)`).
22
+ 2. **vDOM Syntax**: Explicit JSON-based structures (e.g., `{ tag: 'div', ... }`).
23
+ 3. **Object DOM**: Compact JSON syntax with automatic tag detection (e.g., `{ div: { ... } }`).
24
+ 4. **Custom Elements**: Standard HTML tags (e.g., `<lv-button>`) for progressive enhancement.
64
25
 
65
- All four approaches use the same underlying reactive system based on **signals** and **state**.
66
-
67
- ## Installation
68
-
69
- ```html
70
- <script src="lightview.js"></script>
71
- ```
26
+ All syntaxes share the same underlying reactive engine based on **Signals** and **State**.
72
27
 
73
28
  ## Quick Start
74
29
 
75
- ### Style 1: Tagged API
30
+ ### 1. Tagged API (Concise & Expressive)
76
31
 
77
32
  ```javascript
78
- const lv = new Lightview();
79
- const { signal, computed, tags } = lv;
80
- const { div, h1, p, button } = tags;
33
+ const { tags, signal, $ } = Lightview;
34
+ const { div, button, p } = tags;
81
35
 
82
36
  const count = signal(0);
83
- const doubled = computed(() => count.value * 2);
84
37
 
85
- const app = div({ class: 'container' },
86
- h1('Counter App'),
87
- p(() => `Count: ${count.value}`),
88
- p(() => `Doubled: ${doubled.value}`),
89
- button({ onclick: () => count.value++ }, 'Increment'),
90
- button({ onclick: () => count.value-- }, 'Decrement')
38
+ const app = div(
39
+ p(() => `Count: ${count.value}`),
40
+ button({ onclick: () => count.value++ }, 'Increment')
91
41
  );
92
42
 
93
- document.body.appendChild(app.domEl);
94
- ```
95
-
96
- ### Style 2: vDOM Syntax (Plain JSON)
97
-
98
- ```javascript
99
- const { signal, element } = new Lightview();
100
-
101
- const count = signal(0);
102
-
103
- const app = element('div', { class: 'container' }, [
104
- {
105
- tag: 'h1',
106
- attributes: {},
107
- children: ['Counter App']
108
- },
109
- {
110
- tag: 'p',
111
- attributes: {},
112
- children: [() => `Count: ${count()}`] // or count.value
113
- },
114
- {
115
- tag: 'button',
116
- attributes: { onclick: () => count.value++ }, // or count(count() + 1)
117
- children: ['Increment']
118
- }
119
- ]);
120
-
121
-
122
- document.body.appendChild(app.domEl);
43
+ $('body').content(app);
123
44
  ```
124
45
 
125
- ### Style 3: Object DOM Syntax (lightview-x)
126
-
127
- Object DOM syntax provides a more compact way to define elements. Instead of `{ tag, attributes, children }`, you use `{ tag: { ...attributes, children } }`.
128
-
129
- **Requires lightview-x.js** and must be enabled:
130
-
131
- ```javascript
132
- // Enable Object DOM syntax (call once at startup)
133
- LightviewX.useObjectDOMSyntax(); // Non-strict mode (default)
134
- LightviewX.useObjectDOMSyntax(true); // Strict mode - validates HTML tag names
135
- ```
46
+ ### 2. vDOM Syntax (Standard JSON)
136
47
 
137
48
  ```javascript
138
- const { signal, element, tags } = Lightview;
139
- const { div, button } = tags;
140
-
141
- // Enable Object DOM syntax
142
- LightviewX.useObjectDOMSyntax();
143
-
49
+ const { signal, $ } = Lightview;
144
50
  const count = signal(0);
145
51
 
146
- // Object DOM syntax in children arrays
147
- const app = div({ class: 'container' },
148
- { h1: { children: ['Counter App'] } },
149
- { p: { children: [() => `Count: ${count.value}`] } },
150
- { button: { onclick: () => count.value++, children: ['Increment'] } }
151
- );
152
-
153
- document.body.appendChild(app.domEl);
154
- ```
155
-
156
- **Comparison:**
157
-
158
- | vDOM Syntax | Object DOM Syntax |
159
- |-------------|-------------------|
160
- | `{ tag: 'div', attributes: { class: 'box' }, children: ['Hello'] }` | `{ div: { class: 'box', children: ['Hello'] } }` |
161
-
162
- **Pros & Cons:**
163
-
164
- | Aspect | vDOM Syntax | Object DOM Syntax |
165
- |--------|-------------|-------------------|
166
- | **Verbosity** | More verbose | More compact |
167
- | **Explicit** | ✅ Clear structure, easy to validate | ⚠️ Tag name is a dynamic key |
168
- | **Serialization** | ✅ Easy to serialize/deserialize | ⚠️ Requires detection logic |
169
- | **Reserved words** | ✅ None - `children` is just a property | ⚠️ `children` is reserved |
170
- | **TypeScript** | ✅ Easy to type | ⚠️ Harder to provide autocomplete |
171
- | **Dynamic tags** | ✅ `{ tag: myVar, ... }` | ⚠️ Requires `{ [myVar]: {...} }` |
172
- | **Multiple elements** | ✅ Can have array of objects | ⚠️ One element per object |
173
- | **Readability** | Familiar to React/vDOM users | Cleaner for static templates |
174
-
175
- **Why vDOM is the default:**
176
-
177
- 1. **Unambiguous parsing** - The presence of `tag` clearly identifies an element. Object DOM requires heuristics to detect (single key that's a valid tag name).
178
-
179
- 2. **No reserved attribute names** - In vDOM, you can have an attribute literally named `children`. In Object DOM, `children` is reserved for child elements.
180
-
181
- 3. **Better for data interchange** - vDOM objects can be safely serialized to JSON and parsed back without any special handling. They're self-describing.
182
-
183
- 4. **Predictable validation** - Easy to check `if (obj.tag)` vs. finding the unknown key and checking if it's a valid tag.
184
-
185
- 5. **Works without extensions** - vDOM is supported by core `lightview.js`. Object DOM requires `lightview-x.js`.
186
-
187
- Object DOM is ideal for **hand-written templates** where brevity matters, or **configuration files** where you want a cleaner syntax. Use vDOM when you need **programmatic generation**, **serialization**, or **maximum compatibility**.
188
-
189
- **Nested Example:**
190
-
191
- ```javascript
192
- // Object DOM - compact and readable
193
- { div: {
194
- class: 'card',
52
+ const app = {
53
+ tag: 'div',
195
54
  children: [
196
- { h2: { children: ['Title'] } },
197
- { p: { style: 'color: gray', children: ['Description'] } },
198
- { button: { onclick: handleClick, children: ['Action'] } }
55
+ { tag: 'p', children: [() => `Count: ${count.value}`] },
56
+ { tag: 'button', attributes: { onclick: () => count.value++ }, children: ['Increment'] }
199
57
  ]
200
- }}
201
-
202
- // Equivalent vDOM
203
- { tag: 'div', attributes: { class: 'card' }, children: [
204
- { tag: 'h2', attributes: {}, children: ['Title'] },
205
- { tag: 'p', attributes: { style: 'color: gray' }, children: ['Description'] },
206
- { tag: 'button', attributes: { onclick: handleClick }, children: ['Action'] }
207
- ]}
208
- ```
209
-
210
- **Strict Mode:**
211
-
212
- When `useObjectDOMSyntax(true)` is called, tag names are validated using the browser's own HTML parser. Unknown tags like `foo` or `notreal` will be rejected, while standard HTML tags and valid custom elements (with hyphens) are accepted.
213
-
214
- ```javascript
215
- LightviewX.useObjectDOMSyntax(true); // Enable strict validation
216
-
217
- // Valid - browser recognizes these
218
- { div: { children: ['OK'] } } // Standard HTML tag
219
- { 'my-widget': { children: ['OK'] } } // Custom element (valid in browser)
220
-
221
- // Invalid in strict mode (HTMLUnknownElement - won't be detected as Object DOM)
222
- { notarealtag: { children: ['Nope'] } } // Browser returns HTMLUnknownElement
58
+ };
223
59
 
224
- // You can also check directly:
225
- LightviewX.isKnownHTMLTag('div'); // true
226
- LightviewX.isKnownHTMLTag('my-widget'); // true (custom elements are valid)
227
- LightviewX.isKnownHTMLTag('faketag'); // false (HTMLUnknownElement)
60
+ $('body').content(app);
228
61
  ```
229
62
 
230
- ### Style 5: Component Functions
231
-
232
- The `tag` property (or Object DOM key) can be a **function** instead of a string. This enables reusable components that return HTML, DOM nodes, vDOM, or Object DOM.
63
+ ### 3. Object DOM (Compact JSON)
233
64
 
234
65
  ```javascript
235
- const { element, signal, tags } = Lightview;
236
- const { div, button } = tags;
66
+ const { signal, $ } = Lightview;
67
+ const count = signal(0);
237
68
 
238
- // Define a component function
239
- const Card = (props) => ({
69
+ const app = {
240
70
  div: {
241
- class: 'card',
242
- style: `border: 1px solid ${props.borderColor || '#ccc'}; padding: 16px;`,
243
71
  children: [
244
- { h3: { children: [props.title] } },
245
- { p: { children: [props.description] } },
246
- ...(props.children || [])
72
+ { p: { children: [() => `Count: ${count.value}`] } },
73
+ { button: { onclick: () => count.value++, children: ['Increment'] } }
247
74
  ]
248
75
  }
249
- });
250
-
251
- // Use component with element() - tag is a function
252
- const app = element('div', {}, [
253
- { tag: Card, attributes: { title: 'Hello', description: 'A card component' } },
254
- { tag: Card, attributes: { title: 'World', borderColor: 'blue', children: [
255
- button({ onclick: () => alert('Clicked!') }, 'Click Me')
256
- ]}}
257
- ]);
258
- ```
259
-
260
- **Component Return Types:**
261
-
262
- Components can return any of these formats:
263
-
264
- ```javascript
265
- // 1. Object DOM (recommended for simplicity)
266
- const Badge = (props) => ({
267
- span: { class: 'badge', children: [props.text] }
268
- });
269
-
270
- // 2. vDOM
271
- const Badge = (props) => ({
272
- tag: 'span',
273
- attributes: { class: 'badge' },
274
- children: [props.text]
275
- });
276
-
277
- // 3. HTML string
278
- const Badge = (props) => `<span class="badge">${props.text}</span>`;
279
-
280
- // 4. DOM node
281
- const Badge = (props) => {
282
- const el = document.createElement('span');
283
- el.className = 'badge';
284
- el.textContent = props.text;
285
- return el;
286
76
  };
287
- ```
288
-
289
- **Registering Components:**
290
77
 
291
- If you add a component to Ligtvhiew.tags, then you can treate it ligke any other tag. It will even work with Object DOM syntax.
292
-
293
- ```javascript
294
- // Register individual components
295
- Lightview.tags['Badge'] = Badge;
296
- ```
297
-
298
- ```javascript
299
- const { div, Badge } = tags;
300
- const app = div(
301
- Badge({ text: 'New' })
302
- );
303
- ```
304
-
305
- ```javascript
306
- // Now use by name in Object DOM syntax
307
- LightviewX.useObjectDOMSyntax();
308
-
309
- const app2 = div(
310
- { Badge: { text: 'New' } }
311
- );
78
+ $('body').content(app);
312
79
  ```
313
80
 
314
- **With Global Scope:**
315
-
316
- ```javascript
317
- // Define component globally
318
- window.Alert = (props) => ({
319
- div: {
320
- class: `alert alert-${props.type || 'info'}`,
321
- children: [props.message]
322
- }
323
- });
324
-
325
- // Enable global lookup
326
- LightviewX.useObjectDOMSyntax();
327
-
328
- // Use directly
329
- const app = div(
330
- { Alert: { type: 'success', message: 'Operation completed!' } }
331
- );
332
- ```
333
-
334
- ## API Reference
335
-
336
- ### Lightview Class
337
-
338
- ```javascript
339
- const lv = new Lightview();
340
- ```
341
-
342
- Creates a Lightview instance with:
343
- - `signal(value)` - Create reactive state
344
- - `computed(fn)` - Create derived state
345
- - `effect(fn)` - Run side effects
346
- - `state(obj)` - Create deep reactive store
347
- - `element(tag, attrs, children)` - Create elements
348
- - `tags` - Proxy for creating elements: `tags.div()`, `tags.button()`, etc.
349
-
350
- ### Signals
351
-
352
- Signals in Lightview are versatile. They can be used as **function calls** or by accessing the `.value` property. This dual API allows for flexible coding styles.
353
-
354
- ```javascript
355
- const name = signal('John');
356
-
357
- // 1. Property Access Style
358
- // Read
359
- console.log(name.value); // 'John'
360
- // Write
361
- name.value = 'Jane';
362
-
363
- // 2. Function Call Style
364
- // Read
365
- console.log(name()); // 'Jane'
366
- // Write
367
- name('Bob');
368
- ```
369
-
370
- You can choose whichever style you prefer or mix them as needed. Both methods are fully reactive and interoperable.
371
-
372
- ### Working with Objects
373
-
374
- Lightview signals are **shallow** by design. This keeps the library extremely small and fast (performant). When working with nested objects, use immutable patterns to update state:
375
-
376
- ```javascript
377
- const user = signal({ name: "Joe", address: { city: "NYC" } });
378
-
379
- // ❌ Don't mutate directly (won't trigger updates)
380
- // user.value.address.city = "LA";
381
-
382
- // ✅ Do use immutable patterns
383
- user.value = {
384
- ...user.value,
385
- address: { ...user.value.address, city: "LA" }
386
- };
387
- ```
388
-
389
-
390
- ### Computed Signals
391
-
392
- ```javascript
393
- const firstName = signal('John');
394
- const lastName = signal('Doe');
395
-
396
- const fullName = computed(() => `${firstName.value} ${lastName.value}`);
397
-
398
- console.log(fullName.value); // 'John Doe'
399
- firstName.value = 'Jane';
400
- console.log(fullName.value); // 'Jane Doe'
401
- ```
402
-
403
- ### State (Store)
404
-
405
- For deeply nested objects or grouped state, use `state()`. It creates a reactive proxy where every property is automatically backed by a signal.
406
-
407
- ```javascript
408
- /*
409
- Creates a deep reactive store.
410
- Accessing properties (e.g. s.user.name) automatically tracks dependencies.
411
- Setting properties automatically triggers updates.
412
- */
413
- const appState = state({
414
- user: {
415
- name: 'Alice',
416
- settings: { theme: 'dark' }
417
- },
418
- count: 0
419
- });
420
-
421
- // Reading tracks dependencies
422
- effect(() => {
423
- console.log(`${appState.user.name} likes ${appState.user.settings.theme} mode`);
424
- });
425
-
426
- // Writing triggers updates
427
- appState.user.name = 'Bob';
428
- appState.user.settings.theme = 'light';
429
- ```
430
-
431
- Note: `state` objects read/write like normal JavaScript objects (no `.value` needed).
432
-
433
- ### Reactive Arrays and Dates
434
-
435
- **Lightview has a unique feature**: it can make Arrays and Date objects fully reactive, even when using their native methods. This is something most reactive libraries don't support!
436
-
437
- #### Reactive Arrays
438
-
439
- When you wrap an array in `state()`, Lightview automatically tracks changes to the array's `length` property. This means array mutation methods like `push()`, `pop()`, `splice()`, `shift()`, `unshift()`, etc. will trigger reactive updates:
440
-
441
- ```javascript
442
- // Style 1: Tagged API
443
- const { state, effect, tags } = new Lightview();
444
- const { div, ul, li, button } = tags;
445
-
446
- const items = state(['Apple', 'Banana']);
447
-
448
- // This effect automatically re-runs when the array length changes
449
- effect(() => {
450
- console.log(`Array has ${items.length} items`);
451
- });
452
-
453
- // All these methods trigger reactivity!
454
- items.push('Cherry'); // Logs: "Array has 3 items"
455
- items.pop(); // Logs: "Array has 2 items"
456
- items.splice(1, 0, 'Date'); // Logs: "Array has 3 items"
457
-
458
- // Use in UI
459
- const app = div(
460
- () => ul(
461
- ...items.map(item => li(item))
462
- ),
463
- button({ onclick: () => items.push('New Item') }, 'Add Item')
464
- );
465
- ```
466
-
467
- **Array elements now have full deep reactivity!** Both the array's `length` and individual element properties are tracked:
468
-
469
- ```javascript
470
- // Style 3: Plain JSON - state works with any API style
471
- const { state, element } = new Lightview();
472
-
473
- const items = state([
474
- { name: 'Item 1', done: false },
475
- { name: 'Item 2', done: true }
476
- ]);
477
-
478
- // Using Plain JSON structure with reactive list:
479
- const list = element('div', {}, [
480
- // Wrap in a function to re-render when items.length changes
481
- () => ({
482
- tag: 'ul',
483
- attributes: {},
484
- children: items.map(item => ({
485
- tag: 'li',
486
- attributes: { class: () => item.done ? 'completed' : '' },
487
- children: [() => item.name]
488
- }))
489
- }),
490
- {
491
- tag: 'button',
492
- attributes: { onclick: () => items.push({ name: `Item ${items.length + 1}`, done: false }) },
493
- children: ['Add Item']
494
- },
495
- {
496
- tag: 'button',
497
- attributes: { onclick: () => items[0].done = !items[0].done },
498
- children: ['Toggle First']
499
- }
500
- ]);
501
-
502
- // All of these trigger UI updates:
503
- // items.push({ name: 'Item 3', done: false }); // ✅ Reactive (length changed)
504
- // items[0].done = true; // ✅ Reactive (element property changed)
505
- // items[1].name = 'Updated Item'; // ✅ Reactive (nested property changed)
506
- ```
507
-
508
- #### Reactive Dates
509
-
510
- Date objects are notoriously difficult to make reactive in most frameworks because their mutation methods (`setDate()`, `setHours()`, etc.) change internal state without changing the object reference. Lightview solves this by monitoring the `getTime()` value:
511
-
512
- ```javascript
513
- // Style 2: Element Function
514
- const { state, effect, element } = new Lightview();
515
-
516
- const currentDate = state(new Date());
517
-
518
- // This effect re-runs whenever the date's timestamp changes
519
- effect(() => {
520
- console.log(`Current time: ${currentDate.getTime()}`);
521
- });
522
-
523
- // All date mutation methods trigger reactivity!
524
- currentDate.setHours(12); // Triggers update
525
- currentDate.setDate(15); // Triggers update
526
- currentDate.setFullYear(2025); // Triggers update
527
-
528
- // Use in UI
529
- const clock = element('div', {}, [
530
- element('p', {}, [() => `Time: ${currentDate.toLocaleTimeString()}`]),
531
- element('button', {
532
- onclick: () => currentDate.setTime(Date.now())
533
- }, ['Update to Now'])
534
- ]);
535
- ```
536
-
537
- **Why this matters**: In most reactive libraries (Vue, Solid, Svelte), you'd need to create a new Date object to trigger updates:
538
-
539
- ```javascript
540
- // What you'd have to do in Vue/Solid: Although, it will still work in Lightview
541
- myDate.value = new Date(myDate.value.setHours(12)); // ❌ Awkward!
542
-
543
- // What you can do in Lightview:
544
- myDate.setHours(12); // ✅ Just works!
545
- ```
546
-
547
- ### Effects
548
-
549
-
550
- ```javascript
551
- const count = signal(0);
552
-
553
- effect(() => {
554
- console.log(`Count is now: ${count.value}`);
555
- });
556
-
557
- count.value = 1; // Logs: "Count is now: 1"
558
- ```
559
-
560
- ### Element Structure
561
-
562
- Every element has:
563
- - `tag` - HTML tag name or component function
564
- - `attributes` - Object of attributes
565
- - `children` - Array of child elements/text/functions
566
- - `domEl` - The actual DOM node (read-only getter)
567
-
568
-
569
-
570
- ## Reactive Features
571
-
572
- ### Reactive Text
573
-
574
- ```javascript
575
- // Style 2: Element Function
576
- const { element, signal } = new Lightview();
577
-
578
- const name = signal('World');
579
-
580
- const app = element('div', {}, [
581
- element('h1', {}, [() => `Hello, ${name.value}!`]),
582
- element('button', { onclick: () => name.value = 'Lightview' }, ['Change Name'])
583
- ]);
584
- // Click the button to see the greeting update
585
- ```
586
-
587
- ### Reactive Attributes
588
-
589
- ```javascript
590
- // Style 1: Tagged API
591
- const { tags, signal } = new Lightview();
592
- const { div, button } = tags;
593
-
594
- const isActive = signal(false);
595
-
596
- const app = div(
597
- button({
598
- class: () => isActive.value ? 'active' : 'inactive',
599
- disabled: () => !isActive.value
600
- }, 'I am toggled'),
601
- button({ onclick: () => isActive.value = !isActive.value }, 'Toggle Active')
602
- );
603
- // Click 'Toggle Active' to enable/disable the first button
604
- ```
605
-
606
- ### Reactive Styles
607
-
608
- ```javascript
609
- // Style 3: Plain JSON
610
- const { element, signal } = new Lightview();
611
-
612
- const color = signal('blue');
613
-
614
- const box = element('div', {}, [
615
- {
616
- tag: 'div',
617
- attributes: {
618
- style: () => ({
619
- backgroundColor: color.value,
620
- padding: '20px',
621
- transition: 'background-color 0.3s'
622
- })
623
- },
624
- children: [() => `Color: ${color.value}`]
625
- },
626
- {
627
- tag: 'button',
628
- attributes: { onclick: () => color.value = 'red' },
629
- children: ['Red']
630
- },
631
- {
632
- tag: 'button',
633
- attributes: { onclick: () => color.value = 'green' },
634
- children: ['Green']
635
- },
636
- {
637
- tag: 'button',
638
- attributes: { onclick: () => color.value = 'blue' },
639
- children: ['Blue']
640
- }
641
- ]);
642
- // Click the color buttons to change the box color
643
- ```
644
-
645
- ### Reactive Lists
646
-
647
- ```javascript
648
- // Style 2: Element Function
649
- const { element, signal } = new Lightview();
650
-
651
- const items = signal(['Apple', 'Banana', 'Cherry']);
652
-
653
- const list = element('div', {}, [
654
- () => element('ul', {},
655
- items.value.map(item =>
656
- element('li', {}, [item])
657
- )
658
- ),
659
- element('button', {
660
- onclick: () => items.value = [...items.value, 'New Fruit']
661
- }, ['Add Fruit']),
662
- element('button', {
663
- onclick: () => items.value = items.value.slice(0, -1)
664
- }, ['Remove Last'])
665
- ]);
666
- // Click buttons to add or remove items from the list
667
- ```
668
-
669
- ## Lifecycle & Cleanup
670
-
671
- ### Lifecycle Hooks
672
-
673
- Lightview provides built-in lifecycle hooks for elements, allowing you to run code when an element enters or leaves the DOM.
674
-
675
- ```javascript
676
- // Style 2: Element Function
677
- const { element } = new Lightview();
678
-
679
- /*
680
- onmount: Called when the element is added to the DOM.
681
- onunmount: Called when the element is removed from the DOM.
682
- */
683
- const timer = element('div', {
684
- onmount: (el) => {
685
- console.log('Timer mounted!');
686
- el._interval = setInterval(() => console.log('Tick'), 1000);
687
- },
688
- onunmount: (el) => {
689
- console.log('Timer removed!');
690
- clearInterval(el._interval);
691
- }
692
- }, ['I am a timer']);
693
- ```
694
-
695
- These hooks are robust and triggered whether you remove elements via Lightview logic or standard DOM methods (like `element.remove()` or `innerHTML = ''`).
696
-
697
- ### Automatic Cleanup
698
-
699
- One of Lightview's most powerful features is its **fully automatic memory management**.
700
-
701
- * **Self-Cleaning Effects**: When an element is removed from the DOM, Lightview automatically stops all reactive effects (signals, computed values) attached to it.
702
- * **Leak Prevention**: You don't need to manually unsubscribe from signals. The built-in `MutationObserver` watches the document and cleans up dependencies instantly when nodes are detached.
703
-
704
- ## Complete Examples
705
-
706
- ### Todo App
707
-
708
- ```javascript
709
- const lv = new Lightview();
710
- const { signal, tags } = lv;
711
- const { div, h1, input, button, span } = tags;
712
-
713
- const state = {
714
- todos: signal([]),
715
- input: signal(''),
716
- filter: signal('all')
717
- };
718
-
719
- const addTodo = () => {
720
- if (state.input.value.trim()) {
721
- state.todos.value = [...state.todos.value, {
722
- id: Date.now(),
723
- text: signal(state.input.value),
724
- done: signal(false)
725
- }];
726
- state.input.value = '';
727
- }
728
- };
729
-
730
- const filteredTodos = lv.computed(() => {
731
- const todos = state.todos.value;
732
- const filter = state.filter.value;
733
-
734
- if (filter === 'active') return todos.filter(t => !t.done.value);
735
- if (filter === 'completed') return todos.filter(t => t.done.value);
736
- return todos;
737
- });
738
-
739
- const app = div({ class: 'todo-app' },
740
- h1('Lightview Todos'),
741
-
742
- div({ class: 'input-row' },
743
- input({
744
- placeholder: 'What needs to be done?',
745
- value: () => state.input.value,
746
- oninput: (e) => state.input.value = e.target.value,
747
- onkeypress: (e) => { if (e.key === 'Enter') addTodo(); }
748
- }),
749
- button({ onclick: addTodo }, 'Add')
750
- ),
751
-
752
- div({ class: 'filters' },
753
- button({
754
- class: () => state.filter.value === 'all' ? 'active' : '',
755
- onclick: () => state.filter.value = 'all'
756
- }, 'All'),
757
- button({
758
- class: () => state.filter.value === 'active' ? 'active' : '',
759
- onclick: () => state.filter.value = 'active'
760
- }, 'Active'),
761
- button({
762
- class: () => state.filter.value === 'completed' ? 'active' : '',
763
- onclick: () => state.filter.value = 'completed'
764
- }, 'Completed')
765
- ),
766
-
767
- () => div({ class: 'todo-list' },
768
- ...filteredTodos.value.map(todo =>
769
- div({ class: 'todo-item' },
770
- input({
771
- type: 'checkbox',
772
- checked: () => todo.done.value,
773
- onchange: () => todo.done.value = !todo.done.value
774
- }),
775
- span({
776
- class: () => todo.done.value ? 'completed' : '',
777
- ondblclick: () => {
778
- const newText = prompt('Edit:', todo.text.value);
779
- if (newText !== null) todo.text.value = newText;
780
- }
781
- }, () => todo.text.value),
782
- button({
783
- class: 'delete',
784
- onclick: () => {
785
- state.todos.value = state.todos.value.filter(t => t.id !== todo.id);
786
- }
787
- }, '×')
788
- )
789
- )
790
- )
791
- );
792
-
793
- document.body.appendChild(app.domEl);
794
- ```
795
-
796
- ### Form with Validation
797
-
798
- ```javascript
799
- const lv = new Lightview();
800
- const { signal, computed, tags } = lv;
801
- const { form, div, label, input, span, button } = tags;
802
-
803
- const email = signal('');
804
- const password = signal('');
805
-
806
- const isValidEmail = computed(() =>
807
- /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)
808
- );
809
-
810
- const isValidPassword = computed(() =>
811
- password.value.length >= 8
812
- );
813
-
814
- const canSubmit = computed(() =>
815
- isValidEmail.value && isValidPassword.value
816
- );
817
-
818
- const formEl = form({
819
- onsubmit: (e) => {
820
- e.preventDefault();
821
- if (canSubmit.value) { // or canSubmit()
822
- alert('Form submitted!');
823
- }
824
- }
825
- },
826
- div(
827
- label('Email:'),
828
- input({
829
- type: 'email',
830
- value: () => email.value,
831
- oninput: (e) => email.value = e.target.value,
832
- class: () => email.value && !isValidEmail.value ? 'invalid' : ''
833
- }),
834
- () => email.value && !isValidEmail.value
835
- ? span({ class: 'error' }, 'Invalid email')
836
- : span()
837
- ),
838
-
839
- div(
840
- label('Password:'),
841
- input({
842
- type: 'password',
843
- value: () => password.value,
844
- oninput: (e) => password.value = e.target.value,
845
- class: () => password.value && !isValidPassword.value ? 'invalid' : ''
846
- }),
847
- () => password.value && !isValidPassword.value
848
- ? span({ class: 'error' }, 'Must be 8+ characters')
849
- : span()
850
- ),
851
-
852
- button({
853
- type: 'submit',
854
- disabled: () => !canSubmit.value
855
- }, 'Submit')
856
- );
857
-
858
- document.body.appendChild(formEl.domEl);
859
- ```
860
-
861
- ### Dynamic Chart
862
-
863
- ```javascript
864
- const lv = new Lightview();
865
- const { signal, tags } = lv;
866
- const { div, button } = tags;
867
-
868
- const data = signal([10, 20, 15, 30, 25]);
869
-
870
- const addDataPoint = () => {
871
- data.value = [...data.value, Math.floor(Math.random() * 50)];
872
- };
873
-
874
- const chart = div({ class: 'chart' },
875
- button({ onclick: addDataPoint }, 'Add Data Point'),
876
-
877
- () => div({ class: 'bars' },
878
- ...data.value.map((value, i) =>
879
- div({
880
- class: 'bar',
881
- style: () => ({
882
- height: `${value * 3}px`,
883
- width: '40px',
884
- backgroundColor: `hsl(${i * 40}, 70%, 50%)`,
885
- display: 'inline-block',
886
- margin: '0 2px',
887
- transition: 'height 0.3s'
888
- })
889
- }, value.toString())
890
- )
891
- )
892
- );
893
-
894
- document.body.appendChild(chart.domEl);
895
- ```
896
-
897
- ## Hypermedia
898
-
899
- Lightview enhances the use of 'href' and 'src' across any element.
900
-
901
- ## Smart `src` Attribute
902
-
903
- Lightview enhances the `src` attribute with smart loading capabilities. You can use it to inject content from external files or other parts of the DOM.
904
-
905
- ### 1. Fetching Content (HTML/JSON)
906
- If `src` is a file path, Lightview fetches it:
907
-
908
- ```javascript
909
- // Style 2: Element Function
910
- const { element } = new Lightview();
911
-
912
- // Fetches header.html and parses it into reactive elements
913
- element('div', { src: '/components/header.html' }, [])
914
-
915
- // Fetches data.json and converts it to elements
916
- element('div', { src: '/api/data.json' }, [])
917
- ```
918
-
919
- ### 2. DOM Cloning
920
- If `src` is a CSS selector, Lightview clones the targeted DOM elements:
921
-
922
- ```javascript
923
- // Style 3: Plain JSON
924
- const { element } = new Lightview();
925
-
926
- // Clones the element with id="template-sidebar"
927
- element('div', {}, [
928
- {
929
- tag: 'div',
930
- attributes: { src: '#template-sidebar' },
931
- children: []
932
- }
933
- ])
934
- ```
935
-
936
- This is useful for using hidden templates or duplicating content.
937
-
938
- ### 3. Interactive `href` Attribute
939
- On elements (like `div`, `button`, etc.), a non-standard `href` attribute acts as a click trigger for the `src` behavior. When the element is clicked, its `src` attribute is set to the value of its `href`, triggering the content loading or cloning.
940
-
941
- ```javascript
942
- // Style 1: Tagged API
943
- const { tags } = new Lightview();
944
- const { div, button } = tags;
945
-
946
- // On click, this div will load 'content.html' into itself
947
- div({ href: 'content.html' }, 'Click to load content')
948
-
949
- // On click, this button will clone #modal-template into itself
950
- div({ href: '#modal-template' }, 'Open Modal')
951
- ```
952
-
953
- ## HTML Template Literals
954
-
955
- Lightview-X supports reactive template literals in external HTML and JSON files. This allows you to create reusable templates that automatically bind to your global signals and state objects using standard JavaScript template literal syntax.
956
-
957
- **Note:** This feature requires `lightview-x.js` and uses named signals/state registered globally.
958
-
959
- ### Template Literals in HTML Files
960
-
961
- Create HTML templates with `${...}` expressions that reference named signals:
962
-
963
- **template.html:**
964
- ```html
965
- <div class="card">
966
- <h3>Welcome, User!</h3>
967
- <p>Your status: <strong>${signal.get('userStatus').value}</strong></p>
968
- <p>Messages: <span>${signal.get('messageCount').value}</span></p>
969
- <p>Last updated: ${new Date().toLocaleTimeString()}</p>
970
- </div>
971
- ```
972
-
973
- **main.js:**
974
- ```javascript
975
- const { signal, tags } = Lightview;
976
- const { section, button } = tags;
977
-
978
- // Register named signals that the template will reference
979
- const userStatus = signal('Online', 'userStatus');
980
- const messageCount = signal(5, 'messageCount');
981
-
982
- // Load and render the template
983
- const app = section({ src: './template.html' });
984
-
985
- // Update signals - reload template to see changes
986
- userStatus.value = 'Away';
987
- messageCount.value++;
988
- ```
989
-
990
- ### Template Literals in JSON Files
991
-
992
- JSON templates use the same `${...}` syntax within string values to access registered state, provide defaults, or do calculations and conditional logic. You can load these with the 'src' attribute on any element.
993
-
994
- #### Supported JSON Formats
995
-
996
- JSON files loaded via `src` support both **vDOM** and **Object DOM** formats:
997
-
998
- | Format | Structure | Requirement |
999
- |--------|-----------|-------------|
1000
- | **vDOM** | `{ "tag": "div", "attributes": {...}, "children": [...] }` | Works by default |
1001
- | **Object DOM** | `{ "div": { "class": "foo", "children": [...] } }` | Requires `LightviewX.useObjectDOMSyntax()` |
1002
-
1003
- #### vDOM Format (Default)
1004
-
1005
- **template.json:**
1006
- ```json
1007
- [
1008
- {
1009
- "tag": "div",
1010
- "attributes": { "class": "card" },
1011
- "children": [
1012
- {
1013
- "tag": "h3",
1014
- "children": ["Product: ${state.get('product',{name:'Widget One',price:0,inStock:3}).name}"]
1015
- },
1016
- {
1017
- "tag": "p",
1018
- "children": ["Price: $${state.get('product').price}"]
1019
- },
1020
- {
1021
- "tag": "p",
1022
- "children": ["In Stock: ${state.get('product').inStock ? 'Yes ✅' : 'No ❌'}"]
1023
- }
1024
- ]
1025
- }
1026
- ]
1027
- ```
1028
-
1029
- #### Object DOM Format
1030
-
1031
- To use the more compact Object DOM format in JSON files, enable it before loading:
1032
-
1033
- **template-objectdom.json:**
1034
- ```json
1035
- [
1036
- {
1037
- "div": {
1038
- "class": "card",
1039
- "children": [
1040
- { "h3": { "children": ["Product: ${state.get('product').name}"] } },
1041
- { "p": { "children": ["Price: $${state.get('product').price}"] } },
1042
- { "p": { "children": ["In Stock: ${state.get('product').inStock ? 'Yes ✅' : 'No ❌'}"] } }
1043
- ]
1044
- }
1045
- }
1046
- ]
1047
- ```
1048
-
1049
- **main.js:**
1050
- ```javascript
1051
- // Enable Object DOM syntax BEFORE loading JSON templates
1052
- LightviewX.useObjectDOMSyntax();
1053
-
1054
- const { state, tags } = Lightview;
1055
- const { section } = tags;
1056
-
1057
- const product = state({ name: 'Widget', price: 29.99, inStock: true }, 'product');
1058
-
1059
- // Now JSON files can use Object DOM format
1060
- const app = section({ src: './template-objectdom.json' });
1061
- ```
1062
-
1063
- **main.js:**
1064
- ```javascript
1065
- const { state, tags } = Lightview;
1066
- const { section, button } = tags;
1067
-
1068
- // Register named state that the template will reference
1069
- const product = state({
1070
- name: 'Lightview Widget',
1071
- price: 29.99,
1072
- inStock: true
1073
- }, 'product');
1074
-
1075
- // Load and render the JSON template
1076
- const app = section({ src: './template.json' });
1077
-
1078
- // Update state values
1079
- product.name = 'Super Widget';
1080
- product.price = 39.99;
1081
- ```
1082
-
1083
- ### Reloading Templates
1084
-
1085
- To see updated values after changing signals/state, reload the template by re-setting the `src` attribute:
1086
-
1087
- ```javascript
1088
- const { signal, state, tags } = Lightview;
1089
- const { section, button, div } = tags;
1090
-
1091
- // Named signal and state
1092
- const counter = signal(0, 'counter');
1093
- const user = state({ name: 'Alice' }, 'user');
1094
-
1095
- const app = div(
1096
- section({ src: './dashboard.html', id: 'dashboard' }),
1097
-
1098
- button({ onclick: () => counter.value++ }, 'Increment'),
1099
- button({ onclick: () => user.name = 'Bob' }, 'Change User'),
1100
-
1101
- button({ onclick: () => {
1102
- // Reload template to reflect updated values
1103
- const container = document.getElementById('dashboard');
1104
- const el = Lightview.internals.domToElement.get(container);
1105
- if (el) {
1106
- el.attributes = { ...el.attributes, src: './dashboard.html?' + Date.now() };
1107
- }
1108
- }}, 'Reload Template')
1109
- );
1110
- ```
1111
-
1112
- ### Why Use Template Literals?
1113
-
1114
- - **Separation of Concerns**: Keep HTML structure in `.html` files, logic in `.js` files
1115
- - **Reusable Templates**: Share templates across different parts of your application
1116
- - **Server-Side Templates**: Generate templates on the server with dynamic `${...}` expressions
1117
- - **CMS Integration**: Non-developers can edit HTML templates without touching JavaScript
1118
-
1119
- ## Enhancing Existing DOM Elements
1120
-
1121
- Lightview-X provides the `enhance()` function to add reactivity to existing DOM elements. This is useful for progressive enhancement - adding interactivity to server-rendered HTML or gradually migrating a codebase without rebuilding the entire page.
1122
-
1123
- **Note:** This feature requires `lightview-x.js`.
1124
-
1125
- ### Basic Usage
1126
-
1127
- ```html
1128
- <!-- Existing HTML (server-rendered, static HTML, etc.) -->
1129
- <button id="counter-btn" class="btn">Clicked 0 times</button>
1130
- <div id="status-display">Status: Unknown</div>
1131
- ```
1132
-
1133
- ```javascript
1134
- const { signal, state } = Lightview;
1135
-
1136
- // Get or create a named signal with default value
1137
- const counter = signal.get('counter', 0);
1138
-
1139
- // Enhance the button with reactivity
1140
- LightviewX.enhance('#counter-btn', {
1141
- innerText: () => `Clicked ${counter.value} times`,
1142
- onclick: () => counter.value++
1143
- });
1144
-
1145
- // Enhance with innerHTML for richer content
1146
- LightviewX.enhance('#status-display', {
1147
- innerHTML: () => `Status: <strong>${counter.value > 5 ? 'Active' : 'Idle'}</strong>`
1148
- });
1149
- ```
1150
-
1151
- ### API
1152
-
1153
- ```javascript
1154
- LightviewX.enhance(selectorOrNode, options)
1155
- ```
1156
-
1157
- **Parameters:**
1158
- - `selectorOrNode` - CSS selector string or DOM element
1159
- - `options` - Object containing:
1160
- - `innerText` - Static string or reactive function for text content
1161
- - `innerHTML` - Static string or reactive function for HTML content
1162
- - `on*` - Event handlers (`onclick`, `oninput`, etc.)
1163
- - Any other attribute (reactive functions supported)
1164
-
1165
- **Returns:** Lightview reactive element wrapper, or `null` if element not found.
1166
-
1167
- ### signal.get() and state.get() with Defaults
1168
-
1169
- When using `enhance()`, you often need to access or create global state. The `.get()` method now supports a default value that creates and registers the signal/state if it doesn't exist:
1170
-
1171
- ```javascript
1172
- // If 'counter' doesn't exist, creates signal(0) and registers it as 'counter'
1173
- const counter = signal.get('counter', 0);
1174
-
1175
- // If 'user' doesn't exist, creates state({name: 'Guest'}) and registers it as 'user'
1176
- const user = state.get('user', { name: 'Guest' });
1177
-
1178
- // Without default - returns undefined if not registered
1179
- const maybeCounter = signal.get('counter');
1180
- ```
1181
-
1182
- This pattern is similar to `getOrCreate` - the default value is only used if the signal/state hasn't been registered yet.
1183
-
1184
- ### Complete Example
1185
-
1186
- ```html
1187
- <!DOCTYPE html>
1188
- <html>
1189
- <body>
1190
- <!-- Server-rendered content -->
1191
- <div class="card">
1192
- <h2 id="greeting">Hello, Guest!</h2>
1193
- <p id="click-count">Clicks: 0</p>
1194
- <button id="increment">Click Me</button>
1195
- <button id="change-name">Change Name</button>
1196
- </div>
1197
-
1198
- <script src="lightview.js"></script>
1199
- <script src="lightview-x.js"></script>
1200
- <script>
1201
- const { signal, state } = Lightview;
1202
-
1203
- // Create or get global state with defaults
1204
- const clicks = signal.get('clicks', 0);
1205
- const user = state.get('user', { name: 'Guest' });
1206
-
1207
- // Enhance existing elements
1208
- LightviewX.enhance('#greeting', {
1209
- innerText: () => `Hello, ${user.name}!`
1210
- });
1211
-
1212
- LightviewX.enhance('#click-count', {
1213
- innerText: () => `Clicks: ${clicks.value}`
1214
- });
1215
-
1216
- LightviewX.enhance('#increment', {
1217
- onclick: () => clicks.value++
1218
- });
1219
-
1220
- LightviewX.enhance('#change-name', {
1221
- onclick: () => {
1222
- user.name = user.name === 'Guest' ? 'Alice' : 'Guest';
1223
- }
1224
- });
1225
- </script>
1226
- </body>
1227
- </html>
1228
- ```
1229
-
1230
- ### When to Use enhance()
1231
-
1232
- - **Progressive Enhancement**: Add interactivity to static HTML
1233
- - **Server-Side Rendering**: Hydrate server-rendered content
1234
- - **Legacy Integration**: Add reactivity to existing applications
1235
- - **CMS Content**: Enhance content from a CMS without rebuilding
1236
- - **Third-Party Widgets**: Add reactive behavior to elements you don't control
1237
-
1238
- ### enhance() vs element()
1239
-
1240
- | Use Case | Use `enhance()` | Use `element()` |
1241
- |----------|-----------------|------------------|
1242
- | Existing HTML | ✅ | ❌ |
1243
- | Build from scratch | ❌ | ✅ |
1244
- | Server-rendered | ✅ | ❌ |
1245
- | Full control over structure | ❌ | ✅ |
1246
- | Progressive enhancement | ✅ | ❌ |
1247
-
1248
-
1249
- ## Framework Comparison
1250
-
1251
- Lightview offers a unique combination of features that sets it apart from other reactive libraries. Here's how it compares:
1252
-
1253
- ### Features
1254
-
1255
- | Feature | Lightview | Vue 3 | SolidJS | Svelte | Bau.js | Juris.js |
1256
- |---------|-----------|-------|---------|--------|--------|----------|
1257
- | **Bundle Size** | ~6.5KB | ~33KB | ~7KB | ~2KB* | ~2KB | ~50KB |
1258
- | **Reactivity Model** | Signals/Proxy | Proxy | Signals | Compiler | Proxy | Intentional |
1259
- | **No Build Required** | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
1260
- | **No JSX/Templates** | ✅ | ❌ (SFC) | ❌ (JSX) | ❌ (Templates) | ✅ | ✅ |
1261
- | **Tagged API** | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
1262
- | **Plain Objects** | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
1263
- | **Hypermedia** *| ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
1264
- | **HTML Template Literals** *| ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
1265
-
1266
- *Svelte size is after compilation. However, substantive Svelete apps are often larger than those created with other libraries due to the lack of rutime abstractions after compilation/transpilation.
1267
-
1268
- *Lighview Hypermedia and HTML Template Literals require lightview-x, an additional 5K.
1269
-
1270
- ### Reactive Capabilities
1271
-
1272
- | Feature | Lightview | Vue 3 | SolidJS | Svelte | Bau.js | Juris.js |
1273
- |---------|-----------|-------|---------|--------|--------|----------|
1274
- | **Array Mutations** | ✅ Auto | ✅ Auto | ❌ Manual** | ✅ Auto | ✅ Auto | ❌ Manual |
1275
- | **Array Element Mutations** | ✅ **Auto** | ❌ Manual | ❌ Manual | ❌ Manual | ❌ Manual | ❌ Manual |
1276
- | **Date Mutations** | ✅ **Auto** | ❌ Manual | ❌ Manual | ❌ Manual | ❌ Manual | ❌ Manual |
1277
- | **Deep Reactivity** | ✅ | ✅ | ✅*** | ✅ | ✅ | ✅ |
1278
- | **Computed Values** | ✅ | ✅ | ✅ | ✅ | ✅ (derive) | ✅ |
1279
- | **Ehancing Existing HTML** | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
1280
- | **Fine-grained Updates** | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
1281
- | **Auto Cleanup** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
1282
-
1283
- **SolidJS supports array mutations with `createStore`, but not with `createSignal`
1284
- ***SolidJS requires `createStore` for deep reactivity
1285
-
1286
- ### Developer Experience
1287
-
1288
- | Feature | Lightview | Vue 3 | SolidJS | Svelte | Bau.js | Juris.js |
1289
- |---------|-----------|-------|---------|--------|--------|----------|
1290
- | **Learning Curve** | Low | Medium | Medium | Low | Low | Medium |
1291
- | **TypeScript** | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
1292
- | **DevTools** | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
1293
- | **Lifecycle Hooks** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
1294
- | **Progressive Enhancement** | ✅ (src/href) | ❌ | ❌ | ❌ | ❌ | ✅ (enhance) |
1295
- | **Multiple Instances** | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
1296
-
1297
- ### Unique Features
1298
-
1299
- **Lightview's Standout Features:**
1300
- - 🎯 **Reactive Date Objects** - Only library with automatic Date mutation tracking
1301
- - 📊 **Reactive Array Elements** - Full deep reactivity for array elements (rare feature!)
1302
- - 🏷️ **Tagged API** - Bau.js-inspired concise syntax (`tags.div()`)
1303
- - 🔗 **Smart src/href** - Load HTML/JSON or clone DOM elements declaratively
1304
- - 📝 **HTML Template Literals** - Use `${signal.get('name').value}` in external HTML/JSON files
1305
- - 🔧 **Progressive Enhancement** - Enhance existing DOM with `enhance()` for server-rendered content
1306
- - 🧩 **Component Functions** - Use functions as tags, with registry and global scope support
1307
- - 🧹 **Automatic Cleanup** - MutationObserver-based memory management
1308
- - 📦 **Zero Dependencies** - Pure JavaScript, no build tools needed
1309
- - 🎨 **Five API Styles** - Tagged, Element function, vDOM JSON, Object DOM JSON, Component functions
1310
-
1311
- **When to Choose Lightview:**
1312
- - ✅ You want minimal bundle size with maximum features
1313
- - ✅ You need reactive Date objects (calendars, timers, scheduling apps)
1314
- - ✅ You prefer no build step and pure JavaScript
1315
- - ✅ You like the simplicity of Bau.js but want more power
1316
- - ✅ You're building hypermedia-driven applications
81
+ ## Why Lightview?
1317
82
 
1318
- **When to Choose Alternatives:**
1319
- - Vue 3: Large ecosystem, TypeScript, mature tooling
1320
- - SolidJS: Maximum performance, fine-grained reactivity
1321
- - Svelte: Best DX, compiler optimizations
1322
- - Bau.js: Even simpler API, minimal features
1323
- - Juris.js: Object-first architecture, intentional reactivity
83
+ - **Zero Build Step**: No compiler or bundler required.
84
+ - **Deep Reactivity**: Automatic tracking of nested object and array mutations.
85
+ - **Hypermedia Built-in**: Fetch HTML/JSON components via `src` attributes.
86
+ - **Isolated Components**: Shadow DOM support with automatic DaisyUI theme integration.
87
+ - **Fully Automatic Cleanup**: Memory-safe reactivity via MutationObserver.
1324
88
 
1325
- ## Browser Support
89
+ ## Documentation
1326
90
 
91
+ For detailed API references, component gallery, and tutorials, visit [lightview.dev](https://lightview.dev).
1327
92
 
1328
- Modern browsers with Proxy support (ES6+):
1329
- - Chrome 49+
1330
- - Firefox 18+
1331
- - Safari 10+
1332
- - Edge 12+
93
+ - [Getting Started](https://lightview.dev/docs/getting-started)
94
+ - [Syntax Comparison](https://lightview.dev/docs/syntax)
95
+ - [API Reference](https://lightview.dev/docs/api)
96
+ - [Component Library](https://lightview.dev/docs/components)
1333
97
 
1334
98
  ## License
1335
99