toway-vtable-gantt
Version:
canvas table width high performance
955 lines (948 loc) • 375 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@visactor/vtable/es/vrender'), require('@visactor/vtable')) :
typeof define === 'function' && define.amd ? define(['exports', '@visactor/vtable/es/vrender', '@visactor/vtable'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.VTableGantt = {}, global.vrender$1, global.vtable$1));
})(this, (function (exports, vrender$1, vtable$1) { 'use strict';
var InteractionState;
(function (InteractionState) {
InteractionState["default"] = "default";
InteractionState["grabing"] = "grabing";
InteractionState["scrolling"] = "scrolling";
})(InteractionState || (InteractionState = {}));
var DependencyType;
(function (DependencyType) {
DependencyType["FinishToStart"] = "finish_to_start";
DependencyType["StartToStart"] = "start_to_start";
DependencyType["FinishToFinish"] = "finish_to_finish";
DependencyType["StartToFinish"] = "start_to_finish";
})(DependencyType || (DependencyType = {}));
var TasksShowMode;
(function (TasksShowMode) {
TasksShowMode["Tasks_Separate"] = "tasks_separate";
TasksShowMode["Sub_Tasks_Inline"] = "sub_tasks_inline";
TasksShowMode["Sub_Tasks_Separate"] = "sub_tasks_separate";
TasksShowMode["Sub_Tasks_Arrange"] = "sub_tasks_arrange";
TasksShowMode["Sub_Tasks_Compact"] = "sub_tasks_compact";
})(TasksShowMode || (TasksShowMode = {}));
const GANTT_EVENT_TYPE = {
SCROLL: 'scroll',
CHANGE_DATE_RANGE: 'change_date_range',
CLICK_TASK_BAR: 'click_task_bar',
CONTEXTMENU_TASK_BAR: 'contextmenu_task_bar',
MOUSEENTER_TASK_BAR: 'mouseenter_task_bar',
MOUSELEAVE_TASK_BAR: 'mouseleave_task_bar',
CREATE_TASK_SCHEDULE: 'create_task_schedule',
CREATE_DEPENDENCY_LINK: 'create_dependency_link',
DELETE_DEPENDENCY_LINK: 'delete_dependency_link',
CLICK_DEPENDENCY_LINK_POINT: 'click_dependency_link_point',
CONTEXTMENU_DEPENDENCY_LINK: 'contextmenu_dependency_link',
CLICK_MARKLINE_CREATE: 'click_markline_create',
CLICK_MARKLINE_CONTENT: 'click_markline_content'
};
var index$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
get DependencyType () { return DependencyType; },
GANTT_EVENT_TYPE: GANTT_EVENT_TYPE,
get InteractionState () { return InteractionState; },
get TasksShowMode () { return TasksShowMode; }
});
const defaultTaskBarStyle = {
barColor: 'blue',
completedBarColor: 'gray',
width: 30,
cornerRadius: 3,
borderWidth: 0,
fontFamily: 'Arial',
fontSize: 14
};
function setWidthToDefaultTaskBarStyle(width) {
defaultTaskBarStyle.width = width;
}
const isNode = typeof window === 'undefined' || typeof window.window === 'undefined';
const DayTimes = 1000 * 60 * 60 * 24;
function getDateIndexByX(x, gantt) {
const totalX = x + gantt.stateManager.scroll.horizontalBarPos;
const firstDateColWidth = gantt.getDateColWidth(0);
const dateIndex = Math.floor((totalX - firstDateColWidth) / gantt.parsedOptions.timelineColWidth) + 1;
return dateIndex;
}
function generateMarkLine(markLine) {
if (!markLine) {
return [];
}
if (markLine === true) {
return [
{
date: createDateAtMidnight().toLocaleDateString(),
scrollToMarkLine: true,
position: 'left',
style: {
lineColor: 'red',
lineWidth: 1
}
}
];
}
else if (Array.isArray(markLine)) {
return markLine.map((item, index) => {
return {
...item,
date: item.date,
scrollToMarkLine: item.scrollToMarkLine,
position: item.position ?? 'left',
style: {
lineColor: item.style?.lineColor || 'red',
lineWidth: item.style?.lineWidth || 1,
lineDash: item.style?.lineDash
}
};
});
}
return [
{
...markLine,
date: markLine.date,
scrollToMarkLine: markLine.scrollToMarkLine ?? true,
position: markLine.position ?? 'left',
style: {
lineColor: markLine.style?.lineColor || 'red',
lineWidth: markLine.style?.lineWidth || 1,
lineDash: markLine.style?.lineDash
}
}
];
}
function getHorizontalScrollBarSize(scrollStyle) {
if (scrollStyle?.hoverOn ||
(scrollStyle?.horizontalVisible && scrollStyle?.horizontalVisible === 'none') ||
(!scrollStyle?.horizontalVisible && scrollStyle?.visible === 'none')) {
return 0;
}
return scrollStyle?.width ?? 7;
}
function getVerticalScrollBarSize(scrollStyle) {
if (scrollStyle?.hoverOn ||
(scrollStyle?.verticalVisible && scrollStyle?.verticalVisible === 'none') ||
(!scrollStyle?.verticalVisible && scrollStyle?.visible === 'none')) {
return 0;
}
return scrollStyle?.width ?? 7;
}
function initOptions(gantt) {
const options = gantt.options;
gantt.parsedOptions.tasksShowMode = options?.tasksShowMode ?? TasksShowMode.Tasks_Separate;
gantt.parsedOptions.pixelRatio = options?.pixelRatio ?? 1;
gantt.parsedOptions.rowHeight = options?.rowHeight ?? 40;
gantt.parsedOptions.timelineColWidth = options?.timelineHeader?.colWidth ?? 60;
gantt.parsedOptions.startDateField = options.taskBar?.startDateField ?? 'startDate';
gantt.parsedOptions.endDateField = options.taskBar?.endDateField ?? 'endDate';
gantt.parsedOptions.progressField = options.taskBar?.progressField ?? 'progress';
const { unit: minTimeUnit, startOfWeek, step } = gantt.parsedOptions.reverseSortedTimelineScales[0];
gantt.parsedOptions.minDate = options?.minDate
? getStartDateByTimeUnit(new Date(options.minDate), minTimeUnit, startOfWeek)
: undefined;
gantt.parsedOptions.maxDate = options?.maxDate
? getEndDateByTimeUnit(gantt.parsedOptions.minDate, new Date(options.maxDate), minTimeUnit, step)
: undefined;
gantt.parsedOptions._minDateTime = gantt.parsedOptions.minDate?.getTime();
gantt.parsedOptions._maxDateTime = gantt.parsedOptions.maxDate?.getTime();
gantt.parsedOptions.overscrollBehavior = options?.overscrollBehavior ?? 'auto';
gantt.parsedOptions.underlayBackgroundColor = options?.underlayBackgroundColor ?? '#FFF';
gantt.parsedOptions.scrollStyle = Object.assign({}, {
scrollRailColor: 'rgba(100, 100, 100, 0.2)',
scrollSliderColor: 'rgba(100, 100, 100, 0.5)',
scrollSliderCornerRadius: 4,
width: 10,
visible: 'always',
hoverOn: true,
barToSide: false
}, options?.scrollStyle);
gantt.parsedOptions.timelineHeaderHorizontalLineStyle = options?.timelineHeader?.horizontalLine;
gantt.parsedOptions.timelineHeaderVerticalLineStyle = options?.timelineHeader?.verticalLine;
gantt.parsedOptions.timelineHeaderBackgroundColor = options?.timelineHeader?.backgroundColor;
gantt.parsedOptions.timeLineHeaderRowHeights = [];
gantt.parsedOptions.timelineHeaderStyles = [];
for (let i = 0; i < gantt.parsedOptions.sortedTimelineScales.length ?? 0; i++) {
const style = gantt.parsedOptions.sortedTimelineScales[i].style;
gantt.parsedOptions.timelineHeaderStyles.push(Object.assign({
fontSize: 20,
fontWeight: 'bold',
textAlign: 'center',
textBaseline: 'middle',
color: '#000',
backgroundColor: '#fff'
}, style));
gantt.parsedOptions.timeLineHeaderRowHeights.push(gantt.parsedOptions.sortedTimelineScales[i].rowHeight ?? options?.headerRowHeight ?? 40);
}
gantt.parsedOptions.grid = Object.assign({}, options?.grid);
setWidthToDefaultTaskBarStyle((gantt.parsedOptions.rowHeight * 3) / 4);
gantt.parsedOptions.taskBarStyle =
options?.taskBar?.barStyle && typeof options?.taskBar?.barStyle === 'function'
? options.taskBar.barStyle
: Object.assign({}, defaultTaskBarStyle, options?.taskBar?.barStyle);
gantt.parsedOptions.taskBarMilestoneStyle = Object.assign(typeof gantt.parsedOptions.taskBarStyle === 'function'
? {}
: {
width: gantt.parsedOptions.taskBarStyle.width,
borderColor: gantt.parsedOptions.taskBarStyle.borderColor,
borderLineWidth: gantt.parsedOptions.taskBarStyle.borderLineWidth ?? 1,
fillColor: gantt.parsedOptions.taskBarStyle.barColor,
cornerRadius: 0
}, options?.taskBar?.milestoneStyle);
gantt.parsedOptions.taskBarMilestoneHypotenuse = gantt.parsedOptions.taskBarMilestoneStyle.width * Math.sqrt(2);
gantt.parsedOptions.dateFormat = options?.dateFormat;
gantt.parsedOptions.taskBarHoverStyle = Object.assign({
barOverlayColor: 'rgba(99, 144, 0, 0.4)'
}, options?.taskBar?.hoverBarStyle);
gantt.parsedOptions.taskBarSelectable = options?.taskBar?.selectable ?? true;
gantt.parsedOptions.taskBarSelectedStyle = Object.assign(typeof gantt.parsedOptions.taskBarStyle === 'function'
? {
shadowBlur: 6,
shadowOffsetX: 0,
shadowOffsetY: 0,
borderLineWidth: 1
}
: {
shadowBlur: 6,
shadowOffsetX: 0,
shadowOffsetY: 0,
shadowColor: gantt.parsedOptions.taskBarStyle.barColor,
borderColor: gantt.parsedOptions.taskBarStyle.barColor,
borderLineWidth: 1
}, options?.taskBar?.selectedBarStyle);
gantt.parsedOptions.taskBarLabelText = options?.taskBar?.labelText ?? '';
gantt.parsedOptions.taskBarMoveable = options?.taskBar?.moveable ?? true;
gantt.parsedOptions.moveTaskBarToExtendDateRange = options?.taskBar?.moveToExtendDateRange ?? true;
gantt.parsedOptions.taskBarResizable = options?.taskBar?.resizable ?? true;
gantt.parsedOptions.taskBarDragOrder = options?.taskBar?.dragOrder ?? true;
gantt.parsedOptions.taskBarLabelStyle = {
fontFamily: options?.taskBar?.labelTextStyle?.fontFamily ?? 'Arial',
fontSize: options?.taskBar?.labelTextStyle?.fontSize ?? 20,
color: options?.taskBar?.labelTextStyle?.color ?? '#F01',
textAlign: options?.taskBar?.labelTextStyle?.textAlign ?? 'left',
textBaseline: options?.taskBar?.labelTextStyle?.textBaseline ?? 'middle',
padding: options?.taskBar?.labelTextStyle?.padding ?? [0, 0, 0, 10],
textOverflow: options?.taskBar?.labelTextStyle?.textOverflow
};
gantt.parsedOptions.taskBarCustomLayout = options?.taskBar?.customLayout;
gantt.parsedOptions.taskBarCreatable =
options?.taskBar?.scheduleCreatable ??
!!(gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate ||
gantt.parsedOptions.tasksShowMode === TasksShowMode.Tasks_Separate);
gantt.parsedOptions.taskBarCreationButtonStyle = Object.assign({
lineColor: 'rgb(99, 144, 0)',
lineWidth: 1,
lineDash: [5, 5],
cornerRadius: 4,
backgroundColor: '#FFF'
}, options?.taskBar?.scheduleCreation?.buttonStyle);
gantt.parsedOptions.taskBarCreationCustomLayout = options?.taskBar?.scheduleCreation?.customLayout;
gantt.parsedOptions.taskBarCreationMaxWidth = options?.taskBar?.scheduleCreation?.maxWidth;
gantt.parsedOptions.taskBarCreationMinWidth = options?.taskBar?.scheduleCreation?.minWidth;
gantt.parsedOptions.outerFrameStyle = Object.assign({
borderColor: '#e1e4e8',
borderLineWidth: 1,
cornerRadius: 4
}, options.frame?.outerFrameStyle);
gantt.parsedOptions.markLine = generateMarkLine(options?.markLine);
if (gantt.parsedOptions.markLine?.length ?? 0) {
if (gantt.parsedOptions.markLine?.every(item => item.scrollToMarkLine === undefined)) {
gantt.parsedOptions.markLine[0].scrollToMarkLine = true;
}
if (gantt.parsedOptions.markLine?.find(item => item.scrollToMarkLine)) {
gantt.parsedOptions.scrollToMarkLineDate = getStartDateByTimeUnit(new Date(gantt.parsedOptions.markLine?.find(item => item.scrollToMarkLine).date), minTimeUnit, startOfWeek);
}
}
gantt.parsedOptions.verticalSplitLineHighlight = options.frame?.verticalSplitLineHighlight;
gantt.parsedOptions.verticalSplitLine = Object.assign({
lineColor: gantt.parsedOptions.outerFrameStyle?.borderColor,
lineWidth: gantt.parsedOptions.outerFrameStyle?.borderLineWidth
}, options.frame?.verticalSplitLine);
gantt.parsedOptions.horizontalSplitLine = options.frame?.horizontalSplitLine;
gantt.parsedOptions.verticalSplitLineMoveable = options.frame?.verticalSplitLineMoveable;
gantt.parsedOptions.taskKeyField = options.taskKeyField ?? 'id';
gantt.parsedOptions.dependencyLinks = options.dependency?.links ?? [];
gantt.parsedOptions.dependencyLinkCreatable = options.dependency?.linkCreatable ?? false;
gantt.parsedOptions.dependencyLinkSelectable = options.dependency?.linkSelectable ?? true;
gantt.parsedOptions.dependencyLinkDeletable = options.dependency?.linkDeletable ?? false;
gantt.parsedOptions.dependencyLinkLineStyle = Object.assign({
lineColor: 'red',
lineWidth: 1
}, options.dependency?.linkLineStyle);
gantt.parsedOptions.dependencyLinkSelectedLineStyle = Object.assign({
shadowBlur: 4,
shadowOffset: 0,
shadowColor: gantt.parsedOptions.dependencyLinkLineStyle.lineColor,
lineColor: gantt.parsedOptions.dependencyLinkLineStyle.lineColor,
lineWidth: gantt.parsedOptions.dependencyLinkLineStyle.lineWidth
}, options?.dependency?.linkSelectedLineStyle);
gantt.parsedOptions.dependencyLinkLineCreatePointStyle = Object.assign({
strokeColor: 'red',
fillColor: 'white',
radius: 5,
strokeWidth: 1
}, options?.dependency?.linkCreatePointStyle);
gantt.parsedOptions.dependencyLinkLineCreatingPointStyle = Object.assign({
strokeColor: 'red',
fillColor: 'red',
radius: 5,
strokeWidth: 1
}, options?.dependency?.linkCreatingPointStyle);
gantt.parsedOptions.dependencyLinkLineCreatingStyle = Object.assign({
lineColor: 'red',
lineWidth: 1,
lineDash: [5, 5]
}, options?.dependency?.linkCreatingLineStyle);
gantt.parsedOptions.eventOptions = options?.eventOptions;
gantt.parsedOptions.keyboardOptions = options?.keyboardOptions;
gantt.parsedOptions.markLineCreateOptions = options?.markLineCreateOptions;
}
function updateOptionsWhenScaleChanged(gantt) {
const options = gantt.options;
const { unit: minTimeUnit, startOfWeek, step } = gantt.parsedOptions.reverseSortedTimelineScales[0];
gantt.parsedOptions.minDate = getStartDateByTimeUnit(new Date(gantt.parsedOptions.minDate), minTimeUnit, startOfWeek);
gantt.parsedOptions.maxDate = getEndDateByTimeUnit(gantt.parsedOptions.minDate, new Date(gantt.parsedOptions.maxDate), minTimeUnit, step);
gantt.parsedOptions._minDateTime = gantt.parsedOptions.minDate?.getTime();
gantt.parsedOptions._maxDateTime = gantt.parsedOptions.maxDate?.getTime();
gantt.parsedOptions.timeLineHeaderRowHeights = [];
gantt.parsedOptions.timelineHeaderStyles = [];
for (let i = 0; i < gantt.parsedOptions.sortedTimelineScales.length ?? 0; i++) {
const style = gantt.parsedOptions.sortedTimelineScales[i].style;
gantt.parsedOptions.timelineHeaderStyles.push(Object.assign({
fontSize: 20,
fontWeight: 'bold',
textAlign: 'center',
textBaseline: 'middle',
color: '#000',
backgroundColor: '#fff'
}, style));
gantt.parsedOptions.timeLineHeaderRowHeights.push(gantt.parsedOptions.sortedTimelineScales[i].rowHeight ?? options?.headerRowHeight ?? 40);
}
}
function generateTimeLineDate(currentDate, endDate, scale) {
const { unit, step, format } = scale;
const timelineDates = [];
while (currentDate < endDate) {
if (unit === 'day') {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const day = currentDate.getDate();
const end = createDateAtLastHour(new Date(year, month, day + step - 1), true);
if (end.getTime() > endDate.getTime()) {
end.setTime(endDate.getTime());
}
const start = currentDate;
const formattedDate = format?.({ dateIndex: day, startDate: start, endDate: end });
const columnTitle = formattedDate || day.toString();
const dayCellConfig = {
days: Math.abs(end.getTime() - currentDate.getTime() + 1) / DayTimes,
startDate: start,
endDate: end,
step,
unit: 'day',
title: columnTitle,
dateIndex: day
};
timelineDates.push(dayCellConfig);
currentDate = new Date(year, month, day + step);
}
else if (unit === 'month') {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const end = createDateAtLastHour(new Date(year, month + step, 0), true);
if (end.getTime() > endDate.getTime()) {
end.setTime(endDate.getTime());
}
const start = currentDate;
const formattedDate = format?.({ dateIndex: month + 1, startDate: start, endDate: end });
const columnTitle = formattedDate || (month + 1).toString();
const dayCellConfig = {
days: Math.abs(end.getTime() - currentDate.getTime() + 1) / DayTimes,
startDate: start,
step,
unit: 'month',
endDate: end,
title: columnTitle,
dateIndex: month + 1
};
timelineDates.push(dayCellConfig);
currentDate = new Date(year, month + step, 1);
}
else if (unit === 'quarter') {
const year = currentDate.getFullYear();
const quarter = Math.floor(currentDate.getMonth() / 3);
const end = createDateAtLastHour(new Date(year, (quarter + step) * 3, 0), true);
if (end.getTime() > endDate.getTime()) {
end.setTime(endDate.getTime());
}
const start = currentDate;
const formattedDate = format?.({ dateIndex: quarter + 1, startDate: start, endDate: end });
const columnTitle = formattedDate || (quarter + 1).toString();
const dayCellConfig = {
days: Math.abs(end.getTime() - currentDate.getTime() + 1) / (1000 * 60 * 60 * 24),
startDate: start,
step,
unit: 'quarter',
endDate: end,
title: columnTitle,
dateIndex: quarter + 1
};
timelineDates.push(dayCellConfig);
currentDate = new Date(year, (quarter + step) * 3, 1);
}
else if (unit === 'year') {
const year = currentDate.getFullYear();
const end = createDateAtLastHour(new Date(year + step - 1, 11, 31), true);
if (end.getTime() > endDate.getTime()) {
end.setTime(endDate.getTime());
}
const start = currentDate;
const formattedDate = format?.({ dateIndex: year, startDate: start, endDate: end });
const columnTitle = formattedDate || year.toString();
const dayCellConfig = {
days: Math.abs(end.getTime() - currentDate.getTime() + 1) / DayTimes,
startDate: start,
endDate: end,
step,
unit: 'year',
title: columnTitle,
dateIndex: year
};
timelineDates.push(dayCellConfig);
currentDate = new Date(year + step, 0, 1);
}
else if (unit === 'week') {
const startOfWeekSetting = scale.startOfWeek ?? 'monday';
let dayOfWeek = currentDate.getDay();
if (startOfWeekSetting === 'monday') {
dayOfWeek = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
}
const startOfWeek = createDateAtMidnight(currentDate);
const dateEnd = createDateAtLastHour(currentDate.getTime() + (7 * step - dayOfWeek) * 24 * 60 * 60 * 1000 - 1, true);
if (dateEnd > endDate) {
dateEnd.setTime(endDate.getTime());
}
const weekNumber = getWeekNumber(startOfWeek);
const columnTitle = format?.({ dateIndex: weekNumber, startDate: startOfWeek, endDate: dateEnd }) || weekNumber.toString();
const dayCellConfig = {
days: (dateEnd.getTime() - startOfWeek.getTime() + 1) / DayTimes,
startDate: startOfWeek,
endDate: dateEnd,
step,
unit: 'week',
title: columnTitle,
dateIndex: weekNumber
};
timelineDates.push(dayCellConfig);
currentDate.setTime(createDateAtMidnight(currentDate.getTime() + (7 * step - dayOfWeek) * 24 * 60 * 60 * 1000, true).getTime());
}
else if (unit === 'hour') {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const day = currentDate.getDate();
const hour = currentDate.getHours();
const end = createDateAtLastMinute(new Date(year, month, day, hour + step - 1), true);
if (end.getTime() > endDate.getTime()) {
end.setTime(endDate.getTime());
}
const start = currentDate;
const formattedDate = format?.({ dateIndex: hour, startDate: start, endDate: end });
const columnTitle = formattedDate || hour.toString();
const dayCellConfig = {
days: Math.abs(end.getTime() - currentDate.getTime() + 1) / DayTimes,
startDate: start,
endDate: end,
step,
unit: 'hour',
title: columnTitle,
dateIndex: currentDate.getHours()
};
timelineDates.push(dayCellConfig);
currentDate = new Date(year, month, day, hour + step);
}
else if (unit === 'minute') {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const day = currentDate.getDate();
const hour = currentDate.getHours();
const minute = currentDate.getMinutes();
const end = createDateAtLastSecond(new Date(year, month, day, hour, minute + step - 1), true);
if (end.getTime() > endDate.getTime()) {
end.setTime(endDate.getTime());
}
const start = currentDate;
const formattedDate = format?.({ dateIndex: minute, startDate: start, endDate: end });
const columnTitle = formattedDate || minute.toString();
const dayCellConfig = {
days: Math.abs(end.getTime() - currentDate.getTime() + 1) / DayTimes,
startDate: start,
endDate: end,
step,
unit: 'minute',
title: columnTitle,
dateIndex: currentDate.getMinutes()
};
timelineDates.push(dayCellConfig);
currentDate = new Date(year, month, day, hour, minute + step);
}
else if (unit === 'second') {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const day = currentDate.getDate();
const hour = currentDate.getHours();
const minute = currentDate.getMinutes();
const second = currentDate.getSeconds();
const end = createDateAtLastMillisecond(new Date(year, month, day, hour, minute, second + step - 1), true);
if (end.getTime() > endDate.getTime()) {
end.setTime(endDate.getTime());
}
const start = currentDate;
const formattedDate = format?.({ dateIndex: second, startDate: start, endDate: end });
const columnTitle = formattedDate || second.toString();
const dayCellConfig = {
days: Math.abs(end.getTime() - currentDate.getTime() + 1) / DayTimes,
startDate: start,
endDate: end,
step,
unit: 'second',
title: columnTitle,
dateIndex: currentDate.getSeconds()
};
timelineDates.push(dayCellConfig);
currentDate = new Date(year, month, day, hour, minute, second + step);
}
}
return timelineDates;
}
function getTextPos(padding, textAlign, textBaseline, width, height) {
let textX = padding[3] ?? 10;
if (textAlign === 'right' || textAlign === 'end') {
textX = width - 0 - (padding[1] ?? 10);
}
else if (textAlign === 'center') {
textX = 0 + (width - 0 + (padding[3] ?? 10) - (padding[1] ?? 10)) / 2;
}
let textY = 0 + (padding[0] ?? 10);
if (textBaseline === 'bottom' || textBaseline === 'alphabetic' || textBaseline === 'ideographic') {
textY = height - 0 - (padding[2] ?? 10);
}
else if (textBaseline === 'middle') {
textY = 0 + (height - 0 - (padding[0] ?? 10) - (padding[2] ?? 10)) / 2 + (padding[0] ?? 10);
}
return {
x: textX,
y: textY
};
}
function convertProgress(progress) {
if (typeof progress === 'string') {
progress = progress.replace('%', '');
progress = parseFloat(progress);
}
return Math.round(progress);
}
function createSplitLineAndResizeLine(gantt) {
if (gantt.taskListTableInstance) {
gantt.verticalSplitResizeLine = document.createElement('div');
gantt.verticalSplitResizeLine.style.position = 'absolute';
gantt.verticalSplitResizeLine.style.top = gantt.tableY + 'px';
gantt.verticalSplitResizeLine.style.left =
(gantt.taskTableWidth ? gantt.taskTableWidth - 7 + gantt.parsedOptions.verticalSplitLine.lineWidth / 2 : 0) +
'px';
gantt.verticalSplitResizeLine.style.width = '14px';
gantt.verticalSplitResizeLine.style.height = gantt.drawHeight + 'px';
gantt.verticalSplitResizeLine.style.backgroundColor = 'rgba(0,0,0,0)';
gantt.verticalSplitResizeLine.style.zIndex = '100';
gantt.parsedOptions.verticalSplitLineMoveable && (gantt.verticalSplitResizeLine.style.cursor = 'col-resize');
gantt.verticalSplitResizeLine.style.userSelect = 'none';
gantt.verticalSplitResizeLine.style.opacity = '1';
const verticalSplitLine = document.createElement('div');
verticalSplitLine.style.position = 'absolute';
verticalSplitLine.style.top = '0px';
verticalSplitLine.style.left = `${(14 - gantt.parsedOptions.verticalSplitLine.lineWidth) / 2}px`;
verticalSplitLine.style.width = gantt.parsedOptions.verticalSplitLine.lineWidth + 'px';
verticalSplitLine.style.height = '100%';
verticalSplitLine.style.backgroundColor = gantt.parsedOptions.verticalSplitLine.lineColor;
verticalSplitLine.style.zIndex = '100';
verticalSplitLine.style.userSelect = 'none';
verticalSplitLine.style.pointerEvents = 'none';
verticalSplitLine.style.transition = 'background-color 0.3s';
gantt.verticalSplitResizeLine.appendChild(verticalSplitLine);
if (gantt.parsedOptions.verticalSplitLineHighlight) {
const highlightLine = document.createElement('div');
highlightLine.style.position = 'absolute';
highlightLine.style.top = '0px';
highlightLine.style.left = `${(14 - gantt.parsedOptions.verticalSplitLineHighlight.lineWidth ?? 2) / 2}px`;
highlightLine.style.width = (gantt.parsedOptions.verticalSplitLineHighlight.lineWidth ?? 2) + 'px';
highlightLine.style.height = '100%';
highlightLine.style.backgroundColor = gantt.parsedOptions.verticalSplitLineHighlight.lineColor;
highlightLine.style.zIndex = '100';
highlightLine.style.cursor = 'col-resize';
highlightLine.style.userSelect = 'none';
highlightLine.style.pointerEvents = 'none';
highlightLine.style.opacity = '0';
highlightLine.style.transition = 'background-color 0.3s';
gantt.verticalSplitResizeLine.appendChild(highlightLine);
}
gantt.container.appendChild(gantt.verticalSplitResizeLine);
}
}
function updateSplitLineAndResizeLine(gantt) {
if (gantt.verticalSplitResizeLine) {
gantt.verticalSplitResizeLine.style.position = 'absolute';
gantt.verticalSplitResizeLine.style.top = gantt.tableY + 'px';
gantt.verticalSplitResizeLine.style.left = gantt.taskTableWidth
? `${gantt.taskTableWidth - 7 + gantt.parsedOptions.verticalSplitLine.lineWidth / 2}px`
: '0px';
gantt.verticalSplitResizeLine.style.width = '14px';
gantt.verticalSplitResizeLine.style.height = gantt.drawHeight + 'px';
gantt.verticalSplitResizeLine.style.backgroundColor = 'rgba(0,0,0,0)';
gantt.verticalSplitResizeLine.style.zIndex = '100';
gantt.parsedOptions.verticalSplitLineMoveable && (gantt.verticalSplitResizeLine.style.cursor = 'col-resize');
gantt.verticalSplitResizeLine.style.userSelect = 'none';
gantt.verticalSplitResizeLine.style.opacity = '1';
const verticalSplitLine = gantt.verticalSplitResizeLine.childNodes[0];
verticalSplitLine.style.position = 'absolute';
verticalSplitLine.style.top = '0px';
verticalSplitLine.style.left = `${(14 - gantt.parsedOptions.verticalSplitLine.lineWidth) / 2}px`;
verticalSplitLine.style.width = gantt.parsedOptions.verticalSplitLine.lineWidth + 'px';
verticalSplitLine.style.height = '100%';
verticalSplitLine.style.backgroundColor = gantt.parsedOptions.verticalSplitLine.lineColor;
verticalSplitLine.style.zIndex = '100';
verticalSplitLine.style.userSelect = 'none';
verticalSplitLine.style.pointerEvents = 'none';
verticalSplitLine.style.transition = 'background-color 0.3s';
if (gantt.verticalSplitResizeLine.childNodes[1]) {
const highlightLine = gantt.verticalSplitResizeLine.childNodes[1];
highlightLine.style.position = 'absolute';
highlightLine.style.top = '0px';
highlightLine.style.left = `${(14 - gantt.parsedOptions.verticalSplitLineHighlight.lineWidth ?? 2) / 2}px`;
highlightLine.style.width = (gantt.parsedOptions.verticalSplitLineHighlight.lineWidth ?? 2) + 'px';
highlightLine.style.height = '100%';
highlightLine.style.backgroundColor = gantt.parsedOptions.verticalSplitLineHighlight.lineColor;
highlightLine.style.zIndex = '100';
highlightLine.style.cursor = 'col-resize';
highlightLine.style.userSelect = 'none';
highlightLine.style.pointerEvents = 'none';
highlightLine.style.opacity = '0';
highlightLine.style.transition = 'background-color 0.3s';
}
}
}
function findRecordByTaskKey(records, taskKeyField, taskKey, childrenField = 'children') {
for (let i = 0; i < records.length; i++) {
if ((Array.isArray(taskKey) && taskKey.length === 1 && records[i][taskKeyField] === taskKey[0]) ||
records[i][taskKeyField] === taskKey) {
return { record: records[i], index: [i] };
}
else if (records[i][childrenField]?.length) {
if (Array.isArray(taskKey) && taskKey[0] === records[i][taskKeyField]) {
const result = findRecordByTaskKey(records[i][childrenField], taskKeyField, taskKey.slice(1));
if (result) {
result.index.unshift(i);
return result;
}
}
else if (!Array.isArray(taskKey)) {
const result = findRecordByTaskKey(records[i][childrenField], taskKeyField, taskKey);
if (result) {
result.index.unshift(i);
return result;
}
}
}
}
}
function clearRecordLinkInfos(records, childrenField = 'children') {
for (let i = 0; i < records.length; i++) {
if (records[i][childrenField]?.length) {
clearRecordLinkInfos(records[i][childrenField], childrenField);
}
else {
delete records[i].vtable_gantt_linkedTo;
delete records[i].vtable_gantt_linkedFrom;
}
}
}
function clearRecordShowIndex(records, childrenField = 'children') {
for (let i = 0; i < records.length; i++) {
if (records[i][childrenField]?.length) {
clearRecordShowIndex(records[i][childrenField], childrenField);
}
else {
delete records[i].vtable_gantt_showIndex;
}
}
}
function getTaskIndexsByTaskY(y, gantt) {
let task_index;
let sub_task_index;
if (gantt.taskListTableInstance) {
const rowInfo = gantt.taskListTableInstance.getTargetRowAt(y + gantt.headerHeight);
if (rowInfo) {
const { row } = rowInfo;
task_index = row - gantt.taskListTableInstance.columnHeaderLevelCount;
const beforeRowsHeight = gantt.getRowsHeightByIndex(0, task_index - 1);
if (gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline ||
gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange ||
gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ||
gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) {
sub_task_index = Math.floor((y - beforeRowsHeight) / gantt.parsedOptions.rowHeight);
}
}
}
else {
task_index = Math.floor(y / gantt.parsedOptions.rowHeight);
}
return { task_index, sub_task_index };
}
function computeRowsCountByRecordDateForCompact(gantt, record) {
if (!record.children || record.children.length === 1) {
if (record.children?.length === 1) {
record.children[0].vtable_gantt_showIndex = 0;
}
else {
record.vtable_gantt_showIndex = 0;
}
return 1;
}
const sortedChildren = record.children.slice().sort((a, b) => {
const { startDate: aStartDate } = formatRecordDateConsiderHasHour(gantt, a);
const { startDate: bStartDate } = formatRecordDateConsiderHasHour(gantt, b);
return aStartDate.getTime() - bStartDate.getTime();
});
const rows = [];
for (let i = 0; i <= sortedChildren.length - 1; i++) {
const newRecord = sortedChildren[i];
const { startDate, endDate } = formatRecordDateConsiderHasHour(gantt, newRecord);
let placed = false;
for (let j = 0; j < rows.length; j++) {
if (startDate.getTime() > rows[j]) {
rows[j] = endDate.getTime();
placed = true;
newRecord.vtable_gantt_showIndex = j;
break;
}
}
if (!placed) {
rows.push(endDate.getTime());
newRecord.vtable_gantt_showIndex = rows.length - 1;
}
}
return rows.length;
}
function isOverlapping(startDate, endDate, rowTasks, gantt) {
return rowTasks.some(rowTask => {
const { startDate: startDate2, endDate: endDate2 } = formatRecordDateConsiderHasHour(gantt, rowTask);
return startDate <= endDate2 && startDate2 <= endDate;
});
}
function computeRowsCountByRecordDate(gantt, record) {
if (!record.children || record.children.length === 1) {
if (record.children?.length === 1) {
record.children[0].vtable_gantt_showIndex = 0;
}
else {
record.vtable_gantt_showIndex = 0;
}
return 1;
}
const rows = [];
for (let i = 0; i <= record.children.length - 1; i++) {
const newRecord = record.children[i];
const { startDate, endDate } = formatRecordDateConsiderHasHour(gantt, newRecord);
let placed = false;
for (let j = 0; j < rows.length; j++) {
const rowTasks = record.children.filter((t) => t !== newRecord && t.vtable_gantt_showIndex === j);
if (!isOverlapping(startDate, endDate, rowTasks, gantt)) {
rows[j] = endDate.getTime();
placed = true;
newRecord.vtable_gantt_showIndex = j;
break;
}
}
if (!placed) {
rows.push(endDate.getTime());
newRecord.vtable_gantt_showIndex = rows.length - 1;
}
}
return rows.length;
}
function formatRecordDateConsiderHasHour(gantt, record) {
const { timeScaleIncludeHour, startDateField, endDateField } = gantt.parsedOptions;
const startDate = record[startDateField];
const endDate = record[endDateField];
if (timeScaleIncludeHour) {
return { startDate: createDateAtMidnight(startDate), endDate: createDateAtLastHour(endDate) };
}
return { startDate: createDateAtMidnight(startDate, true), endDate: createDateAtLastHour(endDate, true) };
}
function updateOptionsWhenRecordChanged(gantt) {
const options = gantt.options;
const { unit: minTimeUnit, startOfWeek } = gantt.parsedOptions.reverseSortedTimelineScales[0];
gantt.parsedOptions.markLine = generateMarkLine(options?.markLine);
if (gantt.parsedOptions.markLine?.length ?? 0) {
if (gantt.parsedOptions.markLine?.every(item => item.scrollToMarkLine === undefined)) {
gantt.parsedOptions.markLine[0].scrollToMarkLine = true;
}
if (gantt.parsedOptions.markLine?.find(item => item.scrollToMarkLine)) {
gantt.parsedOptions.scrollToMarkLineDate = getStartDateByTimeUnit(new Date(gantt.parsedOptions.markLine?.find(item => item.scrollToMarkLine).date), minTimeUnit, startOfWeek);
}
}
gantt.parsedOptions.dependencyLinks = options.dependency?.links;
}
function updateOptionsWhenDateRangeChanged(gantt) {
const options = gantt.options;
const { unit: minTimeUnit, startOfWeek, step } = gantt.parsedOptions.reverseSortedTimelineScales[0];
gantt.parsedOptions.minDate = options?.minDate
? getStartDateByTimeUnit(new Date(options.minDate), minTimeUnit, startOfWeek)
: undefined;
gantt.parsedOptions.maxDate = options?.maxDate
? getEndDateByTimeUnit(gantt.parsedOptions.minDate, new Date(options.maxDate), minTimeUnit, step)
: undefined;
gantt.parsedOptions._minDateTime = gantt.parsedOptions.minDate?.getTime();
gantt.parsedOptions._maxDateTime = gantt.parsedOptions.maxDate?.getTime();
}
function updateOptionsWhenMarkLineChanged(gantt) {
const options = gantt.options;
gantt.parsedOptions.markLine = generateMarkLine(options?.markLine);
}
function _getTaskInfoByXYForCreateSchedule(eventX, eventY, gantt) {
const taskIndex = getTaskIndexsByTaskY(eventY - gantt.headerHeight + gantt.stateManager.scrollTop, gantt);
const recordParent = gantt.getRecordByIndex(taskIndex.task_index);
const dateIndex = getDateIndexByX(eventX, gantt);
const dateRange = gantt.getDateRangeByIndex(dateIndex);
if (recordParent?.children) {
const taskIndex = getTaskIndexsByTaskY(eventY - gantt.headerHeight + gantt.stateManager.scrollTop, gantt);
for (let i = 0; i < recordParent.children.length; i++) {
const { startDate, endDate, taskDays, progress, taskRecord } = gantt.getTaskInfoByTaskListIndex(taskIndex.task_index, i);
if (((gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact ||
gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) &&
taskRecord.vtable_gantt_showIndex === taskIndex.sub_task_index) ||
gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) {
if (dateRange.startDate.getTime() >= startDate.getTime() && dateRange.endDate.getTime() <= endDate.getTime()) {
return { startDate, endDate, taskDays, progress, taskRecord };
}
}
}
}
}
function getNodeClickPos(marklineIconNode, gantt) {
const left = marklineIconNode.globalTransMatrix.e +
gantt.taskListTableInstance.tableNoFrameWidth +
gantt.taskListTableInstance.tableX +
gantt.tableX;
const top = marklineIconNode.globalTransMatrix.f;
const width = marklineIconNode.attribute.width;
const height = marklineIconNode.attribute.height;
return {
left,
top,
width,
height
};
}
function judgeIfHasMarkLine(data, markLine) {
const beginTime = data.startDate.getTime();
const endTime = data.endDate.getTime();
return markLine.some(item => {
const marklineTime = new Date(item.date).getTime();
return marklineTime >= beginTime && marklineTime <= endTime;
});
}
function throttle(func, delay) {
let timer = null;
return function (...args) {
if (!timer) {
func.apply(this, args);
timer = setTimeout(() => {
timer = null;
}, delay);
}
};
}
function getTodayNearDay(dayOffset, format) {
const today = new Date();
const todayTime = today.getTime();
const oneDayTime = 24 * 60 * 60 * 1000;
const targetTime = todayTime + dayOffset * oneDayTime;
const date = new Date(targetTime);
if (format) {
const year = date.getFullYear().toString();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
format = format.replace('yyyy', year);
format = format.replace('mm', month);
format = format.replace('dd', day);
return format;
}
return date;
}
function formatDate(date, format) {
const year = date.getFullYear().toString();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
format = format.replace('yyyy', year);
format = format.replace('mm', month);
format = format.replace('dd', day);
if (format.length > 10) {
const hour = date.getHours().toString().padStart(2, '0');
const minute = date.getMinutes().toString().padStart(2, '0');
const second = date.getSeconds().toString().padStart(2, '0');
format = format.replace('hh', hour);
format = format.replace('mm', minute);
format = format.replace('ss', second);
}
return format;
}
function validateDate(dateParts, format) {
const yearIndex = format.indexOf('yyyy');
const monthIndex = format.indexOf('mm');
const dayIndex = format.indexOf('dd');
const dateYearIndex = yearIndex < monthIndex ? (yearIndex < dayIndex ? 0 : 1) : monthIndex < dayIndex ? 1 : 2;
const dateMonthIndex = monthIndex < yearIndex ? (monthIndex < dayIndex ? 0 : 1) : monthIndex < dayIndex ? 1 : 2;
const dateDayIndex = dayIndex < yearIndex ? (dayIndex < monthIndex ? 0 : 1) : dayIndex < monthIndex ? 1 : 2;
const year = parseInt(dateParts[dateYearIndex], 10);
const month = parseInt(dateParts[dateMonthIndex], 10) - 1;
const day = parseInt(dateParts[dateDayIndex], 10);
if (isNaN(year) || year < 1) {
return false;
}
if (isNaN(month) || month < 0 || month > 11) {
return false;
}
const daysInMonth = [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if (isNaN(day) || day < 1 || day > daysInMonth[month]) {
return false;
}
return true;
}
function validateTime(dateParts, format) {
if (format.includes('hh') || format.includes('mm') || format.includes('ss')) {
const timeIndex = format.indexOf('hh') > -1 ? format.indexOf('hh') : format.indexOf('HH');
const hour = parseInt(dateParts[timeIndex], 10);
const minute = parseInt(dateParts[timeIndex + 1], 10);
const second = dateParts.length > timeIndex + 2 ? parseInt(dateParts[timeIndex + 2], 10) : 0;
if (isNaN(hour) || hour < 0 || hour > 23) {
return false;
}
if (isNaN(minute) || minute < 0 || minute > 59) {
return false;
}
if (isNaN(second) || second < 0 || second > 59) {
return false;
}
}
return true;
}
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
function parseDateFormat(dateString) {
const formats = [
'yyyy-mm-dd',
'dd-mm-yyyy',
'mm/dd/yyyy',
'yyyy/mm/dd',
'dd/mm/yyyy',
'yyyy.mm.dd',