forked from vergnet/application-amicale
Play tab switching animations
This commit is contained in:
parent
172842294c
commit
13e7a3b593
8 changed files with 156 additions and 57 deletions
54
src/components/Custom/AnimatedFocusView.js
Normal file
54
src/components/Custom/AnimatedFocusView.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import * as Animatable from "react-native-animatable";
|
||||
import {CommonActions} from "@react-navigation/native";
|
||||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
route: Object,
|
||||
children: React$Node
|
||||
}
|
||||
|
||||
export default class AnimatedFocusView extends React.Component<Props> {
|
||||
|
||||
ref: Object;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ref = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.navigation.addListener('focus', this.onScreenFocus);
|
||||
}
|
||||
|
||||
onScreenFocus = () => {
|
||||
if (this.props.route.params !== undefined) {
|
||||
if (this.props.route.params.animationDir && this.ref.current) {
|
||||
if (this.props.route.params.animationDir === "right")
|
||||
this.ref.current.fadeInRight(300);
|
||||
else
|
||||
this.ref.current.fadeInLeft(300);
|
||||
// reset params to prevent infinite loop
|
||||
this.props.navigation.dispatch(CommonActions.setParams({animationDir: null}));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Animatable.View
|
||||
ref={this.ref}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
useNativeDriver
|
||||
>
|
||||
{this.props.children}
|
||||
</Animatable.View>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,25 @@ const TAB_BAR_HEIGHT = 48;
|
|||
*/
|
||||
class CustomTabBar extends React.Component<Props> {
|
||||
|
||||
shouldComponentUpdate(nextProps: Props): boolean {
|
||||
return (nextProps.theme.dark !== this.props.theme.dark)
|
||||
|| (nextProps.state.index !== this.props.state.index);
|
||||
}
|
||||
|
||||
onItemPress(route: Object, currentIndex: number, destIndex: number) {
|
||||
const event = this.props.navigation.emit({
|
||||
type: 'tabPress',
|
||||
target: route.key,
|
||||
canPreventDefault: true,
|
||||
});
|
||||
if (currentIndex !== destIndex && !event.defaultPrevented) {
|
||||
this.props.navigation.navigate(route.name, {
|
||||
screen: 'index',
|
||||
params: {animationDir: currentIndex < destIndex ? "right" : "left"}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const state = this.props.state;
|
||||
const descriptors = this.props.descriptors;
|
||||
|
@ -38,17 +57,7 @@ class CustomTabBar extends React.Component<Props> {
|
|||
|
||||
const isFocused = state.index === index;
|
||||
|
||||
const onPress = () => {
|
||||
const event = navigation.emit({
|
||||
type: 'tabPress',
|
||||
target: route.key,
|
||||
canPreventDefault: true,
|
||||
});
|
||||
|
||||
if (!isFocused && !event.defaultPrevented) {
|
||||
navigation.navigate(route.name);
|
||||
}
|
||||
};
|
||||
const onPress = () => this.onItemPress(route, state.index, index);
|
||||
|
||||
const onLongPress = () => {
|
||||
navigation.emit({
|
||||
|
@ -66,7 +75,9 @@ class CustomTabBar extends React.Component<Props> {
|
|||
icon={options.tabBarIcon(iconData)}
|
||||
color={color}
|
||||
label={label}
|
||||
focused={isFocused}/>
|
||||
focused={isFocused}
|
||||
extraData={state.index > index}
|
||||
/>
|
||||
} else
|
||||
return <TabHomeIcon
|
||||
onPress={onPress}
|
||||
|
|
|
@ -14,6 +14,7 @@ type Props = {
|
|||
onPress: Function,
|
||||
onLongPress: Function,
|
||||
theme: Object,
|
||||
extraData: any,
|
||||
}
|
||||
|
||||
const AnimatedIcon = Animatable.createAnimatableComponent(MaterialCommunityIcons);
|
||||
|
@ -58,7 +59,8 @@ class TabIcon extends React.Component<Props> {
|
|||
|
||||
shouldComponentUpdate(nextProps: Props): boolean {
|
||||
return (nextProps.focused !== this.props.focused)
|
||||
|| (nextProps.theme.dark !== this.props.theme.dark);
|
||||
|| (nextProps.theme.dark !== this.props.theme.dark)
|
||||
|| (nextProps.extraData !== this.props.extraData);
|
||||
}
|
||||
|
||||
render(): React$Node {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Animated, FlatList, View} from 'react-native';
|
||||
import {Animated, FlatList} from 'react-native';
|
||||
import i18n from "i18n-js";
|
||||
import DashboardItem from "../components/Home/EventDashboardItem";
|
||||
import WebSectionList from "../components/Lists/WebSectionList";
|
||||
|
@ -16,6 +16,7 @@ import {CommonActions} from '@react-navigation/native';
|
|||
import MaterialHeaderButtons, {Item} from "../components/Custom/HeaderButton";
|
||||
import {AnimatedValue} from "react-native-reanimated";
|
||||
import AnimatedFAB from "../components/Custom/AnimatedFAB";
|
||||
import AnimatedFocusView from "../components/Custom/AnimatedFocusView";
|
||||
// import DATA from "../dashboard_data.json";
|
||||
|
||||
|
||||
|
@ -113,12 +114,22 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
</MaterialHeaderButtons>;
|
||||
};
|
||||
|
||||
onProxiwashClick = () => this.props.navigation.navigate('proxiwash');
|
||||
onProxiwashClick = () => {
|
||||
this.props.navigation.navigate("proxiwash", {
|
||||
screen: 'index',
|
||||
params: {animationDir: "right"} // Play tab animation
|
||||
});
|
||||
};
|
||||
|
||||
onProximoClick = () => {
|
||||
this.props.navigation.navigate("proximo", {
|
||||
screen: 'index',
|
||||
params: {animationDir: "left"} // Play tab animation
|
||||
});
|
||||
};
|
||||
|
||||
onTutorInsaClick = () => this.props.navigation.navigate('tutorinsa');
|
||||
|
||||
onProximoClick = () => this.props.navigation.navigate('proximo');
|
||||
|
||||
onMenuClick = () => this.props.navigation.navigate('self-menu');
|
||||
|
||||
/**
|
||||
|
@ -460,7 +471,9 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
render() {
|
||||
const nav = this.props.navigation;
|
||||
return (
|
||||
<View>
|
||||
<AnimatedFocusView
|
||||
{...this.props}
|
||||
>
|
||||
<WebSectionList
|
||||
createDataset={this.createDataset}
|
||||
navigation={nav}
|
||||
|
@ -476,7 +489,7 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
icon="qrcode-scan"
|
||||
onPress={this.openScanner}
|
||||
/>
|
||||
</View>
|
||||
</AnimatedFocusView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from '../../utils/Planning';
|
||||
import {Avatar, Divider, List} from 'react-native-paper';
|
||||
import CustomAgenda from "../../components/Custom/CustomAgenda";
|
||||
import AnimatedFocusView from "../../components/Custom/AnimatedFocusView";
|
||||
|
||||
LocaleConfig.locales['fr'] = {
|
||||
monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
|
||||
|
@ -26,6 +27,7 @@ LocaleConfig.locales['fr'] = {
|
|||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
route: Object,
|
||||
}
|
||||
|
||||
type State = {
|
||||
|
@ -229,34 +231,38 @@ class PlanningScreen extends React.Component<Props, State> {
|
|||
render() {
|
||||
// console.log("rendering PlanningScreen");
|
||||
return (
|
||||
<CustomAgenda
|
||||
<AnimatedFocusView
|
||||
{...this.props}
|
||||
// the list of items that have to be displayed in agenda. If you want to render item as empty date
|
||||
// the value of date key kas to be an empty array []. If there exists no value for date key it is
|
||||
// considered that the date in question is not yet loaded
|
||||
items={this.state.agendaItems}
|
||||
// initially selected day
|
||||
selected={this.currentDate}
|
||||
// Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
|
||||
minDate={this.currentDate}
|
||||
// Max amount of months allowed to scroll to the past. Default = 50
|
||||
pastScrollRange={1}
|
||||
// Max amount of months allowed to scroll to the future. Default = 50
|
||||
futureScrollRange={AGENDA_MONTH_SPAN}
|
||||
// If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
|
||||
onRefresh={this.onRefresh}
|
||||
// callback that fires when the calendar is opened or closed
|
||||
onCalendarToggled={this.onCalendarToggled}
|
||||
// Set this true while waiting for new data from a refresh
|
||||
refreshing={this.state.refreshing}
|
||||
renderItem={this.getRenderItem}
|
||||
renderEmptyDate={this.getRenderEmptyDate}
|
||||
rowHasChanged={this.rowHasChanged}
|
||||
// If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
|
||||
firstDay={1}
|
||||
// ref to this agenda in order to handle back button event
|
||||
onRef={this.onAgendaRef}
|
||||
/>
|
||||
>
|
||||
<CustomAgenda
|
||||
{...this.props}
|
||||
// the list of items that have to be displayed in agenda. If you want to render item as empty date
|
||||
// the value of date key kas to be an empty array []. If there exists no value for date key it is
|
||||
// considered that the date in question is not yet loaded
|
||||
items={this.state.agendaItems}
|
||||
// initially selected day
|
||||
selected={this.currentDate}
|
||||
// Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
|
||||
minDate={this.currentDate}
|
||||
// Max amount of months allowed to scroll to the past. Default = 50
|
||||
pastScrollRange={1}
|
||||
// Max amount of months allowed to scroll to the future. Default = 50
|
||||
futureScrollRange={AGENDA_MONTH_SPAN}
|
||||
// If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
|
||||
onRefresh={this.onRefresh}
|
||||
// callback that fires when the calendar is opened or closed
|
||||
onCalendarToggled={this.onCalendarToggled}
|
||||
// Set this true while waiting for new data from a refresh
|
||||
refreshing={this.state.refreshing}
|
||||
renderItem={this.getRenderItem}
|
||||
renderEmptyDate={this.getRenderEmptyDate}
|
||||
rowHasChanged={this.rowHasChanged}
|
||||
// If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
|
||||
firstDay={1}
|
||||
// ref to this agenda in order to handle back button event
|
||||
onRef={this.onAgendaRef}
|
||||
/>
|
||||
</AnimatedFocusView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@ import i18n from "i18n-js";
|
|||
import WebSectionList from "../../components/Lists/WebSectionList";
|
||||
import {List, withTheme} from 'react-native-paper';
|
||||
import MaterialHeaderButtons, {Item} from "../../components/Custom/HeaderButton";
|
||||
import AnimatedFocusView from "../../components/Custom/AnimatedFocusView";
|
||||
|
||||
const DATA_URL = "https://etud.insa-toulouse.fr/~proximo/data/stock-v2.json";
|
||||
const LIST_ITEM_HEIGHT = 84;
|
||||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
route: Object,
|
||||
}
|
||||
|
||||
type State = {
|
||||
|
@ -232,13 +234,17 @@ class ProximoMainScreen extends React.Component<Props, State> {
|
|||
render() {
|
||||
const nav = this.props.navigation;
|
||||
return (
|
||||
<WebSectionList
|
||||
createDataset={this.createDataset}
|
||||
navigation={nav}
|
||||
autoRefreshTime={0}
|
||||
refreshOnFocus={false}
|
||||
fetchUrl={DATA_URL}
|
||||
renderItem={this.getRenderItem}/>
|
||||
<AnimatedFocusView
|
||||
{...this.props}
|
||||
>
|
||||
<WebSectionList
|
||||
createDataset={this.createDataset}
|
||||
navigation={nav}
|
||||
autoRefreshTime={0}
|
||||
refreshOnFocus={false}
|
||||
fetchUrl={DATA_URL}
|
||||
renderItem={this.getRenderItem}/>
|
||||
</AnimatedFocusView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import AprilFoolsManager from "../../managers/AprilFoolsManager";
|
|||
import MaterialHeaderButtons, {Item} from "../../components/Custom/HeaderButton";
|
||||
import ProxiwashSectionHeader from "../../components/Lists/ProxiwashSectionHeader";
|
||||
import {withCollapsible} from "../../utils/withCollapsible";
|
||||
import AnimatedFocusView from "../../components/Custom/AnimatedFocusView";
|
||||
|
||||
const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/washinsa/washinsa.json";
|
||||
|
||||
|
@ -25,6 +26,7 @@ const LIST_ITEM_HEIGHT = 64;
|
|||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
route: Object,
|
||||
theme: Object,
|
||||
collapsibleStack: Object,
|
||||
}
|
||||
|
@ -420,7 +422,9 @@ class ProxiwashScreen extends React.Component<Props, State> {
|
|||
const nav = this.props.navigation;
|
||||
const {containerPaddingTop} = this.props.collapsibleStack;
|
||||
return (
|
||||
<View>
|
||||
<AnimatedFocusView
|
||||
{...this.props}
|
||||
>
|
||||
<Banner
|
||||
style={{
|
||||
marginTop: this.state.bannerVisible ? containerPaddingTop : 0,
|
||||
|
@ -451,7 +455,7 @@ class ProxiwashScreen extends React.Component<Props, State> {
|
|||
autoRefreshTime={REFRESH_TIME}
|
||||
refreshOnFocus={true}
|
||||
updateData={this.state.machinesWatched.length}/>
|
||||
</View>
|
||||
</AnimatedFocusView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import DateManager from "../../managers/DateManager";
|
|||
import AnimatedBottomBar from "../../components/Custom/AnimatedBottomBar";
|
||||
import {CommonActions} from "@react-navigation/native";
|
||||
import ErrorView from "../../components/Custom/ErrorView";
|
||||
import AnimatedFocusView from "../../components/Custom/AnimatedFocusView";
|
||||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
|
@ -299,7 +300,9 @@ class PlanexScreen extends React.Component<Props, State> {
|
|||
render() {
|
||||
const {containerPaddingTop} = this.props.collapsibleStack;
|
||||
return (
|
||||
<View style={{height: '100%'}}>
|
||||
<AnimatedFocusView
|
||||
{...this.props}
|
||||
>
|
||||
<Banner
|
||||
style={{
|
||||
marginTop: this.state.bannerVisible ? containerPaddingTop : 0,
|
||||
|
@ -337,7 +340,7 @@ class PlanexScreen extends React.Component<Props, State> {
|
|||
onPress={this.sendMessage}
|
||||
seekAttention={this.state.currentGroup.id === -1}
|
||||
/>
|
||||
</View>
|
||||
</AnimatedFocusView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue