Add option to enforce pasting plain text (#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 https://github.com/microsoft/react-native-macos/pull/1350
This commit is contained in:
Christoph Purrer 2022-09-21 09:42:58 -07:00 коммит произвёл GitHub
Родитель 377063c627
Коммит d8612d0376
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 74 добавлений и 20 удалений

Просмотреть файл

@ -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
@ -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,
/**
@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -99,6 +99,9 @@
}
}
- (void)setReadablePasteBoardTypes:(NSArray<NSPasteboardType> *)readablePasteboardTypes {
[_backedTextInputView setReadablePasteBoardTypes:readablePasteboardTypes];
}
#endif // ]TODO(macOS GH#774)
#pragma mark - UIScrollViewDelegate

Просмотреть файл

@ -7,6 +7,7 @@
#import <React/RCTMultilineTextInputViewManager.h>
#import <React/RCTMultilineTextInputView.h>
#import <React/RCTUITextView.h> // TODO(macOS GH#774)
@implementation RCTMultilineTextInputViewManager
@ -22,4 +23,14 @@ RCT_EXPORT_MODULE()
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

Просмотреть файл

@ -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

Просмотреть файл

@ -21,6 +21,9 @@
#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()
@ -177,6 +180,11 @@ static RCTUIColor *defaultPlaceholderColor() // TODO(OSS Candidate ISS#2710739)
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.
@ -344,9 +352,7 @@ static RCTUIColor *defaultPlaceholderColor() // TODO(OSS Candidate ISS#2710739)
}
- (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)

Просмотреть файл

@ -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 {
@ -680,10 +681,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)decoder)
- (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]);
}

Просмотреть файл

@ -1233,11 +1233,12 @@ RCT_JSON_ARRAY_CONVERTER(NSNumber)
}
if ([type isEqualToString:@"fileUrl"]) {
return @[NSFilenamesPboardType];
return @[NSPasteboardTypeFileURL];
} else if ([type isEqualToString:@"image"]) {
return @[NSPasteboardTypePNG, NSPasteboardTypeTIFF];
} else if ([type isEqualToString:@"string"]) {
return @[NSPasteboardTypeString];
}
return @[];
}
@ -1246,11 +1247,11 @@ RCT_JSON_ARRAY_CONVERTER(NSNumber)
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 @[];
}

Просмотреть файл

@ -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