gantt-lib 0.75.0 → 0.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -758,13 +758,13 @@ function computeParentProgress(parentId, tasks) {
758
758
  if (children.length === 0) {
759
759
  return 0;
760
760
  }
761
- const DAY_MS3 = 24 * 60 * 60 * 1e3;
761
+ const DAY_MS4 = 24 * 60 * 60 * 1e3;
762
762
  let totalWeight = 0;
763
763
  let weightedSum = 0;
764
764
  for (const child of children) {
765
765
  const start = new Date(child.startDate).getTime();
766
766
  const end = new Date(child.endDate).getTime();
767
- const duration = (end - start + DAY_MS3) / DAY_MS3;
767
+ const duration = (end - start + DAY_MS4) / DAY_MS4;
768
768
  const progress = child.progress ?? 0;
769
769
  totalWeight += duration;
770
770
  weightedSum += duration * progress;
@@ -859,10 +859,10 @@ function buildTaskRangeFromStart(startDate, duration, businessDays = false, week
859
859
  end: parseDateOnly(addBusinessDays(normalizedStart, duration, weekendPredicate))
860
860
  };
861
861
  }
862
- const DAY_MS3 = 24 * 60 * 60 * 1e3;
862
+ const DAY_MS4 = 24 * 60 * 60 * 1e3;
863
863
  return {
864
864
  start: normalizedStart,
865
- end: new Date(normalizedStart.getTime() + (Math.max(1, duration) - 1) * DAY_MS3)
865
+ end: new Date(normalizedStart.getTime() + (Math.max(1, duration) - 1) * DAY_MS4)
866
866
  };
867
867
  }
868
868
  function buildTaskRangeFromEnd(endDate, duration, businessDays = false, weekendPredicate, snapDirection = -1) {
@@ -873,9 +873,9 @@ function buildTaskRangeFromEnd(endDate, duration, businessDays = false, weekendP
873
873
  end: normalizedEnd
874
874
  };
875
875
  }
876
- const DAY_MS3 = 24 * 60 * 60 * 1e3;
876
+ const DAY_MS4 = 24 * 60 * 60 * 1e3;
877
877
  return {
878
- start: new Date(normalizedEnd.getTime() - (Math.max(1, duration) - 1) * DAY_MS3),
878
+ start: new Date(normalizedEnd.getTime() - (Math.max(1, duration) - 1) * DAY_MS4),
879
879
  end: normalizedEnd
880
880
  };
881
881
  }
@@ -4185,21 +4185,56 @@ var LINK_TYPE_LABELS = {
4185
4185
  SF: "\u041D\u0430\u0447\u0430\u043B\u043E-\u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435"
4186
4186
  };
4187
4187
 
4188
+ // src/components/TaskList/defaultTaskDates.ts
4189
+ init_dateUtils();
4190
+ var DAY_MS2 = 24 * 60 * 60 * 1e3;
4191
+ var DEFAULT_TASK_DURATION_DAYS = 5;
4192
+ function toISODate(date) {
4193
+ return date.toISOString().split("T")[0];
4194
+ }
4195
+ function getTodayISODate() {
4196
+ const now = /* @__PURE__ */ new Date();
4197
+ return toISODate(new Date(Date.UTC(
4198
+ now.getUTCFullYear(),
4199
+ now.getUTCMonth(),
4200
+ now.getUTCDate()
4201
+ )));
4202
+ }
4203
+ function buildDefaultTaskDateRange(startDate, {
4204
+ businessDays,
4205
+ defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
4206
+ weekendPredicate
4207
+ } = {}) {
4208
+ const durationDays = Math.max(1, Math.round(defaultTaskDurationDays));
4209
+ const start = parseUTCDate(startDate);
4210
+ if (businessDays && weekendPredicate) {
4211
+ const range = buildTaskRangeFromStart(start, durationDays, true, weekendPredicate);
4212
+ return {
4213
+ startDate: toISODate(range.start),
4214
+ endDate: toISODate(range.end)
4215
+ };
4216
+ }
4217
+ return {
4218
+ startDate: toISODate(start),
4219
+ endDate: toISODate(new Date(start.getTime() + (durationDays - 1) * DAY_MS2))
4220
+ };
4221
+ }
4222
+
4188
4223
  // src/components/TaskList/TaskListRow.tsx
4189
4224
  var import_jsx_runtime12 = require("react/jsx-runtime");
4190
- var DAY_MS2 = 24 * 60 * 60 * 1e3;
4225
+ var DAY_MS3 = 24 * 60 * 60 * 1e3;
4191
4226
  var LINK_TYPE_ORDER = ["FS", "SS", "FF", "SF"];
4192
4227
  var getInclusiveDurationDays = (startDate, endDate) => {
4193
4228
  const start = parseUTCDate(startDate);
4194
4229
  const end = parseUTCDate(endDate);
4195
4230
  return Math.max(
4196
4231
  1,
4197
- Math.round((end.getTime() - start.getTime()) / DAY_MS2) + 1
4232
+ Math.round((end.getTime() - start.getTime()) / DAY_MS3) + 1
4198
4233
  );
4199
4234
  };
4200
4235
  var getEndDateFromDuration = (startDate, durationDays) => {
4201
4236
  const start = parseUTCDate(startDate);
4202
- return new Date(start.getTime() + (durationDays - 1) * DAY_MS2).toISOString().split("T")[0];
4237
+ return new Date(start.getTime() + (durationDays - 1) * DAY_MS3).toISOString().split("T")[0];
4203
4238
  };
4204
4239
  var TrashIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4205
4240
  "svg",
@@ -4684,7 +4719,7 @@ var DepChip = ({
4684
4719
  )
4685
4720
  ] });
4686
4721
  };
4687
- var toISODate = (value) => {
4722
+ var toISODate2 = (value) => {
4688
4723
  if (value instanceof Date) return value.toISOString().split("T")[0];
4689
4724
  if (typeof value === "string" && value.includes("T"))
4690
4725
  return value.split("T")[0];
@@ -4738,6 +4773,7 @@ var TaskListRow = import_react10.default.memo(
4738
4773
  customDays,
4739
4774
  isWeekend: isWeekend3,
4740
4775
  businessDays,
4776
+ defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
4741
4777
  isFilterMatch = false,
4742
4778
  isFilterHideMode = false,
4743
4779
  resolvedColumns,
@@ -5545,8 +5581,8 @@ var TaskListRow = import_react10.default.memo(
5545
5581
  },
5546
5582
  [selectedChip?.successorId, selectedChip?.predecessorId, selectedChip?.linkType, onRemoveDependency, onChipSelect]
5547
5583
  );
5548
- const startDateISO = toISODate(normalizedTask.startDate);
5549
- const endDateISO = editingDuration ? getEndDate(normalizedTask.startDate, durationValue) : toISODate(normalizedTask.endDate);
5584
+ const startDateISO = toISODate2(normalizedTask.startDate);
5585
+ const endDateISO = editingDuration ? getEndDate(normalizedTask.startDate, durationValue) : toISODate2(normalizedTask.endDate);
5550
5586
  const numberCell = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5551
5587
  "div",
5552
5588
  {
@@ -5764,26 +5800,16 @@ var TaskListRow = import_react10.default.memo(
5764
5800
  className: "gantt-tl-name-action-btn gantt-tl-action-insert",
5765
5801
  onClick: (e) => {
5766
5802
  e.stopPropagation();
5767
- const now = /* @__PURE__ */ new Date();
5768
- const todayISO = new Date(
5769
- Date.UTC(
5770
- now.getUTCFullYear(),
5771
- now.getUTCMonth(),
5772
- now.getUTCDate()
5773
- )
5774
- ).toISOString().split("T")[0];
5775
- const endISO = new Date(
5776
- Date.UTC(
5777
- now.getUTCFullYear(),
5778
- now.getUTCMonth(),
5779
- now.getUTCDate() + 7
5780
- )
5781
- ).toISOString().split("T")[0];
5803
+ const range = buildDefaultTaskDateRange(getTodayISODate(), {
5804
+ businessDays,
5805
+ defaultTaskDurationDays,
5806
+ weekendPredicate
5807
+ });
5782
5808
  const newTask = {
5783
5809
  id: crypto.randomUUID(),
5784
5810
  name: "\u041D\u043E\u0432\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430",
5785
- startDate: todayISO,
5786
- endDate: endISO,
5811
+ startDate: range.startDate,
5812
+ endDate: range.endDate,
5787
5813
  parentId: task.parentId
5788
5814
  };
5789
5815
  onInsertAfter(task.id, newTask);
@@ -6612,6 +6638,7 @@ var TaskList = ({
6612
6638
  onReorder,
6613
6639
  editingTaskId: propEditingTaskId,
6614
6640
  enableAddTask = true,
6641
+ defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
6615
6642
  collapsedParentIds: externalCollapsedParentIds,
6616
6643
  onToggleCollapse: externalOnToggleCollapse,
6617
6644
  onPromoteTask,
@@ -7014,26 +7041,20 @@ var TaskList = ({
7014
7041
  dragTaskIdRef.current = null;
7015
7042
  }, []);
7016
7043
  const handleConfirmNewTask = (0, import_react12.useCallback)((name) => {
7017
- const now = /* @__PURE__ */ new Date();
7018
- const todayISO = new Date(Date.UTC(
7019
- now.getUTCFullYear(),
7020
- now.getUTCMonth(),
7021
- now.getUTCDate()
7022
- )).toISOString().split("T")[0];
7023
- const endISO = new Date(Date.UTC(
7024
- now.getUTCFullYear(),
7025
- now.getUTCMonth(),
7026
- now.getUTCDate() + 7
7027
- )).toISOString().split("T")[0];
7044
+ const range = buildDefaultTaskDateRange(getTodayISODate(), {
7045
+ businessDays,
7046
+ defaultTaskDurationDays,
7047
+ weekendPredicate
7048
+ });
7028
7049
  const newTask = {
7029
7050
  id: crypto.randomUUID(),
7030
7051
  name,
7031
- startDate: todayISO,
7032
- endDate: endISO
7052
+ startDate: range.startDate,
7053
+ endDate: range.endDate
7033
7054
  };
7034
7055
  onAdd?.(newTask);
7035
7056
  setIsCreating(false);
7036
- }, [onAdd]);
7057
+ }, [businessDays, defaultTaskDurationDays, onAdd, weekendPredicate]);
7037
7058
  const handleCancelNewTask = (0, import_react12.useCallback)(() => setIsCreating(false), []);
7038
7059
  const findInsertAfterTaskId = (0, import_react12.useCallback)((anchorTaskId) => {
7039
7060
  const anchorIndex = orderedTasks.findIndex((task) => task.id === anchorTaskId);
@@ -7299,6 +7320,7 @@ var TaskList = ({
7299
7320
  customDays,
7300
7321
  isWeekend: isWeekend3,
7301
7322
  businessDays,
7323
+ defaultTaskDurationDays,
7302
7324
  isFilterMatch: filterMode === "highlight" ? highlightedTaskIds.has(task.id) : false,
7303
7325
  isFilterHideMode: filterMode === "hide" && isFilterActive,
7304
7326
  resolvedColumns,
@@ -7380,12 +7402,86 @@ var compareParsedItems = (a, b) => {
7380
7402
  }
7381
7403
  return a.item.id.localeCompare(b.item.id);
7382
7404
  };
7405
+ var getOverlapRange = (left, right) => {
7406
+ const startDay = Math.max(getUTCDayNumber(left.startDate), getUTCDayNumber(right.startDate));
7407
+ const endDay = Math.min(getUTCDayNumber(left.endDate), getUTCDayNumber(right.endDate));
7408
+ if (startDay > endDay) {
7409
+ return null;
7410
+ }
7411
+ return {
7412
+ startDate: new Date(startDay * 864e5),
7413
+ endDate: new Date(endDay * 864e5),
7414
+ itemIds: [left.item.id, right.item.id]
7415
+ };
7416
+ };
7417
+ var mergeConflictRanges = (ranges) => {
7418
+ const sortedRanges = [...ranges].sort(
7419
+ (left, right) => getUTCDayNumber(left.startDate) - getUTCDayNumber(right.startDate) || getUTCDayNumber(left.endDate) - getUTCDayNumber(right.endDate)
7420
+ );
7421
+ const merged = [];
7422
+ for (const range of sortedRanges) {
7423
+ const previous = merged[merged.length - 1];
7424
+ const rangeStartDay = getUTCDayNumber(range.startDate);
7425
+ const rangeEndDay = getUTCDayNumber(range.endDate);
7426
+ if (!previous || rangeStartDay > getUTCDayNumber(previous.endDate) + 1) {
7427
+ merged.push({
7428
+ startDate: range.startDate,
7429
+ endDate: range.endDate,
7430
+ itemIds: [...range.itemIds]
7431
+ });
7432
+ continue;
7433
+ }
7434
+ if (rangeEndDay > getUTCDayNumber(previous.endDate)) {
7435
+ previous.endDate = range.endDate;
7436
+ }
7437
+ previous.itemIds = Array.from(/* @__PURE__ */ new Set([...previous.itemIds, ...range.itemIds])).sort();
7438
+ }
7439
+ return merged;
7440
+ };
7441
+ var calculateConflictInfo = (parsedItems) => {
7442
+ const conflictRangesByItemId = /* @__PURE__ */ new Map();
7443
+ const conflictsWithByItemId = /* @__PURE__ */ new Map();
7444
+ for (let i = 0; i < parsedItems.length; i++) {
7445
+ for (let j = i + 1; j < parsedItems.length; j++) {
7446
+ const left = parsedItems[i];
7447
+ const right = parsedItems[j];
7448
+ if (getUTCDayNumber(right.startDate) > getUTCDayNumber(left.endDate)) {
7449
+ break;
7450
+ }
7451
+ const overlap = getOverlapRange(left, right);
7452
+ if (!overlap) {
7453
+ continue;
7454
+ }
7455
+ const leftRanges = conflictRangesByItemId.get(left.item.id) ?? [];
7456
+ const rightRanges = conflictRangesByItemId.get(right.item.id) ?? [];
7457
+ leftRanges.push(overlap);
7458
+ rightRanges.push(overlap);
7459
+ conflictRangesByItemId.set(left.item.id, leftRanges);
7460
+ conflictRangesByItemId.set(right.item.id, rightRanges);
7461
+ const leftConflicts = conflictsWithByItemId.get(left.item.id) ?? /* @__PURE__ */ new Set();
7462
+ const rightConflicts = conflictsWithByItemId.get(right.item.id) ?? /* @__PURE__ */ new Set();
7463
+ leftConflicts.add(right.item.id);
7464
+ rightConflicts.add(left.item.id);
7465
+ conflictsWithByItemId.set(left.item.id, leftConflicts);
7466
+ conflictsWithByItemId.set(right.item.id, rightConflicts);
7467
+ }
7468
+ }
7469
+ const result = /* @__PURE__ */ new Map();
7470
+ for (const parsedItem of parsedItems) {
7471
+ result.set(parsedItem.item.id, {
7472
+ conflictRanges: mergeConflictRanges(conflictRangesByItemId.get(parsedItem.item.id) ?? []),
7473
+ conflictsWith: Array.from(conflictsWithByItemId.get(parsedItem.item.id) ?? []).sort()
7474
+ });
7475
+ }
7476
+ return result;
7477
+ };
7383
7478
  var layoutResourceTimelineItems = (resources, options) => {
7384
7479
  const rows = [];
7385
7480
  const items = [];
7386
7481
  const diagnostics = [];
7387
7482
  let currentTop = 0;
7388
- for (const resource of resources) {
7483
+ const rowGap = options.rowGap ?? 0;
7484
+ resources.forEach((resource, resourceIndex) => {
7389
7485
  const parsedItems = [];
7390
7486
  for (const item of resource.items) {
7391
7487
  try {
@@ -7404,6 +7500,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7404
7500
  }
7405
7501
  }
7406
7502
  parsedItems.sort(compareParsedItems);
7503
+ const conflictInfoByItemId = calculateConflictInfo(parsedItems);
7407
7504
  const laneEndDays = [];
7408
7505
  const laidOutItems = [];
7409
7506
  for (const parsed of parsedItems) {
@@ -7417,6 +7514,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7417
7514
  laneEndDays[laneIndex] = endDay;
7418
7515
  }
7419
7516
  const { left, width } = calculateTaskBar(parsed.startDate, parsed.endDate, options.monthStart, options.dayWidth);
7517
+ const conflictInfo = conflictInfoByItemId.get(parsed.item.id);
7420
7518
  laidOutItems.push({
7421
7519
  item: parsed.item,
7422
7520
  itemId: parsed.item.id,
@@ -7429,15 +7527,19 @@ var layoutResourceTimelineItems = (resources, options) => {
7429
7527
  top: currentTop + laneIndex * options.laneHeight,
7430
7528
  height: options.laneHeight,
7431
7529
  startDate: parsed.startDate,
7432
- endDate: parsed.endDate
7530
+ endDate: parsed.endDate,
7531
+ conflictRanges: conflictInfo?.conflictRanges ?? [],
7532
+ conflictsWith: conflictInfo?.conflictsWith ?? []
7433
7533
  });
7434
7534
  }
7435
7535
  const laneCount = Math.max(1, laneEndDays.length);
7436
7536
  const resourceRowHeight = laneCount * options.laneHeight;
7537
+ const conflictCount = laidOutItems.filter((item) => item.conflictsWith.length > 0).length;
7437
7538
  const row = {
7438
7539
  resource,
7439
7540
  resourceId: resource.id,
7440
7541
  laneCount,
7542
+ conflictCount,
7441
7543
  resourceRowTop: currentTop,
7442
7544
  resourceRowHeight
7443
7545
  };
@@ -7446,8 +7548,8 @@ var layoutResourceTimelineItems = (resources, options) => {
7446
7548
  ...item,
7447
7549
  resourceRowHeight
7448
7550
  })));
7449
- currentTop += resourceRowHeight;
7450
- }
7551
+ currentTop += resourceRowHeight + rowGap;
7552
+ });
7451
7553
  return {
7452
7554
  rows,
7453
7555
  items,
@@ -7464,6 +7566,56 @@ var snapToDay = (pixels, dayWidth) => {
7464
7566
  var addUTCDays = (date, days) => {
7465
7567
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days));
7466
7568
  };
7569
+ var getDayOffset2 = (date, monthStart) => {
7570
+ return Math.round(
7571
+ (Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()) - Date.UTC(monthStart.getUTCFullYear(), monthStart.getUTCMonth(), monthStart.getUTCDate())) / (24 * 60 * 60 * 1e3)
7572
+ );
7573
+ };
7574
+ var resolveResourceMoveRange = (activeDrag, nextLeft, nextWidth) => {
7575
+ const dayOffset = Math.round(nextLeft / activeDrag.dayWidth);
7576
+ const durationDays = Math.round(nextWidth / activeDrag.dayWidth);
7577
+ const rawStartDate = addUTCDays(activeDrag.monthStart, dayOffset);
7578
+ const rawEndDate = addUTCDays(rawStartDate, durationDays - 1);
7579
+ if (!activeDrag.businessDays || !activeDrag.weekendPredicate) {
7580
+ return {
7581
+ startDate: rawStartDate,
7582
+ endDate: rawEndDate,
7583
+ left: nextLeft,
7584
+ width: nextWidth
7585
+ };
7586
+ }
7587
+ const range = (() => {
7588
+ if (activeDrag.mode === "resize-start") {
7589
+ const snapDirection2 = rawStartDate.getTime() >= activeDrag.startDate.getTime() ? 1 : -1;
7590
+ const duration = getBusinessDaysCount(rawStartDate, activeDrag.endDate, activeDrag.weekendPredicate);
7591
+ return buildTaskRangeFromEnd(activeDrag.endDate, duration, true, activeDrag.weekendPredicate, snapDirection2);
7592
+ }
7593
+ if (activeDrag.mode === "resize-end") {
7594
+ const snapDirection2 = rawEndDate.getTime() >= activeDrag.endDate.getTime() ? 1 : -1;
7595
+ const duration = getBusinessDaysCount(activeDrag.startDate, rawEndDate, activeDrag.weekendPredicate);
7596
+ return buildTaskRangeFromStart(activeDrag.startDate, duration, true, activeDrag.weekendPredicate, snapDirection2);
7597
+ }
7598
+ const dayDelta = Math.round((nextLeft - activeDrag.initialLeft) / activeDrag.dayWidth);
7599
+ const proposedStartDate = addUTCDays(activeDrag.startDate, dayDelta);
7600
+ const snapDirection = proposedStartDate.getTime() >= activeDrag.startDate.getTime() ? 1 : -1;
7601
+ return moveTaskRange(
7602
+ activeDrag.startDate,
7603
+ activeDrag.endDate,
7604
+ proposedStartDate,
7605
+ true,
7606
+ activeDrag.weekendPredicate,
7607
+ snapDirection
7608
+ );
7609
+ })();
7610
+ const startOffset = getDayOffset2(range.start, activeDrag.monthStart);
7611
+ const endOffset = getDayOffset2(range.end, activeDrag.monthStart);
7612
+ return {
7613
+ startDate: range.start,
7614
+ endDate: range.end,
7615
+ left: Math.round(startOffset * activeDrag.dayWidth),
7616
+ width: Math.round((endOffset - startOffset + 1) * activeDrag.dayWidth)
7617
+ };
7618
+ };
7467
7619
  var resolveTargetResource = (rows, clientY, gridTop) => {
7468
7620
  const localY = clientY - gridTop;
7469
7621
  return rows.find(
@@ -7472,10 +7624,13 @@ var resolveTargetResource = (rows, clientY, gridTop) => {
7472
7624
  };
7473
7625
  var useResourceItemDrag = ({
7474
7626
  dayWidth,
7627
+ monthStart,
7475
7628
  rows,
7476
7629
  gridElementRef,
7477
7630
  readonly,
7478
7631
  disableResourceReassignment,
7632
+ businessDays = false,
7633
+ weekendPredicate,
7479
7634
  onResourceItemMove
7480
7635
  }) => {
7481
7636
  const activeDragRef = (0, import_react13.useRef)(null);
@@ -7512,14 +7667,30 @@ var useResourceItemDrag = ({
7512
7667
  if (!latestDrag) {
7513
7668
  return;
7514
7669
  }
7515
- const nextLeft = latestDrag.initialLeft + snapToDay(event.clientX - latestDrag.startX, latestDrag.dayWidth);
7516
- const nextTop = disableResourceReassignment ? latestDrag.initialTop : latestDrag.initialTop + (event.clientY - latestDrag.startY);
7517
- latestDrag.currentLeft = nextLeft;
7670
+ const snappedDelta = snapToDay(event.clientX - latestDrag.startX, latestDrag.dayWidth);
7671
+ const rightEdge = latestDrag.initialLeft + latestDrag.initialWidth;
7672
+ const nextGeometry = (() => {
7673
+ if (latestDrag.mode === "resize-start") {
7674
+ const left = Math.min(latestDrag.initialLeft + snappedDelta, rightEdge - latestDrag.dayWidth);
7675
+ return { left, width: rightEdge - left };
7676
+ }
7677
+ if (latestDrag.mode === "resize-end") {
7678
+ return { left: latestDrag.initialLeft, width: Math.max(latestDrag.dayWidth, latestDrag.initialWidth + snappedDelta) };
7679
+ }
7680
+ return { left: latestDrag.initialLeft + snappedDelta, width: latestDrag.initialWidth };
7681
+ })();
7682
+ const nextRange = resolveResourceMoveRange(latestDrag, nextGeometry.left, nextGeometry.width);
7683
+ const nextTop = disableResourceReassignment || latestDrag.mode !== "move" ? latestDrag.initialTop : latestDrag.initialTop + (event.clientY - latestDrag.startY);
7684
+ latestDrag.currentLeft = nextRange.left;
7685
+ latestDrag.currentWidth = nextRange.width;
7518
7686
  latestDrag.currentTop = nextTop;
7519
7687
  setPreview({
7520
7688
  itemId: latestDrag.itemId,
7521
- left: nextLeft,
7522
- top: nextTop
7689
+ left: nextRange.left,
7690
+ top: nextTop,
7691
+ width: nextRange.width,
7692
+ startDate: nextRange.startDate,
7693
+ endDate: nextRange.endDate
7523
7694
  });
7524
7695
  });
7525
7696
  };
@@ -7532,18 +7703,20 @@ var useResourceItemDrag = ({
7532
7703
  activeDragRef.current = null;
7533
7704
  setPreview(null);
7534
7705
  const gridTop = gridElementRef?.current?.getBoundingClientRect().top ?? 0;
7535
- const targetResource = disableResourceReassignment ? rowsRef.current.find((row) => row.resourceId === activeDrag.fromResourceId)?.resource ?? null : resolveTargetResource(rowsRef.current, event.clientY, gridTop);
7706
+ const targetResource = disableResourceReassignment || activeDrag.mode !== "move" ? rowsRef.current.find((row) => row.resourceId === activeDrag.fromResourceId)?.resource ?? null : resolveTargetResource(rowsRef.current, event.clientY, gridTop);
7536
7707
  if (!targetResource) {
7537
7708
  return;
7538
7709
  }
7539
- const dayDelta = Math.round((activeDrag.currentLeft - activeDrag.initialLeft) / activeDrag.dayWidth);
7710
+ const nextRange = resolveResourceMoveRange(activeDrag, activeDrag.currentLeft, activeDrag.currentWidth);
7540
7711
  onResourceItemMoveRef.current?.({
7541
7712
  item: activeDrag.item,
7542
7713
  itemId: activeDrag.itemId,
7714
+ taskId: activeDrag.item.taskId,
7543
7715
  fromResourceId: activeDrag.fromResourceId,
7544
7716
  toResourceId: targetResource.id,
7545
- startDate: addUTCDays(activeDrag.startDate, dayDelta),
7546
- endDate: addUTCDays(activeDrag.endDate, dayDelta)
7717
+ startDate: nextRange.startDate,
7718
+ endDate: nextRange.endDate,
7719
+ changeType: activeDrag.mode
7547
7720
  });
7548
7721
  };
7549
7722
  window.addEventListener("mousemove", handleMouseMove);
@@ -7558,27 +7731,38 @@ var useResourceItemDrag = ({
7558
7731
  if (readonly || layoutItem.item.locked || event.button !== 0) {
7559
7732
  return;
7560
7733
  }
7734
+ const target = event.target;
7735
+ const mode = target.closest(".gantt-resourceTimeline-resizeHandleStart") ? "resize-start" : target.closest(".gantt-resourceTimeline-resizeHandleEnd") ? "resize-end" : "move";
7561
7736
  event.preventDefault();
7562
7737
  activeDragRef.current = {
7563
7738
  item: layoutItem.item,
7564
7739
  itemId: layoutItem.itemId,
7740
+ mode,
7565
7741
  fromResourceId: layoutItem.resourceId,
7566
7742
  startX: event.clientX,
7567
7743
  startY: event.clientY,
7568
7744
  initialLeft: layoutItem.left,
7569
7745
  initialTop: layoutItem.top,
7746
+ initialWidth: layoutItem.width,
7570
7747
  currentLeft: layoutItem.left,
7571
7748
  currentTop: layoutItem.top,
7749
+ currentWidth: layoutItem.width,
7572
7750
  dayWidth,
7573
7751
  startDate: layoutItem.startDate,
7574
- endDate: layoutItem.endDate
7752
+ endDate: layoutItem.endDate,
7753
+ monthStart,
7754
+ businessDays,
7755
+ weekendPredicate
7575
7756
  };
7576
7757
  setPreview({
7577
7758
  itemId: layoutItem.itemId,
7578
7759
  left: layoutItem.left,
7579
- top: layoutItem.top
7760
+ top: layoutItem.top,
7761
+ width: layoutItem.width,
7762
+ startDate: layoutItem.startDate,
7763
+ endDate: layoutItem.endDate
7580
7764
  });
7581
- }, [dayWidth, readonly]);
7765
+ }, [businessDays, dayWidth, monthStart, readonly, weekendPredicate]);
7582
7766
  return {
7583
7767
  preview,
7584
7768
  startDrag,
@@ -7592,6 +7776,7 @@ var DEFAULT_DAY_WIDTH = 40;
7592
7776
  var DEFAULT_HEADER_HEIGHT = 40;
7593
7777
  var DEFAULT_LANE_HEIGHT = 40;
7594
7778
  var DEFAULT_ROW_HEADER_WIDTH = 240;
7779
+ var DEFAULT_RESOURCE_ROW_GAP = 8;
7595
7780
  var ITEM_OUTER_VERTICAL_INSET = 2;
7596
7781
  var ITEM_INNER_VERTICAL_INSET = 1;
7597
7782
  var ITEM_HORIZONTAL_INSET = 1;
@@ -7651,12 +7836,58 @@ var getVisualItemGeometry = (geometry, laneIndex, laneCount) => {
7651
7836
  height: Math.max(0, geometry.height - topInset - bottomInset)
7652
7837
  };
7653
7838
  };
7839
+ var getWeekendOverlaySegments = (startDate, endDate, dayWidth, weekendPredicate) => {
7840
+ const segments2 = [];
7841
+ const current = new Date(Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(), startDate.getUTCDate()));
7842
+ let activeStartOffset = null;
7843
+ let offset = 0;
7844
+ while (current.getTime() <= endDate.getTime()) {
7845
+ const isWeekendDay = weekendPredicate(current);
7846
+ if (isWeekendDay && activeStartOffset === null) {
7847
+ activeStartOffset = offset;
7848
+ }
7849
+ if (!isWeekendDay && activeStartOffset !== null) {
7850
+ segments2.push({
7851
+ left: Math.round(activeStartOffset * dayWidth),
7852
+ width: Math.round((offset - activeStartOffset) * dayWidth)
7853
+ });
7854
+ activeStartOffset = null;
7855
+ }
7856
+ current.setUTCDate(current.getUTCDate() + 1);
7857
+ offset += 1;
7858
+ }
7859
+ if (activeStartOffset !== null) {
7860
+ segments2.push({
7861
+ left: Math.round(activeStartOffset * dayWidth),
7862
+ width: Math.round((offset - activeStartOffset) * dayWidth)
7863
+ });
7864
+ }
7865
+ return segments2;
7866
+ };
7867
+ var getRangeOverlaySegments = (itemStartDate, ranges, dayWidth) => {
7868
+ return ranges.map((range) => {
7869
+ const startOffset = Math.max(0, Math.round(
7870
+ (Date.UTC(range.startDate.getUTCFullYear(), range.startDate.getUTCMonth(), range.startDate.getUTCDate()) - Date.UTC(itemStartDate.getUTCFullYear(), itemStartDate.getUTCMonth(), itemStartDate.getUTCDate())) / (24 * 60 * 60 * 1e3)
7871
+ ));
7872
+ const endOffset = Math.max(startOffset, Math.round(
7873
+ (Date.UTC(range.endDate.getUTCFullYear(), range.endDate.getUTCMonth(), range.endDate.getUTCDate()) - Date.UTC(itemStartDate.getUTCFullYear(), itemStartDate.getUTCMonth(), itemStartDate.getUTCDate())) / (24 * 60 * 60 * 1e3)
7874
+ ));
7875
+ return {
7876
+ left: Math.round(startOffset * dayWidth),
7877
+ width: Math.round((endOffset - startOffset + 1) * dayWidth)
7878
+ };
7879
+ });
7880
+ };
7881
+ var getDurationValue = (startDate, endDate, businessDays, weekendPredicate) => businessDays ? getBusinessDaysCount(startDate, endDate, weekendPredicate) : Math.max(1, Math.round((endDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1e3)) + 1);
7654
7882
  function ResourceTimelineChart({
7655
7883
  resources,
7656
7884
  dayWidth = DEFAULT_DAY_WIDTH,
7657
7885
  rowHeaderWidth = DEFAULT_ROW_HEADER_WIDTH,
7658
7886
  laneHeight = DEFAULT_LANE_HEIGHT,
7659
7887
  headerHeight = DEFAULT_HEADER_HEIGHT,
7888
+ customDays,
7889
+ isWeekend: isWeekend3,
7890
+ businessDays = true,
7660
7891
  readonly,
7661
7892
  disableResourceReassignment,
7662
7893
  renderItem,
@@ -7674,9 +7905,18 @@ function ResourceTimelineChart({
7674
7905
  const firstDay = dateRange[0] ?? /* @__PURE__ */ new Date();
7675
7906
  return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));
7676
7907
  }, [dateRange]);
7908
+ const weekendPredicate = (0, import_react14.useMemo)(
7909
+ () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
7910
+ [customDays, isWeekend3]
7911
+ );
7677
7912
  const gridWidth = (0, import_react14.useMemo)(() => Math.round(dateRange.length * dayWidth), [dateRange.length, dayWidth]);
7678
7913
  const layout = (0, import_react14.useMemo)(
7679
- () => layoutResourceTimelineItems(resources, { monthStart, dayWidth, laneHeight }),
7914
+ () => layoutResourceTimelineItems(resources, {
7915
+ monthStart,
7916
+ dayWidth,
7917
+ laneHeight,
7918
+ rowGap: DEFAULT_RESOURCE_ROW_GAP
7919
+ }),
7680
7920
  [resources, monthStart, dayWidth, laneHeight]
7681
7921
  );
7682
7922
  const todayInRange = (0, import_react14.useMemo)(() => {
@@ -7695,10 +7935,13 @@ function ResourceTimelineChart({
7695
7935
  }, [layout.items]);
7696
7936
  const { preview, startDrag } = useResourceItemDrag({
7697
7937
  dayWidth,
7938
+ monthStart,
7698
7939
  rows: layout.rows,
7699
7940
  gridElementRef: gridRef,
7700
7941
  readonly,
7701
7942
  disableResourceReassignment,
7943
+ businessDays,
7944
+ weekendPredicate,
7702
7945
  onResourceItemMove
7703
7946
  });
7704
7947
  const handlePanStart = (0, import_react14.useCallback)((event) => {
@@ -7777,13 +8020,26 @@ function ResourceTimelineChart({
7777
8020
  style: { height: `${headerHeight}px` }
7778
8021
  }
7779
8022
  ),
7780
- layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8023
+ layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7781
8024
  "div",
7782
8025
  {
7783
8026
  className: "gantt-resourceTimeline-resourceHeader",
7784
8027
  "data-resource-row-id": row.resourceId,
7785
- style: { height: `${row.resourceRowHeight}px` },
7786
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceName", children: row.resource.name })
8028
+ style: {
8029
+ height: `${row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP}px`,
8030
+ paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
8031
+ },
8032
+ children: [
8033
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resourceName", children: row.resource.name }),
8034
+ row.conflictCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8035
+ "span",
8036
+ {
8037
+ className: "gantt-resourceTimeline-conflictBadge",
8038
+ "aria-label": `${row.conflictCount} \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u043E\u0432`,
8039
+ children: row.conflictCount
8040
+ }
8041
+ )
8042
+ ]
7787
8043
  },
7788
8044
  row.resourceId
7789
8045
  ))
@@ -7796,7 +8052,15 @@ function ResourceTimelineChart({
7796
8052
  className: "gantt-resourceTimeline-chartSurface",
7797
8053
  style: { minWidth: `${gridWidth}px` },
7798
8054
  children: [
7799
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-resourceTimeline-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TimeScaleHeader_default, { days: dateRange, dayWidth, headerHeight }) }),
8055
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-resourceTimeline-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8056
+ TimeScaleHeader_default,
8057
+ {
8058
+ days: dateRange,
8059
+ dayWidth,
8060
+ headerHeight,
8061
+ isCustomWeekend: weekendPredicate
8062
+ }
8063
+ ) }),
7800
8064
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7801
8065
  "div",
7802
8066
  {
@@ -7804,7 +8068,15 @@ function ResourceTimelineChart({
7804
8068
  className: "gantt-resourceTimeline-grid",
7805
8069
  style: { width: `${gridWidth}px`, height: `${layout.totalHeight}px` },
7806
8070
  children: [
7807
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(GridBackground_default, { dateRange, dayWidth, totalHeight: layout.totalHeight }),
8071
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8072
+ GridBackground_default,
8073
+ {
8074
+ dateRange,
8075
+ dayWidth,
8076
+ totalHeight: layout.totalHeight,
8077
+ isCustomWeekend: weekendPredicate
8078
+ }
8079
+ ),
7808
8080
  todayInRange && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
7809
8081
  layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7810
8082
  "div",
@@ -7813,7 +8085,7 @@ function ResourceTimelineChart({
7813
8085
  "data-resource-row-id": row.resourceId,
7814
8086
  style: {
7815
8087
  top: `${row.resourceRowTop}px`,
7816
- height: `${row.resourceRowHeight}px`
8088
+ height: `${row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP}px`
7817
8089
  }
7818
8090
  },
7819
8091
  row.resourceId
@@ -7821,17 +8093,18 @@ function ResourceTimelineChart({
7821
8093
  Array.from(itemsByResourceId.values()).flatMap(
7822
8094
  (resourceItems) => resourceItems.map((layoutItem) => {
7823
8095
  const customClassName = getItemClassName?.(layoutItem.item);
8096
+ const isDraggingItem = preview?.itemId === layoutItem.itemId;
7824
8097
  const className = [
7825
8098
  "gantt-resourceTimeline-item",
7826
- preview?.itemId === layoutItem.itemId && "gantt-resourceTimeline-itemDragging",
8099
+ isDraggingItem && "gantt-resourceTimeline-itemDragging",
7827
8100
  (readonly || layoutItem.item.locked) && "gantt-resourceTimeline-itemDisabled",
7828
8101
  customClassName
7829
8102
  ].filter(Boolean).join(" ");
7830
8103
  const laneCount = Math.max(1, Math.round(layoutItem.resourceRowHeight / layoutItem.height));
7831
- const previewStyle = preview?.itemId === layoutItem.itemId ? getVisualItemGeometry({
8104
+ const previewStyle = isDraggingItem ? getVisualItemGeometry({
7832
8105
  left: preview.left,
7833
8106
  top: preview.top,
7834
- width: layoutItem.width,
8107
+ width: preview.width,
7835
8108
  height: layoutItem.height
7836
8109
  }, layoutItem.laneIndex, laneCount) : void 0;
7837
8110
  const itemGeometry = getVisualItemGeometry({
@@ -7840,7 +8113,27 @@ function ResourceTimelineChart({
7840
8113
  width: layoutItem.width,
7841
8114
  height: layoutItem.height
7842
8115
  }, layoutItem.laneIndex, laneCount);
7843
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8116
+ const overlayStartDate = isDraggingItem ? preview.startDate : layoutItem.startDate;
8117
+ const overlayEndDate = isDraggingItem ? preview.endDate : layoutItem.endDate;
8118
+ const weekendOverlaySegments = businessDays ? getWeekendOverlaySegments(overlayStartDate, overlayEndDate, dayWidth, weekendPredicate) : [];
8119
+ const conflictOverlaySegments = getRangeOverlaySegments(
8120
+ overlayStartDate,
8121
+ isDraggingItem ? [] : layoutItem.conflictRanges,
8122
+ dayWidth
8123
+ );
8124
+ const durationValue = getDurationValue(
8125
+ overlayStartDate,
8126
+ overlayEndDate,
8127
+ businessDays,
8128
+ weekendPredicate
8129
+ );
8130
+ const renderContext = {
8131
+ startDate: overlayStartDate,
8132
+ endDate: overlayEndDate,
8133
+ durationDays: durationValue,
8134
+ isDragging: isDraggingItem
8135
+ };
8136
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7844
8137
  "div",
7845
8138
  {
7846
8139
  className,
@@ -7849,16 +8142,54 @@ function ResourceTimelineChart({
7849
8142
  style: {
7850
8143
  left: `${itemGeometry.left}px`,
7851
8144
  top: `${itemGeometry.top}px`,
7852
- ...previewStyle,
7853
8145
  width: `${itemGeometry.width}px`,
7854
8146
  height: `${itemGeometry.height}px`,
8147
+ ...previewStyle,
7855
8148
  backgroundColor: layoutItem.item.color ?? "var(--gantt-task-bar-default-color, #3b82f6)"
7856
8149
  },
7857
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-resourceTimeline-itemInner", children: renderItem ? renderItem(layoutItem.item) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
7858
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-itemTitle", children: layoutItem.item.title }),
7859
- layoutItem.item.subtitle && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-itemSubtitle", children: layoutItem.item.subtitle }),
7860
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-itemDates", children: formatDateRangeLabel(layoutItem.startDate, layoutItem.endDate) })
7861
- ] }) })
8150
+ children: [
8151
+ !readonly && !layoutItem.item.locked && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8152
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resizeHandle gantt-resourceTimeline-resizeHandleStart" }),
8153
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-resizeHandle gantt-resourceTimeline-resizeHandleEnd" })
8154
+ ] }),
8155
+ weekendOverlaySegments.map((segment, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8156
+ "span",
8157
+ {
8158
+ className: "gantt-resourceTimeline-weekendOverlay",
8159
+ "data-resource-weekend-overlay": "true",
8160
+ style: {
8161
+ left: `${segment.left}px`,
8162
+ width: `${segment.width}px`
8163
+ }
8164
+ },
8165
+ `weekend-overlay-${index}`
8166
+ )),
8167
+ conflictOverlaySegments.map((segment, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8168
+ "span",
8169
+ {
8170
+ className: "gantt-resourceTimeline-conflictOverlay",
8171
+ "data-resource-conflict-overlay": "true",
8172
+ style: {
8173
+ left: `${segment.left}px`,
8174
+ width: `${segment.width}px`
8175
+ }
8176
+ },
8177
+ `conflict-overlay-${index}`
8178
+ )),
8179
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-resourceTimeline-itemInner", children: renderItem ? renderItem(layoutItem.item, renderContext) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
8180
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8181
+ "span",
8182
+ {
8183
+ className: "gantt-resourceTimeline-itemDurationChip",
8184
+ "aria-label": `${durationValue} \u0434`,
8185
+ children: durationValue
8186
+ }
8187
+ ),
8188
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-itemTitle", children: layoutItem.item.title }),
8189
+ layoutItem.item.subtitle && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-itemSubtitle", children: layoutItem.item.subtitle }),
8190
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-itemDates", children: formatDateRangeLabel(layoutItem.startDate, layoutItem.endDate) })
8191
+ ] }) })
8192
+ ]
7862
8193
  },
7863
8194
  layoutItem.itemId
7864
8195
  );
@@ -8253,6 +8584,7 @@ function TaskGanttChartInner(props, ref) {
8253
8584
  onDemoteTask,
8254
8585
  onUngroupTask,
8255
8586
  enableAddTask = true,
8587
+ defaultTaskDurationDays,
8256
8588
  viewMode = "day",
8257
8589
  customDays,
8258
8590
  isWeekend: isWeekend3,
@@ -8782,6 +9114,7 @@ function TaskGanttChartInner(props, ref) {
8782
9114
  onReorder: handleReorder,
8783
9115
  editingTaskId,
8784
9116
  enableAddTask,
9117
+ defaultTaskDurationDays,
8785
9118
  collapsedParentIds,
8786
9119
  onToggleCollapse: handleToggleCollapse,
8787
9120
  onPromoteTask: onPromoteTask ?? handlePromoteTask,