diff --git a/static/css/notes2.css b/static/css/notes2.css
index 4aabc0a..b6b0963 100644
--- a/static/css/notes2.css
+++ b/static/css/notes2.css
@@ -233,3 +233,32 @@ html {
grid-area: blank;
height: 32px;
}
+dialog.op::backdrop {
+ background: rgba(0, 0, 0, 0.5);
+}
+dialog.op .header {
+ font-weight: bold;
+ margin-top: 16px;
+}
+dialog.op .header:first-child {
+ margin-top: 0px;
+}
+#op-search .results {
+ display: grid;
+ grid-template-columns: min-content min-content;
+ grid-gap: 6px 16px;
+}
+#op-search .results div {
+ white-space: nowrap;
+}
+#op-search .results .ancestors {
+ display: flex;
+}
+#op-search .results .ancestors .ancestor::after {
+ content: ">";
+ margin: 0px 8px;
+ color: #a00;
+}
+#op-search .results .ancestors .ancestor:last-child::after {
+ content: "";
+}
diff --git a/static/foo b/static/foo
deleted file mode 100644
index 257cc56..0000000
--- a/static/foo
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/static/js/mbus.mjs b/static/js/mbus.mjs
new file mode 100644
index 0000000..da5f098
--- /dev/null
+++ b/static/js/mbus.mjs
@@ -0,0 +1,17 @@
+export class MessageBus {
+ constructor() {
+ this.bus = new EventTarget()
+ }
+
+ subscribe(eventName, fn) {
+ this.bus.addEventListener(eventName, fn)
+ }
+
+ unsubscribe(eventName, fn) {
+ this.bus.removeEventListener(eventName, fn)
+ }
+
+ dispatch(eventName, data) {
+ this.bus.dispatchEvent(new CustomEvent(eventName, { detail: data }))
+ }
+}
diff --git a/static/js/node.mjs b/static/js/node.mjs
index 9433f72..92c8879 100644
--- a/static/js/node.mjs
+++ b/static/js/node.mjs
@@ -215,6 +215,10 @@ export class NodeUI extends Component {
else
document.getElementById('tree').focus()
break
+
+ case 'F':
+ _mbus.dispatch('op-search')
+ break
/*
case 'C':
this.showPage('node')
diff --git a/static/js/notes2.mjs b/static/js/notes2.mjs
index 6c33502..d73ef53 100644
--- a/static/js/notes2.mjs
+++ b/static/js/notes2.mjs
@@ -13,10 +13,13 @@ export class Notes2 extends Component {
this.state = {
startNode: null,
}
+ this.op = signal('')
window._sync = new Sync()
window._sync.run()
+ new OpSearch()
+
this.getStartNode()
}//}}}
render(_props, { startNode }) {//{{{
@@ -27,6 +30,15 @@ export class Notes2 extends Component {
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} />
@@ -99,8 +111,8 @@ class Tree extends Component {
_notes2.current.goToNode(ROOT_NODE)}>

-

_sync.run()} />
-

_sync.run()} />
+

_mbus.dispatch('op-search')} />
+

_sync.run()} />
${renderedTreeTrunk}
`
@@ -189,7 +201,7 @@ class Tree extends Component {
}//}}}
async setNodeExpanded(node, value) {//{{{
return new Promise((resolve, reject) => {
- const work = uuid=>{
+ const work = uuid => {
// Creating a default value if it doesn't exist already.
this.getNodeExpanded(uuid)
this.expandedNodes[uuid].value = value
@@ -200,7 +212,7 @@ class Tree extends Component {
work(node.UUID)
return
} else {
- this.fetchChildrenNotify(node.UUID, uuid=>work(uuid))
+ this.fetchChildrenNotify(node.UUID, uuid => work(uuid))
}
})
}//}}}
@@ -468,4 +480,79 @@ class TreeNode extends Component {
}//}}}
}
+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 = ``
+ 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(`
+
+
+
+
+
+
+ `)
+
+ 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(`${a.data.Name}
`)
+ div[0].addEventListener('click', ()=>_notes2.current.goToNode(a.UUID))
+ return div[0]
+ })
+
+
+ const div = tmpl(`${r.name}
`)
+ div[0].addEventListener('click', ()=>_notes2.current.goToNode(r.uuid))
+ rs.push(...div)
+
+ const ancDev = tmpl('')
+ ancDev[0].append(...ancestors)
+ rs.push(ancDev[0])
+ }
+ this.get('.results').replaceChildren(...rs)
+ }
+}
+
// vim: foldmethod=marker
diff --git a/static/less/notes2.less b/static/less/notes2.less
index 08c8251..a1ae783 100644
--- a/static/less/notes2.less
+++ b/static/less/notes2.less
@@ -305,3 +305,46 @@ html {
grid-area: blank;
height: 32px;
}
+
+dialog.op {
+ &::backdrop {
+ background: rgba(0, 0, 0, 0.5);
+ }
+
+ .header {
+ font-weight: bold;
+ margin-top: 16px;
+
+ &:first-child {
+ margin-top: 0px;
+ }
+ }
+
+}
+
+#op-search {
+ .results {
+ display: grid;
+ grid-template-columns: min-content min-content;
+ grid-gap: 6px 16px;
+
+ div {
+ white-space: nowrap;
+ }
+
+
+ .ancestors {
+ display: flex;
+
+ .ancestor::after {
+ content: ">";
+ margin: 0px 8px;
+ color: #a00;
+ }
+
+ .ancestor:last-child::after {
+ content: "";
+ }
+ }
+ }
+}
diff --git a/views/layouts/main.gotmpl b/views/layouts/main.gotmpl
index 62a4e62..123fc68 100644
--- a/views/layouts/main.gotmpl
+++ b/views/layouts/main.gotmpl
@@ -10,6 +10,10 @@
if (navigator.serviceWorker)
navigator.serviceWorker.register('/service_worker.js')
+
-
{{ block "page" . }}{{ end }}
+