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
|
||||
import { useIssueDetail } from "hooks/store";
|
||||
import { useChart } from "../hooks";
|
||||
@ -20,7 +21,7 @@ export type GanttChartBlocksProps = {
|
||||
showAllBlocks: boolean;
|
||||
};
|
||||
|
||||
export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
||||
export const GanttChartBlocksList: FC<GanttChartBlocksProps> = observer((props) => {
|
||||
const {
|
||||
itemsContainerWidth,
|
||||
blocks,
|
||||
@ -31,12 +32,10 @@ export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
||||
enableBlockMove,
|
||||
showAllBlocks,
|
||||
} = props;
|
||||
// refs
|
||||
const blocksContainerRef = useRef<HTMLDivElement>(null);
|
||||
// store hooks
|
||||
const { peekIssue } = useIssueDetail();
|
||||
// chart hook
|
||||
const { activeBlock, dispatch, scrollTop, updateScrollTop } = useChart();
|
||||
const { activeBlock, dispatch, updateScrollTop } = useChart();
|
||||
|
||||
// update the active block on hover
|
||||
const updateActiveBlock = (block: IGanttBlock | null) => {
|
||||
@ -87,54 +86,44 @@ export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
||||
sidebarScrollContainer.scrollTop = e.currentTarget.scrollTop;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const blocksContainer = blocksContainerRef.current;
|
||||
if (!blocksContainer) return;
|
||||
|
||||
blocksContainer.scrollTop = scrollTop;
|
||||
}, [scrollTop]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={blocksContainerRef}
|
||||
className="relative z-[5] mt-[72px] h-full overflow-hidden overflow-y-auto"
|
||||
style={{ width: `${itemsContainerWidth}px` }}
|
||||
onScroll={handleBlocksScroll}
|
||||
>
|
||||
{blocks &&
|
||||
blocks.length > 0 &&
|
||||
blocks.map((block) => {
|
||||
// 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;
|
||||
{blocks?.map((block) => {
|
||||
// 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;
|
||||
|
||||
const isBlockVisibleOnChart = block.start_date && block.target_date;
|
||||
const isBlockVisibleOnChart = block.start_date && block.target_date;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`block-${block.id}`}
|
||||
className={cn("relative h-11", {
|
||||
"rounded bg-custom-background-80": activeBlock?.id === block.id,
|
||||
"rounded-l border border-r-0 border-custom-primary-70 hover:border-custom-primary-70":
|
||||
peekIssue?.issueId === block.data.id,
|
||||
})}
|
||||
onMouseEnter={() => updateActiveBlock(block)}
|
||||
onMouseLeave={() => updateActiveBlock(null)}
|
||||
>
|
||||
{isBlockVisibleOnChart ? (
|
||||
<ChartDraggable
|
||||
block={block}
|
||||
blockToRender={blockToRender}
|
||||
handleBlock={(...args) => handleChartBlockPosition(block, ...args)}
|
||||
enableBlockLeftResize={enableBlockLeftResize}
|
||||
enableBlockRightResize={enableBlockRightResize}
|
||||
enableBlockMove={enableBlockMove}
|
||||
/>
|
||||
) : (
|
||||
<ChartAddBlock block={block} blockUpdateHandler={blockUpdateHandler} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<div
|
||||
key={`block-${block.id}`}
|
||||
className={cn("relative h-11", {
|
||||
"rounded bg-custom-background-80": activeBlock?.id === block.id,
|
||||
"rounded-l border border-r-0 border-custom-primary-70 hover:border-custom-primary-70":
|
||||
peekIssue?.issueId === block.data.id,
|
||||
})}
|
||||
onMouseEnter={() => updateActiveBlock(block)}
|
||||
onMouseLeave={() => updateActiveBlock(null)}
|
||||
>
|
||||
{isBlockVisibleOnChart ? (
|
||||
<ChartDraggable
|
||||
block={block}
|
||||
blockToRender={blockToRender}
|
||||
handleBlock={(...args) => handleChartBlockPosition(block, ...args)}
|
||||
enableBlockLeftResize={enableBlockLeftResize}
|
||||
enableBlockRightResize={enableBlockRightResize}
|
||||
enableBlockMove={enableBlockMove}
|
||||
/>
|
||||
) : (
|
||||
<ChartAddBlock block={block} blockUpdateHandler={blockUpdateHandler} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { useRef } from "react";
|
||||
// components
|
||||
import {
|
||||
BiWeekChartView,
|
||||
DayChartView,
|
||||
GanttChartBlocksList,
|
||||
GanttChartSidebar,
|
||||
HourChartView,
|
||||
IBlockUpdateData,
|
||||
IGanttBlock,
|
||||
@ -51,16 +51,14 @@ export const GanttChartMainContent: React.FC<Props> = (props) => {
|
||||
title,
|
||||
updateCurrentViewRenderPayload,
|
||||
} = props;
|
||||
// refs
|
||||
const sidebarRef = useRef<HTMLDivElement>(null);
|
||||
// chart hook
|
||||
const { currentView, currentViewData, updateScrollLeft, updateScrollTop } = useChart();
|
||||
|
||||
// handling scroll functionality
|
||||
const onScroll = (e: React.UIEvent<HTMLDivElement, UIEvent>) => {
|
||||
const { clientWidth: clientVisibleWidth, scrollLeft: currentLeftScrollPosition, scrollWidth } = e.currentTarget;
|
||||
|
||||
updateScrollLeft(currentLeftScrollPosition);
|
||||
updateScrollTop(e.currentTarget.scrollTop);
|
||||
|
||||
const approxRangeLeft = scrollWidth >= clientVisibleWidth + 1000 ? 1000 : scrollWidth - clientVisibleWidth;
|
||||
const approxRangeRight = scrollWidth - (approxRangeLeft + clientVisibleWidth);
|
||||
@ -69,8 +67,6 @@ export const GanttChartMainContent: React.FC<Props> = (props) => {
|
||||
if (currentLeftScrollPosition <= approxRangeLeft) updateCurrentViewRenderPayload("left", currentView);
|
||||
};
|
||||
|
||||
const onSidebarScroll = (e: React.UIEvent<HTMLDivElement, UIEvent>) => updateScrollTop(e.currentTarget.scrollTop);
|
||||
|
||||
const CHART_VIEW_COMPONENTS: {
|
||||
[key in TGanttViews]: React.FC;
|
||||
} = {
|
||||
@ -82,58 +78,45 @@ export const GanttChartMainContent: React.FC<Props> = (props) => {
|
||||
quarter: QuarterChartView,
|
||||
year: YearChartView,
|
||||
};
|
||||
|
||||
if (!currentView) return null;
|
||||
const ActiveChartView = CHART_VIEW_COMPONENTS[currentView];
|
||||
|
||||
return (
|
||||
<div
|
||||
// DO NOT REMOVE THE ID
|
||||
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,
|
||||
})}
|
||||
>
|
||||
<GanttChartSidebar
|
||||
blocks={blocks}
|
||||
blockUpdateHandler={blockUpdateHandler}
|
||||
enableReorder={enableReorder}
|
||||
sidebarToRender={sidebarToRender}
|
||||
title={title}
|
||||
/>
|
||||
<div
|
||||
// DO NOT REMOVE THE ID
|
||||
id="gantt-sidebar"
|
||||
className="flex h-full w-1/4 flex-col border-r border-custom-border-200"
|
||||
id="scroll-container"
|
||||
className="relative h-full w-full flex flex-col flex-1 overflow-hidden overflow-x-auto horizontal-scroll-enable"
|
||||
onScroll={onScroll}
|
||||
>
|
||||
<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">
|
||||
<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>
|
||||
<ActiveChartView />
|
||||
{currentViewData && (
|
||||
<GanttChartBlocksList
|
||||
itemsContainerWidth={itemsContainerWidth}
|
||||
blocks={chartBlocks}
|
||||
blockToRender={blockToRender}
|
||||
blockUpdateHandler={blockUpdateHandler}
|
||||
enableBlockLeftResize={enableBlockLeftResize}
|
||||
enableBlockRightResize={enableBlockRightResize}
|
||||
enableBlockMove={enableBlockMove}
|
||||
showAllBlocks={showAllBlocks}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{currentView && (
|
||||
<div
|
||||
// DO NOT REMOVE THE ID
|
||||
id="scroll-container"
|
||||
className="relative flex h-full w-full flex-1 flex-col overflow-hidden overflow-x-auto horizontal-scroll-enable"
|
||||
onScroll={onScroll}
|
||||
>
|
||||
<ActiveChartView />
|
||||
{/* blocks */}
|
||||
{currentViewData && (
|
||||
<GanttChartBlocksList
|
||||
itemsContainerWidth={itemsContainerWidth}
|
||||
blocks={chartBlocks}
|
||||
blockToRender={blockToRender}
|
||||
blockUpdateHandler={blockUpdateHandler}
|
||||
enableBlockLeftResize={enableBlockLeftResize}
|
||||
enableBlockRightResize={enableBlockRightResize}
|
||||
enableBlockMove={enableBlockMove}
|
||||
showAllBlocks={showAllBlocks}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ export const MonthChartView: FC<any> = () => {
|
||||
|
||||
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) => (
|
||||
<div key={`month-${block?.month}-${block?.year}`} className="relative flex flex-col">
|
||||
<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 { MoreVertical } from "lucide-react";
|
||||
// hooks
|
||||
@ -20,12 +19,8 @@ type Props = {
|
||||
};
|
||||
|
||||
export const CycleGanttSidebar: React.FC<Props> = (props) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { title, blockUpdateHandler, blocks, enableReorder } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { cycleId } = router.query;
|
||||
|
||||
const { blockUpdateHandler, blocks, enableReorder } = props;
|
||||
// chart hook
|
||||
const { activeBlock, dispatch } = useChart();
|
||||
|
||||
// update the active block on hover
|
||||
@ -85,7 +80,6 @@ export const CycleGanttSidebar: React.FC<Props> = (props) => {
|
||||
<Droppable droppableId="gantt-sidebar">
|
||||
{(droppableProvided) => (
|
||||
<div
|
||||
id={`gantt-sidebar-${cycleId}`}
|
||||
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
||||
ref={droppableProvided.innerRef}
|
||||
{...droppableProvided.droppableProps}
|
@ -1,4 +1,5 @@
|
||||
export * from "./cycle-sidebar";
|
||||
export * from "./module-sidebar";
|
||||
export * from "./sidebar";
|
||||
export * from "./project-view-sidebar";
|
||||
export * from "./cycles";
|
||||
export * from "./issues";
|
||||
export * from "./modules";
|
||||
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 { MoreVertical } from "lucide-react";
|
||||
// hooks
|
||||
@ -30,7 +31,7 @@ type Props = {
|
||||
showAllBlocks?: boolean;
|
||||
};
|
||||
|
||||
export const IssueGanttSidebar: React.FC<Props> = (props) => {
|
||||
export const IssueGanttSidebar: React.FC<Props> = observer((props) => {
|
||||
const {
|
||||
blockUpdateHandler,
|
||||
blocks,
|
||||
@ -101,7 +102,7 @@ export const IssueGanttSidebar: React.FC<Props> = (props) => {
|
||||
<DragDropContext onDragEnd={handleOrderChange}>
|
||||
<Droppable droppableId="gantt-sidebar">
|
||||
{(droppableProvided) => (
|
||||
<div id={`gantt-sidebar-${viewId}`} ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}>
|
||||
<div ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}>
|
||||
<>
|
||||
{blocks ? (
|
||||
blocks.map((block, index) => {
|
||||
@ -189,4 +190,4 @@ export const IssueGanttSidebar: React.FC<Props> = (props) => {
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
);
|
||||
};
|
||||
});
|
@ -1,4 +1,3 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
|
||||
import { MoreVertical } from "lucide-react";
|
||||
// hooks
|
||||
@ -20,12 +19,8 @@ type Props = {
|
||||
};
|
||||
|
||||
export const ModuleGanttSidebar: React.FC<Props> = (props) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { title, blockUpdateHandler, blocks, enableReorder } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { cycleId } = router.query;
|
||||
|
||||
const { blockUpdateHandler, blocks, enableReorder } = props;
|
||||
// chart hook
|
||||
const { activeBlock, dispatch } = useChart();
|
||||
|
||||
// update the active block on hover
|
||||
@ -85,7 +80,6 @@ export const ModuleGanttSidebar: React.FC<Props> = (props) => {
|
||||
<Droppable droppableId="gantt-sidebar">
|
||||
{(droppableProvided) => (
|
||||
<div
|
||||
id={`gantt-sidebar-${cycleId}`}
|
||||
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
||||
ref={droppableProvided.innerRef}
|
||||
{...droppableProvided.droppableProps}
|
@ -1,4 +1,3 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
|
||||
import { MoreVertical } from "lucide-react";
|
||||
// hooks
|
||||
@ -21,12 +20,8 @@ type Props = {
|
||||
};
|
||||
|
||||
export const ProjectViewGanttSidebar: React.FC<Props> = (props) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { title, blockUpdateHandler, blocks, enableReorder } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { cycleId } = router.query;
|
||||
|
||||
const { blockUpdateHandler, blocks, enableReorder } = props;
|
||||
// chart hook
|
||||
const { activeBlock, dispatch } = useChart();
|
||||
|
||||
// update the active block on hover
|
||||
@ -86,7 +81,6 @@ export const ProjectViewGanttSidebar: React.FC<Props> = (props) => {
|
||||
<Droppable droppableId="gantt-sidebar">
|
||||
{(droppableProvided) => (
|
||||
<div
|
||||
id={`gantt-sidebar-${cycleId}`}
|
||||
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
||||
ref={droppableProvided.innerRef}
|
||||
{...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