From 2aaf0a16373c9bc89773e618719a5e1ace93304a Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Wed, 8 May 2024 14:39:27 +0530 Subject: [PATCH] [WEB-1040] chore: list layout sub issue nesting implementation (#4404) --- .../issues/issue-layouts/list/block-root.tsx | 89 +++++++++++++++++++ .../issues/issue-layouts/list/block.tsx | 72 ++++++++++++--- .../issues/issue-layouts/list/blocks-list.tsx | 41 ++++----- .../issues/issue-layouts/list/index.ts | 1 + .../issues/issue-layouts/properties/index.ts | 1 + 5 files changed, 166 insertions(+), 38 deletions(-) create mode 100644 web/components/issues/issue-layouts/list/block-root.tsx diff --git a/web/components/issues/issue-layouts/list/block-root.tsx b/web/components/issues/issue-layouts/list/block-root.tsx new file mode 100644 index 000000000..a311d643a --- /dev/null +++ b/web/components/issues/issue-layouts/list/block-root.tsx @@ -0,0 +1,89 @@ +import React, { FC, MutableRefObject, useState } from "react"; +import { observer } from "mobx-react"; +import { IIssueDisplayProperties, TIssue, TIssueMap } from "@plane/types"; +// components +import RenderIfVisible from "@/components/core/render-if-visible-HOC"; +import { IssueBlock } from "@/components/issues/issue-layouts/list"; +// hooks +import { useIssueDetail } from "@/hooks/store"; +// types +import { TRenderQuickActions } from "./list-view-types"; + +type Props = { + issueIds: string[]; + issueId: string; + issuesMap: TIssueMap; + updateIssue: ((projectId: string, issueId: string, data: Partial) => Promise) | undefined; + quickActions: TRenderQuickActions; + canEditProperties: (projectId: string | undefined) => boolean; + displayProperties: IIssueDisplayProperties | undefined; + nestingLevel: number; + spacingLeft?: number; + containerRef: MutableRefObject; +}; + +export const IssueBlockRoot: FC = observer((props) => { + const { + issueIds, + issueId, + issuesMap, + updateIssue, + quickActions, + canEditProperties, + displayProperties, + nestingLevel, + spacingLeft = 14, + containerRef, + } = props; + // states + const [isExpanded, setExpanded] = useState(false); + // store hooks + const { subIssues: subIssuesStore } = useIssueDetail(); + + if (!issueId) return null; + + const subIssues = subIssuesStore.subIssuesByIssueId(issueId); + return ( + <> + + + + + {isExpanded && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssueId: string) => ( + + ))} + + ); +}); diff --git a/web/components/issues/issue-layouts/list/block.tsx b/web/components/issues/issue-layouts/list/block.tsx index b34b83473..075323a41 100644 --- a/web/components/issues/issue-layouts/list/block.tsx +++ b/web/components/issues/issue-layouts/list/block.tsx @@ -1,15 +1,18 @@ -import { useRef } from "react"; +import { Dispatch, MouseEvent, SetStateAction, useRef } from "react"; import { observer } from "mobx-react-lite"; +import { ChevronRight } from "lucide-react"; +// types import { TIssue, IIssueDisplayProperties, TIssueMap } from "@plane/types"; // ui import { Spinner, Tooltip, ControlLink } from "@plane/ui"; +// components +import { IssueProperties } from "@/components/issues/issue-layouts/properties"; // helpers import { cn } from "@/helpers/common.helper"; // hooks import { useApplication, useIssueDetail, useProject } from "@/hooks/store"; import { usePlatformOS } from "@/hooks/use-platform-os"; // types -import { IssueProperties } from "../properties/all-properties"; import { TRenderQuickActions } from "./list-view-types"; interface IssueBlockProps { @@ -19,18 +22,35 @@ interface IssueBlockProps { quickActions: TRenderQuickActions; displayProperties: IIssueDisplayProperties | undefined; canEditProperties: (projectId: string | undefined) => boolean; + nestingLevel: number; + spacingLeft?: number; + isExpanded: boolean; + setExpanded: Dispatch>; } export const IssueBlock: React.FC = observer((props: IssueBlockProps) => { - const { issuesMap, issueId, updateIssue, quickActions, displayProperties, canEditProperties } = props; + const { + issuesMap, + issueId, + updateIssue, + quickActions, + displayProperties, + canEditProperties, + nestingLevel, + spacingLeft = 14, + isExpanded, + setExpanded, + } = props; // refs const parentRef = useRef(null); // hooks const { router: { workspaceSlug }, } = useApplication(); + + // store hooks const { getProjectIdentifierById } = useProject(); - const { getIsIssuePeeked, setPeekIssue } = useIssueDetail(); + const { getIsIssuePeeked, setPeekIssue, subIssues: subIssuesStore } = useIssueDetail(); const handleIssuePeekOverview = (issue: TIssue) => workspaceSlug && @@ -47,28 +67,52 @@ export const IssueBlock: React.FC = observer((props: IssueBlock const canEditIssueProperties = canEditProperties(issue.project_id); const projectIdentifier = getProjectIdentifierById(issue.project_id); + const paddingLeft = `${spacingLeft}px`; + + const handleToggleExpand = (e: MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + if (nestingLevel >= 3) { + handleIssuePeekOverview(issue); + } else { + setExpanded((prevState) => { + if (!prevState && workspaceSlug && issue) + subIssuesStore.fetchSubIssues(workspaceSlug.toString(), issue.project_id, issue.id); + return !prevState; + }); + } + }; + return (
-
+
- {displayProperties && displayProperties?.key && ( -
- {projectIdentifier}-{issue.sequence_id} -
- )} +
+ + {displayProperties && displayProperties?.key && ( +
+ {projectIdentifier}-{issue.sequence_id} +
+ )} - {issue?.tempId !== undefined && ( -
- )} + {issue?.tempId !== undefined && ( +
+ )} +
{issue?.is_draft ? ( diff --git a/web/components/issues/issue-layouts/list/blocks-list.tsx b/web/components/issues/issue-layouts/list/blocks-list.tsx index 1e1751b76..b8e5df1d8 100644 --- a/web/components/issues/issue-layouts/list/blocks-list.tsx +++ b/web/components/issues/issue-layouts/list/blocks-list.tsx @@ -1,10 +1,9 @@ import { FC, MutableRefObject } from "react"; // components import { TGroupedIssues, TIssue, IIssueDisplayProperties, TIssueMap, TUnGroupedIssues } from "@plane/types"; -import RenderIfVisible from "@/components/core/render-if-visible-HOC"; -import { IssueBlock } from "@/components/issues"; -import { TRenderQuickActions } from "./list-view-types"; +import { IssueBlockRoot } from "@/components/issues/issue-layouts/list"; // types +import { TRenderQuickActions } from "./list-view-types"; interface Props { issueIds: TGroupedIssues | TUnGroupedIssues | any; @@ -22,27 +21,21 @@ export const IssueBlocksList: FC = (props) => { return (
{issueIds && issueIds.length > 0 ? ( - issueIds.map((issueId: string) => { - if (!issueId) return null; - return ( - - - - ); - }) + issueIds.map((issueId: string) => ( + + )) ) : (
No issues
)} diff --git a/web/components/issues/issue-layouts/list/index.ts b/web/components/issues/issue-layouts/list/index.ts index c245a2cca..26c125b51 100644 --- a/web/components/issues/issue-layouts/list/index.ts +++ b/web/components/issues/issue-layouts/list/index.ts @@ -1,4 +1,5 @@ export * from "./roots"; +export * from "./block-root"; export * from "./block"; export * from "./roots"; export * from "./blocks-list"; diff --git a/web/components/issues/issue-layouts/properties/index.ts b/web/components/issues/issue-layouts/properties/index.ts index 95f3ce21f..8bdc3ea3f 100644 --- a/web/components/issues/issue-layouts/properties/index.ts +++ b/web/components/issues/issue-layouts/properties/index.ts @@ -1 +1,2 @@ export * from "./labels"; +export * from "./all-properties";