Open sourced the onSelectionChange event
Summary: public Open-sourced the onSelectionChange event for RCTTextView, and also added onSelectionChange support for RCTTextField. Reviewed By: javache Differential Revision: D2647541 fb-gh-sync-id: ab0ab37f5f087e708a199461ffc33231a47d2133
This commit is contained in:
parent
397791fcea
commit
5a34a097f2
|
@ -31,7 +31,6 @@ var invariant = require('invariant');
|
|||
var requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
var onlyMultiline = {
|
||||
onSelectionChange: true, // not supported in Open Source yet
|
||||
onTextInput: true, // not supported in Open Source yet
|
||||
children: true,
|
||||
};
|
||||
|
@ -413,6 +412,17 @@ var TextInput = React.createClass({
|
|||
_renderIOS: function() {
|
||||
var textContainer;
|
||||
|
||||
var onSelectionChange;
|
||||
if (this.props.selectionState || this.props.onSelectionChange) {
|
||||
onSelectionChange = function(event: Event) {
|
||||
if (this.props.selectionState) {
|
||||
var selection = event.nativeEvent.selection;
|
||||
this.props.selectionState.update(selection.start, selection.end);
|
||||
}
|
||||
this.props.onSelectionChange && this.props.onSelectionChange(event);
|
||||
};
|
||||
}
|
||||
|
||||
var props = Object.assign({}, this.props);
|
||||
props.style = [styles.input, this.props.style];
|
||||
if (!props.multiline) {
|
||||
|
@ -430,7 +440,8 @@ var TextInput = React.createClass({
|
|||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
onSelectionChangeShouldSetResponder={() => true}
|
||||
onSelectionChange={onSelectionChange}
|
||||
onSelectionChangeShouldSetResponder={emptyFunction.thatReturnsTrue}
|
||||
text={this._getText()}
|
||||
mostRecentEventCount={this.state.mostRecentEventCount}
|
||||
/>;
|
||||
|
@ -465,7 +476,7 @@ var TextInput = React.createClass({
|
|||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
onSelectionChange={this._onSelectionChange}
|
||||
onSelectionChange={onSelectionChange}
|
||||
onTextInput={this._onTextInput}
|
||||
onSelectionChangeShouldSetResponder={emptyFunction.thatReturnsTrue}
|
||||
text={this._getText()}
|
||||
|
@ -581,14 +592,6 @@ var TextInput = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_onSelectionChange: function(event: Event) {
|
||||
if (this.props.selectionState) {
|
||||
var selection = event.nativeEvent.selection;
|
||||
this.props.selectionState.update(selection.start, selection.end);
|
||||
}
|
||||
this.props.onSelectionChange && this.props.onSelectionChange(event);
|
||||
},
|
||||
|
||||
_onTextInput: function(event: Event) {
|
||||
this.props.onTextInput && this.props.onTextInput(event);
|
||||
},
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTComponent.h"
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
|
||||
@interface RCTTextField : UITextField
|
||||
|
@ -23,6 +25,8 @@
|
|||
@property (nonatomic, strong) NSNumber *maxLength;
|
||||
@property (nonatomic, assign) BOOL textWasPasted;
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onSelectionChange;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)textFieldDidChange;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
BOOL _jsRequestingFirstResponder;
|
||||
NSInteger _nativeEventCount;
|
||||
BOOL _submitted;
|
||||
UITextRange *_previousSelectionRange;
|
||||
}
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
|
@ -28,15 +29,22 @@
|
|||
if ((self = [super initWithFrame:CGRectZero])) {
|
||||
RCTAssert(eventDispatcher, @"eventDispatcher is a required parameter");
|
||||
_eventDispatcher = eventDispatcher;
|
||||
_previousSelectionRange = self.selectedTextRange;
|
||||
[self addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
|
||||
[self addTarget:self action:@selector(textFieldBeginEditing) forControlEvents:UIControlEventEditingDidBegin];
|
||||
[self addTarget:self action:@selector(textFieldEndEditing) forControlEvents:UIControlEventEditingDidEnd];
|
||||
[self addTarget:self action:@selector(textFieldSubmitEditing) forControlEvents:UIControlEventEditingDidEndOnExit];
|
||||
[self addObserver:self forKeyPath:@"selectedTextRange" options:0 context:nil];
|
||||
_reactSubviews = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self removeObserver:self forKeyPath:@"selectedTextRange"];
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||
|
||||
|
@ -154,6 +162,10 @@ static void RCTUpdatePlaceholder(RCTTextField *self)
|
|||
text:self.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
|
||||
// selectedTextRange observer isn't triggered when you type even though the
|
||||
// cursor position moves, so we send event again here.
|
||||
[self sendSelectionEvent];
|
||||
}
|
||||
|
||||
- (void)textFieldEndEditing
|
||||
|
@ -197,6 +209,36 @@ static void RCTUpdatePlaceholder(RCTTextField *self)
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(RCTTextField *)textField
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context
|
||||
{
|
||||
if ([keyPath isEqualToString:@"selectedTextRange"]) {
|
||||
[self sendSelectionEvent];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendSelectionEvent
|
||||
{
|
||||
if (_onSelectionChange &&
|
||||
self.selectedTextRange != _previousSelectionRange &&
|
||||
![self.selectedTextRange isEqual:_previousSelectionRange]) {
|
||||
|
||||
_previousSelectionRange = self.selectedTextRange;
|
||||
|
||||
UITextRange *selection = self.selectedTextRange;
|
||||
NSInteger start = [self offsetFromPosition:[self beginningOfDocument] toPosition:selection.start];
|
||||
NSInteger end = [self offsetFromPosition:[self beginningOfDocument] toPosition:selection.end];
|
||||
_onSelectionChange(@{
|
||||
@"selection": @{
|
||||
@"start": @(start),
|
||||
@"end": @(end),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
_jsRequestingFirstResponder = YES;
|
||||
|
|
|
@ -87,6 +87,7 @@ RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL)
|
|||
RCT_EXPORT_VIEW_PROPERTY(blurOnSubmit, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(keyboardType, UIKeyboardType)
|
||||
RCT_EXPORT_VIEW_PROPERTY(keyboardAppearance, UIKeyboardAppearance)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(returnKeyType, UIReturnKeyType)
|
||||
RCT_EXPORT_VIEW_PROPERTY(enablesReturnKeyAutomatically, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(secureTextEntry, BOOL)
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
@property (nonatomic, assign) NSInteger mostRecentEventCount;
|
||||
@property (nonatomic, strong) NSNumber *maxLength;
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onSelectionChange;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)performTextUpdate;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
NSAttributedString *_pendingAttributedText;
|
||||
NSMutableArray<UIView<RCTComponent> *> *_subviews;
|
||||
BOOL _blockTextShouldChange;
|
||||
UITextRange *_previousSelectionRange;
|
||||
}
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
|
@ -59,6 +60,8 @@
|
|||
_textView.scrollsToTop = NO;
|
||||
_textView.delegate = self;
|
||||
|
||||
_previousSelectionRange = _textView.selectedTextRange;
|
||||
|
||||
_subviews = [NSMutableArray new];
|
||||
[self addSubview:_textView];
|
||||
}
|
||||
|
@ -284,6 +287,30 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
}
|
||||
}
|
||||
|
||||
- (void)textViewDidChangeSelection:(RCTUITextView *)textView
|
||||
{
|
||||
if (_onSelectionChange &&
|
||||
textView.selectedTextRange != _previousSelectionRange &&
|
||||
![textView.selectedTextRange isEqual:_previousSelectionRange]) {
|
||||
|
||||
_previousSelectionRange = textView.selectedTextRange;
|
||||
|
||||
UITextRange *selection = textView.selectedTextRange;
|
||||
NSInteger start = [textView offsetFromPosition:[textView beginningOfDocument] toPosition:selection.start];
|
||||
NSInteger end = [textView offsetFromPosition:[textView beginningOfDocument] toPosition:selection.end];
|
||||
_onSelectionChange(@{
|
||||
@"selection": @{
|
||||
@"start": @(start),
|
||||
@"end": @(end),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (textView.editable && [textView isFirstResponder]) {
|
||||
[textView scrollRangeToVisible:textView.selectedRange];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
|
||||
|
|
|
@ -34,6 +34,7 @@ RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL)
|
|||
RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(keyboardType, textView.keyboardType, UIKeyboardType)
|
||||
RCT_REMAP_VIEW_PROPERTY(keyboardAppearance, textView.keyboardAppearance, UIKeyboardAppearance)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
|
||||
RCT_REMAP_VIEW_PROPERTY(returnKeyType, textView.returnKeyType, UIReturnKeyType)
|
||||
RCT_REMAP_VIEW_PROPERTY(enablesReturnKeyAutomatically, textView.enablesReturnKeyAutomatically, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(color, textColor, UIColor)
|
||||
|
|
Loading…
Reference in New Issue