import { forwardRef, useLayoutEffect, useRef, useState, Children, cloneElement, isValidElement, } from 'react' import Block from '../Primitives/Block/Block' import { useResponsiveTokens } from '../../hooks/useResponsiveTokens' import { filterBlockedProps } from '../../utils/prop-helpers' import { TimelineTokensType } from './timeline.token' import { getLineTop, getLineBottom, parsePx, clampLineHeight } from './utils' import TimelineLabel from './TimelineLabel' import TimelineHeader from './TimelineHeader' import TimelineSubstep from './TimelineSubstep' import TimelineNode from './TimelineNode' import TimelineShowMore from './TimelineShowMore' import type { LineLayout, TimelineRootProps } from './types' const TimelineRoot = forwardRef( (props, ref) => { const { children, className, ...rest } = props const tokens = useResponsiveTokens('TIMELINE') const filteredProps = filterBlockedProps(rest) const [lineLayout, setLineLayout] = useState(null) const containerRef = useRef(null) useLayoutEffect(() => { const container = containerRef.current if (!container) return const indicatorHeightPx = parsePx(tokens.indicator.height) const indicatorTopPx = parsePx(tokens.subsection.rootIndicator.top) const labelCircleHeightPx = parsePx(tokens.label.circle.height) const options = { indicatorHeightPx, indicatorTopPx, labelCircleHeightPx, } const top = getLineTop(container, options) const bottom = getLineBottom(container, options) const clamped = clampLineHeight(top, bottom) setLineLayout(clamped) }, [ children, tokens.indicator.height, tokens.subsection.rootIndicator.top, tokens.label.circle.height, ]) const setRef = (el: HTMLDivElement | null) => { containerRef.current = el const refObj = ref as React.RefObject | null if (typeof ref === 'function') ref(el) else if (refObj) refObj.current = el } return ( {lineLayout && ( )} {Children.map(children, (child) => { if ( isValidElement(child) && child.type === TimelineSubstep ) { return cloneElement(child, { showIndicator: false, } as { showIndicator: boolean }) } return child })} ) } ) TimelineRoot.displayName = 'Timeline' // Statics reuse the flat exports, so they are typed as `typeof` those exports // to keep the emitted declarations stating the value identity. const Timeline: typeof TimelineRoot & { Label: typeof TimelineLabel Header: typeof TimelineHeader Substep: typeof TimelineSubstep Node: typeof TimelineNode ShowMore: typeof TimelineShowMore } = Object.assign(TimelineRoot, { Label: TimelineLabel, Header: TimelineHeader, Substep: TimelineSubstep, Node: TimelineNode, ShowMore: TimelineShowMore, }) export default Timeline