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` `, 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` ${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` ${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; }