mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-06-12 06:03:08 +08:00
Paste event handling WIP
This commit is contained in:
@@ -252,7 +252,7 @@ u-blueprint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ueb-node-inputs {
|
.ueb-node-inputs {
|
||||||
margin-right: 2em;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ueb-node-value-icon {
|
.ueb-node-value-icon {
|
||||||
|
|||||||
480
dist/ueblueprint.js
vendored
480
dist/ueblueprint.js
vendored
@@ -323,6 +323,14 @@ class PinEntity extends Entity {
|
|||||||
return PinEntity.attributes
|
return PinEntity.attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
getPinDisplayName() {
|
||||||
|
return this.PinName
|
||||||
|
}
|
||||||
|
|
||||||
isOutput() {
|
isOutput() {
|
||||||
if (this.Direction === "EGPD_Output") {
|
if (this.Direction === "EGPD_Output") {
|
||||||
return true
|
return true
|
||||||
@@ -361,6 +369,14 @@ class ObjectEntity extends Entity {
|
|||||||
getAttributes() {
|
getAttributes() {
|
||||||
return ObjectEntity.attributes
|
return ObjectEntity.attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {String} The name of the node
|
||||||
|
*/
|
||||||
|
getNodeDisplayName() {
|
||||||
|
return this.Name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
||||||
@@ -681,6 +697,20 @@ class ObjectSerializer extends Serializer {
|
|||||||
return parseResult.value
|
return parseResult.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {String} value
|
||||||
|
* @returns {ObjectEntity[]}
|
||||||
|
*/
|
||||||
|
readMultiple(value) {
|
||||||
|
const parseResult = Serializer.grammar.MultipleObject.parse(value);
|
||||||
|
if (!parseResult.status) {
|
||||||
|
console.error("Error when trying to parse the object.");
|
||||||
|
return parseResult
|
||||||
|
}
|
||||||
|
return parseResult.value
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {ObjectEntity} object
|
* @param {ObjectEntity} object
|
||||||
@@ -708,7 +738,7 @@ class Template {
|
|||||||
* @returns The computed html
|
* @returns The computed html
|
||||||
*/
|
*/
|
||||||
render(entity) {
|
render(entity) {
|
||||||
return ``
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -804,7 +834,7 @@ class MouseClickDrag extends Pointing {
|
|||||||
const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace;
|
const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace;
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
this.mouseDownHandler = function (e) {
|
this.mouseDownHandler = e => {
|
||||||
switch (e.button) {
|
switch (e.button) {
|
||||||
case self.clickButton:
|
case self.clickButton:
|
||||||
// Either doesn't matter or consider the click only when clicking on the parent, not descandants
|
// Either doesn't matter or consider the click only when clicking on the parent, not descandants
|
||||||
@@ -826,7 +856,7 @@ class MouseClickDrag extends Pointing {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.mouseStartedMovingHandler = function (e) {
|
this.mouseStartedMovingHandler = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
@@ -839,7 +869,7 @@ class MouseClickDrag extends Pointing {
|
|||||||
self.started = true;
|
self.started = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.mouseMoveHandler = function (e) {
|
this.mouseMoveHandler = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const location = self.getLocation(e);
|
const location = self.getLocation(e);
|
||||||
@@ -847,7 +877,7 @@ class MouseClickDrag extends Pointing {
|
|||||||
self.dragTo(location, movement);
|
self.dragTo(location, movement);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.mouseUpHandler = function (e) {
|
this.mouseUpHandler = e => {
|
||||||
if (!self.exitAnyButton || e.button == self.clickButton) {
|
if (!self.exitAnyButton || e.button == self.clickButton) {
|
||||||
// Remove the handlers of "mousemove" and "mouseup"
|
// Remove the handlers of "mousemove" and "mouseup"
|
||||||
movementListenedElement.removeEventListener('mousemove', self.mouseStartedMovingHandler);
|
movementListenedElement.removeEventListener('mousemove', self.mouseStartedMovingHandler);
|
||||||
@@ -1322,7 +1352,7 @@ class MouseWheel extends Pointing {
|
|||||||
this.looseTarget = options?.looseTarget ?? true;
|
this.looseTarget = options?.looseTarget ?? true;
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
this.mouseWheelHandler = function (e) {
|
this.mouseWheelHandler = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const location = self.getLocation(e);
|
const location = self.getLocation(e);
|
||||||
self.wheel(Math.sign(e.deltaY), location);
|
self.wheel(Math.sign(e.deltaY), location);
|
||||||
@@ -1361,6 +1391,239 @@ class BlueprintData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import("../entity/ObjectEntity").default} ObjectEntity
|
||||||
|
*/
|
||||||
|
class NodeTemplate extends Template {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the html content of the target element.
|
||||||
|
* @param {ObjectEntity} entity Entity representing the element
|
||||||
|
* @returns The computed html
|
||||||
|
*/
|
||||||
|
header(entity) {
|
||||||
|
return `
|
||||||
|
<div class="ueb-node-header">
|
||||||
|
<span class="ueb-node-name">
|
||||||
|
<span class="ueb-node-symbol"></span>
|
||||||
|
<span class="ueb-node-text">${entity.getNodeDisplayName()}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the html content of the target element.
|
||||||
|
* @param {ObjectEntity} entity Entity representing the element
|
||||||
|
* @returns The computed html
|
||||||
|
*/
|
||||||
|
body(entity) {
|
||||||
|
/** @type {PinEntity[]} */
|
||||||
|
let inputs = entity.CustomProperties.filter(v => v instanceof PinEntity);
|
||||||
|
let outputs = inputs.filter(v => v.isOutput());
|
||||||
|
inputs = inputs.filter(v => !v.isOutput());
|
||||||
|
return `
|
||||||
|
<div class="ueb-node-body">
|
||||||
|
<div class="ueb-node-inputs">
|
||||||
|
${inputs.map((input, index) => `
|
||||||
|
<div class="ueb-node-input ueb-node-value-${input.type}">
|
||||||
|
<span class="ueb-node-value-icon ${inputs[index].connected ? 'ueb-node-value-fill' : ''}"></span>
|
||||||
|
${input.getPinDisplayName()}
|
||||||
|
</div>
|
||||||
|
`).join("") ?? ""}
|
||||||
|
</div>
|
||||||
|
<div class="ueb-node-outputs">
|
||||||
|
${outputs.map((output, index) => `
|
||||||
|
<div class="ueb-node-output ueb-node-value-${output.type}">
|
||||||
|
${output.getPinDisplayName()}
|
||||||
|
<span class="ueb-node-value-icon ${outputs[index].connected ? 'ueb-node-value-fill' : ''}"></span>
|
||||||
|
</div>
|
||||||
|
`).join("") ?? ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the html content of the target element.
|
||||||
|
* @param {ObjectEntity} entity Entity representing the element
|
||||||
|
* @returns The computed html
|
||||||
|
*/
|
||||||
|
render(entity) {
|
||||||
|
return `
|
||||||
|
<div class="ueb-node-border">
|
||||||
|
<div class="ueb-node-content">
|
||||||
|
${this.header(entity)}
|
||||||
|
${this.body(entity)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Drag extends MouseClickDrag {
|
||||||
|
constructor(target, blueprint, options) {
|
||||||
|
super(target, blueprint, options);
|
||||||
|
this.stepSize = parseInt(options?.stepSize);
|
||||||
|
this.mousePosition = [0, 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
snapToGrid(location) {
|
||||||
|
return [
|
||||||
|
this.stepSize * Math.round(location[0] / this.stepSize),
|
||||||
|
this.stepSize * Math.round(location[1] / this.stepSize)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
startDrag() {
|
||||||
|
if (isNaN(this.stepSize) || this.stepSize <= 0) {
|
||||||
|
this.stepSize = parseInt(getComputedStyle(this.target).getPropertyValue('--ueb-grid-snap'));
|
||||||
|
if (isNaN(this.stepSize) || this.stepSize <= 0) {
|
||||||
|
this.stepSize = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get the current mouse position
|
||||||
|
this.mousePosition = this.stepSize != 1 ? this.snapToGrid(this.clickedPosition) : this.clickedPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
dragTo(location, movement) {
|
||||||
|
const mousePosition = this.stepSize != 1 ? this.snapToGrid(location) : location;
|
||||||
|
const d = [mousePosition[0] - this.mousePosition[0], mousePosition[1] - this.mousePosition[1]];
|
||||||
|
|
||||||
|
if (d[0] == 0 && d[1] == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.target.dragDispatch(d);
|
||||||
|
|
||||||
|
// Reassign the position of mouse
|
||||||
|
this.mousePosition = mousePosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectableDraggable extends GraphElement {
|
||||||
|
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
this.dragObject = null;
|
||||||
|
this.location = [0, 0];
|
||||||
|
this.selected = false;
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
this.dragHandler = (e) => {
|
||||||
|
self.addLocation(e.detail.value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.dragObject = new Drag(this, null, { // UDrag doesn't need blueprint
|
||||||
|
looseTarget: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
this.dragObject.unlistenDOMElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocation(value = [0, 0]) {
|
||||||
|
this.location = value;
|
||||||
|
this.style.setProperty('--ueb-position-x', this.location[0]);
|
||||||
|
this.style.setProperty('--ueb-position-y', this.location[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
addLocation(value) {
|
||||||
|
this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
dragDispatch(value) {
|
||||||
|
if (!this.selected) {
|
||||||
|
this.blueprint.unselectAll();
|
||||||
|
this.setSelected(true);
|
||||||
|
}
|
||||||
|
let dragEvent = new CustomEvent('uDragSelected', {
|
||||||
|
detail: {
|
||||||
|
instigator: this,
|
||||||
|
value: value
|
||||||
|
},
|
||||||
|
bubbles: false,
|
||||||
|
cancelable: true,
|
||||||
|
composed: false,
|
||||||
|
});
|
||||||
|
this.blueprint.dispatchEvent(dragEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelected(value = true) {
|
||||||
|
if (this.selected == value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.selected = value;
|
||||||
|
if (this.selected) {
|
||||||
|
this.classList.add('ueb-selected');
|
||||||
|
this.blueprint.addEventListener('uDragSelected', this.dragHandler);
|
||||||
|
} else {
|
||||||
|
this.classList.remove('ueb-selected');
|
||||||
|
this.blueprint.removeEventListener('uDragSelected', this.dragHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class GraphNode extends SelectableDraggable {
|
||||||
|
|
||||||
|
static fromSerializedObject(str) {
|
||||||
|
let entity = SerializerFactory.getSerializer(ObjectEntity).read(str);
|
||||||
|
return new GraphNode(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(entity) {
|
||||||
|
super(entity, new NodeTemplate());
|
||||||
|
this.graphNodeName = 'n/a';
|
||||||
|
this.inputs = [];
|
||||||
|
this.outputs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.getAttribute('type')?.trim();
|
||||||
|
super.connectedCallback();
|
||||||
|
this.classList.add('ueb-node');
|
||||||
|
if (this.selected) {
|
||||||
|
this.classList.add('ueb-selected');
|
||||||
|
}
|
||||||
|
this.style.setProperty('--ueb-position-x', this.location[0]);
|
||||||
|
this.style.setProperty('--ueb-position-y', this.location[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('u-node', GraphNode);
|
||||||
|
|
||||||
|
class Paste {
|
||||||
|
|
||||||
|
constructor(target, blueprint, options) {
|
||||||
|
/** @type {HTMLElement} */
|
||||||
|
this.target = target;
|
||||||
|
/** @type {import("../Blueprint").default}" */
|
||||||
|
this.blueprint = blueprint;
|
||||||
|
this.serializer = new ObjectSerializer();
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
this.pasteHandler = e => {
|
||||||
|
self.pasted(e.clipboardData.getData("text"));
|
||||||
|
};
|
||||||
|
this.target.addEventListener("paste", this.pasteHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
pasted(value) {
|
||||||
|
let nodes = this.serializer.readMultiple(value).map(entity => new GraphNode(entity));
|
||||||
|
this.blueprint.addNode(...nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlistenDOMElement() {
|
||||||
|
this.target.removeEventListener("paste", this.pasteHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/** @typedef {import("./graph/GraphNode").default} GraphNode */
|
/** @typedef {import("./graph/GraphNode").default} GraphNode */
|
||||||
class Blueprint extends GraphElement {
|
class Blueprint extends GraphElement {
|
||||||
|
|
||||||
@@ -1438,6 +1701,8 @@ class Blueprint extends GraphElement {
|
|||||||
moveEverywhere: true,
|
moveEverywhere: true,
|
||||||
exitAnyButton: true
|
exitAnyButton: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.pasteObject = new Paste(this.getGridDOMElement(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getGridDOMElement() {
|
getGridDOMElement() {
|
||||||
@@ -1448,6 +1713,7 @@ class Blueprint extends GraphElement {
|
|||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this.dragObject.unlistenDOMElement();
|
this.dragObject.unlistenDOMElement();
|
||||||
this.selectObject.unlistenDOMElement();
|
this.selectObject.unlistenDOMElement();
|
||||||
|
this.pasteObject.unlistenDOMElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
getScroll() {
|
getScroll() {
|
||||||
@@ -1666,208 +1932,6 @@ class Blueprint extends GraphElement {
|
|||||||
|
|
||||||
customElements.define('u-blueprint', Blueprint);
|
customElements.define('u-blueprint', Blueprint);
|
||||||
|
|
||||||
class NodeTemplate extends Template {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the html content of the target element.
|
|
||||||
* @param {HTMLElement} entity Entity representing the element
|
|
||||||
* @returns The computed html
|
|
||||||
*/
|
|
||||||
header(entity) {
|
|
||||||
return `
|
|
||||||
<div class="ueb-node-header">
|
|
||||||
<span class="ueb-node-name">
|
|
||||||
<span class="ueb-node-symbol"></span>
|
|
||||||
<span class="ueb-node-text">${entity.graphNodeName}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the html content of the target element.
|
|
||||||
* @param {import("../entity/ObjectEntity").default} entity Entity representing the element
|
|
||||||
* @returns The computed html
|
|
||||||
*/
|
|
||||||
body(entity) {
|
|
||||||
let inputs = entity.CustomProperties.filter(v => v instanceof PinEntity);
|
|
||||||
let outputs = inputs.filter(v => v.isOutput());
|
|
||||||
inputs = inputs.filter(v => !v.isOutput());
|
|
||||||
return `
|
|
||||||
<div class="ueb-node-body">
|
|
||||||
<div class="ueb-node-inputs">
|
|
||||||
${inputs.map((input, index) => `
|
|
||||||
<div class="ueb-node-input ueb-node-value-${input.type}">
|
|
||||||
<span class="ueb-node-value-icon ${inputs[index].connected ? 'ueb-node-value-fill' : ''}"></span>
|
|
||||||
${input.name}
|
|
||||||
</div>
|
|
||||||
`).join("") ?? ""}
|
|
||||||
</div>
|
|
||||||
<div class="ueb-node-outputs">
|
|
||||||
${outputs.map((output, index) => `
|
|
||||||
<div class="ueb-node-output ueb-node-value-${output.type}">
|
|
||||||
${output.name}
|
|
||||||
<span class="ueb-node-value-icon ${outputs[index].connected ? 'ueb-node-value-fill' : ''}"></span>
|
|
||||||
</div>
|
|
||||||
`).join("") ?? ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the html content of the target element.
|
|
||||||
* @param {HTMLElement} entity Entity representing the element
|
|
||||||
* @returns The computed html
|
|
||||||
*/
|
|
||||||
render(entity) {
|
|
||||||
return `
|
|
||||||
<div class="ueb-node-border">
|
|
||||||
<div class="ueb-node-content">
|
|
||||||
${this.header(entity)}
|
|
||||||
${this.body(entity)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Drag extends MouseClickDrag {
|
|
||||||
constructor(target, blueprint, options) {
|
|
||||||
super(target, blueprint, options);
|
|
||||||
this.stepSize = parseInt(options?.stepSize);
|
|
||||||
this.mousePosition = [0, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
snapToGrid(location) {
|
|
||||||
return [
|
|
||||||
this.stepSize * Math.round(location[0] / this.stepSize),
|
|
||||||
this.stepSize * Math.round(location[1] / this.stepSize)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
startDrag() {
|
|
||||||
if (isNaN(this.stepSize) || this.stepSize <= 0) {
|
|
||||||
this.stepSize = parseInt(getComputedStyle(this.target).getPropertyValue('--ueb-grid-snap'));
|
|
||||||
if (isNaN(this.stepSize) || this.stepSize <= 0) {
|
|
||||||
this.stepSize = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get the current mouse position
|
|
||||||
this.mousePosition = this.stepSize != 1 ? this.snapToGrid(this.clickedPosition) : this.clickedPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
dragTo(location, movement) {
|
|
||||||
const mousePosition = this.stepSize != 1 ? this.snapToGrid(location) : location;
|
|
||||||
const d = [mousePosition[0] - this.mousePosition[0], mousePosition[1] - this.mousePosition[1]];
|
|
||||||
|
|
||||||
if (d[0] == 0 && d[1] == 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.target.dragDispatch(d);
|
|
||||||
|
|
||||||
// Reassign the position of mouse
|
|
||||||
this.mousePosition = mousePosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SelectableDraggable extends GraphElement {
|
|
||||||
|
|
||||||
constructor(...args) {
|
|
||||||
super(...args);
|
|
||||||
this.dragObject = null;
|
|
||||||
this.location = [0, 0];
|
|
||||||
this.selected = false;
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
this.dragHandler = (e) => {
|
|
||||||
self.addLocation(e.detail.value);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
this.dragObject = new Drag(this, null, { // UDrag doesn't need blueprint
|
|
||||||
looseTarget: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
this.dragObject.unlistenDOMElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
setLocation(value = [0, 0]) {
|
|
||||||
this.location = value;
|
|
||||||
this.style.setProperty('--ueb-position-x', this.location[0]);
|
|
||||||
this.style.setProperty('--ueb-position-y', this.location[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
addLocation(value) {
|
|
||||||
this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
dragDispatch(value) {
|
|
||||||
if (!this.selected) {
|
|
||||||
this.blueprint.unselectAll();
|
|
||||||
this.setSelected(true);
|
|
||||||
}
|
|
||||||
let dragEvent = new CustomEvent('uDragSelected', {
|
|
||||||
detail: {
|
|
||||||
instigator: this,
|
|
||||||
value: value
|
|
||||||
},
|
|
||||||
bubbles: false,
|
|
||||||
cancelable: true,
|
|
||||||
composed: false,
|
|
||||||
});
|
|
||||||
this.blueprint.dispatchEvent(dragEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelected(value = true) {
|
|
||||||
if (this.selected == value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.selected = value;
|
|
||||||
if (this.selected) {
|
|
||||||
this.classList.add('ueb-selected');
|
|
||||||
this.blueprint.addEventListener('uDragSelected', this.dragHandler);
|
|
||||||
} else {
|
|
||||||
this.classList.remove('ueb-selected');
|
|
||||||
this.blueprint.removeEventListener('uDragSelected', this.dragHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class GraphNode extends SelectableDraggable {
|
|
||||||
|
|
||||||
static fromSerializedObject(str) {
|
|
||||||
let entity = SerializerFactory.getSerializer(ObjectEntity).read(str);
|
|
||||||
return new GraphNode(entity)
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(entity) {
|
|
||||||
super(entity, new NodeTemplate());
|
|
||||||
this.graphNodeName = 'n/a';
|
|
||||||
this.inputs = [];
|
|
||||||
this.outputs = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
this.getAttribute('type')?.trim();
|
|
||||||
super.connectedCallback();
|
|
||||||
this.classList.add('ueb-node');
|
|
||||||
if (this.selected) {
|
|
||||||
this.classList.add('ueb-selected');
|
|
||||||
}
|
|
||||||
this.style.setProperty('--ueb-position-x', this.location[0]);
|
|
||||||
this.style.setProperty('--ueb-position-y', this.location[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('u-node', GraphNode);
|
|
||||||
|
|
||||||
class GraphLink extends GraphElement {
|
class GraphLink extends GraphElement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Select from "./input/Select"
|
|||||||
import Utility from "./Utility"
|
import Utility from "./Utility"
|
||||||
import Zoom from "./input/Zoom"
|
import Zoom from "./input/Zoom"
|
||||||
import BlueprintData from "./BlueprintData"
|
import BlueprintData from "./BlueprintData"
|
||||||
|
import Paste from "./input/Paste"
|
||||||
|
|
||||||
/** @typedef {import("./graph/GraphNode").default} GraphNode */
|
/** @typedef {import("./graph/GraphNode").default} GraphNode */
|
||||||
export default class Blueprint extends GraphElement {
|
export default class Blueprint extends GraphElement {
|
||||||
@@ -84,6 +85,8 @@ export default class Blueprint extends GraphElement {
|
|||||||
moveEverywhere: true,
|
moveEverywhere: true,
|
||||||
exitAnyButton: true
|
exitAnyButton: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.pasteObject = new Paste(this.getGridDOMElement(), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
getGridDOMElement() {
|
getGridDOMElement() {
|
||||||
@@ -94,6 +97,7 @@ export default class Blueprint extends GraphElement {
|
|||||||
super.disconnectedCallback()
|
super.disconnectedCallback()
|
||||||
this.dragObject.unlistenDOMElement()
|
this.dragObject.unlistenDOMElement()
|
||||||
this.selectObject.unlistenDOMElement()
|
this.selectObject.unlistenDOMElement()
|
||||||
|
this.pasteObject.unlistenDOMElement()
|
||||||
}
|
}
|
||||||
|
|
||||||
getScroll() {
|
getScroll() {
|
||||||
|
|||||||
@@ -24,4 +24,12 @@ export default class ObjectEntity extends Entity {
|
|||||||
getAttributes() {
|
getAttributes() {
|
||||||
return ObjectEntity.attributes
|
return ObjectEntity.attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {String} The name of the node
|
||||||
|
*/
|
||||||
|
getNodeDisplayName() {
|
||||||
|
return this.Name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,14 @@ export default class PinEntity extends Entity {
|
|||||||
return PinEntity.attributes
|
return PinEntity.attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
getPinDisplayName() {
|
||||||
|
return this.PinName
|
||||||
|
}
|
||||||
|
|
||||||
isOutput() {
|
isOutput() {
|
||||||
if (this.Direction === "EGPD_Output") {
|
if (this.Direction === "EGPD_Output") {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default class MouseClickDrag extends Pointing {
|
|||||||
const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace
|
const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
this.mouseDownHandler = function (e) {
|
this.mouseDownHandler = e => {
|
||||||
switch (e.button) {
|
switch (e.button) {
|
||||||
case self.clickButton:
|
case self.clickButton:
|
||||||
// Either doesn't matter or consider the click only when clicking on the parent, not descandants
|
// Either doesn't matter or consider the click only when clicking on the parent, not descandants
|
||||||
@@ -37,7 +37,7 @@ export default class MouseClickDrag extends Pointing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mouseStartedMovingHandler = function (e) {
|
this.mouseStartedMovingHandler = e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ export default class MouseClickDrag extends Pointing {
|
|||||||
self.started = true
|
self.started = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mouseMoveHandler = function (e) {
|
this.mouseMoveHandler = e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
const location = self.getLocation(e)
|
const location = self.getLocation(e)
|
||||||
@@ -58,7 +58,7 @@ export default class MouseClickDrag extends Pointing {
|
|||||||
self.dragTo(location, movement)
|
self.dragTo(location, movement)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mouseUpHandler = function (e) {
|
this.mouseUpHandler = e => {
|
||||||
if (!self.exitAnyButton || e.button == self.clickButton) {
|
if (!self.exitAnyButton || e.button == self.clickButton) {
|
||||||
// Remove the handlers of "mousemove" and "mouseup"
|
// Remove the handlers of "mousemove" and "mouseup"
|
||||||
movementListenedElement.removeEventListener('mousemove', self.mouseStartedMovingHandler)
|
movementListenedElement.removeEventListener('mousemove', self.mouseStartedMovingHandler)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default class MouseWheel extends Pointing {
|
|||||||
this.looseTarget = options?.looseTarget ?? true
|
this.looseTarget = options?.looseTarget ?? true
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
this.mouseWheelHandler = function (e) {
|
this.mouseWheelHandler = e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const location = self.getLocation(e)
|
const location = self.getLocation(e)
|
||||||
self.wheel(Math.sign(e.deltaY), location)
|
self.wheel(Math.sign(e.deltaY), location)
|
||||||
|
|||||||
29
js/input/Paste.js
Normal file
29
js/input/Paste.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import GraphNode from "../graph/GraphNode"
|
||||||
|
import ObjectSerializer from "../serialization/ObjectSerializer"
|
||||||
|
|
||||||
|
export default class Paste {
|
||||||
|
|
||||||
|
constructor(target, blueprint, options) {
|
||||||
|
/** @type {HTMLElement} */
|
||||||
|
this.target = target
|
||||||
|
/** @type {import("../Blueprint").default}" */
|
||||||
|
this.blueprint = blueprint
|
||||||
|
this.serializer = new ObjectSerializer()
|
||||||
|
|
||||||
|
let self = this
|
||||||
|
this.pasteHandler = e => {
|
||||||
|
self.pasted(e.clipboardData.getData("text"))
|
||||||
|
}
|
||||||
|
this.target.addEventListener("paste", this.pasteHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
pasted(value) {
|
||||||
|
let nodes = this.serializer.readMultiple(value).map(entity => new GraphNode(entity))
|
||||||
|
this.blueprint.addNode(...nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
unlistenDOMElement() {
|
||||||
|
this.target.removeEventListener("paste", this.pasteHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -29,6 +29,20 @@ export default class ObjectSerializer extends Serializer {
|
|||||||
return parseResult.value
|
return parseResult.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {String} value
|
||||||
|
* @returns {ObjectEntity[]}
|
||||||
|
*/
|
||||||
|
readMultiple(value) {
|
||||||
|
const parseResult = Serializer.grammar.MultipleObject.parse(value)
|
||||||
|
if (!parseResult.status) {
|
||||||
|
console.error("Error when trying to parse the object.")
|
||||||
|
return parseResult
|
||||||
|
}
|
||||||
|
return parseResult.value
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {ObjectEntity} object
|
* @param {ObjectEntity} object
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import PinEntity from "../entity/PinEntity"
|
import PinEntity from "../entity/PinEntity"
|
||||||
import Template from "./Template"
|
import Template from "./Template"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import("../entity/ObjectEntity").default} ObjectEntity
|
||||||
|
*/
|
||||||
export default class NodeTemplate extends Template {
|
export default class NodeTemplate extends Template {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the html content of the target element.
|
* Computes the html content of the target element.
|
||||||
* @param {HTMLElement} entity Entity representing the element
|
* @param {ObjectEntity} entity Entity representing the element
|
||||||
* @returns The computed html
|
* @returns The computed html
|
||||||
*/
|
*/
|
||||||
header(entity) {
|
header(entity) {
|
||||||
@@ -13,7 +16,7 @@ export default class NodeTemplate extends Template {
|
|||||||
<div class="ueb-node-header">
|
<div class="ueb-node-header">
|
||||||
<span class="ueb-node-name">
|
<span class="ueb-node-name">
|
||||||
<span class="ueb-node-symbol"></span>
|
<span class="ueb-node-symbol"></span>
|
||||||
<span class="ueb-node-text">${entity.graphNodeName}</span>
|
<span class="ueb-node-text">${entity.getNodeDisplayName()}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@@ -21,10 +24,11 @@ export default class NodeTemplate extends Template {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the html content of the target element.
|
* Computes the html content of the target element.
|
||||||
* @param {import("../entity/ObjectEntity").default} entity Entity representing the element
|
* @param {ObjectEntity} entity Entity representing the element
|
||||||
* @returns The computed html
|
* @returns The computed html
|
||||||
*/
|
*/
|
||||||
body(entity) {
|
body(entity) {
|
||||||
|
/** @type {PinEntity[]} */
|
||||||
let inputs = entity.CustomProperties.filter(v => v instanceof PinEntity)
|
let inputs = entity.CustomProperties.filter(v => v instanceof PinEntity)
|
||||||
let outputs = inputs.filter(v => v.isOutput())
|
let outputs = inputs.filter(v => v.isOutput())
|
||||||
inputs = inputs.filter(v => !v.isOutput())
|
inputs = inputs.filter(v => !v.isOutput())
|
||||||
@@ -34,14 +38,14 @@ export default class NodeTemplate extends Template {
|
|||||||
${inputs.map((input, index) => `
|
${inputs.map((input, index) => `
|
||||||
<div class="ueb-node-input ueb-node-value-${input.type}">
|
<div class="ueb-node-input ueb-node-value-${input.type}">
|
||||||
<span class="ueb-node-value-icon ${inputs[index].connected ? 'ueb-node-value-fill' : ''}"></span>
|
<span class="ueb-node-value-icon ${inputs[index].connected ? 'ueb-node-value-fill' : ''}"></span>
|
||||||
${input.name}
|
${input.getPinDisplayName()}
|
||||||
</div>
|
</div>
|
||||||
`).join("") ?? ""}
|
`).join("") ?? ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="ueb-node-outputs">
|
<div class="ueb-node-outputs">
|
||||||
${outputs.map((output, index) => `
|
${outputs.map((output, index) => `
|
||||||
<div class="ueb-node-output ueb-node-value-${output.type}">
|
<div class="ueb-node-output ueb-node-value-${output.type}">
|
||||||
${output.name}
|
${output.getPinDisplayName()}
|
||||||
<span class="ueb-node-value-icon ${outputs[index].connected ? 'ueb-node-value-fill' : ''}"></span>
|
<span class="ueb-node-value-icon ${outputs[index].connected ? 'ueb-node-value-fill' : ''}"></span>
|
||||||
</div>
|
</div>
|
||||||
`).join("") ?? ''}
|
`).join("") ?? ''}
|
||||||
@@ -52,7 +56,7 @@ export default class NodeTemplate extends Template {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the html content of the target element.
|
* Computes the html content of the target element.
|
||||||
* @param {HTMLElement} entity Entity representing the element
|
* @param {ObjectEntity} entity Entity representing the element
|
||||||
* @returns The computed html
|
* @returns The computed html
|
||||||
*/
|
*/
|
||||||
render(entity) {
|
render(entity) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default class Template {
|
|||||||
* @returns The computed html
|
* @returns The computed html
|
||||||
*/
|
*/
|
||||||
render(entity) {
|
render(entity) {
|
||||||
return ``
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user