authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-08-28 20:05:40-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-28 20:05:40-07:00
log38dcd7a404d24c2fa514cc82c1a5c4fd6a9d4a7a
tree83c653561ae9b8c04e617ad5a88c27867f1e5b07
parent3e6ab36f6ed303f95f210971f56cc8642214c6fc
signature Signed by PGP key B5690EEEBB952194

fix(mobile): harden native header toolbar items (#8611)

Co-authored-by: Julius Marminge <julius@mac.lan>

2 files changed, 271 insertions(+), 82 deletions(-)

patches/react-native-screens@4.26.2.patch+265-76
......@@ -159,7 +159,7 @@ index 8a41697223f6c4b5cb2dd6d450fc58cbb3b27536..309ba2f762068dcf452c172f7c7cbf2a
159159
160160 NS_ASSUME_NONNULL_END
161161diff --git a/ios/RNSScreenStackHeaderConfig.mm b/ios/RNSScreenStackHeaderConfig.mm
162index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338eff90c694 100644
162index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..a60b96b44e85d3f4b29dc0d190b95ab60e185033 100644
163163--- a/ios/RNSScreenStackHeaderConfig.mm
164164+++ b/ios/RNSScreenStackHeaderConfig.mm
165165@@ -24,11 +24,46 @@
......@@ -294,7 +294,7 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
294294 break;
295295 }
296296 case RNSScreenStackHeaderSubviewTypeCenter:
297@@ -648,10 +693,453 @@ + (void)updateViewController:(UIViewController *)vc
297@@ -648,10 +693,470 @@ + (void)updateViewController:(UIViewController *)vc
298298 // This assignment should be done after `navitem.titleView = ...` assignment (iOS 16.0 bug).
299299 // See: https://github.com/software-mansion/react-native-screens/issues/1570 (comments)
300300 navitem.title = config.title;
......@@ -323,21 +323,24 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
323323+ navitem, &RNSAppliedLeadingHeaderItemsKey, @[ config, headerLeftConfigs, subviewLeftItems ])) {
324324+ NSArray *items = [config barButtonItemsFromConfigs:config.headerLeftBarButtonItems
325325+ withCurrentItems:subviewLeftItems
326+ navigationItem:navitem];
326+ navigationItem:navitem
327+ allowsSearchBarPlacement:NO];
327328+ navitem.leadingItemGroups = [config barButtonItemGroupsFromItems:items];
328329+ }
329330+ if (RNSUpdateHeaderItemsCache(
330331+ navitem, &RNSAppliedTrailingHeaderItemsKey, @[ config, headerRightConfigs, subviewRightItems ])) {
331332+ NSArray *items = [config barButtonItemsFromConfigs:config.headerRightBarButtonItems
332333+ withCurrentItems:subviewRightItems
333+ navigationItem:navitem];
334+ navigationItem:navitem
335+ allowsSearchBarPlacement:NO];
334336+ navitem.trailingItemGroups = [config barButtonItemGroupsFromItems:items];
335337+ }
336338+ if (@available(iOS 26.0, *)) {
337339+ if (RNSUpdateHeaderItemsCache(navitem, &RNSAppliedCenterHeaderItemsKey, @[ config, headerCenterConfigs ])) {
338340+ NSArray *items = [config barButtonItemsFromConfigs:config.headerCenterBarButtonItems
339341+ withCurrentItems:@[]
340+ navigationItem:navitem];
342+ navigationItem:navitem
343+ allowsSearchBarPlacement:NO];
341344+ navitem.centerItemGroups = [config barButtonItemGroupsFromItems:items];
342345+ }
343346+ }
......@@ -346,20 +349,27 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
346349+ {
347350+ NSArray<UIBarButtonItem *> *leftBarButtonItems = [config barButtonItemsFromConfigs:config.headerLeftBarButtonItems
348351+ withCurrentItems:subviewLeftItems
349+ navigationItem:navitem];
352+ navigationItem:navitem
353+ allowsSearchBarPlacement:NO];
350354+ NSArray<UIBarButtonItem *> *rightBarButtonItems = [config barButtonItemsFromConfigs:config.headerRightBarButtonItems
351355+ withCurrentItems:subviewRightItems
352+ navigationItem:navitem];
356+ navigationItem:navitem
357+ allowsSearchBarPlacement:NO];
353358+ navitem.leftBarButtonItems = leftBarButtonItems;
354359+ navitem.rightBarButtonItems = rightBarButtonItems;
355360+ }
356361+ NSDictionary<NSString *, id> *mailSearchToolbarConfig = nil;
362+ NSMutableArray<NSDictionary<NSString *, id> *> *nonMailSearchToolbarConfigs = [NSMutableArray array];
357363+ for (NSDictionary<NSString *, id> *toolbarConfig in config.headerToolbarItems) {
358364+ if (toolbarConfig[@"mailSearchToolbar"]) {
359+ mailSearchToolbarConfig = toolbarConfig;
360+ break;
365+ if (mailSearchToolbarConfig == nil) {
366+ mailSearchToolbarConfig = toolbarConfig;
367+ }
368+ } else {
369+ [nonMailSearchToolbarConfigs addObject:toolbarConfig];
361370+ }
362371+ }
372+ NSArray<NSDictionary<NSString *, id> *> *navigationToolbarConfigs = [nonMailSearchToolbarConfigs copy];
363373+ if (mailSearchToolbarConfig == nil) {
364374+ for (NSDictionary<NSString *, id> *rightConfig in config.headerRightBarButtonItems) {
365375+ if ([rightConfig[@"bottomMailSearchToolbar"] boolValue]) {
......@@ -560,14 +570,6 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
560570+ searchField.attributedPlaceholder =
561571+ [[NSAttributedString alloc] initWithString:placeholder attributes:placeholderAttributes];
562572+ }
563+ NSString *searchTextChangeId = mailSearchToolbarConfig[@"searchTextChangeId"];
564+ if (searchTextChangeId != nil) {
565+ [searchField addAction:[UIAction actionWithHandler:^(__kindof UIAction *_Nonnull action) {
566+ UISearchTextField *field = (UISearchTextField *)action.sender;
567+ emitButtonPress([NSString stringWithFormat:@"%@:%@", searchTextChangeId, field.text ?: @""]);
568+ }]
569+ forControlEvents:UIControlEventEditingChanged];
570+ }
571573+ searchField.borderStyle = UITextBorderStyleNone;
572574+ searchField.font = searchFont;
573575+ searchField.adjustsFontForContentSizeCategory = YES;
......@@ -584,6 +586,25 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
584586+ ]];
585587+ }
586588+
589+ NSString *searchTextChangeId = mailSearchToolbarConfig[@"searchTextChangeId"];
590+ NSString *searchTextChangeActionIdentifier = @"org.react-native-screens.mail-search-toolbar.text-change";
591+ if (resolvedSearchTextField != nil) {
592+ [resolvedSearchTextField removeActionForIdentifier:searchTextChangeActionIdentifier
593+ forControlEvents:UIControlEventEditingChanged];
594+ }
595+ if (searchTextChangeId != nil && resolvedSearchTextField != nil) {
596+ [resolvedSearchTextField
597+ addAction:[UIAction actionWithTitle:@""
598+ image:nil
599+ identifier:searchTextChangeActionIdentifier
600+ handler:^(__kindof UIAction *_Nonnull action) {
601+ UITextField *field = (UITextField *)action.sender;
602+ emitButtonPress(
603+ [NSString stringWithFormat:@"%@:%@", searchTextChangeId, field.text ?: @""]);
604+ }]
605+ forControlEvents:UIControlEventEditingChanged];
606+ }
607+
587608+ UIButton *filterButton = nil;
588609+ if (hasFilterButton) {
589610+ filterButton = makeGlassButton(
......@@ -720,11 +741,6 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
720741+#endif
721742+ }
722743+
723+ NSArray<NSDictionary<NSString *, id> *> *navigationToolbarConfigs = config.headerToolbarItems;
724+ if (mailSearchToolbarConfig != nil) {
725+ navigationToolbarConfigs = @[];
726+ }
727+
728744+ NSArray *toolbarConfigsKey = navigationToolbarConfigs ?: @[];
729745+ // Same reuse rule as the header item groups above (including the config
730746+ // identity — cached toolbar items capture this config's event emitter).
......@@ -738,7 +754,8 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
738754+ if (!reuseToolbarItems) {
739755+ NSArray<UIBarButtonItem *> *toolbarItems = [config barButtonItemsFromConfigs:navigationToolbarConfigs
740756+ withCurrentItems:@[]
741+ navigationItem:navitem];
757+ navigationItem:navitem
758+ allowsSearchBarPlacement:YES];
742759+ if (toolbarItems.count > 0) {
743760+ vc.toolbarItems = toolbarItems;
744761+ [navctr setToolbarHidden:NO animated:animated];
......@@ -752,15 +769,16 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
752769
753770 // Setting navigation bar visibility is split to mitigate iOS 26 bug with bar button items
754771 // (setting nav bar visibility should be done after `navitem.*BarButtonItems`).
755@@ -784,6 +1272,7 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *
772@@ -784,6 +1289,8 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *
756773
757774 - (NSArray<UIBarButtonItem *> *)barButtonItemsFromConfigs:(NSArray<NSDictionary<NSString *, id> *> *)dicts
758775 withCurrentItems:(NSArray<UIBarButtonItem *> *)currentItems
759776+ navigationItem:(UINavigationItem *)navitem
777+ allowsSearchBarPlacement:(BOOL)allowsSearchBarPlacement
760778 {
761779 if (dicts.count == 0) {
762780 return currentItems;
763@@ -792,7 +1281,197 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *
781@@ -792,7 +1299,200 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *
764782 [items addObjectsFromArray:currentItems];
765783 for (NSUInteger i = 0; i < dicts.count; i++) {
766784 NSDictionary *dict = dicts[i];
......@@ -896,6 +914,9 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
896914+ }
897915+#endif
898916+ } else if (dict[@"searchBarPlacement"]) {
917+ if (!allowsSearchBarPlacement) {
918+ continue;
919+ }
899920+#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
900921+ if (@available(iOS 26.0, *)) {
901922+ NSNumber *width = dict[@"width"];
......@@ -959,7 +980,7 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
959980 RNSBarButtonItem *item = [[RNSBarButtonItem alloc] initWithConfig:dict
960981 action:^(NSString *buttonId) {
961982 auto eventEmitter = std::static_pointer_cast<const facebook::react::RNSScreenStackHeaderConfigEventEmitter>(
962@@ -814,19 +1493,23 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *
983@@ -814,19 +1514,23 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *
963984 }
964985 imageLoader:_imageLoader];
965986 NSNumber *index = dict[@"index"];
......@@ -989,7 +1010,7 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
9891010 [items insertObject:item atIndex:index.integerValue];
9901011 } else {
9911012 [items addObject:item];
992@@ -836,6 +1519,47 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *
1013@@ -836,6 +1540,47 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *
9931014 return items;
9941015 }
9951016
......@@ -1037,7 +1058,7 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
10371058 RNS_IGNORE_SUPER_CALL_BEGIN
10381059 - (void)insertReactSubview:(RNSScreenStackHeaderSubview *)subview atIndex:(NSInteger)atIndex
10391060 {
1040@@ -1023,6 +1747,8 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
1061@@ -1023,6 +1768,8 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
10411062 }
10421063
10431064 _title = RCTNSStringFromStringNilIfEmpty(newScreenProps.title);
......@@ -1046,7 +1067,7 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
10461067 if (newScreenProps.titleFontFamily != oldScreenProps.titleFontFamily) {
10471068 _titleFontFamily = RCTNSStringFromStringNilIfEmpty(newScreenProps.titleFontFamily);
10481069 }
1049@@ -1048,6 +1774,7 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
1070@@ -1048,6 +1795,7 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
10501071 _disableBackButtonMenu = newScreenProps.disableBackButtonMenu;
10511072 _backButtonDisplayMode =
10521073 [RNSConvert UINavigationItemBackButtonDisplayModeFromCppEquivalent:newScreenProps.backButtonDisplayMode];
......@@ -1054,7 +1075,7 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..d36bda28552e5ac75088e18d0fde338e
10541075
10551076 if (newScreenProps.userInterfaceStyle != oldScreenProps.userInterfaceStyle) {
10561077 _userInterfaceStyle = [RNSConvert UIUserInterfaceStyleFromCppEquivalent:newScreenProps.userInterfaceStyle];
1057@@ -1094,6 +1821,30 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
1078@@ -1094,6 +1842,30 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
10581079 _headerRightBarButtonItems = array;
10591080 }
10601081
......@@ -1145,10 +1166,10 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622
11451166
11461167 // Needed because of this: https://github.com/facebook/react-native/pull/37274
11471168diff --git a/lib/commonjs/components/ScreenStackHeaderConfig.js b/lib/commonjs/components/ScreenStackHeaderConfig.js
1148index 8c509f2657105cfd86448347c1a01fb73651ff9e..d1f53322a7d2401852732ce46bfc6f32ba1aca64 100644
1169index 8c509f2657105cfd86448347c1a01fb73651ff9e..5e3b5185c0ea33c6b98f5daed69239d2c0d0ce90 100644
11491170--- a/lib/commonjs/components/ScreenStackHeaderConfig.js
11501171+++ b/lib/commonjs/components/ScreenStackHeaderConfig.js
1151@@ -26,18 +26,42 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
1172@@ -26,17 +26,42 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
11521173 } = (0, _EdgeInsetApplicationContext.useEdgeInsetApplication)(!props.hidden, props.disableTopInsetApplication ?? false, props.disableLeftInsetApplication ?? false, props.disableRightInsetApplication ?? false, props.disableBottomInsetApplication ?? false);
11531174 const {
11541175 headerLeftBarButtonItems,
......@@ -1160,8 +1181,8 @@ index 8c509f2657105cfd86448347c1a01fb73651ff9e..d1f53322a7d2401852732ce46bfc6f32
11601181 const preparedHeaderLeftBarButtonItems = headerLeftBarButtonItems && _utils.isHeaderBarButtonsAvailableForCurrentPlatform ? (0, _prepareHeaderBarButtonItems.prepareHeaderBarButtonItems)(headerLeftBarButtonItems, 'left') : undefined;
11611182 const preparedHeaderRightBarButtonItems = headerRightBarButtonItems && _utils.isHeaderBarButtonsAvailableForCurrentPlatform ? (0, _prepareHeaderBarButtonItems.prepareHeaderBarButtonItems)(headerRightBarButtonItems, 'right') : undefined;
11621183- const hasHeaderBarButtonItems = _utils.isHeaderBarButtonsAvailableForCurrentPlatform && (preparedHeaderLeftBarButtonItems?.length || preparedHeaderRightBarButtonItems?.length);
1163+ const preparedHeaderCenterBarButtonItems = headerCenterBarButtonItems && _utils.isHeaderBarButtonsAvailableForCurrentPlatform ? (0, _prepareHeaderBarButtonItems.prepareHeaderBarButtonItems)(headerCenterBarButtonItems, 'right') : undefined;
1164+ const preparedHeaderToolbarItems = headerToolbarItems && _utils.isHeaderBarButtonsAvailableForCurrentPlatform ? (0, _prepareHeaderBarButtonItems.prepareHeaderBarButtonItems)(headerToolbarItems, 'right') : undefined;
1184+ const preparedHeaderCenterBarButtonItems = headerCenterBarButtonItems && _utils.isHeaderBarButtonsAvailableForCurrentPlatform ? (0, _prepareHeaderBarButtonItems.prepareHeaderBarButtonItems)(headerCenterBarButtonItems, 'center') : undefined;
1185+ const preparedHeaderToolbarItems = headerToolbarItems && _utils.isHeaderBarButtonsAvailableForCurrentPlatform ? (0, _prepareHeaderBarButtonItems.prepareHeaderBarButtonItems)(headerToolbarItems, 'toolbar') : undefined;
11651186+ const hasHeaderBarButtonItems = _utils.isHeaderBarButtonsAvailableForCurrentPlatform && (preparedHeaderLeftBarButtonItems?.length || preparedHeaderRightBarButtonItems?.length || preparedHeaderCenterBarButtonItems?.length || preparedHeaderToolbarItems?.length);
11661187
11671188 // Handle bar button item presses
......@@ -1172,7 +1193,8 @@ index 8c509f2657105cfd86448347c1a01fb73651ff9e..d1f53322a7d2401852732ce46bfc6f32
11721193+ const pressedItem = allItems.find(item => item && 'buttonId' in item && item.buttonId === buttonId);
11731194 if (pressedItem && pressedItem.type === 'button' && pressedItem.onPress) {
11741195 pressedItem.onPress();
1175 }
1196+ return;
1197+ }
11761198+ for (const item of allItems) {
11771199+ if (!item || item.type !== 'mailSearchToolbar') {
11781200+ continue;
......@@ -1190,11 +1212,10 @@ index 8c509f2657105cfd86448347c1a01fb73651ff9e..d1f53322a7d2401852732ce46bfc6f32
11901212+ item.onSearchTextChange?.(buttonId.slice(searchTextChangePrefix.length));
11911213+ return;
11921214+ }
1193+ }
1215 }
11941216 } : undefined;
11951217
1196 // Handle bar button menu item presses by deep-searching nested menus
1197@@ -59,7 +83,7 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
1218@@ -59,7 +84,7 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
11981219 };
11991220
12001221 // Check each bar-button item with a menu
......@@ -1203,7 +1224,7 @@ index 8c509f2657105cfd86448347c1a01fb73651ff9e..d1f53322a7d2401852732ce46bfc6f32
12031224 for (const item of allItems) {
12041225 if (item && item.type === 'menu' && item.menu) {
12051226 const action = findInMenu(item.menu, event.nativeEvent.menuId);
1206@@ -67,6 +91,15 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
1227@@ -67,6 +92,15 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
12071228 action.onPress();
12081229 return;
12091230 }
......@@ -1219,7 +1240,7 @@ index 8c509f2657105cfd86448347c1a01fb73651ff9e..d1f53322a7d2401852732ce46bfc6f32
12191240 }
12201241 }
12211242 } : undefined;
1222@@ -74,6 +107,8 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
1243@@ -74,6 +108,8 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
12231244 userInterfaceStyle: props.experimental_userInterfaceStyle,
12241245 headerLeftBarButtonItems: preparedHeaderLeftBarButtonItems,
12251246 headerRightBarButtonItems: preparedHeaderRightBarButtonItems,
......@@ -1229,20 +1250,49 @@ index 8c509f2657105cfd86448347c1a01fb73651ff9e..d1f53322a7d2401852732ce46bfc6f32
12291250 onPressHeaderBarButtonMenuItem: onPressHeaderBarButtonMenuItem,
12301251 ref: ref,
12311252diff --git a/lib/commonjs/components/helpers/prepareHeaderBarButtonItems.js b/lib/commonjs/components/helpers/prepareHeaderBarButtonItems.js
1232index ab93f62e4a7049d63c6681cecbd6cf8b1d07ce10..b5ea122473b023f84eabdb1914aef09a28c6d525 100644
1253index ab93f62e4a7049d63c6681cecbd6cf8b1d07ce10..9dca2ab640e92161eb647b819a989acd0d41d9fe 100644
12331254--- a/lib/commonjs/components/helpers/prepareHeaderBarButtonItems.js
12341255+++ b/lib/commonjs/components/helpers/prepareHeaderBarButtonItems.js
1235@@ -41,10 +41,31 @@ const prepareMenu = (menu, index, side, path = '') => {
1256@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
1257 });
1258 exports.prepareHeaderBarButtonItems = void 0;
1259 var _reactNative = require("react-native");
1260-const prepareMenu = (menu, index, side, path = '') => {
1261+const prepareMenu = (menu, index, placement, path = '') => {
1262 return {
1263 ...menu,
1264 items: menu.items.map((menuItem, menuIndex) => {
1265@@ -26,7 +26,7 @@ const prepareMenu = (menu, index, side, path = '') => {
1266 xcassetName,
1267 imageSource,
1268 templateSource,
1269- ...prepareMenu(menuItem, index, side, currentPath)
1270+ ...prepareMenu(menuItem, index, placement, currentPath)
1271 };
1272 }
1273 return {
1274@@ -35,16 +35,40 @@ const prepareMenu = (menu, index, side, path = '') => {
1275 xcassetName,
1276 imageSource,
1277 templateSource,
1278- menuId: `${currentPath}-${index}-${side}`
1279+ menuId: `${currentPath}-${index}-${placement}`
1280 };
1281 })
12361282 };
12371283 };
1238 const prepareHeaderBarButtonItems = (barButtonItems, side) => {
1284-const prepareHeaderBarButtonItems = (barButtonItems, side) => {
12391285- return barButtonItems?.map((item, index) => {
1286+const prepareHeaderBarButtonItems = (barButtonItems, placement) => {
12401287+ const items = Array.isArray(barButtonItems) ? barButtonItems : barButtonItems && typeof barButtonItems === 'object' && 'type' in barButtonItems ? [barButtonItems] : undefined;
12411288+ return items?.map((item, index) => {
12421289 if (item.type === 'spacing') {
12431290 return item;
12441291 }
12451292+ if (item.type === 'searchBarPlacement') {
1293+ if (placement !== 'toolbar') {
1294+ return null;
1295+ }
12461296+ return {
12471297+ ...item,
12481298+ searchBarPlacement: true
......@@ -1258,18 +1308,40 @@ index ab93f62e4a7049d63c6681cecbd6cf8b1d07ce10..b5ea122473b023f84eabdb1914aef09a
12581308+ return {
12591309+ ...item,
12601310+ mailSearchToolbar: true,
1261+ filterMenu: item.filterMenu ? prepareMenu(item.filterMenu, index, side, 'filter') : undefined,
1262+ composeMenu: item.composeMenu ? prepareMenu(item.composeMenu, index, side, 'compose') : undefined
1311+ filterMenu: item.filterMenu ? prepareMenu(item.filterMenu, index, placement, 'filter') : undefined,
1312+ composeMenu: item.composeMenu ? prepareMenu(item.composeMenu, index, placement, 'compose') : undefined
12631313+ };
12641314+ }
12651315 let imageSource, templateSource;
12661316 if (item.icon?.type === 'imageSource') {
12671317 imageSource = _reactNative.Image.resolveAssetSource(item.icon.imageSource);
1318@@ -77,17 +101,17 @@ const prepareHeaderBarButtonItems = (barButtonItems, side) => {
1319 if (item.type === 'button') {
1320 return {
1321 ...processedItem,
1322- buttonId: `${index}-${side}`
1323+ buttonId: `${index}-${placement}`
1324 };
1325 }
1326 if (item.type === 'menu') {
1327 return {
1328 ...processedItem,
1329- menu: prepareMenu(item.menu, index, side)
1330+ menu: prepareMenu(item.menu, index, placement)
1331 };
1332 }
1333 return null;
1334- });
1335+ }).filter(item => item !== null);
1336 };
1337 exports.prepareHeaderBarButtonItems = prepareHeaderBarButtonItems;
1338 //# sourceMappingURL=prepareHeaderBarButtonItems.js.map
1339\ No newline at end of file
12681340diff --git a/lib/module/components/ScreenStackHeaderConfig.js b/lib/module/components/ScreenStackHeaderConfig.js
1269index 68e3ba6a78314aba908d07098bb6982d11e6ee80..05cb8563d3a7d3811860058df23bbd1f2f4bca00 100644
1341index 68e3ba6a78314aba908d07098bb6982d11e6ee80..b787b3cf0284ffb81c80360a06c4059277bf4c1e 100644
12701342--- a/lib/module/components/ScreenStackHeaderConfig.js
12711343+++ b/lib/module/components/ScreenStackHeaderConfig.js
1272@@ -22,18 +22,42 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
1344@@ -22,17 +22,42 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
12731345 } = useEdgeInsetApplication(!props.hidden, props.disableTopInsetApplication ?? false, props.disableLeftInsetApplication ?? false, props.disableRightInsetApplication ?? false, props.disableBottomInsetApplication ?? false);
12741346 const {
12751347 headerLeftBarButtonItems,
......@@ -1281,8 +1353,8 @@ index 68e3ba6a78314aba908d07098bb6982d11e6ee80..05cb8563d3a7d3811860058df23bbd1f
12811353 const preparedHeaderLeftBarButtonItems = headerLeftBarButtonItems && isHeaderBarButtonsAvailableForCurrentPlatform ? prepareHeaderBarButtonItems(headerLeftBarButtonItems, 'left') : undefined;
12821354 const preparedHeaderRightBarButtonItems = headerRightBarButtonItems && isHeaderBarButtonsAvailableForCurrentPlatform ? prepareHeaderBarButtonItems(headerRightBarButtonItems, 'right') : undefined;
12831355- const hasHeaderBarButtonItems = isHeaderBarButtonsAvailableForCurrentPlatform && (preparedHeaderLeftBarButtonItems?.length || preparedHeaderRightBarButtonItems?.length);
1284+ const preparedHeaderCenterBarButtonItems = headerCenterBarButtonItems && isHeaderBarButtonsAvailableForCurrentPlatform ? prepareHeaderBarButtonItems(headerCenterBarButtonItems, 'right') : undefined;
1285+ const preparedHeaderToolbarItems = headerToolbarItems && isHeaderBarButtonsAvailableForCurrentPlatform ? prepareHeaderBarButtonItems(headerToolbarItems, 'right') : undefined;
1356+ const preparedHeaderCenterBarButtonItems = headerCenterBarButtonItems && isHeaderBarButtonsAvailableForCurrentPlatform ? prepareHeaderBarButtonItems(headerCenterBarButtonItems, 'center') : undefined;
1357+ const preparedHeaderToolbarItems = headerToolbarItems && isHeaderBarButtonsAvailableForCurrentPlatform ? prepareHeaderBarButtonItems(headerToolbarItems, 'toolbar') : undefined;
12861358+ const hasHeaderBarButtonItems = isHeaderBarButtonsAvailableForCurrentPlatform && (preparedHeaderLeftBarButtonItems?.length || preparedHeaderRightBarButtonItems?.length || preparedHeaderCenterBarButtonItems?.length || preparedHeaderToolbarItems?.length);
12871359
12881360 // Handle bar button item presses
......@@ -1293,7 +1365,8 @@ index 68e3ba6a78314aba908d07098bb6982d11e6ee80..05cb8563d3a7d3811860058df23bbd1f
12931365+ const pressedItem = allItems.find(item => item && 'buttonId' in item && item.buttonId === buttonId);
12941366 if (pressedItem && pressedItem.type === 'button' && pressedItem.onPress) {
12951367 pressedItem.onPress();
1296 }
1368+ return;
1369+ }
12971370+ for (const item of allItems) {
12981371+ if (!item || item.type !== 'mailSearchToolbar') {
12991372+ continue;
......@@ -1311,11 +1384,10 @@ index 68e3ba6a78314aba908d07098bb6982d11e6ee80..05cb8563d3a7d3811860058df23bbd1f
13111384+ item.onSearchTextChange?.(buttonId.slice(searchTextChangePrefix.length));
13121385+ return;
13131386+ }
1314+ }
1387 }
13151388 } : undefined;
13161389
1317 // Handle bar button menu item presses by deep-searching nested menus
1318@@ -55,7 +79,7 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
1390@@ -55,7 +80,7 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
13191391 };
13201392
13211393 // Check each bar-button item with a menu
......@@ -1324,7 +1396,7 @@ index 68e3ba6a78314aba908d07098bb6982d11e6ee80..05cb8563d3a7d3811860058df23bbd1f
13241396 for (const item of allItems) {
13251397 if (item && item.type === 'menu' && item.menu) {
13261398 const action = findInMenu(item.menu, event.nativeEvent.menuId);
1327@@ -63,6 +87,15 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
1399@@ -63,6 +88,15 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
13281400 action.onPress();
13291401 return;
13301402 }
......@@ -1340,7 +1412,7 @@ index 68e3ba6a78314aba908d07098bb6982d11e6ee80..05cb8563d3a7d3811860058df23bbd1f
13401412 }
13411413 }
13421414 } : undefined;
1343@@ -70,6 +103,8 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
1415@@ -70,6 +104,8 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
13441416 userInterfaceStyle: props.experimental_userInterfaceStyle,
13451417 headerLeftBarButtonItems: preparedHeaderLeftBarButtonItems,
13461418 headerRightBarButtonItems: preparedHeaderRightBarButtonItems,
......@@ -1350,20 +1422,47 @@ index 68e3ba6a78314aba908d07098bb6982d11e6ee80..05cb8563d3a7d3811860058df23bbd1f
13501422 onPressHeaderBarButtonMenuItem: onPressHeaderBarButtonMenuItem,
13511423 ref: ref,
13521424diff --git a/lib/module/components/helpers/prepareHeaderBarButtonItems.js b/lib/module/components/helpers/prepareHeaderBarButtonItems.js
1353index 8a70ffd78617147418d628c03c580bb7ff9a8a72..8dbd0ba2ee61ee54d44cdf286a720ce26af01f26 100644
1425index 8a70ffd78617147418d628c03c580bb7ff9a8a72..bca930372d2a7c3f8e0a35c3dcf28474f899f21c 100644
13541426--- a/lib/module/components/helpers/prepareHeaderBarButtonItems.js
13551427+++ b/lib/module/components/helpers/prepareHeaderBarButtonItems.js
1356@@ -35,10 +35,31 @@ const prepareMenu = (menu, index, side, path = '') => {
1428@@ -1,5 +1,5 @@
1429 import { Image, processColor } from 'react-native';
1430-const prepareMenu = (menu, index, side, path = '') => {
1431+const prepareMenu = (menu, index, placement, path = '') => {
1432 return {
1433 ...menu,
1434 items: menu.items.map((menuItem, menuIndex) => {
1435@@ -20,7 +20,7 @@ const prepareMenu = (menu, index, side, path = '') => {
1436 xcassetName,
1437 imageSource,
1438 templateSource,
1439- ...prepareMenu(menuItem, index, side, currentPath)
1440+ ...prepareMenu(menuItem, index, placement, currentPath)
1441 };
1442 }
1443 return {
1444@@ -29,16 +29,40 @@ const prepareMenu = (menu, index, side, path = '') => {
1445 xcassetName,
1446 imageSource,
1447 templateSource,
1448- menuId: `${currentPath}-${index}-${side}`
1449+ menuId: `${currentPath}-${index}-${placement}`
1450 };
1451 })
13571452 };
13581453 };
1359 export const prepareHeaderBarButtonItems = (barButtonItems, side) => {
1454-export const prepareHeaderBarButtonItems = (barButtonItems, side) => {
13601455- return barButtonItems?.map((item, index) => {
1456+export const prepareHeaderBarButtonItems = (barButtonItems, placement) => {
13611457+ const items = Array.isArray(barButtonItems) ? barButtonItems : barButtonItems && typeof barButtonItems === 'object' && 'type' in barButtonItems ? [barButtonItems] : undefined;
13621458+ return items?.map((item, index) => {
13631459 if (item.type === 'spacing') {
13641460 return item;
13651461 }
13661462+ if (item.type === 'searchBarPlacement') {
1463+ if (placement !== 'toolbar') {
1464+ return null;
1465+ }
13671466+ return {
13681467+ ...item,
13691468+ searchBarPlacement: true
......@@ -1379,13 +1478,53 @@ index 8a70ffd78617147418d628c03c580bb7ff9a8a72..8dbd0ba2ee61ee54d44cdf286a720ce2
13791478+ return {
13801479+ ...item,
13811480+ mailSearchToolbar: true,
1382+ filterMenu: item.filterMenu ? prepareMenu(item.filterMenu, index, side, 'filter') : undefined,
1383+ composeMenu: item.composeMenu ? prepareMenu(item.composeMenu, index, side, 'compose') : undefined
1481+ filterMenu: item.filterMenu ? prepareMenu(item.filterMenu, index, placement, 'filter') : undefined,
1482+ composeMenu: item.composeMenu ? prepareMenu(item.composeMenu, index, placement, 'compose') : undefined
13841483+ };
13851484+ }
13861485 let imageSource, templateSource;
13871486 if (item.icon?.type === 'imageSource') {
13881487 imageSource = Image.resolveAssetSource(item.icon.imageSource);
1488@@ -71,16 +95,16 @@ export const prepareHeaderBarButtonItems = (barButtonItems, side) => {
1489 if (item.type === 'button') {
1490 return {
1491 ...processedItem,
1492- buttonId: `${index}-${side}`
1493+ buttonId: `${index}-${placement}`
1494 };
1495 }
1496 if (item.type === 'menu') {
1497 return {
1498 ...processedItem,
1499- menu: prepareMenu(item.menu, index, side)
1500+ menu: prepareMenu(item.menu, index, placement)
1501 };
1502 }
1503 return null;
1504- });
1505+ }).filter(item => item !== null);
1506 };
1507 //# sourceMappingURL=prepareHeaderBarButtonItems.js.map
1508\ No newline at end of file
1509diff --git a/lib/typescript/components/helpers/prepareHeaderBarButtonItems.d.ts b/lib/typescript/components/helpers/prepareHeaderBarButtonItems.d.ts
1510index c41ef943e554754f599f2fcd157dd0dc61d78b7e..ed02d44f78ee3f1387ba93bf292be2b99bb0e3b3 100644
1511--- a/lib/typescript/components/helpers/prepareHeaderBarButtonItems.d.ts
1512+++ b/lib/typescript/components/helpers/prepareHeaderBarButtonItems.d.ts
1513@@ -1,5 +1,5 @@
1514 import { HeaderBarButtonItem } from 'react-native-screens/types';
1515-export declare const prepareHeaderBarButtonItems: (barButtonItems: HeaderBarButtonItem[], side: "left" | "right") => (import("react-native-screens/types").HeaderBarButtonItemSpacing | {
1516+export declare const prepareHeaderBarButtonItems: (barButtonItems: HeaderBarButtonItem[], side: "left" | "right" | "center" | "toolbar") => (import("react-native-screens/types").HeaderBarButtonItemSpacing | {
1517 buttonId: string;
1518 imageSource: import("react-native").ImageResolvedAssetSource | undefined;
1519 templateSource: import("react-native").ImageResolvedAssetSource | undefined;
1520@@ -161,5 +161,5 @@ export declare const prepareHeaderBarButtonItems: (barButtonItems: HeaderBarButt
1521 identifier?: string | undefined;
1522 accessibilityLabel?: string | undefined;
1523 accessibilityHint?: string | undefined;
1524-} | null)[];
1525+})[];
1526 //# sourceMappingURL=prepareHeaderBarButtonItems.d.ts.map
1527\ No newline at end of file
13891528diff --git a/lib/typescript/fabric/ScreenStackHeaderConfigNativeComponent.d.ts b/lib/typescript/fabric/ScreenStackHeaderConfigNativeComponent.d.ts
13901529index a51d8648567cff29cd2be0d5262bd15c6fc69c61..8b7439aff4d7e6fe8a72bf0bac93c3c19568b1a6 100644
13911530--- a/lib/typescript/fabric/ScreenStackHeaderConfigNativeComponent.d.ts
......@@ -1423,7 +1562,7 @@ index a51d8648567cff29cd2be0d5262bd15c6fc69c61..8b7439aff4d7e6fe8a72bf0bac93c3c1
14231562 onPressHeaderBarButtonMenuItem?: CT.DirectEventHandler<OnPressHeaderBarButtonMenuItemEvent> | undefined;
14241563 synchronousShadowStateUpdatesEnabled?: CT.WithDefault<boolean, true>;
14251564diff --git a/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts b/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts
1426index 1ca926e6753294861f5f79ab6267dc7999f70639..15929959d1e274cc6abd55f2eb8db50ba102169a 100644
1565index 1ca926e6753294861f5f79ab6267dc7999f70639..94e676fdb08ff9d1cf81a1cf06a45e931b5461f1 100644
14271566--- a/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts
14281567+++ b/lib/typescript/fabric/ScreenStackHeaderSubviewNativeComponent.d.ts
14291568@@ -3,6 +3,7 @@ export type HeaderSubviewTypes = 'back' | 'right' | 'left' | 'title' | 'center'
......@@ -1576,7 +1715,7 @@ index 10a31341f4eff258a7f8ea6abae3eddf0dd21218..bab5031f5f9b246f15e5396f3f2fee90
15761715 * Custom Screen Transition
15771716 */
15781717diff --git a/src/components/ScreenStackHeaderConfig.tsx b/src/components/ScreenStackHeaderConfig.tsx
1579index c3a88b6f6c8362cf717372a35cdc35a6b397bdd0..9da8553b2c2e81fa13ef4f5b3e805efc0004ef29 100644
1718index c3a88b6f6c8362cf717372a35cdc35a6b397bdd0..9e8173da567933fd3f6819fb26bf4a0e87640767 100644
15801719--- a/src/components/ScreenStackHeaderConfig.tsx
15811720+++ b/src/components/ScreenStackHeaderConfig.tsx
15821721@@ -48,7 +48,12 @@ export const ScreenStackHeaderConfig = React.forwardRef<
......@@ -1599,11 +1738,11 @@ index c3a88b6f6c8362cf717372a35cdc35a6b397bdd0..9da8553b2c2e81fa13ef4f5b3e805efc
15991738 : undefined;
16001739+ const preparedHeaderCenterBarButtonItems =
16011740+ headerCenterBarButtonItems && isHeaderBarButtonsAvailableForCurrentPlatform
1602+ ? prepareHeaderBarButtonItems(headerCenterBarButtonItems, 'right')
1741+ ? prepareHeaderBarButtonItems(headerCenterBarButtonItems, 'center')
16031742+ : undefined;
16041743+ const preparedHeaderToolbarItems =
16051744+ headerToolbarItems && isHeaderBarButtonsAvailableForCurrentPlatform
1606+ ? prepareHeaderBarButtonItems(headerToolbarItems, 'right')
1745+ ? prepareHeaderBarButtonItems(headerToolbarItems, 'toolbar')
16071746+ : undefined;
16081747 const hasHeaderBarButtonItems =
16091748 isHeaderBarButtonsAvailableForCurrentPlatform &&
......@@ -1634,10 +1773,12 @@ index c3a88b6f6c8362cf717372a35cdc35a6b397bdd0..9da8553b2c2e81fa13ef4f5b3e805efc
16341773 );
16351774 if (
16361775 pressedItem &&
1637@@ -82,6 +101,31 @@ export const ScreenStackHeaderConfig = React.forwardRef<
1776@@ -81,6 +100,32 @@ export const ScreenStackHeaderConfig = React.forwardRef<
1777 pressedItem.onPress
16381778 ) {
16391779 pressedItem.onPress();
1640 }
1780+ return;
1781+ }
16411782+ for (const item of allItems) {
16421783+ if (!item || item.type !== 'mailSearchToolbar') {
16431784+ continue;
......@@ -1662,11 +1803,10 @@ index c3a88b6f6c8362cf717372a35cdc35a6b397bdd0..9da8553b2c2e81fa13ef4f5b3e805efc
16621803+ );
16631804+ return;
16641805+ }
1665+ }
1806 }
16661807 }
16671808 : undefined;
1668
1669@@ -111,6 +155,8 @@ export const ScreenStackHeaderConfig = React.forwardRef<
1809@@ -111,6 +156,8 @@ export const ScreenStackHeaderConfig = React.forwardRef<
16701810 const allItems = [
16711811 ...(preparedHeaderLeftBarButtonItems ?? []),
16721812 ...(preparedHeaderRightBarButtonItems ?? []),
......@@ -1675,7 +1815,7 @@ index c3a88b6f6c8362cf717372a35cdc35a6b397bdd0..9da8553b2c2e81fa13ef4f5b3e805efc
16751815 ];
16761816 for (const item of allItems) {
16771817 if (item && item.type === 'menu' && item.menu) {
1678@@ -119,6 +165,17 @@ export const ScreenStackHeaderConfig = React.forwardRef<
1818@@ -119,6 +166,17 @@ export const ScreenStackHeaderConfig = React.forwardRef<
16791819 action.onPress();
16801820 return;
16811821 }
......@@ -1693,7 +1833,7 @@ index c3a88b6f6c8362cf717372a35cdc35a6b397bdd0..9da8553b2c2e81fa13ef4f5b3e805efc
16931833 }
16941834 }
16951835 }
1696@@ -130,6 +187,8 @@ export const ScreenStackHeaderConfig = React.forwardRef<
1836@@ -130,6 +188,8 @@ export const ScreenStackHeaderConfig = React.forwardRef<
16971837 userInterfaceStyle={props.experimental_userInterfaceStyle}
16981838 headerLeftBarButtonItems={preparedHeaderLeftBarButtonItems}
16991839 headerRightBarButtonItems={preparedHeaderRightBarButtonItems}
......@@ -1703,20 +1843,47 @@ index c3a88b6f6c8362cf717372a35cdc35a6b397bdd0..9da8553b2c2e81fa13ef4f5b3e805efc
17031843 onPressHeaderBarButtonMenuItem={onPressHeaderBarButtonMenuItem}
17041844 ref={ref}
17051845diff --git a/src/components/helpers/prepareHeaderBarButtonItems.ts b/src/components/helpers/prepareHeaderBarButtonItems.ts
1706index be2c24dfee77f5883b5ab1d7473be80933b5dc6f..0b038d2005b2d51bbb2ab1d7dba7eaccda83d42f 100644
1846index be2c24dfee77f5883b5ab1d7473be80933b5dc6f..2008a342f13806641909c19a9f4ab6829f239016 100644
17071847--- a/src/components/helpers/prepareHeaderBarButtonItems.ts
17081848+++ b/src/components/helpers/prepareHeaderBarButtonItems.ts
1709@@ -50,13 +50,43 @@ const prepareMenu = (
1849@@ -7,7 +7,7 @@ import {
1850 const prepareMenu = (
1851 menu: HeaderBarButtonItemWithMenu['menu'],
1852 index: number,
1853- side: 'left' | 'right',
1854+ placement: 'left' | 'right' | 'center' | 'toolbar',
1855 path: string = '',
1856 ): HeaderBarButtonItemWithMenu['menu'] => {
1857 return {
1858@@ -34,7 +34,7 @@ const prepareMenu = (
1859 xcassetName,
1860 imageSource,
1861 templateSource,
1862- ...prepareMenu(menuItem, index, side, currentPath),
1863+ ...prepareMenu(menuItem, index, placement, currentPath),
1864 };
1865 }
1866 return {
1867@@ -43,20 +43,53 @@ const prepareMenu = (
1868 xcassetName,
1869 imageSource,
1870 templateSource,
1871- menuId: `${currentPath}-${index}-${side}`,
1872+ menuId: `${currentPath}-${index}-${placement}`,
1873 };
1874 }),
1875 };
17101876 };
17111877
17121878 export const prepareHeaderBarButtonItems = (
17131879- barButtonItems: HeaderBarButtonItem[],
1880- side: 'left' | 'right',
17141881+ barButtonItems:
17151882+ | HeaderBarButtonItem[]
17161883+ | HeaderBarButtonItem
17171884+ | null
17181885+ | undefined,
1719 side: 'left' | 'right',
1886+ placement: 'left' | 'right' | 'center' | 'toolbar',
17201887 ) => {
17211888- return barButtonItems?.map((item, index) => {
17221889+ const items = Array.isArray(barButtonItems)
......@@ -1730,6 +1897,9 @@ index be2c24dfee77f5883b5ab1d7473be80933b5dc6f..0b038d2005b2d51bbb2ab1d7dba7eacc
17301897 return item;
17311898 }
17321899+ if (item.type === 'searchBarPlacement') {
1900+ if (placement !== 'toolbar') {
1901+ return null;
1902+ }
17331903+ return {
17341904+ ...item,
17351905+ searchBarPlacement: true,
......@@ -1745,13 +1915,32 @@ index be2c24dfee77f5883b5ab1d7473be80933b5dc6f..0b038d2005b2d51bbb2ab1d7dba7eacc
17451915+ return {
17461916+ ...item,
17471917+ mailSearchToolbar: true,
1748+ filterMenu: item.filterMenu ? prepareMenu(item.filterMenu, index, side, 'filter') : undefined,
1749+ composeMenu: item.composeMenu ? prepareMenu(item.composeMenu, index, side, 'compose') : undefined,
1918+ filterMenu: item.filterMenu ? prepareMenu(item.filterMenu, index, placement, 'filter') : undefined,
1919+ composeMenu: item.composeMenu ? prepareMenu(item.composeMenu, index, placement, 'compose') : undefined,
17501920+ };
17511921+ }
17521922 let imageSource, templateSource;
17531923 if (item.icon?.type === 'imageSource') {
17541924 imageSource = Image.resolveAssetSource(item.icon.imageSource);
1925@@ -91,15 +124,15 @@ export const prepareHeaderBarButtonItems = (
1926 if (item.type === 'button') {
1927 return {
1928 ...processedItem,
1929- buttonId: `${index}-${side}`,
1930+ buttonId: `${index}-${placement}`,
1931 };
1932 }
1933 if (item.type === 'menu') {
1934 return {
1935 ...processedItem,
1936- menu: prepareMenu(item.menu, index, side),
1937+ menu: prepareMenu(item.menu, index, placement),
1938 };
1939 }
1940 return null;
1941- });
1942+ }).filter(item => item !== null);
1943 };
17551944diff --git a/src/fabric/ScreenStackHeaderConfigNativeComponent.ts b/src/fabric/ScreenStackHeaderConfigNativeComponent.ts
17561945index a80d9fef0c0ab5ce8221c5e0c7fae65162bc6164..fc16f51425702a418c09de553a54753331f19d15 100644
17571946--- a/src/fabric/ScreenStackHeaderConfigNativeComponent.ts
pnpm-lock.yaml+6-6
......@@ -94,7 +94,7 @@ patchedDependencies:
9494 react-native-gesture-handler@2.32.0: 808eb26f9e57cf4945efd3985af4d9c764da6f91f4c9764433cc868602bbf4d3
9595 react-native-keyboard-controller@1.21.13: 20be72c84d74253acdcfefbc6defe36dc396944f1a44cab2bdd0e3cd572ae008
9696 react-native-nitro-modules@0.35.9: 825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675
97 react-native-screens@4.26.2: 11566900d0c90514867ede5a2333ba412ab3b5a532ba1955e6306fff8b12de1a
97 react-native-screens@4.26.2: 5d2e7beefe55f0b7ee157993cbc0ec38d32aa558e4e780764381fe8f96ba728d
9898
9999importers:
100100
......@@ -248,7 +248,7 @@ importers:
248248 version: 7.3.4(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
249249 '@react-navigation/native-stack':
250250 specifier: 7.17.6
251 version: 7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(df02ad16d8d764efad8e903a73894296)
251 version: 7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(1d8c6f507069f6f6aada9b6419c84a6c)
252252 '@shikijs/core':
253253 specifier: 4.2.0
254254 version: 4.2.0
......@@ -416,7 +416,7 @@ importers:
416416 version: 5.7.0(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
417417 react-native-screens:
418418 specifier: ~4.26.0
419 version: 4.26.2(patch_hash=11566900d0c90514867ede5a2333ba412ab3b5a532ba1955e6306fff8b12de1a)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
419 version: 4.26.2(patch_hash=5d2e7beefe55f0b7ee157993cbc0ec38d32aa558e4e780764381fe8f96ba728d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
420420 react-native-shiki-engine:
421421 specifier: ^0.3.12
422422 version: 0.3.12(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
......@@ -13880,7 +13880,7 @@ snapshots:
1388013880 optionalDependencies:
1388113881 '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
1388213882
13883 '@react-navigation/native-stack@7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(df02ad16d8d764efad8e903a73894296)':
13883 '@react-navigation/native-stack@7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(1d8c6f507069f6f6aada9b6419c84a6c)':
1388413884 dependencies:
1388513885 '@react-navigation/elements': 2.9.26(c10301b6e0c42fc6434d2b643197a81e)
1388613886 '@react-navigation/native': 7.3.4(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
......@@ -13888,7 +13888,7 @@ snapshots:
1388813888 react: 19.2.3
1388913889 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)
1389013890 react-native-safe-area-context: 5.7.0(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
13891 react-native-screens: 4.26.2(patch_hash=11566900d0c90514867ede5a2333ba412ab3b5a532ba1955e6306fff8b12de1a)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
13891 react-native-screens: 4.26.2(patch_hash=5d2e7beefe55f0b7ee157993cbc0ec38d32aa558e4e780764381fe8f96ba728d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
1389213892 sf-symbols-typescript: 2.2.0
1389313893 warn-once: 0.1.1
1389413894 transitivePeerDependencies:
......@@ -19492,7 +19492,7 @@ snapshots:
1949219492 react: 19.2.3
1949319493 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)
1949419494
19495 react-native-screens@4.26.2(patch_hash=11566900d0c90514867ede5a2333ba412ab3b5a532ba1955e6306fff8b12de1a)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3):
19495 react-native-screens@4.26.2(patch_hash=5d2e7beefe55f0b7ee157993cbc0ec38d32aa558e4e780764381fe8f96ba728d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3):
1949619496 dependencies:
1949719497 react: 19.2.3
1949819498 react-freeze: 1.0.4(react@19.2.3)