lightview 2.4.4 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI-GUIDANCE.md +28 -11
- package/README.md +4 -4
- package/docs/assets/styles/site.css +16 -7
- package/docs/benchmarks/bau-tagged-fragment.js +41 -0
- package/docs/cdom-xpath.html +27 -9
- package/docs/cdom.html +186 -125
- package/docs/dom-benchmark.html +103 -4
- package/docs/getting-started/index.html +40 -0
- package/docs/index.html +40 -31
- package/jprx/README.md +15 -13
- package/jprx/package.json +1 -1
- package/jprx/parser.js +85 -1
- package/lightview-all.js +131 -34
- package/lightview-cdom.js +131 -34
- package/package.json +3 -3
- package/scratch.html +83 -0
- package/src/lightview-cdom.js +86 -44
package/docs/cdom.html
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
|
|
54
54
|
<script>
|
|
55
55
|
globalThis.switchCDOMTab = (tabId) => {
|
|
56
|
-
const tabs = ['jprx', '
|
|
56
|
+
const tabs = ['jprx', 'cdomc', 'operators'];
|
|
57
57
|
tabs.forEach(t => {
|
|
58
58
|
const btn = document.getElementById(`tab-btn-${t}`);
|
|
59
59
|
const pane = document.getElementById(`pane-${t}`);
|
|
@@ -89,6 +89,20 @@
|
|
|
89
89
|
helper functions, and integration patterns are subject to change as we continue to evolve the
|
|
90
90
|
library. Use with caution in production environments.
|
|
91
91
|
</p>
|
|
92
|
+
<p
|
|
93
|
+
style="margin: 0.75rem 0 0 0; padding-top: 0.75rem; border-top: 1px solid var(--site-border); font-size: 0.95rem; opacity: 0.9;">
|
|
94
|
+
<strong>v2.5.0 Syntax Update:</strong> Starting with Lightview v2.5.0 and JPRX v1.4.0,
|
|
95
|
+
expressions use a new wrapper syntax:
|
|
96
|
+
<code
|
|
97
|
+
style="background: var(--site-bg-secondary); padding: 0.125rem 0.375rem; border-radius: 3px;">=(expr)</code>
|
|
98
|
+
for JPRX and
|
|
99
|
+
<code
|
|
100
|
+
style="background: var(--site-bg-secondary); padding: 0.125rem 0.375rem; border-radius: 3px;">#(xpath)</code>
|
|
101
|
+
for XPath.
|
|
102
|
+
The legacy prefix-only syntax (e.g., <code
|
|
103
|
+
style="background: var(--site-bg-secondary); padding: 0.125rem 0.375rem; border-radius: 3px;">=path</code>)
|
|
104
|
+
will be <strong>fully deprecated after March 31, 2026</strong>.
|
|
105
|
+
</p>
|
|
92
106
|
</div>
|
|
93
107
|
</div>
|
|
94
108
|
</div>
|
|
@@ -132,13 +146,14 @@ const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
|
132
146
|
const { $ } = Lightview;
|
|
133
147
|
|
|
134
148
|
const cdom = `{
|
|
135
|
-
div: {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
{
|
|
140
|
-
{
|
|
141
|
-
{ button: { onclick:
|
|
149
|
+
"div": {
|
|
150
|
+
"id": "Counter",
|
|
151
|
+
"onmount": "=state({ count: 0 }, { name: 'local', schema: 'auto', scope: $this })",
|
|
152
|
+
"children": [
|
|
153
|
+
{ "h2": "#(../../@id)" }, // XPath: text node -> h2 -> div
|
|
154
|
+
{ "p": ["Count: ", "=(/local/count)"] },
|
|
155
|
+
{ "button": { "onclick": "=(++/local/count)", "children": ["+"] } },
|
|
156
|
+
{ "button": { "onclick": "=(--/local/count)", "children": ["-"] } }
|
|
142
157
|
]
|
|
143
158
|
}
|
|
144
159
|
}`;
|
|
@@ -150,14 +165,45 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
|
|
|
150
165
|
</p>
|
|
151
166
|
<ul>
|
|
152
167
|
<li><code>onmount</code> — A Lightview lifecycle hook where we initialize state.</li>
|
|
168
|
+
<li><code>#../../@id</code> — An XPath expression that extracts the <code>id</code> from the grandparent
|
|
169
|
+
element.</li>
|
|
153
170
|
<li><code>=state(...)</code> — An initializer that creates local reactive state.</li>
|
|
154
171
|
<li><code>scope: $this</code> — Explicitly attaches the state to the current element.</li>
|
|
155
172
|
<li><code>=/local/count</code> — A path that reactively displays the count value.</li>
|
|
156
173
|
</ul>
|
|
174
|
+
<p style="font-size: 0.95rem; font-style: italic; color: var(--site-text-secondary);">
|
|
175
|
+
<strong>Note:</strong> In cDOM, expressions use a wrapper syntax for clarity: <code>#(xpath)</code> for
|
|
176
|
+
<strong>XPath</strong> expressions that
|
|
177
|
+
navigate and extract data from the DOM, and <code>=(expr)</code> for <strong>JPRX</strong>
|
|
178
|
+
expressions that navigate or apply functions to reactive state. Legacy syntax without parentheses is still
|
|
179
|
+
supported.
|
|
180
|
+
</p>
|
|
157
181
|
<p>
|
|
158
|
-
The UI automatically updates whenever <code>count</code> changes — no manual DOM manipulation required.
|
|
182
|
+
The UI automatically updates whenever <code>count</code> changes — no manual DOM manipulation required. It
|
|
183
|
+
also uses XPath to navigate the DOM structure during construction.
|
|
159
184
|
</p>
|
|
160
185
|
|
|
186
|
+
<!-- ===== ADVANTAGES ===== -->
|
|
187
|
+
<h2 id="advantages">Advantages</h2>
|
|
188
|
+
<div class="feature-grid" style="margin: 2rem 0;">
|
|
189
|
+
<div class="feature-card">
|
|
190
|
+
<h3 class="feature-title">🛡️ Enhanced Security</h3>
|
|
191
|
+
<p>
|
|
192
|
+
cDOM strictly avoids <code>eval()</code> and direct HTML injection. By using a custom
|
|
193
|
+
high-performance parser and a registry of pre-defined helper functions, it provides a safe sandbox
|
|
194
|
+
for dynamic content, making it highly resistant to XSS attacks.
|
|
195
|
+
</p>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="feature-card">
|
|
198
|
+
<h3 class="feature-title">🤖 LLM Friendly</h3>
|
|
199
|
+
<p>
|
|
200
|
+
Large Language Models excel at generating structured data and formulaic expressions. cDOM's
|
|
201
|
+
declarative nature and concise syntax make it far easier for AI to generate correct,
|
|
202
|
+
bug-free UI components compared to traditional JavaScript-heavy frameworks.
|
|
203
|
+
</p>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
161
207
|
<!-- ===== XPATH NAVIGATION ===== -->
|
|
162
208
|
<h2 id="xpath-navigation">Using XPath</h2>
|
|
163
209
|
<p>
|
|
@@ -184,17 +230,20 @@ const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
|
184
230
|
const { $ } = Lightview;
|
|
185
231
|
|
|
186
232
|
const cdom = `{
|
|
233
|
+
// The cdom below uses a compressed syntax. It does not require quotes
|
|
234
|
+
// around properties or JPRX/XPath values and supports comments.
|
|
235
|
+
// It is JSON-like, but not JSON.
|
|
187
236
|
div: {
|
|
188
237
|
id: "profile-container",
|
|
189
238
|
class: "card",
|
|
190
|
-
|
|
239
|
+
data-theme: "dark",
|
|
191
240
|
children: [
|
|
192
241
|
{ h3: "User Profile" },
|
|
193
242
|
{ button: {
|
|
194
243
|
id: "7",
|
|
195
|
-
// XPath
|
|
196
|
-
// XPath
|
|
197
|
-
children: ["Button ",
|
|
244
|
+
// XPath #(../@id) gets the "7" from this button's id
|
|
245
|
+
// XPath #(../../@id) gets "profile-container" from the g-parent div
|
|
246
|
+
children: ["Button ", #(../@id), " in section ", #(../../@id)]
|
|
198
247
|
}}
|
|
199
248
|
]
|
|
200
249
|
}
|
|
@@ -204,33 +253,12 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
|
|
|
204
253
|
</div>
|
|
205
254
|
<p>
|
|
206
255
|
In the example above, the button's text is derived entirely from its own <code>id</code> and its
|
|
207
|
-
parent's <code>id</code> using <code
|
|
256
|
+
parent's <code>id</code> using <code>#(../@id)</code> and <code>#(../../@id)</code>.
|
|
208
257
|
</p>
|
|
209
258
|
<p>
|
|
210
259
|
For more details, see the <a href="/docs/cdom-xpath.html">Full XPath Documentation</a>.
|
|
211
260
|
</p>
|
|
212
261
|
|
|
213
|
-
<!-- ===== ADVANTAGES ===== -->
|
|
214
|
-
<h2 id="advantages">Advantages</h2>
|
|
215
|
-
<div class="feature-grid" style="margin: 2rem 0;">
|
|
216
|
-
<div class="feature-card">
|
|
217
|
-
<h3 class="feature-title">🛡️ Enhanced Security</h3>
|
|
218
|
-
<p>
|
|
219
|
-
cDOM strictly avoids <code>eval()</code> and direct HTML injection. By using a custom
|
|
220
|
-
high-performance parser and a registry of pre-defined helper functions, it provides a safe sandbox
|
|
221
|
-
for dynamic content, making it highly resistant to XSS attacks.
|
|
222
|
-
</p>
|
|
223
|
-
</div>
|
|
224
|
-
<div class="feature-card">
|
|
225
|
-
<h3 class="feature-title">🤖 LLM Friendly</h3>
|
|
226
|
-
<p>
|
|
227
|
-
Large Language Models excel at generating structured data and formulaic expressions. cDOM's
|
|
228
|
-
declarative nature and concise syntax make it far easier for AI to generate correct,
|
|
229
|
-
bug-free UI components compared to traditional JavaScript-heavy frameworks.
|
|
230
|
-
</p>
|
|
231
|
-
</div>
|
|
232
|
-
</div>
|
|
233
|
-
|
|
234
262
|
<!-- ===== JPRX INTRODUCTION ===== -->
|
|
235
263
|
<h2 id="JPRX">JPRX (JSON Pointer Reactive eXpressions)</h2>
|
|
236
264
|
<p>
|
|
@@ -239,54 +267,61 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
|
|
|
239
267
|
with reactivity, relative paths, and helper functions.
|
|
240
268
|
</p>
|
|
241
269
|
|
|
242
|
-
<h3 id="JPRX-delimiters">
|
|
270
|
+
<h3 id="JPRX-delimiters">Expression Syntax</h3>
|
|
243
271
|
<p>
|
|
244
|
-
JPRX expressions
|
|
272
|
+
JPRX expressions use a <strong>wrapper syntax</strong> for unambiguous parsing. The recommended syntax is
|
|
273
|
+
<code>=(expression)</code>:
|
|
245
274
|
</p>
|
|
246
275
|
<table class="api-table">
|
|
247
276
|
<thead>
|
|
248
277
|
<tr>
|
|
249
|
-
<th>
|
|
278
|
+
<th>Syntax</th>
|
|
250
279
|
<th>Purpose</th>
|
|
251
280
|
<th>Example</th>
|
|
252
281
|
</tr>
|
|
253
282
|
</thead>
|
|
254
283
|
<tbody>
|
|
255
284
|
<tr>
|
|
256
|
-
<td><code
|
|
285
|
+
<td><code>=(/path)</code></td>
|
|
257
286
|
<td>Access a path in the global registry</td>
|
|
258
|
-
<td><code
|
|
287
|
+
<td><code>=(/users/0/name)</code></td>
|
|
259
288
|
</tr>
|
|
260
289
|
<tr>
|
|
261
|
-
<td><code>=function(</code></td>
|
|
290
|
+
<td><code>=(function(...))</code></td>
|
|
262
291
|
<td>Call a helper function</td>
|
|
263
|
-
<td><code>=sum(/items...price)</code></td>
|
|
292
|
+
<td><code>=(sum(/items...price))</code></td>
|
|
293
|
+
</tr>
|
|
294
|
+
<tr>
|
|
295
|
+
<td><code>#(xpath)</code></td>
|
|
296
|
+
<td>XPath expression for DOM navigation</td>
|
|
297
|
+
<td><code>#(../../@id)</code></td>
|
|
264
298
|
</tr>
|
|
265
299
|
</tbody>
|
|
266
300
|
</table>
|
|
267
301
|
<p>
|
|
268
|
-
|
|
269
|
-
|
|
302
|
+
The wrapper syntax <code>=()</code> and <code>#()</code> makes expressions unambiguous and eliminates
|
|
303
|
+
conflicts with literal strings.
|
|
304
|
+
Legacy syntax without parentheses (e.g., <code>=/path</code>) is still supported for backward compatibility.
|
|
270
305
|
</p>
|
|
271
306
|
|
|
272
|
-
<h3 id="JPRX-
|
|
307
|
+
<h3 id="JPRX-literal-strings">Literal Strings</h3>
|
|
273
308
|
<p>
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
with a single quote:
|
|
309
|
+
With the new wrapper syntax, you no longer need escape sequences. Any string that doesn't match the wrapper
|
|
310
|
+
pattern
|
|
311
|
+
is treated as a literal:
|
|
278
312
|
</p>
|
|
279
313
|
<div class="code-block" style="margin-bottom: 1rem;">
|
|
280
|
-
<pre><code>//
|
|
281
|
-
{ "p": "
|
|
314
|
+
<pre><code>// JPRX expression (wrapped):
|
|
315
|
+
{ "p": "=(/user/name)" } // Resolves the path /user/name
|
|
282
316
|
|
|
283
|
-
//
|
|
284
|
-
{ "p": "
|
|
285
|
-
{ "p": "
|
|
317
|
+
// Literal strings (no wrapper):
|
|
318
|
+
{ "p": "=E=mc²" } // Renders as "=E=mc²"
|
|
319
|
+
{ "p": "=42" } // Renders as "=42"
|
|
320
|
+
{ "p": "#ff0000" } // Renders as "#ff0000" (hex color)</code></pre>
|
|
286
321
|
</div>
|
|
287
322
|
<p>
|
|
288
|
-
The
|
|
289
|
-
the
|
|
323
|
+
The wrapper syntax eliminates ambiguity: <code>=(expr)</code> is always an expression, while strings without
|
|
324
|
+
the wrapper pattern are always literals.
|
|
290
325
|
</p>
|
|
291
326
|
|
|
292
327
|
<h3 id="JPRX-anatomy">Anatomy of a Path</h3>
|
|
@@ -369,6 +404,44 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
|
|
|
369
404
|
</tbody>
|
|
370
405
|
</table>
|
|
371
406
|
|
|
407
|
+
<h3 id="JPRX-explosion">Explosion & Mapping (<code>...</code>)</h3>
|
|
408
|
+
<p>
|
|
409
|
+
The <code>...</code> operator is a powerful JPRX extension used for mapping properties from arrays and
|
|
410
|
+
spreading array elements as function arguments.
|
|
411
|
+
</p>
|
|
412
|
+
|
|
413
|
+
<h4>Mapping & Auto-Explosion: <code>path...property</code></h4>
|
|
414
|
+
<p>
|
|
415
|
+
When used between a path to an array and a property name, it acts as a shorthand for mapping. It extracts
|
|
416
|
+
the specified property from every object in the array. <strong>Inside a function call, it automatically
|
|
417
|
+
spreads (explodes) the resulting values as individual arguments.</strong>
|
|
418
|
+
</p>
|
|
419
|
+
<div class="code-block" style="margin-bottom: 1.5rem;">
|
|
420
|
+
<pre><code>// Correct: Maps 'price' and automatically spreads results as arguments to sum()
|
|
421
|
+
=sum(/cart/items...price)
|
|
422
|
+
|
|
423
|
+
// Mapping also works for direct display (returns an array)
|
|
424
|
+
{ p: ["Prices: ", =/cart/items...price] }</code></pre>
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
<h4>Argument Spreading: <code>path...</code></h4>
|
|
428
|
+
<p>
|
|
429
|
+
When an array path is followed by a trailing <code>...</code> at the end of the path expression (and no
|
|
430
|
+
property name follows), the array elements are spread as individual arguments. Use this when you have a
|
|
431
|
+
direct reference to an array.
|
|
432
|
+
</p>
|
|
433
|
+
<div class="code-block">
|
|
434
|
+
<pre><code>// Passes each number in the array as a separate argument
|
|
435
|
+
=sum(/listOfNumbers...)</code></pre>
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
<div class="warning-notice"
|
|
439
|
+
style="margin-top: 1.5rem; padding: 1rem; background: rgba(239, 68, 68, 0.1); border-left: 4px solid #ef4444; border-radius: 4px;">
|
|
440
|
+
<strong>Warning:</strong> Do not combine infix mapping with trailing dots. <code>/items...price...</code> is
|
|
441
|
+
invalid because the trailing dots are interpreted as part of the property name (looking for a property
|
|
442
|
+
literally named "price...").
|
|
443
|
+
</div>
|
|
444
|
+
|
|
372
445
|
<!-- ===== COMPARISON TO EXCEL ===== -->
|
|
373
446
|
<h2 id="comparison">Comparison to Excel</h2>
|
|
374
447
|
<p>
|
|
@@ -536,8 +609,37 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
|
|
|
536
609
|
<h3 id="lifecycle-state">Lifecycle State</h3>
|
|
537
610
|
<p>
|
|
538
611
|
In Lightview, you initialize state within the <code>onmount</code> hook. The <code>=state</code> and
|
|
539
|
-
<code>=signal</code> helpers
|
|
540
|
-
|
|
612
|
+
<code>=signal</code> helpers allow you to create reactive data that is tied to the DOM structure.
|
|
613
|
+
</p>
|
|
614
|
+
|
|
615
|
+
<h4 id="scope-resolution">Scope & Name Resolution</h4>
|
|
616
|
+
<p>
|
|
617
|
+
When JPRX encounters a name (e.g., <code>=/profile/name</code>), it doesn't just look in a global registry.
|
|
618
|
+
Instead, it performs an <strong>up-tree search</strong>:
|
|
619
|
+
</p>
|
|
620
|
+
<ol>
|
|
621
|
+
<li>It starts at the element where the expression is defined.</li>
|
|
622
|
+
<li>It looks for a registered signal or state with that name.</li>
|
|
623
|
+
<li>If not found, it moves to the parent element and repeats the search.</li>
|
|
624
|
+
<li>This continues all the way to the <code>document</code> root, finally checking the global registry if
|
|
625
|
+
needed.</li>
|
|
626
|
+
</ol>
|
|
627
|
+
|
|
628
|
+
<h4>The <code>$this</code> Placeholder</h4>
|
|
629
|
+
<p>
|
|
630
|
+
The <code>$this</code> keyword is a special placeholder that represents the <strong>current
|
|
631
|
+
element</strong>. When used in the <code>scope</code> option of <code>=state</code> or
|
|
632
|
+
<code>=signal</code>, it instructs Lightview to register the name specifically at that element's level in
|
|
633
|
+
the DOM.
|
|
634
|
+
</p>
|
|
635
|
+
<div class="code-block" style="margin-bottom: 1.5rem;">
|
|
636
|
+
<pre><code>// Scoping state to the current element creates a local namespace
|
|
637
|
+
{ "onmount": "=state({ count: 0 }, { name: 'local', scope: $this })" }</code></pre>
|
|
638
|
+
</div>
|
|
639
|
+
<p>
|
|
640
|
+
This mechanism is what allows you to create <strong>reusable components</strong>. Each instance of a
|
|
641
|
+
component can have its own "local" state because the search for <code>local</code> will stop at the first
|
|
642
|
+
element it finds that has it registered—typically the root of the component instance.
|
|
541
643
|
</p>
|
|
542
644
|
|
|
543
645
|
<h4>Using a Registered Schema</h4>
|
|
@@ -578,10 +680,7 @@ Lightview.registerSchema('User', { name: 'string', age: 'number' });
|
|
|
578
680
|
}
|
|
579
681
|
}</code></pre>
|
|
580
682
|
</div>
|
|
581
|
-
|
|
582
|
-
By scoping the state to the element, you can create multiple independent instances of components.
|
|
583
|
-
Lightview uses a high-performance <strong>up-tree search</strong> to resolve these names.
|
|
584
|
-
</p>
|
|
683
|
+
|
|
585
684
|
|
|
586
685
|
<h3 id="bind-helper">Two-Way Binding ($bind)</h3>
|
|
587
686
|
<p>
|
|
@@ -646,11 +745,11 @@ const cdomString = `{
|
|
|
646
745
|
children: [
|
|
647
746
|
{ h3: "Shopping Cart" },
|
|
648
747
|
{ ul: {
|
|
649
|
-
children: =map(/store/cart/items, { li: { children: [_/name, " - ", currency(_/price)] } })
|
|
748
|
+
children: =(map(/store/cart/items, { li: { children: [_/name, " - ", currency(_/price)] } }))
|
|
650
749
|
}},
|
|
651
750
|
{ p: {
|
|
652
751
|
style: "font-weight: bold; margin-top: 1rem;",
|
|
653
|
-
children: ["Total: ", =currency(sum(/store/cart/items...price))]
|
|
752
|
+
children: ["Total: ", =(currency(sum(/store/cart/items...price)))]
|
|
654
753
|
}}
|
|
655
754
|
]
|
|
656
755
|
}
|
|
@@ -664,17 +763,15 @@ $('#example').content(hydrated);
|
|
|
664
763
|
<!-- ===== INTERACTIVE EXAMPLE ===== -->
|
|
665
764
|
<h2 id="interactive-example">Interactive Example</h2>
|
|
666
765
|
<p>
|
|
667
|
-
Choose a syntax to see how the same reactive counter can be defined using standard JSON, concise
|
|
766
|
+
Choose a syntax to see how the same reactive counter can be defined using standard JSON, concise cDOMC, or
|
|
668
767
|
operator syntax.
|
|
669
768
|
</p>
|
|
670
769
|
|
|
671
770
|
<div role="tablist" class="syntax-tabs">
|
|
672
771
|
<button id="tab-btn-jprx" class="syntax-tab syntax-tab-active" onclick="switchCDOMTab('jprx')">JPRX
|
|
673
772
|
(Standard)</button>
|
|
674
|
-
<button id="tab-btn-
|
|
773
|
+
<button id="tab-btn-cdomc" class="syntax-tab" onclick="switchCDOMTab('cdomc')">cDOMC</button>
|
|
675
774
|
<button id="tab-btn-operators" class="syntax-tab" onclick="switchCDOMTab('operators')">Operators</button>
|
|
676
|
-
<button id="tab-btn-embedded" class="syntax-tab" onclick="switchCDOMTab('embedded')">Embedded
|
|
677
|
-
Signals</button>
|
|
678
775
|
</div>
|
|
679
776
|
|
|
680
777
|
<!-- JPRX Pane -->
|
|
@@ -690,18 +787,17 @@ $('#example').content(hydrated);
|
|
|
690
787
|
</script><code contenteditable="true">// JPRX: Standard JSON format (strict)
|
|
691
788
|
await import('/lightview-cdom.js');
|
|
692
789
|
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
693
|
-
const {
|
|
694
|
-
|
|
695
|
-
const count = signal(0, 'count');
|
|
790
|
+
const { $ } = Lightview;
|
|
696
791
|
|
|
697
792
|
const cdomString = `{
|
|
698
793
|
"div": {
|
|
794
|
+
onmount: "=signal(0, 'count')",
|
|
699
795
|
"children": [
|
|
700
796
|
{ "h3": ["Standard JPRX Counter"] },
|
|
701
|
-
{ "p": { "children": ["Count: ", "
|
|
797
|
+
{ "p": { "children": ["Count: ", "=(/count)"] }},
|
|
702
798
|
{ "div": { "children": [
|
|
703
|
-
{ "button": { "onclick": "=decrement(/count)", "children": ["-"] } },
|
|
704
|
-
{ "button": { "onclick": "=increment(/count)", "children": ["+"] } }
|
|
799
|
+
{ "button": { "onclick": "=(decrement(/count))", "children": ["-"] } },
|
|
800
|
+
{ "button": { "onclick": "=(increment(/count))", "children": ["+"] } }
|
|
705
801
|
]}}
|
|
706
802
|
]
|
|
707
803
|
}
|
|
@@ -709,12 +805,11 @@ const cdomString = `{
|
|
|
709
805
|
|
|
710
806
|
const hydrated = hydrate(parseJPRX(cdomString));
|
|
711
807
|
$('#example').content(hydrated);
|
|
712
|
-
globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
713
808
|
</code></pre>
|
|
714
809
|
</div>
|
|
715
810
|
|
|
716
|
-
<!--
|
|
717
|
-
<div id="pane-
|
|
811
|
+
<!-- cDOMC Pane -->
|
|
812
|
+
<div id="pane-cdomc" style="display: none;">
|
|
718
813
|
<pre><script>
|
|
719
814
|
examplify(document.currentScript.nextElementSibling, {
|
|
720
815
|
at: document.currentScript.parentElement,
|
|
@@ -722,21 +817,21 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
|
722
817
|
type: 'module',
|
|
723
818
|
height: '250px'
|
|
724
819
|
});
|
|
725
|
-
</script><code contenteditable="true">//
|
|
820
|
+
</script><code contenteditable="true">// cDOMC: Compressed format (unquoted keys, comments)
|
|
726
821
|
await import('/lightview-cdom.js');
|
|
727
822
|
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
728
|
-
const {
|
|
823
|
+
const { $ } = ightview;
|
|
729
824
|
|
|
730
|
-
const count = signal(0, 'count');
|
|
731
825
|
|
|
732
826
|
const cdomString = `{
|
|
733
827
|
div: {
|
|
828
|
+
onmount: =signal(0, 'count'),
|
|
734
829
|
children: [
|
|
735
|
-
{ h3: ["
|
|
736
|
-
{ p: { children: ["Count: ",
|
|
830
|
+
{ h3: ["Compressed Counter"] },
|
|
831
|
+
{ p: { children: ["Count: ", =(/count)] }},
|
|
737
832
|
{ div: { children: [
|
|
738
|
-
{ button: { onclick: =decrement(/count), children: ["-"] } },
|
|
739
|
-
{ button: { onclick: =increment(/count), children: ["+"] } }
|
|
833
|
+
{ button: { onclick: =(decrement(/count)), children: ["-"] } },
|
|
834
|
+
{ button: { onclick: =(increment(/count)), children: ["+"] } }
|
|
740
835
|
]}}
|
|
741
836
|
]
|
|
742
837
|
}
|
|
@@ -760,19 +855,18 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
|
760
855
|
</script><code contenteditable="true">// Operators: Using =++/path and =--/path
|
|
761
856
|
await import('/lightview-cdom.js');
|
|
762
857
|
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
763
|
-
const {
|
|
764
|
-
|
|
765
|
-
const count = signal(0, 'count');
|
|
858
|
+
const { $ } = Lightview;
|
|
766
859
|
|
|
767
860
|
const cdomString = `{
|
|
768
861
|
div: {
|
|
862
|
+
onmount: =signal(0, 'count'),
|
|
769
863
|
children: [
|
|
770
864
|
{ h3: ["Operator Counter"] },
|
|
771
|
-
{ p: { children: ["Count: ",
|
|
865
|
+
{ p: { children: ["Count: ", =(/count)] }},
|
|
772
866
|
{ div: { children: [
|
|
773
|
-
// Prefix operators:
|
|
774
|
-
{ button: { onclick:
|
|
775
|
-
{ button: { onclick:
|
|
867
|
+
// Prefix operators: =(--/count) and =(++/count)
|
|
868
|
+
{ button: { onclick: =(--/count), children: ["-"] } },
|
|
869
|
+
{ button: { onclick: =(++/count), children: ["+"] } }
|
|
776
870
|
]}}
|
|
777
871
|
]
|
|
778
872
|
}
|
|
@@ -784,39 +878,6 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
|
784
878
|
</code></pre>
|
|
785
879
|
</div>
|
|
786
880
|
|
|
787
|
-
<!-- Embedded Pane -->
|
|
788
|
-
<div id="pane-embedded" style="display: none;">
|
|
789
|
-
<pre><script>
|
|
790
|
-
examplify(document.currentScript.nextElementSibling, {
|
|
791
|
-
at: document.currentScript.parentElement,
|
|
792
|
-
scripts: ['/lightview.js', '/lightview-x.js', '/lightview-cdom.js'],
|
|
793
|
-
type: 'module',
|
|
794
|
-
height: '250px'
|
|
795
|
-
});
|
|
796
|
-
</script><code contenteditable="true">// Embedded Signals: Using onmount to initialize local state
|
|
797
|
-
await import('/lightview-cdom.js');
|
|
798
|
-
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
799
|
-
const { $ } = Lightview;
|
|
800
|
-
|
|
801
|
-
const cdomString = `{
|
|
802
|
-
div: {
|
|
803
|
-
// Initialize a named signal explicitly scoped to '$this' element.
|
|
804
|
-
onmount: =signal(0, { name: 'count', scope: $this }),
|
|
805
|
-
children: [
|
|
806
|
-
{ h3: ["Embedded Counter"] },
|
|
807
|
-
{ p: ["Count: ", =/count] },
|
|
808
|
-
{ div: { children: [
|
|
809
|
-
{ button: { onclick: =--/count, children: ["-"] } },
|
|
810
|
-
{ button: { onclick: =++/count, children: ["+"] } }
|
|
811
|
-
]}}
|
|
812
|
-
]
|
|
813
|
-
}
|
|
814
|
-
}`;
|
|
815
|
-
|
|
816
|
-
const hydrated = hydrate(parseJPRX(cdomString));
|
|
817
|
-
$('#example').content(hydrated);
|
|
818
|
-
</code></pre>
|
|
819
|
-
</div>
|
|
820
881
|
|
|
821
882
|
<!-- ===== OPERATOR SYNTAX EXAMPLE ===== -->
|
|
822
883
|
<h2 id="operator-syntax">Operator Syntax</h2>
|
|
@@ -938,7 +999,7 @@ LightviewCDOM.activate(document.getElementById('myApp'));</code></pre>
|
|
|
938
999
|
|
|
939
1000
|
<h3 id="api-hydrate">hydrate(object)</h3>
|
|
940
1001
|
<p>
|
|
941
|
-
Converts <code
|
|
1002
|
+
Converts <code>=/</code> prefixed strings into reactive computed signals.
|
|
942
1003
|
</p>
|
|
943
1004
|
<div class="code-block">
|
|
944
1005
|
<pre><code>const config = { title: "Dashboard", total: "=/cart/items...price" };
|