From a7d05f9125cb31ccdb791db75b1121b8e95c3f2d Mon Sep 17 00:00:00 2001
From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com>
Date: Wed, 20 Dec 2023 10:43:33 +0100
Subject: [PATCH 1/7] Migrate Icon and FloatingActionButton to function
component
---
ios/Podfile.lock | 31 +---
package-lock.json | 14 +-
package.json | 2 +-
src/components/FloatingActionButton.js | 132 ------------------
.../FloatingActionButton/FabPlusIcon.js | 74 ++++++++++
src/components/FloatingActionButton/index.js | 86 ++++++++++++
src/styles/utils/index.ts | 8 --
tests/ui/UnreadIndicatorsTest.js | 5 +
8 files changed, 176 insertions(+), 176 deletions(-)
delete mode 100644 src/components/FloatingActionButton.js
create mode 100644 src/components/FloatingActionButton/FabPlusIcon.js
create mode 100644 src/components/FloatingActionButton/index.js
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 0fac30a26430..eebd6ad532d4 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -777,35 +777,10 @@ PODS:
- React-Core
- RNReactNativeHapticFeedback (1.14.0):
- React-Core
- - RNReanimated (3.5.4):
- - DoubleConversion
- - FBLazyVector
- - glog
- - hermes-engine
- - RCT-Folly
- - RCTRequired
- - RCTTypeSafety
- - React-callinvoker
+ - RNReanimated (3.6.1):
+ - RCT-Folly (= 2021.07.22.00)
- React-Core
- - React-Core/DevSupport
- - React-Core/RCTWebSocket
- - React-CoreModules
- - React-cxxreact
- - React-hermes
- - React-jsi
- - React-jsiexecutor
- - React-jsinspector
- - React-RCTActionSheet
- - React-RCTAnimation
- - React-RCTAppDelegate
- - React-RCTBlob
- - React-RCTImage
- - React-RCTLinking
- - React-RCTNetwork
- - React-RCTSettings
- - React-RCTText
- ReactCommon/turbomodule/core
- - Yoga
- RNScreens (3.21.0):
- React-Core
- React-RCTImage
@@ -1280,7 +1255,7 @@ SPEC CHECKSUMS:
rnmapbox-maps: 6f638ec002aa6e906a6f766d69cd45f968d98e64
RNPermissions: 9b086c8f05b2e2faa587fdc31f4c5ab4509728aa
RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c
- RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87
+ RNReanimated: fdbaa9c964bbab7fac50c90862b6cc5f041679b9
RNScreens: d037903436160a4b039d32606668350d2a808806
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
diff --git a/package-lock.json b/package-lock.json
index 1c2ae325575d..5b4c8c737681 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -100,7 +100,7 @@
"react-native-plaid-link-sdk": "10.8.0",
"react-native-qrcode-svg": "^6.2.0",
"react-native-quick-sqlite": "^8.0.0-beta.2",
- "react-native-reanimated": "3.5.4",
+ "react-native-reanimated": "^3.6.1",
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "3.21.0",
@@ -44555,9 +44555,9 @@
}
},
"node_modules/react-native-reanimated": {
- "version": "3.5.4",
- "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz",
- "integrity": "sha512-8we9LLDO1o4Oj9/DICeEJ2K1tjfqkJagqQUglxeUAkol/HcEJ6PGxIrpBcNryLqCDYEcu6FZWld/FzizBIw6bg==",
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz",
+ "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==",
"dependencies": {
"@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
@@ -84872,9 +84872,9 @@
"requires": {}
},
"react-native-reanimated": {
- "version": "3.5.4",
- "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz",
- "integrity": "sha512-8we9LLDO1o4Oj9/DICeEJ2K1tjfqkJagqQUglxeUAkol/HcEJ6PGxIrpBcNryLqCDYEcu6FZWld/FzizBIw6bg==",
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz",
+ "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==",
"requires": {
"@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
diff --git a/package.json b/package.json
index 7281ab12cfa7..25e818c3cfcb 100644
--- a/package.json
+++ b/package.json
@@ -148,7 +148,7 @@
"react-native-plaid-link-sdk": "10.8.0",
"react-native-qrcode-svg": "^6.2.0",
"react-native-quick-sqlite": "^8.0.0-beta.2",
- "react-native-reanimated": "3.5.4",
+ "react-native-reanimated": "^3.6.1",
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "3.21.0",
diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js
deleted file mode 100644
index 791eb150f8c9..000000000000
--- a/src/components/FloatingActionButton.js
+++ /dev/null
@@ -1,132 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {PureComponent} from 'react';
-import {Animated, Easing, View} from 'react-native';
-import compose from '@libs/compose';
-import Icon from './Icon';
-import * as Expensicons from './Icon/Expensicons';
-import PressableWithFeedback from './Pressable/PressableWithFeedback';
-import Tooltip from './Tooltip/PopoverAnchorTooltip';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
-import withStyleUtils, {withStyleUtilsPropTypes} from './withStyleUtils';
-import withTheme, {withThemePropTypes} from './withTheme';
-import withThemeStyles, {withThemeStylesPropTypes} from './withThemeStyles';
-
-const AnimatedIcon = Animated.createAnimatedComponent(Icon);
-AnimatedIcon.displayName = 'AnimatedIcon';
-
-const AnimatedPressable = Animated.createAnimatedComponent(PressableWithFeedback);
-AnimatedPressable.displayName = 'AnimatedPressable';
-
-const propTypes = {
- // Callback to fire on request to toggle the FloatingActionButton
- onPress: PropTypes.func.isRequired,
-
- // Current state (active or not active) of the component
- isActive: PropTypes.bool.isRequired,
-
- // Ref for the button
- buttonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
-
- ...withLocalizePropTypes,
- ...withThemePropTypes,
- ...withThemeStylesPropTypes,
- ...withStyleUtilsPropTypes,
-};
-
-const defaultProps = {
- buttonRef: () => {},
-};
-
-class FloatingActionButton extends PureComponent {
- constructor(props) {
- super(props);
- this.animatedValue = new Animated.Value(props.isActive ? 1 : 0);
- }
-
- componentDidUpdate(prevProps) {
- if (prevProps.isActive === this.props.isActive) {
- return;
- }
-
- this.animateFloatingActionButton();
- }
-
- /**
- * Animates the floating action button
- * Method is called when the isActive prop changes
- */
- animateFloatingActionButton() {
- const animationFinalValue = this.props.isActive ? 1 : 0;
-
- Animated.timing(this.animatedValue, {
- toValue: animationFinalValue,
- duration: 340,
- easing: Easing.inOut(Easing.ease),
- useNativeDriver: false,
- }).start();
- }
-
- render() {
- const rotate = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: ['0deg', '135deg'],
- });
-
- const backgroundColor = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: [this.props.theme.success, this.props.theme.buttonDefaultBG],
- });
-
- const fill = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: [this.props.theme.textLight, this.props.theme.textDark],
- });
-
- return (
-
-
- {
- this.fabPressable = el;
- if (this.props.buttonRef) {
- this.props.buttonRef.current = el;
- }
- }}
- accessibilityLabel={this.props.accessibilityLabel}
- role={this.props.role}
- pressDimmingValue={1}
- onPress={(e) => {
- // Drop focus to avoid blue focus ring.
- this.fabPressable.blur();
- this.props.onPress(e);
- }}
- onLongPress={() => {}}
- style={[this.props.themeStyles.floatingActionButton, this.props.StyleUtils.getAnimatedFABStyle(rotate, backgroundColor)]}
- >
-
-
-
-
- );
- }
-}
-
-FloatingActionButton.propTypes = propTypes;
-FloatingActionButton.defaultProps = defaultProps;
-
-const FloatingActionButtonWithLocalize = withLocalize(FloatingActionButton);
-
-const FloatingActionButtonWithLocalizeWithRef = React.forwardRef((props, ref) => (
-
-));
-
-FloatingActionButtonWithLocalizeWithRef.displayName = 'FloatingActionButtonWithLocalizeWithRef';
-
-export default compose(withThemeStyles, withTheme, withStyleUtils)(FloatingActionButtonWithLocalizeWithRef);
diff --git a/src/components/FloatingActionButton/FabPlusIcon.js b/src/components/FloatingActionButton/FabPlusIcon.js
new file mode 100644
index 000000000000..500606522b3b
--- /dev/null
+++ b/src/components/FloatingActionButton/FabPlusIcon.js
@@ -0,0 +1,74 @@
+import PropTypes from 'prop-types';
+import React, {useEffect} from 'react';
+import Animated, {createAnimatedPropAdapter, Easing, interpolateColor, processColor, useAnimatedProps, useSharedValue, withTiming} from 'react-native-reanimated';
+import Svg, {Path} from 'react-native-svg';
+import useTheme from '@hooks/useTheme';
+import variables from '@styles/variables';
+
+const AnimatedPath = Animated.createAnimatedComponent(Path);
+
+const adapter = createAnimatedPropAdapter(
+ (props) => {
+ // eslint-disable-next-line rulesdir/prefer-underscore-method
+ if (Object.keys(props).includes('fill')) {
+ // eslint-disable-next-line no-param-reassign
+ props.fill = {type: 0, payload: processColor(props.fill)};
+ }
+ // eslint-disable-next-line rulesdir/prefer-underscore-method
+ if (Object.keys(props).includes('stroke')) {
+ // eslint-disable-next-line no-param-reassign
+ props.stroke = {type: 0, payload: processColor(props.stroke)};
+ }
+ },
+ ['fill', 'stroke'],
+);
+adapter.propTypes = {
+ fill: PropTypes.string,
+ stroke: PropTypes.string,
+};
+
+const propTypes = {
+ /* Current state (active or not active) of the component */
+ isActive: PropTypes.bool.isRequired,
+};
+
+function FabPlusIcon({isActive}) {
+ const {textLight, textDark} = useTheme();
+ const sharedValue = useSharedValue(isActive ? 1 : 0);
+
+ useEffect(() => {
+ sharedValue.value = withTiming(isActive ? 1 : 0, {
+ duration: 340,
+ easing: Easing.inOut(Easing.ease),
+ });
+ }, [isActive, sharedValue]);
+
+ const animatedProps = useAnimatedProps(
+ () => {
+ const fill = interpolateColor(sharedValue.value, [0, 1], [textLight, textDark]);
+
+ return {
+ fill,
+ };
+ },
+ [],
+ adapter,
+ );
+
+ return (
+
+ );
+}
+
+FabPlusIcon.propTypes = propTypes;
+FabPlusIcon.displayName = 'FabPlusIcon';
+
+export default FabPlusIcon;
diff --git a/src/components/FloatingActionButton/index.js b/src/components/FloatingActionButton/index.js
new file mode 100644
index 000000000000..6922c8b1949b
--- /dev/null
+++ b/src/components/FloatingActionButton/index.js
@@ -0,0 +1,86 @@
+import PropTypes from 'prop-types';
+import React, {useEffect, useRef} from 'react';
+import {View} from 'react-native';
+import Animated, {Easing, interpolateColor, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
+import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
+import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import FabPlusIcon from './FabPlusIcon';
+
+const AnimatedPressable = Animated.createAnimatedComponent(PressableWithFeedback);
+AnimatedPressable.displayName = 'AnimatedPressable';
+
+const propTypes = {
+ /* Callback to fire on request to toggle the FloatingActionButton */
+ onPress: PropTypes.func.isRequired,
+
+ /* Current state (active or not active) of the component */
+ isActive: PropTypes.bool.isRequired,
+
+ /* An accessibility label for the button */
+ accessibilityLabel: PropTypes.string.isRequired,
+
+ /* An accessibility role for the button */
+ role: PropTypes.string.isRequired,
+};
+
+const FloatingActionButton = React.forwardRef(({onPress, isActive, accessibilityLabel, role}, ref) => {
+ const {success, buttonDefaultBG} = useTheme();
+ const styles = useThemeStyles();
+ const borderRadius = styles.floatingActionButton.borderRadius;
+ const {translate} = useLocalize();
+ const fabPressable = useRef(null);
+ const sharedValue = useSharedValue(isActive ? 1 : 0);
+ const buttonRef = ref;
+
+ useEffect(() => {
+ sharedValue.value = withTiming(isActive ? 1 : 0, {
+ duration: 340,
+ easing: Easing.inOut(Easing.ease),
+ });
+ }, [isActive, sharedValue]);
+
+ const animatedStyle = useAnimatedStyle(() => {
+ const backgroundColor = interpolateColor(sharedValue.value, [0, 1], [success, buttonDefaultBG]);
+
+ return {
+ transform: [{rotate: `${sharedValue.value * 135}deg`}],
+ backgroundColor,
+ borderRadius,
+ };
+ });
+
+ return (
+
+
+ {
+ fabPressable.current = el;
+ if (buttonRef) {
+ buttonRef.current = el;
+ }
+ }}
+ accessibilityLabel={accessibilityLabel}
+ role={role}
+ pressDimmingValue={1}
+ onPress={(e) => {
+ // Drop focus to avoid blue focus ring.
+ fabPressable.current.blur();
+ onPress(e);
+ }}
+ onLongPress={() => {}}
+ style={[styles.floatingActionButton, animatedStyle]}
+ >
+
+
+
+
+ );
+});
+
+FloatingActionButton.propTypes = propTypes;
+FloatingActionButton.displayName = 'FloatingActionButton';
+
+export default FloatingActionButton;
diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts
index de87d2b5dd59..6f3d55e5acb0 100644
--- a/src/styles/utils/index.ts
+++ b/src/styles/utils/index.ts
@@ -446,13 +446,6 @@ function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: nu
return {};
}
-function getAnimatedFABStyle(rotate: Animated.Value, backgroundColor: Animated.Value): Animated.WithAnimatedValue {
- return {
- transform: [{rotate}],
- backgroundColor,
- };
-}
-
function getWidthAndHeightStyle(width: number, height?: number): ViewStyle {
return {
width,
@@ -1015,7 +1008,6 @@ const staticStyleUtils = {
combineStyles,
displayIfTrue,
getAmountFontSizeAndLineHeight,
- getAnimatedFABStyle,
getAutoCompleteSuggestionContainerStyle,
getAvatarBorderRadius,
getAvatarBorderStyle,
diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js
index 93bb2cb94d30..f4d054ab15e8 100644
--- a/tests/ui/UnreadIndicatorsTest.js
+++ b/tests/ui/UnreadIndicatorsTest.js
@@ -31,6 +31,11 @@ jest.setTimeout(30000);
jest.mock('../../src/libs/Notification/LocalNotification');
jest.mock('../../src/components/Icon/Expensicons');
+jest.mock('react-native-reanimated', () => ({
+ ...jest.requireActual('react-native-reanimated/mock'),
+ createAnimatedPropAdapter: jest.fn,
+}));
+
beforeAll(() => {
// In this test, we are generically mocking the responses of all API requests by mocking fetch() and having it
// return 200. In other tests, we might mock HttpUtils.xhr() with a more specific mock data response (which means
From 1ca211550fa96799569207938599244172616c94 Mon Sep 17 00:00:00 2001
From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com>
Date: Thu, 21 Dec 2023 13:04:26 +0100
Subject: [PATCH 2/7] fix bug: black color of the + icon on web
---
src/components/FloatingActionButton/FabPlusIcon.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/components/FloatingActionButton/FabPlusIcon.js b/src/components/FloatingActionButton/FabPlusIcon.js
index 500606522b3b..67e5df5dbe3e 100644
--- a/src/components/FloatingActionButton/FabPlusIcon.js
+++ b/src/components/FloatingActionButton/FabPlusIcon.js
@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React, {useEffect} from 'react';
+import {Platform} from 'react-native';
import Animated, {createAnimatedPropAdapter, Easing, interpolateColor, processColor, useAnimatedProps, useSharedValue, withTiming} from 'react-native-reanimated';
import Svg, {Path} from 'react-native-svg';
import useTheme from '@hooks/useTheme';
@@ -51,8 +52,8 @@ function FabPlusIcon({isActive}) {
fill,
};
},
- [],
- adapter,
+ undefined,
+ Platform.OS === 'web' ? undefined : adapter,
);
return (
From df210d5cde63b96ef292db4039df9cb76a91ebbb Mon Sep 17 00:00:00 2001
From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com>
Date: Thu, 21 Dec 2023 13:38:45 +0100
Subject: [PATCH 3/7] fix bug: the animation of the + icon not working on web
---
.../index.js => FloatingActionButton.js} | 52 ++++++++++++-
.../FloatingActionButton/FabPlusIcon.js | 75 -------------------
2 files changed, 48 insertions(+), 79 deletions(-)
rename src/components/{FloatingActionButton/index.js => FloatingActionButton.js} (60%)
delete mode 100644 src/components/FloatingActionButton/FabPlusIcon.js
diff --git a/src/components/FloatingActionButton/index.js b/src/components/FloatingActionButton.js
similarity index 60%
rename from src/components/FloatingActionButton/index.js
rename to src/components/FloatingActionButton.js
index 6922c8b1949b..98862d422cd0 100644
--- a/src/components/FloatingActionButton/index.js
+++ b/src/components/FloatingActionButton.js
@@ -1,17 +1,41 @@
import PropTypes from 'prop-types';
import React, {useEffect, useRef} from 'react';
import {View} from 'react-native';
-import Animated, {Easing, interpolateColor, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import {Platform} from 'react-native';
+import Animated, {createAnimatedPropAdapter, Easing, interpolateColor, processColor, useAnimatedProps, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
+import Svg, {Path} from 'react-native-svg';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
-import FabPlusIcon from './FabPlusIcon';
+import variables from '@styles/variables';
+
+const AnimatedPath = Animated.createAnimatedComponent(Path);
const AnimatedPressable = Animated.createAnimatedComponent(PressableWithFeedback);
AnimatedPressable.displayName = 'AnimatedPressable';
+const adapter = createAnimatedPropAdapter(
+ (props) => {
+ // eslint-disable-next-line rulesdir/prefer-underscore-method
+ if (Object.keys(props).includes('fill')) {
+ // eslint-disable-next-line no-param-reassign
+ props.fill = {type: 0, payload: processColor(props.fill)};
+ }
+ // eslint-disable-next-line rulesdir/prefer-underscore-method
+ if (Object.keys(props).includes('stroke')) {
+ // eslint-disable-next-line no-param-reassign
+ props.stroke = {type: 0, payload: processColor(props.stroke)};
+ }
+ },
+ ['fill', 'stroke'],
+);
+adapter.propTypes = {
+ fill: PropTypes.string,
+ stroke: PropTypes.string,
+};
+
const propTypes = {
/* Callback to fire on request to toggle the FloatingActionButton */
onPress: PropTypes.func.isRequired,
@@ -27,7 +51,7 @@ const propTypes = {
};
const FloatingActionButton = React.forwardRef(({onPress, isActive, accessibilityLabel, role}, ref) => {
- const {success, buttonDefaultBG} = useTheme();
+ const {success, buttonDefaultBG, textLight, textDark} = useTheme();
const styles = useThemeStyles();
const borderRadius = styles.floatingActionButton.borderRadius;
const {translate} = useLocalize();
@@ -52,6 +76,18 @@ const FloatingActionButton = React.forwardRef(({onPress, isActive, accessibility
};
});
+ const animatedProps = useAnimatedProps(
+ () => {
+ const fill = interpolateColor(sharedValue.value, [0, 1], [textLight, textDark]);
+
+ return {
+ fill,
+ };
+ },
+ undefined,
+ Platform.OS === 'web' ? undefined : adapter,
+ );
+
return (
@@ -73,7 +109,15 @@ const FloatingActionButton = React.forwardRef(({onPress, isActive, accessibility
onLongPress={() => {}}
style={[styles.floatingActionButton, animatedStyle]}
>
-
+
diff --git a/src/components/FloatingActionButton/FabPlusIcon.js b/src/components/FloatingActionButton/FabPlusIcon.js
deleted file mode 100644
index 67e5df5dbe3e..000000000000
--- a/src/components/FloatingActionButton/FabPlusIcon.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {useEffect} from 'react';
-import {Platform} from 'react-native';
-import Animated, {createAnimatedPropAdapter, Easing, interpolateColor, processColor, useAnimatedProps, useSharedValue, withTiming} from 'react-native-reanimated';
-import Svg, {Path} from 'react-native-svg';
-import useTheme from '@hooks/useTheme';
-import variables from '@styles/variables';
-
-const AnimatedPath = Animated.createAnimatedComponent(Path);
-
-const adapter = createAnimatedPropAdapter(
- (props) => {
- // eslint-disable-next-line rulesdir/prefer-underscore-method
- if (Object.keys(props).includes('fill')) {
- // eslint-disable-next-line no-param-reassign
- props.fill = {type: 0, payload: processColor(props.fill)};
- }
- // eslint-disable-next-line rulesdir/prefer-underscore-method
- if (Object.keys(props).includes('stroke')) {
- // eslint-disable-next-line no-param-reassign
- props.stroke = {type: 0, payload: processColor(props.stroke)};
- }
- },
- ['fill', 'stroke'],
-);
-adapter.propTypes = {
- fill: PropTypes.string,
- stroke: PropTypes.string,
-};
-
-const propTypes = {
- /* Current state (active or not active) of the component */
- isActive: PropTypes.bool.isRequired,
-};
-
-function FabPlusIcon({isActive}) {
- const {textLight, textDark} = useTheme();
- const sharedValue = useSharedValue(isActive ? 1 : 0);
-
- useEffect(() => {
- sharedValue.value = withTiming(isActive ? 1 : 0, {
- duration: 340,
- easing: Easing.inOut(Easing.ease),
- });
- }, [isActive, sharedValue]);
-
- const animatedProps = useAnimatedProps(
- () => {
- const fill = interpolateColor(sharedValue.value, [0, 1], [textLight, textDark]);
-
- return {
- fill,
- };
- },
- undefined,
- Platform.OS === 'web' ? undefined : adapter,
- );
-
- return (
-
- );
-}
-
-FabPlusIcon.propTypes = propTypes;
-FabPlusIcon.displayName = 'FabPlusIcon';
-
-export default FabPlusIcon;
From 677e29252d34ff85678bed8e243dcc1ebd1c96c3 Mon Sep 17 00:00:00 2001
From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com>
Date: Thu, 21 Dec 2023 13:53:04 +0100
Subject: [PATCH 4/7] add displayName to AnimatedPath
---
src/components/FloatingActionButton.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js
index 98862d422cd0..f52260045336 100644
--- a/src/components/FloatingActionButton.js
+++ b/src/components/FloatingActionButton.js
@@ -12,6 +12,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
const AnimatedPath = Animated.createAnimatedComponent(Path);
+AnimatedPath.displayName = 'AnimatedPath';
const AnimatedPressable = Animated.createAnimatedComponent(PressableWithFeedback);
AnimatedPressable.displayName = 'AnimatedPressable';
From 3354e9e840a997816a38db87abbdfd49f8be03c9 Mon Sep 17 00:00:00 2001
From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com>
Date: Thu, 21 Dec 2023 13:59:03 +0100
Subject: [PATCH 5/7] Migrate Icon/index.tsx to function component
---
src/components/Icon/index.tsx | 112 ++++++++++++++++------------------
1 file changed, 54 insertions(+), 58 deletions(-)
diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx
index 59b1639dce33..5f82421c0e8e 100644
--- a/src/components/Icon/index.tsx
+++ b/src/components/Icon/index.tsx
@@ -1,8 +1,8 @@
-import React, {PureComponent} from 'react';
+import React from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
-import withStyleUtils, {WithStyleUtilsProps} from '@components/withStyleUtils';
-import withTheme, {WithThemeProps} from '@components/withTheme';
-import withThemeStyles, {type WithThemeStylesProps} from '@components/withThemeStyles';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import IconWrapperStyles from './IconWrapperStyles';
@@ -41,67 +41,63 @@ type IconProps = {
/** Additional styles to add to the Icon */
additionalStyles?: StyleProp;
-} & WithThemeStylesProps &
- WithThemeProps &
- WithStyleUtilsProps;
-
-// We must use a class component to create an animatable component with the Animated API
-// eslint-disable-next-line react/prefer-stateless-function
-class Icon extends PureComponent {
- // eslint-disable-next-line react/static-property-placement
- public static defaultProps = {
- width: variables.iconSizeNormal,
- height: variables.iconSizeNormal,
- fill: undefined,
- small: false,
- inline: false,
- additionalStyles: [],
- hovered: false,
- pressed: false,
- };
-
- render() {
- const width = this.props.small ? variables.iconSizeSmall : this.props.width;
- const height = this.props.small ? variables.iconSizeSmall : this.props.height;
- const iconStyles = [this.props.StyleUtils.getWidthAndHeightStyle(width ?? 0, height), IconWrapperStyles, this.props.themeStyles.pAbsolute, this.props.additionalStyles];
- const fill = this.props.fill ?? this.props.theme.icon;
-
- if (this.props.inline) {
- return (
-
-
-
-
-
- );
- }
+};
+function Icon({
+ src,
+ width = variables.iconSizeNormal,
+ height = variables.iconSizeNormal,
+ fill = undefined,
+ small = false,
+ inline = false,
+ hovered = false,
+ pressed = false,
+ additionalStyles = [],
+}: IconProps) {
+ const theme = useTheme();
+ const StyleUtils = useStyleUtils();
+ const styles = useThemeStyles();
+ const iconWidth = small ? variables.iconSizeSmall : width;
+ const iconHeight = small ? variables.iconSizeSmall : height;
+ const iconStyles = [StyleUtils.getWidthAndHeightStyle(width ?? 0, height), IconWrapperStyles, styles.pAbsolute, additionalStyles];
+ const iconFill = fill ?? theme.icon;
+ const IconComponent = src;
+
+ if (inline) {
return (
-
+
+
+
);
}
+
+ return (
+
+
+
+ );
}
-export type {SrcProps};
+Icon.displayName = 'Icon';
-export default withTheme(withThemeStyles(withStyleUtils(Icon)));
+export default Icon;
From 9885e6930c3d9448be9aff4922b7e253ff529130 Mon Sep 17 00:00:00 2001
From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com>
Date: Thu, 21 Dec 2023 14:02:27 +0100
Subject: [PATCH 6/7] export type {SrcProps} from Icon component
---
src/components/Icon/index.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx
index 5f82421c0e8e..932ea17541b9 100644
--- a/src/components/Icon/index.tsx
+++ b/src/components/Icon/index.tsx
@@ -100,4 +100,6 @@ function Icon({
Icon.displayName = 'Icon';
+export type {SrcProps};
+
export default Icon;
From 6b6634d6e5acda5669c679b018e946b979ab20f5 Mon Sep 17 00:00:00 2001
From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com>
Date: Thu, 21 Dec 2023 14:05:58 +0100
Subject: [PATCH 7/7] fix lint errors
---
src/components/FloatingActionButton.js | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js
index f52260045336..59e741001063 100644
--- a/src/components/FloatingActionButton.js
+++ b/src/components/FloatingActionButton.js
@@ -1,15 +1,14 @@
import PropTypes from 'prop-types';
import React, {useEffect, useRef} from 'react';
-import {View} from 'react-native';
-import {Platform} from 'react-native';
+import {Platform, View} from 'react-native';
import Animated, {createAnimatedPropAdapter, Easing, interpolateColor, processColor, useAnimatedProps, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import Svg, {Path} from 'react-native-svg';
-import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
-import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
+import PressableWithFeedback from './Pressable/PressableWithFeedback';
+import Tooltip from './Tooltip/PopoverAnchorTooltip';
const AnimatedPath = Animated.createAnimatedComponent(Path);
AnimatedPath.displayName = 'AnimatedPath';