@foreach (var url in MediaUrls)
{
var isSelected = url == _selectedUrl;
var type = GetMediaType(url);
}
}
@code {
[Parameter] public IEnumerable MediaUrls { get; set; } = Enumerable.Empty();
[Parameter] public string? Class { get; set; }
[Parameter] public int Width { get; set; } = 1;
[Parameter] public int Height { get; set; } = 1;
[Parameter] public int ThumbSize { get; set; } = 64;
private ElementReference _thumbContainer;
private string? _selectedUrl;
private string _zoomStyle = "";
protected override void OnInitialized()
{
if (MediaUrls.Any()) _selectedUrl = MediaUrls.First();
}
protected override void OnParametersSet()
{
if (!string.IsNullOrEmpty(_selectedUrl) && !MediaUrls.Contains(_selectedUrl))
{
_selectedUrl = MediaUrls.FirstOrDefault();
}
else if (string.IsNullOrEmpty(_selectedUrl) && MediaUrls.Any())
{
_selectedUrl = MediaUrls.First();
}
}
private void HandleMouseMove(MouseEventArgs e)
{
// For a spotlight lens, we need to calculate the percentage of mouse position
// We assume a base size or just use a placeholder for now.
// Since we can't get the width/height easily, we'll use a standard percentage approach
// if we assume the container is roughly the size of viewport or fixed.
// Actually, without JS, OffsetX/Y is the only way.
// We'll use a rough 600px base for calculation or just standard 0-1 range if we knew bounds.
// Let's use a "Magic Lens" approach:
double zoomLevel = 2.5;
double lensSize = 180;
// We'll estimate the percentage by assuming the mouse is within the container bounds.
// A better way is to pass the container size via a simplistic JS on resize.
// But for now, let's just use the current offset and hope it's reasonably mapped.
double posX = e.OffsetX;
double posY = e.OffsetY;
// We'll just use the raw offsets to position the lens
_zoomStyle = $"display: block; left: {posX - (lensSize / 2)}px; top: {posY - (lensSize / 2)}px; " +
$"background-image: url('{_selectedUrl}'); " +
$"background-size: {zoomLevel * 100}%; " +
$"background-position: {(posX / 500.0) * 100}% {(posY / 500.0) * 100}%;";
}
private void HandleMouseLeave()
{
_zoomStyle = "display: none;";
}
private async Task SelectMedia(string url)
{
_selectedUrl = url;
HandleMouseLeave();
StateHasChanged();
await ScrollToActiveThumbnail();
}
private async Task NextMedia()
{
var list = MediaUrls.ToList();
var index = list.IndexOf(_selectedUrl ?? "");
if (index != -1)
{
var nextIndex = (index + 1) % list.Count;
await SelectMedia(list[nextIndex]);
}
}
private async Task PreviousMedia()
{
var list = MediaUrls.ToList();
var index = list.IndexOf(_selectedUrl ?? "");
if (index != -1)
{
var prevIndex = (index - 1 + list.Count) % list.Count;
await SelectMedia(list[prevIndex]);
}
}
private async Task ScrollToActiveThumbnail()
{
try
{
await JSRuntime.InvokeVoidAsync("eval", @"
var container = document.querySelector('.scrollbar-hide');
if (container) {
var activeThumb = container.querySelector('.border-sky-500');
if (activeThumb) {
activeThumb.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
}
}
");
}
catch { }
}
private async Task Scroll(int amount)
{
try
{
await JSRuntime.InvokeVoidAsync("eval", $"document.querySelector('.scrollbar-hide').scrollBy({{ left: {amount}, behavior: 'smooth' }})");
}
catch { } // Fail gracefully if JS is busy
}
private enum MediaType { Image, Video, YouTube, Vimeo, Unknown }
private MediaType GetMediaType(string url)
{
if (string.IsNullOrWhiteSpace(url)) return MediaType.Image;
url = url.ToLower();
if (url.Contains("youtube.com") || url.Contains("youtu.be")) return MediaType.YouTube;
if (url.Contains("vimeo.com")) return MediaType.Vimeo;
if (url.EndsWith(".mp4") || url.EndsWith(".webm") || url.EndsWith(".ogg")) return MediaType.Video;
return MediaType.Image;
}
private string GetYouTubeId(string url)
{
if (url.Contains("youtu.be/")) return url.Split("youtu.be/")[1].Split("?")[0];
if (url.Contains("v=")) return url.Split("v=")[1].Split("&")[0];
return "";
}
}