Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS textTransform style support #18387

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Libraries/StyleSheet/StyleSheetTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ export type TextStyle = $ReadOnly<{|
| 'underline line-through',
textDecorationStyle?: 'solid' | 'double' | 'dotted' | 'dashed',
textDecorationColor?: ColorValue,
textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase',
writingDirection?: 'auto' | 'ltr' | 'rtl',
|}>;

Expand Down
2 changes: 1 addition & 1 deletion Libraries/Text/BaseText/RCTBaseTextShadowView.m
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ - (NSAttributedString *)attributedTextWithBaseTextAttributes:(nullable RCTTextAt
NSString *text = rawTextShadowView.text;
if (text) {
NSAttributedString *rawTextAttributedString =
[[NSAttributedString alloc] initWithString:rawTextShadowView.text
[[NSAttributedString alloc] initWithString:[textAttributes applyTextAttributesToText:text]
attributes:textAttributes.effectiveTextAttributes];
[attributedText appendAttributedString:rawTextAttributedString];
}
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/BaseText/RCTBaseTextViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@ - (RCTShadowView *)shadowView
RCT_REMAP_SHADOW_PROPERTY(textShadowColor, textAttributes.textShadowColor, UIColor)
// Special
RCT_REMAP_SHADOW_PROPERTY(isHighlighted, textAttributes.isHighlighted, BOOL)
RCT_REMAP_SHADOW_PROPERTY(textTransform, textAttributes.textTransform, RCTTextTransform)

@end
7 changes: 7 additions & 0 deletions Libraries/Text/RCTTextAttributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>

#import <React/RCTTextDecorationLineType.h>
#import <React/RCTTextTransform.h>

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -50,6 +51,7 @@ extern NSString *const RCTTextAttributesTagAttributeName;
@property (nonatomic, assign) BOOL isHighlighted;
@property (nonatomic, strong, nullable) NSNumber *tag;
@property (nonatomic, assign) UIUserInterfaceLayoutDirection layoutDirection;
@property (nonatomic, assign) RCTTextTransform textTransform;

#pragma mark - Inheritance

Expand Down Expand Up @@ -78,6 +80,11 @@ extern NSString *const RCTTextAttributesTagAttributeName;
- (UIColor *)effectiveForegroundColor;
- (UIColor *)effectiveBackgroundColor;

/**
* Text transformed per 'none', 'uppercase', 'lowercase', 'capitalize'
*/
- (NSString *)applyTextAttributesToText:(NSString *)text;

@end

NS_ASSUME_NONNULL_END
20 changes: 19 additions & 1 deletion Libraries/Text/RCTTextAttributes.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ - (instancetype)init
_baseWritingDirection = NSWritingDirectionNatural;
_textShadowRadius = NAN;
_opacity = NAN;
_textTransform = RCTTextTransformUndefined;
}

return self;
Expand Down Expand Up @@ -73,6 +74,7 @@ - (void)applyTextAttributes:(RCTTextAttributes *)textAttributes
_isHighlighted = textAttributes->_isHighlighted || _isHighlighted; // *
_tag = textAttributes->_tag ?: _tag;
_layoutDirection = textAttributes->_layoutDirection != UIUserInterfaceLayoutDirectionLeftToRight ? textAttributes->_layoutDirection : _layoutDirection;
_textTransform = textAttributes->_textTransform != RCTTextTransformUndefined ? textAttributes->_textTransform : _textTransform;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is why we have to have undefined value!

}

- (NSDictionary<NSAttributedStringKey, id> *)effectiveTextAttributes
Expand Down Expand Up @@ -214,6 +216,21 @@ - (UIColor *)effectiveBackgroundColor
return effectiveBackgroundColor ?: [UIColor clearColor];
}

- (NSString *)applyTextAttributesToText:(NSString *)text
{
switch (_textTransform) {
case RCTTextTransformUndefined:
case RCTTextTransformNone:
return text;
case RCTTextTransformLowercase:
return [text lowercaseString];
case RCTTextTransformUppercase:
return [text uppercaseString];
case RCTTextTransformCapitalize:
return [text capitalizedString];
}
}

- (RCTTextAttributes *)copyWithZone:(NSZone *)zone
{
RCTTextAttributes *textAttributes = [RCTTextAttributes new];
Expand Down Expand Up @@ -263,7 +280,8 @@ - (BOOL)isEqual:(RCTTextAttributes *)textAttributes
// Special
RCTTextAttributesCompareOthers(_isHighlighted) &&
RCTTextAttributesCompareObjects(_tag) &&
RCTTextAttributesCompareOthers(_layoutDirection);
RCTTextAttributesCompareOthers(_layoutDirection) &&
RCTTextAttributesCompareOthers(_textTransform);
}

