Skip to content
This repository was archived by the owner on Nov 3, 2025. It is now read-only.

Commit c0ed67a

Browse files
committed
✨ (BurgerMenuLeft) add transition
1 parent 5ee1e47 commit c0ed67a

12 files changed

Lines changed: 600 additions & 9 deletions

File tree

example/src/canals/home/Home.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {DownloadScreen} from './screens/DownloadScreen';
99
import {SearchModule} from '../../module/SearchModule';
1010
import {HomeModule} from '../../module/HomeModule';
1111
import {observer} from 'mobx-react';
12+
import {BurgerMenuScreen} from './screens/BurgerMenuScreen';
1213

1314
export const Home = observer(() => (
1415
<View style={StyleSheet.absoluteFill}>
@@ -25,6 +26,15 @@ export const Home = observer(() => (
2526
visible={HomeModule.isFilteringDownloaded}
2627
Transitioner={transition.Fade}
2728
/>
29+
<Screen
30+
name="BurgerMenu"
31+
key="BurgerMenu"
32+
Component={BurgerMenuScreen}
33+
visible={HomeModule.isBurgerMenuOpen}
34+
Transitioner={transition.BurgerMenuLeft}
35+
isFullScreen
36+
onBack={HomeModule.closeBurgerMenu}
37+
/>
2838
</Canal>
2939
<TabBar
3040
items={[
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react';
2+
import {Text, TouchableOpacity, SafeAreaView} from 'react-native';
3+
4+
export const BurgerMenuScreen = () => (
5+
<SafeAreaView style={{backgroundColor: '#FFFFFF', flex: 1}}>
6+
<TouchableOpacity>
7+
<Text>Téléchargements</Text>
8+
</TouchableOpacity>
9+
</SafeAreaView>
10+
);

example/src/canals/home/screens/HomeScreen.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import React from 'react';
2-
import {View, FlatList, Text} from 'react-native';
2+
import {
3+
View,
4+
FlatList,
5+
Text,
6+
TouchableOpacity,
7+
SafeAreaView,
8+
} from 'react-native';
39
import {Header} from '../../../atoms/Header';
410
import {MovieCard} from '../../../atoms/MovieCard';
511
import {MoviesModule} from '../../../module/MoviesModule';
12+
import Icon from 'react-native-vector-icons/SimpleLineIcons';
13+
import {HomeModule} from '../../../module/HomeModule';
614

715
const data = [
816
{
@@ -28,12 +36,21 @@ export const HomeScreen = () => {
2836
<FlatList
2937
contentContainerStyle={{paddingBottom: 60}}
3038
ListHeaderComponent={
31-
<Header
32-
onPress={() => {}}
33-
title={headerMovie.title}
34-
subtitle="Nouveaux épisodes disponibles"
35-
imageUri={headerMovie.imageUri}
36-
/>
39+
<View style={{position: 'relative'}}>
40+
<Header
41+
onPress={() => {}}
42+
title={headerMovie.title}
43+
subtitle="Nouveaux épisodes disponibles"
44+
imageUri={headerMovie.imageUri}
45+
/>
46+
<SafeAreaView style={{position: 'absolute'}}>
47+
<TouchableOpacity
48+
onPress={HomeModule.openBurgerMenu}
49+
style={{margin: 10}}>
50+
<Icon name="menu" size={24} color="#FFFFFF" />
51+
</TouchableOpacity>
52+
</SafeAreaView>
53+
</View>
3754
}
3855
data={data}
3956
keyExtractor={item => item.title}

example/src/canals/search/Search.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const Search = observer(() => {
1010
<Screen
1111
isFullScreen
1212
name="Search"
13+
key="Search"
1314
visible={SearchModule.isSearching}
1415
Component={SearchScreen}
1516
Transitioner={transition.FadeDown}

example/src/module/HomeModule.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ class HomeModule_ {
1111
cancelFilter = () => {
1212
this.isFilteringDownloaded = false;
1313
};
14+
15+
@observable
16+
isBurgerMenuOpen = false;
17+
18+
openBurgerMenu = () => {
19+
this.isBurgerMenuOpen = true;
20+
};
21+
22+
closeBurgerMenu = () => {
23+
this.isBurgerMenuOpen = false;
24+
};
1425
}
1526

1627
export const HomeModule = new HomeModule_();

src/Navigation/FullScreenDelegate.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Subject, Observable } from 'rxjs';
2-
import { map, scan } from 'rxjs/operators';
2+
import { map, scan, tap } from 'rxjs/operators';
33
import { ReactElement } from 'react';
44
import { ScreenProps, Screen } from '../Screen';
55

@@ -34,6 +34,7 @@ export class FullScreenDelegate {
3434
fullScreenStack.concat(fullScreenStackMap[canalId]),
3535
[]
3636
)
37-
)
37+
),
38+
tap(o => console.warn(o))
3839
);
3940
}

src/Screen.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Screen } from 'react-nonav';
1717
| :-------------: | :------: | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1818
| `isFullScreen` | `false` | `false` | Specifies if the screen should be render in the `FullScreenPortal`. |
1919
| `onBack` | `false` | `undefined` | Function to call when a back event occurs. Use this prop and `visible` to control your Canal. |
20+
| `key` | \* | `undefined` | Unique identifier of the screen. Required when `isFullScreen` is true. |
2021
| `name` | `true` | `undefined` | Unique identifier of the screen. |
2122
| `Component` | `true` | `undefined` | Component to render. This is your page. |
2223
| `props` | `false` | `undefined` | Props to pass to the Component. Those props are transition-safe. This means while a transition occurs, those props are frozen. When the transition completes, the props flow is released. |

src/transitions/BurgerMenuLeft.tsx

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import React, { Children } from 'react';
2+
import { StyleSheet, Dimensions, TouchableWithoutFeedback, View } from 'react-native';
3+
import Animated, { Easing } from 'react-native-reanimated';
4+
5+
import { TransitionComponent } from './Transition';
6+
import { Navigation } from '../Navigation';
7+
8+
const { width: SCREEN_WIDTH } = Dimensions.get('window');
9+
10+
const {
11+
block,
12+
call,
13+
Clock,
14+
clockRunning,
15+
cond,
16+
multiply,
17+
set,
18+
startClock,
19+
stopClock,
20+
sub,
21+
timing,
22+
Value,
23+
} = Animated;
24+
25+
function runTiming(
26+
clock: Animated.Clock,
27+
value: Animated.Value<number>,
28+
dest: Animated.Value<number>,
29+
updateVisibility: () => any
30+
) {
31+
const state = {
32+
finished: new Value(0),
33+
frameTime: new Value(0),
34+
position: value,
35+
time: new Value(0),
36+
};
37+
38+
const config = {
39+
duration: 300,
40+
easing: Easing.inOut(Easing.ease),
41+
toValue: dest,
42+
};
43+
44+
return block([
45+
cond(
46+
clockRunning(clock),
47+
[
48+
// if the clock is already running we update the toValue, in case a new dest has been passed in
49+
set(config.toValue, dest),
50+
],
51+
[
52+
// If the clock isn't running we reset all the animation params and start the clock
53+
set(state.finished, 0),
54+
set(state.time, 0),
55+
set(state.position, value),
56+
set(state.frameTime, 0),
57+
set(config.toValue, dest),
58+
startClock(clock),
59+
]
60+
),
61+
// we run the step here that is going to update position
62+
timing(clock, state, config),
63+
// if the animation is over we stop the clock
64+
cond(state.finished, stopClock(clock)),
65+
cond(state.finished, [call([state.finished], updateVisibility), stopClock(clock)]),
66+
// we made the block return the updated position
67+
state.position,
68+
]);
69+
}
70+
71+
export class BurgerMenuLeft extends TransitionComponent {
72+
state = {
73+
hidden: !this.props.directionForward,
74+
};
75+
76+
clock = new Clock();
77+
progress: Animated.Value<number> = new Value(this.props.directionForward ? 0 : 1);
78+
animation: Animated.Value<number> = new Value(this.props.directionForward ? 0 : 1);
79+
// istanbul ignore next
80+
trans = runTiming(this.clock, this.progress, this.animation, () => {
81+
// react-native-reanimated is not correctly mocked
82+
// istanbul ignore next
83+
// @ts-ignore
84+
if (process.env.NODE_ENV !== 'test' && this.state.hidden !== !this.props.directionForward) {
85+
this.setState({ hidden: !this.props.directionForward });
86+
}
87+
});
88+
89+
componentDidUpdate() {
90+
this.animation.setValue(this.props.directionForward ? 0 : 1);
91+
if (this.state.hidden && this.props.directionForward) {
92+
this.setState({ hidden: false });
93+
}
94+
}
95+
96+
render() {
97+
return (
98+
<View style={[StyleSheet.absoluteFill]} pointerEvents="box-none">
99+
<Animated.View
100+
style={[StyleSheet.absoluteFill, { opacity: sub(new Value(1), this.trans) }]}
101+
pointerEvents={this.state.hidden ? 'none' : 'auto'}
102+
>
103+
<TouchableWithoutFeedback
104+
onPress={Navigation.instance.backHandlerDelegate.back}
105+
style={StyleSheet.absoluteFill}
106+
>
107+
<View style={[StyleSheet.absoluteFill, { backgroundColor: '#000000aa' }]}></View>
108+
</TouchableWithoutFeedback>
109+
</Animated.View>
110+
<View style={StyleSheet.absoluteFill} pointerEvents="box-none">
111+
<Animated.View
112+
pointerEvents="box-none"
113+
style={{
114+
flex: 1,
115+
width: (SCREEN_WIDTH / 3) * 2,
116+
transform: [{ translateX: multiply(this.trans, -(SCREEN_WIDTH / 3) * 2) }],
117+
}}
118+
>
119+
{!this.state.hidden && Children.only(this.props.children)}
120+
</Animated.View>
121+
</View>
122+
</View>
123+
);
124+
}
125+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { describeTransitioner } from '../utils/describeTransitioner';
2+
import { BurgerMenuLeft } from '../BurgerMenuLeft';
3+
4+
describeTransitioner(BurgerMenuLeft);

0 commit comments

Comments
 (0)