Skip to content

Commit 27d4648

Browse files
committed
ExpandableCalendar - close calendar with knob press
remove propTypes Wrap const with useMemo and useCallback
1 parent a9d9394 commit 27d4648

File tree

1 file changed

+89
-92
lines changed

1 file changed

+89
-92
lines changed

src/expandableCalendar/index.tsx

Lines changed: 89 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import first from 'lodash/first';
2-
import values from 'lodash/values';
32
import isFunction from 'lodash/isFunction';
43
import isNumber from 'lodash/isNumber';
54
import throttle from 'lodash/throttle';
65

7-
import PropTypes from 'prop-types';
86
import XDate from 'xdate';
97

108
import 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;
4847
const PAN_GESTURE_THRESHOLD = 30;
4948
const LEFT_ARROW = require('../calendar/img/previous.png');
5049
const RIGHT_ARROW = require('../calendar/img/next.png');
50+
const knobHitSlop = {left: 10, right: 10, top: 10, bottom: 10};
5151

5252
export interface ExpandableCalendarProps extends CalendarListProps {
5353
/** the initial position of the calendar ('open' or 'closed') */
@@ -100,35 +100,37 @@ const headerStyleOverride = {
100100
const 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) => {
592603
export default ExpandableCalendar;
593604

594605
ExpandableCalendar.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-
};
609606
ExpandableCalendar.defaultProps = {
610607
horizontal: true,
611608
initialPosition: Positions.CLOSED,

0 commit comments

Comments
 (0)