diff --git a/example/package-lock.json b/example/package-lock.json index e461fe2e..c96c3e18 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -104,9 +104,9 @@ } }, "@types/node": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", - "integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA==" + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz", + "integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==" }, "@types/testing-library__jest-dom": { "version": "5.9.5", @@ -179,9 +179,9 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "core-js-pure": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.11.1.tgz", - "integrity": "sha512-2JukQi8HgAOCD5CSimxWWXVrUBoA9Br796uIA5Z06bIjt7PBBI19ircFaAxplgE1mJf3x2BY6MkT/HWA/UryPg==" + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz", + "integrity": "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ==" }, "css": { "version": "3.0.0", @@ -395,9 +395,9 @@ } }, "@testing-library/dom": { - "version": "7.30.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.30.4.tgz", - "integrity": "sha512-GObDVMaI4ARrZEXaRy4moolNAxWPKvEYNV/fa6Uc2eAzR/t4otS6A7EhrntPBIQLeehL9DbVhscvvv7gd6hWqA==", + "version": "7.31.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.0.tgz", + "integrity": "sha512-0X7ACg4YvTRDFMIuTOEj6B4NpN7i3F/4j5igOcTI5NC5J+N4TribNdErCHOZF1LBWhhcyfwxelVwvoYNMUXTOA==", "requires": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -436,9 +436,9 @@ } }, "@types/node": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", - "integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA==" + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz", + "integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==" }, "@types/yargs": { "version": "15.0.13", @@ -534,9 +534,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "core-js-pure": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.11.1.tgz", - "integrity": "sha512-2JukQi8HgAOCD5CSimxWWXVrUBoA9Br796uIA5Z06bIjt7PBBI19ircFaAxplgE1mJf3x2BY6MkT/HWA/UryPg==" + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz", + "integrity": "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ==" }, "dom-accessibility-api": { "version": "0.5.4", @@ -681,9 +681,9 @@ } }, "@types/node": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", - "integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA==" + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz", + "integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==" }, "@types/yargs": { "version": "15.0.13", @@ -828,9 +828,9 @@ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, "@types/react": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz", - "integrity": "sha512-onz2BqScSFMoTRdJUZUDD/7xrusM8hBA2Fktk2qgaTYPCgPvWnDEgkrOs8hhPUf2jfcIXkJ5yK6VfYormJS3Jw==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.5.tgz", + "integrity": "sha512-bj4biDB9ZJmGAYTWSKJly6bMr4BLUiBrx9ujiJEoP9XIDY9CTaPGxE5QWN/1WjpPLzYF7/jRNnV2nNxNe970sw==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", diff --git a/package.json b/package.json index 04efde96..0fd75b3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gantt-task-react", - "version": "0.3.1", + "version": "0.3.2", "description": "Interactive Gantt Chart for React with TypeScript.", "author": "MaTeMaTuK ", "homepage": "https://github.com/MaTeMaTuK/gantt-task-react", diff --git a/src/components/gantt/gantt.module.css b/src/components/gantt/gantt.module.css index 0531ffc1..8169a19c 100644 --- a/src/components/gantt/gantt.module.css +++ b/src/components/gantt/gantt.module.css @@ -17,4 +17,5 @@ margin: 0; list-style: none; outline: none; + position: relative; } diff --git a/src/components/gantt/gantt.tsx b/src/components/gantt/gantt.tsx index d5865f9f..affe8baf 100644 --- a/src/components/gantt/gantt.tsx +++ b/src/components/gantt/gantt.tsx @@ -6,7 +6,7 @@ import { CalendarProps } from "../calendar/calendar"; import { TaskGanttContentProps } from "./task-gantt-content"; import { TaskListHeaderDefault } from "../task-list/task-list-header"; import { TaskListTableDefault } from "../task-list/task-list-table"; -import { StandardTooltipContent } from "../other/tooltip"; +import { StandardTooltipContent, Tooltip } from "../other/tooltip"; import { VerticalScroll } from "../other/vertical-scroll"; import { TaskListProps, TaskList } from "../task-list/task-list"; import { TaskGantt } from "./task-gantt"; @@ -56,7 +56,6 @@ export const Gantt: React.FunctionComponent = ({ }) => { const wrapperRef = useRef(null); const taskListRef = useRef(null); - const verticalGanttContainerRef = useRef(null); const [dateSetup, setDateSetup] = useState(() => { const [startDate, endDate] = ganttDateRange(tasks, viewMode); return { viewMode, dates: seedDates(startDate, endDate, viewMode) }; @@ -64,6 +63,8 @@ export const Gantt: React.FunctionComponent = ({ const [taskHeight, setTaskHeight] = useState((rowHeight * barFill) / 100); const [taskListWidth, setTaskListWidth] = useState(0); + const [svgContainerWidth, setSvgContainerWidth] = useState(0); + const [svgContainerHeight, setSvgContainerHeight] = useState(ganttHeight); const [barTasks, setBarTasks] = useState([]); const [ganttEvent, setGanttEvent] = useState({ action: "", @@ -76,7 +77,6 @@ export const Gantt: React.FunctionComponent = ({ const [scrollX, setScrollX] = useState(0); const [ignoreScrollEvent, setIgnoreScrollEvent] = useState(false); - const svgHeight = rowHeight * barTasks.length; const svgWidth = dateSetup.dates.length * columnWidth; const ganttFullHeight = barTasks.length * rowHeight; @@ -170,10 +170,27 @@ export const Gantt: React.FunctionComponent = ({ }, [rowHeight, barFill, taskHeight]); useEffect(() => { + if (!listCellWidth) { + setTaskListWidth(0); + } if (taskListRef.current) { setTaskListWidth(taskListRef.current.offsetWidth); } - }, [taskListRef]); + }, [taskListRef, listCellWidth]); + + useEffect(() => { + if (wrapperRef.current) { + setSvgContainerWidth(wrapperRef.current.offsetWidth - taskListWidth); + } + }, [wrapperRef, taskListWidth]); + + useEffect(() => { + if (ganttHeight) { + setSvgContainerHeight(ganttHeight + headerHeight); + } else { + setSvgContainerHeight(tasks.length * rowHeight + headerHeight); + } + }, [ganttHeight, tasks]); // scroll events useEffect(() => { @@ -326,7 +343,6 @@ export const Gantt: React.FunctionComponent = ({ fontFamily, fontSize, arrowIndent, - svgHeight, svgWidth, setGanttEvent, setFailedTask, @@ -335,7 +351,6 @@ export const Gantt: React.FunctionComponent = ({ onProgressChange, onDoubleClick, onDelete, - TooltipContent, }; const tableProps: TaskListProps = { @@ -371,8 +386,23 @@ export const Gantt: React.FunctionComponent = ({ ganttHeight={ganttHeight} scrollY={scrollY} scrollX={scrollX} - verticalGanttContainerRef={verticalGanttContainerRef} /> + {ganttEvent.changedTask && ( + + )} ; - svgHeight: number; svgWidth: number; - displayXStartEndpoint?: { - start: number; - end: number; - }; taskHeight: number; arrowColor: string; arrowIndent: number; @@ -35,11 +29,6 @@ export type TaskGanttContentProps = { setGanttEvent: (value: GanttEvent) => void; setFailedTask: (value: BarTask | null) => void; setSelectedTask: (taskId: string) => void; - TooltipContent: React.FC<{ - task: Task; - fontSize: string; - fontFamily: string; - }>; } & EventOption; export const TaskGanttContent: React.FC = ({ @@ -51,8 +40,6 @@ export const TaskGanttContent: React.FC = ({ columnWidth, timeStep, svg, - svgHeight, - displayXStartEndpoint, taskHeight, arrowColor, arrowIndent, @@ -65,7 +52,6 @@ export const TaskGanttContent: React.FC = ({ onProgressChange, onDoubleClick, onDelete, - TooltipContent, }) => { const point = svg?.current?.createSVGPoint(); const [xStep, setXStep] = useState(0); @@ -292,20 +278,6 @@ export const TaskGanttContent: React.FC = ({ ); })} - - {ganttEvent.changedTask && displayXStartEndpoint && ( - - )} - ); }; diff --git a/src/components/gantt/task-gantt.tsx b/src/components/gantt/task-gantt.tsx index 64f69437..7f9bc57c 100644 --- a/src/components/gantt/task-gantt.tsx +++ b/src/components/gantt/task-gantt.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useEffect, useState } from "react"; +import React, { useRef, useEffect } from "react"; import { GridProps, Grid } from "../grid/grid"; import { CalendarProps, Calendar } from "../calendar/calendar"; import { TaskGanttContentProps, TaskGanttContent } from "./task-gantt-content"; @@ -11,7 +11,6 @@ export type TaskGanttProps = { ganttHeight: number; scrollY: number; scrollX: number; - verticalGanttContainerRef: React.RefObject; }; export const TaskGantt: React.FC = ({ gridProps, @@ -20,15 +19,11 @@ export const TaskGantt: React.FC = ({ ganttHeight, scrollY, scrollX, - verticalGanttContainerRef, }) => { const ganttSVGRef = useRef(null); const horizontalContainerRef = useRef(null); - const [displayXStartEndpoint, setDisplayXStartEndpoint] = useState({ - start: 0, - end: 0, - }); - const newBarProps = { ...barProps, svg: ganttSVGRef, displayXStartEndpoint }; + const verticalGanttContainerRef = useRef(null); + const newBarProps = { ...barProps, svg: ganttSVGRef }; useEffect(() => { if (horizontalContainerRef.current) { @@ -39,13 +34,8 @@ export const TaskGantt: React.FC = ({ useEffect(() => { if (verticalGanttContainerRef.current) { verticalGanttContainerRef.current.scrollLeft = scrollX; - setDisplayXStartEndpoint({ - start: scrollX, - end: verticalGanttContainerRef.current.clientWidth + scrollX, - }); } - // verticalContainerRef.current?.clientWidth need for resize window tracking - }, [scrollX, verticalGanttContainerRef.current?.clientWidth]); + }, [scrollX]); return (
= ({ task, rowHeight, - svgHeight, - displayXStartEndpoint, + svgContainerHeight, + svgContainerWidth, + scrollX, + scrollY, arrowIndent, fontSize, fontFamily, + headerHeight, + taskListWidth, TooltipContent, }) => { const tooltipRef = useRef(null); - const [toolWidth, setToolWidth] = useState(1000); - const [toolHeight, setToolHeight] = useState(1000); - const [relatedY, setRelatedY] = useState(task.index * rowHeight); - const [relatedX, setRelatedX] = useState(displayXStartEndpoint.end); + const [relatedY, setRelatedY] = useState(0); + const [relatedX, setRelatedX] = useState(0); useEffect(() => { if (tooltipRef.current) { + let newRelatedX = + task.x2 + arrowIndent + arrowIndent * 0.5 + taskListWidth - scrollX; + let newRelatedY = task.index * rowHeight - scrollY + headerHeight; + const tooltipHeight = tooltipRef.current.offsetHeight * 1.1; - let tooltipY = task.index * rowHeight; - const newWidth = tooltipRef.current.scrollWidth * 1.1; - let newRelatedX = task.x2 + arrowIndent + arrowIndent * 0.5; - if (newWidth + newRelatedX > displayXStartEndpoint.end) { - newRelatedX = task.x1 - arrowIndent - arrowIndent * 0.5 - newWidth; - } - const tooltipLowerPoint = tooltipHeight + tooltipY; + const tooltipWidth = tooltipRef.current.offsetWidth * 1.1; - if ( - newRelatedX < displayXStartEndpoint.start && - tooltipLowerPoint > svgHeight - ) { - tooltipY -= tooltipHeight; - newRelatedX = (task.x1 + task.x2 - newWidth) * 0.5; - if (newRelatedX + newWidth > displayXStartEndpoint.end) { - newRelatedX = displayXStartEndpoint.end - newWidth; - } - if ( - newRelatedX + newWidth > displayXStartEndpoint.end || - newRelatedX - newWidth < displayXStartEndpoint.start - ) { - newRelatedX = displayXStartEndpoint.end - newWidth; - } - } else if ( - newRelatedX < displayXStartEndpoint.start && - tooltipLowerPoint < svgHeight - ) { - tooltipY += rowHeight; - newRelatedX = (task.x1 + task.x2 - newWidth) * 0.5; - if ( - newRelatedX + newWidth > displayXStartEndpoint.end || - newRelatedX - newWidth < displayXStartEndpoint.start - ) { - newRelatedX = displayXStartEndpoint.end - newWidth; - } - } else if (tooltipLowerPoint > svgHeight) { - tooltipY = svgHeight - tooltipHeight; - } + const tooltipLowerPoint = tooltipHeight + newRelatedY - scrollY; + const tooltipLeftmostPoint = tooltipWidth + newRelatedX; + const fullChartWidth = taskListWidth + svgContainerWidth; - setRelatedY(tooltipY); - setToolWidth(newWidth); - setRelatedX(newRelatedX); - if (tooltipHeight !== 1000) { - setToolHeight(tooltipHeight); + if (tooltipLeftmostPoint > fullChartWidth) { + newRelatedX = + task.x1 + + taskListWidth - + arrowIndent - + arrowIndent * 0.5 - + scrollX - + tooltipWidth; } + if (newRelatedX < taskListWidth) { + newRelatedX = svgContainerWidth + taskListWidth - tooltipWidth; + newRelatedY += rowHeight; + } else if (tooltipLowerPoint > svgContainerHeight - scrollY) { + newRelatedY = svgContainerHeight - tooltipHeight; + } + setRelatedY(newRelatedY); + setRelatedX(newRelatedX); } - }, [tooltipRef, task, arrowIndent, displayXStartEndpoint]); + }, [ + tooltipRef.current, + task, + arrowIndent, + scrollX, + scrollY, + headerHeight, + taskListWidth, + rowHeight, + svgContainerHeight, + svgContainerWidth, + ]); + return ( - -
- -
-
+ +
); };