hy-vue-gantt
Version:
Evolution of vue-ganttastic package
8,570 lines • 364 kB
JavaScript
import dayjs from "dayjs";
import isoWeek from "dayjs/plugin/isoWeek";
import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js";
import isBetween from "dayjs/plugin/isBetween.js";
import weekOfYear from "dayjs/plugin/weekOfYear";
import advancedFormat from "dayjs/plugin/advancedFormat";
import customParseFormat from "dayjs/plugin/customParseFormat.js";
import dayOfYear from "dayjs/plugin/dayOfYear.js";
import localizedFormat from "dayjs/plugin/localizedFormat";
import utc from "dayjs/plugin/utc";
import { inject, computed, defineComponent, createElementBlock, openBlock, unref, Fragment, renderList, normalizeStyle, normalizeClass, ref, reactive, onMounted, createCommentVNode, createElementVNode, renderSlot, createTextVNode, toDisplayString, createVNode, watch, toRefs, nextTick, createBlock, Teleport, Transition, withCtx, normalizeProps, guardReactiveProps, withDirectives, vModelText, provide, resolveComponent, withModifiers, mergeProps, TransitionGroup, createSlots, useTemplateRef, vModelSelect, useCssVars, useSlots, toRef, onUnmounted, resolveDynamicComponent, isRef, createStaticVNode, h } from "vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { faChevronDown, faChevronRight, faSort, faArrowDownAZ, faArrowDownZA, faXmark, faCheck, faFileImport, faSpinner, faExclamationTriangle, faAngleUp, faAngleDown, faExpandAlt, faCompressAlt, faAnglesLeft, faAngleLeft, faAngleRight, faAnglesRight, faMagnifyingGlassMinus, faMagnifyingGlassPlus, faUndo, faRedo, faFileExport } from "@fortawesome/free-solid-svg-icons";
import { useIntervalFn, useMouseInElement, useElementBounding, computedWithControl, watchThrottled, useElementSize } from "@vueuse/core";
import { v4 } from "uuid";
import Holidays from "date-holidays";
import { cloneDeep } from "lodash-es";
import html2canvas from "html2canvas";
import jsPDF from "jspdf";
import * as XLSX from "xlsx";
import "dayjs/locale/it";
import "dayjs/locale/en";
import "dayjs/locale/fr";
import "dayjs/locale/de";
import "dayjs/locale/es";
import "dayjs/locale/pt";
import "dayjs/locale/ru";
import "dayjs/locale/zh-cn";
import "dayjs/locale/zh-tw";
import "dayjs/locale/ja";
import "dayjs/locale/ko";
import "dayjs/locale/nl";
import "dayjs/locale/pl";
import "dayjs/locale/cs";
const CONFIG_KEY = Symbol("CONFIG_KEY");
const BOOLEAN_KEY = Symbol("BOOLEAN_KEY");
const EMIT_BAR_EVENT_KEY = Symbol("EMIT_BAR_EVENT_KEY");
const BAR_CONTAINER_KEY = Symbol("BAR_CONTAINER_KEY");
const CHART_AREA_KEY = Symbol("CHART_AREA_KEY");
const CHART_WRAPPER_KEY = Symbol("CHART_WRAPPER_KEY");
const GANTT_ID_KEY = Symbol("GANTT_ID_KEY");
function provideConfig() {
const config = inject(CONFIG_KEY);
if (!config) {
throw Error("Failed to inject config!");
}
return config;
}
const DEFAULT_DATE_FORMAT = "YYYY-MM-DD HH:mm";
function useDayjsHelper(config = provideConfig()) {
const { chartStart, chartEnd, barStart, barEnd, dateFormat, locale } = config;
const chartStartDayjs = computed(() => toDayjs(chartStart.value));
const chartEndDayjs = computed(() => toDayjs(chartEnd.value));
dayjs.locale(locale.value);
const toDayjs = (input, startOrEnd) => {
let value;
if (startOrEnd !== void 0 && typeof input !== "string" && !(input instanceof Date)) {
value = startOrEnd === "start" ? input[barStart.value] : input[barEnd.value];
}
if (typeof input === "string") {
value = input;
} else if (input instanceof Date) {
return dayjs(input);
}
const format2 = dateFormat.value || DEFAULT_DATE_FORMAT;
return dayjs(value, format2, false);
};
const format = (input, pattern) => {
if (pattern === false) {
return input instanceof Date ? input : dayjs(input).toDate();
}
const inputDayjs = typeof input === "string" || input instanceof Date ? toDayjs(input) : input;
return inputDayjs.format(pattern);
};
const diffDates = () => {
return chartEndDayjs.value.diff(chartStartDayjs.value, "day");
};
return {
chartStartDayjs,
chartEndDayjs,
toDayjs,
format,
diffDates
};
}
const _hoisted_1$a = {
class: "g-grid-container",
ref: "time"
};
const _sfc_main$b = /* @__PURE__ */ defineComponent({
__name: "GGanttGrid",
props: {
timeaxisUnits: {},
internalPrecision: {}
},
setup(__props) {
const props = __props;
const { toDayjs } = useDayjsHelper();
const {
colors,
highlightedHours,
highlightedDaysInWeek,
highlightedDaysInMonth,
highlightedMonths,
highlightedWeek,
enableMinutes
} = provideConfig();
const highlightLine = (date) => {
if (props.internalPrecision === "hour") {
return isHighlightedHour(date) || isHighlightedDay(date) || isHighlightedMonth(date) || isHighlightedWeek(date);
}
if (props.internalPrecision === "day" || props.internalPrecision === "date") {
return isHighlightedDay(date) || isHighlightedMonth(date) || isHighlightedWeek(date);
}
if (props.internalPrecision === "week") {
return isHighlightedWeek(date) || isHighlightedMonth(date);
}
if (props.internalPrecision === "month") {
return isHighlightedMonth(date);
}
return false;
};
const isHighlightedHour = (date) => highlightedHours == null ? void 0 : highlightedHours.value.includes(date.getHours());
const isHighlightedDay = (date) => {
return (highlightedDaysInWeek == null ? void 0 : highlightedDaysInWeek.value.includes(date.getDay())) || (highlightedDaysInMonth == null ? void 0 : highlightedDaysInMonth.value.includes(date.getDate()));
};
const isHighlightedWeek = (date) => highlightedWeek == null ? void 0 : highlightedWeek.value.includes(toDayjs(date).week());
const isHighlightedMonth = (date) => highlightedMonths == null ? void 0 : highlightedMonths.value.includes(date.getMonth());
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", _hoisted_1$a, [
!unref(enableMinutes) ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(_ctx.timeaxisUnits.result.lowerUnits, ({ label, date, width }, index) => {
return openBlock(), createElementBlock("div", {
key: `${label}_${index}`,
class: "g-grid-line",
style: normalizeStyle({
width,
borderLeft: `1px solid ${unref(colors).gridAndBorder}`,
background: highlightLine(date) ? unref(colors).hoverHighlight : void 0
})
}, null, 4);
}), 128)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(_ctx.timeaxisUnits.result.lowerUnits, ({ label, date, width }, index) => {
return openBlock(), createElementBlock(Fragment, {
key: `${label}_${index}`
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.timeaxisUnits.globalMinuteStep, (step) => {
return openBlock(), createElementBlock("div", {
key: `${date}-${step}`,
class: normalizeClass([step, "g-grid-line step"]),
style: normalizeStyle({
width,
borderLeft: `1px solid ${unref(colors).gridAndBorder}`,
background: highlightLine(date) ? unref(colors).hoverHighlight : void 0
})
}, null, 6);
}), 128))
], 64);
}), 128))
], 512);
};
}
});
function useRowDragAndDrop(rows, isSorted, updateRows, emit) {
const dragState = ref({
isDragging: false,
draggedRow: null,
dropTarget: {
row: null,
position: "before"
}
});
const originalOrder = ref(/* @__PURE__ */ new Map());
const isValidDrop = (source, target) => {
const isParentOfTarget = (row) => {
var _a;
if (!((_a = row.children) == null ? void 0 : _a.length)) return false;
return row.children.some((child) => child === target || isParentOfTarget(child));
};
return !isParentOfTarget(source);
};
const handleDragStart = (row, event) => {
if (isSorted.value) return;
dragState.value.isDragging = true;
dragState.value.draggedRow = row;
if (event.dataTransfer) {
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text/plain", "");
}
if (originalOrder.value.size === 0) {
rows.value.forEach((row2, index) => {
if (row2.id) {
originalOrder.value.set(row2.id, index);
}
});
}
};
const handleDragOver = (row, event) => {
var _a;
if (!dragState.value.draggedRow || !isValidDrop(dragState.value.draggedRow, row)) {
return;
}
event.preventDefault();
const rect = event.currentTarget.getBoundingClientRect();
const mouseY = event.clientY - rect.top;
if ((_a = row.children) == null ? void 0 : _a.length) {
if (mouseY < rect.height * 0.25) {
dragState.value.dropTarget = { row, position: "before" };
} else if (mouseY > rect.height * 0.75) {
dragState.value.dropTarget = { row, position: "after" };
} else {
dragState.value.dropTarget = { row, position: "child" };
}
} else {
dragState.value.dropTarget = {
row,
position: mouseY < rect.height / 2 ? "before" : "after"
};
}
};
const isDescendant = (parent, potentialChild) => {
if (!parent.children) {
return false;
}
return parent.children.some(
(child) => child.id === potentialChild.id || isDescendant(child, potentialChild)
);
};
const findRowIndexById = (rows2, id) => {
var _a;
for (let i = 0; i < rows2.length; i++) {
if (rows2[i].id === id) {
return [i, rows2];
}
if ((_a = rows2[i].children) == null ? void 0 : _a.length) {
const [index, parentRows] = findRowIndexById(rows2[i].children, id);
if (index !== -1) {
return [index, parentRows];
}
}
}
return [-1, rows2];
};
const handleDrop = () => {
const { draggedRow, dropTarget } = dragState.value;
if (!draggedRow || !dropTarget.row || isSorted.value || draggedRow === dropTarget.row) {
return;
}
if (isDescendant(draggedRow, dropTarget.row)) {
return;
}
const [sourceIndex, sourceParentRows] = findRowIndexById(rows.value, draggedRow.id);
const [targetIndex, targetParentRows] = findRowIndexById(rows.value, dropTarget.row.id);
if (sourceIndex === -1 || targetIndex === -1) {
return;
}
const [removed] = sourceParentRows.splice(sourceIndex, 1);
const insertIndex = dropTarget.position === "after" ? targetIndex + 1 : targetIndex;
targetParentRows.splice(insertIndex, 0, removed);
updateRows([...rows.value]);
emit("row-drop", {
sourceRow: draggedRow,
targetRow: dropTarget.row,
newIndex: insertIndex,
parentId: targetParentRows === rows.value ? void 0 : dropTarget.row.id
});
dragState.value = {
isDragging: false,
draggedRow: null,
dropTarget: { row: null, position: "before" }
};
};
const resetOrder = () => {
if (originalOrder.value.size === 0) return;
rows.value.sort((a, b) => {
const aIndex = a.id ? originalOrder.value.get(a.id) ?? 0 : 0;
const bIndex = b.id ? originalOrder.value.get(b.id) ?? 0 : 0;
return aIndex - bIndex;
});
};
return {
dragState,
handleDragStart,
handleDragOver,
handleDrop,
resetOrder,
isDescendant,
findRowIndexById
};
}
function useColumnTouchResize() {
const touchState = ref({
isResizing: false,
startX: 0,
currentColumn: null,
initialWidth: 0
});
const resetTouchState = () => {
touchState.value = {
isResizing: false,
startX: 0,
currentColumn: null,
initialWidth: 0
};
};
const handleTouchStart = (e, column, currentWidth) => {
const touch = e.touches[0];
if (!touch) return;
e.preventDefault();
touchState.value = {
isResizing: true,
startX: touch.clientX,
currentColumn: column,
initialWidth: currentWidth
};
};
const handleTouchMove = (e, onResize) => {
const touch = e.touches[0];
if (!touch || !touchState.value.isResizing) return;
e.preventDefault();
const deltaX = touch.clientX - touchState.value.startX;
const newWidth = Math.max(50, touchState.value.initialWidth + deltaX);
if (touchState.value.currentColumn) {
onResize(touchState.value.currentColumn, newWidth);
}
};
const handleTouchEnd = () => {
if (touchState.value.isResizing) {
resetTouchState();
}
};
const handleTouchCancel = handleTouchEnd;
return {
touchState,
handleTouchStart,
handleTouchMove,
handleTouchEnd,
handleTouchCancel
};
}
function useRowTouchDrag() {
const touchState = ref({
isDragging: false,
startY: 0,
currentY: 0,
draggedRow: null,
dropTarget: {
row: null,
position: "before"
},
dragElement: null,
initialTransform: ""
});
const resetTouchState = () => {
if (touchState.value.dragElement) {
touchState.value.dragElement.style.transform = touchState.value.initialTransform;
}
touchState.value = {
isDragging: false,
startY: 0,
currentY: 0,
draggedRow: null,
dropTarget: {
row: null,
position: "before"
},
dragElement: null,
initialTransform: ""
};
};
const handleTouchStart = (event, row, element) => {
const touch = event.touches[0];
if (!touch) return;
setTimeout(() => {
if (touchState.value.isDragging) {
event.preventDefault();
}
}, 100);
touchState.value = {
isDragging: true,
startY: touch.clientY,
currentY: touch.clientY,
draggedRow: row,
dropTarget: {
row: null,
position: "before"
},
dragElement: element,
initialTransform: element.style.transform || ""
};
};
const handleTouchMove = (event, targetRow, rowElement) => {
var _a;
const touch = event.touches[0];
if (!touch || !touchState.value.isDragging || !touchState.value.dragElement) return;
event.preventDefault();
touchState.value.currentY = touch.clientY;
const deltaY = touch.clientY - touchState.value.startY;
touchState.value.dragElement.style.transform = `translateY(${deltaY}px)`;
const rect = rowElement.getBoundingClientRect();
const relativeY = touch.clientY - rect.top;
const position = relativeY / rect.height;
if (touchState.value.draggedRow !== targetRow) {
if ((_a = targetRow.children) == null ? void 0 : _a.length) {
if (position < 0.25) {
touchState.value.dropTarget = { row: targetRow, position: "before" };
} else if (position > 0.75) {
touchState.value.dropTarget = { row: targetRow, position: "after" };
} else {
touchState.value.dropTarget = { row: targetRow, position: "child" };
}
} else {
touchState.value.dropTarget = {
row: targetRow,
position: position < 0.5 ? "before" : "after"
};
}
}
};
const handleTouchEnd = (event) => {
if (!touchState.value.isDragging) return null;
const touch = event.changedTouches[0];
if (!touch) return null;
const result = {
sourceRow: touchState.value.draggedRow,
dropTarget: touchState.value.dropTarget,
dropPosition: touchState.value.dropTarget.position
};
resetTouchState();
return result;
};
return {
touchState,
handleTouchStart,
handleTouchMove,
handleTouchEnd,
resetTouchState
};
}
const _hoisted_1$9 = ["onClick"];
const _hoisted_2$7 = { class: "text-ellipsis" };
const _hoisted_3$7 = {
key: 0,
class: "sort-icon"
};
const _hoisted_4$6 = ["onMousedown", "onTouchstart"];
const _hoisted_5$6 = ["data-row-id", "draggable", "onDragstart", "onDragover", "onTouchstart", "onTouchmove"];
const _hoisted_6$5 = { class: "g-label-column-row-inner" };
const _hoisted_7$5 = ["onClick"];
const _hoisted_8$5 = { class: "text-ellipsis-value" };
const LONG_PRESS_DURATION = 500;
const INDENT_WIDTH = 24;
const _sfc_main$a = /* @__PURE__ */ defineComponent({
__name: "GGanttLabelColumn",
emits: ["scroll", "row-drop"],
setup(__props, { expose: __expose, emit: __emit }) {
const emit = __emit;
const rowManager = inject("useRows");
if (!rowManager) {
throw new Error("useRows does not provide ");
}
const { rows, sortState, toggleSort } = rowManager;
const {
font,
colors,
labelColumnTitle,
labelColumnWidth,
rowHeight,
maxRows,
multiColumnLabel,
precision,
barStart,
barEnd,
dateFormat,
rowLabelClass,
labelResizable,
enableRowDragAndDrop,
hideTimeaxis,
sortable,
showEventsAxis,
eventsAxisHeight
} = provideConfig();
const { toDayjs, format } = useDayjsHelper();
const columnWidths = reactive(/* @__PURE__ */ new Map());
const isDragging = ref(false);
const dragStartX = ref(0);
const draggedColumn = ref(null);
const labelContainer = ref(null);
const {
dragState,
handleDragStart: handleRowDragStart,
handleDragOver,
handleDrop
} = useRowDragAndDrop(
rows,
computed(() => sortState.value.direction !== "none"),
rowManager.updateRows,
(_event, payload) => emit("row-drop", payload)
);
const { touchState, handleTouchStart, handleTouchMove, handleTouchEnd, handleTouchCancel } = useColumnTouchResize();
const {
touchState: rowTouchState,
handleTouchStart: handleRowTouchStart,
handleTouchMove: handleRowTouchMove,
handleTouchEnd: handleRowTouchEnd,
resetTouchState: resetRowTouchState
} = useRowTouchDrag();
const columns = computed(() => {
var _a;
if (!((_a = multiColumnLabel.value) == null ? void 0 : _a.length) || !labelColumnTitle.value) {
return [{ field: "Label", sortable: sortable.value }];
}
const filteredColumns = multiColumnLabel.value.filter((col) => col.field !== "Label");
const labelColumn = { field: "Label", sortable: sortable.value };
return [labelColumn, ...filteredColumns];
});
const totalWidth = computed(() => {
let total = 0;
columnWidths.forEach((width) => total += width);
return total;
});
const getProcessedRows = computed(() => {
const processRows = (rows2, level = 0) => {
return rows2.flatMap((row) => {
var _a;
const processedRow = {
...row,
indentLevel: level
};
const isExpanded = row.id ? rowManager.isGroupExpanded(row.id) : false;
if (((_a = row.children) == null ? void 0 : _a.length) && isExpanded) {
return [processedRow, ...processRows(row.children, level + 1)];
}
return [processedRow];
});
};
return processRows(rows.value);
});
const labelContainerStyle = computed(() => {
if (maxRows.value === 0) return {};
const minRows = Math.min(maxRows.value, getProcessedRows.value.length);
return {
height: `${minRows * rowHeight.value}px`,
"overflow-y": "auto"
};
});
const columnSortableStates = computed(
() => columns.value.reduce(
(acc, column) => {
acc[column.field] = !!(column.sortable !== false && (sortable.value || !sortable.value && column.sortable) && (isValidColumn(column.field) || column.sortFn));
return acc;
},
{}
)
);
const headerHeight = computed(() => {
if (hideTimeaxis.value) return 0;
return showEventsAxis.value ? 80 + (eventsAxisHeight.value || 25) : 80;
});
let touchStartTime = 0;
const onRowTouchStart = (e, row) => {
if (!enableRowDragAndDrop.value || sortState.value.direction !== "none") return;
touchStartTime = Date.now();
const element = e.currentTarget;
handleRowTouchStart(e, row, element);
};
const onRowTouchMove = (e, targetRow) => {
if (!enableRowDragAndDrop.value || sortState.value.direction !== "none") return;
if (Date.now() - touchStartTime < LONG_PRESS_DURATION) {
resetRowTouchState();
return;
}
const rowElement = e.currentTarget;
handleRowTouchMove(e, targetRow, rowElement);
};
const onRowTouchEnd = (e) => {
if (!enableRowDragAndDrop.value || sortState.value.direction !== "none") return;
if (Date.now() - touchStartTime < LONG_PRESS_DURATION) {
const target = e.target;
const button = target.closest(".group-toggle-button");
if (button) {
const rowElement = target.closest("[data-row-id]");
if (rowElement) {
const rowId = rowElement.dataset.rowId;
if (rowId) {
rowManager.toggleGroupExpansion(rowId);
}
}
}
resetRowTouchState();
return;
}
const result = handleRowTouchEnd(e);
if (result && result.sourceRow && result.dropTarget.row) {
let newIndex = getProcessedRows.value.findIndex((r) => r === result.dropTarget.row);
if (result.dropTarget.position === "after") {
newIndex += 1;
}
const sourceIndex = getProcessedRows.value.findIndex((r) => r === result.sourceRow);
if (sourceIndex < newIndex) {
newIndex -= 1;
}
const payload = {
sourceRow: result.sourceRow,
targetRow: result.dropTarget.row,
newIndex,
parentId: result.dropTarget.position === "child" ? result.dropTarget.row.id : void 0
};
const newRows = [...getProcessedRows.value];
newRows.splice(sourceIndex, 1);
newRows.splice(newIndex, 0, result.sourceRow);
rowManager.updateRows(newRows);
emit("row-drop", payload);
}
};
const handleColumnTouchStart = (e, column) => {
if (!labelResizable) return;
const currentWidth = columnWidths.get(column) || labelColumnWidth.value;
handleTouchStart(e, column, currentWidth);
};
const handleColumnTouchMove = (e) => {
if (!labelResizable) return;
handleTouchMove(e, (column, newWidth) => {
columnWidths.set(column, newWidth);
});
};
const initializeColumnWidths = () => {
columns.value.forEach((column) => {
if (!columnWidths.has(column.field)) {
columnWidths.set(column.field, labelColumnWidth.value);
}
});
};
const isValidColumn = (field) => {
return ["Id", "Label", "StartDate", "EndDate", "Duration", "Progress"].includes(field);
};
const getSortIcon = (field) => {
if (field !== sortState.value.column || sortState.value.direction === "none") {
return faSort;
}
return sortState.value.direction === "asc" ? faArrowDownAZ : faArrowDownZA;
};
const getVisibleColumns = (row) => {
if (row.children && row.children.length > 0) {
return [{ field: "Label", sortable: sortable.value }];
}
return columns.value;
};
const rowClasses = (row) => {
var _a;
const classes = ["g-label-column-row"];
if (rowLabelClass.value) {
classes.push(rowLabelClass.value(row));
}
if ((_a = row.children) == null ? void 0 : _a.length) {
classes.push("g-label-column-group");
}
return classes;
};
const getDragClasses = (row) => {
if (!enableRowDragAndDrop.value) return {};
const isTarget = dragState.value.dropTarget.row === row;
const isDragged = dragState.value.draggedRow === row;
return {
"g-label-column-row-draggable": true,
"g-label-column-row-dragging": isDragged,
"g-label-column-row-drop-target": isTarget,
[`g-label-column-row-drop-${dragState.value.dropTarget.position}`]: isTarget
};
};
const getRowStyle = (row, isLabelColumn) => {
var _a;
if (!isLabelColumn) {
return {
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%"
};
}
const style = {
display: "flex",
alignItems: "center",
width: "100%",
position: "relative"
};
if (!((_a = row.children) == null ? void 0 : _a.length) && !row.indentLevel) {
style.paddingLeft = `${INDENT_WIDTH}px`;
} else if (row.indentLevel) {
style.paddingLeft = `${row.indentLevel * INDENT_WIDTH}px`;
}
return style;
};
const getCellStyle = (isLabelColumn) => {
const style = {
display: "flex",
alignItems: "center",
width: "100%"
};
if (!isLabelColumn) {
style.justifyContent = "center";
style.paddingLeft = "0";
}
return style;
};
const getColumnStyle = (column, isGroup) => {
if (isGroup && column === "Label") {
return {
width: "100%",
minWidth: "100%",
maxWidth: "100%",
position: "relative",
flexShrink: 0,
flexGrow: 0
};
}
return {
width: `${columnWidths.get(column) || labelColumnWidth.value}px`,
minWidth: `${columnWidths.get(column) || labelColumnWidth.value}px`,
maxWidth: `${columnWidths.get(column) || labelColumnWidth.value}px`,
position: "relative",
flexShrink: 0,
flexGrow: 0
};
};
const getRowValue = (row, column, index) => {
if (column.valueGetter) {
return column.valueGetter(row);
}
switch (column.field) {
case "Id":
return row.id ?? index + 1;
case "Label":
return row.label;
case "StartDate": {
if (!row.bars.length) return "-";
const minDate = row.bars.reduce((min, bar) => {
const currentStart = bar[barStart.value];
return !min || toDayjs(currentStart).isBefore(toDayjs(min)) ? currentStart : min;
}, "");
return format(minDate, dateFormat.value);
}
case "EndDate": {
if (!row.bars.length) return "-";
const maxDate = row.bars.reduce((max, bar) => {
const currentEnd = bar[barEnd.value];
return !max || toDayjs(currentEnd).isAfter(toDayjs(max)) ? currentEnd : max;
}, "");
return format(maxDate, dateFormat.value);
}
case "Duration": {
if (!row.bars.length) return "-";
const minStart = row.bars.reduce((min, bar) => {
const currentStart = bar[barStart.value];
return !min || toDayjs(currentStart).isBefore(toDayjs(min)) ? currentStart : min;
}, "");
const maxEnd = row.bars.reduce((max, bar) => {
const currentEnd = bar[barEnd.value];
return !max || toDayjs(currentEnd).isAfter(toDayjs(max)) ? currentEnd : max;
}, "");
return calculateDuration(minStart, maxEnd);
}
case "Progress": {
if (!row.bars.length) return "-";
const progressValues = row.bars.map((bar) => bar.ganttBarConfig.progress).filter((progress) => progress !== void 0);
if (progressValues.length === 0) return "-";
const averageProgress = progressValues.reduce((sum, curr) => sum + curr, 0) / progressValues.length;
return `${Math.round(averageProgress)}%`;
}
default:
return "";
}
};
const calculateDuration = (startDate, endDate) => {
const start = toDayjs(startDate);
const end = toDayjs(endDate);
switch (precision.value) {
case "hour":
return `${end.diff(start, "hour")}h`;
case "day":
case "date":
return `${end.diff(start, "day")}d`;
case "week":
return `${end.diff(start, "week")}w`;
case "month":
return `${end.diff(start, "month")}m`;
default:
return `${end.diff(start, "day")}d`;
}
};
const handleLabelScroll = (e) => {
const target = e.target;
emit("scroll", target.scrollTop);
};
const handleGroupToggle = (row, event) => {
event.stopPropagation();
if (row.id) {
rowManager.toggleGroupExpansion(row.id);
}
};
const handleDragStart = (e, column) => {
isDragging.value = true;
dragStartX.value = e.clientX;
draggedColumn.value = column;
document.addEventListener("mousemove", handleDrag);
document.addEventListener("mouseup", handleDragEnd);
};
const handleDrag = (e) => {
if (!isDragging.value || !draggedColumn.value) return;
const deltaX = e.clientX - dragStartX.value;
const currentWidth = columnWidths.get(draggedColumn.value) || labelColumnWidth.value;
const newWidth = Math.max(50, currentWidth + deltaX);
columnWidths.set(draggedColumn.value, newWidth);
dragStartX.value = e.clientX;
};
const handleDragEnd = () => {
isDragging.value = false;
draggedColumn.value = null;
document.removeEventListener("mousemove", handleDrag);
document.removeEventListener("mouseup", handleDragEnd);
};
onMounted(() => {
initializeColumnWidths();
});
__expose({
setScroll: (value) => {
if (labelContainer.value) {
labelContainer.value.scrollTop = value;
}
}
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "g-label-column",
style: normalizeStyle({
fontFamily: unref(font),
color: unref(colors).text,
minWidth: `100%`,
flex: `0 0 ${totalWidth.value}px`,
borderRight: `1px solid ${unref(colors).gridAndBorder}`
})
}, [
!unref(hideTimeaxis) ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-label-column-header",
style: normalizeStyle({
background: unref(colors).primary,
borderBottom: `1px solid ${unref(colors).gridAndBorder}`,
height: `${headerHeight.value}px`,
minHeight: `${headerHeight.value}px`
})
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(columns.value, (column) => {
return openBlock(), createElementBlock(Fragment, { key: column }, [
isValidColumn(column.field) || column.valueGetter ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(["g-label-column-header-cell", { sortable: columnSortableStates.value[column.field] }]),
role: "columnheader",
style: normalizeStyle(getColumnStyle(column.field, false))
}, [
createElementVNode("div", {
class: "header-content",
onClick: ($event) => columnSortableStates.value[column.field] ? unref(toggleSort)(column.field) : void 0
}, [
createElementVNode("span", _hoisted_2$7, [
renderSlot(_ctx.$slots, `label-column-title-${column.field.toLowerCase()}`, {}, () => [
createTextVNode(toDisplayString(column.field), 1)
])
]),
columnSortableStates.value[column.field] ? (openBlock(), createElementBlock("span", _hoisted_3$7, [
createVNode(unref(FontAwesomeIcon), {
icon: getSortIcon(column.field)
}, null, 8, ["icon"])
])) : createCommentVNode("", true)
], 8, _hoisted_1$9),
unref(labelResizable) ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(["column-resizer", {
"is-dragging": isDragging.value && draggedColumn.value === column.field,
"is-touch-resizing": unref(touchState).isResizing && unref(touchState).currentColumn === column.field
}]),
onMousedown: (e) => handleDragStart(e, column.field),
onTouchstart: (e) => handleColumnTouchStart(e, column.field),
onTouchmove: handleColumnTouchMove,
onTouchend: _cache[0] || (_cache[0] = //@ts-ignore
(...args) => unref(handleTouchEnd) && unref(handleTouchEnd)(...args)),
onTouchcancel: _cache[1] || (_cache[1] = //@ts-ignore
(...args) => unref(handleTouchCancel) && unref(handleTouchCancel)(...args))
}, null, 42, _hoisted_4$6)) : createCommentVNode("", true)
], 6)) : createCommentVNode("", true)
], 64);
}), 128))
], 4)) : createCommentVNode("", true),
createElementVNode("div", {
class: "g-label-column-rows",
style: normalizeStyle(labelContainerStyle.value),
ref_key: "labelContainer",
ref: labelContainer,
onScroll: handleLabelScroll
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(getProcessedRows.value, (row, index) => {
var _a;
return openBlock(), createElementBlock("div", {
key: `${row.id || row.label}_${index}`,
"data-row-id": row.id,
style: normalizeStyle({
background: ((_a = row.children) == null ? void 0 : _a.length) ? unref(colors).rowContainer : index % 2 === 0 ? unref(colors).ternary : unref(colors).quartenary,
height: `${unref(rowHeight)}px`,
borderBottom: `1px solid ${unref(colors).gridAndBorder}`,
transform: unref(rowTouchState).draggedRow === row ? `translateY(${unref(rowTouchState).currentY - unref(rowTouchState).startY}px)` : void 0,
zIndex: unref(rowTouchState).draggedRow === row ? 1e3 : void 0
}),
class: normalizeClass([
rowClasses(row),
getDragClasses(row),
{
"is-touch-dragging": unref(rowTouchState).draggedRow === row,
"is-touch-drop-target": unref(rowTouchState).dropTarget.row === row,
[`is-touch-drop-${unref(rowTouchState).dropTarget.position}`]: unref(rowTouchState).dropTarget.row === row
}
]),
draggable: unref(enableRowDragAndDrop),
onDragstart: ($event) => unref(handleRowDragStart)(row, $event),
onDragover: ($event) => unref(handleDragOver)(row, $event),
onDrop: _cache[2] || (_cache[2] = ($event) => unref(handleDrop)()),
onTouchstart: (e) => onRowTouchStart(e, row),
onTouchmove: (e) => onRowTouchMove(e, row),
onTouchend: onRowTouchEnd,
onTouchcancel: _cache[3] || (_cache[3] = //@ts-ignore
(...args) => unref(resetRowTouchState) && unref(resetRowTouchState)(...args))
}, [
createElementVNode("div", _hoisted_6$5, [
(openBlock(true), createElementBlock(Fragment, null, renderList(getVisibleColumns(row), (column) => {
var _a2, _b;
return openBlock(), createElementBlock(Fragment, {
key: column.field
}, [
isValidColumn(column.field) || column.valueGetter ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-label-column-cell",
style: normalizeStyle(getColumnStyle(column.field, Boolean((_a2 = row.children) == null ? void 0 : _a2.length)))
}, [
createElementVNode("div", {
style: normalizeStyle(getCellStyle(column.field === "Label"))
}, [
createElementVNode("div", {
style: normalizeStyle(getRowStyle(row, column.field === "Label")),
class: "cell-content"
}, [
column.field === "Label" && row.children && row.children.length > 0 ? (openBlock(), createElementBlock("button", {
key: 0,
class: "group-toggle-button",
onClick: ($event) => handleGroupToggle(row, $event)
}, [
createVNode(unref(FontAwesomeIcon), {
icon: row.id && unref(rowManager).isGroupExpanded(row.id) ? unref(faChevronDown) : unref(faChevronRight),
class: "group-icon"
}, null, 8, ["icon"])
], 8, _hoisted_7$5)) : createCommentVNode("", true),
createElementVNode("span", _hoisted_8$5, [
((_b = row.children) == null ? void 0 : _b.length) ? renderSlot(_ctx.$slots, `label-column-${column.field.toLowerCase()}-group`, {
key: 0,
row,
value: getRowValue(row, column, index)
}, () => [
renderSlot(_ctx.$slots, `label-column-${column.field.toLowerCase()}`, {
row,
value: getRowValue(row, column, index)
}, () => [
createTextVNode(toDisplayString(getRowValue(row, column, index)), 1)
])
]) : renderSlot(_ctx.$slots, `label-column-${column.field.toLowerCase()}`, {
key: 1,
row,
value: getRowValue(row, column, index)
}, () => [
createTextVNode(toDisplayString(getRowValue(row, column, index)), 1)
])
])
], 4)
], 4)
], 4)) : createCommentVNode("", true)
], 64);
}), 128))
])
], 46, _hoisted_5$6);
}), 128))
], 36)
], 4);
};
}
});
function useHolidays(config) {
const { chartStartDayjs, chartEndDayjs } = useDayjsHelper(config);
const holidays = ref([]);
const hd = new Holidays();
const loadHolidays = (country) => {
hd.init(country);
const start = chartStartDayjs.value.toDate();
const end = chartEndDayjs.value.toDate();
const holidaysListStart = hd.getHolidays(start);
const holidaysListEnd = hd.getHolidays(end);
const startHolidays = holidaysListStart.map((h2) => ({
date: new Date(h2.date),
name: h2.name,
type: h2.type
}));
const endHolidays = holidaysListEnd.map((h2) => ({
date: new Date(h2.date),
name: h2.name,
type: h2.type
}));
holidays.value = [...startHolidays, ...endHolidays];
};
const getHolidayInfo = (date) => {
const holiday = holidays.value.find((h2) => h2.date.toDateString() === date.toDateString());
if (!holiday) return null;
return {
isHoliday: true,
holidayName: holiday.name,
holidayType: holiday.type
};
};
watch(
() => {
var _a;
return (_a = config.holidayHighlight.value) == null ? void 0 : _a.toUpperCase();
},
(newCountry) => {
if (newCountry) {
loadHolidays(newCountry);
} else {
holidays.value = [];
}
},
{ immediate: true }
);
return {
holidays,
getHolidayInfo
};
}
const ganttWidth = ref();
const MAX_ZOOM = 10;
const MIN_ZOOM = 1;
const CACHE_TTL = 5 * 60 * 1e3;
const capitalizeString = (str) => {
if (!str) return str;
return str.normalize("NFD").replace(new RegExp("^\\p{L}", "u"), (letter) => letter.toLocaleUpperCase());
};
const capitalizeWords = (str) => {
return str.split(/(\s+|\.|\,)/).map((word) => {
if (new RegExp("^\\p{L}", "u").test(word)) {
return capitalizeString(word);
}
return word;
}).join("");
};
function useTimeaxisUnits(config = provideConfig()) {
const { getHolidayInfo } = useHolidays(config);
const {
precision: configPrecision,
holidayHighlight,
locale,
timeaxisEvents,
baseUnitWidth,
defaultZoom
} = config;
const internalPrecision = ref(configPrecision.value);
const zoomLevel = ref(defaultZoom.value);
const processedTimeaxisEvents = ref([]);
const cache = {
lower: /* @__PURE__ */ new Map(),
upper: /* @__PURE__ */ new Map(),
events: /* @__PURE__ */ new Map()
};
const displayFormats = {
hour: "HH",
date: "DD.MMM",
day: "DD.MMM",
week: "WW",
month: "MMMM YYYY",
year: "YYYY"
};
const getDisplayFormat = (unit) => {
if (unit === "isoWeek") return displayFormats.week;
return displayFormats[unit] || displayFormats.day;
};
const precisionHierarchy = ["hour", "day", "week", "month"];
const unitWidth = computed(() => baseUnitWidth.value * zoomLevel.value);
const getNextPrecision = (currentPrecision) => {
const currentIndex = precisionHierarchy.indexOf(currentPrecision);
if (currentIndex < precisionHierarchy.length - 1) {
return precisionHierarchy[currentIndex + 1];
}
return currentPrecision;
};
const getPreviousPrecision = (currentPrecision) => {
const currentIndex = precisionHierarchy.indexOf(currentPrecision);
const configIndex = precisionHierarchy.indexOf(configPrecision.value);
if (currentIndex > 0 && currentIndex > configIndex) {
return precisionHierarchy[currentIndex - 1];
}
return currentPrecision;
};
const upperPrecision = computed(() => {
const precisionMap = {
hour: "day",
day: "month",
date: "month",
week: "month",
month: "year"
};
return precisionMap[internalPrecision.value] || "month";
});
const getCacheKey = (startDate, endDate, precision, zoom) => {
return `${startDate.valueOf()}-${endDate.valueOf()}-${precision}-${zoom}`;
};
const getEventsCacheKey = (startDate, endDate, zoom) => {
return `${startDate.valueOf()}-${endDate.valueOf()}-${zoom}`;
};
const getFromCache = (cacheMap, key) => {
const entry = cacheMap.get(key);
if (!entry) return null;
if (Date.now() - entry.timestamp > CACHE_TTL) {
cacheMap.delete(key);
return null;
}
return entry.units;
};
const getEventsFromCache = (key) => {
const entry = cache.events.get(key);
if (!entry) return null;
if (Date.now() - entry.timestamp > CACHE_TTL) {
cache.events.delete(key);
return null;
}
return entry.events;
};
const setInCache = (cacheMap, key, units) => {
cacheMap.set(key, {
timestamp: Date.now(),
units
});
};
const setEventsInCache = (key, events) => {
cache.events.set(key, {
timestamp: Date.now(),
events
});
};
const getDayjsUnit = (unit) => {
const unitMap = {
hour: "hour",
day: "day",
date: "day",
week: "week",
month: "month",
year: "year",
isoWeek: "week"
};
return unitMap[unit];
};
const createTimeaxisUnit = (moment, format, width) => {
const date = moment.toDate();
const holidayInfo = holidayHighlight.value ? getHolidayInfo(date) : null;
const formattedLabel = moment.format(format);
const capitalizedLabel = capitalizeWords(formattedLabel);
return {
label: capitalizedLabel,
value: String(moment),
date,
width,
isHoliday: (holidayInfo == null ? void 0 : holidayInfo.isHoliday) || false,
holidayName: holidayInfo == null ? void 0 : holidayInfo.holidayName,
holidayType: holidayInfo == null ? void 0 : holidayInfo.holidayType
};
};
const updateEventPositions = () => {
var _a;
if (!((_a = timeaxisEvents.value) == null ? void 0 : _a.length)) {
processedTimeaxisEvents.value = [];
return;
}
const { chartStartDayjs, chartEndDayjs } = useDayjsHelper(config);
const totalMinutes = chartEndDayjs.value.diff(chartStartDayjs.value, "minutes");
const eventsCacheKey = getEventsCacheKey(
chartStartDayjs.value,
chartEndDayjs.value,
zoomLevel.value
);
const cachedEvents = getEventsFromCache(eventsCacheKey);
if (cachedEvents) {
processedTimeaxisEvents.value = cachedEvents;
return;
}
const processedEvents = timeaxisEvents.value.map((event) => {
const eventStartDayjs = dayjs(event.startDate);
const eventEndDayjs = dayjs(event.endDate);
if (eventEndDayjs.isBefore(chartStartDayjs.value) || eventStartDayjs.isAfter(chartEndDayjs.value)) {
return {
...event,
width: "0px",
xPosition: 0
};
}
const startTime = eventStartDayjs.isBefore(chartStartDayjs.value) ? chartStartDayjs.value : eventStartDayjs;
const endTime = eventEndDayjs.isAfter(chartEndDayjs.value) ? chartEndDayjs.value : eventEndDayjs;
const startMinutes = startTime.diff(chartStartDayjs.value, "minutes");
const endMinutes = endTime.diff(chartStartDayjs.value, "minutes");
const startPosition = startMinutes / totalMinutes * ganttWidth.value;
const endPosition = endMinutes / totalMinutes * ganttWidth.value;
const width = Math.max(endPosition - startPosition, 2);
return {
...event,
width: `${width}px`,
xPosition: startPosition
};
});
processedTimeaxisEvents.value = processedEvents;
setEventsInCache(eventsCacheKey, processedEvents);
};
watch(
() => configPrecision.value,
() => {
internalPrecision.value = configPrecision.value;
zoomLevel.value = defaultZoom.value;
}
);
watch([() => holidayHighlight.value, () => locale.value], () => {
dayjs.locale(locale.value);
cache.lower.clear();
cache.upper.clear();
});
watch(ganttWidth, () => {
updateEventPositions();
});
watch(
[() => timeaxisEvents.value, () => config.chartStart.value, () => config.chartEnd.value],
() => {
updateEventPositions();
},
{ deep: true }
);
const timeaxisUnits = computed(() => {
var _a;
const { chartStartDayjs, chartEndDayjs } = useDayjsHelper(config);
const lowerCacheKey = getCacheKey(
chartStartDayjs.value,
chartEndDayjs.value,
internalPrecision.value,
zoomLevel.value
);
let lowerUnits = getFromCache(cache.lower, lowerCacheKey);
const lowerUnitsByStartTime = /* @__PURE__ */ new Map();
if (!lowerUnits) {
lowerUnits = [];
let currentLower = chartStartDayjs.value.clone();
while (currentLower.isBefore(chartEndDayjs.value)) {
const unit = createTimeaxisUnit(
currentLower,
getDisplayFormat(internalPrecision.value),
`${unitWidth.value}px`
);
lowerUnits.push(unit);
lowerUnitsByStartTime.set(currentLower.valueOf(), unit);
currentLower = currentLower.add(1, getDayjsUnit(internalPrecision.value));
}
setInCache(cache.lower, lowerCacheKey, lowerUnits);
}
const upperCacheKey = getCacheKey(
chartStartDayjs.value,
chartEndDayjs.value,
upperPrecision.value,
zoomLevel.value
);
let upperUnits = getFromCache(cache.upper, upperCacheKey);
if (!upperUnits) {
upperUnits = [];
let currentUpper = chartStartDayjs.value.startOf(getDayjsUnit(upperPrecision.value));
while (currentUpper.isBefore(chartEndDayjs.value)) {
const nextUpper = currentUpper.add(1, getDayjsUnit(upperPrecision.value));
const effectiveStart = currentUpper.isBefore(chartStartDayjs.value) ? chartStartDayjs.value : currentUpper;
const effectiveEnd = nextUpper.isAfter(chartEndDayjs.value) ? chartEndDayjs.value : nextUpper;
let unitsInPeriod = 0;
if (internalPrecision.value === "day") {
unitsInPeriod = Math.ceil(effectiveEnd.diff(effectiveStart, "day", true));
} else if (internalPrecision.value === "month") {
unitsInPeriod = Math.ceil(effectiveEnd.diff(effectiveStart, "month", true));
} else if (internalPrecision.value === "week") {
unitsInPeriod = Math.ceil(effectiveEnd.diff(effectiveStart, "week", true));
} else {
unitsInPeriod = Math.ceil(effectiveEnd.diff(effectiveStart, "hour", true));
}
const totalWidth = unitsInPeriod * unitWidth.value;
if (totalWidth > 0) {
upperUnits.push(
createTimeaxisUnit(
currentUpper,
getDisplayFormat(upperPrecision.value),
`${totalWidth}px`
)
);
}
currentUpper = nextUpper;
}
setInCache(cache.upper, upperCacheKey, upperUnits);
}
if (((_a = timeaxisEvents.value) == null ? void 0 : _a.length) && processedTimeaxisEvents.value.length === 0) {
updateEventPositions();
}
const minuteSteps = calculateMinuteSteps();
cleanExpiredCache();
return {
result: {
upperUnits,
lowerUnits,
events: processedTimeaxisEvents.value
},
globalMinuteStep: minuteSteps
};
});
const calculateMinuteSteps = () => {
if (!config.enableMinutes.value || internalPrecision.value !== "hour") {
return [];
}
const cellWidth = unitWidth.value;
const minCellWidth = 16;
const possibleDivisions = Math.floor(cellWidth / minCellWidth);
let step;
if (possibleDivisions >= 60) step = 1;
else if (possibleDivisions >= 12) step = 5;
else if (possibleDivisions >= 6) step = 10;
else if (possibleDivisions >= 4) step = 15;
else if (possibleDivisions >= 2) step = 30;
else return ["00"];
return Array.from({ length: 60 / step }, (_, i) => (i * step).toString().padStart(2, "0"));
};
const cleanExpiredCache = () => {
const now = Date.now();
for (const [key, entry] of cache.lower.entries()) {
if (now - entry.timestamp > CACHE_TTL) {
cache.lower.delete(key);
}
}
for (const [key, entry] of cache.upper.entries()) {
if (now - entry.timestamp > CACHE_TTL) {
cache.upper.delete(key);
}
}
for (const [key, entry] of cache.events.entries()) {
if (now - entry.timestamp > CACHE_TTL) {
cache.events.delete(key);
}
}
};
const adjustZoomAndPrecision = (increase) => {
if (increase) {
if (zoomLevel.value === MAX_ZOOM) {
const previousPrecision = getPreviousPrecision(internalPrecision.value);
if (previousPrecision !== internalPrecision.value) {
internalPrecision.value = previousPrecision;
zoomLevel.value = MIN_ZOOM;
}
} else {
zoomLevel.value += 1;
}
} else {
if (zoomLevel.value === MIN_ZOOM) {
const nextPrecision = getNextPrecision(internalPrecision.value);
if (nextPrecision !== internalPrecision.value) {
internalPrecision.value = nextPrecision;
zoomLevel.value = MAX_ZOOM;
}
} else {
zoomLevel.value -= 1;
}
}
};
watch(
[
() => config.chartStart.value,
() => config.chartEnd.value,
internalPrecision,
zoomLevel,
() => config.timeaxisEvents.value
],
() => {
cache.lower.clear();
cache.upper.clear();
cache.events.clear();
updateEventPositions();
}
);
updateEventPositions();
return {
timeaxisUnits,
internalPrecision,
zoomLevel,
adjustZoomAndPrecision
};
}
const _hoisted_1$8 = { class: "g-gantt-event-tooltip-content" };
const _hoisted_2$6 = { class: "g-gantt-event-tooltip-title" };
const _hoisted_3$6 = { class: "g-gantt-event-tooltip-time" };
const _hoisted_4$5 = {
key: 0,
class: "g-gantt-event-tooltip-description"
};
const _hoisted_5$5 = { class: "g-gantt-holiday-tooltip-content" };
const DEFAULT_DOT_COLOR = "cadetblue";
const _sfc_main$9 = /* @__PURE__ */ defineComponent({
__name: "GGanttTooltip",
props: {
modelValue: { type: Boolean },
type: {},
bar: {},
event: {},
unit: {},
targetElement: {}
},
setup(__props) {
const TOOLTIP_FORMATS = {
hour: "HH:mm",
day: "DD. MMM HH:mm",
date: "DD. MMMM YYYY",
month: "DD. MMMM YYYY",
week: "DD. MMMM YYYY (WW)"
};
const props = __props;
const { bar, event, unit, targetElement, type } = toRefs(props);
const { precision, font, barStart, barEnd, rowHeight, milestones, colors, dateFormat } = provideConfig();
const tooltipTop = ref("0px");
const tooltipLeft = ref("0px");
const { toDayjs, format } = useDayjsHelper();
watch(
[
() => props.bar,
() => props.event,
() => props.unit,
() => props.targetElement,
() => props.type
],
async () => {
var _a;
await nextTick();
if (type.value === "bar" && bar.value) {
const barId = bar.value.ganttBarConfig.id || "";
if (!barId) {
return;
}
const barElement = document.getElementById(barId);
const { top, left } = (barElement == null ? void 0 : barElement.getBoundingClientRect()) || {
top: 0,
left: 0
};
const leftValue = Math.max(left, 10);
tooltipTop.value = `${top + rowHeight.value - 10}px`;
tooltipLeft.value = `${leftValue}px`;
} else if (type.value === "event" && event.value && targetElement.value) {
const rect = targetElement.value.getBoundingClientRect();
tooltipTop.value = `${rect.top - 95}px`;
tooltipLeft.value = `${rect.left + rect.width / 2}px`;
} else if (type.value === "holiday" && ((_a = unit.value) == null ? void 0 : _a.holidayName) && targetElement.value) {
const rect = targetElement.value.getBoundingClientRect();
tooltipTop.value = `${rect.top - 30}px`;
tooltipLeft.value = `${rect.left + rect.width / 2}px`;
}
},
{ deep: true, immediate: true }
);
const dotColor = computed(() => {
var _a, _b;
return ((_b = (_a = bar.value) == null ? void 0 : _a.ganttBarConfig.style) == null ? void 0 : _b.background) || DEFAULT_DOT_COLOR;
});
const barStartRaw = computed(() => {
var _a;
return (_a = bar.value) == null ? void 0 : _a[barStart.value];
});
const barEndRaw = computed(() => {
var _a;
return (_a = bar.value) == null ? void 0 : _a[barEnd.value];
});
const tooltipContent = computed(() => {
if (!bar.value) {
return "";
}
const milestone = milestones.value.find((m) => {
var _a;
return m.id === ((_a = bar.value) == null ? void 0 : _a.ganttBarConfig.milestoneId);
});
const format2 = TOOLTIP_FORMATS[precision.value];
const barStartFormatted = toDayjs(barStartRaw.value).format(format2);
const barEndFormatted = toDayjs(barEndRaw.value).format(format2);
const milestoneName = milestone ? ` - (${milestone.name})` : "";
return `${barStartFormatted} – ${barEndFormatted}${milestoneName}`;
});
const formatDate = (date) => {
return format(date, dateFormat.value);
};
const tooltipClass = computed(() => {
switch (type.value) {
case "bar":
return "g-gantt-tooltip";
case "event":
return "g-gantt-event-tooltip";
case "holiday":
return "g-gantt-holiday-tooltip";
default:
return "g-gantt-tooltip";
}
});
const tooltipStyle = computed(() => {
const baseStyle = {
top: tooltipTop.value,
left: tooltipLeft.value,
fontFamily: font.value
};
switch (type.value) {
case "bar":
return baseStyle;
case "event":
return {
...baseStyle,
background: colors.value.primary,
color: colors.value.text
};
case "holiday":
return baseStyle;
default:
return baseStyle;
}
});
return (_ctx, _cache) => {
return openBlock(), createBlock(Teleport, { to: "body" }, [
createVNode(Transition, {
name: "g-fade",
mode: "out-in"
}, {
default: withCtx(() => {
var _a;
return [
_ctx.modelValue && unref(type) === "bar" && unref(bar) ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(tooltipClass.value),
style: normalizeStyle(tooltipStyle.value)
}, [
createElementVNode("div", {
class: "g-gantt-tooltip-color-dot",
style: normalizeStyle({ background: dotColor.value })
}, null, 4),
renderSlot(_ctx.$slots, "default", {
bar: unref(bar),
barStart: barStartRaw.value,
barEnd: barEndRaw.value
}, () => [
createTextVNode(toDisplayString(tooltipContent.value), 1)
])
], 6)) : _ctx.modelValue && unref(type) === "event" && unref(event) ? (openBlock(), createElementBlock("div", {
key: 1,
class: normalizeClass(tooltipClass.value),
style: normalizeStyle(tooltipStyle.value)
}, [
renderSlot(_ctx.$slots, "event-tooltip", {
event: unref(event),
formatDate
}, () => [
createElementVNode("div", _hoisted_1$8, [
createElementVNode("div", _hoisted_2$6, toDisplayString(unref(event).label), 1),
createElementVNode("div", _hoisted_3$6, toDisplayString(formatDate(unref(event).startDate)) + " - " + toDisplayString(formatDate(unref(event).endDate)), 1),
unref(event).description ? (openBlock(), createElementBlock("div", _hoisted_4$5, toDisplayString(unref(event).description), 1)) : createCommentVNode("", true)
])
])
], 6)) : _ctx.modelValue && unref(type) === "holiday" && ((_a = unref(unit)) == null ? void 0 : _a.holidayName) ? (openBlock(), createElementBlock("div", {
key: 2,
class: normalizeClass(tooltipClass.value),
style: normalizeStyle(tooltipStyle.value)
}, [
renderSlot(_ctx.$slots, "holiday-tooltip", { unit: unref(unit) }, () => [
createElementVNode("div", _hoisted_5$5, toDisplayString(unref(unit).holidayName), 1)
])
], 6)) : createCommentVNode("", true)
];
}),
_: 3
})
]);
};
}
});
const _hoisted_1$7 = { class: "g-timeunits-container" };
const _hoisted_2$5 = ["onMouseenter"];
const _hoisted_3$5 = { class: "g-timeunits-container" };
const _hoisted_4$4 = ["onMouseenter"];
const _hoisted_5$4 = { class: "g-timeunit-min" };
const _hoisted_6$4 = { class: "label-unit" };
const _hoisted_7$4 = {
key: 0,
class: "g-timeunit-step"
};
const _hoisted_8$4 = { class: "label-unit" };
const _hoisted_9$4 = ["onMouseenter"];
const _hoisted_10$3 = { class: "g-timeaxis-event-label" };
const _sfc_main$8 = /* @__PURE__ */ defineComponent({
__name: "GGanttTimeaxis",
props: {
timeaxisUnits: {},
internalPrecision: {}
},
emits: ["dragStart", "drag", "dragEnd"],
setup(__props, { expose: __expose, emit: __emit }) {
const props = __props;
const { timeaxisUnits, internalPrecision } = toRefs(props);
const emit = __emit;
const timeaxisElement = ref(null);
const hoveredUnit = ref();
const showTooltip = ref(false);
const hoveredElement = ref(null);
const hoveredEvent = ref();
const showEventTooltip = ref(false);
const hoveredEventElement = ref(null);
const {
precision,
colors,
holidayHighlight,
dayOptionLabel,
enableMinutes,
showEventsAxis,
eventsAxisHeight
} = provideConfig();
const { toDayjs } = useDayjsHelper();
const handleMouseDown = (e) => {
emit("dragStart", e);
};
const dayUnitLevel = computed(() => {
if (internalPrecision.value === "hour") {
return "upper";
} else if (internalPrecision.value === "day") {
return "lower";
}
return null;
});
const getHolidayStyle = (unit, unitType) => {
if (!holidayHighlight.value || dayUnitLevel.value !== unitType || !unit.isHoliday) {
return {};
}
return {
background: colors.value.holidayHighlight || "#ffebee"
};
};
const handleUnitMouseEnter = (unit, unitType, event) => {
if (!holidayHighlight.value || dayUnitLevel.value === unitType || !unit.isHoliday) {
hoveredUnit.value = unit;
hoveredElement.value = event.currentTarget;
showTooltip.value = true;
}
};
const handleUnitMouseLeave = () => {
showTooltip.value = false;
hoveredUnit.value = void 0;
hoveredElement.value = null;
};
const handleEventMouseEnter = (event, mouseEvent) => {
hoveredEvent.value = event;
hoveredEventElement.value = mouseEvent.currentTarget;
showEventTooltip.value = true;
};
const handleEventMouseLeave = () => {
showEventTooltip.value = false;
hoveredEvent.value = void 0;
hoveredEventElement.value = null;
};
const formatTimeUnitLabel = (unit, unitType) => {
if (dayUnitLevel.value !== unitType || !dayOptionLabel.value) {
return unit.label;
}
let result = "";
for (const option of dayOptionLabel.value) {
if (result) result += " ";
switch (option) {
case "day":
result += unit.label;
break;
case "doy":
result += `(${toDayjs(unit.date).dayOfYear()})`;
break;
case "name":
result += capitalizeWords(toDayjs(unit.date).format("dd")[0]);
break;
case "number":
result += toDayjs(unit.date).date();
break;
}
}
return result;
};
const eventsAxisHeightValue = computed(() => {
return eventsAxisHeight.value || 25;
});
const shouldShowEventsAxis = computed(() => {
return showEventsAxis.value && timeaxisUnits.value.result.events.length > 0;
});
const eventPositions = computed(() => {
if (!timeaxisUnits.value.result.events.length) return [];
const { chartStartDayjs, chartEndDayjs } = useDayjsHelper();
const totalMinutes = chartEndDayjs.value.diff(chartStartDayjs.value, "minutes");
return timeaxisUnits.value.result.events.map((event) => {
const eventStartDayjs = dayjs(event.startDate);
const eventEndDayjs = dayjs(event.endDate);
const startTime = eventStartDayjs.isBefore(chartStartDayjs.value) ? chartStartDayjs.value : eventStartDayjs;
const endTime = eventEndDayjs.isAfter(chartEndDayjs.value) ? chartEndDayjs.value : eventEndDayjs;
const startMinutes = startTime.diff(chartStartDayjs.value, "minutes");
const endMinutes = endTime.diff(chartStartDayjs.value, "minutes");
const xPosition = startMinutes / totalMinutes * ganttWidth.value;
const width = Math.max((endMinutes - startMinutes) / totalMinutes * ganttWidth.value, 2);
return {
...event,
calculatedWidth: `${width}px`,
calculatedX: xPosition
};
});
});
__expose({ timeaxisElement });
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "timeaxisElement",
ref: timeaxisElement,
class: "g-timeaxis",
onMousedown: handleMouseDown,
role: "tablist",
"aria-label": "Time Axis",
style: normalizeStyle({
borderBottom: `1px solid ${unref(colors).gridAndBorder}`,
height: shouldShowEventsAxis.value ? `${80 + eventsAxisHeightValue.value}px` : "80px"
})
}, [
createElementVNode("div", _hoisted_1$7, [
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(timeaxisUnits).result.upperUnits, (unit, index) => {
return openBlock(), createElementBlock("div", {
key: unit.date.toISOString(),
class: "g-upper-timeunit",
style: normalizeStyle({
background: index % 2 === 0 ? unref(colors).primary : unref(colors).secondary,
...getHolidayStyle(unit, "upper"),
color: unref(colors).text,
width: unit.width
}),
onMouseenter: (e) => handleUnitMouseEnter(unit, "upper", e),
onMouseleave: handleUnitMouseLeave
}, [
renderSlot(_ctx.$slots, "upper-timeunit", {
label: formatTimeUnitLabel(unit, "upper"),
value: unit.value,
date: unit.date
}, () => [
createTextVNode(toDisplayString(formatTimeUnitLabel(unit, "upper")), 1)
])
], 44, _hoisted_2$5);
}), 128))
]),
createElementVNode("div", _hoisted_3$5, [
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(timeaxisUnits).result.lowerUnits, (unit, index) => {
return openBlock(), createElementBlock("div", {
key: unit.date.toISOString(),
class: "g-timeunit",
style: normalizeStyle({
background: index % 2 === 0 ? unref(colors).ternary : unref(colors).quartenary,
...getHolidayStyle(unit, "lower"),
color: unref(colors).text,
flexDirection: unref(precision) === "hour" ? unref(enableMinutes) ? "column" : "row-reverse" : "row",
alignItems: "center",
width: unit.width
}),
onMouseenter: (e) => handleUnitMouseEnter(unit, "lower", e),
onMouseleave: handleUnitMouseLeave
}, [
createElementVNode("div", _hoisted_5$4, [
renderSlot(_ctx.$slots, "timeunit", {
label: formatTimeUnitLabel(unit, "lower"),
value: unit.value,
date: unit.date
}, () => [
createElementVNode("div", _hoisted_6$4, toDisplayString(formatTimeUnitLabel(unit, "lower")), 1)
]),
unref(precision) === "hour" ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-timeaxis-hour-pin",
style: normalizeStyle({ background: unref(colors).text })
}, null, 4)) : createCommentVNode("", true)
]),
unref(precision) === "hour" && unref(enableMinutes) ? (openBlock(), createElementBlock("div", _hoisted_7$4, [
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(timeaxisUnits).globalMinuteStep, (step) => {
return openBlock(), createElementBlock("div", {
key: `${unit.label}-${step}`,
style: normalizeStyle({
background: index % 2 === 0 ? unref(colors).ternary : unref(colors).quartenary,
color: unref(colors).text,
display: "flex",
flexGrow: 1,
flexDirection: "row-reverse",
alignItems: "center"
})
}, [
createElementVNode("div", _hoisted_8$4, toDisplayString(step), 1),
createElementVNode("div", {
class: "g-timeaxis-hour-pin",
style: normalizeStyle({ background: unref(colors).text })
}, null, 4)
], 4);
}), 128))
])) : createCommentVNode("", true)
], 44, _hoisted_4$4);
}), 128))
]),
shouldShowEventsAxis.value ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-events-container",
style: normalizeStyle({
height: `${eventsAxisHeightValue.value}px`,
background: unref(colors).background
})
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(eventPositions.value, (event) => {
return openBlock(), createElementBlock("div", {
key: event.id,
class: "g-timeaxis-event",
style: normalizeStyle({
left: `${event.calculatedX}px`,
width: event.calculatedWidth,
backgroundColor: event.backgroundColor || unref(colors).primary,
color: event.color || unref(colors).text,
borderColor: event.color || unref(colors).primary
}),
onMouseenter: (e) => handleEventMouseEnter(event, e),
onMouseleave: handleEventMouseLeave
}, [
createElementVNode("div", _hoisted_10$3, [
renderSlot(_ctx.$slots, "timeaxis-event", { event }, () => [
createTextVNode(toDisplayString(event.label), 1)
])
])
], 44, _hoisted_9$4);
}), 128))
], 4)) : createCommentVNode("", true),
createVNode(_sfc_main$9, {
type: "holiday",
"model-value": showTooltip.value,
unit: hoveredUnit.value,
"target-element": hoveredElement.value
}, {
"holiday-tooltip": withCtx((slotProps) => [
renderSlot(_ctx.$slots, "holiday-tooltip", normalizeProps(guardReactiveProps(slotProps)))
]),
_: 3
}, 8, ["model-value", "unit", "target-element"]),
createVNode(_sfc_main$9, {
type: "event",
"model-value": showEventTooltip.value,
event: hoveredEvent.value,
"target-element": hoveredEventElement.value
}, {
"event-tooltip": withCtx((slotProps) => [
renderSlot(_ctx.$slots, "event-tooltip", normalizeProps(guardReactiveProps(slotProps)))
]),
_: 3
}, 8, ["model-value", "event", "target-element"])
], 36);
};
}
});
function useTimePositionMapping(config = provideConfig()) {
const { dateFormat } = config;
const { chartStartDayjs, chartEndDayjs, toDayjs, format } = useDayjsHelper(config);
const totalNumOfMinutes = computed(() => {
return chartEndDayjs.value.diff(chartStartDayjs.value, "minutes");
});
const mapTimeToPosition = (time) => {
const width = ganttWidth.value || 0;
const diffFromStart = toDayjs(time).diff(chartStartDayjs.value, "minutes", true);
const position = Math.ceil(diffFromStart / totalNumOfMinutes.value * width);
return position;
};
const mapPositionToTime = (xPos) => {
const width = ganttWidth.value || 0;
const diffFromStart = xPos / width * totalNumOfMinutes.value;
return format(chartStartDayjs.value.add(diffFromStart, "minutes"), dateFormat.value);
};
return {
mapTimeToPosition,
mapPositionToTime
};
}
const _sfc_main$7 = /* @__PURE__ */ defineComponent({
__name: "GGanttCurrentTime",
setup(__props) {
const { mapTimeToPosition } = useTimePositionMapping();
const currentMoment = ref(dayjs());
const { colors, dateFormat, currentTimeLabel, utc: utc2 } = provideConfig();
const xDist = ref();
const loopTime = () => {
const now = utc2.value ? dayjs().utc() : dayjs();
currentMoment.value = now;
const format = dateFormat.value || "YYYY-MM-DD HH:mm:ss";
xDist.value = mapTimeToPosition(dayjs(currentMoment.value, format).format(format));
};
useIntervalFn(loopTime, 1e3);
const currentTimeDisplay = computed(() => {
if (utc2.value) {
return `${currentTimeLabel.value} (UTC)`;
}
return currentTimeLabel.value;
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "g-grid-current-time",
style: normalizeStyle({
left: `${xDist.value}px`
})
}, [
createElementVNode("div", {
class: "g-grid-current-time-marker",
style: normalizeStyle({
border: `1px dashed ${unref(colors).markerCurrentTime}`
})
}, null, 4),
createElementVNode("span", {
class: "g-grid-current-time-text",
style: normalizeStyle({ color: unref(colors).markerCurrentTime })
}, [
renderSlot(_ctx.$slots, "current-time-label", {}, () => [
createTextVNode(toDisplayString(currentTimeDisplay.value), 1)
])
], 4)
], 4);
};
}
});
const _hoisted_1$6 = {
class: "gantt-connector",
style: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
zIndex: 1001,
overflow: "visible"
}
};
const _hoisted_2$4 = ["id"];
const _hoisted_3$4 = ["fill"];
const _hoisted_4$3 = ["id"];
const _hoisted_5$3 = ["stop-color"];
const _hoisted_6$3 = ["stop-color"];
const _hoisted_7$3 = ["stop-color"];
const _hoisted_8$3 = ["stop-color"];
const _hoisted_9$3 = ["dur"];
const _hoisted_10$2 = ["dur"];
const _hoisted_11$2 = ["d", "stroke", "stroke-width", "stroke-dasharray"];
const _hoisted_12$2 = ["x", "y"];
const _hoisted_13$2 = ["cx", "cy"];
const _hoisted_14$2 = ["cx", "cy"];
const STANDARD_OFFSET = 20;
const _sfc_main$6 = /* @__PURE__ */ defineComponent({
__name: "GGanttConnector",
props: {
sourceBar: {},
targetBar: {},
type: { default: "straight" },
color: { default: "#ff0000" },
strokeWidth: { default: 2 },
pattern: { default: "solid" },
animated: { type: Boolean, default: false },
animationSpeed: { default: "normal" },
marker: {},
isSelected: { type: Boolean, default: false },
relation: { default: "FS" },
label: { default: "" },
labelAlwaysVisible: { type: Boolean, default: false },
labelStyle: { default: () => ({}) }
},
setup(__props) {
const props = __props;
const { enableConnectionDeletion } = provideConfig();
const pathRef = ref(null);
const animationClass = computed(() => {
if (!props.animated) return "";
return `connector-animated-${props.pattern}-${props.animationSpeed}`;
});
const markerId = computed(() => `marker-${props.sourceBar.id}-${props.targetBar.id}`);
const hasMarkerEnd = computed(() => props.marker === "bidirectional" || props.marker === "forward");
const hasMarkerStart = computed(() => props.marker === "bidirectional");
const markerDelta = computed(() => 4);
const shouldShowLabel = computed(() => {
return props.label && (props.labelAlwaysVisible || props.isSelected);
});
const labelComputedStyle = computed(() => {
const defaultStyle = {
fill: props.color,
fontWeight: "bold"
};
return {
...defaultStyle,
...props.labelStyle
};
});
const connectionPoints = computed(() => {
const { sourceBar, targetBar, relation } = props;
switch (relation) {
case "FS":
return {
sourceX: sourceBar.x + sourceBar.width,
sourceY: sourceBar.y + sourceBar.height / 2,
targetX: targetBar.x,
targetY: targetBar.y + targetBar.height / 2
};
case "SS":
return {
sourceX: sourceBar.x,
sourceY: sourceBar.y + sourceBar.height / 2,
targetX: targetBar.x,
targetY: targetBar.y + targetBar.height / 2
};
case "FF":
return {
sourceX: sourceBar.x + sourceBar.width,
sourceY: sourceBar.y + sourceBar.height / 2,
targetX: targetBar.x + targetBar.width,
targetY: targetBar.y + targetBar.height / 2
};
case "SF":
return {
sourceX: sourceBar.x,
sourceY: sourceBar.y + sourceBar.height / 2,
targetX: targetBar.x + targetBar.width,
targetY: targetBar.y + targetBar.height / 2
};
default:
return {
sourceX: sourceBar.x + sourceBar.width,
sourceY: sourceBar.y + sourceBar.height / 2,
targetX: targetBar.x,
targetY: targetBar.y + targetBar.height / 2
};
}
});
const pathData = computed(() => {
const { sourceX, sourceY, targetX, targetY } = connectionPoints.value;
const { relation, type } = props;
const verticalDiff = targetY - sourceY;
const extraOffset = relation === "FF" || relation === "SF" ? 20 : 0;
const horizontalSpace = targetX - sourceX;
const isOverlapping = Math.abs(horizontalSpace) < STANDARD_OFFSET * (relation === "FS" ? 1 : 2);
const offset = isOverlapping ? Math.max(STANDARD_OFFSET, Math.abs(horizontalSpace) / 2) : STANDARD_OFFSET;
const verticalOffset = verticalDiff === 0 && isOverlapping ? 20 : 0;
const startAdjust = hasMarkerStart.value ? markerDelta.value : 0;
const endAdjust = hasMarkerEnd.value ? markerDelta.value : 0;
switch (type) {
case "straight":
if (isOverlapping) {
const midY = (sourceY + targetY) / 2 + verticalOffset * (sourceY > targetY ? -1 : 1);
return `M ${sourceX + startAdjust},${sourceY}
Q ${sourceX + offset},${midY} ${(sourceX + targetX) / 2},${midY}
Q ${targetX - offset},${midY} ${targetX - endAdjust},${targetY}`;
} else {
return `M ${sourceX + startAdjust},${sourceY} L ${targetX - endAdjust},${targetY}`;
}
case "squared":
if (relation === "FS") {
return `M ${sourceX + startAdjust},${sourceY}
h ${offset / 2}
v ${verticalDiff}
h ${horizontalSpace - offset / 2 - endAdjust}`;
} else if (relation === "SS") {
return `M ${sourceX + startAdjust},${sourceY}
h ${-offset / 2}
v ${verticalDiff}
h ${horizontalSpace + offset / 2 - endAdjust}`;
} else if (relation === "FF") {
return `M ${sourceX + startAdjust},${sourceY}
h ${offset / 2}
v ${verticalDiff / 2}
h ${horizontalSpace + offset / 2}
v ${verticalDiff / 2}
h ${-offset / 2 - endAdjust}`;
} else if (relation === "SF") {
return `M ${sourceX + startAdjust},${sourceY}
h ${-offset / 2}
v ${verticalDiff / 2}
h ${horizontalSpace + offset * 1.5}
v ${verticalDiff / 2}
h ${-offset / 2 - endAdjust}`;
}
return `M ${sourceX + startAdjust},${sourceY}
h ${offset}
v ${verticalDiff}
h ${horizontalSpace - offset - endAdjust}`;
case "bezier":
default:
let controlOffset = Math.max(Math.abs(horizontalSpace) / 3, offset * 1.5);
if (isOverlapping) controlOffset = Math.max(controlOffset, offset * 2);
if (relation === "FF" || relation === "SF") {
const midX = targetX + extraOffset;
return `M ${sourceX + (hasMarkerStart.value ? startAdjust : 0)},${sourceY}
C ${midX},${sourceY}
${midX},${targetY}
${targetX + (hasMarkerEnd.value ? endAdjust : 0)},${targetY}`;
} else {
let cp1x, cp2x;
switch (relation) {
case "FS":
cp1x = sourceX + controlOffset;
cp2x = targetX - controlOffset;
break;
case "SS":
cp1x = sourceX - controlOffset;
cp2x = targetX - controlOffset;
break;
default:
cp1x = sourceX + controlOffset;
cp2x = targetX - controlOffset;
}
return `M ${sourceX + (hasMarkerStart.value ? startAdjust : 0)},${sourceY}
C ${cp1x},${sourceY}
${cp2x},${targetY}
${targetX - (hasMarkerEnd.value ? endAdjust : 0)},${targetY}`;
}
}
});
const nonAnimatedDashArray = computed(() => {
if (props.animated) return void 0;
switch (props.pattern) {
case "dash":
return "8,8";
case "dot":
return "2,6";
case "dashdot":
return "12,6,3,6";
default:
return "";
}
});
const getStrokeWidth = computed(() => {
if (props.isSelected && enableConnectionDeletion.value) {
return props.strokeWidth * 1.5;
}
return props.strokeWidth;
});
const endpointPositions = computed(() => {
const { sourceX, sourceY, targetX, targetY } = connectionPoints.value;
return {
source: { x: sourceX, y: sourceY },
target: { x: targetX, y: targetY }
};
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("svg", _hoisted_1$6, [
createElementVNode("defs", null, [
createElementVNode("marker", {
id: markerId.value,
viewBox: "0 0 10 10",
refX: "5",
refY: "5",
markerWidth: "6",
markerHeight: "6",
orient: "auto-start-reverse"
}, [
createElementVNode("path", {
d: "M 0 0 L 10 5 L 0 10 z",
fill: _ctx.color
}, null, 8, _hoisted_3$4)
], 8, _hoisted_2$4),
_ctx.animated && _ctx.pattern === "solid" ? (openBlock(), createElementBlock("linearGradient", {
key: 0,
id: `gradient-${_ctx.sourceBar.id}-${_ctx.targetBar.id}`,
gradientUnits: "userSpaceOnUse",
x1: "0%",
y1: "0%",
x2: "100%",
y2: "0%"
}, [
createElementVNode("stop", {
offset: "0%",
"stop-color": _ctx.color,
"stop-opacity": "0.3"
}, null, 8, _hoisted_5$3),
createElementVNode("stop", {
offset: "45%",
"stop-color": _ctx.color,
"stop-opacity": "1"
}, null, 8, _hoisted_6$3),
createElementVNode("stop", {
offset: "55%",
"stop-color": _ctx.color,
"stop-opacity": "1"
}, null, 8, _hoisted_7$3),
createElementVNode("stop", {
offset: "100%",
"stop-color": _ctx.color,
"stop-opacity": "0.3"
}, null, 8, _hoisted_8$3),
createElementVNode("animate", {
attributeName: "x1",
from: "-100%",
to: "100%",
dur: _ctx.animationSpeed === "slow" ? "4s" : _ctx.animationSpeed === "fast" ? "1s" : "2s",
repeatCount: "indefinite"
}, null, 8, _hoisted_9$3),
createElementVNode("animate", {
attributeName: "x2",
from: "0%",
to: "200%",
dur: _ctx.animationSpeed === "slow" ? "4s" : _ctx.animationSpeed === "fast" ? "1s" : "2s",
repeatCount: "indefinite"
}, null, 8, _hoisted_10$2)
], 8, _hoisted_4$3)) : createCommentVNode("", true)
]),
createElementVNode("path", {
ref_key: "pathRef",
ref: pathRef,
d: pathData.value,
fill: "none",
stroke: _ctx.animated && _ctx.pattern === "solid" ? `url(#gradient-${_ctx.sourceBar.id}-${_ctx.targetBar.id})` : _ctx.color,
"stroke-width": getStrokeWidth.value,
"stroke-dasharray": nonAnimatedDashArray.value,
class: normalizeClass([
"connector-path",
animationClass.value,
{ selected: _ctx.isSelected && unref(enableConnectionDeletion) },
`connector-relation-${_ctx.relation || "FS"}`
]),
style: normalizeStyle({
markerStart: hasMarkerStart.value ? `url(#${markerId.value})` : "none",
markerEnd: hasMarkerEnd.value ? `url(#${markerId.value})` : "none",
cursor: unref(enableConnectionDeletion) ? "pointer" : "inherit",
pointerEvents: unref(enableConnectionDeletion) ? "all" : "none"
})
}, null, 14, _hoisted_11$2),
shouldShowLabel.value ? (openBlock(), createElementBlock("text", {
key: 0,
x: (endpointPositions.value.source.x + endpointPositions.value.target.x) / 2 - 50,
y: (endpointPositions.value.source.y + endpointPositions.value.target.y) / 2 - 20,
class: "connection-label-container",
"text-anchor": "middle",
style: normalizeStyle({
pointerEvents: "none",
...labelComputedStyle.value
})
}, toDisplayString(_ctx.label), 13, _hoisted_12$2)) : createCommentVNode("", true),
_ctx.isSelected && unref(enableConnectionDeletion) ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
createElementVNode("circle", {
cx: endpointPositions.value.source.x,
cy: endpointPositions.value.source.y,
r: "6",
fill: "white",
class: "connection-endpoint"
}, null, 8, _hoisted_13$2),
createElementVNode("circle", {
cx: endpointPositions.value.target.x,
cy: endpointPositions.value.target.y,
r: "6",
fill: "white",
class: "connection-endpoint"
}, null, 8, _hoisted_14$2)
], 64)) : createCommentVNode("", true)
]);
};
}
});
const _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const GGanttConnector = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["__scopeId", "data-v-652d5ef8"]]);
const _hoisted_1$5 = { class: "g-gantt-milestone-tooltip-title" };
const _hoisted_2$3 = { class: "g-gantt-milestone-tooltip-date" };
const _hoisted_3$3 = { class: "g-gantt-milestone-tooltip-description" };
const _sfc_main$5 = /* @__PURE__ */ defineComponent({
__name: "GGanttMilestone",
props: {
milestone: {}
},
setup(__props) {
const props = __props;
const { mapTimeToPosition } = useTimePositionMapping();
const { colors } = provideConfig();
const showTooltip = ref(false);
const tooltipPosition = ref({ x: 0, y: 0 });
const milestoneDate = computed(() => {
const date = dayjs(props.milestone.date);
if (!date.hour() && !date.minute()) {
return date.hour(12).minute(0).format("YYYY-MM-DD HH:mm");
}
return props.milestone.date;
});
const xPosition = computed(() => {
return mapTimeToPosition(milestoneDate.value);
});
const styleConfig = computed(() => {
if (props.milestone.color) {
return {
label: {
background: props.milestone.color,
color: "#000",
border: `2px solid ${props.milestone.color}`
},
marker: {
borderLeft: `2px solid ${props.milestone.color}`
}
};
}
return {
label: {
background: colors.value.primary,
color: colors.value.text,
border: "none"
},
marker: {
borderLeft: `2px solid ${colors.value.markerCurrentTime}`
}
};
});
const handleMouseEnter = (event) => {
const element = event.target;
const rect = element.getBoundingClientRect();
tooltipPosition.value = {
x: rect.left,
y: rect.top + 10
};
showTooltip.value = true;
};
const handleMouseLeave = () => {
showTooltip.value = false;
};
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "g-gantt-milestone",
style: normalizeStyle({
left: `${xPosition.value}px`
}),
onMouseenter: handleMouseEnter,
onMouseleave: handleMouseLeave
}, [
renderSlot(_ctx.$slots, `milestone-${_ctx.milestone.id}`, {
milestone: _ctx.milestone,
styleConfig: styleConfig.value,
position: xPosition.value
}, () => [
renderSlot(_ctx.$slots, "milestone", {
milestone: _ctx.milestone,
styleConfig: styleConfig.value,
position: xPosition.value
}, () => [
createElementVNode("div", {
class: "g-gantt-milestone-label",
style: normalizeStyle(styleConfig.value.label)
}, toDisplayString(_ctx.milestone.name), 5)
])
]),
createElementVNode("div", {
class: "g-gantt-milestone-marker",
style: normalizeStyle(styleConfig.value.marker)
}, null, 4),
(openBlock(), createBlock(Teleport, { to: "body" }, [
createVNode(Transition, {
name: "g-fade",
mode: "out-in"
}, {
default: withCtx(() => [
showTooltip.value ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-gantt-milestone-tooltip",
style: normalizeStyle({
top: `${tooltipPosition.value.y}px`,
left: `${tooltipPosition.value.x}px`,
background: unref(colors).primary,
color: unref(colors).text
})
}, [
createElementVNode("div", _hoisted_1$5, toDisplayString(_ctx.milestone.name), 1),
createElementVNode("div", _hoisted_2$3, toDisplayString(_ctx.milestone.date), 1),
createElementVNode("div", _hoisted_3$3, toDisplayString(_ctx.milestone.description), 1)
], 4)) : createCommentVNode("", true)
]),
_: 1
})
]))
], 36);
};
}
});
function provideEmitBarEvent() {
const emitBarEvent = inject(EMIT_BAR_EVENT_KEY);
if (!emitBarEvent) {
throw Error("Failed to inject emitBarEvent!");
}
return emitBarEvent;
}
function useBarSelector() {
const findBarElement = (ganttId, barId) => {
const ganttContainer = document.getElementById(ganttId);
if (!ganttContainer) return null;
return ganttContainer.querySelector(`#${CSS.escape(barId)}`);
};
const findAllBarElements = (ganttId) => {
const ganttContainer = document.getElementById(ganttId);
if (!ganttContainer) return document.querySelectorAll(".non-existent");
return ganttContainer.querySelectorAll(".g-gantt-bar");
};
const barExistsInGantt = (ganttId, barId) => {
return !!findBarElement(ganttId, barId);
};
return {
findBarElement,
findAllBarElements,
barExistsInGantt
};
}
function createBarDrag(bar, onDrag = () => null, onEndDrag = () => null, config, movementAPI, ganttId) {
const { findBarElement } = useBarSelector();
const { barStart, barEnd } = config;
const isDragging = ref(false);
let cursorOffsetX = 0;
let initialBarLeft = 0;
let dragCallBack;
const { mapPositionToTime } = useTimePositionMapping(config);
const { toDayjs } = useDayjsHelper(config);
const initDrag = (e) => {
if (bar.ganttBarConfig.immobile) {
return;
}
const barElement = findBarElement(ganttId, bar.ganttBarConfig.id);
if (!barElement) {
return;
}
const rect = barElement.getBoundingClientRect();
initialBarLeft = rect.left;
cursorOffsetX = e.clientX - initialBarLeft;
const mousedownType = e.target.className;
switch (mousedownType) {
case "g-gantt-bar-handle-left":
document.body.style.cursor = "ew-resize";
dragCallBack = dragByLeftHandle;
break;
case "g-gantt-bar-handle-right":
document.body.style.cursor = "ew-resize";
dragCallBack = dragByRightHandle;
break;
default:
dragCallBack = drag;
}
isDragging.value = true;
window.addEventListener("mousemove", dragCallBack);
window.addEventListener("mouseup", endDrag);
};
const getBarElements = () => {
const barElement = findBarElement(ganttId, bar.ganttBarConfig.id);
let currentElement = barElement;
let barContainer = null;
while (currentElement && !barContainer) {
const container = currentElement.closest(".g-gantt-row-bars-container");
if (container) {
barContainer = container.getBoundingClientRect();
}
currentElement = currentElement.parentElement;
}
return { barElement, barContainer };
};
const drag = (e) => {
const { barElement, barContainer } = getBarElements();
if (!barElement || !barContainer) {
console.warn("Missing elements:", { barElement, barContainer });
return;
}
const barWidth = barElement.getBoundingClientRect().width;
const relativeX = e.clientX - cursorOffsetX - barContainer.left;
const xStart = Math.max(0, relativeX);
const xEnd = xStart + barWidth;
const newBarStart = mapPositionToTime(xStart);
const newBarEnd = mapPositionToTime(xEnd);
const currentStart = bar[barStart.value];
const currentEnd = bar[barEnd.value];
const result = movementAPI.moveBar(bar, newBarStart, newBarEnd);
if (!result.success) {
bar[barStart.value] = currentStart;
bar[barEnd.value] = currentEnd;
endDrag(e);
return;
}
onDrag(e, bar);
};
const dragByLeftHandle = (e) => {
const { barContainer } = getBarElements();
if (!barContainer) {
return;
}
const xStart = e.clientX - barContainer.left;
const newBarStart = mapPositionToTime(xStart);
if (toDayjs(newBarStart).isSameOrAfter(toDayjs(bar[barEnd.value]))) {
return;
}
const result = movementAPI.moveBar(bar, newBarStart, bar[barEnd.value]);
if (result.success) {
onDrag(e, bar);
}
};
const dragByRightHandle = (e) => {
const { barContainer } = getBarElements();
if (!barContainer) {
return;
}
const xEnd = e.clientX - barContainer.left;
const newBarEnd = mapPositionToTime(xEnd);
if (toDayjs(newBarEnd).isSameOrBefore(toDayjs(bar[barStart.value]))) {
return;
}
const result = movementAPI.moveBar(bar, bar[barStart.value], newBarEnd);
if (result.success) {
onDrag(e, bar);
}
};
const endDrag = (e) => {
isDragging.value = false;
document.body.style.cursor = "";
window.removeEventListener("mousemove", dragCallBack);
window.removeEventListener("mouseup", endDrag);
onEndDrag(e, bar);
};
return {
isDragging,
initDrag
};
}
function useBarMovement(config, rowManager, dayjsHelper) {
const { barStart, barEnd, dateFormat, pushOnOverlap, pushOnConnect } = config;
const processedBars = /* @__PURE__ */ new Set();
const formatDate = (date) => {
const result = dayjsHelper.format(date, dateFormat.value);
return typeof result === "string" ? result : result.toISOString();
};
const checkMilestoneConstraint = (bar, newEnd) => {
if (!bar.ganttBarConfig.milestoneId || !config.milestones.value) return true;
const milestone = config.milestones.value.find((m) => m.id === bar.ganttBarConfig.milestoneId);
if (!milestone) return true;
const endDate = dayjsHelper.toDayjs(newEnd);
const date = dayjs(milestone.date);
let milestoneDate = date;
if (!date.hour() && !date.minute()) {
milestoneDate = dayjsHelper.toDayjs(date.hour(23).minute(59).format("YYYY-MM-DD HH:mm"));
}
return endDate.isSameOrBefore(milestoneDate);
};
const getAllBars = () => {
const extractBarsFromRow = (row) => {
var _a;
let bars = [...row.bars];
if ((_a = row.children) == null ? void 0 : _a.length) {
row.children.forEach((child) => {
bars = [...bars, ...extractBarsFromRow(child)];
});
}
return bars;
};
return rowManager.rows.value.flatMap((row) => extractBarsFromRow(row));
};
const moveBar = (bar, newStart, newEnd, initialMove = true) => {
if (processedBars.has(bar.ganttBarConfig.id)) {
return { success: true, affectedBars: /* @__PURE__ */ new Set() };
}
if (!checkMilestoneConstraint(bar, newEnd)) {
return { success: false, affectedBars: /* @__PURE__ */ new Set() };
}
processedBars.add(bar.ganttBarConfig.id);
const affectedBars = /* @__PURE__ */ new Set();
const originalStart = bar[barStart.value];
const originalEnd = bar[barEnd.value];
bar[barStart.value] = newStart;
bar[barEnd.value] = newEnd;
if (bar.ganttBarConfig.bundle && initialMove) {
const bundleBars = getAllBars().filter(
(b) => b.ganttBarConfig.bundle === bar.ganttBarConfig.bundle && b !== bar
);
const timeDiff = dayjsHelper.toDayjs(newStart).diff(dayjsHelper.toDayjs(originalStart), "minutes");
for (const bundleBar of bundleBars) {
const bundleBarNewStart = formatDate(
dayjsHelper.toDayjs(bundleBar[barStart.value]).add(timeDiff, "minutes")
);
const bundleBarNewEnd = formatDate(
dayjsHelper.toDayjs(bundleBar[barEnd.value]).add(timeDiff, "minutes")
);
const bundleResult = moveBar(bundleBar, bundleBarNewStart, bundleBarNewEnd, false);
if (!bundleResult.success) {
bar[barStart.value] = originalStart;
bar[barEnd.value] = originalEnd;
processedBars.delete(bar.ganttBarConfig.id);
return { success: false, affectedBars: /* @__PURE__ */ new Set() };
}
bundleResult.affectedBars.forEach((b) => affectedBars.add(b));
}
}
const result = handleBarInteractions(bar, affectedBars);
if (!result.success) {
bar[barStart.value] = originalStart;
bar[barEnd.value] = originalEnd;
processedBars.delete(bar.ganttBarConfig.id);
return { success: false, affectedBars: /* @__PURE__ */ new Set() };
}
if (initialMove) {
processedBars.clear();
}
affectedBars.add(bar);
return { success: true, affectedBars };
};
const handleBarInteractions = (bar, affectedBars) => {
const overlappingBars = pushOnOverlap.value ? findOverlappingBars(bar) : [];
const connectedBars = pushOnConnect.value ? findConnectedBars(bar) : [];
const impactedBars = [.../* @__PURE__ */ new Set([...overlappingBars, ...connectedBars])];
for (const impactedBar of impactedBars) {
if (impactedBar.ganttBarConfig.immobile) {
return { success: false };
}
const { shouldMove, minutesToMove, direction } = calculateMovement(bar, impactedBar);
if (!shouldMove) continue;
const newStart = formatDate(
direction === "left" ? dayjsHelper.toDayjs(impactedBar[barStart.value]).subtract(minutesToMove, "minutes") : dayjsHelper.toDayjs(impactedBar[barStart.value]).add(minutesToMove, "minutes")
);
const newEnd = formatDate(
direction === "left" ? dayjsHelper.toDayjs(impactedBar[barEnd.value]).subtract(minutesToMove, "minutes") : dayjsHelper.toDayjs(impactedBar[barEnd.value]).add(minutesToMove, "minutes")
);
if (!checkMilestoneConstraint(impactedBar, newEnd)) {
return { success: false };
}
const result = moveBar(impactedBar, newStart, newEnd, false);
if (!result.success) {
return { success: false };
}
affectedBars.add(impactedBar);
result.affectedBars.forEach((b) => affectedBars.add(b));
}
return { success: true };
};
const calculateMovement = (sourceBar, targetBar) => {
const sourceStart = dayjsHelper.toDayjs(sourceBar[barStart.value]);
const sourceEnd = dayjsHelper.toDayjs(sourceBar[barEnd.value]);
const targetStart = dayjsHelper.toDayjs(targetBar[barStart.value]);
const targetEnd = dayjsHelper.toDayjs(targetBar[barEnd.value]);
if (targetBar.ganttBarConfig.immobile && (sourceStart.isBefore(targetEnd) && sourceEnd.isAfter(targetStart) || sourceEnd.isAfter(targetStart) && sourceStart.isBefore(targetEnd))) {
return { shouldMove: false, minutesToMove: 0, direction: "right" };
}
if (sourceEnd.isSameOrBefore(targetStart) || sourceStart.isSameOrAfter(targetEnd)) {
return { shouldMove: false, minutesToMove: 0, direction: "right" };
}
const direction = sourceStart.isBefore(targetStart) ? "right" : "left";
let minutesToMove = 0;
if (direction === "right") {
minutesToMove = sourceEnd.diff(targetStart, "minutes", true);
} else {
minutesToMove = targetEnd.diff(sourceStart, "minutes", true);
}
return {
shouldMove: true,
minutesToMove: Math.abs(minutesToMove),
direction
};
};
const findOverlappingBars = (bar) => {
const findRowForBar = (searchBar, rows) => {
var _a;
for (const row of rows) {
if (row.bars.includes(searchBar)) return row;
if ((_a = row.children) == null ? void 0 : _a.length) {
const foundInChildren = findRowForBar(searchBar, row.children);
if (foundInChildren) return foundInChildren;
}
}
return null;
};
const barRow = findRowForBar(bar, rowManager.rows.value);
if (!barRow) return [];
return barRow.bars.filter((otherBar) => {
if (otherBar === bar || otherBar.ganttBarConfig.pushOnOverlap === false) return false;
if (otherBar.ganttBarConfig.id.startsWith("group-")) return false;
const start1 = dayjsHelper.toDayjs(bar[barStart.value]);
const end1 = dayjsHelper.toDayjs(bar[barEnd.value]);
const start2 = dayjsHelper.toDayjs(otherBar[barStart.value]);
const end2 = dayjsHelper.toDayjs(otherBar[barEnd.value]);
return start1.isBefore(end2) && end1.isAfter(start2) || start2.isBefore(end1) && end2.isAfter(start1);
});
};
const findConnectedBars = (bar) => {
var _a;
const allBars = getAllBars();
const connectedBars = [];
(_a = bar.ganttBarConfig.connections) == null ? void 0 : _a.forEach((conn) => {
const targetBar = allBars.find((b) => b.ganttBarConfig.id === conn.targetId);
if (targetBar && targetBar.ganttBarConfig.pushOnConnect !== false) {
connectedBars.push(targetBar);
}
});
allBars.forEach((otherBar) => {
var _a2;
(_a2 = otherBar.ganttBarConfig.connections) == null ? void 0 : _a2.forEach((conn) => {
if (conn.targetId === bar.ganttBarConfig.id && otherBar.ganttBarConfig.pushOnConnect !== false) {
connectedBars.push(otherBar);
}
});
});
return connectedBars;
};
return {
moveBar,
findOverlappingBars,
findConnectedBars,
getAllBars
};
}
const useBarDragManagement = () => {
const config = provideConfig();
const emitBarEvent = provideEmitBarEvent();
const dayjs2 = useDayjsHelper(config);
const { barStart, barEnd } = config;
const rowManager = inject("useRows");
const ganttId = inject(GANTT_ID_KEY);
const movement = useBarMovement(config, rowManager, dayjs2);
const dragState = {
movedBars: /* @__PURE__ */ new Map(),
isDragging: false
};
const getBundleBars = (bundle) => {
const res = [];
if (bundle != null) {
const allBars = movement.getAllBars();
allBars.forEach((bar) => {
if (bar.ganttBarConfig.bundle === bundle) {
res.push(bar);
}
});
}
return res;
};
const initDragOfBar = (bar, e) => {
if (bar.ganttBarConfig.bundle) {
initDragOfBundle(bar, e);
return;
}
const dragHandler = createDragHandler(bar);
dragHandler.initiateDrag(e);
addBarToMovedBars(bar);
emitBarEvent({ ...e, type: "dragstart" }, bar);
};
const initDragOfBundle = (mainBar, e) => {
const bundle = mainBar.ganttBarConfig.bundle;
if (!bundle) return;
const bundleBars = getBundleBars(bundle);
bundleBars.forEach((bar) => {
const isMainBar = bar === mainBar;
const dragHandler = createDragHandler(bar, isMainBar);
dragHandler.initiateDrag(e);
addBarToMovedBars(bar);
});
emitBarEvent({ ...e, type: "dragstart" }, mainBar);
};
const createDragHandler = (bar, isMainBar = true) => ({
initiateDrag: (e) => {
const { initDrag } = createBarDrag(
bar,
(e2) => handleDrag(e2, bar),
isMainBar ? handleDragEnd : () => null,
config,
movement,
ganttId
);
initDrag(e);
}
});
const handleDrag = (e, bar) => {
emitBarEvent({ ...e, type: "drag" }, bar);
const result = movement.moveBar(bar, bar[barStart.value], bar[barEnd.value]);
if (!result.success) {
snapBackMovedBars();
} else {
result.affectedBars.forEach((affectedBar) => {
if (!dragState.movedBars.has(affectedBar)) {
addBarToMovedBars(affectedBar);
}
});
}
};
const handleDragEnd = (e, bar) => {
emitBarEvent({ ...e, type: "dragend" }, bar, void 0, new Map(dragState.movedBars));
dragState.movedBars.clear();
dragState.isDragging = false;
};
const addBarToMovedBars = (bar) => {
if (!dragState.movedBars.has(bar)) {
dragState.movedBars.set(bar, {
oldStart: bar[barStart.value],
oldEnd: bar[barEnd.value]
});
}
};
const snapBackMovedBars = () => {
dragState.movedBars.forEach(({ oldStart, oldEnd }, bar) => {
bar[barStart.value] = oldStart;
bar[barEnd.value] = oldEnd;
});
};
return {
initDragOfBar,
initDragOfBundle,
snapBackMovedBars,
handleDrag,
getConnectedBars: movement.findConnectedBars
};
};
function useBarDragLimit() {
const { pushOnOverlap, pushOnConnect } = provideConfig();
const { getConnectedBars } = useBarDragManagement();
const rowManager = inject("useRows");
const ganttId = inject(GANTT_ID_KEY);
const { findBarElement } = useBarSelector();
const getBarsFromBundle = (bundle) => {
const res = [];
if (bundle != null) {
rowManager.rows.value.forEach((row) => {
row.bars.forEach((bar) => {
if (bar.ganttBarConfig.bundle === bundle) {
res.push(bar);
}
});
});
}
return res;
};
const setDragLimitsOfGanttBar = (bar) => {
if (!pushOnOverlap.value || bar.ganttBarConfig.pushOnOverlap === false || !pushOnConnect.value || bar.ganttBarConfig.pushOnConnect === false) {
return;
}
for (const sideValue of ["left", "right"]) {
const side = sideValue;
const { gapDistanceSoFar, bundleBarsAndGapDist } = countGapDistanceToNextImmobileBar(
bar,
0,
side
);
let totalGapDistance = gapDistanceSoFar;
const bundleBarsOnPath = bundleBarsAndGapDist;
if (!bundleBarsOnPath) {
continue;
}
for (let i = 0; i < bundleBarsOnPath.length; i++) {
const barFromBundle = bundleBarsOnPath[i].bar;
const gapDist = bundleBarsOnPath[i].gapDistance;
const otherBarsFromBundle = getBarsFromBundle(barFromBundle.ganttBarConfig.bundle).filter(
(otherBar) => otherBar !== barFromBundle
);
otherBarsFromBundle.forEach((otherBar) => {
const nextGapDistanceAndBars = countGapDistanceToNextImmobileBar(otherBar, gapDist, side);
const newGapDistance = nextGapDistanceAndBars.gapDistanceSoFar;
const newBundleBars = nextGapDistanceAndBars.bundleBarsAndGapDist;
if (newGapDistance != null && (!totalGapDistance || newGapDistance < totalGapDistance)) {
totalGapDistance = newGapDistance;
}
newBundleBars.forEach((newBundleBar) => {
if (!bundleBarsOnPath.find((barAndGap) => barAndGap.bar === newBundleBar.bar)) {
bundleBarsOnPath.push(newBundleBar);
}
});
});
}
const barElem = findBarElement(ganttId, bar.ganttBarConfig.id);
if (totalGapDistance != null && side === "left") {
bar.ganttBarConfig.dragLimitLeft = barElem.offsetLeft - totalGapDistance;
} else if (totalGapDistance != null && side === "right") {
bar.ganttBarConfig.dragLimitRight = barElem.offsetLeft + barElem.offsetWidth + totalGapDistance;
}
}
const barsFromBundleOfClickedBar = getBarsFromBundle(bar.ganttBarConfig.bundle);
barsFromBundleOfClickedBar.forEach((barFromBundle) => {
barFromBundle.ganttBarConfig.dragLimitLeft = bar.ganttBarConfig.dragLimitLeft;
barFromBundle.ganttBarConfig.dragLimitRight = bar.ganttBarConfig.dragLimitRight;
});
};
const countGapDistanceToNextImmobileBar = (bar, gapDistanceSoFar = 0, side) => {
const bundleBarsAndGapDist = bar.ganttBarConfig.bundle ? [{ bar, gapDistance: gapDistanceSoFar }] : [];
let currentBar = bar;
let nextBar = getNextGanttBar(currentBar, side);
if (side === "left") {
while (nextBar) {
const currentBarElem = findBarElement(ganttId, currentBar.ganttBarConfig.id);
const nextBarElem = findBarElement(ganttId, nextBar.ganttBarConfig.id);
const nextBarOffsetRight = nextBarElem.offsetLeft + nextBarElem.offsetWidth;
gapDistanceSoFar += currentBarElem.offsetLeft - nextBarOffsetRight;
if (nextBar.ganttBarConfig.immobile) {
return { gapDistanceSoFar, bundleBarsAndGapDist };
} else if (nextBar.ganttBarConfig.bundle) {
bundleBarsAndGapDist.push({
bar: nextBar,
gapDistance: gapDistanceSoFar
});
}
currentBar = nextBar;
nextBar = getNextGanttBar(nextBar, "left");
}
}
if (side === "right") {
while (nextBar) {
const currentBarElem = findBarElement(ganttId, currentBar.ganttBarConfig.id);
const nextBarElem = findBarElement(ganttId, nextBar.ganttBarConfig.id);
const currentBarOffsetRight = currentBarElem.offsetLeft + currentBarElem.offsetWidth;
gapDistanceSoFar += nextBarElem.offsetLeft - currentBarOffsetRight;
if (nextBar.ganttBarConfig.immobile) {
return { gapDistanceSoFar, bundleBarsAndGapDist };
} else if (nextBar.ganttBarConfig.bundle) {
bundleBarsAndGapDist.push({
bar: nextBar,
gapDistance: gapDistanceSoFar
});
}
currentBar = nextBar;
nextBar = getNextGanttBar(nextBar, "right");
}
}
return { gapDistanceSoFar: null, bundleBarsAndGapDist };
};
const getNextGanttBar = (bar, side) => {
var _a;
const barElem = findBarElement(ganttId, bar.ganttBarConfig.id);
let allBarsInRow = [];
if (pushOnOverlap.value) {
allBarsInRow = ((_a = rowManager.rows.value.find((row) => row.bars.includes(bar))) == null ? void 0 : _a.bars) || [];
}
if (pushOnConnect.value) {
allBarsInRow = [...allBarsInRow, ...getConnectedBars(bar)];
}
let allBarsLeftOrRight = [];
if (side === "left") {
allBarsLeftOrRight = allBarsInRow.filter((otherBar) => {
const otherBarElem = findBarElement(ganttId, otherBar.ganttBarConfig.id);
return otherBarElem && otherBarElem.offsetLeft < barElem.offsetLeft && otherBar.ganttBarConfig.pushOnOverlap !== false && otherBar.ganttBarConfig.pushOnConnect !== false;
});
} else {
allBarsLeftOrRight = allBarsInRow.filter((otherBar) => {
const otherBarElem = findBarElement(ganttId, otherBar.ganttBarConfig.id);
return otherBarElem && otherBarElem.offsetLeft > barElem.offsetLeft && otherBar.ganttBarConfig.pushOnOverlap !== false && otherBar.ganttBarConfig.pushOnConnect !== false;
});
}
if (allBarsLeftOrRight.length > 0) {
return allBarsLeftOrRight.reduce((bar1, bar2) => {
const bar1Elem = findBarElement(ganttId, bar1.ganttBarConfig.id);
const bar2Elem = findBarElement(ganttId, bar2.ganttBarConfig.id);
const bar1Dist = Math.abs(bar1Elem.offsetLeft - barElem.offsetLeft);
const bar2Dist = Math.abs(bar2Elem.offsetLeft - barElem.offsetLeft);
return bar1Dist < bar2Dist ? bar1 : bar2;
}, allBarsLeftOrRight[0]);
} else {
return null;
}
};
return {
setDragLimitsOfGanttBar
};
}
function useBarKeyboardControl(bar, config, emitBarEvent) {
const dayjs2 = useDayjsHelper(config);
const { barStart, barEnd, dateFormat, precision } = config;
const rowManager = inject("useRows");
const movement = useBarMovement(config, rowManager, dayjs2);
const TIME_STEP = {
hour: 5,
day: 120,
week: 840,
month: 3600
};
const getTimeStep = (isShiftPressed) => {
const baseStep = TIME_STEP[precision.value] || TIME_STEP.hour;
return isShiftPressed ? baseStep * 12 : baseStep;
};
const moveBarPosition = (direction, isShiftPressed) => {
const multiplier = direction === "forward" ? 1 : -1;
const minutesToMove = getTimeStep(isShiftPressed);
const currentStart = dayjs2.toDayjs(bar[barStart.value]);
const currentEnd = dayjs2.toDayjs(bar[barEnd.value]);
const newStart = currentStart.add(minutesToMove * multiplier, "minutes");
const newEnd = currentEnd.add(minutesToMove * multiplier, "minutes");
if (newStart.isBefore(config.chartStart.value) || newEnd.isAfter(config.chartEnd.value)) {
return;
}
const newStartStr = dayjs2.format(newStart, dateFormat.value);
const newEndStr = dayjs2.format(newEnd, dateFormat.value);
const result = movement.moveBar(bar, newStartStr, newEndStr);
if (result.success) {
emitDragEvents();
}
};
const resizeBar = (type, isShiftPressed) => {
const currentStart = dayjs2.toDayjs(bar[barStart.value]);
const currentEnd = dayjs2.toDayjs(bar[barEnd.value]);
let minutesToMove = getTimeStep(isShiftPressed);
if (minutesToMove === 5) {
minutesToMove = 10;
}
const timePerSide = minutesToMove / 2;
let newStart;
let newEnd;
if (type === "expand") {
newStart = dayjs2.format(
currentStart.subtract(timePerSide, "minutes"),
dateFormat.value
);
newEnd = dayjs2.format(currentEnd.add(timePerSide, "minutes"), dateFormat.value);
} else {
const currentDuration = currentEnd.diff(currentStart, "minutes");
if (currentDuration <= minutesToMove) {
return;
}
newStart = dayjs2.format(currentStart.add(timePerSide, "minutes"), dateFormat.value);
newEnd = dayjs2.format(currentEnd.subtract(timePerSide, "minutes"), dateFormat.value);
}
const startDayjs = dayjs2.toDayjs(newStart);
const endDayjs = dayjs2.toDayjs(newEnd);
if (startDayjs.isBefore(config.chartStart.value) || endDayjs.isAfter(config.chartEnd.value)) {
return;
}
const result = movement.moveBar(bar, newStart, newEnd);
if (result.success) {
emitDragEvents();
}
};
const emitDragEvents = () => {
const mockEvent = new MouseEvent("drag", { bubbles: true });
emitBarEvent(mockEvent, bar);
const mockEndEvent = new MouseEvent("dragend", { bubbles: true });
emitBarEvent(mockEndEvent, bar);
};
const onBarKeyDown = (event) => {
const target = event.target;
if (!target.id || target.id !== bar.ganttBarConfig.id || bar.ganttBarConfig.immobile) {
return;
}
switch (event.key) {
case "ArrowLeft":
event.preventDefault();
moveBarPosition("backward", event.shiftKey);
break;
case "ArrowRight":
event.preventDefault();
moveBarPosition("forward", event.shiftKey);
break;
case "ArrowUp":
event.preventDefault();
resizeBar("expand", event.shiftKey);
break;
case "ArrowDown":
event.preventDefault();
resizeBar("shrink", event.shiftKey);
break;
}
};
return {
onBarKeyDown
};
}
function useTouchEvents(initDragCallback, threshold = 5) {
const touchState = ref({
isDragging: false,
startX: 0,
startY: 0,
lastX: 0,
lastY: 0,
currentBar: null,
dragTarget: null
});
const resetTouchState = () => {
touchState.value = {
isDragging: false,
startX: 0,
startY: 0,
lastX: 0,
lastY: 0,
currentBar: null,
dragTarget: null
};
};
const determineDragTarget = (element) => {
if (element.classList.contains("g-gantt-bar-handle-left")) {
return "leftHandle";
}
if (element.classList.contains("g-gantt-bar-handle-right")) {
return "rightHandle";
}
if (element.classList.contains("g-gantt-bar")) {
return "bar";
}
const barElement = element.closest(".g-gantt-bar");
if (barElement) {
return "bar";
}
return null;
};
const createMouseEventFromTouch = (touch, eventType, movementX = 0, movementY = 0) => {
const mouseEvent = new MouseEvent(eventType, {
bubbles: true,
cancelable: true,
clientX: touch.clientX,
clientY: touch.clientY,
movementX,
movementY,
view: window
});
if (touchState.value.currentBar) {
const targetElement = document.querySelector(
`#${touchState.value.currentBar.ganttBarConfig.id}`
);
if (targetElement) {
Object.defineProperty(mouseEvent, "target", { value: targetElement });
}
}
return mouseEvent;
};
const handleTouchStart = (event, bar) => {
const touch = event.touches[0];
if (!touch || !bar) return;
const targetElement = event.target;
if (!targetElement) return;
const dragTarget = determineDragTarget(targetElement);
if (!dragTarget) return;
event.preventDefault();
touchState.value = {
isDragging: false,
startX: touch.clientX,
startY: touch.clientY,
lastX: touch.clientX,
lastY: touch.clientY,
currentBar: bar,
dragTarget
};
const mouseEvent = createMouseEventFromTouch(touch, "mousedown");
initDragCallback(bar, mouseEvent);
};
const handleTouchMove = (event) => {
const touch = event.touches[0];
if (!touch || !touchState.value.currentBar) return;
const deltaX = Math.abs(touch.clientX - touchState.value.startX);
const deltaY = Math.abs(touch.clientY - touchState.value.startY);
if (!touchState.value.isDragging && (deltaX > threshold || deltaY > threshold)) {
touchState.value.isDragging = true;
}
if (touchState.value.isDragging) {
event.preventDefault();
const movementX = touch.clientX - touchState.value.lastX;
const movementY = touch.clientY - touchState.value.lastY;
touchState.value.lastX = touch.clientX;
touchState.value.lastY = touch.clientY;
const mouseEvent = createMouseEventFromTouch(touch, "mousemove", movementX, movementY);
window.dispatchEvent(mouseEvent);
}
};
const handleTouchEnd = (event) => {
const touch = event.changedTouches[0];
if (!touch || !touchState.value.currentBar) return;
if (touchState.value.isDragging) {
event.preventDefault();
const mouseEvent = createMouseEventFromTouch(touch, "mouseup");
window.dispatchEvent(mouseEvent);
}
resetTouchState();
};
const handleTouchCancel = handleTouchEnd;
return {
handleTouchStart,
handleTouchMove,
handleTouchEnd,
handleTouchCancel
};
}
const _hoisted_1$4 = ["id", "aria-label", "aria-grabbed", "aria-describedby"];
const _hoisted_2$2 = {
key: 0,
class: "progress-text"
};
const _hoisted_3$2 = ["width", "height"];
const _hoisted_4$2 = ["d", "fill"];
const _hoisted_5$2 = {
key: 3,
class: "g-gantt-bar-label"
};
const _hoisted_6$2 = { key: 0 };
const _hoisted_7$2 = {
key: 0,
class: "g-gantt-bar-label-edit"
};
const _hoisted_8$2 = { key: 1 };
const _hoisted_9$2 = ["innerHTML"];
const _sfc_main$4 = /* @__PURE__ */ defineComponent({
__name: "GGanttBar",
props: {
bar: {}
},
setup(__props) {
const props = __props;
const ganttId = inject(GANTT_ID_KEY);
const connectionCreation = inject("connectionCreation");
const emitBarEvent = provideEmitBarEvent();
const config = provideConfig();
const { rowHeight } = config;
const { bar } = toRefs(props);
const { mapTimeToPosition, mapPositionToTime } = useTimePositionMapping();
const { initDragOfBar, initDragOfBundle } = useBarDragManagement();
const { setDragLimitsOfGanttBar } = useBarDragLimit();
const isDragging = ref(false);
const isEditing = ref(false);
const editedLabel = ref("");
const labelInput = ref(null);
const {
barStart,
barEnd,
width,
chartStart,
chartEnd,
chartSize,
showLabel,
showProgress,
defaultProgressResizable,
enableConnectionCreation,
barLabelEditable
} = config;
const xStart = ref(0);
const xEnd = ref(0);
const barConfig = computed(() => bar.value.ganttBarConfig);
const isGroupBar = computed(() => {
return bar.value.ganttBarConfig.id.startsWith("group-");
});
const progressStyle = computed(() => {
var _a;
const progress = props.bar.ganttBarConfig.progress ?? 0;
const baseStyle = props.bar.ganttBarConfig.progressStyle || {};
const barColor = ((_a = props.bar.ganttBarConfig.style) == null ? void 0 : _a.background) || "#5F9EA0";
return {
...baseStyle,
left: 0,
width: `${Math.min(Math.max(progress, 0), 100)}%`,
backgroundColor: baseStyle.backgroundColor || getDarkerColor(barColor),
transition: "width 0.3s ease",
borderRadius: "inherit",
height: "100%"
};
});
const connectionPointStyle = computed(() => ({
width: "12px",
height: "12px",
borderRadius: "50%",
cursor: "pointer",
background: canBeTarget.value ? "#00ff00" : "#ff0000",
transition: "all 0.2s ease",
opacity: (connectionCreation == null ? void 0 : connectionCreation.connectionState.value.isCreating) || isBarHovered.value || startPointHover.value || endPointHover.value ? 1 : 0,
zIndex: 1e3
}));
const startPointStyle = computed(() => ({
...connectionPointStyle.value,
left: 0,
top: "50%",
transform: "translate(-50%, -50%)"
}));
const endPointStyle = computed(() => ({
...connectionPointStyle.value,
right: 0,
top: "50%",
transform: "translate(50%, -50%)"
}));
const canBeTarget = computed(() => {
if (!(connectionCreation == null ? void 0 : connectionCreation.connectionState.value.isCreating)) return false;
return connectionCreation.canBeConnectionTarget.value(props.bar);
});
const firstMousemoveCallback = (e) => {
if (barConfig.value.bundle != null) {
initDragOfBundle(bar.value, e);
} else {
initDragOfBar(bar.value, e);
}
isDragging.value = true;
};
const prepareForDrag = () => {
setDragLimitsOfGanttBar(bar.value);
if (barConfig.value.immobile) {
return;
}
window.addEventListener("mousemove", firstMousemoveCallback, {
once: true
});
window.addEventListener(
"mouseup",
() => {
window.removeEventListener("mousemove", firstMousemoveCallback);
isDragging.value = false;
},
{ once: true }
);
};
const onMouseEvent = (e) => {
var _a;
e.preventDefault();
if (e.type === "mousedown") {
prepareForDrag();
}
const barContainer = (_a = barContainerEl == null ? void 0 : barContainerEl.value) == null ? void 0 : _a.getBoundingClientRect();
if (!barContainer) {
return;
}
const datetime = mapPositionToTime(e.clientX - barContainer.left);
emitBarEvent(e, bar.value, datetime);
};
const barContainerEl = inject(BAR_CONTAINER_KEY);
const { handleTouchStart, handleTouchMove, handleTouchEnd, handleTouchCancel } = useTouchEvents(
(_draggedBar, e) => {
firstMousemoveCallback(e);
isDragging.value = true;
}
);
const onTouchEvent = (e) => {
if (bar.value.ganttBarConfig.immobile) return;
let mouseEvent;
switch (e.type) {
case "touchstart":
mouseEvent = handleTouchStart(e, bar.value);
break;
case "touchmove":
mouseEvent = handleTouchMove(e);
break;
case "touchend":
mouseEvent = handleTouchEnd(e);
break;
case "touchcancel":
mouseEvent = handleTouchCancel(e);
break;
}
if (mouseEvent) {
onMouseEvent(mouseEvent);
}
};
const isProgressDragging = ref(false);
const progressDragStart = ref(0);
const initialProgress = ref(0);
const handleProgressDragStart = (e) => {
if (!props.bar.ganttBarConfig.progressResizable && !defaultProgressResizable.value) return;
e.stopPropagation();
isProgressDragging.value = true;
progressDragStart.value = e.clientX;
initialProgress.value = props.bar.ganttBarConfig.progress ?? 0;
window.addEventListener("mousemove", handleProgressDrag);
window.addEventListener("mouseup", handleProgressDragEnd);
emitBarEvent(
{
...e,
type: "progress-drag-start"
},
props.bar
);
};
const { findBarElement } = useBarSelector();
const handleProgressDrag = (e) => {
if (!isProgressDragging.value) return;
const barElement = findBarElement(ganttId, props.bar.ganttBarConfig.id);
if (!barElement) return;
const rect = barElement.getBoundingClientRect();
const deltaX = e.clientX - progressDragStart.value;
const percentageDelta = deltaX / rect.width * 100;
const newProgress = Math.min(Math.max(initialProgress.value + percentageDelta, 0), 100);
bar.value.ganttBarConfig.progress = Math.round(newProgress);
emitBarEvent(
{
...e,
type: "progress-change"
},
props.bar
);
};
const handleProgressDragEnd = (e) => {
if (!isProgressDragging.value) return;
isProgressDragging.value = false;
window.removeEventListener("mousemove", handleProgressDrag);
window.removeEventListener("mouseup", handleProgressDragEnd);
emitBarEvent(
{
...e,
type: "progress-drag-end"
},
props.bar
);
};
const startPointHover = ref(false);
const endPointHover = ref(false);
const isBarHovered = ref(false);
const handleConnectionPointMouseEnter = (point) => {
if (!enableConnectionCreation.value) return;
if (point === "start") {
startPointHover.value = true;
} else {
endPointHover.value = true;
}
connectionCreation == null ? void 0 : connectionCreation.handleConnectionPointHover(props.bar.ganttBarConfig.id, point, true);
};
const handleConnectionPointMouseLeave = (point) => {
if (!enableConnectionCreation.value) return;
if (point === "start") {
startPointHover.value = false;
} else {
endPointHover.value = false;
}
connectionCreation == null ? void 0 : connectionCreation.handleConnectionPointHover(props.bar.ganttBarConfig.id, point, false);
};
const handleConnectionPointMouseDown = (e, point) => {
if (!enableConnectionCreation.value) return;
e.stopPropagation();
connectionCreation == null ? void 0 : connectionCreation.startConnectionCreation(props.bar, point, e);
};
const handleConnectionDrop = (e, point) => {
if (!enableConnectionCreation.value) return;
e.stopPropagation();
connectionCreation == null ? void 0 : connectionCreation.completeConnection(props.bar, point, e);
};
const handleBarMouseEnter = (e) => {
isBarHovered.value = true;
onMouseEvent(e);
};
const handleBarMouseLeave = (e) => {
isBarHovered.value = false;
onMouseEvent(e);
};
const startEditing = (e) => {
if (!barLabelEditable.value || isGroupBar.value) return;
e.stopPropagation();
bar.value.ganttBarConfig._previousLabel = barConfig.value.label || "";
isEditing.value = true;
editedLabel.value = barConfig.value.label || "";
setTimeout(() => {
if (labelInput.value) {
labelInput.value.focus();
labelInput.value.select();
}
}, 0);
};
const saveLabel = () => {
if (!isEditing.value) return;
bar.value.ganttBarConfig.label = editedLabel.value;
emitBarEvent(
{
type: "label-edit",
preventDefault: () => {
},
stopPropagation: () => {
}
},
bar.value
);
isEditing.value = false;
};
const cancelEditing = () => {
isEditing.value = false;
};
const handleLabelKeydown = (e) => {
if (e.key === "Enter") {
saveLabel();
} else if (e.key === "Escape") {
cancelEditing();
}
};
const handleInputBlur = () => {
saveLabel();
};
const getDarkerColor = (color) => {
const rgb = color.startsWith("#") ? hexToRgb(color) : parseRgb(color);
return `rgba(${Math.max(0, rgb.r - 40)}, ${Math.max(0, rgb.g - 40)}, ${Math.max(0, rgb.b - 40)}, ${rgb.a})`;
};
const hexToRgb = (hex) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
a: 1
} : { r: 0, g: 0, b: 0, a: 1 };
};
const parseRgb = (color) => {
const matches = color.match(/(\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?/);
if (matches) {
return {
r: parseInt(matches[1]),
g: parseInt(matches[2]),
b: parseInt(matches[3]),
a: matches[4] ? parseFloat(matches[4]) : 1
};
}
return { r: 0, g: 0, b: 0, a: 1 };
};
const getGroupBarPath = (width2, height) => {
const mainBarHeight = height * 0.5;
return `
M 0 0
L 0 ${height}
L ${15} ${mainBarHeight}
L ${width2 - 15} ${mainBarHeight}
L ${width2} ${height}
L ${width2} 0
L 0 0
`;
};
const { onBarKeyDown } = useBarKeyboardControl(bar.value, config, emitBarEvent);
onMounted(() => {
xStart.value = mapTimeToPosition(bar.value[barStart.value]);
xEnd.value = mapTimeToPosition(bar.value[barEnd.value]);
watch(
[() => bar.value, width, chartStart, chartEnd, chartSize.width],
() => {
const newXStart = mapTimeToPosition(bar.value[barStart.value]);
const newXEnd = mapTimeToPosition(bar.value[barEnd.value]);
xStart.value = newXStart;
xEnd.value = newXEnd;
},
{ deep: true, immediate: true }
);
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
id: barConfig.value.id,
class: normalizeClass(["g-gantt-bar", barConfig.value.class || ""]),
style: normalizeStyle({
...barConfig.value.style,
position: "absolute",
top: `${unref(rowHeight) * 0.15}px`,
left: `${xStart.value}px`,
width: `${xEnd.value - xStart.value}px`,
height: `${unref(rowHeight) * 0.7}px`,
zIndex: isDragging.value ? 3 : 2,
cursor: unref(bar).ganttBarConfig.immobile ? "" : "grab"
}),
onMousedown: onMouseEvent,
onClick: onMouseEvent,
onMouseenter: handleBarMouseEnter,
onMouseleave: handleBarMouseLeave,
onContextmenu: onMouseEvent,
onTouchstart: onTouchEvent,
onTouchmove: onTouchEvent,
onTouchend: onTouchEvent,
onTouchcancel: onTouchEvent,
onKeydown: _cache[9] || (_cache[9] = //@ts-ignore
(...args) => unref(onBarKeyDown) && unref(onBarKeyDown)(...args)),
onDblclick: startEditing,
role: "listitem",
"aria-label": `Activity ${barConfig.value.label}`,
"aria-grabbed": isDragging.value,
tabindex: "0",
"aria-describedby": `tooltip-${barConfig.value.id}`
}, [
unref(enableConnectionCreation) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
createElementVNode("div", {
class: "connection-point start",
style: normalizeStyle([startPointStyle.value, { position: "absolute" }]),
onMouseenter: _cache[0] || (_cache[0] = ($event) => handleConnectionPointMouseEnter("start")),
onMouseleave: _cache[1] || (_cache[1] = ($event) => handleConnectionPointMouseLeave("start")),
onMousedown: _cache[2] || (_cache[2] = ($event) => handleConnectionPointMouseDown($event, "start")),
onMouseup: _cache[3] || (_cache[3] = ($event) => handleConnectionDrop($event, "start"))
}, null, 36),
createElementVNode("div", {
class: "connection-point end",
style: normalizeStyle([endPointStyle.value, { position: "absolute" }]),
onMouseenter: _cache[4] || (_cache[4] = ($event) => handleConnectionPointMouseEnter("end")),
onMouseleave: _cache[5] || (_cache[5] = ($event) => handleConnectionPointMouseLeave("end")),
onMousedown: _cache[6] || (_cache[6] = ($event) => handleConnectionPointMouseDown($event, "end")),
onMouseup: _cache[7] || (_cache[7] = ($event) => handleConnectionDrop($event, "end"))
}, null, 36)
], 64)) : createCommentVNode("", true),
barConfig.value.progress !== void 0 ? (openBlock(), createElementBlock("div", {
key: 1,
class: "g-gantt-progress-bar",
style: normalizeStyle(progressStyle.value)
}, [
unref(showProgress) ? (openBlock(), createElementBlock("span", _hoisted_2$2, toDisplayString(Math.round(barConfig.value.progress)) + "%", 1)) : createCommentVNode("", true),
barConfig.value.progressResizable || unref(defaultProgressResizable) ? (openBlock(), createElementBlock("div", {
key: 1,
class: "g-gantt-progress-handle",
style: normalizeStyle({ right: unref(bar).ganttBarConfig.progress === 0 ? 0 : "-4px" }),
onMousedown: handleProgressDragStart
}, null, 36)) : createCommentVNode("", true)
], 4)) : createCommentVNode("", true),
isGroupBar.value ? renderSlot(_ctx.$slots, "group-bar", {
key: 2,
width: xEnd.value - xStart.value,
height: unref(rowHeight) * 0.7,
bar: unref(bar)
}, () => [
(openBlock(), createElementBlock("svg", {
class: "group-bar-decoration",
width: xEnd.value - xStart.value,
height: unref(rowHeight) * 0.7
}, [
createElementVNode("path", {
d: getGroupBarPath(xEnd.value - xStart.value, unref(rowHeight) * 0.65),
fill: unref(config).colors.value.barContainer
}, null, 8, _hoisted_4$2)
], 8, _hoisted_3$2))
]) : (openBlock(), createElementBlock("div", _hoisted_5$2, [
renderSlot(_ctx.$slots, "bar-label", { bar: unref(bar) }, () => [
!isGroupBar.value && unref(showLabel) ? (openBlock(), createElementBlock("div", _hoisted_6$2, [
isEditing.value && unref(barLabelEditable) ? (openBlock(), createElementBlock("div", _hoisted_7$2, [
withDirectives(createElementVNode("input", {
ref_key: "labelInput",
ref: labelInput,
"onUpdate:modelValue": _cache[8] || (_cache[8] = ($event) => editedLabel.value = $event),
onKeydown: handleLabelKeydown,
onBlur: handleInputBlur,
class: "g-gantt-bar-label-input"
}, null, 544), [
[vModelText, editedLabel.value]
])
])) : (openBlock(), createElementBlock("div", _hoisted_8$2, toDisplayString(barConfig.value.label || ""), 1))
])) : createCommentVNode("", true),
barConfig.value.html ? (openBlock(), createElementBlock("div", {
key: 1,
innerHTML: barConfig.value.html
}, null, 8, _hoisted_9$2)) : createCommentVNode("", true)
])
])),
barConfig.value.hasHandles ? (openBlock(), createElementBlock(Fragment, { key: 4 }, [
_cache[10] || (_cache[10] = createElementVNode("div", { class: "g-gantt-bar-handle-left" }, null, -1)),
_cache[11] || (_cache[11] = createElementVNode("div", { class: "g-gantt-bar-handle-right" }, null, -1))
], 64)) : createCommentVNode("", true)
], 46, _hoisted_1$4);
};
}
});
const _hoisted_1$3 = {
key: 0,
class: "g-gantt-row-children"
};
const _sfc_main$3 = /* @__PURE__ */ defineComponent({
__name: "GGanttRow",
props: {
label: {},
bars: {},
highlightOnHover: { type: Boolean },
id: {},
children: {},
connections: {}
},
emits: ["drop", "range-selection"],
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const rowManager = inject("useRows");
const { rowHeight, colors, labelColumnTitle, rowClass } = provideConfig();
const { highlightOnHover } = toRefs(props);
const barContainer = ref(null);
const isHovering = ref(false);
const isSelecting = ref(false);
const selectionStartX = ref(0);
const selectionEndX = ref(0);
const selectionVisible = ref(false);
const isGroup = computed(() => {
var _a;
return Boolean((_a = props.children) == null ? void 0 : _a.length);
});
const isExpanded = computed(() => {
if (!isGroup.value || !props.id) return false;
return rowManager.isGroupExpanded(props.id);
});
const rowStyle = computed(() => {
const baseStyle = {
height: `${rowHeight.value}px`,
borderBottom: `1px solid ${colors.value.gridAndBorder}`,
background: (highlightOnHover == null ? void 0 : highlightOnHover.value) && isHovering.value ? colors.value.hoverHighlight : void 0
};
if (isGroup.value) {
return {
...baseStyle,
background: (highlightOnHover == null ? void 0 : highlightOnHover.value) && isHovering.value ? colors.value.hoverHighlight : void 0
};
}
return baseStyle;
});
const rowClasses = computed(() => {
const classes = ["g-gantt-row"];
if (rowClass.value && props) {
classes.push(rowClass.value(props));
}
if (isGroup.value) {
classes.push("g-gantt-row-group");
}
return classes;
});
const visibleChildRows = computed(() => {
if (!isGroup.value || !isExpanded.value) return [];
return props.children || [];
});
const selectionStyle = computed(() => {
if (!selectionVisible.value) return { display: "none" };
const left = Math.min(selectionStartX.value, selectionEndX.value);
const width = Math.abs(selectionEndX.value - selectionStartX.value);
return {
position: "absolute",
left: `${left}px`,
width: `${width}px`,
height: `${rowHeight.value - 4}px`,
background: colors.value.rangeHighlight,
opacity: 0.6,
pointerEvents: "none",
zIndex: 1,
margin: "auto 0",
top: "1px"
};
});
const { mapPositionToTime } = useTimePositionMapping();
const isBlank = (str) => {
return !str || /^\s*$/.test(str);
};
const onDrop = (e) => {
var _a;
if (isGroup.value) return;
const container = (_a = barContainer.value) == null ? void 0 : _a.getBoundingClientRect();
if (!container) {
console.error("Hyper Vue Gantt: failed to find bar container element for row.");
return;
}
const xPos = e.clientX - container.left;
const datetime = mapPositionToTime(xPos);
emit("drop", { e, datetime });
};
const handleGroupToggle = (event) => {
event.stopPropagation();
if (props.id) {
rowManager.toggleGroupExpansion(props.id);
}
};
const handleSelectionStart = (e) => {
var _a;
if (isGroup.value) return;
const target = e.target;
if (target.closest(".g-gantt-bar")) return;
const container = (_a = barContainer.value) == null ? void 0 : _a.getBoundingClientRect();
if (!container) return;
const xPos = e.clientX - container.left;
isSelecting.value = true;
selectionStartX.value = xPos;
selectionEndX.value = xPos;
selectionVisible.value = false;
e.preventDefault();
document.addEventListener("mousemove", handleSelectionMove);
document.addEventListener("mouseup", handleSelectionEnd);
};
const handleSelectionMove = (e) => {
if (!isSelecting.value || !barContainer.value) return;
const container = barContainer.value.getBoundingClientRect();
const xPos = Math.max(0, e.clientX - container.left);
selectionEndX.value = xPos;
selectionVisible.value = Math.abs(selectionEndX.value - selectionStartX.value) > 5;
};
const handleSelectionEnd = (e) => {
document.removeEventListener("mousemove", handleSelectionMove);
document.removeEventListener("mouseup", handleSelectionEnd);
if (!isSelecting.value || !selectionVisible.value) {
resetSelection();
return;
}
const startTime = mapPositionToTime(Math.min(selectionStartX.value, selectionEndX.value));
const endTime = mapPositionToTime(Math.max(selectionStartX.value, selectionEndX.value));
const rowData = {
id: props.id,
label: props.label,
bars: props.bars,
children: props.children,
connections: props.connections
};
emit("range-selection", {
row: rowData,
startDate: startTime,
endDate: endTime,
e
});
resetSelection();
};
const resetSelection = () => {
isSelecting.value = false;
selectionVisible.value = false;
selectionStartX.value = 0;
selectionEndX.value = 0;
};
provide(BAR_CONTAINER_KEY, barContainer);
return (_ctx, _cache) => {
const _component_g_gantt_row = resolveComponent("g-gantt-row", true);
return openBlock(), createElementBlock(Fragment, null, [
createElementVNode("div", {
class: normalizeClass(rowClasses.value),
style: normalizeStyle(rowStyle.value),
onDragover: _cache[2] || (_cache[2] = withModifiers(($event) => isHovering.value = true, ["prevent"])),
onDragleave: _cache[3] || (_cache[3] = ($event) => isHovering.value = false),
onDrop: _cache[4] || (_cache[4] = ($event) => onDrop($event)),
onMouseover: _cache[5] || (_cache[5] = ($event) => isHovering.value = true),
onMouseleave: _cache[6] || (_cache[6] = ($event) => isHovering.value = false),
role: "list",
onMousedown: handleSelectionStart
}, [
!isBlank(_ctx.label) && !unref(labelColumnTitle) ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(["g-gantt-row-label", { "g-gantt-row-group-label": isGroup.value }]),
style: normalizeStyle({ background: unref(colors).primary, color: unref(colors).text }),
onClick: _cache[1] || (_cache[1] = ($event) => isGroup.value ? handleGroupToggle($event) : void 0)
}, [
isGroup.value ? (openBlock(), createElementBlock("button", {
key: 0,
class: "group-toggle-button",
onClick: _cache[0] || (_cache[0] = ($event) => handleGroupToggle($event))
}, [
createVNode(unref(FontAwesomeIcon), {
icon: isExpanded.value ? unref(faChevronDown) : unref(faChevronRight),
class: "group-icon"
}, null, 8, ["icon"])
])) : createCommentVNode("", true),
renderSlot(_ctx.$slots, "label", {}, () => [
createTextVNode(toDisplayString(_ctx.label), 1)
])
], 6)) : createCommentVNode("", true),
createElementVNode("div", mergeProps({
ref_key: "barContainer",
ref: barContainer,
class: "g-gantt-row-bars-container"
}, _ctx.$attrs), [
selectionVisible.value ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-gantt-range-selection",
style: normalizeStyle(selectionStyle.value)
}, null, 4)) : createCommentVNode("", true),
createVNode(TransitionGroup, {
name: "bar-transition",
tag: "div"
}, {
default: withCtx(() => [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.bars, (bar) => {
return openBlock(), createBlock(_sfc_main$4, {
key: bar.ganttBarConfig.id,
bar,
class: normalizeClass({ "g-gantt-group-bar": isGroup.value })
}, createSlots({ _: 2 }, [
renderList(_ctx.$slots, (_, name) => {
return {
name,
fn: withCtx((slotProps) => [
renderSlot(_ctx.$slots, name, mergeProps({ ref_for: true }, slotProps))
])
};
})
]), 1032, ["bar", "class"]);
}), 128))
]),
_: 3
})
], 16)
], 38),
isGroup.value && isExpanded.value ? (openBlock(), createElementBlock("div", _hoisted_1$3, [
(openBlock(true), createElementBlock(Fragment, null, renderList(visibleChildRows.value, (child) => {
return openBlock(), createBlock(_component_g_gantt_row, mergeProps({
key: child.id || child.label,
ref_for: true
}, child, {
highlightOnHover: unref(highlightOnHover),
onRangeSelection: _cache[7] || (_cache[7] = (event) => _ctx.$emit("range-selection", event))
}), createSlots({ _: 2 }, [
renderList(_ctx.$slots, (_, name) => {
return {
name,
fn: withCtx((slotProps) => [
renderSlot(_ctx.$slots, name, mergeProps({ ref_for: true }, slotProps))
])
};
})
]), 1040, ["highlightOnHover"]);
}), 128))
])) : createCommentVNode("", true)
], 64);
};
}
});
const _hoisted_1$2 = { style: { "font-weight": "bold" } };
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
__name: "GGanttPointerMarker",
setup(__props) {
const chartAreaEl = inject(CHART_AREA_KEY);
const chartWrapperEl = inject(CHART_WRAPPER_KEY);
const hitBars = ref([]);
const tooltipContainer = useTemplateRef("tooltip");
const { colors, barStart, barEnd } = provideConfig();
const { mapPositionToTime } = useTimePositionMapping();
const rowManager = inject("useRows");
const { toDayjs } = useDayjsHelper();
const { elementX } = useMouseInElement(chartAreaEl);
const { isOutside, x } = useMouseInElement(chartWrapperEl);
const { width, height } = useElementBounding(tooltipContainer);
const { top, bottom } = useElementBounding(chartWrapperEl);
const leftOffset = computed((prev) => isOutside.value ? prev ?? 0 : elementX.value);
const datetime = computed(() => mapPositionToTime(leftOffset.value));
const bars = computedWithControl(rowManager.getFlattenedRows, () => rowManager.getFlattenedRows().flatMap((row) => row.bars));
const tooltipStylePosition = computed(() => {
if (top.value - height.value > 0) {
return {
top: `${top.value}px`,
transform: `translateY(-100%)`,
left: `${x.value - width.value / 2}px`
};
}
return {
top: `${bottom.value}px`,
left: `${x.value - width.value / 2}px`
};
});
watchThrottled(leftOffset, () => {
const hitBarsElement = [];
const cursorTime = toDayjs(datetime.value);
for (let i = 0; i < bars.value.length; i++) {
const element = bars.value[i];
const begin = toDayjs(element[barStart.value]);
const end = toDayjs(element[barEnd.value]);
if (cursorTime.isBetween(begin, end)) {
hitBarsElement.push(element);
}
}
hitBars.value = hitBarsElement;
}, { throttle: 200 });
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "g-grid-pointer-marker-container",
style: normalizeStyle({
left: `${leftOffset.value}px`
})
}, [
createElementVNode("div", {
class: "g-grid-pointer-marker-marker",
style: normalizeStyle({
border: `1px dashed ${unref(colors).markerCurrentTime}`
})
}, null, 4),
(openBlock(), createBlock(Teleport, { to: "body" }, [
createVNode(Transition, {
name: "g-fade",
mode: "out-in"
}, {
default: withCtx(() => [
!unref(isOutside) ? (openBlock(), createElementBlock("div", {
key: 0,
ref: "tooltip",
class: "g-grid-pointer-marker-tooltip",
style: normalizeStyle(tooltipStylePosition.value)
}, [
renderSlot(_ctx.$slots, "pointer-marker-tooltips", normalizeProps(guardReactiveProps({ hitBars: hitBars.value, datetime: datetime.value })), () => [
createElementVNode("div", {
class: "g-grid-pointer-marker-tooltip-content",
style: normalizeStyle({ background: unref(colors).primary, color: unref(colors).text })
}, [
createElementVNode("div", _hoisted_1$2, "Event at " + toDisplayString(datetime.value), 1),
createElementVNode("ul", null, [
(openBlock(true), createElementBlock(Fragment, null, renderList(hitBars.value, (bar) => {
return openBlock(), createElementBlock("li", {
key: bar.ganttBarConfig.id
}, toDisplayString(bar.ganttBarConfig.label ?? bar.ganttBarConfig.id), 1);
}), 128))
])
], 4)
])
], 4)) : createCommentVNode("", true)
]),
_: 3
})
]))
], 4);
};
}
});
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}
var papaparse_min$1 = { exports: {} };
/* @license
Papa Parse
v5.5.2
https://github.com/mholt/PapaParse
License: MIT
*/
var papaparse_min = papaparse_min$1.exports;
var hasRequiredPapaparse_min;
function requirePapaparse_min() {
if (hasRequiredPapaparse_min) return papaparse_min$1.exports;
hasRequiredPapaparse_min = 1;
(function(module, exports) {
((e, t) => {
module.exports = t();
})(papaparse_min, function r() {
var n = "undefined" != typeof self ? self : "undefined" != typeof window ? window : void 0 !== n ? n : {};
var d, s = !n.document && !!n.postMessage, a = n.IS_PAPA_WORKER || false, o = {}, h2 = 0, v = {};
function u(e) {
this._handle = null, this._finished = false, this._completed = false, this._halted = false, this._input = null, this._baseIndex = 0, this._partialLine = "", this._rowCount = 0, this._start = 0, this._nextChunk = null, this.isFirstChunk = true, this._completeResults = { data: [], errors: [], meta: {} }, (function(e2) {
var t = w(e2);
t.chunkSize = parseInt(t.chunkSize), e2.step || e2.chunk || (t.chunkSize = null);
this._handle = new i(t), (this._handle.streamer = this)._config = t;
}).call(this, e), this.parseChunk = function(t, e2) {
var i2 = parseInt(this._config.skipFirstNLines) || 0;
if (this.isFirstChunk && 0 < i2) {
let e3 = this._config.newline;
e3 || (r2 = this._config.quoteChar || '"', e3 = this._handle.guessLineEndings(t, r2)), t = [...t.split(e3).slice(i2)].join(e3);
}
this.isFirstChunk && U(this._config.beforeFirstChunk) && void 0 !== (r2 = this._config.beforeFirstChunk(t)) && (t = r2), this.isFirstChunk = false, this._halted = false;
var i2 = this._partialLine + t, r2 = (this._partialLine = "", this._handle.parse(i2, this._baseIndex, !this._finished));
if (!this._handle.paused() && !this._handle.aborted()) {
t = r2.meta.cursor, i2 = (this._finished || (this._partialLine = i2.substring(t - this._baseIndex), this._baseIndex = t), r2 && r2.data && (this._rowCount += r2.data.length), this._finished || this._config.preview && this._rowCount >= this._config.preview);
if (a) n.postMessage({ results: r2, workerId: v.WORKER_ID, finished: i2 });
else if (U(this._config.chunk) && !e2) {
if (this._config.chunk(r2, this._handle), this._handle.paused() || this._handle.aborted()) return void (this._halted = true);
this._completeResults = r2 = void 0;
}
return this._config.step || this._config.chunk || (this._completeResults.data = this._completeResults.data.concat(r2.data), this._completeResults.errors = this._completeResults.errors.concat(r2.errors), this._completeResults.meta = r2.meta), this._completed || !i2 || !U(this._config.complete) || r2 && r2.meta.aborted || (this._config.complete(this._completeResults, this._input), this._completed = true), i2 || r2 && r2.meta.paused || this._nextChunk(), r2;
}
this._halted = true;
}, this._sendError = function(e2) {
U(this._config.error) ? this._config.error(e2) : a && this._config.error && n.postMessage({ workerId: v.WORKER_ID, error: e2, finished: false });
};
}
function f(e) {
var r2;
(e = e || {}).chunkSize || (e.chunkSize = v.RemoteChunkSize), u.call(this, e), this._nextChunk = s ? function() {
this._readChunk(), this._chunkLoaded();
} : function() {
this._readChunk();
}, this.stream = function(e2) {
this._input = e2, this._nextChunk();
}, this._readChunk = function() {
if (this._finished) this._chunkLoaded();
else {
if (r2 = new XMLHttpRequest(), this._config.withCredentials && (r2.withCredentials = this._config.withCredentials), s || (r2.onload = y(this._chunkLoaded, this), r2.onerror = y(this._chunkError, this)), r2.open(this._config.downloadRequestBody ? "POST" : "GET", this._input, !s), this._config.downloadRequestHeaders) {
var e2, t = this._config.downloadRequestHeaders;
for (e2 in t) r2.setRequestHeader(e2, t[e2]);
}
var i2;
this._config.chunkSize && (i2 = this._start + this._config.chunkSize - 1, r2.setRequestHeader("Range", "bytes=" + this._start + "-" + i2));
try {
r2.send(this._config.downloadRequestBody);
} catch (e3) {
this._chunkError(e3.message);
}
s && 0 === r2.status && this._chunkError();
}
}, this._chunkLoaded = function() {
4 === r2.readyState && (r2.status < 200 || 400 <= r2.status ? this._chunkError() : (this._start += this._config.chunkSize || r2.responseText.length, this._finished = !this._config.chunkSize || this._start >= ((e2) => null !== (e2 = e2.getResponseHeader("Content-Range")) ? parseInt(e2.substring(e2.lastIndexOf("/") + 1)) : -1)(r2), this.parseChunk(r2.responseText)));
}, this._chunkError = function(e2) {
e2 = r2.statusText || e2;
this._sendError(new Error(e2));
};
}
function l(e) {
(e = e || {}).chunkSize || (e.chunkSize = v.LocalChunkSize), u.call(this, e);
var i2, r2, n2 = "undefined" != typeof FileReader;
this.stream = function(e2) {
this._input = e2, r2 = e2.slice || e2.webkitSlice || e2.mozSlice, n2 ? ((i2 = new FileReader()).onload = y(this._chunkLoaded, this), i2.onerror = y(this._chunkError, this)) : i2 = new FileReaderSync(), this._nextChunk();
}, this._nextChunk = function() {
this._finished || this._config.preview && !(this._rowCount < this._config.preview) || this._readChunk();
}, this._readChunk = function() {
var e2 = this._input, t = (this._config.chunkSize && (t = Math.min(this._start + this._config.chunkSize, this._input.size), e2 = r2.call(e2, this._start, t)), i2.readAsText(e2, this._config.encoding));
n2 || this._chunkLoaded({ target: { result: t } });
}, this._chunkLoaded = function(e2) {
this._start += this._config.chunkSize, this._finished = !this._config.chunkSize || this._start >= this._input.size, this.parseChunk(e2.target.result);
}, this._chunkError = function() {
this._sendError(i2.error);
};
}
function c(e) {
var i2;
u.call(this, e = e || {}), this.stream = function(e2) {
return i2 = e2, this._nextChunk();
}, this._nextChunk = function() {
var e2, t;
if (!this._finished) return e2 = this._config.chunkSize, i2 = e2 ? (t = i2.substring(0, e2), i2.substring(e2)) : (t = i2, ""), this._finished = !i2, this.parseChunk(t);
};
}
function p(e) {
u.call(this, e = e || {});
var t = [], i2 = true, r2 = false;
this.pause = function() {
u.prototype.pause.apply(this, arguments), this._input.pause();
}, this.resume = function() {
u.prototype.resume.apply(this, arguments), this._input.resume();
}, this.stream = function(e2) {
this._input = e2, this._input.on("data", this._streamData), this._input.on("end", this._streamEnd), this._input.on("error", this._streamError);
}, this._checkIsFinished = function() {
r2 && 1 === t.length && (this._finished = true);
}, this._nextChunk = function() {
this._checkIsFinished(), t.length ? this.parseChunk(t.shift()) : i2 = true;
}, this._streamData = y(function(e2) {
try {
t.push("string" == typeof e2 ? e2 : e2.toString(this._config.encoding)), i2 && (i2 = false, this._checkIsFinished(), this.parseChunk(t.shift()));
} catch (e3) {
this._streamError(e3);
}
}, this), this._streamError = y(function(e2) {
this._streamCleanUp(), this._sendError(e2);
}, this), this._streamEnd = y(function() {
this._streamCleanUp(), r2 = true, this._streamData("");
}, this), this._streamCleanUp = y(function() {
this._input.removeListener("data", this._streamData), this._input.removeListener("end", this._streamEnd), this._input.removeListener("error", this._streamError);
}, this);
}
function i(m2) {
var n2, s2, a2, t, o2 = Math.pow(2, 53), h3 = -o2, u2 = /^\s*-?(\d+\.?|\.\d+|\d+\.\d+)([eE][-+]?\d+)?\s*$/, d2 = /^((\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)))$/, i2 = this, r2 = 0, f2 = 0, l2 = false, e = false, c2 = [], p2 = { data: [], errors: [], meta: {} };
function y2(e2) {
return "greedy" === m2.skipEmptyLines ? "" === e2.join("").trim() : 1 === e2.length && 0 === e2[0].length;
}
function g2() {
if (p2 && a2 && (k("Delimiter", "UndetectableDelimiter", "Unable to auto-detect delimiting character; defaulted to '" + v.DefaultDelimiter + "'"), a2 = false), m2.skipEmptyLines && (p2.data = p2.data.filter(function(e3) {
return !y2(e3);
})), _2()) {
let t2 = function(e3, t3) {
U(m2.transformHeader) && (e3 = m2.transformHeader(e3, t3)), c2.push(e3);
};
if (p2) if (Array.isArray(p2.data[0])) {
for (var e2 = 0; _2() && e2 < p2.data.length; e2++) p2.data[e2].forEach(t2);
p2.data.splice(0, 1);
} else p2.data.forEach(t2);
}
function i3(e3, t2) {
for (var i4 = m2.header ? {} : [], r4 = 0; r4 < e3.length; r4++) {
var n3 = r4, s3 = e3[r4], s3 = ((e4, t3) => ((e5) => (m2.dynamicTypingFunction && void 0 === m2.dynamicTyping[e5] && (m2.dynamicTyping[e5] = m2.dynamicTypingFunction(e5)), true === (m2.dynamicTyping[e5] || m2.dynamicTyping)))(e4) ? "true" === t3 || "TRUE" === t3 || "false" !== t3 && "FALSE" !== t3 && (((e5) => {
if (u2.test(e5)) {
e5 = parseFloat(e5);
if (h3 < e5 && e5 < o2) return 1;
}
})(t3) ? parseFloat(t3) : d2.test(t3) ? new Date(t3) : "" === t3 ? null : t3) : t3)(n3 = m2.header ? r4 >= c2.length ? "__parsed_extra" : c2[r4] : n3, s3 = m2.transform ? m2.transform(s3, n3) : s3);
"__parsed_extra" === n3 ? (i4[n3] = i4[n3] || [], i4[n3].push(s3)) : i4[n3] = s3;
}
return m2.header && (r4 > c2.length ? k("FieldMismatch", "TooManyFields", "Too many fields: expected " + c2.length + " fields but parsed " + r4, f2 + t2) : r4 < c2.length && k("FieldMismatch", "TooFewFields", "Too few fields: expected " + c2.length + " fields but parsed " + r4, f2 + t2)), i4;
}
var r3;
p2 && (m2.header || m2.dynamicTyping || m2.transform) && (r3 = 1, !p2.data.length || Array.isArray(p2.data[0]) ? (p2.data = p2.data.map(i3), r3 = p2.data.length) : p2.data = i3(p2.data, 0), m2.header && p2.meta && (p2.meta.fields = c2), f2 += r3);
}
function _2() {
return m2.header && 0 === c2.length;
}
function k(e2, t2, i3, r3) {
e2 = { type: e2, code: t2, message: i3 };
void 0 !== r3 && (e2.row = r3), p2.errors.push(e2);
}
U(m2.step) && (t = m2.step, m2.step = function(e2) {
p2 = e2, _2() ? g2() : (g2(), 0 !== p2.data.length && (r2 += e2.data.length, m2.preview && r2 > m2.preview ? s2.abort() : (p2.data = p2.data[0], t(p2, i2))));
}), this.parse = function(e2, t2, i3) {
var r3 = m2.quoteChar || '"', r3 = (m2.newline || (m2.newline = this.guessLineEndings(e2, r3)), a2 = false, m2.delimiter ? U(m2.delimiter) && (m2.delimiter = m2.delimiter(e2), p2.meta.delimiter = m2.delimiter) : ((r3 = ((e3, t3, i4, r4, n3) => {
var s3, a3, o3, h4;
n3 = n3 || [",", " ", "|", ";", v.RECORD_SEP, v.UNIT_SEP];
for (var u3 = 0; u3 < n3.length; u3++) {
for (var d3, f3 = n3[u3], l3 = 0, c3 = 0, p3 = 0, g3 = (o3 = void 0, new E({ comments: r4, delimiter: f3, newline: t3, preview: 10 }).parse(e3)), _3 = 0; _3 < g3.data.length; _3++) i4 && y2(g3.data[_3]) ? p3++ : (d3 = g3.data[_3].length, c3 += d3, void 0 === o3 ? o3 = d3 : 0 < d3 && (l3 += Math.abs(d3 - o3), o3 = d3));
0 < g3.data.length && (c3 /= g3.data.length - p3), (void 0 === a3 || l3 <= a3) && (void 0 === h4 || h4 < c3) && 1.99 < c3 && (a3 = l3, s3 = f3, h4 = c3);
}
return { successful: !!(m2.delimiter = s3), bestDelimiter: s3 };
})(e2, m2.newline, m2.skipEmptyLines, m2.comments, m2.delimitersToGuess)).successful ? m2.delimiter = r3.bestDelimiter : (a2 = true, m2.delimiter = v.DefaultDelimiter), p2.meta.delimiter = m2.delimiter), w(m2));
return m2.preview && m2.header && r3.preview++, n2 = e2, s2 = new E(r3), p2 = s2.parse(n2, t2, i3), g2(), l2 ? { meta: { paused: true } } : p2 || { meta: { paused: false } };
}, this.paused = function() {
return l2;
}, this.pause = function() {
l2 = true, s2.abort(), n2 = U(m2.chunk) ? "" : n2.substring(s2.getCharIndex());
}, this.resume = function() {
i2.streamer._halted ? (l2 = false, i2.streamer.parseChunk(n2, true)) : setTimeout(i2.resume, 3);
}, this.aborted = function() {
return e;
}, this.abort = function() {
e = true, s2.abort(), p2.meta.aborted = true, U(m2.complete) && m2.complete(p2), n2 = "";
}, this.guessLineEndings = function(e2, t2) {
e2 = e2.substring(0, 1048576);
var t2 = new RegExp(P(t2) + "([^]*?)" + P(t2), "gm"), i3 = (e2 = e2.replace(t2, "")).split("\r"), t2 = e2.split("\n"), e2 = 1 < t2.length && t2[0].length < i3[0].length;
if (1 === i3.length || e2) return "\n";
for (var r3 = 0, n3 = 0; n3 < i3.length; n3++) "\n" === i3[n3][0] && r3++;
return r3 >= i3.length / 2 ? "\r\n" : "\r";
};
}
function P(e) {
return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function E(C) {
var S = (C = C || {}).delimiter, O = C.newline, x = C.comments, I = C.step, A = C.preview, T = C.fastMode, D = null, L = false, F = null == C.quoteChar ? '"' : C.quoteChar, j = F;
if (void 0 !== C.escapeChar && (j = C.escapeChar), ("string" != typeof S || -1 < v.BAD_DELIMITERS.indexOf(S)) && (S = ","), x === S) throw new Error("Comment character same as delimiter");
true === x ? x = "#" : ("string" != typeof x || -1 < v.BAD_DELIMITERS.indexOf(x)) && (x = false), "\n" !== O && "\r" !== O && "\r\n" !== O && (O = "\n");
var z = 0, M = false;
this.parse = function(i2, t, r2) {
if ("string" != typeof i2) throw new Error("Input must be a string");
var n2 = i2.length, e = S.length, s2 = O.length, a2 = x.length, o2 = U(I), h3 = [], u2 = [], d2 = [], f2 = z = 0;
if (!i2) return b();
if (T || false !== T && -1 === i2.indexOf(F)) {
for (var l2 = i2.split(O), c2 = 0; c2 < l2.length; c2++) {
if (d2 = l2[c2], z += d2.length, c2 !== l2.length - 1) z += O.length;
else if (r2) return b();
if (!x || d2.substring(0, a2) !== x) {
if (o2) {
if (h3 = [], k(d2.split(S)), R(), M) return b();
} else k(d2.split(S));
if (A && A <= c2) return h3 = h3.slice(0, A), b(true);
}
}
return b();
}
for (var p2 = i2.indexOf(S, z), g2 = i2.indexOf(O, z), _2 = new RegExp(P(j) + P(F), "g"), m2 = i2.indexOf(F, z); ; ) if (i2[z] === F) for (m2 = z, z++; ; ) {
if (-1 === (m2 = i2.indexOf(F, m2 + 1))) return r2 || u2.push({ type: "Quotes", code: "MissingQuotes", message: "Quoted field unterminated", row: h3.length, index: z }), E2();
if (m2 === n2 - 1) return E2(i2.substring(z, m2).replace(_2, F));
if (F === j && i2[m2 + 1] === j) m2++;
else if (F === j || 0 === m2 || i2[m2 - 1] !== j) {
-1 !== p2 && p2 < m2 + 1 && (p2 = i2.indexOf(S, m2 + 1));
var y2 = v2(-1 === (g2 = -1 !== g2 && g2 < m2 + 1 ? i2.indexOf(O, m2 + 1) : g2) ? p2 : Math.min(p2, g2));
if (i2.substr(m2 + 1 + y2, e) === S) {
d2.push(i2.substring(z, m2).replace(_2, F)), i2[z = m2 + 1 + y2 + e] !== F && (m2 = i2.indexOf(F, z)), p2 = i2.indexOf(S, z), g2 = i2.indexOf(O, z);
break;
}
y2 = v2(g2);
if (i2.substring(m2 + 1 + y2, m2 + 1 + y2 + s2) === O) {
if (d2.push(i2.substring(z, m2).replace(_2, F)), w2(m2 + 1 + y2 + s2), p2 = i2.indexOf(S, z), m2 = i2.indexOf(F, z), o2 && (R(), M)) return b();
if (A && h3.length >= A) return b(true);
break;
}
u2.push({ type: "Quotes", code: "InvalidQuotes", message: "Trailing quote on quoted field is malformed", row: h3.length, index: z }), m2++;
}
}
else if (x && 0 === d2.length && i2.substring(z, z + a2) === x) {
if (-1 === g2) return b();
z = g2 + s2, g2 = i2.indexOf(O, z), p2 = i2.indexOf(S, z);
} else if (-1 !== p2 && (p2 < g2 || -1 === g2)) d2.push(i2.substring(z, p2)), z = p2 + e, p2 = i2.indexOf(S, z);
else {
if (-1 === g2) break;
if (d2.push(i2.substring(z, g2)), w2(g2 + s2), o2 && (R(), M)) return b();
if (A && h3.length >= A) return b(true);
}
return E2();
function k(e2) {
h3.push(e2), f2 = z;
}
function v2(e2) {
var t2 = 0;
return t2 = -1 !== e2 && (e2 = i2.substring(m2 + 1, e2)) && "" === e2.trim() ? e2.length : t2;
}
function E2(e2) {
return r2 || (void 0 === e2 && (e2 = i2.substring(z)), d2.push(e2), z = n2, k(d2), o2 && R()), b();
}
function w2(e2) {
z = e2, k(d2), d2 = [], g2 = i2.indexOf(O, z);
}
function b(e2) {
if (C.header && !t && h3.length && !L) {
var s3 = h3[0], a3 = {}, o3 = new Set(s3);
let n3 = false;
for (let r3 = 0; r3 < s3.length; r3++) {
let i3 = s3[r3];
if (a3[i3 = U(C.transformHeader) ? C.transformHeader(i3, r3) : i3]) {
let e3, t2 = a3[i3];
for (; e3 = i3 + "_" + t2, t2++, o3.has(e3); ) ;
o3.add(e3), s3[r3] = e3, a3[i3]++, n3 = true, (D = null === D ? {} : D)[e3] = i3;
} else a3[i3] = 1, s3[r3] = i3;
o3.add(i3);
}
n3 && console.warn("Duplicate headers found and renamed."), L = true;
}
return { data: h3, errors: u2, meta: { delimiter: S, linebreak: O, aborted: M, truncated: !!e2, cursor: f2 + (t || 0), renamedHeaders: D } };
}
function R() {
I(b()), h3 = [], u2 = [];
}
}, this.abort = function() {
M = true;
}, this.getCharIndex = function() {
return z;
};
}
function g(e) {
var t = e.data, i2 = o[t.workerId], r2 = false;
if (t.error) i2.userError(t.error, t.file);
else if (t.results && t.results.data) {
var n2 = { abort: function() {
r2 = true, _(t.workerId, { data: [], errors: [], meta: { aborted: true } });
}, pause: m, resume: m };
if (U(i2.userStep)) {
for (var s2 = 0; s2 < t.results.data.length && (i2.userStep({ data: t.results.data[s2], errors: t.results.errors, meta: t.results.meta }, n2), !r2); s2++) ;
delete t.results;
} else U(i2.userChunk) && (i2.userChunk(t.results, n2, t.file), delete t.results);
}
t.finished && !r2 && _(t.workerId, t.results);
}
function _(e, t) {
var i2 = o[e];
U(i2.userComplete) && i2.userComplete(t), i2.terminate(), delete o[e];
}
function m() {
throw new Error("Not implemented.");
}
function w(e) {
if ("object" != typeof e || null === e) return e;
var t, i2 = Array.isArray(e) ? [] : {};
for (t in e) i2[t] = w(e[t]);
return i2;
}
function y(e, t) {
return function() {
e.apply(t, arguments);
};
}
function U(e) {
return "function" == typeof e;
}
return v.parse = function(e, t) {
var i2 = (t = t || {}).dynamicTyping || false;
U(i2) && (t.dynamicTypingFunction = i2, i2 = {});
if (t.dynamicTyping = i2, t.transform = !!U(t.transform) && t.transform, !t.worker || !v.WORKERS_SUPPORTED) return i2 = null, v.NODE_STREAM_INPUT, "string" == typeof e ? (e = ((e2) => 65279 !== e2.charCodeAt(0) ? e2 : e2.slice(1))(e), i2 = new (t.download ? f : c)(t)) : true === e.readable && U(e.read) && U(e.on) ? i2 = new p(t) : (n.File && e instanceof File || e instanceof Object) && (i2 = new l(t)), i2.stream(e);
(i2 = (() => {
var e2;
return !!v.WORKERS_SUPPORTED && (e2 = (() => {
var e3 = n.URL || n.webkitURL || null, t2 = r.toString();
return v.BLOB_URL || (v.BLOB_URL = e3.createObjectURL(new Blob(["var global = (function() { if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } return {}; })(); global.IS_PAPA_WORKER=true; ", "(", t2, ")();"], { type: "text/javascript" })));
})(), (e2 = new n.Worker(e2)).onmessage = g, e2.id = h2++, o[e2.id] = e2);
})()).userStep = t.step, i2.userChunk = t.chunk, i2.userComplete = t.complete, i2.userError = t.error, t.step = U(t.step), t.chunk = U(t.chunk), t.complete = U(t.complete), t.error = U(t.error), delete t.worker, i2.postMessage({ input: e, config: t, workerId: i2.id });
}, v.unparse = function(e, t) {
var n2 = false, _2 = true, m2 = ",", y2 = "\r\n", s2 = '"', a2 = s2 + s2, i2 = false, r2 = null, o2 = false, h3 = ((() => {
if ("object" == typeof t) {
if ("string" != typeof t.delimiter || v.BAD_DELIMITERS.filter(function(e2) {
return -1 !== t.delimiter.indexOf(e2);
}).length || (m2 = t.delimiter), "boolean" != typeof t.quotes && "function" != typeof t.quotes && !Array.isArray(t.quotes) || (n2 = t.quotes), "boolean" != typeof t.skipEmptyLines && "string" != typeof t.skipEmptyLines || (i2 = t.skipEmptyLines), "string" == typeof t.newline && (y2 = t.newline), "string" == typeof t.quoteChar && (s2 = t.quoteChar), "boolean" == typeof t.header && (_2 = t.header), Array.isArray(t.columns)) {
if (0 === t.columns.length) throw new Error("Option columns is empty");
r2 = t.columns;
}
void 0 !== t.escapeChar && (a2 = t.escapeChar + s2), t.escapeFormulae instanceof RegExp ? o2 = t.escapeFormulae : "boolean" == typeof t.escapeFormulae && t.escapeFormulae && (o2 = /^[=+\-@\t\r].*$/);
}
})(), new RegExp(P(s2), "g"));
"string" == typeof e && (e = JSON.parse(e));
if (Array.isArray(e)) {
if (!e.length || Array.isArray(e[0])) return u2(null, e, i2);
if ("object" == typeof e[0]) return u2(r2 || Object.keys(e[0]), e, i2);
} else if ("object" == typeof e) return "string" == typeof e.data && (e.data = JSON.parse(e.data)), Array.isArray(e.data) && (e.fields || (e.fields = e.meta && e.meta.fields || r2), e.fields || (e.fields = Array.isArray(e.data[0]) ? e.fields : "object" == typeof e.data[0] ? Object.keys(e.data[0]) : []), Array.isArray(e.data[0]) || "object" == typeof e.data[0] || (e.data = [e.data])), u2(e.fields || [], e.data || [], i2);
throw new Error("Unable to serialize unrecognized input");
function u2(e2, t2, i3) {
var r3 = "", n3 = ("string" == typeof e2 && (e2 = JSON.parse(e2)), "string" == typeof t2 && (t2 = JSON.parse(t2)), Array.isArray(e2) && 0 < e2.length), s3 = !Array.isArray(t2[0]);
if (n3 && _2) {
for (var a3 = 0; a3 < e2.length; a3++) 0 < a3 && (r3 += m2), r3 += k(e2[a3], a3);
0 < t2.length && (r3 += y2);
}
for (var o3 = 0; o3 < t2.length; o3++) {
var h4 = (n3 ? e2 : t2[o3]).length, u3 = false, d2 = n3 ? 0 === Object.keys(t2[o3]).length : 0 === t2[o3].length;
if (i3 && !n3 && (u3 = "greedy" === i3 ? "" === t2[o3].join("").trim() : 1 === t2[o3].length && 0 === t2[o3][0].length), "greedy" === i3 && n3) {
for (var f2 = [], l2 = 0; l2 < h4; l2++) {
var c2 = s3 ? e2[l2] : l2;
f2.push(t2[o3][c2]);
}
u3 = "" === f2.join("").trim();
}
if (!u3) {
for (var p2 = 0; p2 < h4; p2++) {
0 < p2 && !d2 && (r3 += m2);
var g2 = n3 && s3 ? e2[p2] : p2;
r3 += k(t2[o3][g2], p2);
}
o3 < t2.length - 1 && (!i3 || 0 < h4 && !d2) && (r3 += y2);
}
}
return r3;
}
function k(e2, t2) {
var i3, r3;
return null == e2 ? "" : e2.constructor === Date ? JSON.stringify(e2).slice(1, 25) : (r3 = false, o2 && "string" == typeof e2 && o2.test(e2) && (e2 = "'" + e2, r3 = true), i3 = e2.toString().replace(h3, a2), (r3 = r3 || true === n2 || "function" == typeof n2 && n2(e2, t2) || Array.isArray(n2) && n2[t2] || ((e3, t3) => {
for (var i4 = 0; i4 < t3.length; i4++) if (-1 < e3.indexOf(t3[i4])) return true;
return false;
})(i3, v.BAD_DELIMITERS) || -1 < i3.indexOf(m2) || " " === i3.charAt(0) || " " === i3.charAt(i3.length - 1)) ? s2 + i3 + s2 : i3);
}
}, v.RECORD_SEP = String.fromCharCode(30), v.UNIT_SEP = String.fromCharCode(31), v.BYTE_ORDER_MARK = "\uFEFF", v.BAD_DELIMITERS = ["\r", "\n", '"', v.BYTE_ORDER_MARK], v.WORKERS_SUPPORTED = !s && !!n.Worker, v.NODE_STREAM_INPUT = 1, v.LocalChunkSize = 10485760, v.RemoteChunkSize = 5242880, v.DefaultDelimiter = ",", v.Parser = E, v.ParserHandle = i, v.NetworkStreamer = f, v.FileStreamer = l, v.StringStreamer = c, v.ReadableStreamStreamer = p, n.jQuery && ((d = n.jQuery).fn.parse = function(o2) {
var i2 = o2.config || {}, h3 = [];
return this.each(function(e2) {
if (!("INPUT" === d(this).prop("tagName").toUpperCase() && "file" === d(this).attr("type").toLowerCase() && n.FileReader) || !this.files || 0 === this.files.length) return true;
for (var t = 0; t < this.files.length; t++) h3.push({ file: this.files[t], inputElem: this, instanceConfig: d.extend({}, i2) });
}), e(), this;
function e() {
if (0 === h3.length) U(o2.complete) && o2.complete();
else {
var e2, t, i3, r2, n2 = h3[0];
if (U(o2.before)) {
var s2 = o2.before(n2.file, n2.inputElem);
if ("object" == typeof s2) {
if ("abort" === s2.action) return e2 = "AbortError", t = n2.file, i3 = n2.inputElem, r2 = s2.reason, void (U(o2.error) && o2.error({ name: e2 }, t, i3, r2));
if ("skip" === s2.action) return void u2();
"object" == typeof s2.config && (n2.instanceConfig = d.extend(n2.instanceConfig, s2.config));
} else if ("skip" === s2) return void u2();
}
var a2 = n2.instanceConfig.complete;
n2.instanceConfig.complete = function(e3) {
U(a2) && a2(e3, n2.file, n2.inputElem), u2();
}, v.parse(n2.file, n2.instanceConfig);
}
}
function u2() {
h3.splice(0, 1), e();
}
}), a && (n.onmessage = function(e) {
e = e.data;
void 0 === v.WORKER_ID && e && (v.WORKER_ID = e.workerId);
"string" == typeof e.input ? n.postMessage({ workerId: v.WORKER_ID, results: v.parse(e.input, e.config), finished: true }) : (n.File && e.input instanceof File || e.input instanceof Object) && (e = v.parse(e.input, e.config)) && n.postMessage({ workerId: v.WORKER_ID, results: e, finished: true });
}), (f.prototype = Object.create(u.prototype)).constructor = f, (l.prototype = Object.create(u.prototype)).constructor = l, (c.prototype = Object.create(c.prototype)).constructor = c, (p.prototype = Object.create(u.prototype)).constructor = p, v;
});
})(papaparse_min$1);
return papaparse_min$1.exports;
}
var papaparse_minExports = requirePapaparse_min();
const Papa = /* @__PURE__ */ getDefaultExportFromCjs(papaparse_minExports);
function useImport() {
const isImporting = ref(false);
const lastError = ref(null);
const importProgress = ref(0);
const parseFileContent = async (content, format) => {
try {
switch (format) {
case "jira":
return JSON.parse(content.toString());
case "csv":
return parseCsv(content.toString());
default:
throw new Error(`Format not supported: ${format}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error during parsing";
throw new Error(`Failed to parse file: ${errorMessage}`);
}
};
const parseCsv = (csvContent) => {
const result = Papa.parse(csvContent, {
header: true,
skipEmptyLines: true,
dynamicTyping: true,
transformHeader: (header) => {
return header.trim().toLowerCase().replace(/\s+/g, "_");
}
});
if (result.errors && result.errors.length > 0) {
const errorMessage = result.errors.map((err) => err.message).join("; ");
throw new Error(`CSV parsing errors: ${errorMessage}`);
}
return result.data;
};
const convertJiraToGantt = (data, options) => {
const warnings = [];
const issues = data.issues || [];
const issueMap = /* @__PURE__ */ new Map();
const barConfigMap = /* @__PURE__ */ new Map();
const connectionsMap = /* @__PURE__ */ new Map();
issues.forEach((issue) => {
issueMap.set(issue.id, issue);
const barConfig = {
id: `issue-${issue.key}`,
label: issue.fields.summary,
progress: calculateProgress(issue.fields.status),
connections: []
};
if (issue.fields.issuetype) {
barConfig.class = issue.fields.issuetype.name.toLowerCase().replace(/\s+/g, "-");
}
barConfigMap.set(issue.key, barConfig);
});
issues.forEach((issue) => {
if (issue.fields.issuelinks && Array.isArray(issue.fields.issuelinks)) {
issue.fields.issuelinks.forEach((link) => {
var _a, _b, _c, _d;
if (link.outwardIssue) {
const sourceKey = issue.key;
const targetKey = link.outwardIssue.key;
if (!connectionsMap.has(sourceKey)) {
connectionsMap.set(sourceKey, []);
}
const connection = {
targetId: `issue-${targetKey}`,
type: mapLinkTypeToConnectionType((_a = link.type) == null ? void 0 : _a.name)
};
(_b = connectionsMap.get(sourceKey)) == null ? void 0 : _b.push(connection);
}
if (link.inwardIssue) {
const sourceKey = link.inwardIssue.key;
const targetKey = issue.key;
if (!connectionsMap.has(sourceKey)) {
connectionsMap.set(sourceKey, []);
}
const connection = {
targetId: `issue-${targetKey}`,
type: mapLinkTypeToConnectionType((_c = link.type) == null ? void 0 : _c.name)
};
(_d = connectionsMap.get(sourceKey)) == null ? void 0 : _d.push(connection);
}
});
}
});
const childrenMap = /* @__PURE__ */ new Map();
issues.forEach((issue) => {
if (issue.fields.subtasks && Array.isArray(issue.fields.subtasks) && issue.fields.subtasks.length > 0) {
issue.fields.subtasks.forEach((subtask) => {
var _a;
const fullSubtask = issues.find((i) => i.id === subtask.id) || subtask;
if (!childrenMap.has(issue.id)) {
childrenMap.set(issue.id, []);
}
(_a = childrenMap.get(issue.id)) == null ? void 0 : _a.push(fullSubtask);
});
}
});
issues.forEach((issue) => {
var _a;
if (issue.fields.parent && issue.fields.parent.id) {
const parentId = issue.fields.parent.id;
const isExplicitSubtask = issues.some(
(parentIssue) => {
var _a2;
return parentIssue.id === parentId && ((_a2 = parentIssue.fields.subtasks) == null ? void 0 : _a2.some((st) => st.id === issue.id));
}
);
if (!isExplicitSubtask) {
if (!childrenMap.has(parentId)) {
childrenMap.set(parentId, []);
}
(_a = childrenMap.get(parentId)) == null ? void 0 : _a.push(issue);
}
}
});
const rootIssues = issues.filter(
(issue) => !issue.fields.parent || !issueMap.has(issue.fields.parent.id)
);
const buildRows = (issues2) => {
return issues2.map((issue) => {
var _a, _b;
const { fields } = issue;
let startDate = fields.created;
let endDate = fields.duedate || fields.updated;
if (!endDate || !startDate) {
warnings.push(`Issue "${fields.summary}" has missing date information`);
startDate = dayjs().format("YYYY-MM-DD");
endDate = dayjs().add(7, "day").format("YYYY-MM-DD");
}
const barConfig = barConfigMap.get(issue.key) || {
id: `issue-${issue.key}`,
label: fields.summary,
progress: calculateProgress(fields.status),
connections: []
};
const connections = connectionsMap.get(issue.key) || [];
barConfig.connections = connections;
const barObject = {
[((_a = options.mapFields) == null ? void 0 : _a.startDate) || "start"]: startDate,
[((_b = options.mapFields) == null ? void 0 : _b.endDate) || "end"]: endDate,
ganttBarConfig: barConfig
};
const chartRow = {
id: issue.id,
label: fields.summary,
bars: [barObject]
};
const children = childrenMap.get(issue.id) || [];
if (children.length > 0) {
chartRow.children = buildRows(children);
}
return chartRow;
});
};
return {
rows: buildRows(rootIssues),
warnings
};
};
function calculateProgress(status) {
if (!status) return 0;
switch (status.name.toLowerCase()) {
case "to do":
case "open":
case "nuovo":
return 0;
case "in progress":
case "in corso":
return 50;
case "done":
case "closed":
case "resolved":
case "completato":
return 100;
default:
if (status.name.toLowerCase().includes("progress")) {
return 50;
}
return 0;
}
}
function mapLinkTypeToConnectionType(linkType) {
if (!linkType) return "straight";
const lowerType = linkType.toLowerCase();
if (lowerType.includes("block")) {
return "squared";
}
if (lowerType.includes("depend") || lowerType.includes("relate")) {
return "bezier";
}
return "straight";
}
const ensureDateHasTime = (dateStr) => {
if (!dateStr) return dayjs().format("YYYY-MM-DD HH:mm:ss");
const date = dayjs(dateStr);
if (!date.isValid()) {
return dayjs().format("YYYY-MM-DD HH:mm:ss");
}
const hasTimeComponent = date.hour() !== 0 || date.minute() !== 0 || date.second() !== 0 || /\d{1,2}[:hHT]/.test(dateStr);
return hasTimeComponent ? date.format("YYYY-MM-DD HH:mm:ss") : `${date.format("YYYY-MM-DD")} 00:00:00`;
};
const convertSpreadsheetToGantt = (data, options) => {
const warnings = [];
const fieldMap = {
id: ["id", "taskid", "task_id", "key"],
name: ["name", "task", "taskname", "task_name", "summary", "title"],
startDate: ["start", "startdate", "start_date", "begins", "begin_date"],
endDate: [
"end",
"enddate",
"end_date",
"finish",
"finishdate",
"finish_date",
"due",
"duedate",
"due_date"
],
progress: ["progress", "percent", "completion", "complete", "percent_complete"],
parentId: ["parent", "parentid", "parent_id", "parent_task"],
dependencies: ["dependencies", "depends", "predecessors", "links"],
milestone: ["milestone", "is_milestone", "ismilestone"],
...options.mapFields
};
const normalizeFieldName = (row, fieldOptions) => {
const keys = Object.keys(row);
for (const option of fieldOptions) {
const matchingKey = keys.find((k) => k.toLowerCase() === option.toLowerCase());
if (matchingKey !== void 0) {
return matchingKey;
}
}
return void 0;
};
const rowsById = /* @__PURE__ */ new Map();
data.forEach((row) => {
const idField = normalizeFieldName(row, fieldMap.id);
const id = idField ? row[idField] : void 0;
if (!id) {
warnings.push(`Row is missing an ID field: ${JSON.stringify(row)}`);
return;
}
rowsById.set(id, { ...row, children: [], barConfig: { id: `task-${id}`, connections: [] } });
});
data.forEach((row) => {
const idField = normalizeFieldName(row, fieldMap.id);
const id = idField ? row[idField] : void 0;
if (!id) return;
const parentField = normalizeFieldName(row, fieldMap.parentId);
const parentId = parentField ? row[parentField] : void 0;
if (parentId && rowsById.has(parentId)) {
const parent = rowsById.get(parentId);
if (parent) {
parent.children.push(id);
}
}
const dependenciesField = normalizeFieldName(row, fieldMap.dependencies);
if (dependenciesField && row[dependenciesField]) {
const dependencies = String(row[dependenciesField]).split(/[,;]\s*/);
dependencies.forEach((depId) => {
const depIdAsNumber = Number(depId);
const depIdAsString = String(depId);
const dependencyExists = rowsById.has(depIdAsNumber) || rowsById.has(depIdAsString);
const dependencyRow = rowsById.get(depIdAsNumber) || rowsById.get(depIdAsString);
if (dependencyExists && dependencyRow) {
if (dependencyRow.barConfig) {
dependencyRow.barConfig.connections = dependencyRow.barConfig.connections || [];
dependencyRow.barConfig.connections.push({
targetId: `task-${id}`,
type: "straight"
});
}
} else {
warnings.push(`Task ${id} refers to a dependency ${depId} that doesn't exist`);
}
});
}
});
const rootIds = Array.from(rowsById.keys()).filter((id) => {
const row = rowsById.get(id);
if (!row) return false;
const parentField = normalizeFieldName(row, fieldMap.parentId);
const parentId = parentField ? row[parentField] : void 0;
return !parentId || !rowsById.has(parentId);
});
const buildRows = (ids) => {
return ids.map((id) => {
var _a, _b;
const row = rowsById.get(id);
if (!row) {
warnings.push(`Referenced ID ${id} not found in data`);
return {
id: String(id),
label: `Unknown Task (${id})`,
bars: []
};
}
const nameField = normalizeFieldName(row, fieldMap.name);
const name = nameField ? String(row[nameField] || "") : `Task ${id}`;
let startDateField = normalizeFieldName(row, fieldMap.startDate);
if (!startDateField && "start_date" in row) {
startDateField = "start_date";
}
let startDate = startDateField ? row[startDateField] : void 0;
let endDateField = normalizeFieldName(row, fieldMap.endDate);
if (!endDateField && "end_date" in row) {
endDateField = "end_date";
}
let endDate = endDateField ? row[endDateField] : void 0;
const progressField = normalizeFieldName(row, fieldMap.progress);
let progress = progressField ? row[progressField] : void 0;
if (progress !== void 0) {
if (typeof progress === "string") {
progress = parseFloat(progress.replace("%", ""));
}
progress = Math.max(0, Math.min(100, Number(progress)));
if (isNaN(progress)) {
progress = 0;
}
}
const milestoneField = normalizeFieldName(row, fieldMap.milestone);
const isMilestone = milestoneField ? row[milestoneField] === true || row[milestoneField] === "true" || row[milestoneField] === 1 : false;
let start = startDate ? ensureDateHasTime(String(startDate)) : dayjs().format("YYYY-MM-DD HH:mm:ss");
let end = endDate ? ensureDateHasTime(String(endDate)) : dayjs().add(1, "day").format("YYYY-MM-DD HH:mm:ss");
if (isMilestone) {
end = start;
}
const barConfig = {
...row.barConfig,
id: `task-${id}`,
label: name,
progress,
immobile: false
};
if (isMilestone) {
barConfig.class = "milestone";
}
const barObject = {
[((_a = options.mapFields) == null ? void 0 : _a.startDate) || "start"]: start,
[((_b = options.mapFields) == null ? void 0 : _b.endDate) || "end"]: end,
ganttBarConfig: barConfig
};
const chartRow = {
id,
label: name,
bars: [barObject]
};
if (row.children.length > 0) {
chartRow.children = buildRows(row.children);
}
return chartRow;
});
};
return {
rows: buildRows(rootIds),
warnings
};
};
const importFromFile = async (file, options) => {
isImporting.value = true;
importProgress.value = 0;
lastError.value = null;
try {
let format = options.format;
if (!format) {
format = detectFormatFromFile(file);
}
const content = await readFileContent(file);
importProgress.value = 20;
const parsedData = await parseFileContent(content, format);
importProgress.value = 50;
const { rows, warnings, chartStart, chartEnd } = await convertToGantt(
parsedData,
format,
options
);
importProgress.value = 80;
if (!options.skipValidation) {
validateImportedData(rows, warnings);
}
importProgress.value = 100;
if (options.onProgress) {
options.onProgress(100);
}
return {
success: true,
data: {
rows,
chartStart,
chartEnd
},
warnings
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error during import";
lastError.value = errorMessage;
return {
success: false,
error: errorMessage
};
} finally {
isImporting.value = false;
}
};
const detectFormatFromFile = (file) => {
const fileName = file.name.toLowerCase();
if (fileName.endsWith(".json")) {
return "jira";
} else if (fileName.endsWith(".csv")) {
return "csv";
}
throw new Error(`Could not determine format from file: ${file.name}`);
};
const readFileContent = async (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
var _a;
if ((_a = e.target) == null ? void 0 : _a.result) {
resolve(e.target.result);
} else {
reject(new Error("Failed to read file"));
}
};
reader.onerror = () => {
reject(new Error("Error reading file"));
};
reader.readAsText(file);
});
};
const convertToGantt = async (data, format, options) => {
let result;
switch (format) {
case "jira":
result = convertJiraToGantt(data, options);
break;
case "csv":
result = convertSpreadsheetToGantt(data, options);
break;
default:
throw new Error(`Conversion not implemented for format: ${format}`);
}
const { chartStart, chartEnd } = calculateChartDateRange(result.rows);
return {
...result,
chartStart,
chartEnd
};
};
const calculateChartDateRange = (rows) => {
let minDate = null;
let maxDate = null;
const processRows = (rows2) => {
rows2.forEach((row) => {
row.bars.forEach((bar) => {
const startDate = dayjs(bar.start || bar.begin || bar.startDate);
const endDate = dayjs(bar.end || bar.finish || bar.endDate);
if (!minDate || startDate.isBefore(minDate)) {
minDate = startDate;
}
if (!maxDate || endDate.isAfter(maxDate)) {
maxDate = endDate;
}
});
if (row.children) {
processRows(row.children);
}
});
};
processRows(rows);
if (minDate && maxDate) {
const rangeDays = maxDate.diff(minDate, "day");
const buffer = Math.max(1, Math.ceil(rangeDays * 0.1));
return {
chartStart: minDate.subtract(buffer, "day").toDate(),
chartEnd: maxDate.add(buffer, "day").toDate()
};
}
return {};
};
const validateImportedData = (rows, warnings) => {
const validateRow = (row, path) => {
if (!row.id) {
warnings.push(`Row at ${path} is missing an ID`);
}
if (!row.label) {
warnings.push(`Row at ${path} is missing a label`);
}
if (!row.bars || row.bars.length === 0) {
warnings.push(`Row at ${path} has no bars`);
} else {
row.bars.forEach((bar, i) => {
if (!bar.ganttBarConfig.id) {
warnings.push(`Bar ${i} at ${path} is missing an ID`);
}
if (bar.ganttBarConfig.connections) {
bar.ganttBarConfig.connections.forEach((conn, j) => {
if (!conn.targetId) {
warnings.push(`Connection ${j} in bar ${i} at ${path} is missing a target ID`);
}
});
}
});
}
if (row.children) {
row.children.forEach((child, i) => {
validateRow(child, `${path} > child[${i}]`);
});
}
};
rows.forEach((row, i) => {
validateRow(row, `root[${i}]`);
});
};
return {
importFromFile,
isImporting,
importProgress,
lastError
};
}
const colorSchemes = {
default: {
primary: "#eeeeee",
secondary: "#E0E0E0",
ternary: "#F5F5F5",
quartenary: "#ededed",
hoverHighlight: "rgba(204, 216, 219, 0.5)",
markerCurrentTime: "#000",
markerPointer: "#000",
text: "#404040",
background: "white",
commands: "#eeeeee",
rangeHighlight: "#000",
holidayHighlight: "rgba(240, 120, 96, 0.8)",
barContainer: "rgba(0, 0, 0, 0.7)",
rowContainer: "rgba(255, 255, 255, 1)",
gridAndBorder: "#eaeaea"
},
creamy: {
primary: "#ffe8d9",
secondary: "#fcdcc5",
ternary: "#fff6f0",
quartenary: "#f7ece6",
hoverHighlight: "rgba(230, 221, 202, 0.5)",
markerCurrentTime: "#000",
markerPointer: "#000",
text: "#542d05",
background: "white",
commands: "white",
rangeHighlight: "#000",
holidayHighlight: "rgba(240, 120, 96, 0.8)",
barContainer: "#fcdcc5",
rowContainer: "rgba(255, 255, 255, 1)",
gridAndBorder: "#eaeaea"
},
crimson: {
primary: "#a82039",
secondary: "#c41238",
ternary: "#db4f56",
quartenary: "#ce5f64",
hoverHighlight: "rgba(196, 141, 141, 0.5)",
markerCurrentTime: "#000",
markerPointer: "#000",
text: "black",
background: "white",
commands: "white",
rangeHighlight: "#000",
holidayHighlight: "rgba(240, 120, 96, 0.8)",
barContainer: "#c41238",
rowContainer: "rgba(255, 255, 255, 1)",
gridAndBorder: "#eaeaea"
},
dark: {
primary: "#404040",
secondary: "#303030",
ternary: "#353535",
quartenary: "#383838",
hoverHighlight: "rgba(159, 160, 161, 0.5)",
markerCurrentTime: "#fff",
markerPointer: "#fff",
text: "white",
background: "#525252",
toast: "#1f1f1f",
commands: "#525252",
rangeHighlight: "#000",
holidayHighlight: "#ff9d3b",
barContainer: "rgba(0, 0, 0, 1)",
rowContainer: "rgba(0, 0, 0, 1)",
gridAndBorder: "#eaeaea"
},
flare: {
primary: "#e08a38",
secondary: "#e67912",
ternary: "#5e5145",
quartenary: "#665648",
hoverHighlight: "rgba(196, 141, 141, 0.5)",
markerCurrentTime: "#000",
markerPointer: "#000",
text: "white",
background: "white",
rangeHighlight: "#000",
holidayHighlight: "rgba(240, 120, 96, 0.8)",
barContainer: "rgba(0, 0, 0, 1)",
rowContainer: "rgba(0, 0, 0, 1)",
gridAndBorder: "#eaeaea"
},
fuchsia: {
primary: "#de1d5a",
secondary: "#b50b41",
ternary: "#ff7da6",
quartenary: "#f2799f",
hoverHighlight: "rgba(196, 141, 141, 0.5)",
markerCurrentTime: "#000",
markerPointer: "#000",
text: "white",
background: "white",
commands: "white",
rangeHighlight: "#000",
holidayHighlight: "rgba(240, 120, 96, 0.8)",
barContainer: "#de1d5a",
rowContainer: "rgba(0, 0, 0, 1)",
gridAndBorder: "#eaeaea"
},
grove: {
primary: "#3d9960",
secondary: "#288542",
ternary: "#72b585",
quartenary: "#65a577",
hoverHighlight: "rgba(160, 219, 171, 0.5)",
markerCurrentTime: "#000",
markerPointer: "#000",
text: "white",
background: "white",
commands: "white",
rangeHighlight: "#000",
holidayHighlight: "rgba(240, 120, 96, 0.8)",
barContainer: "#3d9960",
rowContainer: "rgba(0, 0, 0, 1)",
gridAndBorder: "#eaeaea"
},
"material-blue": {
primary: "#0D47A1",
secondary: "#1565C0",
ternary: "#42a5f5",
quartenary: "#409fed",
hoverHighlight: "rgba(110, 165, 196, 0.5)",
markerCurrentTime: "#000",
markerPointer: "#000",
text: "white",
background: "white",
commands: "white",
rangeHighlight: "#000",
holidayHighlight: "rgba(240, 120, 96, 0.8)",
barContainer: "#0D47A1",
rowContainer: "rgba(0, 0, 0, 1)",
gridAndBorder: "#eaeaea"
},
sky: {
primary: "#b5e3ff",
secondary: "#a1d6f7",
ternary: "#d6f7ff",
quartenary: "#d0edf4",
hoverHighlight: "rgba(193, 202, 214, 0.5)",
markerCurrentTime: "#000",
markerPointer: "#000",
text: "#022c47",
background: "white",
commands: "white",
rangeHighlight: "#000",
holidayHighlight: "rgba(240, 120, 96, 0.8)",
barContainer: "rgb(20,20,20)",
rowContainer: "#a1d6f7",
gridAndBorder: "#eaeaea"
},
slumber: {
primary: "#2a2f42",
secondary: "#2f3447",
ternary: "#35394d",
quartenary: "#2c3044",
hoverHighlight: "rgba(179, 162, 127, 0.5)",
markerCurrentTime: "#fff",
markerPointer: "#fff",
text: "#ffe0b3",
background: "#38383b",
toast: "#1f1f1f",
commands: "#38383b",
rangeHighlight: "#000",
holidayHighlight: "rgba(240, 120, 96, 0.8)",
barContainer: "#2a2f42",
rowContainer: "rgba(0, 0, 0, 1)",
gridAndBorder: "#eaeaea"
},
vue: {
primary: "#258a5d",
secondary: "#41B883",
ternary: "#35495E",
quartenary: "#2a3d51",
hoverHighlight: "rgba(160, 219, 171, 0.5)",
markerCurrentTime: "#000",
markerPointer: "#000",
text: "white",
background: "white",
commands: "white",
rangeHighlight: "#000",
holidayHighlight: "#f7842d",
barContainer: "#258a5d",
rowContainer: "rgba(0, 0, 0, 1)",
gridAndBorder: "#eaeaea"
}
};
const _hoisted_1$1 = {
key: 0,
class: "g-gantt-importer-overlay"
};
const _hoisted_2$1 = { class: "g-gantt-importer-content" };
const _hoisted_3$1 = { class: "g-gantt-importer-stepper" };
const _hoisted_4$1 = { key: 0 };
const _hoisted_5$1 = { key: 0 };
const _hoisted_6$1 = { key: 0 };
const _hoisted_7$1 = {
key: 0,
class: "g-gantt-importer-step"
};
const _hoisted_8$1 = { class: "g-gantt-file-upload" };
const _hoisted_9$1 = ["accept"];
const _hoisted_10$1 = {
key: 1,
class: "g-gantt-importer-step"
};
const _hoisted_11$1 = {
key: 0,
class: "g-gantt-selected-file"
};
const _hoisted_12$1 = { class: "g-gantt-import-options" };
const _hoisted_13$1 = { class: "g-gantt-option-group" };
const _hoisted_14$1 = ["value"];
const _hoisted_15$1 = { class: "g-gantt-option-toggle" };
const _hoisted_16$1 = { class: "g-gantt-option-group" };
const _hoisted_17$1 = { class: "g-gantt-option-group" };
const _hoisted_18$1 = { class: "g-gantt-import-actions" };
const _hoisted_19$1 = ["disabled"];
const _hoisted_20$1 = { key: 1 };
const _hoisted_21$1 = {
key: 2,
class: "g-gantt-importer-step"
};
const _hoisted_22$1 = { class: "g-gantt-import-result" };
const _hoisted_23$1 = {
key: 0,
class: "g-gantt-result-success"
};
const _hoisted_24$1 = { style: { color: "#aa8700" } };
const _hoisted_25$1 = {
key: 1,
class: "g-gantt-result-error"
};
const _hoisted_26$1 = { class: "g-gantt-result-actions" };
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
__name: "GGanttImporter",
props: {
modelValue: { type: Boolean },
title: {},
defaultFormat: {},
allowedFormats: {}
},
emits: ["update:modelValue", "import", "close"],
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const config = inject(CONFIG_KEY);
if (!config) {
throw Error("GGanttImporter must be used as a child of GGanttChart!");
}
const dateFormat = computed(() => config.dateFormat.value);
const font = computed(() => config.font.value);
const colorScheme = computed(() => config.colorScheme.value);
const barStart = computed(() => config.barStart.value);
const barEnd = computed(() => config.barEnd.value);
const { importFromFile, isImporting, importProgress, lastError } = useImport();
const visible = ref(props.modelValue || false);
const selectedFormat = ref(props.defaultFormat || "csv");
const selectedFile = ref(null);
const fileInput = ref(null);
const mapStartField = ref(barStart.value);
const mapEndField = ref(barEnd.value);
const importWarnings = ref([]);
const importSuccess = ref(null);
const showOptions = ref(false);
const activeStep = ref(1);
const fontStyle = computed(() => font.value || "inherit");
const colors = computed(() => {
if (typeof colorScheme.value === "string") {
return colorSchemes[colorScheme.value] || colorSchemes.default;
}
return colorScheme.value || colorSchemes.default;
});
const textStyle = computed(() => colors.value.text);
const backgroundStyle = computed(() => colors.value.background);
const primaryStyle = computed(() => colors.value.primary);
const secondaryStyle = computed(() => colors.value.secondary);
const tertiaryStyle = computed(() => colors.value.ternary);
const accentColor = computed(() => colors.value.rangeHighlight || "#4a9eff");
const successColor = computed(() => "#4caf50");
const errorColor = computed(() => "#f44336");
const warningColor = computed(() => "#ffc107");
const availableFormats = computed(() => {
const formats = [
{ value: "jira", label: "Jira (JSON)" },
{ value: "csv", label: "CSV" }
];
if (props.allowedFormats && props.allowedFormats.length > 0) {
return formats.filter((f) => props.allowedFormats.includes(f.value));
}
return formats;
});
watch(
() => props.modelValue,
(newVal) => {
visible.value = newVal || false;
}
);
watch(visible, (newVal) => {
emit("update:modelValue", newVal);
});
watch(barStart, (newVal) => {
if (newVal) {
mapStartField.value = newVal;
}
});
watch(barEnd, (newVal) => {
if (newVal) {
mapEndField.value = newVal;
}
});
const triggerFileInput = () => {
var _a;
(_a = fileInput.value) == null ? void 0 : _a.click();
};
const handleFileSelect = (event) => {
const input = event.target;
if (input.files && input.files.length > 0) {
selectedFile.value = input.files[0];
const fileName = selectedFile.value.name.toLowerCase();
if (fileName.endsWith(".json")) {
selectedFormat.value = "jira";
} else if (fileName.endsWith(".csv")) {
selectedFormat.value = "csv";
}
activeStep.value = 2;
}
};
const startImport = async () => {
if (!selectedFile.value) return;
importWarnings.value = [];
importSuccess.value = null;
const options = {
format: selectedFormat.value,
dateFormat: dateFormat.value,
mapFields: {
startDate: mapStartField.value,
endDate: mapEndField.value
},
colorScheme: colors.value,
onProgress: (progress) => {
importProgress.value = progress;
}
};
const result = await importFromFile(selectedFile.value, options);
if (result.success) {
importSuccess.value = true;
if (result.warnings) {
importWarnings.value = result.warnings;
}
activeStep.value = 3;
emit("import", result);
} else {
importSuccess.value = false;
}
};
const reset = () => {
selectedFile.value = null;
importSuccess.value = null;
importWarnings.value = [];
activeStep.value = 1;
if (fileInput.value) {
fileInput.value.value = "";
}
};
const close = () => {
visible.value = false;
reset();
emit("close");
};
return (_ctx, _cache) => {
return openBlock(), createBlock(Teleport, { to: "body" }, [
createVNode(Transition, { name: "g-fade" }, {
default: withCtx(() => [
visible.value ? (openBlock(), createElementBlock("div", _hoisted_1$1, [
createElementVNode("div", {
class: "g-gantt-importer-modal",
style: normalizeStyle({
fontFamily: fontStyle.value,
color: textStyle.value,
background: backgroundStyle.value
})
}, [
createElementVNode("div", {
class: "g-gantt-importer-header",
style: normalizeStyle({ background: primaryStyle.value })
}, [
createElementVNode("h3", null, toDisplayString(_ctx.title || "Import Data"), 1),
createElementVNode("button", {
class: "g-gantt-importer-close-btn",
onClick: close,
style: normalizeStyle({ color: textStyle.value })
}, [
createVNode(unref(FontAwesomeIcon), { icon: unref(faXmark) }, null, 8, ["icon"])
], 4)
], 4),
createElementVNode("div", _hoisted_2$1, [
createElementVNode("div", _hoisted_3$1, [
createElementVNode("div", {
class: normalizeClass(["g-gantt-stepper-step", { active: activeStep.value === 1, completed: activeStep.value > 1 }])
}, [
createElementVNode("div", {
class: "g-gantt-step-number",
style: normalizeStyle({
backgroundColor: activeStep.value === 1 ? accentColor.value : activeStep.value > 1 ? successColor.value : "#e0e0e0",
color: activeStep.value >= 1 ? "white" : textStyle.value
})
}, [
activeStep.value <= 1 ? (openBlock(), createElementBlock("span", _hoisted_4$1, "1")) : (openBlock(), createBlock(unref(FontAwesomeIcon), {
key: 1,
icon: unref(faCheck)
}, null, 8, ["icon"]))
], 4),
createElementVNode("div", {
class: "g-gantt-step-label",
style: normalizeStyle({ color: activeStep.value === 1 ? textStyle.value : secondaryStyle.value })
}, " Select File ", 4)
], 2),
createElementVNode("div", {
class: "g-gantt-step-connector",
style: normalizeStyle({ backgroundColor: colors.value.gridAndBorder })
}, null, 4),
createElementVNode("div", {
class: normalizeClass(["g-gantt-stepper-step", { active: activeStep.value === 2, completed: activeStep.value > 2 }])
}, [
createElementVNode("div", {
class: "g-gantt-step-number",
style: normalizeStyle({
backgroundColor: activeStep.value === 2 ? accentColor.value : activeStep.value > 2 ? successColor.value : "#e0e0e0",
color: activeStep.value >= 2 ? "white" : textStyle.value
})
}, [
activeStep.value <= 2 ? (openBlock(), createElementBlock("span", _hoisted_5$1, "2")) : (openBlock(), createBlock(unref(FontAwesomeIcon), {
key: 1,
icon: unref(faCheck)
}, null, 8, ["icon"]))
], 4),
createElementVNode("div", {
class: "g-gantt-step-label",
style: normalizeStyle({ color: activeStep.value === 2 ? textStyle.value : secondaryStyle.value })
}, " Configure ", 4)
], 2),
createElementVNode("div", {
class: "g-gantt-step-connector",
style: normalizeStyle({ backgroundColor: colors.value.gridAndBorder })
}, null, 4),
createElementVNode("div", {
class: normalizeClass(["g-gantt-stepper-step", { active: activeStep.value === 3, completed: activeStep.value > 3 }])
}, [
createElementVNode("div", {
class: "g-gantt-step-number",
style: normalizeStyle({
backgroundColor: activeStep.value === 3 ? accentColor.value : activeStep.value > 3 ? successColor.value : "#e0e0e0",
color: activeStep.value >= 3 ? "white" : textStyle.value
})
}, [
activeStep.value <= 3 ? (openBlock(), createElementBlock("span", _hoisted_6$1, "3")) : (openBlock(), createBlock(unref(FontAwesomeIcon), {
key: 1,
icon: unref(faCheck)
}, null, 8, ["icon"]))
], 4),
createElementVNode("div", {
class: "g-gantt-step-label",
style: normalizeStyle({ color: activeStep.value === 3 ? textStyle.value : secondaryStyle.value })
}, " Result ", 4)
], 2)
]),
activeStep.value === 1 ? (openBlock(), createElementBlock("div", _hoisted_7$1, [
createElementVNode("div", _hoisted_8$1, [
createElementVNode("div", {
class: "g-gantt-file-dropzone",
onClick: triggerFileInput,
onDragover: _cache[0] || (_cache[0] = withModifiers(() => {
}, ["prevent"])),
onDrop: _cache[1] || (_cache[1] = withModifiers(
(e) => {
var _a;
const files = (_a = e.dataTransfer) == null ? void 0 : _a.files;
if (files && files.length > 0) {
selectedFile.value = files[0];
activeStep.value = 2;
}
},
["prevent"]
)),
style: normalizeStyle({
borderColor: colors.value.gridAndBorder
})
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faFileImport),
class: "file-icon",
style: normalizeStyle({ color: accentColor.value })
}, null, 8, ["icon", "style"]),
_cache[7] || (_cache[7] = createElementVNode("p", null, "Click or drag a file here to import", -1)),
createElementVNode("small", null, "Supported formats: " + toDisplayString(availableFormats.value.map((f) => f.label).join(", ")), 1)
], 36),
createElementVNode("input", {
type: "file",
ref_key: "fileInput",
ref: fileInput,
onChange: handleFileSelect,
class: "g-gantt-file-input",
accept: availableFormats.value.map((f) => {
switch (f.value) {
case "jira":
return ".json";
case "csv":
return ".csv";
default:
return "";
}
}).filter(Boolean).join(",")
}, null, 40, _hoisted_9$1)
])
])) : activeStep.value === 2 ? (openBlock(), createElementBlock("div", _hoisted_10$1, [
selectedFile.value ? (openBlock(), createElementBlock("div", _hoisted_11$1, [
createElementVNode("div", {
class: "g-gantt-file-info",
style: normalizeStyle({ backgroundColor: tertiaryStyle.value })
}, [
createElementVNode("div", null, [
_cache[8] || (_cache[8] = createElementVNode("strong", null, "Selected file:", -1)),
createTextVNode(" " + toDisplayString(selectedFile.value.name), 1)
]),
createElementVNode("div", null, [
_cache[9] || (_cache[9] = createElementVNode("strong", null, "Size:", -1)),
createTextVNode(" " + toDisplayString((selectedFile.value.size / 1024).toFixed(2)) + " KB", 1)
])
], 4),
createElementVNode("div", _hoisted_12$1, [
createElementVNode("div", _hoisted_13$1, [
_cache[10] || (_cache[10] = createElementVNode("label", null, "Format", -1)),
withDirectives(createElementVNode("select", {
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => selectedFormat.value = $event),
style: normalizeStyle({ borderColor: colors.value.gridAndBorder })
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(availableFormats.value, (format) => {
return openBlock(), createElementBlock("option", {
key: format.value,
value: format.value
}, toDisplayString(format.label), 9, _hoisted_14$1);
}), 128))
], 4), [
[vModelSelect, selectedFormat.value]
])
]),
createElementVNode("div", _hoisted_15$1, [
createElementVNode("button", {
class: "g-gantt-toggle-btn",
onClick: _cache[3] || (_cache[3] = ($event) => showOptions.value = !showOptions.value),
style: normalizeStyle({ color: accentColor.value })
}, toDisplayString(showOptions.value ? "Hide advanced options" : "Show advanced options"), 5)
]),
showOptions.value ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-gantt-advanced-options",
style: normalizeStyle({ borderTopColor: colors.value.gridAndBorder })
}, [
createElementVNode("div", _hoisted_16$1, [
_cache[11] || (_cache[11] = createElementVNode("label", null, "Start date field", -1)),
withDirectives(createElementVNode("input", {
type: "text",
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => mapStartField.value = $event),
style: normalizeStyle({ borderColor: colors.value.gridAndBorder })
}, null, 4), [
[vModelText, mapStartField.value]
])
]),
createElementVNode("div", _hoisted_17$1, [
_cache[12] || (_cache[12] = createElementVNode("label", null, "End date field", -1)),
withDirectives(createElementVNode("input", {
type: "text",
"onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => mapEndField.value = $event),
style: normalizeStyle({ borderColor: colors.value.gridAndBorder })
}, null, 4), [
[vModelText, mapEndField.value]
])
])
], 4)) : createCommentVNode("", true)
]),
createElementVNode("div", _hoisted_18$1, [
createElementVNode("button", {
class: "g-gantt-button secondary",
onClick: _cache[6] || (_cache[6] = ($event) => activeStep.value = 1),
style: normalizeStyle({
backgroundColor: secondaryStyle.value,
color: textStyle.value
})
}, " Back ", 4),
createElementVNode("button", {
class: "g-gantt-button primary",
onClick: startImport,
disabled: unref(isImporting),
style: normalizeStyle({
backgroundColor: accentColor.value,
color: "white"
})
}, [
unref(isImporting) ? (openBlock(), createBlock(unref(FontAwesomeIcon), {
key: 0,
icon: unref(faSpinner),
class: "fa-spin"
}, null, 8, ["icon"])) : (openBlock(), createElementBlock("span", _hoisted_20$1, "Import"))
], 12, _hoisted_19$1)
])
])) : createCommentVNode("", true)
])) : activeStep.value === 3 ? (openBlock(), createElementBlock("div", _hoisted_21$1, [
createElementVNode("div", _hoisted_22$1, [
importSuccess.value ? (openBlock(), createElementBlock("div", _hoisted_23$1, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faCheck),
class: "result-icon success",
style: normalizeStyle({
color: successColor.value,
backgroundColor: `rgba(${parseInt(successColor.value.slice(1, 3), 16)},
${parseInt(successColor.value.slice(3, 5), 16)},
${parseInt(successColor.value.slice(5, 7), 16)}, 0.1)`
})
}, null, 8, ["icon", "style"]),
_cache[13] || (_cache[13] = createElementVNode("h4", null, "Import completed successfully", -1)),
importWarnings.value.length > 0 ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-gantt-warnings",
style: normalizeStyle({
backgroundColor: `rgba(${parseInt(warningColor.value.slice(1, 3), 16)},
${parseInt(warningColor.value.slice(3, 5), 16)},
${parseInt(warningColor.value.slice(5, 7), 16)}, 0.1)`,
borderLeftColor: warningColor.value
})
}, [
createElementVNode("h5", _hoisted_24$1, [
createVNode(unref(FontAwesomeIcon), { icon: unref(faExclamationTriangle) }, null, 8, ["icon"]),
createTextVNode(" Warnings (" + toDisplayString(importWarnings.value.length) + ") ", 1)
]),
createElementVNode("ul", null, [
(openBlock(true), createElementBlock(Fragment, null, renderList(importWarnings.value, (warning, index) => {
return openBlock(), createElementBlock("li", {
key: index,
style: { color: "#aa8700" }
}, toDisplayString(warning), 1);
}), 128))
])
], 4)) : createCommentVNode("", true)
])) : importSuccess.value === false ? (openBlock(), createElementBlock("div", _hoisted_25$1, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faXmark),
class: "result-icon error",
style: normalizeStyle({
color: errorColor.value,
backgroundColor: `rgba(${parseInt(errorColor.value.slice(1, 3), 16)},
${parseInt(errorColor.value.slice(3, 5), 16)},
${parseInt(errorColor.value.slice(5, 7), 16)}, 0.1)`
})
}, null, 8, ["icon", "style"]),
_cache[14] || (_cache[14] = createElementVNode("h4", null, "Error during import", -1)),
createElementVNode("p", null, toDisplayString(unref(lastError)), 1)
])) : createCommentVNode("", true),
createElementVNode("div", _hoisted_26$1, [
createElementVNode("button", {
class: "g-gantt-button secondary",
onClick: reset,
style: normalizeStyle({
backgroundColor: secondaryStyle.value,
color: textStyle.value
})
}, " Import another file ", 4),
createElementVNode("button", {
class: "g-gantt-button primary",
onClick: close,
style: normalizeStyle({
backgroundColor: accentColor.value,
color: "white"
})
}, " Close ", 4)
])
])
])) : createCommentVNode("", true)
])
], 4)
])) : createCommentVNode("", true)
]),
_: 1
})
]);
};
}
});
function useConnections(rowManager, props, id, emit) {
const connections = ref([]);
const barPositions = ref(/* @__PURE__ */ new Map());
const selectedConnection = ref(null);
const getConnectorProps = computed(() => (conn) => {
var _a, _b;
const sourceBar = barPositions.value.get(conn.sourceId);
const targetBar = barPositions.value.get(conn.targetId);
if (!sourceBar || !targetBar) {
return null;
}
const connectionProps = {
type: conn.type ?? props.defaultConnectionType,
color: conn.color ?? props.defaultConnectionColor,
pattern: conn.pattern ?? props.defaultConnectionPattern,
animated: conn.animated ?? props.defaultConnectionAnimated,
animationSpeed: conn.animationSpeed ?? props.defaultConnectionAnimationSpeed,
relation: conn.relation ?? props.defaultConnectionRelation,
label: conn.label ?? props.defaultConnectionLabel,
labelAlwaysVisible: conn.labelAlwaysVisible ?? props.defaultConnectionLabelAlwaysVisible,
labelStyle: conn.labelStyle ?? props.defaultConnectionLabelStyle,
isSelected: ((_a = selectedConnection.value) == null ? void 0 : _a.sourceId) === conn.sourceId && ((_b = selectedConnection.value) == null ? void 0 : _b.targetId) === conn.targetId
};
return {
sourceBar,
targetBar,
...connectionProps
};
});
const handleConnectionClick = (connection) => {
var _a, _b;
if (((_a = selectedConnection.value) == null ? void 0 : _a.sourceId) === connection.sourceId && ((_b = selectedConnection.value) == null ? void 0 : _b.targetId) === connection.targetId) {
selectedConnection.value = null;
} else {
selectedConnection.value = connection;
}
};
const deleteSelectedConnection = () => {
if (!selectedConnection.value) return;
const allBars = getAllBars(rowManager.rows.value);
const sourceBar = allBars.find(
(bar) => {
var _a;
return bar.ganttBarConfig.id === ((_a = selectedConnection.value) == null ? void 0 : _a.sourceId);
}
);
if (sourceBar && sourceBar.ganttBarConfig.connections) {
sourceBar.ganttBarConfig.connections = sourceBar.ganttBarConfig.connections.filter(
(conn) => {
var _a;
return conn.targetId !== ((_a = selectedConnection.value) == null ? void 0 : _a.targetId);
}
);
const targetBar = allBars.find(
(bar) => {
var _a;
return bar.ganttBarConfig.id === ((_a = selectedConnection.value) == null ? void 0 : _a.targetId);
}
);
emit("connection-delete", {
sourceBar,
targetBar,
e: new MouseEvent("mouseup")
});
selectedConnection.value = null;
initializeConnections();
rowManager.onBarMove();
}
};
const getAllBars = (rows) => {
return rows.flatMap((row) => {
var _a;
const bars = [...row.bars];
if ((_a = row.children) == null ? void 0 : _a.length) {
return [...bars, ...getAllBars(row.children)];
}
return bars;
});
};
const initializeConnections = () => {
connections.value = [];
const allBars = getAllBars(rowManager.rows.value);
allBars.forEach((el) => {
var _a;
if ((_a = el.ganttBarConfig.connections) == null ? void 0 : _a.length) {
el.ganttBarConfig.connections.forEach((conn) => {
connections.value.push({
sourceId: el.ganttBarConfig.id,
targetId: conn.targetId,
type: conn.type,
color: conn.color,
pattern: conn.pattern,
animated: conn.animated,
animationSpeed: conn.animationSpeed,
relation: conn.relation,
label: conn.label,
labelAlwaysVisible: conn.labelAlwaysVisible,
labelStyle: conn.labelStyle
});
});
}
});
};
watch(
() => rowManager.rows.value,
() => {
initializeConnections();
},
{ deep: true }
);
const updateBarPositions = async () => {
await new Promise((resolve) => requestAnimationFrame(resolve));
const parentElement = document.getElementById(id.value);
const rowsContainer = parentElement.querySelector(".g-gantt-rows-container");
if (!rowsContainer) return;
const scrollTop = rowsContainer.scrollTop;
const scrollLeft = rowsContainer.scrollLeft;
const containerRect = rowsContainer.getBoundingClientRect();
const bars = parentElement.querySelectorAll(".g-gantt-bar");
barPositions.value.clear();
bars.forEach((bar) => {
const rect = bar.getBoundingClientRect();
const barId = bar.getAttribute("id");
if (barId) {
const position = {
id: barId,
x: rect.left - containerRect.left + scrollLeft,
y: rect.top - containerRect.top + scrollTop,
width: rect.width,
height: rect.height
};
barPositions.value.set(barId, position);
}
});
};
return {
connections,
barPositions,
getConnectorProps,
initializeConnections,
updateBarPositions,
handleConnectionClick,
selectedConnection,
deleteSelectedConnection
};
}
function useTooltip() {
const showTooltip = ref(false);
const tooltipBar = ref(void 0);
let tooltipTimeoutId;
const initTooltip = (bar) => {
if (tooltipTimeoutId) {
clearTimeout(tooltipTimeoutId);
}
tooltipTimeoutId = setTimeout(() => {
showTooltip.value = true;
}, 800);
tooltipBar.value = bar;
};
const clearTooltip = () => {
clearTimeout(tooltipTimeoutId);
showTooltip.value = false;
};
return {
showTooltip,
tooltipBar,
initTooltip,
clearTooltip
};
}
function useChartNavigation(options, maxRows) {
const { scrollRefs, updateBarPositions, timeaxisUnits } = options;
const { adjustZoomAndPrecision } = timeaxisUnits;
const scrollPosition = ref(0);
const isAtTop = ref(true);
const isAtBottom = ref(false);
const totalWidth = computed(() => {
return timeaxisUnits.timeaxisUnits.value.result.lowerUnits.reduce((total, unit) => {
return total + parseInt(unit.width);
}, 0);
});
const handleStep = (newPosition, wrapper) => {
const maxScroll = totalWidth.value - wrapper.clientWidth;
const targetScroll = maxScroll * newPosition / 100;
wrapper.scrollLeft = targetScroll;
scrollPosition.value = newPosition;
};
const handleScroll = (wrapper) => {
const maxScroll = totalWidth.value - wrapper.clientWidth;
const targetScroll = maxScroll * scrollPosition.value / 100;
wrapper.scrollLeft = targetScroll;
};
const handleWheel = (e, wrapper) => {
if (maxRows !== 0) {
if (e.deltaX !== 0) {
e.preventDefault();
}
return;
}
wrapper.scrollLeft += e.deltaX || e.deltaY;
const maxScroll = totalWidth.value - wrapper.clientWidth;
scrollPosition.value = wrapper.scrollLeft / maxScroll * 100;
};
const handleZoomUpdate = async (increase) => {
adjustZoomAndPrecision(increase);
await nextTick();
updateBarPositions();
};
const handleContentScroll = (e) => {
const target = e.target;
if (scrollRefs.labelColumn.value) {
scrollRefs.labelColumn.value.setScroll(target.scrollTop);
}
updateVerticalScrollState();
};
const handleLabelScroll = (scrollTop) => {
if (scrollRefs.rowsContainer.value) {
scrollRefs.rowsContainer.value.scrollTop = scrollTop;
updateVerticalScrollState();
}
};
const updateVerticalScrollState = () => {
if (!scrollRefs.rowsContainer.value) return;
const { scrollTop, scrollHeight, clientHeight } = scrollRefs.rowsContainer.value;
isAtTop.value = scrollTop === 0;
isAtBottom.value = Math.ceil(scrollTop + clientHeight) >= scrollHeight;
};
const scrollRowUp = () => {
var _a;
if (!scrollRefs.rowsContainer.value) return;
const currentScroll = scrollRefs.rowsContainer.value.scrollTop;
const rowHeight = ((_a = scrollRefs.rowsContainer.value.firstElementChild) == null ? void 0 : _a.clientHeight) || 0;
scrollRefs.rowsContainer.value.scrollTop = Math.max(0, currentScroll - rowHeight);
handleContentScroll(createScrollEvent(scrollRefs.rowsContainer.value));
};
const scrollRowDown = () => {
var _a;
if (!scrollRefs.rowsContainer.value) return;
const currentScroll = scrollRefs.rowsContainer.value.scrollTop;
const rowHeight = ((_a = scrollRefs.rowsContainer.value.firstElementChild) == null ? void 0 : _a.clientHeight) || 0;
const maxScroll = scrollRefs.rowsContainer.value.scrollHeight - scrollRefs.rowsContainer.value.clientHeight;
scrollRefs.rowsContainer.value.scrollTop = Math.min(maxScroll, currentScroll + rowHeight);
handleContentScroll(createScrollEvent(scrollRefs.rowsContainer.value));
};
const createScrollEvent = (target) => {
const event = new Event("scroll", {
bubbles: true,
cancelable: true
});
Object.defineProperty(event, "target", {
value: target,
enumerable: true
});
return event;
};
return {
scrollPosition,
isAtTop,
isAtBottom,
handleStep,
handleScroll,
handleWheel,
handleContentScroll,
handleLabelScroll,
handleZoomUpdate,
scrollRowUp,
scrollRowDown
};
}
function useKeyboardNavigation(chartNavigation, wrapperRef, ganttContainerRef, connectionControls, enableConnectionDeletion) {
const { handleStep, handleZoomUpdate, scrollPosition } = chartNavigation;
const { selectedConnection, deleteSelectedConnection } = connectionControls;
const handleKeyDown = (event) => {
const target = event.target;
if (!ganttContainerRef.value || target !== ganttContainerRef.value) {
return;
}
if (selectedConnection.value && enableConnectionDeletion.value) {
switch (event.key) {
case "Delete":
deleteSelectedConnection();
return;
case "Escape":
selectedConnection.value = null;
return;
}
}
switch (event.key) {
case "ArrowLeft":
if (wrapperRef.value && scrollPosition.value > 0) {
handleStep(Math.max(0, scrollPosition.value - 10), wrapperRef.value);
}
break;
case "ArrowRight":
if (wrapperRef.value && scrollPosition.value < 100) {
handleStep(Math.min(100, scrollPosition.value + 10), wrapperRef.value);
}
break;
case "+":
handleZoomUpdate(true);
break;
case "-":
handleZoomUpdate(false);
break;
case "Home":
if (wrapperRef.value) {
handleStep(0, wrapperRef.value);
}
break;
case "End":
if (wrapperRef.value) {
handleStep(100, wrapperRef.value);
}
break;
case "PageUp":
if (wrapperRef.value && scrollPosition.value >= 10) {
handleStep(scrollPosition.value - 10, wrapperRef.value);
} else if (wrapperRef.value) {
handleStep(0, wrapperRef.value);
}
break;
case "PageDown":
if (wrapperRef.value && scrollPosition.value <= 90) {
handleStep(scrollPosition.value + 10, wrapperRef.value);
} else if (wrapperRef.value) {
handleStep(100, wrapperRef.value);
}
break;
}
};
return {
handleKeyDown
};
}
function createHistoryState(rows, expandedGroups, customOrder) {
const prepareRowForCloning = (row) => {
var _a, _b;
const cleanRow = {
id: row.id,
label: row.label,
bars: ((_a = row.bars) == null ? void 0 : _a.map((bar) => ({
...bar,
ganttBarConfig: {
id: bar.ganttBarConfig.id,
label: bar.ganttBarConfig.label,
html: bar.ganttBarConfig.html,
hasHandles: bar.ganttBarConfig.hasHandles,
immobile: bar.ganttBarConfig.immobile,
bundle: bar.ganttBarConfig.bundle,
pushOnOverlap: bar.ganttBarConfig.pushOnOverlap,
pushOnConnect: bar.ganttBarConfig.pushOnConnect,
style: bar.ganttBarConfig.style,
class: bar.ganttBarConfig.class,
connections: bar.ganttBarConfig.connections,
milestoneId: bar.ganttBarConfig.milestoneId,
progress: bar.ganttBarConfig.progress,
progressStyle: bar.ganttBarConfig.progressStyle,
progressResizable: bar.ganttBarConfig.progressResizable
}
}))) || [],
connections: row.connections,
children: (_b = row.children) == null ? void 0 : _b.map(prepareRowForCloning)
};
return cleanRow;
};
const preparedRows = rows.map(prepareRowForCloning);
return {
rows: cloneDeep(preparedRows),
expandedGroups: new Set(expandedGroups),
customOrder: new Map(customOrder),
timestamp: Date.now()
};
}
function restoreState(state, originalRows) {
const restoreRow = (historyRow, originalRow) => {
const restored = cloneDeep(historyRow);
if (originalRow) {
restored._originalNode = originalRow._originalNode;
}
if (restored.children && (originalRow == null ? void 0 : originalRow.children)) {
restored.children = restored.children.map(
(child, index) => restoreRow(child, originalRow.children[index])
);
}
return restored;
};
return {
rows: state.rows.map((historyRow) => {
const originalRow = originalRows.find((r) => r.id === historyRow.id);
return restoreRow(historyRow, originalRow);
}),
expandedGroups: new Set(state.expandedGroups),
customOrder: new Map(state.customOrder)
};
}
function findParentId(rows, path) {
if (path.length <= 1) return void 0;
let current = rows[path[0]];
for (let i = 1; i < path.length - 1; i++) {
if (!(current == null ? void 0 : current.children)) return void 0;
current = current.children[path[i]];
}
return current == null ? void 0 : current.id;
}
function findBarInRows(rows, barId) {
for (const row of rows) {
const found = row.bars.find((bar) => bar.ganttBarConfig.id === barId);
if (found) return found;
if (row.children) {
const foundInChildren = findBarInRows(row.children, barId);
if (foundInChildren) return foundInChildren;
}
}
return null;
}
function findRowById(rows, id) {
for (const row of rows) {
if (row.id === id) return row;
if (row.children) {
const found = findRowById(row.children, id);
if (found) return found;
}
}
return null;
}
function findRowPath(rows, id) {
for (let i = 0; i < rows.length; i++) {
if (rows[i].id === id) return [i];
if (rows[i].children) {
const childPath = findRowPath(rows[i].children, id);
if (childPath.length) return [i, ...childPath];
}
}
return [];
}
function findRowIdForBar(barId, rows) {
function searchInRows(rows2) {
for (const row of rows2) {
if (row.bars.some((bar) => bar.ganttBarConfig.id === barId)) {
return row.id;
}
if (row.children) {
const foundId = searchInRows(row.children);
if (foundId) return foundId;
}
}
return null;
}
return searchInRows(rows) || "";
}
function getAllBarsFromState(state) {
const barsMap = /* @__PURE__ */ new Map();
function collectBars(rows) {
rows.forEach((row) => {
row.bars.forEach((bar) => {
barsMap.set(bar.ganttBarConfig.id, bar);
});
if (row.children) {
collectBars(row.children);
}
});
}
collectBars(state.rows);
return barsMap;
}
function compareBarStates(oldBar, newBar, barStart, barEnd, rows) {
if (oldBar[barStart.value] === newBar[barStart.value] && oldBar[barEnd.value] === newBar[barEnd.value]) {
return null;
}
return {
barId: oldBar.ganttBarConfig.id,
rowId: findRowIdForBar(oldBar.ganttBarConfig.id, rows.value),
oldStart: oldBar[barStart.value],
newStart: newBar[barStart.value],
oldEnd: oldBar[barEnd.value],
newEnd: newBar[barEnd.value]
};
}
function compareAllBars(oldState, newState, barStart, barEnd, rows) {
const changes = [];
const oldBars = getAllBarsFromState(oldState);
const newBars = getAllBarsFromState(newState);
oldBars.forEach((oldBar, barId) => {
const newBar = newBars.get(barId);
if (newBar) {
const change = compareBarStates(oldBar, newBar, barStart, barEnd, rows);
if (change) {
changes.push(change);
}
}
});
return changes;
}
function calculateHistoryChanges(prevState, newState, barStart, barEnd, rows) {
const rowChanges = [];
function compareRows(oldRows) {
for (const oldRow of oldRows) {
if (!oldRow.id) continue;
const oldPath = findRowPath(prevState.rows, oldRow.id);
const newPath = findRowPath(newState.rows, oldRow.id);
if (oldPath.length !== newPath.length || !oldPath.every((v, i) => v === newPath[i])) {
const oldParentId = oldPath.length > 1 ? findParentId(prevState.rows, oldPath) : void 0;
const newParentId = newPath.length > 1 ? findParentId(newState.rows, newPath) : void 0;
rowChanges.push({
type: oldParentId !== newParentId ? "group" : "reorder",
sourceRow: oldRow,
oldIndex: oldPath[oldPath.length - 1],
newIndex: newPath[newPath.length - 1],
oldParentId,
newParentId
});
}
if (oldRow.children) {
const newRow = findRowById(newState.rows, oldRow.id);
if (newRow == null ? void 0 : newRow.children) {
compareRows(oldRow.children);
}
}
}
}
compareRows(prevState.rows);
const barChanges = compareAllBars(prevState, newState, barStart, barEnd, rows);
return {
rowChanges,
barChanges
};
}
const MAX_HISTORY_STATES = 50;
function useRows(slots, {
barStart,
barEnd,
dateFormat,
multiColumnLabel,
onSort,
initialSort,
onGroupExpansion
}, initialRows) {
const sortState = ref({
column: initialSort.column,
direction: initialSort.direction
});
const sortChangeCallbacks = ref(/* @__PURE__ */ new Set());
const expandedGroups = ref(/* @__PURE__ */ new Set());
const groupExpansionCallbacks = ref(/* @__PURE__ */ new Set());
const customOrder = ref(/* @__PURE__ */ new Map());
const reorderedRows = ref([]);
const historyStates = ref([]);
const currentHistoryIndex = ref(-1);
const initializeHistory = () => {
historyStates.value = [
createHistoryState(reorderedRows.value, expandedGroups.value, customOrder.value)
];
currentHistoryIndex.value = 0;
};
onMounted(() => {
initializeHistory();
});
const canUndo = computed(() => currentHistoryIndex.value > 0 && historyStates.value.length > 1);
const canRedo = computed(
() => historyStates.value.length > 1 && currentHistoryIndex.value < historyStates.value.length - 1
);
const addHistoryState = () => {
if (currentHistoryIndex.value < historyStates.value.length - 1) {
historyStates.value = historyStates.value.slice(0, currentHistoryIndex.value + 1);
}
historyStates.value.push(
createHistoryState(reorderedRows.value, expandedGroups.value, customOrder.value)
);
currentHistoryIndex.value++;
if (historyStates.value.length > MAX_HISTORY_STATES) {
const excess = historyStates.value.length - MAX_HISTORY_STATES;
historyStates.value = historyStates.value.slice(excess);
currentHistoryIndex.value = Math.max(0, currentHistoryIndex.value - excess);
}
};
const undo = () => {
const currentState = historyStates.value[currentHistoryIndex.value];
currentHistoryIndex.value--;
const previousState = historyStates.value[currentHistoryIndex.value];
const changes = calculateHistoryChanges(currentState, previousState, barStart, barEnd, rows);
const restored = restoreState(previousState, reorderedRows.value);
reorderedRows.value = restored.rows;
customOrder.value = restored.customOrder;
return changes;
};
const redo = () => {
const currentState = historyStates.value[currentHistoryIndex.value];
currentHistoryIndex.value++;
const nextState = historyStates.value[currentHistoryIndex.value];
const changes = calculateHistoryChanges(currentState, nextState, barStart, barEnd, rows);
const restored = restoreState(nextState, reorderedRows.value);
reorderedRows.value = restored.rows;
customOrder.value = restored.customOrder;
return changes;
};
const onBarMove = () => {
addHistoryState();
};
const clearHistory = () => {
initializeHistory();
};
const extractRowsFromSlots = () => {
var _a;
const defaultSlot = (_a = slots.default) == null ? void 0 : _a.call(slots);
const rows2 = [];
if (!defaultSlot) return rows2;
defaultSlot.forEach((child) => {
var _a2, _b;
if (((_a2 = child.props) == null ? void 0 : _a2.bars) || ((_b = child.props) == null ? void 0 : _b.children)) {
const { label, bars = [], children = [], id, connections = [] } = child.props;
rows2.push({
id,
label,
bars,
children,
connections,
_originalNode: child
});
} else if (Array.isArray(child.children)) {
child.children.forEach((grandchild) => {
var _a3, _b2;
const grandchildNode = grandchild;
if (((_a3 = grandchildNode == null ? void 0 : grandchildNode.props) == null ? void 0 : _a3.bars) || ((_b2 = grandchildNode == null ? void 0 : grandchildNode.props) == null ? void 0 : _b2.children)) {
const { label, bars = [], children = [], id, connections = [] } = grandchildNode.props;
rows2.push({
id,
label,
bars,
children,
connections,
_originalNode: grandchildNode
});
}
});
}
});
return rows2;
};
const getSourceRows = () => {
var _a;
if ((_a = initialRows == null ? void 0 : initialRows.value) == null ? void 0 : _a.length) {
return initialRows.value;
}
return extractRowsFromSlots();
};
reorderedRows.value = getSourceRows();
watch(
() => getSourceRows(),
(newRows) => {
reorderedRows.value = newRows;
}
);
const calculateGroupBars = (row) => {
var _a;
if (!((_a = row.children) == null ? void 0 : _a.length)) return row.bars || [];
const allChildBars = row.children.flatMap((child) => {
const childGroupBars = calculateGroupBars(child);
return [...childGroupBars, ...child.bars || []];
});
if (!allChildBars.length) return [];
const minStart = allChildBars.reduce(
(min, bar) => {
const currentStart = toDayjs(bar[barStart.value]);
return !min || currentStart.isBefore(min) ? currentStart : min;
},
null
);
const maxEnd = allChildBars.reduce(
(max, bar) => {
const currentEnd = toDayjs(bar[barEnd.value]);
return !max || currentEnd.isAfter(max) ? currentEnd : max;
},
null
);
if (!minStart || !maxEnd) return [];
const format = typeof dateFormat.value === "string" ? dateFormat.value : "YYYY-MM-DD HH:mm";
return [
{
[barStart.value]: minStart.format(format),
[barEnd.value]: maxEnd.format(format),
ganttBarConfig: {
id: `group-${row.id || row.label}`,
immobile: true,
label: row.label,
style: {
background: "transparent"
},
connections: row.connections || []
}
}
];
};
const toDayjs = (input) => {
if (typeof input === "string") {
return dayjs(input);
} else if (input instanceof Date) {
return dayjs(input);
}
return dayjs();
};
const getStartDate = (row) => {
var _a;
if ((_a = row.children) == null ? void 0 : _a.length) {
const childDates = row.children.map((child) => getStartDate(child)).filter((date) => date !== null);
if (childDates.length === 0) {
return getBarsStartDate(row.bars);
}
return childDates.reduce((min, date) => !min || date.isBefore(min) ? date : min);
}
return getBarsStartDate(row.bars);
};
const getBarsStartDate = (bars) => {
if (bars.length === 0) return null;
return bars.reduce((min, bar) => {
const currentStart = toDayjs(bar[barStart.value]);
return !min || currentStart.isBefore(min) ? currentStart : min;
}, null);
};
const getEndDate = (row) => {
var _a;
if ((_a = row.children) == null ? void 0 : _a.length) {
const childDates = row.children.map((child) => getEndDate(child)).filter((date) => date !== null);
if (childDates.length === 0) {
return getBarsEndDate(row.bars);
}
return childDates.reduce((max, date) => !max || date.isAfter(max) ? date : max);
}
return getBarsEndDate(row.bars);
};
const getBarsEndDate = (bars) => {
if (bars.length === 0) return null;
return bars.reduce((max, bar) => {
const currentEnd = toDayjs(bar[barEnd.value]);
return !max || currentEnd.isAfter(max) ? currentEnd : max;
}, null);
};
const calculateDuration = (row) => {
const startDate = getStartDate(row);
const endDate = getEndDate(row);
if (!startDate || !endDate) return 0;
return endDate.diff(startDate, "minutes");
};
const compareValues = (a, b, column) => {
var _a, _b, _c, _d, _e;
if ((((_a = a.children) == null ? void 0 : _a.length) || 0) !== (((_b = b.children) == null ? void 0 : _b.length) || 0)) {
return (((_c = b.children) == null ? void 0 : _c.length) || 0) - (((_d = a.children) == null ? void 0 : _d.length) || 0);
}
const columnConfig = (_e = multiColumnLabel.value) == null ? void 0 : _e.find((conf) => conf.field === column);
if ((columnConfig == null ? void 0 : columnConfig.sortFn) && !isStandardField(column)) {
return columnConfig.sortFn(a, b);
}
switch (column) {
case "Id":
const aId = a.id ?? 0;
const bId = b.id ?? 0;
return aId < bId ? -1 : aId > bId ? 1 : 0;
case "Label":
return a.label.localeCompare(b.label, void 0, {
numeric: true,
sensitivity: "base"
});
case "StartDate": {
const aStartDate = getStartDate(a);
const bStartDate = getStartDate(b);
if (!aStartDate && !bStartDate) return 0;
if (!aStartDate) return 1;
if (!bStartDate) return -1;
return aStartDate.valueOf() - bStartDate.valueOf();
}
case "EndDate": {
const aEndDate = getEndDate(a);
const bEndDate = getEndDate(b);
if (!aEndDate && !bEndDate) return 0;
if (!aEndDate) return 1;
if (!bEndDate) return -1;
return aEndDate.valueOf() - bEndDate.valueOf();
}
case "Duration": {
const aDuration = calculateDuration(a);
const bDuration = calculateDuration(b);
return aDuration - bDuration;
}
case "Progress": {
const getAvgProgress = (row) => {
const progressValues = row.bars.map((bar) => bar.ganttBarConfig.progress).filter((progress) => progress !== void 0);
if (progressValues.length === 0) return -1;
return progressValues.reduce((sum, curr) => sum + curr, 0) / progressValues.length;
};
const progressA = getAvgProgress(a);
const progressB = getAvgProgress(b);
if (progressA === -1 && progressB === -1) return 0;
if (progressA === -1) return 1;
if (progressB === -1) return -1;
return progressA - progressB;
}
default:
if (columnConfig == null ? void 0 : columnConfig.valueGetter) {
const aValue = columnConfig.valueGetter(a);
const bValue = columnConfig.valueGetter(b);
return String(aValue).localeCompare(String(bValue));
}
return 0;
}
};
const isStandardField = (field) => {
return ["Id", "Label", "StartDate", "EndDate", "Duration"].includes(field);
};
const applyCustomOrder = (rowsToSort) => {
if (customOrder.value.size === 0 || sortState.value.direction !== "none") {
return rowsToSort;
}
return [...rowsToSort].sort((a, b) => {
const orderA = a.id ? customOrder.value.get(a.id) ?? Number.MAX_VALUE : Number.MAX_VALUE;
const orderB = b.id ? customOrder.value.get(b.id) ?? Number.MAX_VALUE : Number.MAX_VALUE;
return orderA - orderB;
});
};
const rows = computed(() => {
let sourceRows = [...reorderedRows.value];
if (!sourceRows.length) return sourceRows;
const processRowsWithGroupBars = (rows2) => {
return rows2.map((row) => {
var _a;
if ((_a = row.children) == null ? void 0 : _a.length) {
const processedChildren = processRowsWithGroupBars(row.children);
return {
...row,
children: processedChildren,
bars: calculateGroupBars(row)
};
}
return row;
});
};
sourceRows = processRowsWithGroupBars(sourceRows);
if (sortState.value.direction !== "none") {
return sortRows(sourceRows, sortState.value.column, sortState.value.direction);
}
if (customOrder.value.size > 0) {
return sourceRows.sort((a, b) => {
const orderA = a.id ? customOrder.value.get(a.id) ?? Number.MAX_VALUE : Number.MAX_VALUE;
const orderB = b.id ? customOrder.value.get(b.id) ?? Number.MAX_VALUE : Number.MAX_VALUE;
return orderA - orderB;
});
}
return sourceRows;
});
const resetCustomOrder = () => {
customOrder.value.clear();
};
const sortRows = (rows2, column, direction) => {
return rows2.map((row) => {
var _a;
if ((_a = row.children) == null ? void 0 : _a.length) {
return {
...row,
children: sortRows(row.children, column, direction)
};
}
return row;
}).sort((a, b) => {
const comparison = compareValues(a, b, column);
return direction === "asc" ? comparison : -comparison;
});
};
const toggleSort = (column) => {
const previousDirection = sortState.value.direction;
if (sortState.value.column !== column) {
sortState.value = {
column,
direction: "asc"
};
} else {
switch (sortState.value.direction) {
case "none":
sortState.value.direction = "asc";
break;
case "asc":
sortState.value.direction = "desc";
break;
case "desc":
sortState.value.direction = "none";
break;
}
}
onSort(sortState.value);
sortChangeCallbacks.value.forEach((callback) => callback());
if (previousDirection !== "none" && sortState.value.direction === "none") {
applyCustomOrder(rows.value);
}
};
const toggleGroupExpansion = (rowId) => {
if (expandedGroups.value.has(rowId)) {
expandedGroups.value.delete(rowId);
} else {
expandedGroups.value.add(rowId);
}
onGroupExpansion(rowId);
groupExpansionCallbacks.value.forEach((callback) => callback());
};
const isGroupExpanded = (rowId) => {
return expandedGroups.value.has(rowId);
};
const getFlattenedRows = () => {
const flatten = (rows2) => {
return rows2.flatMap((row) => {
var _a;
if (!((_a = row.children) == null ? void 0 : _a.length) || !isGroupExpanded(row.id)) {
return [row];
}
return [row, ...flatten(row.children)];
});
};
return flatten(rows.value);
};
const onSortChange = (callback) => {
sortChangeCallbacks.value.add(callback);
return () => {
sortChangeCallbacks.value.delete(callback);
};
};
const onGroupExpansionChange = (callback) => {
groupExpansionCallbacks.value.add(callback);
return () => {
groupExpansionCallbacks.value.delete(callback);
};
};
const expandAllGroups = () => {
const addGroupsToExpanded = (rows2) => {
rows2.forEach((row) => {
var _a;
if (((_a = row.children) == null ? void 0 : _a.length) && row.id) {
expandedGroups.value.add(row.id);
addGroupsToExpanded(row.children);
}
});
};
addGroupsToExpanded(getSourceRows());
groupExpansionCallbacks.value.forEach((callback) => callback());
};
const collapseAllGroups = () => {
expandedGroups.value.clear();
groupExpansionCallbacks.value.forEach((callback) => callback());
};
const hasAnyGroup = computed(() => {
const checkForGroups = (rows2) => {
return rows2.some(
(row) => {
var _a;
return ((_a = row.children) == null ? void 0 : _a.length) > 0 || row.children && checkForGroups(row.children);
}
);
};
return checkForGroups(rows.value);
});
const areAllGroupsExpanded = computed(() => {
if (!hasAnyGroup.value) return false;
const checkAllExpanded = (rows2) => {
return rows2.every((row) => {
var _a;
if ((_a = row.children) == null ? void 0 : _a.length) {
const isCurrentExpanded = row.id ? expandedGroups.value.has(row.id) : false;
return isCurrentExpanded && checkAllExpanded(row.children);
}
return true;
});
};
return checkAllExpanded(rows.value);
});
const areAllGroupsCollapsed = computed(() => {
if (!hasAnyGroup.value) return false;
const checkAllCollapsed = (rows2) => {
return rows2.every((row) => {
var _a;
if ((_a = row.children) == null ? void 0 : _a.length) {
const isCurrentCollapsed = row.id ? !expandedGroups.value.has(row.id) : true;
return isCurrentCollapsed && checkAllCollapsed(row.children);
}
return true;
});
};
return checkAllCollapsed(rows.value);
});
const getChartRows = () => rows.value;
const updateRows = (newRows) => {
reorderedRows.value = newRows;
addHistoryState();
};
return {
rows,
updateRows,
sortState,
toggleSort,
getChartRows,
onSortChange,
toggleGroupExpansion,
isGroupExpanded,
getFlattenedRows,
onGroupExpansionChange,
customOrder,
resetCustomOrder,
expandAllGroups,
collapseAllGroups,
canUndo,
canRedo,
undo,
redo,
clearHistory,
onBarMove,
areAllGroupsExpanded,
areAllGroupsCollapsed
};
}
const useSectionResize = () => {
const resizeState = ref({
isResizing: false,
startX: 0,
startWidth: 0
});
const resetResizeState = () => {
resizeState.value = {
isResizing: false,
startX: 0,
startWidth: 0
};
};
const handleResizeStart = (e, currentWidth) => {
e.preventDefault();
resizeState.value = {
isResizing: true,
startX: e.clientX,
startWidth: currentWidth
};
document.body.style.cursor = "col-resize";
};
const handleResizeMove = (e, onResize) => {
if (!resizeState.value.isResizing) return;
e.preventDefault();
const deltaX = e.clientX - resizeState.value.startX;
const newWidth = Math.max(0, resizeState.value.startWidth + deltaX);
onResize(newWidth);
};
const handleResizeEnd = () => {
if (resizeState.value.isResizing) {
document.body.style.cursor = "";
resetResizeState();
}
};
const handleTouchStart = (e, currentWidth) => {
const touch = e.touches[0];
if (!touch) return;
e.preventDefault();
resizeState.value = {
isResizing: true,
startX: touch.clientX,
startWidth: currentWidth
};
};
const handleTouchMove = (e, onResize) => {
if (!resizeState.value.isResizing) return;
const touch = e.touches[0];
if (!touch) return;
e.preventDefault();
const deltaX = touch.clientX - resizeState.value.startX;
const newWidth = Math.max(0, resizeState.value.startWidth + deltaX);
onResize(newWidth);
};
return {
resizeState,
handleResizeStart,
handleResizeMove,
handleResizeEnd,
handleTouchStart,
handleTouchMove,
resetResizeState
};
};
function useConnectionCreation(config, rowManager, emit, reinitializeConnections) {
const connectionState = ref({
isCreating: false,
sourceBar: null,
sourcePoint: null,
mouseX: 0,
mouseY: 0
});
const hoverState = ref({
isVisible: false,
barId: null,
point: null
});
const validateConnection = (sourceBar, targetBar) => {
var _a;
if (sourceBar.ganttBarConfig.id === targetBar.ganttBarConfig.id) {
return { isValid: false, message: "Cannot connect a bar to itself" };
}
const existingConnection = (_a = sourceBar.ganttBarConfig.connections) == null ? void 0 : _a.find(
(conn) => conn.targetId === targetBar.ganttBarConfig.id
);
if (existingConnection) {
return { isValid: false, message: "Existing connection" };
}
return { isValid: true };
};
const determineRelationType = (sourcePoint, targetPoint) => {
if (sourcePoint === "end" && targetPoint === "start") return "FS";
if (sourcePoint === "start" && targetPoint === "start") return "SS";
if (sourcePoint === "end" && targetPoint === "end") return "FF";
if (sourcePoint === "start" && targetPoint === "end") return "SF";
return "FS";
};
const startConnectionCreation = (bar, point, e) => {
connectionState.value = {
isCreating: true,
sourceBar: bar,
sourcePoint: point,
mouseX: e.clientX,
mouseY: e.clientY
};
emit("connection-start", {
sourceBar: bar,
connectionPoint: point,
e
});
};
const updateConnectionDrag = (e) => {
if (!connectionState.value.isCreating) return;
connectionState.value.mouseX = e.clientX;
connectionState.value.mouseY = e.clientY;
emit("connection-drag", {
sourceBar: connectionState.value.sourceBar,
connectionPoint: connectionState.value.sourcePoint,
currentX: e.clientX,
currentY: e.clientY,
e
});
};
const completeConnection = (targetBar, targetPoint, e) => {
var _a, _b, _c;
if (!connectionState.value.sourceBar || !connectionState.value.sourcePoint) return;
const validation = validateConnection(connectionState.value.sourceBar, targetBar);
if (validation.isValid) {
const relation = determineRelationType(connectionState.value.sourcePoint, targetPoint);
const newConnection = {
targetId: targetBar.ganttBarConfig.id,
type: config.defaultConnectionType.value,
color: config.defaultConnectionColor.value,
pattern: config.defaultConnectionPattern.value,
animated: config.defaultConnectionAnimated.value,
animationSpeed: config.defaultConnectionAnimationSpeed.value,
relation,
label: (_a = config.defaultConnectionLabel) == null ? void 0 : _a.value,
labelAlwaysVisible: (_b = config.defaultConnectionLabelAlwaysVisible) == null ? void 0 : _b.value,
labelStyle: (_c = config.defaultConnectionLabelStyle) == null ? void 0 : _c.value
};
if (!connectionState.value.sourceBar.ganttBarConfig.connections) {
connectionState.value.sourceBar.ganttBarConfig.connections = [];
}
connectionState.value.sourceBar.ganttBarConfig.connections.push(newConnection);
const updatedRows = [...rowManager.rows.value];
rowManager.updateRows(updatedRows);
reinitializeConnections();
emit("connection-complete", {
sourceBar: connectionState.value.sourceBar,
targetBar,
sourcePoint: connectionState.value.sourcePoint,
targetPoint,
e
});
}
resetConnectionState();
};
const handleConnectionPointHover = (barId, point, isEnter) => {
hoverState.value = {
isVisible: isEnter,
barId: isEnter ? barId : null,
point: isEnter ? point : null
};
};
const cancelConnectionCreation = (e) => {
if (connectionState.value.sourceBar && connectionState.value.sourcePoint) {
emit("connection-cancel", {
sourceBar: connectionState.value.sourceBar,
connectionPoint: connectionState.value.sourcePoint,
e
});
}
resetConnectionState();
};
const resetConnectionState = () => {
connectionState.value = {
isCreating: false,
sourceBar: null,
sourcePoint: null,
mouseX: 0,
mouseY: 0
};
};
const canBeConnectionTarget = computed(() => (bar) => {
if (!connectionState.value.sourceBar) return false;
return validateConnection(connectionState.value.sourceBar, bar).isValid;
});
return {
connectionState,
hoverState,
startConnectionCreation,
updateConnectionDrag,
completeConnection,
cancelConnectionCreation,
handleConnectionPointHover,
canBeConnectionTarget
};
}
function useExport(getChartElement, getWrapperElement, rowManager, config) {
const isExporting = ref(false);
const lastError = ref(null);
const prepareElementForExport = (element, wrapperELement, options) => {
const clonedElement = wrapperELement.cloneNode(true);
clonedElement.style.width = element.offsetWidth + "px";
clonedElement.style.height = element.offsetHeight + "px";
const textSelectors = [".g-gantt-bar-label > div", ".g-timeunit-min", ".label-unit"];
const textElements = clonedElement.querySelectorAll(textSelectors.join(", "));
const commands = clonedElement.querySelector(".g-gantt-command");
commands.style.display = "none";
if (!options.exportColumnLabel) {
const columnLabels = clonedElement.querySelector(".g-gantt-label-section");
columnLabels.style.display = "none";
}
textElements.forEach((el) => {
const element2 = el;
element2.style.display = "flex";
element2.style.alignItems = "center";
element2.style.justifyContent = "center";
element2.style.textAlign = "center";
element2.style.position = "relative";
element2.style.fontSize = "10px";
element2.style.transform = "translateY(-25%)";
});
const ellipsisElements = clonedElement.querySelectorAll(
".cell-content, .text-ellipsis, .text-ellipsis-value"
);
ellipsisElements.forEach((el) => {
const element2 = el;
element2.style.overflow = "visible";
element2.style.alignItems = "center";
element2.style.whiteSpace = "normal";
element2.style.textOverflow = "ellipsis";
element2.style.fontSize = "10px";
element2.style.transform = "translateY(-1%)";
});
const rows = clonedElement.querySelectorAll(".g-label-column-row");
rows.forEach((row) => {
const rowElement = row;
rowElement.style.overflow = "visible";
rowElement.style.flexWrap = "wrap";
});
const barLabels = clonedElement.querySelectorAll(".g-gantt-bar-label");
barLabels.forEach((el) => {
const element2 = el;
element2.style.overflow = "hidden";
element2.style.textOverflow = "ellipsis";
element2.style.whiteSpace = "nowrap";
element2.style.transform = "translateY(-15%)";
});
const progressBars = clonedElement.querySelectorAll(".g-gantt-progress-bar");
progressBars.forEach((el) => {
const element2 = el;
element2.style.display = "";
element2.style.alignItems = "";
element2.style.justifyContent = "";
element2.style.transform = "";
element2.style.position = "";
});
const minUnitLabels = clonedElement.querySelectorAll(".g-timeunit-min");
minUnitLabels.forEach((el) => {
const element2 = el;
element2.style.transform = "translateY(-35%)";
});
const progressTexts = clonedElement.querySelectorAll(".progress-text");
progressTexts.forEach((el) => {
const element2 = el;
element2.style.transform = "translateY(-40%)";
});
const timeAxisEventsLabels = clonedElement.querySelectorAll(".g-timeaxis-event-label");
timeAxisEventsLabels.forEach((el) => {
const element2 = el;
element2.style.fontSize = "8px";
element2.style.transform = "translateY(-45%)";
});
const milestoneLabels = clonedElement.querySelectorAll(".g-gantt-milestone-label");
milestoneLabels.forEach((el) => {
const element2 = el;
element2.style.transform = "translateY(-15%)";
});
const milestoneMarkers = clonedElement.querySelectorAll(".g-gantt-milestone-marker");
milestoneMarkers.forEach((el) => {
const element2 = el;
element2.style.transform = "translateY(-0.2%)";
});
const rowLabels = clonedElement.querySelectorAll(".g-gantt-row-label");
rowLabels.forEach((el) => {
const element2 = el;
element2.style.transform = "translateY(-15%)";
});
return clonedElement;
};
const exportChart = async (options) => {
isExporting.value = true;
lastError.value = null;
try {
const element = getChartElement();
const wrapper = getWrapperElement();
if (!element || !wrapper) {
throw new Error("Gantt chart element not found");
}
const filename = options.filename || `gantt-export-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
switch (options.format) {
case "pdf":
return await exportToPdf(element, wrapper, {
...options,
filename: filename + ".pdf"
});
case "png":
return await exportToPng(element, wrapper, {
...options,
filename: filename + ".png"
});
case "svg":
return await exportToSvg(element, wrapper, {
...options,
filename: filename + ".svg"
});
case "excel":
return await exportToExcel({
...options,
filename: filename + ".xlsx"
});
default:
throw new Error(`Format file export not supported: ${options.format}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown Error";
lastError.value = errorMessage;
return {
success: false,
data: null,
error: errorMessage,
filename: options.filename || "export-error"
};
} finally {
isExporting.value = false;
}
};
const exportToPdf = async (element, wrapper, options) => {
try {
const processedElement = prepareElementForExport(element, wrapper, options);
const tempContainer = document.createElement("div");
tempContainer.style.position = "absolute";
tempContainer.style.left = "-9999px";
tempContainer.style.width = element.offsetWidth + "px";
tempContainer.style.height = element.offsetHeight + "px";
tempContainer.style.overflow = "hidden";
tempContainer.appendChild(processedElement);
document.body.appendChild(tempContainer);
await new Promise((resolve) => setTimeout(resolve, 100));
const paperSize = options.paperSize || "a4";
const orientation = options.orientation || "landscape";
const scale = options.scale || 1;
const margin = options.margin !== void 0 ? options.margin : 10;
const pdf = new jsPDF({
orientation,
unit: "mm",
format: paperSize
});
const canvas = await html2canvas(processedElement, {
scale,
logging: false,
allowTaint: true,
useCORS: true,
backgroundColor: "#ffffff",
imageTimeout: 0,
removeContainer: false,
foreignObjectRendering: false
});
document.body.removeChild(tempContainer);
const pageWidth = orientation === "landscape" ? pdf.internal.pageSize.getHeight() : pdf.internal.pageSize.getWidth();
const pageHeight = orientation === "landscape" ? pdf.internal.pageSize.getWidth() : pdf.internal.pageSize.getHeight();
const pdfWidth = pageWidth - margin * 2;
const imgWidth = canvas.width;
const imgHeight = canvas.height;
const ratio = imgWidth / imgHeight;
const pdfHeight = pdfWidth / ratio;
if (pdfHeight > pageHeight - margin * 2) {
let currentHeight = 0;
while (currentHeight < imgHeight) {
if (currentHeight > 0) {
pdf.addPage();
}
const canvasSection = Math.min(
imgHeight - currentHeight,
imgWidth / pdfWidth * (pageHeight - margin * 2)
);
const sectionHeight = canvasSection / imgHeight * imgHeight;
pdf.addImage(
canvas.toDataURL("image/jpeg", options.quality || 0.95),
"JPEG",
margin,
margin,
pdfWidth,
pdfWidth * (sectionHeight / imgWidth),
"",
"FAST",
currentHeight / imgHeight
);
currentHeight += canvasSection;
}
} else {
pdf.addImage(
canvas.toDataURL("image/jpeg", options.quality || 0.95),
"JPEG",
margin,
margin,
pdfWidth,
pdfHeight,
"",
"FAST"
);
}
const blob = pdf.output("blob");
return {
success: true,
data: blob,
filename: options.filename || "gantt-chart.pdf"
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Error during PDF export";
return {
success: false,
data: null,
error: errorMessage,
filename: options.filename || "gantt-chart.pdf"
};
}
};
const exportToPng = async (element, wrapper, options) => {
try {
const processedElement = prepareElementForExport(element, wrapper, options);
const tempContainer = document.createElement("div");
tempContainer.style.position = "absolute";
tempContainer.style.left = "-9999px";
tempContainer.style.width = element.offsetWidth + "px";
tempContainer.style.height = element.offsetHeight + "px";
tempContainer.style.overflow = "hidden";
tempContainer.appendChild(processedElement);
document.body.appendChild(tempContainer);
await new Promise((resolve) => setTimeout(resolve, 100));
const scale = options.scale || 2;
const canvas = await html2canvas(processedElement, {
scale,
logging: false,
allowTaint: true,
useCORS: true,
backgroundColor: "#ffffff",
imageTimeout: 0,
removeContainer: false,
foreignObjectRendering: false
});
document.body.removeChild(tempContainer);
const blob = await new Promise((resolve, reject) => {
canvas.toBlob(
(blob2) => {
if (blob2) {
resolve(blob2);
} else {
reject(new Error("Error creating blob PNG"));
}
},
"image/png",
options.quality || 0.95
);
});
return {
success: true,
data: blob,
filename: options.filename || "gantt-chart.png"
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Error exporting PNG";
return {
success: false,
data: null,
error: errorMessage,
filename: options.filename || "gantt-chart.png"
};
}
};
const exportToSvg = async (element, wrapper, options) => {
try {
const processedElement = prepareElementForExport(element, wrapper, options);
const tempContainer = document.createElement("div");
tempContainer.style.position = "absolute";
tempContainer.style.left = "-9999px";
tempContainer.style.width = element.offsetWidth + "px";
tempContainer.style.height = element.offsetHeight + "px";
tempContainer.style.overflow = "hidden";
tempContainer.appendChild(processedElement);
document.body.appendChild(tempContainer);
await new Promise((resolve) => setTimeout(resolve, 100));
const canvas = await html2canvas(processedElement, {
scale: options.scale || 2,
logging: false,
allowTaint: true,
useCORS: true,
backgroundColor: "#ffffff",
imageTimeout: 0,
removeContainer: false,
foreignObjectRendering: false
});
document.body.removeChild(tempContainer);
const width = canvas.width;
const height = canvas.height;
const svgNS = "http://www.w3.org/2000/svg";
const svgRoot = document.createElementNS(svgNS, "svg");
svgRoot.setAttribute("width", width.toString());
svgRoot.setAttribute("height", height.toString());
svgRoot.setAttribute("viewBox", `0 0 ${width} ${height}`);
svgRoot.setAttribute("xmlns", svgNS);
svgRoot.setAttribute("version", "1.1");
const img = document.createElementNS(svgNS, "image");
img.setAttribute("width", width.toString());
img.setAttribute("height", height.toString());
img.setAttribute("x", "0");
img.setAttribute("y", "0");
img.setAttribute("href", canvas.toDataURL("image/png", options.quality || 0.95));
svgRoot.appendChild(img);
const serializer = new XMLSerializer();
const svgString = serializer.serializeToString(svgRoot);
const finalSvgString = '<?xml version="1.0" standalone="no"?>\n' + svgString;
const blob = new Blob([finalSvgString], { type: "image/svg+xml;charset=utf-8" });
return {
success: true,
data: blob,
filename: options.filename || "gantt-chart.svg"
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Error exporting in SVG";
return {
success: false,
data: null,
error: errorMessage,
filename: options.filename || "gantt-chart.svg"
};
}
};
const exportToExcel = async (options) => {
try {
const workbook = XLSX.utils.book_new();
const getAllRows = (rows) => {
return rows.flatMap((row) => {
var _a;
if (!((_a = row.children) == null ? void 0 : _a.length)) {
return [row];
}
return [row, ...getAllRows(row.children)];
});
};
const allRows = getAllRows(rowManager.rows.value);
const firstSheetData = [];
firstSheetData.push(["ID", "Task", "Start Date", "End Date", "Duration", "Progress (%)"]);
allRows.forEach((row, index) => {
let startDate = "-";
let endDate = "-";
let duration = "-";
let progress = "-";
if (row.bars && row.bars.length > 0) {
const minStartDate = row.bars.reduce(
(min, bar) => {
const currentStart = dayjs(bar[config.barStart.value]);
return !min || currentStart.isBefore(min) ? currentStart : min;
},
null
);
const maxEndDate = row.bars.reduce(
(max, bar) => {
const currentEnd = dayjs(bar[config.barEnd.value]);
return !max || currentEnd.isAfter(max) ? currentEnd : max;
},
null
);
if (minStartDate) {
startDate = minStartDate.format(config.dateFormat.value || "YYYY-MM-DD HH:mm");
}
if (maxEndDate) {
endDate = maxEndDate.format(config.dateFormat.value || "YYYY-MM-DD HH:mm");
}
if (minStartDate && maxEndDate) {
const durationValue = maxEndDate.diff(minStartDate, config.precision.value);
duration = `${durationValue}${config.precision.value.charAt(0)}`;
}
const progressValues = row.bars.map((bar) => bar.ganttBarConfig.progress).filter((progress2) => progress2 !== void 0);
if (progressValues.length > 0) {
const avgProgress = progressValues.reduce((sum, curr) => sum + curr, 0) / progressValues.length;
progress = `${Math.round(avgProgress)}%`;
}
}
firstSheetData.push([
row.id || index + 1,
row.label,
startDate,
endDate,
duration,
progress
]);
});
const worksheet1 = XLSX.utils.aoa_to_sheet(firstSheetData);
XLSX.utils.book_append_sheet(workbook, worksheet1, "Gantt Rows");
const secondSheetData = [];
secondSheetData.push([
"Bar ID",
"Label",
"Parent Row",
"Row ID",
"Start Date",
"End Date",
"Duration",
"Progress (%)",
"Connections",
"Milestone"
]);
const processedBarIds = /* @__PURE__ */ new Set();
const allBars = [];
const collectBars = (rows) => {
rows.forEach((row) => {
row.bars.forEach((bar) => {
if (!processedBarIds.has(bar.ganttBarConfig.id)) {
processedBarIds.add(bar.ganttBarConfig.id);
allBars.push({
bar,
rowLabel: row.label,
rowId: row.id || ""
});
}
});
if (row.children && row.children.length > 0) {
collectBars(row.children);
}
});
};
collectBars(rowManager.rows.value);
allBars.forEach((item) => {
const { bar, rowLabel, rowId } = item;
const barConfig = bar.ganttBarConfig;
const startDate = dayjs(bar[config.barStart.value]).format(
config.dateFormat.value || "YYYY-MM-DD HH:mm"
);
const endDate = dayjs(bar[config.barEnd.value]).format(
config.dateFormat.value || "YYYY-MM-DD HH:mm"
);
const durationValue = dayjs(bar[config.barEnd.value]).diff(
dayjs(bar[config.barStart.value]),
config.precision.value
);
const duration = `${durationValue}${config.precision.value.charAt(0)}`;
const progress = barConfig.progress !== void 0 ? `${Math.round(barConfig.progress)}%` : "-";
const connections = barConfig.connections && barConfig.connections.length > 0 ? barConfig.connections.map((conn) => conn.targetId).join(", ") : "-";
const milestone = barConfig.milestoneId ? barConfig.milestoneId : "-";
secondSheetData.push([
barConfig.id,
barConfig.label || "",
rowLabel,
rowId,
startDate,
endDate,
duration,
progress,
connections,
milestone
]);
});
const worksheet2 = XLSX.utils.aoa_to_sheet(secondSheetData);
XLSX.utils.book_append_sheet(workbook, worksheet2, "Bars Detail");
[worksheet1, worksheet2].forEach((worksheet) => {
const headerRange = XLSX.utils.decode_range(worksheet["!ref"] || "A1");
for (let col = headerRange.s.c; col <= headerRange.e.c; col++) {
const cellRef = XLSX.utils.encode_cell({ r: 0, c: col });
if (!worksheet[cellRef]) worksheet[cellRef] = {};
worksheet[cellRef].s = { font: { bold: true } };
}
});
const colWidths1 = [
{ wch: 10 },
{ wch: 40 },
{ wch: 20 },
{ wch: 20 },
{ wch: 10 },
{ wch: 15 }
];
worksheet1["!cols"] = colWidths1;
const colWidths2 = [
{ wch: 15 },
{ wch: 30 },
{ wch: 30 },
{ wch: 10 },
{ wch: 20 },
{ wch: 20 },
{ wch: 10 },
{ wch: 15 },
{ wch: 30 },
{ wch: 20 }
];
worksheet2["!cols"] = colWidths2;
const excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "array" });
const blob = new Blob([excelBuffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
return {
success: true,
data: blob,
filename: options.filename || "gantt-chart.xlsx"
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Error exporting to Excel";
console.error("Error during Excel export:", error);
return {
success: false,
data: null,
error: errorMessage,
filename: options.filename || "gantt-chart.xlsx"
};
}
};
const downloadExport = (result) => {
if (result.success && result.data) {
const url = URL.createObjectURL(result.data);
const link = document.createElement("a");
link.href = url;
link.download = result.filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
};
return {
exportChart,
downloadExport,
isExporting,
lastError
};
}
const _hoisted_1 = ["id"];
const _hoisted_2 = {
class: "g-gantt-main-layout",
"aria-controls": "gantt-controls"
};
const _hoisted_3 = {
key: 0,
class: "connection-preview",
style: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
pointerEvents: "none",
zIndex: 2e3,
overflow: "visible"
}
};
const _hoisted_4 = { class: "g-gantt-command-block" };
const _hoisted_5 = {
key: 0,
class: "g-gantt-command-vertical"
};
const _hoisted_6 = ["disabled"];
const _hoisted_7 = ["disabled"];
const _hoisted_8 = {
key: 1,
class: "g-gantt-command-groups"
};
const _hoisted_9 = ["disabled"];
const _hoisted_10 = ["disabled"];
const _hoisted_11 = { class: "g-gantt-command-fixed" };
const _hoisted_12 = { class: "g-gantt-command-slider" };
const _hoisted_13 = ["disabled"];
const _hoisted_14 = ["disabled"];
const _hoisted_15 = ["aria-valuenow"];
const _hoisted_16 = ["disabled"];
const _hoisted_17 = ["disabled"];
const _hoisted_18 = { class: "g-gantt-command-block" };
const _hoisted_19 = { class: "g-gantt-command-zoom" };
const _hoisted_20 = ["disabled"];
const _hoisted_21 = ["disabled"];
const _hoisted_22 = { class: "g-gantt-command-history" };
const _hoisted_23 = ["disabled"];
const _hoisted_24 = ["disabled"];
const _hoisted_25 = { class: "g-gantt-command-block" };
const _hoisted_26 = {
key: 0,
class: "g-gantt-command-export"
};
const _hoisted_27 = { class: "g-gantt-export-container" };
const _hoisted_28 = ["disabled"];
const _hoisted_29 = ["disabled"];
const _hoisted_30 = {
key: 0,
class: "g-gantt-export-loading"
};
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "GGanttChart",
props: {
chartStart: {},
chartEnd: {},
precision: { default: "day" },
barStart: {},
barEnd: {},
currentTime: { type: Boolean },
currentTimeLabel: { default: "" },
pointerMarker: { type: Boolean },
dateFormat: { type: [String, Boolean], default: DEFAULT_DATE_FORMAT },
width: { default: "100%" },
hideTimeaxis: { type: Boolean, default: false },
colorScheme: { default: "default" },
grid: { type: Boolean, default: false },
pushOnOverlap: { type: Boolean, default: false },
pushOnConnect: { type: Boolean, default: false },
noOverlap: { type: Boolean, default: false },
rowHeight: { default: 40 },
font: { default: "inherit" },
labelColumnTitle: { default: "" },
labelColumnWidth: { default: 120 },
multiColumnLabel: { default: () => [] },
commands: { type: Boolean, default: true },
enableMinutes: { type: Boolean, default: false },
enableConnections: { type: Boolean, default: true },
enableConnectionCreation: { type: Boolean, default: false },
enableConnectionDeletion: { type: Boolean, default: false },
defaultConnectionType: { default: "straight" },
defaultConnectionColor: { default: "#ff0000" },
defaultConnectionPattern: { default: "solid" },
defaultConnectionAnimated: { type: Boolean, default: false },
defaultConnectionAnimationSpeed: { default: "normal" },
defaultConnectionRelation: { default: "FS" },
defaultConnectionLabel: { default: "" },
defaultConnectionLabelAlwaysVisible: { type: Boolean, default: false },
defaultConnectionLabelStyle: { default: () => ({}) },
maxRows: { default: 0 },
initialSort: { default: () => ({
column: "Label",
direction: "none"
}) },
initialRows: { default: () => [] },
sortable: { type: Boolean, default: true },
labelResizable: { type: Boolean, default: true },
milestones: { default: () => [] },
timeaxisEvents: { default: () => [] },
showEventsAxis: { type: Boolean, default: false },
eventsAxisHeight: { default: 25 },
holidayHighlight: { default: "" },
rowClass: { type: Function, default: () => "" },
rowLabelClass: { type: Function, default: () => "" },
dayOptionLabel: { default: () => ["day"] },
highlightedHours: { default: () => [] },
highlightedDaysInWeek: { default: () => [] },
highlightedDaysInMonth: { default: () => [] },
highlightedMonths: { default: () => [] },
highlightedWeek: { default: () => [] },
locale: { default: "en" },
enableRowDragAndDrop: { type: Boolean, default: false },
markerConnection: { default: "forward" },
showLabel: { type: Boolean, default: true },
showProgress: { type: Boolean, default: true },
defaultProgressResizable: { type: Boolean, default: true },
utc: { type: Boolean, default: false },
barLabelEditable: { type: Boolean, default: false },
exportEnabled: { type: Boolean, default: true },
exportOptions: { default: () => ({
format: "pdf",
quality: 0.95,
paperSize: "a4",
orientation: "landscape",
scale: 1.5,
margin: 10,
exportColumnLabel: true
}) },
showImporter: { type: Boolean, default: false },
importerTitle: { default: "Import data" },
importerDefaultFormat: { default: "csv" },
importerAllowedFormats: { default: () => ["jira", "csv"] },
importerBarStartField: { default: "start" },
importerBarEndField: { default: "end" },
baseUnitWidth: { default: 24 },
defaultZoom: { default: 3 }
},
emits: ["click-bar", "mousedown-bar", "mouseup-bar", "dblclick-bar", "mouseenter-bar", "mouseleave-bar", "dragstart-bar", "drag-bar", "dragend-bar", "contextmenu-bar", "sort", "group-expansion", "row-drop", "progress-change", "progress-drag-start", "progress-drag-end", "connection-start", "connection-drag", "connection-complete", "connection-cancel", "connection-delete", "label-edit", "export-start", "export-success", "export-error", "import-data", "range-selection"],
setup(__props, { expose: __expose, emit: __emit }) {
useCssVars((_ctx) => ({
"1fed23a5": colors.value.rangeHighlight,
"787ffb4a": colors.value.text,
"31d9cbd8": colors.value.primary
}));
const props = __props;
const emit = __emit;
const id = ref(v4());
const slots = useSlots();
const isDragging = ref(false);
const isDraggingTimeaxis = ref(false);
const lastMouseX = ref(0);
const isImporterVisible = ref(props.showImporter);
const gGantt = ref(null);
const ganttChart = ref(null);
const ganttWrapper = ref(null);
const timeaxisComponent = ref(null);
const ganttContainer = ref(null);
const rowsContainer = ref(null);
const labelColumn = ref(null);
const validatedBaseUnitWidth = ref(Math.min(50, Math.max(20, props.baseUnitWidth)));
const validatedDefaultZoom = ref(Math.min(10, Math.max(1, props.defaultZoom)));
const setLabelWidth = () => {
return props.labelColumnWidth * (props.multiColumnLabel.length === 0 ? 1 : props.multiColumnLabel.length + (props.multiColumnLabel.some((el) => el.field === "Label") ? 0 : 1));
};
const labelSectionWidth = ref(setLabelWidth());
watch(
() => props.multiColumnLabel,
() => {
labelSectionWidth.value = setLabelWidth();
}
);
const chartSize = useElementSize(ganttChart);
const handleTimeaxisTouch = {
startX: 0,
isDragging: false
};
const rowManager = useRows(
slots,
{
barStart: toRef(props, "barStart"),
barEnd: toRef(props, "barEnd"),
dateFormat: toRef(props, "dateFormat"),
multiColumnLabel: toRef(props, "multiColumnLabel"),
onSort: (sortState) => emit("sort", { sortState }),
initialSort: props.initialSort,
onGroupExpansion: (rowId) => emit("group-expansion", { rowId })
},
props.initialRows ? toRef(props, "initialRows") : void 0
);
provide("useRows", rowManager);
const {
connections,
barPositions,
getConnectorProps,
initializeConnections,
updateBarPositions,
handleConnectionClick,
selectedConnection,
deleteSelectedConnection
} = useConnections(rowManager, props, id, emit);
const { showTooltip, tooltipBar, initTooltip, clearTooltip } = useTooltip();
const { font, colorScheme } = toRefs(props);
const colors = computed(() => getColorScheme(colorScheme.value));
const { timeaxisUnits, internalPrecision, zoomLevel, adjustZoomAndPrecision } = useTimeaxisUnits({
...toRefs(props),
baseUnitWidth: validatedBaseUnitWidth,
defaultZoom: validatedDefaultZoom
});
const {
scrollPosition,
handleStep,
handleScroll,
handleWheel,
handleContentScroll,
handleLabelScroll,
handleZoomUpdate,
scrollRowUp,
scrollRowDown,
isAtTop,
isAtBottom
} = useChartNavigation(
{
scrollRefs: {
rowsContainer,
labelColumn
},
updateBarPositions,
timeaxisUnits: { timeaxisUnits, internalPrecision, zoomLevel, adjustZoomAndPrecision }
},
props.maxRows
);
const { handleKeyDown } = useKeyboardNavigation(
{
scrollPosition,
handleStep,
handleZoomUpdate
},
ganttWrapper,
ganttContainer,
{
selectedConnection,
deleteSelectedConnection
},
toRef(props.enableConnectionDeletion)
);
const {
handleResizeStart,
handleResizeMove,
handleResizeEnd,
handleTouchStart,
handleTouchMove,
resetResizeState
} = useSectionResize();
const {
connectionState,
hoverState,
startConnectionCreation,
updateConnectionDrag,
completeConnection,
cancelConnectionCreation,
handleConnectionPointHover,
canBeConnectionTarget
} = useConnectionCreation(
{
...toRefs(props)
},
rowManager,
emit,
initializeConnections
);
provide("connectionCreation", {
connectionState,
hoverState,
startConnectionCreation,
updateConnectionDrag,
completeConnection,
cancelConnectionCreation,
handleConnectionPointHover,
canBeConnectionTarget
});
const handleChartMouseMove = (e) => {
if (connectionState.value.isCreating) {
updateConnectionDrag(e);
}
};
const handleChartMouseUp = (e) => {
if (connectionState.value.isCreating) {
cancelConnectionCreation(e);
}
};
const { findBarElement } = useBarSelector();
const previewLinePoints = computed(() => {
if (!connectionState.value.isCreating || !connectionState.value.sourceBar) return null;
const sourceBarElement = findBarElement(
id.value,
connectionState.value.sourceBar.ganttBarConfig.id
);
if (!sourceBarElement) return null;
const sourceRect = sourceBarElement.getBoundingClientRect();
const containerElement = ganttContainer.value;
const rowsContainer2 = containerElement == null ? void 0 : containerElement.querySelector(".g-gantt-rows-container");
if (!rowsContainer2) return null;
const containerRect = rowsContainer2.getBoundingClientRect();
const scrollLeft = rowsContainer2.scrollLeft;
const scrollTop = rowsContainer2.scrollTop;
const sourceX = connectionState.value.sourcePoint === "start" ? sourceRect.left - containerRect.left + scrollLeft : sourceRect.right - containerRect.left + scrollLeft;
const sourceY = sourceRect.top - containerRect.top + scrollTop + sourceRect.height / 2;
const mouseX = connectionState.value.mouseX - containerRect.left + scrollLeft;
const mouseY = connectionState.value.mouseY - containerRect.top + scrollTop;
return {
x1: sourceX,
y1: sourceY,
x2: mouseX,
y2: mouseY
};
});
watch(
() => scrollPosition.value,
() => {
var _a;
if (connectionState.value.isCreating) {
const container = (_a = ganttContainer.value) == null ? void 0 : _a.querySelector(".g-gantt-rows-container");
if (container) {
connectionState.value.mouseX = connectionState.value.mouseX;
}
}
}
);
const { exportChart, downloadExport, isExporting } = useExport(
() => ganttChart.value,
() => gGantt.value,
rowManager,
{
barStart: toRef(props, "barStart"),
barEnd: toRef(props, "barEnd"),
dateFormat: toRef(props, "dateFormat"),
precision: toRef(props, "precision")
}
);
const handleExport = async (options) => {
const mergedOptions = {
format: props.exportOptions.format || "pdf",
quality: props.exportOptions.quality,
filename: props.exportOptions.filename,
paperSize: props.exportOptions.paperSize,
orientation: props.exportOptions.orientation,
scale: props.exportOptions.scale,
margin: props.exportOptions.margin,
...options
};
emit("export-start", mergedOptions.format);
try {
const result = await exportChart(mergedOptions);
if (result.success) {
emit("export-success", result);
} else {
emit("export-error", result.error || "Unknown error");
}
return result;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
emit("export-error", errorMessage);
return {
success: false,
data: null,
error: errorMessage,
filename: mergedOptions.filename || "export-error"
};
}
};
const selectedExportFormat = ref("");
const triggerExport = async () => {
if (!selectedExportFormat.value || isExporting.value) return;
const options = {
...props.exportOptions,
format: selectedExportFormat.value
};
try {
const result = await handleExport(options);
downloadExport(result);
selectedExportFormat.value = "";
} catch (error) {
console.error("Error during export:", error);
}
};
const handleImport = (result) => {
if (result.success && result.data) {
if (result.data.rows && result.data.rows.length > 0) {
rowManager.updateRows(result.data.rows);
}
emit("import-data", result);
}
};
const closeImporter = () => {
isImporterVisible.value = false;
};
const rows = computed(() => rowManager.rows.value);
const rowsContainerStyle = computed(() => {
if (props.maxRows === 0) return {};
return {
"max-height": `${props.maxRows * props.rowHeight}px`,
"overflow-y": "auto"
};
});
const totalWidth = computed(() => {
const lowerUnits = timeaxisUnits.value.result.lowerUnits;
return lowerUnits.reduce((total, unit) => {
return total + parseInt(unit.width);
}, 0);
});
const hasGroupRows = computed(() => {
const checkForGroups = (rows2) => {
return rows2.some(
(row) => row.children && row.children.length > 0 || row.children && checkForGroups(row.children)
);
};
return checkForGroups(rows.value);
});
const labelSectionStyle = computed(() => ({
width: `${labelSectionWidth.value}px`,
maxWidth: `${labelSectionWidth.value}px`,
position: "relative",
flexShrink: 0,
flexGrow: 0
}));
const handleTimeaxisMouseDown = (e) => {
isDraggingTimeaxis.value = true;
lastMouseX.value = e.clientX;
};
const handleTimeaxisMouseMove = (e) => {
if (!isDraggingTimeaxis.value || !ganttWrapper.value) return;
const deltaX = e.clientX - lastMouseX.value;
lastMouseX.value = e.clientX;
ganttWrapper.value.scrollLeft -= deltaX;
const maxScroll = ganttWrapper.value.scrollWidth - ganttWrapper.value.clientWidth;
scrollPosition.value = ganttWrapper.value.scrollLeft / maxScroll * 100;
};
const handleTimeaxisMouseUp = () => {
isDraggingTimeaxis.value = false;
};
const handleTimeaxisTouchStart = (e) => {
const touch = e.touches[0];
if (!touch) return;
handleTimeaxisTouch.isDragging = true;
handleTimeaxisTouch.startX = touch.clientX;
e.preventDefault();
};
const handleTimeaxisTouchMove = (e) => {
if (!handleTimeaxisTouch.isDragging || !ganttWrapper.value) return;
const touch = e.touches[0];
if (!touch) return;
const deltaX = touch.clientX - handleTimeaxisTouch.startX;
handleTimeaxisTouch.startX = touch.clientX;
ganttWrapper.value.scrollLeft -= deltaX;
const maxScroll = ganttWrapper.value.scrollWidth - ganttWrapper.value.clientWidth;
scrollPosition.value = ganttWrapper.value.scrollLeft / maxScroll * 100;
e.preventDefault();
};
const handleTimeaxisTouchEnd = () => {
handleTimeaxisTouch.isDragging = false;
};
const handleSectionResize = (newWidth) => {
labelSectionWidth.value = newWidth;
};
const emitBarEvent = (e, bar, datetime, movedBars) => {
switch (e.type) {
case "click":
emit("click-bar", { bar, e, datetime });
break;
case "mousedown":
emit("mousedown-bar", { bar, e, datetime });
break;
case "mouseup":
emit("mouseup-bar", { bar, e, datetime });
break;
case "dblclick":
emit("dblclick-bar", { bar, e, datetime });
break;
case "mouseenter":
initTooltip(bar);
emit("mouseenter-bar", { bar, e });
break;
case "mouseleave":
clearTooltip();
emit("mouseleave-bar", { bar, e });
break;
case "dragstart":
isDragging.value = true;
emit("dragstart-bar", { bar, e });
updateBarPositions();
break;
case "drag":
emit("drag-bar", { bar, e });
updateBarPositions();
break;
case "dragend":
isDragging.value = false;
emit("dragend-bar", { bar, e, movedBars });
updateBarPositions();
rowManager.onBarMove();
break;
case "contextmenu":
emit("contextmenu-bar", { bar, e, datetime });
break;
case "progress-drag-start":
initTooltip(bar);
emit("progress-drag-start", { bar, e });
break;
case "progress-change":
initTooltip(bar);
emit("progress-change", { bar, e });
break;
case "progress-drag-end":
initTooltip(bar);
emit("progress-drag-end", { bar, e });
rowManager.onBarMove();
break;
case "label-edit":
emit("label-edit", {
bar,
e,
oldValue: bar.ganttBarConfig._previousLabel || "",
newValue: bar.ganttBarConfig.label || ""
});
rowManager.onBarMove();
break;
}
};
const dropRow = (event) => {
emit("row-drop", event);
updateBarPositions();
};
const handleRangeSelection = (event) => {
emit("range-selection", event);
};
const getColorScheme = (scheme) => typeof scheme !== "string" ? scheme : colorSchemes[scheme] || colorSchemes.default;
watch(
() => totalWidth.value,
() => {
ganttWidth.value = totalWidth.value;
},
{ immediate: true }
);
const renderRow = (row) => {
if (row._originalNode) {
return h(
_sfc_main$3,
{
...row._originalNode.props,
label: row.label,
bars: row.bars,
children: row.children,
id: row.id,
key: row.id || row.label,
onRangeSelection: handleRangeSelection
},
row._originalNode.children || {}
);
}
return h(_sfc_main$3, {
label: row.label,
bars: row.bars,
id: row.id,
key: row.id || row.label,
children: row.children,
connections: row.connections,
onRangeSelection: handleRangeSelection
});
};
const updateRangeBackground = () => {
const parentElement = document.getElementById(id.value);
const slider = parentElement.querySelector(".g-gantt-scroller");
if (slider) {
slider.style.setProperty("--value", `${scrollPosition.value}%`);
}
};
const undo = () => {
const changes = rowManager.undo();
if (!changes) return;
changes.rowChanges.forEach((rowChange) => {
emit("row-drop", {
sourceRow: rowChange.sourceRow,
targetRow: void 0,
newIndex: rowChange.newIndex,
parentId: rowChange.newParentId
});
});
changes.barChanges.forEach((barChange) => {
const bar = findBarInRows(rowManager.rows.value, barChange.barId);
if (!bar) return;
emit("dragend-bar", {
bar,
e: new MouseEvent("mouseup"),
movedBars: /* @__PURE__ */ new Map([
[
bar,
{
oldStart: barChange.newStart,
oldEnd: barChange.newEnd
}
]
])
});
});
updateBarPositions();
};
const redo = () => {
const changes = rowManager.redo();
if (!changes) return;
changes.rowChanges.forEach((rowChange) => {
emit("row-drop", {
sourceRow: rowChange.sourceRow,
targetRow: void 0,
newIndex: rowChange.newIndex,
parentId: rowChange.newParentId
});
});
changes.barChanges.forEach((barChange) => {
const bar = findBarInRows(rowManager.rows.value, barChange.barId);
if (!bar) return;
emit("dragend-bar", {
bar,
e: new MouseEvent("mouseup"),
movedBars: /* @__PURE__ */ new Map([
[
bar,
{
oldStart: barChange.oldStart,
oldEnd: barChange.oldEnd
}
]
])
});
});
updateBarPositions();
};
const handleKeyboardShortcuts = (e) => {
const isCtrlPressed = e.ctrlKey || e.metaKey;
if (isCtrlPressed && e.code === "KeyZ") {
e.preventDefault();
if (e.shiftKey) {
if (rowManager.canRedo.value) {
redo();
}
} else {
if (rowManager.canUndo.value) {
undo();
}
}
}
};
let resizeObserver;
onMounted(() => {
const cleanup = rowManager.onSortChange(updateBarPositions);
const cleanupGroup = rowManager.onGroupExpansionChange(updateBarPositions);
onUnmounted(cleanup);
onUnmounted(cleanupGroup);
if (ganttWrapper.value) {
ganttWrapper.value.addEventListener("wheel", (e) => handleWheel(e, ganttWrapper.value));
}
window.addEventListener("mousemove", handleTimeaxisMouseMove);
window.addEventListener("mouseup", handleTimeaxisMouseUp);
window.addEventListener("mousemove", (e) => handleResizeMove(e, handleSectionResize));
window.addEventListener("mouseup", handleResizeEnd);
window.addEventListener("keydown", handleKeyboardShortcuts);
resizeObserver = new ResizeObserver(updateBarPositions);
const container = document.querySelector(".g-gantt-chart");
if (container) {
resizeObserver.observe(container);
}
window.addEventListener("resize", updateBarPositions);
initializeConnections();
nextTick(() => {
updateBarPositions();
});
watch(scrollPosition, updateRangeBackground, { immediate: true });
});
onUnmounted(() => {
if (ganttWrapper.value) {
ganttWrapper.value.removeEventListener("wheel", (e) => handleWheel(e, ganttWrapper.value));
}
window.removeEventListener("mousemove", handleTimeaxisMouseMove);
window.removeEventListener("mouseup", handleTimeaxisMouseUp);
window.removeEventListener("mousemove", (e) => handleResizeMove(e, handleSectionResize));
window.removeEventListener("mouseup", handleResizeEnd);
window.removeEventListener("keydown", handleKeyboardShortcuts);
if (resizeObserver) {
resizeObserver.disconnect();
}
window.removeEventListener("resize", updateBarPositions);
});
watch([() => props.chartStart, () => props.chartEnd], () => {
updateBarPositions();
});
watch(
() => props.showImporter,
(newValue) => {
isImporterVisible.value = newValue;
}
);
watch(
() => props.baseUnitWidth,
(newValue) => {
validatedBaseUnitWidth.value = Math.max(20, newValue);
}
);
watch(
() => props.defaultZoom,
(newValue) => {
validatedDefaultZoom.value = Math.min(10, Math.max(1, newValue));
}
);
provide(CONFIG_KEY, {
...toRefs(props),
baseUnitWidth: validatedBaseUnitWidth,
defaultZoom: validatedDefaultZoom,
colors,
chartSize
});
provide(EMIT_BAR_EVENT_KEY, emitBarEvent);
provide(BOOLEAN_KEY, { ...props });
provide(GANTT_ID_KEY, id.value);
provide(CHART_AREA_KEY, ganttChart);
provide(CHART_WRAPPER_KEY, ganttWrapper);
__expose({
exportChart,
isExporting
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "g-gantt-container",
role: "application",
"aria-label": "Interactive Gantt",
tabindex: "0",
onKeydown: _cache[20] || (_cache[20] = //@ts-ignore
(...args) => unref(handleKeyDown) && unref(handleKeyDown)(...args)),
onMousemove: handleChartMouseMove,
onMouseup: handleChartMouseUp,
ref_key: "ganttContainer",
ref: ganttContainer,
id: id.value
}, [
createElementVNode("div", {
class: "g-gantt-rounded-wrapper",
ref_key: "gGantt",
ref: gGantt
}, [
createElementVNode("div", _hoisted_2, [
_ctx.labelColumnTitle ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-gantt-label-section",
style: normalizeStyle(labelSectionStyle.value)
}, [
createVNode(_sfc_main$a, {
ref_key: "labelColumn",
ref: labelColumn,
onScroll: unref(handleLabelScroll),
onRowDrop: dropRow
}, createSlots({ _: 2 }, [
renderList(_ctx.$slots, (_, name) => {
return {
name,
fn: withCtx((slotData) => [
renderSlot(_ctx.$slots, name, normalizeProps(guardReactiveProps(slotData)))
])
};
})
]), 1032, ["onScroll"]),
createElementVNode("div", {
class: "g-gantt-section-resizer",
onMousedown: _cache[0] || (_cache[0] = (e) => unref(handleResizeStart)(e, labelSectionWidth.value)),
onTouchstart: _cache[1] || (_cache[1] = (e) => unref(handleTouchStart)(e, labelSectionWidth.value)),
onTouchmove: _cache[2] || (_cache[2] = (e) => unref(handleTouchMove)(e, handleSectionResize)),
onTouchend: _cache[3] || (_cache[3] = //@ts-ignore
(...args) => unref(resetResizeState) && unref(resetResizeState)(...args)),
onTouchcancel: _cache[4] || (_cache[4] = //@ts-ignore
(...args) => unref(resetResizeState) && unref(resetResizeState)(...args))
}, null, 32)
], 4)) : createCommentVNode("", true),
createElementVNode("div", {
ref_key: "ganttWrapper",
ref: ganttWrapper,
class: "gantt-wrapper",
style: normalizeStyle({
width: "100%",
"overflow-x": _ctx.commands ? "hidden" : "auto"
})
}, [
createElementVNode("div", {
ref_key: "ganttChart",
ref: ganttChart,
class: "g-gantt-chart",
style: normalizeStyle({
width: `${totalWidth.value}px`,
background: colors.value.background,
fontFamily: unref(font)
})
}, [
!_ctx.hideTimeaxis ? (openBlock(), createBlock(_sfc_main$8, {
key: 0,
ref_key: "timeaxisComponent",
ref: timeaxisComponent,
onDragStart: handleTimeaxisMouseDown,
onTouchstart: handleTimeaxisTouchStart,
onTouchmove: handleTimeaxisTouchMove,
onTouchend: handleTimeaxisTouchEnd,
onTouchcancel: handleTimeaxisTouchEnd,
timeaxisUnits: unref(timeaxisUnits),
internalPrecision: unref(internalPrecision)
}, {
"upper-timeunit": withCtx((slotProps) => [
renderSlot(_ctx.$slots, "upper-timeunit", normalizeProps(guardReactiveProps(slotProps)))
]),
timeunit: withCtx((slotProps) => [
renderSlot(_ctx.$slots, "timeunit", normalizeProps(guardReactiveProps(slotProps)))
]),
"holiday-tooltip": withCtx((slotProps) => [
renderSlot(_ctx.$slots, "holiday-tooltip", normalizeProps(guardReactiveProps(slotProps)))
]),
"event-tooltip": withCtx((slotProps) => [
renderSlot(_ctx.$slots, "event-tooltip", normalizeProps(guardReactiveProps(slotProps)))
]),
"timeaxis-event": withCtx((slotProps) => [
renderSlot(_ctx.$slots, "timeaxis-event", normalizeProps(guardReactiveProps(slotProps)))
]),
_: 3
}, 8, ["timeaxisUnits", "internalPrecision"])) : createCommentVNode("", true),
_ctx.grid ? (openBlock(), createBlock(_sfc_main$b, {
key: 1,
timeaxisUnits: unref(timeaxisUnits),
internalPrecision: unref(internalPrecision)
}, null, 8, ["timeaxisUnits", "internalPrecision"])) : createCommentVNode("", true),
_ctx.currentTime ? (openBlock(), createBlock(_sfc_main$7, { key: 2 }, {
"current-time-label": withCtx(() => [
renderSlot(_ctx.$slots, "current-time-label")
]),
_: 3
})) : createCommentVNode("", true),
_ctx.pointerMarker ? (openBlock(), createBlock(_sfc_main$2, { key: 3 }, {
"pointer-marker-tooltips": withCtx(({ hitBars, datetime }) => [
renderSlot(_ctx.$slots, "pointer-marker-tooltips", normalizeProps(guardReactiveProps({ hitBars, datetime })))
]),
_: 3
})) : createCommentVNode("", true),
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.milestones, (milestone) => {
return openBlock(), createBlock(_sfc_main$5, {
key: milestone.date.toString(),
milestone
}, createSlots({ _: 2 }, [
renderList(_ctx.$slots, (_, name) => {
return {
name,
fn: withCtx((slotData) => [
name.startsWith("milestone-") || name === "milestone" ? renderSlot(_ctx.$slots, name, mergeProps({
key: 0,
ref_for: true
}, slotData)) : createCommentVNode("", true)
])
};
})
]), 1032, ["milestone"]);
}), 128)),
createElementVNode("div", {
class: "g-gantt-rows-container",
style: normalizeStyle(rowsContainerStyle.value),
ref_key: "rowsContainer",
ref: rowsContainer,
onScroll: _cache[5] || (_cache[5] = //@ts-ignore
(...args) => unref(handleContentScroll) && unref(handleContentScroll)(...args))
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(rows.value, (row) => {
return openBlock(), createBlock(resolveDynamicComponent(renderRow(row)), {
key: row.id || row.label
});
}), 128)),
unref(connectionState).isCreating && previewLinePoints.value ? (openBlock(), createElementBlock("svg", _hoisted_3, [
previewLinePoints.value ? (openBlock(), createBlock(GGanttConnector, {
key: 0,
"source-bar": {
id: unref(connectionState).sourceBar.ganttBarConfig.id,
x: previewLinePoints.value.x1,
y: previewLinePoints.value.y1,
width: 0,
height: 0
},
"target-bar": {
id: "preview",
x: previewLinePoints.value.x2,
y: previewLinePoints.value.y2,
width: 0,
height: 0
},
type: "straight",
color: _ctx.defaultConnectionColor,
pattern: _ctx.defaultConnectionPattern,
animated: _ctx.defaultConnectionAnimated,
"animation-speed": _ctx.defaultConnectionAnimationSpeed,
relation: _ctx.defaultConnectionRelation,
style: { opacity: 0.6 },
marker: _ctx.markerConnection
}, null, 8, ["source-bar", "target-bar", "color", "pattern", "animated", "animation-speed", "relation", "marker"])) : createCommentVNode("", true)
])) : createCommentVNode("", true),
_ctx.enableConnections ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(connections), (conn) => {
return openBlock(), createElementBlock(Fragment, {
key: `${conn.sourceId}-${conn.targetId}`
}, [
unref(barPositions).get(conn.sourceId) && unref(barPositions).get(conn.targetId) ? (openBlock(), createBlock(GGanttConnector, mergeProps({
key: 0,
ref_for: true
}, unref(getConnectorProps)(conn), {
marker: _ctx.markerConnection,
onClick: ($event) => unref(handleConnectionClick)(conn)
}), null, 16, ["marker", "onClick"])) : createCommentVNode("", true)
], 64);
}), 128)) : createCommentVNode("", true)
], 36)
], 4)
], 4)
]),
_ctx.commands ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-gantt-command",
style: normalizeStyle({ background: colors.value.commands, fontFamily: unref(font) }),
"aria-label": "Gantt Commands"
}, [
renderSlot(_ctx.$slots, "commands", {
zoomIn: () => unref(handleZoomUpdate)(true),
zoomOut: () => unref(handleZoomUpdate)(false),
scrollRowUp: () => unref(scrollRowUp)(),
scrollRowDown: () => unref(scrollRowDown)(),
expandAllGroups: () => unref(rowManager).expandAllGroups(),
collapseAllGroups: () => unref(rowManager).collapseAllGroups(),
handleToStart: () => unref(handleStep)(0, ganttWrapper.value),
handleBack: () => unref(handleStep)(unref(scrollPosition) - 10, ganttWrapper.value),
handleScroll: () => unref(handleScroll)(ganttWrapper.value),
handleForward: () => unref(handleStep)(unref(scrollPosition) + 10, ganttWrapper.value),
handleToEnd: () => unref(handleStep)(100, ganttWrapper.value),
undo: () => undo(),
redo: () => redo(),
canUndo: unref(rowManager).canUndo,
canRedo: unref(rowManager).canRedo,
isAtTop: unref(isAtTop),
isAtBottom: unref(isAtBottom),
zoomLevel: unref(zoomLevel),
export: () => triggerExport()
}, () => [
createElementVNode("div", _hoisted_4, [
_ctx.maxRows > 0 ? (openBlock(), createElementBlock("div", _hoisted_5, [
createElementVNode("button", {
onClick: _cache[6] || (_cache[6] = //@ts-ignore
(...args) => unref(scrollRowUp) && unref(scrollRowUp)(...args)),
"aria-label": "Scroll row up",
disabled: unref(isAtTop)
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faAngleUp),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_6),
createElementVNode("button", {
onClick: _cache[7] || (_cache[7] = //@ts-ignore
(...args) => unref(scrollRowDown) && unref(scrollRowDown)(...args)),
"aria-label": "Scroll row down",
disabled: unref(isAtBottom)
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faAngleDown),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_7)
])) : createCommentVNode("", true),
hasGroupRows.value ? (openBlock(), createElementBlock("div", _hoisted_8, [
createElementVNode("button", {
onClick: _cache[8] || (_cache[8] = ($event) => unref(rowManager).expandAllGroups()),
"aria-label": "Expand all groups",
disabled: unref(rowManager).areAllGroupsExpanded.value
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faExpandAlt),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_9),
createElementVNode("button", {
onClick: _cache[9] || (_cache[9] = ($event) => unref(rowManager).collapseAllGroups()),
"aria-label": "Collapse all groups",
disabled: unref(rowManager).areAllGroupsCollapsed.value
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faCompressAlt),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_10)
])) : createCommentVNode("", true)
]),
createElementVNode("div", _hoisted_11, [
createElementVNode("div", _hoisted_12, [
createElementVNode("button", {
disabled: unref(scrollPosition) === 0,
onClick: _cache[10] || (_cache[10] = ($event) => unref(handleStep)(0, ganttWrapper.value)),
"aria-label": "Scroll to start"
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faAnglesLeft),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_13),
createElementVNode("button", {
disabled: unref(scrollPosition) === 0,
onClick: _cache[11] || (_cache[11] = ($event) => unref(handleStep)(unref(scrollPosition) - 10, ganttWrapper.value)),
"aria-label": "Scroll back"
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faAngleLeft),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_14),
withDirectives(createElementVNode("input", {
"onUpdate:modelValue": _cache[12] || (_cache[12] = ($event) => isRef(scrollPosition) ? scrollPosition.value = $event : null),
type: "range",
min: "0",
max: "100",
class: "g-gantt-scroller",
style: normalizeStyle({ "--value": `${unref(scrollPosition)}%` }),
onInput: _cache[13] || (_cache[13] = ($event) => unref(handleScroll)(ganttWrapper.value)),
"aria-valuemin": 0,
"aria-valuemax": 100,
"aria-valuenow": unref(scrollPosition),
"aria-label": "Gantt scroll position"
}, null, 44, _hoisted_15), [
[vModelText, unref(scrollPosition)]
]),
createElementVNode("button", {
disabled: unref(scrollPosition) === 100,
onClick: _cache[14] || (_cache[14] = ($event) => unref(handleStep)(unref(scrollPosition) + 10, ganttWrapper.value)),
"aria-label": "Scroll up"
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faAngleRight),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_16),
createElementVNode("button", {
disabled: unref(scrollPosition) === 100,
onClick: _cache[15] || (_cache[15] = ($event) => unref(handleStep)(100, ganttWrapper.value)),
"aria-label": "Scroll to end"
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faAnglesRight),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_17)
])
]),
createElementVNode("div", _hoisted_18, [
createElementVNode("div", _hoisted_19, [
createElementVNode("button", {
onClick: _cache[16] || (_cache[16] = () => unref(handleZoomUpdate)(false)),
"aria-label": "Zoom-out Gantt",
disabled: unref(zoomLevel) === 1 && unref(internalPrecision) === "month"
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faMagnifyingGlassMinus),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_20),
createElementVNode("button", {
onClick: _cache[17] || (_cache[17] = () => unref(handleZoomUpdate)(true)),
"aria-label": "Zoom-out Gantt",
disabled: unref(zoomLevel) === 10 && unref(internalPrecision) === _ctx.precision
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faMagnifyingGlassPlus),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_21)
]),
createElementVNode("div", _hoisted_22, [
createElementVNode("button", {
onClick: undo,
disabled: !unref(rowManager).canUndo.value,
"aria-label": "Undo last action"
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faUndo),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_23),
createElementVNode("button", {
onClick: redo,
disabled: !unref(rowManager).canRedo.value,
"aria-label": "Redo action"
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faRedo),
class: "command-icon"
}, null, 8, ["icon"])
], 8, _hoisted_24)
])
]),
createElementVNode("div", _hoisted_25, [
_ctx.exportEnabled ? (openBlock(), createElementBlock("div", _hoisted_26, [
createElementVNode("div", _hoisted_27, [
withDirectives(createElementVNode("select", {
"onUpdate:modelValue": _cache[18] || (_cache[18] = ($event) => selectedExportFormat.value = $event),
class: "g-gantt-export-select",
disabled: unref(isExporting)
}, _cache[21] || (_cache[21] = [
createStaticVNode('<option value="" disabled>Export</option><option value="pdf">PDF</option><option value="png">PNG</option><option value="svg">SVG</option><option value="excel">Excel</option>', 5)
]), 8, _hoisted_28), [
[vModelSelect, selectedExportFormat.value]
]),
createElementVNode("button", {
onClick: triggerExport,
disabled: !selectedExportFormat.value || unref(isExporting)
}, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faFileExport),
class: "command-icon"
}, null, 8, ["icon"]),
unref(isExporting) ? (openBlock(), createElementBlock("span", _hoisted_30, [
createVNode(unref(FontAwesomeIcon), {
icon: unref(faSpinner),
class: "fa-spin"
}, null, 8, ["icon"])
])) : createCommentVNode("", true)
], 8, _hoisted_29)
])
])) : createCommentVNode("", true)
])
])
], 4)) : createCommentVNode("", true)
], 512),
createVNode(_sfc_main$9, {
type: "bar",
"model-value": unref(showTooltip) || isDragging.value,
bar: unref(tooltipBar)
}, {
default: withCtx((slotProps) => [
renderSlot(_ctx.$slots, "bar-tooltip", normalizeProps(guardReactiveProps(slotProps)))
]),
_: 3
}, 8, ["model-value", "bar"]),
createVNode(_sfc_main$1, {
modelValue: isImporterVisible.value,
"onUpdate:modelValue": _cache[19] || (_cache[19] = ($event) => isImporterVisible.value = $event),
title: props.importerTitle,
"default-format": props.importerDefaultFormat,
"allowed-formats": props.importerAllowedFormats,
onImport: handleImport,
onClose: closeImporter
}, null, 8, ["modelValue", "title", "default-format", "allowed-formats"])
], 40, _hoisted_1);
};
}
});
function extendDayjs() {
dayjs.extend(isSameOrBefore);
dayjs.extend(isSameOrAfter);
dayjs.extend(isBetween);
dayjs.extend(customParseFormat);
dayjs.extend(weekOfYear);
dayjs.extend(isoWeek);
dayjs.extend(advancedFormat);
dayjs.extend(dayOfYear);
dayjs.extend(localizedFormat);
dayjs.extend(utc);
}
const hyvuegantt = {
install(app) {
extendDayjs();
app.component("GGanttChart", _sfc_main);
app.component("GGanttRow", _sfc_main$3);
}
};
function injectStyle(css, insertAt = "top") {
if (!css || typeof document === "undefined") return;
const head = document.head || document.querySelector("head");
const firstChild = head.querySelector(":first-child");
const style = document.createElement("style");
style.appendChild(document.createTextNode(css));
if (insertAt === "top" && firstChild) {
head.insertBefore(style, firstChild);
} else {
head.appendChild(style);
}
}
injectStyle('\n.g-gantt-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n/* Layout */\n.g-gantt-chart {\n position: relative;\n display: flex;\n flex-direction: column;\n overflow-x: hidden;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n font-feature-settings: "tnum";\n font-variant-numeric: tabular-nums;\n}\n\n/* Container Styles */\n.g-gantt-rows-container {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: visible;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.labels-in-column {\n display: flex;\n flex-direction: row;\n}\n\n/* Command Section Styles */\n.g-gantt-command {\n display: flex;\n align-items: center;\n height: 40px;\n border-top: 1px solid #eaeaea;\n padding: 0px 6px;\n gap: 8px;\n}\n.g-gantt-command-block {\n display: flex;\n gap: 8px;\n}\n.g-gantt-command-fixed,\n.g-gantt-command-slider,\n.g-gantt-command-vertical,\n.g-gantt-command-zoom,\n.g-gantt-command-history,\n.g-gantt-command-groups,\n.g-gantt-export-container {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n.g-gantt-command-custom {\n flex-grow: 1;\n}\n.g-gantt-command-vertical button:disabled,\n.g-gantt-command-slider button:disabled,\n.g-gantt-command-zoom button:disabled,\n.g-gantt-command-groups button:disabled,\n.g-gantt-command-history button:disabled,\n.g-gantt-export-container button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.g-gantt-export-select {\n border-radius: 4px;\n font-size: 0.75rem;\n padding-left: 2px;\n padding-right: 2px;\n padding-top: 0;\n padding-bottom: 0;\n height: 22px;\n}\n.g-gantt-export-loading {\n margin-left: 4px;\n}\n.g-gantt-export-menu button:hover {\n background: #f5f5f5;\n}\n@media screen and (max-width: 768px) {\n.g-gantt-command {\n height: auto;\n min-height: 0;\n min-height: initial;\n flex-direction: column;\n align-items: stretch;\n padding: 12px 6px;\n gap: 12px;\n}\n.g-gantt-command > * {\n width: 100%;\n}\n.g-gantt-command-block {\n display: flex;\n justify-content: center;\n gap: 20px;\n}\n.g-gantt-command-fixed {\n flex-direction: column;\n gap: 8px;\n}\n.g-gantt-command-groups {\n justify-content: center;\n margin-right: 0;\n}\n.g-gantt-command-slider {\n width: 100%;\n justify-content: center;\n}\n.g-gantt-command-vertical {\n flex-direction: row;\n justify-content: center;\n}\n.g-gantt-command-zoom,\n .g-gantt-command-history {\n justify-content: center;\n}\n.g-gantt-command-export {\n display: flex;\n align-items: center;\n}\n.command-icon {\n padding: 8px;\n width: 16px;\n height: 16px;\n}\n.g-gantt-scroller {\n height: 12px;\n}\n.g-gantt-scroller::-webkit-slider-thumb {\n width: 24px;\n height: 24px;\n}\n.g-gantt-scroller::-moz-range-thumb {\n width: 24px;\n height: 24px;\n}\n}\n\n/* Scroller Styles */\n.g-gantt-scroller {\n -webkit-appearance: none;\n height: 8px;\n border-radius: 4px;\n outline: none;\n background: linear-gradient(\n to right,\n var(--1fed23a5) var(--value),\n #ddd var(--value)\n );\n}\n\n/* Scroller Thumb Styles */\n.g-gantt-scroller::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 16px;\n height: 16px;\n background: var(--1fed23a5);\n border-radius: 50%;\n cursor: pointer;\n border: none;\n}\n.g-gantt-scroller::-moz-range-thumb {\n width: 16px;\n height: 16px;\n background: var(--1fed23a5);\n border-radius: 50%;\n cursor: pointer;\n border: none;\n}\n\n/* Track Styles */\n.g-gantt-scroller::-moz-range-track {\n height: 8px;\n background: #ddd;\n border-radius: 4px;\n border: none;\n}\n\n/* Hover States */\n.g-gantt-scroller::-webkit-slider-thumb:hover {\n background: var(--1fed23a5);\n}\n.g-gantt-scroller::-moz-range-thumb:hover {\n background: var(--1fed23a5);\n}\n\n/* Icon Styles */\n.command-icon {\n background: var(--1fed23a5);\n padding: 4px;\n margin: 2px;\n width: 14px;\n height: 14px;\n border-radius: 4px;\n}\nbutton {\n display: flex;\n padding: 0;\n background-color: transparent;\n background-image: none;\n border: 0;\n color: var(--787ffb4a);\n}\n.g-gantt-chart:focus-within {\n outline: 2px solid var(--31d9cbd8);\n outline-offset: 2px;\n}\n.g-gantt-rounded-wrapper {\n border-radius: 5px;\n overflow: hidden;\n border: 1px solid #eaeaea;\n background: white;\n display: flex;\n flex-direction: column;\n}\n.g-gantt-rows-container::-webkit-scrollbar {\n display: none;\n}\n.g-gantt-main-layout {\n display: flex;\n width: 100%;\n position: relative;\n}\n.g-gantt-label-section {\n position: relative;\n display: flex;\n}\n.g-gantt-section-resizer {\n position: absolute;\n right: -4px;\n top: 0;\n width: 8px;\n height: 100%;\n cursor: col-resize;\n background: transparent;\n z-index: 10;\n transition: background 0.2s ease;\n}\n.g-gantt-section-resizer:hover {\n background: rgba(0, 0, 0, 0.1);\n}\n.g-gantt-section-resizer:active {\n background: rgba(0, 0, 0, 0.2);\n}\n@media (max-width: 768px) {\n.g-gantt-section-resizer {\n width: 16px;\n right: -8px;\n}\n}\n.connection-preview {\n pointer-events: none;\n}\n\n.g-gantt-row {\n width: 100%;\n transition: background 0.4s;\n position: relative;\n}\n\n/*.g-gantt-row:last-child {\n border-bottom: 0px !important;\n}*/\n.g-gantt-row > .g-gantt-row-bars-container {\n position: relative;\n width: 100%;\n}\n.g-gantt-row-label {\n position: absolute;\n top: 0;\n left: 0px;\n padding: 0px 8px;\n display: flex;\n align-items: center;\n height: 60%;\n min-height: 20px;\n font-size: 0.8em;\n font-weight: bold;\n border-bottom-right-radius: 6px;\n background: #f2f2f2;\n z-index: 3;\n box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.6);\n}\n.g-gantt-row-group-label {\n font-weight: bold;\n background: #e0e0e0 !important;\n}\n.g-gantt-row-children {\n transition: max-height 0.3s ease-in-out;\n}\n.bar-transition-leave-active,\n.bar-transition-enter-active {\n transition: all 0.2s;\n}\n.bar-transition-enter-from,\n.bar-transition-leave-to {\n transform: scale(0.8);\n opacity: 0;\n}\n.g-gantt-range-selection {\n border-radius: 2px;\n box-sizing: border-box;\n}\n.g-gantt-row-bars-container {\n cursor: crosshair;\n}\n.g-gantt-row-bars-container:has(.g-gantt-bar) {\n cursor: default;\n}\n\n.g-gantt-bar {\n display: flex;\n justify-content: center;\n align-items: center;\n background: cadetblue;\n overflow: visible;\n position: relative;\n}\n.g-gantt-bar-label {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n padding: 0 14px 0 14px;\n display: flex;\n justify-content: center;\n align-items: center;\n position: relative;\n z-index: 2;\n pointer-events: none;\n}\n.g-gantt-bar-label > * {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.g-gantt-bar-handle-left,\n.g-gantt-bar-handle-right {\n position: absolute;\n width: 10px;\n height: 100%;\n background: white;\n opacity: 0.7;\n border-radius: 0px;\n cursor: ew-resize;\n top: 0;\n}\n.g-gantt-bar-handle-left {\n left: 0;\n}\n.g-gantt-bar-handle-right {\n right: 0;\n}\n.g-gantt-bar-label img {\n pointer-events: none;\n}\n.g-gantt-bar-label-edit {\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n pointer-events: all;\n}\n.g-gantt-bar-label-input {\n background: rgba(255, 255, 255, 0.2);\n border-radius: 4px;\n width: 100%;\n height: 100%;\n background: white;\n color: black;\n outline: none;\n font: inherit;\n padding: 2px;\n text-align: center;\n}\n.is-group-bar {\n background: transparent !important;\n}\n.group-bar-decoration {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n}\n.g-gantt-progress-bar {\n position: absolute;\n pointer-events: none;\n overflow: hidden;\n min-width: 8px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n /*padding-right: 10px;*/\n color: #fff;\n font-size: 0.8em;\n font-weight: 500;\n z-index: 1;\n}\n.progress-text {\n padding-right: 10px;\n}\n.g-gantt-progress-handle {\n position: absolute;\n top: 0;\n width: 8px;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.3);\n cursor: ew-resize;\n pointer-events: all;\n transition: background-color 0.2s ease;\n z-index: 3;\n}\n.g-gantt-progress-handle:hover {\n background-color: rgba(0, 0, 0, 0.5);\n}\n.g-gantt-progress-handle:active {\n background-color: rgba(0, 0, 0, 0.7);\n}\n.connection-point {\n z-index: 10;\n}\n.connection-point:hover {\n transform: translate(-50%, -50%) scale(1.2);\n}\n.connection-point.end:hover {\n transform: translate(50%, -50%) scale(1.2);\n}\n\n.g-timeaxis {\n position: sticky;\n top: 0;\n width: 100%;\n height: 80px;\n background: white;\n z-index: 4;\n display: flex;\n flex-direction: column;\n cursor: grab;\n}\n.g-timeaxis:active {\n cursor: grabbing;\n}\n.g-timeunits-container {\n display: flex;\n width: 100%;\n height: 50%;\n}\n.g-timeunit {\n height: 100%;\n font-size: 65%;\n display: flex;\n justify-content: center;\n}\n.g-upper-timeunit {\n display: flex;\n height: 100%;\n justify-content: center;\n align-items: center;\n}\n.g-timeaxis-hour-pin {\n width: 1px;\n height: 10px;\n}\n.label-unit {\n flex-grow: 1;\n text-align: center;\n line-height: normal;\n}\n.g-timeunit-min {\n display: flex;\n flex-direction: row-reverse;\n align-items: center;\n width: 100%;\n line-height: 20px;\n}\n.g-timeunit-step {\n display: flex;\n width: 100%;\n line-height: 20px;\n}\n.g-events-container {\n display: flex;\n width: 100%;\n position: relative;\n overflow: hidden;\n height: 100%;\n}\n.g-timeaxis-event {\n height: 100%;\n position: absolute;\n font-size: 65%;\n padding: 0 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border: 1px solid;\n box-sizing: border-box;\n cursor: pointer;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);\n transition: box-shadow 0.2s ease;\n}\n.g-timeaxis-event:hover {\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n z-index: 5;\n}\n\n.g-grid-container {\n position: absolute;\n top: 0;\n left: 0%;\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: space-between;\n}\n.g-grid-line {\n width: 1px;\n height: 100%;\n}\n.g-grid-line:first-child {\n border-left: 0px !important;\n}\n\n/* Bar Tooltip Styles */\n.g-gantt-tooltip {\n position: fixed;\n background: black;\n color: white;\n z-index: 4;\n font-size: 0.85em;\n padding: 5px;\n border-radius: 3px;\n transition: opacity 0.2s;\n display: flex;\n align-items: center;\n font-feature-settings: "tnum";\n font-variant-numeric: tabular-nums;\n}\n.g-gantt-tooltip:before {\n content: "";\n position: absolute;\n top: 0;\n left: 10%;\n width: 0;\n height: 0;\n border: 10px solid transparent;\n border-bottom-color: black;\n border-top: 0;\n margin-left: -5px;\n margin-top: -5px;\n}\n.g-gantt-tooltip-color-dot {\n width: 8px;\n height: 8px;\n border-radius: 100%;\n margin-right: 4px;\n}\n\n/* Event Tooltip Styles */\n.g-gantt-event-tooltip {\n position: fixed;\n z-index: 1000;\n min-width: 180px;\n max-width: 280px;\n border-radius: 4px;\n padding: 8px 12px;\n font-size: 12px;\n transform: translateX(-50%);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n pointer-events: none;\n}\n.g-gantt-event-tooltip:after {\n content: "";\n position: absolute;\n bottom: -6px;\n left: 50%;\n transform: translateX(-50%);\n width: 0;\n height: 0;\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-top: 6px solid #2a2f42;\n border-top: 6px solid var(--tooltip-background, #2a2f42);\n}\n.g-gantt-event-tooltip-title {\n font-weight: bold;\n margin-bottom: 4px;\n}\n.g-gantt-event-tooltip-time {\n font-size: 11px;\n opacity: 0.9;\n margin-bottom: 4px;\n}\n.g-gantt-event-tooltip-description {\n font-size: 11px;\n line-height: 1.4;\n opacity: 0.9;\n margin-top: 4px;\n white-space: normal;\n word-break: break-word;\n}\n\n/* Holiday Tooltip Styles */\n.g-gantt-holiday-tooltip {\n position: fixed;\n background: rgba(0, 0, 0, 0.8);\n color: white;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n z-index: 1000;\n transform: translateX(-50%);\n white-space: nowrap;\n}\n.g-gantt-holiday-tooltip:after {\n content: "";\n position: absolute;\n bottom: -5px;\n left: 50%;\n transform: translateX(-50%);\n width: 0;\n height: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n border-top: 5px solid rgba(0, 0, 0, 0.8);\n}\n\n/* Transition Animations */\n.g-fade-enter-active,\n.g-fade-leave-active {\n transition: opacity 0.3s ease;\n}\n.g-fade-enter-from,\n.g-fade-leave-to {\n opacity: 0;\n}\n\n.g-grid-current-time {\n position: absolute;\n height: 100%;\n display: flex;\n z-index: 5;\n pointer-events: none;\n}\n.g-grid-current-time-marker {\n width: 0px;\n height: calc(100% - 2px);\n display: flex;\n}\n.g-grid-current-time-text {\n font-size: x-small;\n}\n\n.g-label-column {\n display: flex;\n flex-direction: column;\n color: rgb(64, 64, 64);\n font-feature-settings: "tnum";\n font-variant-numeric: tabular-nums;\n font-size: 0.9em;\n background: white;\n box-sizing: border-box;\n flex-shrink: 0;\n}\n.g-label-column-header {\n width: 100%;\n height: 80px;\n min-height: 80px;\n overflow: visible;\n display: flex;\n align-items: center;\n overflow: visible;\n}\n.g-label-column-row-inner {\n display: flex;\n width: 100%;\n min-width: 100%;\n flex-wrap: nowrap;\n align-items: center;\n}\n.g-label-column-header-cell {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 0.5rem;\n height: 100%;\n gap: 0.5rem;\n box-sizing: border-box;\n position: relative;\n overflow: visible;\n text-align: center;\n}\n.g-label-column-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0.1rem 0.3rem;\n box-sizing: border-box;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: none;\n}\n.header-content {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0 4px;\n}\n.cell-content {\n display: flex;\n align-items: center;\n width: 100%;\n gap: 4px;\n}\n.text-ellipsis {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.text-ellipsis-value {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.g-label-column-header-cell.sortable {\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.g-label-column-header-cell.sortable:hover {\n background-color: rgba(0, 0, 0, 0.1);\n}\n.g-label-column-rows {\n width: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n}\n.g-label-column-rows::-webkit-scrollbar {\n display: none;\n}\n.g-label-column-row {\n width: 100%;\n display: flex;\n box-sizing: border-box;\n align-items: center;\n flex-wrap: nowrap;\n}\n\n/*.g-label-column-row:last-child {\n border-bottom: 0px !important;\n}*/\n.sort-icon {\n display: inline-flex;\n align-items: center;\n opacity: 0.6;\n font-size: 0.8em;\n transition: opacity 0.2s ease;\n}\n.sortable:hover .sort-icon {\n opacity: 1;\n}\n.column-resizer {\n position: absolute;\n right: -1px;\n top: 0;\n width: 8px;\n height: 100%;\n cursor: col-resize;\n z-index: 1;\n background: transparent;\n}\n.column-resizer:hover,\n.column-resizer.is-dragging,\n.column-resizer.is-touch-resizing {\n background: rgba(0, 0, 0, 0.1);\n}\n.g-label-column-header-cell:has(.column-resizer.is-touch-resizing) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n@media (max-width: 768px) {\n.column-resizer {\n width: 16px;\n}\n}\n.g-label-column {\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n.g-label-column.dragging {\n cursor: col-resize;\n}\n.g-label-column-group {\n font-weight: 600;\n background-color: rgba(0, 0, 0, 0.03);\n}\n.group-toggle-button {\n flex: 0 0 20px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: none;\n background: transparent;\n cursor: pointer;\n transition: transform 0.2s ease;\n}\n.group-toggle-button:hover {\n background-color: rgba(0, 0, 0, 0.05);\n border-radius: 4px;\n}\n.group-icon {\n width: 12px;\n height: 12px;\n transition: transform 0.2s ease;\n}\n.group-label {\n font-weight: 600;\n}\n.g-label-column-row-draggable {\n cursor: move;\n}\n.g-label-column-row-dragging {\n opacity: 0.5;\n background: #f0f0f0 !important;\n background: var(--dragging-background, #f0f0f0) !important;\n}\n.g-label-column-row-drop-target {\n position: relative;\n}\n.g-label-column-row-drop-before::before,\n.g-label-column-row-drop-after::after {\n content: "";\n position: absolute;\n left: 0;\n right: 0;\n height: 2px;\n background: #4a9eff;\n background: var(--drop-indicator-color, #4a9eff);\n z-index: 1;\n}\n.g-label-column-row-drop-before::before {\n top: 0;\n}\n.g-label-column-row-drop-after::after {\n bottom: 0;\n}\n.g-label-column-row-drop-child {\n background: rgba(74, 158, 255, 0.1) !important;\n background: var(--drop-child-background, rgba(74, 158, 255, 0.1)) !important;\n}\n.is-touch-dragging {\n opacity: 0.8;\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n pointer-events: none;\n}\n@media (max-width: 768px) {\n.group-toggle-button {\n padding: 12px;\n}\n}\n.g-label-column-header-cell-ex {\n position: relative;\n flex-grow: 1;\n}\n\n.g-gantt-milestone {\n position: absolute;\n height: 100%;\n display: flex;\n z-index: 5;\n pointer-events: auto;\n flex-direction: column;\n align-items: center;\n}\n.g-gantt-milestone-marker {\n width: 0px;\n height: calc(100% - 30px);\n display: flex;\n margin-top: 25px;\n}\n.g-gantt-milestone-label {\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 0.8em;\n white-space: nowrap;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n position: absolute;\n top: 4px;\n transform: translateY(0);\n}\n.g-gantt-milestone-tooltip {\n position: fixed;\n padding: 8px;\n border-radius: 4px;\n font-size: 0.75em;\n z-index: 1000;\n min-width: 200px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n transform: translateY(-100%);\n}\n.g-gantt-milestone-tooltip-title {\n font-weight: bold;\n}\n.g-gantt-milestone-tooltip-date {\n font-size: 0.9em;\n opacity: 0.8;\n}\n.g-gantt-milestone-tooltip-description {\n font-size: 0.9em;\n line-height: 1.4;\n}\n.g-fade-enter-active,\n.g-fade-leave-active {\n transition: opacity 0.3s ease;\n}\n.g-fade-enter-from,\n.g-fade-leave-to {\n opacity: 0;\n}\n\n.g-grid-pointer-marker-container {\n position: absolute;\n height: 100%;\n display: flex;\n z-index: 5;\n pointer-events: none;\n}\n.g-grid-pointer-marker-tooltip {\n position: fixed;\n padding: 8px;\n border-radius: 4px;\n font-size: 0.75em;\n z-index: 1000;\n min-width: 200px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n}\n.g-grid-pointer-marker-tooltip-content {\n padding: 8px;\n}\n.g-grid-pointer-marker-marker {\n width: 0px;\n height: calc(100% - 2px);\n display: flex;\n}\n.g-grid-pointer-marker-text {\n font-size: x-small;\n}\n\n.g-gantt-importer-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1000;\n}\n.g-gantt-importer-modal {\n width: 650px;\n max-width: 95%;\n max-height: 90vh;\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.g-gantt-importer-header {\n padding: 10px 14px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n.g-gantt-importer-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n}\n.g-gantt-importer-close-btn {\n background: none;\n border: none;\n font-size: 18px;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: 4px;\n transition: background-color 0.2s;\n}\n.g-gantt-importer-close-btn:hover {\n background-color: rgba(0, 0, 0, 0.1);\n}\n.g-gantt-importer-content {\n padding: 20px;\n overflow-y: auto;\n}\n\n/* Stepper styles */\n.g-gantt-importer-stepper {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 14px;\n}\n.g-gantt-stepper-step {\n display: flex;\n flex-direction: column;\n align-items: center;\n position: relative;\n}\n.g-gantt-step-number {\n width: 30px;\n height: 30px;\n border-radius: 50%;\n display: flex;\n justify-content: center;\n align-items: center;\n margin-bottom: 8px;\n font-weight: 500;\n transition: all 0.3s;\n}\n.g-gantt-step-label {\n font-size: 14px;\n transition: color 0.3s;\n}\n.g-gantt-step-connector {\n flex-grow: 1;\n height: 2px;\n margin: 0 10px;\n margin-bottom: 30px;\n}\n\n/* File upload styles */\n.g-gantt-file-upload {\n width: 100%;\n}\n.g-gantt-file-dropzone {\n border: 2px dashed;\n border-radius: 8px;\n padding: 40px;\n text-align: center;\n cursor: pointer;\n transition: all 0.3s;\n}\n.g-gantt-file-dropzone:hover {\n border-color: currentColor;\n background-color: rgba(0, 0, 0, 0.03);\n}\n.g-gantt-file-dropzone .file-icon {\n font-size: 48px;\n margin-bottom: 16px;\n}\n.g-gantt-file-dropzone p {\n margin: 0 0 8px;\n font-size: 16px;\n}\n.g-gantt-file-dropzone small {\n color: #666;\n}\n.g-gantt-file-input {\n display: none;\n}\n\n/* Configuration styles */\n.g-gantt-selected-file {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.g-gantt-file-info {\n padding: 12px;\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.g-gantt-import-options {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n.g-gantt-option-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.g-gantt-option-group label {\n font-weight: 500;\n font-size: 14px;\n}\n.g-gantt-option-group select,\n.g-gantt-option-group input {\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 4px;\n font-size: 14px;\n}\n.g-gantt-toggle-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n font-size: 14px;\n -webkit-text-decoration: underline;\n text-decoration: underline;\n}\n.g-gantt-advanced-options {\n border-top: 1px solid;\n padding-top: 16px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n.g-gantt-import-actions {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n margin-top: 10px;\n}\n\n/* Result styles */\n.g-gantt-import-result {\n display: flex;\n flex-direction: column;\n gap: 24px;\n align-items: center;\n text-align: center;\n}\n.g-gantt-result-success,\n.g-gantt-result-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n}\n.result-icon {\n font-size: 48px;\n border-radius: 50%;\n padding: 16px;\n margin-bottom: 8px;\n}\n.g-gantt-warnings {\n margin-top: 16px;\n text-align: left;\n padding: 16px;\n border-radius: 8px;\n border-left: 4px solid;\n width: 100%;\n}\n.g-gantt-warnings h5 {\n margin-top: 0;\n margin-bottom: 6px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.g-gantt-warnings ul {\n margin: 0;\n padding-left: 14px;\n max-height: 150px;\n overflow-y: overlay;\n}\n\n/* Button styles */\n.g-gantt-button {\n padding: 8px 16px;\n border-radius: 4px;\n font-size: 14px;\n cursor: pointer;\n transition: all 0.2s;\n border: none;\n font-weight: 500;\n}\n.g-gantt-result-actions {\n display: flex;\n gap: 4px;\n}\n.g-gantt-button:hover {\n filter: brightness(0.95);\n}\n.g-gantt-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* Animations */\n.g-fade-enter-active,\n.g-fade-leave-active {\n transition: opacity 0.3s ease;\n}\n.g-fade-enter-from,\n.g-fade-leave-to {\n opacity: 0;\n}\n\n.gantt-connector[data-v-652d5ef8] {\n overflow: visible;\n pointer-events: none;\n}\n.connector-path[data-v-652d5ef8] {\n transition: d 0.3s ease;\n}\n.connector-path.selected[data-v-652d5ef8] {\n filter: drop-shadow(0 0 5px rgba(33, 150, 243, 0.6));\n}\n.relation-indicator[data-v-652d5ef8] {\n font-weight: bold;\n pointer-events: none;\n}\n\n/* Animation for dash pattern */\n.connector-animated-dash-slow[data-v-652d5ef8] {\n animation: dashFlow-652d5ef8 4s linear infinite;\n}\n.connector-animated-dash-normal[data-v-652d5ef8] {\n animation: dashFlow-652d5ef8 2s linear infinite;\n}\n.connector-animated-dash-fast[data-v-652d5ef8] {\n animation: dashFlow-652d5ef8 1s linear infinite;\n}\n\n/* Animation for dot pattern */\n.connector-animated-dot-slow[data-v-652d5ef8] {\n animation: dotFlow-652d5ef8 4s linear infinite;\n}\n.connector-animated-dot-normal[data-v-652d5ef8] {\n animation: dotFlow-652d5ef8 2s linear infinite;\n}\n.connector-animated-dot-fast[data-v-652d5ef8] {\n animation: dotFlow-652d5ef8 1s linear infinite;\n}\n\n/* Animation for dashdot pattern */\n.connector-animated-dashdot-slow[data-v-652d5ef8] {\n animation: dashdotFlow-652d5ef8 4s linear infinite;\n}\n.connector-animated-dashdot-normal[data-v-652d5ef8] {\n animation: dashdotFlow-652d5ef8 2s linear infinite;\n}\n.connector-animated-dashdot-fast[data-v-652d5ef8] {\n animation: dashdotFlow-652d5ef8 1s linear infinite;\n}\n.connector-path[data-v-652d5ef8] {\n marker-start: none;\n transition:\n d 0.3s ease, marker-start 0.3s ease;\n}\n.connection-endpoint[data-v-652d5ef8] {\n filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.3));\n transition: all 0.3s ease;\n border: 1px solid black;\n border-radius: 100%;\n}\n.connection-endpoint[data-v-652d5ef8]:hover {\n r: 8;\n}\n@keyframes dashFlow-652d5ef8 {\n0% {\n stroke-dasharray: 10, 10;\n stroke-dashoffset: 0;\n}\n100% {\n stroke-dasharray: 10, 10;\n stroke-dashoffset: -20;\n}\n}\n@keyframes dotFlow-652d5ef8 {\n0% {\n stroke-dasharray: 2, 8;\n stroke-dashoffset: 0;\n}\n100% {\n stroke-dasharray: 2, 8;\n stroke-dashoffset: -10;\n}\n}\n@keyframes dashdotFlow-652d5ef8 {\n0% {\n stroke-dasharray: 12, 6, 3, 6;\n stroke-dashoffset: 0;\n}\n100% {\n stroke-dasharray: 12, 6, 3, 6;\n stroke-dashoffset: -27;\n}\n}\n', "top");
export {
_sfc_main as GGanttChart,
_sfc_main$3 as GGanttRow,
hyvuegantt as default,
extendDayjs,
hyvuegantt
};