Skip to content

Commit bb4d015

Browse files
committed
Properly implement DataTransfer object that matches paper and is closer to web
1 parent 9702a1a commit bb4d015

File tree

13 files changed

+144
-142
lines changed

13 files changed

+144
-142
lines changed

packages/react-native/Libraries/Components/TextInput/TextInput.flow.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import type {HostInstance} from '../../../src/private/types/HostInstance';
1212
import type {
13+
DataTransfer,
1314
GestureResponderEvent,
1415
NativeSyntheticEvent,
1516
ScrollEvent,
@@ -108,20 +109,7 @@ export type SettingChangeEvent = NativeSyntheticEvent<
108109

109110
export type PasteEvent = NativeSyntheticEvent<
110111
$ReadOnly<{|
111-
dataTransfer: {|
112-
files: $ReadOnlyArray<{|
113-
height: number,
114-
size: number,
115-
type: string,
116-
uri: string,
117-
width: number,
118-
|}>,
119-
items: $ReadOnlyArray<{|
120-
kind: string,
121-
type: string,
122-
|}>,
123-
types: $ReadOnlyArray<string>,
124-
|},
112+
dataTransfer: DataTransfer,
125113
|}>,
126114
>;
127115

packages/react-native/Libraries/Components/TextInput/TextInput.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import type {HostInstance} from '../../../src/private/types/HostInstance';
1212
import type {____TextStyle_Internal as TextStyleInternal} from '../../StyleSheet/StyleSheetTypes';
1313
import type {
14+
DataTransfer,
1415
GestureResponderEvent,
1516
NativeSyntheticEvent,
1617
ScrollEvent,
@@ -143,20 +144,7 @@ export type SettingChangeEvent = NativeSyntheticEvent<
143144

144145
export type PasteEvent = NativeSyntheticEvent<
145146
$ReadOnly<{|
146-
dataTransfer: {|
147-
files: $ReadOnlyArray<{|
148-
height: number,
149-
size: number,
150-
type: string,
151-
uri: string,
152-
width: number,
153-
|}>,
154-
items: $ReadOnlyArray<{|
155-
kind: string,
156-
type: string,
157-
|}>,
158-
types: $ReadOnlyArray<string>,
159-
|},
147+
dataTransfer: DataTransfer,
160148
|}>,
161149
>;
162150

packages/react-native/Libraries/Types/CoreEventTypes.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -333,19 +333,24 @@ export type MouseEvent = NativeSyntheticEvent<
333333
>;
334334

335335
// [macOS
336-
export type DataTransferItem = $ReadOnly<{
336+
export type DataTransferFile = $ReadOnly<{
337337
name: string,
338-
kind: string,
339-
type: string,
338+
type: ?string,
340339
uri: string,
341340
size?: number,
342341
width?: number,
343342
height?: number,
344343
}>;
345344

345+
export type DataTransferItem = $ReadOnly<{
346+
kind: string,
347+
type: ?string,
348+
}>;
349+
346350
export type DataTransfer = $ReadOnly<{
347-
files: $ReadOnlyArray<DataTransferItem>,
348-
types: $ReadOnlyArray<string>,
351+
files: $ReadOnlyArray<DataTransferFile>,
352+
items: $ReadOnlyArray<DataTransferItem>,
353+
types: $ReadOnlyArray<?string>,
349354
}>;
350355

351356
export type DragEvent = NativeSyntheticEvent<

packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -616,10 +616,8 @@ - (BOOL)textInputShouldHandlePaste:(nonnull id<RCTBackedTextInputViewProtocol>)s
616616
// If there's a fileType that is of interest, notify JS. Also blocks notifying JS if it's a text paste
617617
if (_eventEmitter && fileType != nil && [pastedTypes containsObject:fileType]) {
618618
auto const &textInputEventEmitter = *std::static_pointer_cast<TextInputEventEmitter const>(_eventEmitter);
619-
std::vector<DataTransferItem> dataTransferItems{};
620-
[self buildDataTransferItems:dataTransferItems forPasteboard:pasteboard];
621-
622-
textInputEventEmitter.onPaste({.dataTransferItems = dataTransferItems});
619+
DataTransfer dataTransfer = [self dataTransferForPasteboard:pasteboard];
620+
textInputEventEmitter.onPaste({.dataTransfer = std::move(dataTransfer)});
623621
}
624622

625623
// Only allow pasting text.

packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN
8585

8686
#if TARGET_OS_OSX // [macOS
8787
- (BOOL)handleKeyboardEvent:(NSEvent *)event;
88-
- (void)buildDataTransferItems:(std::vector<facebook::react::DataTransferItem> &)dataTransferItems forPasteboard:(NSPasteboard *)pasteboard;
88+
- (facebook::react::DataTransfer)dataTransferForPasteboard:(NSPasteboard *)pasteboard;
8989
#endif // macOS]
9090

9191
/*

packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,7 +1714,12 @@ - (void)keyUp:(NSEvent *)event {
17141714
Drop,
17151715
};
17161716

1717-
- (void)buildDataTransferItems:(std::vector<DataTransferItem> &)dataTransferItems forPasteboard:(NSPasteboard *)pasteboard {
1717+
- (DataTransfer)dataTransferForPasteboard:(NSPasteboard *)pasteboard {
1718+
DataTransfer dataTransfer{};
1719+
auto &files = dataTransfer.files;
1720+
auto &items = dataTransfer.items;
1721+
auto &types = dataTransfer.types;
1722+
17181723
NSArray *fileNames = [pasteboard propertyListForType:NSFilenamesPboardType] ?: @[];
17191724
for (NSString *file in fileNames) {
17201725
NSURL *fileURL = [NSURL fileURLWithPath:file];
@@ -1739,25 +1744,31 @@ - (void)buildDataTransferItems:(std::vector<DataTransferItem> &)dataTransferItem
17391744
forKey:NSURLFileSizeKey
17401745
error:&fileSizeError];
17411746

1742-
DataTransferItem transferItem = {
1747+
std::string typeString = MIMETypeString != nil ? [MIMETypeString UTF8String] : "";
1748+
1749+
DataTransferFile fileEntry = {
17431750
.name = fileURL.lastPathComponent ? fileURL.lastPathComponent.UTF8String : "",
1744-
.kind = "file",
1745-
.type = MIMETypeString ? MIMETypeString.UTF8String : "",
1751+
.type = typeString,
17461752
.uri = fileURL.path ? fileURL.path.UTF8String : "",
17471753
};
1748-
1754+
17491755
if (success) {
1750-
transferItem.size = fileSizeValue.intValue;
1756+
fileEntry.size = fileSizeValue.intValue;
17511757
}
17521758

1753-
if ([MIMETypeString hasPrefix:@"image/"]) {
1759+
if (MIMETypeString != nil && [MIMETypeString hasPrefix:@"image/"]) {
17541760
NSImage *image = [[NSImage alloc] initWithContentsOfURL:fileURL];
17551761
CGImageRef cgImage = [image CGImageForProposedRect:nil context:nil hints:nil];
1756-
transferItem.width = static_cast<int>(CGImageGetWidth(cgImage));
1757-
transferItem.height = static_cast<int>(CGImageGetHeight(cgImage));
1762+
fileEntry.width = static_cast<int>(CGImageGetWidth(cgImage));
1763+
fileEntry.height = static_cast<int>(CGImageGetHeight(cgImage));
17581764
}
1759-
1760-
dataTransferItems.push_back(transferItem);
1765+
1766+
files.push_back(fileEntry);
1767+
items.push_back({
1768+
.kind = "file",
1769+
.type = typeString,
1770+
});
1771+
types.push_back(typeString);
17611772
}
17621773
}
17631774

@@ -1769,18 +1780,26 @@ - (void)buildDataTransferItems:(std::vector<DataTransferItem> &)dataTransferItem
17691780
CGImageRef cgImage = [image CGImageForProposedRect:nil context:nil hints:nil];
17701781

17711782
NSString *dataURLString = RCTDataURL(MIMETypeString, imageData).absoluteString;
1783+
std::string typeString = MIMETypeString != nil ? [MIMETypeString UTF8String] : "";
17721784

1773-
DataTransferItem transferItem = {
1774-
.kind = "image",
1775-
.type = MIMETypeString ? MIMETypeString.UTF8String : "",
1785+
DataTransferFile fileEntry = {
1786+
.name = "",
1787+
.type = typeString,
17761788
.uri = dataURLString ? dataURLString.UTF8String : "",
1777-
.size = static_cast<int>(imageData.length),
1778-
.width = static_cast<int>(CGImageGetWidth(cgImage)),
1779-
.height = static_cast<int>(CGImageGetHeight(cgImage)),
17801789
};
17811790

1782-
dataTransferItems.push_back(transferItem);
1791+
fileEntry.size = static_cast<int>(imageData.length);
1792+
fileEntry.width = static_cast<int>(CGImageGetWidth(cgImage));
1793+
fileEntry.height = static_cast<int>(CGImageGetHeight(cgImage));
1794+
1795+
files.push_back(fileEntry);
1796+
items.push_back({
1797+
.kind = "image",
1798+
.type = typeString,
1799+
});
1800+
types.push_back(typeString);
17831801
}
1802+
return dataTransfer;
17841803
}
17851804

17861805
- (void)emitDragEvent:(DragEventType)eventType draggingInfo:(id<NSDraggingInfo>)sender {
@@ -1791,8 +1810,7 @@ - (void)emitDragEvent:(DragEventType)eventType draggingInfo:(id<NSDraggingInfo>)
17911810
NSPoint locationInWindow = sender.draggingLocation;
17921811
NSPasteboard *pasteboard = sender.draggingPasteboard;
17931812

1794-
std::vector<DataTransferItem> dataTransferItems{};
1795-
[self buildDataTransferItems:dataTransferItems forPasteboard:pasteboard];
1813+
DataTransfer dataTransfer = [self dataTransferForPasteboard:pasteboard];
17961814

17971815
NSPoint locationInView = [self convertPoint:locationInWindow fromView:nil];
17981816
NSEventModifierFlags modifierFlags = self.window.currentEvent.modifierFlags;
@@ -1808,7 +1826,7 @@ - (void)emitDragEvent:(DragEventType)eventType draggingInfo:(id<NSDraggingInfo>)
18081826
.shiftKey = static_cast<bool>(modifierFlags & NSEventModifierFlagShift),
18091827
.metaKey = static_cast<bool>(modifierFlags & NSEventModifierFlagCommand),
18101828
},
1811-
.dataTransferItems = dataTransferItems,
1829+
.dataTransfer = dataTransfer,
18121830
};
18131831

18141832
switch (eventType) {

packages/react-native/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ static jsi::Value pasteMetricsPayload(
137137
jsi::Runtime& runtime,
138138
const TextInputEventEmitter::PasteMetrics& pasteMetrics) {
139139
auto payload = jsi::Object(runtime);
140-
auto dataTransferObject = HostPlatformViewEventEmitter::dataTransferPayload(runtime, pasteMetrics.dataTransferItems);
140+
auto dataTransferObject = HostPlatformViewEventEmitter::dataTransferPayload(
141+
runtime,
142+
pasteMetrics.dataTransfer);
141143
payload.setProperty(runtime, "dataTransfer", dataTransferObject);
142144
return payload;
143145
};

packages/react-native/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
#include <react/renderer/attributedstring/AttributedString.h>
1111
#include <react/renderer/components/view/ViewEventEmitter.h>
12+
#if TARGET_OS_OSX // [macOS
13+
#include <react/renderer/components/view/MouseEvent.h>
14+
#endif // macOS]
1215

1316
namespace facebook::react {
1417

@@ -42,7 +45,7 @@ class TextInputEventEmitter : public ViewEventEmitter {
4245

4346
#if TARGET_OS_OSX // [macOS
4447
struct PasteMetrics {
45-
std::vector<DataTransferItem> dataTransferItems;
48+
DataTransfer dataTransfer;
4649
};
4750
#endif // macOS]
4851

packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.cpp

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -82,34 +82,73 @@ void HostPlatformViewEventEmitter::onMouseLeave(const MouseEvent& mouseEvent) co
8282

8383
jsi::Value HostPlatformViewEventEmitter::dataTransferPayload(
8484
jsi::Runtime& runtime,
85-
const std::vector<DataTransferItem>& dataTransferItems) {
86-
auto filesArray = jsi::Array(runtime, dataTransferItems.size());
87-
auto itemsArray = jsi::Array(runtime, dataTransferItems.size());
88-
auto typesArray = jsi::Array(runtime, dataTransferItems.size());
89-
int i = 0;
90-
for (const auto& transferItem : dataTransferItems) {
85+
DataTransfer const& dataTransfer) {
86+
const auto& files = dataTransfer.files;
87+
const auto& items = dataTransfer.items;
88+
const auto& types = dataTransfer.types;
89+
auto filesArray = jsi::Array(runtime, files.size());
90+
auto itemsArray = jsi::Array(runtime, items.size());
91+
auto maxEntries = std::max(files.size(), std::max(items.size(), types.size()));
92+
auto typesArray = jsi::Array(runtime, maxEntries);
93+
94+
for (size_t fileIndex = 0; fileIndex < files.size(); ++fileIndex) {
95+
const auto& fileItem = files[fileIndex];
9196
auto fileObject = jsi::Object(runtime);
92-
fileObject.setProperty(runtime, "name", transferItem.name);
93-
fileObject.setProperty(runtime, "type", transferItem.type);
94-
fileObject.setProperty(runtime, "uri", transferItem.uri);
95-
if (transferItem.size.has_value()) {
96-
fileObject.setProperty(runtime, "size", *transferItem.size);
97+
fileObject.setProperty(runtime, "name", fileItem.name);
98+
if (!fileItem.type.empty()) {
99+
fileObject.setProperty(runtime, "type", fileItem.type);
100+
} else {
101+
fileObject.setProperty(runtime, "type", jsi::Value::null());
97102
}
98-
if (transferItem.width.has_value()) {
99-
fileObject.setProperty(runtime, "width", *transferItem.width);
103+
fileObject.setProperty(runtime, "uri", fileItem.uri);
104+
if (fileItem.size.has_value()) {
105+
fileObject.setProperty(runtime, "size", *fileItem.size);
100106
}
101-
if (transferItem.height.has_value()) {
102-
fileObject.setProperty(runtime, "height", *transferItem.height);
107+
if (fileItem.width.has_value()) {
108+
fileObject.setProperty(runtime, "width", *fileItem.width);
103109
}
104-
filesArray.setValueAtIndex(runtime, i, fileObject);
110+
if (fileItem.height.has_value()) {
111+
fileObject.setProperty(runtime, "height", *fileItem.height);
112+
}
113+
filesArray.setValueAtIndex(runtime, static_cast<int>(fileIndex), fileObject);
114+
}
105115

116+
for (size_t itemIndex = 0; itemIndex < items.size(); ++itemIndex) {
117+
const auto& item = items[itemIndex];
106118
auto itemObject = jsi::Object(runtime);
107-
itemObject.setProperty(runtime, "kind", transferItem.kind);
108-
itemObject.setProperty(runtime, "type", transferItem.type);
109-
itemsArray.setValueAtIndex(runtime, i, itemObject);
119+
itemObject.setProperty(runtime, "kind", item.kind);
120+
if (!item.type.empty()) {
121+
itemObject.setProperty(runtime, "type", item.type);
122+
} else {
123+
itemObject.setProperty(runtime, "type", jsi::Value::null());
124+
}
125+
itemsArray.setValueAtIndex(runtime, static_cast<int>(itemIndex), itemObject);
126+
}
110127

111-
typesArray.setValueAtIndex(runtime, i, transferItem.type);
112-
i++;
128+
for (size_t typeIndex = 0; typeIndex < maxEntries; ++typeIndex) {
129+
if (typeIndex < types.size()) {
130+
const auto& typeEntry = types[typeIndex];
131+
if (!typeEntry.empty()) {
132+
typesArray.setValueAtIndex(
133+
runtime,
134+
static_cast<int>(typeIndex),
135+
jsi::String::createFromUtf8(runtime, typeEntry));
136+
continue;
137+
}
138+
}
139+
140+
const std::string* fallbackType = nullptr;
141+
if (typeIndex < items.size() && !items[typeIndex].type.empty()) {
142+
fallbackType = &items[typeIndex].type;
143+
} else if (typeIndex < files.size() && !files[typeIndex].type.empty()) {
144+
fallbackType = &files[typeIndex].type;
145+
}
146+
147+
if (fallbackType != nullptr) {
148+
typesArray.setValueAtIndex(runtime, static_cast<int>(typeIndex), jsi::String::createFromUtf8(runtime, *fallbackType));
149+
} else {
150+
typesArray.setValueAtIndex(runtime, static_cast<int>(typeIndex), jsi::Value::null());
151+
}
113152
}
114153

115154
auto dataTransferObject = jsi::Object(runtime);
@@ -124,7 +163,9 @@ static jsi::Value dragEventPayload(
124163
jsi::Runtime& runtime,
125164
const DragEvent& event) {
126165
auto payload = mouseEventPayload(runtime, event);
127-
auto dataTransferObject = HostPlatformViewEventEmitter::dataTransferPayload(runtime, event.dataTransferItems);
166+
auto dataTransferObject = HostPlatformViewEventEmitter::dataTransferPayload(
167+
runtime,
168+
event.dataTransfer);
128169
payload.setProperty(runtime, "dataTransfer", dataTransferObject);
129170
return payload;
130171
}

packages/react-native/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ class HostPlatformViewEventEmitter : public BaseViewEventEmitter {
4040
void onDragLeave(DragEvent const& dragEvent) const;
4141
void onDrop(DragEvent const& dragEvent) const;
4242

43-
static jsi::Value dataTransferPayload(jsi::Runtime& runtime, std::vector<DataTransferItem> const& dataTransferItems);
43+
static jsi::Value dataTransferPayload(
44+
jsi::Runtime& runtime,
45+
DataTransfer const& dataTransfer);
4446

4547
};
4648

0 commit comments

Comments
 (0)