diff --git a/src/frontend/src/pages/GanttPage/GanttChart/GanttChartComponents/GanttTaskBar/GanttTaskBarEditView.tsx b/src/frontend/src/pages/GanttPage/GanttChart/GanttChartComponents/GanttTaskBar/GanttTaskBarEditView.tsx index 75284bc6ba..7174640aae 100644 --- a/src/frontend/src/pages/GanttPage/GanttChart/GanttChartComponents/GanttTaskBar/GanttTaskBarEditView.tsx +++ b/src/frontend/src/pages/GanttPage/GanttChart/GanttChartComponents/GanttTaskBar/GanttTaskBarEditView.tsx @@ -1,6 +1,6 @@ import { Box, Chip, Typography } from '@mui/material'; import { useTheme } from '@mui/system'; -import { CSSProperties, DragEvent, MouseEvent, useEffect, useState } from 'react'; +import { CSSProperties, DragEvent, MouseEvent, useCallback, useEffect, useRef, useState } from 'react'; import useMeasure from 'react-use-measure'; import { addDaysToDate } from 'shared'; import { GanttChange, GanttTask, GANTT_CHART_CELL_SIZE } from '../../../../../utils/gantt.utils'; @@ -33,18 +33,20 @@ export const GanttTaskBarEditView = ({ onAddTaskPressed }: GanttTaskBarEditProps) => { const theme = useTheme(); - const [startX, setStartX] = useState(null); const [showDropPoints, setShowDropPoints] = useState(false); const [isResizing, setIsResizing] = useState(false); - const [width, setWidth] = useState(0); // current width of component, will change on resize + const [width, setWidth] = useState(0); + const [correctWidth, setCorrectWidth] = useState(0); const [measureRef, bounds] = useMeasure(); + const hasMeasuredRef = useRef(false); + const boxRef = useRef(null); const widthPerDay = 7.2; //width per day to use for resizing calculations, kind of arbitrary, const taskBarDisplayStyles: CSSProperties = { gridColumnStart: getStartCol(task.start), gridColumnEnd: getEndCol(task.end), height: '2rem', - width: task.root ? 'unset' : width === 0 ? `unset` : `${width}px`, + width: task.root ? 'unset' : correctWidth > 0 ? `${correctWidth}px` : 'auto', border: `1px solid ${isResizing ? theme.palette.text.primary : theme.palette.divider}`, borderRadius: '0.25rem', backgroundColor: task.styles ? task.styles.backgroundColor : theme.palette.background.paper, @@ -67,48 +69,54 @@ export const GanttTaskBarEditView = ({ right: '-10' }; + const getCorrectWidth = useCallback((rawWidth: number) => { + const newEventLengthInDays = roundToMultipleOf7(rawWidth / widthPerDay); + const displayWeeks = newEventLengthInDays / 7 + 1; + return displayWeeks * 40 + (displayWeeks - 1) * 10; + }, []); + useEffect(() => { - if (bounds.width !== 0 && width === 0) { + if (!hasMeasuredRef.current && bounds.width > 0) { setWidth(bounds.width); + setCorrectWidth(getCorrectWidth(bounds.width)); + hasMeasuredRef.current = true; } - }, [bounds, width]); + }, [bounds.width, getCorrectWidth]); // used to make sure that any changes to the start and end dates are made in multiples of 7 const roundToMultipleOf7 = (num: number) => { - return Math.round(num / 7) * 7; + return Math.ceil(num / 7) * 7; + }; + + const getDistanceFromLeft = (clientX: number) => { + const rect = boxRef.current!.getBoundingClientRect(); + return clientX - rect.left; }; const handleMouseDown = (e: MouseEvent) => { setIsResizing(true); - setStartX(e.clientX); + boxRef.current = (e.currentTarget as HTMLElement).closest('[data-gantt-bar]') as HTMLDivElement; }; const handleMouseMove = (e: MouseEvent) => { - if (isResizing) { - const currentX = e.clientX; - const deltaX = currentX - startX!; - setWidth(Math.max(100, width + deltaX)); - setStartX(currentX); - } + if (!isResizing) return; + + const newWidth = Math.max(100, getDistanceFromLeft(e.clientX)); + + setWidth(newWidth); // sync render + setCorrectWidth(getCorrectWidth(newWidth)); }; const handleMouseUp = () => { if (isResizing) { setIsResizing(false); - // Use change in width to calculate new length const newEventLengthInDays = roundToMultipleOf7(width / widthPerDay); - // The gantt chart tasks are inclusive (their width includes the full width of their start and end date) - const displayWeeks = newEventLengthInDays / 7 + 1; - // We need these magic pixel numbers to dynamically calculate the correct width of the task to keep it in sync with the stored end date - const correctWidth = displayWeeks * 38 + (displayWeeks - 1) * 10; - const newEndDate = addDaysToDate(task.start, newEventLengthInDays); - setWidth(correctWidth); createChange({ id: uuidv4(), element: task.element, type: 'change-end-date', originalEnd: task.end, - newEnd: newEndDate + newEnd: addDaysToDate(task.start, newEventLengthInDays) }); } }; @@ -156,7 +164,7 @@ export const GanttTaskBarEditView = ({ }; })} > -
+