1
1
import './Timeline.css' ;
2
2
import { Key , ReactElement , useEffect , useRef } from 'react' ;
3
- import { PropsWithKey , TimelineItem , TimelineItemProps } from './TimelineItem' ;
3
+ import { PropsWithKey , TimelineItem , TimelineItemProps , TimelineItemRefs } from './TimelineItem' ;
4
4
import { OffsetConfig , resolveOffsets } from '../models/offset' ;
5
5
import { Positioning } from '../models/positioning' ;
6
6
import { convertToCssVariable , StyleConfig } from '../models/style' ;
@@ -11,6 +11,7 @@ export type TimelineProps = {
11
11
gap ?: number ;
12
12
offset ?: OffsetConfig ;
13
13
minMarkerGap ?: number ;
14
+ defaultPointerOffset ?: number ;
14
15
dateFormat ?: string ;
15
16
dateLocale ?: Locale ;
16
17
customMarker ?: ReactElement ;
@@ -23,18 +24,32 @@ const defaultTimelineConfig: Partial<TimelineProps> = {
23
24
positioning : 'alternating' ,
24
25
gap : 50 ,
25
26
offset : 50 ,
26
- minMarkerGap : 100 ,
27
+ minMarkerGap : 50 ,
28
+ defaultPointerOffset : 40 ,
27
29
dateFormat : 'P' ,
28
30
} ;
29
31
30
32
export 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
+ } = {
32
47
...defaultTimelineConfig ,
33
48
...props ,
34
49
} ;
35
50
36
51
const timelineRef = useRef < HTMLDivElement > ( null ) ;
37
- const itemsRef = useRef < Map < Key , HTMLElement > > ( ) ;
52
+ const itemsRef = useRef < Map < Key , TimelineItemRefs > > ( ) ;
38
53
39
54
function getRefMap ( ) {
40
55
if ( ! itemsRef . current ) {
@@ -61,26 +76,34 @@ export function Timeline(props: TimelineProps) {
61
76
let leftHeight = left ;
62
77
let rightHeight = right ;
63
78
64
- elements . forEach ( ( item ) => {
65
- const element = item ;
79
+ let nextMarkerOffset = defaultPointerOffset ?? 0 ;
66
80
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 ;
69
84
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 ) ;
76
91
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 ) ;
81
103
}
82
104
} ) ;
83
105
106
+ // update height of container element to match absolute positioned timeline
84
107
const timelineElement = timelineRef . current ;
85
108
if ( timelineElement ) {
86
109
timelineElement . style . height = `${ Math . max ( leftHeight , rightHeight ) } px` ;
0 commit comments