diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afe93bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +datagraph diff --git a/datagraph b/datagraph deleted file mode 100755 index 8e0afb0..0000000 Binary files a/datagraph and /dev/null differ diff --git a/static/js/app.mjs b/static/js/app.mjs index 9f01f59..725c087 100644 --- a/static/js/app.mjs +++ b/static/js/app.mjs @@ -257,6 +257,9 @@ export class App { }) }// }}} nodeDelete(nodeID) {// {{{ + const node = this.tree.treeNodes.get(nodeID) + const parentID = node.node.ParentID + fetch(`/nodes/delete/${nodeID}`) .then(data => data.json()) .then(json => { @@ -264,6 +267,8 @@ export class App { showError(json.Error) return } + + this.tree.updateNode(parseInt(parentID)) }) .catch(err => showError(err)) }// }}} @@ -283,36 +288,13 @@ export class App { return } - const parentTreenode = this.tree.treeNodes.get(newParentID) - const node = await this.tree.fetchNodes(newParentID) - parentTreenode.node = node - parentTreenode.updateExpandImages() - - const newParentElement = this.tree.treeNodes.get(newParentID).children - for (const n of nodes) { - const movedTreeNode = this.tree.treeNodes.get(n.ID) - newParentElement.append(movedTreeNode.element) - - // Moved nodes' parents are updated to remove the moved nodes from their children. - const treenode = this.tree.treeNodes.get(n.ParentID) - const node = await this.tree.fetchNodes(n.ParentID) - treenode.node = node - treenode.updateExpandImages() - - // Children are resorted to get the moved node into correct order. - /* - this.tree.sortChildren(parentTreenode.node.Children) - for (const c of parentTreenode.node.Children) { - const treenode = this.tree.treeNodes.get(c.ID) - if (treenode) - parentTreenode.children.append(treenode.element) - } - */ - - // The moved node is updated with its new parent ID for future moves. - movedTreeNode.node.ParentID = newParentID - } + const updateParents = new Map() + for (const n of nodes) + updateParents.set(n.ParentID, true) + updateParents.set(newParentID, true) + for (const nodeID of updateParents.keys()) + this.tree.updateNode(nodeID) }) .catch(err => showError(err)) }// }}} @@ -465,10 +447,10 @@ export class Tree { this.fetchNodes(0) .then(node => { const top = document.getElementById('nodes') - const topNode = new TreeNode(node) - this.treeNodes.set(node.ID, topNode) + const topNode = this.treeNodes.get(0) + topNode.expanded = true top.appendChild(topNode.render()) - this.updateNode(0) + //this.updateNode(0) }) .catch(err => showError(err)) }// }}} @@ -492,6 +474,28 @@ export class Tree { reject(json.Error) return } + + // Make sure treenodes are updated with the latest fetched data. + // The parent node is processed after the children, since the render function needs to have the children already available. + const nodes = [] + nodes.push(...json.Nodes.Children) + nodes.push(json.Nodes) + + for (const n of nodes) { + let treenode = this.treeNodes.get(n.ID) + if (treenode === undefined) { + treenode = new TreeNode(this, n) + treenode.render() + this.treeNodes.set(n.ID, treenode) + } else { + // Since the depth is set to 1, the childrens' children array will be empty. + // If children have been fetched, these should be kept. + if (n.NumChildren > 0 && n.Children.length == 0) + n.Children = treenode.node.Children + treenode.node = n + } + } + resolve(json.Nodes) }) }) @@ -503,41 +507,13 @@ export class Tree { // If not found, created and added. // // Newly created nodes are found and added, existing but renamed nodes are modified, and unchanged are left as is. - this.fetchNodes(nodeID) + this.fetchNodes(nodeID, true) .then(node => { const thisTreeNode = this.treeNodes.get(nodeID) - thisTreeNode.childrenFetched = true - thisTreeNode.node = node - thisTreeNode.updateExpandImages() - thisTreeNode.toggleExpand(true) + thisTreeNode.render() - // Children are sorted according to type and name. - this.sortChildren(node.Children) - - // Deleted or moved children - for (const c of thisTreeNode.children.children) { - const nodeID = parseInt(c.dataset.nodeId) - const nodeStillExist = node.Children.some(n => n.ID === nodeID) - if (!nodeStillExist) { - c.remove() - mbus.dispatch('NODE_REMOVED', nodeID) - } - - } - - // Update or add children - for (const n of node.Children) { - if (this.treeNodes.has(n.ID)) { - const treenode = this.treeNodes.get(n.ID) - treenode.node = n - treenode.element.querySelector('.name').innerText = n.Name - treenode.updateExpandImages() - } else { - const treenode = new TreeNode(n) - this.treeNodes.set(n.ID, treenode) - thisTreeNode.children.appendChild(treenode.render()) - } - } + for (const n of thisTreeNode.node.Children) + this.treeNodes.get(n.ID)?.render() resolve() @@ -568,82 +544,96 @@ export class Tree { } export class TreeNode { - constructor(data) {// {{{ + constructor(tree, data) {// {{{ + this.tree = tree this.node = data this.childrenFetched = false this.element = null this.children = null + this.nameElement = null this.expandEventListenerAdded = false + this.expanded = false }// }}} - render() {// {{{ - const nodeHTML = ` -
-
-
${this.name()}
-
- ` + if (this.element === null) { + this.element = document.createElement('div') + this.element.classList.add('node') + this.element.setAttribute('data-node-id', this.node.ID) - const div = document.createElement('div') - div.classList.add('node') - div.setAttribute('data-node-id', this.node.ID) - div.innerHTML = nodeHTML + const nodeHTML = ` +
+
+
+
+ ` + this.element.innerHTML = nodeHTML - this.children = div.querySelector('.children') - this.expandImg = div.querySelector('.expand-status img') + this.nameElement = this.element.querySelector('.name') + this.children = this.element.querySelector('.children') + this.expandImg = this.element.querySelector('.expand-status img') + this.expandStatus = this.element.querySelector('.expand-status img') - div.querySelector('.name').addEventListener('click', event => { - if (!event.shiftKey) - mbus.dispatch('NODE_SELECTED', this.node.ID) - else - this.element.classList.toggle('marked') - event.stopPropagation() - }) + this.nameElement.addEventListener('click', event => this.clickNode(event)) + } // data.NumChildren is set regardless of having fetched the children or not. - this.expandStatus = div.querySelector('.expand-status img') this.updateExpandImages() if (this.node.TypeIcon) { - const img = div.querySelector('.type-icon img') + const img = this.element.querySelector('.type-icon img') img.setAttribute('src', `/images/${_VERSION}/node_modules/@mdi/svg/svg/${this.node.TypeIcon}.svg`) } - this.element = div - return div + this.nameElement.innerText = this.name() + + this.tree.sortChildren(this.node.Children) + const children = [] + for (const c of this.node.Children) + children.push(this.tree.treeNodes.get(c.ID).element) + + this.children.replaceChildren(...children) + + return this.element }// }}} + name() {// {{{ if (this.node.TypeName === 'root_node') return 'Start' return this.node.Name }// }}} + clickNode(event) {// {{{ + if (!event.shiftKey) + mbus.dispatch('NODE_SELECTED', this.node.ID) + else + this.element.classList.toggle('marked') + event.stopPropagation() + }// }}} hasChildren() {// {{{ return this.node.NumChildren > 0 }// }}} updateExpandImages() {// {{{ if (this.hasChildren()) { - this.expandStatus.setAttribute('src', `/images/${_VERSION}/node_modules/@mdi/svg/svg/plus-box-outline.svg`) + if (this.expanded) + this.element.classList.add('expanded') + else + this.element.classList.remove('expanded') + + this.expandStatus.setAttribute('src', `/images/${_VERSION}/node_modules/@mdi/svg/svg/${this.expanded ? 'minus-box-outline.svg' : 'plus-box-outline.svg'}`) if (!this.expandEventListenerAdded) { this.expandStatus.addEventListener('click', () => this.toggleExpand()) this.expandEventListenerAdded = true } - } else { + } else this.expandStatus.setAttribute('src', `/images/${_VERSION}/node_modules/@mdi/svg/svg/circle-medium.svg`) - } }// }}} toggleExpand(expanded) {// {{{ - const node = this.element - if (expanded === undefined) - node?.classList.toggle('expanded') - else if (expanded === true) - node?.classList.add('expanded') + this.expanded = !this.expanded else - node?.classList.remove('expanded') + this.expanded = expanded - const img = node?.classList.contains('expanded') ? 'minus-box-outline' : 'plus-box-outline' - this.expandStatus.setAttribute('src', `/images/${_VERSION}/node_modules/@mdi/svg/svg/${img}.svg`) + this.updateExpandImages() if (!this.childrenFetched && this.node.NumChildren > 0 && this.node.Children.length == 0) { mbus.dispatch('NODE_EXPAND', this)