wip
This commit is contained in:
168
tp-flow-nodes.js
168
tp-flow-nodes.js
@@ -9,7 +9,9 @@ import { connections, connectionStyles } from './connections.js';
|
||||
import { panning } from './panning.js';
|
||||
import { zoom } from './zoom.js';
|
||||
|
||||
class TpFlowNodes extends zoom(panning(connections(LitElement))) {
|
||||
export class TpFlowNodes extends zoom(panning(connections(LitElement))) {
|
||||
static nodeTypes = new Map();
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
connectionStyles,
|
||||
@@ -53,32 +55,174 @@ class TpFlowNodes extends zoom(panning(connections(LitElement))) {
|
||||
constructor() {
|
||||
super();
|
||||
this.nodes = [];
|
||||
this.previewConnection = null;
|
||||
this._boundDeleteHandler = this._handleNodeDeleteRequested.bind(this);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.addEventListener('node-delete-requested', this._boundDeleteHandler);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.removeEventListener('node-delete-requested', this._boundDeleteHandler);
|
||||
}
|
||||
|
||||
_handleNodeDeleteRequested(e) {
|
||||
const { nodeId } = e.detail;
|
||||
this.removeNode(nodeId);
|
||||
}
|
||||
|
||||
// Export flow chart data
|
||||
exportFlow() {
|
||||
return {
|
||||
nodes: this.nodes.map(node => node.exportData()),
|
||||
connections: this.connections
|
||||
connections: this.connections,
|
||||
canvas: {
|
||||
scale: this.scale,
|
||||
position: {
|
||||
x: this.currentX || 0,
|
||||
y: this.currentY || 0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Import flow chart data
|
||||
importFlow(flowData) {
|
||||
|
||||
async importFlow(flowData) {
|
||||
// Clear existing
|
||||
this.nodes = [];
|
||||
this.connections = [];
|
||||
|
||||
// Create nodes
|
||||
flowData.nodes.forEach(nodeData => {
|
||||
// Create all nodes first
|
||||
const nodes = flowData.nodes.map(nodeData => {
|
||||
const node = this._createNode(nodeData.type);
|
||||
node.importData(nodeData);
|
||||
this.nodes.push(node);
|
||||
return node;
|
||||
});
|
||||
|
||||
// Set nodes and wait for render
|
||||
this.nodes = nodes;
|
||||
await this.updateComplete;
|
||||
|
||||
// Wait for all nodes to be ready
|
||||
await Promise.all(this.nodes.map(node => node.updateComplete));
|
||||
|
||||
// Only after nodes are ready, restore connections
|
||||
this.connections = flowData.connections;
|
||||
|
||||
// Restore canvas state
|
||||
if (flowData.canvas) {
|
||||
if (flowData.canvas.scale) {
|
||||
this._applyZoom(flowData.canvas.scale);
|
||||
}
|
||||
if (flowData.canvas.position) {
|
||||
this.currentX = flowData.canvas.position.x;
|
||||
this.currentY = flowData.canvas.position.y;
|
||||
this.canvas.style.transform =
|
||||
`translate(${this.currentX}px, ${this.currentY}px) scale(${this.scale})`;
|
||||
}
|
||||
}
|
||||
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a node type
|
||||
* @param {string} type Node type identifier
|
||||
* @param {typeof TpFlowNode} nodeClass Node class to register
|
||||
*/
|
||||
static registerNode(type, nodeClass) {
|
||||
TpFlowNodes.nodeTypes.set(type, nodeClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new node instance
|
||||
* @param {string} type Node type identifier
|
||||
* @param {Object} initialData Initial data for the node
|
||||
* @param {number} x Initial x position
|
||||
* @param {number} y Initial y position
|
||||
* @returns {TpFlowNode} The created node instance
|
||||
*/
|
||||
createNode(type, initialData = {}, x = 0, y = 0) {
|
||||
const nodeClass = TpFlowNodes.nodeTypes.get(type);
|
||||
if (!nodeClass) {
|
||||
throw new Error(`Unknown node type: ${type}`);
|
||||
}
|
||||
|
||||
const node = new nodeClass();
|
||||
node.setAttribute('part', 'node');
|
||||
node.setAttribute('exportparts', 'node-content');
|
||||
node.importData({
|
||||
id: `node_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
||||
position: { x, y },
|
||||
data: initialData
|
||||
});
|
||||
|
||||
// Restore connections
|
||||
this.connections = flowData.connections;
|
||||
this.nodes = [...this.nodes, node];
|
||||
|
||||
this._dispatchChangeEvent({
|
||||
type: 'node-added',
|
||||
data: { nodeId: node.id, nodeType: type }
|
||||
});
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
_createNode(type) {
|
||||
const nodeClass = TpFlowNodes.nodeTypes.get(type);
|
||||
if (!nodeClass) {
|
||||
throw new Error(`Unknown node type: ${type}`);
|
||||
}
|
||||
return new nodeClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a node type
|
||||
* @param {string} type Node type identifier
|
||||
* @returns {boolean} True if the type was unregistered, false if it didn't exist
|
||||
*/
|
||||
static unregisterNode(type) {
|
||||
return TpFlowNodes.nodeTypes.delete(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a node by its ID
|
||||
* @param {string} nodeId ID of the node to remove
|
||||
* @returns {boolean} True if the node was found and removed, false otherwise
|
||||
*/
|
||||
removeNode(nodeId) {
|
||||
const initialLength = this.nodes.length;
|
||||
|
||||
// Remove the node
|
||||
this.nodes = this.nodes.filter(node => node.id !== nodeId);
|
||||
|
||||
// Remove any connections associated with this node
|
||||
this.connections = this.connections.filter(conn =>
|
||||
conn.sourceNodeId !== nodeId && conn.targetNodeId !== nodeId
|
||||
);
|
||||
|
||||
// Return true if a node was actually removed
|
||||
const removed = this.nodes.length < initialLength;
|
||||
|
||||
if (removed) {
|
||||
this._dispatchChangeEvent({
|
||||
type: 'node-removed',
|
||||
data: { nodeId }
|
||||
});
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
_dispatchChangeEvent(detail = {}) {
|
||||
this.dispatchEvent(new CustomEvent('flow-changed', {
|
||||
detail: {
|
||||
type: detail.type, // Type of change: 'node-added', 'node-removed', 'node-moved', 'connection-added', etc.
|
||||
data: detail.data, // Additional data specific to the change
|
||||
flow: this.exportFlow() // Current state of the entire flow
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user