190 lines
6.2 KiB
Plaintext
190 lines
6.2 KiB
Plaintext
@inject IJSRuntime js
|
|
|
|
|
|
<div class="flex p-64 h-screen w-screen">
|
|
<div id="dropzone" name="file" class="w-full h-96 bg-gray-300 rounded-2xl " type="file"></div>
|
|
<img id="cropped-image" class="w-full h-screen py-60" src="" alt="">
|
|
|
|
<div id="cropped" class="hidden">
|
|
<img id="cropped-image" class="w-full h-96" src="" alt="">
|
|
<div class="flex">
|
|
<button type="button" @onclick="RemoveCropped" class="btn btn-primary">test</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
@code {
|
|
|
|
[Parameter]
|
|
public string UploadLink { get; set; }
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender)
|
|
{
|
|
await js.InvokeVoidAsync("startCropper");
|
|
}
|
|
}
|
|
|
|
async Task RemoveCropped()
|
|
{
|
|
await js.InvokeVoidAsync("removeCropped");
|
|
}
|
|
|
|
public async Task<string> UploadPhoto()
|
|
{
|
|
var photoLink = "";
|
|
|
|
return photoLink;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
@* @namespace MediaComponentLib
|
|
@using Microsoft.Extensions.Localization
|
|
@using Microsoft.AspNetCore.Components.Forms
|
|
@inject IStringLocalizer<Cropper> Loc
|
|
@inject IJSRuntime JSRuntime
|
|
@implements IAsyncDisposable
|
|
|
|
<div class="flex flex-col p-4 w-full h-full min-h-[500px] items-center gap-4">
|
|
|
|
@if (string.IsNullOrEmpty(_imageData))
|
|
{
|
|
<div
|
|
class="w-full h-64 bg-gray-100 rounded-2xl flex flex-col items-center justify-center relative border-2 border-dashed border-gray-300 hover:bg-gray-50 transition-colors">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-gray-400 mb-2" fill="none" viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
</svg>
|
|
<span class="text-lg text-gray-500 font-medium">Drop image here or click to upload</span>
|
|
<InputFile class="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10" OnChange="HandleFileSelected"
|
|
accept=".jpg,.jpeg,.png,.webp" />
|
|
</div>
|
|
}
|
|
else if (!_isCropped)
|
|
{
|
|
<div class="w-full flex-1 bg-black rounded-lg overflow-hidden relative" style="min-height: 400px;">
|
|
<!-- Unique ID used by Interop -->
|
|
<img id="@_uniqueId" src="@_imageData" @onload="OnImageLoaded" class="max-w-full max-h-full block mx-auto"
|
|
style="display: block;" />
|
|
</div>
|
|
|
|
<div class="flex gap-2 w-full justify-end">
|
|
<button class="btn btn-ghost" @onclick="Reset">@Loc["Cancel"]</button>
|
|
<button class="btn btn-primary" @onclick="CropImage">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
@Loc["Crop"]
|
|
</button>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="flex flex-col gap-4 items-center w-full">
|
|
<div class="bg-base-200 p-2 rounded-lg shadow-sm">
|
|
<img src="@_croppedData" class="max-w-full max-h-[500px] rounded" />
|
|
</div>
|
|
<button class="btn btn-warning" @onclick="Reset">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24"
|
|
stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
|
</svg>
|
|
@Loc["Remove"]
|
|
</button>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
@code {
|
|
[Parameter] public string? UploadLink { get; set; }
|
|
|
|
// Unique ID for this instance
|
|
private string _uniqueId = "cropper-" + Guid.NewGuid().ToString("N");
|
|
|
|
private string? _imageData;
|
|
private string? _croppedData;
|
|
private bool _isCropped;
|
|
private MediaJsInterop? _mediaInterop;
|
|
private DotNetObjectReference<Cropper>? _objRef;
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
_mediaInterop = new MediaJsInterop(JSRuntime);
|
|
_objRef = DotNetObjectReference.Create(this);
|
|
}
|
|
|
|
private async Task HandleFileSelected(InputFileChangeEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
var file = e.File;
|
|
if (file == null) return;
|
|
|
|
// Limit to 10MB
|
|
var maxFileSize = 10L * 1024 * 1024;
|
|
var stream = file.OpenReadStream(maxFileSize);
|
|
using var ms = new MemoryStream();
|
|
await stream.CopyToAsync(ms);
|
|
|
|
var base64 = Convert.ToBase64String(ms.ToArray());
|
|
_imageData = $"data:{file.ContentType};base64,{base64}";
|
|
_isCropped = false;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error reading file: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private async Task OnImageLoaded()
|
|
{
|
|
if (_mediaInterop != null && !string.IsNullOrEmpty(_imageData))
|
|
{
|
|
// Small delay to ensure render limits are applied by browser
|
|
await Task.Delay(50);
|
|
await _mediaInterop.InitCropperAsync(_uniqueId, 16.0 / 9.0, _objRef!);
|
|
}
|
|
}
|
|
|
|
private async Task CropImage()
|
|
{
|
|
if (_mediaInterop != null)
|
|
{
|
|
_croppedData = await _mediaInterop.GetCroppedImageAsync();
|
|
await _mediaInterop.DestroyCropperAsync();
|
|
_isCropped = true;
|
|
}
|
|
}
|
|
|
|
private async Task Reset()
|
|
{
|
|
if (_mediaInterop != null)
|
|
await _mediaInterop.DestroyCropperAsync();
|
|
|
|
_imageData = null;
|
|
_croppedData = null;
|
|
_isCropped = false;
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (_mediaInterop != null)
|
|
{
|
|
try { await _mediaInterop.DestroyCropperAsync(); } catch { }
|
|
}
|
|
_objRef?.Dispose();
|
|
}
|
|
} *@ |