diff --git a/apps/web/core/components/analytics/work-items/modal/content.tsx b/apps/web/core/components/analytics/work-items/modal/content.tsx index 082b9266d4..dfaa2f8412 100644 --- a/apps/web/core/components/analytics/work-items/modal/content.tsx +++ b/apps/web/core/components/analytics/work-items/modal/content.tsx @@ -69,13 +69,11 @@ export const WorkItemsModalMainContent = observer(function WorkItemsModalMainCon ); return ( - -
- - - - -
-
+
+ + + + +
); }); diff --git a/apps/web/core/components/core/image-picker-popover.tsx b/apps/web/core/components/core/image-picker-popover.tsx index f1f7bdcc64..44068048ff 100644 --- a/apps/web/core/components/core/image-picker-popover.tsx +++ b/apps/web/core/components/core/image-picker-popover.tsx @@ -5,11 +5,12 @@ import { useDropzone } from "react-dropzone"; import type { Control } from "react-hook-form"; import { Controller } from "react-hook-form"; import useSWR from "swr"; -import { Tab, Popover } from "@headlessui/react"; +import { Popover } from "@headlessui/react"; // plane imports import { ACCEPTED_COVER_IMAGE_MIME_TYPES_FOR_REACT_DROPZONE, MAX_FILE_SIZE } from "@plane/constants"; import { useOutsideClickDetector } from "@plane/hooks"; import { Button } from "@plane/propel/button"; +import { Tabs } from "@plane/propel/tabs"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import { EFileAssetType } from "@plane/types"; import { Input, Loader } from "@plane/ui"; @@ -197,91 +198,88 @@ export const ImagePickerPopover = observer(function ImagePickerPopover(props: Pr ref={imagePickerRef} className="flex h-96 w-80 flex-col overflow-auto rounded border border-custom-border-300 bg-custom-background-100 p-3 shadow-2xl md:h-[28rem] md:w-[36rem]" > - - + + {tabOptions.map((tab) => ( - - `rounded px-4 py-1 text-center text-sm outline-none transition-colors ${ - selected ? "bg-custom-primary text-white" : "text-custom-text-100" - }` - } + value={tab.key} + className="rounded px-4 py-1 text-center text-sm outline-none transition-colors data-[selected]:bg-custom-primary data-[selected]:text-white text-custom-text-100" > {tab.title} - + ))} - - - - {(unsplashImages || !unsplashError) && ( - <> -
- ( - { - if (e.key === "Enter") { - e.preventDefault(); - setSearchParams(formData.search); - } + + +
+ {(unsplashImages || !unsplashError) && ( + +
+ ( + { + if (e.key === "Enter") { + e.preventDefault(); + setSearchParams(formData.search); + } + }} + value={value} + onChange={(e) => setFormData({ ...formData, search: e.target.value })} + ref={ref} + placeholder="Search for images" + className="w-full text-sm" + /> + )} + /> + +
+ {unsplashImages ? ( + unsplashImages.length > 0 ? ( +
+ {unsplashImages.map((image) => ( +
{ + setIsOpen(false); + onChange(image.urls.regular); }} - value={value} - onChange={(e) => setFormData({ ...formData, search: e.target.value })} - ref={ref} - placeholder="Search for images" - className="w-full text-sm" - /> - )} - /> - -
- {unsplashImages ? ( - unsplashImages.length > 0 ? ( -
- {unsplashImages.map((image) => ( -
{ - setIsOpen(false); - onChange(image.urls.regular); - }} - > - {image.alt_description} -
- ))} -
- ) : ( -

No images found.

- ) + > + {image.alt_description} +
+ ))} +
) : ( - - - - - - - - - - - )} - - )} - - +

No images found.

