{"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["import {\n  ApplicationRef,\n  DestroyRef,\n  Injector,\n  afterRenderEffect,\n  assertInInjectionContext,\n  computed,\n  inject,\n  linkedSignal,\n  runInInjectionContext,\n  untracked,\n} from '@angular/core'\nimport {\n  Virtualizer,\n  elementScroll,\n  observeElementOffset,\n  observeElementRect,\n  observeWindowOffset,\n  observeWindowRect,\n  windowScroll,\n} from '@tanstack/virtual-core'\nimport { signalProxy } from './proxy'\nimport type { ElementRef } from '@angular/core'\nimport type { PartialKeys, VirtualizerOptions } from '@tanstack/virtual-core'\nimport type { AngularVirtualizer } from './types'\n\nexport * from '@tanstack/virtual-core'\nexport * from './types'\n\nexport type AngularVirtualizerOptions<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n> = VirtualizerOptions<TScrollElement, TItemElement> & {\n  /**\n   * Whether to flush the DOM using `ApplicationRef.tick()`\n   * @default true\n   * */\n  useApplicationRefTick?: boolean\n}\n\nexport type AngularExtensionOptions = {\n  /**\n   * The injector to use for the virtualizer.\n   * @default inject(Injector)\n   */\n  injector?: Injector\n}\n\n// Flush CD after virtual-core updates so template bindings hit the DOM\n// before the next frame's scroll reconciliation reads `scrollHeight`.\nfunction injectScheduleDomFlushViaAppRefTick() {\n  const appRef = inject(ApplicationRef)\n  const destroyRef = inject(DestroyRef)\n  let hostDestroyed = false\n  destroyRef.onDestroy(() => {\n    hostDestroyed = true\n  })\n  let domFlushQueued = false\n\n  return () => {\n    if (domFlushQueued) return\n    domFlushQueued = true\n    queueMicrotask(() => {\n      domFlushQueued = false\n      if (hostDestroyed) return\n      appRef.tick()\n    })\n  }\n}\n\nfunction injectVirtualizerBase<\n  TScrollElement extends Element | Window,\n  TItemElement extends Element,\n>(\n  options: () => AngularVirtualizerOptions<TScrollElement, TItemElement>,\n  extensions: AngularExtensionOptions = {},\n) {\n  let injector = extensions.injector\n  if (!injector) {\n    assertInInjectionContext(injectVirtualizerBase)\n    injector = inject(Injector)\n  }\n\n  return runInInjectionContext(injector, () => {\n    const scheduleDomFlush = injectScheduleDomFlushViaAppRefTick()\n\n    const resolvedOptions = computed<\n      VirtualizerOptions<TScrollElement, TItemElement>\n    >(() => {\n      const { useApplicationRefTick = true, ..._options } = options()\n      return {\n        ..._options,\n        onChange: (instance, sync) => {\n          reactiveVirtualizer.set(instance)\n          if (useApplicationRefTick) {\n            scheduleDomFlush()\n          }\n          _options.onChange?.(instance, sync)\n        },\n      }\n    })\n\n    // Computed here is used to lazily initialize the Virtualizer instance,\n    // allowing it to be created after input/model signals are initialized.\n    // Options are untracked to maintain a single instance of the Virtualizer.\n    const lazyVirtualizer = computed(\n      () => new Virtualizer(untracked(resolvedOptions)),\n    )\n\n    // The reference in onChange is safe since computed signals are not evaluated eagerly.\n    const reactiveVirtualizer = linkedSignal(\n      () => {\n        const virtualizer = lazyVirtualizer()\n        // If setOptions does not call onChange, it's safe to call it here\n        virtualizer.setOptions(resolvedOptions())\n        return virtualizer\n      },\n      { equal: () => false },\n    )\n\n    afterRenderEffect((cleanup) => {\n      cleanup(lazyVirtualizer()._didMount())\n    })\n\n    afterRenderEffect(() => {\n      reactiveVirtualizer()._willUpdate()\n    })\n\n    return signalProxy(\n      reactiveVirtualizer,\n      // Methods that pass through: call on the instance without tracking the signal read\n      [\n        '_didMount',\n        '_willUpdate',\n        'calculateRange',\n        'getVirtualIndexes',\n        'measure',\n        'measureElement',\n        'resizeItem',\n        'scrollBy',\n        'scrollToIndex',\n        'scrollToOffset',\n        'setOptions',\n      ],\n      // Attributes that will be transformed to signals\n      [\n        'isScrolling',\n        'measurementsCache',\n        'options',\n        'range',\n        'scrollDirection',\n        'scrollElement',\n        'scrollOffset',\n        'scrollRect',\n      ],\n      // Methods that will be tracked to the virtualizer signal\n      [\n        'getOffsetForAlignment',\n        'getOffsetForIndex',\n        'getVirtualItemForOffset',\n        'indexFromElement',\n      ],\n      // Zero-arg methods exposed as computed signals\n      ['getTotalSize', 'getVirtualItems'],\n      // The rest is passed as is, and can be accessed or called before initialization\n    ) as unknown as AngularVirtualizer<TScrollElement, TItemElement>\n  })\n}\n\nexport function injectVirtualizer<\n  TScrollElement extends Element,\n  TItemElement extends Element,\n>(\n  options: () => PartialKeys<\n    Omit<\n      AngularVirtualizerOptions<TScrollElement, TItemElement>,\n      'getScrollElement'\n    >,\n    'observeElementRect' | 'observeElementOffset' | 'scrollToFn'\n  > & {\n    scrollElement: ElementRef<TScrollElement> | TScrollElement | undefined\n  },\n): AngularVirtualizer<TScrollElement, TItemElement> {\n  return injectVirtualizerBase<TScrollElement, TItemElement>(() => {\n    const _options = options()\n    return {\n      observeElementRect: observeElementRect,\n      observeElementOffset: observeElementOffset,\n      scrollToFn: elementScroll,\n      getScrollElement: () => {\n        const elementOrRef = _options.scrollElement\n        return (\n          (isElementRef(elementOrRef)\n            ? elementOrRef.nativeElement\n            : elementOrRef) ?? null\n        )\n      },\n      ..._options,\n    }\n  })\n}\n\nfunction isElementRef<T extends Element>(\n  elementOrRef: ElementRef<T> | T | undefined,\n): elementOrRef is ElementRef<T> {\n  return elementOrRef != null && 'nativeElement' in elementOrRef\n}\n\nexport function injectWindowVirtualizer<TItemElement extends Element>(\n  options: () => PartialKeys<\n    AngularVirtualizerOptions<Window, TItemElement>,\n    | 'getScrollElement'\n    | 'observeElementRect'\n    | 'observeElementOffset'\n    | 'scrollToFn'\n  >,\n): AngularVirtualizer<Window, TItemElement> {\n  return injectVirtualizerBase<Window, TItemElement>(() => ({\n    getScrollElement: () => (typeof document !== 'undefined' ? window : null),\n    observeElementRect: observeWindowRect,\n    observeElementOffset: observeWindowOffset,\n    scrollToFn: windowScroll,\n    initialOffset: () => (typeof document !== 'undefined' ? window.scrollY : 0),\n    ...options(),\n  }))\n}\n"],"names":["inject","ApplicationRef","DestroyRef","assertInInjectionContext","Injector","runInInjectionContext","computed","Virtualizer","untracked","linkedSignal","afterRenderEffect","signalProxy","observeElementRect","observeElementOffset","elementScroll","observeWindowRect","observeWindowOffset","windowScroll"],"mappings":"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"}