From 6365c2c0f65f37081f6d2279d3065e20c9ed9a55 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Wed, 3 Mar 2021 11:18:03 +0100 Subject: [PATCH 01/49] feat: introduce first iteration of AnimatedFAB component --- example/src/ExampleList.tsx | 2 + example/src/Examples/AnimatedFABExample.tsx | 437 ++++++++++++++++++++ src/components/FAB/AnimatedFAB.tsx | 389 +++++++++++++++++ src/index.tsx | 3 +- 4 files changed, 830 insertions(+), 1 deletion(-) create mode 100644 example/src/Examples/AnimatedFABExample.tsx create mode 100644 src/components/FAB/AnimatedFAB.tsx diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index dc38d73c4d..f39c7708e9 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -37,11 +37,13 @@ import ToggleButtonExample from './Examples/ToggleButtonExample'; import TouchableRippleExample from './Examples/TouchableRippleExample'; import ThemeExample from './Examples/ThemeExample'; import RadioButtonItemExample from './Examples/RadioButtonItemExample'; +import AnimatedFABExample from './Examples/AnimatedFABExample'; export const examples: Record< string, React.ComponentType & { title: string } > = { + animatedFAB: AnimatedFABExample, activityIndicator: ActivityIndicatorExample, appbar: AppbarExample, avatar: AvatarExample, diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx new file mode 100644 index 0000000000..642e04f1d4 --- /dev/null +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -0,0 +1,437 @@ +import * as React from 'react'; +import { View, StyleSheet, FlatList } from 'react-native'; +import type { NativeSyntheticEvent, NativeScrollEvent } from 'react-native'; +import { + Colors, + AnimatedFAB, + useTheme, + Avatar, + Paragraph, +} from 'react-native-paper'; +import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; + +type Item = { + id: string; + sender: string; + header: string; + message: string; + initials: string; + date: string; + read: boolean; + favorite: boolean; + bgColor: string; +}; + +const DATA = [ + { + id: '1', + sender: 'Hermann, Pfannel & Schumm', + header: + 'Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl. Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', + message: + 'Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\n\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.', + initials: 'H', + date: '29.1.2021', + read: false, + favorite: false, + bgColor: '#ff0173', + }, + { + id: '2', + sender: 'Ziemann, Lockman and Kuvalis', + header: + 'Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam vel augue.', + message: + 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\n\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.', + initials: 'J', + date: '5.9.2020', + read: false, + favorite: false, + bgColor: '#b287a9', + }, + { + id: '3', + sender: 'Daniel, Kuhn and Wolf', + header: + 'Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh. Quisque id justo sit amet sapien dignissim vestibulum.', + message: + 'Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\n\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.', + initials: 'Y', + date: '13.6.2020', + read: true, + favorite: false, + bgColor: '#c1bde9', + }, + { + id: '4', + sender: 'Crona, Lind and Stoltenberg', + header: + 'Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus. Phasellus in felis. Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla.', + message: + 'Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', + initials: 'W', + date: '20.5.2020', + read: false, + favorite: false, + bgColor: '#932a24', + }, + { + id: '5', + sender: 'Bashirian-Hudson', + header: + 'Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue. Etiam justo. Etiam pretium iaculis justo. In hac habitasse platea dictumst.', + message: + 'Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\n\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\n\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + initials: 'V', + date: '21.9.2020', + read: true, + favorite: false, + bgColor: '#eda5b7', + }, + { + id: '6', + sender: 'Schmitt-Jacobs', + header: + 'Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh. In quis justo.', + message: + 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.', + initials: 'M', + date: '2.6.2020', + read: true, + favorite: true, + bgColor: '#18aaba', + }, + { + id: '7', + sender: 'Graham-Champlin', + header: + 'Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros. Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue.', + message: + 'In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', + initials: 'T', + date: '17.10.2020', + read: false, + favorite: true, + bgColor: '#cc5e54', + }, + { + id: '8', + sender: 'Schoen, Carroll and Herzog', + header: + 'Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', + message: + 'Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\n\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.', + initials: 'F', + date: '31.10.2020', + read: false, + favorite: false, + bgColor: '#28db04', + }, + { + id: '9', + sender: 'Pouros-Fay', + header: + 'Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius. Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.', + message: + 'Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\n\nSed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.', + initials: 'Z', + date: '6.1.2021', + read: true, + favorite: true, + bgColor: '#b6f3fb', + }, + { + id: '10', + sender: 'McKenzie, Ruecker and Bernhard', + header: 'Nunc purus. Phasellus in felis.', + message: + 'In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.', + initials: 'F', + date: '23.2.2021', + read: false, + favorite: false, + bgColor: '#96f066', + }, + { + id: '11', + sender: 'Olson Inc', + header: + 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui. Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.', + message: + 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\n\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\n\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.', + initials: 'V', + date: '17.10.2020', + read: false, + favorite: false, + bgColor: '#f2d49d', + }, + { + id: '12', + sender: 'Walsh LLC', + header: + 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis.', + message: 'Phasellus in felis. Donec semper sapien a libero. Nam dui.', + initials: 'O', + date: '6.1.2021', + read: false, + favorite: true, + bgColor: '#f477dc', + }, + { + id: '13', + sender: 'Lemke, Cremin and Kutch', + header: + 'Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat. Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede. Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus.', + message: + 'In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\n\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\n\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', + initials: 'D', + date: '10.12.2020', + read: false, + favorite: false, + bgColor: '#b2880e', + }, + { + id: '14', + sender: 'Ruecker Group', + header: + 'Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.', + message: + 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', + initials: 'F', + date: '21.8.2020', + read: true, + favorite: false, + bgColor: '#64983a', + }, + { + id: '15', + sender: 'Kovacek, Lockman and Kautzer', + header: + 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus. Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + message: + 'Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', + initials: 'L', + date: '5.3.2020', + read: true, + favorite: false, + bgColor: '#c7486b', + }, + { + id: '16', + sender: 'Jaskolski-Cummerata', + header: + 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi. Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.', + message: + 'Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\n\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.', + initials: 'C', + date: '21.4.2020', + read: false, + favorite: false, + bgColor: '#2f89a1', + }, + { + id: '17', + sender: 'Wunsch-Walker', + header: 'Donec vitae nisi.', + message: + 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', + initials: 'A', + date: '15.4.2020', + read: false, + favorite: true, + bgColor: '#033cbf', + }, + { + id: '18', + sender: 'Ledner Inc', + header: + 'Curabitur convallis. Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus. Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum.', + message: + 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', + initials: 'Y', + date: '16.5.2020', + read: false, + favorite: true, + bgColor: '#166b59', + }, + { + id: '19', + sender: 'Heaney-Rolfson', + header: 'Nunc rhoncus dui vel sem.', + message: + 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', + initials: 'T', + date: '19.1.2021', + read: true, + favorite: false, + bgColor: '#da0148', + }, + { + id: '20', + sender: 'Dietrich, Beier and Leannon', + header: + 'Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est. Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem. Duis aliquam convallis nunc.', + message: + 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.', + initials: 'Z', + date: '21.5.2020', + read: false, + favorite: false, + bgColor: '#22b33f', + }, +]; + +const AnimatedFABExample = () => { + const { + colors: { background }, + } = useTheme(); + + const [scrollPosition, setScrollPosition] = React.useState(0); + const [extended, setExtended] = React.useState(false); + const [visible, setVisible] = React.useState(true); + + const renderItem = ({ item }: { item: Item }) => { + return ( + + + + + + {item.sender} + + + {item.date} + + + + + + + {item.header} + + + {item.message} + + + + setVisible(!visible)} + style={styles.icon} + /> + + + + ); + }; + + const onScroll = ({ + nativeEvent, + }: NativeSyntheticEvent) => { + const currentScrollPosition = Math.floor(nativeEvent.contentOffset.y); + + const scrollHeight = + nativeEvent.contentSize.height - nativeEvent.layoutMeasurement.height; + + setScrollPosition(currentScrollPosition); + + if (currentScrollPosition > scrollPosition && scrollPosition > 0) { + setExtended(false); + } else if ( + currentScrollPosition < scrollPosition && + scrollPosition < scrollHeight + ) { + setExtended(true); + } + }; + + return ( + <> + item.id} + onEndReachedThreshold={0} + scrollEventThrottle={16} + style={[styles.flex, { backgroundColor: background }]} + contentContainerStyle={[ + styles.container, + { backgroundColor: background }, + ]} + onScroll={onScroll} + /> + console.log('Pressed')} + visible={visible} + /> + + ); +}; + +AnimatedFABExample.title = 'Animated Floating Action Button'; + +const styles = StyleSheet.create({ + container: { + padding: 16, + }, + avatar: { + marginRight: 16, + marginTop: 8, + }, + flex: { + flex: 1, + }, + itemContainer: { + marginBottom: 16, + flexDirection: 'row', + }, + itemTextContentContainer: { + flexDirection: 'column', + flex: 1, + }, + itemHeaderContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + itemMessageContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + flexGrow: 1, + }, + read: { + fontWeight: 'bold', + }, + icon: { + marginLeft: 16, + alignSelf: 'flex-end', + }, + date: { + fontSize: 12, + }, + header: { + fontSize: 14, + marginRight: 8, + flex: 1, + }, +}); + +export default AnimatedFABExample; diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx new file mode 100644 index 0000000000..d7aa78925f --- /dev/null +++ b/src/components/FAB/AnimatedFAB.tsx @@ -0,0 +1,389 @@ +import color from 'color'; +import * as React from 'react'; +import { + Animated, + View, + ViewStyle, + StyleSheet, + StyleProp, + Easing, +} from 'react-native'; +import Surface from '../Surface'; +import Icon from '../Icon'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import { withTheme } from '../../core/theming'; +import type { $RemoveChildren } from '../../types'; +import type { IconSource } from './../Icon'; +//@ts-ignore +import type { AccessibilityState, TextLayoutEvent } from 'react-native'; +import { white, black } from '../../styles/colors'; +import AnimatedText from '../Typography/AnimatedText'; + +type Props = $RemoveChildren & { + /** + * Icon to display for the `FAB`. + */ + icon: IconSource; + /** + * Label for extended `FAB`. + */ + label: string; + /** + * Make the label text uppercased. + */ + uppercase?: boolean; + /** + * Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB. + * Uses `label` by default if specified. + */ + accessibilityLabel?: string; + /** + * Accessibility state for the FAB. This is read by the screen reader when the user taps the FAB. + */ + accessibilityState?: AccessibilityState; + /** + * Custom color for the icon and label of the `FAB`. + */ + color?: string; + /** + * Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch. + */ + disabled?: boolean; + /** + * Whether `FAB` is currently visible. + */ + visible?: boolean; + /** + * Function to execute on press. + */ + onPress?: () => void; + /** + * Function to execute on long press. + */ + onLongPress?: () => void; + /** + * Whether icon should be translated to the end of extended `FAB` or be static and stay in the same place. The default value is `dynamic`. + */ + iconMode?: 'static' | 'dynamic'; + /** + * Indicates from which direction animation should be performed. The default value is `right`. + */ + animateFrom?: 'right' | 'left'; + /** + * Whether `FAB` should start animation to extend. + */ + extended: boolean; + style?: StyleProp; + /** + * @optional + */ + theme: ReactNativePaper.Theme; + testID?: string; +}; + +const SIZE = 56; + +const AnimatedFAB = ({ + icon, + label, + accessibilityLabel = label, + accessibilityState, + color: customColor, + disabled, + onPress, + onLongPress, + theme, + style, + visible = true, + uppercase = true, + testID, + animateFrom = 'right', + extended = false, + iconMode = 'dynamic', + ...rest +}: Props) => { + const { current: visibility } = React.useRef( + new Animated.Value(visible ? 1 : 0) + ); + const { current: scaleFAB } = React.useRef(new Animated.Value(1)); + const { current: translateFAB } = React.useRef(new Animated.Value(0)); + const { current: translateIcon } = React.useRef(new Animated.Value(0)); + const { current: opacityLabel } = React.useRef(new Animated.Value(0)); + + const { scale } = theme.animation; + + const [textWidth, setTextWidth] = React.useState(0); + const [textHeight, setTextHeight] = React.useState(0); + + const animateFromRight = animateFrom === 'right'; + const isIconStatic = iconMode === 'static'; + + React.useEffect(() => { + if (visible) { + Animated.timing(visibility, { + toValue: 1, + duration: 200 * scale, + useNativeDriver: true, + }).start(); + } else { + Animated.timing(visibility, { + toValue: 0, + duration: 150 * scale, + useNativeDriver: true, + }).start(); + } + }, [visible, scale, visibility]); + + const disabledColor = color(theme.dark ? white : black) + .alpha(0.12) + .rgb() + .string(); + + const { backgroundColor = disabled ? disabledColor : theme.colors.accent } = + StyleSheet.flatten(style) || {}; + + let foregroundColor; + + if (typeof customColor !== 'undefined') { + foregroundColor = customColor; + } else if (disabled) { + foregroundColor = color(theme.dark ? white : black) + .alpha(0.32) + .rgb() + .string(); + } else { + foregroundColor = !color(backgroundColor).isLight() + ? white + : 'rgba(0, 0, 0, .54)'; + } + + const rippleColor = color(foregroundColor).alpha(0.32).rgb().string(); + + const totalWidth = animateFromRight + ? -textWidth - SIZE / 2 + : textWidth + SIZE / 2; + + React.useEffect(() => { + const animations = [ + { + property: scaleFAB, + toValue: !extended ? 1 : 0.9, + }, + { + property: translateFAB, + toValue: !extended ? 0 : totalWidth, + }, + { + property: translateIcon, + toValue: !extended ? 0 : totalWidth, + exclude: isIconStatic, + }, + { + property: opacityLabel, + toValue: !extended ? 0 : 1, + duration: extended ? 250 : 100, + delay: extended ? 100 : 0, + }, + ]; + + Animated.parallel( + animations + .filter(({ exclude }) => !exclude) + .map(({ property, toValue, duration = 250, delay = 0 }) => { + return Animated.timing(property, { + toValue, + duration: duration * scale, + delay: delay * scale, + useNativeDriver: true, + easing: Easing.linear, + }); + }) + ).start(); + }, [ + extended, + scaleFAB, + opacityLabel, + totalWidth, + translateIcon, + translateFAB, + scale, + isIconStatic, + ]); + + const onTextLayout = ({ nativeEvent }: TextLayoutEvent) => { + const currentWidth = Math.ceil(nativeEvent.lines[0].width); + const currentHeight = Math.ceil(nativeEvent.lines[0].height); + + if (currentWidth !== textWidth || currentHeight !== textHeight) { + setTextWidth(currentWidth); + setTextHeight(currentHeight); + } + }; + + return ( + + } + > + + + + + + + + + + + + + + + + {label} + + + ); +}; + +const styles = StyleSheet.create({ + standard: { + height: SIZE, + borderRadius: SIZE / 2, + }, + disabled: { + elevation: 0, + }, + container: { + position: 'absolute', + borderRadius: SIZE / 2, + elevation: 6, + right: 16, + bottom: 32, + }, + innerWrapper: { + position: 'absolute', + flexDirection: 'row', + overflow: 'hidden', + borderRadius: SIZE / 2, + right: 0, + }, + touchable: { + borderRadius: SIZE / 2, + }, + leftInnerWrapper: { + left: 0, + right: undefined, + }, + iconWrapper: { + alignItems: 'center', + justifyContent: 'center', + position: 'absolute', + height: SIZE, + width: SIZE, + right: 0, + }, + moveIconFromLeft: { + left: 0, + right: undefined, + }, + label: { + position: 'absolute', + zIndex: 2, + }, + uppercaseLabel: { + textTransform: 'uppercase', + }, +}); + +export default withTheme(AnimatedFAB); diff --git a/src/index.tsx b/src/index.tsx index 871bd69d00..a8dcc282a7 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -28,7 +28,8 @@ export { default as Chip } from './components/Chip'; export { default as DataTable } from './components/DataTable/DataTable'; export { default as Dialog } from './components/Dialog/Dialog'; export { default as Divider } from './components/Divider'; -export { default as FAB } from './components/FAB'; +export { default as FAB } from './components/FAB/FAB'; +export { default as AnimatedFAB } from './components/FAB/AnimatedFAB'; export { default as HelperText } from './components/HelperText'; export { default as IconButton } from './components/IconButton'; export { default as Menu } from './components/Menu/Menu'; From ca0617ddaf3a7a8b4ea98473a5b4e88d6da3e7a3 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Wed, 3 Mar 2021 11:37:36 +0100 Subject: [PATCH 02/49] refactor: remove useless code --- src/components/FAB/AnimatedFAB.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index d7aa78925f..30fdf64f8d 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -226,7 +226,6 @@ const AnimatedFAB = ({ style={ [ { - backgroundColor: 'red', opacity: visibility, transform: [ { From b05e782f1f122c8623d4a3dd4a3b8d4ac335c334 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Wed, 3 Mar 2021 16:49:56 +0100 Subject: [PATCH 03/49] refactor: don't move fab when switching animation direction prop --- example/src/Examples/AnimatedFABExample.tsx | 2 ++ src/components/FAB/AnimatedFAB.tsx | 15 +++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index 642e04f1d4..f6944b806e 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -382,6 +382,8 @@ const AnimatedFABExample = () => { uppercase={false} onPress={() => console.log('Pressed')} visible={visible} + animateFrom="right" + iconMode="dynamic" /> ); diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 30fdf64f8d..9cc771e872 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -262,9 +262,6 @@ const AnimatedFAB = ({ accessibilityState={{ ...accessibilityState, disabled }} style={[styles.touchable, styles.standard]} testID={testID} - hitSlop={{ - left: textWidth + 1.5 * SIZE, - }} > Date: Thu, 4 Mar 2021 09:55:53 +0100 Subject: [PATCH 04/49] refactor: interpolate animation on fab text --- src/components/FAB/AnimatedFAB.tsx | 81 ++++++++++++++++++------------ 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 9cc771e872..272596daf7 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -181,19 +181,16 @@ const AnimatedFAB = ({ { property: opacityLabel, toValue: !extended ? 0 : 1, - duration: extended ? 250 : 100, - delay: extended ? 100 : 0, }, ]; Animated.parallel( animations .filter(({ exclude }) => !exclude) - .map(({ property, toValue, duration = 250, delay = 0 }) => { + .map(({ property, toValue }) => { return Animated.timing(property, { toValue, - duration: duration * scale, - delay: delay * scale, + duration: 150 * scale, useNativeDriver: true, easing: Easing.linear, }); @@ -248,6 +245,8 @@ const AnimatedFAB = ({ }, ], }, + styles.standard, + styles.innerContainer, ]} > - - {label} - + + + {label} + + ); }; @@ -347,7 +361,10 @@ const styles = StyleSheet.create({ borderRadius: SIZE / 2, elevation: 6, right: 390 / 2 + SIZE / 2, - bottom: 32, + bottom: 150, + }, + innerContainer: { + position: 'absolute', }, innerWrapper: { position: 'absolute', From f0ae001d3138243f540fbc5e254fdf827323848f Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Thu, 4 Mar 2021 15:11:03 +0100 Subject: [PATCH 05/49] refactor: code and example clean up --- example/src/Examples/AnimatedFABExample.tsx | 6 ++ src/components/FAB/AnimatedFAB.tsx | 95 +++++++++++---------- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index f6944b806e..0907b59674 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -384,6 +384,7 @@ const AnimatedFABExample = () => { visible={visible} animateFrom="right" iconMode="dynamic" + style={styles.fabStyle} /> ); @@ -434,6 +435,11 @@ const styles = StyleSheet.create({ marginRight: 8, flex: 1, }, + fabStyle: { + right: 16, + bottom: 32, + position: 'absolute', + }, }); export default AnimatedFABExample; diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 272596daf7..0eed2255c2 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -7,6 +7,9 @@ import { StyleSheet, StyleProp, Easing, + ScrollView, + Text, + Platform, } from 'react-native'; import Surface from '../Surface'; import Icon from '../Icon'; @@ -217,6 +220,20 @@ const AnimatedFAB = ({ } }; + const getSidesPosition = () => { + if (animateFromRight) { + return { + left: -totalWidth, + right: undefined, + }; + } else { + return { + left: undefined, + right: totalWidth, + }; + } + }; + return ( - + - - - + /> + @@ -308,16 +316,16 @@ const AnimatedFAB = ({ + + {Platform.OS !== 'ios' && ( + + {/* @ts-ignore */} + {label} + + )} ); }; @@ -358,47 +373,35 @@ const styles = StyleSheet.create({ }, container: { position: 'absolute', + backgroundColor: 'transparent', borderRadius: SIZE / 2, elevation: 6, - right: 390 / 2 + SIZE / 2, - bottom: 150, - }, - innerContainer: { - position: 'absolute', }, innerWrapper: { - position: 'absolute', flexDirection: 'row', overflow: 'hidden', borderRadius: SIZE / 2, - right: -SIZE, }, touchable: { borderRadius: SIZE / 2, }, - leftInnerWrapper: { - left: 0, - right: undefined, - }, iconWrapper: { alignItems: 'center', justifyContent: 'center', position: 'absolute', height: SIZE, width: SIZE, - right: -SIZE, - }, - moveIconFromLeft: { - left: 0, - right: undefined, }, label: { position: 'absolute', - zIndex: 2, }, uppercaseLabel: { textTransform: 'uppercase', }, + textPlaceholderContainer: { + height: 0, + position: 'absolute', + }, }); export default withTheme(AnimatedFAB); From 554e79a9f66daa491fbe7b9a520708cb6856f6ac Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Mon, 8 Mar 2021 10:20:19 +0100 Subject: [PATCH 06/49] refactor: use one animated value --- src/components/FAB/AnimatedFAB.tsx | 98 +++++++++++------------------- 1 file changed, 35 insertions(+), 63 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 0eed2255c2..3c638544ca 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -85,6 +85,7 @@ type Props = $RemoveChildren & { }; const SIZE = 56; +const SCALE = 0.9; const AnimatedFAB = ({ icon, @@ -105,14 +106,13 @@ const AnimatedFAB = ({ iconMode = 'dynamic', ...rest }: Props) => { + const isIOS = Platform.OS === 'ios'; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) ); - const { current: scaleFAB } = React.useRef(new Animated.Value(1)); - const { current: translateFAB } = React.useRef(new Animated.Value(0)); - const { current: translateIcon } = React.useRef(new Animated.Value(0)); - const { current: opacityLabel } = React.useRef(new Animated.Value(0)); - + const { current: animFAB } = React.useRef( + new Animated.Value(0) + ); const { scale } = theme.animation; const [textWidth, setTextWidth] = React.useState(0); @@ -162,53 +162,20 @@ const AnimatedFAB = ({ const rippleColor = color(foregroundColor).alpha(0.32).rgb().string(); - const totalWidth = animateFromRight + const extendedWidth = textWidth + 1.5 * SIZE; + + const distance = animateFromRight ? -textWidth - SIZE / 2 : textWidth + SIZE / 2; React.useEffect(() => { - const animations = [ - { - property: scaleFAB, - toValue: !extended ? 1 : 0.9, - }, - { - property: translateFAB, - toValue: !extended ? 0 : totalWidth, - }, - { - property: translateIcon, - toValue: !extended ? 0 : totalWidth, - exclude: isIconStatic, - }, - { - property: opacityLabel, - toValue: !extended ? 0 : 1, - }, - ]; - - Animated.parallel( - animations - .filter(({ exclude }) => !exclude) - .map(({ property, toValue }) => { - return Animated.timing(property, { - toValue, - duration: 150 * scale, - useNativeDriver: true, - easing: Easing.linear, - }); - }) - ).start(); - }, [ - extended, - scaleFAB, - opacityLabel, - totalWidth, - translateIcon, - translateFAB, - scale, - isIconStatic, - ]); + Animated.timing(animFAB, { + toValue: !extended ? 0 : distance, + duration: 150 * scale, + useNativeDriver: true, + easing: Easing.linear, + }).start(); + }, [animFAB, scale, distance, extended]); const onTextLayout = ({ nativeEvent }: TextLayoutEvent) => { const currentWidth = Math.ceil(nativeEvent.lines[0].width); @@ -223,13 +190,13 @@ const AnimatedFAB = ({ const getSidesPosition = () => { if (animateFromRight) { return { - left: -totalWidth, + left: -distance, right: undefined, }; } else { return { left: undefined, - right: totalWidth, + right: distance, }; } }; @@ -258,7 +225,10 @@ const AnimatedFAB = ({ { transform: [ { - scaleY: scaleFAB, + scaleY: animFAB.interpolate({ + inputRange: animateFromRight ? [distance, 0] : [0, distance], + outputRange: animateFromRight ? [SCALE, 1] : [1, SCALE], + }), }, ], }, @@ -286,10 +256,10 @@ const AnimatedFAB = ({ { transform: [ { - translateX: translateFAB, + translateX: animFAB, }, ], - width: textWidth + 1.5 * SIZE, + width: extendedWidth, backgroundColor, }, getSidesPosition(), @@ -302,8 +272,8 @@ const AnimatedFAB = ({ - {Platform.OS !== 'ios' && ( + {!isIOS && ( {/* @ts-ignore */} {label} From 0049b6d0f0cd27e77390f2d69758874a4675b47f Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Mon, 8 Mar 2021 15:10:16 +0100 Subject: [PATCH 07/49] feat: configure shadow for Android --- src/components/FAB/AnimatedFAB.tsx | 154 ++++++++++++++++++++--------- 1 file changed, 109 insertions(+), 45 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 3c638544ca..f66298cb45 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -85,6 +85,7 @@ type Props = $RemoveChildren & { }; const SIZE = 56; +const BORDER_RADIUS = SIZE / 2; const SCALE = 0.9; const AnimatedFAB = ({ @@ -106,7 +107,6 @@ const AnimatedFAB = ({ iconMode = 'dynamic', ...rest }: Props) => { - const isIOS = Platform.OS === 'ios'; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) ); @@ -165,8 +165,8 @@ const AnimatedFAB = ({ const extendedWidth = textWidth + 1.5 * SIZE; const distance = animateFromRight - ? -textWidth - SIZE / 2 - : textWidth + SIZE / 2; + ? -textWidth - BORDER_RADIUS + : textWidth + BORDER_RADIUS; React.useEffect(() => { Animated.timing(animFAB, { @@ -213,6 +213,7 @@ const AnimatedFAB = ({ scale: visibility, }, ], + elevation: Platform.OS === 'ios' ? 6 : 0, }, styles.container, disabled && styles.disabled, @@ -235,44 +236,101 @@ const AnimatedFAB = ({ styles.standard, ]} > - - - + + + + + + + + + - + - {!isIOS && ( + {Platform.OS !== 'ios' && ( {/* @ts-ignore */} {label} @@ -338,7 +396,7 @@ const AnimatedFAB = ({ const styles = StyleSheet.create({ standard: { height: SIZE, - borderRadius: SIZE / 2, + borderRadius: BORDER_RADIUS, }, disabled: { elevation: 0, @@ -346,16 +404,22 @@ const styles = StyleSheet.create({ container: { position: 'absolute', backgroundColor: 'transparent', - borderRadius: SIZE / 2, - elevation: 6, + borderRadius: BORDER_RADIUS, }, innerWrapper: { flexDirection: 'row', overflow: 'hidden', - borderRadius: SIZE / 2, + borderRadius: BORDER_RADIUS, + }, + shadowWrapper: { + elevation: 0, + }, + shadow: { + elevation: 6, + borderRadius: BORDER_RADIUS, }, touchable: { - borderRadius: SIZE / 2, + borderRadius: BORDER_RADIUS, }, iconWrapper: { alignItems: 'center', From 396e775c24f73341b123f89dac6be3353e535dde Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Mon, 8 Mar 2021 16:23:01 +0100 Subject: [PATCH 08/49] refactor: introduce isIOS --- src/components/FAB/AnimatedFAB.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index f66298cb45..35667bb22f 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -107,6 +107,9 @@ const AnimatedFAB = ({ iconMode = 'dynamic', ...rest }: Props) => { + const isIOS = Platform.OS === 'ios'; + const animateFromRight = animateFrom === 'right'; + const isIconStatic = iconMode === 'static'; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) ); @@ -118,9 +121,6 @@ const AnimatedFAB = ({ const [textWidth, setTextWidth] = React.useState(0); const [textHeight, setTextHeight] = React.useState(0); - const animateFromRight = animateFrom === 'right'; - const isIconStatic = iconMode === 'static'; - React.useEffect(() => { if (visible) { Animated.timing(visibility, { @@ -213,7 +213,7 @@ const AnimatedFAB = ({ scale: visibility, }, ], - elevation: Platform.OS === 'ios' ? 6 : 0, + elevation: isIOS ? 6 : 0, }, styles.container, disabled && styles.disabled, @@ -344,7 +344,7 @@ const AnimatedFAB = ({ - {Platform.OS !== 'ios' && ( + {!isIOS && ( {/* @ts-ignore */} {label} From fb69d42db2933e2154976ea75efaef929fb3b561 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Fri, 26 Mar 2021 12:00:30 +0100 Subject: [PATCH 09/49] refactor: fixes after merge --- src/components/FAB/AnimatedFAB.tsx | 3 ++- src/index.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 35667bb22f..7e5c99dba8 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -21,6 +21,7 @@ import type { IconSource } from './../Icon'; import type { AccessibilityState, TextLayoutEvent } from 'react-native'; import { white, black } from '../../styles/colors'; import AnimatedText from '../Typography/AnimatedText'; +import getContrastingColor from '../../utils/getContrastingColor'; type Props = $RemoveChildren & { /** @@ -352,7 +353,7 @@ const AnimatedFAB = ({ { right: isIconStatic ? SIZE : BORDER_RADIUS } : { left: isIconStatic ? SIZE : BORDER_RADIUS }, { - width: textWidth, + minWidth: textWidth, top: -BORDER_RADIUS - textHeight / 2, opacity: animFAB.interpolate({ inputRange: animateFromRight diff --git a/src/index.tsx b/src/index.tsx index a8dcc282a7..439d34d8fb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -28,7 +28,7 @@ export { default as Chip } from './components/Chip'; export { default as DataTable } from './components/DataTable/DataTable'; export { default as Dialog } from './components/Dialog/Dialog'; export { default as Divider } from './components/Divider'; -export { default as FAB } from './components/FAB/FAB'; +export { default as FAB } from './components/FAB'; export { default as AnimatedFAB } from './components/FAB/AnimatedFAB'; export { default as HelperText } from './components/HelperText'; export { default as IconButton } from './components/IconButton'; From 06414eea43c6726ab51ffb059419f72f191109c1 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Fri, 26 Mar 2021 16:56:28 +0100 Subject: [PATCH 10/49] refactor: adjust android example --- example/src/Examples/AnimatedFABExample.tsx | 56 +++++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index 0907b59674..c34eadd8e9 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { View, StyleSheet, FlatList } from 'react-native'; +import { View, StyleSheet, FlatList, Animated, Platform } from 'react-native'; import type { NativeSyntheticEvent, NativeScrollEvent } from 'react-native'; import { Colors, @@ -281,6 +281,40 @@ const DATA = [ }, ]; +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 ( + console.log('Pressed')} + visible={visible} + animateFrom="right" + iconMode="dynamic" + style={styles.fabStyle} + /> + ); +}; + const AnimatedFABExample = () => { const { colors: { background }, @@ -290,6 +324,10 @@ const AnimatedFABExample = () => { const [extended, setExtended] = React.useState(false); const [visible, setVisible] = React.useState(true); + const { current: velocity } = React.useRef( + new Animated.Value(0) + ); + const renderItem = ({ item }: { item: Item }) => { return ( @@ -343,6 +381,10 @@ const AnimatedFABExample = () => { const onScroll = ({ nativeEvent, }: NativeSyntheticEvent) => { + if (!isIOS) { + return velocity.setValue(nativeEvent?.velocity?.y ?? 0); + } + const currentScrollPosition = Math.floor(nativeEvent.contentOffset.y); const scrollHeight = @@ -375,16 +417,10 @@ const AnimatedFABExample = () => { ]} onScroll={onScroll} /> - console.log('Pressed')} + ); From 9f77407e0f4ad3502ad1bb5633906c44cb4d9b7e Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Mon, 29 Mar 2021 10:34:49 +0200 Subject: [PATCH 11/49] refactor: small changes --- example/src/ExampleList.tsx | 1 - example/src/Examples/AnimatedFABExample.tsx | 262 +------------------- example/utils/index.ts | 259 +++++++++++++++++++ src/components/FAB/AnimatedFAB.tsx | 42 ++-- 4 files changed, 286 insertions(+), 278 deletions(-) diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index f39c7708e9..cdd17bb63c 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -113,7 +113,6 @@ export default function ExampleList({ navigation }: Props) { paddingLeft: safeArea.left, paddingRight: safeArea.right, }} - style={{ backgroundColor: colors.background }} ItemSeparatorComponent={Divider} renderItem={renderItem} keyExtractor={keyExtractor} diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index c34eadd8e9..bd96aa2813 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -9,6 +9,7 @@ import { Paragraph, } from 'react-native-paper'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; +import { animatedFABExampleData } from '../../utils'; type Item = { id: string; @@ -22,265 +23,6 @@ type Item = { bgColor: string; }; -const DATA = [ - { - id: '1', - sender: 'Hermann, Pfannel & Schumm', - header: - 'Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl. Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', - message: - 'Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\n\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.', - initials: 'H', - date: '29.1.2021', - read: false, - favorite: false, - bgColor: '#ff0173', - }, - { - id: '2', - sender: 'Ziemann, Lockman and Kuvalis', - header: - 'Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam vel augue.', - message: - 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\n\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.', - initials: 'J', - date: '5.9.2020', - read: false, - favorite: false, - bgColor: '#b287a9', - }, - { - id: '3', - sender: 'Daniel, Kuhn and Wolf', - header: - 'Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh. Quisque id justo sit amet sapien dignissim vestibulum.', - message: - 'Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\n\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.', - initials: 'Y', - date: '13.6.2020', - read: true, - favorite: false, - bgColor: '#c1bde9', - }, - { - id: '4', - sender: 'Crona, Lind and Stoltenberg', - header: - 'Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus. Phasellus in felis. Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla.', - message: - 'Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', - initials: 'W', - date: '20.5.2020', - read: false, - favorite: false, - bgColor: '#932a24', - }, - { - id: '5', - sender: 'Bashirian-Hudson', - header: - 'Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue. Etiam justo. Etiam pretium iaculis justo. In hac habitasse platea dictumst.', - message: - 'Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\n\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\n\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', - initials: 'V', - date: '21.9.2020', - read: true, - favorite: false, - bgColor: '#eda5b7', - }, - { - id: '6', - sender: 'Schmitt-Jacobs', - header: - 'Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh. In quis justo.', - message: - 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.', - initials: 'M', - date: '2.6.2020', - read: true, - favorite: true, - bgColor: '#18aaba', - }, - { - id: '7', - sender: 'Graham-Champlin', - header: - 'Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros. Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue.', - message: - 'In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', - initials: 'T', - date: '17.10.2020', - read: false, - favorite: true, - bgColor: '#cc5e54', - }, - { - id: '8', - sender: 'Schoen, Carroll and Herzog', - header: - 'Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', - message: - 'Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\n\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.', - initials: 'F', - date: '31.10.2020', - read: false, - favorite: false, - bgColor: '#28db04', - }, - { - id: '9', - sender: 'Pouros-Fay', - header: - 'Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius. Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.', - message: - 'Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\n\nSed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.', - initials: 'Z', - date: '6.1.2021', - read: true, - favorite: true, - bgColor: '#b6f3fb', - }, - { - id: '10', - sender: 'McKenzie, Ruecker and Bernhard', - header: 'Nunc purus. Phasellus in felis.', - message: - 'In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.', - initials: 'F', - date: '23.2.2021', - read: false, - favorite: false, - bgColor: '#96f066', - }, - { - id: '11', - sender: 'Olson Inc', - header: - 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui. Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.', - message: - 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\n\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\n\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.', - initials: 'V', - date: '17.10.2020', - read: false, - favorite: false, - bgColor: '#f2d49d', - }, - { - id: '12', - sender: 'Walsh LLC', - header: - 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis.', - message: 'Phasellus in felis. Donec semper sapien a libero. Nam dui.', - initials: 'O', - date: '6.1.2021', - read: false, - favorite: true, - bgColor: '#f477dc', - }, - { - id: '13', - sender: 'Lemke, Cremin and Kutch', - header: - 'Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat. Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede. Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus.', - message: - 'In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\n\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\n\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', - initials: 'D', - date: '10.12.2020', - read: false, - favorite: false, - bgColor: '#b2880e', - }, - { - id: '14', - sender: 'Ruecker Group', - header: - 'Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.', - message: - 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', - initials: 'F', - date: '21.8.2020', - read: true, - favorite: false, - bgColor: '#64983a', - }, - { - id: '15', - sender: 'Kovacek, Lockman and Kautzer', - header: - 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus. Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', - message: - 'Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', - initials: 'L', - date: '5.3.2020', - read: true, - favorite: false, - bgColor: '#c7486b', - }, - { - id: '16', - sender: 'Jaskolski-Cummerata', - header: - 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi. Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.', - message: - 'Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\n\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.', - initials: 'C', - date: '21.4.2020', - read: false, - favorite: false, - bgColor: '#2f89a1', - }, - { - id: '17', - sender: 'Wunsch-Walker', - header: 'Donec vitae nisi.', - message: - 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', - initials: 'A', - date: '15.4.2020', - read: false, - favorite: true, - bgColor: '#033cbf', - }, - { - id: '18', - sender: 'Ledner Inc', - header: - 'Curabitur convallis. Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus. Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum.', - message: - 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', - initials: 'Y', - date: '16.5.2020', - read: false, - favorite: true, - bgColor: '#166b59', - }, - { - id: '19', - sender: 'Heaney-Rolfson', - header: 'Nunc rhoncus dui vel sem.', - message: - 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', - initials: 'T', - date: '19.1.2021', - read: true, - favorite: false, - bgColor: '#da0148', - }, - { - id: '20', - sender: 'Dietrich, Beier and Leannon', - header: - 'Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est. Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem. Duis aliquam convallis nunc.', - message: - 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.', - initials: 'Z', - date: '21.5.2020', - read: false, - favorite: false, - bgColor: '#22b33f', - }, -]; - type CustomFABProps = { animatedValue: Animated.Value; visible: boolean; @@ -405,7 +147,7 @@ const AnimatedFABExample = () => { return ( <> item.id} onEndReachedThreshold={0} diff --git a/example/utils/index.ts b/example/utils/index.ts index 3405e8d078..e17215aa58 100644 --- a/example/utils/index.ts +++ b/example/utils/index.ts @@ -47,3 +47,262 @@ export function inputReducer( return { ...state }; } } + +export const animatedFABExampleData = [ + { + id: '1', + sender: 'Hermann, Pfannel & Schumm', + header: + 'Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl. Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', + message: + 'Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\n\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.', + initials: 'H', + date: '29.1.2021', + read: false, + favorite: false, + bgColor: '#ff0173', + }, + { + id: '2', + sender: 'Ziemann, Lockman and Kuvalis', + header: + 'Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam vel augue.', + message: + 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\n\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.', + initials: 'J', + date: '5.9.2020', + read: false, + favorite: false, + bgColor: '#b287a9', + }, + { + id: '3', + sender: 'Daniel, Kuhn and Wolf', + header: + 'Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh. Quisque id justo sit amet sapien dignissim vestibulum.', + message: + 'Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\n\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.', + initials: 'Y', + date: '13.6.2020', + read: true, + favorite: false, + bgColor: '#c1bde9', + }, + { + id: '4', + sender: 'Crona, Lind and Stoltenberg', + header: + 'Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus. Phasellus in felis. Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla.', + message: + 'Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', + initials: 'W', + date: '20.5.2020', + read: false, + favorite: false, + bgColor: '#932a24', + }, + { + id: '5', + sender: 'Bashirian-Hudson', + header: + 'Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue. Etiam justo. Etiam pretium iaculis justo. In hac habitasse platea dictumst.', + message: + 'Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\n\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\n\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + initials: 'V', + date: '21.9.2020', + read: true, + favorite: false, + bgColor: '#eda5b7', + }, + { + id: '6', + sender: 'Schmitt-Jacobs', + header: + 'Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh. In quis justo.', + message: + 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.', + initials: 'M', + date: '2.6.2020', + read: true, + favorite: true, + bgColor: '#18aaba', + }, + { + id: '7', + sender: 'Graham-Champlin', + header: + 'Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros. Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue.', + message: + 'In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', + initials: 'T', + date: '17.10.2020', + read: false, + favorite: true, + bgColor: '#cc5e54', + }, + { + id: '8', + sender: 'Schoen, Carroll and Herzog', + header: + 'Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', + message: + 'Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\n\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.', + initials: 'F', + date: '31.10.2020', + read: false, + favorite: false, + bgColor: '#28db04', + }, + { + id: '9', + sender: 'Pouros-Fay', + header: + 'Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius. Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.', + message: + 'Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\n\nSed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.', + initials: 'Z', + date: '6.1.2021', + read: true, + favorite: true, + bgColor: '#b6f3fb', + }, + { + id: '10', + sender: 'McKenzie, Ruecker and Bernhard', + header: 'Nunc purus. Phasellus in felis.', + message: + 'In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.', + initials: 'F', + date: '23.2.2021', + read: false, + favorite: false, + bgColor: '#96f066', + }, + { + id: '11', + sender: 'Olson Inc', + header: + 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui. Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.', + message: + 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\n\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\n\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.', + initials: 'V', + date: '17.10.2020', + read: false, + favorite: false, + bgColor: '#f2d49d', + }, + { + id: '12', + sender: 'Walsh LLC', + header: + 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis.', + message: 'Phasellus in felis. Donec semper sapien a libero. Nam dui.', + initials: 'O', + date: '6.1.2021', + read: false, + favorite: true, + bgColor: '#f477dc', + }, + { + id: '13', + sender: 'Lemke, Cremin and Kutch', + header: + 'Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat. Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede. Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus.', + message: + 'In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\n\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\n\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', + initials: 'D', + date: '10.12.2020', + read: false, + favorite: false, + bgColor: '#b2880e', + }, + { + id: '14', + sender: 'Ruecker Group', + header: + 'Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.', + message: + 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', + initials: 'F', + date: '21.8.2020', + read: true, + favorite: false, + bgColor: '#64983a', + }, + { + id: '15', + sender: 'Kovacek, Lockman and Kautzer', + header: + 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus. Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + message: + 'Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', + initials: 'L', + date: '5.3.2020', + read: true, + favorite: false, + bgColor: '#c7486b', + }, + { + id: '16', + sender: 'Jaskolski-Cummerata', + header: + 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi. Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.', + message: + 'Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\n\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.', + initials: 'C', + date: '21.4.2020', + read: false, + favorite: false, + bgColor: '#2f89a1', + }, + { + id: '17', + sender: 'Wunsch-Walker', + header: 'Donec vitae nisi.', + message: + 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', + initials: 'A', + date: '15.4.2020', + read: false, + favorite: true, + bgColor: '#033cbf', + }, + { + id: '18', + sender: 'Ledner Inc', + header: + 'Curabitur convallis. Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus. Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum.', + message: + 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', + initials: 'Y', + date: '16.5.2020', + read: false, + favorite: true, + bgColor: '#166b59', + }, + { + id: '19', + sender: 'Heaney-Rolfson', + header: 'Nunc rhoncus dui vel sem.', + message: + 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', + initials: 'T', + date: '19.1.2021', + read: true, + favorite: false, + bgColor: '#da0148', + }, + { + id: '20', + sender: 'Dietrich, Beier and Leannon', + header: + 'Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est. Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem. Duis aliquam convallis nunc.', + message: + 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.', + initials: 'Z', + date: '21.5.2020', + read: false, + favorite: false, + bgColor: '#22b33f', + }, +]; diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 7e5c99dba8..26df7a8afc 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -17,8 +17,11 @@ import TouchableRipple from '../TouchableRipple/TouchableRipple'; import { withTheme } from '../../core/theming'; import type { $RemoveChildren } from '../../types'; import type { IconSource } from './../Icon'; -//@ts-ignore -import type { AccessibilityState, TextLayoutEvent } from 'react-native'; +import type { + AccessibilityState, + NativeSyntheticEvent, + TextLayoutEventData, +} from 'react-native'; import { white, black } from '../../styles/colors'; import AnimatedText from '../Typography/AnimatedText'; import getContrastingColor from '../../utils/getContrastingColor'; @@ -109,7 +112,7 @@ const AnimatedFAB = ({ ...rest }: Props) => { const isIOS = Platform.OS === 'ios'; - const animateFromRight = animateFrom === 'right'; + const isAnimatedFromRight = animateFrom === 'right'; const isIconStatic = iconMode === 'static'; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) @@ -165,7 +168,7 @@ const AnimatedFAB = ({ const extendedWidth = textWidth + 1.5 * SIZE; - const distance = animateFromRight + const distance = isAnimatedFromRight ? -textWidth - BORDER_RADIUS : textWidth + BORDER_RADIUS; @@ -178,7 +181,9 @@ const AnimatedFAB = ({ }).start(); }, [animFAB, scale, distance, extended]); - const onTextLayout = ({ nativeEvent }: TextLayoutEvent) => { + const onTextLayout = ({ + nativeEvent, + }: NativeSyntheticEvent) => { const currentWidth = Math.ceil(nativeEvent.lines[0].width); const currentHeight = Math.ceil(nativeEvent.lines[0].height); @@ -189,7 +194,7 @@ const AnimatedFAB = ({ }; const getSidesPosition = () => { - if (animateFromRight) { + if (isAnimatedFromRight) { return { left: -distance, right: undefined, @@ -228,8 +233,10 @@ const AnimatedFAB = ({ transform: [ { scaleY: animFAB.interpolate({ - inputRange: animateFromRight ? [distance, 0] : [0, distance], - outputRange: animateFromRight ? [SCALE, 1] : [1, SCALE], + inputRange: isAnimatedFromRight + ? [distance, 0] + : [0, distance], + outputRange: isAnimatedFromRight ? [SCALE, 1] : [1, SCALE], }), }, ], @@ -344,30 +351,28 @@ const AnimatedFAB = ({ {!isIOS && ( + // Method `onTextLayout` on Android returns sizes of text visible on the screen, + // however during render the text in `FAB` isn't fully visible. In order to get + // proper text measurements there is a need to additionaly render that text, but + // wrapped in absolutely positioned `ScrollView` which height is 0. - {/* @ts-ignore */} {label} )} From 4e99ffdbe1c27edce1225e988399fbf156606a42 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Mon, 20 Sep 2021 15:13:52 +0200 Subject: [PATCH 12/49] fix: add support for animateFor: left property --- src/components/FAB/AnimatedFAB.tsx | 57 ++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 26df7a8afc..c91fe2ef8b 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -201,8 +201,8 @@ const AnimatedFAB = ({ }; } else { return { - left: undefined, - right: distance, + left: distance, + right: undefined, }; } }; @@ -253,8 +253,12 @@ const AnimatedFAB = ({ { width: extendedWidth, opacity: animFAB.interpolate({ - inputRange: [distance, 0.9 * distance, 0], - outputRange: [1, 0.15, 0], + inputRange: isAnimatedFromRight + ? [distance, 0.9 * distance, 0] + : [0, 0.9 * distance, distance], + outputRange: isAnimatedFromRight + ? [1, 0.15, 0] + : [0, 0.15, 1], }), }, ]} @@ -266,25 +270,41 @@ const AnimatedFAB = ({ styles.shadow, { opacity: animFAB.interpolate({ - inputRange: [distance, 0.9 * distance, 0], - outputRange: [0, 0.85, 1], + inputRange: isAnimatedFromRight + ? [distance, 0.9 * distance, 0] + : [0, 0.9 * distance, distance], + outputRange: isAnimatedFromRight + ? [0, 0.85, 1] + : [1, 0.85, 0], }), width: SIZE, borderRadius: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [SIZE / (extendedWidth / SIZE), BORDER_RADIUS], + inputRange: isAnimatedFromRight + ? [distance, 0] + : [0, distance], + outputRange: isAnimatedFromRight + ? [SIZE / (extendedWidth / SIZE), BORDER_RADIUS] + : [BORDER_RADIUS, SIZE / (extendedWidth / SIZE)], }), transform: [ { translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [Math.abs(distance) / 2, Math.abs(distance)], + inputRange: isAnimatedFromRight + ? [distance, 0] + : [0, distance], + outputRange: isAnimatedFromRight + ? [Math.abs(distance) / 2, Math.abs(distance)] + : [Math.abs(distance), Math.abs(distance) / 2], }), }, { scaleX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [extendedWidth / SIZE, 1], + inputRange: isAnimatedFromRight + ? [distance, 0] + : [0, distance], + outputRange: isAnimatedFromRight + ? [extendedWidth / SIZE, 1] + : [1, extendedWidth / SIZE], }), }, ], @@ -299,7 +319,14 @@ const AnimatedFAB = ({ { transform: [ { - translateX: animFAB, + translateX: animFAB.interpolate({ + inputRange: isAnimatedFromRight + ? [distance, 0] + : [0, distance], + outputRange: isAnimatedFromRight + ? [distance, 0] + : [0, -distance], + }), }, ], width: extendedWidth, @@ -339,7 +366,7 @@ const AnimatedFAB = ({ style={[ styles.iconWrapper, { - transform: [{ translateX: animFAB }], + transform: [{ translateX: isAnimatedFromRight ? animFAB : 0 }], }, getSidesPosition(), ]} @@ -372,7 +399,7 @@ const AnimatedFAB = ({ inputRange: isAnimatedFromRight ? [distance, 0] : [0, distance], - outputRange: isAnimatedFromRight ? [0, SIZE] : [-SIZE, 0], + outputRange: isAnimatedFromRight ? [0, SIZE] : [SIZE, 0], }), }, ], From 7adecfca8de1400bf519f8f352086f5e9bc59152 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Mon, 20 Sep 2021 16:05:20 +0200 Subject: [PATCH 13/49] test: add snapshot tests for AnimatedFAB component --- src/components/__tests__/AnimatedFAB.test.js | 11 + .../__snapshots__/AnimatedFAB.test.js.snap | 254 ++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 src/components/__tests__/AnimatedFAB.test.js create mode 100644 src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap diff --git a/src/components/__tests__/AnimatedFAB.test.js b/src/components/__tests__/AnimatedFAB.test.js new file mode 100644 index 0000000000..2afb1b7f15 --- /dev/null +++ b/src/components/__tests__/AnimatedFAB.test.js @@ -0,0 +1,11 @@ +import * as React from 'react'; +import renderer from 'react-test-renderer'; +import AnimatedFAB from '../FAB/AnimatedFAB'; + +it('renders animated fab', () => { + const tree = renderer + .create( {}} icon="plus" />) + .toJSON(); + + expect(tree).toMatchSnapshot(); +}); diff --git a/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap new file mode 100644 index 0000000000..a6a572deaa --- /dev/null +++ b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap @@ -0,0 +1,254 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders animated fab 1`] = ` + + + + + + + + + + + + + + + + +  + + + + + + +`; From 20b59136dc58c4325d44d8d8737c505760c747ee Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Tue, 21 Sep 2021 12:00:05 +0200 Subject: [PATCH 14/49] test: add snapshot tests for new properties --- src/components/__tests__/AnimatedFAB.test.js | 26 + .../__snapshots__/AnimatedFAB.test.js.snap | 512 ++++++++++++++++++ 2 files changed, 538 insertions(+) diff --git a/src/components/__tests__/AnimatedFAB.test.js b/src/components/__tests__/AnimatedFAB.test.js index 2afb1b7f15..9dd39ce01a 100644 --- a/src/components/__tests__/AnimatedFAB.test.js +++ b/src/components/__tests__/AnimatedFAB.test.js @@ -9,3 +9,29 @@ it('renders animated fab', () => { expect(tree).toMatchSnapshot(); }); + +it('renders animated fab with label on the right by default', () => { + const tree = renderer + .create( + {}} icon="plus" /> + ) + .toJSON(); + + expect(tree).toMatchSnapshot(); +}); + +it('renders animated fab with label on the left', () => { + const tree = renderer + .create( + {}} + icon="plus" + /> + ) + .toJSON(); + + expect(tree).toMatchSnapshot(); +}); diff --git a/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap index a6a572deaa..70f726f067 100644 --- a/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap +++ b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap @@ -252,3 +252,515 @@ exports[`renders animated fab 1`] = ` `; + +exports[`renders animated fab with label on the left 1`] = ` + + + + + + + + + + + + + + + + +  + + + + + text + + + +`; + +exports[`renders animated fab with label on the right by default 1`] = ` + + + + + + + + + + + + + + + + +  + + + + + text + + + +`; From c84999c6d7ab107181023771e4e58f92f8ff18d6 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Tue, 21 Sep 2021 12:43:35 +0200 Subject: [PATCH 15/49] feat: tweak example to match MD docs --- example/src/Examples/AnimatedFABExample.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index bd96aa2813..1383326ce1 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -36,9 +36,9 @@ const CustomFAB = ({ animatedValue, visible, extended }: CustomFABProps) => { React.useEffect(() => { if (!isIOS) { - animatedValue.addListener(({ value }: { value: number }) => - setIsExtended(value > 0 ? true : false) - ); + animatedValue.addListener(({ value }: { value: number }) => { + setIsExtended(value <= 0); + }); } else setIsExtended(extended); }, [animatedValue, extended]); @@ -124,7 +124,7 @@ const AnimatedFABExample = () => { nativeEvent, }: NativeSyntheticEvent) => { if (!isIOS) { - return velocity.setValue(nativeEvent?.velocity?.y ?? 0); + return velocity.setValue(nativeEvent?.contentOffset?.y ?? 0); } const currentScrollPosition = Math.floor(nativeEvent.contentOffset.y); From 7c7cc8281ec6d2e4b8ff49641f7e7a79cc229a5c Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Tue, 21 Sep 2021 12:53:26 +0200 Subject: [PATCH 16/49] feat: improve AnimatedFAB example for iOS --- example/src/Examples/AnimatedFABExample.tsx | 22 +++++---------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index 1383326ce1..548e4ce4e4 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -62,7 +62,6 @@ const AnimatedFABExample = () => { colors: { background }, } = useTheme(); - const [scrollPosition, setScrollPosition] = React.useState(0); const [extended, setExtended] = React.useState(false); const [visible, setVisible] = React.useState(true); @@ -123,25 +122,14 @@ const AnimatedFABExample = () => { const onScroll = ({ nativeEvent, }: NativeSyntheticEvent) => { + const currentScrollPosition = + Math.floor(nativeEvent?.contentOffset?.y) ?? 0; + if (!isIOS) { - return velocity.setValue(nativeEvent?.contentOffset?.y ?? 0); + return velocity.setValue(currentScrollPosition); } - const currentScrollPosition = Math.floor(nativeEvent.contentOffset.y); - - const scrollHeight = - nativeEvent.contentSize.height - nativeEvent.layoutMeasurement.height; - - setScrollPosition(currentScrollPosition); - - if (currentScrollPosition > scrollPosition && scrollPosition > 0) { - setExtended(false); - } else if ( - currentScrollPosition < scrollPosition && - scrollPosition < scrollHeight - ) { - setExtended(true); - } + setExtended(currentScrollPosition <= 0); }; return ( From c0de872bc549928ad343a7977cb639e3859a63e5 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Tue, 21 Sep 2021 14:33:02 +0200 Subject: [PATCH 17/49] feat: improve AnimateFAB visuals --- example/src/Examples/AnimatedFABExample.tsx | 6 +++--- src/components/FAB/AnimatedFAB.tsx | 2 +- .../__tests__/__snapshots__/AnimatedFAB.test.js.snap | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index 548e4ce4e4..72bd95e44d 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -44,10 +44,10 @@ const CustomFAB = ({ animatedValue, visible, extended }: CustomFABProps) => { return ( console.log('Pressed')} visible={visible} animateFrom="right" @@ -203,7 +203,7 @@ const styles = StyleSheet.create({ }, fabStyle: { right: 16, - bottom: 32, + bottom: 16, position: 'absolute', }, }); diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index c91fe2ef8b..efe95f6cc9 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -372,7 +372,7 @@ const AnimatedFAB = ({ ]} pointerEvents="none" > - + diff --git a/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap index 70f726f067..45adcde47f 100644 --- a/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap +++ b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap @@ -193,7 +193,7 @@ exports[`renders animated fab 1`] = ` style={ Array [ Object { - "color": "#ffffff", + "color": "rgba(0, 0, 0, .54)", "fontSize": 24, }, Array [ @@ -447,7 +447,7 @@ exports[`renders animated fab with label on the left 1`] = ` style={ Array [ Object { - "color": "#ffffff", + "color": "rgba(0, 0, 0, .54)", "fontSize": 24, }, Array [ @@ -703,7 +703,7 @@ exports[`renders animated fab with label on the right by default 1`] = ` style={ Array [ Object { - "color": "#ffffff", + "color": "rgba(0, 0, 0, .54)", "fontSize": 24, }, Array [ From 684c918bb9a3695e6535a0698dfc213c4aba74c2 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Thu, 23 Sep 2021 15:10:39 +0200 Subject: [PATCH 18/49] feat: improve example to showcase animateFrom prop --- example/src/Examples/AnimatedFABExample.tsx | 40 ++++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index 72bd95e44d..a91b39f031 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -1,5 +1,13 @@ import * as React from 'react'; -import { View, StyleSheet, FlatList, Animated, Platform } from 'react-native'; +import { + View, + StyleSheet, + FlatList, + Animated, + Platform, + StyleProp, + ViewStyle, +} from 'react-native'; import type { NativeSyntheticEvent, NativeScrollEvent } from 'react-native'; import { Colors, @@ -27,11 +35,21 @@ type CustomFABProps = { animatedValue: Animated.Value; visible: boolean; extended: boolean; + label: string; + animateFrom: 'left' | 'right'; + style?: StyleProp; }; const isIOS = Platform.OS === 'ios'; -const CustomFAB = ({ animatedValue, visible, extended }: CustomFABProps) => { +const CustomFAB = ({ + animatedValue, + visible, + extended, + label, + animateFrom, + style, +}: CustomFABProps) => { const [isExtended, setIsExtended] = React.useState(true); React.useEffect(() => { @@ -45,14 +63,14 @@ const CustomFAB = ({ animatedValue, visible, extended }: CustomFABProps) => { return ( console.log('Pressed')} visible={visible} - animateFrom="right" + animateFrom={animateFrom} iconMode="dynamic" - style={styles.fabStyle} + style={[styles.fabStyle, style]} /> ); }; @@ -147,10 +165,22 @@ const AnimatedFABExample = () => { ]} onScroll={onScroll} /> + + + ); From 3221e19cd8d638467b68d00463a9acce06c79cb7 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Thu, 23 Sep 2021 15:12:55 +0200 Subject: [PATCH 19/49] feat: add support for RTL and fix animateFrom prop --- src/components/FAB/AnimatedFAB.tsx | 54 ++++++++++++++----- .../__snapshots__/AnimatedFAB.test.js.snap | 8 +-- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index efe95f6cc9..dcc42deb8d 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -10,6 +10,7 @@ import { ScrollView, Text, Platform, + I18nManager, } from 'react-native'; import Surface from '../Surface'; import Icon from '../Icon'; @@ -24,7 +25,6 @@ import type { } from 'react-native'; import { white, black } from '../../styles/colors'; import AnimatedText from '../Typography/AnimatedText'; -import getContrastingColor from '../../utils/getContrastingColor'; type Props = $RemoveChildren & { /** @@ -114,6 +114,7 @@ const AnimatedFAB = ({ const isIOS = Platform.OS === 'ios'; const isAnimatedFromRight = animateFrom === 'right'; const isIconStatic = iconMode === 'static'; + const { isRTL } = I18nManager; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) ); @@ -125,6 +126,8 @@ const AnimatedFAB = ({ const [textWidth, setTextWidth] = React.useState(0); const [textHeight, setTextHeight] = React.useState(0); + console.table([{ isAnimatedFromRight, isRTL, isIconStatic }]); + React.useEffect(() => { if (visible) { Animated.timing(visibility, { @@ -147,7 +150,7 @@ const AnimatedFAB = ({ .string(); const { backgroundColor = disabled ? disabledColor : theme.colors.accent } = - StyleSheet.flatten(style) || {}; + StyleSheet.flatten(style) || {}; let foregroundColor; @@ -159,7 +162,7 @@ const AnimatedFAB = ({ .rgb() .string(); } else { - foregroundColor = !color(backgroundColor).isLight() + foregroundColor = !color(backgroundColor as string).isLight() ? white : 'rgba(0, 0, 0, .54)'; } @@ -201,7 +204,7 @@ const AnimatedFAB = ({ }; } else { return { - left: distance, + left: -distance, right: undefined, }; } @@ -293,8 +296,12 @@ const AnimatedFAB = ({ ? [distance, 0] : [0, distance], outputRange: isAnimatedFromRight - ? [Math.abs(distance) / 2, Math.abs(distance)] - : [Math.abs(distance), Math.abs(distance) / 2], + ? isRTL + ? [0, distance] + : [Math.abs(distance) / 2, Math.abs(distance)] + : isRTL + ? [0, -distance] + : [0, Math.abs(distance) / 2], }), }, { @@ -324,8 +331,12 @@ const AnimatedFAB = ({ ? [distance, 0] : [0, distance], outputRange: isAnimatedFromRight - ? [distance, 0] - : [0, -distance], + ? isRTL + ? [-distance, 0] + : [distance, 0] + : isRTL + ? [0, -distance] + : [0, distance], }), }, ], @@ -342,6 +353,7 @@ const AnimatedFAB = ({ rippleColor={rippleColor} disabled={disabled} accessibilityLabel={accessibilityLabel} + // @ts-expect-error We keep old a11y props for backwards compat with old RN versions accessibilityTraits={disabled ? ['button', 'disabled'] : 'button'} accessibilityComponentType="button" accessibilityRole="button" @@ -366,7 +378,23 @@ const AnimatedFAB = ({ style={[ styles.iconWrapper, { - transform: [{ translateX: isAnimatedFromRight ? animFAB : 0 }], + transform: [ + { + translateX: isAnimatedFromRight + ? isRTL + ? animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [-distance, 0], + }) + : animFAB + : isRTL + ? -distance + : animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [distance, distance * 2], + }), + }, + ], }, getSidesPosition(), ]} @@ -381,9 +409,11 @@ const AnimatedFAB = ({ onTextLayout={isIOS ? onTextLayout : undefined} ellipsizeMode={'tail'} style={[ - isAnimatedFromRight - ? { right: isIconStatic ? SIZE : BORDER_RADIUS } - : { left: isIconStatic ? SIZE : BORDER_RADIUS }, + { + [isAnimatedFromRight || isRTL ? 'right' : 'left']: isIconStatic + ? SIZE + : BORDER_RADIUS, + }, { minWidth: textWidth, top: -BORDER_RADIUS - textHeight / 2, diff --git a/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap index 45adcde47f..05659c1250 100644 --- a/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap +++ b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap @@ -336,7 +336,7 @@ exports[`renders animated fab with label on the left 1`] = ` "top": 0, "transform": Array [ Object { - "translateX": 28, + "translateX": 0, }, Object { "scaleX": 1, @@ -363,7 +363,7 @@ exports[`renders animated fab with label on the left 1`] = ` "backgroundColor": "#03dac4", "borderRadius": 28, "height": 56, - "left": 28, + "left": -28, "right": undefined, "transform": Array [ Object { @@ -426,12 +426,12 @@ exports[`renders animated fab with label on the left 1`] = ` "alignItems": "center", "height": 56, "justifyContent": "center", - "left": 28, + "left": -28, "position": "absolute", "right": undefined, "transform": Array [ Object { - "translateX": 0, + "translateX": 28, }, ], "width": 56, From 77db8d4cde620fd2f65f4a205913f406eb14fc05 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Thu, 23 Sep 2021 15:22:52 +0200 Subject: [PATCH 20/49] fix: align iOS behavior for AnimatedFAB example --- example/src/Examples/AnimatedFABExample.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index a91b39f031..2f382fe7ea 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -80,7 +80,7 @@ const AnimatedFABExample = () => { colors: { background }, } = useTheme(); - const [extended, setExtended] = React.useState(false); + const [extended, setExtended] = React.useState(true); const [visible, setVisible] = React.useState(true); const { current: velocity } = React.useRef( From 20e14817eef2aa3c83440de1d3bfa8249198a438 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Thu, 23 Sep 2021 15:55:04 +0200 Subject: [PATCH 21/49] feat: improve AnimatedFAB example --- example/src/Examples/AnimatedFABExample.tsx | 26 +++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index 2f382fe7ea..74fd1dfb50 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -37,6 +37,7 @@ type CustomFABProps = { extended: boolean; label: string; animateFrom: 'left' | 'right'; + iconMode?: 'static' | 'dynamic'; style?: StyleProp; }; @@ -49,6 +50,7 @@ const CustomFAB = ({ label, animateFrom, style, + iconMode, }: CustomFABProps) => { const [isExtended, setIsExtended] = React.useState(true); @@ -69,7 +71,7 @@ const CustomFAB = ({ onPress={() => console.log('Pressed')} visible={visible} animateFrom={animateFrom} - iconMode="dynamic" + iconMode={iconMode} style={[styles.fabStyle, style]} /> ); @@ -166,13 +168,33 @@ const AnimatedFABExample = () => { onScroll={onScroll} /> + + + + Date: Thu, 23 Sep 2021 16:16:23 +0200 Subject: [PATCH 22/49] feat: implement consistent styles with a dirty approach --- src/components/FAB/AnimatedFAB.tsx | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index dcc42deb8d..751da6650b 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -126,8 +126,6 @@ const AnimatedFAB = ({ const [textWidth, setTextWidth] = React.useState(0); const [textHeight, setTextHeight] = React.useState(0); - console.table([{ isAnimatedFromRight, isRTL, isIconStatic }]); - React.useEffect(() => { if (visible) { Animated.timing(visibility, { @@ -191,8 +189,13 @@ const AnimatedFAB = ({ const currentHeight = Math.ceil(nativeEvent.lines[0].height); if (currentWidth !== textWidth || currentHeight !== textHeight) { - setTextWidth(currentWidth); setTextHeight(currentHeight); + + if (isIOS) { + return setTextWidth(currentWidth - 12); + } + + setTextWidth(currentWidth); } }; @@ -380,7 +383,16 @@ const AnimatedFAB = ({ { transform: [ { - translateX: isAnimatedFromRight + translateX: isIconStatic + ? isAnimatedFromRight + ? 0 + : isRTL + ? animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [-distance, -distance * 2], + }) + : distance + : isAnimatedFromRight ? isRTL ? animFAB.interpolate({ inputRange: [distance, 0], @@ -411,7 +423,9 @@ const AnimatedFAB = ({ style={[ { [isAnimatedFromRight || isRTL ? 'right' : 'left']: isIconStatic - ? SIZE + ? isIOS + ? SIZE - 10 + : SIZE - 12 : BORDER_RADIUS, }, { From d2417837f547b8e85fc993eb6466be3ef194fa8b Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Mon, 27 Sep 2021 11:40:43 +0200 Subject: [PATCH 23/49] chore: refactor innerWrapper styles --- src/components/FAB/AnimatedFAB.tsx | 92 ++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 751da6650b..d412c8f680 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -15,9 +15,9 @@ import { import Surface from '../Surface'; import Icon from '../Icon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; -import { withTheme } from '../../core/theming'; import type { $RemoveChildren } from '../../types'; import type { IconSource } from './../Icon'; +import { withTheme } from '../../core/theming'; import type { AccessibilityState, NativeSyntheticEvent, @@ -150,7 +150,7 @@ const AnimatedFAB = ({ const { backgroundColor = disabled ? disabledColor : theme.colors.accent } = StyleSheet.flatten(style) || {}; - let foregroundColor; + let foregroundColor: string; if (typeof customColor !== 'undefined') { foregroundColor = customColor; @@ -213,6 +213,77 @@ const AnimatedFAB = ({ } }; + type CombinedStyles = { + innerWrapper?: Animated.WithAnimatedValue; + }; + + const getCombinedStyles = (): CombinedStyles => { + const combinedStyles: CombinedStyles = { + innerWrapper: {}, + }; + + const animatedFromRight = isAnimatedFromRight && !isRTL; + const animatedFromRightRTL = isAnimatedFromRight && isRTL; + const animatedFromLeft = !isAnimatedFromRight && !isRTL; + const animatedFromLeftRTL = !isAnimatedFromRight && isRTL; + + if (animatedFromRight) { + combinedStyles.innerWrapper = { + transform: [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [distance, 0], + }), + }, + ], + }; + } + + if (animatedFromRightRTL) { + combinedStyles.innerWrapper = { + transform: [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [-distance, 0], + }), + }, + ], + }; + } + + if (animatedFromLeft) { + combinedStyles.innerWrapper = { + transform: [ + { + translateX: animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [0, distance], + }), + }, + ], + }; + } + + if (animatedFromLeftRTL) { + combinedStyles.innerWrapper = { + transform: [ + { + translateX: animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [0, -distance], + }), + }, + ], + }; + } + + return combinedStyles; + }; + + const combinedStyles = getCombinedStyles(); + return ( From f2030fc644e1edd73471719d7ca6ff0022b7a5aa Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Mon, 27 Sep 2021 12:43:49 +0200 Subject: [PATCH 24/49] chore: refactor icon styles --- src/components/FAB/AnimatedFAB.tsx | 167 ++++++++++++++--------------- 1 file changed, 78 insertions(+), 89 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index d412c8f680..907698ba1b 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -199,27 +199,21 @@ const AnimatedFAB = ({ } }; - const getSidesPosition = () => { - if (isAnimatedFromRight) { - return { - left: -distance, - right: undefined, - }; - } else { - return { - left: -distance, - right: undefined, - }; - } - }; - type CombinedStyles = { - innerWrapper?: Animated.WithAnimatedValue; + innerWrapper: Animated.WithAnimatedValue; + iconWrapper: Animated.WithAnimatedValue; }; const getCombinedStyles = (): CombinedStyles => { + const defaultPositionStyles = { left: -distance, right: undefined }; + const combinedStyles: CombinedStyles = { - innerWrapper: {}, + innerWrapper: { + ...defaultPositionStyles, + }, + iconWrapper: { + ...defaultPositionStyles, + }, }; const animatedFromRight = isAnimatedFromRight && !isRTL; @@ -228,55 +222,82 @@ const AnimatedFAB = ({ const animatedFromLeftRTL = !isAnimatedFromRight && isRTL; if (animatedFromRight) { - combinedStyles.innerWrapper = { - transform: [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [distance, 0], - }), - }, - ], - }; + combinedStyles.iconWrapper.transform = [ + { + translateX: isIconStatic ? 0 : animFAB, + }, + ]; + combinedStyles.innerWrapper.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [distance, 0], + }), + }, + ]; } if (animatedFromRightRTL) { - combinedStyles.innerWrapper = { - transform: [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [-distance, 0], - }), - }, - ], - }; + combinedStyles.iconWrapper.transform = [ + { + translateX: isIconStatic + ? 0 + : animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [-distance, 0], + }), + }, + ]; + combinedStyles.innerWrapper.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [-distance, 0], + }), + }, + ]; } if (animatedFromLeft) { - combinedStyles.innerWrapper = { - transform: [ - { - translateX: animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [0, distance], - }), - }, - ], - }; + combinedStyles.iconWrapper.transform = [ + { + translateX: isIconStatic + ? distance + : animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [distance, distance * 2], + }), + }, + ]; + combinedStyles.innerWrapper.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [0, distance], + }), + }, + ]; } if (animatedFromLeftRTL) { - combinedStyles.innerWrapper = { - transform: [ - { - translateX: animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [0, -distance], - }), - }, - ], - }; + combinedStyles.iconWrapper.transform = [ + { + translateX: isIconStatic + ? animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [-distance, -distance * 2], + }) + : -distance, + }, + ]; + combinedStyles.innerWrapper.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [0, -distance], + }), + }, + ]; } return combinedStyles; @@ -402,7 +423,6 @@ const AnimatedFAB = ({ backgroundColor, }, combinedStyles.innerWrapper, - getSidesPosition(), ]} > From 3ebb9e6e342ecf75d637eaee79cfdb8e893a8b17 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Mon, 27 Sep 2021 13:00:57 +0200 Subject: [PATCH 25/49] chore: refactor reversed properties for direction --- src/components/FAB/AnimatedFAB.tsx | 58 +++++++++++++----------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 907698ba1b..1384290a9b 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -271,10 +271,7 @@ const AnimatedFAB = ({ ]; combinedStyles.innerWrapper.transform = [ { - translateX: animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [0, distance], - }), + translateX: animFAB, }, ]; } @@ -305,6 +302,14 @@ const AnimatedFAB = ({ const combinedStyles = getCombinedStyles(); + const propForDirection = (right: T[]): T[] => { + if (isAnimatedFromRight) { + return right; + } + + return right.reverse(); + }; + return ( Date: Mon, 27 Sep 2021 13:31:51 +0200 Subject: [PATCH 26/49] chore: refactor remaining transforms --- src/components/FAB/AnimatedFAB.tsx | 77 ++++++++++++++++++------------ 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 1384290a9b..33cabd1671 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -199,9 +199,18 @@ const AnimatedFAB = ({ } }; + const propForDirection = (right: T[]): T[] => { + if (isAnimatedFromRight) { + return right; + } + + return right.reverse(); + }; + type CombinedStyles = { innerWrapper: Animated.WithAnimatedValue; iconWrapper: Animated.WithAnimatedValue; + absoluteFill: Animated.WithAnimatedValue; }; const getCombinedStyles = (): CombinedStyles => { @@ -214,6 +223,7 @@ const AnimatedFAB = ({ iconWrapper: { ...defaultPositionStyles, }, + absoluteFill: {}, }; const animatedFromRight = isAnimatedFromRight && !isRTL; @@ -222,16 +232,24 @@ const AnimatedFAB = ({ const animatedFromLeftRTL = !isAnimatedFromRight && isRTL; if (animatedFromRight) { + combinedStyles.innerWrapper.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [distance, 0], + }), + }, + ]; combinedStyles.iconWrapper.transform = [ { translateX: isIconStatic ? 0 : animFAB, }, ]; - combinedStyles.innerWrapper.transform = [ + combinedStyles.absoluteFill.transform = [ { translateX: animFAB.interpolate({ inputRange: [distance, 0], - outputRange: [distance, 0], + outputRange: [Math.abs(distance) / 2, Math.abs(distance)], }), }, ]; @@ -256,6 +274,14 @@ const AnimatedFAB = ({ }), }, ]; + combinedStyles.absoluteFill.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [0, distance], + }), + }, + ]; } if (animatedFromLeft) { @@ -274,6 +300,14 @@ const AnimatedFAB = ({ translateX: animFAB, }, ]; + combinedStyles.absoluteFill.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [0, Math.abs(distance) / 2], + }), + }, + ]; } if (animatedFromLeftRTL) { @@ -295,6 +329,14 @@ const AnimatedFAB = ({ }), }, ]; + combinedStyles.absoluteFill.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [0, -distance], + }), + }, + ]; } return combinedStyles; @@ -302,14 +344,6 @@ const AnimatedFAB = ({ const combinedStyles = getCombinedStyles(); - const propForDirection = (right: T[]): T[] => { - if (isAnimatedFromRight) { - return right; - } - - return right.reverse(); - }; - return ( Date: Mon, 27 Sep 2021 13:59:56 +0200 Subject: [PATCH 27/49] fix: improve shadow behavior --- src/components/FAB/AnimatedFAB.tsx | 10 +--------- .../__tests__/__snapshots__/AnimatedFAB.test.js.snap | 9 --------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 33cabd1671..559cc85dd9 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -412,16 +412,8 @@ const AnimatedFAB = ({ BORDER_RADIUS, ]), }), - transform: [ - ...(combinedStyles?.absoluteFill?.transform || []), - { - scaleX: animFAB.interpolate({ - inputRange: propForDirection([distance, 0]), - outputRange: propForDirection([extendedWidth / SIZE, 1]), - }), - }, - ], }, + combinedStyles.absoluteFill, ]} /> diff --git a/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap index 05659c1250..713f8eee4c 100644 --- a/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap +++ b/src/components/__tests__/__snapshots__/AnimatedFAB.test.js.snap @@ -85,9 +85,6 @@ exports[`renders animated fab 1`] = ` Object { "translateX": 28, }, - Object { - "scaleX": 1, - }, ], "width": 56, } @@ -338,9 +335,6 @@ exports[`renders animated fab with label on the left 1`] = ` Object { "translateX": 0, }, - Object { - "scaleX": 1, - }, ], "width": 56, } @@ -594,9 +588,6 @@ exports[`renders animated fab with label on the right by default 1`] = ` Object { "translateX": 28, }, - Object { - "scaleX": 1, - }, ], "width": 56, } From b0a45f3a93071188246f505cf025668cd31147f0 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Fri, 8 Oct 2021 19:48:34 +0200 Subject: [PATCH 28/49] feat: add config controls to AnimatedFABExample --- example/src/Examples/AnimatedFABExample.tsx | 145 ++++++++++++++++---- 1 file changed, 115 insertions(+), 30 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index 74fd1dfb50..0308df62f2 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -15,6 +15,7 @@ import { useTheme, Avatar, Paragraph, + RadioButton, } from 'react-native-paper'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import { animatedFABExampleData } from '../../utils'; @@ -62,6 +63,8 @@ const CustomFAB = ({ } else setIsExtended(extended); }, [animatedValue, extended]); + const fabStyle = { [animateFrom]: 16 }; + return ( ); }; @@ -85,6 +88,13 @@ const AnimatedFABExample = () => { const [extended, setExtended] = React.useState(true); const [visible, setVisible] = React.useState(true); + const [animateFrom, setAnimateFrom] = React.useState<'left' | 'right'>( + 'right' + ); + const [iconMode, setIconMode] = React.useState<'static' | 'dynamic'>( + 'static' + ); + const { current: velocity } = React.useRef( new Animated.Value(0) ); @@ -168,41 +178,106 @@ const AnimatedFABExample = () => { onScroll={onScroll} /> - + + + iconMode - + + static - + setIconMode('static')} + /> + + + + dynamic + + setIconMode('dynamic')} + /> + + + + + animateFrom + + + left + + setAnimateFrom('left')} + /> + + + + right + + setAnimateFrom('right')} + /> + + + ); @@ -254,10 +329,20 @@ const styles = StyleSheet.create({ flex: 1, }, fabStyle: { - right: 16, bottom: 16, position: 'absolute', }, + controlsWrapper: { + flex: 1, + display: 'flex', + position: 'absolute', + top: 0, + left: 0, + right: 0, + backgroundColor: 'white', + paddingVertical: 12, + paddingHorizontal: 16, + }, }); export default AnimatedFABExample; From 24f8aeb4f47dac1c3a12d2f9ece81fbb43e757b9 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Fri, 8 Oct 2021 20:04:01 +0200 Subject: [PATCH 29/49] refactor: split AnimatedFABExample into separete files --- example/src/ExampleList.tsx | 2 +- .../AnimatedFABExample.tsx | 69 ++----------------- .../Examples/AnimatedFABExample/CustomFAB.tsx | 60 ++++++++++++++++ .../src/Examples/AnimatedFABExample/index.ts | 1 + 4 files changed, 68 insertions(+), 64 deletions(-) rename example/src/Examples/{ => AnimatedFABExample}/AnimatedFABExample.tsx (84%) create mode 100644 example/src/Examples/AnimatedFABExample/CustomFAB.tsx create mode 100644 example/src/Examples/AnimatedFABExample/index.ts diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index cdd17bb63c..9c4f98b91d 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -37,7 +37,7 @@ import ToggleButtonExample from './Examples/ToggleButtonExample'; import TouchableRippleExample from './Examples/TouchableRippleExample'; import ThemeExample from './Examples/ThemeExample'; import RadioButtonItemExample from './Examples/RadioButtonItemExample'; -import AnimatedFABExample from './Examples/AnimatedFABExample'; +import AnimatedFABExample from './Examples/AnimatedFABExample/AnimatedFABExample'; export const examples: Record< string, diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx similarity index 84% rename from example/src/Examples/AnimatedFABExample.tsx rename to example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx index 0308df62f2..b446c18dbf 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx @@ -1,24 +1,16 @@ import * as React from 'react'; -import { - View, - StyleSheet, - FlatList, - Animated, - Platform, - StyleProp, - ViewStyle, -} from 'react-native'; +import { View, StyleSheet, FlatList, Animated, Platform } from 'react-native'; import type { NativeSyntheticEvent, NativeScrollEvent } from 'react-native'; import { Colors, - AnimatedFAB, useTheme, Avatar, Paragraph, RadioButton, } from 'react-native-paper'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; -import { animatedFABExampleData } from '../../utils'; +import { animatedFABExampleData } from '../../../utils'; +import CustomFAB from './CustomFAB'; type Item = { id: string; @@ -32,59 +24,13 @@ type Item = { bgColor: string; }; -type CustomFABProps = { - animatedValue: Animated.Value; - visible: boolean; - extended: boolean; - label: string; - animateFrom: 'left' | 'right'; - iconMode?: 'static' | 'dynamic'; - style?: StyleProp; -}; - -const isIOS = Platform.OS === 'ios'; - -const CustomFAB = ({ - animatedValue, - visible, - extended, - label, - animateFrom, - style, - iconMode, -}: CustomFABProps) => { - const [isExtended, setIsExtended] = React.useState(true); - - React.useEffect(() => { - if (!isIOS) { - animatedValue.addListener(({ value }: { value: number }) => { - setIsExtended(value <= 0); - }); - } else setIsExtended(extended); - }, [animatedValue, extended]); - - const fabStyle = { [animateFrom]: 16 }; - - return ( - console.log('Pressed')} - visible={visible} - animateFrom={animateFrom} - iconMode={iconMode} - style={[styles.fabStyle, style, fabStyle]} - /> - ); -}; - const AnimatedFABExample = () => { const { colors: { background }, } = useTheme(); + const isIOS = Platform.OS === 'ios'; + const [extended, setExtended] = React.useState(true); const [visible, setVisible] = React.useState(true); @@ -278,6 +224,7 @@ const AnimatedFABExample = () => { label={'New Message'} animateFrom={animateFrom} iconMode={iconMode} + isIOS={isIOS} /> ); @@ -328,10 +275,6 @@ const styles = StyleSheet.create({ marginRight: 8, flex: 1, }, - fabStyle: { - bottom: 16, - position: 'absolute', - }, controlsWrapper: { flex: 1, display: 'flex', diff --git a/example/src/Examples/AnimatedFABExample/CustomFAB.tsx b/example/src/Examples/AnimatedFABExample/CustomFAB.tsx new file mode 100644 index 0000000000..a120473876 --- /dev/null +++ b/example/src/Examples/AnimatedFABExample/CustomFAB.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { StyleProp, ViewStyle, Animated, StyleSheet } 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; + isIOS: boolean; +}; + +const CustomFAB = ({ + animatedValue, + visible, + extended, + label, + animateFrom, + style, + iconMode, + isIOS, +}: CustomFABProps) => { + const [isExtended, setIsExtended] = React.useState(true); + + React.useEffect(() => { + if (!isIOS) { + animatedValue.addListener(({ value }: { value: number }) => { + setIsExtended(value <= 0); + }); + } else setIsExtended(extended); + }, [animatedValue, extended, isIOS]); + + const fabStyle = { [animateFrom]: 16 }; + + return ( + 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', + }, +}); diff --git a/example/src/Examples/AnimatedFABExample/index.ts b/example/src/Examples/AnimatedFABExample/index.ts new file mode 100644 index 0000000000..7561c57b50 --- /dev/null +++ b/example/src/Examples/AnimatedFABExample/index.ts @@ -0,0 +1 @@ +export { default } from './AnimatedFABExample'; From 033d87a9e4bd6f7f402ee1806fc10ce0f7da015d Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Fri, 8 Oct 2021 20:10:57 +0200 Subject: [PATCH 30/49] refactor: improve FlatList performance --- .../AnimatedFABExample/AnimatedFABExample.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx index b446c18dbf..867d5401e6 100644 --- a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx @@ -45,8 +45,8 @@ const AnimatedFABExample = () => { new Animated.Value(0) ); - const renderItem = ({ item }: { item: Item }) => { - return ( + const renderItem = React.useCallback( + ({ item }: { item: Item }) => ( { - ); - }; + ), + [] + ); const onScroll = ({ nativeEvent, @@ -108,12 +109,17 @@ const AnimatedFABExample = () => { setExtended(currentScrollPosition <= 0); }; + const _keyExtractor = React.useCallback( + (item: { id: string }) => item.id, + [] + ); + return ( <> item.id} + keyExtractor={_keyExtractor} onEndReachedThreshold={0} scrollEventThrottle={16} style={[styles.flex, { backgroundColor: background }]} From ccb23836cd899ed58dc6261cd6ed28aace247ec6 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Fri, 8 Oct 2021 20:53:10 +0200 Subject: [PATCH 31/49] refactor: extract controls to separate component --- .../AnimatedFABExample/AnimatedFABExample.tsx | 127 ++---------------- .../AnimatedFABExample/CustomFABControls.tsx | 124 +++++++++++++++++ 2 files changed, 134 insertions(+), 117 deletions(-) create mode 100644 example/src/Examples/AnimatedFABExample/CustomFABControls.tsx diff --git a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx index 867d5401e6..0a8591a8ca 100644 --- a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx @@ -1,16 +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, - useTheme, - Avatar, - Paragraph, - RadioButton, -} 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 CustomFAB from './CustomFAB'; +import CustomFABControls, { + Controls, + initialControls, +} from './CustomFABControls'; type Item = { id: string; @@ -34,12 +32,7 @@ const AnimatedFABExample = () => { const [extended, setExtended] = React.useState(true); const [visible, setVisible] = React.useState(true); - const [animateFrom, setAnimateFrom] = React.useState<'left' | 'right'>( - 'right' - ); - const [iconMode, setIconMode] = React.useState<'static' | 'dynamic'>( - 'static' - ); + const [controls, setControls] = React.useState(initialControls); const { current: velocity } = React.useRef( new Animated.Value(0) @@ -93,7 +86,7 @@ const AnimatedFABExample = () => { ), - [] + [visible] ); const onScroll = ({ @@ -114,6 +107,8 @@ const AnimatedFABExample = () => { [] ); + const { animateFrom, iconMode } = controls; + return ( <> { onScroll={onScroll} /> - - - iconMode - - - static - - setIconMode('static')} - /> - - - - dynamic - - setIconMode('dynamic')} - /> - - - - - animateFrom - - - left - - setAnimateFrom('left')} - /> - - - - right - - setAnimateFrom('right')} - /> - - - + ): void; +}; + +type CustomControlProps = { + name: string; + options: IconMode[] | AnimateFrom[]; + value: IconMode | AnimateFrom; + onChange(newValue: IconMode | AnimateFrom): void; +}; + +const CustomControl = ({ + name, + options, + value, + onChange, +}: CustomControlProps) => { + const _renderItem = React.useCallback( + ({ item }) => ( + + {item} + + onChange(item)} + /> + + ), + [value, onChange] + ); + + return ( + + {name} + + + + ); +}; + +const CustomFABControls = ({ + setControls, + controls: { animateFrom, iconMode }, +}: Props) => { + const setIconMode = (newIconMode: IconMode) => + setControls((state) => ({ ...state, iconMode: newIconMode })); + + const setAnimateFrom = (newAnimateFrom: AnimateFrom) => + setControls((state) => ({ ...state, animateFrom: newAnimateFrom })); + + return ( + + + + + + ); +}; + +export default CustomFABControls; + +const styles = StyleSheet.create({ + controlsWrapper: { + flex: 1, + display: 'flex', + position: 'absolute', + top: 0, + left: 0, + right: 0, + backgroundColor: Colors.white, + paddingVertical: 12, + paddingHorizontal: 16, + }, + controlWrapper: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + controlItemsList: { + flex: 1, + display: 'flex', + justifyContent: 'flex-end', + }, + controlItem: { + marginLeft: 16, + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, +}); From ac234a9bb218b995c607e428ee01d9395627bcec Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Wed, 3 Mar 2021 11:18:03 +0100 Subject: [PATCH 32/49] feat: introduce first iteration of AnimatedFAB component --- example/src/ExampleList.tsx | 2 + example/src/Examples/AnimatedFABExample.tsx | 437 ++++++++++++++++++++ src/components/FAB/AnimatedFAB.tsx | 389 +++++++++++++++++ src/index.tsx | 1 + 4 files changed, 829 insertions(+) create mode 100644 example/src/Examples/AnimatedFABExample.tsx create mode 100644 src/components/FAB/AnimatedFAB.tsx diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index dc38d73c4d..f39c7708e9 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -37,11 +37,13 @@ import ToggleButtonExample from './Examples/ToggleButtonExample'; import TouchableRippleExample from './Examples/TouchableRippleExample'; import ThemeExample from './Examples/ThemeExample'; import RadioButtonItemExample from './Examples/RadioButtonItemExample'; +import AnimatedFABExample from './Examples/AnimatedFABExample'; export const examples: Record< string, React.ComponentType & { title: string } > = { + animatedFAB: AnimatedFABExample, activityIndicator: ActivityIndicatorExample, appbar: AppbarExample, avatar: AvatarExample, diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx new file mode 100644 index 0000000000..642e04f1d4 --- /dev/null +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -0,0 +1,437 @@ +import * as React from 'react'; +import { View, StyleSheet, FlatList } from 'react-native'; +import type { NativeSyntheticEvent, NativeScrollEvent } from 'react-native'; +import { + Colors, + AnimatedFAB, + useTheme, + Avatar, + Paragraph, +} from 'react-native-paper'; +import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; + +type Item = { + id: string; + sender: string; + header: string; + message: string; + initials: string; + date: string; + read: boolean; + favorite: boolean; + bgColor: string; +}; + +const DATA = [ + { + id: '1', + sender: 'Hermann, Pfannel & Schumm', + header: + 'Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl. Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', + message: + 'Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\n\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.', + initials: 'H', + date: '29.1.2021', + read: false, + favorite: false, + bgColor: '#ff0173', + }, + { + id: '2', + sender: 'Ziemann, Lockman and Kuvalis', + header: + 'Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam vel augue.', + message: + 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\n\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.', + initials: 'J', + date: '5.9.2020', + read: false, + favorite: false, + bgColor: '#b287a9', + }, + { + id: '3', + sender: 'Daniel, Kuhn and Wolf', + header: + 'Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh. Quisque id justo sit amet sapien dignissim vestibulum.', + message: + 'Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\n\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.', + initials: 'Y', + date: '13.6.2020', + read: true, + favorite: false, + bgColor: '#c1bde9', + }, + { + id: '4', + sender: 'Crona, Lind and Stoltenberg', + header: + 'Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus. Phasellus in felis. Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla.', + message: + 'Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', + initials: 'W', + date: '20.5.2020', + read: false, + favorite: false, + bgColor: '#932a24', + }, + { + id: '5', + sender: 'Bashirian-Hudson', + header: + 'Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue. Etiam justo. Etiam pretium iaculis justo. In hac habitasse platea dictumst.', + message: + 'Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\n\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\n\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + initials: 'V', + date: '21.9.2020', + read: true, + favorite: false, + bgColor: '#eda5b7', + }, + { + id: '6', + sender: 'Schmitt-Jacobs', + header: + 'Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh. In quis justo.', + message: + 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.', + initials: 'M', + date: '2.6.2020', + read: true, + favorite: true, + bgColor: '#18aaba', + }, + { + id: '7', + sender: 'Graham-Champlin', + header: + 'Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros. Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue.', + message: + 'In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', + initials: 'T', + date: '17.10.2020', + read: false, + favorite: true, + bgColor: '#cc5e54', + }, + { + id: '8', + sender: 'Schoen, Carroll and Herzog', + header: + 'Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', + message: + 'Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\n\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.', + initials: 'F', + date: '31.10.2020', + read: false, + favorite: false, + bgColor: '#28db04', + }, + { + id: '9', + sender: 'Pouros-Fay', + header: + 'Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius. Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.', + message: + 'Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\n\nSed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.', + initials: 'Z', + date: '6.1.2021', + read: true, + favorite: true, + bgColor: '#b6f3fb', + }, + { + id: '10', + sender: 'McKenzie, Ruecker and Bernhard', + header: 'Nunc purus. Phasellus in felis.', + message: + 'In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.', + initials: 'F', + date: '23.2.2021', + read: false, + favorite: false, + bgColor: '#96f066', + }, + { + id: '11', + sender: 'Olson Inc', + header: + 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui. Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.', + message: + 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\n\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\n\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.', + initials: 'V', + date: '17.10.2020', + read: false, + favorite: false, + bgColor: '#f2d49d', + }, + { + id: '12', + sender: 'Walsh LLC', + header: + 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis.', + message: 'Phasellus in felis. Donec semper sapien a libero. Nam dui.', + initials: 'O', + date: '6.1.2021', + read: false, + favorite: true, + bgColor: '#f477dc', + }, + { + id: '13', + sender: 'Lemke, Cremin and Kutch', + header: + 'Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat. Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede. Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus.', + message: + 'In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\n\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\n\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', + initials: 'D', + date: '10.12.2020', + read: false, + favorite: false, + bgColor: '#b2880e', + }, + { + id: '14', + sender: 'Ruecker Group', + header: + 'Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.', + message: + 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', + initials: 'F', + date: '21.8.2020', + read: true, + favorite: false, + bgColor: '#64983a', + }, + { + id: '15', + sender: 'Kovacek, Lockman and Kautzer', + header: + 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus. Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + message: + 'Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', + initials: 'L', + date: '5.3.2020', + read: true, + favorite: false, + bgColor: '#c7486b', + }, + { + id: '16', + sender: 'Jaskolski-Cummerata', + header: + 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi. Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.', + message: + 'Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\n\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.', + initials: 'C', + date: '21.4.2020', + read: false, + favorite: false, + bgColor: '#2f89a1', + }, + { + id: '17', + sender: 'Wunsch-Walker', + header: 'Donec vitae nisi.', + message: + 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', + initials: 'A', + date: '15.4.2020', + read: false, + favorite: true, + bgColor: '#033cbf', + }, + { + id: '18', + sender: 'Ledner Inc', + header: + 'Curabitur convallis. Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus. Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum.', + message: + 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', + initials: 'Y', + date: '16.5.2020', + read: false, + favorite: true, + bgColor: '#166b59', + }, + { + id: '19', + sender: 'Heaney-Rolfson', + header: 'Nunc rhoncus dui vel sem.', + message: + 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', + initials: 'T', + date: '19.1.2021', + read: true, + favorite: false, + bgColor: '#da0148', + }, + { + id: '20', + sender: 'Dietrich, Beier and Leannon', + header: + 'Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est. Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem. Duis aliquam convallis nunc.', + message: + 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.', + initials: 'Z', + date: '21.5.2020', + read: false, + favorite: false, + bgColor: '#22b33f', + }, +]; + +const AnimatedFABExample = () => { + const { + colors: { background }, + } = useTheme(); + + const [scrollPosition, setScrollPosition] = React.useState(0); + const [extended, setExtended] = React.useState(false); + const [visible, setVisible] = React.useState(true); + + const renderItem = ({ item }: { item: Item }) => { + return ( + + + + + + {item.sender} + + + {item.date} + + + + + + + {item.header} + + + {item.message} + + + + setVisible(!visible)} + style={styles.icon} + /> + + + + ); + }; + + const onScroll = ({ + nativeEvent, + }: NativeSyntheticEvent) => { + const currentScrollPosition = Math.floor(nativeEvent.contentOffset.y); + + const scrollHeight = + nativeEvent.contentSize.height - nativeEvent.layoutMeasurement.height; + + setScrollPosition(currentScrollPosition); + + if (currentScrollPosition > scrollPosition && scrollPosition > 0) { + setExtended(false); + } else if ( + currentScrollPosition < scrollPosition && + scrollPosition < scrollHeight + ) { + setExtended(true); + } + }; + + return ( + <> + item.id} + onEndReachedThreshold={0} + scrollEventThrottle={16} + style={[styles.flex, { backgroundColor: background }]} + contentContainerStyle={[ + styles.container, + { backgroundColor: background }, + ]} + onScroll={onScroll} + /> + console.log('Pressed')} + visible={visible} + /> + + ); +}; + +AnimatedFABExample.title = 'Animated Floating Action Button'; + +const styles = StyleSheet.create({ + container: { + padding: 16, + }, + avatar: { + marginRight: 16, + marginTop: 8, + }, + flex: { + flex: 1, + }, + itemContainer: { + marginBottom: 16, + flexDirection: 'row', + }, + itemTextContentContainer: { + flexDirection: 'column', + flex: 1, + }, + itemHeaderContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + itemMessageContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + flexGrow: 1, + }, + read: { + fontWeight: 'bold', + }, + icon: { + marginLeft: 16, + alignSelf: 'flex-end', + }, + date: { + fontSize: 12, + }, + header: { + fontSize: 14, + marginRight: 8, + flex: 1, + }, +}); + +export default AnimatedFABExample; diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx new file mode 100644 index 0000000000..d7aa78925f --- /dev/null +++ b/src/components/FAB/AnimatedFAB.tsx @@ -0,0 +1,389 @@ +import color from 'color'; +import * as React from 'react'; +import { + Animated, + View, + ViewStyle, + StyleSheet, + StyleProp, + Easing, +} from 'react-native'; +import Surface from '../Surface'; +import Icon from '../Icon'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import { withTheme } from '../../core/theming'; +import type { $RemoveChildren } from '../../types'; +import type { IconSource } from './../Icon'; +//@ts-ignore +import type { AccessibilityState, TextLayoutEvent } from 'react-native'; +import { white, black } from '../../styles/colors'; +import AnimatedText from '../Typography/AnimatedText'; + +type Props = $RemoveChildren & { + /** + * Icon to display for the `FAB`. + */ + icon: IconSource; + /** + * Label for extended `FAB`. + */ + label: string; + /** + * Make the label text uppercased. + */ + uppercase?: boolean; + /** + * Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB. + * Uses `label` by default if specified. + */ + accessibilityLabel?: string; + /** + * Accessibility state for the FAB. This is read by the screen reader when the user taps the FAB. + */ + accessibilityState?: AccessibilityState; + /** + * Custom color for the icon and label of the `FAB`. + */ + color?: string; + /** + * Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch. + */ + disabled?: boolean; + /** + * Whether `FAB` is currently visible. + */ + visible?: boolean; + /** + * Function to execute on press. + */ + onPress?: () => void; + /** + * Function to execute on long press. + */ + onLongPress?: () => void; + /** + * Whether icon should be translated to the end of extended `FAB` or be static and stay in the same place. The default value is `dynamic`. + */ + iconMode?: 'static' | 'dynamic'; + /** + * Indicates from which direction animation should be performed. The default value is `right`. + */ + animateFrom?: 'right' | 'left'; + /** + * Whether `FAB` should start animation to extend. + */ + extended: boolean; + style?: StyleProp; + /** + * @optional + */ + theme: ReactNativePaper.Theme; + testID?: string; +}; + +const SIZE = 56; + +const AnimatedFAB = ({ + icon, + label, + accessibilityLabel = label, + accessibilityState, + color: customColor, + disabled, + onPress, + onLongPress, + theme, + style, + visible = true, + uppercase = true, + testID, + animateFrom = 'right', + extended = false, + iconMode = 'dynamic', + ...rest +}: Props) => { + const { current: visibility } = React.useRef( + new Animated.Value(visible ? 1 : 0) + ); + const { current: scaleFAB } = React.useRef(new Animated.Value(1)); + const { current: translateFAB } = React.useRef(new Animated.Value(0)); + const { current: translateIcon } = React.useRef(new Animated.Value(0)); + const { current: opacityLabel } = React.useRef(new Animated.Value(0)); + + const { scale } = theme.animation; + + const [textWidth, setTextWidth] = React.useState(0); + const [textHeight, setTextHeight] = React.useState(0); + + const animateFromRight = animateFrom === 'right'; + const isIconStatic = iconMode === 'static'; + + React.useEffect(() => { + if (visible) { + Animated.timing(visibility, { + toValue: 1, + duration: 200 * scale, + useNativeDriver: true, + }).start(); + } else { + Animated.timing(visibility, { + toValue: 0, + duration: 150 * scale, + useNativeDriver: true, + }).start(); + } + }, [visible, scale, visibility]); + + const disabledColor = color(theme.dark ? white : black) + .alpha(0.12) + .rgb() + .string(); + + const { backgroundColor = disabled ? disabledColor : theme.colors.accent } = + StyleSheet.flatten(style) || {}; + + let foregroundColor; + + if (typeof customColor !== 'undefined') { + foregroundColor = customColor; + } else if (disabled) { + foregroundColor = color(theme.dark ? white : black) + .alpha(0.32) + .rgb() + .string(); + } else { + foregroundColor = !color(backgroundColor).isLight() + ? white + : 'rgba(0, 0, 0, .54)'; + } + + const rippleColor = color(foregroundColor).alpha(0.32).rgb().string(); + + const totalWidth = animateFromRight + ? -textWidth - SIZE / 2 + : textWidth + SIZE / 2; + + React.useEffect(() => { + const animations = [ + { + property: scaleFAB, + toValue: !extended ? 1 : 0.9, + }, + { + property: translateFAB, + toValue: !extended ? 0 : totalWidth, + }, + { + property: translateIcon, + toValue: !extended ? 0 : totalWidth, + exclude: isIconStatic, + }, + { + property: opacityLabel, + toValue: !extended ? 0 : 1, + duration: extended ? 250 : 100, + delay: extended ? 100 : 0, + }, + ]; + + Animated.parallel( + animations + .filter(({ exclude }) => !exclude) + .map(({ property, toValue, duration = 250, delay = 0 }) => { + return Animated.timing(property, { + toValue, + duration: duration * scale, + delay: delay * scale, + useNativeDriver: true, + easing: Easing.linear, + }); + }) + ).start(); + }, [ + extended, + scaleFAB, + opacityLabel, + totalWidth, + translateIcon, + translateFAB, + scale, + isIconStatic, + ]); + + const onTextLayout = ({ nativeEvent }: TextLayoutEvent) => { + const currentWidth = Math.ceil(nativeEvent.lines[0].width); + const currentHeight = Math.ceil(nativeEvent.lines[0].height); + + if (currentWidth !== textWidth || currentHeight !== textHeight) { + setTextWidth(currentWidth); + setTextHeight(currentHeight); + } + }; + + return ( + + } + > + + + + + + + + + + + + + + + + {label} + + + ); +}; + +const styles = StyleSheet.create({ + standard: { + height: SIZE, + borderRadius: SIZE / 2, + }, + disabled: { + elevation: 0, + }, + container: { + position: 'absolute', + borderRadius: SIZE / 2, + elevation: 6, + right: 16, + bottom: 32, + }, + innerWrapper: { + position: 'absolute', + flexDirection: 'row', + overflow: 'hidden', + borderRadius: SIZE / 2, + right: 0, + }, + touchable: { + borderRadius: SIZE / 2, + }, + leftInnerWrapper: { + left: 0, + right: undefined, + }, + iconWrapper: { + alignItems: 'center', + justifyContent: 'center', + position: 'absolute', + height: SIZE, + width: SIZE, + right: 0, + }, + moveIconFromLeft: { + left: 0, + right: undefined, + }, + label: { + position: 'absolute', + zIndex: 2, + }, + uppercaseLabel: { + textTransform: 'uppercase', + }, +}); + +export default withTheme(AnimatedFAB); diff --git a/src/index.tsx b/src/index.tsx index 871bd69d00..439d34d8fb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -29,6 +29,7 @@ export { default as DataTable } from './components/DataTable/DataTable'; export { default as Dialog } from './components/Dialog/Dialog'; export { default as Divider } from './components/Divider'; export { default as FAB } from './components/FAB'; +export { default as AnimatedFAB } from './components/FAB/AnimatedFAB'; export { default as HelperText } from './components/HelperText'; export { default as IconButton } from './components/IconButton'; export { default as Menu } from './components/Menu/Menu'; From 2404d9b4b67dacd4462f2a0d6ba28b3c66738984 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Wed, 3 Mar 2021 11:37:36 +0100 Subject: [PATCH 33/49] refactor: remove useless code --- src/components/FAB/AnimatedFAB.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index d7aa78925f..30fdf64f8d 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -226,7 +226,6 @@ const AnimatedFAB = ({ style={ [ { - backgroundColor: 'red', opacity: visibility, transform: [ { From 0aa14fcb024894f91acd1f76e0c1c196a3afb51a Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Wed, 3 Mar 2021 16:49:56 +0100 Subject: [PATCH 34/49] refactor: don't move fab when switching animation direction prop --- example/src/Examples/AnimatedFABExample.tsx | 2 ++ src/components/FAB/AnimatedFAB.tsx | 15 +++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index 642e04f1d4..f6944b806e 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -382,6 +382,8 @@ const AnimatedFABExample = () => { uppercase={false} onPress={() => console.log('Pressed')} visible={visible} + animateFrom="right" + iconMode="dynamic" /> ); diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 30fdf64f8d..9cc771e872 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -262,9 +262,6 @@ const AnimatedFAB = ({ accessibilityState={{ ...accessibilityState, disabled }} style={[styles.touchable, styles.standard]} testID={testID} - hitSlop={{ - left: textWidth + 1.5 * SIZE, - }} > Date: Thu, 4 Mar 2021 09:55:53 +0100 Subject: [PATCH 35/49] refactor: interpolate animation on fab text --- src/components/FAB/AnimatedFAB.tsx | 81 ++++++++++++++++++------------ 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 9cc771e872..272596daf7 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -181,19 +181,16 @@ const AnimatedFAB = ({ { property: opacityLabel, toValue: !extended ? 0 : 1, - duration: extended ? 250 : 100, - delay: extended ? 100 : 0, }, ]; Animated.parallel( animations .filter(({ exclude }) => !exclude) - .map(({ property, toValue, duration = 250, delay = 0 }) => { + .map(({ property, toValue }) => { return Animated.timing(property, { toValue, - duration: duration * scale, - delay: delay * scale, + duration: 150 * scale, useNativeDriver: true, easing: Easing.linear, }); @@ -248,6 +245,8 @@ const AnimatedFAB = ({ }, ], }, + styles.standard, + styles.innerContainer, ]} > - - {label} - + + + {label} + + ); }; @@ -347,7 +361,10 @@ const styles = StyleSheet.create({ borderRadius: SIZE / 2, elevation: 6, right: 390 / 2 + SIZE / 2, - bottom: 32, + bottom: 150, + }, + innerContainer: { + position: 'absolute', }, innerWrapper: { position: 'absolute', From e7047e02b368c34db267da02584f82e22b5d8a4c Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Thu, 4 Mar 2021 15:11:03 +0100 Subject: [PATCH 36/49] refactor: code and example clean up --- example/src/Examples/AnimatedFABExample.tsx | 6 ++ src/components/FAB/AnimatedFAB.tsx | 95 +++++++++++---------- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index f6944b806e..0907b59674 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -384,6 +384,7 @@ const AnimatedFABExample = () => { visible={visible} animateFrom="right" iconMode="dynamic" + style={styles.fabStyle} /> ); @@ -434,6 +435,11 @@ const styles = StyleSheet.create({ marginRight: 8, flex: 1, }, + fabStyle: { + right: 16, + bottom: 32, + position: 'absolute', + }, }); export default AnimatedFABExample; diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 272596daf7..0eed2255c2 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -7,6 +7,9 @@ import { StyleSheet, StyleProp, Easing, + ScrollView, + Text, + Platform, } from 'react-native'; import Surface from '../Surface'; import Icon from '../Icon'; @@ -217,6 +220,20 @@ const AnimatedFAB = ({ } }; + const getSidesPosition = () => { + if (animateFromRight) { + return { + left: -totalWidth, + right: undefined, + }; + } else { + return { + left: undefined, + right: totalWidth, + }; + } + }; + return ( - + - - - + /> + @@ -308,16 +316,16 @@ const AnimatedFAB = ({ + + {Platform.OS !== 'ios' && ( + + {/* @ts-ignore */} + {label} + + )} ); }; @@ -358,47 +373,35 @@ const styles = StyleSheet.create({ }, container: { position: 'absolute', + backgroundColor: 'transparent', borderRadius: SIZE / 2, elevation: 6, - right: 390 / 2 + SIZE / 2, - bottom: 150, - }, - innerContainer: { - position: 'absolute', }, innerWrapper: { - position: 'absolute', flexDirection: 'row', overflow: 'hidden', borderRadius: SIZE / 2, - right: -SIZE, }, touchable: { borderRadius: SIZE / 2, }, - leftInnerWrapper: { - left: 0, - right: undefined, - }, iconWrapper: { alignItems: 'center', justifyContent: 'center', position: 'absolute', height: SIZE, width: SIZE, - right: -SIZE, - }, - moveIconFromLeft: { - left: 0, - right: undefined, }, label: { position: 'absolute', - zIndex: 2, }, uppercaseLabel: { textTransform: 'uppercase', }, + textPlaceholderContainer: { + height: 0, + position: 'absolute', + }, }); export default withTheme(AnimatedFAB); From 1f585a940992a940d871dcaddb8da7f03705f276 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Mon, 8 Mar 2021 10:20:19 +0100 Subject: [PATCH 37/49] refactor: use one animated value --- src/components/FAB/AnimatedFAB.tsx | 98 +++++++++++------------------- 1 file changed, 35 insertions(+), 63 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 0eed2255c2..3c638544ca 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -85,6 +85,7 @@ type Props = $RemoveChildren & { }; const SIZE = 56; +const SCALE = 0.9; const AnimatedFAB = ({ icon, @@ -105,14 +106,13 @@ const AnimatedFAB = ({ iconMode = 'dynamic', ...rest }: Props) => { + const isIOS = Platform.OS === 'ios'; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) ); - const { current: scaleFAB } = React.useRef(new Animated.Value(1)); - const { current: translateFAB } = React.useRef(new Animated.Value(0)); - const { current: translateIcon } = React.useRef(new Animated.Value(0)); - const { current: opacityLabel } = React.useRef(new Animated.Value(0)); - + const { current: animFAB } = React.useRef( + new Animated.Value(0) + ); const { scale } = theme.animation; const [textWidth, setTextWidth] = React.useState(0); @@ -162,53 +162,20 @@ const AnimatedFAB = ({ const rippleColor = color(foregroundColor).alpha(0.32).rgb().string(); - const totalWidth = animateFromRight + const extendedWidth = textWidth + 1.5 * SIZE; + + const distance = animateFromRight ? -textWidth - SIZE / 2 : textWidth + SIZE / 2; React.useEffect(() => { - const animations = [ - { - property: scaleFAB, - toValue: !extended ? 1 : 0.9, - }, - { - property: translateFAB, - toValue: !extended ? 0 : totalWidth, - }, - { - property: translateIcon, - toValue: !extended ? 0 : totalWidth, - exclude: isIconStatic, - }, - { - property: opacityLabel, - toValue: !extended ? 0 : 1, - }, - ]; - - Animated.parallel( - animations - .filter(({ exclude }) => !exclude) - .map(({ property, toValue }) => { - return Animated.timing(property, { - toValue, - duration: 150 * scale, - useNativeDriver: true, - easing: Easing.linear, - }); - }) - ).start(); - }, [ - extended, - scaleFAB, - opacityLabel, - totalWidth, - translateIcon, - translateFAB, - scale, - isIconStatic, - ]); + Animated.timing(animFAB, { + toValue: !extended ? 0 : distance, + duration: 150 * scale, + useNativeDriver: true, + easing: Easing.linear, + }).start(); + }, [animFAB, scale, distance, extended]); const onTextLayout = ({ nativeEvent }: TextLayoutEvent) => { const currentWidth = Math.ceil(nativeEvent.lines[0].width); @@ -223,13 +190,13 @@ const AnimatedFAB = ({ const getSidesPosition = () => { if (animateFromRight) { return { - left: -totalWidth, + left: -distance, right: undefined, }; } else { return { left: undefined, - right: totalWidth, + right: distance, }; } }; @@ -258,7 +225,10 @@ const AnimatedFAB = ({ { transform: [ { - scaleY: scaleFAB, + scaleY: animFAB.interpolate({ + inputRange: animateFromRight ? [distance, 0] : [0, distance], + outputRange: animateFromRight ? [SCALE, 1] : [1, SCALE], + }), }, ], }, @@ -286,10 +256,10 @@ const AnimatedFAB = ({ { transform: [ { - translateX: translateFAB, + translateX: animFAB, }, ], - width: textWidth + 1.5 * SIZE, + width: extendedWidth, backgroundColor, }, getSidesPosition(), @@ -302,8 +272,8 @@ const AnimatedFAB = ({ - {Platform.OS !== 'ios' && ( + {!isIOS && ( {/* @ts-ignore */} {label} From 1f7f13badbf510a78a7f5bc160d8b2a0547409b7 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Mon, 8 Mar 2021 15:10:16 +0100 Subject: [PATCH 38/49] feat: configure shadow for Android --- src/components/FAB/AnimatedFAB.tsx | 154 ++++++++++++++++++++--------- 1 file changed, 109 insertions(+), 45 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 3c638544ca..f66298cb45 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -85,6 +85,7 @@ type Props = $RemoveChildren & { }; const SIZE = 56; +const BORDER_RADIUS = SIZE / 2; const SCALE = 0.9; const AnimatedFAB = ({ @@ -106,7 +107,6 @@ const AnimatedFAB = ({ iconMode = 'dynamic', ...rest }: Props) => { - const isIOS = Platform.OS === 'ios'; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) ); @@ -165,8 +165,8 @@ const AnimatedFAB = ({ const extendedWidth = textWidth + 1.5 * SIZE; const distance = animateFromRight - ? -textWidth - SIZE / 2 - : textWidth + SIZE / 2; + ? -textWidth - BORDER_RADIUS + : textWidth + BORDER_RADIUS; React.useEffect(() => { Animated.timing(animFAB, { @@ -213,6 +213,7 @@ const AnimatedFAB = ({ scale: visibility, }, ], + elevation: Platform.OS === 'ios' ? 6 : 0, }, styles.container, disabled && styles.disabled, @@ -235,44 +236,101 @@ const AnimatedFAB = ({ styles.standard, ]} > - - - + + + + + + + + + - + - {!isIOS && ( + {Platform.OS !== 'ios' && ( {/* @ts-ignore */} {label} @@ -338,7 +396,7 @@ const AnimatedFAB = ({ const styles = StyleSheet.create({ standard: { height: SIZE, - borderRadius: SIZE / 2, + borderRadius: BORDER_RADIUS, }, disabled: { elevation: 0, @@ -346,16 +404,22 @@ const styles = StyleSheet.create({ container: { position: 'absolute', backgroundColor: 'transparent', - borderRadius: SIZE / 2, - elevation: 6, + borderRadius: BORDER_RADIUS, }, innerWrapper: { flexDirection: 'row', overflow: 'hidden', - borderRadius: SIZE / 2, + borderRadius: BORDER_RADIUS, + }, + shadowWrapper: { + elevation: 0, + }, + shadow: { + elevation: 6, + borderRadius: BORDER_RADIUS, }, touchable: { - borderRadius: SIZE / 2, + borderRadius: BORDER_RADIUS, }, iconWrapper: { alignItems: 'center', From 0069512885e86aa2bd4197861455e92724ce3ef2 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Mon, 8 Mar 2021 16:23:01 +0100 Subject: [PATCH 39/49] refactor: introduce isIOS --- src/components/FAB/AnimatedFAB.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index f66298cb45..35667bb22f 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -107,6 +107,9 @@ const AnimatedFAB = ({ iconMode = 'dynamic', ...rest }: Props) => { + const isIOS = Platform.OS === 'ios'; + const animateFromRight = animateFrom === 'right'; + const isIconStatic = iconMode === 'static'; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) ); @@ -118,9 +121,6 @@ const AnimatedFAB = ({ const [textWidth, setTextWidth] = React.useState(0); const [textHeight, setTextHeight] = React.useState(0); - const animateFromRight = animateFrom === 'right'; - const isIconStatic = iconMode === 'static'; - React.useEffect(() => { if (visible) { Animated.timing(visibility, { @@ -213,7 +213,7 @@ const AnimatedFAB = ({ scale: visibility, }, ], - elevation: Platform.OS === 'ios' ? 6 : 0, + elevation: isIOS ? 6 : 0, }, styles.container, disabled && styles.disabled, @@ -344,7 +344,7 @@ const AnimatedFAB = ({ - {Platform.OS !== 'ios' && ( + {!isIOS && ( {/* @ts-ignore */} {label} From 73a71fc153918023042f9d4a2a0ab627d2b37841 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Fri, 26 Mar 2021 12:00:30 +0100 Subject: [PATCH 40/49] refactor: fixes after merge --- src/components/FAB/AnimatedFAB.tsx | 59 ++++++++++++++++++------------ 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 35667bb22f..35c60735f6 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -17,10 +17,14 @@ import TouchableRipple from '../TouchableRipple/TouchableRipple'; import { withTheme } from '../../core/theming'; import type { $RemoveChildren } from '../../types'; import type { IconSource } from './../Icon'; -//@ts-ignore -import type { AccessibilityState, TextLayoutEvent } from 'react-native'; +import type { + AccessibilityState, + NativeSyntheticEvent, + TextLayoutEventData, +} from 'react-native'; import { white, black } from '../../styles/colors'; import AnimatedText from '../Typography/AnimatedText'; +import getContrastingColor from '../../utils/getContrastingColor'; type Props = $RemoveChildren & { /** @@ -108,7 +112,7 @@ const AnimatedFAB = ({ ...rest }: Props) => { const isIOS = Platform.OS === 'ios'; - const animateFromRight = animateFrom === 'right'; + const isAnimatedFromRight = animateFrom === 'right'; const isIconStatic = iconMode === 'static'; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) @@ -142,8 +146,9 @@ const AnimatedFAB = ({ .rgb() .string(); - const { backgroundColor = disabled ? disabledColor : theme.colors.accent } = - StyleSheet.flatten(style) || {}; + const { + backgroundColor = disabled ? disabledColor : theme.colors.accent, + } = (StyleSheet.flatten(style) || {}) as ViewStyle; let foregroundColor; @@ -155,16 +160,18 @@ const AnimatedFAB = ({ .rgb() .string(); } else { - foregroundColor = !color(backgroundColor).isLight() - ? white - : 'rgba(0, 0, 0, .54)'; + foregroundColor = getContrastingColor( + backgroundColor, + white, + 'rgba(0, 0, 0, .54)' + ); } const rippleColor = color(foregroundColor).alpha(0.32).rgb().string(); const extendedWidth = textWidth + 1.5 * SIZE; - const distance = animateFromRight + const distance = isAnimatedFromRight ? -textWidth - BORDER_RADIUS : textWidth + BORDER_RADIUS; @@ -177,7 +184,9 @@ const AnimatedFAB = ({ }).start(); }, [animFAB, scale, distance, extended]); - const onTextLayout = ({ nativeEvent }: TextLayoutEvent) => { + const onTextLayout = ({ + nativeEvent, + }: NativeSyntheticEvent) => { const currentWidth = Math.ceil(nativeEvent.lines[0].width); const currentHeight = Math.ceil(nativeEvent.lines[0].height); @@ -188,7 +197,7 @@ const AnimatedFAB = ({ }; const getSidesPosition = () => { - if (animateFromRight) { + if (isAnimatedFromRight) { return { left: -distance, right: undefined, @@ -227,8 +236,10 @@ const AnimatedFAB = ({ transform: [ { scaleY: animFAB.interpolate({ - inputRange: animateFromRight ? [distance, 0] : [0, distance], - outputRange: animateFromRight ? [SCALE, 1] : [1, SCALE], + inputRange: isAnimatedFromRight + ? [distance, 0] + : [0, distance], + outputRange: isAnimatedFromRight ? [SCALE, 1] : [1, SCALE], }), }, ], @@ -307,6 +318,7 @@ const AnimatedFAB = ({ rippleColor={rippleColor} disabled={disabled} accessibilityLabel={accessibilityLabel} + // @ts-expect-error We keep old a11y props for backwards compat with old RN versions accessibilityTraits={disabled ? ['button', 'disabled'] : 'button'} accessibilityComponentType="button" accessibilityRole="button" @@ -343,30 +355,28 @@ const AnimatedFAB = ({ {!isIOS && ( + // Method `onTextLayout` on Android returns sizes of text visible on the screen, + // however during render the text in `FAB` isn't fully visible. In order to get + // proper text measurements there is a need to additionaly render that text, but + // wrapped in absolutely positioned `ScrollView` which height is 0. - {/* @ts-ignore */} {label} )} From f01ca7de4a65dca79010df291421f22be3b38bb4 Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Fri, 26 Mar 2021 16:56:28 +0100 Subject: [PATCH 41/49] refactor: adjust android example --- example/src/Examples/AnimatedFABExample.tsx | 56 +++++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index 0907b59674..c34eadd8e9 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { View, StyleSheet, FlatList } from 'react-native'; +import { View, StyleSheet, FlatList, Animated, Platform } from 'react-native'; import type { NativeSyntheticEvent, NativeScrollEvent } from 'react-native'; import { Colors, @@ -281,6 +281,40 @@ const DATA = [ }, ]; +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 ( + console.log('Pressed')} + visible={visible} + animateFrom="right" + iconMode="dynamic" + style={styles.fabStyle} + /> + ); +}; + const AnimatedFABExample = () => { const { colors: { background }, @@ -290,6 +324,10 @@ const AnimatedFABExample = () => { const [extended, setExtended] = React.useState(false); const [visible, setVisible] = React.useState(true); + const { current: velocity } = React.useRef( + new Animated.Value(0) + ); + const renderItem = ({ item }: { item: Item }) => { return ( @@ -343,6 +381,10 @@ const AnimatedFABExample = () => { const onScroll = ({ nativeEvent, }: NativeSyntheticEvent) => { + if (!isIOS) { + return velocity.setValue(nativeEvent?.velocity?.y ?? 0); + } + const currentScrollPosition = Math.floor(nativeEvent.contentOffset.y); const scrollHeight = @@ -375,16 +417,10 @@ const AnimatedFABExample = () => { ]} onScroll={onScroll} /> - console.log('Pressed')} + ); From a573c04545c8559d38e1da3ee45b0dcc3bdfb56c Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Mon, 29 Mar 2021 10:34:49 +0200 Subject: [PATCH 42/49] refactor: small changes --- example/src/ExampleList.tsx | 1 - example/src/Examples/AnimatedFABExample.tsx | 262 +------------------- example/utils/index.ts | 259 +++++++++++++++++++ 3 files changed, 261 insertions(+), 261 deletions(-) diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index f39c7708e9..cdd17bb63c 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -113,7 +113,6 @@ export default function ExampleList({ navigation }: Props) { paddingLeft: safeArea.left, paddingRight: safeArea.right, }} - style={{ backgroundColor: colors.background }} ItemSeparatorComponent={Divider} renderItem={renderItem} keyExtractor={keyExtractor} diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx index c34eadd8e9..bd96aa2813 100644 --- a/example/src/Examples/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample.tsx @@ -9,6 +9,7 @@ import { Paragraph, } from 'react-native-paper'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; +import { animatedFABExampleData } from '../../utils'; type Item = { id: string; @@ -22,265 +23,6 @@ type Item = { bgColor: string; }; -const DATA = [ - { - id: '1', - sender: 'Hermann, Pfannel & Schumm', - header: - 'Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl. Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', - message: - 'Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\n\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.', - initials: 'H', - date: '29.1.2021', - read: false, - favorite: false, - bgColor: '#ff0173', - }, - { - id: '2', - sender: 'Ziemann, Lockman and Kuvalis', - header: - 'Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam vel augue.', - message: - 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\n\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.', - initials: 'J', - date: '5.9.2020', - read: false, - favorite: false, - bgColor: '#b287a9', - }, - { - id: '3', - sender: 'Daniel, Kuhn and Wolf', - header: - 'Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh. Quisque id justo sit amet sapien dignissim vestibulum.', - message: - 'Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\n\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.', - initials: 'Y', - date: '13.6.2020', - read: true, - favorite: false, - bgColor: '#c1bde9', - }, - { - id: '4', - sender: 'Crona, Lind and Stoltenberg', - header: - 'Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus. Phasellus in felis. Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla.', - message: - 'Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', - initials: 'W', - date: '20.5.2020', - read: false, - favorite: false, - bgColor: '#932a24', - }, - { - id: '5', - sender: 'Bashirian-Hudson', - header: - 'Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue. Etiam justo. Etiam pretium iaculis justo. In hac habitasse platea dictumst.', - message: - 'Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\n\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\n\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', - initials: 'V', - date: '21.9.2020', - read: true, - favorite: false, - bgColor: '#eda5b7', - }, - { - id: '6', - sender: 'Schmitt-Jacobs', - header: - 'Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh. In quis justo.', - message: - 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.', - initials: 'M', - date: '2.6.2020', - read: true, - favorite: true, - bgColor: '#18aaba', - }, - { - id: '7', - sender: 'Graham-Champlin', - header: - 'Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros. Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue.', - message: - 'In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', - initials: 'T', - date: '17.10.2020', - read: false, - favorite: true, - bgColor: '#cc5e54', - }, - { - id: '8', - sender: 'Schoen, Carroll and Herzog', - header: - 'Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', - message: - 'Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\n\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.', - initials: 'F', - date: '31.10.2020', - read: false, - favorite: false, - bgColor: '#28db04', - }, - { - id: '9', - sender: 'Pouros-Fay', - header: - 'Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius. Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.', - message: - 'Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\n\nSed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.', - initials: 'Z', - date: '6.1.2021', - read: true, - favorite: true, - bgColor: '#b6f3fb', - }, - { - id: '10', - sender: 'McKenzie, Ruecker and Bernhard', - header: 'Nunc purus. Phasellus in felis.', - message: - 'In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.', - initials: 'F', - date: '23.2.2021', - read: false, - favorite: false, - bgColor: '#96f066', - }, - { - id: '11', - sender: 'Olson Inc', - header: - 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui. Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.', - message: - 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\n\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\n\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.', - initials: 'V', - date: '17.10.2020', - read: false, - favorite: false, - bgColor: '#f2d49d', - }, - { - id: '12', - sender: 'Walsh LLC', - header: - 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis.', - message: 'Phasellus in felis. Donec semper sapien a libero. Nam dui.', - initials: 'O', - date: '6.1.2021', - read: false, - favorite: true, - bgColor: '#f477dc', - }, - { - id: '13', - sender: 'Lemke, Cremin and Kutch', - header: - 'Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat. Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede. Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus.', - message: - 'In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\n\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\n\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', - initials: 'D', - date: '10.12.2020', - read: false, - favorite: false, - bgColor: '#b2880e', - }, - { - id: '14', - sender: 'Ruecker Group', - header: - 'Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.', - message: - 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', - initials: 'F', - date: '21.8.2020', - read: true, - favorite: false, - bgColor: '#64983a', - }, - { - id: '15', - sender: 'Kovacek, Lockman and Kautzer', - header: - 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus. Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', - message: - 'Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', - initials: 'L', - date: '5.3.2020', - read: true, - favorite: false, - bgColor: '#c7486b', - }, - { - id: '16', - sender: 'Jaskolski-Cummerata', - header: - 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi. Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.', - message: - 'Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\n\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.', - initials: 'C', - date: '21.4.2020', - read: false, - favorite: false, - bgColor: '#2f89a1', - }, - { - id: '17', - sender: 'Wunsch-Walker', - header: 'Donec vitae nisi.', - message: - 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', - initials: 'A', - date: '15.4.2020', - read: false, - favorite: true, - bgColor: '#033cbf', - }, - { - id: '18', - sender: 'Ledner Inc', - header: - 'Curabitur convallis. Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus. Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum.', - message: - 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', - initials: 'Y', - date: '16.5.2020', - read: false, - favorite: true, - bgColor: '#166b59', - }, - { - id: '19', - sender: 'Heaney-Rolfson', - header: 'Nunc rhoncus dui vel sem.', - message: - 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', - initials: 'T', - date: '19.1.2021', - read: true, - favorite: false, - bgColor: '#da0148', - }, - { - id: '20', - sender: 'Dietrich, Beier and Leannon', - header: - 'Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est. Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem. Duis aliquam convallis nunc.', - message: - 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.', - initials: 'Z', - date: '21.5.2020', - read: false, - favorite: false, - bgColor: '#22b33f', - }, -]; - type CustomFABProps = { animatedValue: Animated.Value; visible: boolean; @@ -405,7 +147,7 @@ const AnimatedFABExample = () => { return ( <> item.id} onEndReachedThreshold={0} diff --git a/example/utils/index.ts b/example/utils/index.ts index 3405e8d078..e17215aa58 100644 --- a/example/utils/index.ts +++ b/example/utils/index.ts @@ -47,3 +47,262 @@ export function inputReducer( return { ...state }; } } + +export const animatedFABExampleData = [ + { + id: '1', + sender: 'Hermann, Pfannel & Schumm', + header: + 'Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl. Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', + message: + 'Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\n\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.', + initials: 'H', + date: '29.1.2021', + read: false, + favorite: false, + bgColor: '#ff0173', + }, + { + id: '2', + sender: 'Ziemann, Lockman and Kuvalis', + header: + 'Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam vel augue.', + message: + 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\n\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.', + initials: 'J', + date: '5.9.2020', + read: false, + favorite: false, + bgColor: '#b287a9', + }, + { + id: '3', + sender: 'Daniel, Kuhn and Wolf', + header: + 'Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh. Quisque id justo sit amet sapien dignissim vestibulum.', + message: + 'Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\n\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.', + initials: 'Y', + date: '13.6.2020', + read: true, + favorite: false, + bgColor: '#c1bde9', + }, + { + id: '4', + sender: 'Crona, Lind and Stoltenberg', + header: + 'Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus. Phasellus in felis. Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla.', + message: + 'Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.', + initials: 'W', + date: '20.5.2020', + read: false, + favorite: false, + bgColor: '#932a24', + }, + { + id: '5', + sender: 'Bashirian-Hudson', + header: + 'Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue. Etiam justo. Etiam pretium iaculis justo. In hac habitasse platea dictumst.', + message: + 'Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\n\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\n\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + initials: 'V', + date: '21.9.2020', + read: true, + favorite: false, + bgColor: '#eda5b7', + }, + { + id: '6', + sender: 'Schmitt-Jacobs', + header: + 'Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh. In quis justo.', + message: + 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.', + initials: 'M', + date: '2.6.2020', + read: true, + favorite: true, + bgColor: '#18aaba', + }, + { + id: '7', + sender: 'Graham-Champlin', + header: + 'Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros. Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat. In congue.', + message: + 'In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', + initials: 'T', + date: '17.10.2020', + read: false, + favorite: true, + bgColor: '#cc5e54', + }, + { + id: '8', + sender: 'Schoen, Carroll and Herzog', + header: + 'Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', + message: + 'Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\n\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.', + initials: 'F', + date: '31.10.2020', + read: false, + favorite: false, + bgColor: '#28db04', + }, + { + id: '9', + sender: 'Pouros-Fay', + header: + 'Donec semper sapien a libero. Nam dui. Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius. Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.', + message: + 'Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\n\nSed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.', + initials: 'Z', + date: '6.1.2021', + read: true, + favorite: true, + bgColor: '#b6f3fb', + }, + { + id: '10', + sender: 'McKenzie, Ruecker and Bernhard', + header: 'Nunc purus. Phasellus in felis.', + message: + 'In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.', + initials: 'F', + date: '23.2.2021', + read: false, + favorite: false, + bgColor: '#96f066', + }, + { + id: '11', + sender: 'Olson Inc', + header: + 'Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui. Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.', + message: + 'Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\n\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\n\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.', + initials: 'V', + date: '17.10.2020', + read: false, + favorite: false, + bgColor: '#f2d49d', + }, + { + id: '12', + sender: 'Walsh LLC', + header: + 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis.', + message: 'Phasellus in felis. Donec semper sapien a libero. Nam dui.', + initials: 'O', + date: '6.1.2021', + read: false, + favorite: true, + bgColor: '#f477dc', + }, + { + id: '13', + sender: 'Lemke, Cremin and Kutch', + header: + 'Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat. Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede. Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus.', + message: + 'In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\n\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\n\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', + initials: 'D', + date: '10.12.2020', + read: false, + favorite: false, + bgColor: '#b2880e', + }, + { + id: '14', + sender: 'Ruecker Group', + header: + 'Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.', + message: + 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\n\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.', + initials: 'F', + date: '21.8.2020', + read: true, + favorite: false, + bgColor: '#64983a', + }, + { + id: '15', + sender: 'Kovacek, Lockman and Kautzer', + header: + 'Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem. Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus. Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + message: + 'Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.', + initials: 'L', + date: '5.3.2020', + read: true, + favorite: false, + bgColor: '#c7486b', + }, + { + id: '16', + sender: 'Jaskolski-Cummerata', + header: + 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi. Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.', + message: + 'Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\n\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.', + initials: 'C', + date: '21.4.2020', + read: false, + favorite: false, + bgColor: '#2f89a1', + }, + { + id: '17', + sender: 'Wunsch-Walker', + header: 'Donec vitae nisi.', + message: + 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\n\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\n\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.', + initials: 'A', + date: '15.4.2020', + read: false, + favorite: true, + bgColor: '#033cbf', + }, + { + id: '18', + sender: 'Ledner Inc', + header: + 'Curabitur convallis. Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus. Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero. Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum.', + message: + 'In congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', + initials: 'Y', + date: '16.5.2020', + read: false, + favorite: true, + bgColor: '#166b59', + }, + { + id: '19', + sender: 'Heaney-Rolfson', + header: 'Nunc rhoncus dui vel sem.', + message: + 'In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.', + initials: 'T', + date: '19.1.2021', + read: true, + favorite: false, + bgColor: '#da0148', + }, + { + id: '20', + sender: 'Dietrich, Beier and Leannon', + header: + 'Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est. Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum. Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem. Duis aliquam convallis nunc.', + message: + 'Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.', + initials: 'Z', + date: '21.5.2020', + read: false, + favorite: false, + bgColor: '#22b33f', + }, +]; From fcaefef94e0e10bc3a3c2fcb492013288af2fc3c Mon Sep 17 00:00:00 2001 From: lukewalczak Date: Wed, 13 Oct 2021 16:41:34 +0200 Subject: [PATCH 43/49] feat: hide AnimatedFab for production --- example/src/ExampleList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index cdd17bb63c..8a7381e88b 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -43,7 +43,7 @@ export const examples: Record< string, React.ComponentType & { title: string } > = { - animatedFAB: AnimatedFABExample, + ...(__DEV__ && { animatedFab: AnimatedFABExample }), activityIndicator: ActivityIndicatorExample, appbar: AppbarExample, avatar: AvatarExample, From ceb03cbcb8f0ebeaca098b6ec4cce7f68919f73c Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Tue, 19 Oct 2021 09:56:36 +0200 Subject: [PATCH 44/49] refactor: apply small feedback notes --- .../AnimatedFABExample/AnimatedFABExample.tsx | 1 - .../src/Examples/AnimatedFABExample/CustomFAB.tsx | 12 +++++++++--- .../AnimatedFABExample/CustomFABControls.tsx | 3 +++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx index 0a8591a8ca..e5c5c8026c 100644 --- a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx @@ -134,7 +134,6 @@ const AnimatedFABExample = () => { label={'New Message'} animateFrom={animateFrom} iconMode={iconMode} - isIOS={isIOS} /> ); diff --git a/example/src/Examples/AnimatedFABExample/CustomFAB.tsx b/example/src/Examples/AnimatedFABExample/CustomFAB.tsx index a120473876..1d6c249c6e 100644 --- a/example/src/Examples/AnimatedFABExample/CustomFAB.tsx +++ b/example/src/Examples/AnimatedFABExample/CustomFAB.tsx @@ -1,5 +1,11 @@ import React from 'react'; -import { StyleProp, ViewStyle, Animated, StyleSheet } from 'react-native'; +import { + StyleProp, + ViewStyle, + Animated, + StyleSheet, + Platform, +} from 'react-native'; import { AnimatedFAB } from 'react-native-paper'; type CustomFABProps = { @@ -10,7 +16,6 @@ type CustomFABProps = { animateFrom: 'left' | 'right'; iconMode?: 'static' | 'dynamic'; style?: StyleProp; - isIOS: boolean; }; const CustomFAB = ({ @@ -21,10 +26,11 @@ const CustomFAB = ({ animateFrom, style, iconMode, - isIOS, }: CustomFABProps) => { const [isExtended, setIsExtended] = React.useState(true); + const isIOS = Platform.OS === 'ios'; + React.useEffect(() => { if (!isIOS) { animatedValue.addListener(({ value }: { value: number }) => { diff --git a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx index a867960af7..b3c9a63a40 100644 --- a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx +++ b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx @@ -48,6 +48,8 @@ const CustomControl = ({ [value, onChange] ); + const _keyExtractor = React.useCallback(({ item }) => item, []); + return ( {name} @@ -56,6 +58,7 @@ const CustomControl = ({ horizontal data={options} renderItem={_renderItem} + keyExtractor={_keyExtractor} contentContainerStyle={styles.controlItemsList} /> From c897aa0260c84290aeb74f98c1b742112335b353 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Thu, 21 Oct 2021 09:27:58 +0200 Subject: [PATCH 45/49] refactor: apply small feedback notes --- example/src/Examples/AnimatedFABExample.tsx | 223 ------------------ .../AnimatedFABExample/CustomFABControls.tsx | 4 - src/components/FAB/AnimatedFAB.tsx | 2 +- 3 files changed, 1 insertion(+), 228 deletions(-) delete mode 100644 example/src/Examples/AnimatedFABExample.tsx diff --git a/example/src/Examples/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample.tsx deleted file mode 100644 index bd96aa2813..0000000000 --- a/example/src/Examples/AnimatedFABExample.tsx +++ /dev/null @@ -1,223 +0,0 @@ -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 Icon from 'react-native-vector-icons/MaterialCommunityIcons'; -import { animatedFABExampleData } from '../../utils'; - -type Item = { - id: string; - sender: string; - header: string; - message: string; - initials: string; - date: string; - read: boolean; - favorite: boolean; - 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 ( - console.log('Pressed')} - visible={visible} - animateFrom="right" - iconMode="dynamic" - style={styles.fabStyle} - /> - ); -}; - -const AnimatedFABExample = () => { - const { - colors: { background }, - } = useTheme(); - - const [scrollPosition, setScrollPosition] = React.useState(0); - const [extended, setExtended] = React.useState(false); - const [visible, setVisible] = React.useState(true); - - const { current: velocity } = React.useRef( - new Animated.Value(0) - ); - - const renderItem = ({ item }: { item: Item }) => { - return ( - - - - - - {item.sender} - - - {item.date} - - - - - - - {item.header} - - - {item.message} - - - - setVisible(!visible)} - style={styles.icon} - /> - - - - ); - }; - - const onScroll = ({ - nativeEvent, - }: NativeSyntheticEvent) => { - if (!isIOS) { - return velocity.setValue(nativeEvent?.velocity?.y ?? 0); - } - - const currentScrollPosition = Math.floor(nativeEvent.contentOffset.y); - - const scrollHeight = - nativeEvent.contentSize.height - nativeEvent.layoutMeasurement.height; - - setScrollPosition(currentScrollPosition); - - if (currentScrollPosition > scrollPosition && scrollPosition > 0) { - setExtended(false); - } else if ( - currentScrollPosition < scrollPosition && - scrollPosition < scrollHeight - ) { - setExtended(true); - } - }; - - return ( - <> - item.id} - onEndReachedThreshold={0} - scrollEventThrottle={16} - style={[styles.flex, { backgroundColor: background }]} - contentContainerStyle={[ - styles.container, - { backgroundColor: background }, - ]} - onScroll={onScroll} - /> - - - ); -}; - -AnimatedFABExample.title = 'Animated Floating Action Button'; - -const styles = StyleSheet.create({ - container: { - padding: 16, - }, - avatar: { - marginRight: 16, - marginTop: 8, - }, - flex: { - flex: 1, - }, - itemContainer: { - marginBottom: 16, - flexDirection: 'row', - }, - itemTextContentContainer: { - flexDirection: 'column', - flex: 1, - }, - itemHeaderContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - }, - itemMessageContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - flexGrow: 1, - }, - read: { - fontWeight: 'bold', - }, - icon: { - marginLeft: 16, - alignSelf: 'flex-end', - }, - date: { - fontSize: 12, - }, - header: { - fontSize: 14, - marginRight: 8, - flex: 1, - }, - fabStyle: { - right: 16, - bottom: 32, - position: 'absolute', - }, -}); - -export default AnimatedFABExample; diff --git a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx index b3c9a63a40..01371aa07a 100644 --- a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx +++ b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx @@ -99,7 +99,6 @@ export default CustomFABControls; const styles = StyleSheet.create({ controlsWrapper: { flex: 1, - display: 'flex', position: 'absolute', top: 0, left: 0, @@ -109,18 +108,15 @@ const styles = StyleSheet.create({ paddingHorizontal: 16, }, controlWrapper: { - display: 'flex', flexDirection: 'row', alignItems: 'center', }, controlItemsList: { flex: 1, - display: 'flex', justifyContent: 'flex-end', }, controlItem: { marginLeft: 16, - display: 'flex', flexDirection: 'row', alignItems: 'center', }, diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 559cc85dd9..1da70c9f92 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -1,5 +1,5 @@ -import color from 'color'; import * as React from 'react'; +import color from 'color'; import { Animated, View, From 7aa93e79c8d09bfad51bc352d5154921eef513c0 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Thu, 21 Oct 2021 12:54:01 +0200 Subject: [PATCH 46/49] refactor: move dynamic styles to a separate utils file --- .../AnimatedFABExample/CustomFABControls.tsx | 25 +-- src/components/FAB/AnimatedFAB.tsx | 150 ++---------------- src/components/FAB/utils.ts | 144 +++++++++++++++++ 3 files changed, 170 insertions(+), 149 deletions(-) create mode 100644 src/components/FAB/utils.ts diff --git a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx index 01371aa07a..fa5b0465f2 100644 --- a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx +++ b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx @@ -1,13 +1,14 @@ import React from 'react'; import { StyleSheet, View, FlatList } from 'react-native'; import { Colors, Paragraph, RadioButton } from 'react-native-paper'; - -export type IconMode = 'static' | 'dynamic'; -export type AnimateFrom = 'left' | 'right'; +import type { + AnimatedFABAnimateFrom, + AnimatedFABIconMode, +} from 'src/components/FAB/AnimatedFAB'; export type Controls = { - iconMode: IconMode; - animateFrom: AnimateFrom; + iconMode: AnimatedFABIconMode; + animateFrom: AnimatedFABAnimateFrom; }; export const initialControls: Controls = { @@ -20,11 +21,13 @@ type Props = { setControls(controls: React.SetStateAction): void; }; +type ControlValue = AnimatedFABIconMode | AnimatedFABAnimateFrom; + type CustomControlProps = { name: string; - options: IconMode[] | AnimateFrom[]; - value: IconMode | AnimateFrom; - onChange(newValue: IconMode | AnimateFrom): void; + options: ControlValue[]; + value: ControlValue; + onChange(newValue: ControlValue): void; }; const CustomControl = ({ @@ -48,7 +51,7 @@ const CustomControl = ({ [value, onChange] ); - const _keyExtractor = React.useCallback(({ item }) => item, []); + const _keyExtractor = React.useCallback((item) => item, []); return ( @@ -69,10 +72,10 @@ const CustomFABControls = ({ setControls, controls: { animateFrom, iconMode }, }: Props) => { - const setIconMode = (newIconMode: IconMode) => + const setIconMode = (newIconMode: AnimatedFABIconMode) => setControls((state) => ({ ...state, iconMode: newIconMode })); - const setAnimateFrom = (newAnimateFrom: AnimateFrom) => + const setAnimateFrom = (newAnimateFrom: AnimatedFABAnimateFrom) => setControls((state) => ({ ...state, animateFrom: newAnimateFrom })); return ( diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 1da70c9f92..4916b79c54 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -25,6 +25,10 @@ import type { } from 'react-native'; import { white, black } from '../../styles/colors'; import AnimatedText from '../Typography/AnimatedText'; +import { getCombinedStyles } from './utils'; + +export type AnimatedFABIconMode = 'static' | 'dynamic'; +export type AnimatedFABAnimateFrom = 'left' | 'right'; type Props = $RemoveChildren & { /** @@ -71,11 +75,11 @@ type Props = $RemoveChildren & { /** * Whether icon should be translated to the end of extended `FAB` or be static and stay in the same place. The default value is `dynamic`. */ - iconMode?: 'static' | 'dynamic'; + iconMode?: AnimatedFABIconMode; /** * Indicates from which direction animation should be performed. The default value is `right`. */ - animateFrom?: 'right' | 'left'; + animateFrom?: AnimatedFABAnimateFrom; /** * Whether `FAB` should start animation to extend. */ @@ -207,142 +211,12 @@ const AnimatedFAB = ({ return right.reverse(); }; - type CombinedStyles = { - innerWrapper: Animated.WithAnimatedValue; - iconWrapper: Animated.WithAnimatedValue; - absoluteFill: Animated.WithAnimatedValue; - }; - - const getCombinedStyles = (): CombinedStyles => { - const defaultPositionStyles = { left: -distance, right: undefined }; - - const combinedStyles: CombinedStyles = { - innerWrapper: { - ...defaultPositionStyles, - }, - iconWrapper: { - ...defaultPositionStyles, - }, - absoluteFill: {}, - }; - - const animatedFromRight = isAnimatedFromRight && !isRTL; - const animatedFromRightRTL = isAnimatedFromRight && isRTL; - const animatedFromLeft = !isAnimatedFromRight && !isRTL; - const animatedFromLeftRTL = !isAnimatedFromRight && isRTL; - - if (animatedFromRight) { - combinedStyles.innerWrapper.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [distance, 0], - }), - }, - ]; - combinedStyles.iconWrapper.transform = [ - { - translateX: isIconStatic ? 0 : animFAB, - }, - ]; - combinedStyles.absoluteFill.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [Math.abs(distance) / 2, Math.abs(distance)], - }), - }, - ]; - } - - if (animatedFromRightRTL) { - combinedStyles.iconWrapper.transform = [ - { - translateX: isIconStatic - ? 0 - : animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [-distance, 0], - }), - }, - ]; - combinedStyles.innerWrapper.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [-distance, 0], - }), - }, - ]; - combinedStyles.absoluteFill.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [0, distance], - }), - }, - ]; - } - - if (animatedFromLeft) { - combinedStyles.iconWrapper.transform = [ - { - translateX: isIconStatic - ? distance - : animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [distance, distance * 2], - }), - }, - ]; - combinedStyles.innerWrapper.transform = [ - { - translateX: animFAB, - }, - ]; - combinedStyles.absoluteFill.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [0, Math.abs(distance) / 2], - }), - }, - ]; - } - - if (animatedFromLeftRTL) { - combinedStyles.iconWrapper.transform = [ - { - translateX: isIconStatic - ? animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [-distance, -distance * 2], - }) - : -distance, - }, - ]; - combinedStyles.innerWrapper.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [0, -distance], - }), - }, - ]; - combinedStyles.absoluteFill.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [0, -distance], - }), - }, - ]; - } - - return combinedStyles; - }; - - const combinedStyles = getCombinedStyles(); + const combinedStyles = getCombinedStyles({ + isAnimatedFromRight, + isIconStatic, + distance, + animFAB, + }); return ( ; + iconWrapper: Animated.WithAnimatedValue; + absoluteFill: Animated.WithAnimatedValue; +}; + +export const getCombinedStyles = ({ + isAnimatedFromRight, + isIconStatic, + distance, + animFAB, +}: GetCombinedStylesProps): CombinedStyles => { + const { isRTL } = I18nManager; + + const defaultPositionStyles = { left: -distance, right: undefined }; + + const combinedStyles: CombinedStyles = { + innerWrapper: { + ...defaultPositionStyles, + }, + iconWrapper: { + ...defaultPositionStyles, + }, + absoluteFill: {}, + }; + + const animatedFromRight = isAnimatedFromRight && !isRTL; + const animatedFromRightRTL = isAnimatedFromRight && isRTL; + const animatedFromLeft = !isAnimatedFromRight && !isRTL; + const animatedFromLeftRTL = !isAnimatedFromRight && isRTL; + + if (animatedFromRight) { + combinedStyles.innerWrapper.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [distance, 0], + }), + }, + ]; + combinedStyles.iconWrapper.transform = [ + { + translateX: isIconStatic ? 0 : animFAB, + }, + ]; + combinedStyles.absoluteFill.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [Math.abs(distance) / 2, Math.abs(distance)], + }), + }, + ]; + } else if (animatedFromRightRTL) { + combinedStyles.iconWrapper.transform = [ + { + translateX: isIconStatic + ? 0 + : animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [-distance, 0], + }), + }, + ]; + combinedStyles.innerWrapper.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [-distance, 0], + }), + }, + ]; + combinedStyles.absoluteFill.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [distance, 0], + outputRange: [0, distance], + }), + }, + ]; + } else if (animatedFromLeft) { + combinedStyles.iconWrapper.transform = [ + { + translateX: isIconStatic + ? distance + : animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [distance, distance * 2], + }), + }, + ]; + combinedStyles.innerWrapper.transform = [ + { + translateX: animFAB, + }, + ]; + combinedStyles.absoluteFill.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [0, Math.abs(distance) / 2], + }), + }, + ]; + } else if (animatedFromLeftRTL) { + combinedStyles.iconWrapper.transform = [ + { + translateX: isIconStatic + ? animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [-distance, -distance * 2], + }) + : -distance, + }, + ]; + combinedStyles.innerWrapper.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [0, -distance], + }), + }, + ]; + combinedStyles.absoluteFill.transform = [ + { + translateX: animFAB.interpolate({ + inputRange: [0, distance], + outputRange: [0, -distance], + }), + }, + ]; + } + + return combinedStyles; +}; From 6a56b0d8deb77d963c304fc384551c3ed37ef736 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Thu, 21 Oct 2021 13:08:34 +0200 Subject: [PATCH 47/49] refactor: move AnimatedFAB files to a separate folder --- .../FAB/{ => AnimatedFAB}/AnimatedFAB.tsx | 16 ++++++++-------- src/components/FAB/AnimatedFAB/index.ts | 3 +++ src/components/FAB/{ => AnimatedFAB}/utils.ts | 0 3 files changed, 11 insertions(+), 8 deletions(-) rename src/components/FAB/{ => AnimatedFAB}/AnimatedFAB.tsx (96%) create mode 100644 src/components/FAB/AnimatedFAB/index.ts rename src/components/FAB/{ => AnimatedFAB}/utils.ts (100%) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB/AnimatedFAB.tsx similarity index 96% rename from src/components/FAB/AnimatedFAB.tsx rename to src/components/FAB/AnimatedFAB/AnimatedFAB.tsx index 4916b79c54..e12f4d9338 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB/AnimatedFAB.tsx @@ -12,19 +12,19 @@ import { Platform, I18nManager, } from 'react-native'; -import Surface from '../Surface'; -import Icon from '../Icon'; -import TouchableRipple from '../TouchableRipple/TouchableRipple'; -import type { $RemoveChildren } from '../../types'; -import type { IconSource } from './../Icon'; -import { withTheme } from '../../core/theming'; +import Surface from '../../Surface'; +import Icon from '../../Icon'; +import TouchableRipple from '../../TouchableRipple/TouchableRipple'; +import type { $RemoveChildren } from '../../../types'; +import type { IconSource } from '../../Icon'; +import { withTheme } from '../../../core/theming'; import type { AccessibilityState, NativeSyntheticEvent, TextLayoutEventData, } from 'react-native'; -import { white, black } from '../../styles/colors'; -import AnimatedText from '../Typography/AnimatedText'; +import { white, black } from '../../../styles/colors'; +import AnimatedText from '../../Typography/AnimatedText'; import { getCombinedStyles } from './utils'; export type AnimatedFABIconMode = 'static' | 'dynamic'; diff --git a/src/components/FAB/AnimatedFAB/index.ts b/src/components/FAB/AnimatedFAB/index.ts new file mode 100644 index 0000000000..ab3d03637c --- /dev/null +++ b/src/components/FAB/AnimatedFAB/index.ts @@ -0,0 +1,3 @@ +export * from './AnimatedFAB'; +export { default } from './AnimatedFAB'; +export * from './utils'; diff --git a/src/components/FAB/utils.ts b/src/components/FAB/AnimatedFAB/utils.ts similarity index 100% rename from src/components/FAB/utils.ts rename to src/components/FAB/AnimatedFAB/utils.ts From c968edaf84a2071bbe95c23be1f9446f4a6128fd Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Thu, 21 Oct 2021 13:18:02 +0200 Subject: [PATCH 48/49] refactor: export missing types from the package --- example/src/Examples/AnimatedFABExample/CustomFABControls.tsx | 2 +- src/components/FAB/AnimatedFAB/index.ts | 2 +- src/index.tsx | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx index fa5b0465f2..5ddf2e8bd1 100644 --- a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx +++ b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx @@ -4,7 +4,7 @@ import { Colors, Paragraph, RadioButton } from 'react-native-paper'; import type { AnimatedFABAnimateFrom, AnimatedFABIconMode, -} from 'src/components/FAB/AnimatedFAB'; +} from 'react-native-paper'; export type Controls = { iconMode: AnimatedFABIconMode; diff --git a/src/components/FAB/AnimatedFAB/index.ts b/src/components/FAB/AnimatedFAB/index.ts index ab3d03637c..00dfec8aa1 100644 --- a/src/components/FAB/AnimatedFAB/index.ts +++ b/src/components/FAB/AnimatedFAB/index.ts @@ -1,3 +1,3 @@ -export * from './AnimatedFAB'; export { default } from './AnimatedFAB'; +export * from './AnimatedFAB'; export * from './utils'; diff --git a/src/index.tsx b/src/index.tsx index 439d34d8fb..241da255fd 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -17,6 +17,8 @@ import * as Drawer from './components/Drawer/Drawer'; export { Avatar, List, Drawer }; +export * from './components/FAB/AnimatedFAB'; + export { default as Badge } from './components/Badge'; export { default as ActivityIndicator } from './components/ActivityIndicator'; export { default as Banner } from './components/Banner'; From d05dc7bd5e081a5fa1d1f85fc7d4b5c98e8cd9f1 Mon Sep 17 00:00:00 2001 From: Drakeoon Date: Wed, 27 Oct 2021 09:41:35 +0200 Subject: [PATCH 49/49] chore: enable AnimateFAB in production --- docs/component-docs.config.js | 1 - example/src/ExampleList.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/component-docs.config.js b/docs/component-docs.config.js index 68c60d4128..7acc917e96 100644 --- a/docs/component-docs.config.js +++ b/docs/component-docs.config.js @@ -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)) { diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index 8a7381e88b..69b42bb4a5 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -43,7 +43,7 @@ export const examples: Record< string, React.ComponentType & { title: string } > = { - ...(__DEV__ && { animatedFab: AnimatedFABExample }), + animatedFab: AnimatedFABExample, activityIndicator: ActivityIndicatorExample, appbar: AppbarExample, avatar: AvatarExample,