108 lines
3.2 KiB
C#
108 lines
3.2 KiB
C#
using Ljbc1994.Blazor.IntersectionObserver;
|
|
using Ljbc1994.Blazor.IntersectionObserver.API;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Rendering;
|
|
using Microsoft.JSInterop;
|
|
|
|
namespace Iceshrimp.Frontend.Components;
|
|
|
|
public class LazyComponent : ComponentBase, IAsyncDisposable
|
|
{
|
|
[Inject] private IIntersectionObserverService ObserverService { get; set; } = null!;
|
|
[Inject] private IJSInProcessRuntime Js { get; set; } = null!;
|
|
[Inject] private ILogger<LazyComponent> Logger { get; set; } = null!;
|
|
[Parameter] [EditorRequired] public required RenderFragment ChildContent { get; set; } = default!;
|
|
[Parameter] public float? InitialHeight { get; set; }
|
|
private IntersectionObserver? Observer { get; set; }
|
|
public ElementReference Target { get; private set; }
|
|
public bool Visible { get; private set; }
|
|
private float? Height { get; set; }
|
|
// private bool minHeightSet = false;
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
if (InitialHeight is not null)
|
|
{
|
|
Height = InitialHeight;
|
|
}
|
|
}
|
|
|
|
public float? GetHeight()
|
|
{
|
|
if (Height != null) return Height.Value;
|
|
if (Visible) return Js.Invoke<float>("getHeight", Target);
|
|
else
|
|
{
|
|
Logger.LogWarning("Invisible, no height available");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
|
{
|
|
builder.OpenElement(1, "div");
|
|
var classnames = "target lazy-component-target-internal";
|
|
if (Visible) classnames = "target lazy-component-target-internal visible";
|
|
builder.AddAttribute(2, "class", classnames);
|
|
if (Height is not null)
|
|
{
|
|
builder.AddAttribute(4, "style", $"--height: {Height}px");
|
|
}
|
|
builder.AddElementReferenceCapture(5, elementReference => Target = elementReference);
|
|
|
|
builder.OpenRegion(10);
|
|
if (Visible)
|
|
{
|
|
builder.AddContent(1, ChildContent);
|
|
}
|
|
else
|
|
{
|
|
builder.OpenElement(2, "div");
|
|
builder.AddAttribute(3, "class", "placeholder");
|
|
if (Height is not null)
|
|
{
|
|
builder.AddAttribute(4, "style", $"height: {Height}px");
|
|
}
|
|
else
|
|
{
|
|
builder.AddAttribute(5, "style", "height: 5rem");
|
|
}
|
|
|
|
builder.CloseElement();
|
|
}
|
|
|
|
builder.CloseRegion();
|
|
builder.CloseElement();
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender)
|
|
{
|
|
Observer = await ObserverService.Create(OnIntersect);
|
|
await Observer.Observe(Target);
|
|
}
|
|
}
|
|
|
|
private void OnIntersect(IList<IntersectionObserverEntry> entries)
|
|
{
|
|
var entry = entries.First();
|
|
if (Visible && entry.IsIntersecting is false)
|
|
{
|
|
Height = Js.Invoke<float>("getHeight", Target);
|
|
Visible = false;
|
|
StateHasChanged();
|
|
}
|
|
|
|
if (Visible is false && entry.IsIntersecting)
|
|
{
|
|
Visible = true;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (Observer != null) await Observer.Dispose();
|
|
}
|
|
}
|