164 lines
4 KiB
JavaScript
164 lines
4 KiB
JavaScript
import { h, Component, createRef } from 'preact'
|
|
import { signal } from 'preact/signals'
|
|
import htm from 'htm'
|
|
import { Node, NodeUI } from 'node'
|
|
import { ROOT_NODE } from 'node_store'
|
|
import { TreeNative } from 'tree'
|
|
const html = htm.bind(h)
|
|
|
|
export class Notes2 extends Component {
|
|
constructor() {//{{{
|
|
super()
|
|
this.nodeUI = createRef()
|
|
this.reloadTree = signal(0)
|
|
this.state = {
|
|
startNode: null,
|
|
}
|
|
this.op = signal('')
|
|
this.treeNative = new TreeNative()
|
|
|
|
window._sync = new Sync()
|
|
window._sync.run()
|
|
|
|
new OpSearch()
|
|
|
|
this.getStartNode()
|
|
}//}}}
|
|
render(_props, { startNode }) {//{{{
|
|
console.log('notes2 render')
|
|
const treeKey = `tree-${this.reloadTree}`
|
|
console.log('treeKey', treeKey)
|
|
|
|
if (startNode === null)
|
|
return
|
|
|
|
/*
|
|
let op = null
|
|
switch(this.op.value) {
|
|
case 'search':
|
|
op = html`<${OpSearch} />`
|
|
break
|
|
}
|
|
*/
|
|
|
|
return html`
|
|
<${Tree} app=${this} key=${treeKey} startNode=${startNode} />
|
|
<${NodeUI} app=${this} ref=${this.nodeUI} startNode=${startNode} />
|
|
`
|
|
}//}}}
|
|
getStartNode() {//{{{
|
|
let nodeUUID = ROOT_NODE
|
|
|
|
// Is a UUID provided on the URI as an anchor?
|
|
const parts = document.URL.split('#')
|
|
if (parts[1]?.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i))
|
|
nodeUUID = parts[1]
|
|
|
|
nodeStore.get(nodeUUID).then(node => {
|
|
this.setState({ startNode: node })
|
|
})
|
|
}//}}}
|
|
async goToNode(nodeUUID, dontPush, dontExpand) {//{{{
|
|
if (nodeUUID === null || nodeUUID === undefined)
|
|
return
|
|
|
|
// Don't switch notes until saved.
|
|
if (this.nodeUI.current.nodeModified.value) {
|
|
if (!confirm("Changes not saved. Do you want to discard changes?"))
|
|
return
|
|
}
|
|
|
|
if (!dontPush)
|
|
history.pushState({ nodeUUID }, '', `/#${nodeUUID}`)
|
|
|
|
// New node is fetched in order to retrieve content and files.
|
|
// Such data is unnecessary to transfer for tree/navigational purposes.
|
|
const node = nodeStore.node(nodeUUID)
|
|
const ancestors = await nodeStore.getNodeAncestry(node)
|
|
this.nodeUI.current.setNode(node)
|
|
this.nodeUI.current.setCrumbs(ancestors)
|
|
this.tree.setSelected(node, dontExpand)
|
|
this.treeNative.setSelected(node, dontExpand)
|
|
}//}}}
|
|
logout() {//{{{
|
|
localStorage.removeItem('session.UUID')
|
|
location.href = '/'
|
|
}//}}}
|
|
}
|
|
|
|
class Op {
|
|
constructor(id) {
|
|
this.id = id
|
|
_mbus.subscribe(this.id, p => this.render(p))
|
|
}
|
|
render(html) {
|
|
const op = document.getElementById('op')
|
|
const t = document.createElement('template')
|
|
t.innerHTML = `<dialog id="${this.id}" class="op">${html}</dialog>`
|
|
op.replaceChildren(t.content)
|
|
document.getElementById(this.id).showModal()
|
|
}
|
|
get(selector) {
|
|
return document.querySelector(`#${this.id} ${selector}`)
|
|
}
|
|
bind(selector, event, fn) {
|
|
this.get(selector).addEventListener(event, evt => fn(evt))
|
|
}
|
|
}
|
|
|
|
function tmpl(html) {
|
|
const el = document.createElement('template')
|
|
el.innerHTML = html
|
|
return el.content.children
|
|
}
|
|
|
|
class OpSearch extends Op {
|
|
constructor() {
|
|
super('op-search')
|
|
}
|
|
|
|
render() {
|
|
super.render(`
|
|
<div class="header">Search</div>
|
|
<div>
|
|
<input type="text" />
|
|
</div>
|
|
<div class="header">Results</div>
|
|
<div class="results"></div>
|
|
`)
|
|
|
|
this.bind('input[type="text"]', 'keydown', evt => this.search(evt))
|
|
}
|
|
|
|
search(event) {
|
|
if (event.key !== 'Enter')
|
|
return
|
|
|
|
const searchFor = document.querySelector('#op-search input').value
|
|
nodeStore.search(searchFor, ROOT_NODE)
|
|
.then(res => this.displayResults(res))
|
|
}
|
|
|
|
displayResults(results) {
|
|
const rs = []
|
|
for (const r of results) {
|
|
const ancestors = r.ancestry.reverse().map(a => {
|
|
const div = tmpl(`<div class="ancestor">${a.data.Name}</div>`)
|
|
div[0].addEventListener('click', () => _notes2.current.goToNode(a.UUID))
|
|
return div[0]
|
|
})
|
|
|
|
|
|
const div = tmpl(`<div>${r.name}</div>`)
|
|
div[0].addEventListener('click', () => _notes2.current.goToNode(r.uuid))
|
|
rs.push(...div)
|
|
|
|
const ancDev = tmpl('<div class="ancestors"></div>')
|
|
ancDev[0].append(...ancestors)
|
|
rs.push(ancDev[0])
|
|
}
|
|
this.get('.results').replaceChildren(...rs)
|
|
}
|
|
}
|
|
|
|
// vim: foldmethod=marker
|