Files
leonpan-assets/src/component/Common/ResponsiveTabs.tsx
2025-10-19 13:31:11 +00:00

105 lines
3.2 KiB
TypeScript
Executable File

import * as React from "react";
import { useLayoutEffect, useRef, useState } from "react";
import { Box, ListItemIcon, ListItemText, Menu, MenuItem, Typography, useMediaQuery, useTheme } from "@mui/material";
import { StyledTab, StyledTabs } from "./StyledComponents.tsx";
import CaretDown from "../Icons/CaretDown.tsx";
import { useTranslation } from "react-i18next";
import { bindMenu, bindTrigger, usePopupState } from "material-ui-popup-state/hooks";
export interface Tab<T> {
label: React.ReactNode;
value: T;
icon?: React.ReactElement;
}
export interface ResponsiveTabsProps<T> {
tabs: Tab<T>[];
value: T;
onChange: (event: React.SyntheticEvent, value: T) => void;
}
const ResponsiveTabs = <T,>({ tabs, value, onChange }: ResponsiveTabsProps<T>) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const [hideTabs, setHideTabs] = useState(false);
const tabsRef = useRef<HTMLDivElement>(null);
const { t } = useTranslation();
const moreOptionState = usePopupState({
variant: "popover",
popupId: "tabMore",
});
const { onClose, ...menuProps } = bindMenu(moreOptionState);
useLayoutEffect(() => {
const checkOverflow = () => {
if (tabsRef.current?.children[0]?.children[0]) {
setHideTabs((e) =>
e
? true
: (tabsRef.current?.children[0]?.children[0]?.scrollWidth ?? 0) >
(tabsRef.current?.children[0]?.children[0]?.clientWidth ?? 0),
);
}
};
checkOverflow();
window.addEventListener("resize", checkOverflow);
return () => window.removeEventListener("resize", checkOverflow);
}, []);
return (
<Box
sx={{
borderBottom: 1,
borderColor: "divider",
pb: "2px",
}}
>
<StyledTabs ref={tabsRef} value={value} onChange={onChange}>
{tabs
.filter((tab) => (isMobile || hideTabs ? tab.value == value : true))
.map((tab) => (
<StyledTab label={tab.label} value={tab.value} icon={tab.icon} />
))}
{(isMobile || hideTabs) && tabs.length > 1 && (
<>
<StyledTab
label={
<Typography
sx={{
display: "flex",
gap: "4px",
}}
variant={"inherit"}
>
{t("application:navbar.showMore")}
<CaretDown sx={{ fontSize: 15 }} />
</Typography>
}
{...bindTrigger(moreOptionState)}
/>
<Menu {...menuProps} onClose={onClose}>
{tabs
.filter((tab) => tab.value != value)
.map((option, index) => (
<MenuItem
dense
key={index}
onClick={(e) => {
onClose();
onChange(e, option.value);
}}
>
{option.icon && <ListItemIcon>{option.icon}</ListItemIcon>}
<ListItemText>{option.label}</ListItemText>
</MenuItem>
))}
</Menu>
</>
)}
</StyledTabs>
</Box>
);
};
export default ResponsiveTabs;