1542 lines · 74.0 KB · Text
RawDownload
diff --git a/ios/RNSBarButtonItem.h b/ios/RNSBarButtonItem.hindex ea5325ea8d17b1ddfa790ff8dab48ce83142d4d3..acd2fd7ceb7162ed300abc4d3c9f0c24f4d63898 100644--- a/ios/RNSBarButtonItem.h+++ b/ios/RNSBarButtonItem.h@@ -13,4 +13,8 @@ typedef void (^RNSBarButtonMenuItemAction)(NSString *menuId);                     menuAction:(RNSBarButtonMenuItemAction)menuAction                    imageLoader:(RCTImageLoader *)imageLoader; ++ (UIMenu *)initUIMenuWithDict:(NSDictionary<NSString *, id> *)dict+                    menuAction:(RNSBarButtonMenuItemAction)menuAction+                   imageLoader:(RCTImageLoader *)imageLoader;+ @enddiff --git a/ios/RNSBarButtonItem.mm b/ios/RNSBarButtonItem.mmindex 0eb1f09dee82edd99e3e614938233db5cdeaebfe..73298f10807520970f625e166d7f5dd1338206f1 100644--- a/ios/RNSBarButtonItem.mm+++ b/ios/RNSBarButtonItem.mm@@ -110,6 +110,58 @@ - (instancetype)initWithConfig:(NSDictionary<NSString *, id> *)dict   if (dict[@"accessibilityHint"]) {     self.accessibilityHint = dict[@"accessibilityHint"];   }+#if !TARGET_OS_TV && RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)+  NSNumber *glassEffectNum = dict[@"glassEffect"];+  if (@available(iOS 26.0, *)) {+    if ([glassEffectNum boolValue]) {+      UIButtonConfiguration *configuration = [UIButtonConfiguration glassButtonConfiguration];+      configuration.cornerStyle = UIButtonConfigurationCornerStyleCapsule;+      UIBackgroundConfiguration *background = configuration.background;+      background.strokeWidth = 0.0;+      background.strokeColor = UIColor.clearColor;+      configuration.background = background;+      configuration.contentInsets = NSDirectionalEdgeInsetsZero;+      if (title != nil) {+        configuration.title = title;+      }++      UIButton *button = [UIButton buttonWithConfiguration:configuration primaryAction:nil];+      button.translatesAutoresizingMaskIntoConstraints = NO;+      button.enabled = self.enabled;+      button.accessibilityLabel = self.accessibilityLabel;+      button.accessibilityHint = self.accessibilityHint;++      CGFloat resolvedWidth = width != nil ? width.doubleValue : 56.0;+      [button.widthAnchor constraintEqualToConstant:resolvedWidth].active = YES;+      [button.heightAnchor constraintEqualToConstant:50.0].active = YES;++      [[self class] resolveImageFromConfig:dict+                               imageLoader:imageLoader+                           completionBlock:^(UIImage *img) {+                             UIButtonConfiguration *updatedConfiguration = button.configuration;+                             updatedConfiguration.image = img;+                             button.configuration = updatedConfiguration;+                           }];++      NSDictionary *menu = dict[@"menu"];+      if (menu) {+        button.menu = [[self class] initUIMenuWithDict:menu menuAction:menuAction imageLoader:imageLoader];+        button.showsMenuAsPrimaryAction = YES;+      } else {+        NSString *buttonId = dict[@"buttonId"];+        if (buttonId && action) {+          UIAction *pressAction = [UIAction actionWithHandler:^(__kindof UIAction *_Nonnull sender) {+            action(buttonId);+          }];+          [button addAction:pressAction forControlEvents:UIControlEventTouchUpInside];+        }+      }++      self.customView = button;+      return self;+    }+  }+#endif  #if !TARGET_OS_TV || __TV_OS_VERSION_MAX_ALLOWED >= 170000   if (@available(tvOS 17.0, *)) {diff --git a/ios/RNSScreenStackHeaderConfig.h b/ios/RNSScreenStackHeaderConfig.hindex 919b984edc9f91ee9ac26faf257d8a721e26457c..5bb0cd6736ed6bc51db57e2a9326f758f22c51d8 100644--- a/ios/RNSScreenStackHeaderConfig.h+++ b/ios/RNSScreenStackHeaderConfig.h@@ -21,6 +21,8 @@ NS_ASSUME_NONNULL_BEGIN  @property (nonatomic, retain) NSString *title;+@property (nonatomic, retain) NSString *subtitle;+@property (nonatomic, retain) NSString *largeSubtitle; @property (nonatomic, retain) NSString *titleFontFamily; @property (nonatomic, retain) NSNumber *titleFontSize; @property (nonatomic, retain) NSString *titleFontWeight;@@ -45,9 +47,12 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) BOOL backButtonInCustomView; @property (nonatomic) UISemanticContentAttribute direction; @property (nonatomic) UINavigationItemBackButtonDisplayMode backButtonDisplayMode;+@property (nonatomic) NSInteger navigationItemStyle; @property (nonatomic) RNSBlurEffectStyle blurEffect; @property (nonatomic, copy, nullable) NSArray<NSDictionary<NSString *, id> *> *headerRightBarButtonItems; @property (nonatomic, copy, nullable) NSArray<NSDictionary<NSString *, id> *> *headerLeftBarButtonItems;+@property (nonatomic, copy, nullable) NSArray<NSDictionary<NSString *, id> *> *headerCenterBarButtonItems;+@property (nonatomic, copy, nullable) NSArray<NSDictionary<NSString *, id> *> *headerToolbarItems; @property (nonatomic, readwrite) BOOL synchronousShadowStateUpdatesEnabled;  NS_ASSUME_NONNULL_ENDdiff --git a/ios/RNSScreenStackHeaderConfig.mm b/ios/RNSScreenStackHeaderConfig.mmindex 5970e3e3a624b9498b8dedfc16831df03a274d0c..073a72a68688369f39e54b61d1381b8d652cbb83 100644--- a/ios/RNSScreenStackHeaderConfig.mm+++ b/ios/RNSScreenStackHeaderConfig.mm@@ -30,6 +30,20 @@ static const NSNumber *const DEFAULT_TITLE_FONT_SIZE = @17; static const NSNumber *const DEFAULT_TITLE_LARGE_FONT_SIZE = @34; +static NSInteger navigationItemStyleFromCppEquivalent(+    react::RNSScreenStackHeaderConfigNavigationItemStyle navigationItemStyle)+{+  switch (navigationItemStyle) {+    case react::RNSScreenStackHeaderConfigNavigationItemStyle::Browser:+      return UINavigationItemStyleBrowser;+    case react::RNSScreenStackHeaderConfigNavigationItemStyle::Editor:+      return UINavigationItemStyleEditor;+    case react::RNSScreenStackHeaderConfigNavigationItemStyle::Navigator:+    default:+      return UINavigationItemStyleNavigator;+  }+}+ @interface RCTImageLoader (Private) - (id<RCTImageCache>)imageCache; @end@@ -47,6 +61,9 @@ + (BOOL)rnscreens_isBlankOrNull:(NSString *)string @end  @interface RNSScreenStackHeaderConfig () <RCTMountingTransactionObserving>+#if !TARGET_OS_TV+- (NSArray<UIBarButtonItemGroup *> *)barButtonItemGroupsFromItems:(NSArray<UIBarButtonItem *> *)items;+#endif @end  @implementation RNSScreenStackHeaderConfig {@@ -81,6 +98,7 @@ - (void)initProps   self.hidden = YES;   _reactSubviews = [NSMutableArray new];   _backTitleVisible = YES;+  _navigationItemStyle = UINavigationItemStyleNavigator;   _blurEffect = RNSBlurEffectStyleNone; } @@ -496,6 +514,10 @@ + (void)updateViewController:(UIViewController *)vc    if (shouldHide) {     navitem.title = config.title;+    if (@available(iOS 26.0, *)) {+      navitem.subtitle = config.subtitle;+      navitem.largeSubtitle = config.largeSubtitle;+    }      // Setting navigation bar visibility is split to mitigate iOS 26 bug with bar button items.     [navctr setNavigationBarHidden:YES animated:animated];@@ -512,11 +534,19 @@ + (void)updateViewController:(UIViewController *)vc   }   navitem.largeTitleDisplayMode =       config.largeTitle ? UINavigationItemLargeTitleDisplayModeAlways : UINavigationItemLargeTitleDisplayModeNever;++  if (@available(iOS 16.0, *)) {+    navitem.style = (UINavigationItemStyle)config.navigationItemStyle;+  } #endif    UINavigationBarAppearance *appearance = [self buildAppearance:vc withConfig:config];   navitem.standardAppearance = appearance;   navitem.compactAppearance = appearance;+  if (@available(iOS 26.0, *)) {+    navitem.subtitle = config.subtitle;+    navitem.largeSubtitle = config.largeSubtitle;+  }  // appearance does not apply to the tvOS so we need to use lagacy customization #if TARGET_OS_TV@@ -637,10 +667,286 @@ + (void)updateViewController:(UIViewController *)vc   // This assignment should be done after `navitem.titleView = ...` assignment (iOS 16.0 bug).   // See: https://github.com/software-mansion/react-native-screens/issues/1570 (comments)   navitem.title = config.title;-  navitem.leftBarButtonItems = [config barButtonItemsFromConfigs:config.headerLeftBarButtonItems-                                                withCurrentItems:navitem.leftBarButtonItems];-  navitem.rightBarButtonItems = [config barButtonItemsFromConfigs:config.headerRightBarButtonItems-                                                 withCurrentItems:navitem.rightBarButtonItems];+  NSArray<UIBarButtonItem *> *leftBarButtonItems = [config barButtonItemsFromConfigs:config.headerLeftBarButtonItems+                                                                    withCurrentItems:navitem.leftBarButtonItems+                                                                      navigationItem:navitem];+  NSArray<UIBarButtonItem *> *rightBarButtonItems = [config barButtonItemsFromConfigs:config.headerRightBarButtonItems+                                                                     withCurrentItems:navitem.rightBarButtonItems+                                                                       navigationItem:navitem];+  NSArray<UIBarButtonItem *> *centerBarButtonItems = [config barButtonItemsFromConfigs:config.headerCenterBarButtonItems+                                                                      withCurrentItems:@[]+                                                                        navigationItem:navitem];+#if !TARGET_OS_TV+  if (@available(iOS 16.0, *)) {+    navitem.leadingItemGroups = [config barButtonItemGroupsFromItems:leftBarButtonItems];+    navitem.trailingItemGroups = [config barButtonItemGroupsFromItems:rightBarButtonItems];+    if (@available(iOS 26.0, *)) {+      navitem.centerItemGroups = [config barButtonItemGroupsFromItems:centerBarButtonItems];+    }+    navitem.leftBarButtonItems = nil;+    navitem.rightBarButtonItems = nil;+  } else {+    navitem.leftBarButtonItems = leftBarButtonItems;+    navitem.rightBarButtonItems = rightBarButtonItems;+  }+#else+  navitem.leftBarButtonItems = leftBarButtonItems;+  navitem.rightBarButtonItems = rightBarButtonItems;+#endif+  NSDictionary<NSString *, id> *mailSearchToolbarConfig = nil;+  for (NSDictionary<NSString *, id> *toolbarConfig in config.headerToolbarItems) {+    if (toolbarConfig[@"mailSearchToolbar"]) {+      mailSearchToolbarConfig = toolbarConfig;+      break;+    }+  }+  if (mailSearchToolbarConfig == nil) {+    for (NSDictionary<NSString *, id> *rightConfig in config.headerRightBarButtonItems) {+      if ([rightConfig[@"bottomMailSearchToolbar"] boolValue]) {+        mailSearchToolbarConfig = rightConfig;+        break;+      }+    }+  }++  static NSInteger const RNSMailSearchToolbarViewTag = 260628;+  UIView *chromeHostView = vc.view;+  UIView *existingMailSearchToolbar = [chromeHostView viewWithTag:RNSMailSearchToolbarViewTag];+  if (existingMailSearchToolbar == nil) {+    existingMailSearchToolbar = [vc.view viewWithTag:RNSMailSearchToolbarViewTag];+  }+  [existingMailSearchToolbar removeFromSuperview];++  if (mailSearchToolbarConfig != nil) {+#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)+    if (@available(iOS 26.0, *)) {+      CGFloat horizontalInset = 18.0;+      NSNumber *width = mailSearchToolbarConfig[@"width"];+      CGFloat hostWidth = chromeHostView.bounds.size.width;+      if (hostWidth <= 0.0) {+        hostWidth = vc.view.bounds.size.width;+      }+      if (hostWidth <= 0.0) {+        hostWidth = [UIScreen mainScreen].bounds.size.width;+      }+      CGFloat fallbackWidth = MIN(560.0, MAX(300.0, hostWidth - (horizontalInset * 2.0)));+      CGFloat resolvedWidth = width != nil ? width.doubleValue : fallbackWidth;+      if (hostWidth > 0.0) {+        resolvedWidth = MIN(resolvedWidth, MAX(260.0, hostWidth - 12.0));+      }++      CGFloat toolbarHeight = 48.0;+      CGFloat toolbarRadius = toolbarHeight / 2.0;+      CGFloat buttonSize = toolbarHeight;+      CGFloat sideButtonReserve = buttonSize + 8.0;+      CGFloat searchFieldHeight = 42.0;+      CGFloat toolbarBottomSpacing = 4.0;+      UIFont *searchFont = [UIFont preferredFontForTextStyle:UIFontTextStyleTitle3];+      NSDictionary<NSAttributedStringKey, id> *placeholderAttributes = @{+        NSForegroundColorAttributeName : UIColor.secondaryLabelColor,+        NSFontAttributeName : searchFont,+      };+      UIKeyboardLayoutGuide *keyboardLayoutGuide = chromeHostView.keyboardLayoutGuide;++      UIView *toolbarHost = [[UIView alloc] init];+      toolbarHost.tag = RNSMailSearchToolbarViewTag;+      toolbarHost.translatesAutoresizingMaskIntoConstraints = NO;+      [chromeHostView addSubview:toolbarHost];+      // Keyboard avoidance is best-effort: on the iOS 27 beta the keyboard+      // layout guide no longer rests at the bottom safe-area edge while the+      // keyboard is hidden, which pushed the toolbar offscreen. The required+      // resting position is the safe area; the keyboard guide only pulls the+      // toolbar up when it actually tracks a visible keyboard.+      NSLayoutConstraint *keyboardAvoidConstraint =+          [toolbarHost.bottomAnchor constraintEqualToAnchor:keyboardLayoutGuide.topAnchor+                                                   constant:-toolbarBottomSpacing];+      keyboardAvoidConstraint.priority = UILayoutPriorityDefaultHigh;+      NSLayoutConstraint *restingBottomConstraint =+          [toolbarHost.bottomAnchor constraintEqualToAnchor:chromeHostView.safeAreaLayoutGuide.bottomAnchor+                                                   constant:-toolbarBottomSpacing];+      restingBottomConstraint.priority = UILayoutPriorityDefaultLow;+      [NSLayoutConstraint activateConstraints:@[+        [toolbarHost.centerXAnchor constraintEqualToAnchor:chromeHostView.centerXAnchor],+        [toolbarHost.bottomAnchor constraintLessThanOrEqualToAnchor:chromeHostView.safeAreaLayoutGuide.bottomAnchor+                                                           constant:-toolbarBottomSpacing],+        keyboardAvoidConstraint,+        restingBottomConstraint,+        [toolbarHost.widthAnchor constraintEqualToConstant:resolvedWidth],+        [toolbarHost.heightAnchor constraintEqualToConstant:toolbarHeight],+      ]];+      [chromeHostView bringSubviewToFront:toolbarHost];++      UIGlassEffect *glassEffect = [UIGlassEffect effectWithStyle:UIGlassEffectStyleRegular];+      glassEffect.interactive = YES;+      UIVisualEffectView *glassView = [[UIVisualEffectView alloc] initWithEffect:glassEffect];+      glassView.clipsToBounds = YES;+      glassView.layer.cornerRadius = toolbarRadius;+      glassView.translatesAutoresizingMaskIntoConstraints = NO;+      [toolbarHost addSubview:glassView];+      BOOL hasFilterButton =+          mailSearchToolbarConfig[@"filterButtonId"] != nil || mailSearchToolbarConfig[@"filterMenu"] != nil;+      BOOL hasComposeButton =+          mailSearchToolbarConfig[@"composeButtonId"] != nil || mailSearchToolbarConfig[@"composeMenu"] != nil;+      CGFloat glassLeadingInset = hasFilterButton ? sideButtonReserve : 0.0;+      CGFloat glassTrailingInset = hasComposeButton ? -sideButtonReserve : 0.0;+      [NSLayoutConstraint activateConstraints:@[+        [glassView.leadingAnchor constraintEqualToAnchor:toolbarHost.leadingAnchor constant:glassLeadingInset],+        [glassView.trailingAnchor constraintEqualToAnchor:toolbarHost.trailingAnchor constant:glassTrailingInset],+        [glassView.centerYAnchor constraintEqualToAnchor:toolbarHost.centerYAnchor],+        [glassView.heightAnchor constraintEqualToConstant:toolbarHeight],+      ]];++      void (^emitButtonPress)(NSString *) = ^(NSString *buttonId) {+        auto eventEmitter = std::static_pointer_cast<const facebook::react::RNSScreenStackHeaderConfigEventEmitter>(+            config->_eventEmitter);+        if (eventEmitter && buttonId) {+          eventEmitter->onPressHeaderBarButtonItem(+              facebook::react::RNSScreenStackHeaderConfigEventEmitter::OnPressHeaderBarButtonItem{+                  .buttonId = std::string([buttonId UTF8String])});+        }+      };++      void (^emitMenuPress)(NSString *) = ^(NSString *menuId) {+        auto eventEmitter = std::static_pointer_cast<const facebook::react::RNSScreenStackHeaderConfigEventEmitter>(+            config->_eventEmitter);+        if (eventEmitter && menuId) {+          eventEmitter->onPressHeaderBarButtonMenuItem(+              facebook::react::RNSScreenStackHeaderConfigEventEmitter::OnPressHeaderBarButtonMenuItem{+                  .menuId = std::string([menuId UTF8String])});+        }+      };++      UIButton *(^makeGlassButton)(NSString *, NSString *, NSDictionary *) =+          ^UIButton *(NSString *systemImageName, NSString *buttonId, NSDictionary *menuConfig) {+        UIButtonConfiguration *configuration = [UIButtonConfiguration glassButtonConfiguration];+        configuration.cornerStyle = UIButtonConfigurationCornerStyleCapsule;+        configuration.image = [UIImage systemImageNamed:systemImageName ?: @"circle"];+        configuration.baseForegroundColor = UIColor.labelColor;+        UIButton *button = [UIButton buttonWithConfiguration:configuration primaryAction:nil];+        if (menuConfig != nil) {+          button.menu = [RNSBarButtonItem initUIMenuWithDict:menuConfig+                                                  menuAction:emitMenuPress+                                                 imageLoader:config->_imageLoader];+          button.showsMenuAsPrimaryAction = YES;+        } else if (buttonId != nil) {+          UIAction *action = [UIAction actionWithHandler:^(__kindof UIAction *_Nonnull action) {+            emitButtonPress(buttonId);+          }];+          [button addAction:action forControlEvents:UIControlEventTouchUpInside];+        }+        return button;+      };++      BOOL useFallbackSearchField = [mailSearchToolbarConfig[@"useFallbackSearchField"] boolValue];+      UISearchBar *searchBar =+          !useFallbackSearchField && navitem.searchController != nil ? navitem.searchController.searchBar : nil;+      NSString *placeholder = mailSearchToolbarConfig[@"placeholder"];+      if (searchBar != nil) {+        if (placeholder != nil) {+          searchBar.placeholder = placeholder;+        }+        searchBar.hidden = NO;+        searchBar.alpha = 1.0;+        searchBar.userInteractionEnabled = YES;+        searchBar.searchBarStyle = UISearchBarStyleMinimal;+        searchBar.backgroundImage = [UIImage new];+        searchBar.searchTextField.hidden = NO;+        searchBar.searchTextField.alpha = 1.0;+        searchBar.searchTextField.backgroundColor = UIColor.clearColor;+        searchBar.searchTextField.font = searchFont;+        searchBar.searchTextField.adjustsFontForContentSizeCategory = YES;+        searchBar.searchTextField.textColor = UIColor.labelColor;+        searchBar.searchTextField.tintColor = UIColor.labelColor;+        if (placeholder != nil) {+          searchBar.searchTextField.attributedPlaceholder =+              [[NSAttributedString alloc] initWithString:placeholder attributes:placeholderAttributes];+        }+        searchBar.translatesAutoresizingMaskIntoConstraints = NO;+        [glassView.contentView addSubview:searchBar];+        [NSLayoutConstraint activateConstraints:@[+          [searchBar.leadingAnchor constraintEqualToAnchor:glassView.contentView.leadingAnchor constant:8.0],+          [searchBar.trailingAnchor constraintEqualToAnchor:glassView.contentView.trailingAnchor constant:-8.0],+          [searchBar.centerYAnchor constraintEqualToAnchor:glassView.contentView.centerYAnchor],+          [searchBar.heightAnchor constraintEqualToConstant:searchFieldHeight],+        ]];+      } else {+        UISearchTextField *searchField = [UISearchTextField new];+        if (placeholder != nil) {+          searchField.placeholder = placeholder;+          searchField.attributedPlaceholder =+              [[NSAttributedString alloc] initWithString:placeholder attributes:placeholderAttributes];+        }+        NSString *searchTextChangeId = mailSearchToolbarConfig[@"searchTextChangeId"];+        if (searchTextChangeId != nil) {+          [searchField addAction:[UIAction actionWithHandler:^(__kindof UIAction *_Nonnull action) {+                         UISearchTextField *field = (UISearchTextField *)action.sender;+                         emitButtonPress([NSString stringWithFormat:@"%@:%@", searchTextChangeId, field.text ?: @""]);+                       }]+              forControlEvents:UIControlEventEditingChanged];+        }+        searchField.borderStyle = UITextBorderStyleNone;+        searchField.font = searchFont;+        searchField.adjustsFontForContentSizeCategory = YES;+        searchField.textColor = UIColor.labelColor;+        searchField.tintColor = UIColor.labelColor;+        searchField.translatesAutoresizingMaskIntoConstraints = NO;+        [glassView.contentView addSubview:searchField];+        [NSLayoutConstraint activateConstraints:@[+          [searchField.leadingAnchor constraintEqualToAnchor:glassView.contentView.leadingAnchor constant:14.0],+          [searchField.trailingAnchor constraintEqualToAnchor:glassView.contentView.trailingAnchor constant:-14.0],+          [searchField.centerYAnchor constraintEqualToAnchor:glassView.contentView.centerYAnchor],+          [searchField.heightAnchor constraintEqualToConstant:searchFieldHeight],+        ]];+      }++      if (hasFilterButton) {+        UIButton *filterButton = makeGlassButton(+            mailSearchToolbarConfig[@"filterSystemImageName"] ?: @"line.3.horizontal.decrease",+            mailSearchToolbarConfig[@"filterButtonId"],+            mailSearchToolbarConfig[@"filterMenu"]);+        filterButton.translatesAutoresizingMaskIntoConstraints = NO;+        [toolbarHost addSubview:filterButton];+        [NSLayoutConstraint activateConstraints:@[+          [filterButton.leadingAnchor constraintEqualToAnchor:toolbarHost.leadingAnchor],+          [filterButton.centerYAnchor constraintEqualToAnchor:toolbarHost.centerYAnchor],+          [filterButton.widthAnchor constraintEqualToConstant:buttonSize],+          [filterButton.heightAnchor constraintEqualToConstant:buttonSize],+        ]];+      }++      if (hasComposeButton) {+        UIButton *composeButton = makeGlassButton(+            mailSearchToolbarConfig[@"composeSystemImageName"] ?: @"square.and.pencil",+            mailSearchToolbarConfig[@"composeButtonId"],+            mailSearchToolbarConfig[@"composeMenu"]);+        composeButton.translatesAutoresizingMaskIntoConstraints = NO;+        [toolbarHost addSubview:composeButton];+        [NSLayoutConstraint activateConstraints:@[+          [composeButton.trailingAnchor constraintEqualToAnchor:toolbarHost.trailingAnchor],+          [composeButton.centerYAnchor constraintEqualToAnchor:toolbarHost.centerYAnchor],+          [composeButton.widthAnchor constraintEqualToConstant:buttonSize],+          [composeButton.heightAnchor constraintEqualToConstant:buttonSize],+        ]];+      }+    }+#endif+  }++  NSArray<NSDictionary<NSString *, id> *> *navigationToolbarConfigs = config.headerToolbarItems;+  if (mailSearchToolbarConfig != nil) {+    navigationToolbarConfigs = @[];+  }++  NSArray<UIBarButtonItem *> *toolbarItems = [config barButtonItemsFromConfigs:navigationToolbarConfigs+                                                               withCurrentItems:@[]+                                                                 navigationItem:navitem];+  if (toolbarItems.count > 0) {+    vc.toolbarItems = toolbarItems;+    [navctr setToolbarHidden:NO animated:animated];+  } else {+    vc.toolbarItems = nil;+    [navctr setToolbarHidden:YES animated:animated];+  }    // Setting navigation bar visibility is split to mitigate iOS 26 bug with bar button items   // (setting nav bar visibility should be done after `navitem.*BarButtonItems`).@@ -773,6 +1079,7 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *  - (NSArray<UIBarButtonItem *> *)barButtonItemsFromConfigs:(NSArray<NSDictionary<NSString *, id> *> *)dicts                                          withCurrentItems:(NSArray<UIBarButtonItem *> *)currentItems+                                           navigationItem:(UINavigationItem *)navitem {   if (dicts.count == 0) {     return currentItems;@@ -781,7 +1088,197 @@ - (void)applySemanticContentAttributeIfNeededToNavCtrl:(UINavigationController *   [items addObjectsFromArray:currentItems];   for (NSUInteger i = 0; i < dicts.count; i++) {     NSDictionary *dict = dicts[i];-    if (dict[@"buttonId"] || dict[@"menu"]) {+    if (dict[@"mailSearchToolbar"]) {+#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)+      if (@available(iOS 26.0, *)) {+        NSNumber *width = dict[@"width"];+        CGFloat resolvedWidth = width != nil ? width.doubleValue : MAX(320.0, [UIScreen mainScreen].bounds.size.width - 56.0);+        CGFloat resolvedHeight = 58.0;+        UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, resolvedWidth, resolvedHeight)];+        container.translatesAutoresizingMaskIntoConstraints = NO;+        [container.widthAnchor constraintEqualToConstant:resolvedWidth].active = YES;+        [container.heightAnchor constraintEqualToConstant:resolvedHeight].active = YES;++        UIStackView *stackView = [[UIStackView alloc] initWithFrame:container.bounds];+        stackView.axis = UILayoutConstraintAxisHorizontal;+        stackView.alignment = UIStackViewAlignmentCenter;+        stackView.distribution = UIStackViewDistributionFill;+        stackView.spacing = 10.0;+        stackView.translatesAutoresizingMaskIntoConstraints = NO;+        [container addSubview:stackView];+        [NSLayoutConstraint activateConstraints:@[+          [stackView.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],

Lines 1–500 of 1542.