gantt and timeline has separated, added OOB list

This commit is contained in:
unknown committed 2020-08-23 22:33:25 +03:00
1 parent ac061d6cb6
commit 764b027bd8
30 files changed
+713 -324

No files matched your search

+6
View File
@@ -32,6 +32,12 @@
.barLabelOutside {
fill: #555;
text-anchor: start;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
}
.barBackground {
+4 -4
View File
@@ -1,14 +1,14 @@
import React, { useState } from "react";
import { BarProgressHandle } from "./bar-progress-handle";
import { BarDateHandle } from "./bar-date-handle";
import { BarDisplay } from "./bar-display";
import { BarTask } from "../../types/bar-task";
import {
progressWithByParams,
getProgressPoint,
} from "../../helpers/bar-helper";
import styles from "./bar.module.css";
import { GanttContentMoveAction } from "../Gantt/gantt-content";
import { GanttContentMoveAction } from "../gantt/task-gantt-content";
import { BarDisplay } from "./bar-display";
import { BarDateHandle } from "./bar-date-handle";
import { BarProgressHandle } from "./bar-progress-handle";
export type BarProps = {
task: BarTask;
@@ -23,3 +23,9 @@
user-select: none;
pointer-events: none;
}
.calendarHeader {
fill: #ffffff;
stroke: #e0e0e0;
stroke-width: 1.4;
}
+10 -3
View File
@@ -67,7 +67,7 @@ export const Calendar: React.FC<CalendarProps> = ({
const getCalendarValuesForWeek = () => {
const topValues: ReactChild[] = [];
const bottomValues: ReactChild[] = [];
let weeksCount: number = 0;
let weeksCount: number = 1;
const topDefaultHeight = headerHeight * 0.5;
for (let i = dates.length - 1; i >= 0; i--) {
const date = dates[i];
@@ -138,7 +138,7 @@ export const Calendar: React.FC<CalendarProps> = ({
topValues.push(
<TopPartOfCalendar
key={topValue}
key={topValue + date.getFullYear()}
value={topValue}
x1Line={columnWidth * (i + 1)}
y1Line={0}
@@ -179,7 +179,7 @@ export const Calendar: React.FC<CalendarProps> = ({
const topValue = `${date.getDate()} ${getLocaleMonth(date, locale)}`;
topValues.push(
<TopPartOfCalendar
key={topValue}
key={topValue + date.getFullYear()}
value={topValue}
x1Line={columnWidth * i + ticks * columnWidth}
y1Line={0}
@@ -210,6 +210,13 @@ export const Calendar: React.FC<CalendarProps> = ({
}
return (
<g className="calendar" fontSize={fontSize} fontFamily={fontFamily}>
<rect
x={0}
y={0}
width={columnWidth * dates.length}
height={headerHeight}
className={styles.calendarHeader}
/>
{bottomValues} {topValues}
</g>
);
+20
View File
@@ -0,0 +1,20 @@
.ganttVerticalContainer {
overflow-x: auto;
overflow-y: hidden;
font-size: 0;
margin: 0;
padding: 0;
}
.horizontalContainer {
margin: 0;
padding: 0;
overflow-y: hidden;
}
.wrapper {
display: flex;
padding: 0;
margin: 0;
list-style: none;
}
+63 -25
View File
@@ -1,15 +1,24 @@
import React, { useRef, useState } from "react";
import React, { useState, SyntheticEvent } from "react";
import { ViewMode, GanttProps, Task } from "../../types/public-types";
import { Grid, GridProps } from "../Grid/grid";
import { Calendar, CalendarProps } from "../Calendar/calendar";
import { GanttContent, GanttContentProps } from "./gantt-content";
import { GridProps } from "../grid/grid";
import { ganttDateRange, seedDates } from "../../helpers/date-helper";
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 { Scroll } from "../other/scroll";
import { TaskListProps, TaskList } from "../task-list/task-list";
import styles from "./gantt.module.css";
import { TaskGantt } from "./task-gantt";
export const Gantt: React.SFC<GanttProps> = ({
tasks,
headerHeight = 50,
columnWidth = 60,
listCellWidth = "150px",
rowHeight = 50,
ganttHeight = 0,
viewMode = ViewMode.Day,
locale = "en-GB",
barFill = 60,
@@ -25,27 +34,36 @@ export const Gantt: React.SFC<GanttProps> = ({
fontSize = "14px",
arrowIndent = 20,
todayColor = "rgba(252, 248, 227, 0.5)",
TooltipContent = StandardTooltipContent,
TaskListHeader = TaskListHeaderDefault,
TaskListTable = TaskListTableDefault,
onDateChange,
onProgressChange,
onDoubleClick,
onTaskDelete,
getTooltipContent,
}) => {
const svg = useRef<SVGSVGElement>(null);
const [ganttTasks, setGanttTasks] = useState<Task[]>(tasks);
const [scroll, setScroll] = useState(0);
const [startDate, endDate] = ganttDateRange(ganttTasks, viewMode);
const dates = seedDates(startDate, endDate, viewMode);
const handleOnTasksChange = (tasks: Task[]) => {
const svgHeight = rowHeight * tasks.length;
const gridWidth = dates.length * columnWidth;
const onTasksDateChange = (tasks: Task[]) => {
setGanttTasks(tasks);
};
const handleScroll = (event: SyntheticEvent<HTMLDivElement>) => {
setScroll(event.currentTarget.scrollTop);
};
const gridProps: GridProps = {
columnWidth,
gridWidth: dates.length * columnWidth,
gridWidth,
tasks: ganttTasks,
rowHeight,
headerHeight,
dates,
todayColor,
};
@@ -58,7 +76,7 @@ export const Gantt: React.SFC<GanttProps> = ({
fontFamily,
fontSize,
};
const barProps: GanttContentProps = {
const barProps: TaskGanttContentProps = {
tasks: ganttTasks,
rowHeight,
barCornerRadius,
@@ -69,32 +87,52 @@ export const Gantt: React.SFC<GanttProps> = ({
barProgressSelectedColor,
barBackgroundColor,
barBackgroundSelectedColor,
headerHeight,
handleWidth,
arrowColor,
timeStep,
fontFamily,
fontSize,
arrowIndent,
svg,
onTasksDateChange: handleOnTasksChange,
svgHeight,
onTasksDateChange: onTasksDateChange,
onDateChange,
onProgressChange,
onDoubleClick,
onTaskDelete,
getTooltipContent,
TooltipContent,
};
const tableProps: TaskListProps = {
rowHeight,
rowWidth: listCellWidth,
fontFamily,
fontSize,
tasks: ganttTasks,
locale,
headerHeight,
scroll,
ganttHeight,
horizontalContainerClass: styles.horizontalContainer,
TaskListHeader,
TaskListTable,
};
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={columnWidth * dates.length}
height={headerHeight + rowHeight * tasks.length}
fontFamily={fontFamily}
ref={svg}
>
<Grid {...gridProps} />
<Calendar {...calendarProps} />
<GanttContent {...barProps} />
</svg>
<div className={styles.wrapper}>
{listCellWidth && <TaskList {...tableProps} />}
<TaskGantt
gridProps={gridProps}
calendarProps={calendarProps}
barProps={barProps}
ganttHeight={ganttHeight}
scroll={scroll}
/>
<Scroll
ganttFullHeight={ganttTasks.length * rowHeight}
ganttHeight={ganttHeight}
headerHeight={headerHeight}
onScroll={handleScroll}
/>
</div>
);
};
@@ -1,14 +1,14 @@
import React, { useEffect, useState } from "react";
import { Task, EventOption } from "../../types/public-types";
import { Bar } from "../Bar/bar";
import { Bar } from "../bar/bar";
import { BarTask } from "../../types/bar-task";
import { Arrow } from "../Other/arrow";
import { Arrow } from "../other/arrow";
import {
convertToBarTasks,
handleTaskBySVGMouseEvent,
BarMoveAction,
} from "../../helpers/bar-helper";
import { Tooltip } from "../Other/tooltip";
import { Tooltip } from "../other/tooltip";
import { isKeyboardEvent } from "../../helpers/other-helper";
export type GanttContentMoveAction =
@@ -21,7 +21,7 @@ export type BarEvent = {
selectedTask?: BarTask;
action: GanttContentMoveAction;
};
export type GanttContentProps = {
export type TaskGanttContentProps = {
tasks: Task[];
dates: Date[];
rowHeight: number;
@@ -32,23 +32,23 @@ export type GanttContentProps = {
barProgressSelectedColor: string;
barBackgroundColor: string;
barBackgroundSelectedColor: string;
headerHeight: number;
handleWidth: number;
timeStep: number;
svg: React.RefObject<SVGSVGElement>;
svg?: React.RefObject<SVGSVGElement>;
svgHeight: number;
arrowColor: string;
arrowIndent: number;
fontSize: string;
fontFamily: string;
getTooltipContent?: (
task: Task,
fontSize: string,
fontFamily: string
) => JSX.Element;
TooltipContent: React.FC<{
task: Task;
fontSize: string;
fontFamily: string;
}>;
onTasksDateChange: (tasks: Task[]) => void;
} & EventOption;
export const GanttContent: React.FC<GanttContentProps> = ({
export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
tasks,
dates,
rowHeight,
@@ -59,10 +59,10 @@ export const GanttContent: React.FC<GanttContentProps> = ({
barProgressSelectedColor,
barBackgroundColor,
barBackgroundSelectedColor,
headerHeight,
handleWidth,
timeStep,
svg,
svgHeight,
arrowColor,
arrowIndent,
fontFamily,
@@ -72,9 +72,9 @@ export const GanttContent: React.FC<GanttContentProps> = ({
onProgressChange,
onDoubleClick,
onTaskDelete,
getTooltipContent,
TooltipContent,
}) => {
const point = svg.current?.createSVGPoint();
const point = svg?.current?.createSVGPoint();
const [barEvent, setBarEvent] = useState<BarEvent>({
action: "",
});
@@ -103,7 +103,6 @@ export const GanttContent: React.FC<GanttContentProps> = ({
columnWidth,
rowHeight,
barFill,
headerHeight,
barCornerRadius,
handleWidth,
barProgressColor,
@@ -120,7 +119,6 @@ export const GanttContent: React.FC<GanttContentProps> = ({
dates,
barFill,
handleWidth,
headerHeight,
barProgressColor,
barProgressSelectedColor,
barBackgroundColor,
@@ -139,9 +137,7 @@ export const GanttContent: React.FC<GanttContentProps> = ({
if (action === "delete") {
if (onTaskDelete) {
await onTaskDelete(selectedTask);
const newTasks = barTasks.filter(
t => t.id !== barEvent.selectedTask?.id
);
const newTasks = barTasks.filter(t => t.id !== selectedTask.id);
onTasksDateChange(newTasks);
}
}
@@ -154,7 +150,7 @@ export const GanttContent: React.FC<GanttContentProps> = ({
setBarEvent({ action: "" });
}
} else if (action === "move") {
if (!svg.current || !point) return;
if (!svg?.current || !point) return;
point.x = event.clientX;
const cursor = point.matrixTransform(
svg.current.getScreenCTM()?.inverse()
@@ -173,12 +169,12 @@ export const GanttContent: React.FC<GanttContentProps> = ({
useEffect(() => {
const handleMouseMove = async (event: MouseEvent) => {
if (!barEvent.selectedTask || !point || !svg.current) return;
if (!barEvent.selectedTask || !point || !svg?.current) return;
event.preventDefault();
point.x = event.clientX;
const cursor = point.matrixTransform(
svg.current.getScreenCTM()?.inverse()
svg?.current.getScreenCTM()?.inverse()
);
const { isChanged, changedTask } = handleTaskBySVGMouseEvent(
@@ -199,12 +195,12 @@ export const GanttContent: React.FC<GanttContentProps> = ({
const handleMouseUp = async (event: MouseEvent) => {
const { selectedTask, action } = barEvent;
if (!selectedTask || !point || !svg.current) return;
if (!selectedTask || !point || !svg?.current) return;
event.preventDefault();
point.x = event.clientX;
const cursor = point.matrixTransform(
svg.current.getScreenCTM()?.inverse()
svg?.current.getScreenCTM()?.inverse()
);
const { changedTask } = handleTaskBySVGMouseEvent(
@@ -239,7 +235,7 @@ export const GanttContent: React.FC<GanttContentProps> = ({
barEvent.action === "end" ||
barEvent.action === "start" ||
barEvent.action === "progress") &&
svg.current
svg?.current
) {
svg.current.addEventListener("mousemove", handleMouseMove);
svg.current.addEventListener("mouseup", handleMouseUp);
@@ -282,7 +278,7 @@ export const GanttContent: React.FC<GanttContentProps> = ({
arrowIndent={arrowIndent}
isProgressChangeable={!!onProgressChange && !task.isDisabled}
isDateChangeable={!!onDateChange && !task.isDisabled}
isDelete={!!onTaskDelete && !task.isDisabled}
isDelete={!task.isDisabled}
onEventStart={handleBarEventStart}
key={task.id}
/>
@@ -293,11 +289,12 @@ export const GanttContent: React.FC<GanttContentProps> = ({
{barEvent.selectedTask && (
<Tooltip
x={barEvent.selectedTask.x2 + arrowIndent + arrowIndent * 0.5}
y={barEvent.selectedTask.y + rowHeight}
rowHeight={rowHeight}
svgHeight={svgHeight}
task={barEvent.selectedTask}
fontFamily={fontFamily}
fontSize={fontSize}
getTooltipContent={getTooltipContent}
TooltipContent={TooltipContent}
/>
)}
</g>
+63
View File
@@ -0,0 +1,63 @@
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";
import styles from "./gantt.module.css";
export type TaskGanttProps = {
gridProps: GridProps;
calendarProps: CalendarProps;
barProps: TaskGanttContentProps;
ganttHeight: number;
scroll: number;
};
export const TaskGantt: React.FC<TaskGanttProps> = ({
gridProps,
calendarProps,
barProps,
ganttHeight,
scroll,
}) => {
const ganttSVGRef = useRef<SVGSVGElement>(null);
const horizontalContainerRef = useRef<HTMLDivElement>(null);
const newBarProps = { ...barProps, svg: ganttSVGRef };
useEffect(() => {
if (horizontalContainerRef.current) {
horizontalContainerRef.current.scrollTop = scroll;
}
}, [scroll]);
return (
<div className={styles.ganttVerticalContainer}>
<svg
xmlns="http://www.w3.org/2000/svg"
width={gridProps.gridWidth}
height={calendarProps.headerHeight}
fontFamily={barProps.fontFamily}
>
<Calendar {...calendarProps} />
</svg>
<div
ref={horizontalContainerRef}
className={styles.horizontalContainer}
style={
ganttHeight
? { height: ganttHeight, width: gridProps.gridWidth }
: { width: gridProps.gridWidth }
}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width={gridProps.gridWidth}
height={barProps.rowHeight * barProps.tasks.length}
fontFamily={barProps.fontFamily}
ref={ganttSVGRef}
>
<Grid {...gridProps} />
<TaskGanttContent {...newBarProps} />
</svg>
</div>
</div>
);
};
+12 -5
View File
@@ -8,7 +8,6 @@ export type GridBodyProps = {
dates: Date[];
gridWidth: number;
rowHeight: number;
headerHeight: number;
columnWidth: number;
todayColor: string;
};
@@ -16,14 +15,22 @@ export const GridBody: React.FC<GridBodyProps> = ({
tasks,
dates,
rowHeight,
headerHeight,
gridWidth,
columnWidth,
todayColor,
}) => {
let y = headerHeight;
let y = 0;
const gridRows: ReactChild[] = [];
const rowLines: ReactChild[] = [];
const rowLines: ReactChild[] = [
<line
key="RowLineFirst"
x="0"
y1={0}
x2={gridWidth}
y2={0}
className={styles.gridRowLine}
/>,
];
for (const task of tasks) {
gridRows.push(
<rect
@@ -58,7 +65,7 @@ export const GridBody: React.FC<GridBodyProps> = ({
<line
key={date.getTime()}
x1={tickX}
y1={headerHeight}
y1={0}
x2={tickX}
y2={y}
className={styles.gridTick}
-21
View File
@@ -1,21 +0,0 @@
import React from "react";
import styles from "./grid.module.css";
export type GridHeaderProps = {
gridWidth: number;
headerHeight: number;
};
export const GridHeader: React.FC<GridHeaderProps> = ({
gridWidth,
headerHeight,
}) => {
return (
<rect
x="0"
y="0"
width={gridWidth}
height={headerHeight}
className={styles.gridHeader}
/>
);
};
+1 -7
View File
@@ -1,17 +1,11 @@
.gridRow {
fill: #ffffff;
fill: #fff;
}
.gridRow:nth-child(even) {
fill: #f5f5f5;
}
.gridHeader {
fill: #ffffff;
stroke: #e0e0e0;
stroke-width: 1.4;
}
.gridRowLine {
stroke: #ebeff2;
}
+1 -3
View File
@@ -1,12 +1,10 @@
import React from "react";
import { GridBody, GridBodyProps } from "./grid-body";
import { GridHeader, GridHeaderProps } from "./grid-header";
export type GridProps = GridBodyProps & GridHeaderProps;
export type GridProps = GridBodyProps;
export const Grid: React.FC<GridProps> = props => {
return (
<g className="grid">
<GridHeader {...props} />
<GridBody {...props} />
</g>
);
+6
View File
@@ -0,0 +1,6 @@
.scroll {
overflow: hidden auto;
margin-left: -17px;
width: 17px;
flex-shrink: 0;
}
+19
View File
@@ -0,0 +1,19 @@
import React, { SyntheticEvent } from "react";
import styles from "./scroll.module.css";
export const Scroll: React.FC<{
ganttHeight: number;
ganttFullHeight: number;
headerHeight: number;
onScroll: (event: SyntheticEvent<HTMLDivElement>) => void;
}> = ({ ganttHeight, ganttFullHeight, headerHeight, onScroll }) => {
return (
<div
style={{ height: ganttHeight, marginTop: headerHeight }}
className={styles.scroll}
onScroll={onScroll}
>
<div style={{ height: ganttFullHeight, width: 1 }} />
</div>
);
};
+34 -25
View File
@@ -1,54 +1,63 @@
import React, { useRef, useEffect, useState } from "react";
import { Task } from "../../types/public-types";
import { BarTask } from "../../types/bar-task";
import styles from "./tooltip.module.css";
export type TooltipProps = {
x: number;
y: number;
task: Task;
svgHeight: number;
rowHeight: number;
task: BarTask;
fontSize: string;
fontFamily: string;
getTooltipContent?: (
task: Task,
fontSize: string,
fontFamily: string
) => JSX.Element;
TooltipContent: React.FC<{
task: Task;
fontSize: string;
fontFamily: string;
}>;
};
export const Tooltip: React.FC<TooltipProps> = ({
x,
y,
rowHeight,
svgHeight,
task,
fontSize,
fontFamily,
getTooltipContent = getStandardTooltipContent,
TooltipContent,
}) => {
const tooltipRef = useRef<HTMLDivElement | null>(null);
const [toolWidth, setToolWidth] = useState(1000);
const [relatedY, setRelatedY] = useState(y);
const [relatedY, setRelatedY] = useState((task.index - 1) * rowHeight);
useEffect(() => {
if (tooltipRef.current) {
const height =
tooltipRef.current.offsetHeight +
tooltipRef.current.offsetHeight * 0.15;
setRelatedY(y - height);
const tooltipHeight = tooltipRef.current.offsetHeight;
const tooltipY = task.index * rowHeight + rowHeight;
if (tooltipHeight > tooltipY) {
setRelatedY(tooltipHeight * 0.5);
} else if (tooltipY + tooltipHeight > svgHeight) {
setRelatedY(svgHeight - tooltipHeight * 1.05);
}
setToolWidth(tooltipRef.current.scrollWidth * 1.1);
}
}, [tooltipRef, y]);
}, [tooltipRef, task]);
return (
<foreignObject x={x} y={relatedY} width={toolWidth} height={1000}>
<div ref={tooltipRef} className={styles.tooltipDetailsContainer}>
{getTooltipContent(task, fontSize, fontFamily)}
<TooltipContent
task={task}
fontSize={fontSize}
fontFamily={fontFamily}
/>
</div>
</foreignObject>
);
};
const getStandardTooltipContent = (
task: Task,
fontSize: string,
fontFamily: string
) => {
export const StandardTooltipContent: React.FC<{
task: Task;
fontSize: string;
fontFamily: string;
}> = ({ task, fontSize, fontFamily }) => {
const style = {
fontSize,
fontFamily,
@@ -66,9 +75,9 @@ const getStandardTooltipContent = (
(task.end.getTime() - task.start.getTime()) /
(1000 * 60 * 60 * 24)
)} day(s)`}</p>
<p
className={styles.tooltipDefaultContainerParagraph}
>{`Progress: ${task.progress} %`}</p>
<p className={styles.tooltipDefaultContainerParagraph}>
{!!task.progress && `Progress: ${task.progress} %`}
</p>
</div>
);
};
@@ -0,0 +1,23 @@
.ganttTable {
display: table;
border-bottom: #e6e4e4 1px solid;
border-top: #e6e4e4 1px solid;
border-left: #e6e4e4 1px solid;
}
.ganttTable_Header {
display: table-row;
list-style: none;
}
.ganttTable_HeaderSeparator {
border-right: 1px solid rgb(196, 196, 196);
opacity: 1;
margin-left: -2px;
}
.ganttTable_HeaderItem {
display: table-cell;
vertical-align: -webkit-baseline-middle;
vertical-align: middle;
}
@@ -0,0 +1,65 @@
import React from "react";
import styles from "./task-list-header.module.css";
export const TaskListHeaderDefault: React.SFC<{
headerHeight: number;
rowWidth: string;
fontFamily: string;
fontSize: string;
}> = ({ headerHeight, fontFamily, fontSize, rowWidth }) => {
return (
<div
className={styles.ganttTable}
style={{
fontFamily: fontFamily,
fontSize: fontSize,
}}
>
<div
className={styles.ganttTable_Header}
style={{
height: headerHeight - 2,
}}
>
<div
className={styles.ganttTable_HeaderItem}
style={{
minWidth: rowWidth,
}}
>
&nbsp;Name
</div>
<div
className={styles.ganttTable_HeaderSeparator}
style={{
height: headerHeight * 0.5,
marginTop: headerHeight * 0.2,
}}
/>
<div
className={styles.ganttTable_HeaderItem}
style={{
minWidth: rowWidth,
}}
>
&nbsp;From
</div>
<div
className={styles.ganttTable_HeaderSeparator}
style={{
height: headerHeight * 0.5,
marginTop: headerHeight * 0.25,
}}
/>
<div
className={styles.ganttTable_HeaderItem}
style={{
minWidth: rowWidth,
}}
>
&nbsp;To
</div>
</div>
</div>
);
};
@@ -0,0 +1,19 @@
.taskListWrapper {
display: table;
border-bottom: #e6e4e4 1px solid;
border-left: #e6e4e4 1px solid;
}
.taskListTableRow {
display: table-row;
text-overflow: ellipsis;
}
.taskListTableRow:nth-of-type(even) {
background-color: #f5f5f5;
}
.taskListCell {
display: table-cell;
vertical-align: middle;
}
@@ -0,0 +1,64 @@
import React from "react";
import styles from "./task-list-table.module.css";
import { Task } from "../../types/public-types";
export const TaskListTableDefault: React.SFC<{
rowHeight: number;
rowWidth: string;
fontFamily: string;
fontSize: string;
locale: string;
tasks: Task[];
}> = ({ rowHeight, rowWidth, tasks, fontFamily, fontSize, locale }) => {
const dateTimeOptions = {
weekday: "short",
year: "numeric",
month: "long",
day: "numeric",
};
return (
<div
className={styles.taskListWrapper}
style={{
fontFamily: fontFamily,
fontSize: fontSize,
}}
>
{tasks.map(t => {
return (
<div
className={styles.taskListTableRow}
style={{ height: rowHeight }}
key={`${t.id}row`}
>
<div
className={styles.taskListCell}
style={{
minWidth: rowWidth,
maxWidth: rowWidth,
}}
>
&nbsp;{t.name}
</div>
<div
className={styles.taskListCell}
style={{
minWidth: rowWidth,
}}
>
&nbsp;{t.start.toLocaleDateString(locale, dateTimeOptions)}
</div>
<div
className={styles.taskListCell}
style={{
minWidth: rowWidth,
}}
>
&nbsp;{t.end.toLocaleDateString(locale, dateTimeOptions)}
</div>
</div>
);
})}
</div>
);
};
+78
View File
@@ -0,0 +1,78 @@
import React, { useEffect, useRef } from "react";
import { Task } from "../../types/public-types";
export type TaskListProps = {
headerHeight: number;
rowWidth: string;
fontFamily: string;
fontSize: string;
rowHeight: number;
ganttHeight: number;
scroll: number;
locale: string;
tasks: Task[];
horizontalContainerClass?: string;
TaskListHeader: React.SFC<{
headerHeight: number;
rowWidth: string;
fontFamily: string;
fontSize: string;
}>;
TaskListTable: React.SFC<{
rowHeight: number;
rowWidth: string;
fontFamily: string;
fontSize: string;
locale: string;
tasks: Task[];
}>;
};
export const TaskList: React.FC<TaskListProps> = ({
headerHeight,
fontFamily,
fontSize,
rowWidth,
rowHeight,
scroll,
tasks,
locale,
ganttHeight,
horizontalContainerClass,
TaskListHeader,
TaskListTable,
}) => {
const horizontalContainerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (horizontalContainerRef.current) {
horizontalContainerRef.current.scrollTop = scroll;
}
}, [scroll]);
const headerProps = {
headerHeight,
fontFamily,
fontSize,
rowWidth,
};
const tableProps = {
rowHeight,
rowWidth,
fontFamily,
fontSize,
tasks,
locale,
};
return (
<div>
<TaskListHeader {...headerProps} />
<div
ref={horizontalContainerRef}
className={horizontalContainerClass}
style={ganttHeight ? { height: ganttHeight } : {}}
>
<TaskListTable {...tableProps} />
</div>
</div>
);
};
+3 -7
View File
@@ -7,7 +7,6 @@ export const convertToBarTasks = (
columnWidth: number,
rowHeight: number,
barFill: number,
headerHeight: number,
barCornerRadius: number,
handleWidth: number,
barProgressColor: string,
@@ -31,7 +30,6 @@ export const convertToBarTasks = (
columnWidth,
rowHeight,
taskHeight,
headerHeight,
barCornerRadius,
handleWidth,
barProgressColor,
@@ -64,7 +62,6 @@ export const convertToBarTask = (
columnWidth: number,
rowHeight: number,
taskHeight: number,
headerHeight: number,
barCornerRadius: number,
handleWidth: number,
barProgressColor: string,
@@ -74,7 +71,7 @@ export const convertToBarTask = (
): BarTask => {
const x1 = taskXCoordinate(task.start, dates, dateDelta, columnWidth);
const x2 = taskXCoordinate(task.end, dates, dateDelta, columnWidth);
const y = taskYCoordinate(index, rowHeight, taskHeight, headerHeight);
const y = taskYCoordinate(index, rowHeight, taskHeight);
const styles = {
backgroundColor: barBackgroundColor,
@@ -125,10 +122,9 @@ export const taskXCoordinate = (
export const taskYCoordinate = (
index: number,
rowHeight: number,
taskHeight: number,
headerHeight: number
taskHeight: number
) => {
const y = index * rowHeight + headerHeight + (rowHeight - taskHeight) / 2;
const y = index * rowHeight + (rowHeight - taskHeight) / 2;
return y;
};
+1 -1
View File
@@ -1,4 +1,4 @@
export { Gantt } from "./components/Gantt/gantt";
export { Gantt } from "./components/gantt/gantt";
export { ViewMode } from "./types/public-types";
export type {
GanttProps,
+10 -10
View File
@@ -8,6 +8,14 @@
outline: none;
}
.GanttBar-handle {
fill: #ddd;
cursor: ew-resize;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease;
}
.GanttBar-wrapper:hover .GanttBar-handle {
visibility: visible;
opacity: 1;
@@ -31,14 +39,6 @@
text-anchor: start;
}
.GanttBar-handle {
fill: #ddd;
cursor: ew-resize;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease;
}
.GanttCalendar-bottomText {
text-anchor: middle;
fill: #333;
@@ -66,7 +66,7 @@
}
.GanttGrid-row {
fill: #ffffff;
fill: #fff;
}
.GanttGrid-row:nth-child(even) {
@@ -74,7 +74,7 @@
}
.GanttGrid-header {
fill: #ffffff;
fill: #fff;
stroke: #e0e0e0;
stroke-width: 1.4;
}
+21 -5
View File
@@ -47,7 +47,9 @@ export interface DisplayOption {
export interface StylingOption {
headerHeight?: number;
columnWidth?: number;
listCellWidth?: string;
rowHeight?: number;
ganttHeight?: number;
barCornerRadius?: number;
handleWidth?: number;
fontFamily?: string;
@@ -64,11 +66,25 @@ export interface StylingOption {
arrowColor?: string;
arrowIndent?: number;
todayColor?: string;
getTooltipContent?: (
task: Task,
fontSize: string,
fontFamily: string
) => JSX.Element;
TooltipContent?: React.FC<{
task: Task;
fontSize: string;
fontFamily: string;
}>;
TaskListHeader?: React.SFC<{
headerHeight: number;
rowWidth: string;
fontFamily: string;
fontSize: string;
}>;
TaskListTable?: React.SFC<{
rowHeight: number;
rowWidth: string;
fontFamily: string;
fontSize: string;
locale: string;
tasks: Task[];
}>;
}
export interface GanttProps extends EventOption, DisplayOption, StylingOption {