From 9ae1c50410a4fc1f4cd3809ba1fa56635fc41c67 Mon Sep 17 00:00:00 2001 From: fabriziobertoglio1987 Date: Thu, 11 May 2023 23:21:18 +0800 Subject: [PATCH 01/31] basic working functionality --- .../Libraries/Text/RCTTextAttributes.m | 16 +- .../RCTBackedTextInputViewProtocol.h | 2 + .../TextInput/RCTBaseTextInputShadowView.m | 69 ++- .../Text/TextInput/RCTBaseTextInputView.h | 2 + .../Text/TextInput/RCTBaseTextInputView.m | 14 + .../TextInput/Singleline/RCTUITextField.h | 2 + .../TextInput/Singleline/RCTUITextField.m | 91 ++- .../TextInput/TextInputExample.ios.js | 581 +----------------- .../TextInput/TextInputSharedExamples.js | 429 +++++-------- 9 files changed, 343 insertions(+), 863 deletions(-) diff --git a/packages/react-native/Libraries/Text/RCTTextAttributes.m b/packages/react-native/Libraries/Text/RCTTextAttributes.m index c8323388ce684b..c379f5feeeafc1 100644 --- a/packages/react-native/Libraries/Text/RCTTextAttributes.m +++ b/packages/react-native/Libraries/Text/RCTTextAttributes.m @@ -145,7 +145,6 @@ - (NSParagraphStyle *)effectiveParagraphStyle - (NSDictionary *)effectiveTextAttributes { NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:10]; - // Font UIFont *font = self.effectiveFont; if (font) { @@ -172,6 +171,21 @@ - (NSParagraphStyle *)effectiveParagraphStyle NSParagraphStyle *paragraphStyle = [self effectiveParagraphStyle]; if (paragraphStyle) { attributes[NSParagraphStyleAttributeName] = paragraphStyle; + if(!isnan(paragraphStyle.maximumLineHeight)) { + if (paragraphStyle.maximumLineHeight > font.lineHeight) { + CGFloat baseLineOffset = (paragraphStyle.maximumLineHeight - font.lineHeight) / 2.0; + // ORIGINAL API ---> COMMENT HERE + attributes[NSBaselineOffsetAttributeName] = @(baseLineOffset); + } else { + // attributes[NSBaselineOffsetAttributeName] = @(50); + if (paragraphStyle.maximumLineHeight < font.lineHeight) { + // + } + // CGFloat baseLineOffset = paragraphStyle.maximumLineHeight - font.lineHeight; + // CGFloat previousBaseline = [attributes[NSBaselineOffsetAttributeName] floatValue]; + // attributes[NSBaselineOffsetAttributeName] = @(previousBaseline - baseLineOffset); + } + } } // Decoration diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h index 686af9e3a363e2..3c32d04ebe26aa 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +++ b/packages/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h @@ -34,6 +34,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, readonly) CGFloat zoomScale; @property (nonatomic, assign, readonly) CGPoint contentOffset; @property (nonatomic, assign, readonly) UIEdgeInsets contentInset; +@property (nonatomic, assign) CGRect reactTextInsets; +@property (nonatomic, assign) CGRect reactEditingInsets; // This protocol disallows direct access to `selectedTextRange` property because // unwise usage of it can break the `delegate` behavior. So, we always have to diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputShadowView.m b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputShadowView.m index 04d2446f86d9b3..b708e84230be4b 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputShadowView.m +++ b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputShadowView.m @@ -59,6 +59,7 @@ - (void)didSetProps:(NSArray *)changedProps - (void)layoutSubviewsWithContext:(RCTLayoutContext)layoutContext { // Do nothing. + // add your logic here } - (void)setLocalData:(NSObject *)localData @@ -177,7 +178,23 @@ - (void)uiManagerWillPerformMounting baseTextInputView.textAttributes = textAttributes; baseTextInputView.reactBorderInsets = borderInsets; - baseTextInputView.reactPaddingInsets = paddingInsets; + + // add logic when fontSize not provided + if (!isnan(textAttributes.lineHeight) && !isnan(textAttributes.effectiveFont.lineHeight)) { + if (textAttributes.lineHeight > textAttributes.effectiveFont.lineHeight) { + CGFloat height = self.layoutMetrics.frame.size.height; + CGFloat width = self.layoutMetrics.frame.size.width; + CGFloat padding = (height - textAttributes.lineHeight) / 2.0; + baseTextInputView.reactTextInsets = CGRectMake(0, padding, width, height / 2.0); + baseTextInputView.reactEditingInsets = CGRectMake(0, padding, width, height); + } else { + baseTextInputView.reactPaddingInsets = paddingInsets; + } + } else { + // add logic to handle textAttributes.effectiveFont.lineHeight > textAttributes.lineHeight + } + // ORIGINAL API ---> COMMENT HERE + // baseTextInputView.reactPaddingInsets = paddingInsets; if (newAttributedText) { // Don't set `attributedText` if length equal to zero, otherwise it would shrink when attributes contain like @@ -243,7 +260,7 @@ - (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximu - (CGFloat)lastBaselineForSize:(CGSize)size { NSAttributedString *attributedText = [self measurableAttributedText]; - + __block CGFloat maximumDescender = 0.0; [attributedText enumerateAttribute:NSFontAttributeName @@ -255,7 +272,53 @@ - (CGFloat)lastBaselineForSize:(CGSize)size } }]; - return size.height + maximumDescender; + return size.height + maximumDescender + 100; +} + +- (void)postprocessAttributedText:(NSMutableAttributedString *)attributedText +{ + __block CGFloat maximumLineHeight = 0; + + [attributedText enumerateAttribute:NSParagraphStyleAttributeName + inRange:NSMakeRange(0, attributedText.length) + options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(NSParagraphStyle *paragraphStyle, __unused NSRange range, __unused BOOL *stop) { + if (!paragraphStyle) { + return; + } + + maximumLineHeight = MAX(paragraphStyle.maximumLineHeight, maximumLineHeight); + }]; + + if (maximumLineHeight == 0) { + // `lineHeight` was not specified, nothing to do. + return; + } + + __block CGFloat maximumFontLineHeight = 0; + + [attributedText enumerateAttribute:NSFontAttributeName + inRange:NSMakeRange(0, attributedText.length) + options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(UIFont *font, NSRange range, __unused BOOL *stop) { + if (!font) { + return; + } + + if (maximumFontLineHeight <= font.lineHeight) { + maximumFontLineHeight = font.lineHeight; + } + }]; + + if (maximumLineHeight < maximumFontLineHeight) { + return; + } + + CGFloat baseLineOffset = maximumLineHeight / 2.0 - maximumFontLineHeight / 2.0; + + [attributedText addAttribute:NSBaselineOffsetAttributeName + value:@(baseLineOffset) + range:NSMakeRange(0, attributedText.length)]; } static YGSize RCTBaseTextInputShadowViewMeasure( diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.h b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.h index 209947de9b4aaa..b307db0f0ace2a 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.h +++ b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.h @@ -31,6 +31,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) RCTTextAttributes *textAttributes; @property (nonatomic, assign) UIEdgeInsets reactPaddingInsets; @property (nonatomic, assign) UIEdgeInsets reactBorderInsets; +@property (nonatomic, assign) CGRect reactTextInsets; +@property (nonatomic, assign) CGRect reactEditingInsets; @property (nonatomic, copy, nullable) RCTDirectEventBlock onContentSizeChange; @property (nonatomic, copy, nullable) RCTDirectEventBlock onSelectionChange; diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m index cb496feb2791d1..3d136924e26f70 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m +++ b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m @@ -84,6 +84,20 @@ - (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets [self setNeedsLayout]; } +- (void)setReactTextInsets:(CGRect)reactTextInsets +{ + _reactTextInsets = reactTextInsets; + self.backedTextInputView.reactTextInsets = reactTextInsets; + [self setNeedsLayout]; +} + +- (void)setReactEditingInsets:(CGRect)reactEditingInsets +{ + _reactEditingInsets = reactEditingInsets; + self.backedTextInputView.reactEditingInsets = reactEditingInsets; + [self setNeedsLayout]; +} + - (void)setReactBorderInsets:(UIEdgeInsets)reactBorderInsets { _reactBorderInsets = reactBorderInsets; diff --git a/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.h b/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.h index b26b41f8693d7b..9884372ee5cd94 100644 --- a/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.h +++ b/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.h @@ -32,6 +32,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, readonly) CGFloat zoomScale; @property (nonatomic, assign, readonly) CGPoint contentOffset; @property (nonatomic, assign, readonly) UIEdgeInsets contentInset; +@property (nonatomic, assign) CGRect reactTextInsets; +@property (nonatomic, assign) CGRect reactEditingInsets; @end diff --git a/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.m b/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.m index 3d116019b89cde..d49e41988df6e9 100644 --- a/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.m +++ b/packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.m @@ -12,6 +12,42 @@ #import #import +//the UITextSelectionRect subclass needs to be created because the original version is not writable +@interface CustomTextSelectionRect : UITextSelectionRect + +@property (nonatomic) CGRect _rect; +@property (nonatomic) NSWritingDirection _writingDirection; +@property (nonatomic) BOOL _containsStart; // Returns YES if the rect contains the start of the selection. +@property (nonatomic) BOOL _containsEnd; // Returns YES if the rect contains the end of the selection. +@property (nonatomic) BOOL _isVertical; // Returns YES if the rect is for vertically oriented text. + +@end + +@implementation CustomTextSelectionRect + +- (CGRect)rect { + return __rect; +} + +- (NSWritingDirection)writingDirection { + return __writingDirection; +} + +- (BOOL)containsStart { + return __containsStart; +} + +- (BOOL)containsEnd { + return __containsEnd; +} + +- (BOOL)isVertical { + return __isVertical; +} + +@end + + @implementation RCTUITextField { RCTBackedTextFieldDelegateAdapter *_textInputDelegateAdapter; NSDictionary *_defaultTextAttributes; @@ -121,7 +157,10 @@ - (void)setSecureTextEntry:(BOOL)secureTextEntry { NSMutableDictionary *textAttributes = [_defaultTextAttributes mutableCopy] ?: [NSMutableDictionary new]; - + CGFloat baseLineOffset = [[textAttributes objectForKey:NSBaselineOffsetAttributeName] floatValue]; + if (baseLineOffset > 0) { + // [textAttributes setValue:@(baseLineOffset / 2.0) forKey:NSBaselineOffsetAttributeName]; + } if (self.placeholderColor) { [textAttributes setValue:self.placeholderColor forKey:NSForegroundColorAttributeName]; } else { @@ -146,23 +185,67 @@ - (BOOL)canPerformAction:(SEL)action withSender:(id)sender - (CGRect)caretRectForPosition:(UITextPosition *)position { + CGRect originalRect = [super caretRectForPosition:position]; + if (_caretHidden) { return CGRectZero; } + + // NSNumber *baseLineOffset = [self.attributedText valueForKey:NSBaselineOffsetAttributeName]; + // CGFloat baseLineOffset = 0; + // CGFloat zero = 0; + // search how to do this in objc with float + // if (![baseLineOffset isEqual:0]) { + // originalRect.origin.y -= [[self.defaultTextAttributes valueForKey:NSBaselineOffsetAttributeName] floatValue] / 2.0; + // } + + return originalRect; +} - return [super caretRectForPosition:position]; +/* +- (NSArray *)selectionRectsForRange:(UITextRange *)range { + NSArray *superRects = [super selectionRectsForRange:range]; + NSMutableArray *customTextSelectionRects = [NSMutableArray array]; + + for (UITextSelectionRect *rect in superRects) { + CustomTextSelectionRect *customTextRect = [[CustomTextSelectionRect alloc] init]; + + customTextRect._rect = CGRectMake(rect.rect.origin.x, rect.rect.origin.y - [self.defaultTextAttributes[NSBaselineOffsetAttributeName] floatValue] / 2 , rect.rect.size.width, rect.rect.size.height); + customTextRect._writingDirection = rect.writingDirection; + customTextRect._containsStart = rect.containsStart; + customTextRect._containsEnd = rect.containsEnd; + customTextRect._isVertical = rect.isVertical; + [customTextSelectionRects addObject:customTextRect]; + } + + return customTextSelectionRects; + } + */ #pragma mark - Positioning Overrides - (CGRect)textRectForBounds:(CGRect)bounds { - return UIEdgeInsetsInsetRect([super textRectForBounds:bounds], _textContainerInset); + if (self.reactTextInsets.size.height > 0) { + return self.reactTextInsets; + } else { + return UIEdgeInsetsInsetRect([super textRectForBounds:bounds], _textContainerInset); + } } - (CGRect)editingRectForBounds:(CGRect)bounds { - return [self textRectForBounds:bounds]; + if (self.reactEditingInsets.size.height > 0) { + return self.reactEditingInsets; + } else { + return UIEdgeInsetsInsetRect([super textRectForBounds:bounds], _textContainerInset); + } +} + +- (CGRect)placeholderRectForBounds:(CGRect)bounds +{ + return UIEdgeInsetsInsetRect([super textRectForBounds:bounds], _textContainerInset); } #pragma mark - Overrides diff --git a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js index b0563e0d2f384d..ee8e0207a5d865 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js @@ -327,583 +327,4 @@ exports.title = 'TextInput'; exports.documentationURL = 'https://reactnative.dev/docs/textinput'; exports.category = 'Basic'; exports.description = 'Single and multi-line text inputs.'; -exports.examples = ([ - ...TextInputSharedExamples, - { - title: 'Live Re-Write (ひ -> 日)', - render: function (): React.Node { - return ; - }, - }, - { - title: 'Keyboard Input Accessory View', - render: function (): React.Node { - return ( - - - - - ); - }, - }, - { - title: "Default Input Accessory View with returnKeyType = 'done'", - render: function (): React.Node { - const keyboardTypesWithDoneButton = [ - 'number-pad', - 'phone-pad', - 'decimal-pad', - 'ascii-capable-number-pad', - ]; - const examples = keyboardTypesWithDoneButton.map(type => { - return ( - - - - ); - }); - return {examples}; - }, - }, - { - title: 'Nested content and `value` property', - render: function (): React.Node { - return ( - - - - (first raw text node) - (internal raw text node) - (last raw text node) - - - - - (first raw text node) - (internal raw text node) - (last raw text node) - - - - ); - }, - }, - { - title: 'Keyboard appearance', - render: function (): React.Node { - const keyboardAppearance = ['default', 'light', 'dark']; - const examples = keyboardAppearance.map(type => { - return ( - - - - ); - }); - return {examples}; - }, - }, - { - title: 'Return key types', - render: function (): React.Node { - const returnKeyTypes = [ - 'default', - 'go', - 'google', - 'join', - 'next', - 'route', - 'search', - 'send', - 'yahoo', - 'done', - 'emergency-call', - ]; - const examples = returnKeyTypes.map(type => { - return ( - - - - ); - }); - return {examples}; - }, - }, - { - title: 'Enable return key automatically', - render: function (): React.Node { - return ( - - - - - - ); - }, - }, - { - title: 'Secure text entry', - render: function (): React.Node { - return ; - }, - }, - { - title: 'Colored input text', - render: function (): React.Node { - return ( - - - - - ); - }, - }, - { - title: 'Colored highlight/cursor for text input', - render: function (): React.Node { - return ( - - - - - ); - }, - }, - { - title: 'Clear button mode', - render: function (): React.Node { - const clearButtonModes = [ - 'never', - 'while-editing', - 'unless-editing', - 'always', - ]; - const examples = clearButtonModes.map(mode => { - return ( - - - - ); - }); - return {examples}; - }, - }, - { - title: 'Clear and select', - render: function (): React.Node { - return ( - - - - - - - - - - - - - - - ); - }, - }, - { - title: 'Multiline blur on submit', - render: function (): React.Node { - return ( - - - Alert.alert('Alert', event.nativeEvent.text) - } - /> - - ); - }, - }, - { - title: 'Multiline', - render: function (): React.Node { - return ( - - - - - - - - ); - }, - }, - { - title: 'Editable and Read only', - render: function (): React.Node { - return ( - - - - - - - ); - }, - }, - { - title: 'TextInput Intrinsic Size', - render: function (): React.Node { - return ( - - Singleline TextInput - - - - Multiline TextInput - - - - - - - - ); - }, - }, - { - title: 'Auto-expanding', - render: function (): React.Node { - return ( - - - - ); - }, - }, - { - title: 'Auto-expanding', - render: function (): React.Node { - return ( - - - huge - generic generic generic - - small small small small small small - - regular regular - - huge huge huge huge huge - - generic generic generic - - - ); - }, - }, - { - title: 'TextInput maxLength', - render: function (): React.Node { - return ( - - - - - - - - - - - - - - - ); - }, - }, - { - title: 'Text Auto Complete', - render: function (): React.Node { - return ( - - - - - - - - - ); - }, - }, - { - title: 'Text Content Type', - render: function (): React.Node { - return ( - - - - - - - - - - - - ); - }, - }, - { - title: 'TextInput Placeholder Styles', - render: function (): React.Node { - return ( - - - - - - - - - ); - }, - }, - { - title: 'showSoftInputOnFocus', - render: function (): React.Node { - return ( - - - - - - ); - }, - }, - { - title: 'Line Break Strategy', - render: function (): React.Node { - const lineBreakStrategy = ['none', 'standard', 'hangul-word', 'push-out']; - const textByCode = { - en: 'lineBreakStrategy lineBreakStrategy lineBreakStrategy lineBreakStrategy', - ko: '한글개행한글개행 한글개행한글개행 한글개행한글개행 한글개행한글개행 한글개행한글개행 한글개행한글개행', - ja: 'かいぎょう かいぎょう かいぎょう かいぎょう かいぎょう かいぎょう', - cn: '改行 改行 改行 改行 改行 改行 改行 改行 改行 改行 改行 改行', - }; - return ( - - {lineBreakStrategy.map(strategy => { - return ( - - {`Strategy: ${strategy}`} - {Object.keys(textByCode).map(code => { - return ( - - {`[${code}]`} - - - ); - })} - - ); - })} - - ); - }, - }, -]: Array); +exports.examples = ([...TextInputSharedExamples]: Array); diff --git a/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js b/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js index c05948b0639800..2ac05d411aa7c8 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js @@ -25,8 +25,24 @@ import type {TextStyle} from 'react-native/Libraries/StyleSheet/StyleSheet'; import RNTesterButton from '../../components/RNTesterButton'; import {RNTesterThemeContext} from '../../components/RNTesterTheme'; import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; +import {set} from '../../../../react-native/Libraries/Settings/Settings.ios'; +import {T} from '../../../../react-native/sdks/hermes/tools/hermes-parser/js/flow-api-translator/__tests__/flowDefToTSDef/fixtures/export/declare/named/specifiers/spec'; const styles = StyleSheet.create({ + normalTextInput: { + height: 50, + width: 200, + backgroundColor: 'white', + padding: 0, + fontSize: 14, + marginTop: 10, + borderWidth: 1, + }, + wrapper: { + height: '100%', + width: '100%', + backgroundColor: 'lightgray', + }, default: { borderWidth: StyleSheet.hairlineWidth, borderColor: '#0f0f0f', @@ -834,286 +850,149 @@ function MultilineStyledTextInput({ ); } -module.exports = ([ - { - title: 'Auto-focus', - render: function (): React.Node { - return ( +function LineHeightExample() { + const LINE_HEIGHT = 10; + const HEIGHT = 72; + const STEP = 5; + const [lineHeight, setLineHeight] = React.useState(LINE_HEIGHT); + const [height, setHeight] = React.useState(HEIGHT); + const [font, setFont] = React.useState(10); + const [padding, setPadding] = React.useState(0); + const [yoga, setYoga] = React.useState(false); + const [borderWidth, setBorderWidth] = React.useState(1); + const increase = prev => { + if (prev + STEP > 150) return 150; + return prev + STEP; + }; + const decrease = prev => { + if (prev - STEP <= 0) return 0; + return prev - STEP; + }; + const increaseLineHeight = () => { + if (increase(lineHeight) > height) { + setLineHeight(height); + console.log( + 'we are not increasing the lineHeight which stays at: ' + lineHeight, + ); + } else { + setLineHeight(increase); + } + }; + const changeState = prev => !prev; + const enableYoga = () => setYoga(changeState); + const yogaStyles = yoga ? {display: 'flex', flexDirection: 'row'} : {}; + const textInputStyles = { + height, + minWidth: 120, + lineHeight, + borderWidth: 1, + fontSize: font, + paddingTop: padding, + borderWidth, + }; + const resetState = () => { + setLineHeight(LINE_HEIGHT); + setHeight(HEIGHT); + setFont(10); + setPadding(0); + setYoga(false); + setBorderWidth(1); + }; + return ( + + lineHeight is {lineHeight} + height is {height} + + - ); - }, - }, - { - name: 'maxLength', - title: "Live Re-Write ( -> '_') + maxLength", - render: function (): React.Node { - return ; - }, - }, - { - title: 'Live Re-Write (no spaces allowed)', - render: function (): React.Node { - return ; - }, - }, - { - name: 'clearButton', - title: 'Live Re-Write (no spaces allowed) and clear', - render: function (): React.Node { - return ; - }, - }, - { - title: 'Auto-capitalize', - name: 'autoCapitalize', - render: function (): React.Node { - return ( - - - - - - - - - - - - - - - ); - }, - }, - { - title: 'Auto-correct', - render: function (): React.Node { - return ( - - - - - - - - - ); - }, - }, - { - title: 'Keyboard types', - name: 'keyboardTypes', - render: function (): React.Node { - const keyboardTypes = [ - 'default', - 'ascii-capable', - 'numbers-and-punctuation', - 'url', - 'number-pad', - 'phone-pad', - 'name-phone-pad', - 'email-address', - 'decimal-pad', - 'twitter', - 'web-search', - 'ascii-capable-number-pad', - 'numeric', - ]; - const examples = keyboardTypes.map(type => { - return ( - - - - ); - }); - return {examples}; - }, - }, - { - title: 'Input modes', - name: 'inputModes', - render: function (): React.Node { - const inputMode = [ - 'none', - 'text', - 'decimal', - 'numeric', - 'tel', - 'search', - 'email', - 'url', - ]; - const examples = inputMode.map(mode => { - return ( - - - - ); - }); - return {examples}; - }, - }, - { - title: 'Blur on submit', - render: function (): React.Element { - return ; - }, - }, - { - title: 'enterKeyHint modes', - name: 'enterKeyHintTypes', - render: function (): React.Node { - const enterKeyHintTypesHints = [ - 'enter', - 'done', - 'go', - 'next', - 'previous', - 'search', - 'send', - ]; - const examples = enterKeyHintTypesHints.map(hint => { - return ( - - - - ); - }); - return {examples}; - }, - }, - { - title: 'Submit behavior', - render: function (): React.Element { - return ; - }, - }, - { - title: 'Event handling', - render: function (): React.Element { - return ; - }, - }, - { - title: 'fontFamily, fontWeight and fontStyle', - render: function (): React.Node { - const fontFamilyA = Platform.OS === 'ios' ? 'Cochin' : 'sans-serif'; - const fontFamilyB = Platform.OS === 'ios' ? 'Courier' : 'serif'; + +