[WEB-1716] chore: sidebar improvements for guests/viewers (#4941)

* chore: sidebar improvements for guests/viewers

* chore: store workspace menu open state in local storage
This commit is contained in:
Aaryan Khandelwal
2024-06-26 19:42:31 +05:30
committed by GitHub
parent eda1599c0d
commit 67784b45fd
4 changed files with 93 additions and 101 deletions

View File

@@ -351,11 +351,11 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
className="grid place-items-center p-0.5 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-80 rounded"
onClick={() => setIsMenuActive(!isMenuActive)}
>
<MoreHorizontal className="size-4" />
<MoreHorizontal className="size-3.5" />
</span>
}
className={cn(
"opacity-0 pointer-events-none flex-shrink-0 mr-1 group-hover/project-item:opacity-100 group-hover/project-item:pointer-events-auto",
"opacity-0 pointer-events-none flex-shrink-0 group-hover/project-item:opacity-100 group-hover/project-item:pointer-events-auto",
{
"opacity-100 pointer-events-auto": isMenuActive,
}
@@ -404,7 +404,6 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
<span>Copy link</span>
</span>
</CustomMenu.MenuItem>
{!isViewerOrGuest && (
<CustomMenu.MenuItem>
<Link href={`/${workspaceSlug}/projects/${project?.id}/archives/issues`}>

View File

@@ -220,21 +220,23 @@ export const SidebarProjectsList: FC = observer(() => {
<span>{isCollapsed ? <section.icon className="flex-shrink-0 size-3" /> : section.title}</span>
</Tooltip>
</Disclosure.Button>
{!isCollapsed && isAuthorizedUser && (
{!isCollapsed && (
<div className="flex items-center opacity-0 group-hover:opacity-100">
<Tooltip tooltipHeading="Create project" tooltipContent="">
<button
type="button"
className="p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0"
onClick={() => {
setTrackElement(`APP_SIDEBAR_${section.type}_BLOCK`);
setIsFavoriteProjectCreate(section.key === "favorite");
setIsProjectModalOpen(true);
}}
>
<Plus className="size-3" />
</button>
</Tooltip>
{isAuthorizedUser && (
<Tooltip tooltipHeading="Create project" tooltipContent="">
<button
type="button"
className="p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0"
onClick={() => {
setTrackElement(`APP_SIDEBAR_${section.type}_BLOCK`);
setIsFavoriteProjectCreate(section.key === "favorite");
setIsProjectModalOpen(true);
}}
>
<Plus className="size-3" />
</button>
</Tooltip>
)}
<Disclosure.Button
as="button"
type="button"
@@ -296,14 +298,14 @@ export const SidebarProjectsList: FC = observer(() => {
{isAuthorizedUser && joinedProjects?.length === 0 && (
<button
type="button"
className="flex w-full items-center gap-2 px-3 text-sm text-custom-sidebar-text-200"
className="w-full flex items-center gap-1.5 px-2 py-1.5 text-sm leading-5 font-medium text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-90 rounded-md"
onClick={() => {
setTrackElement("Sidebar");
toggleCreateProjectModal(true);
}}
>
<Plus className="size-5" />
{!isCollapsed && "Add Project"}
<Plus className="flex-shrink-0 size-4" />
{!isCollapsed && "Add project"}
</button>
)}
</div>

View File

@@ -8,11 +8,10 @@ import { TIssue } from "@plane/types";
import { CreateUpdateIssueModal } from "@/components/issues";
// constants
import { EIssuesStoreType } from "@/constants/issue";
import { EUserWorkspaceRoles } from "@/constants/workspace";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme, useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
import { useAppTheme, useCommandPalette, useEventTracker, useProject } from "@/hooks/store";
import useLocalStorage from "@/hooks/use-local-storage";
export const SidebarQuickActions = observer(() => {
@@ -30,16 +29,11 @@ export const SidebarQuickActions = observer(() => {
const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme();
const { setTrackElement } = useEventTracker();
const { joinedProjectIds } = useProject();
const {
membership: { currentWorkspaceRole },
} = useUser();
// local storage
const { storedValue, setValue } = useLocalStorage<Record<string, Partial<TIssue>>>("draftedIssue", {});
// derived values
const disabled = joinedProjectIds.length === 0;
const workspaceDraftIssue = workspaceSlug ? storedValue?.[workspaceSlug] ?? undefined : undefined;
// auth
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
const handleMouseEnter = () => {
// if enter before time out clear the timeout
@@ -74,66 +68,64 @@ export const SidebarQuickActions = observer(() => {
"flex-col gap-0": isSidebarCollapsed,
})}
>
{isAuthorizedUser && (
<div
<div
className={cn(
"relative flex-grow flex items-center justify-between gap-1 rounded h-8 hover:bg-custom-sidebar-background-90",
{
"size-8 aspect-square": isSidebarCollapsed,
"px-3 border-[0.5px] border-custom-sidebar-border-300": !isSidebarCollapsed,
}
)}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<button
type="button"
className={cn(
"relative flex-grow flex items-center justify-between gap-1 rounded h-8 hover:bg-custom-sidebar-background-90",
"relative flex flex-shrink-0 flex-grow items-center gap-2 text-custom-sidebar-text-300 rounded outline-none",
{
"size-8 aspect-square": isSidebarCollapsed,
"px-3 border-[0.5px] border-custom-sidebar-border-300": !isSidebarCollapsed,
}
)}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<button
type="button"
className={cn("relative flex flex-shrink-0 flex-grow items-center gap-2 rounded outline-none", {
"justify-center": isSidebarCollapsed,
"cursor-not-allowed opacity-50": disabled,
})}
onClick={() => {
setTrackElement("APP_SIDEBAR_QUICK_ACTIONS");
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
}}
disabled={disabled}
>
<PenSquare className="size-4 text-custom-sidebar-text-300" />
{!isSidebarCollapsed && <span className="text-sm font-medium">New issue</span>}
</button>
{!disabled && workspaceDraftIssue && (
<>
{!isSidebarCollapsed && (
<button type="button" className="grid place-items-center">
<ChevronUp
className={cn(
"size-4 transform !text-custom-sidebar-text-300 transition-transform duration-300",
{
"rotate-180": isDraftButtonOpen,
}
)}
/>
</button>
)}
{isDraftButtonOpen && (
<div
className={`fixed left-4 mt-0 h-10 w-[203px] pt-2 ${isSidebarCollapsed ? "top-[5.5rem]" : "top-24"}`}
>
<div className="h-full w-full">
<button
onClick={() => setIsDraftIssueModalOpen(true)}
className="flex w-full flex-shrink-0 items-center rounded border-[0.5px] border-custom-border-300 bg-custom-background-100 px-3 py-[10px] text-sm text-custom-text-300 shadow"
>
<PenSquare size={16} className="mr-2 !text-lg !leading-4 text-custom-sidebar-text-300" />
Last Drafted Issue
</button>
</div>
</div>
)}
</>
}
)}
</div>
)}
onClick={() => {
setTrackElement("APP_SIDEBAR_QUICK_ACTIONS");
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
}}
disabled={disabled}
>
<PenSquare className="size-4" />
{!isSidebarCollapsed && <span className="text-sm font-medium">New issue</span>}
</button>
{!disabled && workspaceDraftIssue && (
<>
{!isSidebarCollapsed && (
<button type="button" className="grid place-items-center">
<ChevronUp
className={cn("size-4 transform !text-custom-sidebar-text-300 transition-transform duration-300", {
"rotate-180": isDraftButtonOpen,
})}
/>
</button>
)}
{isDraftButtonOpen && (
<div
className={`fixed left-4 mt-0 h-10 w-[203px] pt-2 ${isSidebarCollapsed ? "top-[5.5rem]" : "top-24"}`}
>
<div className="h-full w-full">
<button
onClick={() => setIsDraftIssueModalOpen(true)}
className="flex w-full flex-shrink-0 items-center rounded border-[0.5px] border-custom-border-300 bg-custom-background-100 px-3 py-[10px] text-sm text-custom-text-300 shadow"
>
<PenSquare size={16} className="mr-2 !text-lg !leading-4 text-custom-sidebar-text-300" />
Last Drafted Issue
</button>
</div>
</div>
)}
</>
)}
</div>
<button
className={cn(
"flex-shrink-0 size-8 aspect-square grid place-items-center rounded hover:bg-custom-sidebar-background-90 outline-none",

View File

@@ -1,6 +1,6 @@
"use client";
import React, { useEffect, useState } from "react";
import React, { useEffect } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
@@ -16,11 +16,10 @@ import { EUserWorkspaceRoles } from "@/constants/workspace";
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme, useEventTracker, useUser } from "@/hooks/store";
import useLocalStorage from "@/hooks/use-local-storage";
import { usePlatformOS } from "@/hooks/use-platform-os";
export const SidebarWorkspaceMenu = observer(() => {
// states
const [isWorkspaceMenuOpen, setIsWorkspaceMenuOpen] = useState(true);
// store hooks
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
const { captureEvent } = useEventTracker();
@@ -32,7 +31,11 @@ export const SidebarWorkspaceMenu = observer(() => {
const { workspaceSlug } = useParams();
// pathname
const pathname = usePathname();
// computed
// local storage
const { setValue: toggleWorkspaceMenu, storedValue } = useLocalStorage<boolean>("is_workspace_menu_open", true);
// derived values
const isWorkspaceMenuOpen = !!storedValue;
// auth
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
const handleLinkClick = (itemKey: string) => {
@@ -45,8 +48,8 @@ export const SidebarWorkspaceMenu = observer(() => {
};
useEffect(() => {
if (sidebarCollapsed) setIsWorkspaceMenuOpen(true);
}, [sidebarCollapsed]);
if (sidebarCollapsed) toggleWorkspaceMenu(true);
}, [sidebarCollapsed, toggleWorkspaceMenu]);
return (
<Disclosure as="div" defaultOpen>
@@ -54,20 +57,16 @@ export const SidebarWorkspaceMenu = observer(() => {
<Disclosure.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"
onClick={() => setIsWorkspaceMenuOpen((prev) => !prev)}
onClick={() => toggleWorkspaceMenu(!isWorkspaceMenuOpen)}
>
{({ open }) => (
<>
<span>Workspace</span>
<span className="flex-shrink-0 hidden group-hover/workspace-button:inline-block rounded p-0.5 hover:bg-custom-sidebar-background-80">
<ChevronRight
className={cn("size-3.5 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
"rotate-90": open,
})}
/>
</span>
</>
)}
<span>Workspace</span>
<span className="flex-shrink-0 hidden group-hover/workspace-button:inline-block rounded p-0.5 hover:bg-custom-sidebar-background-80">
<ChevronRight
className={cn("size-3.5 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
"rotate-90": isWorkspaceMenuOpen,
})}
/>
</span>
</Disclosure.Button>
)}
<Transition