diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js
index d472a7f..5f20a6c 100644
--- a/dist/ueblueprint.js
+++ b/dist/ueblueprint.js
@@ -675,160 +675,6 @@ class Utility {
}
}
-class Pointing extends Context {
-
- constructor(target, blueprint, options) {
- super(target, blueprint, options);
- this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement;
- }
-
- /**
- *
- * @param {MouseEvent} mouseEvent
- * @returns
- */
- getLocation(mouseEvent) {
- const scaleCorrection = 1 / Utility.getScale(this.target);
- const targetOffset = this.movementSpace.getBoundingClientRect();
- let location = [
- Math.round((mouseEvent.clientX - targetOffset.x) * scaleCorrection),
- Math.round((mouseEvent.clientY - targetOffset.y) * scaleCorrection)
- ];
- return location
- }
-}
-
-/**
- * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses.
- */
-class MouseClickDrag extends Pointing {
-
- constructor(target, blueprint, options) {
- super(target, blueprint, options);
- this.clickButton = options?.clickButton ?? 0;
- this.exitAnyButton = options?.exitAnyButton ?? true;
- this.moveEverywhere = options?.moveEverywhere ?? false;
- this.looseTarget = options?.looseTarget ?? false;
- this.started = false;
- this.clickedPosition = [0, 0];
-
- const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace;
- let self = this;
-
- this.mouseDownHandler = e => {
- this.blueprint.setFocused(true);
- switch (e.button) {
- case self.clickButton:
- // Either doesn't matter or consider the click only when clicking on the parent, not descandants
- if (self.looseTarget || e.target == e.currentTarget) {
- e.stopPropagation();
- self.started = false;
- // Attach the listeners
- movementListenedElement.addEventListener("mousemove", self.mouseStartedMovingHandler);
- document.addEventListener("mouseup", self.mouseUpHandler);
- self.clickedPosition = self.getLocation(e);
- self.clicked(self.clickedPosition);
- }
- break
- default:
- if (!self.exitAnyButton) {
- self.mouseUpHandler(e);
- }
- break
- }
- };
-
- this.mouseStartedMovingHandler = e => {
- e.preventDefault();
- e.stopPropagation();
-
- // Delegate from now on to self.mouseMoveHandler
- movementListenedElement.removeEventListener("mousemove", self.mouseStartedMovingHandler);
- movementListenedElement.addEventListener("mousemove", self.mouseMoveHandler);
-
- // Do actual actions
- self.startDrag();
- self.started = true;
- };
-
- this.mouseMoveHandler = e => {
- e.preventDefault();
- e.stopPropagation();
- const location = self.getLocation(e);
- const movement = [e.movementX, e.movementY];
- self.dragTo(location, movement);
- };
-
- this.mouseUpHandler = e => {
- if (!self.exitAnyButton || e.button == self.clickButton) {
- // Remove the handlers of "mousemove" and "mouseup"
- movementListenedElement.removeEventListener("mousemove", self.mouseStartedMovingHandler);
- movementListenedElement.removeEventListener("mousemove", self.mouseMoveHandler);
- document.removeEventListener("mouseup", self.mouseUpHandler);
- self.endDrag();
- }
- };
-
- this.target.addEventListener("mousedown", this.mouseDownHandler);
- if (this.clickButton == 2) {
- this.target.addEventListener("contextmenu", this.preventDefault);
- }
- }
-
- preventDefault(e) {
- e.preventDefault();
- }
-
- unlistenDOMElement() {
- super.unlistenDOMElement();
- this.target.removeEventListener("mousedown", this.mouseDownHandler);
- if (this.clickButton == 2) {
- this.target.removeEventListener("contextmenu", this.preventDefault);
- } blueprintunfocusHandler;
- }
-
- /* Subclasses will override the following methods */
- clicked(location) {
- }
-
- startDrag() {
- }
-
- dragTo(location, movement) {
- }
-
- endDrag() {
- }
-}
-
-class DragScroll extends MouseClickDrag {
-
- dragTo(location, movement) {
- this.blueprint.scrollDelta([-movement[0], -movement[1]]);
- }
-}
-
-class MouseTracking extends Pointing {
-
- constructor(target, blueprint, options = {}) {
- options.wantsFocusCallback = true;
- super(target, blueprint, options);
-
- let self = this;
- this.mousemoveHandler = e => {
- self.blueprint.entity.mousePosition = self.getLocation(e);
- };
- }
-
- blueprintFocused() {
- this.target.addEventListener("mousemove", this.mousemoveHandler);
- }
-
- blueprintUnfocused() {
- this.target.removeEventListener("mousemove", this.mousemoveHandler);
- }
-}
-
class Entity {
constructor(options = {}) {
@@ -882,6 +728,30 @@ class Entity {
}
}
+class ObjectReferenceEntity extends Entity {
+
+ static attributes = {
+ type: String,
+ path: String
+ }
+
+ getAttributes() {
+ return ObjectReferenceEntity.attributes
+ }
+}
+
+class FunctionReferenceEntity extends Entity {
+
+ static attributes = {
+ MemberParent: ObjectReferenceEntity,
+ MemberName: ""
+ }
+
+ getAttributes() {
+ return FunctionReferenceEntity.attributes
+ }
+}
+
class GuidEntity extends Entity {
static attributes = {
@@ -909,6 +779,35 @@ class GuidEntity extends Entity {
}
}
+class IntegerEntity extends Entity {
+
+ static attributes = {
+ value: Number
+ }
+
+ getAttributes() {
+ return IntegerEntity.attributes
+ }
+
+ constructor(options = {}) {
+ if (options.constructor === Number || options.constructor === String) {
+ options = {
+ value: options
+ };
+ }
+ super(options);
+ this.value = Math.round(this.value);
+ }
+
+ valueOf() {
+ return this.value
+ }
+
+ toString() {
+ return this.value.toString()
+ }
+}
+
class LocalizedTextEntity extends Entity {
static attributes = {
@@ -922,18 +821,6 @@ class LocalizedTextEntity extends Entity {
}
}
-class ObjectReferenceEntity extends Entity {
-
- static attributes = {
- type: String,
- path: String
- }
-
- getAttributes() {
- return ObjectReferenceEntity.attributes
- }
-}
-
class PathSymbolEntity extends Entity {
static attributes = {
@@ -1018,224 +905,6 @@ class PinEntity$1 extends Entity {
}
}
-/**
- * @typedef {import("../graph/SelectableDraggable").default} SelectableDraggable
- */
-class SelectableDraggableTemplate extends Template {
-
- /**
- * Returns the html elements rendered from this template.
- * @param {SelectableDraggable} element Element of the graph
- */
- applyLocation(element) {
- element.style.setProperty("--ueb-position-x", element.location[0]);
- element.style.setProperty("--ueb-position-y", element.location[1]);
- }
-
- /**
- * Returns the html elements rendered from this template.
- * @param {SelectableDraggable} element Element of the graph
- */
- applySelected(element) {
- if (element.selected) {
- element.classList.add("ueb-selected");
- } else {
- element.classList.remove("ueb-selected");
- }
- }
-}
-
-/**
- * @typedef {import("../graph/GraphPin").default} GraphPin
- */
-class PinTemplate extends Template {
-
- /**
- * Computes the html content of the pin.
- * @param {GraphPin} pin Pin entity
- * @returns The result html
- */
- render(pin) {
- if (pin.isInput()) {
- return html`
-
- ${pin.getPinDisplayName()}
- `
- } else {
- return html`
- ${pin.getPinDisplayName()}
-
- `
- }
- }
-
- /**
- * Applies the style to the element.
- * @param {GraphPin} pin Element of the graph
- */
- apply(pin) {
- super.apply(pin);
- pin.classList.add("ueb-node-" + pin.isInput() ? "input" : "output", "ueb-node-value-" + pin.getType());
- }
-}
-
-class GraphPin extends GraphElement {
-
- constructor(entity) {
- super(entity, new PinTemplate());
- /** @type {import("../entity/PinEntity").default} */
- this.entity;
- }
-
- connectedCallback() {
- super.connectedCallback();
- }
-
- /**
- *
- * @returns {String}
- */
- getPinDisplayName() {
- return this.entity.PinName
- }
-
- getAttributes() {
- return PinEntity.attributes
- }
-
- isInput() {
- return this.entity.isInput()
- }
-
- isOutput() {
- return this.entity.isOutput()
- }
-
- isConnected() {
- return this.entity.isConnected()
- }
-
- getType() {
- return this.entity.getType()
- }
-}
-
-customElements.define("ueb-pin", GraphPin);
-
-/**
- * @typedef {import("../graph/GraphNode").default} GraphNode
- */
-class NodeTemplate extends SelectableDraggableTemplate {
-
- /**
- * Computes the html content of the target element.
- * @param {GraphNode} node Graph node element
- * @returns The result html
- */
- header(node) {
- return html`
- `
- }
-
- /**
- * Computes the html content of the target element.
- * @param {GraphNode} node Graph node element
- * @returns The result html
- */
- body(node) {
- let inputs = node.entity.CustomProperties.filter(v => v instanceof PinEntity$1);
- inputs.filter(v => v.isOutput());
- inputs = inputs.filter(v => v.isInput());
- return html`
- `
- }
-
- /**
- * Computes the html content of the target element.
- * @param {GraphNode} node Graph node element
- * @returns The result html
- */
- render(node) {
- return html`
-
- `
- }
-
- /**
- * Returns the html elements rendered from this template.
- * @param {GraphNode} node Element of the graph
- */
- apply(node) {
- super.apply(node);
- if (node.selected) {
- node.classList.add("ueb-selected");
- }
- node.style.setProperty("--ueb-position-x", node.location[0]);
- node.style.setProperty("--ueb-position-y", node.location[1]);
- /** @type {HTMLElement} */
- let inputContainer = node.querySelector(".ueb-node-inputs");
- /** @type {HTMLElement} */
- let outputContainer = node.querySelector(".ueb-node-outputs");
- let pins = node.getPinEntities();
- pins.filter(v => v.isInput()).forEach(v => inputContainer.appendChild(new GraphPin(v)));
- pins.filter(v => v.isOutput()).forEach(v => outputContainer.appendChild(new GraphPin(v)));
- }
-}
-
-class FunctionReferenceEntity extends Entity {
-
- static attributes = {
- MemberParent: ObjectReferenceEntity,
- MemberName: ""
- }
-
- getAttributes() {
- return FunctionReferenceEntity.attributes
- }
-}
-
-class IntegerEntity extends Entity {
-
- static attributes = {
- value: Number
- }
-
- getAttributes() {
- return IntegerEntity.attributes
- }
-
- constructor(options = {}) {
- if (options.constructor === Number || options.constructor === String) {
- options = {
- value: options
- };
- }
- super(options);
- this.value = Math.round(this.value);
- }
-
- valueOf() {
- return this.value
- }
-
- toString() {
- return this.value.toString()
- }
-}
-
class VariableReferenceEntity extends Entity {
static attributes = {
@@ -1280,199 +949,6 @@ class ObjectEntity extends Entity {
}
}
-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.dispatchDragEvent(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;
- /** @type {import("../template/SelectableDraggableTemplate").default} */
- this.template;
-
- let self = this;
- this.dragHandler = (e) => {
- self.addLocation(e.detail.value);
- };
- }
-
- connectedCallback() {
- super.connectedCallback();
- this.dragObject = new Drag(this, this.blueprint, {
- looseTarget: true
- });
- }
-
- disconnectedCallback() {
- this.dragObject.unlistenDOMElement();
- }
-
- setLocation(value = [0, 0]) {
- this.location = value;
- this.template.applyLocation(this);
- }
-
- addLocation(value) {
- this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]);
- }
-
- dispatchDragEvent(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.blueprint.addEventListener("uDragSelected", this.dragHandler);
- } else {
- this.blueprint.removeEventListener("uDragSelected", this.dragHandler);
- }
- this.template.applySelected(this);
- }
-}
-
-class SerializerFactory {
-
- static #serializers = new Map()
-
- static registerSerializer(entity, object) {
- SerializerFactory.#serializers.set(entity, object);
- }
-
- static getSerializer(entity) {
- return SerializerFactory.#serializers.get(Utility.getType(entity))
- }
-}
-
-class DragLink extends MouseClickDrag {
-
- constructor(target, blueprint, options) {
- super(target, blueprint, options);
- }
-
- startDrag() {
- //this.selectorElement.startSelecting(this.clickedPosition)
- }
-
- dragTo(location, movement) {
- //this.selectorElement.doSelecting(location)
- }
-
- endDrag() {
- if (this.started) ;
- }
-}
-
-class GraphNode extends SelectableDraggable {
-
- /**
- *
- * @param {ObjectEntity} entity
- */
- constructor(entity) {
- super(entity, new NodeTemplate());
- /** @type {ObjectEntity} */
- this.entity;
- this.graphNodeName = "n/a";
- this.dragLinkObjects = [];
- super.setLocation([this.entity.NodePosX, this.entity.NodePosY]);
- }
-
- static fromSerializedObject(str) {
- let entity = SerializerFactory.getSerializer(ObjectEntity).read(str);
- return new GraphNode(entity)
- }
-
- /**
- *
- * @returns {PinEntity[]}
- */
- getPinEntities() {
- return this.entity.CustomProperties.filter(v => v instanceof PinEntity$1)
- }
-
- connectedCallback() {
- this.getAttribute("type")?.trim();
- super.connectedCallback();
- this.querySelectorAll(".ueb-node-input, .ueb-node-output").forEach(element => {
- this.dragLinkObjects.push(
- new DragLink(element, this.blueprint, {
- clickButton: 0,
- moveEverywhere: true,
- exitAnyButton: true,
- looseTarget: true
- })
- );
- });
- }
-
- setLocation(value = [0, 0]) {
- let nodeType = this.entity.NodePosX.constructor;
- this.entity.NodePosX = new nodeType(value[0]);
- this.entity.NodePosY = new nodeType(value[1]);
- super.setLocation(value);
- }
-}
-
-customElements.define("ueb-node", GraphNode);
-
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function getDefaultExportFromCjs (x) {
@@ -1659,6 +1135,19 @@ class Grammar {
MultipleObject = r => r.Object.sepBy1(P.whitespace).trim(P.optWhitespace)
}
+class SerializerFactory {
+
+ static #serializers = new Map()
+
+ static registerSerializer(entity, object) {
+ SerializerFactory.#serializers.set(entity, object);
+ }
+
+ static getSerializer(entity) {
+ return SerializerFactory.#serializers.get(Utility.getType(entity))
+ }
+}
+
class Serializer {
static grammar = Parsimmon.createLanguage(new Grammar())
@@ -1788,6 +1277,592 @@ End Object`;
}
}
+class Copy extends Context {
+
+ constructor(target, blueprint, options = {}) {
+ options.wantsFocusCallback = true;
+ super(target, blueprint, options);
+ this.serializer = new ObjectSerializer();
+ let self = this;
+ this.copyHandle = _ => self.copied();
+ }
+
+ blueprintFocused() {
+ document.body.addEventListener("copy", this.copyHandle);
+ }
+
+ blueprintUnfocused() {
+ document.body.removeEventListener("copy", this.copyHandle);
+ }
+
+ copied() {
+ const value = this.blueprint.getNodes(true).map(node => this.serializer.write(node.entity)).join("\n");
+ navigator.clipboard.writeText(value);
+ }
+}
+
+class Pointing extends Context {
+
+ constructor(target, blueprint, options) {
+ super(target, blueprint, options);
+ this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement;
+ }
+
+ /**
+ *
+ * @param {MouseEvent} mouseEvent
+ * @returns
+ */
+ getLocation(mouseEvent) {
+ const scaleCorrection = 1 / Utility.getScale(this.target);
+ const targetOffset = this.movementSpace.getBoundingClientRect();
+ let location = [
+ Math.round((mouseEvent.clientX - targetOffset.x) * scaleCorrection),
+ Math.round((mouseEvent.clientY - targetOffset.y) * scaleCorrection)
+ ];
+ return location
+ }
+}
+
+/**
+ * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses.
+ */
+class MouseClickDrag extends Pointing {
+
+ constructor(target, blueprint, options) {
+ super(target, blueprint, options);
+ this.clickButton = options?.clickButton ?? 0;
+ this.exitAnyButton = options?.exitAnyButton ?? true;
+ this.moveEverywhere = options?.moveEverywhere ?? false;
+ this.looseTarget = options?.looseTarget ?? false;
+ this.started = false;
+ this.clickedPosition = [0, 0];
+
+ const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace;
+ let self = this;
+
+ this.mouseDownHandler = e => {
+ this.blueprint.setFocused(true);
+ switch (e.button) {
+ case self.clickButton:
+ // Either doesn't matter or consider the click only when clicking on the parent, not descandants
+ if (self.looseTarget || e.target == e.currentTarget) {
+ e.stopPropagation();
+ self.started = false;
+ // Attach the listeners
+ movementListenedElement.addEventListener("mousemove", self.mouseStartedMovingHandler);
+ document.addEventListener("mouseup", self.mouseUpHandler);
+ self.clickedPosition = self.getLocation(e);
+ self.clicked(self.clickedPosition);
+ }
+ break
+ default:
+ if (!self.exitAnyButton) {
+ self.mouseUpHandler(e);
+ }
+ break
+ }
+ };
+
+ this.mouseStartedMovingHandler = e => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ // Delegate from now on to self.mouseMoveHandler
+ movementListenedElement.removeEventListener("mousemove", self.mouseStartedMovingHandler);
+ movementListenedElement.addEventListener("mousemove", self.mouseMoveHandler);
+
+ // Do actual actions
+ self.startDrag();
+ self.started = true;
+ };
+
+ this.mouseMoveHandler = e => {
+ e.preventDefault();
+ e.stopPropagation();
+ const location = self.getLocation(e);
+ const movement = [e.movementX, e.movementY];
+ self.dragTo(location, movement);
+ };
+
+ this.mouseUpHandler = e => {
+ if (!self.exitAnyButton || e.button == self.clickButton) {
+ // Remove the handlers of "mousemove" and "mouseup"
+ movementListenedElement.removeEventListener("mousemove", self.mouseStartedMovingHandler);
+ movementListenedElement.removeEventListener("mousemove", self.mouseMoveHandler);
+ document.removeEventListener("mouseup", self.mouseUpHandler);
+ self.endDrag();
+ }
+ };
+
+ this.target.addEventListener("mousedown", this.mouseDownHandler);
+ if (this.clickButton == 2) {
+ this.target.addEventListener("contextmenu", this.preventDefault);
+ }
+ }
+
+ preventDefault(e) {
+ e.preventDefault();
+ }
+
+ unlistenDOMElement() {
+ super.unlistenDOMElement();
+ this.target.removeEventListener("mousedown", this.mouseDownHandler);
+ if (this.clickButton == 2) {
+ this.target.removeEventListener("contextmenu", this.preventDefault);
+ }
+ }
+
+ /* Subclasses will override the following methods */
+ clicked(location) {
+ }
+
+ startDrag() {
+ }
+
+ dragTo(location, movement) {
+ }
+
+ endDrag() {
+ }
+}
+
+class DragScroll extends MouseClickDrag {
+
+ dragTo(location, movement) {
+ this.blueprint.scrollDelta([-movement[0], -movement[1]]);
+ }
+}
+
+class KeyboardShortcut extends Context {
+
+ constructor(target, blueprint, options = {}) {
+ options.wantsFocusCallback = true;
+ super(target, blueprint, options);
+
+ /** @type {String[]} */
+ this.key = this.options.key;
+ this.ctrlKey = options.ctrlKey ?? false;
+ this.shiftKey = options.shiftKey ?? false;
+ this.altKey = options.altKey ?? false;
+ this.metaKey = options.metaKey ?? false;
+
+ let self = this;
+ this.keyDownHandler = e => {
+ if (
+ e.code == self.key
+ && e.ctrlKey === self.ctrlKey
+ && e.shiftKey === self.shiftKey
+ && e.altKey === self.altKey
+ && e.metaKey === self.metaKey
+ ) {
+ self.fire();
+ }
+ };
+ }
+
+ blueprintFocused() {
+ document.addEventListener("keydown", this.keyDownHandler);
+ }
+
+ blueprintUnfocused() {
+ document.removeEventListener("keydown", this.keyDownHandler);
+ }
+
+ fire() {
+ }
+}
+
+class KeyvoardCanc extends KeyboardShortcut {
+
+ constructor(target, blueprint, options = {}) {
+ options.key = "Delete";
+ super(target, blueprint, options);
+ }
+
+ fire() {
+ this.blueprint.deleteNode(...this.blueprint.getNodes(true));
+ }
+}
+
+class MouseTracking extends Pointing {
+
+ constructor(target, blueprint, options = {}) {
+ options.wantsFocusCallback = true;
+ super(target, blueprint, options);
+
+ let self = this;
+ this.mousemoveHandler = e => {
+ self.blueprint.entity.mousePosition = self.getLocation(e);
+ };
+ }
+
+ blueprintFocused() {
+ this.target.addEventListener("mousemove", this.mousemoveHandler);
+ }
+
+ blueprintUnfocused() {
+ this.target.removeEventListener("mousemove", this.mousemoveHandler);
+ }
+}
+
+/**
+ * @typedef {import("../graph/SelectableDraggable").default} SelectableDraggable
+ */
+class SelectableDraggableTemplate extends Template {
+
+ /**
+ * Returns the html elements rendered from this template.
+ * @param {SelectableDraggable} element Element of the graph
+ */
+ applyLocation(element) {
+ element.style.setProperty("--ueb-position-x", element.location[0]);
+ element.style.setProperty("--ueb-position-y", element.location[1]);
+ }
+
+ /**
+ * Returns the html elements rendered from this template.
+ * @param {SelectableDraggable} element Element of the graph
+ */
+ applySelected(element) {
+ if (element.selected) {
+ element.classList.add("ueb-selected");
+ } else {
+ element.classList.remove("ueb-selected");
+ }
+ }
+}
+
+/**
+ * @typedef {import("../graph/GraphPin").default} GraphPin
+ */
+class PinTemplate extends Template {
+
+ /**
+ * Computes the html content of the pin.
+ * @param {GraphPin} pin Pin entity
+ * @returns The result html
+ */
+ render(pin) {
+ if (pin.isInput()) {
+ return html`
+
+ ${pin.getPinDisplayName()}
+ `
+ } else {
+ return html`
+ ${pin.getPinDisplayName()}
+
+ `
+ }
+ }
+
+ /**
+ * Applies the style to the element.
+ * @param {GraphPin} pin Element of the graph
+ */
+ apply(pin) {
+ super.apply(pin);
+ pin.classList.add("ueb-node-" + pin.isInput() ? "input" : "output", "ueb-node-value-" + pin.getType());
+ }
+}
+
+class GraphPin extends GraphElement {
+
+ constructor(entity) {
+ super(entity, new PinTemplate());
+ /** @type {import("../entity/PinEntity").default} */
+ this.entity;
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ }
+
+ /**
+ *
+ * @returns {String}
+ */
+ getPinDisplayName() {
+ return this.entity.PinName
+ }
+
+ getAttributes() {
+ return PinEntity.attributes
+ }
+
+ isInput() {
+ return this.entity.isInput()
+ }
+
+ isOutput() {
+ return this.entity.isOutput()
+ }
+
+ isConnected() {
+ return this.entity.isConnected()
+ }
+
+ getType() {
+ return this.entity.getType()
+ }
+}
+
+customElements.define("ueb-pin", GraphPin);
+
+/**
+ * @typedef {import("../graph/GraphNode").default} GraphNode
+ */
+class NodeTemplate extends SelectableDraggableTemplate {
+
+ /**
+ * Computes the html content of the target element.
+ * @param {GraphNode} node Graph node element
+ * @returns The result html
+ */
+ header(node) {
+ return html`
+ `
+ }
+
+ /**
+ * Computes the html content of the target element.
+ * @param {GraphNode} node Graph node element
+ * @returns The result html
+ */
+ body(node) {
+ let inputs = node.entity.CustomProperties.filter(v => v instanceof PinEntity$1);
+ inputs.filter(v => v.isOutput());
+ inputs = inputs.filter(v => v.isInput());
+ return html`
+ `
+ }
+
+ /**
+ * Computes the html content of the target element.
+ * @param {GraphNode} node Graph node element
+ * @returns The result html
+ */
+ render(node) {
+ return html`
+
+ `
+ }
+
+ /**
+ * Returns the html elements rendered from this template.
+ * @param {GraphNode} node Element of the graph
+ */
+ apply(node) {
+ super.apply(node);
+ if (node.selected) {
+ node.classList.add("ueb-selected");
+ }
+ node.style.setProperty("--ueb-position-x", node.location[0]);
+ node.style.setProperty("--ueb-position-y", node.location[1]);
+ /** @type {HTMLElement} */
+ let inputContainer = node.querySelector(".ueb-node-inputs");
+ /** @type {HTMLElement} */
+ let outputContainer = node.querySelector(".ueb-node-outputs");
+ let pins = node.getPinEntities();
+ pins.filter(v => v.isInput()).forEach(v => inputContainer.appendChild(new GraphPin(v)));
+ pins.filter(v => v.isOutput()).forEach(v => outputContainer.appendChild(new GraphPin(v)));
+ }
+}
+
+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.dispatchDragEvent(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;
+ /** @type {import("../template/SelectableDraggableTemplate").default} */
+ this.template;
+
+ let self = this;
+ this.dragHandler = (e) => {
+ self.addLocation(e.detail.value);
+ };
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.dragObject = new Drag(this, this.blueprint, {
+ looseTarget: true
+ });
+ }
+
+ disconnectedCallback() {
+ this.dragObject.unlistenDOMElement();
+ }
+
+ setLocation(value = [0, 0]) {
+ this.location = value;
+ this.template.applyLocation(this);
+ }
+
+ addLocation(value) {
+ this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]);
+ }
+
+ dispatchDragEvent(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.blueprint.addEventListener("uDragSelected", this.dragHandler);
+ } else {
+ this.blueprint.removeEventListener("uDragSelected", this.dragHandler);
+ }
+ this.template.applySelected(this);
+ }
+}
+
+class DragLink extends MouseClickDrag {
+
+ constructor(target, blueprint, options) {
+ super(target, blueprint, options);
+ }
+
+ startDrag() {
+ //this.selectorElement.startSelecting(this.clickedPosition)
+ }
+
+ dragTo(location, movement) {
+ //this.selectorElement.doSelecting(location)
+ }
+
+ endDrag() {
+ if (this.started) ;
+ }
+}
+
+class GraphNode extends SelectableDraggable {
+
+ /**
+ *
+ * @param {ObjectEntity} entity
+ */
+ constructor(entity) {
+ super(entity, new NodeTemplate());
+ /** @type {ObjectEntity} */
+ this.entity;
+ this.graphNodeName = "n/a";
+ this.dragLinkObjects = [];
+ super.setLocation([this.entity.NodePosX, this.entity.NodePosY]);
+ }
+
+ static fromSerializedObject(str) {
+ let entity = SerializerFactory.getSerializer(ObjectEntity).read(str);
+ return new GraphNode(entity)
+ }
+
+ /**
+ *
+ * @returns {PinEntity[]}
+ */
+ getPinEntities() {
+ return this.entity.CustomProperties.filter(v => v instanceof PinEntity$1)
+ }
+
+ connectedCallback() {
+ this.getAttribute("type")?.trim();
+ super.connectedCallback();
+ this.querySelectorAll(".ueb-node-input, .ueb-node-output").forEach(element => {
+ this.dragLinkObjects.push(
+ new DragLink(element, this.blueprint, {
+ clickButton: 0,
+ moveEverywhere: true,
+ exitAnyButton: true,
+ looseTarget: true
+ })
+ );
+ });
+ }
+
+ setLocation(value = [0, 0]) {
+ let nodeType = this.entity.NodePosX.constructor;
+ this.entity.NodePosX = new nodeType(value[0]);
+ this.entity.NodePosY = new nodeType(value[1]);
+ super.setLocation(value);
+ }
+}
+
+customElements.define("ueb-node", GraphNode);
+
class Paste extends Context {
constructor(target, blueprint, options = {}) {
@@ -1939,30 +2014,6 @@ class Zoom extends MouseWheel {
}
}
-class Copy extends Context {
-
- constructor(target, blueprint, options = {}) {
- options.wantsFocusCallback = true;
- super(target, blueprint, options);
- this.serializer = new ObjectSerializer();
- let self = this;
- this.copyHandle = _ => self.copied();
- }
-
- blueprintFocused() {
- document.body.addEventListener("copy", this.copyHandle);
- }
-
- blueprintUnfocused() {
- document.body.removeEventListener("copy", this.copyHandle);
- }
-
- copied() {
- const value = this.blueprint.getNodes(true).map(node => this.serializer.write(node.entity)).join("\n");
- navigator.clipboard.writeText(value);
- }
-}
-
/** @typedef {import("./graph/GraphNode").default} GraphNode */
class Blueprint extends GraphElement {
@@ -1971,7 +2022,7 @@ class Blueprint extends GraphElement {
/** @type {BlueprintTemplate} */
this.template;
/** @type {GraphNode[]}" */
- this.nodes = new Array();
+ this.nodes = [];
this.expandGridSize = 400;
/** @type {number[]} */
this.additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0];
@@ -1989,8 +2040,6 @@ class Blueprint extends GraphElement {
this.selectorElement = null;
/** @type {HTMLElement} */
this.nodesContainerElement = null;
- this.dragObject = null;
- this.selectObject = null;
/** @type {number} */
this.zoom = 0;
/** @type {HTMLElement} */
@@ -2020,22 +2069,21 @@ class Blueprint extends GraphElement {
this.copyObject = new Copy(this.getGridDOMElement(), this);
this.pasteObject = new Paste(this.getGridDOMElement(), this);
-
- this.dragObject = new DragScroll(this.getGridDOMElement(), this, {
- clickButton: 2,
- moveEverywhere: true,
- exitAnyButton: false
- });
+ this.cancObject = new KeyvoardCanc(this.getGridDOMElement(), this);
this.zoomObject = new Zoom(this.getGridDOMElement(), this, {
looseTarget: true
});
-
this.selectObject = new Select(this.getGridDOMElement(), this, {
clickButton: 0,
moveEverywhere: true,
exitAnyButton: true
});
+ this.dragObject = new DragScroll(this.getGridDOMElement(), this, {
+ clickButton: 2,
+ moveEverywhere: true,
+ exitAnyButton: false
+ });
this.unfocusObject = new Unfocus(this.getGridDOMElement(), this);
this.mouseTrackingObject = new MouseTracking(this.getGridDOMElement(), this);
@@ -2261,12 +2309,32 @@ class Blueprint extends GraphElement {
[...graphNodes].reduce(
(s, e) => {
s.push(e);
+ if (this.nodesContainerElement) {
+ this.nodesContainerElement.appendChild(e);
+ }
return s
},
this.nodes);
- if (this.nodesContainerElement) {
- this.nodesContainerElement.append(...graphNodes);
+ }
+
+ /**
+ *
+ * @param {...GraphNode} graphNodes
+ */
+ deleteNode(...graphNodes) {
+ let deleteNodes = [...graphNodes];
+ if (deleteNodes.length == 0) {
+ return
}
+ let currentDeleteI = 0;
+ this.nodes = this.nodes.filter(v => {
+ if (v == deleteNodes[currentDeleteI]) {
+ ++currentDeleteI;
+ v.remove();
+ return false
+ }
+ return true
+ });
}
setFocused(value = true) {
diff --git a/js/Blueprint.js b/js/Blueprint.js
index 0220b5f..c0ee579 100755
--- a/js/Blueprint.js
+++ b/js/Blueprint.js
@@ -1,14 +1,15 @@
import BlueprintTemplate from "./template/BlueprintTemplate"
+import Copy from "./input/Copy"
import DragScroll from "./input/DragScroll"
import GraphElement from "./graph/GraphElement"
import GraphSelector from "./graph/GraphSelector"
+import KeyboardCanc from "./input/KeyboardCanc"
import MouseTracking from "./input/MouseTracking"
import Paste from "./input/Paste"
import Select from "./input/Select"
import Unfocus from "./input/Unfocus"
import Utility from "./Utility"
import Zoom from "./input/Zoom"
-import Copy from "./input/Copy"
/** @typedef {import("./graph/GraphNode").default} GraphNode */
export default class Blueprint extends GraphElement {
@@ -18,7 +19,7 @@ export default class Blueprint extends GraphElement {
/** @type {BlueprintTemplate} */
this.template
/** @type {GraphNode[]}" */
- this.nodes = new Array()
+ this.nodes = []
this.expandGridSize = 400
/** @type {number[]} */
this.additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0]
@@ -36,8 +37,6 @@ export default class Blueprint extends GraphElement {
this.selectorElement = null
/** @type {HTMLElement} */
this.nodesContainerElement = null
- this.dragObject = null
- this.selectObject = null
/** @type {number} */
this.zoom = 0
/** @type {HTMLElement} */
@@ -67,22 +66,21 @@ export default class Blueprint extends GraphElement {
this.copyObject = new Copy(this.getGridDOMElement(), this)
this.pasteObject = new Paste(this.getGridDOMElement(), this)
-
- this.dragObject = new DragScroll(this.getGridDOMElement(), this, {
- clickButton: 2,
- moveEverywhere: true,
- exitAnyButton: false
- })
+ this.cancObject = new KeyboardCanc(this.getGridDOMElement(), this)
this.zoomObject = new Zoom(this.getGridDOMElement(), this, {
looseTarget: true
})
-
this.selectObject = new Select(this.getGridDOMElement(), this, {
clickButton: 0,
moveEverywhere: true,
exitAnyButton: true
})
+ this.dragObject = new DragScroll(this.getGridDOMElement(), this, {
+ clickButton: 2,
+ moveEverywhere: true,
+ exitAnyButton: false
+ })
this.unfocusObject = new Unfocus(this.getGridDOMElement(), this)
this.mouseTrackingObject = new MouseTracking(this.getGridDOMElement(), this)
@@ -308,12 +306,32 @@ export default class Blueprint extends GraphElement {
[...graphNodes].reduce(
(s, e) => {
s.push(e)
+ if (this.nodesContainerElement) {
+ this.nodesContainerElement.appendChild(e)
+ }
return s
},
this.nodes)
- if (this.nodesContainerElement) {
- this.nodesContainerElement.append(...graphNodes)
+ }
+
+ /**
+ *
+ * @param {...GraphNode} graphNodes
+ */
+ deleteNode(...graphNodes) {
+ let deleteNodes = [...graphNodes]
+ if (deleteNodes.length == 0) {
+ return
}
+ let currentDeleteI = 0
+ this.nodes = this.nodes.filter(v => {
+ if (v == deleteNodes[currentDeleteI]) {
+ ++currentDeleteI
+ v.remove()
+ return false
+ }
+ return true
+ })
}
setFocused(value = true) {
diff --git a/js/BlueprintData.js b/js/BlueprintData.js
deleted file mode 100644
index e69de29..0000000
diff --git a/js/input/KeyboardCanc.js b/js/input/KeyboardCanc.js
new file mode 100644
index 0000000..9734cb2
--- /dev/null
+++ b/js/input/KeyboardCanc.js
@@ -0,0 +1,14 @@
+import KeyboardShortcut from "./KeyboardShortcut";
+
+
+export default class KeyvoardCanc extends KeyboardShortcut {
+
+ constructor(target, blueprint, options = {}) {
+ options.key = "Delete"
+ super(target, blueprint, options)
+ }
+
+ fire() {
+ this.blueprint.deleteNode(...this.blueprint.getNodes(true))
+ }
+}
\ No newline at end of file
diff --git a/js/input/KeyboardShortcut.js b/js/input/KeyboardShortcut.js
index 302b35d..d2b2420 100644
--- a/js/input/KeyboardShortcut.js
+++ b/js/input/KeyboardShortcut.js
@@ -2,7 +2,7 @@ import Context from "./Context"
export default class KeyboardShortcut extends Context {
- constructor(target, blueprint, options) {
+ constructor(target, blueprint, options = {}) {
options.wantsFocusCallback = true
super(target, blueprint, options)
@@ -15,7 +15,6 @@ export default class KeyboardShortcut extends Context {
let self = this
this.keyDownHandler = e => {
- e.preventDefault()
if (
e.code == self.key
&& e.ctrlKey === self.ctrlKey
diff --git a/js/input/MouseClickDrag.js b/js/input/MouseClickDrag.js
index bcf133a..3d6e145 100755
--- a/js/input/MouseClickDrag.js
+++ b/js/input/MouseClickDrag.js
@@ -86,7 +86,7 @@ export default class MouseClickDrag extends Pointing {
this.target.removeEventListener("mousedown", this.mouseDownHandler)
if (this.clickButton == 2) {
this.target.removeEventListener("contextmenu", this.preventDefault)
- } blueprintunfocusHandler
+ }
}
/* Subclasses will override the following methods */