[WEB-4471]fix: full screen view visibility (#7387)
* fix: full screen mode for analytics, work items peek and timeline chart * chore: added null check for portal elements
This commit is contained in:
parent
fce4729f22
commit
86f3ff1bd2
4 changed files with 185 additions and 178 deletions
|
|
@ -11,6 +11,7 @@ export default function WorkspaceLayout({ children }: { children: React.ReactNod
|
||||||
<CommandPalette />
|
<CommandPalette />
|
||||||
<WorkspaceAuthWrapper>
|
<WorkspaceAuthWrapper>
|
||||||
<div className="relative flex flex-col h-full w-full overflow-hidden rounded-lg border border-custom-border-200">
|
<div className="relative flex flex-col h-full w-full overflow-hidden rounded-lg border border-custom-border-200">
|
||||||
|
<div id="full-screen-portal" className="inset-0 absolute w-full" />
|
||||||
<div className="relative flex size-full overflow-hidden">
|
<div className="relative flex size-full overflow-hidden">
|
||||||
<ProjectAppSidebar />
|
<ProjectAppSidebar />
|
||||||
<main className="relative flex h-full w-full flex-col overflow-hidden bg-custom-background-100">
|
<main className="relative flex h-full w-full flex-col overflow-hidden bg-custom-background-100">
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
|
||||||
// plane package imports
|
// plane package imports
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
import { ICycle, IModule, IProject } from "@plane/types";
|
import { ICycle, IModule, IProject } from "@plane/types";
|
||||||
|
import { cn } from "@plane/utils";
|
||||||
import { useAnalytics } from "@/hooks/store";
|
import { useAnalytics } from "@/hooks/store";
|
||||||
// plane web components
|
// plane web components
|
||||||
import { WorkItemsModalMainContent } from "./content";
|
import { WorkItemsModalMainContent } from "./content";
|
||||||
|
|
@ -23,6 +24,7 @@ export const WorkItemsModal: React.FC<Props> = observer((props) => {
|
||||||
const [fullScreen, setFullScreen] = useState(false);
|
const [fullScreen, setFullScreen] = useState(false);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
setFullScreen(false);
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -30,23 +32,15 @@ export const WorkItemsModal: React.FC<Props> = observer((props) => {
|
||||||
updateIsEpic(isEpic ?? false);
|
updateIsEpic(isEpic ?? false);
|
||||||
}, [isEpic, updateIsEpic]);
|
}, [isEpic, updateIsEpic]);
|
||||||
|
|
||||||
return (
|
const portalContainer = document.getElementById("full-screen-portal") as HTMLElement;
|
||||||
<Transition.Root appear show={isOpen} as={React.Fragment}>
|
|
||||||
<Dialog as="div" className="relative z-30" onClose={handleClose}>
|
if (!isOpen) return null;
|
||||||
<Transition.Child
|
|
||||||
as={React.Fragment}
|
const content = (
|
||||||
enter="transition-transform duration-300"
|
<div className={cn("inset-0 z-[25] h-full w-full overflow-y-auto", fullScreen ? "absolute" : "fixed")}>
|
||||||
enterFrom="translate-x-full"
|
|
||||||
enterTo="translate-x-0"
|
|
||||||
leave="transition-transform duration-200"
|
|
||||||
leaveFrom="translate-x-0"
|
|
||||||
leaveTo="translate-x-full"
|
|
||||||
>
|
|
||||||
<div className="fixed inset-0 z-20 h-full w-full overflow-y-auto">
|
|
||||||
<Dialog.Panel>
|
|
||||||
<div
|
<div
|
||||||
className={`fixed right-0 top-0 z-20 h-full bg-custom-background-100 shadow-custom-shadow-md ${
|
className={`right-0 top-0 z-20 h-full bg-custom-background-100 shadow-custom-shadow-md ${
|
||||||
fullScreen ? "w-full p-2" : "w-full sm:w-full md:w-1/2"
|
fullScreen ? "w-full p-2 absolute" : "w-full sm:w-full md:w-1/2 fixed"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
@ -70,10 +64,8 @@ export const WorkItemsModal: React.FC<Props> = observer((props) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Panel>
|
|
||||||
</div>
|
</div>
|
||||||
</Transition.Child>
|
|
||||||
</Dialog>
|
|
||||||
</Transition.Root>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return fullScreen && portalContainer ? createPortal(content, portalContainer) : content;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { FC, useEffect, useState } from "react";
|
import { FC, useEffect, useState } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// plane imports
|
// plane imports
|
||||||
// components
|
// components
|
||||||
|
|
@ -176,10 +177,12 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
|
||||||
scrollContainer.scrollLeft = scrollWidth;
|
scrollContainer.scrollLeft = scrollWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const portalContainer = document.getElementById("full-screen-portal") as HTMLElement;
|
||||||
|
|
||||||
|
const content = (
|
||||||
<div
|
<div
|
||||||
className={cn("relative flex flex-col h-full select-none rounded-sm bg-custom-background-100 shadow", {
|
className={cn("relative flex flex-col h-full select-none rounded-sm bg-custom-background-100 shadow", {
|
||||||
"fixed inset-0 z-30 bg-custom-background-100": fullScreenMode,
|
"inset-0 z-[25] bg-custom-background-100": fullScreenMode,
|
||||||
"border-[0.5px] border-custom-border-200": border,
|
"border-[0.5px] border-custom-border-200": border,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|
@ -217,4 +220,6 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return fullScreenMode && portalContainer ? createPortal(content, portalContainer) : content;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import useKeypress from "@/hooks/use-keypress";
|
||||||
import usePeekOverviewOutsideClickDetector from "@/hooks/use-peek-overview-outside-click";
|
import usePeekOverviewOutsideClickDetector from "@/hooks/use-peek-overview-outside-click";
|
||||||
// store hooks
|
// store hooks
|
||||||
import { IssueActivity } from "../issue-detail/issue-activity";
|
import { IssueActivity } from "../issue-detail/issue-activity";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
interface IIssueView {
|
interface IIssueView {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -108,40 +109,20 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
||||||
|
|
||||||
const peekOverviewIssueClassName = cn(
|
const peekOverviewIssueClassName = cn(
|
||||||
!embedIssue
|
!embedIssue
|
||||||
? "fixed z-20 flex flex-col overflow-hidden rounded border border-custom-border-200 bg-custom-background-100 transition-all duration-300"
|
? "fixed z-[25] flex flex-col overflow-hidden rounded border border-custom-border-200 bg-custom-background-100 transition-all duration-300"
|
||||||
: `w-full h-full`,
|
: `w-full h-full`,
|
||||||
!embedIssue && {
|
!embedIssue && {
|
||||||
"bottom-0 right-0 top-0 w-full md:w-[50%] border-0 border-l": peekMode === "side-peek",
|
"bottom-0 right-0 top-0 w-full md:w-[50%] border-0 border-l": peekMode === "side-peek",
|
||||||
"size-5/6 top-[8.33%] left-[8.33%]": peekMode === "modal",
|
"size-5/6 top-[8.33%] left-[8.33%]": peekMode === "modal",
|
||||||
"inset-0 m-4": peekMode === "full-screen",
|
"inset-0 m-4 absolute": peekMode === "full-screen",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
const shouldUsePortal = !embedIssue && peekMode === "full-screen";
|
||||||
<>
|
|
||||||
{issue && !is_archived && (
|
|
||||||
<ArchiveIssueModal
|
|
||||||
isOpen={isArchiveIssueModalOpen === issueId}
|
|
||||||
handleClose={() => toggleArchiveIssueModal(null)}
|
|
||||||
data={issue}
|
|
||||||
onSubmit={async () => {
|
|
||||||
if (issueOperations.archive) await issueOperations.archive(workspaceSlug, projectId, issueId);
|
|
||||||
removeRoutePeekId();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{issue && isDeleteIssueModalOpen === issue.id && (
|
const portalContainer = document.getElementById("full-screen-portal") as HTMLElement;
|
||||||
<DeleteIssueModal
|
|
||||||
isOpen={!!isDeleteIssueModalOpen}
|
|
||||||
handleClose={() => {
|
|
||||||
toggleDeleteIssueModal(null);
|
|
||||||
}}
|
|
||||||
data={issue}
|
|
||||||
onSubmit={async () => issueOperations.remove(workspaceSlug, projectId, issueId)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
const content = (
|
||||||
<div className="w-full !text-base">
|
<div className="w-full !text-base">
|
||||||
{issueId && (
|
{issueId && (
|
||||||
<div
|
<div
|
||||||
|
|
@ -271,6 +252,34 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{issue && !is_archived && (
|
||||||
|
<ArchiveIssueModal
|
||||||
|
isOpen={isArchiveIssueModalOpen === issueId}
|
||||||
|
handleClose={() => toggleArchiveIssueModal(null)}
|
||||||
|
data={issue}
|
||||||
|
onSubmit={async () => {
|
||||||
|
if (issueOperations.archive) await issueOperations.archive(workspaceSlug, projectId, issueId);
|
||||||
|
removeRoutePeekId();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{issue && isDeleteIssueModalOpen === issue.id && (
|
||||||
|
<DeleteIssueModal
|
||||||
|
isOpen={!!isDeleteIssueModalOpen}
|
||||||
|
handleClose={() => {
|
||||||
|
toggleDeleteIssueModal(null);
|
||||||
|
}}
|
||||||
|
data={issue}
|
||||||
|
onSubmit={async () => issueOperations.remove(workspaceSlug, projectId, issueId)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{shouldUsePortal && portalContainer ? createPortal(content, portalContainer) : content}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue