gantt-canvas-chart 1.4.0 → 1.5.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.cjs.js +30 -26
- package/dist/index.css +1 -1
- package/dist/index.d.ts +26 -3
- package/dist/index.es.js +30 -26
- package/dist/index.umd.js +30 -26
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* gantt-canvas-chart v1.
|
|
2
|
+
* gantt-canvas-chart v1.5.1
|
|
3
3
|
* (c) 2025-present chandq
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -205,8 +205,8 @@ class GanttChart {
|
|
|
205
205
|
}
|
|
206
206
|
init() {
|
|
207
207
|
this.buildTaskMap();
|
|
208
|
-
this.calculateFullTimeline();
|
|
209
208
|
this.updatePixelsPerDay();
|
|
209
|
+
this.calculateFullTimeline();
|
|
210
210
|
this.setupEvents();
|
|
211
211
|
this.handleResize();
|
|
212
212
|
}
|
|
@@ -237,8 +237,8 @@ class GanttChart {
|
|
|
237
237
|
updateConfig(newConfig) {
|
|
238
238
|
Object.assign(this.config, newConfig);
|
|
239
239
|
if (newConfig.viewMode) {
|
|
240
|
-
this.container.scrollLeft =
|
|
241
|
-
this.scrollLeft =
|
|
240
|
+
this.container.scrollLeft = this.config.scrollEdgeThresholds + 2;
|
|
241
|
+
this.scrollLeft = this.config.scrollEdgeThresholds + 2;
|
|
242
242
|
this.updatePixelsPerDay();
|
|
243
243
|
this.calculateFullTimeline();
|
|
244
244
|
}
|
|
@@ -278,6 +278,9 @@ class GanttChart {
|
|
|
278
278
|
maxDate = DateUtils.addDays(/* @__PURE__ */ new Date(), 60);
|
|
279
279
|
} else {
|
|
280
280
|
this.taskMap.forEach(({ task }) => {
|
|
281
|
+
if (task.hide) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
281
284
|
const pStart = new Date(task.planStart);
|
|
282
285
|
const pEnd = new Date(task.planEnd);
|
|
283
286
|
if (pStart < minDate) minDate = pStart;
|
|
@@ -311,8 +314,8 @@ class GanttChart {
|
|
|
311
314
|
break;
|
|
312
315
|
case "Day":
|
|
313
316
|
default:
|
|
314
|
-
this.timelineStart = DateUtils.addDays(minDate,
|
|
315
|
-
this.timelineEnd = DateUtils.addDays(maxDate,
|
|
317
|
+
this.timelineStart = DateUtils.addDays(minDate, 3);
|
|
318
|
+
this.timelineEnd = DateUtils.addDays(maxDate, -5);
|
|
316
319
|
break;
|
|
317
320
|
}
|
|
318
321
|
}
|
|
@@ -478,7 +481,7 @@ class GanttChart {
|
|
|
478
481
|
if (this.scrollTop !== scrollTop) this.container.scrollTop = scrollTop;
|
|
479
482
|
}
|
|
480
483
|
updateVirtualRanges() {
|
|
481
|
-
const buffer =
|
|
484
|
+
const buffer = 100;
|
|
482
485
|
this.visibleDateRange = {
|
|
483
486
|
start: this.xToDate(this.scrollLeft - buffer),
|
|
484
487
|
end: this.xToDate(this.scrollLeft + this.viewportWidth + buffer)
|
|
@@ -508,8 +511,14 @@ class GanttChart {
|
|
|
508
511
|
this.taskPositions.clear();
|
|
509
512
|
for (let i = 0; i < this.data.length; i++) {
|
|
510
513
|
const row = this.data[i];
|
|
514
|
+
if (row.hide) {
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
511
517
|
const y = i * this.config.rowHeight;
|
|
512
518
|
row.tasks.forEach((task) => {
|
|
519
|
+
if (task.hide) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
513
522
|
const x_plan_start = this.dateToX(new Date(task.planStart));
|
|
514
523
|
const x_plan_end = this.dateToX(DateUtils.addDays(task.planEnd, 1));
|
|
515
524
|
let x_actual_start = null, x_actual_end = null;
|
|
@@ -526,21 +535,17 @@ class GanttChart {
|
|
|
526
535
|
x_plan_end && (offset_x_plan_end = offset_x_plan_start + x_plan_width * percent_plan);
|
|
527
536
|
isValidPlanTask = true;
|
|
528
537
|
}
|
|
529
|
-
|
|
530
|
-
|
|
538
|
+
x_actual_start = this.dateToX(new Date(task.actualStart));
|
|
539
|
+
x_actual_end = this.dateToX(DateUtils.addDays(task.actualEnd ? task.actualEnd : this.today, 1));
|
|
540
|
+
if (x_actual_start && x_actual_end && x_actual_start < x_actual_end) {
|
|
541
|
+
x_actual_width = x_actual_end - x_actual_start;
|
|
542
|
+
offset_x_actual_start = Math.round(x_actual_start + x_actual_width * offsetX_actual);
|
|
543
|
+
offset_x_actual_end = offset_x_actual_start + x_actual_width * percent_actual;
|
|
531
544
|
isValidActualTask = true;
|
|
532
545
|
}
|
|
533
546
|
if (!isValidPlanTask && !isValidActualTask) {
|
|
534
547
|
return;
|
|
535
548
|
}
|
|
536
|
-
if (task.actualEnd) {
|
|
537
|
-
x_actual_end = this.dateToX(DateUtils.addDays(task.actualEnd, 1));
|
|
538
|
-
}
|
|
539
|
-
if (x_actual_start) {
|
|
540
|
-
x_actual_width = (x_actual_end ? x_actual_end : this.dateToX(this.today)) - x_actual_start;
|
|
541
|
-
offset_x_actual_start = Math.round(x_actual_start + x_actual_width * offsetX_actual);
|
|
542
|
-
x_actual_end && (offset_x_actual_end = offset_x_actual_start + x_actual_width * percent_actual);
|
|
543
|
-
}
|
|
544
549
|
this.taskPositions.set(task.id, {
|
|
545
550
|
x_plan_start,
|
|
546
551
|
x_plan_end,
|
|
@@ -852,10 +857,9 @@ class GanttChart {
|
|
|
852
857
|
row.tasks.forEach((task) => {
|
|
853
858
|
const pos = this.taskPositions.get(task.id);
|
|
854
859
|
if (!pos) return;
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
}
|
|
860
|
+
const isPlanVisible = pos.x_plan_end >= this.scrollLeft && pos.x_plan_start <= this.scrollLeft + this.viewportWidth;
|
|
861
|
+
const isActualVisible = pos.x_actual_start && pos.x_actual_end && pos.x_actual_end >= this.scrollLeft && pos.x_actual_start <= this.scrollLeft + this.viewportWidth;
|
|
862
|
+
if (!isPlanVisible && !isActualVisible) return;
|
|
859
863
|
this.drawTask(ctx, task, y, pos);
|
|
860
864
|
});
|
|
861
865
|
}
|
|
@@ -941,7 +945,7 @@ class GanttChart {
|
|
|
941
945
|
ctx.stroke();
|
|
942
946
|
}
|
|
943
947
|
drawToday(ctx) {
|
|
944
|
-
const x = this.dateToX(this.today);
|
|
948
|
+
const x = this.dateToX(DateUtils.addDays(this.today, 1));
|
|
945
949
|
if (x >= this.scrollLeft && x <= this.scrollLeft + this.viewportWidth) {
|
|
946
950
|
ctx.strokeStyle = this.config.todayColor;
|
|
947
951
|
ctx.lineWidth = 1;
|
|
@@ -972,11 +976,11 @@ class GanttChart {
|
|
|
972
976
|
ctx.fillStyle = "#000";
|
|
973
977
|
if (this.config.showLeftRemark && task.leftRemark) {
|
|
974
978
|
ctx.textAlign = "right";
|
|
975
|
-
ctx.fillText(task.leftRemark, Math.round(Math.min(...[pos.offset_x_plan_start, pos.offset_x_actual_start].filter((val) => val !== null && val !== void 0)) - 8), Math.round(textY));
|
|
979
|
+
ctx.fillText(task.leftRemark, Math.round(Math.min(...[pos.offset_x_plan_start, pos.offset_x_actual_start].filter((val) => val !== null && val !== void 0)) - 8 * 2), Math.round(textY));
|
|
976
980
|
}
|
|
977
981
|
if (this.config.showRightRemark && task.rightRemark) {
|
|
978
982
|
ctx.textAlign = "left";
|
|
979
|
-
ctx.fillText(task.rightRemark, Math.round(Math.max(...[pos.offset_x_plan_end, pos.offset_x_actual_end].filter((val) => val !== null && val !== void 0)) + 8), Math.round(textY));
|
|
983
|
+
ctx.fillText(task.rightRemark, Math.round(Math.max(...[pos.offset_x_plan_end, pos.offset_x_actual_end].filter((val) => val !== null && val !== void 0)) + 8 * 2), Math.round(textY));
|
|
980
984
|
}
|
|
981
985
|
if (this.config.showCenterRemark && task.centerRemark) {
|
|
982
986
|
const centerX = pos.offset_x_actual_start + (pos.offset_x_actual_end - pos.offset_x_actual_start) / 2;
|
|
@@ -1090,7 +1094,7 @@ class GanttChart {
|
|
|
1090
1094
|
const startDate = date ? date : this.minDate;
|
|
1091
1095
|
if (startDate) {
|
|
1092
1096
|
const xPosition = this.dateToX(startDate);
|
|
1093
|
-
this.container.scrollTo({ left: xPosition - 80 });
|
|
1097
|
+
this.container.scrollTo({ left: Math.max(this.config.scrollEdgeThresholds + 2, xPosition - 80) });
|
|
1094
1098
|
}
|
|
1095
1099
|
}
|
|
1096
1100
|
/**
|
|
@@ -1102,7 +1106,7 @@ class GanttChart {
|
|
|
1102
1106
|
if (params && (params.rowId || params.rowIndex)) {
|
|
1103
1107
|
const rowIndex = params.rowIndex ? params.rowIndex : this.data.findIndex((row) => row.id === params.rowId);
|
|
1104
1108
|
const yPosition = this.config.rowHeight * rowIndex;
|
|
1105
|
-
this.container.scrollTo({ top: yPosition - 80 });
|
|
1109
|
+
this.container.scrollTo({ top: Math.max(this.config.scrollEdgeThresholds + 2, yPosition - 80) });
|
|
1106
1110
|
}
|
|
1107
1111
|
}
|
|
1108
1112
|
}
|
package/dist/index.css
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -20,10 +20,28 @@ export declare class DateUtils {
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function firstValidValue(...args: any[]): any;
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* GanttChart Class
|
|
25
|
+
*
|
|
26
|
+
* Core Features:
|
|
27
|
+
* 1. Virtual Rendering: Only renders tasks visible in the current viewport for performance optimization
|
|
28
|
+
* 2. Dynamic Scrolling: Supports both horizontal and vertical scrolling with virtualized data loading
|
|
29
|
+
* 3. Dual Timeline Display: Shows both planned and actual timelines for tasks
|
|
30
|
+
* 4. Data Validation Handling:
|
|
31
|
+
* - Automatically handles invalid data where start dates are after end dates by skipping rendering
|
|
32
|
+
* - For tasks with only actual start time (no end time), automatically extends visualization to today
|
|
33
|
+
* - Filters out tasks with completely invalid date ranges
|
|
34
|
+
* 5. Dependency Visualization: Draws arrows between dependent tasks with smart routing
|
|
35
|
+
* 6. Infinite Scroll Loading: Supports dynamic data loading when scrolling to edges
|
|
36
|
+
* 7. Responsive Design: Adapts to container size changes using ResizeObserver
|
|
37
|
+
* 8. Tooltip System: Provides detailed information on hover with customizable formatting
|
|
38
|
+
* 9. Multiple View Modes: Supports Day/Week/Month/Year views with appropriate scaling
|
|
39
|
+
* 10. Custom Styling: Allows per-task styling through configuration options
|
|
40
|
+
*/
|
|
23
41
|
export declare class GanttChart {
|
|
24
42
|
private rootContainer;
|
|
25
43
|
container: HTMLElement;
|
|
26
|
-
|
|
44
|
+
data: GanttData;
|
|
27
45
|
private config;
|
|
28
46
|
private headerCanvas;
|
|
29
47
|
private mainCanvas;
|
|
@@ -49,7 +67,10 @@ export declare class GanttChart {
|
|
|
49
67
|
private totalHeight;
|
|
50
68
|
private resizeObserver;
|
|
51
69
|
private taskPositions;
|
|
52
|
-
|
|
70
|
+
taskMap: Map<string, {
|
|
71
|
+
row: number;
|
|
72
|
+
task: Task;
|
|
73
|
+
}>;
|
|
53
74
|
private isLoadingData;
|
|
54
75
|
private hasMoreDataLeft;
|
|
55
76
|
private hasMoreDataRight;
|
|
@@ -160,13 +181,14 @@ export declare type LoadMoreDirection = 'left' | 'right' | 'bottom';
|
|
|
160
181
|
export declare interface Row {
|
|
161
182
|
id: string;
|
|
162
183
|
name: string;
|
|
184
|
+
hide?: boolean;
|
|
163
185
|
tasks: Task[];
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
export declare interface Task {
|
|
167
189
|
id: string;
|
|
168
190
|
name: string;
|
|
169
|
-
type?: 'task' | 'leave';
|
|
191
|
+
type?: 'task' | 'leave' | 'overtime' | string;
|
|
170
192
|
planStart?: string;
|
|
171
193
|
planEnd?: string;
|
|
172
194
|
actualStart?: string;
|
|
@@ -178,6 +200,7 @@ export declare interface Task {
|
|
|
178
200
|
styleClass?: string;
|
|
179
201
|
planBorderColor?: string;
|
|
180
202
|
actualBgColor?: string;
|
|
203
|
+
hide?: boolean;
|
|
181
204
|
_data?: any;
|
|
182
205
|
planOffsetPercent?: [number, number];
|
|
183
206
|
actualOffsetPercent?: [number, number];
|
package/dist/index.es.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* gantt-canvas-chart v1.
|
|
2
|
+
* gantt-canvas-chart v1.5.1
|
|
3
3
|
* (c) 2025-present chandq
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -203,8 +203,8 @@ class GanttChart {
|
|
|
203
203
|
}
|
|
204
204
|
init() {
|
|
205
205
|
this.buildTaskMap();
|
|
206
|
-
this.calculateFullTimeline();
|
|
207
206
|
this.updatePixelsPerDay();
|
|
207
|
+
this.calculateFullTimeline();
|
|
208
208
|
this.setupEvents();
|
|
209
209
|
this.handleResize();
|
|
210
210
|
}
|
|
@@ -235,8 +235,8 @@ class GanttChart {
|
|
|
235
235
|
updateConfig(newConfig) {
|
|
236
236
|
Object.assign(this.config, newConfig);
|
|
237
237
|
if (newConfig.viewMode) {
|
|
238
|
-
this.container.scrollLeft =
|
|
239
|
-
this.scrollLeft =
|
|
238
|
+
this.container.scrollLeft = this.config.scrollEdgeThresholds + 2;
|
|
239
|
+
this.scrollLeft = this.config.scrollEdgeThresholds + 2;
|
|
240
240
|
this.updatePixelsPerDay();
|
|
241
241
|
this.calculateFullTimeline();
|
|
242
242
|
}
|
|
@@ -276,6 +276,9 @@ class GanttChart {
|
|
|
276
276
|
maxDate = DateUtils.addDays(/* @__PURE__ */ new Date(), 60);
|
|
277
277
|
} else {
|
|
278
278
|
this.taskMap.forEach(({ task }) => {
|
|
279
|
+
if (task.hide) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
279
282
|
const pStart = new Date(task.planStart);
|
|
280
283
|
const pEnd = new Date(task.planEnd);
|
|
281
284
|
if (pStart < minDate) minDate = pStart;
|
|
@@ -309,8 +312,8 @@ class GanttChart {
|
|
|
309
312
|
break;
|
|
310
313
|
case "Day":
|
|
311
314
|
default:
|
|
312
|
-
this.timelineStart = DateUtils.addDays(minDate,
|
|
313
|
-
this.timelineEnd = DateUtils.addDays(maxDate,
|
|
315
|
+
this.timelineStart = DateUtils.addDays(minDate, 3);
|
|
316
|
+
this.timelineEnd = DateUtils.addDays(maxDate, -5);
|
|
314
317
|
break;
|
|
315
318
|
}
|
|
316
319
|
}
|
|
@@ -476,7 +479,7 @@ class GanttChart {
|
|
|
476
479
|
if (this.scrollTop !== scrollTop) this.container.scrollTop = scrollTop;
|
|
477
480
|
}
|
|
478
481
|
updateVirtualRanges() {
|
|
479
|
-
const buffer =
|
|
482
|
+
const buffer = 100;
|
|
480
483
|
this.visibleDateRange = {
|
|
481
484
|
start: this.xToDate(this.scrollLeft - buffer),
|
|
482
485
|
end: this.xToDate(this.scrollLeft + this.viewportWidth + buffer)
|
|
@@ -506,8 +509,14 @@ class GanttChart {
|
|
|
506
509
|
this.taskPositions.clear();
|
|
507
510
|
for (let i = 0; i < this.data.length; i++) {
|
|
508
511
|
const row = this.data[i];
|
|
512
|
+
if (row.hide) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
509
515
|
const y = i * this.config.rowHeight;
|
|
510
516
|
row.tasks.forEach((task) => {
|
|
517
|
+
if (task.hide) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
511
520
|
const x_plan_start = this.dateToX(new Date(task.planStart));
|
|
512
521
|
const x_plan_end = this.dateToX(DateUtils.addDays(task.planEnd, 1));
|
|
513
522
|
let x_actual_start = null, x_actual_end = null;
|
|
@@ -524,21 +533,17 @@ class GanttChart {
|
|
|
524
533
|
x_plan_end && (offset_x_plan_end = offset_x_plan_start + x_plan_width * percent_plan);
|
|
525
534
|
isValidPlanTask = true;
|
|
526
535
|
}
|
|
527
|
-
|
|
528
|
-
|
|
536
|
+
x_actual_start = this.dateToX(new Date(task.actualStart));
|
|
537
|
+
x_actual_end = this.dateToX(DateUtils.addDays(task.actualEnd ? task.actualEnd : this.today, 1));
|
|
538
|
+
if (x_actual_start && x_actual_end && x_actual_start < x_actual_end) {
|
|
539
|
+
x_actual_width = x_actual_end - x_actual_start;
|
|
540
|
+
offset_x_actual_start = Math.round(x_actual_start + x_actual_width * offsetX_actual);
|
|
541
|
+
offset_x_actual_end = offset_x_actual_start + x_actual_width * percent_actual;
|
|
529
542
|
isValidActualTask = true;
|
|
530
543
|
}
|
|
531
544
|
if (!isValidPlanTask && !isValidActualTask) {
|
|
532
545
|
return;
|
|
533
546
|
}
|
|
534
|
-
if (task.actualEnd) {
|
|
535
|
-
x_actual_end = this.dateToX(DateUtils.addDays(task.actualEnd, 1));
|
|
536
|
-
}
|
|
537
|
-
if (x_actual_start) {
|
|
538
|
-
x_actual_width = (x_actual_end ? x_actual_end : this.dateToX(this.today)) - x_actual_start;
|
|
539
|
-
offset_x_actual_start = Math.round(x_actual_start + x_actual_width * offsetX_actual);
|
|
540
|
-
x_actual_end && (offset_x_actual_end = offset_x_actual_start + x_actual_width * percent_actual);
|
|
541
|
-
}
|
|
542
547
|
this.taskPositions.set(task.id, {
|
|
543
548
|
x_plan_start,
|
|
544
549
|
x_plan_end,
|
|
@@ -850,10 +855,9 @@ class GanttChart {
|
|
|
850
855
|
row.tasks.forEach((task) => {
|
|
851
856
|
const pos = this.taskPositions.get(task.id);
|
|
852
857
|
if (!pos) return;
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
}
|
|
858
|
+
const isPlanVisible = pos.x_plan_end >= this.scrollLeft && pos.x_plan_start <= this.scrollLeft + this.viewportWidth;
|
|
859
|
+
const isActualVisible = pos.x_actual_start && pos.x_actual_end && pos.x_actual_end >= this.scrollLeft && pos.x_actual_start <= this.scrollLeft + this.viewportWidth;
|
|
860
|
+
if (!isPlanVisible && !isActualVisible) return;
|
|
857
861
|
this.drawTask(ctx, task, y, pos);
|
|
858
862
|
});
|
|
859
863
|
}
|
|
@@ -939,7 +943,7 @@ class GanttChart {
|
|
|
939
943
|
ctx.stroke();
|
|
940
944
|
}
|
|
941
945
|
drawToday(ctx) {
|
|
942
|
-
const x = this.dateToX(this.today);
|
|
946
|
+
const x = this.dateToX(DateUtils.addDays(this.today, 1));
|
|
943
947
|
if (x >= this.scrollLeft && x <= this.scrollLeft + this.viewportWidth) {
|
|
944
948
|
ctx.strokeStyle = this.config.todayColor;
|
|
945
949
|
ctx.lineWidth = 1;
|
|
@@ -970,11 +974,11 @@ class GanttChart {
|
|
|
970
974
|
ctx.fillStyle = "#000";
|
|
971
975
|
if (this.config.showLeftRemark && task.leftRemark) {
|
|
972
976
|
ctx.textAlign = "right";
|
|
973
|
-
ctx.fillText(task.leftRemark, Math.round(Math.min(...[pos.offset_x_plan_start, pos.offset_x_actual_start].filter((val) => val !== null && val !== void 0)) - 8), Math.round(textY));
|
|
977
|
+
ctx.fillText(task.leftRemark, Math.round(Math.min(...[pos.offset_x_plan_start, pos.offset_x_actual_start].filter((val) => val !== null && val !== void 0)) - 8 * 2), Math.round(textY));
|
|
974
978
|
}
|
|
975
979
|
if (this.config.showRightRemark && task.rightRemark) {
|
|
976
980
|
ctx.textAlign = "left";
|
|
977
|
-
ctx.fillText(task.rightRemark, Math.round(Math.max(...[pos.offset_x_plan_end, pos.offset_x_actual_end].filter((val) => val !== null && val !== void 0)) + 8), Math.round(textY));
|
|
981
|
+
ctx.fillText(task.rightRemark, Math.round(Math.max(...[pos.offset_x_plan_end, pos.offset_x_actual_end].filter((val) => val !== null && val !== void 0)) + 8 * 2), Math.round(textY));
|
|
978
982
|
}
|
|
979
983
|
if (this.config.showCenterRemark && task.centerRemark) {
|
|
980
984
|
const centerX = pos.offset_x_actual_start + (pos.offset_x_actual_end - pos.offset_x_actual_start) / 2;
|
|
@@ -1088,7 +1092,7 @@ class GanttChart {
|
|
|
1088
1092
|
const startDate = date ? date : this.minDate;
|
|
1089
1093
|
if (startDate) {
|
|
1090
1094
|
const xPosition = this.dateToX(startDate);
|
|
1091
|
-
this.container.scrollTo({ left: xPosition - 80 });
|
|
1095
|
+
this.container.scrollTo({ left: Math.max(this.config.scrollEdgeThresholds + 2, xPosition - 80) });
|
|
1092
1096
|
}
|
|
1093
1097
|
}
|
|
1094
1098
|
/**
|
|
@@ -1100,7 +1104,7 @@ class GanttChart {
|
|
|
1100
1104
|
if (params && (params.rowId || params.rowIndex)) {
|
|
1101
1105
|
const rowIndex = params.rowIndex ? params.rowIndex : this.data.findIndex((row) => row.id === params.rowId);
|
|
1102
1106
|
const yPosition = this.config.rowHeight * rowIndex;
|
|
1103
|
-
this.container.scrollTo({ top: yPosition - 80 });
|
|
1107
|
+
this.container.scrollTo({ top: Math.max(this.config.scrollEdgeThresholds + 2, yPosition - 80) });
|
|
1104
1108
|
}
|
|
1105
1109
|
}
|
|
1106
1110
|
}
|
package/dist/index.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* gantt-canvas-chart v1.
|
|
2
|
+
* gantt-canvas-chart v1.5.1
|
|
3
3
|
* (c) 2025-present chandq
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -207,8 +207,8 @@
|
|
|
207
207
|
}
|
|
208
208
|
init() {
|
|
209
209
|
this.buildTaskMap();
|
|
210
|
-
this.calculateFullTimeline();
|
|
211
210
|
this.updatePixelsPerDay();
|
|
211
|
+
this.calculateFullTimeline();
|
|
212
212
|
this.setupEvents();
|
|
213
213
|
this.handleResize();
|
|
214
214
|
}
|
|
@@ -239,8 +239,8 @@
|
|
|
239
239
|
updateConfig(newConfig) {
|
|
240
240
|
Object.assign(this.config, newConfig);
|
|
241
241
|
if (newConfig.viewMode) {
|
|
242
|
-
this.container.scrollLeft =
|
|
243
|
-
this.scrollLeft =
|
|
242
|
+
this.container.scrollLeft = this.config.scrollEdgeThresholds + 2;
|
|
243
|
+
this.scrollLeft = this.config.scrollEdgeThresholds + 2;
|
|
244
244
|
this.updatePixelsPerDay();
|
|
245
245
|
this.calculateFullTimeline();
|
|
246
246
|
}
|
|
@@ -280,6 +280,9 @@
|
|
|
280
280
|
maxDate = DateUtils.addDays(/* @__PURE__ */ new Date(), 60);
|
|
281
281
|
} else {
|
|
282
282
|
this.taskMap.forEach(({ task }) => {
|
|
283
|
+
if (task.hide) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
283
286
|
const pStart = new Date(task.planStart);
|
|
284
287
|
const pEnd = new Date(task.planEnd);
|
|
285
288
|
if (pStart < minDate) minDate = pStart;
|
|
@@ -313,8 +316,8 @@
|
|
|
313
316
|
break;
|
|
314
317
|
case "Day":
|
|
315
318
|
default:
|
|
316
|
-
this.timelineStart = DateUtils.addDays(minDate,
|
|
317
|
-
this.timelineEnd = DateUtils.addDays(maxDate,
|
|
319
|
+
this.timelineStart = DateUtils.addDays(minDate, 3);
|
|
320
|
+
this.timelineEnd = DateUtils.addDays(maxDate, -5);
|
|
318
321
|
break;
|
|
319
322
|
}
|
|
320
323
|
}
|
|
@@ -480,7 +483,7 @@
|
|
|
480
483
|
if (this.scrollTop !== scrollTop) this.container.scrollTop = scrollTop;
|
|
481
484
|
}
|
|
482
485
|
updateVirtualRanges() {
|
|
483
|
-
const buffer =
|
|
486
|
+
const buffer = 100;
|
|
484
487
|
this.visibleDateRange = {
|
|
485
488
|
start: this.xToDate(this.scrollLeft - buffer),
|
|
486
489
|
end: this.xToDate(this.scrollLeft + this.viewportWidth + buffer)
|
|
@@ -510,8 +513,14 @@
|
|
|
510
513
|
this.taskPositions.clear();
|
|
511
514
|
for (let i = 0; i < this.data.length; i++) {
|
|
512
515
|
const row = this.data[i];
|
|
516
|
+
if (row.hide) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
513
519
|
const y = i * this.config.rowHeight;
|
|
514
520
|
row.tasks.forEach((task) => {
|
|
521
|
+
if (task.hide) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
515
524
|
const x_plan_start = this.dateToX(new Date(task.planStart));
|
|
516
525
|
const x_plan_end = this.dateToX(DateUtils.addDays(task.planEnd, 1));
|
|
517
526
|
let x_actual_start = null, x_actual_end = null;
|
|
@@ -528,21 +537,17 @@
|
|
|
528
537
|
x_plan_end && (offset_x_plan_end = offset_x_plan_start + x_plan_width * percent_plan);
|
|
529
538
|
isValidPlanTask = true;
|
|
530
539
|
}
|
|
531
|
-
|
|
532
|
-
|
|
540
|
+
x_actual_start = this.dateToX(new Date(task.actualStart));
|
|
541
|
+
x_actual_end = this.dateToX(DateUtils.addDays(task.actualEnd ? task.actualEnd : this.today, 1));
|
|
542
|
+
if (x_actual_start && x_actual_end && x_actual_start < x_actual_end) {
|
|
543
|
+
x_actual_width = x_actual_end - x_actual_start;
|
|
544
|
+
offset_x_actual_start = Math.round(x_actual_start + x_actual_width * offsetX_actual);
|
|
545
|
+
offset_x_actual_end = offset_x_actual_start + x_actual_width * percent_actual;
|
|
533
546
|
isValidActualTask = true;
|
|
534
547
|
}
|
|
535
548
|
if (!isValidPlanTask && !isValidActualTask) {
|
|
536
549
|
return;
|
|
537
550
|
}
|
|
538
|
-
if (task.actualEnd) {
|
|
539
|
-
x_actual_end = this.dateToX(DateUtils.addDays(task.actualEnd, 1));
|
|
540
|
-
}
|
|
541
|
-
if (x_actual_start) {
|
|
542
|
-
x_actual_width = (x_actual_end ? x_actual_end : this.dateToX(this.today)) - x_actual_start;
|
|
543
|
-
offset_x_actual_start = Math.round(x_actual_start + x_actual_width * offsetX_actual);
|
|
544
|
-
x_actual_end && (offset_x_actual_end = offset_x_actual_start + x_actual_width * percent_actual);
|
|
545
|
-
}
|
|
546
551
|
this.taskPositions.set(task.id, {
|
|
547
552
|
x_plan_start,
|
|
548
553
|
x_plan_end,
|
|
@@ -854,10 +859,9 @@
|
|
|
854
859
|
row.tasks.forEach((task) => {
|
|
855
860
|
const pos = this.taskPositions.get(task.id);
|
|
856
861
|
if (!pos) return;
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
}
|
|
862
|
+
const isPlanVisible = pos.x_plan_end >= this.scrollLeft && pos.x_plan_start <= this.scrollLeft + this.viewportWidth;
|
|
863
|
+
const isActualVisible = pos.x_actual_start && pos.x_actual_end && pos.x_actual_end >= this.scrollLeft && pos.x_actual_start <= this.scrollLeft + this.viewportWidth;
|
|
864
|
+
if (!isPlanVisible && !isActualVisible) return;
|
|
861
865
|
this.drawTask(ctx, task, y, pos);
|
|
862
866
|
});
|
|
863
867
|
}
|
|
@@ -943,7 +947,7 @@
|
|
|
943
947
|
ctx.stroke();
|
|
944
948
|
}
|
|
945
949
|
drawToday(ctx) {
|
|
946
|
-
const x = this.dateToX(this.today);
|
|
950
|
+
const x = this.dateToX(DateUtils.addDays(this.today, 1));
|
|
947
951
|
if (x >= this.scrollLeft && x <= this.scrollLeft + this.viewportWidth) {
|
|
948
952
|
ctx.strokeStyle = this.config.todayColor;
|
|
949
953
|
ctx.lineWidth = 1;
|
|
@@ -974,11 +978,11 @@
|
|
|
974
978
|
ctx.fillStyle = "#000";
|
|
975
979
|
if (this.config.showLeftRemark && task.leftRemark) {
|
|
976
980
|
ctx.textAlign = "right";
|
|
977
|
-
ctx.fillText(task.leftRemark, Math.round(Math.min(...[pos.offset_x_plan_start, pos.offset_x_actual_start].filter((val) => val !== null && val !== void 0)) - 8), Math.round(textY));
|
|
981
|
+
ctx.fillText(task.leftRemark, Math.round(Math.min(...[pos.offset_x_plan_start, pos.offset_x_actual_start].filter((val) => val !== null && val !== void 0)) - 8 * 2), Math.round(textY));
|
|
978
982
|
}
|
|
979
983
|
if (this.config.showRightRemark && task.rightRemark) {
|
|
980
984
|
ctx.textAlign = "left";
|
|
981
|
-
ctx.fillText(task.rightRemark, Math.round(Math.max(...[pos.offset_x_plan_end, pos.offset_x_actual_end].filter((val) => val !== null && val !== void 0)) + 8), Math.round(textY));
|
|
985
|
+
ctx.fillText(task.rightRemark, Math.round(Math.max(...[pos.offset_x_plan_end, pos.offset_x_actual_end].filter((val) => val !== null && val !== void 0)) + 8 * 2), Math.round(textY));
|
|
982
986
|
}
|
|
983
987
|
if (this.config.showCenterRemark && task.centerRemark) {
|
|
984
988
|
const centerX = pos.offset_x_actual_start + (pos.offset_x_actual_end - pos.offset_x_actual_start) / 2;
|
|
@@ -1092,7 +1096,7 @@
|
|
|
1092
1096
|
const startDate = date ? date : this.minDate;
|
|
1093
1097
|
if (startDate) {
|
|
1094
1098
|
const xPosition = this.dateToX(startDate);
|
|
1095
|
-
this.container.scrollTo({ left: xPosition - 80 });
|
|
1099
|
+
this.container.scrollTo({ left: Math.max(this.config.scrollEdgeThresholds + 2, xPosition - 80) });
|
|
1096
1100
|
}
|
|
1097
1101
|
}
|
|
1098
1102
|
/**
|
|
@@ -1104,7 +1108,7 @@
|
|
|
1104
1108
|
if (params && (params.rowId || params.rowIndex)) {
|
|
1105
1109
|
const rowIndex = params.rowIndex ? params.rowIndex : this.data.findIndex((row) => row.id === params.rowId);
|
|
1106
1110
|
const yPosition = this.config.rowHeight * rowIndex;
|
|
1107
|
-
this.container.scrollTo({ top: yPosition - 80 });
|
|
1111
|
+
this.container.scrollTo({ top: Math.max(this.config.scrollEdgeThresholds + 2, yPosition - 80) });
|
|
1108
1112
|
}
|
|
1109
1113
|
}
|
|
1110
1114
|
}
|