11import './Timeline.css' ;
22import { Key , ReactElement , useEffect , useRef } from 'react' ;
3- import { PropsWithKey , TimelineItem , TimelineItemProps } from './TimelineItem' ;
3+ import { PropsWithKey , TimelineItem , TimelineItemProps , TimelineItemRefs } from './TimelineItem' ;
44import { OffsetConfig , resolveOffsets } from '../models/offset' ;
55import { Positioning } from '../models/positioning' ;
66import { convertToCssVariable , StyleConfig } from '../models/style' ;
@@ -11,6 +11,7 @@ export type TimelineProps = {
1111 gap ?: number ;
1212 offset ?: OffsetConfig ;
1313 minMarkerGap ?: number ;
14+ defaultPointerOffset ?: number ;
1415 dateFormat ?: string ;
1516 dateLocale ?: Locale ;
1617 customMarker ?: ReactElement ;
@@ -23,18 +24,32 @@ const defaultTimelineConfig: Partial<TimelineProps> = {
2324 positioning : 'alternating' ,
2425 gap : 50 ,
2526 offset : 50 ,
26- minMarkerGap : 100 ,
27+ minMarkerGap : 50 ,
28+ defaultPointerOffset : 40 ,
2729 dateFormat : 'P' ,
2830} ;
2931
3032export function Timeline ( props : TimelineProps ) {
31- const { items, positioning, gap, offset, minMarkerGap, className, dateFormat, dateLocale, customMarker, customPointer, styleConfig } = {
33+ const {
34+ items,
35+ positioning,
36+ gap,
37+ offset,
38+ minMarkerGap,
39+ defaultPointerOffset,
40+ className,
41+ dateFormat,
42+ dateLocale,
43+ customMarker,
44+ customPointer,
45+ styleConfig,
46+ } = {
3247 ...defaultTimelineConfig ,
3348 ...props ,
3449 } ;
3550
3651 const timelineRef = useRef < HTMLDivElement > ( null ) ;
37- const itemsRef = useRef < Map < Key , HTMLElement > > ( ) ;
52+ const itemsRef = useRef < Map < Key , TimelineItemRefs > > ( ) ;
3853
3954 function getRefMap ( ) {
4055 if ( ! itemsRef . current ) {
@@ -61,26 +76,34 @@ export function Timeline(props: TimelineProps) {
6176 let leftHeight = left ;
6277 let rightHeight = right ;
6378
64- elements . forEach ( ( item ) => {
65- const element = item ;
79+ let nextMarkerOffset = defaultPointerOffset ?? 0 ;
6680
67- if ( ( positioning !== 'right' && leftHeight > rightHeight ) || positioning === 'left' ) {
68- leftHeight += getMinMarkerGapCompensation ( leftHeight , rightHeight ) ;
81+ elements . forEach ( ( refs ) => {
82+ const { item, pointer, marker } = refs ;
83+ if ( ! item || ! pointer || ! marker ) return ;
6984
70- element . style . top = ` ${ rightHeight } px` ;
71- element . classList . add ( 'timeline-item--right' ) ;
72- element . classList . remove ( 'timeline-item--left' ) ;
73- rightHeight += element . offsetHeight + ( gap ?? 0 ) ;
74- } else {
75- rightHeight += getMinMarkerGapCompensation ( leftHeight , rightHeight ) ;
85+ // offsets marker + pointer if it would get in the way of the last marker
86+ pointer . style . top = ` ${ nextMarkerOffset } px` ;
87+ marker . style . marginTop = ` ${ nextMarkerOffset } px` ;
88+
89+ const markerOffsetCompensation = getMinMarkerGapCompensation ( leftHeight , rightHeight ) ;
90+ nextMarkerOffset = markerOffsetCompensation + ( defaultPointerOffset ?? 0 ) ;
7691
77- element . style . top = `${ leftHeight } px` ;
78- element . classList . add ( 'timeline-item--left' ) ;
79- element . classList . remove ( 'timeline-item--right' ) ;
80- leftHeight += element . offsetHeight + ( gap ?? 0 ) ;
92+ // defines whether an item should be positioned left or right of the timeline
93+ if ( ( positioning !== 'right' && leftHeight > rightHeight ) || positioning === 'left' ) {
94+ item . style . top = `${ rightHeight } px` ;
95+ item . classList . add ( 'timeline-item--right' ) ;
96+ item . classList . remove ( 'timeline-item--left' ) ;
97+ rightHeight += item . offsetHeight + ( gap ?? 0 ) ;
98+ } else {
99+ item . style . top = `${ leftHeight } px` ;
100+ item . classList . add ( 'timeline-item--left' ) ;
101+ item . classList . remove ( 'timeline-item--right' ) ;
102+ leftHeight += item . offsetHeight + ( gap ?? 0 ) ;
81103 }
82104 } ) ;
83105
106+ // update height of container element to match absolute positioned timeline
84107 const timelineElement = timelineRef . current ;
85108 if ( timelineElement ) {
86109 timelineElement . style . height = `${ Math . max ( leftHeight , rightHeight ) } px` ;
0 commit comments