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.mjs CHANGED
@@ -637,13 +637,13 @@ function computeParentProgress(parentId, tasks) {
637
637
  if (children.length === 0) {
638
638
  return 0;
639
639
  }
640
- const DAY_MS3 = 24 * 60 * 60 * 1e3;
640
+ const DAY_MS4 = 24 * 60 * 60 * 1e3;
641
641
  let totalWeight = 0;
642
642
  let weightedSum = 0;
643
643
  for (const child of children) {
644
644
  const start = new Date(child.startDate).getTime();
645
645
  const end = new Date(child.endDate).getTime();
646
- const duration = (end - start + DAY_MS3) / DAY_MS3;
646
+ const duration = (end - start + DAY_MS4) / DAY_MS4;
647
647
  const progress = child.progress ?? 0;
648
648
  totalWeight += duration;
649
649
  weightedSum += duration * progress;
@@ -738,10 +738,10 @@ function buildTaskRangeFromStart(startDate, duration, businessDays = false, week
738
738
  end: parseDateOnly(addBusinessDays(normalizedStart, duration, weekendPredicate))
739
739
  };
740
740
  }
741
- const DAY_MS3 = 24 * 60 * 60 * 1e3;
741
+ const DAY_MS4 = 24 * 60 * 60 * 1e3;
742
742
  return {
743
743
  start: normalizedStart,
744
- end: new Date(normalizedStart.getTime() + (Math.max(1, duration) - 1) * DAY_MS3)
744
+ end: new Date(normalizedStart.getTime() + (Math.max(1, duration) - 1) * DAY_MS4)
745
745
  };
746
746
  }
747
747
  function buildTaskRangeFromEnd(endDate, duration, businessDays = false, weekendPredicate, snapDirection = -1) {
@@ -752,9 +752,9 @@ function buildTaskRangeFromEnd(endDate, duration, businessDays = false, weekendP
752
752
  end: normalizedEnd
753
753
  };
754
754
  }
755
- const DAY_MS3 = 24 * 60 * 60 * 1e3;
755
+ const DAY_MS4 = 24 * 60 * 60 * 1e3;
756
756
  return {
757
- start: new Date(normalizedEnd.getTime() - (Math.max(1, duration) - 1) * DAY_MS3),
757
+ start: new Date(normalizedEnd.getTime() - (Math.max(1, duration) - 1) * DAY_MS4),
758
758
  end: normalizedEnd
759
759
  };
760
760
  }
@@ -4088,21 +4088,56 @@ var LINK_TYPE_LABELS = {
4088
4088
  SF: "\u041D\u0430\u0447\u0430\u043B\u043E-\u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435"
4089
4089
  };
4090
4090
 
4091
+ // src/components/TaskList/defaultTaskDates.ts
4092
+ init_dateUtils();
4093
+ var DAY_MS2 = 24 * 60 * 60 * 1e3;
4094
+ var DEFAULT_TASK_DURATION_DAYS = 5;
4095
+ function toISODate(date) {
4096
+ return date.toISOString().split("T")[0];
4097
+ }
4098
+ function getTodayISODate() {
4099
+ const now = /* @__PURE__ */ new Date();
4100
+ return toISODate(new Date(Date.UTC(
4101
+ now.getUTCFullYear(),
4102
+ now.getUTCMonth(),
4103
+ now.getUTCDate()
4104
+ )));
4105
+ }
4106
+ function buildDefaultTaskDateRange(startDate, {
4107
+ businessDays,
4108
+ defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
4109
+ weekendPredicate
4110
+ } = {}) {
4111
+ const durationDays = Math.max(1, Math.round(defaultTaskDurationDays));
4112
+ const start = parseUTCDate(startDate);
4113
+ if (businessDays && weekendPredicate) {
4114
+ const range = buildTaskRangeFromStart(start, durationDays, true, weekendPredicate);
4115
+ return {
4116
+ startDate: toISODate(range.start),
4117
+ endDate: toISODate(range.end)
4118
+ };
4119
+ }
4120
+ return {
4121
+ startDate: toISODate(start),
4122
+ endDate: toISODate(new Date(start.getTime() + (durationDays - 1) * DAY_MS2))
4123
+ };
4124
+ }
4125
+
4091
4126
  // src/components/TaskList/TaskListRow.tsx
4092
4127
  import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
4093
- var DAY_MS2 = 24 * 60 * 60 * 1e3;
4128
+ var DAY_MS3 = 24 * 60 * 60 * 1e3;
4094
4129
  var LINK_TYPE_ORDER = ["FS", "SS", "FF", "SF"];
4095
4130
  var getInclusiveDurationDays = (startDate, endDate) => {
4096
4131
  const start = parseUTCDate(startDate);
4097
4132
  const end = parseUTCDate(endDate);
4098
4133
  return Math.max(
4099
4134
  1,
4100
- Math.round((end.getTime() - start.getTime()) / DAY_MS2) + 1
4135
+ Math.round((end.getTime() - start.getTime()) / DAY_MS3) + 1
4101
4136
  );
4102
4137
  };
4103
4138
  var getEndDateFromDuration = (startDate, durationDays) => {
4104
4139
  const start = parseUTCDate(startDate);
4105
- return new Date(start.getTime() + (durationDays - 1) * DAY_MS2).toISOString().split("T")[0];
4140
+ return new Date(start.getTime() + (durationDays - 1) * DAY_MS3).toISOString().split("T")[0];
4106
4141
  };
4107
4142
  var TrashIcon = () => /* @__PURE__ */ jsxs9(
4108
4143
  "svg",
@@ -4587,7 +4622,7 @@ var DepChip = ({
4587
4622
  )
4588
4623
  ] });
4589
4624
  };
