Skip to content

Commit

Permalink
feat: added new API which supports pop as well
Browse files Browse the repository at this point in the history
BREAKING CHANGE: The old API where the shared elements could be passed as props has been dropped. Instead you should define a static variable called `sharedElements` on your Screen component. That variable can be either a function or a static array. See the README for more details
  • Loading branch information
IjzerenHein committed Aug 31, 2019
1 parent 1d84d83 commit 9c4136d
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 118 deletions.
92 changes: 62 additions & 30 deletions src/SharedElementRendererData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
SharedElementsConfig,
SharedElementAnimatedValue,
SharedElementTransitionProps,
NavigationProp,
} from './types';
import { normalizeSharedElementsConfig } from './utils';

export type SharedElementRendererUpdateHandler = () => any;

Expand All @@ -13,16 +15,33 @@ export interface ISharedElementRendererData {
endTransition(): void;
willActivateScene(
sceneData: SharedElementSceneData,
sharedElements: SharedElementsConfig,
animValue?: SharedElementAnimatedValue
navigation: NavigationProp
): void;
didActivateScene(sceneData: SharedElementSceneData): void;
didActivateScene(
sceneData: SharedElementSceneData,
navigation: NavigationProp
): void;
}

function getSharedElements(
sceneData: SharedElementSceneData,
navigation: NavigationProp,
otherNavigation: NavigationProp,
show: boolean
): SharedElementsConfig | null {
const { sharedElements } = sceneData.Component;
if (!sharedElements) return null;
// TODO push/pop distinction?
return normalizeSharedElementsConfig(
sharedElements(navigation, otherNavigation, show)
);
}

