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;
|
min-height: 100vh;
|
||||||
max-width: var(--breakpoint-2xl);
|
max-width: var(--breakpoint-2xl);
|
||||||
|
|
||||||
margin-top: 82px;
|
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
|
padding-top: 82px;
|
||||||
padding-inline: 1rem;
|
padding-inline: 1rem;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -1,334 +1,66 @@
|
|||||||
import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
|
import TabGroup, { Tab } from "@/components/tab/TabGroup";
|
||||||
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";
|
|
||||||
|
|
||||||
|
|
||||||
export default function TestPage(){
|
export default function TestPage(){
|
||||||
const sizes: ButtonSizeType[] = ["xsm", "sm", "md", "lg", "xl"];
|
const tabs: Tab[] = [
|
||||||
const variants: ButtonVariant[] = ["solid", "icon", "outline", "outline-ghost", "ghost"];
|
{
|
||||||
const shapes: ButtonShape[] = ["horizontal", "square", "vertical"];
|
tabHeader: "Tab 1",
|
||||||
|
tabContent: <Tab1/>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tabHeader: "Tab 2",
|
||||||
|
tabContent: <Tab2/>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tabHeader: "Tab 3",
|
||||||
|
tabContent: <Tab3/>
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main
|
<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 */}
|
<TabGroup
|
||||||
<div
|
tabs={tabs}
|
||||||
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>
|
|
||||||
</main>
|
</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