UNPKG

15.1 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 _reactIs = require("react-is");
19
20var _propTypes = _interopRequireDefault(require("prop-types"));
21
22var _clsx = _interopRequireDefault(require("clsx"));
23
24var _styles = require("@material-ui/core/styles");
25
26var _Zoom = _interopRequireDefault(require("@material-ui/core/Zoom"));
27
28var _Fab = _interopRequireDefault(require("@material-ui/core/Fab"));
29
30var _utils = require("@material-ui/core/utils");
31
32function getOrientation(direction) {
33 if (direction === 'up' || direction === 'down') {
34 return 'vertical';
35 }
36
37 if (direction === 'right' || direction === 'left') {
38 return 'horizontal';
39 }
40
41 return undefined;
42}
43
44function clamp(value, min, max) {
45 if (value < min) {
46 return min;
47 }
48
49 if (value > max) {
50 return max;
51 }
52
53 return value;
54}
55
56var dialRadius = 32;
57var spacingActions = 16;
58
59var styles = function styles(theme) {
60 return {
61 /* Styles applied to the root element. */
62 root: {
63 zIndex: theme.zIndex.speedDial,
64 display: 'flex',
65 alignItems: 'center',
66 pointerEvents: 'none'
67 },
68
69 /* Styles applied to the Fab component. */
70 fab: {
71 pointerEvents: 'auto'
72 },
73
74 /* Styles applied to the root if direction="up" */
75 directionUp: {
76 flexDirection: 'column-reverse',
77 '& $actions': {
78 flexDirection: 'column-reverse',
79 marginBottom: -dialRadius,
80 paddingBottom: spacingActions + dialRadius
81 }
82 },
83
84 /* Styles applied to the root if direction="down" */
85 directionDown: {
86 flexDirection: 'column',
87 '& $actions': {
88 flexDirection: 'column',
89 marginTop: -dialRadius,
90 paddingTop: spacingActions + dialRadius
91 }
92 },
93
94 /* Styles applied to the root if direction="left" */
95 directionLeft: {
96 flexDirection: 'row-reverse',
97 '& $actions': {
98 flexDirection: 'row-reverse',
99 marginRight: -dialRadius,
100 paddingRight: spacingActions + dialRadius
101 }
102 },
103
104 /* Styles applied to the root if direction="right" */
105 directionRight: {
106 flexDirection: 'row',
107 '& $actions': {
108 flexDirection: 'row',
109 marginLeft: -dialRadius,
110 paddingLeft: spacingActions + dialRadius
111 }
112 },
113
114 /* Styles applied to the actions (`children` wrapper) element. */
115 actions: {
116 display: 'flex',
117 pointerEvents: 'auto'
118 },
119
120 /* Styles applied to the actions (`children` wrapper) element if `open={false}`. */
121 actionsClosed: {
122 transition: 'top 0s linear 0.2s',
123 pointerEvents: 'none'
124 }
125 };
126};
127
128exports.styles = styles;
129var SpeedDial = /*#__PURE__*/React.forwardRef(function SpeedDial(props, ref) {
130 var ariaLabel = props.ariaLabel,
131 _props$FabProps = props.FabProps;
132 _props$FabProps = _props$FabProps === void 0 ? {} : _props$FabProps;
133 var origDialButtonRef = _props$FabProps.ref,
134 FabProps = (0, _objectWithoutProperties2.default)(_props$FabProps, ["ref"]),
135 childrenProp = props.children,
136 classes = props.classes,
137 className = props.className,
138 _props$direction = props.direction,
139 direction = _props$direction === void 0 ? 'up' : _props$direction,
140 _props$hidden = props.hidden,
141 hidden = _props$hidden === void 0 ? false : _props$hidden,
142 icon = props.icon,
143 onBlur = props.onBlur,
144 onClose = props.onClose,
145 onFocus = props.onFocus,
146 onKeyDown = props.onKeyDown,
147 onMouseEnter = props.onMouseEnter,
148 onMouseLeave = props.onMouseLeave,
149 onOpen = props.onOpen,
150 open = props.open,
151 openIcon = props.openIcon,
152 _props$TransitionComp = props.TransitionComponent,
153 TransitionComponent = _props$TransitionComp === void 0 ? _Zoom.default : _props$TransitionComp,
154 _props$transitionDura = props.transitionDuration,
155 transitionDuration = _props$transitionDura === void 0 ? {
156 enter: _styles.duration.enteringScreen,
157 exit: _styles.duration.leavingScreen
158 } : _props$transitionDura,
159 TransitionProps = props.TransitionProps,
160 other = (0, _objectWithoutProperties2.default)(props, ["ariaLabel", "FabProps", "children", "classes", "className", "direction", "hidden", "icon", "onBlur", "onClose", "onFocus", "onKeyDown", "onMouseEnter", "onMouseLeave", "onOpen", "open", "openIcon", "TransitionComponent", "transitionDuration", "TransitionProps"]);
161 var eventTimer = React.useRef();
162 React.useEffect(function () {
163 return function () {
164 clearTimeout(eventTimer.current);
165 };
166 }, []);
167 /**
168 * an index in actions.current
169 */
170
171 var focusedAction = React.useRef(0);
172 /**
173 * pressing this key while the focus is on a child SpeedDialAction focuses
174 * the next SpeedDialAction.
175 * It is equal to the first arrow key pressed while focus is on the SpeedDial
176 * that is not orthogonal to the direction.
177 * @type {utils.ArrowKey?}
178 */
179
180 var nextItemArrowKey = React.useRef();
181 /**
182 * refs to the Button that have an action associated to them in this SpeedDial
183 * [Fab, ...(SpeedDialActions > Button)]
184 * @type {HTMLButtonElement[]}
185 */
186
187 var actions = React.useRef([]);
188 actions.current = [actions.current[0]];
189 var handleOwnFabRef = React.useCallback(function (fabFef) {
190 actions.current[0] = fabFef;
191 }, []);
192 var handleFabRef = (0, _utils.useForkRef)(origDialButtonRef, handleOwnFabRef);
193 /**
194 * creates a ref callback for the Button in a SpeedDialAction
195 * Is called before the original ref callback for Button that was set in buttonProps
196 *
197 * @param dialActionIndex {number}
198 * @param origButtonRef {React.RefObject?}
199 */
200
201 var createHandleSpeedDialActionButtonRef = function createHandleSpeedDialActionButtonRef(dialActionIndex, origButtonRef) {
202 return function (buttonRef) {
203 actions.current[dialActionIndex + 1] = buttonRef;
204
205 if (origButtonRef) {
206 origButtonRef(buttonRef);
207 }
208 };
209 };
210
211 var handleKeyDown = function handleKeyDown(event) {
212 if (onKeyDown) {
213 onKeyDown(event);
214 }
215
216 var key = event.key.replace('Arrow', '').toLowerCase();
217 var _nextItemArrowKey$cur = nextItemArrowKey.current,
218 nextItemArrowKeyCurrent = _nextItemArrowKey$cur === void 0 ? key : _nextItemArrowKey$cur;
219
220 if (event.key === 'Escape') {
221 if (onClose) {
222 actions.current[0].focus();
223 onClose(event, 'escapeKeyDown');
224 }
225
226 return;
227 }
228
229 if (getOrientation(key) === getOrientation(nextItemArrowKeyCurrent) && getOrientation(key) !== undefined) {
230 event.preventDefault();
231 var actionStep = key === nextItemArrowKeyCurrent ? 1 : -1; // stay within array indices
232
233 var nextAction = clamp(focusedAction.current + actionStep, 0, actions.current.length - 1);
234 actions.current[nextAction].focus();
235 focusedAction.current = nextAction;
236 nextItemArrowKey.current = nextItemArrowKeyCurrent;
237 }
238 };
239
240 React.useEffect(function () {
241 // actions were closed while navigation state was not reset
242 if (!open) {
243 focusedAction.current = 0;
244 nextItemArrowKey.current = undefined;
245 }
246 }, [open]);
247
248 var handleClose = function handleClose(event) {
249 if (event.type === 'mouseleave' && onMouseLeave) {
250 onMouseLeave(event);
251 }
252
253 if (event.type === 'blur' && onBlur) {
254 onBlur(event);
255 }
256
257 clearTimeout(eventTimer.current);
258
259 if (onClose) {
260 if (event.type === 'blur') {
261 event.persist();
262 eventTimer.current = setTimeout(function () {
263 onClose(event, 'blur');
264 });
265 } else {
266 onClose(event, 'mouseLeave');
267 }
268 }
269 };
270
271 var handleClick = function handleClick(event) {
272 if (FabProps.onClick) {
273 FabProps.onClick(event);
274 }
275
276 clearTimeout(eventTimer.current);
277
278 if (open) {
279 if (onClose) {
280 onClose(event, 'toggle');
281 }
282 } else if (onOpen) {
283 onOpen(event, 'toggle');
284 }
285 };
286
287 var handleOpen = function handleOpen(event) {
288 if (event.type === 'mouseenter' && onMouseEnter) {
289 onMouseEnter(event);
290 }
291
292 if (event.type === 'focus' && onFocus) {
293 onFocus(event);
294 } // When moving the focus between two items,
295 // a chain if blur and focus event is triggered.
296 // We only handle the last event.
297
298
299 clearTimeout(eventTimer.current);
300
301 if (onOpen && !open) {
302 event.persist(); // Wait for a future focus or click event
303
304 eventTimer.current = setTimeout(function () {
305 var eventMap = {
306 focus: 'focus',
307 mouseenter: 'mouseEnter'
308 };
309 onOpen(event, eventMap[event.type]);
310 });
311 }
312 }; // Filter the label for valid id characters.
313
314
315 var id = ariaLabel.replace(/^[^a-z]+|[^\w:.-]+/gi, '');
316 var allItems = React.Children.toArray(childrenProp).filter(function (child) {
317 if (process.env.NODE_ENV !== 'production') {
318 if ((0, _reactIs.isFragment)(child)) {
319 console.error(["Material-UI: The SpeedDial component doesn't accept a Fragment as a child.", 'Consider providing an array instead.'].join('\n'));
320 }
321 }
322
323 return /*#__PURE__*/React.isValidElement(child);
324 });
325 var children = allItems.map(function (child, index) {
326 var _child$props$FabProps = child.props.FabProps;
327 _child$props$FabProps = _child$props$FabProps === void 0 ? {} : _child$props$FabProps;
328 var origButtonRef = _child$props$FabProps.ref,
329 ChildFabProps = (0, _objectWithoutProperties2.default)(_child$props$FabProps, ["ref"]);
330 return /*#__PURE__*/React.cloneElement(child, {
331 FabProps: (0, _extends2.default)({}, ChildFabProps, {
332 ref: createHandleSpeedDialActionButtonRef(index, origButtonRef)
333 }),
334 delay: 30 * (open ? index : allItems.length - index),
335 open: open,
336 id: "".concat(id, "-action-").concat(index)
337 });
338 });
339 return /*#__PURE__*/React.createElement("div", (0, _extends2.default)({
340 className: (0, _clsx.default)(classes.root, classes["direction".concat((0, _utils.capitalize)(direction))], className),
341 ref: ref,
342 role: "presentation",
343 onKeyDown: handleKeyDown,
344 onBlur: handleClose,
345 onFocus: handleOpen,
346 onMouseEnter: handleOpen,
347 onMouseLeave: handleClose
348 }, other), /*#__PURE__*/React.createElement(TransitionComponent, (0, _extends2.default)({
349 in: !hidden,
350 timeout: transitionDuration,
351 unmountOnExit: true
352 }, TransitionProps), /*#__PURE__*/React.createElement(_Fab.default, (0, _extends2.default)({
353 color: "primary",
354 "aria-label": ariaLabel,
355 "aria-haspopup": "true",
356 "aria-expanded": open,
357 "aria-controls": "".concat(id, "-actions")
358 }, FabProps, {
359 onClick: handleClick,
360 className: (0, _clsx.default)(classes.fab, FabProps.className),
361 ref: handleFabRef
362 }), /*#__PURE__*/React.isValidElement(icon) && (0, _utils.isMuiElement)(icon, ['SpeedDialIcon']) ? /*#__PURE__*/React.cloneElement(icon, {
363 open: open
364 }) : icon)), /*#__PURE__*/React.createElement("div", {
365 id: "".concat(id, "-actions"),
366 role: "menu",
367 "aria-orientation": getOrientation(direction),
368 className: (0, _clsx.default)(classes.actions, !open && classes.actionsClosed)
369 }, children));
370});
371process.env.NODE_ENV !== "production" ? SpeedDial.propTypes = {
372 // ----------------------------- Warning --------------------------------
373 // | These PropTypes are generated from the TypeScript type definitions |
374 // | To update them edit the d.ts file and run "yarn proptypes" |
375 // ----------------------------------------------------------------------
376
377 /**
378 * The aria-label of the button element.
379 * Also used to provide the `id` for the `SpeedDial` element and its children.
380 */
381 ariaLabel: _propTypes.default.string.isRequired,
382
383 /**
384 * SpeedDialActions to display when the SpeedDial is `open`.
385 */
386 children: _propTypes.default.node,
387
388 /**
389 * Override or extend the styles applied to the component.
390 * See [CSS API](#css) below for more details.
391 */
392 classes: _propTypes.default.object,
393
394 /**
395 * @ignore
396 */
397 className: _propTypes.default.string,
398
399 /**
400 * The direction the actions open relative to the floating action button.
401 */
402 direction: _propTypes.default.oneOf(['down', 'left', 'right', 'up']),
403
404 /**
405 * Props applied to the [`Fab`](/api/fab/) element.
406 */
407 FabProps: _propTypes.default.object,
408
409 /**
410 * If `true`, the SpeedDial will be hidden.
411 */
412 hidden: _propTypes.default.bool,
413
414 /**
415 * The icon to display in the SpeedDial Fab. The `SpeedDialIcon` component
416 * provides a default Icon with animation.
417 */
418 icon: _propTypes.default.node,
419
420 /**
421 * @ignore
422 */
423 onBlur: _propTypes.default.func,
424
425 /**
426 * Callback fired when the component requests to be closed.
427 *
428 * @param {object} event The event source of the callback.
429 * @param {string} reason Can be: `"toggle"`, `"blur"`, `"mouseLeave"`, `"escapeKeyDown"`.
430 */
431 onClose: _propTypes.default.func,
432
433 /**
434 * @ignore
435 */
436 onFocus: _propTypes.default.func,
437
438 /**
439 * @ignore
440 */
441 onKeyDown: _propTypes.default.func,
442
443 /**
444 * @ignore
445 */
446 onMouseEnter: _propTypes.default.func,
447
448 /**
449 * @ignore
450 */
451 onMouseLeave: _propTypes.default.func,
452
453 /**
454 * Callback fired when the component requests to be open.
455 *
456 * @param {object} event The event source of the callback.
457 * @param {string} reason Can be: `"toggle"`, `"focus"`, `"mouseEnter"`.
458 */
459 onOpen: _propTypes.default.func,
460
461 /**
462 * If `true`, the SpeedDial is open.
463 */
464 open: _propTypes.default.bool.isRequired,
465
466 /**
467 * The icon to display in the SpeedDial Fab when the SpeedDial is open.
468 */
469 openIcon: _propTypes.default.node,
470
471 /**
472 * The component used for the transition.
473 * [Follow this guide](/components/transitions/#transitioncomponent-prop) to learn more about the requirements for this component.
474 */
475 TransitionComponent: _propTypes.default.elementType,
476
477 /**
478 * The duration for the transition, in milliseconds.
479 * You may specify a single timeout for all transitions, or individually with an object.
480 */
481 transitionDuration: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.shape({
482 appear: _propTypes.default.number,
483 enter: _propTypes.default.number,
484 exit: _propTypes.default.number
485 })]),
486
487 /**
488 * Props applied to the [`Transition`](http://reactcommunity.org/react-transition-group/transition#Transition-props) element.
489 */
490 TransitionProps: _propTypes.default.object
491} : void 0;
492
493var _default = (0, _styles.withStyles)(styles, {
494 name: 'MuiSpeedDial'
495})(SpeedDial);
496
497exports.default = _default;
\No newline at end of file