Add tab components
This commit is contained in:
118
src/components/tab/TabGroup.tsx
Normal file
118
src/components/tab/TabGroup.tsx
Normal 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)"
|
||||
>
|
||||
|
||||
</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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user