Skip to content

Commit 5045968

Browse files
authored
Merge pull request #1014 from marian2js/master
fix: default link widget with react strict mode
2 parents 633d03a + 20766f5 commit 5045968

File tree

2 files changed

+84
-109
lines changed

2 files changed

+84
-109
lines changed

.changeset/moody-radios-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@projectstorm/react-diagrams-defaults': patch
3+
---
4+
5+
fix default link widget with react strict mode
Lines changed: 79 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import * as React from 'react';
21
import { DiagramEngine, LinkWidget, PointModel } from '@projectstorm/react-diagrams-core';
2+
import * as React from 'react';
3+
import { MouseEvent, useEffect, useRef } from 'react';
34
import { DefaultLinkModel } from './DefaultLinkModel';
45
import { DefaultLinkPointWidget } from './DefaultLinkPointWidget';
56
import { DefaultLinkSegmentWidget } from './DefaultLinkSegmentWidget';
6-
import { MouseEvent } from 'react';
77

88
export interface DefaultLinkProps {
99
link: DefaultLinkModel;
@@ -13,150 +13,120 @@ export interface DefaultLinkProps {
1313
selected?: (event: MouseEvent) => any;
1414
}
1515

16-
export interface DefaultLinkState {
17-
selected: boolean;
18-
}
16+
export const DefaultLinkWidget: React.FC<DefaultLinkProps> = (props) => {
17+
const [selected, setSelected] = React.useState(false);
18+
const refPaths = useRef<React.RefObject<SVGPathElement>[]>([]);
1919

20-
export class DefaultLinkWidget extends React.Component<DefaultLinkProps, DefaultLinkState> {
21-
refPaths: React.RefObject<SVGPathElement>[];
20+
const renderPoints = () => {
21+
return props.renderPoints ?? true;
22+
};
2223

23-
constructor(props: DefaultLinkProps) {
24-
super(props);
25-
this.refPaths = [];
26-
this.state = {
27-
selected: false
24+
useEffect(() => {
25+
props.link.setRenderedPaths(refPaths.current.map((ref) => ref.current).filter(Boolean) as SVGPathElement[]);
26+
return () => {
27+
props.link.setRenderedPaths([]);
2828
};
29-
}
30-
31-
renderPoints() {
32-
return this.props.renderPoints ?? true;
33-
}
34-
35-
componentDidUpdate(): void {
36-
this.props.link.setRenderedPaths(
37-
this.refPaths.map((ref) => {
38-
return ref.current;
39-
})
40-
);
41-
}
29+
}, [props.link]);
4230

43-
componentDidMount(): void {
44-
this.props.link.setRenderedPaths(
45-
this.refPaths.map((ref) => {
46-
return ref.current;
47-
})
48-
);
49-
}
50-
51-
componentWillUnmount(): void {
52-
this.props.link.setRenderedPaths([]);
53-
}
31+
const generateRef = () => {
32+
const ref = React.createRef<SVGPathElement>();
33+
refPaths.current.push(ref);
34+
return ref;
35+
};
5436

55-
addPointToLink(event: MouseEvent, index: number) {
37+
const addPointToLink = (event: MouseEvent, index: number) => {
5638
if (
5739
!event.shiftKey &&
58-
!this.props.link.isLocked() &&
59-
this.props.link.getPoints().length - 1 <= this.props.diagramEngine.getMaxNumberPointsPerLink()
40+
!props.link.isLocked() &&
41+
props.link.getPoints().length - 1 <= props.diagramEngine.getMaxNumberPointsPerLink()
6042
) {
61-
const position = this.props.diagramEngine.getRelativeMousePoint(event);
62-
const point = this.props.link.point(position.x, position.y, index);
43+
const position = props.diagramEngine.getRelativeMousePoint(event);
44+
const point = props.link.point(position.x, position.y, index);
6345
event.persist();
6446
event.stopPropagation();
65-
this.forceUpdate(() => {
66-
this.props.diagramEngine.getActionEventBus().fireAction({
67-
event,
68-
model: point
69-
});
47+
props.diagramEngine.getActionEventBus().fireAction({
48+
event,
49+
model: point
7050
});
7151
}
72-
}
52+
};
7353

74-
generatePoint(point: PointModel): JSX.Element {
54+
const generatePoint = (point: PointModel): JSX.Element => {
7555
return (
7656
<DefaultLinkPointWidget
7757
key={point.getID()}
7858
point={point as any}
79-
colorSelected={this.props.link.getOptions().selectedColor}
80-
color={this.props.link.getOptions().color}
59+
colorSelected={props.link.getOptions().selectedColor ?? ''}
60+
color={props.link.getOptions().color}
8161
/>
8262
);
83-
}
63+
};
8464

85-
generateLink(path: string, extraProps: any, id: string | number): JSX.Element {
86-
const ref = React.createRef<SVGPathElement>();
87-
this.refPaths.push(ref);
65+
const generateLink = (path: string, extraProps: any, id: string | number): JSX.Element => {
8866
return (
8967
<DefaultLinkSegmentWidget
9068
key={`link-${id}`}
9169
path={path}
92-
selected={this.state.selected}
93-
diagramEngine={this.props.diagramEngine}
94-
factory={this.props.diagramEngine.getFactoryForLink(this.props.link)}
95-
link={this.props.link}
96-
forwardRef={ref}
97-
onSelection={(selected) => {
98-
this.setState({ selected: selected });
99-
}}
70+
selected={selected}
71+
diagramEngine={props.diagramEngine}
72+
factory={props.diagramEngine.getFactoryForLink(props.link)}
73+
link={props.link}
74+
forwardRef={generateRef()}
75+
onSelection={setSelected}
10076
extras={extraProps}
10177
/>
10278
);
103-
}
104-
105-
render() {
106-
//ensure id is present for all points on the path
107-
var points = this.props.link.getPoints();
108-
var paths = [];
109-
this.refPaths = [];
79+
};
80+
81+
const points = props.link.getPoints();
82+
const paths = [];
83+
refPaths.current = []; // Reset the refPaths for the current render
84+
85+
if (points.length === 2) {
86+
paths.push(
87+
generateLink(
88+
props.link.getSVGPath(),
89+
{
90+
onMouseDown: (event: MouseEvent) => {
91+
props.selected?.(event);
92+
addPointToLink(event, 1);
93+
}
94+
},
95+
'0'
96+
)
97+
);
11098

111-
if (points.length === 2) {
99+
if (props.link.getTargetPort() == null) {
100+
paths.push(generatePoint(points[1]));
101+
}
102+
} else {
103+
for (let j = 0; j < points.length - 1; j++) {
112104
paths.push(
113-
this.generateLink(
114-
this.props.link.getSVGPath(),
105+
generateLink(
106+
LinkWidget.generateLinePath(points[j], points[j + 1]),
115107
{
116-
onMouseDown: (event) => {
117-
this.props.selected?.(event);
118-
this.addPointToLink(event, 1);
108+
'data-linkid': props.link.getID(),
109+
'data-point': j,
110+
onMouseDown: (event: MouseEvent) => {
111+
props.selected?.(event);
112+
addPointToLink(event, j + 1);
119113
}
120114
},
121-
'0'
115+
j
122116
)
123117
);
118+
}
124119

125-
// draw the link as dangeling
126-
if (this.props.link.getTargetPort() == null) {
127-
paths.push(this.generatePoint(points[1]));
128-
}
129-
} else {
130-
//draw the multiple anchors and complex line instead
131-
for (let j = 0; j < points.length - 1; j++) {
132-
paths.push(
133-
this.generateLink(
134-
LinkWidget.generateLinePath(points[j], points[j + 1]),
135-
{
136-
'data-linkid': this.props.link.getID(),
137-
'data-point': j,
138-
onMouseDown: (event: MouseEvent) => {
139-
this.props.selected?.(event);
140-
this.addPointToLink(event, j + 1);
141-
}
142-
},
143-
j
144-
)
145-
);
120+
if (renderPoints()) {
121+
for (let i = 1; i < points.length - 1; i++) {
122+
paths.push(generatePoint(points[i]));
146123
}
147124

148-
if (this.renderPoints()) {
149-
//render the circles
150-
for (let i = 1; i < points.length - 1; i++) {
151-
paths.push(this.generatePoint(points[i]));
152-
}
153-
154-
if (this.props.link.getTargetPort() == null) {
155-
paths.push(this.generatePoint(points[points.length - 1]));
156-
}
125+
if (props.link.getTargetPort() == null) {
126+
paths.push(generatePoint(points[points.length - 1]));
157127
}
158128
}
159-
160-
return <g data-default-link-test={this.props.link.getOptions().testName}>{paths}</g>;
161129
}
162-
}
130+
131+
return <g data-default-link-test={props.link.getOptions().testName}>{paths}</g>;
132+
};

0 commit comments

Comments
 (0)