@@ -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 { getLeftStyles } from './utils' ;
1931import { useLocale } from '../../core/locale' ;
2032import { useInternalTheme } from '../../core/theming' ;
33+ import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext' ;
2134import type { ThemeProp } from '../../types' ;
2235import MaterialCommunityIcon from '../MaterialCommunityIcon' ;
2336import TouchableRipple from '../TouchableRipple/TouchableRipple' ;
@@ -249,6 +262,62 @@ const ListAccordion = ({
249262 ? groupContext . expandedId === id
250263 : expandedInternal ;
251264
265+ const reduceMotion = useReduceMotion ( ) ;
266+ const reanimatedReduceMotion = reduceMotion
267+ ? ReduceMotion . Always
268+ : ReduceMotion . Never ;
269+
270+ const timingConfig = React . useMemo (
271+ ( ) => ( {
272+ duration : theme . motion . duration . medium2 ,
273+ easing : Easing . bezier ( ...theme . motion . easing . emphasized ) ,
274+ reduceMotion : reanimatedReduceMotion ,
275+ } ) ,
276+ [
277+ theme . motion . duration . medium2 ,
278+ theme . motion . easing . emphasized ,
279+ reanimatedReduceMotion ,
280+ ]
281+ ) ;
282+
283+ const chevronProgress = useSharedValue ( isExpanded ? 1 : 0 ) ;
284+
285+ React . useEffect ( ( ) => {
286+ chevronProgress . value = withTiming ( isExpanded ? 1 : 0 , timingConfig ) ;
287+ } , [ isExpanded , chevronProgress , timingConfig ] ) ;
288+
289+ const chevronStyle = useAnimatedStyle ( ( ) => ( {
290+ transform : [ { rotate : `${ chevronProgress . value * 180 } deg` } ] ,
291+ } ) ) ;
292+
293+ const expandAnimation = React . useCallback (
294+ ( values : EntryAnimationsValues ) => {
295+ 'worklet' ;
296+ return {
297+ initialValues : { height : 0 , opacity : 0 } ,
298+ animations : {
299+ height : withTiming ( values . targetHeight , timingConfig ) ,
300+ opacity : withTiming ( 1 , timingConfig ) ,
301+ } ,
302+ } ;
303+ } ,
304+ [ timingConfig ]
305+ ) ;
306+
307+ const collapseAnimation = React . useCallback (
308+ ( values : ExitAnimationsValues ) => {
309+ 'worklet' ;
310+ return {
311+ initialValues : { height : values . currentHeight , opacity : 1 } ,
312+ animations : {
313+ height : withTiming ( 0 , timingConfig ) ,
314+ opacity : withTiming ( 0 , timingConfig ) ,
315+ } ,
316+ } ;
317+ } ,
318+ [ timingConfig ]
319+ ) ;
320+
252321 const selectedContentColor = theme . colors [ ListTokens . selectedContentColor ] ;
253322 const titleTextColor = selected
254323 ? selectedContentColor
@@ -342,20 +411,27 @@ const ListAccordion = ({
342411 isExpanded : isExpanded ,
343412 } )
344413 ) : (
345- < MaterialCommunityIcon
346- name = { isExpanded ? 'chevron-up' : 'chevron-down' }
347- color = { trailingIconColor }
348- size = { 24 }
349- direction = { direction }
350- />
414+ < Animated . View style = { chevronStyle } >
415+ < MaterialCommunityIcon
416+ name = "chevron-down"
417+ color = { trailingIconColor }
418+ size = { 24 }
419+ direction = { direction }
420+ />
421+ </ Animated . View >
351422 ) }
352423 </ View >
353424 </ View >
354425 </ TouchableRipple >
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} ;
@@ -403,6 +480,9 @@ const styles = StyleSheet.create({
403480 child : {
404481 paddingLeft : 40 ,
405482 } ,
483+ expandedContent : {
484+ overflow : 'hidden' ,
485+ } ,
406486 content : {
407487 flex : 1 ,
408488 justifyContent : 'center' ,
0 commit comments