not-react-native-macos 0.87.1-rc.5 → 0.87.1-rc.6

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.
@@ -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.
@@ -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,
@@ -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
- static jsi::Value mouseEventPayload(jsi::Runtime &runtime, const HostPlatformViewEventEmitter::MouseEvent &event)
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
- * A mouse crossing into or out of the view. There is no touch equivalent, so
24
- * these have no counterpart in BaseViewEventEmitter.
24
+ * Named here as well so `HostPlatformViewEventEmitter::MouseEvent` keeps
25
+ * working; the type itself is free-standing, in MouseEvent.h.
25
26
  */
26
- struct MouseEvent {
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<8> bits{};
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<8>::reference operator[](Offset offset)
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "not-react-native-macos",
3
- "version": "0.87.1-rc.5",
3
+ "version": "0.87.1-rc.6",
4
4
  "description": "A framework for building native apps using React",
5
5
  "license": "MIT",
6
6
  "repository": {