fix: favorite improvements (#5307)
This commit is contained in:
parent
07574b4222
commit
a93dfc1b8d
4 changed files with 218 additions and 193 deletions
|
|
@ -111,6 +111,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
const [publishModalOpen, setPublishModal] = useState(false);
|
||||
const [isMenuActive, setIsMenuActive] = useState(false);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isProjectListOpen, setIsProjectListOpen] = useState(false);
|
||||
const [instruction, setInstruction] = useState<"DRAG_OVER" | "DRAG_BELOW" | undefined>(undefined);
|
||||
// refs
|
||||
const actionSectionRef = useRef<HTMLDivElement | null>(null);
|
||||
|
|
@ -266,200 +267,209 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
|
||||
if (!project) return null;
|
||||
|
||||
useEffect(() => {
|
||||
if (URLProjectId === project.id) setIsProjectListOpen(true);
|
||||
}, [URLProjectId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PublishProjectModal isOpen={publishModalOpen} project={project} onClose={() => setPublishModal(false)} />
|
||||
<LeaveProjectModal project={project} isOpen={leaveProjectModalOpen} onClose={() => setLeaveProjectModal(false)} />
|
||||
<Disclosure key={`${project.id}_${URLProjectId}`} ref={projectRef} defaultOpen={URLProjectId === project.id}>
|
||||
{({ open }) => (
|
||||
<Disclosure key={`${project.id}_${URLProjectId}`} ref={projectRef} defaultOpen={isProjectListOpen}>
|
||||
<div
|
||||
id={`sidebar-${projectId}-${projectListType}`}
|
||||
className={cn("relative", {
|
||||
"bg-custom-sidebar-background-80 opacity-60": isDragging,
|
||||
})}
|
||||
>
|
||||
<DropIndicator classNames="absolute top-0" isVisible={instruction === "DRAG_OVER"} />
|
||||
<div
|
||||
id={`sidebar-${projectId}-${projectListType}`}
|
||||
className={cn("relative", {
|
||||
"bg-custom-sidebar-background-80 opacity-60": isDragging,
|
||||
})}
|
||||
className={cn(
|
||||
"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,
|
||||
"p-0 size-8 aspect-square justify-center mx-auto": isSidebarCollapsed,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<DropIndicator classNames="absolute top-0" isVisible={instruction === "DRAG_OVER"} />
|
||||
<div
|
||||
className={cn(
|
||||
"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,
|
||||
"p-0 size-8 aspect-square justify-center mx-auto": isSidebarCollapsed,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{!disableDrag && (
|
||||
<Tooltip
|
||||
isMobile={isMobile}
|
||||
tooltipContent={project.sort_order === null ? "Join the project to rearrange" : "Drag to rearrange"}
|
||||
position="top-right"
|
||||
disabled={isDragging}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"hidden group-hover/project-item:flex items-center justify-center absolute top-1/2 -left-3 -translate-y-1/2 rounded text-custom-sidebar-text-400 cursor-grab",
|
||||
{
|
||||
"cursor-not-allowed opacity-60": project.sort_order === null,
|
||||
"cursor-grabbing": isDragging,
|
||||
flex: isMenuActive,
|
||||
"!hidden": isSidebarCollapsed,
|
||||
}
|
||||
)}
|
||||
ref={dragHandleRef}
|
||||
>
|
||||
<DragHandle className="bg-transparent" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isSidebarCollapsed ? (
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${project.id}/issues`}
|
||||
className={cn("flex-grow flex items-center gap-1.5 truncate text-left select-none", {
|
||||
"justify-center": isSidebarCollapsed,
|
||||
})}
|
||||
>
|
||||
<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">
|
||||
<Logo logo={project.logo_props} size={16} />
|
||||
</div>
|
||||
</Disclosure.Button>
|
||||
</Link>
|
||||
) : (
|
||||
<>
|
||||
<Tooltip
|
||||
tooltipContent={`${project.name}`}
|
||||
position="right"
|
||||
disabled={!isSidebarCollapsed}
|
||||
isMobile={isMobile}
|
||||
>
|
||||
<Link href={`/${workspaceSlug}/projects/${project.id}/issues`} className="flex-grow flex truncate">
|
||||
<Disclosure.Button
|
||||
as="button"
|
||||
type="button"
|
||||
className={cn("flex-grow flex items-center gap-1.5 text-left select-none w-full", {
|
||||
"justify-center": isSidebarCollapsed,
|
||||
})}
|
||||
>
|
||||
<div className="size-4 grid place-items-center flex-shrink-0">
|
||||
<Logo logo={project.logo_props} size={16} />
|
||||
</div>
|
||||
<p className="truncate text-sm font-medium text-custom-sidebar-text-200">{project.name}</p>
|
||||
</Disclosure.Button>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
<CustomMenu
|
||||
customButton={
|
||||
<span
|
||||
ref={actionSectionRef}
|
||||
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" />
|
||||
</span>
|
||||
{!disableDrag && (
|
||||
<Tooltip
|
||||
isMobile={isMobile}
|
||||
tooltipContent={project.sort_order === null ? "Join the project to rearrange" : "Drag to rearrange"}
|
||||
position="top-right"
|
||||
disabled={isDragging}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"hidden group-hover/project-item:flex items-center justify-center absolute top-1/2 -left-3 -translate-y-1/2 rounded text-custom-sidebar-text-400 cursor-grab",
|
||||
{
|
||||
"cursor-not-allowed opacity-60": project.sort_order === null,
|
||||
"cursor-grabbing": isDragging,
|
||||
flex: isMenuActive,
|
||||
"!hidden": isSidebarCollapsed,
|
||||
}
|
||||
className={cn(
|
||||
"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,
|
||||
}
|
||||
)}
|
||||
customButtonClassName="grid place-items-center"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={project.is_favorite ? handleRemoveFromFavorites : handleAddToFavorites}
|
||||
>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<Star
|
||||
className={cn("h-3.5 w-3.5 ", {
|
||||
"fill-yellow-500 stroke-yellow-500": project.is_favorite,
|
||||
})}
|
||||
/>
|
||||
<span>{project.is_favorite ? "Remove from favorites" : "Add to favorites"}</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
|
||||
{/* publish project settings */}
|
||||
{isAdmin && (
|
||||
<CustomMenu.MenuItem onClick={() => setPublishModal(true)}>
|
||||
<div className="relative flex flex-shrink-0 items-center justify-start gap-2">
|
||||
<div className="flex h-4 w-4 cursor-pointer items-center justify-center rounded text-custom-sidebar-text-200 transition-all duration-300 hover:bg-custom-sidebar-background-80">
|
||||
<Share2 className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
</div>
|
||||
<div>{project.anchor ? "Publish settings" : "Publish"}</div>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
<CustomMenu.MenuItem>
|
||||
<Link href={`/${workspaceSlug}/projects/${project?.id}/draft-issues/`}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<PenSquare className="h-3.5 w-3.5 stroke-[1.5] text-custom-text-300" />
|
||||
<span>Draft issues</span>
|
||||
</div>
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem onClick={handleCopyText}>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<LinkIcon className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
<span>Copy link</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
{!isViewerOrGuest && (
|
||||
<CustomMenu.MenuItem>
|
||||
<Link href={`/${workspaceSlug}/projects/${project?.id}/archives/issues`}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<ArchiveIcon className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
<span>Archives</span>
|
||||
</div>
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
<CustomMenu.MenuItem>
|
||||
<Link href={`/${workspaceSlug}/projects/${project?.id}/settings`}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<Settings className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
<span>Settings</span>
|
||||
</div>
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
{/* leave project */}
|
||||
{isViewerOrGuest && (
|
||||
<CustomMenu.MenuItem onClick={handleLeaveProject}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<LogOut className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
<span>Leave project</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</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-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
||||
"rotate-90": open,
|
||||
)}
|
||||
ref={dragHandleRef}
|
||||
>
|
||||
<DragHandle className="bg-transparent" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isSidebarCollapsed ? (
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${project.id}/issues`}
|
||||
className={cn("flex-grow flex items-center gap-1.5 truncate text-left select-none", {
|
||||
"justify-center": isSidebarCollapsed,
|
||||
})}
|
||||
>
|
||||
<Disclosure.Button
|
||||
as="button"
|
||||
className="size-8 aspect-square flex-shrink-0 grid place-items-center"
|
||||
onClick={() => setIsProjectListOpen(!isProjectListOpen)}
|
||||
>
|
||||
<div className="size-4 grid place-items-center flex-shrink-0">
|
||||
<Logo logo={project.logo_props} size={16} />
|
||||
</div>
|
||||
</Disclosure.Button>
|
||||
</Link>
|
||||
) : (
|
||||
<>
|
||||
<Tooltip
|
||||
tooltipContent={`${project.name}`}
|
||||
position="right"
|
||||
disabled={!isSidebarCollapsed}
|
||||
isMobile={isMobile}
|
||||
>
|
||||
<Link href={`/${workspaceSlug}/projects/${project.id}/issues`} className="flex-grow flex truncate">
|
||||
<Disclosure.Button
|
||||
as="button"
|
||||
type="button"
|
||||
className={cn("flex-grow flex items-center gap-1.5 text-left select-none w-full", {
|
||||
"justify-center": isSidebarCollapsed,
|
||||
})}
|
||||
/>
|
||||
</Disclosure.Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<Transition
|
||||
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"
|
||||
>
|
||||
onClick={() => setIsProjectListOpen(!isProjectListOpen)}
|
||||
>
|
||||
<div className="size-4 grid place-items-center flex-shrink-0">
|
||||
<Logo logo={project.logo_props} size={16} />
|
||||
</div>
|
||||
<p className="truncate text-sm font-medium text-custom-sidebar-text-200">{project.name}</p>
|
||||
</Disclosure.Button>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
<CustomMenu
|
||||
customButton={
|
||||
<span
|
||||
ref={actionSectionRef}
|
||||
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" />
|
||||
</span>
|
||||
}
|
||||
className={cn(
|
||||
"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,
|
||||
}
|
||||
)}
|
||||
customButtonClassName="grid place-items-center"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={project.is_favorite ? handleRemoveFromFavorites : handleAddToFavorites}>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<Star
|
||||
className={cn("h-3.5 w-3.5 ", {
|
||||
"fill-yellow-500 stroke-yellow-500": project.is_favorite,
|
||||
})}
|
||||
/>
|
||||
<span>{project.is_favorite ? "Remove from favorites" : "Add to favorites"}</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
|
||||
{/* publish project settings */}
|
||||
{isAdmin && (
|
||||
<CustomMenu.MenuItem onClick={() => setPublishModal(true)}>
|
||||
<div className="relative flex flex-shrink-0 items-center justify-start gap-2">
|
||||
<div className="flex h-4 w-4 cursor-pointer items-center justify-center rounded text-custom-sidebar-text-200 transition-all duration-300 hover:bg-custom-sidebar-background-80">
|
||||
<Share2 className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
</div>
|
||||
<div>{project.anchor ? "Publish settings" : "Publish"}</div>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
<CustomMenu.MenuItem>
|
||||
<Link href={`/${workspaceSlug}/projects/${project?.id}/draft-issues/`}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<PenSquare className="h-3.5 w-3.5 stroke-[1.5] text-custom-text-300" />
|
||||
<span>Draft issues</span>
|
||||
</div>
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem onClick={handleCopyText}>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
<LinkIcon className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
<span>Copy link</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
{!isViewerOrGuest && (
|
||||
<CustomMenu.MenuItem>
|
||||
<Link href={`/${workspaceSlug}/projects/${project?.id}/archives/issues`}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<ArchiveIcon className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
<span>Archives</span>
|
||||
</div>
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
<CustomMenu.MenuItem>
|
||||
<Link href={`/${workspaceSlug}/projects/${project?.id}/settings`}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<Settings className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
<span>Settings</span>
|
||||
</div>
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
{/* leave project */}
|
||||
{isViewerOrGuest && (
|
||||
<CustomMenu.MenuItem onClick={handleLeaveProject}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<LogOut className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
<span>Leave project</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</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,
|
||||
}
|
||||
)}
|
||||
onClick={() => setIsProjectListOpen(!isProjectListOpen)}
|
||||
>
|
||||
<ChevronRight
|
||||
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
||||
"rotate-90": isProjectListOpen,
|
||||
})}
|
||||
/>
|
||||
</Disclosure.Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<Transition
|
||||
show={isProjectListOpen}
|
||||
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"
|
||||
>
|
||||
{isProjectListOpen && (
|
||||
<Disclosure.Panel as="div" className="flex flex-col gap-0.5 mt-1">
|
||||
{navigation(workspaceSlug?.toString(), project?.id).map((item) => {
|
||||
if (
|
||||
|
|
@ -498,10 +508,10 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
);
|
||||
})}
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
{isLastChild && <DropIndicator isVisible={instruction === "DRAG_BELOW"} />}
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</Transition>
|
||||
{isLastChild && <DropIndicator isVisible={instruction === "DRAG_BELOW"} />}
|
||||
</div>
|
||||
</Disclosure>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useState, FC, useRef, useEffect } from "react";
|
|||
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
|
||||
import { autoScrollForElements } from "@atlaskit/pragmatic-drag-and-drop-auto-scroll/element";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { Briefcase, ChevronRight, Plus } from "lucide-react";
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
// types
|
||||
|
|
@ -43,6 +43,8 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||
const { getProjectById, joinedProjectIds: joinedProjects, updateProjectView } = useProject();
|
||||
// router params
|
||||
const { workspaceSlug } = useParams();
|
||||
const pathname = usePathname();
|
||||
|
||||
// auth
|
||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
|
||||
|
|
@ -127,7 +129,12 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||
setIsAllProjectsListOpen(isOpen);
|
||||
localStorage.setItem("isAllProjectsListOpen", isOpen.toString());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (pathname.includes("projects")) {
|
||||
setIsAllProjectsListOpen(true);
|
||||
localStorage.setItem("isAllProjectsListOpen", "true");
|
||||
}
|
||||
}, [pathname]);
|
||||
return (
|
||||
<>
|
||||
{workspaceSlug && (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue