[WEB-5614] refactor: update styling and structure across various components (#8388)

This commit is contained in:
Jayash Tripathy 2025-12-18 20:19:51 +05:30 committed by GitHub
parent cb56fbe3ca
commit 81dbd5ab19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 102 additions and 69 deletions

View file

@ -72,17 +72,17 @@ function AnalyticsPage({ params }: Route.ComponentProps) {
<div className={"flex flex-col w-full h-full"}> <div className={"flex flex-col w-full h-full"}>
<div <div
className={cn( className={cn(
"px-6 py-2 border-b border-subtle flex items-center gap-4 overflow-hidden w-full justify-between bg-layer-1" "px-6 py-2 border-b border-subtle flex items-center gap-4 overflow-hidden w-full justify-between bg-surface-1"
)} )}
> >
<Tabs.List background="layer-2" className={"my-2 overflow-x-auto flex w-fit"}> <Tabs.List className={"overflow-x-auto flex w-fit h-7"}>
{ANALYTICS_TABS.map((tab) => ( {ANALYTICS_TABS.map((tab) => (
<Tabs.Trigger <Tabs.Trigger
key={tab.key} key={tab.key}
value={tab.key} value={tab.key}
disabled={tab.isDisabled} disabled={tab.isDisabled}
size="md" size="md"
className="px-3" className="px-3 h-6"
onClick={() => { onClick={() => {
if (!tab.isDisabled) { if (!tab.isDisabled) {
handleTabChange(tab.key); handleTabChange(tab.key);

View file

@ -1,8 +1,10 @@
import { observer } from "mobx-react"; import { observer } from "mobx-react";
// plane package imports // plane package imports
import { getButtonStyling } from "@plane/propel/button";
import { Logo } from "@plane/propel/emoji-icon-picker"; import { Logo } from "@plane/propel/emoji-icon-picker";
import { ProjectIcon } from "@plane/propel/icons"; import { ChevronDownIcon, ProjectIcon } from "@plane/propel/icons";
import { CustomSearchSelect } from "@plane/ui"; import { CustomSearchSelect } from "@plane/ui";
import { cn } from "@plane/utils";
// hooks // hooks
import { useProject } from "@/hooks/store/use-project"; import { useProject } from "@/hooks/store/use-project";
@ -40,8 +42,9 @@ export const ProjectSelect = observer(function ProjectSelect(props: Props) {
value={value ?? []} value={value ?? []}
onChange={(val: string[]) => onChange(val)} onChange={(val: string[]) => onChange(val)}
options={options} options={options}
label={ className="border-none p-0"
<div className="flex items-center gap-2 p-1 "> customButton={
<div className={cn(getButtonStyling("secondary", "lg"), "gap-2")}>
<ProjectIcon className="h-4 w-4" /> <ProjectIcon className="h-4 w-4" />
{value && value.length > 3 {value && value.length > 3
? `3+ projects` ? `3+ projects`
@ -51,8 +54,10 @@ export const ProjectSelect = observer(function ProjectSelect(props: Props) {
.map((p) => getProjectById(p)?.name) .map((p) => getProjectById(p)?.name)
.join(", ") .join(", ")
: "All projects"} : "All projects"}
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
</div> </div>
} }
customButtonClassName="border-none p-0 bg-transparent hover:bg-transparent w-auto h-auto"
multiple multiple
/> />
); );

View file

@ -35,7 +35,7 @@ export const BaseKanbanGroup = observer(function BaseKanbanGroup<T extends IBase
<div <div
ref={groupRef} ref={groupRef}
className={cn( className={cn(
"relative flex flex-shrink-0 flex-col w-[350px] border-[1px] border-transparent p-2 pt-0 max-h-full overflow-y-auto bg-surface-2 rounded-md", "relative flex flex-shrink-0 flex-col w-[350px] border-[1px] border-transparent p-2 pt-0 max-h-full overflow-y-auto bg-layer-1 rounded-md",
{ {
"bg-layer-1": isDraggingOver, "bg-layer-1": isDraggingOver,
}, },
@ -43,7 +43,7 @@ export const BaseKanbanGroup = observer(function BaseKanbanGroup<T extends IBase
)} )}
> >
{/* Group Header */} {/* Group Header */}
<div className="sticky top-0 z-[2] w-full flex-shrink-0 bg-surface-2 px-1 py-2 cursor-pointer"> <div className="sticky top-0 z-[2] w-full flex-shrink-0 px-1 py-2 cursor-pointer">
{renderGroupHeader ? ( {renderGroupHeader ? (
renderGroupHeader({ group, itemCount: itemIds.length, isCollapsed, onToggleGroup }) renderGroupHeader({ group, itemCount: itemIds.length, isCollapsed, onToggleGroup })
) : ( ) : (

View file

@ -1,6 +1,7 @@
import React from "react"; import { useTranslation } from "@plane/i18n";
import { Tooltip } from "@plane/propel/tooltip"; import { Tooltip } from "@plane/propel/tooltip";
import type { TBaseLayoutType } from "@plane/types"; import type { TBaseLayoutType } from "@plane/types";
import { cn } from "@plane/utils";
import { usePlatformOS } from "@/hooks/use-platform-os"; import { usePlatformOS } from "@/hooks/use-platform-os";
import { BASE_LAYOUTS } from "./constants"; import { BASE_LAYOUTS } from "./constants";
@ -13,6 +14,7 @@ type Props = {
export function LayoutSwitcher(props: Props) { export function LayoutSwitcher(props: Props) {
const { layouts, onChange, selectedLayout } = props; const { layouts, onChange, selectedLayout } = props;
const { isMobile } = usePlatformOS(); const { isMobile } = usePlatformOS();
const { t } = useTranslation();
const handleOnChange = (layoutKey: TBaseLayoutType) => { const handleOnChange = (layoutKey: TBaseLayoutType) => {
if (selectedLayout !== layoutKey) { if (selectedLayout !== layoutKey) {
@ -21,21 +23,29 @@ export function LayoutSwitcher(props: Props) {
}; };
return ( return (
<div className="flex items-center gap-1 rounded-sm bg-layer-1 p-1"> <div className="flex items-center gap-1 rounded-md bg-layer-3 p-1">
{BASE_LAYOUTS.filter((l) => (layouts ? layouts.includes(l.key) : true)).map((layout) => { {BASE_LAYOUTS.filter((l) => (layouts ? layouts.includes(l.key) : true)).map((layout) => {
const Icon = layout.icon; const Icon = layout.icon;
return ( return (
<Tooltip key={layout.key} tooltipContent={layout.label} isMobile={isMobile}> <Tooltip key={layout.key} tooltipContent={t(layout.label)} isMobile={isMobile}>
<button <button
type="button" type="button"
className={`group grid h-[22px] w-7 place-items-center overflow-hidden rounded-sm transition-all hover:bg-surface-1 ${ className={cn(
selectedLayout === layout.key ? "bg-surface-1 shadow-raised-100" : "" "group grid h-5.5 w-7 place-items-center overflow-hidden rounded-sm transition-all hover:bg-layer-transparent-hover",
}`} {
"bg-layer-transparent-active hover:bg-layer-transparent-active": selectedLayout === layout.key,
}
)}
onClick={() => handleOnChange(layout.key)} onClick={() => handleOnChange(layout.key)}
> >
<Icon <Icon
width={14}
height={14}
strokeWidth={2} strokeWidth={2}
className={`h-3.5 w-3.5 ${selectedLayout === layout.key ? "text-primary" : "text-secondary"}`} className={cn("size-3.5", {
"text-primary": selectedLayout === layout.key,
"text-secondary": selectedLayout !== layout.key,
})}
/> />
</button> </button>
</Tooltip> </Tooltip>

View file

@ -441,8 +441,10 @@ export const SidebarProjectsListItem = observer(function SidebarProjectsListItem
size="sm" size="sm"
icon={ChevronRightIcon} icon={ChevronRightIcon}
onClick={() => setIsProjectListOpen(!isProjectListOpen)} onClick={() => setIsProjectListOpen(!isProjectListOpen)}
className={cn("hidden group-hover/project-item:inline-flex text-placeholder transition-transform", { className={cn("hidden group-hover/project-item:inline-flex text-placeholder", {
"inline-flex": isMenuActive, "inline-flex": isMenuActive,
})}
iconClassName={cn("transition-transform", {
"rotate-90": isProjectListOpen, "rotate-90": isProjectListOpen,
})} })}
aria-label={t( aria-label={t(

View file

@ -9,6 +9,7 @@ import { Disclosure, Transition } from "@headlessui/react";
import { EUserPermissions, EUserPermissionsLevel, PROJECT_TRACKER_ELEMENTS } from "@plane/constants"; import { EUserPermissions, EUserPermissionsLevel, PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
import { ChevronRightIcon } from "@plane/propel/icons"; import { ChevronRightIcon } from "@plane/propel/icons";
import { IconButton } from "@plane/propel/icon-button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { Tooltip } from "@plane/propel/tooltip"; import { Tooltip } from "@plane/propel/tooltip";
import { Loader } from "@plane/ui"; import { Loader } from "@plane/ui";
@ -178,39 +179,37 @@ export const SidebarProjectsList = observer(function SidebarProjectsList() {
> >
<span className="text-13 font-semibold">{t("projects")}</span> <span className="text-13 font-semibold">{t("projects")}</span>
</Disclosure.Button> </Disclosure.Button>
<div className="flex items-center opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto"> <div className="flex items-center gap-1">
{isAuthorizedUser && ( {isAuthorizedUser && (
<Tooltip tooltipHeading={t("create_project")} tooltipContent=""> <Tooltip tooltipHeading={t("create_project")} tooltipContent="">
<button <IconButton
type="button" variant="ghost"
data-ph-element={PROJECT_TRACKER_ELEMENTS.SIDEBAR_CREATE_PROJECT_TOOLTIP} size="sm"
className="p-0.5 rounded-sm hover:bg-layer-1 flex-shrink-0" icon={Plus}
onClick={() => { onClick={() => {
setIsProjectModalOpen(true); setIsProjectModalOpen(true);
}} }}
data-ph-element={PROJECT_TRACKER_ELEMENTS.SIDEBAR_CREATE_PROJECT_TOOLTIP}
className="hidden group-hover:inline-flex text-placeholder"
aria-label={t("aria_labels.projects_sidebar.create_new_project")} aria-label={t("aria_labels.projects_sidebar.create_new_project")}
> />
<Plus className="size-3" />
</button>
</Tooltip> </Tooltip>
)} )}
<Disclosure.Button <IconButton
as="button" variant="ghost"
type="button" size="sm"
className="p-0.5 rounded-sm hover:bg-layer-1 flex-shrink-0" icon={ChevronRightIcon}
onClick={() => toggleListDisclosure(!isAllProjectsListOpen)} onClick={() => toggleListDisclosure(!isAllProjectsListOpen)}
className="text-placeholder"
iconClassName={cn("transition-transform", {
"rotate-90": isAllProjectsListOpen,
})}
aria-label={t( aria-label={t(
isAllProjectsListOpen isAllProjectsListOpen
? "aria_labels.projects_sidebar.close_projects_menu" ? "aria_labels.projects_sidebar.close_projects_menu"
: "aria_labels.projects_sidebar.open_projects_menu" : "aria_labels.projects_sidebar.open_projects_menu"
)} )}
> />
<ChevronRightIcon
className={cn("flex-shrink-0 size-3 transition-all", {
"rotate-90": isAllProjectsListOpen,
})}
/>
</Disclosure.Button>
</div> </div>
</div> </div>
<Transition <Transition

View file

@ -112,7 +112,7 @@ export const AreaChart = React.memo(function AreaChart<K extends string, T exten
left: margin?.left === undefined ? 20 : margin.left, left: margin?.left === undefined ? 20 : margin.left,
}} }}
> >
<CartesianGrid stroke="--alpha(var(--border-color-subtle) / 80%)" vertical={false} /> <CartesianGrid stroke="var(--border-color-subtle)" vertical={false} />
<XAxis <XAxis
dataKey={xAxis.key} dataKey={xAxis.key}
tick={(props) => { tick={(props) => {

View file

@ -127,7 +127,7 @@ export const BarChart = React.memo(function BarChart<K extends string, T extends
barSize={barSize} barSize={barSize}
className="recharts-wrapper" className="recharts-wrapper"
> >
<CartesianGrid stroke="--alpha(var(--border-color-subtle) / 80%)" vertical={false} /> <CartesianGrid stroke="var(--border-color-subtle)" vertical={false} />
<XAxis <XAxis
dataKey={xAxis.key} dataKey={xAxis.key}
tick={(props) => { tick={(props) => {

View file

@ -98,7 +98,7 @@ export const LineChart = React.memo(function LineChart<K extends string, T exten
left: margin?.left === undefined ? 20 : margin.left, left: margin?.left === undefined ? 20 : margin.left,
}} }}
> >
<CartesianGrid stroke="--alpha(var(--border-color-subtle) / 80%)" vertical={false} /> <CartesianGrid stroke="var(--border-color-subtle)" vertical={false} />
<XAxis <XAxis
dataKey={xAxis.key} dataKey={xAxis.key}
tick={(props) => { tick={(props) => {

View file

@ -82,7 +82,7 @@ export const ScatterChart = React.memo(function ScatterChart<K extends string, T
left: margin?.left === undefined ? 20 : margin.left, left: margin?.left === undefined ? 20 : margin.left,
}} }}
> >
<CartesianGrid stroke="--alpha(var(--border-color-subtle) / 80%)" vertical={false} /> <CartesianGrid stroke="var(--border-color-subtle)" vertical={false} />
<XAxis <XAxis
dataKey={xAxis.key} dataKey={xAxis.key}
tick={(props) => { tick={(props) => {

View file

@ -16,7 +16,7 @@ export const TreeMapChart = React.memo(function TreeMapChart(props: TreeMapChart
data={data} data={data}
nameKey="name" nameKey="name"
dataKey="value" dataKey="value"
stroke="currentColor" stroke="transparent"
className="bg-layer-1 cursor-pointer" className="bg-layer-1 cursor-pointer"
content={<CustomTreeMapContent />} content={<CustomTreeMapContent />}
animationEasing="ease-out" animationEasing="ease-out"

View file

@ -38,6 +38,7 @@ type IconButtonPropsWithChildren = React.ButtonHTMLAttributes<HTMLButtonElement>
VariantProps<typeof iconButtonVariants> & { VariantProps<typeof iconButtonVariants> & {
icon: React.FC<{ className?: string }>; icon: React.FC<{ className?: string }>;
loading?: boolean; loading?: boolean;
iconClassName?: string;
}; };
export type IconButtonProps = Omit<IconButtonPropsWithChildren, "children">; export type IconButtonProps = Omit<IconButtonPropsWithChildren, "children">;

View file

@ -15,6 +15,7 @@ const IconButton = React.forwardRef(function IconButton(
loading = false, loading = false,
disabled = false, disabled = false,
icon: Icon, icon: Icon,
iconClassName = "",
...rest ...rest
} = props; } = props;
@ -27,11 +28,14 @@ const IconButton = React.forwardRef(function IconButton(
{...rest} {...rest}
> >
<Icon <Icon
className={cn({ className={cn(
"size-3.5": size === "sm", {
"size-4": size === "base" || size === "lg", "size-3.5": size === "sm",
"size-5": size === "xl", "size-4": size === "base" || size === "lg",
})} "size-5": size === "xl",
},
iconClassName
)}
/> />
</button> </button>
); );

View file

@ -2,20 +2,29 @@ import * as React from "react";
import { Tabs as TabsPrimitive } from "@base-ui-components/react/tabs"; import { Tabs as TabsPrimitive } from "@base-ui-components/react/tabs";
import { cn } from "../utils/classname"; import { cn } from "../utils/classname";
type BackgroundVariant = "layer-1" | "layer-2" | "layer-3" | "layer-transparent"; type TabsVariant = "contained";
type TabsContextType = {
variant?: TabsVariant;
};
const TabsContext = React.createContext<TabsContextType | undefined>(undefined);
type TabsCompound = React.ForwardRefExoticComponent< type TabsCompound = React.ForwardRefExoticComponent<
React.ComponentProps<typeof TabsPrimitive.Root> & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.Root>> React.ComponentProps<typeof TabsPrimitive.Root> & {
variant?: TabsVariant;
} & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.Root>>
> & { > & {
List: React.ForwardRefExoticComponent< List: React.ForwardRefExoticComponent<
React.ComponentProps<typeof TabsPrimitive.List> & { React.ComponentProps<typeof TabsPrimitive.List> & {
background?: BackgroundVariant; background?: TabsVariant;
} & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.List>> } & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.List>>
>; >;
Trigger: React.ForwardRefExoticComponent< Trigger: React.ForwardRefExoticComponent<
React.ComponentProps<typeof TabsPrimitive.Tab> & { size?: "sm" | "md" | "lg" } & React.RefAttributes< React.ComponentProps<typeof TabsPrimitive.Tab> & {
React.ElementRef<typeof TabsPrimitive.Tab> size?: "sm" | "md" | "lg";
> variant?: TabsVariant;
} & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.Tab>>
>; >;
Content: React.ForwardRefExoticComponent< Content: React.ForwardRefExoticComponent<
React.ComponentProps<typeof TabsPrimitive.Panel> & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.Panel>> React.ComponentProps<typeof TabsPrimitive.Panel> & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.Panel>>
@ -24,26 +33,28 @@ type TabsCompound = React.ForwardRefExoticComponent<
}; };
const TabsRoot = React.forwardRef(function TabsRoot( const TabsRoot = React.forwardRef(function TabsRoot(
{ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>, { className, variant, ...props }: React.ComponentProps<typeof TabsPrimitive.Root> & { variant?: TabsVariant },
ref: React.ForwardedRef<React.ElementRef<typeof TabsPrimitive.Root>> ref: React.ForwardedRef<React.ElementRef<typeof TabsPrimitive.Root>>
) { ) {
return ( return (
<TabsPrimitive.Root <TabsContext.Provider value={{ variant }}>
data-slot="tabs" <TabsPrimitive.Root
className={cn("flex flex-col w-full h-full", className)} data-slot="tabs"
{...props} className={cn("flex flex-col w-full h-full", className)}
ref={ref} {...props}
/> ref={ref}
/>
</TabsContext.Provider>
); );
}); });
const TabsList = React.forwardRef(function TabsList( const TabsList = React.forwardRef(function TabsList(
{ {
className, className,
background = "layer-2", background = "contained",
...props ...props
}: React.ComponentProps<typeof TabsPrimitive.List> & { }: React.ComponentProps<typeof TabsPrimitive.List> & {
background?: BackgroundVariant; background?: TabsVariant;
}, },
ref: React.ForwardedRef<React.ElementRef<typeof TabsPrimitive.List>> ref: React.ForwardedRef<React.ElementRef<typeof TabsPrimitive.List>>
) { ) {
@ -51,12 +62,9 @@ const TabsList = React.forwardRef(function TabsList(
<TabsPrimitive.List <TabsPrimitive.List
data-slot="tabs-list" data-slot="tabs-list"
className={cn( className={cn(
"flex w-full items-center justify-between gap-1.5 rounded-md text-13 p-0.5 relative overflow-auto", "flex w-full items-center justify-between gap-1.5 rounded-lg text-13 p-0.5 relative overflow-auto",
{ {
"bg-layer-1": background === "layer-1", "bg-layer-3": background === "contained",
"bg-layer-2": background === "layer-2",
"bg-layer-3": background === "layer-3",
"bg-layer-transparent": background === "layer-transparent",
}, },
className className
)} )}
@ -67,16 +75,20 @@ const TabsList = React.forwardRef(function TabsList(
}); });
const TabsTrigger = React.forwardRef(function TabsTrigger( const TabsTrigger = React.forwardRef(function TabsTrigger(
{ className, size = "md", ...props }: React.ComponentProps<typeof TabsPrimitive.Tab> & { size?: "sm" | "md" | "lg" }, {
className,
size = "md",
...props
}: React.ComponentProps<typeof TabsPrimitive.Tab> & { size?: "sm" | "md" | "lg"; variant?: TabsVariant },
ref: React.ForwardedRef<React.ElementRef<typeof TabsPrimitive.Tab>> ref: React.ForwardedRef<React.ElementRef<typeof TabsPrimitive.Tab>>
) { ) {
return ( return (
<TabsPrimitive.Tab <TabsPrimitive.Tab
data-slot="tabs-trigger" data-slot="tabs-trigger"
className={cn( className={cn(
"flex items-center justify-center p-1 min-w-fit w-full font-medium text-primary outline-none focus:outline-none cursor-pointer transition-all duration-200 ease-in-out rounded-sm", "flex items-center justify-center p-1 min-w-fit w-full font-medium text-primary outline-none focus:outline-none cursor-pointer transition-all duration-200 ease-in-out rounded-md border border-transparent",
"data-[selected]:bg-layer-transparent-active data-[selected]:text-primary data-[selected]:shadow-sm", " data-[selected]:text-primary data-[selected]:shadow-sm data-[selected]:bg-layer-2 data-[selected]:border data-[selected]:border-subtle-1 data-[selected]:raised-200",
"text-placeholder hover:text-tertiary hover:bg-layer-transparent-hover", "text-placeholder hover:text-tertiary hover:bg-layer-transparent-hover",
"disabled:text-placeholder disabled:cursor-not-allowed", "disabled:text-placeholder disabled:cursor-not-allowed",
{ {
"text-11": size === "sm", "text-11": size === "sm",