refactor: gantt sidebar

This commit is contained in:
Aaryan Khandelwal 2024-02-05 14:55:58 +05:30
parent 4943eb397e
commit 38f6c03298
9 changed files with 122 additions and 123 deletions

View File

@ -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,54 +86,44 @@ 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 && // hide the block if it doesn't have start and target dates and showAllBlocks is false
blocks.map((block) => { if (!showAllBlocks && !(block.start_date && block.target_date)) return;
// 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 ( return (
<div <div
key={`block-${block.id}`} key={`block-${block.id}`}
className={cn("relative h-11", { className={cn("relative h-11", {
"rounded bg-custom-background-80": activeBlock?.id === block.id, "rounded bg-custom-background-80": activeBlock?.id === block.id,
"rounded-l border border-r-0 border-custom-primary-70 hover:border-custom-primary-70": "rounded-l border border-r-0 border-custom-primary-70 hover:border-custom-primary-70":
peekIssue?.issueId === block.data.id, peekIssue?.issueId === block.data.id,
})} })}
onMouseEnter={() => updateActiveBlock(block)} onMouseEnter={() => updateActiveBlock(block)}
onMouseLeave={() => updateActiveBlock(null)} onMouseLeave={() => updateActiveBlock(null)}
> >
{isBlockVisibleOnChart ? ( {isBlockVisibleOnChart ? (
<ChartDraggable <ChartDraggable
block={block} block={block}
blockToRender={blockToRender} blockToRender={blockToRender}
handleBlock={(...args) => handleChartBlockPosition(block, ...args)} handleBlock={(...args) => handleChartBlockPosition(block, ...args)}
enableBlockLeftResize={enableBlockLeftResize} enableBlockLeftResize={enableBlockLeftResize}
enableBlockRightResize={enableBlockRightResize} enableBlockRightResize={enableBlockRightResize}
enableBlockMove={enableBlockMove} enableBlockMove={enableBlockMove}
/> />
) : ( ) : (
<ChartAddBlock block={block} blockUpdateHandler={blockUpdateHandler} /> <ChartAddBlock block={block} blockUpdateHandler={blockUpdateHandler} />
)} )}
</div> </div>
); );
})} })}
</div> </div>
); );
}; });

View File

@ -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,58 +78,45 @@ 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,
})} })}
> >
<GanttChartSidebar
blocks={blocks}
blockUpdateHandler={blockUpdateHandler}
enableReorder={enableReorder}
sidebarToRender={sidebarToRender}
title={title}
/>
<div <div
// DO NOT REMOVE THE ID // DO NOT REMOVE THE ID
id="gantt-sidebar" id="scroll-container"
className="flex h-full w-1/4 flex-col border-r border-custom-border-200" 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"> <ActiveChartView />
<h6>{title}</h6> {currentViewData && (
<h6>Duration</h6> <GanttChartBlocksList
</div> itemsContainerWidth={itemsContainerWidth}
blocks={chartBlocks}
<div blockToRender={blockToRender}
id="gantt-sidebar-scroll-container" blockUpdateHandler={blockUpdateHandler}
className="max-h-full mt-[12px] overflow-y-auto pl-2.5" enableBlockLeftResize={enableBlockLeftResize}
onScroll={onSidebarScroll} enableBlockRightResize={enableBlockRightResize}
ref={sidebarRef} enableBlockMove={enableBlockMove}
> showAllBlocks={showAllBlocks}
{sidebarToRender && sidebarToRender({ title, blockUpdateHandler, blocks, enableReorder })} />
</div> )}
</div> </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> </div>
); );
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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