Skip to content

Commit

Permalink
TEMP: Demo
Browse files Browse the repository at this point in the history
  • Loading branch information
lordrip committed Aug 6, 2024
1 parent a60843d commit bac2ac0
Show file tree
Hide file tree
Showing 23 changed files with 5,311 additions and 298 deletions.
20 changes: 10 additions & 10 deletions packages/ui/src/components/Visualization/Canvas/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ interface CanvasProps {
entities: BaseVisualCamelEntity[];
}

export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = (props) => {
export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = ({ entities, contextToolbar }) => {
/** State for @patternfly/react-topology */
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const [selectedNode, setSelectedNode] = useState<CanvasNode | undefined>(undefined);
Expand All @@ -54,10 +54,10 @@ export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = (props)
const controller = useMemo(() => CanvasService.createController(), []);
const { visibleFlows } = useContext(VisibleFlowsContext)!;
const shouldShowEmptyState = useMemo(() => {
const areNoFlows = props.entities.length === 0;
const areNoFlows = entities.length === 0;
const areAllFlowsHidden = Object.values(visibleFlows).every((visible) => !visible);
return areNoFlows || areAllFlowsHidden;
}, [props.entities.length, visibleFlows]);
}, [entities.length, visibleFlows]);

const controlButtons = useMemo(() => {
const customButtons: TopologyControlButton[] = catalogModalContext
Expand Down Expand Up @@ -121,7 +121,7 @@ export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = (props)
legend: false,
customButtons,
});
}, [catalogModalContext, controller]);
}, [catalogModalContext, controller, setActiveLayout]);

