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 |
|
18 | import {DOMAttributes, FocusEvents} from '@react-types/shared';
|
19 | import {FocusEvent, useCallback} from 'react';
|
20 | import {useSyntheticBlurEvent} from './utils';
|
21 |
|
22 | export interface FocusProps extends FocusEvents {
|
23 | /** Whether the focus events should be disabled. */
|
24 | isDisabled?: boolean
|
25 | }
|
26 |
|
27 | export interface FocusResult {
|
28 | /** Props to spread onto the target element. */
|
29 | focusProps: DOMAttributes
|
30 | }
|
31 |
|
32 | /**
|
33 | * Handles focus events for the immediate target.
|
34 | * Focus events on child elements will be ignored.
|
35 | */
|
36 | export function useFocus(props: FocusProps): FocusResult {
|
37 | let {
|
38 | isDisabled,
|
39 | onFocus: onFocusProp,
|
40 | onBlur: onBlurProp,
|
41 | onFocusChange
|
42 | } = props;
|
43 |
|
44 | const onBlur: FocusProps['onBlur'] = useCallback((e: FocusEvent) => {
|
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(onBlur);
|
60 |
|
61 | const onFocus: FocusProps['onFocus'] = useCallback((e: FocusEvent) => {
|
62 | if (e.target === e.currentTarget) {
|
63 | if (onFocusProp) {
|
64 | onFocusProp(e);
|
65 | }
|
66 |
|
67 | if (onFocusChange) {
|
68 | onFocusChange(true);
|
69 | }
|
70 |
|
71 | onSyntheticFocus(e);
|
72 | }
|
73 | }, [onFocusChange, onFocusProp, onSyntheticFocus]);
|
74 |
|
75 | return {
|
76 | focusProps: {
|
77 | onFocus: (!isDisabled && (onFocusProp || onFocusChange || onBlurProp)) ? onFocus : undefined,
|
78 | onBlur: (!isDisabled && (onBlurProp || onFocusChange)) ? onBlur : undefined
|
79 | }
|
80 | };
|
81 | }
|