From b5fe57a66022fd9362795d21d4dc7dff69125a13 Mon Sep 17 00:00:00 2001 From: Alex Kim Date: Tue, 2 Jun 2026 18:28:38 +1000 Subject: [PATCH] fix(platform-dropdown-component): Auto-size iOS trigger wrapper to prevent stale label wrapping --- components/PlatformDropdown.tsx | 50 ++++----------------------------- 1 file changed, 6 insertions(+), 44 deletions(-) diff --git a/components/PlatformDropdown.tsx b/components/PlatformDropdown.tsx index aaea71b3f..5487393dd 100644 --- a/components/PlatformDropdown.tsx +++ b/components/PlatformDropdown.tsx @@ -1,13 +1,7 @@ import { Ionicons } from "@expo/vector-icons"; import { BottomSheetScrollView } from "@gorhom/bottom-sheet"; -import React, { useEffect, useState } from "react"; -import { - type LayoutChangeEvent, - Platform, - StyleSheet, - TouchableOpacity, - View, -} from "react-native"; +import React, { useEffect } from "react"; +import { Platform, StyleSheet, TouchableOpacity, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Text } from "@/components/common/Text"; import { useGlobalModal } from "@/providers/GlobalModalProvider"; @@ -217,24 +211,6 @@ const PlatformDropdownComponent = ({ }: PlatformDropdownProps) => { const { showModal, hideModal, isVisible } = useGlobalModal(); - // @expo/ui's (SDK 55) fills its available space by default, and - // `matchContents` doesn't help here: it reports the native Menu's size via - // setStyleSize and overrides any explicit size. Instead we measure the - // trigger's intrinsic size in plain RN (off-layout) and pin it on the Host. - const [triggerSize, setTriggerSize] = useState<{ - width: number; - height: number; - } | null>(null); - - const handleMeasureTrigger = (e: LayoutChangeEvent) => { - const { width, height } = e.nativeEvent.layout; - setTriggerSize((prev) => - prev && prev.width === width && prev.height === height - ? prev - : { width, height }, - ); - }; - // Handle controlled open state for Android useEffect(() => { if (Platform.OS === "android" && controlledOpen === true) { @@ -265,25 +241,11 @@ const PlatformDropdownComponent = ({ }, [isVisible, controlledOpen, controlledOnOpenChange]); if (Platform.OS === "ios" && !Platform.isTV) { - // Pin the wrapper to the measured trigger size. @expo/ui's (SDK 55) - // fills its parent and reports its own size via setStyleSize, so it can't - // size itself to content. If the wrapper has no size, the Host's `flex: 1` - // height depends on the parent while the parent depends on the Host — a - // circular dependency that collapses to 0 for any selector nested more than - // one level deep (so only the first, shallowest dropdown stays visible). - // Giving the wrapper the measured size breaks the cycle; the Host then - // fills a concrete box. + // @expo/ui's can't size to content, so an in-flow invisible copy of + // the trigger sizes the wrapper while the Host overlays the real Menu. return ( - - {/* Hidden measurer: lays the trigger out off-flow to capture its - intrinsic size. Absolutely positioned WITHOUT right/bottom so it - sizes to the trigger's content rather than to its parent. */} - + + {trigger}