-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[WEB-5614] refactor: update styling and structure across various components #8388
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||
| import React from "react"; | ||||||
| import { useTranslation } from "@plane/i18n"; | ||||||
| import { Tooltip } from "@plane/propel/tooltip"; | ||||||
| import type { TBaseLayoutType } from "@plane/types"; | ||||||
| import { cn } from "@plane/utils"; | ||||||
| import { usePlatformOS } from "@/hooks/use-platform-os"; | ||||||
| import { BASE_LAYOUTS } from "./constants"; | ||||||
|
|
||||||
|
|
@@ -13,6 +14,7 @@ type Props = { | |||||
| export function LayoutSwitcher(props: Props) { | ||||||
| const { layouts, onChange, selectedLayout } = props; | ||||||
| const { isMobile } = usePlatformOS(); | ||||||
| const { t } = useTranslation(); | ||||||
|
|
||||||
| const handleOnChange = (layoutKey: TBaseLayoutType) => { | ||||||
| if (selectedLayout !== layoutKey) { | ||||||
|
|
@@ -21,21 +23,29 @@ export function LayoutSwitcher(props: Props) { | |||||
| }; | ||||||
|
|
||||||
| 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) => { | ||||||
| const Icon = layout.icon; | ||||||
| return ( | ||||||
| <Tooltip key={layout.key} tooltipContent={layout.label} isMobile={isMobile}> | ||||||
| <Tooltip key={layout.key} tooltipContent={t(layout.label)} isMobile={isMobile}> | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Handle missing label for gantt layout. From the This is a blocking issue that prevents the build from passing. 🔎 Apply this diff to add a fallback:- <Tooltip key={layout.key} tooltipContent={t(layout.label)} isMobile={isMobile}>
+ <Tooltip key={layout.key} tooltipContent={t(layout.label || layout.key)} isMobile={isMobile}>Alternative solution: Add a {
key: "gantt",
icon: TimelineLayoutIcon,
label: "Timeline Layout", // Add this
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| <button | ||||||
| type="button" | ||||||
| className={`group grid h-[22px] w-7 place-items-center overflow-hidden rounded-sm transition-all hover:bg-surface-1 ${ | ||||||
| selectedLayout === layout.key ? "bg-surface-1 shadow-raised-100" : "" | ||||||
| }`} | ||||||
| className={cn( | ||||||
| "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)} | ||||||
| > | ||||||
| <Icon | ||||||
| width={14} | ||||||
| height={14} | ||||||
| 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> | ||||||
| </Tooltip> | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import { Disclosure, Transition } from "@headlessui/react"; | |
| import { EUserPermissions, EUserPermissionsLevel, PROJECT_TRACKER_ELEMENTS } from "@plane/constants"; | ||
| import { useTranslation } from "@plane/i18n"; | ||
| import { ChevronRightIcon } from "@plane/propel/icons"; | ||
| import { IconButton } from "@plane/propel/icon-button"; | ||
| import { TOAST_TYPE, setToast } from "@plane/propel/toast"; | ||
| import { Tooltip } from "@plane/propel/tooltip"; | ||
| import { Loader } from "@plane/ui"; | ||
|
|
@@ -178,39 +179,37 @@ export const SidebarProjectsList = observer(function SidebarProjectsList() { | |
| > | ||
| <span className="text-13 font-semibold">{t("projects")}</span> | ||
| </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 && ( | ||
| <Tooltip tooltipHeading={t("create_project")} tooltipContent=""> | ||
| <button | ||
| type="button" | ||
| data-ph-element={PROJECT_TRACKER_ELEMENTS.SIDEBAR_CREATE_PROJECT_TOOLTIP} | ||
| className="p-0.5 rounded-sm hover:bg-layer-1 flex-shrink-0" | ||
| <IconButton | ||
| variant="ghost" | ||
| size="sm" | ||
| icon={Plus} | ||
| onClick={() => { | ||
| 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")} | ||
| > | ||
| <Plus className="size-3" /> | ||
| </button> | ||
| /> | ||
| </Tooltip> | ||
| )} | ||
| <Disclosure.Button | ||
| as="button" | ||
| type="button" | ||
| className="p-0.5 rounded-sm hover:bg-layer-1 flex-shrink-0" | ||
| <IconButton | ||
| variant="ghost" | ||
| size="sm" | ||
| icon={ChevronRightIcon} | ||
| onClick={() => toggleListDisclosure(!isAllProjectsListOpen)} | ||
| className="text-placeholder" | ||
| iconClassName={cn("transition-transform", { | ||
| "rotate-90": isAllProjectsListOpen, | ||
| })} | ||
| aria-label={t( | ||
| isAllProjectsListOpen | ||
| ? "aria_labels.projects_sidebar.close_projects_menu" | ||
| : "aria_labels.projects_sidebar.open_projects_menu" | ||
| )} | ||
| > | ||
| <ChevronRightIcon | ||
| className={cn("flex-shrink-0 size-3 transition-all", { | ||
| "rotate-90": isAllProjectsListOpen, | ||
| })} | ||
| /> | ||
| </Disclosure.Button> | ||
| /> | ||
|
Comment on lines
+198
to
+212
|
||
| </div> | ||
| </div> | ||
| <Transition | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2,20 +2,29 @@ import * as React from "react"; | |||||
| import { Tabs as TabsPrimitive } from "@base-ui-components/react/tabs"; | ||||||
| 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); | ||||||
|
Comment on lines
+7
to
+11
|
||||||
|
|
||||||
| 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< | ||||||
| React.ComponentProps<typeof TabsPrimitive.List> & { | ||||||
| background?: BackgroundVariant; | ||||||
| background?: TabsVariant; | ||||||
| } & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.List>> | ||||||
| >; | ||||||
| Trigger: React.ForwardRefExoticComponent< | ||||||
| React.ComponentProps<typeof TabsPrimitive.Tab> & { size?: "sm" | "md" | "lg" } & React.RefAttributes< | ||||||
| React.ElementRef<typeof TabsPrimitive.Tab> | ||||||
| > | ||||||
| React.ComponentProps<typeof TabsPrimitive.Tab> & { | ||||||
| size?: "sm" | "md" | "lg"; | ||||||
| variant?: TabsVariant; | ||||||
| } & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.Tab>> | ||||||
| >; | ||||||
| Content: React.ForwardRefExoticComponent< | ||||||
| React.ComponentProps<typeof TabsPrimitive.Panel> & React.RefAttributes<React.ElementRef<typeof TabsPrimitive.Panel>> | ||||||
|
|
@@ -24,39 +33,38 @@ type TabsCompound = React.ForwardRefExoticComponent< | |||||
| }; | ||||||
|
|
||||||
| 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>> | ||||||
| ) { | ||||||
| return ( | ||||||
| <TabsPrimitive.Root | ||||||
| data-slot="tabs" | ||||||
| className={cn("flex flex-col w-full h-full", className)} | ||||||
| {...props} | ||||||
| ref={ref} | ||||||
| /> | ||||||
| <TabsContext.Provider value={{ variant }}> | ||||||
| <TabsPrimitive.Root | ||||||
| data-slot="tabs" | ||||||
| className={cn("flex flex-col w-full h-full", className)} | ||||||
| {...props} | ||||||
| ref={ref} | ||||||
| /> | ||||||
| </TabsContext.Provider> | ||||||
| ); | ||||||
| }); | ||||||
|
|
||||||
| const TabsList = React.forwardRef(function TabsList( | ||||||
| { | ||||||
| className, | ||||||
| background = "layer-2", | ||||||
| background = "contained", | ||||||
| ...props | ||||||
| }: React.ComponentProps<typeof TabsPrimitive.List> & { | ||||||
| background?: BackgroundVariant; | ||||||
| background?: TabsVariant; | ||||||
| }, | ||||||
| ref: React.ForwardedRef<React.ElementRef<typeof TabsPrimitive.List>> | ||||||
| ) { | ||||||
| return ( | ||||||
| <TabsPrimitive.List | ||||||
| data-slot="tabs-list" | ||||||
| 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-2": background === "layer-2", | ||||||
| "bg-layer-3": background === "layer-3", | ||||||
| "bg-layer-transparent": background === "layer-transparent", | ||||||
| "bg-layer-3": background === "contained", | ||||||
| }, | ||||||
| className | ||||||
| )} | ||||||
|
|
@@ -67,16 +75,20 @@ const TabsList = React.forwardRef(function TabsList( | |||||
| }); | ||||||
|
|
||||||
| 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 }, | ||||||
|
Comment on lines
+78
to
+82
|
||||||
| ref: React.ForwardedRef<React.ElementRef<typeof TabsPrimitive.Tab>> | ||||||
| ) { | ||||||
| return ( | ||||||
| <TabsPrimitive.Tab | ||||||
| data-slot="tabs-trigger" | ||||||
| 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", | ||||||
| "data-[selected]:bg-layer-transparent-active data-[selected]:text-primary data-[selected]:shadow-sm", | ||||||
| "text-placeholder hover:text-tertiary hover:bg-layer-transparent-hover", | ||||||
| "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]:text-primary data-[selected]:shadow-sm data-[selected]:bg-layer-2 data-[selected]:border data-[selected]:border-subtle-1 data-[selected]:raised-200", | ||||||
|
||||||
| " 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", | |
| "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", |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are extra spaces in this class string. Remove the double space after 'text-placeholder' for consistency.
| "text-placeholder hover:text-tertiary hover:bg-layer-transparent-hover", | |
| "text-placeholder hover:text-tertiary hover:bg-layer-transparent-hover", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The background prop was removed from Tabs.List, but based on the tabs.tsx implementation, the default background is now 'contained' which applies 'bg-layer-3'. Since this was previously 'layer-2', this change will alter the visual appearance. Verify this is intentional or explicitly set background="contained" for clarity.