ct-gantt-core 1.0.12 → 1.0.13

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.cjs CHANGED
@@ -22,12 +22,14 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  DAY_MS: () => DAY_MS,
24
24
  DEFAULT_HOUR_COLORS: () => DEFAULT_HOUR_COLORS,
25
+ DEFAULT_RESOURCE_COLORS: () => DEFAULT_RESOURCE_COLORS,
25
26
  DEFAULT_TASK_COLORS: () => DEFAULT_TASK_COLORS,
26
27
  EDITOR_COLOR_OPTIONS: () => EDITOR_COLOR_OPTIONS,
27
28
  GanttEngine: () => GanttEngine,
28
29
  HOUR_MS: () => HOUR_MS,
29
30
  OWNER_COLOR_PALETTE: () => OWNER_COLOR_PALETTE,
30
31
  RESOURCE_COLOR_PALETTE: () => RESOURCE_COLOR_PALETTE,
32
+ RESOURCE_EDITOR_COLOR_OPTIONS: () => RESOURCE_EDITOR_COLOR_OPTIONS,
31
33
  addDays: () => addDays,
32
34
  addHours: () => addHours,
33
35
  addPlannedDuration: () => addPlannedDuration,
@@ -38,8 +40,10 @@ __export(index_exports, {
38
40
  computeLayout: () => computeLayout,
39
41
  computeResourceLayout: () => computeResourceLayout,
40
42
  computeTimeScale: () => computeTimeScale,
43
+ computeVisibleWindow: () => computeVisibleWindow,
41
44
  computeWorkload: () => computeWorkload,
42
45
  createGanttEngine: () => createGanttEngine,
46
+ dateExtent: () => dateExtent,
43
47
  defaultConfig: () => defaultConfig,
44
48
  diffDays: () => diffDays,
45
49
  diffHours: () => diffHours,
@@ -49,15 +53,18 @@ __export(index_exports, {
49
53
  hasTimeComponent: () => hasTimeComponent,
50
54
  inclusiveDays: () => inclusiveDays,
51
55
  isValidDate: () => isValidDate,
56
+ mergeGanttConfig: () => mergeGanttConfig,
52
57
  mergeTaskPatch: () => mergeTaskPatch,
53
58
  normalizeLinks: () => normalizeLinks,
54
59
  plannedDurationBetween: () => plannedDurationBetween,
60
+ resolveGanttConfig: () => resolveGanttConfig,
55
61
  resolveResourceColor: () => resolveResourceColor,
56
62
  resolveUsageColor: () => resolveUsageColor,
57
63
  resolveUsageTimes: () => resolveUsageTimes,
58
64
  resultInvalidDate: () => resultInvalidDate,
59
65
  scheduleByDependencies: () => scheduleByDependencies,
60
66
  shiftTask: () => shiftTask,
67
+ sliceRangeItems: () => sliceRangeItems,
61
68
  startOfDay: () => startOfDay,
62
69
  subtractPlannedDuration: () => subtractPlannedDuration,
63
70
  toDate: () => toDate,
@@ -67,41 +74,50 @@ module.exports = __toCommonJS(index_exports);
67
74
 
68
75
  // src/styles/colors.ts
69
76
  var DEFAULT_TASK_COLORS = {
70
- task: "#4d77b3",
71
- // 普通任务实际条
72
- summary: "#475467",
73
- // 阶段条
74
- milestone: "#b57e4a",
75
- // 里程碑菱形 / 参考线默认
76
- plan: "#cbd5e1",
77
- // 计划条外部底色
78
- progress: "#367d73"
79
- // 计划条内部进度色(任务未设置 planColor 时)
77
+ task: "#409EFF",
78
+ // 普通任务实际条 · Element 主色
79
+ summary: "#909399",
80
+ // 阶段条 · Element Info
81
+ milestone: "#E6A23C",
82
+ // 里程碑菱形 / 参考线默认 · Element Warning
83
+ plan: "#C6E2FF",
84
+ // 计划条外部底色 · 主色浅阶
85
+ progress: "#67C23A"
86
+ // 计划条内部进度色 · Element Success
80
87
  };
81
88
  var DEFAULT_HOUR_COLORS = {
82
- plan: "#cbd5e1",
83
- // 计划条外部底色
84
- progress: "#367d73",
85
- // 计划条内部进度色(任务未设置 planColor 时)
86
- milestone: "#b57e4a"
89
+ plan: "#409EFF",
90
+ // 小时视图只有计划条,用主色保证在白底网格上可见
91
+ progress: "#67C23A",
92
+ // 兼容保留,小时视图不再绘制进度填充
93
+ milestone: "#E6A23C"
87
94
  // 里程碑菱形 / 参考线默认
88
95
  };
89
- var RESOURCE_COLOR_PALETTE = ["#f10755", "#9b87c4", "#509b81", "#c18f67", "#5e7fb0"];
96
+ var DEFAULT_RESOURCE_COLORS = {
97
+ booking: "#409EFF",
98
+ assignment: "#909399"
99
+ };
100
+ var RESOURCE_COLOR_PALETTE = [DEFAULT_RESOURCE_COLORS.booking];
101
+ var RESOURCE_EDITOR_COLOR_OPTIONS = [
102
+ "#409eff",
103
+ "#79bbff",
104
+ "#909399"
105
+ ];
90
106
  var EDITOR_COLOR_OPTIONS = [
91
- "#4d77b3",
92
- "#8f77c5",
93
- "#c16794",
94
- "#b57e4a",
95
- "#b3a24d",
96
- "#47ae8f",
97
- "#3f99a6",
98
- "#64748b"
107
+ "#409eff",
108
+ "#66b1ff",
109
+ "#67c23a",
110
+ "#e6a23c",
111
+ "#f56c6c",
112
+ "#909399",
113
+ "#9b87c4",
114
+ "#36cfc9"
99
115
  ];
100
- var OWNER_COLOR_PALETTE = ["#c2708b", "#9b87c4", "#509b81", "#c18f67", "#5e7fb0"];
116
+ var OWNER_COLOR_PALETTE = ["#409EFF", "#67C23A", "#E6A23C", "#F56C6C", "#909399"];
101
117
 
102
118
  // src/types/index.ts
103
119
  var defaultConfig = {
104
- viewMode: "month",
120
+ viewMode: "day",
105
121
  rowHeight: 44,
106
122
  columnWidth: 30,
107
123
  headerHeight: 50,
@@ -124,7 +140,9 @@ var defaultConfig = {
124
140
  hourWidth: 12,
125
141
  autoSchedule: true,
126
142
  editable: true,
127
- enableSelection: false
143
+ enableSelection: false,
144
+ showAddTaskColumn: true,
145
+ addTaskAs: "child"
128
146
  };
129
147
 
130
148
  // src/services/checkCyclicDependency.ts
@@ -234,12 +252,21 @@ function flattenTasks(tasks, collapsedIds = /* @__PURE__ */ new Set()) {
234
252
  const children = /* @__PURE__ */ new Map();
235
253
  for (const task of tasks) {
236
254
  const parentId = task.parentId ?? null;
237
- const bucket = children.get(parentId) ?? [];
238
- bucket.push(task);
239
- children.set(parentId, bucket);
255
+ const bucket = children.get(parentId);
256
+ if (bucket) {
257
+ bucket.push(task);
258
+ } else {
259
+ children.set(parentId, [task]);
260
+ }
240
261
  }
241
262
  const flattened = [];
263
+ const visited = /* @__PURE__ */ new Set();
264
+ const taskIds = new Set(tasks.map((task) => task.id));
242
265
  const visit = (task, depth) => {
266
+ if (visited.has(task.id)) {
267
+ return;
268
+ }
269
+ visited.add(task.id);
243
270
  const childTasks = children.get(task.id) ?? [];
244
271
  const collapsed = collapsedIds.has(task.id);
245
272
  flattened.push({
@@ -258,6 +285,12 @@ function flattenTasks(tasks, collapsedIds = /* @__PURE__ */ new Set()) {
258
285
  for (const root of children.get(null) ?? []) {
259
286
  visit(root, 0);
260
287
  }
288
+ for (const task of tasks) {
289
+ if (visited.has(task.id)) continue;
290
+ const parentId = task.parentId ?? null;
291
+ if (parentId && taskIds.has(parentId)) continue;
292
+ visit(task, 0);
293
+ }
261
294
  return flattened;
262
295
  }
263
296
 
@@ -359,6 +392,18 @@ function isValidDate(value) {
359
392
  const date = value instanceof Date ? value : new Date(value);
360
393
  return !Number.isNaN(date.getTime());
361
394
  }
395
+ function dateExtent(dates) {
396
+ let min = Number.POSITIVE_INFINITY;
397
+ let max = Number.NEGATIVE_INFINITY;
398
+ for (const value of dates) {
399
+ const time = typeof value === "number" ? value : value.getTime();
400
+ if (!Number.isFinite(time)) continue;
401
+ if (time < min) min = time;
402
+ if (time > max) max = time;
403
+ }
404
+ if (!Number.isFinite(min) || !Number.isFinite(max)) return void 0;
405
+ return { start: new Date(min), end: new Date(max) };
406
+ }
362
407
  function formatDate(date) {
363
408
  const value = toDate(date);
364
409
  const year = value.getFullYear();
@@ -463,6 +508,49 @@ function computeHourScale(start, end, hourWidth) {
463
508
  return { days, hours, start: rangeStart, end: rangeEnd };
464
509
  }
465
510
 
511
+ // src/services/computeVisibleWindow.ts
512
+ function computeVisibleWindow(scroll, viewportSize, itemSize, itemCount, overscan = 8) {
513
+ if (itemCount <= 0 || itemSize <= 0) {
514
+ return { start: 0, end: -1, before: 0, after: 0 };
515
+ }
516
+ if (viewportSize <= 0) {
517
+ return { start: 0, end: itemCount - 1, before: 0, after: 0 };
518
+ }
519
+ const start = Math.max(0, Math.floor(scroll / itemSize) - overscan);
520
+ const end = Math.min(
521
+ itemCount - 1,
522
+ Math.ceil((scroll + viewportSize) / itemSize) + overscan
523
+ );
524
+ return {
525
+ start,
526
+ end,
527
+ before: start * itemSize,
528
+ after: Math.max(0, (itemCount - end - 1) * itemSize)
529
+ };
530
+ }
531
+ function sliceRangeItems(items, scrollLeft, viewportWidth, overscanPx = 320) {
532
+ if (viewportWidth <= 0 || items.length === 0) {
533
+ return items;
534
+ }
535
+ const min = scrollLeft - overscanPx;
536
+ const max = scrollLeft + viewportWidth + overscanPx;
537
+ return items.filter((item) => item.left + item.width >= min && item.left <= max);
538
+ }
539
+
540
+ // src/utils/mergeConfig.ts
541
+ function mergeGanttConfig(base, patch = {}) {
542
+ return {
543
+ ...base,
544
+ ...patch,
545
+ taskColors: { ...base.taskColors, ...patch.taskColors },
546
+ columnWidths: { ...base.columnWidths, ...patch.columnWidths },
547
+ resourceColors: { ...base.resourceColors, ...patch.resourceColors }
548
+ };
549
+ }
550
+ function resolveGanttConfig(patch = {}) {
551
+ return mergeGanttConfig(defaultConfig, patch);
552
+ }
553
+
466
554
  // src/engines/scheduling.ts
467
555
  function scheduleByDependencies(tasks, links) {
468
556
  const cycle = checkCyclicDependency(links);
@@ -491,8 +579,18 @@ function scheduleByDependencies(tasks, links) {
491
579
  }
492
580
  };
493
581
  }
494
- incoming.set(link.targetId, [...incoming.get(link.targetId) ?? [], link]);
495
- outgoing.set(link.sourceId, [...outgoing.get(link.sourceId) ?? [], link]);
582
+ const incomingList = incoming.get(link.targetId);
583
+ if (incomingList) {
584
+ incomingList.push(link);
585
+ } else {
586
+ incoming.set(link.targetId, [link]);
587
+ }
588
+ const outgoingList = outgoing.get(link.sourceId);
589
+ if (outgoingList) {
590
+ outgoingList.push(link);
591
+ } else {
592
+ outgoing.set(link.sourceId, [link]);
593
+ }
496
594
  }
497
595
  const ordered = topologicalOrder(tasks.map((task) => task.id), links);
498
596
  for (const taskId of ordered) {
@@ -550,12 +648,20 @@ function topologicalOrder(ids, links) {
550
648
  const outgoing = /* @__PURE__ */ new Map();
551
649
  for (const link of links) {
552
650
  indegree.set(link.targetId, (indegree.get(link.targetId) ?? 0) + 1);
553
- outgoing.set(link.sourceId, [...outgoing.get(link.sourceId) ?? [], link.targetId]);
651
+ const targets = outgoing.get(link.sourceId);
652
+ if (targets) {
653
+ targets.push(link.targetId);
654
+ } else {
655
+ outgoing.set(link.sourceId, [link.targetId]);
656
+ }
554
657
  }
555
658
  const queue = ids.filter((id) => (indegree.get(id) ?? 0) === 0);
556
659
  const ordered = [];
557
- while (queue.length) {
558
- const current = queue.shift();
660
+ let cursor = 0;
661
+ while (cursor < queue.length) {
662
+ const current = queue[cursor];
663
+ cursor += 1;
664
+ if (!current) continue;
559
665
  ordered.push(current);
560
666
  for (const target of outgoing.get(current) ?? []) {
561
667
  indegree.set(target, (indegree.get(target) ?? 1) - 1);
@@ -590,7 +696,7 @@ function shiftTask(task, start) {
590
696
 
591
697
  // src/engines/computeLayout.ts
592
698
  function computeLayout(tasks, links, config, collapsedIds = /* @__PURE__ */ new Set(), viewport) {
593
- const mergedConfig = { ...defaultConfig, ...config };
699
+ const mergedConfig = resolveGanttConfig(config);
594
700
  for (const task of tasks) {
595
701
  if (!isValidDate(task.actual.start) || !isValidDate(task.actual.end)) {
596
702
  return {
@@ -608,7 +714,7 @@ function computeLayout(tasks, links, config, collapsedIds = /* @__PURE__ */ new
608
714
  return scheduled;
609
715
  }
610
716
  const flatTasks = flattenTasks(scheduled.data, collapsedIds);
611
- const firstDate = mergedConfig.visibleRange ? toDate(mergedConfig.visibleRange.start) : minDate(scheduled.data.map((task) => toDate(task.actual.start)));
717
+ const firstDate = mergedConfig.visibleRange ? toDate(mergedConfig.visibleRange.start) : dateExtent(scheduled.data.map((task) => toDate(task.actual.start)))?.start ?? toDate(/* @__PURE__ */ new Date());
612
718
  const visibleStart = viewport ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8) : 0;
613
719
  const visibleEnd = viewport ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8 : Number.POSITIVE_INFINITY;
614
720
  const layouts = flatTasks.filter((flat) => flat.rowIndex >= visibleStart && flat.rowIndex <= visibleEnd).map((flat) => {
@@ -627,9 +733,6 @@ function computeLayout(tasks, links, config, collapsedIds = /* @__PURE__ */ new
627
733
  });
628
734
  return { ok: true, data: layouts };
629
735
  }
630
- function minDate(dates) {
631
- return new Date(Math.min(...dates.map((date) => date.getTime())));
632
- }
633
736
 
634
737
  // src/engines/computeImpact.ts
635
738
  function computeImpact(taskId, patch, tasks, links, config) {
@@ -719,7 +822,13 @@ function resolveResourceColor(resource, resourceColors) {
719
822
  return RESOURCE_COLOR_PALETTE[colorHash(source) % RESOURCE_COLOR_PALETTE.length];
720
823
  }
721
824
  function resolveUsageColor(usage, resource, resourceColors) {
722
- return usage.color || resolveResourceColor(resource, resourceColors);
825
+ if (usage.color) {
826
+ return usage.color;
827
+ }
828
+ if (resource.color || resourceColors?.[resource.id]) {
829
+ return resolveResourceColor(resource, resourceColors);
830
+ }
831
+ return usage.type === "assignment" ? DEFAULT_RESOURCE_COLORS.assignment : DEFAULT_RESOURCE_COLORS.booking;
723
832
  }
724
833
  function resolveUsageTimes(usage, timeUnit) {
725
834
  if (timeUnit === "hour") {
@@ -730,7 +839,7 @@ function resolveUsageTimes(usage, timeUnit) {
730
839
  return { start: toDate(usage.start), end: toDate(usage.end) };
731
840
  }
732
841
  function computeResourceLayout(resources, usages, config, viewport) {
733
- const mergedConfig = { ...defaultConfig, ...config };
842
+ const mergedConfig = resolveGanttConfig(config);
734
843
  const timeUnit = mergedConfig.timeUnit ?? "day";
735
844
  const hourMode = timeUnit === "hour";
736
845
  const hourW = mergedConfig.hourWidth ?? 12;
@@ -763,7 +872,12 @@ function computeResourceLayout(resources, usages, config, viewport) {
763
872
  const resourceById = new Map(resources.map((resource) => [resource.id, resource]));
764
873
  const usagesByResource = /* @__PURE__ */ new Map();
765
874
  for (const usage of usages) {
766
- usagesByResource.set(usage.resourceId, [...usagesByResource.get(usage.resourceId) ?? [], usage]);
875
+ const grouped = usagesByResource.get(usage.resourceId);
876
+ if (grouped) {
877
+ grouped.push(usage);
878
+ } else {
879
+ usagesByResource.set(usage.resourceId, [usage]);
880
+ }
767
881
  }
768
882
  const rows = [];
769
883
  const usageLayouts = [];
@@ -808,8 +922,8 @@ function computeResourceLayout(resources, usages, config, viewport) {
808
922
  const visibleStart = viewport ? Math.max(0, Math.floor(viewport.scrollTop / mergedConfig.rowHeight) - 8) : 0;
809
923
  const visibleEnd = viewport ? Math.ceil((viewport.scrollTop + viewport.clientHeight) / mergedConfig.rowHeight) + 8 : Number.POSITIVE_INFINITY;
810
924
  const firstDate = hourMode ? startOfDay(
811
- mergedConfig.visibleRange ? toDateTime(mergedConfig.visibleRange.start) : usages.length ? minDate2(usages.map((usage) => resolveUsageTimes(usage, "hour").start)) : toDateTime(/* @__PURE__ */ new Date())
812
- ) : mergedConfig.visibleRange ? toDate(mergedConfig.visibleRange.start) : usages.length ? minDate2(usages.map((usage) => toDate(usage.start))) : toDate(/* @__PURE__ */ new Date());
925
+ mergedConfig.visibleRange ? toDateTime(mergedConfig.visibleRange.start) : dateExtent(usages.map((usage) => resolveUsageTimes(usage, "hour").start))?.start ?? toDateTime(/* @__PURE__ */ new Date())
926
+ ) : mergedConfig.visibleRange ? toDate(mergedConfig.visibleRange.start) : dateExtent(usages.map((usage) => toDate(usage.start)))?.start ?? toDate(/* @__PURE__ */ new Date());
813
927
  for (const row of rows) {
814
928
  if (row.rowIndex < visibleStart || row.rowIndex > visibleEnd) {
815
929
  continue;
@@ -846,16 +960,13 @@ function computeResourceLayout(resources, usages, config, viewport) {
846
960
  function isLaneFree(laneEnd, start, hourMode) {
847
961
  return hourMode ? diffHours(laneEnd, start) >= 0 : diffDays(laneEnd, start) > 0;
848
962
  }
849
- function minDate2(dates) {
850
- return new Date(Math.min(...dates.map((date) => date.getTime())));
851
- }
852
963
 
853
964
  // src/engines/computeWorkload.ts
854
965
  function computeWorkload(tasks, people, departments = [], range) {
855
966
  const dated = range ?? inferRange(tasks);
856
967
  if (!dated) return { dates: [], departments: [], ungrouped: [] };
857
968
  const dates = datesBetween(dated.start, dated.end);
858
- const dateSet = new Set(dates);
969
+ const dateIndex = new Map(dates.map((date, index) => [date, index]));
859
970
  const rows = new Map(people.map((person) => [person.id, createPerson(person, dates)]));
860
971
  for (const task of tasks) {
861
972
  if (task.type === "summary" || task.type === "milestone") continue;
@@ -865,21 +976,32 @@ function computeWorkload(tasks, people, departments = [], range) {
865
976
  for (const personId of ids) {
866
977
  const row = rows.get(personId);
867
978
  if (!row) continue;
868
- for (const [date, hours] of Object.entries(values)) if (dateSet.has(date)) row.days.find((day) => day.date === date).hours += hours;
979
+ for (const [date, hours] of Object.entries(values)) {
980
+ const index = dateIndex.get(date);
981
+ if (index !== void 0) row.days[index].hours += hours;
982
+ }
869
983
  }
870
984
  }
871
985
  for (const row of rows.values()) finalize(row);
872
986
  const grouped = /* @__PURE__ */ new Map();
873
987
  const ungrouped = [];
874
- for (const row of rows.values()) (row.person.departmentId ? grouped.get(row.person.departmentId) ?? (grouped.set(row.person.departmentId, []), grouped.get(row.person.departmentId)) : ungrouped).push(row);
988
+ for (const row of rows.values()) {
989
+ if (row.person.departmentId) {
990
+ const members = grouped.get(row.person.departmentId);
991
+ if (members) members.push(row);
992
+ else grouped.set(row.person.departmentId, [row]);
993
+ } else {
994
+ ungrouped.push(row);
995
+ }
996
+ }
875
997
  const departmentMap = new Map(departments.map((item) => [item.id, item]));
876
998
  const result = [...grouped].map(([id, members]) => aggregateDepartment(departmentMap.get(id) ?? { id, name: id }, members, dates));
877
999
  return { dates, departments: result, ungrouped };
878
1000
  }
879
1001
  function inferRange(tasks) {
880
1002
  if (!tasks.length) return void 0;
881
- const dates = tasks.flatMap((t) => [toDate(t.plan.start), toDate(t.plan.end)]);
882
- return { start: new Date(Math.min(...dates.map((d) => d.getTime()))), end: new Date(Math.max(...dates.map((d) => d.getTime()))) };
1003
+ const extent = dateExtent(tasks.flatMap((task) => [toDate(task.plan.start), toDate(task.plan.end)]));
1004
+ return extent ? { start: extent.start, end: extent.end } : void 0;
883
1005
  }
884
1006
  function datesBetween(start, end) {
885
1007
  const values = [];
@@ -952,7 +1074,7 @@ var GanttEngine = class {
952
1074
  constructor(options = {}) {
953
1075
  this.tasks = options.tasks ? options.tasks.map((task) => ({ ...task })) : [];
954
1076
  this.links = options.links ? options.links.map((link) => ({ ...link })) : [];
955
- this.config = { ...defaultConfig, ...options.config };
1077
+ this.config = mergeGanttConfig({ ...defaultConfig }, { ...options.config });
956
1078
  this.container = options.container ?? null;
957
1079
  this.collapsedIds = new Set(options.collapsedIds ?? []);
958
1080
  this.markers = options.markers ? options.markers.map((marker) => ({ ...marker })) : [];
@@ -986,22 +1108,22 @@ var GanttEngine = class {
986
1108
  setTasks(tasks) {
987
1109
  this.assertNotDestroyed();
988
1110
  this.tasks = tasks.map((task) => ({ ...task }));
989
- this.emit("taskschange", this.getTasks());
1111
+ this.emit("taskschange", this.tasks);
990
1112
  }
991
1113
  setLinks(links) {
992
1114
  this.assertNotDestroyed();
993
1115
  this.links = links.map((link) => ({ ...link }));
994
- this.emit("linkschange", this.getLinks());
1116
+ this.emit("linkschange", this.links);
995
1117
  }
996
1118
  setMarkers(markers) {
997
1119
  this.assertNotDestroyed();
998
1120
  this.markers = markers.map((marker) => ({ ...marker }));
999
- this.emit("markerschange", this.getMarkers());
1121
+ this.emit("markerschange", this.markers);
1000
1122
  }
1001
1123
  /** 合并配置 patch,等价于 setConfig(mergeConfig(patch)) */
1002
1124
  mergeConfig(patch) {
1003
1125
  this.assertNotDestroyed();
1004
- this.config = { ...this.config, ...patch };
1126
+ this.config = mergeGanttConfig(this.config, patch);
1005
1127
  this.emit("configchange", this.getConfig());
1006
1128
  }
1007
1129
  setConfig(config) {
@@ -1121,10 +1243,8 @@ var GanttEngine = class {
1121
1243
  const today = toDate(/* @__PURE__ */ new Date());
1122
1244
  return { start: today, end: addDays(today, 30) };
1123
1245
  }
1124
- return {
1125
- start: new Date(Math.min(...dates.map((d) => d.getTime()))),
1126
- end: new Date(Math.max(...dates.map((d) => d.getTime())))
1127
- };
1246
+ const extent = dateExtent(dates);
1247
+ return extent ?? { start: dates[0], end: dates[0] };
1128
1248
  }
1129
1249
  getTimeScale() {
1130
1250
  const range = this.getDateRange();
@@ -1289,12 +1409,14 @@ function createGanttEngine(options = {}) {
1289
1409
  0 && (module.exports = {
1290
1410
  DAY_MS,
1291
1411
  DEFAULT_HOUR_COLORS,
1412
+ DEFAULT_RESOURCE_COLORS,
1292
1413
  DEFAULT_TASK_COLORS,
1293
1414
  EDITOR_COLOR_OPTIONS,
1294
1415
  GanttEngine,
1295
1416
  HOUR_MS,
1296
1417
  OWNER_COLOR_PALETTE,
1297
1418
  RESOURCE_COLOR_PALETTE,
1419
+ RESOURCE_EDITOR_COLOR_OPTIONS,
1298
1420
  addDays,
1299
1421
  addHours,
1300
1422
  addPlannedDuration,
@@ -1305,8 +1427,10 @@ function createGanttEngine(options = {}) {
1305
1427
  computeLayout,
1306
1428
  computeResourceLayout,
1307
1429
  computeTimeScale,
1430
+ computeVisibleWindow,
1308
1431
  computeWorkload,
1309
1432
  createGanttEngine,
1433
+ dateExtent,
1310
1434
  defaultConfig,
1311
1435
  diffDays,
1312
1436
  diffHours,
@@ -1316,15 +1440,18 @@ function createGanttEngine(options = {}) {
1316
1440
  hasTimeComponent,
1317
1441
  inclusiveDays,
1318
1442
  isValidDate,
1443
+ mergeGanttConfig,
1319
1444
  mergeTaskPatch,
1320
1445
  normalizeLinks,
1321
1446
  plannedDurationBetween,
1447
+ resolveGanttConfig,
1322
1448
  resolveResourceColor,
1323
1449
  resolveUsageColor,
1324
1450
  resolveUsageTimes,
1325
1451
  resultInvalidDate,
1326
1452
  scheduleByDependencies,
1327
1453
  shiftTask,
1454
+ sliceRangeItems,
1328
1455
  startOfDay,
1329
1456
  subtractPlannedDuration,
1330
1457
  toDate,