import dayjs from 'dayjs' ; import * as React from 'react' ; import { Platform , ScrollView , StyleSheet , TextStyle , View , ViewStyle } from 'react-native' ; import { u } from '../commonStyles' ; import { useNow } from '../hooks/useNow' ; import { usePanResponder } from '../hooks/usePanResponder' ; import { EventCellStyle , EventRenderer , HorizontalDirection , ICalendarEventBase , } from '../interfaces' ; import { useTheme } from '../theme/ThemeContext' ; import { getCountOfEventsAtEvent , getOrderOfEvent , getRelativeTopInDay , hours , isToday , typedMemo , } from '../utils' ; import { CalendarEvent } from './CalendarEvent' ; import { HourGuideCell } from './HourGuideCell' ; import { HourGuideColumn } from './HourGuideColumn' ; const styles = StyleSheet . create ( { nowIndicator : { position : 'absolute' , zIndex : 10000 , height : 2 , width : '100%' , } , } ) ; interface CalendarBodyProps < T extends ICalendarEventBase > { cellHeight : number ; containerHeight : number ; dateRange : dayjs . Dayjs [ ] ; events : T [ ] ; scrollOffsetMinutes : number ; ampm : boolean ; showTime : boolean ; style : ViewStyle ; eventCellStyle ? : EventCellStyle < T > ; hideNowIndicator ? : boolean ; overlapOffset ? : number ; onPressCell ? : ( date : Date ) => void ; onPressEvent ? : ( event : T ) => void ; onSwipeHorizontal ? : ( d : HorizontalDirection ) => void ; renderEvent ? : EventRenderer < T > ; headerComponent ? : React . ReactElement | null ; headerComponentStyle ? : ViewStyle ; hourStyle ? : TextStyle ; } function _CalendarBody < T extends ICalendarEventBase > ( { containerHeight , cellHeight , dateRange , style , onPressCell , events , onPressEvent , eventCellStyle , ampm , showTime , scrollOffsetMinutes , onSwipeHorizontal , hideNowIndicator , overlapOffset , renderEvent , headerComponent = null , headerComponentStyle = { } , hourStyle = { } , } ) { const scrollView = React . useRef < ScrollView > ( null ) ; const { now } = useNow ( ! hideNowIndicator ) ; React . useEffect ( ( ) => { if ( scrollView . current && scrollOffsetMinutes && Platform . OS !== 'ios' ) { setTimeout ( ( ) => { if ( scrollView && scrollView . current ) { scrollView . current . scrollTo ( { y : ( cellHeight * scrollOffsetMinutes ) / 60 , animated : false , } ) ; } } , Platform . OS === 'web' ? 0 : 10 ) ; } } , [ scrollView , scrollOffsetMinutes , cellHeight ] ) ; const panResponder = usePanResponder ( { onSwipeHorizontal , } ) ; const _onPressCell = React . useCallback ( ( date ) => { onPressCell && onPressCell ( date . toDate ( ) ) ; } , [ onPressCell ] ) ; const _renderMappedEvent = ( event ) => ( < CalendarEvent key = { `${ event . start }${ event . title }${ event . end }` } event = { event } onPressEvent = { onPressEvent } eventCellStyle = { eventCellStyle } showTime = { showTime } eventCount = { getCountOfEventsAtEvent ( event , events ) } eventOrder = { getOrderOfEvent ( event , events ) } overlapOffset = { overlapOffset } renderEvent = { renderEvent } ampm = { ampm } / > ) ; const theme = useTheme ( ) ; return ( < React . Fragment > { headerComponent != null ? < View style = { headerComponentStyle } > { headerComponent } < /View> : null} < ScrollView style = { [ { height : containerHeight - cellHeight * 3 , } , style , ] } ref = { scrollView } scrollEventThrottle = { 32 } { ... ( Platform . OS !== 'web' ? panResponder . panHandlers : { } ) } showsVerticalScrollIndicator = { false } nestedScrollEnabled contentOffset = { Platform . OS === 'ios' ? { x : 0 , y : scrollOffsetMinutes } : { x : 0 , y : 0 } } > < View style = { [ u [ 'flex-1' ] , theme . isRTL ? u [ 'flex-row-reverse' ] : u [ 'flex-row' ] ] } { ... ( Platform . OS === 'web' ? panResponder . panHandlers : { } ) } > < View style = { [ u [ 'z-20' ] , u [ 'w-50' ] ] } > { hours . map ( ( hour , index ) => ( < HourGuideColumn key = { hour } cellHeight = { cellHeight } hour = { hour } ampm = { ampm } index = { index } hourStyle = { hourStyle } / > ) ) } < /View> { dateRange . map ( ( date ) => ( < View style = { [ u [ 'flex-1' ] , u [ 'overflow-hidden' ] ] } key = { date . toString ( ) } > { hours . map ( ( hour , index ) => ( < HourGuideCell key = { hour } cellHeight = { cellHeight } date = { date } hour = { hour } onPress = { _onPressCell } index = { index } / > ) ) } { } { } { } { events . filter ( ( { start } ) => dayjs ( start ) . isBetween ( date . startOf ( 'day' ) , date . endOf ( 'day' ) , null , '[)' ) ) . map ( _renderMappedEvent ) } { } { } { } { events . filter ( ( { start , end } ) => dayjs ( start ) . isBefore ( date . startOf ( 'day' ) ) && dayjs ( end ) . isBetween ( date . startOf ( 'day' ) , date . endOf ( 'day' ) , null , '[)' ) ) . map ( ( event ) => ( { ... event , start : dayjs ( event . end ) . startOf ( 'day' ) , } ) ) . map ( _renderMappedEvent ) } { } { } { } { events . filter ( ( { start , end } ) => dayjs ( start ) . isBefore ( date . startOf ( 'day' ) ) && dayjs ( end ) . isAfter ( date . endOf ( 'day' ) ) ) . map ( ( event ) => ( { ... event , start : dayjs ( event . end ) . startOf ( 'day' ) , end : dayjs ( event . end ) . endOf ( 'day' ) , } ) ) . map ( _renderMappedEvent ) } { isToday ( date ) && ! hideNowIndicator && ( < View style = { [ styles . nowIndicator , { backgroundColor : theme . palette . nowIndicator } , { top : `${ getRelativeTopInDay ( now ) }%` } , ] } />)} < /View>))} < /View> < /ScrollView> < /React.Fragment>); } export const CalendarBody = typedMemo ( _CalendarBody ) ;