not-react-native-macos 0.87.1-rc.5 → 0.87.1-rc.7
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/Libraries/AppState/AppState.js +5 -1
- package/Libraries/Components/Button.js +2 -1
- package/Libraries/Components/Keyboard/Keyboard.js +5 -1
- package/Libraries/Components/ScrollView/ScrollView.js +5 -1
- package/Libraries/Components/TextInput/TextInput.js +7 -2
- package/Libraries/Components/TextInput/TextInputState.js +9 -3
- package/Libraries/Components/View/ViewPropTypes.js +67 -0
- package/Libraries/EventEmitter/NativeEventEmitter.js +6 -1
- package/Libraries/Linking/Linking.js +6 -1
- package/Libraries/Modal/Modal.js +5 -1
- package/Libraries/NativeComponent/BaseViewConfig.macos.js +7 -0
- package/Libraries/Pressability/HoverState.js +5 -0
- package/Libraries/PushNotificationIOS/PushNotificationIOS.js +5 -1
- package/Libraries/ReactNative/PaperUIManager.js +2 -1
- package/Libraries/Utilities/DevSettings.js +5 -1
- package/Libraries/Utilities/HMRClient.js +4 -2
- package/Libraries/WebSocket/WebSocket.js +5 -1
- package/Libraries/WebSocket/WebSocketInterceptor.js +5 -1
- package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +181 -0
- package/React/React-RCTFabric.podspec +4 -0
- package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.cpp +61 -1
- package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.h +14 -18
- package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEvents.h +5 -2
- package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.cpp +8 -0
- package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.h +8 -0
- package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewTraitsInitializer.h +1 -1
- package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/MouseEvent.h +81 -0
- package/macos/UIKitCompat/UIKit/UIText.h +50 -0
- package/macos/UIKitCompat/UIKit/UIText.m +211 -1
- package/package.json +1 -1
- package/src/private/animated/NativeAnimatedHelper.js +5 -1
- package/src/private/specs_DEPRECATED/modules/NativeExceptionsManager.js +6 -1
|
@@ -88,7 +88,11 @@ class AppStateImpl {
|
|
|
88
88
|
new NativeEventEmitter(
|
|
89
89
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
90
90
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
91
|
-
|
|
91
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
92
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
93
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
94
|
+
? null
|
|
95
|
+
: NativeAppState,
|
|
92
96
|
);
|
|
93
97
|
this._emitter = emitter;
|
|
94
98
|
|
|
@@ -234,7 +234,8 @@ const Button: component(
|
|
|
234
234
|
const buttonStyles: Array<ViewStyleProp> = [styles.button];
|
|
235
235
|
const textStyles: Array<TextStyleProp> = [styles.text];
|
|
236
236
|
if (color) {
|
|
237
|
-
|
|
237
|
+
// [macOS] Apple platforms tint the label; Android tints the background.
|
|
238
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
238
239
|
textStyles.push({color: color});
|
|
239
240
|
} else {
|
|
240
241
|
buttonStyles.push({backgroundColor: color});
|
|
@@ -77,7 +77,11 @@ class KeyboardImpl {
|
|
|
77
77
|
new NativeEventEmitter(
|
|
78
78
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
79
79
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
80
|
-
|
|
80
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
81
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
82
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
83
|
+
? null
|
|
84
|
+
: NativeKeyboardObserver,
|
|
81
85
|
);
|
|
82
86
|
|
|
83
87
|
constructor() {
|
|
@@ -1101,7 +1101,11 @@ class ScrollView extends React.Component<ScrollViewProps, ScrollViewState> {
|
|
|
1101
1101
|
},
|
|
1102
1102
|
animated?: boolean, // deprecated, put this inside the rect argument instead
|
|
1103
1103
|
) => {
|
|
1104
|
-
|
|
1104
|
+
// [macOS] The AppKit scroll view zooms as well.
|
|
1105
|
+
invariant(
|
|
1106
|
+
Platform.OS === 'ios' || Platform.OS === 'macos',
|
|
1107
|
+
'zoomToRect is not implemented',
|
|
1108
|
+
);
|
|
1105
1109
|
if ('animated' in rect) {
|
|
1106
1110
|
this._animated = rect.animated;
|
|
1107
1111
|
delete rect.animated;
|
|
@@ -75,7 +75,11 @@ if (Platform.OS === 'android') {
|
|
|
75
75
|
AndroidTextInput = require('./AndroidTextInputNativeComponent').default;
|
|
76
76
|
AndroidTextInputCommands =
|
|
77
77
|
require('./AndroidTextInputNativeComponent').Commands;
|
|
78
|
-
|
|
78
|
+
// [macOS] macOS uses the same two Apple-platform components. Without this
|
|
79
|
+
// branch neither module is ever required, both locals stay undefined, and
|
|
80
|
+
// the render below -- also gated on `ios` -- returns nothing at all: a
|
|
81
|
+
// TextInput silently renders as empty space, with no error anywhere.
|
|
82
|
+
} else if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
79
83
|
RCTSinglelineTextInputView =
|
|
80
84
|
require('./RCTSingelineTextInputNativeComponent').default;
|
|
81
85
|
RCTSinglelineTextInputNativeCommands =
|
|
@@ -564,7 +568,8 @@ function InternalTextInput(props: TextInputProps): React.Node {
|
|
|
564
568
|
}
|
|
565
569
|
}
|
|
566
570
|
|
|
567
|
-
|
|
571
|
+
// [macOS] The Apple-platform render path; see the require above.
|
|
572
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
568
573
|
const RCTTextInputView =
|
|
569
574
|
props.multiline === true
|
|
570
575
|
? RCTMultilineTextInputView
|
|
@@ -87,7 +87,9 @@ function focusTextInput(textField: ?HostInstance) {
|
|
|
87
87
|
|
|
88
88
|
if (textField != null) {
|
|
89
89
|
const fieldCanBeFocused =
|
|
90
|
-
|
|
90
|
+
// [macOS] On a desktop any view can hold focus, so the currently focused
|
|
91
|
+
// *input* is not a reliable answer to whether this field already has it.
|
|
92
|
+
(Platform.OS === 'macos' || currentlyFocusedInputRef !== textField) &&
|
|
91
93
|
// $FlowFixMe[prop-missing] - `currentProps` is missing in `NativeMethods`
|
|
92
94
|
textField.currentProps?.editable !== false;
|
|
93
95
|
|
|
@@ -95,7 +97,9 @@ function focusTextInput(textField: ?HostInstance) {
|
|
|
95
97
|
return;
|
|
96
98
|
}
|
|
97
99
|
focusInput(textField);
|
|
98
|
-
|
|
100
|
+
// [macOS] The commands are declared by the Apple-platform native
|
|
101
|
+
// components, which macOS shares.
|
|
102
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
99
103
|
// This isn't necessarily a single line text input
|
|
100
104
|
// But commands don't actually care as long as the thing being passed in
|
|
101
105
|
// actually has a command with that name. So this should work with single
|
|
@@ -126,7 +130,9 @@ function blurTextInput(textField: ?HostInstance) {
|
|
|
126
130
|
|
|
127
131
|
if (currentlyFocusedInputRef === textField && textField != null) {
|
|
128
132
|
blurInput(textField);
|
|
129
|
-
|
|
133
|
+
// [macOS] The commands are declared by the Apple-platform native
|
|
134
|
+
// components, which macOS shares.
|
|
135
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
130
136
|
// This isn't necessarily a single line text input
|
|
131
137
|
// But commands don't actually care as long as the thing being passed in
|
|
132
138
|
// actually has a command with that name. So this should work with single
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
LayoutChangeEvent,
|
|
23
23
|
LayoutRectangle,
|
|
24
24
|
MouseEvent,
|
|
25
|
+
NativeSyntheticEvent, // [macOS] DragEvent below is built on it
|
|
25
26
|
PointerEvent,
|
|
26
27
|
} from '../../Types/CoreEventTypes';
|
|
27
28
|
import type {
|
|
@@ -146,7 +147,73 @@ export type HandledKeyEvent = Readonly<{
|
|
|
146
147
|
shiftKey?: ?boolean,
|
|
147
148
|
}>;
|
|
148
149
|
|
|
150
|
+
/**
|
|
151
|
+
* A dragged file, or dragged image data with no file behind it -- in which case
|
|
152
|
+
* `uri` is a data: URL rather than a path.
|
|
153
|
+
*
|
|
154
|
+
* @platform macos
|
|
155
|
+
*/
|
|
156
|
+
export type DataTransferFile = Readonly<{
|
|
157
|
+
name: string,
|
|
158
|
+
type: string,
|
|
159
|
+
uri: string,
|
|
160
|
+
size?: number,
|
|
161
|
+
width?: number,
|
|
162
|
+
height?: number,
|
|
163
|
+
}>;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The pasteboard contents of a drag, shaped like the DOM DataTransfer.
|
|
167
|
+
*
|
|
168
|
+
* @platform macos
|
|
169
|
+
*/
|
|
170
|
+
export type DataTransfer = Readonly<{
|
|
171
|
+
files: $ReadOnlyArray<DataTransferFile>,
|
|
172
|
+
items: $ReadOnlyArray<Readonly<{kind: string, type: string}>>,
|
|
173
|
+
types: $ReadOnlyArray<string>,
|
|
174
|
+
}>;
|
|
175
|
+
|
|
176
|
+
export type DragEvent = NativeSyntheticEvent<
|
|
177
|
+
Readonly<{
|
|
178
|
+
clientX: number,
|
|
179
|
+
clientY: number,
|
|
180
|
+
pageX: number,
|
|
181
|
+
pageY: number,
|
|
182
|
+
screenX: number,
|
|
183
|
+
screenY: number,
|
|
184
|
+
altKey: boolean,
|
|
185
|
+
ctrlKey: boolean,
|
|
186
|
+
metaKey: boolean,
|
|
187
|
+
shiftKey: boolean,
|
|
188
|
+
button: number,
|
|
189
|
+
dataTransfer: DataTransfer,
|
|
190
|
+
}>,
|
|
191
|
+
>;
|
|
192
|
+
|
|
149
193
|
type MacOSViewProps = Readonly<{
|
|
194
|
+
/**
|
|
195
|
+
* What kinds of dragged content the view accepts: 'fileUrl', 'image',
|
|
196
|
+
* 'string'. Required for the drag handlers to fire at all -- AppKit routes a
|
|
197
|
+
* drag only to views that registered for one of the pasteboard types it
|
|
198
|
+
* carries.
|
|
199
|
+
*
|
|
200
|
+
* @platform macos
|
|
201
|
+
*/
|
|
202
|
+
draggedTypes?: ?$ReadOnlyArray<'fileUrl' | 'image' | 'string'>,
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Called as a drag enters, leaves, or is released over the view.
|
|
206
|
+
*
|
|
207
|
+
* @platform macos
|
|
208
|
+
*/
|
|
209
|
+
onDragEnter?: ?(event: DragEvent) => void,
|
|
210
|
+
|
|
211
|
+
/** @platform macos */
|
|
212
|
+
onDragLeave?: ?(event: DragEvent) => void,
|
|
213
|
+
|
|
214
|
+
/** @platform macos */
|
|
215
|
+
onDrop?: ?(event: DragEvent) => void,
|
|
216
|
+
|
|
150
217
|
/**
|
|
151
218
|
* Keys this view handles itself, suppressing AppKit's own interpretation of
|
|
152
219
|
* them -- Tab moving focus, Escape cancelling, an unclaimed key beeping.
|
|
@@ -71,7 +71,12 @@ export default class NativeEventEmitter<
|
|
|
71
71
|
* an invariant error if undefined.
|
|
72
72
|
*/
|
|
73
73
|
constructor(nativeModule?: ?NativeModule) {
|
|
74
|
-
|
|
74
|
+
// [macOS] Same requirement on macOS: the module backing the emitter is an
|
|
75
|
+
// RCTEventEmitter subclass here too, so a null one is a bug rather than a
|
|
76
|
+
// platform difference. This only holds because the callers that used to
|
|
77
|
+
// pass `Platform.OS !== 'ios' ? null : NativeX` now pass the module on
|
|
78
|
+
// macOS as well -- widening this without those is an import-time crash.
|
|
79
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
75
80
|
invariant(
|
|
76
81
|
nativeModule != null,
|
|
77
82
|
'`new NativeEventEmitter()` requires a non-null argument.',
|
|
@@ -23,7 +23,12 @@ type LinkingEventDefinitions = {
|
|
|
23
23
|
|
|
24
24
|
class LinkingImpl extends NativeEventEmitter<LinkingEventDefinitions> {
|
|
25
25
|
constructor() {
|
|
26
|
-
|
|
26
|
+
// [macOS] RCTLinkingManager is built for macOS too.
|
|
27
|
+
super(
|
|
28
|
+
Platform.OS === 'ios' || Platform.OS === 'macos'
|
|
29
|
+
? nullthrows(NativeLinkingManager)
|
|
30
|
+
: undefined,
|
|
31
|
+
);
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
/**
|
package/Libraries/Modal/Modal.js
CHANGED
|
@@ -43,7 +43,11 @@ const ModalEventEmitter =
|
|
|
43
43
|
? new NativeEventEmitter<ModalEventDefinitions>(
|
|
44
44
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
45
45
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
46
|
-
|
|
46
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
47
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
48
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
49
|
+
? null
|
|
50
|
+
: NativeModalManager,
|
|
47
51
|
)
|
|
48
52
|
: null;
|
|
49
53
|
|
|
@@ -44,11 +44,15 @@ const directEventTypes = {
|
|
|
44
44
|
topAuxClick: {registrationName: 'onAuxClick'},
|
|
45
45
|
topMouseEnter: {registrationName: 'onMouseEnter'},
|
|
46
46
|
topMouseLeave: {registrationName: 'onMouseLeave'},
|
|
47
|
+
topDragEnter: {registrationName: 'onDragEnter'},
|
|
48
|
+
topDragLeave: {registrationName: 'onDragLeave'},
|
|
49
|
+
topDrop: {registrationName: 'onDrop'},
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
const validAttributesForNonEventProps = {
|
|
50
53
|
acceptsFirstMouse: true,
|
|
51
54
|
allowsVibrancy: true,
|
|
55
|
+
draggedTypes: true,
|
|
52
56
|
enableFocusRing: true,
|
|
53
57
|
focusable: true,
|
|
54
58
|
keyDownEvents: true,
|
|
@@ -60,6 +64,9 @@ const validAttributesForNonEventProps = {
|
|
|
60
64
|
const validAttributesForEventProps = ConditionallyIgnoredEventHandlers({
|
|
61
65
|
onAuxClick: true,
|
|
62
66
|
onDoubleClick: true,
|
|
67
|
+
onDragEnter: true,
|
|
68
|
+
onDragLeave: true,
|
|
69
|
+
onDrop: true,
|
|
63
70
|
onKeyDown: true,
|
|
64
71
|
onKeyUp: true,
|
|
65
72
|
onMouseEnter: true,
|
|
@@ -53,6 +53,11 @@ if (Platform.OS === 'web') {
|
|
|
53
53
|
document.addEventListener('touchstart', disableHover, true);
|
|
54
54
|
document.addEventListener('touchmove', disableHover, true);
|
|
55
55
|
document.addEventListener('mousemove', enableHover, true);
|
|
56
|
+
// [macOS] A Mac always has a pointer, and unlike the web there is no touch
|
|
57
|
+
// input to disable hover for. Without this `onHoverIn` / `onHoverOut` never
|
|
58
|
+
// fire on Pressable, because Pressability checks this flag first.
|
|
59
|
+
} else if (Platform.OS === 'macos') {
|
|
60
|
+
isEnabled = true;
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
@@ -67,7 +67,11 @@ const PushNotificationEmitter =
|
|
|
67
67
|
new NativeEventEmitter<NativePushNotificationIOSEventDefinitions>(
|
|
68
68
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
69
69
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
70
|
-
|
|
70
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
71
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
72
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
73
|
+
? null
|
|
74
|
+
: NativePushNotificationManagerIOS,
|
|
71
75
|
);
|
|
72
76
|
|
|
73
77
|
const _notifHandlers = new Map<string, void | EventSubscription>();
|
|
@@ -155,7 +155,8 @@ function lazifyViewManagerConfig(viewName: string) {
|
|
|
155
155
|
* only needed for iOS, which puts the constants in the ViewManager
|
|
156
156
|
* namespace instead of UIManager, unlike Android.
|
|
157
157
|
*/
|
|
158
|
-
|
|
158
|
+
// [macOS] Apple platforms namespace view-manager constants the same way.
|
|
159
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
159
160
|
Object.keys(getConstants()).forEach(viewName => {
|
|
160
161
|
lazifyViewManagerConfig(viewName);
|
|
161
162
|
});
|
|
@@ -53,7 +53,11 @@ if (__DEV__) {
|
|
|
53
53
|
const emitter = new NativeEventEmitter<DevSettingsEventDefinitions>(
|
|
54
54
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
55
55
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
56
|
-
|
|
56
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
57
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
58
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
59
|
+
? null
|
|
60
|
+
: NativeDevSettings,
|
|
57
61
|
);
|
|
58
62
|
const subscriptions = new Map<string, EventSubscription>();
|
|
59
63
|
|
|
@@ -192,7 +192,8 @@ const HMRClient: HMRClientNativeInterface = {
|
|
|
192
192
|
Try the following to fix the issue:
|
|
193
193
|
- Ensure that Metro is running and available on the same network`;
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
// [macOS] The AppDelegate advice is just as true here.
|
|
196
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
196
197
|
error += `
|
|
197
198
|
- Ensure that the Metro URL is correctly set in AppDelegate`;
|
|
198
199
|
} else {
|
|
@@ -349,7 +350,8 @@ function flushEarlyLogs(client: MetroHMRClient) {
|
|
|
349
350
|
|
|
350
351
|
function dismissRedbox() {
|
|
351
352
|
if (
|
|
352
|
-
|
|
353
|
+
// [macOS] RedBox is an Apple-platform module; macOS has it too.
|
|
354
|
+
(Platform.OS === 'ios' || Platform.OS === 'macos') &&
|
|
353
355
|
NativeRedBox != null &&
|
|
354
356
|
NativeRedBox.dismiss != null
|
|
355
357
|
) {
|
|
@@ -141,7 +141,11 @@ class WebSocket extends EventTarget {
|
|
|
141
141
|
this._eventEmitter = new NativeEventEmitter(
|
|
142
142
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
143
143
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
144
|
-
|
|
144
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
145
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
146
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
147
|
+
? null
|
|
148
|
+
: NativeWebSocketModule,
|
|
145
149
|
);
|
|
146
150
|
this._socketId = nextWebSocketId++;
|
|
147
151
|
this._registerEvents();
|
|
@@ -169,7 +169,11 @@ const WebSocketInterceptor = {
|
|
|
169
169
|
eventEmitter = new NativeEventEmitter(
|
|
170
170
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
171
171
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
172
|
-
|
|
172
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
173
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
174
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
175
|
+
? null
|
|
176
|
+
: NativeWebSocketModule,
|
|
173
177
|
);
|
|
174
178
|
WebSocketInterceptor._registerEvents();
|
|
175
179
|
|
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
#import <React/RCTLinearGradient.h>
|
|
24
24
|
#import <React/RCTLocalizedString.h>
|
|
25
25
|
#import <React/RCTRadialGradient.h>
|
|
26
|
+
#if TARGET_OS_OSX // [macOS] drag and drop: RCTDataURL for dragged image data,
|
|
27
|
+
// UTType to name what was dropped.
|
|
28
|
+
#import <React/RCTUtils.h>
|
|
29
|
+
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
|
30
|
+
#endif // macOS]
|
|
26
31
|
#import <react/featureflags/ReactNativeFeatureFlags.h>
|
|
27
32
|
#import <react/renderer/components/view/ViewComponentDescriptor.h>
|
|
28
33
|
#import <react/renderer/components/view/ViewEventEmitter.h>
|
|
@@ -664,6 +669,38 @@ static BOOL RCTLayerTransformCollapsesAxis(CALayer *layer)
|
|
|
664
669
|
if (oldViewProps.hostPlatformEvents != newViewProps.hostPlatformEvents) {
|
|
665
670
|
[self _updateMouseTracking:newViewProps.hostPlatformEvents.wantsMouseTracking()];
|
|
666
671
|
}
|
|
672
|
+
|
|
673
|
+
if (oldViewProps.draggedTypes != newViewProps.draggedTypes) {
|
|
674
|
+
[self _updateDraggedTypes:newViewProps.draggedTypes];
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* AppKit delivers a drag only to views that registered for one of the
|
|
680
|
+
* pasteboard types it carries, so without this the drag handlers are
|
|
681
|
+
* unreachable. The three names are the kinds React Native models; each maps to
|
|
682
|
+
* the pasteboard types AppKit actually uses for it.
|
|
683
|
+
*/
|
|
684
|
+
- (void)_updateDraggedTypes:(const std::vector<std::string> &)draggedTypes
|
|
685
|
+
{
|
|
686
|
+
[self unregisterDraggedTypes];
|
|
687
|
+
|
|
688
|
+
if (draggedTypes.empty()) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
NSMutableArray<NSPasteboardType> *types = [NSMutableArray arrayWithCapacity:draggedTypes.size()];
|
|
693
|
+
for (const auto &draggedType : draggedTypes) {
|
|
694
|
+
if (draggedType == "fileUrl") {
|
|
695
|
+
[types addObject:NSPasteboardTypeFileURL];
|
|
696
|
+
} else if (draggedType == "image") {
|
|
697
|
+
[types addObject:NSPasteboardTypePNG];
|
|
698
|
+
[types addObject:NSPasteboardTypeTIFF];
|
|
699
|
+
} else if (draggedType == "string") {
|
|
700
|
+
[types addObject:NSPasteboardTypeString];
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
[self registerForDraggedTypes:types];
|
|
667
704
|
}
|
|
668
705
|
|
|
669
706
|
/**
|
|
@@ -741,6 +778,150 @@ static BOOL RCTLayerTransformCollapsesAxis(CALayer *layer)
|
|
|
741
778
|
}
|
|
742
779
|
}
|
|
743
780
|
|
|
781
|
+
#pragma mark - Drag and Drop Events
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* The pasteboard, shaped like the DOM DataTransfer so a drop handler reads the
|
|
785
|
+
* same on macOS as on the web.
|
|
786
|
+
*
|
|
787
|
+
* Dragged files are reported by path. Dragged image *data* -- an image dragged
|
|
788
|
+
* out of a browser, say, with no file behind it -- has no path to give, so it
|
|
789
|
+
* is reported as a data: URL instead; `uri` is what a consumer feeds to
|
|
790
|
+
* <Image> either way.
|
|
791
|
+
*/
|
|
792
|
+
- (DataTransfer)_dataTransferForPasteboard:(NSPasteboard *)pasteboard
|
|
793
|
+
{
|
|
794
|
+
DataTransfer dataTransfer{};
|
|
795
|
+
|
|
796
|
+
NSArray<NSURL *> *fileURLs = [pasteboard readObjectsForClasses:@[ [NSURL class] ]
|
|
797
|
+
options:@{NSPasteboardURLReadingFileURLsOnlyKey : @YES}]
|
|
798
|
+
?: @[];
|
|
799
|
+
|
|
800
|
+
for (NSURL *fileURL in fileURLs) {
|
|
801
|
+
BOOL isDirectory = NO;
|
|
802
|
+
if (![NSFileManager.defaultManager fileExistsAtPath:fileURL.path isDirectory:&isDirectory] || isDirectory) {
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
UTType *type = [UTType typeWithFilenameExtension:fileURL.pathExtension];
|
|
807
|
+
NSString *mimeType = type.preferredMIMEType;
|
|
808
|
+
std::string typeString = mimeType != nil ? mimeType.UTF8String : "";
|
|
809
|
+
|
|
810
|
+
DataTransferFile file = {
|
|
811
|
+
.name = fileURL.lastPathComponent != nil ? fileURL.lastPathComponent.UTF8String : "",
|
|
812
|
+
.type = typeString,
|
|
813
|
+
.uri = fileURL.path != nil ? fileURL.path.UTF8String : "",
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
NSNumber *fileSize = nil;
|
|
817
|
+
if ([fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL]) {
|
|
818
|
+
file.size = fileSize.intValue;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if ([mimeType hasPrefix:@"image/"]) {
|
|
822
|
+
NSImage *image = [[NSImage alloc] initWithContentsOfURL:fileURL];
|
|
823
|
+
CGImageRef cgImage = [image CGImageForProposedRect:NULL context:nil hints:nil];
|
|
824
|
+
if (cgImage != NULL) {
|
|
825
|
+
file.width = static_cast<int>(CGImageGetWidth(cgImage));
|
|
826
|
+
file.height = static_cast<int>(CGImageGetHeight(cgImage));
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
dataTransfer.files.push_back(file);
|
|
831
|
+
dataTransfer.items.push_back({.kind = "file", .type = typeString});
|
|
832
|
+
dataTransfer.types.push_back(typeString);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
NSPasteboardType imageType = [pasteboard availableTypeFromArray:@[ NSPasteboardTypePNG, NSPasteboardTypeTIFF ]];
|
|
836
|
+
if (imageType != nil && fileURLs.count == 0) {
|
|
837
|
+
NSString *mimeType = [imageType isEqualToString:NSPasteboardTypePNG] ? UTTypePNG.preferredMIMEType
|
|
838
|
+
: UTTypeTIFF.preferredMIMEType;
|
|
839
|
+
NSData *imageData = [pasteboard dataForType:imageType];
|
|
840
|
+
std::string typeString = mimeType != nil ? mimeType.UTF8String : "";
|
|
841
|
+
|
|
842
|
+
NSString *dataURL = RCTDataURL(mimeType, imageData).absoluteString;
|
|
843
|
+
DataTransferFile file = {
|
|
844
|
+
.name = "",
|
|
845
|
+
.type = typeString,
|
|
846
|
+
.uri = dataURL != nil ? dataURL.UTF8String : "",
|
|
847
|
+
};
|
|
848
|
+
file.size = static_cast<int>(imageData.length);
|
|
849
|
+
|
|
850
|
+
NSImage *image = [[NSImage alloc] initWithData:imageData];
|
|
851
|
+
CGImageRef cgImage = [image CGImageForProposedRect:NULL context:nil hints:nil];
|
|
852
|
+
if (cgImage != NULL) {
|
|
853
|
+
file.width = static_cast<int>(CGImageGetWidth(cgImage));
|
|
854
|
+
file.height = static_cast<int>(CGImageGetHeight(cgImage));
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
dataTransfer.files.push_back(file);
|
|
858
|
+
dataTransfer.items.push_back({.kind = "image", .type = typeString});
|
|
859
|
+
dataTransfer.types.push_back(typeString);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
return dataTransfer;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
- (DragEvent)_dragEventFromDraggingInfo:(id<NSDraggingInfo>)info
|
|
866
|
+
{
|
|
867
|
+
NSPoint inWindow = info.draggingLocation;
|
|
868
|
+
NSPoint inView = [self convertPoint:inWindow fromView:nil];
|
|
869
|
+
NSEventModifierFlags flags = self.window.currentEvent.modifierFlags;
|
|
870
|
+
|
|
871
|
+
DragEvent event = {};
|
|
872
|
+
event.clientX = inView.x;
|
|
873
|
+
event.clientY = inView.y;
|
|
874
|
+
event.pageX = inWindow.x;
|
|
875
|
+
event.pageY = inWindow.y;
|
|
876
|
+
event.screenX = inWindow.x;
|
|
877
|
+
event.screenY = inWindow.y;
|
|
878
|
+
event.altKey = static_cast<bool>(flags & NSEventModifierFlagOption);
|
|
879
|
+
event.ctrlKey = static_cast<bool>(flags & NSEventModifierFlagControl);
|
|
880
|
+
event.shiftKey = static_cast<bool>(flags & NSEventModifierFlagShift);
|
|
881
|
+
event.metaKey = static_cast<bool>(flags & NSEventModifierFlagCommand);
|
|
882
|
+
event.dataTransfer = [self _dataTransferForPasteboard:info.draggingPasteboard];
|
|
883
|
+
return event;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
|
|
887
|
+
{
|
|
888
|
+
if (_eventEmitter != nullptr) {
|
|
889
|
+
_eventEmitter->onDragEnter([self _dragEventFromDraggingInfo:sender]);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// The answer is the cursor the user sees, so it has to reflect what this view
|
|
893
|
+
// would actually accept -- claiming a drag the pasteboard cannot satisfy shows
|
|
894
|
+
// a drop cursor over content that will refuse it.
|
|
895
|
+
if ([sender.draggingPasteboard availableTypeFromArray:self.registeredDraggedTypes] == nil) {
|
|
896
|
+
return NSDragOperationNone;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
NSDragOperation offered = sender.draggingSourceOperationMask;
|
|
900
|
+
if (offered & NSDragOperationLink) {
|
|
901
|
+
return NSDragOperationLink;
|
|
902
|
+
}
|
|
903
|
+
if (offered & NSDragOperationCopy) {
|
|
904
|
+
return NSDragOperationCopy;
|
|
905
|
+
}
|
|
906
|
+
return NSDragOperationNone;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
- (void)draggingExited:(id<NSDraggingInfo>)sender
|
|
910
|
+
{
|
|
911
|
+
if (_eventEmitter != nullptr) {
|
|
912
|
+
_eventEmitter->onDragLeave([self _dragEventFromDraggingInfo:sender]);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
|
|
917
|
+
{
|
|
918
|
+
if (_eventEmitter == nullptr) {
|
|
919
|
+
return NO;
|
|
920
|
+
}
|
|
921
|
+
_eventEmitter->onDrop([self _dragEventFromDraggingInfo:sender]);
|
|
922
|
+
return YES;
|
|
923
|
+
}
|
|
924
|
+
|
|
744
925
|
#pragma mark - Keyboard Events
|
|
745
926
|
|
|
746
927
|
/**
|
|
@@ -50,6 +50,10 @@ Pod::Spec.new do |s|
|
|
|
50
50
|
s.module_name = module_name
|
|
51
51
|
s.weak_framework = "JavaScriptCore"
|
|
52
52
|
s.framework = "MobileCoreServices"
|
|
53
|
+
# [macOS] Drag and drop names what was dropped with UTType. On iOS the symbols
|
|
54
|
+
# come in with MobileCoreServices; the macOS remap of that is CoreServices,
|
|
55
|
+
# which does not carry them.
|
|
56
|
+
s.osx.framework = "UniformTypeIdentifiers"
|
|
53
57
|
s.pod_target_xcconfig = {
|
|
54
58
|
"HEADER_SEARCH_PATHS" => header_search_paths,
|
|
55
59
|
"OTHER_CFLAGS" => "$(inherited) " + new_arch_flags,
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
namespace facebook::react {
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
// Returns an Object, not a Value: dragEventPayload starts from one and adds to it.
|
|
15
|
+
static jsi::Object mouseEventPayload(jsi::Runtime &runtime, const MouseEvent &event)
|
|
15
16
|
{
|
|
16
17
|
auto payload = jsi::Object(runtime);
|
|
17
18
|
payload.setProperty(runtime, "clientX", event.clientX);
|
|
@@ -71,4 +72,63 @@ RCT_MACOS_MOUSE_EVENT(onAuxClick)
|
|
|
71
72
|
RCT_MACOS_KEY_EVENT(onKeyDown)
|
|
72
73
|
RCT_MACOS_KEY_EVENT(onKeyUp)
|
|
73
74
|
|
|
75
|
+
static jsi::Value dragEventPayload(jsi::Runtime &runtime, const DragEvent &event)
|
|
76
|
+
{
|
|
77
|
+
auto dataTransfer = jsi::Object(runtime);
|
|
78
|
+
|
|
79
|
+
auto files = jsi::Array(runtime, event.dataTransfer.files.size());
|
|
80
|
+
for (size_t i = 0; i < event.dataTransfer.files.size(); i++) {
|
|
81
|
+
const auto &file = event.dataTransfer.files[i];
|
|
82
|
+
auto entry = jsi::Object(runtime);
|
|
83
|
+
entry.setProperty(runtime, "name", jsi::String::createFromUtf8(runtime, file.name));
|
|
84
|
+
entry.setProperty(runtime, "type", jsi::String::createFromUtf8(runtime, file.type));
|
|
85
|
+
entry.setProperty(runtime, "uri", jsi::String::createFromUtf8(runtime, file.uri));
|
|
86
|
+
// Absent rather than zero when unknown: a directory has no meaningful size,
|
|
87
|
+
// and only an image has dimensions.
|
|
88
|
+
if (file.size.has_value()) {
|
|
89
|
+
entry.setProperty(runtime, "size", *file.size);
|
|
90
|
+
}
|
|
91
|
+
if (file.width.has_value()) {
|
|
92
|
+
entry.setProperty(runtime, "width", *file.width);
|
|
93
|
+
}
|
|
94
|
+
if (file.height.has_value()) {
|
|
95
|
+
entry.setProperty(runtime, "height", *file.height);
|
|
96
|
+
}
|
|
97
|
+
files.setValueAtIndex(runtime, i, entry);
|
|
98
|
+
}
|
|
99
|
+
dataTransfer.setProperty(runtime, "files", files);
|
|
100
|
+
|
|
101
|
+
auto items = jsi::Array(runtime, event.dataTransfer.items.size());
|
|
102
|
+
for (size_t i = 0; i < event.dataTransfer.items.size(); i++) {
|
|
103
|
+
const auto &item = event.dataTransfer.items[i];
|
|
104
|
+
auto entry = jsi::Object(runtime);
|
|
105
|
+
entry.setProperty(runtime, "kind", jsi::String::createFromUtf8(runtime, item.kind));
|
|
106
|
+
entry.setProperty(runtime, "type", jsi::String::createFromUtf8(runtime, item.type));
|
|
107
|
+
items.setValueAtIndex(runtime, i, entry);
|
|
108
|
+
}
|
|
109
|
+
dataTransfer.setProperty(runtime, "items", items);
|
|
110
|
+
|
|
111
|
+
auto types = jsi::Array(runtime, event.dataTransfer.types.size());
|
|
112
|
+
for (size_t i = 0; i < event.dataTransfer.types.size(); i++) {
|
|
113
|
+
types.setValueAtIndex(runtime, i, jsi::String::createFromUtf8(runtime, event.dataTransfer.types[i]));
|
|
114
|
+
}
|
|
115
|
+
dataTransfer.setProperty(runtime, "types", types);
|
|
116
|
+
|
|
117
|
+
auto payload = mouseEventPayload(runtime, event);
|
|
118
|
+
payload.setProperty(runtime, "dataTransfer", dataTransfer);
|
|
119
|
+
return payload;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#define RCT_MACOS_DRAG_EVENT(name) \
|
|
123
|
+
void HostPlatformViewEventEmitter::name(const DragEvent &event) const \
|
|
124
|
+
{ \
|
|
125
|
+
dispatchEvent(#name, [event](jsi::Runtime &runtime) { \
|
|
126
|
+
return dragEventPayload(runtime, event); \
|
|
127
|
+
}); \
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
RCT_MACOS_DRAG_EVENT(onDragEnter)
|
|
131
|
+
RCT_MACOS_DRAG_EVENT(onDragLeave)
|
|
132
|
+
RCT_MACOS_DRAG_EVENT(onDrop)
|
|
133
|
+
|
|
74
134
|
} // namespace facebook::react
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include <react/renderer/components/view/BaseViewEventEmitter.h>
|
|
13
13
|
|
|
14
14
|
#include "KeyEvent.h"
|
|
15
|
+
#include "MouseEvent.h"
|
|
15
16
|
|
|
16
17
|
namespace facebook::react {
|
|
17
18
|
|
|
@@ -20,25 +21,10 @@ class HostPlatformViewEventEmitter : public BaseViewEventEmitter {
|
|
|
20
21
|
using BaseViewEventEmitter::BaseViewEventEmitter;
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
24
|
+
* Named here as well so `HostPlatformViewEventEmitter::MouseEvent` keeps
|
|
25
|
+
* working; the type itself is free-standing, in MouseEvent.h.
|
|
25
26
|
*/
|
|
26
|
-
|
|
27
|
-
Float clientX{};
|
|
28
|
-
Float clientY{};
|
|
29
|
-
Float screenX{};
|
|
30
|
-
Float screenY{};
|
|
31
|
-
Float pageX{};
|
|
32
|
-
Float pageY{};
|
|
33
|
-
|
|
34
|
-
bool altKey{false};
|
|
35
|
-
bool ctrlKey{false};
|
|
36
|
-
bool shiftKey{false};
|
|
37
|
-
bool metaKey{false};
|
|
38
|
-
|
|
39
|
-
/** DOM button numbering: 0 left, 1 middle, 2 right. */
|
|
40
|
-
int button{0};
|
|
41
|
-
};
|
|
27
|
+
using MouseEvent = ::facebook::react::MouseEvent;
|
|
42
28
|
|
|
43
29
|
void onMouseEnter(const MouseEvent &event) const;
|
|
44
30
|
void onMouseLeave(const MouseEvent &event) const;
|
|
@@ -47,6 +33,16 @@ class HostPlatformViewEventEmitter : public BaseViewEventEmitter {
|
|
|
47
33
|
|
|
48
34
|
void onKeyDown(const KeyEvent &event) const;
|
|
49
35
|
void onKeyUp(const KeyEvent &event) const;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A drag passing over or finishing on the view. Only reported for views that
|
|
39
|
+
* registered interest through the `draggedTypes` prop -- AppKit routes a drag
|
|
40
|
+
* to the topmost view that registered for one of the pasteboard types it
|
|
41
|
+
* carries, and ignores the rest.
|
|
42
|
+
*/
|
|
43
|
+
void onDragEnter(const DragEvent &event) const;
|
|
44
|
+
void onDragLeave(const DragEvent &event) const;
|
|
45
|
+
void onDrop(const DragEvent &event) const;
|
|
50
46
|
};
|
|
51
47
|
|
|
52
48
|
} // namespace facebook::react
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
namespace facebook::react {
|
|
21
21
|
|
|
22
22
|
struct HostPlatformViewEvents {
|
|
23
|
-
std::bitset<
|
|
23
|
+
std::bitset<16> bits{};
|
|
24
24
|
|
|
25
25
|
enum class Offset : std::size_t {
|
|
26
26
|
MouseEnter = 0,
|
|
@@ -29,6 +29,9 @@ struct HostPlatformViewEvents {
|
|
|
29
29
|
AuxClick = 3,
|
|
30
30
|
KeyDown = 4,
|
|
31
31
|
KeyUp = 5,
|
|
32
|
+
DragEnter = 6,
|
|
33
|
+
DragLeave = 7,
|
|
34
|
+
Drop = 8,
|
|
32
35
|
};
|
|
33
36
|
|
|
34
37
|
constexpr bool operator[](Offset offset) const
|
|
@@ -36,7 +39,7 @@ struct HostPlatformViewEvents {
|
|
|
36
39
|
return bits[static_cast<std::size_t>(offset)];
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
std::bitset<
|
|
42
|
+
std::bitset<16>::reference operator[](Offset offset)
|
|
40
43
|
{
|
|
41
44
|
return bits[static_cast<std::size_t>(offset)];
|
|
42
45
|
}
|
|
@@ -35,6 +35,9 @@ static inline HostPlatformViewEvents convertRawProp(
|
|
|
35
35
|
RCT_MACOS_CONVERT_EVENT("onAuxClick", AuxClick);
|
|
36
36
|
RCT_MACOS_CONVERT_EVENT("onKeyDown", KeyDown);
|
|
37
37
|
RCT_MACOS_CONVERT_EVENT("onKeyUp", KeyUp);
|
|
38
|
+
RCT_MACOS_CONVERT_EVENT("onDragEnter", DragEnter);
|
|
39
|
+
RCT_MACOS_CONVERT_EVENT("onDragLeave", DragLeave);
|
|
40
|
+
RCT_MACOS_CONVERT_EVENT("onDrop", Drop);
|
|
38
41
|
|
|
39
42
|
#undef RCT_MACOS_CONVERT_EVENT
|
|
40
43
|
|
|
@@ -63,6 +66,7 @@ HostPlatformViewProps::HostPlatformViewProps(
|
|
|
63
66
|
RCT_MACOS_PROP(enableFocusRing),
|
|
64
67
|
RCT_MACOS_PROP(keyDownEvents),
|
|
65
68
|
RCT_MACOS_PROP(keyUpEvents),
|
|
69
|
+
RCT_MACOS_PROP(draggedTypes),
|
|
66
70
|
RCT_MACOS_PROP(tooltip),
|
|
67
71
|
RCT_MACOS_PROP(acceptsFirstMouse),
|
|
68
72
|
RCT_MACOS_PROP(allowsVibrancy),
|
|
@@ -105,10 +109,14 @@ void HostPlatformViewProps::setProp(
|
|
|
105
109
|
RCT_MACOS_EVENT_CASE(AuxClick);
|
|
106
110
|
RCT_MACOS_EVENT_CASE(KeyDown);
|
|
107
111
|
RCT_MACOS_EVENT_CASE(KeyUp);
|
|
112
|
+
RCT_MACOS_EVENT_CASE(DragEnter);
|
|
113
|
+
RCT_MACOS_EVENT_CASE(DragLeave);
|
|
114
|
+
RCT_MACOS_EVENT_CASE(Drop);
|
|
108
115
|
RAW_SET_PROP_SWITCH_CASE_BASIC(focusable);
|
|
109
116
|
RAW_SET_PROP_SWITCH_CASE_BASIC(enableFocusRing);
|
|
110
117
|
RAW_SET_PROP_SWITCH_CASE_BASIC(keyDownEvents);
|
|
111
118
|
RAW_SET_PROP_SWITCH_CASE_BASIC(keyUpEvents);
|
|
119
|
+
RAW_SET_PROP_SWITCH_CASE_BASIC(draggedTypes);
|
|
112
120
|
RAW_SET_PROP_SWITCH_CASE_BASIC(tooltip);
|
|
113
121
|
RAW_SET_PROP_SWITCH_CASE_BASIC(acceptsFirstMouse);
|
|
114
122
|
RAW_SET_PROP_SWITCH_CASE_BASIC(allowsVibrancy);
|
|
@@ -57,6 +57,14 @@ class HostPlatformViewProps : public BaseViewProps {
|
|
|
57
57
|
std::vector<HandledKey> keyDownEvents{};
|
|
58
58
|
std::vector<HandledKey> keyUpEvents{};
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Which kinds of dragged content the view accepts: "fileUrl", "image",
|
|
62
|
+
* "string". Empty means the view takes no part in drag and drop -- AppKit
|
|
63
|
+
* routes a drag only to views that registered for one of its pasteboard
|
|
64
|
+
* types, so this is what makes onDragEnter/onDrop reachable at all.
|
|
65
|
+
*/
|
|
66
|
+
std::vector<std::string> draggedTypes{};
|
|
67
|
+
|
|
60
68
|
/** The view's help tag. std::nullopt leaves any inherited tooltip alone. */
|
|
61
69
|
std::optional<std::string> tooltip{};
|
|
62
70
|
|
|
@@ -27,7 +27,7 @@ inline bool formsView(const ViewProps &props)
|
|
|
27
27
|
// has to attach to something, and mouse tracking needs an NSTrackingArea
|
|
28
28
|
// owner. Flattening is otherwise invisible -- the handler simply never fires.
|
|
29
29
|
return props.focusable || props.tooltip.has_value() || props.hostPlatformEvents.bits.any() ||
|
|
30
|
-
!props.keyDownEvents.empty() || !props.keyUpEvents.empty();
|
|
30
|
+
!props.keyDownEvents.empty() || !props.keyUpEvents.empty() || !props.draggedTypes.empty();
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
inline bool isKeyboardFocusable(const ViewProps &props)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// [macOS] Pointer and drag payloads, which only a desktop platform reports.
|
|
9
|
+
//
|
|
10
|
+
// Free-standing rather than nested in the event emitter, because
|
|
11
|
+
// microsoft/react-native-macos puts `facebook::react::MouseEvent` and
|
|
12
|
+
// `DragEvent` here and third-party Fabric components name them directly.
|
|
13
|
+
|
|
14
|
+
#pragma once
|
|
15
|
+
|
|
16
|
+
#include <react/renderer/graphics/Float.h>
|
|
17
|
+
|
|
18
|
+
#include <optional>
|
|
19
|
+
#include <string>
|
|
20
|
+
#include <vector>
|
|
21
|
+
|
|
22
|
+
namespace facebook::react {
|
|
23
|
+
|
|
24
|
+
/** A mouse crossing, click or drag position. Fields follow the DOM MouseEvent. */
|
|
25
|
+
struct MouseEvent {
|
|
26
|
+
/** Pointer location in the target view. */
|
|
27
|
+
Float clientX{0};
|
|
28
|
+
Float clientY{0};
|
|
29
|
+
|
|
30
|
+
/** Pointer location in the window. */
|
|
31
|
+
Float screenX{0};
|
|
32
|
+
Float screenY{0};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Pointer location in the window as well. The DOM distinguishes page from
|
|
36
|
+
* screen coordinates; AppKit has no document scroll offset to make them
|
|
37
|
+
* differ, so both report the window position.
|
|
38
|
+
*/
|
|
39
|
+
Float pageX{0};
|
|
40
|
+
Float pageY{0};
|
|
41
|
+
|
|
42
|
+
bool altKey{false};
|
|
43
|
+
bool ctrlKey{false};
|
|
44
|
+
bool shiftKey{false};
|
|
45
|
+
bool metaKey{false};
|
|
46
|
+
|
|
47
|
+
/** DOM button numbering: 0 primary, 1 auxiliary, 2 secondary. */
|
|
48
|
+
int button{0};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** One dragged file, described the way the DOM File interface does. */
|
|
52
|
+
struct DataTransferFile {
|
|
53
|
+
std::string name{};
|
|
54
|
+
std::string type{};
|
|
55
|
+
|
|
56
|
+
/** A file path for a dragged file, or a data: URL for dragged image data. */
|
|
57
|
+
std::string uri{};
|
|
58
|
+
|
|
59
|
+
std::optional<int> size{};
|
|
60
|
+
std::optional<int> width{};
|
|
61
|
+
std::optional<int> height{};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
struct DataTransferItem {
|
|
65
|
+
/** "file" or "image". */
|
|
66
|
+
std::string kind{};
|
|
67
|
+
std::string type{};
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** The pasteboard contents of a drag, shaped like the DOM DataTransfer. */
|
|
71
|
+
struct DataTransfer {
|
|
72
|
+
std::vector<DataTransferFile> files{};
|
|
73
|
+
std::vector<DataTransferItem> items{};
|
|
74
|
+
std::vector<std::string> types{};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
struct DragEvent : MouseEvent {
|
|
78
|
+
DataTransfer dataTransfer{};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
} // namespace facebook::react
|
|
@@ -158,6 +158,35 @@ typedef NS_ENUM(NSInteger, UIKeyboardAppearance) {
|
|
|
158
158
|
// UIKit's per-field default attributes. NSTextField applies typing attributes
|
|
159
159
|
// through its field editor instead, so these are stored and applied on edit.
|
|
160
160
|
@property (nonatomic, copy, nullable) NSDictionary<NSAttributedStringKey, id> *defaultTextAttributes;
|
|
161
|
+
// The attributes newly typed text takes on. NSTextField has no such property:
|
|
162
|
+
// editing happens in the window's shared field editor, which is an NSTextView
|
|
163
|
+
// and does. Reads and writes are forwarded there while editing, and held here
|
|
164
|
+
// otherwise so the value survives between edits.
|
|
165
|
+
@property (nonatomic, copy, null_resettable) NSDictionary<NSAttributedStringKey, id> *typingAttributes;
|
|
166
|
+
// NSTextField spells the styled value attributedStringValue.
|
|
167
|
+
@property (nonatomic, copy, nullable) NSAttributedString *attributedText;
|
|
168
|
+
// NSControl spells it alignment.
|
|
169
|
+
@property (nonatomic, assign) NSTextAlignment textAlignment;
|
|
170
|
+
// UIKit's clear button. NSTextField has no equivalent, so this is stored and
|
|
171
|
+
// otherwise unused -- a Mac text field does not show one.
|
|
172
|
+
@property (nonatomic, assign) UITextFieldViewMode clearButtonMode;
|
|
173
|
+
// UIKit hangs custom keyboards and toolbars off the responder. AppKit has no
|
|
174
|
+
// software keyboard, so these are stored and never presented.
|
|
175
|
+
@property (nonatomic, strong, nullable) UIView *inputView;
|
|
176
|
+
@property (nonatomic, strong, nullable) UIView *inputAccessoryView;
|
|
177
|
+
// Traits UITextInputTraits declares as @optional. A protocol property creates
|
|
178
|
+
// no storage, so each one an ObjC class is expected to answer has to be
|
|
179
|
+
// synthesized by that class -- omitting them is an unrecognized selector at
|
|
180
|
+
// the first access, not a compile error.
|
|
181
|
+
@property (nonatomic, assign) UITextSmartInsertDeleteType smartInsertDeleteType;
|
|
182
|
+
@property (nonatomic, assign) UITextSmartQuotesType smartQuotesType;
|
|
183
|
+
@property (nonatomic, assign) UITextSmartDashesType smartDashesType;
|
|
184
|
+
@property (nonatomic, copy, nullable) NSString *textContentType;
|
|
185
|
+
@property (nonatomic, strong, nullable) id passwordRules;
|
|
186
|
+
@property (nonatomic, assign) BOOL enablesReturnKeyAutomatically;
|
|
187
|
+
// UIKit tints the caret and selection through the view; AppKit takes it from
|
|
188
|
+
// the field editor's insertion-point colour.
|
|
189
|
+
@property (nonatomic, strong, nullable) UIColor *tintColor;
|
|
161
190
|
// NSTextField spells it placeholderAttributedString.
|
|
162
191
|
@property (nonatomic, copy, nullable) NSAttributedString *attributedPlaceholder;
|
|
163
192
|
// UIResponder's editing-menu hook; NSResponder has -validateUserInterfaceItem:.
|
|
@@ -219,6 +248,27 @@ typedef NS_ENUM(NSInteger, UIKeyboardAppearance) {
|
|
|
219
248
|
@property (nonatomic, assign) UIReturnKeyType returnKeyType;
|
|
220
249
|
@property (nonatomic, copy, nullable) NSString *text;
|
|
221
250
|
@property (nonatomic, weak, nullable) id<UITextDropDelegate> textDropDelegate;
|
|
251
|
+
// As on UITextField: UITextInputTraits declares these @optional, so a class
|
|
252
|
+
// expected to answer them has to synthesize its own storage.
|
|
253
|
+
@property (nonatomic, assign) UITextAutocapitalizationType autocapitalizationType;
|
|
254
|
+
@property (nonatomic, assign) UITextAutocorrectionType autocorrectionType;
|
|
255
|
+
@property (nonatomic, assign) UITextSpellCheckingType spellCheckingType;
|
|
256
|
+
@property (nonatomic, assign) UIKeyboardAppearance keyboardAppearance;
|
|
257
|
+
@property (nonatomic, assign) UITextSmartInsertDeleteType smartInsertDeleteType;
|
|
258
|
+
@property (nonatomic, assign) UITextSmartQuotesType smartQuotesType;
|
|
259
|
+
@property (nonatomic, assign) UITextSmartDashesType smartDashesType;
|
|
260
|
+
@property (nonatomic, assign, getter=isSecureTextEntry) BOOL secureTextEntry;
|
|
261
|
+
@property (nonatomic, assign) BOOL enablesReturnKeyAutomatically;
|
|
262
|
+
@property (nonatomic, copy, nullable) NSString *textContentType;
|
|
263
|
+
@property (nonatomic, strong, nullable) id passwordRules;
|
|
264
|
+
// UIKit's text view scrolls itself. NSTextView is the document view of an
|
|
265
|
+
// enclosing NSScrollView, so this forwards to that.
|
|
266
|
+
@property (nonatomic, assign, getter=isScrollEnabled) BOOL scrollEnabled;
|
|
267
|
+
@property (nonatomic, assign) CGPoint contentOffset;
|
|
268
|
+
@property (nonatomic, assign) CGFloat zoomScale;
|
|
269
|
+
@property (nonatomic, strong, nullable) UIView *inputView;
|
|
270
|
+
@property (nonatomic, strong, nullable) UIView *inputAccessoryView;
|
|
271
|
+
@property (nonatomic, assign) UIDataDetectorTypes dataDetectorTypes;
|
|
222
272
|
@end
|
|
223
273
|
|
|
224
274
|
// UIKit's editing notifications. AppKit posts NSControlTextDidChange and
|
|
@@ -78,10 +78,141 @@
|
|
|
78
78
|
|
|
79
79
|
@end
|
|
80
80
|
|
|
81
|
-
@implementation UITextField
|
|
81
|
+
@implementation UITextField {
|
|
82
|
+
NSDictionary<NSAttributedStringKey, id> *_typingAttributes;
|
|
83
|
+
}
|
|
82
84
|
|
|
83
85
|
UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
84
86
|
|
|
87
|
+
/**
|
|
88
|
+
* The live field editor, when this field is the one being edited.
|
|
89
|
+
*
|
|
90
|
+
* `-fieldEditor:forObject:` with NO does not create one, so this is nil unless
|
|
91
|
+
* editing is actually under way -- which is what makes it safe to ask for on
|
|
92
|
+
* every access.
|
|
93
|
+
*/
|
|
94
|
+
- (NSTextView *)uikitCompat_activeFieldEditor
|
|
95
|
+
{
|
|
96
|
+
NSText *editor = [self.window fieldEditor:NO forObject:self];
|
|
97
|
+
if ([editor isKindOfClass:[NSTextView class]] && self.currentEditor == editor) {
|
|
98
|
+
return (NSTextView *)editor;
|
|
99
|
+
}
|
|
100
|
+
return nil;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@synthesize smartInsertDeleteType = _smartInsertDeleteType;
|
|
104
|
+
@synthesize smartQuotesType = _smartQuotesType;
|
|
105
|
+
@synthesize smartDashesType = _smartDashesType;
|
|
106
|
+
@synthesize textContentType = _textContentType;
|
|
107
|
+
@synthesize passwordRules = _passwordRules;
|
|
108
|
+
@synthesize enablesReturnKeyAutomatically = _enablesReturnKeyAutomatically;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Marked text is an in-progress IME composition. AppKit tracks it on the field
|
|
112
|
+
* editor, so there is nothing marked when the field is not being edited.
|
|
113
|
+
*/
|
|
114
|
+
- (UITextRange *)markedTextRange
|
|
115
|
+
{
|
|
116
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
117
|
+
if (editor == nil || !editor.hasMarkedText) {
|
|
118
|
+
return nil;
|
|
119
|
+
}
|
|
120
|
+
NSRange range = editor.markedRange;
|
|
121
|
+
return [UITextRange rangeWithStart:[UITextPosition positionWithOffset:(NSInteger)range.location]
|
|
122
|
+
end:[UITextPosition positionWithOffset:(NSInteger)(range.location + range.length)]];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
- (UITextInputMode *)textInputMode
|
|
126
|
+
{
|
|
127
|
+
// UIKit reports the software keyboard's language. AppKit has no software
|
|
128
|
+
// keyboard, and no caller here does more than null-check the result.
|
|
129
|
+
return nil;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
- (UIColor *)tintColor
|
|
133
|
+
{
|
|
134
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
135
|
+
return editor != nil ? editor.insertionPointColor : nil;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
- (void)setTintColor:(UIColor *)tintColor
|
|
139
|
+
{
|
|
140
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
141
|
+
if (editor != nil && tintColor != nil) {
|
|
142
|
+
editor.insertionPointColor = tintColor;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
- (NSAttributedString *)attributedText
|
|
147
|
+
{
|
|
148
|
+
return self.attributedStringValue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
- (void)setAttributedText:(NSAttributedString *)attributedText
|
|
152
|
+
{
|
|
153
|
+
self.attributedStringValue = attributedText ?: [[NSAttributedString alloc] initWithString:@""];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
- (NSTextAlignment)textAlignment
|
|
157
|
+
{
|
|
158
|
+
return self.alignment;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
- (void)setTextAlignment:(NSTextAlignment)textAlignment
|
|
162
|
+
{
|
|
163
|
+
self.alignment = textAlignment;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The selection, which on AppKit lives in the field editor rather than the
|
|
168
|
+
* field. With no editor there is nothing selected, and UIKit reports nil for
|
|
169
|
+
* a field that is not being edited too.
|
|
170
|
+
*/
|
|
171
|
+
- (UITextRange *)selectedTextRange
|
|
172
|
+
{
|
|
173
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
174
|
+
if (editor == nil) {
|
|
175
|
+
return nil;
|
|
176
|
+
}
|
|
177
|
+
NSRange range = editor.selectedRange;
|
|
178
|
+
return [UITextRange rangeWithStart:[UITextPosition positionWithOffset:(NSInteger)range.location]
|
|
179
|
+
end:[UITextPosition positionWithOffset:(NSInteger)(range.location + range.length)]];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
- (void)setSelectedTextRange:(UITextRange *)selectedTextRange
|
|
183
|
+
{
|
|
184
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
185
|
+
if (editor == nil || selectedTextRange == nil) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
NSInteger start = MAX((NSInteger)0, selectedTextRange.start.offset);
|
|
189
|
+
NSInteger end = MAX(start, selectedTextRange.end.offset);
|
|
190
|
+
NSInteger length = (NSInteger)self.stringValue.length;
|
|
191
|
+
start = MIN(start, length);
|
|
192
|
+
end = MIN(end, length);
|
|
193
|
+
editor.selectedRange = NSMakeRange((NSUInteger)start, (NSUInteger)(end - start));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
- (NSDictionary<NSAttributedStringKey, id> *)typingAttributes
|
|
197
|
+
{
|
|
198
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
199
|
+
if (editor != nil) {
|
|
200
|
+
return editor.typingAttributes;
|
|
201
|
+
}
|
|
202
|
+
// Between edits there is no field editor to ask. UIKit's own default for a
|
|
203
|
+
// field that was never typed into is its default text attributes.
|
|
204
|
+
return _typingAttributes ?: self.defaultTextAttributes ?: @{};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
- (void)setTypingAttributes:(NSDictionary<NSAttributedStringKey, id> *)typingAttributes
|
|
208
|
+
{
|
|
209
|
+
_typingAttributes = [typingAttributes copy];
|
|
210
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
211
|
+
if (editor != nil) {
|
|
212
|
+
editor.typingAttributes = _typingAttributes ?: @{};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
85
216
|
- (UITextPosition *)endOfDocument
|
|
86
217
|
{
|
|
87
218
|
return [UITextPosition positionWithOffset:(NSInteger)self.stringValue.length];
|
|
@@ -223,6 +354,85 @@ UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
|
223
354
|
|
|
224
355
|
@implementation UITextView
|
|
225
356
|
|
|
357
|
+
@synthesize autocapitalizationType = _autocapitalizationType;
|
|
358
|
+
@synthesize autocorrectionType = _autocorrectionType;
|
|
359
|
+
@synthesize spellCheckingType = _spellCheckingType;
|
|
360
|
+
@synthesize keyboardAppearance = _keyboardAppearance;
|
|
361
|
+
@synthesize smartInsertDeleteType = _smartInsertDeleteType;
|
|
362
|
+
@synthesize smartQuotesType = _smartQuotesType;
|
|
363
|
+
@synthesize smartDashesType = _smartDashesType;
|
|
364
|
+
@synthesize secureTextEntry = _secureTextEntry;
|
|
365
|
+
@synthesize enablesReturnKeyAutomatically = _enablesReturnKeyAutomatically;
|
|
366
|
+
@synthesize textContentType = _textContentType;
|
|
367
|
+
@synthesize passwordRules = _passwordRules;
|
|
368
|
+
@synthesize inputView = _inputView;
|
|
369
|
+
@synthesize inputAccessoryView = _inputAccessoryView;
|
|
370
|
+
@synthesize dataDetectorTypes = _dataDetectorTypes;
|
|
371
|
+
@synthesize zoomScale = _zoomScale;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* UIKit's text view scrolls on its own. NSTextView is the document view inside
|
|
375
|
+
* an NSScrollView, so scrolling is a property of the enclosing view -- and
|
|
376
|
+
* there may not be one, when the text view is used unwrapped.
|
|
377
|
+
*/
|
|
378
|
+
- (BOOL)isScrollEnabled
|
|
379
|
+
{
|
|
380
|
+
NSScrollView *scrollView = self.enclosingScrollView;
|
|
381
|
+
return scrollView != nil ? (scrollView.hasVerticalScroller || scrollView.hasHorizontalScroller) : NO;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
- (void)setScrollEnabled:(BOOL)scrollEnabled
|
|
385
|
+
{
|
|
386
|
+
NSScrollView *scrollView = self.enclosingScrollView;
|
|
387
|
+
scrollView.hasVerticalScroller = scrollEnabled;
|
|
388
|
+
scrollView.hasHorizontalScroller = NO;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
- (CGPoint)contentOffset
|
|
392
|
+
{
|
|
393
|
+
NSScrollView *scrollView = self.enclosingScrollView;
|
|
394
|
+
return scrollView != nil ? scrollView.contentView.bounds.origin : CGPointZero;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
- (void)setContentOffset:(CGPoint)contentOffset
|
|
398
|
+
{
|
|
399
|
+
[self.enclosingScrollView.contentView scrollToPoint:contentOffset];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
- (UITextInputMode *)textInputMode
|
|
403
|
+
{
|
|
404
|
+
// No software keyboard on macOS; callers only null-check this.
|
|
405
|
+
return nil;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
- (UITextRange *)markedTextRange
|
|
409
|
+
{
|
|
410
|
+
if (!self.hasMarkedText) {
|
|
411
|
+
return nil;
|
|
412
|
+
}
|
|
413
|
+
NSRange range = self.markedRange;
|
|
414
|
+
return [UITextRange rangeWithStart:[UITextPosition positionWithOffset:(NSInteger)range.location]
|
|
415
|
+
end:[UITextPosition positionWithOffset:(NSInteger)(range.location + range.length)]];
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
- (UITextRange *)selectedTextRange
|
|
419
|
+
{
|
|
420
|
+
NSRange range = self.selectedRange;
|
|
421
|
+
return [UITextRange rangeWithStart:[UITextPosition positionWithOffset:(NSInteger)range.location]
|
|
422
|
+
end:[UITextPosition positionWithOffset:(NSInteger)(range.location + range.length)]];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
- (void)setSelectedTextRange:(UITextRange *)selectedTextRange
|
|
426
|
+
{
|
|
427
|
+
if (selectedTextRange == nil) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
NSInteger length = (NSInteger)self.string.length;
|
|
431
|
+
NSInteger start = MIN(MAX((NSInteger)0, selectedTextRange.start.offset), length);
|
|
432
|
+
NSInteger end = MIN(MAX(start, selectedTextRange.end.offset), length);
|
|
433
|
+
self.selectedRange = NSMakeRange((NSUInteger)start, (NSUInteger)(end - start));
|
|
434
|
+
}
|
|
435
|
+
|
|
226
436
|
UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
227
437
|
|
|
228
438
|
- (UITextPosition *)endOfDocument
|
package/package.json
CHANGED
|
@@ -489,7 +489,11 @@ export default {
|
|
|
489
489
|
nativeEventEmitter = new NativeEventEmitter(
|
|
490
490
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
491
491
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
492
|
-
|
|
492
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
493
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
494
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
495
|
+
? null
|
|
496
|
+
: NativeAnimatedModule,
|
|
493
497
|
);
|
|
494
498
|
}
|
|
495
499
|
return nativeEventEmitter;
|
|
@@ -70,7 +70,12 @@ const ExceptionsManager = {
|
|
|
70
70
|
NativeModule.reportSoftException(message, stack, exceptionId);
|
|
71
71
|
},
|
|
72
72
|
dismissRedbox(): void {
|
|
73
|
-
|
|
73
|
+
// [macOS] RedBox is dismissed natively on Apple platforms.
|
|
74
|
+
if (
|
|
75
|
+
Platform.OS !== 'ios' &&
|
|
76
|
+
Platform.OS !== 'macos' &&
|
|
77
|
+
NativeModule.dismissRedbox
|
|
78
|
+
) {
|
|
74
79
|
// TODO(T53311281): This is a noop on iOS now. Implement it.
|
|
75
80
|
NativeModule.dismissRedbox();
|
|
76
81
|
}
|