@pagopa/io-app-design-system 4.5.6 → 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/accordion/AccordionItem.js +20 -56
- package/lib/commonjs/components/accordion/AccordionItem.js.map +1 -1
- package/lib/commonjs/components/claimsSelector/ClaimsSelector.js +119 -0
- package/lib/commonjs/components/claimsSelector/ClaimsSelector.js.map +1 -0
- package/lib/commonjs/components/claimsSelector/__test__/ClaimsSelector.test.js +46 -0
- package/lib/commonjs/components/claimsSelector/__test__/ClaimsSelector.test.js.map +1 -0
- package/lib/commonjs/components/claimsSelector/__test__/__snapshots__/ClaimsSelector.test.tsx.snap +1270 -0
- package/lib/commonjs/components/claimsSelector/index.js +17 -0
- package/lib/commonjs/components/claimsSelector/index.js.map +1 -0
- package/lib/commonjs/components/index.js +11 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/components/listitems/ListItemInfo.js +6 -2
- package/lib/commonjs/components/listitems/ListItemInfo.js.map +1 -1
- package/lib/commonjs/components/listitems/__test__/__snapshots__/listitem.test.tsx.snap +10 -0
- package/lib/commonjs/components/textInput/TextInputValidation.js +19 -7
- package/lib/commonjs/components/textInput/TextInputValidation.js.map +1 -1
- package/lib/commonjs/components/typography/markdown/MdH1.js +1 -2
- package/lib/commonjs/components/typography/markdown/MdH1.js.map +1 -1
- package/lib/commonjs/components/typography/markdown/MdH2.js +2 -2
- package/lib/commonjs/components/typography/markdown/MdH2.js.map +1 -1
- package/lib/commonjs/components/typography/markdown/MdH3.js +2 -2
- package/lib/commonjs/components/typography/markdown/MdH3.js.map +1 -1
- package/lib/commonjs/core/IOColors.js +1 -1
- package/lib/commonjs/hooks/useAccordionAnimation.js +83 -0
- package/lib/commonjs/hooks/useAccordionAnimation.js.map +1 -0
- package/lib/commonjs/index.js +11 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/accordion/AccordionItem.js +19 -52
- package/lib/module/components/accordion/AccordionItem.js.map +1 -1
- package/lib/module/components/claimsSelector/ClaimsSelector.js +109 -0
- package/lib/module/components/claimsSelector/ClaimsSelector.js.map +1 -0
- package/lib/module/components/claimsSelector/__test__/ClaimsSelector.test.js +41 -0
- package/lib/module/components/claimsSelector/__test__/ClaimsSelector.test.js.map +1 -0
- package/lib/module/components/claimsSelector/__test__/__snapshots__/ClaimsSelector.test.tsx.snap +1270 -0
- package/lib/module/components/claimsSelector/index.js +2 -0
- package/lib/module/components/claimsSelector/index.js.map +1 -0
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/listitems/ListItemInfo.js +6 -2
- package/lib/module/components/listitems/ListItemInfo.js.map +1 -1
- package/lib/module/components/listitems/__test__/__snapshots__/listitem.test.tsx.snap +10 -0
- package/lib/module/components/textInput/TextInputValidation.js +20 -8
- package/lib/module/components/textInput/TextInputValidation.js.map +1 -1
- package/lib/module/components/typography/markdown/MdH1.js +1 -2
- package/lib/module/components/typography/markdown/MdH1.js.map +1 -1
- package/lib/module/components/typography/markdown/MdH2.js +2 -2
- package/lib/module/components/typography/markdown/MdH2.js.map +1 -1
- package/lib/module/components/typography/markdown/MdH3.js +2 -2
- package/lib/module/components/typography/markdown/MdH3.js.map +1 -1
- package/lib/module/core/IOColors.js +1 -1
- package/lib/module/hooks/useAccordionAnimation.js +76 -0
- package/lib/module/hooks/useAccordionAnimation.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/components/accordion/AccordionItem.d.ts +0 -6
- package/lib/typescript/components/accordion/AccordionItem.d.ts.map +1 -1
- package/lib/typescript/components/claimsSelector/ClaimsSelector.d.ts +42 -0
- package/lib/typescript/components/claimsSelector/ClaimsSelector.d.ts.map +1 -0
- package/lib/typescript/components/claimsSelector/__test__/ClaimsSelector.test.d.ts +2 -0
- package/lib/typescript/components/claimsSelector/__test__/ClaimsSelector.test.d.ts.map +1 -0
- package/lib/typescript/components/claimsSelector/index.d.ts +2 -0
- package/lib/typescript/components/claimsSelector/index.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/components/listitems/ListItemInfo.d.ts +2 -1
- package/lib/typescript/components/listitems/ListItemInfo.d.ts.map +1 -1
- package/lib/typescript/components/textInput/TextInputValidation.d.ts +31 -4
- package/lib/typescript/components/textInput/TextInputValidation.d.ts.map +1 -1
- package/lib/typescript/components/typography/markdown/MdH1.d.ts.map +1 -1
- package/lib/typescript/hooks/useAccordionAnimation.d.ts +41 -0
- package/lib/typescript/hooks/useAccordionAnimation.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/accordion/AccordionItem.tsx +21 -82
- package/src/components/claimsSelector/ClaimsSelector.tsx +185 -0
- package/src/components/claimsSelector/__test__/ClaimsSelector.test.tsx +55 -0
- package/src/components/claimsSelector/__test__/__snapshots__/ClaimsSelector.test.tsx.snap +1270 -0
- package/src/components/claimsSelector/index.tsx +1 -0
- package/src/components/index.tsx +1 -0
- package/src/components/listitems/ListItemInfo.tsx +7 -2
- package/src/components/listitems/__test__/__snapshots__/listitem.test.tsx.snap +10 -0
- package/src/components/textInput/TextInputValidation.tsx +140 -97
- package/src/components/typography/markdown/MdH1.tsx +1 -2
- package/src/components/typography/markdown/MdH2.tsx +2 -2
- package/src/components/typography/markdown/MdH3.tsx +2 -2
- package/src/core/IOColors.ts +1 -1
- package/src/hooks/useAccordionAnimation.tsx +106 -0
- package/src/index.tsx +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./ClaimsSelector";
|
package/src/components/index.tsx
CHANGED
|
@@ -60,6 +60,7 @@ export type ListItemInfo = WithTestID<{
|
|
|
60
60
|
// Accessibility
|
|
61
61
|
accessibilityLabel?: string;
|
|
62
62
|
accessibilityRole?: AccessibilityRole;
|
|
63
|
+
reversed?: boolean;
|
|
63
64
|
}> &
|
|
64
65
|
GraphicProps &
|
|
65
66
|
InteractiveProps;
|
|
@@ -70,6 +71,7 @@ export const ListItemInfo = ({
|
|
|
70
71
|
label,
|
|
71
72
|
value,
|
|
72
73
|
numberOfLines = 2,
|
|
74
|
+
reversed = false,
|
|
73
75
|
icon,
|
|
74
76
|
paymentLogoIcon,
|
|
75
77
|
endElement,
|
|
@@ -99,7 +101,10 @@ export const ListItemInfo = ({
|
|
|
99
101
|
|
|
100
102
|
const itemInfoTextComponent = useMemo(
|
|
101
103
|
() => (
|
|
102
|
-
<View
|
|
104
|
+
<View
|
|
105
|
+
accessible={Platform.OS === "ios"}
|
|
106
|
+
style={{ flexDirection: reversed ? "column-reverse" : "column" }}
|
|
107
|
+
>
|
|
103
108
|
<BodySmall weight="Regular" color={theme["textBody-tertiary"]}>
|
|
104
109
|
{label}
|
|
105
110
|
</BodySmall>
|
|
@@ -112,7 +117,7 @@ export const ListItemInfo = ({
|
|
|
112
117
|
)}
|
|
113
118
|
</View>
|
|
114
119
|
),
|
|
115
|
-
[label, value, numberOfLines, theme]
|
|
120
|
+
[label, value, numberOfLines, theme, reversed]
|
|
116
121
|
);
|
|
117
122
|
|
|
118
123
|
const listItemInfoAction = useCallback(() => {
|
|
@@ -239,6 +239,11 @@ exports[`Test List Item Components - Experimental Enabled ListItemInfo Snapshot
|
|
|
239
239
|
>
|
|
240
240
|
<View
|
|
241
241
|
accessible={true}
|
|
242
|
+
style={
|
|
243
|
+
{
|
|
244
|
+
"flexDirection": "column",
|
|
245
|
+
}
|
|
246
|
+
}
|
|
242
247
|
>
|
|
243
248
|
<Text
|
|
244
249
|
allowFontScaling={true}
|
|
@@ -1909,6 +1914,11 @@ exports[`Test List Item Components ListItemInfo Snapshot 1`] = `
|
|
|
1909
1914
|
>
|
|
1910
1915
|
<View
|
|
1911
1916
|
accessible={true}
|
|
1917
|
+
style={
|
|
1918
|
+
{
|
|
1919
|
+
"flexDirection": "column",
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1912
1922
|
>
|
|
1913
1923
|
<Text
|
|
1914
1924
|
allowFontScaling={true}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useCallback,
|
|
4
|
+
useMemo,
|
|
5
|
+
useState,
|
|
6
|
+
forwardRef,
|
|
7
|
+
useImperativeHandle
|
|
8
|
+
} from "react";
|
|
3
9
|
import { AccessibilityInfo, View } from "react-native";
|
|
4
10
|
import Animated from "react-native-reanimated";
|
|
5
11
|
import { useIOTheme } from "../../core";
|
|
@@ -28,6 +34,14 @@ type TextInputValidationProps = Omit<
|
|
|
28
34
|
* In case of a dynamic `errorMessage`, use the `onValidate` function with a `ValidationWithOptions` object as the return value to ensure that screen readers announce the correct value.
|
|
29
35
|
*/
|
|
30
36
|
errorMessage: string;
|
|
37
|
+
/**
|
|
38
|
+
* Determines the validation mode. If "onBlur", validation occurs on blur. If "onContinue", validation occurs when an external button is pressed.
|
|
39
|
+
*/
|
|
40
|
+
validationMode?: "onBlur" | "onContinue";
|
|
41
|
+
/**
|
|
42
|
+
* A string that will be read by screen readers when the field is not valid.
|
|
43
|
+
*/
|
|
44
|
+
accessibilityErrorLabel?: string;
|
|
31
45
|
};
|
|
32
46
|
|
|
33
47
|
function isValidationWithOptions(
|
|
@@ -42,107 +56,136 @@ function isValidationWithOptions(
|
|
|
42
56
|
|
|
43
57
|
const feedbackIconSize: IOIconSizeScale = 24;
|
|
44
58
|
|
|
45
|
-
export const TextInputValidation =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const labelError = useMemo(
|
|
89
|
-
() => (isValid === false && errMessage ? errMessage : bottomMessage),
|
|
90
|
-
[isValid, errMessage, bottomMessage]
|
|
91
|
-
);
|
|
59
|
+
export const TextInputValidation = forwardRef<
|
|
60
|
+
{
|
|
61
|
+
validateInput: () => void;
|
|
62
|
+
},
|
|
63
|
+
TextInputValidationProps
|
|
64
|
+
>(
|
|
65
|
+
(
|
|
66
|
+
{
|
|
67
|
+
onValidate,
|
|
68
|
+
errorMessage,
|
|
69
|
+
value,
|
|
70
|
+
bottomMessage,
|
|
71
|
+
onBlur,
|
|
72
|
+
onFocus,
|
|
73
|
+
validationMode = "onBlur",
|
|
74
|
+
accessibilityErrorLabel,
|
|
75
|
+
...props
|
|
76
|
+
},
|
|
77
|
+
ref
|
|
78
|
+
) => {
|
|
79
|
+
const theme = useIOTheme();
|
|
80
|
+
const [isValid, setIsValid] = useState<boolean | undefined>(undefined);
|
|
81
|
+
const [errMessage, setErrMessage] = useState(errorMessage);
|
|
82
|
+
|
|
83
|
+
const getErrorFeedback = useCallback(
|
|
84
|
+
(isValid: boolean, message: string) => {
|
|
85
|
+
setIsValid(isValid);
|
|
86
|
+
setErrMessage(message);
|
|
87
|
+
|
|
88
|
+
if (!isValid) {
|
|
89
|
+
triggerHaptic("notificationError");
|
|
90
|
+
AccessibilityInfo.announceForAccessibilityWithOptions(
|
|
91
|
+
accessibilityErrorLabel ?? message,
|
|
92
|
+
{
|
|
93
|
+
queue: true
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
} else {
|
|
97
|
+
triggerHaptic("notificationSuccess");
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
[accessibilityErrorLabel]
|
|
101
|
+
);
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
[isValid, errMessage, theme.errorText]
|
|
96
|
-
);
|
|
103
|
+
const validateInput = useCallback(() => {
|
|
104
|
+
const validation = onValidate(value);
|
|
97
105
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
() => ({
|
|
103
|
-
valid: {
|
|
104
|
-
name: "success",
|
|
105
|
-
color: theme.successIcon
|
|
106
|
-
},
|
|
107
|
-
notValid: {
|
|
108
|
-
name: "errorFilled",
|
|
109
|
-
color: theme.errorIcon
|
|
106
|
+
if (isValidationWithOptions(validation)) {
|
|
107
|
+
getErrorFeedback(validation.isValid, validation.errorMessage);
|
|
108
|
+
} else {
|
|
109
|
+
getErrorFeedback(validation, errorMessage);
|
|
110
110
|
}
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
}, [value, errorMessage, onValidate, getErrorFeedback]);
|
|
112
|
+
|
|
113
|
+
// Expose the validateInput function to the parent component
|
|
114
|
+
useImperativeHandle(ref, () => ({
|
|
115
|
+
validateInput
|
|
116
|
+
}));
|
|
114
117
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
<View style={{ width: feedbackIconSize, height: feedbackIconSize }} />
|
|
118
|
+
const onBlurHandler = useCallback(() => {
|
|
119
|
+
if (validationMode === "onBlur") {
|
|
120
|
+
validateInput();
|
|
121
|
+
}
|
|
122
|
+
onBlur?.();
|
|
123
|
+
}, [validationMode, validateInput, onBlur]);
|
|
124
|
+
|
|
125
|
+
const onFocusHandler = useCallback(() => {
|
|
126
|
+
setIsValid(undefined);
|
|
127
|
+
onFocus?.();
|
|
128
|
+
}, [onFocus]);
|
|
129
|
+
|
|
130
|
+
const labelError = useMemo(
|
|
131
|
+
() => (isValid === false && errMessage ? errMessage : bottomMessage),
|
|
132
|
+
[isValid, errMessage, bottomMessage]
|
|
131
133
|
);
|
|
132
|
-
}, [feedbackIconAttrMap, isValid]);
|
|
133
134
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
135
|
+
const labelErrorColor: IOColors | undefined = useMemo(
|
|
136
|
+
() => (isValid === false && errMessage ? theme.errorText : undefined),
|
|
137
|
+
[isValid, errMessage, theme.errorText]
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const feedbackIconAttrMap: Record<
|
|
141
|
+
string,
|
|
142
|
+
{ name: IOIcons; color: IOColors }
|
|
143
|
+
> = useMemo(
|
|
144
|
+
() => ({
|
|
145
|
+
valid: {
|
|
146
|
+
name: "success",
|
|
147
|
+
color: theme.successIcon
|
|
148
|
+
},
|
|
149
|
+
notValid: {
|
|
150
|
+
name: "errorFilled",
|
|
151
|
+
color: theme.errorIcon
|
|
152
|
+
}
|
|
153
|
+
}),
|
|
154
|
+
[theme]
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const feedbackIcon = useMemo(() => {
|
|
158
|
+
const validationStatus = isValid ? "valid" : "notValid";
|
|
159
|
+
|
|
160
|
+
return isValid !== undefined ? (
|
|
161
|
+
<Animated.View
|
|
162
|
+
entering={enterTransitionInputIcon}
|
|
163
|
+
exiting={exitTransitionInputIcon}
|
|
164
|
+
>
|
|
165
|
+
<Icon
|
|
166
|
+
name={feedbackIconAttrMap[validationStatus].name}
|
|
167
|
+
color={feedbackIconAttrMap[validationStatus].color}
|
|
168
|
+
size={feedbackIconSize}
|
|
169
|
+
/>
|
|
170
|
+
</Animated.View>
|
|
171
|
+
) : (
|
|
172
|
+
<View style={{ width: feedbackIconSize, height: feedbackIconSize }} />
|
|
173
|
+
);
|
|
174
|
+
}, [feedbackIconAttrMap, isValid]);
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<TextInputBase
|
|
178
|
+
{...props}
|
|
179
|
+
value={value}
|
|
180
|
+
status={isValid === false ? "error" : undefined}
|
|
181
|
+
bottomMessage={labelError}
|
|
182
|
+
bottomMessageColor={labelErrorColor}
|
|
183
|
+
rightElement={feedbackIcon}
|
|
184
|
+
onBlur={onBlurHandler}
|
|
185
|
+
onFocus={onFocusHandler}
|
|
186
|
+
/>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
);
|
|
147
190
|
|
|
148
191
|
export default TextInputValidation;
|
|
@@ -6,7 +6,6 @@ import { IOText, IOTextProps, TypographicStyleProps } from "../IOText";
|
|
|
6
6
|
/**
|
|
7
7
|
* `MdH1` typographic style
|
|
8
8
|
*/
|
|
9
|
-
|
|
10
9
|
export const MdH1 = forwardRef<View, TypographicStyleProps>(
|
|
11
10
|
({ color: customColor, ...props }, ref?: ForwardedRef<View>) => {
|
|
12
11
|
const theme = useIOTheme();
|
|
@@ -18,7 +17,7 @@ export const MdH1 = forwardRef<View, TypographicStyleProps>(
|
|
|
18
17
|
weight: "Semibold",
|
|
19
18
|
size: 20,
|
|
20
19
|
lineHeight: 24,
|
|
21
|
-
color: customColor ?? theme["textHeading-
|
|
20
|
+
color: customColor ?? theme["textHeading-default"]
|
|
22
21
|
};
|
|
23
22
|
|
|
24
23
|
return (
|
|
@@ -15,9 +15,9 @@ export const MdH2 = forwardRef<View, TypographicStyleProps>(
|
|
|
15
15
|
...props,
|
|
16
16
|
font: isExperimental ? "Titillio" : "TitilliumSansPro",
|
|
17
17
|
weight: "Semibold",
|
|
18
|
-
size:
|
|
18
|
+
size: 18,
|
|
19
19
|
lineHeight: 24,
|
|
20
|
-
color: customColor ?? theme["textHeading-
|
|
20
|
+
color: customColor ?? theme["textHeading-default"]
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
return (
|
|
@@ -14,10 +14,10 @@ export const MdH3 = forwardRef<View, TypographicStyleProps>(
|
|
|
14
14
|
const MdH3Props: IOTextProps = {
|
|
15
15
|
...props,
|
|
16
16
|
font: isExperimental ? "Titillio" : "TitilliumSansPro",
|
|
17
|
-
weight: "
|
|
17
|
+
weight: "Semibold",
|
|
18
18
|
size: 16,
|
|
19
19
|
lineHeight: 24,
|
|
20
|
-
color: customColor ?? theme["textHeading-
|
|
20
|
+
color: customColor ?? theme["textHeading-default"]
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
return (
|
package/src/core/IOColors.ts
CHANGED
|
@@ -381,7 +381,7 @@ export const IOThemeDark: IOTheme = {
|
|
|
381
381
|
"textHeading-tertiary": "grey-450",
|
|
382
382
|
"textBody-default": "white",
|
|
383
383
|
"textBody-secondary": "grey-100",
|
|
384
|
-
"textBody-tertiary": "grey-
|
|
384
|
+
"textBody-tertiary": "grey-300",
|
|
385
385
|
// Design System related
|
|
386
386
|
"cardBorder-default": "grey-850",
|
|
387
387
|
"icon-default": "grey-450",
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { StyleSheet, type LayoutChangeEvent } from "react-native";
|
|
3
|
+
import {
|
|
4
|
+
useAnimatedStyle,
|
|
5
|
+
useSharedValue,
|
|
6
|
+
withSpring
|
|
7
|
+
} from "react-native-reanimated";
|
|
8
|
+
import { IOSpacingScale, IOSpringValues } from "../core";
|
|
9
|
+
|
|
10
|
+
const accordionBodySpacing: IOSpacingScale = 16;
|
|
11
|
+
|
|
12
|
+
type Params = {
|
|
13
|
+
defaultExpanded?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const useAccordionAnimation = ({
|
|
17
|
+
defaultExpanded = false
|
|
18
|
+
}: Params = {}) => {
|
|
19
|
+
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
20
|
+
// Disable animation when starting expanded
|
|
21
|
+
const animationEnabled = useSharedValue(!defaultExpanded);
|
|
22
|
+
const bodyHeight = useSharedValue(0);
|
|
23
|
+
|
|
24
|
+
const toggleAccordion = useCallback(() => {
|
|
25
|
+
// Re-enable animation when the user interacts with the accordion
|
|
26
|
+
animationEnabled.value = true; // eslint-disable-line functional/immutable-data
|
|
27
|
+
setExpanded(expanded => !expanded);
|
|
28
|
+
}, [animationEnabled]);
|
|
29
|
+
|
|
30
|
+
const iconAnimatedStyle = useAnimatedStyle(
|
|
31
|
+
() => ({
|
|
32
|
+
transform: [
|
|
33
|
+
{
|
|
34
|
+
rotate: withSpring(
|
|
35
|
+
expanded ? "180deg" : "0deg",
|
|
36
|
+
IOSpringValues.accordion
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}),
|
|
41
|
+
[expanded]
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// The code below is a re-adaptation of Dima Portenko's code:
|
|
45
|
+
// https://github.com/dimaportenko/reanimated-collapsable-card-tutorial
|
|
46
|
+
const onBodyLayout = useCallback(
|
|
47
|
+
(event: LayoutChangeEvent) => {
|
|
48
|
+
const { height: onLayoutHeight } = event.nativeEvent.layout;
|
|
49
|
+
|
|
50
|
+
if (onLayoutHeight > 0 && bodyHeight.value !== onLayoutHeight) {
|
|
51
|
+
bodyHeight.value = onLayoutHeight; // eslint-disable-line functional/immutable-data
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[bodyHeight]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const bodyAnimatedHeight = useAnimatedStyle(() => {
|
|
58
|
+
const nextHeight = expanded ? bodyHeight.value : 0;
|
|
59
|
+
return {
|
|
60
|
+
height: animationEnabled.value
|
|
61
|
+
? withSpring(nextHeight, IOSpringValues.accordion)
|
|
62
|
+
: nextHeight
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const bodyAnimatedStyle = [
|
|
67
|
+
bodyAnimatedHeight,
|
|
68
|
+
styles.accordionCollapsableContainer
|
|
69
|
+
];
|
|
70
|
+
const bodyInnerStyle = styles.accordionBodyContainer;
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
expanded,
|
|
74
|
+
/**
|
|
75
|
+
* Toggle the accordion expanded/collapsed state.
|
|
76
|
+
*/
|
|
77
|
+
toggleAccordion,
|
|
78
|
+
/**
|
|
79
|
+
* The style to apply to the accordion icon.
|
|
80
|
+
*/
|
|
81
|
+
iconAnimatedStyle,
|
|
82
|
+
/**
|
|
83
|
+
* Callback to execute on the body's inner container layout.
|
|
84
|
+
*/
|
|
85
|
+
onBodyLayout,
|
|
86
|
+
/**
|
|
87
|
+
* The animated style to apply to the collapsible body container – it must be an `Animated.View`.
|
|
88
|
+
*/
|
|
89
|
+
bodyAnimatedStyle,
|
|
90
|
+
/**
|
|
91
|
+
* The style to apply to the inner body container.
|
|
92
|
+
*/
|
|
93
|
+
bodyInnerStyle
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const styles = StyleSheet.create({
|
|
98
|
+
accordionCollapsableContainer: {
|
|
99
|
+
overflow: "hidden"
|
|
100
|
+
},
|
|
101
|
+
accordionBodyContainer: {
|
|
102
|
+
position: "absolute",
|
|
103
|
+
padding: accordionBodySpacing,
|
|
104
|
+
paddingTop: 0
|
|
105
|
+
}
|
|
106
|
+
});
|