UNPKG

peepee

Version:

Visual Programming Language Where You Connect Ports Of One EventEmitter to Ports Of Another EventEmitter

170 lines (120 loc) 6.18 kB
import { Signal } from "signals"; import { Resizable } from "elements"; import { Component } from "../core/Component.js"; export class PortComponentPlugin { start() {} stop() {} createComponent(attributes, engine) { return new PortComponent(attributes, engine); } } class PortComponent extends Component { constructor(...a) { super(...a); const componentAttributes = { caption: "Port", name: "port", offset: 10, type: 'input', left: 0, top: 0, width: 16, height: 16, fontSize: 10, gap: 5, rx: 2, textAlign: "center", socketRadius: 4, // computed captionTextX: 0, captionTextY: 0, portSocketY:0, portSocketX: null, portCaptionWidth: null, portSocketXCTM:0, portSocketYCTM:0, }; this.installAttributeSignals(componentAttributes, { override: false }); } render(parentComponent, parentElement) { this.element = document.createElementNS("http://www.w3.org/2000/svg", "g"); this.element.setAttribute("id", this.id); this.element.classList.add('port'); // Apply positioning from left, right, top, bottom attributes this.subscriptions.add(() => this.element.remove()); // destroy element on stop this.listenToAttributeSignals(["left", "top"], (left, top) => this.element.setAttribute("transform", `translate(${left}, ${top})`)); // const positionX = parentComponent.attributes.width // ?.combineLatest( this.attributes.offset, this.attributes.type ) // .map((parentWidth, offset, type)=>type=='input'?0-offset:parentWidth+offset); // positionX?.subscribe(x=>console.info('XXX positionX', x)) // Port background const portBg = document.createElementNS("http://www.w3.org/2000/svg", "rect"); portBg.classList.add('port-bg'); this.setAttributeSignal(portBg, "width"); this.setAttributeSignal(portBg, "height"); this.setAttributeSignal(portBg, "rx"); this.element.appendChild(portBg); // Port Socket const portSocket = document.createElementNS("http://www.w3.org/2000/svg", "circle"); this.portSocket = portSocket; this.subscriptions.add(() => portSocket.remove()); // destroy element on stop this.attributes.portSocketX = this.attributes.width.combineLatest(this.attributes.offset,this.attributes.type).map(([width, offset, type])=>type=='input'?0-offset:width+offset); portSocket.classList.add('port-socket'); portSocket.classList.add('station-port'); this.setAttributeSignal(portSocket, "cy", "portSocketY"); this.setAttributeSignal(portSocket, "cx", "portSocketX"); this.listenToAttributeSignals(["socketRadius", "height"], ( socketRadius, height ) => this.attributes.portSocketY.value = height/2 ); this.setAttributeSignal(portSocket, "r", "socketRadius"); portSocket.setAttribute("data-port-id", [this.attributes.group, this.attributes.type, this.attributes.id].join(':')); // portSocket.setAttribute("data-port-name", [this.attributes.type, this.attributes.id].join(':')); portSocket.setAttribute("data-station-id", this.attributes.group); this.listenToAttributeSignals([ "id" ], ( id ) => portSocket.setAttribute("data-port-id", id )); this.listenToAttributeSignals([ "name" ], ( name ) => portSocket.setAttribute("data-port-name", name )); // this.listenToAttributeSignals([ "type", "id" ], ( type, id ) => portSocket.setAttribute("data-port-name", [ type, id ].join(':'))); this.listenToAttributeSignals([ "group" ], ( group ) => portSocket.setAttribute("data-station-id", group )); this.element.appendChild(portSocket); // .map(( [parentWidth, offset, type] ) => ({ parentWidth, offset, type }) ); // positionX?.subscribe(x=>console.info('XXX positionX', x)) // const { e: x, f: y } = port.portElement.getCTM(); // const point = this.engine.clientToWorld(x, y); // port.x.value = point.x; // port.y.value = point.y + 8; // this.attributes.portSocketX.combineLatest(this.attributes.portSocketY).subscribe(()=>{ // // when values change // const { e: x, f: y } = portSocket.getCTM(); // this.attributes.portSocketXCTM.value = x; // this.attributes.portSocketYCTM.value = y; // }) // Port text const portCaption = document.createElementNS("http://www.w3.org/2000/svg", "text"); this.attributes.portCaptionWidth = new Resizable(portCaption); portCaption.classList.add('port-caption'); portCaption.setAttribute("text-anchor", "middle"); this.setAttributeSignal(portCaption, "x", "captionTextX"); this.setAttributeSignal(portCaption, "y", "captionTextY"); this.listenToAttributeSignals(["textAlign", "width"], (textAlign, width) => (this.attributes.captionTextX.value = textAlign === "center" ? width / 2 : 1)); this.listenToAttributeSignals(["height", "fontSize"], (height, fontSize) => (this.attributes.captionTextY.value = height / 2 + fontSize / 3)); this.setAttributeSignal(portCaption, "font-size", "fontSize"); this.listenToAttributeSignals("name", (name) => (portCaption.textContent = name)); this.element.appendChild(portCaption); // Measure the width of the port caption and update the component's width attribute // WARNING: portCaptionWidth is a ResizeObserver in a Signal API: new Resizable(portCaption); // when element resizes, this function will run, and it resizez when it is inserted into dom or text changes // this.listenToAttributeSignals(['caption', 'gap', 'portCaptionWidth'], (caption, gap, {width}) => this.attributes.width.value = gap + width + gap); // Add hover effects this.element.addEventListener("mouseenter", () => { portBg.classList.add('hover'); }); this.element.addEventListener("mouseleave", () => { portBg.classList.remove('hover'); }); this.element.addEventListener("mousedown", () => { portBg.classList.add('active'); }); this.element.addEventListener("mouseup", () => { portBg.classList.remove('active'); }); parentElement.appendChild(this.element); return this.element; } }