/
useButton.ts
120 lines (113 loc) · 4.49 KB
/
useButton.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import {
AnchorHTMLAttributes,
ButtonHTMLAttributes,
ElementType,
HTMLAttributes,
InputHTMLAttributes,
RefObject
} from 'react';
import {AriaButtonProps} from '@react-types/button';
import {DOMAttributes} from '@react-types/shared';
import {filterDOMProps, mergeProps} from '@react-aria/utils';
import {useFocusable} from '@react-aria/focus';
import {usePress} from '@react-aria/interactions';
export interface AriaButtonOptions<E extends ElementType> extends Omit<AriaButtonProps<E>, 'children'> {}
export interface ButtonAria<T> {
/** Props for the button element. */
buttonProps: T,
/** Whether the button is currently pressed. */
isPressed: boolean
}
// Order with overrides is important: 'button' should be default
export function useButton(props: AriaButtonOptions<'button'>, ref: RefObject<HTMLButtonElement>): ButtonAria<ButtonHTMLAttributes<HTMLButtonElement>>;
export function useButton(props: AriaButtonOptions<'a'>, ref: RefObject<HTMLAnchorElement>): ButtonAria<AnchorHTMLAttributes<HTMLAnchorElement>>;
export function useButton(props: AriaButtonOptions<'div'>, ref: RefObject<HTMLDivElement>): ButtonAria<HTMLAttributes<HTMLDivElement>>;
export function useButton(props: AriaButtonOptions<'input'>, ref: RefObject<HTMLInputElement>): ButtonAria<InputHTMLAttributes<HTMLInputElement>>;
export function useButton(props: AriaButtonOptions<'span'>, ref: RefObject<HTMLSpanElement>): ButtonAria<HTMLAttributes<HTMLSpanElement>>;
export function useButton(props: AriaButtonOptions<ElementType>, ref: RefObject<Element>): ButtonAria<DOMAttributes>;
/**
* Provides the behavior and accessibility implementation for a button component. Handles mouse, keyboard, and touch interactions,
* focus behavior, and ARIA props for both native button elements and custom element types.
* @param props - Props to be applied to the button.
* @param ref - A ref to a DOM element for the button.
*/
export function useButton(props: AriaButtonOptions<ElementType>, ref: RefObject<any>): ButtonAria<HTMLAttributes<any>> {
let {
elementType = 'button',
isDisabled,
onPress,
onPressStart,
onPressEnd,
onPressUp,
onPressChange,
// @ts-ignore - undocumented
preventFocusOnPress,
// @ts-ignore - undocumented
allowFocusWhenDisabled,
// @ts-ignore
onClick: deprecatedOnClick,
href,
target,
rel,
type = 'button'
} = props;
let additionalProps;
if (elementType === 'button') {
additionalProps = {
type,
disabled: isDisabled
};
} else {
additionalProps = {
role: 'button',
tabIndex: isDisabled ? undefined : 0,
href: elementType === 'a' && isDisabled ? undefined : href,
target: elementType === 'a' ? target : undefined,
type: elementType === 'input' ? type : undefined,
disabled: elementType === 'input' ? isDisabled : undefined,
'aria-disabled': !isDisabled || elementType === 'input' ? undefined : isDisabled,
rel: elementType === 'a' ? rel : undefined
};
}
let {pressProps, isPressed} = usePress({
onPressStart,
onPressEnd,
onPressChange,
onPress,
onPressUp,
isDisabled,
preventFocusOnPress,
ref
});
let {focusableProps} = useFocusable(props, ref);
if (allowFocusWhenDisabled) {
focusableProps.tabIndex = isDisabled ? -1 : focusableProps.tabIndex;
}
let buttonProps = mergeProps(focusableProps, pressProps, filterDOMProps(props, {labelable: true}));
return {
isPressed, // Used to indicate press state for visual
buttonProps: mergeProps(additionalProps, buttonProps, {
'aria-haspopup': props['aria-haspopup'],
'aria-expanded': props['aria-expanded'],
'aria-controls': props['aria-controls'],
'aria-pressed': props['aria-pressed'],
onClick: (e) => {
if (deprecatedOnClick) {
deprecatedOnClick(e);
console.warn('onClick is deprecated, please use onPress');
}
}
})
};
}