mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-03 23:55:04 +08:00
Links improved, refactoring
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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"}
|
||||
2
dist/css/ueblueprint-style.css
vendored
2
dist/css/ueblueprint-style.css
vendored
File diff suppressed because one or more lines are too long
2
dist/css/ueblueprint-style.css.map
vendored
2
dist/css/ueblueprint-style.css.map
vendored
@@ -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
408
dist/ueblueprint.js
vendored
@@ -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 {
|
||||
|
||||
|
||||
171
js/Blueprint.js
171
js/Blueprint.js
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
58
js/graph/GraphLinkMessage.js
Normal file
58
js/graph/GraphLinkMessage.js
Normal 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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -21,7 +21,6 @@ export default class Context {
|
||||
this.blueprint.removeEventListener("blueprint-unfocus", this.blueprintUnfocusHandler)
|
||||
}
|
||||
|
||||
|
||||
/* Subclasses will probabily override the following methods */
|
||||
listenEvents() {
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import KeyboardShortcut from "./KeyboardShortcut"
|
||||
import Configuration from "../../Configuration"
|
||||
|
||||
|
||||
export default class KeyvoardCanc extends KeyboardShortcut {
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import KeyboardShortcut from "./KeyboardShortcut"
|
||||
import Configuration from "../../Configuration"
|
||||
|
||||
|
||||
export default class KeyboardSelectAll extends KeyboardShortcut {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
35
js/template/LinkMessageTemplate.js
Normal file
35
js/template/LinkMessageTemplate.js
Normal 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()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user