@end
6 changes: 6 additions & 0 deletions Libraries/Text/TextStylePropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ const TextStylePropTypes = {
* @platform ios
*/
textDecorationColor: ColorPropType,
/**
* @platform ios
*/
textTransform: ReactPropTypes.oneOf(
['none' /*default*/, 'capitalize', 'uppercase', 'lowercase']
),
/**
* @platform ios
*/
Expand Down
36 changes: 36 additions & 0 deletions RNTester/js/TextExample.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,42 @@ exports.examples = [
title: 'Text `alignItems: \'baseline\'` style',
render: function() {
return <TextBaseLineLayoutExample />;
}
},
{
title: 'Transform',
render: function() {
return (
<View>
<Text style={{ textTransform: 'uppercase'}}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a test with <Text> with no textTransform style.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This text should be uppercased.
</Text>
<Text style={{ textTransform: 'lowercase'}}>
This TEXT SHOULD be lowercased.
</Text>
<Text style={{ textTransform: 'capitalize'}}>
This text should be CAPITALIZED.
</Text>
<Text style={{ textTransform: 'capitalize'}}>
Mixed:{' '}
<Text style={{ textTransform: 'uppercase'}}>
uppercase{' '}
</Text>
<Text style={{ textTransform: 'lowercase'}}>
LoWeRcAsE{' '}
</Text>
<Text style={{ textTransform: 'capitalize'}}>
capitalize each word
</Text>
</Text>
<Text>Should be "ABC":
<Text style={{ textTransform: 'uppercase' }}>a<Text>b</Text>c</Text>
</Text>
<Text>Should be "AbC":
<Text style={{ textTransform: 'uppercase' }}>a<Text style={{ textTransform: 'none' }}>b</Text>c</Text>
</Text>
</View>
);
},
},
];
2 changes: 2 additions & 0 deletions React/Base/RCTConvert.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#import <React/RCTLog.h>
#import <React/RCTPointerEvents.h>
#import <React/RCTTextDecorationLineType.h>
#import <React/RCTTextTransform.h>
#import <yoga/Yoga.h>

