UNPKG

15.9 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4
5var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6
7Object.defineProperty(exports, "__esModule", {
8 value: true
9});
10exports.default = exports.styles = void 0;
11
12var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
13
14var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
15
16var React = _interopRequireWildcard(require("react"));
17
18var _propTypes = _interopRequireDefault(require("prop-types"));
19
20var ReactDOM = _interopRequireWildcard(require("react-dom"));
21
22var _clsx = _interopRequireDefault(require("clsx"));
23
24var _utils = require("@material-ui/utils");
25
26var _useForkRef = _interopRequireDefault(require("../utils/useForkRef"));
27
28var _useEventCallback = _interopRequireDefault(require("../utils/useEventCallback"));
29
30var _withStyles = _interopRequireDefault(require("../styles/withStyles"));
31
32var _useIsFocusVisible2 = _interopRequireDefault(require("../utils/useIsFocusVisible"));
33
34var _TouchRipple = _interopRequireDefault(require("./TouchRipple"));
35
36var styles = {
37 /* Styles applied to the root element. */
38 root: {
39 display: 'inline-flex',
40 alignItems: 'center',
41 justifyContent: 'center',
42 position: 'relative',
43 WebkitTapHighlightColor: 'transparent',
44 backgroundColor: 'transparent',
45 // Reset default value
46 // We disable the focus ring for mouse, touch and keyboard users.
47 outline: 0,
48 border: 0,
49 margin: 0,
50 // Remove the margin in Safari
51 borderRadius: 0,
52 padding: 0,
53 // Remove the padding in Firefox
54 cursor: 'pointer',
55 userSelect: 'none',
56 verticalAlign: 'middle',
57 '-moz-appearance': 'none',
58 // Reset
59 '-webkit-appearance': 'none',
60 // Reset
61 textDecoration: 'none',
62 // So we take precedent over the style of a native <a /> element.
63 color: 'inherit',
64 '&::-moz-focus-inner': {
65 borderStyle: 'none' // Remove Firefox dotted outline.
66
67 },
68 '&$disabled': {
69 pointerEvents: 'none',
70 // Disable link interactions
71 cursor: 'default'
72 },
73 '@media print': {
74 colorAdjust: 'exact'
75 }
76 },
77
78 /* Pseudo-class applied to the root element if `disabled={true}`. */
79 disabled: {},
80
81 /* Pseudo-class applied to the root element if keyboard focused. */
82 focusVisible: {}
83};
84/**
85 * `ButtonBase` contains as few styles as possible.
86 * It aims to be a simple building block for creating a button.
87 * It contains a load of style reset and some focus/ripple logic.
88 */
89
90exports.styles = styles;
91var ButtonBase = /*#__PURE__*/React.forwardRef(function ButtonBase(props, ref) {
92 var action = props.action,
93 buttonRefProp = props.buttonRef,
94 _props$centerRipple = props.centerRipple,
95 centerRipple = _props$centerRipple === void 0 ? false : _props$centerRipple,
96 children = props.children,
97 classes = props.classes,
98 className = props.className,
99 _props$component = props.component,
100 component = _props$component === void 0 ? 'button' : _props$component,
101 _props$disabled = props.disabled,
102 disabled = _props$disabled === void 0 ? false : _props$disabled,
103 _props$disableRipple = props.disableRipple,
104 disableRipple = _props$disableRipple === void 0 ? false : _props$disableRipple,
105 _props$disableTouchRi = props.disableTouchRipple,
106 disableTouchRipple = _props$disableTouchRi === void 0 ? false : _props$disableTouchRi,
107 _props$focusRipple = props.focusRipple,
108 focusRipple = _props$focusRipple === void 0 ? false : _props$focusRipple,
109 focusVisibleClassName = props.focusVisibleClassName,
110 onBlur = props.onBlur,
111 onClick = props.onClick,
112 onFocus = props.onFocus,
113 onFocusVisible = props.onFocusVisible,
114 onKeyDown = props.onKeyDown,
115 onKeyUp = props.onKeyUp,
116 onMouseDown = props.onMouseDown,
117 onMouseLeave = props.onMouseLeave,
118 onMouseUp = props.onMouseUp,
119 onTouchEnd = props.onTouchEnd,
120 onTouchMove = props.onTouchMove,
121 onTouchStart = props.onTouchStart,
122 onDragLeave = props.onDragLeave,
123 _props$tabIndex = props.tabIndex,
124 tabIndex = _props$tabIndex === void 0 ? 0 : _props$tabIndex,
125 TouchRippleProps = props.TouchRippleProps,
126 _props$type = props.type,
127 type = _props$type === void 0 ? 'button' : _props$type,
128 other = (0, _objectWithoutProperties2.default)(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"]);
129 var buttonRef = React.useRef(null);
130
131 function getButtonNode() {
132 // #StrictMode ready
133 return ReactDOM.findDOMNode(buttonRef.current);
134 }
135
136 var rippleRef = React.useRef(null);
137
138 var _React$useState = React.useState(false),
139 focusVisible = _React$useState[0],
140 setFocusVisible = _React$useState[1];
141
142 if (disabled && focusVisible) {
143 setFocusVisible(false);
144 }
145
146 var _useIsFocusVisible = (0, _useIsFocusVisible2.default)(),
147 isFocusVisible = _useIsFocusVisible.isFocusVisible,
148 onBlurVisible = _useIsFocusVisible.onBlurVisible,
149 focusVisibleRef = _useIsFocusVisible.ref;
150
151 React.useImperativeHandle(action, function () {
152 return {
153 focusVisible: function focusVisible() {
154 setFocusVisible(true);
155 buttonRef.current.focus();
156 }
157 };
158 }, []);
159 React.useEffect(function () {
160 if (focusVisible && focusRipple && !disableRipple) {
161 rippleRef.current.pulsate();
162 }
163 }, [disableRipple, focusRipple, focusVisible]);
164
165 function useRippleHandler(rippleAction, eventCallback) {
166 var skipRippleAction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : disableTouchRipple;
167 return (0, _useEventCallback.default)(function (event) {
168 if (eventCallback) {
169 eventCallback(event);
170 }
171
172 var ignore = skipRippleAction;
173
174 if (!ignore && rippleRef.current) {
175 rippleRef.current[rippleAction](event);
176 }
177
178 return true;
179 });
180 }
181
182 var handleMouseDown = useRippleHandler('start', onMouseDown);
183 var handleDragLeave = useRippleHandler('stop', onDragLeave);
184 var handleMouseUp = useRippleHandler('stop', onMouseUp);
185 var handleMouseLeave = useRippleHandler('stop', function (event) {
186 if (focusVisible) {
187 event.preventDefault();
188 }
189
190 if (onMouseLeave) {
191 onMouseLeave(event);
192 }
193 });
194 var handleTouchStart = useRippleHandler('start', onTouchStart);
195 var handleTouchEnd = useRippleHandler('stop', onTouchEnd);
196 var handleTouchMove = useRippleHandler('stop', onTouchMove);
197 var handleBlur = useRippleHandler('stop', function (event) {
198 if (focusVisible) {
199 onBlurVisible(event);
200 setFocusVisible(false);
201 }
202
203 if (onBlur) {
204 onBlur(event);
205 }
206 }, false);
207 var handleFocus = (0, _useEventCallback.default)(function (event) {
208 // Fix for https://github.com/facebook/react/issues/7769
209 if (!buttonRef.current) {
210 buttonRef.current = event.currentTarget;
211 }
212
213 if (isFocusVisible(event)) {
214 setFocusVisible(true);
215
216 if (onFocusVisible) {
217 onFocusVisible(event);
218 }
219 }
220
221 if (onFocus) {
222 onFocus(event);
223 }
224 });
225
226 var isNonNativeButton = function isNonNativeButton() {
227 var button = getButtonNode();
228 return component && component !== 'button' && !(button.tagName === 'A' && button.href);
229 };
230 /**
231 * IE 11 shim for https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat
232 */
233
234
235 var keydownRef = React.useRef(false);
236 var handleKeyDown = (0, _useEventCallback.default)(function (event) {
237 // Check if key is already down to avoid repeats being counted as multiple activations
238 if (focusRipple && !keydownRef.current && focusVisible && rippleRef.current && event.key === ' ') {
239 keydownRef.current = true;
240 event.persist();
241 rippleRef.current.stop(event, function () {
242 rippleRef.current.start(event);
243 });
244 }
245
246 if (event.target === event.currentTarget && isNonNativeButton() && event.key === ' ') {
247 event.preventDefault();
248 }
249
250 if (onKeyDown) {
251 onKeyDown(event);
252 } // Keyboard accessibility for non interactive elements
253
254
255 if (event.target === event.currentTarget && isNonNativeButton() && event.key === 'Enter' && !disabled) {
256 event.preventDefault();
257
258 if (onClick) {
259 onClick(event);
260 }
261 }
262 });
263 var handleKeyUp = (0, _useEventCallback.default)(function (event) {
264 // calling preventDefault in keyUp on a <button> will not dispatch a click event if Space is pressed
265 // https://codesandbox.io/s/button-keyup-preventdefault-dn7f0
266 if (focusRipple && event.key === ' ' && rippleRef.current && focusVisible && !event.defaultPrevented) {
267 keydownRef.current = false;
268 event.persist();
269 rippleRef.current.stop(event, function () {
270 rippleRef.current.pulsate(event);
271 });
272 }
273
274 if (onKeyUp) {
275 onKeyUp(event);
276 } // Keyboard accessibility for non interactive elements
277
278
279 if (onClick && event.target === event.currentTarget && isNonNativeButton() && event.key === ' ' && !event.defaultPrevented) {
280 onClick(event);
281 }
282 });
283 var ComponentProp = component;
284
285 if (ComponentProp === 'button' && other.href) {
286 ComponentProp = 'a';
287 }
288
289 var buttonProps = {};
290
291 if (ComponentProp === 'button') {
292 buttonProps.type = type;
293 buttonProps.disabled = disabled;
294 } else {
295 if (ComponentProp !== 'a' || !other.href) {
296 buttonProps.role = 'button';
297 }
298
299 buttonProps['aria-disabled'] = disabled;
300 }
301
302 var handleUserRef = (0, _useForkRef.default)(buttonRefProp, ref);
303 var handleOwnRef = (0, _useForkRef.default)(focusVisibleRef, buttonRef);
304 var handleRef = (0, _useForkRef.default)(handleUserRef, handleOwnRef);
305
306 var _React$useState2 = React.useState(false),
307 mountedState = _React$useState2[0],
308 setMountedState = _React$useState2[1];
309
310 React.useEffect(function () {
311 setMountedState(true);
312 }, []);
313 var enableTouchRipple = mountedState && !disableRipple && !disabled;
314
315 if (process.env.NODE_ENV !== 'production') {
316 // eslint-disable-next-line react-hooks/rules-of-hooks
317 React.useEffect(function () {
318 if (enableTouchRipple && !rippleRef.current) {
319 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'));
320 }
321 }, [enableTouchRipple]);
322 }
323
324 return /*#__PURE__*/React.createElement(ComponentProp, (0, _extends2.default)({
325 className: (0, _clsx.default)(classes.root, className, focusVisible && [classes.focusVisible, focusVisibleClassName], disabled && classes.disabled),
326 onBlur: handleBlur,
327 onClick: onClick,
328 onFocus: handleFocus,
329 onKeyDown: handleKeyDown,
330 onKeyUp: handleKeyUp,
331 onMouseDown: handleMouseDown,
332 onMouseLeave: handleMouseLeave,
333 onMouseUp: handleMouseUp,
334 onDragLeave: handleDragLeave,
335 onTouchEnd: handleTouchEnd,
336 onTouchMove: handleTouchMove,
337 onTouchStart: handleTouchStart,
338 ref: handleRef,
339 tabIndex: disabled ? -1 : tabIndex
340 }, buttonProps, other), children, enableTouchRipple ?
341 /*#__PURE__*/
342
343 /* TouchRipple is only needed client-side, x2 boost on the server. */
344 React.createElement(_TouchRipple.default, (0, _extends2.default)({
345 ref: rippleRef,
346 center: centerRipple
347 }, TouchRippleProps)) : null);
348});
349process.env.NODE_ENV !== "production" ? ButtonBase.propTypes = {
350 // ----------------------------- Warning --------------------------------
351 // | These PropTypes are generated from the TypeScript type definitions |
352 // | To update them edit the d.ts file and run "yarn proptypes" |
353 // ----------------------------------------------------------------------
354
355 /**
356 * A ref for imperative actions.
357 * It currently only supports `focusVisible()` action.
358 */
359 action: _utils.refType,
360
361 /**
362 * @ignore
363 *
364 * Use that prop to pass a ref to the native button component.
365 * @deprecated Use `ref` instead.
366 */
367 buttonRef: _utils.refType,
368
369 /**
370 * If `true`, the ripples will be centered.
371 * They won't start at the cursor interaction position.
372 */
373 centerRipple: _propTypes.default.bool,
374
375 /**
376 * The content of the component.
377 */
378 children: _propTypes.default.node,
379
380 /**
381 * Override or extend the styles applied to the component.
382 * See [CSS API](#css) below for more details.
383 */
384 classes: _propTypes.default.object,
385
386 /**
387 * @ignore
388 */
389 className: _propTypes.default.string,
390
391 /**
392 * The component used for the root node.
393 * Either a string to use a HTML element or a component.
394 */
395 component: _utils.elementTypeAcceptingRef,
396
397 /**
398 * If `true`, the base button will be disabled.
399 */
400 disabled: _propTypes.default.bool,
401
402 /**
403 * If `true`, the ripple effect will be disabled.
404 *
405 * ⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure
406 * to highlight the element by applying separate styles with the `focusVisibleClassName`.
407 */
408 disableRipple: _propTypes.default.bool,
409
410 /**
411 * If `true`, the touch ripple effect will be disabled.
412 */
413 disableTouchRipple: _propTypes.default.bool,
414
415 /**
416 * If `true`, the base button will have a keyboard focus ripple.
417 */
418 focusRipple: _propTypes.default.bool,
419
420 /**
421 * This prop can help a person know which element has the keyboard focus.
422 * The class name will be applied when the element gain the focus through a keyboard interaction.
423 * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo).
424 * The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/master/explainer.md).
425 * A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components
426 * if needed.
427 */
428 focusVisibleClassName: _propTypes.default.string,
429
430 /**
431 * @ignore
432 */
433 href: _propTypes.default.string,
434
435 /**
436 * @ignore
437 */
438 onBlur: _propTypes.default.func,
439
440 /**
441 * @ignore
442 */
443 onClick: _propTypes.default.func,
444
445 /**
446 * @ignore
447 */
448 onDragLeave: _propTypes.default.func,
449
450 /**
451 * @ignore
452 */
453 onFocus: _propTypes.default.func,
454
455 /**
456 * Callback fired when the component is focused with a keyboard.
457 * We trigger a `onFocus` callback too.
458 */
459 onFocusVisible: _propTypes.default.func,
460
461 /**
462 * @ignore
463 */
464 onKeyDown: _propTypes.default.func,
465
466 /**
467 * @ignore
468 */
469 onKeyUp: _propTypes.default.func,
470
471 /**
472 * @ignore
473 */
474 onMouseDown: _propTypes.default.func,
475
476 /**
477 * @ignore
478 */
479 onMouseLeave: _propTypes.default.func,
480
481 /**
482 * @ignore
483 */
484 onMouseUp: _propTypes.default.func,
485
486 /**
487 * @ignore
488 */
489 onTouchEnd: _propTypes.default.func,
490
491 /**
492 * @ignore
493 */
494 onTouchMove: _propTypes.default.func,
495
496 /**
497 * @ignore
498 */
499 onTouchStart: _propTypes.default.func,
500
501 /**
502 * @ignore
503 */
504 tabIndex: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]),
505
506 /**
507 * Props applied to the `TouchRipple` element.
508 */
509 TouchRippleProps: _propTypes.default.object,
510
511 /**
512 * @ignore
513 */
514 type: _propTypes.default.oneOfType([_propTypes.default.oneOf(['button', 'reset', 'submit']), _propTypes.default.string])
515} : void 0;
516
517var _default = (0, _withStyles.default)(styles, {
518 name: 'MuiButtonBase'
519})(ButtonBase);
520
521exports.default = _default;
\No newline at end of file