Skip to content

Commit

Permalink
feat: added support for nested stack navigators
Browse files Browse the repository at this point in the history
  • Loading branch information
IjzerenHein committed Aug 29, 2019
1 parent a41a3af commit 69ddd7a
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 22 deletions.
8 changes: 8 additions & 0 deletions src/SharedElementRendererContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
import SharedElementRendererData from './SharedElementRendererData';

const SharedElementRendererContext = React.createContext<SharedElementRendererData | null>(
null
);

export default SharedElementRendererContext;
22 changes: 19 additions & 3 deletions src/SharedElementRendererData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,19 @@ import {

export type SharedElementRendererUpdateHandler = () => any;

export default class SharedElementRendererData {
export interface ISharedElementRendererData {
startTransition(animValue: SharedElementAnimatedValue): void;
endTransition(): void;
willActivateScene(
sceneData: SharedElementSceneData,
sharedElements: SharedElementsConfig,
animValue?: SharedElementAnimatedValue
): void;
didActivateScene(sceneData: SharedElementSceneData): void;
}

export default class SharedElementRendererData
implements ISharedElementRendererData {
private sceneData: SharedElementSceneData | null = null;
private prevSceneData: SharedElementSceneData | null = null;
private updateSubscribers = new Set<SharedElementRendererUpdateHandler>();
Expand All @@ -31,7 +43,11 @@ export default class SharedElementRendererData {
): void {
/*console.log(
'SharedElementRendererData.willActivateScene: ',
this.prevSceneData
sceneData.name,
', previous: ',
this.prevSceneData ? this.prevSceneData.name : '',
', sharedElements: ',
sharedElements
);*/
this.sceneData = sceneData;
if (!this.prevSceneData) return;
Expand All @@ -47,7 +63,7 @@ export default class SharedElementRendererData {
}

didActivateScene(sceneData: SharedElementSceneData): void {
//console.log('SharedElementRendererData.didActivateScene');
// console.log('SharedElementRendererData.didActivateScene: ', sceneData.name);
if (this.sceneSubscription) {
this.sceneSubscription.remove();
this.sceneSubscription = null;
Expand Down
61 changes: 61 additions & 0 deletions src/SharedElementRendererProxy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import SharedElementRendererData, {
ISharedElementRendererData,
} from './SharedElementRendererData';
import { SharedElementAnimatedValue, SharedElementsConfig } from './types';
import SharedElementSceneData from './SharedElementSceneData';

export class SharedElementRendererProxy implements ISharedElementRendererData {
private data: SharedElementRendererData | null = null;

startTransition(animValue: SharedElementAnimatedValue) {
if (!this.data) {
console.warn(
'SharedElementRendererProxy.startTransition called before Proxy was initialized'
);
return;
}
return this.data.startTransition(animValue);
}

endTransition() {
if (!this.data) {
console.warn(
'SharedElementRendererProxy.endTransition called before Proxy was initialized'
);
return;
}
return this.data.endTransition();
}

willActivateScene(
sceneData: SharedElementSceneData,
sharedElements: SharedElementsConfig,
animValue?: SharedElementAnimatedValue
) {
if (!this.data) {
console.warn(
'SharedElementRendererProxy.willActivateScene called before Proxy was initialized'
);
return;
}
return this.data.willActivateScene(sceneData, sharedElements, animValue);
}

didActivateScene(sceneData: SharedElementSceneData) {
if (!this.data) {
console.warn(
'SharedElementRendererProxy.didActivateScene called before Proxy was initialized'
);
return;
}
return this.data.didActivateScene(sceneData);
}

get source(): SharedElementRendererData | null {
return this.data;
}

set source(data: SharedElementRendererData | null) {
this.data = data;
}
}
1 change: 1 addition & 0 deletions src/SharedElementRendererView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default class SharedElementRendererView extends React.PureComponent<

render() {
const transitions = this.props.rendererData.getTransitions();
// console.log('SharedElementRendererView.render: ', transitions);
return (
<View style={StyleSheet.absoluteFill} pointerEvents="none">
{transitions.map((transitionProps, index) => (
Expand Down
5 changes: 5 additions & 0 deletions src/SharedElementSceneData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export default class SharedElementSceneData {
private nodes: {
[key: string]: SharedElementNode;
} = {};
public readonly name: string;

constructor(name: string) {
this.name = name;
}

getAncestor(): SharedElementNode | undefined {
return this.ancestorNode;
Expand Down
11 changes: 8 additions & 3 deletions src/createSharedElementScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { nodeFromRef } from 'react-native-shared-element';
import SharedElementSceneData from './SharedElementSceneData';
import SharedElementSceneContext from './SharedElementSceneContext';
import { SharedElementsConfig, SharedElementEventSubscription } from './types';
import SharedElementRendererData from './SharedElementRendererData';
import { ISharedElementRendererData } from './SharedElementRendererData';
import { normalizeSharedElementsConfig } from './utils';

const styles = StyleSheet.create({
Expand All @@ -20,13 +20,18 @@ type PropsType = {

function createSharedElementScene(
Component: React.ComponentType<any>,
rendererData: SharedElementRendererData
rendererData: ISharedElementRendererData
): React.ComponentType<any> {
class SharedElementSceneView extends React.PureComponent<PropsType> {
private subscriptions: {
[key: string]: SharedElementEventSubscription;
} = {};
private sceneData: SharedElementSceneData = new SharedElementSceneData();
private sceneData: SharedElementSceneData = new SharedElementSceneData(
Component.displayName ||
Component.name ||
(Component.constructor ? Component.constructor.name : undefined) ||
''
);

componentDidMount() {
const { navigation } = this.props;
Expand Down
69 changes: 53 additions & 16 deletions src/createSharedElementStackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,39 @@ import * as React from 'react';
import { Animated } from 'react-native';
import hoistNonReactStatics from 'hoist-non-react-statics';
import SharedElementRendererView from './SharedElementRendererView';
import SharedElementRendererData from './SharedElementRendererData';
import SharedElementRendererData, {
ISharedElementRendererData,
} from './SharedElementRendererData';
import createSharedElementScene from './createSharedElementScene';
import SharedElementRendererContext from './SharedElementRendererContext';
import { SharedElementRendererProxy } from './SharedElementRendererProxy';

function createSharedElementEnabledNavigator(
createNavigator: any,
routeConfigs: any,
navigatorConfig: any,
rendererData: SharedElementRendererData
rendererData: ISharedElementRendererData
) {
const wrappedRouteConfigs = {
...routeConfigs,
};
for (const key in routeConfigs) {
wrappedRouteConfigs[key] = createSharedElementScene(
routeConfigs[key],
rendererData
);
const component = wrappedRouteConfigs[key];
if (component.name === 'Navigator') {
// console.log('Navigator detected, ignoring: ', component);
wrappedRouteConfigs[key] = component;
} else {
wrappedRouteConfigs[key] = createSharedElementScene(
component,
rendererData
);
}
}
return createNavigator(wrappedRouteConfigs, {
...navigatorConfig,
onTransitionStart: (transitionProps: any, prevTransitionProps: any) => {
//console.log('onTransitionStart: ', transitionProps, prevTransitionProps);
if (transitionProps.index === prevTransitionProps.index) return;
// console.log('onTransitionStart: ', transitionProps, prevTransitionProps);
rendererData.startTransition(
Animated.subtract(transitionProps.position, transitionProps.index - 1)
);
Expand All @@ -32,7 +43,7 @@ function createSharedElementEnabledNavigator(
}
},
onTransitionEnd: (transitionProps: any, prevTransitionProps: any) => {
//console.log('onTransitionEnd: ', transitionProps, prevTransitionProps);
// console.log('onTransitionEnd: ', transitionProps, prevTransitionProps);
rendererData.endTransition();
if (navigatorConfig.onTransitionEnd) {
navigatorConfig.onTransitionEnd(transitionProps, prevTransitionProps);
Expand All @@ -46,25 +57,51 @@ function createSharedElementStackNavigator(
RouteConfigs: any,
NavigatorConfig: any
): React.ComponentType<any> {
const rendererData = new SharedElementRendererData();
const Navigator = createSharedElementEnabledNavigator(
// Create a proxy which is later updated to link
// to the renderer
const rendererDataProxy = new SharedElementRendererProxy();

//const rendererData = new SharedElementRendererData();
const SharedElementNavigator = createSharedElementEnabledNavigator(
createNavigator,
RouteConfigs,
NavigatorConfig,
rendererData
rendererDataProxy
);

class SharedElementRenderer extends React.Component {
static displayName = 'SharedElementRenderer';
private rendererData?: SharedElementRendererData;
render() {
return (
<React.Fragment>
<Navigator {...this.props} />
<SharedElementRendererView rendererData={rendererData} />
</React.Fragment>
<SharedElementRendererContext.Consumer>
{rendererData => {
// In case a renderer is already present higher up in the chain
// then don't bother creating a renderer here, but use that one instead
if (rendererData) {
rendererDataProxy.source = rendererData;
return <SharedElementNavigator {...this.props} />;

// Create/use our own renderer here
} else {
this.rendererData =
this.rendererData || new SharedElementRendererData();
rendererDataProxy.source = this.rendererData;
return (
<SharedElementRendererContext.Provider
value={this.rendererData}
>
<SharedElementNavigator {...this.props} />
<SharedElementRendererView rendererData={this.rendererData} />
</SharedElementRendererContext.Provider>
);
}
}}
</SharedElementRendererContext.Consumer>
);
}
}
hoistNonReactStatics(SharedElementRenderer, Navigator);
hoistNonReactStatics(SharedElementRenderer, SharedElementNavigator);
return SharedElementRenderer;
}

Expand Down

0 comments on commit 69ddd7a

Please sign in to comment.