diff --git a/src/main.js b/src/main.js index b6c0a7d..904208b 100644 --- a/src/main.js +++ b/src/main.js @@ -1,290 +1,7 @@ import {demoConfig} from "./demo-config"; import "@vanillawc/wc-codemirror"; -import { - LitElement, - html, - css, - svg, - property -} from "lit-element"; - -import { - mdiCallSplit, - mdiAbTesting, - mdiCheck, - mdiClose, - mdiChevronRight, - mdiExclamation, - mdiTimerOutline, - mdiTrafficLight, - mdiRefresh, - mdiArrowUp, - mdiCodeJson, - mdiCheckBoxOutline, - mdiCheckboxBlankOutline, - mdiAsterisk, -} from "@mdi/js"; - - -const SIZE = 35; -const BORDER = 5; -const R = SIZE/2; -const DIST = 20; - -const ICONS = { - choose: mdiCallSplit, - chooseChoice: mdiCheckBoxOutline, - chooseDefault: mdiCheckboxBlankOutline, - condition: mdiAbTesting, - TRUE: mdiCheck, - FALSE: mdiClose, - service: mdiChevronRight, - event: mdiExclamation, - delay: mdiTimerOutline, - wait: mdiTrafficLight, - loop: mdiRefresh, - loopReturn: mdiArrowUp, - YAML: mdiCodeJson, -}; - -class ScriptGraph extends LitElement { - - static get properties() { - return { - tree: {}, - selected: {}, - }; - } - - _select(idx) { - this.selected = idx; - const ev = new CustomEvent("selected", {detail: idx}); - this.dispatchEvent(ev); - } - - _draw_node(x, y, node, idx) { - const selected = (Array.isArray(this.selected) - ? (this.selected[0] === idx) - : false - ) || (idx === this.selected); - return svg` - this._select(idx)} - /> - - ${node in ICONS - ? svg` - - - - ` - : svg` - ${node} - `} - - `; - } - _draw_connector(x1, y1, x2, y2) { - return svg` - - `; - } - - - draw_tree(tree) { - if(!tree) return {svg: svg``, width: 1, height: 1}; - let nodes = []; - let connections = []; - if(!Array.isArray(tree)) { - const selected = tree.idx == this.selected; - if(tree.type === "loop") { - const seq = this.draw_tree(tree.sequence); - const sep = seq.width + DIST; - const left = -(seq.width + SIZE + DIST)/2 + SIZE/2; - const right = left+DIST+seq.width/2 + SIZE/2; - return { - svg: svg` - ${this._draw_connector( - 0, R, - left, SIZE + DIST + R - )} - ${this._draw_connector( - 0, R, - right, SIZE + DIST + R - )} - ${this._draw_connector( - right, SIZE + DIST + seq.height - R, - 0, SIZE + DIST + seq.height + DIST - )} - ${this._draw_connector( - left, SIZE + DIST + seq.height - R, - 0, SIZE + DIST + seq.height + DIST - )} - ${this._draw_connector( - left, SIZE + DIST + R, - left, SIZE + DIST + seq.height - R - )} - ${this._draw_node(0, 0, "loop", tree.idx)} - ${this._draw_node(left, SIZE+DIST+seq.height/2-R, "loopReturn", tree.idx)} - - ${seq.svg} - - `, - height: SIZE + DIST + seq.height + DIST, - width: seq.width + SIZE, - } - } - if(tree.type === "condition") { - const sep = SIZE + DIST; - return { - svg: svg` - ${this._draw_connector( - 0, R, - -sep/2, SIZE + DIST + R - )} - ${this._draw_connector( - 0, R, - sep/2, SIZE + DIST + R - )} - ${this._draw_connector( - -sep/2, SIZE + DIST + R, - 0, SIZE + DIST + SIZE + DIST, - )} - ${this._draw_node(0, 0, "condition", tree.idx)} - ${this._draw_node(-sep/2, SIZE+DIST, "TRUE", tree.idx)} - ${this._draw_node(sep/2, SIZE+DIST, "FALSE", tree.idx)} - `, - height: SIZE + DIST + SIZE + DIST, - width: SIZE + DIST + SIZE, - } - } - - if(tree.type === "choose") { - const choices = tree.choices.map(x => this.draw_tree(x)); - const maxHeight = choices.reduce((a,i) => Math.max(a, i.height), 0); - const sep = SIZE + DIST; - const totWidth = choices.reduce((a,i) => a+i.width, 0)+DIST*(choices.length-1); - const offset = choices.map((sum => value => sum += value.width)(0)); - return { - svg: svg` - ${choices.map((choice, idx) => this._draw_connector( - 0, - R, - -totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2, - SIZE + DIST + R, - ))} - ${choices.map((choice, idx) => this._draw_connector( - -totWidth/2+idx*DIST + (idx?offset[idx-1]:0)+ choice.width/2, - SIZE + DIST + R, - -totWidth/2+idx*DIST + (idx?offset[idx-1]:0)+ choice.width/2, - SIZE + DIST + R + DIST, - ))} - ${choices.map((choice, idx) => svg` - ${this._draw_connector( - -totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2, - SIZE + DIST + R + DIST + choice.height, - -totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2, - SIZE + DIST + R + DIST + maxHeight + DIST - )} - ${this._draw_connector( - -totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2, - SIZE + DIST + R + DIST + maxHeight + DIST, - 0, - SIZE + DIST + R + DIST + maxHeight + DIST + SIZE + DIST - )} - `)} - ${this._draw_node(0, 0, tree.type, tree.idx)} - ${choices.map((choice, idx) => svg` - - ${choice.svg} - - `)} - `, - height: SIZE + DIST + R + DIST + maxHeight + DIST + SIZE + DIST, - width: totWidth, - } - } - - return { - svg: this._draw_node(0, 0, tree.type, tree.idx), - height: SIZE, - width: SIZE, - } - } - let height = 0; - let width = 0; - for (const [idx, node] of tree.entries()) { - const n = this.draw_tree(node); - if(idx) { - nodes.splice(nodes.length-1, 0, this._draw_connector( - 0, - height-DIST, - 0, - height+DIST - )); - } - nodes.push(svg`${n.svg}`); - height += n.height+DIST; - width = Math.max(width, n.width); - } - height = height-DIST; - return { - svg: svg`${nodes.map((node, idx) => node)}`, - height, - width, - }; - - } - - render() { - let processed_tree = this.draw_tree(this.tree); - return html` - - - - ${processed_tree.svg} - - - `; - } - -} - -customElements.define("script-graph", ScriptGraph); +import "./script-graph"; let index_counter = 0; let nodes = []; diff --git a/src/script-graph.js b/src/script-graph.js new file mode 100644 index 0000000..92e15b7 --- /dev/null +++ b/src/script-graph.js @@ -0,0 +1,282 @@ +import { + LitElement, + html, + css, + svg, + property +} from "lit-element"; +import { + mdiCallSplit, + mdiAbTesting, + mdiCheck, + mdiClose, + mdiChevronRight, + mdiExclamation, + mdiTimerOutline, + mdiTrafficLight, + mdiRefresh, + mdiArrowUp, + mdiCodeJson, + mdiCheckBoxOutline, + mdiCheckboxBlankOutline, + mdiAsterisk, +} from "@mdi/js"; + +const SIZE = 35; +const BORDER = 5; +const R = SIZE/2; +const DIST = 20; + +const ICONS = { + choose: mdiCallSplit, + chooseChoice: mdiCheckBoxOutline, + chooseDefault: mdiCheckboxBlankOutline, + condition: mdiAbTesting, + TRUE: mdiCheck, + FALSE: mdiClose, + service: mdiChevronRight, + event: mdiExclamation, + delay: mdiTimerOutline, + wait: mdiTrafficLight, + loop: mdiRefresh, + loopReturn: mdiArrowUp, + YAML: mdiCodeJson, +}; + +class ScriptGraph extends LitElement { + + static get properties() { + return { + tree: {}, + selected: {}, + }; + } + + _select(idx) { + this.selected = idx; + const ev = new CustomEvent("selected", {detail: idx}); + this.dispatchEvent(ev); + } + + _draw_node(x, y, node, idx) { + const selected = (Array.isArray(this.selected) + ? (this.selected[0] === idx) + : false + ) || (idx === this.selected); + return svg` + this._select(idx)} + /> + + ${node in ICONS + ? svg` + + + + ` + : svg` + ${node} + `} + + `; + } + _draw_connector(x1, y1, x2, y2) { + return svg` + + `; + } + + + draw_tree(tree) { + if(!tree) return {svg: svg``, width: 1, height: 1}; + let nodes = []; + let connections = []; + if(!Array.isArray(tree)) { + const selected = tree.idx == this.selected; + if(tree.type === "loop") { + const seq = this.draw_tree(tree.sequence); + const sep = seq.width + DIST; + const left = -(seq.width + SIZE + DIST)/2 + SIZE/2; + const right = left+DIST+seq.width/2 + SIZE/2; + return { + svg: svg` + ${this._draw_connector( + 0, R, + left, SIZE + DIST + R + )} + ${this._draw_connector( + 0, R, + right, SIZE + DIST + R + )} + ${this._draw_connector( + right, SIZE + DIST + seq.height - R, + 0, SIZE + DIST + seq.height + DIST + )} + ${this._draw_connector( + left, SIZE + DIST + seq.height - R, + 0, SIZE + DIST + seq.height + DIST + )} + ${this._draw_connector( + left, SIZE + DIST + R, + left, SIZE + DIST + seq.height - R + )} + ${this._draw_node(0, 0, "loop", tree.idx)} + ${this._draw_node(left, SIZE+DIST+seq.height/2-R, "loopReturn", tree.idx)} + + ${seq.svg} + + `, + height: SIZE + DIST + seq.height + DIST, + width: seq.width + SIZE, + } + } + if(tree.type === "condition") { + const sep = SIZE + DIST; + return { + svg: svg` + ${this._draw_connector( + 0, R, + -sep/2, SIZE + DIST + R + )} + ${this._draw_connector( + 0, R, + sep/2, SIZE + DIST + R + )} + ${this._draw_connector( + -sep/2, SIZE + DIST + R, + 0, SIZE + DIST + SIZE + DIST, + )} + ${this._draw_node(0, 0, "condition", tree.idx)} + ${this._draw_node(-sep/2, SIZE+DIST, "TRUE", tree.idx)} + ${this._draw_node(sep/2, SIZE+DIST, "FALSE", tree.idx)} + `, + height: SIZE + DIST + SIZE + DIST, + width: SIZE + DIST + SIZE, + } + } + + if(tree.type === "choose") { + const choices = tree.choices.map(x => this.draw_tree(x)); + const maxHeight = choices.reduce((a,i) => Math.max(a, i.height), 0); + const sep = SIZE + DIST; + const totWidth = choices.reduce((a,i) => a+i.width, 0)+DIST*(choices.length-1); + const offset = choices.map((sum => value => sum += value.width)(0)); + return { + svg: svg` + ${choices.map((choice, idx) => this._draw_connector( + 0, + R, + -totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2, + SIZE + DIST + R, + ))} + ${choices.map((choice, idx) => this._draw_connector( + -totWidth/2+idx*DIST + (idx?offset[idx-1]:0)+ choice.width/2, + SIZE + DIST + R, + -totWidth/2+idx*DIST + (idx?offset[idx-1]:0)+ choice.width/2, + SIZE + DIST + R + DIST, + ))} + ${choices.map((choice, idx) => svg` + ${this._draw_connector( + -totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2, + SIZE + DIST + R + DIST + choice.height, + -totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2, + SIZE + DIST + R + DIST + maxHeight + DIST + )} + ${this._draw_connector( + -totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2, + SIZE + DIST + R + DIST + maxHeight + DIST, + 0, + SIZE + DIST + R + DIST + maxHeight + DIST + SIZE + DIST + )} + `)} + ${this._draw_node(0, 0, tree.type, tree.idx)} + ${choices.map((choice, idx) => svg` + + ${choice.svg} + + `)} + `, + height: SIZE + DIST + R + DIST + maxHeight + DIST + SIZE + DIST, + width: totWidth, + } + } + + return { + svg: this._draw_node(0, 0, tree.type, tree.idx), + height: SIZE, + width: SIZE, + } + } + let height = 0; + let width = 0; + for (const [idx, node] of tree.entries()) { + const n = this.draw_tree(node); + if(idx) { + nodes.splice(nodes.length-1, 0, this._draw_connector( + 0, + height-DIST, + 0, + height+DIST + )); + } + nodes.push(svg`${n.svg}`); + height += n.height+DIST; + width = Math.max(width, n.width); + } + height = height-DIST; + return { + svg: svg`${nodes.map((node, idx) => node)}`, + height, + width, + }; + + } + + render() { + let processed_tree = this.draw_tree(this.tree); + return html` + + + + ${processed_tree.svg} + + + `; + } + +} + +customElements.define("script-graph", ScriptGraph);