Skip to content

Commit c199ec1

Browse files
Enhance PopoverWithMeasuredContent with shouldSkipRemeasurement prop to optimize re-measurement logic. Update DatePickerModal and EmojiPicker to utilize the new prop.
1 parent 29bf5d6 commit c199ec1

5 files changed

Lines changed: 81 additions & 30 deletions

File tree

src/components/DatePicker/DatePickerModal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ function DatePickerModal({
7373
shouldSwitchPositionIfOverflow
7474
shouldEnableNewFocusManagement
7575
shouldMeasureAnchorPositionFromTop={shouldPositionFromTop}
76+
shouldSkipRemeasurement
7677
>
7778
<CalendarPicker
7879
minDate={minDate}

src/components/EmojiPicker/EmojiPicker.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ function EmojiPicker({viewportOffsetTop}: EmojiPickerProps, ref: ForwardedRef<Em
216216
shouldSwitchPositionIfOverflow
217217
shouldEnableNewFocusManagement
218218
restoreFocusType={CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE}
219+
shouldSkipRemeasurement
219220
>
220221
<FocusTrapForModal active={isEmojiPickerVisible}>
221222
<View>

src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,13 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r
6868
}
6969
}, 300);
7070

71-
const scrollToHeader = (headerIndex: number) => {
72-
const calculatedOffset = Math.floor(headerIndex / CONST.EMOJI_NUM_PER_ROW) * CONST.EMOJI_PICKER_HEADER_HEIGHT;
73-
emojiListRef.current?.scrollToOffset({offset: calculatedOffset, animated: true});
74-
};
71+
const scrollToHeader = useCallback(
72+
(headerIndex: number) => {
73+
const calculatedOffset = Math.floor(headerIndex / CONST.EMOJI_NUM_PER_ROW) * CONST.EMOJI_PICKER_HEADER_HEIGHT;
74+
emojiListRef.current?.scrollToOffset({offset: calculatedOffset, animated: true});
75+
},
76+
[emojiListRef],
77+
);
7578

7679
/**
7780
* Given an emoji item object, render a component based on its type.

src/components/PopoverWithMeasuredContent.tsx

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ type PopoverWithMeasuredContentProps = Omit<PopoverProps, 'anchorPosition'> & {
2929

3030
/** Whether we should should use top side for the anchor positioning */
3131
shouldMeasureAnchorPositionFromTop?: boolean;
32+
33+
/** Whether to skip re-measurement when becoming visible (for components with static dimensions) */
34+
shouldSkipRemeasurement?: boolean;
3235
};
3336

