lightview 1.3.1-b → 1.4.0-b
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/README.md +2 -2
- package/directives.html +50 -53
- package/lightview.js +108 -126
- package/package.json +6 -3
- package/remote.html +2 -1
- package/remoteform.html +60 -32
- package/scratch.html +69 -0
- package/xor.html +3 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# lightview v1.
|
|
1
|
+
# lightview v1.4.0b (BETA)
|
|
2
2
|
|
|
3
|
-
Small, simple, powerful web UI creation ...
|
|
3
|
+
Small, simple, powerful web UI and micro front end creation ...
|
|
4
4
|
|
|
5
5
|
Great ideas from Svelte, React, Vue and Riot combined into one small tool: < 6K (minified/gzipped).
|
|
6
6
|
|
package/directives.html
CHANGED
|
@@ -7,65 +7,57 @@
|
|
|
7
7
|
|
|
8
8
|
<body>
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
<p>
|
|
12
|
-
Show: <input type="checkbox"
|
|
10
|
+
<div style="margin:20px">
|
|
11
|
+
<p>
|
|
12
|
+
Show: <input type="checkbox" value="${on}">
|
|
13
13
|
<div l-if="${on}">
|
|
14
|
-
|
|
14
|
+
Now you've done it. You've exposed me.
|
|
15
15
|
</div>
|
|
16
|
-
</p>
|
|
17
|
-
<p>
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
</
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<
|
|
52
|
-
</
|
|
53
|
-
<ul l-for:entries:entry="${children}">
|
|
54
|
-
<li>${entry[0]}:${entry[1]}</li>
|
|
55
|
-
</ul>
|
|
56
|
-
|
|
57
|
-
Variable Values
|
|
58
|
-
<p id="variables"></p>
|
|
59
|
-
|
|
16
|
+
</p>
|
|
17
|
+
<p>
|
|
18
|
+
|
|
19
|
+
<p>
|
|
20
|
+
How would you like that burger?<br>
|
|
21
|
+
<select value="${options}" multiple>
|
|
22
|
+
<option>lettuce</option>
|
|
23
|
+
<option>tomato</option>
|
|
24
|
+
<option>cheese</option>
|
|
25
|
+
</select>
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
For (defaults to each)
|
|
30
|
+
<ul l-for:each="${options}">
|
|
31
|
+
<li>${index}:${item}</li>
|
|
32
|
+
</ul>
|
|
33
|
+
For Each
|
|
34
|
+
<ul l-for:each="${options}">
|
|
35
|
+
<li>${index}:${item}</li>
|
|
36
|
+
</ul>
|
|
37
|
+
For Values
|
|
38
|
+
<ul l-for:values="${options}">
|
|
39
|
+
<li>${item}:${index}</li>
|
|
40
|
+
</ul>
|
|
41
|
+
For Keys
|
|
42
|
+
<ul l-for:keys="${options}">
|
|
43
|
+
<li>${item}</li>
|
|
44
|
+
</ul>
|
|
45
|
+
For Entries
|
|
46
|
+
<ul l-for:entries="${options}">
|
|
47
|
+
<li>${item[0]}:${item[1]}</li>
|
|
48
|
+
</ul>
|
|
49
|
+
|
|
50
|
+
Variable Values
|
|
51
|
+
<p id="variables"></p>
|
|
52
|
+
</div>
|
|
60
53
|
<script type="lightview/module">
|
|
61
|
-
self.variables({on:boolean,
|
|
54
|
+
self.variables({on:boolean,options:Array},{reactive});
|
|
62
55
|
|
|
63
56
|
on = true;
|
|
64
|
-
|
|
65
|
-
children = ["John","Mary","Jane"];
|
|
66
|
-
options = ["tomato"];
|
|
57
|
+
options = ["lettuce"];
|
|
67
58
|
|
|
68
|
-
|
|
59
|
+
// demo instrumentation
|
|
60
|
+
const variableValues = () => {
|
|
69
61
|
const el = self.getElementById("variables");
|
|
70
62
|
while(el.lastElementChild) el.lastElementChild.remove();
|
|
71
63
|
self.getVariableNames().forEach((name) => {
|
|
@@ -73,7 +65,12 @@ addEventListener("change",()=> {
|
|
|
73
65
|
line.innerText = `${name} = ${JSON.stringify(self.getValue(name))}`;
|
|
74
66
|
el.appendChild(line);
|
|
75
67
|
});
|
|
68
|
+
};
|
|
69
|
+
variableValues();
|
|
70
|
+
addEventListener("change",()=> {
|
|
71
|
+
variableValues()
|
|
76
72
|
});
|
|
73
|
+
|
|
77
74
|
</script>
|
|
78
75
|
</body>
|
|
79
76
|
|
package/lightview.js
CHANGED
|
@@ -264,14 +264,14 @@ const {observe} = (() => {
|
|
|
264
264
|
nodes.push(node);
|
|
265
265
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
266
266
|
let skip;
|
|
267
|
+
if(node.getAttribute("type")==="radio") nodes.push(node);
|
|
267
268
|
[...node.attributes].forEach((attr) => {
|
|
268
269
|
if (attr.value.includes("${")) {
|
|
269
270
|
attr.template ||= attr.value;
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
if (attr.name.includes(":") || attr.name.startsWith("l-")) {
|
|
271
|
+
nodes.push(node);
|
|
272
|
+
} else if (attr.name.includes(":") || attr.name.startsWith("l-")) {
|
|
273
273
|
skip = attr.name.includes("l-for:");
|
|
274
|
-
|
|
274
|
+
nodes.push(node)
|
|
275
275
|
}
|
|
276
276
|
})
|
|
277
277
|
if (!skip && !node.shadowRoot) nodes.push(...getNodes(node));
|
|
@@ -281,7 +281,7 @@ const {observe} = (() => {
|
|
|
281
281
|
return nodes;
|
|
282
282
|
}
|
|
283
283
|
const resolveNode = (node, component) => {
|
|
284
|
-
if (node
|
|
284
|
+
if (node?.template) {
|
|
285
285
|
try {
|
|
286
286
|
const value = Function("context", "with(context) { return `" + node.template + "` }")(component.varsProxy);
|
|
287
287
|
node.nodeValue = value === "null" || value === "undefined" ? "" : value;
|
|
@@ -289,11 +289,11 @@ const {observe} = (() => {
|
|
|
289
289
|
if (!e.message.includes("defined")) throw e; // actually looking for undefined or not defined
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
|
-
return node
|
|
292
|
+
return node?.nodeValue;
|
|
293
293
|
}
|
|
294
|
-
const render = (
|
|
294
|
+
const render = (hasTemplate, render) => {
|
|
295
295
|
let observer;
|
|
296
|
-
if (
|
|
296
|
+
if (hasTemplate) {
|
|
297
297
|
if (observer) observer.cancel();
|
|
298
298
|
observer = observe(render)
|
|
299
299
|
} else {
|
|
@@ -314,37 +314,17 @@ const {observe} = (() => {
|
|
|
314
314
|
addListener(node,"click", anchorHandler);
|
|
315
315
|
})
|
|
316
316
|
}
|
|
317
|
-
const
|
|
318
|
-
[...node.querySelectorAll("input")].forEach((input) => bindInput(input, component))
|
|
319
|
-
}
|
|
320
|
-
const bindInput = (input, component) => {
|
|
321
|
-
let name = input.getAttribute("name"),
|
|
322
|
-
vname = input.getAttribute("l-bind") || name;
|
|
323
|
-
name ||= vname;
|
|
324
|
-
if (name) {
|
|
325
|
-
if (!input.hasAttribute("l-bind")) input.setAttribute("l-bind", vname)
|
|
317
|
+
const bindInput = (input, name, component) => {
|
|
326
318
|
const inputtype = input.tagName === "SELECT" ? "text" : input.getAttribute("type"),
|
|
327
319
|
type = input.tagName === "SELECT" && input.hasAttribute("multiple") ? Array : inputTypeToType(inputtype),
|
|
328
320
|
deflt = input.getAttribute("default"),
|
|
329
321
|
value = input.getAttribute("value");
|
|
330
|
-
let variable = component.vars[
|
|
322
|
+
let variable = component.vars[name] || {type};
|
|
331
323
|
if (type !== variable.type) {
|
|
332
324
|
if (variable.type === "any" || variable.type === "unknown") variable.type = type;
|
|
333
|
-
else throw new TypeError(`Attempt to bind <input name="${name}" type="${type}"> to variable ${
|
|
325
|
+
else throw new TypeError(`Attempt to bind <input name="${name}" type="${type}"> to variable ${name}:${variable.type}`)
|
|
334
326
|
}
|
|
335
|
-
component.variables({[
|
|
336
|
-
variable = component.vars[vname];
|
|
337
|
-
//if (value || deflt) {
|
|
338
|
-
if (inputtype !== "radio") {
|
|
339
|
-
if (value && !value.includes("${")) {
|
|
340
|
-
variable.value = coerce(value, type);
|
|
341
|
-
//input.setAttribute("value", `\${${name}}`);
|
|
342
|
-
} else if (deflt && !deflt.includes("${")) {
|
|
343
|
-
variable.value = coerce(deflt, type);
|
|
344
|
-
//input.setAttribute("default", `\${${name}}`);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
//}
|
|
327
|
+
component.variables({[name]: type});
|
|
348
328
|
addListener(input,"change", (event) => {
|
|
349
329
|
event.stopImmediatePropagation();
|
|
350
330
|
const target = event.target;
|
|
@@ -354,12 +334,18 @@ const {observe} = (() => {
|
|
|
354
334
|
} else if (target.tagName === "SELECT") {
|
|
355
335
|
if (target.hasAttribute("multiple")) {
|
|
356
336
|
value = [...target.querySelectorAll("option")]
|
|
357
|
-
.filter((option) => option.selected || option.
|
|
337
|
+
.filter((option) => option.selected || resolveNode(option.attributes.value,component)==value || option.innerText == value)
|
|
358
338
|
.map((option) => option.getAttribute("value") || option.innerText);
|
|
359
339
|
}
|
|
360
340
|
}
|
|
361
|
-
component.varsProxy[
|
|
341
|
+
component.varsProxy[name] = coerce(value, type);
|
|
362
342
|
})
|
|
343
|
+
}
|
|
344
|
+
const tryParse = (value) => {
|
|
345
|
+
try {
|
|
346
|
+
return JSON.parse(value);
|
|
347
|
+
} catch(e) {
|
|
348
|
+
return value;
|
|
363
349
|
}
|
|
364
350
|
}
|
|
365
351
|
let reserved = {
|
|
@@ -372,7 +358,7 @@ const {observe} = (() => {
|
|
|
372
358
|
exported: {value: true, constant: true},
|
|
373
359
|
imported: {value: true, constant: true}
|
|
374
360
|
};
|
|
375
|
-
const createClass = (domElementNode, {observer,
|
|
361
|
+
const createClass = (domElementNode, {observer, importAnchors}) => {
|
|
376
362
|
const instances = new Set(),
|
|
377
363
|
dom = domElementNode.tagName === "TEMPLATE"
|
|
378
364
|
? domElementNode.content.cloneNode(true)
|
|
@@ -425,7 +411,6 @@ const {observe} = (() => {
|
|
|
425
411
|
})
|
|
426
412
|
});
|
|
427
413
|
[...dom.childNodes].forEach((child) => shadow.appendChild(child.cloneNode(true)));
|
|
428
|
-
if (bindForms) _bindForms(shadow, this);
|
|
429
414
|
if (importAnchors) _importAnchors(shadow, this);
|
|
430
415
|
}
|
|
431
416
|
|
|
@@ -459,9 +444,7 @@ const {observe} = (() => {
|
|
|
459
444
|
const text = script.innerHTML.replaceAll(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, "$1").replaceAll(/\r?\n/g, "");
|
|
460
445
|
currentScript.innerHTML = `Function('if(window["${scriptid}"]?.ctx) { with(window["${scriptid}"].ctx) { ${text}; } window["${scriptid}"](); }')(); `;
|
|
461
446
|
let resolver;
|
|
462
|
-
promises.push(new Promise((resolve) =>
|
|
463
|
-
resolver = resolve;
|
|
464
|
-
}));
|
|
447
|
+
promises.push(new Promise((resolve) => resolver = resolve));
|
|
465
448
|
window[scriptid] = () => {
|
|
466
449
|
delete window[scriptid];
|
|
467
450
|
currentScript.remove();
|
|
@@ -471,49 +454,93 @@ const {observe} = (() => {
|
|
|
471
454
|
ctx.appendChild(currentScript);
|
|
472
455
|
}
|
|
473
456
|
Promise.all(promises).then(() => {
|
|
474
|
-
const inputs = [...ctx.shadowRoot.querySelectorAll("input[l-bind]"), ...ctx.shadowRoot.querySelectorAll("select[l-bind]")];
|
|
475
|
-
inputs.forEach((input) => {
|
|
476
|
-
bindInput(input, ctx);
|
|
477
|
-
})
|
|
478
457
|
const nodes = getNodes(ctx);
|
|
479
458
|
nodes.forEach((node) => {
|
|
480
459
|
if (node.nodeType === Node.TEXT_NODE && node.template.includes("${")) {
|
|
481
460
|
render(!!node.template, () => resolveNode(node, this))
|
|
482
461
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
462
|
+
// resolve the value before all else;
|
|
463
|
+
const attr = node.attributes.value;
|
|
464
|
+
let name;
|
|
465
|
+
if(attr && attr.template) {
|
|
466
|
+
render(!!attr.template,() => {
|
|
467
|
+
const value = resolveNode(attr, this),
|
|
468
|
+
eltype = resolveNode(node.attributes.type,ctx);
|
|
469
|
+
if(eltype==="checkbox") {
|
|
470
|
+
if(coerce(value,"boolean")===true) {
|
|
471
|
+
node.setAttribute("checked","");
|
|
472
|
+
node.checked = true;
|
|
473
|
+
} else {
|
|
474
|
+
node.removeAttribute("checked");
|
|
475
|
+
node.checked = false;
|
|
476
|
+
}
|
|
477
|
+
const vname = resolveNode(node.attributes.name,ctx);
|
|
478
|
+
if(vname) ctx.setValue(vname,node.checked,{coerceTo:"boolean"});
|
|
479
|
+
}
|
|
480
|
+
if(node.tagName==="SELECT") {
|
|
481
|
+
let values = [value];
|
|
482
|
+
if(node.hasAttribute("multiple")) values = coerce(value,Array);
|
|
483
|
+
[...node.querySelectorAll("option")].forEach((option) => {
|
|
484
|
+
if(option.hasAttribute("value")) {
|
|
485
|
+
if (values.includes(resolveNode(option.attributes.value, ctx))) {
|
|
486
|
+
option.setAttribute("selected", "");
|
|
487
|
+
option.selected = true;
|
|
488
|
+
}
|
|
489
|
+
} else if(option.innerText.trim()===value) {
|
|
490
|
+
option.setAttribute("selected","");
|
|
491
|
+
option.selected = true;
|
|
492
|
+
}
|
|
493
|
+
})
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
let name;
|
|
497
|
+
for(const vname of this.getVariableNames()) {
|
|
498
|
+
if("${" + vname + "}" === attr.template) {
|
|
499
|
+
name = vname;
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if(name) bindInput(node,name,ctx);
|
|
504
|
+
}
|
|
483
505
|
[...node.attributes].forEach((attr) => {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
if
|
|
506
|
+
if(attr.name==="value") return;
|
|
507
|
+
const {name, value} = attr;
|
|
508
|
+
if(name==="type") {
|
|
509
|
+
if (value === "radio") {
|
|
510
|
+
const name = resolveNode(node.attributes.name, ctx);
|
|
511
|
+
for (const vname of this.getVariableNames()) {
|
|
512
|
+
if (vname === name) {
|
|
513
|
+
render(true, () => {
|
|
514
|
+
const name = resolveNode(node.attributes.name, ctx),
|
|
515
|
+
varvalue = Function("context", "with(context) { return `${" + name + "}` }")(ctx.varsProxy);
|
|
516
|
+
if (varvalue == resolveNode(node.attributes.value,ctx)) {
|
|
517
|
+
node.setAttribute("checked", "");
|
|
518
|
+
node.checked = true;
|
|
519
|
+
} else {
|
|
520
|
+
node.removeAttribute("checked");
|
|
521
|
+
node.checked = false;
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
bindInput(node, name, ctx);
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const [type, ...params] = name.split(":");
|
|
532
|
+
if (type === "") { // name is :something
|
|
487
533
|
render(!!attr.template, () => {
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
elname = node.
|
|
534
|
+
const value = attr.value,
|
|
535
|
+
elvalue = resolveNode(node.attributes.value,ctx),
|
|
536
|
+
eltype = resolveNode(node.attributes.type,ctx),
|
|
537
|
+
elname = resolveNode(node.attributes.name,ctx);
|
|
492
538
|
if (params[0]) {
|
|
493
539
|
if (value === "true") node.setAttribute(params[0], "")
|
|
494
540
|
else node.removeAttribute(params[0]);
|
|
495
|
-
} else if (
|
|
496
|
-
if (value === "true")
|
|
497
|
-
|
|
498
|
-
} else {
|
|
499
|
-
node.removeAttribute("checked");
|
|
500
|
-
}
|
|
501
|
-
} else if (attrtype === "radio") {
|
|
502
|
-
if (elvalue === value) {
|
|
503
|
-
node.setAttribute("checked", "");
|
|
504
|
-
}
|
|
505
|
-
} else if (name === "value" && node.tagName === "SELECT") {
|
|
506
|
-
node.setAttribute("value", value);
|
|
507
|
-
const values = value[0] === "[" ? JSON.parse(value) : value.split(","); // handle multiselect
|
|
508
|
-
[...node.querySelectorAll("option")].forEach((option) => {
|
|
509
|
-
if (option.hasAttribute("value")) {
|
|
510
|
-
if (values.includes(option.getAttribute("value"))) {
|
|
511
|
-
option.setAttribute("selected", true);
|
|
512
|
-
}
|
|
513
|
-
} else if (values.includes(option.innerText)) {
|
|
514
|
-
option.setAttribute("selected", true);
|
|
515
|
-
}
|
|
516
|
-
})
|
|
541
|
+
} else if (eltype=== "checkbox" || node.tagName === "OPTION") {
|
|
542
|
+
if (value === "true") node.setAttribute("checked", "")
|
|
543
|
+
else node.removeAttribute("checked");
|
|
517
544
|
}
|
|
518
545
|
})
|
|
519
546
|
} else if (type === "l-on") {
|
|
@@ -532,7 +559,7 @@ const {observe} = (() => {
|
|
|
532
559
|
} else if (type === "l-for") {
|
|
533
560
|
node.template ||= node.innerHTML;
|
|
534
561
|
render(!!attr.template, () => {
|
|
535
|
-
const [what = "each", vname = "
|
|
562
|
+
const [what = "each", vname = "item", index = "index", array = "array", after = false] = params,
|
|
536
563
|
value = resolveNode(attr, this),
|
|
537
564
|
coerced = coerce(value, what === "each" ? Array : "object"),
|
|
538
565
|
target = what === "each" ? coerced : Object[what](coerced),
|
|
@@ -652,9 +679,7 @@ const {observe} = (() => {
|
|
|
652
679
|
variable.shared = true;
|
|
653
680
|
addEventListener("change", ({variableName, value}) => {
|
|
654
681
|
if (this.vars[variableName]?.shared) {
|
|
655
|
-
this.siblings.forEach((instance) =>
|
|
656
|
-
instance.setValue(variableName, value);
|
|
657
|
-
})
|
|
682
|
+
this.siblings.forEach((instance) => instance.setValue(variableName, value))
|
|
658
683
|
}
|
|
659
684
|
})
|
|
660
685
|
}
|
|
@@ -669,47 +694,6 @@ const {observe} = (() => {
|
|
|
669
694
|
})
|
|
670
695
|
}
|
|
671
696
|
});
|
|
672
|
-
addEventListener("change", ({variableName, value}) => {
|
|
673
|
-
[...this.shadowRoot.querySelectorAll(`input[l-bind=${variableName}]`),
|
|
674
|
-
...this.shadowRoot.querySelectorAll(`select[l-bind=${variableName}]`)]
|
|
675
|
-
.forEach((input) => {
|
|
676
|
-
const eltype = input.getAttribute("type");
|
|
677
|
-
if (eltype === "checkbox") { // at el option selected
|
|
678
|
-
if(!!value) {
|
|
679
|
-
input.setAttribute("checked", "");
|
|
680
|
-
} else {
|
|
681
|
-
input.removeAttribute("checked");
|
|
682
|
-
}
|
|
683
|
-
input.checked = !!value;
|
|
684
|
-
} else if (eltype === "radio") {
|
|
685
|
-
if (input.getAttribute("value") === value) {
|
|
686
|
-
input.setAttribute("checked", "");
|
|
687
|
-
input.checked = true;
|
|
688
|
-
}
|
|
689
|
-
} else if (input.tagName === "SELECT") {
|
|
690
|
-
const values = value && typeof (value) === "object" && value instanceof Array ? value : [value];
|
|
691
|
-
[...input.querySelectorAll("option")].forEach((option) => {
|
|
692
|
-
if (values.includes(option.getAttribute("value") || option.innerText)) {
|
|
693
|
-
option.setAttribute("selected", "");
|
|
694
|
-
option.selected = true;
|
|
695
|
-
}
|
|
696
|
-
})
|
|
697
|
-
} else if (!eltype || eltype === "text") {
|
|
698
|
-
value = typeof (value) === "string" || value == null ? value : JSON.stringify(value);
|
|
699
|
-
const oldvalue = input.getAttribute("value") || "";
|
|
700
|
-
if (oldvalue !== value) {
|
|
701
|
-
if (value == null) input.removeAttribute("value");
|
|
702
|
-
else input.setAttribute("value", value);
|
|
703
|
-
try {
|
|
704
|
-
input.setSelectionRange(0, Math.max(oldvalue.length, value ? value.length : 0)); // shadowDom sometimes fails to rerender unless this is done;
|
|
705
|
-
input.setRangeText(value || "", 0, Math.max(oldvalue.length, value ? value.length : 0));
|
|
706
|
-
} catch (e) {
|
|
707
|
-
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
})
|
|
712
|
-
})
|
|
713
697
|
}
|
|
714
698
|
return Object.entries(this.vars)
|
|
715
699
|
.reduce((result, [key, variable]) => {
|
|
@@ -737,13 +721,13 @@ const {observe} = (() => {
|
|
|
737
721
|
}
|
|
738
722
|
}
|
|
739
723
|
}
|
|
740
|
-
const createComponent = (name, node, {observer,
|
|
724
|
+
const createComponent = (name, node, {observer, importAnchors} = {}) => {
|
|
741
725
|
let ctor = customElements.get(name);
|
|
742
726
|
if (ctor) {
|
|
743
727
|
console.warn(new Error(`${name} is already a CustomElement. Not redefining`));
|
|
744
728
|
return ctor;
|
|
745
729
|
}
|
|
746
|
-
ctor = createClass(node, {observer,
|
|
730
|
+
ctor = createClass(node, {observer, importAnchors});
|
|
747
731
|
customElements.define(name, ctor);
|
|
748
732
|
return ctor;
|
|
749
733
|
}
|
|
@@ -759,10 +743,9 @@ const {observe} = (() => {
|
|
|
759
743
|
const html = await (await fetch(url.href)).text(),
|
|
760
744
|
dom = parser.parseFromString(html, "text/html"),
|
|
761
745
|
importAnchors = !!dom.head.querySelector('meta[name="l-importAnchors"]'),
|
|
762
|
-
bindForms = !!dom.head.querySelector('meta[name="l-bindForms"]'),
|
|
763
746
|
unhide = !!dom.head.querySelector('meta[name="l-unhide"]');
|
|
764
747
|
if (unhide) dom.body.removeAttribute("hidden");
|
|
765
|
-
createComponent(as, dom.body, {observer, importAnchors
|
|
748
|
+
createComponent(as, dom.body, {observer, importAnchors});
|
|
766
749
|
}
|
|
767
750
|
return {as};
|
|
768
751
|
}
|
|
@@ -773,9 +756,9 @@ const {observe} = (() => {
|
|
|
773
756
|
}
|
|
774
757
|
}
|
|
775
758
|
|
|
776
|
-
const bodyAsComponent = ({as = "x-body", unhide, importAnchors
|
|
759
|
+
const bodyAsComponent = ({as = "x-body", unhide, importAnchors} = {}) => {
|
|
777
760
|
const parent = document.body.parentElement;
|
|
778
|
-
createComponent(as, document.body, {importAnchors
|
|
761
|
+
createComponent(as, document.body, {importAnchors});
|
|
779
762
|
const component = document.createElement(as);
|
|
780
763
|
parent.replaceChild(component, document.body);
|
|
781
764
|
Object.defineProperty(document, "body", {
|
|
@@ -821,12 +804,11 @@ const {observe} = (() => {
|
|
|
821
804
|
const loader = async (whenFramed) => {
|
|
822
805
|
if (!!document.querySelector('meta[name="l-importLinks"]')) await importLinks();
|
|
823
806
|
const importAnchors = !!document.querySelector('meta[name="l-importAnchors"]'),
|
|
824
|
-
bindForms = !!document.querySelector('meta[name="l-bindForms"]'),
|
|
825
807
|
unhide = !!document.querySelector('meta[name="l-unhide"]'),
|
|
826
808
|
isolated = !!document.querySelector('meta[name="l-isolate"]'),
|
|
827
809
|
enableFrames = !!document.querySelector('meta[name="l-enableFrames"]');
|
|
828
810
|
if (whenFramed) {
|
|
829
|
-
whenFramed({unhide, importAnchors,
|
|
811
|
+
whenFramed({unhide, importAnchors, isolated, enableFrames});
|
|
830
812
|
if (!isolated) {
|
|
831
813
|
postMessage.enabled = true;
|
|
832
814
|
addListener(window,"message", ({data}) => {
|
|
@@ -861,7 +843,7 @@ const {observe} = (() => {
|
|
|
861
843
|
postMessage({type: "DOMContentLoaded"})
|
|
862
844
|
}
|
|
863
845
|
} else if (url.searchParams.has("as")) {
|
|
864
|
-
bodyAsComponent({as: url.searchParams.get("as"), unhide, importAnchors
|
|
846
|
+
bodyAsComponent({as: url.searchParams.get("as"), unhide, importAnchors});
|
|
865
847
|
}
|
|
866
848
|
if (enableFrames) {
|
|
867
849
|
postMessage.enabled = true;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lightview",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Small, simple, powerful web UI creation ... imagine a blend of Svelte, React, Vue, Riot and more.",
|
|
3
|
+
"version": "1.4.0b",
|
|
4
|
+
"description": "Small, simple, powerful web UI and micro front end creation ... imagine a blend of Svelte, React, Vue, Riot and more.",
|
|
5
5
|
"main": "lightview.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
"vue",
|
|
19
19
|
"moon",
|
|
20
20
|
"hyperapp",
|
|
21
|
-
"hyperhtml"
|
|
21
|
+
"hyperhtml",
|
|
22
|
+
"micro front end",
|
|
23
|
+
"custom elements",
|
|
24
|
+
"web components"
|
|
22
25
|
],
|
|
23
26
|
"author": "Simon Y. Blackwell",
|
|
24
27
|
"license": "MIT",
|
package/remote.html
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
<body>
|
|
11
11
|
<p>
|
|
12
|
-
The component below is loaded from an alternate domain and running in
|
|
12
|
+
The component below is loaded from an alternate domain and running in a child iframe.
|
|
13
|
+
The logging console is below the component in this frame.
|
|
13
14
|
</p>
|
|
14
15
|
<iframe id="myframe" src="https://lightview.dev/remoteform.html?id=myframe"></iframe>
|
|
15
16
|
<div id="console" style="max-height:250px;scroll:auto"></div>
|
package/remoteform.html
CHANGED
|
@@ -2,45 +2,73 @@
|
|
|
2
2
|
|
|
3
3
|
<head>
|
|
4
4
|
<title>Form</title>
|
|
5
|
-
<meta name="l-bindForms">
|
|
6
5
|
<script src="./lightview.js?as=x-body"></script>
|
|
7
|
-
<script>Lightview.whenFramed(({as,unhide,importAnchors,
|
|
8
|
-
Lightview.bodyAsComponent({as,unhide,importAnchors,
|
|
6
|
+
<script>Lightview.whenFramed(({as,unhide,importAnchors,isolated,enableFrames}) => {
|
|
7
|
+
Lightview.bodyAsComponent({as,unhide,importAnchors,isolated,enableFrames});
|
|
9
8
|
})</script>
|
|
10
9
|
</head>
|
|
11
10
|
|
|
12
11
|
<body style="height:fit-content;width:fit-content;display:flex;flex-direction:column;max-height:100%;overflow:auto;">
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<
|
|
12
|
+
<div style="margin:20px">
|
|
13
|
+
<p>
|
|
14
|
+
<input type="text" value="${color}">
|
|
15
|
+
<input type="checkbox" value="${checked}">
|
|
16
|
+
<input type="radio" name="color" value="red">
|
|
17
|
+
<input type="radio" name="color" value="yellow">
|
|
18
|
+
<input type="radio" name="color" value="green">
|
|
19
|
+
<select value="${color}">
|
|
20
|
+
<option value="red">red</option>
|
|
21
|
+
<option>yellow</option>
|
|
22
|
+
<option> green </option>
|
|
23
|
+
</select>
|
|
24
|
+
<div>Hamburger options:</div>
|
|
25
|
+
<select value="${hamburger}" multiple>
|
|
26
|
+
<option value="lettuce">lettuce</option>
|
|
27
|
+
<option>tomato</option>
|
|
28
|
+
<option>cheese</option>
|
|
29
|
+
</select>
|
|
30
|
+
</p>
|
|
31
|
+
<p l-if="${checked}">
|
|
32
|
+
Now you've done it. You've exposed me.
|
|
33
|
+
</p>
|
|
34
|
+
<ul l-for="${hamburger}">
|
|
35
|
+
<li>${item}</li>
|
|
36
|
+
</ul>
|
|
37
|
+
<ul l-for:entries="${hamburger}">
|
|
38
|
+
<li>${item[0]}:${item[1]}</li>
|
|
39
|
+
</ul>
|
|
40
|
+
<ul l-for:values="${hamburger}">
|
|
41
|
+
<li>${item}</li>
|
|
42
|
+
</ul>
|
|
43
|
+
<p id="variables">
|
|
44
|
+
|
|
45
|
+
</p>
|
|
46
|
+
</div>
|
|
21
47
|
<script type="lightview/module">
|
|
22
|
-
self.variables({
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
self.variables({
|
|
49
|
+
color: string,
|
|
50
|
+
checked: boolean,
|
|
51
|
+
hamburger: Array
|
|
52
|
+
}, {
|
|
53
|
+
reactive
|
|
54
|
+
});
|
|
55
|
+
color = "green";
|
|
56
|
+
checked = true;
|
|
57
|
+
hamburger = ["lettuce"];
|
|
58
|
+
// demo instrumentation
|
|
59
|
+
const variableValues = () => {
|
|
60
|
+
const el = self.getElementById("variables");
|
|
61
|
+
while (el.lastElementChild) el.lastElementChild.remove();
|
|
62
|
+
self.getVariableNames().forEach((name) => {
|
|
63
|
+
const line = document.createElement("div");
|
|
64
|
+
line.innerText = `${name} = ${JSON.stringify(self.getValue(name))}`;
|
|
65
|
+
el.appendChild(line);
|
|
66
|
+
});
|
|
38
67
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
68
|
+
variableValues();
|
|
69
|
+
addEventListener("change", () => {
|
|
70
|
+
variableValues()
|
|
71
|
+
});
|
|
44
72
|
</script>
|
|
45
73
|
</body>
|
|
46
74
|
|
package/scratch.html
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Scratch</title>
|
|
6
|
+
<script src="./lightview.js?as=x-body"></script>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div style="margin:20px;padding:5px;border:1px;border-style:solid;border-color:${color}">
|
|
10
|
+
<p>
|
|
11
|
+
<input type="text" value="${color}">
|
|
12
|
+
<input type="radio" name="color" value="red">
|
|
13
|
+
<input type="radio" name="color" value="yellow">
|
|
14
|
+
<input type="radio" name="color" value="green">
|
|
15
|
+
<select value="${color}">
|
|
16
|
+
<option value="red">red</option>
|
|
17
|
+
<option>yellow</option>
|
|
18
|
+
<option> green</option>
|
|
19
|
+
</select>
|
|
20
|
+
<div>Hamburger options:</div>
|
|
21
|
+
<select value="${hamburger}" multiple>
|
|
22
|
+
<option value="lettuce">lettuce</option>
|
|
23
|
+
<option>tomato</option>
|
|
24
|
+
<option>cheese</option>
|
|
25
|
+
</select>
|
|
26
|
+
</p>
|
|
27
|
+
Expose: <input type="checkbox" value="${checked}">
|
|
28
|
+
<p l-if="${checked}">
|
|
29
|
+
Now you've done it. You've exposed me.
|
|
30
|
+
</p>
|
|
31
|
+
<ul l-for="${hamburger}">
|
|
32
|
+
<li>${item}</li>
|
|
33
|
+
</ul>
|
|
34
|
+
<ul l-for:entries="${hamburger}">
|
|
35
|
+
<li>${item[0]}:${item[1]}</li>
|
|
36
|
+
</ul>
|
|
37
|
+
<ul l-for:values="${hamburger}">
|
|
38
|
+
<li>${item}</li>
|
|
39
|
+
</ul>
|
|
40
|
+
<p id="variables">
|
|
41
|
+
|
|
42
|
+
</p>
|
|
43
|
+
</div>
|
|
44
|
+
<script type="lightview/module">
|
|
45
|
+
self.variables({color:string,checked:boolean,hamburger:Array},{reactive});
|
|
46
|
+
|
|
47
|
+
color = "green";
|
|
48
|
+
checked = true;
|
|
49
|
+
hamburger = ["lettuce"];
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
// demo instrumentation
|
|
53
|
+
const variableValues = () => {
|
|
54
|
+
const el = self.getElementById("variables");
|
|
55
|
+
while (el.lastElementChild) el.lastElementChild.remove();
|
|
56
|
+
self.getVariableNames().forEach((name) => {
|
|
57
|
+
const line = document.createElement("div");
|
|
58
|
+
line.innerText = `${name} = ${JSON.stringify(self.getValue(name))}`;
|
|
59
|
+
el.appendChild(line);
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
variableValues();
|
|
63
|
+
addEventListener("change", () => {
|
|
64
|
+
variableValues()
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
</script>
|
|
68
|
+
</body>
|
|
69
|
+
</html>
|
package/xor.html
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<template id="audiostream">
|
|
6
6
|
<p>${name}</p>
|
|
7
7
|
<p>
|
|
8
|
-
Play: <input
|
|
8
|
+
Play: <input type="checkbox" value="${run}">
|
|
9
9
|
</p>
|
|
10
10
|
<script type="lightview/module">
|
|
11
11
|
self.variables({
|
|
12
12
|
run: boolean
|
|
13
|
-
});
|
|
13
|
+
},{reactive});
|
|
14
14
|
self.variables({
|
|
15
15
|
name: string
|
|
16
16
|
}, {
|
|
@@ -31,9 +31,7 @@
|
|
|
31
31
|
<title>Form</title>
|
|
32
32
|
<script src="./lightview.js"></script>
|
|
33
33
|
<script>
|
|
34
|
-
Lightview.createComponent("x-audiostream", document.getElementById("audiostream")
|
|
35
|
-
bindForms: true
|
|
36
|
-
})
|
|
34
|
+
Lightview.createComponent("x-audiostream", document.getElementById("audiostream"))
|
|
37
35
|
</script>
|
|
38
36
|
</head>
|
|
39
37
|
|