gantt-lib 0.75.1 → 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.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1, { ReactNode } from 'react';
2
- import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as ResourcePlannerChartProps, b as ResourceTimelineResource } from './index-B2Hmdkey.js';
3
- export { D as DAY_MS, c as DependencyError, G as GanttChartMode, d as GanttDateRange, e as GridConfig, f as GridLine, L as LinkType, M as MonthSpan, g as ResourceTimelineMove, h as TaskBarGeometry, W as WeekendBlock, i as alignToWorkingDay, j as areTasksHierarchicallyRelated, k as buildAdjacencyList, l as buildTaskRangeFromEnd, m as buildTaskRangeFromStart, n as calculateSuccessorDate, o as cascadeByLinks, p as clampTaskRangeForIncomingFS, q as computeLagFromDates, r as computeParentDates, s as computeParentProgress, t as detectCycles, u as findParentId, v as getAllDependencyEdges, w as getAllDescendants, x as getBusinessDayOffset, y as getChildren, z as getDependencyLag, A as getSuccessorChain, B as getTaskDuration, C as getTransitiveCascadeChain, E as isAncestorTask, F as isTaskParent, H as moveTaskRange, I as moveTaskWithCascade, J as normalizeDependencyLag, K as normalizePredecessorDates, N as normalizeUTCDate, O as parseDateOnly, P as recalculateIncomingLags, Q as recalculateProjectSchedule, S as recalculateTaskFromDependencies, U as reflowTasksOnModeSwitch, X as removeDependenciesBetweenTasks, Y as resizeTaskWithCascade, Z as shiftBusinessDayOffset, _ as universalCascade, $ as validateDependencies } from './index-B2Hmdkey.js';
2
+ import { T as Task$1, R as ResourceTimelineItem, V as ValidationResult, a as ResourcePlannerChartProps, b as ResourceTimelineResource } from './index-Bl8l7O3Y.js';
3
+ export { D as DAY_MS, c as DependencyError, G as GanttChartMode, d as GanttDateRange, e as GridConfig, f as GridLine, L as LinkType, M as MonthSpan, g as ResourceTimelineMove, h as TaskBarGeometry, W as WeekendBlock, i as alignToWorkingDay, j as areTasksHierarchicallyRelated, k as buildAdjacencyList, l as buildTaskRangeFromEnd, m as buildTaskRangeFromStart, n as calculateSuccessorDate, o as cascadeByLinks, p as clampTaskRangeForIncomingFS, q as computeLagFromDates, r as computeParentDates, s as computeParentProgress, t as detectCycles, u as findParentId, v as getAllDependencyEdges, w as getAllDescendants, x as getBusinessDayOffset, y as getChildren, z as getDependencyLag, A as getSuccessorChain, B as getTaskDuration, C as getTransitiveCascadeChain, E as isAncestorTask, F as isTaskParent, H as moveTaskRange, I as moveTaskWithCascade, J as normalizeDependencyLag, K as normalizePredecessorDates, N as normalizeUTCDate, O as parseDateOnly, P as recalculateIncomingLags, Q as recalculateProjectSchedule, S as recalculateTaskFromDependencies, U as reflowTasksOnModeSwitch, X as removeDependenciesBetweenTasks, Y as resizeTaskWithCascade, Z as shiftBusinessDayOffset, _ as universalCascade, $ as validateDependencies } from './index-Bl8l7O3Y.js';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import * as RadixPopover from '@radix-ui/react-popover';
6
6
 
@@ -540,7 +540,7 @@ declare const GanttChart: <TTask extends Task = Task, TItem extends ResourceTime
540
540
  ref?: React$1.Ref<GanttChartHandle>;
541
541
  }) => React$1.ReactElement;
542
542
 
