commit
ad2f242a18
42
example/package-lock.json
generated
42
example/package-lock.json
generated
@ -104,9 +104,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "15.0.1",
|
"version": "15.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz",
|
||||||
"integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA=="
|
"integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ=="
|
||||||
},
|
},
|
||||||
"@types/testing-library__jest-dom": {
|
"@types/testing-library__jest-dom": {
|
||||||
"version": "5.9.5",
|
"version": "5.9.5",
|
||||||
@ -179,9 +179,9 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
},
|
},
|
||||||
"core-js-pure": {
|
"core-js-pure": {
|
||||||
"version": "3.11.1",
|
"version": "3.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz",
|
||||||
"integrity": "sha512-2JukQi8HgAOCD5CSimxWWXVrUBoA9Br796uIA5Z06bIjt7PBBI19ircFaAxplgE1mJf3x2BY6MkT/HWA/UryPg=="
|
"integrity": "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ=="
|
||||||
},
|
},
|
||||||
"css": {
|
"css": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@ -395,9 +395,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@testing-library/dom": {
|
"@testing-library/dom": {
|
||||||
"version": "7.30.4",
|
"version": "7.31.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.30.4.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.0.tgz",
|
||||||
"integrity": "sha512-GObDVMaI4ARrZEXaRy4moolNAxWPKvEYNV/fa6Uc2eAzR/t4otS6A7EhrntPBIQLeehL9DbVhscvvv7gd6hWqA==",
|
"integrity": "sha512-0X7ACg4YvTRDFMIuTOEj6B4NpN7i3F/4j5igOcTI5NC5J+N4TribNdErCHOZF1LBWhhcyfwxelVwvoYNMUXTOA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/code-frame": "^7.10.4",
|
"@babel/code-frame": "^7.10.4",
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
@ -436,9 +436,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "15.0.1",
|
"version": "15.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz",
|
||||||
"integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA=="
|
"integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ=="
|
||||||
},
|
},
|
||||||
"@types/yargs": {
|
"@types/yargs": {
|
||||||
"version": "15.0.13",
|
"version": "15.0.13",
|
||||||
@ -534,9 +534,9 @@
|
|||||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
},
|
},
|
||||||
"core-js-pure": {
|
"core-js-pure": {
|
||||||
"version": "3.11.1",
|
"version": "3.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz",
|
||||||
"integrity": "sha512-2JukQi8HgAOCD5CSimxWWXVrUBoA9Br796uIA5Z06bIjt7PBBI19ircFaAxplgE1mJf3x2BY6MkT/HWA/UryPg=="
|
"integrity": "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ=="
|
||||||
},
|
},
|
||||||
"dom-accessibility-api": {
|
"dom-accessibility-api": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
@ -681,9 +681,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "15.0.1",
|
"version": "15.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz",
|
||||||
"integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA=="
|
"integrity": "sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ=="
|
||||||
},
|
},
|
||||||
"@types/yargs": {
|
"@types/yargs": {
|
||||||
"version": "15.0.13",
|
"version": "15.0.13",
|
||||||
@ -828,9 +828,9 @@
|
|||||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
|
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
|
||||||
},
|
},
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
"version": "17.0.4",
|
"version": "17.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.5.tgz",
|
||||||
"integrity": "sha512-onz2BqScSFMoTRdJUZUDD/7xrusM8hBA2Fktk2qgaTYPCgPvWnDEgkrOs8hhPUf2jfcIXkJ5yK6VfYormJS3Jw==",
|
"integrity": "sha512-bj4biDB9ZJmGAYTWSKJly6bMr4BLUiBrx9ujiJEoP9XIDY9CTaPGxE5QWN/1WjpPLzYF7/jRNnV2nNxNe970sw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"@types/scheduler": "*",
|
"@types/scheduler": "*",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gantt-task-react",
|
"name": "gantt-task-react",
|
||||||
"version": "0.3.1",
|
"version": "0.3.2",
|
||||||
"description": "Interactive Gantt Chart for React with TypeScript.",
|
"description": "Interactive Gantt Chart for React with TypeScript.",
|
||||||
"author": "MaTeMaTuK <maksym.vikarii@gmail.com>",
|
"author": "MaTeMaTuK <maksym.vikarii@gmail.com>",
|
||||||
"homepage": "https://github.com/MaTeMaTuK/gantt-task-react",
|
"homepage": "https://github.com/MaTeMaTuK/gantt-task-react",
|
||||||
|
|||||||
@ -17,4 +17,5 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { CalendarProps } from "../calendar/calendar";
|
|||||||
import { TaskGanttContentProps } from "./task-gantt-content";
|
import { TaskGanttContentProps } from "./task-gantt-content";
|
||||||
import { TaskListHeaderDefault } from "../task-list/task-list-header";
|
import { TaskListHeaderDefault } from "../task-list/task-list-header";
|
||||||
import { TaskListTableDefault } from "../task-list/task-list-table";
|
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 { VerticalScroll } from "../other/vertical-scroll";
|
||||||
import { TaskListProps, TaskList } from "../task-list/task-list";
|
import { TaskListProps, TaskList } from "../task-list/task-list";
|
||||||
import { TaskGantt } from "./task-gantt";
|
import { TaskGantt } from "./task-gantt";
|
||||||
@ -56,7 +56,6 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
const taskListRef = useRef<HTMLDivElement>(null);
|
const taskListRef = useRef<HTMLDivElement>(null);
|
||||||
const verticalGanttContainerRef = useRef<HTMLDivElement>(null);
|
|
||||||
const [dateSetup, setDateSetup] = useState<DateSetup>(() => {
|
const [dateSetup, setDateSetup] = useState<DateSetup>(() => {
|
||||||
const [startDate, endDate] = ganttDateRange(tasks, viewMode);
|
const [startDate, endDate] = ganttDateRange(tasks, viewMode);
|
||||||
return { viewMode, dates: seedDates(startDate, endDate, viewMode) };
|
return { viewMode, dates: seedDates(startDate, endDate, viewMode) };
|
||||||
@ -64,6 +63,8 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
|||||||
|
|
||||||
const [taskHeight, setTaskHeight] = useState((rowHeight * barFill) / 100);
|
const [taskHeight, setTaskHeight] = useState((rowHeight * barFill) / 100);
|
||||||
const [taskListWidth, setTaskListWidth] = useState(0);
|
const [taskListWidth, setTaskListWidth] = useState(0);
|
||||||
|
const [svgContainerWidth, setSvgContainerWidth] = useState(0);
|
||||||
|
const [svgContainerHeight, setSvgContainerHeight] = useState(ganttHeight);
|
||||||
const [barTasks, setBarTasks] = useState<BarTask[]>([]);
|
const [barTasks, setBarTasks] = useState<BarTask[]>([]);
|
||||||
const [ganttEvent, setGanttEvent] = useState<GanttEvent>({
|
const [ganttEvent, setGanttEvent] = useState<GanttEvent>({
|
||||||
action: "",
|
action: "",
|
||||||
@ -76,7 +77,6 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
|||||||
const [scrollX, setScrollX] = useState(0);
|
const [scrollX, setScrollX] = useState(0);
|
||||||
const [ignoreScrollEvent, setIgnoreScrollEvent] = useState(false);
|
const [ignoreScrollEvent, setIgnoreScrollEvent] = useState(false);
|
||||||
|
|
||||||
const svgHeight = rowHeight * barTasks.length;
|
|
||||||
const svgWidth = dateSetup.dates.length * columnWidth;
|
const svgWidth = dateSetup.dates.length * columnWidth;
|
||||||
const ganttFullHeight = barTasks.length * rowHeight;
|
const ganttFullHeight = barTasks.length * rowHeight;
|
||||||
|
|
||||||
@ -170,10 +170,27 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
|||||||
}, [rowHeight, barFill, taskHeight]);
|
}, [rowHeight, barFill, taskHeight]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!listCellWidth) {
|
||||||
|
setTaskListWidth(0);
|
||||||
|
}
|
||||||
if (taskListRef.current) {
|
if (taskListRef.current) {
|
||||||
setTaskListWidth(taskListRef.current.offsetWidth);
|
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
|
// scroll events
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -326,7 +343,6 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
|||||||
fontFamily,
|
fontFamily,
|
||||||
fontSize,
|
fontSize,
|
||||||
arrowIndent,
|
arrowIndent,
|
||||||
svgHeight,
|
|
||||||
svgWidth,
|
svgWidth,
|
||||||
setGanttEvent,
|
setGanttEvent,
|
||||||
setFailedTask,
|
setFailedTask,
|
||||||
@ -335,7 +351,6 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
|||||||
onProgressChange,
|
onProgressChange,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
onDelete,
|
onDelete,
|
||||||
TooltipContent,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const tableProps: TaskListProps = {
|
const tableProps: TaskListProps = {
|
||||||
@ -371,8 +386,23 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
|||||||
ganttHeight={ganttHeight}
|
ganttHeight={ganttHeight}
|
||||||
scrollY={scrollY}
|
scrollY={scrollY}
|
||||||
scrollX={scrollX}
|
scrollX={scrollX}
|
||||||
verticalGanttContainerRef={verticalGanttContainerRef}
|
|
||||||
/>
|
/>
|
||||||
|
{ganttEvent.changedTask && (
|
||||||
|
<Tooltip
|
||||||
|
arrowIndent={arrowIndent}
|
||||||
|
rowHeight={rowHeight}
|
||||||
|
svgContainerHeight={svgContainerHeight}
|
||||||
|
svgContainerWidth={svgContainerWidth}
|
||||||
|
fontFamily={fontFamily}
|
||||||
|
fontSize={fontSize}
|
||||||
|
scrollX={scrollX}
|
||||||
|
scrollY={scrollY}
|
||||||
|
task={ganttEvent.changedTask}
|
||||||
|
headerHeight={headerHeight}
|
||||||
|
taskListWidth={taskListWidth}
|
||||||
|
TooltipContent={TooltipContent}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<VerticalScroll
|
<VerticalScroll
|
||||||
ganttFullHeight={ganttFullHeight}
|
ganttFullHeight={ganttFullHeight}
|
||||||
ganttHeight={ganttHeight}
|
ganttHeight={ganttHeight}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Task, EventOption } from "../../types/public-types";
|
import { EventOption } from "../../types/public-types";
|
||||||
import { BarTask } from "../../types/bar-task";
|
import { BarTask } from "../../types/bar-task";
|
||||||
import { Arrow } from "../other/arrow";
|
import { Arrow } from "../other/arrow";
|
||||||
import { handleTaskBySVGMouseEvent } from "../../helpers/bar-helper";
|
import { handleTaskBySVGMouseEvent } from "../../helpers/bar-helper";
|
||||||
import { Tooltip } from "../other/tooltip";
|
|
||||||
import { isKeyboardEvent } from "../../helpers/other-helper";
|
import { isKeyboardEvent } from "../../helpers/other-helper";
|
||||||
import { TaskItem } from "../task-item/task-item";
|
import { TaskItem } from "../task-item/task-item";
|
||||||
import {
|
import {
|
||||||
@ -21,12 +20,7 @@ export type TaskGanttContentProps = {
|
|||||||
columnWidth: number;
|
columnWidth: number;
|
||||||
timeStep: number;
|
timeStep: number;
|
||||||
svg?: React.RefObject<SVGSVGElement>;
|
svg?: React.RefObject<SVGSVGElement>;
|
||||||
svgHeight: number;
|
|
||||||
svgWidth: number;
|
svgWidth: number;
|
||||||
displayXStartEndpoint?: {
|
|
||||||
start: number;
|
|
||||||
end: number;
|
|
||||||
};
|
|
||||||
taskHeight: number;
|
taskHeight: number;
|
||||||
arrowColor: string;
|
arrowColor: string;
|
||||||
arrowIndent: number;
|
arrowIndent: number;
|
||||||
@ -35,11 +29,6 @@ export type TaskGanttContentProps = {
|
|||||||
setGanttEvent: (value: GanttEvent) => void;
|
setGanttEvent: (value: GanttEvent) => void;
|
||||||
setFailedTask: (value: BarTask | null) => void;
|
setFailedTask: (value: BarTask | null) => void;
|
||||||
setSelectedTask: (taskId: string) => void;
|
setSelectedTask: (taskId: string) => void;
|
||||||
TooltipContent: React.FC<{
|
|
||||||
task: Task;
|
|
||||||
fontSize: string;
|
|
||||||
fontFamily: string;
|
|
||||||
}>;
|
|
||||||
} & EventOption;
|
} & EventOption;
|
||||||
|
|
||||||
export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||||
@ -51,8 +40,6 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
|||||||
columnWidth,
|
columnWidth,
|
||||||
timeStep,
|
timeStep,
|
||||||
svg,
|
svg,
|
||||||
svgHeight,
|
|
||||||
displayXStartEndpoint,
|
|
||||||
taskHeight,
|
taskHeight,
|
||||||
arrowColor,
|
arrowColor,
|
||||||
arrowIndent,
|
arrowIndent,
|
||||||
@ -65,7 +52,6 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
|||||||
onProgressChange,
|
onProgressChange,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
onDelete,
|
onDelete,
|
||||||
TooltipContent,
|
|
||||||
}) => {
|
}) => {
|
||||||
const point = svg?.current?.createSVGPoint();
|
const point = svg?.current?.createSVGPoint();
|
||||||
const [xStep, setXStep] = useState(0);
|
const [xStep, setXStep] = useState(0);
|
||||||
@ -292,20 +278,6 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</g>
|
</g>
|
||||||
<g className="toolTip">
|
|
||||||
{ganttEvent.changedTask && displayXStartEndpoint && (
|
|
||||||
<Tooltip
|
|
||||||
arrowIndent={arrowIndent}
|
|
||||||
rowHeight={rowHeight}
|
|
||||||
svgHeight={svgHeight}
|
|
||||||
displayXStartEndpoint={displayXStartEndpoint}
|
|
||||||
task={ganttEvent.changedTask}
|
|
||||||
fontFamily={fontFamily}
|
|
||||||
fontSize={fontSize}
|
|
||||||
TooltipContent={TooltipContent}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</g>
|
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, useEffect, useState } from "react";
|
import React, { useRef, useEffect } from "react";
|
||||||
import { GridProps, Grid } from "../grid/grid";
|
import { GridProps, Grid } from "../grid/grid";
|
||||||
import { CalendarProps, Calendar } from "../calendar/calendar";
|
import { CalendarProps, Calendar } from "../calendar/calendar";
|
||||||
import { TaskGanttContentProps, TaskGanttContent } from "./task-gantt-content";
|
import { TaskGanttContentProps, TaskGanttContent } from "./task-gantt-content";
|
||||||
@ -11,7 +11,6 @@ export type TaskGanttProps = {
|
|||||||
ganttHeight: number;
|
ganttHeight: number;
|
||||||
scrollY: number;
|
scrollY: number;
|
||||||
scrollX: number;
|
scrollX: number;
|
||||||
verticalGanttContainerRef: React.RefObject<HTMLDivElement>;
|
|
||||||
};
|
};
|
||||||
export const TaskGantt: React.FC<TaskGanttProps> = ({
|
export const TaskGantt: React.FC<TaskGanttProps> = ({
|
||||||
gridProps,
|
gridProps,
|
||||||
@ -20,15 +19,11 @@ export const TaskGantt: React.FC<TaskGanttProps> = ({
|
|||||||
ganttHeight,
|
ganttHeight,
|
||||||
scrollY,
|
scrollY,
|
||||||
scrollX,
|
scrollX,
|
||||||
verticalGanttContainerRef,
|
|
||||||
}) => {
|
}) => {
|
||||||
const ganttSVGRef = useRef<SVGSVGElement>(null);
|
const ganttSVGRef = useRef<SVGSVGElement>(null);
|
||||||
const horizontalContainerRef = useRef<HTMLDivElement>(null);
|
const horizontalContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const [displayXStartEndpoint, setDisplayXStartEndpoint] = useState({
|
const verticalGanttContainerRef = useRef<HTMLDivElement>(null);
|
||||||
start: 0,
|
const newBarProps = { ...barProps, svg: ganttSVGRef };
|
||||||
end: 0,
|
|
||||||
});
|
|
||||||
const newBarProps = { ...barProps, svg: ganttSVGRef, displayXStartEndpoint };
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (horizontalContainerRef.current) {
|
if (horizontalContainerRef.current) {
|
||||||
@ -39,13 +34,8 @@ export const TaskGantt: React.FC<TaskGanttProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (verticalGanttContainerRef.current) {
|
if (verticalGanttContainerRef.current) {
|
||||||
verticalGanttContainerRef.current.scrollLeft = scrollX;
|
verticalGanttContainerRef.current.scrollLeft = scrollX;
|
||||||
setDisplayXStartEndpoint({
|
|
||||||
start: scrollX,
|
|
||||||
end: verticalGanttContainerRef.current.clientWidth + scrollX,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// verticalContainerRef.current?.clientWidth need for resize window tracking
|
}, [scrollX]);
|
||||||
}, [scrollX, verticalGanttContainerRef.current?.clientWidth]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -11,5 +11,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tooltipDetailsContainer {
|
.tooltipDetailsContainer {
|
||||||
display: table;
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltipDetailsContainerHidden {
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,11 +6,12 @@ import styles from "./tooltip.module.css";
|
|||||||
export type TooltipProps = {
|
export type TooltipProps = {
|
||||||
task: BarTask;
|
task: BarTask;
|
||||||
arrowIndent: number;
|
arrowIndent: number;
|
||||||
svgHeight: number;
|
svgContainerHeight: number;
|
||||||
displayXStartEndpoint: {
|
svgContainerWidth: number;
|
||||||
start: number;
|
headerHeight: number;
|
||||||
end: number;
|
taskListWidth: number;
|
||||||
};
|
scrollX: number;
|
||||||
|
scrollY: number;
|
||||||
rowHeight: number;
|
rowHeight: number;
|
||||||
fontSize: string;
|
fontSize: string;
|
||||||
fontFamily: string;
|
fontFamily: string;
|
||||||
@ -23,83 +24,76 @@ export type TooltipProps = {
|
|||||||
export const Tooltip: React.FC<TooltipProps> = ({
|
export const Tooltip: React.FC<TooltipProps> = ({
|
||||||
task,
|
task,
|
||||||
rowHeight,
|
rowHeight,
|
||||||
svgHeight,
|
svgContainerHeight,
|
||||||
displayXStartEndpoint,
|
svgContainerWidth,
|
||||||
|
scrollX,
|
||||||
|
scrollY,
|
||||||
arrowIndent,
|
arrowIndent,
|
||||||
fontSize,
|
fontSize,
|
||||||
fontFamily,
|
fontFamily,
|
||||||
|
headerHeight,
|
||||||
|
taskListWidth,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
}) => {
|
}) => {
|
||||||
const tooltipRef = useRef<HTMLDivElement | null>(null);
|
const tooltipRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [toolWidth, setToolWidth] = useState(1000);
|
const [relatedY, setRelatedY] = useState(0);
|
||||||
const [toolHeight, setToolHeight] = useState(1000);
|
const [relatedX, setRelatedX] = useState(0);
|
||||||
const [relatedY, setRelatedY] = useState(task.index * rowHeight);
|
|
||||||
const [relatedX, setRelatedX] = useState(displayXStartEndpoint.end);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tooltipRef.current) {
|
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;
|
const tooltipHeight = tooltipRef.current.offsetHeight * 1.1;
|
||||||
let tooltipY = task.index * rowHeight;
|
const tooltipWidth = tooltipRef.current.offsetWidth * 1.1;
|
||||||
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;
|
|
||||||
|
|
||||||
if (
|
const tooltipLowerPoint = tooltipHeight + newRelatedY - scrollY;
|
||||||
newRelatedX < displayXStartEndpoint.start &&
|
const tooltipLeftmostPoint = tooltipWidth + newRelatedX;
|
||||||
tooltipLowerPoint > svgHeight
|
const fullChartWidth = taskListWidth + svgContainerWidth;
|
||||||
) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
setRelatedY(tooltipY);
|
if (tooltipLeftmostPoint > fullChartWidth) {
|
||||||
setToolWidth(newWidth);
|
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);
|
setRelatedX(newRelatedX);
|
||||||
if (tooltipHeight !== 1000) {
|
|
||||||
setToolHeight(tooltipHeight);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [tooltipRef, task, arrowIndent, displayXStartEndpoint]);
|
}, [
|
||||||
|
tooltipRef.current,
|
||||||
|
task,
|
||||||
|
arrowIndent,
|
||||||
|
scrollX,
|
||||||
|
scrollY,
|
||||||
|
headerHeight,
|
||||||
|
taskListWidth,
|
||||||
|
rowHeight,
|
||||||
|
svgContainerHeight,
|
||||||
|
svgContainerWidth,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<foreignObject
|
<div
|
||||||
x={relatedX}
|
ref={tooltipRef}
|
||||||
y={relatedY}
|
className={
|
||||||
width={toolWidth}
|
relatedX
|
||||||
height={toolHeight}
|
? styles.tooltipDetailsContainer
|
||||||
|
: styles.tooltipDetailsContainerHidden
|
||||||
|
}
|
||||||
|
style={{ left: relatedX, top: relatedY }}
|
||||||
>
|
>
|
||||||
<div ref={tooltipRef} className={styles.tooltipDetailsContainer}>
|
<TooltipContent task={task} fontSize={fontSize} fontFamily={fontFamily} />
|
||||||
<TooltipContent
|
</div>
|
||||||
task={task}
|
|
||||||
fontSize={fontSize}
|
|
||||||
fontFamily={fontFamily}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</foreignObject>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user