import { cloneElement, useCallback, useEffect, useMemo, useState } from "react"; import PropTypes from "prop-types"; import styled from "styled-components"; import { ActiveTabIndicator } from "./ActiveTabIndicator"; import { usePrefixedTabIds, useTabsChildren } from "./hooks"; const Container = styled.div.attrs({ className: "tabs-container flex flex-col h-full", })``; const Header = styled.div.attrs({ className: "relative flex justify-start overflow-hidden grow-0 shrink-0", })``; const TabList = styled.div.attrs(({ variant }) => ({ role: "tablist", className: `relative inline-grid grid-flow-col auto-cols-fr ${variant === "regular" ? "w-full sm:w-auto" : "w-full"}`, }))``; const Divider = styled.div.attrs({ "aria-hidden": "true", className: "absolute bottom-0 w-screen border-b border-b-palette-200", })` right: calc(-100vw - 2px); `; const Body = styled.div.attrs({ className: "grow min-h-0" })``; /** * Besides documented props, it accepts all HMTL attributes a `
` element does. */ export const Tabs = ({ defaultTab, children, variant }) => { const getTabId = usePrefixedTabIds(); const { tabs, panels, tabsRefs } = useTabsChildren(children, getTabId); const defaultTabId = useMemo(() => { const requestedTabIsPresent = tabs.find(({ props }) => props.id === defaultTab); return getTabId(requestedTabIsPresent ? defaultTab : tabs[0].props.id); }, [getTabId, defaultTab, tabs]); const [activeTabId, setActiveTabId] = useState(defaultTabId); const [activeTabRef, setActiveTabRef] = useState(tabsRefs[activeTabId]); const isActive = (id) => id === activeTabId; const onTabChange = useCallback( (id) => { setActiveTabId(id); }, [setActiveTabId] ); useEffect(() => { // Refresh active tab indicator whenever active tab changes. setActiveTabRef(tabsRefs[activeTabId]); }, [setActiveTabRef, tabsRefs, activeTabId]); return (
{tabs.map((tab) => { const tabId = getTabId(tab.props.id); return cloneElement(tab, { ref: tabsRefs[tabId], id: tabId, variant, active: isActive(tabId), onClick: () => onTabChange(tabId), }); })}
{panels.map((panel) => { const tabId = getTabId(panel.props.tabId); return cloneElement(panel, { ...panel.props, tabId, active: isActive(tabId), }); })}
); }; Tabs.propTypes = { /** * Should include `` and `` components. */ children: PropTypes.node.isRequired, /** * ID of the `` which should be open by default */ defaultTab: PropTypes.string, /** * `regular` (default) will make the tabs only take as much space as they need * * `fill` will make the tabs spread throughout the available width */ variant: PropTypes.oneOf(["regular", "fill"]), }; Tabs.defaultProps = { variant: "regular", };