author | Alan Dipert
<alan@dipert.org> 2021-06-08 05:25:44 UTC |
committer | Alan Dipert
<alan@dipert.org> 2021-06-08 05:25:44 UTC |
parent | 8c2579aa84559b223038f75e05c5e405777df9f2 |
el.mjs | +103 | -29 |
reactives.mjs | +3 | -0 |
todo.mjs | +9 | -5 |
diff --git a/el.mjs b/el.mjs index 1979dfa..93503ba 100644 --- a/el.mjs +++ b/el.mjs @@ -1,5 +1,5 @@ import { _ } from './datalog.mjs'; -import { Input, Formula, input, database, view, watch } from './reactives.mjs'; +import { Input, Formula, input, formula, database, view, watch } from './reactives.mjs'; function el(tag, attrs, kids) { let el = document.createElement(tag); @@ -22,9 +22,8 @@ function el(tag, attrs, kids) { })(kid); el.appendChild(textNode); } else if (kid instanceof EntityNodes) { - kid.setCallback(([func, ...args]) => { - func.apply(el, args); - }) + // TODO error if any EntityNodes siblings for now + kid.bind(el); } else { el.appendChild(kid); } @@ -77,47 +76,122 @@ function makeAttrCells(db, eidCell) { }) } +function insertSorted(el, nodeToAttrs, sortAttr, node) { + if (!el.children.length) { + el.appendChild(node); + } else if (nodeToAttrs.get(el.children[el.children.length-1])[sortAttr].value <= nodeToAttrs.get(node)[sortAttr].value){ + el.appendChild(node); + } else { + for (let i = 0; i < el.children.length; i++) { + if (nodeToAttrs.get(el.children[i])[sortAttr].value >= nodeToAttrs.get(node)[sortAttr].value) { + el.insertBefore(node, el.children[i]); + break; + } + } + } +} + class EntityNodes { - constructor(db, makeNode) { + constructor(db, makeNode, sortAttr = input("_eid")) { this.db = db; this.makeNode = makeNode; - this.pool = []; - this.nodeToEidCell = new Map(); - this.eidToNode = new Map(); - this.bufferedCommands = []; - this.callback = command => this.bufferedCommands.push(command); + this.sortAttr = sortAttr; + } + bind(el) { + let eidToNode = new Map(), + nodeToEidCell = new Map(), + nodeToAttrs = new Map(), + eidToInsertFormula = new Map(), + pool = []; watch((added, removed) => { + let node; for (let [eid] of removed) { - let node = this.eidToNode.get(eid); - this.eidToNode.delete(eid); - this.callback([Node.prototype.removeChild, node]); + node = eidToNode.get(eid); + el.removeChild(node); + eidToNode.delete(eid); + eidToInsertFormula.get(eid).detach(); + eidToInsertFormula.delete(eid); + pool.push(node); } for (let [eid] of added) { - let node; - if (this.pool.length) { - node = this.pool.pop(); + if (pool.length) { + node = pool.pop(); nodeToEidCell.get(node).set(eid); + eidToNode.set(eid, node); } else { let eidCell = input(eid), - attrs = makeAttrCells(db, eidCell); + attrs = makeAttrCells(this.db, eidCell); node = this.makeNode(attrs); - this.eidToNode.set(eid, node); - this.nodeToEidCell.set(node, eidCell); + eidToNode.set(eid, node); + nodeToEidCell.set(node, eidCell); + nodeToAttrs.set(node, attrs); } - this.callback([Node.prototype.appendChild, node]); + eidToInsertFormula.set(eid, null); } - })(view({find: [_.eid], where: [[_.eid]]})(db)) - } - setCallback(f) { - this.callback = f; - while (this.bufferedCommands.length) { - this.callback(this.bufferedCommands.pop()); - } + })(view({find: [_.eid], where: [[_.eid]]})(this.db)); + watch((oldv, newv) => { + for (let [eid, f] of [...eidToInsertFormula]) { + if (el.contains(eidToNode.get(eid))) { + el.removeChild(eidToNode.get(eid)); + } + f?.detach(); + eidToInsertFormula.set(eid, formula(attr => { + insertSorted(el, nodeToAttrs, newv, eidToNode.get(eid)); + })(nodeToAttrs.get(eidToNode.get(eid))[newv])); + } + })(this.sortAttr); } } +// class EntityNodes { +// constructor(db, makeNode) { +// this.db = db; +// this.makeNode = makeNode; +// this.pool = []; +// this.nodeToEidCell = new Map(); +// this.eidToNode = new Map(); +// this.bufferedCommands = []; +// this.callback = command => this.bufferedCommands.push(command); +// watch((added, removed) => { +// for (let [eid] of removed) { +// let node = this.eidToNode.get(eid); +// this.eidToNode.delete(eid); +// this.callback([Node.prototype.removeChild, node]); +// } +// for (let [eid] of added) { +// let node; +// if (this.pool.length) { +// node = this.pool.pop(); +// nodeToEidCell.get(node).set(eid); +// } else { +// let eidCell = input(eid), +// attrs = makeAttrCells(db, eidCell); +// node = this.makeNode(attrs); +// this.eidToNode.set(eid, node); +// this.nodeToEidCell.set(node, eidCell); +// } +// this.callback([Node.prototype.appendChild, node]); +// } +// })(view({find: [_.eid], where: [[_.eid]]})(db)) +// } +// setCallback(f) { +// this.callback = f; +// while (this.bufferedCommands.length) { +// this.callback(this.bufferedCommands.pop()); +// } +// } +// } + function entities(db, makeNode) { - return new EntityNodes(db, makeNode); + return new EntityNodes(db, makeNode, input("_eid")); +} + +function entitiesBy(db, sortAttr, makeNode) { + if (!(sortAttr instanceof Input) + && !(sortAttr instanceof Formula )) { + sortAttr = input(sortAttr); + } + return new EntityNodes(db, makeNode, sortAttr) } // function mapEntities(parent, db, eidView, attrs, makeChild) { @@ -176,7 +250,7 @@ function entities(db, makeNode) { // return parent; // } -export { $el, entities }; +export { $el, entities, entitiesBy }; // function renderInto(parent, view, templateFunction) { // } diff --git a/reactives.mjs b/reactives.mjs index 97a4ac6..06d7280 100644 --- a/reactives.mjs +++ b/reactives.mjs @@ -83,6 +83,9 @@ class Formula { static isReactiveSource(x) { return x instanceof Input || x instanceof Formula; } + detach() { + this.sources.forEach(source => removeEdge(depGraph, source, this)); + } sourceValues() { return this.sources.map(x => Formula.isReactiveSource(x) ? x.value : x); } diff --git a/todo.mjs b/todo.mjs index 91c2460..cd55cf9 100644 --- a/todo.mjs +++ b/todo.mjs @@ -1,6 +1,6 @@ import { query, _, ANY, paths } from './datalog.mjs'; import { input, formula, database, view } from './reactives.mjs'; -import { $el, entities } from './el.mjs'; +import { $el, entities, entitiesBy } from './el.mjs'; let todos = database([ [0, "text", "do the thing"], @@ -22,7 +22,9 @@ let visibleTodos = view({ ] })(todos, showStatus); -let todoList = $el.ul( +window.by = input("_eid"); + +let todoList = $el.div( $el.select({ onchange: ({target: {value}}) => { showStatus.set(value === "any" ? ANY.VALUE : value); @@ -32,9 +34,11 @@ let todoList = $el.ul( $el.option({value: "ready"}, "Ready"), $el.option({value: "done"}, "Done") ), - entities(visibleTodos, attrs => $el.li( - formula((eid, text) => `${eid} - ${text}`)(attrs._eid, attrs.text) - )) + $el.ul( + entitiesBy(visibleTodos, window.by, attrs => $el.li( + formula((eid, status, text) => `${eid} - ${status} - ${text}`)(attrs._eid, attrs.status, attrs.text) + )) + ) ); document.addEventListener("DOMContentLoaded", () => {