Updated the native treenode to a custom HTML element

This commit is contained in:
Magnus Åhall 2026-04-29 14:03:08 +02:00
parent d9c82868ab
commit e2b20816c2
3 changed files with 110 additions and 48 deletions

View file

@ -1,4 +1,5 @@
import { ROOT_NODE } from 'node_store'
import { CustomHTMLElement } from './lib/custom_html_element.mjs'
export class TreeNative {
constructor() {// {{{
@ -26,14 +27,14 @@ export class TreeNative {
const treeEl = tmpl.content.getElementById('tree-nodes')
treeEl.addEventListener('keydown', event=>this.keyHandler(event))
tmpl.content.querySelector('.icons .search').addEventListener('click', ()=>_mbus.dispatch('op-search'))
tmpl.content.querySelector('.icons .sync').addEventListener('click', ()=>_sync.run())
treeEl.addEventListener('keydown', event => this.keyHandler(event))
tmpl.content.querySelector('.icons .search').addEventListener('click', () => _mbus.dispatch('op-search'))
tmpl.content.querySelector('.icons .sync').addEventListener('click', () => _sync.run())
tmpl.content.getElementById('logo').addEventListener('click', ()=>_app.goToNode(ROOT_NODE, false, false))
tmpl.content.getElementById('logo').addEventListener('click', () => _app.goToNode(ROOT_NODE, false, false))
for (const node of this.treeTrunk) {
const treenode = new TreeNodeNative(this, node)
const treenode = new N2TreeNode(this, node)
this.treeNodeComponents[node.UUID] = treenode
treeEl.appendChild(treenode.render())
}
@ -326,47 +327,46 @@ export class TreeNative {
}
// The ROOT_NODE for example hasn't got a treenode.
treenode?.element.scrollIntoView({ block: 'nearest' })
treenode?.scrollIntoView({ block: 'nearest' })
}// }}}
}
export class TreeNodeNative {
export class N2TreeNode extends CustomHTMLElement {
static {// {{{
this.tmpl = document.createElement('template')
this.tmpl.innerHTML = `
<div data-el="expand-toggle" class="expand-toggle">
<img data-el="expand">
</div>
<div data-el="name" class="name"></div>
<div data-el="children" class="children"></div>
`
}// }}}
constructor(tree, node, parent) {//{{{
super()
this.classList.add('node')
this.tree = tree
this.node = node
this.parent = parent
this.element = document.createElement('div')
this.element.classList.add('node')
this.icon_expand = document.createElement('img')
this.children_populated = false
this.rendered = false
this.createElements()
this.elExpandToggle.addEventListener('click', () => this.tree.setNodeExpanded(this.node, !this.tree.getNodeExpanded(this.node.UUID)))
this.elName.addEventListener('click', () => _mbus.dispatch('TREE_NODE_SELECTED', this.node))
_mbus.subscribe(`NODE_CHILDREN_FETCHED_${node.UUID}`, ()=>{
_mbus.subscribe(`NODE_CHILDREN_FETCHED_${node.UUID}`, () => {
this.render(true)
})
_mbus.subscribe(`NODE_EXPAND_${node.UUID}`, state=>{
_mbus.subscribe(`NODE_EXPAND_${node.UUID}`, state => {
this.render(true)
})
if (this.node.Level === 0 || this.tree.getNodeExpanded(this.node.UUID))
this.fetchChildren()
}//}}}
createElements() {// {{{
this.element.innerHTML = `
<div class="expand-toggle"></div>
<div class="name" ></div>
<div class="children"></div>
`
this.element.children[0].addEventListener('click', ()=>this.tree.setNodeExpanded(this.node, !this.tree.getNodeExpanded(this.node.UUID)))
this.element.children[0].appendChild(this.icon_expand)
this.element.children[1].addEventListener('click', ()=>_mbus.dispatch('TREE_NODE_SELECTED', this.node))
}// }}}
async fetchChildren() {//{{{
await this.node.fetchChildren()
@ -374,53 +374,57 @@ export class TreeNodeNative {
}//}}}
render(force_update) {//{{{
if (this.rendered && force_update !== true)
return this.element
return this
// Fetch the next level of children if the parent tree node is expanded and our children thus will be visible.
const expanded = this.node.Children.length > 0 && this.tree.getNodeExpanded(this.node.UUID)
const selected = this.tree.isSelected(this.node) ? 'selected' : ''
if (!this.children_populated && this.tree.getNodeExpanded(this.parent?.node.UUID)) {
this.node.fetchChildren().then(()=>this.children_populated = true)
this.node.fetchChildren().then(() => this.children_populated = true)
}
// Update the name and selected status
this.element.children[1].innerText = this.node.get('Name')
this.element.children[1].className = `name ${selected}`
this.elName.innerText = this.node.get('Name')
if (this.tree.isSelected(this.node))
this.elName.classList.add('selected')
else
this.elName.classList.remove('selected')
// Update expansion state
this.element.children[2].className = `children ${expanded ? 'expanded' : 'collapsed'}`
// The expand icon <img> is cached to not get a flickering when re-rendering.
if (this.icon_expand === null)
this.icon_expand = document.createElement('img')
if (expanded) {
this.elChildren.classList.add('expanded')
this.elChildren.classList.remove('collapsed')
} else {
this.elChildren.classList.remove('expanded')
this.elChildren.classList.add('collapsed')
}
// The expand icon <img> is only changed to not get a flickering when re-rendering.
if (this.node.Children.length === 0)
this.setImgSrc(this.icon_expand, `/images/${window._VERSION}/leaf.svg`)
this.setImgSrc(this.elExpand, `/images/${window._VERSION}/leaf.svg`)
else if (this.tree.getNodeExpanded(this.node.UUID))
this.setImgSrc(this.icon_expand, `/images/${window._VERSION}/expanded.svg`)
this.setImgSrc(this.elExpand, `/images/${window._VERSION}/expanded.svg`)
else
this.setImgSrc(this.icon_expand, `/images/${window._VERSION}/collapsed.svg`)
this.setImgSrc(this.elExpand, `/images/${window._VERSION}/collapsed.svg`)
// Should children be rendered?
this.element.children[2].innerHTML = ''
this.elChildren.innerHTML = ''
let children = []
if (expanded)
children = this.node.Children.map(node => {
let treenode = this.tree.treeNodeComponents[node.UUID]
if (treenode === undefined) {
treenode = new TreeNodeNative(this.tree, node, this)
treenode = new N2TreeNode(this.tree, node, this)
this.tree.treeNodeComponents[node.UUID] = treenode
}
return treenode
//return html`<${TreeNode} key=${`treenode_${node.UUID}`} tree=${tree} node=${node} parent=${this} ref=${tree.treeNodeComponents[node.UUID]} selected=${node.UUID === tree.props.app.startNode?.UUID} />`
})
for(const c of children)
this.element.children[2].appendChild(c.render())
for (const c of children)
this.elChildren.appendChild(c.render())
this.rendered = true
return this.element
return this
}//}}}
setImgSrc(img, newSrc) {// {{{
@ -429,5 +433,6 @@ export class TreeNodeNative {
img.setAttribute('src', newSrc)
}// }}}
}
customElements.define('n2-treenode', N2TreeNode)
// vim: foldmethod=marker