import * as React from "react";
import { ReactNode } from "react";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import { cn } from "../libs/utils";
import { SelectBox } from "./selectBox";
export interface Tab {
name: string;
current?: boolean;
href?: string;
label: ReactNode;
content: ReactNode;
disabled?: boolean;
is_allowed?: boolean;
}
const TabsContext = React.createContext<{
size?: number;
tabs?: Tab[];
current?: string;
setTab?: (name: string) => void;
responsive?: boolean;
variant?: "tabs" | "pills";
updateHash?: boolean;
}>({
size: undefined,
tabs: undefined,
current: undefined,
setTab: undefined,
responsive: false,
variant: "tabs",
updateHash: true
});
interface TabsProps {
current?: string | (() => string);
tabs: Tab[];
defaultValue?: string;
className?: string;
fullWidth?: boolean;
fullHeight?: boolean;
children?: React.ReactNode;
onTabChange?: (tabName: string) => void;
responsive?: boolean;
variant?: "tabs" | "pills";
updateHash?: boolean;
}
const Tabs = ({
tabs,
defaultValue,
current,
className,
fullWidth,
fullHeight,
children,
onTabChange,
responsive = false,
variant = "tabs",
updateHash = true
}: TabsProps) => {
// Filter tabs based on is_allowed (undefined or true means visible)
const visibleTabs = React.useMemo(() =>
tabs.filter(tab => tab.is_allowed === undefined || tab.is_allowed === true),
[tabs]
);
// Initialize value
const [value, setValue] = React.useState(() => {
// First check if current is provided
const currentValue = typeof current === 'function' ? current() : current;
if (currentValue) {
return currentValue;
}
// Then check hash
const hash = window.location.hash;
const currentTab = hash ? hash.substring(1) : undefined;
// Check if the tab from hash exists in visible tabs
if (currentTab && visibleTabs.some(tab => tab.name === currentTab)) {
return currentTab;
}
// Fall back to default or first visible tab
return defaultValue || visibleTabs[0]?.name;
});
// Update when current prop changes (but don't create a loop)
React.useEffect(() => {
const currentValue = typeof current === 'function' ? current() : current;
if (currentValue && currentValue !== value) {
setValue(currentValue);
}
}, [current]);
// Listen to hash changes only when there's no current prop being controlled externally
React.useEffect(() => {
if (current) return; // Skip hash handling if controlled by parent
const handleHashChange = () => {
const hash = window.location.hash;
const currentTab = hash ? hash.substring(1) : undefined;
// Only update if the tab exists in visible tabs
if (currentTab && visibleTabs.some(tab => tab.name === currentTab)) {
setValue(currentTab);
} else if (!hash && defaultValue) {
// If no hash, fall back to default
setValue(defaultValue);
}
};
// Check initial hash
handleHashChange();
window.addEventListener('hashchange', handleHashChange);
return () => window.removeEventListener('hashchange', handleHashChange);
}, [current, visibleTabs, defaultValue]);
const handleValueChange = (newValue: string) => {
setValue(newValue);
// Update the URL hash when tab changes (only if updateHash is true and not controlled by parent)
if (updateHash && !current) {
// Preserve existing history state when changing hash
const currentState = window.history.state;
const newUrl = window.location.pathname + window.location.search + '#' + newValue;
window.history.pushState(currentState, '', newUrl);
}
if (onTabChange) {
onTabChange(newValue);
}
};
const setTab = React.useCallback((tabName: string) => {
handleValueChange(tabName);
}, [handleValueChange]);
return (