Initial use of simple message bus
This commit is contained in:
parent
b36ca0d635
commit
23307d7967
7 changed files with 189 additions and 6 deletions
|
|
@ -233,3 +233,32 @@ html {
|
||||||
grid-area: blank;
|
grid-area: blank;
|
||||||
height: 32px;
|
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: "";
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
foo
|
|
||||||
17
static/js/mbus.mjs
Normal file
17
static/js/mbus.mjs
Normal file
|
|
@ -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 }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -215,6 +215,10 @@ export class NodeUI extends Component {
|
||||||
else
|
else
|
||||||
document.getElementById('tree').focus()
|
document.getElementById('tree').focus()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case 'F':
|
||||||
|
_mbus.dispatch('op-search')
|
||||||
|
break
|
||||||
/*
|
/*
|
||||||
case 'C':
|
case 'C':
|
||||||
this.showPage('node')
|
this.showPage('node')
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,13 @@ export class Notes2 extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
startNode: null,
|
startNode: null,
|
||||||
}
|
}
|
||||||
|
this.op = signal('')
|
||||||
|
|
||||||
window._sync = new Sync()
|
window._sync = new Sync()
|
||||||
window._sync.run()
|
window._sync.run()
|
||||||
|
|
||||||
|
new OpSearch()
|
||||||
|
|
||||||
this.getStartNode()
|
this.getStartNode()
|
||||||
}//}}}
|
}//}}}
|
||||||
render(_props, { startNode }) {//{{{
|
render(_props, { startNode }) {//{{{
|
||||||
|
|
@ -27,6 +30,15 @@ export class Notes2 extends Component {
|
||||||
if (startNode === null)
|
if (startNode === null)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
/*
|
||||||
|
let op = null
|
||||||
|
switch(this.op.value) {
|
||||||
|
case 'search':
|
||||||
|
op = html`<${OpSearch} />`
|
||||||
|
break
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<${Tree} app=${this} key=${treeKey} startNode=${startNode} />
|
<${Tree} app=${this} key=${treeKey} startNode=${startNode} />
|
||||||
<${NodeUI} app=${this} ref=${this.nodeUI} startNode=${startNode} />
|
<${NodeUI} app=${this} ref=${this.nodeUI} startNode=${startNode} />
|
||||||
|
|
@ -99,8 +111,8 @@ class Tree extends Component {
|
||||||
<div id="tree" ref=${this.treeDiv} tabindex="0">
|
<div id="tree" ref=${this.treeDiv} tabindex="0">
|
||||||
<div id="logo" onclick=${() => _notes2.current.goToNode(ROOT_NODE)}><img src="/images/${_VERSION}/logo.svg" /></div>
|
<div id="logo" onclick=${() => _notes2.current.goToNode(ROOT_NODE)}><img src="/images/${_VERSION}/logo.svg" /></div>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<img src="/images/${_VERSION}/icon_search.svg" style="height: 22px" onclick=${()=>_sync.run()} />
|
<img src="/images/${_VERSION}/icon_search.svg" style="height: 22px" onclick=${() => _mbus.dispatch('op-search')} />
|
||||||
<img src="/images/${_VERSION}/icon_refresh.svg" onclick=${()=>_sync.run()} />
|
<img src="/images/${_VERSION}/icon_refresh.svg" onclick=${() => _sync.run()} />
|
||||||
</div>
|
</div>
|
||||||
${renderedTreeTrunk}
|
${renderedTreeTrunk}
|
||||||
</div>`
|
</div>`
|
||||||
|
|
@ -189,7 +201,7 @@ class Tree extends Component {
|
||||||
}//}}}
|
}//}}}
|
||||||
async setNodeExpanded(node, value) {//{{{
|
async setNodeExpanded(node, value) {//{{{
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const work = uuid=>{
|
const work = uuid => {
|
||||||
// Creating a default value if it doesn't exist already.
|
// Creating a default value if it doesn't exist already.
|
||||||
this.getNodeExpanded(uuid)
|
this.getNodeExpanded(uuid)
|
||||||
this.expandedNodes[uuid].value = value
|
this.expandedNodes[uuid].value = value
|
||||||
|
|
@ -200,7 +212,7 @@ class Tree extends Component {
|
||||||
work(node.UUID)
|
work(node.UUID)
|
||||||
return
|
return
|
||||||
} else {
|
} 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 = `<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
|
// vim: foldmethod=marker
|
||||||
|
|
|
||||||
|
|
@ -305,3 +305,46 @@ html {
|
||||||
grid-area: blank;
|
grid-area: blank;
|
||||||
height: 32px;
|
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: "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@
|
||||||
if (navigator.serviceWorker)
|
if (navigator.serviceWorker)
|
||||||
navigator.serviceWorker.register('/service_worker.js')
|
navigator.serviceWorker.register('/service_worker.js')
|
||||||
</script>
|
</script>
|
||||||
|
<script type="module" defer>
|
||||||
|
import { MessageBus } from '/js/{{ .VERSION }}/mbus.mjs'
|
||||||
|
window._mbus = new MessageBus()
|
||||||
|
</script>
|
||||||
<script type="importmap">
|
<script type="importmap">
|
||||||
{
|
{
|
||||||
"imports": {
|
"imports": {
|
||||||
|
|
@ -42,7 +46,7 @@
|
||||||
<script type="text/javascript" src="/js/{{ .VERSION }}/lib/fullcalendar.min.js"></script>
|
<script type="text/javascript" src="/js/{{ .VERSION }}/lib/fullcalendar.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="app">{{ block "page" . }}{{ end }}</div>
|
<div id="app">{{ block "page" . }}{{ end }}</div>
|
||||||
|
<div id="op"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue