gantt-lib 0.72.2 → 0.73.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.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1, { ReactNode } from 'react';
2
- import { T as Task$1, V as ValidationResult } from './index-DlJm2l_7.js';
3
- export { D as DAY_MS, a as DependencyError, G as GanttDateRange, b as GridConfig, c as GridLine, L as LinkType, M as MonthSpan, d as TaskBarGeometry, W as WeekendBlock, e as alignToWorkingDay, f as areTasksHierarchicallyRelated, g as buildAdjacencyList, h as buildTaskRangeFromEnd, i as buildTaskRangeFromStart, j as calculateSuccessorDate, k as cascadeByLinks, l as clampTaskRangeForIncomingFS, m as computeLagFromDates, n as computeParentDates, o as computeParentProgress, p as detectCycles, q as findParentId, r as getAllDependencyEdges, s as getAllDescendants, t as getBusinessDayOffset, u as getChildren, v as getDependencyLag, w as getSuccessorChain, x as getTaskDuration, y as getTransitiveCascadeChain, z as isAncestorTask, A as isTaskParent, B as moveTaskRange, C as moveTaskWithCascade, E as normalizeDependencyLag, F as normalizePredecessorDates, H as normalizeUTCDate, I as parseDateOnly, J as recalculateIncomingLags, K as recalculateProjectSchedule, N as recalculateTaskFromDependencies, O as reflowTasksOnModeSwitch, P as removeDependenciesBetweenTasks, Q as resizeTaskWithCascade, R as shiftBusinessDayOffset, S as universalCascade, U as validateDependencies } from './index-DlJm2l_7.js';
2
+ import { T as Task$1, V as ValidationResult } from './index-D8XWS1Ku.js';
3
+ export { D as DAY_MS, a as DependencyError, G as GanttDateRange, b as GridConfig, c as GridLine, L as LinkType, M as MonthSpan, d as TaskBarGeometry, W as WeekendBlock, e as alignToWorkingDay, f as areTasksHierarchicallyRelated, g as buildAdjacencyList, h as buildTaskRangeFromEnd, i as buildTaskRangeFromStart, j as calculateSuccessorDate, k as cascadeByLinks, l as clampTaskRangeForIncomingFS, m as computeLagFromDates, n as computeParentDates, o as computeParentProgress, p as detectCycles, q as findParentId, r as getAllDependencyEdges, s as getAllDescendants, t as getBusinessDayOffset, u as getChildren, v as getDependencyLag, w as getSuccessorChain, x as getTaskDuration, y as getTransitiveCascadeChain, z as isAncestorTask, A as isTaskParent, B as moveTaskRange, C as moveTaskWithCascade, E as normalizeDependencyLag, F as normalizePredecessorDates, H as normalizeUTCDate, I as parseDateOnly, J as recalculateIncomingLags, K as recalculateProjectSchedule, N as recalculateTaskFromDependencies, O as reflowTasksOnModeSwitch, P as removeDependenciesBetweenTasks, Q as resizeTaskWithCascade, R as shiftBusinessDayOffset, S as universalCascade, U as validateDependencies } from './index-D8XWS1Ku.js';
4
4
  import * as RadixPopover from '@radix-ui/react-popover';
5
5
 
6
6
  /**
@@ -385,6 +385,26 @@ interface TaskDependency {
385
385
  /** Lag in days */
386
386
  lag: number;
387
387
  }
