Better TextInput: RCTUITextView was decoupled in separate file and now handles placeholder feature

Reviewed By: mmmulani

Differential Revision: D4663151

fbshipit-source-id: ce57ca4bebf4676df2ae5e586a1b175ec2aac760
This commit is contained in:
Valentin Shergin 2017-03-20 00:00:23 -07:00 committed by Facebook Github Bot
parent 26e2c08544
commit b53d76efb7
7 changed files with 323 additions and 197 deletions

View File

@ -779,7 +779,7 @@ exports.examples = [
<View>
<TextInput
placeholder="height increases with content"
defaultValue="React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native."
defaultValue="React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React. The focus of React Native is on developer efficiency across all the platforms you care about - learn once, write anywhere. Facebook uses React Native in multiple production apps and will continue investing in React Native."
multiline={true}
enablesReturnKeyAutomatically={true}
returnKeyType="go"

View File

@ -27,6 +27,8 @@
58B511D01A9E6C5C00147676 /* RCTShadowText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511CB1A9E6C5C00147676 /* RCTShadowText.m */; };
58B511D11A9E6C5C00147676 /* RCTTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B511CD1A9E6C5C00147676 /* RCTTextManager.m */; };
58B512161A9E6EFF00147676 /* RCTText.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512141A9E6EFF00147676 /* RCTText.m */; };
59B125C91E6E4E15004E2A67 /* RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59B125C81E6E4E15004E2A67 /* RCTUITextView.m */; };
59B125CA1E6E4E15004E2A67 /* RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59B125C81E6E4E15004E2A67 /* RCTUITextView.m */; };
59F60E911E661BDD0081153B /* RCTShadowTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */; };
59F60E921E661BDD0081153B /* RCTShadowTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */; };
59F60E931E661BDD0081153B /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; };
@ -58,6 +60,8 @@
58B511CD1A9E6C5C00147676 /* RCTTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextManager.m; sourceTree = "<group>"; };
58B512141A9E6EFF00147676 /* RCTText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTText.m; sourceTree = "<group>"; };
58B512151A9E6EFF00147676 /* RCTText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTText.h; sourceTree = "<group>"; };
59B125C71E6E4E15004E2A67 /* RCTUITextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUITextView.h; sourceTree = "<group>"; };
59B125C81E6E4E15004E2A67 /* RCTUITextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUITextView.m; sourceTree = "<group>"; };
59F60E8D1E661BDD0081153B /* RCTShadowTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowTextField.h; sourceTree = "<group>"; };
59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowTextField.m; sourceTree = "<group>"; };
59F60E8F1E661BDD0081153B /* RCTShadowTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowTextView.h; sourceTree = "<group>"; };
@ -97,6 +101,8 @@
131B6ABD1AF0CD0600FFC3E0 /* RCTTextView.m */,
131B6ABE1AF0CD0600FFC3E0 /* RCTTextViewManager.h */,
131B6ABF1AF0CD0600FFC3E0 /* RCTTextViewManager.m */,
59B125C71E6E4E15004E2A67 /* RCTUITextView.h */,
59B125C81E6E4E15004E2A67 /* RCTUITextView.m */,
);
indentWidth = 2;
sourceTree = "<group>";
@ -194,6 +200,7 @@
2D3B5F341D9B103100451313 /* RCTRawTextManager.m in Sources */,
59F60E921E661BDD0081153B /* RCTShadowTextField.m in Sources */,
AF3225FA1DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */,
59B125CA1E6E4E15004E2A67 /* RCTUITextView.m in Sources */,
2D3B5F3C1D9B106F00451313 /* RCTTextViewManager.m in Sources */,
59F60E941E661BDD0081153B /* RCTShadowTextView.m in Sources */,
2D3B5F331D9B102D00451313 /* RCTTextSelection.m in Sources */,
@ -214,6 +221,7 @@
1362F1011B4D51F400E06D8C /* RCTTextFieldManager.m in Sources */,
59F60E911E661BDD0081153B /* RCTShadowTextField.m in Sources */,
AF3225F91DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */,
59B125C91E6E4E15004E2A67 /* RCTUITextView.m in Sources */,
131B6AC11AF0CD0600FFC3E0 /* RCTTextViewManager.m in Sources */,
59F60E931E661BDD0081153B /* RCTShadowTextView.m in Sources */,
58B511CF1A9E6C5C00147676 /* RCTShadowRawText.m in Sources */,

