-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathFocusTrap.js
More file actions
116 lines (91 loc) · 5.17 KB
/
FocusTrap.js
File metadata and controls
116 lines (91 loc) · 5.17 KB
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import React from 'react';
import { PrimeReactContext } from '../api/Api';
import { useMountEffect, useStyle } from '../hooks/Hooks';
import { DomHandler } from '../utils/Utils';
import { FocusTrapBase } from './FocusTrapBase';
export const FocusTrap = React.memo(
React.forwardRef((inProps, ref) => {
const targetRef = React.useRef(null);
const firstFocusableElementRef = React.useRef(null);
const lastFocusableElementRef = React.useRef(null);
const context = React.useContext(PrimeReactContext);
const props = FocusTrapBase.getProps(inProps, context);
const metaData = {
props
};
useStyle(FocusTrapBase.css.styles, { name: 'focustrap' });
const { ptm } = FocusTrapBase.setMetaData({
...metaData
});
React.useImperativeHandle(ref, () => ({
props,
getInk: () => firstFocusableElementRef.current,
getTarget: () => targetRef.current
}));
useMountEffect(() => {
if (!props.disabled) {
targetRef.current = getTarget();
setAutoFocus(targetRef.current, props);
}
});
const getTarget = () => {
return firstFocusableElementRef.current && firstFocusableElementRef.current.parentElement;
};
/**
* This method sets the auto focus on the first focusable element within the target element.
* It first tries to find a focusable element using the autoFocusSelector. If no such element is found,
* it then tries to find a focusable element using the firstFocusableSelector.
* If the autoFocus prop is set to true and a focusable element is found, it sets the focus on that element.
*
* @param {HTMLElement} target - The target element within which to find a focusable element.
*/
const setAutoFocus = (target) => {
const { autoFocusSelector = '', firstFocusableSelector = '', autoFocus = false } = props || {};
const defaultAutoFocusSelector = `${getComputedSelector(autoFocusSelector)}`;
const computedAutoFocusSelector = `[autofocus]${defaultAutoFocusSelector}, [data-pc-autofocus='true']${defaultAutoFocusSelector}`;
let focusableElement = DomHandler.getFirstFocusableElement(target, computedAutoFocusSelector);
autoFocus && !focusableElement && (focusableElement = DomHandler.getFirstFocusableElement(target, getComputedSelector(firstFocusableSelector)));
DomHandler.focus(focusableElement);
};
const getComputedSelector = (selector) => {
return `:not(.p-hidden-focusable):not([data-p-hidden-focusable="true"])${selector ?? ''}`;
};
const onFirstHiddenElementFocus = (event) => {
const { currentTarget, relatedTarget } = event;
const focusableElement =
relatedTarget === currentTarget.$_pfocustrap_lasthiddenfocusableelement || !targetRef.current?.contains(relatedTarget)
? DomHandler.getFirstFocusableElement(currentTarget.parentElement, getComputedSelector(currentTarget.$_pfocustrap_focusableselector))
: currentTarget.$_pfocustrap_lasthiddenfocusableelement;
DomHandler.focus(focusableElement);
};
const onLastHiddenElementFocus = (event) => {
const { currentTarget, relatedTarget } = event;
const focusableElement =
relatedTarget === currentTarget.$_pfocustrap_firsthiddenfocusableelement || !targetRef.current?.contains(relatedTarget)
? DomHandler.getLastFocusableElement(currentTarget.parentElement, getComputedSelector(currentTarget.$_pfocustrap_focusableselector))
: currentTarget.$_pfocustrap_firsthiddenfocusableelement;
DomHandler.focus(focusableElement);
};
const createHiddenFocusableElements = () => {
const { tabIndex = 0 } = props || {};
const createFocusableElement = (inRef, onFocus, section) => {
return <span ref={inRef} className={'p-hidden-accessible p-hidden-focusable'} tabIndex={tabIndex} data-p-hidden-accessible={true} data-p-hidden-focusable={true} onFocus={onFocus} data-pc-section={section} />;
};
const firstFocusableElement = createFocusableElement(firstFocusableElementRef, onFirstHiddenElementFocus, 'firstfocusableelement');
const lastFocusableElement = createFocusableElement(lastFocusableElementRef, onLastHiddenElementFocus, 'lastfocusableelement');
if (firstFocusableElementRef.current && lastFocusableElementRef.current) {
firstFocusableElementRef.current.$_pfocustrap_lasthiddenfocusableelement = lastFocusableElementRef.current;
lastFocusableElementRef.current.$_pfocustrap_firsthiddenfocusableelement = firstFocusableElementRef.current;
}
return (
<>
{firstFocusableElement}
{props.children}
{lastFocusableElement}
</>
);
};
return createHiddenFocusableElements();
})
);
export default FocusTrap;