/**
Expand Down Expand Up @@ -124,6 +125,7 @@ typedef BOOL css_backface_visibility_t;
+ (RCTAnimationType)RCTAnimationType:(id)json;
+ (RCTBorderStyle)RCTBorderStyle:(id)json;
+ (RCTTextDecorationLineType)RCTTextDecorationLineType:(id)json;
+ (RCTTextTransform)RCTTextTransform:(id)json;

@end

Expand Down
7 changes: 7 additions & 0 deletions React/Base/RCTConvert.m
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,13 @@ + (NSLocale *)NSLocale:(id)json
@"underline line-through": @(RCTTextDecorationLineTypeUnderlineStrikethrough),
}), RCTTextDecorationLineTypeNone, integerValue)

RCT_ENUM_CONVERTER(RCTTextTransform, (@{
@"none": @(RCTTextTransformNone),
@"capitalize": @(RCTTextTransformCapitalize),
@"uppercase": @(RCTTextTransformUppercase),
@"lowercase": @(RCTTextTransformLowercase),
}), RCTTextTransformNone, integerValue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value here should be RCTTextTransformUndefined, not RCTTextTransformNone.

@janicduplessis Right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm not how this works, does [RCTTextAttributes applyTextAttributes] only receives changed attributes (and some undefined value for the ones that did not change)?

I think both options would work but I'm not sure if one makes more sense. Maybe check what we do for other enum text attributes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's easy to test.
<Text uppercase>a<Text>b</Text>c</Text> must produce "ABC", but with RCTTextTransformNone as a default value it will produce "AbC", I believe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


RCT_ENUM_CONVERTER(NSWritingDirection, (@{
@"auto": @(NSWritingDirectionNatural),
@"ltr": @(NSWritingDirectionLeftToRight),
Expand Down
8 changes: 8 additions & 0 deletions React/React.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,9 @@
AC70D2E91DE489E4002E6351 /* RCTJavaScriptLoader.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC70D2E81DE489E4002E6351 /* RCTJavaScriptLoader.mm */; };
B233E6EA1D2D845D00BC68BA /* RCTI18nManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */; };
B95154321D1B34B200FE7B80 /* RCTActivityIndicatorView.m in Sources */ = {isa = PBXBuildFile; fileRef = B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */; };
C50FB6CF201BE256008F64BB /* RCTTextTransform.h in Headers */ = {isa = PBXBuildFile; fileRef = C50FB6CE201BE255008F64BB /* RCTTextTransform.h */; };
C50FB6D0201BE380008F64BB /* RCTTextTransform.h in Headers */ = {isa = PBXBuildFile; fileRef = C50FB6CE201BE255008F64BB /* RCTTextTransform.h */; };
C50FB6D1201BE454008F64BB /* RCTTextTransform.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = C50FB6CE201BE255008F64BB /* RCTTextTransform.h */; };
C60128AB1F3D1258009DF9FF /* RCTCxxConvert.h in Headers */ = {isa = PBXBuildFile; fileRef = C60128A91F3D1258009DF9FF /* RCTCxxConvert.h */; };
C60128AC1F3D1258009DF9FF /* RCTCxxConvert.h in Headers */ = {isa = PBXBuildFile; fileRef = C60128A91F3D1258009DF9FF /* RCTCxxConvert.h */; };
C60128AD1F3D1258009DF9FF /* RCTCxxConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = C60128AA1F3D1258009DF9FF /* RCTCxxConvert.m */; };
Expand Down Expand Up @@ -1594,6 +1597,7 @@
files = (
39C50FF92046EACF00CEE534 /* RCTVersion.h in Copy Headers */,
591F78DE202ADB8F004A668C /* RCTLayout.h in Copy Headers */,
C50FB6D1201BE454008F64BB /* RCTTextTransform.h in Copy Headers */,
59EDBCBD1FDF4E43003573DE /* RCTScrollableProtocol.h in Copy Headers */,
59EDBCBE1FDF4E43003573DE /* (null) in Copy Headers */,
59EDBCBF1FDF4E43003573DE /* RCTScrollContentView.h in Copy Headers */,
Expand Down Expand Up @@ -2286,6 +2290,7 @@
B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTI18nManager.m; sourceTree = "<group>"; };
B95154301D1B34B200FE7B80 /* RCTActivityIndicatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTActivityIndicatorView.h; sourceTree = "<group>"; };
B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTActivityIndicatorView.m; sourceTree = "<group>"; };
C50FB6CE201BE255008F64BB /* RCTTextTransform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextTransform.h; sourceTree = "<group>"; };
C60128A91F3D1258009DF9FF /* RCTCxxConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCxxConvert.h; sourceTree = "<group>"; };
C60128AA1F3D1258009DF9FF /* RCTCxxConvert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCxxConvert.m; sourceTree = "<group>"; };
C606692D1F3CC60500E67165 /* RCTModuleMethod.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTModuleMethod.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2629,6 +2634,7 @@
137327E51AA5CF210034F82E /* RCTTabBarManager.h */,
137327E61AA5CF210034F82E /* RCTTabBarManager.m */,
E3BBC8EB1ADE6F47001BBD81 /* RCTTextDecorationLineType.h */,
C50FB6CE201BE255008F64BB /* RCTTextTransform.h */,
130443D61E401AD800D93A67 /* RCTTVView.h */,
130443D71E401AD800D93A67 /* RCTTVView.m */,
13E0674F1A70F44B002CDEE1 /* RCTView.h */,
Expand Down Expand Up @@ -3268,6 +3274,7 @@
59E604A11FE9CCE300BD90C5 /* RCTScrollContentShadowView.h in Headers */,
3D302F8F1DF828F800D6DDAE /* RCTSlider.h in Headers */,
3D302F901DF828F800D6DDAE /* RCTSliderManager.h in Headers */,
C50FB6D0201BE380008F64BB /* RCTTextTransform.h in Headers */,
3D302F911DF828F800D6DDAE /* RCTSwitch.h in Headers */,
3D302F921DF828F800D6DDAE /* RCTSwitchManager.h in Headers */,
3D302F931DF828F800D6DDAE /* RCTTabBar.h in Headers */,
Expand Down Expand Up @@ -3583,6 +3590,7 @@
657734901EE8354A00A0E9EA /* RCTInspectorPackagerConnection.h in Headers */,
3D7BFD1D1EA8E351008DFB7A /* RCTPackagerConnection.h in Headers */,
3D80DA811DF820620028D040 /* RCTSegmentedControl.h in Headers */,
C50FB6CF201BE256008F64BB /* RCTTextTransform.h in Headers */,
599FAA3C1FB274980058CCF6 /* RCTSurfaceRootShadowView.h in Headers */,
3D80DA821DF820620028D040 /* RCTSegmentedControlManager.h in Headers */,
3D80DA831DF820620028D040 /* RCTShadowView.h in Headers */,
Expand Down
16 changes: 16 additions & 0 deletions React/Views/RCTTextTransform.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import <Foundation/Foundation.h>

typedef NS_ENUM(NSInteger, RCTTextTransform) {
RCTTextTransformUndefined = 0,
RCTTextTransformNone,
RCTTextTransformCapitalize,
RCTTextTransformUppercase,
RCTTextTransformLowercase,
};