git » hoplite.git » commit bbc73d9

major progro

author Alan Dipert
2021-06-13 05:37:45 UTC
committer Alan Dipert
2021-06-13 05:37:45 UTC
parent 8b2bf36ce1904f5a1efa058584043ad43c653135

major progro

el.mjs +22 -13
hntr2.mjs +87 -8
reactives.mjs +0 -65
scratch.js +65 -0

diff --git a/el.mjs b/el.mjs
index 399def1..1cc9517 100644
--- a/el.mjs
+++ b/el.mjs
@@ -15,7 +15,9 @@ function el(tag, attrs, kids) {
       el.setAttribute(k, v);
     }
   }
-  for (const kid of kids) {
+  let offset = 0;
+  for (let i = 0; i < kids.length; i++) {
+    let kid = kids[i];
     if (kid instanceof Input || kid instanceof Formula) {
       let node,
           placeholder = document.createComment("placeholder");
@@ -39,12 +41,19 @@ function el(tag, attrs, kids) {
         }
       })(kid);
       el.appendChild(node);
+    } else if (kid instanceof Promise) {
+      el.appendChild(placeholder);
+      kid.then(x => el.replaceChild(x, placeholder));
     } else if (kid instanceof EntityNodes) {
       // TODO error if any EntityNodes siblings for now
-      kid.bind(el);
+      kid.bind(el, offset);
+      if ((kids.length - i) > 1) {
+        throw new Error(`entities() must be last child`);
+      }
     } else {
       el.appendChild(kid);
     }
+    offset++;
   }
   return el;
 }
@@ -104,16 +113,16 @@ function getComparator(name) {
   }
 }
 
