[WEB-1716] fix: sidebar UI inconsistencies (#4933)

* fix: sidebar UI inconsistencies

* fix: minor UI fixes
This commit is contained in:
Aaryan Khandelwal 2024-06-25 17:35:43 +05:30 committed by GitHub
parent 245962d5a5
commit 711494b72e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 160 additions and 126 deletions

View file

@ -53,19 +53,19 @@ export const AppSidebar: FC<IAppSidebar> = observer(() => {
<SidebarAppSwitcher /> <SidebarAppSwitcher />
<SidebarQuickActions /> <SidebarQuickActions />
<hr <hr
className={cn("flex-shrink-0 border-custom-sidebar-border-200 h-[0.5px] w-3/5 mx-auto my-2", { className={cn("flex-shrink-0 border-custom-sidebar-border-300 h-[0.5px] w-3/5 mx-auto my-2", {
"opacity-0": !sidebarCollapsed, "opacity-0": !sidebarCollapsed,
})} })}
/> />
<SidebarUserMenu /> <SidebarUserMenu />
<hr <hr
className={cn("flex-shrink-0 border-custom-sidebar-border-200 h-[0.5px] w-3/5 mx-auto my-2", { className={cn("flex-shrink-0 border-custom-sidebar-border-300 h-[0.5px] w-3/5 mx-auto my-2", {
"opacity-0": !sidebarCollapsed, "opacity-0": !sidebarCollapsed,
})} })}
/> />
<SidebarWorkspaceMenu /> <SidebarWorkspaceMenu />
<hr <hr
className={cn("flex-shrink-0 border-custom-sidebar-border-200 h-[0.5px] w-3/5 mx-auto my-2", { className={cn("flex-shrink-0 border-custom-sidebar-border-300 h-[0.5px] w-3/5 mx-auto my-2", {
"opacity-0": !sidebarCollapsed, "opacity-0": !sidebarCollapsed,
})} })}
/> />

View file

@ -288,7 +288,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
"group/project-item relative w-full px-2 py-1.5 flex items-center rounded-md text-custom-sidebar-text-100 hover:bg-custom-sidebar-background-90", "group/project-item relative w-full px-2 py-1.5 flex items-center rounded-md text-custom-sidebar-text-100 hover:bg-custom-sidebar-background-90",
{ {
"bg-custom-sidebar-background-90": isMenuActive, "bg-custom-sidebar-background-90": isMenuActive,
"p-0 size-7 aspect-square justify-center mx-auto": isSidebarCollapsed, "p-0 size-8 aspect-square justify-center mx-auto": isSidebarCollapsed,
} }
)} )}
> >
@ -317,7 +317,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
</Tooltip> </Tooltip>
)} )}
{isSidebarCollapsed ? ( {isSidebarCollapsed ? (
<Disclosure.Button as="button" className="grid place-items-center"> <Disclosure.Button as="button" className="size-8 aspect-square flex-shrink-0 grid place-items-center">
<div className="size-4 grid place-items-center flex-shrink-0"> <div className="size-4 grid place-items-center flex-shrink-0">
<Logo logo={project.logo_props} size={16} /> <Logo logo={project.logo_props} size={16} />
</div> </div>
@ -344,21 +344,6 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
)} )}
</Link> </Link>
</Tooltip> </Tooltip>
<Disclosure.Button
as="button"
className={cn(
"hidden group-hover/project-item:inline-block p-0.5 rounded hover:bg-custom-sidebar-background-80",
{
"inline-block": isMenuActive,
}
)}
>
<ChevronRight
className={cn("size-3.5 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
"rotate-90": open,
})}
/>
</Disclosure.Button>
<CustomMenu <CustomMenu
customButton={ customButton={
<span <span
@ -448,6 +433,22 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
)} )}
</CustomMenu> </CustomMenu>
<Disclosure.Button
as="button"
type="button"
className={cn(
"hidden group-hover/project-item:inline-block p-0.5 rounded hover:bg-custom-sidebar-background-80",
{
"inline-block": isMenuActive,
}
)}
>
<ChevronRight
className={cn("size-3.5 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
"rotate-90": open,
})}
/>
</Disclosure.Button>
</> </>
)} )}
</div> </div>
@ -482,7 +483,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
> >
<div <div
className={cn( className={cn(
"flex items-center gap-1.5 rounded-md pl-[30px] pr-2 py-1.5 outline-none text-custom-sidebar-text-300 hover:bg-custom-sidebar-background-90 focus:bg-custom-sidebar-background-90", "flex items-center gap-1.5 rounded-md pl-[18px] pr-2 py-1.5 outline-none text-custom-sidebar-text-300 hover:bg-custom-sidebar-background-90 focus:bg-custom-sidebar-background-90",
{ {
"text-custom-primary-100 bg-custom-primary-100/10 hover:bg-custom-primary-100/10": "text-custom-primary-100 bg-custom-primary-100/10 hover:bg-custom-primary-100/10":
pathname.includes(item.href), pathname.includes(item.href),

View file

@ -5,7 +5,7 @@ import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
import { autoScrollForElements } from "@atlaskit/pragmatic-drag-and-drop-auto-scroll/element"; import { autoScrollForElements } from "@atlaskit/pragmatic-drag-and-drop-auto-scroll/element";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { ChevronRight, Plus } from "lucide-react"; import { Briefcase, ChevronRight, LucideIcon, Plus, Star } from "lucide-react";
import { Disclosure, Transition } from "@headlessui/react"; import { Disclosure, Transition } from "@headlessui/react";
// types // types
import { IProject } from "@plane/types"; import { IProject } from "@plane/types";
@ -146,7 +146,7 @@ export const SidebarProjectsList: FC = observer(() => {
key: "all" | "favorite"; key: "all" | "favorite";
type: "FAVORITES" | "JOINED"; type: "FAVORITES" | "JOINED";
title: string; title: string;
shortTitle: string; icon: LucideIcon;
projects: string[]; projects: string[];
isOpen: boolean; isOpen: boolean;
}[] = [ }[] = [
@ -154,7 +154,7 @@ export const SidebarProjectsList: FC = observer(() => {
key: "favorite", key: "favorite",
type: "FAVORITES", type: "FAVORITES",
title: "Favorites", title: "Favorites",
shortTitle: "FP", icon: Star,
projects: favoriteProjects, projects: favoriteProjects,
isOpen: isFavoriteProjectsListOpen, isOpen: isFavoriteProjectsListOpen,
}, },
@ -162,7 +162,7 @@ export const SidebarProjectsList: FC = observer(() => {
key: "all", key: "all",
type: "JOINED", type: "JOINED",
title: "My projects", title: "My projects",
shortTitle: "MP", icon: Briefcase,
projects: joinedProjects, projects: joinedProjects,
isOpen: isAllProjectsListOpen, isOpen: isAllProjectsListOpen,
}, },
@ -180,90 +180,117 @@ export const SidebarProjectsList: FC = observer(() => {
)} )}
<div <div
ref={containerRef} ref={containerRef}
className={cn("vertical-scrollbar h-full space-y-2 !overflow-y-scroll scrollbar-sm -mr-3 -ml-4 pl-4", { className={cn("vertical-scrollbar h-full !overflow-y-scroll scrollbar-sm -mr-3 -ml-4 pl-4", {
"border-t border-custom-sidebar-border-300": isScrolled, "border-t border-custom-sidebar-border-300": isScrolled,
})} })}
> >
{projectSections.map((section) => { {projectSections.map((section, index) => {
if (!section.projects || section.projects.length === 0) return; if (!section.projects || section.projects.length === 0) return;
return ( return (
<Disclosure key={section.title} as="div" className="flex flex-col" defaultOpen={section.isOpen}> <>
<> <Disclosure key={section.title} as="div" className="flex flex-col" defaultOpen={section.isOpen}>
<div <>
className={cn( <div
"group w-full flex items-center justify-between px-2 py-0.5 rounded text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-90",
{
"p-0 justify-center w-fit mx-auto bg-custom-sidebar-background-90 hover:bg-custom-sidebar-background-80":
isCollapsed,
}
)}
>
<Disclosure.Button
as="button"
type="button"
className={cn( className={cn(
"group w-full flex items-center gap-1 whitespace-nowrap text-left text-sm font-medium text-custom-sidebar-text-400", "group w-full flex items-center justify-between px-2 py-0.5 rounded text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-90",
{ {
"!text-center w-8 px-2 py-0.5 justify-center": isCollapsed, "p-0 justify-center w-fit mx-auto bg-custom-sidebar-background-90 hover:bg-custom-sidebar-background-80":
isCollapsed,
} }
)} )}
onClick={() => toggleListDisclosure(!section.isOpen, section.key)}
> >
<Tooltip tooltipHeading={section.title} tooltipContent=""> <Disclosure.Button
<span>{isCollapsed ? section.shortTitle : section.title}</span> as="button"
</Tooltip> type="button"
{!isCollapsed && ( className={cn(
<ChevronRight "group w-full flex items-center gap-1 whitespace-nowrap text-left text-sm font-semibold text-custom-sidebar-text-400",
className={cn("flex-shrink-0 size-3.5 transition-all", { {
"rotate-90": section.isOpen, "!text-center w-8 px-2 py-1.5 justify-center": isCollapsed,
})} }
/> )}
)} onClick={() => toggleListDisclosure(!section.isOpen, section.key)}
</Disclosure.Button> >
{!isCollapsed && isAuthorizedUser && ( <Tooltip
<Tooltip tooltipHeading="Create project" tooltipContent=""> tooltipHeading={section.title}
<button tooltipContent=""
className="opacity-0 group-hover:opacity-100 p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0" position="right"
onClick={() => { disabled={!isCollapsed}
setTrackElement(`APP_SIDEBAR_${section.type}_BLOCK`);
setIsFavoriteProjectCreate(section.key === "favorite");
setIsProjectModalOpen(true);
}}
> >
<Plus className="size-3" /> <span>{isCollapsed ? <section.icon className="flex-shrink-0 size-3" /> : section.title}</span>
</button> </Tooltip>
</Tooltip> </Disclosure.Button>
)} {!isCollapsed && isAuthorizedUser && (
</div> <div className="flex items-center opacity-0 group-hover:opacity-100">
<Transition <Tooltip tooltipHeading="Create project" tooltipContent="">
show={section.isOpen} <button
enter="transition duration-100 ease-out" type="button"
enterFrom="transform scale-95 opacity-0" className="p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0"
enterTo="transform scale-100 opacity-100" onClick={() => {
leave="transition duration-75 ease-out" setTrackElement(`APP_SIDEBAR_${section.type}_BLOCK`);
leaveFrom="transform scale-100 opacity-100" setIsFavoriteProjectCreate(section.key === "favorite");
leaveTo="transform scale-95 opacity-0" setIsProjectModalOpen(true);
> }}
{section.isOpen && ( >
<Disclosure.Panel as="div" className="mt-3" static> <Plus className="size-3" />
{section.projects.map((projectId, index) => ( </button>
<SidebarProjectsListItem </Tooltip>
key={projectId} <Disclosure.Button
projectId={projectId} as="button"
handleCopyText={() => handleCopyText(projectId)} type="button"
projectListType={section.type} className="p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0"
disableDrag={section.key === "favorite"} onClick={() => toggleListDisclosure(!section.isOpen, section.key)}
disableDrop={section.key === "favorite"} >
isLastChild={index === section.projects.length - 1} <ChevronRight
handleOnProjectDrop={handleOnProjectDrop} className={cn("flex-shrink-0 size-3.5 transition-all", {
/> "rotate-90": section.isOpen,
))} })}
</Disclosure.Panel> />
)} </Disclosure.Button>
</Transition> </div>
</> )}
</Disclosure> </div>
<Transition
show={section.isOpen}
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
{section.isOpen && (
<Disclosure.Panel
as="div"
className={cn("mt-2 ml-1 space-y-1", {
"space-y-0 ml-0": isCollapsed,
})}
static
>
{section.projects.map((projectId, index) => (
<SidebarProjectsListItem
key={projectId}
projectId={projectId}
handleCopyText={() => handleCopyText(projectId)}
projectListType={section.type}
disableDrag={section.key === "favorite"}
disableDrop={section.key === "favorite"}
isLastChild={index === section.projects.length - 1}
handleOnProjectDrop={handleOnProjectDrop}
/>
))}
</Disclosure.Panel>
)}
</Transition>
</>
</Disclosure>
<hr
className={cn("flex-shrink-0 border-custom-sidebar-border-300 h-[0.5px] w-3/5 mx-auto my-2", {
"opacity-0": !sidebarCollapsed,
hidden: index === projectSections.length - 1,
})}
/>
</>
); );
})} })}
{isAuthorizedUser && joinedProjects?.length === 0 && ( {isAuthorizedUser && joinedProjects?.length === 0 && (

View file

@ -71,7 +71,9 @@ export const SidebarUserMenu = observer(() => {
} }
)} )}
> >
{<link.Icon className="size-4" />} <span className="flex-shrink-0 size-4 grid place-items-center">
<link.Icon className="size-4" />
</span>
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{link.label}</p>} {!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{link.label}</p>}
</div> </div>
</Tooltip> </Tooltip>

View file

@ -4,7 +4,7 @@ import React, { useEffect, useState } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import Link from "next/link"; import Link from "next/link";
import { useParams, usePathname } from "next/navigation"; import { useParams, usePathname } from "next/navigation";
import { ChevronRight, Crown, Settings } from "lucide-react"; import { ChevronRight, Crown } from "lucide-react";
import { Disclosure, Transition } from "@headlessui/react"; import { Disclosure, Transition } from "@headlessui/react";
// ui // ui
import { Tooltip } from "@plane/ui"; import { Tooltip } from "@plane/ui";
@ -51,28 +51,24 @@ export const SidebarWorkspaceMenu = observer(() => {
return ( return (
<Disclosure as="div" defaultOpen> <Disclosure as="div" defaultOpen>
{!sidebarCollapsed && ( {!sidebarCollapsed && (
<div className="group/workspace-button flex items-center justify-between text-custom-sidebar-text-400 px-2 py-0.5 hover:bg-custom-sidebar-background-90 rounded"> <Disclosure.Button
<Disclosure.Button as="button"
as="button" className="group/workspace-button w-full px-2 py-0.5 flex items-center justify-between gap-1 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-90 rounded text-sm font-semibold"
className="flex-grow flex items-center gap-1 text-sm font-medium" onClick={() => setIsWorkspaceMenuOpen((prev) => !prev)}
onClick={() => setIsWorkspaceMenuOpen((prev) => !prev)} >
> {({ open }) => (
<span>Workspace</span> <>
<ChevronRight <span>Workspace</span>
className={cn("flex-shrink-0 size-3.5 transition-all", { <span className="flex-shrink-0 hidden group-hover/workspace-button:inline-block rounded p-0.5 hover:bg-custom-sidebar-background-80">
"rotate-90": isWorkspaceMenuOpen, <ChevronRight
})} className={cn("size-3.5 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
/> "rotate-90": open,
</Disclosure.Button> })}
<Link />
href={`/${workspaceSlug}/settings`} </span>
className="flex-shrink-0 hidden group-hover/workspace-button:block rounded p-0.5 hover:bg-custom-sidebar-background-80" </>
> )}
<Tooltip tooltipHeading="Workspace settings" tooltipContent=""> </Disclosure.Button>
<Settings className="size-3" />
</Tooltip>
</Link>
</div>
)} )}
<Transition <Transition
show={isWorkspaceMenuOpen} show={isWorkspaceMenuOpen}
@ -86,8 +82,8 @@ export const SidebarWorkspaceMenu = observer(() => {
{isWorkspaceMenuOpen && ( {isWorkspaceMenuOpen && (
<Disclosure.Panel <Disclosure.Panel
as="div" as="div"
className={cn("mt-3 space-y-1", { className={cn("mt-2 ml-1 space-y-1", {
"space-y-0 mt-0": sidebarCollapsed, "space-y-0 mt-0 ml-0": sidebarCollapsed,
})} })}
static static
> >
@ -117,13 +113,13 @@ export const SidebarWorkspaceMenu = observer(() => {
} }
)} )}
> >
{ <span className="flex-shrink-0 size-4 grid place-items-center">
<link.Icon <link.Icon
className={cn("size-4", { className={cn("size-4", {
"rotate-180": link.key === "active-cycles", "rotate-180": link.key === "active-cycles",
})} })}
/> />
} </span>
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{link.label}</p>} {!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{link.label}</p>}
{!sidebarCollapsed && link.key === "active-cycles" && ( {!sidebarCollapsed && link.key === "active-cycles" && (
<Crown className="size-3.5 text-amber-400" /> <Crown className="size-3.5 text-amber-400" />

View file

@ -2,7 +2,7 @@
import { linearGradientDef } from "@nivo/core"; import { linearGradientDef } from "@nivo/core";
// icons // icons
import { BarChart2, Briefcase, CheckCircle, Home } from "lucide-react"; import { BarChart2, Briefcase, CheckCircle, Home, Settings } from "lucide-react";
// types // types
import { TIssuesListTypes, TStateGroups } from "@plane/types"; import { TIssuesListTypes, TStateGroups } from "@plane/types";
// ui // ui
@ -291,6 +291,14 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: {
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/analytics/`), highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/analytics/`),
Icon: BarChart2, Icon: BarChart2,
}, },
{
key: "settings",
label: "Settings",
href: `/settings`,
access: EUserWorkspaceRoles.GUEST,
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/analytics/`),
Icon: Settings,
},
]; ];
export const SIDEBAR_USER_MENU_ITEMS: { export const SIDEBAR_USER_MENU_ITEMS: {