UNPKG

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