import React, { createContext, useContext, useCallback, useRef, useLayoutEffect, } from 'react' import type { SectionProps } from './types' import { ChevronDown } from 'lucide-react' import NavItem from './NavItem' import Block from '../Primitives/Block/Block' import Text from '../Text/Text' import styled from 'styled-components' import { useResponsiveTokens } from '../../hooks/useResponsiveTokens' import { DirectoryTokenType } from './directory.tokens' import { useSectionScroll } from '../../hooks/useSectionScroll' import { getItemPathSegment } from './utils' type SectionStateContextValue = { sectionStates: Map setSectionState: (index: number, isOpen: boolean) => void } const SectionStateContext = createContext(null) const useSectionState = () => { const context = useContext(SectionStateContext) return context } export { SectionStateContext } const ChevronWrapper = styled(Block)<{ $isOpen: boolean $tokens: DirectoryTokenType }>` display: flex; align-items: center; justify-content: center; margin-left: auto; & > svg { width: ${({ $tokens }) => $tokens.section.header.chevron.width} !important; height: ${({ $tokens }) => $tokens.section.header.chevron.width} !important; transition: transform 150ms; transform: ${({ $isOpen }) => $isOpen ? 'rotate(180deg)' : 'rotate(0)'}; } ` const Section = ({ section, sectionIndex, onNavigateBetweenSections, idPrefix, iconOnlyMode = false, showHierarchyLines = false, hierarchyLineBorderRadius = 0, enableParentSelection = false, highlightActivePath = false, }: SectionProps) => { const tokens = useResponsiveTokens('DIRECTORY') const sectionStateContext = useSectionState() const { scrollIntoView } = useSectionScroll() const defaultIsOpen = section.defaultOpen !== false const sharedIsOpen = sectionStateContext?.sectionStates.get(sectionIndex) const [localIsOpen, setLocalIsOpen] = React.useState(defaultIsOpen) const isOpen = sharedIsOpen !== undefined ? sharedIsOpen : localIsOpen const previousIsOpen = useRef(isOpen) const setIsOpen = useCallback( (value: boolean) => { if (sectionStateContext) { sectionStateContext.setSectionState(sectionIndex, value) } else { setLocalIsOpen(value) } }, [sectionIndex, sectionStateContext] ) const sectionRef = useRef(null) const headerRef = useRef(null) const itemRefs = useRef>>([]) React.useEffect(() => { if (section.items) { itemRefs.current = section.items.map(() => React.createRef() ) } }, [section.items]) useLayoutEffect(() => { const wasCollapsed = !previousIsOpen.current const isExpanding = isOpen && wasCollapsed previousIsOpen.current = isOpen if (isExpanding && sectionRef.current) { scrollIntoView(sectionRef.current) } }, [isOpen, scrollIntoView]) const toggleSection = () => { if (!iconOnlyMode) { setIsOpen(!isOpen) } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault() toggleSection() } else if (e.key === 'ArrowDown') { e.preventDefault() if (!isOpen) { setIsOpen(true) } else { const firstItem = sectionRef.current ?.querySelector('ul') ?.querySelector('button, a') if (firstItem) { ;(firstItem as HTMLElement).focus() } else { onNavigateBetweenSections('down', sectionIndex) } } } else if (e.key === 'ArrowUp') { e.preventDefault() onNavigateBetweenSections('up', sectionIndex) } } const handleItemNavigation = ( direction: 'up' | 'down', currentIndex: number ) => { if (direction === 'up') { if (currentIndex === 0) { headerRef.current?.focus() } else { const prevItem = sectionRef.current ?.querySelector('ul') ?.querySelectorAll('button, a')[currentIndex - 1] if (prevItem) { ;(prevItem as HTMLElement).focus() } } } else { if (currentIndex === (section.items?.length || 0) - 1) { onNavigateBetweenSections('down', sectionIndex) } else { const nextItem = sectionRef.current ?.querySelector('ul') ?.querySelectorAll('button, a')[currentIndex + 1] if (nextItem) { ;(nextItem as HTMLElement).focus() } } } } const isCollapsible = section.isCollapsible !== false return ( {section.label && !iconOnlyMode && ( {section.label.toUpperCase()} {isCollapsible && ( )} )} {iconOnlyMode && sectionIndex > 0 && ( ) } export default Section