Skip to content

Commit

Permalink
Add option to enforce pasting plain text
Browse files Browse the repository at this point in the history
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 #1350
  • Loading branch information
christophpurrer committed Sep 21, 2022
1 parent 377063c commit df8cfbd
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 20 deletions.
32 changes: 24 additions & 8 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,26 @@ type MacOSProps = $ReadOnly<{|
*/
clearTextOnSubmit?: ?boolean,

/**
* Fired when a supported element is pasted
*
* @platform macos
*/
onPaste?: (event: PasteEvent) => void,

/**
* Enables Paste support for certain types of pasted types
*
* Possible values for `pastedTypes` are:
*
* - `'fileUrl'`
* - `'image'`
* - `'string'`
*
* @platform macos
*/
pastedTypes?: PastedTypesType,

/**
* Configures keys that can be used to submit editing for the TextInput. Defaults to 'Enter' key.
* @platform macos
Expand Down Expand Up @@ -537,10 +557,13 @@ type AndroidProps = $ReadOnly<{|
underlineColorAndroid?: ?ColorValue,
|}>;

export type PasteType = 'fileUrl' | 'image' | 'string'; // TODO(macOS GH#774)
export type PastedTypesType = PasteType | $ReadOnlyArray<PasteType>; // TODO(macOS GH#774)

export type Props = $ReadOnly<{|
...$Diff<ViewProps, $ReadOnly<{|style: ?ViewStyleProp|}>>,
...IOSProps,
...MacOSProps,
...MacOSProps, // TODO(macOS GH#774)
...AndroidProps,

/**
Expand Down Expand Up @@ -786,13 +809,6 @@ export type Props = $ReadOnly<{|
*/
onPressOut?: ?(event: PressEvent) => mixed,

/**
* Fired when a supported element is pasted
*
* @platform macos
*/
onPaste?: (event: PasteEvent) => void, // TODO(macOS GH#774)

/**
* Callback that is called when the text input selection is changed.
* This will be called with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ NS_ASSUME_NONNULL_BEGIN

@interface RCTMultilineTextInputView : RCTBaseTextInputView

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

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ - (void)setEnableFocusRing:(BOOL)enableFocusRing {
}
}

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

#pragma mark - UIScrollViewDelegate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#import <React/RCTMultilineTextInputViewManager.h>
#import <React/RCTMultilineTextInputView.h>
#import <React/RCTUITextView.h> // TODO(macOS GH#774)

@implementation RCTMultilineTextInputViewManager

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

#if TARGET_OS_OSX // [TODO(macOS GH#774)
RCT_CUSTOM_VIEW_PROPERTY(pastedTypes, NSArray<NSPasteboardType>*, RCTUITextView)
{
NSArray<NSPasteboardType> *types = json ? [RCTConvert NSPasteboardTypeArray:json] : nil;
if ([view respondsToSelector:@selector(setReadablePasteBoardTypes:)]) {
[view setReadablePasteBoardTypes: types];
}
}
#endif // ]TODO(macOS GH#774)

@end
1 change: 1 addition & 0 deletions Libraries/Text/TextInput/Multiline/RCTUITextView.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) NSTextAlignment textAlignment;
@property (nonatomic, copy, nullable) NSAttributedString *attributedText;
- (NSSize)sizeThatFits:(NSSize)size;
- (void)setReadablePasteBoardTypes:(NSArray<NSPasteboardType> *)readablePasteboardTypes;
#endif // ]TODO(macOS GH#774)

@end
Expand Down
12 changes: 9 additions & 3 deletions Libraries/Text/TextInput/Multiline/RCTUITextView.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ @implementation RCTUITextView
#endif // TODO(macOS GH#774)
RCTBackedTextViewDelegateAdapter *_textInputDelegateAdapter;
NSDictionary<NSAttributedStringKey, id> *_defaultTextAttributes;
#if TARGET_OS_OSX // [TODO(macOS GH#774)
NSArray<NSPasteboardType> *_readablePasteboardTypes;
#endif // TODO(macOS GH#774)
}

static UIFont *defaultPlaceholderFont()
Expand Down Expand Up @@ -177,6 +180,11 @@ - (void)setText:(NSString *)text
self.string = text;
}

- (void)setReadablePasteBoardTypes:(NSArray<NSPasteboardType> *)readablePasteboardTypes
{
_readablePasteboardTypes = readablePasteboardTypes;
}

- (void)setTypingAttributes:(__unused NSDictionary *)typingAttributes
{
// Prevent NSTextView from changing its own typing attributes out from under us.
Expand Down Expand Up @@ -344,9 +352,7 @@ - (BOOL)performDragOperation:(id<NSDraggingInfo>)draggingInfo
}
- (NSArray *)readablePasteboardTypes
{
NSArray *types = [super readablePasteboardTypes];
// TODO: Optionally support files/images with a prop
return [types arrayByAddingObjectsFromArray:@[NSFilenamesPboardType, NSPasteboardTypePNG, NSPasteboardTypeTIFF]];
return _readablePasteboardTypes ? _readablePasteboardTypes : [super readablePasteboardTypes];
}

#endif // ]TODO(macOS GH#774)
Expand Down
8 changes: 5 additions & 3 deletions Libraries/Text/TextInput/RCTBaseTextInputView.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#import <React/RCTInputAccessoryViewContent.h>
#import <React/RCTTextAttributes.h>
#import <React/RCTTextSelection.h>
#import <React/RCTUITextView.h> // TODO(macOS GH#774)
#import "../RCTTextUIKit.h" // TODO(macOS GH#774)

@implementation RCTBaseTextInputView {
Expand Down Expand Up @@ -680,10 +681,11 @@ - (BOOL)textInputShouldHandleKeyEvent:(NSEvent *)event {
- (BOOL)textInputShouldHandlePaste:(__unused id)sender
{
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
NSPasteboardType fileType = [pasteboard availableTypeFromArray:@[NSFilenamesPboardType, NSPasteboardTypePNG, NSPasteboardTypeTIFF]];

NSPasteboardType fileType = [pasteboard availableTypeFromArray:@[NSPasteboardTypeFileURL, NSPasteboardTypePNG, NSPasteboardTypeTIFF]];
NSArray<NSPasteboardType>* pastedTypes = ((RCTUITextView*) self.backedTextInputView).readablePasteboardTypes;

// If there's a fileType that is of interest, notify JS. Also blocks notifying JS if it's a text paste
if (_onPaste && fileType != nil) {
if (_onPaste && fileType != nil && [pastedTypes containsObject:fileType]) {
_onPaste([self dataTransferInfoFromPasteboard:pasteboard]);
}

Expand Down
11 changes: 6 additions & 5 deletions React/Base/RCTConvert.m
Original file line number Diff line number Diff line change
Expand Up @@ -1233,11 +1233,12 @@ + (NSArray *)CGColorArray:(id)json
}

if ([type isEqualToString:@"fileUrl"]) {
return @[NSFilenamesPboardType];
return @[NSPasteboardTypeFileURL];
} else if ([type isEqualToString:@"image"]) {
return @[NSPasteboardTypePNG, NSPasteboardTypeTIFF];
} else if ([type isEqualToString:@"string"]) {
return @[NSPasteboardTypeString];
}

return @[];
}

Expand All @@ -1246,11 +1247,11 @@ + (NSArray *)CGColorArray:(id)json
if ([json isKindOfClass:[NSString class]]) {
return [RCTConvert NSPasteboardType:json];
} else if ([json isKindOfClass:[NSArray class]]) {
NSMutableArray *mutablePastboardTypes = [NSMutableArray new];
NSMutableArray *mutablePasteboardTypes = [NSMutableArray new];
for (NSString *type in json) {
[mutablePastboardTypes addObjectsFromArray:[RCTConvert NSPasteboardType:type]];
return mutablePastboardTypes.copy;
[mutablePasteboardTypes addObjectsFromArray:[RCTConvert NSPasteboardType:type]];
}
return mutablePasteboardTypes.copy;
}
return @[];
}
Expand Down
13 changes: 12 additions & 1 deletion packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,18 @@ function OnPaste(): React.Node {
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
}}
placeholder="MULTI LINE with onPaste() for PNG and TIFF images"
pastedTypes={['string']}
placeholder="MULTI LINE with onPaste() text from clipboard"
/>
<TextInput
multiline={true}
style={styles.multiline}
onPaste={(e: PasteEvent) => {
appendLog(JSON.stringify(e.nativeEvent.dataTransfer.types));
setImageUri(e.nativeEvent.dataTransfer.files[0].uri);
}}
pastedTypes={['fileUrl', 'image', 'string']}
placeholder="MULTI LINE with onPaste() for PNG/TIFF images from clipboard or fileUrl (via Finder) and text from clipboard"
/>
<Text style={{height: 30}}>{log.join('\n')}</Text>
<Image
Expand Down

0 comments on commit df8cfbd

Please sign in to comment.