UNPKG

2.9 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 {useSyntheticBlurEvent} from './utils';
21
22export interface FocusProps<Target = FocusableElement> extends FocusEvents<Target> {
23 /** Whether the focus events should be disabled. */
24 isDisabled?: boolean
25}
26
27export interface FocusResult<Target = FocusableElement> {
28 /** Props to spread onto the target element. */
29 focusProps: DOMAttributes<Target>
30}
31
32/**
33 * Handles focus events for the immediate target.
34 * Focus events on child elements will be ignored.
35 */
36export function useFocus<Target extends FocusableElement = FocusableElement>(props: FocusProps<Target>): FocusResult<Target> {
37 let {
38 isDisabled,
39 onFocus: onFocusProp,
40 onBlur: onBlurProp,
41 onFocusChange
42 } = props;
43
44 const onBlur: FocusProps<Target>['onBlur'] = useCallback((e: FocusEvent<Target>) => {
45 if (e.target === e.currentTarget) {
46 if (onBlurProp) {
47 onBlurProp(e);
48 }
49
50 if (onFocusChange) {
51 onFocusChange(false);
52 }
53
54 return true;
55 }
56 }, [onBlurProp, onFocusChange]);
57
58
59 const onSyntheticFocus = useSyntheticBlurEvent<Target>(onBlur);
60
61 const onFocus: FocusProps<Target>['onFocus'] = useCallback((e: FocusEvent<Target>) => {
62 // Double check that document.activeElement actually matches e.target in case a previously chained
63 // focus handler already moved focus somewhere else.
64 if (e.target === e.currentTarget && document.activeElement === e.target) {
65 if (onFocusProp) {
66 onFocusProp(e);
67 }
68
69 if (onFocusChange) {
70 onFocusChange(true);
71 }
72
73 onSyntheticFocus(e);
74 }
75 }, [onFocusChange, onFocusProp, onSyntheticFocus]);
76
77 return {
78 focusProps: {
79 onFocus: (!isDisabled && (onFocusProp || onFocusChange || onBlurProp)) ? onFocus : undefined,
80 onBlur: (!isDisabled && (onBlurProp || onFocusChange)) ? onBlur : undefined
81 }
82 };
83}