1 | import _extends from "@babel/runtime/helpers/esm/extends";
|
2 | import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
3 | import * as React from 'react';
|
4 | import PropTypes from 'prop-types';
|
5 | import * as ReactDOM from 'react-dom';
|
6 | import clsx from 'clsx';
|
7 | import { elementTypeAcceptingRef, refType } from '@material-ui/utils';
|
8 | import useForkRef from '../utils/useForkRef';
|
9 | import useEventCallback from '../utils/useEventCallback';
|
10 | import deprecatedPropType from '../utils/deprecatedPropType';
|
11 | import withStyles from '../styles/withStyles';
|
12 | import useIsFocusVisible from '../utils/useIsFocusVisible';
|
13 | import TouchRipple from './TouchRipple';
|
14 | export const styles = {
|
15 |
|
16 | root: {
|
17 | display: 'inline-flex',
|
18 | alignItems: 'center',
|
19 | justifyContent: 'center',
|
20 | position: 'relative',
|
21 | WebkitTapHighlightColor: 'transparent',
|
22 | backgroundColor: 'transparent',
|
23 |
|
24 |
|
25 | outline: 0,
|
26 | border: 0,
|
27 | margin: 0,
|
28 |
|
29 | borderRadius: 0,
|
30 | padding: 0,
|
31 |
|
32 | cursor: 'pointer',
|
33 | userSelect: 'none',
|
34 | verticalAlign: 'middle',
|
35 | '-moz-appearance': 'none',
|
36 |
|
37 | '-webkit-appearance': 'none',
|
38 |
|
39 | textDecoration: 'none',
|
40 |
|
41 | color: 'inherit',
|
42 | '&::-moz-focus-inner': {
|
43 | borderStyle: 'none'
|
44 |
|
45 | },
|
46 | '&$disabled': {
|
47 | pointerEvents: 'none',
|
48 |
|
49 | cursor: 'default'
|
50 | },
|
51 | '@media print': {
|
52 | colorAdjust: 'exact'
|
53 | }
|
54 | },
|
55 |
|
56 |
|
57 | disabled: {},
|
58 |
|
59 |
|
60 | focusVisible: {}
|
61 | };
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | const ButtonBase = React.forwardRef(function ButtonBase(props, ref) {
|
69 | const {
|
70 | action,
|
71 | buttonRef: buttonRefProp,
|
72 | centerRipple = false,
|
73 | children,
|
74 | classes,
|
75 | className,
|
76 | component = 'button',
|
77 | disabled = false,
|
78 | disableRipple = false,
|
79 | disableTouchRipple = false,
|
80 | focusRipple = false,
|
81 | focusVisibleClassName,
|
82 | onBlur,
|
83 | onClick,
|
84 | onFocus,
|
85 | onFocusVisible,
|
86 | onKeyDown,
|
87 | onKeyUp,
|
88 | onMouseDown,
|
89 | onMouseLeave,
|
90 | onMouseUp,
|
91 | onTouchEnd,
|
92 | onTouchMove,
|
93 | onTouchStart,
|
94 | onDragLeave,
|
95 | tabIndex = 0,
|
96 | TouchRippleProps,
|
97 | type = 'button'
|
98 | } = props,
|
99 | other = _objectWithoutPropertiesLoose(props, ["action", "buttonRef", "centerRipple", "children", "classes", "className", "component", "disabled", "disableRipple", "disableTouchRipple", "focusRipple", "focusVisibleClassName", "onBlur", "onClick", "onFocus", "onFocusVisible", "onKeyDown", "onKeyUp", "onMouseDown", "onMouseLeave", "onMouseUp", "onTouchEnd", "onTouchMove", "onTouchStart", "onDragLeave", "tabIndex", "TouchRippleProps", "type"]);
|
100 |
|
101 | const buttonRef = React.useRef(null);
|
102 |
|
103 | function getButtonNode() {
|
104 |
|
105 | return ReactDOM.findDOMNode(buttonRef.current);
|
106 | }
|
107 |
|
108 | const rippleRef = React.useRef(null);
|
109 | const [focusVisible, setFocusVisible] = React.useState(false);
|
110 |
|
111 | if (disabled && focusVisible) {
|
112 | setFocusVisible(false);
|
113 | }
|
114 |
|
115 | const {
|
116 | isFocusVisible,
|
117 | onBlurVisible,
|
118 | ref: focusVisibleRef
|
119 | } = useIsFocusVisible();
|
120 | React.useImperativeHandle(action, () => ({
|
121 | focusVisible: () => {
|
122 | setFocusVisible(true);
|
123 | buttonRef.current.focus();
|
124 | }
|
125 | }), []);
|
126 | React.useEffect(() => {
|
127 | if (focusVisible && focusRipple && !disableRipple) {
|
128 | rippleRef.current.pulsate();
|
129 | }
|
130 | }, [disableRipple, focusRipple, focusVisible]);
|
131 |
|
132 | function useRippleHandler(rippleAction, eventCallback, skipRippleAction = disableTouchRipple) {
|
133 | return useEventCallback(event => {
|
134 | if (eventCallback) {
|
135 | eventCallback(event);
|
136 | }
|
137 |
|
138 | const ignore = skipRippleAction;
|
139 |
|
140 | if (!ignore && rippleRef.current) {
|
141 | rippleRef.current[rippleAction](event);
|
142 | }
|
143 |
|
144 | return true;
|
145 | });
|
146 | }
|
147 |
|
148 | const handleMouseDown = useRippleHandler('start', onMouseDown);
|
149 | const handleDragLeave = useRippleHandler('stop', onDragLeave);
|
150 | const handleMouseUp = useRippleHandler('stop', onMouseUp);
|
151 | const handleMouseLeave = useRippleHandler('stop', event => {
|
152 | if (focusVisible) {
|
153 | event.preventDefault();
|
154 | }
|
155 |
|
156 | if (onMouseLeave) {
|
157 | onMouseLeave(event);
|
158 | }
|
159 | });
|
160 | const handleTouchStart = useRippleHandler('start', onTouchStart);
|
161 | const handleTouchEnd = useRippleHandler('stop', onTouchEnd);
|
162 | const handleTouchMove = useRippleHandler('stop', onTouchMove);
|
163 | const handleBlur = useRippleHandler('stop', event => {
|
164 | if (focusVisible) {
|
165 | onBlurVisible(event);
|
166 | setFocusVisible(false);
|
167 | }
|
168 |
|
169 | if (onBlur) {
|
170 | onBlur(event);
|
171 | }
|
172 | }, false);
|
173 | const handleFocus = useEventCallback(event => {
|
174 |
|
175 | if (!buttonRef.current) {
|
176 | buttonRef.current = event.currentTarget;
|
177 | }
|
178 |
|
179 | if (isFocusVisible(event)) {
|
180 | setFocusVisible(true);
|
181 |
|
182 | if (onFocusVisible) {
|
183 | onFocusVisible(event);
|
184 | }
|
185 | }
|
186 |
|
187 | if (onFocus) {
|
188 | onFocus(event);
|
189 | }
|
190 | });
|
191 |
|
192 | const isNonNativeButton = () => {
|
193 | const button = getButtonNode();
|
194 | return component && component !== 'button' && !(button.tagName === 'A' && button.href);
|
195 | };
|
196 | |
197 |
|
198 |
|
199 |
|
200 |
|
201 | const keydownRef = React.useRef(false);
|
202 | const handleKeyDown = useEventCallback(event => {
|
203 |
|
204 | if (focusRipple && !keydownRef.current && focusVisible && rippleRef.current && event.key === ' ') {
|
205 | keydownRef.current = true;
|
206 | event.persist();
|
207 | rippleRef.current.stop(event, () => {
|
208 | rippleRef.current.start(event);
|
209 | });
|
210 | }
|
211 |
|
212 | if (event.target === event.currentTarget && isNonNativeButton() && event.key === ' ') {
|
213 | event.preventDefault();
|
214 | }
|
215 |
|
216 | if (onKeyDown) {
|
217 | onKeyDown(event);
|
218 | }
|
219 |
|
220 |
|
221 | if (event.target === event.currentTarget && isNonNativeButton() && event.key === 'Enter' && !disabled) {
|
222 | event.preventDefault();
|
223 |
|
224 | if (onClick) {
|
225 | onClick(event);
|
226 | }
|
227 | }
|
228 | });
|
229 | const handleKeyUp = useEventCallback(event => {
|
230 |
|
231 |
|
232 | if (focusRipple && event.key === ' ' && rippleRef.current && focusVisible && !event.defaultPrevented) {
|
233 | keydownRef.current = false;
|
234 | event.persist();
|
235 | rippleRef.current.stop(event, () => {
|
236 | rippleRef.current.pulsate(event);
|
237 | });
|
238 | }
|
239 |
|
240 | if (onKeyUp) {
|
241 | onKeyUp(event);
|
242 | }
|
243 |
|
244 |
|
245 | if (onClick && event.target === event.currentTarget && isNonNativeButton() && event.key === ' ' && !event.defaultPrevented) {
|
246 | onClick(event);
|
247 | }
|
248 | });
|
249 | let ComponentProp = component;
|
250 |
|
251 | if (ComponentProp === 'button' && other.href) {
|
252 | ComponentProp = 'a';
|
253 | }
|
254 |
|
255 | const buttonProps = {};
|
256 |
|
257 | if (ComponentProp === 'button') {
|
258 | buttonProps.type = type;
|
259 | buttonProps.disabled = disabled;
|
260 | } else {
|
261 | if (ComponentProp !== 'a' || !other.href) {
|
262 | buttonProps.role = 'button';
|
263 | }
|
264 |
|
265 | buttonProps['aria-disabled'] = disabled;
|
266 | }
|
267 |
|
268 | const handleUserRef = useForkRef(buttonRefProp, ref);
|
269 | const handleOwnRef = useForkRef(focusVisibleRef, buttonRef);
|
270 | const handleRef = useForkRef(handleUserRef, handleOwnRef);
|
271 | const [mountedState, setMountedState] = React.useState(false);
|
272 | React.useEffect(() => {
|
273 | setMountedState(true);
|
274 | }, []);
|
275 | const enableTouchRipple = mountedState && !disableRipple && !disabled;
|
276 |
|
277 | if (process.env.NODE_ENV !== 'production') {
|
278 |
|
279 | React.useEffect(() => {
|
280 | if (enableTouchRipple && !rippleRef.current) {
|
281 | console.error(['Material-UI: The `component` prop provided to ButtonBase is invalid.', 'Please make sure the children prop is rendered in this custom component.'].join('\n'));
|
282 | }
|
283 | }, [enableTouchRipple]);
|
284 | }
|
285 |
|
286 | return React.createElement(ComponentProp, _extends({
|
287 | className: clsx(classes.root, className, focusVisible && [classes.focusVisible, focusVisibleClassName], disabled && classes.disabled),
|
288 | onBlur: handleBlur,
|
289 | onClick: onClick,
|
290 | onFocus: handleFocus,
|
291 | onKeyDown: handleKeyDown,
|
292 | onKeyUp: handleKeyUp,
|
293 | onMouseDown: handleMouseDown,
|
294 | onMouseLeave: handleMouseLeave,
|
295 | onMouseUp: handleMouseUp,
|
296 | onDragLeave: handleDragLeave,
|
297 | onTouchEnd: handleTouchEnd,
|
298 | onTouchMove: handleTouchMove,
|
299 | onTouchStart: handleTouchStart,
|
300 | ref: handleRef,
|
301 | tabIndex: disabled ? -1 : tabIndex
|
302 | }, buttonProps, other), children, enableTouchRipple ?
|
303 |
|
304 |
|
305 |
|
306 | React.createElement(TouchRipple, _extends({
|
307 | ref: rippleRef,
|
308 | center: centerRipple
|
309 | }, TouchRippleProps)) : null);
|
310 | });
|
311 | process.env.NODE_ENV !== "production" ? ButtonBase.propTypes = {
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 | |
318 |
|
319 |
|
320 |
|
321 | action: refType,
|
322 |
|
323 | |
324 |
|
325 |
|
326 |
|
327 |
|
328 |
|
329 | buttonRef: deprecatedPropType(refType, 'Use `ref` instead.'),
|
330 |
|
331 | |
332 |
|
333 |
|
334 |
|
335 | centerRipple: PropTypes.bool,
|
336 |
|
337 | |
338 |
|
339 |
|
340 | children: PropTypes.node,
|
341 |
|
342 | |
343 |
|
344 |
|
345 |
|
346 | classes: PropTypes.object,
|
347 |
|
348 | |
349 |
|
350 |
|
351 | className: PropTypes.string,
|
352 |
|
353 | |
354 |
|
355 |
|
356 |
|
357 | component: elementTypeAcceptingRef,
|
358 |
|
359 | |
360 |
|
361 |
|
362 | disabled: PropTypes.bool,
|
363 |
|
364 | |
365 |
|
366 |
|
367 |
|
368 |
|
369 |
|
370 | disableRipple: PropTypes.bool,
|
371 |
|
372 | |
373 |
|
374 |
|
375 | disableTouchRipple: PropTypes.bool,
|
376 |
|
377 | |
378 |
|
379 |
|
380 | focusRipple: PropTypes.bool,
|
381 |
|
382 | |
383 |
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 | focusVisibleClassName: PropTypes.string,
|
391 |
|
392 | |
393 |
|
394 |
|
395 | href: PropTypes.string,
|
396 |
|
397 | |
398 |
|
399 |
|
400 | onBlur: PropTypes.func,
|
401 |
|
402 | |
403 |
|
404 |
|
405 | onClick: PropTypes.func,
|
406 |
|
407 | |
408 |
|
409 |
|
410 | onDragLeave: PropTypes.func,
|
411 |
|
412 | |
413 |
|
414 |
|
415 | onFocus: PropTypes.func,
|
416 |
|
417 | |
418 |
|
419 |
|
420 |
|
421 | onFocusVisible: PropTypes.func,
|
422 |
|
423 | |
424 |
|
425 |
|
426 | onKeyDown: PropTypes.func,
|
427 |
|
428 | |
429 |
|
430 |
|
431 | onKeyUp: PropTypes.func,
|
432 |
|
433 | |
434 |
|
435 |
|
436 | onMouseDown: PropTypes.func,
|
437 |
|
438 | |
439 |
|
440 |
|
441 | onMouseLeave: PropTypes.func,
|
442 |
|
443 | |
444 |
|
445 |
|
446 | onMouseUp: PropTypes.func,
|
447 |
|
448 | |
449 |
|
450 |
|
451 | onTouchEnd: PropTypes.func,
|
452 |
|
453 | |
454 |
|
455 |
|
456 | onTouchMove: PropTypes.func,
|
457 |
|
458 | |
459 |
|
460 |
|
461 | onTouchStart: PropTypes.func,
|
462 |
|
463 | |
464 |
|
465 |
|
466 | tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
467 |
|
468 | |
469 |
|
470 |
|
471 | TouchRippleProps: PropTypes.object,
|
472 |
|
473 | |
474 |
|
475 |
|
476 | type: PropTypes.oneOfType([PropTypes.oneOf(['button', 'reset', 'submit']), PropTypes.string])
|
477 | } : void 0;
|
478 | export default withStyles(styles, {
|
479 | name: 'MuiButtonBase'
|
480 | })(ButtonBase); |
\ | No newline at end of file |