Update animated components to use TypeScript

This commit is contained in:
Arnaud Vergnet 2020-09-22 17:43:40 +02:00
parent f43dc55735
commit 140bcf3675
3 changed files with 84 additions and 70 deletions

View file

@ -17,42 +17,37 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {View} from 'react-native'; import {View, ViewStyle} from 'react-native';
import {List, withTheme} from 'react-native-paper'; import {List, withTheme} from 'react-native-paper';
import Collapsible from 'react-native-collapsible'; import Collapsible from 'react-native-collapsible';
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import type {CustomThemeType} from '../../managers/ThemeManager';
import type {ListIconPropsType} from '../../constants/PaperStyles';
type PropsType = { type PropsType = {
theme: CustomThemeType, theme: ReactNativePaper.Theme;
title: string, title: string;
subtitle?: string, subtitle?: string;
left?: () => React.Node, style: ViewStyle;
opened?: boolean, left?: (props: {
unmountWhenCollapsed?: boolean, color: string;
children?: React.Node, style?: {
marginRight: number;
marginVertical?: number;
};
}) => React.ReactNode;
opened?: boolean;
unmountWhenCollapsed?: boolean;
children?: React.ReactNode;
}; };
type StateType = { type StateType = {
expanded: boolean, expanded: boolean;
}; };
const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon); const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon);
class AnimatedAccordion extends React.Component<PropsType, StateType> { class AnimatedAccordion extends React.Component<PropsType, StateType> {
static defaultProps = { chevronRef: {current: null | (typeof AnimatedListIcon & List.Icon)};
subtitle: '',
left: null,
opened: null,
unmountWhenCollapsed: false,
children: null,
};
chevronRef: {current: null | AnimatedListIcon};
chevronIcon: string; chevronIcon: string;
@ -62,6 +57,9 @@ class AnimatedAccordion extends React.Component<PropsType, StateType> {
constructor(props: PropsType) { constructor(props: PropsType) {
super(props); super(props);
this.chevronIcon = '';
this.animStart = '';
this.animEnd = '';
this.state = { this.state = {
expanded: props.opened != null ? props.opened : false, expanded: props.opened != null ? props.opened : false,
}; };
@ -71,8 +69,9 @@ class AnimatedAccordion extends React.Component<PropsType, StateType> {
shouldComponentUpdate(nextProps: PropsType): boolean { shouldComponentUpdate(nextProps: PropsType): boolean {
const {state, props} = this; const {state, props} = this;
if (nextProps.opened != null && nextProps.opened !== props.opened) if (nextProps.opened != null && nextProps.opened !== props.opened) {
state.expanded = nextProps.opened; state.expanded = nextProps.opened;
}
return true; return true;
} }
@ -101,17 +100,17 @@ class AnimatedAccordion extends React.Component<PropsType, StateType> {
} }
}; };
render(): React.Node { render() {
const {props, state} = this; const {props, state} = this;
const {colors} = props.theme; const {colors} = props.theme;
return ( return (
<View> <View style={props.style}>
<List.Item <List.Item
title={props.title} title={props.title}
subtitle={props.subtitle} description={props.subtitle}
titleStyle={state.expanded ? {color: colors.primary} : null} titleStyle={state.expanded ? {color: colors.primary} : null}
onPress={this.toggleAccordion} onPress={this.toggleAccordion}
right={(iconProps: ListIconPropsType): React.Node => ( right={(iconProps) => (
<AnimatedListIcon <AnimatedListIcon
ref={this.chevronRef} ref={this.chevronRef}
style={iconProps.style} style={iconProps.style}

View file

@ -17,29 +17,30 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {StyleSheet, View} from 'react-native'; import {
NativeScrollEvent,
NativeSyntheticEvent,
StyleSheet,
View,
} from 'react-native';
import {FAB, IconButton, Surface, withTheme} from 'react-native-paper'; import {FAB, IconButton, Surface, withTheme} from 'react-native-paper';
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import {StackNavigationProp} from '@react-navigation/stack'; import {StackNavigationProp} from '@react-navigation/stack';
import AutoHideHandler from '../../utils/AutoHideHandler'; import AutoHideHandler from '../../utils/AutoHideHandler';
import CustomTabBar from '../Tabbar/CustomTabBar'; import CustomTabBar from '../Tabbar/CustomTabBar';
import type {CustomThemeType} from '../../managers/ThemeManager';
import type {OnScrollType} from '../../utils/AutoHideHandler';
const AnimatedFAB = Animatable.createAnimatableComponent(FAB); const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
type PropsType = { type PropsType = {
navigation: StackNavigationProp, navigation: StackNavigationProp<any>;
theme: CustomThemeType, theme: ReactNativePaper.Theme;
onPress: (action: string, data?: string) => void, onPress: (action: string, data?: string) => void;
seekAttention: boolean, seekAttention: boolean;
}; };
type StateType = { type StateType = {
currentMode: string, currentMode: string;
}; };
const DISPLAY_MODES = { const DISPLAY_MODES = {
@ -78,14 +79,14 @@ const styles = StyleSheet.create({
}); });
class AnimatedBottomBar extends React.Component<PropsType, StateType> { class AnimatedBottomBar extends React.Component<PropsType, StateType> {
ref: {current: null | Animatable.View}; ref: {current: null | (Animatable.View & View)};
hideHandler: AutoHideHandler; hideHandler: AutoHideHandler;
displayModeIcons: {[key: string]: string}; displayModeIcons: {[key: string]: string};
constructor() { constructor(props: PropsType) {
super(); super(props);
this.state = { this.state = {
currentMode: DISPLAY_MODES.WEEK, currentMode: DISPLAY_MODES.WEEK,
}; };
@ -108,13 +109,17 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
} }
onHideChange = (shouldHide: boolean) => { onHideChange = (shouldHide: boolean) => {
if (this.ref.current != null) { const ref = this.ref;
if (shouldHide) this.ref.current.fadeOutDown(500); if (ref && ref.current && ref.current.fadeOutDown && ref.current.fadeInUp) {
else this.ref.current.fadeInUp(500); if (shouldHide) {
ref.current.fadeOutDown(500);
} else {
ref.current.fadeInUp(500);
}
} }
}; };
onScroll = (event: OnScrollType) => { onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
this.hideHandler.onScroll(event); this.hideHandler.onScroll(event);
}; };
@ -139,7 +144,7 @@ class AnimatedBottomBar extends React.Component<PropsType, StateType> {
props.onPress('changeView', newMode); props.onPress('changeView', newMode);
}; };
render(): React.Node { render() {
const {props, state} = this; const {props, state} = this;
const buttonColor = props.theme.colors.primary; const buttonColor = props.theme.colors.primary;
return ( return (

View file

@ -17,22 +17,23 @@
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/ */
// @flow
import * as React from 'react'; import * as React from 'react';
import {StyleSheet} from 'react-native'; import {
NativeScrollEvent,
NativeSyntheticEvent,
StyleSheet,
View,
} from 'react-native';
import {FAB} from 'react-native-paper'; import {FAB} from 'react-native-paper';
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import AutoHideHandler from '../../utils/AutoHideHandler'; import AutoHideHandler from '../../utils/AutoHideHandler';
import CustomTabBar from '../Tabbar/CustomTabBar'; import CustomTabBar from '../Tabbar/CustomTabBar';
type PropsType = { type PropsType = {
icon: string, icon: string;
onPress: () => void, onPress: () => void;
}; };
const AnimatedFab = Animatable.createAnimatableComponent(FAB);
const styles = StyleSheet.create({ const styles = StyleSheet.create({
fab: { fab: {
position: 'absolute', position: 'absolute',
@ -42,41 +43,50 @@ const styles = StyleSheet.create({
}); });
export default class AnimatedFAB extends React.Component<PropsType> { export default class AnimatedFAB extends React.Component<PropsType> {
ref: {current: null | Animatable.View}; ref: {current: null | (Animatable.View & View)};
hideHandler: AutoHideHandler; hideHandler: AutoHideHandler;
constructor() { constructor(props: PropsType) {
super(); super(props);
this.ref = React.createRef(); this.ref = React.createRef();
this.hideHandler = new AutoHideHandler(false); this.hideHandler = new AutoHideHandler(false);
this.hideHandler.addListener(this.onHideChange); this.hideHandler.addListener(this.onHideChange);
} }
onScroll = (event: SyntheticEvent<EventTarget>) => { onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
this.hideHandler.onScroll(event); this.hideHandler.onScroll(event);
}; };
onHideChange = (shouldHide: boolean) => { onHideChange = (shouldHide: boolean) => {
if (this.ref.current != null) { const ref = this.ref;
if (shouldHide) this.ref.current.bounceOutDown(1000); if (
else this.ref.current.bounceInUp(1000); ref &&
ref.current &&
ref.current.bounceOutDown &&
ref.current.bounceInUp
) {
if (shouldHide) {
ref.current.bounceOutDown(1000);
} else {
ref.current.bounceInUp(1000);
}
} }
}; };
render(): React.Node { render() {
const {props} = this; const {props} = this;
return ( return (
<AnimatedFab <Animatable.View ref={this.ref} useNativeDriver={true}>
ref={this.ref} <FAB
useNativeDriver icon={props.icon}
icon={props.icon} onPress={props.onPress}
onPress={props.onPress} style={{
style={{ ...styles.fab,
...styles.fab, bottom: CustomTabBar.TAB_BAR_HEIGHT,
bottom: CustomTabBar.TAB_BAR_HEIGHT, }}
}} />
/> </Animatable.View>
); );
} }
} }