Skip to content

Commit 6513a91

Browse files
committed
feat: add caretHeight and caretYoffset to TextInput component
1 parent 3fa098e commit 6513a91

File tree

11 files changed

+146
-1
lines changed

11 files changed

+146
-1
lines changed

Libraries/Components/TextInput/TextInput.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,18 @@ export interface TextInputProps
573573
*/
574574
caretHidden?: boolean | undefined;
575575

576+
/**
577+
* Allows to adjust caret height.
578+
* The default value is 0, which means the height of the caret will be calculated automatically
579+
*/
580+
caretHeight?: number | undefined;
581+
582+
/**
583+
* Allows to adjust caret postiion relative to the Y axis
584+
* The default value is 0.
585+
*/
586+
caretYOffset?: number | undefined;
587+
576588
/**
577589
* If true, context menu is hidden. The default value is false.
578590
*/

Libraries/Components/TextInput/TextInput.flow.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,18 @@ export type Props = $ReadOnly<{|
557557
*/
558558
caretHidden?: ?boolean,
559559

560+
/**
561+
* Allows to adjust caret height.
562+
* The default value is 0, which means the height of the caret will be calculated automatically
563+
*/
564+
565+
caretYOffset?: ?number,
566+
/**
567+
* Allows to adjust caret postiion relative to the Y axis
568+
* The default value is 0.
569+
*/
570+
caretYHeight?: ?number,
571+
560572
/*
561573
* If `true`, contextMenuHidden is hidden. The default value is `false`.
562574
*/

Libraries/Components/TextInput/TextInput.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,17 @@ type IOSProps = $ReadOnly<{|
368368
* @platform ios
369369
*/
370370
smartInsertDelete?: ?boolean,
371+
/**
372+
* Allows to adjust caret height.
373+
* The default value is 0, which means the height of the caret will be calculated automatically
374+
*/
375+
caretYOffset?: ?number,
376+
377+
/**
378+
* Allows to adjust caret postiion relative to the Y axis
379+
* The default value is 0.
380+
*/
381+
caretHeight?: ?number,
371382
|}>;
372383

373384
type AndroidProps = $ReadOnly<{|
@@ -1459,6 +1470,8 @@ function InternalTextInput(props: Props): React.Node {
14591470
selection={selection}
14601471
style={style}
14611472
text={text}
1473+
caretYOffset={props.caretYOffset}
1474+
caretHeight={props.caretHeight}
14621475
/>
14631476
);
14641477
} else if (Platform.OS === 'android') {

Libraries/Text/TextInput/Multiline/RCTUITextView.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
3434
@property (nonatomic, assign) UITextFieldViewMode clearButtonMode;
3535

3636
@property (nonatomic, assign) BOOL caretHidden;
37+
@property (nonatomic, assign) CGFloat caretYOffset;
38+
@property (nonatomic, assign) CGFloat caretHeight;
3739

3840
@property (nonatomic, strong, nullable) NSString *inputAccessoryViewID;
3941

Libraries/Text/TextInput/Multiline/RCTUITextView.m

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,41 @@
1313
#import <React/RCTBackedTextInputDelegateAdapter.h>
1414
#import <React/RCTTextAttributes.h>
1515

16+
//the UITextSelectionRect subclass needs to be created because the original version is not writable
17+
@interface CustomTextSelectionRect : UITextSelectionRect
18+
19+
@property (nonatomic) CGRect _rect;
20+
@property (nonatomic) NSWritingDirection _writingDirection;
21+
@property (nonatomic) BOOL _containsStart; // Returns YES if the rect contains the start of the selection.
22+
@property (nonatomic) BOOL _containsEnd; // Returns YES if the rect contains the end of the selection.
23+
@property (nonatomic) BOOL _isVertical; // Returns YES if the rect is for vertically oriented text.
24+
25+
@end
26+
27+
@implementation CustomTextSelectionRect
28+
29+
- (CGRect)rect {
30+
return __rect;
31+
}
32+
33+
- (NSWritingDirection)writingDirection {
34+
return __writingDirection;
35+
}
36+
37+
- (BOOL)containsStart {
38+
return __containsStart;
39+
}
40+
41+
- (BOOL)containsEnd {
42+
return __containsEnd;
43+
}
44+
45+
- (BOOL)isVertical {
46+
return __isVertical;
47+
}
48+
49+
@end
50+
1651
@implementation RCTUITextView {
1752
UILabel *_placeholderView;
1853
UITextView *_detachedTextView;
@@ -294,11 +329,43 @@ - (void)_updatePlaceholder
294329

295330
- (CGRect)caretRectForPosition:(UITextPosition *)position
296331
{
332+
CGRect originalRect = [super caretRectForPosition:position];
333+
297334
if (_caretHidden) {
298335
return CGRectZero;
299336
}
300337

301-
return [super caretRectForPosition:position];
338+
if(_caretYOffset != 0) {
339+
originalRect.origin.y += _caretYOffset;
340+
}
341+
342+
if(_caretHeight != 0) {
343+
originalRect.size.height = _caretHeight;
344+
}
345+
return originalRect;
346+
}
347+
348+
- (NSArray *)selectionRectsForRange:(UITextRange *)range {
349+
NSArray *superRects = [super selectionRectsForRange:range];
350+
if(_caretYOffset != 0 && _caretHeight != 0) {
351+
NSMutableArray *customTextSelectionRects = [NSMutableArray array];
352+
353+
for (UITextSelectionRect *rect in superRects) {
354+
CustomTextSelectionRect *customTextRect = [[CustomTextSelectionRect alloc] init];
355+
356+
customTextRect._rect = CGRectMake(rect.rect.origin.x, rect.rect.origin.y + _caretYOffset, rect.rect.size.width, _caretHeight);
357+
customTextRect._writingDirection = rect.writingDirection;
358+
customTextRect._containsStart = rect.containsStart;
359+
customTextRect._containsEnd = rect.containsEnd;
360+
customTextRect._isVertical = rect.isVertical;
361+
[customTextSelectionRects addObject:customTextRect];
362+
}
363+
364+
return customTextSelectionRects;
365+
366+
}
367+
return superRects;
368+
302369
}
303370

304371
#pragma mark - Utility Methods

Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ NS_ASSUME_NONNULL_BEGIN
2727
@property (nonatomic, assign) BOOL contextMenuHidden;
2828
@property (nonatomic, assign, getter=isEditable) BOOL editable;
2929
@property (nonatomic, assign) BOOL caretHidden;
30+
@property (nonatomic, assign) CGFloat caretYOffset;
31+
@property (nonatomic, assign) CGFloat caretHeight;
3032
@property (nonatomic, assign) BOOL enablesReturnKeyAutomatically;
3133
@property (nonatomic, assign) UITextFieldViewMode clearButtonMode;
3234
@property (nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;

Libraries/Text/TextInput/RCTBaseTextInputViewManager.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ @implementation RCTBaseTextInputViewManager {
4444
RCT_REMAP_VIEW_PROPERTY(selectionColor, backedTextInputView.tintColor, UIColor)
4545
RCT_REMAP_VIEW_PROPERTY(spellCheck, backedTextInputView.spellCheckingType, UITextSpellCheckingType)
4646
RCT_REMAP_VIEW_PROPERTY(caretHidden, backedTextInputView.caretHidden, BOOL)
47+
RCT_REMAP_VIEW_PROPERTY(caretYOffset, backedTextInputView.caretYOffset, CGFloat)
48+
RCT_REMAP_VIEW_PROPERTY(caretHeight, backedTextInputView.caretHeight, CGFloat)
4749
RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode)
4850
RCT_REMAP_VIEW_PROPERTY(scrollEnabled, backedTextInputView.scrollEnabled, BOOL)
4951
RCT_REMAP_VIEW_PROPERTY(secureTextEntry, backedTextInputView.secureTextEntry, BOOL)

React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
153153
_backedTextInputView.caretHidden = newTextInputProps.traits.caretHidden;
154154
}
155155

156+
if(newTextInputProps.traits.caretYOffset != oldTextInputProps.traits.caretYOffset) {
157+
_backedTextInputView.caretYOffset = newTextInputProps.traits.caretYOffset;
158+
}
159+
160+
if(newTextInputProps.traits.caretHeight != oldTextInputProps.traits.caretHeight) {
161+
_backedTextInputView.caretHeight = newTextInputProps.traits.caretHeight;
162+
}
163+
156164
if (newTextInputProps.traits.clearButtonMode != oldTextInputProps.traits.clearButtonMode) {
157165
_backedTextInputView.clearButtonMode =
158166
RCTUITextFieldViewModeFromTextInputAccessoryVisibilityMode(newTextInputProps.traits.clearButtonMode);

React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ void RCTCopyBackedTextInput(
3838
toTextInput.keyboardAppearance = fromTextInput.keyboardAppearance;
3939
toTextInput.spellCheckingType = fromTextInput.spellCheckingType;
4040
toTextInput.caretHidden = fromTextInput.caretHidden;
41+
toTextInput.caretYOffset = fromTextInput.caretYOffset;
42+
toTextInput.caretHeight = toTextInput.caretHeight;
4143
toTextInput.clearButtonMode = fromTextInput.clearButtonMode;
4244
toTextInput.scrollEnabled = fromTextInput.scrollEnabled;
4345
toTextInput.secureTextEntry = fromTextInput.secureTextEntry;

ReactCommon/react/renderer/components/textinput/iostextinput/primitives.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,19 @@ class TextInputTraits final {
156156
*/
157157
bool caretHidden{false};
158158

159+
/*
160+
* iOS-only (inherently iOS-specific)
161+
* Default value: 0 with a default font.
162+
*/
163+
164+
int caretHeight{0};
165+
166+
/*
167+
* iOS-only (inherently iOS-specific)
168+
* Default value: 0 means that the caret offset will have the default value
169+
*/
170+
int caretYOffset{0};
171+
159172
/*
160173
* Controls the visibility of a `Clean` button.
161174
* iOS-only (implemented only on iOS for now)

0 commit comments

Comments
 (0)