Updated the native treenode to a custom HTML element
This commit is contained in:
parent
d9c82868ab
commit
e2b20816c2
3 changed files with 110 additions and 48 deletions
57
static/js/lib/custom_html_element.mjs
Normal file
57
static/js/lib/custom_html_element.mjs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
export class CustomHTMLElement extends HTMLElement {
|
||||
constructor() {// {{{
|
||||
super()
|
||||
|
||||
this.appendChild(this.constructor.tmpl.content.cloneNode(true))
|
||||
|
||||
this.querySelectorAll('*').forEach(el => {
|
||||
const field = el.dataset.field
|
||||
if (field !== undefined) {
|
||||
const fieldName = this.toElementName('field', field)
|
||||
this[fieldName] = el
|
||||
}
|
||||
|
||||
const name = el.dataset.el
|
||||
if (name !== undefined) {
|
||||
const elName = this.toElementName('el', name)
|
||||
this[elName] = el
|
||||
el.classList.add('el-' + name)
|
||||
}
|
||||
})
|
||||
}// }}}
|
||||
toElementName(prefix, str) {// {{{
|
||||
str = prefix + '-' + str
|
||||
return str.replace(/-(id|[a-z])/g, match => match.toUpperCase().replace('-', ''))
|
||||
}// }}}
|
||||
}
|
||||
|
||||
export class StupidPreactCustomHTMLElement extends HTMLElement {
|
||||
constructor() {// {{{
|
||||
super()
|
||||
|
||||
// Stupid stuff because of Preact.
|
||||
this.clonedNodes = this.constructor.tmpl.content.cloneNode(true)
|
||||
this.clonedNodes.querySelectorAll('*').forEach(el => {
|
||||
const field = el.dataset.field
|
||||
if (field !== undefined) {
|
||||
const fieldName = this.toElementName('field', field)
|
||||
this[fieldName] = el
|
||||
}
|
||||
|
||||
const name = el.dataset.el
|
||||
if (name !== undefined) {
|
||||
const elName = this.toElementName('el', name)
|
||||
this[elName] = el
|
||||
el.classList.add('el-' + name)
|
||||
}
|
||||
})
|
||||
}// }}}
|
||||
toElementName(prefix, str) {// {{{
|
||||
str = prefix + '-' + str
|
||||
return str.replace(/-(id|[a-z])/g, match => match.toUpperCase().replace('-', ''))
|
||||
}// }}}
|
||||
connectedCallback() {// {{{
|
||||
// Stupid stuff because of Preact.
|
||||
this.appendChild(this.clonedNodes)
|
||||
}// }}}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { signal } from 'preact/signals'
|
|||
import htm from 'htm'
|
||||
import { Node, NodeUI } from 'node'
|
||||
import { ROOT_NODE } from 'node_store'
|
||||
import { TreeNative, TreeNodeNative } from 'tree'
|
||||
import { TreeNative } from 'tree'
|
||||
const html = htm.bind(h)
|
||||
|
||||
export class Notes2 extends Component {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { ROOT_NODE } from 'node_store'
|
||||
import { CustomHTMLElement } from './lib/custom_html_element.mjs'
|
||||
|
||||
export class TreeNative {
|
||||
constructor() {// {{{
|
||||
|
|
@ -33,7 +34,7 @@ export class TreeNative {
|
|||
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,24 +327,35 @@ 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}`, () => {
|
||||
this.render(true)
|
||||
|
|
@ -356,71 +368,63 @@ export class TreeNodeNative {
|
|||
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()
|
||||
this.children_populated = true
|
||||
}//}}}
|
||||
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)
|
||||
}
|
||||
|
||||
// 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())
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue