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 CHANGED
@@ -1,6 +1,6 @@
1
- # lightview v1.3.1b (BETA)
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" :="${on}" l-bind="on">
10
+ <div style="margin:20px">
11
+ <p>
12
+ Show: <input type="checkbox" value="${on}">
13
13
  <div l-if="${on}">
14
- Show is true
14
+ Now you've done it. You've exposed me.
15
15
  </div>
16
- </p>
17
- <p>
18
-
19
- <input id="red" type="radio" name="myradio" value="red" :="${color}" l-bind="color"> Red
20
- <input id="yellow" type="radio" name="myradio" value="yellow" :="${color}" l-bind="color"> Yellow
21
- <input id="green" type="radio" name="myradio" value="green" :="${color}" l-bind="color"> Green
22
- </p>
23
-
24
- <p>
25
- <select l-bind="color" value="${color}">
26
- <option value="red">Red</option>
27
- <option value="yellow">Yellow</option>
28
- <option value="green">Green</option>
29
- </select>
30
- </p>
31
-
32
-
33
- <p>
34
- How would you like that burger?<br>
35
- <select l-bind="options" value="${options}" multiple>
36
- <option>lettuce</option>
37
- <option>tomato</option>
38
- <option>cheese</option>
39
- </select>
40
- </p>
41
-
42
-
43
-
44
- <ul l-for:each="${children}">
45
- <li>${index}:${element}</li>
46
- </ul>
47
- <ul l-for:values:value:index='{"1":"v1","2":"v2","3":"v3"}'>
48
- <li>${value}:${index}</li>
49
- </ul>
50
- <ul l-for:keys:key='{"name":"joe","age":27}'>
51
- <li>${key}</li>
52
- </ul>
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,off:boolean,color:string,children:Array,options:Array},{reactive});
54
+ self.variables({on:boolean,options:Array},{reactive});
62
55
 
63
56
  on = true;
64
- color = "yellow";
65
- children = ["John","Mary","Jane"];
66
- options = ["tomato"];
57
+ options = ["lettuce"];
67
58
 
68
- addEventListener("change",()=> {
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
- if (!nodes.includes(node)) nodes.push(node);
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
- if (!nodes.includes(node)) nodes.push(node);
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.template) {
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.nodeValue;
292
+ return node?.nodeValue;
293
293
  }
294
- const render = (template, render) => {
294
+ const render = (hasTemplate, render) => {
295
295
  let observer;
296
- if (template) {
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 _bindForms = (node, component) => {
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[vname] || {type};
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 ${vname}:${variable.type}`)
325
+ else throw new TypeError(`Attempt to bind <input name="${name}" type="${type}"> to variable ${name}:${variable.type}`)
334
326
  }
335
- component.variables({[vname]: type});
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.getAttribute("value") == value || option.innerText == value)
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[vname] = coerce(value, type);
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, bindForms, importAnchors}) => {
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
- const {name, value} = attr,
485
- [type, ...params] = name.split(":");
486
- if (type === "" || type=="checked" || node.tagName === "SELECT") { // name is :something
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 attrtype = node.getAttribute("type"),
489
- value = resolveNode(attr, this),
490
- elvalue = node.getAttribute("value"),
491
- elname = node.getAttribute("name");
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 (attrtype === "checkbox" || node.tagName === "OPTION") {
496
- if (value === "true") {
497
- node.setAttribute("checked", "");
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 = "element", index = "index", array = "array", after = false] = params,
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, bindForms, importAnchors} = {}) => {
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, bindForms, importAnchors});
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, bindForms});
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, bindForms} = {}) => {
759
+ const bodyAsComponent = ({as = "x-body", unhide, importAnchors} = {}) => {
777
760
  const parent = document.body.parentElement;
778
- createComponent(as, document.body, {importAnchors, bindForms});
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, bindForms, isolated, enableFrames});
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, bindForms});
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.3.1b",
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 an iframe.
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,bindForms,isolated,enableFrames}) => {
8
- Lightview.bodyAsComponent({as,unhide,importAnchors,bindForms,isolated,enableFrames});
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
- <p>
14
- <button l-on:click="run">Run</button> <button l-on:click="reset">Reset</button> <button l-on:click="setNull">Set Null</button>
15
- </p>
16
- <form>
17
- <input name="name" type="text" value="Joe"><br>
18
- <input name="age" type="number" value="20"><br>
19
- </form>
20
- <div id="console"></div>
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({name:string,age:number},{exported,imported});
23
- self.run = () => {
24
- name = "Bill";
25
- age = 30;
26
- action("run");
27
- };
28
- self.reset = () => {
29
- name = "Joe";
30
- age = 20;
31
- const console = self.getElementById("console");
32
- while(console.lastElementChild) console.lastElementChild.remove();
33
- };
34
- self.setNull = () => {
35
- name = null;
36
- age = undefined;
37
- action("setNull");
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
- const action = (name) => {
40
- const div = document.createElement("div");
41
- div.innerText = name;
42
- self.getElementById("console").appendChild(div);
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 name="play" type="checkbox" l-bind="run" checked="${run}">
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