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()); + } + } +}