Skip to content

Commit

Permalink
bump
Browse files Browse the repository at this point in the history
  • Loading branch information
rdlabo committed Jul 30, 2024
1 parent 06cd116 commit a23563e
Show file tree
Hide file tree
Showing 12 changed files with 235 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
<p>scroll-advanced-item works!</p>
<ion-item>
<ion-avatar slot="start">
<ion-img [src]="item()?.photo"></ion-img>
</ion-avatar>
<ion-label class="ion-text-wrap">
<h2>{{ item()?.name }}</h2>
<p>{{ item()?.description }}</p>
</ion-label>
</ion-item>
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
import { Component, OnInit } from '@angular/core';
import { Component, effect, ElementRef, inject, input, OnInit } from '@angular/core';
import { ScrollAdvancedItem } from '../../scroll-strategies.type';
import { IonAvatar, IonImg, IonItem, IonLabel } from '@ionic/angular/standalone';
import { ScrollAdvancedCalcService } from '../../scroll-advanced-calc.service';

@Component({
selector: 'app-scroll-advanced-item',
templateUrl: './scroll-advanced-item.component.html',
styleUrls: ['./scroll-advanced-item.component.scss'],
standalone: true,
imports: [IonItem, IonAvatar, IonImg, IonLabel],
})
export class ScrollAdvancedItemComponent implements OnInit {
constructor() {}
item = input<ScrollAdvancedItem>();
#el = inject(ElementRef);
#calcService = inject(ScrollAdvancedCalcService);

constructor() {
effect(() =>
(async (item: ScrollAdvancedItem | undefined) => {
if (item === undefined) {
return;
}
// Wait for rendering
await new Promise((resolve) => requestAnimationFrame(resolve));
this.#calcService.cacheCalcDynamic.update((cache) => {
if (!cache.some((c) => c.trackId === item.trackId)) {
cache.push({
trackId: item.trackId,
itemSize: this.#el.nativeElement.getBoundingClientRect().height,
});
}
// Update, but not notify to computed
return cache;
});
})(this.item()),
);
}

ngOnInit() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,29 @@
<ion-back-button defaultHref="/main/scroll-strategies"></ion-back-button>
</ion-buttons>
<ion-title>scroll-advanced</ion-title>
<ion-buttons slot="end">
<ion-button color="dark" href="https://github.com/rdlabo-team/ionic-angular-library/tree/main/projects/scroll-strategies#readme"
><ion-icon name="logo-github" slot="icon-only"></ion-icon
></ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>

