gantt-lib 0.74.0 → 0.75.1

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
@@ -515,6 +515,7 @@ __export(index_exports, {
515
515
  Popover: () => Popover,
516
516
  PopoverContent: () => PopoverContent,
517
517
  PopoverTrigger: () => PopoverTrigger,
518
+ ResourceTimelineChart: () => ResourceTimelineChart,
518
519
  TaskList: () => TaskList,
519
520
  TaskRow: () => TaskRow_default,
520
521
  TimeScaleHeader: () => TimeScaleHeader_default,
@@ -578,6 +579,7 @@ __export(index_exports, {
578
579
  isTaskParent: () => isTaskParent,
579
580
  isToday: () => isToday,
580
581
  isWeekend: () => isWeekend,
582
+ layoutResourceTimelineItems: () => layoutResourceTimelineItems,
581
583
  moveTaskRange: () => moveTaskRange,
582
584
  moveTaskWithCascade: () => moveTaskWithCascade,
583
585
  nameContains: () => nameContains,
@@ -610,7 +612,7 @@ __export(index_exports, {
610
612
  module.exports = __toCommonJS(index_exports);
611
613
 
612
614
  // src/components/GanttChart/GanttChart.tsx
613
- var import_react13 = require("react");
615
+ var import_react15 = require("react");
614
616
  init_dateUtils();
615
617
 
616
618
  // src/core/scheduling/index.ts
@@ -756,13 +758,13 @@ function computeParentProgress(parentId, tasks) {
756
758
  if (children.length === 0) {
757
759
  return 0;
758
760
  }
759
- const DAY_MS3 = 24 * 60 * 60 * 1e3;
761
+ const DAY_MS4 = 24 * 60 * 60 * 1e3;
760
762
  let totalWeight = 0;
761
763
  let weightedSum = 0;
762
764
  for (const child of children) {
763
765
  const start = new Date(child.startDate).getTime();
764
766
  const end = new Date(child.endDate).getTime();
765
- const duration = (end - start + DAY_MS3) / DAY_MS3;
767
+ const duration = (end - start + DAY_MS4) / DAY_MS4;
766
768
  const progress = child.progress ?? 0;
767
769
  totalWeight += duration;
768
770
  weightedSum += duration * progress;
@@ -857,10 +859,10 @@ function buildTaskRangeFromStart(startDate, duration, businessDays = false, week
857
859
  end: parseDateOnly(addBusinessDays(normalizedStart, duration, weekendPredicate))
858
860
  };
859
861
  }
860
- const DAY_MS3 = 24 * 60 * 60 * 1e3;
862
+ const DAY_MS4 = 24 * 60 * 60 * 1e3;
861
863
  return {
862
864
  start: normalizedStart,
863
- 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)
864
866
  };
865
867
  }
866
868
  function buildTaskRangeFromEnd(endDate, duration, businessDays = false, weekendPredicate, snapDirection = -1) {
@@ -871,9 +873,9 @@ function buildTaskRangeFromEnd(endDate, duration, businessDays = false, weekendP
871
873
  end: normalizedEnd
872
874
  };
873
875
  }
874
- const DAY_MS3 = 24 * 60 * 60 * 1e3;
876
+ const DAY_MS4 = 24 * 60 * 60 * 1e3;
875
877
  return {
876
- 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),
877
879
  end: normalizedEnd
878
880
  };
879
881
  }
@@ -4183,21 +4185,56 @@ var LINK_TYPE_LABELS = {
4183
4185
  SF: "\u041D\u0430\u0447\u0430\u043B\u043E-\u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u0435"
4184
4186
  };
4185
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
+
4186
4223
  // src/components/TaskList/TaskListRow.tsx
4187
4224
  var import_jsx_runtime12 = require("react/jsx-runtime");
4188
- var DAY_MS2 = 24 * 60 * 60 * 1e3;
4225
+ var DAY_MS3 = 24 * 60 * 60 * 1e3;
4189
4226
  var LINK_TYPE_ORDER = ["FS", "SS", "FF", "SF"];
4190
4227
  var getInclusiveDurationDays = (startDate, endDate) => {
4191
4228
  const start = parseUTCDate(startDate);
4192
4229
  const end = parseUTCDate(endDate);
4193
4230
  return Math.max(
4194
4231
  1,
4195
- Math.round((end.getTime() - start.getTime()) / DAY_MS2) + 1
4232
+ Math.round((end.getTime() - start.getTime()) / DAY_MS3) + 1
4196
4233
  );
4197
4234
  };
4198
4235
  var getEndDateFromDuration = (startDate, durationDays) => {
4199
4236
  const start = parseUTCDate(startDate);
4200
- 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];
4201
4238
  };
4202
4239
  var TrashIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4203
4240
  "svg",
@@ -4682,7 +4719,7 @@ var DepChip = ({
4682
4719
  )
4683
4720
  ] });
4684
4721
  };
