Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pipelines): add support for collapsible groups #162

Merged
merged 9 commits into from
Mar 26, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,67 @@ import { observer } from 'mobx-react';
import {
AnchorEnd,
DagreLayoutOptions,
DefaultGroup,
DefaultTaskGroup,
GraphElement,
isNode,
LabelPosition,
Node,
TOP_TO_BOTTOM,
useAnchor,
WithContextMenuProps,
WithSelectionProps,
ShapeProps,
WithDragNodeProps,
EdgeCreationTypes,
} from '@patternfly/react-topology';
import TaskGroupSourceAnchor from './TaskGroupSourceAnchor';
import TaskGroupTargetAnchor from './TaskGroupTargetAnchor';

interface DemoTaskNodeProps {
type DemoTaskGroupProps = {
element: GraphElement;
}
collapsible?: boolean;
collapsedWidth?: number;
collapsedHeight?: number;
onCollapseChange?: (group: Node, collapsed: boolean) => void;
getCollapsedShape?: (node: Node) => React.FunctionComponent<ShapeProps>;
collapsedShadowOffset?: number; // defaults to 10
} & WithContextMenuProps &
WithDragNodeProps &
WithSelectionProps;

const DemoTaskGroup: React.FunctionComponent<DemoTaskNodeProps> = ({ element, ...rest }) => {
export const DEFAULT_TASK_WIDTH = 180;
export const DEFAULT_TASK_HEIGHT = 32;

const getEdgeCreationTypes = (): EdgeCreationTypes => ({
edgeType: 'edge',
spacerEdgeType: 'edge'
});

const DemoTaskGroup: React.FunctionComponent<DemoTaskGroupProps> = ({ element, ...rest }) => {
const verticalLayout = (element.getGraph().getLayoutOptions?.() as DagreLayoutOptions)?.rankdir === TOP_TO_BOTTOM;

useAnchor(
React.useCallback((node: Node) =>new TaskGroupSourceAnchor(node, verticalLayout), [verticalLayout]),
React.useCallback((node: Node) => new TaskGroupSourceAnchor(node, verticalLayout), [verticalLayout]),
AnchorEnd.source
);
useAnchor(
React.useCallback((node: Node) => new TaskGroupTargetAnchor(node, verticalLayout),[verticalLayout]),
React.useCallback((node: Node) => new TaskGroupTargetAnchor(node, verticalLayout), [verticalLayout]),
AnchorEnd.target
);
if (!isNode(element)) {
return null;
}
return (
<DefaultGroup hulledOutline={false} labelPosition={verticalLayout ? LabelPosition.top : LabelPosition.bottom} element={element as Node} {...rest} />
<DefaultTaskGroup
labelPosition={verticalLayout ? LabelPosition.top : LabelPosition.bottom}
collapsible
collapsedWidth={DEFAULT_TASK_WIDTH}
collapsedHeight={DEFAULT_TASK_HEIGHT}
element={element as Node}
recreateLayoutOnCollapseChange
getEdgeCreationTypes={getEdgeCreationTypes}
{...rest}
/>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { createDemoPipelineGroupsNodes } from './createDemoPipelineGroupsNodes';
import { PipelineGroupsDemoContext, PipelineGroupsDemoModel } from './PipelineGroupsDemoContext';
import OptionsBar from './OptionsBar';
import DemoControlBar from '../DemoControlBar';
import pipelineElementFactory
from '@patternfly/react-topology/dist/esm/pipelines/elements/pipelineElementFactory';

const TopologyPipelineGroups: React.FC<{ nodes: PipelineNodeModel[] }> = observer(({ nodes }) => {
const controller = useVisualizationController();
Expand Down Expand Up @@ -64,6 +66,7 @@ TopologyPipelineGroups.displayName = 'TopologyPipelineLayout';

export const PipelineGroupsDemo = observer(() => {
const controller = new Visualization();
controller.registerElementFactory(pipelineElementFactory);
controller.registerComponentFactory(pipelineGroupsComponentFactory);
controller.registerLayoutFactory(
(type: string, graph: Graph): Layout | undefined =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable camelcase */
import { PipelineNodeModel, RunStatus } from '@patternfly/react-topology';

export const NODE_PADDING_VERTICAL = 15;
export const NODE_PADDING_VERTICAL = 0;
export const NODE_PADDING_HORIZONTAL = 15;

export const GROUP_PADDING_VERTICAL = 15;
export const GROUP_PADDING_VERTICAL = 40;
export const GROUP_PADDING_HORIZONTAL = 25;

export const DEFAULT_TASK_WIDTH = 180;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const pipelineGroupsComponentFactory: ComponentFactory = (
}
switch (type) {
case 'Execution':
return DemoTaskGroup;
return withSelection()(DemoTaskGroup);
case 'Task':
return withSelection()(DemoTaskNode);
case DEFAULT_SPACER_NODE_TYPE:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';
import {
DefaultTaskGroup,
GraphElement,
LabelPosition,
observer,
ScaleDetailsLevel,
WithContextMenuProps,
WithDragNodeProps,
WithSelectionProps,
} from '@patternfly/react-topology';

type DemoPipelinesGroupProps = {
element: GraphElement;
} & WithContextMenuProps &
WithDragNodeProps &
WithSelectionProps;

const DemoPipelinesGroup: React.FunctionComponent<DemoPipelinesGroupProps> = ({ element }) => {
const data = element.getData();
const detailsLevel = element.getGraph().getDetailsLevel()

return (
<DefaultTaskGroup
element={element}
collapsible={false}
showLabel={detailsLevel === ScaleDetailsLevel.high}
labelPosition={LabelPosition.top}
badge={data?.badge}
/>
);
};

export default observer(DemoPipelinesGroup);
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import DemoTaskNode from './DemoTaskNode';
import DemoFinallyNode from './DemoFinallyNode';
import DemoTaskGroupEdge from './DemoTaskGroupEdge';
import DemoTaskEdge from './DemoTaskEdge';
import DemoPipelinesGroup from "./DemoPipelinesGroup";

export const GROUPED_EDGE_TYPE = 'GROUPED_EDGE';
export const SPACER_EDGE_TYPE = 'spacer-edge';
Expand Down Expand Up @@ -55,7 +56,7 @@ const pipelineComponentFactory: ComponentFactory = (
case DEFAULT_FINALLY_NODE_TYPE:
return withContextMenu(() => defaultMenu)(withSelection()(DemoFinallyNode));
case 'task-group':
return DefaultTaskGroup;
return withSelection()(DemoPipelinesGroup);
case 'finally-group':
return DefaultTaskGroup;
case DEFAULT_SPACER_NODE_TYPE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
DEFAULT_TASK_NODE_TYPE,
DEFAULT_WHEN_OFFSET,
DEFAULT_WHEN_SIZE,
LabelPosition,
PipelineNodeModel,
RunStatus,
WhenStatus
Expand Down Expand Up @@ -149,7 +150,10 @@ export const useDemoPipelineNodes = (
type: 'task-group',
children: parallelTasks.map((t) => t.id),
group: true,
label: 'Parallel tasks'
label: 'Parallel tasks',
data: {
badge: 'Label',
}
});
}
}
Expand Down Expand Up @@ -190,7 +194,8 @@ export const useDemoPipelineNodes = (
type: 'task-group',
children: [],
group: true,
label: `Group ${task.data.columnGroup}`
label: `Group ${task.data.columnGroup}`,
labelPosition: LabelPosition.top
};
acc.push(taskGroup);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,4 @@ const stylesComponentFactory: ComponentFactory = (
}
};

export default stylesComponentFactory;
export default stylesComponentFactory;
1 change: 1 addition & 0 deletions packages/module/src/components/nodes/labels/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { default as LabelBadge } from './LabelBadge';
export { default as LabelContextMenu } from './LabelContextMenu';
export { default as LabelIcon } from './LabelIcon';
export { default as NodeLabel } from './NodeLabel';

Loading
Loading