[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:
Vamsi Krishna 2025-07-11 11:50:26 +05:30 committed by GitHub
parent fce4729f22
commit 86f3ff1bd2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 185 additions and 178 deletions

View file

@ -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">

View file

@ -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;
}); });

View file

@ -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;
}); });

View file

@ -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}
</> </>
); );
}); });