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', })`` const Header = styled.div.attrs({ className: 'relative flex justify-start overflow-hidden', })`` 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`` /** * 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(() => getTabId(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', }