11// @flow
22import XDate from 'xdate' ;
3+ import constants from '../commons/constants' ;
34import { Event , PackedEvent } from './EventBlock' ;
45
6+ type PartialPackedEvent = Event & { index : number } ;
7+ interface PopulateOptions {
8+ screenWidth ?: number ;
9+ dayStart ?: number ;
10+ hourBlockHeight ?: number ;
11+ overlapEventsSpacing ?: number ;
12+ }
13+
514export const HOUR_BLOCK_HEIGHT = 100 ;
6- const EVENT_BLOCK_RIGHT_MARGIN = 10 ;
15+ const OVERLAP_EVENTS_SPACINGS = 10 ;
716
8- function buildEvent ( column : any , left : number , width : number , dayStart : number ) {
9- const startTime = new XDate ( column . start ) ;
10- const endTime = column . end ? new XDate ( column . end ) : new XDate ( startTime ) . addHours ( 1 ) ;
17+ function buildEvent ( event : Event & { index : number } , left : number , width : number , { dayStart = 0 , hourBlockHeight = HOUR_BLOCK_HEIGHT } : PopulateOptions ) : PackedEvent {
18+ const startTime = new XDate ( event . start ) ;
19+ const endTime = event . end ? new XDate ( event . end ) : new XDate ( startTime ) . addHours ( 1 ) ;
1120
1221 const dayStartTime = new XDate ( startTime ) . clearTime ( ) ;
1322
14- column . top = ( dayStartTime . diffHours ( startTime ) - dayStart ) * HOUR_BLOCK_HEIGHT ;
15- column . height = startTime . diffHours ( endTime ) * HOUR_BLOCK_HEIGHT ;
16- column . width = width ;
17- column . left = left ;
18- return column ;
23+ return {
24+ ...event ,
25+ top : ( dayStartTime . diffHours ( startTime ) - dayStart ) * hourBlockHeight ,
26+ height : startTime . diffHours ( endTime ) * hourBlockHeight ,
27+ width,
28+ left
29+ } ;
1930}
2031
21- function collision ( a : Event , b : Event ) {
32+ function hasCollision ( a : Event , b : Event ) {
2233 return a . end > b . start && a . start < b . end ;
2334}
2435
25- function expand ( ev : Event , column : any , columns : any ) {
36+ function calcColumnSpan ( event : Event , columnIndex : number , columns : Event [ ] [ ] ) {
2637 let colSpan = 1 ;
2738
28- for ( let i = column + 1 ; i < columns . length ; i ++ ) {
29- const col = columns [ i ] ;
30- for ( let j = 0 ; j < col . length ; j ++ ) {
31- const ev1 = col [ j ] ;
32- if ( collision ( ev , ev1 ) ) {
33- return colSpan ;
34- }
39+ for ( let i = columnIndex + 1 ; i < columns . length ; i ++ ) {
40+ const column = columns [ i ] ;
41+
42+ const foundCollision = column . find ( ev => hasCollision ( event , ev ) ) ;
43+ if ( foundCollision ) {
44+ return colSpan ;
3545 }
46+
3647 colSpan ++ ;
3748 }
3849
3950 return colSpan ;
4051}
4152
42- function pack ( columns : any , width : number , calculatedEvents : Event [ ] , dayStart : number ) {
43- const colLength = columns . length ;
44-
45- for ( let i = 0 ; i < colLength ; i ++ ) {
46- const col = columns [ i ] ;
47- for ( let j = 0 ; j < col . length ; j ++ ) {
48- const colSpan = expand ( col [ j ] , i , columns ) ;
49- const L = ( i / colLength ) * width ;
50- const W = ( width * colSpan ) / colLength - EVENT_BLOCK_RIGHT_MARGIN ;
53+ function packOverlappingEventGroup (
54+ columns : PartialPackedEvent [ ] [ ] ,
55+ calculatedEvents : PackedEvent [ ] ,
56+ populateOptions : PopulateOptions
57+ ) {
58+ const { screenWidth = constants . screenWidth , overlapEventsSpacing = OVERLAP_EVENTS_SPACINGS } = populateOptions ;
59+ columns . forEach ( ( column , columnIndex ) => {
60+ column . forEach ( event => {
61+ const columnSpan = calcColumnSpan ( event , columnIndex , columns ) ;
62+ const eventLeft = ( columnIndex / columns . length ) * screenWidth ;
63+ let eventWidth = screenWidth * ( columnSpan / columns . length ) ;
64+
65+ if ( columnIndex + columnSpan <= columns . length - 1 ) {
66+ eventWidth -= overlapEventsSpacing ;
67+ }
5168
52- calculatedEvents . push ( buildEvent ( col [ j ] , L , W , dayStart ) ) ;
53- }
54- }
69+ calculatedEvents . push ( buildEvent ( event , eventLeft , eventWidth , populateOptions ) ) ;
70+ } ) ;
71+ } ) ;
5572}
5673
57- function populateEvents ( events : Event [ ] , screenWidth : number , dayStart : number ) {
58- let lastEnd : any ;
59- let columns : any ;
60- const calculatedEvents : Event [ ] = [ ] ;
74+ function populateEvents ( _events : Event [ ] , populateOptions : PopulateOptions ) {
75+ let lastEnd : string | null = null ;
76+ let columns : PartialPackedEvent [ ] [ ] = [ ] ;
77+ const calculatedEvents : PackedEvent [ ] = [ ] ;
6178
62- events = events
79+ const events : PartialPackedEvent [ ] = _events
6380 . map ( ( ev : Event , index : number ) => ( { ...ev , index : index } ) )
6481 . sort ( function ( a : Event , b : Event ) {
6582 if ( a . start < b . start ) return - 1 ;
@@ -69,26 +86,26 @@ function populateEvents(events: Event[], screenWidth: number, dayStart: number)
6986 return 0 ;
7087 } ) ;
7188
72- columns = [ ] ;
73- lastEnd = null ;
74-
75- events . forEach ( function ( ev : Event ) {
89+ events . forEach ( function ( ev ) {
90+ // Reset recent overlapping event group and start a new one
7691 if ( lastEnd !== null && ev . start >= lastEnd ) {
77- pack ( columns , screenWidth , calculatedEvents , dayStart ) ;
92+ packOverlappingEventGroup ( columns , calculatedEvents , populateOptions ) ;
7893 columns = [ ] ;
7994 lastEnd = null ;
8095 }
8196
97+ // Place current event in the right column where it doesn't overlap
8298 let placed = false ;
8399 for ( let i = 0 ; i < columns . length ; i ++ ) {
84100 const col = columns [ i ] ;
85- if ( ! collision ( col [ col . length - 1 ] , ev ) ) {
101+ if ( ! hasCollision ( col [ col . length - 1 ] , ev ) ) {
86102 col . push ( ev ) ;
87103 placed = true ;
88104 break ;
89105 }
90106 }
91107
108+ // If curr event wasn't placed in any of the columns, create a new column for it
92109 if ( ! placed ) {
93110 columns . push ( [ ev ] ) ;
94111 }
@@ -99,9 +116,10 @@ function populateEvents(events: Event[], screenWidth: number, dayStart: number)
99116 } ) ;
100117
101118 if ( columns . length > 0 ) {
102- pack ( columns , screenWidth , calculatedEvents , dayStart ) ;
119+ packOverlappingEventGroup ( columns , calculatedEvents , populateOptions ) ;
103120 }
104- return calculatedEvents as PackedEvent [ ] ;
121+
122+ return calculatedEvents ;
105123}
106124
107125export default populateEvents ;
0 commit comments