-
Notifications
You must be signed in to change notification settings - Fork 199
Description
Warning
The following issues may be implemented before the one described here (see below for details):
- Refactor Perimeter and EdgeStyle to support tree shaking #759
- Refactor Graph class and mixins to improve modularity and tree-shaking #762 (only the move of 2 Graph methods involved here)
Is your feature request related to a problem? Please describe.
In version 0.17.0, the methods Graph.isOrthogonal and Graph.createEdgeHandler contain hardcoded references to several EdgeStyle implementations. This tight coupling creates multiple issues:
-
Poor maintainability:
- When adding a new
EdgeStyleto maxGraph, these methods must be updated manually to ensure compatibility. - If this step is forgotten (as it was when
ManhattanConnectorwas introduced), bugs or inconsistencies can occur. See #707 for an example. - This design forces contributors and users to be aware of internal dependencies.
- When adding a new
-
Limited extensibility:
- Applications using custom
EdgeStyleimplementations must override these two methods to ensure correct behavior (this is what has to be done in the Wires story which introduces theWireConnector). - Inconsistent overrides (for example, missing the
isOrthogonaloverride) lead to incorrect rendering or behavior.
- Applications using custom
-
Broken tree-shaking:
- Because the methods reference all
EdgeStyletypes directly, all of them are bundled into the final build, regardless of actual usage. - For example, even if the application does not use
TopToBottomorManhattanConnector, they are still included. - This is especially problematic for large edge styles like
ManhattanConnector(several KB minified).
- Because the methods reference all
Reminder: what these methods do
isOrthogonal: Returnstrueif edge points should be computed such that the resulting edge has only horizontal or vertical segments.createEdgeHandler: Creates a newEdgeHandlerinstance for the givenCellState, depending on theEdgeStyleused to render the edge. The handler manages edge reconnections, control points, and label positioning.
Warning
These methods will be relocated in a future version (#762):
Graph.isOrthogonal→GraphViewGraph.createEdgeHandler→SelectionCellsHandler
Note
Be aware that in version 0.17.0, the definition of EdgeStyle makes tree shaking impossible.
Importing a single EdgeStyle function imports all of them due to how they are packaged together in the EdgeStyle class.
This must be addressed first to make the tree-shaking benefits of this proposal fully effective. See #759
Describe the solution you'd like
We want to provide a mechanism that offers the same behavior in createEdgeHandler and isOrthogonal as today, but without hardcoding references to specific EdgeStyle implementations.
Ideally, these methods should rely only on the EdgeStyle functions that are globally registered for rendering. This would solve both the maintainability issue and the tree-shaking limitation.
Categorizing EdgeStyle metadata at registration
Each EdgeStyle function should be registered with two distinct metadata fields, each serving one of the two internal behaviors we want to preserve:
createEdgeHandlerneeds to determine what type of handler to instantiateisOrthogonalneeds to determine whether the edge should follow orthogonal routing
Since these two concerns are independent, it is clearer and safer to define two explicit fields in the registration metadata.
For createEdgeHandler, we define a category field:
This field determines which EdgeHandler implementation to use:
'elbow''segment''default'(used when no specific category is matched)
For isOrthogonal, we use a simple boolean:
truefor styles that result in orthogonal routing (typically associated with'elbow'or'segment'handlers)falseotherwise
Note
Loop styles (for example, Loop) technically use the 'elbow' handler, but they should not be considered orthogonal in the context of isOrthogonal.
This registration mechanism gives us flexibility while maintaining clarity, and avoids reliance on naming conventions or type-checking against known functions.
Proposal
To implement this, we propose introducing a dedicated EdgeStyleRegistry. This new registry would replace hardcoded logic with a flexible metadata-based approach. It would serve as the single source of truth for determining how a given EdgeStyle should be handled, both in terms of which EdgeHandler to use and whether it should be considered orthogonal.
The registry would store two key metadata fields for each registered EdgeStyle function:
isOrthogonal: A boolean that indicates whether the style should be treated as orthogonaledgeHandlerCategory: A string category that identifies the appropriate handler type (for example,'elbow','segment','default'). If the category is not provided, a fallback handler (such asdefaultEdgeHandler) would be used
The StyleRegistry would remain, but its scope would be limited to Perimeter functions only, making its role more explicit and reducing the chances of confusion between style types.
Example registration API
To register a new EdgeStyle function, the API could look like this:
EdgeStyleRegistry.register(myEdgeStyleFunction, {
isOrthogonal: true,
edgeHandlerCategory: 'segment'
});This approach is simple, declarative, and scales well. If a style is not explicitly registered:
createEdgeHandlerwill fall back to a default handlerisOrthogonalwill returnfalse
If needed, naming can be adjusted (for example, edgeHandlerKind, handlerKind, isOrthogonalRouting), but the structure should remain straightforward.
Tip
The type of property categorizing the associated handler will be a string including the 3 builtin use cases (for type guidance) and will also any string to allow extension
Benefits
- Easier maintenance: no need to update core methods when adding or changing an
EdgeStyle, just register it with the right metadata. - Better tree-shaking: only registered and used
EdgeStylefunctions are included in the final bundle. - Improved extensibility: custom or third-party
EdgeStylefunctions can integrate cleanly, without needing to override internal methods. - Cleaner architecture: separating rendering logic from metadata makes the system easier to understand and maintain.
Drawbacks and considerations
This approach introduces flexibility, but it also adds some constraints. This mainly applies to applications that won't use the default builtins registration (#760):
- Registration becomes mandatory for default builtins provided by maxGraph: unregistered edge styles assigned to
CellStateStyle.edgeStylewill no longer work correctly withcreateEdgeHandlerandisOrthogonal.
In the prior implementation, using a builtin EdgeStyle without registration worked because it was hard coded in the 2 Graph methods. - Risk of misconfiguration: forgetting to register a custom edge style, or registering a builtin edge style with incorrect metadata, may cause rendering issues.
Not registering a custom EdgeStyle will behave as in the past: it is considered as not orthogonal and the default EdgeHandler is used.
To reduce friction:
- The JSDoc for
CellStateStyle.edgeStylewill be updated to highlight the registration requirement. - The official documentation will clearly describe this behavior with examples.
Tip
Libraries or teams can provide a helper function to register their custom EdgeStyle functions with the appropriate metadata. This helps ensure consistency and reduces boilerplate.
Describe alternatives you've considered
We considered keeping StyleRegistry and introducing a second metadata registry. However, this would:
- Increase complexity
- Introduce a risk of mismatched registrations between style logic and metadata
Instead, we propose:
- Making
StyleRegistryexclusive toPerimeterfunctions - Introducing
EdgeStyleRegistryas the single registry forEdgeStylefunctions and their metadata
Tasks
- Introduce
EdgeStyleRegistryand use it inGraphViewto resolve the actualEdgeStylefunction from thecellStyle.edgeStylevalue - Use it in
Graph.isOrthogonalto retrieve the category of the currentEdgeStyle - Use it in
Graph.createEdgeHandlerto retrieve the category of the currentEdgeStyle - Update the JSDoc of
cellStyle.edgeStyleand the list of default edge styles; remove references toStyleRegistry - Introduce
PerimeterRegistryto accept onlyPerimeterfunctions as values (breaking change) - Remove
StyleRegistry(breaking change) - Add
EdgeStyleRegistryto the global configuration documentation - Add breaking changes in changelog