/* ============================================================================ * Copyright (c) Palo Alto Networks * * 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, useRef, useEffect, useState, ReactElement, LegacyRef, } from "react"; import { sanitizeTabsChildren, type TabItemProps, TabProps, TabsProvider, useScrollPositionBlocker, useTabsContextValue, } from "@docusaurus/theme-common/internal"; import useIsBrowser from "@docusaurus/useIsBrowser"; import clsx from "clsx"; import flatten from "lodash/flatten"; export interface SchemaTabsProps extends TabProps { /** * Optional callback fired when the selected tab changes. * Receives the index of the newly selected tab. */ onChange?: (index: number) => void; } function TabList({ className, block, selectedValue, selectValue, tabValues, onChange, }: SchemaTabsProps & 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); // Notify parent component of the tab change onChange?.(newTabIndex); } }; 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(); }; const tabItemListContainerRef = useRef(null); const [showTabArrows, setShowTabArrows] = useState(false); useEffect(() => { const resizeObserver = new ResizeObserver((entries) => { for (let entry of entries) { requestAnimationFrame(() => { if (entry.target.clientWidth < entry.target.scrollWidth) { setShowTabArrows(true); } else { setShowTabArrows(false); } }); } }); resizeObserver.observe(tabItemListContainerRef.current!); return () => { resizeObserver.disconnect(); }; }, []); const handleRightClick = (e: any) => { e.preventDefault(); if (tabItemListContainerRef.current) { tabItemListContainerRef.current.scrollLeft += 90; } }; const handleLeftClick = (e: any) => { e.preventDefault(); if (tabItemListContainerRef.current) { tabItemListContainerRef.current.scrollLeft -= 90; } }; return (
{showTabArrows && (
); } function TabContent({ lazy, children, selectedValue, }: SchemaTabsProps & ReturnType) { const childTabs = (Array.isArray(children) ? children : [children]).filter( Boolean ) as ReactElement[]; const flattenedChildTabs = flatten(childTabs); if (lazy) { const selectedTabItem = flattenedChildTabs.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}
; } function TabsComponent(props: SchemaTabsProps): React.JSX.Element { const tabs = useTabsContextValue(props); return (
); } export default function SchemaTabs( props: SchemaTabsProps ): React.JSX.Element | null { const isBrowser = useIsBrowser(); const children = Array.isArray(props.children) ? props.children.filter(Boolean) : props.children ? [props.children] : []; if (children.length === 0) { return null; } let sanitizedChildren; try { sanitizedChildren = sanitizeTabsChildren(children); } catch { return null; } if ( !sanitizedChildren || (Array.isArray(sanitizedChildren) && sanitizedChildren.length === 0) ) { return null; } return ( {sanitizedChildren} ); }