chore: double click action added and code refactor (#5821)

This commit is contained in:
Anmol Singh Bhatia
2024-10-14 16:46:08 +05:30
committed by GitHub
parent 658542cc62
commit cb90810d02
2 changed files with 162 additions and 197 deletions

View File

@@ -1,8 +1,14 @@
"use client";
import React, { FC, useRef } from "react";
import React, { FC, useRef, useState } from "react";
import { omit } from "lodash";
import { observer } from "mobx-react";
import { Copy, Pencil, SquareStackIcon, Trash2 } from "lucide-react";
// types
import { TWorkspaceDraftIssue } from "@plane/types";
// ui
import { Row, Tooltip } from "@plane/ui";
import { Row, TContextMenuItem, Tooltip } from "@plane/ui";
// constants
import { EIssuesStoreType } from "@/constants/issue";
// helper
import { cn } from "@/helpers/common.helper";
// hooks
@@ -11,6 +17,8 @@ import { useAppTheme, useProject, useWorkspaceDraftIssues } from "@/hooks/store"
import { IdentifierText, IssueTypeIdentifier } from "@/plane-web/components/issues";
// local components
import { WorkspaceDraftIssueQuickActions } from "../issue-layouts";
import { CreateUpdateIssueModal } from "../issue-modal";
import { WorkspaceDraftIssueDeleteIssueModal } from "./delete-modal";
import { DraftIssueProperties } from "./draft-issue-properties";
type Props = {
@@ -21,8 +29,13 @@ type Props = {
export const DraftIssueBlock: FC<Props> = observer((props) => {
// props
const { workspaceSlug, issueId } = props;
// states
const [moveToIssue, setMoveToIssue] = useState(false);
const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false);
const [issueToEdit, setIssueToEdit] = useState<TWorkspaceDraftIssue | undefined>(undefined);
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
// hooks
const { getIssueById, updateIssue, deleteIssue, moveIssue } = useWorkspaceDraftIssues();
const { getIssueById, updateIssue, deleteIssue } = useWorkspaceDraftIssues();
const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme();
const { getProjectIdentifierById } = useProject();
// ref
@@ -32,97 +45,155 @@ export const DraftIssueBlock: FC<Props> = observer((props) => {
const projectIdentifier = (issue && issue.project_id && getProjectIdentifierById(issue.project_id)) || undefined;
if (!issue || !projectIdentifier) return null;
return (
<div id={`issue-${issue.id}`} className=" relative border-b border-b-custom-border-200 w-full cursor-pointer">
<Row
ref={issueRef}
className={cn(
"group/list-block min-h-11 relative flex flex-col gap-3 bg-custom-background-100 hover:bg-custom-background-90 py-3 text-sm transition-colors border border-transparent last:border-b-transparent",
{
"md:flex-row md:items-center": isSidebarCollapsed,
"lg:flex-row lg:items-center": !isSidebarCollapsed,
}
)}
>
<div className="flex w-full truncate">
<div className="flex flex-grow items-center gap-0.5 truncate">
<div className="flex items-center gap-1">
{/* {displayProperties && (displayProperties.key || displayProperties.issue_type) && ( */}
<div className="flex-shrink-0">
{issue.project_id && (
<div className="flex items-center space-x-2">
{issue?.type_id && <IssueTypeIdentifier issueTypeId={issue.type_id} />}
<IdentifierText
identifier={projectIdentifier}
enableClickToCopyIdentifier
textContainerClassName="text-xs font-medium text-custom-text-300"
/>
</div>
)}
</div>
{/* )} */}
const duplicateIssuePayload = omit(
{
...issue,
name: `${issue.name} (copy)`,
is_draft: true,
},
["id"]
);
{/* sub-issues chevron */}
<div className="size-4 grid place-items-center flex-shrink-0" />
const MENU_ITEMS: TContextMenuItem[] = [
{
key: "edit",
title: "Edit",
icon: Pencil,
action: () => {
setIssueToEdit(issue);
setCreateUpdateIssueModal(true);
},
},
{
key: "make-a-copy",
title: "Make a copy",
icon: Copy,
action: () => {
setCreateUpdateIssueModal(true);
},
},
{
key: "move-to-issues",
title: "Move to issues",
icon: SquareStackIcon,
action: () => {
setMoveToIssue(true);
setIssueToEdit(issue);
setCreateUpdateIssueModal(true);
},
},
{
key: "delete",
title: "Delete",
icon: Trash2,
action: () => {
setDeleteIssueModal(true);
},
},
];
return (
<>
<WorkspaceDraftIssueDeleteIssueModal
data={issue}
isOpen={deleteIssueModal}
handleClose={() => setDeleteIssueModal(false)}
onSubmit={async () => deleteIssue(workspaceSlug, issueId)}
/>
<CreateUpdateIssueModal
isOpen={createUpdateIssueModal}
onClose={() => {
setCreateUpdateIssueModal(false);
setIssueToEdit(undefined);
setMoveToIssue(false);
}}
data={issueToEdit ?? duplicateIssuePayload}
onSubmit={async (data) => {
if (issueToEdit) await updateIssue(workspaceSlug, issueId, data);
}}
storeType={EIssuesStoreType.WORKSPACE_DRAFT}
fetchIssueDetails={false}
moveToIssue={moveToIssue}
isDraft
/>
<div
id={`issue-${issue.id}`}
className=" relative border-b border-b-custom-border-200 w-full cursor-pointer"
onDoubleClick={() => {
setIssueToEdit(issue);
setCreateUpdateIssueModal(true);
}}
>
<Row
ref={issueRef}
className={cn(
"group/list-block min-h-11 relative flex flex-col gap-3 bg-custom-background-100 hover:bg-custom-background-90 py-3 text-sm transition-colors border border-transparent last:border-b-transparent",
{
"md:flex-row md:items-center": isSidebarCollapsed,
"lg:flex-row lg:items-center": !isSidebarCollapsed,
}
)}
>
<div className="flex w-full truncate">
<div className="flex flex-grow items-center gap-0.5 truncate">
<div className="flex items-center gap-1">
<div className="flex-shrink-0">
{issue.project_id && (
<div className="flex items-center space-x-2">
{issue?.type_id && <IssueTypeIdentifier issueTypeId={issue.type_id} />}
<IdentifierText
identifier={projectIdentifier}
enableClickToCopyIdentifier
textContainerClassName="text-xs font-medium text-custom-text-300"
/>
</div>
)}
</div>
{/* sub-issues chevron */}
<div className="size-4 grid place-items-center flex-shrink-0" />
</div>
<Tooltip tooltipContent={issue.name} position="top-left" renderByDefault={false}>
<p className="w-full truncate cursor-pointer text-sm text-custom-text-100">{issue.name}</p>
</Tooltip>
</div>
<Tooltip
tooltipContent={issue.name}
// isMobile={isMobile}
position="top-left"
// disabled={isCurrentBlockDragging}
renderByDefault={false}
{/* quick actions */}
<div
className={cn("block border border-custom-border-300 rounded", {
"md:hidden": isSidebarCollapsed,
"lg:hidden": !isSidebarCollapsed,
})}
>
<p className="w-full truncate cursor-pointer text-sm text-custom-text-100">{issue.name}</p>
</Tooltip>
<WorkspaceDraftIssueQuickActions parentRef={issueRef} MENU_ITEMS={MENU_ITEMS} />
</div>
</div>
{/* quick actions */}
<div
className={cn("block border border-custom-border-300 rounded", {
"md:hidden": isSidebarCollapsed,
"lg:hidden": !isSidebarCollapsed,
})}
>
<WorkspaceDraftIssueQuickActions
parentRef={issueRef}
<div className="flex flex-shrink-0 items-center gap-2">
<DraftIssueProperties
className={`relative flex flex-wrap ${isSidebarCollapsed ? "md:flex-grow md:flex-shrink-0" : "lg:flex-grow lg:flex-shrink-0"} items-center gap-2 whitespace-nowrap`}
issue={issue}
handleUpdate={async (data) => updateIssue(workspaceSlug, issueId, data)}
handleDelete={async () => deleteIssue(workspaceSlug, issueId)}
handleMoveToIssues={async () => moveIssue(workspaceSlug, issueId, issue)}
updateIssue={async (projectId, issueId, data) => {
await updateIssue(workspaceSlug, issueId, data);
}}
activeLayout="List"
/>
<div
className={cn("hidden", {
"md:flex": isSidebarCollapsed,
"lg:flex": !isSidebarCollapsed,
})}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<WorkspaceDraftIssueQuickActions parentRef={issueRef} MENU_ITEMS={MENU_ITEMS} />
</div>
</div>
</div>
<div className="flex flex-shrink-0 items-center gap-2">
<DraftIssueProperties
className={`relative flex flex-wrap ${isSidebarCollapsed ? "md:flex-grow md:flex-shrink-0" : "lg:flex-grow lg:flex-shrink-0"} items-center gap-2 whitespace-nowrap`}
issue={issue}
updateIssue={async (projectId, issueId, data) => {
await updateIssue(workspaceSlug, issueId, data);
}}
activeLayout="List"
/>
<div
className={cn("hidden", {
"md:flex": isSidebarCollapsed,
"lg:flex": !isSidebarCollapsed,
})}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<WorkspaceDraftIssueQuickActions
parentRef={issueRef}
issue={issue}
handleUpdate={async (data) => updateIssue(workspaceSlug, issueId, data)}
handleDelete={async () => deleteIssue(workspaceSlug, issueId)}
handleMoveToIssues={async () => moveIssue(workspaceSlug, issueId, issue)}
/>
</div>
</div>
</Row>
</div>
</Row>
</div>
</>
);
});

View File

@@ -1,131 +1,25 @@
"use client";
import { useState } from "react";
import { Placement } from "@popperjs/core";
import omit from "lodash/omit";
import { observer } from "mobx-react";
// icons
import { Copy, Pencil, SquareStackIcon, Trash2 } from "lucide-react";
// types
import { TWorkspaceDraftIssue } from "@plane/types";
// ui
import { ContextMenu, CustomMenu, TContextMenuItem } from "@plane/ui";
// components
import { CreateUpdateIssueModal } from "@/components/issues";
// constant
import { EIssuesStoreType } from "@/constants/issue";
// helpers
import { cn } from "@/helpers/common.helper";
// local components
import { WorkspaceDraftIssueDeleteIssueModal } from "./delete-modal";
export interface IQuickActionProps {
issue: TWorkspaceDraftIssue;
handleDelete: () => Promise<void>;
handleUpdate: (payload: Partial<TWorkspaceDraftIssue>) => Promise<TWorkspaceDraftIssue | undefined>;
handleMoveToIssues?: () => Promise<void>;
customActionButton?: React.ReactElement;
portalElement?: HTMLDivElement | null;
placements?: Placement;
export interface Props {
parentRef: React.RefObject<HTMLElement>;
MENU_ITEMS: TContextMenuItem[];
}
export const WorkspaceDraftIssueQuickActions: React.FC<IQuickActionProps> = observer((props) => {
const {
issue,
handleDelete,
handleUpdate,
handleMoveToIssues,
customActionButton,
portalElement,
placements = "bottom-end",
parentRef,
} = props;
// states
const [moveToIssue, setMoveToIssue] = useState(false);
const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false);
const [issueToEdit, setIssueToEdit] = useState<TWorkspaceDraftIssue | undefined>(undefined);
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
const duplicateIssuePayload = omit(
{
...issue,
name: `${issue.name} (copy)`,
is_draft: true,
},
["id"]
);
const MENU_ITEMS: TContextMenuItem[] = [
{
key: "edit",
title: "Edit",
icon: Pencil,
action: () => {
setIssueToEdit(issue);
setCreateUpdateIssueModal(true);
},
},
{
key: "make-a-copy",
title: "Make a copy",
icon: Copy,
action: () => {
setCreateUpdateIssueModal(true);
},
},
{
key: "move-to-issues",
title: "Move to issues",
icon: SquareStackIcon,
action: () => {
if (handleMoveToIssues) {
setMoveToIssue(true);
setIssueToEdit(issue);
setCreateUpdateIssueModal(true);
}
},
},
{
key: "delete",
title: "Delete",
icon: Trash2,
action: () => {
setDeleteIssueModal(true);
},
},
];
export const WorkspaceDraftIssueQuickActions: React.FC<Props> = observer((props) => {
const { parentRef, MENU_ITEMS } = props;
return (
<>
<WorkspaceDraftIssueDeleteIssueModal
data={issue}
isOpen={deleteIssueModal}
handleClose={() => setDeleteIssueModal(false)}
onSubmit={handleDelete}
/>
<CreateUpdateIssueModal
isOpen={createUpdateIssueModal}
onClose={() => {
setCreateUpdateIssueModal(false);
setIssueToEdit(undefined);
setMoveToIssue(false);
}}
data={issueToEdit ?? duplicateIssuePayload}
onSubmit={async (data) => {
if (issueToEdit && handleUpdate) await handleUpdate(data as TWorkspaceDraftIssue);
}}
storeType={EIssuesStoreType.WORKSPACE_DRAFT}
fetchIssueDetails={false}
moveToIssue={moveToIssue}
isDraft
/>
<ContextMenu parentRef={parentRef} items={MENU_ITEMS} />
<CustomMenu
ellipsis
customButton={customActionButton}
portalElement={portalElement}
placement={placements}
placement="bottom-end"
menuItemsClassName="z-[14]"
maxHeight="lg"
useCaptureForOutsideClick