Fixes and improvements
This commit is contained in:
parent
6d77ff13c9
commit
083a25f302
@ -9,16 +9,18 @@ export const connectionStyles = css`
|
|||||||
transition: stroke 0.3s ease, stroke-width 0.3s ease;
|
transition: stroke 0.3s ease, stroke-width 0.3s ease;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
stroke: var(--connection-stroke-color, #999);
|
||||||
|
stroke-width: var(--connection-stroke-width, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.connections path:hover {
|
.connections path:hover {
|
||||||
stroke: #999;
|
stroke: var(--connection-stroke-color-hover, #999);
|
||||||
stroke-width: 3;
|
stroke-width: var(--connection-stroke-width-hover, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.connections path.selected {
|
.connections path.selected {
|
||||||
stroke: #3498db;
|
stroke: var(--connection-stroke-color-selected, #3498db);
|
||||||
stroke-width: 3;
|
stroke-width: var(--connection-stroke-width-selected, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete-button-group {
|
.delete-button-group {
|
||||||
@ -55,13 +57,27 @@ export const connections = function(superClass) {
|
|||||||
this.connections = [];
|
this.connections = [];
|
||||||
this.draggingConnection = null;
|
this.draggingConnection = null;
|
||||||
this.mousePosition = { x: 0, y: 0 };
|
this.mousePosition = { x: 0, y: 0 };
|
||||||
|
this._updatePreviewConnection = this._updatePreviewConnection.bind(this)
|
||||||
|
this._conDocClick = this._conDocClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
this.addEventListener('port-click', this._handlePortClick);
|
super.firstUpdated();
|
||||||
document.addEventListener('mousemove', this._updatePreviewConnection.bind(this));
|
|
||||||
|
|
||||||
document.addEventListener('click', (e) => {
|
this.addEventListener('port-click', this._handlePortClick);
|
||||||
|
document.addEventListener('mousemove', this._updatePreviewConnection);
|
||||||
|
document.addEventListener('click', this._conDocClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
|
||||||
|
this.removeEventListener('port-click', this._handlePortClick);
|
||||||
|
document.removeEventListener('mousemove', this._updatePreviewConnection);
|
||||||
|
document.removeEventListener('click', this._conDocClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
_conDocClick(e) {
|
||||||
// Check if clicking on an output port
|
// Check if clicking on an output port
|
||||||
const path = e.composedPath();
|
const path = e.composedPath();
|
||||||
const isOutputPort = path.some(el =>
|
const isOutputPort = path.some(el =>
|
||||||
@ -91,9 +107,6 @@ export const connections = function(superClass) {
|
|||||||
this.selectedConnection = null;
|
this.selectedConnection = null;
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
super.firstUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_updatePreviewConnection(e) {
|
_updatePreviewConnection(e) {
|
||||||
@ -117,6 +130,7 @@ export const connections = function(superClass) {
|
|||||||
width: ${bounds.width}px;
|
width: ${bounds.width}px;
|
||||||
height: ${bounds.height}px;
|
height: ${bounds.height}px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
` : `
|
` : `
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -124,6 +138,7 @@ export const connections = function(superClass) {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@ -143,7 +158,7 @@ export const connections = function(superClass) {
|
|||||||
if (!sourceNode) return '';
|
if (!sourceNode) return '';
|
||||||
|
|
||||||
const sourcePort = sourceNode.shadowRoot.querySelector(
|
const sourcePort = sourceNode.shadowRoot.querySelector(
|
||||||
`.output-ports [data-port-id="${this.draggingConnection.sourcePortId}"]`
|
`.output-ports [portid="${this.draggingConnection.sourcePortId}"]`
|
||||||
);
|
);
|
||||||
if (!sourcePort) return '';
|
if (!sourcePort) return '';
|
||||||
|
|
||||||
@ -190,8 +205,8 @@ export const connections = function(superClass) {
|
|||||||
|
|
||||||
if (!sourceNode?.shadowRoot || !targetNode?.shadowRoot) return '';
|
if (!sourceNode?.shadowRoot || !targetNode?.shadowRoot) return '';
|
||||||
|
|
||||||
const sourcePort = sourceNode.shadowRoot.querySelector(`.output-ports [data-port-id="${conn.sourcePortId}"]`);
|
const sourcePort = sourceNode.shadowRoot.querySelector(`.output-ports [portid="${conn.sourcePortId}"]`);
|
||||||
const targetPort = targetNode.shadowRoot.querySelector(`.input-ports [data-port-id="${conn.targetPortId}"]`);
|
const targetPort = targetNode.shadowRoot.querySelector(`.input-ports [portid="${conn.targetPortId}"]`);
|
||||||
|
|
||||||
if (!sourcePort || !targetPort) return '';
|
if (!sourcePort || !targetPort) return '';
|
||||||
|
|
||||||
@ -312,18 +327,31 @@ export const connections = function(superClass) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handlePortClick(e) {
|
_handlePortClick(e) {
|
||||||
const { nodeId, portType, portId, portName } = e.detail;
|
const { node, port } = e.detail;
|
||||||
|
const nodeId = node.id;
|
||||||
|
const { portType, portId, portName } = port;
|
||||||
|
|
||||||
if (!this.draggingConnection) {
|
if (!this.draggingConnection) {
|
||||||
if (portType === 'output') {
|
if (portType === 'output') {
|
||||||
this.draggingConnection = {
|
this.draggingConnection = {
|
||||||
sourceNodeId: nodeId,
|
sourceNodeId: nodeId,
|
||||||
sourcePortId: portId,
|
sourcePortId: portId,
|
||||||
sourcePortType: portType
|
sourcePortType: portType,
|
||||||
|
sourceNode: node
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (portType === 'input' && nodeId !== this.draggingConnection.sourceNodeId) {
|
if (portType === 'input' && nodeId !== this.draggingConnection.sourceNodeId) {
|
||||||
|
const targetNode = node;
|
||||||
|
const sourceNode = this.draggingConnection.sourceNode;
|
||||||
|
|
||||||
|
// Validate the connection
|
||||||
|
const isValid = targetNode.validateConnection(sourceNode, this.draggingConnection.sourcePortId, portId);
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const connectionExists = this.connections.some(conn =>
|
const connectionExists = this.connections.some(conn =>
|
||||||
conn.sourceNodeId === this.draggingConnection.sourceNodeId &&
|
conn.sourceNodeId === this.draggingConnection.sourceNodeId &&
|
||||||
conn.sourcePortId === this.draggingConnection.sourcePortId &&
|
conn.sourcePortId === this.draggingConnection.sourcePortId &&
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@tp/tp-flow-nodes",
|
"name": "@tp/tp-flow-nodes",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "tp-flow-nodes.js",
|
"main": "tp-flow-nodes.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -13,6 +13,8 @@
|
|||||||
"author": "trading_peter",
|
"author": "trading_peter",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lit": "^3.0.0"
|
"lit": "^3.0.0",
|
||||||
|
"@tp/helpers": "^2.0.0",
|
||||||
|
"@tp/tp-timeout-strip": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
panning.js
32
panning.js
@ -7,6 +7,8 @@ export const panning = function(superClass) {
|
|||||||
isDragging: { type: Boolean },
|
isDragging: { type: Boolean },
|
||||||
currentX: { type: Number },
|
currentX: { type: Number },
|
||||||
currentY: { type: Number },
|
currentY: { type: Number },
|
||||||
|
currentNodeX: { type: Number },
|
||||||
|
currentNodeY: { type: Number },
|
||||||
initialX: { type: Number },
|
initialX: { type: Number },
|
||||||
initialY: { type: Number },
|
initialY: { type: Number },
|
||||||
xOffset: { type: Number },
|
xOffset: { type: Number },
|
||||||
@ -20,6 +22,8 @@ export const panning = function(superClass) {
|
|||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
this.currentX = 0;
|
this.currentX = 0;
|
||||||
this.currentY = 0;
|
this.currentY = 0;
|
||||||
|
this.currentNodeX = 0;
|
||||||
|
this.currentNodeY = 0;
|
||||||
this.initialX = 0;
|
this.initialX = 0;
|
||||||
this.initialY = 0;
|
this.initialY = 0;
|
||||||
this.xOffset = 0;
|
this.xOffset = 0;
|
||||||
@ -51,8 +55,17 @@ export const panning = function(superClass) {
|
|||||||
_bringToFront(element) {
|
_bringToFront(element) {
|
||||||
if (!(element instanceof TpFlowNode)) return;
|
if (!(element instanceof TpFlowNode)) return;
|
||||||
|
|
||||||
this.highestZIndex++;
|
// Get all elements and filter for TpFlowNode instances
|
||||||
element.style.zIndex = this.highestZIndex;
|
const nodes = Array.from(this.shadowRoot.querySelector('.canvas').children)
|
||||||
|
.filter(node => node instanceof TpFlowNode);
|
||||||
|
|
||||||
|
for (const node of nodes) {
|
||||||
|
node.style.zIndex = 1;
|
||||||
|
node.classList.remove('focused');
|
||||||
|
}
|
||||||
|
|
||||||
|
element.style.zIndex = 2;
|
||||||
|
element.classList.add('focused');
|
||||||
}
|
}
|
||||||
|
|
||||||
_startDrag(e) {
|
_startDrag(e) {
|
||||||
@ -61,6 +74,11 @@ export const panning = function(superClass) {
|
|||||||
if (topNode) {
|
if (topNode) {
|
||||||
this.targetElement = topNode;
|
this.targetElement = topNode;
|
||||||
this._bringToFront(topNode);
|
this._bringToFront(topNode);
|
||||||
|
|
||||||
|
// Check if a element with the "drag-node" attribute is part of the event path. Only then we can start dragging.
|
||||||
|
if (!e.composedPath().some(el => typeof el.hasAttribute === 'function' && el.hasAttribute('drag-node'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.targetElement = this.canvas;
|
this.targetElement = this.canvas;
|
||||||
}
|
}
|
||||||
@ -96,10 +114,10 @@ export const panning = function(superClass) {
|
|||||||
`translate(${this.currentX}px, ${this.currentY}px) scale(${this.scale})`;
|
`translate(${this.currentX}px, ${this.currentY}px) scale(${this.scale})`;
|
||||||
} else {
|
} else {
|
||||||
// For nodes, compensate for canvas scale
|
// For nodes, compensate for canvas scale
|
||||||
this.currentX = (e.clientX - this.initialX) / this.scale;
|
this.currentNodeX = (e.clientX - this.initialX) / this.scale;
|
||||||
this.currentY = (e.clientY - this.initialY) / this.scale;
|
this.currentNodeY = (e.clientY - this.initialY) / this.scale;
|
||||||
this.targetElement.style.transform =
|
this.targetElement.style.transform =
|
||||||
`translate(${this.currentX}px, ${this.currentY}px)`;
|
`translate(${this.currentNodeX}px, ${this.currentNodeY}px)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
@ -113,8 +131,8 @@ export const panning = function(superClass) {
|
|||||||
data: {
|
data: {
|
||||||
nodeId: this.targetElement.id,
|
nodeId: this.targetElement.id,
|
||||||
position: {
|
position: {
|
||||||
x: this.currentX,
|
x: this.currentNodeX,
|
||||||
y: this.currentY
|
y: this.currentNodeY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
109
tp-flow-node-port.js
Normal file
109
tp-flow-node-port.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
@license
|
||||||
|
Copyright (c) 2025 trading_peter
|
||||||
|
This program is available under Apache License Version 2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import '@tp/tp-timeout-strip/tp-timeout-strip.js';
|
||||||
|
import { LitElement, html, css } from 'lit';
|
||||||
|
|
||||||
|
class TpFlowNodePort extends LitElement {
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connectionPoint {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: #666;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connectionPoint:hover {
|
||||||
|
background: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
position: absolute;
|
||||||
|
background: #888;
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([portType="input"]) .tag {
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
transform: translateX(calc(-100% - 10px));
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([portType="output"]) .tag {
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
transform: translateX(calc(100% + 10px));
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(:hover) .tag,
|
||||||
|
.tag[visible] {
|
||||||
|
visibility: visible;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { showTag, errorMsg, tagContent } = this;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="connectionPoint" part="connection-point"></div>
|
||||||
|
|
||||||
|
${showTag || Boolean(tagContent) ? html`
|
||||||
|
<div class="tag" ?visible=${showTag} part="tag">
|
||||||
|
${errorMsg ? html`
|
||||||
|
<div part="tag-error">
|
||||||
|
${errorMsg}
|
||||||
|
</div>
|
||||||
|
` : null}
|
||||||
|
${tagContent ? html`
|
||||||
|
<div class="tag-content" part="tag-content">${tagContent}</div>
|
||||||
|
` : null}
|
||||||
|
<tp-timeout-strip part="tag-timeout" @timeout=${this._onTimeout}></tp-timeout-strip>
|
||||||
|
</div>
|
||||||
|
` : null}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
portType: { type: String, reflect: true },
|
||||||
|
portId: { type: Number, reflect: true },
|
||||||
|
portName: { type: String },
|
||||||
|
tagContent: { type: String },
|
||||||
|
errorMsg: { type: String },
|
||||||
|
showTag: { type: Boolean },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
showConnectionError(msg, timeout = 0) {
|
||||||
|
this.errorMsg = msg;
|
||||||
|
this.showTag = true;
|
||||||
|
|
||||||
|
if (timeout > 0) {
|
||||||
|
this.updateComplete.then(() => {
|
||||||
|
this.shadowRoot.querySelector('tp-timeout-strip').show(timeout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onTimeout() {
|
||||||
|
this.showTag = false;
|
||||||
|
this.errorMsg = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('tp-flow-node-port', TpFlowNodePort);
|
@ -4,6 +4,7 @@ Copyright (c) 2024 trading_peter
|
|||||||
This program is available under Apache License Version 2.0
|
This program is available under Apache License Version 2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import './tp-flow-node-port.js';
|
||||||
import { LitElement, html, css } from 'lit';
|
import { LitElement, html, css } from 'lit';
|
||||||
|
|
||||||
// tp-flow-node.js
|
// tp-flow-node.js
|
||||||
@ -46,24 +47,14 @@ export class TpFlowNode extends LitElement {
|
|||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.port {
|
header {
|
||||||
width: 12px;
|
display: flex;
|
||||||
height: 12px;
|
align-items: center;
|
||||||
background: #666;
|
column-gap: 10px;
|
||||||
border-radius: 50%;
|
padding: 2px 5px;
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.port:hover {
|
|
||||||
background: #888;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete-btn {
|
.delete-btn {
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
right: 5px;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
@ -78,16 +69,17 @@ export class TpFlowNode extends LitElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
|
${this.renderNodeHeader()}
|
||||||
<div class="node-body">
|
<div class="node-body">
|
||||||
<div class="delete-btn" @click="${this._handleDelete}">✕</div>
|
|
||||||
<div class="input-ports">
|
<div class="input-ports">
|
||||||
${this.inputs.map((input, idx) => html`
|
${this.inputs.map((input, idx) => html`
|
||||||
<div class="port"
|
<tp-flow-node-port class="port" exportparts="connectionPoint"
|
||||||
data-port-type="input"
|
portType="input"
|
||||||
data-port-id="${idx}"
|
.portId=${idx}
|
||||||
data-port-name="${input.name}"
|
.portName=${input.name}
|
||||||
|
.tagContent=${input.tagContent}
|
||||||
@mousedown="${this._handlePortClick}">
|
@mousedown="${this._handlePortClick}">
|
||||||
</div>
|
</tp-flow-node-port>
|
||||||
`)}
|
`)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -97,18 +89,28 @@ export class TpFlowNode extends LitElement {
|
|||||||
|
|
||||||
<div class="output-ports">
|
<div class="output-ports">
|
||||||
${this.outputs.map((output, idx) => html`
|
${this.outputs.map((output, idx) => html`
|
||||||
<div class="port"
|
<tp-flow-node-port class="port" exportparts="connectionPoint"
|
||||||
data-port-type="output"
|
portType="output"
|
||||||
data-port-id="${idx}"
|
.portId=${idx}
|
||||||
data-port-name="${output.name}"
|
.portName=${output.name}
|
||||||
|
.tagContent=${output.tagContent}
|
||||||
@mousedown="${this._handlePortClick}">
|
@mousedown="${this._handlePortClick}">
|
||||||
</div>
|
</tp-flow-node-port>
|
||||||
`)}
|
`)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderNodeHeader() {
|
||||||
|
return html`
|
||||||
|
<header drag-node>
|
||||||
|
A Node
|
||||||
|
<div class="delete-btn" @click="${this._handleDelete}">✕</div>
|
||||||
|
</header>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
renderNodeContent() {
|
renderNodeContent() {
|
||||||
console.warn('Your node should override the renderNodeContent method.');
|
console.warn('Your node should override the renderNodeContent method.');
|
||||||
return null;
|
return null;
|
||||||
@ -133,19 +135,17 @@ export class TpFlowNode extends LitElement {
|
|||||||
this.data = {};
|
this.data = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updated(changes) {
|
||||||
|
super.updated(changes);
|
||||||
|
|
||||||
|
this.dispatchEvent(new CustomEvent('update-layout', { detail: this, bubbles: true, composed: true }));
|
||||||
|
}
|
||||||
|
|
||||||
_handlePortClick(e) {
|
_handlePortClick(e) {
|
||||||
e.stopPropagation(); // Prevents the event from getting caught by the panning action.
|
e.stopPropagation(); // Prevents the event from getting caught by the panning action.
|
||||||
|
|
||||||
const portEl = e.target;
|
|
||||||
const detail = {
|
|
||||||
nodeId: this.id,
|
|
||||||
portType: portEl.dataset.portType,
|
|
||||||
portId: portEl.dataset.portId,
|
|
||||||
portName: portEl.dataset.portName
|
|
||||||
};
|
|
||||||
|
|
||||||
this.dispatchEvent(new CustomEvent('port-click', {
|
this.dispatchEvent(new CustomEvent('port-click', {
|
||||||
detail,
|
detail: { node: this, port: e.target },
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true
|
composed: true
|
||||||
}));
|
}));
|
||||||
|
@ -8,6 +8,7 @@ import { LitElement, html, css } from 'lit';
|
|||||||
import { connections, connectionStyles } from './connections.js';
|
import { connections, connectionStyles } from './connections.js';
|
||||||
import { panning } from './panning.js';
|
import { panning } from './panning.js';
|
||||||
import { zoom } from './zoom.js';
|
import { zoom } from './zoom.js';
|
||||||
|
import { repeat } from 'lit/directives/repeat.js';
|
||||||
|
|
||||||
export class TpFlowNodes extends zoom(panning(connections(LitElement))) {
|
export class TpFlowNodes extends zoom(panning(connections(LitElement))) {
|
||||||
static nodeTypes = new Map();
|
static nodeTypes = new Map();
|
||||||
@ -36,10 +37,11 @@ export class TpFlowNodes extends zoom(panning(connections(LitElement))) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using the repeat directive to render the nodes is important here to prevent the nodes from running into a inconsistent data state when nodes are removed.
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="canvas">
|
<div class="canvas">
|
||||||
${this.nodes.map(node => html`${node}`)}
|
${repeat(this.nodes, node => node.id, node => html`${node}`)}
|
||||||
${this._renderConnections()}
|
${this._renderConnections()}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -61,6 +63,9 @@ export class TpFlowNodes extends zoom(panning(connections(LitElement))) {
|
|||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.addEventListener('node-delete-requested', this._boundDeleteHandler);
|
this.addEventListener('node-delete-requested', this._boundDeleteHandler);
|
||||||
|
this.addEventListener('update-layout', () => {
|
||||||
|
this.requestUpdate();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user