UNPKG

3.01 kBPlain TextView Raw
1/*
2 * Copyright 2020 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12
13// Portions of the code in this file are based on code from react.
14// Original licensing for the following can be found in the
15// NOTICE file in the root directory of this source tree.
16// See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions
17
18import {DOMAttributes, FocusableElement, FocusEvents} from '@react-types/shared';
19import {FocusEvent, useCallback} from 'react';
20import {getOwnerDocument} from '@react-aria/utils';
21import {useSyntheticBlurEvent} from './utils';
22
23export interface FocusProps<Target = FocusableElement> extends FocusEvents<Target> {
24 /** Whether the focus events should be disabled. */
25 isDisabled?: boolean
26}
27
28export interface FocusResult<Target = FocusableElement> {
29 /** Props to spread onto the target element. */
30 focusProps: DOMAttributes<Target>
31}
32
33/**
34 * Handles focus events for the immediate target.
35 * Focus events on child elements will be ignored.
36 */
37export function useFocus<Target extends FocusableElement = FocusableElement>(props: FocusProps<Target>): FocusResult<Target> {
38 let {
39 isDisabled,
40 onFocus: onFocusProp,
41 onBlur: onBlurProp,
42 onFocusChange
43 } = props;
44
45 const onBlur: FocusProps<Target>['onBlur'] = useCallback((e: FocusEvent<Target>) => {
46 if (e.target === e.currentTarget) {
47 if (onBlurProp) {
48 onBlurProp(e);
49 }
50
51 if (onFocusChange) {
52 onFocusChange(false);
53 }
54
55 return true;
56 }
57 }, [onBlurProp, onFocusChange]);
58
59
60 const onSyntheticFocus = useSyntheticBlurEvent<Target>(onBlur);
61
62 const onFocus: FocusProps<Target>['onFocus'] = useCallback((e: FocusEvent<Target>) => {
63 // Double check that document.activeElement actually matches e.target in case a previously chained
64 // focus handler already moved focus somewhere else.
65
66 const ownerDocument = getOwnerDocument(e.target);
67
68 if (e.target === e.currentTarget && ownerDocument.activeElement === e.target) {
69 if (onFocusProp) {
70 onFocusProp(e);
71 }
72
73 if (onFocusChange) {
74 onFocusChange(true);
75 }
76
77 onSyntheticFocus(e);
78 }
79 }, [onFocusChange, onFocusProp, onSyntheticFocus]);
80
81 return {
82 focusProps: {
83 onFocus: (!isDisabled && (onFocusProp || onFocusChange || onBlurProp)) ? onFocus : undefined,
84 onBlur: (!isDisabled && (onBlurProp || onFocusChange)) ? onBlur : undefined
85 }
86 };
87}