Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dcd98b0

Browse files
committed
feat(template): introduce experimental virtual-scrolling package
1 parent a1b6982 commit dcd98b0

29 files changed

+4034
-1
lines changed

libs/template/experimental/virtual-scrolling/README.md

Lines changed: 564 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "../../../../node_modules/ng-packagr/package.schema.json",
3+
"lib": {
4+
"entryFile": "src/index.ts",
5+
"flatModuleFile": "template-experimental-virtual-scrolling"
6+
}
7+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export {
2+
ListRange,
3+
RxVirtualForViewContext,
4+
RxVirtualScrollStrategy,
5+
RxVirtualViewRepeater,
6+
} from './model';
7+
export * from './scroll-strategies/autosized-virtual-scroll-strategy';
8+
export * from './scroll-strategies/dynamic-size-virtual-scroll-strategy';
9+
export * from './scroll-strategies/fixed-size-virtual-scroll-strategy';
10+
export * from './virtual-for.directive';
11+
export {
12+
RX_VIRTUAL_SCROLL_DEFAULT_OPTIONS,
13+
RX_VIRTUAL_SCROLL_DEFAULT_OPTIONS_FACTORY,
14+
RxVirtualScrollDefaultOptions,
15+
} from './virtual-scroll.config';
16+
export * from './virtual-scroll-viewport.component';
17+
export * from './virtual-scrolling.module';
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import {
2+
ChangeDetectorRef,
3+
Directive,
4+
EmbeddedViewRef,
5+
ErrorHandler,
6+
NgIterable,
7+
NgZone,
8+
Output,
9+
TemplateRef,
10+
TrackByFunction,
11+
ViewContainerRef,
12+
} from '@angular/core';
13+
import { RxStrategies } from '@rx-angular/cdk/render-strategies';
14+
import { RxDefaultListViewContext } from '@rx-angular/cdk/template';
15+
import { Observable, Subject } from 'rxjs';
16+
17+
type CreateViewContext<T, C, U> = (value: T, computedContext: U) => C;
18+
19+
type UpdateViewContext<T, C, U> = (
20+
value: T,
21+
view: EmbeddedViewRef<C>,
22+
computedContext?: U
23+
) => void;
24+
25+
export interface TemplateSettings<T, C, U> {
26+
viewContainerRef: ViewContainerRef;
27+
templateRef: TemplateRef<C>;
28+
createViewContext: CreateViewContext<T, C, U>;
29+
updateViewContext: UpdateViewContext<T, C, U>;
30+
viewCacheSize: number;
31+
}
32+
33+
export interface RenderSettings {
34+
cdRef: ChangeDetectorRef;
35+
parent: boolean;
36+
patchZone?: NgZone;
37+
strategies: RxStrategies<string>;
38+
defaultStrategyName: string;
39+
errorHandler?: ErrorHandler;
40+
}
41+
42+
export const enum ListTemplateChangeType {
43+
insert,
44+
remove,
45+
move,
46+
update,
47+
context,
48+
}
49+
// [value, index, oldIndex?]
50+
export type ListTemplateChangePayload<T> = [T, number?, number?];
51+
export type ListTemplateChange<T = any> = [
52+
ListTemplateChangeType,
53+
ListTemplateChangePayload<T>
54+
];
55+
export type ListTemplateChanges<T = any> = [
56+
ListTemplateChange<T>[], // changes to apply
57+
boolean // notify parent
58+
];
59+
60+
export interface ListRange {
61+
start: number;
62+
end: number;
63+
}
64+
65+
/**
66+
* @Directive RxVirtualScrollStrategy
67+
*
68+
* @description
69+
* Abstract implementation for the actual implementations of the ScrollStrategies
70+
* being consumed by `*rxVirtualFor` and `rx-virtual-scroll-viewport`.
71+
*
72+
* This is one of the core parts for the virtual scrolling implementation. It has
73+
* to determine the `ListRange` being rendered to the DOM as well as managing
74+
* the layouting task for the `*rxVirtualFor` directive.
75+
*
76+
* @docsCategory RxVirtualFor
77+
* @docsPage RxVirtualFor
78+
* @publicApi
79+
*/
80+
@Directive()
81+
export abstract class RxVirtualScrollStrategy<
82+
T,
83+
U extends NgIterable<T> = NgIterable<T>
84+
> {
85+
/** Emits when the index of the first element visible in the viewport changes. */
86+
/** @internal */
87+
abstract scrolledIndex$: Observable<number>;
88+
/** @internal */
89+
abstract renderedRange$: Observable<ListRange>;
90+
/** @internal */
91+
abstract contentSize$: Observable<number>;
92+
93+
/**
94+
* @description
95+
*
96+
* Emits whenever an update to a single view was rendered
97+
*/
98+
@Output() readonly viewRenderCallback = new Subject<{
99+
view: EmbeddedViewRef<RxVirtualForViewContext<T, U>>;
100+
item: T;
101+
index: number;
102+
}>();
103+
104+
/** @internal */
105+
private nodeIndex?: number;
106+
107+
/** @internal */
108+
protected getElement(
109+
view: EmbeddedViewRef<RxVirtualForViewContext<T, U>>
110+
): HTMLElement {
111+
if (this.nodeIndex !== undefined) {
112+
return view.rootNodes[this.nodeIndex];
113+
}
114+
const rootNode = view.rootNodes[0];
115+
this.nodeIndex = rootNode instanceof HTMLElement ? 0 : 1;
116+
return view.rootNodes[this.nodeIndex] as HTMLElement;
117+
}
118+
119+
/**
120+
* Attaches this scroll strategy to a viewport.
121+
* @param viewport The viewport to attach this strategy to.
122+
* @param viewRepeater The viewRepeater attached to the viewport.
123+
*/
124+
abstract attach(
125+
viewport: RxVirtualScrollViewport,
126+
viewRepeater: RxVirtualViewRepeater<any>
127+
): void;
128+
129+
/** Detaches this scroll strategy from the currently attached viewport. */
130+
abstract detach(): void;
131+
132+
/**
133+
* Scroll to the offset for the given index.
134+
* @param index The index of the element to scroll to.
135+
* @param behavior The ScrollBehavior to use when scrolling.
136+
*/
137+
abstract scrollToIndex(index: number, behavior?: ScrollBehavior): void;
138+
}
139+
140+
/** @internal */
141+
@Directive()
142+
export abstract class RxVirtualScrollViewport {
143+
abstract rendered$: Observable<any>;
144+
abstract viewRange: Observable<ListRange>;
145+
abstract elementScrolled$: Observable<void>;
146+
abstract containerRect$: Observable<{ height: number; width: number }>;
147+
abstract getScrollTop(): number;
148+
abstract scrollTo(scrollTo: number, behavior?: ScrollBehavior): void;
149+
}
150+
151+
/** @internal */
152+
@Directive()
153+
export abstract class RxVirtualViewRepeater<
154+
T,
155+
U extends NgIterable<T> = NgIterable<T>
156+
> {
157+
abstract values$: Observable<U | null | undefined>;
158+
abstract rendered$: Observable<any>;
159+
abstract viewsRendered$: Observable<EmbeddedViewRef<any>[]>;
160+
abstract viewRendered$: Observable<{
161+
view: EmbeddedViewRef<RxVirtualForViewContext<T, U>>;
162+
index: number;
163+
item: T;
164+
}>;
165+
abstract renderingStart$: Observable<void>;
166+
_trackBy: TrackByFunction<T> = (i, a) => a;
167+
}
168+
169+
/** @internal */
170+
export class RxVirtualForViewContext<
171+
T,
172+
U extends NgIterable<T> = NgIterable<T>,
173+
C extends { count: number; index: number } = { count: number; index: number },
174+
K = keyof T
175+
> extends RxDefaultListViewContext<T, U, K> {
176+
constructor(item: T, public rxVirtualForOf: U, customProps?: C) {
177+
super(item, customProps);
178+
}
179+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Observable } from 'rxjs';
2+
3+
export function observeElementSize(
4+
element: Element,
5+
config?: {
6+
options?: ResizeObserverOptions;
7+
}
8+
): Observable<DOMRectReadOnly>;
9+
export function observeElementSize<T>(
10+
element: Element,
11+
config?: {
12+
options?: ResizeObserverOptions;
13+
extract: (entries: ResizeObserverEntry[]) => T;
14+
}
15+
): Observable<T>;
16+
export function observeElementSize<T>(
17+
element: Element,
18+
config?: {
19+
options?: ResizeObserverOptions;
20+
extract?: (entries: ResizeObserverEntry[]) => T;
21+
}
22+
): Observable<T | DOMRectReadOnly> {
23+
const extractProp: (entries: ResizeObserverEntry[]) => T | DOMRectReadOnly =
24+
config?.extract ?? ((entries) => entries[0].contentRect);
25+
return new Observable<T | DOMRectReadOnly>((subscriber) => {
26+
const observer = new ResizeObserver((entries) => {
27+
subscriber.next(extractProp(entries));
28+
});
29+
observer.observe(element, config?.options);
30+
return () => observer.disconnect();
31+
});
32+
}

0 commit comments

Comments
 (0)