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

Support onPress/onLongPress in usePressableState #1061

Merged
merged 3 commits into from
Oct 12, 2021
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Support onPress/onLongPress in usePressableState",
"packageName": "@fluentui-react-native/interactive-hooks",
"email": "[email protected]",
"dependentChangeType": "patch"
}
75 changes: 0 additions & 75 deletions packages/utils/interactive-hooks/etc/interactive-hooks.api.md

This file was deleted.

71 changes: 45 additions & 26 deletions packages/utils/interactive-hooks/src/useAsPressable.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { PressableProps, Platform } from 'react-native';
import { PressableProps } from 'react-native';
import { PressableFocusProps, PressableHoverProps, PressablePressProps } from './Pressability/Pressability.types';
import {
IPressableHooks,
Expand Down Expand Up @@ -73,6 +73,16 @@ function useFocusHelper(props: PressableFocusProps): [PressableFocusProps, IFocu
function usePressHelper(props: PressablePressProps): [PressablePressProps, IPressState] {
const [pressState, setPressState] = React.useState({ pressed: false });

const onPress = React.useCallback(
(e) => {
setPressState({ pressed: true });
if (props.onPress) {
props.onPress(e);
}
},
[setPressState, props.onPress],
);

const onPressIn = React.useCallback(
(e) => {
setPressState({ pressed: true });
Expand All @@ -92,27 +102,17 @@ function usePressHelper(props: PressablePressProps): [PressablePressProps, IPres
},
[setPressState, props.onPressOut],
);
return [{ onPressIn, onPressOut }, pressState];
}

/**
* useAsPressable wraps the usePressability hook that will be available in RN 0.63 and beyond. Once this library is on a recent enough
* version of react-native this implementation can switch to using the official version.
*
* The useAsPressable hook adds a simple state change function for listening to hover, press, and focus events on the base pressability implementation
* @param props - input props for the component, mixed in with pressable and pressability options
*/
// eslint-disable-next-line @typescript-eslint/ban-types
export function useAsPressable<T extends object>(props: IWithPressableOptions<T>): IPressableHooks<T> {
const [hoverProps, hoverState] = useHoverHelper(props);
const [focusProps, focusState] = useFocusHelper(props);
const [pressProps, pressState] = usePressHelper(props);
const pressabilityProps = usePressability({ ...props, ...hoverProps, ...focusProps, ...pressProps });

return {
props: { ...props, ...pressabilityProps },
state: { ...hoverState, ...pressState, ...focusState },
};
const onLongPress = React.useCallback(
(e) => {
setPressState({ pressed: true });
if (props.onLongPress) {
props.onLongPress(e);
}
},
[setPressState, props.onLongPress],
);
return [{ onPress, onPressIn, onPressOut, onLongPress }, pressState];
}

/**
Expand Down Expand Up @@ -148,6 +148,26 @@ export function useHoverState<T extends object>(props: IWithPressableOptions<T>)
return [{ ...props, ...usePressability({ ...props, ...hoverProps }) }, hoverState];
}

/**
* useAsPressable wraps the usePressability hook that will be available in RN 0.63 and beyond. Once this library is on a recent enough
* version of react-native this implementation can switch to using the official version.
*
* The useAsPressable hook adds a simple state change function for listening to hover, press, and focus events on the base pressability implementation
* @param props - input props for the component, mixed in with pressable and pressability options
*/
// eslint-disable-next-line @typescript-eslint/ban-types
export function useAsPressable<T extends object>(props: IWithPressableOptions<T>): IPressableHooks<T> {
const [hoverProps, hoverState] = useHoverHelper(props);
const [focusProps, focusState] = useFocusHelper(props);
const [pressProps, pressState] = usePressHelper(props);
const pressabilityProps = usePressability({ ...props, ...hoverProps, ...focusProps, ...pressProps });

return {
props: { ...props, ...pressabilityProps },
state: { ...hoverState, ...focusState, ...pressState },
};
}

/**
* This routine hooks the props to pass to a Pressable component to obtain the current state of the Pressable as well as generating
* state change updates when those props change. This allows a parent component to control the render of the whole component rather than having
Expand All @@ -157,11 +177,10 @@ export function useHoverState<T extends object>(props: IWithPressableOptions<T>)
* @returns - modified props to pass into the Pressable as well as the current state with regards to hover, focus, and press
*/
export function usePressableState(props: PressablePropsExtended): { props: PressableProps; state: IPressableState } {
const { onPressIn, onPressOut, onHoverIn, onHoverOut, onFocus, onBlur, ...rest } = props;
const { onPress, onPressIn, onPressOut, onLongPress, onHoverIn, onHoverOut, onFocus, onBlur, ...rest } = props;
const [hoverProps, hoverState] = useHoverHelper({ onHoverIn, onHoverOut });
const [focusProps, focusState] = useFocusHelper({ onFocus, onBlur });
const [pressProps, pressState] = usePressHelper({ onPressIn, onPressOut });
const platformSupportsHover = Platform.OS !== 'android' && Platform.OS !== 'ios';
const [hoverProps, hoverState] = platformSupportsHover ? useHoverHelper({ onHoverIn, onHoverOut }) : [{}, {}];
const [pressProps, pressState] = usePressHelper({ onPress, onPressIn, onPressOut, onLongPress });

return { props: { ...rest, ...focusProps, ...pressProps, ...hoverProps }, state: { ...focusState, ...pressState, ...hoverState } };
return { props: { ...hoverProps, ...focusProps, ...pressProps, ...rest }, state: { ...hoverState, ...focusState, ...pressState } };
}