109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
|
import React, { DetailedHTMLProps, HTMLAttributes } from "react";
|
||
|
import { twMerge } from "tailwind-merge";
|
||
|
import Border from "./Border";
|
||
|
import Stack from "../layout/Stack";
|
||
|
import Row from "../layout/Row";
|
||
|
import Span from "../layout/Span";
|
||
|
|
||
|
export type TWUITabsObject = {
|
||
|
title: string;
|
||
|
value: string;
|
||
|
content: React.ReactNode;
|
||
|
defaultActive?: boolean;
|
||
|
};
|
||
|
|
||
|
export type TWUI_TOGGLE_PROPS = React.ComponentProps<typeof Stack> & {
|
||
|
tabsContentArray: TWUITabsObject[];
|
||
|
tabsBorderProps?: React.ComponentProps<typeof Border>;
|
||
|
tabsButtonsWrapperProps?: React.DetailedHTMLProps<
|
||
|
React.HTMLAttributes<HTMLDivElement>,
|
||
|
HTMLDivElement
|
||
|
>;
|
||
|
centered?: boolean;
|
||
|
debounce?: number;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* # Tabs Component
|
||
|
* @className twui-tabs-wrapper
|
||
|
* @className twui-tab-buttons
|
||
|
* @className twui-tab-button-active
|
||
|
* @className twui-tab-buttons-wrapper
|
||
|
*/
|
||
|
export default function Tabs({
|
||
|
tabsContentArray,
|
||
|
tabsBorderProps,
|
||
|
tabsButtonsWrapperProps,
|
||
|
centered,
|
||
|
debounce = 100,
|
||
|
...props
|
||
|
}: TWUI_TOGGLE_PROPS) {
|
||
|
const values = tabsContentArray.map((obj) => obj.value);
|
||
|
|
||
|
const [activeValue, setActiveValue] = React.useState(
|
||
|
tabsContentArray.find((ctn) => ctn.defaultActive)?.value ||
|
||
|
values[0] ||
|
||
|
undefined
|
||
|
);
|
||
|
|
||
|
const targetContent = tabsContentArray.find(
|
||
|
(ctn) => ctn.value == activeValue
|
||
|
);
|
||
|
|
||
|
return (
|
||
|
<Stack
|
||
|
{...props}
|
||
|
className={twMerge("w-full", "twui-tabs-wrapper", props.className)}
|
||
|
>
|
||
|
<div
|
||
|
{...tabsButtonsWrapperProps}
|
||
|
className={twMerge(
|
||
|
"w-full",
|
||
|
"twui-tab-buttons-wrapper",
|
||
|
tabsButtonsWrapperProps?.className
|
||
|
)}
|
||
|
>
|
||
|
<Border className="p-0 w-full" {...tabsBorderProps}>
|
||
|
<Row
|
||
|
className={twMerge(
|
||
|
"gap-0 items-stretch w-full",
|
||
|
centered && "justify-center"
|
||
|
)}
|
||
|
>
|
||
|
{values.map((value, index) => {
|
||
|
const targetObject = tabsContentArray.find(
|
||
|
(ctn) => ctn.value == value
|
||
|
);
|
||
|
|
||
|
const isActive = value == activeValue;
|
||
|
|
||
|
return (
|
||
|
<span
|
||
|
className={twMerge(
|
||
|
"px-6 py-2 rounded -ml-[1px]",
|
||
|
isActive
|
||
|
? "bg-blue-500 text-white outline-none twui-tab-button-active"
|
||
|
: "text-slate-400 dark:text-white/40 hover:text-slate-800 dark:hover:text-white" +
|
||
|
" cursor-pointer",
|
||
|
"twui-tab-buttons"
|
||
|
)}
|
||
|
onClick={() => {
|
||
|
setActiveValue(undefined);
|
||
|
setTimeout(() => {
|
||
|
setActiveValue(value);
|
||
|
}, debounce);
|
||
|
}}
|
||
|
key={index}
|
||
|
>
|
||
|
{targetObject?.title}
|
||
|
</span>
|
||
|
);
|
||
|
})}
|
||
|
</Row>
|
||
|
</Border>
|
||
|
</div>
|
||
|
{targetContent?.content}
|
||
|
</Stack>
|
||
|
);
|
||
|
}
|