|
1 | | -import React, { forwardRef, useContext } from 'react'; |
| 1 | +import React, { |
| 2 | + forwardRef, |
| 3 | + useContext, |
| 4 | + useLayoutEffect, |
| 5 | + useRef, |
| 6 | +} from 'react'; |
| 7 | +import { mergeRefs } from '@gluestack-ui/utils/common'; |
| 8 | +import { Platform } from 'react-native'; |
2 | 9 | import { AccordionItemContext } from './Context'; |
3 | 10 | import AnimatedHeight from './AnimatedHeight'; |
4 | 11 |
|
5 | 12 | export const AccordionContent = (StyledAccordionContent: any) => |
6 | 13 | forwardRef(({ children, ...props }: any, ref?: any) => { |
7 | 14 | const { regionProps, isExpanded } = useContext(AccordionItemContext); |
| 15 | + const contentRef = useRef<HTMLElement | null>(null); |
| 16 | + const mergedRef = mergeRefs([ref, contentRef]); |
| 17 | + |
| 18 | + // On web, collapsed panel content stays mounted (e.g. for height animation) but must |
| 19 | + // not be tab-focusable. RN Web's View does not forward `inert`, so set it on the host. |
| 20 | + useLayoutEffect(() => { |
| 21 | + if (Platform.OS !== 'web') return; |
| 22 | + const node = contentRef.current; |
| 23 | + if (!node || typeof node.setAttribute !== 'function') return; |
| 24 | + if (!isExpanded) { |
| 25 | + node.setAttribute('inert', ''); |
| 26 | + } else { |
| 27 | + node.removeAttribute('inert'); |
| 28 | + } |
| 29 | + }, [isExpanded]); |
| 30 | + |
| 31 | + const webCollapsedA11y = |
| 32 | + Platform.OS === 'web' && !isExpanded ? { 'aria-hidden': true } : {}; |
8 | 33 |
|
9 | 34 | return ( |
10 | 35 | <AnimatedHeight hide={!isExpanded}> |
11 | | - <StyledAccordionContent ref={ref} {...props} {...regionProps}> |
| 36 | + <StyledAccordionContent |
| 37 | + ref={mergedRef} |
| 38 | + {...props} |
| 39 | + {...regionProps} |
| 40 | + {...webCollapsedA11y} |
| 41 | + > |
12 | 42 | {children} |
13 | 43 | </StyledAccordionContent> |
14 | 44 | </AnimatedHeight> |
|
0 commit comments