Skip to content

Commit ced121a

Browse files
authored
Merge pull request #55478 from software-mansion-labs/feature/kuba_nowakowski/add_animation_for_switch_childreans
Add animation for switch children
2 parents ac8e232 + a278e11 commit ced121a

File tree

13 files changed

+433
-201
lines changed

13 files changed

+433
-201
lines changed
+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import type {ReactNode} from 'react';
2+
import React from 'react';
3+
import type {StyleProp, ViewStyle} from 'react-native';
4+
import {View} from 'react-native';
5+
import type {SharedValue} from 'react-native-reanimated';
6+
import Animated, {Easing, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming} from 'react-native-reanimated';
7+
import useThemeStyles from '@hooks/useThemeStyles';
8+
9+
type AccordionProps = {
10+
/** Giving information whether the component is open */
11+
isExpanded: SharedValue<boolean>;
12+
13+
/** Element that is inside Accordion */
14+
children: ReactNode;
15+
16+
/** Duration of expansion animation */
17+
duration?: number;
18+
19+
/** Additional external style */
20+
style?: StyleProp<ViewStyle>;
21+
22+
/** Was toggle triggered */
23+
isToggleTriggered: SharedValue<boolean>;
24+
};
25+
26+
function Accordion({isExpanded, children, duration = 300, isToggleTriggered, style}: AccordionProps) {
27+
const height = useSharedValue(0);
28+
const styles = useThemeStyles();
29+
30+
const derivedHeight = useDerivedValue(() => {
31+
if (!isToggleTriggered.get()) {
32+
return isExpanded.get() ? height.get() : 0;
33+
}
34+
35+
return withTiming(height.get() * Number(isExpanded.get()), {
36+
duration,
37+
easing: Easing.inOut(Easing.quad),
38+
});
39+
});
40+
41+
const derivedOpacity = useDerivedValue(() => {
42+
if (!isToggleTriggered.get()) {
43+
return isExpanded.get() ? 1 : 0;
44+
}
45+
46+
return withTiming(isExpanded.get() ? 1 : 0, {
47+
duration,
48+
easing: Easing.inOut(Easing.quad),
49+
});
50+
});
51+
52+
const animatedStyle = useAnimatedStyle(() => {
53+
if (!isToggleTriggered.get() && !isExpanded.get()) {
54+
return {
55+
height: 0,
56+
opacity: 0,
57+
};
58+
}
59+
return {
60+
height: !isToggleTriggered.get() ? height.get() : derivedHeight.get(),
61+
opacity: derivedOpacity.get(),
62+
};
63+
});
64+
65+
return (
66+
<Animated.View style={[animatedStyle, style]}>
67+
<View
68+
onLayout={(e) => {
69+
height.set(e.nativeEvent.layout.height);
70+
}}
71+
style={[styles.pAbsolute, styles.l0, styles.r0, styles.t0]}
72+
>
73+
{children}
74+
</View>
75+
</Animated.View>
76+
);
77+
}
78+
79+
Accordion.displayName = 'Accordion';
80+
81+
export default Accordion;

src/components/Accordion/index.tsx

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import type {ReactNode} from 'react';
2+
import React from 'react';
3+
import type {StyleProp, ViewStyle} from 'react-native';
4+
import {View} from 'react-native';
5+
import type {SharedValue} from 'react-native-reanimated';
6+
import Animated, {Easing, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming} from 'react-native-reanimated';
7+
8+
type AccordionProps = {
9+
/** Giving information whether the component is open */
10+
isExpanded: SharedValue<boolean>;
11+
12+
/** Element that is inside Accordion */
13+
children: ReactNode;
14+
15+
/** Duration of expansion animation */
16+
duration?: number;
17+
18+
/** Additional external style */
19+
style?: StyleProp<ViewStyle>;
20+
21+
/** Was toggle triggered */
22+
isToggleTriggered: SharedValue<boolean>;
23+
};
24+
25+
function Accordion({isExpanded, children, duration = 300, isToggleTriggered, style}: AccordionProps) {
26+
const height = useSharedValue(0);
27+
28+
const derivedHeight = useDerivedValue(() => {
29+
if (!isToggleTriggered.get()) {
30+
return isExpanded.get() ? height.get() : 0;
31+
}
32+
33+
return withTiming(height.get() * Number(isExpanded.get()), {
34+
duration,
35+
easing: Easing.inOut(Easing.quad),
36+
});
37+
});
38+
39+
const derivedOpacity = useDerivedValue(() => {
40+
if (!isToggleTriggered.get()) {
41+
return isExpanded.get() ? 1 : 0;
42+
}
43+
44+
return withTiming(isExpanded.get() ? 1 : 0, {
45+
duration,
46+
easing: Easing.inOut(Easing.quad),
47+
});
48+
});
49+
50+
const animatedStyle = useAnimatedStyle(() => {
51+
if (!isToggleTriggered.get() && !isExpanded.get()) {
52+
return {
53+
height: 0,
54+
opacity: 0,
55+
};
56+
}
57+
58+
return {
59+
height: !isToggleTriggered.get() ? undefined : derivedHeight.get(),
60+
opacity: derivedOpacity.get(),
61+
};
62+
});
63+
64+
return (
65+
<Animated.View style={[animatedStyle, style]}>
66+
<View
67+
onLayout={(e) => {
68+
height.set(e.nativeEvent.layout.height);
69+
}}
70+
>
71+
{children}
72+
</View>
73+
</Animated.View>
74+
);
75+
}
76+
Accordion.displayName = 'Accordion';
77+
78+
export default Accordion;

