Skip to content

Commit

Permalink
Fix fabric
Browse files Browse the repository at this point in the history
  • Loading branch information
Saadnajmi committed Feb 20, 2024
1 parent 8ff05b5 commit f550094
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
borderTopLeftRadius: true,
borderTopRightRadius: true,
borderTopStartRadius: true,
cursor: true,
opacity: true,
pointerEvents: true,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export type DimensionValue =
type AnimatableNumericValue = number | Animated.AnimatedNode;
type AnimatableStringValue = string | Animated.AnimatedNode;

export type CursorValue = 'auto' | 'pointer';

/**
* Flex Prop Types
* @see https://reactnative.dev/docs/flexbox
Expand Down Expand Up @@ -274,6 +276,7 @@ export interface ViewStyle extends FlexStyle, ShadowStyleIOS, TransformsStyle {
* Controls whether the View can be the target of touch events.
*/
pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto' | undefined;
cursor?: CursorValue;
}

export type FontVariant =
Expand Down Expand Up @@ -403,4 +406,5 @@ export interface ImageStyle extends FlexStyle, ShadowStyleIOS, TransformsStyle {
tintColor?: ColorValue | undefined;
opacity?: AnimatableNumericValue | undefined;
objectFit?: 'cover' | 'contain' | 'fill' | 'scale-down' | undefined;
cursor?: CursorValue;
}
3 changes: 3 additions & 0 deletions packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export type EdgeInsetsValue = {
bottom: number,
};

export type CursorValue = ?('auto' | 'pointer');

export type DimensionValue = number | string | 'auto' | AnimatedNode | null;
export type AnimatableNumericValue = number | AnimatedNode;

Expand Down Expand Up @@ -729,6 +731,7 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{
opacity?: AnimatableNumericValue,
elevation?: number,
pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only',
cursor?: CursorValue,
}>;

export type ____ViewStyle_Internal = $ReadOnly<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ @implementation RCTViewComponentView {
BOOL _needsInvalidateLayer;
BOOL _isJSResponder;
BOOL _removeClippedSubviews;
NSString *_cursor;
NSMutableArray<UIView *> *_reactSubviews;
NSSet<NSString *> *_Nullable _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN;
}
Expand Down Expand Up @@ -256,6 +257,12 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
if (oldViewProps.backfaceVisibility != newViewProps.backfaceVisibility) {
self.layer.doubleSided = newViewProps.backfaceVisibility == BackfaceVisibility::Visible;
}

// `cursor`
if (oldViewProps.cursor != newViewProps.cursor) {
_cursor = [NSString stringWithUTF8String:newViewProps.cursor.c_str()];
needsInvalidateLayer = YES;
}

// `shouldRasterize`
if (oldViewProps.shouldRasterize != newViewProps.shouldRasterize) {
Expand Down Expand Up @@ -573,14 +580,14 @@ - (void)invalidateLayer
}

const auto borderMetrics = _props->resolveBorderMetrics(_layoutMetrics);
const RCTCornerInsets cornerInsets =
RCTGetCornerInsets(RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), UIEdgeInsetsZero);

// Stage 1. Shadow Path
BOOL const layerHasShadow = layer.shadowOpacity > 0 && CGColorGetAlpha(layer.shadowColor) > 0;
if (layerHasShadow) {
if (CGColorGetAlpha(_backgroundColor.CGColor) > 0.999) {
// If view has a solid background color, calculate shadow path from border.
const RCTCornerInsets cornerInsets =
RCTGetCornerInsets(RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), UIEdgeInsetsZero);
CGPathRef shadowPath = RCTPathCreateWithRoundedRect(self.bounds, cornerInsets, nil);
layer.shadowPath = shadowPath;
CGPathRelease(shadowPath);
Expand All @@ -591,6 +598,20 @@ - (void)invalidateLayer
} else {
layer.shadowPath = nil;
}

