composables/useListInfiniteScroll.ts
import { useDebounceFn, useInfiniteScroll, useResizeObserver, useScroll,} from '@vueuse/core'import { INFINITE_SCROLL_CONTAINER_ID, INFINITE_SCROLL_ITEM_CLASS_NAME,} from '@/utils/constants' type LoadDirection = 'up' | 'down' Function `default` has a Cognitive Complexity of 31 (exceeds 5 allowed). Consider refactoring.export default function ({ defaultFirst, defaultScrollContainerId, defaultScrollItemClassName, gotoPage, fetchPageData,}: { defaultFirst?: number defaultScrollContainerId?: string defaultScrollItemClassName?: string gotoPage: (page: number) => void fetchPageData: ( page: number, loadDirection?: LoadDirection, ) => Promise<boolean>}) { const route = useRoute() const router = useRouter() const { $consola } = useNuxtApp() const currentPage = ref(parseInt(route.query?.page as string) || 1) const startPage = ref(currentPage.value) const endPage = ref(startPage.value) const scrollItemHeight = ref(300) const itemsPerRow = ref(4) const scrollItemSizeInit = ref(false) const first = ref(defaultFirst || 40) const total = ref(0) const isFetchingData = ref(false) const containerRef = ref<Window>(window) useInfiniteScroll( containerRef, () => { reachBottomHandler() }, { distance: 2000, }, ) const scrollContainerId = ref( defaultScrollContainerId ?? INFINITE_SCROLL_CONTAINER_ID, ) const scrollItemClassName = ref( defaultScrollItemClassName ?? INFINITE_SCROLL_ITEM_CLASS_NAME, ) const canLoadNextPage = computed( () => endPage.value < Math.ceil(total.value / first.value) && total.value > 0, ) const pageHeight = computed( (): number => scrollItemHeight.value * (first.value / itemsPerRow.value), ) const updateCurrentPage = () => { // allow page update only when current path is same as route path // i.e. scope it to only the page in which useListInfiniteScroll is used const allowUpdate = import.meta.client && window.location.pathname === route.path if (!allowUpdate) { return } const page = Math.floor(document.documentElement.scrollTop / pageHeight.value) + startPage.value if (page) { replaceUrlPage(String(page)) currentPage.value = page } } const onResize = useDebounceFn(() => { try { const container = document.getElementById(scrollContainerId.value) const scrollItem = document.body.querySelector( `.${scrollItemClassName.value}`, ) if (scrollItem && container) { scrollItemHeight.value = scrollItem.clientHeight itemsPerRow.value = Math.max( Math.floor(container.clientWidth / scrollItem.clientWidth), 1, ) scrollItemSizeInit.value = true } } catch (err) { $consola.warn('resize scroll item', err) } }, 1000) useScroll(window, { onScroll: updateCurrentPage, throttle: 1000 }) useResizeObserver(document.body, onResize) const replaceUrlPage = (targetPage: string) => { const isFirstPage = targetPage === '1' if (targetPage === route.query.page || (isFirstPage && !route.query.page)) { return } const { page, ...restQuery } = route.query router .replace({ path: String(route.path), query: isFirstPage ? { ...restQuery } : { ...restQuery, page: targetPage }, }) .catch($consola.warn) } const reachTopHandler = useDebounceFn(() => { fetchPreviousPage() }, 1000) const fetchDataCallback = async ( page: number, direction: LoadDirection, successCb: () => void, ) => { return await fetchPageData(page, direction).then((isSuccess) => { if (isSuccess) { successCb() } return isSuccess }) } const fetchPreviousPage = async () => { if (startPage.value <= 1) { return } const nextPage = startPage.value - 1 await fetchDataCallback(startPage.value - 1, 'up', () => { startPage.value = nextPage checkAfterFetchDataSuccess() }) } const reachBottomHandler = useDebounceFn(() => { fetchNextPage() }, 1000) const fetchNextPage = async () => { if (!canLoadNextPage.value) { return } const nextPage = endPage.value + 1 await fetchDataCallback(nextPage, 'down', () => { endPage.value = nextPage checkAfterFetchDataSuccess() prefetchNextPage() }) } const prefetchNextPage = async () => { updateCurrentPage() if (endPage.value - currentPage.value <= 1 && canLoadNextPage.value) { await fetchNextPage() } } const checkAfterFetchDataSuccess = () => { checkCurrentPageIsValid() checkScrollItemSize() } const checkCurrentPageIsValid = () => { const maxPage = Math.ceil(total.value / first.value) if (maxPage > 0 && currentPage.value > maxPage) { gotoPage(maxPage) } } const checkScrollItemSize = () => { if (scrollItemSizeInit.value) { return } onResize() } Similar blocks of code found in 2 locations. Consider refactoring. return { isFetchingData, fetchPreviousPage, fetchNextPage, reachTopHandler, prefetchNextPage, first, total, startPage, currentPage, endPage, scrollItemClassName, scrollContainerId, }}