2026-01-07 18:24:24 -06:00
|
|
|
import { InfiniteScrollAnchor } from "./InfiniteScrollAnchor";
|
2026-01-03 15:53:17 -06:00
|
|
|
|
2026-01-07 18:24:24 -06:00
|
|
|
export class InfiniteScroll {
|
|
|
|
|
public scrollHost: Element;
|
|
|
|
|
public sentinel: Element;
|
2026-01-11 14:38:24 -06:00
|
|
|
public dotnet: any;
|
2026-01-03 15:53:17 -06:00
|
|
|
|
2026-01-11 14:38:24 -06:00
|
|
|
public static Create(dotnet: any, scrollHost: Element, sentinel: Element): InfiniteScroll {
|
|
|
|
|
return new InfiniteScroll(dotnet, scrollHost, sentinel);
|
2026-01-07 18:24:24 -06:00
|
|
|
}
|
2026-01-03 15:53:17 -06:00
|
|
|
|
2026-01-11 14:38:24 -06:00
|
|
|
constructor(dotnet: any, scrollHost: Element, sentinel: Element) {
|
|
|
|
|
this.dotnet = dotnet;
|
2026-01-07 18:24:24 -06:00
|
|
|
this.scrollHost = scrollHost;
|
|
|
|
|
this.sentinel = sentinel;
|
|
|
|
|
}
|
2026-01-03 15:53:17 -06:00
|
|
|
|
2026-01-07 18:24:24 -06:00
|
|
|
ObserveSentinel() {
|
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
|
|
|
for (const entry of entries) {
|
|
|
|
|
if (entry.isIntersecting) {
|
2026-01-11 14:38:24 -06:00
|
|
|
this.dotnet.invokeMethodAsync("OnSentinelVisible");
|
2026-01-07 18:24:24 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, {
|
|
|
|
|
root: this.scrollHost,
|
|
|
|
|
threshold: 0.01
|
|
|
|
|
});
|
2026-01-03 15:53:17 -06:00
|
|
|
|
2026-01-07 18:24:24 -06:00
|
|
|
observer.observe(this.sentinel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CaptureAnchor(): InfiniteScrollAnchor {
|
|
|
|
|
const first = this.scrollHost.querySelector("[data-id]");
|
|
|
|
|
|
|
|
|
|
if (!first)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
const rect = first.getBoundingClientRect();
|
|
|
|
|
const hostRect = this.scrollHost.getBoundingClientRect();
|
|
|
|
|
|
|
|
|
|
const anchor = new InfiniteScrollAnchor();
|
|
|
|
|
|
|
|
|
|
anchor.Id = first.getAttribute("data-id");
|
|
|
|
|
anchor.OffsetTop = rect.top - hostRect.top;
|
|
|
|
|
|
|
|
|
|
return anchor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RestoreAfterPrepend(anchor: InfiniteScrollAnchor) {
|
|
|
|
|
if (!anchor)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const el = this.scrollHost.querySelector(`[data-id="${anchor.Id}"]`);
|
|
|
|
|
|
|
|
|
|
if (!el)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const rect = el.getBoundingClientRect();
|
|
|
|
|
const hostRect = this.scrollHost.getBoundingClientRect();
|
|
|
|
|
const newOffset = rect.top - hostRect.top;
|
|
|
|
|
|
|
|
|
|
this.scrollHost.scrollTop += (newOffset - anchor.OffsetTop);
|
|
|
|
|
}
|
2026-01-11 14:38:24 -06:00
|
|
|
|
|
|
|
|
IsSentinelVisible(): boolean {
|
|
|
|
|
const rect = this.sentinel.getBoundingClientRect();
|
|
|
|
|
const hostRect = this.scrollHost.getBoundingClientRect();
|
|
|
|
|
|
|
|
|
|
// Check if sentinel is within the visible area of the scroll host
|
|
|
|
|
return rect.top >= hostRect.top && rect.top <= hostRect.bottom;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ScrollToBottom() {
|
|
|
|
|
this.scrollHost.scrollTop = this.scrollHost.scrollHeight;
|
|
|
|
|
}
|
2026-01-03 15:53:17 -06:00
|
|
|
}
|