543
- declare function ResourceTimelineChart<TItem extends ResourceTimelineItem = ResourceTimelineItem>({ resources, dayWidth, rowHeaderWidth, laneHeight, headerHeight, readonly, disableResourceReassignment, renderItem, getItemClassName, onResourceItemMove, }: ResourcePlannerChartProps<TItem>): react_jsx_runtime.JSX.Element;
543
+ declare function ResourceTimelineChart<TItem extends ResourceTimelineItem = ResourceTimelineItem>({ resources, dayWidth, rowHeaderWidth, laneHeight, headerHeight, customDays, isWeekend, businessDays, readonly, disableResourceReassignment, renderItem, getItemClassName, onResourceItemMove, }: ResourcePlannerChartProps<TItem>): react_jsx_runtime.JSX.Element;
544
544
 
545
545
  interface TaskRowProps {
546
546
  /** Task data to render */
@@ -1189,14 +1189,21 @@ interface ResourceTimelineLayoutOptions {
1189
1189
  monthStart: Date;
1190
1190
  dayWidth: number;
1191
1191
  laneHeight: number;
1192
+ rowGap?: number;
1192
1193
  }
1193
1194
  interface ResourceTimelineLayoutRow<TItem extends ResourceTimelineItem = ResourceTimelineItem> {
1194
1195
  resource: ResourceTimelineResource<TItem>;
1195
1196
  resourceId: string;
1196
1197
  laneCount: number;
1198
+ conflictCount: number;
1197
1199
  resourceRowTop: number;
1198
1200
  resourceRowHeight: number;
1199
1201
  }
1202
+ interface ResourceTimelineConflictRange {
1203
+ startDate: Date;
1204
+ endDate: Date;
1205
+ itemIds: string[];
1206
+ }
1200
1207
  interface ResourceTimelineLayoutItem<TItem extends ResourceTimelineItem = ResourceTimelineItem> {
1201
1208
  item: TItem;
1202
1209
  itemId: string;
@@ -1210,6 +1217,8 @@ interface ResourceTimelineLayoutItem<TItem extends ResourceTimelineItem = Resour
1210
1217
  height: number;
1211
1218
  startDate: Date;
1212
1219
  endDate: Date;
1220
+ conflictRanges: ResourceTimelineConflictRange[];
1221
+ conflictsWith: string[];
1213
1222
  }
1214
1223
  interface ResourceTimelineLayoutDiagnostic {
1215
1224
  itemId: string;
@@ -1240,4 +1249,4 @@ interface VisibleReorderPosition {
1240
1249
  */
1241
1250
  declare function getVisibleReorderPosition(orderedTasks: TaskLike[], visibleTasks: TaskLike[], movedTaskId: string, originVisibleIndex: number, dropVisibleIndex: number): VisibleReorderPosition | null;
1242
1251
 
1243
- export { type BuiltInTaskListColumnId, Button, type ButtonProps, Calendar, type CalendarProps, type CustomDayConfig, type CustomDayPredicateConfig, DatePicker, type DatePickerProps, DragGuideLines, type ExportToPdfHeaderOptions, type ExportToPdfOptions, GanttChart, type GanttChartHandle, type GanttChartProps, type GanttModeProps, GridBackground, Input, type InputProps, type MonthBlock, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, ResourcePlannerChartProps, ResourceTimelineChart, ResourceTimelineItem, type ResourceTimelineLayoutDiagnostic, type ResourceTimelineLayoutItem, type ResourceTimelineLayoutOptions, type ResourceTimelineLayoutResult, type ResourceTimelineLayoutRow, ResourceTimelineResource, type Task, type TaskDependency, TaskList, type TaskListColumn, type TaskListColumnContext, type TaskListMenuCommand, type TaskListProps, type TaskPredicate, TaskRow, TimeScaleHeader, TodayIndicator, ValidationResult, type VisibleReorderPosition, type WeekBlock, type WeekSpan, type YearSpan, addBusinessDays, and, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateMilestoneConnectionBounds, calculateMilestoneGeometry, calculateMonthGridLines, calculateOrthogonalPath, calculateTaskBar, calculateWeekGridLines, calculateWeekendBlocks, clampDateRangeForIncomingFS, createCustomDayPredicate, createDateKey, detectEdgeZone, expired, flattenHierarchy, formatDateLabel, formatDateRangeLabel, getBusinessDaysCount, getCursorForPosition, getDayOffset, getMonthBlocks, getMonthDays, getMonthSpans, getMultiMonthDays, getVisibleReorderPosition, getWeekBlocks, getWeekSpans, getYearSpans, inDateRange, isTaskExpired, isToday, isWeekend, layoutResourceTimelineItems, nameContains, normalizeHierarchyTasks, normalizeTaskDates, not, or, parseUTCDate, pixelsToDate, progressInRange, resolveDateRangeFromPixels, resolveTaskHorizontalGeometry, subtractBusinessDays, useTaskDrag, withoutDeps };
1252
+ export { type BuiltInTaskListColumnId, Button, type ButtonProps, Calendar, type CalendarProps, type CustomDayConfig, type CustomDayPredicateConfig, DatePicker, type DatePickerProps, DragGuideLines, type ExportToPdfHeaderOptions, type ExportToPdfOptions, GanttChart, type GanttChartHandle, type GanttChartProps, type GanttModeProps, GridBackground, Input, type InputProps, type MonthBlock, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, ResourcePlannerChartProps, ResourceTimelineChart, type ResourceTimelineConflictRange, ResourceTimelineItem, type ResourceTimelineLayoutDiagnostic, type ResourceTimelineLayoutItem, type ResourceTimelineLayoutOptions, type ResourceTimelineLayoutResult, type ResourceTimelineLayoutRow, ResourceTimelineResource, type Task, type TaskDependency, TaskList, type TaskListColumn, type TaskListColumnContext, type TaskListMenuCommand, type TaskListProps, type TaskPredicate, TaskRow, TimeScaleHeader, TodayIndicator, ValidationResult, type VisibleReorderPosition, type WeekBlock, type WeekSpan, type YearSpan, addBusinessDays, and, calculateBezierPath, calculateDependencyPath, calculateGridLines, calculateGridWidth, calculateMilestoneConnectionBounds, calculateMilestoneGeometry, calculateMonthGridLines, calculateOrthogonalPath, calculateTaskBar, calculateWeekGridLines, calculateWeekendBlocks, clampDateRangeForIncomingFS, createCustomDayPredicate, createDateKey, detectEdgeZone, expired, flattenHierarchy, formatDateLabel, formatDateRangeLabel, getBusinessDaysCount, getCursorForPosition, getDayOffset, getMonthBlocks, getMonthDays, getMonthSpans, getMultiMonthDays, getVisibleReorderPosition, getWeekBlocks, getWeekSpans, getYearSpans, inDateRange, isTaskExpired, isToday, isWeekend, layoutResourceTimelineItems, nameContains, normalizeHierarchyTasks, normalizeTaskDates, not, or, parseUTCDate, pixelsToDate, progressInRange, resolveDateRangeFromPixels, resolveTaskHorizontalGeometry, subtractBusinessDays, useTaskDrag, withoutDeps };
package/dist/index.js CHANGED
@@ -7402,12 +7402,86 @@ var compareParsedItems = (a, b) => {
7402
7402
  }
7403
7403
  return a.item.id.localeCompare(b.item.id);
7404
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
+ };
7405
7478
  var layoutResourceTimelineItems = (resources, options) => {
7406
7479
  const rows = [];
7407
7480
  const items = [];
7408
7481
  const diagnostics = [];
7409
7482
  let currentTop = 0;
7410
- for (const resource of resources) {
7483
+ const rowGap = options.rowGap ?? 0;
7484
+ resources.forEach((resource, resourceIndex) => {
7411
7485
  const parsedItems = [];
7412
7486
  for (const item of resource.items) {
7413
7487
  try {
@@ -7426,6 +7500,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7426
7500
  }
7427
7501
  }
7428
7502
  parsedItems.sort(compareParsedItems);
7503
+ const conflictInfoByItemId = calculateConflictInfo(parsedItems);
7429
7504
  const laneEndDays = [];
7430
7505
  const laidOutItems = [];
7431
7506
  for (const parsed of parsedItems) {
@@ -7439,6 +7514,7 @@ var layoutResourceTimelineItems = (resources, options) => {
7439
7514
  laneEndDays[laneIndex] = endDay;
7440
7515
  }
7441
7516
  const { left, width } = calculateTaskBar(parsed.startDate, parsed.endDate, options.monthStart, options.dayWidth);
7517
+ const conflictInfo = conflictInfoByItemId.get(parsed.item.id);
7442
7518
  laidOutItems.push({
7443
7519
  item: parsed.item,
7444
7520
  itemId: parsed.item.id,
@@ -7451,15 +7527,19 @@ var layoutResourceTimelineItems = (resources, options) => {
7451
7527
  top: currentTop + laneIndex * options.laneHeight,
7452
7528
  height: options.laneHeight,
7453
7529
  startDate: parsed.startDate,
7454
- endDate: parsed.endDate
7530
+ endDate: parsed.endDate,
7531
+ conflictRanges: conflictInfo?.conflictRanges ?? [],
7532
+ conflictsWith: conflictInfo?.conflictsWith ?? []
7455
7533
  });
7456
7534
  }
7457
7535
  const laneCount = Math.max(1, laneEndDays.length);
7458
7536
  const resourceRowHeight = laneCount * options.laneHeight;
7537
+ const conflictCount = laidOutItems.filter((item) => item.conflictsWith.length > 0).length;
7459
7538
  const row = {
7460
7539
  resource,
7461
7540
  resourceId: resource.id,
7462
7541
  laneCount,
7542
+ conflictCount,
7463
7543
  resourceRowTop: currentTop,
7464
7544
  resourceRowHeight
7465
7545
  };
@@ -7468,8 +7548,8 @@ var layoutResourceTimelineItems = (resources, options) => {
7468
7548
  ...item,
7469
7549
  resourceRowHeight
7470
7550
  })));
7471
- currentTop += resourceRowHeight;
7472
- }
7551
+ currentTop += resourceRowHeight + rowGap;
7552
+ });
7473
7553
  return {
7474
7554
  rows,
7475
7555
  items,
@@ -7486,6 +7566,56 @@ var snapToDay = (pixels, dayWidth) => {
7486
7566
  var addUTCDays = (date, days) => {
7487
7567
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days));
7488
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
+ };
7489
7619
  var resolveTargetResource = (rows, clientY, gridTop) => {
7490
7620
  const localY = clientY - gridTop;
7491
7621
  return rows.find(
@@ -7494,10 +7624,13 @@ var resolveTargetResource = (rows, clientY, gridTop) => {
7494
7624
  };
7495
7625
  var useResourceItemDrag = ({
7496
7626
  dayWidth,
7627
+ monthStart,
7497
7628
  rows,
7498
7629
  gridElementRef,
7499
7630
  readonly,
7500
7631
  disableResourceReassignment,
7632
+ businessDays = false,
7633
+ weekendPredicate,
7501
7634
  onResourceItemMove
7502
7635
  }) => {
7503
7636
  const activeDragRef = (0, import_react13.useRef)(null);
@@ -7534,14 +7667,30 @@ var useResourceItemDrag = ({
7534
7667
  if (!latestDrag) {
7535
7668
  return;
7536
7669
  }
7537
- const nextLeft = latestDrag.initialLeft + snapToDay(event.clientX - latestDrag.startX, latestDrag.dayWidth);
7538
- const nextTop = disableResourceReassignment ? latestDrag.initialTop : latestDrag.initialTop + (event.clientY - latestDrag.startY);
7539
- 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;
7540
7686
  latestDrag.currentTop = nextTop;
7541
7687
  setPreview({
7542
7688
  itemId: latestDrag.itemId,
7543
- left: nextLeft,
7544
- top: nextTop
7689
+ left: nextRange.left,
7690
+ top: nextTop,
7691
+ width: nextRange.width,
7692
+ startDate: nextRange.startDate,
7693
+ endDate: nextRange.endDate
7545
7694
  });
7546
7695
  });
7547
7696
  };
@@ -7554,18 +7703,20 @@ var useResourceItemDrag = ({
7554
7703
  activeDragRef.current = null;
7555
7704
  setPreview(null);
7556
7705
  const gridTop = gridElementRef?.current?.getBoundingClientRect().top ?? 0;
7557
- 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);
7558
7707
  if (!targetResource) {
7559
7708
  return;
7560
7709
  }
7561
- const dayDelta = Math.round((activeDrag.currentLeft - activeDrag.initialLeft) / activeDrag.dayWidth);
7710
+ const nextRange = resolveResourceMoveRange(activeDrag, activeDrag.currentLeft, activeDrag.currentWidth);
7562
7711
  onResourceItemMoveRef.current?.({
7563
7712
  item: activeDrag.item,
7564
7713
  itemId: activeDrag.itemId,
7714
+ taskId: activeDrag.item.taskId,
7565
7715
  fromResourceId: activeDrag.fromResourceId,
7566
7716
  toResourceId: targetResource.id,
7567
- startDate: addUTCDays(activeDrag.startDate, dayDelta),
7568
- endDate: addUTCDays(activeDrag.endDate, dayDelta)
7717
+ startDate: nextRange.startDate,
7718
+ endDate: nextRange.endDate,
7719
+ changeType: activeDrag.mode
7569
7720
  });
7570
7721
  };
7571
7722
  window.addEventListener("mousemove", handleMouseMove);
@@ -7580,27 +7731,38 @@ var useResourceItemDrag = ({
7580
7731
  if (readonly || layoutItem.item.locked || event.button !== 0) {
7581
7732
  return;
7582
7733
  }
7734
+ const target = event.target;
7735
+ const mode = target.closest(".gantt-resourceTimeline-resizeHandleStart") ? "resize-start" : target.closest(".gantt-resourceTimeline-resizeHandleEnd") ? "resize-end" : "move";
7583
7736
  event.preventDefault();
7584
7737
  activeDragRef.current = {
7585
7738
  item: layoutItem.item,
7586
7739
  itemId: layoutItem.itemId,
7740
+ mode,
7587
7741
  fromResourceId: layoutItem.resourceId,
7588
7742
  startX: event.clientX,
7589
7743
  startY: event.clientY,
7590
7744
  initialLeft: layoutItem.left,
7591
7745
  initialTop: layoutItem.top,
7746
+ initialWidth: layoutItem.width,
7592
7747
  currentLeft: layoutItem.left,
7593
7748
  currentTop: layoutItem.top,
7749
+ currentWidth: layoutItem.width,
7594
7750
  dayWidth,
7595
7751
  startDate: layoutItem.startDate,
7596
- endDate: layoutItem.endDate
7752
+ endDate: layoutItem.endDate,
7753
+ monthStart,
7754
+ businessDays,
7755
+ weekendPredicate
7597
7756
  };
7598
7757
  setPreview({
7599
7758
  itemId: layoutItem.itemId,
7600
7759
  left: layoutItem.left,
7601
- top: layoutItem.top
7760
+ top: layoutItem.top,
7761
+ width: layoutItem.width,
7762
+ startDate: layoutItem.startDate,
7763
+ endDate: layoutItem.endDate
7602
7764
  });
7603
- }, [dayWidth, readonly]);
7765
+ }, [businessDays, dayWidth, monthStart, readonly, weekendPredicate]);
7604
7766
  return {
7605
7767
  preview,
7606
7768
  startDrag,
@@ -7614,6 +7776,7 @@ var DEFAULT_DAY_WIDTH = 40;
7614
7776
  var DEFAULT_HEADER_HEIGHT = 40;
7615
7777
  var DEFAULT_LANE_HEIGHT = 40;
7616
7778
  var DEFAULT_ROW_HEADER_WIDTH = 240;
7779
+ var DEFAULT_RESOURCE_ROW_GAP = 8;
7617
7780
  var ITEM_OUTER_VERTICAL_INSET = 2;
7618
7781
  var ITEM_INNER_VERTICAL_INSET = 1;
7619
7782
  var ITEM_HORIZONTAL_INSET = 1;
@@ -7673,12 +7836,58 @@ var getVisualItemGeometry = (geometry, laneIndex, laneCount) => {
7673
7836
  height: Math.max(0, geometry.height - topInset - bottomInset)
7674
7837
  };
7675
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);
7676
7882
  function ResourceTimelineChart({
7677
7883
  resources,
7678
7884
  dayWidth = DEFAULT_DAY_WIDTH,
7679
7885
  rowHeaderWidth = DEFAULT_ROW_HEADER_WIDTH,
7680
7886
  laneHeight = DEFAULT_LANE_HEIGHT,
7681
7887
  headerHeight = DEFAULT_HEADER_HEIGHT,
7888
+ customDays,
7889
+ isWeekend: isWeekend3,
7890
+ businessDays = true,
7682
7891
  readonly,
7683
7892
  disableResourceReassignment,
7684
7893
  renderItem,
@@ -7696,9 +7905,18 @@ function ResourceTimelineChart({
7696
7905
  const firstDay = dateRange[0] ?? /* @__PURE__ */ new Date();
7697
7906
  return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));
7698
7907
  }, [dateRange]);
7908
+ const weekendPredicate = (0, import_react14.useMemo)(
7909
+ () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
7910
+ [customDays, isWeekend3]
7911
+ );
7699
7912
  const gridWidth = (0, import_react14.useMemo)(() => Math.round(dateRange.length * dayWidth), [dateRange.length, dayWidth]);
7700
7913
  const layout = (0, import_react14.useMemo)(
7701
- () => layoutResourceTimelineItems(resources, { monthStart, dayWidth, laneHeight }),
7914
+ () => layoutResourceTimelineItems(resources, {
7915
+ monthStart,
7916
+ dayWidth,
7917
+ laneHeight,
7918
+ rowGap: DEFAULT_RESOURCE_ROW_GAP
7919
+ }),
7702
7920
  [resources, monthStart, dayWidth, laneHeight]
7703
7921
  );
7704
7922
  const todayInRange = (0, import_react14.useMemo)(() => {
@@ -7717,10 +7935,13 @@ function ResourceTimelineChart({
7717
7935
  }, [layout.items]);
7718
7936
  const { preview, startDrag } = useResourceItemDrag({
7719
7937
  dayWidth,
7938
+ monthStart,
7720
7939
  rows: layout.rows,
7721
7940
  gridElementRef: gridRef,
7722
7941
  readonly,
7723
7942
  disableResourceReassignment,
7943
+ businessDays,
7944
+ weekendPredicate,
7724
7945
  onResourceItemMove
7725
7946
  });
7726
7947
  const handlePanStart = (0, import_react14.useCallback)((event) => {
@@ -7799,13 +8020,26 @@ function ResourceTimelineChart({
7799
8020
  style: { height: `${headerHeight}px` }
7800
8021
  }
7801
8022
  ),
7802
- layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8023
+ layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7803
8024
  "div",
7804
8025
  {
7805
8026
  className: "gantt-resourceTimeline-resourceHeader",
7806
8027
  "data-resource-row-id": row.resourceId,
7807
- style: { height: `${row.resourceRowHeight}px` },
7808
- 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
+ ]
7809
8043
  },
7810
8044
  row.resourceId
7811
8045
  ))
@@ -7818,7 +8052,15 @@ function ResourceTimelineChart({
7818
8052
  className: "gantt-resourceTimeline-chartSurface",
7819
8053
  style: { minWidth: `${gridWidth}px` },
7820
8054
  children: [
7821
- /* @__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
+ ) }),
7822
8064
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7823
8065
  "div",
7824
8066
  {
@@ -7826,7 +8068,15 @@ function ResourceTimelineChart({
7826
8068
  className: "gantt-resourceTimeline-grid",
7827
8069
  style: { width: `${gridWidth}px`, height: `${layout.totalHeight}px` },
7828
8070
  children: [
7829
- /* @__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
+ ),
7830
8080
  todayInRange && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
7831
8081
  layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7832
8082
  "div",
@@ -7835,7 +8085,7 @@ function ResourceTimelineChart({
7835
8085
  "data-resource-row-id": row.resourceId,
7836
8086
  style: {
7837
8087
  top: `${row.resourceRowTop}px`,
7838
- height: `${row.resourceRowHeight}px`
8088
+ height: `${row.resourceRowHeight + DEFAULT_RESOURCE_ROW_GAP}px`
7839
8089
  }
7840
8090
  },
7841
8091
  row.resourceId
@@ -7843,17 +8093,18 @@ function ResourceTimelineChart({
7843
8093
  Array.from(itemsByResourceId.values()).flatMap(
7844
8094
  (resourceItems) => resourceItems.map((layoutItem) => {
7845
8095
  const customClassName = getItemClassName?.(layoutItem.item);
8096
+ const isDraggingItem = preview?.itemId === layoutItem.itemId;
7846
8097
  const className = [
7847
8098
  "gantt-resourceTimeline-item",
7848
- preview?.itemId === layoutItem.itemId && "gantt-resourceTimeline-itemDragging",
8099
+ isDraggingItem && "gantt-resourceTimeline-itemDragging",
7849
8100
  (readonly || layoutItem.item.locked) && "gantt-resourceTimeline-itemDisabled",
7850
8101
  customClassName
7851
8102
  ].filter(Boolean).join(" ");
7852
8103
  const laneCount = Math.max(1, Math.round(layoutItem.resourceRowHeight / layoutItem.height));
7853
- const previewStyle = preview?.itemId === layoutItem.itemId ? getVisualItemGeometry({
8104
+ const previewStyle = isDraggingItem ? getVisualItemGeometry({
7854
8105
  left: preview.left,
7855
8106
  top: preview.top,
7856
- width: layoutItem.width,
8107
+ width: preview.width,
7857
8108
  height: layoutItem.height
7858
8109
  }, layoutItem.laneIndex, laneCount) : void 0;
7859
8110
  const itemGeometry = getVisualItemGeometry({
@@ -7862,7 +8113,27 @@ function ResourceTimelineChart({
7862
8113
  width: layoutItem.width,
7863
8114
  height: layoutItem.height
7864
8115
  }, layoutItem.laneIndex, laneCount);
7865
- 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)(
7866
8137
  "div",
7867
8138
  {
7868
8139
  className,
@@ -7871,16 +8142,54 @@ function ResourceTimelineChart({
7871
8142
  style: {
7872
8143
  left: `${itemGeometry.left}px`,
7873
8144
  top: `${itemGeometry.top}px`,
7874
- ...previewStyle,
7875
8145
  width: `${itemGeometry.width}px`,
7876
8146
  height: `${itemGeometry.height}px`,
8147
+ ...previewStyle,
7877
8148
  backgroundColor: layoutItem.item.color ?? "var(--gantt-task-bar-default-color, #3b82f6)"
7878
8149
  },
7879
- 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: [
7880
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-itemTitle", children: layoutItem.item.title }),
7881
- layoutItem.item.subtitle && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-itemSubtitle", children: layoutItem.item.subtitle }),
7882
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gantt-resourceTimeline-itemDates", children: formatDateRangeLabel(layoutItem.startDate, layoutItem.endDate) })
7883
- ] }) })
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
+ ]
7884
8193
  },
7885
8194
  layoutItem.itemId
7886
8195
  );