// Stage 1.5. Cursor / Hover Effects
if (@available(iOS 17.0, *)) {
UIHoverStyle *hoverStyle = nil;
if ([_cursor isEqual:@"pointer"]) {
CGPathRef borderPath = RCTPathCreateWithRoundedRect(self.bounds, cornerInsets, nil);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithCGPath:borderPath];
UIShape *shape = [UIShape shapeWithBezierPath:bezierPath];

hoverStyle = [UIHoverStyle styleWithEffect:[UIHoverHighlightEffect effect] shape:shape];
}
[self setHoverStyle:hoverStyle];
}


// Stage 2. Border Rendering
const bool useCoreAnimationBorderRendering =
Expand Down
20 changes: 20 additions & 0 deletions packages/react-native/React/Views/RCTConvert+Cursor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import <React/RCTConvert.h>

typedef NS_ENUM(NSInteger, RCTCursor) {
RCTCursorAuto,
RCTCursorPointer,
};


@interface RCTConvert (Transform)

+ (RCTCursor)RCTCursor:(id)json;

@end
22 changes: 22 additions & 0 deletions packages/react-native/React/Views/RCTConvert+Cursor.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import "RCTConvert+Cursor.h"


@implementation RCTConvert (Cursor)

RCT_ENUM_CONVERTER(
RCTCursor,
(@{
@"auto" : @(RCTCursorAuto),
@"pointer" : @(RCTCursorPointer),
}),
RCTCursorAuto,
integerValue)

@end
7 changes: 7 additions & 0 deletions packages/react-native/React/Views/RCTView.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#import <React/RCTComponent.h>
#import <React/RCTPointerEvents.h>

#import "RCTConvert+Cursor.h"

extern const UIAccessibilityTraits SwitchAccessibilityTrait;

@protocol RCTAutoInsetsProtocol;
Expand Down Expand Up @@ -120,6 +122,11 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait;
*/
@property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;

/**
* Used to control hover effects
*/
@property (nonatomic, assign) RCTCursor cursor;

/**
* (Experimental and unused for Paper) Pointer event handlers.
*/
Expand Down
43 changes: 30 additions & 13 deletions packages/react-native/React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ - (instancetype)initWithFrame:(CGRect)frame
_borderCurve = RCTBorderCurveCircular;
_borderStyle = RCTBorderStyleSolid;
_hitTestEdgeInsets = UIEdgeInsetsZero;
_cursor = RCTCursorAuto;

_backgroundColor = super.backgroundColor;
}
Expand Down Expand Up @@ -861,6 +862,14 @@ - (void)displayLayer:(CALayer *)layer
[self updateClippingForLayer:layer];
}

CGPathRef RCTBorderPathForView(RCTView *view)
{
const RCTCornerRadii cornerRadii = [view cornerRadii];
const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero);
CGPathRef borderPath = RCTPathCreateWithRoundedRect(view.bounds, cornerInsets, NULL);
return borderPath;
}

static BOOL RCTLayerHasShadow(CALayer *layer)
{
return layer.shadowOpacity * CGColorGetAlpha(layer.shadowColor) > 0;
Expand All @@ -873,7 +882,7 @@ static void RCTUpdateShadowPathForView(RCTView *view)
// If view has a solid background color, calculate shadow path from border
const RCTCornerRadii cornerRadii = [view cornerRadii];
const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero);
CGPathRef shadowPath = RCTPathCreateWithRoundedRect(view.bounds, cornerInsets, NULL);
CGPathRef shadowPath = RCTBorderPathForView(view);
view.layer.shadowPath = shadowPath;
CGPathRelease(shadowPath);

Expand All @@ -897,24 +906,32 @@ - (void)updateClippingForLayer:(CALayer *)layer
CGFloat cornerRadius = 0;