388
+ interface TaskListMenuCommand<TTask extends Task = Task> {
389
+ /** Stable command id for React keys and consumer bookkeeping */
390
+ id: string;
391
+ /** Visible label in the three-dots menu */
392
+ label: string;
393
+ /** Optional icon rendered before the label */
394
+ icon?: React$1.ReactNode;
395
+ /** Command handler receives the current task row */
396
+ onSelect: (row: TTask) => void;
397
+ /** Optional per-row visibility predicate */
398
+ isVisible?: (row: TTask) => boolean;
399
+ /** Optional per-row disabled predicate */
400
+ isDisabled?: (row: TTask) => boolean;
401
+ /** Scope of the command in the hierarchy: all rows, parent/group rows, regular linear rows, or milestones */
402
+ scope?: 'all' | 'group' | 'linear' | 'milestone';
403
+ /** Marks the command with danger styling */
404
+ danger?: boolean;
405
+ /** Close the menu after click (default: true) */
406
+ closeOnSelect?: boolean;
407
+ }
388
408
  interface GanttChartProps<TTask extends Task = Task> {
389
409
  /** Array of tasks to display */
390
410
  tasks: TTask[];
@@ -428,6 +448,8 @@ interface GanttChartProps<TTask extends Task = Task> {
428
448
  onPromoteTask?: (taskId: string) => void;
429
449
  /** Callback when a task is demoted (parentId set). If not provided, default internal logic is used. */
430
450
  onDemoteTask?: (taskId: string, newParentId: string) => void;
451
+ /** Callback when a parent task is ungrouped (removed while direct children move one level up). */
452
+ onUngroupTask?: (taskId: string) => void;
431
453
  /** Enable add task button at bottom of task list (default: true) */
432
454
  enableAddTask?: boolean;
433
455
  /** View mode: 'day' renders one column per day, 'week' renders one column per 7 days, 'month' renders one column per month (default: 'day') */
@@ -458,6 +480,8 @@ interface GanttChartProps<TTask extends Task = Task> {
458
480
  showChart?: boolean;
459
481
  /** Additional custom columns to render in the TaskList after built-in columns */
460
482
  additionalColumns?: TaskListColumn<TTask>[];
483
+ /** Additional commands rendered in the TaskList row three-dots menu */
484
+ taskListMenuCommands?: TaskListMenuCommand<TTask>[];
461
485
  }
462
486
  interface ExportToPdfOptions {
463
487
  /** Structured header displayed above the exported chart */
@@ -696,6 +720,8 @@ interface TaskListProps {
696
720
  onPromoteTask?: (taskId: string) => void;
697
721
  /** Callback when task is demoted (parentId set to previous task) */
698
722
  onDemoteTask?: (taskId: string, newParentId: string) => void;
723
+ /** Callback when parent task is ungrouped (removed while direct children move one level up) */
724
+ onUngroupTask?: (taskId: string) => void;
699
725
  /** Custom day configurations for date picker */
700
726
  customDays?: CustomDayConfig[];
701
727
  /** Optional base weekend predicate for date picker */
@@ -712,6 +738,8 @@ interface TaskListProps {
712
738
  isFilterActive?: boolean;
713
739
  /** Additional columns to display after built-in columns */
714
740
  additionalColumns?: TaskListColumn<any>[];
741
+ /** Additional commands rendered in each row three-dots menu */
742
+ taskListMenuCommands?: TaskListMenuCommand<Task>[];
715
743
  }
716
744
  /**
717
745
  * TaskList component - displays tasks in a table format as an overlay
@@ -1155,4 +1183,4 @@ interface VisibleReorderPosition {
1155
1183
  */
1156
1184
  declare function getVisibleReorderPosition(orderedTasks: TaskLike[], visibleTasks: TaskLike[], movedTaskId: string, originVisibleIndex: number, dropVisibleIndex: number): VisibleReorderPosition | null;
1157
1185
 
1158
- 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, GridBackground, Input, type InputProps, type MonthBlock, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type Task, type TaskDependency, TaskList, type TaskListColumn, type TaskListColumnContext, 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, nameContains, normalizeHierarchyTasks, normalizeTaskDates, not, or, parseUTCDate, pixelsToDate, progressInRange, resolveDateRangeFromPixels, resolveTaskHorizontalGeometry, subtractBusinessDays, useTaskDrag, withoutDeps };
1186
+ 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, GridBackground, Input, type InputProps, type MonthBlock, Popover, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, 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, nameContains, normalizeHierarchyTasks, normalizeTaskDates, not, or, parseUTCDate, pixelsToDate, progressInRange, resolveDateRangeFromPixels, resolveTaskHorizontalGeometry, subtractBusinessDays, useTaskDrag, withoutDeps };
package/dist/index.js CHANGED
@@ -1255,45 +1255,9 @@ function universalCascade(movedTask, newStart, newEnd, allTasks, businessDays =
1255
1255
  }
1256
1256
  return Array.from(resultMap.values());
1257
1257
  }
1258
- function reflowTasksOnModeSwitch(sourceTasks, toBusinessDays, weekendPredicate) {
1259
- const fromBusinessDays = !toBusinessDays;
1260
- let tasks = sourceTasks.map((t) => ({ ...t }));
1261
- const toISO = (d) => d.toISOString().split("T")[0];
1262
- for (const task of tasks) {
1263
- if (isTaskParent(task.id, tasks)) continue;
1264
- const start = normalizeUTCDate(/* @__PURE__ */ new Date(`${task.startDate}T00:00:00.000Z`));
1265
- const duration = getTaskDuration(task.startDate, task.endDate, fromBusinessDays, weekendPredicate);
1266
- let range;
1267
- if (toBusinessDays) {
1268
- const alignedStart = alignToWorkingDay(start, 1, weekendPredicate);
1269
- range = buildTaskRangeFromStart(alignedStart, duration, true, weekendPredicate);
1270
- } else {
1271
- range = buildTaskRangeFromStart(start, duration, false);
1272
- }
1273
- task.startDate = toISO(range.start);
1274
- task.endDate = toISO(range.end);
1275
- }
1276
- for (const task of tasks) {
1277
- if (!isTaskParent(task.id, tasks)) continue;
1278
- const { startDate, endDate } = computeParentDates(task.id, tasks);
1279
- task.startDate = toISO(startDate);
1280
- task.endDate = toISO(endDate);
1281
- }
1282
- if (toBusinessDays) {
1283
- const rootSeeds = tasks.filter(
1284
- (t) => !t.parentId && (!t.dependencies || t.dependencies.length === 0)
1285
- );
1286
- for (const seed of rootSeeds) {
1287
- const current = tasks.find((t) => t.id === seed.id);
1288
- const start = /* @__PURE__ */ new Date(`${current.startDate}T00:00:00.000Z`);
1289
- const end = /* @__PURE__ */ new Date(`${current.endDate}T00:00:00.000Z`);
1290
- const cascaded = universalCascade(current, start, end, tasks, toBusinessDays, weekendPredicate);
1291
- const updates = new Map(cascaded.map((t) => [t.id, t]));
1292
- tasks = tasks.map((t) => updates.get(t.id) ?? t);
1293
- }
1294
- }
1295
- return tasks;
1296
- }
1258
+
1259
+ // src/core/scheduling/modeSwitch.ts
1260
+ init_dateMath();
1297
1261
 
1298
1262
  // src/core/scheduling/execute.ts
1299
1263
  init_dateMath();
@@ -1600,6 +1564,44 @@ function recalculateProjectSchedule(snapshot, options) {
1600
1564
  return createChangedResult(snapshot, Array.from(workingMap.values()));
1601
1565
  }
1602
1566
 
1567
+ // src/core/scheduling/modeSwitch.ts
1568
+ function reflowTasksOnModeSwitch(sourceTasks, toBusinessDays, weekendPredicate) {
1569
+ const fromBusinessDays = !toBusinessDays;
1570
+ let tasks = sourceTasks.map((task) => ({
1571
+ ...task,
1572
+ dependencies: task.dependencies?.map((dependency) => ({ ...dependency }))
1573
+ }));
1574
+ const toISO = (date) => date.toISOString().split("T")[0];
1575
+ for (const task of tasks) {
1576
+ if (isTaskParent(task.id, tasks)) continue;
1577
+ const start = normalizeUTCDate(/* @__PURE__ */ new Date(`${task.startDate}T00:00:00.000Z`));
1578
+ const duration = getTaskDuration(task.startDate, task.endDate, fromBusinessDays, weekendPredicate);
1579
+ const range = toBusinessDays ? buildTaskRangeFromStart(
1580
+ alignToWorkingDay(start, 1, weekendPredicate),
1581
+ duration,
1582
+ true,
1583
+ weekendPredicate
1584
+ ) : buildTaskRangeFromStart(start, duration, false);
1585
+ task.startDate = toISO(range.start);
1586
+ task.endDate = toISO(range.end);
1587
+ }
1588
+ for (const task of tasks) {
1589
+ if (!isTaskParent(task.id, tasks)) continue;
1590
+ const { startDate, endDate } = computeParentDates(task.id, tasks);
1591
+ task.startDate = toISO(startDate);
1592
+ task.endDate = toISO(endDate);
1593
+ }
1594
+ const rescheduled = recalculateProjectSchedule(tasks, {
1595
+ businessDays: toBusinessDays,
1596
+ weekendPredicate
1597
+ });
1598
+ if (rescheduled.changedTasks.length === 0) {
1599
+ return tasks;
1600
+ }
1601
+ const updates = new Map(rescheduled.changedTasks.map((task) => [task.id, task]));
1602
+ return tasks.map((task) => updates.get(task.id) ?? task);
1603
+ }
1604
+
1603
1605
  // src/core/scheduling/validation.ts
1604
1606
  function buildAdjacencyList(tasks) {
1605
1607
  const graph = /* @__PURE__ */ new Map();
@@ -4246,6 +4248,26 @@ var CopyIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4246
4248
  ]
4247
4249
  }
