lightview 2.2.2 → 2.3.4
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/cDOMIntro.md +279 -0
- package/docs/about.html +15 -12
- package/docs/api/computed.html +1 -1
- package/docs/api/effects.html +1 -1
- package/docs/api/elements.html +56 -25
- package/docs/api/enhance.html +1 -1
- package/docs/api/hypermedia.html +1 -1
- package/docs/api/index.html +1 -1
- package/docs/api/nav.html +28 -3
- package/docs/api/signals.html +1 -1
- package/docs/api/state.html +283 -85
- package/docs/assets/js/examplify.js +2 -1
- package/docs/cdom-nav.html +3 -2
- package/docs/cdom.html +383 -114
- package/jprx/README.md +112 -71
- package/jprx/helpers/state.js +21 -0
- package/jprx/package.json +1 -1
- package/jprx/parser.js +136 -86
- package/jprx/specs/expressions.json +71 -0
- package/jprx/specs/helpers.json +150 -0
- package/lightview-all.js +618 -431
- package/lightview-cdom.js +311 -605
- package/lightview-router.js +6 -0
- package/lightview-x.js +226 -54
- package/lightview.js +351 -42
- package/package.json +2 -1
- package/src/lightview-cdom.js +211 -315
- package/src/lightview-router.js +10 -0
- package/src/lightview-x.js +121 -1
- package/src/lightview.js +88 -16
- package/src/reactivity/signal.js +73 -29
- package/src/reactivity/state.js +84 -21
- package/tests/cdom/fixtures/helpers.cdomc +24 -24
- package/tests/cdom/helpers.test.js +28 -28
- package/tests/cdom/parser.test.js +39 -114
- package/tests/cdom/reactivity.test.js +32 -29
- package/tests/jprx/spec.test.js +99 -0
- package/tests/cdom/loader.test.js +0 -125
package/docs/api/state.html
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<script src="/lightview-router.js?base=/index.html"></script>
|
|
3
3
|
|
|
4
4
|
<div class="docs-layout">
|
|
5
|
-
<aside class="docs-sidebar" src="./nav.html"></aside>
|
|
5
|
+
<aside class="docs-sidebar" src="./nav.html" data-preserve-scroll="docs-nav"></aside>
|
|
6
6
|
|
|
7
7
|
<main class="docs-content">
|
|
8
8
|
<h1>State (Store)</h1>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
state tracks nested property changes automatically.
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
|
-
<h2>The
|
|
14
|
+
<h2 id="shortcomings">The Shortcomings of Signals</h2>
|
|
15
15
|
<pre><code>// Signals only react to reassignment
|
|
16
16
|
const user = signal({ name: 'Alice', age: 25 });
|
|
17
17
|
|
|
@@ -23,7 +23,7 @@ const items = signal([1, 2, 3]);
|
|
|
23
23
|
items.value.push(4); // ❌ Won't trigger updates!
|
|
24
24
|
items.value = [...items.value, 4]; // ✅ Works, but tedious</code></pre>
|
|
25
25
|
|
|
26
|
-
<h2>State to the Rescue</h2>
|
|
26
|
+
<h2 id="state-to-the-rescue">State to the Rescue</h2>
|
|
27
27
|
<pre><code>const { state } = LightviewX;
|
|
28
28
|
|
|
29
29
|
// Deep reactivity - mutations work!
|
|
@@ -38,7 +38,38 @@ items.push(4); // ✅ Triggers updates!
|
|
|
38
38
|
items[0] = 10; // ✅ Triggers updates!
|
|
39
39
|
items.sort(); // ✅ Triggers updates!</code></pre>
|
|
40
40
|
|
|
41
|
-
<h2>
|
|
41
|
+
<h2 id="state-function">State Function</h2>
|
|
42
|
+
<p>The <code>state</code> function is the primary initializer for reactive stores.</p>
|
|
43
|
+
<div class="code-block">
|
|
44
|
+
<pre><code>state(initialValue, nameOrOptions?)</code></pre>
|
|
45
|
+
</div>
|
|
46
|
+
<h3>Parameters</h3>
|
|
47
|
+
<ul>
|
|
48
|
+
<li><code>initialValue</code>: The object or array to be wrapped in a reactive proxy.</li>
|
|
49
|
+
<li><code>nameOrOptions</code> (Optional):
|
|
50
|
+
<ul>
|
|
51
|
+
<li>If a <strong>string</strong>: This becomes the <code>name</code> of the state for global
|
|
52
|
+
registration.</li>
|
|
53
|
+
<li>If an <strong>object</strong>: Supported keys include:
|
|
54
|
+
<ul>
|
|
55
|
+
<li><code>name</code>: A unique identifier for the state in the registry.</li>
|
|
56
|
+
<li><code>storage</code>: An object implementing the Storage interface (e.g.,
|
|
57
|
+
<code>localStorage</code>) to enable persistence.
|
|
58
|
+
</li>
|
|
59
|
+
<li><code>scope</code>: A DOM element or object to bind the state's visibility for up-tree
|
|
60
|
+
lookups.</li>
|
|
61
|
+
<li><code>schema</code>: A validation behavior (<code>"auto"</code>, <code>"dynamic"</code>,
|
|
62
|
+
<code>"polymorphic"</code>) or a formal registered JSON Schema name (see <a
|
|
63
|
+
href="#json-schema-lite">JSON Schema Lite</a> below).
|
|
64
|
+
</li>
|
|
65
|
+
</ul>
|
|
66
|
+
</li>
|
|
67
|
+
</ul>
|
|
68
|
+
</li>
|
|
69
|
+
</ul>
|
|
70
|
+
<p>See the examples in the sections below for detailed usage of these parameters.</p>
|
|
71
|
+
|
|
72
|
+
<h2 id="nested-objects">Nested Objects</h2>
|
|
42
73
|
<p>State tracks changes at any depth:</p>
|
|
43
74
|
<pre><code>const app = state({
|
|
44
75
|
user: {
|
|
@@ -58,33 +89,52 @@ app.user.profile.name = 'Bob';
|
|
|
58
89
|
app.user.profile.settings.theme = 'light';
|
|
59
90
|
app.items.push({ id: 1, text: 'Hello' });</code></pre>
|
|
60
91
|
|
|
61
|
-
<h2>In the UI</h2>
|
|
92
|
+
<h2 id="in-the-ui">In the UI</h2>
|
|
93
|
+
|
|
94
|
+
<div id="ui-example">
|
|
95
|
+
<pre><script>
|
|
96
|
+
examplify(document.currentScript.nextElementSibling, {
|
|
97
|
+
at: document.currentScript.parentElement,
|
|
98
|
+
scripts: ['/lightview.js', '/lightview-x.js'],
|
|
99
|
+
type: 'module',
|
|
100
|
+
height: '200px',
|
|
101
|
+
autoRun: true,
|
|
102
|
+
controls: false
|
|
103
|
+
});
|
|
104
|
+
</script><code>const { state } = LightviewX;
|
|
105
|
+
const { tags, $ } = Lightview;
|
|
106
|
+
const { div, ul, li, input, span, button } = tags;
|
|
62
107
|
|
|
63
|
-
|
|
64
|
-
<div class="code-example-preview" id="state-demo"></div>
|
|
65
|
-
<div class="code-example-code">
|
|
66
|
-
<pre><code>const todos = state([
|
|
108
|
+
const todos = state([
|
|
67
109
|
{ text: 'Learn Lightview', done: true },
|
|
68
110
|
{ text: 'Build app', done: false }
|
|
69
111
|
]);
|
|
70
112
|
|
|
71
|
-
div(
|
|
72
|
-
ul(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
113
|
+
const app = div({ style: 'padding: 1rem;' },
|
|
114
|
+
ul({ style: 'list-style: none; padding: 0; margin-bottom: 1rem;' },
|
|
115
|
+
() => todos.map((todo, i) =>
|
|
116
|
+
li({ style: 'display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem;' },
|
|
117
|
+
input({
|
|
118
|
+
type: 'checkbox',
|
|
119
|
+
checked: todo.done,
|
|
120
|
+
onchange: () => todos[i].done = !todos[i].done,
|
|
121
|
+
style: 'cursor: pointer;'
|
|
122
|
+
}),
|
|
123
|
+
span({
|
|
124
|
+
style: () => todo.done ? 'text-decoration: line-through; opacity: 0.6;' : ''
|
|
125
|
+
}, todo.text)
|
|
126
|
+
)
|
|
80
127
|
)
|
|
81
|
-
)
|
|
82
|
-
button({
|
|
83
|
-
)
|
|
84
|
-
|
|
128
|
+
),
|
|
129
|
+
button({
|
|
130
|
+
onclick: () => todos.push({ text: `Task ${todos.length + 1}`, done: false }),
|
|
131
|
+
}, '+ Add Task')
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
$('#example').content(app);</code></pre>
|
|
85
135
|
</div>
|
|
86
136
|
|
|
87
|
-
<h2>Array Methods</h2>
|
|
137
|
+
<h2 id="array-methods">Array Methods</h2>
|
|
88
138
|
<p>All mutating array methods are reactive:</p>
|
|
89
139
|
<pre><code>const items = state([1, 2, 3]);
|
|
90
140
|
|
|
@@ -96,8 +146,17 @@ items.splice(1, 1); // Remove at index
|
|
|
96
146
|
items.sort(); // Sort in place
|
|
97
147
|
items.reverse(); // Reverse in place
|
|
98
148
|
items.fill(0); // Fill with value</code></pre>
|
|
149
|
+
<h2 id="date-methods">Date Methods</h2>
|
|
150
|
+
<p>When a property is a <code>Date</code> object, all mutating methods are reactive. This includes all
|
|
151
|
+
<code>set*</code> methods (e.g. <code>setFullYear()</code>, <code>setDate()</code>, <code>setHours()</code>,
|
|
152
|
+
etc.):
|
|
153
|
+
</p>
|
|
154
|
+
<pre><code>const event = state({ date: new Date() });
|
|
155
|
+
|
|
156
|
+
// This will trigger UI updates
|
|
157
|
+
event.date.setFullYear(2025);</code></pre>
|
|
99
158
|
|
|
100
|
-
<h2>Named State</h2>
|
|
159
|
+
<h2 id="named-state">Named State</h2>
|
|
101
160
|
<p>
|
|
102
161
|
Like signals, you can name state objects for global access. This is especially useful for
|
|
103
162
|
shared application state:
|
|
@@ -114,7 +173,7 @@ const globalState = state.get('app');
|
|
|
114
173
|
// Get or create
|
|
115
174
|
const settings = state.get('settings', { notifications: true });</code></pre>
|
|
116
175
|
|
|
117
|
-
<h2>Stored State</h2>
|
|
176
|
+
<h2 id="stored-state">Stored State</h2>
|
|
118
177
|
<p>
|
|
119
178
|
You can store named state objects in Storage objects (e.g. sessionStorage or localStorage) for persistence.
|
|
120
179
|
It will be saved any time there is a change. Objects are automatically
|
|
@@ -130,83 +189,222 @@ const sameUser = state.get('user');
|
|
|
130
189
|
const score = state.get('user', {storage:sessionStorage, defaultValue:{name:'Guest', theme:'dark'}});</code></pre>
|
|
131
190
|
<p>Note: Manually updating the object in storage will not trigger updates.</p>
|
|
132
191
|
|
|
133
|
-
<h2>
|
|
192
|
+
<h2 id="schema-validation">Schema Validation</h2>
|
|
193
|
+
<p>
|
|
194
|
+
State objects can be validated and transformed using the <code>schema</code> option. This ensures data
|
|
195
|
+
integrity and can automatically coerce values to the expected types.
|
|
196
|
+
</p>
|
|
197
|
+
<pre><code>const user = state({ name: 'Alice', age: 25 }, { schema: 'auto' });
|
|
198
|
+
|
|
199
|
+
user.age = 26; // ✅ Works (same type)
|
|
200
|
+
user.age = '30'; // ❌ Throws: Type mismatch
|
|
201
|
+
user.status = 'active'; // ❌ Throws: Cannot add new property</code></pre>
|
|
202
|
+
|
|
203
|
+
<h3>Built-in Schema Behaviors</h3>
|
|
134
204
|
<table class="api-table">
|
|
135
205
|
<thead>
|
|
136
206
|
<tr>
|
|
137
|
-
<th>
|
|
138
|
-
<th>
|
|
207
|
+
<th>Behavior</th>
|
|
208
|
+
<th>Description</th>
|
|
139
209
|
</tr>
|
|
140
210
|
</thead>
|
|
141
211
|
<tbody>
|
|
142
212
|
<tr>
|
|
143
|
-
<td>
|
|
144
|
-
<td>
|
|
213
|
+
<td><code>"auto"</code></td>
|
|
214
|
+
<td>Infers a fixed schema from the initial value. Prevents adding new properties and enforces strict
|
|
215
|
+
type checking.</td>
|
|
145
216
|
</tr>
|
|
146
217
|
<tr>
|
|
147
|
-
<td>
|
|
148
|
-
<td>
|
|
218
|
+
<td><code>"dynamic"</code></td>
|
|
219
|
+
<td>Like <code>auto</code>, but allows the state object to grow with new properties.</td>
|
|
149
220
|
</tr>
|
|
150
221
|
<tr>
|
|
151
|
-
<td>
|
|
152
|
-
<td>
|
|
222
|
+
<td><code>"polymorphic"</code></td>
|
|
223
|
+
<td>Allows growth and automatically <strong>coerces</strong> values to match the initial type (e.g.,
|
|
224
|
+
setting "100" to a number property saves it as the number 100).</td>
|
|
153
225
|
</tr>
|
|
226
|
+
</tbody>
|
|
227
|
+
</table>
|
|
228
|
+
<pre><code>// Polymorphic coerces types automatically
|
|
229
|
+
const settings = state({ volume: 50, muted: false }, { schema: 'polymorphic' });
|
|
230
|
+
|
|
231
|
+
settings.volume = '75'; // ✅ Coerced to number 75
|
|
232
|
+
settings.muted = 'true'; // ✅ Coerced to boolean true
|
|
233
|
+
settings.newProp = 'ok'; // ✅ Allowed (dynamic growth)</code></pre>
|
|
234
|
+
|
|
235
|
+
<h3 id="json-schema-lite">JSON Schema Lite</h3>
|
|
236
|
+
<p>
|
|
237
|
+
When you load <code>lightview-x.js</code>, the schema engine is upgraded to a "JSON Schema Lite" validator.
|
|
238
|
+
It supports standard Draft 7 keywords while remaining incredibly lightweight.
|
|
239
|
+
</p>
|
|
240
|
+
|
|
241
|
+
<h4>Supported Keywords</h4>
|
|
242
|
+
<table class="api-table">
|
|
243
|
+
<thead>
|
|
154
244
|
<tr>
|
|
155
|
-
<
|
|
156
|
-
<
|
|
245
|
+
<th>Type</th>
|
|
246
|
+
<th>Keywords</th>
|
|
247
|
+
</tr>
|
|
248
|
+
</thead>
|
|
249
|
+
<tbody>
|
|
250
|
+
<tr>
|
|
251
|
+
<td><strong>String</strong></td>
|
|
252
|
+
<td><code>minLength</code>, <code>maxLength</code>, <code>pattern</code>,
|
|
253
|
+
<code>format: 'email'</code>
|
|
254
|
+
</td>
|
|
255
|
+
</tr>
|
|
256
|
+
<tr>
|
|
257
|
+
<td><strong>Number</strong></td>
|
|
258
|
+
<td><code>minimum</code>, <code>maximum</code>, <code>multipleOf</code></td>
|
|
259
|
+
</tr>
|
|
260
|
+
<tr>
|
|
261
|
+
<td><strong>Object</strong></td>
|
|
262
|
+
<td><code>required</code>, <code>properties</code>, <code>additionalProperties: false</code></td>
|
|
263
|
+
</tr>
|
|
264
|
+
<tr>
|
|
265
|
+
<td><strong>Array</strong></td>
|
|
266
|
+
<td><code>items</code>, <code>minItems</code>, <code>maxItems</code>, <code>uniqueItems</code></td>
|
|
267
|
+
</tr>
|
|
268
|
+
<tr>
|
|
269
|
+
<td><strong>General</strong></td>
|
|
270
|
+
<td><code>type</code>, <code>enum</code>, <code>const</code></td>
|
|
157
271
|
</tr>
|
|
158
272
|
</tbody>
|
|
159
273
|
</table>
|
|
160
274
|
|
|
161
|
-
<
|
|
275
|
+
<h4>Named Schemas</h4>
|
|
162
276
|
<p>
|
|
163
|
-
You can
|
|
277
|
+
You can register schemas globally and reference them by name. This encourages reuse and keeps your
|
|
278
|
+
<code>state</code> initializations clean.
|
|
164
279
|
</p>
|
|
165
|
-
<pre><code
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
280
|
+
<pre><code>// 1. Register a schema
|
|
281
|
+
Lightview.registerSchema('User', {
|
|
282
|
+
type: "object",
|
|
283
|
+
properties: {
|
|
284
|
+
username: { type: "string", minLength: 3 },
|
|
285
|
+
email: { type: "string", format: "email" }
|
|
286
|
+
},
|
|
287
|
+
required: ["username"]
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// 2. Use it by name
|
|
291
|
+
const user = state({ username: "alice" }, { schema: "User" });</code></pre>
|
|
292
|
+
|
|
293
|
+
<h3 id="transformations">Transformation Helpers</h3>
|
|
294
|
+
<p>
|
|
295
|
+
Unlike industry-standard validators (which only allow/disallow data), Lightview's engine supports
|
|
296
|
+
<strong>declarative transformations</strong>. This allows you to specify how data should be "cleaned" before
|
|
297
|
+
it hits the state.
|
|
298
|
+
</p>
|
|
299
|
+
<pre><code>const UserSchema = {
|
|
300
|
+
type: "object",
|
|
301
|
+
properties: {
|
|
302
|
+
username: {
|
|
303
|
+
type: "string",
|
|
304
|
+
transform: "lower" // Using a registered helper name
|
|
305
|
+
},
|
|
306
|
+
salary: {
|
|
307
|
+
type: "number",
|
|
308
|
+
transform: (v) => Math.round(v) // Using an inline function
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const user = state({ username: 'ALICE', salary: 50212.55 }, { schema: UserSchema });
|
|
314
|
+
// user.username is 'alice' (via "lower")
|
|
315
|
+
// user.salary is 50213 (via inline function)</code></pre>
|
|
316
|
+
|
|
317
|
+
<p>
|
|
318
|
+
When using a <strong>string</strong> for the transform (like <code>"lower"</code>), Lightview looks for a
|
|
319
|
+
matching function in the global registry. This requires that the helper has been registered via JPRX
|
|
320
|
+
(requires <code>lightview-cdom.js</code>) or manually in <code>Lightview.helpers</code>. For more details on
|
|
321
|
+
built-in
|
|
322
|
+
helpers, see the <a href="/docs/cdom.html">cDOM documentation</a>.
|
|
323
|
+
</p>
|
|
324
|
+
|
|
325
|
+
<div class="code-block info"
|
|
326
|
+
style="border-left: 4px solid #3b82f6; padding: 1rem; background: #eff6ff; margin-bottom: 2rem;">
|
|
327
|
+
<strong>🔍 Naming Tip:</strong> When using string names for transforms, use the <strong>bare helper
|
|
328
|
+
name</strong> (e.g., <code>"lower"</code>, <code>"round"</code>). You do <strong>not</strong> need a
|
|
329
|
+
leading <code>$</code>.
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<h4>Loading Helpers</h4>
|
|
333
|
+
<p>
|
|
334
|
+
When using the <code>transform</code> keyword, Lightview searches for the named function in the following
|
|
335
|
+
order:
|
|
336
|
+
</p>
|
|
337
|
+
<ol style="margin-bottom: 2rem;">
|
|
338
|
+
<li><strong>JPRX Helpers</strong>: Any helper registered via <code>registerHelper</code> (requires
|
|
339
|
+
<code>lightview-cdom.js</code>).
|
|
340
|
+
</li>
|
|
341
|
+
<li><strong>Public API</strong>: Functions attached to <code>Lightview.helpers</code>.</li>
|
|
342
|
+
<li><strong>Inline</strong>: You can also pass a function directly to the <code>transform</code> property.
|
|
343
|
+
</li>
|
|
344
|
+
</ol>
|
|
345
|
+
|
|
346
|
+
<div class="code-block info"
|
|
347
|
+
style="border-left: 4px solid #3b82f6; padding: 1rem; background: #eff6ff; margin-bottom: 2rem;">
|
|
348
|
+
<strong>💡 Note:</strong> To use JPRX helpers (like <code>math</code>, <code>string</code>, or
|
|
349
|
+
<code>array</code> helpers) for state transformations, you must ensure <code>lightview-cdom.js</code> is
|
|
350
|
+
loaded before your state is initialized.
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
<h3 id="lite-vs-full">Lite vs. Full Validators</h3>
|
|
354
|
+
<p>
|
|
355
|
+
Why use our "Lite" engine instead of a full validator like <strong>Ajv</strong>?
|
|
356
|
+
</p>
|
|
357
|
+
<table class="api-table">
|
|
358
|
+
<thead>
|
|
359
|
+
<tr>
|
|
360
|
+
<th>Feature</th>
|
|
361
|
+
<th>Lightview Lite</th>
|
|
362
|
+
<th>Full (Ajv, etc.)</th>
|
|
363
|
+
</tr>
|
|
364
|
+
</thead>
|
|
365
|
+
<tbody>
|
|
366
|
+
<tr>
|
|
367
|
+
<td><strong>Size</strong></td>
|
|
368
|
+
<td>~2KB (built-in)</td>
|
|
369
|
+
<td>~40KB+ (external)</td>
|
|
370
|
+
</tr>
|
|
371
|
+
<tr>
|
|
372
|
+
<td><strong>Transformation</strong></td>
|
|
373
|
+
<td>✅ First-class support</td>
|
|
374
|
+
<td>❌ Not supported (Validators only)</td>
|
|
375
|
+
</tr>
|
|
376
|
+
<tr>
|
|
377
|
+
<td><strong>Performance</strong></td>
|
|
378
|
+
<td>🚀 Optimized for UI cycles</td>
|
|
379
|
+
<td>Heavy overhead for simple checks</td>
|
|
380
|
+
</tr>
|
|
381
|
+
<tr>
|
|
382
|
+
<td><strong>Spec Compliance</strong></td>
|
|
383
|
+
<td>Basic Draft 7 (No <code>$ref</code>)</td>
|
|
384
|
+
<td>100% Full Specification</td>
|
|
385
|
+
</tr>
|
|
386
|
+
</tbody>
|
|
387
|
+
</table>
|
|
388
|
+
|
|
389
|
+
<h4 id="full-validator">Using a Full Validator</h4>
|
|
390
|
+
<p>
|
|
391
|
+
If you have complex enterprise schemas that require full specification compliance, you can swap out the
|
|
392
|
+
Lightview engine for any industry-standard validator:
|
|
393
|
+
</p>
|
|
394
|
+
<div class="code-block warning"
|
|
395
|
+
style="border-left: 4px solid #f59e0b; padding: 1rem; background: #fffbeb; margin-bottom: 2rem;">
|
|
396
|
+
<strong>⚠️ Warning:</strong> If you swap the validator, you will lose support for the declarative
|
|
397
|
+
<code>transform</code> keyword in your schemas unless your external validator also supports it.
|
|
398
|
+
</div>
|
|
399
|
+
<pre><code>import Ajv from "ajv";
|
|
400
|
+
const ajv = new Ajv();
|
|
401
|
+
|
|
402
|
+
// Swap the validation hook
|
|
403
|
+
Lightview.internals.hooks.validate = (value, schema) => {
|
|
404
|
+
const valid = ajv.validate(schema, value);
|
|
405
|
+
if (!valid) throw new Error(ajv.errorsText());
|
|
406
|
+
return true;
|
|
407
|
+
};</code></pre>
|
|
408
|
+
|
|
173
409
|
</main>
|
|
174
|
-
</div>
|
|
175
|
-
|
|
176
|
-
<script>
|
|
177
|
-
(function () {
|
|
178
|
-
const { tags } = Lightview;
|
|
179
|
-
const { state } = LightviewX;
|
|
180
|
-
const { div, ul, li, input, span, button } = tags;
|
|
181
|
-
|
|
182
|
-
const todos = state([
|
|
183
|
-
{ text: 'Learn Lightview', done: true },
|
|
184
|
-
{ text: 'Build something', done: false }
|
|
185
|
-
]);
|
|
186
|
-
|
|
187
|
-
const demo = div({ style: 'padding: 0.5rem;' },
|
|
188
|
-
ul({ style: 'list-style: none; padding: 0; margin: 0 0 1rem;' },
|
|
189
|
-
() => todos.map((todo, i) =>
|
|
190
|
-
li({ style: 'display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0;' },
|
|
191
|
-
input({
|
|
192
|
-
type: 'checkbox',
|
|
193
|
-
checked: todo.done,
|
|
194
|
-
onchange: () => todos[i].done = !todos[i].done,
|
|
195
|
-
style: 'cursor: pointer;'
|
|
196
|
-
}),
|
|
197
|
-
span({
|
|
198
|
-
style: () => `${todo.done ? 'text-decoration: line-through; opacity: 0.6;' : ''}`
|
|
199
|
-
}, todo.text)
|
|
200
|
-
)
|
|
201
|
-
)
|
|
202
|
-
),
|
|
203
|
-
button({
|
|
204
|
-
onclick: () => todos.push({ text: `Task ${todos.length + 1}`, done: false }),
|
|
205
|
-
style: 'padding: 0.5rem 1rem; cursor: pointer; background: var(--site-primary); color: white; border: none; border-radius: 6px;'
|
|
206
|
-
}, '+ Add Task')
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
const container = document.getElementById('state-demo');
|
|
210
|
-
if (container) container.appendChild(demo.domEl);
|
|
211
|
-
})();
|
|
212
|
-
</script>
|
|
410
|
+
</div>
|
|
@@ -3,7 +3,7 @@ var examplifyIdCounter = globalThis.examplifyIdCounter || 0;
|
|
|
3
3
|
globalThis.examplifyIdCounter = examplifyIdCounter;
|
|
4
4
|
|
|
5
5
|
globalThis.examplify = function examplify(target, options = {}) {
|
|
6
|
-
const { scripts, styles, modules, html, at, location = 'beforeBegin', type, height, minHeight = 100, maxHeight = Infinity, allowSameOrigin = false, useOrigin = null, language = 'js', autoRun = false } = options;
|
|
6
|
+
const { scripts, styles, modules, html, at, location = 'beforeBegin', type, height, minHeight = 100, maxHeight = Infinity, allowSameOrigin = false, useOrigin = null, language = 'js', autoRun = false, controls: showControls = true } = options;
|
|
7
7
|
const originalContent = target.textContent;
|
|
8
8
|
const autoResize = !height; // Auto-resize if no explicit height is provided
|
|
9
9
|
const iframeId = `examplify-${++examplifyIdCounter}`;
|
|
@@ -17,6 +17,7 @@ globalThis.examplify = function examplify(target, options = {}) {
|
|
|
17
17
|
const controls = document.createElement('div');
|
|
18
18
|
const editable = target.getAttribute('contenteditable') == 'true';
|
|
19
19
|
controls.className = 'examplify-controls';
|
|
20
|
+
if (!showControls) controls.style.display = 'none';
|
|
20
21
|
|
|
21
22
|
// Controls HTML
|
|
22
23
|
controls.innerHTML = `
|
package/docs/cdom-nav.html
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<div class="docs-nav-title">JPRX</div>
|
|
10
10
|
<a href="#JPRX" class="docs-nav-link">Introduction</a>
|
|
11
11
|
<a href="#JPRX-delimiters" class="docs-nav-link">Delimiters</a>
|
|
12
|
+
<a href="#JPRX-escaping" class="docs-nav-link">Escaping</a>
|
|
12
13
|
<a href="#JPRX-anatomy" class="docs-nav-link">Anatomy of a Path</a>
|
|
13
14
|
<a href="#JPRX-placeholders" class="docs-nav-link">Placeholders</a>
|
|
14
15
|
</div>
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
<div class="docs-nav-title">Concepts</div>
|
|
17
18
|
<a href="#comparison" class="docs-nav-link">Comparison to Excel</a>
|
|
18
19
|
<a href="#integration" class="docs-nav-link">Lightview Integration</a>
|
|
19
|
-
<a href="#
|
|
20
|
+
<a href="#dom-patches" class="docs-nav-link">Decentralized Layouts (=move)</a>
|
|
20
21
|
</div>
|
|
21
22
|
<div class="docs-nav-section">
|
|
22
23
|
<div class="docs-nav-title">Examples</div>
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
<a href="#helpers-datetime" class="docs-nav-link" style="font-size: 0.85em;">DateTime</a>
|
|
49
50
|
<a href="#helpers-lookup" class="docs-nav-link" style="font-size: 0.85em;">Lookup</a>
|
|
50
51
|
<a href="#helpers-mutation" class="docs-nav-link" style="font-size: 0.85em;">State Mutation</a>
|
|
51
|
-
<a href="#helpers-network" class="docs-nav-link" style="font-size: 0.85em;">Network</a>
|
|
52
|
+
<a href="#helpers-network" class="docs-nav-link" style="font-size: 0.85em;">Network (=mount)</a>
|
|
52
53
|
</div>
|
|
53
54
|
</div>
|
|
54
55
|
</div>
|