import { html } from "lit" import Configuration from "../Configuration.js" import Shortcuts from "../Shortcuts.js" import Utility from "../Utility.js" import Copy from "../input/common/Copy.js" import Cut from "../input/common/Cut.js" import Paste from "../input/common/Paste.js" import KeyboardEnableZoom from "../input/keyboard/KeyboardEnableZoom.js" import KeyboardShortcut from "../input/keyboard/KeyboardShortcut.js" import MouseScrollGraph from "../input/mouse/MouseScrollGraph.js" import MouseTracking from "../input/mouse/MouseTracking.js" import Select from "../input/mouse/Select.js" import Unfocus from "../input/mouse/Unfocus.js" import Zoom from "../input/mouse/Zoom.js" import ITemplate from "./ITemplate.js" /** @extends ITemplate */ export default class BlueprintTemplate extends ITemplate { static styleVariables = { "--ueb-font-size": `${Configuration.fontSize}`, "--ueb-grid-axis-line-color": `${Configuration.gridAxisLineColor}`, "--ueb-grid-expand": `${Configuration.expandGridSize}px`, "--ueb-grid-line-color": `${Configuration.gridLineColor}`, "--ueb-grid-line-width": `${Configuration.gridLineWidth}px`, "--ueb-grid-set-line-color": `${Configuration.gridSetLineColor}`, "--ueb-grid-set": `${Configuration.gridSet}`, "--ueb-grid-size": `${Configuration.gridSize}px`, "--ueb-link-min-width": `${Configuration.linkMinWidth}`, "--ueb-node-radius": `${Configuration.nodeRadius}px`, } #resizeObserver = new ResizeObserver(entries => { const size = entries.find(entry => entry.target === this.viewportElement)?.devicePixelContentBoxSize?.[0] if (size) { this.viewportSize[0] = size.inlineSize this.viewportSize[1] = size.blockSize } }) /** @type {Copy} */ #copyInputObject /** @type {Paste} */ #pasteInputObject /** @type {Zoom} */ #zoomInputObject /** @type {HTMLElement} */ headerElement /** @type {HTMLElement} */ overlayElement /** @type {HTMLElement} */ viewportElement /** @type {SelectorElement} */ selectorElement /** @type {HTMLElement} */ gridElement /** @type {HTMLElement} */ linksContainerElement /** @type {HTMLElement} */ nodesContainerElement viewportSize = [0, 0] #setViewportSize() { } /** @param {Blueprint} element */ initialize(element) { super.initialize(element) this.element.style.cssText = Object.entries(BlueprintTemplate.styleVariables) .map(([k, v]) => `${k}:${v};`).join("") const htmlTemplate = /** @type {HTMLTemplateElement} */( this.element.querySelector(":scope > template") )?.content.textContent if (htmlTemplate) { this.element.requestUpdate() this.element.updateComplete.then(() => { this.blueprint.mousePosition = [ Math.round(this.viewportSize[0] / 2), Math.round(this.viewportSize[1] / 2), ] this.getPasteInputObject().pasted(htmlTemplate) this.blueprint.unselectAll() }) } } setup() { super.setup() this.#resizeObserver.observe(this.viewportElement, { box: "device-pixel-content-box", }) const bounding = this.viewportElement.getBoundingClientRect() this.viewportSize[0] = bounding.width this.viewportSize[1] = bounding.height if (this.blueprint.nodes.length > 0) { this.blueprint.requestUpdate() this.blueprint.updateComplete.then(() => this.centerContentInViewport()) } } cleanup() { super.cleanup() this.#resizeObserver.unobserve(this.viewportElement) } createInputObjects() { const gridElement = this.element.getGridDOMElement() this.#copyInputObject = new Copy(gridElement, this.blueprint) this.#pasteInputObject = new Paste(gridElement, this.blueprint) this.#zoomInputObject = new Zoom(gridElement, this.blueprint) return [ ...super.createInputObjects(), this.#copyInputObject, this.#pasteInputObject, this.#zoomInputObject, new Cut(gridElement, this.blueprint), new KeyboardShortcut(gridElement, this.blueprint, { activationKeys: Shortcuts.duplicateNodes }, () => this.blueprint.template.getPasteInputObject().pasted( this.blueprint.template.getCopyInputObject().copied() ) ), new KeyboardShortcut(gridElement, this.blueprint, { activationKeys: Shortcuts.deleteNodes }, () => this.blueprint.removeGraphElement(...this.blueprint.getNodes(true))), new KeyboardShortcut(gridElement, this.blueprint, { activationKeys: Shortcuts.selectAllNodes }, () => this.blueprint.selectAll()), new Select(gridElement, this.blueprint, { clickButton: Configuration.mouseClickButton, exitAnyButton: true, moveEverywhere: true, }), new MouseScrollGraph(gridElement, this.blueprint, { clickButton: Configuration.mouseRightClickButton, exitAnyButton: false, moveEverywhere: true, }), new Unfocus(gridElement, this.blueprint), new MouseTracking(gridElement, this.blueprint), new KeyboardEnableZoom(gridElement, this.blueprint), ] } render() { return html`
Zoom ${this.blueprint.zoom == 0 ? "1:1" : (this.blueprint.zoom > 0 ? "+" : "") + this.blueprint.zoom}
` } /** @param {PropertyValues} changedProperties */ firstUpdated(changedProperties) { super.firstUpdated(changedProperties) this.headerElement = this.blueprint.querySelector('.ueb-viewport-header') this.overlayElement = this.blueprint.querySelector('.ueb-viewport-overlay') this.viewportElement = this.blueprint.querySelector('.ueb-viewport-body') this.selectorElement = this.blueprint.querySelector('ueb-selector') this.gridElement = this.viewportElement.querySelector(".ueb-grid") this.linksContainerElement = this.blueprint.querySelector("[data-links]") this.linksContainerElement.append(...this.blueprint.getLinks()) this.nodesContainerElement = this.blueprint.querySelector("[data-nodes]") this.nodesContainerElement.append(...this.blueprint.getNodes()) this.viewportElement.scroll(Configuration.expandGridSize, Configuration.expandGridSize) } /** @param {PropertyValues} changedProperties */ willUpdate(changedProperties) { super.willUpdate(changedProperties) if (this.headerElement && changedProperties.has("zoom")) { this.headerElement.classList.add("ueb-zoom-changed") this.headerElement.addEventListener( "animationend", () => this.headerElement.classList.remove("ueb-zoom-changed") ) } } /** @param {PropertyValues} changedProperties */ updated(changedProperties) { super.updated(changedProperties) if (changedProperties.has("scrollX") || changedProperties.has("scrollY")) { this.viewportElement.scroll(this.blueprint.scrollX, this.blueprint.scrollY) } if (changedProperties.has("zoom")) { this.blueprint.style.setProperty("--ueb-scale", this.blueprint.getScale()) const previousZoom = changedProperties.get("zoom") const minZoom = Math.min(previousZoom, this.blueprint.zoom) const maxZoom = Math.max(previousZoom, this.blueprint.zoom) const classes = Utility.range(minZoom, maxZoom) const getClassName = v => `ueb-zoom-${v}` if (previousZoom < this.blueprint.zoom) { this.blueprint.classList.remove(...classes.filter(v => v < 0).map(getClassName)) this.blueprint.classList.add(...classes.filter(v => v > 0).map(getClassName)) } else { this.blueprint.classList.remove(...classes.filter(v => v > 0).map(getClassName)) this.blueprint.classList.add(...classes.filter(v => v < 0).map(getClassName)) } } } getCommentNodes(justSelected = false) { return this.blueprint.querySelectorAll( `ueb-node[data-type="${Configuration.paths.comment}"]${justSelected ? '[data-selected="true"]' : ''}` + `, ueb-node[data-type="${Configuration.paths.materialGraphNodeComment}"]${justSelected ? '[data-selected="true"]' : ''}` ) } /** @param {PinReferenceEntity} pinReference */ getPin(pinReference) { return /** @type {PinElement} */(this.blueprint.querySelector( `ueb-node[data-title="${pinReference.objectName}"] ueb-pin[data-id="${pinReference.pinGuid}"]` )) } getCopyInputObject() { return this.#copyInputObject } getPasteInputObject() { return this.#pasteInputObject } getZoomInputObject() { return this.#zoomInputObject } /** * @param {Number} x * @param {Number} y */ isPointVisible(x, y) { return false } gridTopVisibilityBoundary() { return this.blueprint.scaleCorrect(this.blueprint.scrollY) - this.blueprint.translateY } gridRightVisibilityBoundary() { return this.gridLeftVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[0]) } gridBottomVisibilityBoundary() { return this.gridTopVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[1]) } gridLeftVisibilityBoundary() { return this.blueprint.scaleCorrect(this.blueprint.scrollX) - this.blueprint.translateX } centerViewport(x = 0, y = 0, smooth = true) { const centerX = this.gridLeftVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[0] / 2) const centerY = this.gridTopVisibilityBoundary() + this.blueprint.scaleCorrect(this.viewportSize[1] / 2) this.blueprint.scrollDelta( this.blueprint.scaleCorrectReverse(x - centerX), this.blueprint.scaleCorrectReverse(y - centerY), smooth ) } centerContentInViewport(smooth = true) { let avgX = 0 let avgY = 0 let minX = Number.MAX_SAFE_INTEGER let maxX = Number.MIN_SAFE_INTEGER let minY = Number.MAX_SAFE_INTEGER let maxY = Number.MIN_SAFE_INTEGER const nodes = this.blueprint.getNodes() for (const node of nodes) { avgX += node.leftBoundary() + node.rightBoundary() avgY += node.topBoundary() + node.bottomBoundary() minX = Math.min(minX, node.leftBoundary()) maxX = Math.max(maxX, node.rightBoundary()) minY = Math.min(minY, node.topBoundary()) maxY = Math.max(maxY, node.bottomBoundary()) } avgX = Math.round(maxX - minX <= this.viewportSize[0] ? (maxX + minX) / 2 : avgX / (2 * nodes.length) ) avgY = Math.round(maxY - minY <= this.viewportSize[1] ? (maxY + minY) / 2 : avgY / (2 * nodes.length) ) this.centerViewport(avgX, avgY, smooth) } }