Remove old code
This commit is contained in:
parent
da9cbbe8a5
commit
f9aab2b70e
@ -1,3 +1,5 @@
|
|||||||
|
import "./hat-graph";
|
||||||
|
import "./hat-graph-node";
|
||||||
import { HatGraphNode } from "./hat-graph-node";
|
import { HatGraphNode } from "./hat-graph-node";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
53
src/main.js
53
src/main.js
@ -1,61 +1,8 @@
|
|||||||
import { demoConfig2 } from "./demo-config";
|
import { demoConfig2 } from "./demo-config";
|
||||||
import "@vanillawc/wc-codemirror";
|
import "@vanillawc/wc-codemirror";
|
||||||
|
|
||||||
import "./script-graph";
|
|
||||||
// import "./script-graph3";
|
|
||||||
import "./hat-graph/hat-graph-node";
|
|
||||||
import "./hat-graph/hat-graph";
|
|
||||||
import { makeGraph } from "./hat-graph/make-graph";
|
import { makeGraph } from "./hat-graph/make-graph";
|
||||||
|
|
||||||
import { ActionHandler } from "./script-to-graph";
|
|
||||||
|
|
||||||
import { mdiAsterisk, mdiArrowUp, mdiArrowDown } from "@mdi/js";
|
|
||||||
|
|
||||||
let index_counter = 0;
|
|
||||||
let nodes = [];
|
|
||||||
|
|
||||||
window.onload = () => {
|
|
||||||
return;
|
|
||||||
//const graph2 = document.createElement("script-graph3");
|
|
||||||
//document.querySelector("#graph2").appendChild(graph2);
|
|
||||||
|
|
||||||
let src = demoConfig;
|
|
||||||
|
|
||||||
const fullcode = document.querySelector("#fullcode");
|
|
||||||
fullcode.mode = "yaml";
|
|
||||||
window.setTimeout(() => (fullcode.value = jsyaml.safeDump(src)), 100);
|
|
||||||
|
|
||||||
const updateButton = document.querySelector("#updateButton");
|
|
||||||
updateButton.addEventListener("click", () => {
|
|
||||||
src = jsyaml.safeLoad(fullcode.value);
|
|
||||||
index_counter = 0;
|
|
||||||
nodes = [];
|
|
||||||
tr.actions = src;
|
|
||||||
graph.tree = tr.graph;
|
|
||||||
});
|
|
||||||
|
|
||||||
const graph = document.createElement("script-graph");
|
|
||||||
const tr = new ActionHandler();
|
|
||||||
window.tr = tr;
|
|
||||||
tr.actions = src;
|
|
||||||
tr.updateCallback = (actions) => {
|
|
||||||
graph.tree = tr.graph;
|
|
||||||
fullcode.value = jsyaml.safeDump(tr.actions);
|
|
||||||
};
|
|
||||||
tr.selectCallback = (idx, action, update) => {
|
|
||||||
graph.tree = tr.graph;
|
|
||||||
const code = document.querySelector("#snippet");
|
|
||||||
code.value = jsyaml.safeDump(action);
|
|
||||||
document.querySelector("#saveSnippet").onclick = () =>
|
|
||||||
update(jsyaml.safeLoad(code.value));
|
|
||||||
document.querySelector("#deleteSnippet").onclick = () =>
|
|
||||||
update(jsyaml.safeLoad(null));
|
|
||||||
};
|
|
||||||
|
|
||||||
graph.tree = tr.graph;
|
|
||||||
document.querySelector("#graph").appendChild(graph);
|
|
||||||
};
|
|
||||||
|
|
||||||
function nodeSelected(ev) {
|
function nodeSelected(ev) {
|
||||||
const code = document.querySelector("#snippet");
|
const code = document.querySelector("#snippet");
|
||||||
code.value = jsyaml.safeDump(ev.detail.config);
|
code.value = jsyaml.safeDump(ev.detail.config);
|
||||||
|
@ -1,204 +0,0 @@
|
|||||||
import {
|
|
||||||
LitElement,
|
|
||||||
html,
|
|
||||||
css,
|
|
||||||
svg,
|
|
||||||
property
|
|
||||||
} from "lit-element";
|
|
||||||
|
|
||||||
import { mdiPlus } from "@mdi/js";
|
|
||||||
|
|
||||||
const SIZE = 35;
|
|
||||||
const DIST = 20;
|
|
||||||
|
|
||||||
export interface TreeNode {
|
|
||||||
icon: String;
|
|
||||||
styles?: String;
|
|
||||||
end?: Boolean;
|
|
||||||
children?: (TreeNode | TreeNode[])[];
|
|
||||||
clickCallback?: any;
|
|
||||||
addCallback?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScriptGraph2 extends LitElement {
|
|
||||||
@property() selected = null;
|
|
||||||
@property() tree: [TreeNode];
|
|
||||||
@property() nodeSize = SIZE;
|
|
||||||
@property() nodeSeparation = DIST;
|
|
||||||
|
|
||||||
private _draw_node(x, y, node) {
|
|
||||||
return svg`
|
|
||||||
<circle
|
|
||||||
cx="${x}"
|
|
||||||
cy="${y + this.nodeSize/2}"
|
|
||||||
r="${this.nodeSize/2}"
|
|
||||||
class="node"
|
|
||||||
@click=${node.clickCallback}
|
|
||||||
style=${node.styles}
|
|
||||||
/>
|
|
||||||
<g style="pointer-events: none" transform="translate(${x - 12} ${y + this.nodeSize/2 - 12})">
|
|
||||||
<path d="${node.icon}"/>
|
|
||||||
</g>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _draw_new_node(x, y, node) {
|
|
||||||
return svg`
|
|
||||||
<circle
|
|
||||||
cx="${x}"
|
|
||||||
cy="${y + this.nodeSize/4}"
|
|
||||||
r="${this.nodeSize/4}"
|
|
||||||
class="newnode"
|
|
||||||
@click=${node.addCallback}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private _draw_connector(x1, y1, x2, y2) {
|
|
||||||
return svg`
|
|
||||||
<line
|
|
||||||
x1=${x1}
|
|
||||||
y1=${y1}
|
|
||||||
x2=${x2}
|
|
||||||
y2=${y2}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _draw_tree(tree: TreeNode | TreeNode[]) {
|
|
||||||
if(!tree) return {svg: `Hello`, width: 0, height: 0};
|
|
||||||
if(!Array.isArray(tree)) {
|
|
||||||
let height = this.nodeSize;
|
|
||||||
let width = this.nodeSize;
|
|
||||||
let pieces = [];
|
|
||||||
|
|
||||||
if(tree.children) {
|
|
||||||
const childTrees = tree.children.map((c) =>this._draw_tree(c));
|
|
||||||
height += childTrees.reduce((a,i) => Math.max(a, i.height), 0);
|
|
||||||
width = childTrees.reduce((a,i) => a+i.width, 0) + this.nodeSeparation*(tree.children.length - 1);
|
|
||||||
const offsets = childTrees.map((sum => value => sum += value.width + this.nodeSeparation)(0));
|
|
||||||
|
|
||||||
let bottomConnectors = false;
|
|
||||||
|
|
||||||
for (const [idx, child] of childTrees.entries()) {
|
|
||||||
const x = -width/2 + (idx? offsets[idx-1] : 0) + child.width/2;
|
|
||||||
// Draw top connectors
|
|
||||||
pieces.push(this._draw_connector(
|
|
||||||
0,
|
|
||||||
this.nodeSize/2,
|
|
||||||
x,
|
|
||||||
this.nodeSize + this.nodeSeparation
|
|
||||||
));
|
|
||||||
|
|
||||||
|
|
||||||
let endNode = tree.children[idx];
|
|
||||||
if(Array.isArray(endNode)) endNode = endNode[endNode.length -1];
|
|
||||||
if(endNode.end !== false) {
|
|
||||||
// Draw bottom fill
|
|
||||||
pieces.push(this._draw_connector(
|
|
||||||
x,
|
|
||||||
this.nodeSeparation + child.height,
|
|
||||||
x,
|
|
||||||
this.nodeSeparation + height
|
|
||||||
));
|
|
||||||
|
|
||||||
// Draw bottom connectors
|
|
||||||
pieces.push(this._draw_connector(
|
|
||||||
x,
|
|
||||||
this.nodeSeparation + height,
|
|
||||||
0,
|
|
||||||
this.nodeSeparation + height + this.nodeSize/2 + this.nodeSeparation
|
|
||||||
));
|
|
||||||
bottomConnectors = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw child tree
|
|
||||||
pieces.push(svg`
|
|
||||||
<g class="a" transform="translate(${x} ${this.nodeSize + this.nodeSeparation})">
|
|
||||||
${child.svg}
|
|
||||||
</g>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
if(bottomConnectors)
|
|
||||||
height += this.nodeSize + this.nodeSeparation;
|
|
||||||
|
|
||||||
}
|
|
||||||
if(tree.addCallback) {
|
|
||||||
pieces.push(this._draw_connector(
|
|
||||||
0,
|
|
||||||
height,
|
|
||||||
0,
|
|
||||||
height + this.nodeSeparation
|
|
||||||
));
|
|
||||||
pieces.push(this._draw_new_node(
|
|
||||||
0, height + this.nodeSeparation,
|
|
||||||
tree
|
|
||||||
));
|
|
||||||
height += this.nodeSeparation + this.nodeSize/2;
|
|
||||||
}
|
|
||||||
if(tree.end !== false) {
|
|
||||||
// Draw bottom connector
|
|
||||||
pieces.push(this._draw_connector(
|
|
||||||
0,
|
|
||||||
height,
|
|
||||||
0,
|
|
||||||
height + this.nodeSeparation
|
|
||||||
));
|
|
||||||
height += this.nodeSeparation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the node itself
|
|
||||||
pieces.push(this._draw_node(0, 0, tree));
|
|
||||||
|
|
||||||
return {svg: pieces, width, height};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Array of trees
|
|
||||||
let pieces = [];
|
|
||||||
let height = 0;
|
|
||||||
const children = tree.map((n) => this._draw_tree(n));
|
|
||||||
const width = children.reduce((a,i) => Math.max(a, i.width), 0);
|
|
||||||
for (const [_, node] of children.entries()) {
|
|
||||||
pieces.push(svg`
|
|
||||||
<g class="b" transform="translate(0, ${height})">
|
|
||||||
${node.svg}
|
|
||||||
</g>
|
|
||||||
`);
|
|
||||||
height += node.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {svg: pieces, width, height};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let tree = this._draw_tree(this.tree);
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
--stroke-clr: var(--stroke-color, rgb(3, 169, 244));
|
|
||||||
--hover-clr: var(--hover-color, rgb(255, 152, 0));
|
|
||||||
}
|
|
||||||
circle, line {
|
|
||||||
stroke: var(--stroke-clr);
|
|
||||||
stroke-width: 5;
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
.newnode:hover {
|
|
||||||
stroke: var(--hover-clr);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<svg width=${tree.width + 32} height=${tree.height + 32}>
|
|
||||||
<g transform="translate(${tree.width/2 + 16} 16)">
|
|
||||||
${tree.svg}
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("script-graph", ScriptGraph2);
|
|
||||||
|
|
@ -1,298 +0,0 @@
|
|||||||
import {
|
|
||||||
LitElement,
|
|
||||||
html,
|
|
||||||
css,
|
|
||||||
svg,
|
|
||||||
property,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit-element";
|
|
||||||
|
|
||||||
import {
|
|
||||||
mdiCallSplit,
|
|
||||||
mdiAbTesting,
|
|
||||||
mdiCheck,
|
|
||||||
mdiClose,
|
|
||||||
mdiChevronRight,
|
|
||||||
mdiExclamation,
|
|
||||||
mdiTimerOutline,
|
|
||||||
mdiTrafficLight,
|
|
||||||
mdiRefresh,
|
|
||||||
mdiArrowUp,
|
|
||||||
mdiCodeJson,
|
|
||||||
mdiCheckBoxOutline,
|
|
||||||
mdiCheckboxBlankOutline,
|
|
||||||
mdiAsterisk,
|
|
||||||
mdiCircleOutline,
|
|
||||||
} from "@mdi/js";
|
|
||||||
|
|
||||||
const ICONS = {
|
|
||||||
"call-split": mdiCallSplit,
|
|
||||||
"ab-testing": mdiAbTesting,
|
|
||||||
check: mdiCheck,
|
|
||||||
close: mdiClose,
|
|
||||||
"chevron-right": mdiChevronRight,
|
|
||||||
exclamation: mdiExclamation,
|
|
||||||
asterisk: mdiAsterisk,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SIZE = 24;
|
|
||||||
|
|
||||||
interface GraphNode extends LitElement {
|
|
||||||
render_svg(): TemplateResult;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScriptGraphNode extends LitElement {
|
|
||||||
@property() icon = "chevron-right";
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
if (!this.hasAttribute("tabindex")) this.setAttribute("tabindex", "0");
|
|
||||||
}
|
|
||||||
|
|
||||||
get width() {
|
|
||||||
return SIZE + 5;
|
|
||||||
}
|
|
||||||
get height() {
|
|
||||||
return SIZE + 5;
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
return svg`
|
|
||||||
<svg
|
|
||||||
width="${this.width}"
|
|
||||||
height="${this.height}"
|
|
||||||
viewBox="${-this.width / 2} 0 ${this.width} ${this.height}"
|
|
||||||
>
|
|
||||||
<circle
|
|
||||||
cx="0"
|
|
||||||
cy="${this.width / 2}"
|
|
||||||
r="${SIZE / 2}"
|
|
||||||
/>
|
|
||||||
<g
|
|
||||||
style="pointer-events: none"
|
|
||||||
transform="translate(${-12} ${this.width / 2 - 12})"
|
|
||||||
>
|
|
||||||
${
|
|
||||||
ICONS[this.icon]
|
|
||||||
? svg`
|
|
||||||
<path d="${ICONS[this.icon]}"/>
|
|
||||||
`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
--stroke-clr: var(--stroke-color, rgb(3, 169, 244));
|
|
||||||
--hover-clr: var(--hover-color, rgb(255, 152, 0));
|
|
||||||
}
|
|
||||||
circle {
|
|
||||||
stroke: var(--stroke-clr);
|
|
||||||
stroke-width: 2;
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
:host(:hover) {
|
|
||||||
--stroke-clr: var(--hover-clr);
|
|
||||||
}
|
|
||||||
:host(:focus) {
|
|
||||||
--stroke-clr: green;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScriptGraphBranch extends LitElement {
|
|
||||||
@property() _num_items = 0;
|
|
||||||
@property() _branch_height = 30;
|
|
||||||
@property() _branch_curve = 25;
|
|
||||||
|
|
||||||
get width() {
|
|
||||||
let w = 0;
|
|
||||||
for (const c of this.children) {
|
|
||||||
w += (c as any).width ?? 0;
|
|
||||||
}
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
get height() {
|
|
||||||
let h = 0;
|
|
||||||
for (const c of this.children) {
|
|
||||||
h = Math.max(h, (c as any).height ?? 0);
|
|
||||||
}
|
|
||||||
return h + 2 * this._branch_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateChildren() {
|
|
||||||
this._num_items = this.children.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let branch_x = [];
|
|
||||||
let total = 0;
|
|
||||||
for (const c of Array.from(this.children)) {
|
|
||||||
const rect = c.getBoundingClientRect();
|
|
||||||
branch_x.push(rect.width / 2 + total);
|
|
||||||
total += rect.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
const line_end = this.height - this._branch_height;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<svg width="${this.width}" height="${this.height}">
|
|
||||||
<rect
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="${this.width}"
|
|
||||||
height="${this.height}"
|
|
||||||
fill="white"
|
|
||||||
/>
|
|
||||||
${branch_x.map((x) => {
|
|
||||||
return svg`
|
|
||||||
<path
|
|
||||||
class="line"
|
|
||||||
d="
|
|
||||||
M ${this.width / 2} 0
|
|
||||||
C ${this.width / 2} ${this._branch_curve}
|
|
||||||
${x} ${this._branch_height - this._branch_curve}
|
|
||||||
${x} ${this._branch_height}
|
|
||||||
L ${x} ${line_end}
|
|
||||||
C ${x} ${line_end + this._branch_curve}
|
|
||||||
${this.width / 2} ${this.height - this._branch_curve}
|
|
||||||
${this.width / 2} ${this.height}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</svg>
|
|
||||||
<script-graph-node id="head" .icon=${"call-split"}> </script-graph-node>
|
|
||||||
<div id="branches" style="top: ${this._branch_height}px;">
|
|
||||||
<slot @slotchange=${this.updateChildren}></slot>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
--stroke-clr: var(--stroke-color, rgb(3, 169, 244));
|
|
||||||
--hover-clr: var(--hover-color, rgb(255, 152, 0));
|
|
||||||
}
|
|
||||||
#branches {
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
left: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
path.line {
|
|
||||||
stroke: var(--stroke-clr);
|
|
||||||
stroke-width: 2;
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
#head {
|
|
||||||
position: Absolute;
|
|
||||||
top: 5px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
:host(:focus-within) #head {
|
|
||||||
--stroke-color: green;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScriptGraph3 extends LitElement {
|
|
||||||
@property() content = [];
|
|
||||||
@property() _width = 0;
|
|
||||||
@property() _height = 0;
|
|
||||||
@property() _distance = 20;
|
|
||||||
|
|
||||||
async updateChildren() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
childrenChangedCallback() {
|
|
||||||
console.log("Children changed");
|
|
||||||
}
|
|
||||||
|
|
||||||
get height() {
|
|
||||||
let h = 0;
|
|
||||||
for (const c of this.children) {
|
|
||||||
h += (c as any).height ?? 0;
|
|
||||||
h += this._distance;
|
|
||||||
}
|
|
||||||
return h + this._distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
get width() {
|
|
||||||
let w = 0;
|
|
||||||
for (const c of this.children) {
|
|
||||||
w = Math.max(w, (c as any).width ?? 0);
|
|
||||||
}
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<svg width="${this.width}" height="${this.height}">
|
|
||||||
<rect
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="${this.width}"
|
|
||||||
height="${this.height}"
|
|
||||||
fill="white"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
class="line"
|
|
||||||
d="
|
|
||||||
M ${this.width / 2} 0
|
|
||||||
L ${this.width / 2} ${this.height}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<div id="nodes" style="--distance: ${this._distance}px;">
|
|
||||||
<slot @slotchange=${this.updateChildren}></slot>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
--stroke-clr: var(--stroke-color, rgb(3, 169, 244));
|
|
||||||
--hover-clr: var(--hover-color, rgb(255, 152, 0));
|
|
||||||
}
|
|
||||||
#nodes {
|
|
||||||
position: absolute;
|
|
||||||
top: var(--distance, 10px);
|
|
||||||
left: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
::slotted(*) {
|
|
||||||
padding-bottom: var(--distance, 10px);
|
|
||||||
}
|
|
||||||
path.line {
|
|
||||||
stroke: var(--stroke-clr);
|
|
||||||
stroke-width: 2;
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("script-graph3", ScriptGraph3);
|
|
||||||
customElements.define("script-graph-node", ScriptGraphNode);
|
|
||||||
customElements.define("script-graph-branch", ScriptGraphBranch);
|
|
@ -1,262 +0,0 @@
|
|||||||
import {
|
|
||||||
mdiCallSplit,
|
|
||||||
mdiAbTesting,
|
|
||||||
mdiCheck,
|
|
||||||
mdiClose,
|
|
||||||
mdiChevronRight,
|
|
||||||
mdiExclamation,
|
|
||||||
mdiTimerOutline,
|
|
||||||
mdiTrafficLight,
|
|
||||||
mdiRefresh,
|
|
||||||
mdiArrowUp,
|
|
||||||
mdiCodeJson,
|
|
||||||
mdiCheckBoxOutline,
|
|
||||||
mdiCheckboxBlankOutline,
|
|
||||||
mdiAsterisk,
|
|
||||||
} from "@mdi/js";
|
|
||||||
|
|
||||||
const ICONS = {
|
|
||||||
new: mdiAsterisk,
|
|
||||||
service: mdiChevronRight,
|
|
||||||
condition: mdiAbTesting,
|
|
||||||
TRUE: mdiCheck,
|
|
||||||
FALSE: mdiClose,
|
|
||||||
delay: mdiTimerOutline,
|
|
||||||
wait_template: mdiTrafficLight,
|
|
||||||
event: mdiExclamation,
|
|
||||||
repeat: mdiRefresh,
|
|
||||||
repeatReturn: mdiArrowUp,
|
|
||||||
choose: mdiCallSplit,
|
|
||||||
chooseChoice: mdiCheckBoxOutline,
|
|
||||||
chooseDefault: mdiCheckboxBlankOutline,
|
|
||||||
YAML: mdiCodeJson,
|
|
||||||
};
|
|
||||||
|
|
||||||
import { TreeNode} from "./script-graph";
|
|
||||||
import { Action } from "./types";
|
|
||||||
|
|
||||||
const OPTIONS = [
|
|
||||||
"condition",
|
|
||||||
"delay",
|
|
||||||
"device_id",
|
|
||||||
"event",
|
|
||||||
"scene",
|
|
||||||
"service",
|
|
||||||
"wait_template",
|
|
||||||
"repeat",
|
|
||||||
"choose",
|
|
||||||
];
|
|
||||||
|
|
||||||
interface NodeHandler {
|
|
||||||
(action, selected: any[], select, update): TreeNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SPECIAL = {
|
|
||||||
condition: (action, selected, select, update) => {
|
|
||||||
return {
|
|
||||||
icon: ICONS["condition"],
|
|
||||||
clickCallback: () => select([1], action, update),
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
icon: ICONS["TRUE"],
|
|
||||||
clickCallback: () => select([2], action, update),
|
|
||||||
styles: selected[0]
|
|
||||||
? "stroke: orange;"
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: ICONS["FALSE"],
|
|
||||||
end: false,
|
|
||||||
clickCallback: () => select([3], action, update),
|
|
||||||
styles: selected[0]
|
|
||||||
? "stroke: orange;"
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
repeat: (action, selected, select, update) => {
|
|
||||||
let seq = action.repeat.sequence;
|
|
||||||
if(!seq || !seq.length) seq = [{}];
|
|
||||||
const seqHandler = new ActionHandler(seq);
|
|
||||||
if(selected[0] !== undefined && selected[0] !== -1)
|
|
||||||
seqHandler.selected = selected;
|
|
||||||
seqHandler.selectCallback = select;
|
|
||||||
seqHandler.updateCallback = (a) => {
|
|
||||||
action.repeat["sequence"] = a;
|
|
||||||
update(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
icon: ICONS["repeat"],
|
|
||||||
clickCallback: () => select([-1], action, update),
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
icon: ICONS["repeatReturn"],
|
|
||||||
clickCallback: () => select([-1], action, update),
|
|
||||||
styles: selected[0] === -1
|
|
||||||
? "stroke: orange;"
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
seqHandler.graph,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
choose: (action, selected, select, update) => {
|
|
||||||
|
|
||||||
let def = action.default || [{}];
|
|
||||||
const defaultHandler = new ActionHandler(def);
|
|
||||||
if(selected[0] === -2)
|
|
||||||
defaultHandler.selected = selected.slice(1);
|
|
||||||
defaultHandler.selectCallback = (i, a, u) => {
|
|
||||||
select( [-2].concat(i), a, u);
|
|
||||||
};
|
|
||||||
defaultHandler.updateCallback = (a) => {
|
|
||||||
action.default = defaultHandler.actions;
|
|
||||||
update(action);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
|
||||||
icon: ICONS["choose"],
|
|
||||||
clickCallback: () => select([-1], action, update),
|
|
||||||
children: [
|
|
||||||
...action.choose.map((b,idx) => {
|
|
||||||
const handler = new ActionHandler(b.sequence || [{}]);
|
|
||||||
if(selected[0] === idx)
|
|
||||||
handler.selected = selected.slice(1);
|
|
||||||
handler.selectCallback = (i, a, u) => {
|
|
||||||
select([idx].concat(i), a, u);
|
|
||||||
};
|
|
||||||
handler.updateCallback = (a) => {
|
|
||||||
b.sequence = handler.actions;
|
|
||||||
action.choose[idx] = b;
|
|
||||||
update(action);
|
|
||||||
};
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
icon: ICONS["chooseChoice"],
|
|
||||||
clickCallback: () => select([idx], b, (a) => {
|
|
||||||
action.choose[idx] = a;
|
|
||||||
update(action);
|
|
||||||
}),
|
|
||||||
styles: selected[0] === idx
|
|
||||||
? "stroke: orange;"
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
handler.graph,
|
|
||||||
];
|
|
||||||
|
|
||||||
}),
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
icon: ICONS["chooseDefault"],
|
|
||||||
clickCallback: () => select([-2], def, (a) => {
|
|
||||||
action.default = a;
|
|
||||||
update(action);
|
|
||||||
}),
|
|
||||||
styles: selected[0] === -2
|
|
||||||
? "stroke: orange;"
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
defaultHandler.graph,
|
|
||||||
]
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
interface NoAction {
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ActionHandler {
|
|
||||||
_actions: (Action | NoAction)[] = [];
|
|
||||||
updateCallback = null;
|
|
||||||
selectCallback = null;
|
|
||||||
selected: number[] = [];
|
|
||||||
|
|
||||||
constructor(actions: (Action | NoAction)[] = []) {
|
|
||||||
this._actions = actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
set actions(actions) {
|
|
||||||
this._actions = actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
get actions() {
|
|
||||||
if(!this._actions.length) this._actions = [{}];
|
|
||||||
return this._actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
get graph() {
|
|
||||||
return this.actions.map((action, idx) => this._make_graph_node(idx, action));
|
|
||||||
}
|
|
||||||
|
|
||||||
_update_action(idx: number, action) {
|
|
||||||
if(action === null)
|
|
||||||
this.actions.splice(idx, 1);
|
|
||||||
else
|
|
||||||
this.actions[idx] = action;
|
|
||||||
if (this.updateCallback)
|
|
||||||
this.updateCallback(this.actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
_add_action(idx: number) {
|
|
||||||
this.actions.splice(idx, 0, {});
|
|
||||||
if (this.updateCallback)
|
|
||||||
this.updateCallback(this.actions);
|
|
||||||
this._select_node([idx], {}, (a) =>this._update_action(idx, a));
|
|
||||||
}
|
|
||||||
|
|
||||||
_select_node(path: number[], action, update=null) {
|
|
||||||
this.selected = path;
|
|
||||||
if (this.selectCallback)
|
|
||||||
this.selectCallback(path, action, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
_make_graph_node(idx: number, action): TreeNode {
|
|
||||||
let _type = "yaml";
|
|
||||||
if (Object.keys(action).length === 0)
|
|
||||||
_type = "new";
|
|
||||||
else
|
|
||||||
_type = OPTIONS.find((option) => option in action) || "YAML";
|
|
||||||
|
|
||||||
const selected = this.selected.length >= 1 && this.selected[0] == idx;
|
|
||||||
let node: TreeNode = {icon: ""};
|
|
||||||
|
|
||||||
if (_type in SPECIAL) {
|
|
||||||
node = SPECIAL[_type](
|
|
||||||
action,
|
|
||||||
selected ? this.selected.slice(1): [],
|
|
||||||
(i, a, u) => this._select_node([idx].concat(i), a, u),
|
|
||||||
(a) => this._update_action(idx, a),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
node = {
|
|
||||||
icon: ICONS[_type],
|
|
||||||
clickCallback: () => {
|
|
||||||
this._select_node(
|
|
||||||
[idx],
|
|
||||||
action,
|
|
||||||
(a) => this._update_action(idx, a)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
addCallback: () => this._add_action(idx+1),
|
|
||||||
styles: selected
|
|
||||||
? "stroke: orange"
|
|
||||||
: _type === "new"
|
|
||||||
? "stroke: lightgreen;"
|
|
||||||
: undefined,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
70
src/types.ts
70
src/types.ts
@ -1,70 +0,0 @@
|
|||||||
export interface Condition {
|
|
||||||
condition: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EventAction {
|
|
||||||
event: string;
|
|
||||||
event_data?: { [key: string]: any };
|
|
||||||
event_data_template?: { [key: string]: any };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ServiceAction {
|
|
||||||
service: string;
|
|
||||||
entity_id?: string;
|
|
||||||
data?: { [key: string]: any };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeviceAction {
|
|
||||||
device_id: string;
|
|
||||||
domain: string;
|
|
||||||
entity_id: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DelayAction {
|
|
||||||
delay: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SceneAction {
|
|
||||||
scene: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WaitAction {
|
|
||||||
wait_template: string;
|
|
||||||
timeout?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RepeatAction {
|
|
||||||
repeat: CountRepeat | WhileRepeat | UntilRepeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BaseRepeat {
|
|
||||||
sequence: Action[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CountRepeat extends BaseRepeat {
|
|
||||||
count: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WhileRepeat extends BaseRepeat {
|
|
||||||
while: Condition[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UntilRepeat extends BaseRepeat {
|
|
||||||
until: Condition[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChooseAction {
|
|
||||||
choose: [{ conditions: Condition[]; sequence: Action[] }];
|
|
||||||
default?: Action[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Action =
|
|
||||||
| EventAction
|
|
||||||
| DeviceAction
|
|
||||||
| ServiceAction
|
|
||||||
| Condition
|
|
||||||
| DelayAction
|
|
||||||
| SceneAction
|
|
||||||
| WaitAction
|
|
||||||
| RepeatAction
|
|
||||||
| ChooseAction;
|
|
Loading…
x
Reference in New Issue
Block a user