UNPKG

3.93 kBJavaScriptView Raw
1import React from 'react';
2import { View, findNodeHandle } from 'react-native';
3import ReanimatedModule from './ReanimatedModule';
4
5const TransitioningContext = React.createContext();
6
7function configFromProps(type, props) {
8 const config = { type };
9 if ('durationMs' in props) {
10 config.durationMs = props.durationMs;
11 }
12 if ('interpolation' in props) {
13 config.interpolation = props.interpolation;
14 }
15 if ('type' in props) {
16 config.animation = props.type;
17 }
18 if ('delayMs' in props) {
19 config.delayMs = props.delayMs;
20 }
21 if ('propagation' in props) {
22 config.propagation = props.propagation;
23 }
24 return config;
25}
26
27/**
28 * The below wrapper is used to support legacy context API with Context.Consumer
29 * render prop. We need it as we want to access `context` from within
30 * `componentDidMount` callback. If we decided to drop support for older
31 * react native we could rewrite it using hooks or `static contextType` API.
32 */
33function wrapTransitioningContext(Comp) {
34 return props => {
35 return (
36 <TransitioningContext.Consumer>
37 {context => <Comp context={context} {...props} />}
38 </TransitioningContext.Consumer>
39 );
40 };
41}
42
43class In extends React.Component {
44 componentDidMount() {
45 this.props.context.push(configFromProps('in', this.props));
46 }
47
48 render() {
49 return this.props.children || null;
50 }
51}
52
53class Change extends React.Component {
54 componentDidMount() {
55 this.props.context.push(configFromProps('change', this.props));
56 }
57
58 render() {
59 return this.props.children || null;
60 }
61}
62
63class Out extends React.Component {
64 componentDidMount() {
65 this.props.context.push(configFromProps('out', this.props));
66 }
67
68 render() {
69 return this.props.children || null;
70 }
71}
72
73class Together extends React.Component {
74 transitions = [];
75 componentDidMount() {
76 const config = configFromProps('group', this.props);
77 config.transitions = this.transitions;
78 this.props.context.push(config);
79 }
80
81 render() {
82 return (
83 <TransitioningContext.Provider value={this.transitions}>
84 {this.props.children}
85 </TransitioningContext.Provider>
86 );
87 }
88}
89
90class Sequence extends React.Component {
91 transitions = [];
92 componentDidMount() {
93 const config = configFromProps('group', this.props);
94 config.sequence = true;
95 config.transitions = this.transitions;
96 this.props.context.push(config);
97 }
98
99 render() {
100 return (
101 <TransitioningContext.Provider value={this.transitions}>
102 {this.props.children}
103 </TransitioningContext.Provider>
104 );
105 }
106}
107
108function createTransitioningComponent(Component) {
109 class Wrapped extends React.Component {
110 propTypes = Component.propTypes;
111 transitions = [];
112 viewRef = React.createRef();
113
114 componentDidMount() {
115 if (this.props.animateMount) {
116 this.animateNextTransition();
117 }
118 }
119
120 setNativeProps(props) {
121 this.viewRef.current.setNativeProps(props);
122 }
123
124 animateNextTransition() {
125 const viewTag = findNodeHandle(this.viewRef.current);
126 ReanimatedModule.animateNextTransition(viewTag, {
127 transitions: this.transitions,
128 });
129 }
130
131 render() {
132 const { transition, ...rest } = this.props;
133 return (
134 <React.Fragment>
135 <TransitioningContext.Provider value={this.transitions}>
136 {transition}
137 </TransitioningContext.Provider>
138 <Component {...rest} ref={this.viewRef} collapsable={false} />
139 </React.Fragment>
140 );
141 }
142 }
143 return Wrapped;
144}
145
146const Transitioning = {
147 View: createTransitioningComponent(View),
148};
149
150const Transition = {
151 Sequence: wrapTransitioningContext(Sequence),
152 Together: wrapTransitioningContext(Together),
153 In: wrapTransitioningContext(In),
154 Out: wrapTransitioningContext(Out),
155 Change: wrapTransitioningContext(Change),
156};
157
158export { Transitioning, Transition, createTransitioningComponent };