diff --git a/.ado/setup_droid_deps.sh b/.ado/setup_droid_deps.sh old mode 100644 new mode 100755 diff --git a/Libraries/Components/Button.js b/Libraries/Components/Button.js index 8e5fae6eaf..f1811c2519 100644 --- a/Libraries/Components/Button.js +++ b/Libraries/Components/Button.js @@ -21,6 +21,7 @@ const View = require('./View/View'); const invariant = require('invariant'); import type {PressEvent} from '../Types/CoreEventTypes'; +import type {FocusEvent, BlurEvent} from './TextInput/TextInput'; // TODO(OSS Candidate ISS#2710739) type ButtonProps = $ReadOnly<{| /** @@ -100,6 +101,18 @@ type ButtonProps = $ReadOnly<{| * Used to locate this view in end-to-end tests. */ testID?: ?string, + + // [TODO(OSS Candidate ISS#2710739) + /** + * Handler to be called when the button receives key focus + */ + onBlur?: ?(e: BlurEvent) => void, + + /** + * Handler to be called when the button loses key focus + */ + onFocus?: ?(e: FocusEvent) => void, + // ]TODO(OSS Candidate ISS#2710739) |}>; /** @@ -147,6 +160,8 @@ class Button extends React.Component { nextFocusUp, disabled, testID, + onFocus, // TODO(OSS Candidate ISS#2710739) + onBlur, // TODO(OSS Candidate ISS#2710739) } = this.props; const buttonStyles = [styles.button]; const textStyles = [styles.text]; @@ -189,6 +204,8 @@ class Button extends React.Component { testID={testID} disabled={disabled} onPress={onPress} + onFocus={onFocus} // TODO(OSS Candidate ISS#2710739) + onBlur={onBlur} // TODO(OSS Candidate ISS#2710739) touchSoundDisabled={touchSoundDisabled}> diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index e0301cbcf2..388f1c007b 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -1025,6 +1025,8 @@ const TextInput = createReactClass({ {this.props.children} diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index b7c8bf1158..5b19eb1869 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -454,6 +454,8 @@ const TouchableHighlight = ((createReactClass({ onDragEnter={this.props.onDragEnter} onDragLeave={this.props.onDragLeave} onDrop={this.props.onDrop} + onFocus={this.props.onFocus} + onBlur={this.props.onBlur} draggedTypes={this.props.draggedTypes} // ]TODO(macOS/win ISS#2323203) nativeID={this.props.nativeID} testID={this.props.testID}> diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 5a8ae9d3d1..ae5fb4efba 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -355,6 +355,8 @@ const TouchableOpacity = ((createReactClass({ onDragEnter={this.props.onDragEnter} onDragLeave={this.props.onDragLeave} onDrop={this.props.onDrop} + onFocus={this.props.onFocus} + onBlur={this.props.onBlur} draggedTypes={this.props.draggedTypes} // ]TODO(macOS ISS#2323203) onResponderTerminate={this.touchableHandleResponderTerminate}> {this.props.children} diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index 9cf5530d57..5584507e50 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -351,6 +351,8 @@ const TouchableWithoutFeedback = ((createReactClass({ onDragEnter: this.props.onDragEnter, onDragLeave: this.props.onDragLeave, onDrop: this.props.onDrop, + onFocus: this.props.onFocus, + onBlur: this.props.onBlur, draggedTypes: this.props.draggedTypes, // ]TODO(macOS ISS#2323203) children, }); diff --git a/Libraries/Text/Text/RCTTextView.h b/Libraries/Text/Text/RCTTextView.h index 7edc12ffe8..26b74c1174 100644 --- a/Libraries/Text/Text/RCTTextView.h +++ b/Libraries/Text/Text/RCTTextView.h @@ -6,6 +6,7 @@ */ #import +#import // TODO(OSS Candidate ISS#2710739) #import // TODO(macOS ISS#2323203) @@ -13,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN @interface RCTTextView : RCTUIView // TODO(macOS ISS#3536887) +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher; // TODO(OSS Candidate ISS#2710739) + @property (nonatomic, assign) BOOL selectable; - (void)setTextStorage:(NSTextStorage *)textStorage diff --git a/Libraries/Text/Text/RCTTextView.m b/Libraries/Text/Text/RCTTextView.m index a4d9eca8e1..655c250a11 100644 --- a/Libraries/Text/Text/RCTTextView.m +++ b/Libraries/Text/Text/RCTTextView.m @@ -16,6 +16,7 @@ #import // TODO(macOS ISS#2323203) #import #import +#import // TODO(OSS Candidate ISS#2710739) #import @@ -30,11 +31,22 @@ NSString * _accessibilityLabel; #endif // ]TODO(macOS ISS#2323203) + RCTEventDispatcher *_eventDispatcher; // TODO(OSS Candidate ISS#2710739) NSArray *_Nullable _descendantViews; // TODO(macOS ISS#3536887) NSTextStorage *_Nullable _textStorage; CGRect _contentFrame; } +// [TODO(OSS Candidate ISS#2710739) +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +{ + if ((self = [self initWithFrame:CGRectZero])) { + _eventDispatcher = eventDispatcher; + } + return self; +} +// ]TODO(OSS Candidate ISS#2710739) + - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { @@ -374,6 +386,31 @@ } } } + +- (BOOL)becomeFirstResponder +{ + if (![super becomeFirstResponder]) { + return NO; + } + + // If we've gained focus, notify listeners + [_eventDispatcher sendEvent:[RCTFocusChangeEvent focusEventWithReactTag:self.reactTag]]; + + return YES; +} + +- (BOOL)resignFirstResponder +{ + if (![super resignFirstResponder]) { + return NO; + } + + // If we've lost focus, notify listeners + [_eventDispatcher sendEvent:[RCTFocusChangeEvent blurEventWithReactTag:self.reactTag]]; + + return YES; +} + #endif // ]TODO(macOS ISS#2323203) - (BOOL)canBecomeFirstResponder diff --git a/Libraries/Text/Text/RCTTextViewManager.m b/Libraries/Text/Text/RCTTextViewManager.m index 8903ec74f7..5826c75466 100644 --- a/Libraries/Text/Text/RCTTextViewManager.m +++ b/Libraries/Text/Text/RCTTextViewManager.m @@ -59,7 +59,7 @@ RCT_EXPORT_VIEW_PROPERTY(selectable, BOOL) - (RCTUIView *)view // TODO(macOS ISS#3536887) { - return [RCTTextView new]; + return [[RCTTextView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; // TODO(OSS Candidate ISS#2710739) } - (RCTShadowView *)shadowView diff --git a/RNTester/Podfile.lock b/RNTester/Podfile.lock index 6801d61867..8c9d0a6d4c 100644 --- a/RNTester/Podfile.lock +++ b/RNTester/Podfile.lock @@ -348,34 +348,34 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost-for-react-native: a110407d9db2642fd2e1bcd7c5a51c81f2521dc9 DoubleConversion: a1bc12a74baa397a2609e0f10e19b8062d864053 - FBLazyVector: f5bad3ee19c3b19198bc28f46d6659e25572ac47 - FBReactNativeSpec: 7732ed43803015907c93dfe88fdf2be2e24d0d70 + FBLazyVector: dd4758081b0f021c1143d98bfb9b9c417c4b9759 + FBReactNativeSpec: c6032881e58249a39922c173508a450a8c0fa84a Folly: feff29ba9d0b7c2e4f793a94942831d6cc5bbad7 glog: b3f6d74f3e2d33396addc0ee724d2b2b79fc3e00 - RCTRequired: 812bd7bbc1be8b3256048f9b5f34535639b29732 - RCTTypeSafety: 193e6faaa597b67aff42fddb7c2fd541ef623f1b - React: fe6fcc68ad4f08c1fe87a4f2fa15b017f239bc0f - React-ART: bc65ee94c935651318476044693d899f8aa8efd5 - React-Core: a35413a7c9a7bea6971d7c0f97f760cc5e08fbaa - React-CoreModules: c94d232fd1a586cb39ba012e619a153197c87789 - React-cxxreact: f80f29082cdceb455e30db3970a01cba7e3e04e6 - React-jsi: 140a17602e41ecfdd12d05ecc6d4da581d9a8106 - React-jsiexecutor: 557554207726e37d82725e5e90536f528d97578c - React-jsinspector: cc8beb2af2ace265bf9d32535611caa06b39c9c1 - React-RCTActionSheet: b7034fd12b78151e1ca8c58c860a965b925df4ea - React-RCTAnimation: a3ad6ef884a511336589ee9c8f7ee3f377d5466c - React-RCTBlob: fbb81655e4d135a57e29954f2c2d67d1aafdfff6 - React-RCTImage: 85746d1b88dd0f6ab0d9c3783b0b034838e4daad - React-RCTLinking: 57aa7ef8760443195c2cfd704ca64c85436bcde4 - React-RCTNetwork: 08ef45f90629939182c1a33d70892ed66b8f5cc2 - React-RCTPushNotification: df58486300dfbf81dee444b725d1208cd79b808d - React-RCTSettings: 3eb68e04de7e8b99e38eb299fb7d2161ae14743c - React-RCTTest: 262ad0c0820cbbcbb609386c9dde07083b3d62db - React-RCTText: 5b457f78863067483d3f74ed43b8740a57d94fb0 - React-RCTVibration: 4616073e79bd812b402aabe21aa9f663bccaf1b0 - ReactCommon: 8d2b53e8598fa17b6fb8ef814b726a3f17cce4f9 - Yoga: 9cc54b8ca13d6fa87f8214c6eac97745e1df8092 + RCTRequired: 733f6fc9179aa8fcc87fb63f3f3aad9929241fc3 + RCTTypeSafety: c52f0b46836e84cb951d9c40c8209d10775b6bbc + React: b94e937a7271f35ee705c21d0c54990192cc1634 + React-ART: c55a2a4bc6f9bec3bb604606175f05ee081a7455 + React-Core: 5fc0c8ad37c5f0f6a40433be0524cee6058edd17 + React-CoreModules: 015ba93e2945bf810a6ef67f5b4086c87ae63009 + React-cxxreact: 07fae12835719892e7fe92dfc46103f69a1a8e39 + React-jsi: 20175357a063599ea4c8f969c4b325e76eae5aaa + React-jsiexecutor: 308871755c64cb5eacd94cd01ee34ba02a50f5da + React-jsinspector: 03688e20257352e97f0e69016743b90561a3b23f + React-RCTActionSheet: 3a6b052391c5fff7e1b27539ef9d7834f7cefed5 + React-RCTAnimation: 15e4bf4f576bb5f610dbaa016c2e555ea4a7c228 + React-RCTBlob: 4b22ab790b485e2d1b5bc0183ccc02dc15ae866d + React-RCTImage: 71640ec76e906b84d86af56f98421aeb4f82466b + React-RCTLinking: af1912929e4da9443ac128f6ded8eecca7fcde90 + React-RCTNetwork: fc021eb11dabf0100f570f89588df3e27b0499b1 + React-RCTPushNotification: 15e4ffb1d0db67e906afdecc8cacfab348f451e5 + React-RCTSettings: d5486cbc262bf0b1b76ab7a0c60e78e8973f5563 + React-RCTTest: 1ade3c47acd1eef8ed042be56d6d2ba153fca552 + React-RCTText: 89c4bc63088b38b68b9008c525b507edaa9277e8 + React-RCTVibration: 13734e1938ee92a3363e7d437602e3e0cbe0b1f9 + ReactCommon: cdaca147e8b50f4c424044db5055e602f9addf07 + Yoga: 39d21425da1d0e0463995aaa8f5af56db0b39ed3 -PODFILE CHECKSUM: 2bf56de39dc7e153b4f1637e0f4874f2591fc55a +PODFILE CHECKSUM: 1a5700c13b2bc8ffc5ed4282d795e8909c883f63 -COCOAPODS: 1.9.1 +COCOAPODS: 1.9.3 diff --git a/RNTester/js/examples/FocusEventsExample/FocusEventsExample.js b/RNTester/js/examples/FocusEventsExample/FocusEventsExample.js index 8a61bb1e42..65be0afd2c 100644 --- a/RNTester/js/examples/FocusEventsExample/FocusEventsExample.js +++ b/RNTester/js/examples/FocusEventsExample/FocusEventsExample.js @@ -13,7 +13,7 @@ var React = require('react'); var ReactNative = require('react-native'); import {Platform} from 'react-native'; -var {StyleSheet, Text, View, TextInput} = ReactNative; +var {Button, StyleSheet, Text, View, TextInput} = ReactNative; type State = { eventStream: string, @@ -110,33 +110,103 @@ class FocusEventExample extends React.Component<{}, State> { {// Only test View on MacOS, since canBecomeFirstResponder is false on all iOS, therefore we can't focus Platform.OS === 'macos' ? ( - { - this.setState(prevState => ({ - eventStream: - prevState.eventStream + '\nNested View Parent Focus', - })); - }} - onBlur={() => { - this.setState(prevState => ({ - eventStream: - prevState.eventStream + '\nNested View Parent Blur', - })); - }}> + { this.setState(prevState => ({ - eventStream: prevState.eventStream + '\nNested View Focus', + eventStream: + prevState.eventStream + '\nDescendent Button Focus', })); }} onBlur={() => { this.setState(prevState => ({ - eventStream: prevState.eventStream + '\nNested View Blur', + eventStream: + prevState.eventStream + '\nDescendent Button Blur', })); }}> - Nested Focusable View + +