export default class SharedElementRendererData
implements ISharedElementRendererData {
private sceneData: SharedElementSceneData | null = null;
private prevSceneData: SharedElementSceneData | null = null;
private prevNavigation: NavigationProp | null = null;
private updateSubscribers = new Set<SharedElementRendererUpdateHandler>();
private sceneSubscription: SharedElementEventSubscription | null = null;
private sharedElements: SharedElementsConfig = [];
Expand All @@ -38,22 +57,27 @@ export default class SharedElementRendererData

willActivateScene(
sceneData: SharedElementSceneData,
sharedElements: SharedElementsConfig,
animValue?: SharedElementAnimatedValue
navigation: NavigationProp
): void {
/*console.log(
'SharedElementRendererData.willActivateScene: ',
sceneData.name,
', previous: ',
this.prevSceneData ? this.prevSceneData.name : '',
', sharedElements: ',
sharedElements
this.prevSceneData ? this.prevSceneData.name : ''
);*/
this.sceneData = sceneData;
if (!this.prevSceneData) return;
this.sharedElements = sharedElements;
if (animValue) this.animValue = animValue;
if (sharedElements.length) {
if (!this.prevSceneData || !this.prevNavigation) return;
const sharedElements =
getSharedElements(sceneData, navigation, this.prevNavigation, true) ||
getSharedElements(
this.prevSceneData,
this.prevNavigation,
navigation,
false
);
if (sharedElements && sharedElements.length) {
// console.log('sharedElements: ', sharedElements, sceneData);
this.sceneData = sceneData;
this.sharedElements = sharedElements;
this.sceneSubscription = this.sceneData.addUpdateListener(() => {
// TODO optimize
this.emitUpdateEvent();
Expand All @@ -62,13 +86,17 @@ export default class SharedElementRendererData
}
}

didActivateScene(sceneData: SharedElementSceneData): void {
// console.log('SharedElementRendererData.didActivateScene: ', sceneData.name);
didActivateScene(
sceneData: SharedElementSceneData,
navigation: NavigationProp
): void {
//console.log('SharedElementRendererData.didActivateScene: ', sceneData.name);
if (this.sceneSubscription) {
this.sceneSubscription.remove();
this.sceneSubscription = null;
}
this.prevSceneData = sceneData;
this.prevNavigation = navigation;
if (this.sceneData) {
this.sceneData = null;
if (this.sharedElements.length) {
Expand All @@ -93,20 +121,24 @@ export default class SharedElementRendererData

getTransitions(): SharedElementTransitionProps[] {
const { prevSceneData, sceneData } = this;
return this.sharedElements.map(({ id, sourceId, animation, debug }) => ({
position: this.animValue,
start: {
ancestor:
(prevSceneData ? prevSceneData.getAncestor() : undefined) || null,
node:
(prevSceneData ? prevSceneData.getNode(sourceId) : undefined) || null,
},
end: {
ancestor: (sceneData ? sceneData.getAncestor() : undefined) || null,
node: (sceneData ? sceneData.getNode(id) : undefined) || null,
},
...animation,
debug,
}));
// console.log('getTransitions: ', sceneData, prevSceneData);
return this.sharedElements.map(({ id, sourceId, animation, debug }) => {
return {
position: this.animValue,
start: {
ancestor:
(prevSceneData ? prevSceneData.getAncestor() : undefined) || null,
node:
(prevSceneData ? prevSceneData.getNode(sourceId) : undefined) ||
null,
},
end: {
ancestor: (sceneData ? sceneData.getAncestor() : undefined) || null,
node: (sceneData ? sceneData.getNode(id) : undefined) || null,
},
...animation,
debug,
};
});
}
}
14 changes: 8 additions & 6 deletions src/SharedElementRendererProxy.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import SharedElementRendererData, {
ISharedElementRendererData,
} from './SharedElementRendererData';
import { SharedElementAnimatedValue, SharedElementsConfig } from './types';
import { SharedElementAnimatedValue, NavigationProp } from './types';
import SharedElementSceneData from './SharedElementSceneData';

export class SharedElementRendererProxy implements ISharedElementRendererData {
Expand Down Expand Up @@ -29,26 +29,28 @@ export class SharedElementRendererProxy implements ISharedElementRendererData {

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

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

get source(): SharedElementRendererData | null {
Expand Down
16 changes: 13 additions & 3 deletions src/SharedElementSceneData.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { SharedElementNode, SharedElementEventSubscription } from './types';
import {
SharedElementNode,
SharedElementEventSubscription,
SharedElementSceneComponent,
} from './types';

export type SharedElementSceneUpdateHandlerEventType =
| 'ancestor'
Expand All @@ -17,10 +21,16 @@ export default class SharedElementSceneData {
private nodes: {
[key: string]: SharedElementNode;
} = {};
public readonly Component: SharedElementSceneComponent;
public readonly name: string;

constructor(name: string) {
this.name = name;
constructor(Component: SharedElementSceneComponent) {
this.Component = Component;
this.name =
Component.displayName ||
Component.name ||
(Component.constructor ? Component.constructor.name : undefined) ||
'';
}

getAncestor(): SharedElementNode | undefined {
Expand Down
42 changes: 12 additions & 30 deletions src/createSharedElementScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import hoistNonReactStatics from 'hoist-non-react-statics';
import { nodeFromRef } from 'react-native-shared-element';
import SharedElementSceneData from './SharedElementSceneData';
import SharedElementSceneContext from './SharedElementSceneContext';
import { SharedElementsConfig, SharedElementEventSubscription } from './types';
import {
SharedElementEventSubscription,
NavigationProp,
SharedElementSceneComponent,
} from './types';
import { ISharedElementRendererData } from './SharedElementRendererData';
import { normalizeSharedElementsConfig } from './utils';

const styles = StyleSheet.create({
container: {
Expand All @@ -15,7 +18,7 @@ const styles = StyleSheet.create({
});

type PropsType = {
navigation: any;
navigation: NavigationProp;
};

function getActiveRouteState(route: any): any {
Expand All @@ -31,19 +34,15 @@ function getActiveRouteState(route: any): any {
}

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

componentDidMount() {
Expand Down Expand Up @@ -79,34 +78,17 @@ function createSharedElementScene(
this.sceneData.setAncestor(nodeFromRef(ref));
};

private getSharedElements(): SharedElementsConfig | null {
const { navigation } = this.props;
let sharedElements =
// @ts-ignore
navigation.getParam('sharedElements') || Component.sharedElements;
if (!sharedElements) return null;
if (typeof sharedElements === 'function') {
sharedElements = sharedElements(navigation);
}
return sharedElements
? normalizeSharedElementsConfig(sharedElements)
: null;
}

private onWillFocus = () => {
const sharedElements = this.getSharedElements();
if (sharedElements && this.initial) {
this.initial = false;
rendererData.willActivateScene(this.sceneData, sharedElements);
}
const { navigation } = this.props;
rendererData.willActivateScene(this.sceneData, navigation);
};

private onDidFocus = () => {
const { navigation } = this.props;
const activeRoute = getActiveRouteState(navigation.state);
if (navigation.state.routeName === activeRoute.routeName) {
// console.log('onDidFocus: ', this.sceneData.name, activeRoute, this.props.navigation.state.routeName);
rendererData.didActivateScene(this.sceneData);
// console.log('onDidFocus: ', this.sceneData.name, navigation);
rendererData.didActivateScene(this.sceneData, navigation);
}
};
}
Expand Down
11 changes: 7 additions & 4 deletions src/createSharedElementStackNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react';
import { Animated } from 'react-native';
import hoistNonReactStatics from 'hoist-non-react-statics';
import SharedElementRendererView from './SharedElementRendererView';
import SharedElementRendererData, {
Expand Down Expand Up @@ -38,10 +37,14 @@ function createSharedElementEnabledNavigator(
return createNavigator(wrappedRouteConfigs, {
...navigatorConfig,
onTransitionStart: (transitionProps: any, prevTransitionProps: any) => {
if (transitionProps.index === prevTransitionProps.index) return;
// console.log('onTransitionStart: ', transitionProps, prevTransitionProps);
const { index, position } = transitionProps;
const prevIndex = prevTransitionProps.index;
if (index === prevIndex) return;
rendererData.startTransition(
Animated.subtract(transitionProps.position, transitionProps.index - 1)
position.interpolate({
inputRange: [index - 1, index],
outputRange: index > prevIndex ? [0, 1] : [2, 1],
})
);
if (navigatorConfig.onTransitionStart) {
navigatorConfig.onTransitionStart(transitionProps, prevTransitionProps);
Expand Down
Loading

0 comments on commit 9c4136d

Please sign in to comment.