[WEB-3128] improvement: made tab list items modular for independent use and added few icons (#6387)

* chore: added bar and tree map icons

* improvement: made tab list items modular for independent use
This commit is contained in:
Prateek Shourya 2025-01-13 19:21:01 +05:30 committed by GitHub
parent c779fc095c
commit 7183cffdb7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 127 additions and 45 deletions

View file

@ -0,0 +1,20 @@
import * as React from "react";
import { ISvgIcons } from "./type";
export const BarIcon: React.FC<ISvgIcons> = ({ className = "", ...rest }) => (
<svg className={className} viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" {...rest}>
<path
d="M0 12.5859C0 11.4814 0.89543 10.5859 2 10.5859H3.64706C4.75163 10.5859 5.64706 11.4814 5.64706 12.5859V23.9977H0V12.5859Z"
fill="currentColor"
/>
<path
d="M9.17773 2C9.17773 0.89543 10.0732 0 11.1777 0H12.8248C13.9294 0 14.8248 0.895431 14.8248 2V24H9.17773V2Z"
fill="currentColor"
/>
<path
d="M18.3535 8.35156C18.3535 7.247 19.2489 6.35156 20.3535 6.35156H22.0006C23.1051 6.35156 24.0006 7.24699 24.0006 8.35156V23.9986H18.3535V8.35156Z"
fill="currentColor"
/>
</svg>
);

View file

@ -49,3 +49,5 @@ export * from "./off-track-icon";
export * from "./at-risk-icon";
export * from "./multiple-sticky";
export * from "./sticky-note-icon";
export * from "./bar-icon";
export * from "./tree-map-icon";

View file

@ -0,0 +1,16 @@
import * as React from "react";
import { ISvgIcons } from "./type";
export const TreeMapIcon: React.FC<ISvgIcons> = ({ className = "", ...rest }) => (
<svg className={className} viewBox="0 0 27 22" fill="currentColor" xmlns="http://www.w3.org/2000/svg" {...rest}>
<rect width="10" height="10" rx="1" fill="currentColor" />
<rect x="11.5" width="10" height="6" rx="1" fill="currentColor" />
<rect y="12" width="10" height="10" rx="1" fill="currentColor" />
<rect x="11.5" y="16" width="10" height="6" rx="1" fill="currentColor" />
<rect x="11.5" y="8" width="10" height="6" rx="1" fill="currentColor" />
<rect x="23" width="4" height="11" rx="1" fill="currentColor" />
<rect x="23" y="13" width="4" height="4" rx="1" fill="currentColor" />
<rect x="23" y="19" width="4" height="3" rx="1" fill="currentColor" />
</svg>
);

View file

@ -1 +1,2 @@
export * from "./tabs";
export * from "./tab-list";

View file

@ -0,0 +1,71 @@
import { Tab } from "@headlessui/react";
import { LucideProps } from "lucide-react";
import React, { FC } from "react";
// helpers
import { cn } from "../../helpers";
export type TabListItem = {
key: string;
icon?: FC<LucideProps>;
label?: React.ReactNode;
disabled?: boolean;
onClick?: () => void;
};
type TTabListProps = {
tabs: TabListItem[];
tabListClassName?: string;
tabClassName?: string;
size?: "sm" | "md" | "lg";
selectedTab?: string;
onTabChange?: (key: string) => void;
};
export const TabList: FC<TTabListProps> = ({
tabs,
tabListClassName,
tabClassName,
size = "md",
selectedTab,
onTabChange,
}) => (
<Tab.List
as="div"
className={cn(
"flex w-full min-w-fit items-center justify-between gap-1.5 rounded-md text-sm p-0.5 bg-custom-background-80/60",
tabListClassName
)}
>
{tabs.map((tab) => (
<Tab
className={({ selected }) =>
cn(
"flex items-center justify-center p-1 min-w-fit w-full font-medium text-custom-text-100 outline-none focus:outline-none cursor-pointer transition-all rounded",
(selectedTab ? selectedTab === tab.key : selected)
? "bg-custom-background-100 text-custom-text-100 shadow-sm"
: tab.disabled
? "text-custom-text-400 cursor-not-allowed"
: "text-custom-text-400 hover:text-custom-text-300 hover:bg-custom-background-80/60",
{
"text-xs": size === "sm",
"text-sm": size === "md",
"text-base": size === "lg",
},
tabClassName
)
}
key={tab.key}
onClick={() => {
if (!tab.disabled) {
onTabChange?.(tab.key);
tab.onClick?.();
}
}}
disabled={tab.disabled}
>
{tab.icon && <tab.icon className="size-4" />}
{tab.label}
</Tab>
))}
</Tab.List>
);

View file

@ -1,19 +1,17 @@
import React, { FC, Fragment, useEffect, useState } from "react";
import { Tab } from "@headlessui/react";
import { LucideProps } from "lucide-react";
import React, { FC, Fragment, useEffect, useState } from "react";
// helpers
import { useLocalStorage } from "@plane/hooks";
import { cn } from "../../helpers";
// types
import { TabList, TabListItem } from "./tab-list";
type TabItem = {
key: string;
icon?: FC<LucideProps>;
label?: React.ReactNode;
export type TabContent = {
content: React.ReactNode;
disabled?: boolean;
onClick?: () => void;
};
export type TabItem = TabListItem & TabContent;
type TTabsProps = {
tabs: TabItem[];
storageKey?: string;
@ -58,48 +56,22 @@ export const Tabs: FC<TTabsProps> = (props: TTabsProps) => {
const currentTabIndex = (tabKey: string): number => tabs.findIndex((tab) => tab.key === tabKey);
const handleTabChange = (key: string) => {
setSelectedTab(key);
};
return (
<div className="flex flex-col w-full h-full">
<Tab.Group defaultIndex={currentTabIndex(selectedTab)}>
<div className={cn("flex flex-col w-full h-full gap-2", containerClassName)}>
<div className={cn("flex w-full items-center gap-4", tabListContainerClassName)}>
<Tab.List
as="div"
className={cn(
"flex w-full min-w-fit items-center justify-between gap-1.5 rounded-md text-sm p-0.5 bg-custom-background-80/60",
tabListClassName
)}
>
{tabs.map((tab) => (
<Tab
className={({ selected }) =>
cn(
`flex items-center justify-center p-1 min-w-fit w-full font-medium text-custom-text-100 outline-none focus:outline-none cursor-pointer transition-all rounded`,
selected
? "bg-custom-background-100 text-custom-text-100 shadow-sm"
: tab.disabled
? "text-custom-text-400 cursor-not-allowed"
: "text-custom-text-400 hover:text-custom-text-300 hover:bg-custom-background-80/60",
{
"text-xs": size === "sm",
"text-sm": size === "md",
"text-base": size === "lg",
},
tabClassName
)
}
key={tab.key}
onClick={() => {
if (!tab.disabled) setSelectedTab(tab.key);
tab.onClick?.();
}}
disabled={tab.disabled}
>
{tab.icon && <tab.icon className="size-4" />}
{tab.label}
</Tab>
))}
</Tab.List>
<TabList
tabs={tabs}
tabListClassName={tabListClassName}
tabClassName={tabClassName}
size={size}
onTabChange={handleTabChange}
/>
{actions && <div className="flex-grow">{actions}</div>}
</div>
<Tab.Panels as={Fragment}>