@fluentui-react-native/callout 0.19.31 → 0.19.34
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 +46 -1
- package/CHANGELOG.md +26 -2
- package/macos/Callout.swift +73 -15
- package/macos/CalloutWindow.swift +2 -41
- package/macos/FRNCalloutManager.m +2 -2
- package/macos/FlippedVisualEffectView.swift +8 -0
- package/macos/GuardedEventMonitor.swift +28 -0
- package/package.json +1 -1
package/CHANGELOG.json
CHANGED
|
@@ -2,7 +2,52 @@
|
|
|
2
2
|
"name": "@fluentui-react-native/callout",
|
|
3
3
|
"entries": [
|
|
4
4
|
{
|
|
5
|
-
"date": "Mon,
|
|
5
|
+
"date": "Mon, 21 Mar 2022 01:47:26 GMT",
|
|
6
|
+
"tag": "@fluentui-react-native/callout_v0.19.34",
|
|
7
|
+
"version": "0.19.34",
|
|
8
|
+
"comments": {
|
|
9
|
+
"patch": [
|
|
10
|
+
{
|
|
11
|
+
"author": "sanajmi@microsoft.com",
|
|
12
|
+
"package": "@fluentui-react-native/callout",
|
|
13
|
+
"commit": "b772dfe4542b7032f216bf5c2faa807c695fa2a7",
|
|
14
|
+
"comment": "width -> maxWidth"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"date": "Fri, 18 Mar 2022 07:19:17 GMT",
|
|
21
|
+
"tag": "@fluentui-react-native/callout_v0.19.33",
|
|
22
|
+
"version": "0.19.33",
|
|
23
|
+
"comments": {
|
|
24
|
+
"patch": [
|
|
25
|
+
{
|
|
26
|
+
"author": "sanajmi@microsoft.com",
|
|
27
|
+
"package": "@fluentui-react-native/callout",
|
|
28
|
+
"commit": "be918e28dc086179801a25f9e664ceedbb81e85c",
|
|
29
|
+
"comment": "ContextualMenu macOS: Improve lifecycle management"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"date": "Fri, 11 Mar 2022 00:34:53 GMT",
|
|
36
|
+
"tag": "@fluentui-react-native/callout_v0.19.32",
|
|
37
|
+
"version": "0.19.32",
|
|
38
|
+
"comments": {
|
|
39
|
+
"patch": [
|
|
40
|
+
{
|
|
41
|
+
"author": "sanajmi@microsoft.com",
|
|
42
|
+
"package": "@fluentui-react-native/callout",
|
|
43
|
+
"commit": "489339a0f6ebd9a4687c1c84e9bf8d274d8806d5",
|
|
44
|
+
"comment": "ContextualMenu macOS: Focus on menu rather than first menu item"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"date": "Mon, 07 Mar 2022 19:15:33 GMT",
|
|
6
51
|
"tag": "@fluentui-react-native/callout_v0.19.31",
|
|
7
52
|
"version": "0.19.31",
|
|
8
53
|
"comments": {
|
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
# Change Log - @fluentui-react-native/callout
|
|
2
2
|
|
|
3
|
-
This log was last generated on Mon,
|
|
3
|
+
This log was last generated on Mon, 21 Mar 2022 01:47:26 GMT and should not be manually modified.
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
+
## 0.19.34
|
|
8
|
+
|
|
9
|
+
Mon, 21 Mar 2022 01:47:26 GMT
|
|
10
|
+
|
|
11
|
+
### Patches
|
|
12
|
+
|
|
13
|
+
- width -> maxWidth (sanajmi@microsoft.com)
|
|
14
|
+
|
|
15
|
+
## 0.19.33
|
|
16
|
+
|
|
17
|
+
Fri, 18 Mar 2022 07:19:17 GMT
|
|
18
|
+
|
|
19
|
+
### Patches
|
|
20
|
+
|
|
21
|
+
- ContextualMenu macOS: Improve lifecycle management (sanajmi@microsoft.com)
|
|
22
|
+
|
|
23
|
+
## 0.19.32
|
|
24
|
+
|
|
25
|
+
Fri, 11 Mar 2022 00:34:53 GMT
|
|
26
|
+
|
|
27
|
+
### Patches
|
|
28
|
+
|
|
29
|
+
- ContextualMenu macOS: Focus on menu rather than first menu item (sanajmi@microsoft.com)
|
|
30
|
+
|
|
7
31
|
## 0.19.31
|
|
8
32
|
|
|
9
|
-
Mon, 07 Mar 2022 19:
|
|
33
|
+
Mon, 07 Mar 2022 19:15:33 GMT
|
|
10
34
|
|
|
11
35
|
### Patches
|
|
12
36
|
|
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,76 @@ 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: .leftMouseDown, handler: { [weak self] (event) -> NSEvent? in
|
|
93
|
+
func isClickInsideWindowHierarchy(window: NSWindow?, event: NSEvent) -> Bool {
|
|
94
|
+
guard let window = window else {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
guard let clickedWindow = event.window else {
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
var isClickInHierarchy = false
|
|
102
|
+
|
|
103
|
+
if (window.isEqual(to: clickedWindow)) {
|
|
104
|
+
isClickInHierarchy = true
|
|
105
|
+
} else {
|
|
106
|
+
if let childWindows = window.childWindows {
|
|
107
|
+
for childWindow in childWindows {
|
|
108
|
+
isClickInHierarchy = isClickInsideWindowHierarchy(window: childWindow, event: event)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return isClickInHierarchy
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
var window: NSWindow? = self?.calloutWindow
|
|
117
|
+
while (window?.parent as? CalloutWindow != nil) {
|
|
118
|
+
window = window?.parent
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!isClickInsideWindowHierarchy(window: window, event: event)) {
|
|
122
|
+
self?.dismissCallout()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return event
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
isCalloutWindowShown = true
|
|
84
129
|
onShowCallout()
|
|
85
130
|
}
|
|
86
131
|
|
|
87
|
-
private func dismissCallout() {
|
|
88
|
-
|
|
89
|
-
|
|
132
|
+
@objc private func dismissCallout() {
|
|
133
|
+
guard isCalloutWindowShown else {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Dismiss any children Callouts (I.E: Submenus) first
|
|
138
|
+
if let childWindows = calloutWindow.childWindows {
|
|
139
|
+
for childWindow in childWindows {
|
|
140
|
+
if let childCallout = childWindow as? CalloutWindow {
|
|
141
|
+
childCallout.dismissCallout()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
calloutWindow.dismissCallout()
|
|
147
|
+
|
|
148
|
+
NotificationCenter.default.removeObserver(self)
|
|
149
|
+
mouseEventMonitor.removeMonitor()
|
|
150
|
+
|
|
151
|
+
isCalloutWindowShown = false
|
|
90
152
|
}
|
|
91
153
|
|
|
92
154
|
/// Sets the frame of the Callout Window (in screen coordinates to be off of the Anchor on the preferred edge
|
|
@@ -274,7 +336,7 @@ class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
|
|
|
274
336
|
private var anchorView: NSView?
|
|
275
337
|
|
|
276
338
|
/// The view we forward Callout's Children to. It's hosted within the CalloutWindow's
|
|
277
|
-
/// view
|
|
339
|
+
/// view hierarchy, ensuring our React Views are not placed in the main window.
|
|
278
340
|
private lazy var proxyView: NSView = {
|
|
279
341
|
let visualEffectView = FlippedVisualEffectView()
|
|
280
342
|
visualEffectView.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -307,18 +369,14 @@ class CalloutView: RCTView, CalloutWindowLifeCycleDelegate {
|
|
|
307
369
|
if let parentWindow = self.window {
|
|
308
370
|
parentWindow.addChildWindow(window, ordered: .above)
|
|
309
371
|
}
|
|
310
|
-
|
|
311
372
|
return window
|
|
312
373
|
}()
|
|
313
|
-
}
|
|
314
374
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
private class FlippedVisualEffectView: NSVisualEffectView {
|
|
319
|
-
override var isFlipped: Bool {
|
|
320
|
-
return true
|
|
321
|
-
}
|
|
375
|
+
private var mouseEventMonitor = GuardedEventMonitor()
|
|
376
|
+
|
|
377
|
+
private var isCalloutWindowShown = false
|
|
322
378
|
}
|
|
323
379
|
|
|
380
|
+
|
|
381
|
+
|
|
324
382
|
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,15 +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
|
-
|
|
71
|
-
if let monitor = mouseEventMonitor {
|
|
72
|
-
NSEvent.removeMonitor(monitor)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
37
|
orderOut(self)
|
|
76
38
|
}
|
|
77
|
-
|
|
78
|
-
private var mouseEventMonitor: Any?
|
|
79
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,28 @@
|
|
|
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
|
+
/// we set the reference to nil after `removeMonitor` so that subsequent calls don't cause an over release.
|
|
6
|
+
/// One instance should only be used to allocate and manage one NSEvent monitor.
|
|
7
|
+
/// https://developer.apple.com/documentation/appkit/nsevent/1533709-removemonitor
|
|
8
|
+
internal class GuardedEventMonitor: Any {
|
|
9
|
+
|
|
10
|
+
private var allocatedMonitor: Any?
|
|
11
|
+
|
|
12
|
+
func addLocalMonitorForEvents(matching mask: NSEvent.EventTypeMask, handler block: @escaping (NSEvent) -> NSEvent?) {
|
|
13
|
+
if (allocatedMonitor == nil) {
|
|
14
|
+
allocatedMonitor = NSEvent.addLocalMonitorForEvents(matching: mask, handler: block)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func removeMonitor() {
|
|
19
|
+
if let monitor = allocatedMonitor {
|
|
20
|
+
NSEvent.removeMonitor(monitor)
|
|
21
|
+
allocatedMonitor = nil
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
deinit {
|
|
26
|
+
removeMonitor()
|
|
27
|
+
}
|
|
28
|
+
}
|