Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/ui/src/icons/bar-icon.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
2 changes: 2 additions & 0 deletions packages/ui/src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
16 changes: 16 additions & 0 deletions packages/ui/src/icons/tree-map-icon.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
1 change: 1 addition & 0 deletions packages/ui/src/tabs/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./tabs";
export * from "./tab-list";
71 changes: 71 additions & 0 deletions packages/ui/src/tabs/tab-list.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
62 changes: 17 additions & 45 deletions packages/ui/src/tabs/tabs.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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}>
Expand Down
Loading