All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Conflict with MudBlazor's javascript variable
- Support for .NET 9 (thanks to @PanayiotisTheodosiou)
registerDefaultBehaviorsargument toBlazorDiagramconstructor, it'strueby default and can be disabled if you want to control what behaviors are added and their order.
- Make
DiagramCanvasawait un-subscription to resizes subscription (thanks to @toluwtglobal)
- Exception thrown when
node.Visiblewasfalsefrom the creation and the library was trying to observe its resizes when the element won't exist (fixes #476).
- gzip versions of assets, it was causing an issue with .NET 9 (fixes #471, thanks a lot @robertmclaws)
- Support for .NET 8 (thanks to @SebastianWachsmuth)
- Link labels not showing in Firefox
- NRE in the DemoSite's Options page (thanks to @robertmclaws)
Routeproperty toBaseLinkModelto hold the result of the executed router
- Constraints not checked when using
RemoveControl(thanks to @K0369) - NRE Exception on the landing page demo when dragging a new link and not linking it to something (thanks to @K0369)
LinkVertexWidgetTestsfailing on cultures that are not using a dot as decimal separator (thanks to @K0369)- NRE exception in the Diagram Demo project (thanks to @Suraj0500)
Finally, the new documentation website is here!
Please don't hesitate to create issues for any problems or improvements.
PS: I suck at design.
AddLabelmethod to links to easily createLinkLabelModelAddVertexmethod to links to easily createLinkVertexModelControlledSizeproperty to nodes. Iftrue, the node will not be registered in theResizeObserver(saves a JS call).autoSizeargument toSvgGroupModelconstructor
- Renamed
Point.SubstracttoSubtract(duh) - Avoid rendering link selection helper on dragged link
SmoothPathGeneratornot working withLinkAnchor- Mouse overlapping dragged link
- Useless Console Logs from
GroupModelare now removed - JS exception in
(un)obervemethods when the element doesn't exist anymore
Styleparameter toPortRendererTargetAttachedto links, which triggers when a dragged link attaches to a target- If port snapping is enabled, it will trigger only once when you let go of the mouse
SuspendSortingtoDiagramin order to suspend sorting models in eachOrderChanged- If you know what you're doing, you could save some processing and avoid sorting everytime
RefreshOrdersto be called after unsuspending sorting in order to sort the models again and refresh the diagram
BaseLayer.Addnow returns the specific type given to it in argument- [BREAKING] CSS classes are now prefixed with
diagram-to avoid clashes with other librariesdiagram-group,diagram-node,diagram-link,diagram-port,diagram-link-label,diagram-link-vertex,diagram-control
- Portless links in children not refreshing when moving the parent group
- Link's
GetBoundsnot returning a valid box - Port snapping choosing the first port in radius rather than the closest one
- Remove
Console.WriteLinefromKeyboardShortcutsBehavior - Diagram overwriting
Orderwhen it's not zero (zero being the default int value, which we now consider as not set)
AdditionalSvgoption toDiagramCanvasin order to render any exatra SVG content you wantAdditionalHtmloption toDiagramCanvasin order to render any exatra SVG content you wantDistanceTooverload method toPointthat takes x and yMoveAlongLinemethod toPointFullPathtoPathGeneratorResultto represent the full path without cuts- Fallback router to Orthogonal router
- Margin options to
OrthogonalRouter radiusoption toStraightPathGeneratorin order to generate rounded bends- Support for custom vertices
AutoSizeoption to groups to control whether moving children resizes the group
- All routers are now classes instead of functions, they inherit from the new abstract class
Router - All path generators are now classes instead of functions, they inherit from the new abstract class
PathGenerator - Optimize Orthogonal router by using custom A* (x5 improvement)
RouterdelegatePathGeneratordelegate
- Initial version of Ordering!
- Nodes, groups and links can now be ordered using the new
Orderproperty orSendToFront/Backmethods Diagram.OrderedSelectablesreturns the ordered selectables/modelsDiagramCanvasnow uses this new property to render everything
- Nodes, groups and links can now be ordered using the new
GridSnapToCenteroption in order to snap nodes from their center instead of their top/left position (thanks to @Jeremy Vance)- More unit tests
Groupsis not a list of groups anymore, but a layer instead (just likeNodesandLinks)
- Deleting a group doesn't delete links attached to it
- Deleting a group inside of a group doesn't refresh the parent group
- Links not refreshing when a group's dimensions are updated directly (e.g. deleting a child)
- Layers causing more refreshes than intended
- All group-related methods and events from
Diagram, please use the new layer from now on
- Support for
LinkFactoryto return null in order to not create an ongoing link - Support for free links (no source/target required)
PositionAnchorwhich reprensents a simple plain position (mutable)ArrowHeadControlto control a link's Source/Target on the flyattachedcss class to attached links
- Replace
OngoingPositionwith the newPositionAnchorBaseLinkModel.Targetwill never be null anymore. An ongoing link will have a position anchor as the target
Links.Factorysignature now takes the diagram, source (model) and the target anchor- Move
DynamicAnchorback toAnchorsnamespace and seal allAnchorclasses
- Links attached to links not refreshing when the others are
LinkPathPositionProvidernot working with maxlength ratios- Deleting a link not deleting the links attached to it
PositionProviderargument fromExecutableControlfor more freedomIdandRefreshfromILinkable- Unused
OffsetfromAnchorand makeModelnullable
Movedevent to MovablesVisibleproperty andVisbilityChangedevent to modelsOptions.Virtualization(of type[Diagram]VirtualizationOptions) for virtualization optionsPointerEnter/Leaveevents for groups as well- Experimental Link to Link (using
LinkAnchor)
- Rename
RegisterModelComponenttoRegisterComponent - Rename
GetComponentForModeltoGetComponent - Virtualization is now handled by a behavior instead of NodeRender
- This means that it works for almost all models (nodes, groups and links)
- Render link labels without foreignObject in widget nor MarkupString (Thank you .NET 6)
- Custom link labels only need to contain relevant content, they don't need to handle positioning anymore
EnableVirtualizationoption (see added alternative)
.NET 6!
A lot of things changed in this version, a lot of breaking changes were introduced but I believe it was necessary.
Many changes were required to make everything clearer, customizable and cleaner (code wise).
I'm aiming to completely decouple the Core library from the UI, because I'm thinking of giving MAUI Diagrams a try very soon!
BlazorDiagramclass (inheritsDiagram) to the blazor package to replace the old Core oneBlazorDiagramOptionsthat inherit from the other diagram options to add Blazor (UI) specific optionsBlazor.Diagrams.Models.SvgNodeModelclass to represent a node that needs to be rendered in the SVG layerGetBehavior<T>method toDiagramin order to retrieve a registered behaviorKeyboardShortcutsBehaviorclass which handles keyboard shortcuts/actions:SetShortcut: sets an action (Func<Diagrambase, ValueTask>) to be executed whenever the specified combination is pressedRemoveShortcut: removes a defined action (if it exists)
KeyboardShortcutsDefaultscontaining the default shortcuts that were deleted (DeleteSelectionandGrouping)- Anchors functionality:
- An Anchor determines where on an element the link will connect
- Instead of links requiring the source and target to be either both nodes or both ports, there is now only one
SourceandTargetof typeAnchor - This lets the link not worry about the details of from/to where its going, as long as the anchor provides it with its position when asked for
- Current implementations:
SinglePortAnchor: Specifies that the connection point is a specific port (supports shape & alignment)ShapeIntersectionAnchor: Specifies that the connection point is the intersection of a line with the node's shapeDynamicAnchor: Specifies that the connection point is one of the given positions (closest)
- Virtual
IShape GetShape()method on nodes (defaultRectangle) and ports (defaultCircle) Options.LinksLayerOrderto indicate the order of the links layer (svg for blazor)Options.NodesLayerOrderto indicate the order of the nodes layer (html for blazor)- Support for SVG groups that also represent children as a hierarchy (
SvgGroupModel) - Node renderer will now append, in addition to
node locked, the classesselected grouped IHasBoundsandIHasShapeinterfaces to both nodes and portsIPositionProviderto encapsulate how certain positions are calculated given a model- They are used for dynamic anchors and controls for now
BoundsBasedPositionProviderreturns the position based on the bounds of the model (e.g. (0.5, 0.5) would be the center)ShapeAnglePositionProviderreturns the position as the point at the angle of the shapeLinkPathPositionProviderreturns the position based on the link's path (getPointAtLength)
- Links have a reference to
Diagramnow PointerEnterandPointerLeaveevents for nodes and links for nowGeneratedPathResultandPathstoBaseLinkModelto always have access to the actual pathsControlsfeature (beta):- They are things that can show up on top of nodes/links and can even be clicked to be executed
- Their UI is also picked up from the registered components
Controldesignates a control that has a position and will be rendered if visibleExecutableControldesignates a control that has a position and will be executed when pressed (PointerDown event)- Default controls for now are:
BoundaryControlshows the model's boundaryRemoveControlshows a button that when clicked, removes the model from the diagramDragNewLinkshows a button that when clicked, starts a new link dragging from that node
GridWidgeta background grid that moves with the diagram instead of being fixed like in the Snap to grid example- More unit tests
- Core package changes:
- Web dependency was removed from the Core package
Diagramis now abstract- These changes were done to decouple the core from the rendering, in the future we might have a MAUI renderer
- Web dependency was removed from the Core package
Diagram.GetComponentForModelnow accepts acheckSubclassesargument (defaulttrue)- Constraints now must return a
ValueTask<bool>instead of a simplebool - Renamed
AllLinkstoPortLinksfor more clarity on which links, sinceLinkscontains the others - Dragging links from ports will now follow the mouse at the same pace minus 5 pixels so that it doesn't go on top of the link it self or other ports
- How groups are rendered
GroupRendererwill take care of rendering the group with the appropriate style and classes- Only
GroupNodesis required,GroupLinkswas deleted because all links are shown in the svg layer (with appropriate order)
Diagram.AddGroupwill now return the added group- All
Mouseevents have been converted toPointerevents PathGeneratornow returnSvgPathinstead of just stringsNavigatorWidgetwas rewritten to be faster, lighter, WORKING and customizable- It now can also take the shape of the nodes into account (rect and ellipse for now)
- Virtualization throwing a JSException (#155)
- Ports not rendering correctly because of the missing
@key(#220) - Link not refreshing when a new vertex is created, which was showing out of link
DefaultNodeComponentandDefaultLinkComponentoptions (seeGetComponentForModelchanges)RenderLayerfrom the Core package and all its usageDeleteSelectionBehaviorsince there is a new keyboard shortcuts systemGroupingBehaviorsince there is a new keyboard shortcuts systemBaseLinkModelExtensionssince it was Obselete- Unnecessary port refreshes when dragging a link ends or when link snapping
ShapeDefinerdelegate and constructor arguments on nodes since delegates can't be serializedTouchXevents
ZoomBehaviorusing new zoom before Clamp to set Pan. (fixes #141)PanChangednot triggering when zooming with the mouse wheel.- Zoom value decreasing when the mouse wheel delta is zero.
- Ports aren't refreshed when links are added in
OnInitializedAsync. (fixes #111)
- Links not being removed from the node after they have been removed from the Links layer. (fixes #136)
- A regression in
ZoomToFit. (fixes #138)
MouseDoubleClick(500ms interval) event inDiagram.GetScreenPointinDiagramin order to get the screen points from a diagram point (e.g. node position).Titleproperty inNodeModel, used by the default node widget.
ZoomToFitnot triggeringZoomChangedevent.SourceNodeandTargetNodenot being set inBaseLinkModelwhen the ports change.
- Multiple demo projects by TrevorDArcyEvans showing:
- Minimal project setup
- Custom nodes & links
- GraphShape layout algorithms
- Link labels not appearing (fixes #109).
ZoomToFitthrowing aNullReferenceExceptionwhen one of the nodes doesn't have a size yet (fixes #118).
- Inability to interact with nodes after re-using ids on new node instances.
- Exception when navigating to the Options page (#95).
- Portless links not deleted when node is deleted (#96).
ScaleFactorzoom option (@joezearing)- Add
Constraintsoptions which gives you more control on what happens in the diagram:ShouldDeleteNode: Whether a selected node should be deleted or notShouldDeleteLink: Whether a selected link should be deleted or notShouldDeleteGroup: Whether a selected group should be deleted or not
DeleteSelectionBehaviorunit tests
GroupUngroupedevent (not the greatest name 😄)- Touch events
TouchStart,TouchMoveandTouchEnd - Mobile support
- Dragging new links from ports
- Selecting models
- Panning
- Dragging movables
RemoveGroupmethod to delete a group and all its children- Pressing DEL on a selected group will now remove it
- Ability for links to be segmentable (by setting
Segmentabletotrue)- Clicking a segmentable link will add a new vertex in that position
- Vertices are movable models, which mean you can drag them to reposition them
- Double clicking a vertex will delete it
- Link routers
- Routers are functions that take into account a link's vertices, and decide whether to add more points along the path
- They are specified using
Options.Links.DefaultRouteror by settingRouterin links - There are currently two routers,
Routers.NormalandRouters.Orthogonal - You can provide your own router by simply respecting the delegate
Router
- Link path generators
- Path generators are functions that takes as input a link's route, and return a list of generated SVG paths (strings), as well as the position and angle of the source/target markers
- They are specified using
Options.Links.DefaultPathGeneratoror by settingPathGeneratorin links - There are currently two generators,
PathGenerators.StraightandPathGenerators.Smooth - You can provide your own generator by simply respecting the delegate
PathGenerator
- On-the-fly link markers
- A link can either have a
SourceMarkerorTargetMarker - Link markers are SVG Paths with a known width (to calculate angle and where to cut the links)
- Static markers are available (e.g.
LinkMarker.Arrow) as well as utility methods to create known shapes
- A link can either have a
MouseClickevent, which only fires if the model (or canvas) in question didn't move, as opposed to MouseUpdata-X-idHTML attributes to nodes, ports and links- Abstract
BaseLinkModel,LinkModelnow inherits from it- All the parts of the library now refer to
BaseLinkModelinstead ofLinkModel(e.g. PortModel.Links)
- All the parts of the library now refer to
Color,SelectedColorandWidthproperties toLinkModel- Link labels
- Can have custom models, using
RegisterModelComponent - Due to the limitation of SVGs in Blazor, labels aren't interactive and must be created using
MarkupString - They can have a specified distance
- A number between 0 and 1: Position relative to the link's length
- A positive number, greater than 1: Position away from the start
- A negative number, less than 0: Position away from the end
- They can have an offset (Point)
- Can have custom models, using
- Link snapping
- While the user is dragging a link, it will try to find the closest port it can attach the link to
- Options (in
DiagramOptions.Links):EnableSnappingandSnappingRadius
GetBoundsextension method onIEnumerable<NodeModel>- Expose
Paddingproperty inGroupModel Factoryoption toOptions.Links- This is a delegate (
BaseLinkModel LinkFactory(DiagramManager diagram, PortModel sourcePort)) to let you control how links are created
- This is a delegate (
Groups(of typeDiagramGroupOptions) option toDiagramOptionsEnabledoption controls whether users are allow to group nodes together or notKeyboardShortcut(Func<KeyboardEventArgs, bool>) controls what keys need to be pressed to trigger the behaviorFactorylets you control how groups are created
GetRelativePointmethod toDiagram- This method gives the relative mouse point on the canvas
SelectionBoxWidget- If used, holding SHIFT and clicking then dragging will show a selection box that selects all models intersecting with it
- Minified versions of all assets
- Observe document body mutations to update canvas container
- Ability to add/remove children after the group is created
Diagramnow also inheritsModel- Ability to suspend refreshes in
Diagramby settingDiagram.SuspendRefreshtotrue- As the name suggests, this will stop the method
Refreshfrom triggering the eventChanged, which tries to re-render the UI
- As the name suggests, this will stop the method
Batch(Action action)inDiagram.- This is just a helper method that suspends refreshes, runs the action and then refreshes
- This is now used internally in node/link layers to only trigger 1 UI re-render
- Add
FillColorparameter toNavigatorWidget - Ability to create links between two nodes directly, without needing ports
- This depends on the shape of the nodes, which can be defined by providing a
ShapeDefinerinNodeModel's constructor - The default shape definer is
Shapes.Rectangle - Currently, there is only the Rectangle and Ellipse (including Circle) shapes, others will be added later
- This also works for groups, since groups are nodes
- This depends on the shape of the nodes, which can be defined by providing a
- Unit tests
- Renamed
DiagramManagertoDiagram - Remove need to specify
Namein diagram'sCascadingValue - Separate Nodes/Links from DiagramManager, they are now layers (
Diagram.NodesandDiagram.Links) -
- Adding/Removing nodes or links is now done inside these layers (e.g.
Diagram.Nodes.Add) - Added/Removed events are now inside these layers, not in the diagram (e.g.
Diagram.Links.Added)
- Adding/Removing nodes or links is now done inside these layers (e.g.
LinkAttachedevent removed (TargetPortChangedis the alternative)- It is now mandatory to add nodes to diagram before creating groups
SelectionChangedevent only contains the model now, useSelectedproperty- Removed
SelectedModelsin favor ofGetSelectedModels(), this is because we don't hold a list of selected models anymore (unnecessary) - Renamed
DiagramSubManagertoBehavior, it makes more sense RegisterBehaviornow takes as an argument the behavior instance to add. No need to useActivator.CreateInstancefor something like this, as it just slows things down- Removed
LinkTypeenum - Removed
DefaultLinkTypelink option - Removed
DefaultLinkModellink option - Removed
GetNodesRectmethod from DiagramManager (useGetBounds) - Removed diagram dependency from
GroupModel(was only usingGetNodesRect) - Renamed
GetRelativePointtoGetRelativeMousePoint- This method gives the relative mouse point inside the diagram, taking into account the current pan & zoom
Widgetsare inside theDiagramCanvasnow- This change is necessary so that widgets with absolute position have their relative parent be the canvas
- Renamed
Navigatorwidget toNavigatorWidget - Allow empty groups
- Compare received JS sizes (width and height) with precision of 0.0001
- In most cases, sizes retrieved from JS (especially with a zoom <> 1) can't be compared accurately (e.g.
80and79.9999975). We fix this by comparing with a tolerance.
- In most cases, sizes retrieved from JS (especially with a zoom <> 1) can't be compared accurately (e.g.
- Update ports dimensions only if Initialized is false
- This avoids useless re-renders/JS calls when node is re-visible
NodeModel.Portsis now aIReadOnlyList<PortModel>BaseLinkModelExtensionsis now obsolete- Moved everything in
Blazor.Diagrams.Core.Models.CoretoBlazor.Diagrams.Core.Geometry- This includes
Point,SizeandRectangle - This is to better structure things in the project, the new
Geometrynamespace will contain many other things related to it
- This includes
- Only render links when ports/nodes are initialized (position and/or size received)
- This will avoid the weird flicker where links show at (0, 0) then move to the correct position
- Remove links when groups are removed
- Issue where links are clickable outside the visible stroke
pointer-eventsis now set tovisiblePaintedinstead ofall
MouseUpevent bubbles up fromPortModeltoNodeModelSizenot taking into account zoom when nodes become visible again- Only allow link creation using left mouse button
- JS errors in razor pages without a diagram
- CustomNodeWidget was movable from text input
- All users who create custom nodes with HTML inputs should use
x:stopPropagationonmousedown,mousemoveandmouseupto prevent the node from being movable through inputs.
- All users who create custom nodes with HTML inputs should use
- Deleted nodes from groups would still show them
NavigatorWidgetnot handling negative node positions- Panning with right click. It is now disallowed
- PortRender not taking into account its parent's
RenderLayer- Ports on SVG nodes will now render as
<g>elements
- Ports on SVG nodes will now render as
- Useless refreshes when diagram
Containervalues didn't change
- Missing MouseUp event on links.
AddGroup: add an instance of a group to the diagram.- Custom group documentation/demo.
- Clicking the canvas in the Events demo throws an exception.
- The ability to have ports on groups.
- EXPERIMENTAL/INCOMPLETE Nested groups. Since
GroupModelnow inheritsNodeMode, it became possible to have nested groups, but there are still problems with the order of links between groups. - A
Classparameter toGroupContainer.
- Only rerender groups when necessary.
- Receiving the same size from
ResizeObserverdoesn't trigger a rerender anymore. - Avoid rerendering ports twice to update positions.
- Avoid rerendering ports when their parent node is moving.
- Padding is now handled in
GroupModelinstead ofGroupContainer(UI). This is because the padding is necessary to have accurate size/position in the group model directly.
- Use
@keywhen rendering the list of groups. Not using it caused big/weird render times. - Groups not showing in Navigator/Overview.
- Locked nodes now have a
lockedclass and their cursor is changed topointer.
EnableVirtualizationoption: whether to only render visible nodes or not.RegisterModelComponentoverload that takesTypes as input for dynamic registrations.
- Two zoom related options,
MinimumandMaximum, to clamp the zoom value.
- [BREAKING] Grouped zoom related options into
DiagramZoomOptions, available unnderOptions.Zoom.- The option
AllowZoomingwas renamed toEnabled. - The option
InverseZoomwas renamed toInverse.
- The option
- The diagram canvas' container wasn't updated when the user scrolls (#51).
- Abstract
MovableModel, which inherits fromSelectableModeland represents models that can be move with the mouse (e.g. nodes and groups). - Groups widget customization using
RegisterModelComponent. SizeChangedevent onNodeModel.
SelectableModelis now abstract.- Groups:
- Renamed model
GrouptoGroupModel. - Rendered as a single entity (HTML div) with padding.
- Movable and Selectable.
- Selecting a node inside a group doesn't select the others anymore.
- Renamed model
- [BREAKING] Renamed
DiagramManager.ChangePantoUpdatePan. - [BREAKING] Renamed
DiagramManager.ChangeZoomtoSetZoom.
ZoomToFitwasn't unhiding hidden nodes.
DefaultLinkModelinDiagramLinkOptions(@joezearing).- Ability to change source/target ports of a link (
SetSourcePortandSetTargetPort).
The methods also trigger the new eventsSourcePortChangedandTargetPortChanged.
- Curved link paths now take into account the ports alignements and a margin (@joezearing).
- The
AddLink<T>(T link, PortModel source, PortModel? target = null)overload inDiagramManageris now public.
This is useful when developers want to create the link instance themselves,DiagramManangerwill setup ports and trigger appropriate events.
GetMiddleTargetXandGetMiddleTargetYusingSourcePortinstead ofTargetPort(@joezearing).
A new project that aims to group all the algorithms that can be applied to DiagramManager.
It's a seperate package so that you only include it when you need it.
- Reconnect links to the closest ports (Idea & Initial work by @kolbjornb).
- Diagram Container not ready when ports/nodes need it.
- Drag & Drop demo.
- Helper method
DiagramManager.GetRelativePoint.
- Diagram container resizes don't update the offsets/position (top/left).
- Container changes don't trigger node visibility checks.
- Ports aren't refreshed when nodes are resized.
- Track mouseup events on nodes @joriskalz.
- Zooming in/out will now trigger the nodes visibility check.
AllowZoomingandAllowPanningoptions.DefaultColorandDefaultSelectedColorlink options.- Custom ports/links demos/documentation.
- Options documentation.
- Group link related options into
DiagramLinkOptions, available inDiagramOptions.Links. - All link related calculations (e.g.
MiddleSourceX) are now extension methods available inLinkModelExtensions.
LinkWidget's behind file is not needed anymore.