RTL + NEW ROWS
This commit is contained in:
parent
6c51146f91
commit
a85961cc20
@ -64,20 +64,17 @@ const App = () => {
|
||||
isChecked={isChecked}
|
||||
/>
|
||||
<h3>Gantt With Unlimited Height</h3>
|
||||
<div dir="rtl">
|
||||
<Gantt
|
||||
tasks={tasks}
|
||||
viewMode={view}
|
||||
onDateChange={onTaskChange}
|
||||
onDelete={onTaskDelete}
|
||||
onProgressChange={onProgressChange}
|
||||
onDoubleClick={onDblClick}
|
||||
onSelect={onSelect}
|
||||
listCellWidth={isChecked ? "155px" : ""}
|
||||
columnWidth={columnWidth}
|
||||
rtl={true}
|
||||
/>
|
||||
</div>
|
||||
<Gantt
|
||||
tasks={tasks}
|
||||
viewMode={view}
|
||||
onDateChange={onTaskChange}
|
||||
onDelete={onTaskDelete}
|
||||
onProgressChange={onProgressChange}
|
||||
onDoubleClick={onDblClick}
|
||||
onSelect={onSelect}
|
||||
listCellWidth={isChecked ? "155px" : ""}
|
||||
columnWidth={columnWidth}
|
||||
/>
|
||||
<h3>Gantt With Limited Height</h3>
|
||||
<Gantt
|
||||
tasks={tasks}
|
||||
|
||||
@ -2,6 +2,7 @@ import React, { ReactChild } from "react";
|
||||
import { ViewMode } from "../../types/public-types";
|
||||
import { TopPartOfCalendar } from "./top-part-of-calendar";
|
||||
import {
|
||||
getDaysInMonth,
|
||||
getLocaleMonth,
|
||||
getWeekNumberISO8601,
|
||||
} from "../../helpers/date-helper";
|
||||
@ -12,6 +13,7 @@ export type CalendarProps = {
|
||||
dateSetup: DateSetup;
|
||||
locale: string;
|
||||
viewMode: ViewMode;
|
||||
rtl: boolean;
|
||||
headerHeight: number;
|
||||
columnWidth: number;
|
||||
fontFamily: string;
|
||||
@ -22,6 +24,7 @@ export const Calendar: React.FC<CalendarProps> = ({
|
||||
dateSetup,
|
||||
locale,
|
||||
viewMode,
|
||||
rtl,
|
||||
headerHeight,
|
||||
columnWidth,
|
||||
fontFamily,
|
||||
@ -30,7 +33,6 @@ export const Calendar: React.FC<CalendarProps> = ({
|
||||
const getCalendarValuesForMonth = () => {
|
||||
const topValues: ReactChild[] = [];
|
||||
const bottomValues: ReactChild[] = [];
|
||||
const topDefaultWidth = columnWidth * 6;
|
||||
const topDefaultHeight = headerHeight * 0.5;
|
||||
for (let i = 0; i < dateSetup.dates.length; i++) {
|
||||
const date = dateSetup.dates[i];
|
||||
@ -50,6 +52,12 @@ export const Calendar: React.FC<CalendarProps> = ({
|
||||
date.getFullYear() !== dateSetup.dates[i - 1].getFullYear()
|
||||
) {
|
||||
const topValue = date.getFullYear().toString();
|
||||
let xText: number;
|
||||
if (rtl) {
|
||||
xText = (6 + i + date.getMonth() + 1) * columnWidth;
|
||||
} else {
|
||||
xText = (6 + i - date.getMonth()) * columnWidth;
|
||||
}
|
||||
topValues.push(
|
||||
<TopPartOfCalendar
|
||||
key={topValue}
|
||||
@ -57,9 +65,7 @@ export const Calendar: React.FC<CalendarProps> = ({
|
||||
x1Line={columnWidth * i}
|
||||
y1Line={0}
|
||||
y2Line={topDefaultHeight}
|
||||
xText={
|
||||
topDefaultWidth + columnWidth * i - date.getMonth() * columnWidth
|
||||
}
|
||||
xText={xText}
|
||||
yText={topDefaultHeight * 0.9}
|
||||
/>
|
||||
);
|
||||
@ -88,7 +94,7 @@ export const Calendar: React.FC<CalendarProps> = ({
|
||||
<text
|
||||
key={date.getTime()}
|
||||
y={headerHeight * 0.8}
|
||||
x={columnWidth * i}
|
||||
x={columnWidth * (i + +rtl)}
|
||||
className={styles.calendarBottomText}
|
||||
>
|
||||
{bottomValue}
|
||||
@ -149,7 +155,12 @@ export const Calendar: React.FC<CalendarProps> = ({
|
||||
x1Line={columnWidth * (i + 1)}
|
||||
y1Line={0}
|
||||
y2Line={topDefaultHeight}
|
||||
xText={columnWidth * (i + 1) - date.getDate() * columnWidth * 0.5}
|
||||
xText={
|
||||
columnWidth * (i + 1) -
|
||||
getDaysInMonth(date.getMonth(), date.getFullYear()) *
|
||||
columnWidth *
|
||||
0.5
|
||||
}
|
||||
yText={topDefaultHeight * 0.9}
|
||||
/>
|
||||
);
|
||||
@ -174,7 +185,7 @@ export const Calendar: React.FC<CalendarProps> = ({
|
||||
<text
|
||||
key={date.getTime()}
|
||||
y={headerHeight * 0.8}
|
||||
x={columnWidth * i}
|
||||
x={columnWidth * (i + +rtl)}
|
||||
className={styles.calendarBottomText}
|
||||
fontFamily={fontFamily}
|
||||
>
|
||||
@ -196,8 +207,10 @@ export const Calendar: React.FC<CalendarProps> = ({
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return [topValues, bottomValues];
|
||||
};
|
||||
|
||||
let topValues: ReactChild[] = [];
|
||||
let bottomValues: ReactChild[] = [];
|
||||
switch (dateSetup.viewMode) {
|
||||
|
||||
@ -74,19 +74,22 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
||||
const [selectedTask, setSelectedTask] = useState<BarTask>();
|
||||
const [failedTask, setFailedTask] = useState<BarTask | null>(null);
|
||||
|
||||
const [scrollY, setScrollY] = useState(0);
|
||||
const [scrollX, setScrollX] = useState(0);
|
||||
const [ignoreScrollEvent, setIgnoreScrollEvent] = useState(false);
|
||||
|
||||
const svgWidth = dateSetup.dates.length * columnWidth;
|
||||
const ganttFullHeight = barTasks.length * rowHeight;
|
||||
|
||||
const [scrollY, setScrollY] = useState(0);
|
||||
const [scrollX, setScrollX] = useState(-1);
|
||||
const [ignoreScrollEvent, setIgnoreScrollEvent] = useState(false);
|
||||
|
||||
// task change events
|
||||
useEffect(() => {
|
||||
const [startDate, endDate] = ganttDateRange(tasks, viewMode);
|
||||
let newDates = seedDates(startDate, endDate, viewMode);
|
||||
if (rtl) {
|
||||
newDates = newDates.reverse();
|
||||
if (scrollX === -1) {
|
||||
setScrollX(newDates.length * columnWidth);
|
||||
}
|
||||
}
|
||||
setDateSetup({ dates: newDates, viewMode });
|
||||
setBarTasks(
|
||||
@ -130,6 +133,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
||||
milestoneBackgroundColor,
|
||||
milestoneBackgroundSelectedColor,
|
||||
rtl,
|
||||
scrollX,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -211,7 +215,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
||||
}
|
||||
setScrollX(newScrollX);
|
||||
event.preventDefault();
|
||||
} else {
|
||||
} else if (ganttHeight) {
|
||||
let newScrollY = scrollY + event.deltaY;
|
||||
if (newScrollY < 0) {
|
||||
newScrollY = 0;
|
||||
@ -238,7 +242,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
||||
wrapperRef.current.removeEventListener("wheel", handleWheel);
|
||||
}
|
||||
};
|
||||
}, [wrapperRef.current, scrollY, scrollX, ganttHeight, svgWidth]);
|
||||
}, [wrapperRef.current, scrollY, scrollX, ganttHeight, svgWidth, rtl]);
|
||||
|
||||
const handleScrollY = (event: SyntheticEvent<HTMLDivElement>) => {
|
||||
if (scrollY !== event.currentTarget.scrollTop && !ignoreScrollEvent) {
|
||||
@ -326,6 +330,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
||||
rowHeight,
|
||||
dates: dateSetup.dates,
|
||||
todayColor,
|
||||
rtl,
|
||||
};
|
||||
const calendarProps: CalendarProps = {
|
||||
dateSetup,
|
||||
@ -335,6 +340,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
||||
columnWidth,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
rtl,
|
||||
};
|
||||
const barProps: TaskGanttContentProps = {
|
||||
tasks: barTasks,
|
||||
@ -408,6 +414,8 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
||||
headerHeight={headerHeight}
|
||||
taskListWidth={taskListWidth}
|
||||
TooltipContent={TooltipContent}
|
||||
rtl={rtl}
|
||||
svgWidth={svgWidth}
|
||||
/>
|
||||
)}
|
||||
<VerticalScroll
|
||||
@ -416,6 +424,7 @@ export const Gantt: React.FunctionComponent<GanttProps> = ({
|
||||
headerHeight={headerHeight}
|
||||
scroll={scrollY}
|
||||
onScroll={handleScrollY}
|
||||
rtl={rtl}
|
||||
/>
|
||||
</div>
|
||||
<HorizontalScroll
|
||||
|
||||
@ -260,6 +260,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
rowHeight={rowHeight}
|
||||
taskHeight={taskHeight}
|
||||
arrowIndent={arrowIndent}
|
||||
rtl={rtl}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@ -278,6 +279,7 @@ export const TaskGanttContent: React.FC<TaskGanttContentProps> = ({
|
||||
onEventStart={handleBarEventStart}
|
||||
key={task.id}
|
||||
isSelected={!!selectedTask && task.id === selectedTask.id}
|
||||
rtl={rtl}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@ -41,6 +41,7 @@ export const TaskGantt: React.FC<TaskGanttProps> = ({
|
||||
<div
|
||||
className={styles.ganttVerticalContainer}
|
||||
ref={verticalGanttContainerRef}
|
||||
dir="ltr"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@ -10,6 +10,7 @@ export type GridBodyProps = {
|
||||
rowHeight: number;
|
||||
columnWidth: number;
|
||||
todayColor: string;
|
||||
rtl: boolean;
|
||||
};
|
||||
export const GridBody: React.FC<GridBodyProps> = ({
|
||||
tasks,
|
||||
@ -18,6 +19,7 @@ export const GridBody: React.FC<GridBodyProps> = ({
|
||||
svgWidth,
|
||||
columnWidth,
|
||||
todayColor,
|
||||
rtl,
|
||||
}) => {
|
||||
let y = 0;
|
||||
const gridRows: ReactChild[] = [];
|
||||
@ -95,6 +97,23 @@ export const GridBody: React.FC<GridBodyProps> = ({
|
||||
/>
|
||||
);
|
||||
}
|
||||
// rtl for today
|
||||
if (
|
||||
rtl &&
|
||||
i + 1 !== dates.length &&
|
||||
date.getTime() >= now.getTime() &&
|
||||
dates[i + 1].getTime() < now.getTime()
|
||||
) {
|
||||
today = (
|
||||
<rect
|
||||
x={tickX + columnWidth}
|
||||
y={0}
|
||||
width={columnWidth}
|
||||
height={y}
|
||||
fill={todayColor}
|
||||
/>
|
||||
);
|
||||
}
|
||||
tickX += columnWidth;
|
||||
}
|
||||
return (
|
||||
|
||||
@ -7,6 +7,7 @@ type ArrowProps = {
|
||||
rowHeight: number;
|
||||
taskHeight: number;
|
||||
arrowIndent: number;
|
||||
rtl: boolean;
|
||||
};
|
||||
export const Arrow: React.FC<ArrowProps> = ({
|
||||
taskFrom,
|
||||
@ -14,19 +15,28 @@ export const Arrow: React.FC<ArrowProps> = ({
|
||||
rowHeight,
|
||||
taskHeight,
|
||||
arrowIndent,
|
||||
rtl,
|
||||
}) => {
|
||||
const indexCompare = taskFrom.index > taskTo.index ? -1 : 1;
|
||||
const taskToEndPosition = taskTo.y + taskHeight / 2;
|
||||
let path: string;
|
||||
let trianglePoints: string;
|
||||
if (rtl) {
|
||||
[path, trianglePoints] = drownPathAndTriangleRTL(
|
||||
taskFrom,
|
||||
taskTo,
|
||||
rowHeight,
|
||||
taskHeight,
|
||||
arrowIndent
|
||||
);
|
||||
} else {
|
||||
[path, trianglePoints] = drownPathAndTriangle(
|
||||
taskFrom,
|
||||
taskTo,
|
||||
rowHeight,
|
||||
taskHeight,
|
||||
arrowIndent
|
||||
);
|
||||
}
|
||||
|
||||
const path = `M ${taskFrom.x2} ${taskFrom.y + taskHeight / 2}
|
||||
h ${arrowIndent}
|
||||
v ${(indexCompare * rowHeight) / 2}
|
||||
H ${taskTo.x1 - arrowIndent}
|
||||
V ${taskToEndPosition}
|
||||
h ${arrowIndent}`;
|
||||
const trianglePoints = `${taskTo.x1},${taskToEndPosition}
|
||||
${taskTo.x1 - 5},${taskToEndPosition - 5}
|
||||
${taskTo.x1 - 5},${taskToEndPosition + 5}`;
|
||||
return (
|
||||
<g className="arrow">
|
||||
<path strokeWidth="1.5" d={path} fill="none" />
|
||||
@ -34,3 +44,63 @@ export const Arrow: React.FC<ArrowProps> = ({
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
const drownPathAndTriangle = (
|
||||
taskFrom: BarTask,
|
||||
taskTo: BarTask,
|
||||
rowHeight: number,
|
||||
taskHeight: number,
|
||||
arrowIndent: number
|
||||
) => {
|
||||
const indexCompare = taskFrom.index > taskTo.index ? -1 : 1;
|
||||
const taskToEndPosition = taskTo.y + taskHeight / 2;
|
||||
const taskFromEndPosition = taskFrom.x2 + arrowIndent * 2;
|
||||
const taskFromHorizontalOffsetValue =
|
||||
taskFromEndPosition < taskTo.x1 ? "" : `H ${taskTo.x1 - arrowIndent}`;
|
||||
const taskToHorizontalOffsetValue =
|
||||
taskFromEndPosition > taskTo.x1
|
||||
? arrowIndent
|
||||
: taskTo.x1 - taskFrom.x2 - arrowIndent;
|
||||
|
||||
const path = `M ${taskFrom.x2} ${taskFrom.y + taskHeight / 2}
|
||||
h ${arrowIndent}
|
||||
v ${(indexCompare * rowHeight) / 2}
|
||||
${taskFromHorizontalOffsetValue}
|
||||
V ${taskToEndPosition}
|
||||
h ${taskToHorizontalOffsetValue}`;
|
||||
|
||||
const trianglePoints = `${taskTo.x1},${taskToEndPosition}
|
||||
${taskTo.x1 - 5},${taskToEndPosition - 5}
|
||||
${taskTo.x1 - 5},${taskToEndPosition + 5}`;
|
||||
return [path, trianglePoints];
|
||||
};
|
||||
|
||||
const drownPathAndTriangleRTL = (
|
||||
taskFrom: BarTask,
|
||||
taskTo: BarTask,
|
||||
rowHeight: number,
|
||||
taskHeight: number,
|
||||
arrowIndent: number
|
||||
) => {
|
||||
const indexCompare = taskFrom.index > taskTo.index ? -1 : 1;
|
||||
const taskToEndPosition = taskTo.y + taskHeight / 2;
|
||||
const taskFromEndPosition = taskFrom.x1 - arrowIndent * 2;
|
||||
const taskFromHorizontalOffsetValue =
|
||||
taskFromEndPosition > taskTo.x2 ? "" : `H ${taskTo.x2 + arrowIndent}`;
|
||||
const taskToHorizontalOffsetValue =
|
||||
taskFromEndPosition < taskTo.x2
|
||||
? -arrowIndent
|
||||
: taskTo.x2 - taskFrom.x1 + arrowIndent;
|
||||
|
||||
const path = `M ${taskFrom.x1} ${taskFrom.y + taskHeight / 2}
|
||||
h ${-arrowIndent}
|
||||
v ${(indexCompare * rowHeight) / 2}
|
||||
${taskFromHorizontalOffsetValue}
|
||||
V ${taskToEndPosition}
|
||||
h ${taskToHorizontalOffsetValue}`;
|
||||
|
||||
const trianglePoints = `${taskTo.x2},${taskToEndPosition}
|
||||
${taskTo.x2 + 5},${taskToEndPosition + 5}
|
||||
${taskTo.x2 + 5},${taskToEndPosition - 5}`;
|
||||
return [path, trianglePoints];
|
||||
};
|
||||
|
||||
@ -18,6 +18,7 @@ export const HorizontalScroll: React.FC<{
|
||||
|
||||
return (
|
||||
<div
|
||||
dir="ltr"
|
||||
style={{
|
||||
margin: rtl
|
||||
? `0px ${taskListWidth}px 0px 0px`
|
||||
|
||||
@ -6,8 +6,10 @@ import styles from "./tooltip.module.css";
|
||||
export type TooltipProps = {
|
||||
task: BarTask;
|
||||
arrowIndent: number;
|
||||
rtl: boolean;
|
||||
svgContainerHeight: number;
|
||||
svgContainerWidth: number;
|
||||
svgWidth: number;
|
||||
headerHeight: number;
|
||||
taskListWidth: number;
|
||||
scrollX: number;
|
||||
@ -24,6 +26,7 @@ export type TooltipProps = {
|
||||
export const Tooltip: React.FC<TooltipProps> = ({
|
||||
task,
|
||||
rowHeight,
|
||||
rtl,
|
||||
svgContainerHeight,
|
||||
svgContainerWidth,
|
||||
scrollX,
|
||||
@ -40,30 +43,41 @@ export const Tooltip: React.FC<TooltipProps> = ({
|
||||
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;
|
||||
const tooltipWidth = tooltipRef.current.offsetWidth * 1.1;
|
||||
|
||||
const tooltipLowerPoint = tooltipHeight + newRelatedY - scrollY;
|
||||
const tooltipLeftmostPoint = tooltipWidth + newRelatedX;
|
||||
const fullChartWidth = taskListWidth + svgContainerWidth;
|
||||
|
||||
if (tooltipLeftmostPoint > fullChartWidth) {
|
||||
newRelatedX =
|
||||
task.x1 +
|
||||
taskListWidth -
|
||||
arrowIndent -
|
||||
arrowIndent * 0.5 -
|
||||
scrollX -
|
||||
tooltipWidth;
|
||||
let newRelatedY = task.index * rowHeight - scrollY + headerHeight;
|
||||
let newRelatedX: number;
|
||||
if (rtl) {
|
||||
newRelatedX = task.x1 - arrowIndent * 1.5 - tooltipWidth - scrollX;
|
||||
if (newRelatedX < 0) {
|
||||
newRelatedX = task.x2 + arrowIndent * 1.5 - scrollX;
|
||||
}
|
||||
const tooltipLeftmostPoint = tooltipWidth + newRelatedX;
|
||||
if (tooltipLeftmostPoint > svgContainerWidth) {
|
||||
newRelatedX = svgContainerWidth - tooltipWidth;
|
||||
newRelatedY += rowHeight;
|
||||
}
|
||||
} else {
|
||||
newRelatedX = task.x2 + arrowIndent * 1.5 + taskListWidth - scrollX;
|
||||
const tooltipLeftmostPoint = tooltipWidth + newRelatedX;
|
||||
const fullChartWidth = taskListWidth + svgContainerWidth;
|
||||
if (tooltipLeftmostPoint > fullChartWidth) {
|
||||
newRelatedX =
|
||||
task.x1 +
|
||||
taskListWidth -
|
||||
arrowIndent * 1.5 -
|
||||
scrollX -
|
||||
tooltipWidth;
|
||||
}
|
||||
if (newRelatedX < taskListWidth) {
|
||||
newRelatedX = svgContainerWidth + taskListWidth - tooltipWidth;
|
||||
newRelatedY += rowHeight;
|
||||
}
|
||||
}
|
||||
if (newRelatedX < taskListWidth) {
|
||||
newRelatedX = svgContainerWidth + taskListWidth - tooltipWidth;
|
||||
newRelatedY += rowHeight;
|
||||
} else if (tooltipLowerPoint > svgContainerHeight - scrollY) {
|
||||
|
||||
const tooltipLowerPoint = tooltipHeight + newRelatedY - scrollY;
|
||||
if (tooltipLowerPoint > svgContainerHeight - scrollY) {
|
||||
newRelatedY = svgContainerHeight - tooltipHeight;
|
||||
}
|
||||
setRelatedY(newRelatedY);
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
.scroll {
|
||||
overflow: hidden auto;
|
||||
margin-left: -17px;
|
||||
width: 17px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@ -6,8 +6,16 @@ export const VerticalScroll: React.FC<{
|
||||
ganttHeight: number;
|
||||
ganttFullHeight: number;
|
||||
headerHeight: number;
|
||||
rtl: boolean;
|
||||
onScroll: (event: SyntheticEvent<HTMLDivElement>) => void;
|
||||
}> = ({ scroll, ganttHeight, ganttFullHeight, headerHeight, onScroll }) => {
|
||||
}> = ({
|
||||
scroll,
|
||||
ganttHeight,
|
||||
ganttFullHeight,
|
||||
headerHeight,
|
||||
rtl,
|
||||
onScroll,
|
||||
}) => {
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@ -18,7 +26,11 @@ export const VerticalScroll: React.FC<{
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ height: ganttHeight, marginTop: headerHeight }}
|
||||
style={{
|
||||
height: ganttHeight,
|
||||
marginTop: headerHeight,
|
||||
marginLeft: rtl ? "" : "-17px",
|
||||
}}
|
||||
className={styles.scroll}
|
||||
onScroll={onScroll}
|
||||
ref={scrollRef}
|
||||
|
||||
@ -10,11 +10,12 @@ export const Bar: React.FC<TaskItemProps> = ({
|
||||
task,
|
||||
isProgressChangeable,
|
||||
isDateChangeable,
|
||||
rtl,
|
||||
onEventStart,
|
||||
isSelected,
|
||||
}) => {
|
||||
const progressPoint = getProgressPoint(
|
||||
task.progressWidth + task.x1,
|
||||
+!rtl * task.progressWidth + task.progressX,
|
||||
task.y,
|
||||
task.height
|
||||
);
|
||||
|
||||
@ -15,6 +15,7 @@ export type TaskItemProps = {
|
||||
isDateChangeable: boolean;
|
||||
isDelete: boolean;
|
||||
isSelected: boolean;
|
||||
rtl: boolean;
|
||||
onEventStart: (
|
||||
action: GanttContentMoveAction,
|
||||
selectedTask: BarTask,
|
||||
@ -29,6 +30,7 @@ export const TaskItem: React.FC<TaskItemProps> = props => {
|
||||
isDelete,
|
||||
taskHeight,
|
||||
isSelected,
|
||||
rtl,
|
||||
onEventStart,
|
||||
} = {
|
||||
...props,
|
||||
@ -63,9 +65,19 @@ export const TaskItem: React.FC<TaskItemProps> = props => {
|
||||
const getX = () => {
|
||||
const width = task.x2 - task.x1;
|
||||
const hasChild = task.barChildren.length > 0;
|
||||
return isTextInside
|
||||
? task.x1 + width * 0.5
|
||||
: task.x1 + width + arrowIndent * +hasChild + arrowIndent * 0.2;
|
||||
if (isTextInside) {
|
||||
return task.x1 + width * 0.5;
|
||||
}
|
||||
if (rtl && textRef.current) {
|
||||
return (
|
||||
task.x1 -
|
||||
textRef.current.getBBox().width -
|
||||
arrowIndent * +hasChild -
|
||||
arrowIndent * 0.2
|
||||
);
|
||||
} else {
|
||||
return task.x1 + width + arrowIndent * +hasChild + arrowIndent * 0.2;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -12,7 +12,7 @@ export const TaskListTableDefault: React.FC<{
|
||||
selectedTaskId: string;
|
||||
setSelectedTask: (taskId: string) => void;
|
||||
}> = ({ rowHeight, rowWidth, tasks, fontFamily, fontSize, locale }) => {
|
||||
const dateTimeOptions = {
|
||||
const dateTimeOptions: Intl.DateTimeFormatOptions = {
|
||||
weekday: "short",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
|
||||
@ -165,8 +165,8 @@ const convertToBar = (
|
||||
let x1: number;
|
||||
let x2: number;
|
||||
if (rtl) {
|
||||
x2 = taskXCoordinate(task.start, dates, dateDelta, columnWidth);
|
||||
x1 = taskXCoordinate(task.end, dates, dateDelta, columnWidth);
|
||||
x2 = taskXCoordinateRTL(task.start, dates, dateDelta, columnWidth);
|
||||
x1 = taskXCoordinateRTL(task.end, dates, dateDelta, columnWidth);
|
||||
} else {
|
||||
x1 = taskXCoordinate(task.start, dates, dateDelta, columnWidth);
|
||||
x2 = taskXCoordinate(task.end, dates, dateDelta, columnWidth);
|
||||
@ -177,14 +177,12 @@ const convertToBar = (
|
||||
x2 = x1 + handleWidth * 2;
|
||||
}
|
||||
|
||||
const progressWidth = progressWithByParams(x1, x2, task.progress);
|
||||
let progressX: number;
|
||||
if (rtl) {
|
||||
progressX = x2 - progressWidth;
|
||||
} else {
|
||||
progressX = x1;
|
||||
}
|
||||
|
||||
const [progressWidth, progressX] = progressWithByParams(
|
||||
x1,
|
||||
x2,
|
||||
task.progress,
|
||||
rtl
|
||||
);
|
||||
const y = taskYCoordinate(index, rowHeight, taskHeight);
|
||||
|
||||
const styles = {
|
||||
@ -281,7 +279,16 @@ const taskXCoordinate = (
|
||||
);
|
||||
return x;
|
||||
};
|
||||
|
||||
const taskXCoordinateRTL = (
|
||||
xDate: Date,
|
||||
dates: Date[],
|
||||
dateDelta: number,
|
||||
columnWidth: number
|
||||
) => {
|
||||
let x = taskXCoordinate(xDate, dates, dateDelta, columnWidth);
|
||||
x += columnWidth;
|
||||
return x;
|
||||
};
|
||||
const taskYCoordinate = (
|
||||
index: number,
|
||||
rowHeight: number,
|
||||
@ -294,9 +301,17 @@ const taskYCoordinate = (
|
||||
export const progressWithByParams = (
|
||||
taskX1: number,
|
||||
taskX2: number,
|
||||
progress: number
|
||||
progress: number,
|
||||
rtl: boolean
|
||||
) => {
|
||||
return (taskX2 - taskX1) * progress * 0.01;
|
||||
const progressWidth = (taskX2 - taskX1) * progress * 0.01;
|
||||
let progressX: number;
|
||||
if (rtl) {
|
||||
progressX = taskX2 - progressWidth;
|
||||
} else {
|
||||
progressX = taskX1;
|
||||
}
|
||||
return [progressWidth, progressX];
|
||||
};
|
||||
|
||||
export const progressByProgressWidth = (
|
||||
@ -319,6 +334,15 @@ const progressByX = (x: number, task: BarTask) => {
|
||||
return progressPercent;
|
||||
}
|
||||
};
|
||||
const progressByXRTL = (x: number, task: BarTask) => {
|
||||
if (x >= task.x2) return 0;
|
||||
else if (x <= task.x1) return 100;
|
||||
else {
|
||||
const barWidth = task.x2 - task.x1;
|
||||
const progressPercent = Math.round(((task.x2 - x) * 100) / barWidth);
|
||||
return progressPercent;
|
||||
}
|
||||
};
|
||||
|
||||
export const getProgressPoint = (
|
||||
progressX: number,
|
||||
@ -431,19 +455,21 @@ const handleTaskBySVGMouseEventForBar = (
|
||||
let isChanged = false;
|
||||
switch (action) {
|
||||
case "progress":
|
||||
changedTask.progress = progressByX(svgX, selectedTask);
|
||||
if (rtl) {
|
||||
changedTask.progress = progressByXRTL(svgX, selectedTask);
|
||||
} else {
|
||||
changedTask.progress = progressByX(svgX, selectedTask);
|
||||
}
|
||||
isChanged = changedTask.progress !== selectedTask.progress;
|
||||
if (isChanged) {
|
||||
changedTask.progressWidth = progressWithByParams(
|
||||
const [progressWidth, progressX] = progressWithByParams(
|
||||
changedTask.x1,
|
||||
changedTask.x2,
|
||||
changedTask.progress
|
||||
changedTask.progress,
|
||||
rtl
|
||||
);
|
||||
if (rtl) {
|
||||
changedTask.progressX = changedTask.x2 - changedTask.progressWidth;
|
||||
} else {
|
||||
changedTask.progressX = changedTask.x1;
|
||||
}
|
||||
changedTask.progressWidth = progressWidth;
|
||||
changedTask.progressX = progressX;
|
||||
}
|
||||
break;
|
||||
case "start": {
|
||||
@ -451,13 +477,31 @@ const handleTaskBySVGMouseEventForBar = (
|
||||
changedTask.x1 = newX1;
|
||||
isChanged = changedTask.x1 !== selectedTask.x1;
|
||||
if (isChanged) {
|
||||
changedTask.start = dateByX(
|
||||
newX1,
|
||||
selectedTask.x1,
|
||||
selectedTask.start,
|
||||
xStep,
|
||||
timeStep
|
||||
if (rtl) {
|
||||
changedTask.end = dateByX(
|
||||
newX1,
|
||||
selectedTask.x1,
|
||||
selectedTask.end,
|
||||
xStep,
|
||||
timeStep
|
||||
);
|
||||
} else {
|
||||
changedTask.start = dateByX(
|
||||
newX1,
|
||||
selectedTask.x1,
|
||||
selectedTask.start,
|
||||
xStep,
|
||||
timeStep
|
||||
);
|
||||
}
|
||||
const [progressWidth, progressX] = progressWithByParams(
|
||||
changedTask.x1,
|
||||
changedTask.x2,
|
||||
changedTask.progress,
|
||||
rtl
|
||||
);
|
||||
changedTask.progressWidth = progressWidth;
|
||||
changedTask.progressX = progressX;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -466,13 +510,31 @@ const handleTaskBySVGMouseEventForBar = (
|
||||
changedTask.x2 = newX2;
|
||||
isChanged = changedTask.x2 !== selectedTask.x2;
|
||||
if (isChanged) {
|
||||
changedTask.end = dateByX(
|
||||
newX2,
|
||||
selectedTask.x2,
|
||||
selectedTask.end,
|
||||
xStep,
|
||||
timeStep
|
||||
if (rtl) {
|
||||
changedTask.start = dateByX(
|
||||
newX2,
|
||||
selectedTask.x2,
|
||||
selectedTask.start,
|
||||
xStep,
|
||||
timeStep
|
||||
);
|
||||
} else {
|
||||
changedTask.end = dateByX(
|
||||
newX2,
|
||||
selectedTask.x2,
|
||||
selectedTask.end,
|
||||
xStep,
|
||||
timeStep
|
||||
);
|
||||
}
|
||||
const [progressWidth, progressX] = progressWithByParams(
|
||||
changedTask.x1,
|
||||
changedTask.x2,
|
||||
changedTask.progress,
|
||||
rtl
|
||||
);
|
||||
changedTask.progressWidth = progressWidth;
|
||||
changedTask.progressX = progressX;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -500,6 +562,14 @@ const handleTaskBySVGMouseEventForBar = (
|
||||
);
|
||||
changedTask.x1 = newMoveX1;
|
||||
changedTask.x2 = newMoveX2;
|
||||
const [progressWidth, progressX] = progressWithByParams(
|
||||
changedTask.x1,
|
||||
changedTask.x2,
|
||||
changedTask.progress,
|
||||
rtl
|
||||
);
|
||||
changedTask.progressWidth = progressWidth;
|
||||
changedTask.progressX = progressX;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -83,11 +83,17 @@ export const ganttDateRange = (tasks: Task[], viewMode: ViewMode) => {
|
||||
newStartDate = addToDate(newStartDate, -1, "day");
|
||||
newEndDate = addToDate(newEndDate, 19, "day");
|
||||
break;
|
||||
default:
|
||||
case ViewMode.QuarterDay:
|
||||
newStartDate = startOfDate(newStartDate, "day");
|
||||
newEndDate = startOfDate(newEndDate, "day");
|
||||
newStartDate = addToDate(newStartDate, -1, "day");
|
||||
newEndDate = addToDate(newEndDate, 5, "day");
|
||||
newEndDate = addToDate(newEndDate, 66, "hour"); // 24(1 day)*3 - 6
|
||||
break;
|
||||
case ViewMode.HalfDay:
|
||||
newStartDate = startOfDate(newStartDate, "day");
|
||||
newEndDate = startOfDate(newEndDate, "day");
|
||||
newStartDate = addToDate(newStartDate, -1, "day");
|
||||
newEndDate = addToDate(newEndDate, 108, "hour"); // 24(1 day)*5 - 12
|
||||
break;
|
||||
}
|
||||
return [newStartDate, newEndDate];
|
||||
@ -163,3 +169,7 @@ export const getWeekNumberISO8601 = (date: Date) => {
|
||||
return weekNumber;
|
||||
}
|
||||
};
|
||||
|
||||
export const getDaysInMonth = (month: number, year: number) => {
|
||||
return new Date(year, month + 1, 0).getDate();
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user