123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473 |
- import { GraphNode } from './graphNode';
- import { GraphCanvasComponent, FramePortData } from './graphCanvas';
- import { Nullable } from 'babylonjs/types';
- import { Observer, Observable } from 'babylonjs/Misc/observable';
- import { NodeLink } from './nodeLink';
- import { IFrameData } from '../nodeLocationInfo';
- import { Color3 } from 'babylonjs/Maths/math.color';
- import { NodePort } from './nodePort';
- import { SerializationTools } from '../serializationTools';
- import { StringTools } from '../stringTools';
- import { FrameNodePort } from './frameNodePort';
- enum ResizingDirection {
- Right,
- Left,
- Top,
- Bottom,
- TopRight,
- TopLeft,
- BottomRight,
- BottomLeft
- }
- export enum FramePortPosition {
- Top, Middle, Bottom
- };
- export class GraphFrame {
- private readonly CollapsedWidth = 200;
- private static _FrameCounter = 0;
- private static _FramePortCounter = 0;
- private _name: string;
- private _color: Color3;
- private _x = 0;
- private _y = 0;
- private _gridAlignedX = 0;
- private _gridAlignedY = 0;
- private _width: number;
- private _height: number;
- public element: HTMLDivElement;
- private _borderElement: HTMLDivElement;
- private _headerElement: HTMLDivElement;
- private _headerTextElement: HTMLDivElement;
- private _headerCollapseElement: HTMLDivElement;
- private _headerCloseElement: HTMLDivElement;
- private _commentsElement: HTMLDivElement;
- private _portContainer: HTMLDivElement;
- private _outputPortContainer: HTMLDivElement;
- private _inputPortContainer: HTMLDivElement;
- private _nodes: GraphNode[] = [];
- private _ownerCanvas: GraphCanvasComponent;
- private _mouseStartPointX: Nullable<number> = null;
- private _mouseStartPointY: Nullable<number> = null;
- private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
- private _onGraphNodeRemovalObserver: Nullable<Observer<GraphNode>>;
- private _onExposePortOnFrameObserver: Nullable<Observer<GraphNode>>;
- private _onNodeLinkDisposedObservers: Nullable<Observer<NodeLink>>[] = [];
- private _isCollapsed = false;
- private _frameInPorts: FrameNodePort[] = [];
- private _frameOutPorts: FrameNodePort[] = [];
- private _controlledPorts: NodePort[] = []; // Ports on Nodes that are shown on outside of frame
- private _exposedInPorts: NodePort[] = [];
- private _exposedOutPorts: NodePort[] = [];
- private _id: number;
- private _comments: string;
- private _frameIsResizing: boolean;
- private _resizingDirection: Nullable<ResizingDirection>;
- private _minFrameHeight = 40;
- private _minFrameWidth = 220;
- private mouseXLimit: Nullable<number>;
- public onExpandStateChanged = new Observable<GraphFrame>();
- private readonly CloseSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><g id="Layer_2" data-name="Layer 2"><path d="M16,15l5.85,5.84-1,1L15,15.93,9.15,21.78l-1-1L14,15,8.19,9.12l1-1L15,14l5.84-5.84,1,1Z"/></g></svg>`;
- private readonly ExpandSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><g id="Layer_2" data-name="Layer 2"><path d="M22.31,7.69V22.31H7.69V7.69ZM21.19,8.81H8.81V21.19H21.19Zm-6.75,6.75H11.06V14.44h3.38V11.06h1.12v3.38h3.38v1.12H15.56v3.38H14.44Z"/></g></svg>`;
- private readonly CollapseSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><g id="Layer_2" data-name="Layer 2"><path d="M22.31,7.69V22.31H7.69V7.69ZM21.19,8.81H8.81V21.19H21.19Zm-2.25,6.75H11.06V14.44h7.88Z"/></g></svg>`;
- public get id() {
- return this._id;
- }
- public get isCollapsed() {
- return this._isCollapsed;
- }
- private _createInputPort(port: NodePort, node: GraphNode) {
- let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._inputPortContainer, null, this._ownerCanvas.globalState, true, GraphFrame._FramePortCounter++, this.id);
- this._frameInPorts.push(localPort);
- port.delegatedPort = localPort;
- this._controlledPorts.push(port);
- if(!port.isExposed) {
- port.isExposed = true;
- this._exposedInPorts.push(port);
- }
- }
- // Mark ports with FramePortPosition for re-arrangement support
- private _markFramePortPositions() {
- // mark FrameInPorts
- if(this._frameInPorts.length == 2){
- this._frameInPorts[0].framePortPosition = FramePortPosition.Top;
- this._frameInPorts[1].framePortPosition = FramePortPosition.Bottom;
- } else {
- for(let i = 0; i < this._frameInPorts.length; i++) {
- const port = this._frameInPorts[i];
- if(i === 0){
- port.framePortPosition = FramePortPosition.Top;
- } else if(i === this._frameInPorts.length -1){
- port.framePortPosition = FramePortPosition.Bottom;
- } else {
- port.framePortPosition = FramePortPosition.Middle;
- }
- }
- }
- // mark FrameOutPorts
- if(this._frameOutPorts.length == 2){
- this._frameOutPorts[0].framePortPosition = FramePortPosition.Top;
- this._frameOutPorts[1].framePortPosition = FramePortPosition.Bottom;
- } else {
- for(let i = 0; i < this._frameOutPorts.length; i++) {
- const port = this._frameOutPorts[i];
- if(i === 0){
- port.framePortPosition = FramePortPosition.Top
- } else if(i === this._frameInPorts.length -1){
- port.framePortPosition = FramePortPosition.Bottom
- } else {
- port.framePortPosition = FramePortPosition.Middle
- }
- }
- }
- }
- private _createFramePorts() {
- for (var node of this._nodes) {
- node.isVisible = false;
- }
- for (var port of this._exposedOutPorts) { // Output
- if(port.node === null || port.node.enclosingFrameId != this.id) {
- let index = this._exposedOutPorts.findIndex(nodePort => nodePort === port);
- this._exposedOutPorts.splice(index,1);
- port.isExposed = false;
- }
- else {
- this.createOutputPorts(port, port.node);
- }
- }
- for (var port of this._exposedInPorts) { // Input
- if(port.node === null || port.node.enclosingFrameId != this.id) {
- let index = this._exposedInPorts.findIndex(nodePort => nodePort === port);
- this._exposedInPorts.splice(index,1);
- port.isExposed = false;
- }
- else {
- this.createInputPorts(port, port.node);
- }
- }
- for (var node of this._nodes) {
- for (var port of node.outputPorts) { // Output
- if(!port.isExposed) {
- if(this.createOutputPorts(port,node)) {
- this._exposedOutPorts.push(port);
- port.isExposed = true;
- }
- }
- }
- for (var port of node.inputPorts) { // Input
- if(!port.isExposed){
- this.createInputPorts(port, node);
- }
- }
- }
- }
- private removePortFromExposed(nodeLink: NodeLink, exposedPorts: NodePort[])
- {
- let aPort = exposedPorts.findIndex(nodePort => nodePort === nodeLink.portA);
- let bPort = exposedPorts.findIndex(nodePort => nodePort === nodeLink.portB);
- if(aPort >= 0) {
- exposedPorts.splice(aPort,1);
- nodeLink.portA.isExposed = false;
- } else if(bPort >= 0) {
- exposedPorts.splice(bPort,1);
- if(nodeLink.portB) nodeLink.portB.isExposed = false;
- }
- }
-
- private createInputPorts(port: NodePort, node: GraphNode){
- if (port.connectionPoint.isConnected) {
- for (var link of node.links) {
- if (link.portB === port && this.nodes.indexOf(link.nodeA) === -1) {
- this._createInputPort(port, node);
- link.isVisible = true;
- const onLinkDisposedObserver = link.onDisposedObservable.add((nodeLink: NodeLink) => {
- this.removePortFromExposed(nodeLink, this._exposedInPorts);
- this._redrawFramePorts();
- });
- this._onNodeLinkDisposedObservers.push(onLinkDisposedObserver);
- }
- }
- } else if(port.exposedOnFrame) {
- this._createInputPort(port, node);
- }
- }
- private createOutputPorts(port: NodePort, node: GraphNode){
- if (port.connectionPoint.hasEndpoints) {
- let portAdded = false;
- for (var link of node.links) {
- if (link.portA === port && this.nodes.indexOf(link.nodeB!) === -1 || (link.portA === port && port.exposedOnFrame)) {
- let localPort: FrameNodePort;
- if (!portAdded) {
- portAdded = true;
- localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, link.nodeA!, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
- this._frameOutPorts.push(localPort);
- link.isVisible = true;
- const onLinkDisposedObserver = link.onDisposedObservable.add((nodeLink: NodeLink) => {
- this.removePortFromExposed(nodeLink, this._exposedOutPorts);
- this._redrawFramePorts();
- });
- this._onNodeLinkDisposedObservers.push(onLinkDisposedObserver);
- } else if (this.nodes.indexOf(link.nodeB!) === -1) {
- link.isVisible = true;
- localPort = this.ports.filter(p => p.connectionPoint === port.connectionPoint)[0];
- } else {
- localPort = this.ports.filter(p => p.connectionPoint === port.connectionPoint)[0];
- }
- port.delegatedPort = localPort;
- this._controlledPorts.push(port);
- return true;
- }
- }
- } else if(port.exposedOnFrame) {
- let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
- this._frameOutPorts.push(localPort);
- port.delegatedPort = localPort;
- this._controlledPorts.push(port);
- return true;
- }
- return false
- }
- private _redrawFramePorts() {
- if(!this.isCollapsed) {
- return;
- }
- this._outputPortContainer.innerHTML = "";
- this._inputPortContainer.innerHTML = "";
- this.ports.forEach((framePort:FrameNodePort) => {
- framePort.dispose();
- });
- this._controlledPorts.forEach(port => {
- port.delegatedPort = null;
- port.refresh();
- })
- this._frameInPorts = [];
- this._frameOutPorts = [];
- this._controlledPorts = [];
- this._createFramePorts();
- this.ports.forEach((framePort: FrameNodePort) => framePort.node._refreshLinks());
- }
- public set isCollapsed(value: boolean) {
- if (this._isCollapsed === value) {
- return;
- }
- this._isCollapsed = value;
- this._ownerCanvas._frameIsMoving = true;
- // Need to delegate the outside ports to the frame
- if (value) {
- this.element.classList.add("collapsed");
- this._moveFrame((this.width - this.CollapsedWidth) / 2, 0);
- this._createFramePorts()
- this._markFramePortPositions()
- } else {
- this.element.classList.remove("collapsed");
- this._outputPortContainer.innerHTML = "";
- this._inputPortContainer.innerHTML = "";
- this._frameInPorts.forEach(p => {
- p.dispose();
- });
- this._frameOutPorts.forEach(p => {
- p.dispose();
- });
- this._controlledPorts.forEach(port => {
- port.delegatedPort = null;
- port.refresh();
- })
- this._frameInPorts = [];
- this._frameOutPorts = [];
- this._controlledPorts = [];
- this._onNodeLinkDisposedObservers = [];
- for (var node of this._nodes) {
- node.isVisible = true;
- }
- this._moveFrame(-(this.width - this.CollapsedWidth) / 2, 0);
- }
- this.cleanAccumulation();
- this._ownerCanvas._frameIsMoving = false;
- // UI
- if (this._isCollapsed) {
- this._headerCollapseElement.innerHTML = this.ExpandSVG;
- this._headerCollapseElement.title = "Expand";
- } else {
- this._headerCollapseElement.innerHTML = this.CollapseSVG;
- this._headerCollapseElement.title = "Collapse";
- }
- this.onExpandStateChanged.notifyObservers(this);
- }
- public get nodes() {
- return this._nodes;
- }
- public get ports(){
- return this._frameInPorts.concat(this._frameOutPorts);
- }
- public get name() {
- return this._name;
- }
- public set name(value: string) {
- this._name = value;
- this._headerTextElement.innerHTML = value;
- }
- public get color() {
- return this._color;
- }
- public set color(value: Color3) {
- this._color = value;
- this._headerElement.style.background = `rgba(${value.r * 255}, ${value.g * 255}, ${value.b * 255}, 1)`;
- this._headerElement.style.borderColor = `rgba(${value.r * 255}, ${value.g * 255}, ${value.b * 255}, 1)`;
- this.element.style.background = `rgba(${value.r * 255}, ${value.g * 255}, ${value.b * 255}, 0.7)`;
- }
- public get x() {
- return this._x;
- }
- public set x(value: number) {
- if (this._x === value) {
- return;
- }
- this._x = value;
-
- this._gridAlignedX = this._ownerCanvas.getGridPosition(value);
- this.element.style.left = `${this._gridAlignedX}px`;
- }
- public get y() {
- return this._y;
- }
- public set y(value: number) {
- if (this._y === value) {
- return;
- }
- this._y = value;
- this._gridAlignedY = this._ownerCanvas.getGridPosition(value);
- this.element.style.top = `${this._gridAlignedY}px`;
- }
-
- public get width() {
- return this._width;
- }
- public set width(value: number) {
- if (this._width === value) {
- return;
- }
- let viableWidth = value > this._minFrameWidth ? value : this._minFrameWidth;
- this._width = viableWidth;
-
- var gridAlignedRight = this._ownerCanvas.getGridPositionCeil(viableWidth + this._gridAlignedX);
- this.element.style.width = `${gridAlignedRight - this._gridAlignedX}px`;
- }
- public get height() {
- return this._height;
- }
- public set height(value: number) {
- if (this._height === value) {
- return;
- }
- this._height = value;
- var gridAlignedBottom = this._ownerCanvas.getGridPositionCeil(value + this._gridAlignedY);
- this.element.style.height = `${gridAlignedBottom - this._gridAlignedY}px`;
- }
- public get comments(): string {
- return this._comments;
- }
- public set comments(comments: string) {
- if (comments && !this._comments && comments.length > 0) {
- this.element.style.gridTemplateRows = "40px min-content 1fr";
- this._borderElement.style.gridRow = "1 / span 3";
- this._portContainer.style.gridRow = "3";
- this._commentsElement.classList.add("has-comments");
- } else if (!comments) {
- this.element.style.gridTemplateRows = "40px calc(100% - 40px)";
- this._borderElement.style.gridRow = "1 / span 2";
- this._portContainer.style.gridRow = "2";
- this._commentsElement.classList.remove('has-comments');
- }
- if (comments === "" || (comments && comments.length >= 0)) {
- (this._commentsElement.children[0] as HTMLSpanElement).innerText = comments;
- }
- this.height = this._borderElement.offsetHeight;
- this._comments = comments;
- this.updateMinHeightWithComments();
- }
- public constructor(candidate: Nullable<HTMLDivElement>, canvas: GraphCanvasComponent, doNotCaptureNodes = false) {
- this._id = GraphFrame._FrameCounter++;
- this._ownerCanvas = canvas;
- const root = canvas.frameContainer;
- this.element = root.ownerDocument!.createElement("div");
- this.element.classList.add("frame-box");
- root.appendChild(this.element);
- this._headerElement = root.ownerDocument!.createElement("div");
- this._headerElement.classList.add("frame-box-header");
- this._headerElement.addEventListener("dblclick", () => {
- this.isCollapsed = !this.isCollapsed;
- });
- this.element.appendChild(this._headerElement);
- this._borderElement = root.ownerDocument!.createElement("div");
- this._borderElement.classList.add("frame-box-border");
- this.element.appendChild(this._borderElement);
- // add resizing side handles
- const rightHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
- rightHandle.className = "handle right-handle";
- this.element.appendChild(rightHandle);
- rightHandle.addEventListener("pointerdown", this._onRightHandlePointerDown);
- const leftHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
- leftHandle.className = "handle left-handle";
- this.element.appendChild(leftHandle);
- leftHandle.addEventListener("pointerdown", this._onLeftHandlePointerDown);
- const bottomHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
- bottomHandle.className = "handle bottom-handle";
- this.element.appendChild(bottomHandle);
- bottomHandle.addEventListener("pointerdown", this._onBottomHandlePointerDown);
- const topHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
- topHandle.className = "handle top-handle";
- this.element.appendChild(topHandle);
- topHandle.addEventListener("pointerdown", this._onTopHandlePointerDown);
- const topRightCornerHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
- topRightCornerHandle.className = "handle right-handle top-right-corner-handle";
- this.element.appendChild(topRightCornerHandle);
- topRightCornerHandle.addEventListener("pointerdown", this._onTopRightHandlePointerDown);
- const bottomRightCornerHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
- bottomRightCornerHandle.className = "handle right-handle bottom-right-corner-handle";
- this.element.appendChild(bottomRightCornerHandle);
- bottomRightCornerHandle.addEventListener("pointerdown", this._onBottomRightHandlePointerDown);
- const topLeftCornerHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
- topLeftCornerHandle.className = "handle left-handle top-left-corner-handle";
- this.element.appendChild(topLeftCornerHandle);
- topLeftCornerHandle.addEventListener("pointerdown", this._onTopLeftHandlePointerDown);
- const bottomLeftCornerHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
- bottomLeftCornerHandle.className = "handle left-handle bottom-left-corner-handle";
- this.element.appendChild(bottomLeftCornerHandle);
- bottomLeftCornerHandle.addEventListener("pointerdown", this._onBottomLeftHandlePointerDown);
- // add header elements
- this._headerTextElement = root.ownerDocument!.createElement("div");
- this._headerTextElement.classList.add("frame-box-header-title");
- this._headerElement.appendChild(this._headerTextElement);
- this._headerCollapseElement = root.ownerDocument!.createElement("div");
- this._headerCollapseElement.classList.add("frame-box-header-collapse");
- this._headerCollapseElement.classList.add("frame-box-header-button");
- this._headerCollapseElement.title = "Collapse";
- this._headerCollapseElement.ondragstart= () => false;
- this._headerCollapseElement.addEventListener("pointerdown", (evt) => {
- this._headerCollapseElement.classList.add("down");
- evt.stopPropagation();
- });
- this._headerCollapseElement.addEventListener("pointerup", (evt) => {
- evt.stopPropagation();
- this._headerCollapseElement.classList.remove("down");
- this.isCollapsed = !this.isCollapsed;
- });
- this._headerCollapseElement.innerHTML = this.CollapseSVG;
- this._headerElement.appendChild(this._headerCollapseElement);
- this._headerCloseElement = root.ownerDocument!.createElement("div");
- this._headerCloseElement.classList.add("frame-box-header-close");
- this._headerCloseElement.classList.add("frame-box-header-button");
- this._headerCloseElement.title = "Close";
- this._headerCloseElement.ondragstart= () => false;
- this._headerCloseElement.addEventListener("pointerdown", (evt) => {
- evt.stopPropagation();
- });
- this._headerCloseElement.addEventListener("pointerup", (evt) => {
- evt.stopPropagation();
- this.dispose();
- });
- this._headerCloseElement.innerHTML = this.CloseSVG;
- this._headerElement.appendChild(this._headerCloseElement);
- this._portContainer = root.ownerDocument!.createElement("div");
- this._portContainer.classList.add("port-container");
- this.element.appendChild(this._portContainer);
- this._outputPortContainer = root.ownerDocument!.createElement("div");
- this._outputPortContainer.classList.add("outputsContainer");
- this._portContainer.appendChild(this._outputPortContainer);
- this._inputPortContainer = root.ownerDocument!.createElement("div");
- this._inputPortContainer.classList.add("inputsContainer");
- this._portContainer.appendChild(this._inputPortContainer);
- this.name = "Frame";
- this.color = Color3.FromInts(72, 72, 72);
- if (candidate) {
- this.x = parseFloat(candidate.style.left!.replace("px", ""));
- this.y = parseFloat(candidate.style.top!.replace("px", ""));
- this.width = parseFloat(candidate.style.width!.replace("px", ""));
- this.height = parseFloat(candidate.style.height!.replace("px", ""));
- this.cleanAccumulation();
- }
- this._headerTextElement.addEventListener("pointerdown", evt => this._onDown(evt));
- this._headerTextElement.addEventListener("pointerup", evt => this._onUp(evt));
- this._headerTextElement.addEventListener("pointermove", evt => this._onMove(evt));
- this._onSelectionChangedObserver = canvas.globalState.onSelectionChangedObservable.add(node => {
- if (node === this) {
- this.element.classList.add("selected");
- } else {
- this.element.classList.remove("selected");
- }
- });
- this._onGraphNodeRemovalObserver = canvas.globalState.onGraphNodeRemovalObservable.add((node: GraphNode) => {
- // remove node from this._nodes
- const index = this._nodes.indexOf(node);
- if (index === -1) {
- return;
- } else {
- node.enclosingFrameId = -1;
- this._nodes.splice(index, 1);
- }
- });
- this._onExposePortOnFrameObserver = canvas.globalState.onExposePortOnFrameObservable.add((node: GraphNode) => {
- if (this.nodes.indexOf(node) === -1) {
- return;
- }
- this._redrawFramePorts();
- });
- this._commentsElement = document.createElement('div');
- this._commentsElement.className = 'frame-comments';
- this._commentsElement.style.color = 'white';
- this._commentsElement.style.fontSize = '16px';
- let commentSpan = document.createElement('span');
- commentSpan.className = "frame-comment-span"
- this._commentsElement.appendChild(commentSpan)
- this.element.appendChild(this._commentsElement);
- // Get nodes
- if (!doNotCaptureNodes) {
- this.refresh();
- }
- }
- public refresh() {
- this._nodes = [];
- this._ownerCanvas.globalState.onFrameCreatedObservable.notifyObservers(this);
- }
- public addNode(node: GraphNode) {
- let index = this.nodes.indexOf(node);
- if (index === -1) {
- this.nodes.push(node);
- }
- }
- public removeNode(node: GraphNode) {
- let index = this.nodes.indexOf(node);
- if (index > -1) {
- node.enclosingFrameId = -1;
- this.nodes.splice(index, 1);
- }
- }
- public syncNode(node: GraphNode) {
- if (this.isCollapsed) {
- return;
- }
- if (node.isOverlappingFrame(this)) {
- this.addNode(node);
- } else {
- this.removeNode(node);
- }
- }
- public cleanAccumulation() {
- for (var selectedNode of this._nodes) {
- selectedNode.cleanAccumulation();
- }
- this.x = this._ownerCanvas.getGridPosition(this.x);
- this.y = this._ownerCanvas.getGridPosition(this.y);
- }
- private _onDown(evt: PointerEvent) {
- evt.stopPropagation();
- this._mouseStartPointX = evt.clientX;
- this._mouseStartPointY = evt.clientY;
- this._headerTextElement.setPointerCapture(evt.pointerId);
- this._ownerCanvas.globalState.onSelectionChangedObservable.notifyObservers(this);
- this._ownerCanvas._frameIsMoving = true;
- this.move(this._ownerCanvas.getGridPosition(this.x), this._ownerCanvas.getGridPosition(this.y))
- }
- public move(newX: number, newY: number, align = true) {
- let oldX = this.x;
- let oldY = this.y;
- this.x = newX;
- this.y = newY;
- for (var selectedNode of this._nodes) {
- selectedNode.x += this.x - oldX;
- selectedNode.y += this.y - oldY;
- if (align) {
- selectedNode.cleanAccumulation(true);
- }
- }
- }
- private _onUp(evt: PointerEvent) {
- evt.stopPropagation();
- this.cleanAccumulation();
- this._mouseStartPointX = null;
- this._mouseStartPointY = null;
- this._headerTextElement.releasePointerCapture(evt.pointerId);
- this._ownerCanvas._frameIsMoving = false;
- }
- private _moveFrame(offsetX: number, offsetY: number) {
- this.x += offsetX;
- this.y += offsetY;
- for (var selectedNode of this._nodes) {
- selectedNode.x += offsetX;
- selectedNode.y += offsetY;
- }
- }
- private _onMove(evt: PointerEvent) {
- if (this._mouseStartPointX === null || this._mouseStartPointY === null || evt.ctrlKey || this._frameIsResizing) {
- return;
- }
- let newX = (evt.clientX - this._mouseStartPointX) / this._ownerCanvas.zoom;
- let newY = (evt.clientY - this._mouseStartPointY) / this._ownerCanvas.zoom;
- this._moveFrame(newX, newY);
- this._mouseStartPointX = evt.clientX;
- this._mouseStartPointY = evt.clientY;
- evt.stopPropagation();
- }
- public moveFramePortUp(nodePort: FrameNodePort){
- let elementsArray: ChildNode[];
- if(nodePort.isInput) {
- if(this._inputPortContainer.children.length < 2) {
- return;
- }
- elementsArray = Array.from(this._inputPortContainer.childNodes);
- const indexInContainer = this._frameInPorts.findIndex(framePort => framePort === nodePort);
- [this._exposedInPorts[indexInContainer -1], this._exposedInPorts[indexInContainer]] = [this._exposedInPorts[indexInContainer], this._exposedInPorts[indexInContainer -1]]; // swap idicies
- this._movePortUp(elementsArray, nodePort, this._frameInPorts);
-
- } else {
- if(this._outputPortContainer.children.length < 2) {
- return;
- }
- elementsArray = Array.from(this._outputPortContainer.childNodes);
- const indexInContainer = this._frameOutPorts.findIndex(framePort => framePort === nodePort);
- [this._exposedOutPorts[indexInContainer -1], this._exposedOutPorts[indexInContainer]] = [this._exposedOutPorts[indexInContainer], this._exposedOutPorts[indexInContainer -1]]; // swap idicies
- this._movePortUp(elementsArray, nodePort, this._frameOutPorts);
- }
- this.ports.forEach((framePort: FrameNodePort) => framePort.node._refreshLinks());
- }
- private _movePortUp(elementsArray: ChildNode[], nodePort: FrameNodePort, framePortList: FrameNodePort[]) {
- // update UI
- const indexInElementArray = (elementsArray as HTMLElement[]).findIndex(elem => elem.dataset.framePortId === `${nodePort.framePortId}`)
- if(indexInElementArray === 0){
- return;
- }
- const secondPortElement = elementsArray[indexInElementArray];
- const firstPortElement = elementsArray[indexInElementArray -1];
- firstPortElement.parentElement?.insertBefore(secondPortElement, firstPortElement);
- // update Frame Port Container
- const indexInContainer = framePortList.findIndex(framePort => framePort === nodePort);
- [framePortList[indexInContainer -1], framePortList[indexInContainer]] = [framePortList[indexInContainer], framePortList[indexInContainer -1]]; // swap idicies
-
- //special case framePortList.length == 2
- if(framePortList.length == 2) {
- framePortList[1].framePortPosition = FramePortPosition.Bottom;
- framePortList[0].framePortPosition = FramePortPosition.Top;
- } else {
- // notify nodePort if it is now at Top (indexInElementArray === 1)
- if (indexInElementArray === 1) {
- framePortList[1].framePortPosition = FramePortPosition.Middle;
- framePortList[0].framePortPosition = FramePortPosition.Top;
- } else if(indexInContainer === elementsArray.length-1) {
- framePortList[framePortList.length -1].framePortPosition = FramePortPosition.Bottom;
- framePortList[framePortList.length -2].framePortPosition = FramePortPosition.Middle;
- } else {
- nodePort.framePortPosition = FramePortPosition.Middle;
- }
- }
- }
-
- public moveFramePortDown(nodePort: FrameNodePort){
- let elementsArray: ChildNode[];
- if(nodePort.isInput) {
- if(this._inputPortContainer.children.length < 2) {
- return;
- }
- elementsArray = Array.from(this._inputPortContainer.childNodes);
- const indexInContainer = this._frameInPorts.findIndex(framePort => framePort === nodePort);
- [this._exposedInPorts[indexInContainer], this._exposedInPorts[indexInContainer + 1]] = [this._exposedInPorts[indexInContainer + 1], this._exposedInPorts[indexInContainer]]; // swap idicies
- this._movePortDown(elementsArray, nodePort, this._frameInPorts);
-
- } else {
- if(this._outputPortContainer.children.length < 2) {
- return;
- }
- elementsArray = Array.from(this._outputPortContainer.childNodes);
- const indexInContainer = this._frameOutPorts.findIndex(framePort => framePort === nodePort);
- [this._exposedOutPorts[indexInContainer], this._exposedOutPorts[indexInContainer + 1]] = [this._exposedOutPorts[indexInContainer + 1], this._exposedOutPorts[indexInContainer]]; // swap idicies
- this._movePortDown(elementsArray, nodePort, this._frameOutPorts);
- }
- this.ports.forEach((framePort: FrameNodePort) => framePort.node._refreshLinks());
- }
- private _movePortDown(elementsArray: ChildNode[], nodePort: FrameNodePort, framePortList: FrameNodePort[]) {
- // update UI
- const indexInElementArray = (elementsArray as HTMLElement[]).findIndex(elem => elem.dataset.framePortId === `${nodePort.framePortId}`)
- if(indexInElementArray === elementsArray.length -1){
- return;
- }
- const firstPort = elementsArray[indexInElementArray];
- const secondPort = elementsArray[indexInElementArray + 1];
- firstPort.parentElement?.insertBefore(secondPort, firstPort);
- // update Frame Port Container
- const indexInContainer = framePortList.findIndex(framePort => framePort === nodePort);
- [framePortList[indexInContainer], framePortList[indexInContainer + 1]] = [framePortList[indexInContainer + 1], framePortList[indexInContainer]]; // swap idicies
- // notify nodePort if it is now at bottom (indexInContainer === elementsArray.length-2)
- if(framePortList.length == 2) {
- framePortList[0].framePortPosition = FramePortPosition.Top;
- framePortList[1].framePortPosition = FramePortPosition.Bottom;
- } else {
- if(indexInContainer === elementsArray.length-2) {
- framePortList[elementsArray.length-2].framePortPosition = FramePortPosition.Middle;
- framePortList[elementsArray.length-1].framePortPosition = FramePortPosition.Bottom;
- } else if(indexInContainer === 0){
- framePortList[0].framePortPosition = FramePortPosition.Top;
- framePortList[1].framePortPosition = FramePortPosition.Middle;
- } else {
- nodePort.framePortPosition = FramePortPosition.Middle;
- }
- }
- }
- private initResizing = (evt: PointerEvent) => {
- evt.stopPropagation();
- this._mouseStartPointX = evt.clientX;
- this._mouseStartPointY = evt.clientY;
- this._frameIsResizing = true;
- }
- private cleanUpResizing = (evt: PointerEvent) => {
- evt.stopPropagation();
- this._frameIsResizing = false;
- this._resizingDirection = null;
- this._mouseStartPointX = null;
- this._mouseStartPointY = null;
- this.mouseXLimit = null;
- this.refresh();
- }
- private updateMinHeightWithComments = () => {
- if (this.comments && this.comments.length > 0) {
- const minFrameHeightWithComments = this._commentsElement.offsetHeight + 40;
- this._minFrameHeight = minFrameHeightWithComments;
- }
- }
- private _isResizingTop(){
- return this._resizingDirection === ResizingDirection.Top || this._resizingDirection === ResizingDirection.TopRight || this._resizingDirection === ResizingDirection.TopLeft;
- }
- private _isResizingRight(){
- return this._resizingDirection === ResizingDirection.Right || this._resizingDirection === ResizingDirection.TopRight || this._resizingDirection === ResizingDirection.BottomRight;
- }
- private _isResizingBottom(){
- return this._resizingDirection === ResizingDirection.Bottom || this._resizingDirection === ResizingDirection.BottomLeft || this._resizingDirection === ResizingDirection.BottomRight;
- }
- private _isResizingLeft() {
- return this._resizingDirection === ResizingDirection.Left || this._resizingDirection === ResizingDirection.TopLeft || this._resizingDirection === ResizingDirection.BottomLeft;
- }
- private _onRightHandlePointerDown = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.isCollapsed) {
- return;
- }
- this.initResizing(evt);
- _this._resizingDirection = ResizingDirection.Right;
- _this.mouseXLimit = evt.clientX - (_this.width - _this._minFrameWidth);
- _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onRightHandlePointerUp);
- _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onRightHandlePointerMove);
- }
- private _onRightHandlePointerMove = (evt: PointerEvent) => {
- const slack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
- const xLimit = (this._mouseStartPointX as number) - slack;
- this._moveRightHandle(evt, xLimit);
- }
- private _moveRightHandle = (evt: PointerEvent, xLimit: number) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.mouseXLimit) {
- if (!_this._isResizingRight() || _this._mouseStartPointX === null || _this._mouseStartPointY === null || evt.clientX < xLimit) {
- return;
- }
- if (_this._isResizingRight()) {
- evt.stopPropagation();
- const distanceMouseMoved = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandRight(distanceMouseMoved, evt.clientX);
- _this._mouseStartPointX = evt.clientX;
- }
- }
- }
- private _onRightHandlePointerUp = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this._isResizingRight()) {
- _this.width = parseFloat(_this.element.style.width.replace("px", ""));
- _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onRightHandlePointerUp);
- _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onRightHandlePointerMove);
- _this.cleanUpResizing(evt);
- }
- }
- private _onBottomHandlePointerDown = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.isCollapsed) {
- return;
- }
- _this.initResizing(evt);
- _this._resizingDirection = ResizingDirection.Bottom;
- _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onBottomHandlePointerMove);
- _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onBottomHandlePointerUp);
- }
- private _onBottomHandlePointerMove = (evt: PointerEvent) => {
- const slack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
- const yLimit = (this._mouseStartPointY as number) - slack;
- this._moveBottomHandle(evt, yLimit);
- }
- private _moveBottomHandle = (evt: PointerEvent, yLimit: number) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this._resizingDirection !== ResizingDirection.Bottom || _this._mouseStartPointX === null || _this._mouseStartPointY === null || evt.clientY < yLimit) {
- return;
- }
- if (_this._resizingDirection === ResizingDirection.Bottom) {
- evt.stopPropagation();
- const distanceMouseMoved = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandBottom(distanceMouseMoved);
- _this._mouseStartPointY = evt.clientY;
- }
- }
- private _onBottomHandlePointerUp = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this._resizingDirection === ResizingDirection.Bottom) {
- _this.height = parseFloat(_this.element.style.height.replace("px", ""));
- _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onBottomHandlePointerMove);
- _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onBottomHandlePointerUp);
- _this.cleanUpResizing(evt);
- }
- }
- private _onLeftHandlePointerDown = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.isCollapsed) {
- return;
- }
- _this.initResizing(evt);
- _this._resizingDirection = ResizingDirection.Left;
- _this.mouseXLimit = evt.clientX + _this.width - _this._minFrameWidth;
- _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onLeftHandlePointerUp);
- _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onLeftHandlePointerMove);
- }
- private _onLeftHandlePointerMove = (evt: PointerEvent) => {
- const slack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
- const xLimit = (this._mouseStartPointX as number) + slack;
- this._moveLeftHandle(evt, xLimit);
- }
- private _moveLeftHandle = (evt: PointerEvent, xLimit: number) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.mouseXLimit) {
- if (_this._resizingDirection !== ResizingDirection.Left || _this._mouseStartPointX === null || _this._mouseStartPointY === null || evt.clientX > xLimit) {
- return;
- }
- if (_this._resizingDirection === ResizingDirection.Left) {
- evt.stopPropagation();
- const distanceMouseMoved = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandLeft(distanceMouseMoved);
- _this._mouseStartPointX = evt.clientX;
- }
- }
- }
- private _onLeftHandlePointerUp = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this._resizingDirection === ResizingDirection.Left) {
- _this.x = parseFloat(_this.element.style.left!.replace("px", ""));
- _this.width = parseFloat(_this.element.style.width.replace("px", ""));
- _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onLeftHandlePointerUp);
- _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onLeftHandlePointerMove);
- _this.cleanUpResizing(evt);
- }
- }
- private _onTopHandlePointerDown = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.isCollapsed) {
- return;
- }
- _this.initResizing(evt);
- _this._resizingDirection = ResizingDirection.Top;
- _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onTopHandlePointerUp);
- _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onTopHandlePointerMove);
- }
- private _onTopHandlePointerMove = (evt: PointerEvent) => {
- const slack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
- const yLimit = (this._mouseStartPointY as number) + slack;
- this._moveTopHandle(evt, yLimit);
- }
- private _moveTopHandle = (evt: PointerEvent, yLimit: number) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (!_this._isResizingTop() || _this._mouseStartPointX === null || _this._mouseStartPointY === null || evt.clientY > yLimit) {
- return;
- }
- if (_this._isResizingTop()) {
- evt.stopPropagation();
- const distanceMouseMoved = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandTop(distanceMouseMoved);
- _this._mouseStartPointY = evt.clientY;
- }
- }
- private _onTopHandlePointerUp = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this._isResizingTop()) {
- _this.y = parseFloat(_this.element.style.top!.replace("px", ""));
- _this.height = parseFloat(_this.element.style.height.replace("px", ""));
- _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onTopHandlePointerUp);
- _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onTopHandlePointerMove);
- _this.cleanUpResizing(evt);
- }
- }
- private _onTopRightHandlePointerDown = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.isCollapsed) {
- return;
- }
- _this.initResizing(evt);
- _this._resizingDirection = ResizingDirection.TopRight;
- _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onTopRightHandlePointerUp);
- _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onTopRightHandlePointerMove);
- }
- private _onTopRightHandlePointerMove = (evt: PointerEvent) => {
- const topSlack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
- const yLimit = (this._mouseStartPointY as number) + topSlack;
- const rightSlack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
- const xLimit = (this._mouseStartPointX as number) - rightSlack;
- this._moveTopRightHandle(evt, xLimit, yLimit);
- }
- private _moveTopRightHandle = (evt: PointerEvent, xLimit: number, yLimit: number) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (!(_this._isResizingTop() && _this._isResizingRight()) || _this._mouseStartPointX === null || _this._mouseStartPointY === null) {
- return;
- }
- if (_this._isResizingRight() && _this._isResizingTop()) {
- evt.stopPropagation();
- if (evt.clientY < yLimit && evt.clientX > xLimit) { // able to move in X and Y
- const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandRight(distanceMouseMovedX, evt.clientX);
- _this._mouseStartPointX = evt.clientX;
- const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandTop(distanceMouseMovedY);
- _this._mouseStartPointY = evt.clientY;
- } else if (evt.clientY > yLimit && evt.clientX > xLimit) { // able to move in X but not Y
- const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandRight(distanceMouseMovedX, evt.clientX);
- _this._mouseStartPointX = evt.clientX;
- } else if (evt.clientY < yLimit && evt.clientX < xLimit) { // able to move in Y but not X
- const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandTop(distanceMouseMovedY);
- _this._mouseStartPointY = evt.clientY;
- }
- }
- }
- private _onTopRightHandlePointerUp = (evt: PointerEvent) => {
- evt.stopPropagation();
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this._resizingDirection === ResizingDirection.TopRight) {
- _this.y = parseFloat(_this.element.style.top!.replace("px", ""));
- _this.height = parseFloat(_this.element.style.height.replace("px", ""));
- _this.width = parseFloat(_this.element.style.width.replace("px", ""));
- _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onTopRightHandlePointerUp);
- _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onTopRightHandlePointerMove);
- _this.cleanUpResizing(evt);
- }
- }
- private _onBottomRightHandlePointerDown = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.isCollapsed) {
- return;
- }
- _this.initResizing(evt);
- _this._resizingDirection = ResizingDirection.BottomRight;
- _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onBottomRightHandlePointerUp);
- _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onBottomRightHandlePointerMove);
- }
- private _onBottomRightHandlePointerMove = (evt: PointerEvent) => {
- const bottomSlack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
- const yLimit = (this._mouseStartPointY as number) - bottomSlack;
- const rightSlack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
- const xLimit = (this._mouseStartPointX as number) - rightSlack;
- this._moveBottomRightHandle(evt, xLimit, yLimit);
- }
- private _moveBottomRightHandle = (evt: PointerEvent, xLimit: number, yLimit: number) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (!(_this._isResizingBottom() && _this._isResizingRight()) || _this._mouseStartPointX === null || _this._mouseStartPointY === null) {
- return;
- }
- if (_this._isResizingRight() && _this._isResizingBottom()) {
- evt.stopPropagation();
- if (evt.clientY > yLimit && evt.clientX > xLimit) { // able to move in X and Y
- const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandRight(distanceMouseMovedX, evt.clientX);
- _this._mouseStartPointX = evt.clientX;
- const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandBottom(distanceMouseMovedY);
- _this._mouseStartPointY = evt.clientY;
- } else if (evt.clientY < yLimit && evt.clientX > xLimit) { // able to move in X but not Y
- const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandRight(distanceMouseMovedX, evt.clientX);
- _this._mouseStartPointX = evt.clientX;
- } else if (evt.clientY > yLimit && evt.clientX < xLimit) { // able to move in Y but not X
- const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandBottom(distanceMouseMovedY);
- _this._mouseStartPointY = evt.clientY;
- }
- }
- }
- private _onBottomRightHandlePointerUp = (evt: PointerEvent) => {
- evt.stopPropagation();
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this._resizingDirection === ResizingDirection.BottomRight) {
- _this.height = parseFloat(_this.element.style.height.replace("px", ""));
- _this.width = parseFloat(_this.element.style.width.replace("px", ""));
- _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onBottomRightHandlePointerUp);
- _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onBottomRightHandlePointerMove);
- _this.cleanUpResizing(evt);
- }
- }
- private _onBottomLeftHandlePointerDown = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.isCollapsed) {
- return;
- }
- _this.initResizing(evt);
- _this._resizingDirection = ResizingDirection.BottomLeft;
- _this.mouseXLimit = evt.clientX + _this.width - _this._minFrameWidth;
- _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onBottomLeftHandlePointerUp);
- _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onBottomLeftHandlePointerMove);
- }
- private _onBottomLeftHandlePointerMove = (evt: PointerEvent) => {
- const bottomSlack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
- const yLimit = (this._mouseStartPointY as number) - bottomSlack;
- const leftSlack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
- const xLimit = (this._mouseStartPointX as number) + leftSlack;
- this._moveBottomLeftHandle(evt, xLimit, yLimit);
- }
- private _moveBottomLeftHandle = (evt: PointerEvent, xLimit: number, yLimit: number) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (!(_this._isResizingBottom() && _this._isResizingLeft()) || _this._mouseStartPointX === null || _this._mouseStartPointY === null) {
- return;
- }
- if (_this._isResizingLeft() && _this._isResizingBottom()) {
- evt.stopPropagation();
- if (evt.clientY > yLimit && evt.clientX < xLimit) { // able to move in X and Y
- const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandLeft(distanceMouseMovedX);
- _this._mouseStartPointX = evt.clientX;
- const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandBottom(distanceMouseMovedY);
- _this._mouseStartPointY = evt.clientY;
- } else if (evt.clientY < yLimit && evt.clientX < xLimit) { // able to move in X but not Y
- const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandLeft(distanceMouseMovedX);
- _this._mouseStartPointX = evt.clientX;
- } else if (evt.clientY > yLimit && evt.clientX > xLimit) { // able to move in Y but not X
- const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandBottom(distanceMouseMovedY);
- _this._mouseStartPointY = evt.clientY;
- }
- }
- }
- private _onBottomLeftHandlePointerUp = (evt: PointerEvent) => {
- evt.stopPropagation();
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this._resizingDirection === ResizingDirection.BottomLeft) {
- _this.height = parseFloat(_this.element.style.height.replace("px", ""));
- _this.x = parseFloat(_this.element.style.left!.replace("px", ""));
- _this.width = parseFloat(_this.element.style.width.replace("px", ""));
- _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onBottomLeftHandlePointerUp);
- _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onBottomLeftHandlePointerMove);
- _this.cleanUpResizing(evt);
- }
- }
- private _onTopLeftHandlePointerDown = (evt: PointerEvent) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this.isCollapsed) {
- return;
- }
- _this.initResizing(evt);
- _this._resizingDirection = ResizingDirection.TopLeft;
- _this.mouseXLimit = evt.clientX + _this.width - _this._minFrameWidth;
- _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onTopLeftHandlePointerUp);
- _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onTopLeftHandlePointerMove);
- }
- private _onTopLeftHandlePointerMove = (evt: PointerEvent) => {
- const topSlack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
- const yLimit = (this._mouseStartPointY as number) + topSlack;
- const leftSlack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
- const xLimit = (this._mouseStartPointX as number) + leftSlack;
- this._moveTopLeftHandle(evt, xLimit, yLimit);
- }
- private _moveTopLeftHandle = (evt: PointerEvent, xLimit: number, yLimit: number) => {
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (!(_this._isResizingTop() && _this._isResizingLeft()) || _this._mouseStartPointX === null || _this._mouseStartPointY === null) {
- return;
- }
- if (_this._isResizingLeft() && _this._isResizingTop()) {
- evt.stopPropagation();
- if (evt.clientY < yLimit && evt.clientX < xLimit) { // able to move in X and Y
- const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandLeft(distanceMouseMovedX);
- _this._mouseStartPointX = evt.clientX;
- const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandTop(distanceMouseMovedY);
- _this._mouseStartPointY = evt.clientY;
- } else if (evt.clientY > yLimit && evt.clientX < xLimit) { // able to move in X but not Y
- const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
- _this._expandLeft(distanceMouseMovedX);
- _this._mouseStartPointX = evt.clientX;
- } else if (evt.clientY < yLimit && evt.clientX > xLimit) { // able to move in Y but not X
- const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
- _this._expandTop(distanceMouseMovedY);
- _this._mouseStartPointY = evt.clientY;
- }
- }
- }
- private _onTopLeftHandlePointerUp = (evt: PointerEvent) => {
- evt.stopPropagation();
- // tslint:disable-next-line: no-this-assignment
- const _this = this;
- if (_this._resizingDirection === ResizingDirection.TopLeft) {
- _this.y = parseFloat(_this.element.style.top!.replace("px", ""));
- _this.height = parseFloat(_this.element.style.height.replace("px", ""));
- _this.x = parseFloat(_this.element.style.left!.replace("px", ""));
- _this.width = parseFloat(_this.element.style.width.replace("px", ""));
- _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onTopLeftHandlePointerUp);
- _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onTopLeftHandlePointerMove);
- _this.cleanUpResizing(evt);
- }
- }
- private _expandLeft(widthModification: number) {
- const frameElementWidth = parseFloat(this.element.style.width.replace("px", ""));
- const frameElementLeft = parseFloat(this.element.style.left.replace("px", ""));
- this.element.style.width = `${frameElementWidth - widthModification}px`;
- this.element.style.left = `${frameElementLeft + widthModification}px`;
- this.updateMinHeightWithComments();
- }
- private _expandTop(heightModification: number) {
- const frameElementHeight = parseFloat(this.element.style.height.replace("px", ""));
- const frameElementTop = parseFloat(this.element.style.top.replace("px", ""));
- this.element.style.height = `${frameElementHeight - heightModification}px`;
- this.element.style.top = `${frameElementTop + heightModification}px`;
- }
- private _expandRight(widthModification: number, x: number) {
- const frameElementWidth = parseFloat(this.element.style.width.replace("px", ""));
- if ((frameElementWidth + widthModification) > 20) {
- this._mouseStartPointX = x;
- this.element.style.width = `${frameElementWidth + widthModification}px`;
- }
- this.updateMinHeightWithComments();
- }
- private _expandBottom(heightModification: number) {
- const frameElementHeight = parseFloat(this.element.style.height.replace("px", ""));
- this.element.style.height = `${frameElementHeight + heightModification}px`;
- }
- public dispose() {
- if(this.isCollapsed) {
- while(this._nodes.length > 0) {
- this._nodes[0].dispose();
- }
- this.isCollapsed = false;
- }
- else {
- this._nodes.forEach(node => {
- node.enclosingFrameId = -1;
- });
- }
- if (this._onSelectionChangedObserver) {
- this._ownerCanvas.globalState.onSelectionChangedObservable.remove(this._onSelectionChangedObserver);
- };
- if(this._onGraphNodeRemovalObserver) {
- this._ownerCanvas.globalState.onGraphNodeRemovalObservable.remove(this._onGraphNodeRemovalObserver);
- };
- if(this._onExposePortOnFrameObserver) {
- this._ownerCanvas.globalState.onExposePortOnFrameObservable.remove(this._onExposePortOnFrameObserver);
- };
- this.element.parentElement?.removeChild(this.element);
- this._ownerCanvas.frames.splice(this._ownerCanvas.frames.indexOf(this), 1);
- this.onExpandStateChanged.clear();
- }
- private serializePortData(exposedPorts: NodePort[])
- {
- if(exposedPorts.length > 0) {
- for(let i = 0; i < exposedPorts.length; ++i) {
- exposedPorts[i].exposedPortPosition = i;
- }
- }
- }
- public serialize(): IFrameData {
- this.serializePortData(this._exposedInPorts);
- this.serializePortData(this._exposedOutPorts);
- return {
- x: this._x,
- y: this._y,
- width: this._width,
- height: this._height,
- color: this._color.asArray(),
- name: this.name,
- isCollapsed: true, //keeping closed to make reimporting cleaner
- blocks: this.nodes.map(n => n.block.uniqueId),
- comments: this._comments
- }
- }
- public export() {
- const state = this._ownerCanvas.globalState;
- const json = SerializationTools.Serialize(state.nodeMaterial, state, this);
- StringTools.DownloadAsFile(state.hostDocument, json, this._name + ".json");
- }
- public static Parse(serializationData: IFrameData, canvas: GraphCanvasComponent, map?: {[key: number]: number}) {
- let newFrame = new GraphFrame(null, canvas, true);
- const isCollapsed = !!serializationData.isCollapsed;
- newFrame.x = serializationData.x;
- newFrame.y = serializationData.y;
- newFrame.width = serializationData.width;
- newFrame.height = serializationData.height;
- newFrame.name = serializationData.name;
- newFrame.color = Color3.FromArray(serializationData.color);
- newFrame.comments = serializationData.comments;
- if (serializationData.blocks && map) {
- for (var blockId of serializationData.blocks) {
- let actualId = map[blockId];
- let node = canvas.nodes.filter(n => n.block.uniqueId === actualId);
-
- if (node.length) {
- newFrame.nodes.push(node[0]);
- node[0].enclosingFrameId = newFrame.id;
- }
- }
- } else {
- newFrame.refresh();
- }
-
-
- for (var node of newFrame.nodes) {
- for (var port of node.outputPorts) { // Output
- if(port.exposedOnFrame) {
- port.isExposed = true;
- if(port.exposedPortPosition) {
- newFrame._exposedOutPorts[port.exposedPortPosition] = port;
- }
- else {
- newFrame._exposedOutPorts.push(port);
- }
- }
- }
- for (var port of node.inputPorts) { // Inports
- if(port.exposedOnFrame) {
- port.isExposed = true;
- if(port.exposedPortPosition) {
- newFrame._exposedInPorts[port.exposedPortPosition] = port;
- }
- else {
- newFrame._exposedInPorts.push(port);
- }
- }
- }
- }
- newFrame.isCollapsed = isCollapsed;
- if (isCollapsed) {
- canvas._frameIsMoving = true;
- newFrame._moveFrame(-(newFrame.width - newFrame.CollapsedWidth) / 2, 0);
- let diff = serializationData.x - newFrame.x;
- newFrame._moveFrame(diff, 0);
- newFrame.cleanAccumulation();
- for (var selectedNode of newFrame.nodes) {
- selectedNode.refresh();
- }
- canvas._frameIsMoving = false;
- }
- return newFrame;
- }
- }
|