diff --git a/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js b/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js index de77b65aaba7f5..7ecdb3f3b5d16b 100644 --- a/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js +++ b/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js @@ -144,7 +144,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = { borderTopLeftRadius: true, borderTopRightRadius: true, borderTopStartRadius: true, - cursor: true, + cursor: true, // [macOS] [visionOS] opacity: true, pointerEvents: true, diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts index 92fe7cdaf36bb3..8b163e69cb9a18 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts @@ -27,6 +27,30 @@ export type DimensionValue = type AnimatableNumericValue = number | Animated.AnimatedNode; type AnimatableStringValue = string | Animated.AnimatedNode; +// [macOS +export type CursorValue = + | 'alias' + | 'auto' + | 'col-resize' + | 'context-menu' + | 'copy' + | 'crosshair' + | 'default' + | 'disappearing-item' + | 'e-resize' + | 'grab' + | 'grabbing' + | 'n-resize' + | 'no-drop' + | 'not-allowed' + | 'pointer' + | 'row-resize' + | 's-resize' + | 'text' + | 'vertical-text' + | 'w-resize'; +// macOS] + /** * Flex Prop Types * @see https://reactnative.dev/docs/flexbox @@ -273,6 +297,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 | undefined; } export type FontVariant = @@ -363,4 +388,5 @@ export interface ImageStyle extends FlexStyle, ShadowStyleIOS, TransformsStyle { tintColor?: ColorValue | undefined; opacity?: AnimatableNumericValue | undefined; objectFit?: 'cover' | 'contain' | 'fill' | 'scale-down' | undefined; + cursor?: CursorValue | undefined; } diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js index 27d3f868257431..9ff7c20aa5b189 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js @@ -20,8 +20,25 @@ import type { } from './private/_StyleSheetTypesOverrides'; import type {____TransformStyle_Internal} from './private/_TransformStyle'; +declare export opaque type NativeColorValue; +export type ____ColorValue_Internal = null | string | number | NativeColorValue; +export type ColorArrayValue = null | $ReadOnlyArray<____ColorValue_Internal>; +export type PointValue = { + x: number, + y: number, +}; +export type EdgeInsetsValue = { + top: number, + left: number, + right: number, + bottom: number, +}; + +export type DimensionValue = number | string | 'auto' | AnimatedNode | null; +export type AnimatableNumericValue = number | AnimatedNode; + // [macOS -export type CursorValue = ?( +export type CursorValue = | 'alias' | 'auto' | 'col-resize' @@ -41,27 +58,9 @@ export type CursorValue = ?( | 's-resize' | 'text' | 'vertical-text' - | 'w-resize' -); + | 'w-resize'; // macOS] -declare export opaque type NativeColorValue; -export type ____ColorValue_Internal = null | string | number | NativeColorValue; -export type ColorArrayValue = null | $ReadOnlyArray<____ColorValue_Internal>; -export type PointValue = { - x: number, - y: number, -}; -export type EdgeInsetsValue = { - top: number, - left: number, - right: number, - bottom: number, -}; - -export type DimensionValue = number | string | 'auto' | AnimatedNode | null; -export type AnimatableNumericValue = number | AnimatedNode; - /** * React Native's layout system is based on Flexbox and is powered both * on iOS and Android by an open source project called `Yoga`: @@ -752,7 +751,7 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{ opacity?: AnimatableNumericValue, elevation?: number, pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only', - cursor?: CursorValue, // [macOS] + cursor?: CursorValue, // [macOS][visionOS] }>; export type ____ViewStyle_Internal = $ReadOnly<{ diff --git a/packages/react-native/Libraries/Text/BaseText/RCTBaseTextViewManager.mm b/packages/react-native/Libraries/Text/BaseText/RCTBaseTextViewManager.mm index 427a4254e77d0a..d250da3386f3a5 100644 --- a/packages/react-native/Libraries/Text/BaseText/RCTBaseTextViewManager.mm +++ b/packages/react-native/Libraries/Text/BaseText/RCTBaseTextViewManager.mm @@ -6,9 +6,7 @@ */ #import -#if TARGET_OS_OSX // [macOS -#import -#endif // macOS] +#import // [macOS] @implementation RCTBaseTextViewManager diff --git a/packages/react-native/Libraries/Text/RCTTextAttributes.h b/packages/react-native/Libraries/Text/RCTTextAttributes.h index 518d0535e26f50..8265439a286a6c 100644 --- a/packages/react-native/Libraries/Text/RCTTextAttributes.h +++ b/packages/react-native/Libraries/Text/RCTTextAttributes.h @@ -7,15 +7,12 @@ #import // [macOS] +#import // [macOS] #import #import #import "RCTTextTransform.h" -#if TARGET_OS_OSX // [macOS -#import -#endif // macOS] - NS_ASSUME_NONNULL_BEGIN extern NSString *const RCTTextAttributesIsHighlightedAttributeName; diff --git a/packages/react-native/Libraries/Text/RCTTextAttributes.mm b/packages/react-native/Libraries/Text/RCTTextAttributes.mm index 9bfdb93cc9a82c..fe9ccb5d47ba00 100644 --- a/packages/react-native/Libraries/Text/RCTTextAttributes.mm +++ b/packages/react-native/Libraries/Text/RCTTextAttributes.mm @@ -8,13 +8,10 @@ #import #import +#import // [macOS] #import #import -#if TARGET_OS_OSX // [macOS -#import -#endif // macOS] - NSString *const RCTTextAttributesIsHighlightedAttributeName = @"RCTTextAttributesIsHighlightedAttributeName"; NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttributeName"; @@ -235,7 +232,7 @@ - (NSParagraphStyle *)effectiveParagraphStyle #if TARGET_OS_OSX // [macOS if (_cursor != RCTCursorAuto) { - attributes[NSCursorAttributeName] = [RCTConvert NSCursor:_cursor]; + attributes[NSCursorAttributeName] = NSCursorFromRCTCursor(_cursor); } #endif // macOS] diff --git a/packages/react-native/React/Base/RCTConvert.h b/packages/react-native/React/Base/RCTConvert.h index 9bb88471fe0048..e13030ee3952ae 100644 --- a/packages/react-native/React/Base/RCTConvert.h +++ b/packages/react-native/React/Base/RCTConvert.h @@ -11,6 +11,7 @@ #import #import #import +#import // [macOS] [visionOS] #import #import #import @@ -84,6 +85,8 @@ typedef NSURL RCTFileURL; #endif #endif // [macOS] ++ (RCTCursor)RCTCursor:(id)json; // [macOS] [visionOS] + #if TARGET_OS_OSX // [macOS + (NSTextCheckingTypes)NSTextCheckingTypes:(id)json; #endif // macOS] diff --git a/packages/react-native/React/Base/RCTConvert.m b/packages/react-native/React/Base/RCTConvert.m index a2b652b1daa02a..2906e6f57957dd 100644 --- a/packages/react-native/React/Base/RCTConvert.m +++ b/packages/react-native/React/Base/RCTConvert.m @@ -549,7 +549,38 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC }), UIBarStyleDefault, integerValue) -#else // [macOS +#endif // [macOS] + +// [macOS [visionOS] +RCT_ENUM_CONVERTER( + RCTCursor, + (@{ + @"alias" : @(RCTCursorAlias), + @"auto" : @(RCTCursorAuto), + @"col-resize" : @(RCTCursorColumnResize), + @"context-menu" : @(RCTCursorContextualMenu), + @"copy" : @(RCTCursorCopy), + @"crosshair" : @(RCTCursorCrosshair), + @"default" : @(RCTCursorDefault), + @"disappearing-item" : @(RCTCursorDisappearingItem), + @"e-resize" : @(RCTCursorEastResize), + @"grab" : @(RCTCursorGrab), + @"grabbing" : @(RCTCursorGrabbing), + @"n-resize" : @(RCTCursorNorthResize), + @"no-drop" : @(RCTCursorNoDrop), + @"not-allowed" : @(RCTCursorNotAllowed), + @"pointer" : @(RCTCursorPointer), + @"row-resize" : @(RCTCursorRowResize), + @"s-resize" : @(RCTCursorSouthResize), + @"text" : @(RCTCursorText), + @"vertical-text" : @(RCTCursorVerticalText), + @"w-resize" : @(RCTCursorWestResize), + }), + RCTCursorAuto, + integerValue) +// macOS] [visionOS] + +#if TARGET_OS_OSX // [macOS RCT_MULTI_ENUM_CONVERTER(NSTextCheckingTypes, (@{ @"ortography": @(NSTextCheckingTypeOrthography), @"spelling": @(NSTextCheckingTypeSpelling), diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index d1340d92eda18d..afd8d23a5d989a 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -102,6 +102,18 @@ - (void)setBackgroundColor:(RCTUIColor *)backgroundColor // [macOS] _backgroundColor = backgroundColor; } +#if TARGET_OS_OSX // [macOS +- (void)resetCursorRects +{ + [self discardCursorRects]; + if (_props->cursor != Cursor::Auto) + { + NSCursor *cursor = NSCursorFromCursor(_props->cursor); + [self addCursorRect:self.bounds cursor:cursor]; + } +} +#endif // macOS] + #pragma mark - RCTComponentViewProtocol + (ComponentDescriptorProvider)componentDescriptorProvider @@ -258,6 +270,11 @@ - (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) { + needsInvalidateLayer = YES; + } // `shouldRasterize` if (oldViewProps.shouldRasterize != newViewProps.shouldRasterize) { @@ -579,6 +596,55 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle) } } +#if TARGET_OS_OSX // [macOS + static NSCursor *NSCursorFromCursor(Cursor cursor) +{ + switch (cursor) { + case Cursor::Auto: + return [NSCursor arrowCursor]; + case Cursor::Alias: + return [NSCursor dragLinkCursor]; + case Cursor::ColumnResize: + return [NSCursor resizeLeftRightCursor]; + case Cursor::ContextualMenu: + return [NSCursor contextualMenuCursor]; + case Cursor::Copy: + return [NSCursor dragCopyCursor]; + case Cursor::Crosshair: + return [NSCursor crosshairCursor]; + case Cursor::Default: + return [NSCursor arrowCursor]; + case Cursor::DisappearingItem: + return [NSCursor disappearingItemCursor]; + case Cursor::EastResize: + return [NSCursor resizeRightCursor]; + case Cursor::Grab: + return [NSCursor openHandCursor]; + case Cursor::Grabbing: + return [NSCursor closedHandCursor]; + case Cursor::NorthResize: + return [NSCursor resizeUpCursor]; + case Cursor::NoDrop: + return [NSCursor operationNotAllowedCursor]; + case Cursor::NotAllowed: + return [NSCursor operationNotAllowedCursor]; + case Cursor::Pointer: + return [NSCursor pointingHandCursor]; + case Cursor::RowResize: + return [NSCursor resizeUpDownCursor]; + case Cursor::SouthResize: + return [NSCursor resizeDownCursor]; + case Cursor::Text: + return [NSCursor IBeamCursor]; + case Cursor::VerticalText: + return [NSCursor IBeamCursorForVerticalLayout]; + case Cursor::WestResize: + return [NSCursor resizeLeftCursor]; + } +} +#endif // macOS] + + - (void)invalidateLayer { CALayer *layer = self.layer; @@ -606,6 +672,33 @@ - (void)invalidateLayer } else { layer.shadowPath = nil; } + +#if !TARGET_OS_OSX // [visionOS] + // Stage 1.5. Cursor / Hover Effects + if (@available(iOS 17.0, *)) { + UIHoverStyle *hoverStyle = nil; + if (_props->cursor == Cursor::Pointer) { + const RCTCornerInsets cornerInsets = + RCTGetCornerInsets(RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), UIEdgeInsetsZero); +#if TARGET_OS_IOS + // Due to an Apple bug, it seems on iOS, UIShapes made with `[UIShape shapeWithBezierPath:]` + // evaluate their shape on the superviews' coordinate space. This leads to the hover shape + // rendering incorrectly on iOS, iOS apps in compatibility mode on visionOS, but not on visionOS. + // To work around this, for iOS, we can calculate the border path based on `view.frame` (the + // superview's coordinate space) instead of view.bounds. + CGPathRef borderPath = RCTPathCreateWithRoundedRect(self.frame, cornerInsets, NULL); +#else // TARGET_OS_VISION + CGPathRef borderPath = RCTPathCreateWithRoundedRect(self.bounds, cornerInsets, NULL); +#endif + UIBezierPath *bezierPath = [UIBezierPath bezierPathWithCGPath:borderPath]; + CGPathRelease(borderPath); + UIShape *shape = [UIShape shapeWithBezierPath:bezierPath]; + + hoverStyle = [UIHoverStyle styleWithEffect:[UIHoverAutomaticEffect effect] shape:shape]; + } + [self setHoverStyle:hoverStyle]; + } +#endif // [visionOS] // Stage 2. Border Rendering const bool useCoreAnimationBorderRendering = diff --git a/packages/react-native/React/Views/RCTCursor.h b/packages/react-native/React/Views/RCTCursor.h index a893cd40b66fa5..5d6727ca2cb1e5 100644 --- a/packages/react-native/React/Views/RCTCursor.h +++ b/packages/react-native/React/Views/RCTCursor.h @@ -5,35 +5,76 @@ * LICENSE file in the root directory of this source tree. */ -#import + #import typedef NS_ENUM(NSInteger, RCTCursor) { + RCTCursorAlias, RCTCursorAuto, - RCTCursorArrow, - RCTCursorIBeam, + RCTCursorColumnResize, + RCTCursorContextualMenu, + RCTCursorCopy, RCTCursorCrosshair, - RCTCursorClosedHand, - RCTCursorOpenHand, - RCTCursorPointingHand, - RCTCursorResizeLeft, - RCTCursorResizeRight, - RCTCursorResizeLeftRight, - RCTCursorResizeUp, - RCTCursorResizeDown, - RCTCursorResizeUpDown, + RCTCursorDefault, RCTCursorDisappearingItem, - RCTCursorIBeamCursorForVerticalLayout, - RCTCursorOperationNotAllowed, - RCTCursorDragLink, - RCTCursorDragCopy, - RCTCursorContextualMenu, + RCTCursorEastResize, + RCTCursorGrab, + RCTCursorGrabbing, + RCTCursorNorthResize, + RCTCursorNoDrop, + RCTCursorNotAllowed, + RCTCursorPointer, + RCTCursorRowResize, + RCTCursorSouthResize, + RCTCursorText, + RCTCursorVerticalText, + RCTCursorWestResize, }; -@interface RCTConvert (RCTCursor) - -+ (RCTCursor)RCTCursor:(id)json; #if TARGET_OS_OSX // [macOS -+ (NSCursor *)NSCursor:(RCTCursor)rctCursor; +inline static NSCursor *NSCursorFromRCTCursor(RCTCursor cursor) +{ + switch (cursor) { + case RCTCursorAlias: + return [NSCursor dragLinkCursor]; + case RCTCursorAuto: + return [NSCursor arrowCursor]; + case RCTCursorColumnResize: + return [NSCursor resizeLeftRightCursor]; + case RCTCursorContextualMenu: + return [NSCursor contextualMenuCursor]; + case RCTCursorCopy: + return [NSCursor dragCopyCursor]; + case RCTCursorCrosshair: + return [NSCursor crosshairCursor]; + case RCTCursorDefault: + return [NSCursor arrowCursor]; + case RCTCursorDisappearingItem: + return [NSCursor disappearingItemCursor]; + case RCTCursorEastResize: + return [NSCursor resizeRightCursor]; + case RCTCursorGrab: + return [NSCursor openHandCursor]; + case RCTCursorGrabbing: + return [NSCursor closedHandCursor]; + case RCTCursorNorthResize: + return [NSCursor resizeUpCursor]; + case RCTCursorNoDrop: + return [NSCursor operationNotAllowedCursor]; + case RCTCursorNotAllowed: + return [NSCursor operationNotAllowedCursor]; + case RCTCursorPointer: + return [NSCursor pointingHandCursor]; + case RCTCursorRowResize: + return [NSCursor resizeUpDownCursor]; + case RCTCursorSouthResize: + return [NSCursor resizeDownCursor]; + case RCTCursorText: + return [NSCursor IBeamCursor]; + case RCTCursorVerticalText: + return [NSCursor IBeamCursorForVerticalLayout]; + case RCTCursorWestResize: + return [NSCursor resizeLeftCursor]; + } +} #endif // macOS] -@end diff --git a/packages/react-native/React/Views/RCTCursor.m b/packages/react-native/React/Views/RCTCursor.m deleted file mode 100644 index a0f3b21146d907..00000000000000 --- a/packages/react-native/React/Views/RCTCursor.m +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -@implementation RCTConvert (RCTCursor) - -RCT_ENUM_CONVERTER( - RCTCursor, - (@{ - @"alias" : @(RCTCursorDragLink), - @"auto" : @(RCTCursorAuto), - @"col-resize" : @(RCTCursorResizeLeftRight), - @"context-menu" : @(RCTCursorContextualMenu), - @"copy" : @(RCTCursorDragCopy), - @"crosshair" : @(RCTCursorCrosshair), - @"default" : @(RCTCursorArrow), - @"disappearing-item" : @(RCTCursorDisappearingItem), - @"e-resize" : @(RCTCursorResizeRight), - @"grab" : @(RCTCursorOpenHand), - @"grabbing" : @(RCTCursorClosedHand), - @"n-resize" : @(RCTCursorResizeUp), - @"no-drop" : @(RCTCursorOperationNotAllowed), - @"not-allowed" : @(RCTCursorOperationNotAllowed), - @"pointer" : @(RCTCursorPointingHand), - @"row-resize" : @(RCTCursorResizeUpDown), - @"s-resize" : @(RCTCursorResizeDown), - @"text" : @(RCTCursorIBeam), - @"vertical-text" : @(RCTCursorIBeamCursorForVerticalLayout), - @"w-resize" : @(RCTCursorResizeLeft), - }), - RCTCursorAuto, - integerValue) - -#if TARGET_OS_OSX // [macOS -+ (NSCursor *)NSCursor:(RCTCursor)rctCursor -{ - NSCursor *cursor; - - switch (rctCursor) { - case RCTCursorArrow: - cursor = [NSCursor arrowCursor]; - break; - case RCTCursorClosedHand: - cursor = [NSCursor closedHandCursor]; - break; - case RCTCursorContextualMenu: - cursor = [NSCursor contextualMenuCursor]; - break; - case RCTCursorCrosshair: - cursor = [NSCursor crosshairCursor]; - break; - case RCTCursorDisappearingItem: - cursor = [NSCursor disappearingItemCursor]; - break; - case RCTCursorDragCopy: - cursor = [NSCursor dragCopyCursor]; - break; - case RCTCursorDragLink: - cursor = [NSCursor dragLinkCursor]; - break; - case RCTCursorIBeam: - cursor = [NSCursor IBeamCursor]; - break; - case RCTCursorIBeamCursorForVerticalLayout: - cursor = [NSCursor IBeamCursorForVerticalLayout]; - break; - case RCTCursorOpenHand: - cursor = [NSCursor openHandCursor]; - break; - case RCTCursorOperationNotAllowed: - cursor = [NSCursor operationNotAllowedCursor]; - break; - case RCTCursorPointingHand: - cursor = [NSCursor pointingHandCursor]; - break; - case RCTCursorResizeDown: - cursor = [NSCursor resizeDownCursor]; - break; - case RCTCursorResizeLeft: - cursor = [NSCursor resizeLeftCursor]; - break; - case RCTCursorResizeLeftRight: - cursor = [NSCursor resizeLeftRightCursor]; - break; - case RCTCursorResizeRight: - cursor = [NSCursor resizeRightCursor]; - break; - case RCTCursorResizeUp: - cursor = [NSCursor resizeUpCursor]; - break; - case RCTCursorResizeUpDown: - cursor = [NSCursor resizeUpDownCursor]; - break; - } - - return cursor; -} -#endif // macOS] - -@end diff --git a/packages/react-native/React/Views/RCTView.h b/packages/react-native/React/Views/RCTView.h index f556254869ef13..da1704914906ae 100644 --- a/packages/react-native/React/Views/RCTView.h +++ b/packages/react-native/React/Views/RCTView.h @@ -10,13 +10,10 @@ #import #import #import +#import // [macOS] [visionOS] #import // [macOS] #import -#if TARGET_OS_OSX // [macOS -#import -#endif // macOS] - #if !TARGET_OS_OSX // [macOS] extern const UIAccessibilityTraits SwitchAccessibilityTrait; #endif // [macOS] @@ -143,6 +140,8 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait; */ @property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets; +@property (nonatomic, assign) RCTCursor cursor; + /** * (Experimental and unused for Paper) Pointer event handlers. */ @@ -162,8 +161,6 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait; /** * macOS Properties */ -@property (nonatomic, assign) RCTCursor cursor; - @property (nonatomic, assign) CATransform3D transform3D; // `allowsVibrancy` is readonly on NSView, so let's create a new property to make it assignable diff --git a/packages/react-native/React/Views/RCTView.m b/packages/react-native/React/Views/RCTView.m index 1c9e45862ad73b..d9481022f067e0 100644 --- a/packages/react-native/React/Views/RCTView.m +++ b/packages/react-native/React/Views/RCTView.m @@ -179,6 +179,7 @@ - (instancetype)initWithFrame:(CGRect)frame _borderCurve = RCTBorderCurveCircular; _borderStyle = RCTBorderStyleSolid; _hitTestEdgeInsets = UIEdgeInsetsZero; + _cursor = RCTCursorAuto; #if TARGET_OS_OSX // [macOS _transform3D = CATransform3DIdentity; _shadowColor = nil; @@ -1208,7 +1209,11 @@ - (void)displayLayer:(CALayer *)layer } RCTUpdateShadowPathForView(self); - + +#if !TARGET_OS_OSX // [visionOS] + RCTUpdateHoverStyleForView(self); +#endif // [visionOS] + #if TARGET_OS_OSX // [macOS // clipsToBounds is stubbed out on macOS because it's not part of NSView layer.masksToBounds = self.clipsToBounds; @@ -1347,6 +1352,33 @@ static void RCTUpdateShadowPathForView(RCTView *view) } } +#if !TARGET_OS_OSX // [visionOS +static void RCTUpdateHoverStyleForView(RCTView *view) +{ + if (@available(iOS 17.0, *)) { + UIHoverStyle *hoverStyle = nil; + if ([view cursor] == RCTCursorPointer) { + const RCTCornerRadii cornerRadii = [view cornerRadii]; + const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero); +#if TARGET_OS_IOS + // Due to an Apple bug, it seems on iOS, `[UIShape shapeWithBezierPath:]` needs to + // be calculated in the superviews' coordinate space (view.frame). This is not true + // on other platforms like visionOS. + CGPathRef borderPath = RCTPathCreateWithRoundedRect(view.frame, cornerInsets, NULL); +#else // TARGET_OS_VISION + CGPathRef borderPath = RCTPathCreateWithRoundedRect(view.bounds, cornerInsets, NULL); +#endif + UIBezierPath *bezierPath = [UIBezierPath bezierPathWithCGPath:borderPath]; + CGPathRelease(borderPath); + UIShape *shape = [UIShape shapeWithBezierPath:bezierPath]; + + hoverStyle = [UIHoverStyle styleWithEffect:[UIHoverHighlightEffect effect] shape:shape]; + } + [view setHoverStyle:hoverStyle]; + } +} +#endif // visionOS] + - (void)updateClippingForLayer:(CALayer *)layer { CALayer *mask = nil; @@ -1474,8 +1506,9 @@ - (void)drawFocusRingMask - (void)resetCursorRects { [self discardCursorRects]; - NSCursor *cursor = [RCTConvert NSCursor:self.cursor]; - if (cursor) { + if ([self cursor] != RCTCursorAuto) + { + NSCursor *cursor = NSCursorFromRCTCursor(self.cursor); [self addCursorRect:self.bounds cursor:cursor]; } } diff --git a/packages/react-native/React/Views/RCTViewManager.m b/packages/react-native/React/Views/RCTViewManager.m index 53c82b94775669..b52dfc3f09d700 100644 --- a/packages/react-native/React/Views/RCTViewManager.m +++ b/packages/react-native/React/Views/RCTViewManager.m @@ -13,6 +13,7 @@ #import "RCTBridge.h" #import "RCTConvert+Transform.h" #import "RCTConvert.h" +#import "RCTCursor.h" // [macOS] [visionOS] #import "RCTLog.h" #import "RCTShadowView.h" #import "RCTUIManager.h" @@ -21,10 +22,6 @@ #import "RCTView.h" #import "UIView+React.h" -#if TARGET_OS_OSX // [macOS -#import "RCTCursor.h" -#endif // macOS] - @implementation RCTConvert (UIAccessibilityTraits) #if !TARGET_OS_OSX // [macOS] @@ -245,6 +242,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) // [visionOS] #if !TARGET_OS_OSX // [macOS] RCT_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat) #else // [macOS @@ -580,7 +578,6 @@ - (void)updateAccessibilityTraitsForRole:(RCTView *)view withDefaultView:(RCTVie #if TARGET_OS_OSX // [macOS #pragma mark - macOS properties -RCT_EXPORT_VIEW_PROPERTY(cursor, RCTCursor) RCT_EXPORT_VIEW_PROPERTY(onMouseEnter, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onMouseLeave, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onDragEnter, RCTDirectEventBlock) diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp index 58bb38b20385c1..59ab0732b4e9ee 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp @@ -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( diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h index b7bdd9afc7eb55..9e8d56f6d50a5a 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h @@ -51,6 +51,8 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps { Size shadowOffset{0, -3}; Float shadowOpacity{}; Float shadowRadius{3}; + + Cursor cursor{}; // Transform Transform transform{}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp index 42575ec6085f35..12a9dd40370e34 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp @@ -51,7 +51,7 @@ void ViewShadowNode::initialize() noexcept { viewProps.accessibilityElementsHidden || viewProps.accessibilityViewIsModal || viewProps.importantForAccessibility != ImportantForAccessibility::Auto || - viewProps.removeClippedSubviews || + viewProps.removeClippedSubviews || viewProps.cursor != Cursor::Auto || HostPlatformViewTraitsInitializer::formsStackingContext(viewProps); bool formsView = formsStackingContext || diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h index e38ab6b7b51805..bb8a03002d4a57 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h @@ -686,6 +686,102 @@ inline void fromRawValue( react_native_expect(false); } +// [macOS [visionOS] +inline void fromRawValue( + const PropsParserContext& context, + const RawValue& value, + Cursor& result) { + result = Cursor::Auto; + react_native_expect(value.hasType()); + if (!value.hasType()) { + return; + } + auto stringValue = (std::string)value; + if (stringValue == "alias") { + result = Cursor::Alias; + return; + } + if (stringValue == "auto") { + result = Cursor::Auto; + return; + } + if (stringValue == "col-resize") { + result = Cursor::ColumnResize; + return; + } + if (stringValue == "context-menu") { + result = Cursor::ContextualMenu; + return; + } + if (stringValue == "copy") { + result = Cursor::Copy; + return; + } + if (stringValue == "crosshair") { + result = Cursor::Crosshair; + return; + } + if (stringValue == "default") { + result = Cursor::Default; + return; + } + if (stringValue == "disappearing-item") { + result = Cursor::DisappearingItem; + return; + } + if (stringValue == "e-resize") { + result = Cursor::EastResize; + return; + } + if (stringValue == "grab") { + result = Cursor::Grab; + return; + } + if (stringValue == "grabbing") { + result = Cursor::Grabbing; + return; + } + if (stringValue == "n-resize") { + result = Cursor::NorthResize; + return; + } + if (stringValue == "no-drop") { + result = Cursor::NoDrop; + return; + } + if (stringValue == "not-allowed") { + result = Cursor::NotAllowed; + return; + } + if (stringValue == "pointer") { + result = Cursor::Pointer; + return; + } + if (stringValue == "row-resize") { + result = Cursor::RowResize; + return; + } + if (stringValue == "s-resize") { + result = Cursor::SouthResize; + return; + } + if (stringValue == "text") { + result = Cursor::Text; + return; + } + if (stringValue == "vertical-text") { + result = Cursor::VerticalText; + return; + } + if (stringValue == "w-resize") { + result = Cursor::WestResize; + return; + } + LOG(ERROR) << "Could not parse Cursor:" << stringValue; + react_native_expect(false); +} +// macOS] [visionOS] + inline void fromRawValue( const PropsParserContext& /*context*/, const RawValue& value, diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h b/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h index 162f2292cc6a64..b97035e7cde480 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h @@ -91,6 +91,31 @@ enum class BorderCurve : uint8_t { Circular, Continuous }; enum class BorderStyle : uint8_t { Solid, Dotted, Dashed }; +// [macOS [visionOS] +enum class Cursor : uint8_t { + Auto, + Alias, + ColumnResize, + ContextualMenu, + Copy, + Crosshair, + Default, + DisappearingItem, + EastResize, + Grab, + Grabbing, + NorthResize, + NoDrop, + NotAllowed, + Pointer, + RowResize, + SouthResize, + Text, + VerticalText, + WestResize, +}; +// macOS] [visionOS] + enum class LayoutConformance : uint8_t { Undefined, Classic, Strict }; template diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 40bb1d2ce67d5e..47ee80fab4a167 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -2,15 +2,46 @@ PODS: - boost (1.83.0) - DoubleConversion (1.1.6) - FBLazyVector (1000.0.0) - - FBReactNativeSpec (1000.0.0): - - RCT-Folly (= 2023.08.07.00) - - RCTRequired (= 1000.0.0) - - RCTTypeSafety (= 1000.0.0) - - React-Core (= 1000.0.0) - - React-jsi (= 1000.0.0) - - ReactCommon/turbomodule/core (= 1000.0.0) - fmt (9.1.0) - glog (0.3.5) + - MyNativeView (0.0.1): + - glog + - RCT-Folly (= 2023.08.07.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - NativeCxxModuleExample (0.0.1): + - glog + - RCT-Folly (= 2023.08.07.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - OCMock (3.9.3) - RCT-Folly (2023.08.07.00): - boost @@ -49,17 +80,20 @@ PODS: - React-callinvoker (1000.0.0) - React-Codegen (1000.0.0): - DoubleConversion - - FBReactNativeSpec - glog - RCT-Folly - RCTRequired - RCTTypeSafety - React-Core + - React-debug + - React-Fabric + - React-graphics - React-jsc - React-jsi - React-jsiexecutor - React-NativeModulesApple - - React-rncore + - React-rendererdebug + - React-utils - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - React-Core (1000.0.0): @@ -832,6 +866,8 @@ PODS: - React-jsi (= 1000.0.0) - React-perflogger (= 1000.0.0) - React-jsinspector (1000.0.0) + - React-jsitracing (1000.0.0): + - React-jsi - React-logger (1000.0.0): - glog - React-Mapbuffer (1000.0.0): @@ -863,13 +899,20 @@ PODS: - RCTTypeSafety - React-Core - React-CoreModules + - React-debug + - React-Fabric + - React-graphics - React-jsc - React-nativeconfig - React-NativeModulesApple - React-RCTFabric - React-RCTImage - React-RCTNetwork + - React-rendererdebug + - React-RuntimeApple + - React-RuntimeCore - React-runtimescheduler + - React-utils - ReactCommon/turbomodule/core - React-RCTBlob (1000.0.0): - RCT-Folly (= 2023.08.07.00) @@ -950,6 +993,32 @@ PODS: - RCT-Folly (= 2023.08.07.00) - React-debug - React-rncore (1000.0.0) + - React-RuntimeApple (1000.0.0): + - RCT-Folly/Fabric (= 2023.08.07.00) + - React-callinvoker + - React-Core/Default + - React-CoreModules + - React-cxxreact + - React-jsc + - React-jserrorhandler + - React-jsi + - React-jsiexecutor + - React-Mapbuffer + - React-NativeModulesApple + - React-RCTFabric + - React-RuntimeCore + - React-runtimeexecutor + - React-utils + - React-RuntimeCore (1000.0.0): + - glog + - RCT-Folly/Fabric (= 2023.08.07.00) + - React-cxxreact + - React-jsc + - React-jserrorhandler + - React-jsi + - React-jsiexecutor + - React-runtimeexecutor + - React-runtimescheduler - React-runtimeexecutor (1000.0.0): - React-jsi (= 1000.0.0) - React-runtimescheduler (1000.0.0): @@ -998,7 +1067,22 @@ PODS: - ScreenshotManager (0.0.1): - glog - RCT-Folly (= 2023.08.07.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen - React-Core + - React-debug + - React-Fabric + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - SocketRocket (0.7.0) - Yoga (1.14.0) @@ -1006,9 +1090,10 @@ DEPENDENCIES: - boost (from `../react-native/third-party-podspecs/boost.podspec`) - DoubleConversion (from `../react-native/third-party-podspecs/DoubleConversion.podspec`) - FBLazyVector (from `../react-native/Libraries/FBLazyVector`) - - FBReactNativeSpec (from `../react-native/React/FBReactNativeSpec`) - fmt (from `../react-native/third-party-podspecs/fmt.podspec`) - glog (from `../react-native/third-party-podspecs/glog.podspec`) + - MyNativeView (from `NativeComponentExample`) + - NativeCxxModuleExample (from `NativeCxxModuleExample`) - OCMock (~> 3.9.1) - RCT-Folly (from `../react-native/third-party-podspecs/RCT-Folly.podspec`) - RCT-Folly/Fabric (from `../react-native/third-party-podspecs/RCT-Folly.podspec`) @@ -1027,10 +1112,12 @@ DEPENDENCIES: - React-graphics (from `../react-native/ReactCommon/react/renderer/graphics`) - React-ImageManager (from `../react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) - React-jsc (from `../react-native/ReactCommon/jsc`) + - React-jsc/Fabric (from `../react-native/ReactCommon/jsc`) - React-jserrorhandler (from `../react-native/ReactCommon/jserrorhandler`) - React-jsi (from `../react-native/ReactCommon/jsi`) - React-jsiexecutor (from `../react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../react-native/ReactCommon/jsinspector-modern`) + - React-jsitracing (from `../react-native/ReactCommon/hermes/executor/`) - React-logger (from `../react-native/ReactCommon/logger`) - React-Mapbuffer (from `../react-native/ReactCommon`) - React-nativeconfig (from `../react-native/ReactCommon`) @@ -1051,6 +1138,8 @@ DEPENDENCIES: - React-RCTVibration (from `../react-native/Libraries/Vibration`) - React-rendererdebug (from `../react-native/ReactCommon/react/renderer/debug`) - React-rncore (from `../react-native/ReactCommon`) + - React-RuntimeApple (from `../react-native/ReactCommon/react/runtime/platform/ios`) + - React-RuntimeCore (from `../react-native/ReactCommon/react/runtime`) - React-runtimeexecutor (from `../react-native/ReactCommon/runtimeexecutor`) - React-runtimescheduler (from `../react-native/ReactCommon/react/renderer/runtimescheduler`) - React-utils (from `../react-native/ReactCommon/react/utils`) @@ -1071,12 +1160,14 @@ EXTERNAL SOURCES: :podspec: "../react-native/third-party-podspecs/DoubleConversion.podspec" FBLazyVector: :path: "../react-native/Libraries/FBLazyVector" - FBReactNativeSpec: - :path: "../react-native/React/FBReactNativeSpec" fmt: :podspec: "../react-native/third-party-podspecs/fmt.podspec" glog: :podspec: "../react-native/third-party-podspecs/glog.podspec" + MyNativeView: + :path: NativeComponentExample + NativeCxxModuleExample: + :path: NativeCxxModuleExample RCT-Folly: :podspec: "../react-native/third-party-podspecs/RCT-Folly.podspec" RCTRequired: @@ -1115,6 +1206,8 @@ EXTERNAL SOURCES: :path: "../react-native/ReactCommon/jsiexecutor" React-jsinspector: :path: "../react-native/ReactCommon/jsinspector-modern" + React-jsitracing: + :path: "../react-native/ReactCommon/hermes/executor/" React-logger: :path: "../react-native/ReactCommon/logger" React-Mapbuffer: @@ -1155,6 +1248,10 @@ EXTERNAL SOURCES: :path: "../react-native/ReactCommon/react/renderer/debug" React-rncore: :path: "../react-native/ReactCommon" + React-RuntimeApple: + :path: "../react-native/ReactCommon/react/runtime/platform/ios" + React-RuntimeCore: + :path: "../react-native/ReactCommon/react/runtime" React-runtimeexecutor: :path: "../react-native/ReactCommon/runtimeexecutor" React-runtimescheduler: @@ -1175,58 +1272,62 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 0686b6af8cbd638c784fea5afb789be66699823c DoubleConversion: ca54355f8932558971f6643521d62b9bc8231cee - FBLazyVector: 4284d9824090bc5a099233954071bc736e77d8bb - FBReactNativeSpec: 3af739618c41c120f846260583bfa7a5321395f8 + FBLazyVector: 13594cc2c4da792806868d79173f27ff976613b3 fmt: 03574da4b7ba40de39da59677ca66610ce8c4a02 glog: 3a72874c0322c7caf24931d3a2777cb7a3090529 + MyNativeView: 6cb0c7d3ae020a85cad002a6d9118750729e4e91 + NativeCxxModuleExample: 7fc41b0e5eafa575e581916df12de1f6407faf20 OCMock: 300b1b1b9155cb6378660b981c2557448830bdc6 RCT-Folly: e0ca497e3ebd44051a537b0e95c1dcfa3f4adb8f - RCTRequired: d856e236d5bbba94e3836b7a9a9ac3f19bc999ed - RCTTypeSafety: 941b573ed9143dae5000629bdddc1b8c5ac60a36 - React: 278b439bc243890a05e951ba665c5f0a47ebdce4 - React-callinvoker: d63e6babf698ff70097fdb0e3ff9d7a97c6959d8 - React-Codegen: 9b7fc89f7163d73816eb03fee0d9b870402bc44d - React-Core: 8b10f83fc9638359a525f75cd15d1a5b60f21be6 - React-CoreModules: 4d65a7766d28a94cae3b8f7ec67bee4a23291398 - React-cxxreact: c79026f2fbbb9fc1d862c42bac2cffc261110fba - React-debug: 0efd63dcefb638403577dafdbd072fcb14aea8cd - React-Fabric: d1db0d8442c34867bda3846a7b6df8538f9fc9cb - React-FabricImage: 33a8aff5ecafb9f30b2deab7a5113a9f4dc95be5 - React-graphics: f1b9bc7985ceca132dcc0143d1617baee3aec079 - React-ImageManager: f74dbe7927adfab2720c1a29734f95da87353d0d - React-jsc: b522318db3598eed3eb6d9d47493409ce86d141d - React-jserrorhandler: fe09653657e8264a10b5e4d79621b1d8e8af57f9 - React-jsi: d167ed449a088f45f05529c833421d241e5a19e4 - React-jsiexecutor: 9cf0c32cfc4a1299de4f63868cbbdd5796184768 - React-jsinspector: 447ff675d2378927c444bc01a5a2cfa34266832d - React-logger: f4a6e5c5a5d43f77868eff225bade9c6a27dfa7e - React-Mapbuffer: c669260718a94c4e5e0a6c0697b90a443ea5eeaf - React-nativeconfig: b98997b3864a7d27724c695476c048df23fdbee0 - React-NativeModulesApple: a295239ca5480efc3426aa4c3978e7eb497aa17c - React-perflogger: 6cc92ff36b3f8f1b06f599a0089660d1d43e0849 - React-RCTActionSheet: 898051d7504709b7784eb301458c7d5cd04a5153 - React-RCTAnimation: d338c1ca08e3f0f44fb72c3a13a22031bca2de80 - React-RCTAppDelegate: 4a331a5ff9d2a8901caf617b51db372ce52b319f - React-RCTBlob: 66f8443906051bc07763a740604b062d00d4de56 - React-RCTFabric: 9e8b711a363b43835fc575b45c033fa066236c05 - React-RCTImage: 4a73720ab915f732448bfb059430ddec7fb53547 - React-RCTLinking: 119405f2eb2abe4d36baaae87f83a6bd29d9dcfb - React-RCTNetwork: 906ffc6133672762d481711498f816f019bdf6f9 - React-RCTPushNotification: 45e1f9f904b49931d8455726d8a2237c2acc124c - React-RCTSettings: fa6cd7ded0aae23c3487119115440e1e93b9c56c - React-RCTTest: 1684debc65e0d9a4f2534f4d5911600c6925549c - React-RCTText: 4dbdb083cae614b9aecf713b4bb32ba1bcb34cc2 - React-RCTVibration: 2f1aaea320f946f665f9554e5d469a2a4330b8ff - React-rendererdebug: 991be38e99c0da3b68f75bdf61c81a6f02d59804 - React-rncore: c79aa1c28359460d1a2209811c673afeb5109a29 - React-runtimeexecutor: 65c3f76be4ca9f82f61bc3aa2d39aaf79783b657 - React-runtimescheduler: 1deaf97aacf3bc768cffd26c10072f6159df3c0d - React-utils: 37edc94df8b25164137ec03489192f086525a43d - ReactCommon: a0321c82264cf62a53518bb8a35ca5f80515bf85 - ReactCommon-Samples: c129361c42ea5c59b893f80520f2d24d61586721 - ScreenshotManager: b2b5732286c9d80ceca6a8576c4fd9f619608d96 + RCTRequired: ec67360771e1a976bac5ed873345a75ba0adcb7b + RCTTypeSafety: 5218f167c52e3f87a7df471f0a30df3a8ba33465 + React: 018827c2813a2e9e3fb0a1527b17353a542054be + React-callinvoker: 2fa244c3b3ce3c502e34de4163b6a8f1c5972602 + React-Codegen: 45962e8bd5c611779190bbaec2b1541837906576 + React-Core: 27b561f622d60ca18f2a14ff688c0b0bbed348b6 + React-CoreModules: 323339a5b67bcfeed4c8f357f5ff27303592b756 + React-cxxreact: a799a601a6f0e36103e36a1ededcf3f03159d3e0 + React-debug: 574a1a5d0c9305f7b8f683b197a96599bc0bba78 + React-Fabric: 006a1b37e8c7ae3b2c89e0772fd7ca1890b35b25 + React-FabricImage: 61f3ecde18fa320fea233448b2178ae075c4f049 + React-graphics: 7f104a00573d3ac1c81e94dd282cb94cf67991bd + React-ImageManager: 6cf1890f8c3ade795dcf2b54b266412e5c33a213 + React-jsc: a02e7898a610296918e333746dbb10a07b03650a + React-jserrorhandler: 7670a9b54ccfb3d1574c5122d20ad474ffa83e7c + React-jsi: ba7875ee0f0acd7af6a9d61ca9fcd16526600de3 + React-jsiexecutor: da2e93427a66e6f5b06086f7a6325558754ac3a8 + React-jsinspector: 1ed30c68a3c2828315744cbce7b071bd8d0313d7 + React-jsitracing: 3d7ebf6fc4d229fef0d9ed2e51fadb9aee34be8f + React-logger: 75e96c0db8be46e810e3cc5a15e0ffdca537dacd + React-Mapbuffer: 2e43a5a9a317f8ca3bfc42d1e3353f54cc4decf1 + React-nativeconfig: 77ad01da345ece765cf6babfa4db37e276de2663 + React-NativeModulesApple: 994e53995a69d3df63ec677b36e9485754565ed2 + React-perflogger: c2469a2a1f9167c06250e6bcff5b7a4e5bf84a5a + React-RCTActionSheet: 531895fa278c80eec242f7fb048eec1ad3c8c642 + React-RCTAnimation: 0accc050caf0c6e2319d4671575acb4dbfbd5ba3 + React-RCTAppDelegate: 0ab1e42f41b1ab0889f21b09a196ea3163bcfa9b + React-RCTBlob: 973d25279348cd23625f28559a733934897b9008 + React-RCTFabric: 57f628ad6f87c51209eec66f74a3ecbb09903ba6 + React-RCTImage: f2a0c56e845196220d1b59c95f55c68c741d036b + React-RCTLinking: cf1af2304382a2e230c44885ac6d586fc1ed115b + React-RCTNetwork: e862b009499c50e4612ecc605c29913827049222 + React-RCTPushNotification: 1e7460e6bec344bc38dbaa76078f98fb93f08bc8 + React-RCTSettings: e536749aba8b9fd8c30408073b28b3246347873d + React-RCTTest: beafcecd4f31d28d04024e154fb7fb8936f173de + React-RCTText: 8c26abc0325c6e1ce8c7c2f83373bcf6d72731e8 + React-RCTVibration: 4b0637d7953ce3db36f53c849567c6838ef1247a + React-rendererdebug: 931314933469ab3f2e12a263d94936c131cdd411 + React-rncore: 2d0652b4614151b6221142950b371437b3162a78 + React-RuntimeApple: fe392c36edd0e1887c525c03d0df36bb004305e6 + React-RuntimeCore: 71fc743cb1d57487a58de67100d84fd09c985743 + React-runtimeexecutor: 2d4e057f9627b687ace73f3c3a0646242ee2cb60 + React-runtimescheduler: 7deed0f16b07c9f82721c69dee1f7389064a2d1b + React-utils: 91eaf0a0feba835a4e3eb8bd368ff9259789cde7 + ReactCommon: a3c60b933163c736735323eac2392cb270db93eb + ReactCommon-Samples: 870897396f5c528cdc2c8fc49da8be7f70876faf + ScreenshotManager: 9940d0179e2ee682548232f9e406b7d23d8b581d SocketRocket: f6c6249082c011e6de2de60ed641ef8bbe0cfac9 - Yoga: 03cb6e99ea2ee88d3239531f01a6c147bfa0a9e1 + Yoga: 1e5cd719d4dd69cceda8456c7059aa064d35a107 PODFILE CHECKSUM: b1a8404bc4b61ed54677e78b8e2f8269707283c7 diff --git a/packages/rn-tester/js/examples/Cursor/CursorExample.js b/packages/rn-tester/js/examples/Cursor/CursorExample.js new file mode 100644 index 00000000000000..bba1ed7870850c --- /dev/null +++ b/packages/rn-tester/js/examples/Cursor/CursorExample.js @@ -0,0 +1,195 @@ +/** + * 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. + * + * @format + */ + +'use strict'; + +// [macOS] + +const React = require('react'); +const {StyleSheet, Text, View} = require('react-native'); + +const styles = StyleSheet.create({ + invisibleBox: { + width: 100, + height: 100, + }, + box: { + width: 100, + height: 100, + borderWidth: 2, + }, + circle: { + width: 100, + height: 100, + borderWidth: 2, + borderRadius: 100, + }, + halfcircle: { + width: 100, + height: 100, + borderWidth: 2, + borderTopStartRadius: 100, + borderBottomStartRadius: 100, + }, + solid: { + backgroundColor: 'blue', + }, + pointer: { + cursor: 'alias', + }, + row: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 10, + }, + centerContent: { + justifyContent: 'center', + alignItems: 'center', + }, +}); + +function CursorExampleAuto() { + return ( + + + + + + + + + ); +} + +function CursorExamplePointer() { + return ( + + + + + + + + + ); +} + +function CursorExamplePointer() { + return ( + + + + + + + + + ); +} + +function CursorExampleViewFlattening() { + return ( + + + pointer + + + ); +} + +// [macOS +function CursorExampleMacOS() { + return ( + + <> + + auto + + + default + + + context-menu + + + pointer + + + text + + + vertical-text + + + alias + + + copy + + + not-allowed + + + grab + + + grabbing + + + col-resize + + + row-resize + + + n-resize + + + e-resize + + + s-resize + + + w-resize + + + + ); +} +// macOS] + +exports.title = 'Cursor'; +exports.category = 'UI'; +exports.description = + 'Demonstrates setting a cursor, which affects the appearance when a pointer is over the View.'; +exports.examples = [ + { + title: 'Default', + description: "Cursor: 'auto' or no cursor set ", + render: CursorExampleAuto, + }, + { + title: 'Pointer', + description: 'cursor: pointer', + render: CursorExamplePointer, + }, + { + title: 'Pointer', + description: 'Views with a cursor do not get flattened', + render: CursorExampleViewFlattening, + }, + // [macOS + { + title: 'macOS Cursors', + description: 'macOS supports many more cursors', + render: CursorExampleMacOS, + }, + // macOS] +]; diff --git a/packages/rn-tester/js/utils/RNTesterList.ios.js b/packages/rn-tester/js/utils/RNTesterList.ios.js index cbecdf8d5334f1..d8e8953e909b98 100644 --- a/packages/rn-tester/js/utils/RNTesterList.ios.js +++ b/packages/rn-tester/js/utils/RNTesterList.ios.js @@ -234,7 +234,10 @@ const APIs: Array = ([ key: 'CrashExample', module: require('../examples/Crash/CrashExample'), }, - + { + key: 'CursorExample', + module: require('../examples/Cursor/CursorExample'), + }, { key: 'DevSettings', module: require('../examples/DevSettings/DevSettingsExample'),