-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
[TS migration] Migrate 'PopoverWithMeasuredContent.js' component to TypeScript #32551
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,21 @@ | ||
import PropTypes from 'prop-types'; | ||
import isEqual from 'lodash/isEqual'; | ||
import React, {useMemo, useState} from 'react'; | ||
import {View} from 'react-native'; | ||
import _ from 'underscore'; | ||
import {LayoutChangeEvent, View} from 'react-native'; | ||
import useThemeStyles from '@hooks/useThemeStyles'; | ||
import useWindowDimensions from '@hooks/useWindowDimensions'; | ||
import PopoverWithMeasuredContentUtils from '@libs/PopoverWithMeasuredContentUtils'; | ||
import CONST from '@src/CONST'; | ||
import Popover from './Popover'; | ||
import {defaultProps as defaultPopoverProps, propTypes as popoverPropTypes} from './Popover/popoverPropTypes'; | ||
import {windowDimensionsPropTypes} from './withWindowDimensions'; | ||
import {PopoverProps} from './Popover/types'; | ||
|
||
const propTypes = { | ||
// All popover props except: | ||
// 1) anchorPosition (which is overridden for this component) | ||
// 2) windowDimensionsPropTypes - we exclude them because we use useWindowDimensions hook instead | ||
..._.omit(popoverPropTypes, ['anchorPosition', ..._.keys(windowDimensionsPropTypes)]), | ||
|
||
/** The horizontal and vertical anchors points for the popover */ | ||
anchorPosition: PropTypes.shape({ | ||
horizontal: PropTypes.number.isRequired, | ||
vertical: PropTypes.number.isRequired, | ||
}).isRequired, | ||
|
||
/** How the popover should be aligned. The value you passed will is the part of the component that will be aligned to the | ||
* anchorPosition. ie: vertical:top means the top of the menu will be positioned in the anchorPosition */ | ||
anchorAlignment: PropTypes.shape({ | ||
horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)), | ||
vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)), | ||
}), | ||
|
||
/** Static dimensions for the popover. | ||
* Note: When passed, it will skip dimensions measuring of the popover, and provided dimensions will be used to calculate the anchor position. | ||
*/ | ||
popoverDimensions: PropTypes.shape({ | ||
height: PropTypes.number, | ||
width: PropTypes.number, | ||
}), | ||
type AnchorPosition = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We define this type in 3 different files (styles/index.ts, libs/calculateAnchorPosition.ts and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type reusing added |
||
horizontal: number; | ||
vertical: number; | ||
}; | ||
|
||
const defaultProps = { | ||
...defaultPopoverProps, | ||
|
||
// Default positioning of the popover | ||
anchorAlignment: { | ||
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, | ||
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, | ||
}, | ||
popoverDimensions: { | ||
height: 0, | ||
width: 0, | ||
}, | ||
withoutOverlay: false, | ||
type PopoverWithMeasuredContentProps = Omit<PopoverProps, 'anchorPosition'> & { | ||
/** The horizontal and vertical anchors points for the popover */ | ||
anchorPosition: AnchorPosition; | ||
}; | ||
|
||
/** | ||
|
@@ -60,73 +25,85 @@ const defaultProps = { | |
* anchor position. | ||
*/ | ||
|
||
function PopoverWithMeasuredContent(props) { | ||
function PopoverWithMeasuredContent({ | ||
popoverDimensions = { | ||
height: 0, | ||
width: 0, | ||
}, | ||
anchorPosition, | ||
mountiny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
isVisible, | ||
mountiny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
anchorAlignment = { | ||
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, | ||
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, | ||
}, | ||
children, | ||
withoutOverlay = false, | ||
...props | ||
}: PopoverWithMeasuredContentProps) { | ||
const styles = useThemeStyles(); | ||
const {windowWidth, windowHeight} = useWindowDimensions(); | ||
const [popoverWidth, setPopoverWidth] = useState(props.popoverDimensions.width); | ||
const [popoverHeight, setPopoverHeight] = useState(props.popoverDimensions.height); | ||
const [popoverWidth, setPopoverWidth] = useState(popoverDimensions.width); | ||
const [popoverHeight, setPopoverHeight] = useState(popoverDimensions.height); | ||
const [isContentMeasured, setIsContentMeasured] = useState(popoverWidth > 0 && popoverHeight > 0); | ||
const [isVisible, setIsVisible] = useState(false); | ||
const [isCurrentVisible, setIsCurrentVisible] = useState(false); | ||
pasyukevich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* When Popover becomes visible, we need to recalculate the Dimensions. | ||
* Skip render on Popover until recalculations have done by setting isContentMeasured false as early as possible. | ||
pasyukevich marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
if (!isVisible && props.isVisible) { | ||
if (!isCurrentVisible && isVisible) { | ||
// When Popover is shown recalculate | ||
setIsContentMeasured(props.popoverDimensions.width > 0 && props.popoverDimensions.height > 0); | ||
setIsVisible(true); | ||
} else if (isVisible && !props.isVisible) { | ||
setIsVisible(false); | ||
setIsContentMeasured(popoverDimensions.width > 0 && popoverDimensions.height > 0); | ||
setIsCurrentVisible(true); | ||
} else if (isCurrentVisible && !isVisible) { | ||
setIsCurrentVisible(false); | ||
} | ||
|
||
/** | ||
* Measure the size of the popover's content. | ||
* | ||
* @param {Object} nativeEvent | ||
*/ | ||
const measurePopover = ({nativeEvent}) => { | ||
const measurePopover = ({nativeEvent}: LayoutChangeEvent) => { | ||
setPopoverWidth(nativeEvent.layout.width); | ||
setPopoverHeight(nativeEvent.layout.height); | ||
setIsContentMeasured(true); | ||
}; | ||
|
||
const adjustedAnchorPosition = useMemo(() => { | ||
let horizontalConstraint; | ||
switch (props.anchorAlignment.horizontal) { | ||
switch (anchorAlignment.horizontal) { | ||
case CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT: | ||
horizontalConstraint = {left: props.anchorPosition.horizontal - popoverWidth}; | ||
horizontalConstraint = {left: anchorPosition.horizontal - popoverWidth}; | ||
break; | ||
case CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.CENTER: | ||
horizontalConstraint = { | ||
left: Math.floor(props.anchorPosition.horizontal - popoverWidth / 2), | ||
left: Math.floor(anchorPosition.horizontal - popoverWidth / 2), | ||
}; | ||
break; | ||
case CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT: | ||
default: | ||
horizontalConstraint = {left: props.anchorPosition.horizontal}; | ||
horizontalConstraint = {left: anchorPosition.horizontal}; | ||
} | ||
|
||
let verticalConstraint; | ||
switch (props.anchorAlignment.vertical) { | ||
switch (anchorAlignment.vertical) { | ||
case CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM: | ||
verticalConstraint = {top: props.anchorPosition.vertical - popoverHeight}; | ||
verticalConstraint = {top: anchorPosition.vertical - popoverHeight}; | ||
break; | ||
case CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.CENTER: | ||
verticalConstraint = { | ||
top: Math.floor(props.anchorPosition.vertical - popoverHeight / 2), | ||
top: Math.floor(anchorPosition.vertical - popoverHeight / 2), | ||
}; | ||
break; | ||
case CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP: | ||
default: | ||
verticalConstraint = {top: props.anchorPosition.vertical}; | ||
verticalConstraint = {top: anchorPosition.vertical}; | ||
} | ||
|
||
return { | ||
...horizontalConstraint, | ||
...verticalConstraint, | ||
}; | ||
}, [props.anchorPosition, props.anchorAlignment, popoverWidth, popoverHeight]); | ||
}, [anchorPosition, anchorAlignment, popoverWidth, popoverHeight]); | ||
|
||
const horizontalShift = PopoverWithMeasuredContentUtils.computeHorizontalShift(adjustedAnchorPosition.left, popoverWidth, windowWidth); | ||
const verticalShift = PopoverWithMeasuredContentUtils.computeVerticalShift(adjustedAnchorPosition.top, popoverHeight, windowHeight); | ||
|
@@ -136,11 +113,15 @@ function PopoverWithMeasuredContent(props) { | |
}; | ||
return isContentMeasured ? ( | ||
<Popover | ||
popoverDimensions={popoverDimensions} | ||
anchorAlignment={anchorAlignment} | ||
isVisible={isVisible} | ||
withoutOverlay={withoutOverlay} | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...props} | ||
anchorPosition={shiftedAnchorPosition} | ||
> | ||
<View onLayout={measurePopover}>{props.children}</View> | ||
<View onLayout={measurePopover}>{children}</View> | ||
</Popover> | ||
) : ( | ||
/* | ||
|
@@ -153,18 +134,15 @@ function PopoverWithMeasuredContent(props) { | |
style={styles.invisiblePopover} | ||
onLayout={measurePopover} | ||
> | ||
{props.children} | ||
{children} | ||
</View> | ||
); | ||
} | ||
|
||
PopoverWithMeasuredContent.propTypes = propTypes; | ||
PopoverWithMeasuredContent.defaultProps = defaultProps; | ||
PopoverWithMeasuredContent.displayName = 'PopoverWithMeasuredContent'; | ||
|
||
export default React.memo(PopoverWithMeasuredContent, (prevProps, nextProps) => { | ||
if (prevProps.isVisible === nextProps.isVisible && nextProps.isVisible === false) { | ||
return true; | ||
} | ||
return _.isEqual(prevProps, nextProps); | ||
return isEqual(prevProps, nextProps); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to export those types?
AnchorAlignment
andPopoverDimensions
aren't used in any other file it seemsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reuse added