View File

@ -25,6 +25,7 @@
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
@property (nonatomic, copy) NSString *text;
@property (nonatomic, strong) UIColor *placeholderTextColor;
@property (nonatomic, copy) NSString *placeholder;
@property (nonatomic, strong) UIFont *font;
@property (nonatomic, assign) NSInteger mostRecentEventCount;
@property (nonatomic, strong) NSNumber *maxLength;

View File

@ -18,64 +18,14 @@
#import "RCTShadowText.h"
#import "RCTText.h"
#import "RCTTextSelection.h"
@interface RCTUITextView : UITextView
@property (nonatomic, assign) BOOL textWasPasted;
@end
@implementation RCTUITextView
{
BOOL _jsRequestingFirstResponder;
}
- (void)paste:(id)sender
{
_textWasPasted = YES;
[super paste:sender];
}
- (void)reactWillMakeFirstResponder
{
_jsRequestingFirstResponder = YES;
}
- (BOOL)canBecomeFirstResponder
{
return _jsRequestingFirstResponder;
}
- (void)reactDidMakeFirstResponder
{
_jsRequestingFirstResponder = NO;
}
- (void)didMoveToWindow
{
if (_jsRequestingFirstResponder) {
[self becomeFirstResponder];
[self reactDidMakeFirstResponder];
}
}
- (void)setContentOffset:(CGPoint)contentOffset animated:(__unused BOOL)animated
{
// Turning off scroll animation.
// This fixes the problem also known as "flaky scrolling".
[super setContentOffset:contentOffset animated:NO];
}
@end
#import "RCTUITextView.h"
@implementation RCTTextView
{
RCTBridge *_bridge;
RCTEventDispatcher *_eventDispatcher;
NSString *_placeholder;
UITextView *_placeholderView;
UITextView *_textView;
RCTUITextView *_textView;
RCTText *_richTextView;
NSAttributedString *_pendingAttributedText;
@ -99,7 +49,6 @@
_contentInset = UIEdgeInsetsZero;
_bridge = bridge;
_eventDispatcher = bridge.eventDispatcher;
_placeholderTextColor = [self defaultPlaceholderTextColor];
_blurOnSubmit = NO;
_textView = [[RCTUITextView alloc] initWithFrame:self.bounds];
@ -241,39 +190,12 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string)
[_textView layoutIfNeeded];
[self updatePlaceholderVisibility];
[self invalidateContentSize];
_blockTextShouldChange = NO;
}
- (void)updatePlaceholder
{
[_placeholderView removeFromSuperview];
_placeholderView = nil;
if (_placeholder) {
_placeholderView = [[UITextView alloc] initWithFrame:_textView.frame];
_placeholderView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_placeholderView.textContainer.lineFragmentPadding = 0;
_placeholderView.userInteractionEnabled = NO;
_placeholderView.backgroundColor = [UIColor clearColor];
_placeholderView.scrollEnabled = NO;
#if !TARGET_OS_TV
_placeholderView.editable = NO;
_placeholderView.scrollsToTop = NO;
#endif
_placeholderView.attributedText =
[[NSAttributedString alloc] initWithString:_placeholder attributes:@{
NSFontAttributeName : (_textView.font ? _textView.font : [self defaultPlaceholderFont]),
NSForegroundColorAttributeName : _placeholderTextColor
}];
_placeholderView.textAlignment = _textView.textAlignment;
[self insertSubview:_placeholderView belowSubview:_textView];
[self updatePlaceholderVisibility];
}
}
#pragma mark - Properties
- (UIFont *)font
{
@ -283,40 +205,110 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string)
- (void)setFont:(UIFont *)font
{
_textView.font = font;
[self updatePlaceholder];
}
- (void)setPlaceholder:(NSString *)placeholder
{
_placeholder = placeholder;
[self updatePlaceholder];
}
- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor
{
if (placeholderTextColor) {
_placeholderTextColor = placeholderTextColor;
} else {
_placeholderTextColor = [self defaultPlaceholderTextColor];
}
[self updatePlaceholder];
}
- (void)setContentInset:(UIEdgeInsets)contentInset
{
_contentInset = contentInset;
_textView.textContainerInset = contentInset;
_placeholderView.textContainerInset = contentInset;
[self setNeedsLayout];
}
- (void)setSelection:(RCTTextSelection *)selection
{
if (!selection) {
return;
}
UITextRange *currentSelection = _textView.selectedTextRange;
UITextPosition *start = [_textView positionFromPosition:_textView.beginningOfDocument offset:selection.start];
UITextPosition *end = [_textView positionFromPosition:_textView.beginningOfDocument offset:selection.end];
UITextRange *selectedTextRange = [_textView textRangeFromPosition:start toPosition:end];
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
if (eventLag == 0 && ![currentSelection isEqual:selectedTextRange]) {
_previousSelectionRange = selectedTextRange;
_textView.selectedTextRange = selectedTextRange;
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag);
}
}
- (NSString *)text
{
return _textView.text;
}
- (void)setText:(NSString *)text
{
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
if (eventLag == 0 && ![text isEqualToString:_textView.text]) {
UITextRange *selection = _textView.selectedTextRange;
NSInteger oldTextLength = _textView.text.length;
_predictedText = text;
_textView.text = text;
if (selection.empty) {
// maintain cursor position relative to the end of the old text
NSInteger start = [_textView offsetFromPosition:_textView.beginningOfDocument toPosition:selection.start];
NSInteger offsetFromEnd = oldTextLength - start;
NSInteger newOffset = text.length - offsetFromEnd;
UITextPosition *position = [_textView positionFromPosition:_textView.beginningOfDocument offset:newOffset];
_textView.selectedTextRange = [_textView textRangeFromPosition:position toPosition:position];
}
[self invalidateContentSize];
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag);
}
}
- (NSString *)placeholder
{
return _textView.placeholderText;
}
- (void)setPlaceholder:(NSString *)placeholder
{
_textView.placeholderText = placeholder;
}
- (UIColor *)placeholderTextColor
{
return _textView.placeholderTextColor;
}
- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor
{
_textView.placeholderTextColor = placeholderTextColor;
}
- (void)setAutocorrectionType:(UITextAutocorrectionType)autocorrectionType
{
_textView.autocorrectionType = autocorrectionType;
}
- (UITextAutocorrectionType)autocorrectionType
{
return _textView.autocorrectionType;
}
- (void)setSpellCheckingType:(UITextSpellCheckingType)spellCheckingType
{
_textView.spellCheckingType = spellCheckingType;
}
- (UITextSpellCheckingType)spellCheckingType
{
return _textView.spellCheckingType;
}
#pragma mark - UITextViewDelegate
- (BOOL)textView:(RCTUITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if (textView.textWasPasted) {
textView.textWasPasted = NO;
} else {
if (!textView.textWasPasted) {
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeKeyPress
reactTag:self.reactTag
text:nil
@ -425,86 +417,6 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string)
}
}
- (NSString *)text
{
return _textView.text;
}
- (void)setSelection:(RCTTextSelection *)selection
{
if (!selection) {
return;
}
UITextRange *currentSelection = _textView.selectedTextRange;
UITextPosition *start = [_textView positionFromPosition:_textView.beginningOfDocument offset:selection.start];
UITextPosition *end = [_textView positionFromPosition:_textView.beginningOfDocument offset:selection.end];
UITextRange *selectedTextRange = [_textView textRangeFromPosition:start toPosition:end];
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
if (eventLag == 0 && ![currentSelection isEqual:selectedTextRange]) {
_previousSelectionRange = selectedTextRange;
_textView.selectedTextRange = selectedTextRange;
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag);
}
}
- (void)setText:(NSString *)text
{
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
if (eventLag == 0 && ![text isEqualToString:_textView.text]) {
UITextRange *selection = _textView.selectedTextRange;
NSInteger oldTextLength = _textView.text.length;
_predictedText = text;
_textView.text = text;
if (selection.empty) {
// maintain cursor position relative to the end of the old text
NSInteger start = [_textView offsetFromPosition:_textView.beginningOfDocument toPosition:selection.start];
NSInteger offsetFromEnd = oldTextLength - start;
NSInteger newOffset = text.length - offsetFromEnd;
UITextPosition *position = [_textView positionFromPosition:_textView.beginningOfDocument offset:newOffset];
_textView.selectedTextRange = [_textView textRangeFromPosition:position toPosition:position];
}
[self updatePlaceholderVisibility];
[self invalidateContentSize];
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag);
}
}
- (void)updatePlaceholderVisibility
{
if (_textView.text.length > 0) {
[_placeholderView setHidden:YES];
} else {
[_placeholderView setHidden:NO];
}
}
- (void)setAutocorrectionType:(UITextAutocorrectionType)autocorrectionType
{
_textView.autocorrectionType = autocorrectionType;
}
- (UITextAutocorrectionType)autocorrectionType
{
return _textView.autocorrectionType;
}
- (void)setSpellCheckingType:(UITextSpellCheckingType)spellCheckingType
{
_textView.spellCheckingType = spellCheckingType;
}
- (UITextSpellCheckingType)spellCheckingType
{
return _textView.spellCheckingType;
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
if (_selectTextOnFocus) {
@ -519,7 +431,6 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string)
{
if (_clearTextOnFocus) {
_textView.text = @"";
[self updatePlaceholderVisibility];
}
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus
@ -560,7 +471,6 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange,
- (void)textViewDidChange:(UITextView *)textView
{
[self updatePlaceholderVisibility];
[self invalidateContentSize];
// Detect when textView updates happend that didn't invoke `shouldChangeTextInRange`
@ -580,6 +490,7 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange,
_nativeUpdatesInFlight = NO;
_nativeEventCount++;
// TODO: t16435709 This part will be removed soon.
if (!self.reactTag || !_onChange) {
return;
}
@ -718,18 +629,6 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange,
[self invalidateContentSize];
}
#pragma mark - Default values
- (UIFont *)defaultPlaceholderFont
{
return [UIFont systemFontOfSize:17];
}
- (UIColor *)defaultPlaceholderTextColor
{
return [UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.098/255.0 alpha:0.22];
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/*
* Just regular UITextView... but much better!
*/
@interface RCTUITextView : UITextView
- (instancetype)initWithFrame:(CGRect)frame textContainer:(nullable NSTextContainer *)textContainer NS_UNAVAILABLE;
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
@property (nonatomic, assign, readonly) BOOL textWasPasted;
@property (nonatomic, copy, nullable) NSString *placeholderText;
@property (nonatomic, assign, nullable) UIColor *placeholderTextColor;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,189 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTUITextView.h"
@implementation RCTUITextView
{
BOOL _jsRequestingFirstResponder;
UILabel *_placeholderView;
UITextView *_detachedTextView;
}
static UIFont *defaultPlaceholderFont()
{
return [UIFont systemFontOfSize:17];
}
static UIColor *defaultPlaceholderTextColor()
{
// Default placeholder color from UITextField.
return [UIColor colorWithRed:0 green:0 blue:0.0980392 alpha:0.22];
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(textDidChange)
name:UITextViewTextDidChangeNotification
object:self];
_placeholderView = [[UILabel alloc] initWithFrame:self.bounds];
_placeholderView.hidden = YES;
_placeholderView.isAccessibilityElement = NO;
_placeholderView.numberOfLines = 0;
[self addSubview:_placeholderView];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - Properties
- (void)setPlaceholderText:(NSString *)placeholderText
{
_placeholderText = placeholderText;
[self invalidatePlaceholder];
}
- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor
{
_placeholderTextColor = placeholderTextColor;
[self invalidatePlaceholder];
}
- (void)textDidChange
{
_textWasPasted = NO;
[self invalidatePlaceholder];
}
#pragma mark - UIResponder
- (void)reactWillMakeFirstResponder
{
_jsRequestingFirstResponder = YES;
}
- (BOOL)canBecomeFirstResponder
{
return _jsRequestingFirstResponder;
}
- (void)reactDidMakeFirstResponder
{
_jsRequestingFirstResponder = NO;
}
- (void)didMoveToWindow
{
if (_jsRequestingFirstResponder) {
[self becomeFirstResponder];
[self reactDidMakeFirstResponder];
}
}
#pragma mark - Overrides
- (void)setFont:(UIFont *)font
{
[super setFont:font];
[self invalidatePlaceholder];
}
- (void)setText:(NSString *)text
{
[super setText:text];
[self textDidChange];
}
- (void)setAttributedText:(NSAttributedString *)attributedText
{
[super setAttributedText:attributedText];
[self textDidChange];
}
- (void)paste:(id)sender
{
[super paste:sender];
_textWasPasted = YES;
}
- (void)setContentOffset:(CGPoint)contentOffset animated:(__unused BOOL)animated
{
// Turning off scroll animation.
// This fixes the problem also known as "flaky scrolling".
[super setContentOffset:contentOffset animated:NO];
}
#pragma mark - Layout
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect textFrame = UIEdgeInsetsInsetRect(self.bounds, self.textContainerInset);
CGFloat placeholderHeight = [_placeholderView sizeThatFits:textFrame.size].height;
textFrame.size.height = MIN(placeholderHeight, textFrame.size.height);
_placeholderView.frame = textFrame;
}
- (CGSize)sizeThatFits:(CGSize)size
{
// UITextView on iOS 8 has a bug that automatically scrolls to the top
// when calling `sizeThatFits:`. Use a copy so that self is not screwed up.
static BOOL useCustomImplementation = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
useCustomImplementation = ![[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){9,0,0}];
});
if (!useCustomImplementation) {
return [super sizeThatFits:size];
}
if (!_detachedTextView) {
_detachedTextView = [UITextView new];
}
_detachedTextView.attributedText = self.attributedText;
_detachedTextView.font = self.font;
_detachedTextView.textContainerInset = self.textContainerInset;
return [_detachedTextView sizeThatFits:size];
}
#pragma mark - Placeholder
- (void)invalidatePlaceholder
{
BOOL wasVisible = !_placeholderView.isHidden;
BOOL isVisible = _placeholderText.length != 0 && self.text.length == 0;
if (wasVisible != isVisible) {
_placeholderView.hidden = !isVisible;
}
if (isVisible) {
_placeholderView.font = self.font ?: defaultPlaceholderFont();
_placeholderView.textColor = _placeholderTextColor ?: defaultPlaceholderTextColor();
_placeholderView.textAlignment = self.textAlignment;
_placeholderView.text = _placeholderText;
[self setNeedsLayout];
}
}
@end

View File

@ -10,6 +10,7 @@
#import <Foundation/Foundation.h>
#import "RCTAssert.h"
#import "RCTBridge+Private.h"
#import "RCTBridge.h"
#import "RCTBridgeMethod.h"