UNPKG

@spaced-out/ui-design-system

Version:
162 lines (143 loc) 4.71 kB
// @flow strict import * as React from 'react'; import {useWindowSize} from '../../../hooks/useWindowSize/useWindowSize'; import {size36, size100} from '../../../styles/variables/_size'; import {spaceMedium} from '../../../styles/variables/_space'; import {classify} from '../../../utils/classify'; import type {ElevationType} from '../../Tooltip'; import {TabDropdown} from './TabDropdown'; import css from './TabList.module.css'; type ClassNames = $ReadOnly<{wrapper?: string}>; export type TabListProps = { classNames?: ClassNames, onSelect?: ({tabId: string, label?: string}) => mixed, children?: React.Node, size?: 'medium', selectedTab?: {tabId: string, label?: string}, menuWidth?: string, elevation?: ElevationType, }; export const TabList: React$AbstractComponent<TabListProps, HTMLDivElement> = React.forwardRef<TabListProps, HTMLDivElement>( ( { classNames, children, size, onSelect, selectedTab, menuWidth, elevation = 'modal', }: TabListProps, forwardRef, ): React.Node => { const ref = React.useRef(null); React.useImperativeHandle(forwardRef, () => ref.current); const {width} = useWindowSize(); const [containerWidth, setContainerWidth] = React.useState(0); const childrenWithProps = () => { const childrenArray = React.Children.toArray(children); const totalChildCount = childrenArray.length; let tabListWidth = 0; const menuOptions = []; let nodes: React.Node[] = []; const tabListNodes: React.Node[] = []; for (let i = 0; i < totalChildCount; i++) { const child = childrenArray[i]; const { width = size100, tabId, label, disabled, iconName, iconType, } = child.props; const widthInt = parseInt(width); tabListWidth = tabListWidth + widthInt; if (tabListWidth < containerWidth || i === 0) { const childOnSelect = (params) => { onSelect && onSelect(params); // call the tab level onSelect }; tabListNodes.push( React.cloneElement(child, { size, onSelect: childOnSelect, selectedTab, }), ); } else { menuOptions.push({ key: tabId, label, disabled, iconLeft: iconName, iconLeftType: iconType, }); } } const menuOnSelect = ({key, label}) => { onSelect && onSelect({tabId: key, label}); }; const selectedKeys = [selectedTab?.tabId || '']; const isMenuOptionSelected = (() => { for (let i = 0; i < menuOptions.length; i++) { if (menuOptions[i].key === selectedTab?.tabId) { return true; } } return false; })(); const tabDropDownNode = menuOptions.length ? ( <TabDropdown key={'tabDropdown' + menuOptions.length} size={size} onOptionSelect={menuOnSelect} props={{ tab: { iconName: 'ellipsis', tabId: 'tab-dropdown', selectedTab: { tabId: isMenuOptionSelected ? 'tab-dropdown' : '', }, }, menu: { isFluid: false, menuDisabled: false, options: menuOptions, selectedKeys, width: menuWidth, }, }} elevation={elevation} /> ) : null; nodes = [...tabListNodes, tabDropDownNode]; return nodes; }; React.useLayoutEffect(() => { if (ref.current && ref.current.offsetWidth) { const availableContainerWidth = ref.current.offsetWidth - (parseInt(size36) + parseInt(spaceMedium)); setContainerWidth(availableContainerWidth); } // it depends on width of the screen as ref.current.offsetWidth will not provide the events when changed. }, [ref.current, width]); return ( <div ref={ref} data-testid="Tabs" className={classify( css.tabsContainer, { [css.mediumSize]: size === 'medium', [css.smallSize]: size === 'small', }, classNames?.wrapper, )} > {containerWidth ? childrenWithProps() : null} </div> ); }, );