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">
@if (initialized) {
@if (this.initialized) {
<p-virtualscroller
[items]="cards"
[itemSize]="itemSize"
[items]="cardRows"
[itemSize]="[itemWidthInPx,itemHeightInPx]"
[lazy]="true"
[loading]="lazyLoading"
[appendOnly]="true"
[step]="pageSize"
[numToleratedItems]="numberOfToleratedItems"
(onLazyLoad)="onLazyLoad($event)"
scrollHeight="600px"
scrollWidth="600px"
orientation="both"
[style]="{ width: '100vw', height: '100%' }"
>
<ng-template
[appVirtualScroll]="cards"
pTemplate="content"
let-options="options"
<ng-template #item let-item let-options="options">
<div class="flex justify-around">
<p-card
*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">
<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"
@@ -50,11 +46,10 @@
quas!
</p>
</p-card>
}
</div>
</ng-template>
</p-virtualscroller>
} @else {
hold on im pogging rn
hold on im poggin
}
</div>

View File

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