lightview 2.0.9 → 2.2.1
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/build-bundles.mjs +105 -0
- package/build.js +236 -46
- package/components/actions/button.js +16 -3
- package/components/actions/swap.js +26 -3
- package/components/daisyui.js +1 -1
- package/components/data-display/alert.js +13 -3
- package/components/data-display/avatar.js +25 -1
- package/components/data-display/badge.js +11 -3
- package/components/data-display/chart.js +22 -5
- package/components/data-display/countdown.js +3 -2
- package/components/data-display/kbd.js +9 -3
- package/components/data-display/loading.js +11 -3
- package/components/data-display/progress.js +11 -3
- package/components/data-display/radial-progress.js +12 -3
- package/components/data-display/tooltip.js +17 -0
- package/components/data-input/checkbox.js +23 -1
- package/components/data-input/input.js +24 -1
- package/components/data-input/radio.js +37 -2
- package/components/data-input/select.js +24 -1
- package/components/data-input/toggle.js +21 -1
- package/components/layout/divider.js +21 -1
- package/components/layout/indicator.js +14 -0
- package/components/navigation/breadcrumbs.js +42 -2
- package/components/navigation/tabs.js +291 -16
- package/docs/api/elements.html +125 -49
- package/docs/api/hypermedia.html +29 -2
- package/docs/api/index.html +6 -2
- package/docs/api/nav.html +18 -4
- package/docs/assets/js/examplify.js +1 -1
- package/docs/cdom-nav.html +55 -0
- package/docs/cdom.html +792 -0
- package/docs/components/alert.html +8 -8
- package/docs/components/avatar.html +24 -54
- package/docs/components/badge.html +69 -14
- package/docs/components/breadcrumbs.html +95 -29
- package/docs/components/button.html +78 -92
- package/docs/components/chart-area.html +3 -3
- package/docs/components/chart-bar.html +4 -181
- package/docs/components/chart-column.html +4 -189
- package/docs/components/chart-line.html +3 -3
- package/docs/components/chart-pie.html +112 -166
- package/docs/components/chart.html +11 -13
- package/docs/components/checkbox.html +48 -28
- package/docs/components/collapse.html +6 -6
- package/docs/components/component-nav.html +1 -1
- package/docs/components/countdown.html +12 -12
- package/docs/components/divider.html +65 -21
- package/docs/components/dropdown.html +1 -1
- package/docs/components/file-input.html +4 -4
- package/docs/components/footer.html +11 -11
- package/docs/components/indicator.html +85 -31
- package/docs/components/input.html +45 -29
- package/docs/components/join.html +4 -4
- package/docs/components/kbd.html +67 -28
- package/docs/components/loading.html +96 -92
- package/docs/components/pagination.html +4 -4
- package/docs/components/progress.html +50 -7
- package/docs/components/radial-progress.html +32 -12
- package/docs/components/radio.html +42 -31
- package/docs/components/select.html +48 -59
- package/docs/components/swap.html +183 -100
- package/docs/components/tabs.html +146 -278
- package/docs/components/toggle.html +44 -25
- package/docs/components/tooltip.html +71 -31
- package/docs/getting-started/index.html +8 -6
- package/docs/index.html +1 -1
- package/docs/syntax-nav.html +10 -0
- package/docs/syntax.html +8 -6
- package/index.html +2 -2
- package/jprx/LICENSE +21 -0
- package/jprx/README.md +130 -0
- package/jprx/helpers/array.js +75 -0
- package/jprx/helpers/compare.js +26 -0
- package/jprx/helpers/conditional.js +34 -0
- package/jprx/helpers/datetime.js +54 -0
- package/jprx/helpers/format.js +20 -0
- package/jprx/helpers/logic.js +24 -0
- package/jprx/helpers/lookup.js +25 -0
- package/jprx/helpers/math.js +34 -0
- package/jprx/helpers/network.js +41 -0
- package/jprx/helpers/state.js +80 -0
- package/jprx/helpers/stats.js +39 -0
- package/jprx/helpers/string.js +49 -0
- package/jprx/index.js +69 -0
- package/jprx/package.json +24 -0
- package/jprx/parser.js +1517 -0
- package/lightview-all.js +3785 -0
- package/lightview-cdom.js +2128 -0
- package/lightview-router.js +179 -208
- package/lightview-x.js +1435 -1608
- package/lightview.js +613 -766
- package/lightview.js.bak +1 -0
- package/package.json +10 -3
- package/src/lightview-all.js +10 -0
- package/src/lightview-cdom.js +457 -0
- package/src/lightview-router.js +210 -0
- package/src/lightview-x.js +1630 -0
- package/src/lightview.js +705 -0
- package/src/reactivity/signal.js +133 -0
- package/src/reactivity/state.js +217 -0
- package/{watch.js → start-dev.js} +2 -1
- package/tests/cdom/fixtures/helpers.cdomc +62 -0
- package/tests/cdom/fixtures/user.cdom +14 -0
- package/tests/cdom/fixtures/user.cdomc +12 -0
- package/tests/cdom/fixtures/user.odom +18 -0
- package/tests/cdom/fixtures/user.vdom +11 -0
- package/tests/cdom/helpers.test.js +121 -0
- package/tests/cdom/loader.test.js +125 -0
- package/tests/cdom/parser.test.js +179 -0
- package/tests/cdom/reactivity.test.js +186 -0
- package/tests/text-tag.test.js +77 -0
- package/vite.config.mjs +52 -0
- package/wrangler.toml +0 -3
- package/components/data-display/skeleton.js +0 -66
- package/docs/components/skeleton.html +0 -447
package/docs/cdom.html
ADDED
|
@@ -0,0 +1,792 @@
|
|
|
1
|
+
<script src="/lightview-router.js?base=/index.html"></script>
|
|
2
|
+
|
|
3
|
+
<style>
|
|
4
|
+
/* Syntax Tabs */
|
|
5
|
+
.syntax-tabs {
|
|
6
|
+
display: inline-flex;
|
|
7
|
+
gap: 0.25rem;
|
|
8
|
+
padding: 0.25rem;
|
|
9
|
+
background: #f1f5f9;
|
|
10
|
+
border-radius: 8px;
|
|
11
|
+
border: 1px solid #e2e8f0;
|
|
12
|
+
margin-bottom: 1rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
[data-theme="dark"] .syntax-tabs {
|
|
16
|
+
background: #1e293b;
|
|
17
|
+
border-color: #334155;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.syntax-tab {
|
|
21
|
+
padding: 0.5rem 1rem;
|
|
22
|
+
font-size: 0.875rem;
|
|
23
|
+
font-weight: 500;
|
|
24
|
+
border: none;
|
|
25
|
+
background: transparent;
|
|
26
|
+
border-radius: 6px;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
color: #64748b;
|
|
29
|
+
transition: all 0.2s;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.syntax-tab:hover {
|
|
33
|
+
background: #ffffff;
|
|
34
|
+
color: #1e293b;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[data-theme="dark"] .syntax-tab:hover {
|
|
38
|
+
background: #334155;
|
|
39
|
+
color: #f1f5f9;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.syntax-tab-active {
|
|
43
|
+
background: #ffffff !important;
|
|
44
|
+
color: #6366f1 !important;
|
|
45
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
[data-theme="dark"] .syntax-tab-active {
|
|
49
|
+
background: #0f172a !important;
|
|
50
|
+
color: #818cf8 !important;
|
|
51
|
+
}
|
|
52
|
+
</style>
|
|
53
|
+
|
|
54
|
+
<script>
|
|
55
|
+
globalThis.switchCDOMTab = (tabId) => {
|
|
56
|
+
const tabs = ['jprx', 'jprxc', 'operators'];
|
|
57
|
+
tabs.forEach(t => {
|
|
58
|
+
const btn = document.getElementById(`tab-btn-${t}`);
|
|
59
|
+
const pane = document.getElementById(`pane-${t}`);
|
|
60
|
+
if (t === tabId) {
|
|
61
|
+
btn.classList.add('syntax-tab-active');
|
|
62
|
+
pane.style.display = 'block';
|
|
63
|
+
} else {
|
|
64
|
+
btn.classList.remove('syntax-tab-active');
|
|
65
|
+
pane.style.display = 'none';
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<div class="docs-layout">
|
|
72
|
+
<aside class="docs-sidebar" src="/docs/cdom-nav.html"></aside>
|
|
73
|
+
|
|
74
|
+
<main class="docs-content">
|
|
75
|
+
<h1>cDOM (Computational DOM)</h1>
|
|
76
|
+
<p class="text-secondary" style="font-size: 1.125rem;">
|
|
77
|
+
A declarative, expression-based way to build reactive UIs.
|
|
78
|
+
</p>
|
|
79
|
+
|
|
80
|
+
<!-- ===== OVERVIEW ===== -->
|
|
81
|
+
<h2 id="overview">Overview</h2>
|
|
82
|
+
<p>
|
|
83
|
+
The <strong>Computational DOM (cDOM)</strong> is a way to describe user interfaces using reactive
|
|
84
|
+
expressions
|
|
85
|
+
that feel as natural as spreadsheet formulas. Instead of writing JavaScript logic to update your UI,
|
|
86
|
+
you define the <em>relationships</em> between your data and your elements.
|
|
87
|
+
</p>
|
|
88
|
+
<p>
|
|
89
|
+
cDOM was designed so that both developers and LLMs can generate safe user interfaces that humans
|
|
90
|
+
can interact with across platforms.
|
|
91
|
+
</p>
|
|
92
|
+
<p>
|
|
93
|
+
cDOM uses <strong>JPRX (JSON Pointer Reactive eXpressions)</strong> as its expression language. JPRX
|
|
94
|
+
extends <a href="https://www.rfc-editor.org/rfc/rfc6901" target="_blank">JSON Pointer (RFC 6901)</a>
|
|
95
|
+
with reactivity, relative paths, and helper functions.
|
|
96
|
+
</p>
|
|
97
|
+
<p>
|
|
98
|
+
If you wish to explore the implementation further, you can visit the <a
|
|
99
|
+
href="https://github.com/anywhichway/lightview" target="_blank">GitHub repository</a>, browse the <a
|
|
100
|
+
href="https://github.com/anywhichway/lightview/tree/main/cdom" target="_blank">cdom directory</a>, and
|
|
101
|
+
check out the <a href="https://github.com/anywhichway/lightview/tree/main/tests/cdom" target="_blank">cdom
|
|
102
|
+
unit tests</a>.
|
|
103
|
+
</p>
|
|
104
|
+
|
|
105
|
+
<!-- ===== SIMPLE EXAMPLE ===== -->
|
|
106
|
+
<h2 id="simple-example">Simple Example</h2>
|
|
107
|
+
<p>Here's a simple counter in cDOM. Notice how the UI is purely declarative — no JavaScript logic:</p>
|
|
108
|
+
<div class="code-block">
|
|
109
|
+
<pre><code>{
|
|
110
|
+
div: {
|
|
111
|
+
"cdom-state": { count: 0 },
|
|
112
|
+
children: [
|
|
113
|
+
{ h2: "Counter" },
|
|
114
|
+
{ p: ["Count: ", $/count] },
|
|
115
|
+
{ button: { onclick: $increment(/count), children: ["+"] } },
|
|
116
|
+
{ button: { onclick: $decrement(/count), children: ["-"] } }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
}</code></pre>
|
|
120
|
+
</div>
|
|
121
|
+
<p>
|
|
122
|
+
This defines a counter with:
|
|
123
|
+
</p>
|
|
124
|
+
<ul>
|
|
125
|
+
<li><code>cdom-state</code> — Local reactive state with <code>count: 0</code></li>
|
|
126
|
+
<li><code>$/count</code> — A path that reactively displays the count value</li>
|
|
127
|
+
<li><code>$increment(/count)</code> — Increment the count when clicked</li>
|
|
128
|
+
<li><code>$decrement(/count)</code> — Decrement the count when clicked</li>
|
|
129
|
+
</ul>
|
|
130
|
+
<p>
|
|
131
|
+
The UI automatically updates whenever <code>count</code> changes — no manual DOM manipulation required.
|
|
132
|
+
</p>
|
|
133
|
+
|
|
134
|
+
<!-- ===== ADVANTAGES ===== -->
|
|
135
|
+
<h2 id="advantages">Advantages</h2>
|
|
136
|
+
<div class="feature-grid" style="margin: 2rem 0;">
|
|
137
|
+
<div class="feature-card">
|
|
138
|
+
<h3 class="feature-title">🛡️ Enhanced Security</h3>
|
|
139
|
+
<p>
|
|
140
|
+
cDOM strictly avoids <code>eval()</code> and direct HTML injection. By using a custom
|
|
141
|
+
high-performance parser and a registry of pre-defined helper functions, it provides a safe sandbox
|
|
142
|
+
for dynamic content, making it highly resistant to XSS attacks.
|
|
143
|
+
</p>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="feature-card">
|
|
146
|
+
<h3 class="feature-title">🤖 LLM Friendly</h3>
|
|
147
|
+
<p>
|
|
148
|
+
Large Language Models excel at generating structured data and formulaic expressions. cDOM's
|
|
149
|
+
declarative nature and concise syntax make it far easier for AI to generate correct,
|
|
150
|
+
bug-free UI components compared to traditional JavaScript-heavy frameworks.
|
|
151
|
+
</p>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<!-- ===== JPRX INTRODUCTION ===== -->
|
|
156
|
+
<h2 id="JPRX">JPRX (JSON Pointer Reactive eXpressions)</h2>
|
|
157
|
+
<p>
|
|
158
|
+
<strong>JPRX</strong> is the expression language that powers cDOM. It extends
|
|
159
|
+
<a href="https://www.rfc-editor.org/rfc/rfc6901" target="_blank">JSON Pointer (RFC 6901)</a>
|
|
160
|
+
with reactivity, relative paths, and helper functions.
|
|
161
|
+
</p>
|
|
162
|
+
|
|
163
|
+
<h3 id="JPRX-delimiters">Delimiters</h3>
|
|
164
|
+
<p>
|
|
165
|
+
JPRX expressions begin with a <code>$</code> delimiter:
|
|
166
|
+
</p>
|
|
167
|
+
<table class="api-table">
|
|
168
|
+
<thead>
|
|
169
|
+
<tr>
|
|
170
|
+
<th>Delimiter</th>
|
|
171
|
+
<th>Purpose</th>
|
|
172
|
+
<th>Example</th>
|
|
173
|
+
</tr>
|
|
174
|
+
</thead>
|
|
175
|
+
<tbody>
|
|
176
|
+
<tr>
|
|
177
|
+
<td><code>$/</code></td>
|
|
178
|
+
<td>Access a path in the global registry</td>
|
|
179
|
+
<td><code>$/users/0/name</code></td>
|
|
180
|
+
</tr>
|
|
181
|
+
<tr>
|
|
182
|
+
<td><code>$function(</code></td>
|
|
183
|
+
<td>Call a helper function</td>
|
|
184
|
+
<td><code>$sum(/items...price)</code></td>
|
|
185
|
+
</tr>
|
|
186
|
+
</tbody>
|
|
187
|
+
</table>
|
|
188
|
+
<p>
|
|
189
|
+
Once inside a JPRX expression, paths follow <strong>JSON Pointer</strong> syntax. The <code>$</code>
|
|
190
|
+
is only needed at the start of the expression.
|
|
191
|
+
</p>
|
|
192
|
+
|
|
193
|
+
<h3 id="JPRX-anatomy">Anatomy of a Path</h3>
|
|
194
|
+
<p>
|
|
195
|
+
Inside a JPRX expression, paths follow JSON Pointer with these extensions:
|
|
196
|
+
</p>
|
|
197
|
+
<table class="api-table">
|
|
198
|
+
<thead>
|
|
199
|
+
<tr>
|
|
200
|
+
<th>Path</th>
|
|
201
|
+
<th>Description</th>
|
|
202
|
+
<th>Origin</th>
|
|
203
|
+
</tr>
|
|
204
|
+
</thead>
|
|
205
|
+
<tbody>
|
|
206
|
+
<tr>
|
|
207
|
+
<td><code>/users/0/name</code></td>
|
|
208
|
+
<td>Absolute path (property access)</td>
|
|
209
|
+
<td>JSON Pointer</td>
|
|
210
|
+
</tr>
|
|
211
|
+
<tr>
|
|
212
|
+
<td><code>/items[2]</code></td>
|
|
213
|
+
<td>Array index (bracket notation)</td>
|
|
214
|
+
<td>JPRX extension</td>
|
|
215
|
+
</tr>
|
|
216
|
+
<tr>
|
|
217
|
+
<td><code>./child</code></td>
|
|
218
|
+
<td>Relative to current context</td>
|
|
219
|
+
<td>JPRX extension</td>
|
|
220
|
+
</tr>
|
|
221
|
+
<tr>
|
|
222
|
+
<td><code>../sibling</code></td>
|
|
223
|
+
<td>Parent context access</td>
|
|
224
|
+
<td>JPRX extension</td>
|
|
225
|
+
</tr>
|
|
226
|
+
<tr>
|
|
227
|
+
<td><code>/items...price</code></td>
|
|
228
|
+
<td>Extract <code>price</code> from all items (explosion)</td>
|
|
229
|
+
<td>JPRX extension</td>
|
|
230
|
+
</tr>
|
|
231
|
+
</tbody>
|
|
232
|
+
</table>
|
|
233
|
+
|
|
234
|
+
<h4>Function Calls</h4>
|
|
235
|
+
<p>
|
|
236
|
+
Paths can contain function calls to transform data:
|
|
237
|
+
</p>
|
|
238
|
+
<div class="code-block">
|
|
239
|
+
<pre><code>$currency(sum(map(filter(/orders, eq(_/status, 'paid')), _/total)...))</code></pre>
|
|
240
|
+
</div>
|
|
241
|
+
<ul>
|
|
242
|
+
<li><strong>/orders</strong>: Access the <code>orders</code> collection (JSON Pointer)</li>
|
|
243
|
+
<li><strong>filter(..., eq(...))</strong>: Keep only "paid" orders</li>
|
|
244
|
+
<li><strong>map(..., _/total)</strong>: Extract the <code>total</code> from each order</li>
|
|
245
|
+
<li><strong>...</strong>: Explode the array into individual arguments</li>
|
|
246
|
+
<li><strong>sum(...)</strong>: Add up all the totals</li>
|
|
247
|
+
<li><strong>$currency(...)</strong>: Format as currency string</li>
|
|
248
|
+
</ul>
|
|
249
|
+
|
|
250
|
+
<h3 id="JPRX-placeholders">Placeholders</h3>
|
|
251
|
+
<table class="api-table">
|
|
252
|
+
<thead>
|
|
253
|
+
<tr>
|
|
254
|
+
<th>Placeholder</th>
|
|
255
|
+
<th>Description</th>
|
|
256
|
+
<th>Example</th>
|
|
257
|
+
</tr>
|
|
258
|
+
</thead>
|
|
259
|
+
<tbody>
|
|
260
|
+
<tr>
|
|
261
|
+
<td><code>_</code></td>
|
|
262
|
+
<td>Current item during iteration</td>
|
|
263
|
+
<td><code>$map(/items, _/name)</code></td>
|
|
264
|
+
</tr>
|
|
265
|
+
<tr>
|
|
266
|
+
<td><code>$event</code></td>
|
|
267
|
+
<td>Event object in handlers</td>
|
|
268
|
+
<td><code>$set($/selected, $event/target/value)</code></td>
|
|
269
|
+
</tr>
|
|
270
|
+
</tbody>
|
|
271
|
+
</table>
|
|
272
|
+
|
|
273
|
+
<!-- ===== COMPARISON TO EXCEL ===== -->
|
|
274
|
+
<h2 id="comparison">Comparison to Excel</h2>
|
|
275
|
+
<p>
|
|
276
|
+
Think of your UI as a spreadsheet. In Excel, if Cell C1 has the formula <code>=A1+B1</code>, C1
|
|
277
|
+
updates automatically whenever A1 or B1 changes.
|
|
278
|
+
</p>
|
|
279
|
+
<p>
|
|
280
|
+
cDOM brings this exact paradigm to the web. Every attribute and text node can be a "cell" that
|
|
281
|
+
computes its value based on other "cells" (reactive signals).
|
|
282
|
+
</p>
|
|
283
|
+
|
|
284
|
+
<table class="api-table">
|
|
285
|
+
<thead>
|
|
286
|
+
<tr>
|
|
287
|
+
<th>Feature</th>
|
|
288
|
+
<th>Excel</th>
|
|
289
|
+
<th>cDOM / JPRX</th>
|
|
290
|
+
</tr>
|
|
291
|
+
</thead>
|
|
292
|
+
<tbody>
|
|
293
|
+
<tr>
|
|
294
|
+
<td><strong>Reactive Unit</strong></td>
|
|
295
|
+
<td>Cell</td>
|
|
296
|
+
<td>Signal / State Proxy</td>
|
|
297
|
+
</tr>
|
|
298
|
+
<tr>
|
|
299
|
+
<td><strong>Formulas</strong></td>
|
|
300
|
+
<td><code>=SUM(A1:A10)</code></td>
|
|
301
|
+
<td><code>$sum(/items...price)</code></td>
|
|
302
|
+
</tr>
|
|
303
|
+
<tr>
|
|
304
|
+
<td><strong>Path Resolution</strong></td>
|
|
305
|
+
<td>Cell References (A1, $B$2)</td>
|
|
306
|
+
<td>JSON Pointer paths (./name, $/global)</td>
|
|
307
|
+
</tr>
|
|
308
|
+
<tr>
|
|
309
|
+
<td><strong>Recalculation</strong></td>
|
|
310
|
+
<td>Automatic on change</td>
|
|
311
|
+
<td>Automatic on change</td>
|
|
312
|
+
</tr>
|
|
313
|
+
</tbody>
|
|
314
|
+
</table>
|
|
315
|
+
|
|
316
|
+
<!-- ===== LIGHTVIEW INTEGRATION ===== -->
|
|
317
|
+
<h2 id="integration">Lightview Integration</h2>
|
|
318
|
+
<p>
|
|
319
|
+
cDOM integrates seamlessly with Lightview's existing DOM formats. You can use JPRX expressions
|
|
320
|
+
in any of Lightview's hypermedia formats:
|
|
321
|
+
</p>
|
|
322
|
+
|
|
323
|
+
<h3>vDOM (Virtual DOM)</h3>
|
|
324
|
+
<p>JPRX expressions work directly in vDOM arrays:</p>
|
|
325
|
+
<div class="code-block">
|
|
326
|
+
<pre><code>["div", { class: "counter" },
|
|
327
|
+
["p", "Count: ", "$/count"],
|
|
328
|
+
["button", { onclick: "$increment(/count)" }, "+"]
|
|
329
|
+
]</code></pre>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<h3>oDOM (Object DOM)</h3>
|
|
333
|
+
<p>JPRX expressions work in oDOM objects:</p>
|
|
334
|
+
<div class="code-block">
|
|
335
|
+
<pre><code>{
|
|
336
|
+
tag: "div",
|
|
337
|
+
attributes: { class: "counter" },
|
|
338
|
+
children: [
|
|
339
|
+
{ tag: "p", children: ["Count: ", "$/count"] },
|
|
340
|
+
{ tag: "button", attributes: { onclick: "$increment(/count)" }, children: ["+"] }
|
|
341
|
+
]
|
|
342
|
+
}</code></pre>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<h3>cDOM Shorthand</h3>
|
|
346
|
+
<p>cDOM's concise shorthand syntax:</p>
|
|
347
|
+
<div class="code-block">
|
|
348
|
+
<pre><code>{
|
|
349
|
+
div: {
|
|
350
|
+
class: "counter",
|
|
351
|
+
children: [
|
|
352
|
+
{ p: ["Count: ", $/count] },
|
|
353
|
+
{ button: { onclick: "$increment(/count)", children: ["+"] } }
|
|
354
|
+
]
|
|
355
|
+
}
|
|
356
|
+
}</code></pre>
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
<!-- ===== DIRECTIVES ===== -->
|
|
360
|
+
<h2 id="directives">Directives</h2>
|
|
361
|
+
<p>
|
|
362
|
+
cDOM provides custom attributes (directives) to bridge static HTML and reactive state.
|
|
363
|
+
</p>
|
|
364
|
+
|
|
365
|
+
<h3 id="cdom-state">cdom-state</h3>
|
|
366
|
+
<p>
|
|
367
|
+
Defines local reactive state on an element. Children can access this state using relative paths.
|
|
368
|
+
</p>
|
|
369
|
+
<div class="code-block">
|
|
370
|
+
<pre><code>{
|
|
371
|
+
div: {
|
|
372
|
+
"cdom-state": { count: 0, name: "Guest" },
|
|
373
|
+
children: [
|
|
374
|
+
{ p: ["Hello, ", $/name] },
|
|
375
|
+
{ p: ["Count: ", $/count] }
|
|
376
|
+
]
|
|
377
|
+
}
|
|
378
|
+
}</code></pre>
|
|
379
|
+
</div>
|
|
380
|
+
|
|
381
|
+
<h3 id="cdom-bind">cdom-bind</h3>
|
|
382
|
+
<p>
|
|
383
|
+
Creates two-way data binding between an input and a reactive path.
|
|
384
|
+
</p>
|
|
385
|
+
<div class="code-block">
|
|
386
|
+
<pre><code>{ input: { type: "text", "cdom-bind": "$/user/name", placeholder: "Enter name" } }
|
|
387
|
+
{ input: { type: "checkbox", "cdom-bind": "$/settings/enabled" } }</code></pre>
|
|
388
|
+
</div>
|
|
389
|
+
<p>
|
|
390
|
+
<strong>Smart Initialization:</strong> If the state is <code>undefined</code>,
|
|
391
|
+
<code>cdom-bind</code> will use the input's initial value to populate the state.
|
|
392
|
+
</p>
|
|
393
|
+
|
|
394
|
+
<!-- ===== SHOPPING CART EXAMPLE ===== -->
|
|
395
|
+
<h2 id="shopping-cart">Shopping Cart Example</h2>
|
|
396
|
+
<p>A more complete example showing reactive expressions with data transformations:</p>
|
|
397
|
+
|
|
398
|
+
<div id="shopping-cart-example">
|
|
399
|
+
<pre><script>
|
|
400
|
+
examplify(document.currentScript.nextElementSibling, {
|
|
401
|
+
at: document.currentScript.parentElement,
|
|
402
|
+
scripts: ['/lightview.js', '/lightview-x.js', '/lightview-cdom.js'],
|
|
403
|
+
type: 'module',
|
|
404
|
+
height: '350px',
|
|
405
|
+
autoRun: true
|
|
406
|
+
});
|
|
407
|
+
</script><code contenteditable="true">// Shopping Cart: Demonstrating $map and $currency helpers
|
|
408
|
+
await import('/lightview-cdom.js');
|
|
409
|
+
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
410
|
+
const { $ } = Lightview;
|
|
411
|
+
|
|
412
|
+
const cdomString = `{
|
|
413
|
+
div: {
|
|
414
|
+
"cdom-state": {
|
|
415
|
+
cart: {
|
|
416
|
+
items: [
|
|
417
|
+
{ name: "Apple", price: 1.00 },
|
|
418
|
+
{ name: "Orange", price: 2.00 }
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
children: [
|
|
423
|
+
{ h3: "Shopping Cart" },
|
|
424
|
+
{ ul: {
|
|
425
|
+
children: $map(/cart/items, { li: { children: [_/name, " - ", $currency(_/price)] } })
|
|
426
|
+
}},
|
|
427
|
+
{ p: {
|
|
428
|
+
style: "font-weight: bold; margin-top: 1rem;",
|
|
429
|
+
children: ["Total: ", $currency(sum(/cart/items...price))]
|
|
430
|
+
}}
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
}`;
|
|
434
|
+
|
|
435
|
+
const hydrated = hydrate(parseJPRX(cdomString));
|
|
436
|
+
$('#example').content(hydrated);
|
|
437
|
+
globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
438
|
+
</code></pre>
|
|
439
|
+
</div>
|
|
440
|
+
|
|
441
|
+
<!-- ===== INTERACTIVE EXAMPLE ===== -->
|
|
442
|
+
<h2 id="interactive-example">Interactive Example</h2>
|
|
443
|
+
<p>
|
|
444
|
+
Choose a syntax to see how the same reactive counter can be defined using standard JSON, concise JPRXC, or
|
|
445
|
+
operator syntax.
|
|
446
|
+
</p>
|
|
447
|
+
|
|
448
|
+
<div role="tablist" class="syntax-tabs">
|
|
449
|
+
<button id="tab-btn-jprx" class="syntax-tab syntax-tab-active" onclick="switchCDOMTab('jprx')">JPRX
|
|
450
|
+
(Standard)</button>
|
|
451
|
+
<button id="tab-btn-jprxc" class="syntax-tab" onclick="switchCDOMTab('jprxc')">JPRXC (Concise)</button>
|
|
452
|
+
<button id="tab-btn-operators" class="syntax-tab" onclick="switchCDOMTab('operators')">Operators</button>
|
|
453
|
+
</div>
|
|
454
|
+
|
|
455
|
+
<!-- JPRX Pane -->
|
|
456
|
+
<div id="pane-jprx">
|
|
457
|
+
<pre><script>
|
|
458
|
+
examplify(document.currentScript.nextElementSibling, {
|
|
459
|
+
at: document.currentScript.parentElement,
|
|
460
|
+
scripts: ['/lightview.js', '/lightview-x.js', '/lightview-cdom.js'],
|
|
461
|
+
type: 'module',
|
|
462
|
+
height: '250px',
|
|
463
|
+
autoRun: true
|
|
464
|
+
});
|
|
465
|
+
</script><code contenteditable="true">// JPRX: Standard JSON format (strict)
|
|
466
|
+
await import('/lightview-cdom.js');
|
|
467
|
+
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
468
|
+
const { signal, $ } = Lightview;
|
|
469
|
+
|
|
470
|
+
const count = signal(0, 'count');
|
|
471
|
+
|
|
472
|
+
const cdomString = `{
|
|
473
|
+
"div": {
|
|
474
|
+
"children": [
|
|
475
|
+
{ "h3": ["Standard JPRX Counter"] },
|
|
476
|
+
{ "p": { "children": ["Count: ", "$/count"] }},
|
|
477
|
+
{ "div": { "children": [
|
|
478
|
+
{ "button": { "onclick": "$decrement(/count)", "children": ["-"] } },
|
|
479
|
+
{ "button": { "onclick": "$increment(/count)", "children": ["+"] } }
|
|
480
|
+
]}}
|
|
481
|
+
]
|
|
482
|
+
}
|
|
483
|
+
}`;
|
|
484
|
+
|
|
485
|
+
const hydrated = hydrate(parseJPRX(cdomString));
|
|
486
|
+
$('#example').content(hydrated);
|
|
487
|
+
globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
488
|
+
</code></pre>
|
|
489
|
+
</div>
|
|
490
|
+
|
|
491
|
+
<!-- JPRXC Pane -->
|
|
492
|
+
<div id="pane-jprxc" style="display: none;">
|
|
493
|
+
<pre><script>
|
|
494
|
+
examplify(document.currentScript.nextElementSibling, {
|
|
495
|
+
at: document.currentScript.parentElement,
|
|
496
|
+
scripts: ['/lightview.js', '/lightview-x.js', '/lightview-cdom.js'],
|
|
497
|
+
type: 'module',
|
|
498
|
+
height: '250px'
|
|
499
|
+
});
|
|
500
|
+
</script><code contenteditable="true">// JPRXC: Concise format (unquoted keys, comments)
|
|
501
|
+
await import('/lightview-cdom.js');
|
|
502
|
+
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
503
|
+
const { signal, $ } = Lightview;
|
|
504
|
+
|
|
505
|
+
const count = signal(0, 'count');
|
|
506
|
+
|
|
507
|
+
const cdomString = `{
|
|
508
|
+
div: {
|
|
509
|
+
children: [
|
|
510
|
+
{ h3: ["Concise Counter"] },
|
|
511
|
+
{ p: { children: ["Count: ", $/count] }},
|
|
512
|
+
{ div: { children: [
|
|
513
|
+
{ button: { onclick: $decrement(/count), children: ["-"] } },
|
|
514
|
+
{ button: { onclick: $increment(/count), children: ["+"] } }
|
|
515
|
+
]}}
|
|
516
|
+
]
|
|
517
|
+
}
|
|
518
|
+
}`;
|
|
519
|
+
|
|
520
|
+
const hydrated = hydrate(parseJPRX(cdomString));
|
|
521
|
+
$('#example').content(hydrated);
|
|
522
|
+
globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
523
|
+
</code></pre>
|
|
524
|
+
</div>
|
|
525
|
+
|
|
526
|
+
<!-- Operators Pane -->
|
|
527
|
+
<div id="pane-operators" style="display: none;">
|
|
528
|
+
<pre><script>
|
|
529
|
+
examplify(document.currentScript.nextElementSibling, {
|
|
530
|
+
at: document.currentScript.parentElement,
|
|
531
|
+
scripts: ['/lightview.js', '/lightview-x.js', '/lightview-cdom.js'],
|
|
532
|
+
type: 'module',
|
|
533
|
+
height: '250px'
|
|
534
|
+
});
|
|
535
|
+
</script><code contenteditable="true">// Operators: Using $++/path and $--/path
|
|
536
|
+
await import('/lightview-cdom.js');
|
|
537
|
+
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
538
|
+
const { signal, $ } = Lightview;
|
|
539
|
+
|
|
540
|
+
const count = signal(0, 'count');
|
|
541
|
+
|
|
542
|
+
const cdomString = `{
|
|
543
|
+
div: {
|
|
544
|
+
children: [
|
|
545
|
+
{ h3: ["Operator Counter"] },
|
|
546
|
+
{ p: { children: ["Count: ", $/count] }},
|
|
547
|
+
{ div: { children: [
|
|
548
|
+
// Prefix operators: $-- and $++
|
|
549
|
+
{ button: { onclick: $--/count, children: ["-"] } },
|
|
550
|
+
{ button: { onclick: $++/count, children: ["+"] } }
|
|
551
|
+
]}}
|
|
552
|
+
]
|
|
553
|
+
}
|
|
554
|
+
}`;
|
|
555
|
+
|
|
556
|
+
const hydrated = hydrate(parseJPRX(cdomString));
|
|
557
|
+
$('#example').content(hydrated);
|
|
558
|
+
globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
559
|
+
</code></pre>
|
|
560
|
+
</div>
|
|
561
|
+
|
|
562
|
+
<!-- ===== OPERATOR SYNTAX EXAMPLE ===== -->
|
|
563
|
+
<h2 id="operator-syntax">Operator Syntax</h2>
|
|
564
|
+
<p>
|
|
565
|
+
JPRX now supports <strong>prefix</strong> and <strong>postfix</strong> operator syntax as alternatives to
|
|
566
|
+
function calls.
|
|
567
|
+
This makes expressions more concise and familiar to JavaScript developers.
|
|
568
|
+
</p>
|
|
569
|
+
|
|
570
|
+
<h3>Equivalent Expressions</h3>
|
|
571
|
+
<table class="api-table">
|
|
572
|
+
<thead>
|
|
573
|
+
<tr>
|
|
574
|
+
<th>Function Syntax</th>
|
|
575
|
+
<th>Operator Syntax</th>
|
|
576
|
+
<th>Description</th>
|
|
577
|
+
</tr>
|
|
578
|
+
</thead>
|
|
579
|
+
<tbody>
|
|
580
|
+
<tr>
|
|
581
|
+
<td><code>$increment(/count)</code></td>
|
|
582
|
+
<td><code>$++/count</code> or <code>$/count++</code></td>
|
|
583
|
+
<td>Increment by 1</td>
|
|
584
|
+
</tr>
|
|
585
|
+
<tr>
|
|
586
|
+
<td><code>$decrement(/count)</code></td>
|
|
587
|
+
<td><code>$--/count</code> or <code>$/count--</code></td>
|
|
588
|
+
<td>Decrement by 1</td>
|
|
589
|
+
</tr>
|
|
590
|
+
<tr>
|
|
591
|
+
<td><code>$toggle(/enabled)</code></td>
|
|
592
|
+
<td><code>$!!/enabled</code></td>
|
|
593
|
+
<td>Toggle boolean (prefix only)</td>
|
|
594
|
+
</tr>
|
|
595
|
+
</tbody>
|
|
596
|
+
</table>
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
<!-- ===== EVENTS ===== -->
|
|
601
|
+
<h2 id="events">Events and Interaction</h2>
|
|
602
|
+
<p>
|
|
603
|
+
cDOM handles user interactions gracefully, whether in standalone applications or LLM-driven environments.
|
|
604
|
+
</p>
|
|
605
|
+
|
|
606
|
+
<h3 id="events-manual">Manual Implementation</h3>
|
|
607
|
+
<p>
|
|
608
|
+
Use standard event attributes with JPRX expressions:
|
|
609
|
+
</p>
|
|
610
|
+
<div class="code-block">
|
|
611
|
+
<pre><code>{ button: { onclick: $(++, $/count), children: ["Click Me"] } }
|
|
612
|
+
{ input: { oninput: $set($/name, $event/target/value) } }</code></pre>
|
|
613
|
+
</div>
|
|
614
|
+
|
|
615
|
+
<h3 id="events-llm">LLM-Generated Interaction</h3>
|
|
616
|
+
<p>
|
|
617
|
+
LLMs can generate event handlers to register interest in user actions:
|
|
618
|
+
</p>
|
|
619
|
+
<div class="code-block">
|
|
620
|
+
<pre><code>{
|
|
621
|
+
button: {
|
|
622
|
+
onclick: $fetch('/api/notify', { method: 'POST', body: $event }),
|
|
623
|
+
children: ["Notify LLM"]
|
|
624
|
+
}
|
|
625
|
+
}</code></pre>
|
|
626
|
+
</div>
|
|
627
|
+
<p>
|
|
628
|
+
The <code>$event</code> placeholder gives the LLM full context of the interaction.
|
|
629
|
+
</p>
|
|
630
|
+
|
|
631
|
+
<h3 id="events-lifecycle">The Interaction Lifecycle</h3>
|
|
632
|
+
<div class="feature-grid" style="margin: 2rem 0;">
|
|
633
|
+
<div class="feature-card">
|
|
634
|
+
<h4 class="feature-title">🤖 LLM-Driven Flow</h4>
|
|
635
|
+
<ol style="margin-top: 1rem; font-size: 0.9em; line-height: 1.6;">
|
|
636
|
+
<li><strong>LLM</strong> generates UI structure as JSON</li>
|
|
637
|
+
<li><strong>Server</strong> serves the JSON to the client</li>
|
|
638
|
+
<li><strong>Client</strong> renders cDOM and activates reactivity</li>
|
|
639
|
+
<li><strong>User</strong> interacts (e.g., clicks a button)</li>
|
|
640
|
+
<li><strong>Client</strong> sends interaction data to server</li>
|
|
641
|
+
<li><strong>Server</strong> relays event to LLM</li>
|
|
642
|
+
<li><strong>LLM</strong> sends UI patch back</li>
|
|
643
|
+
<li><strong>Client</strong> merges update, UI refreshes</li>
|
|
644
|
+
</ol>
|
|
645
|
+
</div>
|
|
646
|
+
<div class="feature-card">
|
|
647
|
+
<h4 class="feature-title">🏠 Standalone Flow</h4>
|
|
648
|
+
<ol style="margin-top: 1rem; font-size: 0.9em; line-height: 1.6;">
|
|
649
|
+
<li><strong>cDOM</strong> defined in source code</li>
|
|
650
|
+
<li><strong>Client</strong> activates UI on page load</li>
|
|
651
|
+
<li><strong>User</strong> interacts (e.g., toggles a switch)</li>
|
|
652
|
+
<li><strong>State</strong> updates immediately</li>
|
|
653
|
+
<li><strong>UI</strong> recalculates reactively (0 latency)</li>
|
|
654
|
+
<li><strong>Server</strong> only for persistence/APIs</li>
|
|
655
|
+
</ol>
|
|
656
|
+
</div>
|
|
657
|
+
</div>
|
|
658
|
+
|
|
659
|
+
<!-- ===== JAVASCRIPT API ===== -->
|
|
660
|
+
<h2 id="js-api">JavaScript API</h2>
|
|
661
|
+
<p>Interact with cDOM programmatically using the <code>LightviewCDOM</code> global object.</p>
|
|
662
|
+
|
|
663
|
+
<h3 id="api-activate">activate(root)</h3>
|
|
664
|
+
<p>
|
|
665
|
+
Scans the DOM from <code>root</code> (defaults to <code>document.body</code>) and
|
|
666
|
+
initializes all cDOM directives.
|
|
667
|
+
</p>
|
|
668
|
+
<div class="code-block">
|
|
669
|
+
<pre><code>LightviewCDOM.activate();
|
|
670
|
+
LightviewCDOM.activate(document.getElementById('myApp'));</code></pre>
|
|
671
|
+
</div>
|
|
672
|
+
|
|
673
|
+
<h3 id="api-hydrate">hydrate(object)</h3>
|
|
674
|
+
<p>
|
|
675
|
+
Converts <code>$</code>-prefixed strings into reactive computed signals.
|
|
676
|
+
</p>
|
|
677
|
+
<div class="code-block">
|
|
678
|
+
<pre><code>const config = { title: "Dashboard", total: "$/cart/items...price" };
|
|
679
|
+
const liveConfig = LightviewCDOM.hydrate(config);</code></pre>
|
|
680
|
+
</div>
|
|
681
|
+
|
|
682
|
+
<h3 id="api-parseJPRX">parseJPRX(string)</h3>
|
|
683
|
+
<p>
|
|
684
|
+
Parses cDOM's concise syntax (unquoted keys, JPRX expressions) into a JavaScript object.
|
|
685
|
+
</p>
|
|
686
|
+
<div class="code-block">
|
|
687
|
+
<pre><code>const obj = LightviewCDOM.parseJPRX(`{ div: { children: [$/name] } }`);</code></pre>
|
|
688
|
+
</div>
|
|
689
|
+
|
|
690
|
+
<h3 id="api-registerHelper">registerHelper(name, fn, options?)</h3>
|
|
691
|
+
<p>
|
|
692
|
+
Registers a custom helper function for use in JPRX expressions.
|
|
693
|
+
</p>
|
|
694
|
+
<div class="code-block">
|
|
695
|
+
<pre><code>LightviewCDOM.registerHelper('double', (x) => x * 2);
|
|
696
|
+
// Now usable: $double($/count)</code></pre>
|
|
697
|
+
</div>
|
|
698
|
+
|
|
699
|
+
<!-- ===== JPRX HELPERS ===== -->
|
|
700
|
+
<h2 id="helpers">JPRX Helper Functions</h2>
|
|
701
|
+
<p>
|
|
702
|
+
cDOM includes a rich set of built-in helpers for common transformations.
|
|
703
|
+
For security, only registered helpers are available — no access to <code>globalThis</code>.
|
|
704
|
+
</p>
|
|
705
|
+
|
|
706
|
+
<h3 id="helpers-math">Math</h3>
|
|
707
|
+
<p>Basic arithmetic operations.</p>
|
|
708
|
+
<div class="code-block">
|
|
709
|
+
<pre><code>+, add, -, sub, *, mul, /, div, round, ceil, floor, abs, mod, pow, sqrt</code></pre>
|
|
710
|
+
</div>
|
|
711
|
+
|
|
712
|
+
<h3 id="helpers-stats">Stats</h3>
|
|
713
|
+
<p>Aggregate calculations.</p>
|
|
714
|
+
<div class="code-block">
|
|
715
|
+
<pre><code>sum, avg, min, max, median, stdev, var</code></pre>
|
|
716
|
+
</div>
|
|
717
|
+
|
|
718
|
+
<h3 id="helpers-string">String</h3>
|
|
719
|
+
<p>Text manipulation.</p>
|
|
720
|
+
<div class="code-block">
|
|
721
|
+
<pre><code>upper, lower, trim, capitalize, titleCase, contains, startsWith, endsWith, replace, split, len, join, concat, default</code></pre>
|
|
722
|
+
</div>
|
|
723
|
+
|
|
724
|
+
<h3 id="helpers-array">Array</h3>
|
|
725
|
+
<p>Collection processing.</p>
|
|
726
|
+
<div class="code-block">
|
|
727
|
+
<pre><code>count, map, filter, find, unique, sort, reverse, first, last, slice, flatten, join, len, length</code></pre>
|
|
728
|
+
</div>
|
|
729
|
+
|
|
730
|
+
<h3 id="helpers-logic">Logic & Comparison</h3>
|
|
731
|
+
<p>Boolean logic and comparisons.</p>
|
|
732
|
+
|
|
733
|
+
<h4 class="text-xs font-bold opacity-60 uppercase mb-2">Named Operators</h4>
|
|
734
|
+
<div class="code-block mb-4">
|
|
735
|
+
<pre><code>if, and, or, not, eq, neq, gt, lt, gte, lte, between, in</code></pre>
|
|
736
|
+
</div>
|
|
737
|
+
|
|
738
|
+
<h4 class="text-xs font-bold opacity-60 uppercase mb-2">Aliases</h4>
|
|
739
|
+
<div class="code-block mb-4">
|
|
740
|
+
<pre><code>&&, ||, !, ==, ===, !=, >, <, >=, <=</code></pre>
|
|
741
|
+
</div>
|
|
742
|
+
|
|
743
|
+
<p class="text-sm italic opacity-80" style="margin-top: 1rem;">
|
|
744
|
+
Example: <code>$if(gt($/count, 10), 'Large', 'Small')</code>
|
|
745
|
+
</p>
|
|
746
|
+
|
|
747
|
+
<h3 id="helpers-conditional">Conditional Aggregates</h3>
|
|
748
|
+
<p>Statistical functions with predicates.</p>
|
|
749
|
+
<div class="code-block">
|
|
750
|
+
<pre><code>sumIf, countIf, avgIf</code></pre>
|
|
751
|
+
</div>
|
|
752
|
+
|
|
753
|
+
<h3 id="helpers-formatting">Formatting</h3>
|
|
754
|
+
<p>Display formatting.</p>
|
|
755
|
+
<div class="code-block">
|
|
756
|
+
<pre><code>number, currency, percent, thousands</code></pre>
|
|
757
|
+
</div>
|
|
758
|
+
<p class="text-xs italic opacity-70" style="margin-top: 0.5rem;">
|
|
759
|
+
<strong>Explosion Operator:</strong> In JPRX, <code>...</code> is placed at the end of a path
|
|
760
|
+
(e.g., <code>$/items...price</code>) to expand arrays into arguments.
|
|
761
|
+
</p>
|
|
762
|
+
|
|
763
|
+
<h3 id="helpers-datetime">DateTime</h3>
|
|
764
|
+
<p>Date operations.</p>
|
|
765
|
+
<div class="code-block">
|
|
766
|
+
<pre><code>now, today, date, formatDate, year, month, day, weekday, addDays, dateDiff</code></pre>
|
|
767
|
+
</div>
|
|
768
|
+
|
|
769
|
+
<h3 id="helpers-lookup">Lookup</h3>
|
|
770
|
+
<p>Data retrieval from indexed structures.</p>
|
|
771
|
+
<div class="code-block">
|
|
772
|
+
<pre><code>lookup, vlookup, index, match</code></pre>
|
|
773
|
+
</div>
|
|
774
|
+
|
|
775
|
+
<h3 id="helpers-mutation">State Mutation</h3>
|
|
776
|
+
<p>Modify reactive state from event handlers.</p>
|
|
777
|
+
<div class="code-block">
|
|
778
|
+
<pre><code>set, increment (++), decrement (--), toggle (!!), push, pop, assign, clear</code></pre>
|
|
779
|
+
</div>
|
|
780
|
+
|
|
781
|
+
<h3 id="helpers-network">Network</h3>
|
|
782
|
+
<p>HTTP requests.</p>
|
|
783
|
+
<div class="code-block">
|
|
784
|
+
<pre><code>fetch(url, options?)</code></pre>
|
|
785
|
+
</div>
|
|
786
|
+
<p>
|
|
787
|
+
The <code>fetch</code> helper auto-serializes object bodies to JSON and sets
|
|
788
|
+
<code>Content-Type: application/json</code>.
|
|
789
|
+
</p>
|
|
790
|
+
|
|
791
|
+
</main>
|
|
792
|
+
</div>
|