@spaced-out/ui-design-system
Version:
Sense UI components library
162 lines (143 loc) • 4.71 kB
Flow
// @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>
);
},
);