gantt-canvas-chart 1.3.1 → 1.5.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/dist/index.cjs.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.3.1
2
+ * gantt-canvas-chart v1.5.0
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -121,6 +121,9 @@ class GanttChart {
121
121
  onDataLoad = null;
122
122
  scrollLoadTimer = null;
123
123
  constructor(rootContainer, data, config = {}) {
124
+ if (rootContainer.querySelector(".__gantt-chart-container")) {
125
+ throw new Error("GanttChart already exists in this container");
126
+ }
124
127
  const container = document.createElement("div");
125
128
  const scrollEl = document.createElement("div");
126
129
  const headerCanvas = document.createElement("canvas");
@@ -157,6 +160,7 @@ class GanttChart {
157
160
  todayColor: "#ff4d4f",
158
161
  offsetTop: 0,
159
162
  offsetLeft: 0,
163
+ scrollEdgeThresholds: 10,
160
164
  enabledLoadMore: [],
161
165
  viewFactors: { Day: 80, Week: 20, Month: 15, Year: 6 },
162
166
  planBorderColor: "#C1EFCF",
@@ -201,8 +205,8 @@ class GanttChart {
201
205
  }
202
206
  init() {
203
207
  this.buildTaskMap();
204
- this.calculateFullTimeline();
205
208
  this.updatePixelsPerDay();
209
+ this.calculateFullTimeline();
206
210
  this.setupEvents();
207
211
  this.handleResize();
208
212
  }
@@ -274,6 +278,9 @@ class GanttChart {
274
278
  maxDate = DateUtils.addDays(/* @__PURE__ */ new Date(), 60);
275
279
  } else {
276
280
  this.taskMap.forEach(({ task }) => {
281
+ if (task.hide) {
282
+ return;
283
+ }
277
284
  const pStart = new Date(task.planStart);
278
285
  const pEnd = new Date(task.planEnd);
279
286
  if (pStart < minDate) minDate = pStart;
@@ -307,8 +314,8 @@ class GanttChart {
307
314
  break;
308
315
  case "Day":
309
316
  default:
310
- this.timelineStart = DateUtils.addDays(minDate, -3);
311
- this.timelineEnd = DateUtils.addDays(maxDate, 3);
317
+ this.timelineStart = DateUtils.addDays(minDate, 3);
318
+ this.timelineEnd = DateUtils.addDays(maxDate, -5);
312
319
  break;
313
320
  }
314
321
  }
@@ -372,9 +379,10 @@ class GanttChart {
372
379
  const viewportHeight = this.viewportHeight;
373
380
  const totalWidth = this.totalWidth;
374
381
  const totalHeight = this.totalHeight;
375
- const atLeftEdge = scrollLeft <= 5;
376
- const atRightEdge = scrollLeft + viewportWidth >= totalWidth - 5;
377
- const atBottomEdge = scrollTop + viewportHeight >= totalHeight - 5;
382
+ const thresholds = this.config.scrollEdgeThresholds;
383
+ const atLeftEdge = scrollLeft <= thresholds;
384
+ const atRightEdge = scrollLeft + viewportWidth >= totalWidth - thresholds;
385
+ const atBottomEdge = scrollTop + viewportHeight >= totalHeight - thresholds;
378
386
  try {
379
387
  if (this.hasMoreDataLeft && atLeftEdge && scrollLeft < this.lastScrollLeft) {
380
388
  await this.loadMoreData("left");
@@ -390,9 +398,7 @@ class GanttChart {
390
398
  }
391
399
  // Add this method to reset scroll loading state
392
400
  resetScrollLoadingState() {
393
- this.hasMoreDataLeft = true;
394
- this.hasMoreDataRight = true;
395
- this.hasMoreDataBottom = true;
401
+ this.updateLoadMoreConf();
396
402
  this.lastScrollLeft = 0;
397
403
  this.lastScrollTop = 0;
398
404
  if (this.scrollLoadTimer !== null) {
@@ -475,7 +481,7 @@ class GanttChart {
475
481
  if (this.scrollTop !== scrollTop) this.container.scrollTop = scrollTop;
476
482
  }
477
483
  updateVirtualRanges() {
478
- const buffer = 200;
484
+ const buffer = 100;
479
485
  this.visibleDateRange = {
480
486
  start: this.xToDate(this.scrollLeft - buffer),
481
487
  end: this.xToDate(this.scrollLeft + this.viewportWidth + buffer)
@@ -505,8 +511,14 @@ class GanttChart {
505
511
  this.taskPositions.clear();
506
512
  for (let i = 0; i < this.data.length; i++) {
507
513
  const row = this.data[i];
514
+ if (row.hide) {
515
+ continue;
516
+ }
508
517
  const y = i * this.config.rowHeight;
509
518
  row.tasks.forEach((task) => {
519
+ if (task.hide) {
520
+ return;
521
+ }
510
522
  const x_plan_start = this.dateToX(new Date(task.planStart));
511
523
  const x_plan_end = this.dateToX(DateUtils.addDays(task.planEnd, 1));
512
524
  let x_actual_start = null, x_actual_end = null;
@@ -514,19 +526,25 @@ class GanttChart {
514
526
  let offset_x_actual_start = null, offset_x_actual_end = null;
515
527
  let x_plan_width = 0;
516
528
  let x_actual_width = 0;
529
+ let isValidPlanTask = false, isValidActualTask = false;
517
530
  const [offsetX_actual, percent_actual] = this.config.viewMode === "Day" && task.actualOffsetPercent ? task.actualOffsetPercent : [0, 1];
518
531
  const [offsetX, percent_plan] = this.config.viewMode === "Day" && task.planOffsetPercent ? task.planOffsetPercent : [0, 1];
519
- if (x_plan_start && x_plan_end) {
532
+ if (x_plan_start && x_plan_end && x_plan_start < x_plan_end) {
520
533
  x_plan_width = x_plan_end - x_plan_start;
521
534
  offset_x_plan_start = x_plan_start + x_plan_width * offsetX;
522
535
  x_plan_end && (offset_x_plan_end = offset_x_plan_start + x_plan_width * percent_plan);
536
+ isValidPlanTask = true;
523
537
  }
524
538
  if (task.actualStart) {
525
539
  x_actual_start = this.dateToX(new Date(task.actualStart));
540
+ isValidActualTask = true;
526
541
  }
527
542
  if (task.actualEnd) {
528
543
  x_actual_end = this.dateToX(DateUtils.addDays(task.actualEnd, 1));
529
544
  }
545
+ if (!isValidPlanTask && !isValidActualTask) {
546
+ return;
547
+ }
530
548
  if (x_actual_start) {
531
549
  x_actual_width = (x_actual_end ? x_actual_end : this.dateToX(this.today)) - x_actual_start;
532
550
  offset_x_actual_start = Math.round(x_actual_start + x_actual_width * offsetX_actual);
package/dist/index.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.3.1
2
+ * gantt-canvas-chart v1.5.0
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
package/dist/index.d.ts CHANGED
@@ -23,7 +23,7 @@ export declare function firstValidValue(...args: any[]): any;
23
23
  export declare class GanttChart {
24
24
  private rootContainer;
25
25
  container: HTMLElement;
26
- private data;
26
+ data: GanttData;
27
27
  private config;
28
28
  private headerCanvas;
29
29
  private mainCanvas;
@@ -142,6 +142,7 @@ export declare interface GanttConfig {
142
142
  todayColor?: string;
143
143
  offsetTop?: number;
144
144
  offsetLeft?: number;
145
+ scrollEdgeThresholds?: number;
145
146
  enabledLoadMore?: [LoadMoreDirection?, LoadMoreDirection?, LoadMoreDirection?];
146
147
  viewFactors?: {
147
148
  Day: number;
@@ -159,13 +160,14 @@ export declare type LoadMoreDirection = 'left' | 'right' | 'bottom';
159
160
  export declare interface Row {
160
161
  id: string;
161
162
  name: string;
163
+ hide?: boolean;
162
164
  tasks: Task[];
163
165
  }
164
166
 
165
167
  export declare interface Task {
166
168
  id: string;
167
169
  name: string;
168
- type?: 'task' | 'leave';
170
+ type?: 'task' | 'leave' | 'overtime' | string;
169
171
  planStart?: string;
170
172
  planEnd?: string;
171
173
  actualStart?: string;
@@ -177,6 +179,7 @@ export declare interface Task {
177
179
  styleClass?: string;
178
180
  planBorderColor?: string;
179
181
  actualBgColor?: string;
182
+ hide?: boolean;
180
183
  _data?: any;
181
184
  planOffsetPercent?: [number, number];
182
185
  actualOffsetPercent?: [number, number];
package/dist/index.es.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.3.1
2
+ * gantt-canvas-chart v1.5.0
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -119,6 +119,9 @@ class GanttChart {
119
119
  onDataLoad = null;
120
120
  scrollLoadTimer = null;
121
121
  constructor(rootContainer, data, config = {}) {
122
+ if (rootContainer.querySelector(".__gantt-chart-container")) {
123
+ throw new Error("GanttChart already exists in this container");
124
+ }
122
125
  const container = document.createElement("div");
123
126
  const scrollEl = document.createElement("div");
124
127
  const headerCanvas = document.createElement("canvas");
@@ -155,6 +158,7 @@ class GanttChart {
155
158
  todayColor: "#ff4d4f",
156
159
  offsetTop: 0,
157
160
  offsetLeft: 0,
161
+ scrollEdgeThresholds: 10,
158
162
  enabledLoadMore: [],
159
163
  viewFactors: { Day: 80, Week: 20, Month: 15, Year: 6 },
160
164
  planBorderColor: "#C1EFCF",
@@ -199,8 +203,8 @@ class GanttChart {
199
203
  }
200
204
  init() {
201
205
  this.buildTaskMap();
202
- this.calculateFullTimeline();
203
206
  this.updatePixelsPerDay();
207
+ this.calculateFullTimeline();
204
208
  this.setupEvents();
205
209
  this.handleResize();
206
210
  }
@@ -272,6 +276,9 @@ class GanttChart {
272
276
  maxDate = DateUtils.addDays(/* @__PURE__ */ new Date(), 60);
273
277
  } else {
274
278
  this.taskMap.forEach(({ task }) => {
279
+ if (task.hide) {
280
+ return;
281
+ }
275
282
  const pStart = new Date(task.planStart);
276
283
  const pEnd = new Date(task.planEnd);
277
284
  if (pStart < minDate) minDate = pStart;
@@ -305,8 +312,8 @@ class GanttChart {
305
312
  break;
306
313
  case "Day":
307
314
  default:
308
- this.timelineStart = DateUtils.addDays(minDate, -3);
309
- this.timelineEnd = DateUtils.addDays(maxDate, 3);
315
+ this.timelineStart = DateUtils.addDays(minDate, 3);
316
+ this.timelineEnd = DateUtils.addDays(maxDate, -5);
310
317
  break;
311
318
  }
312
319
  }
@@ -370,9 +377,10 @@ class GanttChart {
370
377
  const viewportHeight = this.viewportHeight;
371
378
  const totalWidth = this.totalWidth;
372
379
  const totalHeight = this.totalHeight;
373
- const atLeftEdge = scrollLeft <= 5;
374
- const atRightEdge = scrollLeft + viewportWidth >= totalWidth - 5;
375
- const atBottomEdge = scrollTop + viewportHeight >= totalHeight - 5;
380
+ const thresholds = this.config.scrollEdgeThresholds;
381
+ const atLeftEdge = scrollLeft <= thresholds;
382
+ const atRightEdge = scrollLeft + viewportWidth >= totalWidth - thresholds;
383
+ const atBottomEdge = scrollTop + viewportHeight >= totalHeight - thresholds;
376
384
  try {
377
385
  if (this.hasMoreDataLeft && atLeftEdge && scrollLeft < this.lastScrollLeft) {
378
386
  await this.loadMoreData("left");
@@ -388,9 +396,7 @@ class GanttChart {
388
396
  }
389
397
  // Add this method to reset scroll loading state
390
398
  resetScrollLoadingState() {
391
- this.hasMoreDataLeft = true;
392
- this.hasMoreDataRight = true;
393
- this.hasMoreDataBottom = true;
399
+ this.updateLoadMoreConf();
394
400
  this.lastScrollLeft = 0;
395
401
  this.lastScrollTop = 0;
396
402
  if (this.scrollLoadTimer !== null) {
@@ -473,7 +479,7 @@ class GanttChart {
473
479
  if (this.scrollTop !== scrollTop) this.container.scrollTop = scrollTop;
474
480
  }
475
481
  updateVirtualRanges() {
476
- const buffer = 200;
482
+ const buffer = 100;
477
483
  this.visibleDateRange = {
478
484
  start: this.xToDate(this.scrollLeft - buffer),
479
485
  end: this.xToDate(this.scrollLeft + this.viewportWidth + buffer)
@@ -503,8 +509,14 @@ class GanttChart {
503
509
  this.taskPositions.clear();
504
510
  for (let i = 0; i < this.data.length; i++) {
505
511
  const row = this.data[i];
512
+ if (row.hide) {
513
+ continue;
514
+ }
506
515
  const y = i * this.config.rowHeight;
507
516
  row.tasks.forEach((task) => {
517
+ if (task.hide) {
518
+ return;
519
+ }
508
520
  const x_plan_start = this.dateToX(new Date(task.planStart));
509
521
  const x_plan_end = this.dateToX(DateUtils.addDays(task.planEnd, 1));
510
522
  let x_actual_start = null, x_actual_end = null;
@@ -512,19 +524,25 @@ class GanttChart {
512
524
  let offset_x_actual_start = null, offset_x_actual_end = null;
513
525
  let x_plan_width = 0;
514
526
  let x_actual_width = 0;
527
+ let isValidPlanTask = false, isValidActualTask = false;
515
528
  const [offsetX_actual, percent_actual] = this.config.viewMode === "Day" && task.actualOffsetPercent ? task.actualOffsetPercent : [0, 1];
516
529
  const [offsetX, percent_plan] = this.config.viewMode === "Day" && task.planOffsetPercent ? task.planOffsetPercent : [0, 1];
517
- if (x_plan_start && x_plan_end) {
530
+ if (x_plan_start && x_plan_end && x_plan_start < x_plan_end) {
518
531
  x_plan_width = x_plan_end - x_plan_start;
519
532
  offset_x_plan_start = x_plan_start + x_plan_width * offsetX;
520
533
  x_plan_end && (offset_x_plan_end = offset_x_plan_start + x_plan_width * percent_plan);
534
+ isValidPlanTask = true;
521
535
  }
522
536
  if (task.actualStart) {
523
537
  x_actual_start = this.dateToX(new Date(task.actualStart));
538
+ isValidActualTask = true;
524
539
  }
525
540
  if (task.actualEnd) {
526
541
  x_actual_end = this.dateToX(DateUtils.addDays(task.actualEnd, 1));
527
542
  }
543
+ if (!isValidPlanTask && !isValidActualTask) {
544
+ return;
545
+ }
528
546
  if (x_actual_start) {
529
547
  x_actual_width = (x_actual_end ? x_actual_end : this.dateToX(this.today)) - x_actual_start;
530
548
  offset_x_actual_start = Math.round(x_actual_start + x_actual_width * offsetX_actual);
package/dist/index.umd.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.3.1
2
+ * gantt-canvas-chart v1.5.0
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -123,6 +123,9 @@
123
123
  onDataLoad = null;
124
124
  scrollLoadTimer = null;
125
125
  constructor(rootContainer, data, config = {}) {
126
+ if (rootContainer.querySelector(".__gantt-chart-container")) {
127
+ throw new Error("GanttChart already exists in this container");
128
+ }
126
129
  const container = document.createElement("div");
127
130
  const scrollEl = document.createElement("div");
128
131
  const headerCanvas = document.createElement("canvas");
@@ -159,6 +162,7 @@
159
162
  todayColor: "#ff4d4f",
160
163
  offsetTop: 0,
161
164
  offsetLeft: 0,
165
+ scrollEdgeThresholds: 10,
162
166
  enabledLoadMore: [],
163
167
  viewFactors: { Day: 80, Week: 20, Month: 15, Year: 6 },
164
168
  planBorderColor: "#C1EFCF",
@@ -203,8 +207,8 @@
203
207
  }
204
208
  init() {
205
209
  this.buildTaskMap();
206
- this.calculateFullTimeline();
207
210
  this.updatePixelsPerDay();
211
+ this.calculateFullTimeline();
208
212
  this.setupEvents();
209
213
  this.handleResize();
210
214
  }
@@ -276,6 +280,9 @@
276
280
  maxDate = DateUtils.addDays(/* @__PURE__ */ new Date(), 60);
277
281
  } else {
278
282
  this.taskMap.forEach(({ task }) => {
283
+ if (task.hide) {
284
+ return;
285
+ }
279
286
  const pStart = new Date(task.planStart);
280
287
  const pEnd = new Date(task.planEnd);
281
288
  if (pStart < minDate) minDate = pStart;
@@ -309,8 +316,8 @@
309
316
  break;
310
317
  case "Day":
311
318
  default:
312
- this.timelineStart = DateUtils.addDays(minDate, -3);
313
- this.timelineEnd = DateUtils.addDays(maxDate, 3);
319
+ this.timelineStart = DateUtils.addDays(minDate, 3);
320
+ this.timelineEnd = DateUtils.addDays(maxDate, -5);
314
321
  break;
315
322
  }
316
323
  }
@@ -374,9 +381,10 @@
374
381
  const viewportHeight = this.viewportHeight;
375
382
  const totalWidth = this.totalWidth;
376
383
  const totalHeight = this.totalHeight;
377
- const atLeftEdge = scrollLeft <= 5;
378
- const atRightEdge = scrollLeft + viewportWidth >= totalWidth - 5;
379
- const atBottomEdge = scrollTop + viewportHeight >= totalHeight - 5;
384
+ const thresholds = this.config.scrollEdgeThresholds;
385
+ const atLeftEdge = scrollLeft <= thresholds;
386
+ const atRightEdge = scrollLeft + viewportWidth >= totalWidth - thresholds;
387
+ const atBottomEdge = scrollTop + viewportHeight >= totalHeight - thresholds;
380
388
  try {
381
389
  if (this.hasMoreDataLeft && atLeftEdge && scrollLeft < this.lastScrollLeft) {
382
390
  await this.loadMoreData("left");
@@ -392,9 +400,7 @@
392
400
  }
393
401
  // Add this method to reset scroll loading state
394
402
  resetScrollLoadingState() {
395
- this.hasMoreDataLeft = true;
396
- this.hasMoreDataRight = true;
397
- this.hasMoreDataBottom = true;
403
+ this.updateLoadMoreConf();
398
404
  this.lastScrollLeft = 0;
399
405
  this.lastScrollTop = 0;
400
406
  if (this.scrollLoadTimer !== null) {
@@ -477,7 +483,7 @@
477
483
  if (this.scrollTop !== scrollTop) this.container.scrollTop = scrollTop;
478
484
  }
479
485
  updateVirtualRanges() {
480
- const buffer = 200;
486
+ const buffer = 100;
481
487
  this.visibleDateRange = {
482
488
  start: this.xToDate(this.scrollLeft - buffer),
483
489
  end: this.xToDate(this.scrollLeft + this.viewportWidth + buffer)
@@ -507,8 +513,14 @@
507
513
  this.taskPositions.clear();
508
514
  for (let i = 0; i < this.data.length; i++) {
509
515
  const row = this.data[i];
516
+ if (row.hide) {
517
+ continue;
518
+ }
510
519
  const y = i * this.config.rowHeight;
511
520
  row.tasks.forEach((task) => {
521
+ if (task.hide) {
522
+ return;
523
+ }
512
524
  const x_plan_start = this.dateToX(new Date(task.planStart));
513
525
  const x_plan_end = this.dateToX(DateUtils.addDays(task.planEnd, 1));
514
526
  let x_actual_start = null, x_actual_end = null;
@@ -516,19 +528,25 @@
516
528
  let offset_x_actual_start = null, offset_x_actual_end = null;
517
529
  let x_plan_width = 0;
518
530
  let x_actual_width = 0;
531
+ let isValidPlanTask = false, isValidActualTask = false;
519
532
  const [offsetX_actual, percent_actual] = this.config.viewMode === "Day" && task.actualOffsetPercent ? task.actualOffsetPercent : [0, 1];
520
533
  const [offsetX, percent_plan] = this.config.viewMode === "Day" && task.planOffsetPercent ? task.planOffsetPercent : [0, 1];
521
- if (x_plan_start && x_plan_end) {
534
+ if (x_plan_start && x_plan_end && x_plan_start < x_plan_end) {
522
535
  x_plan_width = x_plan_end - x_plan_start;
523
536
  offset_x_plan_start = x_plan_start + x_plan_width * offsetX;
524
537
  x_plan_end && (offset_x_plan_end = offset_x_plan_start + x_plan_width * percent_plan);
538
+ isValidPlanTask = true;
525
539
  }
526
540
  if (task.actualStart) {
527
541
  x_actual_start = this.dateToX(new Date(task.actualStart));
542
+ isValidActualTask = true;
528
543
  }
529
544
  if (task.actualEnd) {
530
545
  x_actual_end = this.dateToX(DateUtils.addDays(task.actualEnd, 1));
531
546
  }
547
+ if (!isValidPlanTask && !isValidActualTask) {
548
+ return;
549
+ }
532
550
  if (x_actual_start) {
533
551
  x_actual_width = (x_actual_end ? x_actual_end : this.dateToX(this.today)) - x_actual_start;
534
552
  offset_x_actual_start = Math.round(x_actual_start + x_actual_width * offsetX_actual);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gantt-canvas-chart",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "High performance Gantt chart component based on Canvas, can be applied to any framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.es.js",