diff --git a/lib/src/components/Tab/Tab.tsx b/lib/src/components/Tab/Tab.tsx new file mode 100644 index 0000000..1be0479 --- /dev/null +++ b/lib/src/components/Tab/Tab.tsx @@ -0,0 +1,103 @@ +import { assignInlineVars } from "@vanilla-extract/dynamic"; +import { + forwardRef, + ForwardRefRenderFunction, + MouseEvent, + useEffect, + useState, +} from "react"; +import { Badge } from "../Badge"; +import { Flex } from "../Flex"; +import { + badgeStyles, + contentStyles, + iconStyles, + tabHeaderContainerStyles, + tabRecipe, + tabVars, +} from "./tab.css"; +import { TabsObjectProps, TabProps } from "./tab.types"; + +const TabComponent: ForwardRefRenderFunction = ( + { + defaultValue, + value, + color = "#2F80ED", + background = "#d7e9ff", + onChange = () => {}, + grow = false, + height = "40px", + tabData, + style, + className, + children, + ...nativeProps + }, + ref +) => { + const [selectedTab, setSelectedTab] = useState(null); + + useEffect(() => { + const activeValue = value || defaultValue; + const tab = tabData.find((tabItem) => tabItem.value === activeValue); + tab && setSelectedTab(tab); + }, [defaultValue, tabData, value]); + + const internalOnClickHandler = ( + event: MouseEvent, + tabItem: TabsObjectProps + ) => { + onChange(tabItem, event); + !value && setSelectedTab(tabItem); + }; + + return ( +
+ + {tabData.map((tabItem, ind) => { + const tabClass = tabRecipe({ + active: tabItem.value === selectedTab?.value, + disabled: tabItem.disabled, + }); + return ( + internalOnClickHandler(event, tabItem)} + key={ind} + > + {tabItem.icon && ( + {tabItem.icon} + )} + {tabItem.label && {tabItem.label}} + {tabItem.badge && ( + + )} + + ); + })} + + {selectedTab && ( +
{children(selectedTab)}
+ )} +
+ ); +}; + +export const Tab = forwardRef(TabComponent); diff --git a/lib/src/components/Tab/index.tsx b/lib/src/components/Tab/index.tsx new file mode 100644 index 0000000..3621813 --- /dev/null +++ b/lib/src/components/Tab/index.tsx @@ -0,0 +1,2 @@ +export * from "./Tab"; +export * from "./tab.css"; diff --git a/lib/src/components/Tab/tab.css.ts b/lib/src/components/Tab/tab.css.ts new file mode 100644 index 0000000..0dd1b23 --- /dev/null +++ b/lib/src/components/Tab/tab.css.ts @@ -0,0 +1,66 @@ +import { createTheme, style } from "@vanilla-extract/css"; +import { recipe } from "@vanilla-extract/recipes"; +import { TabTheme } from "./tab.types"; + +export const [tabTheme, tabVars]: TabTheme = createTheme({ + color: "#2F80ED", + background: "#BDDBFF60", + height: "50px", +}); + +export const tabHeaderContainerStyles = style({ + height: tabVars.height, + position: "relative", + borderBottom: "2px solid #EBECF0", +}); + +export const tabRecipe = recipe({ + base: { + cursor: "pointer", + height: "100%", + position: "relative", + zIndex: 1, + padding: "8px 10px", + borderRadius: "4px 4px 0px 0px", + ":hover": { + background: tabVars.background, + color: tabVars.color, + }, + }, + variants: { + active: { + true: { + color: tabVars.color, + ":after": { + content: "", + background: tabVars.color, + position: "absolute", + bottom: "-2px", + left: 0, + borderRadius: "20px", + height: "2px", + width: "100%", + zIndex: 0, + }, + }, + }, + disabled: { + true: { + color: "#42526E", + pointerEvents: "none", + }, + }, + }, +}); + +export const contentStyles = style({ + padding: "8px 0", +}); + +export const iconStyles = style({ + marginRight: "6px", +}); + +export const badgeStyles = style({ + marginLeft: "6px", +}); diff --git a/lib/src/components/Tab/tab.types.ts b/lib/src/components/Tab/tab.types.ts new file mode 100644 index 0000000..5a44b69 --- /dev/null +++ b/lib/src/components/Tab/tab.types.ts @@ -0,0 +1,31 @@ +export type TabProps = JSX.IntrinsicElements["div"] & { + children: (selectedTab: TabsObjectProps) => JSX.Element; + color?: string; + background?: string; + defaultValue?: string | number; + value?: string | number; + height?: string; + onChange?: ( + value: TabsObjectProps, + event: React.MouseEvent + ) => void; + tabData: TabsObjectProps[]; + grow?: boolean; +}; + +export type TabsObjectProps = { + label: string; + value: string | number; + icon?: JSX.Element; + disabled?: boolean; + badge?: string | JSX.Element; +}; + +export type TabTheme = [ + string, + { + color: string; + background: string; + height: string; + } +]; diff --git a/lib/src/index.ts b/lib/src/index.ts index 0315a7b..8dfd88f 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -8,3 +8,4 @@ export * from "./components/reset"; export * from "./components/Switch"; export * from "./components/TextArea"; export * from "./components/Divider"; +export * from "./components/Tab";