Mergin better performance branch

This commit is contained in:
barsdeveloper
2022-09-04 14:33:22 +02:00
parent 47c15fbf8d
commit 715dee6a5a
97 changed files with 2725 additions and 2833 deletions

View File

@@ -1,8 +1,6 @@
// @ts-check
import { html } from "lit"
import Configuration from "../Configuration"
import Copy from "../input/common/Copy"
import html from "./html"
import ITemplate from "./ITemplate"
import KeyboardCanc from "../input/keybaord/KeyboardCanc"
import KeyboardEnableZoom from "../input/keybaord/KeyboardEnableZoom"
@@ -10,7 +8,6 @@ import KeyboardSelectAll from "../input/keybaord/KeyboardSelectAll"
import MouseScrollGraph from "../input/mouse/MouseScrollGraph"
import MouseTracking from "../input/mouse/MouseTracking"
import Paste from "../input/common/Paste"
import sanitizeText from "./sanitizeText"
import Select from "../input/mouse/Select"
import SelectorElement from "../element/SelectorElement"
import Unfocus from "../input/mouse/Unfocus"
@@ -24,6 +21,33 @@ import Zoom from "../input/mouse/Zoom"
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`,
"--ueb-pin-bool-color": `${Configuration.pinColor.bool}`,
"--ueb-pin-default-color": `${Configuration.pinColor.default}`,
"--ueb-pin-exec-color": `${Configuration.pinColor.exec}`,
"--ueb-pin-name-color": `${Configuration.pinColor.name}`,
"--ueb-pin-real-color": `${Configuration.pinColor.real}`,
"--ueb-pin-string-color": `${Configuration.pinColor.string}`,
"--ueb-pin-struct-color": `${Configuration.pinColor.struct}`,
}
/**
* @param {Blueprint} blueprint
*/
constructed(blueprint) {
blueprint.style.cssText = Object.entries(BlueprintTemplate.styleVariables).map(([k, v]) => `${k}:${v};`).join("")
}
/**
* @param {Blueprint} blueprint
*/
@@ -55,36 +79,22 @@ export default class BlueprintTemplate extends ITemplate {
}
/**
* @param {Blueprint} element
* Computes the html content of the target element.
* @param {Blueprint} element Target element
* @returns The computed html
*/
header(element) {
render(element) {
return html`
<div class="ueb-viewport-header">
<div class="ueb-viewport-zoom">1:1</div>
</div>
`
}
/**
* @param {Blueprint} element
*/
overlay(element) {
return html`
<div class="ueb-viewport-overlay"></div>
`
}
/**
* @param {Blueprint} element
*/
viewport(element) {
return html`
<div class="ueb-viewport-body">
<div class="ueb-grid" style="
--ueb-additional-x:${sanitizeText(element.additional[0])};
--ueb-additional-y:${sanitizeText(element.additional[1])};
--ueb-translate-x:${sanitizeText(element.translateValue[0])};
--ueb-translate-y:${sanitizeText(element.translateValue[1])};
--ueb-additional-x: ${element};
--ueb-additional-y: ${element.translateY};
--ueb-translate-x: ${element.translateX};
--ueb-translate-y: ${element.translateY};
">
<div class="ueb-grid-content">
<div data-links></div>
@@ -96,100 +106,43 @@ export default class BlueprintTemplate extends ITemplate {
}
/**
* Computes the html content of the target element.
* @param {Blueprint} element Target element
* @returns The computed html
* @param {Blueprint} blueprint
* @param {Map} changedProperties
*/
render(element) {
return html`
${this.header(element)}
${this.overlay(element)}
${this.viewport(element)}
`
}
/**
* Applies the style to the element.
* @param {Blueprint} blueprint The blueprint element
*/
setup(blueprint) {
super.setup(blueprint)
blueprint.classList.add("ueb", `ueb-zoom-${blueprint.zoom}`)
Object.entries({
"--ueb-font-size": sanitizeText(Configuration.fontSize),
"--ueb-grid-size": `${sanitizeText(Configuration.gridSize)}px`,
"--ueb-grid-line-width": `${sanitizeText(Configuration.gridLineWidth)}px`,
"--ueb-grid-line-color": sanitizeText(Configuration.gridLineColor),
"--ueb-grid-set": sanitizeText(Configuration.gridSet),
"--ueb-grid-set-line-color": sanitizeText(Configuration.gridSetLineColor),
"--ueb-grid-axis-line-color": sanitizeText(Configuration.gridAxisLineColor),
"--ueb-node-radius": `${sanitizeText(Configuration.nodeRadius)}px`,
"--ueb-link-min-width": sanitizeText(Configuration.linkMinWidth)
}).forEach(entry => blueprint.style.setProperty(entry[0], entry[1]))
blueprint.headerElement = blueprint.querySelector('.ueb-viewport-header')
blueprint.overlayElement = blueprint.querySelector('.ueb-viewport-overlay')
blueprint.viewportElement = blueprint.querySelector('.ueb-viewport-body')
firstUpdated(blueprint, changedProperties) {
super.firstUpdated(blueprint, changedProperties)
blueprint.headerElement = /** @type {HTMLElement} */(blueprint.querySelector('.ueb-viewport-header'))
blueprint.overlayElement = /** @type {HTMLElement} */(blueprint.querySelector('.ueb-viewport-overlay'))
blueprint.viewportElement = /** @type {HTMLElement} */(blueprint.querySelector('.ueb-viewport-body'))
blueprint.selectorElement = new SelectorElement()
blueprint.gridElement = blueprint.viewportElement.querySelector(".ueb-grid")
blueprint.querySelector(".ueb-grid-content").append(blueprint.selectorElement)
blueprint.linksContainerElement = blueprint.querySelector("[data-links]")
blueprint.querySelector(".ueb-grid-content")?.append(blueprint.selectorElement)
blueprint.gridElement = /** @type {HTMLElement} */(blueprint.viewportElement.querySelector(".ueb-grid"))
blueprint.linksContainerElement = /** @type {HTMLElement} */(blueprint.querySelector("[data-links]"))
blueprint.linksContainerElement.append(...blueprint.getLinks())
blueprint.nodesContainerElement = blueprint.querySelector("[data-nodes]")
blueprint.nodesContainerElement = /** @type {HTMLElement} */(blueprint.querySelector("[data-nodes]"))
blueprint.nodesContainerElement.append(...blueprint.getNodes())
this.applyEndDragScrolling(blueprint)
blueprint.viewportElement.scroll(Configuration.expandGridSize, Configuration.expandGridSize)
}
/**
* Applies the style to the element.
* @param {Blueprint} blueprint The blueprint element
*/
applyZoom(blueprint, newZoom) {
blueprint.classList.remove("ueb-zoom-" + sanitizeText(blueprint.zoom))
blueprint.classList.add("ueb-zoom-" + sanitizeText(newZoom))
}
/**
* Applies the style to the element.
* @param {Blueprint} blueprint The blueprint element
* @param {Blueprint} blueprint
* @param {Map} changedProperties
*/
applyExpand(blueprint) {
blueprint.gridElement.style.setProperty("--ueb-additional-x", sanitizeText(blueprint.additional[0]))
blueprint.gridElement.style.setProperty("--ueb-additional-y", sanitizeText(blueprint.additional[1]))
}
/**
* Applies the style to the element.
* @param {Blueprint} blueprint The blueprint element
*/
applyTranlate(blueprint) {
blueprint.gridElement.style.setProperty("--ueb-translate-x", sanitizeText(blueprint.translateValue[0]))
blueprint.gridElement.style.setProperty("--ueb-translate-y", sanitizeText(blueprint.translateValue[1]))
}
/**
* Applies the style to the element.
* @param {Blueprint} blueprint The blueprint element
*/
applyStartDragScrolling(blueprint) {
blueprint.dataset.dragScrolling = "true"
}
/**
* Applies the style to the element.
* @param {Blueprint} blueprint The blueprint element
*/
applyEndDragScrolling(blueprint) {
blueprint.dataset.dragScrolling = "false"
updated(blueprint, changedProperties) {
super.updated(blueprint, changedProperties)
if (changedProperties.has("scrollX") || changedProperties.has("scrollY")) {
blueprint.viewportElement.scroll(blueprint.scrollX, blueprint.scrollY)
}
}
/**
* @param {Blueprint} blueprint
* @param {PinReferenceEntity} pinReference
* @returns {PinElement}
*/
getPin(blueprint, pinReference) {
return blueprint.querySelector(
return /** @type {PinElement} */(blueprint.querySelector(
`ueb-node[data-name="${pinReference.objectName}"] ueb-pin[data-id="${pinReference.pinGuid}"]`
)
))
}
}

View File

@@ -1,6 +1,4 @@
// @ts-check
import html from "./html"
import { html } from "lit"
import IInputPinTemplate from "./IInputPinTemplate"
/**
@@ -14,9 +12,10 @@ export default class BoolPinTemplate extends IInputPinTemplate {
/**
* @param {PinElement} pin
* @param {Map} changedProperties
*/
setup(pin) {
super.setup(pin)
firstUpdated(pin, changedProperties) {
super.firstUpdated(pin, changedProperties)
this.#input = pin.querySelector(".ueb-pin-input")
let self = this
this.onChangeHandler = _ => pin.entity.DefaultValue = self.#input.checked ? "true" : "false"
@@ -38,22 +37,13 @@ export default class BoolPinTemplate extends IInputPinTemplate {
return [this.#input.checked ? "true" : "false"]
}
/**
* @param {PinElement} pin
* @param {String[]?} value
*/
setInputs(pin, value = []) {
pin.entity.DefaultValue = value.length ? value[0] : this.getInput(pin)
this.#input.checked = pin.entity.DefaultValue == "true"
}
/**
* @param {PinElement} pin
*/
renderInput(pin) {
if (pin.isInput()) {
return html`
<input type="checkbox" class="ueb-pin-input" ${pin.entity.DefaultValue == "true" ? "checked" : ""} />
<input type="checkbox" class="ueb-pin-input" .checked=${pin.defaultValue == "true"} />
`
}
return super.renderInput(pin)

View File

@@ -1,6 +1,4 @@
// @ts-check
import html from "./html"
import { html } from "lit"
import PinTemplate from "./PinTemplate"
/**
@@ -15,19 +13,9 @@ export default class ExecPinTemplate extends PinTemplate {
renderIcon(pin) {
return html`
<svg class="ueb-pin-icon-exec" width="16" height="16" viewBox="-2 0 16 16" fill="none">
<path class="ueb-pin-tofill" stroke-width="1.25" stroke="currentColor" d="
M 2 1
a 2 2 0 0 0 -2 2
v 10
a 2 2 0 0 0 2 2
h 4
a 2 2 0 0 0 1.519 -0.698
l 4.843 -5.651
a 1 1 0 0 0 0 -1.302
L 7.52 1.7
a 2 2 0 0 0 -1.519 -0.698
z
" />
<path class="ueb-pin-tofill" stroke-width="1.25" stroke="currentColor"
d="M 2 1 a 2 2 0 0 0 -2 2 v 10 a 2 2 0 0 0 2 2 h 4 a 2 2 0 0 0 1.519 -0.698 l 4.843 -5.651 a 1 1 0 0 0 0 -1.302 L 7.52 1.7 a 2 2 0 0 0 -1.519 -0.698 z"
/>
</svg>
`
}

View File

@@ -0,0 +1,33 @@
import ITemplate from "./ITemplate"
/**
* @typedef {import("../element/IFromToPositionedElement").default} IFromToPositionedElement
*/
/**
* @template {IFromToPositionedElement} T
* @extends {ITemplate<T>}
*/
export default class IFromToPositionedTemplate extends ITemplate {
/**
* @param {T} selector
* @param {Map} changedProperties
*/
update(selector, changedProperties) {
super.update(selector, changedProperties)
if (changedProperties.has("initialPositionX")) {
selector.style.setProperty("--ueb-from-x", `${selector.initialPositionX}`)
}
if (changedProperties.has("initialPositionY")) {
selector.style.setProperty("--ueb-from-y", `${selector.initialPositionY}`)
}
if (changedProperties.has("finaPositionX")) {
selector.style.setProperty("--ueb-to-x", `${selector.finaPositionX}`)
}
if (changedProperties.has("finaPositionY")) {
selector.style.setProperty("--ueb-to-y", `${selector.finaPositionY}`)
}
}
}

View File

@@ -1,6 +1,4 @@
// @ts-check
import html from "./html"
import { html } from "lit"
import MouseIgnore from "../input/mouse/MouseIgnore"
import PinTemplate from "./PinTemplate"
import Utility from "../Utility"
@@ -19,21 +17,18 @@ export default class IInputPinTemplate extends PinTemplate {
/**
* @param {PinElement} pin
* @param {Map} changedProperties
*/
setup(pin) {
super.setup(pin)
this.#inputContentElements = /** @type {HTMLElement[]} */(
[...pin.querySelectorAll(".ueb-pin-input-content")]
)
firstUpdated(pin, changedProperties) {
super.firstUpdated(pin, changedProperties)
this.#inputContentElements = [...pin.querySelectorAll(".ueb-pin-input-content")]
if (this.#inputContentElements.length) {
this.setInputs(pin, [
Utility.decodeInputString(/** @type {String} */(pin.entity.DefaultValue))
])
this.setInputs(pin, this.getInputs(pin))
let self = this
this.onFocusHandler = _ => pin.blueprint.dispatchEditTextEvent(true)
this.onFocusOutHandler = e => {
e.preventDefault()
document.getSelection().removeAllRanges() // Deselect text inside the input
document.getSelection()?.removeAllRanges() // Deselect text inside the input
self.setInputs(pin, this.getInputs(pin))
pin.blueprint.dispatchEditTextEvent(false)
}
@@ -76,6 +71,7 @@ export default class IInputPinTemplate extends PinTemplate {
* @param {PinElement} pin
*/
getInputs(pin) {
Utility.decodeInputString(pin.entity.DefaultValue)
return this.#inputContentElements.map(element =>
// Faster than innerText which causes reflow
element.innerHTML.replaceAll("<br>", "\n"))
@@ -88,7 +84,7 @@ export default class IInputPinTemplate extends PinTemplate {
setInputs(pin, values = [], updateDefaultValue = true) {
this.#inputContentElements.forEach((element, i) => element.innerText = values[i])
if (updateDefaultValue) {
pin.entity.DefaultValue = this.getInput(pin)
pin.setDefaultValue(values.reduce((acc, cur) => acc + cur, ""))
}
}
@@ -99,10 +95,10 @@ export default class IInputPinTemplate extends PinTemplate {
if (pin.isInput()) {
return html`
<div class="ueb-pin-input">
<span class="ueb-pin-input-content" role="textbox" contenteditable="true"></span>
<span class="ueb-pin-input-content" role="textbox" contenteditable="true" .innerText=${pin.unreactiveDefaultValue}></span>
</div>
`
}
return ""
return html``
}
}

View File

@@ -1,8 +1,8 @@
// @ts-check
import { css, html } from "lit"
/**
* @typedef {import("../element/IElement").default} IElement
* @typedef {import("../input/IInput").default} IInput")}
* @typedef {import("../input/IInput").default} IInput
*/
/**
@@ -10,26 +10,59 @@
*/
export default class ITemplate {
static styles = css``
/** @type {IInput[]} */
#inputObjects = []
get inputObjects() {
return this.#inputObjects
}
/**
* @param {T} entity
* @param {T} element
*/
render(entity) {
return ""
constructed(element) {
}
/**
* @param {T} element
*/
setup(element) {
// TODO replace with the safer element.setHTML(...) when it will be availableBreack
element.innerHTML = this.render(element)
connectedCallback(element) {
}
/**
* @param {T} element
* @param {Map} changedProperties
*/
willUpdate(element, changedProperties) {
}
/**
* @param {T} element
* @param {Map} changedProperties
*/
update(element, changedProperties) {
}
/**
* @param {T} element
*/
render(element) {
return html``
}
/**
* @param {T} element
* @param {Map} changedProperties
*/
firstUpdated(element, changedProperties) {
}
/**
* @param {T} element
* @param {Map} changedProperties
*/
updated(element, changedProperties) {
}
/**

View File

@@ -1,11 +1,9 @@
// @ts-check
import html from "./html"
import { html } from "lit"
import IInputPinTemplate from "./IInputPinTemplate"
/**
* @typedef {import("../element/PinElement").default} PinElement
* @typedef {import("../entity/LinearColorEntity").default} LinearColorEntity)}
* @typedef {import("../entity/LinearColorEntity").default} LinearColorEntity}
*/
export default class LinearColorPinTemplate extends IInputPinTemplate {
@@ -15,12 +13,11 @@ export default class LinearColorPinTemplate extends IInputPinTemplate {
/**
* @param {PinElement} pin
* @param {Map} changedProperties
*/
setup(pin) {
super.setup(pin)
firstUpdated(pin, changedProperties) {
super.firstUpdated(pin, changedProperties)
this.#input = pin.querySelector(".ueb-pin-input")
this.#input.dataset.linearColor = /** @type {LinearColorEntity} */(pin.entity.DefaultValue).toString()
let self = this
}
/**
@@ -32,7 +29,7 @@ export default class LinearColorPinTemplate extends IInputPinTemplate {
/**
* @param {PinElement} pin
* @param {String[]?} value
* @param {String[]} value
*/
setInputs(pin, value = []) {
}
@@ -43,7 +40,7 @@ export default class LinearColorPinTemplate extends IInputPinTemplate {
renderInput(pin) {
if (pin.isInput()) {
return html`
<span class="ueb-pin-input"></span>
<span class="ueb-pin-input" data-linear-color="${pin.defaultValue.toString()}"></span>
`
}
return super.renderInput(pin)

View File

@@ -1,42 +0,0 @@
// @ts-check
import html from "./html"
import ITemplate from "./ITemplate"
import sanitizeText from "./sanitizeText"
/**
* @typedef {import("../element/LinkMessageElement").default} LinkMessageElement
*/
export default class LinkMessageTemplate extends ITemplate {
/**
* @param {LinkMessageElement} linkMessage
*/
render(linkMessage) {
return html`
<span class="${sanitizeText(linkMessage.icon)}"></span>
<span class="ueb-link-message"></span>
`
}
/**
* Applies the style to the element.
* @param {LinkMessageElement} linkMessage
*/
setup(linkMessage) {
const a = super.setup(linkMessage)
const linkMessageSetup = _ =>
/** @type {HTMLElement} */(linkMessage.querySelector(".ueb-link-message")).innerText = linkMessage.message(
linkMessage.linkElement.sourcePin,
linkMessage.linkElement.destinationPin
)
linkMessage.linkElement = linkMessage.closest("ueb-link")
if (linkMessage.linkElement) {
linkMessageSetup()
} else {
window.customElements.whenDefined("ueb-link-message").then(linkMessageSetup)
}
}
}

View File

@@ -1,16 +1,17 @@
// @ts-check
import { css, html } from "lit"
import Configuration from "../Configuration"
import html from "./html"
import ITemplate from "./ITemplate"
import sanitizeText from "./sanitizeText"
import Utility from "../Utility"
import IFromToPositionedTemplate from "./IFromToPositionedTemplate"
/**
* @typedef {import("../element/LinkElement").default} LinkElement
* @typedef {import("../element/LinkMessageElement").default} LinkMessageElement
*/
export default class LinkTemplate extends ITemplate {
/**
* @extends {IFromToPositionedTemplate<LinkElement>}
*/
export default class LinkTemplate extends IFromToPositionedTemplate {
/**
* Returns a function performing the inverse multiplication y = a / x + q. The value of a and q are calculated using
@@ -53,12 +54,59 @@ export default class LinkTemplate extends ITemplate {
: m * x + q
}
static c1DecreasingValue = LinkTemplate.decreasingValue(-0.1, [100, 15])
static c1DecreasingValue = LinkTemplate.decreasingValue(-0.15, [100, 15])
static c2DecreasingValue = LinkTemplate.decreasingValue(-0.06, [500, 130])
static c2Clamped = LinkTemplate.clampedLine([0, 100], [200, 30])
/**
* @param {LinkElement} link
* @param {Map} changedProperties
*/
willUpdate(link, changedProperties) {
super.willUpdate(link, changedProperties)
const dx = Math.max(Math.abs(link.initialPositionX - link.finaPositionX), 1)
const width = Math.max(dx, Configuration.linkMinWidth)
// const height = Math.max(Math.abs(link.initialPositionY - link.finaPositionY), 1)
const fillRatio = dx / width
// const aspectRatio = width / height
const xInverted = link.originatesFromInput
? link.initialPositionX < link.finaPositionX
: link.finaPositionX < link.initialPositionX
link.startPixels = dx < width // If under minimum width
? (width - dx) / 2 // Start from half the empty space
: 0 // Otherwise start from the beginning
link.startPercentage = xInverted ? link.startPixels + fillRatio * 100 : link.startPixels
const c1
= link.startPercentage
+ (xInverted
? LinkTemplate.c1DecreasingValue(width)
: 10
)
* fillRatio
let c2 = LinkTemplate.c2Clamped(xInverted ? -dx : dx) + link.startPercentage
c2 = Math.min(c2, LinkTemplate.c2DecreasingValue(width))
link.svgPathD = Configuration.linkRightSVGPath(link.startPercentage, c1, c2)
}
/**
* @param {LinkElement} link
* @param {Map} changedProperties
*/
update(link, changedProperties) {
super.update(link, changedProperties)
if (changedProperties.has("originatesFromInput")) {
link.style.setProperty("--ueb-from-input", link.originatesFromInput ? "1" : "0")
}
const referencePin = link.sourcePin ?? link.destinationPin
if (referencePin) {
link.style.setProperty("--ueb-link-color-rgb", Utility.printLinearColor(referencePin.color))
}
link.style.setProperty("--ueb-link-start", `${Math.round(link.startPixels)}`)
link.style.setProperty("--ueb-start-percentage", `${Math.round(link.startPercentage)}%`)
}
/**
* @param {LinkElement} link
*/
@@ -67,116 +115,16 @@ export default class LinkTemplate extends ITemplate {
return html`
<svg version="1.2" baseProfile="tiny" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="none">
<g>
<path id="${uniqueId}" fill="none" vector-effect="non-scaling-stroke" />
<path id="${uniqueId}" fill="none" vector-effect="non-scaling-stroke" d="${link.svgPathD}" />
<use href="#${uniqueId}" pointer-events="stroke" stroke-width="10" />
</g>
</svg>
${link.linkMessageIcon != "" || link.linkMessageText != "" ? html`
<div class="ueb-link-message">
<span class="${link.linkMessageIcon}"></span>
<span class="ueb-link-message-text">${link.linkMessageText}</span>
</div>
` : html``}
`
}
/**
* @param {LinkElement} link
*/
setup(link) {
super.setup(link)
if (link.linkMessageElement) {
link.appendChild(link.linkMessageElement)
}
link.classList.add("ueb-positioned")
link.pathElement = link.querySelector("path")
const referencePin = link.sourcePin ?? link.destinationPin
if (referencePin) {
link.style.setProperty("--ueb-pin-color", referencePin.getColor())
}
this.applyPins(link)
if (link.sourcePin && link.destinationPin) {
this.applyFullLocation(link)
}
}
/**
* @param {LinkElement} link
*/
applyPins(link) {
if (link.sourcePin) {
link.dataset.source = link.sourcePin.GetPinId().toString()
}
if (link.destinationPin) {
link.dataset.destination = link.destinationPin.GetPinId().toString()
}
}
/**
* @param {LinkElement} link
*/
applyStartDragging(link) {
link.blueprint.dataset.creatingLink = "true"
link.classList.add("ueb-link-dragging")
}
/**
* @param {LinkElement} link
*/
applyFinishDragging(link) {
link.blueprint.dataset.creatingLink = "false"
link.classList.remove("ueb-link-dragging")
}
/**
* @param {LinkElement} link
*/
applySourceLocation(link) {
link.style.setProperty("--ueb-from-input", link.originatesFromInput ? "1" : "0")
link.style.setProperty("--ueb-from-x", sanitizeText(link.sourceLocation[0]))
link.style.setProperty("--ueb-from-y", sanitizeText(link.sourceLocation[1]))
}
/**
* @param {LinkElement} link
*/
applyFullLocation(link) {
const dx = Math.max(Math.abs(link.sourceLocation[0] - link.destinationLocation[0]), 1)
const width = Math.max(dx, Configuration.linkMinWidth)
const height = Math.max(Math.abs(link.sourceLocation[1] - link.destinationLocation[1]), 1)
const fillRatio = dx / width
const aspectRatio = width / height
const xInverted = link.originatesFromInput
? link.sourceLocation[0] < link.destinationLocation[0]
: link.destinationLocation[0] < link.sourceLocation[0]
let start = dx < width // If under minimum width
? (width - dx) / 2 // Start from half the empty space
: 0 // Otherwise start from the beginning
link.style.setProperty("--ueb-from-x", sanitizeText(link.sourceLocation[0]))
link.style.setProperty("--ueb-from-y", sanitizeText(link.sourceLocation[1]))
link.style.setProperty("--ueb-to-x", sanitizeText(link.destinationLocation[0]))
link.style.setProperty("--ueb-to-y", sanitizeText(link.destinationLocation[1]))
link.style.setProperty("margin-left", `-${start}px`)
if (xInverted) {
start += fillRatio * 100
}
link.style.setProperty("--ueb-start-percentage", `${start}%`)
const c1
= start
+ (xInverted
? LinkTemplate.c1DecreasingValue(width)
: 10
)
* fillRatio
let c2 = LinkTemplate.c2Clamped(xInverted ? -dx : dx) + start
c2 = Math.min(c2, LinkTemplate.c2DecreasingValue(width))
const d = Configuration.linkRightSVGPath(start, c1, c2)
// TODO move to CSS when Firefox will support property d and css will have enough functions
link.pathElement?.setAttribute("d", d)
}
/**
* @param {LinkElement} link
* @param {LinkMessageElement} linkMessage
*/
applyLinkMessage(link, linkMessage) {
link.querySelectorAll("ueb-link-message").forEach(element => element.remove())
link.appendChild(linkMessage)
link.linkMessageElement = linkMessage
}
}

View File

@@ -1,5 +1,3 @@
// @ts-check
import IInputPinTemplate from "./IInputPinTemplate"
/**
@@ -13,9 +11,10 @@ export default class NamePinTemplate extends IInputPinTemplate {
/**
* @param {PinElement} pin
* @param {Map} changedProperties
*/
setup(pin) {
super.setup(pin)
firstUpdated(pin, changedProperties) {
super.firstUpdated(pin, changedProperties)
this.onInputHandler = e => {
e.stopPropagation()
if (

View File

@@ -1,8 +1,5 @@
// @ts-check
import html from "./html"
import { html } from "lit"
import PinElement from "../element/PinElement"
import sanitizeText from "./sanitizeText"
import SelectableDraggableTemplate from "./SelectableDraggableTemplate"
/**
@@ -14,9 +11,7 @@ export default class NodeTemplate extends SelectableDraggableTemplate {
toggleAdvancedDisplayHandler
/**
* Computes the html content of the target element.
* @param {NodeElement} node Graph node element
* @returns The result html
* @param {NodeElement} node
*/
render(node) {
return html`
@@ -26,7 +21,7 @@ export default class NodeTemplate extends SelectableDraggableTemplate {
<div class="ueb-node-name">
<span class="ueb-node-name-symbol"></span>
<span class="ueb-node-name-text">
${sanitizeText(node.getNodeDisplayName())}
${node.nodeDisplayName}
</span>
</div>
</div>
@@ -34,11 +29,11 @@ export default class NodeTemplate extends SelectableDraggableTemplate {
<div class="ueb-node-inputs"></div>
<div class="ueb-node-outputs"></div>
</div>
${node.entity.EnabledState?.toString() == "DevelopmentOnly" ? html`
${node.enabledState?.toString() == "DevelopmentOnly" ? html`
<div class="ueb-node-developmentonly">Development Only</div>
` : ""}
${node.entity.AdvancedPinDisplay ? html`
<div class="ueb-node-expansion">
` : html``}
${node.advancedPinDisplay ? html`
<div class="ueb-node-expansion" @click="${this.toggleAdvancedDisplayHandler}">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
@@ -47,18 +42,10 @@ export default class NodeTemplate extends SelectableDraggableTemplate {
class="ueb-node-expansion-icon"
viewBox="4 4 24 24"
>
<path
d="
M 16.003 18.626
l 7.081 -7.081
L 25 13.46
l -8.997 8.998 -9.003 -9 1.917 -1.916
z
"
/>
<path d="M 16.003 18.626 l 7.081 -7.081 L 25 13.46 l -8.997 8.998 -9.003 -9 1.917 -1.916 z" />
</svg>
</div>
` : ""}
` : html``}
</div>
</div>
`
@@ -66,52 +53,25 @@ export default class NodeTemplate extends SelectableDraggableTemplate {
/**
* @param {NodeElement} node
* @param {Map} changedProperties
*/
setup(node) {
super.setup(node)
node.dataset.name = sanitizeText(node.entity.getObjectName())
if (node.entity.EnabledState) {
node.dataset.enabledState = node.entity.EnabledState.toString()
}
if (node.selected) {
node.classList.add("ueb-selected")
}
this.applyAdvancedPinDisplay(node)
node.style.setProperty("--ueb-position-x", sanitizeText(node.location[0]))
node.style.setProperty("--ueb-position-y", sanitizeText(node.location[1]))
/** @type {HTMLElement} */
let inputContainer = node.querySelector(".ueb-node-inputs")
/** @type {HTMLElement} */
let outputContainer = node.querySelector(".ueb-node-outputs")
let pins = node.getPinEntities()
pins.filter(v => v.isInput()).forEach(v => inputContainer.appendChild(new PinElement(v)))
pins.filter(v => v.isOutput()).forEach(v => outputContainer.appendChild(new PinElement(v)))
let self = this
async firstUpdated(node, changedProperties) {
super.firstUpdated(node, changedProperties)
const inputContainer = /** @type {HTMLElement} */(node.querySelector(".ueb-node-inputs"))
const outputContainer = /** @type {HTMLElement} */(node.querySelector(".ueb-node-outputs"))
Promise.all(node.getPinElements().map(n => n.updateComplete)).then(() => node.dispatchReflowEvent())
node.getPinElements().forEach(p => {
if (p.isInput()) {
inputContainer.appendChild(p)
} else if (p.isOutput()) {
outputContainer.appendChild(p)
}
})
this.toggleAdvancedDisplayHandler = _ => {
node.toggleShowAdvancedPinDisplay()
node.addNextUpdatedCallbacks(() => node.dispatchReflowEvent(), true)
}
if (node.entity.AdvancedPinDisplay) {
node.querySelector(".ueb-node-expansion").addEventListener("click", this.toggleAdvancedDisplayHandler)
}
}
/**
* @param {NodeElement} node
*/
applyAdvancedPinDisplay(node) {
if (node.entity.AdvancedPinDisplay) {
node.dataset.advancedDisplay = node.entity.AdvancedPinDisplay.toString()
}
}
/**
* @param {NodeElement} node
*/
applyRename(node) {
const nodeName = node.entity.getObjectName()
node.dataset.name = sanitizeText(nodeName)
// @ts-expect-error
node.querySelector(".ueb-node-name-text").innerText = sanitizeText(node.getNodeDisplayName())
node.nodeNameElement = /** @type {HTMLElement} */(node.querySelector(".ueb-node-name-text"))
}
/**

View File

@@ -1,19 +1,26 @@
// @ts-check
import html from "./html"
import { css, html } from "lit"
import ITemplate from "./ITemplate"
import MouseCreateLink from "../input/mouse/MouseCreateLink"
import sanitizeText from "./sanitizeText"
import Utility from "../Utility"
/**
* @typedef {import ("../input/IInput").default} IInput
* @typedef {import("../input/IInput").default} IInput
* @typedef {import("../element/NodeElement").default} NodeElement
* @typedef {import("../element/PinElement").default} PinElement
*/
export default class PinTemplate extends ITemplate {
static styles = css``
/**
* @param {PinElement} pin
*/
connectedCallback(pin) {
super.connectedCallback(pin)
pin.nodeElement = pin.closest("ueb-node")
}
/**
* @param {PinElement} pin
* @returns {IInput[]}
@@ -38,13 +45,13 @@ export default class PinTemplate extends ITemplate {
`
const content = html`
<div class="ueb-pin-content">
<span class="ueb-pin-name">${sanitizeText(pin.getPinDisplayName())}</span>
<span class="ueb-pin-name">${pin.getPinDisplayName()}</span>
${this.renderInput(pin)}
</div>
`
return html`
<div class="ueb-pin-wrapper">
${pin.isInput() ? icon + content : content + icon}
${pin.isInput() ? html`${icon}${content}` : html`${content}${icon}`}
</div>
`
}
@@ -53,42 +60,24 @@ export default class PinTemplate extends ITemplate {
* @param {PinElement} pin
*/
renderIcon(pin) {
return '<span class="ueb-pin-icon-value"></span>'
return html`<span class="ueb-pin-icon-value"></span>`
}
/**
* @param {PinElement} pin
*/
renderInput(pin) {
return ""
return html``
}
/**
* @param {PinElement} pin
* @param {Map} changedProperties
*/
setup(pin) {
super.setup(pin)
pin.classList.add(
"ueb-node-" + (pin.isInput() ? "input" : pin.isOutput() ? "output" : "hidden"),
"ueb-pin-type-" + sanitizeText(pin.getType())
)
firstUpdated(pin, changedProperties) {
super.firstUpdated(pin, changedProperties)
pin.dataset.id = pin.GetPinIdValue()
if (pin.entity.bAdvancedView) {
pin.dataset.advancedView = "true"
}
pin.clickableElement = pin
pin.nodeElement = pin.closest("ueb-node")
}
/**
* @param {PinElement} pin
*/
applyConnected(pin) {
if (pin.isLinked()) {
pin.classList.add("ueb-pin-fill")
} else {
pin.classList.remove("ueb-pin-fill")
}
}
/**
@@ -96,8 +85,10 @@ export default class PinTemplate extends ITemplate {
*/
getLinkLocation(pin) {
const rect = pin.querySelector(".ueb-pin-icon").getBoundingClientRect()
return pin.blueprint.compensateTranslation(Utility.convertLocation(
const location = Utility.convertLocation(
[(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2],
pin.blueprint.gridElement))
pin.blueprint.gridElement
)
return pin.blueprint.compensateTranslation(location)
}
}

View File

@@ -1,7 +1,5 @@
// @ts-check
import Utility from "../Utility"
import IInputPinTemplate from "./IInputPinTemplate"
import Utility from "../Utility"
/**
* @typedef {import("../element/PinElement").default} PinElement
@@ -18,7 +16,7 @@ export default class RealPinTemplate extends IInputPinTemplate {
let updateDefaultValue = true
if (isNaN(num)) {
num = parseFloat(pin.entity.DefaultValue != ""
? pin.entity.DefaultValue
? /** @type {String} */(pin.entity.DefaultValue)
: pin.entity.AutogeneratedDefaultValue)
}
if (isNaN(num)) {

View File

@@ -1,20 +1,18 @@
// @ts-check
import ITemplate from "./ITemplate"
import MouseMoveNodes from "../input/mouse/MouseMoveNodes"
import sanitizeText from "./sanitizeText"
/**
* @typedef {import("../element/ISelectableDraggableElement").default} ISelectableDraggableElement
*/
/**
* @extends {ITemplate<ISelectableDraggableElement>}
* @template {ISelectableDraggableElement} T
* @extends {ITemplate<T>}
*/
export default class SelectableDraggableTemplate extends ITemplate {
/**
* @param {ISelectableDraggableElement} element
* @param {T} element
*/
createInputObjects(element) {
return [
@@ -26,21 +24,27 @@ export default class SelectableDraggableTemplate extends ITemplate {
}
/**
* @param {ISelectableDraggableElement} element
* @param {T} element
* @param {Map} changedProperties
*/
applyLocation(element) {
element.style.setProperty("--ueb-position-x", sanitizeText(element.location[0]))
element.style.setProperty("--ueb-position-y", sanitizeText(element.location[1]))
update(element, changedProperties) {
super.update(element, changedProperties)
if (changedProperties.has("locationX")) {
element.style.setProperty("--ueb-position-x", `${element.locationX}`)
}
if (changedProperties.has("locationY")) {
element.style.setProperty("--ueb-position-y", `${element.locationY}`)
}
}
/**
* @param {ISelectableDraggableElement} element
* @param {T} element
* @param {Map} changedProperties
*/
applySelected(element) {
if (element.selected) {
element.classList.add("ueb-selected")
} else {
element.classList.remove("ueb-selected")
firstUpdated(element, changedProperties) {
super.firstUpdated(element, changedProperties)
if (element.selected && !element.listeningDrag) {
element.setSelected(true)
}
}
}

View File

@@ -1,51 +1,9 @@
// @ts-check
import ITemplate from "./ITemplate"
import sanitizeText from "./sanitizeText"
import IFromToPositionedTemplate from "./IFromToPositionedTemplate"
/**
* @typedef {import("../element/SelectorElement").default} SelectorElement
*/
export default class SelectorTemplate extends ITemplate {
export default class SelectorTemplate extends IFromToPositionedTemplate {
/**
* Applies the style to the element.
* @param {SelectorElement} selector Selector element
*/
setup(selector) {
super.setup(selector)
this.applyFinishSelecting(selector)
}
/**
* Applies the style relative to selection beginning.
* @param {SelectorElement} selector Selector element
*/
applyStartSelecting(selector, initialPosition) {
// Set initial position
selector.style.setProperty("--ueb-from-x", sanitizeText(initialPosition[0]))
selector.style.setProperty("--ueb-from-y", sanitizeText(initialPosition[1]))
// Final position coincide with the initial position, at the beginning of selection
selector.style.setProperty("--ueb-to-x", sanitizeText(initialPosition[0]))
selector.style.setProperty("--ueb-to-y", sanitizeText(initialPosition[1]))
selector.blueprint.dataset.selecting = "true"
}
/**
* Applies the style relative to selection.
* @param {SelectorElement} selector Selector element
*/
applyDoSelecting(selector, finalPosition) {
selector.style.setProperty("--ueb-to-x", sanitizeText(finalPosition[0]))
selector.style.setProperty("--ueb-to-y", sanitizeText(finalPosition[1]))
}
/**
* Applies the style relative to selection finishing.
* @param {SelectorElement} selector Selector element
*/
applyFinishSelecting(selector) {
selector.blueprint.dataset.selecting = "false"
}
}

View File

@@ -1,5 +1,3 @@
// @ts-check
import IInputPinTemplate from "./IInputPinTemplate"
/**
@@ -10,8 +8,9 @@ export default class StringPinTemplate extends IInputPinTemplate {
/**
* @param {PinElement} pin
* @param {Map} changedProperties
*/
setup(pin) {
super.setup(pin)
firstUpdated(pin, changedProperties) {
super.firstUpdated(pin, changedProperties)
}
}

View File

@@ -1,5 +1,3 @@
// @ts-check
import NodeTemplate from "./NodeTemplate"
export default class VariadicNodeTemplate extends NodeTemplate {

View File

@@ -1,7 +0,0 @@
// @ts-check
/**
* This solves the sole purpose of providing compression capability for html inside template literals strings. Check rollup.config.js function minifyHTML()
*/
const html = String.raw
export default html

View File

@@ -1,17 +0,0 @@
// @ts-check
const div = document.createElement("div")
const tagReplacement = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
"'": '&#39;',
'"': '&quot;'
}
function sanitizeText(value) {
return value.toString().replace(/[&<>'"]/g, tag => tagReplacement[tag])
}
export default sanitizeText