All Downloads are FREE. Search and download functionalities are using the official Maven repository.

components.regions.ScrollSpy.Content.jsx Maven / Gradle / Ivy

The newest version!
import React, { useRef, useImperativeHandle, forwardRef, useCallback, useEffect } from 'react'
import PropTypes from 'prop-types'
import { debounce } from 'lodash'

import { usePrevious } from '../../../utils/usePrevious'

import { SectionGroup } from './Section'
import { CONTENT_GROUP_CLASS_NAME, SCROLL_DELAY, SCROLL_THROTTLE } from './const'
import { useTimeoutSwitcher, getActive } from './utils'

/**
 * ScrollSpy Контент-контейнер
 * Компонент отдаёт наружу метод scrollTo(id) для прокрутки до нужного элемента
 * и меняет активный элемент в зависимости от скрола
 */
export const Content = forwardRef(({
    items,
    pageId,
    active,
    setActive,
    headlines,
    scrollState,
}, forwardedRef) => {
    const prevScroll = usePrevious(scrollState)
    const sections = useRef({})
    const contentRef = useRef(null)
    const setSectionRef = useCallback((id, ref) => {
        sections.current[id] = ref
    }, [sections])
    const [ignoreScroll, setIgnoreScroll] = useTimeoutSwitcher(SCROLL_DELAY, false)

    useImperativeHandle(forwardedRef, () => ({
        scrollTo(id) {
            setIgnoreScroll()
            sections.current[id]?.scrollIntoView({
                behavior: 'smooth',
            })
        },
    }), [sections, setIgnoreScroll])

    // eslint-disable-next-line react-hooks/exhaustive-deps
    const onScroll = useCallback(debounce((scrollState) => {
        const content = contentRef.current

        if (ignoreScroll || !content) { return }

        const { top } = content.getBoundingClientRect()
        const offset = Math.round(top - scrollState.top + scrollState.scrollTop)

        const newActive = getActive(scrollState, sections.current, offset)

        if (newActive && newActive !== active) {
            setActive(newActive)
        }
    }, SCROLL_THROTTLE, { maxWait: SCROLL_THROTTLE }), [ignoreScroll, active, setActive])

    useEffect(() => {
        if (!prevScroll) { return }

        // Корректировочный скрол при изменении размеров контейнера
        if (
            (scrollState.scrollHeight !== prevScroll.scrollHeight) ||
            (scrollState.clientHeight !== prevScroll.clientHeight)
        ) {
            forwardedRef.current.scrollTo(active)

            return
        }

        if (scrollState.scrollTop !== prevScroll.scrollTop) {
            onScroll(scrollState)
        }
    }, [scrollState, prevScroll, onScroll, forwardedRef, active])

    return (
        
{items.map(item => ( ))}
) }) Content.propTypes = { pageId: PropTypes.string, active: PropTypes.string, setActive: PropTypes.func, items: PropTypes.array, }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy