Node replace bug fix, names simplify

This commit is contained in:
barsdeveloper
2023-04-23 10:26:48 +02:00
parent 8a96af670e
commit 3ccd3ce9f3
15 changed files with 172 additions and 192 deletions

View File

@@ -6,7 +6,10 @@ module.exports = defineConfig({
// implement node event listeners here // implement node event listeners here
}, },
experimentalRunAllSpecs: true, experimentalRunAllSpecs: true,
scrollBehavior: false,
testIsolation: false, testIsolation: false,
video: false, video: false,
viewportHeight: 1000,
viewportWidth: 1000,
}, },
}) })

View File

@@ -490,7 +490,7 @@ before(() => {
cy.visit(`http://127.0.0.1:${Cypress.env("UEBLUEPRINT_TEST_SERVER_PORT")}/empty.html`, { cy.visit(`http://127.0.0.1:${Cypress.env("UEBLUEPRINT_TEST_SERVER_PORT")}/empty.html`, {
onLoad: () => { onLoad: () => {
cy.get("ueb-blueprint") cy.get("ueb-blueprint")
.then(b => blueprint = b[0]) .then(([b]) => blueprint = b)
.click(100, 300) .click(100, 300)
} }
}) })

172
dist/ueblueprint.js vendored
View File

@@ -96,6 +96,8 @@ class Configuration {
} }
static maxZoom = 7 static maxZoom = 7
static minZoom = -12 static minZoom = -12
static mouseClickButton = 0
static mouseRightClickButton = 2
static mouseWheelFactor = 0.2 static mouseWheelFactor = 0.2
static nodeDragGeneralEventName = "ueb-node-drag-general" static nodeDragGeneralEventName = "ueb-node-drag-general"
static nodeDragEventName = "ueb-node-drag" static nodeDragEventName = "ueb-node-drag"
@@ -495,13 +497,6 @@ class Utility {
} }
} }
static arrayConverter = {
/** @param {String} value */
fromAttribute: (value, type) => value.split(/(?<!\\),/).map(v => v.trim()),
/** @param {String[]} value */
toAttribute: (value, type) => value.join(","),
}
/** @param {Number} x */ /** @param {Number} x */
static sigmoid(x, curvature = 1.7) { static sigmoid(x, curvature = 1.7) {
return 1 / (1 + (x / (1 - x) ** -curvature)) return 1 / (1 + (x / (1 - x) ** -curvature))
@@ -5251,7 +5246,7 @@ class IMouseClickDrag extends IPointing {
* @param {Object} options * @param {Object} options
*/ */
constructor(target, blueprint, options = {}) { constructor(target, blueprint, options = {}) {
options.clickButton ??= 0; options.clickButton ??= Configuration.mouseClickButton;
options.consumeEvent ??= true; options.consumeEvent ??= true;
options.draggableElement ??= target; options.draggableElement ??= target;
options.exitAnyButton ??= true; options.exitAnyButton ??= true;
@@ -5271,7 +5266,7 @@ class IMouseClickDrag extends IPointing {
listenEvents() { listenEvents() {
super.listenEvents(); super.listenEvents();
this.#draggableElement.addEventListener("mousedown", this.#mouseDownHandler); this.#draggableElement.addEventListener("mousedown", this.#mouseDownHandler);
if (this.options.clickButton == 2) { if (this.options.clickButton === Configuration.mouseRightClickButton) {
this.#draggableElement.addEventListener("contextmenu", e => e.preventDefault()); this.#draggableElement.addEventListener("contextmenu", e => e.preventDefault());
} }
} }
@@ -5601,12 +5596,12 @@ class BlueprintTemplate extends ITemplate {
new KeyboardSelectAll(this.element.getGridDOMElement(), this.element), new KeyboardSelectAll(this.element.getGridDOMElement(), this.element),
new Zoom(this.element.getGridDOMElement(), this.element), new Zoom(this.element.getGridDOMElement(), this.element),
new Select(this.element.getGridDOMElement(), this.element, { new Select(this.element.getGridDOMElement(), this.element, {
clickButton: 0, clickButton: Configuration.mouseClickButton,
exitAnyButton: true, exitAnyButton: true,
moveEverywhere: true, moveEverywhere: true,
}), }),
new MouseScrollGraph(this.element.getGridDOMElement(), this.element, { new MouseScrollGraph(this.element.getGridDOMElement(), this.element, {
clickButton: 2, clickButton: Configuration.mouseRightClickButton,
exitAnyButton: false, exitAnyButton: false,
moveEverywhere: true, moveEverywhere: true,
}), }),
@@ -6003,7 +5998,7 @@ class LinkTemplate extends IFromToPositionedTemplate {
/** @param {[Number, Number]} location */ /** @param {[Number, Number]} location */
#createKnot = location => { #createKnot = location => {
const knotEntity = new KnotEntity({}, this.element.sourcePin.entity); const knotEntity = new KnotEntity({}, this.element.source.entity);
const knot = /** @type {NodeElementConstructor} */(ElementFactory.getConstructor("ueb-node")) const knot = /** @type {NodeElementConstructor} */(ElementFactory.getConstructor("ueb-node"))
.newObject(knotEntity); .newObject(knotEntity);
knot.setLocation(...this.blueprint.snapToGrid(...location)); knot.setLocation(...this.blueprint.snapToGrid(...location));
@@ -6011,13 +6006,13 @@ class LinkTemplate extends IFromToPositionedTemplate {
this.blueprint.addGraphElement(knot); // Important: keep it before changing existing links this.blueprint.addGraphElement(knot); // Important: keep it before changing existing links
const inputPin = this.element.getInputPin(); const inputPin = this.element.getInputPin();
const outputPin = this.element.getOutputPin(); const outputPin = this.element.getOutputPin();
this.element.sourcePin = null; this.element.source = null;
this.element.destinationPin = null; this.element.destination = null;
const link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link")) const link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link"))
.newObject(outputPin, knotTemplate.inputPin); .newObject(outputPin, knotTemplate.inputPin);
this.blueprint.addGraphElement(link); this.blueprint.addGraphElement(link);
this.element.sourcePin = knotTemplate.outputPin; this.element.source = knotTemplate.outputPin;
this.element.destinationPin = inputPin; this.element.destination = inputPin;
} }
createInputObjects() { createInputObjects() {
@@ -6040,8 +6035,8 @@ class LinkTemplate extends IFromToPositionedTemplate {
/** @param {PropertyValues} changedProperties */ /** @param {PropertyValues} changedProperties */
willUpdate(changedProperties) { willUpdate(changedProperties) {
super.willUpdate(changedProperties); super.willUpdate(changedProperties);
const sourcePin = this.element.sourcePin; const sourcePin = this.element.source;
const destinationPin = this.element.destinationPin; const destinationPin = this.element.destination;
if (changedProperties.has("fromX") || changedProperties.has("toX")) { if (changedProperties.has("fromX") || changedProperties.has("toX")) {
const from = this.element.fromX; const from = this.element.fromX;
const to = this.element.toX; const to = this.element.toX;
@@ -6049,17 +6044,17 @@ class LinkTemplate extends IFromToPositionedTemplate {
const isDestinationAKnot = destinationPin?.nodeElement.getType() == Configuration.nodeType.knot; const isDestinationAKnot = destinationPin?.nodeElement.getType() == Configuration.nodeType.knot;
if (isSourceAKnot && (!destinationPin || isDestinationAKnot)) { if (isSourceAKnot && (!destinationPin || isDestinationAKnot)) {
if (sourcePin?.isInput() && to > from + Configuration.distanceThreshold) { if (sourcePin?.isInput() && to > from + Configuration.distanceThreshold) {
this.element.sourcePin = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).outputPin; this.element.source = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).outputPin;
} else if (sourcePin?.isOutput() && to < from - Configuration.distanceThreshold) { } else if (sourcePin?.isOutput() && to < from - Configuration.distanceThreshold) {
this.element.sourcePin = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).inputPin; this.element.source = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).inputPin;
} }
} }
if (isDestinationAKnot && (!sourcePin || isSourceAKnot)) { if (isDestinationAKnot && (!sourcePin || isSourceAKnot)) {
if (destinationPin?.isInput() && to < from - Configuration.distanceThreshold) { if (destinationPin?.isInput() && to < from - Configuration.distanceThreshold) {
this.element.destinationPin = this.element.destination =
/** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).outputPin; /** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).outputPin;
} else if (destinationPin?.isOutput() && to > from + Configuration.distanceThreshold) { } else if (destinationPin?.isOutput() && to > from + Configuration.distanceThreshold) {
this.element.destinationPin = this.element.destination =
/** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).inputPin; /** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).inputPin;
} }
} }
@@ -6097,7 +6092,7 @@ class LinkTemplate extends IFromToPositionedTemplate {
if (changedProperties.has("originatesFromInput")) { if (changedProperties.has("originatesFromInput")) {
this.element.style.setProperty("--ueb-from-input", this.element.originatesFromInput ? "1" : "0"); this.element.style.setProperty("--ueb-from-input", this.element.originatesFromInput ? "1" : "0");
} }
const referencePin = this.element.sourcePin ?? this.element.destinationPin; const referencePin = this.element.source ?? this.element.destination;
if (referencePin) { if (referencePin) {
this.element.style.setProperty("--ueb-link-color-rgb", Utility.printLinearColor(referencePin.color)); this.element.style.setProperty("--ueb-link-color-rgb", Utility.printLinearColor(referencePin.color));
} }
@@ -6140,14 +6135,6 @@ class LinkElement extends IFromToPositionedElement {
static properties = { static properties = {
...super.properties, ...super.properties,
source: {
type: String,
reflect: true,
},
destination: {
type: String,
reflect: true,
},
dragging: { dragging: {
type: Boolean, type: Boolean,
attribute: "data-dragging", attribute: "data-dragging",
@@ -6173,20 +6160,20 @@ class LinkElement extends IFromToPositionedElement {
} }
/** @type {PinElement} */ /** @type {PinElement} */
#sourcePin #source
get sourcePin() { get source() {
return this.#sourcePin return this.#source
} }
set sourcePin(pin) { set source(pin) {
this.#setPin(pin, false); this.#setPin(pin, false);
} }
/** @type {PinElement} */ /** @type {PinElement} */
#destinationPin #destination
get destinationPin() { get destination() {
return this.#destinationPin return this.#destination
} }
set destinationPin(pin) { set destination(pin) {
this.#setPin(pin, true); this.#setPin(pin, true);
} }
@@ -6208,8 +6195,6 @@ class LinkElement extends IFromToPositionedElement {
constructor() { constructor() {
super(); super();
this.source = null;
this.destination = null;
this.dragging = false; this.dragging = false;
this.originatesFromInput = false; this.originatesFromInput = false;
this.startPercentage = 0; this.startPercentage = 0;
@@ -6234,14 +6219,14 @@ class LinkElement extends IFromToPositionedElement {
initialize(source, destination) { initialize(source, destination) {
super.initialize({}, new LinkTemplate()); super.initialize({}, new LinkTemplate());
if (source) { if (source) {
this.sourcePin = source; this.source = source;
if (!destination) { if (!destination) {
this.toX = this.fromX; this.toX = this.fromX;
this.toY = this.fromY; this.toY = this.fromY;
} }
} }
if (destination) { if (destination) {
this.destinationPin = destination; this.destination = destination;
if (!source) { if (!source) {
this.fromX = this.toX; this.fromX = this.toX;
this.fromY = this.toY; this.fromY = this.toY;
@@ -6254,7 +6239,7 @@ class LinkElement extends IFromToPositionedElement {
* @param {Boolean} isDestinationPin * @param {Boolean} isDestinationPin
*/ */
#setPin(pin, isDestinationPin) { #setPin(pin, isDestinationPin) {
const getCurrentPin = () => isDestinationPin ? this.destinationPin : this.sourcePin; const getCurrentPin = () => isDestinationPin ? this.destination : this.source;
if (getCurrentPin() == pin) { if (getCurrentPin() == pin) {
return return
} }
@@ -6272,8 +6257,8 @@ class LinkElement extends IFromToPositionedElement {
this.#unlinkPins(); this.#unlinkPins();
} }
isDestinationPin isDestinationPin
? this.#destinationPin = pin ? this.#destination = pin
: this.#sourcePin = pin; : this.#source = pin;
if (getCurrentPin()) { if (getCurrentPin()) {
const nodeElement = getCurrentPin().getNodeElement(); const nodeElement = getCurrentPin().getNodeElement();
nodeElement.addEventListener(Configuration.removeEventName, this.#nodeDeleteHandler); nodeElement.addEventListener(Configuration.removeEventName, this.#nodeDeleteHandler);
@@ -6287,42 +6272,42 @@ class LinkElement extends IFromToPositionedElement {
); );
isDestinationPin isDestinationPin
? this.setDestinationLocation() ? this.setDestinationLocation()
: (this.setSourceLocation(), this.originatesFromInput = this.sourcePin.isInput()); : (this.setSourceLocation(), this.originatesFromInput = this.source.isInput());
this.#linkPins(); this.#linkPins();
} }
} }
#linkPins() { #linkPins() {
if (this.sourcePin && this.destinationPin) { if (this.source && this.destination) {
this.sourcePin.linkTo(this.destinationPin); this.source.linkTo(this.destination);
this.destinationPin.linkTo(this.sourcePin); this.destination.linkTo(this.source);
} }
} }
#unlinkPins() { #unlinkPins() {
if (this.sourcePin && this.destinationPin) { if (this.source && this.destination) {
this.sourcePin.unlinkFrom(this.destinationPin, false); this.source.unlinkFrom(this.destination, false);
this.destinationPin.unlinkFrom(this.sourcePin, false); this.destination.unlinkFrom(this.source, false);
} }
} }
cleanup() { cleanup() {
super.cleanup(); super.cleanup();
this.#unlinkPins(); this.#unlinkPins();
this.sourcePin = null; this.source = null;
this.destinationPin = null; this.destination = null;
} }
/** @param {Number[]?} location */ /** @param {Number[]?} location */
setSourceLocation(location = null, canPostpone = true) { setSourceLocation(location = null, canPostpone = true) {
if (location == null) { if (location == null) {
const self = this; const self = this;
if (canPostpone && (!this.hasUpdated || !this.sourcePin.hasUpdated)) { if (canPostpone && (!this.hasUpdated || !this.source.hasUpdated)) {
Promise.all([this.updateComplete, this.sourcePin.updateComplete]) Promise.all([this.updateComplete, this.source.updateComplete])
.then(() => self.setSourceLocation(null, false)); .then(() => self.setSourceLocation(null, false));
return return
} }
location = this.sourcePin.template.getLinkLocation(); location = this.source.template.getLinkLocation();
} }
const [x, y] = location; const [x, y] = location;
this.fromX = x; this.fromX = x;
@@ -6333,45 +6318,45 @@ class LinkElement extends IFromToPositionedElement {
setDestinationLocation(location = null, canPostpone = true) { setDestinationLocation(location = null, canPostpone = true) {
if (location == null) { if (location == null) {
const self = this; const self = this;
if (canPostpone && (!this.hasUpdated || !this.destinationPin.hasUpdated)) { if (canPostpone && (!this.hasUpdated || !this.destination.hasUpdated)) {
Promise.all([this.updateComplete, this.destinationPin.updateComplete]) Promise.all([this.updateComplete, this.destination.updateComplete])
.then(() => self.setDestinationLocation(null, false)); .then(() => self.setDestinationLocation(null, false));
return return
} }
location = this.destinationPin.template.getLinkLocation(); location = this.destination.template.getLinkLocation();
} }
this.toX = location[0]; this.toX = location[0];
this.toY = location[1]; this.toY = location[1];
} }
getInputPin() { getInputPin() {
if (this.sourcePin?.isInput()) { if (this.source?.isInput()) {
return this.sourcePin return this.source
} }
return this.destinationPin return this.destination
} }
/** @param {PinElement} pin */ /** @param {PinElement} pin */
setInputPin(pin) { setInputPin(pin) {
if (this.sourcePin?.isInput()) { if (this.source?.isInput()) {
this.sourcePin = pin; this.source = pin;
} }
this.destinationPin = pin; this.destination = pin;
} }
getOutputPin() { getOutputPin() {
if (this.destinationPin?.isOutput()) { if (this.destination?.isOutput()) {
return this.destinationPin return this.destination
} }
return this.sourcePin return this.source
} }
/** @param {PinElement} pin */ /** @param {PinElement} pin */
setOutputPin(pin) { setOutputPin(pin) {
if (this.destinationPin?.isOutput()) { if (this.destination?.isOutput()) {
this.destinationPin = pin; this.destination = pin;
} }
this.sourcePin = pin; this.source = pin;
} }
startDragging() { startDragging() {
@@ -6389,7 +6374,7 @@ class LinkElement extends IFromToPositionedElement {
setMessageConvertType() { setMessageConvertType() {
this.linkMessageIcon = "ueb-icon-conver-type"; this.linkMessageIcon = "ueb-icon-conver-type";
this.linkMessageText = `Convert ${this.sourcePin.pinType} to ${this.destinationPin.pinType}.`; this.linkMessageText = `Convert ${this.source.pinType} to ${this.destination.pinType}.`;
} }
setMessageCorrect() { setMessageCorrect() {
@@ -6429,7 +6414,7 @@ class LinkElement extends IFromToPositionedElement {
setMEssagetypesIncompatible() { setMEssagetypesIncompatible() {
this.linkMessageIcon = SVGIcon.reject; this.linkMessageIcon = SVGIcon.reject;
this.linkMessageText = x`${this.sourcePin.pinType} is not compatible with ${this.destinationPin.pinType}.`; this.linkMessageText = x`${this.source.pinType} is not compatible with ${this.destination.pinType}.`;
} }
} }
@@ -7187,7 +7172,7 @@ class MouseCreateLink extends IMouseClickDrag {
if (!this.enteredPin) { if (!this.enteredPin) {
this.linkValid = false; this.linkValid = false;
this.enteredPin = /** @type {PinElement} */(e.target); this.enteredPin = /** @type {PinElement} */(e.target);
const a = this.link.sourcePin ?? this.target; // Remember target might have change const a = this.link.source ?? this.target; // Remember target might have change
const b = this.enteredPin; const b = this.enteredPin;
const outputPin = a.isOutput() ? a : b; const outputPin = a.isOutput() ? a : b;
if ( if (
@@ -7270,14 +7255,15 @@ class MouseCreateLink extends IMouseClickDrag {
pin.removeEventListener("mouseenter", this.#mouseenterHandler); pin.removeEventListener("mouseenter", this.#mouseenterHandler);
pin.removeEventListener("mouseleave", this.#mouseleaveHandler); pin.removeEventListener("mouseleave", this.#mouseleaveHandler);
}); });
this.#listenedPins = null;
if (this.enteredPin && this.linkValid) { if (this.enteredPin && this.linkValid) {
if (this.#knotPin) { if (this.#knotPin) {
const otherPin = this.#knotPin !== this.link.sourcePin ? this.link.sourcePin : this.enteredPin; const otherPin = this.#knotPin !== this.link.source ? this.link.source : this.enteredPin;
// Knot pin direction correction // Knot pin direction correction
if (this.#knotPin.isInput() && otherPin.isInput() || this.#knotPin.isOutput() && otherPin.isOutput()) { if (this.#knotPin.isInput() && otherPin.isInput() || this.#knotPin.isOutput() && otherPin.isOutput()) {
const oppositePin = /** @type {KnotPinTemplate} */(this.#knotPin.template).getOppositePin(); const oppositePin = /** @type {KnotPinTemplate} */(this.#knotPin.template).getOppositePin();
if (this.#knotPin === this.link.sourcePin) { if (this.#knotPin === this.link.source) {
this.link.sourcePin = oppositePin; this.link.source = oppositePin;
} else { } else {
this.enteredPin = oppositePin; this.enteredPin = oppositePin;
} }
@@ -7285,17 +7271,19 @@ class MouseCreateLink extends IMouseClickDrag {
} else if (this.enteredPin.nodeElement.getType() === Configuration.nodeType.knot) { } else if (this.enteredPin.nodeElement.getType() === Configuration.nodeType.knot) {
this.enteredPin = /** @type {KnotPinTemplate} */(this.enteredPin.template).getOppositePin(); this.enteredPin = /** @type {KnotPinTemplate} */(this.enteredPin.template).getOppositePin();
} }
this.blueprint.addGraphElement(this.link); if (!this.link.source.getLinks().find(ref => ref.equals(this.enteredPin.createPinReference()))) {
this.link.destinationPin = this.enteredPin; this.blueprint.addGraphElement(this.link);
this.link.removeMessage(); this.link.destination = this.enteredPin;
this.link.finishDragging(); } else {
this.link.remove();
}
} else { } else {
this.link.finishDragging();
this.link.remove(); this.link.remove();
} }
this.enteredPin = null; this.enteredPin = null;
this.link.removeMessage();
this.link.finishDragging();
this.link = null; this.link = null;
this.#listenedPins = null;
} }
} }
@@ -8215,7 +8203,7 @@ class Blueprint extends IElement {
} }
} }
scrollCenter() { scrollCenter(smooth = false) {
const scroll = this.getScroll(); const scroll = this.getScroll();
const offset = [ const offset = [
this.translateX - scroll[0], this.translateX - scroll[0],
@@ -8226,7 +8214,7 @@ class Blueprint extends IElement {
offset[0] - targetOffset[0], offset[0] - targetOffset[0],
offset[1] - targetOffset[1] offset[1] - targetOffset[1]
]; ];
this.scrollDelta(deltaOffset[0], deltaOffset[1], true); this.scrollDelta(deltaOffset[0], deltaOffset[1], smooth);
} }
getViewportSize() { getViewportSize() {
@@ -8388,12 +8376,12 @@ class Blueprint extends IElement {
getLinks(a = null, b = null) { getLinks(a = null, b = null) {
if ((a == null) != (b == null)) { if ((a == null) != (b == null)) {
const pin = a ?? b; const pin = a ?? b;
return this.links.filter(link => link.sourcePin == pin || link.destinationPin == pin) return this.links.filter(link => link.source == pin || link.destination == pin)
} }
if (a != null && b != null) { if (a != null && b != null) {
return this.links.filter(link => return this.links.filter(link =>
link.sourcePin == a && link.destinationPin == b link.source == a && link.destination == b
|| link.sourcePin == b && link.destinationPin == a || link.source == b && link.destination == a
) )
} }
return this.links return this.links
@@ -8405,8 +8393,8 @@ class Blueprint extends IElement {
*/ */
getLink(sourcePin, destinationPin, strictDirection = false) { getLink(sourcePin, destinationPin, strictDirection = false) {
return this.links.find(link => return this.links.find(link =>
link.sourcePin == sourcePin && link.destinationPin == destinationPin link.source == sourcePin && link.destination == destinationPin
|| !strictDirection && link.sourcePin == destinationPin && link.destinationPin == sourcePin || !strictDirection && link.source == destinationPin && link.destination == sourcePin
) )
} }

File diff suppressed because one or more lines are too long

View File

@@ -166,7 +166,7 @@ export default class Blueprint extends IElement {
} }
} }
scrollCenter() { scrollCenter(smooth = false) {
const scroll = this.getScroll() const scroll = this.getScroll()
const offset = [ const offset = [
this.translateX - scroll[0], this.translateX - scroll[0],
@@ -177,7 +177,7 @@ export default class Blueprint extends IElement {
offset[0] - targetOffset[0], offset[0] - targetOffset[0],
offset[1] - targetOffset[1] offset[1] - targetOffset[1]
] ]
this.scrollDelta(deltaOffset[0], deltaOffset[1], true) this.scrollDelta(deltaOffset[0], deltaOffset[1], smooth)
} }
getViewportSize() { getViewportSize() {
@@ -339,12 +339,12 @@ export default class Blueprint extends IElement {
getLinks(a = null, b = null) { getLinks(a = null, b = null) {
if ((a == null) != (b == null)) { if ((a == null) != (b == null)) {
const pin = a ?? b const pin = a ?? b
return this.links.filter(link => link.sourcePin == pin || link.destinationPin == pin) return this.links.filter(link => link.source == pin || link.destination == pin)
} }
if (a != null && b != null) { if (a != null && b != null) {
return this.links.filter(link => return this.links.filter(link =>
link.sourcePin == a && link.destinationPin == b link.source == a && link.destination == b
|| link.sourcePin == b && link.destinationPin == a || link.source == b && link.destination == a
) )
} }
return this.links return this.links
@@ -356,8 +356,8 @@ export default class Blueprint extends IElement {
*/ */
getLink(sourcePin, destinationPin, strictDirection = false) { getLink(sourcePin, destinationPin, strictDirection = false) {
return this.links.find(link => return this.links.find(link =>
link.sourcePin == sourcePin && link.destinationPin == destinationPin link.source == sourcePin && link.destination == destinationPin
|| !strictDirection && link.sourcePin == destinationPin && link.destinationPin == sourcePin || !strictDirection && link.source == destinationPin && link.destination == sourcePin
) )
} }

View File

@@ -72,6 +72,8 @@ export default class Configuration {
} }
static maxZoom = 7 static maxZoom = 7
static minZoom = -12 static minZoom = -12
static mouseClickButton = 0
static mouseRightClickButton = 2
static mouseWheelFactor = 0.2 static mouseWheelFactor = 0.2
static nodeDragGeneralEventName = "ueb-node-drag-general" static nodeDragGeneralEventName = "ueb-node-drag-general"
static nodeDragEventName = "ueb-node-drag" static nodeDragEventName = "ueb-node-drag"

View File

@@ -31,13 +31,6 @@ export default class Utility {
} }
} }
static arrayConverter = {
/** @param {String} value */
fromAttribute: (value, type) => value.split(/(?<!\\),/).map(v => v.trim()),
/** @param {String[]} value */
toAttribute: (value, type) => value.join(","),
}
/** @param {Number} x */ /** @param {Number} x */
static sigmoid(x, curvature = 1.7) { static sigmoid(x, curvature = 1.7) {
return 1 / (1 + (x / (1 - x) ** -curvature)) return 1 / (1 + (x / (1 - x) ** -curvature))

View File

@@ -1,6 +1,6 @@
import Configuration from "../Configuration.js" import Configuration from "../Configuration.js"
import Utility from "../Utility.js"
import IDraggableElement from "./IDraggableElement.js" import IDraggableElement from "./IDraggableElement.js"
import Utility from "../Utility.js"
/** /**
* @typedef {import("../element/IDraggableElement.js").DragEvent} DragEvent * @typedef {import("../element/IDraggableElement.js").DragEvent} DragEvent

View File

@@ -1,6 +1,6 @@
import IElement from "./IElement.js"
import InputTemplate from "../template/pin/InputTemplate.js" import InputTemplate from "../template/pin/InputTemplate.js"
import Utility from "../Utility.js" import Utility from "../Utility.js"
import IElement from "./IElement.js"
export default class InputElement extends IElement { export default class InputElement extends IElement {

View File

@@ -17,14 +17,6 @@ export default class LinkElement extends IFromToPositionedElement {
static properties = { static properties = {
...super.properties, ...super.properties,
source: {
type: String,
reflect: true,
},
destination: {
type: String,
reflect: true,
},
dragging: { dragging: {
type: Boolean, type: Boolean,
attribute: "data-dragging", attribute: "data-dragging",
@@ -50,20 +42,20 @@ export default class LinkElement extends IFromToPositionedElement {
} }
/** @type {PinElement} */ /** @type {PinElement} */
#sourcePin #source
get sourcePin() { get source() {
return this.#sourcePin return this.#source
} }
set sourcePin(pin) { set source(pin) {
this.#setPin(pin, false) this.#setPin(pin, false)
} }
/** @type {PinElement} */ /** @type {PinElement} */
#destinationPin #destination
get destinationPin() { get destination() {
return this.#destinationPin return this.#destination
} }
set destinationPin(pin) { set destination(pin) {
this.#setPin(pin, true) this.#setPin(pin, true)
} }
@@ -85,8 +77,6 @@ export default class LinkElement extends IFromToPositionedElement {
constructor() { constructor() {
super() super()
this.source = null
this.destination = null
this.dragging = false this.dragging = false
this.originatesFromInput = false this.originatesFromInput = false
this.startPercentage = 0 this.startPercentage = 0
@@ -111,14 +101,14 @@ export default class LinkElement extends IFromToPositionedElement {
initialize(source, destination) { initialize(source, destination) {
super.initialize({}, new LinkTemplate()) super.initialize({}, new LinkTemplate())
if (source) { if (source) {
this.sourcePin = source this.source = source
if (!destination) { if (!destination) {
this.toX = this.fromX this.toX = this.fromX
this.toY = this.fromY this.toY = this.fromY
} }
} }
if (destination) { if (destination) {
this.destinationPin = destination this.destination = destination
if (!source) { if (!source) {
this.fromX = this.toX this.fromX = this.toX
this.fromY = this.toY this.fromY = this.toY
@@ -131,7 +121,7 @@ export default class LinkElement extends IFromToPositionedElement {
* @param {Boolean} isDestinationPin * @param {Boolean} isDestinationPin
*/ */
#setPin(pin, isDestinationPin) { #setPin(pin, isDestinationPin) {
const getCurrentPin = () => isDestinationPin ? this.destinationPin : this.sourcePin const getCurrentPin = () => isDestinationPin ? this.destination : this.source
if (getCurrentPin() == pin) { if (getCurrentPin() == pin) {
return return
} }
@@ -149,8 +139,8 @@ export default class LinkElement extends IFromToPositionedElement {
this.#unlinkPins() this.#unlinkPins()
} }
isDestinationPin isDestinationPin
? this.#destinationPin = pin ? this.#destination = pin
: this.#sourcePin = pin : this.#source = pin
if (getCurrentPin()) { if (getCurrentPin()) {
const nodeElement = getCurrentPin().getNodeElement() const nodeElement = getCurrentPin().getNodeElement()
nodeElement.addEventListener(Configuration.removeEventName, this.#nodeDeleteHandler) nodeElement.addEventListener(Configuration.removeEventName, this.#nodeDeleteHandler)
@@ -164,42 +154,42 @@ export default class LinkElement extends IFromToPositionedElement {
) )
isDestinationPin isDestinationPin
? this.setDestinationLocation() ? this.setDestinationLocation()
: (this.setSourceLocation(), this.originatesFromInput = this.sourcePin.isInput()) : (this.setSourceLocation(), this.originatesFromInput = this.source.isInput())
this.#linkPins() this.#linkPins()
} }
} }
#linkPins() { #linkPins() {
if (this.sourcePin && this.destinationPin) { if (this.source && this.destination) {
this.sourcePin.linkTo(this.destinationPin) this.source.linkTo(this.destination)
this.destinationPin.linkTo(this.sourcePin) this.destination.linkTo(this.source)
} }
} }
#unlinkPins() { #unlinkPins() {
if (this.sourcePin && this.destinationPin) { if (this.source && this.destination) {
this.sourcePin.unlinkFrom(this.destinationPin, false) this.source.unlinkFrom(this.destination, false)
this.destinationPin.unlinkFrom(this.sourcePin, false) this.destination.unlinkFrom(this.source, false)
} }
} }
cleanup() { cleanup() {
super.cleanup() super.cleanup()
this.#unlinkPins() this.#unlinkPins()
this.sourcePin = null this.source = null
this.destinationPin = null this.destination = null
} }
/** @param {Number[]?} location */ /** @param {Number[]?} location */
setSourceLocation(location = null, canPostpone = true) { setSourceLocation(location = null, canPostpone = true) {
if (location == null) { if (location == null) {
const self = this const self = this
if (canPostpone && (!this.hasUpdated || !this.sourcePin.hasUpdated)) { if (canPostpone && (!this.hasUpdated || !this.source.hasUpdated)) {
Promise.all([this.updateComplete, this.sourcePin.updateComplete]) Promise.all([this.updateComplete, this.source.updateComplete])
.then(() => self.setSourceLocation(null, false)) .then(() => self.setSourceLocation(null, false))
return return
} }
location = this.sourcePin.template.getLinkLocation() location = this.source.template.getLinkLocation()
} }
const [x, y] = location const [x, y] = location
this.fromX = x this.fromX = x
@@ -210,45 +200,45 @@ export default class LinkElement extends IFromToPositionedElement {
setDestinationLocation(location = null, canPostpone = true) { setDestinationLocation(location = null, canPostpone = true) {
if (location == null) { if (location == null) {
const self = this const self = this
if (canPostpone && (!this.hasUpdated || !this.destinationPin.hasUpdated)) { if (canPostpone && (!this.hasUpdated || !this.destination.hasUpdated)) {
Promise.all([this.updateComplete, this.destinationPin.updateComplete]) Promise.all([this.updateComplete, this.destination.updateComplete])
.then(() => self.setDestinationLocation(null, false)) .then(() => self.setDestinationLocation(null, false))
return return
} }
location = this.destinationPin.template.getLinkLocation() location = this.destination.template.getLinkLocation()
} }
this.toX = location[0] this.toX = location[0]
this.toY = location[1] this.toY = location[1]
} }
getInputPin() { getInputPin() {
if (this.sourcePin?.isInput()) { if (this.source?.isInput()) {
return this.sourcePin return this.source
} }
return this.destinationPin return this.destination
} }
/** @param {PinElement} pin */ /** @param {PinElement} pin */
setInputPin(pin) { setInputPin(pin) {
if (this.sourcePin?.isInput()) { if (this.source?.isInput()) {
this.sourcePin = pin this.source = pin
} }
this.destinationPin = pin this.destination = pin
} }
getOutputPin() { getOutputPin() {
if (this.destinationPin?.isOutput()) { if (this.destination?.isOutput()) {
return this.destinationPin return this.destination
} }
return this.sourcePin return this.source
} }
/** @param {PinElement} pin */ /** @param {PinElement} pin */
setOutputPin(pin) { setOutputPin(pin) {
if (this.destinationPin?.isOutput()) { if (this.destination?.isOutput()) {
this.destinationPin = pin this.destination = pin
} }
this.sourcePin = pin this.source = pin
} }
startDragging() { startDragging() {
@@ -266,7 +256,7 @@ export default class LinkElement extends IFromToPositionedElement {
setMessageConvertType() { setMessageConvertType() {
this.linkMessageIcon = "ueb-icon-conver-type" this.linkMessageIcon = "ueb-icon-conver-type"
this.linkMessageText = `Convert ${this.sourcePin.pinType} to ${this.destinationPin.pinType}.` this.linkMessageText = `Convert ${this.source.pinType} to ${this.destination.pinType}.`
} }
setMessageCorrect() { setMessageCorrect() {
@@ -306,6 +296,6 @@ export default class LinkElement extends IFromToPositionedElement {
setMEssagetypesIncompatible() { setMEssagetypesIncompatible() {
this.linkMessageIcon = SVGIcon.reject this.linkMessageIcon = SVGIcon.reject
this.linkMessageText = html`${this.sourcePin.pinType} is not compatible with ${this.destinationPin.pinType}.` this.linkMessageText = html`${this.source.pinType} is not compatible with ${this.destination.pinType}.`
} }
} }

View File

@@ -140,7 +140,7 @@ export default class IMouseClickDrag extends IPointing {
* @param {Object} options * @param {Object} options
*/ */
constructor(target, blueprint, options = {}) { constructor(target, blueprint, options = {}) {
options.clickButton ??= 0 options.clickButton ??= Configuration.mouseClickButton
options.consumeEvent ??= true options.consumeEvent ??= true
options.draggableElement ??= target options.draggableElement ??= target
options.exitAnyButton ??= true options.exitAnyButton ??= true
@@ -160,7 +160,7 @@ export default class IMouseClickDrag extends IPointing {
listenEvents() { listenEvents() {
super.listenEvents() super.listenEvents()
this.#draggableElement.addEventListener("mousedown", this.#mouseDownHandler) this.#draggableElement.addEventListener("mousedown", this.#mouseDownHandler)
if (this.options.clickButton == 2) { if (this.options.clickButton === Configuration.mouseRightClickButton) {
this.#draggableElement.addEventListener("contextmenu", e => e.preventDefault()) this.#draggableElement.addEventListener("contextmenu", e => e.preventDefault())
} }
} }

View File

@@ -1,3 +1,4 @@
import Configuration from "../../Configuration.js"
import IPointing from "./IPointing.js" import IPointing from "./IPointing.js"
/** /**
@@ -47,7 +48,7 @@ export default class MouseClick extends IPointing {
clickedPosition = [0, 0] clickedPosition = [0, 0]
constructor(target, blueprint, options = {}) { constructor(target, blueprint, options = {}) {
options.clickButton ??= 0 options.clickButton ??= Configuration.mouseClickButton
options.consumeEvent ??= true options.consumeEvent ??= true
options.exitAnyButton ??= true options.exitAnyButton ??= true
options.strictTarget ??= false options.strictTarget ??= false
@@ -57,7 +58,7 @@ export default class MouseClick extends IPointing {
listenEvents() { listenEvents() {
this.target.addEventListener("mousedown", this.#mouseDownHandler) this.target.addEventListener("mousedown", this.#mouseDownHandler)
if (this.options.clickButton == 2) { if (this.options.clickButton === Configuration.mouseRightClickButton) {
this.target.addEventListener("contextmenu", e => e.preventDefault()) this.target.addEventListener("contextmenu", e => e.preventDefault())
} }
} }

View File

@@ -24,7 +24,7 @@ export default class MouseCreateLink extends IMouseClickDrag {
if (!this.enteredPin) { if (!this.enteredPin) {
this.linkValid = false this.linkValid = false
this.enteredPin = /** @type {PinElement} */(e.target) this.enteredPin = /** @type {PinElement} */(e.target)
const a = this.link.sourcePin ?? this.target // Remember target might have change const a = this.link.source ?? this.target // Remember target might have change
const b = this.enteredPin const b = this.enteredPin
const outputPin = a.isOutput() ? a : b const outputPin = a.isOutput() ? a : b
if ( if (
@@ -107,14 +107,15 @@ export default class MouseCreateLink extends IMouseClickDrag {
pin.removeEventListener("mouseenter", this.#mouseenterHandler) pin.removeEventListener("mouseenter", this.#mouseenterHandler)
pin.removeEventListener("mouseleave", this.#mouseleaveHandler) pin.removeEventListener("mouseleave", this.#mouseleaveHandler)
}) })
this.#listenedPins = null
if (this.enteredPin && this.linkValid) { if (this.enteredPin && this.linkValid) {
if (this.#knotPin) { if (this.#knotPin) {
const otherPin = this.#knotPin !== this.link.sourcePin ? this.link.sourcePin : this.enteredPin const otherPin = this.#knotPin !== this.link.source ? this.link.source : this.enteredPin
// Knot pin direction correction // Knot pin direction correction
if (this.#knotPin.isInput() && otherPin.isInput() || this.#knotPin.isOutput() && otherPin.isOutput()) { if (this.#knotPin.isInput() && otherPin.isInput() || this.#knotPin.isOutput() && otherPin.isOutput()) {
const oppositePin = /** @type {KnotPinTemplate} */(this.#knotPin.template).getOppositePin() const oppositePin = /** @type {KnotPinTemplate} */(this.#knotPin.template).getOppositePin()
if (this.#knotPin === this.link.sourcePin) { if (this.#knotPin === this.link.source) {
this.link.sourcePin = oppositePin this.link.source = oppositePin
} else { } else {
this.enteredPin = oppositePin this.enteredPin = oppositePin
} }
@@ -122,16 +123,18 @@ export default class MouseCreateLink extends IMouseClickDrag {
} else if (this.enteredPin.nodeElement.getType() === Configuration.nodeType.knot) { } else if (this.enteredPin.nodeElement.getType() === Configuration.nodeType.knot) {
this.enteredPin = /** @type {KnotPinTemplate} */(this.enteredPin.template).getOppositePin() this.enteredPin = /** @type {KnotPinTemplate} */(this.enteredPin.template).getOppositePin()
} }
this.blueprint.addGraphElement(this.link) if (!this.link.source.getLinks().find(ref => ref.equals(this.enteredPin.createPinReference()))) {
this.link.destinationPin = this.enteredPin this.blueprint.addGraphElement(this.link)
this.link.removeMessage() this.link.destination = this.enteredPin
this.link.finishDragging() } else {
this.link.remove()
}
} else { } else {
this.link.finishDragging()
this.link.remove() this.link.remove()
} }
this.enteredPin = null this.enteredPin = null
this.link.removeMessage()
this.link.finishDragging()
this.link = null this.link = null
this.#listenedPins = null
} }
} }

View File

@@ -89,12 +89,12 @@ export default class BlueprintTemplate extends ITemplate {
new KeyboardSelectAll(this.element.getGridDOMElement(), this.element), new KeyboardSelectAll(this.element.getGridDOMElement(), this.element),
new Zoom(this.element.getGridDOMElement(), this.element), new Zoom(this.element.getGridDOMElement(), this.element),
new Select(this.element.getGridDOMElement(), this.element, { new Select(this.element.getGridDOMElement(), this.element, {
clickButton: 0, clickButton: Configuration.mouseClickButton,
exitAnyButton: true, exitAnyButton: true,
moveEverywhere: true, moveEverywhere: true,
}), }),
new MouseScrollGraph(this.element.getGridDOMElement(), this.element, { new MouseScrollGraph(this.element.getGridDOMElement(), this.element, {
clickButton: 2, clickButton: Configuration.mouseRightClickButton,
exitAnyButton: false, exitAnyButton: false,
moveEverywhere: true, moveEverywhere: true,
}), }),

View File

@@ -67,7 +67,7 @@ export default class LinkTemplate extends IFromToPositionedTemplate {
/** @param {[Number, Number]} location */ /** @param {[Number, Number]} location */
#createKnot = location => { #createKnot = location => {
const knotEntity = new KnotEntity({}, this.element.sourcePin.entity) const knotEntity = new KnotEntity({}, this.element.source.entity)
const knot = /** @type {NodeElementConstructor} */(ElementFactory.getConstructor("ueb-node")) const knot = /** @type {NodeElementConstructor} */(ElementFactory.getConstructor("ueb-node"))
.newObject(knotEntity) .newObject(knotEntity)
knot.setLocation(...this.blueprint.snapToGrid(...location)) knot.setLocation(...this.blueprint.snapToGrid(...location))
@@ -75,13 +75,13 @@ export default class LinkTemplate extends IFromToPositionedTemplate {
this.blueprint.addGraphElement(knot) // Important: keep it before changing existing links this.blueprint.addGraphElement(knot) // Important: keep it before changing existing links
const inputPin = this.element.getInputPin() const inputPin = this.element.getInputPin()
const outputPin = this.element.getOutputPin() const outputPin = this.element.getOutputPin()
this.element.sourcePin = null this.element.source = null
this.element.destinationPin = null this.element.destination = null
const link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link")) const link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link"))
.newObject(outputPin, knotTemplate.inputPin) .newObject(outputPin, knotTemplate.inputPin)
this.blueprint.addGraphElement(link) this.blueprint.addGraphElement(link)
this.element.sourcePin = knotTemplate.outputPin this.element.source = knotTemplate.outputPin
this.element.destinationPin = inputPin this.element.destination = inputPin
} }
createInputObjects() { createInputObjects() {
@@ -104,8 +104,8 @@ export default class LinkTemplate extends IFromToPositionedTemplate {
/** @param {PropertyValues} changedProperties */ /** @param {PropertyValues} changedProperties */
willUpdate(changedProperties) { willUpdate(changedProperties) {
super.willUpdate(changedProperties) super.willUpdate(changedProperties)
const sourcePin = this.element.sourcePin const sourcePin = this.element.source
const destinationPin = this.element.destinationPin const destinationPin = this.element.destination
if (changedProperties.has("fromX") || changedProperties.has("toX")) { if (changedProperties.has("fromX") || changedProperties.has("toX")) {
const from = this.element.fromX const from = this.element.fromX
const to = this.element.toX const to = this.element.toX
@@ -113,17 +113,17 @@ export default class LinkTemplate extends IFromToPositionedTemplate {
const isDestinationAKnot = destinationPin?.nodeElement.getType() == Configuration.nodeType.knot const isDestinationAKnot = destinationPin?.nodeElement.getType() == Configuration.nodeType.knot
if (isSourceAKnot && (!destinationPin || isDestinationAKnot)) { if (isSourceAKnot && (!destinationPin || isDestinationAKnot)) {
if (sourcePin?.isInput() && to > from + Configuration.distanceThreshold) { if (sourcePin?.isInput() && to > from + Configuration.distanceThreshold) {
this.element.sourcePin = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).outputPin this.element.source = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).outputPin
} else if (sourcePin?.isOutput() && to < from - Configuration.distanceThreshold) { } else if (sourcePin?.isOutput() && to < from - Configuration.distanceThreshold) {
this.element.sourcePin = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).inputPin this.element.source = /** @type {KnotNodeTemplate} */(sourcePin.nodeElement.template).inputPin
} }
} }
if (isDestinationAKnot && (!sourcePin || isSourceAKnot)) { if (isDestinationAKnot && (!sourcePin || isSourceAKnot)) {
if (destinationPin?.isInput() && to < from - Configuration.distanceThreshold) { if (destinationPin?.isInput() && to < from - Configuration.distanceThreshold) {
this.element.destinationPin = this.element.destination =
/** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).outputPin /** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).outputPin
} else if (destinationPin?.isOutput() && to > from + Configuration.distanceThreshold) { } else if (destinationPin?.isOutput() && to > from + Configuration.distanceThreshold) {
this.element.destinationPin = this.element.destination =
/** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).inputPin /** @type {KnotNodeTemplate} */(destinationPin.nodeElement.template).inputPin
} }
} }
@@ -161,7 +161,7 @@ export default class LinkTemplate extends IFromToPositionedTemplate {
if (changedProperties.has("originatesFromInput")) { if (changedProperties.has("originatesFromInput")) {
this.element.style.setProperty("--ueb-from-input", this.element.originatesFromInput ? "1" : "0") this.element.style.setProperty("--ueb-from-input", this.element.originatesFromInput ? "1" : "0")
} }
const referencePin = this.element.sourcePin ?? this.element.destinationPin const referencePin = this.element.source ?? this.element.destination
if (referencePin) { if (referencePin) {
this.element.style.setProperty("--ueb-link-color-rgb", Utility.printLinearColor(referencePin.color)) this.element.style.setProperty("--ueb-link-color-rgb", Utility.printLinearColor(referencePin.color))
} }