Skip to content

Commit dbaff70

Browse files
authored
refactor: optimize by loading only required graph features (#214)
Create `CustomGraph` that registers only the features needed by our examples instead of loading all built-in styles and shapes. This prepares for future tree-shaking improvements in maxGraph which will reduce bundle size.
1 parent 4d866cc commit dbaff70

2 files changed

Lines changed: 131 additions & 42 deletions

File tree

projects/_shared/src/custom-shapes.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import {CellRenderer, EllipseShape, RectangleShape} from '@maxgraph/core';
33

44
export const registerCustomShapes = (): void => {
55
console.info('Registering custom shapes...');
6-
// @ts-ignore TODO fix CellRenderer. Calls to this function are also marked as 'ts-ignore' in CellRenderer
6+
// TODO remove ts-ignore when maxGraph 0.18.0 is released
7+
// @ts-ignore
78
CellRenderer.registerShape('customRectangle', CustomRectangleShape);
89
// @ts-ignore
910
CellRenderer.registerShape('customEllipse', CustomEllipseShape);

projects/_shared/src/generate-graph.ts

Lines changed: 129 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,115 @@
11
import {
2+
type AbstractCanvas2D,
3+
CellRenderer,
24
constants,
3-
getDefaultPlugins,
5+
EdgeStyle,
6+
EllipseShape,
47
Graph,
5-
GraphDataModel,
68
InternalEvent,
9+
MarkerShape,
10+
PanningHandler,
711
Perimeter,
12+
type Point,
813
RubberBandHandler,
14+
SelectionCellsHandler,
15+
SelectionHandler,
16+
type Shape,
17+
type StyleArrowValue,
18+
StyleRegistry,
919
} from '@maxgraph/core';
1020
import {registerCustomShapes} from "./custom-shapes";
1121

22+
23+
// TODO remove this function when maxGraph 0.18.0 is released and import it from maxGraph instead using EdgeMarker.createArrow
24+
// It is currently duplicated from maxGraph as it is not exported in version 0.17.0
25+
const createArrow =
26+
(widthFactor: number) =>
27+
(
28+
canvas: AbstractCanvas2D,
29+
_shape: Shape,
30+
type: StyleArrowValue,
31+
pe: Point,
32+
unitX: number,
33+
unitY: number,
34+
size: number,
35+
_source: boolean,
36+
sw: number,
37+
filled: boolean
38+
) => {
39+
// The angle of the forward facing arrow sides against the x axis is
40+
// 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for
41+
// only half the strokewidth is processed ).
42+
const endOffsetX = unitX * sw * 1.118;
43+
const endOffsetY = unitY * sw * 1.118;
44+
45+
unitX *= size + sw;
46+
unitY *= size + sw;
47+
48+
const pt = pe.clone();
49+
pt.x -= endOffsetX;
50+
pt.y -= endOffsetY;
51+
52+
const f = type !== constants.ARROW.CLASSIC && type !== constants.ARROW.CLASSIC_THIN ? 1 : 3 / 4;
53+
pe.x += -unitX * f - endOffsetX;
54+
pe.y += -unitY * f - endOffsetY;
55+
56+
return () => {
57+
canvas.begin();
58+
canvas.moveTo(pt.x, pt.y);
59+
canvas.lineTo(
60+
pt.x - unitX - unitY / widthFactor,
61+
pt.y - unitY + unitX / widthFactor
62+
);
63+
64+
if (type === constants.ARROW.CLASSIC || type === constants.ARROW.CLASSIC_THIN) {
65+
canvas.lineTo(pt.x - (unitX * 3) / 4, pt.y - (unitY * 3) / 4);
66+
}
67+
68+
canvas.lineTo(
69+
pt.x + unitY / widthFactor - unitX,
70+
pt.y - unitY - unitX / widthFactor
71+
);
72+
canvas.close();
73+
74+
if (filled) {
75+
canvas.fillAndStroke();
76+
} else {
77+
canvas.stroke();
78+
}
79+
};
80+
};
81+
82+
/**
83+
* Create a custom implementation to not load all default built-in styles. This is because Graph registers them.
84+
*
85+
* In the future, we expect to have an implementation of Graph that does not do it.
86+
* See https://github.com/maxGraph/maxGraph/issues/760
87+
*/
88+
class CustomGraph extends Graph {
89+
/**
90+
* Only registers the elements required for this example. Do not let Graph load all default built-in styles.
91+
*/
92+
protected override registerDefaults() {
93+
// Register builtin shapes
94+
// RectangleShape is not registered here because it is always available. It is the fallback shape for vertices when no shape is returned by the registry
95+
// TODO remove ts-ignore when maxGraph 0.18.0 is released
96+
// @ts-ignore
97+
CellRenderer.registerShape('ellipse', EllipseShape);
98+
99+
// Register builtin styles
100+
StyleRegistry.putValue('ellipsePerimeter', Perimeter.EllipsePerimeter);
101+
StyleRegistry.putValue('rectanglePerimeter', Perimeter.RectanglePerimeter); // declared in the default vertex style, so must be registered to be used
102+
StyleRegistry.putValue('orthogonalEdgeStyle', EdgeStyle.OrthConnector);
103+
104+
const arrowFunction = createArrow(2);
105+
MarkerShape.addMarker('classic', arrowFunction);
106+
MarkerShape.addMarker('block', arrowFunction);
107+
108+
// Register custom shapes
109+
registerCustomShapes();
110+
}
111+
}
112+
12113
/**
13114
* Initializes the graph inside the given container.
14115
* @param container if not set, use the element matching the selector '#graph-container'
@@ -19,71 +120,60 @@ export const initializeGraph = (container?: HTMLElement) => {
19120
// Disables the built-in context menu
20121
InternalEvent.disableContextMenu(container);
21122

22-
const graph = new Graph(container, new GraphDataModel(), [
23-
...getDefaultPlugins(),
123+
const graph = new CustomGraph(container, undefined, [
124+
PanningHandler, // Enables panning with the mouse
24125
RubberBandHandler, // Enables rubber band selection
126+
SelectionCellsHandler, // Enables management of selected cells
127+
SelectionHandler, // Enables selection with the mouse
25128
]);
26129
graph.setPanning(true); // Use mouse right button for panning
27130

28131
// Customize the rubber band selection
29132
graph.getPlugin<RubberBandHandler>('RubberBandHandler').fadeOut = true;
30133

31-
// shapes and styles
32-
registerCustomShapes();
33134
// create a dedicated style for "ellipse" to share properties
34135
graph.getStylesheet().putCellStyle('myEllipse', {
35-
perimeter: Perimeter.EllipsePerimeter,
136+
perimeter: 'ellipsePerimeter',
36137
shape: 'ellipse',
37138
verticalAlign: 'top',
38139
verticalLabelPosition: 'bottom',
39140
});
40141

41-
// Gets the default parent for inserting new cells. This
42-
// is normally the first child of the root (ie. layer 0).
43-
const parent = graph.getDefaultParent();
44-
45142
// Adds cells to the model in a single step
46143
graph.batchUpdate(() => {
47-
// use the legacy insertVertex method
48-
const vertex01 = graph.insertVertex(
49-
parent,
50-
null,
51-
'a regular rectangle',
52-
10,
53-
10,
54-
100,
55-
100
144+
const vertex01 = graph.insertVertex({
145+
value: 'a regular rectangle',
146+
position: [10, 10],
147+
size: [100, 100],
148+
}
56149
);
57-
const vertex02 = graph.insertVertex(
58-
parent,
59-
null,
60-
'a regular ellipse',
61-
350,
62-
90,
63-
50,
64-
50,
65-
{
66-
baseStyleNames: ['myEllipse'],
67-
fillColor: 'orange',
150+
const vertex02 = graph.insertVertex({
151+
value: 'a regular ellipse',
152+
position: [350, 90],
153+
size: [50, 50],
154+
style: {
155+
baseStyleNames: ['myEllipse'],
156+
fillColor: 'orange',
157+
}
68158
}
69159
);
70-
// use the legacy insertEdge method
71-
graph.insertEdge(parent, null, 'an orthogonal style edge', vertex01, vertex02, {
72-
edgeStyle: constants.EDGESTYLE.ORTHOGONAL,
73-
rounded: true,
160+
graph.insertEdge({
161+
value: 'an orthogonal style edge',
162+
source: vertex01,
163+
target: vertex02,
164+
style: {
165+
edgeStyle: 'orthogonalEdgeStyle',
166+
rounded: true,
167+
}
74168
});
75169

76-
// insert vertex using custom shapes using the new insertVertex method
77170
const vertex11 = graph.insertVertex({
78-
parent,
79171
value: 'a custom rectangle',
80172
position: [20, 200],
81173
size: [100, 100],
82174
style: {shape: 'customRectangle'},
83175
});
84-
// use the new insertVertex method using position and size parameters
85176
const vertex12 = graph.insertVertex({
86-
parent,
87177
value: 'a custom ellipse',
88178
x: 150,
89179
y: 350,
@@ -94,9 +184,7 @@ export const initializeGraph = (container?: HTMLElement) => {
94184
shape: 'customEllipse',
95185
},
96186
});
97-
// use the new insertEdge method
98187
graph.insertEdge({
99-
parent,
100188
value: 'another edge',
101189
source: vertex11,
102190
target: vertex12,

0 commit comments

Comments
 (0)