UNPKG

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