Amend VirtualScroller

This commit is contained in:
2025-04-30 15:52:46 +02:00
parent ea27f1e23d
commit 6756678b1d
6 changed files with 144 additions and 92 deletions

View File

@@ -0,0 +1,17 @@
<div #container class="card flex justify-center">
hi there!
<p-virtualscroller
[items]="items"
[itemSize]="[50, 100]"
orientation="both"
styleClass="border border-surface"
[autoSize]="true"
[style]="{ width: '400px', height: '200px' }"
>
<ng-template pTemplate="item" let-item let-options="options">
<div class="flex items-center p-2" [ngClass]="{ 'bg-surface-100 dark:bg-surface-700': options.odd }" style="height: 50px;">
<div *ngFor="let el of item" style="width: 100px">{{ el }}</div>
</div>
</ng-template>
</p-virtualscroller>
</div>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GridItemsComponent } from './grid-items.component';
describe('GridItemsComponent', () => {
let component: GridItemsComponent;
let fixture: ComponentFixture<GridItemsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [GridItemsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(GridItemsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,33 @@
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {Scroller, ScrollerModule} from 'primeng/scroller';
import {NgClass, NgForOf, NgStyle} from '@angular/common';
@Component({
selector: 'app-grid-items',
imports: [
ScrollerModule,
NgClass,
NgForOf,
NgStyle
],
styles: [
`:host ::ng-deep {
.p-scroller-viewport {
flex: none;
}
}`
],
templateUrl: './grid-items.component.html',
styleUrl: './grid-items.component.css'
})
export class GridItemsComponent implements OnInit {
@ViewChild('container') containerRef!: ElementRef<HTMLElement>;
private readonly FONT_SIZE_PX = 14;
items!: string[][];
widthTest: string = '300px';
ngOnInit() {
this.items = Array.from({ length: 1000 }).map((_, i) => Array.from({ length: 1000 }).map((_j, j) => `Item #${i}_${j}`));
}
}

View File

@@ -1,25 +1,21 @@
<div #container class="h-full w-screen"> <div #container class="h-full w-screen">
@if (initialized) { @if (this.initialized) {
<p-virtualscroller <p-virtualscroller
[items]="cards" [items]="cardRows"
[itemSize]="itemSize" [itemSize]="[itemWidthInPx,itemHeightInPx]"
[lazy]="true" [lazy]="true"
[loading]="lazyLoading" [loading]="lazyLoading"
[appendOnly]="true" [appendOnly]="true"
[step]="pageSize"
[numToleratedItems]="numberOfToleratedItems"
(onLazyLoad)="onLazyLoad($event)" (onLazyLoad)="onLazyLoad($event)"
scrollHeight="600px" orientation="both"
scrollWidth="600px" [style]="{ width: '100vw', height: '100%' }"
> >
<ng-template <ng-template #item let-item let-options="options">
[appVirtualScroll]="cards" <div class="flex justify-around">
pTemplate="content" <p-card
let-options="options" *ngFor="let card of item"
styleClass="w-[24rem] h-[36rem] m-2 overflow-hidden"
> >
<div class="flex-none flex-wrap justify-around">
@for (card of options.items; track card.id) {
<p-card styleClass="w-[24rem] h-[36rem] m-2 overflow-hidden">
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<div <div
class="relative duration-200 w-[23rem] rounded-lg ease-in-out h-64 z-10 overflow-hidden hover:h-[34rem] hover:translate-y-2 m-2 after:absolute after:inset-0 after:shadow-[inset_0_-10px_30px_15px_rgba(0,0,0,0.85)] after:content-[''] hover:inset-0" class="relative duration-200 w-[23rem] rounded-lg ease-in-out h-64 z-10 overflow-hidden hover:h-[34rem] hover:translate-y-2 m-2 after:absolute after:inset-0 after:shadow-[inset_0_-10px_30px_15px_rgba(0,0,0,0.85)] after:content-[''] hover:inset-0"
@@ -50,11 +46,10 @@
quas! quas!
</p> </p>
</p-card> </p-card>
}
</div> </div>
</ng-template> </ng-template>
</p-virtualscroller> </p-virtualscroller>
} @else { } @else {
hold on im pogging rn hold on im poggin
} }
</div> </div>

View File

@@ -1,22 +1,22 @@
// noinspection DuplicatedCode // noinspection DuplicatedCode
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core'; import {AfterViewInit, Component, ElementRef, ViewChild} from '@angular/core';
import {Scroller, ScrollerLazyLoadEvent} from 'primeng/scroller'; import {Scroller, ScrollerLazyLoadEvent} from 'primeng/scroller';
import {Card as CardModel, CardService, PageDeck} from '../../openapi'; import {Card as CardModel, CardService} from '../../openapi';
import {BehaviorSubject, catchError, debounceTime, of, Subject, switchMap} from 'rxjs'; import {catchError, debounceTime, of, Subject, switchMap} from 'rxjs';
import {Button} from 'primeng/button'; import {Button} from 'primeng/button';
import {Card} from 'primeng/card'; import {Card} from 'primeng/card';
import {PrimeTemplate} from 'primeng/api'; import {PrimeTemplate} from 'primeng/api';
import {VirtualScrollDirective} from '../../directives/virtual-scroll.directive'; import {NgForOf} from '@angular/common';
@Component({ @Component({
selector: 'app-cards', selector: 'app-cards',
imports: [ imports: [
Scroller,
Button, Button,
Card, Card,
PrimeTemplate, PrimeTemplate,
Scroller, NgForOf
VirtualScrollDirective
], ],
templateUrl: './cards.component.html', templateUrl: './cards.component.html',
styleUrl: './cards.component.css' styleUrl: './cards.component.css'
@@ -29,21 +29,21 @@ export class CardsComponent implements AfterViewInit {
private readonly FONT_SIZE_PX = 14; private readonly FONT_SIZE_PX = 14;
private pageSubject = new BehaviorSubject<number>(0); private pageSubject = new Subject<number>();
private resizeObserver!: ResizeObserver; private resizeObserver!: ResizeObserver;
pageSize: number = 5;
page: number = 0; page: number = 0;
// noinspection PointlessArithmeticExpressionJS Card height + ( margin top/bottom) (rem) pageSize: number = 5;
rowHeight: number = 36 + (0.5 * 2); itemHeight: number = 36 + (0.5 * 2);
itemWidth: number = 24; // Card width (rem) itemWidth: number = 24;
cards: CardModel[] = []; itemHeightInPx: number = this.itemHeight * this.FONT_SIZE_PX;
lazyLoading: boolean = false; itemWidthInPx: number = this.itemWidth * this.FONT_SIZE_PX;
cardRows: CardModel[][] = [];
lazyLoading: boolean = true;
initialized: boolean = false;
itemsPerRow!: number; itemsPerRow!: number;
rowsInPage!: number; rowsInPage!: number;
itemSize: number = this.rowHeight; lastResponseSize?: number;
numberOfToleratedItems: number = 5;
initialized: boolean = false;
ngAfterViewInit(): void { ngAfterViewInit(): void {
this.setupResizeObserver(); this.setupResizeObserver();
@@ -65,54 +65,38 @@ export class CardsComponent implements AfterViewInit {
) )
.subscribe(cards => { .subscribe(cards => {
this.lazyLoading = false; this.lazyLoading = false;
if (!this.lastResponseSize) {
this.lastResponseSize = cards.length;
}
if (!this.initialized) { if (!this.initialized) {
this.initialized = true; this.initialized = true;
} }
this.cards = [ this.cardRows[this.page] = cards
...this.cards, console.log(cards.length);
...cards
]
}) })
} }
private setupResizeObserver(): void { private setupResizeObserver(): void {
this.resizeObserver = new ResizeObserver(entries => { this.resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) { for (const entry of entries) {
this.calculateRowsInPage(entry.contentRect.height); this.rowsInPage = Math.ceil(entry.contentRect.height / this.itemHeightInPx);
this.calculateItemsPerRow(entry.contentRect.width); this.itemsPerRow = Math.floor(entry.contentRect.width / this.itemWidthInPx);
this.pageSize = this.itemsPerRow;
const itemsInViewPort = this.itemsPerRow * this.rowsInPage;
this.pageSize = itemsInViewPort;
this.numberOfToleratedItems = itemsInViewPort;
} }
}); });
this.resizeObserver.observe(this.containerRef.nativeElement); this.resizeObserver.observe(this.containerRef.nativeElement);
} }
private calculateItemsPerRow(containerWidth: number): void {
const newItemsPerRow = Math.floor(containerWidth / (this.itemWidth * this.FONT_SIZE_PX)) || 1;
if (newItemsPerRow !== this.itemsPerRow) {
this.itemsPerRow = newItemsPerRow;
}
this.updateVirtualScrollerSettings();
}
private calculateRowsInPage(containerHeight: number) {
this.rowsInPage = Math.ceil(containerHeight / (this.rowHeight * this.FONT_SIZE_PX));
}
private updateVirtualScrollerSettings(): void {
this.itemSize = (this.rowHeight * this.FONT_SIZE_PX) / this.itemsPerRow;
}
onLazyLoad(event: ScrollerLazyLoadEvent) { onLazyLoad(event: ScrollerLazyLoadEvent) {
if (!event || event.last % this.pageSize !== 0) { console.log(`kinda pogging rn cuz ${JSON.stringify(event)}`)
return;
}
this.pageSubject.next(++this.page); this.pageSubject.next(++this.page);
} }
protected readonly JSON = JSON;
} }