Links improved, refactoring

This commit is contained in:
barsdeveloper
2022-02-27 11:54:40 +01:00
parent d2f7de4f88
commit 96f0d593e7
23 changed files with 554 additions and 299 deletions

View File

@@ -1 +1 @@
.ueb{--ueb-node-value-color: white}.ueb-node-value-boolean{--ueb-node-value-color: #930000}.ueb-node-value-integer{--ueb-node-value-color: #1fe0ad}.ueb-node-value-float{--ueb-node-value-color: #9ffb44}.ueb-node-value-vector{--ueb-node-value-color: #fcc823}.ueb-node-value-rotator{--ueb-node-value-color: #9eb1fc}.ueb-node-value-string{--ueb-node-value-color: #fc00d2;--ueb-node-value-background: linear-gradient(90deg, #fc00d220, #fc00d280 15%, #fc00d250 85%, transparent)}.ueb-node-value-name{--ueb-node-value-color: #cb81fc}.ueb-node-value-objectreference{--ueb-node-value-color: #00a8f2}/*# sourceMappingURL=ueblueprint-node-value-type-color.css.map */
.ueb{--ueb-node-value-color: white;--ueb-node-value-dim-color: #afafaf}.ueb-node-value-boolean{--ueb-node-value-color: #930000}.ueb-node-value-integer{--ueb-node-value-color: #1fe0ad}.ueb-node-value-float{--ueb-node-value-color: #9ffb44}.ueb-node-value-vector{--ueb-node-value-color: #fcc823}.ueb-node-value-rotator{--ueb-node-value-color: #9eb1fc}.ueb-node-value-string{--ueb-node-value-color: #fc00d2;--ueb-node-value-background: linear-gradient(90deg, #fc00d220, #fc00d280 15%, #fc00d250 85%, transparent)}.ueb-node-value-name{--ueb-node-value-color: #cb81fc}.ueb-node-value-objectreference{--ueb-node-value-color: #00a8f2}/*# sourceMappingURL=ueblueprint-node-value-type-color.css.map */

View File

@@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../scss/ueblueprint-node-value-type-color.scss"],"names":[],"mappings":"AAAA,KAEI,8BAGJ,wBAEI,gCAGJ,wBAEI,gCAGJ,sBAEI,gCAGJ,uBAEI,gCAGJ,wBAEI,gCAGJ,uBAEI,gCACA,0GAGJ,qBAEI,gCAGJ,gCAEI","file":"ueblueprint-node-value-type-color.css"}
{"version":3,"sourceRoot":"","sources":["../../scss/ueblueprint-node-value-type-color.scss"],"names":[],"mappings":"AAAA,KAGI,8BACA,oCAGJ,wBAEI,gCAGJ,wBAEI,gCAGJ,sBAEI,gCAGJ,uBAEI,gCAGJ,wBAEI,gCAGJ,uBAEI,gCACA,0GAGJ,qBAEI,gCAGJ,gCAEI","file":"ueblueprint-node-value-type-color.css"}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../scss/ueblueprint-style.scss"],"names":[],"mappings":"AAAA,WACI,qBACA,iBACA,IACI,kGAIR,WACI,qBACA,mBACA,IACI,sGAIR,cACI,cACA,kBACA,8EACA,+BACA,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,aACA,0BACA,UAGJ,mBACI,gBAGJ,mBACI,kBACA,aACA,gBACA,qBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,kDACI,gBAGJ,qBAGI,eACA,6CAGJ,iBAEI,mBAGJ,iBAEI,kBAGJ,iBAEI,mBAGJ,iBAEI,iBACA,uDAGJ,iBAEI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBAEI,iBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,SACI,cACA,kBACA,sGACA,qCACA,uDACA,sBAGJ,wEACI,YAGJ,iBACI,YACA,YACA,+CAGJ,cACI,UAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,gCACA,qCACA,0BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,WACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,QACI,cACA,gBAGJ,mDACI,eAGJ,sFACI,4CACA,iBAGJ,qBACI,qBACA,kBACA,YACA,aACA,wBACA,wBAGJ,6BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,6CACA,kBAGJ,6BACI,uCAGJ,4BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,mDAGJ,gEACI,8DACA,8DACA,8DACA,8DACA,oGACA,qGACA,kBACA,wCACA,yCACA,0CACA,4CAGJ,aACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,gDACI,mBAIJ,eACI,mBAGJ,SACI,+DACA,cACA,8CACA,qBAGJ,aACI,yDACA,kBACA,MACA,OACA,WACA,YACA,iGAKA,iBAGJ,kBAEI,oJAOA,uFACA,qBACA,eACA,4BAGJ,wBACI","file":"ueblueprint-style.css"}
{"version":3,"sourceRoot":"","sources":["../../scss/ueblueprint-style.scss"],"names":[],"mappings":"AAAA,WACI,qBACA,iBACA,IACI,kGAIR,WACI,qBACA,mBACA,IACI,sGAIR,cACI,cACA,kBACA,8EACA,+BACA,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,aACA,0BACA,UAGJ,mBACI,gBAGJ,mBACI,kBACA,aACA,gBACA,qBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,kDACI,gBAGJ,qBAGI,eACA,6CAGJ,iBAEI,mBAGJ,iBAEI,kBAGJ,iBAEI,mBAGJ,iBAEI,iBACA,uDAGJ,iBAEI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBAEI,iBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBAEI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,SACI,cACA,kBACA,sGACA,qCACA,uDACA,sBAGJ,wEACI,YAGJ,iBACI,YACA,YACA,+CAGJ,cACI,UAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,gCACA,qCACA,0BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,WACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,QACI,cACA,gBAGJ,mDACI,eAGJ,sFACI,4CACA,iBAGJ,qBACI,qBACA,kBACA,YACA,aACA,wBACA,wBAGJ,6BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,6CACA,kBAGJ,6BACI,uCAGJ,4BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,mDAGJ,gEACI,8DACA,8DACA,8DACA,8DACA,oGACA,qGACA,kBACA,wCACA,yCACA,0CACA,4CAGJ,aACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,gDACI,mBAIJ,eACI,mBAGJ,SACI,+DACA,cACA,8CACA,qBAGJ,aACI,yDACA,kBACA,MACA,OACA,WACA,YACA,iGAKA,iBAGJ,kBAEI,oJAOA,uFACA,qBACA,eACA,4BAGJ,wBACI,eAGJ,iBACI,cACA,kBACA,8CACA,aACA,sBACA,gBACA,kBACA,4EACA,sCACA","file":"ueblueprint-style.css"}

408
dist/ueblueprint.js vendored
View File

@@ -4,7 +4,7 @@ class Configuration {
static fontSize = "13px"
static gridAxisLineColor = "black"
static gridLineColor = "#353535"
static gridLineWidth = 2 // pixel
static gridLineWidth = 1 // pixel
static gridSet = 8
static gridSetLineColor = "#161616"
static gridSize = 16 // pixel
@@ -405,7 +405,6 @@ class FastSelectionModel {
this.initialPosition[0] < this.boundaries.primaryP.v && this.initialPosition[0] < finalPosition[0]);
}
const secondaryBoundaryCrossed = (index, added) => {
this.selectFunc(this.rectangles[index], added);
this.computeBoundaries(finalPosition);
@@ -563,6 +562,8 @@ class SelectorTemplate extends Template {
class GraphSelector extends GraphElement {
static tagName = "ueb-selector"
constructor() {
super({}, new SelectorTemplate());
this.selectionModel = null;
@@ -594,7 +595,7 @@ class GraphSelector extends GraphElement {
}
}
customElements.define("ueb-selector", GraphSelector);
customElements.define(GraphSelector.tagName, GraphSelector);
/**
* This solves the sole purpose of providing compression capability for html inside template literals strings. Check rollup.config.js function minifyHTML()
@@ -675,7 +676,7 @@ class BlueprintTemplate extends Template {
blueprint.gridElement = blueprint.viewportElement.querySelector(".ueb-grid");
blueprint.nodesContainerElement = blueprint.querySelector("[data-nodes]");
blueprint.selectorElement = new GraphSelector();
blueprint.nodesContainerElement.append(blueprint.selectorElement, ...blueprint.nodes);
blueprint.nodesContainerElement.append(blueprint.selectorElement, ...blueprint.getNodes());
this.applyEndDragScrolling(blueprint);
}
@@ -746,7 +747,6 @@ class Context {
this.blueprint.removeEventListener("blueprint-unfocus", this.blueprintUnfocusHandler);
}
/* Subclasses will probabily override the following methods */
listenEvents() {
}
@@ -1519,6 +1519,7 @@ class Copy extends Context {
/**
* @typedef {import("../graph/GraphLink").default} GraphLink
* @typedef {import("../graph/GraphLinkMessage").default} GraphLinkMessage
*/
class LinkTemplate extends Template {
@@ -1532,7 +1533,7 @@ class LinkTemplate extends Template {
/**
* Computes the html content of the target element.
* @param {GraphLink} link Link connecting two graph nodes
* @param {GraphLink} link connecting two graph nodes
* @returns The result html
*/
render(link) {
@@ -1551,6 +1552,9 @@ class LinkTemplate extends Template {
super.apply(link);
link.classList.add("ueb-positioned");
link.pathElement = link.querySelector("path");
if (link.linkMessageElement) {
link.appendChild(link.linkMessageElement);
}
}
/**
@@ -1587,11 +1591,12 @@ class LinkTemplate extends Template {
link.style.setProperty("margin-left", `-${start}px`);
}
if (xInverted) {
start = start + dx;
start += fillRatio * 100;
}
link.style.setProperty("--ueb-start-percentage", `${100 - start}%`);
const c1 = start + 15 * fillRatio;
let c2 = Math.max(40 / aspectRatio, 30) + start;
const c2Decreasing = -0.05;
const c2Decreasing = -0.06;
const getMaxC2 = (m, p) => {
const a = -m * p[0] * p[0];
const q = p[1] - a / p[0];
@@ -1603,12 +1608,26 @@ class LinkTemplate extends Template {
// TODO move to CSS when Firefox will support property d
link.pathElement.setAttribute("d", d);
}
/**
*
* @param {GraphLink} link element
* @param {GraphLinkMessage} linkMessage
*/
applyLinkMessage(link, linkMessage) {
link.querySelectorAll(linkMessage.constructor.tagName).forEach(element => element.remove());
link.appendChild(linkMessage);
link.linkMessageElement = linkMessage;
}
}
/**
* @typedef {import("./GraphPin").default} GraphPin
* @typedef {import("./GraphLinkMessage").default} GraphLinkMessage
*/
class GraphLink extends GraphElement {
static tagName = "ueb-link"
/** @type {GraphPin} */
#source
/** @type {GraphPin} */
@@ -1616,6 +1635,13 @@ class GraphLink extends GraphElement {
#nodeDeleteHandler
#nodeDragSourceHandler
#nodeDragDestinatonHandler
sourceLocation = [0, 0]
/** @type {SVGPathElement} */
pathElement
/** @type {GraphLinkMessage} */
linkMessageElement
originatesFromInput = false
destinationLocation = [0, 0]
/**
* @param {?GraphPin} source
@@ -1625,17 +1651,16 @@ class GraphLink extends GraphElement {
super({}, new LinkTemplate());
/** @type {import("../template/LinkTemplate").default} */
this.template;
/** @type {SVGPathElement} */
this.pathElement = null;
this.originatesFromInput = false;
this.sourceLocation = [0, 0];
this.destinationLocation = [0, 0];
const self = this;
this.#nodeDeleteHandler = _ => self.blueprint.removeGraphElement(self);
this.#nodeDeleteHandler = _ => self.remove();
this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value);
this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value);
this.setSourcePin(source);
this.setDestinationPin(destination);
if (source) {
this.setSourcePin(source);
}
if (destination) {
this.setDestinationPin(destination);
}
}
/**
@@ -1703,7 +1728,6 @@ class GraphLink extends GraphElement {
this.template.applyFullLocation(this);
}
/**
*
* @returns {GraphPin}
@@ -1745,20 +1769,29 @@ class GraphLink extends GraphElement {
*/
setDestinationPin(graphPin) {
if (this.#destination) {
const nodeElement = this.#source.getGraphNode();
const nodeElement = this.#destination.getGraphNode();
nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler);
nodeElement.removeEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler);
}
this.#destination = graphPin;
if (this.#destination) {
const nodeElement = this.#source.getGraphNode();
const nodeElement = this.#destination.getGraphNode();
nodeElement.addEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler);
nodeElement.addEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler);
this.setDestinationLocation();
}
}
/**
*
* @param {GraphLinkMessage} linkMessage
*/
setLinkMessage(linkMessage) {
this.template.applyLinkMessage(this, linkMessage);
}
}
customElements.define("ueb-link", GraphLink);
customElements.define(GraphLink.tagName, GraphLink);
/**
* @typedef {import("../graph/GraphPin").default} GraphPin
@@ -1808,6 +1841,94 @@ class PinTemplate extends Template {
}
}
/**
* @typedef {import("../graph/GraphLinkMessage").default} GraphLinkMessage
*/
class LinkMessageTemplate extends Template {
/**
* Computes the html content of the target element.
* @param {GraphLinkMessage} linkMessage attached to link destination
* @returns The result html
*/
render(linkMessage) {
return html`
<span class="${linkMessage.icon}"></span>
<span class="ueb-link-message"></span>
`
}
/**
* Applies the style to the element.
* @param {GraphLinkMessage} linkMessage element
*/
apply(linkMessage) {
super.apply(linkMessage);
linkMessage.linkElement = linkMessage.closest(GraphLink.tagName);
linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message(
linkMessage.linkElement.getSourcePin(),
linkMessage.linkElement.getDestinationPin()
);
}
}
/**
* @typedef {import("./GraphPin").default} GraphPin
* @typedef {import("./GraphLink").default} GraphLink
* @typedef {(sourcePin: GraphPin, sourcePin: GraphPin) => String} LinkRetrieval
*/
class GraphLinkMessage extends GraphElement {
static tagName = "ueb-link-message"
static CONVERT_TYPE = _ => new GraphLinkMessage(
"ueb-icon-conver-type",
/** @type {LinkRetrieval} */
(s, d) => `Convert ${s.getType()} to ${d.getType()}.`
)
static DIRECTIONS_INCOMPATIBLE = _ => new GraphLinkMessage(
"ueb-icon-directions-incompatible",
/** @type {LinkRetrieval} */
(s, d) => "Directions are not compatbile."
)
static PLACE_NODE = _ => new GraphLinkMessage(
"ueb-icon-place-node",
/** @type {LinkRetrieval} */
(s, d) => "Place a new node."
)
static REPLACE_LiNK = _ => new GraphLinkMessage(
"ueb-icon-replace-link",
/** @type {LinkRetrieval} */
(s, d) => "Replace existing input connections."
)
static SAME_NODE = _ => new GraphLinkMessage(
"ueb-icon-same-node",
/** @type {LinkRetrieval} */
(s, d) => "Both are on the same node."
)
static TYPES_INCOMPATIBLE = _ => new GraphLinkMessage(
"ueb-icon-types-incompatible",
/** @type {LinkRetrieval} */
(s, d) => `${s.getType()} is not compatible with ${d.getType()}.`
)
/** @type {String} */
icon
/** @type {String} */
message
/** @type {GraphLink} */
linkElement
constructor(icon, message) {
super({}, new LinkMessageTemplate());
this.icon = icon;
this.message = message;
}
}
customElements.define(GraphLinkMessage.tagName, GraphLinkMessage);
class Pointing extends Context {
constructor(target, blueprint, options) {
@@ -1965,8 +2086,12 @@ class MouseClickDrag extends Pointing {
}
}
/** @typedef {import("../../graph/GraphPin").default} GraphPin */
class MouseCreateLink extends MouseClickDrag {
/** @type {NodeListOf<GraphPin>} */
#listenedPins
/** @type {(e: MouseEvent) => void} */
#mouseenterHandler
@@ -1997,12 +2122,15 @@ class MouseCreateLink extends MouseClickDrag {
startDrag() {
this.link = new GraphLink(this.target, null);
this.link.setLinkMessage(GraphLinkMessage.PLACE_NODE());
this.blueprint.nodesContainerElement.insertBefore(this.link, this.blueprint.selectorElement.nextElementSibling);
this.blueprint.querySelectorAll("ueb-pin." + this.target.isInput() ? "output" : "input")
.forEach(pin => {
pin.addEventListener("mouseenter", this.#mouseenterHandler);
pin.addEventListener("mouseleave", this.#mouseleaveHandler);
});
this.#listenedPins = this.blueprint.querySelectorAll(this.target.constructor.tagName);
this.#listenedPins.forEach(pin => {
if (pin != this.target) {
pin.getClickableElement().addEventListener("mouseenter", this.#mouseenterHandler);
pin.getClickableElement().addEventListener("mouseleave", this.#mouseleaveHandler);
}
});
}
dragTo(location, movement) {
@@ -2010,13 +2138,19 @@ class MouseCreateLink extends MouseClickDrag {
}
endDrag() {
this.blueprint.querySelectorAll("ueb-pin." + this.target.isInput() ? "output" : "input")
.forEach(pin => {
pin.removeEventListener("mouseenter", this.#mouseenterHandler);
pin.removeEventListener("mouseleave", this.#mouseleaveHandler);
});
if (this.enteredPin) {
this.link.setDestinationPin(this.link);
this.#listenedPins.forEach(pin => {
pin.removeEventListener("mouseenter", this.#mouseenterHandler);
pin.removeEventListener("mouseleave", this.#mouseleaveHandler);
});
if (this.enteredPin && !this.blueprint.getLinks().find(
link =>
link.getSourcePin() == this.target && link.getDestinationPin() == this.enteredPin
|| link.getSourcePin() == this.enteredPin && link.getDestinationPin() == this.target
)) {
this.link.setDestinationPin(this.enteredPin);
this.blueprint.addGraphElement(this.link);
} else {
this.link.remove();
}
this.link = null;
}
@@ -2024,6 +2158,8 @@ class MouseCreateLink extends MouseClickDrag {
class GraphPin extends GraphElement {
static tagName = "ueb-pin"
constructor(entity) {
super(entity, new PinTemplate());
/** @type {import("../entity/PinEntity").default} */
@@ -2071,6 +2207,10 @@ class GraphPin extends GraphElement {
return this.entity.getType()
}
getClickableElement() {
return this.clickableElement
}
/**
* Returns The exact location where the link originates from or arrives at.
* @returns {Number[]} The location array
@@ -2084,7 +2224,7 @@ class GraphPin extends GraphElement {
}
}
customElements.define("ueb-pin", GraphPin);
customElements.define(GraphPin.tagName, GraphPin);
/**
* @typedef {import("../graph/SelectableDraggable").default} SelectableDraggable
@@ -2118,29 +2258,6 @@ class SelectableDraggableTemplate extends Template {
*/
class NodeTemplate extends SelectableDraggableTemplate {
/**
* Computes the html content of the target element.
* @param {GraphNode} node Graph node element
* @returns The result html
*/
header(node) {
return html`
`
}
/**
* Computes the html content of the target element.
* @param {GraphNode} node Graph node element
* @returns The result html
*/
body(node) {
let inputs = node.entity.CustomProperties.filter(v => v instanceof PinEntity$1);
inputs.filter(v => v.isOutput());
inputs = inputs.filter(v => v.isInput());
return html`
`
}
/**
* Computes the html content of the target element.
* @param {GraphNode} node Graph node element
@@ -2314,6 +2431,8 @@ class SelectableDraggable extends GraphElement {
class GraphNode extends SelectableDraggable {
static tagName = "ueb-node"
/**
*
* @param {ObjectEntity} entity
@@ -2357,15 +2476,15 @@ class GraphNode extends SelectableDraggable {
}
dispatchDeleteEvent(value) {
let dragEvent = new CustomEvent(Configuration.nodeDragEventName, {
let deleteEvent = new CustomEvent(Configuration.nodeDeleteEventName, {
bubbles: true,
cancelable: true,
});
this.dispatchEvent(dragEvent);
this.dispatchEvent(deleteEvent);
}
}
customElements.define("ueb-node", GraphNode);
customElements.define(GraphNode.tagName, GraphNode);
let P = Parsimmon;
@@ -2733,55 +2852,57 @@ class Zoom extends MouseWheel {
class Blueprint extends GraphElement {
static tagName = "ueb-blueprint"
/** @type {number} */
gridSize = Configuration.gridSize
/** @type {GraphNode[]}" */
nodes = []
/** @type {GraphLink[]}" */
links = []
expandGridSize = Configuration.expandGridSize
/** @type {number[]} */
additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0]
/** @type {number[]} */
translateValue = /*[this.expandGridSize, this.expandGridSize]*/[0, 0]
/** @type {number[]} */
mousePosition = [0, 0]
/** @type {HTMLElement} */
gridElement = null
/** @type {HTMLElement} */
viewportElement = null
/** @type {HTMLElement} */
overlayElement = null
/** @type {GraphSelector} */
selectorElement = null
/** @type {HTMLElement} */
nodesContainerElement = null
/** @type {number} */
zoom = 0
/** @type {HTMLElement} */
headerElement = null
focused = false
/** @type {(node: GraphNode) => BoundariesInfo} */
nodeBoundariesSupplier = node => {
let rect = node.getBoundingClientRect();
let gridRect = this.nodesContainerElement.getBoundingClientRect();
const scaleCorrection = 1 / this.getScale();
return {
primaryInf: (rect.left - gridRect.left) * scaleCorrection,
primarySup: (rect.right - gridRect.right) * scaleCorrection,
// Counter intuitive here: the y (secondary axis is positive towards the bottom, therefore upper bound "sup" is bottom)
secondaryInf: (rect.top - gridRect.top) * scaleCorrection,
secondarySup: (rect.bottom - gridRect.bottom) * scaleCorrection
}
}
/** @type {(node: GraphNode, selected: bool) => void}} */
nodeSelectToggleFunction = (node, selected) => {
node.setSelected(selected);
}
constructor() {
super({}, new BlueprintTemplate());
/** @type {BlueprintTemplate} */
this.template;
/** @type {number} */
this.gridSize = Configuration.gridSize;
/** @type {GraphNode[]}" */
this.nodes = [];
/** @type {GraphLink[]}" */
this.links = [];
this.expandGridSize = Configuration.expandGridSize;
/** @type {number[]} */
this.additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0];
/** @type {number[]} */
this.translateValue = /*[this.expandGridSize, this.expandGridSize]*/[0, 0];
/** @type {number[]} */
this.mousePosition = [0, 0];
/** @type {HTMLElement} */
this.gridElement = null;
/** @type {HTMLElement} */
this.viewportElement = null;
/** @type {HTMLElement} */
this.overlayElement = null;
/** @type {GraphSelector} */
this.selectorElement = null;
/** @type {HTMLElement} */
this.nodesContainerElement = null;
/** @type {number} */
this.zoom = 0;
/** @type {HTMLElement} */
this.headerElement = null;
this.focused = false;
/** @type {(node: GraphNode) => BoundariesInfo} */
this.nodeBoundariesSupplier = node => {
let rect = node.getBoundingClientRect();
let gridRect = this.nodesContainerElement.getBoundingClientRect();
const scaleCorrection = 1 / this.getScale();
return {
primaryInf: (rect.left - gridRect.left) * scaleCorrection,
primarySup: (rect.right - gridRect.right) * scaleCorrection,
// Counter intuitive here: the y (secondary axis is positive towards the bottom, therefore upper bound "sup" is bottom)
secondaryInf: (rect.top - gridRect.top) * scaleCorrection,
secondarySup: (rect.bottom - gridRect.bottom) * scaleCorrection
}
};
/** @type {(node: GraphNode, selected: bool) => void}} */
this.nodeSelectToggleFunction = (node, selected) => {
node.setSelected(selected);
};
}
/**
@@ -2974,7 +3095,6 @@ class Blueprint extends GraphElement {
this.template.applyZoom(this, zoom);
this.zoom = zoom;
if (center) {
center[0] += this.translateValue[0];
center[1] += this.translateValue[1];
@@ -3001,16 +3121,12 @@ class Blueprint extends GraphElement {
}
/**
*
* Returns the list of nodes in this blueprint. It can filter the list providing just the selected ones.
* @returns {GraphNode[]} Nodes
*/
getNodes(selected = false) {
if (selected) {
return this.nodes.filter(
/**
*
* @param {GraphNode} node
*/
node => node.selected
)
} else {
@@ -3018,18 +3134,26 @@ class Blueprint extends GraphElement {
}
}
/**
* Returns the list of links in this blueprint.
* @returns {GraphLink[]} Nodes
*/
getLinks() {
return this.links
}
/**
* Select all nodes
*/
selectAll() {
this.nodes.forEach(node => this.nodeSelectToggleFunction(node, true));
this.getNodes().forEach(node => this.nodeSelectToggleFunction(node, true));
}
/**
* Unselect all nodes
*/
unselectAll() {
this.nodes.forEach(node => this.nodeSelectToggleFunction(node, false));
this.getNodes().forEach(node => this.nodeSelectToggleFunction(node, false));
}
/**
@@ -3037,16 +3161,23 @@ class Blueprint extends GraphElement {
* @param {...GraphElement} graphElements
*/
addGraphElement(...graphElements) {
[...graphElements].forEach(v => {
if (v instanceof GraphNode) {
this.nodes.push(v);
this.nodesContainerElement?.appendChild(v);
}
if (v instanceof GraphLink) {
this.links.push(v);
this.nodesContainerElement?.appendChild(v);
}
});
if (this.nodesContainerElement) {
graphElements.forEach(element => {
if (element.closest(Blueprint.tagName) != this) {
this.nodesContainerElement.appendChild(element);
}
this.nodes = [...this.querySelectorAll(GraphNode.tagName)];
this.links = [...this.querySelectorAll(GraphLink.tagName)];
});
} else {
graphElements.forEach(element => {
if (element instanceof GraphNode) {
this.nodes.push(element);
} else if (element instanceof GraphLink) {
this.links.push(element);
}
});
}
}
/**
@@ -3054,28 +3185,17 @@ class Blueprint extends GraphElement {
* @param {...GraphElement} graphElements
*/
removeGraphElement(...graphElements) {
let deleteElements = [...graphElements];
if (deleteElements.length == 0) {
return
let removed = false;
graphElements.forEach(element => {
if (element.closest(Blueprint.tagName) == this) {
element.remove();
removed = false;
}
});
if (removed) {
this.nodes = [...this.querySelectorAll(GraphNode.tagName)];
this.links = [...this.querySelectorAll(GraphLink.tagName)];
}
let currentDeleteI = 0;
this.nodes = this.nodes.filter(v => {
if (v == deleteElements[currentDeleteI]) {
++currentDeleteI;
v.remove();
return false
}
return true
});
currentDeleteI = 0;
this.links = this.links.filter(v => {
if (v == deleteElements[currentDeleteI]) {
++currentDeleteI;
v.remove();
return false
}
return true
});
}
setFocused(value = true) {
@@ -3092,7 +3212,7 @@ class Blueprint extends GraphElement {
}
}
customElements.define("ueb-blueprint", Blueprint);
customElements.define(Blueprint.tagName, Blueprint);
class GeneralSerializer extends Serializer {

View File

@@ -17,55 +17,57 @@ import Zoom from "./input/mouse/Zoom"
export default class Blueprint extends GraphElement {
static tagName = "ueb-blueprint"
/** @type {number} */
gridSize = Configuration.gridSize
/** @type {GraphNode[]}" */
nodes = []
/** @type {GraphLink[]}" */
links = []
expandGridSize = Configuration.expandGridSize
/** @type {number[]} */
additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0]
/** @type {number[]} */
translateValue = /*[this.expandGridSize, this.expandGridSize]*/[0, 0]
/** @type {number[]} */
mousePosition = [0, 0]
/** @type {HTMLElement} */
gridElement = null
/** @type {HTMLElement} */
viewportElement = null
/** @type {HTMLElement} */
overlayElement = null
/** @type {GraphSelector} */
selectorElement = null
/** @type {HTMLElement} */
nodesContainerElement = null
/** @type {number} */
zoom = 0
/** @type {HTMLElement} */
headerElement = null
focused = false
/** @type {(node: GraphNode) => BoundariesInfo} */
nodeBoundariesSupplier = node => {
let rect = node.getBoundingClientRect()
let gridRect = this.nodesContainerElement.getBoundingClientRect()
const scaleCorrection = 1 / this.getScale()
return {
primaryInf: (rect.left - gridRect.left) * scaleCorrection,
primarySup: (rect.right - gridRect.right) * scaleCorrection,
// Counter intuitive here: the y (secondary axis is positive towards the bottom, therefore upper bound "sup" is bottom)
secondaryInf: (rect.top - gridRect.top) * scaleCorrection,
secondarySup: (rect.bottom - gridRect.bottom) * scaleCorrection
}
}
/** @type {(node: GraphNode, selected: bool) => void}} */
nodeSelectToggleFunction = (node, selected) => {
node.setSelected(selected)
}
constructor() {
super({}, new BlueprintTemplate())
/** @type {BlueprintTemplate} */
this.template
/** @type {number} */
this.gridSize = Configuration.gridSize
/** @type {GraphNode[]}" */
this.nodes = []
/** @type {GraphLink[]}" */
this.links = []
this.expandGridSize = Configuration.expandGridSize
/** @type {number[]} */
this.additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0]
/** @type {number[]} */
this.translateValue = /*[this.expandGridSize, this.expandGridSize]*/[0, 0]
/** @type {number[]} */
this.mousePosition = [0, 0]
/** @type {HTMLElement} */
this.gridElement = null
/** @type {HTMLElement} */
this.viewportElement = null
/** @type {HTMLElement} */
this.overlayElement = null
/** @type {GraphSelector} */
this.selectorElement = null
/** @type {HTMLElement} */
this.nodesContainerElement = null
/** @type {number} */
this.zoom = 0
/** @type {HTMLElement} */
this.headerElement = null
this.focused = false
/** @type {(node: GraphNode) => BoundariesInfo} */
this.nodeBoundariesSupplier = node => {
let rect = node.getBoundingClientRect()
let gridRect = this.nodesContainerElement.getBoundingClientRect()
const scaleCorrection = 1 / this.getScale()
return {
primaryInf: (rect.left - gridRect.left) * scaleCorrection,
primarySup: (rect.right - gridRect.right) * scaleCorrection,
// Counter intuitive here: the y (secondary axis is positive towards the bottom, therefore upper bound "sup" is bottom)
secondaryInf: (rect.top - gridRect.top) * scaleCorrection,
secondarySup: (rect.bottom - gridRect.bottom) * scaleCorrection
}
}
/** @type {(node: GraphNode, selected: bool) => void}} */
this.nodeSelectToggleFunction = (node, selected) => {
node.setSelected(selected)
}
}
/**
@@ -258,7 +260,6 @@ export default class Blueprint extends GraphElement {
this.template.applyZoom(this, zoom)
this.zoom = zoom
if (center) {
center[0] += this.translateValue[0]
center[1] += this.translateValue[1]
@@ -285,16 +286,12 @@ export default class Blueprint extends GraphElement {
}
/**
*
* Returns the list of nodes in this blueprint. It can filter the list providing just the selected ones.
* @returns {GraphNode[]} Nodes
*/
getNodes(selected = false) {
if (selected) {
return this.nodes.filter(
/**
*
* @param {GraphNode} node
*/
node => node.selected
)
} else {
@@ -302,18 +299,26 @@ export default class Blueprint extends GraphElement {
}
}
/**
* Returns the list of links in this blueprint.
* @returns {GraphLink[]} Nodes
*/
getLinks() {
return this.links
}
/**
* Select all nodes
*/
selectAll() {
this.nodes.forEach(node => this.nodeSelectToggleFunction(node, true))
this.getNodes().forEach(node => this.nodeSelectToggleFunction(node, true))
}
/**
* Unselect all nodes
*/
unselectAll() {
this.nodes.forEach(node => this.nodeSelectToggleFunction(node, false))
this.getNodes().forEach(node => this.nodeSelectToggleFunction(node, false))
}
/**
@@ -321,16 +326,23 @@ export default class Blueprint extends GraphElement {
* @param {...GraphElement} graphElements
*/
addGraphElement(...graphElements) {
[...graphElements].forEach(v => {
if (v instanceof GraphNode) {
this.nodes.push(v)
this.nodesContainerElement?.appendChild(v)
}
if (v instanceof GraphLink) {
this.links.push(v)
this.nodesContainerElement?.appendChild(v)
}
})
if (this.nodesContainerElement) {
graphElements.forEach(element => {
if (element.closest(Blueprint.tagName) != this) {
this.nodesContainerElement.appendChild(element)
}
this.nodes = [...this.querySelectorAll(GraphNode.tagName)]
this.links = [...this.querySelectorAll(GraphLink.tagName)]
})
} else {
graphElements.forEach(element => {
if (element instanceof GraphNode) {
this.nodes.push(element)
} else if (element instanceof GraphLink) {
this.links.push(element)
}
})
}
}
/**
@@ -338,28 +350,17 @@ export default class Blueprint extends GraphElement {
* @param {...GraphElement} graphElements
*/
removeGraphElement(...graphElements) {
let deleteElements = [...graphElements]
if (deleteElements.length == 0) {
return
let removed = false
graphElements.forEach(element => {
if (element.closest(Blueprint.tagName) == this) {
element.remove()
removed = false
}
})
if (removed) {
this.nodes = [...this.querySelectorAll(GraphNode.tagName)]
this.links = [...this.querySelectorAll(GraphLink.tagName)]
}
let currentDeleteI = 0
this.nodes = this.nodes.filter(v => {
if (v == deleteElements[currentDeleteI]) {
++currentDeleteI
v.remove()
return false
}
return true
})
currentDeleteI = 0
this.links = this.links.filter(v => {
if (v == deleteElements[currentDeleteI]) {
++currentDeleteI
v.remove()
return false
}
return true
})
}
setFocused(value = true) {
@@ -376,4 +377,4 @@ export default class Blueprint extends GraphElement {
}
}
customElements.define("ueb-blueprint", Blueprint)
customElements.define(Blueprint.tagName, Blueprint)

View File

@@ -4,7 +4,7 @@ export default class Configuration {
static fontSize = "13px"
static gridAxisLineColor = "black"
static gridLineColor = "#353535"
static gridLineWidth = 2 // pixel
static gridLineWidth = 1 // pixel
static gridSet = 8
static gridSetLineColor = "#161616"
static gridSize = 16 // pixel

View File

@@ -2,11 +2,13 @@ import GraphElement from "./GraphElement"
import LinkTemplate from "../template/LinkTemplate"
import Configuration from "../Configuration"
/**
* @typedef {import("./GraphPin").default} GraphPin
* @typedef {import("./GraphLinkMessage").default} GraphLinkMessage
*/
export default class GraphLink extends GraphElement {
static tagName = "ueb-link"
/** @type {GraphPin} */
#source
/** @type {GraphPin} */
@@ -14,6 +16,13 @@ export default class GraphLink extends GraphElement {
#nodeDeleteHandler
#nodeDragSourceHandler
#nodeDragDestinatonHandler
sourceLocation = [0, 0]
/** @type {SVGPathElement} */
pathElement
/** @type {GraphLinkMessage} */
linkMessageElement
originatesFromInput = false
destinationLocation = [0, 0]
/**
* @param {?GraphPin} source
@@ -23,17 +32,16 @@ export default class GraphLink extends GraphElement {
super({}, new LinkTemplate())
/** @type {import("../template/LinkTemplate").default} */
this.template
/** @type {SVGPathElement} */
this.pathElement = null
this.originatesFromInput = false
this.sourceLocation = [0, 0]
this.destinationLocation = [0, 0]
const self = this
this.#nodeDeleteHandler = _ => self.blueprint.removeGraphElement(self)
this.#nodeDeleteHandler = _ => self.remove()
this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value)
this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value)
this.setSourcePin(source)
this.setDestinationPin(destination)
if (source) {
this.setSourcePin(source)
}
if (destination) {
this.setDestinationPin(destination)
}
}
/**
@@ -101,7 +109,6 @@ export default class GraphLink extends GraphElement {
this.template.applyFullLocation(this)
}
/**
*
* @returns {GraphPin}
@@ -143,17 +150,26 @@ export default class GraphLink extends GraphElement {
*/
setDestinationPin(graphPin) {
if (this.#destination) {
const nodeElement = this.#source.getGraphNode()
const nodeElement = this.#destination.getGraphNode()
nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
nodeElement.removeEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler)
}
this.#destination = graphPin
if (this.#destination) {
const nodeElement = this.#source.getGraphNode()
const nodeElement = this.#destination.getGraphNode()
nodeElement.addEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
nodeElement.addEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler)
this.setDestinationLocation()
}
}
/**
*
* @param {GraphLinkMessage} linkMessage
*/
setLinkMessage(linkMessage) {
this.template.applyLinkMessage(this, linkMessage)
}
}
customElements.define("ueb-link", GraphLink)
customElements.define(GraphLink.tagName, GraphLink)

View File

@@ -0,0 +1,58 @@
import LinkMessageTemplate from "../template/LinkMessageTemplate";
import GraphElement from "./GraphElement";
/**
* @typedef {import("./GraphPin").default} GraphPin
* @typedef {import("./GraphLink").default} GraphLink
* @typedef {(sourcePin: GraphPin, sourcePin: GraphPin) => String} LinkRetrieval
*/
export default class GraphLinkMessage extends GraphElement {
static tagName = "ueb-link-message"
static CONVERT_TYPE = _ => new GraphLinkMessage(
"ueb-icon-conver-type",
/** @type {LinkRetrieval} */
(s, d) => `Convert ${s.getType()} to ${d.getType()}.`
)
static DIRECTIONS_INCOMPATIBLE = _ => new GraphLinkMessage(
"ueb-icon-directions-incompatible",
/** @type {LinkRetrieval} */
(s, d) => "Directions are not compatbile."
)
static PLACE_NODE = _ => new GraphLinkMessage(
"ueb-icon-place-node",
/** @type {LinkRetrieval} */
(s, d) => "Place a new node."
)
static REPLACE_LiNK = _ => new GraphLinkMessage(
"ueb-icon-replace-link",
/** @type {LinkRetrieval} */
(s, d) => "Replace existing input connections."
)
static SAME_NODE = _ => new GraphLinkMessage(
"ueb-icon-same-node",
/** @type {LinkRetrieval} */
(s, d) => "Both are on the same node."
)
static TYPES_INCOMPATIBLE = _ => new GraphLinkMessage(
"ueb-icon-types-incompatible",
/** @type {LinkRetrieval} */
(s, d) => `${s.getType()} is not compatible with ${d.getType()}.`
)
/** @type {String} */
icon
/** @type {String} */
message
/** @type {GraphLink} */
linkElement
constructor(icon, message) {
super({}, new LinkMessageTemplate())
this.icon = icon
this.message = message
}
}
customElements.define(GraphLinkMessage.tagName, GraphLinkMessage)

View File

@@ -7,6 +7,8 @@ import Configuration from "../Configuration"
export default class GraphNode extends SelectableDraggable {
static tagName = "ueb-node"
/**
*
* @param {ObjectEntity} entity
@@ -50,12 +52,12 @@ export default class GraphNode extends SelectableDraggable {
}
dispatchDeleteEvent(value) {
let dragEvent = new CustomEvent(Configuration.nodeDragEventName, {
let deleteEvent = new CustomEvent(Configuration.nodeDeleteEventName, {
bubbles: true,
cancelable: true,
})
this.dispatchEvent(dragEvent)
this.dispatchEvent(deleteEvent)
}
}
customElements.define("ueb-node", GraphNode)
customElements.define(GraphNode.tagName, GraphNode)

View File

@@ -4,6 +4,8 @@ import MouseCreateLink from "../input/mouse/MouseCreateLink"
export default class GraphPin extends GraphElement {
static tagName = "ueb-pin"
constructor(entity) {
super(entity, new PinTemplate())
/** @type {import("../entity/PinEntity").default} */
@@ -51,6 +53,10 @@ export default class GraphPin extends GraphElement {
return this.entity.getType()
}
getClickableElement() {
return this.clickableElement
}
/**
* Returns The exact location where the link originates from or arrives at.
* @returns {Number[]} The location array
@@ -64,4 +70,4 @@ export default class GraphPin extends GraphElement {
}
}
customElements.define("ueb-pin", GraphPin)
customElements.define(GraphPin.tagName, GraphPin)

View File

@@ -4,6 +4,8 @@ import SelectorTemplate from "../template/SelectorTemplate"
export default class GraphSelector extends GraphElement {
static tagName = "ueb-selector"
constructor() {
super({}, new SelectorTemplate())
this.selectionModel = null
@@ -35,4 +37,4 @@ export default class GraphSelector extends GraphElement {
}
}
customElements.define("ueb-selector", GraphSelector)
customElements.define(GraphSelector.tagName, GraphSelector)

View File

@@ -21,7 +21,6 @@ export default class Context {
this.blueprint.removeEventListener("blueprint-unfocus", this.blueprintUnfocusHandler)
}
/* Subclasses will probabily override the following methods */
listenEvents() {
}

View File

@@ -1,7 +1,6 @@
import KeyboardShortcut from "./KeyboardShortcut"
import Configuration from "../../Configuration"
export default class KeyvoardCanc extends KeyboardShortcut {
/**

View File

@@ -1,7 +1,6 @@
import KeyboardShortcut from "./KeyboardShortcut"
import Configuration from "../../Configuration"
export default class KeyboardSelectAll extends KeyboardShortcut {
/**

View File

@@ -1,8 +1,13 @@
import GraphLink from "../../graph/GraphLink"
import GraphLinkMessage from "../../graph/GraphLinkMessage"
import MouseClickDrag from "./MouseClickDrag"
/** @typedef {import("../../graph/GraphPin").default} GraphPin */
export default class MouseCreateLink extends MouseClickDrag {
/** @type {NodeListOf<GraphPin>} */
#listenedPins
/** @type {(e: MouseEvent) => void} */
#mouseenterHandler
@@ -33,12 +38,15 @@ export default class MouseCreateLink extends MouseClickDrag {
startDrag() {
this.link = new GraphLink(this.target, null)
this.link.setLinkMessage(GraphLinkMessage.PLACE_NODE())
this.blueprint.nodesContainerElement.insertBefore(this.link, this.blueprint.selectorElement.nextElementSibling)
this.blueprint.querySelectorAll("ueb-pin." + this.target.isInput() ? "output" : "input")
.forEach(pin => {
pin.addEventListener("mouseenter", this.#mouseenterHandler)
pin.addEventListener("mouseleave", this.#mouseleaveHandler)
})
this.#listenedPins = this.blueprint.querySelectorAll(this.target.constructor.tagName)
this.#listenedPins.forEach(pin => {
if (pin != this.target) {
pin.getClickableElement().addEventListener("mouseenter", this.#mouseenterHandler)
pin.getClickableElement().addEventListener("mouseleave", this.#mouseleaveHandler)
}
})
}
dragTo(location, movement) {
@@ -46,15 +54,19 @@ export default class MouseCreateLink extends MouseClickDrag {
}
endDrag() {
this.blueprint.querySelectorAll("ueb-pin." + this.target.isInput() ? "output" : "input")
.forEach(pin => {
pin.removeEventListener("mouseenter", this.#mouseenterHandler)
pin.removeEventListener("mouseleave", this.#mouseleaveHandler)
})
if (this.enteredPin) {
this.link.setDestinationPin(this.link)
this.#listenedPins.forEach(pin => {
pin.removeEventListener("mouseenter", this.#mouseenterHandler)
pin.removeEventListener("mouseleave", this.#mouseleaveHandler)
})
if (this.enteredPin && !this.blueprint.getLinks().find(
link =>
link.getSourcePin() == this.target && link.getDestinationPin() == this.enteredPin
|| link.getSourcePin() == this.enteredPin && link.getDestinationPin() == this.target
)) {
this.link.setDestinationPin(this.enteredPin)
this.blueprint.addGraphElement(this.link)
} else {
// this.link.remove()
this.link.remove()
}
this.link = null
}

View File

@@ -140,7 +140,6 @@ export default class FastSelectionModel {
this.initialPosition[0] < this.boundaries.primaryP.v && this.initialPosition[0] < finalPosition[0])
}
const secondaryBoundaryCrossed = (index, added) => {
this.selectFunc(this.rectangles[index], added)
this.computeBoundaries(finalPosition)

View File

@@ -78,7 +78,7 @@ export default class BlueprintTemplate extends Template {
blueprint.gridElement = blueprint.viewportElement.querySelector(".ueb-grid")
blueprint.nodesContainerElement = blueprint.querySelector("[data-nodes]")
blueprint.selectorElement = new GraphSelector()
blueprint.nodesContainerElement.append(blueprint.selectorElement, ...blueprint.nodes)
blueprint.nodesContainerElement.append(blueprint.selectorElement, ...blueprint.getNodes())
this.applyEndDragScrolling(blueprint)
}

View File

@@ -0,0 +1,35 @@
import GraphLink from "../graph/GraphLink"
import html from "./html"
import Template from "./Template"
/**
* @typedef {import("../graph/GraphLinkMessage").default} GraphLinkMessage
*/
export default class LinkMessageTemplate extends Template {
/**
* Computes the html content of the target element.
* @param {GraphLinkMessage} linkMessage attached to link destination
* @returns The result html
*/
render(linkMessage) {
return html`
<span class="${linkMessage.icon}"></span>
<span class="ueb-link-message"></span>
`
}
/**
* Applies the style to the element.
* @param {GraphLinkMessage} linkMessage element
*/
apply(linkMessage) {
super.apply(linkMessage)
linkMessage.linkElement = linkMessage.closest(GraphLink.tagName)
linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message(
linkMessage.linkElement.getSourcePin(),
linkMessage.linkElement.getDestinationPin()
)
}
}

View File

@@ -2,10 +2,10 @@ import html from "./html"
import sanitizeText from "./sanitizeText"
import Template from "./Template"
import Configuration from "../Configuration"
import Utility from "../Utility"
/**
* @typedef {import("../graph/GraphLink").default} GraphLink
* @typedef {import("../graph/GraphLinkMessage").default} GraphLinkMessage
*/
export default class LinkTemplate extends Template {
@@ -19,7 +19,7 @@ export default class LinkTemplate extends Template {
/**
* Computes the html content of the target element.
* @param {GraphLink} link Link connecting two graph nodes
* @param {GraphLink} link connecting two graph nodes
* @returns The result html
*/
render(link) {
@@ -38,6 +38,9 @@ export default class LinkTemplate extends Template {
super.apply(link)
link.classList.add("ueb-positioned")
link.pathElement = link.querySelector("path")
if (link.linkMessageElement) {
link.appendChild(link.linkMessageElement)
}
}
/**
@@ -74,11 +77,12 @@ export default class LinkTemplate extends Template {
link.style.setProperty("margin-left", `-${start}px`)
}
if (xInverted) {
start = start + fillRatio * 100
start += fillRatio * 100
}
link.style.setProperty("--ueb-start-percentage", `${100 - start}%`)
const c1 = start + 15 * fillRatio
let c2 = Math.max(40 / aspectRatio, 30) + start
const c2Decreasing = -0.05
const c2Decreasing = -0.06
const getMaxC2 = (m, p) => {
const a = -m * p[0] * p[0]
const q = p[1] - a / p[0]
@@ -90,4 +94,15 @@ export default class LinkTemplate extends Template {
// TODO move to CSS when Firefox will support property d
link.pathElement.setAttribute("d", d)
}
/**
*
* @param {GraphLink} link element
* @param {GraphLinkMessage} linkMessage
*/
applyLinkMessage(link, linkMessage) {
link.querySelectorAll(linkMessage.constructor.tagName).forEach(element => element.remove())
link.appendChild(linkMessage)
link.linkMessageElement = linkMessage
}
}

View File

@@ -9,29 +9,6 @@ import SelectableDraggableTemplate from "./SelectableDraggableTemplate"
*/
export default class NodeTemplate extends SelectableDraggableTemplate {
/**
* Computes the html content of the target element.
* @param {GraphNode} node Graph node element
* @returns The result html
*/
header(node) {
return html`
`
}
/**
* Computes the html content of the target element.
* @param {GraphNode} node Graph node element
* @returns The result html
*/
body(node) {
let inputs = node.entity.CustomProperties.filter(v => v instanceof PinEntity)
let outputs = inputs.filter(v => v.isOutput())
inputs = inputs.filter(v => v.isInput())
return html`
`
}
/**
* Computes the html content of the target element.
* @param {GraphNode} node Graph node element

View File

@@ -1,6 +1,8 @@
.ueb {
$ueb-node-value-color : white;
--ueb-node-value-color: #{$ueb-node-value-color};
$ueb-node-value-color : white;
$ueb-node-value-dim-color : #afafaf;
--ueb-node-value-color : #{$ueb-node-value-color};
--ueb-node-value-dim-color: #{$ueb-node-value-dim-color};
}
.ueb-node-value-boolean {

View File

@@ -462,4 +462,17 @@ ueb-link svg path {
ueb-link svg path:hover {
stroke-width: 5;
}
ueb-link-message {
display : block;
position : absolute;
left : calc(var(--ueb-start-percentage) + 15px);
bottom : -42px;
border : 1px solid #000;
padding : 4px 8px;
border-radius: 2px;
background : linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);
color : var(--ueb-node-value-dim-color);
white-space : nowrap;
}