11import first from 'lodash/first' ;
2- import values from 'lodash/values' ;
32import isFunction from 'lodash/isFunction' ;
43import isNumber from 'lodash/isNumber' ;
54import throttle from 'lodash/throttle' ;
65
7- import PropTypes from 'prop-types' ;
86import XDate from 'xdate' ;
97
108import React , { useContext , useRef , useState , useEffect , useCallback , useMemo } from 'react' ;
@@ -17,7 +15,8 @@ import {
1715 Image ,
1816 ImageSourcePropType ,
1917 GestureResponderEvent ,
20- PanResponderGestureState
18+ PanResponderGestureState ,
19+ TouchableOpacity
2120} from 'react-native' ;
2221
2322// @ts -expect-error
@@ -48,6 +47,7 @@ const DAY_NAMES_PADDING = 24;
4847const PAN_GESTURE_THRESHOLD = 30 ;
4948const LEFT_ARROW = require ( '../calendar/img/previous.png' ) ;
5049const RIGHT_ARROW = require ( '../calendar/img/next.png' ) ;
50+ const knobHitSlop = { left : 10 , right : 10 , top : 10 , bottom : 10 } ;
5151
5252export interface ExpandableCalendarProps extends CalendarListProps {
5353 /** the initial position of the calendar ('open' or 'closed') */
@@ -100,35 +100,37 @@ const headerStyleOverride = {
100100const ExpandableCalendar = ( props : ExpandableCalendarProps ) => {
101101 const { date, setDate, numberOfDays = 1 , timelineLeftInset} = useContext ( Context ) ;
102102 const {
103- theme,
104- horizontal = true ,
103+ /** ExpandableCalendar props */
105104 initialPosition = Positions . CLOSED ,
106- firstDay = 0 ,
105+ onCalendarToggled ,
107106 disablePan,
107+ hideKnob = numberOfDays > 1 ,
108+ leftArrowImageSource = LEFT_ARROW ,
109+ rightArrowImageSource = RIGHT_ARROW ,
110+ allowShadow = true ,
108111 disableWeekScroll,
109112 openThreshold = PAN_GESTURE_THRESHOLD ,
110113 closeThreshold = PAN_GESTURE_THRESHOLD ,
111- onCalendarToggled,
112114 closeOnDayPress = true ,
113- onDayPress,
114- style : propsStyle ,
115+
116+ /** CalendarList props */
117+ horizontal = true ,
115118 calendarStyle,
116- allowShadow = true ,
117- hideKnob = numberOfDays > 1 ,
119+ theme,
120+ style : propsStyle ,
121+ firstDay = 0 ,
122+ markedDates,
123+ onDayPress,
118124 hideArrows,
119125 onPressArrowLeft,
120126 onPressArrowRight,
121127 renderArrow,
122- rightArrowImageSource = RIGHT_ARROW ,
123- leftArrowImageSource = LEFT_ARROW ,
124- markedDates,
125128 testID,
126129 ...others
127130 } = props ;
128131
129132 /** Date */
130133
131- const initialDate = date ;
132134 const getYear = ( date : string ) => {
133135 const d = new XDate ( date ) ;
134136 return d . getFullYear ( ) ;
@@ -286,7 +288,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
286288 }
287289 } ;
288290
289- const scrollPage = ( next : boolean ) => {
291+ const scrollPage = useCallback ( ( next : boolean ) => {
290292 if ( horizontal ) {
291293 const d = parseDate ( date ) ;
292294
@@ -310,7 +312,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
310312
311313 setDate ?.( toMarkingFormat ( d ) , updateSources . PAGE_SCROLL ) ;
312314 }
313- } ;
315+ } , [ horizontal , isOpen , firstDay , numberOfDays , setDate ] ) ;
314316
315317 /** Pan Gesture */
316318
@@ -322,7 +324,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
322324 // disable pan detection when vertical calendar is open to allow calendar scroll
323325 return false ;
324326 }
325- if ( position === Positions . CLOSED && gestureState . dy < 0 ) {
327+ if ( ! isOpen && gestureState . dy < 0 ) {
326328 // disable pan detection to limit to closed height
327329 return false ;
328330 }
@@ -338,7 +340,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
338340 _headerStyles . style . top = Math . min ( Math . max ( - gestureState . dy , - HEADER_HEIGHT ) , 0 ) ;
339341 } else {
340342 // horizontal Week view
341- if ( position === Positions . CLOSED ) {
343+ if ( ! isOpen ) {
342344 _weekCalendarStyles . style . opacity = Math . min ( 1 , Math . max ( 1 - gestureState . dy / 100 , 0 ) ) ;
343345 }
344346 }
@@ -351,12 +353,12 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
351353 bounceToPosition ( ) ;
352354 } ;
353355
354- const panResponder = numberOfDays <= 1 ? PanResponder . create ( {
356+ const panResponder = useMemo ( ( ) => numberOfDays <= 1 ? PanResponder . create ( {
355357 onMoveShouldSetPanResponder : handleMoveShouldSetPanResponder ,
356358 onPanResponderMove : handlePanResponderMove ,
357359 onPanResponderRelease : handlePanResponderEnd ,
358360 onPanResponderTerminate : handlePanResponderEnd
359- } ) : PanResponder . create ( { } ) ;
361+ } ) : PanResponder . create ( { } ) , [ numberOfDays ] ) ;
360362
361363 /** Animated */
362364
@@ -403,6 +405,15 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
403405 }
404406 } ;
405407
408+ const closeCalendar = ( ) => {
409+ setTimeout ( ( ) => {
410+ // to allows setDate to be completed
411+ if ( isOpen ) {
412+ bounceToPosition ( closedHeight ) ;
413+ }
414+ } , 0 ) ;
415+ } ;
416+
406417 /** Events */
407418
408419 const _onPressArrowLeft = useCallback ( ( method : ( ) => void , month ?: XDate ) => {
@@ -417,16 +428,9 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
417428
418429 const _onDayPress = useCallback ( ( value : DateData ) => {
419430 numberOfDays <= 1 && setDate ?.( value . dateString , updateSources . DAY_PRESS ) ;
420-
421431 if ( closeOnDayPress ) {
422- setTimeout ( ( ) => {
423- // to allows setDate to be completed
424- if ( isOpen ) {
425- bounceToPosition ( closedHeight ) ;
426- }
427- } , 0 ) ;
432+ closeCalendar ( ) ;
428433 }
429-
430434 onDayPress ?.( value ) ;
431435 } , [ onDayPress , closeOnDayPress , isOpen , numberOfDays ] ) ;
432436
@@ -462,12 +466,24 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
462466 } , 0 ) ;
463467 }
464468 }
465- } ,
466- 100 ,
467- { trailing : true , leading : false }
469+ } , 100 , { trailing : true , leading : false }
468470 ) , [ date , scrollPage ] ) ;
469471
470472 /** Renders */
473+
474+ const _renderArrow = useCallback ( ( direction : Direction ) => {
475+ if ( isFunction ( renderArrow ) ) {
476+ return renderArrow ( direction ) ;
477+ }
478+
479+ return (
480+ < Image
481+ source = { direction === 'right' ? rightArrowImageSource : leftArrowImageSource }
482+ style = { style . current . arrowImage }
483+ testID = { `${ testID } -${ direction } -arrow` }
484+ />
485+ ) ;
486+ } , [ renderArrow , rightArrowImageSource , leftArrowImageSource , testID ] ) ;
471487
472488 const renderWeekDaysNames = ( ) => {
473489 return (
@@ -480,7 +496,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
480496 ) ;
481497 } ;
482498
483- const renderHeader = ( ) => {
499+ const renderAnimatedHeader = ( ) => {
484500 const monthYear = new XDate ( date ) . toString ( 'MMMM yyyy' ) ;
485501
486502 return (
@@ -497,6 +513,14 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
497513 ) ;
498514 } ;
499515
516+ const renderKnob = ( ) => {
517+ return (
518+ < View style = { style . current . knobContainer } testID = { `${ testID } -knob` } pointerEvents = { 'box-none' } >
519+ < TouchableOpacity style = { style . current . knob } testID = { CALENDAR_KNOB } onPress = { closeCalendar } hitSlop = { knobHitSlop } />
520+ </ View >
521+ ) ;
522+ } ;
523+
500524 const renderWeekCalendar = ( ) => {
501525 const WeekComponent = disableWeekScroll ? Week : WeekCalendar ;
502526 const weekCalendarProps = disableWeekScroll ? undefined : { allowShadow : false } ;
@@ -505,41 +529,50 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
505529 < Animated . View
506530 ref = { weekCalendar }
507531 style = { weekCalendarStyle }
508- pointerEvents = { position === Positions . CLOSED ? 'auto' : 'none' }
532+ pointerEvents = { ! isOpen ? 'auto' : 'none' }
509533 >
510534 < WeekComponent
511- { ...props }
535+ testID = "week_calendar"
536+ firstDay = { firstDay }
537+ { ...others }
512538 { ...weekCalendarProps }
513539 current = { date }
514- onDayPress = { _onDayPress }
540+ markedDates = { _markedDates }
541+ theme = { themeObject }
515542 style = { calendarStyle }
516543 hideDayNames = { true }
544+ onDayPress = { _onDayPress }
517545 accessibilityElementsHidden // iOS
518546 importantForAccessibility = { 'no-hide-descendants' } // Android
519547 />
520548 </ Animated . View >
521549 ) ;
522550 } ;
523551
524- const renderKnob = ( ) => {
525- // TODO: turn to TouchableOpacity with onPress that closes it
552+ const renderCalendarList = ( ) => {
526553 return (
527- < View style = { style . current . knobContainer } pointerEvents = { 'none' } testID = { `${ testID } -knob` } >
528- < View style = { style . current . knob } testID = { CALENDAR_KNOB } />
529- </ View >
530- ) ;
531- } ;
532-
533- const _renderArrow = ( direction : Direction ) => {
534- if ( isFunction ( renderArrow ) ) {
535- return renderArrow ( direction ) ;
536- }
537-
538- return (
539- < Image
540- source = { direction === 'right' ? rightArrowImageSource : leftArrowImageSource }
541- style = { style . current . arrowImage }
542- testID = { `${ testID } -${ direction } -arrow` }
554+ < CalendarList
555+ testID = "calendar"
556+ horizontal = { horizontal }
557+ firstDay = { firstDay }
558+ calendarStyle = { calendarStyle }
559+ { ...others }
560+ markedDates = { _markedDates }
561+ theme = { themeObject }
562+ ref = { calendar }
563+ // current={date}
564+ onDayPress = { _onDayPress }
565+ onVisibleMonthsChange = { onVisibleMonthsChange }
566+ pagingEnabled
567+ scrollEnabled = { isOpen }
568+ hideArrows = { shouldHideArrows }
569+ onPressArrowLeft = { _onPressArrowLeft }
570+ onPressArrowRight = { _onPressArrowRight }
571+ hideExtraDays = { ! horizontal && isOpen }
572+ renderArrow = { _renderArrow }
573+ staticHeader
574+ numberOfDays = { numberOfDays }
575+ timelineLeftInset = { timelineLeftInset }
543576 />
544577 ) ;
545578 } ;
@@ -557,32 +590,10 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
557590 />
558591 ) : (
559592 < Animated . View ref = { wrapper } style = { wrapperStyle } { ...panResponder . panHandlers } >
560- < CalendarList
561- testID = "calendar"
562- horizontal = { horizontal }
563- firstDay = { firstDay }
564- calendarStyle = { calendarStyle }
565- { ...others }
566- markedDates = { _markedDates }
567- theme = { themeObject }
568- ref = { calendar }
569- current = { initialDate }
570- onDayPress = { _onDayPress }
571- onVisibleMonthsChange = { onVisibleMonthsChange }
572- pagingEnabled
573- scrollEnabled = { isOpen }
574- hideArrows = { shouldHideArrows }
575- onPressArrowLeft = { _onPressArrowLeft }
576- onPressArrowRight = { _onPressArrowRight }
577- hideExtraDays = { ! horizontal && isOpen }
578- renderArrow = { _renderArrow }
579- staticHeader
580- numberOfDays = { numberOfDays }
581- timelineLeftInset = { timelineLeftInset }
582- />
593+ { renderCalendarList ( ) }
583594 { renderWeekCalendar ( ) }
584595 { ! hideKnob && renderKnob ( ) }
585- { ! horizontal && renderHeader ( ) }
596+ { ! horizontal && renderAnimatedHeader ( ) }
586597 </ Animated . View >
587598 ) }
588599 </ View >
@@ -592,20 +603,6 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
592603export default ExpandableCalendar ;
593604
594605ExpandableCalendar . displayName = 'ExpandableCalendar' ;
595- ExpandableCalendar . propTypes = {
596- ...CalendarList . propTypes ,
597- initialPosition : PropTypes . oneOf ( values ( Positions ) ) ,
598- onCalendarToggled : PropTypes . func ,
599- disablePan : PropTypes . bool ,
600- hideKnob : PropTypes . bool ,
601- leftArrowImageSource : PropTypes . oneOfType ( [ PropTypes . object , PropTypes . number , PropTypes . func ] ) ,
602- rightArrowImageSource : PropTypes . oneOfType ( [ PropTypes . object , PropTypes . number , PropTypes . func ] ) ,
603- allowShadow : PropTypes . bool ,
604- disableWeekScroll : PropTypes . bool ,
605- openThreshold : PropTypes . number ,
606- closeThreshold : PropTypes . number ,
607- closeOnDayPress : PropTypes . bool
608- } ;
609606ExpandableCalendar . defaultProps = {
610607 horizontal : true ,
611608 initialPosition : Positions . CLOSED ,
0 commit comments