UNPKG

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