Compare commits

..

No commits in common. "697576cb34663fc9597558117249eb7999bbf186" and "83abc3f456e6b540a84856d8cd9e610fb7a825f3" have entirely different histories.

7 changed files with 91 additions and 83 deletions

View file

@ -92,7 +92,7 @@ button {
border-right: none; border-right: none;
} }
n2-sidebar { n2-tree {
display: none; display: none;
} }
@ -164,7 +164,7 @@ button {
border-right: 1px solid var(--line-color); border-right: 1px solid var(--line-color);
n2-sidebar { n2-tree {
.el-treenodes { .el-treenodes {
margin: 24px 32px 32px 32px; margin: 24px 32px 32px 32px;
} }
@ -508,7 +508,7 @@ dialog.op {
} }
#tree { #tree {
n2-sidebar { n2-tree {
.el-treenodes { .el-treenodes {
height: calc(100vh - 64px - 64px); height: calc(100vh - 64px - 64px);
margin: 0px; margin: 0px;

View file

@ -1,21 +1,16 @@
import { ROOT_NODE } from 'node_store' import { ROOT_NODE } from 'node_store'
import { CustomHTMLElement } from './lib/custom_html_element.mjs' import { CustomHTMLElement } from './lib/custom_html_element.mjs'
import { N2Sidebar } from 'sidebar' import { N2Tree } from 'tree'
import { Node } from 'node' import { Node } from 'node'
export class App { export class App {
constructor() {// {{{ constructor() {// {{{
this.currentNode = null this.currentNode = null
this.sidebar = new N2Sidebar() this.tree = new N2Tree()
this.crumbs = new N2Crumbs() this.crumbs = new N2Crumbs()
this.crumbsElement = document.getElementById('crumbs') this.crumbsElement = document.getElementById('crumbs')
this.nodeUI = document.getElementById('note') this.nodeUI = document.getElementById('note')
this.showNodeEventSequence = new ShowNodeEventSequence() // discard all GO_TO_NODE events
this.sidebar.render().then(sidebar => {
document.getElementById('tree').append(sidebar)
document.getElementById('tree-nodes')?.focus()
})
_mbus.subscribe('TREE_TRUNK_FETCHED', async () => { _mbus.subscribe('TREE_TRUNK_FETCHED', async () => {
// Subscribing to the start node existing after the tree trunk is // Subscribing to the start node existing after the tree trunk is
@ -23,8 +18,11 @@ export class App {
// root node itself, and the root node should be selected in the tree // root node itself, and the root node should be selected in the tree
// after it is rendered when the site is shown without UUID in the URL. // after it is rendered when the site is shown without UUID in the URL.
const startNode = await this.getStartNode() const startNode = await this.getStartNode()
_mbus.subscribe(`NODE_COMPONENT_EXIST_${startNode.UUID}`, () => {
this.goToNode(startNode.UUID, false, false)
})
document.getElementById('tree').append(await this.sidebar.render()) document.getElementById('tree').append(await this.tree.render())
document.getElementById('tree-nodes')?.focus() document.getElementById('tree-nodes')?.focus()
if (startNode.UUID == ROOT_NODE) if (startNode.UUID == ROOT_NODE)
@ -87,7 +85,7 @@ export class App {
if (document.activeElement.id === 'tree-nodes') { if (document.activeElement.id === 'tree-nodes') {
this.nodeUI.takeFocus() this.nodeUI.takeFocus()
} else { } else {
this.sidebar.focus() this.tree.focus()
} }
break break
@ -186,38 +184,17 @@ export class App {
node.reset() // any modifications are discarded. node.reset() // any modifications are discarded.
this.currentNode = node this.currentNode = node
this.sidebar.setSelected(node, dontExpand) this.tree.setSelected(node, dontExpand)
const ancestors = await nodeStore.getNodeAncestry(node) const ancestors = await nodeStore.getNodeAncestry(node)
_mbus.dispatch('CRUMBS_SET', ancestors, () => this.crumbsElement.replaceChildren(this.crumbs.render()))
_mbus.dispatch('NODE_UI_OPEN', node)
_mbus.dispatch('NODE_UNMODIFIED')
_mbus.dispatch('TREE_EXPANSION', { expand: false, when: 'narrow' })
// Scrolls node into view. // Scrolls node into view.
// makeVisible normally expands all ancestor nodes to make the whole chain visible. this.tree.makeVisible(node)
// This is a bad idea when quickly navigating the tree, since the arrow navigation
// has collapsed nodes which the event calling goToNode can come to undo, if the
// event processing lags behind.
await this.sidebar.makeVisible(node, ancestors, dontExpand)
_mbus.dispatch('CRUMBS_SET', ancestors, () => this.crumbsElement.replaceChildren(this.crumbs.render()))
_mbus.dispatch('NODE_UI_OPEN', { node, eventSequence: this.showNodeEventSequence.next() })
_mbus.dispatch('TREE_EXPANSION', { expand: false, when: 'narrow' })
_mbus.dispatch('NODE_UNMODIFIED')
}//}}} }//}}}
pageIsVisible(page) {// {{{
let classList = document.querySelector('#main-page').classList
return classList.contains(page)
}// }}}
}
class ShowNodeEventSequence {
constructor() {
this.seq = 0
}
next() {
return ++this.seq
}
current() {
return this.seq
}
} }
class N2Crumbs extends CustomHTMLElement { class N2Crumbs extends CustomHTMLElement {

View file

@ -199,7 +199,9 @@ export class NodeStore {
} }
Promise.all(hasChildrenPromises) Promise.all(hasChildrenPromises)
.then(() => resolve(nodes)) .then(() => {
resolve(nodes)
})
} }
req.onerror = (event) => reject(event.target.error) req.onerror = (event) => reject(event.target.error)
}) })

View file

@ -80,11 +80,8 @@ export class N2PageHistory extends CustomHTMLElement {
}) })
_mbus.subscribe('SHOW_PAGE', async (event) => { _mbus.subscribe('NODE_UI_OPEN', async (event) => {
if(event.detail.data.page != 'history') await this.useNode(event.detail.data)
return
await this.useNode(_app.nodeUI.node)
this.render() this.render()
}) })

View file

@ -42,8 +42,7 @@ export class N2PageNodeUI extends CustomHTMLElement {
this.marked = new MarkedPosition() this.marked = new MarkedPosition()
_mbus.subscribe('NODE_UI_OPEN', event => { _mbus.subscribe('NODE_UI_OPEN', event => {
console.log(event.detail.data.eventSequence, _app.showNodeEventSequence.current()) this.node = event.detail.data
this.node = event.detail.data.node
this.showMarkdown(true) this.showMarkdown(true)
this.render() this.render()
}) })
@ -377,6 +376,10 @@ export class Node {
this.Children[i]._parent = this this.Children[i]._parent = this
} }
// Notify the tree that all children are fetched and ready to process.
//_notes2.current.tree.fetchChildrenOn(this.UUID)
_mbus.dispatch(`NODE_CHILDREN_FETCHED_${this.UUID}`)
return this.Children return this.Children
}//}}} }//}}}
setHasChildren(v) {// {{{ setHasChildren(v) {// {{{

View file

@ -1,6 +1,7 @@
import { ROOT_NODE } from 'node_store' import { ROOT_NODE } from 'node_store'
import { CustomHTMLElement } from './lib/custom_html_element.mjs' import { CustomHTMLElement } from './lib/custom_html_element.mjs'
import { Color, Solver } from './lib/css_colorize.mjs' import { Color, Solver } from './lib/css_colorize.mjs'
import { Node } from './page_node.mjs'
// TreeExpandedHandler is responsible for collapsing or expanding // TreeExpandedHandler is responsible for collapsing or expanding
// the node tree, wide view or narrow "mobile" view. // the node tree, wide view or narrow "mobile" view.
@ -58,12 +59,12 @@ class TreeExpansionHandler {// {{{
} }
}// }}} }// }}}
export class N2Sidebar extends CustomHTMLElement { export class N2Tree extends CustomHTMLElement {
static {// {{{ static {// {{{
this.tmpl = document.createElement('template') this.tmpl = document.createElement('template')
this.tmpl.innerHTML = ` this.tmpl.innerHTML = `
<style> <style>
n2-sidebar { n2-tree {
#logo { #logo {
display: grid; display: grid;
grid-template-columns: 1fr min-content; grid-template-columns: 1fr min-content;
@ -144,21 +145,24 @@ export class N2Sidebar extends CustomHTMLElement {
treenode.render(true) treenode.render(true)
}) })
this.populateFirstLevel()
/* XXX - set color */ /* XXX - set color */
let color = new Color(0x80, 0x00, 0x33) let color = new Color(0x80, 0x00, 0x33)
let solver = new Solver(color) let solver = new Solver(color)
let result = solver.solve() let result = solver.solve()
console.log(result.filter) // console.log(result.filter)
}// }}} }// }}}
async render() {// {{{ async render() {// {{{
if (this.rendered) if (this.rendered)
alert('Tree should only be rendered once.') alert('Tree should only be rendered once.')
this.expandedNodes[ROOT_NODE] = true this.expandedNodes[ROOT_NODE] = true
const startnode = await nodeStore.get(ROOT_NODE) const startnode = await nodeStore.get(ROOT_NODE)
const starttreenode = new N2TreeNode(this, startnode, null) const starttreenode = new N2TreeNode(this, startnode, null)
this.treeNodeComponents[startnode.UUID] = starttreenode this.treeNodeComponents[startnode.UUID] = starttreenode
this.elTreenodes.appendChild(await starttreenode.render()) this.elTreenodes.appendChild(starttreenode.render())
this.rendered = true this.rendered = true
return this return this
@ -170,6 +174,27 @@ export class N2Sidebar extends CustomHTMLElement {
this.elTreenodes.replaceChildren() this.elTreenodes.replaceChildren()
this.populateFirstLevel() this.populateFirstLevel()
}// }}} }// }}}
populateFirstLevel() {//{{{
nodeStore.get(ROOT_NODE)
.then(node => node.fetchChildren())
.then(children => {
this.treeNodeComponents = {}
this.treeTrunk = []
for (const node of children) {
// The root node isn't supposed to be shown in the tree.
if (node.UUID === ROOT_NODE)
continue
if (node.ParentUUID === ROOT_NODE)
this.treeTrunk.push(node)
}
_mbus.dispatch('TREE_TRUNK_FETCHED')
})
.catch(e => {
console.error(e)
console.log(e.type, e.error)
alert(e.error)
})
}//}}}
getNodeExpanded(UUID) {//{{{ getNodeExpanded(UUID) {//{{{
if (this.expandedNodes[UUID] === undefined) if (this.expandedNodes[UUID] === undefined)
this.expandedNodes[UUID] = false this.expandedNodes[UUID] = false
@ -224,8 +249,6 @@ export class N2Sidebar extends CustomHTMLElement {
// Holding shift down does it recursively. // Holding shift down does it recursively.
case Space: case Space:
case 'Enter': case 'Enter':
if (n.UUID === ROOT_NODE)
return
const expanded = this.getNodeExpanded(n.UUID) const expanded = this.getNodeExpanded(n.UUID)
if (event.shiftKey) { if (event.shiftKey) {
this.recursiveExpand(n, !expanded) this.recursiveExpand(n, !expanded)
@ -279,7 +302,7 @@ export class N2Sidebar extends CustomHTMLElement {
return return
const expanded = this.getNodeExpanded(n.UUID) const expanded = this.getNodeExpanded(n.UUID)
if (expanded && n.hasChildren() && n.UUID !== ROOT_NODE) { if (expanded && n.hasChildren()) {
this.setNodeExpanded(n, false) this.setNodeExpanded(n, false)
return return
} }
@ -429,27 +452,26 @@ export class N2Sidebar extends CustomHTMLElement {
if (!state) if (!state)
await this.setNodeExpanded(node, false) await this.setNodeExpanded(node, false)
}//}}} }//}}}
async makeVisible(node, providedAncestors, dontExpand) {// {{{ async makeVisible(node) {// {{{
const treenode = this.treeNodeComponents[node.UUID] const treenode = this.treeNodeComponents[node.UUID]
if (!dontExpand) { const ancestors = await nodeStore.getNodeAncestry(node)
const ancestors = providedAncestors || await nodeStore.getNodeAncestry(node)
for (const ancestor of ancestors.reverse()) { for (const ancestor of ancestors.reverse()) {
this.setNodeExpanded(ancestor, true) this.setNodeExpanded(ancestor, true)
} }
}
// The ROOT_NODE for example hasn't got a treenode.
treenode?.scrollIntoView({ block: 'nearest' }) treenode?.scrollIntoView({ block: 'nearest' })
}// }}} }// }}}
} }
customElements.define('n2-sidebar', N2Sidebar) customElements.define('n2-tree', N2Tree)
export class N2TreeNode extends CustomHTMLElement { export class N2TreeNode extends CustomHTMLElement {
static {// {{{ static {// {{{
this.tmpl = document.createElement('template') this.tmpl = document.createElement('template')
this.tmpl.innerHTML = ` this.tmpl.innerHTML = `
<style> <style>
n2-sidebar:focus-within { n2-tree:focus-within {
.el-name { .el-name {
&.selected { &.selected {
span { span {
@ -481,48 +503,55 @@ export class N2TreeNode extends CustomHTMLElement {
` `
}// }}} }// }}}
constructor(sidebar, node, parent) {//{{{ constructor(tree, node, parent) {//{{{
super() super()
this.classList.add('node') this.classList.add('node')
this.sidebar = sidebar this.tree = tree
this.node = node this.node = node
this.parent = parent this.parent = parent
this.children_populated = false this.children_populated = false
this.rendered = false this.rendered = false
this.elExpandToggle.addEventListener('click', () => this.sidebar.setNodeExpanded(this.node, !this.sidebar.getNodeExpanded(this.node.UUID))) 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)) this.elName.addEventListener('click', () => _mbus.dispatch('TREE_NODE_SELECTED', this.node))
_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) this.render(true)
}) })
}// }}}
async fetchChildren(force_fetch) {//{{{
if (this.children_populated && !force_fetch)
return
if (this.node.Level === 0 || this.tree.getNodeExpanded(this.node.UUID))
this.fetchChildren()
}// }}}
async fetchChildren() {//{{{
await this.node.fetchChildren() await this.node.fetchChildren()
this.children_populated = true this.children_populated = true
}//}}} }//}}}
async render(force_update, force_refetch_children) {//{{{ render(force_update) {//{{{
if (this.rendered && force_update !== true) if (this.rendered && force_update !== true)
return this return this
if (this.sidebar.getNodeExpanded(this.node.UUID)) // Fetch the next level of children if the parent tree node is expanded and our children thus will be visible.
await this.fetchChildren() const expanded = this.node.hasChildren() && this.tree.getNodeExpanded(this.node.UUID)
// Update the name and selected status. 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.elName.querySelector('span').innerText = this.node.get('Name') this.elName.querySelector('span').innerText = this.node.get('Name')
if (this.sidebar.isSelected(this.node)) if (this.tree.isSelected(this.node))
this.elName.classList.add('selected') this.elName.classList.add('selected')
else else
this.elName.classList.remove('selected') this.elName.classList.remove('selected')
// Update expansion state // Update expansion state
const expanded = this.node.hasChildren() && this.sidebar.getNodeExpanded(this.node.UUID)
if (expanded) { if (expanded) {
this.elChildren.classList.add('expanded') this.elChildren.classList.add('expanded')
this.elChildren.classList.remove('collapsed') this.elChildren.classList.remove('collapsed')
@ -536,27 +565,27 @@ export class N2TreeNode extends CustomHTMLElement {
this.setImgSrc(this.elExpand, `/images/${window._VERSION}/icon_home.svg`) this.setImgSrc(this.elExpand, `/images/${window._VERSION}/icon_home.svg`)
else if (!this.node.hasChildren()) else if (!this.node.hasChildren())
this.setImgSrc(this.elExpand, `/images/${window._VERSION}/leaf.svg`) this.setImgSrc(this.elExpand, `/images/${window._VERSION}/leaf.svg`)
else if (this.sidebar.getNodeExpanded(this.node.UUID)) else if (this.tree.getNodeExpanded(this.node.UUID))
this.setImgSrc(this.elExpand, `/images/${window._VERSION}/expanded.svg`) this.setImgSrc(this.elExpand, `/images/${window._VERSION}/expanded.svg`)
else else
this.setImgSrc(this.elExpand, `/images/${window._VERSION}/collapsed.svg`) this.setImgSrc(this.elExpand, `/images/${window._VERSION}/collapsed.svg`)
// Should children be rendered? // Should children be rendered?
this.elChildren.innerHTML = ''
let children = [] let children = []
if (expanded) if (expanded)
children = this.node.Children.map(node => { children = this.node.Children.map(node => {
let treenode = this.sidebar.treeNodeComponents[node.UUID] let treenode = this.tree.treeNodeComponents[node.UUID]
if (treenode === undefined) { if (treenode === undefined) {
treenode = new N2TreeNode(this.sidebar, node, this) treenode = new N2TreeNode(this.tree, node, this)
this.sidebar.treeNodeComponents[node.UUID] = treenode this.tree.treeNodeComponents[node.UUID] = treenode
_mbus.dispatch(`NODE_COMPONENT_EXIST_${node.UUID}`)
} }
return treenode return treenode
}) })
const renderedChildren = []
for (const c of children) for (const c of children)
renderedChildren.push(await c.render()) this.elChildren.appendChild(c.render())
this.elChildren.replaceChildren(...renderedChildren)
this.rendered = true this.rendered = true
return this return this

View file

@ -15,7 +15,7 @@
"crypto": "/js/{{ .VERSION }}/crypto.mjs", "crypto": "/js/{{ .VERSION }}/crypto.mjs",
"node_store": "/js/{{ .VERSION }}/node_store.mjs", "node_store": "/js/{{ .VERSION }}/node_store.mjs",
"node": "/js/{{ .VERSION }}/page_node.mjs", "node": "/js/{{ .VERSION }}/page_node.mjs",
"sidebar": "/js/{{ .VERSION }}/sidebar.mjs" "tree": "/js/{{ .VERSION }}/tree.mjs"
} }
} }
</script> </script>