mirror of
https://github.com/VibedByKaKi/t3-code-android-nightly.git
synced 2026-10-09 03:41:17 +02:00
Co-authored-by: maria-rcks <maria@kuuro.net> Co-authored-by: Bilal Bakr <62337003+Bil0000@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: Vitalii Yehorov <vitalyiegorov@gmail.com> Co-authored-by: Jake Leventhal <jakeleventhal@me.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Alex Southwell <saphid@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Nicholas Wasmiller <derped@mineperial.com> Co-authored-by: PB <poilmb@gmail.com> Co-authored-by: Exotic <118054752+extoci@users.noreply.github.com> Co-authored-by: Yash Singh <saiansh2525@gmail.com> Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Theo Browne <me@t3.gg> Co-authored-by: Gabriel De Andrade <30420087+gabrielelpidio@users.noreply.github.com> Co-authored-by: Dara Adedeji <76637177+SunkenInTime@users.noreply.github.com> Co-authored-by: scratchyone <scratchywon@gmail.com> Co-authored-by: Dominic Roy <dominic@sdko.org> Co-authored-by: chukfinley <chuk@chuk.dev> Co-authored-by: Primož Ajdišek <bigpod@bigpod.si> Co-authored-by: benthecarman <benthecarman@live.com> Co-authored-by: NaveDanan <nave0712@gmail.com> Co-authored-by: aaditagrawal <103925638+aaditagrawal@users.noreply.github.com> Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: Nick Anisimov <n.anisimov.23@gmail.com> Co-authored-by: MacKinley Smith <smithmackinley@gmail.com> Co-authored-by: Yordis Prieto <yordis.prieto@gmail.com> Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com> Co-authored-by: AKolenda <akole779@mtroyal.ca> Co-authored-by: Guillermo Casanova <75276669+Gigioxx@users.noreply.github.com> Co-authored-by: Otavio Salvador <otavio@ossystems.com.br> Co-authored-by: Shirish Pothi <183252392+shirishpothi@users.noreply.github.com> Co-authored-by: Ishaan Kothari <ishaanko.mail@gmail.com> Co-authored-by: Bob Fowler <bob@rjf.ca> Co-authored-by: Anton Bezdenezhnykh <gamer392@yandex.ru> Co-authored-by: ValeraZSD <48602572+ValeraZSD@users.noreply.github.com> Co-authored-by: Ephraim <ephraim39hr14m@gmail.com> Co-authored-by: Ryan Ilano <ryanilano@users.noreply.github.com> Co-authored-by: Alex <me@pixp.cc> Co-authored-by: maco <gosarmarcel7@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tristan Knight <admin@snappeh.com> Co-authored-by: PR Batch Tester <agent@local.test> Co-authored-by: oliver <97427849+flamboh@users.noreply.github.com> Co-authored-by: kamkm <99585688+Kamkmgamer@users.noreply.github.com> Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
523 lines
24 KiB
Diff
523 lines
24 KiB
Diff
diff --git a/ios/MenuViewManager.mm b/ios/MenuViewManager.mm
|
|
index 3d149b3e7426b8f7c999603ddf1e15e12331c33a..e80fe2caae86e339fcb03408419a7d64b3e5b04f 100644
|
|
--- a/ios/MenuViewManager.mm
|
|
+++ b/ios/MenuViewManager.mm
|
|
@@ -1,6 +1,6 @@
|
|
#import <React/RCTViewManager.h>
|
|
#import <React/RCTUIManager.h>
|
|
-#import "RCTBridge.h"
|
|
+#import <React/RCTBridge.h>
|
|
|
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
// NEW ARCH
|
|
@@ -65,6 +65,10 @@ - (UIView *)view
|
|
* onOpenMenu: callback to be called when the menu is opened
|
|
*/
|
|
RCT_EXPORT_VIEW_PROPERTY(onOpenMenu, RCTDirectEventBlock);
|
|
+/**
|
|
+ * onMenuInteractionStart: callback to be called when UIKit starts preparing a menu interaction
|
|
+ */
|
|
+RCT_EXPORT_VIEW_PROPERTY(onMenuInteractionStart, RCTDirectEventBlock);
|
|
/**
|
|
* shouldOpenOnLongPress: determines whether menu should be opened after long press or normal press
|
|
*/
|
|
diff --git a/ios/NewArch/FabricActionSheetView.swift b/ios/NewArch/FabricActionSheetView.swift
|
|
index 484ff8a1d73a6d445273883c5b28b1c8e80901ec..bc92310d00369b853a80f4d2080578f3903191d2 100644
|
|
--- a/ios/NewArch/FabricActionSheetView.swift
|
|
+++ b/ios/NewArch/FabricActionSheetView.swift
|
|
@@ -3,6 +3,7 @@ public class FabricActionSheetView: ActionSheetView, FabricViewImplementationPro
|
|
public var onPressAction: ((String) -> Void)?
|
|
public var onCloseMenu: (() -> Void)?
|
|
public var onOpenMenu: (() -> Void)?
|
|
+ public var onMenuInteractionStart: (() -> Void)?
|
|
|
|
@objc override func sendButtonAction(_ action: String) {
|
|
if let onPress = onPressAction {
|
|
diff --git a/ios/NewArch/FabricMenuViewImplementation.swift b/ios/NewArch/FabricMenuViewImplementation.swift
|
|
index 33d27c5a66593ae1a9b143804413578eb79f1a7d..359cd19fa53d10c0374b2b537517ea8629e79d0c 100644
|
|
--- a/ios/NewArch/FabricMenuViewImplementation.swift
|
|
+++ b/ios/NewArch/FabricMenuViewImplementation.swift
|
|
@@ -12,6 +12,7 @@ public class FabricMenuViewImplementation: MenuViewImplementation, FabricViewImp
|
|
public var onPressAction: ((String) -> Void)?
|
|
public var onCloseMenu: (() -> Void)?
|
|
public var onOpenMenu: (() -> Void)?
|
|
+ public var onMenuInteractionStart: (() -> Void)?
|
|
|
|
@objc override func sendButtonAction(_ action: UIAction) {
|
|
if let onPress = onPressAction {
|
|
@@ -31,4 +32,10 @@ public class FabricMenuViewImplementation: MenuViewImplementation, FabricViewImp
|
|
}
|
|
}
|
|
|
|
+ @objc override func sendMenuInteractionStart() {
|
|
+ if let onMenuInteractionStart = onMenuInteractionStart {
|
|
+ onMenuInteractionStart()
|
|
+ }
|
|
+ }
|
|
+
|
|
}
|
|
diff --git a/ios/NewArch/FabricViewImplementationProtocol.swift b/ios/NewArch/FabricViewImplementationProtocol.swift
|
|
index 2cef2fb8c74497da46775a490edc58cf8cbf29cc..54429c756eeecf6e96b55d62cc1e70213096f62a 100644
|
|
--- a/ios/NewArch/FabricViewImplementationProtocol.swift
|
|
+++ b/ios/NewArch/FabricViewImplementationProtocol.swift
|
|
@@ -8,4 +8,5 @@ import Foundation
|
|
var onPressAction: ((String) -> Void)? { get set }
|
|
var onCloseMenu: (() -> Void)? { get set }
|
|
var onOpenMenu: (() -> Void)? { get set }
|
|
+ var onMenuInteractionStart: (() -> Void)? { get set }
|
|
}
|
|
diff --git a/ios/NewArch/MenuView.mm b/ios/NewArch/MenuView.mm
|
|
index a54e619eb39e3402fa63f2ee4f734063d4f8fc58..773f6f1f5d4e0663fd3211fe51085a7f3935e445 100644
|
|
--- a/ios/NewArch/MenuView.mm
|
|
+++ b/ios/NewArch/MenuView.mm
|
|
@@ -46,6 +46,10 @@ - (instancetype)initWithFrame:(CGRect)frame
|
|
[self onOpenMenu];
|
|
};
|
|
|
|
+ _view.onMenuInteractionStart = ^{
|
|
+ [self onMenuInteractionStart];
|
|
+ };
|
|
+
|
|
_view.onCloseMenu = ^{
|
|
[self onCloseMenu];
|
|
};
|
|
@@ -92,6 +96,14 @@ - (void)onOpenMenu {
|
|
}
|
|
}
|
|
|
|
+- (void)onMenuInteractionStart {
|
|
+ // If screen is already unmounted then there will be no event emitter
|
|
+ const auto eventEmitter = [self getEventEmitter];
|
|
+ if (eventEmitter != nullptr) {
|
|
+ eventEmitter->onMenuInteractionStart({});
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
Responsible for iterating through the C++ vector<struct> and convert each struct element to NSDictionary, then return it all in an NSArray
|
|
*/
|
|
@@ -105,6 +117,27 @@ - (void)onOpenMenu {
|
|
NSMutableArray<NSDictionary *> *subactionsArray = [NSMutableArray arrayWithCapacity:actions.size()];
|
|
if (action.subactions.size() > 0) {
|
|
for (const MenuViewActionsSubactionsStruct &subaction : action.subactions) {
|
|
+ NSMutableArray<NSDictionary *> *subSubactionsArray =
|
|
+ [NSMutableArray arrayWithCapacity:subaction.subactions.size()];
|
|
+ for (const MenuViewActionsSubactionsSubactionsStruct &subSubaction : subaction.subactions) {
|
|
+ NSDictionary *subSubactionDict = @{
|
|
+ @"id": [NSString stringWithUTF8String:subSubaction.id.c_str()],
|
|
+ @"title": [NSString stringWithUTF8String:subSubaction.title.c_str()],
|
|
+ @"titleColor": @(subSubaction.titleColor),
|
|
+ @"subtitle": [NSString stringWithUTF8String:subSubaction.subtitle.c_str()],
|
|
+ @"state": [NSString stringWithUTF8String:subSubaction.state.c_str()],
|
|
+ @"image": [NSString stringWithUTF8String:subSubaction.image.c_str()],
|
|
+ @"imageColor": @(subSubaction.imageColor),
|
|
+ @"displayInline": @(subSubaction.displayInline),
|
|
+ @"attributes": @{
|
|
+ @"destructive": @(subSubaction.attributes.destructive),
|
|
+ @"disabled": @(subSubaction.attributes.disabled),
|
|
+ @"hidden": @(subSubaction.attributes.hidden),
|
|
+ @"keepsMenuPresented": @(subSubaction.attributes.keepsMenuPresented),
|
|
+ },
|
|
+ };
|
|
+ [subSubactionsArray addObject:subSubactionDict];
|
|
+ }
|
|
NSDictionary *subactionDict = @{
|
|
@"id": [NSString stringWithUTF8String:subaction.id.c_str()],
|
|
@"title": [NSString stringWithUTF8String:subaction.title.c_str()],
|
|
@@ -118,7 +151,9 @@ - (void)onOpenMenu {
|
|
@"destructive": @(subaction.attributes.destructive),
|
|
@"disabled": @(subaction.attributes.disabled),
|
|
@"hidden": @(subaction.attributes.hidden),
|
|
+ @"keepsMenuPresented": @(subaction.attributes.keepsMenuPresented),
|
|
},
|
|
+ @"subactions": subSubactionsArray,
|
|
};
|
|
[subactionsArray addObject:subactionDict];
|
|
}
|
|
@@ -138,6 +173,7 @@ - (void)onOpenMenu {
|
|
@"destructive": @(action.attributes.destructive),
|
|
@"disabled": @(action.attributes.disabled),
|
|
@"hidden": @(action.attributes.hidden),
|
|
+ @"keepsMenuPresented": @(action.attributes.keepsMenuPresented),
|
|
},
|
|
@"subactions": subactionsArray,
|
|
};
|
|
diff --git a/ios/OldArch/LegacyActionSheetView.swift b/ios/OldArch/LegacyActionSheetView.swift
|
|
index 3cd1a2517624d99b51331c564fba9145d7c8d799..e458504bea6ef167d9b0db0c889f0f76ea965287 100644
|
|
--- a/ios/OldArch/LegacyActionSheetView.swift
|
|
+++ b/ios/OldArch/LegacyActionSheetView.swift
|
|
@@ -3,6 +3,7 @@ public class LegacyActionSheetView: ActionSheetView {
|
|
@objc var onPressAction: RCTDirectEventBlock?
|
|
@objc var onCloseMenu: RCTDirectEventBlock?
|
|
@objc var onOpenMenu: RCTDirectEventBlock?
|
|
+ @objc var onMenuInteractionStart: RCTDirectEventBlock?
|
|
|
|
|
|
|
|
diff --git a/ios/OldArch/LegacyMenuViewImplementation.swift b/ios/OldArch/LegacyMenuViewImplementation.swift
|
|
index 6a2a205f95c1001f3fdf6fb835815fa7c4c02c16..4f42b64349966fb1438f72439cec8256a76aab9c 100644
|
|
--- a/ios/OldArch/LegacyMenuViewImplementation.swift
|
|
+++ b/ios/OldArch/LegacyMenuViewImplementation.swift
|
|
@@ -5,6 +5,7 @@ public class LegacyMenuViewImplementation: MenuViewImplementation {
|
|
@objc var onPressAction: RCTDirectEventBlock?
|
|
@objc var onCloseMenu: RCTDirectEventBlock?
|
|
@objc var onOpenMenu: RCTDirectEventBlock?
|
|
+ @objc var onMenuInteractionStart: RCTDirectEventBlock?
|
|
|
|
@objc override func sendButtonAction(_ action: UIAction) {
|
|
if let onPress = onPressAction {
|
|
@@ -24,4 +25,10 @@ public class LegacyMenuViewImplementation: MenuViewImplementation {
|
|
}
|
|
}
|
|
|
|
+ @objc override func sendMenuInteractionStart() {
|
|
+ if let onMenuInteractionStart = onMenuInteractionStart {
|
|
+ onMenuInteractionStart([:])
|
|
+ }
|
|
+ }
|
|
+
|
|
}
|
|
diff --git a/ios/Shared/MenuViewImplementation.swift b/ios/Shared/MenuViewImplementation.swift
|
|
index 5c4e0da4292b15d3a27b5ea1555f11452a470815..475a99b2a3b1800c9d3e3e5f77a64f552fa35828 100644
|
|
--- a/ios/Shared/MenuViewImplementation.swift
|
|
+++ b/ios/Shared/MenuViewImplementation.swift
|
|
@@ -60,17 +60,46 @@ public class MenuViewImplementation: UIButton {
|
|
}
|
|
|
|
public override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
|
- sendMenuOpen()
|
|
+ // Flush updates deferred by the presented-guard before the action
|
|
+ // provider snapshots self.menu (covers a stuck flag from an
|
|
+ // interaction that never ended cleanly).
|
|
+ if pendingMenu != nil {
|
|
+ pendingMenu = nil
|
|
+ isMenuPresented = false
|
|
+ self.setup()
|
|
+ }
|
|
+ isMenuPresented = true
|
|
+ sendMenuInteractionStart()
|
|
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { [weak self] _ in
|
|
guard let self = self else { return nil }
|
|
return self.menu
|
|
}
|
|
}
|
|
-
|
|
+
|
|
+ public override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willDisplayMenuFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
|
|
+ // UIKit can request a configuration for a press that never opens a
|
|
+ // menu. Notify React only once presentation actually begins, and
|
|
+ // preserve UIButton's presentation behavior by calling super.
|
|
+ super.contextMenuInteraction(interaction, willDisplayMenuFor: configuration, animator: animator)
|
|
+ sendMenuOpen()
|
|
+ }
|
|
+
|
|
public override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
|
|
+ super.contextMenuInteraction(interaction, willEndFor: configuration, animator: animator)
|
|
sendMenuClose()
|
|
+ isMenuPresented = false
|
|
+ if pendingMenu != nil {
|
|
+ // Re-run the full assignment now that presentation is over, so
|
|
+ // any props deferred by the presented-guard (menu contents, press
|
|
+ // mode) land on the button.
|
|
+ pendingMenu = nil
|
|
+ self.setup()
|
|
+ }
|
|
}
|
|
|
|
+ private var isMenuPresented = false
|
|
+ private var pendingMenu: UIMenu?
|
|
+
|
|
func setup () {
|
|
let menu = UIMenu(title: _title,
|
|
identifier: nil,
|
|
@@ -86,8 +115,102 @@ public class MenuViewImplementation: UIButton {
|
|
}
|
|
}
|
|
|
|
+ if isMenuPresented {
|
|
+ // An action fired with keepsMenuPresented leaves the menu on
|
|
+ // screen, and reassigning self.menu while it is presented makes
|
|
+ // UIKit dismiss it — the update triggered by a selection would
|
|
+ // defeat the keep-presented attribute. Update the visible copy in
|
|
+ // place instead and defer the button assignment to dismissal.
|
|
+ pendingMenu = menu
|
|
+ self.refreshPresentedMenu(menu)
|
|
+ return
|
|
+ }
|
|
+
|
|
self.menu = menu
|
|
self.showsMenuAsPrimaryAction = !shouldOpenOnLongPress
|
|
+ // In long-press mode the button must not intercept touches: as a
|
|
+ // full-bounds contentView it sits IN FRONT of the React children of
|
|
+ // the hosting component view, which would swallow their taps (e.g. a
|
|
+ // row Pressable wrapped by MenuView). The context-menu interaction is
|
|
+ // hosted by the superview instead (see updateLongPressInteraction) so
|
|
+ // long-press and pointer secondary-click still present the menu, with
|
|
+ // the wrapped content as the zoom preview.
|
|
+ self.isUserInteractionEnabled = !shouldOpenOnLongPress
|
|
+ self.updateLongPressInteraction()
|
|
+ }
|
|
+
|
|
+ private func refreshPresentedMenu(_ menu: UIMenu) {
|
|
+ // Tap mode presents through the button's built-in interaction,
|
|
+ // long-press mode through the superview-hosted one; cover both (plus
|
|
+ // the interaction added in init) and dedupe by identity.
|
|
+ var candidates = self.interactions.compactMap { $0 as? UIContextMenuInteraction }
|
|
+ if let builtin = self.contextMenuInteraction {
|
|
+ candidates.append(builtin)
|
|
+ }
|
|
+ if let host = longPressInteraction {
|
|
+ candidates.append(host)
|
|
+ }
|
|
+ var visited: Set<ObjectIdentifier> = []
|
|
+ for interaction in candidates where visited.insert(ObjectIdentifier(interaction)).inserted {
|
|
+ // The block receives whichever menu level is currently on screen —
|
|
+ // the navigated submenu when the user picked inside one, not the
|
|
+ // root. Swap in the matching node from the rebuilt tree (stable
|
|
+ // identifiers from the JS action ids) so that level updates in
|
|
+ // place; returning an unrelated menu instead makes UIKit render it
|
|
+ // as navigation into a foreign menu, with a stale or blank
|
|
+ // expanded-submenu header row above the actions. The root carries
|
|
+ // an auto-generated identifier that never matches, so it falls
|
|
+ // through to a children-only replacement.
|
|
+ interaction.updateVisibleMenu { [weak self] visibleMenu in
|
|
+ guard let self = self else { return visibleMenu }
|
|
+ if let replacement = self.menuMatching(visibleMenu.identifier, in: menu) {
|
|
+ return replacement
|
|
+ }
|
|
+ return visibleMenu.replacingChildren(menu.children)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private func menuMatching(_ identifier: UIMenu.Identifier, in menu: UIMenu) -> UIMenu? {
|
|
+ if menu.identifier == identifier {
|
|
+ return menu
|
|
+ }
|
|
+ for element in menu.children {
|
|
+ if let submenu = element as? UIMenu, let match = menuMatching(identifier, in: submenu) {
|
|
+ return match
|
|
+ }
|
|
+ }
|
|
+ return nil
|
|
+ }
|
|
+
|
|
+ private var longPressInteraction: UIContextMenuInteraction?
|
|
+
|
|
+ public override func didMoveToSuperview() {
|
|
+ super.didMoveToSuperview()
|
|
+ self.updateLongPressInteraction()
|
|
+ }
|
|
+
|
|
+ private func updateLongPressInteraction() {
|
|
+ if shouldOpenOnLongPress, let host = self.superview {
|
|
+ if let existing = longPressInteraction, existing.view === host {
|
|
+ return
|
|
+ }
|
|
+ if let existing = longPressInteraction {
|
|
+ existing.view?.removeInteraction(existing)
|
|
+ }
|
|
+ // UIControl's built-in interaction presents a compact menu that
|
|
+ // preserves the keyboard; a separate view interaction presents a
|
|
+ // rich context menu and temporarily hides it.
|
|
+ guard let interaction = self.contextMenuInteraction else { return }
|
|
+ host.addInteraction(interaction)
|
|
+ longPressInteraction = interaction
|
|
+ return
|
|
+ }
|
|
+ if let existing = longPressInteraction {
|
|
+ existing.view?.removeInteraction(existing)
|
|
+ self.addInteraction(existing)
|
|
+ longPressInteraction = nil
|
|
+ }
|
|
}
|
|
|
|
public override func reactSetFrame(_ frame: CGRect) {
|
|
@@ -124,4 +247,8 @@ public class MenuViewImplementation: UIButton {
|
|
@objc func sendMenuOpen() {
|
|
// NO-OP (should be overriden by parent)
|
|
}
|
|
+
|
|
+ @objc func sendMenuInteractionStart() {
|
|
+ // NO-OP (should be overriden by parent)
|
|
+ }
|
|
}
|
|
diff --git a/ios/Shared/RCTMenuItem.swift b/ios/Shared/RCTMenuItem.swift
|
|
index bb6bb2b7ad56135089f267587c974b166760539d..949b3f7ec49af7ba26d966a8923a51f619443d5a 100644
|
|
--- a/ios/Shared/RCTMenuItem.swift
|
|
+++ b/ios/Shared/RCTMenuItem.swift
|
|
@@ -103,10 +103,18 @@ class RCTMenuAction {
|
|
subMenuActions.append(subaction.createUIMenuElement(handler))
|
|
}
|
|
var menu: UIMenu;
|
|
+ // Stable identifiers let updateVisibleMenu match submenu nodes in
|
|
+ // place, so a refresh while the menu is presented keeps the user's
|
|
+ // current submenu level instead of popping back to the root.
|
|
+ // Inline sections don't navigate, so they stay stock.
|
|
+ let menuIdentifier = identifier.map { UIMenu.Identifier(rawValue: $0.rawValue) }
|
|
if self.displayInline {
|
|
menu = UIMenu(title: title, image: image, options: .displayInline, children: subMenuActions)
|
|
} else {
|
|
- menu = UIMenu(title: title, image: image, children: subMenuActions)
|
|
+ menu = UIMenu(title: title, image: image, identifier: menuIdentifier, children: subMenuActions)
|
|
+ }
|
|
+ if #available(iOS 15.0, *) {
|
|
+ menu.subtitle = subtitle
|
|
}
|
|
|
|
if #available(iOS 16.0, *) {
|
|
diff --git a/lib/commonjs/NativeModuleSpecs/UIMenuNativeComponent.ts b/lib/commonjs/NativeModuleSpecs/UIMenuNativeComponent.ts
|
|
index e6509355275596c451f9d082223cc16bfe504e1a..dd16e1a7403970cc3a404355f95ed99580776353 100644
|
|
--- a/lib/commonjs/NativeModuleSpecs/UIMenuNativeComponent.ts
|
|
+++ b/lib/commonjs/NativeModuleSpecs/UIMenuNativeComponent.ts
|
|
@@ -48,6 +48,7 @@ export interface NativeProps extends ViewProps {
|
|
onPressAction?: DirectEventHandler<{ event: string }>;
|
|
onCloseMenu?: DirectEventHandler<{ event: string }>;
|
|
onOpenMenu?: DirectEventHandler<{ event: string }>;
|
|
+ onMenuInteractionStart?: DirectEventHandler<{ event: string }>;
|
|
actions: Array<MenuAction>;
|
|
actionsHash: string; // just a workaround to make sure we don't have to manually compare MenuActions manually in C++ (since it's a struct and that's a pain)
|
|
title?: string;
|
|
diff --git a/lib/module/NativeModuleSpecs/UIMenuNativeComponent.ts b/lib/module/NativeModuleSpecs/UIMenuNativeComponent.ts
|
|
index e6509355275596c451f9d082223cc16bfe504e1a..dd16e1a7403970cc3a404355f95ed99580776353 100644
|
|
--- a/lib/module/NativeModuleSpecs/UIMenuNativeComponent.ts
|
|
+++ b/lib/module/NativeModuleSpecs/UIMenuNativeComponent.ts
|
|
@@ -48,6 +48,7 @@ export interface NativeProps extends ViewProps {
|
|
onPressAction?: DirectEventHandler<{ event: string }>;
|
|
onCloseMenu?: DirectEventHandler<{ event: string }>;
|
|
onOpenMenu?: DirectEventHandler<{ event: string }>;
|
|
+ onMenuInteractionStart?: DirectEventHandler<{ event: string }>;
|
|
actions: Array<MenuAction>;
|
|
actionsHash: string; // just a workaround to make sure we don't have to manually compare MenuActions manually in C++ (since it's a struct and that's a pain)
|
|
title?: string;
|
|
diff --git a/lib/typescript/src/NativeModuleSpecs/UIMenuNativeComponent.d.ts b/lib/typescript/src/NativeModuleSpecs/UIMenuNativeComponent.d.ts
|
|
index 24ee7fdcd9f81cfe2c813cfca9d4e276b846fb7b..c1b4ea53b29193283295d35c18e068f2836c6176 100644
|
|
--- a/lib/typescript/src/NativeModuleSpecs/UIMenuNativeComponent.d.ts
|
|
+++ b/lib/typescript/src/NativeModuleSpecs/UIMenuNativeComponent.d.ts
|
|
@@ -41,6 +41,9 @@ export interface NativeProps extends ViewProps {
|
|
onOpenMenu?: DirectEventHandler<{
|
|
event: string;
|
|
}>;
|
|
+ onMenuInteractionStart?: DirectEventHandler<{
|
|
+ event: string;
|
|
+ }>;
|
|
actions: Array<MenuAction>;
|
|
actionsHash: string;
|
|
title?: string;
|
|
diff --git a/lib/typescript/src/index.d.ts b/lib/typescript/src/index.d.ts
|
|
index 61d210aa9364f0354f77bddaecdc0a127b1cdbb4..0672bea3352b9ad0623124e308f740c70a7e6940 100644
|
|
--- a/lib/typescript/src/index.d.ts
|
|
+++ b/lib/typescript/src/index.d.ts
|
|
@@ -4,6 +4,7 @@ declare const MenuView: import("react").ForwardRefExoticComponent<{
|
|
onPressAction?: ({ nativeEvent }: NativeActionEvent) => void;
|
|
onCloseMenu?: () => void;
|
|
onOpenMenu?: () => void;
|
|
+ onMenuInteractionStart?: () => void;
|
|
actions: MenuAction[];
|
|
title?: string;
|
|
isAnchoredToRight?: boolean;
|
|
diff --git a/lib/typescript/src/types.d.ts b/lib/typescript/src/types.d.ts
|
|
index eac7940ab7eb6a69ac329848c9f3eeffdcbaf43d..19e82df15b042bc0cda27349b2b8a8ee1d3f0b51 100644
|
|
--- a/lib/typescript/src/types.d.ts
|
|
+++ b/lib/typescript/src/types.d.ts
|
|
@@ -113,6 +113,12 @@ type MenuComponentPropsBase = {
|
|
* Callback function that will be called when the menu opens.
|
|
*/
|
|
onOpenMenu?: () => void;
|
|
+ /**
|
|
+ * Callback function that will be called when UIKit starts preparing a menu interaction.
|
|
+ * Preparation can end without the menu opening.
|
|
+ * @platform iOS
|
|
+ */
|
|
+ onMenuInteractionStart?: () => void;
|
|
/**
|
|
* Actions to be displayed in the menu.
|
|
*/
|
|
@@ -170,6 +176,7 @@ export type NativeMenuComponentProps = {
|
|
onPressAction?: ({ nativeEvent }: NativeActionEvent) => void;
|
|
onCloseMenu?: () => void;
|
|
onOpenMenu?: () => void;
|
|
+ onMenuInteractionStart?: () => void;
|
|
actions: ProcessedMenuAction[];
|
|
actionsHash: string;
|
|
title?: string;
|
|
diff --git a/src/NativeModuleSpecs/UIMenuNativeComponent.ts b/src/NativeModuleSpecs/UIMenuNativeComponent.ts
|
|
index e6509355275596c451f9d082223cc16bfe504e1a..604dba0d90a2545910f8724a80205565de2391d4 100644
|
|
--- a/src/NativeModuleSpecs/UIMenuNativeComponent.ts
|
|
+++ b/src/NativeModuleSpecs/UIMenuNativeComponent.ts
|
|
@@ -13,6 +13,22 @@ import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNati
|
|
types here, to avoid issues while `pod install` takes place.
|
|
*/
|
|
|
|
+type SubSubAction = {
|
|
+ id?: string;
|
|
+ title: string;
|
|
+ titleColor?: Int32;
|
|
+ subtitle?: string;
|
|
+ state?: string;
|
|
+ image?: string;
|
|
+ imageColor?: Int32;
|
|
+ displayInline?: boolean;
|
|
+ attributes?: {
|
|
+ destructive?: boolean;
|
|
+ disabled?: boolean;
|
|
+ hidden?: boolean;
|
|
+ keepsMenuPresented?: boolean;
|
|
+ };
|
|
+};
|
|
type SubAction = {
|
|
id?: string;
|
|
title: string;
|
|
@@ -26,7 +42,11 @@ type SubAction = {
|
|
destructive?: boolean;
|
|
disabled?: boolean;
|
|
hidden?: boolean;
|
|
+ keepsMenuPresented?: boolean;
|
|
};
|
|
+ // One extra nesting level (menu → submenu → nested submenu leaves); the
|
|
+ // codegen structs can't recurse, so depth is capped explicitly.
|
|
+ subactions?: Array<SubSubAction>;
|
|
};
|
|
type MenuAction = {
|
|
id?: string;
|
|
@@ -41,6 +61,7 @@ type MenuAction = {
|
|
destructive?: boolean;
|
|
disabled?: boolean;
|
|
hidden?: boolean;
|
|
+ keepsMenuPresented?: boolean;
|
|
};
|
|
subactions?: Array<SubAction>;
|
|
};
|
|
@@ -48,6 +69,7 @@ export interface NativeProps extends ViewProps {
|
|
onPressAction?: DirectEventHandler<{ event: string }>;
|
|
onCloseMenu?: DirectEventHandler<{ event: string }>;
|
|
onOpenMenu?: DirectEventHandler<{ event: string }>;
|
|
+ onMenuInteractionStart?: DirectEventHandler<{ event: string }>;
|
|
actions: Array<MenuAction>;
|
|
actionsHash: string; // just a workaround to make sure we don't have to manually compare MenuActions manually in C++ (since it's a struct and that's a pain)
|
|
title?: string;
|
|
diff --git a/src/types.ts b/src/types.ts
|
|
index 9c323d9c510e7359c83d4d6633affde673570632..5d5fdfb8b8235fcaa8295b85630443eaf5991a17 100644
|
|
--- a/src/types.ts
|
|
+++ b/src/types.ts
|
|
@@ -123,6 +123,12 @@ type MenuComponentPropsBase = {
|
|
* Callback function that will be called when the menu opens.
|
|
*/
|
|
onOpenMenu?: () => void;
|
|
+ /**
|
|
+ * Callback function that will be called when UIKit starts preparing a menu interaction.
|
|
+ * Preparation can end without the menu opening.
|
|
+ * @platform iOS
|
|
+ */
|
|
+ onMenuInteractionStart?: () => void;
|
|
/**
|
|
* Actions to be displayed in the menu.
|
|
*/
|
|
@@ -189,6 +195,7 @@ export type NativeMenuComponentProps = {
|
|
onPressAction?: ({ nativeEvent }: NativeActionEvent) => void;
|
|
onCloseMenu?: () => void;
|
|
onOpenMenu?: () => void;
|
|
+ onMenuInteractionStart?: () => void;
|
|
actions: ProcessedMenuAction[];
|
|
actionsHash: string;
|
|
title?: string;
|