Skip to content
This repository has been archived by the owner on Feb 8, 2020. It is now read-only.

Commit

Permalink
fix: use pure component for stack items
Browse files Browse the repository at this point in the history
  • Loading branch information
satya164 committed Aug 18, 2019
1 parent 78c4f25 commit aeec520
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 67 deletions.
5 changes: 4 additions & 1 deletion packages/stack/src/TransitionConfigs/TransitionSpecs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export const WipeFromBottomAndroidSpec: TransitionSpec = {
duration: 425,
// This is super rough approximation of the path used for the curve by android
// See http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/res/res/interpolator/fast_out_extra_slow_in.xml
easing: t => Easing.bezier(0.90, 0.06, 0.57, 0)((Easing.bezier(0.06, 0.94, 0.22, 1.02)(t))),
easing: t =>
Easing.bezier(0.9, 0.06, 0.57, 0)(
Easing.bezier(0.06, 0.94, 0.22, 1.02)(t)
),
},
};
12 changes: 7 additions & 5 deletions packages/stack/src/views/Header/HeaderContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import {
} from '../../types';
import Header from './Header';

type Props = {
export type Props = {
mode: 'float' | 'screen';
layout: Layout;
scenes: HeaderScene<Route>[];
scenes: Array<HeaderScene<Route> | undefined>;
navigation: NavigationProp;
getPreviousRoute: (props: { route: Route }) => Route | undefined;
onLayout?: (e: LayoutChangeEvent) => void;
Expand All @@ -42,7 +42,7 @@ export default function HeaderContainer({
return (
<View pointerEvents="box-none" style={style}>
{scenes.map((scene, i, self) => {
if (mode === 'screen' && i !== self.length - 1) {
if ((mode === 'screen' && i !== self.length - 1) || !scene) {
return null;
}

Expand All @@ -56,8 +56,10 @@ export default function HeaderContainer({
// The previous scene will be shortly before the current scene in the array
// So loop back from current index to avoid looping over the full array
for (let j = i - 1; j >= 0; j--) {
if (self[j].route.key === previousRoute.key) {
previous = self[j];
const s = self[j];

if (s && s.route.key === previousRoute.key) {
previous = s;
break;
}
}
Expand Down
100 changes: 40 additions & 60 deletions packages/stack/src/views/Stack/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
} from 'react-native';
import Animated from 'react-native-reanimated';
import { getDefaultHeaderHeight } from '../Header/HeaderSegment';
import HeaderContainer from '../Header/HeaderContainer';
import Card from './Card';
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
import StackItem from './StackItem';
import {
Route,
Layout,
Expand Down Expand Up @@ -38,6 +38,7 @@ type Props = {
onCloseRoute: (props: { route: Route }) => void;
getPreviousRoute: (props: { route: Route }) => Route | undefined;
getGesturesEnabled: (props: { route: Route }) => boolean;
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
renderScene: (props: { route: Route }) => React.ReactNode;
transparentCard?: boolean;
headerMode: HeaderMode;
Expand Down Expand Up @@ -182,6 +183,7 @@ export default class Stack extends React.Component<Props, State> {
onGoBack,
getPreviousRoute,
getGesturesEnabled,
renderHeader,
renderScene,
transparentCard,
headerMode,
Expand Down Expand Up @@ -212,79 +214,57 @@ export default class Stack extends React.Component<Props, State> {
const scene = scenes[index];

return (
<Card
<StackItem
key={route.key}
index={index}
active={index === self.length - 1}
transparent={transparentCard}
direction={direction}
focused={focused}
closing={closingRoutes.includes(route.key)}
layout={layout}
current={current}
next={scene.progress.next}
closing={closingRoutes.includes(route.key)}
onOpen={() => onOpenRoute({ route })}
onClose={() => onCloseRoute({ route })}
overlayEnabled={cardOverlayEnabled}
shadowEnabled={cardShadowEnabled}
scene={scene}
previousScene={scenes[index - 1]}
navigation={navigation}
direction={direction}
transparentCard={transparentCard}
cardOverlayEnabled={cardOverlayEnabled}
cardShadowEnabled={cardShadowEnabled}
gesturesEnabled={index !== 0 && getGesturesEnabled({ route })}
onTransitionStart={({ closing }) => {
onTransitionStart &&
onTransitionStart(
{ index: closing ? index - 1 : index },
{ index }
);

closing && onGoBack({ route });
}}
onGestureBegin={onGestureBegin}
onGestureCanceled={onGestureCanceled}
onGestureEnd={onGestureEnd}
gestureResponseDistance={
descriptor.options.gestureResponseDistance
}
transitionSpec={transitionSpec}
styleInterpolator={cardStyleInterpolator}
accessibilityElementsHidden={!focused}
importantForAccessibility={
focused ? 'auto' : 'no-hide-descendants'
}
pointerEvents="box-none"
style={[
StyleSheet.absoluteFill,
headerMode === 'float' &&
descriptor &&
descriptor.options.header !== null
? { marginTop: floaingHeaderHeight }
: null,
]}
>
{headerMode === 'screen' ? (
<HeaderContainer
mode="screen"
layout={layout}
scenes={[scenes[index - 1], scenes[index]]}
navigation={navigation}
getPreviousRoute={getPreviousRoute}
styleInterpolator={headerStyleInterpolator}
style={styles.header}
/>
) : null}
{renderScene({ route })}
</Card>
headerStyleInterpolator={headerStyleInterpolator}
cardStyleInterpolator={cardStyleInterpolator}
floaingHeaderHeight={floaingHeaderHeight}
hasCustomHeader={descriptor.options.header === null}
getPreviousRoute={getPreviousRoute}
headerMode={headerMode}
renderHeader={renderHeader}
renderScene={renderScene}
onOpenRoute={onOpenRoute}
onCloseRoute={onCloseRoute}
onTransitionStart={onTransitionStart}
onGoBack={onGoBack}
/>
);
})}
</View>
{headerMode === 'float' ? (
<HeaderContainer
mode="float"
layout={layout}
scenes={scenes}
navigation={navigation}
getPreviousRoute={getPreviousRoute}
onLayout={this.handleFloatingHeaderLayout}
styleInterpolator={headerStyleInterpolator}
style={[styles.header, styles.floating]}
/>
) : null}
{headerMode === 'float'
? renderHeader({
mode: 'float',
layout,
scenes,
navigation,
getPreviousRoute,
onLayout: this.handleFloatingHeaderLayout,
styleInterpolator: headerStyleInterpolator,
style: [styles.header, styles.floating],
})
: null}
</React.Fragment>
);
}
Expand Down
160 changes: 160 additions & 0 deletions packages/stack/src/views/Stack/StackItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import * as React from 'react';
import { StyleSheet, Platform } from 'react-native';
import Animated from 'react-native-reanimated';
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
import Card from './Card';
import {
Route,
HeaderScene,
GestureDirection,
Layout,
TransitionSpec,
CardStyleInterpolator,
HeaderMode,
NavigationProp,
HeaderStyleInterpolator,
} from '../../types';

type Props = {
index: number;
active: boolean;
focused: boolean;
closing: boolean;
layout: Layout;
current: Animated.Value<number>;
previousScene?: HeaderScene<Route>;
scene: HeaderScene<Route>;
navigation: NavigationProp;
transparentCard?: boolean;
cardOverlayEnabled?: boolean;
cardShadowEnabled?: boolean;
gesturesEnabled?: boolean;
direction: GestureDirection;
getPreviousRoute: (props: { route: Route }) => Route | undefined;
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
renderScene: (props: { route: Route }) => React.ReactNode;
onOpenRoute: (props: { route: Route }) => void;
onCloseRoute: (props: { route: Route }) => void;
onGoBack: (props: { route: Route }) => void;
onTransitionStart?: (
curr: { index: number },
prev: { index: number }
) => void;
onGestureBegin?: () => void;
onGestureCanceled?: () => void;
onGestureEnd?: () => void;
gestureResponseDistance?: {
vertical?: number;
horizontal?: number;
};
transitionSpec: {
open: TransitionSpec;
close: TransitionSpec;
};
headerStyleInterpolator: HeaderStyleInterpolator;
cardStyleInterpolator: CardStyleInterpolator;
headerMode: HeaderMode;
floaingHeaderHeight: number;
hasCustomHeader: boolean;
};

export default class StackItem extends React.PureComponent<Props> {
private handleOpen = () =>
this.props.onOpenRoute({ route: this.props.scene.route });

private handleClose = () =>
this.props.onOpenRoute({ route: this.props.scene.route });

private handleTransitionStart = ({ closing }: { closing: boolean }) => {
const { index, scene, onTransitionStart, onGoBack } = this.props;

onTransitionStart &&
onTransitionStart({ index: closing ? index - 1 : index }, { index });

closing && onGoBack({ route: scene.route });
};

render() {
const {
layout,
active,
focused,
closing,
current,
navigation,
scene,
previousScene,
direction,
transparentCard,
cardOverlayEnabled,
cardShadowEnabled,
gesturesEnabled,
onGestureBegin,
onGestureCanceled,
onGestureEnd,
gestureResponseDistance,
transitionSpec,
headerStyleInterpolator,
cardStyleInterpolator,
floaingHeaderHeight,
hasCustomHeader,
getPreviousRoute,
headerMode,
renderHeader,
renderScene,
} = this.props;

return (
<Card
active={active}
transparent={transparentCard}
direction={direction}
layout={layout}
current={current}
next={scene.progress.next}
closing={closing}
onOpen={this.handleOpen}
onClose={this.handleClose}
overlayEnabled={cardOverlayEnabled}
shadowEnabled={cardShadowEnabled}
gesturesEnabled={gesturesEnabled}
onTransitionStart={this.handleTransitionStart}
onGestureBegin={onGestureBegin}
onGestureCanceled={onGestureCanceled}
onGestureEnd={onGestureEnd}
gestureResponseDistance={gestureResponseDistance}
transitionSpec={transitionSpec}
styleInterpolator={cardStyleInterpolator}
accessibilityElementsHidden={!focused}
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
pointerEvents="box-none"
style={[
StyleSheet.absoluteFill,
headerMode === 'float' && !hasCustomHeader
? { marginTop: floaingHeaderHeight }
: null,
]}
>
{headerMode === 'screen'
? renderHeader({
mode: 'screen',
layout,
scenes: [previousScene, scene],
navigation,
getPreviousRoute,
styleInterpolator: headerStyleInterpolator,
style: styles.header,
})
: null}
{renderScene({ route: scene.route })}
</Card>
);
}
}

const styles = StyleSheet.create({
header: {
// This is needed to show elevation shadow
zIndex: Platform.OS === 'android' ? 1 : 0,
},
});
10 changes: 9 additions & 1 deletion packages/stack/src/views/Stack/StackView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as React from 'react';
import { Platform } from 'react-native';
import { SceneView, StackActions } from '@react-navigation/core';
import Stack from './Stack';
import HeaderContainer, {
Props as HeaderContainerProps,
} from '../Header/HeaderContainer';
import {
DefaultTransition,
ModalSlideFromBottomIOS,
Expand All @@ -11,7 +15,6 @@ import {
NavigationConfig,
Route,
} from '../../types';
import { Platform } from 'react-native';

type Descriptors = { [key: string]: SceneDescriptor };

Expand Down Expand Up @@ -223,6 +226,10 @@ class StackView extends React.Component<Props, State> {
);
};

private renderHeader = (props: HeaderContainerProps) => {
return <HeaderContainer {...props} />;
};

private handleTransitionComplete = () => {
// TODO: remove when the new event system lands
this.props.navigation.dispatch(StackActions.completeTransition());
Expand Down Expand Up @@ -294,6 +301,7 @@ class StackView extends React.Component<Props, State> {
onGestureBegin={onGestureBegin}
onGestureCanceled={onGestureCanceled}
onGestureEnd={onGestureEnd}
renderHeader={this.renderHeader}
renderScene={this.renderScene}
headerMode={headerMode}
navigation={navigation}
Expand Down

0 comments on commit aeec520

Please sign in to comment.