Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/Modal/BaseModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ function BaseModal(
onModalHide={hideModal}
onModalWillShow={() => ComposerFocusManager.resetReadyToFocus()}
onDismiss={handleDismissModal}
onSwipeComplete={onClose}
onSwipeComplete={() => onClose?.()}
swipeDirection={swipeDirection}
isVisible={isVisible}
backdropColor={theme.overlay}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Modal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type BaseModalProps = WindowDimensionsProps &
shouldSetModalVisibility?: boolean;

/** Callback method fired when the user requests to close the modal */
onClose: () => void;
onClose: (ref?: React.RefObject<HTMLElement>) => void;

/** State that determines whether to display the modal or not */
isVisible: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,74 @@
import React, {useMemo} from 'react';
import React, {ForwardedRef, forwardRef, useContext, useEffect, useMemo} from 'react';
import {View} from 'react-native';
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
import {defaultProps, propTypes} from '@components/Popover/popoverPropTypes';
import {PopoverContext} from '@components/PopoverProvider';
import withWindowDimensions from '@components/withWindowDimensions';
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Modal from '@userActions/Modal';
import PopoverWithoutOverlayProps from './types';

function Popover(props) {
function PopoverWithoutOverlay(
{
anchorPosition = {},
anchorRef,
withoutOverlayRef,
innerContainerStyle = {},
outerStyle,
onModalShow = () => {},
isVisible,
onClose,
onModalHide = () => {},
children,
}: PopoverWithoutOverlayProps,
ref: ForwardedRef<View>,
) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {onOpen, close} = React.useContext(PopoverContext);
const {onOpen, close} = useContext(PopoverContext);
const {windowWidth, windowHeight} = useWindowDimensions();
const insets = useSafeAreaInsets();
const {modalStyle, modalContainerStyle, shouldAddTopSafeAreaMargin, shouldAddBottomSafeAreaMargin, shouldAddTopSafeAreaPadding, shouldAddBottomSafeAreaPadding} =
StyleUtils.getModalStyles(
'popover',
{
windowWidth: props.windowWidth,
windowHeight: props.windowHeight,
windowWidth,
windowHeight,
isSmallScreenWidth: false,
},
props.anchorPosition,
props.innerContainerStyle,
props.outerStyle,
anchorPosition,
innerContainerStyle,
outerStyle,
);

useEffect(() => {
let removeOnClose: () => void;
if (isVisible) {
onModalShow();
onOpen?.({
ref: withoutOverlayRef,
close: onClose,
anchorRef,
});
removeOnClose = Modal.setCloseModal(() => onClose(anchorRef));
} else {
onModalHide();
close(anchorRef);
Modal.onModalDidClose();
}
Modal.willAlertModalBecomeVisible(isVisible);

return () => {
if (!removeOnClose) {
return;
}
removeOnClose();
};
// We want this effect to run strictly ONLY when isVisible prop changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isVisible]);

const {
paddingTop: safeAreaPaddingTop,
paddingBottom: safeAreaPaddingBottom,
Expand Down Expand Up @@ -69,58 +111,29 @@ function Popover(props) {
],
);

React.useEffect(() => {
let removeOnClose;
if (props.isVisible) {
props.onModalShow();
onOpen({
ref: props.withoutOverlayRef,
close: props.onClose,
anchorRef: props.anchorRef,
});
removeOnClose = Modal.setCloseModal(() => props.onClose(props.anchorRef));
} else {
props.onModalHide();
close(props.anchorRef);
Modal.onModalDidClose();
}
Modal.willAlertModalBecomeVisible(props.isVisible);

return () => {
if (!removeOnClose) {
return;
}
removeOnClose();
};
// We want this effect to run strictly ONLY when isVisible prop changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.isVisible]);

if (!props.isVisible) {
if (!isVisible) {
return null;
}

return (
<View
style={[modalStyle, {zIndex: 1}]}
ref={props.withoutOverlayRef}
ref={withoutOverlayRef}
>
<View
style={{
...styles.defaultModalContainer,
...modalContainerStyle,
...modalPaddingStyles,
}}
ref={props.forwardedRef}
ref={ref}
>
<ColorSchemeWrapper>{props.children}</ColorSchemeWrapper>
<ColorSchemeWrapper>{children}</ColorSchemeWrapper>
</View>
</View>
);
}

Popover.propTypes = propTypes;
Popover.defaultProps = defaultProps;
Popover.displayName = 'Popover';
PopoverWithoutOverlay.displayName = 'PopoverWithoutOverlay';

export default withWindowDimensions(Popover);
export default forwardRef(PopoverWithoutOverlay);
28 changes: 28 additions & 0 deletions src/components/PopoverWithoutOverlay/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {View} from 'react-native';
import BaseModalProps from '@components/Modal/types';
import ChildrenProps from '@src/types/utils/ChildrenProps';

type PopoverWithoutOverlayProps = ChildrenProps &
Omit<BaseModalProps, 'type' | 'popoverAnchorPosition'> & {
/** The anchor position of the popover */
anchorPosition?: {
top?: number;
right?: number;
bottom?: number;
left?: number;
};

/** The anchor ref of the popover */
anchorRef: React.RefObject<HTMLElement>;

/** A react-native-animatable animation timing for the modal display animation */
animationInTiming?: number;

/** Whether disable the animations */
disableAnimation?: boolean;

/** The ref of the popover */
withoutOverlayRef: React.RefObject<HTMLElement & View>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any issue with this suggestion?

Suggested change
withoutOverlayRef: React.RefObject<HTMLElement & View>;
withoutOverlayRef?: React.RefObject<HTMLElement>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@situchan I've found only one PopoverWithoutOverlay component usage and it's not optional. If we get rid of View in withoutOverlayRef typing we will have TS issue since this ref is then passed to the View. What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok and what about & View part?
No issue itself but just asking because it's inconsistent with other ref typings

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@situchan As I mentioned, if we get rid of & View part we will have TS issue in the place where we pass withoutOverlayRef to the View ref.

We also have similar cases in the code: one, two

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok no problem

};

export default PopoverWithoutOverlayProps;