Amend VirtualScroller
This commit is contained in:
17
src/app/component/grid-items/grid-items.component.html
Normal file
17
src/app/component/grid-items/grid-items.component.html
Normal 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>
|
||||
23
src/app/component/grid-items/grid-items.component.spec.ts
Normal file
23
src/app/component/grid-items/grid-items.component.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
33
src/app/component/grid-items/grid-items.component.ts
Normal file
33
src/app/component/grid-items/grid-items.component.ts
Normal 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}`));
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,55 @@
|
||||
<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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<div class="absolute -inset-0">
|
||||
<!--suppress AngularNgOptimizedImage -->
|
||||
<img
|
||||
alt="Card"
|
||||
class="w-full h-full object-cover object-center"
|
||||
src="http://localhost:8080{{ card.imageApiPath }}"
|
||||
/>
|
||||
</div>
|
||||
<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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<div class="absolute -inset-0">
|
||||
<!--suppress AngularNgOptimizedImage -->
|
||||
<img
|
||||
alt="Card"
|
||||
class="w-full h-full object-cover object-center"
|
||||
src="http://localhost:8080{{ card.imageApiPath }}"
|
||||
/>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="title"> {{ card.name }}</ng-template>
|
||||
<ng-template pTemplate="subtitle"> Card subtitle</ng-template>
|
||||
<ng-template pTemplate="footer">
|
||||
<div class="flex gap-4 mt-1">
|
||||
<p-button label="Cancel" severity="secondary" class="w-full" [outlined]="true" styleClass="w-full"/>
|
||||
<p-button label="Save" class="w-full" styleClass="w-full"/>
|
||||
</div>
|
||||
</ng-template>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae
|
||||
numquam deserunt
|
||||
quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate
|
||||
neque
|
||||
quas!
|
||||
</p>
|
||||
</p-card>
|
||||
}
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="title"> {{ card.name }}</ng-template>
|
||||
<ng-template pTemplate="subtitle"> Card subtitle</ng-template>
|
||||
<ng-template pTemplate="footer">
|
||||
<div class="flex gap-4 mt-1">
|
||||
<p-button label="Cancel" severity="secondary" class="w-full" [outlined]="true" styleClass="w-full"/>
|
||||
<p-button label="Save" class="w-full" styleClass="w-full"/>
|
||||
</div>
|
||||
</ng-template>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae
|
||||
numquam deserunt
|
||||
quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate
|
||||
neque
|
||||
quas!
|
||||
</p>
|
||||
</p-card>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-virtualscroller>
|
||||
} @else {
|
||||
hold on im pogging rn
|
||||
hold on im poggin
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user