Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
6365c2c
feat: introduce first iteration of AnimatedFAB component
lukewalczak Mar 3, 2021
ca0617d
refactor: remove useless code
lukewalczak Mar 3, 2021
b05e782
refactor: don't move fab when switching animation direction prop
lukewalczak Mar 3, 2021
c397460
refactor: interpolate animation on fab text
lukewalczak Mar 4, 2021
f0ae001
refactor: code and example clean up
lukewalczak Mar 4, 2021
554e79a
refactor: use one animated value
lukewalczak Mar 8, 2021
0049b6d
feat: configure shadow for Android
lukewalczak Mar 8, 2021
396e775
refactor: introduce isIOS
lukewalczak Mar 8, 2021
fb69d42
refactor: fixes after merge
lukewalczak Mar 26, 2021
06414ee
refactor: adjust android example
lukewalczak Mar 26, 2021
9f77407
refactor: small changes
lukewalczak Mar 29, 2021
4e99ffd
fix: add support for animateFor: left property
Drakeoon Sep 20, 2021
7adecfc
test: add snapshot tests for AnimatedFAB component
Drakeoon Sep 20, 2021
20b5913
test: add snapshot tests for new properties
Drakeoon Sep 21, 2021
c84999c
feat: tweak example to match MD docs
Drakeoon Sep 21, 2021
7c7cc82
feat: improve AnimatedFAB example for iOS
Drakeoon Sep 21, 2021
c0de872
feat: improve AnimateFAB visuals
Drakeoon Sep 21, 2021
684c918
feat: improve example to showcase animateFrom prop
Drakeoon Sep 23, 2021
3221e19
feat: add support for RTL and fix animateFrom prop
Drakeoon Sep 23, 2021
77db8d4
fix: align iOS behavior for AnimatedFAB example
Drakeoon Sep 23, 2021
20e1481
feat: improve AnimatedFAB example
Drakeoon Sep 23, 2021
67e1ef5
feat: implement consistent styles with a dirty approach
Drakeoon Sep 23, 2021
d241783
chore: refactor innerWrapper styles
Drakeoon Sep 27, 2021
f2030fc
chore: refactor icon styles
Drakeoon Sep 27, 2021
3ebb9e6
chore: refactor reversed properties for direction
Drakeoon Sep 27, 2021
7cff972
chore: refactor remaining transforms
Drakeoon Sep 27, 2021
acaa3d2
fix: improve shadow behavior
Drakeoon Sep 27, 2021
b0a45f3
feat: add config controls to AnimatedFABExample
Drakeoon Oct 8, 2021
24f8aeb
refactor: split AnimatedFABExample into separete files
Drakeoon Oct 8, 2021
033d87a
refactor: improve FlatList performance
Drakeoon Oct 8, 2021
ccb2383
refactor: extract controls to separate component
Drakeoon Oct 8, 2021
ac234a9
feat: introduce first iteration of AnimatedFAB component
lukewalczak Mar 3, 2021
2404d9b
refactor: remove useless code
lukewalczak Mar 3, 2021
0aa14fc
refactor: don't move fab when switching animation direction prop
lukewalczak Mar 3, 2021
dd61dc7
refactor: interpolate animation on fab text
lukewalczak Mar 4, 2021
e7047e0
refactor: code and example clean up
lukewalczak Mar 4, 2021
1f585a9
refactor: use one animated value
lukewalczak Mar 8, 2021
1f7f13b
feat: configure shadow for Android
lukewalczak Mar 8, 2021
0069512
refactor: introduce isIOS
lukewalczak Mar 8, 2021
73a71fc
refactor: fixes after merge
lukewalczak Mar 26, 2021
f01ca7d
refactor: adjust android example
lukewalczak Mar 26, 2021
a573c04
refactor: small changes
lukewalczak Mar 29, 2021
5af9ef8
Merge branch 'feat/animated-extended-fab' of github.com:callstack/rea…
Drakeoon Oct 11, 2021
fcaefef
feat: hide AnimatedFab for production
lukewalczak Oct 13, 2021
39f8ede
Merge branch 'feat/animated-extended-fab' of github.com:callstack/rea…
Drakeoon Oct 18, 2021
ceb03cb
refactor: apply small feedback notes
Drakeoon Oct 19, 2021
f240cff
Merge branch 'main' into feat/animated-extended-fab-improvements
Drakeoon Oct 19, 2021
c897aa0
refactor: apply small feedback notes
Drakeoon Oct 21, 2021
7aa93e7
refactor: move dynamic styles to a separate utils file
Drakeoon Oct 21, 2021
6a56b0d
refactor: move AnimatedFAB files to a separate folder
Drakeoon Oct 21, 2021
c968eda
refactor: export missing types from the package
Drakeoon Oct 21, 2021
d05dc7b
chore: enable AnimateFAB in production
Drakeoon Oct 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/component-docs.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ function getPages() {
.split('\n')
.map((line) => line.split(' ').pop().replace(/('|;)/g, ''))
.filter((line) => line.startsWith('./components/'))
.filter((line) => line !== './components/FAB/AnimatedFAB')
.map((line) => {
const file = require.resolve(path.join(__dirname, '../src', line));
if (/\/index\.(js|tsx?)$/.test(file)) {
Expand Down
2 changes: 1 addition & 1 deletion example/src/ExampleList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const examples: Record<
string,
React.ComponentType<any> & { title: string }
> = {
...(__DEV__ && { animatedFab: AnimatedFABExample }),
animatedFab: AnimatedFABExample,
activityIndicator: ActivityIndicatorExample,
appbar: AppbarExample,
avatar: AvatarExample,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import * as React from 'react';
import { View, StyleSheet, FlatList, Animated, Platform } from 'react-native';
import type { NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
import {
Colors,
AnimatedFAB,
useTheme,
Avatar,
Paragraph,
} from 'react-native-paper';
import { Colors, useTheme, Avatar, Paragraph } from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { animatedFABExampleData } from '../../utils';
import { animatedFABExampleData } from '../../../utils';
import CustomFAB from './CustomFAB';
import CustomFABControls, {
Controls,
initialControls,
} from './CustomFABControls';

type Item = {
id: string;
Expand All @@ -23,55 +22,24 @@ type Item = {
bgColor: string;
};

type CustomFABProps = {
animatedValue: Animated.Value;
visible: boolean;
extended: boolean;
};

const isIOS = Platform.OS === 'ios';

const CustomFAB = ({ animatedValue, visible, extended }: CustomFABProps) => {
const [isExtended, setIsExtended] = React.useState(true);

React.useEffect(() => {
if (!isIOS) {
animatedValue.addListener(({ value }: { value: number }) =>
setIsExtended(value > 0 ? true : false)
);
} else setIsExtended(extended);
}, [animatedValue, extended]);

return (
<AnimatedFAB
icon={'pencil'}
label={'Create'}
extended={isExtended}
uppercase={false}
onPress={() => console.log('Pressed')}
visible={visible}
animateFrom="right"
iconMode="dynamic"
style={styles.fabStyle}
/>
);
};

const AnimatedFABExample = () => {
const {
colors: { background },
} = useTheme();

const [scrollPosition, setScrollPosition] = React.useState<number>(0);
const [extended, setExtended] = React.useState<boolean>(false);
const isIOS = Platform.OS === 'ios';

const [extended, setExtended] = React.useState<boolean>(true);
const [visible, setVisible] = React.useState<boolean>(true);

const [controls, setControls] = React.useState<Controls>(initialControls);

const { current: velocity } = React.useRef<Animated.Value>(
new Animated.Value(0)
);

const renderItem = ({ item }: { item: Item }) => {
return (
const renderItem = React.useCallback(
({ item }: { item: Item }) => (
<View style={styles.itemContainer}>
<Avatar.Text
style={[styles.avatar, { backgroundColor: item.bgColor }]}
Expand Down Expand Up @@ -117,39 +85,36 @@ const AnimatedFABExample = () => {
</View>
</View>
</View>
);
};
),
[visible]
);

const onScroll = ({
nativeEvent,
}: NativeSyntheticEvent<NativeScrollEvent>) => {
const currentScrollPosition =
Math.floor(nativeEvent?.contentOffset?.y) ?? 0;

if (!isIOS) {
return velocity.setValue(nativeEvent?.velocity?.y ?? 0);
return velocity.setValue(currentScrollPosition);
}

const currentScrollPosition = Math.floor(nativeEvent.contentOffset.y);
setExtended(currentScrollPosition <= 0);
};

const scrollHeight =
nativeEvent.contentSize.height - nativeEvent.layoutMeasurement.height;
const _keyExtractor = React.useCallback(
Comment thread
Drakeoon marked this conversation as resolved.
(item: { id: string }) => item.id,
[]
);

setScrollPosition(currentScrollPosition);

if (currentScrollPosition > scrollPosition && scrollPosition > 0) {
setExtended(false);
} else if (
currentScrollPosition < scrollPosition &&
scrollPosition < scrollHeight
) {
setExtended(true);
}
};
const { animateFrom, iconMode } = controls;

return (
<>
<FlatList
data={animatedFABExampleData}
renderItem={renderItem}
keyExtractor={(item: { id: string }) => item.id}
keyExtractor={_keyExtractor}
onEndReachedThreshold={0}
scrollEventThrottle={16}
style={[styles.flex, { backgroundColor: background }]}
Expand All @@ -159,10 +124,16 @@ const AnimatedFABExample = () => {
]}
onScroll={onScroll}
/>

<CustomFABControls controls={controls} setControls={setControls} />

<CustomFAB
visible={visible}
animatedValue={velocity}
extended={extended}
label={'New Message'}
animateFrom={animateFrom}
iconMode={iconMode}
/>
</>
);
Expand Down Expand Up @@ -213,11 +184,6 @@ const styles = StyleSheet.create({
marginRight: 8,
flex: 1,
},
fabStyle: {
right: 16,
bottom: 32,
position: 'absolute',
},
});

export default AnimatedFABExample;
66 changes: 66 additions & 0 deletions example/src/Examples/AnimatedFABExample/CustomFAB.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import {
StyleProp,
ViewStyle,
Animated,
StyleSheet,
Platform,
} from 'react-native';
import { AnimatedFAB } from 'react-native-paper';

type CustomFABProps = {
animatedValue: Animated.Value;
visible: boolean;
extended: boolean;
label: string;
animateFrom: 'left' | 'right';
iconMode?: 'static' | 'dynamic';
style?: StyleProp<ViewStyle>;
};

const CustomFAB = ({
animatedValue,
visible,
extended,
label,
animateFrom,
style,
iconMode,
}: CustomFABProps) => {
const [isExtended, setIsExtended] = React.useState(true);

const isIOS = Platform.OS === 'ios';

React.useEffect(() => {
if (!isIOS) {
animatedValue.addListener(({ value }: { value: number }) => {
setIsExtended(value <= 0);
});
} else setIsExtended(extended);
}, [animatedValue, extended, isIOS]);

const fabStyle = { [animateFrom]: 16 };

return (
<AnimatedFAB
icon={'plus'}
label={label}
extended={isExtended}
uppercase={true}
onPress={() => console.log('Pressed')}
visible={visible}
animateFrom={animateFrom}
iconMode={iconMode}
style={[styles.fabStyle, style, fabStyle]}
/>
);
};

export default CustomFAB;

const styles = StyleSheet.create({
fabStyle: {
bottom: 16,
position: 'absolute',
},
});
126 changes: 126 additions & 0 deletions example/src/Examples/AnimatedFABExample/CustomFABControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React from 'react';
import { StyleSheet, View, FlatList } from 'react-native';
import { Colors, Paragraph, RadioButton } from 'react-native-paper';
import type {
AnimatedFABAnimateFrom,
AnimatedFABIconMode,
} from 'react-native-paper';

export type Controls = {
iconMode: AnimatedFABIconMode;
animateFrom: AnimatedFABAnimateFrom;
};

export const initialControls: Controls = {
iconMode: 'static',
animateFrom: 'right',
};

type Props = {
controls: Controls;
setControls(controls: React.SetStateAction<Controls>): void;
};

type ControlValue = AnimatedFABIconMode | AnimatedFABAnimateFrom;

type CustomControlProps = {
name: string;
options: ControlValue[];
value: ControlValue;
onChange(newValue: ControlValue): void;
};

const CustomControl = ({
name,
options,
value,
onChange,
}: CustomControlProps) => {
const _renderItem = React.useCallback(
({ item }) => (
<View style={styles.controlItem}>
<Paragraph>{item}</Paragraph>

<RadioButton
value="dynamic"
status={value === item ? 'checked' : 'unchecked'}
onPress={() => onChange(item)}
/>
</View>
),
[value, onChange]
);

const _keyExtractor = React.useCallback((item) => item, []);

return (
<View style={styles.controlWrapper}>
<Paragraph>{name}</Paragraph>

<FlatList
horizontal
data={options}
renderItem={_renderItem}
keyExtractor={_keyExtractor}
contentContainerStyle={styles.controlItemsList}
Comment thread
Drakeoon marked this conversation as resolved.
/>
</View>
);
};

const CustomFABControls = ({
setControls,
controls: { animateFrom, iconMode },
}: Props) => {
const setIconMode = (newIconMode: AnimatedFABIconMode) =>
setControls((state) => ({ ...state, iconMode: newIconMode }));

const setAnimateFrom = (newAnimateFrom: AnimatedFABAnimateFrom) =>
setControls((state) => ({ ...state, animateFrom: newAnimateFrom }));

return (
<View style={styles.controlsWrapper}>
<CustomControl
name="iconMode"
options={['static', 'dynamic']}
value={iconMode}
onChange={setIconMode}
/>

<CustomControl
name="animateFrom"
options={['left', 'right']}
value={animateFrom}
onChange={setAnimateFrom}
/>
</View>
);
};

export default CustomFABControls;

const styles = StyleSheet.create({
controlsWrapper: {
flex: 1,
position: 'absolute',
top: 0,
left: 0,
right: 0,
backgroundColor: Colors.white,
paddingVertical: 12,
paddingHorizontal: 16,
},
controlWrapper: {
flexDirection: 'row',
alignItems: 'center',
},
controlItemsList: {
flex: 1,
justifyContent: 'flex-end',
},
controlItem: {
marginLeft: 16,
flexDirection: 'row',
alignItems: 'center',
},
});
1 change: 1 addition & 0 deletions example/src/Examples/AnimatedFABExample/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './AnimatedFABExample';
Loading