diff --git a/site/Site/Pages/Documentation/Diagram/Behaviors.razor b/site/Site/Pages/Documentation/Diagram/Behaviors.razor
index 540daba7e..094b33112 100644
--- a/site/Site/Pages/Documentation/Diagram/Behaviors.razor
+++ b/site/Site/Pages/Documentation/Diagram/Behaviors.razor
@@ -114,6 +114,20 @@ Diagram.UnregisterBehavior<SelectionBehavior>();
Diagram.RegisterBehavior(new MySelectionBehavior(Diagram));
+
Configure behaviors for different actions on input
+
+You can configure a behavior to perform different actions for an input. Such as scrolling a diagram on mouse wheel instead of zooming in and out.
+This can be done using the BehaviorOptions. See the below example.
+
+Scrolling a diagram on mouse wheel
+
+To scroll a diagram using the mouse wheel set the DiagramWheelBehavior property of BehaviorOptions to use ScrollBehavior.
+
+
+_diagram.BehaviorOptions.DiagramWheelBehavior = _diagram.GetBehavior<ScrollBehavior>();
+
+
+
Move(e.ClientX, e.ClientY);
-
- private void OnPointerUp(Model? model, PointerEventArgs e) => End();
-
- private void Start(Model? model, double clientX, double clientY, bool shiftKey)
- {
- if (!Diagram.Options.AllowPanning || model != null || shiftKey)
- return;
-
- _initialPan = Diagram.Pan;
- _lastClientX = clientX;
- _lastClientY = clientY;
- }
-
- private void Move(double clientX, double clientY)
- {
- if (!Diagram.Options.AllowPanning || _initialPan == null)
- return;
-
- var deltaX = clientX - _lastClientX - (Diagram.Pan.X - _initialPan.X);
- var deltaY = clientY - _lastClientY - (Diagram.Pan.Y - _initialPan.Y);
- Diagram.UpdatePan(deltaX, deltaY);
- }
-
- private void End()
- {
- if (!Diagram.Options.AllowPanning)
- return;
-
- _initialPan = null;
- }
-
- public override void Dispose()
+ public class PanBehavior : DragBehavior
{
- Diagram.PointerDown -= OnPointerDown;
- Diagram.PointerMove -= OnPointerMove;
- Diagram.PointerUp -= OnPointerUp;
+ private Point? _initialPan;
+ private double _lastClientX;
+ private double _lastClientY;
+
+ public PanBehavior(Diagram diagram) : base(diagram)
+ {
+ }
+
+ protected override void OnPointerDown(Model? model, PointerEventArgs e)
+ {
+ if (e.Button != (int)MouseEventButton.Left || model != null || !Diagram.Options.AllowPanning || !IsBehaviorEnabled(e))
+ return;
+
+ _initialPan = Diagram.Pan;
+ _lastClientX = e.ClientX;
+ _lastClientY = e.ClientY;
+ }
+
+ protected override void OnPointerMove(Model? model, PointerEventArgs e)
+ {
+ if (_initialPan == null)
+ return;
+
+ var deltaX = e.ClientX - _lastClientX - (Diagram.Pan.X - _initialPan.X);
+ var deltaY = e.ClientY - _lastClientY - (Diagram.Pan.Y - _initialPan.Y);
+ Diagram.UpdatePan(deltaX, deltaY);
+ }
+
+ protected override void OnPointerUp(Model? model, PointerEventArgs e)
+ {
+ _initialPan = null;
+ }
}
}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/ScrollBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/ScrollBehavior.cs
new file mode 100644
index 000000000..fa3ef63e1
--- /dev/null
+++ b/src/Blazor.Diagrams.Core/Behaviors/ScrollBehavior.cs
@@ -0,0 +1,25 @@
+using Blazor.Diagrams.Core.Behaviors.Base;
+using Blazor.Diagrams.Core.Events;
+using Blazor.Diagrams.Core.Options;
+
+namespace Blazor.Diagrams.Core.Behaviors
+{
+ public class ScrollBehavior : WheelBehavior
+ {
+ public ScrollBehavior(Diagram diagram)
+ : base(diagram)
+ {
+ }
+
+ protected override void OnDiagramWheel(WheelEventArgs e)
+ {
+ if (Diagram.Container == null || !IsBehaviorEnabled(e))
+ return;
+
+ var x = Diagram.Pan.X - (e.DeltaX / Diagram.Options.Zoom.ScaleFactor);
+ var y = Diagram.Pan.Y - (e.DeltaY / Diagram.Options.Zoom.ScaleFactor);
+
+ Diagram.SetPan(x, y);
+ }
+ }
+}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs
index 8c3d75ab8..3c1b6c3ae 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs
@@ -1,5 +1,6 @@
using Blazor.Diagrams.Core.Models.Base;
using Blazor.Diagrams.Core.Events;
+using Blazor.Diagrams.Core.Behaviors.Base;
namespace Blazor.Diagrams.Core.Behaviors;
diff --git a/src/Blazor.Diagrams.Core/Behaviors/SelectionBoxBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/SelectionBoxBehavior.cs
new file mode 100644
index 000000000..3364becd3
--- /dev/null
+++ b/src/Blazor.Diagrams.Core/Behaviors/SelectionBoxBehavior.cs
@@ -0,0 +1,79 @@
+using Blazor.Diagrams.Core.Behaviors.Base;
+using Blazor.Diagrams.Core.Events;
+using Blazor.Diagrams.Core.Geometry;
+using Blazor.Diagrams.Core.Models.Base;
+using System;
+using System.Linq;
+
+namespace Blazor.Diagrams.Core.Behaviors
+{
+ public class SelectionBoxBehavior : DragBehavior
+ {
+ private Point? _initialClientPoint;
+
+ public event EventHandler? SelectionBoundsChanged;
+
+ public SelectionBoxBehavior(Diagram diagram)
+ : base(diagram)
+ {
+ Diagram.PointerDown += OnPointerDown;
+ Diagram.PointerMove += OnPointerMove;
+ Diagram.PointerUp += OnPointerUp;
+ }
+
+ public override void Dispose()
+ {
+ Diagram.PointerDown -= OnPointerDown;
+ Diagram.PointerMove -= OnPointerMove;
+ Diagram.PointerUp -= OnPointerUp;
+ }
+
+ protected override void OnPointerDown(Model? model, PointerEventArgs e)
+ {
+ if (SelectionBoundsChanged is null || model != null || !IsBehaviorEnabled(e))
+ return;
+
+ _initialClientPoint = new Point(e.ClientX, e.ClientY);
+ }
+
+ protected override void OnPointerMove(Model? model, PointerEventArgs e)
+ {
+ if (_initialClientPoint == null)
+ return;
+
+ UpdateSelectionBox(e);
+
+ var start = Diagram.GetRelativeMousePoint(_initialClientPoint.X, _initialClientPoint.Y);
+ var end = Diagram.GetRelativeMousePoint(e.ClientX, e.ClientY);
+ var (sX, sY) = (Math.Min(start.X, end.X), Math.Min(start.Y, end.Y));
+ var (eX, eY) = (Math.Max(start.X, end.X), Math.Max(start.Y, end.Y));
+ var bounds = new Rectangle(sX, sY, eX, eY);
+
+ foreach (var node in Diagram.Nodes)
+ {
+ var nodeBounds = node.GetBounds();
+ if (nodeBounds == null)
+ continue;
+
+ if (bounds.Overlap(nodeBounds))
+ Diagram.SelectModel(node, false);
+ else if (node.Selected) Diagram.UnselectModel(node);
+ }
+ }
+
+ void UpdateSelectionBox(MouseEventArgs e)
+ {
+ var start = Diagram.GetRelativePoint(_initialClientPoint!.X, _initialClientPoint.Y);
+ var end = Diagram.GetRelativePoint(e.ClientX, e.ClientY);
+ var (sX, sY) = (Math.Min(start.X, end.X), Math.Min(start.Y, end.Y));
+ var (eX, eY) = (Math.Max(start.X, end.X), Math.Max(start.Y, end.Y));
+ SelectionBoundsChanged?.Invoke(this, new Rectangle(sX, sY, eX, eY));
+ }
+
+ protected override void OnPointerUp(Model? model, PointerEventArgs e)
+ {
+ _initialClientPoint = null;
+ SelectionBoundsChanged?.Invoke(this, null);
+ }
+ }
+}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/VirtualizationBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/VirtualizationBehavior.cs
index acd4dd7b0..7d0573b06 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/VirtualizationBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/VirtualizationBehavior.cs
@@ -1,3 +1,4 @@
+using Blazor.Diagrams.Core.Behaviors.Base;
using Blazor.Diagrams.Core.Models.Base;
namespace Blazor.Diagrams.Core.Behaviors;
diff --git a/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs
index 3da507b75..e8b939e51 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs
@@ -1,23 +1,19 @@
-using Blazor.Diagrams.Core.Events;
-
+using Blazor.Diagrams.Core.Behaviors.Base;
+using Blazor.Diagrams.Core.Events;
using System;
-namespace Blazor.Diagrams.Core.Behaviors;
-
-public class ZoomBehavior : Behavior
+namespace Blazor.Diagrams.Core.Behaviors
{
- public ZoomBehavior(Diagram diagram) : base(diagram)
+ public class ZoomBehavior : WheelBehavior
{
- Diagram.Wheel += Diagram_Wheel;
- }
-
- private void Diagram_Wheel(WheelEventArgs e)
- {
- if (Diagram.Container == null || e.DeltaY == 0)
- return;
+ public ZoomBehavior(Diagram diagram) : base(diagram)
+ {
+ }
- if (!Diagram.Options.Zoom.Enabled)
- return;
+ protected override void OnDiagramWheel(WheelEventArgs e)
+ {
+ if (Diagram.Container == null || e.DeltaY == 0 || !Diagram.Options.Zoom.Enabled || !IsBehaviorEnabled(e))
+ return;
var scale = Diagram.Options.Zoom.ScaleFactor;
var oldZoom = Diagram.Zoom;
@@ -41,15 +37,11 @@ private void Diagram_Wheel(WheelEventArgs e)
var newPanX = Diagram.Pan.X - widthDiff * xFactor;
var newPanY = Diagram.Pan.Y - heightDiff * yFactor;
- Diagram.Batch(() =>
- {
- Diagram.SetPan(newPanX, newPanY);
- Diagram.SetZoom(newZoom);
- });
- }
-
- public override void Dispose()
- {
- Diagram.Wheel -= Diagram_Wheel;
+ Diagram.Batch(() =>
+ {
+ Diagram.SetPan(newPanX, newPanY);
+ Diagram.SetZoom(newZoom);
+ });
+ }
}
}
diff --git a/src/Blazor.Diagrams.Core/Controls/ControlsBehavior.cs b/src/Blazor.Diagrams.Core/Controls/ControlsBehavior.cs
index 05148b5e6..82f53a592 100644
--- a/src/Blazor.Diagrams.Core/Controls/ControlsBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Controls/ControlsBehavior.cs
@@ -1,3 +1,4 @@
+using Blazor.Diagrams.Core.Behaviors.Base;
using Blazor.Diagrams.Core.Events;
using Blazor.Diagrams.Core.Models.Base;
diff --git a/src/Blazor.Diagrams.Core/Diagram.cs b/src/Blazor.Diagrams.Core/Diagram.cs
index a2fffeacb..5f7abba6c 100644
--- a/src/Blazor.Diagrams.Core/Diagram.cs
+++ b/src/Blazor.Diagrams.Core/Diagram.cs
@@ -10,6 +10,7 @@
using System.Runtime.CompilerServices;
using Blazor.Diagrams.Core.Options;
using Blazor.Diagrams.Core.Controls;
+using Blazor.Diagrams.Core.Behaviors.Base;
[assembly: InternalsVisibleTo("Blazor.Diagrams")]
[assembly: InternalsVisibleTo("Blazor.Diagrams.Tests")]
@@ -47,6 +48,7 @@ protected Diagram()
Links = new LinkLayer(this);
Groups = new GroupLayer(this);
Controls = new ControlsLayer();
+ BehaviorOptions = new DiagramBehaviorOptions();
Nodes.Added += OnSelectableAdded;
Links.Added += OnSelectableAdded;
@@ -56,18 +58,15 @@ protected Diagram()
Links.Removed += OnSelectableRemoved;
Groups.Removed += OnSelectableRemoved;
- RegisterBehavior(new SelectionBehavior(this));
- RegisterBehavior(new DragMovablesBehavior(this));
- RegisterBehavior(new DragNewLinkBehavior(this));
- RegisterBehavior(new PanBehavior(this));
- RegisterBehavior(new ZoomBehavior(this));
- RegisterBehavior(new EventsBehavior(this));
- RegisterBehavior(new KeyboardShortcutsBehavior(this));
- RegisterBehavior(new ControlsBehavior(this));
- RegisterBehavior(new VirtualizationBehavior(this));
+ RegisterDefaultBehaviors();
+
+ BehaviorOptions.DiagramDragBehavior ??= GetBehavior();
+ BehaviorOptions.DiagramShiftDragBehavior ??= GetBehavior();
+ BehaviorOptions.DiagramWheelBehavior ??= GetBehavior();
}
public abstract DiagramOptions Options { get; }
+ public DiagramBehaviorOptions BehaviorOptions { get; }
public NodeLayer Nodes { get; }
public LinkLayer Links { get; }
public GroupLayer Groups { get; }
@@ -170,6 +169,21 @@ public void UnselectAll()
#region Behaviors
+ void RegisterDefaultBehaviors()
+ {
+ RegisterBehavior(new SelectionBehavior(this));
+ RegisterBehavior(new DragMovablesBehavior(this));
+ RegisterBehavior(new DragNewLinkBehavior(this));
+ RegisterBehavior(new PanBehavior(this));
+ RegisterBehavior(new ZoomBehavior(this));
+ RegisterBehavior(new EventsBehavior(this));
+ RegisterBehavior(new KeyboardShortcutsBehavior(this));
+ RegisterBehavior(new ControlsBehavior(this));
+ RegisterBehavior(new VirtualizationBehavior(this));
+ RegisterBehavior(new ScrollBehavior(this));
+ RegisterBehavior(new SelectionBoxBehavior(this));
+ }
+
public void RegisterBehavior(Behavior behavior)
{
var type = behavior.GetType();
@@ -402,5 +416,5 @@ private void OnModelOrderChanged(Model model)
public void TriggerPointerDoubleClick(Model? model, PointerEventArgs e) => PointerDoubleClick?.Invoke(model, e);
- #endregion
+ #endregion
}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams.Core/Options/DiagramBehaviorOptions.cs b/src/Blazor.Diagrams.Core/Options/DiagramBehaviorOptions.cs
new file mode 100644
index 000000000..a0b7b0aa7
--- /dev/null
+++ b/src/Blazor.Diagrams.Core/Options/DiagramBehaviorOptions.cs
@@ -0,0 +1,26 @@
+using Blazor.Diagrams.Core.Behaviors;
+using Blazor.Diagrams.Core.Behaviors.Base;
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Blazor.Diagrams.Core.Options
+{
+ public class DiagramBehaviorOptions
+ {
+ public WheelBehavior? DiagramWheelBehavior { get; set; }
+
+ public WheelBehavior? DiagramAltWheelBehavior { get; set; }
+
+ public WheelBehavior? DiagramCtrlWheelBehavior { get; set; }
+
+ public WheelBehavior? DiagramShiftWheelBehavior { get; set; }
+
+ public DragBehavior? DiagramDragBehavior { get; set; }
+
+ public DragBehavior? DiagramAltDragBehavior { get; set; }
+
+ public DragBehavior? DiagramCtrlDragBehavior { get; set; }
+
+ public DragBehavior? DiagramShiftDragBehavior { get; set; }
+ }
+}
diff --git a/src/Blazor.Diagrams.Core/Options/DiagramOptions.cs b/src/Blazor.Diagrams.Core/Options/DiagramOptions.cs
index 4175eda0e..9fcb6ed0e 100644
--- a/src/Blazor.Diagrams.Core/Options/DiagramOptions.cs
+++ b/src/Blazor.Diagrams.Core/Options/DiagramOptions.cs
@@ -1,3 +1,5 @@
+using Blazor.Diagrams.Core.Behaviors;
+
namespace Blazor.Diagrams.Core.Options;
public class DiagramOptions
diff --git a/src/Blazor.Diagrams/BlazorDiagram.cs b/src/Blazor.Diagrams/BlazorDiagram.cs
index ab61d98a8..0b087c89b 100644
--- a/src/Blazor.Diagrams/BlazorDiagram.cs
+++ b/src/Blazor.Diagrams/BlazorDiagram.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using Blazor.Diagrams.Components.Controls;
using Blazor.Diagrams.Core;
+using Blazor.Diagrams.Core.Behaviors;
using Blazor.Diagrams.Core.Controls.Default;
using Blazor.Diagrams.Core.Models.Base;
using Blazor.Diagrams.Options;
diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs
index 8c723e043..bf20a7e07 100644
--- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs
+++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
+using Blazor.Diagrams.Core.Behaviors;
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Extensions;
using Microsoft.AspNetCore.Components;
@@ -62,6 +63,10 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
{
BlazorDiagram.SetContainer(await JSRuntime.GetBoundingClientRect(elementReference));
await JSRuntime.ObserveResizes(elementReference, _reference!);
+ if (BlazorDiagram.BehaviorOptions.DiagramWheelBehavior is ScrollBehavior)
+ {
+ await JSRuntime.AddDefaultPreventingForWheelHandler(elementReference);
+ }
}
}
diff --git a/src/Blazor.Diagrams/Components/Widgets/SelectionBoxWidget.razor b/src/Blazor.Diagrams/Components/Widgets/SelectionBoxWidget.razor
index 4ad4e761e..5cfa9417e 100644
--- a/src/Blazor.Diagrams/Components/Widgets/SelectionBoxWidget.razor
+++ b/src/Blazor.Diagrams/Components/Widgets/SelectionBoxWidget.razor
@@ -1,4 +1,4 @@
-@if (_selectionBoxTopLeft != null && _selectionBoxSize != null)
+@if (_selectionBounds is not null)
{
}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams/Components/Widgets/SelectionBoxWidget.razor.cs b/src/Blazor.Diagrams/Components/Widgets/SelectionBoxWidget.razor.cs
index 738fabf1a..988166755 100644
--- a/src/Blazor.Diagrams/Components/Widgets/SelectionBoxWidget.razor.cs
+++ b/src/Blazor.Diagrams/Components/Widgets/SelectionBoxWidget.razor.cs
@@ -1,4 +1,5 @@
using System;
+using Blazor.Diagrams.Core.Behaviors;
using Blazor.Diagrams.Core.Events;
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Models.Base;
@@ -8,9 +9,8 @@ namespace Blazor.Diagrams.Components.Widgets;
public partial class SelectionBoxWidget : IDisposable
{
- private Point? _initialClientPoint;
- private Size? _selectionBoxSize; // Todo: remove unneeded instantiations
- private Point? _selectionBoxTopLeft; // Todo: remove unneeded instantiations
+ private Rectangle? _selectionBounds;
+ private SelectionBoxBehavior? _selectionBoxBehavior;
[CascadingParameter] public BlazorDiagram BlazorDiagram { get; set; } = null!;
@@ -18,74 +18,30 @@ public partial class SelectionBoxWidget : IDisposable
public void Dispose()
{
- BlazorDiagram.PointerDown -= OnPointerDown;
- BlazorDiagram.PointerMove -= OnPointerMove;
- BlazorDiagram.PointerUp -= OnPointerUp;
+ if (_selectionBoxBehavior is not null)
+ {
+ _selectionBoxBehavior.SelectionBoundsChanged -= SelectionBoundsChanged;
+ }
}
protected override void OnInitialized()
{
- BlazorDiagram.PointerDown += OnPointerDown;
- BlazorDiagram.PointerMove += OnPointerMove;
- BlazorDiagram.PointerUp += OnPointerUp;
- }
-
- private string GenerateStyle()
- {
- return FormattableString.Invariant(
- $"position: absolute; background: {Background}; top: {_selectionBoxTopLeft!.Y}px; left: {_selectionBoxTopLeft.X}px; width: {_selectionBoxSize!.Width}px; height: {_selectionBoxSize.Height}px;");
- }
-
- private void OnPointerDown(Model? model, MouseEventArgs e)
- {
- if (model != null || !e.ShiftKey)
- return;
-
- _initialClientPoint = new Point(e.ClientX, e.ClientY);
- }
-
- private void OnPointerMove(Model? model, MouseEventArgs e)
- {
- if (_initialClientPoint == null)
- return;
-
- SetSelectionBoxInformation(e);
-
- var start = BlazorDiagram.GetRelativeMousePoint(_initialClientPoint.X, _initialClientPoint.Y);
- var end = BlazorDiagram.GetRelativeMousePoint(e.ClientX, e.ClientY);
- var (sX, sY) = (Math.Min(start.X, end.X), Math.Min(start.Y, end.Y));
- var (eX, eY) = (Math.Max(start.X, end.X), Math.Max(start.Y, end.Y));
- var bounds = new Rectangle(sX, sY, eX, eY);
-
- foreach (var node in BlazorDiagram.Nodes)
+ _selectionBoxBehavior = BlazorDiagram.GetBehavior();
+ if (_selectionBoxBehavior is not null)
{
- var nodeBounds = node.GetBounds();
- if (nodeBounds == null)
- continue;
-
- if (bounds.Overlap(nodeBounds))
- BlazorDiagram.SelectModel(node, false);
- else if (node.Selected) BlazorDiagram.UnselectModel(node);
+ _selectionBoxBehavior.SelectionBoundsChanged += SelectionBoundsChanged;
}
-
- InvokeAsync(StateHasChanged);
}
- private void SetSelectionBoxInformation(MouseEventArgs e)
+ void SelectionBoundsChanged(object? sender, Rectangle? bounds)
{
- var start = BlazorDiagram.GetRelativePoint(_initialClientPoint!.X, _initialClientPoint.Y);
- var end = BlazorDiagram.GetRelativePoint(e.ClientX, e.ClientY);
- var (sX, sY) = (Math.Min(start.X, end.X), Math.Min(start.Y, end.Y));
- var (eX, eY) = (Math.Max(start.X, end.X), Math.Max(start.Y, end.Y));
- _selectionBoxTopLeft = new Point(sX, sY);
- _selectionBoxSize = new Size(eX - sX, eY - sY);
+ _selectionBounds = bounds;
+ InvokeAsync(StateHasChanged);
}
- private void OnPointerUp(Model? model, MouseEventArgs e)
+ private string GenerateStyle()
{
- _initialClientPoint = null;
- _selectionBoxTopLeft = null;
- _selectionBoxSize = null;
- InvokeAsync(StateHasChanged);
+ return FormattableString.Invariant(
+ $"position: absolute; background: {Background}; top: {_selectionBounds!.Top}px; left: {_selectionBounds.Left}px; width: {_selectionBounds.Width}px; height: {_selectionBounds.Height}px;");
}
}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs b/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs
index 459456863..5509f7428 100644
--- a/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs
+++ b/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs
@@ -30,4 +30,9 @@ public static async Task UnobserveResizes(this IJSRuntime jsRuntime, ElementRefe
{
await jsRuntime.InvokeVoidAsync("ZBlazorDiagrams.unobserve", element, element.Id);
}
+
+ public static async Task AddDefaultPreventingForWheelHandler(this IJSRuntime jsRuntime, ElementReference element)
+ {
+ await jsRuntime.InvokeVoidAsync("ZBlazorDiagrams.addDefaultPreventingHandler", element, "wheel");
+ }
}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams/wwwroot/script.js b/src/Blazor.Diagrams/wwwroot/script.js
index be3b4728c..3f25c16f8 100644
--- a/src/Blazor.Diagrams/wwwroot/script.js
+++ b/src/Blazor.Diagrams/wwwroot/script.js
@@ -45,6 +45,9 @@ var s = {
}
delete s.tracked[id];
delete s.canvases[id];
+ },
+ addDefaultPreventingHandler: (element, eventName) => {
+ element.addEventListener(eventName, e => e.preventDefault(), { passive: false });
}
};
window.ZBlazorDiagrams = s;
diff --git a/src/Blazor.Diagrams/wwwroot/script.min.js b/src/Blazor.Diagrams/wwwroot/script.min.js
index a4065277c..a900d0005 100644
--- a/src/Blazor.Diagrams/wwwroot/script.min.js
+++ b/src/Blazor.Diagrams/wwwroot/script.min.js
@@ -1 +1 @@
-var s={canvases:{},tracked:{},getBoundingClientRect:n=>n.getBoundingClientRect(),mo:new MutationObserver(()=>{for(id in s.canvases){const t=s.canvases[id],i=t.lastBounds,n=t.elem.getBoundingClientRect();(i.left!==n.left||i.top!==n.top||i.width!==n.width||i.height!==n.height)&&(t.lastBounds=n,t.ref.invokeMethodAsync("OnResize",n))}}),ro:new ResizeObserver(n=>{for(const t of n){let i=Array.from(t.target.attributes).find(n=>n.name.startsWith("_bl")).name.substring(4),n=s.tracked[i];n&&n.ref.invokeMethodAsync("OnResize",t.target.getBoundingClientRect())}}),observe:(n,t,i)=>{n&&(s.ro.observe(n),s.tracked[i]={ref:t},n.classList.contains("diagram-canvas")&&(s.canvases[i]={elem:n,ref:t,lastBounds:n.getBoundingClientRect()}))},unobserve:(n,t)=>{n&&s.ro.unobserve(n),delete s.tracked[t],delete s.canvases[t]}};window.ZBlazorDiagrams=s;window.addEventListener("scroll",()=>{for(id in s.canvases){const n=s.canvases[id];n.lastBounds=n.elem.getBoundingClientRect();n.ref.invokeMethodAsync("OnResize",n.lastBounds)}});s.mo.observe(document.body,{childList:!0,subtree:!0});
\ No newline at end of file
+var s = { canvases: {}, tracked: {}, getBoundingClientRect: e => e.getBoundingClientRect(), mo: new MutationObserver((() => { for (id in s.canvases) { const e = s.canvases[id], t = e.lastBounds, n = e.elem.getBoundingClientRect(); t.left === n.left && t.top === n.top && t.width === n.width && t.height === n.height || (e.lastBounds = n, e.ref.invokeMethodAsync("OnResize", n)) } })), ro: new ResizeObserver((e => { for (const t of e) { let e = Array.from(t.target.attributes).find((e => e.name.startsWith("_bl"))).name.substring(4), n = s.tracked[e]; n && n.ref.invokeMethodAsync("OnResize", t.target.getBoundingClientRect()) } })), observe: (e, t, n) => { e && (s.ro.observe(e), s.tracked[n] = { ref: t }, e.classList.contains("diagram-canvas") && (s.canvases[n] = { elem: e, ref: t, lastBounds: e.getBoundingClientRect() })) }, unobserve: (e, t) => { e && s.ro.unobserve(e), delete s.tracked[t], delete s.canvases[t] }, addDefaultPreventingHandler: (e, s) => { e.addEventListener(s, (e => e.preventDefault()), { passive: !1 }) } }; window.ZBlazorDiagrams = s, window.addEventListener("scroll", (() => { for (id in s.canvases) { const e = s.canvases[id]; e.lastBounds = e.elem.getBoundingClientRect(), e.ref.invokeMethodAsync("OnResize", e.lastBounds) } })), s.mo.observe(document.body, { childList: !0, subtree: !0 });
\ No newline at end of file
diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/PanBehaviorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/PanBehaviorTests.cs
new file mode 100644
index 000000000..3feee5090
--- /dev/null
+++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/PanBehaviorTests.cs
@@ -0,0 +1,54 @@
+using Blazor.Diagrams.Core.Behaviors;
+using Blazor.Diagrams.Core.Events;
+using Blazor.Diagrams.Core.Geometry;
+using Xunit;
+
+namespace Blazor.Diagrams.Core.Tests.Behaviors
+{
+ public class PanBehaviorTests
+ {
+ [Fact]
+ public void Behavior_WhenBehaviorEnabled_ShouldPan()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = diagram.GetBehavior();
+ diagram.SetContainer(new Rectangle(Point.Zero, new Size(100, 100)));
+
+ Assert.Equal(0, diagram.Pan.X);
+ Assert.Equal(0, diagram.Pan.Y);
+
+ // Act
+ diagram.TriggerPointerDown(null,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(200, 200, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ Assert.Equal(100, diagram.Pan.X);
+ Assert.Equal(100, diagram.Pan.Y);
+ }
+
+ [Fact]
+ public void Behavior_WhenBehaviorDisabled_ShouldNotPan()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = null;
+ diagram.SetContainer(new Rectangle(Point.Zero, new Size(100, 100)));
+
+ Assert.Equal(0, diagram.Pan.X);
+ Assert.Equal(0, diagram.Pan.Y);
+
+ // Act
+ diagram.TriggerPointerDown(null,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(200, 200, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ Assert.Equal(0, diagram.Pan.X);
+ Assert.Equal(0, diagram.Pan.Y);
+ }
+ }
+}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/ScrollBehaviorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/ScrollBehaviorTests.cs
new file mode 100644
index 000000000..acd78bbdc
--- /dev/null
+++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/ScrollBehaviorTests.cs
@@ -0,0 +1,43 @@
+using Blazor.Diagrams.Core.Behaviors;
+using Blazor.Diagrams.Core.Events;
+using Blazor.Diagrams.Core.Geometry;
+using Xunit;
+
+namespace Blazor.Diagrams.Core.Tests.Behaviors
+{
+ public class ScrollBehaviorTests
+ {
+ [Fact]
+ public void Behavior_WhenBehaviorEnabled_ShouldScroll()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramWheelBehavior = diagram.GetBehavior();
+ diagram.SetContainer(new Rectangle(Point.Zero, new Size(100, 100)));
+ diagram.Options.Zoom.ScaleFactor = 1.05;
+
+ // Act
+ diagram.TriggerWheel(new WheelEventArgs(100, 100, 0, 0, false, false, false, 100, 200, 0, 0));
+
+ // Assert
+ Assert.Equal(-95, diagram.Pan.X, 0);
+ Assert.Equal(-190, diagram.Pan.Y, 0);
+ }
+
+ [Fact]
+ public void Behavior_WhenBehaviorDisabled_ShouldNotScroll()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramWheelBehavior = null;
+ diagram.SetContainer(new Rectangle(Point.Zero, new Size(100, 100)));
+
+ // Act
+ diagram.TriggerWheel(new WheelEventArgs(100, 100, 0, 0, false, false, false, 100, 200, 0, 0));
+
+ // Assert
+ Assert.Equal(0, diagram.Pan.X);
+ Assert.Equal(0, diagram.Pan.Y);
+ }
+ }
+}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/SelectionBoxBehaviorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/SelectionBoxBehaviorTests.cs
new file mode 100644
index 000000000..ba0d94d6d
--- /dev/null
+++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/SelectionBoxBehaviorTests.cs
@@ -0,0 +1,131 @@
+using Blazor.Diagrams.Core.Behaviors;
+using Blazor.Diagrams.Core.Events;
+using Blazor.Diagrams.Core.Geometry;
+using Blazor.Diagrams.Core.Models;
+using Xunit;
+
+namespace Blazor.Diagrams.Core.Tests.Behaviors
+{
+ public class SelectionBoxBehaviorTests
+ {
+ [Fact]
+ public void Behavior_WhenBehaviorEnabled_ShouldUpdateSelectionBounds()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = diagram.GetBehavior();
+ diagram.SetContainer(new Rectangle(Point.Zero, new Size(100, 100)));
+
+ var selectionBoxBehavior = diagram.GetBehavior()!;
+ bool boundsChangedEventInvoked = false;
+ Rectangle? lastBounds = null;
+ selectionBoxBehavior.SelectionBoundsChanged += (_, newBounds) =>
+ {
+ boundsChangedEventInvoked = true;
+ lastBounds = newBounds;
+ };
+
+ // Act
+ diagram.TriggerPointerDown(null,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(200, 150, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ Assert.True(boundsChangedEventInvoked);
+ Assert.Equal(100, lastBounds!.Width);
+ Assert.Equal(50, lastBounds.Height);
+ Assert.Equal(100, lastBounds.Top);
+ Assert.Equal(100, lastBounds.Left);
+ }
+
+ [Fact]
+ public void Behavior_WhenBehaviorDisabled_ShouldNotUpdateSelectionBounds()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = null;
+ diagram.SetContainer(new Rectangle(Point.Zero, new Size(100, 100)));
+
+ var selectionBoxBehavior = diagram.GetBehavior()!;
+ bool boundsChangedEventInvoked = false;
+ Rectangle? lastBounds = null;
+ selectionBoxBehavior.SelectionBoundsChanged += (_, newBounds) =>
+ {
+ boundsChangedEventInvoked = true;
+ lastBounds = newBounds;
+ };
+
+ // Act
+ diagram.TriggerPointerDown(null,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(200, 150, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ Assert.False(boundsChangedEventInvoked);
+ Assert.Null(lastBounds);
+ }
+
+ [Fact]
+ public void Behavior_WithBoundsChangedDelegate_ShouldSelectNodesInsideArea()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = diagram.GetBehavior();
+ diagram.SetContainer(new Rectangle(Point.Zero, new Size(100, 100)));
+
+ var selectionBoxBehavior = diagram.GetBehavior()!;
+ selectionBoxBehavior.SelectionBoundsChanged += (_, _) => { };
+
+ var node = new NodeModel()
+ {
+ Size = new Size(100, 100),
+ Position = new Point(150, 150)
+ };
+ diagram.Nodes.Add(node);
+
+ // Act
+ diagram.TriggerPointerDown(null,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(300, 300, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ Assert.True(node.Selected);
+
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ Assert.False(node.Selected);
+ }
+
+ [Fact]
+ public void Behavior_WithoutBoundsChangedDelegate_ShouldNotSelectNodesInsideArea()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = diagram.GetBehavior();
+ diagram.SetContainer(new Rectangle(Point.Zero, new Size(100, 100)));
+
+ var node = new NodeModel()
+ {
+ Size = new Size(100, 100),
+ Position = new Point(150, 150)
+ };
+ diagram.Nodes.Add(node);
+
+ // Act
+ diagram.TriggerPointerDown(null,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(300, 300, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ Assert.False(node.Selected);
+
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ Assert.False(node.Selected);
+ }
+ }
+}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/ZoomBehaviorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/ZoomBehaviorTests.cs
new file mode 100644
index 000000000..526c9b6c9
--- /dev/null
+++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/ZoomBehaviorTests.cs
@@ -0,0 +1,40 @@
+using Blazor.Diagrams.Core.Behaviors;
+using Blazor.Diagrams.Core.Events;
+using Blazor.Diagrams.Core.Geometry;
+using Xunit;
+
+namespace Blazor.Diagrams.Core.Tests.Behaviors
+{
+ public class ZoomBehaviorTests
+ {
+ [Fact]
+ public void Behavior_WhenBehaviorEnabled_ShouldZoom()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramWheelBehavior = diagram.GetBehavior();
+ diagram.SetContainer(new Rectangle(0, 0, 100, 100));
+
+ // Act
+ diagram.TriggerWheel(new WheelEventArgs(100, 100, 0, 0, false, false, false, 0, 100, 0, 0));
+
+ // Assert
+ Assert.Equal(1.05, diagram.Zoom);
+ }
+
+ [Fact]
+ public void Behavior_WhenBehaviorDisabled_ShouldNotZoom()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramWheelBehavior = null;
+ diagram.SetContainer(new Rectangle(0, 0, 100, 100));
+
+ // Act
+ diagram.TriggerWheel(new WheelEventArgs(100, 100, 0, 0, false, false, false, 0, 100, 0, 0));
+
+ // Assert
+ Assert.Equal(1, diagram.Zoom);
+ }
+ }
+}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs b/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs
index 1b2201ef5..54a531908 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs
@@ -40,7 +40,7 @@ public async Task OnPointerDown_ShouldDeleteNodeTrue_RemovesNode()
// Arrange
RemoveControl removeControl = new(0, 0);
Diagram diagram = new TestDiagram(
- new Options.DiagramOptions
+ new Core.Options.DiagramOptions
{
Constraints =
{
@@ -63,7 +63,7 @@ public async Task OnPointerDown_ShouldDeleteNodeFalse_KeepsNode()
// Arrange
RemoveControl removeControl = new(0, 0);
Diagram diagram = new TestDiagram(
- new Options.DiagramOptions
+ new Core.Options.DiagramOptions
{
Constraints =
{
@@ -113,7 +113,7 @@ public async Task OnPointerDown_ShouldDeleteLinkTrue_RemovesLink()
// Arrange
RemoveControl removeControl = new(0, 0);
Diagram diagram = new TestDiagram(
- new Options.DiagramOptions
+ new Core.Options.DiagramOptions
{
Constraints =
{
@@ -147,7 +147,7 @@ public async Task OnPointerDown_ShouldDeleteLinkFalse_KeepsLink()
// Arrange
RemoveControl removeControl = new(0, 0);
Diagram diagram = new TestDiagram(
- new Options.DiagramOptions
+ new Core.Options.DiagramOptions
{
Constraints =
{
@@ -207,7 +207,7 @@ public async Task OnPointerDown_ShouldDeleteGroupTrue_RemovesGroup()
// Arrange
RemoveControl removeControl = new(0, 0);
Diagram diagram = new TestDiagram(
- new Options.DiagramOptions
+ new Core.Options.DiagramOptions
{
Constraints =
{
@@ -240,7 +240,7 @@ public async Task OnPointerDown_ShouldDeleteGroupFalse_KeepsGroup()
// Arrange
RemoveControl removeControl = new(0, 0);
Diagram diagram = new TestDiagram(
- new Options.DiagramOptions
+ new Core.Options.DiagramOptions
{
Constraints =
{
diff --git a/tests/Blazor.Diagrams.Core.Tests/Options/DiagramBehaviorOptionsTests.cs b/tests/Blazor.Diagrams.Core.Tests/Options/DiagramBehaviorOptionsTests.cs
new file mode 100644
index 000000000..62e73a38f
--- /dev/null
+++ b/tests/Blazor.Diagrams.Core.Tests/Options/DiagramBehaviorOptionsTests.cs
@@ -0,0 +1,127 @@
+using Blazor.Diagrams.Core.Behaviors;
+using Blazor.Diagrams.Core.Events;
+using Blazor.Diagrams.Core.Options;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Blazor.Diagrams.Core.Tests.Options
+{
+ public class DiagramBehaviorOptionsTests
+ {
+ [Fact]
+ public void DiagramBehaviorOptions_DragBehavior_IsBehaviorEnabled()
+ {
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = null;
+ Assert.False(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+
+ diagram.BehaviorOptions.DiagramDragBehavior = diagram.GetBehavior();
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+ }
+
+ [Fact]
+ public void DiagramBehaviorOptions_AltDragBehavior_IsBehaviorEnabled()
+ {
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = diagram.GetBehavior();
+ diagram.BehaviorOptions.DiagramAltDragBehavior = null;
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, false, false, true, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+
+ diagram.BehaviorOptions.DiagramDragBehavior = null;
+ Assert.False(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, false, false, true, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+
+ diagram.BehaviorOptions.DiagramAltDragBehavior = diagram.GetBehavior();
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, false, false, true, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+ }
+
+ [Fact]
+ public void DiagramBehaviorOptions_CtrlDragBehavior_IsBehaviorEnabled()
+ {
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = diagram.GetBehavior();
+ diagram.BehaviorOptions.DiagramCtrlDragBehavior = null;
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, true, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+
+ diagram.BehaviorOptions.DiagramDragBehavior = null;
+ Assert.False(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, true, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+
+ diagram.BehaviorOptions.DiagramCtrlDragBehavior = diagram.GetBehavior();
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, true, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+ }
+
+ [Fact]
+ public void DiagramBehaviorOptions_ShiftDragBehavior_IsBehaviorEnabled()
+ {
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = diagram.GetBehavior();
+ diagram.BehaviorOptions.DiagramShiftDragBehavior = null;
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, false, true, false, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+
+ diagram.BehaviorOptions.DiagramDragBehavior = null;
+ Assert.False(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, false, true, false, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+
+ diagram.BehaviorOptions.DiagramShiftDragBehavior = diagram.GetBehavior();
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new PointerEventArgs(0, 0, 0, 0, false, true, false, 0, 0, 0, 0, 0, 0, string.Empty, true)));
+ }
+
+ [Fact]
+ public void DiagramBehaviorOptions_DefaultScrollBehavior_IsBehaviorEnabled()
+ {
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramWheelBehavior = null;
+ Assert.False(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0)));
+
+ diagram.BehaviorOptions.DiagramWheelBehavior = diagram.GetBehavior();
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0)));
+ }
+
+ [Fact]
+ public void DiagramBehaviorOptions_AltScrollBehavior_IsBehaviorEnabled()
+ {
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramWheelBehavior = diagram.GetBehavior();
+ diagram.BehaviorOptions.DiagramAltWheelBehavior = null;
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, false, false, true, 0, 0, 0, 0)));
+
+ diagram.BehaviorOptions.DiagramWheelBehavior = null;
+ Assert.False(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, false, false, true, 0, 0, 0, 0)));
+
+ diagram.BehaviorOptions.DiagramAltWheelBehavior = diagram.GetBehavior();
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, false, false, true, 0, 0, 0, 0)));
+ }
+
+ [Fact]
+ public void DiagramBehaviorOptions_CtrlScrollBehavior_IsBehaviorEnabled()
+ {
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramWheelBehavior = diagram.GetBehavior();
+ diagram.BehaviorOptions.DiagramCtrlWheelBehavior = null;
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, true, false, false, 0, 0, 0, 0)));
+
+ diagram.BehaviorOptions.DiagramWheelBehavior = null;
+ Assert.False(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, true, false, false, 0, 0, 0, 0)));
+
+ diagram.BehaviorOptions.DiagramCtrlWheelBehavior = diagram.GetBehavior();
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, true, false, false, 0, 0, 0, 0)));
+ }
+
+ [Fact]
+ public void DiagramBehaviorOptions_ShiftScrollBehavior_IsBehaviorEnabled()
+ {
+ var diagram = new TestDiagram();
+ diagram.BehaviorOptions.DiagramWheelBehavior = diagram.GetBehavior();
+ diagram.BehaviorOptions.DiagramShiftWheelBehavior = null;
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, false, true, false, 0, 0, 0, 0)));
+
+ diagram.BehaviorOptions.DiagramWheelBehavior = null;
+ Assert.False(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, false, true, false, 0, 0, 0, 0)));
+
+ diagram.BehaviorOptions.DiagramShiftWheelBehavior = diagram.GetBehavior();
+ Assert.True(diagram.GetBehavior()!.IsBehaviorEnabled(new WheelEventArgs(0, 0, 0, 0, false, true, false, 0, 0, 0, 0)));
+ }
+ }
+}
diff --git a/tests/Blazor.Diagrams.Tests/Components/Widgets/SelectionBoxWidgetTests.cs b/tests/Blazor.Diagrams.Tests/Components/Widgets/SelectionBoxWidgetTests.cs
new file mode 100644
index 000000000..11d913539
--- /dev/null
+++ b/tests/Blazor.Diagrams.Tests/Components/Widgets/SelectionBoxWidgetTests.cs
@@ -0,0 +1,41 @@
+using Bunit;
+using Xunit;
+using Blazor.Diagrams.Components.Widgets;
+using AngleSharp.Css.Dom;
+using Blazor.Diagrams.Core.Events;
+using Blazor.Diagrams.Core.Geometry;
+using Blazor.Diagrams.Core.Behaviors;
+
+namespace Blazor.Diagrams.Tests.Components.Widgets
+{
+ public class SelectionBoxWidgetTests
+ {
+ [Fact]
+ public void SelectionBoxWidget_SelectionBoundsChanged_RendersSelectionBoxWidgetInCorrectLocation()
+ {
+ // Arrange
+ using var ctx = new TestContext();
+ var diagram = new BlazorDiagram();
+ diagram.BehaviorOptions.DiagramDragBehavior = diagram.GetBehavior();
+ diagram.SetPan(-75, -100);
+ diagram.SetContainer(new Rectangle(new Point(0, 0), new Size(500, 500)));
+
+ // Act
+ var cut = ctx.RenderComponent(parameters => parameters
+ .Add(n => n.BlazorDiagram, diagram));
+ Assert.Throws(() => cut.Find("div"));
+ diagram.TriggerPointerDown(null,
+ new PointerEventArgs(100, 150, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(200, 250, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+
+ // Assert
+ var widget = cut.Find("div");
+ Assert.Equal("100px", widget.GetStyle().GetWidth());
+ Assert.Equal("100px", widget.GetStyle().GetHeight());
+ Assert.Equal("150px", widget.GetStyle().GetTop());
+ Assert.Equal("100px", widget.GetStyle().GetLeft());
+ }
+ }
+}