Skip to content

Commit

Permalink
Add support for BPMN groups and adapt subprocess visualization (#315)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiaslehnertum authored Oct 30, 2023
1 parent b49fbec commit 81b285f
Show file tree
Hide file tree
Showing 36 changed files with 222 additions and 172 deletions.
37 changes: 22 additions & 15 deletions src/main/components/uml-element/resizable/resizable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { ModelState } from '../../store/model-state';
import { styled } from '../../theme/styles';
import { UMLElementComponentProps } from '../uml-element-component-props';

type StateProps = {};
type StateProps = {
zoomFactor: number;
};

type DispatchProps = {
start: AsyncDispatch<typeof UMLElementRepository.startResizing>;
Expand All @@ -25,11 +27,16 @@ const initialState = {

type State = typeof initialState;

const enhance = connect<StateProps, DispatchProps, UMLElementComponentProps, ModelState>(null, {
start: UMLElementRepository.startResizing,
resize: UMLElementRepository.resize,
end: UMLElementRepository.endResizing,
});
const enhance = connect<StateProps, DispatchProps, UMLElementComponentProps, ModelState>(
(state) => ({
zoomFactor: state.editor.zoomFactor,
}),
{
start: UMLElementRepository.startResizing,
resize: UMLElementRepository.resize,
end: UMLElementRepository.endResizing,
},
);

const Handle = {
width: 15,
Expand Down Expand Up @@ -148,7 +155,7 @@ export const resizable =
break;
}

this.setState({ resizing: true, offset });
this.setState({ resizing: true, offset: offset.scale(1 / this.props.zoomFactor) });
this.props.start();
const element = event.currentTarget;
element.setPointerCapture(event.pointerId);
Expand All @@ -163,20 +170,20 @@ export const resizable =
let height = 0;
switch (resizeFrom) {
case ResizeFrom.BOTTOMRIGHT:
width = event.clientX - this.state.offset.x;
height = event.clientY - this.state.offset.y;
width = event.clientX / this.props.zoomFactor - this.state.offset.x;
height = event.clientY / this.props.zoomFactor - this.state.offset.y;
break;
case ResizeFrom.TOPLEFT:
width = -event.clientX - this.state.offset.x;
height = -event.clientY - this.state.offset.y;
width = -event.clientX / this.props.zoomFactor - this.state.offset.x;
height = -event.clientY / this.props.zoomFactor - this.state.offset.y;
break;
case ResizeFrom.TOPRIGHT:
width = event.clientX - this.state.offset.x;
height = -event.clientY - this.state.offset.y;
width = event.clientX / this.props.zoomFactor - this.state.offset.x;
height = -event.clientY / this.props.zoomFactor - this.state.offset.y;
break;
case ResizeFrom.BOTTOMLEFT:
width = -event.clientX - this.state.offset.x;
height = event.clientY - this.state.offset.y;
width = -event.clientX / this.props.zoomFactor - this.state.offset.x;
height = event.clientY / this.props.zoomFactor - this.state.offset.y;
break;
}
this.resize(width, height, resizeFrom);
Expand Down
3 changes: 2 additions & 1 deletion src/main/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@
"BPMNCallConversation": "Aufruf-Konversation",
"BPMNSequenceFlow": "Sequenz",
"BPMNMessageFlow": "Nachricht",
"BPMNAssociationFlow": "Assoziation"
"BPMNAssociationFlow": "Assoziation",
"BPMNGroup": "Gruppe"
}
}
}
3 changes: 2 additions & 1 deletion src/main/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@
"BPMNCallConversation": "Call Conversation",
"BPMNSequenceFlow": "Sequence",
"BPMNMessageFlow": "Message",
"BPMNAssociationFlow": "Association"
"BPMNAssociationFlow": "Association",
"BPMNGroup": "Group"
}
}
}
6 changes: 3 additions & 3 deletions src/main/packages/bpmn/bpmn-annotation/bpmn-annotation.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { BPMNElementType } from '..';
import { ILayer } from '../../../services/layouter/layer';
import { ILayoutable } from '../../../services/layouter/layoutable';
import { UMLElement } from '../../../services/uml-element/uml-element';
import { calculateNameBounds } from '../../../utils/name-bounds';
import { UMLElementType } from '../../uml-element-type';
import { UMLElementFeatures } from '../../../services/uml-element/uml-element-features';
import { UMLContainer } from '../../../services/uml-container/uml-container';

