diff --git a/connections.js b/connections.js index 6abcfff..d3f69fb 100644 --- a/connections.js +++ b/connections.js @@ -124,6 +124,7 @@ export const connections = function(superClass) { // If not an input port, cancel the connection if (!isInputPort) { + this._dispatchConnectionDragEnd(); this.draggingConnection = null; this.requestUpdate(); } @@ -371,10 +372,10 @@ export const connections = function(superClass) { _handlePortClick(e) { if (this.readonly) return; - const { node, port } = e.detail; + const { node, port, dataTypes } = e.detail; const nodeId = node.id; const { portType, portId, portName } = port; - + if (!this.draggingConnection) { if (portType === 'output') { this.draggingConnection = { @@ -382,8 +383,12 @@ export const connections = function(superClass) { sourcePortId: portId, sourcePortName: portName, sourcePortType: portType, - sourceNode: node + sourceNode: node, + sourceDataTypes: dataTypes || [] }; + + // Notify all nodes that a drag has started — they evaluate their ports. + this._dispatchConnectionDragStart(); } } else { if (portType === 'input' && nodeId !== this.draggingConnection.sourceNodeId) { @@ -397,8 +402,8 @@ export const connections = function(superClass) { return; } - const connectionExists = this.connections.some(conn => - conn.sourceNodeId === this.draggingConnection.sourceNodeId && + const connectionExists = this.connections.some(conn => + conn.sourceNodeId === this.draggingConnection.sourceNodeId && conn.sourcePortId === this.draggingConnection.sourcePortId && conn.targetNodeId === nodeId && conn.targetPortId === portId @@ -414,7 +419,7 @@ export const connections = function(superClass) { targetPortId: portId, targetPortName: portName }; - + this.connections = [...this.connections, connection]; this._dispatchChangeEvent({ @@ -423,11 +428,32 @@ export const connections = function(superClass) { }); } } + + this._dispatchConnectionDragEnd(); this.draggingConnection = null; this.requestUpdate(); } } + _dispatchConnectionDragStart() { + const detail = { + sourceNode: this.draggingConnection.sourceNode, + sourcePortId: this.draggingConnection.sourcePortId, + sourceDataTypes: this.draggingConnection.sourceDataTypes + }; + + // Dispatch on each node directly — bubbling from canvas doesn't reach children. + this.nodes.forEach(node => { + node.dispatchEvent(new CustomEvent('connection-drag-start', { detail, bubbles: false, composed: false })); + }); + } + + _dispatchConnectionDragEnd() { + this.nodes.forEach(node => { + node.dispatchEvent(new CustomEvent('connection-drag-end', { detail: {}, bubbles: false, composed: false })); + }); + } + _calculateSVGBounds() { // If canvas is not ready yet, return default bounds if (!this.canvas) { diff --git a/tp-flow-node-port.js b/tp-flow-node-port.js index 1183fdc..3a16c3c 100644 --- a/tp-flow-node-port.js +++ b/tp-flow-node-port.js @@ -16,16 +16,102 @@ class TpFlowNodePort extends LitElement { position: relative; } + .wrapper { + position: relative; + width: 12px; + height: 12px; + transition: transform 0.15s ease; + } + + /* Glow ring sits behind the shape, works for all shapes */ + .wrapper::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 22px; + height: 22px; + transform: translate(-50%, -50%); + border-radius: 50%; + background: transparent; + transition: background 0.15s ease, box-shadow 0.15s ease; + z-index: -1; + pointer-events: none; + } + .connectionPoint { width: 12px; height: 12px; - background: #666; - border-radius: 50%; + background: var(--port-color, #666); cursor: pointer; + position: relative; } - .connectionPoint:hover { - background: #888; + /* Circle (control signals) */ + .connectionPoint.shape-circle { + border-radius: 50%; + } + + /* Square (market data) */ + .connectionPoint.shape-square { + border-radius: 2px; + } + + /* Diamond (trading signals) */ + .connectionPoint.shape-diamond { + transform: rotate(45deg); + border-radius: 2px; + } + + /* Triangle (computed values) */ + .connectionPoint.shape-triangle { + width: 0; + height: 0; + background: transparent; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 12px solid var(--port-color, #666); + } + + /* Hexagon (wildcard) */ + .connectionPoint.shape-hexagon { + clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); + border-radius: 0; + } + + /* Hover: accent glow ring */ + .wrapper:hover::after { + background: color-mix(in srgb, var(--palette-accent, #3498db) 20%, transparent); + box-shadow: 0 0 0 2px var(--palette-accent, #3498db); + } + + .wrapper:hover .connectionPoint { + filter: brightness(1.2); + } + + :host([dimmed]) .wrapper { + opacity: 0.2; + pointer-events: none; + } + + /* Compatible: scale up + accent glow ring */ + :host([compatible]) .wrapper { + transform: scale(1.4); + } + + :host([compatible]) .wrapper::after { + background: color-mix(in srgb, var(--palette-accent, #3498db) 25%, transparent); + box-shadow: 0 0 0 2px var(--palette-accent, #3498db); + } + + :host([compatible]) .connectionPoint { + filter: brightness(1.15); + } + + /* Compatible + hover: strongest signal */ + :host([compatible]) .wrapper:hover::after { + background: color-mix(in srgb, var(--palette-accent, #3498db) 35%, transparent); + box-shadow: 0 0 0 3px var(--palette-accent, #3498db), 0 0 12px color-mix(in srgb, var(--palette-accent, #3498db) 50%, transparent); } .tag { @@ -57,11 +143,17 @@ class TpFlowNodePort extends LitElement { } render() { - const { showTag, errorMsg, tagContent } = this; + const { showTag, errorMsg, tagContent, shape } = this; return html` -
- +