src/libs/Navigation/linkTo/index.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,13 @@ export default function linkTo(navigation: NavigationContainerRef<RootStackParam
183183
}
184184
}
185185

186-
if (action && 'payload' in action && action.payload && 'name' in action.payload && isSideModalNavigator(action.payload.name)) {
186+
if (
187+
action &&
188+
'payload' in action &&
189+
action.payload &&
190+
'name' in action.payload &&
191+
(isSideModalNavigator(action.payload.name) || action.payload.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR)
192+
) {
187193
// Information about the state may be in the params.
188194
const currentFocusedRoute = findFocusedRoute(extrapolateStateFromParams(rootState));
189195
const targetFocusedRoute = findFocusedRoute(stateFromPath);
@@ -201,7 +207,7 @@ export default function linkTo(navigation: NavigationContainerRef<RootStackParam
201207
// There are situations where a route already exists on the current navigation stack
202208
// But we want to push the same route instead of going back in the stack
203209
// Which would break the user navigation history
204-
if (!isActiveRoute && type === CONST.NAVIGATION.ACTION_TYPE.PUSH) {
210+
if ((!isActiveRoute && type === CONST.NAVIGATION.ACTION_TYPE.PUSH) || action.payload.name === NAVIGATORS.FULL_SCREEN_NAVIGATOR) {
205211
minimalAction.type = CONST.NAVIGATION.ACTION_TYPE.PUSH;
206212
}
207213
root.dispatch(minimalAction);

src/libs/PolicyUtils.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1063,9 +1063,9 @@ function removePendingFieldsFromCustomUnit(customUnit: CustomUnit): CustomUnit {
10631063
return cleanedCustomUnit;
10641064
}
10651065

1066-
function navigateWhenEnableFeature(policyID: string) {
1066+
function goBackWhenEnableFeature(policyID: string) {
10671067
setTimeout(() => {
1068-
Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID));
1068+
Navigation.goBack(ROUTES.WORKSPACE_INITIAL.getRoute(policyID));
10691069
}, CONST.WORKSPACE_ENABLE_FEATURE_REDIRECT_DELAY);
10701070
}
10711071

@@ -1355,7 +1355,7 @@ export {
13551355
getDistanceRateCustomUnitRate,
13561356
sortWorkspacesBySelected,
13571357
removePendingFieldsFromCustomUnit,
1358-
navigateWhenEnableFeature,
1358+
goBackWhenEnableFeature,
13591359
getIntegrationLastSuccessfulDate,
13601360
getCurrentConnectionName,
13611361
getCustomersOrJobsLabelNetSuite,

src/libs/actions/Policy/Category.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {translateLocal} from '@libs/Localize';
2727
import Log from '@libs/Log';
2828
import enhanceParameters from '@libs/Network/enhanceParameters';
2929
import {hasEnabledOptions} from '@libs/OptionsListUtils';
30-
import {getPolicy, navigateWhenEnableFeature} from '@libs/PolicyUtils';
30+
import {getPolicy, goBackWhenEnableFeature} from '@libs/PolicyUtils';
3131
import {getAllPolicyReports} from '@libs/ReportUtils';
3232
import CONST from '@src/CONST';
3333
import ONYXKEYS from '@src/ONYXKEYS';
@@ -958,7 +958,7 @@ function deleteWorkspaceCategories(policyID: string, categoryNamesToDelete: stri
958958
API.write(WRITE_COMMANDS.DELETE_WORKSPACE_CATEGORIES, parameters, onyxData);
959959
}
960960

961-
function enablePolicyCategories(policyID: string, enabled: boolean, shouldNavigate = true) {
961+
function enablePolicyCategories(policyID: string, enabled: boolean, shouldGoBack = true) {
962962
const onyxUpdatesToDisableCategories: OnyxUpdate[] = [];
963963
if (!enabled) {
964964
onyxUpdatesToDisableCategories.push(
@@ -1029,8 +1029,8 @@ function enablePolicyCategories(policyID: string, enabled: boolean, shouldNaviga
10291029

10301030
API.write(WRITE_COMMANDS.ENABLE_POLICY_CATEGORIES, parameters, onyxData);
10311031

1032-
if (enabled && getIsNarrowLayout() && shouldNavigate) {
1033-
navigateWhenEnableFeature(policyID);
1032+
if (enabled && getIsNarrowLayout() && shouldGoBack) {
1033+
goBackWhenEnableFeature(policyID);
10341034
}
10351035
}
10361036

src/libs/actions/Policy/DistanceRate.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type {
1313
import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
1414
import * as ErrorUtils from '@libs/ErrorUtils';
1515
import getIsNarrowLayout from '@libs/getIsNarrowLayout';
16-
import {getDistanceRateCustomUnit, navigateWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/PolicyUtils';
16+
import {getDistanceRateCustomUnit, goBackWhenEnableFeature, removePendingFieldsFromCustomUnit} from '@libs/PolicyUtils';
1717
import * as ReportUtils from '@libs/ReportUtils';
1818
import CONST from '@src/CONST';
1919
import ONYXKEYS from '@src/ONYXKEYS';
@@ -166,7 +166,7 @@ function enablePolicyDistanceRates(policyID: string, enabled: boolean) {
166166
API.write(WRITE_COMMANDS.ENABLE_POLICY_DISTANCE_RATES, parameters, onyxData);
167167

168168
if (enabled && getIsNarrowLayout()) {
169-
navigateWhenEnableFeature(policyID);
169+
goBackWhenEnableFeature(policyID);
170170
}
171171
}
172172

src/libs/actions/Policy/PerDiem.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import getIsNarrowLayout from '@libs/getIsNarrowLayout';
1010
import {translateLocal} from '@libs/Localize';
1111
import enhanceParameters from '@libs/Network/enhanceParameters';
1212
import * as NumberUtils from '@libs/NumberUtils';
13-
import {navigateWhenEnableFeature} from '@libs/PolicyUtils';
13+
import {goBackWhenEnableFeature} from '@libs/PolicyUtils';
1414
import * as ReportUtils from '@libs/ReportUtils';
1515
import CONST from '@src/CONST';
1616
import ONYXKEYS from '@src/ONYXKEYS';
@@ -70,7 +70,7 @@ function generateCustomUnitID(): string {
7070
return NumberUtils.generateHexadecimalValue(13);
7171
}
7272

73-
function enablePerDiem(policyID: string, enabled: boolean, customUnitID?: string, disableRedirect?: boolean) {
73+
function enablePerDiem(policyID: string, enabled: boolean, customUnitID?: string, shouldGoBack?: boolean) {
7474
const doesCustomUnitExists = !!customUnitID;
7575
const finalCustomUnitID = doesCustomUnitExists ? customUnitID : generateCustomUnitID();
7676
const optimisticCustomUnit = {
@@ -123,8 +123,8 @@ function enablePerDiem(policyID: string, enabled: boolean, customUnitID?: string
123123

124124
API.write(WRITE_COMMANDS.TOGGLE_POLICY_PER_DIEM, parameters, onyxData);
125125

126-
if (enabled && getIsNarrowLayout() && !disableRedirect) {
127-
navigateWhenEnableFeature(policyID);
126+
if (enabled && getIsNarrowLayout() && shouldGoBack) {
127+
goBackWhenEnableFeature(policyID);
128128
}
129129
}
130130

src/libs/actions/Policy/Policy.ts

+15-15
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ import * as NumberUtils from '@libs/NumberUtils';
7373
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
7474
import * as PhoneNumber from '@libs/PhoneNumber';
7575
import * as PolicyUtils from '@libs/PolicyUtils';
76-
import {navigateWhenEnableFeature} from '@libs/PolicyUtils';
76+
import {goBackWhenEnableFeature} from '@libs/PolicyUtils';
7777
import * as ReportUtils from '@libs/ReportUtils';
7878
import type {PolicySelector} from '@pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover';
7979
import * as PaymentMethods from '@userActions/PaymentMethods';
@@ -2796,7 +2796,7 @@ function enablePolicyConnections(policyID: string, enabled: boolean) {
27962796
API.write(WRITE_COMMANDS.ENABLE_POLICY_CONNECTIONS, parameters, onyxData);
27972797

27982798
if (enabled && getIsNarrowLayout()) {
2799-
navigateWhenEnableFeature(policyID);
2799+
goBackWhenEnableFeature(policyID);
28002800
}
28012801
}
28022802

@@ -2853,11 +2853,11 @@ function enableExpensifyCard(policyID: string, enabled: boolean) {
28532853
API.write(WRITE_COMMANDS.ENABLE_POLICY_EXPENSIFY_CARDS, parameters, onyxData);
28542854

28552855
if (enabled && getIsNarrowLayout()) {
2856-
navigateWhenEnableFeature(policyID);
2856+
goBackWhenEnableFeature(policyID);
28572857
}
28582858
}
28592859

2860-
function enableCompanyCards(policyID: string, enabled: boolean, disableRedirect = false) {
2860+
function enableCompanyCards(policyID: string, enabled: boolean, shouldGoBack = true) {
28612861
const authToken = NetworkStore.getAuthToken();
28622862

28632863
const onyxData: OnyxData = {
@@ -2902,12 +2902,12 @@ function enableCompanyCards(policyID: string, enabled: boolean, disableRedirect
29022902

29032903
API.write(WRITE_COMMANDS.ENABLE_POLICY_COMPANY_CARDS, parameters, onyxData);
29042904

2905-
if (enabled && getIsNarrowLayout() && !disableRedirect) {
2906-
navigateWhenEnableFeature(policyID);
2905+
if (enabled && getIsNarrowLayout() && shouldGoBack) {
2906+
goBackWhenEnableFeature(policyID);
29072907
}
29082908
}
29092909

2910-
function enablePolicyReportFields(policyID: string, enabled: boolean, disableRedirect = false) {
2910+
function enablePolicyReportFields(policyID: string, enabled: boolean, shouldGoBack = true) {
29112911
const onyxData: OnyxData = {
29122912
optimisticData: [
29132913
{
@@ -2950,8 +2950,8 @@ function enablePolicyReportFields(policyID: string, enabled: boolean, disableRed
29502950

29512951
API.write(WRITE_COMMANDS.ENABLE_POLICY_REPORT_FIELDS, parameters, onyxData);
29522952

2953-
if (enabled && getIsNarrowLayout() && !disableRedirect) {
2954-
navigateWhenEnableFeature(policyID);
2953+
if (enabled && getIsNarrowLayout() && shouldGoBack) {
2954+
goBackWhenEnableFeature(policyID);
29552955
}
29562956
}
29572957

@@ -3065,7 +3065,7 @@ function enablePolicyTaxes(policyID: string, enabled: boolean) {
30653065
API.write(WRITE_COMMANDS.ENABLE_POLICY_TAXES, parameters, onyxData);
30663066

30673067
if (enabled && getIsNarrowLayout()) {
3068-
navigateWhenEnableFeature(policyID);
3068+
goBackWhenEnableFeature(policyID);
30693069
}
30703070
}
30713071

@@ -3156,7 +3156,7 @@ function enablePolicyWorkflows(policyID: string, enabled: boolean) {
31563156
API.write(WRITE_COMMANDS.ENABLE_POLICY_WORKFLOWS, parameters, onyxData);
31573157

31583158
if (enabled && getIsNarrowLayout()) {
3159-
navigateWhenEnableFeature(policyID);
3159+
goBackWhenEnableFeature(policyID);
31603160
}
31613161
}
31623162

@@ -3166,7 +3166,7 @@ const DISABLED_MAX_EXPENSE_VALUES: Pick<Policy, 'maxExpenseAmountNoReceipt' | 'm
31663166
maxExpenseAge: CONST.DISABLED_MAX_EXPENSE_VALUE,
31673167
};
31683168

3169-
function enablePolicyRules(policyID: string, enabled: boolean, disableRedirect = false) {
3169+
function enablePolicyRules(policyID: string, enabled: boolean, shouldGoBack = true) {
31703170
const policy = getPolicy(policyID);
31713171
const onyxData: OnyxData = {
31723172
optimisticData: [
@@ -3219,8 +3219,8 @@ function enablePolicyRules(policyID: string, enabled: boolean, disableRedirect =
32193219
const parameters: SetPolicyRulesEnabledParams = {policyID, enabled};
32203220
API.write(WRITE_COMMANDS.SET_POLICY_RULES_ENABLED, parameters, onyxData);
32213221

3222-
if (enabled && getIsNarrowLayout() && !disableRedirect) {
3223-
navigateWhenEnableFeature(policyID);
3222+
if (enabled && getIsNarrowLayout() && shouldGoBack) {
3223+
goBackWhenEnableFeature(policyID);
32243224
}
32253225
}
32263226

@@ -3331,7 +3331,7 @@ function enablePolicyInvoicing(policyID: string, enabled: boolean) {
33313331
API.write(WRITE_COMMANDS.ENABLE_POLICY_INVOICING, parameters, onyxData);
33323332

33333333
if (enabled && getIsNarrowLayout()) {
3334-
navigateWhenEnableFeature(policyID);
3334+
goBackWhenEnableFeature(policyID);
33353335
}
33363336
}
33373337

0 commit comments

Comments
 (0)