<ion-content> </ion-content>
<ion-content [scrollY]="false">
<cdk-virtual-scroll-viewport
class="ion-content-scroll-host"
rdlaboFixVirtualScrollElement
[itemDynamicSizes]="dynamicSize()"
minBufferPx="900"
maxBufferPx="1350"
>
<ion-list>
<div *cdkVirtualFor="let item of items(); let i = index; trackBy: trackByFn">
<app-scroll-advanced-item [item]="item" [style.height]="bindDynamicItemHeight()[i]" class="dynamic-item"></app-scroll-advanced-item>
</div>
</ion-list>
<ion-infinite-scroll (ionInfinite)="loadInfinite($any($event))">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</cdk-virtual-scroll-viewport>
</ion-content>
Original file line number Diff line number Diff line change
@@ -1,17 +1,128 @@
import { Component, OnInit } from '@angular/core';
import { Component, computed, inject, OnInit, signal, viewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonBackButton, IonButtons, IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone';
import {
IonAvatar,
IonBackButton,
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonImg,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonItem,
IonLabel,
IonList,
IonTitle,
IonToolbar,
ViewDidEnter,
ViewDidLeave,
ViewWillEnter,
} from '@ionic/angular/standalone';
import { CdkDynamicSizeVirtualScroll, DynamicSizeVirtualScrollService, itemDynamicSize } from 'scroll-strategies';
import { InfiniteScrollCustomEvent } from '@ionic/angular';
import { DynamicSizeCache, ScrollAdvancedItem } from '../../scroll-strategies.type';
import { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { FixVirtualScrollElementDirective } from 'scroll-header';
import { ScrollAdvancedItemComponent } from '../../components/scroll-advanced-item/scroll-advanced-item.component';
import { ScrollAdvancedCalcService } from '../../scroll-advanced-calc.service';
import { Subscription } from 'rxjs';

@Component({
selector: 'app-scroll-advanced',
templateUrl: './scroll-advanced.page.html',
styleUrls: ['./scroll-advanced.page.scss'],
standalone: true,
imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, IonBackButton, IonButtons],
imports: [
IonContent,
IonHeader,
IonTitle,
IonToolbar,
CommonModule,
FormsModule,
IonBackButton,
IonButtons,
IonButton,
IonIcon,
CdkDynamicSizeVirtualScroll,
CdkVirtualForOf,
CdkVirtualScrollViewport,
FixVirtualScrollElementDirective,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonList,
IonItem,
IonAvatar,
IonImg,
IonLabel,
ScrollAdvancedItemComponent,
],
})
export class ScrollAdvancedPage implements OnInit {
export class ScrollAdvancedPage implements OnInit, ViewDidEnter, ViewDidLeave {
readonly virtualScroll = viewChild(CdkVirtualScrollViewport);
readonly #enterSubscription$: Subscription[] = [];

readonly #scroll = inject(DynamicSizeVirtualScrollService);
readonly #calcService = inject(ScrollAdvancedCalcService);

readonly items = signal<ScrollAdvancedItem[]>([]);
readonly page = signal<number>(0);
readonly dynamicSize = computed<itemDynamicSize[]>(() => {
return this.#calcService.changeItemsToDynamicItemSize(this.items(), this.#calcService.cacheCalcDynamic(), this.virtualScroll());
});
readonly bindDynamicItemHeight = this.#scroll.getBindDynamicItemHeight(this.dynamicSize);

constructor() {}

ngOnInit() {}
ngOnInit() {
this.items.set(this.#createItems(100));
}

ionViewDidEnter() {
this.#enterSubscription$.push(
this.virtualScroll()!.scrolledIndexChange.subscribe(() => {
if (this.#calcService.beforeCacheCalcDynamicSize() !== this.#calcService.cacheCalcDynamic().length) {
this.#calcService.cacheCalcDynamic.update((cache) => [...cache]);
this.#calcService.beforeCacheCalcDynamicSize.set(this.#calcService.cacheCalcDynamic().length);
}
}),
);
}

ionViewDidLeave() {
this.#enterSubscription$.forEach((subscription) => subscription.unsubscribe());
}

async loadInfinite(event: InfiniteScrollCustomEvent) {
await new Promise((resolve) => setTimeout(resolve, 500));
this.items.update((items) => {
// You must create new array.
return [...items, ...this.#createItems(100)];
});
await event.target.complete();
}

#createItems(length: number): ScrollAdvancedItem[] {
this.page.update((page) => page + 1);
return Array.from({ length }).map((_, index) => {
const nameLength = Math.floor(Math.random() * (10 - 5)) + 5;
const descriptionLength = Math.floor(Math.random() * (400 - 20)) + 20;
return {
trackId: this.page() + '-' + index,
name: Array.from({ length: 100 })
.map(() => Math.random().toString(36))
.join()
.slice(-nameLength),
description: Array.from({ length: 100 })
.map(() => Math.random().toString(36))
.join()
.slice(-descriptionLength),
photo: `https://picsum.photos/200?random=${index}`,
};
});
}

trackByFn = (_: number, item: ScrollAdvancedItem) => item.trackId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
<ion-back-button defaultHref="/main/scroll-strategies"></ion-back-button>
</ion-buttons>
<ion-title>scroll-reverse</ion-title>
<ion-buttons slot="end">
<ion-button color="dark" href="https://github.com/rdlabo-team/ionic-angular-library/tree/main/projects/scroll-strategies#readme"
><ion-icon name="logo-github" slot="icon-only"></ion-icon
></ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonBackButton, IonButtons, IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone';
import { IonBackButton, IonButton, IonButtons, IonContent, IonHeader, IonIcon, IonTitle, IonToolbar } from '@ionic/angular/standalone';

@Component({
selector: 'app-scroll-reverse',
templateUrl: './scroll-reverse.page.html',
styleUrls: ['./scroll-reverse.page.scss'],
standalone: true,
imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, IonBackButton, IonButtons],
imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, IonBackButton, IonButtons, IonButton, IonIcon],
})
export class ScrollReversePage implements OnInit {
constructor() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class ScrollSimplePage implements OnInit {
await event.target.complete();
}

#createItems(length: number) {
#createItems(length: number): Item[] {
return Array.from({ length }).map((_, index) => {
return {
trackId: index,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

<ion-content>
<ion-list>
<ion-item detail="true" routerLink="/main/scroll-strategies/simple"><ion-label>Scroll Simple</ion-label></ion-item>
<ion-item detail="true" routerLink="/main/scroll-strategies/advanced"><ion-label>Scroll Advanced</ion-label></ion-item>
<ion-item detail="true" routerLink="/main/scroll-strategies/reverse"><ion-label>Scroll Reverse</ion-label></ion-item>
<ion-item button="true" detail="true" routerLink="/main/scroll-strategies/simple"><ion-label>Scroll Simple</ion-label></ion-item>
<ion-item button="true" detail="true" routerLink="/main/scroll-strategies/advanced"><ion-label>Scroll Advanced</ion-label></ion-item>
<ion-item button="true" detail="true" routerLink="/main/scroll-strategies/reverse"><ion-label>Scroll Reverse</ion-label></ion-item>
</ion-list>
</ion-content>
Original file line number Diff line number Diff line change
@@ -1,8 +1,42 @@
import { Injectable } from '@angular/core';
import { inject, Injectable, signal } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { DynamicSizeCache, ScrollAdvancedItem } from './scroll-strategies.type';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { itemDynamicSize } from 'scroll-strategies';

@Injectable({
providedIn: 'root',
})
export class ScrollAdvancedCalcService {
readonly cacheCalcDynamic = signal<DynamicSizeCache[]>([]);
readonly beforeCacheCalcDynamicSize = signal<number>(0);

constructor() {}

changeItemsToDynamicItemSize(
items: ScrollAdvancedItem[],
dynamicSizeCache: DynamicSizeCache[],
virtualScroll: CdkVirtualScrollViewport | undefined,
): itemDynamicSize[] {
if (virtualScroll === undefined) {
return [];
}
return (
items?.map((item, index) => {
const cacheSize = dynamicSizeCache.find((cache) => cache.trackId === item.trackId)?.itemSize;

/**
* 150 is approximate item size.
* This is important for the accuracy of the initial display.
*/
const itemSize = cacheSize || 150;

return {
itemSize: Math.ceil(itemSize),
trackId: item.trackId,
source: cacheSize !== undefined ? 'cache' : 'temporary',
};
}) || []
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type ScrollAdvancedItem = {
trackId: string;
name: string;
description: string;
photo: string;
};

export type DynamicSizeCache = { trackId: number | string; itemSize: number };
2 changes: 1 addition & 1 deletion projects/demo/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Demo of &#64;rdlabo/ionic-angular-photo-editor</title>
<title>Demo of ionic-angular-library</title>
<base href="/" />
<meta name="color-scheme" content="light dark" />
<meta
Expand Down
1 change: 1 addition & 0 deletions projects/demo/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ cdk-virtual-scroll-viewport {
height: 100%;
.cdk-virtual-scroll-content-wrapper {
padding-top: inherit;
width: 100%;
}
}

0 comments on commit a23563e

Please sign in to comment.