Skip to content

Commit

Permalink
iOS: Implement border(Top|Bottom)(Start|End)Radius and border(Start|E…
Browse files Browse the repository at this point in the history
…nd)(Color|Width) RN styles

Reviewed By: shergin

Differential Revision: D5874536

fbshipit-source-id: 5ad237bddb70745aef0341cddb172da5ee388c38
  • Loading branch information
RSNara authored and facebook-github-bot committed Oct 19, 2017
1 parent 38b5506 commit 1b5f8d3
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 14 deletions.
2 changes: 2 additions & 0 deletions Libraries/Components/View/ReactNativeStyleAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ ReactNativeStyleAttributes.borderColor = colorAttributes;
ReactNativeStyleAttributes.borderLeftColor = colorAttributes;
ReactNativeStyleAttributes.borderRightColor = colorAttributes;
ReactNativeStyleAttributes.borderTopColor = colorAttributes;
ReactNativeStyleAttributes.borderStartColor = colorAttributes;
ReactNativeStyleAttributes.borderEndColor = colorAttributes;
ReactNativeStyleAttributes.color = colorAttributes;
ReactNativeStyleAttributes.shadowColor = colorAttributes;
ReactNativeStyleAttributes.textDecorationColor = colorAttributes;
Expand Down
6 changes: 6 additions & 0 deletions Libraries/Components/View/ViewStylePropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@ var ViewStylePropTypes = {
borderRightColor: ColorPropType,
borderBottomColor: ColorPropType,
borderLeftColor: ColorPropType,
borderStartColor: ColorPropType,
borderEndColor: ColorPropType,
borderRadius: ReactPropTypes.number,
borderTopLeftRadius: ReactPropTypes.number,
borderTopRightRadius: ReactPropTypes.number,
borderTopStartRadius: ReactPropTypes.number,
borderTopEndRadius: ReactPropTypes.number,
borderBottomLeftRadius: ReactPropTypes.number,
borderBottomRightRadius: ReactPropTypes.number,
borderBottomStartRadius: ReactPropTypes.number,
borderBottomEndRadius: ReactPropTypes.number,
borderStyle: ReactPropTypes.oneOf(['solid', 'dotted', 'dashed']),
borderWidth: ReactPropTypes.number,
borderTopWidth: ReactPropTypes.number,
Expand Down
12 changes: 12 additions & 0 deletions Libraries/StyleSheet/LayoutPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,18 @@ var LayoutPropTypes = {
*/
borderTopWidth: ReactPropTypes.number,

/**
* When direction is `ltr`, `borderStartWidth` is equivalent to `borderLeftWidth`.
* When direction is `rtl`, `borderStartWidth` is equivalent to `borderRightWidth`.
*/
borderStartWidth: ReactPropTypes.number,

/**
* When direction is `ltr`, `borderEndWidth` is equivalent to `borderRightWidth`.
* When direction is `rtl`, `borderEndWidth` is equivalent to `borderLeftWidth`.
*/
borderEndWidth: ReactPropTypes.number,

/** `borderRightWidth` works like `border-right-width` in CSS.
* See https://developer.mozilla.org/en-US/docs/Web/CSS/border-right-width
* for more details.
Expand Down
2 changes: 2 additions & 0 deletions React/Views/RCTShadowView.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ typedef void (^RCTApplierBlock)(NSDictionary<NSNumber *, UIView *> *viewRegistry
@property (nonatomic, assign) float borderLeftWidth;
@property (nonatomic, assign) float borderBottomWidth;
@property (nonatomic, assign) float borderRightWidth;
@property (nonatomic, assign) float borderStartWidth;
@property (nonatomic, assign) float borderEndWidth;

/**
* Margin. Defaults to { 0, 0, 0, 0 }.
Expand Down
17 changes: 15 additions & 2 deletions React/Views/RCTShadowView.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ typedef NS_ENUM(unsigned int, meta_prop_t) {
META_PROP_TOP,
META_PROP_RIGHT,
META_PROP_BOTTOM,
META_PROP_START,
META_PROP_END,
META_PROP_HORIZONTAL,
META_PROP_VERTICAL,
META_PROP_ALL,
Expand Down Expand Up @@ -121,8 +123,17 @@ static void RCTProcessMetaPropsMargin(const YGValue metaProps[META_PROP_COUNT],
}

static void RCTProcessMetaPropsBorder(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node) {
YGNodeStyleSetBorder(node, YGEdgeStart, metaProps[META_PROP_LEFT].value);
YGNodeStyleSetBorder(node, YGEdgeEnd, metaProps[META_PROP_RIGHT].value);
if (![[RCTI18nUtil sharedInstance] doesRTLFlipLeftAndRightStyles]) {
YGNodeStyleSetBorder(node, YGEdgeStart, metaProps[META_PROP_START].value);
YGNodeStyleSetBorder(node, YGEdgeEnd, metaProps[META_PROP_END].value);
YGNodeStyleSetBorder(node, YGEdgeLeft, metaProps[META_PROP_LEFT].value);
YGNodeStyleSetBorder(node, YGEdgeRight, metaProps[META_PROP_RIGHT].value);
} else {
const float start = YGFloatIsUndefined(metaProps[META_PROP_START].value) ? metaProps[META_PROP_LEFT].value : metaProps[META_PROP_START].value;
const float end = YGFloatIsUndefined(metaProps[META_PROP_END].value) ? metaProps[META_PROP_RIGHT].value : metaProps[META_PROP_END].value;
YGNodeStyleSetBorder(node, YGEdgeStart, start);
YGNodeStyleSetBorder(node, YGEdgeEnd, end);
}
YGNodeStyleSetBorder(node, YGEdgeTop, metaProps[META_PROP_TOP].value);
YGNodeStyleSetBorder(node, YGEdgeBottom, metaProps[META_PROP_BOTTOM].value);
YGNodeStyleSetBorder(node, YGEdgeHorizontal, metaProps[META_PROP_HORIZONTAL].value);
Expand Down Expand Up @@ -557,6 +568,8 @@ - (float)border##prop##Width \
RCT_BORDER_PROPERTY(Left, LEFT)
RCT_BORDER_PROPERTY(Bottom, BOTTOM)
RCT_BORDER_PROPERTY(Right, RIGHT)
RCT_BORDER_PROPERTY(Start, START)
RCT_BORDER_PROPERTY(End, END)

// Dimensions
#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
Expand Down
8 changes: 8 additions & 0 deletions React/Views/RCTView.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@
@property (nonatomic, assign) CGFloat borderRadius;
@property (nonatomic, assign) CGFloat borderTopLeftRadius;
@property (nonatomic, assign) CGFloat borderTopRightRadius;
@property (nonatomic, assign) CGFloat borderTopStartRadius;
@property (nonatomic, assign) CGFloat borderTopEndRadius;
@property (nonatomic, assign) CGFloat borderBottomLeftRadius;
@property (nonatomic, assign) CGFloat borderBottomRightRadius;
@property (nonatomic, assign) CGFloat borderBottomStartRadius;
@property (nonatomic, assign) CGFloat borderBottomEndRadius;

/**
* Border colors (actually retained).
Expand All @@ -80,6 +84,8 @@
@property (nonatomic, assign) CGColorRef borderRightColor;
@property (nonatomic, assign) CGColorRef borderBottomColor;
@property (nonatomic, assign) CGColorRef borderLeftColor;
@property (nonatomic, assign) CGColorRef borderStartColor;
@property (nonatomic, assign) CGColorRef borderEndColor;
@property (nonatomic, assign) CGColorRef borderColor;

/**
Expand All @@ -89,6 +95,8 @@
@property (nonatomic, assign) CGFloat borderRightWidth;
@property (nonatomic, assign) CGFloat borderBottomWidth;
@property (nonatomic, assign) CGFloat borderLeftWidth;
@property (nonatomic, assign) CGFloat borderStartWidth;
@property (nonatomic, assign) CGFloat borderEndWidth;
@property (nonatomic, assign) CGFloat borderWidth;

/**
Expand Down
115 changes: 103 additions & 12 deletions React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import "RCTLog.h"
#import "RCTUtils.h"
#import "UIView+React.h"
#import "RCTI18nUtil.h"

@implementation UIView (RCTViewUnmounting)

Expand Down Expand Up @@ -110,10 +111,16 @@ - (instancetype)initWithFrame:(CGRect)frame
_borderRightWidth = -1;
_borderBottomWidth = -1;
_borderLeftWidth = -1;
_borderStartWidth = -1;
_borderEndWidth = -1;
_borderTopLeftRadius = -1;
_borderTopRightRadius = -1;
_borderTopStartRadius = -1;
_borderTopEndRadius = -1;
_borderBottomLeftRadius = -1;
_borderBottomRightRadius = -1;
_borderBottomStartRadius = -1;
_borderBottomEndRadius = -1;
_borderStyle = RCTBorderStyleSolid;
_hitTestEdgeInsets = UIEdgeInsetsZero;

Expand All @@ -127,7 +134,10 @@ - (instancetype)initWithFrame:(CGRect)frame

- (void)setReactLayoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
{
_reactLayoutDirection = layoutDirection;
if (_reactLayoutDirection != layoutDirection) {
_reactLayoutDirection = layoutDirection;
[self.layer setNeedsDisplay];
}

if ([self respondsToSelector:@selector(setSemanticContentAttribute:)]) {
self.semanticContentAttribute =
Expand Down Expand Up @@ -424,26 +434,77 @@ - (void)setBackgroundColor:(UIColor *)backgroundColor
[self.layer setNeedsDisplay];
}

static CGFloat RCTDefaultIfNegativeTo(CGFloat defaultValue, CGFloat x) {
return x >= 0 ? x : defaultValue;
};

- (UIEdgeInsets)bordersAsInsets
{
const CGFloat borderWidth = MAX(0, _borderWidth);
const BOOL isRTL = _reactLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;

if ([[RCTI18nUtil sharedInstance] doesRTLFlipLeftAndRightStyles]) {
const CGFloat borderStartWidth = RCTDefaultIfNegativeTo(_borderLeftWidth, _borderStartWidth);
const CGFloat borderEndWidth = RCTDefaultIfNegativeTo(_borderRightWidth, _borderEndWidth);

const CGFloat directionAwareBorderLeftWidth = isRTL ? borderEndWidth : borderStartWidth;
const CGFloat directionAwareBorderRightWidth = isRTL ? borderStartWidth : borderEndWidth;

return (UIEdgeInsets) {
RCTDefaultIfNegativeTo(borderWidth, _borderTopWidth),
RCTDefaultIfNegativeTo(borderWidth, directionAwareBorderLeftWidth),
RCTDefaultIfNegativeTo(borderWidth, _borderBottomWidth),
RCTDefaultIfNegativeTo(borderWidth, directionAwareBorderRightWidth),
};
}

const CGFloat directionAwareBorderLeftWidth = isRTL ? _borderEndWidth : _borderStartWidth;
const CGFloat directionAwareBorderRightWidth = isRTL ? _borderStartWidth : _borderEndWidth;

return (UIEdgeInsets) {
_borderTopWidth >= 0 ? _borderTopWidth : borderWidth,
_borderLeftWidth >= 0 ? _borderLeftWidth : borderWidth,
_borderBottomWidth >= 0 ? _borderBottomWidth : borderWidth,
_borderRightWidth >= 0 ? _borderRightWidth : borderWidth,
RCTDefaultIfNegativeTo(borderWidth, _borderTopWidth),
RCTDefaultIfNegativeTo(borderWidth, RCTDefaultIfNegativeTo(_borderLeftWidth, directionAwareBorderLeftWidth)),
RCTDefaultIfNegativeTo(borderWidth, _borderBottomWidth),
RCTDefaultIfNegativeTo(borderWidth, RCTDefaultIfNegativeTo(_borderRightWidth, directionAwareBorderRightWidth)),
};
}

- (RCTCornerRadii)cornerRadii
{
// Get corner radii
const BOOL isRTL = _reactLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
const CGFloat radius = MAX(0, _borderRadius);
const CGFloat topLeftRadius = _borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius;
const CGFloat topRightRadius = _borderTopRightRadius >= 0 ? _borderTopRightRadius : radius;
const CGFloat bottomLeftRadius = _borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius;
const CGFloat bottomRightRadius = _borderBottomRightRadius >= 0 ? _borderBottomRightRadius : radius;

CGFloat topLeftRadius;
CGFloat topRightRadius;
CGFloat bottomLeftRadius;
CGFloat bottomRightRadius;

if ([[RCTI18nUtil sharedInstance] doesRTLFlipLeftAndRightStyles]) {
const CGFloat topStartRadius = RCTDefaultIfNegativeTo(_borderTopLeftRadius, _borderTopStartRadius);
const CGFloat topEndRadius = RCTDefaultIfNegativeTo(_borderTopRightRadius, _borderTopEndRadius);
const CGFloat bottomStartRadius = RCTDefaultIfNegativeTo(_borderBottomLeftRadius, _borderBottomStartRadius);
const CGFloat bottomEndRadius = RCTDefaultIfNegativeTo(_borderBottomRightRadius, _borderBottomEndRadius);

const CGFloat directionAwareTopLeftRadius = isRTL ? topEndRadius : topStartRadius;
const CGFloat directionAwareTopRightRadius = isRTL ? topStartRadius : topEndRadius;
const CGFloat directionAwareBottomLeftRadius = isRTL ? bottomEndRadius : bottomStartRadius;
const CGFloat directionAwareBottomRightRadius = isRTL ? bottomStartRadius : bottomEndRadius;

topLeftRadius = RCTDefaultIfNegativeTo(radius, directionAwareTopLeftRadius);
topRightRadius = RCTDefaultIfNegativeTo(radius, directionAwareTopRightRadius);
bottomLeftRadius = RCTDefaultIfNegativeTo(radius, directionAwareBottomLeftRadius);
bottomRightRadius = RCTDefaultIfNegativeTo(radius, directionAwareBottomRightRadius);
} else {
const CGFloat directionAwareTopLeftRadius = isRTL ? _borderTopEndRadius : _borderTopStartRadius;
const CGFloat directionAwareTopRightRadius = isRTL ? _borderTopStartRadius : _borderTopEndRadius;
const CGFloat directionAwareBottomLeftRadius = isRTL ? _borderBottomEndRadius : _borderBottomStartRadius;
const CGFloat directionAwareBottomRightRadius = isRTL ? _borderBottomStartRadius : _borderBottomEndRadius;

topLeftRadius = RCTDefaultIfNegativeTo(radius, RCTDefaultIfNegativeTo(_borderTopLeftRadius, directionAwareTopLeftRadius));
topRightRadius = RCTDefaultIfNegativeTo(radius, RCTDefaultIfNegativeTo(_borderTopRightRadius, directionAwareTopRightRadius));
bottomLeftRadius = RCTDefaultIfNegativeTo(radius, RCTDefaultIfNegativeTo(_borderBottomLeftRadius, directionAwareBottomLeftRadius));
bottomRightRadius = RCTDefaultIfNegativeTo(radius, RCTDefaultIfNegativeTo(_borderBottomRightRadius, directionAwareBottomRightRadius));
}

// Get scale factors required to prevent radii from overlapping
const CGSize size = self.bounds.size;
Expand All @@ -463,11 +524,31 @@ - (RCTCornerRadii)cornerRadii

- (RCTBorderColors)borderColors
{
const BOOL isRTL = _reactLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;

if ([[RCTI18nUtil sharedInstance] doesRTLFlipLeftAndRightStyles]) {
const CGColorRef borderStartColor = _borderStartColor ?: _borderLeftColor;
const CGColorRef borderEndColor = _borderEndColor ?: _borderRightColor;

const CGColorRef directionAwareBorderLeftColor = isRTL ? borderEndColor : borderStartColor;
const CGColorRef directionAwareBorderRightColor = isRTL ? borderStartColor : borderEndColor;

return (RCTBorderColors){
_borderTopColor ?: _borderColor,
directionAwareBorderLeftColor ?: _borderColor,
_borderBottomColor ?: _borderColor,
directionAwareBorderRightColor ?: _borderColor,
};
}

const CGColorRef directionAwareBorderLeftColor = isRTL ? _borderEndColor : _borderStartColor;
const CGColorRef directionAwareBorderRightColor = isRTL ? _borderStartColor : _borderEndColor;

return (RCTBorderColors){
_borderTopColor ?: _borderColor,
_borderLeftColor ?: _borderColor,
directionAwareBorderLeftColor ?: _borderLeftColor ?: _borderColor,
_borderBottomColor ?: _borderColor,
_borderRightColor ?: _borderColor,
directionAwareBorderRightColor ?: _borderRightColor ?: _borderColor,
};
}

Expand Down Expand Up @@ -656,6 +737,8 @@ - (void)setBorder##side##Color:(CGColorRef)color \
setBorderColor(Right)
setBorderColor(Bottom)
setBorderColor(Left)
setBorderColor(Start)
setBorderColor(End)

#pragma mark - Border Width

Expand All @@ -674,6 +757,8 @@ - (void)setBorder##side##Width:(CGFloat)width \
setBorderWidth(Right)
setBorderWidth(Bottom)
setBorderWidth(Left)
setBorderWidth(Start)
setBorderWidth(End)

#pragma mark - Border Radius

Expand All @@ -690,8 +775,12 @@ - (void)setBorder##side##Radius:(CGFloat)radius \
setBorderRadius()
setBorderRadius(TopLeft)
setBorderRadius(TopRight)
setBorderRadius(TopStart)
setBorderRadius(TopEnd)
setBorderRadius(BottomLeft)
setBorderRadius(BottomRight)
setBorderRadius(BottomStart)
setBorderRadius(BottomEnd)

#pragma mark - Border Style

Expand All @@ -714,6 +803,8 @@ - (void)dealloc
CGColorRelease(_borderRightColor);
CGColorRelease(_borderBottomColor);
CGColorRelease(_borderLeftColor);
CGColorRelease(_borderStartColor);
CGColorRelease(_borderEndColor);
}

@end
8 changes: 8 additions & 0 deletions React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio
RCT_VIEW_BORDER_PROPERTY(Right)
RCT_VIEW_BORDER_PROPERTY(Bottom)
RCT_VIEW_BORDER_PROPERTY(Left)
RCT_VIEW_BORDER_PROPERTY(Start)
RCT_VIEW_BORDER_PROPERTY(End)

#define RCT_VIEW_BORDER_RADIUS_PROPERTY(SIDE) \
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Radius, CGFloat, RCTView) \
Expand All @@ -257,8 +259,12 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio

RCT_VIEW_BORDER_RADIUS_PROPERTY(TopLeft)
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopRight)
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopStart)
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopEnd)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomLeft)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomRight)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomStart)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomEnd)

RCT_REMAP_VIEW_PROPERTY(display, reactDisplay, YGDisplay)
RCT_REMAP_VIEW_PROPERTY(zIndex, reactZIndex, NSInteger)
Expand Down Expand Up @@ -286,6 +292,8 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio
RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderStartWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderEndWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderWidth, float)

RCT_EXPORT_SHADOW_PROPERTY(marginTop, YGValue)
Expand Down

0 comments on commit 1b5f8d3

Please sign in to comment.