import { html, css, LitElement, unsafeCSS, PropertyValues } from "lit"; import { LitElementWw } from "@webwriter/lit"; import { customElement, property, query, queryAssignedElements, } from "lit/decorators.js"; //Drawflow Imports import { DrawflowNode } from "drawflow"; import { WebWriterGamebookButton } from "../components/gamebook/gamebook-components/gamebook-button/webwriter-gamebook-button"; import { WebWriterGamebookPage } from "../components/gamebook/gamebook-components/gamebook-containers/gamebook-page/webwriter-gamebook-page"; import { WebWriterGamebookPopup } from "../components/gamebook/gamebook-components/gamebook-containers/gamebook-popup/webwriter-gamebook-popup"; import { WebWriterGamebookBranch } from "../components/gamebook/gamebook-components/gamebook-containers/gamebook-branch/webwriter-gamebook-branch"; import { WebWriterGamebookBranchButton } from "../components/gamebook/gamebook-components/gamebook-branch-button/webwriter-gamebook-branch-button"; import { provide, consume, createContext } from "@lit/context"; import { editorState, GamebookEditorState, } from "./gamebook-editor-state-context"; @customElement("gamebook-container-manager") export class GamebookContainerManager extends LitElementWw { @queryAssignedElements({ flatten: true, selector: "webwriter-gamebook-page, webwriter-gamebook-popup, webwriter-gamebook-branch", }) accessor gamebookContainers; @query("slot") accessor slot; @consume({ context: editorState, subscribe: true }) @property({ type: Object, attribute: true, reflect: false }) public accessor editorStore = new GamebookEditorState("Default"); static get scopedElements() { return { // "webwriter-gamebook-page": WebWriterGamebookPage, // "webwriter-gamebook-popup": WebWriterGamebookPopup, // "webwriter-gamebook-branch": WebWriterGamebookBranch, }; } /* */ constructor() { super(); } /* */ protected firstUpdated(_changedProperties: any): void { const event = new CustomEvent("managerInitialized", { bubbles: true, composed: true, }); this.dispatchEvent(event); if (this.editorStore.selectedContainer !== undefined) { //Extraced the drawflowNodeId from the serialized container const value = this.editorStore.selectedContainer.attributes.find( (attr) => attr.name === "drawflownodeid" ).value; if (value) { const event = new CustomEvent("containerSelectFirstUpdate", { detail: { id: value }, bubbles: true, composed: true, }); this.dispatchEvent(event); } else { const event = new CustomEvent("containerError", { detail: { id: value }, bubbles: true, composed: true, }); this.dispatchEvent(event); } } } /* */ render() { return html` `; } /* */ public _deleteGamebookContainersById(drawflowNodeId: Number) { this.gamebookContainers.forEach((container) => { if (container.drawflowNodeId == drawflowNodeId) { container.remove(); } }); } /* */ public _notifyContainerGotDeleted(drawflowNodeId: Number) { const event = new CustomEvent("containerDeleted", { detail: { id: drawflowNodeId }, bubbles: true, composed: true, }); this.dispatchEvent(event); } /* */ public _deleteAllGamebookContainers() { this.gamebookContainers.forEach((container) => { container.remove(); }); //("delete successfull", this.gamebookContainers); } /* */ public _getContainerByDrawflowNodeId(id: number) { const container = this.gamebookContainers.find( (container) => container.drawflowNodeId === id ); if (!container) { console.error(`No container found with drawflowNodeId: ${id}`); } return container; } /* */ public _renameContainer(id: string, title: string) { const container = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == id ); (container as WebWriterGamebookPage).pageTitle = title; } /* */ public _showGamebookContainerById(nodeId: number) { let isContainerShown = false; // Flag to track if any container is shown this.gamebookContainers.forEach((container) => { if (container.drawflowNodeId == nodeId) { container.show(); isContainerShown = true; // Set flag to true when a container is shown } else { container.hide(); } }); if (!isContainerShown) { console.error(`No container found with drawflowNodeId: ${nodeId}`); } } /* */ public _hideAllGamebookContainers() { this.gamebookContainers.forEach((container) => { container.hide(); }); } /* */ public addConnectionButtonToContainer( outputNode: DrawflowNode, inputNode: DrawflowNode, output_class: string, input_class: string ) { const container = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == outputNode.id ); const connButton = document.createElement( "webwriter-gamebook-button" ) as WebWriterGamebookButton; connButton.setAttribute("name", inputNode.data.title); connButton.setAttribute("dataTargetId", inputNode.id.toString()); // Ensure uniqueness by adding a unique identifier connButton.setAttribute( "identifier", `${outputNode.id}-${output_class}-${inputNode.id}-${input_class}` ); container.appendChild(connButton); } /* */ public addSmartBranchButtonToContainer( outputNode: DrawflowNode, inputNode: DrawflowNode, output_class: string, input_class: string ) { const container = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == outputNode.id ); const branchButton = document.createElement( "webwriter-gamebook-branch-button" ) as WebWriterGamebookBranchButton; branchButton.setAttribute("name", inputNode.data.title); branchButton.setAttribute("dataTargetId", inputNode.id.toString()); // Ensure uniqueness by adding a unique identifier branchButton.setAttribute( "identifier", `${outputNode.id}-${output_class}-${inputNode.id}-${input_class}` ); container.appendChild(branchButton); container.branchesOff = inputNode.id; const branchContainer = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == inputNode.id ); branchContainer.incomingContainerId = outputNode.id; } /* */ public removeButtonFromContainer( outputId: string, inputId: string, outputClass: string, inputClass: string ) { const container = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == outputId ); const button = Array.from( container.buttons as NodeListOf ).find((button) => { return ( (button as any).identifier === `${outputId}-${outputClass}-${inputId}-${inputClass}` ); }); if (button) { button.setAttribute("identifier", "x"); button.remove(); if (button instanceof WebWriterGamebookBranchButton) { container.branchesOff = -1; const branchContainer = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == inputId ); branchContainer.incomingContainerId = -1; branchContainer.clearRules(); } } } /* */ public updateButtonIdsAfterOutputRemove( containerId: string, removed_output_class: string ) { const container = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == containerId ); // Extract the number from the output_class parameter const removedOutputClassNumber = parseInt( removed_output_class.split("_")[1], 10 ); // Iterate over each linkButton to update its identifier if (container.buttons) { container.buttons.forEach((button) => { const [output_id, output_class, input_id] = button.identifier.split("-"); const buttonOutputClassNumber = parseInt( output_class.split("_")[1], 10 ); // Check if the linkButton should be updated if (buttonOutputClassNumber > removedOutputClassNumber) { // Generate the new identifier with incremented output_class const newIdentifier = `${output_id}-output_${ buttonOutputClassNumber - 1 }-${input_id}-input_1`; // Update the identifier button.setAttribute("identifier", newIdentifier); } }); } } /* */ public highlightButtonInContainer(containerId, identifier) { const container = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == containerId ); if ( container instanceof WebWriterGamebookPage || container instanceof WebWriterGamebookPopup ) { const connButton = container.buttons.find( (button) => button.identifier === identifier ); if (connButton) { if (!connButton.classList.contains("ww-selected")) { connButton.classList.add("highlighted"); } } } } /* */ public unhighlightButtonInContainer(containerId, identifier) { const container = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == containerId ); if ( container instanceof WebWriterGamebookPage || container instanceof WebWriterGamebookPopup ) { const connButton = container.buttons.find( (button) => button.identifier === identifier ); if (connButton) { connButton.classList.remove("highlighted"); } } } /* */ public selectButtonInContainer(containerId, identifier) { const container = this.gamebookContainers.find( (container) => container.getAttribute("drawflowNodeId") == containerId ); const connButton = container.buttons.find( (button) => button.identifier === identifier ); if (connButton) { connButton.focus(); } } /* */ public createContainerFromNode(node) { switch (node.class) { case "page": case "origin": return this._createContainerElement( node, "webwriter-gamebook-page", node.class == "origin" ? "1" : "0" ); break; case "popup": return this._createContainerElement(node, "webwriter-gamebook-popup"); break; case "branch": return this._createContainerElement(node, "webwriter-gamebook-branch"); break; } } /* */ private _createContainerElement( node: DrawflowNode, tagName: string, originPage?: string ) { const container = document.createElement(tagName) as HTMLElement; container.setAttribute("drawflowNodeId", node.id.toString()); container.setAttribute("pageTitle", node.data.title); if (originPage) { container.setAttribute("originPage", originPage); } (container as any).hide(); return container; } /* */ public importContainers(template: Array) { let containers = template.map((info) => this.createContainerFromImport(info) ); return containers; } /* */ private createContainerFromImport(info) { let element = document.createElement(info.tagName); info.attributes.forEach((attr) => { element.setAttribute(attr.name, attr.value); }); element.innerHTML = info.innerHTML; return element; } /* */ public searchContainers(value: string): Number[] { let matchContainerIds: Number[] = []; const searchValue = value.toLowerCase(); this.gamebookContainers.forEach((container) => { const slotElements = container.querySelectorAll("*"); // Get all child elements in the container slotElements.forEach((element) => { // Check if the tagName or textContent includes the search value if ( element.tagName.toLowerCase().includes(searchValue) || (element.textContent && element.textContent.toLowerCase().includes(searchValue)) ) { matchContainerIds = [...matchContainerIds, container.drawflowNodeId]; } }); }); return matchContainerIds; } /* */ public updateBranchContainerRuleTarget(output_id, output_class, input_id) { const branchContainer = this._getContainerByDrawflowNodeId(output_id); branchContainer._updateRuleTarget(output_class, input_id); } /* */ public removeBranchContainerRuleElements(output_id, element_id, isQuiz) { const branchContainer = this._getContainerByDrawflowNodeId(output_id); const removeConnectionsFromOutputs = ( branchContainer as WebWriterGamebookBranch ).removeElementOfRules(element_id, isQuiz); return removeConnectionsFromOutputs; } /* */ public copyAndPasteContainerContents(pastedNode) { const pastedContainer = this.createContainerFromNode(pastedNode); const copiedContainer = this._getContainerByDrawflowNodeId( this.editorStore.copiedNode.id ); // Iterate through each element in copiedContainer's slotContent if (copiedContainer.slotContent) { copiedContainer.slotContent.forEach((element) => { // Skip elements with specific tag names if ( element.tagName.toLowerCase() === "webwriter-gamebook-button" || element.tagName.toLowerCase() === "webwriter-gamebook-branch-button" ) { return; // Skip this element } // Create a new element of the same type const newElement = document.createElement(element.tagName); // Manually copy desired attributes, skipping 'id' [...element.attributes].forEach((attr) => { if (attr.name !== "id" && attr.name !== "contenteditable") { // Skip the 'id' attribute newElement.setAttribute(attr.name, attr.value); } }); // Copy inner content (text or children) if necessary newElement.innerHTML = element.innerHTML; // or use another approach based on your needs // Append the new element to pastedContainer's slot pastedContainer.appendChild(newElement); }); } return pastedContainer; } /* */ public changeOrigin(newId) { const originPageContainer = this.gamebookContainers.find( (container) => container.getAttribute("originPage") === "1" ); if (originPageContainer) { (originPageContainer as WebWriterGamebookPage).originPage = 0; } const newOriginPageContainer = this._getContainerByDrawflowNodeId(newId); (newOriginPageContainer as WebWriterGamebookPage).originPage = 1; } }