+ ) + ) : ( + + + + + + + + + + + )} + + )} + +
{Object.values(STATIC_COVER_IMAGES).map((imageUrl, index) => (
))}
- - + + +
- - - + +
+
)} diff --git a/apps/web/core/components/cycles/analytics-sidebar/progress-stats.tsx b/apps/web/core/components/cycles/analytics-sidebar/progress-stats.tsx index 722d30e2c6..46c719919e 100644 --- a/apps/web/core/components/cycles/analytics-sidebar/progress-stats.tsx +++ b/apps/web/core/components/cycles/analytics-sidebar/progress-stats.tsx @@ -1,8 +1,8 @@ import type { FC } from "react"; import { observer } from "mobx-react"; -import { Tab } from "@headlessui/react"; // plane imports import { useTranslation } from "@plane/i18n"; +import { Tabs } from "@plane/propel/tabs"; import type { TWorkItemFilterCondition } from "@plane/shared-state"; import type { TCycleDistribution, TCycleEstimateDistribution, TCyclePlotType } from "@plane/types"; import { cn, toFilterArray } from "@plane/utils"; @@ -54,8 +54,6 @@ export const CycleProgressStats = observer(function CycleProgressStats(props: TC `cycle-analytics-tab-${cycleId}`, "stat-assignees" ); - // derived values - const currentTabIndex = (tab: string): number => PROGRESS_STATS.findIndex((stat) => stat.key === tab); const currentDistribution = distribution as TCycleDistribution; const currentEstimateDistribution = distribution as TCycleEstimateDistribution; const selectedAssigneeIds = toFilterArray(selectedFilters?.assignees?.value || []) as string[]; @@ -116,9 +114,8 @@ export const CycleProgressStats = observer(function CycleProgressStats(props: TC return (
- - setCycleTab(value)}> + {PROGRESS_STATS.map((stat) => ( - setCycleTab(stat.key)} > {t(stat.i18n_title)} - + ))} - - - + +
+ - - + + - - + + - - - + +
+
); }); diff --git a/apps/web/core/components/license/modal/card/base-paid-plan-card.tsx b/apps/web/core/components/license/modal/card/base-paid-plan-card.tsx index 94b6cbe3ec..9c3557a619 100644 --- a/apps/web/core/components/license/modal/card/base-paid-plan-card.tsx +++ b/apps/web/core/components/license/modal/card/base-paid-plan-card.tsx @@ -2,8 +2,8 @@ import type { FC } from "react"; import { useState } from "react"; import { observer } from "mobx-react"; import { CheckCircle } from "lucide-react"; -import { Tab } from "@headlessui/react"; // plane imports +import { Tabs } from "@plane/propel/tabs"; // helpers import type { EProductSubscriptionEnum, TBillingFrequency, TSubscriptionPrice } from "@plane/types"; import { getSubscriptionBackgroundColor, getUpgradeCardVariantStyle } from "@plane/ui"; @@ -39,32 +39,27 @@ export const BasePaidPlanCard = observer(function BasePaidPlanCard(props: TBaseP return (
- -
- + setSelectedPlan(value as TBillingFrequency)}> +
+ {prices.map((price: TSubscriptionPrice) => ( - - cn( - "w-full rounded py-1 text-sm font-medium leading-5", - selected - ? "bg-custom-background-100 text-custom-text-100 shadow" - : "text-custom-text-300 hover:text-custom-text-200" - ) - } - onClick={() => setSelectedPlan(price.recurring)} + value={price.recurring} + className={cn( + "w-full rounded text-sm font-medium leading-5 py-2", + "data-[selected]:bg-custom-background-100 data-[selected]:text-custom-text-100 data-[selected]:shadow", + "text-custom-text-300 hover:text-custom-text-200" + )} > {renderPriceContent(price)} - + ))} - +
- +
{prices.map((price: TSubscriptionPrice) => ( - +
Plane {planeName}
{renderActionButton(price)} @@ -88,10 +83,10 @@ export const BasePaidPlanCard = observer(function BasePaidPlanCard(props: TBaseP {extraFeatures &&
{extraFeatures}
}
-
+ ))} - - +
+
); }); diff --git a/apps/web/core/components/license/modal/card/plan-upgrade.tsx b/apps/web/core/components/license/modal/card/plan-upgrade.tsx index ffe880d281..302dcfb5ed 100644 --- a/apps/web/core/components/license/modal/card/plan-upgrade.tsx +++ b/apps/web/core/components/license/modal/card/plan-upgrade.tsx @@ -73,7 +73,12 @@ export const PlanUpgradeCard = observer(function PlanUpgradeCard(props: PlanUpgr <> Yearly {yearlyDiscount > 0 && ( - + -{yearlyDiscount}% )} diff --git a/apps/web/core/components/modules/analytics-sidebar/issue-progress.tsx b/apps/web/core/components/modules/analytics-sidebar/issue-progress.tsx index 344b266455..ca1a2bc048 100644 --- a/apps/web/core/components/modules/analytics-sidebar/issue-progress.tsx +++ b/apps/web/core/components/modules/analytics-sidebar/issue-progress.tsx @@ -114,7 +114,7 @@ export const ModuleAnalyticsProgress = observer(function ModuleAnalyticsProgress if (!moduleDetails) return <>; return ( -
+
{({ open }) => (
diff --git a/apps/web/core/components/modules/analytics-sidebar/progress-stats.tsx b/apps/web/core/components/modules/analytics-sidebar/progress-stats.tsx index e6ed576393..6008480807 100644 --- a/apps/web/core/components/modules/analytics-sidebar/progress-stats.tsx +++ b/apps/web/core/components/modules/analytics-sidebar/progress-stats.tsx @@ -1,7 +1,7 @@ import type { FC } from "react"; import { observer } from "mobx-react"; -import { Tab } from "@headlessui/react"; import { useTranslation } from "@plane/i18n"; +import { Tabs } from "@plane/propel/tabs"; import type { TWorkItemFilterCondition } from "@plane/shared-state"; import type { TModuleDistribution, TModuleEstimateDistribution, TModulePlotType } from "@plane/types"; import { cn, toFilterArray } from "@plane/utils"; @@ -52,8 +52,6 @@ export const ModuleProgressStats = observer(function ModuleProgressStats(props: `module-analytics-tab-${moduleId}`, "stat-assignees" ); - // derived values - const currentTabIndex = (tab: string): number => PROGRESS_STATS.findIndex((stat) => stat.key === tab); const currentDistribution = distribution as TModuleDistribution; const currentEstimateDistribution = distribution as TModuleEstimateDistribution; const selectedAssigneeIds = toFilterArray(selectedFilters?.assignees?.value || []) as string[]; @@ -114,9 +112,8 @@ export const ModuleProgressStats = observer(function ModuleProgressStats(props: return (
- - setModuleTab(value)}> + {PROGRESS_STATS.map((stat) => ( - setModuleTab(stat.key)} > {t(stat.i18n_title)} - + ))} - - - + +
+ - - + + - - + + - - - + +
+
); }); diff --git a/apps/web/core/components/pages/navigation-pane/root.tsx b/apps/web/core/components/pages/navigation-pane/root.tsx index 47f9abdeed..6a16221c59 100644 --- a/apps/web/core/components/pages/navigation-pane/root.tsx +++ b/apps/web/core/components/pages/navigation-pane/root.tsx @@ -2,9 +2,9 @@ import React, { useCallback } from "react"; import { observer } from "mobx-react"; import { useRouter, useSearchParams } from "next/navigation"; import { ArrowRightCircle } from "lucide-react"; -import { Tab } from "@headlessui/react"; // plane imports import { useTranslation } from "@plane/i18n"; +import { Tabs } from "@plane/propel/tabs"; import { Tooltip } from "@plane/propel/tooltip"; // hooks import { useQueryParams } from "@/hooks/use-query-params"; @@ -20,7 +20,6 @@ import { PageNavigationPaneTabsList } from "./tabs-list"; import type { INavigationPaneExtension } from "./types/extensions"; import { - PAGE_NAVIGATION_PANE_TAB_KEYS, PAGE_NAVIGATION_PANE_TABS_QUERY_PARAM, PAGE_NAVIGATION_PANE_VERSION_QUERY_PARAM, PAGE_NAVIGATION_PANE_WIDTH, @@ -49,7 +48,6 @@ export const PageNavigationPaneRoot = observer(function PageNavigationPaneRoot(p PAGE_NAVIGATION_PANE_TABS_QUERY_PARAM ) as TPageNavigationPaneTab | null; const activeTab: TPageNavigationPaneTab = navigationPaneQueryParam || "outline"; - const selectedIndex = PAGE_NAVIGATION_PANE_TAB_KEYS.indexOf(activeTab); // Check if any extension is currently active based on query parameters const ActiveExtension = extensions.find(function ActiveExtension(extension) { @@ -69,8 +67,8 @@ export const PageNavigationPaneRoot = observer(function PageNavigationPaneRoot(p const { t } = useTranslation(); const handleTabChange = useCallback( - (index: number) => { - const updatedTab = PAGE_NAVIGATION_PANE_TAB_KEYS[index]; + (value: string) => { + const updatedTab = value as TPageNavigationPaneTab; const isUpdatedTabInfo = updatedTab === "info"; const updatedRoute = updateQueryParams({ paramsToAdd: { [PAGE_NAVIGATION_PANE_TABS_QUERY_PARAM]: updatedTab }, @@ -106,10 +104,10 @@ export const PageNavigationPaneRoot = observer(function PageNavigationPaneRoot(p {ActiveExtension ? ( ) : showNavigationTabs ? ( - + - + ) : null}
diff --git a/apps/web/core/components/pages/navigation-pane/tab-panels/root.tsx b/apps/web/core/components/pages/navigation-pane/tab-panels/root.tsx index aba1a04444..fba03c63e2 100644 --- a/apps/web/core/components/pages/navigation-pane/tab-panels/root.tsx +++ b/apps/web/core/components/pages/navigation-pane/tab-panels/root.tsx @@ -1,5 +1,6 @@ import React from "react"; -import { Tab } from "@headlessui/react"; +// plane imports +import { Tabs } from "@plane/propel/tabs"; // components import type { TPageRootHandlers } from "@/components/pages/editor/page-root"; // plane web imports @@ -21,19 +22,19 @@ export function PageNavigationPaneTabPanelsRoot(props: Props) { const { page, versionHistory } = props; return ( - + <> {ORDERED_PAGE_NAVIGATION_TABS_LIST.map((tab) => ( - {tab.key === "outline" && } {tab.key === "info" && } {tab.key === "assets" && } - + ))} - + ); } diff --git a/apps/web/core/components/pages/navigation-pane/tabs-list.tsx b/apps/web/core/components/pages/navigation-pane/tabs-list.tsx index bbe9b65782..8d6b8528c9 100644 --- a/apps/web/core/components/pages/navigation-pane/tabs-list.tsx +++ b/apps/web/core/components/pages/navigation-pane/tabs-list.tsx @@ -1,6 +1,6 @@ -import { Tab } from "@headlessui/react"; // plane imports import { useTranslation } from "@plane/i18n"; +import { Tabs } from "@plane/propel/tabs"; // plane web components import { ORDERED_PAGE_NAVIGATION_TABS_LIST } from "@/plane-web/components/pages/navigation-pane"; @@ -9,29 +9,19 @@ export function PageNavigationPaneTabsList() { const { t } = useTranslation(); return ( - - {({ selectedIndex }) => ( - <> - {ORDERED_PAGE_NAVIGATION_TABS_LIST.map((tab) => ( - - {t(tab.i18n_label)} - - ))} - {/* active tab indicator */} -
- - )} - +
+ + {ORDERED_PAGE_NAVIGATION_TABS_LIST.map((tab) => ( + + {t(tab.i18n_label)} + + ))} + + +
); } diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 36aeaa9767..56c3f343e3 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -25,7 +25,6 @@ export * from "./scroll-area"; export * from "./sortable"; export * from "./spinners"; export * from "./tables"; -export * from "./tabs"; export * from "./tag"; export * from "./tooltip"; export * from "./typography"; diff --git a/packages/ui/src/tabs/index.ts b/packages/ui/src/tabs/index.ts deleted file mode 100644 index c232b43ab4..0000000000 --- a/packages/ui/src/tabs/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./tabs"; -export * from "./tab-list"; diff --git a/packages/ui/src/tabs/tab-list.tsx b/packages/ui/src/tabs/tab-list.tsx deleted file mode 100644 index 560d592f38..0000000000 --- a/packages/ui/src/tabs/tab-list.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { Tab } from "@headlessui/react"; -import type { LucideProps } from "lucide-react"; -import type { FC } from "react"; -import React from "react"; -// helpers -import { cn } from "../utils"; - -export type TabListItem = { - key: string; - icon?: FC; - 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 function TabList({ - tabs, - tabListClassName, - tabClassName, - size = "md", - selectedTab, - onTabChange, -}: TTabListProps) { - return ( - - {tabs.map((tab) => ( - - 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.label} - - ))} - - ); -} diff --git a/packages/ui/src/tabs/tabs.tsx b/packages/ui/src/tabs/tabs.tsx deleted file mode 100644 index 3aa7167b9b..0000000000 --- a/packages/ui/src/tabs/tabs.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { Tab } from "@headlessui/react"; -import type { FC } from "react"; -import React, { Fragment, useEffect, useState } from "react"; -// helpers -import { useLocalStorage } from "@plane/hooks"; -import { cn } from "../utils"; -// types -import type { TabListItem } from "./tab-list"; -import { TabList } from "./tab-list"; - -export type TabContent = { - content: React.ReactNode; -}; - -export type TabItem = TabListItem & TabContent; - -type TTabsProps = { - tabs: TabItem[]; - storageKey?: string; - actions?: React.ReactNode; - defaultTab?: string; - containerClassName?: string; - tabListContainerClassName?: string; - tabListClassName?: string; - tabClassName?: string; - tabPanelClassName?: string; - size?: "sm" | "md" | "lg"; - storeInLocalStorage?: boolean; -}; - -export function Tabs(props: TTabsProps) { - const { - tabs, - storageKey, - actions, - defaultTab = tabs[0]?.key, - containerClassName = "", - tabListContainerClassName = "", - tabListClassName = "", - tabClassName = "", - tabPanelClassName = "", - size = "md", - storeInLocalStorage = true, - } = props; - // local storage - const { storedValue, setValue } = useLocalStorage( - storeInLocalStorage && storageKey ? `tab-${storageKey}` : `tab-${tabs[0]?.key}`, - defaultTab - ); - // state - const [selectedTab, setSelectedTab] = useState(storedValue ?? defaultTab); - - useEffect(() => { - if (storeInLocalStorage) { - setValue(selectedTab); - } - }, [selectedTab, setValue, storeInLocalStorage, storageKey]); - - const currentTabIndex = (tabKey: string): number => tabs.findIndex((tab) => tab.key === tabKey); - - const handleTabChange = (key: string) => { - setSelectedTab(key); - }; - - return ( -
- -
-
- - {actions &&
{actions}
} -
- - {tabs.map((tab) => ( - - {tab.content} - - ))} - -
-
-
- ); -}