import { h, Component, createRef } from 'preact' import { signal } from 'preact/signals' import htm from 'htm' import { API } from 'api' import { Node } from 'node' const html = htm.bind(h) export class Notes2 { constructor() {//{{{ this.startNode = null this.tree = createRef() this.setStartNode() }//}}} render() {//{{{ return html` <${Tree} ref=${this.tree} app=${this} /> ` }//}}} setStartNode() {//{{{ let urlParams = new URLSearchParams(window.location.search) let nodeID = urlParams.get('node') this.startNode = new Node(this, nodeID ? parseInt(nodeID) : 0) }//}}} treeGet() {//{{{ const req = {} API.query('POST', '/node/tree', req) .then(response => { console.log(response.Nodes) nodeStore.add(response.Nodes) }) .catch(e => console.log(e.type, e.error)) }//}}} } class Tree extends Component { constructor(props) {//{{{ super(props) this.treeNodes = {} this.treeNodeComponents = {} this.treeTrunk = [] this.selectedTreeNode = null this.retrieve() }//}}} render({ app }) {//{{{ let renderedTreeTrunk = this.treeTrunk.map(node => { this.treeNodeComponents[node.ID] = createRef() return html`<${TreeNode} key=${"treenode_" + node.ID} tree=${this} node=${node} ref=${this.treeNodeComponents[node.ID]} selected=${node.ID == app.startNode.ID} />` }) return html`
${renderedTreeTrunk}
` }//}}} retrieve(callback = null) {//{{{ nodeStore.getTreeNodes() .then(res => { this.treeNodes = {} this.treeNodeComponents = {} this.treeTrunk = [] this.selectedTreeNode = null // A tree of nodes is built. This requires the list of nodes // returned from the server to be sorted in such a way that // a parent node always appears before a child node. // The server uses a recursive SQL query delivering this. res.forEach(nodeData => { let node = new Node( this, nodeData.ID, ) node.Children = [] node.Crumbs = [] node.Files = [] node.Level = nodeData.Level node.Name = nodeData.Name node.ParentID = nodeData.ParentID node.Updated = nodeData.Updated node.UserID = nodeData.UserID this.treeNodes[node.ID] = node if (node.ParentID == 0) this.treeTrunk.push(node) else if (this.treeNodes[node.ParentID] !== undefined) this.treeNodes[node.ParentID].Children.push(node) }) // When starting with an explicit node value, expanding all nodes // on its path gives the user a sense of location. Not necessarily working // as the start node isn't guaranteed to have returned data yet. // XXX this.crumbsUpdateNodes() this.forceUpdate() if (callback) callback() }) .catch(e => { console.log(e); console.log(e.type, e.error); alert(e.error) }) }//}}} setSelected(node) {//{{{ if (this.selectedTreeNode) this.selectedTreeNode.selected.value = false this.selectedTreeNode = this.treeNodeComponents[node.ID].current this.selectedTreeNode.selected.value = true this.selectedTreeNode.expanded.value = true this.expandToTrunk(node.ID) }//}}} crumbsUpdateNodes(node) {//{{{ this.props.app.startNode.Crumbs.forEach(crumb => { // Start node is loaded before the tree. let node = this.treeNodes[crumb.ID] if (node) node._expanded = true // Tree is done before the start node. let component = this.treeNodeComponents[crumb.ID] if (component && component.current) component.current.expanded.value = true }) // Will be undefined when called from tree initialization // (as tree nodes aren't rendered yet) if (node !== undefined) this.setSelected(node) }//}}} expandToTrunk(nodeID) {//{{{ let node = this.treeNodes[nodeID] if (node === undefined) return node = this.treeNodes[node.ParentID] while (node !== undefined) { this.treeNodeComponents[node.ID].current.expanded.value = true node = this.treeNodes[node.ParentID] } }//}}} } class TreeNode extends Component { constructor(props) {//{{{ super(props) this.selected = signal(props.selected) this.expanded = signal(this.props.node._expanded) }//}}} render({ tree, node }) {//{{{ let children = node.Children.map(node => { tree.treeNodeComponents[node.ID] = createRef() return html`<${TreeNode} key=${"treenode_" + node.ID} tree=${tree} node=${node} ref=${tree.treeNodeComponents[node.ID]} selected=${node.ID == tree.props.app.startNode.ID} />` }) let expandImg = '' if (node.Children.length == 0) expandImg = html`` else { if (this.expanded.value) expandImg = html`` else expandImg = html`` } let selected = (this.selected.value ? 'selected' : '') return html`
this.expanded.value ^= true}>${expandImg}
window._app.current.nodeUI.current.goToNode(node.ID)}>${node.Name}
${children}
` }//}}} }