UNPKG

kmap-term-tree

Version:

Renders a tree from a mathematical term

273 lines 8.91 kB
import { __decorate } from "tslib"; import { css, html, LitElement, svg } from 'lit'; import { property, state, query } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { Parser } from "./parser.js"; import { Token } from "./tokenizer.js"; export class KmapTermTree extends LitElement { constructor() { super(...arguments); this.parser = new Parser(); this.tension = 0; } // @ts-ignore updateSlotted({ target }) { // @ts-ignore this.term = target.assignedNodes().map((n) => n.textContent).join(''); } update(_changedProperties) { if (_changedProperties.has("term")) { if (this.term !== undefined) { this.tokens = Token.tokenize(this.term); this.termNode = this.parser.parse(this.tokens); let map = {}; this.maxDepths = this.calcDepths(this.termNode, map); let connections = []; this.termNode.breadthFirst((n) => { if (n.leftChildNode !== undefined) connections.push({ from: n.token.id, to: n.leftChildNode.token.id }); if (n.rightChildNode !== undefined) connections.push({ from: n.token.id, to: n.rightChildNode.token.id }); }); this.depths = map; this.connections = connections; } } super.update(_changedProperties); } calcDepths(node, map) { let depth = 0; if (node.leftChildNode) depth = Math.max(depth, this.calcDepths(node.leftChildNode, map) + 1); if (node.rightChildNode) depth = Math.max(depth, this.calcDepths(node.rightChildNode, map) + 1); map[node.token.id] = depth; return depth; } render() { return html ` ${this.connections ? html ` <kmap-term-tree-edges .tension="${this.tension}"></kmap-term-tree-edges> ` : ''} ${!this.tokens ? '' : html ` <div class="tokens" id="tokens"> ${this.tokens.map((t) => html ` <div class="token" id="${t.id}">${KmapTermTree._prettify(t.value)}</div> `)} </div> <div class="notokens"> ${this.tokens.map((t) => html ` <div>${KmapTermTree._prettify(t.value)}</div> `)} </div> `} ${this.tokens && this.depths ? html ` <div class="nodes"> ${this.tokens.map((t) => html ` <div class="node" id="n${t.id}" depth="${ifDefined(this.depths ? this.depths[t.id] : undefined)}">${KmapTermTree._prettify(t.value)}</div> `)} </div> ` : ''} <div hidden> <slot @slotchange=${this.updateSlotted}></slot> </div> `; } async updated(_changedProperties) { if (_changedProperties.has("connections")) { await this.updateComplete; if (this.tokensElement && this.maxDepths) { let tokensHeight = this.tokensElement.offsetHeight; //console.log(this.maxDepths) //console.log(tokensHeight + "px") this.style.setProperty("--kmap-term-tree-max-depth", "" + this.maxDepths); this.style.setProperty("--kmap-term-tree-vertical-distance", tokensHeight + "px"); this.style.height = "calc(3px + var(--kmap-term-tree-vertical-distance) * (var(--kmap-term-tree-max-depth)*1.5 + 1))"; //console.log(this.style.height); } let edges = []; if (this.edgesElement && this.connections) { for (const connection of this.connections) { let edge = this._connect(connection); if (edge) edges.push(edge); } this.edgesElement.edges = edges; } } } _connect(connection) { let from = this.shadowRoot.getElementById("n" + connection.from); let to = this.shadowRoot.getElementById("n" + connection.to); if (!from || !to) return; let frompos = this._findAbsolutePosition(from); let topos = this._findAbsolutePosition(to); return { from: frompos, to: topos }; } _findAbsolutePosition(element) { var x = 0; var y = 0; var el = element; for (; el !== this; el = el.offsetParent) { x += el.offsetLeft; y += el.offsetTop; } return { x: x, y: y, width: element.offsetWidth, height: element.offsetHeight, }; } static _prettify(value) { switch (value) { case '*': return "·"; case '-': return "−"; // math minus default: return value; } } } // language=CSS KmapTermTree.styles = css ` :host { display: block; color: var(--kmap-term-tree-text-color, #000); position: relative; } .tokens, .nodes, kmap-term-tree-edges { position: absolute; } .tokens, .nodes { white-space: nowrap; } .notokens { white-space: nowrap; height: 0px; } .notokens div { display: inline-block; } kmap-term-tree-edges { width: 100%; height: 100%; } .token { display: inline-block; } .token, .node { display: inline-block; position: relative; background-color: var(--kmap-term-tree-background-color, white); } .node { display: inline-block; position: relative; border: 1px solid var(--kmap-term-tree-border-color, #005b9f); padding: 0px 4px; border-radius: 1em; margin: -1px -5px; } .node[depth="0"], .node:not([depth]) { visibility: hidden; } .node[depth="1"] { top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 1.5); } .node[depth="2"] { top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 3); } .node[depth="3"] { top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 4.5); } .node[depth="4"] { top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 6); } .node[depth="5"] { top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 7.5); } .node[depth="6"] { top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 9); } `; __decorate([ property({ type: String }) ], KmapTermTree.prototype, "term", void 0); __decorate([ property({ type: Number }) ], KmapTermTree.prototype, "tension", void 0); __decorate([ state() ], KmapTermTree.prototype, "tokens", void 0); __decorate([ state() ], KmapTermTree.prototype, "nodes", void 0); __decorate([ state() ], KmapTermTree.prototype, "termNode", void 0); __decorate([ state() ], KmapTermTree.prototype, "depths", void 0); __decorate([ state() ], KmapTermTree.prototype, "maxDepths", void 0); __decorate([ state() ], KmapTermTree.prototype, "connections", void 0); __decorate([ query("kmap-term-tree-edges") ], KmapTermTree.prototype, "edgesElement", void 0); __decorate([ query("#tokens") ], KmapTermTree.prototype, "tokensElement", void 0); export class KmapTermTreeEdge extends LitElement { constructor() { super(...arguments); this.tension = .5; this.edges = []; } render() { const parent = this.offsetParent; //language=SVG return this.edges === undefined ? '' : svg ` <svg style="position:absolute;left:0px;top:0px" width="${parent.clientWidth}" height="${parent.clientHeight}"> ${this.edges.map(edge => svg ` <path d="${this._path(edge)}" fill="none" stroke-width="2" stroke-opacity=".9"/> `)} </svg> `; } _path(edge) { let fromx = edge.from.x + edge.from.width / 2; let fromy = edge.from.y + edge.from.height / 2; let tox = edge.to.x + edge.to.width / 2; let toy = edge.to.y + edge.to.height / 2; var delta = (toy - fromy) * this.tension; var hx1 = fromx; var hy1 = fromy + delta; var hx2 = tox; var hy2 = toy - delta; return "M " + fromx + " " + fromy + " C " + hx1 + " " + hy1 + " " + hx2 + " " + hy2 + " " + tox + " " + toy; } } KmapTermTreeEdge.styles = css ` :host { display: block; stroke: var(--kmap-term-tree-edge-color, #fbc02d); position: relative; } `; __decorate([ property({ type: Number }) ], KmapTermTreeEdge.prototype, "tension", void 0); __decorate([ property({ type: Array }) ], KmapTermTreeEdge.prototype, "edges", void 0); window.customElements.define('kmap-term-tree-edges', KmapTermTreeEdge); //# sourceMappingURL=KmapTermTree.js.map