lightview 1.8.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/.agent/workflows/daisyui-component-migration.md +155 -0
  2. package/.codacy/cli.sh +149 -0
  3. package/.codacy/codacy.yaml +15 -0
  4. package/.github/instructions/codacy.instructions.md +72 -0
  5. package/.wranglerignore +21 -0
  6. package/README.md +1330 -19
  7. package/_headers +4 -0
  8. package/build.js +70 -0
  9. package/components/actions/button.js +151 -0
  10. package/components/actions/dropdown.js +120 -0
  11. package/components/actions/modal.js +146 -0
  12. package/components/actions/swap.js +118 -0
  13. package/components/daisyui.js +288 -0
  14. package/components/data-display/accordion.js +128 -0
  15. package/components/data-display/alert.js +112 -0
  16. package/components/data-display/avatar.js +170 -0
  17. package/components/data-display/badge.js +82 -0
  18. package/components/data-display/card.js +151 -0
  19. package/components/data-display/carousel.js +94 -0
  20. package/components/data-display/chart.js +220 -0
  21. package/components/data-display/chat.js +128 -0
  22. package/components/data-display/collapse.js +103 -0
  23. package/components/data-display/countdown.js +69 -0
  24. package/components/data-display/diff.js +111 -0
  25. package/components/data-display/kbd.js +65 -0
  26. package/components/data-display/loading.js +75 -0
  27. package/components/data-display/progress.js +79 -0
  28. package/components/data-display/radial-progress.js +88 -0
  29. package/components/data-display/skeleton.js +66 -0
  30. package/components/data-display/stats.js +159 -0
  31. package/components/data-display/table.js +146 -0
  32. package/components/data-display/timeline.js +146 -0
  33. package/components/data-display/toast.js +72 -0
  34. package/components/data-display/tooltip.js +74 -0
  35. package/components/data-input/checkbox.js +253 -0
  36. package/components/data-input/file-input.js +224 -0
  37. package/components/data-input/input.js +264 -0
  38. package/components/data-input/radio.js +338 -0
  39. package/components/data-input/range.js +204 -0
  40. package/components/data-input/rating.js +219 -0
  41. package/components/data-input/select.js +287 -0
  42. package/components/data-input/textarea.js +287 -0
  43. package/components/data-input/toggle.js +201 -0
  44. package/components/index.js +137 -0
  45. package/components/layout/divider.js +72 -0
  46. package/components/layout/drawer.js +142 -0
  47. package/components/layout/footer.js +100 -0
  48. package/components/layout/hero.js +109 -0
  49. package/components/layout/indicator.js +90 -0
  50. package/components/layout/join.js +78 -0
  51. package/components/layout/navbar.js +110 -0
  52. package/components/navigation/breadcrumbs.js +91 -0
  53. package/components/navigation/dock.js +103 -0
  54. package/components/navigation/menu.js +126 -0
  55. package/components/navigation/pagination.js +105 -0
  56. package/components/navigation/steps.js +89 -0
  57. package/components/navigation/tabs.css +177 -0
  58. package/components/navigation/tabs.js +123 -0
  59. package/components/theme/theme-switch.css +65 -0
  60. package/components/theme/theme-switch.js +177 -0
  61. package/docs/about.html +164 -0
  62. package/docs/api/computed.html +184 -0
  63. package/docs/api/effects.html +173 -0
  64. package/docs/api/elements.html +180 -0
  65. package/docs/api/enhance.html +225 -0
  66. package/docs/api/hypermedia.html +165 -0
  67. package/docs/api/index.html +178 -0
  68. package/docs/api/nav.html +18 -0
  69. package/docs/api/signals.html +136 -0
  70. package/docs/api/state.html +217 -0
  71. package/docs/assets/images/logo-favicon.svg +42 -0
  72. package/docs/assets/images/logo-static.svg +40 -0
  73. package/docs/assets/images/logo.svg +66 -0
  74. package/docs/assets/js/examplify.js +395 -0
  75. package/docs/assets/styles/site.css +1102 -0
  76. package/docs/assets/styles/themes.css +236 -0
  77. package/docs/components/accordion.html +439 -0
  78. package/docs/components/alert.html +528 -0
  79. package/docs/components/avatar.html +586 -0
  80. package/docs/components/badge.html +531 -0
  81. package/docs/components/breadcrumbs.html +278 -0
  82. package/docs/components/button.html +579 -0
  83. package/docs/components/card.html +561 -0
  84. package/docs/components/carousel.html +286 -0
  85. package/docs/components/chart-area.html +702 -0
  86. package/docs/components/chart-bar.html +782 -0
  87. package/docs/components/chart-column.html +735 -0
  88. package/docs/components/chart-line.html +794 -0
  89. package/docs/components/chart-pie.html +823 -0
  90. package/docs/components/chart.html +610 -15
  91. package/docs/components/chat.html +547 -0
  92. package/docs/components/checkbox.html +641 -0
  93. package/docs/components/collapse.html +536 -0
  94. package/docs/components/component-nav.html +53 -0
  95. package/docs/components/countdown.html +470 -0
  96. package/docs/components/diff.html +245 -0
  97. package/docs/components/divider.html +240 -0
  98. package/docs/components/dock.html +277 -0
  99. package/docs/components/drawer.html +515 -0
  100. package/docs/components/dropdown.html +479 -0
  101. package/docs/components/file-input.html +591 -0
  102. package/docs/components/footer.html +301 -0
  103. package/docs/components/gallery.html +504 -0
  104. package/docs/components/hero.html +264 -0
  105. package/docs/components/index.css +840 -0
  106. package/docs/components/index.html +735 -0
  107. package/docs/components/indicator.html +342 -0
  108. package/docs/components/input.html +644 -0
  109. package/docs/components/join.html +285 -0
  110. package/docs/components/kbd.html +322 -0
  111. package/docs/components/loading.html +521 -0
  112. package/docs/components/menu.html +461 -0
  113. package/docs/components/modal.html +639 -0
  114. package/docs/components/navbar.html +321 -0
  115. package/docs/components/pagination.html +279 -0
  116. package/docs/components/progress.html +514 -0
  117. package/docs/components/radial-progress.html +434 -0
  118. package/docs/components/radio.html +655 -0
  119. package/docs/components/range.html +611 -0
  120. package/docs/components/rating.html +642 -0
  121. package/docs/components/select.html +696 -0
  122. package/docs/components/sidebar-setup.js +93 -0
  123. package/docs/components/skeleton.html +447 -0
  124. package/docs/components/spinner.html +68 -0
  125. package/docs/components/stats.html +486 -0
  126. package/docs/components/steps.html +356 -0
  127. package/docs/components/swap.html +517 -0
  128. package/docs/components/switch.html +68 -0
  129. package/docs/components/table.html +668 -0
  130. package/docs/components/tabs.html +506 -0
  131. package/docs/components/text-input.html +68 -0
  132. package/docs/components/textarea.html +603 -0
  133. package/docs/components/timeline.html +485 -42
  134. package/docs/components/toast.html +474 -0
  135. package/docs/components/toggle.html +564 -0
  136. package/docs/components/tooltip.html +423 -0
  137. package/docs/examples/getting-started-example.html +40 -0
  138. package/docs/examples/index.html +93 -0
  139. package/docs/getting-started/index.html +739 -0
  140. package/docs/getting-started/reviews.html +23 -0
  141. package/docs/getting-started/reviews.odom +108 -0
  142. package/docs/getting-started/reviews.vdom +84 -0
  143. package/docs/index.html +132 -42
  144. package/docs/playground.html +416 -0
  145. package/docs/router.html +285 -0
  146. package/docs/styles/index.html +190 -0
  147. package/functions/_middleware.js +32 -0
  148. package/index.html +309 -0
  149. package/lightview-router.js +364 -0
  150. package/lightview-x.js +1577 -0
  151. package/lightview.js +659 -1200
  152. package/lightview.js.backup +793 -0
  153. package/middleware/locale.js +25 -0
  154. package/middleware/markdown.js +44 -0
  155. package/middleware/notFound.js +37 -0
  156. package/package.json +27 -41
  157. package/watch.js +92 -0
  158. package/wrangler.toml +12 -0
  159. package/.idea/lightview.iml +0 -12
  160. package/.idea/modules.xml +0 -8
  161. package/.idea/vcs.xml +0 -6
  162. package/LICENSE +0 -21
  163. package/codepen-no-tabs-embed.css +0 -2
  164. package/docs/CNAME +0 -1
  165. package/docs/api.html +0 -674
  166. package/docs/blank.html +0 -10
  167. package/docs/comparedto.html +0 -89
  168. package/docs/components/chart-repl.html +0 -69
  169. package/docs/components/components.js +0 -113
  170. package/docs/components/contents.html +0 -17
  171. package/docs/components/gantt-repl.html +0 -61
  172. package/docs/components/gantt.html +0 -42
  173. package/docs/components/gauge-repl.html +0 -66
  174. package/docs/components/gauge.html +0 -20
  175. package/docs/components/orgchart-repl.html +0 -64
  176. package/docs/components/orgchart.html +0 -41
  177. package/docs/components/repl-as-src.html +0 -17
  178. package/docs/components/repl-repl.html +0 -95
  179. package/docs/components/repl.html +0 -527
  180. package/docs/components/timeline-repl.html +0 -72
  181. package/docs/components.html +0 -14
  182. package/docs/css/highlightjs.min.css +0 -9
  183. package/docs/css/tutorial.css +0 -35
  184. package/docs/examples/anchor.html +0 -11
  185. package/docs/examples/chart.html +0 -34
  186. package/docs/examples/counter.html +0 -26
  187. package/docs/examples/counter.test.mjs +0 -47
  188. package/docs/examples/counter2.html +0 -26
  189. package/docs/examples/directives.html +0 -79
  190. package/docs/examples/foreign.html +0 -50
  191. package/docs/examples/forgeinform.html +0 -98
  192. package/docs/examples/form.html +0 -61
  193. package/docs/examples/gauge.html +0 -18
  194. package/docs/examples/invalid-template-literals.html +0 -44
  195. package/docs/examples/medium/remote.html +0 -60
  196. package/docs/examples/message.html +0 -18
  197. package/docs/examples/nested.html +0 -11
  198. package/docs/examples/object-bound-form.html +0 -34
  199. package/docs/examples/remote-server.js +0 -51
  200. package/docs/examples/remote.html +0 -34
  201. package/docs/examples/remote.json +0 -1
  202. package/docs/examples/scratch.html +0 -69
  203. package/docs/examples/sensors/index.html +0 -44
  204. package/docs/examples/sensors/sensor-server.js +0 -30
  205. package/docs/examples/shared.html +0 -41
  206. package/docs/examples/template.html +0 -33
  207. package/docs/examples/timeline.html +0 -21
  208. package/docs/examples/todo.html +0 -40
  209. package/docs/examples/top.html +0 -10
  210. package/docs/examples/types.html +0 -94
  211. package/docs/examples/xor.html +0 -62
  212. package/docs/examples.html +0 -25
  213. package/docs/javascript/codejar.min.js +0 -8
  214. package/docs/javascript/highlightjs.min.js +0 -1173
  215. package/docs/javascript/isomorphic-git.js +0 -9
  216. package/docs/javascript/json5.min.js +0 -1
  217. package/docs/javascript/lightning-fs.js +0 -1
  218. package/docs/javascript/lightview.js +0 -1285
  219. package/docs/javascript/marked.min.js +0 -6
  220. package/docs/javascript/peerjs.min.js +0 -70
  221. package/docs/javascript/turndown.js +0 -973
  222. package/docs/javascript/types.js +0 -606
  223. package/docs/javascript/utils.js +0 -45
  224. package/docs/lightview.html +0 -63
  225. package/docs/old_index.html +0 -965
  226. package/docs/old_index.md +0 -1132
  227. package/docs/slidein.html +0 -51
  228. package/docs/tutorial/0-getting-started.html +0 -67
  229. package/docs/tutorial/1-intro-to-variables.html +0 -103
  230. package/docs/tutorial/10-template-components.html +0 -80
  231. package/docs/tutorial/11-linked-components.html +0 -76
  232. package/docs/tutorial/12-imported-components.html +0 -67
  233. package/docs/tutorial/13-input-binding.html +0 -94
  234. package/docs/tutorial/14-automatic-variable-creation.html +0 -74
  235. package/docs/tutorial/15-form-binding.html +0 -110
  236. package/docs/tutorial/16-if-directive.html +0 -60
  237. package/docs/tutorial/17-loop-directives.html +0 -83
  238. package/docs/tutorial/18-sanitizing-and-escaping-input.html +0 -79
  239. package/docs/tutorial/2-imported-and-exported-variables.html +0 -80
  240. package/docs/tutorial/3-data-types.html +0 -89
  241. package/docs/tutorial/4-extended-data-types.html +0 -83
  242. package/docs/tutorial/5-extended-functional-types.html +0 -96
  243. package/docs/tutorial/5.1-extended-functional-types.html +0 -79
  244. package/docs/tutorial/5.2-extended-functional-types.html +0 -70
  245. package/docs/tutorial/6-conventional-javascript.html +0 -75
  246. package/docs/tutorial/7-monitoring-with-observers.html +0 -107
  247. package/docs/tutorial/8-event-listeners.html +0 -65
  248. package/docs/tutorial/9-intro-to-components.html +0 -91
  249. package/docs/tutorial/contents.html +0 -32
  250. package/docs/tutorial/my-component.html +0 -29
  251. package/docs/tutorial/remote-value.json +0 -4
  252. package/docs/websiterepl.html +0 -46
  253. package/jest-puppeteer.config.js +0 -5
  254. package/jest.config.json +0 -12
  255. package/lightview.min.js +0 -1
  256. package/lightview_good.js +0 -1267
  257. package/lightview_optimized.js +0 -1274
  258. package/repl_hold.html +0 -320
  259. package/test/basic.html +0 -104
  260. package/test/basic.test.mjs +0 -315
  261. package/test/extended.html +0 -29
  262. package/test/extended.test.mjs +0 -448
  263. package/types.js +0 -607
  264. package/unsplash.key +0 -1
