UNPKG

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