Skip to content

Open and close the calendar with a trigger event #2632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
MathieuVce opened this issue Apr 2, 2025 · 4 comments
Open

Open and close the calendar with a trigger event #2632

MathieuVce opened this issue Apr 2, 2025 · 4 comments

Comments

@MathieuVce
Copy link

MathieuVce commented Apr 2, 2025

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch [email protected] for the project I'm working on.

I want to open/close ExpandableCalendar programmatically, on a trigger event. I decided to use the ref of the child component and call a function who will trigger the toggle function and return the state of the position.

Here is the diff that solved my problem:

diff --git a/node_modules/react-native-calendars/src/expandableCalendar/index.d.ts b/node_modules/react-native-calendars/src/expandableCalendar/index.d.ts
index e6e2bff..8d3ac85 100644
--- a/node_modules/react-native-calendars/src/expandableCalendar/index.d.ts
+++ b/node_modules/react-native-calendars/src/expandableCalendar/index.d.ts
@@ -28,6 +28,11 @@ export interface ExpandableCalendarProps extends CalendarListProps {
     closeThreshold?: number;
     /** Whether to close the calendar on day press. Default = true */
     closeOnDayPress?: boolean;
+    /** reference to component */
+    ref?: React.MutableRefObject<ExpandableCalendarHandle | null>
+}
+export interface ExpandableCalendarHandle {
+    toggleCalendar: () => Positions;
 }
 /**
  * @description: Expandable calendar component
@@ -51,5 +56,6 @@ declare const ExpandableCalendar: {
         closeOnDayPress: boolean;
     };
     positions: typeof Positions;
+    ref: ExpandableCalendarHandle;
 };
 export default ExpandableCalendar;
diff --git a/node_modules/react-native-calendars/src/expandableCalendar/index.js b/node_modules/react-native-calendars/src/expandableCalendar/index.js
index 495a3d7..59fa9f4 100644
--- a/node_modules/react-native-calendars/src/expandableCalendar/index.js
+++ b/node_modules/react-native-calendars/src/expandableCalendar/index.js
@@ -3,7 +3,7 @@ import isFunction from 'lodash/isFunction';
 import isNumber from 'lodash/isNumber';
 import throttle from 'lodash/throttle';
 import XDate from 'xdate';
-import React, { useContext, useRef, useState, useEffect, useCallback, useMemo } from 'react';
+import React, { useContext, useRef, useState, useEffect, useCallback, useMemo, useImperativeHandle, forwardRef } from 'react';
 import { AccessibilityInfo, PanResponder, Animated, View, Text, Image, TouchableOpacity } from 'react-native';
 import { page } from '../dateutils';
 import { parseDate, toMarkingFormat } from '../interface';
@@ -50,7 +50,7 @@ const headerStyleOverride = {
  * @extendslink: docs/CalendarList
  * @example: https://github.com/wix/react-native-calendars/blob/master/example/src/screens/expandableCalendar.js
  */
-const ExpandableCalendar = (props) => {
+const ExpandableCalendar = forwardRef((props, ref) => {
     const { date, setDate, numberOfDays, timelineLeftInset } = useContext(Context);
     const {
     /** ExpandableCalendar props */
@@ -62,6 +62,14 @@ const ExpandableCalendar = (props) => {
     const onHeaderLayout = useCallback(({ nativeEvent: { layout: { height } } }) => {
         setHeaderHeight(height);
     }, []);
+    /** handle toggling calendar */
+    useImperativeHandle(ref, () => ({
+        toggleCalendar: () => {
+            toggleCalendarPosition(true);
+            const newState = position === Positions.CLOSED ? Positions.OPEN : Positions.CLOSED;
+            return newState;
+          },
+    }));
     /** Date */
     const getYear = (date) => {
         const d = new XDate(date);
@@ -228,9 +236,9 @@ const ExpandableCalendar = (props) => {
     }, [horizontal, isOpen, firstDay, numberOfDays, setDate, date]);
     /** Pan Gesture */
     const handleMoveShouldSetPanResponder = (_, gestureState) => {
-        if (disablePan) {
-            return false;
-        }
+        // if (disablePan) {
+        //     return false;
+        // }
         if (!horizontal && isOpen) {
             // disable pan detection when vertical calendar is open to allow calendar scroll
             return false;
@@ -273,8 +281,8 @@ const ExpandableCalendar = (props) => {
         onPanResponderTerminate: handlePanResponderEnd
     }) : PanResponder.create({}), [numberOfDays, position, headerHeight]); // All the functions here rely on headerHeight directly or indirectly through refs that are updated in useEffect
     /** Animated */
-    const bounceToPosition = (toValue = 0) => {
-        if (!disablePan) {
+    const bounceToPosition = (toValue = 0, manually = false) => {
+        if (manually || (!disablePan && !manually)) {
             const threshold = isOpen ? openHeight.current - closeThreshold : closedHeight + openThreshold;
             let _isOpen = _height.current >= threshold;
             const newValue = _isOpen ? openHeight.current : closedHeight;
@@ -317,8 +325,8 @@ const ExpandableCalendar = (props) => {
             }
         }, 0);
     }, [isOpen, closedHeight]);
-    const toggleCalendarPosition = useCallback(() => {
-        bounceToPosition(isOpen ? closedHeight : openHeight.current);
+    const toggleCalendarPosition = useCallback((manually = false) => {
+        bounceToPosition(isOpen ? closedHeight : openHeight.current, manually);
     }, [isOpen, bounceToPosition, closedHeight]);
     /** Events */
     const _onPressArrowLeft = useCallback((method, month) => {
@@ -361,7 +369,7 @@ const ExpandableCalendar = (props) => {
                         numberOfWeeks.current = _numberOfWeeks;
                         openHeight.current = getOpenHeight();
                         if (isOpen) {
-                            bounceToPosition(openHeight.current);
+                            bounceToPosition(openHeight.current, true);
                         }
                     }
                 }, 0);
@@ -413,7 +421,7 @@ const ExpandableCalendar = (props) => {
           {!horizontal && renderAnimatedHeader()}
         </Animated.View>)}
     </View>);
-};
+});
 export default ExpandableCalendar;
 ExpandableCalendar.displayName = 'ExpandableCalendar';
 ExpandableCalendar.defaultProps = {
@@ -427,4 +435,5 @@ ExpandableCalendar.defaultProps = {
     closeThreshold: PAN_GESTURE_THRESHOLD,
     closeOnDayPress: true
 };
+ExpandableCalendar.ref = null;
 ExpandableCalendar.positions = Positions;

This issue body was partially generated by patch-package.

@nitzanyiz
Copy link
Contributor

Hi 👋
This functionality will be added in the next release.

@MathieuVce
Copy link
Author

Hi 👋 This functionality will be added in the next release.

Hi,
I’ve tested the new release, but a few things are still missing, especially related to the calendar toggling. For example, the disablePan prop should be set to true when toggling manually, but it’s not being handled correctly at the moment.
Could you please take a closer look at the diff I posted? Thanks a lot !

@nitzanyiz
Copy link
Contributor

Hi @MathieuVce , Did you try with 1.1311.1?

@MathieuVce
Copy link
Author

MathieuVce commented May 15, 2025

Hello @nitzanyiz ! Of course, I implemented it as soon as it was released. However, as I mentioned earlier, when we manually trigger the calendar, we don't want the pan gesture to activate it anymore. Setting disablePan to true prevents this, but it also stops the month toggle from updating since panGesture remains enabled internally.

My previous diff addresses this by ensuring that the height animation still functions correctly even when disablePan is true, because it recognizes that the toggle is being triggered manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants