Add tab components

This commit is contained in:
2025-02-26 23:19:35 -05:00
parent 1499f4055f
commit d68e8864a0
3 changed files with 174 additions and 324 deletions

View File

@@ -0,0 +1,118 @@
import clsx from "clsx";
import { HTMLProps, useState } from "react";
export interface Tab {
tabHeader: React.ReactNode;
headerClasses?: string;
tabContent: React.ReactNode;
contentClasses?: string;
active?: boolean;
onTabClick?: () => void;
}
export interface TabGroupProps extends HTMLProps<HTMLDivElement>{
tabs: Tab[];
}
export default function TabGroup(props: TabGroupProps){
const { tabs, className } = props;
const [ activeTab, setActiveTab ] = useState<number>(tabs.map((tab, index) => tab.active ? index : undefined)[0] ?? 0);
//TODO: Possible to maintain state of past tabs if we "cache" them in a useState<JSX.Element>() on their first render
return (
<div
{...props}
className={clsx(
className,
"flex flex-col w-full"
)}
>
<div
className="flex flex-row items-center justify-start"
>
{
tabs.map((tab, index) => (
<TabHeader
key={index}
tab={tab}
active={activeTab === index}
onClick={() => { setActiveTab(index); tab.onTabClick?.(); }}
/>
))
}
<div
className="w-full h-full py-2 border-b border-(--text-color)"
>
&nbsp;
</div>
</div>
<div
className="flex flex-col items-center justify-center"
>
{
tabs.map((tab, index) => (
<TabContent
key={index}
tab={tab}
active={activeTab === index}
/>
))
}
</div>
</div>
);
}
function TabHeader({
tab,
onClick,
active
}:{
tab: Tab;
onClick: () => void;
active: boolean;
}){
return (
<div
className={clsx(
tab.headerClasses,
"px-4 py-2 rounded-t-lg cursor-pointer whitespace-nowrap",
"border-x border-t border-(--text-color)",
{
"border-b border-(--text-color)": !active
}
)}
onClick={onClick}
>
{tab.tabHeader}
</div>
);
}
function TabContent({
tab,
active
}:{
tab: Tab;
active: boolean;
}){
if(!active){
return null;
}
return (
<div
className={clsx(
tab.headerClasses,
""
)}
>
{tab.tabContent}
</div>
);
}