diff --git a/src/Cropper.Blazor/Client/Shared/MainLayout.razor b/src/Cropper.Blazor/Client/Shared/MainLayout.razor index 7d03b584..15be5a99 100644 --- a/src/Cropper.Blazor/Client/Shared/MainLayout.razor +++ b/src/Cropper.Blazor/Client/Shared/MainLayout.razor @@ -4,7 +4,6 @@ - diff --git a/src/Cropper.Blazor/Client/Shared/MainLayout.razor.cs b/src/Cropper.Blazor/Client/Shared/MainLayout.razor.cs index 34fab34e..0243cba5 100644 --- a/src/Cropper.Blazor/Client/Shared/MainLayout.razor.cs +++ b/src/Cropper.Blazor/Client/Shared/MainLayout.razor.cs @@ -33,7 +33,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } InvokeAsync(StateHasChanged); - }, new MudBlazor.Services.ResizeOptions + }, new ResizeOptions { ReportRate = 50, NotifyOnBreakpointOnly = false, diff --git a/src/Cropper.Blazor/Client/Shared/UpdateAvailableDetector.razor b/src/Cropper.Blazor/Client/Shared/UpdateAvailableDetector.razor deleted file mode 100644 index 8b137891..00000000 --- a/src/Cropper.Blazor/Client/Shared/UpdateAvailableDetector.razor +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/Cropper.Blazor/Client/Shared/UpdateAvailableDetector.razor.cs b/src/Cropper.Blazor/Client/Shared/UpdateAvailableDetector.razor.cs deleted file mode 100644 index 3d7dd439..00000000 --- a/src/Cropper.Blazor/Client/Shared/UpdateAvailableDetector.razor.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.JSInterop; -using MudBlazor; - -namespace Cropper.Blazor.Client.Shared -{ - public partial class UpdateAvailableDetector - { - [Inject] private IJSRuntime? JSRuntime { get; set; } - [Inject] private ISnackbar? Snackbar { get; set; } - [Inject] private NavigationManager? Navigation { get; set; } - - protected override async Task OnInitializedAsync() - { - await RegisterForUpdateAvailableNotification(); - } - - private async Task RegisterForUpdateAvailableNotification() - { - await JSRuntime!.InvokeAsync( - identifier: "registerForUpdateAvailableNotification", - DotNetObjectReference.Create(this), - nameof(OnUpdateAvailable)); - } - - [JSInvokable(nameof(OnUpdateAvailable))] - public void OnUpdateAvailable() - { - Snackbar!.Add("A new version of the application is available.", Severity.Warning, config => - { - config.Action = "Reload"; - config.IconColor = Color.Error; - config.ActionColor = Color.Error; - config.Icon = Icons.Material.Filled.BrowserUpdated; - config.RequireInteraction = true; - config.Onclick = snackbar => - { - Navigation!.NavigateTo(Navigation.Uri, true); - - return Task.CompletedTask; - }; - }); - } - } -} diff --git a/src/Cropper.Blazor/Client/wwwroot/service-worker.published.js b/src/Cropper.Blazor/Client/wwwroot/service-worker.published.js index 861b54f8..241dae9e 100644 --- a/src/Cropper.Blazor/Client/wwwroot/service-worker.published.js +++ b/src/Cropper.Blazor/Client/wwwroot/service-worker.published.js @@ -2,8 +2,30 @@ // offline support. See https://aka.ms/blazor-offline-considerations self.importScripts('./service-worker-assets.js'); -self.addEventListener('install', event => event.waitUntil(onInstall(event))); -self.addEventListener('activate', event => event.waitUntil(onActivate(event))); +self.addEventListener('install', event => { + event.waitUntil( + Promise.all([ + onInstall(), + self.skipWaiting(), + ]) + ); +}); +self.addEventListener('activate', event => { + event.waitUntil( + Promise.all( + [ + onActivate(), + self.clients.claim(), + self.skipWaiting(), + ] + ) + .catch( + (err) => { + event.skipWaiting(); + } + ) + ); +}); self.addEventListener('fetch', event => event.respondWith(onFetch(event))); const cacheNamePrefix = 'offline-cache-'; @@ -18,8 +40,13 @@ async function onInstall(event) { const assetsRequests = self.assetsManifest.assets .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url))) .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url))) - .map(asset => new Request(asset.url, {integrity: asset.hash, cache: 'no-cache'})); - await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)); + .map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' })); + + await caches.open(cacheName) + .then(cache => cache.addAll(assetsRequests)) + .then(() => { + return self.skipWaiting(); + }); } async function onActivate(event) { diff --git a/src/Cropper.Blazor/Client/wwwroot/sw-registrator.js b/src/Cropper.Blazor/Client/wwwroot/sw-registrator.js index 782e60b2..c4ec0c18 100644 --- a/src/Cropper.Blazor/Client/wwwroot/sw-registrator.js +++ b/src/Cropper.Blazor/Client/wwwroot/sw-registrator.js @@ -28,11 +28,3 @@ window.updateAvailable = new Promise((resolve, reject) => { reject(error); }); }); - -window.registerForUpdateAvailableNotification = (caller, methodName) => { - window.updateAvailable.then(isUpdateAvailable => { - if (isUpdateAvailable) { - caller.invokeMethodAsync(methodName).then(); - } - }); -}; \ No newline at end of file diff --git a/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Components/CropperComponent_Should.cs b/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Components/CropperComponent_Should.cs index 4ee8b62a..e5cc633d 100644 --- a/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Components/CropperComponent_Should.cs +++ b/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Components/CropperComponent_Should.cs @@ -56,7 +56,6 @@ public async Task Throw_Exception_Because_Of_Invalid_NumberAsync(float numberIma // arrange Faker faker = new(); CancellationToken cancellationToken = new(); - GetCroppedCanvasOptions getCroppedCanvasOptions = new Faker() .Generate(); string imageFormatType = faker.Random.Word(); @@ -80,6 +79,7 @@ await func .WithMessage($"The given number should be between 0 and 1 for indication the image quality, but found {numberImageQuality}. (Parameter 'number')"); _mockCropperJsInterop.Verify(c => c.GetCroppedCanvasDataURLAsync( + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -87,6 +87,46 @@ await func }); } + [Fact] + private void Should_Dispose_CropperComponent_After_Render() + { + // arrange + CancellationToken cancellationToken = new(); + + // act + IRenderedComponent cropperComponent = _testContext + .RenderComponent(); + + // assert + Guid cropperComponentId = (Guid)cropperComponent.Instance + .GetInstanceField("CropperComponentId"); + + cropperComponent.Instance.Dispose(); + + _mockCropperJsInterop.Verify(c => c.DisposeAsync(), Times.Once()); + _mockCropperJsInterop.Verify(c => c.DestroyAsync(cropperComponentId, cancellationToken), Times.Once()); + } + + [Fact] + private async Task Should_DisposeAsync_CropperComponent_After_Render_Async() + { + // arrange + CancellationToken cancellationToken = new(); + + // act + IRenderedComponent cropperComponent = _testContext + .RenderComponent(); + + // assert + Guid cropperComponentId = (Guid)cropperComponent.Instance + .GetInstanceField("CropperComponentId"); + + await cropperComponent.Instance.DisposeAsync(); + + _mockCropperJsInterop.Verify(c => c.DisposeAsync(), Times.Once()); + _mockCropperJsInterop.Verify(c => c.DestroyAsync(cropperComponentId, cancellationToken), Times.Once()); + } + [Fact] public async Task Should_Render_CropperComponent_SuccessfulAsync() { @@ -264,23 +304,24 @@ public async Task Should_Render_CropperComponent_SuccessfulAsync() onCropReadyEventHandler); _mockCropperJsInterop - .Setup(c => c.GetCanvasDataAsync(cancellationToken)) + .Setup(c => c.GetCanvasDataAsync(It.IsAny(), cancellationToken)) .ReturnsAsync(expectedCanvasData); _mockCropperJsInterop - .Setup(c => c.GetContainerDataAsync(cancellationToken)) + .Setup(c => c.GetContainerDataAsync(It.IsAny(), cancellationToken)) .ReturnsAsync(expectedContainerData); _mockCropperJsInterop - .Setup(c => c.GetCropBoxDataAsync(cancellationToken)) + .Setup(c => c.GetCropBoxDataAsync(It.IsAny(), cancellationToken)) .ReturnsAsync(expectedCropBoxData); _mockCropperJsInterop - .Setup(c => c.GetCroppedCanvasAsync(getCroppedCanvasOptions, cancellationToken)) + .Setup(c => c.GetCroppedCanvasAsync(It.IsAny(), getCroppedCanvasOptions, cancellationToken)) .ReturnsAsync(expectedCroppedCanvas); _mockCropperJsInterop .Setup(c => c.GetCroppedCanvasDataURLAsync( + It.IsAny(), getCroppedCanvasOptions, imageFormatType, numberImageQuality, @@ -288,11 +329,11 @@ public async Task Should_Render_CropperComponent_SuccessfulAsync() .ReturnsAsync(expectedCroppedCanvasDataURL); _mockCropperJsInterop - .Setup(c => c.GetDataAsync(isRounded, cancellationToken)) + .Setup(c => c.GetDataAsync(It.IsAny(), isRounded, cancellationToken)) .ReturnsAsync(expectedCropperData); _mockCropperJsInterop - .Setup(c => c.GetImageDataAsync(cancellationToken)) + .Setup(c => c.GetImageDataAsync(It.IsAny(), cancellationToken)) .ReturnsAsync(expectedImageData); _mockCropperJsInterop @@ -323,6 +364,8 @@ public async Task Should_Render_CropperComponent_SuccessfulAsync() IElement expectedElement = cropperComponent.Find($"img.{imageClass}"); ElementReference elementReference = (ElementReference)cropperComponent.Instance .GetInstanceField("ImageReference"); + Guid cropperComponentId = (Guid)cropperComponent.Instance + .GetInstanceField("CropperComponentId"); _mockCropperJsInterop.Verify(c => c.LoadModuleAsync(cancellationToken), Times.Once()); elementReference.Id.Should().NotBeNullOrEmpty(); @@ -335,6 +378,7 @@ public async Task Should_Render_CropperComponent_SuccessfulAsync() expectedElement.TriggerEvent("onload", progressEventArgs); countCallsOnLoadImageHandler.Should().Be(1); _mockCropperJsInterop.Verify(c => c.InitCropperAsync( + cropperComponentId, elementReference, options, It.IsAny>(), @@ -347,10 +391,10 @@ public async Task Should_Render_CropperComponent_SuccessfulAsync() await cropperComponent.InvokeAsync(async () => { cropperComponent.Instance.Clear(); - _mockCropperJsInterop.Verify(c => c.ClearAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.ClearAsync(cropperComponentId, cancellationToken), Times.Once()); cropperComponent.Instance.Crop(); - _mockCropperJsInterop.Verify(c => c.CropAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.CropAsync(cropperComponentId, cancellationToken), Times.Once()); cropperComponent.Instance.CropperIsCroped(cropEvent); countCallsOnCropEventHandler.Should().Be(1); @@ -368,44 +412,44 @@ await cropperComponent.InvokeAsync(async () => countCallsOnZoomEventHandler.Should().Be(1); cropperComponent.Instance.Destroy(); - _mockCropperJsInterop.Verify(c => c.DestroyAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.DestroyAsync(cropperComponentId, cancellationToken), Times.Once()); cropperComponent.Instance.Disable(); - _mockCropperJsInterop.Verify(c => c.DisableAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.DisableAsync(cropperComponentId, cancellationToken), Times.Once()); cropperComponent.Instance.Enable(); - _mockCropperJsInterop.Verify(c => c.EnableAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.EnableAsync(cropperComponentId, cancellationToken), Times.Once()); CanvasData canvasData = await cropperComponent.Instance.GetCanvasDataAsync(); expectedCanvasData.Should().BeEquivalentTo(canvasData); - _mockCropperJsInterop.Verify(c => c.GetCanvasDataAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.GetCanvasDataAsync(cropperComponentId, cancellationToken), Times.Once()); ContainerData containerData = await cropperComponent.Instance.GetContainerDataAsync(); expectedContainerData.Should().BeEquivalentTo(containerData); - _mockCropperJsInterop.Verify(c => c.GetContainerDataAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.GetContainerDataAsync(cropperComponentId, cancellationToken), Times.Once()); CropBoxData cropBoxData = await cropperComponent.Instance.GetCropBoxDataAsync(); expectedCropBoxData.Should().BeEquivalentTo(cropBoxData); - _mockCropperJsInterop.Verify(c => c.GetCropBoxDataAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.GetCropBoxDataAsync(cropperComponentId, cancellationToken), Times.Once()); object croppedCanvas = await cropperComponent.Instance.GetCroppedCanvasAsync(getCroppedCanvasOptions); expectedCroppedCanvas.Should().BeEquivalentTo(croppedCanvas); - _mockCropperJsInterop.Verify(c => c.GetCroppedCanvasAsync(getCroppedCanvasOptions, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.GetCroppedCanvasAsync(cropperComponentId, getCroppedCanvasOptions, cancellationToken), Times.Once()); string croppedCanvasDataURL = await cropperComponent.Instance.GetCroppedCanvasDataURLAsync(getCroppedCanvasOptions, imageFormatType, numberImageQuality); expectedCroppedCanvasDataURL.Should().BeEquivalentTo(croppedCanvasDataURL); - _mockCropperJsInterop.Verify(c => c.GetCroppedCanvasDataURLAsync(getCroppedCanvasOptions, imageFormatType, numberImageQuality, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.GetCroppedCanvasDataURLAsync(cropperComponentId, getCroppedCanvasOptions, imageFormatType, numberImageQuality, cancellationToken), Times.Once()); CropperData cropperData = await cropperComponent.Instance.GetDataAsync(isRounded); expectedCropperData.Should().BeEquivalentTo(cropperData); - _mockCropperJsInterop.Verify(c => c.GetDataAsync(isRounded, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.GetDataAsync(cropperComponentId, isRounded, cancellationToken), Times.Once()); ImageData imageData = await cropperComponent.Instance.GetImageDataAsync(); expectedImageData.Should().Be(imageData); - _mockCropperJsInterop.Verify(c => c.GetImageDataAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.GetImageDataAsync(cropperComponentId, cancellationToken), Times.Once()); await cropperComponent.Instance.ReplaceAsync(newUrlImage, hasSameSize); - _mockCropperJsInterop.Verify(c => c.ReplaceAsync(newUrlImage, hasSameSize, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.ReplaceAsync(cropperComponentId, newUrlImage, hasSameSize, cancellationToken), Times.Once()); string image = await cropperComponent.Instance.GetImageUsingStreamingAsync(imageFile, maxAllowedSize, cancellationToken); expectedImage.Should().Be(image); @@ -415,55 +459,60 @@ await cropperComponent.InvokeAsync(async () => countCallsOnCropReadyEventHandler.Should().Be(1); cropperComponent.Instance.Move(offsetX, offsetY); - _mockCropperJsInterop.Verify(c => c.MoveAsync(offsetX, offsetY, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.MoveAsync(cropperComponentId, offsetX, offsetY, cancellationToken), Times.Once()); cropperComponent.Instance.MoveTo(x, y); - _mockCropperJsInterop.Verify(c => c.MoveToAsync(x, y, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.MoveToAsync(cropperComponentId, x, y, cancellationToken), Times.Once()); cropperComponent.Instance.OnErrorLoadImage(errorEventArgs); countCallsOnErrorLoadImageHandler.Should().Be(2); cropperComponent.Instance.Reset(); - _mockCropperJsInterop.Verify(c => c.ResetAsync(cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.ResetAsync(cropperComponentId, cancellationToken), Times.Once()); await cropperComponent.Instance.RevokeObjectUrlAsync(url); _mockCropperJsInterop.Verify(c => c.RevokeObjectUrlAsync(url, cancellationToken), Times.Once()); cropperComponent.Instance.Rotate(degree); - _mockCropperJsInterop.Verify(c => c.RotateAsync(degree, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.RotateAsync(cropperComponentId, degree, cancellationToken), Times.Once()); cropperComponent.Instance.Scale(scaleX, scaleY); - _mockCropperJsInterop.Verify(c => c.ScaleAsync(scaleX, scaleY, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.ScaleAsync(cropperComponentId, scaleX, scaleY, cancellationToken), Times.Once()); cropperComponent.Instance.ScaleX(scaleX); - _mockCropperJsInterop.Verify(c => c.ScaleXAsync(scaleX, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.ScaleXAsync(cropperComponentId, scaleX, cancellationToken), Times.Once()); cropperComponent.Instance.ScaleY(scaleY); - _mockCropperJsInterop.Verify(c => c.ScaleYAsync(scaleY, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.ScaleYAsync(cropperComponentId, scaleY, cancellationToken), Times.Once()); cropperComponent.Instance.SetAspectRatio(aspectRatio); - _mockCropperJsInterop.Verify(c => c.SetAspectRatioAsync(aspectRatio, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.SetAspectRatioAsync(cropperComponentId, aspectRatio, cancellationToken), Times.Once()); cropperComponent.Instance.SetCanvasData(setCanvasDataOptions); - _mockCropperJsInterop.Verify(c => c.SetCanvasDataAsync(setCanvasDataOptions, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.SetCanvasDataAsync(cropperComponentId, setCanvasDataOptions, cancellationToken), Times.Once()); cropperComponent.Instance.SetCropBoxData(setCropBoxDataOptions); - _mockCropperJsInterop.Verify(c => c.SetCropBoxDataAsync(setCropBoxDataOptions, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.SetCropBoxDataAsync(cropperComponentId, setCropBoxDataOptions, cancellationToken), Times.Once()); cropperComponent.Instance.SetData(setDataOptions); - _mockCropperJsInterop.Verify(c => c.SetDataAsync(setDataOptions, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.SetDataAsync(cropperComponentId, setDataOptions, cancellationToken), Times.Once()); cropperComponent.Instance.SetDragMode(dragMode); - _mockCropperJsInterop.Verify(c => c.SetDragModeAsync(dragMode, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.SetDragModeAsync(cropperComponentId, dragMode, cancellationToken), Times.Once()); cropperComponent.Instance.Zoom(ratio); - _mockCropperJsInterop.Verify(c => c.ZoomAsync(ratio, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.ZoomAsync(cropperComponentId, ratio, cancellationToken), Times.Once()); cropperComponent.Instance.ZoomTo(ratio, pivotX, pivotY); - _mockCropperJsInterop.Verify(c => c.ZoomToAsync(ratio, pivotX, pivotY, cancellationToken), Times.Once()); + _mockCropperJsInterop.Verify(c => c.ZoomToAsync(cropperComponentId, ratio, pivotX, pivotY, cancellationToken), Times.Once()); await cropperComponent.Instance.DisposeAsync(); _mockCropperJsInterop.Verify(c => c.DisposeAsync(), Times.Once()); + _mockCropperJsInterop.Verify(c => c.DestroyAsync(cropperComponentId, cancellationToken), Times.Exactly(2)); + + cropperComponent.Instance.Dispose(); + _mockCropperJsInterop.Verify(c => c.DisposeAsync(), Times.Exactly(2)); + _mockCropperJsInterop.Verify(c => c.DestroyAsync(cropperComponentId, cancellationToken), Times.Exactly(3)); }); } @@ -515,6 +564,7 @@ public void Should_Render_CropperComponent_With_ErrorLoadImage_Parameter() expectedElement.GetAttribute("blazor:elementreference").Should().BeNullOrEmpty(); _mockCropperJsInterop.Verify(c => c.InitCropperAsync( + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), @@ -573,6 +623,7 @@ public void Should_Not_Render_CropperComponent_With_IsNotAvaibleInitCropper_Para expectedElement.GetAttribute("blazor:elementreference").Should().BeNullOrEmpty(); _mockCropperJsInterop.Verify(c => c.InitCropperAsync( + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), diff --git a/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Extensions/ServiceCollectionExtensions_Should.cs b/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Extensions/ServiceCollectionExtensions_Should.cs index 7536026c..7f923e18 100644 --- a/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Extensions/ServiceCollectionExtensions_Should.cs +++ b/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Extensions/ServiceCollectionExtensions_Should.cs @@ -23,7 +23,7 @@ public void Verify_Cropper_Service_Is_Registered(CropperJsInteropOptions? croppe // assert ServiceCollectionMock = new(ServiceCollection); ServiceCollectionMock.ContainsSingletonService(); - ServiceCollectionMock.TryContainsTransientService(); + ServiceCollectionMock.TryContainsScopedService(); } public static IEnumerable TestData_AddCropper_Service() diff --git a/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Services/CropperJsInterop_Should.cs b/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Services/CropperJsInterop_Should.cs index 55d90ea4..a9c6f235 100644 --- a/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Services/CropperJsInterop_Should.cs +++ b/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Services/CropperJsInterop_Should.cs @@ -46,6 +46,7 @@ public CropperJsInterop_Should() public async Task Verify_InitCropperAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); ElementReference elementReference = new(Guid.NewGuid().ToString()); Options options = new Faker().Generate(); ICropperComponentBase cropperComponentBase = new Mock().Object; @@ -54,6 +55,7 @@ public async Task Verify_InitCropperAsync() object[] expectedInitCropperMethodArguments = new object[] { + cropperComponentId, elementReference, options, refToCropperComponentBase @@ -67,99 +69,110 @@ public async Task Verify_InitCropperAsync() .SetVoidResult(); // act - await _cropperJsInterop.InitCropperAsync(elementReference, options, refToCropperComponentBase); + await _cropperJsInterop.InitCropperAsync(cropperComponentId, elementReference, options, refToCropperComponentBase); } [Fact] public async Task Verify_ClearAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); + _testContext.JSInterop - .SetupVoid("cropper.clear") + .SetupVoid("cropper.clear", cropperComponentId) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.ClearAsync(); + await _cropperJsInterop.ClearAsync(cropperComponentId); } [Fact] public async Task Verify_CropAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); + _testContext.JSInterop - .SetupVoid("cropper.crop") + .SetupVoid("cropper.crop", cropperComponentId) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.CropAsync(); + await _cropperJsInterop.CropAsync(cropperComponentId); } [Fact] public async Task Verify_DestroyAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); + _testContext.JSInterop - .SetupVoid("cropper.destroy") + .SetupVoid("cropper.destroy", cropperComponentId) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.DestroyAsync(); + await _cropperJsInterop.DestroyAsync(cropperComponentId); } [Fact] public async Task Verify_DisableAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); + _testContext.JSInterop - .SetupVoid("cropper.disable") + .SetupVoid("cropper.disable", cropperComponentId) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.DisableAsync(); + await _cropperJsInterop.DisableAsync(cropperComponentId); } [Fact] public async Task Verify_EnableAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); + _testContext.JSInterop - .SetupVoid("cropper.enable") + .SetupVoid("cropper.enable", cropperComponentId) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.EnableAsync(); + await _cropperJsInterop.EnableAsync(cropperComponentId); } [Fact] public async Task Verify_GetCanvasDataAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); CanvasData expectedCanvasData = new Faker(); _testContext.JSInterop - .Setup("cropper.getCanvasData") + .Setup("cropper.getCanvasData", cropperComponentId) .SetResult(expectedCanvasData); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - CanvasData canvasData = await _cropperJsInterop.GetCanvasDataAsync(); + CanvasData canvasData = await _cropperJsInterop.GetCanvasDataAsync(cropperComponentId); // assert expectedCanvasData.Should().BeEquivalentTo(canvasData); @@ -169,17 +182,18 @@ public async Task Verify_GetCanvasDataAsync() public async Task Verify_GetContainerDataAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); ContainerData expectedContainerData = new Faker(); _testContext.JSInterop - .Setup("cropper.getContainerData") + .Setup("cropper.getContainerData", cropperComponentId) .SetResult(expectedContainerData); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - ContainerData containerData = await _cropperJsInterop.GetContainerDataAsync(); + ContainerData containerData = await _cropperJsInterop.GetContainerDataAsync(cropperComponentId); // assert expectedContainerData.Should().BeEquivalentTo(containerData); @@ -189,17 +203,18 @@ public async Task Verify_GetContainerDataAsync() public async Task Verify_GetCropBoxDataAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); CropBoxData expectedCropBoxData = new Faker(); _testContext.JSInterop - .Setup("cropper.getCropBoxData") + .Setup("cropper.getCropBoxData", cropperComponentId) .SetResult(expectedCropBoxData); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - CropBoxData cropBoxData = await _cropperJsInterop.GetCropBoxDataAsync(); + CropBoxData cropBoxData = await _cropperJsInterop.GetCropBoxDataAsync(cropperComponentId); // assert expectedCropBoxData.Should().BeEquivalentTo(cropBoxData); @@ -209,6 +224,7 @@ public async Task Verify_GetCropBoxDataAsync() public async Task Verify_GetCroppedCanvasAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); Mock mockIJSObjectReference = new(); CroppedCanvas expectedCroppedCanvas = new Faker() @@ -216,14 +232,16 @@ public async Task Verify_GetCroppedCanvasAsync() GetCroppedCanvasOptions getCroppedCanvasOptions = new Faker(); _testContext.JSInterop - .SetupModule("cropper.getCroppedCanvas", invocation => invocation.Arguments?[0] is GetCroppedCanvasOptions argumentGetCroppedCanvasOptions - && argumentGetCroppedCanvasOptions.Equals(getCroppedCanvasOptions)); + .SetupModule("cropper.getCroppedCanvas", invocation => invocation.Arguments?[0] is Guid guid + && guid.Equals(cropperComponentId) + && invocation.Arguments?[1] is GetCroppedCanvasOptions argumentGetCroppedCanvasOptions + && argumentGetCroppedCanvasOptions.Equals(getCroppedCanvasOptions)); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - CroppedCanvas croppedCanvas = await _cropperJsInterop.GetCroppedCanvasAsync(getCroppedCanvasOptions); + CroppedCanvas croppedCanvas = await _cropperJsInterop.GetCroppedCanvasAsync(cropperComponentId, getCroppedCanvasOptions); // assert expectedCroppedCanvas.Should().BeEquivalentTo(croppedCanvas); @@ -233,20 +251,21 @@ public async Task Verify_GetCroppedCanvasAsync() public async Task Verify_GetCroppedCanvasDataURL_By_TypeAndNumber_Async() { // arrange + Guid cropperComponentId = Guid.NewGuid(); string expectedCroppedCanvasURL = _faker.Random.Word(); string type = _faker.Random.Word(); float number = _faker.Random.Float(); GetCroppedCanvasOptions getCroppedCanvasOptions = new Faker(); _testContext.JSInterop - .Setup("cropper.getCroppedCanvasDataURL", getCroppedCanvasOptions, type, number) + .Setup("cropper.getCroppedCanvasDataURL", cropperComponentId, getCroppedCanvasOptions, type, number) .SetResult(expectedCroppedCanvasURL); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - string croppedCanvasURL = await _cropperJsInterop.GetCroppedCanvasDataURLAsync(getCroppedCanvasOptions, type, number); + string croppedCanvasURL = await _cropperJsInterop.GetCroppedCanvasDataURLAsync(cropperComponentId, getCroppedCanvasOptions, type, number); // assert expectedCroppedCanvasURL.Should().BeEquivalentTo(croppedCanvasURL); @@ -256,18 +275,19 @@ public async Task Verify_GetCroppedCanvasDataURL_By_TypeAndNumber_Async() public async Task Verify_GetDataAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); CropperData expectedCropperData = new Faker(); bool rounded = _faker.Random.Bool(); _testContext.JSInterop - .Setup("cropper.getData", rounded) + .Setup("cropper.getData", cropperComponentId, rounded) .SetResult(expectedCropperData); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - CropperData cropperData = await _cropperJsInterop.GetDataAsync(rounded); + CropperData cropperData = await _cropperJsInterop.GetDataAsync(cropperComponentId, rounded); // assert expectedCropperData.Should().BeEquivalentTo(cropperData); @@ -277,17 +297,18 @@ public async Task Verify_GetDataAsync() public async Task Verify_GetImageDataAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); ImageData expectedImageData = new Faker(); _testContext.JSInterop - .Setup("cropper.getImageData") + .Setup("cropper.getImageData", cropperComponentId) .SetResult(expectedImageData); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - ImageData imageData = await _cropperJsInterop.GetImageDataAsync(); + ImageData imageData = await _cropperJsInterop.GetImageDataAsync(cropperComponentId); // assert expectedImageData.Should().BeEquivalentTo(imageData); @@ -340,36 +361,38 @@ bool VerifyStreamArgument(JSRuntimeInvocation jSRuntimeInvocation) public async Task Verify_MoveAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal offsetX = _faker.Random.Decimal(); decimal? offsetY = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.move", offsetX, offsetY) + .SetupVoid("cropper.move", cropperComponentId, offsetX, offsetY) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.MoveAsync(offsetX, offsetY); + await _cropperJsInterop.MoveAsync(cropperComponentId, offsetX, offsetY); } [Fact] public async Task Verify_MoveToAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal x = _faker.Random.Decimal(); decimal? y = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.moveTo", x, y) + .SetupVoid("cropper.moveTo", cropperComponentId, x, y) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.MoveToAsync(x, y); + await _cropperJsInterop.MoveToAsync(cropperComponentId, x, y); } [Fact] @@ -391,33 +414,35 @@ public async Task Verify_NoConflictAsync() public async Task Verify_ReplaceAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); string url = _faker.Random.String(); bool onlyColorChanged = _faker.Random.Bool(); _testContext.JSInterop - .SetupVoid("cropper.replace", url, onlyColorChanged) + .SetupVoid("cropper.replace", cropperComponentId, url, onlyColorChanged) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.ReplaceAsync(url, onlyColorChanged); + await _cropperJsInterop.ReplaceAsync(cropperComponentId, url, onlyColorChanged); } [Fact] public async Task Verify_ResetAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); _testContext.JSInterop - .SetupVoid("cropper.reset") + .SetupVoid("cropper.reset", cropperComponentId) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.ResetAsync(); + await _cropperJsInterop.ResetAsync(cropperComponentId); } [Fact] @@ -441,154 +466,163 @@ public async Task Verify_RevokeObjectUrlAsync() public async Task Verify_RotateAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal degree = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.rotate", degree) + .SetupVoid("cropper.rotate", cropperComponentId, degree) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.RotateAsync(degree); + await _cropperJsInterop.RotateAsync(cropperComponentId, degree); } [Fact] public async Task Verify_RotateToAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal degree = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.rotateTo", degree) + .SetupVoid("cropper.rotateTo", cropperComponentId, degree) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.RotateToAsync(degree); + await _cropperJsInterop.RotateToAsync(cropperComponentId, degree); } [Fact] public async Task Verify_ScaleAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal scaleX = _faker.Random.Decimal(); decimal scaleY = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.scale", scaleX, scaleY) + .SetupVoid("cropper.scale", cropperComponentId, scaleX, scaleY) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.ScaleAsync(scaleX, scaleY); + await _cropperJsInterop.ScaleAsync(cropperComponentId, scaleX, scaleY); } [Fact] public async Task Verify_ScaleXAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal scaleX = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.scaleX", scaleX) + .SetupVoid("cropper.scaleX", cropperComponentId, scaleX) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.ScaleXAsync(scaleX); + await _cropperJsInterop.ScaleXAsync(cropperComponentId, scaleX); } [Fact] public async Task Verify_ScaleYAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal scaleY = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.scaleY", scaleY) + .SetupVoid("cropper.scaleY", cropperComponentId, scaleY) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.ScaleYAsync(scaleY); + await _cropperJsInterop.ScaleYAsync(cropperComponentId, scaleY); } [Fact] public async Task Verify_SetAspectRatioAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal aspectRatio = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.setAspectRatio", aspectRatio) + .SetupVoid("cropper.setAspectRatio", cropperComponentId, aspectRatio) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.SetAspectRatioAsync(aspectRatio); + await _cropperJsInterop.SetAspectRatioAsync(cropperComponentId, aspectRatio); } [Fact] public async Task Verify_SetCanvasDataAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); SetCanvasDataOptions setCanvasDataOptions = new Faker(); _testContext.JSInterop - .SetupVoid("cropper.setCanvasData", setCanvasDataOptions) + .SetupVoid("cropper.setCanvasData", cropperComponentId, setCanvasDataOptions) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.SetCanvasDataAsync(setCanvasDataOptions); + await _cropperJsInterop.SetCanvasDataAsync(cropperComponentId, setCanvasDataOptions); } [Fact] public async Task Verify_SetCropBoxDataAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); SetCropBoxDataOptions setCropBoxDataOptions = new Faker(); _testContext.JSInterop - .SetupVoid("cropper.setCropBoxData", setCropBoxDataOptions) + .SetupVoid("cropper.setCropBoxData", cropperComponentId, setCropBoxDataOptions) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.SetCropBoxDataAsync(setCropBoxDataOptions); + await _cropperJsInterop.SetCropBoxDataAsync(cropperComponentId, setCropBoxDataOptions); } [Fact] public async Task Verify_SetDataAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); SetDataOptions setDataOptions = new Faker(); _testContext.JSInterop - .SetupVoid("cropper.setData", setDataOptions) + .SetupVoid("cropper.setData", cropperComponentId, setDataOptions) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.SetDataAsync(setDataOptions); + await _cropperJsInterop.SetDataAsync(cropperComponentId, setDataOptions); } [Fact] @@ -612,53 +646,56 @@ public async Task Verify_SetDefaultsAsync() public async Task Verify_SetDragModeAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); DragMode dragMode = _faker.Random.Enum(); _testContext.JSInterop - .SetupVoid("cropper.setDragMode", dragMode.ToEnumString()) + .SetupVoid("cropper.setDragMode", cropperComponentId, dragMode.ToEnumString()) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.SetDragModeAsync(dragMode); + await _cropperJsInterop.SetDragModeAsync(cropperComponentId, dragMode); } [Fact] public async Task Verify_ZoomAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal ratio = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.zoom", ratio) + .SetupVoid("cropper.zoom", cropperComponentId, ratio) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.ZoomAsync(ratio); + await _cropperJsInterop.ZoomAsync(cropperComponentId, ratio); } [Fact] public async Task Verify_ZoomToAsync() { // arrange + Guid cropperComponentId = Guid.NewGuid(); decimal ratio = _faker.Random.Decimal(); decimal pivotX = _faker.Random.Decimal(); decimal pivotY = _faker.Random.Decimal(); _testContext.JSInterop - .SetupVoid("cropper.zoomTo", ratio, pivotX, pivotY) + .SetupVoid("cropper.zoomTo", cropperComponentId, ratio, pivotX, pivotY) .SetVoidResult(); // assert VerifyLoadCropperModule(DefaultPathToCropperModule); // act - await _cropperJsInterop.ZoomToAsync(ratio, pivotX, pivotY); + await _cropperJsInterop.ZoomToAsync(cropperComponentId, ratio, pivotX, pivotY); } [Fact] diff --git a/src/Cropper.Blazor/Cropper.Blazor/Components/CropperComponent.razor.cs b/src/Cropper.Blazor/Cropper.Blazor/Components/CropperComponent.razor.cs index ea97b3a4..a6f59aa1 100644 --- a/src/Cropper.Blazor/Cropper.Blazor/Components/CropperComponent.razor.cs +++ b/src/Cropper.Blazor/Cropper.Blazor/Components/CropperComponent.razor.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Threading; using System.Threading.Tasks; using Cropper.Blazor.Base; @@ -24,12 +23,20 @@ namespace Cropper.Blazor.Components /// /// The cropper component. /// - public partial class CropperComponent : ICropperComponentBase, IAsyncDisposable + public partial class CropperComponent : ICropperComponentBase, IAsyncDisposable, IDisposable { [Inject] ICropperJsInterop CropperJsIntertop { get; set; } = null!; + /// + /// Gets a reference to the img HTML element rendered by the component. + /// private ElementReference ImageReference; + /// + /// The unique identifier of the cropper component. + /// + private Guid CropperComponentId; + /// /// The options for cropping. Check out the available . /// @@ -153,6 +160,14 @@ protected override async Task OnAfterRenderAsync(bool firstRender) await base.OnAfterRenderAsync(firstRender); } + /// + /// Called when initialized. + /// + protected override void OnInitialized() + { + CropperComponentId = Guid.NewGuid(); + } + /// /// This event is fired when the image is loaded. /// @@ -184,7 +199,7 @@ public void OnErrorLoadImage(ErrorEventArgs errorEventArgs) public void InitCropper(CancellationToken cancellationToken = default) { ICropperComponentBase cropperComponentBase = this; - CropperJsIntertop!.InitCropperAsync(ImageReference, Options!, DotNetObjectReference.Create(cropperComponentBase), cancellationToken); + CropperJsIntertop!.InitCropperAsync(CropperComponentId, ImageReference, Options!, DotNetObjectReference.Create(cropperComponentBase), cancellationToken); OnLoadImageEvent?.Invoke(); } @@ -255,7 +270,7 @@ public void IsReady(JSEventData jSEventData) /// The used to propagate notifications that the operation should be canceled. public void SetDragMode(DragMode dragMode, CancellationToken cancellationToken = default) { - CropperJsIntertop?.SetDragModeAsync(dragMode, cancellationToken); + CropperJsIntertop?.SetDragModeAsync(CropperComponentId, dragMode, cancellationToken); } /// @@ -269,7 +284,7 @@ public void SetDragMode(DragMode dragMode, CancellationToken cancellationToken = /// The used to propagate notifications that the operation should be canceled. public void Zoom(decimal ratio, CancellationToken cancellationToken = default) { - CropperJsIntertop?.ZoomAsync(ratio, cancellationToken); + CropperJsIntertop?.ZoomAsync(CropperComponentId, ratio, cancellationToken); } /// @@ -281,7 +296,7 @@ public void Zoom(decimal ratio, CancellationToken cancellationToken = default) /// The used to propagate notifications that the operation should be canceled. public void ZoomTo(decimal ratio, decimal pivotX, decimal pivotY, CancellationToken cancellationToken = default) { - CropperJsIntertop?.ZoomToAsync(ratio, pivotX, pivotY, cancellationToken); + CropperJsIntertop?.ZoomToAsync(CropperComponentId, ratio, pivotX, pivotY, cancellationToken); } /// @@ -292,7 +307,7 @@ public void ZoomTo(decimal ratio, decimal pivotX, decimal pivotY, CancellationTo /// The used to propagate notifications that the operation should be canceled. public void Move(decimal offsetX, decimal? offsetY, CancellationToken cancellationToken = default) { - CropperJsIntertop?.MoveAsync(offsetX, offsetY, cancellationToken); + CropperJsIntertop?.MoveAsync(CropperComponentId, offsetX, offsetY, cancellationToken); } /// @@ -303,7 +318,7 @@ public void Move(decimal offsetX, decimal? offsetY, CancellationToken cancellati /// The used to propagate notifications that the operation should be canceled. public void MoveTo(decimal x, decimal? y, CancellationToken cancellationToken = default) { - CropperJsIntertop?.MoveToAsync(x, y, cancellationToken); + CropperJsIntertop?.MoveToAsync(CropperComponentId, x, y, cancellationToken); } /// @@ -317,7 +332,7 @@ public void MoveTo(decimal x, decimal? y, CancellationToken cancellationToken = /// The used to propagate notifications that the operation should be canceled. public void Rotate(decimal degree, CancellationToken cancellationToken = default) { - CropperJsIntertop?.RotateAsync(degree, cancellationToken); + CropperJsIntertop?.RotateAsync(CropperComponentId, degree, cancellationToken); } /// @@ -331,7 +346,7 @@ public void Rotate(decimal degree, CancellationToken cancellationToken = default /// The used to propagate notifications that the operation should be canceled. public void ScaleX(decimal scaleX, CancellationToken cancellationToken = default) { - CropperJsIntertop?.ScaleXAsync(scaleX, cancellationToken); + CropperJsIntertop?.ScaleXAsync(CropperComponentId, scaleX, cancellationToken); } /// @@ -345,7 +360,7 @@ public void ScaleX(decimal scaleX, CancellationToken cancellationToken = default /// The used to propagate notifications that the operation should be canceled. public void ScaleY(decimal scaleY, CancellationToken cancellationToken = default) { - CropperJsIntertop?.ScaleYAsync(scaleY, cancellationToken); + CropperJsIntertop?.ScaleYAsync(CropperComponentId, scaleY, cancellationToken); } /// @@ -364,7 +379,7 @@ public void ScaleY(decimal scaleY, CancellationToken cancellationToken = default /// The used to propagate notifications that the operation should be canceled. public void Scale(decimal scaleX, decimal scaleY, CancellationToken cancellationToken = default) { - CropperJsIntertop?.ScaleAsync(scaleX, scaleY, cancellationToken); + CropperJsIntertop?.ScaleAsync(CropperComponentId, scaleX, scaleY, cancellationToken); } /// @@ -373,7 +388,7 @@ public void Scale(decimal scaleX, decimal scaleY, CancellationToken cancellation /// The used to propagate notifications that the operation should be canceled. public void Crop(CancellationToken cancellationToken = default) { - CropperJsIntertop?.CropAsync(cancellationToken); + CropperJsIntertop?.CropAsync(CropperComponentId, cancellationToken); } /// @@ -382,7 +397,7 @@ public void Crop(CancellationToken cancellationToken = default) /// The used to propagate notifications that the operation should be canceled. public void Clear(CancellationToken cancellationToken = default) { - CropperJsIntertop?.ClearAsync(cancellationToken); + CropperJsIntertop?.ClearAsync(CropperComponentId, cancellationToken); } /// @@ -391,7 +406,7 @@ public void Clear(CancellationToken cancellationToken = default) /// The used to propagate notifications that the operation should be canceled. public void Enable(CancellationToken cancellationToken = default) { - CropperJsIntertop?.EnableAsync(cancellationToken); + CropperJsIntertop?.EnableAsync(CropperComponentId, cancellationToken); } /// @@ -400,7 +415,7 @@ public void Enable(CancellationToken cancellationToken = default) /// The used to propagate notifications that the operation should be canceled. public void Disable(CancellationToken cancellationToken = default) { - CropperJsIntertop?.DisableAsync(cancellationToken); + CropperJsIntertop?.DisableAsync(CropperComponentId, cancellationToken); } /// @@ -409,7 +424,7 @@ public void Disable(CancellationToken cancellationToken = default) /// The used to propagate notifications that the operation should be canceled. public void Reset(CancellationToken cancellationToken = default) { - CropperJsIntertop?.ResetAsync(cancellationToken); + CropperJsIntertop?.ResetAsync(CropperComponentId, cancellationToken); } /// @@ -418,7 +433,7 @@ public void Reset(CancellationToken cancellationToken = default) /// The used to propagate notifications that the operation should be canceled. public void Destroy(CancellationToken cancellationToken = default) { - CropperJsIntertop?.DestroyAsync(cancellationToken); + CropperJsIntertop?.DestroyAsync(CropperComponentId, cancellationToken); } /// @@ -428,7 +443,7 @@ public void Destroy(CancellationToken cancellationToken = default) /// The used to propagate notifications that the operation should be canceled. public void SetAspectRatio(decimal aspectRatio, CancellationToken cancellationToken = default) { - CropperJsIntertop?.SetAspectRatioAsync(aspectRatio, cancellationToken); + CropperJsIntertop?.SetAspectRatioAsync(CropperComponentId, aspectRatio, cancellationToken); } /// @@ -438,7 +453,7 @@ public void SetAspectRatio(decimal aspectRatio, CancellationToken cancellationTo /// The used to propagate notifications that the operation should be canceled. public void SetCropBoxData(SetCropBoxDataOptions cropBoxDataOptions, CancellationToken cancellationToken = default) { - CropperJsIntertop?.SetCropBoxDataAsync(cropBoxDataOptions, cancellationToken); + CropperJsIntertop?.SetCropBoxDataAsync(CropperComponentId, cropBoxDataOptions, cancellationToken); } /// @@ -448,7 +463,7 @@ public void SetCropBoxData(SetCropBoxDataOptions cropBoxDataOptions, Cancellatio /// The used to propagate notifications that the operation should be canceled. public void SetData(SetDataOptions setDataOptions, CancellationToken cancellationToken = default) { - CropperJsIntertop?.SetDataAsync(setDataOptions, cancellationToken); + CropperJsIntertop?.SetDataAsync(CropperComponentId, setDataOptions, cancellationToken); } /// @@ -458,7 +473,7 @@ public void SetData(SetDataOptions setDataOptions, CancellationToken cancellatio /// The used to propagate notifications that the operation should be canceled. public void SetCanvasData(SetCanvasDataOptions setCanvasDataOptions, CancellationToken cancellationToken = default) { - CropperJsIntertop?.SetCanvasDataAsync(setCanvasDataOptions, cancellationToken); + CropperJsIntertop?.SetCanvasDataAsync(CropperComponentId, setCanvasDataOptions, cancellationToken); } /// @@ -468,7 +483,7 @@ public void SetCanvasData(SetCanvasDataOptions setCanvasDataOptions, Cancellatio /// A representing cropper box options asynchronous operation. public async ValueTask GetCropBoxDataAsync(CancellationToken cancellationToken = default) { - return await CropperJsIntertop!.GetCropBoxDataAsync(cancellationToken); + return await CropperJsIntertop!.GetCropBoxDataAsync(CropperComponentId, cancellationToken); } /// @@ -479,7 +494,7 @@ public async ValueTask GetCropBoxDataAsync(CancellationToken cancel /// A representing cropper options asynchronous operation. public async ValueTask GetDataAsync(bool rounded, CancellationToken cancellationToken = default) { - return await CropperJsIntertop!.GetDataAsync(rounded, cancellationToken); + return await CropperJsIntertop!.GetDataAsync(CropperComponentId, rounded, cancellationToken); } /// @@ -489,7 +504,7 @@ public async ValueTask GetDataAsync(bool rounded, CancellationToken /// A representing container options asynchronous operation. public async ValueTask GetContainerDataAsync(CancellationToken cancellationToken = default) { - return await CropperJsIntertop!.GetContainerDataAsync(cancellationToken); + return await CropperJsIntertop!.GetContainerDataAsync(CropperComponentId, cancellationToken); } /// @@ -504,7 +519,7 @@ public async ValueTask ReplaceAsync( bool hasSameSize = true, CancellationToken cancellationToken = default) { - await CropperJsIntertop!.ReplaceAsync(url, hasSameSize, cancellationToken); + await CropperJsIntertop!.ReplaceAsync(CropperComponentId, url, hasSameSize, cancellationToken); } /// @@ -514,7 +529,7 @@ public async ValueTask ReplaceAsync( /// A representing image options asynchronous operation. public async ValueTask GetImageDataAsync(CancellationToken cancellationToken = default) { - return await CropperJsIntertop!.GetImageDataAsync(cancellationToken); + return await CropperJsIntertop!.GetImageDataAsync(CropperComponentId, cancellationToken); } /// @@ -524,7 +539,7 @@ public async ValueTask GetImageDataAsync(CancellationToken cancellati /// A representing canvas options asynchronous operation. public async ValueTask GetCanvasDataAsync(CancellationToken cancellationToken = default) { - return await CropperJsIntertop!.GetCanvasDataAsync(cancellationToken); + return await CropperJsIntertop!.GetCanvasDataAsync(CropperComponentId, cancellationToken); } /// @@ -563,7 +578,7 @@ public async ValueTask RevokeObjectUrlAsync(string url, CancellationToken cancel /// A representing canvas drawn the cropped image asynchronous operation. public async ValueTask GetCroppedCanvasAsync(GetCroppedCanvasOptions getCroppedCanvasOptions, CancellationToken cancellationToken = default) { - return await CropperJsIntertop!.GetCroppedCanvasAsync(getCroppedCanvasOptions, cancellationToken); + return await CropperJsIntertop!.GetCroppedCanvasAsync(CropperComponentId, getCroppedCanvasOptions, cancellationToken); } /// @@ -582,15 +597,15 @@ public async ValueTask GetCroppedCanvasDataURLAsync( float number = 1, CancellationToken cancellationToken = default) { - return number switch { < 0 or > 1 => throw new ArgumentException($"The given number should be between 0 and 1 for indication the image quality, but found {number}.", nameof(number)), _ => await CropperJsIntertop!.GetCroppedCanvasDataURLAsync( - getCroppedCanvasOptions, - type, - number, - cancellationToken) + CropperComponentId, + getCroppedCanvasOptions, + type, + number, + cancellationToken) }; } @@ -600,7 +615,16 @@ public async ValueTask GetCroppedCanvasDataURLAsync( /// A representing any asynchronous operation. public async ValueTask DisposeAsync() { + Destroy(); await CropperJsIntertop!.DisposeAsync(); } + + /// + /// Called to dispose this instance and internal services. + /// + public void Dispose() + { + DisposeAsync(); + } } } diff --git a/src/Cropper.Blazor/Cropper.Blazor/Cropper.Blazor.csproj b/src/Cropper.Blazor/Cropper.Blazor/Cropper.Blazor.csproj index ecb17ad9..617ed77f 100644 --- a/src/Cropper.Blazor/Cropper.Blazor/Cropper.Blazor.csproj +++ b/src/Cropper.Blazor/Cropper.Blazor/Cropper.Blazor.csproj @@ -14,7 +14,7 @@ - 1.2.3 + 1.2.4 LICENSE NuGet.png Cropper.Blazor diff --git a/src/Cropper.Blazor/Cropper.Blazor/Extensions/ServiceCollectionExtensions.cs b/src/Cropper.Blazor/Cropper.Blazor/Extensions/ServiceCollectionExtensions.cs index e8db01df..313162a0 100644 --- a/src/Cropper.Blazor/Cropper.Blazor/Extensions/ServiceCollectionExtensions.cs +++ b/src/Cropper.Blazor/Cropper.Blazor/Extensions/ServiceCollectionExtensions.cs @@ -11,7 +11,7 @@ namespace Cropper.Blazor.Extensions public static class ServiceCollectionExtensions { /// - /// Adds a see as a Transient instance. + /// Adds a see as a Scoped instance. /// /// Continues the chain. /// Continues the chain. @@ -21,7 +21,7 @@ public static IServiceCollection AddCropper(this IServiceCollection services, Cr { services.AddSingleton(services => cropperJsInteropOptions ?? new CropperJsInteropOptions()); - services.TryAddTransient(); + services.TryAddScoped(); return services; } diff --git a/src/Cropper.Blazor/Cropper.Blazor/Services/CropperJsInterop.cs b/src/Cropper.Blazor/Cropper.Blazor/Services/CropperJsInterop.cs index fdb30e4b..6a179b31 100644 --- a/src/Cropper.Blazor/Cropper.Blazor/Services/CropperJsInterop.cs +++ b/src/Cropper.Blazor/Cropper.Blazor/Services/CropperJsInterop.cs @@ -54,6 +54,10 @@ public async Task LoadModuleAsync(CancellationToken cancellationToken = default) "import", cancellationToken, globalPathToCropperModule); } + /// + /// Finds path to the cropper module. + /// + /// The path to the cropper module. private string GetGlobalPathToCropperModule() { if (_cropperJsInteropOptions.IsActiveGlobalPath) @@ -72,12 +76,14 @@ private string GetGlobalPathToCropperModule() /// /// Initializes cropper. /// + /// The identifier of the cropper component. /// Reference to img html-DOM /// Cropper options /// Reference to base cropper component. Default equal to 'this' object. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask InitCropperAsync( + [NotNull] Guid cropperComponentId, [NotNull] ElementReference image, [NotNull] Options options, [NotNull] DotNetObjectReference cropperComponentBase, @@ -91,6 +97,7 @@ public async ValueTask InitCropperAsync( await _jsRuntime!.InvokeVoidAsync( "cropper.initCropper", cancellationToken, + cropperComponentId, image, options, cropperComponentBase); @@ -99,130 +106,180 @@ public async ValueTask InitCropperAsync( /// /// Clear the crop box. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - public async ValueTask ClearAsync(CancellationToken cancellationToken = default) + public async ValueTask ClearAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.clear", cancellationToken); + await _jsRuntime!.InvokeVoidAsync( + "cropper.clear", + cancellationToken, + cropperComponentId); } /// /// Show the crop box manually. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - public async ValueTask CropAsync(CancellationToken cancellationToken = default) + public async ValueTask CropAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.crop", cancellationToken); + await _jsRuntime!.InvokeVoidAsync( + "cropper.crop", + cancellationToken, + cropperComponentId); } /// /// Destroy the cropper and remove the instance from the image. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - public async ValueTask DestroyAsync(CancellationToken cancellationToken = default) + public async ValueTask DestroyAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.destroy", cancellationToken); + await _jsRuntime!.InvokeVoidAsync( + "cropper.destroy", + cancellationToken, + cropperComponentId); } /// /// Disable (freeze) the cropper. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - public async ValueTask DisableAsync(CancellationToken cancellationToken = default) + public async ValueTask DisableAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.disable", cancellationToken); + await _jsRuntime!.InvokeVoidAsync( + "cropper.disable", + cancellationToken, + cropperComponentId); } /// /// Enable (unfreeze) the cropper. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - public async ValueTask EnableAsync(CancellationToken cancellationToken = default) + public async ValueTask EnableAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.enable", cancellationToken); + await _jsRuntime!.InvokeVoidAsync( + "cropper.enable", + cancellationToken, + cropperComponentId); } /// /// Get the canvas position and size data. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing result canvas data asynchronous operation. - public async ValueTask GetCanvasDataAsync(CancellationToken cancellationToken = default) + public async ValueTask GetCanvasDataAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - return await _jsRuntime!.InvokeAsync("cropper.getCanvasData", cancellationToken); + return await _jsRuntime!.InvokeAsync( + "cropper.getCanvasData", + cancellationToken, + cropperComponentId); } /// /// Get the container size data. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing result container data asynchronous operation. - public async ValueTask GetContainerDataAsync(CancellationToken cancellationToken = default) + public async ValueTask GetContainerDataAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - return await _jsRuntime!.InvokeAsync("cropper.getContainerData", cancellationToken); + return await _jsRuntime!.InvokeAsync( + "cropper.getContainerData", + cancellationToken, + cropperComponentId); } /// /// Get the crop box position and size data. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing result crop box data asynchronous operation. - public async ValueTask GetCropBoxDataAsync(CancellationToken cancellationToken = default) + public async ValueTask GetCropBoxDataAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - return await _jsRuntime!.InvokeAsync("cropper.getCropBoxData", cancellationToken); + return await _jsRuntime!.InvokeAsync( + "cropper.getCropBoxData", + cancellationToken, + cropperComponentId); } /// /// Get a canvas drawn the cropped image. /// + /// The identifier of the cropper component. /// The config options. /// The used to propagate notifications that the operation should be canceled. /// A representing result canvas asynchronous operation. public async ValueTask GetCroppedCanvasAsync( + [NotNull] Guid cropperComponentId, GetCroppedCanvasOptions getCroppedCanvasOptions, CancellationToken cancellationToken = default) { @@ -231,7 +288,11 @@ public async ValueTask GetCroppedCanvasAsync( await LoadModuleAsync(cancellationToken); } - IJSObjectReference jSCanvas = await _jsRuntime!.InvokeAsync("cropper.getCroppedCanvas", cancellationToken, getCroppedCanvasOptions); + IJSObjectReference jSCanvas = await _jsRuntime!.InvokeAsync( + "cropper.getCroppedCanvas", + cancellationToken, + cropperComponentId, + getCroppedCanvasOptions); return new CroppedCanvas(jSCanvas); } @@ -239,6 +300,7 @@ public async ValueTask GetCroppedCanvasAsync( /// /// Get a canvas drawn the cropped image. /// + /// The identifier of the cropper component. /// The config options. /// A string indicating the image format. The default type is image/png; this image format will be also used if the specified type is not supported. /// A number between 0 and 1 indicating the image quality to be used when creating images using file formats that support lossy compression (such as image/jpeg or image/webp). A user agent will use its default quality value if this option is not specified, or if the number is outside the allowed range. @@ -247,6 +309,7 @@ public async ValueTask GetCroppedCanvasAsync( /// The used to propagate notifications that the operation should be canceled. /// A representing URL result canvas asynchronous operation. public async ValueTask GetCroppedCanvasDataURLAsync( + [NotNull] Guid cropperComponentId, GetCroppedCanvasOptions getCroppedCanvasOptions, string type, float number, @@ -260,6 +323,7 @@ public async ValueTask GetCroppedCanvasDataURLAsync( return await _jsRuntime!.InvokeAsync( "cropper.getCroppedCanvasDataURL", cancellationToken, + cropperComponentId, getCroppedCanvasOptions, type, number); @@ -268,42 +332,58 @@ public async ValueTask GetCroppedCanvasDataURLAsync( /// /// Get the cropped area position and size data (base on the original image). /// + /// The identifier of the cropper component. /// Indicate if round the data values or not. /// The used to propagate notifications that the operation should be canceled. /// A representing result cropped data asynchronous operation. - public async ValueTask GetDataAsync(bool rounded, CancellationToken cancellationToken = default) + public async ValueTask GetDataAsync( + [NotNull] Guid cropperComponentId, + bool rounded, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - return await _jsRuntime!.InvokeAsync("cropper.getData", cancellationToken, rounded); + return await _jsRuntime!.InvokeAsync( + "cropper.getData", + cancellationToken, + cropperComponentId, + rounded); } /// /// Get the image position and size data. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing result image data asynchronous operation. - public async ValueTask GetImageDataAsync(CancellationToken cancellationToken = default) + public async ValueTask GetImageDataAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - return await _jsRuntime!.InvokeAsync("cropper.getImageData", cancellationToken); + return await _jsRuntime!.InvokeAsync( + "cropper.getImageData", + cancellationToken, + cropperComponentId); } /// /// Move the canvas with relative offsets. /// + /// The identifier of the cropper component. /// The relative offset distance on the x-axis. /// The relative offset distance on the y-axis. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask MoveAsync( + [NotNull] Guid cropperComponentId, decimal offsetX, decimal? offsetY, CancellationToken cancellationToken = default) @@ -313,17 +393,24 @@ public async ValueTask MoveAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.move", cancellationToken, offsetX, offsetY); + await _jsRuntime!.InvokeVoidAsync( + "cropper.move", + cancellationToken, + cropperComponentId, + offsetX, + offsetY); } /// /// Move the canvas to an absolute point. /// + /// The identifier of the cropper component. /// The x-axis coordinate. /// The y-axis coordinate. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask MoveToAsync( + [NotNull] Guid cropperComponentId, decimal x, decimal? y, CancellationToken cancellationToken = default) @@ -333,17 +420,24 @@ public async ValueTask MoveToAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.moveTo", cancellationToken, x, y); + await _jsRuntime!.InvokeVoidAsync( + "cropper.moveTo", + cancellationToken, + cropperComponentId, + x, + y); } /// /// Replace the image's src and rebuild the cropper. /// + /// The identifier of the cropper component. /// The new URL. /// If the new image has the same size as the old one, then it will not rebuild the cropper and only update the URLs of all related images. This can be used for applying filters. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask ReplaceAsync( + [NotNull] Guid cropperComponentId, string url, bool hasSameSize, CancellationToken cancellationToken = default) @@ -353,31 +447,44 @@ public async ValueTask ReplaceAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.replace", cancellationToken, url, hasSameSize); + await _jsRuntime!.InvokeVoidAsync( + "cropper.replace", + cancellationToken, + cropperComponentId, + url, + hasSameSize); } /// /// Reset the image and crop box to their initial states. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - public async ValueTask ResetAsync(CancellationToken cancellationToken = default) + public async ValueTask ResetAsync( + [NotNull] Guid cropperComponentId, + CancellationToken cancellationToken = default) { if (Module is null) { await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.reset", cancellationToken); + await _jsRuntime!.InvokeVoidAsync( + "cropper.reset", + cancellationToken, + cropperComponentId); } /// /// Rotate the canvas with a relative degree. /// + /// The identifier of the cropper component. /// The rotate degree. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask RotateAsync( + [NotNull] Guid cropperComponentId, decimal degree, CancellationToken cancellationToken = default) { @@ -386,16 +493,22 @@ public async ValueTask RotateAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.rotate", cancellationToken, degree); + await _jsRuntime!.InvokeVoidAsync( + "cropper.rotate", + cancellationToken, + cropperComponentId, + degree); } /// /// Rotate the canvas to an absolute degree. /// + /// The identifier of the cropper component. /// The rotate degree. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask RotateToAsync( + [NotNull] Guid cropperComponentId, decimal degree, CancellationToken cancellationToken = default) { @@ -404,17 +517,23 @@ public async ValueTask RotateToAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.rotateTo", cancellationToken, degree); + await _jsRuntime!.InvokeVoidAsync( + "cropper.rotateTo", + cancellationToken, + cropperComponentId, + degree); } /// /// Scale the image. /// + /// The identifier of the cropper component. /// The scale ratio on the x-axis. /// The scale ratio on the y-axis. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask ScaleAsync( + [NotNull] Guid cropperComponentId, decimal scaleX, decimal scaleY, CancellationToken cancellationToken = default) @@ -424,16 +543,23 @@ public async ValueTask ScaleAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.scale", cancellationToken, scaleX, scaleY); + await _jsRuntime!.InvokeVoidAsync( + "cropper.scale", + cancellationToken, + cropperComponentId, + scaleX, + scaleY); } /// /// Scale the image on the x-axis. /// + /// The identifier of the cropper component. /// The scale ratio on the x-axis. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask ScaleXAsync( + [NotNull] Guid cropperComponentId, decimal scaleX, CancellationToken cancellationToken = default) { @@ -442,16 +568,22 @@ public async ValueTask ScaleXAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.scaleX", cancellationToken, scaleX); + await _jsRuntime!.InvokeVoidAsync( + "cropper.scaleX", + cancellationToken, + cropperComponentId, + scaleX); } /// /// Scale the image on the y-axis. /// + /// The identifier of the cropper component. /// The scale ratio on the y-axis. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask ScaleYAsync( + [NotNull] Guid cropperComponentId, decimal scaleY, CancellationToken cancellationToken = default) { @@ -460,16 +592,22 @@ public async ValueTask ScaleYAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.scaleY", cancellationToken, scaleY); + await _jsRuntime!.InvokeVoidAsync( + "cropper.scaleY", + cancellationToken, + cropperComponentId, + scaleY); } /// /// Change the aspect ratio of the crop box. /// + /// The identifier of the cropper component. /// The new aspect ratio. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask SetAspectRatioAsync( + [NotNull] Guid cropperComponentId, decimal aspectRatio, CancellationToken cancellationToken = default) { @@ -478,16 +616,22 @@ public async ValueTask SetAspectRatioAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.setAspectRatio", cancellationToken, aspectRatio); + await _jsRuntime!.InvokeVoidAsync( + "cropper.setAspectRatio", + cancellationToken, + cropperComponentId, + aspectRatio); } /// /// Set the canvas position and size with new data. /// + /// The identifier of the cropper component. /// The new canvas data. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask SetCanvasDataAsync( + [NotNull] Guid cropperComponentId, SetCanvasDataOptions setCanvasDataOptions, CancellationToken cancellationToken = default) { @@ -496,16 +640,22 @@ public async ValueTask SetCanvasDataAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.setCanvasData", cancellationToken, setCanvasDataOptions); + await _jsRuntime!.InvokeVoidAsync( + "cropper.setCanvasData", + cancellationToken, + cropperComponentId, + setCanvasDataOptions); } /// /// Set the crop box position and size with new data. /// + /// The identifier of the cropper component. /// The new crop box data. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask SetCropBoxDataAsync( + [NotNull] Guid cropperComponentId, SetCropBoxDataOptions cropBoxDataOptions, CancellationToken cancellationToken = default) { @@ -514,16 +664,22 @@ public async ValueTask SetCropBoxDataAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.setCropBoxData", cancellationToken, cropBoxDataOptions); + await _jsRuntime!.InvokeVoidAsync( + "cropper.setCropBoxData", + cancellationToken, + cropperComponentId, + cropBoxDataOptions); } /// /// Set the cropped area position and size with new data. /// + /// The identifier of the cropper component. /// The new data. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask SetDataAsync( + [NotNull] Guid cropperComponentId, SetDataOptions setDataOptions, CancellationToken cancellationToken = default) { @@ -532,16 +688,22 @@ public async ValueTask SetDataAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.setData", cancellationToken, setDataOptions); + await _jsRuntime!.InvokeVoidAsync( + "cropper.setData", + cancellationToken, + cropperComponentId, + setDataOptions); } /// /// Change the drag mode. /// + /// The identifier of the cropper component. /// The new drag mode. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask SetDragModeAsync( + [NotNull] Guid cropperComponentId, DragMode dragMode, CancellationToken cancellationToken = default) { @@ -550,16 +712,22 @@ public async ValueTask SetDragModeAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.setDragMode", cancellationToken, dragMode.ToEnumString()); + await _jsRuntime!.InvokeVoidAsync( + "cropper.setDragMode", + cancellationToken, + cropperComponentId, + dragMode.ToEnumString()); } /// /// Zoom the canvas with a relative ratio. /// + /// The identifier of the cropper component. /// The target ratio. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask ZoomAsync( + [NotNull] Guid cropperComponentId, decimal ratio, CancellationToken cancellationToken = default) { @@ -568,18 +736,24 @@ public async ValueTask ZoomAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.zoom", cancellationToken, ratio); + await _jsRuntime!.InvokeVoidAsync( + "cropper.zoom", + cancellationToken, + cropperComponentId, + ratio); } /// /// Zoom the canvas to an absolute ratio. /// + /// The identifier of the cropper component. /// The target ratio. /// The zoom pivot point X coordinate. /// The zoom pivot point Y coordinate. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. public async ValueTask ZoomToAsync( + [NotNull] Guid cropperComponentId, decimal ratio, decimal pivotX, decimal pivotY, @@ -590,7 +764,13 @@ public async ValueTask ZoomToAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.zoomTo", cancellationToken, ratio, pivotX, pivotY); + await _jsRuntime!.InvokeVoidAsync( + "cropper.zoomTo", + cancellationToken, + cropperComponentId, + ratio, + pivotX, + pivotY); } /// @@ -605,7 +785,9 @@ public async ValueTask NoConflictAsync(CancellationToken cancellationToken = def await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.noConflict", cancellationToken); + await _jsRuntime!.InvokeVoidAsync( + "cropper.noConflict", + cancellationToken); } /// @@ -623,7 +805,10 @@ public async ValueTask SetDefaultsAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime!.InvokeVoidAsync("cropper.setDefaults", cancellationToken, options); + await _jsRuntime!.InvokeVoidAsync( + "cropper.setDefaults", + cancellationToken, + options); } /// @@ -647,7 +832,11 @@ public async ValueTask GetImageUsingStreamingAsync( var jsImageStream = imageFile.OpenReadStream(maxAllowedSize, cancellationToken); var dotnetImageStream = new DotNetStreamReference(jsImageStream); - return await _jsRuntime.InvokeAsync("cropper.getImageUsingStreaming", cancellationToken, dotnetImageStream); + + return await _jsRuntime.InvokeAsync( + "cropper.getImageUsingStreaming", + cancellationToken, + dotnetImageStream); } /// @@ -665,7 +854,10 @@ public async ValueTask RevokeObjectUrlAsync( await LoadModuleAsync(cancellationToken); } - await _jsRuntime.InvokeVoidAsync("cropper.revokeObjectUrl", cancellationToken, url); + await _jsRuntime.InvokeVoidAsync( + "cropper.revokeObjectUrl", + cancellationToken, + url); } /// diff --git a/src/Cropper.Blazor/Cropper.Blazor/Services/ICropperJsInterop.cs b/src/Cropper.Blazor/Cropper.Blazor/Services/ICropperJsInterop.cs index 0c781b42..4cb47894 100644 --- a/src/Cropper.Blazor/Cropper.Blazor/Services/ICropperJsInterop.cs +++ b/src/Cropper.Blazor/Cropper.Blazor/Services/ICropperJsInterop.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.CodeAnalysis; +using System; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Cropper.Blazor.Base; @@ -24,12 +25,14 @@ public interface ICropperJsInterop /// /// Initializes cropper. /// + /// The identifier of the cropper component. /// Reference to img html-DOM /// Cropper options /// Reference to base cropper component. Default equal to 'this' object. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask InitCropperAsync( + Guid cropperComponentId, [NotNull] ElementReference image, [NotNull] Options options, [NotNull] DotNetObjectReference cropperComponentBase, @@ -38,84 +41,115 @@ ValueTask InitCropperAsync( /// /// Clear the crop box. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - ValueTask ClearAsync(CancellationToken cancellationToken = default); + ValueTask ClearAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Show the crop box manually. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - ValueTask CropAsync(CancellationToken cancellationToken = default); + ValueTask CropAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Destroy the cropper and remove the instance from the image. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - ValueTask DestroyAsync(CancellationToken cancellationToken = default); + ValueTask DestroyAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Disable (freeze) the cropper. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - ValueTask DisableAsync(CancellationToken cancellationToken = default); + ValueTask DisableAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Enable (unfreeze) the cropper. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - ValueTask EnableAsync(CancellationToken cancellationToken = default); + ValueTask EnableAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Get the canvas position and size data. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing result canvas data asynchronous operation. - ValueTask GetCanvasDataAsync(CancellationToken cancellationToken = default); + ValueTask GetCanvasDataAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Get the container size data. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing result container data asynchronous operation. - ValueTask GetContainerDataAsync(CancellationToken cancellationToken = default); + ValueTask GetContainerDataAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Get the crop box position and size data. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing result crop box data asynchronous operation. - ValueTask GetCropBoxDataAsync(CancellationToken cancellationToken = default); + ValueTask GetCropBoxDataAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Get the cropped area position and size data (base on the original image). /// + /// The identifier of the cropper component. /// Indicate if round the data values or not. /// The used to propagate notifications that the operation should be canceled. /// A representing result cropped data asynchronous operation. ValueTask GetDataAsync( + Guid cropperComponentId, bool rounded, CancellationToken cancellationToken = default); /// /// Get the image position and size data. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing result image data asynchronous operation. - ValueTask GetImageDataAsync(CancellationToken cancellationToken = default); + ValueTask GetImageDataAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Move the canvas with relative offsets. /// + /// The identifier of the cropper component. /// The relative offset distance on the x-axis. /// The relative offset distance on the y-axis. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask MoveAsync( + Guid cropperComponentId, decimal offsetX, decimal? offsetY, CancellationToken cancellationToken = default); @@ -123,11 +157,13 @@ ValueTask MoveAsync( /// /// Move the canvas to an absolute point. /// + /// The identifier of the cropper component. /// The x-axis coordinate. /// The y-axis coordinate. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask MoveToAsync( + Guid cropperComponentId, decimal x, decimal? y, CancellationToken cancellationToken = default); @@ -135,11 +171,13 @@ ValueTask MoveToAsync( /// /// Replace the image's src and rebuild the cropper. /// + /// The identifier of the cropper component. /// The new URL. /// If the new image has the same size as the old one, then it will not rebuild the cropper and only update the URLs of all related images. This can be used for applying filters. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask ReplaceAsync( + Guid cropperComponentId, string url, bool hasSameSize, CancellationToken cancellationToken = default); @@ -147,38 +185,47 @@ ValueTask ReplaceAsync( /// /// Reset the image and crop box to their initial states. /// + /// The identifier of the cropper component. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. - ValueTask ResetAsync(CancellationToken cancellationToken = default); + ValueTask ResetAsync( + Guid cropperComponentId, + CancellationToken cancellationToken = default); /// /// Rotate the canvas with a relative degree. /// + /// The identifier of the cropper component. /// The rotate degree. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask RotateAsync( + Guid cropperComponentId, decimal degree, CancellationToken cancellationToken = default); /// /// Rotate the canvas to an absolute degree. /// + /// The identifier of the cropper component. /// The rotate degree. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask RotateToAsync( + Guid cropperComponentId, decimal degree, CancellationToken cancellationToken = default); /// /// Scale the image. /// + /// The identifier of the cropper component. /// The scale ratio on the x-axis. /// The scale ratio on the y-axis. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask ScaleAsync( + Guid cropperComponentId, decimal scaleX, decimal scaleY, CancellationToken cancellationToken = default); @@ -186,82 +233,98 @@ ValueTask ScaleAsync( /// /// Scale the image on the x-axis. /// + /// The identifier of the cropper component. /// The scale ratio on the x-axis. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask ScaleXAsync( + Guid cropperComponentId, decimal scaleX, CancellationToken cancellationToken = default); /// /// Scale the image on the y-axis. /// + /// The identifier of the cropper component. /// The scale ratio on the y-axis. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask ScaleYAsync( + Guid cropperComponentId, decimal scaleY, CancellationToken cancellationToken = default); /// /// Set the canvas position and size with new data. /// + /// The identifier of the cropper component. /// The new canvas data. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask SetCanvasDataAsync( + Guid cropperComponentId, SetCanvasDataOptions setCanvasDataOptions, CancellationToken cancellationToken = default); /// /// Set the crop box position and size with new data. /// + /// The identifier of the cropper component. /// The new crop box data. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask SetCropBoxDataAsync( + Guid cropperComponentId, SetCropBoxDataOptions cropBoxDataOptions, CancellationToken cancellationToken = default); /// /// Set the cropped area position and size with new data. /// + /// The identifier of the cropper component. /// The new data. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask SetDataAsync( + Guid cropperComponentId, SetDataOptions setDataOptions, CancellationToken cancellationToken = default); /// /// Change the drag mode. /// + /// The identifier of the cropper component. /// The new drag mode. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask SetDragModeAsync( + Guid cropperComponentId, DragMode dragMode, CancellationToken cancellationToken = default); /// /// Zoom the canvas with a relative ratio. /// + /// The identifier of the cropper component. /// The target ratio. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask ZoomAsync( + Guid cropperComponentId, decimal ratio, CancellationToken cancellationToken = default); /// /// Zoom the canvas to an absolute ratio. /// + /// The identifier of the cropper component. /// The target ratio. /// The zoom pivot point X coordinate. /// The zoom pivot point Y coordinate. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask ZoomToAsync( + Guid cropperComponentId, decimal ratio, decimal pivotX, decimal pivotY, @@ -287,26 +350,31 @@ ValueTask SetDefaultsAsync( /// /// Change the aspect ratio of the crop box. /// + /// The identifier of the cropper component. /// The new aspect ratio. /// The used to propagate notifications that the operation should be canceled. /// A representing any asynchronous operation. ValueTask SetAspectRatioAsync( + Guid cropperComponentId, decimal aspectRatio, CancellationToken cancellationToken = default); /// /// Get a canvas drawn the cropped image. /// + /// The identifier of the cropper component. /// The config options. /// The used to propagate notifications that the operation should be canceled. /// A representing result canvas asynchronous operation. ValueTask GetCroppedCanvasAsync( + Guid cropperComponentId, GetCroppedCanvasOptions getCroppedCanvasOptions, CancellationToken cancellationToken = default); /// /// Get a canvas drawn the cropped image. /// + /// The identifier of the cropper component. /// The config options. /// A string indicating the image format. The default type is image/png; this image format will be also used if the specified type is not supported. /// A number between 0 and 1 indicating the image quality to be used when creating images using file formats that support lossy compression (such as image/jpeg or image/webp). A user agent will use its default quality value if this option is not specified, or if the number is outside the allowed range. @@ -315,6 +383,7 @@ ValueTask GetCroppedCanvasAsync( /// The used to propagate notifications that the operation should be canceled. /// A representing URL result canvas asynchronous operation. ValueTask GetCroppedCanvasDataURLAsync( + Guid cropperComponentId, GetCroppedCanvasOptions getCroppedCanvasOptions, string type, float number, diff --git a/src/Cropper.Blazor/Cropper.Blazor/wwwroot/cropperJsInterop.js b/src/Cropper.Blazor/Cropper.Blazor/wwwroot/cropperJsInterop.js index 07f35864..0523316b 100644 --- a/src/Cropper.Blazor/Cropper.Blazor/wwwroot/cropperJsInterop.js +++ b/src/Cropper.Blazor/Cropper.Blazor/wwwroot/cropperJsInterop.js @@ -1,119 +1,149 @@ class CropperDecorator { constructor() { - this.cropperInstance = {}; + this.cropperInstances = {}; } - clear() { - return this.cropperInstance.clear(); + clear(cropperComponentId) { + return this.cropperInstances[cropperComponentId] + .clear(); } - crop() { - return this.cropperInstance.crop(); + crop(cropperComponentId) { + return this.cropperInstances[cropperComponentId] + .crop(); } - destroy() { - return this.cropperInstance.destroy(); + destroy(cropperComponentId) { + this.cropperInstances[cropperComponentId] + .destroy(); + delete this.cropperInstances[cropperComponentId]; } - disable() { - return this.cropperInstance.disable(); + disable(cropperComponentId) { + return this.cropperInstances[cropperComponentId] + .disable(); } - enable() { - return this.cropperInstance.enable(); + enable(cropperComponentId) { + return this.cropperInstances[cropperComponentId] + .enable(); } - getCanvasData() { - return this.cropperInstance.getCanvasData(); + getCanvasData(cropperComponentId) { + return this.cropperInstances[cropperComponentId] + .getCanvasData(); } - getContainerData() { - return this.cropperInstance.getContainerData(); + getContainerData(cropperComponentId) { + return this.cropperInstances[cropperComponentId] + .getContainerData(); } - getCropBoxData() { - return this.cropperInstance.getCropBoxData(); + getCropBoxData(cropperComponentId) { + return this.cropperInstances[cropperComponentId] + .getCropBoxData(); } - getCroppedCanvas(options) { - return this.cropperInstance.getCroppedCanvas(options); + getCroppedCanvas(cropperComponentId, options) { + return this.cropperInstances[cropperComponentId] + .getCroppedCanvas(options); } - getCroppedCanvasDataURL(options, type, encoderOptions) { - return this.cropperInstance.getCroppedCanvas(options).toDataURL(type, encoderOptions); + getCroppedCanvasDataURL(cropperComponentId, options, type, encoderOptions) { + return this.cropperInstances[cropperComponentId] + .getCroppedCanvas(options) + .toDataURL(type, encoderOptions); } - getData(rounded) { - return this.cropperInstance.getData(rounded); + getData(cropperComponentId, rounded) { + return this.cropperInstances[cropperComponentId] + .getData(rounded); } - getImageData() { - return this.cropperInstance.getImageData(); + getImageData(cropperComponentId) { + return this.cropperInstances[cropperComponentId] + .getImageData(); } - move(offsetX, offsetY) { - return this.cropperInstance.move(offsetX, offsetY); + move(cropperComponentId, offsetX, offsetY) { + return this.cropperInstances[cropperComponentId] + .move(offsetX, offsetY); } - moveTo(x, y) { - return this.cropperInstance.moveTo(x, y); + moveTo(cropperComponentId, x, y) { + return this.cropperInstances[cropperComponentId] + .moveTo(x, y); } - replace(url, onlyColorChanged) { - return this.cropperInstance.replace(url, onlyColorChanged); + replace(cropperComponentId, url, onlyColorChanged) { + return this.cropperInstances[cropperComponentId] + .replace(url, onlyColorChanged); } - reset() { - return this.cropperInstance.reset(); + reset(cropperComponentId) { + return this.cropperInstances[cropperComponentId] + .reset(); } - rotate(degree) { - return this.cropperInstance.rotate(degree); + rotate(cropperComponentId, degree) { + return this.cropperInstances[cropperComponentId] + .rotate(degree); } - rotateTo(degree) { - return this.cropperInstance.rotateTo(degree); + rotateTo(cropperComponentId, degree) { + return this.cropperInstances[cropperComponentId] + .rotateTo(degree); } - scale(scaleX, scaleY) { - return this.cropperInstance.scale(scaleX, scaleY); + scale(cropperComponentId, scaleX, scaleY) { + return this.cropperInstances[cropperComponentId] + .scale(scaleX, scaleY); } - scaleX(scaleX) { - return this.cropperInstance.scaleX(scaleX); + scaleX(cropperComponentId, scaleX) { + return this.cropperInstances[cropperComponentId] + .scaleX(scaleX); } - scaleY(scaleY) { - return this.cropperInstance.scaleY(scaleY); + scaleY(cropperComponentId, scaleY) { + return this.cropperInstances[cropperComponentId] + .scaleY(scaleY); } - setAspectRatio(aspectRatio) { - return this.cropperInstance.setAspectRatio(aspectRatio); + setAspectRatio(cropperComponentId, aspectRatio) { + return this.cropperInstances[cropperComponentId] + .setAspectRatio(aspectRatio); } - setCanvasData(data) { - return this.cropperInstance.setCanvasData(data); + setCanvasData(cropperComponentId, data) { + return this.cropperInstances[cropperComponentId] + .setCanvasData(data); } - setCropBoxData(data) { - return this.cropperInstance.setCropBoxData(data); + setCropBoxData(cropperComponentId, data) { + return this.cropperInstances[cropperComponentId] + .setCropBoxData(data); } - setData(data) { - return this.cropperInstance.setData(data); + setData(cropperComponentId, data) { + return this.cropperInstances[cropperComponentId] + .setData(data); } - setDragMode(dragMode) { - return this.cropperInstance.setDragMode(dragMode); + setDragMode(cropperComponentId, dragMode) { + return this.cropperInstances[cropperComponentId] + .setDragMode(dragMode); } - zoom(ratio) { - return this.cropperInstance.zoom(ratio); + zoom(cropperComponentId, ratio) { + return this.cropperInstances[cropperComponentId] + .zoom(ratio); } - zoomTo(ratio, pivotX, pivotY) { - return this.cropperInstance.zoomTo(ratio, { pivotX, pivotY }); + zoomTo(cropperComponentId, ratio, pivotX, pivotY) { + return this.cropperInstances[cropperComponentId] + .zoomTo(ratio, { pivotX, pivotY }); } noConflict() { @@ -154,10 +184,10 @@ class CropperDecorator { getJSEventDataDetail(instance) { if (instance.type === "zoom") { return { - oldRatio: instance.detail.oldRatio, - ratio: instance.detail.ratio, - originalEvent: instance.detail.originalEvent ? - DotNet.createJSObjectReference(instance.detail.originalEvent) : null + oldRatio: instance.detail.oldRatio, + ratio: instance.detail.ratio, + originalEvent: instance.detail.originalEvent ? + DotNet.createJSObjectReference(instance.detail.originalEvent) : null }; } else if (instance.type === "cropstart" || instance.type === "cropend" || instance.type === "cropmove") { @@ -201,7 +231,7 @@ class CropperDecorator { imageObject.invokeMethodAsync('CropperIsZoomed', jSEventData); } - initCropper(image, optionsImage, imageObject) { + initCropper(cropperComponentId, image, optionsImage, imageObject) { if (image == null) { throw "Parameter 'image' must be is not null!"; } @@ -242,7 +272,9 @@ class CropperDecorator { }); } - this.cropperInstance = new Cropper(image, options); + const cropper = new Cropper(image, options); + + this.cropperInstances[cropperComponentId] = cropper; } }