Skip to content

Commit

Permalink
feat: introduce href to back button
Browse files Browse the repository at this point in the history
  • Loading branch information
kacperkapusciak authored and satya164 committed Oct 23, 2023
1 parent e4815c5 commit 5561b2b
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 10 deletions.
16 changes: 14 additions & 2 deletions packages/elements/src/Header/HeaderBackButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function HeaderBackButton({
accessibilityLabel = label && label !== 'Back' ? `${label}, back` : 'Go back',
testID,
style,
href,
}: HeaderBackButtonProps) {
const { colors, fonts } = useTheme();
const { direction } = useLocale();
Expand Down Expand Up @@ -147,13 +148,24 @@ export function HeaderBackButton({
);
};

const handlePress = () => onPress && requestAnimationFrame(onPress);
const handlePress = (e: any) => {
const ignoreEvents =
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
(e.button == null || e.button === 0); // ignore everything but left clicks

Check warning on line 154 in packages/elements/src/Header/HeaderBackButton.tsx

View check run for this annotation

Codecov / codecov/patch

packages/elements/src/Header/HeaderBackButton.tsx#L154

Added line #L154 was not covered by tests

if (Platform.OS === 'web' && href && ignoreEvents) {
e.preventDefault();

Check warning on line 157 in packages/elements/src/Header/HeaderBackButton.tsx

View check run for this annotation

Codecov / codecov/patch

packages/elements/src/Header/HeaderBackButton.tsx#L157

Added line #L157 was not covered by tests
}

return onPress && requestAnimationFrame(onPress);
};

return (
<PlatformPressable
disabled={disabled}
href={href}
accessible
accessibilityRole="button"
accessibilityRole={Platform.OS === 'web' && href ? 'link' : 'button'}
accessibilityLabel={accessibilityLabel}
testID={testID}
onPress={disabled ? undefined : handlePress}
Expand Down
7 changes: 3 additions & 4 deletions packages/elements/src/Header/HeaderBackContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { getNamedContext } from '../getNamedContext';

export const HeaderBackContext = getNamedContext<{ title: string } | undefined>(
'HeaderBackContext',
undefined
);
export const HeaderBackContext = getNamedContext<
{ title: string; href?: string } | undefined
>('HeaderBackContext', undefined);
1 change: 1 addition & 0 deletions packages/elements/src/PlatformPressable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type Props = Omit<PressableProps, 'style'> & {
pressColor?: string;
pressOpacity?: number;
style?: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
href?: string;
children: React.ReactNode;
};

Expand Down
4 changes: 4 additions & 0 deletions packages/elements/src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ export type HeaderBackButtonProps = HeaderButtonProps & {
* Function which returns a React Element to display custom image in header's back button.
*/
backImage?: (props: { tintColor: string }) => React.ReactNode;
/**
* The `href` to use for the anchor tag on web
*/
href?: string;
/**
* Label text for the button. Usually the title of the previous screen.
* By default, this is only shown on iOS.
Expand Down
10 changes: 8 additions & 2 deletions packages/native-stack/src/views/NativeStackView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {
SafeAreaProviderCompat,
Screen,
} from '@react-navigation/elements';
import type {
import {
ParamListBase,
StackNavigationState,
useLinkTools,
} from '@react-navigation/native';
import * as React from 'react';
import { Image, StyleSheet, View } from 'react-native';
Expand All @@ -33,6 +34,7 @@ const TRANSPARENT_PRESENTATIONS = [

export function NativeStackView({ state, descriptors }: Props) {
const parentHeaderBack = React.useContext(HeaderBackContext);
const { buildHref } = useLinkTools();

return (
<SafeAreaProviderCompat>
Expand All @@ -53,6 +55,10 @@ export function NativeStackView({ state, descriptors }: Props) {
previousDescriptor.options,
previousDescriptor.route.name
),
href: buildHref(
previousDescriptor.route.name,
previousDescriptor.route.params
),
}
: parentHeaderBack;

Expand Down Expand Up @@ -125,7 +131,7 @@ export function NativeStackView({ state, descriptors }: Props) {
: undefined
}
onPress={navigation.goBack}
canGoBack={canGoBack}
href={headerBack.href}
/>
)
: headerLeft
Expand Down
4 changes: 4 additions & 0 deletions packages/stack/src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ export type StackHeaderProps = {
* Title of the previous screen.
*/
title: string;
/**
* The `href` to use for the anchor tag on web
*/
href?: string;
};
/**
* Animated nodes representing the progress of the animation.
Expand Down
1 change: 1 addition & 0 deletions packages/stack/src/views/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const Header = React.memo(function Header({
}
headerStatusBarHeight={statusBarHeight}
onGoBack={back ? goBack : undefined}
href={back ? back.href : undefined}
styleInterpolator={styleInterpolator}
/>
);
Expand Down
7 changes: 6 additions & 1 deletion packages/stack/src/views/Header/HeaderContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
NavigationRouteContext,
ParamListBase,
Route,
useLinkTools,
} from '@react-navigation/native';
import * as React from 'react';
import { Animated, StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
Expand Down Expand Up @@ -47,6 +48,7 @@ export function HeaderContainer({
}: Props) {
const focusedRoute = getFocusedRoute();
const parentHeaderBack = React.useContext(HeaderBackContext);
const { buildHref } = useLinkTools();

return (
<Animated.View pointerEvents="box-none" style={style}>
Expand Down Expand Up @@ -78,7 +80,10 @@ export function HeaderContainer({
const { options, route } = previousScene.descriptor;

headerBack = previousScene
? { title: getHeaderTitle(options, route.name) }
? {
title: getHeaderTitle(options, route.name),
href: buildHref(route.name, route.params),
}
: parentHeaderBack;
}

Expand Down
6 changes: 5 additions & 1 deletion packages/stack/src/views/Header/HeaderSegment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Props = Omit<StackHeaderOptions, 'headerStatusBarHeight'> & {
title: string;
modal: boolean;
onGoBack?: () => void;
href?: string;
progress: SceneProgress;
styleInterpolator: StackHeaderStyleInterpolator;
};
Expand Down Expand Up @@ -105,9 +106,12 @@ export function HeaderSegment(props: Props) {
layout,
modal,
onGoBack,
href,
headerTitle: title,
headerLeft: left = onGoBack
? (props: HeaderBackButtonProps) => <HeaderBackButton {...props} />
? (props: HeaderBackButtonProps) => (
<HeaderBackButton {...props} href={href} />
)
: undefined,
headerRight: right,
headerBackImage,
Expand Down

0 comments on commit 5561b2b

Please sign in to comment.