Skip to content

Commit c2848b0

Browse files
committed
Support timeline background press for creating new events
1 parent c293f63 commit c2848b0

File tree

4 files changed

+83
-15
lines changed

4 files changed

+83
-15
lines changed

example/src/screens/timelineCalendar.tsx

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import XDate from 'xdate';
22
import React, {Component} from 'react';
3-
// @ts-expect-error
4-
import {ExpandableCalendar, Timeline, CalendarProvider} from 'react-native-calendars';
3+
import {ExpandableCalendar, Timeline, CalendarProvider, TimelineProps} from 'react-native-calendars';
54
import {sameDate} from '../../../src/dateutils';
65

7-
86
const EVENTS = [
97
{
108
start: '2017-09-06 01:30:00',
@@ -81,7 +79,8 @@ const EVENTS = [
8179

8280
export default class TimelineCalendarScreen extends Component {
8381
state = {
84-
currentDate: '2017-09-10'
82+
currentDate: '2017-09-10',
83+
newEvents: []
8584
};
8685

8786
marked = {
@@ -91,7 +90,7 @@ export default class TimelineCalendarScreen extends Component {
9190
'2017-09-10': {marked: true}
9291
};
9392

94-
onDateChanged = date => {
93+
onDateChanged = (date: string) => {
9594
// console.warn('TimelineCalendarScreen onDateChanged: ', date, updateSource);
9695
// fetch and set data for date + week ahead
9796
this.setState({currentDate: date});
@@ -101,7 +100,24 @@ export default class TimelineCalendarScreen extends Component {
101100
// console.warn('TimelineCalendarScreen onMonthChange: ', month, updateSource);
102101
};
103102

103+
createNewEvent: TimelineProps['onEventCreate'] = (timeString, timeObject) => {
104+
const {currentDate, newEvents} = this.state;
105+
106+
const newEvent = {
107+
start: `${currentDate} ${timeString}`,
108+
end: `${currentDate} ${(timeObject.hour + 1).toString().padStart(2, '0')}:${timeObject.minutes
109+
.toString()
110+
.padStart(2, '0')}:00`,
111+
title: 'New Event',
112+
color: '#ffffff'
113+
};
114+
115+
this.setState({newEvents: [...newEvents, newEvent]});
116+
};
117+
104118
render() {
119+
const {newEvents} = this.state;
120+
const events = [...EVENTS, ...newEvents];
105121
return (
106122
<CalendarProvider
107123
date={this.state.currentDate}
@@ -119,8 +135,9 @@ export default class TimelineCalendarScreen extends Component {
119135
<Timeline
120136
format24h={true}
121137
eventTapped={e => e}
122-
events={EVENTS.filter(event => sameDate(new XDate(event.start), new XDate(this.state.currentDate)))}
138+
events={events.filter(event => sameDate(new XDate(event.start), new XDate(this.state.currentDate)))}
123139
scrollToFirst
140+
onEventCreate={this.createNewEvent}
124141
// start={0}
125142
// end={24}
126143
/>

src/timeline/Timeline.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,45 @@ import map from 'lodash/map';
66
import {Theme} from '../types';
77
import styleConstructor from './style';
88
import populateEvents, {HOUR_BLOCK_HEIGHT} from './Packer';
9-
import TimelineHours from './TimelineHours';
9+
import TimelineHours, {TimelineHoursProps} from './TimelineHours';
1010
import EventBlock, {Event, PackedEvent} from './EventBlock';
1111

1212
const LEFT_MARGIN = 60 - 1;
1313
const {width: dimensionWidth} = Dimensions.get('window');
1414

1515
export interface TimelineProps {
1616
events: Event[];
17+
/**
18+
* The timeline day start time
19+
*/
1720
start?: number;
21+
/**
22+
* The timeline day end time
23+
*/
1824
end?: number;
19-
eventTapped?: (event: Event) => void; //TODO: deprecate (prop renamed 'onEventPress', as in the other components).
25+
/**
26+
* @deprecated
27+
* Use onEventPress instead
28+
*/
29+
eventTapped?: (event: Event) => void;
30+
/**
31+
* Handle event press
32+
*/
2033
onEventPress?: (event: Event) => void;
34+
/**
35+
* Pass to handle creation of a new event by long press on the timeline background
36+
*/
37+
onEventCreate?: TimelineHoursProps['onBackgroundLongPress'];
2138
styles?: Theme; //TODO: deprecate (prop renamed 'theme', as in the other components).
2239
theme?: Theme;
2340
scrollToFirst?: boolean;
41+
/**
42+
* Whether to use 24 hours format for the timeline hours
43+
*/
2444
format24h?: boolean;
45+
/**
46+
* Render a custom event block
47+
*/
2548
renderEvent?: (event: PackedEvent) => JSX.Element;
2649
}
2750

@@ -32,6 +55,7 @@ const Timeline = (props: TimelineProps) => {
3255
end = 24,
3356
events = [],
3457
onEventPress,
58+
onEventCreate,
3559
renderEvent,
3660
theme,
3761
scrollToFirst,
@@ -102,7 +126,13 @@ const Timeline = (props: TimelineProps) => {
102126
return (
103127
// @ts-expect-error
104128
<ScrollView ref={scrollView} contentContainerStyle={[styles.current.contentStyle, {width: dimensionWidth}]}>
105-
<TimelineHours start={start} end={end} format24h={format24h} styles={styles.current} />
129+
<TimelineHours
130+
start={start}
131+
end={end}
132+
format24h={format24h}
133+
styles={styles.current}
134+
onBackgroundLongPress={onEventCreate}
135+
/>
106136
{renderEvents()}
107137
</ScrollView>
108138
);

src/timeline/TimelineHours.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
import React, {useMemo} from 'react';
2-
import {View, Text, ViewStyle, TextStyle, Dimensions} from 'react-native';
1+
import React, {useCallback, useMemo} from 'react';
2+
import {View, Text, TouchableWithoutFeedback, ViewStyle, TextStyle, Dimensions, StyleSheet} from 'react-native';
33
import range from 'lodash/range';
44
import {HOUR_BLOCK_HEIGHT} from './Packer';
55

66
const {width: dimensionWidth} = Dimensions.get('window');
77

8-
interface TimelineHoursProps {
8+
export interface TimelineHoursProps {
99
start?: number;
1010
end?: number;
1111
format24h?: boolean;
12+
onBackgroundLongPress?: (timeString: string, time: {hour: number; minutes: number}) => void;
1213
styles: {[key: string]: ViewStyle | TextStyle};
1314
}
1415

1516
const TimelineHours = (props: TimelineHoursProps) => {
16-
const {format24h, start = 0, end = 24, styles} = props;
17+
const {format24h, start = 0, end = 24, styles, onBackgroundLongPress} = props;
1718
// const offset = this.calendarHeight / (end - start);
1819
const offset = HOUR_BLOCK_HEIGHT;
1920
const EVENT_DIFF = 20;
@@ -37,8 +38,23 @@ const TimelineHours = (props: TimelineHoursProps) => {
3738
});
3839
}, [start, end, format24h]);
3940

41+
const handleBackgroundPress = useCallback(event => {
42+
const yPosition = event.nativeEvent.locationY;
43+
const halfHourBlockHeight = HOUR_BLOCK_HEIGHT / 2;
44+
let time = yPosition / halfHourBlockHeight / 2;
45+
time = Math.round(time * 2) / 2;
46+
47+
const hour = Math.floor(time);
48+
const minutes = (time - Math.floor(time)) * 60;
49+
const timeString = `${hour.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00`;
50+
onBackgroundLongPress?.(timeString, {hour, minutes});
51+
}, []);
52+
4053
return (
4154
<>
55+
<TouchableWithoutFeedback onLongPress={handleBackgroundPress}>
56+
<View style={StyleSheet.absoluteFillObject} />
57+
</TouchableWithoutFeedback>
4258
{hours.map(({timeText, time}, index) => {
4359
return (
4460
<React.Fragment key={time}>

tsconfig.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@
1919
"declaration": true,
2020
"baseUrl": ".",
2121
"paths": {
22-
"react-native-calendars": ["src/index.ts"],
22+
"react-native-calendars": ["src/index.ts"]
2323
}
2424
},
2525
// "include": ["src/calendar/day/basic", "src/global.d.ts"],
26-
"include": ["src/**/*", "example/src/screens/calendars.tsx", "example/src/screens/expandableCalendar.tsx"],
26+
"include": [
27+
"src/**/*",
28+
"example/src/screens/calendars.tsx",
29+
"example/src/screens/expandableCalendar.tsx",
30+
"example/src/screens/timelineCalendar.tsx"
31+
],
2732
"exclude": ["node_modules", "testUtils"]
2833
}

0 commit comments

Comments
 (0)