Saving of nodes
This commit is contained in:
parent
c5bec0afa6
commit
08fd2cf4e9
16 changed files with 852 additions and 42 deletions
|
|
@ -8,7 +8,7 @@ html {
|
|||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
background-color: #333;
|
||||
background-color: #444 !important;
|
||||
}
|
||||
*,
|
||||
*:before,
|
||||
|
|
@ -21,13 +21,80 @@ body {
|
|||
[onClick] {
|
||||
cursor: pointer;
|
||||
}
|
||||
#nodes {
|
||||
.section {
|
||||
background-color: #fff;
|
||||
padding: 32px;
|
||||
border-radius: 8px;
|
||||
margin: 32px;
|
||||
display: none;
|
||||
}
|
||||
.section.show {
|
||||
display: block;
|
||||
}
|
||||
#layout {
|
||||
display: grid;
|
||||
grid-template-areas: "menu menu" "navigation details";
|
||||
grid-template-columns: min-content 1fr;
|
||||
grid-gap: 32px;
|
||||
padding: 32px;
|
||||
}
|
||||
#menu {
|
||||
grid-area: menu;
|
||||
grid-template-columns: repeat(100, min-content);
|
||||
grid-gap: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
#menu.section {
|
||||
display: grid;
|
||||
padding: 16px 32px;
|
||||
}
|
||||
#menu .item {
|
||||
cursor: pointer;
|
||||
}
|
||||
#menu .item.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
#logo img {
|
||||
height: 96px;
|
||||
margin-right: 32px;
|
||||
}
|
||||
#nodes {
|
||||
grid-area: navigation;
|
||||
width: min-content;
|
||||
}
|
||||
#editor-node {
|
||||
grid-area: details;
|
||||
}
|
||||
#types {
|
||||
grid-area: navigation;
|
||||
}
|
||||
#types .group {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
margin-top: 32px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
#types .group:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
#types .type {
|
||||
display: grid;
|
||||
grid-template-columns: min-content 1fr;
|
||||
grid-gap: 8px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
#types .type .img img {
|
||||
height: 24px;
|
||||
display: inline;
|
||||
}
|
||||
#types .type .title {
|
||||
white-space: nowrap;
|
||||
line-height: 24px;
|
||||
}
|
||||
#editor-type-schema {
|
||||
grid-area: details;
|
||||
}
|
||||
.node {
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content 100%;
|
||||
|
|
@ -44,7 +111,7 @@ body {
|
|||
padding-right: 8px;
|
||||
}
|
||||
.node .expand-status.leaf {
|
||||
width: 36px;
|
||||
width: 40px;
|
||||
}
|
||||
.node .expand-status.leaf img {
|
||||
display: none;
|
||||
|
|
@ -52,15 +119,16 @@ body {
|
|||
.node .expand-status img {
|
||||
cursor: pointer;
|
||||
}
|
||||
.node .icon {
|
||||
padding-right: 8px;
|
||||
.node .type-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
.node .icon img {
|
||||
filter: invert(0.7) sepia(0.5) hue-rotate(50deg) saturate(300%) brightness(0.85);
|
||||
.node .type-icon img {
|
||||
filter: invert(0.7) sepia(0.5) hue-rotate(50deg) saturate(300%) brightness(0.85) !important;
|
||||
}
|
||||
.node .name {
|
||||
margin-bottom: 8px;
|
||||
line-height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.node .children {
|
||||
display: none;
|
||||
|
|
|
|||
1
static/css/spectre-exp.min.css
vendored
Normal file
1
static/css/spectre-exp.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/css/spectre-icons.min.css
vendored
Normal file
1
static/css/spectre-icons.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/css/spectre.min.css
vendored
Normal file
1
static/css/spectre.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
142
static/images/logo.svg
Normal file
142
static/images/logo.svg
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="79.624962mm"
|
||||
height="78.647499mm"
|
||||
viewBox="0 0 79.624962 78.647499"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
sodipodi:docname="logo.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.60258984"
|
||||
inkscape:cx="66.380144"
|
||||
inkscape:cy="193.33217"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" /><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-38.185371,-67.683764)"><path
|
||||
d="m 51.45301,114.65074 v -1.98318 h 4.210436 q 1.55603,0 2.379811,-0.82378 0.823781,-0.82378 0.823781,-2.50185 V 98.632775 h -3.081551 v -1.983178 h 4.881664 v 12.051613 q 0,5.94953 -5.247789,5.94953 z M 72.535691,98.632775 h -4.942685 q -1.311947,0 -2.074707,0.67123 -0.76276,0.67123 -0.76276,2.135735 0,1.58654 0.610208,2.3493 0.610208,0.73225 1.708582,0.73225 h 1.861134 q 2.379812,0 3.417165,1.34245 1.037354,1.34246 1.037354,3.90533 0,2.41033 -1.342458,3.66125 -1.311947,1.22042 -3.874821,1.22042 h -5.308809 v -1.98318 h 5.827486 q 1.281437,0 1.983176,-0.70174 0.73225,-0.70174 0.73225,-2.16624 0,-1.55603 -0.579698,-2.41032 -0.579697,-0.8848 -1.861134,-0.8848 h -2.013687 q -2.166238,0 -3.142571,-1.25092 -0.976333,-1.28144 -0.976333,-3.84432 0,-2.349305 1.281437,-3.539205 1.281437,-1.220418 3.966352,-1.220418 h 4.454519 z m 10.922694,-1.983178 q 1.372968,0 2.074708,0.671228 0.732249,0.67123 0.732249,2.22726 v 12.021105 q 0,3.08155 -2.959509,3.08155 H 78.45468 q -2.867978,0 -2.867978,-2.86798 V 99.548085 q 0,-2.898488 2.837467,-2.898488 z m -4.362987,1.983178 q -1.617051,0 -1.617051,1.464505 v 10.83119 q 0,0.91531 0.427145,1.34246 0.427146,0.39663 1.006844,0.39663 h 4.088393 q 0.549187,0 0.945823,-0.51867 0.427145,-0.51868 0.427145,-1.22042 v -10.83119 q 0,-0.793275 -0.427145,-1.128895 -0.427146,-0.33561 -1.159396,-0.33561 z M 98.835602,114.65074 H 96.089666 L 90.628304,100.4634 v 14.18734 H 88.858701 V 96.649597 h 2.257769 l 5.919018,15.377243 V 96.649597 h 1.800114 z"
|
||||
id="text1"
|
||||
style="font-size:30.5104px;font-family:'Forgotten Futurist';-inkscape-font-specification:'Forgotten Futurist, Normal';text-align:center;text-anchor:middle;fill:#262626;stroke-width:1.71737"
|
||||
aria-label="JSON" /><circle
|
||||
style="fill:#27a698;fill-opacity:1;stroke-width:0.477313;-inkscape-stroke:none"
|
||||
id="path1"
|
||||
cx="46.627323"
|
||||
cy="90.933174"
|
||||
r="4.6399903" /><circle
|
||||
style="fill:#1e6380;fill-opacity:1;stroke-width:0.452723;-inkscape-stroke:none"
|
||||
id="circle1"
|
||||
cx="96.612923"
|
||||
cy="72.084717"
|
||||
r="4.4009533" /><circle
|
||||
style="fill:#21818c;fill-opacity:1;stroke-width:0.529167;-inkscape-stroke:none"
|
||||
id="circle2"
|
||||
cx="43.329437"
|
||||
cy="121.67689"
|
||||
r="5.1440659" /><circle
|
||||
style="fill:#2988af;fill-opacity:1;stroke-width:0.355205;-inkscape-stroke:none"
|
||||
id="circle3"
|
||||
cx="78.708733"
|
||||
cy="125.57096"
|
||||
r="3.4529741" /><circle
|
||||
style="fill:#1e4c80;fill-opacity:1;stroke-width:0.70227;-inkscape-stroke:none"
|
||||
id="circle4"
|
||||
cx="102.07027"
|
||||
cy="124.97508"
|
||||
r="6.8268108" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.5104px;line-height:normal;font-family:'Forgotten Futurist';-inkscape-font-specification:'Forgotten Futurist, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-decoration-color:#000000;text-anchor:middle;fill:#262626;stroke-width:1.71737;-inkscape-stroke:none"
|
||||
x="73.981911"
|
||||
y="186.11093"
|
||||
id="text4"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.5104px;font-family:'Forgotten Futurist';-inkscape-font-specification:'Forgotten Futurist, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:1.71737"
|
||||
x="73.981911"
|
||||
y="186.11093">JSON</tspan></text><circle
|
||||
style="fill:#1e8071;fill-opacity:1;stroke-width:0.529167;-inkscape-stroke:none"
|
||||
id="circle5"
|
||||
cx="80.797234"
|
||||
cy="85.280693"
|
||||
r="5.1440659" /><circle
|
||||
style="fill:#248d9a;fill-opacity:1;stroke-width:0.529167;-inkscape-stroke:none"
|
||||
id="circle6"
|
||||
cx="112.66627"
|
||||
cy="94.749619"
|
||||
r="5.1440659" /><path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#205d7b;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 51.205022,90.175919 75.722226,86.120215"
|
||||
id="path6"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#path1"
|
||||
inkscape:connection-end="#circle5" /><path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#205d7b;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 84.746953,81.985206 93.23378,74.904138"
|
||||
id="path7"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#circle5"
|
||||
inkscape:connection-end="#circle1" /><path
|
||||
style="fill:#1e6380;fill-opacity:1;fill-rule:evenodd;stroke:#205d7b;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 85.728154,86.745768 22.007196,6.538776"
|
||||
id="path8"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#circle5"
|
||||
inkscape:connection-end="#circle6" /><path
|
||||
style="display:inline;fill:#1e6380;fill-opacity:1;fill-rule:evenodd;stroke:#205d7b;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 48.4426,122.23968 26.833902,2.95351"
|
||||
id="path9"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#circle2"
|
||||
inkscape:connection-end="#circle3" /><path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#205d7b;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 82.160565,125.48292 13.085148,-0.33376"
|
||||
id="path10"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-end="#circle4"
|
||||
inkscape:connection-start="#circle3" /><circle
|
||||
style="fill:#267ea2;fill-opacity:1;stroke-width:0.653982;-inkscape-stroke:none"
|
||||
id="circle10"
|
||||
cx="62.795177"
|
||||
cy="139.97386"
|
||||
r="6.3574047" /><path
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1e6380;fill-opacity:1;fill-rule:evenodd;stroke:#205d7b;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1"
|
||||
d="m 67.508676,135.70781 8.639957,-7.81978"
|
||||
id="path11"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#circle10"
|
||||
inkscape:connection-end="#circle3" /><path
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1e6380;fill-opacity:1;fill-rule:evenodd;stroke:#205d7b;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1"
|
||||
d="M 99.156616,75.676028 109.69306,90.551905"
|
||||
id="path12"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:connection-start="#circle1"
|
||||
inkscape:connection-end="#circle6" /></g></svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
|
|
@ -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 }))
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ html {
|
|||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
background-color: #333;
|
||||
background-color: #444 !important;
|
||||
}
|
||||
|
||||
*,
|
||||
|
|
@ -26,14 +26,105 @@ body {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
#nodes {
|
||||
.section {
|
||||
background-color: #fff;
|
||||
padding: 32px;
|
||||
border-radius: 8px;
|
||||
margin: 32px;
|
||||
display: none;
|
||||
|
||||
&.show {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
#layout {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"menu menu"
|
||||
"navigation details"
|
||||
;
|
||||
grid-template-columns: min-content 1fr;
|
||||
grid-gap: 32px;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
#menu {
|
||||
grid-area: menu;
|
||||
grid-template-columns: repeat(100, min-content);
|
||||
grid-gap: 16px;
|
||||
align-items: center;
|
||||
|
||||
&.section {
|
||||
display: grid;
|
||||
padding: 16px 32px;
|
||||
}
|
||||
|
||||
.item {
|
||||
cursor: pointer;
|
||||
|
||||
&.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#logo {
|
||||
img {
|
||||
height: 96px;
|
||||
margin-right: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#nodes {
|
||||
grid-area: navigation;
|
||||
width: min-content;
|
||||
}
|
||||
|
||||
#editor-node {
|
||||
grid-area: details;
|
||||
}
|
||||
|
||||
#types {
|
||||
grid-area: navigation;
|
||||
|
||||
.group {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
margin-top: 32px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.type {
|
||||
display: grid;
|
||||
grid-template-columns: min-content 1fr;
|
||||
grid-gap: 8px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.img {
|
||||
img {
|
||||
height: 24px;
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
white-space: nowrap;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#editor-type-schema {
|
||||
grid-area: details;
|
||||
}
|
||||
|
||||
.node {
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content 100%;
|
||||
|
|
@ -54,7 +145,7 @@ body {
|
|||
padding-right: 8px;
|
||||
|
||||
&.leaf {
|
||||
width: 36px;
|
||||
width: 40px;
|
||||
img {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -65,17 +156,18 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding-right: 8px;
|
||||
.type-icon {
|
||||
padding-right: 4px;
|
||||
|
||||
img {
|
||||
filter: invert(.7) sepia(.5) hue-rotate(50deg) saturate(300%) brightness(0.85);
|
||||
filter: invert(.7) sepia(.5) hue-rotate(50deg) saturate(300%) brightness(0.85) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-bottom: 8px;
|
||||
line-height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.children {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue