Saving of nodes
This commit is contained in:
parent
c5bec0afa6
commit
08fd2cf4e9
16 changed files with 852 additions and 42 deletions
|
|
@ -1,3 +1,124 @@
|
|||
import { Editor } from '@editor'
|
||||
import { MessageBus } from '@mbus'
|
||||
|
||||
export class App {
|
||||
constructor() {// {{{
|
||||
window.mbus = new MessageBus()
|
||||
this.editor = null
|
||||
this.typesList = null
|
||||
this.currentNodeID = null
|
||||
|
||||
const events = [
|
||||
'MENU_ITEM_SELECTED',
|
||||
'NODE_SELECTED',
|
||||
'EDITOR_NODE_SAVE',
|
||||
'TYPES_LIST_FETCHED',
|
||||
]
|
||||
for (const eventName of events)
|
||||
mbus.subscribe(eventName, event => this.eventHandler(event))
|
||||
|
||||
mbus.dispatch('MENU_ITEM_SELECTED', 'node')
|
||||
}// }}}
|
||||
|
||||
eventHandler(event) {// {{{
|
||||
switch (event.type) {
|
||||
case 'MENU_ITEM_SELECTED':
|
||||
const item = document.querySelector(`#menu [data-section="${event.detail}"]`)
|
||||
this.section(item, event.detail)
|
||||
break
|
||||
|
||||
case 'NODE_SELECTED':
|
||||
this.currentNodeID = event.detail
|
||||
this.edit(this.currentNodeID)
|
||||
break
|
||||
|
||||
case 'EDITOR_NODE_SAVE':
|
||||
this.nodeUpdate()
|
||||
break
|
||||
|
||||
case 'TYPES_LIST_FETCHED':
|
||||
const types = document.getElementById('types')
|
||||
types.replaceChildren(this.typesList.render())
|
||||
|
||||
break
|
||||
|
||||
default:
|
||||
console.log(event)
|
||||
}
|
||||
}// }}}
|
||||
section(item, name) {// {{{
|
||||
for (const el of document.querySelectorAll('#menu .item'))
|
||||
el.classList.remove('selected')
|
||||
item.classList.add('selected')
|
||||
|
||||
for (const el of document.querySelectorAll('.section.show'))
|
||||
el.classList.remove('show')
|
||||
|
||||
switch (name) {
|
||||
case 'node':
|
||||
document.getElementById('nodes').classList.add('show')
|
||||
document.getElementById('editor-node').classList.add('show')
|
||||
break
|
||||
|
||||
case 'type':
|
||||
document.getElementById('types').classList.add('show')
|
||||
document.getElementById('editor-type-schema').classList.add('show')
|
||||
|
||||
if (this.typesList === null)
|
||||
this.typesList = new TypesList()
|
||||
this.typesList.fetchTypes().then(() => {
|
||||
mbus.dispatch('TYPES_LIST_FETCHED')
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
}// }}}
|
||||
edit(nodeID) {// {{{
|
||||
console.log(nodeID)
|
||||
fetch(`/nodes/${nodeID}`)
|
||||
.then(data => data.json())
|
||||
.then(json => {
|
||||
if (!json.OK) {
|
||||
showError(json.Error)
|
||||
return
|
||||
}
|
||||
|
||||
const editorEl = document.querySelector('#editor-node .editor')
|
||||
this.editor = new Editor(json.Node.TypeSchema)
|
||||
editorEl.replaceChildren(this.editor.render(json.Node.Data))
|
||||
|
||||
})
|
||||
}// }}}
|
||||
nodeUpdate() {// {{{
|
||||
if (this.editor === null)
|
||||
return
|
||||
|
||||
const btn = document.querySelector('#editor-node .controls button')
|
||||
btn.disabled = true
|
||||
const buttonPressed = Date.now()
|
||||
|
||||
const nodeData = this.editor.data()
|
||||
|
||||
fetch(`/nodes/update/${this.currentNodeID}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(nodeData),
|
||||
})
|
||||
.then(data => data.json())
|
||||
.then(json => {
|
||||
if (!json.OK) {
|
||||
showError(json.Error)
|
||||
return
|
||||
}
|
||||
|
||||
const timePassed = Date.now() - buttonPressed
|
||||
if (timePassed < 250)
|
||||
setTimeout(()=>btn.disabled = false, 250 - timePassed)
|
||||
else
|
||||
btn.disabled = false
|
||||
})
|
||||
}// }}}
|
||||
}
|
||||
|
||||
export class TreeNode {
|
||||
constructor(parent, data) {// {{{
|
||||
this.data = data
|
||||
|
|
@ -12,16 +133,18 @@ export class TreeNode {
|
|||
const nodeHTML = `
|
||||
<div class="node">
|
||||
<div class="expand-status"><img /></div>
|
||||
<div class="icon"><img /></div>
|
||||
<div class="type-icon"><img /></div>
|
||||
<div class="name">${this.name()}</div>
|
||||
<div class="children"></div>
|
||||
</div>
|
||||
`
|
||||
this.name()
|
||||
|
||||
const tmpl = document.createElement('template')
|
||||
tmpl.innerHTML = nodeHTML
|
||||
this.children = tmpl.content.querySelector('.children')
|
||||
|
||||
tmpl.content.querySelector('.name').addEventListener('click', () => mbus.dispatch('NODE_SELECTED', this.data.ID))
|
||||
|
||||
// data.NumChildren is set regardless of having fetched the children or not.
|
||||
if (this.hasChildren()) {
|
||||
const img = tmpl.content.querySelector('.expand-status img')
|
||||
|
|
@ -31,7 +154,7 @@ export class TreeNode {
|
|||
tmpl.content.querySelector('.expand-status').classList.add('leaf')
|
||||
|
||||
if (this.data.TypeIcon) {
|
||||
const img = tmpl.content.querySelector('.icon img')
|
||||
const img = tmpl.content.querySelector('.type-icon img')
|
||||
img.setAttribute('src', `/images/${_VERSION}/node_modules/@mdi/svg/svg/${this.data.TypeIcon}.svg`)
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +174,6 @@ export class TreeNode {
|
|||
}// }}}
|
||||
sortChildren() {// {{{
|
||||
this.data.Children.sort((a, b) => {
|
||||
console.log(a.Name, b.Name)
|
||||
if (a.TypeName < b.TypeName) return -1
|
||||
if (a.TypeName > b.TypeName) return 1
|
||||
|
||||
|
|
@ -91,10 +213,81 @@ export class TreeNode {
|
|||
return new Promise((resolve, reject) => {
|
||||
fetch(`/nodes/tree/${this.data.ID}?depth=2`)
|
||||
.then(data => data.json())
|
||||
.then(json => resolve(json))
|
||||
.then(json => {
|
||||
if (json.OK)
|
||||
resolve(json.Nodes)
|
||||
else
|
||||
reject(json.Error)
|
||||
})
|
||||
.catch(err => reject(err))
|
||||
})
|
||||
}// }}}
|
||||
}
|
||||
|
||||
export class TypesList {
|
||||
constructur() {// {{{
|
||||
this.types = []
|
||||
}// }}}
|
||||
async fetchTypes() {// {{{
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch('/types/')
|
||||
.then(data => data.json())
|
||||
.then(json => {
|
||||
if (!json.OK) {
|
||||
showError(json.Error)
|
||||
return
|
||||
}
|
||||
this.types = json.Types
|
||||
resolve()
|
||||
})
|
||||
.catch(err => reject(err))
|
||||
})
|
||||
}// }}}
|
||||
render() {// {{{
|
||||
const div = document.createElement('div')
|
||||
|
||||
this.types.sort((a,b)=> {
|
||||
if (a.Schema['x-group'] === undefined)
|
||||
a.Schema['x-group'] = 'No group'
|
||||
|
||||
if (b.Schema['x-group'] === undefined)
|
||||
b.Schema['x-group'] = 'No group'
|
||||
|
||||
if (a.Schema['x-group'] < b.Schema['x-group']) return -1
|
||||
if (a.Schema['x-group'] > b.Schema['x-group']) return 1
|
||||
|
||||
if ((a.Schema.title || a.Name) < (b.Schema.title || b.Name)) return -1
|
||||
if ((a.Schema.title || a.Name) > (b.Schema.title || b.Name)) return 1
|
||||
|
||||
return 0
|
||||
})
|
||||
|
||||
let prevGroup = null
|
||||
|
||||
for (const t of this.types) {
|
||||
if (t.Name == 'root_node')
|
||||
continue
|
||||
|
||||
if (t.Schema['x-group'] != prevGroup) {
|
||||
prevGroup = t.Schema['x-group']
|
||||
const group = document.createElement('div')
|
||||
group.classList.add('group')
|
||||
group.innerText = t.Schema['x-group']
|
||||
div.appendChild(group)
|
||||
}
|
||||
|
||||
const tDiv = document.createElement('div')
|
||||
tDiv.classList.add('type')
|
||||
tDiv.innerHTML = `
|
||||
<div class="img"><img src="/images/${_VERSION}/node_modules/@mdi/svg/svg/file-document-check-outline.svg" /></div>
|
||||
<div class="title">${t.Schema.title || t.Name}</div>
|
||||
`
|
||||
|
||||
div.appendChild(tDiv)
|
||||
}
|
||||
|
||||
return div
|
||||
}// }}}
|
||||
}
|
||||
|
||||
// vim: foldmethod=marker
|
||||
|
|
|
|||
27
static/js/editor.mjs
Normal file
27
static/js/editor.mjs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
export class Editor {
|
||||
constructor(schema) {
|
||||
this.schema = schema
|
||||
this.editor = null
|
||||
}
|
||||
|
||||
render(data) {
|
||||
const div = document.createElement('div')
|
||||
this.editor = new JSONEditor(div, {
|
||||
theme: 'spectre',
|
||||
iconlib: 'spectre',
|
||||
disable_collapse: true,
|
||||
disable_properties: true,
|
||||
schema: this.schema,
|
||||
});
|
||||
|
||||
this.editor.on('ready', () => {
|
||||
this.editor.setValue(data)
|
||||
})
|
||||
|
||||
return div
|
||||
}
|
||||
|
||||
data() {
|
||||
return this.editor.getValue()
|
||||
}
|
||||
}
|
||||
2
static/js/lib/jsoneditor.js
Normal file
2
static/js/lib/jsoneditor.js
Normal file
File diff suppressed because one or more lines are too long
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 }))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue