hy-vue-gantt
Version:
Evolution of vue-ganttastic package
6,866 lines • 275 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, openBlock, createElementBlock, unref, Fragment, renderList, normalizeStyle, normalizeClass, ref, reactive, onMounted, createElementVNode, renderSlot, createTextVNode, toDisplayString, createVNode, createCommentVNode, watch, toRefs, createBlock, Teleport, Transition, withCtx, nextTick, withDirectives, vModelText, provide, resolveComponent, withModifiers, mergeProps, TransitionGroup, createSlots, useTemplateRef, normalizeProps, guardReactiveProps, useCssVars, useSlots, toRef, onUnmounted, resolveDynamicComponent, isRef, createStaticVNode, vModelSelect, h } from "vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { faChevronDown, faChevronRight, faSort, faArrowDownAZ, faArrowDownZA, faAngleUp, faAngleDown, faExpandAlt, faCompressAlt, faAnglesLeft, faAngleLeft, faAngleRight, faAnglesRight, faMagnifyingGlassMinus, faMagnifyingGlassPlus, faUndo, faRedo, faFileExport, faSpinner } 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/ar";
import "dayjs/locale/hi";
import "dayjs/locale/tr";
import "dayjs/locale/nl";
import "dayjs/locale/pl";
import "dayjs/locale/cs";
import "dayjs/locale/hu";
import "dayjs/locale/ro";
import "dayjs/locale/bg";
import "dayjs/locale/sr";
import "dayjs/locale/sr-cyrl";
import "dayjs/locale/af";
import "dayjs/locale/az";
import "dayjs/locale/be";
import "dayjs/locale/bs";
import "dayjs/locale/ca";
import "dayjs/locale/da";
import "dayjs/locale/el";
import "dayjs/locale/et";
import "dayjs/locale/fi";
import "dayjs/locale/gl";
import "dayjs/locale/he";
import "dayjs/locale/hr";
import "dayjs/locale/is";
import "dayjs/locale/ka";
import "dayjs/locale/kk";
import "dayjs/locale/ky";
import "dayjs/locale/lt";
import "dayjs/locale/lv";
import "dayjs/locale/mk";
import "dayjs/locale/mn";
import "dayjs/locale/mt";
import "dayjs/locale/nb";
import "dayjs/locale/nn";
import "dayjs/locale/pt-br";
import "dayjs/locale/sk";
import "dayjs/locale/sl";
import "dayjs/locale/sq";
import "dayjs/locale/sv";
import "dayjs/locale/ta";
import "dayjs/locale/th";
import "dayjs/locale/uk";
import "dayjs/locale/uz";
import "dayjs/locale/vi";
import "dayjs/locale/zh-hk";
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, true);
};
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$c = /* @__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$6 = { class: "text-ellipsis" };
const _hoisted_3$6 = {
key: 0,
class: "sort-icon"
};
const _hoisted_4$5 = ["onMousedown", "onTouchstart"];
const _hoisted_5$4 = ["data-row-id", "draggable", "onDragstart", "onDragover", "onTouchstart", "onTouchmove"];
const _hoisted_6$4 = { class: "g-label-column-row-inner" };
const _hoisted_7$4 = ["onClick"];
const _hoisted_8$4 = { class: "text-ellipsis-value" };
const LONG_PRESS_DURATION = 500;
const INDENT_WIDTH = 24;
const _sfc_main$b = /* @__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$6, [
renderSlot(_ctx.$slots, `label-column-title-${column.field.toLowerCase()}`, {}, () => [
createTextVNode(toDisplayString(column.field), 1)
])
]),
columnSortableStates.value[column.field] ? (openBlock(), createElementBlock("span", _hoisted_3$6, [
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$5)) : 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$4, [
(openBlock(true), createElementBlock(Fragment, null, renderList(getVisibleColumns(row), (column) => {
var _a2;
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$4)) : createCommentVNode("", true),
createElementVNode("span", _hoisted_8$4, [
renderSlot(_ctx.$slots, `label-column-${column.field.toLowerCase()}`, {
row,
value: getRowValue(row, column, index)
}, () => [
createTextVNode(toDisplayString(getRowValue(row, column, index)), 1)
])
])
], 4)
], 4)
], 4)) : createCommentVNode("", true)
], 64);
}), 128))
])
], 46, _hoisted_5$4);
}), 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 BASE_UNIT_WIDTH = 24;
const MAX_ZOOM = 10;
const MIN_ZOOM = 1;
const DEFAULT_ZOOM = 3;
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 } = config;
const internalPrecision = ref(configPrecision.value);
const zoomLevel = ref(DEFAULT_ZOOM);
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(() => BASE_UNIT_WIDTH * 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 = DEFAULT_ZOOM;
}
);
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-holiday-tooltip-content" };
const _sfc_main$a = /* @__PURE__ */ defineComponent({
__name: "GGanttHolidayTooltip",
props: {
unit: {},
modelValue: { type: Boolean },
targetElement: {}
},
setup(__props) {
const props = __props;
const { unit, targetElement } = toRefs(props);
const { font } = provideConfig();
const tooltipTop = ref("0px");
const tooltipLeft = ref("0px");
watch(
[() => props.unit, () => props.targetElement],
async () => {
var _a;
if (!((_a = unit.value) == null ? void 0 : _a.holidayName) || !targetElement.value) {
return;
}
const rect = targetElement.value.getBoundingClientRect();
tooltipTop.value = `${rect.top - 30}px`;
tooltipLeft.value = `${rect.left + rect.width / 2}px`;
},
{ immediate: true }
);
return (_ctx, _cache) => {
return openBlock(), createBlock(Teleport, { to: "body" }, [
createVNode(Transition, {
name: "g-fade",
mode: "out-in"
}, {
default: withCtx(() => {
var _a;
return [
_ctx.modelValue && ((_a = unref(unit)) == null ? void 0 : _a.holidayName) ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-gantt-holiday-tooltip",
style: normalizeStyle({
top: tooltipTop.value,
left: tooltipLeft.value,
fontFamily: unref(font)
})
}, [
createElementVNode("div", _hoisted_1$8, toDisplayString(unref(unit).holidayName), 1)
], 4)) : createCommentVNode("", true)
];
}),
_: 1
})
]);
};
}
});
const _hoisted_1$7 = { class: "g-gantt-event-tooltip-content" };
const _hoisted_2$5 = { class: "g-gantt-event-tooltip-title" };
const _hoisted_3$5 = { class: "g-gantt-event-tooltip-time" };
const _hoisted_4$4 = {
key: 0,
class: "g-gantt-event-tooltip-description"
};
const _sfc_main$9 = /* @__PURE__ */ defineComponent({
__name: "GGanttEventTooltip",
props: {
event: {},
modelValue: { type: Boolean },
targetElement: {}
},
setup(__props) {
const props = __props;
const { event, targetElement } = toRefs(props);
const { font, colors, dateFormat } = provideConfig();
const { format } = useDayjsHelper();
const tooltipTop = ref("0px");
const tooltipLeft = ref("0px");
watch(
[() => props.event, () => props.targetElement],
async () => {
if (!event.value || !targetElement.value) {
return;
}
await nextTick();
const rect = targetElement.value.getBoundingClientRect();
tooltipTop.value = `${rect.top - 95}px`;
tooltipLeft.value = `${rect.left + rect.width / 2}px`;
},
{ immediate: true }
);
const formatDate = (date) => {
return format(date, dateFormat.value);
};
return (_ctx, _cache) => {
return openBlock(), createBlock(Teleport, { to: "body" }, [
createVNode(Transition, {
name: "g-fade",
mode: "out-in"
}, {
default: withCtx(() => [
_ctx.modelValue && unref(event) ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-gantt-event-tooltip",
style: normalizeStyle({
top: tooltipTop.value,
left: tooltipLeft.value,
fontFamily: unref(font),
background: unref(colors).primary,
color: unref(colors).text
})
}, [
createElementVNode("div", _hoisted_1$7, [
createElementVNode("div", _hoisted_2$5, toDisplayString(unref(event).label), 1),
createElementVNode("div", _hoisted_3$5, toDisplayString(formatDate(unref(event).startDate)) + " - " + toDisplayString(formatDate(unref(event).endDate)), 1),
unref(event).description ? (openBlock(), createElementBlock("div", _hoisted_4$4, toDisplayString(unref(event).description), 1)) : createCommentVNode("", true)
])
], 4)) : createCommentVNode("", true)
]),
_: 1
})
]);
};
}
});
const _hoisted_1$6 = { class: "g-timeunits-container" };
const _hoisted_2$4 = ["onMouseenter"];
const _hoisted_3$4 = { class: "g-timeunits-container" };
const _hoisted_4$3 = ["onMouseenter"];
const _hoisted_5$3 = { class: "g-timeunit-min" };
const _hoisted_6$3 = { class: "label-unit" };
const _hoisted_7$3 = {
key: 0,
class: "g-timeunit-step"
};
const _hoisted_8$3 = { class: "label-unit" };
const _hoisted_9$3 = ["onMouseenter"];
const _hoisted_10$2 = { 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$6, [
(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$4);
}), 128))
]),
createElementVNode("div", _hoisted_3$4, [
(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$3, [
renderSlot(_ctx.$slots, "timeunit", {
label: formatTimeUnitLabel(unit, "lower"),
value: unit.value,
date: unit.date
}, () => [
createElementVNode("div", _hoisted_6$3, 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$3, [
(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$3, 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$3);
}), 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$2, [
renderSlot(_ctx.$slots, "timeaxis-event", { event }, () => [
createTextVNode(toDisplayString(event.label), 1)
])
])
], 44, _hoisted_9$3);
}), 128))
], 4)) : createCommentVNode("", true),
createVNode(_sfc_main$a, {
"model-value": showTooltip.value,
unit: hoveredUnit.value,
"target-element": hoveredElement.value
}, null, 8, ["model-value", "unit", "target-element"]),
createVNode(_sfc_main$9, {
"model-value": showEventTooltip.value,
event: hoveredEvent.value,
"target-element": hoveredEventElement.value
}, null, 8, ["model-value", "event", "target-element"])
], 36);
};
}
});
const DEFAULT_DOT_COLOR = "cadetblue";
const _sfc_main$7 = /* @__PURE__ */ defineComponent({
__name: "GGanttBarTooltip",
props: {
bar: {},
modelValue: { type: Boolean }
},
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 } = toRefs(props);
const { precision, font, barStart, barEnd, rowHeight, milestones } = provideConfig();
const tooltipTop = ref("0px");
const tooltipLeft = ref("0px");
watch(
() => props.bar,
async () => {
var _a;
await nextTick();
const barId = ((_a = bar == null ? void 0 : bar.value) == null ? void 0 : _a.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`;
},
{ deep: true, immediate: true }
);
const dotColor = computed(() => {
var _a, _b;
return ((_b = (_a = bar == null ? void 0 : bar.value) == null ? void 0 : _a.ganttBarConfig.style) == null ? void 0 : _b.background) || DEFAULT_DOT_COLOR;
});
const { toDayjs } = useDayjsHelper();
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 == null ? void 0 : bar.value)) {
return "";
}
const milestone = milestones.value.find((m) => {
var _a;
return m.id === ((_a = bar.value) == null ? void 0 : _a.ganttBarConfig.milestoneId);
});
const format = TOOLTIP_FORMATS[precision.value];
const barStartFormatted = toDayjs(barStartRaw.value).format(format);
const barEndFormatted = toDayjs(barEndRaw.value).format(format);
const milestoneName = milestone ? ` - (${milestone.name})` : "";
return `${barStartFormatted} – ${barEndFormatted}${milestoneName}`;
});
return (_ctx, _cache) => {
return openBlock(), createBlock(Teleport, { to: "body" }, [
createVNode(Transition, {
name: "g-fade",
mode: "out-in"
}, {
default: withCtx(() => [
_ctx.modelValue ? (openBlock(), createElementBlock("div", {
key: 0,
class: "g-gantt-tooltip",
style: normalizeStyle({
top: tooltipTop.value,
left: tooltipLeft.value,
fontFamily: unref(font)
})
}, [
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)
])
], 4)) : createCommentVNode("", true)
]),
_: 3
})
]);
};
}
});
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$6 = /* @__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$5 = {
class: "gantt-connector",
style: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
zIndex: 1001,
overflow: "visible"
}
};
const _hoisted_2$3 = ["id"];
const _hoisted_3$3 = ["fill"];
const _hoisted_4$2 = ["id"];
const _hoisted_5$2 = ["stop-color"];
const _hoisted_6$2 = ["stop-color"];
const _hoisted_7$2 = ["stop-color"];
const _hoisted_8$2 = ["stop-color"];
const _hoisted_9$2 = ["dur"];
const _hoisted_10$1 = ["dur"];
const _hoisted_11$1 = ["d", "stroke", "stroke-width", "stroke-dasharray"];
const _hoisted_12$1 = ["cx", "cy"];
const _hoisted_13$1 = ["cx", "cy"];
const _sfc_main$5 = /* @__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 }
},
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-start-${props.sourceBar.id}-${props.targetBar.id}`);
const hasMarkerEnd = computed(() => props.marker === "bidirectional" || props.marker === "forward");
const hasMarkerStart = computed(() => props.marker === "bidirectional");
const markerDeltaEnd = computed(() => hasMarkerEnd.value ? 4 : 0);
const markerDeltaStart = computed(() => hasMarkerStart.value ? 4 : 0);
const pathData = computed(() => {
const sourceX = props.sourceBar.x + props.sourceBar.width;
const sourceY = props.sourceBar.y + props.sourceBar.height / 2;
const targetX = props.targetBar.x;
const targetY = props.targetBar.y + props.targetBar.height / 2;
const OFFSET = 20;
const isGoingBack = targetX <= sourceX;
switch (props.type) {
case "straight":
return `M ${sourceX},${sourceY} L ${targetX - markerDeltaEnd.value},${targetY}`;
case "squared":
if (isGoingBack) {
return `M ${sourceX + markerDeltaStart.value},${sourceY}
h ${OFFSET}
v ${(targetY - sourceY) / 2}
h -${Math.abs(targetX - sourceX) + OFFSET * 2}
v ${(targetY - sourceY) / 2}
h ${OFFSET - markerDeltaEnd.value * 2}`;
}
return `M ${sourceX + markerDeltaStart.value},${sourceY}
h ${OFFSET}
v ${targetY - sourceY}
h ${targetX - sourceX - OFFSET - markerDeltaEnd.value * 2}`;
case "bezier":
default:
const controlPointX = (sourceX + targetX) / 2;
return `M ${sourceX + markerDeltaStart.value},${sourceY}
C ${controlPointX},${sourceY}
${controlPointX},${targetY}
${targetX - markerDeltaEnd.value},${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;
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("svg", _hoisted_1$5, [
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$3)
], 8, _hoisted_2$3),
_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$2),
createElementVNode("stop", {
offset: "45%",
"stop-color": _ctx.color,
"stop-opacity": "1"
}, null, 8, _hoisted_6$2),
createElementVNode("stop", {
offset: "55%",
"stop-color": _ctx.color,
"stop-opacity": "1"
}, null, 8, _hoisted_7$2),
createElementVNode("stop", {
offset: "100%",
"stop-color": _ctx.color,
"stop-opacity": "0.3"
}, null, 8, _hoisted_8$2),
createElementVNode("animate", {
attributeName: "x1",
from: "-100%",
to: "100%",
dur: _ctx.animationSpeed === "slow" ? "4s" : _ctx.animationSpeed === "fast" ? "1s" : "2s",
repeatCount: "indefinite"
}, null, 8, _hoisted_9$2),
createElementVNode("animate", {
attributeName: "x2",
from: "0%",
to: "200%",
dur: _ctx.animationSpeed === "slow" ? "4s" : _ctx.animationSpeed === "fast" ? "1s" : "2s",
repeatCount: "indefinite"
}, null, 8, _hoisted_10$1)
], 8, _hoisted_4$2)) : 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) }
]),
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$1),
_ctx.isSelected && unref(enableConnectionDeletion) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
createElementVNode("circle", {
cx: _ctx.sourceBar.x + _ctx.sourceBar.width,
cy: _ctx.sourceBar.y + _ctx.sourceBar.height / 2,
r: "6",
fill: "white",
class: "connection-endpoint"
}, null, 8, _hoisted_12$1),
createElementVNode("circle", {
cx: _ctx.targetBar.x,
cy: _ctx.targetBar.y + _ctx.targetBar.height / 2,
r: "6",
fill: "white",
class: "connection-endpoint"
}, null, 8, _hoisted_13$1)
], 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$5, [["__scopeId", "data-v-a658cc88"]]);
const _hoisted_1$4 = { class: "g-gantt-milestone-tooltip-title" };
const _hoisted_2$2 = { class: "g-gantt-milestone-tooltip-date" };
const _hoisted_3$2 = { class: "g-gantt-milestone-tooltip-description" };
const _sfc_main$4 = /* @__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$4, toDisplayString(_ctx.milestone.name), 1),
createElementVNode("div", _hoisted_2$2, toDisplayString(_ctx.milestone.date), 1),
createElementVNode("div", _hoisted_3$2, 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$3 = ["id", "aria-label", "aria-grabbed", "aria-describedby"];
const _hoisted_2$1 = {
key: 0,
class: "progress-text"
};
const _hoisted_3$1 = ["width", "height"];
const _hoisted_4$1 = ["d", "fill"];
const _hoisted_5$1 = { class: "g-gantt-bar-label" };
const _hoisted_6$1 = { key: 0 };
const _hoisted_7$1 = {
key: 0,
class: "g-gantt-bar-label-edit"
};
const _hoisted_8$1 = { key: 1 };
const _hoisted_9$1 = ["innerHTML"];
const _sfc_main$3 = /* @__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$1, 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 ? (openBlock(), createElementBlock("svg", {
key: 2,
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$1)
], 8, _hoisted_3$1)) : createCommentVNode("", true),
createElementVNode("div", _hoisted_5$1, [
renderSlot(_ctx.$slots, "default", { bar: unref(bar) }, () => [
!isGroupBar.value && unref(showLabel) ? (openBlock(), createElementBlock("div", _hoisted_6$1, [
isEditing.value && unref(barLabelEditable) ? (openBlock(), createElementBlock("div", _hoisted_7$1, [
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$1, toDisplayString(barConfig.value.label || ""), 1))
])) : createCommentVNode("", true),
barConfig.value.html ? (openBlock(), createElementBlock("div", {
key: 1,
innerHTML: barConfig.value.html
}, null, 8, _hoisted_9$1)) : createCommentVNode("", true)
])
]),
barConfig.value.hasHandles ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [
_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$3);
};
}
});
const _hoisted_1$2 = {
key: 0,
class: "g-gantt-row-children"
};
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
__name: "GGanttRow",
props: {
label: {},
bars: {},
highlightOnHover: { type: Boolean },
id: {},
children: {},
connections: {}
},
emits: ["drop"],
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 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 { 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);
}
};
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"
}, [
!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), [
createVNode(TransitionGroup, {
name: "bar-transition",
tag: "div"
}, {
default: withCtx(() => [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.bars, (bar) => {
return openBlock(), createBlock(_sfc_main$3, {
key: bar.ganttBarConfig.id,
bar,
class: normalizeClass({ "g-gantt-group-bar": isGroup.value })
}, {
default: withCtx(() => [
!isGroup.value ? renderSlot(_ctx.$slots, "bar-label", {
key: 0,
bar
}) : createCommentVNode("", true)
]),
_: 2
}, 1032, ["bar", "class"]);
}), 128))
]),
_: 3
})
], 16)
], 38),
isGroup.value && isExpanded.value ? (openBlock(), createElementBlock("div", _hoisted_1$2, [
(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) }), 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$1 = { style: { "font-weight": "bold" } };
const _sfc_main$1 = /* @__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$1, "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 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,
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
});
});
}
});
};
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 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) => {
if (!connectionState.value.sourceBar) return;
const validation = validateConnection(connectionState.value.sourceBar, targetBar);
if (validation.isValid) {
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
};
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;
let 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 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 = ["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" },
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: true },
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
}) }
},
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"],
setup(__props, { expose: __expose, emit: __emit }) {
useCssVars((_ctx) => ({
"170a1ade": colors.value.rangeHighlight,
"1a0a3e0a": 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 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 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),
colors,
chartSize
});
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),
colors,
chartSize
},
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 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) => {
var _a;
return ((_a = row.children) == null ? void 0 : _a.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 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$2,
{
...row._originalNode.props,
label: row.label,
bars: row.bars,
children: row.children,
id: row.id,
key: row.id || row.label
},
row._originalNode.children || {}
);
}
return h(_sfc_main$2, {
label: row.label,
bars: row.bars,
id: row.id,
key: row.id || row.label
});
};
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();
});
provide(CONFIG_KEY, {
...toRefs(props),
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[19] || (_cache[19] = //@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$b, {
ref_key: "labelColumn",
ref: labelColumn,
onScroll: unref(handleLabelScroll),
onRowDrop: dropRow
}, createSlots({
"label-column-title": withCtx(() => [
renderSlot(_ctx.$slots, "label-column-title")
]),
"label-column-row": withCtx((slotProps) => [
renderSlot(_ctx.$slots, "label-column-row", normalizeProps(guardReactiveProps(slotProps)))
]),
_: 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)))
]),
_: 3
}, 8, ["timeaxisUnits", "internalPrecision"])) : createCommentVNode("", true),
_ctx.grid ? (openBlock(), createBlock(_sfc_main$c, {
key: 1,
timeaxisUnits: unref(timeaxisUnits),
internalPrecision: unref(internalPrecision)
}, null, 8, ["timeaxisUnits", "internalPrecision"])) : createCommentVNode("", true),
_ctx.currentTime ? (openBlock(), createBlock(_sfc_main$6, { key: 2 }, {
"current-time-label": withCtx(() => [
renderSlot(_ctx.$slots, "current-time-label")
]),
_: 3
})) : createCommentVNode("", true),
_ctx.pointerMarker ? (openBlock(), createBlock(_sfc_main$1, { 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$4, {
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: _ctx.defaultConnectionType,
color: _ctx.defaultConnectionColor,
pattern: _ctx.defaultConnectionPattern,
animated: _ctx.defaultConnectionAnimated,
"animation-speed": _ctx.defaultConnectionAnimationSpeed,
style: { opacity: 0.6 },
marker: _ctx.markerConnection
}, null, 8, ["source-bar", "target-bar", "type", "color", "pattern", "animated", "animation-speed", "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[20] || (_cache[20] = [
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$7, {
"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"])
], 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$2);
}
};
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(--170a1ade) 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(--170a1ade);\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(--170a1ade);\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(--170a1ade);\n}\n.g-gantt-scroller::-moz-range-thumb:hover {\n background: var(--170a1ade);\n}\n\n/* Icon Styles */\n.command-icon {\n background: var(--170a1ade);\n padding: 4px;\n margin: 2px;\n width: 14px;\n height: 14px;\n border-radius: 4px;\n}\nbutton {\n display: flex;\n}\n.g-gantt-chart:focus-within {\n outline: 2px solid var(--1a0a3e0a);\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\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.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-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-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.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-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.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.gantt-connector[data-v-a658cc88] {\n overflow: visible;\n pointer-events: none;\n}\n.connector-path[data-v-a658cc88] {\n transition: d 0.3s ease;\n}\n.connector-path.selected[data-v-a658cc88] {\n filter: drop-shadow(0 0 5px rgba(33, 150, 243, 0.6));\n}\n\n/* Animation for dash pattern */\n.connector-animated-dash-slow[data-v-a658cc88] {\n animation: dashFlow-a658cc88 4s linear infinite;\n}\n.connector-animated-dash-normal[data-v-a658cc88] {\n animation: dashFlow-a658cc88 2s linear infinite;\n}\n.connector-animated-dash-fast[data-v-a658cc88] {\n animation: dashFlow-a658cc88 1s linear infinite;\n}\n\n/* Animation for dot pattern */\n.connector-animated-dot-slow[data-v-a658cc88] {\n animation: dotFlow-a658cc88 4s linear infinite;\n}\n.connector-animated-dot-normal[data-v-a658cc88] {\n animation: dotFlow-a658cc88 2s linear infinite;\n}\n.connector-animated-dot-fast[data-v-a658cc88] {\n animation: dotFlow-a658cc88 1s linear infinite;\n}\n\n/* Animation for dashdot pattern */\n.connector-animated-dashdot-slow[data-v-a658cc88] {\n animation: dashdotFlow-a658cc88 4s linear infinite;\n}\n.connector-animated-dashdot-normal[data-v-a658cc88] {\n animation: dashdotFlow-a658cc88 2s linear infinite;\n}\n.connector-animated-dashdot-fast[data-v-a658cc88] {\n animation: dashdotFlow-a658cc88 1s linear infinite;\n}\n.connector-path[data-v-a658cc88] {\n marker-start: none;\n transition:\n d 0.3s ease, marker-start 0.3s ease;\n}\n.connection-endpoint[data-v-a658cc88] {\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-a658cc88]:hover {\n r: 8;\n}\n@keyframes dashFlow-a658cc88 {\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-a658cc88 {\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-a658cc88 {\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\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-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.g-fade-enter-active,\n.g-fade-leave-active {\n transition: opacity 0.2s ease;\n}\n.g-fade-enter-from,\n.g-fade-leave-to {\n opacity: 0;\n}\n\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.g-fade-enter-active,\n.g-fade-leave-active {\n transition: opacity 0.2s ease;\n}\n.g-fade-enter-from,\n.g-fade-leave-to {\n opacity: 0;\n}\n', "top");
export {
_sfc_main as GGanttChart,
_sfc_main$2 as GGanttRow,
hyvuegantt as default,
extendDayjs,
hyvuegantt
};