4685
- var toISODate = (value) => {
4722
+ var toISODate2 = (value) => {
4686
4723
  if (value instanceof Date) return value.toISOString().split("T")[0];
4687
4724
  if (typeof value === "string" && value.includes("T"))
4688
4725
  return value.split("T")[0];
@@ -4736,6 +4773,7 @@ var TaskListRow = import_react10.default.memo(
4736
4773
  customDays,
4737
4774
  isWeekend: isWeekend3,
4738
4775
  businessDays,
4776
+ defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
4739
4777
  isFilterMatch = false,
4740
4778
  isFilterHideMode = false,
4741
4779
  resolvedColumns,
@@ -5543,8 +5581,8 @@ var TaskListRow = import_react10.default.memo(
5543
5581
  },
5544
5582
  [selectedChip?.successorId, selectedChip?.predecessorId, selectedChip?.linkType, onRemoveDependency, onChipSelect]
5545
5583
  );
5546
- const startDateISO = toISODate(normalizedTask.startDate);
5547
- 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);
5548
5586
  const numberCell = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5549
5587
  "div",
5550
5588
  {
@@ -5762,26 +5800,16 @@ var TaskListRow = import_react10.default.memo(
5762
5800
  className: "gantt-tl-name-action-btn gantt-tl-action-insert",
5763
5801
  onClick: (e) => {
5764
5802
  e.stopPropagation();
5765
- const now = /* @__PURE__ */ new Date();
5766
- const todayISO = new Date(
5767
- Date.UTC(
5768
- now.getUTCFullYear(),
5769
- now.getUTCMonth(),
5770
- now.getUTCDate()
5771
- )
5772
- ).toISOString().split("T")[0];
5773
- const endISO = new Date(
5774
- Date.UTC(
5775
- now.getUTCFullYear(),
5776
- now.getUTCMonth(),
5777
- now.getUTCDate() + 7
5778
- )
5779
- ).toISOString().split("T")[0];
5803
+ const range = buildDefaultTaskDateRange(getTodayISODate(), {
5804
+ businessDays,
5805
+ defaultTaskDurationDays,
5806
+ weekendPredicate
5807
+ });
5780
5808
  const newTask = {
5781
5809
  id: crypto.randomUUID(),
5782
5810
  name: "\u041D\u043E\u0432\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430",
5783
- startDate: todayISO,
5784
- endDate: endISO,
5811
+ startDate: range.startDate,
5812
+ endDate: range.endDate,
5785
5813
  parentId: task.parentId
5786
5814
  };
5787
5815
  onInsertAfter(task.id, newTask);
@@ -6610,6 +6638,7 @@ var TaskList = ({
6610
6638
  onReorder,
6611
6639
  editingTaskId: propEditingTaskId,
6612
6640
  enableAddTask = true,
6641
+ defaultTaskDurationDays = DEFAULT_TASK_DURATION_DAYS,
6613
6642
  collapsedParentIds: externalCollapsedParentIds,
6614
6643
  onToggleCollapse: externalOnToggleCollapse,
6615
6644
  onPromoteTask,
@@ -7012,26 +7041,20 @@ var TaskList = ({
7012
7041
  dragTaskIdRef.current = null;
7013
7042
  }, []);
7014
7043
  const handleConfirmNewTask = (0, import_react12.useCallback)((name) => {
7015
- const now = /* @__PURE__ */ new Date();
7016
- const todayISO = new Date(Date.UTC(
7017
- now.getUTCFullYear(),
7018
- now.getUTCMonth(),
7019
- now.getUTCDate()
7020
- )).toISOString().split("T")[0];
7021
- const endISO = new Date(Date.UTC(
7022
- now.getUTCFullYear(),
7023
- now.getUTCMonth(),
7024
- now.getUTCDate() + 7
7025
- )).toISOString().split("T")[0];
7044
+ const range = buildDefaultTaskDateRange(getTodayISODate(), {
7045
+ businessDays,
7046
+ defaultTaskDurationDays,
7047
+ weekendPredicate
7048
+ });
7026
7049
  const newTask = {
7027
7050
  id: crypto.randomUUID(),
7028
7051
  name,
7029
- startDate: todayISO,
7030
- endDate: endISO
7052
+ startDate: range.startDate,
7053
+ endDate: range.endDate
7031
7054
  };
7032
7055
  onAdd?.(newTask);
7033
7056
  setIsCreating(false);
7034
- }, [onAdd]);
7057
+ }, [businessDays, defaultTaskDurationDays, onAdd, weekendPredicate]);
7035
7058
  const handleCancelNewTask = (0, import_react12.useCallback)(() => setIsCreating(false), []);
7036
7059
  const findInsertAfterTaskId = (0, import_react12.useCallback)((anchorTaskId) => {
7037
7060
  const anchorIndex = orderedTasks.findIndex((task) => task.id === anchorTaskId);
@@ -7297,6 +7320,7 @@ var TaskList = ({
7297
7320
  customDays,
7298
7321
  isWeekend: isWeekend3,
7299
7322
  businessDays,
7323
+ defaultTaskDurationDays,
7300
7324
  isFilterMatch: filterMode === "highlight" ? highlightedTaskIds.has(task.id) : false,
7301
7325
  isFilterHideMode: filterMode === "hide" && isFilterActive,
7302
7326
  resolvedColumns,
@@ -7357,6 +7381,522 @@ var TaskList = ({
7357
7381
  );
7358
7382
  };
7359
7383
 
7384
+ // src/components/ResourceTimelineChart/ResourceTimelineChart.tsx
7385
+ var import_react14 = require("react");
7386
+ init_dateUtils();
7387
+
7388
+ // src/utils/resourceTimelineLayout.ts
7389
+ init_dateUtils();
7390
+ var isInvalidDate = (date) => Number.isNaN(date.getTime());
7391
+ var getUTCDayNumber = (date) => {
7392
+ return Math.floor(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()) / 864e5);
7393
+ };
7394
+ var compareParsedItems = (a, b) => {
7395
+ const startDiff = getUTCDayNumber(a.startDate) - getUTCDayNumber(b.startDate);
7396
+ if (startDiff !== 0) {
7397
+ return startDiff;
7398
+ }
7399
+ const endDiff = getUTCDayNumber(a.endDate) - getUTCDayNumber(b.endDate);
7400
+ if (endDiff !== 0) {
7401
+ return endDiff;
7402
+ }
7403
+ return a.item.id.localeCompare(b.item.id);
7404
+ };
7405
+ var layoutResourceTimelineItems = (resources, options) => {
7406
+ const rows = [];
7407
+ const items = [];
7408
+ const diagnostics = [];
7409
+ let currentTop = 0;
7410
+ for (const resource of resources) {
7411
+ const parsedItems = [];
7412
+ for (const item of resource.items) {
7413
+ try {
7414
+ const startDate = parseUTCDate(item.startDate);
7415
+ const endDate = parseUTCDate(item.endDate);
7416
+ if (isInvalidDate(startDate) || isInvalidDate(endDate)) {
7417
+ throw new Error("Invalid date");
7418
+ }
7419
+ parsedItems.push({ item, startDate, endDate });
7420
+ } catch {
7421
+ diagnostics.push({
7422
+ itemId: item.id,
7423
+ resourceId: resource.id,
7424
+ reason: "invalid-date"
7425
+ });
7426
+ }
7427
+ }
7428
+ parsedItems.sort(compareParsedItems);
7429
+ const laneEndDays = [];
7430
+ const laidOutItems = [];
7431
+ for (const parsed of parsedItems) {
7432
+ const startDay = getUTCDayNumber(parsed.startDate);
7433
+ const endDay = getUTCDayNumber(parsed.endDate);
7434
+ let laneIndex = laneEndDays.findIndex((laneEndDay) => laneEndDay < startDay);
7435
+ if (laneIndex === -1) {
7436
+ laneIndex = laneEndDays.length;
7437
+ laneEndDays.push(endDay);
7438
+ } else {
7439
+ laneEndDays[laneIndex] = endDay;
7440
+ }
7441
+ const { left, width } = calculateTaskBar(parsed.startDate, parsed.endDate, options.monthStart, options.dayWidth);
7442
+ laidOutItems.push({
7443
+ item: parsed.item,
7444
+ itemId: parsed.item.id,
7445
+ resourceId: resource.id,
7446
+ laneIndex,
7447
+ left,
7448
+ width,
7449
+ resourceRowTop: currentTop,
7450
+ resourceRowHeight: 0,
7451
+ top: currentTop + laneIndex * options.laneHeight,
7452
+ height: options.laneHeight,
7453
+ startDate: parsed.startDate,
7454
+ endDate: parsed.endDate
7455
+ });
7456
+ }
7457
+ const laneCount = Math.max(1, laneEndDays.length);
7458
+ const resourceRowHeight = laneCount * options.laneHeight;
7459
+ const row = {
7460
+ resource,
7461
+ resourceId: resource.id,
7462
+ laneCount,
7463
+ resourceRowTop: currentTop,
7464
+ resourceRowHeight
7465
+ };
7466
+ rows.push(row);
7467
+ items.push(...laidOutItems.map((item) => ({
7468
+ ...item,
7469
+ resourceRowHeight
7470
+ })));
7471
+ currentTop += resourceRowHeight;
7472
+ }
7473
+ return {
7474
+ rows,
7475
+ items,
7476
+ diagnostics,
7477
+ totalHeight: currentTop
7478
+ };
7479
+ };
7480
+
7481
+ // src/hooks/useResourceItemDrag.ts
7482
+ var import_react13 = require("react");
7483
+ var snapToDay = (pixels, dayWidth) => {
7484
+ return Math.round(pixels / dayWidth) * dayWidth;
7485
+ };
7486
+ var addUTCDays = (date, days) => {
7487
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days));
7488
+ };
7489
+ var resolveTargetResource = (rows, clientY, gridTop) => {
7490
+ const localY = clientY - gridTop;
7491
+ return rows.find(
7492
+ (row) => localY >= row.resourceRowTop && localY < row.resourceRowTop + row.resourceRowHeight
7493
+ )?.resource ?? null;
7494
+ };
7495
+ var useResourceItemDrag = ({
7496
+ dayWidth,
7497
+ rows,
7498
+ gridElementRef,
7499
+ readonly,
7500
+ disableResourceReassignment,
7501
+ onResourceItemMove
7502
+ }) => {
7503
+ const activeDragRef = (0, import_react13.useRef)(null);
7504
+ const rowsRef = (0, import_react13.useRef)(rows);
7505
+ const onResourceItemMoveRef = (0, import_react13.useRef)(onResourceItemMove);
7506
+ const rafRef = (0, import_react13.useRef)(null);
7507
+ const [preview, setPreview] = (0, import_react13.useState)(null);
7508
+ (0, import_react13.useEffect)(() => {
7509
+ rowsRef.current = rows;
7510
+ }, [rows]);
7511
+ (0, import_react13.useEffect)(() => {
7512
+ onResourceItemMoveRef.current = onResourceItemMove;
7513
+ }, [onResourceItemMove]);
7514
+ const clearRaf = (0, import_react13.useCallback)(() => {
7515
+ if (rafRef.current !== null) {
7516
+ cancelAnimationFrame(rafRef.current);
7517
+ rafRef.current = null;
7518
+ }
7519
+ }, []);
7520
+ const cancelDrag2 = (0, import_react13.useCallback)(() => {
7521
+ clearRaf();
7522
+ activeDragRef.current = null;
7523
+ setPreview(null);
7524
+ }, [clearRaf]);
7525
+ (0, import_react13.useEffect)(() => {
7526
+ const handleMouseMove = (event) => {
7527
+ const activeDrag = activeDragRef.current;
7528
+ if (!activeDrag || rafRef.current !== null) {
7529
+ return;
7530
+ }
7531
+ rafRef.current = requestAnimationFrame(() => {
7532
+ rafRef.current = null;
7533
+ const latestDrag = activeDragRef.current;
7534
+ if (!latestDrag) {
7535
+ return;
7536
+ }
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;
7540
+ latestDrag.currentTop = nextTop;
7541
+ setPreview({
7542
+ itemId: latestDrag.itemId,
7543
+ left: nextLeft,
7544
+ top: nextTop
7545
+ });
7546
+ });
7547
+ };
7548
+ const handleMouseUp = (event) => {
7549
+ const activeDrag = activeDragRef.current;
7550
+ if (!activeDrag) {
7551
+ return;
7552
+ }
7553
+ clearRaf();
7554
+ activeDragRef.current = null;
7555
+ setPreview(null);
7556
+ 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);
7558
+ if (!targetResource) {
7559
+ return;
7560
+ }
7561
+ const dayDelta = Math.round((activeDrag.currentLeft - activeDrag.initialLeft) / activeDrag.dayWidth);
7562
+ onResourceItemMoveRef.current?.({
7563
+ item: activeDrag.item,
7564
+ itemId: activeDrag.itemId,
7565
+ fromResourceId: activeDrag.fromResourceId,
7566
+ toResourceId: targetResource.id,
7567
+ startDate: addUTCDays(activeDrag.startDate, dayDelta),
7568
+ endDate: addUTCDays(activeDrag.endDate, dayDelta)
7569
+ });
7570
+ };
7571
+ window.addEventListener("mousemove", handleMouseMove);
7572
+ window.addEventListener("mouseup", handleMouseUp);
7573
+ return () => {
7574
+ window.removeEventListener("mousemove", handleMouseMove);
7575
+ window.removeEventListener("mouseup", handleMouseUp);
7576
+ cancelDrag2();
7577
+ };
7578
+ }, [cancelDrag2, clearRaf, disableResourceReassignment, gridElementRef]);
7579
+ const startDrag = (0, import_react13.useCallback)((event, layoutItem) => {
7580
+ if (readonly || layoutItem.item.locked || event.button !== 0) {
7581
+ return;
7582
+ }
7583
+ event.preventDefault();
7584
+ activeDragRef.current = {
7585
+ item: layoutItem.item,
7586
+ itemId: layoutItem.itemId,
7587
+ fromResourceId: layoutItem.resourceId,
7588
+ startX: event.clientX,
7589
+ startY: event.clientY,
7590
+ initialLeft: layoutItem.left,
7591
+ initialTop: layoutItem.top,
7592
+ currentLeft: layoutItem.left,
7593
+ currentTop: layoutItem.top,
7594
+ dayWidth,
7595
+ startDate: layoutItem.startDate,
7596
+ endDate: layoutItem.endDate
7597
+ };
7598
+ setPreview({
7599
+ itemId: layoutItem.itemId,
7600
+ left: layoutItem.left,
7601
+ top: layoutItem.top
7602
+ });
7603
+ }, [dayWidth, readonly]);
7604
+ return {
7605
+ preview,
7606
+ startDrag,
7607
+ cancelDrag: cancelDrag2
7608
+ };
7609
+ };
7610
+
7611
+ // src/components/ResourceTimelineChart/ResourceTimelineChart.tsx
7612
+ var import_jsx_runtime15 = require("react/jsx-runtime");
7613
+ var DEFAULT_DAY_WIDTH = 40;
7614
+ var DEFAULT_HEADER_HEIGHT = 40;
7615
+ var DEFAULT_LANE_HEIGHT = 40;
7616
+ var DEFAULT_ROW_HEADER_WIDTH = 240;
7617
+ var ITEM_OUTER_VERTICAL_INSET = 2;
7618
+ var ITEM_INNER_VERTICAL_INSET = 1;
7619
+ var ITEM_HORIZONTAL_INSET = 1;
7620
+ var isValidDate = (date) => !Number.isNaN(date.getTime());
7621
+ var getResourceTimelineDays = (items) => {
7622
+ if (items.length === 0) {
7623
+ return getMonthDays(/* @__PURE__ */ new Date());
7624
+ }
7625
+ let minDate = null;
7626
+ let maxDate = null;
7627
+ for (const item of items) {
7628
+ const startDate = parseUTCDate(item.startDate);
7629
+ const endDate = parseUTCDate(item.endDate);
7630
+ if (!minDate || startDate.getTime() < minDate.getTime()) {
7631
+ minDate = startDate;
7632
+ }
7633
+ if (!maxDate || endDate.getTime() > maxDate.getTime()) {
7634
+ maxDate = endDate;
7635
+ }
7636
+ }
7637
+ if (!minDate || !maxDate) {
7638
+ return getMonthDays(/* @__PURE__ */ new Date());
7639
+ }
7640
+ const startOfMonth2 = new Date(Date.UTC(minDate.getUTCFullYear(), minDate.getUTCMonth(), 1));
7641
+ const endOfMonth = new Date(Date.UTC(maxDate.getUTCFullYear(), maxDate.getUTCMonth() + 1, 0));
7642
+ const days = [];
7643
+ const current = new Date(startOfMonth2);
7644
+ while (current.getTime() <= endOfMonth.getTime()) {
7645
+ days.push(new Date(Date.UTC(current.getUTCFullYear(), current.getUTCMonth(), current.getUTCDate())));
7646
+ current.setUTCDate(current.getUTCDate() + 1);
7647
+ }
7648
+ return days;
7649
+ };
7650
+ var collectValidItems = (resources) => {
7651
+ return resources.flatMap(
7652
+ (resource) => resource.items.flatMap((item) => {
7653
+ try {
7654
+ const startDate = parseUTCDate(item.startDate);
7655
+ const endDate = parseUTCDate(item.endDate);
7656
+ if (!isValidDate(startDate) || !isValidDate(endDate)) {
7657
+ return [];
7658
+ }
7659
+ return [{ startDate, endDate }];
7660
+ } catch {
7661
+ return [];
7662
+ }
7663
+ })
7664
+ );
7665
+ };
7666
+ var getVisualItemGeometry = (geometry, laneIndex, laneCount) => {
7667
+ const topInset = laneIndex === 0 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7668
+ const bottomInset = laneIndex === laneCount - 1 ? ITEM_OUTER_VERTICAL_INSET : ITEM_INNER_VERTICAL_INSET;
7669
+ return {
7670
+ left: geometry.left + ITEM_HORIZONTAL_INSET,
7671
+ top: geometry.top + topInset,
7672
+ width: Math.max(0, geometry.width - ITEM_HORIZONTAL_INSET * 2),
7673
+ height: Math.max(0, geometry.height - topInset - bottomInset)
7674
+ };
7675
+ };
7676
+ function ResourceTimelineChart({
7677
+ resources,
7678
+ dayWidth = DEFAULT_DAY_WIDTH,
7679
+ rowHeaderWidth = DEFAULT_ROW_HEADER_WIDTH,
7680
+ laneHeight = DEFAULT_LANE_HEIGHT,
7681
+ headerHeight = DEFAULT_HEADER_HEIGHT,
7682
+ readonly,
7683
+ disableResourceReassignment,
7684
+ renderItem,
7685
+ getItemClassName,
7686
+ onResourceItemMove
7687
+ }) {
7688
+ const scrollContainerRef = (0, import_react14.useRef)(null);
7689
+ const gridRef = (0, import_react14.useRef)(null);
7690
+ const panStateRef = (0, import_react14.useRef)(null);
7691
+ const validItems = (0, import_react14.useMemo)(() => collectValidItems(resources), [resources]);
7692
+ const dateRange = (0, import_react14.useMemo)(() => {
7693
+ return getResourceTimelineDays(validItems);
7694
+ }, [validItems]);
7695
+ const monthStart = (0, import_react14.useMemo)(() => {
7696
+ const firstDay = dateRange[0] ?? /* @__PURE__ */ new Date();
7697
+ return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));
7698
+ }, [dateRange]);
7699
+ const gridWidth = (0, import_react14.useMemo)(() => Math.round(dateRange.length * dayWidth), [dateRange.length, dayWidth]);
7700
+ const layout = (0, import_react14.useMemo)(
7701
+ () => layoutResourceTimelineItems(resources, { monthStart, dayWidth, laneHeight }),
7702
+ [resources, monthStart, dayWidth, laneHeight]
7703
+ );
7704
+ const todayInRange = (0, import_react14.useMemo)(() => {
7705
+ const now = /* @__PURE__ */ new Date();
7706
+ const today = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
7707
+ return dateRange.some((day) => day.getTime() === today.getTime());
7708
+ }, [dateRange]);
7709
+ const itemsByResourceId = (0, import_react14.useMemo)(() => {
7710
+ const map = /* @__PURE__ */ new Map();
7711
+ for (const item of layout.items) {
7712
+ const next = map.get(item.resourceId) ?? [];
7713
+ next.push(item);
7714
+ map.set(item.resourceId, next);
7715
+ }
7716
+ return map;
7717
+ }, [layout.items]);
7718
+ const { preview, startDrag } = useResourceItemDrag({
7719
+ dayWidth,
7720
+ rows: layout.rows,
7721
+ gridElementRef: gridRef,
7722
+ readonly,
7723
+ disableResourceReassignment,
7724
+ onResourceItemMove
7725
+ });
7726
+ const handlePanStart = (0, import_react14.useCallback)((event) => {
7727
+ if (event.button !== 0) {
7728
+ return;
7729
+ }
7730
+ const target = event.target;
7731
+ if (target.closest("[data-resource-item-id]")) {
7732
+ return;
7733
+ }
7734
+ if (target.closest("input, button, textarea, [contenteditable]")) {
7735
+ return;
7736
+ }
7737
+ const container = scrollContainerRef.current;
7738
+ if (!container) {
7739
+ return;
7740
+ }
7741
+ panStateRef.current = {
7742
+ active: true,
7743
+ startX: event.clientX,
7744
+ startY: event.clientY,
7745
+ scrollX: container.scrollLeft,
7746
+ scrollY: container.scrollTop
7747
+ };
7748
+ if (document.activeElement instanceof HTMLElement) {
7749
+ document.activeElement.blur();
7750
+ }
7751
+ container.style.cursor = "grabbing";
7752
+ event.preventDefault();
7753
+ }, []);
7754
+ (0, import_react14.useEffect)(() => {
7755
+ const handlePanMove = (event) => {
7756
+ const pan = panStateRef.current;
7757
+ const container = scrollContainerRef.current;
7758
+ if (!pan?.active || !container) {
7759
+ return;
7760
+ }
7761
+ container.scrollLeft = pan.scrollX - (event.clientX - pan.startX);
7762
+ container.scrollTop = pan.scrollY - (event.clientY - pan.startY);
7763
+ };
7764
+ const handlePanEnd = () => {
7765
+ if (!panStateRef.current?.active) {
7766
+ return;
7767
+ }
7768
+ panStateRef.current = null;
7769
+ const container = scrollContainerRef.current;
7770
+ if (container) {
7771
+ container.style.cursor = "";
7772
+ }
7773
+ };
7774
+ window.addEventListener("mousemove", handlePanMove);
7775
+ window.addEventListener("mouseup", handlePanEnd);
7776
+ return () => {
7777
+ window.removeEventListener("mousemove", handlePanMove);
7778
+ window.removeEventListener("mouseup", handlePanEnd);
7779
+ };
7780
+ }, []);
7781
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-container gantt-resourceTimeline", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7782
+ "div",
7783
+ {
7784
+ ref: scrollContainerRef,
7785
+ className: "gantt-resourceTimeline-scrollContainer",
7786
+ style: { cursor: "grab" },
7787
+ onMouseDown: handlePanStart,
7788
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "gantt-resourceTimeline-scrollContent", children: [
7789
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7790
+ "div",
7791
+ {
7792
+ className: "gantt-resourceTimeline-resourceColumn",
7793
+ style: { width: `${rowHeaderWidth}px`, minWidth: `${rowHeaderWidth}px` },
7794
+ children: [
7795
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7796
+ "div",
7797
+ {
7798
+ className: "gantt-resourceTimeline-corner",
7799
+ style: { height: `${headerHeight}px` }
7800
+ }
7801
+ ),
7802
+ layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7803
+ "div",
7804
+ {
7805
+ className: "gantt-resourceTimeline-resourceHeader",
7806
+ "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 })
7809
+ },
7810
+ row.resourceId
7811
+ ))
7812
+ ]
7813
+ }
7814
+ ),
7815
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7816
+ "div",
7817
+ {
7818
+ className: "gantt-resourceTimeline-chartSurface",
7819
+ style: { minWidth: `${gridWidth}px` },
7820
+ 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 }) }),
7822
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7823
+ "div",
7824
+ {
7825
+ ref: gridRef,
7826
+ className: "gantt-resourceTimeline-grid",
7827
+ style: { width: `${gridWidth}px`, height: `${layout.totalHeight}px` },
7828
+ children: [
7829
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(GridBackground_default, { dateRange, dayWidth, totalHeight: layout.totalHeight }),
7830
+ todayInRange && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
7831
+ layout.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7832
+ "div",
7833
+ {
7834
+ className: "gantt-resourceTimeline-row",
7835
+ "data-resource-row-id": row.resourceId,
7836
+ style: {
7837
+ top: `${row.resourceRowTop}px`,
7838
+ height: `${row.resourceRowHeight}px`
7839
+ }
7840
+ },
7841
+ row.resourceId
7842
+ )),
7843
+ Array.from(itemsByResourceId.values()).flatMap(
7844
+ (resourceItems) => resourceItems.map((layoutItem) => {
7845
+ const customClassName = getItemClassName?.(layoutItem.item);
7846
+ const className = [
7847
+ "gantt-resourceTimeline-item",
7848
+ preview?.itemId === layoutItem.itemId && "gantt-resourceTimeline-itemDragging",
7849
+ (readonly || layoutItem.item.locked) && "gantt-resourceTimeline-itemDisabled",
7850
+ customClassName
7851
+ ].filter(Boolean).join(" ");
7852
+ const laneCount = Math.max(1, Math.round(layoutItem.resourceRowHeight / layoutItem.height));
7853
+ const previewStyle = preview?.itemId === layoutItem.itemId ? getVisualItemGeometry({
7854
+ left: preview.left,
7855
+ top: preview.top,
7856
+ width: layoutItem.width,
7857
+ height: layoutItem.height
7858
+ }, layoutItem.laneIndex, laneCount) : void 0;
7859
+ const itemGeometry = getVisualItemGeometry({
7860
+ left: layoutItem.left,
7861
+ top: layoutItem.top,
7862
+ width: layoutItem.width,
7863
+ height: layoutItem.height
7864
+ }, layoutItem.laneIndex, laneCount);
7865
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7866
+ "div",
7867
+ {
7868
+ className,
7869
+ "data-resource-item-id": layoutItem.itemId,
7870
+ onMouseDown: (event) => startDrag(event, layoutItem),
7871
+ style: {
7872
+ left: `${itemGeometry.left}px`,
7873
+ top: `${itemGeometry.top}px`,
7874
+ ...previewStyle,
7875
+ width: `${itemGeometry.width}px`,
7876
+ height: `${itemGeometry.height}px`,
7877
+ backgroundColor: layoutItem.item.color ?? "var(--gantt-task-bar-default-color, #3b82f6)"
7878
+ },
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
+ ] }) })
7884
+ },
7885
+ layoutItem.itemId
7886
+ );
7887
+ })
7888
+ )
7889
+ ]
7890
+ }
7891
+ )
7892
+ ]
7893
+ }
7894
+ )
7895
+ ] })
7896
+ }
7897
+ ) });
7898
+ }
7899
+
7360
7900
  // src/components/GanttChart/print.ts