-function splice(el, i, node) {
-  if (el.children.length === 0 || el.children.length === i) {
+function splice(el, offset, i, node) {
+  if (el.children.length === offset || el.children.length === offset + i) {
     el.appendChild(node);
   } else {
-    el.insertBefore(node, el.children[i]);
+    el.insertBefore(node, el.children[offset + i]);
   }
 }
 
-function binaryInsert(el, nodeToAttrs, sortAttr, node, cmpFunc) {
-  let left = 0,
+function binaryInsert(el, offset, nodeToAttrs, sortAttr, node, cmpFunc) {
+  let left = offset,
       right = el.children.length,
       middle,
       cmp;
@@ -134,7 +143,7 @@ function binaryInsert(el, nodeToAttrs, sortAttr, node, cmpFunc) {
       throw new Error(`Bad compare value: ${cmp}`);
     }
   }
-  splice(el, left, node)
+  splice(el, offset, left, node);
 }
 
 function asCell(val, defaultVal) {
@@ -154,7 +163,7 @@ class EntityNodes {
     this.sortDir = asCell(sortDir, "asc");
     this.makeNode = makeNode;
   }
-  bind(el) {
+  bind(el, offset = 0) {
     let eidToNode = new Map(),
         nodeToEidCell = new Map(),
         nodeToAttrs = new Map(),
@@ -198,19 +207,19 @@ class EntityNodes {
         //   binaryInsert(el, nodeToAttrs, this.sortAttr.value, node, getComparator(this.sortDir.value));
         // })(nodeToAttrs.get(node)[this.sortAttr.value]));
         eidToInserter.set(eid, watch(() => {
-          binaryInsert(el, nodeToAttrs, this.sortAttr.value, node, getComparator(this.sortDir.value));
+          binaryInsert(el, offset, nodeToAttrs, this.sortAttr.value, node, getComparator(this.sortDir.value));
         })(nodeToAttrs.get(node)[this.sortAttr.value]));
       }
     })(view({find: [_.eid], where: [[_.eid]]})(this.db));
     formula((sortAttr, sortDir) => {
-      while (el.children.length) {
-        el.removeChild(el.children[0]);
+      while (el.children.length - offset) {
+        el.removeChild(el.children[offset]);
       }
       for (let [eid, f] of [...eidToInserter]) {
         let node = eidToNode.get(eid);
         f?.detach();
         eidToInserter.set(eid, watch(() => {
-          binaryInsert(el, nodeToAttrs, sortAttr, node, getComparator(sortDir));
+          binaryInsert(el, offset, nodeToAttrs, sortAttr, node, getComparator(sortDir));
         })(nodeToAttrs.get(node)[sortAttr]));
       }
     })(this.sortAttr, this.sortDir);
diff --git a/hntr2.mjs b/hntr2.mjs
index e4be3b2..0f7a30c 100644
--- a/hntr2.mjs
+++ b/hntr2.mjs
@@ -1,21 +1,100 @@
-import { input, formula, cond, not, database, transaction } from './reactives.mjs';
+import { input, formula, watch, cond, not, database, transaction } from './reactives.mjs';
 import { $el, nbsp, entities } from './el.mjs';
 
 let user = input(),
-    loaded = input(false);
+    loaded = input(false),
+    db = database([
+      // [0, "site", "foo.com"]
+    ]);
+
+watch((added, removed) => {
+  console.log("added", [...added]);
+  console.log("removed", [...removed]);
+})(db);
 
 firebase.auth().onAuthStateChanged(result => {
   if (result) user.set(result)
   loaded.set(true);
 });
 
+function bindTo(input, cell) {
+  input.addEventListener("change", ({target: {value}}) => {
+    cell.set(value);
+  })
+  watch((oldv, newv) => {
+    if (!newv) input.value = "";
+  })(cell);
+  return input;
+}
+
+function record(...fields) {
+  return Object.fromEntries(fields.map(f => [f, input()]));
+}
+
+function recordClear(rec) {
+  transaction(() => {
+    Object.values(rec).forEach(c => c.set());
+  });
+}
+
+function recordTuples(rec, eid) {
+  return Object.entries(rec)
+    .filter(([f, c]) => c.value)
+    .map(([f, c]) => [eid, f, c.value]);
+}
+
+function deleteCredential(eid) {
+  db.remove([...db].filter(([e]) => e === eid));
+}
+
 function authed() {
-  return $el.p(
-    formula(u => `Signed in as ${u?.email}`)(user),
-    nbsp,
-    $el.a({href: "#", onclick: () => {
-      firebase.auth().signOut().then(() => user.set());
-    }}, "Sign out")
+  let rec = record("site", "username", "password", "note")
+  return $el.div(
+    $el.p(
+      formula(u => `Signed in as ${u?.email}`)(user),
+      nbsp,
+      $el.a({href: "#", onclick: () => {
+        firebase.auth().signOut().then(() => user.set());
+      }}, "Sign out")
+    ),
+    $el.div(
+      $el.table(
+        $el.tr(
+          $el.th("Action"),
+          $el.th("Site"),
+          $el.th("Username"),
+          $el.th("Password"),
+          $el.th("Note")
+        ),
+        $el.tr(
+          $el.td($el.input({
+            type: "button",
+            value: "Add",
+            onclick: () => {
+              db.add(recordTuples(rec, db.maxEid + 1));
+              recordClear(rec);
+            }
+          })),
+          $el.td(bindTo($el.input({type: "text"}), rec.site)),
+          $el.td(bindTo($el.input({type: "text"}), rec.username)),
+          $el.td(bindTo($el.input({type: "text"}), rec.password)),
+          $el.td(bindTo($el.input({type: "text"}), rec.note))
+        ),
+        entities(db, "_eid", "asc", attrs => {
+          return $el.tr(
+            $el.td($el.input({
+              type: "button",
+              value: "Delete",
+              onclick: () => deleteCredential(attrs._eid.value)
+            })),
+            $el.td(attrs.site),
+            $el.td(attrs.username),
+            $el.td(attrs.password),
+            $el.td(attrs.note)
+          )
+        })
+      )
+    )
   );
 }
 
diff --git a/reactives.mjs b/reactives.mjs
index b5931fb..61e0ef3 100644
--- a/reactives.mjs
+++ b/reactives.mjs
@@ -340,68 +340,3 @@ function and(...xs) {
 }
 
 export { Input, Formula, transaction, captureCreated, input, formula, database, view, watch, cond, not };
-
-// let A = input(100),
-//     B = input(200),
-//     C = formula(console.log)(A, B);
-
-// console.log("Setting A and B in succession")
-// A.set(A.value + 1);
-// B.set(B.value + 1);
-
-// setTimeout(() => transaction(() => {
-//   console.log("Setting A and B in a transaction")
-//   A.set(A.value + 1);
-//   B.set(B.value + 1);
-// }), 1000);
-
-// class Table extends Vertex {
-//   constructor(columnNames) {
-//     this.columnNames = columnNames;
-//     this.rows = new Map();
-//     this.lastAdded = new Set();
-//     this.lastRemoved = new Set();
-//   }
-//   insert(values) {
-//     // TODO JSON encode values and set rows to [...values] => {col1: val1...}
-//     //   Populate .lastAdded if appropriate
-//     // TODO Might need to modify propagate to call .flush or similar on walked, to clear lastAdded
-//     //   and lastRemoved after a propagation cycle
-//     // TODO values can be Refs
-//     return this;
-//   }
-//   select(columnNames, wherePredicate) {
-//     // TODO
-//     // Return a View wired up to this table to receive updates
-//   }
-//   sum(columnName) {
-//     // TODO
-//     // Return a Formula
-//   }
-// }
-
-// class View extends Vertex {
-// }
-
-//         A
-//        / \
-//       B   C--F--Q
-//        \ / \/
-//         D   E
-// let A = new Input(0),
-//     B = new Formula([A], () => {
-//       console.log("B updated");
-//     }),
-//     C = new Formula([A], () => {
-//       console.log("C updated");
-//     }),
-//     D = new Formula([B, C], () => {
-//       console.log("D updated");
-//     }),
-//     E = new Formula([C], () => {
-//       console.log("E updated");
-//     }),
-//     Q = new Input(0),
-//     F = new Formula([C, E, Q], () => {
-//       console.log("F updated");
-//     });
diff --git a/scratch.js b/scratch.js
new file mode 100644
index 0000000..455669b
--- /dev/null
+++ b/scratch.js
@@ -0,0 +1,65 @@
+
+// let A = input(100),
+//     B = input(200),
+//     C = formula(console.log)(A, B);
+
+// console.log("Setting A and B in succession")
+// A.set(A.value + 1);
+// B.set(B.value + 1);
+
+// setTimeout(() => transaction(() => {
+//   console.log("Setting A and B in a transaction")
+//   A.set(A.value + 1);
+//   B.set(B.value + 1);
+// }), 1000);
+
+// class Table extends Vertex {
+//   constructor(columnNames) {
+//     this.columnNames = columnNames;
+//     this.rows = new Map();
+//     this.lastAdded = new Set();
+//     this.lastRemoved = new Set();
+//   }
+//   insert(values) {
+//     // TODO JSON encode values and set rows to [...values] => {col1: val1...}
+//     //   Populate .lastAdded if appropriate
+//     // TODO Might need to modify propagate to call .flush or similar on walked, to clear lastAdded
+//     //   and lastRemoved after a propagation cycle
+//     // TODO values can be Refs
+//     return this;
+//   }
+//   select(columnNames, wherePredicate) {
+//     // TODO
+//     // Return a View wired up to this table to receive updates
+//   }
+//   sum(columnName) {
+//     // TODO
+//     // Return a Formula
+//   }
+// }
+
+// class View extends Vertex {
+// }
+
+//         A
+//        / \
+//       B   C--F--Q
+//        \ / \/
+//         D   E
+// let A = new Input(0),
+//     B = new Formula([A], () => {
+//       console.log("B updated");
+//     }),
+//     C = new Formula([A], () => {
+//       console.log("C updated");
+//     }),
+//     D = new Formula([B, C], () => {
+//       console.log("D updated");
+//     }),
+//     E = new Formula([C], () => {
+//       console.log("E updated");
+//     }),
+//     Q = new Input(0),
+//     F = new Formula([C, E, Q], () => {
+//       console.log("F updated");
+//     });