const handleSelection = useCallback(
(selectedIds: string[]) => {
Expand Down Expand Up @@ -154,13 +154,13 @@ export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = (props)

/** Draw graph */
useEffect(() => {
if (!Array.isArray(props.entities)) return;
if (!Array.isArray(entities)) return;
setSelectedNode(undefined);

const nodes: CanvasNode[] = [];
const edges: CanvasEdge[] = [];

props.entities.forEach((entity) => {
entities.forEach((entity) => {
if (visibleFlows[entity.id]) {
const { nodes: childNodes, edges: childEdges } = CanvasService.getFlowDiagram(entity.toVizNode());
nodes.push(...childNodes);
Expand All @@ -181,7 +181,7 @@ export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = (props)
};

controller.fromModel(model, false);
}, [controller, props.entities, visibleFlows]);
}, [activeLayout, controller, entities, visibleFlows]);

useEffect(() => {
const timeoutId = setTimeout(() => {
Expand All @@ -201,15 +201,15 @@ export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = (props)

return (
<TopologyView
sideBarResizable={true}
sideBarResizable
sideBarOpen={isSidebarOpen}
sideBar={<CanvasSideBar selectedNode={selectedNode} onClose={handleCloseSideBar} />}
contextToolbar={props.contextToolbar}
contextToolbar={contextToolbar}
controlBar={<TopologyControlBar controlButtons={controlButtons} />}
>
<VisualizationProvider controller={controller}>
{shouldShowEmptyState ? (
<VisualizationEmptyState data-testid="visualization-empty-state" entitiesNumber={props.entities.length} />
<VisualizationEmptyState data-testid="visualization-empty-state" entitiesNumber={entities.length} />
) : (
<VisualizationSurface state={{ selectedIds }} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ describe('CanvasService', () => {
it('should allow consumers to create a new controller and register its factories', () => {
const layoutFactorySpy = jest.spyOn(Visualization.prototype, 'registerLayoutFactory');
const componentFactorySpy = jest.spyOn(Visualization.prototype, 'registerComponentFactory');
const baselineElementFactorySpy = jest.spyOn(Visualization.prototype, 'registerElementFactory');

const controller = CanvasService.createController();

expect(controller).toBeInstanceOf(Visualization);
expect(layoutFactorySpy).toHaveBeenCalledWith(CanvasService.baselineLayoutFactory);
expect(componentFactorySpy).toHaveBeenCalledWith(CanvasService.baselineComponentFactory);
expect(baselineElementFactorySpy).toHaveBeenCalledWith(CanvasService.baselineElementFactory);
});

describe('baselineComponentFactory', () => {
Expand Down
145 changes: 60 additions & 85 deletions packages/ui/src/components/Visualization/Canvas/canvas.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@ import {
ColaLayout,
ComponentFactory,
ConcentricLayout,
DagreLayout,
DagreGroupsLayout,
DefaultEdge,
EdgeStyle,
ForceLayout,
Graph,
GraphComponent,
GraphElement,
GridLayout,
Layout,
Model,
LEFT_TO_RIGHT,
ModelKind,
TOP_TO_BOTTOM,
Visualization,
withPanZoom,
} from '@patternfly/react-topology';
import { IVisualizationNode } from '../../../models/visualization/base-visual-entity';
import { CustomGroupWithSelection, CustomNodeWithSelection } from '../Custom';
import { CustomGroupWithSelection, CustomNodeWithSelection, NoBendpointsEdge } from '../Custom';
import { CanvasDefaults } from './canvas.defaults';
import { CanvasEdge, CanvasNode, CanvasNodesAndEdges, LayoutType } from './canvas.models';

Expand All @@ -32,37 +34,11 @@ export class CanvasService {

newController.registerLayoutFactory(this.baselineLayoutFactory);
newController.registerComponentFactory(this.baselineComponentFactory);

const defaultModel: Model = {
graph: {
id: 'default',
type: 'graph',
},
};

newController.fromModel(defaultModel, false);
newController.registerElementFactory(this.baselineElementFactory);

return newController;
}

static baselineComponentFactory(kind: ModelKind, type: string): ReturnType<ComponentFactory> {
switch (type) {
case 'group':
return CustomGroupWithSelection;
default:
switch (kind) {
case ModelKind.graph:
return withPanZoom()(GraphComponent);
case ModelKind.node:
return CustomNodeWithSelection;
case ModelKind.edge:
return DefaultEdge;
default:
return undefined;
}
}
}

// ### dagre algo options, uses default value on undefined ###
// nodeSep: undefined, // the separation between adjacent nodes in the same rank
// edgeSep: undefined, // the separation between adjacent edges in the same rank
Expand Down Expand Up @@ -103,16 +79,16 @@ export class CanvasService {
case LayoutType.Concentric:
return new ConcentricLayout(graph);
case LayoutType.DagreVertical:
return new DagreLayout(graph, {
rankdir: 'TB',
return new DagreGroupsLayout(graph, {
rankdir: TOP_TO_BOTTOM,
ranker: 'network-simplex',
nodesep: 20,
edgesep: 20,
ranksep: 0,
});
case LayoutType.DagreHorizontal:
return new DagreLayout(graph, {
rankdir: 'LR',
return new DagreGroupsLayout(graph, {
rankdir: LEFT_TO_RIGHT,
ranker: 'network-simplex',
nodesep: 20,
edgesep: 20,
Expand All @@ -129,24 +105,39 @@ export class CanvasService {
}
}

static baselineComponentFactory(kind: ModelKind, type: string): ReturnType<ComponentFactory> {
switch (type) {
case 'group':
return CustomGroupWithSelection;
default:
switch (kind) {
case ModelKind.graph:
return withPanZoom()(GraphComponent);
case ModelKind.node:
return CustomNodeWithSelection;
case ModelKind.edge:
return DefaultEdge;
default:
return undefined;
}
}
}

static baselineElementFactory(kind: ModelKind): GraphElement | undefined {
switch (kind) {
case ModelKind.edge:
return new NoBendpointsEdge();
default:
return undefined;
}
}

static getFlowDiagram(vizNode: IVisualizationNode): CanvasNodesAndEdges {
this.nodes = [];
this.edges = [];
this.visitedNodes = [];

const children = vizNode.getChildren();
if (vizNode.data.isGroup && children) {
children.forEach((child) => this.appendNodesAndEdges(child));
const containerId = vizNode.getBaseEntity()?.getId() ?? 'Unknown';
const group = this.getContainer(containerId, {
label: containerId,
children: this.visitedNodes,
data: { vizNode },
});
this.nodes.push(group);
} else {
this.appendNodesAndEdges(vizNode);
}
this.appendNodesAndEdges(vizNode);

return { nodes: this.nodes, edges: this.edges };
}
Expand All @@ -157,28 +148,31 @@ export class CanvasService {
return;
}

const node = this.getCanvasNode(vizNodeParam);

/** Add node */
this.nodes.push(node);
this.visitedNodes.push(node.id);

/** Add edges */
this.edges.push(...this.getEdgesFromVizNode(vizNodeParam));
let node: CanvasNode;

/** Traverse the children nodes */
const children = vizNodeParam.getChildren();
if (children !== undefined) {
if (vizNodeParam.data.isGroup && children) {
children.forEach((child) => {
this.appendNodesAndEdges(child);
});
}

/** Traverse the next node */
const nextNode = vizNodeParam.getNextNode();
if (nextNode !== undefined) {
this.appendNodesAndEdges(nextNode);
const containerId = vizNodeParam.id;
node = this.getContainer(containerId, {
label: containerId,
children: children.map((child) => child.id),
parentNode: vizNodeParam.getParentNode()?.id,
data: { vizNode: vizNodeParam },
});
} else {
node = this.getCanvasNode(vizNodeParam);
}

/** Add node */
this.nodes.push(node);
this.visitedNodes.push(node.id);

/** Add edges */
this.edges.push(...this.getEdgesFromVizNode(vizNodeParam));
}

private static getCanvasNode(vizNodeParam: IVisualizationNode): CanvasNode {
Expand All @@ -195,43 +189,24 @@ export class CanvasService {
private static getEdgesFromVizNode(vizNodeParam: IVisualizationNode): CanvasEdge[] {
const edges: CanvasEdge[] = [];

/** Connect to previous node if it doesn't have children */
if (vizNodeParam.getPreviousNode() !== undefined && vizNodeParam.getPreviousNode()?.getChildren() === undefined) {
edges.push(this.getEdge(vizNodeParam.getPreviousNode()!.id, vizNodeParam.id));
}

/** Connect to the parent if it's not a group and there is no previous node */
if (
vizNodeParam.getParentNode() !== undefined &&
!vizNodeParam.getParentNode()?.data.isGroup &&
vizNodeParam.getPreviousNode() === undefined
) {
edges.push(this.getEdge(vizNodeParam.getParentNode()!.id, vizNodeParam.id));
}

/** Connect to each leaf of the previous node */
if (vizNodeParam.getPreviousNode() !== undefined && vizNodeParam.getPreviousNode()?.getChildren() !== undefined) {
const leafNodesIds: string[] = [];
vizNodeParam.getPreviousNode()!.populateLeafNodesIds(leafNodesIds);

leafNodesIds.forEach((leafNodeId) => {
edges.push(this.getEdge(leafNodeId, vizNodeParam.id));
});
if (vizNodeParam.getNextNode() !== undefined) {
edges.push(this.getEdge(vizNodeParam.id, vizNodeParam.getNextNode()!.id));
}

return edges;
}

private static getContainer(
id: string,
options: { label?: string; children?: string[]; data?: CanvasNode['data'] } = {},
options: { label?: string; children?: string[]; parentNode?: string; data?: CanvasNode['data'] } = {},
): CanvasNode {
return {
id,
type: 'group',
group: true,
label: options.label ?? id,
children: options.children ?? [],
parentNode: options.parentNode,
data: options.data,
style: {
padding: CanvasDefaults.DEFAULT_NODE_DIAMETER * 0.8,
Expand Down
Loading

0 comments on commit bac2ac0

Please sign in to comment.