7361
7901
  function getPrintDocumentTitle({
7362
7902
  header,
@@ -7695,9 +8235,21 @@ async function printGanttChart({
7695
8235
  }
7696
8236
 
7697
8237
  // src/components/GanttChart/GanttChart.tsx
7698
- var import_jsx_runtime15 = require("react/jsx-runtime");
8238
+ var import_jsx_runtime16 = require("react/jsx-runtime");
7699
8239
  var SCROLL_TO_ROW_CONTEXT_ROWS = 2;
7700
8240
  function GanttChartInner(props, ref) {
8241
+ if (props.mode === "resource-planner") {
8242
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ResourceTimelineChart, { ...props });
8243
+ }
8244
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8245
+ TaskGanttChart,
8246
+ {
8247
+ ...props,
8248
+ ref
8249
+ }
8250
+ );
8251
+ }
8252
+ function TaskGanttChartInner(props, ref) {
7701
8253
  const {
7702
8254
  tasks,
7703
8255
  dayWidth = 40,
@@ -7723,6 +8275,7 @@ function GanttChartInner(props, ref) {
7723
8275
  onDemoteTask,
7724
8276
  onUngroupTask,
7725
8277
  enableAddTask = true,
8278
+ defaultTaskDurationDays,
7726
8279
  viewMode = "day",
7727
8280
  customDays,
7728
8281
  isWeekend: isWeekend3,
@@ -7737,28 +8290,28 @@ function GanttChartInner(props, ref) {
7737
8290
  additionalColumns,
7738
8291
  taskListMenuCommands
7739
8292
  } = props;
7740
- const containerRef = (0, import_react13.useRef)(null);
7741
- const scrollContainerRef = (0, import_react13.useRef)(null);
7742
- const scrollContentRef = (0, import_react13.useRef)(null);
7743
- const [selectedTaskId, setSelectedTaskId] = (0, import_react13.useState)(null);
7744
- const [taskListHasRightShadow, setTaskListHasRightShadow] = (0, import_react13.useState)(false);
7745
- const [selectedChip, setSelectedChip] = (0, import_react13.useState)(null);
7746
- const [internalCollapsedParentIds, setInternalCollapsedParentIds] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
8293
+ const containerRef = (0, import_react15.useRef)(null);
8294
+ const scrollContainerRef = (0, import_react15.useRef)(null);
8295
+ const scrollContentRef = (0, import_react15.useRef)(null);
8296
+ const [selectedTaskId, setSelectedTaskId] = (0, import_react15.useState)(null);
8297
+ const [taskListHasRightShadow, setTaskListHasRightShadow] = (0, import_react15.useState)(false);
8298
+ const [selectedChip, setSelectedChip] = (0, import_react15.useState)(null);
8299
+ const [internalCollapsedParentIds, setInternalCollapsedParentIds] = (0, import_react15.useState)(/* @__PURE__ */ new Set());
7747
8300
  const collapsedParentIds = externalCollapsedParentIds ?? internalCollapsedParentIds;
7748
- const [editingTaskId, setEditingTaskId] = (0, import_react13.useState)(null);
7749
- const normalizedTasks = (0, import_react13.useMemo)(() => normalizeHierarchyTasks(tasks), [tasks]);
7750
- const isCustomWeekend = (0, import_react13.useMemo)(
8301
+ const [editingTaskId, setEditingTaskId] = (0, import_react15.useState)(null);
8302
+ const normalizedTasks = (0, import_react15.useMemo)(() => normalizeHierarchyTasks(tasks), [tasks]);
8303
+ const isCustomWeekend = (0, import_react15.useMemo)(
7751
8304
  () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
7752
8305
  [customDays, isWeekend3]
7753
8306
  );
7754
- const dateRange = (0, import_react13.useMemo)(() => getMultiMonthDays(normalizedTasks), [normalizedTasks]);
7755
- const [validationResult, setValidationResult] = (0, import_react13.useState)(null);
7756
- const [cascadeOverrides, setCascadeOverrides] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
7757
- const gridWidth = (0, import_react13.useMemo)(
8307
+ const dateRange = (0, import_react15.useMemo)(() => getMultiMonthDays(normalizedTasks), [normalizedTasks]);
8308
+ const [validationResult, setValidationResult] = (0, import_react15.useState)(null);
8309
+ const [cascadeOverrides, setCascadeOverrides] = (0, import_react15.useState)(/* @__PURE__ */ new Map());
8310
+ const gridWidth = (0, import_react15.useMemo)(
7758
8311
  () => Math.round(dateRange.length * dayWidth),
7759
8312
  [dateRange.length, dayWidth]
7760
8313
  );
7761
- const visibleTasks = (0, import_react13.useMemo)(() => {
8314
+ const visibleTasks = (0, import_react15.useMemo)(() => {
7762
8315
  const parentMap = new Map(normalizedTasks.map((t) => [t.id, t.parentId]));
7763
8316
  function isAnyAncestorCollapsed(parentId) {
7764
8317
  let current = parentId;
@@ -7774,11 +8327,11 @@ function GanttChartInner(props, ref) {
7774
8327
  }
7775
8328
  return tasks2;
7776
8329
  }, [normalizedTasks, collapsedParentIds, filterMode, taskFilter]);
7777
- const matchedTaskIds = (0, import_react13.useMemo)(() => {
8330
+ const matchedTaskIds = (0, import_react15.useMemo)(() => {
7778
8331
  if (!taskFilter) return /* @__PURE__ */ new Set();
7779
8332
  return new Set(visibleTasks.filter(taskFilter).map((task) => task.id));
7780
8333
  }, [visibleTasks, taskFilter]);
7781
- const taskListHighlightedTaskIds = (0, import_react13.useMemo)(() => {
8334
+ const taskListHighlightedTaskIds = (0, import_react15.useMemo)(() => {
7782
8335
  if (filterMode === "hide") {
7783
8336
  return /* @__PURE__ */ new Set();
7784
8337
  }
@@ -7789,23 +8342,23 @@ function GanttChartInner(props, ref) {
7789
8342
  matchedTaskIds.forEach((taskId) => mergedHighlightedTaskIds.add(taskId));
7790
8343
  return mergedHighlightedTaskIds;
7791
8344
  }, [filterMode, highlightedTaskIds, matchedTaskIds]);
7792
- const totalGridHeight = (0, import_react13.useMemo)(
8345
+ const totalGridHeight = (0, import_react15.useMemo)(
7793
8346
  () => visibleTasks.length * rowHeight,
7794
8347
  [visibleTasks.length, rowHeight]
7795
8348
  );
7796
- const monthStart = (0, import_react13.useMemo)(() => {
8349
+ const monthStart = (0, import_react15.useMemo)(() => {
7797
8350
  if (dateRange.length === 0) {
7798
8351
  return new Date(Date.UTC((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth(), 1));
7799
8352
  }
7800
8353
  const firstDay = dateRange[0];
7801
8354
  return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));
7802
8355
  }, [dateRange]);
7803
- const todayInRange = (0, import_react13.useMemo)(() => {
8356
+ const todayInRange = (0, import_react15.useMemo)(() => {
7804
8357
  const now = /* @__PURE__ */ new Date();
7805
8358
  const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
7806
8359
  return dateRange.some((day) => day.getTime() === today.getTime());
7807
8360
  }, [dateRange]);
7808
- (0, import_react13.useEffect)(() => {
8361
+ (0, import_react15.useEffect)(() => {
7809
8362
  const container = scrollContainerRef.current;
7810
8363
  if (!container || dateRange.length === 0) return;
7811
8364
  const now = /* @__PURE__ */ new Date();
@@ -7817,7 +8370,7 @@ function GanttChartInner(props, ref) {
7817
8370
  const scrollLeft = Math.round(todayOffset + dayWidth / 2 - containerWidth * 0.3);
7818
8371
  container.scrollLeft = Math.max(0, scrollLeft);
7819
8372
  }, []);
7820
- (0, import_react13.useEffect)(() => {
8373
+ (0, import_react15.useEffect)(() => {
7821
8374
  const container = scrollContainerRef.current;
7822
8375
  if (!container) return;
7823
8376
  const updateShadow = () => {
@@ -7829,7 +8382,7 @@ function GanttChartInner(props, ref) {
7829
8382
  container.removeEventListener("scroll", updateShadow);
7830
8383
  };
7831
8384
  }, []);
7832
- const scrollToToday = (0, import_react13.useCallback)(() => {
8385
+ const scrollToToday = (0, import_react15.useCallback)(() => {
7833
8386
  const container = scrollContainerRef.current;
7834
8387
  if (!container || dateRange.length === 0) return;
7835
8388
  const now = /* @__PURE__ */ new Date();
@@ -7841,7 +8394,7 @@ function GanttChartInner(props, ref) {
7841
8394
  const scrollLeft = Math.round(todayOffset + dayWidth / 2 - containerWidth * 0.3);
7842
8395
  container.scrollTo({ left: Math.max(0, scrollLeft), behavior: "smooth" });
7843
8396
  }, [dateRange, dayWidth]);
7844
- const scrollToTask = (0, import_react13.useCallback)((taskId) => {
8397
+ const scrollToTask = (0, import_react15.useCallback)((taskId) => {
7845
8398
  const container = scrollContainerRef.current;
7846
8399
  if (!container || dateRange.length === 0) return;
7847
8400
  const task = tasks.find((t) => t.id === taskId);
@@ -7858,7 +8411,7 @@ function GanttChartInner(props, ref) {
7858
8411
  const scrollLeft = Math.round(taskOffset - dayWidth * 2);
7859
8412
  container.scrollTo({ left: Math.max(0, scrollLeft), behavior: "smooth" });
7860
8413
  }, [tasks, dateRange, dayWidth]);
7861
- const scrollToRow = (0, import_react13.useCallback)((taskId) => {
8414
+ const scrollToRow = (0, import_react15.useCallback)((taskId) => {
7862
8415
  const container = scrollContainerRef.current;
7863
8416
  if (!container) return;
7864
8417
  const task = tasks.find((t) => t.id === taskId);
@@ -7870,15 +8423,15 @@ function GanttChartInner(props, ref) {
7870
8423
  setSelectedTaskId(taskId);
7871
8424
  container.scrollTo({ top: scrollTop, behavior: "smooth" });
7872
8425
  }, [tasks, visibleTasks, rowHeight]);
7873
- const [dragGuideLines, setDragGuideLines] = (0, import_react13.useState)(null);
7874
- const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react13.useState)(null);
7875
- const [previewTasksById, setPreviewTasksById] = (0, import_react13.useState)(/* @__PURE__ */ new Map());
7876
- (0, import_react13.useEffect)(() => {
8426
+ const [dragGuideLines, setDragGuideLines] = (0, import_react15.useState)(null);
8427
+ const [draggedTaskOverride, setDraggedTaskOverride] = (0, import_react15.useState)(null);
8428
+ const [previewTasksById, setPreviewTasksById] = (0, import_react15.useState)(/* @__PURE__ */ new Map());
8429
+ (0, import_react15.useEffect)(() => {
7877
8430
  const result = validateDependencies(tasks);
7878
8431
  setValidationResult(result);
7879
8432
  onValidateDependencies?.(result);
7880
8433
  }, [tasks, onValidateDependencies]);
7881
- const handleTaskChange = (0, import_react13.useCallback)((updatedTasks) => {
8434
+ const handleTaskChange = (0, import_react15.useCallback)((updatedTasks) => {
7882
8435
  const updatedTask = updatedTasks[0];
7883
8436
  if (!updatedTask) return;
7884
8437
  const originalTask = tasks.find((t) => t.id === updatedTask.id);
@@ -7924,7 +8477,7 @@ function GanttChartInner(props, ref) {
7924
8477
  const cascadedTasks = disableConstraints ? [updatedTask] : universalCascade(updatedTask, newStart, newEnd, sourceTasks, businessDays, isCustomWeekend);
7925
8478
  onTasksChange?.(cascadedTasks);
7926
8479
  }, [tasks, onTasksChange, disableConstraints, editingTaskId, businessDays, isCustomWeekend]);
7927
- const handleDelete = (0, import_react13.useCallback)((taskId) => {
8480
+ const handleDelete = (0, import_react15.useCallback)((taskId) => {
7928
8481
  const toDelete = /* @__PURE__ */ new Set([taskId]);
7929
8482
  function collectDescendants(parentId) {
7930
8483
  const children = getChildren(parentId, tasks);
@@ -7949,10 +8502,10 @@ function GanttChartInner(props, ref) {
7949
8502
  }
7950
8503
  toDelete.forEach((id) => onDelete?.(id));
7951
8504
  }, [tasks, onTasksChange, onDelete]);
7952
- const handleInsertAfter = (0, import_react13.useCallback)((taskId, newTask) => {
8505
+ const handleInsertAfter = (0, import_react15.useCallback)((taskId, newTask) => {
7953
8506
  onInsertAfter?.(taskId, newTask);
7954
8507
  }, [onInsertAfter]);
7955
- const handleReorder = (0, import_react13.useCallback)((reorderedTasks, movedTaskId, inferredParentId) => {
8508
+ const handleReorder = (0, import_react15.useCallback)((reorderedTasks, movedTaskId, inferredParentId) => {
7956
8509
  let updated = reorderedTasks;
7957
8510
  if (movedTaskId) {
7958
8511
  updated = updated.map((t) => {
@@ -7969,7 +8522,7 @@ function GanttChartInner(props, ref) {
7969
8522
  }
7970
8523
  onTasksChange?.(normalized);
7971
8524
  }, [onTasksChange, onReorder]);
7972
- const dependencyOverrides = (0, import_react13.useMemo)(() => {
8525
+ const dependencyOverrides = (0, import_react15.useMemo)(() => {
7973
8526
  const map = new Map(cascadeOverrides);
7974
8527
  if (draggedTaskOverride) {
7975
8528
  map.set(draggedTaskOverride.taskId, {
@@ -7979,25 +8532,25 @@ function GanttChartInner(props, ref) {
7979
8532
  }
7980
8533
  return map;
7981
8534
  }, [cascadeOverrides, draggedTaskOverride]);
7982
- const handleCascadeProgress = (0, import_react13.useCallback)((overrides, previewTasks = []) => {
8535
+ const handleCascadeProgress = (0, import_react15.useCallback)((overrides, previewTasks = []) => {
7983
8536
  setCascadeOverrides(new Map(overrides));
7984
8537
  setPreviewTasksById(new Map(previewTasks.map((task) => [task.id, task])));
7985
8538
  }, []);
7986
- const previewNormalizedTasks = (0, import_react13.useMemo)(() => {
8539
+ const previewNormalizedTasks = (0, import_react15.useMemo)(() => {
7987
8540
  if (previewTasksById.size === 0) return normalizedTasks;
7988
8541
  return normalizedTasks.map((task) => previewTasksById.get(task.id) ?? task);
7989
8542
  }, [normalizedTasks, previewTasksById]);
7990
- const previewVisibleTasks = (0, import_react13.useMemo)(() => {
8543
+ const previewVisibleTasks = (0, import_react15.useMemo)(() => {
7991
8544
  if (previewTasksById.size === 0) return visibleTasks;
7992
8545
  return visibleTasks.map((task) => previewTasksById.get(task.id) ?? task);
7993
8546
  }, [visibleTasks, previewTasksById]);
7994
- const handleCascade = (0, import_react13.useCallback)((cascadedTasks) => {
8547
+ const handleCascade = (0, import_react15.useCallback)((cascadedTasks) => {
7995
8548
  onTasksChange?.(cascadedTasks);
7996
8549
  }, [tasks, onTasksChange]);
7997
- const handleTaskSelect = (0, import_react13.useCallback)((taskId) => {
8550
+ const handleTaskSelect = (0, import_react15.useCallback)((taskId) => {
7998
8551
  setSelectedTaskId(taskId);
7999
8552
  }, []);
8000
- const handleToggleCollapse = externalOnToggleCollapse ?? (0, import_react13.useCallback)((parentId) => {
8553
+ const handleToggleCollapse = externalOnToggleCollapse ?? (0, import_react15.useCallback)((parentId) => {
8001
8554
  setInternalCollapsedParentIds((prev) => {
8002
8555
  const next = new Set(prev);
8003
8556
  if (next.has(parentId)) {
@@ -8008,20 +8561,20 @@ function GanttChartInner(props, ref) {
8008
8561
  return next;
8009
8562
  });
8010
8563
  }, []);
8011
- const allParentIds = (0, import_react13.useMemo)(() => {
8564
+ const allParentIds = (0, import_react15.useMemo)(() => {
8012
8565
  return new Set(
8013
8566
  normalizedTasks.filter((t) => isTaskParent(t.id, normalizedTasks)).map((t) => t.id)
8014
8567
  );
8015
8568
  }, [normalizedTasks]);
8016
- const handleCollapseAll = (0, import_react13.useCallback)(() => {
8569
+ const handleCollapseAll = (0, import_react15.useCallback)(() => {
8017
8570
  if (externalCollapsedParentIds) return;
8018
8571
  setInternalCollapsedParentIds(allParentIds);
8019
8572
  }, [allParentIds, externalCollapsedParentIds]);
8020
- const handleExpandAll = (0, import_react13.useCallback)(() => {
8573
+ const handleExpandAll = (0, import_react15.useCallback)(() => {
8021
8574
  if (externalCollapsedParentIds) return;
8022
8575
  setInternalCollapsedParentIds(/* @__PURE__ */ new Set());
8023
8576
  }, [externalCollapsedParentIds]);
8024
- const exportToPdf = (0, import_react13.useCallback)(async (options) => {
8577
+ const exportToPdf = (0, import_react15.useCallback)(async (options) => {
8025
8578
  const sourceContainer = containerRef.current;
8026
8579
  const sourceContent = scrollContentRef.current;
8027
8580
  if (!sourceContainer || !sourceContent || typeof window === "undefined" || typeof document === "undefined") {
@@ -8057,7 +8610,7 @@ function GanttChartInner(props, ref) {
8057
8610
  orientation: options?.orientation
8058
8611
  });
8059
8612
  }, [showTaskList, showChart]);
8060
- (0, import_react13.useImperativeHandle)(
8613
+ (0, import_react15.useImperativeHandle)(
8061
8614
  ref,
8062
8615
  () => ({
8063
8616
  scrollToToday,
@@ -8080,7 +8633,7 @@ function GanttChartInner(props, ref) {
8080
8633
  }
8081
8634
  return depth;
8082
8635
  }
8083
- const handlePromoteTask = (0, import_react13.useCallback)((taskId) => {
8636
+ const handlePromoteTask = (0, import_react15.useCallback)((taskId) => {
8084
8637
  if (onPromoteTask) {
8085
8638
  onPromoteTask(taskId);
8086
8639
  return;
@@ -8110,7 +8663,7 @@ function GanttChartInner(props, ref) {
8110
8663
  ]);
8111
8664
  onTasksChange?.(reorderedTasks);
8112
8665
  }, [tasks, onTasksChange, onPromoteTask]);
8113
- const handleDemoteTask = (0, import_react13.useCallback)((taskId, newParentId) => {
8666
+ const handleDemoteTask = (0, import_react15.useCallback)((taskId, newParentId) => {
8114
8667
  if (onDemoteTask) {
8115
8668
  onDemoteTask(taskId, newParentId);
8116
8669
  return;
@@ -8151,7 +8704,7 @@ function GanttChartInner(props, ref) {
8151
8704
  };
8152
8705
  onTasksChange?.([updatedDemotedTask]);
8153
8706
  }, [tasks, onTasksChange, onDemoteTask]);
8154
- const handleUngroupTask = (0, import_react13.useCallback)((taskId) => {
8707
+ const handleUngroupTask = (0, import_react15.useCallback)((taskId) => {
8155
8708
  if (onUngroupTask) {
8156
8709
  onUngroupTask(taskId);
8157
8710
  return;
@@ -8178,8 +8731,8 @@ function GanttChartInner(props, ref) {
8178
8731
  }
8179
8732
  onDelete?.(taskId);
8180
8733
  }, [tasks, onTasksChange, onDelete, onUngroupTask]);
8181
- const panStateRef = (0, import_react13.useRef)(null);
8182
- const handlePanStart = (0, import_react13.useCallback)((e) => {
8734
+ const panStateRef = (0, import_react15.useRef)(null);
8735
+ const handlePanStart = (0, import_react15.useCallback)((e) => {
8183
8736
  if (e.button !== 0) return;
8184
8737
  const target = e.target;
8185
8738
  if (target.closest("[data-taskbar]")) return;
@@ -8200,7 +8753,7 @@ function GanttChartInner(props, ref) {
8200
8753
  container.style.cursor = "grabbing";
8201
8754
  e.preventDefault();
8202
8755
  }, []);
8203
- (0, import_react13.useEffect)(() => {
8756
+ (0, import_react15.useEffect)(() => {
8204
8757
  const handlePanMove = (e) => {
8205
8758
  const pan = panStateRef.current;
8206
8759
  if (!pan?.active) return;
@@ -8222,15 +8775,15 @@ function GanttChartInner(props, ref) {
8222
8775
  window.removeEventListener("mouseup", handlePanEnd);
8223
8776
  };
8224
8777
  }, []);
8225
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { ref: containerRef, className: "gantt-container", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8778
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: containerRef, className: "gantt-container", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8226
8779
  "div",
8227
8780
  {
8228
8781
  ref: scrollContainerRef,
8229
8782
  className: "gantt-scrollContainer",
8230
8783
  style: { height: containerHeight ?? "auto", cursor: "grab" },
8231
8784
  onMouseDown: handlePanStart,
8232
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { ref: scrollContentRef, className: "gantt-scrollContent", children: [
8233
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8785
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { ref: scrollContentRef, className: "gantt-scrollContent", children: [
8786
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8234
8787
  TaskList,
8235
8788
  {
8236
8789
  tasks: normalizedTasks,
@@ -8252,6 +8805,7 @@ function GanttChartInner(props, ref) {
8252
8805
  onReorder: handleReorder,
8253
8806
  editingTaskId,
8254
8807
  enableAddTask,
8808
+ defaultTaskDurationDays,
8255
8809
  collapsedParentIds,
8256
8810
  onToggleCollapse: handleToggleCollapse,
8257
8811
  onPromoteTask: onPromoteTask ?? handlePromoteTask,
@@ -8268,13 +8822,13 @@ function GanttChartInner(props, ref) {
8268
8822
  taskListMenuCommands
8269
8823
  }
8270
8824
  ),
8271
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8825
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8272
8826
  "div",
8273
8827
  {
8274
8828
  className: showChart ? "gantt-chartSurface" : "gantt-chartSurface gantt-chart-hidden",
8275
8829
  style: { minWidth: `${gridWidth}px`, flex: 1, display: showChart ? void 0 : "none" },
8276
8830
  children: [
8277
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "gantt-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8831
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "gantt-stickyHeader", style: { width: `${gridWidth}px` }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8278
8832
  TimeScaleHeader_default,
8279
8833
  {
8280
8834
  days: dateRange,
@@ -8284,7 +8838,7 @@ function GanttChartInner(props, ref) {
8284
8838
  isCustomWeekend
8285
8839
  }
8286
8840
  ) }),
8287
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8841
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8288
8842
  "div",
8289
8843
  {
8290
8844
  className: "gantt-taskArea",
@@ -8293,7 +8847,7 @@ function GanttChartInner(props, ref) {
8293
8847
  width: `${gridWidth}px`
8294
8848
  },
8295
8849
  children: [
8296
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8850
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8297
8851
  GridBackground_default,
8298
8852
  {
8299
8853
  dateRange,
@@ -8303,8 +8857,8 @@ function GanttChartInner(props, ref) {
8303
8857
  isCustomWeekend
8304
8858
  }
8305
8859
  ),
8306
- todayInRange && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
8307
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8860
+ todayInRange && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TodayIndicator_default, { monthStart, dayWidth }),
8861
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8308
8862
  DependencyLines_default,
8309
8863
  {
8310
8864
  tasks: previewVisibleTasks,
@@ -8320,7 +8874,7 @@ function GanttChartInner(props, ref) {
8320
8874
  weekendPredicate: isCustomWeekend
8321
8875
  }
8322
8876
  ),
8323
- dragGuideLines && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8877
+ dragGuideLines && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8324
8878
  DragGuideLines_default,
8325
8879
  {
8326
8880
  isDragging: dragGuideLines.isDragging,
@@ -8330,7 +8884,7 @@ function GanttChartInner(props, ref) {
8330
8884
  totalHeight: totalGridHeight
8331
8885
  }
8332
8886
  ),
8333
- visibleTasks.map((task, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8887
+ visibleTasks.map((task, index) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8334
8888
  TaskRow_default,
8335
8889
  {
8336
8890
  task,
@@ -8374,13 +8928,14 @@ function GanttChartInner(props, ref) {
8374
8928
  }
8375
8929
  ) });
8376
8930
  }
8377
- var GanttChart = (0, import_react13.forwardRef)(GanttChartInner);
8931
+ var TaskGanttChart = (0, import_react15.forwardRef)(TaskGanttChartInner);
8932
+ var GanttChart = (0, import_react15.forwardRef)(GanttChartInner);
8378
8933
  GanttChart.displayName = "GanttChart";
8379
8934
 
8380
8935
  // src/components/ui/Button.tsx
8381
- var import_react14 = __toESM(require("react"));
8382
- var import_jsx_runtime16 = require("react/jsx-runtime");
8383
- var Button = import_react14.default.forwardRef(
8936
+ var import_react16 = __toESM(require("react"));
8937
+ var import_jsx_runtime17 = require("react/jsx-runtime");
8938
+ var Button = import_react16.default.forwardRef(
8384
8939
  ({ className, variant = "default", size = "default", children, ...props }, ref) => {
8385
8940
  const classes = [
8386
8941
  "gantt-btn",
@@ -8388,7 +8943,7 @@ var Button = import_react14.default.forwardRef(
8388
8943
  size !== "default" ? `gantt-btn-${size}` : "",
8389
8944
  className || ""
8390
8945
  ].filter(Boolean).join(" ");
8391
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { ref, className: classes, ...props, children });
8946
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { ref, className: classes, ...props, children });
8392
8947
  }
8393
8948
  );
8394
8949
  Button.displayName = "Button";
@@ -8434,6 +8989,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
8434
8989
  Popover,
8435
8990
  PopoverContent,
8436
8991
  PopoverTrigger,
8992
+ ResourceTimelineChart,
8437
8993
  TaskList,
8438
8994
  TaskRow,
8439
8995
  TimeScaleHeader,
@@ -8497,6 +9053,7 @@ var nameContains = (substring, caseSensitive = false) => (task) => {
8497
9053
  isTaskParent,
8498
9054
  isToday,
8499
9055
  isWeekend,
9056
+ layoutResourceTimelineItems,
8500
9057
  moveTaskRange,
8501
9058
  moveTaskWithCascade,
8502
9059
  nameContains,