Reworked tree rendering and moving
This commit is contained in:
parent
c7b0823900
commit
d450418bf6
3 changed files with 91 additions and 100 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
datagraph
|
||||
BIN
datagraph
BIN
datagraph
Binary file not shown.
|
|
@ -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 = `
|
||||
<div class="expand-status"><img /></div>
|
||||
<div class="type-icon"><img /></div>
|
||||
<div class="name">${this.name()}</div>
|
||||
<div class="children"></div>
|
||||
`
|
||||
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 = `
|
||||
<div class="expand-status"><img /></div>
|
||||
<div class="type-icon"><img /></div>
|
||||
<div class="name"></div>
|
||||
<div class="children"></div>
|
||||
`
|
||||
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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue