Skip to content

Commit 76c5a37

Browse files
christophpurrerShawn Dempsey
authored andcommitted
Add option to enforce pasting plain text (microsoft#1429)
There are use cases in which we want to ignore the base class [NSTextView readablePasteboardTypes] return values, which mostly, include RTF and formatted URL types We want to ignore them in favor of plain text (NSPasteboardTypeString). This change allows to opt into a custom list of supported paste types. This is a follow-up to microsoft#1350 # Conflicts: # Libraries/Components/TextInput/TextInput.js
1 parent cc267f6 commit 76c5a37

File tree

9 files changed

+99
-19
lines changed

9 files changed

+99
-19
lines changed

Libraries/Components/TextInput/TextInput.js

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,51 @@ type IOSProps = $ReadOnly<{|
345345
textContentType?: ?TextContentType,
346346
|}>;
347347

348+
// [TODO(macOS GH#774)
349+
export type SubmitKeyEvent = $ReadOnly<{|
350+
key: string,
351+
altKey?: ?boolean,
352+
ctrlKey?: ?boolean,
353+
metaKey?: ?boolean,
354+
shiftKey?: ?boolean,
355+
functionKey?: ?boolean,
356+
|}>;
357+
358+
type MacOSProps = $ReadOnly<{|
359+
/**
360+
* If `true`, clears the text field synchronously before `onSubmitEditing` is emitted.
361+
* @platform macos
362+
*/
363+
clearTextOnSubmit?: ?boolean,
364+
365+
/**
366+
* Fired when a supported element is pasted
367+
*
368+
* @platform macos
369+
*/
370+
onPaste?: (event: PasteEvent) => void,
371+
372+
/**
373+
* Enables Paste support for certain types of pasted types
374+
*
375+
* Possible values for `pastedTypes` are:
376+
*
377+
* - `'fileUrl'`
378+
* - `'image'`
379+
* - `'string'`
380+
*
381+
* @platform macos
382+
*/
383+
pastedTypes?: PastedTypesType,
384+
385+
/**
386+
* Configures keys that can be used to submit editing for the TextInput. Defaults to 'Enter' key.
387+
* @platform macos
388+
*/
389+
submitKeyEvents?: ?$ReadOnlyArray<SubmitKeyEvent>,
390+
|}>;
391+
// ]TODO(macOS GH#774)
392+
348393
type AndroidProps = $ReadOnly<{|
349394
/**
350395
* Specifies autocomplete hints for the system, so it can provide autofill. On Android, the system will always attempt to offer autofill by using heuristics to identify the type of content.
@@ -504,9 +549,13 @@ type AndroidProps = $ReadOnly<{|
504549
underlineColorAndroid?: ?ColorValue,
505550
|}>;
506551

552+
export type PasteType = 'fileUrl' | 'image' | 'string'; // TODO(macOS GH#774)
553+
export type PastedTypesType = PasteType | $ReadOnlyArray<PasteType>; // TODO(macOS GH#774)
554+
507555
export type Props = $ReadOnly<{|
508556
...$Diff<ViewProps, $ReadOnly<{|style: ?ViewStyleProp|}>>,
509557
...IOSProps,
558+
...MacOSProps, // TODO(macOS GH#774)
510559
...AndroidProps,
511560

512561
/**
@@ -720,13 +769,6 @@ export type Props = $ReadOnly<{|
720769
*/
721770
onPressOut?: ?(event: PressEvent) => mixed,
722771

723-
/**
724-
* Fired when a supported element is pasted
725-
*
726-
* @platform macos
727-
*/
728-
onPaste?: (event: PasteEvent) => void, // TODO(macOS GH#774)
729-
730772
/**
731773
* Callback that is called when the text input selection is changed.
732774
* This will be called with

Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ NS_ASSUME_NONNULL_BEGIN
1111

1212
@interface RCTMultilineTextInputView : RCTBaseTextInputView
1313

14+
#if TARGET_OS_OSX // [TODO(macOS GH#774)
15+
- (void)setReadablePasteBoardTypes:(NSArray<NSPasteboardType> *)readablePasteboardTypes;
16+
#endif // ]TODO(macOS GH#774)
1417
@end
1518

1619
NS_ASSUME_NONNULL_END

Libraries/Text/TextInput/Multiline/RCTMultilineTextInputView.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ - (void)setEnableFocusRing:(BOOL)enableFocusRing {
9999
}
100100
}
101101

102+
- (void)setReadablePasteBoardTypes:(NSArray<NSPasteboardType> *)readablePasteboardTypes {
103+
[_backedTextInputView setReadablePasteBoardTypes:readablePasteboardTypes];
104+
}
102105
#endif // ]TODO(macOS GH#774)
103106

104107
#pragma mark - UIScrollViewDelegate

Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#import <React/RCTMultilineTextInputViewManager.h>
99
#import <React/RCTMultilineTextInputView.h>
10+
#import <React/RCTUITextView.h> // TODO(macOS GH#774)
1011

1112
@implementation RCTMultilineTextInputViewManager
1213

@@ -22,4 +23,14 @@ - (RCTUIView *)view // TODO(macOS ISS#3536887)
2223
RCT_REMAP_NOT_OSX_VIEW_PROPERTY(dataDetectorTypes, backedTextInputView.dataDetectorTypes, UIDataDetectorTypes) // TODO(macOS GH#774)
2324
RCT_REMAP_OSX_VIEW_PROPERTY(dataDetectorTypes, backedTextInputView.enabledTextCheckingTypes, NSTextCheckingTypes) // TODO(macOS GH#774)
2425

26+
#if TARGET_OS_OSX // [TODO(macOS GH#774)
27+
RCT_CUSTOM_VIEW_PROPERTY(pastedTypes, NSArray<NSPasteboardType>*, RCTUITextView)
28+
{
29+
NSArray<NSPasteboardType> *types = json ? [RCTConvert NSPasteboardTypeArray:json] : nil;
30+
if ([view respondsToSelector:@selector(setReadablePasteBoardTypes:)]) {
31+
[view setReadablePasteBoardTypes: types];
32+
}
33+
}
34+
#endif // ]TODO(macOS GH#774)
35+
2536
@end

Libraries/Text/TextInput/Multiline/RCTUITextView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ NS_ASSUME_NONNULL_BEGIN
5353
@property (nonatomic, assign) NSTextAlignment textAlignment;
5454
@property (nonatomic, copy, nullable) NSAttributedString *attributedText;
5555
- (NSSize)sizeThatFits:(NSSize)size;
56+
- (void)setReadablePasteBoardTypes:(NSArray<NSPasteboardType> *)readablePasteboardTypes;
5657
#endif // ]TODO(macOS GH#774)
5758

5859
@end

Libraries/Text/TextInput/Multiline/RCTUITextView.m

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ @implementation RCTUITextView
2121
#endif // TODO(macOS GH#774)
2222
RCTBackedTextViewDelegateAdapter *_textInputDelegateAdapter;
2323
NSDictionary<NSAttributedStringKey, id> *_defaultTextAttributes;
24+
#if TARGET_OS_OSX // [TODO(macOS GH#774)
25+
NSArray<NSPasteboardType> *_readablePasteboardTypes;
26+
#endif // TODO(macOS GH#774)
2427
}
2528

2629
static UIFont *defaultPlaceholderFont()
@@ -161,6 +164,11 @@ - (void)setText:(NSString *)text
161164
self.string = text;
162165
}
163166

167+
- (void)setReadablePasteBoardTypes:(NSArray<NSPasteboardType> *)readablePasteboardTypes
168+
{
169+
_readablePasteboardTypes = readablePasteboardTypes;
170+
}
171+
164172
- (void)setTypingAttributes:(__unused NSDictionary *)typingAttributes
165173
{
166174
// Prevent NSTextView from changing its own typing attributes out from under us.
@@ -320,9 +328,7 @@ - (BOOL)performDragOperation:(id<NSDraggingInfo>)draggingInfo
320328

321329
- (NSArray *)readablePasteboardTypes
322330
{
323-
NSArray *types = [super readablePasteboardTypes];
324-
// TODO: Optionally support files/images with a prop
325-
return [types arrayByAddingObjectsFromArray:@[NSFilenamesPboardType, NSPasteboardTypePNG, NSPasteboardTypeTIFF]];
331+
return _readablePasteboardTypes ? _readablePasteboardTypes : [super readablePasteboardTypes];
326332
}
327333

328334
#endif // ]TODO(macOS GH#774)

Libraries/Text/TextInput/RCTBaseTextInputView.m

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#import <React/RCTInputAccessoryViewContent.h>
1919
#import <React/RCTTextAttributes.h>
2020
#import <React/RCTTextSelection.h>
21+
#import <React/RCTUITextView.h> // TODO(macOS GH#774)
2122
#import "../RCTTextUIKit.h" // TODO(macOS GH#774)
2223

2324
@implementation RCTBaseTextInputView {
@@ -612,10 +613,11 @@ - (BOOL)textInputShouldHandleKeyEvent:(NSEvent *)event {
612613
- (BOOL)textInputShouldHandlePaste:(__unused id)sender
613614
{
614615
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
615-
NSPasteboardType fileType = [pasteboard availableTypeFromArray:@[NSFilenamesPboardType, NSPasteboardTypePNG, NSPasteboardTypeTIFF]];
616-
616+
NSPasteboardType fileType = [pasteboard availableTypeFromArray:@[NSPasteboardTypeFileURL, NSPasteboardTypePNG, NSPasteboardTypeTIFF]];
617+
NSArray<NSPasteboardType>* pastedTypes = ((RCTUITextView*) self.backedTextInputView).readablePasteboardTypes;
618+
617619
// If there's a fileType that is of interest, notify JS. Also blocks notifying JS if it's a text paste
618-
if (_onPaste && fileType != nil) {
620+
if (_onPaste && fileType != nil && [pastedTypes containsObject:fileType]) {
619621
_onPaste([self dataTransferInfoFromPasteboard:pasteboard]);
620622
}
621623

React/Base/RCTConvert.m

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,11 +1233,12 @@ + (NSArray *)CGColorArray:(id)json
12331233
}
12341234

12351235
if ([type isEqualToString:@"fileUrl"]) {
1236-
return @[NSFilenamesPboardType];
1236+
return @[NSPasteboardTypeFileURL];
12371237
} else if ([type isEqualToString:@"image"]) {
12381238
return @[NSPasteboardTypePNG, NSPasteboardTypeTIFF];
1239+
} else if ([type isEqualToString:@"string"]) {
1240+
return @[NSPasteboardTypeString];
12391241
}
1240-
12411242
return @[];
12421243
}
12431244

@@ -1246,11 +1247,11 @@ + (NSArray *)CGColorArray:(id)json
12461247
if ([json isKindOfClass:[NSString class]]) {
12471248
return [RCTConvert NSPasteboardType:json];
12481249
} else if ([json isKindOfClass:[NSArray class]]) {
1249-
NSMutableArray *mutablePastboardTypes = [NSMutableArray new];
1250+
NSMutableArray *mutablePasteboardTypes = [NSMutableArray new];
12501251
for (NSString *type in json) {
1251-
[mutablePastboardTypes addObjectsFromArray:[RCTConvert NSPasteboardType:type]];
1252-
return mutablePastboardTypes.copy;
1252+
[mutablePasteboardTypes addObjectsFromArray:[RCTConvert NSPasteboardType:type]];
12531253
}
1254+
return mutablePasteboardTypes.copy;
12541255
}
12551256
return @[];
12561257
}

packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,18 @@ function OnPaste(): React.Node {
422422
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
423423
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
424424
}}
425-
placeholder="MULTI LINE with onPaste() for PNG and TIFF images"
425+
pastedTypes={['string']}
426+
placeholder="MULTI LINE with onPaste() text from clipboard"
427+
/>
428+
<TextInput
429+
multiline={true}
430+
style={styles.multiline}
431+
onPaste={(e: PasteEvent) => {
432+
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
433+
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
434+
}}
435+
pastedTypes={['fileUrl', 'image', 'string']}
436+
placeholder="MULTI LINE with onPaste() for PNG/TIFF images from clipboard or fileUrl (via Finder) and text from clipboard"
426437
/>
427438
<Text style={{height: 30}}>{log.join('\n')}</Text>
428439
<Image

0 commit comments

Comments
 (0)