22import min from 'lodash/min' ;
33import map from 'lodash/map' ;
44import PropTypes from 'prop-types' ;
5- import XDate from 'xdate' ;
65
76import React , { Component } from 'react' ;
8- import { View , Text , TouchableOpacity , Dimensions , ScrollView , TextStyle , ViewStyle } from 'react-native' ;
7+ import { View , Dimensions , ScrollView , TextStyle , ViewStyle } from 'react-native' ;
98
109import { Theme } from '../types' ;
1110import styleConstructor from './style' ;
12- import populateEvents from './Packer' ;
13-
11+ import populateEvents , { HALF_HOUR_BLOCK_HEIGHT } from './Packer' ;
12+ import TimelineHours from './TimelineHours' ;
13+ import EventBlock , { Event , PackedEvent } from './EventBlock' ;
1414
1515const LEFT_MARGIN = 60 - 1 ;
16- const TEXT_LINE_HEIGHT = 17 ;
17-
18- function range ( from : number , to : number ) {
19- return Array . from ( Array ( to ) , ( _ , i ) => from + i ) ;
20- }
2116
2217const { width : dimensionWidth } = Dimensions . get ( 'window' ) ;
2318
24- export type Event = {
25- start : string ;
26- end : string ;
27- title : string ;
28- summary : string ;
29- color ?: string ;
30- } ;
31-
3219export interface TimelineProps {
3320 events : Event [ ] ;
3421 start ?: number ;
@@ -39,12 +26,12 @@ export interface TimelineProps {
3926 theme ?: Theme ;
4027 scrollToFirst ?: boolean ;
4128 format24h ?: boolean ;
42- renderEvent ?: ( event : Event ) => JSX . Element ;
29+ renderEvent ?: ( event : PackedEvent ) => JSX . Element ;
4330}
4431
4532interface State {
4633 _scrollY : number ;
47- packedEvents : Event [ ] ;
34+ packedEvents : PackedEvent [ ] ;
4835}
4936
5037export default class Timeline extends Component < TimelineProps , State > {
@@ -72,21 +59,22 @@ export default class Timeline extends Component<TimelineProps, State> {
7259 format24h : true
7360 } ;
7461
75- private scrollView : React . RefObject < any > = React . createRef ( ) ;
62+ private scrollView = React . createRef < ScrollView > ( ) ;
7663 style : { [ key : string ] : ViewStyle | TextStyle } ;
7764 calendarHeight : number ;
7865
7966 constructor ( props : TimelineProps ) {
8067 super ( props ) ;
8168
8269 const { start = 0 , end = 0 } = this . props ;
83- this . calendarHeight = ( end - start ) * 100 ;
70+ this . calendarHeight = ( end - start ) * HALF_HOUR_BLOCK_HEIGHT ;
8471
8572 this . style = styleConstructor ( props . theme || props . styles , this . calendarHeight ) ;
8673
8774 const width = dimensionWidth - LEFT_MARGIN ;
8875 const packedEvents = populateEvents ( props . events , width , start ) ;
89- const initPosition = min ( map ( packedEvents , 'top' ) ) - this . calendarHeight / ( end - start ) ;
76+ const firstTop = min ( map ( packedEvents , 'top' ) ) ?? 0 ;
77+ const initPosition = firstTop - this . calendarHeight / ( end - start ) ;
9078 const verifiedInitPosition = initPosition < 0 ? 0 : initPosition ;
9179
9280 this . state = {
@@ -123,92 +111,31 @@ export default class Timeline extends Component<TimelineProps, State> {
123111 } , 1 ) ;
124112 }
125113
126- _renderLines ( ) {
127- const { format24h, start = 0 , end = 24 } = this . props ;
128- const offset = this . calendarHeight / ( end - start ) ;
129- const EVENT_DIFF = 20 ;
130-
131- return range ( start , end + 1 ) . map ( ( i , index ) => {
132- let timeText ;
133-
134- if ( i === start ) {
135- timeText = '' ;
136- } else if ( i < 12 ) {
137- timeText = ! format24h ? `${ i } AM` : `${ i } :00` ;
138- } else if ( i === 12 ) {
139- timeText = ! format24h ? `${ i } PM` : `${ i } :00` ;
140- } else if ( i === 24 ) {
141- timeText = ! format24h ? '12 AM' : '23:59' ;
142- } else {
143- timeText = ! format24h ? `${ i - 12 } PM` : `${ i } :00` ;
144- }
145-
146- return [
147- < Text key = { `timeLabel${ i } ` } style = { [ this . style . timeLabel , { top : offset * index - 6 } ] } >
148- { timeText }
149- </ Text > ,
150- i === start ? null : (
151- < View key = { `line${ i } ` } style = { [ this . style . line , { top : offset * index , width : dimensionWidth - EVENT_DIFF } ] } />
152- ) ,
153- < View
154- key = { `lineHalf${ i } ` }
155- style = { [ this . style . line , { top : offset * ( index + 0.5 ) , width : dimensionWidth - EVENT_DIFF } ] }
156- />
157- ] ;
158- } ) ;
159- }
160-
161- _onEventPress ( event : Event ) {
162- if ( this . props . eventTapped ) { //TODO: remove after deprecation
114+ onEventPress = ( eventIndex : number ) => {
115+ const event = this . props . events [ eventIndex ] ;
116+ if ( this . props . eventTapped ) {
117+ //TODO: remove after deprecation
163118 this . props . eventTapped ( event ) ;
164119 } else {
165120 this . props . onEventPress ?.( event ) ;
166121 }
167- }
122+ } ;
168123
169- _renderEvents ( ) {
124+ renderEvents ( ) {
170125 const { packedEvents} = this . state ;
171- const events = packedEvents . map ( ( event : any , i : number ) => {
172- const style = {
173- left : event . left ,
174- height : event . height ,
175- width : event . width ,
176- top : event . top ,
177- backgroundColor : event . color ? event . color : '#add8e6'
178- } ;
179-
180- // Fixing the number of lines for the event title makes this calculation easier.
181- // However it would make sense to overflow the title to a new line if needed
182- const numberOfLines = Math . floor ( event . height / TEXT_LINE_HEIGHT ) ;
183- const formatTime = this . props . format24h ? 'HH:mm' : 'hh:mm A' ;
126+ const { format24h, renderEvent} = this . props ;
184127
128+ const events = packedEvents . map ( ( event : PackedEvent , i : number ) => {
185129 return (
186- < TouchableOpacity
187- activeOpacity = { 0.9 }
188- onPress = { ( ) => this . _onEventPress ( this . props . events [ event . index ] ) }
130+ < EventBlock
189131 key = { i }
190- style = { [ this . style . event , style ] }
191- >
192- { this . props . renderEvent ? (
193- this . props . renderEvent ( event )
194- ) : (
195- < View >
196- < Text numberOfLines = { 1 } style = { this . style . eventTitle } >
197- { event . title || 'Event' }
198- </ Text >
199- { numberOfLines > 1 ? (
200- < Text numberOfLines = { numberOfLines - 1 } style = { [ this . style . eventSummary ] } >
201- { event . summary || ' ' }
202- </ Text >
203- ) : null }
204- { numberOfLines > 2 ? (
205- < Text style = { this . style . eventTimes } numberOfLines = { 1 } >
206- { new XDate ( event . start ) . toString ( formatTime ) } - { new XDate ( event . end ) . toString ( formatTime ) }
207- </ Text >
208- ) : null }
209- </ View >
210- ) }
211- </ TouchableOpacity >
132+ index = { i }
133+ event = { event }
134+ styles = { this . style }
135+ format24h = { format24h }
136+ onPress = { this . onEventPress }
137+ renderEvent = { renderEvent }
138+ />
212139 ) ;
213140 } ) ;
214141
@@ -220,13 +147,11 @@ export default class Timeline extends Component<TimelineProps, State> {
220147 }
221148
222149 render ( ) {
150+ const { format24h, start, end} = this . props ;
223151 return (
224- < ScrollView
225- ref = { this . scrollView }
226- contentContainerStyle = { [ this . style . contentStyle , { width : dimensionWidth } ] }
227- >
228- { this . _renderLines ( ) }
229- { this . _renderEvents ( ) }
152+ < ScrollView ref = { this . scrollView } contentContainerStyle = { [ this . style . contentStyle , { width : dimensionWidth } ] } >
153+ < TimelineHours start = { start } end = { end } format24h = { format24h } styles = { this . style } />
154+ { this . renderEvents ( ) }
230155 </ ScrollView >
231156 ) ;
232157 }
0 commit comments