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:
Nick Lockwood 2015-11-14 09:41:59 -08:00 committed by facebook-github-bot-7
parent 397791fcea
commit 5a34a097f2
7 changed files with 91 additions and 11 deletions

View File

@ -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);
},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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