lightview 2.3.8 → 2.4.7
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/.gemini/CODE_ANALYSIS_AND_IMPROVEMENT_PLAN.md +56 -0
- package/AI-GUIDANCE.md +274 -0
- package/README.md +35 -0
- package/build_tmp/lightview-cdom.js +3934 -0
- package/build_tmp/lightview-router.js +185 -0
- package/build_tmp/lightview-x.js +1739 -0
- package/build_tmp/lightview.js +740 -0
- package/components/data-display/diff.js +36 -4
- package/docs/api/hypermedia.html +75 -5
- package/docs/api/index.html +3 -3
- package/docs/api/nav.html +0 -16
- package/docs/articles/html-vs-json-partials.md +102 -0
- package/docs/articles/lightview-vs-htmx.md +610 -0
- package/docs/assets/styles/site.css +16 -7
- package/docs/benchmarks/bau-tagged-fragment.js +41 -0
- package/docs/benchmarks/tagged-fragment.js +36 -0
- package/docs/cdom.html +127 -88
- package/docs/components/chart.html +157 -210
- package/docs/components/component-nav.html +1 -1
- package/docs/components/diff.html +33 -21
- package/docs/components/gallery.html +107 -4
- package/docs/components/index.css +18 -3
- package/docs/components/index.html +20 -9
- package/docs/dom-benchmark.html +771 -0
- package/docs/getting-started/index.html +42 -2
- package/docs/hypermedia/index.html +391 -0
- package/docs/hypermedia/nav.html +17 -0
- package/docs/index.html +136 -17
- package/index.html +59 -10
- package/lightview-all.js +223 -67
- package/lightview-cdom.js +1 -2
- package/lightview-x.js +144 -13
- package/lightview.js +85 -277
- package/package.json +2 -2
- package/src/lightview-cdom.js +1 -5
- package/src/lightview-x.js +158 -27
- package/src/lightview.js +94 -60
- package/docs/articles/calculator-no-javascript-hackernoon.md +0 -283
- package/docs/articles/calculator-no-javascript.md +0 -290
- package/docs/articles/part1-reference.md +0 -236
- package/lightview.js.bak +0 -1
- package/test-xpath.html +0 -63
- package/test_error.txt +0 -0
- package/test_output.txt +0 -0
- package/test_output_full.txt +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
(async function () {
|
|
2
|
+
// In ES modules, document.currentScript is null, so we use import.meta.url
|
|
3
|
+
const urlParams = new URLSearchParams(import.meta.url.split('?')[1]);
|
|
4
|
+
const count = parseInt(urlParams.get('count')) || 1000;
|
|
5
|
+
|
|
6
|
+
// Settle inside the script context
|
|
7
|
+
if (window.gc) window.gc();
|
|
8
|
+
await new Promise(r => setTimeout(r, 100));
|
|
9
|
+
|
|
10
|
+
const start = performance.now();
|
|
11
|
+
|
|
12
|
+
// Import Bau from CDN
|
|
13
|
+
const { default: Bau } = await import('https://cdn.jsdelivr.net/npm/@grucloud/bau@0.106.0/+esm');
|
|
14
|
+
const bau = Bau();
|
|
15
|
+
const { section, header, h2, h3, div, article, p, span, footer } = bau.tags;
|
|
16
|
+
|
|
17
|
+
const items = [];
|
|
18
|
+
for (let i = 0; i < count; i++) {
|
|
19
|
+
items.push(article({ class: 'item-card' },
|
|
20
|
+
header(h3({ class: 'item-title' }, `Item ${i}`)),
|
|
21
|
+
div({ class: 'item-content' }, p({ class: 'item-description' }, `Detailed description for item ${i} in the benchmark list.`)),
|
|
22
|
+
footer({ class: 'item-footer' }, span(`Metadata for item ${i}`))
|
|
23
|
+
));
|
|
24
|
+
}
|
|
25
|
+
const fragment = section({ class: 'benchmark-container' },
|
|
26
|
+
header(h2('Benchmark Results')),
|
|
27
|
+
div({ class: 'items-grid' }, ...items)
|
|
28
|
+
);
|
|
29
|
+
const target = document.getElementById('benchmark-target');
|
|
30
|
+
target.replaceChildren(fragment);
|
|
31
|
+
|
|
32
|
+
const end = performance.now();
|
|
33
|
+
|
|
34
|
+
// Store timing in a global for the driver to pick up
|
|
35
|
+
window.__bauTaggedBenchmarkTime = end - start;
|
|
36
|
+
|
|
37
|
+
// Signal completion
|
|
38
|
+
if (window.__resolveBauTaggedBenchmark) {
|
|
39
|
+
window.__resolveBauTaggedBenchmark();
|
|
40
|
+
}
|
|
41
|
+
})();
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
(async function () {
|
|
2
|
+
const urlParams = new URLSearchParams(document.currentScript.src.split('?')[1]);
|
|
3
|
+
const count = parseInt(urlParams.get('count')) || 1000;
|
|
4
|
+
|
|
5
|
+
// Settle inside the script context
|
|
6
|
+
if (window.gc) window.gc();
|
|
7
|
+
await new Promise(r => setTimeout(r, 100));
|
|
8
|
+
|
|
9
|
+
const start = performance.now();
|
|
10
|
+
|
|
11
|
+
const { section, header, h2, h3, div, article, p, span, footer } = Lightview.tags;
|
|
12
|
+
const items = [];
|
|
13
|
+
for (let i = 0; i < count; i++) {
|
|
14
|
+
items.push(article({ class: 'item-card' },
|
|
15
|
+
header(h3({ class: 'item-title' }, `Item ${i}`)),
|
|
16
|
+
div({ class: 'item-content' }, p({ class: 'item-description' }, `Detailed description for item ${i} in the benchmark list.`)),
|
|
17
|
+
footer({ class: 'item-footer' }, span(`Metadata for item ${i}`))
|
|
18
|
+
));
|
|
19
|
+
}
|
|
20
|
+
const fragment = section({ class: 'benchmark-container' },
|
|
21
|
+
header(h2('Benchmark Results')),
|
|
22
|
+
div({ class: 'items-grid' }, ...items)
|
|
23
|
+
);
|
|
24
|
+
const target = document.getElementById('benchmark-target');
|
|
25
|
+
target.replaceChildren(fragment.domEl);
|
|
26
|
+
|
|
27
|
+
const end = performance.now();
|
|
28
|
+
|
|
29
|
+
// Store timing in a global for the driver to pick up
|
|
30
|
+
window.__taggedBenchmarkTime = end - start;
|
|
31
|
+
|
|
32
|
+
// Signal completion
|
|
33
|
+
if (window.__resolveTaggedBenchmark) {
|
|
34
|
+
window.__resolveTaggedBenchmark();
|
|
35
|
+
}
|
|
36
|
+
})();
|
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}`);
|
|
@@ -132,13 +132,14 @@ const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
|
132
132
|
const { $ } = Lightview;
|
|
133
133
|
|
|
134
134
|
const cdom = `{
|
|
135
|
-
div: {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
{
|
|
140
|
-
{
|
|
141
|
-
{ button: { onclick:
|
|
135
|
+
"div": {
|
|
136
|
+
"id": "Counter",
|
|
137
|
+
"onmount": "=state({ count: 0 }, { name: 'local', schema: 'auto', scope: $this })",
|
|
138
|
+
"children": [
|
|
139
|
+
{ "h2": "#../../@id" }, // XPath shorthand: text node -> h2 -> div
|
|
140
|
+
{ "p": ["Count: ", "=/local/count"] },
|
|
141
|
+
{ "button": { "onclick": "=++/local/count", "children": ["+"] } },
|
|
142
|
+
{ "button": { "onclick": "=--/local/count", "children": ["-"] } }
|
|
142
143
|
]
|
|
143
144
|
}
|
|
144
145
|
}`;
|
|
@@ -150,14 +151,44 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
|
|
|
150
151
|
</p>
|
|
151
152
|
<ul>
|
|
152
153
|
<li><code>onmount</code> — A Lightview lifecycle hook where we initialize state.</li>
|
|
154
|
+
<li><code>#../../@id</code> — An XPath expression that extracts the <code>id</code> from the grandparent
|
|
155
|
+
element.</li>
|
|
153
156
|
<li><code>=state(...)</code> — An initializer that creates local reactive state.</li>
|
|
154
157
|
<li><code>scope: $this</code> — Explicitly attaches the state to the current element.</li>
|
|
155
158
|
<li><code>=/local/count</code> — A path that reactively displays the count value.</li>
|
|
156
159
|
</ul>
|
|
160
|
+
<p style="font-size: 0.95rem; font-style: italic; color: var(--site-text-secondary);">
|
|
161
|
+
<strong>Note:</strong> In cDOM, <code>#</code> is the prefix for <strong>XPath</strong> expressions that
|
|
162
|
+
navigate and extract data from the DOM, while <code>=</code> is the prefix for <strong>JPRX</strong>
|
|
163
|
+
expressions that navigate or apply functions to
|
|
164
|
+
reactive state.
|
|
165
|
+
</p>
|
|
157
166
|
<p>
|
|
158
|
-
The UI automatically updates whenever <code>count</code> changes — no manual DOM manipulation required.
|
|
167
|
+
The UI automatically updates whenever <code>count</code> changes — no manual DOM manipulation required. It
|
|
168
|
+
also uses XPath to navigate the DOM structure during construction.
|
|
159
169
|
</p>
|
|
160
170
|
|
|
171
|
+
<!-- ===== ADVANTAGES ===== -->
|
|
172
|
+
<h2 id="advantages">Advantages</h2>
|
|
173
|
+
<div class="feature-grid" style="margin: 2rem 0;">
|
|
174
|
+
<div class="feature-card">
|
|
175
|
+
<h3 class="feature-title">🛡️ Enhanced Security</h3>
|
|
176
|
+
<p>
|
|
177
|
+
cDOM strictly avoids <code>eval()</code> and direct HTML injection. By using a custom
|
|
178
|
+
high-performance parser and a registry of pre-defined helper functions, it provides a safe sandbox
|
|
179
|
+
for dynamic content, making it highly resistant to XSS attacks.
|
|
180
|
+
</p>
|
|
181
|
+
</div>
|
|
182
|
+
<div class="feature-card">
|
|
183
|
+
<h3 class="feature-title">🤖 LLM Friendly</h3>
|
|
184
|
+
<p>
|
|
185
|
+
Large Language Models excel at generating structured data and formulaic expressions. cDOM's
|
|
186
|
+
declarative nature and concise syntax make it far easier for AI to generate correct,
|
|
187
|
+
bug-free UI components compared to traditional JavaScript-heavy frameworks.
|
|
188
|
+
</p>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
|
|
161
192
|
<!-- ===== XPATH NAVIGATION ===== -->
|
|
162
193
|
<h2 id="xpath-navigation">Using XPath</h2>
|
|
163
194
|
<p>
|
|
@@ -184,10 +215,13 @@ const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
|
184
215
|
const { $ } = Lightview;
|
|
185
216
|
|
|
186
217
|
const cdom = `{
|
|
218
|
+
// The cdom below uses a compressed syntax. It does not require quotes
|
|
219
|
+
// around properties or JPRX/XPath values and supports comments.
|
|
220
|
+
// It is JSON-like, but not JSON.
|
|
187
221
|
div: {
|
|
188
222
|
id: "profile-container",
|
|
189
223
|
class: "card",
|
|
190
|
-
|
|
224
|
+
data-theme: "dark",
|
|
191
225
|
children: [
|
|
192
226
|
{ h3: "User Profile" },
|
|
193
227
|
{ button: {
|
|
@@ -210,27 +244,6 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
|
|
|
210
244
|
For more details, see the <a href="/docs/cdom-xpath.html">Full XPath Documentation</a>.
|
|
211
245
|
</p>
|
|
212
246
|
|
|
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
247
|
<!-- ===== JPRX INTRODUCTION ===== -->
|
|
235
248
|
<h2 id="JPRX">JPRX (JSON Pointer Reactive eXpressions)</h2>
|
|
236
249
|
<p>
|
|
@@ -369,6 +382,44 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
|
|
|
369
382
|
</tbody>
|
|
370
383
|
</table>
|
|
371
384
|
|
|
385
|
+
<h3 id="JPRX-explosion">Explosion & Mapping (<code>...</code>)</h3>
|
|
386
|
+
<p>
|
|
387
|
+
The <code>...</code> operator is a powerful JPRX extension used for mapping properties from arrays and
|
|
388
|
+
spreading array elements as function arguments.
|
|
389
|
+
</p>
|
|
390
|
+
|
|
391
|
+
<h4>Mapping & Auto-Explosion: <code>path...property</code></h4>
|
|
392
|
+
<p>
|
|
393
|
+
When used between a path to an array and a property name, it acts as a shorthand for mapping. It extracts
|
|
394
|
+
the specified property from every object in the array. <strong>Inside a function call, it automatically
|
|
395
|
+
spreads (explodes) the resulting values as individual arguments.</strong>
|
|
396
|
+
</p>
|
|
397
|
+
<div class="code-block" style="margin-bottom: 1.5rem;">
|
|
398
|
+
<pre><code>// Correct: Maps 'price' and automatically spreads results as arguments to sum()
|
|
399
|
+
=sum(/cart/items...price)
|
|
400
|
+
|
|
401
|
+
// Mapping also works for direct display (returns an array)
|
|
402
|
+
{ p: ["Prices: ", =/cart/items...price] }</code></pre>
|
|
403
|
+
</div>
|
|
404
|
+
|
|
405
|
+
<h4>Argument Spreading: <code>path...</code></h4>
|
|
406
|
+
<p>
|
|
407
|
+
When an array path is followed by a trailing <code>...</code> at the end of the path expression (and no
|
|
408
|
+
property name follows), the array elements are spread as individual arguments. Use this when you have a
|
|
409
|
+
direct reference to an array.
|
|
410
|
+
</p>
|
|
411
|
+
<div class="code-block">
|
|
412
|
+
<pre><code>// Passes each number in the array as a separate argument
|
|
413
|
+
=sum(/listOfNumbers...)</code></pre>
|
|
414
|
+
</div>
|
|
415
|
+
|
|
416
|
+
<div class="warning-notice"
|
|
417
|
+
style="margin-top: 1.5rem; padding: 1rem; background: rgba(239, 68, 68, 0.1); border-left: 4px solid #ef4444; border-radius: 4px;">
|
|
418
|
+
<strong>Warning:</strong> Do not combine infix mapping with trailing dots. <code>/items...price...</code> is
|
|
419
|
+
invalid because the trailing dots are interpreted as part of the property name (looking for a property
|
|
420
|
+
literally named "price...").
|
|
421
|
+
</div>
|
|
422
|
+
|
|
372
423
|
<!-- ===== COMPARISON TO EXCEL ===== -->
|
|
373
424
|
<h2 id="comparison">Comparison to Excel</h2>
|
|
374
425
|
<p>
|
|
@@ -536,8 +587,37 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
|
|
|
536
587
|
<h3 id="lifecycle-state">Lifecycle State</h3>
|
|
537
588
|
<p>
|
|
538
589
|
In Lightview, you initialize state within the <code>onmount</code> hook. The <code>=state</code> and
|
|
539
|
-
<code>=signal</code> helpers
|
|
540
|
-
|
|
590
|
+
<code>=signal</code> helpers allow you to create reactive data that is tied to the DOM structure.
|
|
591
|
+
</p>
|
|
592
|
+
|
|
593
|
+
<h4 id="scope-resolution">Scope & Name Resolution</h4>
|
|
594
|
+
<p>
|
|
595
|
+
When JPRX encounters a name (e.g., <code>=/profile/name</code>), it doesn't just look in a global registry.
|
|
596
|
+
Instead, it performs an <strong>up-tree search</strong>:
|
|
597
|
+
</p>
|
|
598
|
+
<ol>
|
|
599
|
+
<li>It starts at the element where the expression is defined.</li>
|
|
600
|
+
<li>It looks for a registered signal or state with that name.</li>
|
|
601
|
+
<li>If not found, it moves to the parent element and repeats the search.</li>
|
|
602
|
+
<li>This continues all the way to the <code>document</code> root, finally checking the global registry if
|
|
603
|
+
needed.</li>
|
|
604
|
+
</ol>
|
|
605
|
+
|
|
606
|
+
<h4>The <code>$this</code> Placeholder</h4>
|
|
607
|
+
<p>
|
|
608
|
+
The <code>$this</code> keyword is a special placeholder that represents the <strong>current
|
|
609
|
+
element</strong>. When used in the <code>scope</code> option of <code>=state</code> or
|
|
610
|
+
<code>=signal</code>, it instructs Lightview to register the name specifically at that element's level in
|
|
611
|
+
the DOM.
|
|
612
|
+
</p>
|
|
613
|
+
<div class="code-block" style="margin-bottom: 1.5rem;">
|
|
614
|
+
<pre><code>// Scoping state to the current element creates a local namespace
|
|
615
|
+
{ "onmount": "=state({ count: 0 }, { name: 'local', scope: $this })" }</code></pre>
|
|
616
|
+
</div>
|
|
617
|
+
<p>
|
|
618
|
+
This mechanism is what allows you to create <strong>reusable components</strong>. Each instance of a
|
|
619
|
+
component can have its own "local" state because the search for <code>local</code> will stop at the first
|
|
620
|
+
element it finds that has it registered—typically the root of the component instance.
|
|
541
621
|
</p>
|
|
542
622
|
|
|
543
623
|
<h4>Using a Registered Schema</h4>
|
|
@@ -578,10 +658,7 @@ Lightview.registerSchema('User', { name: 'string', age: 'number' });
|
|
|
578
658
|
}
|
|
579
659
|
}</code></pre>
|
|
580
660
|
</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>
|
|
661
|
+
|
|
585
662
|
|
|
586
663
|
<h3 id="bind-helper">Two-Way Binding ($bind)</h3>
|
|
587
664
|
<p>
|
|
@@ -664,17 +741,15 @@ $('#example').content(hydrated);
|
|
|
664
741
|
<!-- ===== INTERACTIVE EXAMPLE ===== -->
|
|
665
742
|
<h2 id="interactive-example">Interactive Example</h2>
|
|
666
743
|
<p>
|
|
667
|
-
Choose a syntax to see how the same reactive counter can be defined using standard JSON, concise
|
|
744
|
+
Choose a syntax to see how the same reactive counter can be defined using standard JSON, concise cDOMC, or
|
|
668
745
|
operator syntax.
|
|
669
746
|
</p>
|
|
670
747
|
|
|
671
748
|
<div role="tablist" class="syntax-tabs">
|
|
672
749
|
<button id="tab-btn-jprx" class="syntax-tab syntax-tab-active" onclick="switchCDOMTab('jprx')">JPRX
|
|
673
750
|
(Standard)</button>
|
|
674
|
-
<button id="tab-btn-
|
|
751
|
+
<button id="tab-btn-cdomc" class="syntax-tab" onclick="switchCDOMTab('cdomc')">cDOMC</button>
|
|
675
752
|
<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
753
|
</div>
|
|
679
754
|
|
|
680
755
|
<!-- JPRX Pane -->
|
|
@@ -690,12 +765,11 @@ $('#example').content(hydrated);
|
|
|
690
765
|
</script><code contenteditable="true">// JPRX: Standard JSON format (strict)
|
|
691
766
|
await import('/lightview-cdom.js');
|
|
692
767
|
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
693
|
-
const {
|
|
694
|
-
|
|
695
|
-
const count = signal(0, 'count');
|
|
768
|
+
const { $ } = Lightview;
|
|
696
769
|
|
|
697
770
|
const cdomString = `{
|
|
698
771
|
"div": {
|
|
772
|
+
onmount: "=signal(0, 'count')",
|
|
699
773
|
"children": [
|
|
700
774
|
{ "h3": ["Standard JPRX Counter"] },
|
|
701
775
|
{ "p": { "children": ["Count: ", "=/count"] }},
|
|
@@ -709,12 +783,11 @@ const cdomString = `{
|
|
|
709
783
|
|
|
710
784
|
const hydrated = hydrate(parseJPRX(cdomString));
|
|
711
785
|
$('#example').content(hydrated);
|
|
712
|
-
globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
713
786
|
</code></pre>
|
|
714
787
|
</div>
|
|
715
788
|
|
|
716
|
-
<!--
|
|
717
|
-
<div id="pane-
|
|
789
|
+
<!-- cDOMC Pane -->
|
|
790
|
+
<div id="pane-cdomc" style="display: none;">
|
|
718
791
|
<pre><script>
|
|
719
792
|
examplify(document.currentScript.nextElementSibling, {
|
|
720
793
|
at: document.currentScript.parentElement,
|
|
@@ -722,17 +795,17 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
|
722
795
|
type: 'module',
|
|
723
796
|
height: '250px'
|
|
724
797
|
});
|
|
725
|
-
</script><code contenteditable="true">//
|
|
798
|
+
</script><code contenteditable="true">// cDOMC: Compressed format (unquoted keys, comments)
|
|
726
799
|
await import('/lightview-cdom.js');
|
|
727
800
|
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
728
|
-
const {
|
|
801
|
+
const { $ } = ightview;
|
|
729
802
|
|
|
730
|
-
const count = signal(0, 'count');
|
|
731
803
|
|
|
732
804
|
const cdomString = `{
|
|
733
805
|
div: {
|
|
806
|
+
onmount: =signal(0, 'count'),
|
|
734
807
|
children: [
|
|
735
|
-
{ h3: ["
|
|
808
|
+
{ h3: ["Compressed Counter"] },
|
|
736
809
|
{ p: { children: ["Count: ", =/count] }},
|
|
737
810
|
{ div: { children: [
|
|
738
811
|
{ button: { onclick: =decrement(/count), children: ["-"] } },
|
|
@@ -760,12 +833,11 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
|
760
833
|
</script><code contenteditable="true">// Operators: Using =++/path and =--/path
|
|
761
834
|
await import('/lightview-cdom.js');
|
|
762
835
|
const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
|
|
763
|
-
const {
|
|
764
|
-
|
|
765
|
-
const count = signal(0, 'count');
|
|
836
|
+
const { $ } = Lightview;
|
|
766
837
|
|
|
767
838
|
const cdomString = `{
|
|
768
839
|
div: {
|
|
840
|
+
onmount: =signal(0, 'count'),
|
|
769
841
|
children: [
|
|
770
842
|
{ h3: ["Operator Counter"] },
|
|
771
843
|
{ p: { children: ["Count: ", =/count] }},
|
|
@@ -784,39 +856,6 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
|
|
|
784
856
|
</code></pre>
|
|
785
857
|
</div>
|
|
786
858
|
|
|
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
859
|
|
|
821
860
|
<!-- ===== OPERATOR SYNTAX EXAMPLE ===== -->
|
|
822
861
|
<h2 id="operator-syntax">Operator Syntax</h2>
|
|
@@ -938,7 +977,7 @@ LightviewCDOM.activate(document.getElementById('myApp'));</code></pre>
|
|
|
938
977
|
|
|
939
978
|
<h3 id="api-hydrate">hydrate(object)</h3>
|
|
940
979
|
<p>
|
|
941
|
-
Converts <code
|
|
980
|
+
Converts <code>=/</code> prefixed strings into reactive computed signals.
|
|
942
981
|
</p>
|
|
943
982
|
<div class="code-block">
|
|
944
983
|
<pre><code>const config = { title: "Dashboard", total: "=/cart/items...price" };
|