1- // @flow
1+ import React , { useCallback , useEffect , useMemo , useRef } from 'react' ;
2+ import { View , Dimensions , ScrollView } from 'react-native' ;
23import min from 'lodash/min' ;
34import map from 'lodash/map' ;
4- import PropTypes from 'prop-types' ;
5-
6- import React , { Component } from 'react' ;
7- import { View , Dimensions , ScrollView , TextStyle , ViewStyle } from 'react-native' ;
85
96import { Theme } from '../types' ;
107import styleConstructor from './style' ;
@@ -13,7 +10,6 @@ import TimelineHours from './TimelineHours';
1310import EventBlock , { Event , PackedEvent } from './EventBlock' ;
1411
1512const LEFT_MARGIN = 60 - 1 ;
16-
1713const { width : dimensionWidth } = Dimensions . get ( 'window' ) ;
1814
1915export interface TimelineProps {
@@ -29,111 +25,68 @@ export interface TimelineProps {
2925 renderEvent ?: ( event : PackedEvent ) => JSX . Element ;
3026}
3127
32- interface State {
33- _scrollY : number ;
34- packedEvents : PackedEvent [ ] ;
35- }
36-
37- export default class Timeline extends Component < TimelineProps , State > {
38- static propTypes = {
39- start : PropTypes . number ,
40- end : PropTypes . number ,
41- eventTapped : PropTypes . func , // TODO: remove after deprecation
42- onEventPress : PropTypes . func ,
43- format24h : PropTypes . bool ,
44- events : PropTypes . arrayOf (
45- PropTypes . shape ( {
46- start : PropTypes . string . isRequired ,
47- end : PropTypes . string . isRequired ,
48- title : PropTypes . string . isRequired ,
49- summary : PropTypes . string . isRequired ,
50- color : PropTypes . string
51- } )
52- ) . isRequired
53- } ;
54-
55- static defaultProps = {
56- start : 0 ,
57- end : 24 ,
58- events : [ ] ,
59- format24h : true
60- } ;
61-
62- private scrollView = React . createRef < ScrollView > ( ) ;
63- style : { [ key : string ] : ViewStyle | TextStyle } ;
64- calendarHeight : number ;
65-
66- constructor ( props : TimelineProps ) {
67- super ( props ) ;
68-
69- const { start = 0 , end = 0 } = this . props ;
70- this . calendarHeight = ( end - start ) * HALF_HOUR_BLOCK_HEIGHT ;
71-
72- this . style = styleConstructor ( props . theme || props . styles , this . calendarHeight ) ;
73-
74- const width = dimensionWidth - LEFT_MARGIN ;
75- const packedEvents = populateEvents ( props . events , width , start ) ;
76- const firstTop = min ( map ( packedEvents , 'top' ) ) ?? 0 ;
77- const initPosition = firstTop - this . calendarHeight / ( end - start ) ;
78- const verifiedInitPosition = initPosition < 0 ? 0 : initPosition ;
79-
80- this . state = {
81- _scrollY : verifiedInitPosition ,
82- packedEvents
83- } ;
84- }
85-
86- componentDidUpdate ( prevProps : TimelineProps ) {
28+ const Timeline = ( props : TimelineProps ) => {
29+ const {
30+ format24h = true ,
31+ start = 0 ,
32+ end = 24 ,
33+ events = [ ] ,
34+ onEventPress,
35+ renderEvent,
36+ theme,
37+ scrollToFirst,
38+ eventTapped
39+ } = props ;
40+
41+ const scrollView = useRef < ScrollView > ( ) ;
42+ const calendarHeight = useRef ( ( end - start ) * HALF_HOUR_BLOCK_HEIGHT ) ;
43+ const styles = useRef ( styleConstructor ( theme || props . styles , calendarHeight . current ) ) ;
44+
45+ const packedEvents = useMemo ( ( ) => {
8746 const width = dimensionWidth - LEFT_MARGIN ;
88- const { events : prevEvents , start : prevStart = 0 } = prevProps ;
89- const { events, start = 0 } = this . props ;
90-
91- if ( prevEvents !== events || prevStart !== start ) {
92- this . setState ( {
93- packedEvents : populateEvents ( events , width , start )
94- } ) ;
95- }
96- }
97-
98- componentDidMount ( ) {
99- this . props . scrollToFirst && this . scrollToFirst ( ) ;
100- }
101-
102- scrollToFirst ( ) {
103- setTimeout ( ( ) => {
104- if ( this . state && this . state . _scrollY && this . scrollView ) {
105- this . scrollView ?. current ?. scrollTo ( {
106- x : 0 ,
107- y : this . state . _scrollY ,
108- animated : true
109- } ) ;
47+ return populateEvents ( events , width , start ) ;
48+ } , [ events , start ] ) ;
49+
50+ useEffect ( ( ) => {
51+ if ( scrollToFirst ) {
52+ const firstTop = min ( map ( packedEvents , 'top' ) ) ?? 0 ;
53+ const initPosition = firstTop - calendarHeight . current / ( end - start ) ;
54+ const verifiedInitPosition = initPosition < 0 ? 0 : initPosition ;
55+
56+ if ( verifiedInitPosition ) {
57+ setTimeout ( ( ) => {
58+ scrollView ?. current ?. scrollTo ( {
59+ y : verifiedInitPosition ,
60+ animated : true
61+ } ) ;
62+ } , 0 ) ;
11063 }
111- } , 1 ) ;
112- }
113-
114- onEventPress = ( eventIndex : number ) => {
115- const event = this . props . events [ eventIndex ] ;
116- if ( this . props . eventTapped ) {
117- //TODO: remove after deprecation
118- this . props . eventTapped ( event ) ;
119- } else {
120- this . props . onEventPress ?.( event ) ;
12164 }
122- } ;
123-
124- renderEvents ( ) {
125- const { packedEvents} = this . state ;
126- const { format24h, renderEvent} = this . props ;
65+ } , [ ] ) ;
66+
67+ const _onEventPress = useCallback (
68+ ( eventIndex : number ) => {
69+ const event = events [ eventIndex ] ;
70+ if ( eventTapped ) {
71+ //TODO: remove after deprecation
72+ eventTapped ( event ) ;
73+ } else {
74+ onEventPress ?.( event ) ;
75+ }
76+ } ,
77+ [ events , onEventPress , eventTapped ]
78+ ) ;
12779
80+ const renderEvents = ( ) => {
12881 const events = packedEvents . map ( ( event : PackedEvent , i : number ) => {
12982 return (
13083 < EventBlock
13184 key = { i }
13285 index = { i }
13386 event = { event }
134- styles = { this . style }
87+ styles = { styles . current }
13588 format24h = { format24h }
136- onPress = { this . onEventPress }
89+ onPress = { _onEventPress }
13790 renderEvent = { renderEvent }
13891 />
13992 ) ;
@@ -144,15 +97,15 @@ export default class Timeline extends Component<TimelineProps, State> {
14497 < View style = { { marginLeft : LEFT_MARGIN } } > { events } </ View >
14598 </ View >
14699 ) ;
147- }
100+ } ;
148101
149- render ( ) {
150- const { format24h , start , end } = this . props ;
151- return (
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 ( ) }
155- </ ScrollView >
156- ) ;
157- }
158- }
102+ return (
103+ // @ts -expect-error
104+ < ScrollView ref = { scrollView } contentContainerStyle = { [ styles . current . contentStyle , { width : dimensionWidth } ] } >
105+ < TimelineHours start = { start } end = { end } format24h = { format24h } styles = { styles . current } / >
106+ { renderEvents ( ) }
107+ </ ScrollView >
108+ ) ;
109+ } ;
110+
111+ export default Timeline ;
0 commit comments