import React, { useCallback, useEffect, useRef, useState } from 'react'; import styles from './styles.module.css'; import { useWindowSize } from '../../../../../../hooks/useWindowSize'; import SectionRightItem from './components/SectionRightItem'; import SectionLeftItem from './components/SectionLeftItem'; import { FiChevronDown, FiChevronUp } from 'react-icons/fi'; import { IconTypes } from '../../../../../../components/other/Icons'; export interface SectionInterface { codeWithComment?: string; code: string; title: string; description: string; icon?: IconTypes; } export type Props = { sections: SectionInterface[]; startIndex?: number }; const SectionScroller: React.FC<Props> = (props) => { const { sections } = props; const startIndex = props.startIndex != null && props.startIndex < sections.length ? props.startIndex : Math.floor(sections.length / 2); const { windowWidth } = useWindowSize(); const sectionContainerRef = useRef(null); const [showTopChevron, setShowTopChevron] = useState(false); const [showBottomChevron, setShowBottomChevron] = useState(false); const [index, setIndex] = useState(startIndex); const [codeBlockRefs] = useState<{ [key: string]: HTMLDivElement | null; }>({}); useEffect(() => { setShowBottomChevron(windowWidth <= 768 && index !== sections.length - 1); setShowTopChevron(windowWidth <= 768 && index !== 0); }, [windowWidth]); const calculateTop = (index: number): number => { const topPadding = (sectionContainerRef.current?.clientHeight || 0) / 3 - (codeBlockRefs[index]?.clientHeight || 0) / 2; const spaceBetweenItems = 12; let totalHeight = 0; for (let i = 0; i < index; i++) { totalHeight += codeBlockRefs[i]?.clientHeight || 0; } return -totalHeight - spaceBetweenItems * index + topPadding; }; const handleChevronClick = useCallback( (up: boolean) => { let newIndex = 0; if (up) { newIndex = Math.max(index - 1, 0); } else { newIndex = Math.min(index + 1, sections.length - 1); } setShowTopChevron(newIndex !== 0); setShowBottomChevron(newIndex !== sections.length - 1); setIndex(newIndex); }, [index, sections] ); return ( <div className={styles.SectionContainer} ref={sectionContainerRef}> <div className={styles.ChevronContainer} style={{ visibility: showTopChevron ? 'visible' : 'hidden', }} onClick={() => { handleChevronClick(true); }}> <FiChevronUp /> </div> <div className={styles.SectionInnerContainer}> <div className={styles.SectionLeftContainer}> {sections.map((section, i) => { return ( <SectionLeftItem style={{ top: calculateTop(index) }} key={i} forwardRef={(element) => { codeBlockRefs[i] = element; }} code={ windowWidth < 768 ? section.codeWithComment || section.code : section.code } active={index === i} /> ); })} </div> <div className={styles.SectionRightContainer}> {sections.map((section, i) => { return ( <SectionRightItem key={i} title={section.title} description={section.description} onClick={() => { setIndex(i); }} icon={section.icon} active={index === i} /> ); })} </div> </div> <div className={styles.ChevronContainer} style={{ visibility: showBottomChevron ? 'visible' : 'hidden', }} onClick={() => { handleChevronClick(false); }}> <FiChevronDown /> </div> </div> ); }; export default SectionScroller;