3437
/**
@@ -66,6 +69,7 @@ function PopoverWithMeasuredContent({
6669
shouldHandleNavigationBack = false,
6770
shouldEnableNewFocusManagement,
6871
shouldMeasureAnchorPositionFromTop = false,
72+
shouldSkipRemeasurement = false,
6973
...props
7074
}: PopoverWithMeasuredContentProps) {
7175
const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext);
@@ -75,15 +79,28 @@ function PopoverWithMeasuredContent({
7579
const [popoverHeight, setPopoverHeight] = useState(popoverDimensions.height);
7680
const [isContentMeasured, setIsContentMeasured] = useState(popoverWidth > 0 && popoverHeight > 0);
7781
const prevIsVisible = usePrevious(isVisible);
82+
const prevAnchorPosition = usePrevious(anchorPosition);
83+
const prevWindowDimensions = usePrevious({windowWidth, windowHeight});
7884

7985
const modalId = useMemo(() => ComposerFocusManager.getId(), []);
8086

8187
if (!prevIsVisible && isVisible && shouldEnableNewFocusManagement) {
8288
ComposerFocusManager.saveFocusState(modalId);
8389
}
8490

85-
if (!prevIsVisible && isVisible && isContentMeasured) {
86-
setIsContentMeasured(false);
91+
if (!prevIsVisible && isVisible && isContentMeasured && !shouldSkipRemeasurement) {
92+
// Check if anything significant changed that would require re-measurement
93+
const hasAnchorPositionChanged = !isEqual(prevAnchorPosition, anchorPosition);
94+
const hasWindowSizeChanged = !isEqual(prevWindowDimensions, {windowWidth, windowHeight});
95+
const hasStaticDimensions = popoverDimensions.width > 0 && popoverDimensions.height > 0;
96+
97+
// Only reset if:
98+
// 1. We don't have static dimensions, OR
99+
// 2. The anchor position changed significantly, OR
100+
// 3. The window size changed significantly
101+
if (!hasStaticDimensions || hasAnchorPositionChanged || hasWindowSizeChanged) {
102+
setIsContentMeasured(false);
103+
}
87104
}
88105

89106
/**
@@ -144,28 +161,40 @@ function PopoverWithMeasuredContent({
144161
};
145162
}, [anchorPosition, anchorAlignment, popoverWidth, popoverHeight]);
146163

147-
const horizontalShift = PopoverWithMeasuredContentUtils.computeHorizontalShift(adjustedAnchorPosition.left, popoverWidth, windowWidth);
148-
const verticalShift = PopoverWithMeasuredContentUtils.computeVerticalShift(
149-
adjustedAnchorPosition.top,
150-
popoverHeight,
151-
windowHeight,
152-
anchorDimensions.height,
153-
shouldSwitchPositionIfOverflow,
154-
);
155-
const shiftedAnchorPosition: PopoverAnchorPosition = {
156-
left: adjustedAnchorPosition.left + horizontalShift,
157-
...(shouldMeasureAnchorPositionFromTop ? {top: adjustedAnchorPosition.top + verticalShift} : {}),
158-
};
164+
// Memoize expensive position calculations
165+
const positionCalculations = useMemo(() => {
166+
const horizontalShift = PopoverWithMeasuredContentUtils.computeHorizontalShift(adjustedAnchorPosition.left, popoverWidth, windowWidth);
167+
const verticalShift = PopoverWithMeasuredContentUtils.computeVerticalShift(
168+
adjustedAnchorPosition.top,
169+
popoverHeight,
170+
windowHeight,
171+
anchorDimensions.height,
172+
shouldSwitchPositionIfOverflow,
173+
);
174+
return {horizontalShift, verticalShift};
175+
}, [adjustedAnchorPosition.left, adjustedAnchorPosition.top, popoverWidth, popoverHeight, windowWidth, windowHeight, anchorDimensions.height, shouldSwitchPositionIfOverflow]);
176+
177+
const shiftedAnchorPosition: PopoverAnchorPosition = useMemo(() => {
178+
const result: PopoverAnchorPosition = {
179+
left: adjustedAnchorPosition.left + positionCalculations.horizontalShift,
180+
};
159181

160-
if (anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP) {
161-
const top = adjustedAnchorPosition.top + verticalShift;
162-
const maxTop = windowHeight - popoverHeight - verticalShift;
163-
shiftedAnchorPosition.top = Math.min(Math.max(verticalShift, top), maxTop);
164-
}
182+
if (shouldMeasureAnchorPositionFromTop) {
183+
result.top = adjustedAnchorPosition.top + positionCalculations.verticalShift;
184+
}
165185

166-
if (anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM) {
167-
shiftedAnchorPosition.bottom = windowHeight - (adjustedAnchorPosition.top + popoverHeight) - verticalShift;
168-
}
186+
if (anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP) {
187+
const top = adjustedAnchorPosition.top + positionCalculations.verticalShift;
188+
const maxTop = windowHeight - popoverHeight - positionCalculations.verticalShift;
189+
result.top = Math.min(Math.max(positionCalculations.verticalShift, top), maxTop);
190+
}
191+
192+
if (anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM) {
193+
result.bottom = windowHeight - (adjustedAnchorPosition.top + popoverHeight) - positionCalculations.verticalShift;
194+
}
195+
196+
return result;
197+
}, [adjustedAnchorPosition, positionCalculations, anchorAlignment.vertical, windowHeight, popoverHeight, shouldMeasureAnchorPositionFromTop]);
169198

170199
return isContentMeasured ? (
171200
<Popover

src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,12 +373,29 @@ function ReportActionCompose({
373373
// eslint-disable-next-line react-compiler/react-compiler
374374
onSubmitAction = handleSendMessage;
375375

376+
const emojiPositionValues = useMemo(
377+
() => ({
378+
secondaryRowHeight: styles.chatItemComposeSecondaryRow.height,
379+
secondaryRowMarginTop: styles.chatItemComposeSecondaryRow.marginTop,
380+
secondaryRowMarginBottom: styles.chatItemComposeSecondaryRow.marginBottom,
381+
composeBoxMinHeight: styles.chatItemComposeBox.minHeight,
382+
emojiButtonHeight: styles.chatItemEmojiButton.height,
383+
}),
384+
[
385+
styles.chatItemComposeSecondaryRow.height,
386+
styles.chatItemComposeSecondaryRow.marginTop,
387+
styles.chatItemComposeSecondaryRow.marginBottom,
388+
styles.chatItemComposeBox.minHeight,
389+
styles.chatItemEmojiButton.height,
390+
],
391+
);
392+
376393
const emojiShiftVertical = useMemo(() => {
377-
const chatItemComposeSecondaryRowHeight = styles.chatItemComposeSecondaryRow.height + styles.chatItemComposeSecondaryRow.marginTop + styles.chatItemComposeSecondaryRow.marginBottom;
378-
const reportActionComposeHeight = styles.chatItemComposeBox.minHeight + chatItemComposeSecondaryRowHeight;
379-
const emojiOffsetWithComposeBox = (styles.chatItemComposeBox.minHeight - styles.chatItemEmojiButton.height) / 2;
394+
const chatItemComposeSecondaryRowHeight = emojiPositionValues.secondaryRowHeight + emojiPositionValues.secondaryRowMarginTop + emojiPositionValues.secondaryRowMarginBottom;
395+
const reportActionComposeHeight = emojiPositionValues.composeBoxMinHeight + chatItemComposeSecondaryRowHeight;
396+
const emojiOffsetWithComposeBox = (emojiPositionValues.composeBoxMinHeight - emojiPositionValues.emojiButtonHeight) / 2;
380397
return reportActionComposeHeight - emojiOffsetWithComposeBox - CONST.MENU_POSITION_REPORT_ACTION_COMPOSE_BOTTOM;
381-
}, [styles]);
398+
}, [emojiPositionValues]);
382399

383400
const validateMaxLength = useCallback(
384401
(value: string) => {

0 commit comments

Comments
 (0)