Skip to content

Commit

Permalink
- Added support for Angular 16.
Browse files Browse the repository at this point in the history
- `VirtualScroll.scrollContainer` is now readonly and only refers to the host element.
  • Loading branch information
lVlyke committed Jun 16, 2023
1 parent d6e76ea commit e6b6d62
Show file tree
Hide file tree
Showing 5 changed files with 311 additions and 232 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ Component used to create a virtual scrolling container.
* **`gridList`** - (Optional) Whether or not the list is a grid list with multiple items per row. Defaults to `false`.
* **`itemWidth`** - (Optional) The width of each item being rendered, in pixels. Calculated automatically based on the width of the first item if not given.
* **`itemHeight`** - (Optional) The height of each item being rendered, in pixels. Calculated automatically based on the height of the first item if not given.
* **`scrollContainer`** - (Optional) The HTML element to use as the scroll container. Defaults to the host element.
* **`scrollDebounceMs`** - (Optional) How often to respond to scroll position changes, in milliseconds. Defaults to `50`.
* **`trackBy`** - (Optional) A [`TrackByFunction`](https://angular.io/api/core/TrackByFunction) used to compute the identity of items. Defaults to a function returning the item reference.
* **`viewCache`** - (Optional) Whether views can be cached. Can be a boolean or a number representing the maximum number of views to cache at a given time. Defaults to `false`.
Expand Down
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,29 @@
"test": "node ./scripts/test.js"
},
"peerDependencies": {
"@angular/common": "8.x.x - 15.x.x",
"@angular/core": "8.x.x - 15.x.x",
"@angular/common": "8.x.x - 16.x.x",
"@angular/core": "8.x.x - 16.x.x",
"@lithiumjs/angular": ">=7.0.0",
"rxjs": "6.x.x - 7.x.x"
},
"devDependencies": {
"@angular/common": "^15.2.6",
"@angular/compiler": "^15.2.6",
"@angular/compiler-cli": "^15.2.6",
"@angular/core": "^15.2.6",
"@lithiumjs/angular": "^7.2.2",
"@angular/common": "^16.1.1",
"@angular/compiler": "^16.1.1",
"@angular/compiler-cli": "^16.1.1",
"@angular/core": "^16.1.1",
"@lithiumjs/angular": "^7.3.0",
"@types/jasmine": "^4.3.1",
"detest-bdd": "^1.2.0",
"detest-bdd": "^1.2.1",
"fs-extra": "^11.0.1",
"istanbul": "^0.4.5",
"istanbul-cobertura-badger": "^1.3.1",
"jasmine": "^4.6.0",
"ng-packagr": "15.2.2",
"ng-packagr": "16.1.0",
"rxjs": "~7.4.0",
"source-map-support": "^0.5.12",
"tslib": "^2.3.0",
"typescript": "~4.8.4",
"zone.js": "~0.11.4"
"typescript": "~4.9.5",
"zone.js": "~0.13.0"
},
"dependencies": {}
}
64 changes: 32 additions & 32 deletions src/components/virtual-scroll/virtual-scroll.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
TrackByFunction
} from "@angular/core";
import { OnDestroy, AfterViewInit, AutoPush, DeclareState, ComponentState, ComponentStateRef, ManagedSubject } from "@lithiumjs/angular";
import { Observable, combineLatest, fromEvent, asyncScheduler, forkJoin, EMPTY } from "rxjs";
import { Observable, combineLatest, fromEvent, asyncScheduler, forkJoin, EMPTY, merge, of } from "rxjs";
import {
throttleTime,
tap,
Expand Down Expand Up @@ -122,10 +122,6 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {
@Input()
public trackBy: TrackByFunction<T> = TRACK_BY_IDENTITY_FN;

@Input()
@DeclareState()
public scrollContainer?: HTMLElement;

@Input()
public eventCapture = false;

Expand Down Expand Up @@ -155,6 +151,9 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {
@OnDestroy()
private readonly onDestroy$!: Observable<void>;

@DeclareState("scrollContainer")
private _scrollContainer: HTMLElement;

@DeclareState("renderedItems")
private _renderedItems: T[] = [];

Expand Down Expand Up @@ -188,20 +187,24 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {
) {
AutoPush.enable(this, cdRef);

this.scrollContainer = this._listElement = listElement;
this._scrollContainer = this._listElement = listElement;

// Update the current scroll position on scroll changes
const scrollSubscription = combineLatest(stateRef.getAll(
"scrollContainer",
"eventCapture"
)).pipe(
tap(([scrollContainer]) => this.applyScrollContainerStyles(scrollContainer === listElement)),
switchMap(([scrollContainer, capture]) => fromEvent<MouseEvent>(scrollContainer!, "scroll", { capture })),
map((scrollEvent) => ({
x: (scrollEvent.target as HTMLElement).scrollLeft,
y: (scrollEvent.target as HTMLElement).scrollTop
})),
startWith({ x: 0, y: 0 }),
switchMap(([scrollContainer, capture]) => merge(
fromEvent<MouseEvent>(scrollContainer, "scroll", { capture }),
this.scrollContainerResize(scrollContainer),
of({ x: 0, y: 0 })
).pipe(
map(() => ({
x: scrollContainer.scrollLeft,
y: scrollContainer.scrollTop
})),
)),
distinctUntilChanged((prev, cur) => prev.x === cur.x && prev.y === cur.y)
).subscribe(scrollPosition => {
this._lastScrollOffset.x = scrollPosition.x - this._scrollPosition.x;
Expand Down Expand Up @@ -298,9 +301,8 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {
this.afterViewInit$.pipe(
switchMapTo(this.scrollStateChange),
// Skip updates if we're ignoring scroll updates or item info isn't defined
filter(([, , , itemWidth, itemHeight]) => !this.renderingViews && ((!!itemWidth || !this.gridList) && !!itemHeight)),
filter(([, , itemWidth, itemHeight]) => !this.renderingViews && ((!!itemWidth || !this.gridList) && !!itemHeight)),
).subscribe(([
,
scrollPosition,
items,
itemWidth,
Expand All @@ -313,14 +315,13 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {
const renderedBounds: VirtualScroll.Rect = {
left: scrollPosition.x,
top: scrollPosition.y,
right: scrollPosition.x + scrollContainer!.clientWidth,
bottom: scrollPosition.y + scrollContainer!.clientHeight
right: scrollPosition.x + scrollContainer.clientWidth,
bottom: scrollPosition.y + scrollContainer.clientHeight
};
const bufferLengthPx = (scrollContainer!.clientHeight) * bufferLength;
const bufferLengthPx = (scrollContainer.clientHeight) * bufferLength;

// Calculate the number of rendered items per row
const containerWidth = this._listElement.clientWidth || scrollContainer!.clientWidth;
const itemsPerRow = gridList ? Math.floor(containerWidth / itemWidth!) : 1;
const itemsPerRow = gridList ? Math.floor(scrollContainer.clientWidth / itemWidth!) : 1;
const virtualScrollHeight = items.length * itemHeight! / itemsPerRow;

// Adjust the bounds by the buffer length and clamp to the edges of the container
Expand All @@ -342,7 +343,7 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {

// Calculate the virtual scroll space before/after the rendered items
const spaceBeforePx = Math.floor(this._minIndex / itemsPerRow) * itemHeight!;
const spaceAfterPx = Math.floor((items.length - this._maxIndex) / itemsPerRow) * itemHeight!;
const spaceAfterPx = Math.floor((items.length - (this._maxIndex + 1)) / itemsPerRow) * itemHeight!;

// Update the virtual spacers in the DOM
renderer.setStyle(this._virtualSpacerBefore.nativeElement, "height", `${spaceBeforePx}px`);
Expand All @@ -364,6 +365,10 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {
).subscribe(refItem => this.itemHeight = this.calculateItemHeight(refItem));
}

public get scrollContainer(): HTMLElement {
return this._scrollContainer;
}

public get minIndex(): number {
return this._minIndex;
}
Expand Down Expand Up @@ -436,15 +441,12 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {
this.updateViewInfo(this._renderedViews, view);
}

private get scrollContainerResize(): Observable<unknown> {
return this.stateRef.get("scrollContainer").pipe(
filter(c => !!c),
switchMap((scrollContainer) => new Observable((observer) => {
const res = new ResizeObserver(() => this.zone.run(() => observer.next()));
res.observe(scrollContainer!);
this.onDestroy$.subscribe(() => (res.disconnect(), observer.complete()));
}))
);
private scrollContainerResize(scrollContainer: HTMLElement): Observable<unknown> {
return new Observable((observer) => {
const res = new ResizeObserver(() => this.zone.run(() => observer.next()));
res.observe(scrollContainer);
this.onDestroy$.subscribe(() => (res.disconnect(), observer.complete()));
});
}

private get scrollDebounce(): Observable<VirtualScrollState.Point> {
Expand All @@ -459,8 +461,6 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {

private get scrollStateChange() {
return combineLatest([
// Listen for resizes on the scroll container
this.scrollContainerResize,
// Listen for scroll position changes
this.scrollDebounce,
// Listen for list state changes that affect rendering
Expand Down Expand Up @@ -488,7 +488,7 @@ export class VirtualScroll<T> implements VirtualScrollState<T> {
this._renderedItems = [items[0]];
}
}),
map(([, scrollContainer]) => scrollContainer!.querySelector<HTMLElement>(":scope > :not(.virtual-spacer)")),
map(([, scrollContainer]) => scrollContainer.querySelector<HTMLElement>(":scope > :not(.virtual-spacer)")),
filter((refItem): refItem is HTMLElement => !!refItem)
);
}
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"extends": "./node_modules/ng-packagr/lib/ts/conf/tsconfig.ngc.json",
"compilerOptions": {
// Overrides:
"strict": true
"strict": true,
"useDefineForClassFields": false
},
"files": [],
"include": [
Expand Down
Loading

0 comments on commit e6b6d62

Please sign in to comment.