Skip to content

Commit

Permalink
fix: refactor visionos_hoverStyle to take a string and new arch support
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski committed Oct 15, 2023
1 parent 9fd0e8a commit 6f8d985
Show file tree
Hide file tree
Showing 12 changed files with 80 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type {
AccessibilityState,
AccessibilityValue,
} from '../View/ViewAccessibility';
import type {HoverStyle} from '../View/ViewPropTypes';
import type {HoverEffect} from '../View/ViewPropTypes';

import {PressabilityDebugView} from '../../Pressability/PressabilityDebug';
import usePressability from '../../Pressability/usePressability';
Expand All @@ -33,9 +33,7 @@ import useAndroidRippleForView, {
import * as React from 'react';
import {useImperativeHandle, useMemo, useRef, useState} from 'react';

const defaultHoverStyle: HoverStyle = {
effectType: 'automatic',
};
const defaultHoverEffect: HoverEffect = 'highlight';

type ViewStyleProp = $ElementType<React.ElementConfig<typeof View>, 'style'>;

Expand All @@ -44,7 +42,7 @@ export type StateCallbackType = $ReadOnly<{|
|}>;

type VisionOSProps = $ReadOnly<{|
visionos_hoverStyle?: ?HoverStyle,
visionos_hoverEffect?: ?HoverEffect,
|}>;

type Props = $ReadOnly<{|
Expand Down Expand Up @@ -245,7 +243,7 @@ function Pressable(props: Props, forwardedRef): React.Node {
style,
testOnly_pressed,
unstable_pressDelay,
visionos_hoverStyle = defaultHoverStyle,
visionos_hoverEffect = defaultHoverEffect,
...restProps
} = props;

Expand Down Expand Up @@ -356,7 +354,7 @@ function Pressable(props: Props, forwardedRef): React.Node {
ref={mergedRef}
style={typeof style === 'function' ? style({pressed}) : style}
collapsable={false}
visionos_hoverStyle={visionos_hoverStyle}>
visionos_hoverEffect={visionos_hoverEffect}>
{typeof children === 'function' ? children({pressed}) : children}
{__DEV__ ? <PressabilityDebugView color="red" hitSlop={hitSlop} /> : null}
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

import type {ColorValue} from '../../StyleSheet/StyleSheet';
import type {HoverStyle} from '../View/ViewPropTypes';
import type {HoverEffect} from '../View/ViewPropTypes';
import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback';

import View from '../../Components/View/View';
Expand All @@ -34,7 +34,7 @@ type IOSProps = $ReadOnly<{|
|}>;

type VisionOSProps = $ReadOnly<{|
hoverStyle?: ?HoverStyle,
hoverEffect?: ?HoverEffect,
|}>;

type Props = $ReadOnly<{|
Expand Down Expand Up @@ -347,7 +347,7 @@ class TouchableHighlight extends React.Component<Props, State> {
nextFocusLeft={this.props.nextFocusLeft}
nextFocusRight={this.props.nextFocusRight}
nextFocusUp={this.props.nextFocusUp}
visionos_hoverStyle={this.props.hoverStyle}
visionos_hoverEffect={this.props.hoverEffect}
focusable={
this.props.focusable !== false && this.props.onPress !== undefined
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type * as React from 'react';
import {Constructor} from '../../../types/private/Utilities';
import {TimerMixin} from '../../../types/private/TimerMixin';
import {NativeMethods} from '../../../types/public/ReactNativeTypes';
import {HoverStyle, TVParallaxProperties} from '../View/ViewPropTypes';
import {HoverEffect, TVParallaxProperties} from '../View/ViewPropTypes';
import {TouchableMixin} from './Touchable';
import {TouchableWithoutFeedbackProps} from './TouchableWithoutFeedback';

Expand Down Expand Up @@ -90,7 +90,7 @@ export interface TouchableOpacityProps
/**
* Hover style to apply to the view. Only supported on VisionOS.
*/
visionos_hoverStyle?: HoverStyle | undefined;
visionos_hoverEffect?: HoverEffect | undefined;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
import type {HoverStyle} from '../View/ViewPropTypes';
import type {HoverEffect} from '../View/ViewPropTypes';
import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback';

import Animated from '../../Animated/Animated';
Expand All @@ -22,9 +22,7 @@ import flattenStyle from '../../StyleSheet/flattenStyle';
import Platform from '../../Utilities/Platform';
import * as React from 'react';

const defaultHoverStyle: HoverStyle = {
effectType: 'automatic',
};
const defaultHoverEffect: HoverEffect = 'highlight';

type TVProps = $ReadOnly<{|
hasTVPreferredFocus?: ?boolean,
Expand All @@ -36,7 +34,7 @@ type TVProps = $ReadOnly<{|
|}>;

type VisionOSProps = $ReadOnly<{|
visionos_hoverStyle?: ?HoverStyle,
visionos_hoverEffect?: ?HoverEffect,
|}>;

type Props = $ReadOnly<{|
Expand Down Expand Up @@ -140,8 +138,8 @@ type State = $ReadOnly<{|
*
*/
class TouchableOpacity extends React.Component<Props, State> {
static defaultProps: {|visionos_hoverStyle: HoverStyle|} = {
visionos_hoverStyle: defaultHoverStyle,
static defaultProps: {|visionos_hoverEffect: HoverEffect|} = {
visionos_hoverEffect: defaultHoverEffect,
};

state: State = {
Expand Down Expand Up @@ -300,7 +298,7 @@ class TouchableOpacity extends React.Component<Props, State> {
nextFocusUp={this.props.nextFocusUp}
hasTVPreferredFocus={this.props.hasTVPreferredFocus}
hitSlop={this.props.hitSlop}
visionos_hoverStyle={this.props.visionos_hoverStyle}
visionos_hoverEffect={this.props.visionos_hoverEffect}
focusable={
this.props.focusable !== false && this.props.onPress !== undefined
}
Expand Down
17 changes: 2 additions & 15 deletions packages/react-native/Libraries/Components/View/ViewPropTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,7 @@ import {LayoutChangeEvent, PointerEvents} from '../../Types/CoreEventTypes';
import {Touchable} from '../Touchable/Touchable';
import {AccessibilityProps} from './ViewAccessibility';

export type HoverStyle = {
/**
* If true the hover effect is enabled. Defaults to true.
*/
enabled: boolean;
/**
* Hover effect type to apply to the view.
*/
effectType: 'automatic' | 'lift' | 'highlight';
/**
* Corner radius of the hover effect.
*/
cornerRadius?: number | undefined;
};
export type HoverEffect = 'lift' | 'highlight';

export type TVParallaxProperties = {
/**
Expand Down Expand Up @@ -140,7 +127,7 @@ export interface ViewPropsIOS extends TVViewPropsIOS {
/**
* Hover style to apply to the view. Only supported on VisionOS.
*/
visionos_hoverStyle?: HoverStyle | undefined;
visionos_hoverEffect?: HoverEffect | undefined;
}

export interface ViewPropsAndroid {
Expand Down
17 changes: 2 additions & 15 deletions packages/react-native/Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,20 +263,7 @@ type AndroidDrawableRipple = $ReadOnly<{|
rippleRadius?: ?number,
|}>;

export type HoverStyle = $ReadOnly<{|
/**
* If true the hover effect is enabled. Defaults to true.
*/
enabled?: ?boolean,
/**
* Hover effect type to apply to the view.
*/
effectType: 'automatic' | 'lift' | 'highlight',
/**
* Corner radius of the hover effect.
*/
cornerRadius?: ?number,
|}>;
export type HoverEffect = 'lift' | 'highlight';

type AndroidDrawable = AndroidDrawableThemeAttr | AndroidDrawableRipple;

Expand Down Expand Up @@ -455,7 +442,7 @@ type IOSViewProps = $ReadOnly<{|
/**
* Hover style to apply to the view. Only supported on VisionOS.
*/
visionos_hoverStyle?: ?HoverStyle,
visionos_hoverEffect?: ?HoverEffect,
|}>;

export type ViewProps = $ReadOnly<{|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,12 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
oldViewProps.borderColors != newViewProps.borderColors) {
needsInvalidateLayer = YES;
}

#if TARGET_OS_VISION
if (oldViewProps.visionos_hoverEffect != newViewProps.visionos_hoverEffect) {
[self updateHoverEffect:[NSString stringWithUTF8String:newViewProps.visionos_hoverEffect.c_str()]];
}
#endif

// `nativeId`
if (oldViewProps.nativeId != newViewProps.nativeId) {
Expand Down Expand Up @@ -513,6 +519,28 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
}
}

#if TARGET_OS_VISION
- (void) updateHoverEffect:(NSString*)hoverEffect {
if (hoverEffect == nil || [hoverEffect isEqualToString:@""]) {
self.hoverStyle = nil;
return;
}

UIShape *shape = [UIShape rectShapeWithCornerRadius:self.layer.cornerRadius];
id<UIHoverEffect> effect;

if ([hoverEffect isEqualToString:@"lift"]) {
effect = [UIHoverLiftEffect effect];
} else if ([hoverEffect isEqualToString:@"highlight"]) {
effect = [UIHoverHighlightEffect effect];
}

if (hoverEffect != nil) {
self.hoverStyle = [UIHoverStyle styleWithEffect:effect shape:shape];
}
}
#endif

static RCTCornerRadii RCTCornerRadiiFromBorderRadii(BorderRadii borderRadii)
{
return RCTCornerRadii{
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/React/Views/RCTView.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait;
/**
* The hover style to apply to a view, including an effect and a shape to use for displaying that effect.
*/
@property (nonatomic, copy) NSDictionary *hoverStyleProperties;
@property (nonatomic, copy) NSString *hoverEffect;
#endif

/**
Expand Down
31 changes: 12 additions & 19 deletions packages/react-native/React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -668,33 +668,26 @@ - (UIEdgeInsets)bordersAsInsets


#if TARGET_OS_VISION
- (void)setHoverStyleProperties:(NSDictionary *)hoverStyleProperties {
_hoverStyleProperties = hoverStyleProperties;
- (void)setHoverEffect:(NSString *)hoverEffect {
_hoverEffect = hoverEffect;

BOOL enabled = _hoverStyleProperties[@"enabled"] != nil ? [_hoverStyleProperties[@"enabled"] boolValue] : YES;

if (!enabled || hoverStyleProperties == nil) {
if (hoverEffect == nil) {
self.hoverStyle = nil;
return;
}

NSString *effectType = (NSString *)[_hoverStyleProperties objectForKey:@"effectType"];
NSNumber *cornerRadius = (NSNumber *)[_hoverStyleProperties objectForKey:@"cornerRadius"];

float cornerRadiusFloat = [cornerRadius floatValue];
UIShape *shape = [UIShape rectShapeWithCornerRadius:_borderRadius];
id<UIHoverEffect> effect;

UIShape *shape = [UIShape rectShapeWithCornerRadius:cornerRadiusFloat];
id<UIHoverEffect> hoverEffect;

if ([effectType isEqualToString:@"lift"]) {
hoverEffect = [UIHoverLiftEffect effect];
} else if ([effectType isEqualToString:@"highlight"]) {
hoverEffect = [UIHoverHighlightEffect effect];
} else if ([effectType isEqualToString:@"automatic"]) {
hoverEffect = [UIHoverAutomaticEffect effect];
if ([hoverEffect isEqualToString:@"lift"]) {
effect = [UIHoverLiftEffect effect];
} else if ([hoverEffect isEqualToString:@"highlight"]) {
effect = [UIHoverHighlightEffect effect];
}

self.hoverStyle = [UIHoverStyle styleWithEffect:hoverEffect shape:shape];
if (hoverEffect != nil) {
self.hoverStyle = [UIHoverStyle styleWithEffect:effect shape:shape];
}
}
#endif

Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ - (RCTShadowView *)shadowView
RCT_REMAP_VIEW_PROPERTY(testID, reactAccessibilityElement.accessibilityIdentifier, NSString)

RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
RCT_REMAP_VISIONOS_VIEW_PROPERTY(visionos_hoverStyle, hoverStyleProperties, NSDictionary)
RCT_REMAP_VISIONOS_VIEW_PROPERTY(visionos_hoverEffect, hoverEffect, NSString)
RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t)
RCT_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat)
RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ BaseViewProps::BaseViewProps(
"backgroundColor",
sourceProps.backgroundColor,
{})),
#if TARGET_OS_VISION
visionos_hoverEffect(
CoreFeatures::enablePropIteratorSetter
? sourceProps.visionos_hoverEffect
: convertRawProp(
context,
rawProps,
"visionos_hoverEffect",
sourceProps.visionos_hoverEffect,
{})),
#endif
borderRadii(
CoreFeatures::enablePropIteratorSetter ? sourceProps.borderRadii
: convertRawProp(
Expand Down Expand Up @@ -281,6 +292,9 @@ void BaseViewProps::setProp(
RAW_SET_PROP_SWITCH_CASE_BASIC(collapsable);
RAW_SET_PROP_SWITCH_CASE_BASIC(removeClippedSubviews);
RAW_SET_PROP_SWITCH_CASE_BASIC(experimental_layoutConformance);
#if TARGET_OS_VISION
RAW_SET_PROP_SWITCH_CASE_BASIC(visionos_hoverEffect);
#endif
// events field
VIEW_EVENT_CASE(PointerEnter);
VIEW_EVENT_CASE(PointerEnterCapture);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps {
PointerEventsMode pointerEvents{};
EdgeInsets hitSlop{};
bool onLayout{};

#if TARGET_OS_VISION
std::string visionos_hoverEffect{};
#endif

ViewEvents events{};

Expand Down

0 comments on commit 6f8d985

Please sign in to comment.