mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-03-05 15:17:32 +08:00
Mergin better performance branch
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// @ts-check
|
||||
import { LitElement } from "lit"
|
||||
|
||||
/**
|
||||
* @typedef {import("../Blueprint").default} Blueprint
|
||||
@@ -11,15 +11,20 @@
|
||||
* @template {IEntity} T
|
||||
* @template {ITemplate} U
|
||||
*/
|
||||
export default class IElement extends HTMLElement {
|
||||
export default class IElement extends LitElement {
|
||||
|
||||
static properties = {
|
||||
}
|
||||
|
||||
#nextUpdatedCallbacks = []
|
||||
|
||||
/** @type {Blueprint} */
|
||||
#blueprint
|
||||
get blueprint() {
|
||||
return this.#blueprint
|
||||
}
|
||||
set blueprint(blueprint) {
|
||||
this.#blueprint = blueprint
|
||||
set blueprint(v) {
|
||||
return this.#blueprint = v
|
||||
}
|
||||
|
||||
/** @type {T} */
|
||||
@@ -36,9 +41,6 @@ export default class IElement extends HTMLElement {
|
||||
get template() {
|
||||
return this.#template
|
||||
}
|
||||
set template(template) {
|
||||
this.#template = template
|
||||
}
|
||||
|
||||
/** @type {IInput[]} */
|
||||
inputObjects = []
|
||||
@@ -52,40 +54,79 @@ export default class IElement extends HTMLElement {
|
||||
this.#entity = entity
|
||||
this.#template = template
|
||||
this.inputObjects = []
|
||||
this.#template.constructed(this)
|
||||
}
|
||||
|
||||
getTemplate() {
|
||||
return this.template
|
||||
createRenderRoot() {
|
||||
return this
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.#blueprint = this.closest("ueb-blueprint")
|
||||
this.template.setup(this)
|
||||
super.connectedCallback()
|
||||
this.blueprint = this.closest("ueb-blueprint")
|
||||
this.template.connectedCallback(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Map} changedProperties
|
||||
*/
|
||||
willUpdate(changedProperties) {
|
||||
super.willUpdate(changedProperties)
|
||||
this.template.willUpdate(this, changedProperties)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Map} changedProperties
|
||||
*/
|
||||
update(changedProperties) {
|
||||
super.update(changedProperties)
|
||||
this.template.update(this, changedProperties)
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.template.render(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Map} changedProperties
|
||||
*/
|
||||
firstUpdated(changedProperties) {
|
||||
super.firstUpdated(changedProperties)
|
||||
this.template.firstUpdated(this, changedProperties)
|
||||
this.template.inputSetup(this)
|
||||
}
|
||||
|
||||
updated(changedProperties) {
|
||||
super.updated(changedProperties)
|
||||
this.template.updated(this, changedProperties)
|
||||
this.#nextUpdatedCallbacks.forEach(f => f(changedProperties))
|
||||
this.#nextUpdatedCallbacks = []
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback()
|
||||
this.template.cleanup(this)
|
||||
}
|
||||
|
||||
addNextUpdatedCallbacks(callback, requestUpdate = false) {
|
||||
this.#nextUpdatedCallbacks.push(callback)
|
||||
if (requestUpdate) {
|
||||
this.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {IElement} element
|
||||
*/
|
||||
isSameGraph(element) {
|
||||
return this.#blueprint && this.#blueprint == element?.blueprint
|
||||
return this.blueprint && this.blueprint == element?.blueprint
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {IInput} V
|
||||
* @param {new (...args: any[]) => V} type
|
||||
* @returns {V}
|
||||
*/
|
||||
getInputObject(type) {
|
||||
return /** @type {V} */ (this.template.inputObjects.find(object => object.constructor == type))
|
||||
}
|
||||
|
||||
// Subclasses will want to override
|
||||
createInputObjects() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
68
js/element/IFromToPositionedElement.js
Normal file
68
js/element/IFromToPositionedElement.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import IElement from "./IElement";
|
||||
|
||||
/**
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
* @typedef {import("../template/ITemplate").default} ITemplate
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {IEntity} T
|
||||
* @template {ITemplate} U
|
||||
* @extends {IElement<T, U>}
|
||||
*/
|
||||
export default class IFromToPositionedElement extends IElement {
|
||||
|
||||
static properties = {
|
||||
...super.properties,
|
||||
initialPositionX: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
initialPositionY: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
finaPositionX: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
finaPositionY: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
}
|
||||
|
||||
constructor(...args) {
|
||||
super(...args)
|
||||
this.initialPositionX = 0
|
||||
this.initialPositionY = 0
|
||||
this.finaPositionX = 0
|
||||
this.finaPositionY = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} param0
|
||||
*/
|
||||
setBothLocations([x, y]) {
|
||||
this.initialPositionX = x
|
||||
this.initialPositionY = y
|
||||
this.finaPositionX = x
|
||||
this.finaPositionY = y
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} offset
|
||||
*/
|
||||
addSourceLocation([offsetX, offsetY]) {
|
||||
this.initialPositionX += offsetX
|
||||
this.initialPositionY += offsetY
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} offset
|
||||
*/
|
||||
addDestinationLocation([offsetX, offsetY]) {
|
||||
this.finaPositionX += offsetX
|
||||
this.finaPositionY += offsetY
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// @ts-check
|
||||
|
||||
import Configuration from "../Configuration"
|
||||
import IElement from "./IElement"
|
||||
import Utility from "../Utility"
|
||||
@@ -16,45 +14,57 @@ import Utility from "../Utility"
|
||||
*/
|
||||
export default class ISelectableDraggableElement extends IElement {
|
||||
|
||||
static properties = {
|
||||
...super.properties,
|
||||
selected: {
|
||||
type: Boolean,
|
||||
attribute: "data-selected",
|
||||
reflect: true,
|
||||
converter: Utility.booleanConverter,
|
||||
},
|
||||
locationX: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
locationY: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
}
|
||||
|
||||
constructor(...args) {
|
||||
// @ts-expect-error
|
||||
super(...args)
|
||||
this.dragObject = null
|
||||
this.location = [0, 0]
|
||||
this.selected = false
|
||||
this.locationX = 0
|
||||
this.locationY = 0
|
||||
|
||||
this.listeningDrag = false
|
||||
|
||||
let self = this
|
||||
this.dragHandler = e => self.addLocation(e.detail.value)
|
||||
}
|
||||
|
||||
#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)
|
||||
this.setSelected(this.selected)
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback()
|
||||
this.blueprint.removeEventListener(Configuration.nodeDragEventName, this.dragHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} value
|
||||
* @param {Number[]} param0
|
||||
*/
|
||||
setLocation(value = [0, 0]) {
|
||||
const d = [value[0] - this.location[0], value[1] - this.location[1]]
|
||||
this.location = value
|
||||
this.template.applyLocation(this)
|
||||
setLocation([x, y]) {
|
||||
const d = [x - this.locationX, y - this.locationY]
|
||||
this.locationX = x
|
||||
this.locationY = y
|
||||
if (this.blueprint) {
|
||||
const dragLocalEvent = new CustomEvent(Configuration.nodeDragLocalEventName, {
|
||||
detail: {
|
||||
value: d
|
||||
value: d,
|
||||
},
|
||||
bubbles: false,
|
||||
cancelable: true
|
||||
@@ -63,16 +73,29 @@ export default class ISelectableDraggableElement extends IElement {
|
||||
}
|
||||
}
|
||||
|
||||
addLocation(value) {
|
||||
this.setLocation([this.location[0] + value[0], this.location[1] + value[1]])
|
||||
/**
|
||||
* @param {Number[]} param0
|
||||
*/
|
||||
addLocation([x, y]) {
|
||||
this.setLocation([this.locationX + x, this.locationY + y])
|
||||
}
|
||||
|
||||
setSelected(value = true) {
|
||||
if (this.selected != value) {
|
||||
this.#setSelected(value)
|
||||
this.selected = value
|
||||
if (this.blueprint) {
|
||||
if (this.selected) {
|
||||
this.listeningDrag = true
|
||||
this.blueprint.addEventListener(Configuration.nodeDragEventName, this.dragHandler)
|
||||
} else {
|
||||
this.blueprint.removeEventListener(Configuration.nodeDragEventName, this.dragHandler)
|
||||
this.listeningDrag = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} value
|
||||
*/
|
||||
dispatchDragEvent(value) {
|
||||
const dragEvent = new CustomEvent(Configuration.nodeDragEventName, {
|
||||
detail: {
|
||||
@@ -85,8 +108,8 @@ export default class ISelectableDraggableElement extends IElement {
|
||||
}
|
||||
|
||||
snapToGrid() {
|
||||
let snappedLocation = Utility.snapToGrid(this.location, Configuration.gridSize)
|
||||
if (this.location[0] != snappedLocation[0] || this.location[1] != snappedLocation[1]) {
|
||||
const snappedLocation = Utility.snapToGrid([this.locationX, this.locationY], Configuration.gridSize)
|
||||
if (this.locationX != snappedLocation[0] || this.locationY != snappedLocation[1]) {
|
||||
this.setLocation(snappedLocation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,204 +1,258 @@
|
||||
// @ts-check
|
||||
|
||||
import Configuration from "../Configuration"
|
||||
import IElement from "./IElement"
|
||||
import IFromToPositionedElement from "./IFromToPositionedElement"
|
||||
import LinkTemplate from "../template/LinkTemplate"
|
||||
import Utility from "../Utility"
|
||||
|
||||
/**
|
||||
* @typedef {import("./PinElement").default} PinElement
|
||||
* @typedef {import("./LinkMessageElement").default} LinkMessageElement
|
||||
* @typedef {import("../entity/IEntity").default} IEntity
|
||||
*/
|
||||
|
||||
/**
|
||||
* @extends {IElement<Object, LinkTemplate>}
|
||||
* @extends {IFromToPositionedElement<Object, LinkTemplate>}
|
||||
*/
|
||||
export default class LinkElement extends IElement {
|
||||
export default class LinkElement extends IFromToPositionedElement {
|
||||
|
||||
static properties = {
|
||||
...super.properties,
|
||||
source: {
|
||||
type: String,
|
||||
reflect: true,
|
||||
},
|
||||
destination: {
|
||||
type: String,
|
||||
reflect: true,
|
||||
},
|
||||
dragging: {
|
||||
type: Boolean,
|
||||
attribute: "data-dragging",
|
||||
converter: Utility.booleanConverter,
|
||||
reflect: true,
|
||||
},
|
||||
originatesFromInput: {
|
||||
type: Boolean,
|
||||
attribute: false,
|
||||
},
|
||||
svgPathD: {
|
||||
type: String,
|
||||
attribute: false,
|
||||
},
|
||||
linkMessageIcon: {
|
||||
type: String,
|
||||
attribute: false,
|
||||
},
|
||||
linkMessageText: {
|
||||
type: String,
|
||||
attribute: false,
|
||||
},
|
||||
}
|
||||
|
||||
/** @type {PinElement} */
|
||||
#source
|
||||
#sourcePin
|
||||
get sourcePin() {
|
||||
return this.#source
|
||||
return this.#sourcePin
|
||||
}
|
||||
set sourcePin(pin) {
|
||||
if (this.#source == pin) {
|
||||
return
|
||||
}
|
||||
if (this.#source) {
|
||||
const nodeElement = this.#source.getNodeElement()
|
||||
nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
|
||||
nodeElement.removeEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragSourceHandler)
|
||||
if (this.#destination) {
|
||||
this.#unlinkPins()
|
||||
}
|
||||
}
|
||||
this.#source = pin
|
||||
if (this.#source) {
|
||||
const nodeElement = this.#source.getNodeElement()
|
||||
this.originatesFromInput = pin.isInput()
|
||||
nodeElement.addEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
|
||||
nodeElement.addEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragSourceHandler)
|
||||
this.setSourceLocation()
|
||||
if (this.#destination) {
|
||||
this.#linkPins()
|
||||
}
|
||||
}
|
||||
this.template.applyPins(this)
|
||||
this.#setPin(pin, false)
|
||||
}
|
||||
|
||||
/** @type {PinElement} */
|
||||
#destination
|
||||
#destinationPin
|
||||
get destinationPin() {
|
||||
return this.#destination
|
||||
return this.#destinationPin
|
||||
}
|
||||
set destinationPin(pin) {
|
||||
if (this.#destination == pin) {
|
||||
return
|
||||
}
|
||||
if (this.#destination) {
|
||||
const nodeElement = this.#destination.getNodeElement()
|
||||
nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
|
||||
nodeElement.removeEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler)
|
||||
if (this.#source) {
|
||||
this.#unlinkPins()
|
||||
}
|
||||
}
|
||||
this.#destination = pin
|
||||
if (this.#destination) {
|
||||
const nodeElement = this.#destination.getNodeElement()
|
||||
nodeElement.addEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
|
||||
nodeElement.addEventListener(Configuration.nodeDragLocalEventName, this.#nodeDragDestinatonHandler)
|
||||
this.setDestinationLocation()
|
||||
if (this.#source) {
|
||||
this.#linkPins()
|
||||
}
|
||||
}
|
||||
this.template.applyPins(this)
|
||||
this.#setPin(pin, true)
|
||||
}
|
||||
|
||||
#nodeDeleteHandler
|
||||
#nodeDragSourceHandler
|
||||
#nodeDragDestinatonHandler
|
||||
sourceLocation = [0, 0]
|
||||
#nodeReflowSourceHandler
|
||||
#nodeReflowDestinatonHandler
|
||||
/** @type {SVGPathElement} */
|
||||
pathElement
|
||||
/** @type {LinkMessageElement} */
|
||||
linkMessageElement
|
||||
originatesFromInput = false
|
||||
destinationLocation = [0, 0]
|
||||
|
||||
/**
|
||||
* @param {PinElement} source
|
||||
* @param {PinElement} destination
|
||||
* @param {PinElement?} destination
|
||||
*/
|
||||
constructor(source, destination) {
|
||||
super({}, new LinkTemplate())
|
||||
const self = this
|
||||
this.#nodeDeleteHandler = _ => self.remove()
|
||||
this.#nodeDeleteHandler = () => self.remove()
|
||||
this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value)
|
||||
this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value)
|
||||
this.#nodeReflowSourceHandler = e => self.setSourceLocation()
|
||||
this.#nodeReflowDestinatonHandler = e => self.setDestinationLocation()
|
||||
this.source = null
|
||||
this.destination = null
|
||||
this.dragging = false
|
||||
this.originatesFromInput = false
|
||||
this.startPercentage = 0
|
||||
this.svgPathD = ""
|
||||
this.startPixels = 0
|
||||
this.linkMessageIcon = ""
|
||||
this.linkMessageText = ""
|
||||
if (source) {
|
||||
this.sourcePin = source
|
||||
if (!destination) {
|
||||
this.finaPositionX = this.initialPositionX
|
||||
this.finaPositionY = this.initialPositionY
|
||||
}
|
||||
}
|
||||
if (destination) {
|
||||
this.destinationPin = destination
|
||||
if (!source) {
|
||||
this.initialPositionX = this.finaPositionX
|
||||
this.initialPositionY = this.finaPositionY
|
||||
}
|
||||
}
|
||||
if (source && destination) {
|
||||
this.#linkPins()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
* @param {Boolean} isDestinationPin
|
||||
*/
|
||||
#setPin(pin, isDestinationPin) {
|
||||
const getCurrentPin = () => isDestinationPin ? this.destinationPin : this.sourcePin
|
||||
if (getCurrentPin() == pin) {
|
||||
return
|
||||
}
|
||||
if (getCurrentPin()) {
|
||||
const nodeElement = getCurrentPin().getNodeElement()
|
||||
nodeElement.removeEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
|
||||
nodeElement.removeEventListener(
|
||||
Configuration.nodeDragLocalEventName,
|
||||
isDestinationPin ? this.#nodeDragDestinatonHandler : this.#nodeDragSourceHandler
|
||||
)
|
||||
nodeElement.removeEventListener(
|
||||
Configuration.nodeReflowEventName,
|
||||
isDestinationPin ? this.#nodeReflowDestinatonHandler : this.#nodeReflowSourceHandler
|
||||
)
|
||||
this.#unlinkPins()
|
||||
}
|
||||
isDestinationPin
|
||||
? this.#destinationPin = pin
|
||||
: this.#sourcePin = pin
|
||||
if (getCurrentPin()) {
|
||||
const nodeElement = getCurrentPin().getNodeElement()
|
||||
nodeElement.addEventListener(Configuration.nodeDeleteEventName, this.#nodeDeleteHandler)
|
||||
nodeElement.addEventListener(
|
||||
Configuration.nodeDragLocalEventName,
|
||||
isDestinationPin ? this.#nodeDragDestinatonHandler : this.#nodeDragSourceHandler
|
||||
)
|
||||
nodeElement.addEventListener(
|
||||
Configuration.nodeReflowEventName,
|
||||
isDestinationPin ? this.#nodeReflowDestinatonHandler : this.#nodeReflowSourceHandler
|
||||
)
|
||||
isDestinationPin
|
||||
? this.setDestinationLocation()
|
||||
: (this.setSourceLocation(), this.originatesFromInput = this.sourcePin.isInput())
|
||||
this.#linkPins()
|
||||
}
|
||||
}
|
||||
|
||||
#linkPins() {
|
||||
this.#source.linkTo(this.#destination)
|
||||
this.#destination.linkTo(this.#source)
|
||||
if (this.sourcePin && this.destinationPin) {
|
||||
this.sourcePin.linkTo(this.destinationPin)
|
||||
this.destinationPin.linkTo(this.sourcePin)
|
||||
}
|
||||
}
|
||||
|
||||
#unlinkPins() {
|
||||
if (this.#source && this.#destination) {
|
||||
this.#source.unlinkFrom(this.#destination)
|
||||
this.#destination.unlinkFrom(this.#source)
|
||||
if (this.sourcePin && this.destinationPin) {
|
||||
this.sourcePin.unlinkFrom(this.destinationPin)
|
||||
this.destinationPin.unlinkFrom(this.sourcePin)
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback()
|
||||
this.#unlinkPins()
|
||||
this.sourcePin = null
|
||||
this.destinationPin = null
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Number[]}
|
||||
*/
|
||||
getSourceLocation() {
|
||||
return this.sourceLocation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} offset
|
||||
*/
|
||||
addSourceLocation(offset) {
|
||||
const location = [
|
||||
this.sourceLocation[0] + offset[0],
|
||||
this.sourceLocation[1] + offset[1]
|
||||
]
|
||||
this.sourceLocation = location
|
||||
this.template.applyFullLocation(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} location
|
||||
* @param {Number[]?} location
|
||||
*/
|
||||
setSourceLocation(location = null) {
|
||||
if (location == null) {
|
||||
location = this.#source.template.getLinkLocation(this.#source)
|
||||
const self = this
|
||||
if (!this.hasUpdated || !this.sourcePin.hasUpdated) {
|
||||
Promise.all([this.updateComplete, this.sourcePin.updateComplete]).then(() => self.setSourceLocation())
|
||||
return
|
||||
}
|
||||
location = this.sourcePin.template.getLinkLocation(this.sourcePin)
|
||||
}
|
||||
this.sourceLocation = location
|
||||
this.template.applySourceLocation(this)
|
||||
}
|
||||
|
||||
getDestinationLocation() {
|
||||
return this.destinationLocation
|
||||
const [x, y] = location
|
||||
this.initialPositionX = x
|
||||
this.initialPositionY = y
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} offset
|
||||
*/
|
||||
addDestinationLocation(offset) {
|
||||
const location = [
|
||||
this.destinationLocation[0] + offset[0],
|
||||
this.destinationLocation[1] + offset[1]
|
||||
]
|
||||
this.setDestinationLocation(location)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} location
|
||||
* @param {Number[]?} location
|
||||
*/
|
||||
setDestinationLocation(location = null) {
|
||||
if (location == null) {
|
||||
location = this.#destination.template.getLinkLocation(this.#destination)
|
||||
}
|
||||
this.destinationLocation = location
|
||||
this.template.applyFullLocation(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LinkMessageElement} linkMessage
|
||||
*/
|
||||
setLinkMessage(linkMessage) {
|
||||
if (linkMessage) {
|
||||
this.template.applyLinkMessage(this, linkMessage)
|
||||
} else if (this.linkMessageElement) {
|
||||
this.linkMessageElement.remove()
|
||||
this.linkMessageElement = null
|
||||
const self = this
|
||||
if (!this.hasUpdated || !this.destinationPin.hasUpdated) {
|
||||
Promise.all([this.updateComplete, this.destinationPin.updateComplete]).then(() => self.setDestinationLocation())
|
||||
return
|
||||
}
|
||||
location = this.destinationPin.template.getLinkLocation(this.destinationPin)
|
||||
}
|
||||
this.finaPositionX = location[0]
|
||||
this.finaPositionY = location[1]
|
||||
}
|
||||
|
||||
startDragging() {
|
||||
this.template.applyStartDragging(this)
|
||||
this.dragging = true
|
||||
}
|
||||
|
||||
finishDragging() {
|
||||
this.template.applyFinishDragging(this)
|
||||
this.dragging = false
|
||||
}
|
||||
|
||||
removeMessage() {
|
||||
this.linkMessageIcon = ""
|
||||
this.linkMessageText = ""
|
||||
}
|
||||
|
||||
setMessageConvertType() {
|
||||
this.linkMessageIcon = "ueb-icon-conver-type"
|
||||
this.linkMessageText = `Convert ${this.sourcePin.pinType} to ${this.destinationPin.pinType}.`
|
||||
}
|
||||
|
||||
setMessageCorrect() {
|
||||
this.linkMessageIcon = "ueb-icon-correct"
|
||||
this.linkMessageText = ""
|
||||
}
|
||||
|
||||
setMessageDirectionsIncompatible() {
|
||||
this.linkMessageIcon = "ueb-icon-directions-incompatible"
|
||||
this.linkMessageText = "Directions are not compatbile."
|
||||
}
|
||||
|
||||
setMessagePlaceNode() {
|
||||
this.linkMessageIcon = "ueb-icon-place-node"
|
||||
this.linkMessageText = "Place a new node."
|
||||
}
|
||||
|
||||
setMessageReplaceLink() {
|
||||
this.linkMessageIcon = "ueb-icon-replace-link"
|
||||
this.linkMessageText = "Replace existing input connections."
|
||||
}
|
||||
|
||||
setMessageSameNode() {
|
||||
this.linkMessageIcon = "ueb-icon-same-node"
|
||||
this.linkMessageText = "Both are on the same node."
|
||||
}
|
||||
|
||||
setMEssagetypesIncompatible() {
|
||||
this.linkMessageIcon = "ueb-icon-types-incompatible"
|
||||
this.linkMessageText = `${this.sourcePin.pinType} is not compatible with ${this.destinationPin.pinType}.`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
import IElement from "./IElement"
|
||||
import LinkMessageTemplate from "../template/LinkMessageTemplate"
|
||||
|
||||
/**
|
||||
* @typedef {import("./PinElement").default} PinElement
|
||||
* @typedef {import("./LinkElement").default} LinkElement
|
||||
* @typedef {(sourcePin: PinElement, destinationPin: PinElement) => String} LinkRetrieval
|
||||
*/
|
||||
|
||||
/**
|
||||
* @extends {IElement<Object, LinkMessageTemplate>}
|
||||
*/
|
||||
export default class LinkMessageElement extends IElement {
|
||||
|
||||
static convertType = _ => new LinkMessageElement(
|
||||
"ueb-icon-conver-type",
|
||||
/** @type {LinkRetrieval} */
|
||||
(s, d) => `Convert ${s.getType()} to ${d.getType()}.`
|
||||
)
|
||||
static correct = _ => new LinkMessageElement(
|
||||
"ueb-icon-correct",
|
||||
/** @type {LinkRetrieval} */
|
||||
(s, d) => ""
|
||||
)
|
||||
static directionsIncompatible = _ => new LinkMessageElement(
|
||||
"ueb-icon-directions-incompatible",
|
||||
/** @type {LinkRetrieval} */
|
||||
(s, d) => "Directions are not compatbile."
|
||||
)
|
||||
static placeNode = _ => new LinkMessageElement(
|
||||
"ueb-icon-place-node",
|
||||
/** @type {LinkRetrieval} */
|
||||
(s, d) => "Place a new node."
|
||||
)
|
||||
static replaceLink = _ => new LinkMessageElement(
|
||||
"ueb-icon-replace-link",
|
||||
/** @type {LinkRetrieval} */
|
||||
(s, d) => "Replace existing input connections."
|
||||
)
|
||||
static sameNode = _ => new LinkMessageElement(
|
||||
"ueb-icon-same-node",
|
||||
/** @type {LinkRetrieval} */
|
||||
(s, d) => "Both are on the same node."
|
||||
)
|
||||
static typesIncompatible = _ => new LinkMessageElement(
|
||||
"ueb-icon-types-incompatible",
|
||||
/** @type {LinkRetrieval} */
|
||||
(s, d) => `${s.getType()} is not compatible with ${d.getType()}.`
|
||||
)
|
||||
|
||||
/** @type {String} */
|
||||
icon
|
||||
/** @type {LinkRetrieval} */
|
||||
message
|
||||
/** @type {LinkElement} */
|
||||
linkElement
|
||||
|
||||
constructor(icon, message) {
|
||||
super({}, new LinkMessageTemplate())
|
||||
this.icon = icon
|
||||
this.message = message
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define("ueb-link-message", LinkMessageElement)
|
||||
@@ -1,10 +1,9 @@
|
||||
// @ts-check
|
||||
|
||||
import Configuration from "../Configuration"
|
||||
import IdentifierEntity from "../entity/IdentifierEntity"
|
||||
import ISelectableDraggableElement from "./ISelectableDraggableElement"
|
||||
import NodeTemplate from "../template/NodeTemplate"
|
||||
import ObjectEntity from "../entity/ObjectEntity"
|
||||
import PinElement from "./PinElement"
|
||||
import PinEntity from "../entity/PinEntity"
|
||||
import PinReferenceEntity from "../entity/PinReferenceEntity"
|
||||
import SerializerFactory from "../serialization/SerializerFactory"
|
||||
@@ -14,13 +13,64 @@ import SerializerFactory from "../serialization/SerializerFactory"
|
||||
*/
|
||||
export default class NodeElement extends ISelectableDraggableElement {
|
||||
|
||||
static properties = {
|
||||
...ISelectableDraggableElement.properties,
|
||||
name: {
|
||||
type: String,
|
||||
attribute: "data-name",
|
||||
reflect: true,
|
||||
},
|
||||
advancedPinDisplay: {
|
||||
type: String,
|
||||
attribute: "data-advanced-display",
|
||||
converter: IdentifierEntity.attributeConverter,
|
||||
reflect: true,
|
||||
},
|
||||
enabledState: {
|
||||
type: String,
|
||||
attribute: "data-enabled-state",
|
||||
reflect: true,
|
||||
},
|
||||
nodeDisplayName: {
|
||||
type: String,
|
||||
attribute: false,
|
||||
},
|
||||
}
|
||||
|
||||
get blueprint() {
|
||||
return super.blueprint
|
||||
}
|
||||
set blueprint(v) {
|
||||
super.blueprint = v
|
||||
this.#pins.forEach(p => p.blueprint = v)
|
||||
}
|
||||
|
||||
/** @type {HTMLElement} */
|
||||
#nodeNameElement
|
||||
get nodeNameElement() {
|
||||
return this.#nodeNameElement
|
||||
}
|
||||
set nodeNameElement(value) {
|
||||
this.#nodeNameElement = value
|
||||
}
|
||||
|
||||
#pins
|
||||
|
||||
/**
|
||||
* @param {ObjectEntity} entity
|
||||
*/
|
||||
constructor(entity) {
|
||||
super(entity, new NodeTemplate())
|
||||
this.#pins = this.getPinEntities().filter(v => !v.isHidden()).map(v => new PinElement(v))
|
||||
this.#pins.forEach(pin => pin.nodeElement = this)
|
||||
this.name = entity.getObjectName()
|
||||
this.advancedPinDisplay = entity.AdvancedPinDisplay?.toString()
|
||||
this.enabledState = entity.EnabledState
|
||||
this.nodeDisplayName = entity.getDisplayName()
|
||||
this.dragLinkObjects = []
|
||||
super.setLocation([this.entity.NodePosX.value, this.entity.NodePosY.value])
|
||||
this.entity.subscribe("AdvancedPinDisplay", value => this.advancedPinDisplay = value)
|
||||
this.entity.subscribe("Name", value => this.name = value)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,11 +120,10 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
}
|
||||
}
|
||||
this.entity.Name = name
|
||||
this.template.applyRename(this)
|
||||
}
|
||||
|
||||
getPinElements() {
|
||||
return this.template.getPinElements(this)
|
||||
return this.#pins
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,9 +135,7 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
|
||||
setLocation(value = [0, 0]) {
|
||||
let nodeType = this.entity.NodePosX.constructor
|
||||
// @ts-expect-error
|
||||
this.entity.NodePosX = new nodeType(value[0])
|
||||
// @ts-expect-error
|
||||
this.entity.NodePosY = new nodeType(value[1])
|
||||
super.setLocation(value)
|
||||
}
|
||||
@@ -101,13 +148,20 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
this.dispatchEvent(deleteEvent)
|
||||
}
|
||||
|
||||
dispatchReflowEvent() {
|
||||
let reflowEvent = new CustomEvent(Configuration.nodeReflowEventName, {
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
})
|
||||
this.dispatchEvent(reflowEvent)
|
||||
}
|
||||
|
||||
setShowAdvancedPinDisplay(value) {
|
||||
this.entity.AdvancedPinDisplay = new IdentifierEntity(value ? "Shown" : "Hidden")
|
||||
this.template.applyAdvancedPinDisplay(this)
|
||||
}
|
||||
|
||||
toggleShowAdvancedPinDisplay() {
|
||||
this.setShowAdvancedPinDisplay(this.entity.AdvancedPinDisplay.value != "Shown")
|
||||
this.setShowAdvancedPinDisplay(this.entity.AdvancedPinDisplay?.toString() != "Shown")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// @ts-check
|
||||
|
||||
import BoolPinTemplate from "../template/BoolPinTemplate"
|
||||
import Configuration from "../Configuration"
|
||||
import ExecPinTemplate from "../template/ExecPinTemplate"
|
||||
import IElement from "./IElement"
|
||||
import ISerializer from "../serialization/ISerializer"
|
||||
import LinearColorEntity from "../entity/LinearColorEntity"
|
||||
import LinearColorPinTemplate from "../template/LinearColorPinTemplate"
|
||||
import LinkElement from "./LinkElement"
|
||||
import NamePinTemplate from "../template/NamePinTemplate"
|
||||
@@ -34,6 +35,46 @@ export default class PinElement extends IElement {
|
||||
}
|
||||
}
|
||||
|
||||
static properties = {
|
||||
advancedView: {
|
||||
type: String,
|
||||
attribute: "data-advanced-view",
|
||||
reflect: true,
|
||||
},
|
||||
color: {
|
||||
type: LinearColorEntity,
|
||||
converter: {
|
||||
fromAttribute: (value, type) => {
|
||||
return ISerializer.grammar.LinearColorFromAnyColor.parse(value).value
|
||||
},
|
||||
toAttribute: (value, type) => {
|
||||
return Utility.printLinearColor(value)
|
||||
},
|
||||
},
|
||||
reflect: true,
|
||||
},
|
||||
defaultValue: {
|
||||
type: String,
|
||||
attribute: false,
|
||||
},
|
||||
isLinked: {
|
||||
type: Boolean,
|
||||
converter: Utility.booleanConverter,
|
||||
attribute: "data-linked",
|
||||
reflect: true,
|
||||
},
|
||||
pinType: {
|
||||
type: String,
|
||||
attribute: "data-type",
|
||||
reflect: true,
|
||||
},
|
||||
pinDirection: {
|
||||
type: String,
|
||||
attribute: "data-direction",
|
||||
reflect: true,
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinEntity} pinEntity
|
||||
* @return {PinTemplate}
|
||||
@@ -46,8 +87,6 @@ export default class PinElement extends IElement {
|
||||
return result ?? PinTemplate
|
||||
}
|
||||
|
||||
#color = ""
|
||||
|
||||
/** @type {NodeElement} */
|
||||
nodeElement
|
||||
|
||||
@@ -56,20 +95,42 @@ export default class PinElement extends IElement {
|
||||
|
||||
connections = 0
|
||||
|
||||
get defaultValue() {
|
||||
return this.unreactiveDefaultValue
|
||||
}
|
||||
set defaultValue(value) {
|
||||
let oldValue = this.unreactiveDefaultValue
|
||||
this.unreactiveDefaultValue = value
|
||||
this.requestUpdate("defaultValue", oldValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinEntity} entity
|
||||
*/
|
||||
constructor(entity) {
|
||||
super(
|
||||
entity,
|
||||
// @ts-expect-error
|
||||
new (PinElement.getTypeTemplate(entity))()
|
||||
)
|
||||
this.advancedView = entity.bAdvancedView
|
||||
this.unreactiveDefaultValue = entity.getDefaultValue()
|
||||
this.pinType = this.entity.getType()
|
||||
this.color = this.constructor.properties.color.converter.fromAttribute(Configuration.pinColor[this.pinType].toString())
|
||||
this.isLinked = false
|
||||
this.pinDirection = entity.isInput() ? "input" : entity.isOutput() ? "output" : "hidden"
|
||||
|
||||
this.entity.subscribe("DefaultValue", value => this.defaultValue = value.toString())
|
||||
this.entity.subscribe("PinToolTip", value => {
|
||||
let matchResult = value.match(/\s*(.+?(?=\n)|.+\S)\s*/)
|
||||
if (matchResult) {
|
||||
return Utility.formatStringName(matchResult[1])
|
||||
}
|
||||
return Utility.formatStringName(this.entity.PinName)
|
||||
})
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.#color = window.getComputedStyle(this).getPropertyValue("--ueb-pin-color")
|
||||
}
|
||||
|
||||
/** @return {GuidEntity} */
|
||||
@@ -89,9 +150,6 @@ export default class PinElement extends IElement {
|
||||
return this.entity.PinName
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {String}
|
||||
*/
|
||||
getPinDisplayName() {
|
||||
let matchResult = null
|
||||
if (
|
||||
@@ -112,22 +170,10 @@ export default class PinElement extends IElement {
|
||||
return this.entity.isOutput()
|
||||
}
|
||||
|
||||
isLinked() {
|
||||
return this.entity.isLinked()
|
||||
}
|
||||
|
||||
getType() {
|
||||
return this.entity.getType()
|
||||
}
|
||||
|
||||
getClickableElement() {
|
||||
return this.clickableElement
|
||||
}
|
||||
|
||||
getColor() {
|
||||
return this.#color
|
||||
}
|
||||
|
||||
getLinkLocation() {
|
||||
return this.template.getLinkLocation(this)
|
||||
}
|
||||
@@ -136,13 +182,17 @@ export default class PinElement extends IElement {
|
||||
* @returns {NodeElement}
|
||||
*/
|
||||
getNodeElement() {
|
||||
return this.closest("ueb-node")
|
||||
return this.nodeElement
|
||||
}
|
||||
|
||||
getLinks() {
|
||||
return this.entity.LinkedTo ?? []
|
||||
}
|
||||
|
||||
setDefaultValue(value) {
|
||||
this.entity.DefaultValue = value
|
||||
}
|
||||
|
||||
sanitizeLinks() {
|
||||
this.entity.LinkedTo = this.getLinks().filter(pinReference => {
|
||||
let pin = this.blueprint.getPin(pinReference)
|
||||
@@ -160,16 +210,16 @@ export default class PinElement extends IElement {
|
||||
* @param {PinElement} targetPinElement
|
||||
*/
|
||||
linkTo(targetPinElement) {
|
||||
this.entity.linkTo(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity)
|
||||
this.template.applyConnected(this)
|
||||
this.entity.linkTo(targetPinElement.getNodeElement().getNodeName(), targetPinElement.entity)
|
||||
this.isLinked = this.entity.isLinked()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} targetPinElement
|
||||
*/
|
||||
unlinkFrom(targetPinElement) {
|
||||
this.entity.unlinkFrom(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity)
|
||||
this.template.applyConnected(this)
|
||||
this.entity.unlinkFrom(targetPinElement.getNodeElement().getNodeName(), targetPinElement.entity)
|
||||
this.isLinked = this.entity.isLinked()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,8 +228,8 @@ export default class PinElement extends IElement {
|
||||
*/
|
||||
redirectLink(originalPinElement, newReference) {
|
||||
const index = this.entity.LinkedTo.findIndex(pinReference =>
|
||||
pinReference.objectName.toString() == originalPinElement.getPinName()
|
||||
&& pinReference.pinGuid == originalPinElement.entity.PinId
|
||||
pinReference.objectName.toString() == originalPinElement.getNodeElement().getNodeName()
|
||||
&& pinReference.pinGuid.valueOf() == originalPinElement.entity.PinId.valueOf()
|
||||
)
|
||||
if (index >= 0) {
|
||||
this.entity.LinkedTo[index] = newReference
|
||||
|
||||
@@ -1,38 +1,47 @@
|
||||
// @ts-check
|
||||
|
||||
import FastSelectionModel from "../selection/FastSelectionModel"
|
||||
import IElement from "./IElement"
|
||||
import IFromToPositionedElement from "./IFromToPositionedElement"
|
||||
import SelectorTemplate from "../template/SelectorTemplate"
|
||||
|
||||
/**
|
||||
* @extends {IElement<Object, SelectorTemplate>}
|
||||
* @extends {IFromToPositionedElement<Object, SelectorTemplate>}
|
||||
*/
|
||||
export default class SelectorElement extends IElement {
|
||||
export default class SelectorElement extends IFromToPositionedElement {
|
||||
|
||||
constructor() {
|
||||
super({}, new SelectorTemplate())
|
||||
this.selectionModel = null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} initialPosition
|
||||
*/
|
||||
startSelecting(initialPosition) {
|
||||
this.template.applyStartSelecting(this, initialPosition)
|
||||
this.selectionModel = new FastSelectionModel(initialPosition, this.blueprint.getNodes(), this.blueprint.nodeBoundariesSupplier, this.blueprint.nodeSelectToggleFunction)
|
||||
beginSelect(initialPosition) {
|
||||
this.blueprint.selecting = true
|
||||
this.setBothLocations(initialPosition)
|
||||
this.selectionModel = new FastSelectionModel(
|
||||
initialPosition,
|
||||
this.blueprint.getNodes(),
|
||||
this.blueprint.nodeBoundariesSupplier,
|
||||
this.blueprint.nodeSelectToggleFunction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number[]} finalPosition
|
||||
*/
|
||||
doSelecting(finalPosition) {
|
||||
this.template.applyDoSelecting(this, finalPosition)
|
||||
this.selectionModel.selectTo(finalPosition)
|
||||
selectTo(finalPosition) {
|
||||
/** @type {FastSelectionModel} */ (this.selectionModel)
|
||||
.selectTo(finalPosition)
|
||||
this.finaPositionX = finalPosition[0]
|
||||
this.finaPositionY = finalPosition[1]
|
||||
}
|
||||
|
||||
finishSelecting() {
|
||||
this.template.applyFinishSelecting(this)
|
||||
endSelect() {
|
||||
this.blueprint.selecting = false
|
||||
this.selectionModel = null
|
||||
this.initialPositionX = 0
|
||||
this.initialPositionY = 0
|
||||
this.finaPositionX = 0
|
||||
this.finaPositionY = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user