import { useEffect, useRef, useState } from 'react'; import classnames from 'classnames'; import Collapser from './Collapser'; import { countItemsWithIcon, reduceIconlessChildrenIntoEllipsis, validateChildren, } from './navigationChildrenUtils'; import borderStyles from '../Borders.css'; import Box from '../Box'; import boxStyles from '../Box.css'; import { useSideNavigation } from '../contexts/SideNavigationProvider'; import Divider from '../Divider'; import Flex from '../Flex'; import layoutStyles from '../Layout.css'; import { Props as SideNavigationProps } from '../SideNavigation'; import styles from '../SideNavigation.css'; import { flattenChildrenWithKeys } from '../utils/flattenChildren'; import { FixedZIndex } from '../zIndex'; type Timeout = ReturnType; export default function NavigationContent({ accessibilityLabel, children, footer, header, showBorder, }: SideNavigationProps) { const navigationChildren = flattenChildrenWithKeys(children); validateChildren({ children: navigationChildren, filterLevel: 'main' }); const { collapsible, collapsed: sideNavigationCollapsed, onCollapse, overlayPreview, setOverlayPreview, transitioning, setTransitioning, } = useSideNavigation(); const mainContainer = useRef(null); const scrollContainer = useRef(null); const transitionContainer = useRef(null); const [isScrolled, setIsScrolled] = useState(false); const [collapsedContainerWidth, setCollapsedContainerWidth] = useState(); const previewTimeoutRef = useRef(); useEffect(() => { const element = transitionContainer.current; const transitionEndHandler = ({ target }: Event) => { if (target === element) setTransitioning(false); }; element?.addEventListener('transitionend', transitionEndHandler); return () => element?.removeEventListener('transitionend', transitionEndHandler); }, [setTransitioning]); useEffect(() => { const element = scrollContainer.current; const scrollHandler = () => setIsScrolled(!!element?.scrollTop); const mouseEnterHandler = () => { if (sideNavigationCollapsed && !transitioning) { // @ts-expect-error - TS2769 - No overload matches this call. clearTimeout(previewTimeoutRef.current); setCollapsedContainerWidth(mainContainer.current?.offsetWidth); setOverlayPreview(true); } }; const mouseLeaveHandler = () => { if (sideNavigationCollapsed) { // @ts-expect-error - TS2769 - No overload matches this call. clearTimeout(previewTimeoutRef.current); previewTimeoutRef.current = setTimeout(() => setOverlayPreview(false), 1000); } }; element?.addEventListener('scroll', scrollHandler); element?.addEventListener('mouseenter', mouseEnterHandler); element?.addEventListener('mouseleave', mouseLeaveHandler); return () => { element?.removeEventListener('scroll', scrollHandler); element?.removeEventListener('mouseenter', mouseEnterHandler); element?.removeEventListener('mouseleave', mouseLeaveHandler); }; }, [sideNavigationCollapsed, onCollapse, setOverlayPreview, transitioning]); const isCollapsed = sideNavigationCollapsed && !overlayPreview; const items = isCollapsed ? reduceIconlessChildrenIntoEllipsis(navigationChildren) : navigationChildren; const iconCount = countItemsWithIcon(navigationChildren); const shouldCollapseEmpty = iconCount === 0; const shouldHideItems = sideNavigationCollapsed && shouldCollapseEmpty && !overlayPreview; const normalWidth = 280; const headerWidth = isCollapsed ? 44 : undefined; const collapsedWidth = shouldCollapseEmpty ? 40 : 60; const contentWidth = isCollapsed ? collapsedWidth : normalWidth; return ( // 1st wrapper - always has dynamic width, but in overlay-preview state its width is static {/* 2nd wrapper - mainly acts as scroll-container and has dynamic width */}
{/* 3rd wrapper - when collapsible=true, it has static width and responsible for expand/collpase transition */}
' is not assignable to type 'LegacyRef | undefined'. ref={transitionContainer} className={classnames({ [styles.contentWidthTransition]: collapsible })} style={{ width: collapsible ? contentWidth : undefined }} > {collapsible && } {header ? ( {header} ) : null}
    {items}
{footer ? ( {footer} ) : null}
); }