UNPKG

react-native-calendars

Version:
100 lines (99 loc) 5.3 kB
import min from 'lodash/min'; import map from 'lodash/map'; import times from 'lodash/times'; import groupBy from 'lodash/groupBy'; import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { View, ScrollView } from 'react-native'; import constants from '../commons/constants'; import { generateDay } from '../dateutils'; import { getCalendarDateString } from '../services'; import styleConstructor from './style'; import { populateEvents, HOUR_BLOCK_HEIGHT } from './Packer'; import { calcTimeOffset } from './helpers/presenter'; import TimelineHours from './TimelineHours'; import EventBlock from './EventBlock'; import NowIndicator from './NowIndicator'; import useTimelineOffset from './useTimelineOffset'; const Timeline = (props) => { const { format24h = true, start = 0, end = 24, date = '', events, onEventPress, onBackgroundLongPress, onBackgroundLongPressOut, renderEvent, theme, scrollToFirst, scrollToNow, initialTime, showNowIndicator, scrollOffset, onChangeOffset, overlapEventsSpacing = 0, rightEdgeSpacing = 0, unavailableHours, unavailableHoursColor, eventTapped, numberOfDays = 1, timelineLeftInset = 0, testID } = props; const pageDates = useMemo(() => { return typeof date === 'string' ? [date] : date; }, [date]); const groupedEvents = useMemo(() => { return groupBy(events, e => getCalendarDateString(e.start)); }, [events]); const pageEvents = useMemo(() => { return map(pageDates, d => groupedEvents[d] || []); }, [pageDates, groupedEvents]); const scrollView = useRef(); const calendarHeight = useRef((end - start) * HOUR_BLOCK_HEIGHT); const styles = useRef(styleConstructor(theme || props.styles, calendarHeight.current)); const { scrollEvents } = useTimelineOffset({ onChangeOffset, scrollOffset, scrollViewRef: scrollView }); const width = useMemo(() => { return constants.screenWidth - timelineLeftInset; }, [timelineLeftInset]); const packedEvents = useMemo(() => { return map(pageEvents, (_e, i) => { return populateEvents(pageEvents[i], { screenWidth: width / numberOfDays, dayStart: start, overlapEventsSpacing: overlapEventsSpacing / numberOfDays, rightEdgeSpacing: rightEdgeSpacing / numberOfDays }); }); }, [pageEvents, start, numberOfDays]); useEffect(() => { let initialPosition = 0; if (scrollToNow) { initialPosition = calcTimeOffset(HOUR_BLOCK_HEIGHT); } else if (scrollToFirst && packedEvents[0].length > 0) { initialPosition = min(map(packedEvents[0], 'top')) ?? 0; } else if (initialTime) { initialPosition = calcTimeOffset(HOUR_BLOCK_HEIGHT, initialTime.hour, initialTime.minutes); } if (initialPosition) { setTimeout(() => { scrollView?.current?.scrollTo({ y: Math.max(0, initialPosition - HOUR_BLOCK_HEIGHT), animated: true }); }, 0); } }, []); const _onEventPress = useCallback((dateIndex, eventIndex) => { const event = packedEvents[dateIndex][eventIndex]; if (eventTapped) { //TODO: remove after deprecation eventTapped(event); } else { onEventPress?.(event); } }, [onEventPress, eventTapped]); const renderEvents = (dayIndex) => { const events = packedEvents[dayIndex].map((event, eventIndex) => { const onEventPress = () => _onEventPress(dayIndex, eventIndex); return (<EventBlock key={eventIndex} index={eventIndex} event={event} styles={styles.current} format24h={format24h} onPress={onEventPress} renderEvent={renderEvent} testID={`${testID}.event.${event.id}`}/>); }); return (<View pointerEvents={'box-none'} style={[{ marginLeft: dayIndex === 0 ? timelineLeftInset : undefined }, styles.current.eventsContainer]}> {events} </View>); }; const renderTimelineDay = (dayIndex) => { const indexOfToday = pageDates.indexOf(generateDay(new Date().toString())); const left = timelineLeftInset + indexOfToday * width / numberOfDays; return (<React.Fragment key={dayIndex}> {renderEvents(dayIndex)} {indexOfToday !== -1 && showNowIndicator && <NowIndicator width={width / numberOfDays} left={left} styles={styles.current}/>} </React.Fragment>); }; return (<ScrollView // @ts-expect-error ref={scrollView} style={styles.current.container} contentContainerStyle={[styles.current.contentStyle, { width: constants.screenWidth }]} showsVerticalScrollIndicator={false} {...scrollEvents} testID={testID}> <TimelineHours start={start} end={end} date={pageDates[0]} format24h={format24h} styles={styles.current} unavailableHours={unavailableHours} unavailableHoursColor={unavailableHoursColor} onBackgroundLongPress={onBackgroundLongPress} onBackgroundLongPressOut={onBackgroundLongPressOut} width={width} numberOfDays={numberOfDays} timelineLeftInset={timelineLeftInset} testID={`${testID}.hours`}/> {times(numberOfDays, renderTimelineDay)} </ScrollView>); }; export default React.memo(Timeline);