mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-05-20 04:47:35 +08:00
Fix links on pasted nodes
This commit is contained in:
@@ -107,7 +107,6 @@ export default class Blueprint extends IElement {
|
||||
* @param {Number} y
|
||||
*/
|
||||
#expand(x, y) {
|
||||
// TODO remove
|
||||
x = Math.round(x)
|
||||
y = Math.round(y)
|
||||
this.additional[0] += x
|
||||
@@ -380,6 +379,18 @@ export default class Blueprint extends IElement {
|
||||
return this.links
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} sourcePin
|
||||
* @param {PinElement} destinationPin
|
||||
* @returns
|
||||
*/
|
||||
getLink(sourcePin, destinationPin, ignoreDirection = false) {
|
||||
return this.links.find(link =>
|
||||
link.sourcePin == sourcePin && link.destinationPin == destinationPin
|
||||
|| ignoreDirection && link.sourcePin == destinationPin && link.destinationPin == sourcePin
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all nodes
|
||||
*/
|
||||
@@ -419,12 +430,12 @@ export default class Blueprint extends IElement {
|
||||
this.nodesContainerElement?.appendChild(element)
|
||||
} else if (element instanceof LinkElement && !this.links.includes(element)) {
|
||||
this.links.push(element)
|
||||
this.nodesContainerElement?.appendChild(element)
|
||||
}
|
||||
}
|
||||
// Keep separated for linking purpose
|
||||
if (this.nodesContainerElement) {
|
||||
nodeElements.forEach(node => node.cleanLinks())
|
||||
}
|
||||
graphElements.filter(element => element instanceof NodeElement).forEach(
|
||||
node => /** @type {NodeElement} */(node).sanitizeLinks()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Configuration from "../Configuration"
|
||||
import IElement from "./IElement"
|
||||
import MouseMoveNodes from "../input/mouse/MouseMoveNodes"
|
||||
import Utility from "../Utility"
|
||||
|
||||
/**
|
||||
* @typedef {import("../template/SelectableDraggableTemplate").default} SelectableDraggableTemplate
|
||||
@@ -29,6 +30,23 @@ export default class ISelectableDraggableElement extends IElement {
|
||||
}
|
||||
}
|
||||
|
||||
#setSelected(value = true) {
|
||||
this.selected = value
|
||||
if (this.blueprint) {
|
||||
if (this.selected) {
|
||||
this.blueprint.addEventListener(Configuration.nodeDragEventName, this.dragHandler)
|
||||
} else {
|
||||
this.blueprint.removeEventListener(Configuration.nodeDragEventName, this.dragHandler)
|
||||
}
|
||||
}
|
||||
this.template.applySelected(this)
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.#setSelected(this.selected)
|
||||
}
|
||||
|
||||
createInputObjects() {
|
||||
return [
|
||||
new MouseMoveNodes(this, this.blueprint, {
|
||||
@@ -61,16 +79,9 @@ export default class ISelectableDraggableElement extends IElement {
|
||||
}
|
||||
|
||||
setSelected(value = true) {
|
||||
if (this.selected == value) {
|
||||
return
|
||||
if (this.selected != value) {
|
||||
this.#setSelected(value)
|
||||
}
|
||||
this.selected = value
|
||||
if (this.selected) {
|
||||
this.blueprint.addEventListener(Configuration.nodeDragEventName, this.dragHandler)
|
||||
} else {
|
||||
this.blueprint.removeEventListener(Configuration.nodeDragEventName, this.dragHandler)
|
||||
}
|
||||
this.template.applySelected(this)
|
||||
}
|
||||
|
||||
dispatchDragEvent(value) {
|
||||
@@ -85,7 +96,7 @@ export default class ISelectableDraggableElement extends IElement {
|
||||
}
|
||||
|
||||
snapToGrid() {
|
||||
let snappedLocation = this.blueprint.snapToGrid(this.location)
|
||||
let snappedLocation = Utility.snapToGrid(this.location, Configuration.gridSize)
|
||||
if (this.location[0] != snappedLocation[0] || this.location[1] != snappedLocation[1]) {
|
||||
this.setLocation(snappedLocation)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ export default class LinkElement extends IElement {
|
||||
return this.#source
|
||||
}
|
||||
set sourcePin(pin) {
|
||||
if (this.#source == pin) {
|
||||
return
|
||||
}
|
||||
if (this.#source) {
|
||||
const nodeElement = this.#source.getNodeElement()
|
||||
nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
|
||||
@@ -42,6 +45,7 @@ export default class LinkElement extends IElement {
|
||||
this.#linkPins()
|
||||
}
|
||||
}
|
||||
this.template.applyPins(this)
|
||||
}
|
||||
|
||||
/** @type {PinElement} */
|
||||
@@ -50,6 +54,9 @@ export default class LinkElement extends IElement {
|
||||
return this.#destination
|
||||
}
|
||||
set destinationPin(pin) {
|
||||
if (this.#destination == pin) {
|
||||
return
|
||||
}
|
||||
if (this.#destination) {
|
||||
const nodeElement = this.#destination.getNodeElement()
|
||||
nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
|
||||
@@ -68,6 +75,7 @@ export default class LinkElement extends IElement {
|
||||
this.#linkPins()
|
||||
}
|
||||
}
|
||||
this.template.applyPins(this)
|
||||
}
|
||||
|
||||
#nodeDeleteHandler
|
||||
@@ -149,9 +157,6 @@ export default class LinkElement extends IElement {
|
||||
this.template.applySourceLocation(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Number[]}
|
||||
*/
|
||||
getDestinationLocation() {
|
||||
return this.destinationLocation
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
return this.entity.getFullName()
|
||||
}
|
||||
|
||||
cleanLinks() {
|
||||
this.getPinElements().forEach(pin => pin.cleanLinks())
|
||||
sanitizeLinks() {
|
||||
this.getPinElements().forEach(pin => pin.sanitizeLinks())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// @ts-check
|
||||
|
||||
import ExecPinTemplate from "../template/ExecPinTemplate"
|
||||
import IElement from "./IElement"
|
||||
import LinkElement from "./LinkElement"
|
||||
import MouseCreateLink from "../input/mouse/MouseCreateLink"
|
||||
import PinTemplate from "../template/PinTemplate"
|
||||
import ExecPinTemplate from "../template/ExecPinTemplate"
|
||||
import StringPinTemplate from "../template/StringPinTemplate"
|
||||
|
||||
/**
|
||||
@@ -119,10 +120,17 @@ export default class PinElement extends IElement {
|
||||
return this.entity.LinkedTo ?? []
|
||||
}
|
||||
|
||||
cleanLinks() {
|
||||
this.entity.LinkedTo = this.getLinks().filter(
|
||||
pinReference => this.blueprint.getPin(pinReference)
|
||||
)
|
||||
sanitizeLinks() {
|
||||
this.entity.LinkedTo = this.getLinks().filter(pinReference => {
|
||||
let pin = this.blueprint.getPin(pinReference)
|
||||
if (pin) {
|
||||
let link = this.blueprint.getLink(this, pin, true)
|
||||
if (!link) {
|
||||
this.blueprint.addGraphElement(new LinkElement(this, pin))
|
||||
}
|
||||
}
|
||||
return pin
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,15 @@ export default class IEntity {
|
||||
|
||||
static attributes = {}
|
||||
|
||||
constructor(options = {}) {
|
||||
constructor(options) {
|
||||
// @ts-expect-error
|
||||
const attributes = this.constructor.attributes
|
||||
if (options.constructor !== Object && Object.getOwnPropertyNames(attributes).length == 1) {
|
||||
// Where there is just one attribute, option can be the value of that attribute
|
||||
options = {
|
||||
[Object.getOwnPropertyNames(attributes)[0]]: options
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {String[]} prefix
|
||||
* @param {Object} target
|
||||
@@ -18,8 +26,10 @@ export default class IEntity {
|
||||
const last = fullKey.length - 1
|
||||
for (let property of Object.getOwnPropertyNames(properties)) {
|
||||
fullKey[last] = property
|
||||
let defaultValue = properties[property]
|
||||
const defaultType = (defaultValue instanceof Function) ? defaultValue : defaultValue?.constructor
|
||||
// Not instanceof because all objects are instenceof Object, exact match needed
|
||||
if (properties[property]?.constructor === Object) {
|
||||
if (defaultType === Object) {
|
||||
target[property] = {}
|
||||
defineAllAttributes(fullKey, target[property], properties[property])
|
||||
continue
|
||||
@@ -33,10 +43,9 @@ export default class IEntity {
|
||||
*/
|
||||
const value = Utility.objectGet(options, fullKey)
|
||||
if (value !== undefined) {
|
||||
target[property] = value
|
||||
target[property] = TypeInitialization.sanitize(value, defaultType)
|
||||
continue
|
||||
}
|
||||
let defaultValue = properties[property]
|
||||
if (defaultValue instanceof TypeInitialization) {
|
||||
if (!defaultValue.showDefault) {
|
||||
target[property] = undefined // to preserve the order
|
||||
@@ -49,13 +58,12 @@ export default class IEntity {
|
||||
continue
|
||||
}
|
||||
if (defaultValue instanceof Function) {
|
||||
defaultValue = TypeInitialization.sanitize(new defaultValue())
|
||||
defaultValue = TypeInitialization.sanitize(new defaultValue(), defaultType)
|
||||
}
|
||||
target[property] = TypeInitialization.sanitize(defaultValue)
|
||||
target[property] = TypeInitialization.sanitize(defaultValue, defaultType)
|
||||
}
|
||||
}
|
||||
// @ts-expect-error
|
||||
defineAllAttributes([], this, this.constructor.attributes)
|
||||
defineAllAttributes([], this, attributes)
|
||||
}
|
||||
|
||||
empty() {
|
||||
|
||||
@@ -76,11 +76,11 @@ export default class PinEntity extends IEntity {
|
||||
}
|
||||
|
||||
isInput() {
|
||||
return !this.bHidden && this.Direction !== "EGPD_Output"
|
||||
return !this.bHidden && this.Direction != "EGPD_Output"
|
||||
}
|
||||
|
||||
isOutput() {
|
||||
return !this.bHidden && this.Direction === "EGPD_Output"
|
||||
return !this.bHidden && this.Direction == "EGPD_Output"
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,12 +5,19 @@
|
||||
*/
|
||||
export default class TypeInitialization {
|
||||
|
||||
static sanitize(value) {
|
||||
if (!(value instanceof Object)) {
|
||||
return value // Is already primitive
|
||||
static sanitize(value, targetType) {
|
||||
if (targetType === undefined) {
|
||||
targetType = value?.constructor
|
||||
}
|
||||
let wrongType = false
|
||||
if (targetType && value?.constructor !== targetType && !(value instanceof targetType)) {
|
||||
wrongType = true
|
||||
}
|
||||
if (value instanceof Boolean || value instanceof Number || value instanceof String) {
|
||||
return value.valueOf()
|
||||
value = value.valueOf() // Get the relative primitive value
|
||||
}
|
||||
if (wrongType) {
|
||||
return new targetType(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ export default class Paste extends IContext {
|
||||
this.blueprint.unselectAll()
|
||||
}
|
||||
let mousePosition = this.blueprint.mousePosition
|
||||
this.blueprint.addGraphElement(...nodes)
|
||||
nodes.forEach(node => {
|
||||
const locationOffset = [
|
||||
mousePosition[0] - left,
|
||||
@@ -52,6 +51,7 @@ export default class Paste extends IContext {
|
||||
node.setSelected(true)
|
||||
node.snapToGrid()
|
||||
})
|
||||
this.blueprint.addGraphElement(...nodes)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ export default class Grammar {
|
||||
|
||||
Identifier = r => P.regex(/\w+/).map(v => new IdentifierEntity(v))
|
||||
|
||||
PathSymbol = r => P.regex(/[0-9a-zA-Z_]+/).map(v => new PathSymbolEntity({ value: v }))
|
||||
PathSymbol = r => P.regex(/[0-9\w]+/).map(v => new PathSymbolEntity({ value: v }))
|
||||
|
||||
Reference = r => P.alt(
|
||||
r.None,
|
||||
|
||||
@@ -59,9 +59,7 @@ export default class LinkTemplate extends ITemplate {
|
||||
static c2Clamped = LinkTemplate.clampedLine([0, 100], [200, 30])
|
||||
|
||||
/**
|
||||
* Computes the html content of the target element.
|
||||
* @param {LinkElement} link connecting two graph nodes
|
||||
* @returns The result html
|
||||
* @param {LinkElement} link
|
||||
*/
|
||||
render(link) {
|
||||
const uniqueId = crypto.randomUUID()
|
||||
@@ -76,8 +74,7 @@ export default class LinkTemplate extends ITemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the style to the element.
|
||||
* @param {LinkElement} link Element of the graph
|
||||
* @param {LinkElement} link
|
||||
*/
|
||||
apply(link) {
|
||||
super.apply(link)
|
||||
@@ -90,10 +87,26 @@ export default class LinkTemplate extends ITemplate {
|
||||
if (referencePin) {
|
||||
link.style.setProperty("--ueb-pin-color", referencePin.getColor())
|
||||
}
|
||||
this.applyPins(link)
|
||||
if (link.sourcePin && link.destinationPin) {
|
||||
this.applyFullLocation(link)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LinkElement} link element
|
||||
* @param {LinkElement} link
|
||||
*/
|
||||
applyPins(link) {
|
||||
if (link.sourcePin) {
|
||||
link.dataset.source = link.sourcePin.GetPinId().toString()
|
||||
}
|
||||
if (link.destinationPin) {
|
||||
link.dataset.destination = link.destinationPin.GetPinId().toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LinkElement} link
|
||||
*/
|
||||
applyStartDragging(link) {
|
||||
link.blueprint.dataset.creatingLink = "true"
|
||||
@@ -101,7 +114,7 @@ export default class LinkTemplate extends ITemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LinkElement} link element
|
||||
* @param {LinkElement} link
|
||||
*/
|
||||
applyFinishDragging(link) {
|
||||
link.blueprint.dataset.creatingLink = "false"
|
||||
@@ -109,8 +122,7 @@ export default class LinkTemplate extends ITemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the style relative to the source pin location.
|
||||
* @param {LinkElement} link element
|
||||
* @param {LinkElement} link
|
||||
*/
|
||||
applySourceLocation(link) {
|
||||
link.style.setProperty("--ueb-from-input", link.originatesFromInput ? "1" : "0")
|
||||
@@ -119,8 +131,7 @@ export default class LinkTemplate extends ITemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the style relative to the destination pin location.
|
||||
* @param {LinkElement} link Link element
|
||||
* @param {LinkElement} link
|
||||
*/
|
||||
applyFullLocation(link) {
|
||||
const dx = Math.max(Math.abs(link.sourceLocation[0] - link.destinationLocation[0]), 1)
|
||||
@@ -159,7 +170,7 @@ export default class LinkTemplate extends ITemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LinkElement} link element
|
||||
* @param {LinkElement} link
|
||||
* @param {LinkMessageElement} linkMessage
|
||||
*/
|
||||
applyLinkMessage(link, linkMessage) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import html from "./html"
|
||||
import ITemplate from "./ITemplate"
|
||||
import LinkElement from "../element/LinkElement"
|
||||
import sanitizeText from "./sanitizeText"
|
||||
import Utility from "../Utility"
|
||||
|
||||
@@ -69,15 +68,6 @@ export default class PinTemplate extends ITemplate {
|
||||
}
|
||||
pin.clickableElement = pin
|
||||
pin.nodeElement = pin.closest("ueb-node")
|
||||
pin.getLinks().forEach(pinReference => {
|
||||
const targetPin = pin.blueprint.getPin(pinReference)
|
||||
if (targetPin) {
|
||||
const [sourcePin, destinationPin] = pin.isOutput() ? [pin, targetPin] : [targetPin, pin]
|
||||
pin.blueprint.addGraphElement(
|
||||
new LinkElement(/** @type {PinElement} */(sourcePin), /** @type {PinElement} */(destinationPin))
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user