262 lines
6.2 KiB
TypeScript
262 lines
6.2 KiB
TypeScript
import "./hat-graph";
|
|
import "./hat-graph-node";
|
|
import { HatGraphNode } from "./hat-graph-node";
|
|
|
|
import {
|
|
mdiCallSplit,
|
|
mdiAbTesting,
|
|
mdiCheck,
|
|
mdiClose,
|
|
mdiChevronRight,
|
|
mdiExclamation,
|
|
mdiTimerOutline,
|
|
mdiTrafficLight,
|
|
mdiRefresh,
|
|
mdiArrowUp,
|
|
mdiCodeJson,
|
|
mdiCheckBoxOutline,
|
|
mdiCheckboxBlankOutline,
|
|
mdiAsterisk,
|
|
} from "@mdi/js";
|
|
import { HatGraph } from "./hat-graph";
|
|
import { html } from "lit-element";
|
|
import { render } from "lit-html";
|
|
|
|
const OPTIONS = [
|
|
"condition",
|
|
"delay",
|
|
"device_id",
|
|
"event",
|
|
"scene",
|
|
"service",
|
|
"wait_template",
|
|
"repeat",
|
|
"choose",
|
|
];
|
|
|
|
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,
|
|
};
|
|
|
|
const SPECIAL_NODES = {
|
|
condition: makeConditionNode,
|
|
choose: makeChooseNode,
|
|
repeat: makeRepeatNode,
|
|
};
|
|
|
|
function makeConditionNode(config) {
|
|
const graph = document.createElement("hat-graph") as HatGraph;
|
|
graph.branching = true;
|
|
const focused = () =>
|
|
graph.dispatchEvent(
|
|
new CustomEvent("node-selected", { detail: { config }, bubbles: true })
|
|
);
|
|
|
|
render(
|
|
html`
|
|
<hat-graph-node slot="head" .iconPath=${mdiAbTesting} @focus=${focused}>
|
|
</hat-graph-node>
|
|
<hat-graph-node .iconPath=${mdiCheck} @focus=${focused}></hat-graph-node>
|
|
<hat-graph-node .iconPath=${mdiClose} @focus=${focused}></hat-graph-node>
|
|
`,
|
|
graph
|
|
);
|
|
return graph;
|
|
}
|
|
|
|
function makeChooseNode(config) {
|
|
const graph = document.createElement("hat-graph") as HatGraph;
|
|
graph.config = config;
|
|
graph.branching = true;
|
|
|
|
const focused = () =>
|
|
graph.dispatchEvent(
|
|
new CustomEvent("node-selected", { detail: { config }, bubbles: true })
|
|
);
|
|
|
|
render(
|
|
html`
|
|
<hat-graph-node
|
|
slot="head"
|
|
.iconPath="${mdiCallSplit}"
|
|
@focus=${focused}
|
|
draggable="true"
|
|
.dragtarget=${graph}
|
|
>
|
|
</hat-graph-node>
|
|
${config.choose?.map((branch) => {
|
|
const head = document.createElement("hat-graph-node") as HatGraphNode;
|
|
head.iconPath = mdiCheckBoxOutline;
|
|
head.addEventListener("focus", focused);
|
|
|
|
return makeGraph(branch.sequence, head);
|
|
})}
|
|
${(() => {
|
|
const head = document.createElement("hat-graph-node") as HatGraphNode;
|
|
head.iconPath = mdiCheckboxBlankOutline;
|
|
head.addEventListener("focus", focused);
|
|
return makeGraph(config.default, head);
|
|
})()}
|
|
`,
|
|
graph
|
|
);
|
|
return graph;
|
|
}
|
|
|
|
function makeRepeatNode(config) {
|
|
const graph = document.createElement("hat-graph") as HatGraph;
|
|
graph.branching = true;
|
|
|
|
const focused = () =>
|
|
graph.dispatchEvent(
|
|
new CustomEvent("node-selected", { detail: { config }, bubbles: true })
|
|
);
|
|
|
|
render(
|
|
html`
|
|
<hat-graph-node
|
|
.iconPath=${mdiArrowUp}
|
|
@focus=${focused}
|
|
></hat-graph-node>
|
|
${makeGraph(config.repeat.sequence)}
|
|
`,
|
|
graph
|
|
);
|
|
return graph;
|
|
}
|
|
|
|
function makeNode(config) {
|
|
if (typeof config === "string") return undefined;
|
|
const type = OPTIONS.find((key) => key in config) || "yaml";
|
|
|
|
if (type in SPECIAL_NODES) {
|
|
return SPECIAL_NODES[type](config);
|
|
}
|
|
|
|
const node = document.createElement("hat-graph-node") as HatGraphNode;
|
|
|
|
node.iconPath = ICONS[type];
|
|
node.draggable = true;
|
|
|
|
node.config = config;
|
|
|
|
node.addEventListener("focus", (ev) => {
|
|
node.dispatchEvent(
|
|
new CustomEvent("node-selected", { detail: { config }, bubbles: true })
|
|
);
|
|
});
|
|
|
|
return node;
|
|
}
|
|
|
|
export function makeGraph(nodes, head = undefined) {
|
|
const graph = document.createElement("hat-graph") as HatGraph;
|
|
graph.addEventListener("dragenter", (ev) => {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
try {
|
|
graph.appendChild((window as any)._dragElement);
|
|
} catch (e) {
|
|
if (!(e instanceof DOMException)) throw e;
|
|
}
|
|
});
|
|
(graph as any).test = "Hello!";
|
|
|
|
if (head) {
|
|
head.slot = "head";
|
|
graph.appendChild(head);
|
|
}
|
|
|
|
for (const [i, nodeConfig] of nodes.entries()) {
|
|
const node = makeNode(nodeConfig);
|
|
if (!node) {
|
|
window.setTimeout(() => {
|
|
const config = [...nodes];
|
|
config.splice(i, 1);
|
|
|
|
graph.dispatchEvent(
|
|
new CustomEvent("update-node", { detail: { config }, bubbles: true })
|
|
);
|
|
}, 100);
|
|
continue;
|
|
}
|
|
|
|
node.addEventListener("dragover", (ev) => {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
});
|
|
node.addEventListener("dragenter", (ev) => {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
if (node === (window as any)._dragElement) return;
|
|
try {
|
|
graph.insertBefore((window as any)._dragElement, node);
|
|
(window as any)._dragTarget = node;
|
|
} catch (e) {
|
|
if (!(e instanceof DOMException)) throw e;
|
|
}
|
|
});
|
|
|
|
node.addEventListener("drop", (ev) => {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
if ((window as any)._dragTarget) {
|
|
console.log("Drop onto ", (window as any)._dragTarget);
|
|
const config = { ...(window as any)._dragElement.config };
|
|
(window as any)._dragTarget.placeNode(config);
|
|
}
|
|
});
|
|
|
|
node.addEventListener("update-node", (ev) => {
|
|
ev.stopPropagation();
|
|
|
|
const config = [...nodes];
|
|
config[i] = ev.detail.config;
|
|
|
|
graph.dispatchEvent(
|
|
new CustomEvent("update-node", { detail: { config }, bubbles: true })
|
|
);
|
|
});
|
|
|
|
node.addEventListener("delete-node", (ev) => {
|
|
ev.stopPropagation();
|
|
|
|
const config = [...nodes];
|
|
config.splice(i, 1);
|
|
|
|
graph.dispatchEvent(
|
|
new CustomEvent("update-node", { detail: { config }, bubbles: true })
|
|
);
|
|
});
|
|
|
|
node.addEventListener("place-node", (ev) => {
|
|
ev.stopPropagation();
|
|
|
|
const config = [...nodes];
|
|
config.splice(i, 0, ev.detail.config);
|
|
console.log(config);
|
|
|
|
graph.dispatchEvent(
|
|
new CustomEvent("update-node", { detail: { config }, bubbles: true })
|
|
);
|
|
});
|
|
|
|
graph.appendChild(node);
|
|
}
|
|
|
|
return graph;
|
|
}
|