This repository has been archived on 2022-10-07. You can view files and clone it, but cannot push or open issues or pull requests.
skynet-webportal/packages/dashboard-v2/src/components/Tabs/Tabs.js

107 lines
2.9 KiB
JavaScript
Raw Normal View History

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 `<div>` 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 (
<Container>
<Header>
<TabList variant={variant}>
{tabs.map((tab) => {
const tabId = getTabId(tab.props.id)
return cloneElement(tab, {
ref: tabsRefs[tabId],
id: tabId,
variant,
active: isActive(tabId),
onClick: () => onTabChange(tabId),
})
})}
<Divider />
<ActiveTabIndicator tabRef={activeTabRef} />
</TabList>
</Header>
<Body>
{panels.map((panel) => {
const tabId = getTabId(panel.props.tabId)
return cloneElement(panel, {
...panel.props,
tabId,
active: isActive(tabId),
})
})}
</Body>
</Container>
)
}
Tabs.propTypes = {
/**
* Should include `<Tab />` and `<TabPanel />` components.
*/
children: PropTypes.node.isRequired,
/**
* ID of the `<Tab />` 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',
}