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 | import clsx from 'clsx';
|
14 | import {mergeProps} from '@react-aria/utils';
|
15 | import React, {ReactElement} from 'react';
|
16 | import {useFocusRing} from './useFocusRing';
|
17 |
|
18 | export interface FocusRingProps {
|
19 | /** Child element to apply CSS classes to. */
|
20 | children: ReactElement,
|
21 | /** CSS class to apply when the element is focused. */
|
22 | focusClass?: string,
|
23 | /** CSS class to apply when the element has keyboard focus. */
|
24 | focusRingClass?: string,
|
25 | /**
|
26 | * Whether to show the focus ring when something
|
27 | * inside the container element has focus (true), or
|
28 | * only if the container itself has focus (false).
|
29 | * @default false
|
30 | */
|
31 | within?: boolean,
|
32 | /** Whether the element is a text input. */
|
33 | isTextInput?: boolean,
|
34 | /** Whether the element will be auto focused. */
|
35 | autoFocus?: boolean
|
36 | }
|
37 |
|
38 | /**
|
39 | * A utility component that applies a CSS class when an element has keyboard focus.
|
40 | * Focus rings are visible only when the user is interacting with a keyboard,
|
41 | * not with a mouse, touch, or other input methods.
|
42 | */
|
43 | export function FocusRing(props: FocusRingProps) {
|
44 | let {children, focusClass, focusRingClass} = props;
|
45 | let {isFocused, isFocusVisible, focusProps} = useFocusRing(props);
|
46 | let child = React.Children.only(children);
|
47 |
|
48 | return React.cloneElement(child, mergeProps(child.props as any, {
|
49 | ...focusProps,
|
50 | className: clsx({
|
51 | [focusClass || '']: isFocused,
|
52 | [focusRingClass || '']: isFocusVisible
|
53 | })
|
54 | }));
|
55 | }
|