if (self.clipsToBounds) {
const RCTCornerRadii cornerRadii = [self cornerRadii];
if (RCTCornerRadiiAreEqual(cornerRadii)) {
cornerRadius = cornerRadii.topLeft;

} else {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
CGPathRef path =
RCTPathCreateWithRoundedRect(self.bounds, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
shapeLayer.path = path;
CGPathRelease(path);
mask = shapeLayer;
}
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
CGPathRef path = RCTBorderPathForView(self);
shapeLayer.path = path;
CGPathRelease(path);
mask = shapeLayer;
}

layer.cornerRadius = cornerRadius;
layer.mask = mask;
}

- (void)setCursor:(RCTCursor)cursor
{
if (@available(iOS 17.0, *)) {
UIHoverStyle *hoverStyle = nil;
if (cursor == RCTCursorPointer) {
CGPathRef borderPath = RCTBorderPathForView(self);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithCGPath:borderPath];
UIShape *shape = [UIShape shapeWithBezierPath:bezierPath];

hoverStyle = [UIHoverStyle styleWithEffect:[UIHoverHighlightEffect effect] shape:[UIShape rectShape]];
}
[self setHoverStyle:hoverStyle];
}
}

#pragma mark Border Color

#define setBorderColor(side) \
Expand Down
2 changes: 2 additions & 0 deletions packages/react-native/React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import "RCTBorderCurve.h"
#import "RCTBorderStyle.h"
#import "RCTBridge.h"
#import "RCTConvert+Cursor.h"
#import "RCTConvert+Transform.h"
#import "RCTConvert.h"
#import "RCTLog.h"
Expand Down Expand Up @@ -195,6 +196,7 @@ - (RCTShadowView *)shadowView

RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t)
RCT_EXPORT_VIEW_PROPERTY(cursor, RCTCursor)
RCT_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat)
RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor)
RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ BaseViewProps::BaseViewProps(
"shadowRadius",
sourceProps.shadowRadius,
{})),
cursor(
CoreFeatures::enablePropIteratorSetter
? sourceProps.cursor
: convertRawProp(
context,
rawProps,
"cursor",
sourceProps.cursor,
{})),
transform(
CoreFeatures::enablePropIteratorSetter ? sourceProps.transform
: convertRawProp(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps {
Size shadowOffset{0, -3};
Float shadowOpacity{};
Float shadowRadius{3};

std::string cursor;

// Transform
Transform transform{};
Expand Down
8 changes: 8 additions & 0 deletions packages/rn-tester/js/examples/View/ViewExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ export default ({
borderRadius: 25,
borderWidth: 1,
marginRight: 10,
cursor: 'pointer',
}}
/>
<View
Expand All @@ -581,6 +582,7 @@ export default ({
borderRadius: 25,
borderWidth: 10,
marginRight: 10,
cursor: 'pointer',
}}
/>
<View
Expand All @@ -593,6 +595,7 @@ export default ({
borderBottomLeftRadius: 50,
borderWidth: 1,
marginRight: 10,
cursor: 'pointer',
}}
/>
<View
Expand All @@ -605,6 +608,7 @@ export default ({
borderBottomLeftRadius: 50,
borderWidth: 10,
marginRight: 10,
cursor: 'pointer',
}}
/>
<View
Expand All @@ -614,6 +618,7 @@ export default ({
borderLeftWidth: 6,
borderTopWidth: 6,
borderTopLeftRadius: 20,
cursor: 'pointer',
}}
/>
<View
Expand All @@ -623,6 +628,7 @@ export default ({
borderRightWidth: 6,
borderTopWidth: 6,
borderTopRightRadius: 20,
cursor: 'pointer',
}}
/>
<View
Expand All @@ -632,6 +638,7 @@ export default ({
borderBottomWidth: 6,
borderLeftWidth: 6,
borderBottomLeftRadius: 20,
cursor: 'pointer',
}}
/>
<View
Expand All @@ -641,6 +648,7 @@ export default ({
borderBottomWidth: 6,
borderRightWidth: 6,
borderBottomRightRadius: 20,
cursor: 'pointer',
}}
/>
</View>
Expand Down

0 comments on commit f550094

Please sign in to comment.