+13
-12
src/useDialogFocus.ts
+13
-12
src/useDialogFocus.ts
···
25
25
const hasPriority = usePriority(ref, disabled);
26
26
27
27
useLayoutEffect(() => {
28
-
if (!ref.current || disabled) return;
28
+
const { current: element } = ref;
29
+
if (!element || disabled) return;
29
30
30
31
let selection = snapshotSelection(ownerRef && ownerRef.current);
31
32
let willReceiveFocus = false;
32
33
let focusMovesForward = true;
33
34
34
35
function onClick(event: MouseEvent) {
35
-
if (!ref.current || event.defaultPrevented) return;
36
+
if (!element || event.defaultPrevented) return;
36
37
37
38
const target = event.target as HTMLElement | null;
38
-
if (target && getFocusTargets(ref.current).indexOf(target) > -1) {
39
+
if (target && getFocusTargets(element).indexOf(target) > -1) {
39
40
selection = null;
40
41
willReceiveFocus = true;
41
42
}
42
43
}
43
44
44
45
function onFocus(event: FocusEvent) {
45
-
if (!ref.current || event.defaultPrevented) return;
46
+
if (!element || event.defaultPrevented) return;
46
47
47
48
const active = document.activeElement as HTMLElement;
48
49
const owner =
···
65
66
!contains(ref.current, relatedTarget)
66
67
) {
67
68
// Get the next focus target of the container
68
-
const focusTarget = getNextFocusTarget(ref.current, !focusMovesForward);
69
+
const focusTarget = getNextFocusTarget(element, !focusMovesForward);
69
70
if (focusTarget) {
70
71
focusMovesForward = true;
71
72
event.preventDefault();
···
75
76
}
76
77
77
78
function onKey(event: KeyboardEvent) {
78
-
if (!ref.current || event.defaultPrevented || event.isComposing) return;
79
+
if (!element || event.defaultPrevented || event.isComposing) return;
79
80
80
81
// Mark whether focus is moving forward for the `onFocus` handler
81
82
if (event.code === 'Tab') {
···
87
88
const active = document.activeElement as HTMLElement;
88
89
const owner =
89
90
(ownerRef && ownerRef.current) || (selection && selection.element);
90
-
const focusTargets = getFocusTargets(ref.current);
91
+
const focusTargets = getFocusTargets(element);
91
92
92
93
if (
93
94
!focusTargets.length ||
···
97
98
return;
98
99
} else if (event.code === 'Tab') {
99
100
// Skip over the listbox via the parent if we press tab
100
-
const currentTarget = contains(owner, active) ? owner! : ref.current;
101
+
const currentTarget = contains(owner, active) ? owner! : element;
101
102
const focusTarget = getNextFocusTarget(currentTarget, event.shiftKey);
102
103
if (focusTarget) {
103
104
event.preventDefault();
···
152
153
event.code === 'Enter'
153
154
) {
154
155
// Move focus to first target when Enter is pressed
155
-
const newTarget = getFirstFocusTarget(ref.current);
156
+
const newTarget = getFirstFocusTarget(element);
156
157
if (newTarget) {
157
158
willReceiveFocus = true;
158
159
newTarget.focus();
···
169
170
}
170
171
}
171
172
172
-
ref.current.addEventListener('mousedown', onClick, true);
173
+
element.addEventListener('mousedown', onClick, true);
173
174
document.body.addEventListener('focusin', onFocus);
174
175
document.addEventListener('keydown', onKey);
175
176
176
177
return () => {
177
-
ref.current!.removeEventListener('mousedown', onClick);
178
+
element.removeEventListener('mousedown', onClick);
178
179
document.body.removeEventListener('focusin', onFocus);
179
180
document.removeEventListener('keydown', onKey);
180
181
};
181
-
}, [ref.current!, disabled, hasPriority]);
182
+
}, [ref.current, disabled, hasPriority]);
182
183
}
+7
-9
src/useDismissable.ts
+7
-9
src/useDismissable.ts
···
26
26
}, [onDismiss]);
27
27
28
28
useLayoutEffect(() => {
29
-
if (!ref.current || disabled) return;
29
+
const { current: element } = ref;
30
+
if (!element || disabled) return;
30
31
31
32
function onFocusOut(event: FocusEvent) {
32
33
if (event.defaultPrevented) return;
33
34
34
35
const { target, relatedTarget } = event;
35
-
if (
36
-
contains(ref.current, target) &&
37
-
!contains(ref.current, relatedTarget)
38
-
) {
36
+
if (contains(element, target) && !contains(element, relatedTarget)) {
39
37
onDismissRef.current();
40
38
}
41
39
}
···
45
43
// The current dialog can be dismissed by pressing escape if it either has focus
46
44
// or it has priority
47
45
const active = document.activeElement;
48
-
if (hasPriority || (active && contains(ref.current, active))) {
46
+
if (hasPriority || (active && contains(element, active))) {
49
47
event.preventDefault();
50
48
onDismissRef.current();
51
49
}
···
54
52
55
53
function onClick(event: MouseEvent | TouchEvent) {
56
54
const { target } = event;
57
-
if (contains(ref.current, target) || event.defaultPrevented) {
55
+
if (contains(element, target) || event.defaultPrevented) {
58
56
return;
59
57
}
60
58
61
59
// The current dialog can be dismissed by pressing outside of it if it either has
62
60
// focus or it has priority
63
61
const active = document.activeElement;
64
-
if (hasPriority || (active && contains(ref.current, active))) {
62
+
if (hasPriority || (active && contains(element, active))) {
65
63
event.preventDefault();
66
64
onDismissRef.current();
67
65
}
···
80
78
document.removeEventListener('touchstart', onClick);
81
79
document.removeEventListener('keydown', onKey);
82
80
};
83
-
}, [ref.current!, hasPriority, disabled, focusLoss]);
81
+
}, [ref.current, hasPriority, disabled, focusLoss]);
84
82
}
+5
-7
src/useForwardedRef.ts
+5
-7
src/useForwardedRef.ts
···
9
9
export function useForwardedRef<T extends HTMLElement>(
10
10
forwarded: ForwardedRef<T>
11
11
): Ref<T> {
12
-
const ref: RefWithState<T> = useRef<T>(null);
13
-
if (ref._forwarded !== forwarded) {
14
-
ref._forwarded = forwarded;
15
-
Object.defineProperty(ref, 'current', {
12
+
const ref = useRef<RefWithState<T> | null>(null);
13
+
if (!ref.current || ref.current._forwarded !== forwarded) {
14
+
ref.current = Object.defineProperty({ _forwarded: forwarded }, 'current', {
16
15
enumerable: true,
17
16
configurable: true,
18
17
get() {
···
26
25
this._forwarded.current = value;
27
26
}
28
27
},
29
-
});
28
+
}) as RefWithState<T>;
30
29
}
31
-
32
-
return ref;
30
+
return ref.current;
33
31
}
+12
-11
src/useMenuFocus.ts
+12
-11
src/useMenuFocus.ts
···
21
21
const disabled = !!(options && options.disabled);
22
22
23
23
useLayoutEffect(() => {
24
-
if (!ref.current || disabled) return;
24
+
const { current: element } = ref;
25
+
if (!element || disabled) return;
25
26
26
27
let selection: RestoreSelection | null = null;
27
28
28
29
function onFocus(event: FocusEvent) {
29
-
if (!ref.current || event.defaultPrevented) return;
30
+
if (!element || event.defaultPrevented) return;
30
31
31
32
const owner =
32
33
(ownerRef && ownerRef.current) || (selection && selection.element);
···
35
36
// When owner is explicitly passed we can make a snapshot early
36
37
selection = snapshotSelection(owner);
37
38
} else if (
38
-
contains(ref.current, target) &&
39
-
!contains(ref.current, relatedTarget) &&
39
+
contains(element, target) &&
40
+
!contains(element, relatedTarget) &&
40
41
(!ownerRef || contains(relatedTarget, ownerRef.current))
41
42
) {
42
43
// Check whether focus is about to move into the container and snapshot last focus
43
44
selection = snapshotSelection(owner);
44
45
} else if (
45
-
contains(ref.current, relatedTarget) &&
46
-
!contains(ref.current, target)
46
+
contains(element, relatedTarget) &&
47
+
!contains(element, target)
47
48
) {
48
49
// Reset focus if it's lost and has left the menu
49
50
selection = null;
···
51
52
}
52
53
53
54
function onKey(event: KeyboardEvent) {
54
-
if (!ref.current || event.defaultPrevented || event.isComposing) return;
55
+
if (!element || event.defaultPrevented || event.isComposing) return;
55
56
56
57
const owner =
57
58
(ownerRef && ownerRef.current) || (selection && selection.element);
58
59
const active = document.activeElement as HTMLElement;
59
-
const focusTargets = getFocusTargets(ref.current);
60
+
const focusTargets = getFocusTargets(element);
60
61
if (
61
62
!focusTargets.length ||
62
-
(!contains(ref.current, active) && !contains(owner, active))
63
+
(!contains(element, active) && !contains(owner, active))
63
64
) {
64
65
// Do nothing if container doesn't contain focus or not targets are available
65
66
return;
···
100
101
event.code === 'Enter'
101
102
) {
102
103
// Move focus to first target when enter is pressed
103
-
const newTarget = getFirstFocusTarget(ref.current);
104
+
const newTarget = getFirstFocusTarget(element);
104
105
if (newTarget) newTarget.focus();
105
106
} else if (
106
107
owner &&
···
129
130
document.body.removeEventListener('focusin', onFocus);
130
131
document.removeEventListener('keydown', onKey);
131
132
};
132
-
}, [ref.current!, disabled]);
133
+
}, [ref.current, disabled]);
133
134
}
+12
-16
src/useModalFocus.ts
+12
-16
src/useModalFocus.ts
···
29
29
const hasPriority = usePriority(ref, disabled);
30
30
31
31
useLayoutEffect(() => {
32
-
if (!ref.current || !hasPriority || disabled) return;
32
+
const { current: element } = ref;
33
+
if (!element || !hasPriority || disabled) return;
33
34
34
35
let selection: RestoreSelection | null = null;
35
-
if (
36
-
!document.activeElement ||
37
-
!contains(ref.current, document.activeElement)
38
-
) {
39
-
const newTarget = getAutofocusTarget(ref.current);
40
-
selection = snapshotSelection(ref.current);
36
+
if (!document.activeElement || !contains(element, document.activeElement)) {
37
+
const newTarget = getAutofocusTarget(element);
38
+
selection = snapshotSelection(element);
41
39
newTarget.focus();
42
40
}
43
41
44
42
function onBlur(event: FocusEvent) {
45
-
const parent = ref.current;
46
-
if (!parent || event.defaultPrevented) return;
43
+
if (!element || event.defaultPrevented) return;
47
44
48
45
if (
49
-
contains(parent, event.target) &&
50
-
!contains(parent, event.relatedTarget)
46
+
contains(element, event.target) &&
47
+
!contains(element, event.relatedTarget)
51
48
) {
52
-
const target = getFirstFocusTarget(parent);
49
+
const target = getFirstFocusTarget(element);
53
50
if (target) target.focus();
54
51
}
55
52
}
56
53
57
54
function onKeyDown(event: KeyboardEvent) {
58
-
const parent = ref.current;
59
-
if (!parent || event.defaultPrevented) return;
55
+
if (!element || event.defaultPrevented) return;
60
56
61
57
if (event.code === 'Tab') {
62
58
const activeElement = document.activeElement as HTMLElement;
63
-
const targets = getFocusTargets(parent);
59
+
const targets = getFocusTargets(element);
64
60
const index = targets.indexOf(activeElement);
65
61
if (event.shiftKey && index === 0) {
66
62
event.preventDefault();
···
80
76
document.body.removeEventListener('focusout', onBlur);
81
77
document.removeEventListener('keydown', onKeyDown);
82
78
};
83
-
}, [ref.current!, hasPriority, disabled]);
79
+
}, [ref.current, hasPriority, disabled]);
84
80
}
+5
-6
src/usePriority.ts
+5
-6
src/usePriority.ts
···
31
31
});
32
32
33
33
useLayoutEffect(() => {
34
-
if (!ref.current || isDisabled) return;
35
-
36
-
const { current } = ref;
34
+
const { current: element } = ref;
35
+
if (!element || isDisabled) return;
37
36
38
37
function onChange() {
39
38
setHasPriority(() => priorityStack[0] === ref.current);
40
39
}
41
40
42
-
priorityStack.push(current);
41
+
priorityStack.push(element);
43
42
priorityStack.sort(sortByHierarchy);
44
43
listeners.add(onChange);
45
44
listeners.forEach(fn => fn());
46
45
47
46
return () => {
48
-
const index = priorityStack.indexOf(current);
47
+
const index = priorityStack.indexOf(element);
49
48
priorityStack.splice(index, 1);
50
49
listeners.delete(onChange);
51
50
listeners.forEach(fn => fn());
52
51
};
53
-
}, [ref.current!, isDisabled]);
52
+
}, [ref.current, isDisabled]);
54
53
55
54
return hasPriority;
56
55
};