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+ eventBlockRightMargin ?: number ;
12+ }
13+
514export const HOUR_BLOCK_HEIGHT = 100 ;
615const EVENT_BLOCK_RIGHT_MARGIN = 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 : width ,
28+ left : 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 ;
51-
52- calculatedEvents . push ( buildEvent ( col [ j ] , L , W , dayStart ) ) ;
53- }
54- }
53+ function packOverlappingEventGroup (
54+ columns : PartialPackedEvent [ ] [ ] ,
55+ calculatedEvents : PackedEvent [ ] ,
56+ populateOptions : PopulateOptions
57+ ) {
58+ const { screenWidth = constants . screenWidth , eventBlockRightMargin = EVENT_BLOCK_RIGHT_MARGIN } = 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+ const eventWidth = screenWidth * ( columnSpan / columns . length ) - eventBlockRightMargin ;
64+
65+ calculatedEvents . push ( buildEvent ( event , eventLeft , eventWidth , populateOptions ) ) ;
66+ } ) ;
67+ } ) ;
5568}
5669
57- function populateEvents ( events : Event [ ] , screenWidth : number , dayStart : number ) {
58- let lastEnd : any ;
59- let columns : any ;
60- const calculatedEvents : Event [ ] = [ ] ;
70+ function populateEvents ( _events : Event [ ] , populateOptions : PopulateOptions ) {
71+ let lastEnd : string | null = null ;
72+ let columns : PartialPackedEvent [ ] [ ] = [ ] ;
73+ const calculatedEvents : PackedEvent [ ] = [ ] ;
6174
62- events = events
75+ const events : PartialPackedEvent [ ] = _events
6376 . map ( ( ev : Event , index : number ) => ( { ...ev , index : index } ) )
6477 . sort ( function ( a : Event , b : Event ) {
6578 if ( a . start < b . start ) return - 1 ;
@@ -69,26 +82,26 @@ function populateEvents(events: Event[], screenWidth: number, dayStart: number)
6982 return 0 ;
7083 } ) ;
7184
72- columns = [ ] ;
73- lastEnd = null ;
74-
75- events . forEach ( function ( ev : Event ) {
85+ events . forEach ( function ( ev ) {
86+ // Reset recent overlapping event group and start a new one
7687 if ( lastEnd !== null && ev . start >= lastEnd ) {
77- pack ( columns , screenWidth , calculatedEvents , dayStart ) ;
88+ packOverlappingEventGroup ( columns , calculatedEvents , populateOptions ) ;
7889 columns = [ ] ;
7990 lastEnd = null ;
8091 }
8192
93+ // Place current event in the right column where it doesn't overlap
8294 let placed = false ;
8395 for ( let i = 0 ; i < columns . length ; i ++ ) {
8496 const col = columns [ i ] ;
85- if ( ! collision ( col [ col . length - 1 ] , ev ) ) {
97+ if ( ! hasCollision ( col [ col . length - 1 ] , ev ) ) {
8698 col . push ( ev ) ;
8799 placed = true ;
88100 break ;
89101 }
90102 }
91103
104+ // If curr event wasn't placed in any of the columns, create a new column for it
92105 if ( ! placed ) {
93106 columns . push ( [ ev ] ) ;
94107 }
@@ -99,9 +112,10 @@ function populateEvents(events: Event[], screenWidth: number, dayStart: number)
99112 } ) ;
100113
101114 if ( columns . length > 0 ) {
102- pack ( columns , screenWidth , calculatedEvents , dayStart ) ;
115+ packOverlappingEventGroup ( columns , calculatedEvents , populateOptions ) ;
103116 }
104- return calculatedEvents as PackedEvent [ ] ;
117+
118+ return calculatedEvents ;
105119}
106120
107121export default populateEvents ;
0 commit comments