export class BPMNAnnotation extends UMLElement {
static features: UMLElementFeatures = { ...UMLElement.features };
export class BPMNAnnotation extends UMLContainer {
static features: UMLElementFeatures = { ...UMLContainer.features };

type: UMLElementType = BPMNElementType.BPMNAnnotation;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { BPMNElementType } from '..';
import { ILayer } from '../../../services/layouter/layer';
import { ILayoutable } from '../../../services/layouter/layoutable';
import { UMLElement } from '../../../services/uml-element/uml-element';
import { calculateNameBounds } from '../../../utils/name-bounds';
import { UMLElementType } from '../../uml-element-type';
import { UMLContainer } from '../../../services/uml-container/uml-container';

export class BPMNCallActivity extends UMLElement {
export class BPMNCallActivity extends UMLContainer {
type: UMLElementType = BPMNElementType.BPMNCallActivity;

render(canvas: ILayer): ILayoutable[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { IBoundary } from '../../../utils/geometry/boundary';
import { DeepPartial } from 'redux';
import { assign } from '../../../utils/fx/assign';
import * as Apollon from '../../../typings';
import { UMLContainer } from '../../../services/uml-container/uml-container';

export type BPMNConversationType = 'default' | 'call';

export class BPMNConversation extends UMLElement {
export class BPMNConversation extends UMLContainer {
static features: UMLElementFeatures = { ...UMLElement.features, resizable: false };
static defaultConversationType: BPMNConversationType = 'default';

Expand Down
7 changes: 7 additions & 0 deletions src/main/packages/bpmn/bpmn-diagram-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { BPMNTransaction } from './bpmn-transaction/bpmn-transaction';
import { BPMNCallActivity } from './bpmn-call-activity/bpmn-call-activity';
import { BPMNAnnotation } from './bpmn-annotation/bpmn-annotation';
import { BPMNConversation } from './bpmn-conversation/bpmn-conversation';
import { BPMNGroup } from './bpmn-group/bpmn-group';

export const composeBPMNPreview: ComposePreview = (
layer: ILayer,
Expand Down Expand Up @@ -47,6 +48,12 @@ export const composeBPMNPreview: ComposePreview = (
}),
);

elements.push(
new BPMNGroup({
bounds: defaultBounds,
}),
);

elements.push(
new BPMNAnnotation({
name: translate('packages.BPMN.BPMNAnnotation'),
Expand Down
10 changes: 5 additions & 5 deletions src/main/packages/bpmn/bpmn-end-event/bpmn-end-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ import { DeepPartial } from 'redux';
import { BPMNElementType, BPMNRelationshipType } from '..';
import { ILayer } from '../../../services/layouter/layer';
import { ILayoutable } from '../../../services/layouter/layoutable';
import { IUMLElement, UMLElement } from '../../../services/uml-element/uml-element';
import { UMLElementFeatures } from '../../../services/uml-element/uml-element-features';
import { assign } from '../../../utils/fx/assign';
import { IBoundary } from '../../../utils/geometry/boundary';
import { UMLElementType } from '../../uml-element-type';
import { UMLContainer } from '../../../services/uml-container/uml-container';

export class BPMNEndEvent extends UMLElement {
export class BPMNEndEvent extends UMLContainer {
static supportedRelationships = [BPMNRelationshipType.BPMNFlow];
static features: UMLElementFeatures = { ...UMLElement.features, resizable: false };
static features: UMLElementFeatures = { ...UMLContainer.features, resizable: false };

type: UMLElementType = BPMNElementType.BPMNEndEvent;
bounds: IBoundary = { ...this.bounds, width: 40, height: 40 };
name = 'End Event';

constructor(values?: DeepPartial<IUMLElement>) {
constructor(values?: DeepPartial<UMLContainer>) {
super(values);
assign<IUMLElement>(this, values);
assign<UMLContainer>(this, values);
}

render(canvas: ILayer): ILayoutable[] {
Expand Down
2 changes: 1 addition & 1 deletion src/main/packages/bpmn/bpmn-flow/bpmn-flow-update.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { styled } from '../../../components/theme/styles';
import { UMLElementRepository } from '../../../services/uml-element/uml-element-repository';
import { ExchangeIcon } from '../../../components/controls/icon/exchange';
import { UMLRelationshipRepository } from '../../../services/uml-relationship/uml-relationship-repository';
import { BPMNFlowType, BPMNFlow } from './bpmn-flow';
import { BPMNFlow, BPMNFlowType } from './bpmn-flow';
import { ColorButton } from '../../../components/controls/color-button/color-button';
import { StylePane } from '../../../components/style-pane/style-pane';
import { Dropdown } from '../../../components/controls/dropdown/dropdown';
Expand Down
10 changes: 5 additions & 5 deletions src/main/packages/bpmn/bpmn-gateway/bpmn-gateway.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { BPMNElementType } from '..';
import { ILayer } from '../../../services/layouter/layer';
import { ILayoutable } from '../../../services/layouter/layoutable';
import { IUMLElement, UMLElement } from '../../../services/uml-element/uml-element';
import { UMLElementType } from '../../uml-element-type';
import { UMLElementFeatures } from '../../../services/uml-element/uml-element-features';
import { IBoundary } from '../../../utils/geometry/boundary';
import { DeepPartial } from 'redux';
import { assign } from '../../../utils/fx/assign';
import * as Apollon from '../../../typings';
import { UMLContainer } from '../../../services/uml-container/uml-container';

export type BPMNGatewayType =
| 'complex'
Expand All @@ -18,8 +18,8 @@ export type BPMNGatewayType =
| 'parallel'
| 'parallel-event-based';

export class BPMNGateway extends UMLElement {
static features: UMLElementFeatures = { ...UMLElement.features, resizable: false };
export class BPMNGateway extends UMLContainer {
static features: UMLElementFeatures = { ...UMLContainer.features, resizable: false };
static defaultGatewayType: BPMNGatewayType = 'exclusive';

type: UMLElementType = BPMNElementType.BPMNGateway;
Expand All @@ -28,11 +28,11 @@ export class BPMNGateway extends UMLElement {

constructor(values?: DeepPartial<BPMNGateway>) {
super(values);
assign<IUMLElement>(this, values);
assign<BPMNGateway>(this, values);
this.gatewayType = values?.gatewayType || BPMNGateway.defaultGatewayType;
}

serialize(children?: UMLElement[]): Apollon.BPMNGateway {
serialize(children?: UMLContainer[]): Apollon.BPMNGateway {
return {
...super.serialize(),
type: this.type as keyof typeof BPMNElementType,
Expand Down
36 changes: 36 additions & 0 deletions src/main/packages/bpmn/bpmn-group/bpmn-group-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { FunctionComponent } from 'react';
import { BPMNGroup } from './bpmn-group';
import { ThemedRect } from '../../../components/theme/themedComponents';
import { Multiline } from '../../../utils/svg/multiline';

export const BPMNGroupComponent: FunctionComponent<Props> = ({ element, children }) => (
<g>
<ThemedRect
rx={10}
ry={10}
width="100%"
height="100%"
strokeColor={element.strokeColor}
fillColor="transparent"
strokeDasharray="4"
/>
<Multiline
x={element.bounds.width / 2}
y={element.bounds.height / 2}
width={element.bounds.width}
height={element.bounds.height}
fontWeight="bold"
fill={element.textColor}
lineHeight={16}
capHeight={11}
>
{element.name}
</Multiline>
{children}
</g>
);

interface Props {
element: BPMNGroup;
children?: React.ReactNode;
}
21 changes: 21 additions & 0 deletions src/main/packages/bpmn/bpmn-group/bpmn-group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { BPMNElementType } from '..';
import { ILayer } from '../../../services/layouter/layer';
import { ILayoutable } from '../../../services/layouter/layoutable';
import { UMLPackage } from '../../common/uml-package/uml-package';
import { calculateNameBounds } from '../../../utils/name-bounds';
import { UMLElementType } from '../../uml-element-type';
import { UMLElementFeatures } from '../../../services/uml-element/uml-element-features';

export class BPMNGroup extends UMLPackage {
static features: UMLElementFeatures = {
...UMLPackage.features,
connectable: false,
};

type: UMLElementType = BPMNElementType.BPMNGroup;

render(canvas: ILayer, children: ILayoutable[] = []): ILayoutable[] {
this.bounds = calculateNameBounds(this, canvas);
return [this, ...children];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ import { DeepPartial } from 'redux';
import { BPMNElementType, BPMNRelationshipType } from '..';
import { ILayer } from '../../../services/layouter/layer';
import { ILayoutable } from '../../../services/layouter/layoutable';
import { IUMLElement, UMLElement } from '../../../services/uml-element/uml-element';
import { UMLElementFeatures } from '../../../services/uml-element/uml-element-features';
import { assign } from '../../../utils/fx/assign';
import { IBoundary } from '../../../utils/geometry/boundary';
import { UMLElementType } from '../../uml-element-type';
import { UMLContainer } from '../../../services/uml-container/uml-container';

export class BPMNIntermediateEvent extends UMLElement {
export class BPMNIntermediateEvent extends UMLContainer {
static supportedRelationships = [BPMNRelationshipType.BPMNFlow];
static features: UMLElementFeatures = { ...UMLElement.features, resizable: false };
static features: UMLElementFeatures = { ...UMLContainer.features, resizable: false };

type: UMLElementType = BPMNElementType.BPMNIntermediateEvent;
bounds: IBoundary = { ...this.bounds, width: 40, height: 40 };

constructor(values?: DeepPartial<IUMLElement>) {
constructor(values?: DeepPartial<UMLContainer>) {
super(values);
assign<IUMLElement>(this, values);
assign<UMLContainer>(this, values);
}

render(canvas: ILayer): ILayoutable[] {
Expand Down
10 changes: 5 additions & 5 deletions src/main/packages/bpmn/bpmn-start-event/bpmn-start-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ import { DeepPartial } from 'redux';
import { BPMNElementType, BPMNRelationshipType } from '..';
import { ILayer } from '../../../services/layouter/layer';
import { ILayoutable } from '../../../services/layouter/layoutable';
import { IUMLElement, UMLElement } from '../../../services/uml-element/uml-element';
import { UMLElementFeatures } from '../../../services/uml-element/uml-element-features';
import { assign } from '../../../utils/fx/assign';
import { IBoundary } from '../../../utils/geometry/boundary';
import { UMLElementType } from '../../uml-element-type';
import { UMLContainer } from '../../../services/uml-container/uml-container';

export class BPMNStartEvent extends UMLElement {
export class BPMNStartEvent extends UMLContainer {
static supportedRelationships = [BPMNRelationshipType.BPMNFlow];
static features: UMLElementFeatures = { ...UMLElement.features, resizable: false };
static features: UMLElementFeatures = { ...UMLContainer.features, resizable: false };

type: UMLElementType = BPMNElementType.BPMNStartEvent;
bounds: IBoundary = { ...this.bounds, width: 40, height: 40 };
name = 'Start Event';

constructor(values?: DeepPartial<IUMLElement>) {
constructor(values?: DeepPartial<UMLContainer>) {
super(values);
assign<IUMLElement>(this, values);
assign<UMLContainer>(this, values);
}

render(canvas: ILayer): ILayoutable[] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FunctionComponent } from 'react';
import { BPMNSubprocess } from './bpmn-subprocess';
import { ThemedRect } from '../../../components/theme/themedComponents';
import { ThemedPolyline, ThemedRect } from '../../../components/theme/themedComponents';
import { Multiline } from '../../../utils/svg/multiline';

export const BPMNSubprocessComponent: FunctionComponent<Props> = ({ element, fillColor }) => (
Expand All @@ -12,7 +12,23 @@ export const BPMNSubprocessComponent: FunctionComponent<Props> = ({ element, fil
height="100%"
strokeColor={element.strokeColor}
fillColor={fillColor || element.fillColor}
strokeDasharray="4"
/>
<ThemedRect
x={element.bounds.width / 2 - 7}
y={element.bounds.height - 14}
width={14}
height={14}
strokeColor={element.strokeColor}
/>
<ThemedPolyline
points={`${element.bounds.width / 2 - 4} ${element.bounds.height - 7}, ${element.bounds.width / 2 + 4} ${
element.bounds.height - 7
}`}
/>
<ThemedPolyline
points={`${element.bounds.width / 2} ${element.bounds.height - 11}, ${element.bounds.width / 2} ${
element.bounds.height - 3
}`}
/>
<Multiline
x={element.bounds.width / 2}
Expand Down
4 changes: 2 additions & 2 deletions src/main/packages/bpmn/bpmn-subprocess/bpmn-subprocess.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { BPMNElementType } from '..';
import { ILayer } from '../../../services/layouter/layer';
import { ILayoutable } from '../../../services/layouter/layoutable';
import { UMLElement } from '../../../services/uml-element/uml-element';
import { calculateNameBounds } from '../../../utils/name-bounds';
import { UMLElementType } from '../../uml-element-type';
import { UMLContainer } from '../../../services/uml-container/uml-container';

export class BPMNSubprocess extends UMLElement {
export class BPMNSubprocess extends UMLContainer {
type: UMLElementType = BPMNElementType.BPMNSubprocess;

render(canvas: ILayer): ILayoutable[] {
Expand Down
Loading

0 comments on commit 81b285f

Please sign in to comment.