import { setCSSVar } from "@web3r/flowerkit/css"; import { getDebouncedFn, wait } from "@web3r/flowerkit/fn"; import type { Dispatch, RefObject, SetStateAction } from "react"; import { useEffect, useCallback, useState, useRef, } from "react"; import { getWindow } from "ssr-window"; import { useListeners } from "./useListeners"; type TUseSpoilerProps = { ref: RefObject; isActive?: boolean; }; type TUseSpoilerReturn = { isOpen: boolean; setIsOpen: Dispatch>; setHeight: () => void; }; /** * Возвращает переменную высоты элемента для создания спойлеров и переменные управления состоянием * @param ref - референс элемента, для которого необходимо рассчитать высоту * @param isActive - состояние активности спойлера * @returns {TUseSpoilerReturn} Возвращает текущее состояние спойлера (`isOpen`), setter `setIsOpen` и функцию `setHeight` для пересчёта CSS-переменной высоты. * @example * import { useSpoiler, useCN } from "@delement/ui/utils/react"; * import { useRef } from "react"; * import { Btn } from "@delement/ui/components"; * * const App = ({ baseClass = "myApp" }) => { * * const { getCN } = useCN(baseClass) * * const refOfElement = useRef(null); // к данному элементу добавится переменная --scrollHeight * * const { isOpen, setIsOpen } = useSpoiler({ ref: refOfElement, isActive: false }); * * const onClick = (e) => { * e.preventDefault(); * setIsOpen(isOpen => !isOpen); * } * * return ( *
* *
*

Контент элемента, который необходимо скрыть

*
*
* ) * } */ export const useSpoiler = ({ ref, isActive = false }: TUseSpoilerProps): TUseSpoilerReturn => { const [ isOpen, setIsOpen ] = useState(isActive); const windowRef = useRef(getWindow()); const setHeight = useCallback(() => { const { current } = ref; if (current) { setCSSVar(current, "--scrollHeight", `${Math.ceil(current.scrollHeight)}px`); } else { console.error(`[${useSpoiler.name}] Ref is null or undefined`); } }, [ ref ]); const onResize = getDebouncedFn(() => setHeight()); useListeners({ listeners: { resize: onResize, }, ref: windowRef, }); useEffect(() => { setIsOpen(isOpen); }, [ isOpen ]); useEffect(() => { wait(100).finally(() => { setHeight(); }); }, []); return { isOpen, setIsOpen, setHeight, }; };