lightview 1.4.8-b → 1.6.2-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,4 +1,4 @@
1
- # lightview v1.4.8b (BETA)
1
+ # lightview v1.4.10b (BETA)
2
2
 
3
3
  Small, simple, powerful web UI and micro front end creation ...
4
4
 
@@ -0,0 +1,76 @@
1
+ <html>
2
+ <head>
3
+ <meta charset="UTF-8">
4
+ <title>Lightview:Chart</title>
5
+ </head>
6
+ <body hidden>
7
+ <div id="target"></div>
8
+ <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
9
+ <script src="https://unpkg.com/json5@^2.0.0/dist/index.min.js"></script>
10
+ <script type="lightview/module">
11
+ const target = self.getElementById("target"),
12
+ resizeObserver = new ResizeObserver(entries => {
13
+ for (let entry of entries) {
14
+ if(entry.contentBoxSize) {
15
+ // Firefox implements `contentBoxSize` as a single content rect, rather than an array
16
+ const contentBoxSize = Array.isArray(entry.contentBoxSize) ? entry.contentBoxSize[0] : entry.contentBoxSize;
17
+ options.width = contentBoxSize.inlineSize;
18
+ } else {
19
+ options.width = entry.contentRect.width;
20
+ }
21
+ }
22
+ chart.draw(datatable, options);
23
+ });
24
+ let chart,
25
+ datatable,
26
+ options;
27
+ self.variables({type: "string", title: "string", style: "string"}, {observed});
28
+ if(style) target.setAttribute("style",style);
29
+ self.addRow = (row) => {
30
+ datatable.addRows([row]);
31
+ chart.draw(datatable, options);
32
+ };
33
+ self.setValue = (row,col,value) => {
34
+ if(datatable) {
35
+ datatable.setValue(row,col,value);
36
+ chart.draw(datatable, options);
37
+ }
38
+ };
39
+ self.init = () => {
40
+ const node = self.firstChild,
41
+ callback = (textNode, oldValue, newValue) => {
42
+ datatable = new google.visualization.DataTable();
43
+ try {
44
+ const table = JSON5.parse(newValue.trim());
45
+ datatable = google.visualization.arrayToDataTable(table);
46
+ chart.draw(datatable, options);
47
+ } catch (e) {
48
+ target.innerText = e + newValue;
49
+ }
50
+ };
51
+ options = {
52
+ title: title
53
+ };
54
+ chart = new google.visualization[type](target);
55
+ node.characterDataMutationCallback = callback;
56
+ callback(node, node.textContent, node.textContent);
57
+ addEventListener("change",({target,value}) => {
58
+ if (target === "type") {
59
+ chart = new google.visualization[type](target);
60
+ } else if (target === "title") {
61
+ options.title = value;
62
+ } else if(target === "style") {
63
+ target.setAttribute("style",value);
64
+ }
65
+ chart.draw(datatable, options);
66
+ });
67
+ resizeObserver.observe(target);
68
+ self.removeAttribute("hidden");
69
+ };
70
+ addEventListener("connected", ({target}) => {
71
+ google.charts.load("current", {"packages": ["corechart","gauge"]});
72
+ google.charts.setOnLoadCallback(self.init);
73
+ });
74
+ </script>
75
+ </body>
76
+ </html>
@@ -0,0 +1,57 @@
1
+ <html>
2
+ <head>
3
+ <meta charset="UTF-8">
4
+ <title>Lightview:Gauge</title>
5
+ </head>
6
+ <body hidden>
7
+ <div id="target"></div>
8
+ <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
9
+ <script type="lightview/module">
10
+ const target = self.getElementById("target"),
11
+ resizeObserver = new ResizeObserver(entries => {
12
+ for (let entry of entries) {
13
+ if(entry.contentBoxSize) {
14
+ // Firefox implements `contentBoxSize` as a single content rect, rather than an array
15
+ const contentBoxSize = Array.isArray(entry.contentBoxSize) ? entry.contentBoxSize[0] : entry.contentBoxSize;
16
+ options.width = contentBoxSize.inlineSize;
17
+ } else {
18
+ options.width = entry.contentRect.width;
19
+ }
20
+ }
21
+ chart.draw(datatable, options);
22
+ });
23
+ let chart,
24
+ datatable,
25
+ options;
26
+ self.variables({label: "string", value:"number", style: "string"}, {observed});
27
+ if(style) target.setAttribute("style",style);
28
+ self.setValue = (newValue) => {
29
+ if(datatable) {
30
+ datatable.setValue(0,1,newValue);
31
+ chart.draw(datatable, options);
32
+ }
33
+ };
34
+ self.init = () => {
35
+ options = { };
36
+ chart = new google.visualization.Gauge(target);
37
+ datatable = google.visualization.arrayToDataTable([["Label","Value"],[label,value]]);
38
+ addEventListener("change",({target,value}) => {
39
+ if (target === "label") {
40
+ datatable.setValue(0,0,value);
41
+ } else if(target ==="value") {
42
+ self.setValue(value);
43
+ } else if(target === "style") {
44
+ target.setAttribute("style",value);
45
+ }
46
+ chart.draw(datatable, options);
47
+ });
48
+ resizeObserver.observe(target);
49
+ self.removeAttribute("hidden");
50
+ };
51
+ addEventListener("connected", ({target}) => {
52
+ google.charts.load("current", {"packages": ["corechart","gauge"]});
53
+ google.charts.setOnLoadCallback(self.init);
54
+ });
55
+ </script>
56
+ </body>
57
+ </html>
@@ -0,0 +1,64 @@
1
+ <!DOCTYPE html>
2
+ <head>
3
+ <title>Chart</title>
4
+ <link href="../components/chart.html" rel="module">
5
+ <script src="../lightview.js"></script>
6
+ </head>
7
+ <body>
8
+ <l-chart id="myPieChart" style="max-width:750px" type="PieChart" title="How Much Pizza I Ate Last Night">
9
+ // Chart will resize automatically to a max-width of 750px and repaint on type and title changes.
10
+ // The component DOM element also exposes a method `el.addRow(row:array)` to dynamically add data
11
+ // And, `el.init()` will re-render from the initial data and current attributes.
12
+ [
13
+ ["Topping","Slices"], // column headings
14
+ ["Mushrooms", 3], // remaining rows are data
15
+ ["Onions", 1],
16
+ ["Olives", 1],
17
+ ["Zucchini", 1],
18
+ ["Pepperoni", 2]
19
+ ]
20
+ </l-chart>
21
+ <l-chart id="myBarChart" style="max-width:750px" type="BarChart" title="How Much Pizza I Ate Last Night">
22
+ [
23
+ ["Topping","Slices",{ role: "style" },{ role: "annotation" }], // column headings, annotation does not apply to Pie
24
+ ["Mushrooms", 3, "brown","M"], // remaining rows are data
25
+ ["Onions", 1, "yellow","On"],
26
+ ["Olives", 1, "black", "Ol"],
27
+ ["Zucchini", 1, "green","Z"],
28
+ ["Pepperoni", 2, "red","P"]
29
+ ]
30
+ </l-chart>
31
+ <l-chart id="myColumnChart" style="max-width:750px" type="ColumnChart" title="How Much Pizza I Ate Last Night">
32
+ [
33
+ ["Topping","Slices",{ role: "style" },{ role: "annotation" }], // column headings, style does not apply to Pie or Bar
34
+ ["Mushrooms", 3, "brown","Yum"], // remaining rows are data
35
+ ["Onions", 1, "yellow","Sweet"],
36
+ ["Olives", 1, "black","So, so"],
37
+ ["Zucchini", 1, "green","Ick!"],
38
+ ["Pepperoni", 2, "red","Spicy!"]
39
+ ]
40
+ </l-chart>
41
+ <l-chart id="myGauges" style="max-width:750px" type="Gauge" title="Laptop">
42
+ [
43
+ ['Label', 'Value'], // gauge will always take two columns, Label and Value
44
+ ['Memory', 80],
45
+ ['CPU', 55],
46
+ ['Network', 68]
47
+ ]
48
+ // there is also a separate l-gauge component that shows just one gauge
49
+ // and is driven entirely by attributes
50
+ </l-chart>
51
+ <script>
52
+ const gauges = document.getElementById("myGauges");
53
+ setInterval(function() {
54
+ gauges.setValue(0, 1, 40 + Math.round(60 * Math.random()));
55
+ }, 6000);
56
+ setInterval(function() {
57
+ gauges.setValue(1, 1, 40 + Math.round(60 * Math.random()));
58
+ }, 5000);
59
+ setInterval(function() {
60
+ gauges.setValue(2, 1, 60 + Math.round(40 * Math.random()));
61
+ }, 7500);
62
+ </script>
63
+ </body>
64
+ </html>
@@ -1,5 +1,5 @@
1
1
  <head>
