UNPKG

@tanstack/angular-virtual

Version:

Headless UI for virtualizing scrollable elements in Angular

135 lines (134 loc) 4.26 kB
import { assertInInjectionContext, inject, Injector, runInInjectionContext, computed, untracked, linkedSignal, afterRenderEffect, ApplicationRef, DestroyRef } from "@angular/core"; import { Virtualizer, elementScroll, observeElementOffset, observeElementRect, windowScroll, observeWindowOffset, observeWindowRect } from "@tanstack/virtual-core"; export * from "@tanstack/virtual-core"; import { signalProxy } from "./proxy.js"; function injectScheduleDomFlushViaAppRefTick() { const appRef = inject(ApplicationRef); const destroyRef = inject(DestroyRef); let hostDestroyed = false; destroyRef.onDestroy(() => { hostDestroyed = true; }); let domFlushQueued = false; return () => { if (domFlushQueued) return; domFlushQueued = true; queueMicrotask(() => { domFlushQueued = false; if (hostDestroyed) return; appRef.tick(); }); }; } function injectVirtualizerBase(options, extensions = {}) { let injector = extensions.injector; if (!injector) { assertInInjectionContext(injectVirtualizerBase); injector = inject(Injector); } return runInInjectionContext(injector, () => { const scheduleDomFlush = injectScheduleDomFlushViaAppRefTick(); const resolvedOptions = computed(() => { const { useApplicationRefTick = true, ..._options } = options(); return { ..._options, onChange: (instance, sync) => { var _a; reactiveVirtualizer.set(instance); if (useApplicationRefTick) { scheduleDomFlush(); } (_a = _options.onChange) == null ? void 0 : _a.call(_options, instance, sync); } }; }); const lazyVirtualizer = computed( () => new Virtualizer(untracked(resolvedOptions)) ); const reactiveVirtualizer = linkedSignal( () => { const virtualizer = lazyVirtualizer(); virtualizer.setOptions(resolvedOptions()); return virtualizer; }, { equal: () => false } ); afterRenderEffect((cleanup) => { cleanup(lazyVirtualizer()._didMount()); }); afterRenderEffect(() => { reactiveVirtualizer()._willUpdate(); }); return signalProxy( reactiveVirtualizer, // Methods that pass through: call on the instance without tracking the signal read [ "_didMount", "_willUpdate", "calculateRange", "getVirtualIndexes", "measure", "measureElement", "resizeItem", "scrollBy", "scrollToIndex", "scrollToOffset", "setOptions" ], // Attributes that will be transformed to signals [ "isScrolling", "measurementsCache", "options", "range", "scrollDirection", "scrollElement", "scrollOffset", "scrollRect" ], // Methods that will be tracked to the virtualizer signal [ "getOffsetForAlignment", "getOffsetForIndex", "getVirtualItemForOffset", "indexFromElement" ], // Zero-arg methods exposed as computed signals ["getTotalSize", "getVirtualItems"] // The rest is passed as is, and can be accessed or called before initialization ); }); } function injectVirtualizer(options) { return injectVirtualizerBase(() => { const _options = options(); return { observeElementRect, observeElementOffset, scrollToFn: elementScroll, getScrollElement: () => { const elementOrRef = _options.scrollElement; return (isElementRef(elementOrRef) ? elementOrRef.nativeElement : elementOrRef) ?? null; }, ..._options }; }); } function isElementRef(elementOrRef) { return elementOrRef != null && "nativeElement" in elementOrRef; } function injectWindowVirtualizer(options) { return injectVirtualizerBase(() => ({ getScrollElement: () => typeof document !== "undefined" ? window : null, observeElementRect: observeWindowRect, observeElementOffset: observeWindowOffset, scrollToFn: windowScroll, initialOffset: () => typeof document !== "undefined" ? window.scrollY : 0, ...options() })); } export { injectVirtualizer, injectWindowVirtualizer }; //# sourceMappingURL=index.js.map