import { GetTree, IsNodeSelected } from "./jqtreeMethodTypes"; import { IconElement, OnCreateLi } from "./jqtreeOptions"; import { Node } from "./node"; import { getBoolString } from "./util"; interface ElementsRendererParams { $element: JQuery; autoEscape: boolean; buttonLeft: boolean; closedIcon?: IconElement; dragAndDrop: boolean; getTree: GetTree; isNodeSelected: IsNodeSelected; onCreateLi?: OnCreateLi; openedIcon?: IconElement; rtl?: boolean; showEmptyFolder: boolean; tabIndex?: number; } export default class ElementsRenderer { public closedIconElement?: HTMLElement | Text; public openedIconElement?: HTMLElement | Text; private $element: JQuery; private autoEscape: boolean; private buttonLeft: boolean; private dragAndDrop: boolean; private getTree: GetTree; private isNodeSelected: IsNodeSelected; private onCreateLi?: OnCreateLi; private rtl?: boolean; private showEmptyFolder: boolean; private tabIndex?: number; constructor({ $element, autoEscape, buttonLeft, closedIcon, dragAndDrop, getTree, isNodeSelected, onCreateLi, openedIcon, rtl, showEmptyFolder, tabIndex, }: ElementsRendererParams) { this.autoEscape = autoEscape; this.buttonLeft = buttonLeft; this.dragAndDrop = dragAndDrop; this.$element = $element; this.getTree = getTree; this.isNodeSelected = isNodeSelected; this.onCreateLi = onCreateLi; this.rtl = rtl; this.showEmptyFolder = showEmptyFolder; this.tabIndex = tabIndex; this.openedIconElement = this.createButtonElement(openedIcon ?? "+"); this.closedIconElement = this.createButtonElement(closedIcon ?? "-"); } public render(fromNode: Node | null): void { if (fromNode?.parent) { this.renderFromNode(fromNode); } else { this.renderFromRoot(); } } public renderFromNode(node: Node): void { if (!node.element) { return; } // remember current li const $previousLi = jQuery(node.element); // create element const li = this.createLi(node, node.getLevel()); // add element to dom $previousLi.after(li); // remove previous li $previousLi.remove(); // create children this.createDomElements(li, node.children, false, node.getLevel() + 1); } public renderFromRoot(): void { this.$element.empty(); const tree = this.getTree(); if (this.$element[0] && tree) { this.createDomElements(this.$element[0], tree.children, true, 1); } } private attachNodeData(node: Node, li: HTMLElement): void { node.element = li; jQuery(li).data("node", node); } private createButtonElement( value: IconElement, ): HTMLElement | Text | undefined { if (typeof value === "string") { // convert value to html const div = document.createElement("div"); div.innerHTML = value; return document.createTextNode(div.innerHTML); } else if ((value as HTMLElement).nodeType) { return value as HTMLElement; } else { return jQuery(value)[0]; } } private createDomElements( element: Element, children: Node[], isRootNode: boolean, level: number, ): void { const ul = this.createUl(isRootNode); element.appendChild(ul); for (const child of children) { const li = this.createLi(child, level); ul.appendChild(li); if (child.hasChildren()) { this.createDomElements(li, child.children, false, level + 1); } } } private createFolderLi( node: Node, level: number, isSelected: boolean, ): HTMLLIElement { const buttonClasses = this.getButtonClasses(node); const folderClasses = this.getFolderClasses(node, isSelected); const iconElement = node.is_open ? this.openedIconElement : this.closedIconElement; // li const li = document.createElement("li"); li.className = `jqtree_common ${folderClasses}`; li.setAttribute("role", "none"); // div const div = document.createElement("div"); div.className = "jqtree-element jqtree_common"; div.setAttribute("role", "none"); li.appendChild(div); // button link const buttonLink = document.createElement("a"); buttonLink.className = buttonClasses; if (iconElement) { buttonLink.appendChild(iconElement.cloneNode(true)); } if (this.buttonLeft) { div.appendChild(buttonLink); } // title span const titleSpan = this.createTitleSpan( node.name, isSelected, true, level, ); titleSpan.setAttribute("aria-expanded", getBoolString(node.is_open)); div.appendChild(titleSpan); if (!this.buttonLeft) { div.appendChild(buttonLink); } return li; } /* Create the