mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
refactor: gantt sidebar
This commit is contained in:
parent
4943eb397e
commit
38f6c03298
@ -1,4 +1,5 @@
|
|||||||
import { FC, useEffect, useRef } from "react";
|
import { observer } from "mobx-react";
|
||||||
|
import { FC } from "react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssueDetail } from "hooks/store";
|
import { useIssueDetail } from "hooks/store";
|
||||||
import { useChart } from "../hooks";
|
import { useChart } from "../hooks";
|
||||||
@ -20,7 +21,7 @@ export type GanttChartBlocksProps = {
|
|||||||
showAllBlocks: boolean;
|
showAllBlocks: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
export const GanttChartBlocksList: FC<GanttChartBlocksProps> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
itemsContainerWidth,
|
itemsContainerWidth,
|
||||||
blocks,
|
blocks,
|
||||||
@ -31,12 +32,10 @@ export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
|||||||
enableBlockMove,
|
enableBlockMove,
|
||||||
showAllBlocks,
|
showAllBlocks,
|
||||||
} = props;
|
} = props;
|
||||||
// refs
|
|
||||||
const blocksContainerRef = useRef<HTMLDivElement>(null);
|
|
||||||
// store hooks
|
// store hooks
|
||||||
const { peekIssue } = useIssueDetail();
|
const { peekIssue } = useIssueDetail();
|
||||||
// chart hook
|
// chart hook
|
||||||
const { activeBlock, dispatch, scrollTop, updateScrollTop } = useChart();
|
const { activeBlock, dispatch, updateScrollTop } = useChart();
|
||||||
|
|
||||||
// update the active block on hover
|
// update the active block on hover
|
||||||
const updateActiveBlock = (block: IGanttBlock | null) => {
|
const updateActiveBlock = (block: IGanttBlock | null) => {
|
||||||
@ -87,23 +86,13 @@ export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
|||||||
sidebarScrollContainer.scrollTop = e.currentTarget.scrollTop;
|
sidebarScrollContainer.scrollTop = e.currentTarget.scrollTop;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const blocksContainer = blocksContainerRef.current;
|
|
||||||
if (!blocksContainer) return;
|
|
||||||
|
|
||||||
blocksContainer.scrollTop = scrollTop;
|
|
||||||
}, [scrollTop]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={blocksContainerRef}
|
|
||||||
className="relative z-[5] mt-[72px] h-full overflow-hidden overflow-y-auto"
|
className="relative z-[5] mt-[72px] h-full overflow-hidden overflow-y-auto"
|
||||||
style={{ width: `${itemsContainerWidth}px` }}
|
style={{ width: `${itemsContainerWidth}px` }}
|
||||||
onScroll={handleBlocksScroll}
|
onScroll={handleBlocksScroll}
|
||||||
>
|
>
|
||||||
{blocks &&
|
{blocks?.map((block) => {
|
||||||
blocks.length > 0 &&
|
|
||||||
blocks.map((block) => {
|
|
||||||
// hide the block if it doesn't have start and target dates and showAllBlocks is false
|
// hide the block if it doesn't have start and target dates and showAllBlocks is false
|
||||||
if (!showAllBlocks && !(block.start_date && block.target_date)) return;
|
if (!showAllBlocks && !(block.start_date && block.target_date)) return;
|
||||||
|
|
||||||
@ -137,4 +126,4 @@ export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { useRef } from "react";
|
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
BiWeekChartView,
|
BiWeekChartView,
|
||||||
DayChartView,
|
DayChartView,
|
||||||
GanttChartBlocksList,
|
GanttChartBlocksList,
|
||||||
|
GanttChartSidebar,
|
||||||
HourChartView,
|
HourChartView,
|
||||||
IBlockUpdateData,
|
IBlockUpdateData,
|
||||||
IGanttBlock,
|
IGanttBlock,
|
||||||
@ -51,16 +51,14 @@ export const GanttChartMainContent: React.FC<Props> = (props) => {
|
|||||||
title,
|
title,
|
||||||
updateCurrentViewRenderPayload,
|
updateCurrentViewRenderPayload,
|
||||||
} = props;
|
} = props;
|
||||||
// refs
|
|
||||||
const sidebarRef = useRef<HTMLDivElement>(null);
|
|
||||||
// chart hook
|
// chart hook
|
||||||
const { currentView, currentViewData, updateScrollLeft, updateScrollTop } = useChart();
|
const { currentView, currentViewData, updateScrollLeft, updateScrollTop } = useChart();
|
||||||
|
|
||||||
// handling scroll functionality
|
// handling scroll functionality
|
||||||
const onScroll = (e: React.UIEvent<HTMLDivElement, UIEvent>) => {
|
const onScroll = (e: React.UIEvent<HTMLDivElement, UIEvent>) => {
|
||||||
const { clientWidth: clientVisibleWidth, scrollLeft: currentLeftScrollPosition, scrollWidth } = e.currentTarget;
|
const { clientWidth: clientVisibleWidth, scrollLeft: currentLeftScrollPosition, scrollWidth } = e.currentTarget;
|
||||||
|
|
||||||
updateScrollLeft(currentLeftScrollPosition);
|
updateScrollLeft(currentLeftScrollPosition);
|
||||||
|
updateScrollTop(e.currentTarget.scrollTop);
|
||||||
|
|
||||||
const approxRangeLeft = scrollWidth >= clientVisibleWidth + 1000 ? 1000 : scrollWidth - clientVisibleWidth;
|
const approxRangeLeft = scrollWidth >= clientVisibleWidth + 1000 ? 1000 : scrollWidth - clientVisibleWidth;
|
||||||
const approxRangeRight = scrollWidth - (approxRangeLeft + clientVisibleWidth);
|
const approxRangeRight = scrollWidth - (approxRangeLeft + clientVisibleWidth);
|
||||||
@ -69,8 +67,6 @@ export const GanttChartMainContent: React.FC<Props> = (props) => {
|
|||||||
if (currentLeftScrollPosition <= approxRangeLeft) updateCurrentViewRenderPayload("left", currentView);
|
if (currentLeftScrollPosition <= approxRangeLeft) updateCurrentViewRenderPayload("left", currentView);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSidebarScroll = (e: React.UIEvent<HTMLDivElement, UIEvent>) => updateScrollTop(e.currentTarget.scrollTop);
|
|
||||||
|
|
||||||
const CHART_VIEW_COMPONENTS: {
|
const CHART_VIEW_COMPONENTS: {
|
||||||
[key in TGanttViews]: React.FC;
|
[key in TGanttViews]: React.FC;
|
||||||
} = {
|
} = {
|
||||||
@ -82,44 +78,32 @@ export const GanttChartMainContent: React.FC<Props> = (props) => {
|
|||||||
quarter: QuarterChartView,
|
quarter: QuarterChartView,
|
||||||
year: YearChartView,
|
year: YearChartView,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!currentView) return null;
|
||||||
const ActiveChartView = CHART_VIEW_COMPONENTS[currentView];
|
const ActiveChartView = CHART_VIEW_COMPONENTS[currentView];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
// DO NOT REMOVE THE ID
|
// DO NOT REMOVE THE ID
|
||||||
id="gantt-container"
|
id="gantt-container"
|
||||||
className={cn("relative flex h-full w-full flex-1 overflow-hidden border-t border-custom-border-200", {
|
className={cn("relative h-full w-full flex flex-1 overflow-hidden border-t border-custom-border-200", {
|
||||||
"mb-8": bottomSpacing,
|
"mb-8": bottomSpacing,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div
|
<GanttChartSidebar
|
||||||
// DO NOT REMOVE THE ID
|
blocks={blocks}
|
||||||
id="gantt-sidebar"
|
blockUpdateHandler={blockUpdateHandler}
|
||||||
className="flex h-full w-1/4 flex-col border-r border-custom-border-200"
|
enableReorder={enableReorder}
|
||||||
>
|
sidebarToRender={sidebarToRender}
|
||||||
<div className="box-border flex h-[60px] flex-shrink-0 items-end justify-between gap-2 border-b border-custom-border-200 pb-2 pl-10 pr-4 text-sm font-medium text-custom-text-300">
|
title={title}
|
||||||
<h6>{title}</h6>
|
/>
|
||||||
<h6>Duration</h6>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
id="gantt-sidebar-scroll-container"
|
|
||||||
className="max-h-full mt-[12px] overflow-y-auto pl-2.5"
|
|
||||||
onScroll={onSidebarScroll}
|
|
||||||
ref={sidebarRef}
|
|
||||||
>
|
|
||||||
{sidebarToRender && sidebarToRender({ title, blockUpdateHandler, blocks, enableReorder })}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{currentView && (
|
|
||||||
<div
|
<div
|
||||||
// DO NOT REMOVE THE ID
|
// DO NOT REMOVE THE ID
|
||||||
id="scroll-container"
|
id="scroll-container"
|
||||||
className="relative flex h-full w-full flex-1 flex-col overflow-hidden overflow-x-auto horizontal-scroll-enable"
|
className="relative h-full w-full flex flex-col flex-1 overflow-hidden overflow-x-auto horizontal-scroll-enable"
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
>
|
>
|
||||||
<ActiveChartView />
|
<ActiveChartView />
|
||||||
{/* blocks */}
|
|
||||||
{currentViewData && (
|
{currentViewData && (
|
||||||
<GanttChartBlocksList
|
<GanttChartBlocksList
|
||||||
itemsContainerWidth={itemsContainerWidth}
|
itemsContainerWidth={itemsContainerWidth}
|
||||||
@ -133,7 +117,6 @@ export const GanttChartMainContent: React.FC<Props> = (props) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,7 @@ export const MonthChartView: FC<any> = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="absolute flex h-full flex-grow divide-x divide-custom-border-100/50">
|
<div className="absolute h-full flex flex-grow divide-x divide-custom-border-100/50">
|
||||||
{monthBlocks?.map((block, rootIndex) => (
|
{monthBlocks?.map((block, rootIndex) => (
|
||||||
<div key={`month-${block?.month}-${block?.year}`} className="relative flex flex-col">
|
<div key={`month-${block?.month}-${block?.year}`} className="relative flex flex-col">
|
||||||
<div className="h-[60px] w-full">
|
<div className="h-[60px] w-full">
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { useRouter } from "next/router";
|
|
||||||
import { DragDropContext, Draggable, DropResult, Droppable } from "@hello-pangea/dnd";
|
import { DragDropContext, Draggable, DropResult, Droppable } from "@hello-pangea/dnd";
|
||||||
import { MoreVertical } from "lucide-react";
|
import { MoreVertical } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
@ -20,12 +19,8 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CycleGanttSidebar: React.FC<Props> = (props) => {
|
export const CycleGanttSidebar: React.FC<Props> = (props) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const { blockUpdateHandler, blocks, enableReorder } = props;
|
||||||
const { title, blockUpdateHandler, blocks, enableReorder } = props;
|
// chart hook
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { cycleId } = router.query;
|
|
||||||
|
|
||||||
const { activeBlock, dispatch } = useChart();
|
const { activeBlock, dispatch } = useChart();
|
||||||
|
|
||||||
// update the active block on hover
|
// update the active block on hover
|
||||||
@ -85,7 +80,6 @@ export const CycleGanttSidebar: React.FC<Props> = (props) => {
|
|||||||
<Droppable droppableId="gantt-sidebar">
|
<Droppable droppableId="gantt-sidebar">
|
||||||
{(droppableProvided) => (
|
{(droppableProvided) => (
|
||||||
<div
|
<div
|
||||||
id={`gantt-sidebar-${cycleId}`}
|
|
||||||
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
||||||
ref={droppableProvided.innerRef}
|
ref={droppableProvided.innerRef}
|
||||||
{...droppableProvided.droppableProps}
|
{...droppableProvided.droppableProps}
|
@ -1,4 +1,5 @@
|
|||||||
export * from "./cycle-sidebar";
|
export * from "./cycles";
|
||||||
export * from "./module-sidebar";
|
export * from "./issues";
|
||||||
export * from "./sidebar";
|
export * from "./modules";
|
||||||
export * from "./project-view-sidebar";
|
export * from "./project-views";
|
||||||
|
export * from "./root";
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { observer } from "mobx-react";
|
||||||
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
|
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
|
||||||
import { MoreVertical } from "lucide-react";
|
import { MoreVertical } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
@ -30,7 +31,7 @@ type Props = {
|
|||||||
showAllBlocks?: boolean;
|
showAllBlocks?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IssueGanttSidebar: React.FC<Props> = (props) => {
|
export const IssueGanttSidebar: React.FC<Props> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
blockUpdateHandler,
|
blockUpdateHandler,
|
||||||
blocks,
|
blocks,
|
||||||
@ -101,7 +102,7 @@ export const IssueGanttSidebar: React.FC<Props> = (props) => {
|
|||||||
<DragDropContext onDragEnd={handleOrderChange}>
|
<DragDropContext onDragEnd={handleOrderChange}>
|
||||||
<Droppable droppableId="gantt-sidebar">
|
<Droppable droppableId="gantt-sidebar">
|
||||||
{(droppableProvided) => (
|
{(droppableProvided) => (
|
||||||
<div id={`gantt-sidebar-${viewId}`} ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}>
|
<div ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}>
|
||||||
<>
|
<>
|
||||||
{blocks ? (
|
{blocks ? (
|
||||||
blocks.map((block, index) => {
|
blocks.map((block, index) => {
|
||||||
@ -189,4 +190,4 @@ export const IssueGanttSidebar: React.FC<Props> = (props) => {
|
|||||||
</Droppable>
|
</Droppable>
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
);
|
);
|
||||||
};
|
});
|
@ -1,4 +1,3 @@
|
|||||||
import { useRouter } from "next/router";
|
|
||||||
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
|
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
|
||||||
import { MoreVertical } from "lucide-react";
|
import { MoreVertical } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
@ -20,12 +19,8 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ModuleGanttSidebar: React.FC<Props> = (props) => {
|
export const ModuleGanttSidebar: React.FC<Props> = (props) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const { blockUpdateHandler, blocks, enableReorder } = props;
|
||||||
const { title, blockUpdateHandler, blocks, enableReorder } = props;
|
// chart hook
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { cycleId } = router.query;
|
|
||||||
|
|
||||||
const { activeBlock, dispatch } = useChart();
|
const { activeBlock, dispatch } = useChart();
|
||||||
|
|
||||||
// update the active block on hover
|
// update the active block on hover
|
||||||
@ -85,7 +80,6 @@ export const ModuleGanttSidebar: React.FC<Props> = (props) => {
|
|||||||
<Droppable droppableId="gantt-sidebar">
|
<Droppable droppableId="gantt-sidebar">
|
||||||
{(droppableProvided) => (
|
{(droppableProvided) => (
|
||||||
<div
|
<div
|
||||||
id={`gantt-sidebar-${cycleId}`}
|
|
||||||
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
||||||
ref={droppableProvided.innerRef}
|
ref={droppableProvided.innerRef}
|
||||||
{...droppableProvided.droppableProps}
|
{...droppableProvided.droppableProps}
|
@ -1,4 +1,3 @@
|
|||||||
import { useRouter } from "next/router";
|
|
||||||
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
|
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
|
||||||
import { MoreVertical } from "lucide-react";
|
import { MoreVertical } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
@ -21,12 +20,8 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ProjectViewGanttSidebar: React.FC<Props> = (props) => {
|
export const ProjectViewGanttSidebar: React.FC<Props> = (props) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const { blockUpdateHandler, blocks, enableReorder } = props;
|
||||||
const { title, blockUpdateHandler, blocks, enableReorder } = props;
|
// chart hook
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { cycleId } = router.query;
|
|
||||||
|
|
||||||
const { activeBlock, dispatch } = useChart();
|
const { activeBlock, dispatch } = useChart();
|
||||||
|
|
||||||
// update the active block on hover
|
// update the active block on hover
|
||||||
@ -86,7 +81,6 @@ export const ProjectViewGanttSidebar: React.FC<Props> = (props) => {
|
|||||||
<Droppable droppableId="gantt-sidebar">
|
<Droppable droppableId="gantt-sidebar">
|
||||||
{(droppableProvided) => (
|
{(droppableProvided) => (
|
||||||
<div
|
<div
|
||||||
id={`gantt-sidebar-${cycleId}`}
|
|
||||||
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
||||||
ref={droppableProvided.innerRef}
|
ref={droppableProvided.innerRef}
|
||||||
{...droppableProvided.droppableProps}
|
{...droppableProvided.droppableProps}
|
43
web/components/gantt-chart/sidebar/root.tsx
Normal file
43
web/components/gantt-chart/sidebar/root.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { useRef } from "react";
|
||||||
|
// components
|
||||||
|
import { IBlockUpdateData, IGanttBlock, useChart } from "components/gantt-chart";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
blocks: IGanttBlock[] | null;
|
||||||
|
blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void;
|
||||||
|
enableReorder: boolean;
|
||||||
|
sidebarToRender: (props: any) => React.ReactNode;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GanttChartSidebar: React.FC<Props> = (props) => {
|
||||||
|
const { blocks, blockUpdateHandler, enableReorder, sidebarToRender, title } = props;
|
||||||
|
// refs
|
||||||
|
const sidebarRef = useRef<HTMLDivElement>(null);
|
||||||
|
// chart hook
|
||||||
|
const { updateScrollTop } = useChart();
|
||||||
|
|
||||||
|
const onSidebarScroll = (e: React.UIEvent<HTMLDivElement, UIEvent>) => updateScrollTop(e.currentTarget.scrollTop);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
// DO NOT REMOVE THE ID
|
||||||
|
id="gantt-sidebar"
|
||||||
|
className="flex h-full w-1/4 flex-col border-r border-custom-border-200"
|
||||||
|
>
|
||||||
|
<div className="box-border h-[60px] flex-shrink-0 flex items-end justify-between gap-2 border-b border-custom-border-200 pb-2 pl-10 pr-4 text-sm font-medium text-custom-text-300">
|
||||||
|
<h6>{title}</h6>
|
||||||
|
<h6>Duration</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="gantt-sidebar-scroll-container"
|
||||||
|
className="max-h-full mt-[12px] pl-2.5 overflow-hidden overflow-y-auto"
|
||||||
|
onScroll={onSidebarScroll}
|
||||||
|
ref={sidebarRef}
|
||||||
|
>
|
||||||
|
{sidebarToRender && sidebarToRender({ title, blockUpdateHandler, blocks, enableReorder })}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user