Skip to content

Commit 64cd964

Browse files
authored
Merge pull request #1723 from wix/infra/refactorTimeline_2
Refactor Timeline component - part2
2 parents f4cb153 + f1c2c64 commit 64cd964

File tree

2 files changed

+66
-113
lines changed

2 files changed

+66
-113
lines changed

example/src/screens/timelineCalendar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ const EVENTS = [
8181

8282
export default class TimelineCalendarScreen extends Component {
8383
state = {
84-
currentDate: '2017-09-07'
84+
currentDate: '2017-09-10'
8585
};
8686

8787
marked = {
@@ -120,7 +120,7 @@ export default class TimelineCalendarScreen extends Component {
120120
format24h={true}
121121
eventTapped={e => e}
122122
events={EVENTS.filter(event => sameDate(new XDate(event.start), new XDate(this.state.currentDate)))}
123-
// scrollToFirst={true}
123+
scrollToFirst
124124
// start={0}
125125
// end={24}
126126
/>

src/timeline/Timeline.tsx

Lines changed: 64 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
// @flow
1+
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
2+
import {View, Dimensions, ScrollView} from 'react-native';
23
import min from 'lodash/min';
34
import 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

96
import {Theme} from '../types';
107
import styleConstructor from './style';
@@ -13,7 +10,6 @@ import TimelineHours from './TimelineHours';
1310
import EventBlock, {Event, PackedEvent} from './EventBlock';
1411

1512
const LEFT_MARGIN = 60 - 1;
16-
1713
const {width: dimensionWidth} = Dimensions.get('window');
1814

1915
export 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

Comments
 (0)