4248
4250
  );
4251
+ var UngroupIcon = () => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
4252
+ "svg",
4253
+ {
4254
+ xmlns: "http://www.w3.org/2000/svg",
4255
+ width: "16",
4256
+ height: "16",
4257
+ viewBox: "0 0 24 24",
4258
+ fill: "none",
4259
+ stroke: "currentColor",
4260
+ strokeWidth: "2",
4261
+ strokeLinecap: "round",
4262
+ strokeLinejoin: "round",
4263
+ children: [
4264
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M3 12h10" }),
4265
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 9-3 3 3 3" }),
4266
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M13 6h8" }),
4267
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M13 18h8" })
4268
+ ]
4269
+ }
4270
+ );
4249
4271
  var TASK_COLOR_PALETTE = [
4250
4272
  // { label: "Палисандр", value: "#A61E4D" },
4251
4273
  // { label: "Киноварь", value: "#E8590C" },
@@ -4678,6 +4700,7 @@ var TaskListRow = import_react10.default.memo(
4678
4700
  onToggleCollapse,
4679
4701
  onPromoteTask,
4680
4702
  onDemoteTask,
4703
+ onUngroupTask,
4681
4704
  onDuplicateTask,
4682
4705
  canDemoteTask = true,
4683
4706
  isLastChild = true,
@@ -4689,7 +4712,8 @@ var TaskListRow = import_react10.default.memo(
4689
4712
  businessDays,
4690
4713
  isFilterMatch = false,
4691
4714
  isFilterHideMode = false,
4692
- resolvedColumns
4715
+ resolvedColumns,
4716
+ taskListMenuCommands = []
4693
4717
  }) => {
4694
4718
  const [editingColumnId, setEditingColumnId] = (0, import_react10.useState)(null);
4695
4719
  const editingName = editingColumnId === "name";
@@ -4723,6 +4747,7 @@ var TaskListRow = import_react10.default.memo(
4723
4747
  [task.id, allTasks]
4724
4748
  );
4725
4749
  const isChild = task.parentId !== void 0;
4750
+ const isMilestoneRow = normalizedTask.type === "milestone";
4726
4751
  const weekendPredicate = (0, import_react10.useMemo)(
4727
4752
  () => createCustomDayPredicate({ customDays, isWeekend: isWeekend3 }),
4728
4753
  [customDays, isWeekend3]
@@ -5245,6 +5270,38 @@ var TaskListRow = import_react10.default.memo(
5245
5270
  },
5246
5271
  [allTasks, isParent, onTasksChange, task]
5247
5272
  );
5273
+ const handleUngroup = (0, import_react10.useCallback)(
5274
+ (e) => {
5275
+ e.stopPropagation();
5276
+ setContextMenuOpen(false);
5277
+ onUngroupTask?.(task.id);
5278
+ },
5279
+ [onUngroupTask, task.id]
5280
+ );
5281
+ const visibleCustomMenuCommands = (0, import_react10.useMemo)(
5282
+ () => taskListMenuCommands.filter(
5283
+ (command) => {
5284
+ const scope = command.scope ?? "all";
5285
+ if (scope === "group" && !isParent) return false;
5286
+ if (scope === "linear" && (isParent || isMilestoneRow)) return false;
5287
+ if (scope === "milestone" && !isMilestoneRow) return false;
5288
+ return command.isVisible?.(task) ?? true;
5289
+ }
5290
+ ),
5291
+ [taskListMenuCommands, task, isParent, isMilestoneRow]
5292
+ );
5293
+ const hasContextMenu = visibleCustomMenuCommands.length > 0 || !!onDuplicateTask || !!onDelete || !!onTasksChange || isParent && !!onUngroupTask;
5294
+ const handleCustomMenuCommandClick = (0, import_react10.useCallback)(
5295
+ (command) => (e) => {
5296
+ e.stopPropagation();
5297
+ if (command.closeOnSelect !== false) {
5298
+ setContextMenuOpen(false);
5299
+ setColorMenuOpen(false);
5300
+ }
5301
+ command.onSelect(task);
5302
+ },
5303
+ [task]
5304
+ );
5248
5305
  const handleAddClick = (0, import_react10.useCallback)(
5249
5306
  (e) => {
5250
5307
  e.stopPropagation();
@@ -5671,7 +5728,7 @@ var TaskListRow = import_react10.default.memo(
5671
5728
  "aria-hidden": "true"
5672
5729
  }
5673
5730
  ),
5674
- !editingName && (onInsertAfter || onDelete || onPromoteTask || onDemoteTask || onDuplicateTask || onTasksChange) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-name-actions", children: [
5731
+ !editingName && (onInsertAfter || onDelete || onPromoteTask || onDemoteTask || onUngroupTask || onDuplicateTask || onTasksChange || hasContextMenu) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "gantt-tl-name-actions", children: [
5675
5732
  onInsertAfter && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5676
5733
  "button",
5677
5734
  {
@@ -5717,7 +5774,7 @@ var TaskListRow = import_react10.default.memo(
5717
5774
  onDemote: onDemoteTask ? handleDemote : void 0
5718
5775
  }
5719
5776
  ),
5720
- (onDuplicateTask || onDelete || onTasksChange) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: contextMenuOpen, onOpenChange: (open) => {
5777
+ hasContextMenu && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Popover, { open: contextMenuOpen, onOpenChange: (open) => {
5721
5778
  setContextMenuOpen(open);
5722
5779
  if (!open) setColorMenuOpen(false);
5723
5780
  }, children: [
@@ -5810,6 +5867,32 @@ var TaskListRow = import_react10.default.memo(
5810
5867
  ]
5811
5868
  }
5812
5869
  ),
5870
+ visibleCustomMenuCommands.map((command) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5871
+ "button",
5872
+ {
5873
+ type: "button",
5874
+ className: `gantt-tl-context-menu-item${command.danger ? " gantt-tl-context-menu-item-danger" : ""}`,
5875
+ onClick: handleCustomMenuCommandClick(command),
5876
+ disabled: command.isDisabled?.(task) ?? false,
5877
+ children: [
5878
+ command.icon,
5879
+ command.label
5880
+ ]
5881
+ },
5882
+ command.id
5883
+ )),
5884
+ isParent && onUngroupTask && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5885
+ "button",
5886
+ {
5887
+ type: "button",
5888
+ className: "gantt-tl-context-menu-item",
5889
+ onClick: handleUngroup,
5890
+ children: [
5891
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UngroupIcon, {}),
5892
+ "\u0420\u0430\u0437\u0433\u0440\u0443\u043F\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C"
5893
+ ]
5894
+ }
5895
+ ),
5813
5896
  onDelete && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5814
5897
  "button",
5815
5898
  {
@@ -6505,6 +6588,7 @@ var TaskList = ({
6505
6588
  onToggleCollapse: externalOnToggleCollapse,
6506
6589
  onPromoteTask,
6507
6590
  onDemoteTask,
6591
+ onUngroupTask,
6508
6592
  customDays,
6509
6593
  isWeekend: isWeekend3,
6510
6594
  businessDays,
@@ -6512,7 +6596,8 @@ var TaskList = ({
6512
6596
  filterMode = "highlight",
6513
6597
  filteredTaskIds = /* @__PURE__ */ new Set(),
6514
6598
  isFilterActive = false,
6515
- additionalColumns
6599
+ additionalColumns,
6600
+ taskListMenuCommands
6516
6601
  }) => {
6517
6602
  const [internalCollapsedParentIds, setInternalCollapsedParentIds] = (0, import_react12.useState)(/* @__PURE__ */ new Set());
6518
6603
  const collapsedParentIds = externalCollapsedParentIds ?? internalCollapsedParentIds;
@@ -7176,6 +7261,7 @@ var TaskList = ({
7176
7261
  onToggleCollapse: handleToggleCollapse,
7177
7262
  onPromoteTask,
7178
7263
  onDemoteTask: onDemoteTask ? handleDemoteWrapper : void 0,
7264
+ onUngroupTask,
7179
7265
  onDuplicateTask: onReorder ? handleDuplicateTask : void 0,
7180
7266
  canDemoteTask,
7181
7267
  isLastChild: lastChildIds.has(task.id),
@@ -7187,7 +7273,8 @@ var TaskList = ({
7187
7273
  businessDays,
7188
7274
  isFilterMatch: filterMode === "highlight" ? highlightedTaskIds.has(task.id) : false,
7189
7275
  isFilterHideMode: filterMode === "hide" && isFilterActive,
7190
- resolvedColumns
7276
+ resolvedColumns,
7277
+ taskListMenuCommands
7191
7278
  }
7192
7279
  ),
7193
7280
  pendingInsertDisplayTaskId === task.id && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
@@ -7607,6 +7694,7 @@ function GanttChartInner(props, ref) {
7607
7694
  onReorder,
7608
7695
  onPromoteTask,
7609
7696
  onDemoteTask,
7697
+ onUngroupTask,
7610
7698
  enableAddTask = true,
7611
7699
  viewMode = "day",
7612
7700
  customDays,
@@ -7619,7 +7707,8 @@ function GanttChartInner(props, ref) {
7619
7707
  highlightedTaskIds,
7620
7708
  disableTaskDrag = false,
7621
7709
  showChart = true,
7622
- additionalColumns
7710
+ additionalColumns,
7711
+ taskListMenuCommands
7623
7712
  } = props;
7624
7713
  const containerRef = (0, import_react13.useRef)(null);
7625
7714
  const scrollContainerRef = (0, import_react13.useRef)(null);
@@ -8035,6 +8124,33 @@ function GanttChartInner(props, ref) {
8035
8124
  };
8036
8125
  onTasksChange?.([updatedDemotedTask]);
8037
8126
  }, [tasks, onTasksChange, onDemoteTask]);
8127
+ const handleUngroupTask = (0, import_react13.useCallback)((taskId) => {
8128
+ if (onUngroupTask) {
8129
+ onUngroupTask(taskId);
8130
+ return;
8131
+ }
8132
+ const parentTask = tasks.find((task) => task.id === taskId);
8133
+ if (!parentTask) return;
8134
+ const hasDirectChildren = tasks.some((task) => task.parentId === taskId);
8135
+ if (!hasDirectChildren) return;
8136
+ const changedTasks = [];
8137
+ for (const task of tasks) {
8138
+ if (task.id === taskId) continue;
8139
+ const nextParentId = task.parentId === taskId ? parentTask.parentId : task.parentId;
8140
+ const nextDependencies = task.dependencies?.filter((dep) => dep.taskId !== taskId);
8141
+ if (nextParentId !== task.parentId || nextDependencies?.length !== task.dependencies?.length) {
8142
+ changedTasks.push({
8143
+ ...task,
8144
+ parentId: nextParentId,
8145
+ dependencies: nextDependencies
8146
+ });
8147
+ }
8148
+ }
8149
+ if (changedTasks.length > 0) {
8150
+ onTasksChange?.(changedTasks);
8151
+ }
8152
+ onDelete?.(taskId);
8153
+ }, [tasks, onTasksChange, onDelete, onUngroupTask]);
8038
8154
  const panStateRef = (0, import_react13.useRef)(null);
8039
8155
  const handlePanStart = (0, import_react13.useCallback)((e) => {
8040
8156
  if (e.button !== 0) return;
@@ -8113,6 +8229,7 @@ function GanttChartInner(props, ref) {
8113
8229
  onToggleCollapse: handleToggleCollapse,
8114
8230
  onPromoteTask: onPromoteTask ?? handlePromoteTask,
8115
8231
  onDemoteTask: onDemoteTask ?? handleDemoteTask,
8232
+ onUngroupTask: onUngroupTask ?? handleUngroupTask,
8116
8233
  highlightedTaskIds: taskListHighlightedTaskIds,
8117
8234
  customDays,
8118
8235
  isWeekend: isWeekend3,
@@ -8120,7 +8237,8 @@ function GanttChartInner(props, ref) {
8120
8237
  filterMode,
8121
8238
  filteredTaskIds: matchedTaskIds,
8122
8239
  isFilterActive: !!taskFilter,
8123
- additionalColumns
8240
+ additionalColumns,
8241
+ taskListMenuCommands
8124
8242
  }
8125
8243
  ),
8126
8244
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(