UNPKG

gantt-task-react-powern

Version:

Interactive Gantt Chart for React with TypeScript.

1,456 lines (1,304 loc) 176 kB
import React, { useMemo, useRef, useState, useEffect } from 'react'; import { useVirtualizer } from '@tanstack/react-virtual'; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectDestructuringEmpty(obj) { if (obj == null) throw new TypeError("Cannot destructure undefined"); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (it) return (it = it.call(o)).next.bind(it); if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var ViewMode; (function (ViewMode) { ViewMode["Hour"] = "Hour"; ViewMode["QuarterDay"] = "Quarter Day"; ViewMode["HalfDay"] = "Half Day"; ViewMode["Day"] = "Day"; ViewMode["Week"] = "Week"; ViewMode["Month"] = "Month"; ViewMode["Quarter"] = "Quarter"; ViewMode["Year"] = "Year"; })(ViewMode || (ViewMode = {})); var _VIEW_MODE_DEFAULT_VI, _VIEW_MODE_MAX_VISIBL; var intlDTCache = {}; var getCachedDateTimeFormat = function getCachedDateTimeFormat(locString, opts) { if (opts === void 0) { opts = {}; } var key = JSON.stringify([locString, opts]); var dtf = intlDTCache[key]; if (!dtf) { dtf = new Intl.DateTimeFormat(locString, opts); intlDTCache[key] = dtf; } return dtf; }; var addToDate = function addToDate(date, quantity, scale) { var newDate = new Date(date.getFullYear() + (scale === "year" ? quantity : 0), date.getMonth() + (scale === "month" ? quantity : 0), date.getDate() + (scale === "day" ? quantity : 0), date.getHours() + (scale === "hour" ? quantity : 0), date.getMinutes() + (scale === "minute" ? quantity : 0), date.getSeconds() + (scale === "second" ? quantity : 0), date.getMilliseconds() + (scale === "millisecond" ? quantity : 0)); return newDate; }; var startOfDate = function startOfDate(date, scale) { var scores = ["millisecond", "second", "minute", "hour", "day", "month", "quarter", "year"]; var shouldReset = function shouldReset(_scale) { var maxScore = scores.indexOf(scale); return scores.indexOf(_scale) <= maxScore; }; var newDate = new Date(date.getFullYear(), shouldReset("year") ? 0 : date.getMonth(), shouldReset("month") ? 1 : date.getDate(), shouldReset("day") ? 0 : date.getHours(), shouldReset("hour") ? 0 : date.getMinutes(), shouldReset("minute") ? 0 : date.getSeconds(), shouldReset("second") ? 0 : date.getMilliseconds()); return newDate; }; var getFiscalQuarterStartDate = function getFiscalQuarterStartDate(date, quarterStart) { var month = date.getMonth(); var offset = (month - quarterStart + 12) % 12; var qStartMonth = (quarterStart + Math.floor(offset / 3) * 3) % 12; var year = date.getFullYear(); if (qStartMonth > month) year -= 1; return new Date(year, qStartMonth, 1); }; var VIEW_MODE_DEFAULT_VISIBLE_COUNT = (_VIEW_MODE_DEFAULT_VI = {}, _VIEW_MODE_DEFAULT_VI[ViewMode.Day] = 14, _VIEW_MODE_DEFAULT_VI[ViewMode.Week] = 6, _VIEW_MODE_DEFAULT_VI[ViewMode.Month] = 6, _VIEW_MODE_DEFAULT_VI[ViewMode.Quarter] = 4, _VIEW_MODE_DEFAULT_VI); var VIEW_MODE_MAX_VISIBLE_COUNT = (_VIEW_MODE_MAX_VISIBL = {}, _VIEW_MODE_MAX_VISIBL[ViewMode.Day] = 30, _VIEW_MODE_MAX_VISIBL[ViewMode.Week] = 13, _VIEW_MODE_MAX_VISIBL[ViewMode.Month] = 12, _VIEW_MODE_MAX_VISIBL[ViewMode.Quarter] = 8, _VIEW_MODE_MAX_VISIBL); var ganttDateRange = function ganttDateRange(tasks, viewMode, preStepsCount, quarterStart) { var _tasks$, _tasks$2, _tasks$3, _tasks$4; if (quarterStart === void 0) { quarterStart = 0; } var newStartDate = ((_tasks$ = tasks[0]) === null || _tasks$ === void 0 ? void 0 : _tasks$.start.getTime()) !== 0 ? ((_tasks$2 = tasks[0]) === null || _tasks$2 === void 0 ? void 0 : _tasks$2.start) || new Date() : new Date(); var newEndDate = ((_tasks$3 = tasks[0]) === null || _tasks$3 === void 0 ? void 0 : _tasks$3.end.getTime()) !== 0 ? ((_tasks$4 = tasks[0]) === null || _tasks$4 === void 0 ? void 0 : _tasks$4.end) || new Date() : new Date(); for (var _iterator = _createForOfIteratorHelperLoose(tasks), _step; !(_step = _iterator()).done;) { var task = _step.value; if (task.start && task.start.getTime() !== 0 && task.start < newStartDate) { newStartDate = task.start; } if (task.end && task.end.getTime() !== 0 && task.end > newEndDate) { newEndDate = task.end; } if (task.actualStart && task.actualStart.getTime() !== 0 && task.actualStart < newStartDate) { newStartDate = task.actualStart; } if (task.actualEnd && task.actualEnd.getTime() !== 0 && task.actualEnd > newEndDate) { newEndDate = task.actualEnd; } } switch (viewMode) { case ViewMode.Year: newStartDate = addToDate(newStartDate, -1, "year"); newStartDate = startOfDate(newStartDate, "year"); newEndDate = addToDate(newEndDate, 1, "year"); newEndDate = startOfDate(newEndDate, "year"); break; case ViewMode.Quarter: { newStartDate = addToDate(newStartDate, -1 * preStepsCount, "year"); newStartDate = getFiscalQuarterStartDate(newStartDate, quarterStart); newEndDate = addToDate(newEndDate, 1, "year"); var endQStart = getFiscalQuarterStartDate(newEndDate, quarterStart); newEndDate = endQStart < newEndDate ? addToDate(endQStart, 3, "month") : endQStart; break; } case ViewMode.Month: newStartDate = addToDate(newStartDate, -1 * preStepsCount, "month"); newStartDate = startOfDate(newStartDate, "month"); newEndDate = addToDate(newEndDate, 1, "year"); newEndDate = startOfDate(newEndDate, "year"); break; case ViewMode.Week: newStartDate = startOfDate(newStartDate, "day"); newStartDate = addToDate(getMonday(newStartDate), -7 * preStepsCount, "day"); newEndDate = startOfDate(newEndDate, "day"); newEndDate = addToDate(newEndDate, 1.5, "month"); break; case ViewMode.Day: newStartDate = startOfDate(newStartDate, "day"); newStartDate = addToDate(newStartDate, -1 * preStepsCount, "day"); newEndDate = startOfDate(newEndDate, "day"); newEndDate = addToDate(newEndDate, 19, "day"); break; case ViewMode.QuarterDay: newStartDate = startOfDate(newStartDate, "day"); newStartDate = addToDate(newStartDate, -1 * preStepsCount, "day"); newEndDate = startOfDate(newEndDate, "day"); newEndDate = addToDate(newEndDate, 66, "hour"); break; case ViewMode.HalfDay: newStartDate = startOfDate(newStartDate, "day"); newStartDate = addToDate(newStartDate, -1 * preStepsCount, "day"); newEndDate = startOfDate(newEndDate, "day"); newEndDate = addToDate(newEndDate, 108, "hour"); break; case ViewMode.Hour: newStartDate = startOfDate(newStartDate, "hour"); newStartDate = addToDate(newStartDate, -1 * preStepsCount, "hour"); newEndDate = startOfDate(newEndDate, "day"); newEndDate = addToDate(newEndDate, 1, "day"); break; } return [newStartDate, newEndDate]; }; var seedDates = function seedDates(startDate, endDate, viewMode) { var currentDate = new Date(startDate); var dates = [currentDate]; while (currentDate < endDate) { switch (viewMode) { case ViewMode.Year: currentDate = addToDate(currentDate, 1, "year"); break; case ViewMode.Quarter: currentDate = addToDate(currentDate, 3, "month"); break; case ViewMode.Month: currentDate = addToDate(currentDate, 1, "month"); break; case ViewMode.Week: currentDate = addToDate(currentDate, 7, "day"); break; case ViewMode.Day: currentDate = addToDate(currentDate, 1, "day"); break; case ViewMode.HalfDay: currentDate = addToDate(currentDate, 12, "hour"); break; case ViewMode.QuarterDay: currentDate = addToDate(currentDate, 6, "hour"); break; case ViewMode.Hour: currentDate = addToDate(currentDate, 1, "hour"); break; } dates.push(currentDate); } return dates; }; var getLocalDayOfWeek = function getLocalDayOfWeek(date, locale, format) { var bottomValue = getCachedDateTimeFormat(locale, { weekday: format }).format(date); bottomValue = bottomValue.replace(bottomValue[0], bottomValue[0].toLocaleUpperCase()); return bottomValue; }; var getMonday = function getMonday(date) { var day = date.getDay(); var diff = date.getDate() - day + (day === 0 ? -6 : 1); return new Date(date.setDate(diff)); }; var styles = {"ganttTable":"_3_ygE","ganttTable_Header":"_1nBOt","ganttTable_HeaderSeparator":"_2eZzQ","ganttTable_HeaderItem":"_WuQ0f"}; var TaskListHeaderDefault = function TaskListHeaderDefault(_ref) { var headerHeight = _ref.headerHeight, fontFamily = _ref.fontFamily, fontSize = _ref.fontSize, rowWidth = _ref.rowWidth, scheduleType = _ref.scheduleType, allSelected = _ref.allSelected, onSelectAll = _ref.onSelectAll; return React.createElement("div", { className: styles.ganttTable, style: { fontFamily: fontFamily, fontSize: fontSize } }, React.createElement("div", { className: styles.ganttTable_Header, style: { height: headerHeight - 2 } }, onSelectAll && React.createElement("div", null, React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.3, maxWidth: parseInt(rowWidth) * 0.3 } }, React.createElement("input", { type: "checkbox", checked: allSelected, onChange: function onChange(e) { return onSelectAll(e.target.checked); } }))), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.8, maxWidth: parseInt(rowWidth) * 0.8 } }, "ID"), React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.2 } }), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.8, maxWidth: parseInt(rowWidth) * 0.8 } }, "WBS Code / Activity ID"), React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.2 } }), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 1.8, maxWidth: parseInt(rowWidth) * 1.8 } }, "Task"), React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.2 } }), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.6 }, title: "Planned Start" }, "Planned Start"), React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.25 } }), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.6 }, title: "Planned End" }, "Planned End"), scheduleType === "lookAhead" && React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.6 }, title: "Planned Start" }, "Actual Start"), scheduleType === "lookAhead" && React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.25 } }), scheduleType === "lookAhead" && React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.6 }, title: "Planned End" }, "Actual End"), scheduleType === "main" && React.createElement(React.Fragment, null, React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.25 } }), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 }, title: "% Complete" }, "% Complete"), React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.25 } }), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 }, title: "Planned Duration" }, "Planned Duration"), React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.25 } }), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.7, maxWidth: parseInt(rowWidth) * 0.7 }, title: "Remaining Duration" }, "Remaining Duration"), React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.25 } }), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 }, title: "Actual Duration" }, "Actual Duration"), React.createElement("div", { className: styles.ganttTable_HeaderSeparator, style: { height: headerHeight * 0.5, marginTop: headerHeight * 0.25 } }), React.createElement("div", { className: styles.ganttTable_HeaderItem, style: { minWidth: parseInt(rowWidth) * 0.8, maxWidth: parseInt(rowWidth) * 0.8 }, title: "Duration Type" }, "Duration Type")))); }; var styles$1 = {"taskListWrapper":"_3ZbQT","taskListTableRow":"_34SS0","taskListLookAheadRow":"_GzvG4","taskListMilestoneRow":"_3Ykml","taskListCell":"_3lLk3","taskListNameWrapper":"_nI1Xw","taskListExpander":"_2QjE6","taskListExpanderPlaceholder":"_1fnLB","taskListEmptyExpander":"_2TfEi","taskListText":"_2ZvXU"}; var localeDateStringCache = {}; var toLocaleDateStringFactory = function toLocaleDateStringFactory(locale) { return function (date, dateTimeOptions) { if (!date || date.getTime() === 0) return ""; var key = date.toString(); var lds = localeDateStringCache[key]; if (!lds) { lds = date.toLocaleDateString(locale, dateTimeOptions); localeDateStringCache[key] = lds; } return lds; }; }; var dateTimeOptions = { year: "numeric", month: "numeric", day: "numeric" }; var TaskListTableDefault = function TaskListTableDefault(_ref) { var rowHeight = _ref.rowHeight, rowWidth = _ref.rowWidth, tasks = _ref.tasks, scheduleType = _ref.scheduleType, leafTasks = _ref.leafTasks, fontFamily = _ref.fontFamily, fontSize = _ref.fontSize, locale = _ref.locale, onExpanderClick = _ref.onExpanderClick, _ref$selectedTasks = _ref.selectedTasks, selectedTasks = _ref$selectedTasks === void 0 ? [] : _ref$selectedTasks, onTaskSelect = _ref.onTaskSelect, _ref$taskLabelRendere = _ref.taskLabelRenderer, taskLabelRenderer = _ref$taskLabelRendere === void 0 ? function (t) { return " " + t.name; } : _ref$taskLabelRendere, virtualItems = _ref.virtualItems; var toLocaleDateString = useMemo(function () { return toLocaleDateStringFactory(locale); }, [locale]); var leafTaskIds = useMemo(function () { return new Set(leafTasks.map(function (t) { return t.id; })); }, [leafTasks]); var hasSelectedAncestor = function hasSelectedAncestor(taskId, selectedSet, allTasks) { var task = allTasks.find(function (t) { return t.id === taskId; }); if (!task) return false; var parentWbs = getParentWbs(task.id); if (!parentWbs) return false; if (selectedSet.has(parentWbs)) return true; return hasSelectedAncestor(parentWbs, selectedSet, allTasks); }; var itemsToRender = virtualItems || tasks.map(function (_, index) { return { index: index, start: index * rowHeight, size: rowHeight, key: index, end: (index + 1) * rowHeight, lane: 0, measureElement: function measureElement() {} }; }); return React.createElement("div", { className: styles$1.taskListWrapper, style: { fontFamily: fontFamily, fontSize: fontSize } }, itemsToRender.map(function (vi) { var t = tasks[vi.index]; var expanderSymbol = ""; if (!(leafTaskIds.has(t.id) || t.type === "milestone")) { if (t.hideChildren === false) { expanderSymbol = "▼"; } else if (t.hideChildren === true) { expanderSymbol = "►"; } } var isSelected = selectedTasks.includes(t.id); var isAncestorSelected = hasSelectedAncestor(t.id, new Set(selectedTasks), tasks); return React.createElement("div", { key: "" + vi.key, "data-index": vi.index, style: { position: "absolute", top: 0, left: 0, width: "100%", height: vi.size + "px", transform: "translateY(" + vi.start + "px)" } }, React.createElement("div", { className: t.type === "milestone" ? styles$1.taskListMilestoneRow : scheduleType === "lookAhead" ? styles$1.taskListLookAheadRow : styles$1.taskListTableRow, style: { height: rowHeight }, key: t.id + "row" }, onTaskSelect && React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.3, maxWidth: parseInt(rowWidth) * 0.3 } }, React.createElement("div", { className: styles$1.taskListText, style: { display: "flex", justifyContent: "center", alignItems: "center", height: "100%", paddingLeft: "0", paddingRight: "0" } }, React.createElement("input", { type: "checkbox", checked: isSelected, disabled: isAncestorSelected, onChange: function onChange(e) { return onTaskSelect(t.id, e.target.checked); } }))), React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.8, maxWidth: parseInt(rowWidth) * 0.8 }, title: t.id }, React.createElement("div", { className: styles$1.taskListNameWrapper, style: { paddingLeft: t.depth * 4 + "px" } }, !(leafTaskIds.has(t.id) || t.type === "milestone") ? React.createElement("div", { className: styles$1.taskListExpander, onClick: function onClick() { return onExpanderClick(t); } }, expanderSymbol) : React.createElement("div", { className: styles$1.taskListExpanderPlaceholder }), React.createElement("div", { className: styles$1.taskListText }, t.id, " ", t.actualEnd.getTime() > 0 && t.actualEnd.getTime() < Date.now() ? React.createElement("span", { title: "Task Complete", style: { color: "limegreen", fontSize: "16px" } }, "\u2714") : ""))), React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.8, maxWidth: parseInt(rowWidth) * 0.8 }, title: t.optionalId ? t.optionalId : "" }, t.optionalId), React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 1.8, maxWidth: parseInt(rowWidth) * 1.8 }, title: t.name }, React.createElement("div", { className: styles$1.taskListText }, taskLabelRenderer(t))), React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 } }, React.createElement("div", { className: styles$1.taskListText }, "\xA0", toLocaleDateString(t.start, dateTimeOptions))), React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 } }, React.createElement("div", { className: styles$1.taskListText }, "\xA0", toLocaleDateString(t.end, dateTimeOptions))), scheduleType === "lookAhead" && React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 } }, React.createElement("div", { className: styles$1.taskListText }, "\xA0", toLocaleDateString(t.actualStart, dateTimeOptions))), scheduleType === "lookAhead" && React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 } }, React.createElement("div", { className: styles$1.taskListText }, "\xA0", toLocaleDateString(t.actualEnd, dateTimeOptions))), scheduleType === "main" && function () { var _t$progress, _t$durationType; var percentComplete = t.percentComplete != null ? t.percentComplete : (_t$progress = t.progress) != null ? _t$progress : 0; var plannedDuration = t.plannedDuration != null ? t.plannedDuration : null; var remainingDuration = plannedDuration != null ? Math.round(plannedDuration - plannedDuration * (percentComplete / 100)) : null; var actualDuration = function () { if (!t.actualStart || t.actualStart.getTime() === 0) return null; var endRef = t.actualEnd && t.actualEnd.getTime() > 0 ? t.actualEnd : new Date(); return Math.ceil((endRef.getTime() - t.actualStart.getTime()) / (1000 * 60 * 60 * 24)); }(); return React.createElement(React.Fragment, null, React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 } }, React.createElement("div", { className: styles$1.taskListText }, "\xA0", percentComplete != null ? percentComplete + "%" : "")), React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 } }, React.createElement("div", { className: styles$1.taskListText }, "\xA0", plannedDuration != null ? plannedDuration : "")), React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.7, maxWidth: parseInt(rowWidth) * 0.7 } }, React.createElement("div", { className: styles$1.taskListText }, "\xA0", remainingDuration != null ? remainingDuration : "")), React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.6, maxWidth: parseInt(rowWidth) * 0.6 } }, React.createElement("div", { className: styles$1.taskListText }, "\xA0", actualDuration != null ? actualDuration : "")), React.createElement("div", { className: styles$1.taskListCell, style: { minWidth: parseInt(rowWidth) * 0.8, maxWidth: parseInt(rowWidth) * 0.8 } }, React.createElement("div", { className: styles$1.taskListText }, "\xA0", (_t$durationType = t.durationType) != null ? _t$durationType : "Activity Calendar"))); }())); })); }; var styles$2 = {"tooltipDefaultContainer":"_3T42e","tooltipDefaultContainerParagraph":"_29NTg","tooltipDetailsContainer":"_25P-K","tooltipDetailsContainerHidden":"_3gVAq"}; var Tooltip = function Tooltip(_ref) { var task = _ref.task, type = _ref.type, rowHeight = _ref.rowHeight, rtl = _ref.rtl, svgContainerHeight = _ref.svgContainerHeight, svgContainerWidth = _ref.svgContainerWidth, scrollX = _ref.scrollX, scrollY = _ref.scrollY, arrowIndent = _ref.arrowIndent, fontSize = _ref.fontSize, fontFamily = _ref.fontFamily, headerHeight = _ref.headerHeight, taskListWidth = _ref.taskListWidth, TooltipContent = _ref.TooltipContent, isDragging = _ref.isDragging; var tooltipRef = useRef(null); var _useState = useState(0), relatedY = _useState[0], setRelatedY = _useState[1]; var _useState2 = useState(0), relatedX = _useState2[0], setRelatedX = _useState2[1]; useEffect(function () { if (tooltipRef.current) { var tooltipHeight = tooltipRef.current.offsetHeight * 1.1; var tooltipWidth = tooltipRef.current.offsetWidth * 1.1; var newRelatedY = task.index * rowHeight - scrollY + headerHeight; var newRelatedX; if (isDragging) { newRelatedX = taskListWidth + svgContainerWidth - tooltipWidth - 10; newRelatedY = headerHeight + 5; setRelatedY(newRelatedY); setRelatedX(newRelatedX); return; } if (rtl) { newRelatedX = task.x1 - arrowIndent * 1.5 - tooltipWidth - scrollX; if (newRelatedX < 0) { newRelatedX = task.x2 + arrowIndent * 1.5 - scrollX; } var tooltipLeftmostPoint = tooltipWidth + newRelatedX; if (tooltipLeftmostPoint > svgContainerWidth) { newRelatedX = svgContainerWidth - tooltipWidth; newRelatedY += rowHeight; } } else { newRelatedX = type == "planned" ? task.x2 + arrowIndent * 1.5 + taskListWidth - scrollX : task.actualx2 + arrowIndent * 1.5 + taskListWidth - scrollX; var _tooltipLeftmostPoint = tooltipWidth + newRelatedX; var fullChartWidth = taskListWidth + svgContainerWidth; if (_tooltipLeftmostPoint > fullChartWidth) { newRelatedX = type == "planned" ? task.x1 + taskListWidth - arrowIndent * 1.5 - scrollX - tooltipWidth : task.actualx1 + taskListWidth - arrowIndent * 1.5 - scrollX - tooltipWidth; } if (newRelatedX < taskListWidth) { newRelatedX = svgContainerWidth + taskListWidth - tooltipWidth; newRelatedY += rowHeight; } } var tooltipLowerPoint = tooltipHeight + newRelatedY - scrollY; if (tooltipLowerPoint > svgContainerHeight - scrollY) { newRelatedY = svgContainerHeight - tooltipHeight; } setRelatedY(newRelatedY); setRelatedX(newRelatedX); } }, [tooltipRef, task, arrowIndent, scrollX, scrollY, headerHeight, taskListWidth, rowHeight, svgContainerHeight, svgContainerWidth, rtl, isDragging]); return React.createElement("div", { ref: tooltipRef, className: relatedX ? styles$2.tooltipDetailsContainer : styles$2.tooltipDetailsContainerHidden, style: { left: relatedX, top: relatedY } }, React.createElement(TooltipContent, { task: task, fontSize: fontSize, fontFamily: fontFamily, type: type })); }; var StandardTooltipContent = function StandardTooltipContent(_ref2) { var _task$plannedDuration, _task$actualDuration; var task = _ref2.task, fontSize = _ref2.fontSize, fontFamily = _ref2.fontFamily, type = _ref2.type; var style = { fontSize: fontSize, fontFamily: fontFamily }; var computedPlannedDuration = task.start && task.end && task.end.getTime() - task.start.getTime() > 0 ? Math.max(1, Math.round((task.end.getTime() - task.start.getTime()) / (1000 * 60 * 60 * 24))) : (_task$plannedDuration = task.plannedDuration) != null ? _task$plannedDuration : 0; var computedActualDuration = task.actualStart && task.actualEnd && task.actualEnd.getTime() - task.actualStart.getTime() > 0 ? Math.max(1, Math.round((task.actualEnd.getTime() - task.actualStart.getTime()) / (1000 * 60 * 60 * 24))) : (_task$actualDuration = task.actualDuration) != null ? _task$actualDuration : 0; if (type == "planned") return React.createElement("div", { className: styles$2.tooltipDefaultContainer, style: style }, React.createElement("b", { style: { fontSize: fontSize + 6 } }, task.name + ": Planned dates: "), React.createElement("b", null, task.start.getMonth() + 1 + "/" + task.start.getDate() + "/" + task.start.getFullYear() + " - " + (task.end.getMonth() + 1) + "/" + task.end.getDate() + "/" + task.end.getFullYear()), task.end.getTime() - task.start.getTime() !== 0 && React.createElement("p", { className: styles$2.tooltipDefaultContainerParagraph }, "Duration: " + computedPlannedDuration + " day(s)"), React.createElement("p", { className: styles$2.tooltipDefaultContainerParagraph }, !!task.progress && "Progress: " + task.progress + " %"));else return React.createElement("div", { className: styles$2.tooltipDefaultContainer, style: style }, React.createElement("b", { style: { fontSize: fontSize + 6 } }, task.name + ": Actual dates: "), React.createElement("b", null, task.actualStart.getMonth() + 1 + "/" + task.actualStart.getDate() + "/" + task.actualStart.getFullYear() + " - " + (task.actualEnd.getMonth() + 1) + "/" + task.actualEnd.getDate() + "/" + task.actualEnd.getFullYear()), task.actualEnd.getTime() - task.actualStart.getTime() !== 0 && React.createElement("p", { className: styles$2.tooltipDefaultContainerParagraph }, "Duration: " + computedActualDuration + " day(s)"), React.createElement("p", { className: styles$2.tooltipDefaultContainerParagraph }, !!task.progress && "Progress: " + task.progress + " %")); }; var styles$3 = {"scroll":"_1eT-t"}; var VerticalScroll = function VerticalScroll(_ref) { var scroll = _ref.scroll, ganttHeight = _ref.ganttHeight, ganttFullHeight = _ref.ganttFullHeight, headerHeight = _ref.headerHeight, rtl = _ref.rtl, onScroll = _ref.onScroll; var scrollRef = useRef(null); useEffect(function () { if (scrollRef.current) { scrollRef.current.scrollTop = scroll; } }, [scroll]); return React.createElement("div", { style: { height: ganttHeight, marginTop: headerHeight, marginLeft: rtl ? "" : "-1rem", marginRight: "1rem" }, className: styles$3.scroll, onScroll: onScroll, ref: scrollRef }, React.createElement("div", { style: { height: ganttFullHeight, width: 1 } })); }; var styles$4 = {"hideScrollbar":"_38emS"}; var TaskList = function TaskList(_ref) { var headerHeight = _ref.headerHeight, fontFamily = _ref.fontFamily, fontSize = _ref.fontSize, rowWidth = _ref.rowWidth, rowHeight = _ref.rowHeight, scrollY = _ref.scrollY, tasks = _ref.tasks, scheduleType = _ref.scheduleType, leafTasks = _ref.leafTasks, selectedTask = _ref.selectedTask, setSelectedTask = _ref.setSelectedTask, onExpanderClick = _ref.onExpanderClick, locale = _ref.locale, ganttHeight = _ref.ganttHeight, taskListRef = _ref.taskListRef, horizontalContainerClass = _ref.horizontalContainerClass, TaskListHeader = _ref.TaskListHeader, TaskListTable = _ref.TaskListTable, taskLabelRenderer = _ref.taskLabelRenderer, onMultiSelect = _ref.onMultiSelect, containerWidth = _ref.containerWidth, innerScrollRef = _ref.innerScrollRef, externalHorizontalContainerRef = _ref.horizontalContainerRef; var internalHorizontalContainerRef = useRef(null); var horizontalContainerRef = externalHorizontalContainerRef != null ? externalHorizontalContainerRef : internalHorizontalContainerRef; var headerScrollRef = useRef(null); useEffect(function () { var rowsEl = horizontalContainerRef.current; if (!rowsEl) return; var onScroll = function onScroll() { if (headerScrollRef.current) { headerScrollRef.current.scrollLeft = rowsEl.scrollLeft; } }; rowsEl.addEventListener("scroll", onScroll); return function () { return rowsEl.removeEventListener("scroll", onScroll); }; }, [horizontalContainerRef]); var _useState = useState([]), selectedTasks = _useState[0], setSelectedTasks = _useState[1]; var _useState2 = useState([]), pendingTaskSelect = _useState2[0], setPendingTaskSelect = _useState2[1]; var prevSelectedTasksRef = useRef([]); var expandedTasks = useRef([]); var virtualizer = useVirtualizer({ count: tasks.length, getScrollElement: function getScrollElement() { return horizontalContainerRef.current; }, estimateSize: function estimateSize() { return rowHeight; }, overscan: 10 }); useEffect(function () { if (horizontalContainerRef.current) { horizontalContainerRef.current.scrollTop = scrollY; } }, [scrollY]); useEffect(function () { if (onMultiSelect && JSON.stringify(prevSelectedTasksRef.current) !== JSON.stringify(selectedTasks)) { var selectedTaskObjects = tasks.filter(function (task) { return selectedTasks.includes(task.id); }); prevSelectedTasksRef.current = [].concat(selectedTasks); onMultiSelect(selectedTaskObjects); } }, [selectedTasks, tasks, onMultiSelect]); useEffect(function () { if (pendingTaskSelect.length === 0) return; var newSelected = new Set(selectedTasks); pendingTaskSelect.forEach(function (_ref2) { var taskId = _ref2.taskId, selected = _ref2.selected; if (selected) { recursiveOpen(taskId, tasks); var descendants = getDescendants(taskId, tasks); newSelected.add(taskId); descendants.forEach(function (d) { return newSelected.add(d); }); } else { var _descendants = getDescendants(taskId, tasks); newSelected["delete"](taskId); _descendants.forEach(function (d) { return newSelected["delete"](d); }); } }); setSelectedTasks(Array.from(newSelected)); setPendingTaskSelect([]); }, [pendingTaskSelect]); useEffect(function () { expandedTasks.current = expandedTasks.current.filter(function (id) { var t = tasks.find(function (task) { return task.id === id; }); return t && !t.hideChildren; }); if (selectedTasks.length === 0) return; var newSelected = new Set(); selectedTasks.forEach(function (taskId) { if (tasks.find(function (t) { return t.id === taskId; })) { recursiveOpen(taskId, tasks); newSelected.add(taskId); var descendants = getDescendants(taskId, tasks); descendants.forEach(function (d) { return newSelected.add(d); }); } }); var newSelectedArray = Array.from(newSelected); if (JSON.stringify(newSelectedArray) !== JSON.stringify(selectedTasks)) { setSelectedTasks(newSelectedArray); } }, [tasks.length]); var getDescendants = function getDescendants(taskId, allTasks) { var task = allTasks.find(function (t) { return t.id === taskId; }); if (!task) return []; var children = allTasks.filter(function (t) { return getParentWbs(t.id) === taskId; }); return children.flatMap(function (child) { return [child.id].concat(getDescendants(child.id, allTasks)); }); }; var recursiveOpen = function recursiveOpen(taskId, allTasks) { var task = allTasks.find(function (t) { return t.id === taskId; }); if (!task) return; if (!expandedTasks.current.includes(taskId) && task.hideChildren) { onExpanderClick(task); expandedTasks.current = [].concat(expandedTasks.current, [taskId]); } var children = allTasks.filter(function (t) { return getParentWbs(t.id) === taskId; }); children.forEach(function (c) { return recursiveOpen(c.id, allTasks); }); }; var handleTaskSelect = function handleTaskSelect(taskId, selected) { setPendingTaskSelect(function (prev) { return [].concat(prev, [{ taskId: taskId, selected: selected }]); }); }; var handleSelectAll = function handleSelectAll(selected) { if (selected) { var topLevelTasks = tasks.filter(function (task) { return !getParentWbs(task.id); }); setPendingTaskSelect(topLevelTasks.map(function (task) { return { taskId: task.id, selected: true }; })); } else { setSelectedTasks([]); setPendingTaskSelect([]); } }; var headerProps = { headerHeight: headerHeight, fontFamily: fontFamily, fontSize: fontSize, rowWidth: rowWidth, scheduleType: scheduleType, allSelected: tasks.length > 0 && selectedTasks.length === tasks.length, onSelectAll: onMultiSelect ? handleSelectAll : undefined }; var selectedTaskId = selectedTask ? selectedTask.id : ""; var tableProps = { rowHeight: rowHeight, rowWidth: rowWidth, fontFamily: fontFamily, fontSize: fontSize, tasks: tasks, leafTasks: leafTasks, scheduleType: scheduleType, locale: locale, selectedTaskId: selectedTaskId, setSelectedTask: setSelectedTask, onExpanderClick: onExpanderClick, selectedTasks: onMultiSelect ? selectedTasks : undefined, onTaskSelect: onMultiSelect ? handleTaskSelect : undefined, taskLabelRenderer: taskLabelRenderer, virtualItems: virtualizer.getVirtualItems() }; return React.createElement("div", { ref: taskListRef, style: containerWidth != null ? { width: containerWidth, overflow: "hidden", flexShrink: 0 } : {} }, React.createElement("div", { ref: innerScrollRef, className: styles$4.hideScrollbar, style: { overflowX: "hidden" } }, React.createElement("div", { ref: headerScrollRef, style: { overflow: "hidden" } }, React.createElement(TaskListHeader, Object.assign({}, headerProps))), React.createElement("div", { ref: horizontalContainerRef, className: horizontalContainerClass, style: ganttHeight ? { height: ganttHeight } : {} }, React.createElement("div", { style: { height: virtualizer.getTotalSize() + "px", width: "100%", position: "relative" } }, React.createElement(TaskListTable, Object.assign({}, tableProps)))))); }; function parseTimeToMinutes(t) { var parts = t.split(":").map(Number); return parts[0] * 60 + parts[1] + (parts[2] ? parts[2] / 60 : 0); } function toDateString(date) { var y = date.getFullYear(); var m = String(date.getMonth() + 1).padStart(2, "0"); var d = String(date.getDate()).padStart(2, "0"); return y + "-" + m + "-" + d; } function isHoliday(date, cal) { if (cal.holidays && cal.holidays.length > 0) { return cal.holidays.includes(toDateString(date)); } else { return false; } } function isOffDay(date, cal) { if (cal.off_days && cal.off_days.length > 0) { return cal.off_days.includes(date.getDay()) || isHoliday(date, cal); } else { return false; } } function getShiftsForDay(date, cal) { if (isOffDay(date, cal) || !cal.shifts) return []; return cal.shifts.map(function (_ref) { var s = _ref[0], e = _ref[1]; return { start: parseTimeToMinutes(s), end: parseTimeToMinutes(e) }; }); } function getWorkingIntervals(start, end, cal) { if (start >= end) return []; var intervals = []; var cursor = new Date(start); cursor.setHours(0, 0, 0, 0); while (cursor <= end) { var shifts = getShiftsForDay(cursor, cal); for (var _iterator = _createForOfIteratorHelperLoose(shifts), _step; !(_step = _iterator()).done;) { var shift = _step.value; var shiftStart = new Date(cursor); shiftStart.setHours(Math.floor(shift.start / 60), Math.floor(shift.start % 60), Math.round(shift.start % 1 * 60), 0); var shiftEnd = new Date(cursor); shiftEnd.setHours(Math.floor(shift.end / 60), Math.floor(shift.end % 60), Math.round(shift.end % 1 * 60), 0); var intervalStart = shiftStart < start ? start : shiftStart; var intervalEnd = shiftEnd > end ? end : shiftEnd; if (intervalStart < intervalEnd) { intervals.push({ start: intervalStart, end: intervalEnd }); } } cursor.setDate(cursor.getDate() + 1); } return intervals; } function snapToWorkingTime(date, cal, direction) { var MAX_DAYS = 60; var result = new Date(date); for (var attempt = 0; attempt < MAX_DAYS * 24 * 60; attempt++) { var dayStart = new Date(result); dayStart.setHours(0, 0, 0, 0); var shifts = getShiftsForDay(dayStart, cal); if (shifts.length > 0) { if (direction === "forward") { var currentMins = result.getHours() * 60 + result.getMinutes() + result.getSeconds() / 60; for (var _iterator2 = _createForOfIteratorHelperLoose(shifts), _step2; !(_step2 = _iterator2()).done;) { var shift = _step2.value; if (currentMins <= shift.end) { if (currentMins < shift.start) { result.setHours(Math.floor(shift.start / 60), Math.round(shift.start % 60), 0, 0); } return result; } } result.setDate(result.getDate() + 1); result.setHours(0, 0, 0, 0); } else { var _currentMins = result.getHours() * 60 + result.getMinutes() + result.getSeconds() / 60; for (var i = shifts.length - 1; i >= 0; i--) { if (_currentMins >= shifts[i].start) { if (_currentMins > shifts[i].end) { result.setHours(Math.floor(shifts[i].end / 60), Math.round(shifts[i].end % 60), 0, 0); } return result; } } result.setDate(result.getDate() - 1); result.setHours(23, 59, 59, 0); } } else { if (direction === "forward") { result.setDate(result.getDate() + 1); result.setHours(0, 0, 0, 0); } else { result.setDate(result.getDate() - 1); result.setHours(23, 59, 59, 0); } } } return date; } function getQuarterNumber(date, quarterStart) { var month = date.getMonth(); var offset = (month - quarterStart + 12) % 12; return Math.floor(offset / 3) + 1; } var styles$5 = {"gridRow":"_2dZTy","gridRowLookAhead":"_2RRca","gridRowLine":"_3rUKi","gridTick":"_RuwuK","gridTickDashed":"_Zh9jh","darkerGridRow":"_2M-tt"}; var GridBody = function GridBody(_ref) { var _projectCalendar$week; var tasks = _ref.tasks, scheduleType = _ref.scheduleType, dates = _ref.dates, rowHeight = _ref.rowHeight, svgWidth = _ref.svgWidth, columnWidth = _ref.columnWidth, todayColor = _ref.todayColor, weekendColor = _ref.weekendColor, rtl = _ref.rtl, _ref$virtualItems = _ref.virtualItems, virtualItems = _ref$virtualItems === void 0 ? [] : _ref$virtualItems, visibleStartY = _ref.visibleStartY, visibleEndY = _ref.visibleEndY, projectCalendar = _ref.projectCalendar, viewMode = _ref.viewMode; var visibleHeight = visibleEndY - visibleStartY; var now = new Date(); var items = virtualItems.length > 0 ? virtualItems : tasks.map(function (_, i) { return { index: i, start: i * rowHeight, end: (i + 1) * rowHeight, size: rowHeight, key: i }; }); var isDayOff = function isDayOff(date) { if (projectCalendar) return isOffDay(date, projectCalendar); var d = date.getDay(); return d === 0 || d === 6; }; var isTodayColumn = function isTodayColumn(i) { var date = dates[i]; var next = dates[i + 1]; if (next && date <= now && next > now) return true; if (!next && date <= now) { var step = date.getTime() - dates[i - 1].getTime(); var end = addToDate(date, step, "millisecond"); return end > now; } return false; }; var todayRects = []; var offDayRects = []; var weekStartDay = (_projectCalendar$week = projectCalendar === null || projectCalendar === void 0 ? void 0 : projectCalendar.week_start) != null ? _projectCalendar$week : 1; for (var i = 0, x = 0; i < dates.length; i++, x += columnWidth) { if (isTodayColumn(i)) { todayRects.push(React.createElement("rect", { key: "today-" + i, x: rtl ? x + columnWidth : x, y: visibleStartY, width: columnWidth, height: visibleHeight, fill: todayColor })); } if (viewMode !== ViewMode.Month && viewMode !== ViewMode.Quarter) { var _dates; var pEnd = (_dates = dates[i + 1]) != null ? _dates : addToDate(dates[i], 1, "day"); var periodMs = pEnd.getTime() - dates[i].getTime(); if (periodMs > 0) { var cursor = new Date(dates[i]); cursor.setHours(0, 0, 0, 0); while (cursor.getTime() < pEnd.getTime()) { if (isDayOff(cursor)) { var ds = cursor.getTime() - dates[i].getTime(); var de = ds + 86400000; var startFrac = Math.max(0, ds) / periodMs; var endFrac = Math.min(periodMs, de) / periodMs; var rw = (endFrac - startFrac) * columnWidth; if (rw > 0.5) { offDayRects.push(React.createElement("rect", { key: "offday-" + i + "-" + cursor.getTime(), x: x + startFrac * columnWidth, y: visibleStartY, width: rw, height: visibleHeight, fill: weekendColor })); } } cursor.setDate(cursor.getDate() + 1); } } } } var isDayView = viewMode === ViewMode.Day; var tickLines = dates.map(function (date, i) { var tickClass = styles$5.gridTick; if (isDayView) { tickClass = date.getDay() === weekStartDay ? styles$5.gridTick : styles$5.gridTickDashed; } return React.createElement("line", { key: "tick-" + i, x1: i * columnWidth, y1: visibleStartY, x2: i * columnWidth, y2: visibleEndY, className: tickClass }); }); var rowBackgrounds = []; var rowLines = []; var rowOverlays = []; rowLines.push(React.createElement("line", { key: "top-line", x1: 0, y1: visibleStartY, x2: svgWidth, y2: visibleStartY, className: styles$5.gridRowLine })); var showPerRowOffDays = viewMode !== ViewMode.Month && viewMode !== ViewMode.Quarter; for (var _iterator = _createForOfIteratorHelperLoose(items), _step; !(_step = _iterator()).done;) { var vi = _step.value; var task = tasks[vi.index]; if (!task) break; var y = vi.start; var isMilestone = task.type === "milestone"; var rowClass = isMilestone ? styles$5.darkerGridRow : scheduleType === "lookAhead" ? styles$5.gridRowLookAhead : styles$5.gridRow; rowBackgrounds.push(React.createElement("rect", { key: "bg-" + vi.key, x: 0, y: y, width: svgWidth, height: rowHeight, className: rowClass })); rowLines.push(React.createElement("line", { key: "line-" + vi.key, x1: 0, y1: y + rowHeight, x2: svgWidth, y2: y + rowHeight, className: styles$5.gridRowLine })); if (showPerRowOffDays) { var rowEven = vi.index % 2 === 1; var rowFill = isMilestone ? "#e6e4e4" : scheduleType === "lookAhead" ? "#fff" : rowEven ? "#f5f5f5" : "#fff"; rowOverlays.push(React.createElement("rect", { key: "row-clean-" + vi.key, x: 0, y: y, width: svgWidth, height: rowHeight, fill: rowFill })); var cal = task.calendar; if (cal) { for (var _i = 0, _x = 0; _i < dates.length; _i++, _x += columnWidth) { var _dates2; var _pEnd = (_dates2 = dates[_i + 1]) != null ? _dates2 : addToDate(dates[_i], 1, "day"); var _periodMs = _pEnd.getTime() - dates[_i].getTime(); if (_periodMs <= 0) continue; var _cursor = new Date(dates[_i]); _cursor.setHours(0, 0, 0, 0); while (_cursor.getTime() < _pEnd.getTime()) { if (isOffDay(_cursor, cal)) { var _ds = _cursor.getTime() - dates[_i].getTime(); var _de = _ds + 86400000; var _startFrac = Math.max(0, _ds) / _periodMs; var _endFrac = Math.min(_periodMs, _de) / _periodMs;