import React, { useState, useRef, useEffect, useCallback, } from 'react'; import { debounce } from 'throttle-debounce'; import { useSticky } from '~/client/services/side-effects/use-sticky'; import { useSWRxCurrentPage } from '~/stores/page'; import { useSidebarCollapsed } from '~/stores/ui'; import loggerFactory from '~/utils/logger'; import GrowiContextualSubNavigation from './GrowiContextualSubNavigation'; import styles from './GrowiSubNavigationSwitcher.module.scss'; const logger = loggerFactory('growi:cli:GrowiSubNavigationSticky'); export type GrowiSubNavigationSwitcherProps = { isLinkSharingDisabled: boolean, } /** * GrowiSubNavigation * * needs: * #grw-subnav-fixed-container element * #grw-subnav-sticky-trigger element */ export const GrowiSubNavigationSwitcher = (props: GrowiSubNavigationSwitcherProps): JSX.Element => { const { isLinkSharingDisabled } = props; const { data: currentPage } = useSWRxCurrentPage(); const { data: isSidebarCollapsed } = useSidebarCollapsed(); const [width, setWidth] = useState(0); // use more specific type HTMLDivElement for avoid assertion error. // see: https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement const fixedContainerRef = useRef(null); const clientWidth = fixedContainerRef.current?.parentElement?.clientWidth; // Get sticky status const isSticky = useSticky('#grw-subnav-sticky-trigger'); // Do not use clientWidth as useCallback deps, resizing events will not work in production builds. const initWidth = useCallback(() => { if (fixedContainerRef.current != null && fixedContainerRef.current.parentElement != null) { // get parent elements width const { clientWidth } = fixedContainerRef.current.parentElement; setWidth(clientWidth); } }, []); // setup effect by resizing event useEffect(() => { const resizeHandler = debounce(100, initWidth); window.addEventListener('resize', resizeHandler); // return clean up handler return () => { window.removeEventListener('resize', resizeHandler); }; }, [initWidth]); // update width when sidebar collapsing changed useEffect(() => { if (isSidebarCollapsed != null) { setTimeout(initWidth, 300); } }, [isSidebarCollapsed, initWidth]); /* * initialize width. * Since width is not recalculated at production build first rendering, * make initWidth execution dependent on clientWidth. */ useEffect(() => { if (clientWidth != null) initWidth(); }, [initWidth, clientWidth]); if (currentPage == null) { return <>; } return (
); };