UNPKG

30.7 kBJavaScriptView Raw
1import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
2import _extends from "@babel/runtime/helpers/esm/extends";
3import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
4import * as React from 'react';
5import { isFragment } from 'react-is';
6import PropTypes from 'prop-types';
7import clsx from 'clsx';
8import { refType } from '@mui/utils';
9import { unstable_composeClasses as composeClasses } from '@mui/base';
10import styled from '../styles/styled';
11import useThemeProps from '../styles/useThemeProps';
12import useTheme from '../styles/useTheme';
13import debounce from '../utils/debounce';
14import { getNormalizedScrollLeft, detectScrollType } from '../utils/scrollLeft';
15import animate from '../internal/animate';
16import ScrollbarSize from './ScrollbarSize';
17import TabScrollButton from '../TabScrollButton';
18import useEventCallback from '../utils/useEventCallback';
19import tabsClasses, { getTabsUtilityClass } from './tabsClasses';
20import ownerDocument from '../utils/ownerDocument';
21import ownerWindow from '../utils/ownerWindow';
22import { jsx as _jsx } from "react/jsx-runtime";
23import { jsxs as _jsxs } from "react/jsx-runtime";
24
25var nextItem = function nextItem(list, item) {
26 if (list === item) {
27 return list.firstChild;
28 }
29
30 if (item && item.nextElementSibling) {
31 return item.nextElementSibling;
32 }
33
34 return list.firstChild;
35};
36
37var previousItem = function previousItem(list, item) {
38 if (list === item) {
39 return list.lastChild;
40 }
41
42 if (item && item.previousElementSibling) {
43 return item.previousElementSibling;
44 }
45
46 return list.lastChild;
47};
48
49var moveFocus = function moveFocus(list, currentFocus, traversalFunction) {
50 var wrappedOnce = false;
51 var nextFocus = traversalFunction(list, currentFocus);
52
53 while (nextFocus) {
54 // Prevent infinite loop.
55 if (nextFocus === list.firstChild) {
56 if (wrappedOnce) {
57 return;
58 }
59
60 wrappedOnce = true;
61 } // Same logic as useAutocomplete.js
62
63
64 var nextFocusDisabled = nextFocus.disabled || nextFocus.getAttribute('aria-disabled') === 'true';
65
66 if (!nextFocus.hasAttribute('tabindex') || nextFocusDisabled) {
67 // Move to the next element.
68 nextFocus = traversalFunction(list, nextFocus);
69 } else {
70 nextFocus.focus();
71 return;
72 }
73 }
74};
75
76var useUtilityClasses = function useUtilityClasses(ownerState) {
77 var vertical = ownerState.vertical,
78 fixed = ownerState.fixed,
79 hideScrollbar = ownerState.hideScrollbar,
80 scrollableX = ownerState.scrollableX,
81 scrollableY = ownerState.scrollableY,
82 centered = ownerState.centered,
83 scrollButtonsHideMobile = ownerState.scrollButtonsHideMobile,
84 classes = ownerState.classes;
85 var slots = {
86 root: ['root', vertical && 'vertical'],
87 scroller: ['scroller', fixed && 'fixed', hideScrollbar && 'hideScrollbar', scrollableX && 'scrollableX', scrollableY && 'scrollableY'],
88 flexContainer: ['flexContainer', vertical && 'flexContainerVertical', centered && 'centered'],
89 indicator: ['indicator'],
90 scrollButtons: ['scrollButtons', scrollButtonsHideMobile && 'scrollButtonsHideMobile'],
91 scrollableX: [scrollableX && 'scrollableX'],
92 hideScrollbar: [hideScrollbar && 'hideScrollbar']
93 };
94 return composeClasses(slots, getTabsUtilityClass, classes);
95};
96
97var TabsRoot = styled('div', {
98 name: 'MuiTabs',
99 slot: 'Root',
100 overridesResolver: function overridesResolver(props, styles) {
101 var ownerState = props.ownerState;
102 return [_defineProperty({}, "& .".concat(tabsClasses.scrollButtons), styles.scrollButtons), _defineProperty({}, "& .".concat(tabsClasses.scrollButtons), ownerState.scrollButtonsHideMobile && styles.scrollButtonsHideMobile), styles.root, ownerState.vertical && styles.vertical];
103 }
104})(function (_ref3) {
105 var ownerState = _ref3.ownerState,
106 theme = _ref3.theme;
107 return _extends({
108 overflow: 'hidden',
109 minHeight: 48,
110 // Add iOS momentum scrolling for iOS < 13.0
111 WebkitOverflowScrolling: 'touch',
112 display: 'flex'
113 }, ownerState.vertical && {
114 flexDirection: 'column'
115 }, ownerState.scrollButtonsHideMobile && _defineProperty({}, "& .".concat(tabsClasses.scrollButtons), _defineProperty({}, theme.breakpoints.down('sm'), {
116 display: 'none'
117 })));
118});
119var TabsScroller = styled('div', {
120 name: 'MuiTabs',
121 slot: 'Scroller',
122 overridesResolver: function overridesResolver(props, styles) {
123 var ownerState = props.ownerState;
124 return [styles.scroller, ownerState.fixed && styles.fixed, ownerState.hideScrollbar && styles.hideScrollbar, ownerState.scrollableX && styles.scrollableX, ownerState.scrollableY && styles.scrollableY];
125 }
126})(function (_ref5) {
127 var ownerState = _ref5.ownerState;
128 return _extends({
129 position: 'relative',
130 display: 'inline-block',
131 flex: '1 1 auto',
132 whiteSpace: 'nowrap'
133 }, ownerState.fixed && {
134 overflowX: 'hidden',
135 width: '100%'
136 }, ownerState.hideScrollbar && {
137 // Hide dimensionless scrollbar on macOS
138 scrollbarWidth: 'none',
139 // Firefox
140 '&::-webkit-scrollbar': {
141 display: 'none' // Safari + Chrome
142
143 }
144 }, ownerState.scrollableX && {
145 overflowX: 'auto',
146 overflowY: 'hidden'
147 }, ownerState.scrollableY && {
148 overflowY: 'auto',
149 overflowX: 'hidden'
150 });
151});
152var FlexContainer = styled('div', {
153 name: 'MuiTabs',
154 slot: 'FlexContainer',
155 overridesResolver: function overridesResolver(props, styles) {
156 var ownerState = props.ownerState;
157 return [styles.flexContainer, ownerState.vertical && styles.flexContainerVertical, ownerState.centered && styles.centered];
158 }
159})(function (_ref6) {
160 var ownerState = _ref6.ownerState;
161 return _extends({
162 display: 'flex'
163 }, ownerState.vertical && {
164 flexDirection: 'column'
165 }, ownerState.centered && {
166 justifyContent: 'center'
167 });
168});
169var TabsIndicator = styled('span', {
170 name: 'MuiTabs',
171 slot: 'Indicator',
172 overridesResolver: function overridesResolver(props, styles) {
173 return styles.indicator;
174 }
175})(function (_ref7) {
176 var ownerState = _ref7.ownerState,
177 theme = _ref7.theme;
178 return _extends({
179 position: 'absolute',
180 height: 2,
181 bottom: 0,
182 width: '100%',
183 transition: theme.transitions.create()
184 }, ownerState.indicatorColor === 'primary' && {
185 backgroundColor: (theme.vars || theme).palette.primary.main
186 }, ownerState.indicatorColor === 'secondary' && {
187 backgroundColor: (theme.vars || theme).palette.secondary.main
188 }, ownerState.vertical && {
189 height: '100%',
190 width: 2,
191 right: 0
192 });
193});
194var TabsScrollbarSize = styled(ScrollbarSize, {
195 name: 'MuiTabs',
196 slot: 'ScrollbarSize'
197})({
198 overflowX: 'auto',
199 overflowY: 'hidden',
200 // Hide dimensionless scrollbar on macOS
201 scrollbarWidth: 'none',
202 // Firefox
203 '&::-webkit-scrollbar': {
204 display: 'none' // Safari + Chrome
205
206 }
207});
208var defaultIndicatorStyle = {};
209var warnedOnceTabPresent = false;
210var Tabs = /*#__PURE__*/React.forwardRef(function Tabs(inProps, ref) {
211 var props = useThemeProps({
212 props: inProps,
213 name: 'MuiTabs'
214 });
215 var theme = useTheme();
216 var isRtl = theme.direction === 'rtl';
217
218 var ariaLabel = props['aria-label'],
219 ariaLabelledBy = props['aria-labelledby'],
220 action = props.action,
221 _props$centered = props.centered,
222 centered = _props$centered === void 0 ? false : _props$centered,
223 childrenProp = props.children,
224 className = props.className,
225 _props$component = props.component,
226 component = _props$component === void 0 ? 'div' : _props$component,
227 _props$allowScrollBut = props.allowScrollButtonsMobile,
228 allowScrollButtonsMobile = _props$allowScrollBut === void 0 ? false : _props$allowScrollBut,
229 _props$indicatorColor = props.indicatorColor,
230 indicatorColor = _props$indicatorColor === void 0 ? 'primary' : _props$indicatorColor,
231 onChange = props.onChange,
232 _props$orientation = props.orientation,
233 orientation = _props$orientation === void 0 ? 'horizontal' : _props$orientation,
234 _props$ScrollButtonCo = props.ScrollButtonComponent,
235 ScrollButtonComponent = _props$ScrollButtonCo === void 0 ? TabScrollButton : _props$ScrollButtonCo,
236 _props$scrollButtons = props.scrollButtons,
237 scrollButtons = _props$scrollButtons === void 0 ? 'auto' : _props$scrollButtons,
238 selectionFollowsFocus = props.selectionFollowsFocus,
239 _props$TabIndicatorPr = props.TabIndicatorProps,
240 TabIndicatorProps = _props$TabIndicatorPr === void 0 ? {} : _props$TabIndicatorPr,
241 _props$TabScrollButto = props.TabScrollButtonProps,
242 TabScrollButtonProps = _props$TabScrollButto === void 0 ? {} : _props$TabScrollButto,
243 _props$textColor = props.textColor,
244 textColor = _props$textColor === void 0 ? 'primary' : _props$textColor,
245 value = props.value,
246 _props$variant = props.variant,
247 variant = _props$variant === void 0 ? 'standard' : _props$variant,
248 _props$visibleScrollb = props.visibleScrollbar,
249 visibleScrollbar = _props$visibleScrollb === void 0 ? false : _props$visibleScrollb,
250 other = _objectWithoutProperties(props, ["aria-label", "aria-labelledby", "action", "centered", "children", "className", "component", "allowScrollButtonsMobile", "indicatorColor", "onChange", "orientation", "ScrollButtonComponent", "scrollButtons", "selectionFollowsFocus", "TabIndicatorProps", "TabScrollButtonProps", "textColor", "value", "variant", "visibleScrollbar"]);
251
252 var scrollable = variant === 'scrollable';
253 var vertical = orientation === 'vertical';
254 var scrollStart = vertical ? 'scrollTop' : 'scrollLeft';
255 var start = vertical ? 'top' : 'left';
256 var end = vertical ? 'bottom' : 'right';
257 var clientSize = vertical ? 'clientHeight' : 'clientWidth';
258 var size = vertical ? 'height' : 'width';
259
260 var ownerState = _extends({}, props, {
261 component: component,
262 allowScrollButtonsMobile: allowScrollButtonsMobile,
263 indicatorColor: indicatorColor,
264 orientation: orientation,
265 vertical: vertical,
266 scrollButtons: scrollButtons,
267 textColor: textColor,
268 variant: variant,
269 visibleScrollbar: visibleScrollbar,
270 fixed: !scrollable,
271 hideScrollbar: scrollable && !visibleScrollbar,
272 scrollableX: scrollable && !vertical,
273 scrollableY: scrollable && vertical,
274 centered: centered && !scrollable,
275 scrollButtonsHideMobile: !allowScrollButtonsMobile
276 });
277
278 var classes = useUtilityClasses(ownerState);
279
280 if (process.env.NODE_ENV !== 'production') {
281 if (centered && scrollable) {
282 console.error('MUI: You can not use the `centered={true}` and `variant="scrollable"` properties ' + 'at the same time on a `Tabs` component.');
283 }
284 }
285
286 var _React$useState = React.useState(false),
287 mounted = _React$useState[0],
288 setMounted = _React$useState[1];
289
290 var _React$useState2 = React.useState(defaultIndicatorStyle),
291 indicatorStyle = _React$useState2[0],
292 setIndicatorStyle = _React$useState2[1];
293
294 var _React$useState3 = React.useState({
295 start: false,
296 end: false
297 }),
298 displayScroll = _React$useState3[0],
299 setDisplayScroll = _React$useState3[1];
300
301 var _React$useState4 = React.useState({
302 overflow: 'hidden',
303 scrollbarWidth: 0
304 }),
305 scrollerStyle = _React$useState4[0],
306 setScrollerStyle = _React$useState4[1];
307
308 var valueToIndex = new Map();
309 var tabsRef = React.useRef(null);
310 var tabListRef = React.useRef(null);
311
312 var getTabsMeta = function getTabsMeta() {
313 var tabsNode = tabsRef.current;
314 var tabsMeta;
315
316 if (tabsNode) {
317 var rect = tabsNode.getBoundingClientRect(); // create a new object with ClientRect class props + scrollLeft
318
319 tabsMeta = {
320 clientWidth: tabsNode.clientWidth,
321 scrollLeft: tabsNode.scrollLeft,
322 scrollTop: tabsNode.scrollTop,
323 scrollLeftNormalized: getNormalizedScrollLeft(tabsNode, theme.direction),
324 scrollWidth: tabsNode.scrollWidth,
325 top: rect.top,
326 bottom: rect.bottom,
327 left: rect.left,
328 right: rect.right
329 };
330 }
331
332 var tabMeta;
333
334 if (tabsNode && value !== false) {
335 var _children = tabListRef.current.children;
336
337 if (_children.length > 0) {
338 var tab = _children[valueToIndex.get(value)];
339
340 if (process.env.NODE_ENV !== 'production') {
341 if (!tab) {
342 console.error(["MUI: The `value` provided to the Tabs component is invalid.", "None of the Tabs' children match with \"".concat(value, "\"."), valueToIndex.keys ? "You can provide one of the following values: ".concat(Array.from(valueToIndex.keys()).join(', '), ".") : null].join('\n'));
343 }
344 }
345
346 tabMeta = tab ? tab.getBoundingClientRect() : null;
347
348 if (process.env.NODE_ENV !== 'production') {
349 if (process.env.NODE_ENV !== 'test' && !warnedOnceTabPresent && tabMeta && tabMeta.width === 0 && tabMeta.height === 0) {
350 tabsMeta = null;
351 console.error(['MUI: The `value` provided to the Tabs component is invalid.', "The Tab with this `value` (\"".concat(value, "\") is not part of the document layout."), "Make sure the tab item is present in the document or that it's not `display: none`."].join('\n'));
352 warnedOnceTabPresent = true;
353 }
354 }
355 }
356 }
357
358 return {
359 tabsMeta: tabsMeta,
360 tabMeta: tabMeta
361 };
362 };
363
364 var updateIndicatorState = useEventCallback(function () {
365 var _newIndicatorStyle;
366
367 var _getTabsMeta = getTabsMeta(),
368 tabsMeta = _getTabsMeta.tabsMeta,
369 tabMeta = _getTabsMeta.tabMeta;
370
371 var startValue = 0;
372 var startIndicator;
373
374 if (vertical) {
375 startIndicator = 'top';
376
377 if (tabMeta && tabsMeta) {
378 startValue = tabMeta.top - tabsMeta.top + tabsMeta.scrollTop;
379 }
380 } else {
381 startIndicator = isRtl ? 'right' : 'left';
382
383 if (tabMeta && tabsMeta) {
384 var correction = isRtl ? tabsMeta.scrollLeftNormalized + tabsMeta.clientWidth - tabsMeta.scrollWidth : tabsMeta.scrollLeft;
385 startValue = (isRtl ? -1 : 1) * (tabMeta[startIndicator] - tabsMeta[startIndicator] + correction);
386 }
387 }
388
389 var newIndicatorStyle = (_newIndicatorStyle = {}, _defineProperty(_newIndicatorStyle, startIndicator, startValue), _defineProperty(_newIndicatorStyle, size, tabMeta ? tabMeta[size] : 0), _newIndicatorStyle); // IE11 support, replace with Number.isNaN
390 // eslint-disable-next-line no-restricted-globals
391
392 if (isNaN(indicatorStyle[startIndicator]) || isNaN(indicatorStyle[size])) {
393 setIndicatorStyle(newIndicatorStyle);
394 } else {
395 var dStart = Math.abs(indicatorStyle[startIndicator] - newIndicatorStyle[startIndicator]);
396 var dSize = Math.abs(indicatorStyle[size] - newIndicatorStyle[size]);
397
398 if (dStart >= 1 || dSize >= 1) {
399 setIndicatorStyle(newIndicatorStyle);
400 }
401 }
402 });
403
404 var scroll = function scroll(scrollValue) {
405 var _ref8 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
406 _ref8$animation = _ref8.animation,
407 animation = _ref8$animation === void 0 ? true : _ref8$animation;
408
409 if (animation) {
410 animate(scrollStart, tabsRef.current, scrollValue, {
411 duration: theme.transitions.duration.standard
412 });
413 } else {
414 tabsRef.current[scrollStart] = scrollValue;
415 }
416 };
417
418 var moveTabsScroll = function moveTabsScroll(delta) {
419 var scrollValue = tabsRef.current[scrollStart];
420
421 if (vertical) {
422 scrollValue += delta;
423 } else {
424 scrollValue += delta * (isRtl ? -1 : 1); // Fix for Edge
425
426 scrollValue *= isRtl && detectScrollType() === 'reverse' ? -1 : 1;
427 }
428
429 scroll(scrollValue);
430 };
431
432 var getScrollSize = function getScrollSize() {
433 var containerSize = tabsRef.current[clientSize];
434 var totalSize = 0;
435 var children = Array.from(tabListRef.current.children);
436
437 for (var i = 0; i < children.length; i += 1) {
438 var tab = children[i];
439
440 if (totalSize + tab[clientSize] > containerSize) {
441 // If the first item is longer than the container size, then only scroll
442 // by the container size.
443 if (i === 0) {
444 totalSize = containerSize;
445 }
446
447 break;
448 }
449
450 totalSize += tab[clientSize];
451 }
452
453 return totalSize;
454 };
455
456 var handleStartScrollClick = function handleStartScrollClick() {
457 moveTabsScroll(-1 * getScrollSize());
458 };
459
460 var handleEndScrollClick = function handleEndScrollClick() {
461 moveTabsScroll(getScrollSize());
462 }; // TODO Remove <ScrollbarSize /> as browser support for hidding the scrollbar
463 // with CSS improves.
464
465
466 var handleScrollbarSizeChange = React.useCallback(function (scrollbarWidth) {
467 setScrollerStyle({
468 overflow: null,
469 scrollbarWidth: scrollbarWidth
470 });
471 }, []);
472
473 var getConditionalElements = function getConditionalElements() {
474 var conditionalElements = {};
475 conditionalElements.scrollbarSizeListener = scrollable ? /*#__PURE__*/_jsx(TabsScrollbarSize, {
476 onChange: handleScrollbarSizeChange,
477 className: clsx(classes.scrollableX, classes.hideScrollbar)
478 }) : null;
479 var scrollButtonsActive = displayScroll.start || displayScroll.end;
480 var showScrollButtons = scrollable && (scrollButtons === 'auto' && scrollButtonsActive || scrollButtons === true);
481 conditionalElements.scrollButtonStart = showScrollButtons ? /*#__PURE__*/_jsx(ScrollButtonComponent, _extends({
482 orientation: orientation,
483 direction: isRtl ? 'right' : 'left',
484 onClick: handleStartScrollClick,
485 disabled: !displayScroll.start
486 }, TabScrollButtonProps, {
487 className: clsx(classes.scrollButtons, TabScrollButtonProps.className)
488 })) : null;
489 conditionalElements.scrollButtonEnd = showScrollButtons ? /*#__PURE__*/_jsx(ScrollButtonComponent, _extends({
490 orientation: orientation,
491 direction: isRtl ? 'left' : 'right',
492 onClick: handleEndScrollClick,
493 disabled: !displayScroll.end
494 }, TabScrollButtonProps, {
495 className: clsx(classes.scrollButtons, TabScrollButtonProps.className)
496 })) : null;
497 return conditionalElements;
498 };
499
500 var scrollSelectedIntoView = useEventCallback(function (animation) {
501 var _getTabsMeta2 = getTabsMeta(),
502 tabsMeta = _getTabsMeta2.tabsMeta,
503 tabMeta = _getTabsMeta2.tabMeta;
504
505 if (!tabMeta || !tabsMeta) {
506 return;
507 }
508
509 if (tabMeta[start] < tabsMeta[start]) {
510 // left side of button is out of view
511 var nextScrollStart = tabsMeta[scrollStart] + (tabMeta[start] - tabsMeta[start]);
512 scroll(nextScrollStart, {
513 animation: animation
514 });
515 } else if (tabMeta[end] > tabsMeta[end]) {
516 // right side of button is out of view
517 var _nextScrollStart = tabsMeta[scrollStart] + (tabMeta[end] - tabsMeta[end]);
518
519 scroll(_nextScrollStart, {
520 animation: animation
521 });
522 }
523 });
524 var updateScrollButtonState = useEventCallback(function () {
525 if (scrollable && scrollButtons !== false) {
526 var _tabsRef$current = tabsRef.current,
527 scrollTop = _tabsRef$current.scrollTop,
528 scrollHeight = _tabsRef$current.scrollHeight,
529 clientHeight = _tabsRef$current.clientHeight,
530 scrollWidth = _tabsRef$current.scrollWidth,
531 clientWidth = _tabsRef$current.clientWidth;
532 var showStartScroll;
533 var showEndScroll;
534
535 if (vertical) {
536 showStartScroll = scrollTop > 1;
537 showEndScroll = scrollTop < scrollHeight - clientHeight - 1;
538 } else {
539 var scrollLeft = getNormalizedScrollLeft(tabsRef.current, theme.direction); // use 1 for the potential rounding error with browser zooms.
540
541 showStartScroll = isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1;
542 showEndScroll = !isRtl ? scrollLeft < scrollWidth - clientWidth - 1 : scrollLeft > 1;
543 }
544
545 if (showStartScroll !== displayScroll.start || showEndScroll !== displayScroll.end) {
546 setDisplayScroll({
547 start: showStartScroll,
548 end: showEndScroll
549 });
550 }
551 }
552 });
553 React.useEffect(function () {
554 var handleResize = debounce(function () {
555 // If the Tabs component is replaced by Suspense with a fallback, the last
556 // ResizeObserver's handler that runs because of the change in the layout is trying to
557 // access a dom node that is no longer there (as the fallback component is being shown instead).
558 // See https://github.com/mui/material-ui/issues/33276
559 // TODO: Add tests that will ensure the component is not failing when
560 // replaced by Suspense with a fallback, once React is updated to version 18
561 if (tabsRef.current) {
562 updateIndicatorState();
563 updateScrollButtonState();
564 }
565 });
566 var win = ownerWindow(tabsRef.current);
567 win.addEventListener('resize', handleResize);
568 var resizeObserver;
569
570 if (typeof ResizeObserver !== 'undefined') {
571 resizeObserver = new ResizeObserver(handleResize);
572 Array.from(tabListRef.current.children).forEach(function (child) {
573 resizeObserver.observe(child);
574 });
575 }
576
577 return function () {
578 handleResize.clear();
579 win.removeEventListener('resize', handleResize);
580
581 if (resizeObserver) {
582 resizeObserver.disconnect();
583 }
584 };
585 }, [updateIndicatorState, updateScrollButtonState]);
586 var handleTabsScroll = React.useMemo(function () {
587 return debounce(function () {
588 updateScrollButtonState();
589 });
590 }, [updateScrollButtonState]);
591 React.useEffect(function () {
592 return function () {
593 handleTabsScroll.clear();
594 };
595 }, [handleTabsScroll]);
596 React.useEffect(function () {
597 setMounted(true);
598 }, []);
599 React.useEffect(function () {
600 updateIndicatorState();
601 updateScrollButtonState();
602 });
603 React.useEffect(function () {
604 // Don't animate on the first render.
605 scrollSelectedIntoView(defaultIndicatorStyle !== indicatorStyle);
606 }, [scrollSelectedIntoView, indicatorStyle]);
607 React.useImperativeHandle(action, function () {
608 return {
609 updateIndicator: updateIndicatorState,
610 updateScrollButtons: updateScrollButtonState
611 };
612 }, [updateIndicatorState, updateScrollButtonState]);
613
614 var indicator = /*#__PURE__*/_jsx(TabsIndicator, _extends({}, TabIndicatorProps, {
615 className: clsx(classes.indicator, TabIndicatorProps.className),
616 ownerState: ownerState,
617 style: _extends({}, indicatorStyle, TabIndicatorProps.style)
618 }));
619
620 var childIndex = 0;
621 var children = React.Children.map(childrenProp, function (child) {
622 if (! /*#__PURE__*/React.isValidElement(child)) {
623 return null;
624 }
625
626 if (process.env.NODE_ENV !== 'production') {
627 if (isFragment(child)) {
628 console.error(["MUI: The Tabs component doesn't accept a Fragment as a child.", 'Consider providing an array instead.'].join('\n'));
629 }
630 }
631
632 var childValue = child.props.value === undefined ? childIndex : child.props.value;
633 valueToIndex.set(childValue, childIndex);
634 var selected = childValue === value;
635 childIndex += 1;
636 return /*#__PURE__*/React.cloneElement(child, _extends({
637 fullWidth: variant === 'fullWidth',
638 indicator: selected && !mounted && indicator,
639 selected: selected,
640 selectionFollowsFocus: selectionFollowsFocus,
641 onChange: onChange,
642 textColor: textColor,
643 value: childValue
644 }, childIndex === 1 && value === false && !child.props.tabIndex ? {
645 tabIndex: 0
646 } : {}));
647 });
648
649 var handleKeyDown = function handleKeyDown(event) {
650 var list = tabListRef.current;
651 var currentFocus = ownerDocument(list).activeElement; // Keyboard navigation assumes that [role="tab"] are siblings
652 // though we might warn in the future about nested, interactive elements
653 // as a a11y violation
654
655 var role = currentFocus.getAttribute('role');
656
657 if (role !== 'tab') {
658 return;
659 }
660
661 var previousItemKey = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';
662 var nextItemKey = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';
663
664 if (orientation === 'horizontal' && isRtl) {
665 // swap previousItemKey with nextItemKey
666 previousItemKey = 'ArrowRight';
667 nextItemKey = 'ArrowLeft';
668 }
669
670 switch (event.key) {
671 case previousItemKey:
672 event.preventDefault();
673 moveFocus(list, currentFocus, previousItem);
674 break;
675
676 case nextItemKey:
677 event.preventDefault();
678 moveFocus(list, currentFocus, nextItem);
679 break;
680
681 case 'Home':
682 event.preventDefault();
683 moveFocus(list, null, nextItem);
684 break;
685
686 case 'End':
687 event.preventDefault();
688 moveFocus(list, null, previousItem);
689 break;
690
691 default:
692 break;
693 }
694 };
695
696 var conditionalElements = getConditionalElements();
697 return /*#__PURE__*/_jsxs(TabsRoot, _extends({
698 className: clsx(classes.root, className),
699 ownerState: ownerState,
700 ref: ref,
701 as: component
702 }, other, {
703 children: [conditionalElements.scrollButtonStart, conditionalElements.scrollbarSizeListener, /*#__PURE__*/_jsxs(TabsScroller, {
704 className: classes.scroller,
705 ownerState: ownerState,
706 style: _defineProperty({
707 overflow: scrollerStyle.overflow
708 }, vertical ? "margin".concat(isRtl ? 'Left' : 'Right') : 'marginBottom', visibleScrollbar ? undefined : -scrollerStyle.scrollbarWidth),
709 ref: tabsRef,
710 onScroll: handleTabsScroll,
711 children: [/*#__PURE__*/_jsx(FlexContainer, {
712 "aria-label": ariaLabel,
713 "aria-labelledby": ariaLabelledBy,
714 "aria-orientation": orientation === 'vertical' ? 'vertical' : null,
715 className: classes.flexContainer,
716 ownerState: ownerState,
717 onKeyDown: handleKeyDown,
718 ref: tabListRef,
719 role: "tablist",
720 children: children
721 }), mounted && indicator]
722 }), conditionalElements.scrollButtonEnd]
723 }));
724});
725process.env.NODE_ENV !== "production" ? Tabs.propTypes
726/* remove-proptypes */
727= {
728 // ----------------------------- Warning --------------------------------
729 // | These PropTypes are generated from the TypeScript type definitions |
730 // | To update them edit the d.ts file and run "yarn proptypes" |
731 // ----------------------------------------------------------------------
732
733 /**
734 * Callback fired when the component mounts.
735 * This is useful when you want to trigger an action programmatically.
736 * It supports two actions: `updateIndicator()` and `updateScrollButtons()`
737 *
738 * @param {object} actions This object contains all possible actions
739 * that can be triggered programmatically.
740 */
741 action: refType,
742
743 /**
744 * If `true`, the scroll buttons aren't forced hidden on mobile.
745 * By default the scroll buttons are hidden on mobile and takes precedence over `scrollButtons`.
746 * @default false
747 */
748 allowScrollButtonsMobile: PropTypes.bool,
749
750 /**
751 * The label for the Tabs as a string.
752 */
753 'aria-label': PropTypes.string,
754
755 /**
756 * An id or list of ids separated by a space that label the Tabs.
757 */
758 'aria-labelledby': PropTypes.string,
759
760 /**
761 * If `true`, the tabs are centered.
762 * This prop is intended for large views.
763 * @default false
764 */
765 centered: PropTypes.bool,
766
767 /**
768 * The content of the component.
769 */
770 children: PropTypes.node,
771
772 /**
773 * Override or extend the styles applied to the component.
774 */
775 classes: PropTypes.object,
776
777 /**
778 * @ignore
779 */
780 className: PropTypes.string,
781
782 /**
783 * The component used for the root node.
784 * Either a string to use a HTML element or a component.
785 */
786 component: PropTypes.elementType,
787
788 /**
789 * Determines the color of the indicator.
790 * @default 'primary'
791 */
792 indicatorColor: PropTypes
793 /* @typescript-to-proptypes-ignore */
794 .oneOfType([PropTypes.oneOf(['primary', 'secondary']), PropTypes.string]),
795
796 /**
797 * Callback fired when the value changes.
798 *
799 * @param {React.SyntheticEvent} event The event source of the callback. **Warning**: This is a generic event not a change event.
800 * @param {any} value We default to the index of the child (number)
801 */
802 onChange: PropTypes.func,
803
804 /**
805 * The component orientation (layout flow direction).
806 * @default 'horizontal'
807 */
808 orientation: PropTypes.oneOf(['horizontal', 'vertical']),
809
810 /**
811 * The component used to render the scroll buttons.
812 * @default TabScrollButton
813 */
814 ScrollButtonComponent: PropTypes.elementType,
815
816 /**
817 * Determine behavior of scroll buttons when tabs are set to scroll:
818 *
819 * - `auto` will only present them when not all the items are visible.
820 * - `true` will always present them.
821 * - `false` will never present them.
822 *
823 * By default the scroll buttons are hidden on mobile.
824 * This behavior can be disabled with `allowScrollButtonsMobile`.
825 * @default 'auto'
826 */
827 scrollButtons: PropTypes
828 /* @typescript-to-proptypes-ignore */
829 .oneOf(['auto', false, true]),
830
831 /**
832 * If `true` the selected tab changes on focus. Otherwise it only
833 * changes on activation.
834 */
835 selectionFollowsFocus: PropTypes.bool,
836
837 /**
838 * The system prop that allows defining system overrides as well as additional CSS styles.
839 */
840 sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
841
842 /**
843 * Props applied to the tab indicator element.
844 * @default {}
845 */
846 TabIndicatorProps: PropTypes.object,
847
848 /**
849 * Props applied to the [`TabScrollButton`](/material-ui/api/tab-scroll-button/) element.
850 * @default {}
851 */
852 TabScrollButtonProps: PropTypes.object,
853
854 /**
855 * Determines the color of the `Tab`.
856 * @default 'primary'
857 */
858 textColor: PropTypes.oneOf(['inherit', 'primary', 'secondary']),
859
860 /**
861 * The value of the currently selected `Tab`.
862 * If you don't want any selected `Tab`, you can set this prop to `false`.
863 */
864 value: PropTypes.any,
865
866 /**
867 * Determines additional display behavior of the tabs:
868 *
869 * - `scrollable` will invoke scrolling properties and allow for horizontally
870 * scrolling (or swiping) of the tab bar.
871 * -`fullWidth` will make the tabs grow to use all the available space,
872 * which should be used for small views, like on mobile.
873 * - `standard` will render the default state.
874 * @default 'standard'
875 */
876 variant: PropTypes.oneOf(['fullWidth', 'scrollable', 'standard']),
877
878 /**
879 * If `true`, the scrollbar is visible. It can be useful when displaying
880 * a long vertical list of tabs.
881 * @default false
882 */
883 visibleScrollbar: PropTypes.bool
884} : void 0;
885export default Tabs;
\No newline at end of file