@@ -12,12 +12,25 @@ import type {
1212 ViewStyle ,
1313} from 'react-native' ;
1414
15+ import Animated , {
16+ Easing ,
17+ ReduceMotion ,
18+ useAnimatedStyle ,
19+ useSharedValue ,
20+ withTiming ,
21+ } from 'react-native-reanimated' ;
22+ import type {
23+ EntryAnimationsValues ,
24+ ExitAnimationsValues ,
25+ } from 'react-native-reanimated' ;
26+
1527import { ListAccordionGroupContext } from './ListAccordionGroup' ;
1628import { ListTokens } from './tokens' ;
1729import type { ListChildProps , Style } from './utils' ;
1830import { ListRowContext , getAccordionColors , getLeftStyles } from './utils' ;
1931import { useLocale } from '../../core/locale' ;
2032import { useInternalTheme } from '../../core/theming' ;
33+ import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext' ;
2134import type { ThemeProp } from '../../theme/types' ;
2235import MaterialCommunityIcon from '../MaterialCommunityIcon' ;
2336import TouchableRipple from '../TouchableRipple/TouchableRipple' ;
@@ -239,6 +252,62 @@ const ListAccordion = ({
239252 ? groupContext . expandedId === id
240253 : expandedInternal ;
241254
255+ const reduceMotion = useReduceMotion ( ) ;
256+ const reanimatedReduceMotion = reduceMotion
257+ ? ReduceMotion . Always
258+ : ReduceMotion . Never ;
259+
260+ const timingConfig = React . useMemo (
261+ ( ) => ( {
262+ duration : theme . motion . duration . medium2 ,
263+ easing : Easing . bezier ( ...theme . motion . easing . emphasized ) ,
264+ reduceMotion : reanimatedReduceMotion ,
265+ } ) ,
266+ [
267+ theme . motion . duration . medium2 ,
268+ theme . motion . easing . emphasized ,
269+ reanimatedReduceMotion ,
270+ ]
271+ ) ;
272+
273+ const chevronProgress = useSharedValue ( isExpanded ? 1 : 0 ) ;
274+
275+ React . useEffect ( ( ) => {
276+ chevronProgress . value = withTiming ( isExpanded ? 1 : 0 , timingConfig ) ;
277+ } , [ isExpanded , chevronProgress , timingConfig ] ) ;
278+
279+ const chevronStyle = useAnimatedStyle ( ( ) => ( {
280+ transform : [ { rotate : `${ chevronProgress . value * 180 } deg` } ] ,
281+ } ) ) ;
282+
283+ const expandAnimation = React . useCallback (
284+ ( values : EntryAnimationsValues ) => {
285+ 'worklet' ;
286+ return {
287+ initialValues : { height : 0 , opacity : 0 } ,
288+ animations : {
289+ height : withTiming ( values . targetHeight , timingConfig ) ,
290+ opacity : withTiming ( 1 , timingConfig ) ,
291+ } ,
292+ } ;
293+ } ,
294+ [ timingConfig ]
295+ ) ;
296+
297+ const collapseAnimation = React . useCallback (
298+ ( values : ExitAnimationsValues ) => {
299+ 'worklet' ;
300+ return {
301+ initialValues : { height : values . currentHeight , opacity : 1 } ,
302+ animations : {
303+ height : withTiming ( 0 , timingConfig ) ,
304+ opacity : withTiming ( 0 , timingConfig ) ,
305+ } ,
306+ } ;
307+ } ,
308+ [ timingConfig ]
309+ ) ;
310+
242311 const {
243312 descriptionColor,
244313 titleTextColor,
@@ -341,21 +410,28 @@ const ListAccordion = ({
341410 isExpanded : isExpanded ,
342411 } )
343412 ) : (
344- < MaterialCommunityIcon
345- name = { isExpanded ? 'chevron-up' : 'chevron-down' }
346- color = { trailingIconColor }
347- size = { 24 }
348- direction = { direction }
349- />
413+ < Animated . View style = { chevronStyle } >
414+ < MaterialCommunityIcon
415+ name = "chevron-down"
416+ color = { trailingIconColor }
417+ size = { 24 }
418+ direction = { direction }
419+ />
420+ </ Animated . View >
350421 ) }
351422 </ View >
352423 </ View >
353424 </ TouchableRipple >
354425 </ ListRowContext . Provider >
355426 </ View >
356427
357- { isExpanded
358- ? React . Children . map ( children , ( child ) => {
428+ { isExpanded ? (
429+ < Animated . View
430+ entering = { expandAnimation }
431+ exiting = { collapseAnimation }
432+ style = { styles . expandedContent }
433+ >
434+ { React . Children . map ( children , ( child ) => {
359435 if (
360436 left &&
361437 React . isValidElement < ListChildProps > ( child ) &&
@@ -369,8 +445,9 @@ const ListAccordion = ({
369445 }
370446
371447 return child ;
372- } )
373- : null }
448+ } ) }
449+ </ Animated . View >
450+ ) : null }
374451 </ View >
375452 ) ;
376453} ;
@@ -405,6 +482,9 @@ const styles = StyleSheet.create({
405482 child : {
406483 paddingLeft : 40 ,
407484 } ,
485+ expandedContent : {
486+ overflow : 'hidden' ,
487+ } ,
408488 content : {
409489 flex : 1 ,
410490 justifyContent : 'center' ,
0 commit comments