gantt-lib 0.136.1 → 0.138.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/README.md CHANGED
@@ -115,6 +115,83 @@ const tasks: Task[] = [
115
115
  When using controlled `collapsedParentIds`, provide `onToggleCollapse` to update
116
116
  the collapsed parent IDs in application state.
117
117
 
118
+ ## Row Activities (Flow Line)
119
+
120
+ Give a row `activities` to turn it into a multi-bar lane — several works inside
121
+ one row, packed into sub-lanes (e.g. finishing works on one floor). Sequential
122
+ works share a lane; concurrent ones stack below and the row grows to fit the
123
+ busiest day. Activities are plain bars: drag to move, drag an edge to resize,
124
+ both snap to days. The hover tooltip is a narrow one-liner pinned above the bar:
125
+ the row (floor) name, a middle dot, then the work name — e.g. `Floor 12 ·
126
+ Wallpaper`; there is no native `title`. No hierarchy, dependencies, or
127
+ per-activity progress — works in the default gantt mode only.
128
+
129
+ Set `activityChain: true` to enable the ОН-conveyor (finish-to-start chain):
130
+ inside the row every work follows the previous one, and identical works (same
131
+ `id` or `name`) of consecutive chained rows are linked as well, so dragging one
132
+ work pulls the whole conveyor below — live, while the pointer moves, like every
133
+ other bar in the chart — the classic line-of-balance flow.
134
+
135
+ Consecutive works of a lane are linked: a plain gap is drawn as a horizontal
136
+ arrow with the lag («+N») at its head, while overlapping works (or works in
137
+ different lanes) get the standard orthogonal Gantt connector, routed down when
138
+ the successor drops to a lower lane. Turn links and lag labels off with
139
+ `showActivityLinks` and `showActivityLag`.
140
+
141
+ Click-activation is opt-in via `activityClickToDrag`: the first click arms a
142
+ work (a plain drag pans the canvas) and only the armed work is dragged.
143
+ `activityActivationMode` picks the activation visual — `'none'` (default),
144
+ `'dim'` (other work types fade) or `'highlight'` (the chain of the same work,
145
+ matched by `id` or `name` across rows, is outlined). Appearance is tuned with
146
+ `--gantt-activity-dim-opacity`, `--gantt-activity-active-outline`,
147
+ `--gantt-activity-chain-outline`, `--gantt-activity-tip-bg` and
148
+ `--gantt-activity-tip-color`.
149
+
150
+ The tooltip header (`row · work`) is fixed; extend it with declarative
151
+ `tooltipFields` on the activity (label–value rows, any React node as the value)
152
+ and/or the `activityTooltip(context)` render prop, whose context carries
153
+ `{ task, activity, startDate, endDate, durationDays }` — use it to show dates,
154
+ duration, crew, anything.
155
+
156
+ Two conveyor modes are available via `activityChain`. `true` — rigid: dragging a
157
+ work translates the whole downstream chain by the same delta. `'push'` —
158
+ выталкивание (ASAP): when a work moves earlier the downstream gaps fill in
159
+ immediately; when it moves later, successors stay put (the gap stretches) until
160
+ the predecessor's end crosses their start — then they get pushed. An activity's
161
+ `lag` (days) sets a constant gap before its start (e.g. technological curing):
162
+ both fill and push keep «predecessor end + lag» exactly. The lag belongs to the
163
+ same-floor sequence only — the vertical «same work on the floor above» link
164
+ carries no lag. Starts are clamped so
165
+ a dragged work cannot violate its own incoming links. Gaps wider than zero are
166
+ drawn as horizontal arrows between consecutive works of one lane.
167
+
168
+ In `'push'` mode the clamp is live and visible: the bar physically stops at the
169
+ binding predecessor instead of snapping back on drop, the blocking work gets a
170
+ red outline, and the dragged bar shows a stop edge with a «Блок: … · зазор N д»
171
+ line in the tooltip. The binding gap is labelled «+N» at the arrow head in the
172
+ dependency-line colour, matching the dependency lag labels.
173
+
174
+ ```tsx
175
+ const tasks: Task[] = [
176
+ {
177
+ id: "floor-12",
178
+ name: "Floor 12",
179
+ startDate: "2026-03-24",
180
+ endDate: "2026-04-02",
181
+ activityChain: true,
182
+ activities: [
183
+ { id: "screed", name: "Screed", startDate: "2026-03-24", endDate: "2026-03-26", color: "#8b5cf6" },
184
+ { id: "wallpaper", name: "Wallpaper", startDate: "2026-03-27", endDate: "2026-03-30", color: "#f59e0b" },
185
+ {
186
+ id: "doors", name: "Doors", startDate: "2026-03-31", endDate: "2026-04-01", color: "#10b981",
187
+ lag: 1, // constant gap (days) before this work starts — same-row chain only
188
+ tooltipFields: [{ label: "Contractor", value: "SK Stroy" }],
189
+ },
190
+ ],
191
+ },
192
+ ];
193
+ ```
194
+
118
195
  ## Resource Planner Mode
119
196
 
120
197
  Use `mode="resource-planner"` when the primary rows are people, equipment, rooms, or other resources instead of tasks. Omitted `mode` still renders the default task Gantt chart.
@@ -218,6 +295,9 @@ interface Task {
218
295
  locked?: boolean;
219
296
  divider?: 'top' | 'bottom';
220
297
  parentId?: string;
298
+ activities?: TaskActivity[]; // multi-bar lane mode for the row
299
+ activityChain?: boolean | 'push'; // ОН-conveyor: chained dragging; 'push' = ASAP с выталкиванием
300
+ // Gantt props: activityClickToDrag, activityActivationMode, showActivityLinks, showActivityLag, activityTooltip
221
301
  }
222
302
  ```
223
303
 
@@ -1,2 +1,2 @@
1
- export { ai as CascadeContext, C as CriticalPathOptions, D as DAY_MS, f as DependencyError, L as LinkType, n as ScaleTaskSubtreeAnchor, o as ScaleTaskSubtreeErrorCode, p as ScaleTaskSubtreeExternalPolicy, q as ScaleTaskSubtreeOptions, S as ScaleTaskSubtreeResult, r as ScaleTaskSubtreeWarning, aj as ScheduleCommandOptions, ak as ScheduleCommandResult, al as ScheduleDependency, am as ScheduleTask, an as ScheduleTaskUpdate, T as Task, ao as TaskDependency, V as ValidationResult, ap as addBusinessDays, t as alignToWorkingDay, u as areTasksHierarchicallyRelated, v as buildAdjacencyList, w as buildTaskRangeFromEnd, x as buildTaskRangeFromStart, y as calculateSuccessorDate, z as cascadeByLinks, aq as cascadeTaskProgress, A as clampTaskRangeForIncomingFS, E as computeCriticalPath, F as computeLagFromDates, H as computeParentDates, I as computeParentProgress, ar as createCascadeContext, J as detectCycles, K as extendCriticalIdsToParents, N as findParentId, O as getAllDependencyEdges, P as getAllDescendants, Q as getBusinessDayOffset, as as getBusinessDaysCount, U as getChildren, X as getDependencyLag, Y as getSuccessorChain, Z as getTaskDuration, _ as getTransitiveCascadeChain, $ as isAncestorTask, a0 as isTaskParent, a1 as moveTaskRange, a2 as moveTaskWithCascade, a3 as normalizeDependencyLag, a4 as normalizePredecessorDates, a5 as normalizeTaskDependencyLags, a6 as normalizeUTCDate, a7 as parseDateOnly, a8 as recalculateIncomingLags, a9 as recalculateProjectSchedule, aa as recalculateTaskFromDependencies, ab as reflowTasksOnModeSwitch, ac as removeDependenciesBetweenTasks, ad as resizeTaskWithCascade, ae as scaleTaskSubtreeDuration, af as shiftBusinessDayOffset, at as subtractBusinessDays, ag as universalCascade, ah as validateDependencies } from '../../index-D99kbU4p.mjs';
1
+ export { al as CascadeContext, C as CriticalPathOptions, D as DAY_MS, g as DependencyError, L as LinkType, o as ScaleTaskSubtreeAnchor, p as ScaleTaskSubtreeErrorCode, q as ScaleTaskSubtreeExternalPolicy, r as ScaleTaskSubtreeOptions, S as ScaleTaskSubtreeResult, s as ScaleTaskSubtreeWarning, am as ScheduleCommandOptions, an as ScheduleCommandResult, ao as ScheduleDependency, ap as ScheduleTask, aq as ScheduleTaskUpdate, T as Task, ar as TaskDependency, V as ValidationResult, as as addBusinessDays, v as alignToWorkingDay, w as areTasksHierarchicallyRelated, x as buildAdjacencyList, y as buildTaskRangeFromEnd, z as buildTaskRangeFromStart, E as calculateSuccessorDate, F as cascadeByLinks, at as cascadeTaskProgress, H as clampTaskRangeForIncomingFS, I as computeCriticalPath, J as computeLagFromDates, K as computeParentDates, N as computeParentProgress, au as createCascadeContext, O as detectCycles, P as extendCriticalIdsToParents, Q as findParentId, U as getAllDependencyEdges, X as getAllDescendants, Y as getBusinessDayOffset, av as getBusinessDaysCount, Z as getChildren, _ as getDependencyLag, $ as getSuccessorChain, a0 as getTaskDuration, a1 as getTransitiveCascadeChain, a2 as isAncestorTask, a3 as isTaskParent, a4 as moveTaskRange, a5 as moveTaskWithCascade, a6 as normalizeDependencyLag, a7 as normalizePredecessorDates, a8 as normalizeTaskDependencyLags, a9 as normalizeUTCDate, aa as parseDateOnly, ab as recalculateIncomingLags, ac as recalculateProjectSchedule, ad as recalculateTaskFromDependencies, ae as reflowTasksOnModeSwitch, af as removeDependenciesBetweenTasks, ag as resizeTaskWithCascade, ah as scaleTaskSubtreeDuration, ai as shiftBusinessDayOffset, aw as subtractBusinessDays, aj as universalCascade, ak as validateDependencies } from '../../index-C3mZqSTf.mjs';
2
2
  import 'react';
@@ -1,2 +1,2 @@
1
- export { ai as CascadeContext, C as CriticalPathOptions, D as DAY_MS, f as DependencyError, L as LinkType, n as ScaleTaskSubtreeAnchor, o as ScaleTaskSubtreeErrorCode, p as ScaleTaskSubtreeExternalPolicy, q as ScaleTaskSubtreeOptions, S as ScaleTaskSubtreeResult, r as ScaleTaskSubtreeWarning, aj as ScheduleCommandOptions, ak as ScheduleCommandResult, al as ScheduleDependency, am as ScheduleTask, an as ScheduleTaskUpdate, T as Task, ao as TaskDependency, V as ValidationResult, ap as addBusinessDays, t as alignToWorkingDay, u as areTasksHierarchicallyRelated, v as buildAdjacencyList, w as buildTaskRangeFromEnd, x as buildTaskRangeFromStart, y as calculateSuccessorDate, z as cascadeByLinks, aq as cascadeTaskProgress, A as clampTaskRangeForIncomingFS, E as computeCriticalPath, F as computeLagFromDates, H as computeParentDates, I as computeParentProgress, ar as createCascadeContext, J as detectCycles, K as extendCriticalIdsToParents, N as findParentId, O as getAllDependencyEdges, P as getAllDescendants, Q as getBusinessDayOffset, as as getBusinessDaysCount, U as getChildren, X as getDependencyLag, Y as getSuccessorChain, Z as getTaskDuration, _ as getTransitiveCascadeChain, $ as isAncestorTask, a0 as isTaskParent, a1 as moveTaskRange, a2 as moveTaskWithCascade, a3 as normalizeDependencyLag, a4 as normalizePredecessorDates, a5 as normalizeTaskDependencyLags, a6 as normalizeUTCDate, a7 as parseDateOnly, a8 as recalculateIncomingLags, a9 as recalculateProjectSchedule, aa as recalculateTaskFromDependencies, ab as reflowTasksOnModeSwitch, ac as removeDependenciesBetweenTasks, ad as resizeTaskWithCascade, ae as scaleTaskSubtreeDuration, af as shiftBusinessDayOffset, at as subtractBusinessDays, ag as universalCascade, ah as validateDependencies } from '../../index-D99kbU4p.js';
1
+ export { al as CascadeContext, C as CriticalPathOptions, D as DAY_MS, g as DependencyError, L as LinkType, o as ScaleTaskSubtreeAnchor, p as ScaleTaskSubtreeErrorCode, q as ScaleTaskSubtreeExternalPolicy, r as ScaleTaskSubtreeOptions, S as ScaleTaskSubtreeResult, s as ScaleTaskSubtreeWarning, am as ScheduleCommandOptions, an as ScheduleCommandResult, ao as ScheduleDependency, ap as ScheduleTask, aq as ScheduleTaskUpdate, T as Task, ar as TaskDependency, V as ValidationResult, as as addBusinessDays, v as alignToWorkingDay, w as areTasksHierarchicallyRelated, x as buildAdjacencyList, y as buildTaskRangeFromEnd, z as buildTaskRangeFromStart, E as calculateSuccessorDate, F as cascadeByLinks, at as cascadeTaskProgress, H as clampTaskRangeForIncomingFS, I as computeCriticalPath, J as computeLagFromDates, K as computeParentDates, N as computeParentProgress, au as createCascadeContext, O as detectCycles, P as extendCriticalIdsToParents, Q as findParentId, U as getAllDependencyEdges, X as getAllDescendants, Y as getBusinessDayOffset, av as getBusinessDaysCount, Z as getChildren, _ as getDependencyLag, $ as getSuccessorChain, a0 as getTaskDuration, a1 as getTransitiveCascadeChain, a2 as isAncestorTask, a3 as isTaskParent, a4 as moveTaskRange, a5 as moveTaskWithCascade, a6 as normalizeDependencyLag, a7 as normalizePredecessorDates, a8 as normalizeTaskDependencyLags, a9 as normalizeUTCDate, aa as parseDateOnly, ab as recalculateIncomingLags, ac as recalculateProjectSchedule, ad as recalculateTaskFromDependencies, ae as reflowTasksOnModeSwitch, af as removeDependenciesBetweenTasks, ag as resizeTaskWithCascade, ah as scaleTaskSubtreeDuration, ai as shiftBusinessDayOffset, aw as subtractBusinessDays, aj as universalCascade, ak as validateDependencies } from '../../index-C3mZqSTf.js';
2
2
  import 'react';
@@ -550,6 +550,29 @@ function parseCascadeDateInput(date) {
550
550
  }
551
551
  return normalizeUTCDate(/* @__PURE__ */ new Date(`${date.split("T")[0]}T00:00:00.000Z`));
552
552
  }
553
+ function projectDayDelta(from, to, businessDays, weekendPredicate) {
554
+ if (businessDays && weekendPredicate) {
555
+ return getBusinessDayOffset(from, to, weekendPredicate);
556
+ }
557
+ return Math.round((to.getTime() - from.getTime()) / DAY_MS);
558
+ }
559
+ function shiftTaskActivities(activities, dayDelta, businessDays, weekendPredicate) {
560
+ if (!activities || activities.length === 0 || dayDelta === 0) return void 0;
561
+ const shiftDate = (value) => {
562
+ const base = parseDateOnly(value);
563
+ const shifted = businessDays && weekendPredicate ? shiftBusinessDayOffset(base, dayDelta, weekendPredicate) : new Date(base.getTime() + dayDelta * DAY_MS);
564
+ return typeof value === "string" ? shifted.toISOString().slice(0, 10) : shifted;
565
+ };
566
+ return activities.map((activity) => ({
567
+ ...activity,
568
+ startDate: shiftDate(activity.startDate),
569
+ endDate: shiftDate(activity.endDate)
570
+ }));
571
+ }
572
+ function shiftedActivitiesPatch(activities, dayDelta, businessDays, weekendPredicate) {
573
+ const shifted = shiftTaskActivities(activities, dayDelta, businessDays, weekendPredicate);
574
+ return shifted ? { activities: shifted } : {};
575
+ }
553
576
  function createCascadeContext(allTasks) {
554
577
  const childrenByParentId = /* @__PURE__ */ new Map();
555
578
  const dependentsByPredecessorId = /* @__PURE__ */ new Map();
@@ -625,7 +648,8 @@ function cascadeByLinks(movedTaskId, newStart, newEnd, allTasks, skipChildCascad
625
648
  result.push(normalizeTaskDependencyLags({
626
649
  ...child,
627
650
  startDate: newChildStart.toISOString().split("T")[0],
628
- endDate: newChildEnd.toISOString().split("T")[0]
651
+ endDate: newChildEnd.toISOString().split("T")[0],
652
+ ...shiftedActivitiesPatch(child.activities, Math.round(parentStartDelta / DAY_MS), false)
629
653
  }));
630
654
  queue.push(child.id);
631
655
  }
@@ -769,7 +793,14 @@ function universalCascade(movedTask, newStart, newEnd, allTasks, businessDays =
769
793
  resultMap.set(child.id, normalizeTaskDependencyLags({
770
794
  ...child,
771
795
  startDate: childNewStart.toISOString().split("T")[0],
772
- endDate: childNewEnd.toISOString().split("T")[0]
796
+ endDate: childNewEnd.toISOString().split("T")[0],
797
+ // Дети едут параллельно: вместе с датами строки сдвигаются и её работы.
798
+ ...shiftedActivitiesPatch(
799
+ child.activities,
800
+ projectDayDelta(childOrigStart, childNewStart, businessDays, weekendPredicate),
801
+ businessDays,
802
+ weekendPredicate
803
+ )
773
804
  }));
774
805
  }
775
806
  }
@@ -848,7 +879,13 @@ function universalCascade(movedTask, newStart, newEnd, allTasks, businessDays =
848
879
  resultMap.set(task.id, normalizeTaskDependencyLags({
849
880
  ...task,
850
881
  startDate: succNewStart.toISOString().split("T")[0],
851
- endDate: succNewEnd.toISOString().split("T")[0]
882
+ endDate: succNewEnd.toISOString().split("T")[0],
883
+ ...shiftedActivitiesPatch(
884
+ task.activities,
885
+ projectDayDelta(origStart, succNewStart, businessDays, weekendPredicate),
886
+ businessDays,
887
+ weekendPredicate
888
+ )
852
889
  }));
853
890
  }
854
891
  }