1010 * governing permissions and limitations under the License.
1111 */
1212
13+ import { getOwnerWindow } from '@react-aria/utils' ;
14+
15+ const supportsInert = typeof HTMLElement !== 'undefined' && 'inert' in HTMLElement . prototype ;
16+
17+ interface AriaHideOutsideOptions {
18+ root ?: Element ,
19+ shouldUseInert ?: boolean
20+ }
21+
1322// Keeps a ref count of all hidden elements. Added to when hiding an element, and
1423// subtracted from when showing it again. When it reaches zero, aria-hidden is removed.
1524let refCountMap = new WeakMap < Element , number > ( ) ;
@@ -29,10 +38,28 @@ let observerStack: Array<ObserverWrapper> = [];
2938 * @param root - Nothing will be hidden above this element.
3039 * @returns - A function to restore all hidden elements.
3140 */
32- export function ariaHideOutside ( targets : Element [ ] , root : Element = document . body ) {
41+ export function ariaHideOutside ( targets : Element [ ] , options ?: AriaHideOutsideOptions | Element ) {
42+ let windowObj = getOwnerWindow ( targets ?. [ 0 ] ) ;
43+ let opts = options instanceof windowObj . Element ? { root : options } : options ;
44+ let root = opts ?. root ?? document . body ;
45+ let shouldUseInert = opts ?. shouldUseInert && supportsInert ;
3346 let visibleNodes = new Set < Element > ( targets ) ;
3447 let hiddenNodes = new Set < Element > ( ) ;
3548
49+ let getHidden = ( element : Element ) => {
50+ return shouldUseInert && element instanceof windowObj . HTMLElement ? element . inert : element . getAttribute ( 'aria-hidden' ) === 'true' ;
51+ } ;
52+
53+ let setHidden = ( element : Element , hidden : boolean ) => {
54+ if ( shouldUseInert && element instanceof windowObj . HTMLElement ) {
55+ element . inert = hidden ;
56+ } else if ( hidden ) {
57+ element . setAttribute ( 'aria-hidden' , 'true' ) ;
58+ } else {
59+ element . removeAttribute ( 'aria-hidden' ) ;
60+ }
61+ } ;
62+
3663 let walk = ( root : Element ) => {
3764 // Keep live announcer and top layer elements (e.g. toasts) visible.
3865 for ( let element of root . querySelectorAll ( '[data-live-announcer], [data-react-aria-top-layer]' ) ) {
@@ -87,12 +114,12 @@ export function ariaHideOutside(targets: Element[], root: Element = document.bod
87114
88115 // If already aria-hidden, and the ref count is zero, then this element
89116 // was already hidden and there's nothing for us to do.
90- if ( node . getAttribute ( 'aria-hidden' ) === 'true' && refCount === 0 ) {
117+ if ( getHidden ( node ) && refCount === 0 ) {
91118 return ;
92119 }
93120
94121 if ( refCount === 0 ) {
95- node . setAttribute ( 'aria-hidden' , ' true' ) ;
122+ setHidden ( node , true ) ;
96123 }
97124
98125 hiddenNodes . add ( node ) ;
@@ -161,7 +188,7 @@ export function ariaHideOutside(targets: Element[], root: Element = document.bod
161188 continue ;
162189 }
163190 if ( count === 1 ) {
164- node . removeAttribute ( 'aria-hidden' ) ;
191+ setHidden ( node , false ) ;
165192 refCountMap . delete ( node ) ;
166193 } else {
167194 refCountMap . set ( node , count - 1 ) ;
0 commit comments