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
package/lightview.js
CHANGED
|
@@ -133,65 +133,10 @@
|
|
|
133
133
|
};
|
|
134
134
|
const getRegistry = () => _LV.registry;
|
|
135
135
|
const internals = _LV;
|
|
136
|
-
const stateCache = /* @__PURE__ */ new WeakMap();
|
|
137
|
-
const stateSignals = /* @__PURE__ */ new WeakMap();
|
|
138
|
-
const stateSchemas = /* @__PURE__ */ new WeakMap();
|
|
139
136
|
const { parents, schemas, hooks } = internals;
|
|
140
|
-
const validate = (target, prop, value, schema) => {
|
|
141
|
-
var _a, _b;
|
|
142
|
-
const current = target[prop];
|
|
143
|
-
const type = typeof current;
|
|
144
|
-
const isNew = !(prop in target);
|
|
145
|
-
let behavior = schema;
|
|
146
|
-
if (typeof schema === "object" && schema !== null) behavior = schema.type;
|
|
147
|
-
if (behavior === "auto" && isNew) throw new Error(`Lightview: Cannot add new property "${prop}" to fixed 'auto' state.`);
|
|
148
|
-
if (behavior === "polymorphic" || typeof behavior === "object" && (behavior == null ? void 0 : behavior.coerce)) {
|
|
149
|
-
if (type === "number") return Number(value);
|
|
150
|
-
if (type === "boolean") return Boolean(value);
|
|
151
|
-
if (type === "string") return String(value);
|
|
152
|
-
} else if (behavior === "auto" || behavior === "dynamic") {
|
|
153
|
-
if (!isNew && typeof value !== type) {
|
|
154
|
-
throw new Error(`Lightview: Type mismatch for "${prop}". Expected ${type}, got ${typeof value}.`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (typeof schema === "object" && schema !== null && schema.transform) {
|
|
158
|
-
const trans = schema.transform;
|
|
159
|
-
const transformFn = typeof trans === "function" ? trans : internals.helpers.get(trans) || ((_b = (_a = globalThis.Lightview) == null ? void 0 : _a.helpers) == null ? void 0 : _b[trans]);
|
|
160
|
-
if (transformFn) value = transformFn(value);
|
|
161
|
-
}
|
|
162
|
-
if (hooks.validate(value, schema) === false) {
|
|
163
|
-
throw new Error(`Lightview: Validation failed for "${prop}".`);
|
|
164
|
-
}
|
|
165
|
-
return value;
|
|
166
|
-
};
|
|
167
137
|
const protoMethods = (proto, test) => Object.getOwnPropertyNames(proto).filter((k) => typeof proto[k] === "function" && test(k));
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const ARRAY_TRACKING = [
|
|
171
|
-
"map",
|
|
172
|
-
"forEach",
|
|
173
|
-
"filter",
|
|
174
|
-
"find",
|
|
175
|
-
"findIndex",
|
|
176
|
-
"some",
|
|
177
|
-
"every",
|
|
178
|
-
"reduce",
|
|
179
|
-
"reduceRight",
|
|
180
|
-
"includes",
|
|
181
|
-
"indexOf",
|
|
182
|
-
"lastIndexOf",
|
|
183
|
-
"join",
|
|
184
|
-
"slice",
|
|
185
|
-
"concat",
|
|
186
|
-
"flat",
|
|
187
|
-
"flatMap",
|
|
188
|
-
"at",
|
|
189
|
-
"entries",
|
|
190
|
-
"keys",
|
|
191
|
-
"values"
|
|
192
|
-
];
|
|
193
|
-
const ARRAY_MUTATING = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse", "fill", "copyWithin"];
|
|
194
|
-
const ARRAY_ITERATION = ["map", "forEach", "filter", "find", "findIndex", "some", "every", "flatMap"];
|
|
138
|
+
protoMethods(Date.prototype, (k) => /^(to|get|valueOf)/.test(k));
|
|
139
|
+
protoMethods(Date.prototype, (k) => /^set/.test(k));
|
|
195
140
|
const getOrSet = (map, key, factory) => {
|
|
196
141
|
let v = map.get(key);
|
|
197
142
|
if (!v) {
|
|
@@ -200,169 +145,6 @@
|
|
|
200
145
|
}
|
|
201
146
|
return v;
|
|
202
147
|
};
|
|
203
|
-
const proxyGet = (target, prop, receiver, signals) => {
|
|
204
|
-
if (prop === "__parent__") return parents.get(receiver);
|
|
205
|
-
if (!signals.has(prop)) {
|
|
206
|
-
signals.set(prop, signal(Reflect.get(target, prop, receiver)));
|
|
207
|
-
}
|
|
208
|
-
const signal$1 = signals.get(prop);
|
|
209
|
-
const val = signal$1.value;
|
|
210
|
-
if (typeof val === "object" && val !== null) {
|
|
211
|
-
const childProxy = state(val);
|
|
212
|
-
parents.set(childProxy, receiver);
|
|
213
|
-
return childProxy;
|
|
214
|
-
}
|
|
215
|
-
return val;
|
|
216
|
-
};
|
|
217
|
-
const proxySet = (target, prop, value, receiver, signals) => {
|
|
218
|
-
const schema = stateSchemas.get(receiver);
|
|
219
|
-
const validatedValue = schema ? validate(target, prop, value, schema) : value;
|
|
220
|
-
if (!signals.has(prop)) {
|
|
221
|
-
signals.set(prop, signal(Reflect.get(target, prop, receiver)));
|
|
222
|
-
}
|
|
223
|
-
const success = Reflect.set(target, prop, validatedValue, receiver);
|
|
224
|
-
const signal$1 = signals.get(prop);
|
|
225
|
-
if (success && signal$1) signal$1.value = validatedValue;
|
|
226
|
-
return success;
|
|
227
|
-
};
|
|
228
|
-
const createSpecialProxy = (obj, monitor, trackingProps = []) => {
|
|
229
|
-
const signals = getOrSet(stateSignals, obj, () => /* @__PURE__ */ new Map());
|
|
230
|
-
if (!signals.has(monitor)) {
|
|
231
|
-
const initialValue = typeof obj[monitor] === "function" ? obj[monitor].call(obj) : obj[monitor];
|
|
232
|
-
signals.set(monitor, signal(initialValue));
|
|
233
|
-
}
|
|
234
|
-
const isDate = obj instanceof Date;
|
|
235
|
-
const isArray = Array.isArray(obj);
|
|
236
|
-
const trackingMethods = isDate ? DATE_TRACKING : isArray ? ARRAY_TRACKING : trackingProps;
|
|
237
|
-
const mutatingMethods = isDate ? DATE_MUTATING : isArray ? ARRAY_MUTATING : [];
|
|
238
|
-
return new Proxy(obj, {
|
|
239
|
-
get(target, prop, receiver) {
|
|
240
|
-
if (prop === "__parent__") return parents.get(receiver);
|
|
241
|
-
const value = target[prop];
|
|
242
|
-
if (typeof value === "function") {
|
|
243
|
-
const isTracking = trackingMethods.includes(prop);
|
|
244
|
-
const isMutating = mutatingMethods.includes(prop);
|
|
245
|
-
return function(...args) {
|
|
246
|
-
if (isTracking) {
|
|
247
|
-
const sig = signals.get(monitor);
|
|
248
|
-
if (sig) void sig.value;
|
|
249
|
-
}
|
|
250
|
-
const startValue = typeof target[monitor] === "function" ? target[monitor].call(target) : target[monitor];
|
|
251
|
-
if (isArray && ARRAY_ITERATION.includes(prop) && typeof args[0] === "function") {
|
|
252
|
-
const originalCallback = args[0];
|
|
253
|
-
args[0] = function(element2, index, array) {
|
|
254
|
-
const wrappedElement = typeof element2 === "object" && element2 !== null ? state(element2) : element2;
|
|
255
|
-
if (wrappedElement && typeof wrappedElement === "object") {
|
|
256
|
-
parents.set(wrappedElement, receiver);
|
|
257
|
-
}
|
|
258
|
-
return originalCallback.call(this, wrappedElement, index, array);
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
const result = value.apply(target, args);
|
|
262
|
-
const endValue = typeof target[monitor] === "function" ? target[monitor].call(target) : target[monitor];
|
|
263
|
-
if (startValue !== endValue || isMutating) {
|
|
264
|
-
const sig = signals.get(monitor);
|
|
265
|
-
if (sig && sig.value !== endValue) {
|
|
266
|
-
sig.value = endValue;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return result;
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
if (prop === monitor) {
|
|
273
|
-
const sig = signals.get(monitor);
|
|
274
|
-
return sig ? sig.value : Reflect.get(target, prop, receiver);
|
|
275
|
-
}
|
|
276
|
-
if (isArray && !isNaN(parseInt(prop))) {
|
|
277
|
-
const monitorSig = signals.get(monitor);
|
|
278
|
-
if (monitorSig) void monitorSig.value;
|
|
279
|
-
}
|
|
280
|
-
return proxyGet(target, prop, receiver, signals);
|
|
281
|
-
},
|
|
282
|
-
set(target, prop, value, receiver) {
|
|
283
|
-
if (prop === monitor) {
|
|
284
|
-
const success = Reflect.set(target, prop, value, receiver);
|
|
285
|
-
if (success) {
|
|
286
|
-
const sig = signals.get(monitor);
|
|
287
|
-
if (sig) sig.value = value;
|
|
288
|
-
}
|
|
289
|
-
return success;
|
|
290
|
-
}
|
|
291
|
-
return proxySet(target, prop, value, receiver, signals);
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
};
|
|
295
|
-
const state = (obj, optionsOrName) => {
|
|
296
|
-
if (typeof obj !== "object" || obj === null) return obj;
|
|
297
|
-
const name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
|
|
298
|
-
const storage = optionsOrName == null ? void 0 : optionsOrName.storage;
|
|
299
|
-
const scope = optionsOrName == null ? void 0 : optionsOrName.scope;
|
|
300
|
-
const schema = optionsOrName == null ? void 0 : optionsOrName.schema;
|
|
301
|
-
if (name && storage) {
|
|
302
|
-
try {
|
|
303
|
-
const item = storage.getItem(name);
|
|
304
|
-
if (item) {
|
|
305
|
-
const loaded = JSON.parse(item);
|
|
306
|
-
Array.isArray(obj) && Array.isArray(loaded) ? (obj.length = 0, obj.push(...loaded)) : Object.assign(obj, loaded);
|
|
307
|
-
}
|
|
308
|
-
} catch (e) {
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
let proxy = stateCache.get(obj);
|
|
312
|
-
if (!proxy) {
|
|
313
|
-
const isArray = Array.isArray(obj), isDate = obj instanceof Date;
|
|
314
|
-
const isSpecial = isArray || isDate;
|
|
315
|
-
const monitor = isArray ? "length" : isDate ? "getTime" : null;
|
|
316
|
-
if (isSpecial || !(obj instanceof RegExp || obj instanceof Map || obj instanceof Set || obj instanceof WeakMap || obj instanceof WeakSet)) {
|
|
317
|
-
proxy = isSpecial ? createSpecialProxy(obj, monitor) : new Proxy(obj, {
|
|
318
|
-
get(t, p, r) {
|
|
319
|
-
if (p === "__parent__") return parents.get(r);
|
|
320
|
-
return proxyGet(t, p, r, getOrSet(stateSignals, t, () => /* @__PURE__ */ new Map()));
|
|
321
|
-
},
|
|
322
|
-
set(t, p, v, r) {
|
|
323
|
-
return proxySet(t, p, v, r, getOrSet(stateSignals, t, () => /* @__PURE__ */ new Map()));
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
stateCache.set(obj, proxy);
|
|
327
|
-
} else return obj;
|
|
328
|
-
}
|
|
329
|
-
if (schema) stateSchemas.set(proxy, schema);
|
|
330
|
-
if (name && storage) {
|
|
331
|
-
effect(() => {
|
|
332
|
-
try {
|
|
333
|
-
storage.setItem(name, JSON.stringify(proxy));
|
|
334
|
-
} catch (e) {
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
if (name) {
|
|
339
|
-
const registry2 = scope && typeof scope === "object" ? internals.localRegistries.get(scope) || internals.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : getRegistry();
|
|
340
|
-
if (registry2 && registry2.has(name) && registry2.get(name) !== proxy) {
|
|
341
|
-
throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
|
|
342
|
-
}
|
|
343
|
-
if (registry2) registry2.set(name, proxy);
|
|
344
|
-
const futures = internals.futureSignals.get(name);
|
|
345
|
-
if (futures) {
|
|
346
|
-
futures.forEach((resolve) => resolve(proxy));
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return proxy;
|
|
350
|
-
};
|
|
351
|
-
const getState = (name, defaultValueOrOptions) => {
|
|
352
|
-
const options = typeof defaultValueOrOptions === "object" && defaultValueOrOptions !== null ? defaultValueOrOptions : { defaultValue: defaultValueOrOptions };
|
|
353
|
-
const { scope, defaultValue } = options;
|
|
354
|
-
const existing = lookup(name, scope);
|
|
355
|
-
if (existing) return existing;
|
|
356
|
-
if (defaultValue !== void 0) return state(defaultValue, { name, scope });
|
|
357
|
-
const future = signal(void 0);
|
|
358
|
-
const handler = (realState) => {
|
|
359
|
-
future.value = realState;
|
|
360
|
-
};
|
|
361
|
-
if (!internals.futureSignals.has(name)) internals.futureSignals.set(name, /* @__PURE__ */ new Set());
|
|
362
|
-
internals.futureSignals.get(name).add(handler);
|
|
363
|
-
return future;
|
|
364
|
-
};
|
|
365
|
-
state.get = getState;
|
|
366
148
|
const core = {
|
|
367
149
|
get currentEffect() {
|
|
368
150
|
return (globalThis.__LIGHTVIEW_INTERNALS__ || (globalThis.__LIGHTVIEW_INTERNALS__ = {})).currentEffect;
|
|
@@ -404,9 +186,9 @@
|
|
|
404
186
|
});
|
|
405
187
|
};
|
|
406
188
|
const trackEffect = (node, effectFn) => {
|
|
407
|
-
const
|
|
408
|
-
if (!
|
|
409
|
-
|
|
189
|
+
const state = getOrSet(nodeState, node, nodeStateFactory);
|
|
190
|
+
if (!state.effects) state.effects = [];
|
|
191
|
+
state.effects.push(effectFn);
|
|
410
192
|
};
|
|
411
193
|
const SHADOW_DOM_MARKER = Symbol("lightview.shadowDOM");
|
|
412
194
|
const createShadowDOMMarker = (attributes, children) => ({
|
|
@@ -458,6 +240,7 @@
|
|
|
458
240
|
tag,
|
|
459
241
|
attributes,
|
|
460
242
|
children,
|
|
243
|
+
isProxy: true,
|
|
461
244
|
get domEl() {
|
|
462
245
|
return domNode;
|
|
463
246
|
}
|
|
@@ -466,6 +249,10 @@
|
|
|
466
249
|
domToElement.set(domNode, proxy);
|
|
467
250
|
return proxy;
|
|
468
251
|
};
|
|
252
|
+
const someRecursive = (item, predicate) => {
|
|
253
|
+
if (Array.isArray(item)) return item.some((i) => someRecursive(i, predicate));
|
|
254
|
+
return predicate(item);
|
|
255
|
+
};
|
|
469
256
|
const element = (tag, attributes = {}, children = []) => {
|
|
470
257
|
if (customTags[tag]) tag = customTags[tag];
|
|
471
258
|
if (typeof tag === "function") {
|
|
@@ -486,38 +273,56 @@
|
|
|
486
273
|
}
|
|
487
274
|
};
|
|
488
275
|
const update = () => {
|
|
489
|
-
const
|
|
490
|
-
const
|
|
276
|
+
const bits = [];
|
|
277
|
+
const walk = (c) => {
|
|
278
|
+
if (Array.isArray(c)) {
|
|
279
|
+
for (let i = 0; i < c.length; i++) walk(c[i]);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
491
282
|
const val = typeof c === "function" ? c() : c;
|
|
492
|
-
if (val && typeof val === "object" && val.domEl)
|
|
493
|
-
|
|
494
|
-
}
|
|
283
|
+
if (val && typeof val === "object" && val.domEl) bits.push(val.domEl.textContent);
|
|
284
|
+
else bits.push(val === null || val === void 0 ? "" : String(val));
|
|
285
|
+
};
|
|
286
|
+
walk(el.children);
|
|
495
287
|
domNode2.textContent = bits.join(" ");
|
|
496
288
|
};
|
|
497
|
-
const
|
|
289
|
+
const proxy = new Proxy(el, {
|
|
498
290
|
set(target, prop, value) {
|
|
499
291
|
target[prop] = value;
|
|
500
292
|
if (prop === "children") update();
|
|
501
293
|
return true;
|
|
502
294
|
}
|
|
503
295
|
});
|
|
504
|
-
const hasReactive = children
|
|
296
|
+
const hasReactive = someRecursive(children, (c) => typeof c === "function");
|
|
505
297
|
if (hasReactive) {
|
|
506
298
|
const runner = effect(update);
|
|
507
299
|
trackEffect(domNode2, runner);
|
|
508
300
|
}
|
|
509
301
|
update();
|
|
510
|
-
return
|
|
302
|
+
return proxy;
|
|
511
303
|
}
|
|
512
304
|
const isSVG = tag.toLowerCase() === "svg";
|
|
513
305
|
const wasInSVG = inSVG;
|
|
514
306
|
if (isSVG) inSVG = true;
|
|
515
307
|
const domNode = inSVG ? document.createElementNS("http://www.w3.org/2000/svg", tag) : document.createElement(tag);
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
308
|
+
const hasReactiveAttr = Object.values(attributes).some((v) => typeof v === "function");
|
|
309
|
+
const hasReactiveChild = someRecursive(children, (c) => typeof c === "function" || c && c.isProxy);
|
|
310
|
+
if (hasReactiveAttr || hasReactiveChild) {
|
|
311
|
+
const proxy = wrapDomElement(domNode, tag, attributes, children);
|
|
312
|
+
proxy.attributes = attributes;
|
|
313
|
+
proxy.children = children;
|
|
314
|
+
if (isSVG) inSVG = wasInSVG;
|
|
315
|
+
return proxy;
|
|
316
|
+
}
|
|
317
|
+
makeReactiveAttributes(attributes, domNode);
|
|
318
|
+
setupChildren(children, domNode);
|
|
519
319
|
if (isSVG) inSVG = wasInSVG;
|
|
520
|
-
return
|
|
320
|
+
return {
|
|
321
|
+
tag,
|
|
322
|
+
attributes,
|
|
323
|
+
children,
|
|
324
|
+
domEl: domNode
|
|
325
|
+
};
|
|
521
326
|
};
|
|
522
327
|
const processComponentResult = (result) => {
|
|
523
328
|
if (!result) return null;
|
|
@@ -526,7 +331,7 @@
|
|
|
526
331
|
}
|
|
527
332
|
if (result.domEl) return result;
|
|
528
333
|
const type = typeof result;
|
|
529
|
-
if (type === "object" && result
|
|
334
|
+
if (type === "object" && result && result.nodeType === 1) {
|
|
530
335
|
return wrapDomElement(result, result.tagName.toLowerCase(), {}, []);
|
|
531
336
|
}
|
|
532
337
|
if (type === "object" && result instanceof String) {
|
|
@@ -538,7 +343,7 @@
|
|
|
538
343
|
const template = document.createElement("template");
|
|
539
344
|
template.innerHTML = result.trim();
|
|
540
345
|
const content = template.content;
|
|
541
|
-
if (content.childNodes.length === 1 && content.firstChild
|
|
346
|
+
if (content.childNodes.length === 1 && content.firstChild && content.firstChild.nodeType === 1) {
|
|
542
347
|
const el = content.firstChild;
|
|
543
348
|
return wrapDomElement(el, el.tagName.toLowerCase(), {}, []);
|
|
544
349
|
} else {
|
|
@@ -583,25 +388,26 @@
|
|
|
583
388
|
domNode.setAttribute(key, value);
|
|
584
389
|
}
|
|
585
390
|
};
|
|
586
|
-
const makeReactiveAttributes = (attributes, domNode) => {
|
|
391
|
+
const makeReactiveAttributes = (attributes = {}, domNode) => {
|
|
587
392
|
const reactiveAttrs = {};
|
|
588
|
-
for (
|
|
589
|
-
|
|
393
|
+
for (const key in attributes) {
|
|
394
|
+
const value = attributes[key];
|
|
395
|
+
const type = typeof value;
|
|
396
|
+
if (value && type === "object" && value.__xpath__ && value.__static__) {
|
|
590
397
|
domNode.setAttribute(`data-xpath-${key}`, value.__xpath__);
|
|
591
398
|
reactiveAttrs[key] = value;
|
|
592
399
|
continue;
|
|
593
400
|
}
|
|
594
401
|
if (key === "onmount" || key === "onunmount") {
|
|
595
|
-
const
|
|
596
|
-
|
|
402
|
+
const state = getOrSet(nodeState, domNode, nodeStateFactory);
|
|
403
|
+
state[key] = value;
|
|
597
404
|
if (key === "onmount" && domNode.isConnected) {
|
|
598
405
|
value(domNode);
|
|
599
406
|
}
|
|
600
407
|
} else if (key.startsWith("on")) {
|
|
601
|
-
if (
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
} else if (typeof value === "string") {
|
|
408
|
+
if (type === "function") {
|
|
409
|
+
domNode.addEventListener(key.slice(2).toLowerCase(), value);
|
|
410
|
+
} else if (type === "string") {
|
|
605
411
|
domNode.setAttribute(key, value);
|
|
606
412
|
}
|
|
607
413
|
reactiveAttrs[key] = value;
|
|
@@ -610,7 +416,8 @@
|
|
|
610
416
|
if (processed !== void 0) {
|
|
611
417
|
reactiveAttrs[key] = processed;
|
|
612
418
|
} else if (key === "style") {
|
|
613
|
-
|
|
419
|
+
for (const styleKey in entries) {
|
|
420
|
+
const styleValue = entries[styleKey];
|
|
614
421
|
if (typeof styleValue === "function") {
|
|
615
422
|
const runner = effect(() => {
|
|
616
423
|
domNode.style[styleKey] = styleValue();
|
|
@@ -619,13 +426,13 @@
|
|
|
619
426
|
} else {
|
|
620
427
|
domNode.style[styleKey] = styleValue;
|
|
621
428
|
}
|
|
622
|
-
}
|
|
429
|
+
}
|
|
623
430
|
reactiveAttrs[key] = value;
|
|
624
431
|
} else {
|
|
625
432
|
setAttributeValue(domNode, key, value);
|
|
626
433
|
reactiveAttrs[key] = value;
|
|
627
434
|
}
|
|
628
|
-
} else if (
|
|
435
|
+
} else if (type === "function") {
|
|
629
436
|
const runner = effect(() => {
|
|
630
437
|
const result = value();
|
|
631
438
|
if (key === "style" && typeof result === "object") {
|
|
@@ -649,21 +456,24 @@
|
|
|
649
456
|
}
|
|
650
457
|
const childElements = [];
|
|
651
458
|
const isSpecialElement = targetNode.tagName && (targetNode.tagName.toLowerCase() === "script" || targetNode.tagName.toLowerCase() === "style");
|
|
652
|
-
const
|
|
653
|
-
|
|
459
|
+
const walk = (child) => {
|
|
460
|
+
if (Array.isArray(child)) {
|
|
461
|
+
for (let i = 0; i < child.length; i++) walk(child[i]);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (child === null || child === void 0) return;
|
|
654
465
|
if (Lightview.hooks.processChild && !isSpecialElement) {
|
|
655
466
|
child = Lightview.hooks.processChild(child) ?? child;
|
|
656
467
|
}
|
|
657
|
-
if (isShadowDOMMarker(child)) {
|
|
658
|
-
if (targetNode instanceof ShadowRoot) {
|
|
659
|
-
console.warn("Lightview: Cannot nest shadowDOM inside another shadowDOM");
|
|
660
|
-
continue;
|
|
661
|
-
}
|
|
662
|
-
processShadowDOM(child, targetNode);
|
|
663
|
-
continue;
|
|
664
|
-
}
|
|
665
468
|
const type = typeof child;
|
|
666
|
-
if (type === "
|
|
469
|
+
if (child && type === "object" && child.tag) {
|
|
470
|
+
const childEl = child.domEl ? child : element(child.tag, child.attributes || {}, child.children || []);
|
|
471
|
+
targetNode.appendChild(childEl.domEl);
|
|
472
|
+
childElements.push(childEl);
|
|
473
|
+
} else if (["string", "number", "boolean", "symbol"].includes(type) || child && type === "object" && child instanceof String) {
|
|
474
|
+
targetNode.appendChild(document.createTextNode(child));
|
|
475
|
+
childElements.push(child);
|
|
476
|
+
} else if (type === "function") {
|
|
667
477
|
const startMarker = document.createComment("lv:s");
|
|
668
478
|
const endMarker = document.createComment("lv:e");
|
|
669
479
|
targetNode.appendChild(startMarker);
|
|
@@ -692,17 +502,9 @@
|
|
|
692
502
|
runner = effect(update);
|
|
693
503
|
trackEffect(startMarker, runner);
|
|
694
504
|
childElements.push(child);
|
|
695
|
-
} else if (child && typeof child === "object" && child.__xpath__ && child.__static__) {
|
|
696
|
-
const textNode = document.createTextNode("");
|
|
697
|
-
textNode.__xpathExpr = child.__xpath__;
|
|
698
|
-
targetNode.appendChild(textNode);
|
|
699
|
-
childElements.push(child);
|
|
700
|
-
} else if (["string", "number", "boolean", "symbol"].includes(type) || child && type === "object" && child instanceof String) {
|
|
701
|
-
targetNode.appendChild(document.createTextNode(child));
|
|
702
|
-
childElements.push(child);
|
|
703
505
|
} else if (child instanceof Node) {
|
|
704
506
|
const node = child.domEl || child;
|
|
705
|
-
if (node
|
|
507
|
+
if (node.nodeType === 1) {
|
|
706
508
|
const wrapped = wrapDomElement(node, node.tagName.toLowerCase());
|
|
707
509
|
targetNode.appendChild(node);
|
|
708
510
|
childElements.push(wrapped);
|
|
@@ -710,12 +512,20 @@
|
|
|
710
512
|
targetNode.appendChild(node);
|
|
711
513
|
childElements.push(child);
|
|
712
514
|
}
|
|
713
|
-
} else if (child
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
515
|
+
} else if (isShadowDOMMarker(child)) {
|
|
516
|
+
if (targetNode instanceof ShadowRoot) {
|
|
517
|
+
console.warn("Lightview: Cannot nest shadowDOM inside another shadowDOM");
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
processShadowDOM(child, targetNode);
|
|
521
|
+
} else if (child && typeof child === "object" && child.__xpath__ && child.__static__) {
|
|
522
|
+
const textNode = document.createTextNode("");
|
|
523
|
+
textNode.__xpathExpr = child.__xpath__;
|
|
524
|
+
targetNode.appendChild(textNode);
|
|
525
|
+
childElements.push(child);
|
|
717
526
|
}
|
|
718
|
-
}
|
|
527
|
+
};
|
|
528
|
+
walk(children);
|
|
719
529
|
return childElements;
|
|
720
530
|
};
|
|
721
531
|
const setupChildrenInTarget = (children, targetNode) => {
|
|
@@ -727,7 +537,7 @@
|
|
|
727
537
|
const enhance = (selectorOrNode, options = {}) => {
|
|
728
538
|
const domNode = typeof selectorOrNode === "string" ? document.querySelector(selectorOrNode) : selectorOrNode;
|
|
729
539
|
const node = domNode.domEl || domNode;
|
|
730
|
-
if (!
|
|
540
|
+
if (!node || node.nodeType !== 1) return null;
|
|
731
541
|
const tagName = node.tagName.toLowerCase();
|
|
732
542
|
let el = domToElement.get(node);
|
|
733
543
|
if (!el) {
|
|
@@ -819,8 +629,6 @@
|
|
|
819
629
|
}
|
|
820
630
|
});
|
|
821
631
|
const Lightview = {
|
|
822
|
-
state,
|
|
823
|
-
getState,
|
|
824
632
|
registerSchema: (name, definition) => internals.schemas.set(name, definition),
|
|
825
633
|
signal,
|
|
826
634
|
get: signal.get,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lightview",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "2.4.7",
|
|
4
|
+
"description": "A reactive UI library with features of Bau, Juris, and HTMX plus safe LLM UI generation",
|
|
5
5
|
"main": "lightview.js",
|
|
6
6
|
"workspaces": [
|
|
7
7
|
"jprx"
|
package/src/lightview-cdom.js
CHANGED
|
@@ -432,13 +432,9 @@ const resolveTextNodeXPath = (node) => {
|
|
|
432
432
|
try {
|
|
433
433
|
validateXPath(xpath);
|
|
434
434
|
const doc = globalThis.document || node.ownerDocument;
|
|
435
|
-
// Use the parent node (the element) as the context for evaluation
|
|
436
|
-
// This avoids errors in browsers that don't support Text nodes as context nodes
|
|
437
|
-
// and keeps evaluation consistent with attributes.
|
|
438
|
-
const contextNode = node.parentNode || node;
|
|
439
435
|
const result = doc.evaluate(
|
|
440
436
|
xpath,
|
|
441
|
-
|
|
437
|
+
node,
|
|
442
438
|
null,
|
|
443
439
|
XPathResult.STRING_TYPE,
|
|
444
440
|
null
|