From d11fb3d0c0e9e5921f95c1cc61fd1804744b3335 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sat, 30 May 2026 12:24:39 +0200 Subject: [PATCH] fix(dropdown): use nested Menu submenus for grouped options on iOS Render titled option groups as nested Menu submenus instead of flat Pickers, and convert the Discover filters from ContextMenu to Menu. Keeps single-tap-to-open behavior (ContextMenu requires a long press and reads as a context menu) while giving the nicer nested grouping. --- components/PlatformDropdown.tsx | 63 +++++++++---------- components/search/DiscoverFilters.tsx | 88 ++++++++++++++------------- 2 files changed, 75 insertions(+), 76 deletions(-) diff --git a/components/PlatformDropdown.tsx b/components/PlatformDropdown.tsx index d7d02d274..8f81e567e 100644 --- a/components/PlatformDropdown.tsx +++ b/components/PlatformDropdown.tsx @@ -1,11 +1,5 @@ -import { - Button, - Host, - Menu, - Picker, - Text as SwiftUIText, -} from "@expo/ui/swift-ui"; -import { disabled, tag } from "@expo/ui/swift-ui/modifiers"; +import { Button, Host, Menu } from "@expo/ui/swift-ui"; +import { disabled } from "@expo/ui/swift-ui/modifiers"; import { Ionicons } from "@expo/vector-icons"; import { BottomSheetScrollView } from "@gorhom/bottom-sheet"; import React, { useEffect, useState } from "react"; @@ -299,41 +293,40 @@ const PlatformDropdownComponent = ({ const items = []; - // Add Picker for radio options ONLY if there's a group title + // Group radio options under a submenu ONLY if there's a title // Otherwise render as individual buttons if (radioOptions.length > 0) { if (group.title) { - // Use Picker for grouped options. - // Use the option index (a stable primitive) as the - // tag/selection value and React key. Option `value`s can be - // objects (e.g. bitrate / media source), which collapse to - // "[object Object]" as a key and never match the Picker's - // primitive selection. - const selectedRadioIndex = radioOptions.findIndex( + // Use a nested Menu as a submenu for grouped options. This + // reads as "Title: Selected" and expands to the choices on + // tap, keeping the nested look while staying a dropdown. + // (Menu opens on a single tap and nests cleanly; ContextMenu + // would require a long-press and read as a context menu.) + const selectedOption = radioOptions.find( (opt) => opt.selected, ); + const displayTitle = selectedOption + ? `${group.title}: ${selectedOption.label}` + : group.title; items.push( - = 0 ? selectedRadioIndex : undefined - } - onSelectionChange={(index) => { - const selectedOption = radioOptions[index as number]; - selectedOption?.onPress(); - onOptionSelect?.(selectedOption?.value); - }} - > - {radioOptions.map((opt, optionIndex) => ( - + {radioOptions.map((option, optionIndex) => ( + - - - { - setJellyseerrOrderBy(value as unknown as JellyseerrSearchSort); - }} - > - {sortOptions.map((item) => ( - - {t(`home.settings.plugins.jellyseerr.order_by.${item}`)} - - ))} - - { - setJellyseerrSortOrder(value as "asc" | "desc"); - }} - > - {orderOptions.map((item) => ( - - {t(`library.filters.${item}`)} - - ))} - - - + /> + } + > + + {sortOptions.map((item) => { + const isSelected = + jellyseerrOrderBy === (item as unknown as JellyseerrSearchSort); + return ( + + + {orderOptions.map((item) => { + const isSelected = jellyseerrSortOrder === item; + return ( + + ); }