You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 EdgeStyle to maxGraph, these methods must be updated manually to ensure compatibility.
If this step is forgotten (as it was when ManhattanConnector was introduced), bugs or inconsistencies can occur. See #707 for an example.
This design forces contributors and users to be aware of internal dependencies.
Limited extensibility:
Applications using custom EdgeStyle implementations must override these two methods to ensure correct behavior (this is what has to be done in the Wires story which introduces the WireConnector).
Inconsistent overrides (for example, missing the isOrthogonal override) lead to incorrect rendering or behavior.
Broken tree-shaking:
Because the methods reference all EdgeStyle types directly, all of them are bundled into the final build, regardless of actual usage.
For example, even if the application does not use TopToBottom or ManhattanConnector, they are still included.
This is especially problematic for large edge styles like ManhattanConnector (several KB minified).
Reminder: what these methods do
isOrthogonal: Returns true if edge points should be computed such that the resulting edge has only horizontal or vertical segments.
createEdgeHandler: Creates a new EdgeHandler instance for the given CellState, depending on the EdgeStyle used 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 → GraphView
Graph.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:
createEdgeHandler needs to determine what type of handler to instantiate
isOrthogonal needs 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:
true for styles that result in orthogonal routing (typically associated with 'elbow' or 'segment' handlers)
false otherwise
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 orthogonal
edgeHandlerCategory: A string category that identifies the appropriate handler type (for example, 'elbow', 'segment', 'default'). If the category is not provided, a fallback handler (such as defaultEdgeHandler) 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:
This approach is simple, declarative, and scales well. If a style is not explicitly registered:
createEdgeHandler will fall back to a default handler
isOrthogonal will return false
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 EdgeStyle functions are included in the final bundle.
Improved extensibility: custom or third-party EdgeStyle functions 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.edgeStyle will no longer work correctly with createEdgeHandler and isOrthogonal.
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.edgeStyle will 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 StyleRegistry exclusive to Perimeter functions
Introducing EdgeStyleRegistry as the single registry for EdgeStyle functions and their metadata
Tasks
Introduce EdgeStyleRegistry and use it in GraphView to resolve the actual EdgeStyle function from the cellStyle.edgeStyle value
Use it in Graph.isOrthogonal to retrieve the category of the current EdgeStyle
Use it in Graph.createEdgeHandler to retrieve the category of the current EdgeStyle
Update the JSDoc of cellStyle.edgeStyle and the list of default edge styles; remove references to StyleRegistry
Introduce PerimeterRegistry to accept only Perimeter functions as values (breaking change)
Remove StyleRegistry (breaking change)
Add EdgeStyleRegistry to the global configuration documentation
Warning
The following issues may be implemented before the one described here (see below for details):
Is your feature request related to a problem? Please describe.
In version 0.17.0, the methods
Graph.isOrthogonalandGraph.createEdgeHandlercontain hardcoded references to severalEdgeStyleimplementations. This tight coupling creates multiple issues:Poor maintainability:
EdgeStyleto maxGraph, these methods must be updated manually to ensure compatibility.ManhattanConnectorwas introduced), bugs or inconsistencies can occur. See #707 for an example.Limited extensibility:
EdgeStyleimplementations must override these two methods to ensure correct behavior (this is what has to be done in the Wires story which introduces theWireConnector).isOrthogonaloverride) lead to incorrect rendering or behavior.Broken tree-shaking:
EdgeStyletypes directly, all of them are bundled into the final build, regardless of actual usage.TopToBottomorManhattanConnector, they are still included.ManhattanConnector(several KB minified).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→SelectionCellsHandlerNote
Be aware that in version 0.17.0, the definition of
EdgeStylemakes tree shaking impossible.Importing a single
EdgeStylefunction imports all of them due to how they are packaged together in theEdgeStyleclass.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
createEdgeHandlerandisOrthogonalas today, but without hardcoding references to specificEdgeStyleimplementations.Ideally, these methods should rely only on the
EdgeStylefunctions that are globally registered for rendering. This would solve both the maintainability issue and the tree-shaking limitation.Categorizing EdgeStyle metadata at registration
Each
EdgeStylefunction 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 routingSince 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
EdgeHandlerimplementation 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)falseotherwiseNote
Loop styles (for example,
Loop) technically use the'elbow'handler, but they should not be considered orthogonal in the context ofisOrthogonal.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 givenEdgeStyleshould be handled, both in terms of whichEdgeHandlerto use and whether it should be considered orthogonal.The registry would store two key metadata fields for each registered
EdgeStylefunction: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 usedThe
StyleRegistrywould remain, but its scope would be limited toPerimeterfunctions only, making its role more explicit and reducing the chances of confusion between style types.Example registration API
To register a new
EdgeStylefunction, the API could look like this:This approach is simple, declarative, and scales well. If a style is not explicitly registered:
createEdgeHandlerwill fall back to a default handlerisOrthogonalwill returnfalseIf 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
EdgeStyle, just register it with the right metadata.EdgeStylefunctions are included in the final bundle.EdgeStylefunctions can integrate cleanly, without needing to override internal methods.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):
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.
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:
CellStateStyle.edgeStylewill be updated to highlight the registration requirement.Tip
Libraries or teams can provide a helper function to register their custom
EdgeStylefunctions with the appropriate metadata. This helps ensure consistency and reduces boilerplate.Describe alternatives you've considered
We considered keeping
StyleRegistryand introducing a second metadata registry. However, this would:Instead, we propose:
StyleRegistryexclusive toPerimeterfunctionsEdgeStyleRegistryas the single registry forEdgeStylefunctions and their metadataTasks
EdgeStyleRegistryand use it inGraphViewto resolve the actualEdgeStylefunction from thecellStyle.edgeStylevalueGraph.isOrthogonalto retrieve the category of the currentEdgeStyleGraph.createEdgeHandlerto retrieve the category of the currentEdgeStylecellStyle.edgeStyleand the list of default edge styles; remove references toStyleRegistryPerimeterRegistryto accept onlyPerimeterfunctions as values (breaking change)StyleRegistry(breaking change)EdgeStyleRegistryto the global configuration documentation