@@ -0,0 +1,184 @@
1
+ <!-- SEO-friendly SPA Shim -->
2
+ <script src="/lightview-router.js"></script>
3
+ <script>
4
+ if (window.LightviewRouter) {
5
+ LightviewRouter.base('/index.html');
6
+ }
7
+ </script>
8
+
9
+ <div class="docs-layout">
10
+ <aside class="docs-sidebar" src="./nav.html"></aside>
11
+
12
+ <main class="docs-content">
13
+ <h1>Computed</h1>
14
+ <p>
15
+ Computed values are derived from signals. They update automatically when their dependencies change.
16
+ Think of them as formulas that always stay in sync.
17
+ </p>
18
+
19
+ <h2>Basic Usage</h2>
20
+ <pre><code>const { signal, computed } = Lightview;
21
+
22
+ const count = signal(10);
23
+ const doubled = computed(() => count.value * 2);
24
+
25
+ console.log(doubled.value); // 20
26
+
27
+ count.value = 5;
28
+ console.log(doubled.value); // 10 (automatically updated!)</code></pre>
29
+
30
+ <h2>Chaining Computed Values</h2>
31
+ <p>Computed values can depend on other computed values:</p>
32
+ <pre><code>const price = signal(100);
33
+ const quantity = signal(2);
34
+ const taxRate = signal(0.1);
35
+
36
+ const subtotal = computed(() => price.value * quantity.value);
37
+ const tax = computed(() => subtotal.value * taxRate.value);
38
+ const total = computed(() => subtotal.value + tax.value);
39
+
40
+ console.log(total.value); // 220</code></pre>
41
+
42
+ <h2>Reading Computed Values</h2>
43
+ <pre><code>// Same as signals - two ways to read
44
+ console.log(doubled.value); // Property access
45
+ console.log(doubled()); // Function call</code></pre>
46
+
47
+ <h2>In the UI</h2>
48
+ <p>
49
+ Computed values work seamlessly in your UI, just like signals:
50
+ </p>
51
+
52
+ <div class="code-example">
53
+ <div class="code-example-preview" id="computed-demo"></div>
54
+ <div class="code-example-code">
55
+ <pre><code>const price = signal(100);
56
+ const quantity = signal(1);
57
+ const total = computed(() => price.value * quantity.value);
58
+
59
+ div(
60
+ p(() => `Price: $${price.value}`),
61
+ p(() => `Quantity: ${quantity.value}`),
62
+ p(() => `Total: $${total.value}`),
63
+ button({ onclick: () => quantity.value++ }, 'Add One')
64
+ )</code></pre>
65
+ </div>
66
+ </div>
67
+
68
+ <h2>When to Use Computed</h2>
69
+ <ul>
70
+ <li><strong>Derived values</strong> — Calculations based on other state</li>
71
+ <li><strong>Formatting</strong> — Display formatting (dates, currency, etc.)</li>
72
+ <li><strong>Filtering/Sorting</strong> — Processed lists from raw data</li>
73
+ <li><strong>Validation</strong> — Form validity based on field values</li>
74
+ </ul>
75
+
76
+ <h3>Example: Filtered List</h3>
77
+ <pre><code>const todos = signal([
78
+ { text: 'Learn Lightview', done: true },
79
+ { text: 'Build something cool', done: false },
80
+ { text: 'Ship it', done: false }
81
+ ]);
82
+
83
+ const filter = signal('all'); // 'all', 'active', 'done'
84
+
85
+ const filteredTodos = computed(() => {
86
+ const list = todos.value;
87
+ switch (filter.value) {
88
+ case 'active': return list.filter(t => !t.done);
89
+ case 'done': return list.filter(t => t.done);
90
+ default: return list;
91
+ }
92
+ });</code></pre>
93
+
94
+ <h3>Example: Form Validation</h3>
95
+ <pre><code>const email = signal('');
96
+ const password = signal('');
97
+
98
+ const isEmailValid = computed(() =>
99
+ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)
100
+ );
101
+
102
+ const isPasswordValid = computed(() =>
103
+ password.value.length >= 8
104
+ );
105
+
106
+ const canSubmit = computed(() =>
107
+ isEmailValid.value && isPasswordValid.value
108
+ );
109
+
110
+ button({ disabled: () => !canSubmit.value }, 'Submit')</code></pre>
111
+
112
+ <h2>Computed vs Effect</h2>
113
+ <p>
114
+ Both react to changes, but serve different purposes:
115
+ </p>
116
+ <table class="api-table">
117
+ <thead>
118
+ <tr>
119
+ <th>Computed</th>
120
+ <th>Effect</th>
121
+ </tr>
122
+ </thead>
123
+ <tbody>
124
+ <tr>
125
+ <td>Returns a value</td>
126
+ <td>Doesn't return anything useful</td>
127
+ </tr>
128
+ <tr>
129
+ <td>Pure (no side effects)</td>
130
+ <td>For side effects</td>
131
+ </tr>
132
+ <tr>
133
+ <td>Lazy (computed when read)</td>
134
+ <td>Eager (runs immediately)</td>
135
+ </tr>
136
+ <tr>
137
+ <td>Use in UI for derived data</td>
138
+ <td>Use for logging, storage, API calls</td>
139
+ </tr>
140
+ </tbody>
141
+ </table>
142
+ </main>
143
+ </div>
144
+
145
+ <script>
146
+ (function () {
147
+ const { signal, computed, tags } = Lightview;
148
+ const { div, p, button, span } = tags;
149
+
150
+ const price = signal(100);
151
+ const quantity = signal(1);
152
+ const total = computed(() => price.value * quantity.value);
153
+
154
+ const demo = div({ style: 'padding: 0.5rem;' },
155
+ div({ style: 'display: grid; gap: 0.5rem; margin-bottom: 1rem;' },
156
+ p({ style: 'margin: 0;' },
157
+ span({ style: 'color: var(--site-text-secondary);' }, 'Price: '),
158
+ span(() => `$${price.value}`)
159
+ ),
160
+ p({ style: 'margin: 0;' },
161
+ span({ style: 'color: var(--site-text-secondary);' }, 'Quantity: '),
162
+ span(() => quantity.value)
163
+ ),
164
+ p({ style: 'margin: 0; font-weight: 600; font-size: 1.125rem;' },
165
+ span({ style: 'color: var(--site-text-secondary);' }, 'Total: '),
166
+ span({ style: 'color: var(--site-primary);' }, () => `$${total.value}`)
167
+ )
168
+ ),
169
+ div({ style: 'display: flex; gap: 0.5rem;' },
170
+ button({
171
+ onclick: () => quantity.value = Math.max(1, quantity.value - 1),
172
+ style: 'padding: 0.5rem 1rem; cursor: pointer; border: 1px solid var(--site-border); background: var(--site-surface); border-radius: 6px;'
173
+ }, '−'),
174
+ button({
175
+ onclick: () => quantity.value++,
176
+ style: 'padding: 0.5rem 1rem; cursor: pointer; border: 1px solid var(--site-border); background: var(--site-surface); border-radius: 6px;'
177
+ }, '+')
178
+ )
179
+ );
180
+
181
+ const container = document.getElementById('computed-demo');
182
+ if (container) container.appendChild(demo.domEl);
183
+ })();
184
+ </script>
@@ -0,0 +1,173 @@
1
+ <!-- SEO-friendly SPA Shim -->
2
+ <script src="/lightview-router.js"></script>
3
+ <script>
4
+ if (window.LightviewRouter) {
5
+ LightviewRouter.base('/index.html');
6
+ }
7
+ </script>
8
+
9
+ <div class="docs-layout">
10
+ <aside class="docs-sidebar" src="./nav.html"></aside>
11
+ <main class="docs-content">
12
+ <h1>Effects</h1>
13
+ <p>
14
+ Effects let you run side effects when reactive state changes.
15
+ Logging, DOM manipulation, API calls—anything that needs to happen <em>because</em> something changed.
16
+ </p>
17
+
18
+ <h2>Basic Usage</h2>
19
+ <pre><code>const { signal, effect } = Lightview;
20
+
21
+ const count = signal(0);
22
+
23
+ // This runs immediately, then re-runs when count changes
24
+ effect(() => {
25
+ console.log('Count is now:', count.value);
26
+ });
27
+
28
+ count.value = 1; // Logs: "Count is now: 1"
29
+ count.value = 2; // Logs: "Count is now: 2"</code></pre>
30
+
31
+ <h2>Automatic Dependency Tracking</h2>
32
+ <p>
33
+ Effects automatically track which signals they read. No need to declare dependencies—Lightview figures it
34
+ out:
35
+ </p>
36
+ <pre><code>const firstName = signal('Alice');
37
+ const lastName = signal('Smith');
38
+ const showFull = signal(true);
39
+
40
+ effect(() => {
41
+ if (showFull.value) {
42
+ // Tracks firstName, lastName, and showFull
43
+ console.log(`${firstName.value} ${lastName.value}`);
44
+ } else {
45
+ // Only tracks firstName and showFull
46
+ console.log(firstName.value);
47
+ }
48
+ });</code></pre>
49
+
50
+ <h2>Stopping Effects</h2>
51
+ <p>
52
+ Effects return a stop function:
53
+ </p>
54
+ <pre><code>const count = signal(0);
55
+
56
+ const stop = effect(() => {
57
+ console.log('Count:', count.value);
58
+ });
59
+
60
+ count.value = 1; // Logs
61
+ count.value = 2; // Logs
62
+
63
+ stop(); // Stop the effect
64
+
65
+ count.value = 3; // Nothing logged</code></pre>
66
+
67
+ <h2>Common Patterns</h2>
68
+
69
+ <h3>Syncing to External Systems</h3>
70
+ <pre><code>const theme = signal('light');
71
+
72
+ effect(() => {
73
+ document.body.setAttribute('data-theme', theme.value);
74
+ });</code></pre>
75
+
76
+ <h3>Local Storage Persistence</h3>
77
+ <pre><code>const settings = signal(
78
+ JSON.parse(localStorage.getItem('settings')) || {}
79
+ );
80
+
81
+ effect(() => {
82
+ localStorage.setItem('settings', JSON.stringify(settings.value));
83
+ });</code></pre>
84
+
85
+ <h3>API Calls</h3>
86
+ <pre><code>const userId = signal(1);
87
+ const userData = signal(null);
88
+
89
+ effect(async () => {
90
+ const id = userId.value;
91
+ const response = await fetch(`/api/users/${id}`);
92
+ userData.value = await response.json();
93
+ });</code></pre>
94
+
95
+ <h2>Effects vs Computed</h2>
96
+ <table class="api-table">
97
+ <thead>
98
+ <tr>
99
+ <th>Use Case</th>
100
+ <th>Use This</th>
101
+ </tr>
102
+ </thead>
103
+ <tbody>
104
+ <tr>
105
+ <td>Derive a value from signals</td>
106
+ <td><code>computed()</code></td>
107
+ </tr>
108
+ <tr>
109
+ <td>Side effects (logging, API calls, DOM)</td>
110
+ <td><code>effect()</code></td>
111
+ </tr>
112
+ <tr>
113
+ <td>Value needed in UI</td>
114
+ <td><code>computed()</code></td>
115
+ </tr>
116
+ <tr>
117
+ <td>Just need to "do something"</td>
118
+ <td><code>effect()</code></td>
119
+ </tr>
120
+ </tbody>
121
+ </table>
122
+
123
+ <div class="code-example">
124
+ <div class="code-example-preview" id="effect-demo"></div>
125
+ <div class="code-example-code">
126
+ <pre><code>const name = signal('World');
127
+ const log = signal([]);
128
+
129
+ effect(() => {
130
+ log.value = [...log.value, `Hello, ${name.value}!`].slice(-5);
131
+ });
132
+
133
+ // Change name to see the effect in action</code></pre>
134
+ </div>
135
+ </div>
136
+ </main>
137
+ </div>
138
+
139
+ <script>
140
+ (function () {
141
+ const { signal, effect, tags } = Lightview;
142
+ const { div, p, input, ul, li, label } = tags;
143
+
144
+ const name = signal('World');
145
+ const log = signal([]);
146
+
147
+ effect(() => {
148
+ const entry = `${new Date().toLocaleTimeString()} - Hello, ${name.value}!`;
149
+ log.value = [...log.value, entry].slice(-5);
150
+ });
151
+
152
+ const demo = div({ style: 'padding: 0.5rem;' },
153
+ div({ style: 'margin-bottom: 1rem;' },
154
+ label({ style: 'display: block; margin-bottom: 0.25rem; font-size: 0.875rem; color: var(--site-text-secondary);' }, 'Change name to trigger effect:'),
155
+ input({
156
+ type: 'text',
157
+ value: name.value,
158
+ oninput: (e) => name.value = e.target.value,
159
+ style: 'padding: 0.5rem; border: 1px solid var(--site-border); border-radius: 4px; width: 200px;'
160
+ })
161
+ ),
162
+ div(
163
+ p({ style: 'font-size: 0.875rem; color: var(--site-text-secondary); margin: 0 0 0.5rem;' }, 'Effect log (last 5):'),
164
+ ul({ style: 'margin: 0; padding-left: 1.25rem; font-family: var(--site-font-mono); font-size: 0.8125rem;' },
165
+ () => log.value.map(entry => li(entry))
166
+ )
167
+ )
168
+ );
169
+
170
+ const container = document.getElementById('effect-demo');
171
+ if (container) container.appendChild(demo.domEl);
172
+ })();
173
+ </script>
@@ -0,0 +1,180 @@
1
+ <!-- SEO-friendly SPA Shim -->
2
+ <script src="/lightview-router.js"></script>
3
+ <script>
4
+ if (window.LightviewRouter) {
5
+ LightviewRouter.base('/index.html');
6
+ }
7
+ </script>
8
+
9
+ <link rel="stylesheet" href="../components/index.css">
10
+
11
+ <div class="docs-layout">
12
+ <aside class="docs-sidebar" src="./nav.html"></aside>
13
+
14
+ <main class="docs-content">
15
+ <h1>Creating Elements</h1>
16
+ <p>
17
+ Lightview gives you three ways to build UI. Pick your favorite - or mix and match.
18
+ They all use the same reactive system under the hood.
19
+ </p>
20
+ <h2>Comparison</h2>
21
+ <table class="api-table">
22
+ <thead>
23
+ <tr>
24
+ <th>Style</th>
25
+ <th>Pros</th>
26
+ <th>Cons</th>
27
+ </tr>
28
+ </thead>
29
+ <tbody>
30
+ <tr>
31
+ <td><strong>Tagged API</strong></td>
32
+ <td>Most concise, natural to write</td>
33
+ <td>Requires destructuring</td>
34
+ </tr>
35
+ <tr>
36
+ <td><strong>vDOM</strong></td>
37
+ <td>JSON serializable, easy to validate</td>
38
+ <td>Most verbose</td>
39
+ </tr>
40
+ <tr>
41
+ <td><strong>Object DOM</strong></td>
42
+ <td>Compact JSON, clean templates</td>
43
+ <td>Harder to validate than explicit vDOM</td>
44
+ </tr>
45
+ </tbody>
46
+ </table>
47
+ <!-- Tabs -->
48
+ <script>
49
+ window.switchSyntaxTab = (tabId) => {
50
+ const tabs = ['tagged', 'vdom', 'object'];
51
+ tabs.forEach(t => {
52
+ const tabEl = document.getElementById(`tab-btn-${t}`);
53
+ const contentEl = document.getElementById(`syntax-${t}`);
54
+ if (t === tabId) {
55
+ tabEl.classList.add('syntax-tab-active');
56
+ contentEl.style.display = 'block';
57
+ } else {
58
+ tabEl.classList.remove('syntax-tab-active');
59
+ contentEl.style.display = 'none';
60
+ }
61
+ });
62
+ };
63
+ </script>
64
+ <div role="tablist" class="syntax-tabs" style="margin-bottom: 1rem;">
65
+ <button id="tab-btn-tagged" role="tab" class="syntax-tab syntax-tab-active"
66
+ onclick="switchSyntaxTab('tagged')">Tagged API</button>
67
+ <button id="tab-btn-vdom" role="tab" class="syntax-tab" onclick="switchSyntaxTab('vdom')">vDOM</button>
68
+ <button id="tab-btn-object" role="tab" class="syntax-tab" onclick="switchSyntaxTab('object')">Object
69
+ DOM</button>
70
+ </div>
71
+
72
+ <!-- Tagged Syntax -->
73
+ <div id="syntax-tagged">
74
+ <pre><script>
75
+ examplify(document.currentScript.nextElementSibling, {
76
+ at: document.currentScript.parentElement,
77
+ scripts: ['/lightview.js', '/lightview-x.js'],
78
+ type: 'module',
79
+ minHeight: 120
80
+ });
81
+ </script><code contenteditable="true">const { signal, tags, $ } = Lightview;
82
+ const { div, h1, p, button } = tags;
83
+ const count = signal(0);
84
+ const app = div({ class: 'container' },
85
+ h1('Hello Lightview'),
86
+ p(() => `Count: ${count.value}`),
87
+ button({ onclick: () => count.value++ }, 'Click me')
88
+ );
89
+ $('#example').content(app);</code></pre>
90
+ </div>
91
+
92
+ <!-- vDOM Syntax -->
93
+ <div id="syntax-vdom" style="display: none;">
94
+ <pre><script>
95
+ examplify(document.currentScript.nextElementSibling, {
96
+ at: document.currentScript.parentElement,
97
+ scripts: ['/lightview.js', '/lightview-x.js'],
98
+ type: 'module',
99
+ minHeight: 120
100
+ });
101
+ </script><code contenteditable="true">const { signal, element, $ } = Lightview;
102
+ const count = signal(0);
103
+ const app = { tag:'div', attributes: { class: 'container' }, children: [
104
+ { tag: 'h1', attributes: {}, children: ['Hello Lightview'] },
105
+ { tag: 'p', attributes: {}, children: [() => `Count: ${count.value}`] },
106
+ { tag: 'button', attributes: { onclick: () => count.value++ }, children: ['Click me'] }
107
+ ]};
108
+ $('#example').content(app);</code></pre>
109
+ </div>
110
+
111
+ <!-- Object DOM Syntax -->
112
+ <div id="syntax-object" style="display: none;">
113
+ <pre><script>
114
+ examplify(document.currentScript.nextElementSibling, {
115
+ at: document.currentScript.parentElement,
116
+ scripts: ['/lightview.js', '/lightview-x.js'],
117
+ type: 'module',
118
+ minHeight: 120
119
+ });
120
+ </script><code contenteditable="true">const { signal, tags, $ } = Lightview;
121
+ const { div } = tags;
122
+ const count = signal(0);
123
+ const app = { div: { class: 'container', children: [
124
+ { h1: { children: ['Hello Lightview'] } },
125
+ { p: { children: [() => `Count: ${count.value}`] } },
126
+ { button: { onclick: () => count.value++, children: ['Click me'] } }
127
+ ]}};
128
+ $('#example').content(app);</code></pre>
129
+ </div>
130
+
131
+ <h2>Attributes & Events</h2>
132
+ <p>
133
+ Pass attributes as the first argument (Tagged API) or in the attributes object (others):
134
+ </p>
135
+ <pre><code>// Standard attributes
136
+ div({
137
+ id: 'my-div',
138
+ class: 'container active',
139
+ style: 'color: red;',
140
+ 'data-value': '42'
141
+ })
142
+
143
+ // Reactive attributes - use functions!
144
+ div({
145
+ class: () => isActive.value ? 'active' : 'inactive',
146
+ style: () => `opacity: ${visible.value ? 1 : 0}`,
147
+ disabled: () => isLoading.value
148
+ })
149
+
150
+ // Event handlers - use "on" prefix
151
+ button({
152
+ onclick: (e) => handleClick(e),
153
+ onmouseenter: () => setHovered(true),
154
+ onmouseleave: () => setHovered(false)
155
+ })</code></pre>
156
+
157
+ <h2>Children</h2>
158
+ <p>
159
+ Children can be strings, numbers, elements, arrays, or functions:
160
+ </p>
161
+ <pre><code>div(
162
+ 'Static text', // String
163
+ 42, // Number (converted to string)
164
+ span('Nested element'), // Element
165
+ () => `Dynamic: ${value.value}`, // Reactive function
166
+ () => items.value.map(i => li(i.name)), // Reactive list
167
+ condition && span('Conditional') // Conditional (falsy = not rendered)
168
+ )</code></pre>
169
+
170
+ <h2>The domEl Property</h2>
171
+ <p>
172
+ Every Lightview element has a <code>domEl</code> property - the actual DOM node:
173
+ </p>
174
+ <pre><code>const myDiv = div({ class: 'box' }, 'Hello');
175
+
176
+ // Access the real DOM element
177
+ document.body.appendChild(myDiv.domEl);
178
+
179
+ // You can also manipulate it directly
180
+ myDiv.domEl.classList.add('another-class');</code></pre>