Skip to content

Commit

Permalink
feat KaotoIO#1540: Add quick icon edge end
Browse files Browse the repository at this point in the history
  • Loading branch information
ibek committed Oct 14, 2024
1 parent c9f7a97 commit d87fbcd
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
Visualization,
withPanZoom,
} from '@patternfly/react-topology';
import { CustomGroupWithSelection, CustomNodeWithSelection, NoBendpointsEdge } from '../Custom';
import { CustomGroupWithSelection, CustomNodeWithSelection, NoBendpointsEdge, EdgeEndWithButton } from '../Custom';
import { LayoutType } from './canvas.models';

export class ControllerService {
Expand Down Expand Up @@ -109,6 +109,8 @@ export class ControllerService {
switch (type) {
case 'group':
return CustomGroupWithSelection;
case 'edge-end':
return EdgeEndWithButton;
default:
switch (kind) {
case ModelKind.graph:
Expand Down
13 changes: 13 additions & 0 deletions packages/ui/src/components/Visualization/Canvas/flow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@ export class FlowService {

private static getEdgesFromVizNode(vizNodeParam: IVisualizationNode): CanvasEdge[] {
const edges: CanvasEdge[] = [];
const nodeInteractions = vizNodeParam.getNodeInteraction();

if (vizNodeParam.getNextNode() !== undefined) {
edges.push(this.getEdge(vizNodeParam.id, vizNodeParam.getNextNode()!.id));
} else if (nodeInteractions.canHaveNextStep) {
edges.push(this.getEdgeEnd(vizNodeParam.id));
}

return edges;
Expand Down Expand Up @@ -111,4 +114,14 @@ export class FlowService {
edgeStyle: EdgeStyle.solid,
};
}

private static getEdgeEnd(source: string): CanvasEdge {
return {
id: `${source}-end`,
type: 'edge-end',
source,
target: source,
edgeStyle: EdgeStyle.dashed,
};
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,49 @@
import { ContextMenuItem } from '@patternfly/react-topology';
import { FunctionComponent, PropsWithChildren, useCallback, useContext } from 'react';
import { IDataTestID } from '../../../../models';
import { AddStepMode, IVisualizationNode } from '../../../../models/visualization/base-visual-entity';
import {
AddStepMode,
IVisualizationNode,
IVisualizationNodeData,
} from '../../../../models/visualization/base-visual-entity';
import { CatalogModalContext } from '../../../../providers/catalog-modal.provider';
import { EntitiesContext } from '../../../../providers/entities.provider';
import { EntitiesContextResult } from '../../../../hooks';

interface ItemAddStepProps extends PropsWithChildren<IDataTestID> {
mode: AddStepMode.PrependStep | AddStepMode.AppendStep;
vizNode: IVisualizationNode;
}

export const ItemAddStep: FunctionComponent<ItemAddStepProps> = (props) => {
const entitiesContext = useContext(EntitiesContext);
const catalogModalContext = useContext(CatalogModalContext);
export const addNode = async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
catalogModalContext: any,
entitiesContext: EntitiesContextResult | null,
vizNode: IVisualizationNode<IVisualizationNodeData>,
mode: AddStepMode = AddStepMode.AppendStep,
) => {
if (!vizNode || !entitiesContext) return;

const onAddNode = useCallback(async () => {
if (!props.vizNode || !entitiesContext) return;
/** Get compatible nodes and the location where can be introduced */
const compatibleNodes = entitiesContext.camelResource.getCompatibleComponents(mode, vizNode.data);

/** Open Catalog modal, filtering the compatible nodes */
const definedComponent = await catalogModalContext?.getNewComponent(compatibleNodes);
if (!definedComponent) return;

/** Get compatible nodes and the location where can be introduced */
const compatibleNodes = entitiesContext.camelResource.getCompatibleComponents(props.mode, props.vizNode.data);
/** Add new node to the entities */
vizNode.addBaseEntityStep(definedComponent, mode);

/** Open Catalog modal, filtering the compatible nodes */
const definedComponent = await catalogModalContext?.getNewComponent(compatibleNodes);
if (!definedComponent) return;
/** Update entity */
entitiesContext.updateEntitiesFromCamelResource();
};

/** Add new node to the entities */
props.vizNode.addBaseEntityStep(definedComponent, props.mode);
export const ItemAddStep: FunctionComponent<ItemAddStepProps> = (props) => {
const entitiesContext = useContext(EntitiesContext);
const catalogModalContext = useContext(CatalogModalContext);

/** Update entity */
entitiesContext.updateEntitiesFromCamelResource();
const onAddNode = useCallback(async () => {
addNode(catalogModalContext, entitiesContext, props.vizNode, props.mode);
}, [catalogModalContext, entitiesContext, props.mode, props.vizNode]);

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* stylelint-disable */

.custom-edge {
--pf-topology__edge--m-hover--background--Stroke: none;
background: var(--pf-v5-global--palette--white);
&__end {
border-radius: 3px;
border: 1px solid var(--pf-v5-global--palette--black-400);
--pf-topology__node--Color: var(--pf-v5-global--palette--black-500);
width: 24px;
height: 24px;
padding: 3px;
display: flex;
position: relative;

&:hover {
border-color: var(--pf-v5-global--palette--blue-500);
}
}
}
53 changes: 53 additions & 0 deletions packages/ui/src/components/Visualization/Custom/Edge/EdgeEnd.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { DefaultEdge, Edge, EdgeModel, EdgeTerminalType, observer } from '@patternfly/react-topology';
import { Button, Tooltip } from '@patternfly/react-core';
import { PlusIcon } from '@patternfly/react-icons';
import './CustomEdge.scss';
import { addNode } from '../ContextMenu/ItemAddStep';
import { CatalogModalContext, EntitiesContext } from '../../../../providers';
import { useContext } from 'react';
import { IVisualizationNode } from '../../../../models';

interface EdgeEndProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
element: Edge<EdgeModel, any>;
}

const EdgeEnd: React.FC<EdgeEndProps> = observer(({ element, ...rest }) => {
const entitiesContext = useContext(EntitiesContext);
const catalogModalContext = useContext(CatalogModalContext);
const vizNode: IVisualizationNode = element.getSource().getData()?.vizNode;
const isHorizontal = element.getGraph().getLayout() === 'DagreHorizontal';
const endPoint = element.getEndPoint();
let x, y;
if (isHorizontal) {
x = endPoint.x;
y = endPoint.y - 12;
} else {
x = endPoint.x - 12;
y = endPoint.y;
}
const onAdd = () => {
addNode(catalogModalContext, entitiesContext, vizNode);
};
return (
<DefaultEdge
element={element}
startTerminalType={EdgeTerminalType.none}
endTerminalType={EdgeTerminalType.none}
className="custom-edge"
{...rest}
>
<g data-testid={`custom-edge__${element?.getId()}`}>
<foreignObject x={x} y={y} width="24" height="24" className="custom-edge">
<Tooltip content="Append">
<Button variant="plain" className="custom-edge__end" onClick={onAdd}>
<PlusIcon size={40} />
</Button>
</Tooltip>
</foreignObject>
</g>
</DefaultEdge>
);
});

export const EdgeEndWithButton: typeof DefaultEdge = EdgeEnd as typeof DefaultEdge;
Original file line number Diff line number Diff line change
@@ -1,7 +1,65 @@
import { BaseEdge, Point } from '@patternfly/react-topology';
import { BaseEdge, getTopCollapsedParent, Point } from '@patternfly/react-topology';

export class NoBendpointsEdge extends BaseEdge {
getBendpoints(): Point[] {
return [];
}
getStartPoint(): Point {
if (this.getTarget() === this.getSource()) {
const parent = getTopCollapsedParent(this.getSource());
const isHorizontal = this.getGraph().getLayout() === 'DagreHorizontal';
const parentPos = parent.getPosition();
const parentSize = parent.getDimensions();
let x, y;
if (isHorizontal) {
if (parent.getType() === 'group') {
x = parentPos.x + parentSize.width / 2.0;
y = parentPos.y;
} else {
x = parentPos.x + parentSize.width;
y = parentPos.y + parentSize.height / 2.0;
}
} else {
if (parent.getType() === 'group') {
x = parentPos.x;
y = parentPos.y + parentSize.height / 2.0;
} else {
x = parentPos.x + parentSize.width / 2.0;
y = parentPos.y + parentSize.height;
}
}
return new Point(x, y);
} else {
return super.getStartPoint();
}
}
getEndPoint(): Point {
if (this.getTarget() === this.getSource()) {
const parent = getTopCollapsedParent(this.getSource());
const isHorizontal = this.getGraph().getLayout() === 'DagreHorizontal';
const parentPos = parent.getPosition();
const parentSize = parent.getDimensions();
let x, y;
if (isHorizontal) {
if (parent.getType() === 'group') {
x = parentPos.x + parentSize.width / 2.0 + 15;
y = parentPos.y;
} else {
x = parentPos.x + parentSize.width / 2.0 + 55;
y = parentPos.y + parentSize.height / 2.0;
}
} else {
if (parent.getType() === 'group') {
x = parentPos.x;
y = parentPos.y + parentSize.height / 2.0 + 15;
} else {
x = parentPos.x + parentSize.width / 2.0;
y = parentPos.y + parentSize.height / 2.0 + 85;
}
}
return new Point(x, y);
} else {
return super.getEndPoint();
}
}
}
1 change: 1 addition & 0 deletions packages/ui/src/components/Visualization/Custom/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './Group/CustomGroup';
export * from './NoBendingEdge';
export * from './Node/CustomNode';
export * from './Edge/EdgeEnd';

0 comments on commit d87fbcd

Please sign in to comment.