2
- <title>Counter</title>
2
+ <title>Lightview:Examples:Counter</title>
3
3
  <script src="../lightview.js?as=x-body"></script>
4
4
  </head>
5
5
 
@@ -9,16 +9,9 @@
9
9
  </p>
10
10
 
11
11
  <script type="lightview/module">
12
- debugger;
13
- self.variables({
14
- count: number
15
- }, {
16
- reactive
17
- });
18
- debugger;
19
- count = 0;
12
+ self.variables({count: "number",}, {reactive,set:0});
20
13
  self.bump = () => count++;
21
- </script>
14
+ </script>
22
15
 
23
16
  <style>
24
17
  button {
@@ -0,0 +1,47 @@
1
+ import 'expect-puppeteer';
2
+
3
+ describe('Lightview:Example:Counter', () => {
4
+ beforeAll(async () => {
5
+ await page.goto('http://localhost:8080/examples/counter.html');
6
+ });
7
+
8
+ test('should be titled "Lightview:Examples:Counter"', async () => {
9
+ await expect(page.title()).resolves.toMatch("Lightview:Examples:Counter");
10
+ });
11
+
12
+ test('count should be a variable', async () => {
13
+ const result = await page.evaluate(async () => {
14
+ return document.body.getVariable("count");
15
+ });
16
+ expect(result).toBeDefined();
17
+ const {name,type,value,reactive} = result;
18
+ expect(name).toBe("count");
19
+ expect(type).toBe("number");
20
+ expect(value).toBe(0);
21
+ expect(reactive).toBe(true);
22
+ });
23
+
24
+ test('bump should be called', async () => {
25
+ const result = await page.evaluate(async () => {
26
+ document.body.bump();
27
+ return document.body.getVariableValue("count");
28
+ });
29
+ expect(result).toBe(1);
30
+ });
31
+
32
+ test('click should bump', async () => {
33
+ const buttonHandle = await page.evaluateHandle('document.body.querySelector("button")');
34
+ await buttonHandle.click();
35
+ const result = await page.evaluate(async () => {
36
+ return document.body.getVariableValue("count");
37
+ });
38
+ expect(result).toBe(2);
39
+ });
40
+
41
+ test("should be custom element", async() => {
42
+ const result = await page.evaluate(async () => {
43
+ return document.body.constructor.name;
44
+ });
45
+ expect(result).toBe("CustomElement");
46
+ })
47
+ })
@@ -51,7 +51,7 @@
51
51
  <p id="variables"></p>
52
52
  </div>
53
53
  <script type="lightview/module">
54
- self.variables({on:boolean,options:Array},{reactive});
54
+ self.variables({on:"boolean",options:Array},{reactive});
55
55
 
56
56
  on = true;
57
57
  options = ["lettuce"];
@@ -62,7 +62,7 @@ const variableValues = () => {
62
62
  while(el.lastElementChild) el.lastElementChild.remove();
63
63
  self.getVariableNames().forEach((name) => {
64
64
  const line = document.createElement("div");
65
- line.innerText = `${name} = ${JSON.stringify(self.getValue(name))}`;
65
+ line.innerText = `${name} = ${JSON.stringify(self.getVariableValue(name))}`;
66
66
  el.appendChild(line);
67
67
  });
68
68
  };
@@ -0,0 +1,36 @@
1
+ <!DOCTYPE html>
2
+
3
+ <head>
4
+ <title>Remote</title>
5
+ <link type="module" src="">
6
+ <meta name="l-enableFrames">
7
+ <script src="../lightview.js"></script>
8
+ </head>
9
+
10
+ <body>
11
+ <p>
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.
14
+ </p>
15
+ <iframe id="myframe" src="https://lightview.dev/foreignform.html?id=myframe"></iframe>
16
+ <div id="console" style="max-height:250px;scroll:auto"></div>
17
+ <script>
18
+ const mutationCallback = (mutationsList) => {
19
+ const console = document.getElementById("console");
20
+ for (const {target,attributeName,oldValue} of mutationsList) {
21
+ const line = document.createElement("div"),
22
+ event = {attributeName,oldValue,value:target.getAttribute(attributeName)};
23
+ line.innerText = JSON.stringify(event);
24
+ console.appendChild(line);
25
+ }
26
+ };
27
+ const observer = new MutationObserver(mutationCallback),
28
+ iframe = document.getElementById("myframe");
29
+ observer.observe(iframe, { attributes:true, attributeOldValue: true });
30
+ iframe.addEventListener("DOMContentLoaded",(event) => {
31
+ console.log(event);
32
+ });
33
+ </script>
34
+ </body>
35
+
36
+ </html>
@@ -46,8 +46,8 @@
46
46
  </div>
47
47
  <script type="lightview/module">
48
48
  self.variables({
49
- color: string,
50
- checked: boolean,
49
+ color: "string",
50
+ checked: "boolean",
51
51
  hamburger: Array
52
52
  }, {
53
53
  reactive
@@ -61,7 +61,7 @@
61
61
  while (el.lastElementChild) el.lastElementChild.remove();
62
62
  self.getVariableNames().forEach((name) => {
63
63
  const line = document.createElement("div");
64
- line.innerText = `${name} = ${JSON.stringify(self.getValue(name))}`;
64
+ line.innerText = `${name} = ${JSON.stringify(self.getVariableValue(name))}`;
65
65
  el.appendChild(line);
66
66
  });
67
67
  };
@@ -34,31 +34,25 @@
34
34
  </p>
35
35
  </div>
36
36
  <script type="lightview/module">
37
- self.variables({
38
- color: string,
39
- checked: boolean,
40
- hamburger: Array
41
- }, {
42
- reactive
37
+ self.variables({color:"string"},{reactive});
38
+ addEventListener("change", () => {
39
+ variableValues()
40
+ });
41
+ addEventListener("connected",() => {
42
+ color = "yellow";
43
+ checked = true;
44
+ hamburger = ["cheese"];
43
45
  });
44
- color = "red";
45
- checked = true;
46
- hamburger = ["cheese"];
47
-
48
46
  // demo instrumentation
49
47
  const variableValues = () => {
50
48
  const el = self.getElementById("variables");
51
49
  while (el.lastElementChild) el.lastElementChild.remove();
52
50
  self.getVariableNames().forEach((name) => {
53
51
  const line = document.createElement("div");
54
- line.innerText = `${name} = ${JSON.stringify(self.getValue(name))}`;
52
+ line.innerText = `${name} = ${JSON.stringify(self.getVariableValue(name))}`;
55
53
  el.appendChild(line);
56
54
  });
57
55
  };
58
- variableValues();
59
- addEventListener("change", () => {
60
- variableValues()
61
- });
62
56
  </script>
63
57
  </body>
64
58
 
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <head>
3
+ <title>Chart</title>
4
+ <link href="../components/gauge.html" rel="module">
5
+ <script src="../lightview.js"></script>
6
+ </head>
7
+ <body>
8
+ <l-gauge id="myGauge" style="max-width:750px" type="Gauge" label="Laptop" value="50"></l-gauge>
9
+ <script>
10
+ const gauge = document.getElementById("myGauge");
11
+ setInterval(function() {
12
+ gauge.setValue(40 + Math.round(60 * Math.random()));
13
+ }, 2500);
14
+ </script>
15
+ </body>
16
+ </html>
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Invalid Template Literals</title>
5
+ <script src="../lightview.js?as=x-body"></script>
6
+ </head>
7
+ <body>
8
+ <p>
9
+ <button l-on:click="bump">Click count:${count}</button>
10
+ </p>
11
+ <div style="margin:20px">
12
+ <p>
13
+ ${"<h1>"+(test++)+"</h1>"}
14
+ </p>
15
+ <p>
16
+ ${(while (test)<10 { test++}; test)}
17
+ </p>
18
+ <p>
19
+ ${(() =>test)()}
20
+ </p>
21
+ <p>
22
+ ${(() = >test)()}
23
+ </p>
24
+ <p>
25
+ ${function(){return \${test}})()}
26
+ </p>
27
+ <p>
28
+ ${window.alert("ok")}
29
+ </p>
30
+ </div>
31
+
32
+ <script type="lightview/module">
33
+ self.variables({count: "number",test:"number"}, {reactive,set:0});
34
+ self.bump = () => count++;
35
+ </script>
36
+
37
+ <style>
38
+ button {
39
+ margin: 20px;
40
+ background: gray
41
+ }
42
+ </style>
43
+ </body>
44
+
45
+ </html>
@@ -7,7 +7,7 @@
7
7
  ${value}
8
8
  <script type="lightview/module">
9
9
  debugger;
10
- self.variables({value:string},{imported});
10
+ self.variables({value:"string"},{imported});
11
11
  </script>
12
12
  </body>
13
13
  </html>
@@ -0,0 +1,51 @@
1
+ const http = require("http"),
2
+ fs = require("fs"),
3
+ host = 'localhost',
4
+ port = 8000,
5
+ requestListener = async function (req, res) {
6
+ const path = `.${req.url}`;
7
+ res.setHeader("Access-Control-Allow-Origin","*");
8
+ res.setHeader("Access-Control-Allow-Methods", "*");
9
+ res.setHeader("Access-Control-Allow-Headers", "*");
10
+ res.setHeader("Content-Type", "application/json");
11
+ if(req.method==="OPTIONS") {
12
+ res.end();
13
+ return;
14
+ }
15
+ if(req.method==="GET") {
16
+ console.log("GET",req.url);
17
+ res.write(fs.readFileSync(path));
18
+ res.end();
19
+ return;
20
+ }
21
+ const buffers = [];
22
+ for await(const chunk of req) {
23
+ buffers.push(chunk);
24
+ }
25
+ const data = JSON.parse(Buffer.concat(buffers).toString());
26
+ console.log(req.method,req.url,data);
27
+ if(req.method==="PUT") {
28
+ const string = JSON.stringify(data);
29
+ fs.writeFileSync(path,string);
30
+ res.write(string);
31
+ res.end();
32
+ return;
33
+ }
34
+ if(req.method==="PATCH") {
35
+ const {property,value,oldValue} = data,
36
+ json = JSON.parse(fs.readFileSync(path));
37
+ if(property!==undefined && json[property]===oldValue) { // probably need a deepEqual for a production use
38
+ json[property] = value;
39
+ fs.writeFileSync(path,JSON.stringify(json))
40
+ }
41
+ res.write(JSON.stringify(json));
42
+ res.end();
43
+ return;
44
+ }
45
+ },
46
+ server = http.createServer(requestListener);
47
+ server.listen(port, host, () => {
48
+ console.log(`Server is running on http://${host}:${port}`);
49
+ });
50
+
51
+
@@ -1,36 +1,32 @@
1
1
  <!DOCTYPE html>
2
-
2
+ <html lang="en">
3
3
  <head>
4
+ <meta charset="UTF-8">
4
5
  <title>Remote</title>
5
- <link type="module" src="">
6
- <meta name="l-enableFrames">
7
- <script src="../lightview.js"></script>
6
+ <script src="../lightview.js?as=x-body"></script>
8
7
  </head>
9
-
10
8
  <body>
11
- <p>
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.
14
- </p>
15
- <iframe id="myframe" src="https://lightview.dev/remoteform.html?id=myframe"></iframe>
16
- <div id="console" style="max-height:250px;scroll:auto"></div>
17
- <script>
18
- const mutationCallback = (mutationsList) => {
19
- const console = document.getElementById("console");
20
- for (const {target,attributeName,oldValue} of mutationsList) {
21
- const line = document.createElement("div"),
22
- event = {attributeName,oldValue,value:target.getAttribute(attributeName)};
23
- line.innerText = JSON.stringify(event);
24
- console.appendChild(line);
25
- }
26
- };
27
- const observer = new MutationObserver(mutationCallback),
28
- iframe = document.getElementById("myframe");
29
- observer.observe(iframe, { attributes:true, attributeOldValue: true });
30
- iframe.addEventListener("DOMContentLoaded",(event) => {
31
- console.log(event);
32
- });
9
+
10
+ <input id="myRemote" type=text" value="${JSON.stringify(myRemote)}" size="${JSON.stringify(myRemote).length}"><br>
11
+ <button l-on:click="patch">Patch</button><br>
12
+ <button l-on:click="replace">Replace</button>
13
+
14
+
15
+ <script type="lightview/module">
16
+ const {remote} = await import("../types.js");
17
+
18
+ self.variables({myRemote:"object"},{reactive,remote:remote("http://localhost:8000/remote.json")});
19
+
20
+ await myRemote; // must await remotes before the first time they are used, e.g. before HTML is rendered
21
+
22
+ self.patch = () => {
23
+ const json = JSON.parse(document.body.getElementById("myRemote").value);
24
+ Object.assign(myRemote,json);
25
+ };
26
+
27
+ self.replace = () => {
28
+ myRemote = JSON.parse(document.body.getElementById("myRemote").value);
29
+ };
33
30
  </script>
34
31
  </body>
35
-
36
32
  </html>
@@ -0,0 +1 @@
1
+ {"name":"joe","age":30}
@@ -0,0 +1,30 @@
1
+ <!DOCTYPE html>
2
+ <head>
3
+ <title>Lightview:Sensor Demo</title>
4
+ <link href="../../components/gauge.html" rel="module">
5
+ <script src="../../lightview.js?as=x-body"></script>
6
+ </head>
7
+ <body>
8
+ <div style="width:100%;text-align:center">
9
+ <div style="display:inline-block">
10
+ <l-gauge id="sensor1" type="Gauge" label="Sensor One" value="50"></l-gauge>
11
+ </div>
12
+
13
+ <div style="display:inline-block">
14
+ <l-gauge id="sensor2" type="Gauge" label="Sensor Two" value="50"></l-gauge>
15
+ </div>
16
+ </div>
17
+
18
+ <script type="lightview/module">
19
+ const {remote} = await import("../../types.js");
20
+ self.variables({sensor1:"number"},{remote:remote({ttl:5000,path:"https://lightview.dev/sensors/sensor1"})});
21
+ self.variables({sensor2:"number"},{remote:remote({ttl:7500,path:"https://lightview.dev/sensors/sensor2"})});
22
+ await sensor1;
23
+ await sensor2;
24
+ addEventListener("change",({variableName,value}) => {
25
+ const sensor = document.body.getElementById(variableName);
26
+ sensor.setValue(value);
27
+ });
28
+ </script>
29
+ </body>
30
+ </html>
@@ -0,0 +1,30 @@
1
+ const http = require("http"),
2
+ fs = require("fs"),
3
+ host = 'localhost',
4
+ port = 8000,
5
+ requestListener = async function (req, res) {
6
+ const path = `.${req.url}`;
7
+ res.setHeader("Access-Control-Allow-Origin","*");
8
+ res.setHeader("Access-Control-Allow-Methods", "GET,OPTIONS");
9
+ res.setHeader("Access-Control-Allow-Headers", "*");
10
+ res.setHeader("Content-Type", "application/json");
11
+ if(req.method==="OPTIONS") {
12
+ res.end();
13
+ return;
14
+ }
15
+ if(req.method==="GET") {
16
+ const value = `${40 + Math.round(60 * Math.random())}`;
17
+ console.log("GET",req.url,"<-",value);
18
+ res.setHeader("Content-Length", value.length);
19
+ res.write(value);
20
+ res.end();
21
+ return;
22
+ }
23
+ console.log(req.method);
24
+ },
25
+ server = http.createServer(requestListener);
26
+ server.listen(port, host, () => {
27
+ console.log(`Server is running on http://${host}:${port}`);
28
+ });
29
+
30
+