2024-07-17 16:07:38 +02:00

120 lines
3.5 KiB
TypeScript

import { useCallback, useEffect, useRef, useState } from "react";
import classes from "./classes.module.scss";
import HorizontalTab, { ITab } from "./HorizontalTab";
import VerticalTabs from "./VerticalTabs";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
import { useDebounce, useWindowSize } from "@uidotdev/usehooks";
import useOpenable from "@Front/Hooks/useOpenable";
export type ITabValue<T> = T & {
id: unknown;
};
type ITabInternal<T> = ITab & {
key?: string;
value: ITabValue<T>;
};
type IProps<T> = {
tabs: ITabInternal<T>[];
onSelect: (value: T) => void;
};
export default function Tabs<T>(props: IProps<T>) {
const { onSelect } = props;
const rootRef = useRef<HTMLDivElement>(null);
const [visibleElements, setVisibleElements] = useState<ITabInternal<T>[]>([]);
const [overflowedElements, setOverflowedElements] = useState<ITabInternal<T>[]>([]);
const [selectedTab, setSelectedTab] = useState<ITabValue<T>>(props.tabs[0]!.value);
const { close, isOpen, toggle } = useOpenable();
const windowSize = useWindowSize();
const windowSizeDebounced = useDebounce(windowSize, 100);
const calculateVisibleElements = useCallback(() => {
const container = rootRef.current;
if (!container) return;
const containerWidth = container.offsetWidth;
let totalWidth = 115;
let visibleCount = 0;
const children = Array.from(container.children) as HTMLDivElement[];
for (let i = 0; i < children.length; i++) {
totalWidth += children[i]!.offsetWidth;
if (totalWidth > containerWidth) {
break;
}
visibleCount++;
}
setVisibleElements(props.tabs.slice(0, visibleCount));
setOverflowedElements(props.tabs.slice(visibleCount));
}, [props.tabs]);
useEffect(() => {
calculateVisibleElements();
}, [calculateVisibleElements, windowSizeDebounced]);
const handleSelect = useCallback(
(value: ITabValue<T>) => {
setSelectedTab(value);
onSelect(value);
close();
},
[close, onSelect],
);
return (
<div className={classes["root"]}>
<div className={classes["hidden-tester"]} ref={rootRef}>
{props.tabs.map((element, index) => (
<HorizontalTab<T>
label={element.label}
key={element.key ?? index}
value={element.value}
onSelect={handleSelect}
isSelected={element.value.id === selectedTab.id}
/>
))}
</div>
<div className={classes["horizontal-container"]}>
<div className={classes["horizontal-tab"]}>
{visibleElements.map((element, index) => (
<HorizontalTab<T>
label={element.label}
key={element.key ?? index}
value={element.value}
onSelect={handleSelect}
isSelected={element.value.id === selectedTab.id}
/>
))}
</div>
{overflowedElements.length > 0 && (
<div className={classes["show-more-container"]}>
<div className={classes["show-more"]} onClick={toggle}>
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_500}>
{overflowedElements.length}&nbsp;de&nbsp;plus...
</Typography>
</div>
<div className={classes["vertical-container"]} data-visible={isOpen}>
{overflowedElements.length > 0 &&
overflowedElements.map((element, index) => (
<VerticalTabs<T>
label={element.label}
key={element.key ?? index}
value={element.value}
onSelect={handleSelect}
isSelected={selectedTab === element.value}
/>
))}
</div>
</div>
)}
</div>
</div>
);
}