forked from github/plane
[WEB-905] chore: issue peek overview and kanban layout improvement (#4135)
* chore: peek overview and kanban card improvement * chore: peek overview improvement
This commit is contained in:
parent
fd2cacb0cd
commit
986f81e3ae
@ -41,6 +41,7 @@ export const CalendarIssueBlock: React.FC<Props> = observer((props) => {
|
||||
issue &&
|
||||
issue.project_id &&
|
||||
issue.id &&
|
||||
peekIssue?.issueId !== issue.id &&
|
||||
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id });
|
||||
|
||||
useOutsideClickDetector(menuActionRef, () => setIsMenuActive(false));
|
||||
@ -64,6 +65,7 @@ export const CalendarIssueBlock: React.FC<Props> = observer((props) => {
|
||||
|
||||
return (
|
||||
<ControlLink
|
||||
id={`issue-${issue.id}`}
|
||||
href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}
|
||||
target="_blank"
|
||||
onClick={() => handleIssuePeekOverview(issue)}
|
||||
|
@ -20,6 +20,7 @@ export const IssueGanttBlock: React.FC<Props> = observer((props) => {
|
||||
const { getProjectStates } = useProjectState();
|
||||
const {
|
||||
issue: { getIssueById },
|
||||
peekIssue,
|
||||
setPeekIssue,
|
||||
} = useIssueDetail();
|
||||
// derived values
|
||||
@ -31,11 +32,13 @@ export const IssueGanttBlock: React.FC<Props> = observer((props) => {
|
||||
workspaceSlug &&
|
||||
issueDetails &&
|
||||
!issueDetails.tempId &&
|
||||
peekIssue?.issueId !== issueDetails.id &&
|
||||
setPeekIssue({ workspaceSlug, projectId: issueDetails.project_id, issueId: issueDetails.id });
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
||||
return (
|
||||
<div
|
||||
id={`issue-${issueId}`}
|
||||
className="relative flex h-full w-full cursor-pointer items-center rounded"
|
||||
style={{
|
||||
backgroundColor: stateDetails?.color,
|
||||
|
@ -47,13 +47,14 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = observer((prop
|
||||
const {
|
||||
router: { workspaceSlug },
|
||||
} = useApplication();
|
||||
const { setPeekIssue } = useIssueDetail();
|
||||
const { peekIssue, setPeekIssue } = useIssueDetail();
|
||||
|
||||
const handleIssuePeekOverview = (issue: TIssue) =>
|
||||
workspaceSlug &&
|
||||
issue &&
|
||||
issue.project_id &&
|
||||
issue.id &&
|
||||
peekIssue?.issueId !== issue.id &&
|
||||
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id });
|
||||
|
||||
return (
|
||||
@ -69,16 +70,17 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = observer((prop
|
||||
|
||||
{issue?.is_draft ? (
|
||||
<Tooltip tooltipContent={issue.name} isMobile={isMobile}>
|
||||
<span>{issue.name}</span>
|
||||
<span className="pb-1.5">{issue.name}</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<ControlLink
|
||||
id={`issue-${issue.id}`}
|
||||
href={`/${workspaceSlug}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}issues/${
|
||||
issue.id
|
||||
}`}
|
||||
target="_blank"
|
||||
onClick={() => handleIssuePeekOverview(issue)}
|
||||
className="w-full line-clamp-1 cursor-pointer text-sm text-custom-text-100"
|
||||
className="w-full line-clamp-1 cursor-pointer text-sm text-custom-text-100 pb-1.5"
|
||||
disabled={!!issue?.tempId}
|
||||
>
|
||||
<Tooltip tooltipContent={issue.name} isMobile={isMobile}>
|
||||
@ -138,7 +140,7 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = memo((props) => {
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"rounded border-[0.5px] w-full border-custom-border-200 bg-custom-background-100 text-sm transition-all hover:border-custom-border-400",
|
||||
"rounded border-[0.5px] outline-[0.5px] outline-transparent w-full border-custom-border-200 bg-custom-background-100 text-sm transition-all hover:border-custom-border-400",
|
||||
{ "hover:cursor-grab": !isDragDisabled },
|
||||
{ "border-custom-primary-100": snapshot.isDragging },
|
||||
{ "border border-custom-primary-70 hover:border-custom-primary-70": peekIssueId === issue.id }
|
||||
|
@ -34,6 +34,7 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
|
||||
issue &&
|
||||
issue.project_id &&
|
||||
issue.id &&
|
||||
peekIssue?.issueId !== issue.id &&
|
||||
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id });
|
||||
|
||||
const issue = issuesMap[issueId];
|
||||
@ -71,6 +72,7 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
|
||||
</Tooltip>
|
||||
) : (
|
||||
<ControlLink
|
||||
id={`issue-${issue.id}`}
|
||||
href={`/${workspaceSlug}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}issues/${
|
||||
issue.id
|
||||
}`}
|
||||
|
@ -154,10 +154,13 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
||||
const [isMenuActive, setIsMenuActive] = useState(false);
|
||||
const menuActionRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const handleIssuePeekOverview = (issue: TIssue) => {
|
||||
if (workspaceSlug && issue && issue.project_id && issue.id)
|
||||
setPeekIssue({ workspaceSlug: workspaceSlug.toString(), projectId: issue.project_id, issueId: issue.id });
|
||||
};
|
||||
const handleIssuePeekOverview = (issue: TIssue) =>
|
||||
workspaceSlug &&
|
||||
issue &&
|
||||
issue.project_id &&
|
||||
issue.id &&
|
||||
peekIssue?.issueId !== issue.id &&
|
||||
setPeekIssue({ workspaceSlug: workspaceSlug.toString(), projectId: issue.project_id, issueId: issue.id });
|
||||
|
||||
const { subIssues: subIssuesStore, issue } = useIssueDetail();
|
||||
|
||||
@ -240,6 +243,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
||||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
<ControlLink
|
||||
id={`issue-${issueId}`}
|
||||
href={`/${workspaceSlug}/projects/${issueDetail.project_id}/issues/${issueId}`}
|
||||
target="_blank"
|
||||
onClick={() => handleIssuePeekOverview(issueDetail)}
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
// hooks
|
||||
import { useIssueDetail, useUser } from "@/hooks/store";
|
||||
import useKeypress from "@/hooks/use-keypress";
|
||||
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
||||
import usePeekOverviewOutsideClickDetector from "@/hooks/use-peek-overview-outside-click";
|
||||
// store hooks
|
||||
import { IssueActivity } from "../issue-detail/issue-activity";
|
||||
import { SubIssuesRoot } from "../sub-issues";
|
||||
@ -55,11 +55,15 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
||||
setPeekIssue(undefined);
|
||||
};
|
||||
|
||||
useOutsideClickDetector(issuePeekOverviewRef, () => {
|
||||
if (!isAnyModalOpen) {
|
||||
removeRoutePeekId();
|
||||
}
|
||||
});
|
||||
usePeekOverviewOutsideClickDetector(
|
||||
issuePeekOverviewRef,
|
||||
() => {
|
||||
if (!isAnyModalOpen) {
|
||||
removeRoutePeekId();
|
||||
}
|
||||
},
|
||||
issueId
|
||||
);
|
||||
const handleKeyDown = () => {
|
||||
const slashCommandDropdownElement = document.querySelector("#slash-command");
|
||||
const dropdownElement = document.activeElement?.tagName === "INPUT";
|
||||
|
@ -42,6 +42,7 @@ export const IssueListItem: React.FC<ISubIssues> = observer((props) => {
|
||||
} = props;
|
||||
|
||||
const {
|
||||
peekIssue,
|
||||
setPeekIssue,
|
||||
issue: { getIssueById },
|
||||
subIssues: { subIssueHelpersByIssueId, setSubIssueHelpers },
|
||||
@ -64,6 +65,7 @@ export const IssueListItem: React.FC<ISubIssues> = observer((props) => {
|
||||
issue &&
|
||||
issue.project_id &&
|
||||
issue.id &&
|
||||
peekIssue?.issueId !== issue.id &&
|
||||
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id });
|
||||
|
||||
if (!issue) return <></>;
|
||||
@ -117,6 +119,7 @@ export const IssueListItem: React.FC<ISubIssues> = observer((props) => {
|
||||
</div>
|
||||
|
||||
<ControlLink
|
||||
id={`issue-${issue.id}`}
|
||||
href={`/${workspaceSlug}/projects/${issue.project_id}/issues/${issue.id}`}
|
||||
target="_blank"
|
||||
onClick={() => handleIssuePeekOverview(issue)}
|
||||
|
29
web/hooks/use-peek-overview-outside-click.tsx
Normal file
29
web/hooks/use-peek-overview-outside-click.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
const usePeekOverviewOutsideClickDetector = (
|
||||
ref: React.RefObject<HTMLElement>,
|
||||
callback: () => void,
|
||||
issueId: string
|
||||
) => {
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(event.target as Node)) {
|
||||
let targetElement = event.target as HTMLElement | null;
|
||||
while (targetElement) {
|
||||
if (targetElement.id === `issue-${issueId}`) {
|
||||
return;
|
||||
}
|
||||
targetElement = targetElement.parentElement;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("mousedown", handleClick);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClick);
|
||||
};
|
||||
});
|
||||
};
|
||||
export default usePeekOverviewOutsideClickDetector;
|
Loading…
Reference in New Issue
Block a user