@fluentui-react-native/callout 0.19.32 → 0.19.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +16 -1
- package/CHANGELOG.md +10 -2
- package/macos/Callout.swift +64 -14
- package/macos/CalloutWindow.swift +2 -42
- package/macos/FRNCalloutManager.m +2 -2
- package/macos/FlippedVisualEffectView.swift +8 -0
- package/macos/GuardedEventMonitor.swift +30 -0
- package/package.json +1 -1
package/CHANGELOG.json
CHANGED
|
@@ -2,7 +2,22 @@
|
|
|
2
2
|
"name": "@fluentui-react-native/callout",
|
|
3
3
|
"entries": [
|
|
4
4
|
{
|
|
5
|
-
"date": "Fri,
|
|
5
|
+
"date": "Fri, 18 Mar 2022 07:18:53 GMT",
|
|
6
|
+
"tag": "@fluentui-react-native/callout_v0.19.33",
|
|
7
|
+
"version": "0.19.33",
|
|
8
|
+
"comments": {
|
|
9
|
+
"patch": [
|
|
10
|
+
{
|
|
11
|
+
"author": "sanajmi@microsoft.com",
|
|
12
|
+
"package": "@fluentui-react-native/callout",
|
|
13
|
+
"commit": "be918e28dc086179801a25f9e664ceedbb81e85c",
|
|
14
|
+
"comment": "ContextualMenu macOS: Improve lifecycle management"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"date": "Fri, 11 Mar 2022 00:34:53 GMT",
|
|
6
21
|
"tag": "@fluentui-react-native/callout_v0.19.32",
|
|
7
22
|
"version": "0.19.32",
|
|
8
23
|
"comments": {
|
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# Change Log - @fluentui-react-native/callout
|
|
2
2
|
|
|
3
|
-
This log was last generated on Fri,
|
|
3
|
+
This log was last generated on Fri, 18 Mar 2022 07:18:53 GMT and should not be manually modified.
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
+
## 0.19.33
|
|
8
|
+
|
|
9
|
+
Fri, 18 Mar 2022 07:18:53 GMT
|
|
10
|
+
|
|
11
|
+
### Patches
|
|
12
|
+
|
|
13
|
+
- ContextualMenu macOS: Improve lifecycle management (sanajmi@microsoft.com)
|
|
14
|
+
|
|
7
15
|
## 0.19.32
|
|
8
16
|
|
|
9
|
-
Fri, 11 Mar 2022 00:34:
|
|
17
|
+
Fri, 11 Mar 2022 00:34:53 GMT
|
|
10
18
|
|
|
11
19
|
### Patches
|
|
12
20
|
|
package/macos/Callout.swift
CHANGED
|
@@ -26,9 +26,9 @@ class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
@objc public var onShow:
|
|
29
|
+
@objc public var onShow: RCTDirectEventBlock?
|
|
30
30
|
|
|
31
|
-
@objc public var onDismiss:
|
|
31
|
+
@objc public var onDismiss: RCTDirectEventBlock?
|
|
32
32
|
|
|
33
33
|
public weak var bridge: RCTBridge?
|
|
34
34
|
|
|
@@ -79,14 +79,68 @@ class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
|
|
|
79
79
|
// MARK: Private methods
|
|
80
80
|
|
|
81
81
|
private func showCallout() {
|
|
82
|
+
guard !isCalloutWindowShown else {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
82
86
|
updateCalloutFrameToAnchor()
|
|
83
87
|
calloutWindow.makeKeyAndOrderFront(self)
|
|
88
|
+
|
|
89
|
+
// Dismiss the Callout if the window is no longer active.
|
|
90
|
+
NotificationCenter.default.addObserver(self, selector: #selector(dismissCallout), name: NSApplication.didResignActiveNotification, object: nil)
|
|
91
|
+
|
|
92
|
+
mouseEventMonitor.addLocalMonitorForEvents(matching: .leftMouseUp, handler: { [weak self] (event) -> NSEvent? in
|
|
93
|
+
guard let clickedWindow = event.window else {
|
|
94
|
+
return event
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
var shouldDismissCallout = true
|
|
98
|
+
|
|
99
|
+
// Did the click happened in our own window?
|
|
100
|
+
if (clickedWindow.isEqual(to: self) ) {
|
|
101
|
+
shouldDismissCallout = false
|
|
102
|
+
|
|
103
|
+
// Are we a child window of a Callout (e.g: a ContextualMenu submenu), where the click happened in our parent?
|
|
104
|
+
} else if (clickedWindow.isEqual(to: self?.calloutWindow.parent as? CalloutWindow)) {
|
|
105
|
+
shouldDismissCallout = false
|
|
106
|
+
|
|
107
|
+
// Did the click happened in any of our child windows (e.g: our submenus)?
|
|
108
|
+
} else if (self?.calloutWindow.childWindows?.contains(clickedWindow) ?? false) {
|
|
109
|
+
shouldDismissCallout = false
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (shouldDismissCallout) {
|
|
113
|
+
self?.dismissCallout()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return event
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
isCalloutWindowShown = true
|
|
120
|
+
|
|
84
121
|
onShowCallout()
|
|
85
122
|
}
|
|
86
123
|
|
|
87
|
-
private func dismissCallout() {
|
|
88
|
-
|
|
89
|
-
|
|
124
|
+
@objc private func dismissCallout() {
|
|
125
|
+
guard isCalloutWindowShown else {
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Dismiss any children Callouts (I.E: Submenus) first
|
|
130
|
+
if let childWindows = calloutWindow.childWindows {
|
|
131
|
+
for childWindow in childWindows {
|
|
132
|
+
if let childCallout = childWindow as? CalloutWindow {
|
|
133
|
+
childCallout.dismissCallout()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
calloutWindow.dismissCallout()
|
|
139
|
+
|
|
140
|
+
NotificationCenter.default.removeObserver(self)
|
|
141
|
+
mouseEventMonitor.removeMonitor()
|
|
142
|
+
|
|
143
|
+
isCalloutWindowShown = false
|
|
90
144
|
}
|
|
91
145
|
|
|
92
146
|
/// Sets the frame of the Callout Window (in screen coordinates to be off of the Anchor on the preferred edge
|
|
@@ -307,18 +361,14 @@ class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
|
|
|
307
361
|
if let parentWindow = self.window {
|
|
308
362
|
parentWindow.addChildWindow(window, ordered: .above)
|
|
309
363
|
}
|
|
310
|
-
|
|
311
364
|
return window
|
|
312
365
|
}()
|
|
366
|
+
|
|
367
|
+
private var mouseEventMonitor = GuardedEventMonitor()
|
|
368
|
+
|
|
369
|
+
private var isCalloutWindowShown = false
|
|
313
370
|
}
|
|
314
371
|
|
|
315
|
-
|
|
316
|
-
/// ensure any views hosting React Native views are also flipped. This helps RCTTouchHandler
|
|
317
|
-
/// register clicks in the right location.
|
|
318
|
-
private class FlippedVisualEffectView: NSVisualEffectView {
|
|
319
|
-
override var isFlipped: Bool {
|
|
320
|
-
return true
|
|
321
|
-
}
|
|
322
|
-
}
|
|
372
|
+
|
|
323
373
|
|
|
324
374
|
private var calloutWindowCornerRadius: CGFloat = 5.0
|
|
@@ -7,48 +7,15 @@ protocol CalloutWindowLifeCycleDelegate: AnyObject {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
class CalloutWindow: NSWindow {
|
|
10
|
-
|
|
11
10
|
weak var lifeCycleDelegate: CalloutWindowLifeCycleDelegate?
|
|
12
11
|
|
|
13
12
|
override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) {
|
|
14
13
|
super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag)
|
|
15
|
-
isReleasedWhenClosed = false
|
|
16
14
|
|
|
17
15
|
styleMask = .borderless
|
|
18
16
|
level = .popUpMenu
|
|
19
17
|
backgroundColor = .clear
|
|
20
18
|
isMovable = false
|
|
21
|
-
|
|
22
|
-
// Dismiss the Callout if the window is no longer active.
|
|
23
|
-
NotificationCenter.default.addObserver(self, selector: #selector(dismissCallout), name: NSApplication.didResignActiveNotification, object: nil)
|
|
24
|
-
|
|
25
|
-
// Dismiss the Callout if the user clicked outside the Callout Window or any of our child windows.
|
|
26
|
-
mouseEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .leftMouseUp, handler: { [weak self] (event) -> NSEvent? in
|
|
27
|
-
guard let clickedWindow = event.window else {
|
|
28
|
-
return event
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
var shouldDismissCallout = true
|
|
32
|
-
|
|
33
|
-
// Did the click happened in our own window?
|
|
34
|
-
if (clickedWindow.isEqual(to: self) ) {
|
|
35
|
-
shouldDismissCallout = false
|
|
36
|
-
|
|
37
|
-
// Are we a child window of a Callout (e.g: a ContextualMenu submenu), where the click happened in our parent?
|
|
38
|
-
} else if (clickedWindow.isEqual(to: self?.parent as? CalloutWindow)) {
|
|
39
|
-
shouldDismissCallout = false
|
|
40
|
-
|
|
41
|
-
// Did the click happened in any of our child windows (e.g: our submenus)?
|
|
42
|
-
} else if (self?.childWindows?.contains(clickedWindow) ?? false) {
|
|
43
|
-
shouldDismissCallout = false
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (shouldDismissCallout) {
|
|
47
|
-
self?.dismissCallout()
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return event
|
|
51
|
-
})
|
|
52
19
|
}
|
|
53
20
|
|
|
54
21
|
// Required to get a key view loop in the window
|
|
@@ -65,16 +32,9 @@ class CalloutWindow: NSWindow {
|
|
|
65
32
|
dismissCallout()
|
|
66
33
|
}
|
|
67
34
|
|
|
68
|
-
@objc
|
|
35
|
+
@objc public func dismissCallout() {
|
|
69
36
|
lifeCycleDelegate?.calloutWillDismiss(window: self)
|
|
70
37
|
orderOut(self)
|
|
71
38
|
}
|
|
72
|
-
|
|
73
|
-
deinit {
|
|
74
|
-
if let monitor = mouseEventMonitor {
|
|
75
|
-
NSEvent.removeMonitor(monitor)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private var mouseEventMonitor: Any?
|
|
80
39
|
}
|
|
40
|
+
|
|
@@ -42,8 +42,8 @@ RCT_EXPORT_VIEW_PROPERTY(anchorRect, screenRect)
|
|
|
42
42
|
|
|
43
43
|
RCT_EXPORT_VIEW_PROPERTY(directionalHint, NSRectEdge)
|
|
44
44
|
|
|
45
|
-
RCT_EXPORT_VIEW_PROPERTY(onShow,
|
|
45
|
+
RCT_EXPORT_VIEW_PROPERTY(onShow, RCTDirectEventBlock)
|
|
46
46
|
|
|
47
|
-
RCT_EXPORT_VIEW_PROPERTY(onDismiss,
|
|
47
|
+
RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock)
|
|
48
48
|
|
|
49
49
|
@end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/// React Native macOS uses a flipped coordinate space by default. Let's stay consistent and
|
|
2
|
+
/// ensure any views hosting React Native views are also flipped. This helps RCTTouchHandler
|
|
3
|
+
/// register clicks in the right location.
|
|
4
|
+
internal class FlippedVisualEffectView: NSVisualEffectView {
|
|
5
|
+
override var isFlipped: Bool {
|
|
6
|
+
return true
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/// NSEvent localMonitors are an _old_ API that requires you to do some memory management yourself.
|
|
4
|
+
/// You _must_ call `removeMonitor`exactly once per monitor created from. This helper class enforces
|
|
5
|
+
/// that with the use of an extra boolean to track whether we have already allocated/released our monitor.
|
|
6
|
+
/// https://developer.apple.com/documentation/appkit/nsevent/1533709-removemonitor
|
|
7
|
+
internal class GuardedEventMonitor: Any {
|
|
8
|
+
|
|
9
|
+
private var isMonitorAllocated = false
|
|
10
|
+
private var allocatedMonitor: Any?
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
func addLocalMonitorForEvents(matching mask: NSEvent.EventTypeMask, handler block: @escaping (NSEvent) -> NSEvent?) {
|
|
14
|
+
if (!isMonitorAllocated) {
|
|
15
|
+
allocatedMonitor = NSEvent.addLocalMonitorForEvents(matching: mask, handler: block)
|
|
16
|
+
isMonitorAllocated = true
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func removeMonitor() {
|
|
21
|
+
if (isMonitorAllocated) {
|
|
22
|
+
NSEvent.removeMonitor(allocatedMonitor as Any)
|
|
23
|
+
isMonitorAllocated = false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
deinit {
|
|
28
|
+
removeMonitor()
|
|
29
|
+
}
|
|
30
|
+
}
|