-
Notifications
You must be signed in to change notification settings - Fork 199
Description
Is your feature request related to a problem? Please describe.
Currently, all EdgeHandler implementations are always loaded, even if they are not used. This prevents proper tree-shaking and adds unnecessary weight to applications that don’t use all EdgeStyles.
EdgeHandlers are used to manage interactive behavior on selected edges. maxGraph provides 3 built-in handlers to match the main EdgeStyle implementations.
Historically, handler selection was based on:
- the
EdgeStyleimplementation, - or a
handlerKindcategory defined at registration (v0.20.0).
Historically, the creation logic for handlers lived in factory methods (create...Handler) on AbstractGraph. This had several drawbacks:
- all handlers were always imported, even if unused (e.g. for visualization-only use cases),
- customizing the handler meant subclassing both the handler and the factory methods.
Once #823 has been merged, those methods are removed from AbstractGraph, and tree-shaking is now possible when the SelectionCellsHandler plugin is not used.
Still, when the SelectionCellsHandler plugin is used, all EdgeHandler classes are declared and imported, even if some of the corresponding EdgeStyles are not registered or used.
This limits optimization, and we’d like to improve that further.
With tree-shaking enabled, only loading the necessary EdgeHandlers can decrease the bundle size.
The following figures are estimates done in #823:
| Handlers used | Decreased size |
|---|---|
Only default EdgeHandler |
−7–8 kB |
Only ElbowEdgeHandler |
−5 kB (inherits EdgeHandler) |
Only SegmentEdgeHandler |
−0 kB (inherits Elbow) |
Note
These numbers reflect the size reduction compared to the previous behavior, where all handlers were always bundled.
Describe the solution you'd like
We’d like to:
- only load the handlers actually used in the application, so avoid importing unused EdgeHandlers. Currently, in the
ts-example-selected-features, theSegmentEdgeHandleris unused but still included. - allow customizing handler behavior without subclassing them (prefer composition),
- configure them per
Graphinstance (local configuration, not global).
A minimal app that only uses the default connector should only load the default handler.
Describe alternatives you've considered
✅ Plugin with two variants
Have a base version of SelectionCellsHandler that only registers the default handler, and a second one (the current one) that registers all 3 handlers.
BaseSelectionCellsHandler: only registersEdgeHandlerSelectionCellsHandler: extends base and addsElbowEdgeHandlerandSegmentEdgeHandler
Warning
Both plugins would share the same plugin id for compatibility, but this may lead to confusion if misused.
Limitation: doesn’t support composability. Users can’t change the config of existing handlers without subclassing the plugin.
✅ Register handlers from the Graph constructor
Let Graph be responsible for registering the handlers. It would look up the plugin and register default handlers after plugin initialization.
BaseGraphwould not do this- this promotes composition over inheritance
- preserves current default behavior when using
Graph
Note
This would need to be clearly documented, since the behavior differs slightly from current defaults.
❌ Global registry
Store the handler map globally, like existing registries.
Drawback: goes against the local configuration pattern we introduced for plugins. Would also introduce implicit behavior, harder to debug.
✅ Dedicated plugin for handler selection
Introduce EdgeHandlerSelectorPlugin that stores the handler mapping.
SelectionCellsHandleruses it internally to retrieve the correct handler- for default behavior, we include
EdgeHandlerSelectorPluginin the default plugin list
✅ Pros:
- a single implementation of the selection plugin
- flexible and composable
- handler registration is explicit
- adds a dependency between plugins
- when using a custom plugin list, users must remember to include it
- overall complexity isn’t lower than having two plugin variants
Additional context
This topic has been previously discussed:
- in discussion #8
- and in discussion #151
It also relates to ongoing improvements for tree-shaking and plugin modularity.