Refactoring

This commit is contained in:
Thomas Lovén 2020-08-21 08:43:55 +02:00
parent d039c391e7
commit 2865f8eb07
2 changed files with 283 additions and 284 deletions

View File

@ -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`
<circle
cx=${x}
cy=${y + SIZE/2}
r=${R}
stroke-width=${BORDER}
stroke="black"
fill="white"
class="${selected ? 'selected' : ''}"
@click=${() => this._select(idx)}
/>
<g style="pointer-events: none">
${node in ICONS
? svg`
<g transform="translate(${x-12} ${y+R-12})">
<path d="${ICONS[node]}"/>
</g>
`
: svg`
<text
x=${x}
y=${y+R}
text-anchor="middle"
dominant-baseline="middle"
>${node}</text>
`}
</g>
`;
}
_draw_connector(x1, y1, x2, y2) {
return svg`
<line
x1=${x1}
y1=${y1}
x2=${x2}
y2=${y2}
stroke-width=${BORDER}
stroke="black"
stroke-linecap="round"
/>
`;
}
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)}
<g transform="translate(${right} ${SIZE + DIST})">
${seq.svg}
</g>
`,
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`
<g transform="translate(
${-totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2}
${SIZE + DIST + R + DIST})">
${choice.svg}
</g>
`)}
`,
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`<g transform="translate(0 ${height})">${n.svg}</g>`);
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`
<style>
circle, line {
stroke: rgb(3, 169, 244);
}
circle.selected {
stroke: rgb(255, 152, 0);
}
svg {
font-family: Roboto, Noto, sans-serif;
font-size: 14px;
}
</style>
<svg width=${processed_tree.width + SIZE} height=${processed_tree.height + BORDER}>
<g transform="translate(${processed_tree.width/2 + BORDER/2} ${BORDER/2})">
${processed_tree.svg}
</g>
</svg>
`;
}
}
customElements.define("script-graph", ScriptGraph);
import "./script-graph";
let index_counter = 0;
let nodes = [];

282
src/script-graph.js Normal file
View File

@ -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`
<circle
cx=${x}
cy=${y + SIZE/2}
r=${R}
stroke-width=${BORDER}
stroke="black"
fill="white"
class="${selected ? 'selected' : ''}"
@click=${() => this._select(idx)}
/>
<g style="pointer-events: none">
${node in ICONS
? svg`
<g transform="translate(${x-12} ${y+R-12})">
<path d="${ICONS[node]}"/>
</g>
`
: svg`
<text
x=${x}
y=${y+R}
text-anchor="middle"
dominant-baseline="middle"
>${node}</text>
`}
</g>
`;
}
_draw_connector(x1, y1, x2, y2) {
return svg`
<line
x1=${x1}
y1=${y1}
x2=${x2}
y2=${y2}
stroke-width=${BORDER}
stroke="black"
stroke-linecap="round"
/>
`;
}
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)}
<g transform="translate(${right} ${SIZE + DIST})">
${seq.svg}
</g>
`,
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`
<g transform="translate(
${-totWidth/2+idx*DIST + (idx?offset[idx-1]:0) + choice.width/2}
${SIZE + DIST + R + DIST})">
${choice.svg}
</g>
`)}
`,
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`<g transform="translate(0 ${height})">${n.svg}</g>`);
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`
<style>
circle, line {
stroke: rgb(3, 169, 244);
}
circle.selected {
stroke: rgb(255, 152, 0);
}
svg {
font-family: Roboto, Noto, sans-serif;
font-size: 14px;
}
</style>
<svg width=${processed_tree.width + SIZE} height=${processed_tree.height + BORDER}>
<g transform="translate(${processed_tree.width/2 + BORDER/2} ${BORDER/2})">
${processed_tree.svg}
</g>
</svg>
`;
}
}
customElements.define("script-graph", ScriptGraph);