// @ts-check import crel from "../common/crel.js"; import formatBytes from "../common/formatBytes.js"; /** @typedef {import("./nodePath.js").NodePath} NodePath */ /** @typedef {import("../../types/png.d.ts").PngNode} PngNode */ /** * @typedef {Function} NodeUiFn * @param {PngNode} node * @returns {NodeUi} */ class Explorer { #bytesEl = crel("div", { class: "bytes" }); #treeEl = crel("div", { class: "tree" }); /** * @param {PngNode} rootNode * @param {NodeUiFn} getNodeUi */ constructor(rootNode, getNodeUi) { /** * @param {PngNode} node * @param {NodePath} path * @returns [HTMLElement, HTMLElement] Each node's bytes and tree elements. */ const traverse = (node, path) => { const nodeBytesEl = crel("span", { "data-path": path }); const isRoot = path.length === 0; const { title, description } = getNodeUi(node); const nodeTreeEl = crel( "details", { "data-path": path, ...(isRoot ? { open: "open" } : {}) }, crel( "summary", {}, crel("span", { "class": "title" }, title), crel( "span", { "class": "bytecount" }, "TODO: X bytes", ), ), description, ); if (node.children) { const treeChildrenEl = crel("div", { class: "children" }); node.children.forEach((child, index) => { const [childBytesEl, childTreeEl] = traverse( child, path.concat(index), ); if (index > 0) nodeBytesEl.append(" "); nodeBytesEl.append(childBytesEl); treeChildrenEl.append(childTreeEl); }); nodeTreeEl.append(treeChildrenEl); } else { nodeBytesEl.innerHTML = formatBytes(node.bytes, 256); } return [nodeBytesEl, nodeTreeEl]; }; // TODO: better variable names const [a, b] = traverse(rootNode, []); this.#bytesEl.append(a); this.#treeEl.append(b); this.el = document.createDocumentFragment(); this.el.append(this.#bytesEl, this.#treeEl); } } /** * @param {PngNode} rootNode * @param {NodeUiFn} getNodeUi * @returns {DocumentFragment} */ export default (rootNode, getNodeUi) => new Explorer(rootNode, getNodeUi).el;