UNPKG

7.04 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import * as React from 'react';
3import { unstable_useForkRef as useForkRef, unstable_useIsFocusVisible as useIsFocusVisible } from '@mui/utils';
4import extractEventHandlers from '../utils/extractEventHandlers';
5/**
6 *
7 * Demos:
8 *
9 * - [Button](https://mui.com/base/react-button/#hook)
10 *
11 * API:
12 *
13 * - [useButton API](https://mui.com/base/react-button/hooks-api/#use-button)
14 */
15export default function useButton(parameters = {}) {
16 const {
17 disabled = false,
18 focusableWhenDisabled,
19 href,
20 rootRef: externalRef,
21 tabIndex,
22 to,
23 type
24 } = parameters;
25 const buttonRef = React.useRef();
26 const [active, setActive] = React.useState(false);
27 const {
28 isFocusVisibleRef,
29 onFocus: handleFocusVisible,
30 onBlur: handleBlurVisible,
31 ref: focusVisibleRef
32 } = useIsFocusVisible();
33 const [focusVisible, setFocusVisible] = React.useState(false);
34 if (disabled && !focusableWhenDisabled && focusVisible) {
35 setFocusVisible(false);
36 }
37 React.useEffect(() => {
38 isFocusVisibleRef.current = focusVisible;
39 }, [focusVisible, isFocusVisibleRef]);
40 const [hostElementName, setHostElementName] = React.useState('');
41 const createHandleMouseLeave = otherHandlers => event => {
42 var _otherHandlers$onMous;
43 if (focusVisible) {
44 event.preventDefault();
45 }
46 (_otherHandlers$onMous = otherHandlers.onMouseLeave) == null ? void 0 : _otherHandlers$onMous.call(otherHandlers, event);
47 };
48 const createHandleBlur = otherHandlers => event => {
49 var _otherHandlers$onBlur;
50 handleBlurVisible(event);
51 if (isFocusVisibleRef.current === false) {
52 setFocusVisible(false);
53 }
54 (_otherHandlers$onBlur = otherHandlers.onBlur) == null ? void 0 : _otherHandlers$onBlur.call(otherHandlers, event);
55 };
56 const createHandleFocus = otherHandlers => event => {
57 var _otherHandlers$onFocu2;
58 // Fix for https://github.com/facebook/react/issues/7769
59 if (!buttonRef.current) {
60 buttonRef.current = event.currentTarget;
61 }
62 handleFocusVisible(event);
63 if (isFocusVisibleRef.current === true) {
64 var _otherHandlers$onFocu;
65 setFocusVisible(true);
66 (_otherHandlers$onFocu = otherHandlers.onFocusVisible) == null ? void 0 : _otherHandlers$onFocu.call(otherHandlers, event);
67 }
68 (_otherHandlers$onFocu2 = otherHandlers.onFocus) == null ? void 0 : _otherHandlers$onFocu2.call(otherHandlers, event);
69 };
70 const isNativeButton = () => {
71 const button = buttonRef.current;
72 return hostElementName === 'BUTTON' || hostElementName === 'INPUT' && ['button', 'submit', 'reset'].includes(button == null ? void 0 : button.type) || hostElementName === 'A' && (button == null ? void 0 : button.href);
73 };
74 const createHandleClick = otherHandlers => event => {
75 if (!disabled) {
76 var _otherHandlers$onClic;
77 (_otherHandlers$onClic = otherHandlers.onClick) == null ? void 0 : _otherHandlers$onClic.call(otherHandlers, event);
78 }
79 };
80 const createHandleMouseDown = otherHandlers => event => {
81 var _otherHandlers$onMous2;
82 if (!disabled) {
83 setActive(true);
84 document.addEventListener('mouseup', () => {
85 setActive(false);
86 }, {
87 once: true
88 });
89 }
90 (_otherHandlers$onMous2 = otherHandlers.onMouseDown) == null ? void 0 : _otherHandlers$onMous2.call(otherHandlers, event);
91 };
92 const createHandleKeyDown = otherHandlers => event => {
93 var _otherHandlers$onKeyD;
94 (_otherHandlers$onKeyD = otherHandlers.onKeyDown) == null ? void 0 : _otherHandlers$onKeyD.call(otherHandlers, event);
95 if (event.defaultMuiPrevented) {
96 return;
97 }
98 if (event.target === event.currentTarget && !isNativeButton() && event.key === ' ') {
99 event.preventDefault();
100 }
101 if (event.target === event.currentTarget && event.key === ' ' && !disabled) {
102 setActive(true);
103 }
104
105 // Keyboard accessibility for non interactive elements
106 if (event.target === event.currentTarget && !isNativeButton() && event.key === 'Enter' && !disabled) {
107 var _otherHandlers$onClic2;
108 (_otherHandlers$onClic2 = otherHandlers.onClick) == null ? void 0 : _otherHandlers$onClic2.call(otherHandlers, event);
109 event.preventDefault();
110 }
111 };
112 const createHandleKeyUp = otherHandlers => event => {
113 var _otherHandlers$onKeyU;
114 // calling preventDefault in keyUp on a <button> will not dispatch a click event if Space is pressed
115 // https://codesandbox.io/s/button-keyup-preventdefault-dn7f0
116
117 if (event.target === event.currentTarget) {
118 setActive(false);
119 }
120 (_otherHandlers$onKeyU = otherHandlers.onKeyUp) == null ? void 0 : _otherHandlers$onKeyU.call(otherHandlers, event);
121
122 // Keyboard accessibility for non interactive elements
123 if (event.target === event.currentTarget && !isNativeButton() && !disabled && event.key === ' ' && !event.defaultMuiPrevented) {
124 var _otherHandlers$onClic3;
125 (_otherHandlers$onClic3 = otherHandlers.onClick) == null ? void 0 : _otherHandlers$onClic3.call(otherHandlers, event);
126 }
127 };
128 const updateHostElementName = React.useCallback(instance => {
129 var _instance$tagName;
130 setHostElementName((_instance$tagName = instance == null ? void 0 : instance.tagName) != null ? _instance$tagName : '');
131 }, []);
132 const handleRef = useForkRef(updateHostElementName, externalRef, focusVisibleRef, buttonRef);
133 const buttonProps = {};
134 if (hostElementName === 'BUTTON') {
135 buttonProps.type = type != null ? type : 'button';
136 if (focusableWhenDisabled) {
137 buttonProps['aria-disabled'] = disabled;
138 } else {
139 buttonProps.disabled = disabled;
140 }
141 } else if (hostElementName !== '') {
142 if (!href && !to) {
143 buttonProps.role = 'button';
144 buttonProps.tabIndex = tabIndex != null ? tabIndex : 0;
145 }
146 if (disabled) {
147 buttonProps['aria-disabled'] = disabled;
148 buttonProps.tabIndex = focusableWhenDisabled ? tabIndex != null ? tabIndex : 0 : -1;
149 }
150 }
151 const getRootProps = (otherHandlers = {}) => {
152 const propsEventHandlers = extractEventHandlers(parameters);
153 const externalEventHandlers = _extends({}, propsEventHandlers, otherHandlers);
154
155 // onFocusVisible can be present on the props, but since it's not a valid React event handler,
156 // it must not be forwarded to the inner component.
157 delete externalEventHandlers.onFocusVisible;
158 return _extends({
159 type
160 }, externalEventHandlers, buttonProps, {
161 onBlur: createHandleBlur(externalEventHandlers),
162 onClick: createHandleClick(externalEventHandlers),
163 onFocus: createHandleFocus(externalEventHandlers),
164 onKeyDown: createHandleKeyDown(externalEventHandlers),
165 onKeyUp: createHandleKeyUp(externalEventHandlers),
166 onMouseDown: createHandleMouseDown(externalEventHandlers),
167 onMouseLeave: createHandleMouseLeave(externalEventHandlers),
168 ref: handleRef
169 });
170 };
171 return {
172 getRootProps,
173 focusVisible,
174 setFocusVisible,
175 active,
176 rootRef: handleRef
177 };
178}
\No newline at end of file