dag-builder-js
Advanced tools
Comparing version 0.0.8 to 0.0.9
{ | ||
"name": "dag-builder-js", | ||
"version": "0.0.8", | ||
"version": "0.0.9", | ||
"description": "dag-builder-js is a simple-to-use Javascript DAG library with support to N:N vertices/edges. It supports validating that no cycle can be created in real-time, import/export states and it's built on SVG so you can render graphs pretty much anywhere.", | ||
@@ -9,3 +9,4 @@ "main": "dist/dag.js", | ||
"build": "npm run clean && npx webpack", | ||
"watch": "npm run clean && npx webpack --watch" | ||
"watch": "npm run clean && npx webpack --watch", | ||
"lint": "npx eslint -c .eslintrc.json src/**/*.js" | ||
}, | ||
@@ -36,2 +37,3 @@ "repository": { | ||
"devDependencies": { | ||
"@microsoft/eslint-formatter-sarif": "^2.1.7", | ||
"css-loader": "^6.7.1", | ||
@@ -38,0 +40,0 @@ "eslint": "^8.23.0", |
Directed-Acyclic-Graph-Builder-js (dag-builder-js) | ||
=== | ||
![version](https://img.shields.io/npm/v/dag-builder-js?label=latest%20version) | ||
![downloads](https://img.shields.io/npm/dm/dag-builder-js.svg) | ||
## What is this? | ||
@@ -5,0 +9,0 @@ dag-builder-js is a simple-to-use Javascript DAG library with support to N:N vertices/edges. It supports validating that no cycle can be created in real-time, import/export states and it's built on SVG so you can render graphs pretty much anywhere. |
@@ -8,3 +8,7 @@ /* SPDX-License-Identifier: GPL-3.0-only */ | ||
export { MouseCoordinate, ShapeSize } from "./misc/pojo"; | ||
export { InputVertexConnector, OutputVertexConnector } from "./vertices/connector"; | ||
export { | ||
InputVertexConnector, | ||
CustomInputVertexConnector, | ||
OutputVertexConnector | ||
} from "./vertices/connector"; | ||
@@ -11,0 +15,0 @@ /* import/export */ |
@@ -27,8 +27,7 @@ /* SPDX-License-Identifier: GPL-3.0-only */ | ||
i.vertexHolderB.connector._uuid == uuia_b); | ||
if (!exists){ | ||
if (!exists) { | ||
this.edges.push(edge); | ||
this.triggerEvent(ACTION_TYPE.EDGE_ADDED_ACTION, [edge]); | ||
} | ||
@@ -44,3 +43,3 @@ return !exists; | ||
isInputConnectorUsed(connector) { | ||
if (connector.connectorType !== ConnectorType.INPUT) | ||
if (![ConnectorType.INPUT, ConnectorType.CUSTOM_INPUT].includes(connector.connectorType)) | ||
throw `connector provided isn't of type Input`; | ||
@@ -47,0 +46,0 @@ |
@@ -15,3 +15,15 @@ /* SPDX-License-Identifier: GPL-3.0-only */ | ||
append(vertex) { | ||
this.vertices.push(vertex); | ||
// Add default listeners | ||
const eventsOfInterest = [ | ||
ACTION_TYPE.CUSTOM_INPUT_EDGE_CONN_CLICK_ACTION, | ||
ACTION_TYPE.EDGE_CONN_DRAG_START_ACTION, | ||
ACTION_TYPE.EDGE_CONN_DRAGGING_ACTION, | ||
ACTION_TYPE.EDGE_CONN_DRAG_END_ACTION, | ||
ACTION_TYPE.VERT_DRAGGING_ACTION | ||
]; | ||
this.listeners | ||
.filter((e) => eventsOfInterest.includes(e.type)) | ||
.forEach((e) => vertex.addActionListener(e.type, e.callback, e.params)); | ||
this.vertices.push(vertex); | ||
this.triggerEvent(ACTION_TYPE.VERT_ADDED_ACTION, [vertex]); | ||
@@ -18,0 +30,0 @@ } |
@@ -13,9 +13,12 @@ /* SPDX-License-Identifier: GPL-3.0-only */ | ||
/* Public-facing vertex events */ | ||
/* Public-facing vertex events */ | ||
VERT_ADDED_ACTION: 5, | ||
VERT_REMOVED_ACTION: 6, | ||
/* Public-facing edge events */ | ||
/* Public-facing edge events */ | ||
EDGE_ADDED_ACTION: 7, | ||
EDGE_REMOVED_ACTION: 8, | ||
/* Public-facing edge connector events */ | ||
CUSTOM_INPUT_EDGE_CONN_CLICK_ACTION: 9, | ||
}; | ||
@@ -22,0 +25,0 @@ |
@@ -17,7 +17,7 @@ /* SPDX-License-Identifier: GPL-3.0-only */ | ||
constructor(containerSelector, vertexAddedCallback, vertexRemovedCallback, edgeAddedCallback, edgeRemovedCallback) { | ||
constructor(containerSelector) { | ||
this.containerSelector = containerSelector; | ||
this.svg = null | ||
this.svgMainG = null | ||
this.vertices = new VertexContainer(); | ||
@@ -29,15 +29,2 @@ this.edges = new EdgeContainer(); | ||
/* public-facing listeners */ | ||
if(vertexAddedCallback) | ||
this.vertices.addActionListener(ACTION_TYPE.VERT_ADDED_ACTION, vertexAddedCallback, [this]); | ||
if(vertexRemovedCallback) | ||
this.vertices.addActionListener(ACTION_TYPE.VERT_REMOVED_ACTION, vertexRemovedCallback, [this]); | ||
if(edgeAddedCallback) | ||
this.edges.addActionListener(ACTION_TYPE.EDGE_ADDED_ACTION, edgeAddedCallback, [this]); | ||
if(edgeRemovedCallback) | ||
this.edges.addActionListener(ACTION_TYPE.EDGE_REMOVED_ACTION, edgeRemovedCallback, [this]); | ||
this.init(); | ||
@@ -56,5 +43,26 @@ } | ||
this.addSymbolicDefs(); | ||
this.addListenerDefaults(); | ||
this.addListenerDefaults(); | ||
} | ||
/* public-facing listeners */ | ||
addVertexAddedListener(vertexAddedCallback) { | ||
this.vertices.addActionListener(ACTION_TYPE.VERT_ADDED_ACTION, vertexAddedCallback, [this]); | ||
} | ||
addVertexRemovedListener(vertexRemovedCallback) { | ||
this.vertices.addActionListener(ACTION_TYPE.VERT_REMOVED_ACTION, vertexRemovedCallback, [this]); | ||
} | ||
addEdgeAddedListener(edgeAddedCallback) { | ||
this.edges.addActionListener(ACTION_TYPE.EDGE_ADDED_ACTION, edgeAddedCallback, [this]); | ||
} | ||
addEdgeRemovedListener(edgeRemovedCallback) { | ||
this.edges.addActionListener(ACTION_TYPE.EDGE_REMOVED_ACTION, edgeRemovedCallback, [this]); | ||
} | ||
addCustomInputEdgeConnectorClickedListener(edgeConnectorClickedCallback) { | ||
this.vertices.addActionListener(ACTION_TYPE.CUSTOM_INPUT_EDGE_CONN_CLICK_ACTION, edgeConnectorClickedCallback, [this]); | ||
} | ||
/** | ||
@@ -145,10 +153,11 @@ * Set SVG to container's dimensions | ||
}); | ||
// internal vertex-related events that graph must handle for every vertex | ||
this.vertices.addActionListener(ACTION_TYPE.EDGE_CONN_DRAG_START_ACTION, this.edgeConnectorGenericDragHandler, [this]); | ||
this.vertices.addActionListener(ACTION_TYPE.EDGE_CONN_DRAGGING_ACTION, this.edgeConnectorGenericDragHandler, [this]); | ||
this.vertices.addActionListener(ACTION_TYPE.EDGE_CONN_DRAG_END_ACTION, this.edgeConnectorGenericDragHandler, [this]); | ||
this.vertices.addActionListener(ACTION_TYPE.VERT_DRAGGING_ACTION, this.vertexDragHandler, [this]); | ||
} | ||
appendVertex(vertex) { | ||
vertex.addActionListener(ACTION_TYPE.EDGE_CONN_DRAG_START_ACTION, this.edgeConnectorGenericDragHandler, [this]); | ||
vertex.addActionListener(ACTION_TYPE.EDGE_CONN_DRAGGING_ACTION, this.edgeConnectorGenericDragHandler, [this]); | ||
vertex.addActionListener(ACTION_TYPE.EDGE_CONN_DRAG_END_ACTION, this.edgeConnectorGenericDragHandler, [this]); | ||
vertex.addActionListener(ACTION_TYPE.VERT_DRAGGING_ACTION, this.vertexDragHandler, [this]); | ||
this.vertices.append(vertex); | ||
@@ -155,0 +164,0 @@ } |
@@ -5,12 +5,18 @@ /* SPDX-License-Identifier: GPL-3.0-only */ | ||
import { MouseCoordinate, ShapeSize } from "../misc/pojo"; | ||
import { ConnectorType, InputVertexConnector, OutputVertexConnector } from "../vertices/connector"; | ||
import { | ||
ConnectorType, | ||
InputVertexConnector, | ||
CustomInputVertexConnector, | ||
OutputVertexConnector | ||
} from "../vertices/connector"; | ||
import { Vertex } from "../vertices/vertex"; | ||
export class GraphSerializable { | ||
constructor(vertices, edges){ | ||
constructor(vertices, edges) { | ||
this.vertices = vertices; | ||
this.edges = edges; | ||
} | ||
} | ||
@@ -34,4 +40,18 @@ | ||
this.title, | ||
this.inputs.map((i) => new VertexConnectorSerializable(i.uuid, i.connectorType, i.order, i.name, i.type).deserialize()), | ||
this.outputs.map((i) => new VertexConnectorSerializable(i.uuid, i.connectorType, i.order, i.name, i.type).deserialize()), | ||
this.inputs.map((i) => new VertexConnectorSerializable( | ||
i.uuid, | ||
i.connectorType, | ||
i.order, | ||
i.name, | ||
i.type, | ||
i.customValue, | ||
).deserialize()), | ||
this.outputs.map((i) => new VertexConnectorSerializable( | ||
i.uuid, | ||
i.connectorType, | ||
i.order, | ||
i.name, | ||
i.type, | ||
i.customValue, | ||
).deserialize()), | ||
); | ||
@@ -48,3 +68,3 @@ | ||
constructor(uuid, connectorType, order, name, type) { | ||
constructor(uuid, connectorType, order, name, type, customValue) { | ||
this.uuid = uuid; | ||
@@ -55,10 +75,15 @@ this.connectorType = connectorType; | ||
this.type = type; | ||
this.customValue = customValue; | ||
} | ||
deserialize() { | ||
let connector = null; | ||
if(this.connectorType === ConnectorType.INPUT){ | ||
if (this.connectorType === ConnectorType.INPUT) { | ||
connector = new InputVertexConnector(this.order, this.name, this.type); | ||
}else{ | ||
} else if (this.connectorType === ConnectorType.CUSTOM_INPUT) { | ||
connector = new CustomInputVertexConnector(this.order, this.name, this.type, this.customValue); | ||
} else if (this.connectorType === ConnectorType.OUTPUT) { | ||
connector = new OutputVertexConnector(this.order, this.name, this.type); | ||
} else { // ensuring exaustive search | ||
throw 'Unknown connector type'; | ||
} | ||
@@ -65,0 +90,0 @@ connector.uuid = this.uuid; |
@@ -5,5 +5,5 @@ /* SPDX-License-Identifier: GPL-3.0-only */ | ||
import { TextOverflow } from "../misc/text-overflow"; | ||
import { ACTION_TYPE } from "../events/event"; | ||
import { UniqueComponent } from "./unique-component"; | ||
import { VertexConnectorSerializable } from "../serialization/serialize"; | ||
import { ACTION_TYPE } from "../events/event"; | ||
import * as d3 from "d3"; | ||
@@ -13,3 +13,4 @@ | ||
INPUT: 1, | ||
OUTPUT: 2, | ||
CUSTOM_INPUT: 2, | ||
OUTPUT: 3, | ||
} | ||
@@ -51,2 +52,9 @@ | ||
} | ||
/** | ||
* Abstract method to be implemented on each subclass of EdgeConnector | ||
*/ | ||
getEdgeConnectorConfig() { | ||
throw new Error('You have to implement the method getEdgeConnectorConfig(x, y, maxWidth) in your subclass!'); | ||
} | ||
@@ -93,20 +101,3 @@ draw(drawingContext, x, y, maxWidth) { | ||
// edge connector | ||
let edgeConnector; | ||
if (this.connectorType === ConnectorType.INPUT) { | ||
edgeConnector = { | ||
x: x - (EdgeConnector.EDGE_CONNECTOR_SIZE.width / 2), | ||
y: y - (EdgeConnector.EDGE_CONNECTOR_SIZE.height / 1.5), | ||
class: 'input', | ||
width: EdgeConnector.EDGE_CONNECTOR_SIZE.width, | ||
height: EdgeConnector.EDGE_CONNECTOR_SIZE.height, | ||
} | ||
} else { | ||
edgeConnector = { | ||
x: x + maxWidth - (EdgeConnector.EDGE_CONNECTOR_SIZE.width / 2), | ||
y: y - (EdgeConnector.EDGE_CONNECTOR_SIZE.height / 1.5), | ||
class: 'output', | ||
width: EdgeConnector.EDGE_CONNECTOR_SIZE.width, | ||
height: EdgeConnector.EDGE_CONNECTOR_SIZE.height, | ||
} | ||
} | ||
const edgeConnector = this.getEdgeConnectorConfig(x, y, maxWidth); | ||
@@ -126,3 +117,2 @@ this.edgeConnectorEl = this.drawingContext | ||
this.markNode(this.edgeConnectorEl); | ||
this.setupDragEvents(); | ||
@@ -151,6 +141,6 @@ return this.drawingContext; | ||
this.edgeConnectorEl | ||
.transition() | ||
.duration(intervalInMs) | ||
.attr("stroke-width", minWidth) | ||
.on('end', expandStrokeWidth); | ||
.transition() | ||
.duration(intervalInMs) | ||
.attr("stroke-width", minWidth) | ||
.on('end', expandStrokeWidth); | ||
}; | ||
@@ -161,9 +151,2 @@ | ||
setupDragEvents() { | ||
this.edgeConnectorEl.call(d3.drag() | ||
.on('start', (event) => this.triggerEvent(ACTION_TYPE.EDGE_CONN_DRAG_START_ACTION, [event])) | ||
.on('drag', (event) => this.triggerEvent(ACTION_TYPE.EDGE_CONN_DRAGGING_ACTION, [event])) | ||
.on('end', (event) => this.triggerEvent(ACTION_TYPE.EDGE_CONN_DRAG_END_ACTION, [event]))); | ||
} | ||
selectEdgeConnector(select) { | ||
@@ -192,4 +175,67 @@ this.drawingContext | ||
getEdgeConnectorConfig(x, y) { | ||
return { | ||
x: x - (EdgeConnector.EDGE_CONNECTOR_SIZE.width / 2), | ||
y: y - (EdgeConnector.EDGE_CONNECTOR_SIZE.height / 1.5), | ||
class: 'input', | ||
width: EdgeConnector.EDGE_CONNECTOR_SIZE.width, | ||
height: EdgeConnector.EDGE_CONNECTOR_SIZE.height, | ||
}; | ||
} | ||
} | ||
/** | ||
* Custom Input Vertex Connector fills the void for edge connections in which | ||
* a custom value can be entered instead of linking 2 vertices. | ||
* | ||
* This is meant for a very specific case and I assume that 99% of users should | ||
* be happy enoughwith the regular InputVertexConnector implementation | ||
*/ | ||
export class CustomInputVertexConnector extends InputVertexConnector { | ||
constructor(order, name, type, customValue) { | ||
super(order, name, type); | ||
this.connectorType = ConnectorType.CUSTOM_INPUT; | ||
// container that holds a possible value entered manually | ||
this.customValue = customValue | null; | ||
} | ||
draw(drawingContext, x, y, maxWidth) { | ||
super.draw(drawingContext, x, y, maxWidth); | ||
this.setupEvents(); | ||
return this.drawingContext; | ||
} | ||
setupEvents() { | ||
this.edgeConnectorEl.on('click', (event) => { | ||
event.stopPropagation(); | ||
this.triggerEvent(ACTION_TYPE.CUSTOM_INPUT_EDGE_CONN_CLICK_ACTION, [this, event]) | ||
}); | ||
} | ||
getEdgeConnectorConfig(x, y) { | ||
//TODO change css so that users know straight away that this is a custom input | ||
return { | ||
x: x - (EdgeConnector.EDGE_CONNECTOR_SIZE.width / 2), | ||
y: y - (EdgeConnector.EDGE_CONNECTOR_SIZE.height / 1.5), | ||
class: 'input', | ||
width: EdgeConnector.EDGE_CONNECTOR_SIZE.width, | ||
height: EdgeConnector.EDGE_CONNECTOR_SIZE.height, | ||
}; | ||
} | ||
serialize() { | ||
return new VertexConnectorSerializable( | ||
this._uuid, | ||
this.connectorType, | ||
this.order, | ||
this.name, | ||
this.type, | ||
this.customValue, | ||
); | ||
} | ||
} | ||
export class OutputVertexConnector extends EdgeConnector { | ||
@@ -200,2 +246,25 @@ | ||
} | ||
draw(drawingContext, x, y, maxWidth) { | ||
super.draw(drawingContext, x, y, maxWidth); | ||
this.setupEvents(); | ||
return this.drawingContext; | ||
} | ||
setupEvents() { | ||
this.edgeConnectorEl.call(d3.drag() | ||
.on('start', (event) => this.triggerEvent(ACTION_TYPE.EDGE_CONN_DRAG_START_ACTION, [event])) | ||
.on('drag', (event) => this.triggerEvent(ACTION_TYPE.EDGE_CONN_DRAGGING_ACTION, [event])) | ||
.on('end', (event) => this.triggerEvent(ACTION_TYPE.EDGE_CONN_DRAG_END_ACTION, [event]))); | ||
} | ||
getEdgeConnectorConfig(x, y, maxWidth) { | ||
return { | ||
x: x + maxWidth - (EdgeConnector.EDGE_CONNECTOR_SIZE.width / 2), | ||
y: y - (EdgeConnector.EDGE_CONNECTOR_SIZE.height / 1.5), | ||
class: 'output', | ||
width: EdgeConnector.EDGE_CONNECTOR_SIZE.width, | ||
height: EdgeConnector.EDGE_CONNECTOR_SIZE.height, | ||
}; | ||
} | ||
} |
@@ -8,4 +8,4 @@ /* SPDX-License-Identifier: GPL-3.0-only */ | ||
import { ConnectorType } from "./connector"; | ||
import { VertexSerializable } from "../serialization/serialize"; | ||
import { MouseCoordinate } from "../misc/pojo"; | ||
import { VertexSerializable } from "../serialization/serialize"; | ||
import { MouseCoordinate } from "../misc/pojo"; | ||
import * as d3 from "d3"; | ||
@@ -153,15 +153,17 @@ | ||
drawConnector(connector, next_y) { | ||
// only output connectors can initiate drag events. That ensures that the DAG flows from Output -> Input | ||
const eventsOfInterest = []; | ||
if (connector.connectorType === ConnectorType.OUTPUT) { | ||
// pass down events of interest that might be relevant for this component | ||
const eventsOfInterest = [ | ||
ACTION_TYPE.EDGE_CONN_DRAG_START_ACTION, | ||
ACTION_TYPE.EDGE_CONN_DRAGGING_ACTION, | ||
ACTION_TYPE.EDGE_CONN_DRAG_END_ACTION]; | ||
this.listeners | ||
.filter((e) => eventsOfInterest.includes(e.type)) | ||
.forEach((e) => connector.addActionListener(e.type, e.callback, e.params)); | ||
// only output connectors can initiate drag events. That ensures that the DAG flows from Output -> Input | ||
eventsOfInterest.push(ACTION_TYPE.EDGE_CONN_DRAG_START_ACTION); | ||
eventsOfInterest.push(ACTION_TYPE.EDGE_CONN_DRAGGING_ACTION); | ||
eventsOfInterest.push(ACTION_TYPE.EDGE_CONN_DRAG_END_ACTION); | ||
} else if (connector.connectorType === ConnectorType.CUSTOM_INPUT) { | ||
eventsOfInterest.push(ACTION_TYPE.CUSTOM_INPUT_EDGE_CONN_CLICK_ACTION); | ||
} | ||
// pass down events of interest that might be relevant for this component | ||
this.listeners | ||
.filter((e) => eventsOfInterest.includes(e.type)) | ||
.forEach((e) => connector.addActionListener(e.type, e.callback, e.params)); | ||
// draw | ||
@@ -217,3 +219,3 @@ const wrapper = connector.draw(this.drawingContext, 0, next_y, this.size.width); | ||
serialize() { | ||
serialize() { | ||
const mat = translationMat(this.drawingContext.node()); | ||
@@ -220,0 +222,0 @@ return new VertexSerializable( |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2090839
15910
193
6