import clsx from "clsx"; import { FC, HTMLAttributes, ReactNode, useEffect, useRef, useState, } from "react"; import ChevronDown from "@dndbeyond/fontawesome-cache/svgs/solid/chevron-down.svg"; import { Popover } from "../Popover"; import styles from "./styles.module.css"; /** * This component renders a set of tabs with associated content panels. * It supports different variants for tab behavior, including default, * collapse, and toggle modes. Tabs can be limited in number of visible * items, with overflow handled via a "More" dropdown menu. * ** Variants: * - default: Standard tab behavior where clicking a tab shows its content. * - collapse: Always has a selected tab, but clicking it again collapses the content. * - fullwidth: Tabs stretch to fill the container width. */ interface TabItem { label: ReactNode; content: ReactNode; className?: string; id: string; } interface TabListProps extends HTMLAttributes { tabs: Array; variant?: "default" | "collapse" | "fullwidth"; defaultActiveId?: string; maxNavItemsShow?: number; onChangeTab?: (id: string) => void; themed?: boolean; forceShow?: boolean; } export const TabList: FC = ({ className, tabs, defaultActiveId, maxNavItemsShow = 7, variant = "default", onChangeTab, themed, forceShow, ...props }) => { const tabListRef = useRef(null!); const [activeTab, setActiveTab] = useState(defaultActiveId || tabs[0]?.id); const [isTabVisible, setIsTabVisible] = useState( forceShow ?? activeTab !== "" ); const tabsWithContent = tabs.filter((tab) => tab); const visibleTabs = tabsWithContent.slice(0, maxNavItemsShow); const hiddenTabs = tabsWithContent.slice(maxNavItemsShow); const selectedTab = tabs.find((tab) => tab?.id === activeTab); const isToggleable = variant === "collapse"; const handleTabClick = (id: string) => { if (id === activeTab && variant === "collapse") { setIsTabVisible(!isTabVisible); } if (id !== activeTab) { setActiveTab(id); setIsTabVisible(true); if (onChangeTab) { onChangeTab(id); } } }; useEffect( () => { // Update CSS variable for number of tabs const tabListStyles = tabListRef.current?.style; if (tabListStyles) { tabListStyles.setProperty("--tab-count", tabs.length.toString()); tabListStyles.setProperty( "--tab-active-position", tabs.indexOf(selectedTab as TabItem).toString() ); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [tabs.length, selectedTab] ); return (
{/* List of visible tabs */} {visibleTabs.map((tab: TabItem) => (
  • ))} {/* Toggle and menu for hidden tabs */} {hiddenTabs.length > 0 && (
  • More} > {hiddenTabs.map((tab: TabItem) => (
  • ))}
  • )}
    {/* Content of the active tab */}
    {isTabVisible && activeTab !== "" && selectedTab?.content}
    ); };