From 029d84a5f8af0ebf9a409c3cf0143ff7ea1edef8 Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Date: Thu, 13 Jul 2023 14:35:46 -0400 Subject: [PATCH] fix(event): Fix firing of GRAPH_LAYOUT_END_EVENT --- .../demo-app-ts/src/demos/TopologyPackage.tsx | 22 +++++++++++++++++++ packages/module/src/layouts/BaseLayout.ts | 5 +++-- .../module/src/layouts/BreadthFirstLayout.ts | 3 ++- .../module/src/layouts/ColaGroupsLayout.ts | 11 ++++++---- packages/module/src/layouts/ColaLayout.ts | 21 ++++++++++++++++-- .../module/src/layouts/ConcentricLayout.ts | 3 ++- packages/module/src/layouts/DagreLayout.ts | 4 +++- packages/module/src/layouts/ForceLayout.ts | 3 ++- packages/module/src/layouts/GridLayout.ts | 3 ++- 9 files changed, 62 insertions(+), 13 deletions(-) diff --git a/packages/demo-app-ts/src/demos/TopologyPackage.tsx b/packages/demo-app-ts/src/demos/TopologyPackage.tsx index b0f9e565..0e74858b 100644 --- a/packages/demo-app-ts/src/demos/TopologyPackage.tsx +++ b/packages/demo-app-ts/src/demos/TopologyPackage.tsx @@ -2,9 +2,14 @@ import * as React from 'react'; import { action } from 'mobx'; import * as _ from 'lodash'; import { + Controller, createTopologyControlButtons, defaultControlButtonsOptions, EdgeModel, + EventListener, + GRAPH_LAYOUT_END_EVENT, + isNode, + Node, NodeModel, SELECTION_EVENT, SelectionEventListener, @@ -29,6 +34,15 @@ interface TopologyViewComponentProps { sideBarResizable?: boolean; } +const layoutEndListener: EventListener = ({ graph }): void => { + const controller: Controller = graph.getController(); + const positions = controller.getElements().filter(e => isNode(e)).map((node) => `Node: ${node.getLabel()}: ${Math.round((node as Node).getPosition().x)},${Math.round((node as Node).getPosition().y)}`); + + // eslint-disable-next-line no-console + console.log(`Layout Complete:\n${positions.join('\n')}`); +}; + + const TopologyViewComponent: React.FunctionComponent = ({ useSidebar, sideBarResizable = false @@ -76,6 +90,14 @@ const TopologyViewComponent: React.FunctionComponent setSelectedIds(ids); }); + React.useEffect(() => { + + controller.addEventListener(GRAPH_LAYOUT_END_EVENT, layoutEndListener); + return () => { + controller.removeEventListener(GRAPH_LAYOUT_END_EVENT, layoutEndListener); + } + }, [controller]); + React.useEffect(() => { controller.getGraph().setDetailsLevelThresholds({ low: lowScale, diff --git a/packages/module/src/layouts/BaseLayout.ts b/packages/module/src/layouts/BaseLayout.ts index 25acdc3f..18d5b10b 100644 --- a/packages/module/src/layouts/BaseLayout.ts +++ b/packages/module/src/layouts/BaseLayout.ts @@ -477,10 +477,11 @@ export class BaseLayout implements Layout { // Reset the force simulation this.stopSimulation(); - this.startLayout(this.graph, initialRun, addingNodes); + this.startLayout(this.graph, initialRun, addingNodes, () => { + this.graph.getController().fireEvent(GRAPH_LAYOUT_END_EVENT, { graph: this.graph }); + }); } else if (restart && this.options.layoutOnDrag) { this.updateLayout(); } - this.graph.getController().fireEvent(GRAPH_LAYOUT_END_EVENT, { graph: this.graph }); } } diff --git a/packages/module/src/layouts/BreadthFirstLayout.ts b/packages/module/src/layouts/BreadthFirstLayout.ts index 29055947..c61c6ba4 100644 --- a/packages/module/src/layouts/BreadthFirstLayout.ts +++ b/packages/module/src/layouts/BreadthFirstLayout.ts @@ -1,4 +1,4 @@ -import { Edge, Graph, Layout, Node } from '../types'; +import { Edge, Graph, GRAPH_LAYOUT_END_EVENT, Layout, Node } from '../types'; import { BaseLayout } from './BaseLayout'; import { LayoutOptions } from './LayoutOptions'; import { LayoutNode } from './LayoutNode'; @@ -119,5 +119,6 @@ export class BreadthFirstLayout extends BaseLayout implements Layout { x = 0; } } + this.graph.getController().fireEvent(GRAPH_LAYOUT_END_EVENT, { graph: this.graph }); } } diff --git a/packages/module/src/layouts/ColaGroupsLayout.ts b/packages/module/src/layouts/ColaGroupsLayout.ts index 67687096..7d6e6fb3 100644 --- a/packages/module/src/layouts/ColaGroupsLayout.ts +++ b/packages/module/src/layouts/ColaGroupsLayout.ts @@ -52,7 +52,7 @@ class ColaGroupsLayout extends ColaLayout implements Layout { this.simulationRunning = false; action(() => { if (this.destroyed) { - this.onEnd && this.onEnd(); + this.handleLayoutEnd(); return; } this.layoutNodes.forEach(d => { @@ -68,15 +68,18 @@ class ColaGroupsLayout extends ColaLayout implements Layout { this.simulationStopped = false; if (this.restartOnEnd !== undefined) { this.startColaLayout(false, this.restartOnEnd); - this.startLayout(graph, false, this.restartOnEnd, this.onEnd); + this.startLayout(graph, false, this.restartOnEnd, this.handleLayoutEnd); delete this.restartOnEnd; + } else { + this.handleLayoutEnd(); } } else if (this.addingNodes) { // One round of simulation to adjust for new nodes this.forceSimulation.useForceSimulation(this.nodes, this.edges, this.getFixedNodeDistance); this.forceSimulation.restart(); + } else { + this.handleLayoutEnd(); } - this.onEnd && this.onEnd(); })(); }); } @@ -124,7 +127,7 @@ class ColaGroupsLayout extends ColaLayout implements Layout { edges: LayoutLink[], groups: LayoutGroup[] ): BaseLayout { - const layout = new ColaGroupsLayout(graph, { ...this.options, listenForChanges: false }); + const layout = new ColaGroupsLayout(graph, { ...this.options, onSimulationEnd: undefined, listenForChanges: false }); layout.setupLayout(graph, nodes, edges, groups); return layout; } diff --git a/packages/module/src/layouts/ColaLayout.ts b/packages/module/src/layouts/ColaLayout.ts index 30dbfe18..026f1f98 100644 --- a/packages/module/src/layouts/ColaLayout.ts +++ b/packages/module/src/layouts/ColaLayout.ts @@ -10,6 +10,7 @@ import { LayoutNode } from './LayoutNode'; import { ColaNode } from './ColaNode'; import { ColaGroup } from './ColaGroup'; import { ColaLink } from './ColaLink'; +import { ForceSimulation } from './ForceSimulation'; export interface ColaLayoutOptions { maxTicks: number; @@ -52,6 +53,10 @@ class ColaLayout extends BaseLayout implements Layout { ...COLA_LAYOUT_DEFAULTS, ...options }; + this.forceSimulation = new ForceSimulation({ + ...this.options, + onSimulationEnd: options?.onSimulationEnd ?? this.onSimulationEnd + }); this.initializeLayout(); } @@ -74,7 +79,7 @@ class ColaLayout extends BaseLayout implements Layout { this.simulationRunning = false; action(() => { if (this.destroyed) { - this.onEnd && this.onEnd(); + this.handleLayoutEnd(); return; } this.nodes.forEach(d => { @@ -91,17 +96,28 @@ class ColaLayout extends BaseLayout implements Layout { if (this.restartOnEnd !== undefined) { this.startColaLayout(false, this.restartOnEnd); delete this.restartOnEnd; + } else { + this.handleLayoutEnd(); } } else if (this.addingNodes) { // One round of simulation to adjust for new nodes this.forceSimulation.useForceSimulation(this.nodes, this.edges, this.getFixedNodeDistance); this.forceSimulation.restart(); + } else { + this.handleLayoutEnd(); } - this.onEnd && this.onEnd(); })(); }); } + protected handleLayoutEnd = () => { + if (this.onEnd) { + // Only call on end once, then clear it so that it doesn't get called again on simulations + this.onEnd(); + this.onEnd = undefined; + } + } + protected onSimulationEnd = () => { if (this.addingNodes) { if (!this.options.layoutOnDrag) { @@ -109,6 +125,7 @@ class ColaLayout extends BaseLayout implements Layout { } this.addingNodes = false; } + this.handleLayoutEnd(); }; destroy(): void { diff --git a/packages/module/src/layouts/ConcentricLayout.ts b/packages/module/src/layouts/ConcentricLayout.ts index 84383699..909e445c 100644 --- a/packages/module/src/layouts/ConcentricLayout.ts +++ b/packages/module/src/layouts/ConcentricLayout.ts @@ -1,4 +1,4 @@ -import { Edge, Graph, Layout, Node } from '../types'; +import { Edge, Graph, GRAPH_LAYOUT_END_EVENT, Layout, Node } from '../types'; import { BaseLayout } from './BaseLayout'; import { LayoutOptions } from './LayoutOptions'; import { LayoutNode } from './LayoutNode'; @@ -103,5 +103,6 @@ export class ConcentricLayout extends BaseLayout implements Layout { r += maxWH + padding; } } + this.graph.getController().fireEvent(GRAPH_LAYOUT_END_EVENT, { graph: this.graph }); } } diff --git a/packages/module/src/layouts/DagreLayout.ts b/packages/module/src/layouts/DagreLayout.ts index ecab6c47..cf0a6225 100644 --- a/packages/module/src/layouts/DagreLayout.ts +++ b/packages/module/src/layouts/DagreLayout.ts @@ -1,6 +1,6 @@ import * as dagre from 'dagre'; import * as _ from 'lodash'; -import { Edge, Graph, Layout, Node } from '../types'; +import { Edge, Graph, GRAPH_LAYOUT_END_EVENT, Layout, Node } from '../types'; import { BaseLayout, LAYOUT_DEFAULTS } from './BaseLayout'; import { LayoutOptions } from './LayoutOptions'; import { LayoutLink } from './LayoutLink'; @@ -88,6 +88,8 @@ export class DagreLayout extends BaseLayout implements Layout { if (this.dagreOptions.layoutOnDrag) { this.forceSimulation.useForceSimulation(this.nodes, this.edges, this.getFixedNodeDistance); + } else { + this.graph.getController().fireEvent(GRAPH_LAYOUT_END_EVENT, { graph: this.graph }); } } } diff --git a/packages/module/src/layouts/ForceLayout.ts b/packages/module/src/layouts/ForceLayout.ts index aae92b55..2a69e278 100644 --- a/packages/module/src/layouts/ForceLayout.ts +++ b/packages/module/src/layouts/ForceLayout.ts @@ -1,4 +1,4 @@ -import { Graph, Layout } from '../types'; +import { Graph, GRAPH_LAYOUT_END_EVENT, Layout } from '../types'; import { getGroupPadding } from '../utils/element-utils'; import { ForceSimulationNode } from './ForceSimulation'; import { BaseLayout } from '.'; @@ -12,6 +12,7 @@ export class ForceLayout extends BaseLayout implements Layout { layoutOnDrag: true, onSimulationEnd: () => { this.nodes.forEach(n => n.setFixed(false)); + this.graph.getController().fireEvent(GRAPH_LAYOUT_END_EVENT, { graph: this.graph }); } }); } diff --git a/packages/module/src/layouts/GridLayout.ts b/packages/module/src/layouts/GridLayout.ts index a68d4096..dfaeda0b 100644 --- a/packages/module/src/layouts/GridLayout.ts +++ b/packages/module/src/layouts/GridLayout.ts @@ -1,4 +1,4 @@ -import { Edge, Graph, Layout, Node } from '../types'; +import { Edge, Graph, GRAPH_LAYOUT_END_EVENT, Layout, Node } from '../types'; import { BaseLayout } from './BaseLayout'; import { LayoutOptions } from './LayoutOptions'; import { LayoutNode } from './LayoutNode'; @@ -67,5 +67,6 @@ export class GridLayout extends BaseLayout implements Layout { } } } + this.graph.getController().fireEvent(GRAPH_LAYOUT_END_EVENT, { graph: this.graph }); } }