Skip to content

⬅️ Back to Table of Contents

📄 useVirtualList

📊 Analysis Summary

Metric Count
🔧 Functions 12
📦 Imports 10
📊 Variables & Constants 3
🟢 Vue Composition API 6
📐 Interfaces 9
📑 Type Aliases 7

📚 Table of Contents

🛠️ File Location:

📂 packages/core/useVirtualList/index.ts

📦 Imports

Name Source
ComputedRef vue
MaybeRef vue
Ref vue
ShallowRef vue
StyleValue vue
computed vue
deepRef vue
shallowRef vue
watch vue
useElementSize ../useElementSize

Variables & Constants

Name Type Kind Value Exported
scrollToDictionaryForElemen... { readonly horizontal: "scrollLeft"; ... const { horizontal: 'scrollLeft', vertical: 'scrollTop', } as const
scrollToDictionaryForElemen... { readonly horizontal: "left"; readon... const { horizontal: 'left', vertical: 'top', } as const
defaultScrollToOptions UseVirtualListScrollToOptions const { behavior: 'auto', block: 'start', inline: 'nearest' }

Vue Composition API

Name Type Reactive Variables Composables
watch watch none none
computed computed none none
computed computed none none
computed computed none none
computed computed none none
computed computed none none

Functions

useVirtualList(list: MaybeRef<readonly T[]>, options: UseVirtualListOptions): UseVirtualListReturn<T>

Please consider using vue-virtual-scroller if you are looking for more features.

Raw JSDoc
/**
 * Please consider using [`vue-virtual-scroller`](https://github.com/Akryum/vue-virtual-scroller) if you are looking for more features.
 */

Calls:

  • useVerticalVirtualList
  • useHorizontalVirtualList
  • calculateRange
Code
export function useVirtualList<T = any>(list: MaybeRef<readonly T[]>, options: UseVirtualListOptions): UseVirtualListReturn<T> {
  const { containerStyle, wrapperProps, scrollTo, calculateRange, currentList, containerRef } = 'itemHeight' in options
    ? useVerticalVirtualList(options, list)
    : useHorizontalVirtualList(options, list)

  return {
    list: currentList,
    scrollTo,
    containerProps: {
      ref: containerRef,
      onScroll: () => {
        calculateRange()
      },
      style: containerStyle,
    },
    wrapperProps,
  }
}

useVirtualListResources(list: MaybeRef<readonly T[]>): UseVirtualListResources<T>

Parameters:

  • list MaybeRef<readonly T[]>

Returns: UseVirtualListResources<T>

Calls:

  • shallowRef (from vue)
  • useElementSize (from ../useElementSize)
  • deepRef (from vue)
Code
function useVirtualListResources<T>(list: MaybeRef<readonly T[]>): UseVirtualListResources<T> {
  const containerRef = shallowRef<HTMLElement | null>(null)
  const size = useElementSize(containerRef)

  const currentList: Ref<UseVirtualListItem<T>[]> = deepRef([])
  const source = shallowRef(list)

  const state: Ref<{ start: number, end: number }> = deepRef({ start: 0, end: 10 })

  return { state, source, currentList, size, containerRef }
}

createGetViewCapacity(state: UseVirtualListResources<T>['state'], source: UseVirtualListResources<T>['source'], itemSize: UseVirtualListItemSize): (containerSize: number) => number

Parameters:

  • state UseVirtualListResources<T>['state']
  • source UseVirtualListResources<T>['source']
  • itemSize UseVirtualListItemSize

Returns: (containerSize: number) => number

Calls:

  • Math.ceil
  • itemSize
Code
function createGetViewCapacity<T>(state: UseVirtualListResources<T>['state'], source: UseVirtualListResources<T>['source'], itemSize: UseVirtualListItemSize) {
  return (containerSize: number) => {
    if (typeof itemSize === 'number')
      return Math.ceil(containerSize / itemSize)

    const { start = 0 } = state.value
    let sum = 0
    let capacity = 0
    for (let i = start; i < source.value.length; i++) {
      const size = itemSize(i)
      sum += size
      capacity = i
      if (sum > containerSize)
        break
    }
    return capacity - start
  }
}

createGetOffset(source: UseVirtualListResources<T>['source'], itemSize: UseVirtualListItemSize): (scrollDirection: number) => number

Parameters:

  • source UseVirtualListResources<T>['source']
  • itemSize UseVirtualListItemSize

Returns: (scrollDirection: number) => number

Calls:

  • Math.floor
  • itemSize
Code
function createGetOffset<T>(source: UseVirtualListResources<T>['source'], itemSize: UseVirtualListItemSize) {
  return (scrollDirection: number) => {
    if (typeof itemSize === 'number')
      return Math.floor(scrollDirection / itemSize) + 1

    let sum = 0
    let offset = 0

    for (let i = 0; i < source.value.length; i++) {
      const size = itemSize(i)
      sum += size
      if (sum >= scrollDirection) {
        offset = i
        break
      }
    }
    return offset + 1
  }
}

createCalculateRange(…): () => void

Parameters:

  • type 'horizontal' | 'vertical'
  • overscan number
  • getOffset ReturnType<typeof createGetOffset>
  • getViewCapacity ReturnType<typeof createGetViewCapacity>
  • { containerRef, state, currentList, source } UseVirtualListResources<T>

Returns: () => void

Calls:

  • getOffset
  • getViewCapacity
  • source.value .slice(state.value.start, state.value.end) .map
Code
function createCalculateRange<T>(
  type: 'horizontal' | 'vertical',
  overscan: number,
  getOffset: ReturnType<typeof createGetOffset>,
  getViewCapacity: ReturnType<typeof createGetViewCapacity>,
  { containerRef, state, currentList, source }: UseVirtualListResources<T>,
) {
  return () => {
    const element = containerRef.value
    if (element) {
      const offset = getOffset(type === 'vertical' ? element.scrollTop : element.scrollLeft)
      const viewCapacity = getViewCapacity(type === 'vertical' ? element.clientHeight : element.clientWidth)

      const from = offset - overscan
      const to = offset + viewCapacity + overscan
      state.value = {
        start: from < 0 ? 0 : from,
        end: to > source.value.length
          ? source.value.length
          : to,
      }
      currentList.value = source.value
        .slice(state.value.start, state.value.end)
        .map((ele, index) => ({
          data: ele,
          index: index + state.value.start,
        }))
    }
  }
}

createGetDistance(itemSize: UseVirtualListItemSize, source: UseVirtualListResources<T>['source']): (index: number) => any

Parameters:

  • itemSize UseVirtualListItemSize
  • source UseVirtualListResources<T>['source']

Returns: (index: number) => any

Calls:

  • source.value .slice(0, index) .reduce
  • itemSize
Code
function createGetDistance<T>(itemSize: UseVirtualListItemSize, source: UseVirtualListResources<T>['source']) {
  return (index: number) => {
    if (typeof itemSize === 'number') {
      const size = index * itemSize
      return size
    }

    const size = source.value
      .slice(0, index)
      .reduce((sum, _, i) => sum + itemSize(i), 0)

    return size
  }
}

useWatchForSizes(size: UseVirtualElementSizes, listRef: Ref<readonly T[]>, totalSize: ComputedRef<number>, containerRef: Ref<HTMLElement | null>, calculateRange: () => void): void

Parameters:

  • size UseVirtualElementSizes
  • listRef Ref<readonly T[]>
  • totalSize ComputedRef<number>
  • containerRef Ref<HTMLElement | null>
  • calculateRange () => void

Returns: void

Calls:

  • watch (from vue)
  • calculateRange
Code
function useWatchForSizes<T>(size: UseVirtualElementSizes, listRef: Ref<readonly T[]>, totalSize: ComputedRef<number>, containerRef: Ref<HTMLElement | null>, calculateRange: () => void) {
  watch([size.width, size.height, listRef, totalSize, containerRef], () => {
    calculateRange()
  })
}

createComputedTotalSize(itemSize: UseVirtualListItemSize, source: UseVirtualListResources<T>['source']): any

Parameters:

  • itemSize UseVirtualListItemSize
  • source UseVirtualListResources<T>['source']

Returns: any

Calls:

  • computed (from vue)
  • source.value.reduce
  • itemSize
Code
function createComputedTotalSize<T>(itemSize: UseVirtualListItemSize, source: UseVirtualListResources<T>['source']) {
  return computed(() => {
    if (typeof itemSize === 'number')
      return source.value.length * itemSize

    return source.value.reduce((sum, _, index) => sum + itemSize(index), 0)
  })
}

createScrollTo(…): (index: number, options?: UseVirtualListScrollToOptions) =>…

Parameters:

  • type 'horizontal' | 'vertical'
  • calculateRange () => void
  • getDistance ReturnType<typeof createGetDistance>
  • containerRef UseVirtualListResources<T>['containerRef']
  • itemSize UseVirtualListItemSize

Returns: (index: number, options?: UseVirtualListScrollToOptions) => void

Calls:

  • itemSize
  • getDistance
  • containerRef.value.scrollTo
  • calculateRange
Code
function createScrollTo<T>(
  type: 'horizontal' | 'vertical',
  calculateRange: () => void,
  getDistance: ReturnType<typeof createGetDistance>,
  containerRef: UseVirtualListResources<T>['containerRef'],
  itemSize: UseVirtualListItemSize,
) {
  return (index: number, options: UseVirtualListScrollToOptions = defaultScrollToOptions) => {
    if (!containerRef.value)
      return

    options = { ...defaultScrollToOptions, ...options }
    let offset = 0
    const axisToCheck = options[type === 'horizontal' ? 'inline' : 'block']
    if (axisToCheck) {
      const containerSize = type === 'horizontal' ? containerRef.value.clientWidth : containerRef.value.clientHeight
      const fullItemSize = typeof itemSize === 'number' ? itemSize : itemSize(index)

      if (axisToCheck === 'center') {
        offset = (containerSize / 2) - (fullItemSize / 2)
      }
      else if (axisToCheck === 'end') {
        offset = containerSize - fullItemSize
      }
      else if (axisToCheck === 'nearest') {
        const containerScrollPosition = containerRef.value[scrollToDictionaryForElementScrollKey[type]]
        if (getDistance(index) > containerScrollPosition + (containerSize / 2)) {
          offset = containerSize - fullItemSize
        }
      }
    }

    containerRef.value.scrollTo({
      [scrollToDictionaryForElementScrollToKey[type]]: getDistance(index) - offset,
      behavior: options.behavior,
    })

    calculateRange()
  }
}

useHorizontalVirtualList(options: UseHorizontalVirtualListOptions, list: MaybeRef<readonly T[]>): { scrollTo: (index: number, options?: UseVirtualListScrollT…

Parameters:

  • options UseHorizontalVirtualListOptions
  • list MaybeRef<readonly T[]>

Returns: { scrollTo: (index: number, options?: UseVirtualListScrollToOptions) => void; calculateRange: () => void; wrapperProps: any; containerStyle: StyleValue; currentList: Ref<UseVirtualListArray<T>>; containerRef: Ref<HTMLElement>; }

Calls:

  • useVirtualListResources
  • createGetViewCapacity
  • createGetOffset
  • createCalculateRange
  • createGetDistance
  • computed (from vue)
  • getDistanceLeft
  • createComputedTotalSize
  • useWatchForSizes
  • createScrollTo
Code
function useHorizontalVirtualList<T>(options: UseHorizontalVirtualListOptions, list: MaybeRef<readonly T[]>) {
  const resources = useVirtualListResources(list)
  const { state, source, currentList, size, containerRef } = resources
  const containerStyle: StyleValue = { overflowX: 'auto' }

  const { itemWidth, overscan = 5 } = options

  const getViewCapacity = createGetViewCapacity(state, source, itemWidth)

  const getOffset = createGetOffset(source, itemWidth)

  const calculateRange = createCalculateRange('horizontal', overscan, getOffset, getViewCapacity, resources)

  const getDistanceLeft = createGetDistance(itemWidth, source)

  const offsetLeft = computed(() => getDistanceLeft(state.value.start))

  const totalWidth = createComputedTotalSize(itemWidth, source)

  useWatchForSizes(size, source, totalWidth, containerRef, calculateRange)

  const scrollTo = createScrollTo('horizontal', calculateRange, getDistanceLeft, containerRef, itemWidth)

  const wrapperProps = computed(() => {
    return {
      style: {
        height: '100%',
        width: `${totalWidth.value - offsetLeft.value}px`,
        marginLeft: `${offsetLeft.value}px`,
        display: 'flex',
      },
    }
  })

  return {
    scrollTo,
    calculateRange,
    wrapperProps,
    containerStyle,
    currentList,
    containerRef,
  }
}

useVerticalVirtualList(options: UseVerticalVirtualListOptions, list: MaybeRef<readonly T[]>): { calculateRange: () => void; scrollTo: (index: number, opt…

Parameters:

  • options UseVerticalVirtualListOptions
  • list MaybeRef<readonly T[]>

Returns: { calculateRange: () => void; scrollTo: (index: number, options?: UseVirtualListScrollToOptions) => void; containerStyle: StyleValue; wrapperProps: any; currentList: Ref<UseVirtualListArray<T>>; containerRef: Ref<HTMLElement>; }

Calls:

  • useVirtualListResources
  • createGetViewCapacity
  • createGetOffset
  • createCalculateRange
  • createGetDistance
  • computed (from vue)
  • getDistanceTop
  • createComputedTotalSize
  • useWatchForSizes
  • createScrollTo
Code
function useVerticalVirtualList<T>(options: UseVerticalVirtualListOptions, list: MaybeRef<readonly T[]>) {
  const resources = useVirtualListResources(list)

  const { state, source, currentList, size, containerRef } = resources

  const containerStyle: StyleValue = { overflowY: 'auto' }

  const { itemHeight, overscan = 5 } = options

  const getViewCapacity = createGetViewCapacity(state, source, itemHeight)

  const getOffset = createGetOffset(source, itemHeight)

  const calculateRange = createCalculateRange('vertical', overscan, getOffset, getViewCapacity, resources)

  const getDistanceTop = createGetDistance(itemHeight, source)

  const offsetTop = computed(() => getDistanceTop(state.value.start))

  const totalHeight = createComputedTotalSize(itemHeight, source)

  useWatchForSizes(size, source, totalHeight, containerRef, calculateRange)

  const scrollTo = createScrollTo('vertical', calculateRange, getDistanceTop, containerRef, itemHeight)

  const wrapperProps = computed(() => {
    return {
      style: {
        width: '100%',
        height: `${totalHeight.value - offsetTop.value}px`,
        marginTop: `${offsetTop.value}px`,
      },
    }
  })

  return {
    calculateRange,
    scrollTo,
    containerStyle,
    wrapperProps,
    currentList,
    containerRef,
  }
}

Internal helpers

Declared inside another function in this file.

onScroll(): void

Returns: void

Calls:

  • calculateRange
Code
() => {
        calculateRange()
      }

Interfaces

UseHorizontalVirtualListOptions

Interface Code
export interface UseHorizontalVirtualListOptions extends UseVirtualListOptionsBase {

  /**
   * item width, accept a pixel value or a function that returns the width
   *
   * @default 0
   */
  itemWidth: UseVirtualListItemSize

}

Properties

Name Type Optional Description
itemWidth UseVirtualListItemSize not shown

UseVerticalVirtualListOptions

Interface Code
export interface UseVerticalVirtualListOptions extends UseVirtualListOptionsBase {
  /**
   * item height, accept a pixel value or a function that returns the height
   *
   * @default 0
   */
  itemHeight: UseVirtualListItemSize
}

Properties

Name Type Optional Description
itemHeight UseVirtualListItemSize not shown

UseVirtualListOptionsBase

Interface Code
export interface UseVirtualListOptionsBase {
  /**
   * the extra buffer items outside of the view area
   *
   * @default 5
   */
  overscan?: number
}

Properties

Name Type Optional Description
overscan number not shown

UseVirtualListItem<T>

Interface Code
export interface UseVirtualListItem<T> {
  data: T
  index: number
}

Properties

Name Type Optional Description
data T not shown
index number not shown

UseVirtualListScrollToOptions

Interface Code
export interface UseVirtualListScrollToOptions {
  behavior?: ScrollBehavior
  block?: ScrollLogicalPosition
  inline?: ScrollLogicalPosition
}

Properties

Name Type Optional Description
behavior ScrollBehavior not shown
block ScrollLogicalPosition not shown
inline ScrollLogicalPosition not shown

UseVirtualListReturn<T>

Interface Code
export interface UseVirtualListReturn<T> {
  list: Ref<UseVirtualListItem<T>[]>
  scrollTo: (index: number, options?: UseVirtualListScrollToOptions) => void

  containerProps: {
    ref: Ref<HTMLElement | null>
    onScroll: () => void
    style: StyleValue
  }
  wrapperProps: ComputedRef<{
    style: {
      width: string
      height: string
      marginTop: string
    } | {
      width: string
      height: string
      marginLeft: string
      display: string
    }
  }>
}

Properties

Name Type Optional Description
list Ref<UseVirtualListItem<T>[]> not shown
scrollTo (index: number, options?: UseVirtualListScrollToOptions) => void not shown
containerProps { ref: Ref<HTMLElement \| null> onScroll: () => void style: StyleValue } not shown
wrapperProps ComputedRef<{ style: { width: string height: string marginTop: string } \| { ... not shown

UseVirtualElementSizes

Interface Code
interface UseVirtualElementSizes {
  width: Ref<number>
  height: Ref<number>
}

Properties

Name Type Optional Description
width Ref<number> not shown
height Ref<number> not shown

UseVirtualListState

Interface Code
interface UseVirtualListState { start: number, end: number }

Properties

Name Type Optional Description
start number not shown
end number not shown

UseVirtualListResources<T>

Interface Code
interface UseVirtualListResources<T> {
  state: RefState
  source: UseVirtualListSource<T>
  currentList: UseVirtualListRefArray<T>
  size: UseVirtualElementSizes
  containerRef: UseVirtualListContainerRef
}

Properties

Name Type Optional Description
state RefState not shown
source UseVirtualListSource<T> not shown
currentList UseVirtualListRefArray<T> not shown
size UseVirtualElementSizes not shown
containerRef UseVirtualListContainerRef not shown

Type Aliases

UseVirtualListItemSize

type UseVirtualListItemSize = number | ((index: number) => number);

UseVirtualListOptions

type UseVirtualListOptions = UseHorizontalVirtualListOptions | UseVerticalVirtualListOptions;

UseVirtualListContainerRef

type UseVirtualListContainerRef = Ref<HTMLElement | null>;

UseVirtualListArray<T>

type UseVirtualListArray<T> = UseVirtualListItem<T>[];

UseVirtualListRefArray<T>

type UseVirtualListRefArray<T> = Ref<UseVirtualListArray<T>>;

UseVirtualListSource<T>

type UseVirtualListSource<T> = Ref<readonly T[]> | ShallowRef<readonly T[]>;

RefState

type RefState = Ref<UseVirtualListState>;

Generated by Syntax Scribe