From 86a6704cc2e4c0aeab93b325fc69f626dc8bcc50 Mon Sep 17 00:00:00 2001 From: bang9dev Date: Tue, 30 Mar 2021 22:18:45 +0900 Subject: [PATCH] feat: added line break strategy attribute and example --- .../TextInput/RCTTextInputViewConfig.js | 1 + Libraries/Components/TextInput/TextInput.js | 6 +++ .../Text/BaseText/RCTBaseTextViewManager.m | 1 + Libraries/Text/RCTTextAttributes.h | 1 + Libraries/Text/RCTTextAttributes.m | 20 ++++++--- Libraries/Text/TextNativeComponent.js | 1 + Libraries/Text/TextProps.js | 7 ++++ React/Base/RCTConvert.h | 1 + React/Base/RCTConvert.m | 32 +++++++++++++++ .../js/examples/Text/TextExample.ios.js | 39 ++++++++++++++++++ .../TextInput/TextInputExample.ios.js | 41 +++++++++++++++++++ 11 files changed, 145 insertions(+), 5 deletions(-) diff --git a/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/Libraries/Components/TextInput/RCTTextInputViewConfig.js index ecd4868759c5ff..1eb800e69b06e7 100644 --- a/Libraries/Components/TextInput/RCTTextInputViewConfig.js +++ b/Libraries/Components/TextInput/RCTTextInputViewConfig.js @@ -126,6 +126,7 @@ const RCTTextInputViewConfig = { selectTextOnFocus: true, text: true, clearTextOnFocus: true, + ios_lineBreakStrategy: true, }, }; diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 30e87a6de92ee3..5a26e2ebb947e7 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -304,6 +304,12 @@ type IOSProps = $ReadOnly<{| * @platform ios */ textContentType?: ?TextContentType, + + /** + * Set line break strategy on iOS. + * @platform ios + */ + ios_lineBreakStrategy?: ?('none' | 'standard' | 'hangul-word' | 'push-out'), |}>; type AndroidProps = $ReadOnly<{| diff --git a/Libraries/Text/BaseText/RCTBaseTextViewManager.m b/Libraries/Text/BaseText/RCTBaseTextViewManager.m index 48a83e5c67cf9b..a8691caf1a64d3 100644 --- a/Libraries/Text/BaseText/RCTBaseTextViewManager.m +++ b/Libraries/Text/BaseText/RCTBaseTextViewManager.m @@ -42,6 +42,7 @@ - (RCTShadowView *)shadowView RCT_REMAP_SHADOW_PROPERTY(lineHeight, textAttributes.lineHeight, CGFloat) RCT_REMAP_SHADOW_PROPERTY(textAlign, textAttributes.alignment, NSTextAlignment) RCT_REMAP_SHADOW_PROPERTY(writingDirection, textAttributes.baseWritingDirection, NSWritingDirection) +RCT_REMAP_SHADOW_PROPERTY(ios_lineBreakStrategy, textAttributes.lineBreakStrategy, NSLineBreakStrategy) // Decoration RCT_REMAP_SHADOW_PROPERTY(textDecorationColor, textAttributes.textDecorationColor, UIColor) RCT_REMAP_SHADOW_PROPERTY(textDecorationStyle, textAttributes.textDecorationStyle, NSUnderlineStyle) diff --git a/Libraries/Text/RCTTextAttributes.h b/Libraries/Text/RCTTextAttributes.h index ce043621ef6890..b666884b446528 100644 --- a/Libraries/Text/RCTTextAttributes.h +++ b/Libraries/Text/RCTTextAttributes.h @@ -41,6 +41,7 @@ extern NSString *const RCTTextAttributesTagAttributeName; @property (nonatomic, assign) CGFloat lineHeight; @property (nonatomic, assign) NSTextAlignment alignment; @property (nonatomic, assign) NSWritingDirection baseWritingDirection; +@property (nonatomic, assign) NSLineBreakStrategy lineBreakStrategy; // Decoration @property (nonatomic, strong, nullable) UIColor *textDecorationColor; @property (nonatomic, assign) NSUnderlineStyle textDecorationStyle; diff --git a/Libraries/Text/RCTTextAttributes.m b/Libraries/Text/RCTTextAttributes.m index e8df8b352ef013..bd48b41f91c860 100644 --- a/Libraries/Text/RCTTextAttributes.m +++ b/Libraries/Text/RCTTextAttributes.m @@ -27,6 +27,7 @@ - (instancetype)init _maxFontSizeMultiplier = NAN; _alignment = NSTextAlignmentNatural; _baseWritingDirection = NSWritingDirectionNatural; + _lineBreakStrategy = NSLineBreakStrategyNone; _textShadowRadius = NAN; _opacity = NAN; _textTransform = RCTTextTransformUndefined; @@ -61,6 +62,7 @@ - (void)applyTextAttributes:(RCTTextAttributes *)textAttributes _lineHeight = !isnan(textAttributes->_lineHeight) ? textAttributes->_lineHeight : _lineHeight; _alignment = textAttributes->_alignment != NSTextAlignmentNatural ? textAttributes->_alignment : _alignment; // * _baseWritingDirection = textAttributes->_baseWritingDirection != NSWritingDirectionNatural ? textAttributes->_baseWritingDirection : _baseWritingDirection; // * + _lineBreakStrategy = textAttributes->_lineBreakStrategy != NSLineBreakStrategyNone ? textAttributes->_lineBreakStrategy : _lineBreakStrategy; // Decoration _textDecorationColor = textAttributes->_textDecorationColor ?: _textDecorationColor; @@ -93,27 +95,34 @@ - (NSParagraphStyle *)effectiveParagraphStyle alignment = NSTextAlignmentRight; } } - + paragraphStyle.alignment = alignment; isParagraphStyleUsed = YES; } - + if (_baseWritingDirection != NSWritingDirectionNatural) { paragraphStyle.baseWritingDirection = _baseWritingDirection; isParagraphStyleUsed = YES; } - + + if (_lineBreakStrategy != NSLineBreakStrategyNone) { + if (@available(iOS 14.0, *)) { + paragraphStyle.lineBreakStrategy = _lineBreakStrategy; + isParagraphStyleUsed = YES; + } + } + if (!isnan(_lineHeight)) { CGFloat lineHeight = _lineHeight * self.effectiveFontSizeMultiplier; paragraphStyle.minimumLineHeight = lineHeight; paragraphStyle.maximumLineHeight = lineHeight; isParagraphStyleUsed = YES; } - + if (isParagraphStyleUsed) { return [paragraphStyle copy]; } - + return nil; } @@ -294,6 +303,7 @@ - (BOOL)isEqual:(RCTTextAttributes *)textAttributes RCTTextAttributesCompareFloats(_lineHeight) && RCTTextAttributesCompareFloats(_alignment) && RCTTextAttributesCompareOthers(_baseWritingDirection) && + RCTTextAttributesCompareOthers(_lineBreakStrategy) && // Decoration RCTTextAttributesCompareObjects(_textDecorationColor) && RCTTextAttributesCompareOthers(_textDecorationStyle) && diff --git a/Libraries/Text/TextNativeComponent.js b/Libraries/Text/TextNativeComponent.js index 858aaba750f19b..5c88dc85535550 100644 --- a/Libraries/Text/TextNativeComponent.js +++ b/Libraries/Text/TextNativeComponent.js @@ -42,6 +42,7 @@ export const NativeText: HostComponent = (createReactNativeComp onInlineViewLayout: true, dataDetectorType: true, android_hyphenationFrequency: true, + ios_lineBreakStrategy: true, }, directEventTypes: { topTextLayout: { diff --git a/Libraries/Text/TextProps.js b/Libraries/Text/TextProps.js index b2a39e58df768e..dbf29e0d85a145 100644 --- a/Libraries/Text/TextProps.js +++ b/Libraries/Text/TextProps.js @@ -205,4 +205,11 @@ export type TextProps = $ReadOnly<{| * See https://reactnative.dev/docs/text.html#supperhighlighting */ suppressHighlighting?: ?boolean, + + /** + * Set line break strategy on iOS. + * + * See https://reactnative.dev/docs/text.html#ios_linebreakstrategy + */ + ios_lineBreakStrategy?: ?('none' | 'standard' | 'hangul-word' | 'push-out'), |}>; diff --git a/React/Base/RCTConvert.h b/React/Base/RCTConvert.h index 1137788402b12b..2459b38489031d 100644 --- a/React/Base/RCTConvert.h +++ b/React/Base/RCTConvert.h @@ -64,6 +64,7 @@ typedef NSURL RCTFileURL; + (NSTextAlignment)NSTextAlignment:(id)json; + (NSUnderlineStyle)NSUnderlineStyle:(id)json; + (NSWritingDirection)NSWritingDirection:(id)json; ++ (NSLineBreakStrategy)NSLineBreakStrategy:(id)json; + (UITextAutocapitalizationType)UITextAutocapitalizationType:(id)json; + (UITextFieldViewMode)UITextFieldViewMode:(id)json; + (UIKeyboardType)UIKeyboardType:(id)json; diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index cced186f7a1a33..bf249824f160f4 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -366,6 +366,38 @@ + (NSLocale *)NSLocale:(id)json NSWritingDirectionNatural, integerValue) ++ (NSLineBreakStrategy)NSLineBreakStrategy:(id)json RCT_DYNAMIC +{ + static NSDictionary *mapping; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(iOS 14.0, *)) { + mapping = @{ + @"none" : @(NSLineBreakStrategyNone), +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 + @"standard" : @(NSLineBreakStrategyStandard), + @"hangul-word" : @(NSLineBreakStrategyHangulWordPriority), + @"push-out": @(NSLineBreakStrategyPushOut) +#else + @"standard" : @(NSLineBreakStrategyNone), + @"hangul-word" : @(NSLineBreakStrategyNone), + @"push-out": @(NSLineBreakStrategyNone) +#endif + }; + } else { + mapping = @{ + @"none" : @(NSLineBreakStrategyNone), + @"standard" : @(NSLineBreakStrategyNone), + @"hangul-word" : @(NSLineBreakStrategyNone), + @"push-out": @(NSLineBreakStrategyNone) + }; + } + }); + + NSLineBreakStrategy type = RCTConvertEnumValue("NSLineBreakStrategy", mapping, @(NSLineBreakStrategyNone), json).integerValue; + return type; +} + RCT_ENUM_CONVERTER( UITextAutocapitalizationType, (@{ diff --git a/packages/rn-tester/js/examples/Text/TextExample.ios.js b/packages/rn-tester/js/examples/Text/TextExample.ios.js index 40cf7c1b3b7e02..b04b9b6c1e0674 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.ios.js +++ b/packages/rn-tester/js/examples/Text/TextExample.ios.js @@ -1148,4 +1148,43 @@ exports.examples = [ ); }, }, + { + 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}]`} + + {textByCode[code]} + + + ); + })} + + ); + })} + + ); + }, + }, ]; diff --git a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js index 5a31407a0307cb..d7fda4fc7992fd 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js @@ -816,4 +816,45 @@ exports.examples = ([ ); }, }, + { + 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);