react-native-magic-move
Version:
Create magical move transitions between scenes in react-native
179 lines (156 loc) • 4.06 kB
JavaScript
import React, { Component } from "react";
import { View, StyleSheet } from "react-native";
import { PropTypes } from "prop-types";
import {
withMagicMoveContext,
MagicMoveContextProvider,
MagicMoveContextPropType
} from "./Context";
import { performanceNow, measureLayout } from "./clone/measure";
import MagicMoveClone from "./clone/Clone";
const styles = StyleSheet.create({
container: {
flex: 1
}
});
let autoId = 0;
class MagicMoveScene extends Component {
static propTypes = {
children: PropTypes.any,
id: PropTypes.string,
disabled: PropTypes.bool,
active: PropTypes.bool,
debug: PropTypes.bool,
useNativeClone: PropTypes.bool,
mmContext: MagicMoveContextPropType,
onWillAppear: PropTypes.func,
onWillDisappear: PropTypes.func
// onDidAppear: PropTypes.func,
// onDidDisappear: PropTypes.func,
};
static defaultProps = {
disabled: false,
useNativeClone: MagicMoveClone.isNativeAvailable
};
_ref = undefined;
_uniqueId = "__autoSceneId" + autoId++;
_active = undefined;
_lastMeasureResult = undefined;
_lastMeasureTime = 0;
get isScene() {
return true;
}
get ref() {
return this._ref;
}
get id() {
return this._uniqueId;
}
get isDebug() {
const { debug, mmContext } = this.props;
return debug !== undefined ? debug : mmContext.isDebug;
}
get debugName() {
return `scene "${this.props.id || this.id}"`;
}
componentDidMount() {
if (this.isDebug) {
// eslint-disable-next-line
console.debug(
`[MagicMove] Mounted ${this.debugName} (active = ${this.isActive})`
);
}
}
componentWillUnmount() {
if (this.isDebug) {
// eslint-disable-next-line
console.debug(
`[MagicMove] Unmounted ${this.debugName} (active = ${this.isActive})`
);
}
}
render() {
const {
children,
id, // eslint-disable-line
disabled, // eslint-disable-line
active, // eslint-disable-line
debug, // eslint-disable-line
mmContext, // eslint-disable-line
useNativeClone, // eslint-disable-line
...otherProps
} = this.props;
return (
<View
ref={this._setRef}
style={styles.container}
{...otherProps}
collapsable={false}
>
<MagicMoveContextProvider value={this}>
{children}
</MagicMoveContextProvider>
</View>
);
}
componentDidUpdate() {
const { isActive, isDebug } = this;
if (isActive !== undefined) {
if (this._active !== isActive) {
this._active = isActive;
if (isDebug) {
// eslint-disable-next-line
console.debug(
`[MagicMove] ${isActive ? "Activated" : "De-activated"} ${
this.debugName
}`
);
}
this.props.mmContext.administration.activateScene(this, isActive);
if (!isActive) {
if (this.props.onWillDisappear) {
this.props.onWillDisappear();
}
} else {
if (this.props.onWillAppear) {
this.props.onWillAppear();
}
}
}
}
}
_setRef = ref => {
this._ref = ref;
};
get isActive() {
return this.props.active;
}
measure(forceRemeasure) {
const now = performanceNow();
if (
this._lastMeasureResult &&
(now - this._lastMeasureTime < 16 ||
(now - this._lastMeasureTime <= 100 && !forceRemeasure))
) {
return this._lastMeasureResult;
}
this._lastMeasureTime = now;
this._lastMeasureResult = measureLayout(this);
return this._lastMeasureResult;
}
}
let HookedComponent;
function addHook(Component) {
HookedComponent = Component;
}
const MagicMoveSceneWrapper = props => {
const scene = <MagicMoveScene {...props} />;
if (HookedComponent) {
return <HookedComponent>{scene}</HookedComponent>;
} else {
return scene;
}
};
const Scene = withMagicMoveContext(MagicMoveSceneWrapper);
Scene.addHook = addHook;
export default Scene;