4590
- var toISODate = (value) => {
4625
+ var toISODate2 = (value) => {
4591
4626
  if (value instanceof Date) return value.toISOString().split("T")[0];
4592
4627
  if (typeof value === "string" && value.includes("T"))
4593
4628
  return value.split("T")[0];
@@ -4641,6 +4676,7 @@ var TaskListRow = React9.memo(
4641
4676
  customDays,
4642
4677
  isWeekend: isWeekend3,
4643
4678
  businessDays,
4679
+ defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
4644
4680
  isFilterMatch = false,
4645
4681
  isFilterHideMode = false,
4646
4682
  resolvedColumns,
@@ -5448,8 +5484,8 @@ var TaskListRow = React9.memo(
5448
5484
  },
5449
5485
  [selectedChip?.successorId, selectedChip?.predecessorId, selectedChip?.linkType, onRemoveDependency, onChipSelect]
5450
5486
  );
5451
- const startDateISO = toISODate(normalizedTask.startDate);
5452
- const endDateISO = editingDuration ? getEndDate(normalizedTask.startDate, durationValue) : toISODate(normalizedTask.endDate);
5487
+ const startDateISO = toISODate2(normalizedTask.startDate);
5488
+ const endDateISO = editingDuration ? getEndDate(normalizedTask.startDate, durationValue) : toISODate2(normalizedTask.endDate);
5453
5489
  const numberCell = /* @__PURE__ */ jsxs9(
5454
5490
  "div",
5455
5491
  {
@@ -5667,26 +5703,16 @@ var TaskListRow = React9.memo(
5667
5703
  className: "gantt-tl-name-action-btn gantt-tl-action-insert",
5668
5704
  onClick: (e) => {
5669
5705
  e.stopPropagation();
5670
- const now = /* @__PURE__ */ new Date();
5671
- const todayISO = new Date(
5672
- Date.UTC(
5673
- now.getUTCFullYear(),
5674
- now.getUTCMonth(),
5675
- now.getUTCDate()
5676
- )
5677
- ).toISOString().split("T")[0];
5678
- const endISO = new Date(
5679
- Date.UTC(
5680
- now.getUTCFullYear(),
5681
- now.getUTCMonth(),
5682
- now.getUTCDate() + 7
5683
- )
5684
- ).toISOString().split("T")[0];
5706
+ const range = buildDefaultTaskDateRange(getTodayISODate(), {
5707
+ businessDays,
5708
+ defaultTaskDurationDays,
5709
+ weekendPredicate
5710
+ });
5685
5711
  const newTask = {
5686
5712
  id: crypto.randomUUID(),
5687
5713
  name: "\u041D\u043E\u0432\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430",
5688
- startDate: todayISO,
5689
- endDate: endISO,
5714
+ startDate: range.startDate,
5715
+ endDate: range.endDate,
5690
5716
  parentId: task.parentId
5691
5717
  };
5692
5718
  onInsertAfter(task.id, newTask);
@@ -6515,6 +6541,7 @@ var TaskList = ({
6515
6541
  onReorder,
6516
6542
  editingTaskId: propEditingTaskId,
6517
6543
  enableAddTask = true,
6544
+ defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
6518
6545
  collapsedParentIds: externalCollapsedParentIds,
6519
6546
  onToggleCollapse: externalOnToggleCollapse,
6520
6547
  onPromoteTask,
@@ -6917,26 +6944,20 @@ var TaskList = ({
6917
6944
  dragTaskIdRef.current = null;
6918
6945
  }, []);
6919
6946
  const handleConfirmNewTask = useCallback5((name) => {
6920
- const now = /* @__PURE__ */ new Date();
6921
- const todayISO = new Date(Date.UTC(
6922
- now.getUTCFullYear(),
6923
- now.getUTCMonth(),
6924
- now.getUTCDate()
6925
- )).toISOString().split("T")[0];
6926
- const endISO = new Date(Date.UTC(
6927
- now.getUTCFullYear(),
6928
- now.getUTCMonth(),
6929
- now.getUTCDate() + 7
6930
- )).toISOString().split("T")[0];
6947
+ const range = buildDefaultTaskDateRange(getTodayISODate(), {
6948
+ businessDays,
6949
+ defaultTaskDurationDays,
6950
+ weekendPredicate
6951
+ });
6931
6952
  const newTask = {
6932
6953
  id: crypto.randomUUID(),
6933
6954
  name,
6934
- startDate: todayISO,
6935
- endDate: endISO
6955
+ startDate: range.startDate,
6956
+ endDate: range.endDate
6936
6957
  };
6937
6958
  onAdd?.(newTask);
6938
6959
  setIsCreating(false);
6939
- }, [onAdd]);
6960
+ }, [businessDays, defaultTaskDurationDays, onAdd, weekendPredicate]);
6940
6961
  const handleCancelNewTask = useCallback5(() => setIsCreating(false), []);
6941
6962
  const findInsertAfterTaskId = useCallback5((anchorTaskId) => {
6942
6963
  const anchorIndex = orderedTasks.findIndex((task) => task.id === anchorTaskId);
@@ -7202,6 +7223,7 @@ var TaskList = ({
7202
7223
  customDays,
7203
7224
  isWeekend: isWeekend3,
7204
7225
  businessDays,
7226
+ defaultTaskDurationDays,
7205
7227
  isFilterMatch: filterMode === "highlight" ? highlightedTaskIds.has(task.id) : false,
7206
7228
  isFilterHideMode: filterMode === "hide" && isFilterActive,
7207
7229
  resolvedColumns,
@@ -7283,12 +7305,86 @@ var compareParsedItems = (a, b) => {
7283
7305
  }
7284
7306
  return a.item.id.localeCompare(b.item.id);
7285
7307
  };
7308
+ var getOverlapRange = (left, right) => {
7309
+ const startDay = Math.max(getUTCDayNumber(left.startDate), getUTCDayNumber(right.startDate));
7310
+ const endDay = Math.min(getUTCDayNumber(left.endDate), getUTCDayNumber(right.endDate));
7311
+ if (startDay > endDay) {
7312
+ return null;
7313
+ }
7314
+ return {
7315
+ startDate: new Date(startDay * 864e5),
7316
+ endDate: new Date(endDay * 864e5),
7317
+ itemIds: [left.item.id, right.item.id]
7318
+ };
7319
+ };
7320
+ var mergeConflictRanges = (ranges) => {
7321
+ const sortedRanges = [...ranges].sort(
7322
+ (left, right) => getUTCDayNumber(left.startDate) - getUTCDayNumber(right.startDate) || getUTCDayNumber(left.endDate) - getUTCDayNumber(right.endDate)
7323
+ );
7324
+ const merged = [];
7325
+ for (const range of sortedRanges) {
7326
+ const previous = merged[merged.length - 1];
7327
+ const rangeStartDay = getUTCDayNumber(range.startDate);
7328
+ const rangeEndDay = getUTCDayNumber(range.endDate);
7329
+ if (!previous || rangeStartDay > getUTCDayNumber(previous.endDate) + 1) {
7330
+ merged.push({
7331
+ startDate: range.startDate,
7332
+ endDate: range.endDate,
7333
+ itemIds: [...range.itemIds]
7334
+ });
7335
+ continue;
7336
+ }
7337
+ if (rangeEndDay > getUTCDayNumber(previous.endDate)) {
7338
+ previous.endDate = range.endDate;
7339
+ }
7340
+ previous.itemIds = Array.from(/* @__PURE__ */ new Set([...previous.itemIds, ...range.itemIds])).sort();
7341
+ }
7342
+ return merged;
7343
+ };
7344
+ var calculateConflictInfo = (parsedItems) => {
7345
+ const conflictRangesByItemId = /* @__PURE__ */ new Map();
7346
+ const conflictsWithByItemId = /* @__PURE__ */ new Map();
7347
+ for (let i = 0; i < parsedItems.length; i++) {
7348
+ for (let j = i + 1; j < parsedItems.length; j++) {
7349
+ const left = parsedItems[i];
7350
+ const right = parsedItems[j];
7351
+ if (getUTCDayNumber(right.startDate) > getUTCDayNumber(left.endDate)) {
7352
+ break;
7353
+ }
7354
+ const overlap = getOverlapRange(left, right);
7355
+ if (!overlap) {
7356
+ continue;
7357
+ }
7358
+ const leftRanges = conflictRangesByItemId.get(left.item.id) ?? [];
7359
+ const rightRanges = conflictRangesByItemId.get(right.item.id) ?? [];
7360
+ leftRanges.push(overlap);
7361
+ rightRanges.push(overlap);
7362
+ conflictRangesByItemId.set(left.item.id, leftRanges);
7363
+ conflictRangesByItemId.set(right.item.id, rightRanges);
7364
+ const leftConflicts = conflictsWithByItemId.get(left.item.id) ?? /* @__PURE__ */ new Set();
7365
+ const rightConflicts = conflictsWithByItemId.get(right.item.id) ?? /* @__PURE__ */ new Set();
7366
+ leftConflicts.add(right.item.id);
7367
+ rightConflicts.add(left.item.id);
7368
+ conflictsWithByItemId.set(left.item.id, leftConflicts);
7369
+ conflictsWithByItemId.set(right.item.id, rightConflicts);
7370
+ }
7371
+ }
7372
+ const result = /* @__PURE__ */ new Map();
7373
+ for (const parsedItem of parsedItems) {
7374
+ result.set(parsedItem.item.id, {
7375
+ conflictRanges: mergeConflictRanges(conflictRangesByItemId.get(parsedItem.item.id) ?? []),
7376
+ conflictsWith: Array.from(conflictsWithByItemId.get(parsedItem.item.id) ?? []).sort()
7377
+ });
7378
+ }
7379
+ return result;
7380
+ };
7286
7381
  var layoutResourceTimelineItems = (resources, options) => {
7287
7382
  const rows = [];
7288
7383
  const items = [];
7289
7384
  const diagnostics = [];
7290
7385
  let currentTop = 0;
7291
- for (const resource of resources) {
7386
+ const rowGap = options.rowGap ?? 0;
7387
+ resources.forEach((resource, resourceIndex) => {
7292
7388
  const parsedItems = [];
7293
7389
  for (const item of resource.items) {
7294
7390
  try {
@@ -7307,6 +7403,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7307
7403
  }
7308
7404
  }
7309
7405
  parsedItems.sort(compareParsedItems);
7406
+ const conflictInfoByItemId = calculateConflictInfo(parsedItems);
7310
7407
  const laneEndDays = [];
7311
7408
  const laidOutItems = [];
7312
7409
  for (const parsed of parsedItems) {
@@ -7320,6 +7417,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7320
7417
  laneEndDays[laneIndex] = endDay;
7321
7418
  }
7322
7419
  const { left, width } = calculateTaskBar(parsed.startDate, parsed.endDate, options.monthStart, options.dayWidth);
7420
+ const conflictInfo = conflictInfoByItemId.get(parsed.item.id);
7323
7421
  laidOutItems.push({
7324
7422
  item: parsed.item,
7325
7423
  itemId: parsed.item.id,
@@ -7332,15 +7430,19 @@ var layoutResourceTimelineItems = (resources, options) => {
7332
7430
  top: currentTop + laneIndex * options.laneHeight,
7333
7431
  height: options.laneHeight,
7334
7432
  startDate: parsed.startDate,
7335
- endDate: parsed.endDate
7433
+ endDate: parsed.endDate,
7434
+ conflictRanges: conflictInfo?.conflictRanges ?? [],
7435
+ conflictsWith: conflictInfo?.conflictsWith ?? []
7336
7436
  });
7337
7437
  }
7338
7438
  const laneCount = Math.max(1, laneEndDays.length);
7339
7439
  const resourceRowHeight = laneCount * options.laneHeight;
7440
+ const conflictCount = laidOutItems.filter((item) => item.conflictsWith.length > 0).length;
7340
7441
  const row = {
7341
7442
  resource,
7342
7443
  resourceId: resource.id,
7343
7444
  laneCount,
7445
+ conflictCount,
7344
7446
  resourceRowTop: currentTop,
7345
7447
  resourceRowHeight
7346
7448
  };
@@ -7349,8 +7451,8 @@ var layoutResourceTimelineItems = (resources, options) => {
7349
7451
  ...item,
7350
7452
  resourceRowHeight
7351
7453
  })));
7352
- currentTop += resourceRowHeight;
7353
- }
7454
+ currentTop += resourceRowHeight + rowGap;
7455
+ });
7354
7456
  return {
7355
7457
  rows,
7356
7458
  items,
@@ -7367,6 +7469,56 @@ var snapToDay = (pixels, dayWidth) => {
7367
7469
  var addUTCDays = (date, days) => {
7368
7470
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days));
7369
7471
  };
7472
+ var getDayOffset2 = (date, monthStart) => {
7473
+ return Math.round(
7474
+ (Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()) - Date.UTC(monthStart.getUTCFullYear(), monthStart.getUTCMonth(), monthStart.getUTCDate())) / (24 * 60 * 60 * 1e3)
7475
+ );
7476
+ };
7477
+ var resolveResourceMoveRange = (activeDrag, nextLeft, nextWidth) => {
7478
+ const dayOffset = Math.round(nextLeft / activeDrag.dayWidth);
7479
+ const durationDays = Math.round(nextWidth / activeDrag.dayWidth);
7480
+ const rawStartDate = addUTCDays(activeDrag.monthStart, dayOffset);
7481
+ const rawEndDate = addUTCDays(rawStartDate, durationDays - 1);
7482
+ if (!activeDrag.businessDays || !activeDrag.weekendPredicate) {
7483
+ return {
7484
+ startDate: rawStartDate,
7485
+ endDate: rawEndDate,
7486
+ left: nextLeft,
7487
+ width: nextWidth
7488
+ };
7489
+ }
7490
+ const range = (() => {
7491
+ if (activeDrag.mode === "resize-start") {
7492
+ const snapDirection2 = rawStartDate.getTime() >= activeDrag.startDate.getTime() ? 1 : -1;
7493
+ const duration = getBusinessDaysCount(rawStartDate, activeDrag.endDate, activeDrag.weekendPredicate);
7494
+ return buildTaskRangeFromEnd(activeDrag.endDate, duration, true, activeDrag.weekendPredicate, snapDirection2);
7495
+ }
7496
+ if (activeDrag.mode === "resize-end") {
7497
+ const snapDirection2 = rawEndDate.getTime() >= activeDrag.endDate.getTime() ? 1 : -1;
7498
+ const duration = getBusinessDaysCount(activeDrag.startDate, rawEndDate, activeDrag.weekendPredicate);
7499
+ return buildTaskRangeFromStart(activeDrag.startDate, duration, true, activeDrag.weekendPredicate, snapDirection2);
7500
+ }
7501
+ const dayDelta = Math.round((nextLeft - activeDrag.initialLeft) / activeDrag.dayWidth);
7502
+ const proposedStartDate = addUTCDays(activeDrag.startDate, dayDelta);
7503
+ const snapDirection = proposedStartDate.getTime() >= activeDrag.startDate.getTime() ? 1 : -1;
7504
+ return moveTaskRange(
7505
+ activeDrag.startDate,
7506
+ activeDrag.endDate,
7507
+ proposedStartDate,
7508
+ true,
7509
+ activeDrag.weekendPredicate,
7510
+ snapDirection
7511
+ );
7512
+ })();
7513
+ const startOffset = getDayOffset2(range.start, activeDrag.monthStart);
7514
+ const endOffset = getDayOffset2(range.end, activeDrag.monthStart);
7515
+ return {
7516
+ startDate: range.start,
7517
+ endDate: range.end,
7518
+ left: Math.round(startOffset * activeDrag.dayWidth),
7519
+ width: Math.round((endOffset - startOffset + 1) * activeDrag.dayWidth)
7520
+ };
7521
+ };
7370
7522
  var resolveTargetResource = (rows, clientY, gridTop) => {
7371
7523
  const localY = clientY - gridTop;
7372
7524
  return rows.find(
@@ -7375,10 +7527,13 @@ var resolveTargetResource = (rows, clientY, gridTop) => {
7375
7527
  };
7376
7528
  var useResourceItemDrag = ({
7377
7529
  dayWidth,
7530
+ monthStart,
7378
7531
  rows,
7379
7532
  gridElementRef,
7380
7533
  readonly,
7381
7534
  disableResourceReassignment,
7535
+ businessDays = false,
7536
+ weekendPredicate,
7382
7537
  onResourceItemMove
7383
7538
  }) => {
7384
7539
  const activeDragRef = useRef7(null);
@@ -7415,14 +7570,30 @@ var useResourceItemDrag = ({
7415
7570
  if (!latestDrag) {
7416
7571
  return;
7417
7572
  }
7418
- const nextLeft = latestDrag.initialLeft + snapToDay(event.clientX - latestDrag.startX, latestDrag.dayWidth);
7419
- const nextTop = disableResourceReassignment ? latestDrag.initialTop : latestDrag.initialTop + (event.clientY - latestDrag.startY);
7420
- latestDrag.currentLeft = nextLeft;
7573
+ const snappedDelta = snapToDay(event.clientX - latestDrag.startX, latestDrag.dayWidth);
7574
+ const rightEdge = latestDrag.initialLeft + latestDrag.initialWidth;
7575
+ const nextGeometry = (() => {
7576
+ if (latestDrag.mode === "resize-start") {
7577
+ const left = Math.min(latestDrag.initialLeft + snappedDelta, rightEdge - latestDrag.dayWidth);
7578
+ return { left, width: rightEdge - left };
7579
+ }
7580
+ if (latestDrag.mode === "resize-end") {
7581
+ return { left: latestDrag.initialLeft, width: Math.max(latestDrag.dayWidth, latestDrag.initialWidth + snappedDelta) };
7582
+ }
7583
+ return { left: latestDrag.initialLeft + snappedDelta, width: latestDrag.initialWidth };
7584
+ })();
7585
+ const nextRange = resolveResourceMoveRange(latestDrag, nextGeometry.left, nextGeometry.width);
7586
+ const nextTop = disableResourceReassignment || latestDrag.mode !== "move" ? latestDrag.initialTop : latestDrag.initialTop + (event.clientY - latestDrag.startY);
7587
+ latestDrag.currentLeft = nextRange.left;
7588
+ latestDrag.currentWidth = nextRange.width;
7421
7589
  latestDrag.currentTop = nextTop;
7422
7590
  setPreview({
7423
7591
  itemId: latestDrag.itemId,
7424
- left: nextLeft,
7425
- top: nextTop
7592
+ left: nextRange.left,
7593
+ top: nextTop,
7594
+ width: nextRange.width,
7595
+ startDate: nextRange.startDate,
7596
+ endDate: nextRange.endDate
7426
7597
  });
7427
7598
  });
7428
7599
  };
@@ -7435,18 +7606,20 @@ var useResourceItemDrag = ({
7435
7606
  activeDragRef.current = null;
7436
7607
  setPreview(null);
7437
7608
  const gridTop = gridElementRef?.current?.getBoundingClientRect().top ?? 0;
7438
- const targetResource = disableResourceReassignment ? rowsRef.current.find((row) => row.resourceId === activeDrag.fromResourceId)?.resource ?? null : resolveTargetResource(rowsRef.current, event.clientY, gridTop);
7609
+ const targetResource = disableResourceReassignment || activeDrag.mode !== "move" ? rowsRef.current.find((row) => row.resourceId === activeDrag.fromResourceId)?.resource ?? null : resolveTargetResource(rowsRef.current, event.clientY, gridTop);
7439
7610
  if (!targetResource) {
7440
7611
  return;
7441
7612
  }
7442
- const dayDelta = Math.round((activeDrag.currentLeft - activeDrag.initialLeft) / activeDrag.dayWidth);
7613
+ const nextRange = resolveResourceMoveRange(activeDrag, activeDrag.currentLeft, activeDrag.currentWidth);
7443
7614
  onResourceItemMoveRef.current?.({
7444
7615
  item: activeDrag.item,
7445
7616
  itemId: activeDrag.itemId,
7617
+ taskId: activeDrag.item.taskId,
7446
7618
  fromResourceId: activeDrag.fromResourceId,
7447
7619
  toResourceId: targetResource.id,
7448
- startDate: addUTCDays(activeDrag.startDate, dayDelta),
7449
- endDate: addUTCDays(activeDrag.endDate, dayDelta)
7620
+ startDate: nextRange.startDate,
7621
+ endDate: nextRange.endDate,
7622
+ changeType: activeDrag.mode
7450
7623
  });
7451
7624
  };
7452
7625
  window.addEventListener("mousemove", handleMouseMove);
@@ -7461,27 +7634,38 @@ var useResourceItemDrag = ({
7461
7634
  if (readonly || layoutItem.item.locked || event.button !== 0) {
7462
7635
  return;
7463
7636
  }
7637
+ const target = event.target;
7638
+ const mode = target.closest(".gantt-resourceTimeline-resizeHandleStart") ? "resize-start" : target.closest(".gantt-resourceTimeline-resizeHandleEnd") ? "resize-end" : "move";
7464
7639
  event.preventDefault();
7465
7640
  activeDragRef.current = {
7466
7641
  item: layoutItem.item,
7467
7642
  itemId: layoutItem.itemId,
7643
+ mode,
7468
7644
  fromResourceId: layoutItem.resourceId,
7469
7645
  startX: event.clientX,
7470
7646
  startY: event.clientY,
7471
7647
  initialLeft: layoutItem.left,
7472
7648
  initialTop: layoutItem.top,
7649
+ initialWidth: layoutItem.width,
7473
7650
  currentLeft: layoutItem.left,
7474
7651
  currentTop: layoutItem.top,
7652
+ currentWidth: layoutItem.width,
7475
7653
  dayWidth,
7476
7654
  startDate: layoutItem.startDate,
7477
- endDate: layoutItem.endDate
7655
+ endDate: layoutItem.endDate,
7656
+ monthStart,
7657
+ businessDays,
7658
+ weekendPredicate
7478
7659
  };
7479
7660
  setPreview({
7480
7661
  itemId: layoutItem.itemId,
7481
7662
  left: layoutItem.left,
7482
- top: layoutItem.top
7663
+ top: layoutItem.top,
7664
+ width: layoutItem.width,
7665
+ startDate: layoutItem.startDate,
7666
+ endDate: layoutItem.endDate
7483
7667
  });
7484
- }, [dayWidth, readonly]);
7668
+ }, [businessDays, dayWidth, monthStart, readonly, weekendPredicate]);
7485
7669
  return {
7486
7670
  preview,
7487
7671
  startDrag,
@@ -7495,6 +7679,7 @@ var DEFAULT_DAY_WIDTH = 40;
7495
7679
  var DEFAULT_HEADER_HEIGHT = 40;
7496
7680
  var DEFAULT_LANE_HEIGHT = 40;
7497
7681
  var DEFAULT_ROW_HEADER_WIDTH = 240;
7682
+ var DEFAULT_RESOURCE_ROW_GAP = 8;
7498
7683
  var ITEM_OUTER_VERTICAL_INSET = 2;
7499
7684
  var ITEM_INNER_VERTICAL_INSET = 1;
7500
7685
  var ITEM_HORIZONTAL_INSET = 1;
@@ -7554,12 +7739,58 @@ var getVisualItemGeometry = (geometry, laneIndex, laneCount) => {
7554
7739
  height: Math.max(0, geometry.height - topInset - bottomInset)
7555
7740
  };
7556
7741
  };
7742
+ var getWeekendOverlaySegments = (startDate, endDate, dayWidth, weekendPredicate) => {
7743
+ const segments2 = [];
7744
+ const current = new Date(Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(), startDate.getUTCDate()));
7745
+ let activeStartOffset = null;
7746
+ let offset = 0;
7747
+ while (current.getTime() <= endDate.getTime()) {
7748
+ const isWeekendDay = weekendPredicate(current);
7749
+ if (isWeekendDay && activeStartOffset === null) {
7750
+ activeStartOffset = offset;
7751
+ }
7752
+ if (!isWeekendDay && activeStartOffset !== null) {
7753
+ segments2.push({
7754
+ left: Math.round(activeStartOffset * dayWidth),
7755
+ width: Math.round((offset - activeStartOffset) * dayWidth)
7756
+ });
7757
+ activeStartOffset = null;
7758
+ }
7759
+ current.setUTCDate(current.getUTCDate() + 1);
7760
+ offset += 1;
7761
+ }
7762
+ if (activeStartOffset !== null) {
7763
+ segments2.push({
7764
+ left: Math.round(activeStartOffset * dayWidth),
7765
+ width: Math.round((offset - activeStartOffset) * dayWidth)
7766
+ });
7767
+ }
7768
+ return segments2;
7769
+ };
7770
+ var getRangeOverlaySegments = (itemStartDate, ranges, dayWidth) => {
7771
+ return ranges.map((range) => {
7772
+ const startOffset = Math.max(0, Math.round(
7773
+ (Date.UTC(range.startDate.getUTCFullYear(), range.startDate.getUTCMonth(), range.startDate.getUTCDate()) - Date.UTC(itemStartDate.getUTCFullYear(), itemStartDate.getUTCMonth(), itemStartDate.getUTCDate())) / (24 * 60 * 60 * 1e3)
7774
+ ));
7775
+ const endOffset = Math.max(startOffset, Math.round(
7776
+ (Date.UTC(range.endDate.getUTCFullYear(), range.endDate.getUTCMonth(), range.endDate.getUTCDate()) - Date.UTC(itemStartDate.getUTCFullYear(), itemStartDate.getUTCMonth(), itemStartDate.getUTCDate())) / (24 * 60 * 60 * 1e3)
7777
+ ));
7778
+ return {
7779
+ left: Math.round(startOffset * dayWidth),
7780
+ width: Math.round((endOffset - startOffset + 1) * dayWidth)
7781
+ };
7782
+ });
7783
+ };
7784
+ 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);
7557
7785
  function ResourceTimelineChart({
7558
7786
  resources,
7559
7787
  dayWidth = DEFAULT_DAY_WIDTH,
7560
7788
  rowHeaderWidth = DEFAULT_ROW_HEADER_WIDTH,
7561
7789
  laneHeight = DEFAULT_LANE_HEIGHT,
7562
7790
  headerHeight = DEFAULT_HEADER_HEIGHT,
7791
+ customDays,
7792
+ isWeekend: isWeekend3,
7793
+ businessDays = true,
7563
7794
  readonly,
7564
7795
  disableResourceReassignment,
7565
7796
  renderItem,
@@ -7577,9 +7808,18 @@ function ResourceTimelineChart({
7577
7808
  const firstDay = dateRange[0] ?? /* @__PURE__ */ new Date();
7578
7809
  return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));
7579
7810
  }, [dateRange]);
7811
+ const weekendPredicate = useMemo9(
7812
+ () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
7813
+ [customDays, isWeekend3]
7814
+ );
7580
7815
  const gridWidth = useMemo9(() => Math.round(dateRange.length * dayWidth), [dateRange.length, dayWidth]);
7581
7816
  const layout = useMemo9(
7582
- () => layoutResourceTimelineItems(resources, { monthStart, dayWidth, laneHeight }),
7817
+ () => layoutResourceTimelineItems(resources, {
7818
+ monthStart,
7819
+ dayWidth,
7820
+ laneHeight,
7821
+ rowGap: DEFAULT_RESOURCE_ROW_GAP
7822
+ }),
7583
7823
  [resources, monthStart, dayWidth, laneHeight]
7584
7824
  );
7585
7825
  const todayInRange = useMemo9(() => {
@@ -7598,10 +7838,13 @@ function ResourceTimelineChart({
7598
7838
  }, [layout.items]);
7599
7839
  const { preview, startDrag } = useResourceItemDrag({
7600
7840
  dayWidth,
7841
+ monthStart,
7601
7842
  rows: layout.rows,
7602
7843
  gridElementRef: gridRef,
7603
7844
  readonly,
7604
7845
  disableResourceReassignment,
7846
+ businessDays,
7847
+ weekendPredicate,
7605
7848
  onResourceItemMove
7606
7849
  });
7607
7850
  const handlePanStart = useCallback7((event) => {
@@ -7680,13 +7923,26 @@ function ResourceTimelineChart({
7680
7923
  style: { height: `${headerHeight}px` }
7681
7924
  }
7682
7925
  ),
7683
- layout.rows.map((row) => /* @__PURE__ */ jsx15(
7926
+ layout.rows.map((row) => /* @__PURE__ */ jsxs12(
7684
7927
  "div",
7685
7928
  {
7686
7929
  className: "gantt-resourceTimeline-resourceHeader",
7687
7930
  "data-resource-row-id": row.resourceId,
7688
- style: { height: `${row.resourceRowHeight}px` },
7689
- children: /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceName", children: row.resource.name })
7931
+ style: {
7932
+ height: `${row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP}px`,
7933
+ paddingBottom: `${DEFAULT_RESOURCE_ROW_GAP}px`
7934
+ },
7935
+ children: [
7936
+ /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resourceName", children: row.resource.name }),
7937
+ row.conflictCount > 0 && /* @__PURE__ */ jsx15(
7938
+ "span",
7939
+ {
7940
+ className: "gantt-resourceTimeline-conflictBadge",
7941
+ "aria-label": `${row.conflictCount} \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u043E\u0432`,
7942
+ children: row.conflictCount
7943
+ }
7944
+ )
7945
+ ]
7690
7946
  },
7691
7947
  row.resourceId
7692
7948
  ))
@@ -7699,7 +7955,15 @@ function ResourceTimelineChart({
7699
7955
  className: "gantt-resourceTimeline-chartSurface",
7700
7956
  style: { minWidth: `${gridWidth}px` },
7701
7957
  children: [
7702
- /* @__PURE__ */ jsx15("div", { className: "gantt-resourceTimeline-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ jsx15(TimeScaleHeader_default, { days: dateRange, dayWidth, headerHeight }) }),
7958
+ /* @__PURE__ */ jsx15("div", { className: "gantt-resourceTimeline-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ jsx15(
7959
+ TimeScaleHeader_default,
7960
+ {
7961
+ days: dateRange,
7962
+ dayWidth,
7963
+ headerHeight,
7964
+ isCustomWeekend: weekendPredicate
7965
+ }
7966
+ ) }),
7703
7967
  /* @__PURE__ */ jsxs12(
7704
7968
  "div",
7705
7969
  {
@@ -7707,7 +7971,15 @@ function ResourceTimelineChart({
7707
7971
  className: "gantt-resourceTimeline-grid",
7708
7972
  style: { width: `${gridWidth}px`, height: `${layout.totalHeight}px` },
7709
7973
  children: [
7710
- /* @__PURE__ */ jsx15(GridBackground_default, { dateRange, dayWidth, totalHeight: layout.totalHeight }),
7974
+ /* @__PURE__ */ jsx15(
7975
+ GridBackground_default,
7976
+ {
7977
+ dateRange,
7978
+ dayWidth,
7979
+ totalHeight: layout.totalHeight,
7980
+ isCustomWeekend: weekendPredicate
7981
+ }
7982
+ ),
7711
7983
  todayInRange && /* @__PURE__ */ jsx15(TodayIndicator_default, { monthStart, dayWidth }),
7712
7984
  layout.rows.map((row) => /* @__PURE__ */ jsx15(
7713
7985
  "div",
@@ -7716,7 +7988,7 @@ function ResourceTimelineChart({
7716
7988
  "data-resource-row-id": row.resourceId,
7717
7989
  style: {
7718
7990
  top: `${row.resourceRowTop}px`,
7719
- height: `${row.resourceRowHeight}px`
7991
+ height: `${row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP}px`
7720
7992
  }
7721
7993
  },
7722
7994
  row.resourceId
@@ -7724,17 +7996,18 @@ function ResourceTimelineChart({
7724
7996
  Array.from(itemsByResourceId.values()).flatMap(
7725
7997
  (resourceItems) => resourceItems.map((layoutItem) => {
7726
7998
  const customClassName = getItemClassName?.(layoutItem.item);
7999
+ const isDraggingItem = preview?.itemId === layoutItem.itemId;
7727
8000
  const className = [
7728
8001
  "gantt-resourceTimeline-item",
7729
- preview?.itemId === layoutItem.itemId && "gantt-resourceTimeline-itemDragging",
8002
+ isDraggingItem && "gantt-resourceTimeline-itemDragging",
7730
8003
  (readonly || layoutItem.item.locked) && "gantt-resourceTimeline-itemDisabled",
7731
8004
  customClassName
7732
8005
  ].filter(Boolean).join(" ");
7733
8006
  const laneCount = Math.max(1, Math.round(layoutItem.resourceRowHeight / layoutItem.height));
7734
- const previewStyle = preview?.itemId === layoutItem.itemId ? getVisualItemGeometry({
8007
+ const previewStyle = isDraggingItem ? getVisualItemGeometry({
7735
8008
  left: preview.left,
7736
8009
  top: preview.top,
7737
- width: layoutItem.width,
8010
+ width: preview.width,
7738
8011
  height: layoutItem.height
7739
8012
  }, layoutItem.laneIndex, laneCount) : void 0;
7740
8013
  const itemGeometry = getVisualItemGeometry({
@@ -7743,7 +8016,27 @@ function ResourceTimelineChart({
7743
8016
  width: layoutItem.width,
7744
8017
  height: layoutItem.height
7745
8018
  }, layoutItem.laneIndex, laneCount);
7746
- return /* @__PURE__ */ jsx15(
8019
+ const overlayStartDate = isDraggingItem ? preview.startDate : layoutItem.startDate;
8020
+ const overlayEndDate = isDraggingItem ? preview.endDate : layoutItem.endDate;
8021
+ const weekendOverlaySegments = businessDays ? getWeekendOverlaySegments(overlayStartDate, overlayEndDate, dayWidth, weekendPredicate) : [];
8022
+ const conflictOverlaySegments = getRangeOverlaySegments(
8023
+ overlayStartDate,
8024
+ isDraggingItem ? [] : layoutItem.conflictRanges,
8025
+ dayWidth
8026
+ );
8027
+ const durationValue = getDurationValue(
8028
+ overlayStartDate,
8029
+ overlayEndDate,
8030
+ businessDays,
8031
+ weekendPredicate
8032
+ );
8033
+ const renderContext = {
8034
+ startDate: overlayStartDate,
8035
+ endDate: overlayEndDate,
8036
+ durationDays: durationValue,
8037
+ isDragging: isDraggingItem
8038
+ };
8039
+ return /* @__PURE__ */ jsxs12(
7747
8040
  "div",
7748
8041
  {
7749
8042
  className,
@@ -7752,16 +8045,54 @@ function ResourceTimelineChart({
7752
8045
  style: {
7753
8046
  left: `${itemGeometry.left}px`,
7754
8047
  top: `${itemGeometry.top}px`,
7755
- ...previewStyle,
7756
8048
  width: `${itemGeometry.width}px`,
7757
8049
  height: `${itemGeometry.height}px`,
8050
+ ...previewStyle,
7758
8051
  backgroundColor: layoutItem.item.color ?? "var(--gantt-task-bar-default-color, #3b82f6)"
7759
8052
  },
7760
- children: /* @__PURE__ */ jsx15("div", { className: "gantt-resourceTimeline-itemInner", children: renderItem ? renderItem(layoutItem.item) : /* @__PURE__ */ jsxs12(Fragment3, { children: [
7761
- /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-itemTitle", children: layoutItem.item.title }),
7762
- layoutItem.item.subtitle && /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-itemSubtitle", children: layoutItem.item.subtitle }),
7763
- /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-itemDates", children: formatDateRangeLabel(layoutItem.startDate, layoutItem.endDate) })
7764
- ] }) })
8053
+ children: [
8054
+ !readonly && !layoutItem.item.locked && /* @__PURE__ */ jsxs12(Fragment3, { children: [
8055
+ /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resizeHandle gantt-resourceTimeline-resizeHandleStart" }),
8056
+ /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-resizeHandle gantt-resourceTimeline-resizeHandleEnd" })
8057
+ ] }),
8058
+ weekendOverlaySegments.map((segment, index) => /* @__PURE__ */ jsx15(
8059
+ "span",
8060
+ {
8061
+ className: "gantt-resourceTimeline-weekendOverlay",
8062
+ "data-resource-weekend-overlay": "true",
8063
+ style: {
8064
+ left: `${segment.left}px`,
8065
+ width: `${segment.width}px`
8066
+ }
8067
+ },
8068
+ `weekend-overlay-${index}`
8069
+ )),
8070
+ conflictOverlaySegments.map((segment, index) => /* @__PURE__ */ jsx15(
8071
+ "span",
8072
+ {
8073
+ className: "gantt-resourceTimeline-conflictOverlay",
8074
+ "data-resource-conflict-overlay": "true",
8075
+ style: {
8076
+ left: `${segment.left}px`,
8077
+ width: `${segment.width}px`
8078
+ }
8079
+ },
8080
+ `conflict-overlay-${index}`
8081
+ )),
8082
+ /* @__PURE__ */ jsx15("div", { className: "gantt-resourceTimeline-itemInner", children: renderItem ? renderItem(layoutItem.item, renderContext) : /* @__PURE__ */ jsxs12(Fragment3, { children: [
8083
+ /* @__PURE__ */ jsx15(
8084
+ "span",
8085
+ {
8086
+ className: "gantt-resourceTimeline-itemDurationChip",
8087
+ "aria-label": `${durationValue} \u0434`,
8088
+ children: durationValue
8089
+ }
8090
+ ),
8091
+ /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-itemTitle", children: layoutItem.item.title }),
8092
+ layoutItem.item.subtitle && /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-itemSubtitle", children: layoutItem.item.subtitle }),
8093
+ /* @__PURE__ */ jsx15("span", { className: "gantt-resourceTimeline-itemDates", children: formatDateRangeLabel(layoutItem.startDate, layoutItem.endDate) })
8094
+ ] }) })
8095
+ ]
7765
8096
  },
7766
8097
  layoutItem.itemId
7767
8098
  );
@@ -8156,6 +8487,7 @@ function TaskGanttChartInner(props, ref) {
8156
8487
  onDemoteTask,
8157
8488
  onUngroupTask,
8158
8489
  enableAddTask = true,
8490
+ defaultTaskDurationDays,
8159
8491
  viewMode = "day",
8160
8492
  customDays,
8161
8493
  isWeekend: isWeekend3,
@@ -8685,6 +9017,7 @@ function TaskGanttChartInner(props, ref) {
8685
9017
  onReorder: handleReorder,
8686
9018
  editingTaskId,
8687
9019
  enableAddTask,
9020
+ defaultTaskDurationDays,
8688
9021
  collapsedParentIds,
8689
9022
  onToggleCollapse: handleToggleCollapse,
8690
9023
  onPromoteTask: onPromoteTask ?? handlePromoteTask,