* revert: back to original * feat: close menu support * feat: close menu support * feat: close menu support * feat: close menu support * feat: close menu support * chore: removed package lock file * feat(events): add menu close detection Add onMenuClose event that fires when menu is dismissed. - Implement for both iOS and Android platforms - Add event at start of dismissal for better responsiveness - Support both old and new React Native architectures - Add tests and update documentation The event fires when: - User taps outside the menu - User selects a menu item * feat(events): add onOpenMenu event and rename onMenuClose * refactor(menu): simplify menu event handlers by removing native event parameters * fix(types): update onCloseMenu and onOpenMenu event handlers to use undefined event parameter * fix(types): update onCloseMenu and onOpenMenu event handlers to accept string event parameters * refactor(menu): rename onMenuOpen to onOpenMenu across iOS implementations * feat(menu): implement onOpenMenu event and update event handling in iOS and Android * refactor(menu): streamline event handling for onCloseMenu and onOpenMenu --------- Co-authored-by: mohammed <malkailany@hotmail.com>
128 lines
3.7 KiB
Swift
128 lines
3.7 KiB
Swift
//
|
|
// MenuViewImplementation.swift
|
|
// react-native-menu
|
|
//
|
|
// Created by Jesse Katsumata on 11/3/20.
|
|
//
|
|
|
|
import UIKit
|
|
@available(iOS 14.0, *)
|
|
@objc(MenuViewImplementation)
|
|
public class MenuViewImplementation: UIButton {
|
|
|
|
@objc public var actions: [NSDictionary]? {
|
|
didSet {
|
|
guard let actions = self.actions else {
|
|
return
|
|
}
|
|
_actions.removeAll()
|
|
actions.forEach { menuAction in
|
|
_actions.append(RCTMenuAction(details: menuAction).createUIMenuElement({action in self.sendButtonAction(action)}))
|
|
}
|
|
self.setup()
|
|
}
|
|
}
|
|
|
|
private var _actions: [UIMenuElement] = [];
|
|
|
|
private var _title: String = "";
|
|
@objc public var title: NSString? {
|
|
didSet {
|
|
guard let title = self.title else {
|
|
return
|
|
}
|
|
self._title = title as String
|
|
self.setup()
|
|
}
|
|
}
|
|
|
|
@objc public var shouldOpenOnLongPress: Bool = false {
|
|
didSet {
|
|
self.setup()
|
|
}
|
|
}
|
|
|
|
private var _themeVariant: String?
|
|
@objc public var themeVariant: NSString? {
|
|
didSet {
|
|
self._themeVariant = themeVariant as? String
|
|
self.setup()
|
|
}
|
|
}
|
|
|
|
@objc public var hitSlop: UIEdgeInsets = .zero
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
let interaction = UIContextMenuInteraction(delegate: self)
|
|
self.addInteraction(interaction)
|
|
self.setup()
|
|
}
|
|
|
|
public override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
|
sendMenuOpen()
|
|
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, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
|
|
sendMenuClose()
|
|
}
|
|
|
|
func setup () {
|
|
let menu = UIMenu(title: _title,
|
|
identifier: nil,
|
|
children: self._actions)
|
|
|
|
if self._themeVariant != nil {
|
|
if self._themeVariant == "dark" {
|
|
self.overrideUserInterfaceStyle = .dark
|
|
} else if self._themeVariant == "light" {
|
|
self.overrideUserInterfaceStyle = .light
|
|
} else {
|
|
self.overrideUserInterfaceStyle = .unspecified
|
|
}
|
|
}
|
|
|
|
self.menu = menu
|
|
self.showsMenuAsPrimaryAction = !shouldOpenOnLongPress
|
|
}
|
|
|
|
public override func reactSetFrame(_ frame: CGRect) {
|
|
super.reactSetFrame(frame);
|
|
}
|
|
|
|
public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
|
if hitSlop == .zero || !self.isEnabled || self.isHidden {
|
|
return super.point(inside: point, with: event)
|
|
}
|
|
|
|
let largerFrame = CGRect(
|
|
x: self.bounds.origin.x - hitSlop.left,
|
|
y: self.bounds.origin.y - hitSlop.top,
|
|
width: self.bounds.size.width + hitSlop.left + hitSlop.right,
|
|
height: self.bounds.size.height + hitSlop.top + hitSlop.bottom
|
|
)
|
|
|
|
return largerFrame.contains(point)
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
@objc func sendButtonAction(_ action: UIAction) {
|
|
// NO-OP (should be overriden by parent)
|
|
}
|
|
|
|
@objc func sendMenuClose() {
|
|
// NO-OP (should be overriden by parent)
|
|
}
|
|
|
|
@objc func sendMenuOpen() {
|
|
// NO-OP (should be overriden by parent)
|
|
}
|
|
}
|