/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import React, {cloneElement, type ReactElement} from 'react'; import clsx from 'clsx'; import { useScrollPositionBlocker, useTabs, sanitizeTabsChildren, type TabItemProps, } from '@docusaurus/theme-common/internal'; import useIsBrowser from '@docusaurus/useIsBrowser'; import type {Props} from '@theme/Tabs'; import styles from './styles.module.css'; function TabList({ className, block, selectedValue, selectValue, tabValues, }: Props & ReturnType) { const tabRefs: (HTMLLIElement | null)[] = []; const {blockElementScrollPositionUntilNextRender} = useScrollPositionBlocker(); const handleTabChange = ( event: | React.FocusEvent | React.MouseEvent | React.KeyboardEvent, ) => { const newTab = event.currentTarget; const newTabIndex = tabRefs.indexOf(newTab); const newTabValue = tabValues[newTabIndex]!.value; if (newTabValue !== selectedValue) { blockElementScrollPositionUntilNextRender(newTab); selectValue(newTabValue); } }; const handleKeydown = (event: React.KeyboardEvent) => { let focusElement: HTMLLIElement | null = null; switch (event.key) { case 'Enter': { handleTabChange(event); break; } case 'ArrowRight': { const nextTab = tabRefs.indexOf(event.currentTarget) + 1; focusElement = tabRefs[nextTab] ?? tabRefs[0]!; break; } case 'ArrowLeft': { const prevTab = tabRefs.indexOf(event.currentTarget) - 1; focusElement = tabRefs[prevTab] ?? tabRefs[tabRefs.length - 1]!; break; } default: break; } focusElement?.focus(); }; return (
    {tabValues.map(({value, label, attributes}) => (
  • tabRefs.push(tabControl)} onKeyDown={handleKeydown} onClick={handleTabChange} {...attributes} className={clsx( 'tabs__item', styles.tabItem, attributes?.className as string, { 'tabs__item--active': selectedValue === value, }, )}> {label ?? value}
  • ))}
); } function TabContent({ lazy, children, selectedValue, }: Props & ReturnType) { const childTabs = (Array.isArray(children) ? children : [children]).filter( Boolean, ) as ReactElement[]; if (lazy) { const selectedTabItem = childTabs.find( (tabItem) => tabItem.props.value === selectedValue, ); if (!selectedTabItem) { // fail-safe or fail-fast? not sure what's best here return null; } return cloneElement(selectedTabItem, {className: 'margin-top--md'}); } return (
{childTabs.map((tabItem, i) => cloneElement(tabItem, { key: i, hidden: tabItem.props.value !== selectedValue, }), )}
); } function TabsComponent(props: Props): JSX.Element { const tabs = useTabs(props); return (
); } export default function Tabs(props: Props): JSX.Element { const isBrowser = useIsBrowser(); return ( {sanitizeTabsChildren(props.children)} ); }