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>
);
}

View File

@@ -39,8 +39,8 @@ body {
min-height: 100vh;
max-width: var(--breakpoint-2xl);
margin-top: 82px;
margin-inline: auto;
padding-top: 82px;
padding-inline: 1rem;
text-align: center;

View File

@@ -1,334 +1,66 @@
import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import SecondaryButton from "@/components/button/SecondaryButton";
import SuccessButton from "@/components/button/SuccessButton";
import TertiaryButton from "@/components/button/TertiaryButton";
import WarningButton from "@/components/button/WarningButton";
import TabGroup, { Tab } from "@/components/tab/TabGroup";
export default function TestPage(){
const sizes: ButtonSizeType[] = ["xsm", "sm", "md", "lg", "xl"];
const variants: ButtonVariant[] = ["solid", "icon", "outline", "outline-ghost", "ghost"];
const shapes: ButtonShape[] = ["horizontal", "square", "vertical"];
const tabs: Tab[] = [
{
tabHeader: "Tab 1",
tabContent: <Tab1/>
},
{
tabHeader: "Tab 2",
tabContent: <Tab2/>
},
{
tabHeader: "Tab 3",
tabContent: <Tab3/>
}
];
return (
<main
className="flex flex-col items-center justify-center gap-4"
className="flex flex-col items-center justify-center gap-4 mt-8"
>
{/* Primary Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<PrimaryButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Primary {size}
</PrimaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<PrimaryButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Primary {variant}
</PrimaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<PrimaryButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Primary {shape}
</PrimaryButton>
))
}
</div>
{/* Secondary Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<SecondaryButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Secondary {size}
</SecondaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<SecondaryButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Secondary {variant}
</SecondaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<SecondaryButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Secondary {shape}
</SecondaryButton>
))
}
</div>
{/* Tertiary Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<TertiaryButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Tertiary {size}
</TertiaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<TertiaryButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Tertiary {variant}
</TertiaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<TertiaryButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Tertiary {shape}
</TertiaryButton>
))
}
</div>
{/* Success Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<SuccessButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Success {size}
</SuccessButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<SuccessButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Success {variant}
</SuccessButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<SuccessButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Success {shape}
</SuccessButton>
))
}
</div>
{/* Warning Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<WarningButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Warning {size}
</WarningButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<WarningButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Warning {variant}
</WarningButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<WarningButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Warning {shape}
</WarningButton>
))
}
</div>
{/* Danger Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<DangerButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Danger {size}
</DangerButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<DangerButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Danger {variant}
</DangerButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<DangerButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Danger {shape}
</DangerButton>
))
}
</div>
<TabGroup
tabs={tabs}
/>
</main>
);
}
function Tab1(){
console.log("Tab 1");
return (
<div>
Tab 1 Content
</div>
);
}
function Tab2(){
console.log("Tab 2");
return (
<div>
Tab 2 Content
</div>
);
}
function Tab3(){
console.log("Tab 3");
return (
<div>
Tab 3 Content
</div>
);
}