-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
index.ts
86 lines (80 loc) · 2.28 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
/**
* Internal dependencies
*/
import { debounce } from '../../utils/debounce';
import useRefEffect from '../use-ref-effect';
/**
* In some circumstances, such as block previews, all focusable DOM elements
* (input fields, links, buttons, etc.) need to be disabled. This hook adds the
* behavior to disable nested DOM elements to the returned ref.
*
* If you can, prefer the use of the inert HTML attribute.
*
* @param {Object} config Configuration object.
* @param {boolean=} config.isDisabled Whether the element should be disabled.
* @return {import('react').RefCallback<HTMLElement>} Element Ref.
*
* @example
* ```js
* import { useDisabled } from '@wordpress/compose';
*
* const DisabledExample = () => {
* const disabledRef = useDisabled();
* return (
* <div ref={ disabledRef }>
* <a href="#">This link will have tabindex set to -1</a>
* <input placeholder="This input will have the disabled attribute added to it." type="text" />
* </div>
* );
* };
* ```
*/
export default function useDisabled( {
isDisabled: isDisabledProp = false,
} = {} ) {
return useRefEffect(
( node ) => {
if ( isDisabledProp ) {
return;
}
const defaultView = node?.ownerDocument?.defaultView;
if ( ! defaultView ) {
return;
}
/** A variable keeping track of the previous updates in order to restore them. */
const updates: Function[] = [];
const disable = () => {
node.childNodes.forEach( ( child ) => {
if ( ! ( child instanceof defaultView.HTMLElement ) ) {
return;
}
if ( ! child.getAttribute( 'inert' ) ) {
child.setAttribute( 'inert', 'true' );
updates.push( () => {
child.removeAttribute( 'inert' );
} );
}
} );
};
// Debounce re-disable since disabling process itself will incur
// additional mutations which should be ignored.
const debouncedDisable = debounce( disable, 0, {
leading: true,
} );
disable();
/** @type {MutationObserver | undefined} */
const observer = new window.MutationObserver( debouncedDisable );
observer.observe( node, {
childList: true,
} );
return () => {
if ( observer ) {
observer.disconnect();
}
debouncedDisable.cancel();
updates.forEach( ( update ) => update() );
};
},
[ isDisabledProp ]
);
}