From 00ae1e641cc5b82396e8a8ba0d465b624ae4d225 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Wed, 6 Dec 2023 10:46:34 +0100 Subject: [PATCH 1/2] Migrate 'PopoverWithMeasuredContent.js' component to TypeScript --- src/components/Popover/types.ts | 2 +- ...tent.js => PopoverWithMeasuredContent.tsx} | 120 +++++++----------- 2 files changed, 50 insertions(+), 72 deletions(-) rename src/components/{PopoverWithMeasuredContent.js => PopoverWithMeasuredContent.tsx} (54%) diff --git a/src/components/Popover/types.ts b/src/components/Popover/types.ts index 7f7e2829770c..e76639bd763e 100644 --- a/src/components/Popover/types.ts +++ b/src/components/Popover/types.ts @@ -39,4 +39,4 @@ type PopoverProps = BaseModalProps & { type PopoverWithWindowDimensionsProps = PopoverProps & WindowDimensionsProps; -export type {PopoverProps, PopoverWithWindowDimensionsProps}; +export type {PopoverProps, PopoverWithWindowDimensionsProps, AnchorAlignment, PopoverDimensions}; diff --git a/src/components/PopoverWithMeasuredContent.js b/src/components/PopoverWithMeasuredContent.tsx similarity index 54% rename from src/components/PopoverWithMeasuredContent.js rename to src/components/PopoverWithMeasuredContent.tsx index 7de5be113e53..fec9ac31f8fe 100644 --- a/src/components/PopoverWithMeasuredContent.js +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -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 = { + 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 & { + /** The horizontal and vertical anchors points for the popover */ + anchorPosition: AnchorPosition; }; /** @@ -60,32 +25,44 @@ const defaultProps = { * anchor position. */ -function PopoverWithMeasuredContent(props) { +function PopoverWithMeasuredContent({ + popoverDimensions = { + height: 0, + width: 0, + }, + anchorPosition, + isVisible, + 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); /** * 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. */ - 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); @@ -93,40 +70,40 @@ function PopoverWithMeasuredContent(props) { 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 ? ( - {props.children} + {children} ) : ( /* @@ -153,18 +134,15 @@ function PopoverWithMeasuredContent(props) { style={styles.invisiblePopover} onLayout={measurePopover} > - {props.children} + {children} ); } - -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); }); From 805305812081b501356826c303ed1d2370bb9b17 Mon Sep 17 00:00:00 2001 From: Yauheni Pasiukevich Date: Tue, 19 Dec 2023 12:54:55 +0100 Subject: [PATCH 2/2] update variable name, change type import --- src/components/Popover/types.ts | 2 +- src/components/PopoverWithMeasuredContent.tsx | 18 +++++++----------- src/libs/calculateAnchorPosition.ts | 6 +----- src/styles/index.ts | 2 +- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/components/Popover/types.ts b/src/components/Popover/types.ts index e76639bd763e..7f7e2829770c 100644 --- a/src/components/Popover/types.ts +++ b/src/components/Popover/types.ts @@ -39,4 +39,4 @@ type PopoverProps = BaseModalProps & { type PopoverWithWindowDimensionsProps = PopoverProps & WindowDimensionsProps; -export type {PopoverProps, PopoverWithWindowDimensionsProps, AnchorAlignment, PopoverDimensions}; +export type {PopoverProps, PopoverWithWindowDimensionsProps}; diff --git a/src/components/PopoverWithMeasuredContent.tsx b/src/components/PopoverWithMeasuredContent.tsx index fec9ac31f8fe..9d10f7869f8a 100644 --- a/src/components/PopoverWithMeasuredContent.tsx +++ b/src/components/PopoverWithMeasuredContent.tsx @@ -5,14 +5,10 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import PopoverWithMeasuredContentUtils from '@libs/PopoverWithMeasuredContentUtils'; import CONST from '@src/CONST'; +import type {AnchorPosition} from '@src/styles'; import Popover from './Popover'; import {PopoverProps} from './Popover/types'; -type AnchorPosition = { - horizontal: number; - vertical: number; -}; - type PopoverWithMeasuredContentProps = Omit & { /** The horizontal and vertical anchors points for the popover */ anchorPosition: AnchorPosition; @@ -45,18 +41,18 @@ function PopoverWithMeasuredContent({ const [popoverWidth, setPopoverWidth] = useState(popoverDimensions.width); const [popoverHeight, setPopoverHeight] = useState(popoverDimensions.height); const [isContentMeasured, setIsContentMeasured] = useState(popoverWidth > 0 && popoverHeight > 0); - const [isCurrentVisible, setIsCurrentVisible] = useState(false); + const [isPopoverVisible, setIsPopoverVisible] = useState(false); /** * 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. + * Skip render on Popover until recalculations are done by setting isContentMeasured to false as early as possible. */ - if (!isCurrentVisible && isVisible) { + if (!isPopoverVisible && isVisible) { // When Popover is shown recalculate setIsContentMeasured(popoverDimensions.width > 0 && popoverDimensions.height > 0); - setIsCurrentVisible(true); - } else if (isCurrentVisible && !isVisible) { - setIsCurrentVisible(false); + setIsPopoverVisible(true); + } else if (isPopoverVisible && !isVisible) { + setIsPopoverVisible(false); } /** diff --git a/src/libs/calculateAnchorPosition.ts b/src/libs/calculateAnchorPosition.ts index 8db4c053aba6..b9d0f553ca4f 100644 --- a/src/libs/calculateAnchorPosition.ts +++ b/src/libs/calculateAnchorPosition.ts @@ -2,6 +2,7 @@ import {View} from 'react-native'; import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; +import type {AnchorPosition} from '@src/styles'; type AnchorOrigin = { horizontal: ValueOf; @@ -9,11 +10,6 @@ type AnchorOrigin = { shiftVertical?: number; }; -type AnchorPosition = { - horizontal: number; - vertical: number; -}; - /** * Gets the x,y position of the passed in component for the purpose of anchoring another component to it. */ diff --git a/src/styles/index.ts b/src/styles/index.ts index 9e02335bde0d..f7fe891f292d 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4054,4 +4054,4 @@ const defaultStyles = styles(defaultTheme); export default styles; export {defaultStyles}; -export type {Styles, ThemeStyles, StatusBarStyle, ColorScheme}; +export type {Styles, ThemeStyles, StatusBarStyle, ColorScheme, AnchorPosition};