gantt-canvas-chart 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,11 +46,14 @@ npm install gantt-canvas-chart
46
46
  Or include directly via CDN:
47
47
 
48
48
  ```html
49
- <script src="https://cdn.jsdelivr.net/npm/gantt-canvas-chart/dist/gantt-chart.min.js"></script>
49
+ <script src="https://cdn.jsdelivr.net/npm/gantt-canvas-chart/dist/index.umd.js"></script>
50
50
  ```
51
51
 
52
52
  ## Usage Example
53
53
 
54
+ ![Project plan](/assets/project-plan.jpg)
55
+ ![Project member tasks](/assets/project-member-task.jpg)
56
+
54
57
  ```typescript
55
58
  import { GanttChart } from 'gantt-canvas-chart';
56
59
 
@@ -115,3 +118,68 @@ ganttChart.setData(newData);
115
118
  // Update configuration
116
119
  ganttChart.updateConfig({ viewMode: 'Week' });
117
120
  ```
121
+
122
+ ## Full options list
123
+
124
+ ```typescript
125
+ interface Task {
126
+ id: string;
127
+ name: string;
128
+ type?: 'task' | 'leave' | 'overtime' | string;
129
+ planStart?: string; // Plan start date, date separated by '/'
130
+ planEnd?: string;
131
+ actualStart?: string;
132
+ actualEnd?: string;
133
+ dependencies?: string[];
134
+ leftRemark?: string;
135
+ rightRemark?: string;
136
+ centerRemark?: string;
137
+ styleClass?: string;
138
+ planBorderColor?: string;
139
+ actualBgColor?: string;
140
+ hide?: boolean; // Whether to hide this data, default false
141
+ _data?: any; // Store custom data
142
+ planOffsetPercent?: [number, number]; // Draw daily task progress (plan) based on [start coordinate offset percentage, progress percentage]
143
+ actualOffsetPercent?: [number, number]; // Draw daily task progress (actual) based on [start coordinate offset percentage, progress percentage]
144
+ }
145
+
146
+ interface Row {
147
+ id: string;
148
+ name: string;
149
+ hide?: boolean; // Whether to hide this row
150
+ tasks: Task[];
151
+ }
152
+
153
+ type GanttData = Row[];
154
+
155
+ type LoadMoreDirection = 'left' | 'right' | 'bottom';
156
+
157
+ interface GanttConfig {
158
+ viewMode?: 'Day' | 'Week' | 'Month' | 'Year';
159
+ planBorderColor?: string;
160
+ actualBgColor?: string;
161
+ headerBgColor?: string;
162
+ rowHeight?: number;
163
+ headerHeight?: number;
164
+ showPlan?: boolean;
165
+ showActual?: boolean;
166
+ showRowLines?: boolean;
167
+ showColLines?: boolean;
168
+ showLeftRemark?: boolean;
169
+ showRightRemark?: boolean;
170
+ showCenterRemark?: boolean;
171
+ showTooltip?: boolean;
172
+ tooltipColor?: 'black' | 'white';
173
+ todayColor?: string;
174
+ weekendBgColor?: string; // Weekend/holiday header background color
175
+ holidays?: string[]; // Collection of holiday dates, recommended format yyyy/MM/dd
176
+ dateSeparator?: string; // Date format separator in GanttData, default '/'
177
+ offsetTop?: number; // Tooltip position top offset (when embedded in micro frontend framework, child app page elements have offset relative to main app)
178
+ offsetLeft?: number; // Tooltip position left offset (when embedded in micro frontend framework, child app page elements have offset relative to main app)
179
+ scrollEdgeThresholds?: number; // Scroll edge threshold to trigger load more
180
+ xGap?: number; // Gap between tasks, default 0
181
+ enabledLoadMore?: [LoadMoreDirection?, LoadMoreDirection?, LoadMoreDirection?];
182
+ viewFactors?: { Day: number; Week: number; Month: number; Year: number };
183
+ tooltipFormat?: null | ((task: Row, date: Date, config: GanttConfig) => string);
184
+ }
185
+ ```
package/dist/index.cjs.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.6.0
2
+ * gantt-canvas-chart v1.6.2
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -9,24 +9,35 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
9
9
  class DateUtils {
10
10
  static ONE_DAY_MS = 24 * 60 * 60 * 1e3;
11
11
  static format(date, format = "yyyy-MM-dd") {
12
- const o = {
13
- "M+": date.getMonth() + 1,
14
- "d+": date.getDate(),
15
- "h+": date.getHours(),
16
- "m+": date.getMinutes(),
17
- "s+": date.getSeconds(),
12
+ let fmt = format;
13
+ let ret;
14
+ const opt = {
15
+ "Y+": `${date.getFullYear()}`,
16
+ //
17
+ "y+": `${date.getFullYear()}`,
18
+ // 年
19
+ "M+": `${date.getMonth() + 1}`,
20
+ // 月
21
+ "D+": `${date.getDate()}`,
22
+ // 日
23
+ "d+": `${date.getDate()}`,
24
+ // 日
25
+ "h+": `${date.getHours()}`,
26
+ // 时
27
+ "m+": `${date.getMinutes()}`,
28
+ // 分
29
+ "s+": `${date.getSeconds()}`,
30
+ // 秒
18
31
  "q+": Math.floor((date.getMonth() + 3) / 3),
19
32
  W: ["日", "一", "二", "三", "四", "五", "六"][date.getDay()]
20
33
  };
21
- if (/(y+)/.test(format))
22
- format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
23
- for (let k in o)
24
- if (new RegExp("(" + k + ")").test(format))
25
- format = format.replace(
26
- RegExp.$1,
27
- RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
28
- );
29
- return format;
34
+ for (const k in opt) {
35
+ ret = new RegExp("(" + k + ")").exec(fmt);
36
+ if (ret) {
37
+ fmt = fmt.replace(ret[1], ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, "0"));
38
+ }
39
+ }
40
+ return fmt;
30
41
  }
31
42
  static addDays(date, days) {
32
43
  const r = new Date(date);
@@ -192,6 +203,8 @@ class GanttChart {
192
203
  showRightRemark: false,
193
204
  showCenterRemark: false,
194
205
  showTooltip: true,
206
+ queryStartDate: null,
207
+ queryEndDate: null,
195
208
  tooltipFormat: null,
196
209
  tooltipColor: "black",
197
210
  todayColor: "#ff4d4f",
@@ -331,6 +344,7 @@ class GanttChart {
331
344
  destroy() {
332
345
  if (this.resizeObserver) {
333
346
  this.resizeObserver.disconnect();
347
+ this.resizeObserver = null;
334
348
  }
335
349
  this.container.removeEventListener("scroll", this.handleScroll);
336
350
  this.mainCanvas.removeEventListener("mousemove", this.handleMouseMove);
@@ -338,15 +352,18 @@ class GanttChart {
338
352
  this.data = [];
339
353
  this.taskMap.clear();
340
354
  this.taskPositions.clear();
355
+ this.holidaysMap.clear();
356
+ this.config.holidays = [];
341
357
  this.container.remove();
342
358
  }
343
359
  calculateFullTimeline() {
344
360
  const currentYear = this.today.getFullYear();
345
361
  let minDate = new Date(9999, 0, 1);
346
362
  let maxDate = new Date(1e3, 0, 1);
363
+ const { queryStartDate, queryEndDate } = this.config;
347
364
  if (this.data.length === 0) {
348
- minDate = /* @__PURE__ */ new Date();
349
- maxDate = DateUtils.addDays(/* @__PURE__ */ new Date(), 60);
365
+ minDate = queryStartDate ? queryStartDate : /* @__PURE__ */ new Date();
366
+ maxDate = DateUtils.addDays(queryEndDate ? queryEndDate : /* @__PURE__ */ new Date(), 60);
350
367
  } else {
351
368
  this.taskMap.forEach(({ task }) => {
352
369
  if (task.hide) {
@@ -366,6 +383,12 @@ class GanttChart {
366
383
  }
367
384
  this.minDate = minDate;
368
385
  this.maxDate = maxDate;
386
+ if (queryStartDate && queryStartDate < minDate) {
387
+ minDate = queryStartDate;
388
+ }
389
+ if (queryEndDate && queryEndDate > maxDate) {
390
+ maxDate = queryEndDate;
391
+ }
369
392
  const minYear = minDate.getFullYear();
370
393
  const maxYear = maxDate.getFullYear();
371
394
  minDate = DateUtils.addDays(minYear === 9999 ? new Date(currentYear, 0, 1) : minDate, -7);
@@ -583,9 +606,9 @@ class GanttChart {
583
606
  return ctx;
584
607
  }
585
608
  /**
586
- * 辅助函数:像素对齐(确保MAC/Windows等不同设备上1px线条清晰)
587
- * 用于 1px 线条,使其落在 x.5 位置,填满一个物理像素,避免模糊
588
- */
609
+ * Helper function: Pixel alignment (ensures 1px lines are crisp on different devices like MAC/Windows)
610
+ * Used for 1px lines to position them at x.5 coordinates, filling a physical pixel to avoid blurriness
611
+ */
589
612
  snap(val) {
590
613
  return Math.floor(val) + 0.5;
591
614
  }
@@ -754,7 +777,7 @@ class GanttChart {
754
777
  }
755
778
  const groupedBlocks = this.groupConsecutiveBlocks(visibleBlocks);
756
779
  ctx.fillStyle = "#333";
757
- ctx.font = "bold 14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
780
+ ctx.font = "bold 12px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
758
781
  ctx.textAlign = "left";
759
782
  groupedBlocks.forEach((group) => {
760
783
  const visibleStart = Math.max(group.startX, this.scrollLeft);
@@ -763,7 +786,7 @@ class GanttChart {
763
786
  ctx.fillStyle = this.config.headerBgColor;
764
787
  ctx.fillRect(Math.round(visibleStart), 0, Math.round(visibleEnd - visibleStart), Math.round(h * 0.5));
765
788
  ctx.fillStyle = "#333";
766
- ctx.fillText(group.text, Math.round(visibleStart + 5), Math.round(group.yPos));
789
+ ctx.fillText(group.text, Math.round(visibleStart + 5), Math.round(group.yPos - 3));
767
790
  }
768
791
  });
769
792
  while (currentDateForLower <= this.visibleDateRange.end) {
@@ -802,7 +825,7 @@ class GanttChart {
802
825
  }
803
826
  const unitWidth = this.dateToX(nextDate) - x;
804
827
  ctx.fillStyle = "#000412";
805
- ctx.font = "14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
828
+ ctx.font = "12px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
806
829
  ctx.textAlign = "center";
807
830
  ctx.fillText(lowerText, Math.round(x + unitWidth / 2), Math.round(h * 0.7));
808
831
  ctx.beginPath();
@@ -1190,9 +1213,9 @@ class GanttChart {
1190
1213
  this.showTooltip = false;
1191
1214
  }
1192
1215
  /**
1193
- * 计算任务宽度占的百分比(方便绘制精确到具体时间的每日任务)
1194
- * @param diffMilliseconds 距离目标日期时间的差异毫秒数
1195
- * @param pixelsPerDay 每日的像素数
1216
+ * Calculate the percentage width of a task (convenient for drawing daily tasks accurate to specific times)
1217
+ * @param diffMilliseconds The difference in milliseconds from the target date
1218
+ * @param pixelsPerDay Number of pixels per day
1196
1219
  * @returns
1197
1220
  */
1198
1221
  static getTaskWidthPercent(diffMilliseconds, pixelsPerDay) {
package/dist/index.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.6.0
2
+ * gantt-canvas-chart v1.6.2
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
package/dist/index.d.ts CHANGED
@@ -121,8 +121,8 @@ export declare class GanttChart {
121
121
  private updateDimensions;
122
122
  private setupCanvas;
123
123
  /**
124
- * 辅助函数:像素对齐(确保MAC/Windows等不同设备上1px线条清晰)
125
- * 用于 1px 线条,使其落在 x.5 位置,填满一个物理像素,避免模糊
124
+ * Helper function: Pixel alignment (ensures 1px lines are crisp on different devices like MAC/Windows)
125
+ * Used for 1px lines to position them at x.5 coordinates, filling a physical pixel to avoid blurriness
126
126
  */
127
127
  snap(val: number): number;
128
128
  private calculateAllTaskPositions;
@@ -142,9 +142,9 @@ export declare class GanttChart {
142
142
  private getTaskTooltipHtml;
143
143
  private handleMouseLeave;
144
144
  /**
145
- * 计算任务宽度占的百分比(方便绘制精确到具体时间的每日任务)
146
- * @param diffMilliseconds 距离目标日期时间的差异毫秒数
147
- * @param pixelsPerDay 每日的像素数
145
+ * Calculate the percentage width of a task (convenient for drawing daily tasks accurate to specific times)
146
+ * @param diffMilliseconds The difference in milliseconds from the target date
147
+ * @param pixelsPerDay Number of pixels per day
148
148
  * @returns
149
149
  */
150
150
  static getTaskWidthPercent(diffMilliseconds: number, pixelsPerDay: number): number;
@@ -180,6 +180,8 @@ export declare interface GanttConfig {
180
180
  showRightRemark?: boolean;
181
181
  showCenterRemark?: boolean;
182
182
  showTooltip?: boolean;
183
+ queryStartDate?: Date | null;
184
+ queryEndDate?: Date | null;
183
185
  tooltipColor?: 'black' | 'white';
184
186
  todayColor?: string;
185
187
  weekendBgColor?: string;
package/dist/index.es.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.6.0
2
+ * gantt-canvas-chart v1.6.2
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -7,24 +7,35 @@
7
7
  class DateUtils {
8
8
  static ONE_DAY_MS = 24 * 60 * 60 * 1e3;
9
9
  static format(date, format = "yyyy-MM-dd") {
10
- const o = {
11
- "M+": date.getMonth() + 1,
12
- "d+": date.getDate(),
13
- "h+": date.getHours(),
14
- "m+": date.getMinutes(),
15
- "s+": date.getSeconds(),
10
+ let fmt = format;
11
+ let ret;
12
+ const opt = {
13
+ "Y+": `${date.getFullYear()}`,
14
+ //
15
+ "y+": `${date.getFullYear()}`,
16
+ // 年
17
+ "M+": `${date.getMonth() + 1}`,
18
+ // 月
19
+ "D+": `${date.getDate()}`,
20
+ // 日
21
+ "d+": `${date.getDate()}`,
22
+ // 日
23
+ "h+": `${date.getHours()}`,
24
+ // 时
25
+ "m+": `${date.getMinutes()}`,
26
+ // 分
27
+ "s+": `${date.getSeconds()}`,
28
+ // 秒
16
29
  "q+": Math.floor((date.getMonth() + 3) / 3),
17
30
  W: ["日", "一", "二", "三", "四", "五", "六"][date.getDay()]
18
31
  };
19
- if (/(y+)/.test(format))
20
- format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
21
- for (let k in o)
22
- if (new RegExp("(" + k + ")").test(format))
23
- format = format.replace(
24
- RegExp.$1,
25
- RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
26
- );
27
- return format;
32
+ for (const k in opt) {
33
+ ret = new RegExp("(" + k + ")").exec(fmt);
34
+ if (ret) {
35
+ fmt = fmt.replace(ret[1], ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, "0"));
36
+ }
37
+ }
38
+ return fmt;
28
39
  }
29
40
  static addDays(date, days) {
30
41
  const r = new Date(date);
@@ -190,6 +201,8 @@ class GanttChart {
190
201
  showRightRemark: false,
191
202
  showCenterRemark: false,
192
203
  showTooltip: true,
204
+ queryStartDate: null,
205
+ queryEndDate: null,
193
206
  tooltipFormat: null,
194
207
  tooltipColor: "black",
195
208
  todayColor: "#ff4d4f",
@@ -329,6 +342,7 @@ class GanttChart {
329
342
  destroy() {
330
343
  if (this.resizeObserver) {
331
344
  this.resizeObserver.disconnect();
345
+ this.resizeObserver = null;
332
346
  }
333
347
  this.container.removeEventListener("scroll", this.handleScroll);
334
348
  this.mainCanvas.removeEventListener("mousemove", this.handleMouseMove);
@@ -336,15 +350,18 @@ class GanttChart {
336
350
  this.data = [];
337
351
  this.taskMap.clear();
338
352
  this.taskPositions.clear();
353
+ this.holidaysMap.clear();
354
+ this.config.holidays = [];
339
355
  this.container.remove();
340
356
  }
341
357
  calculateFullTimeline() {
342
358
  const currentYear = this.today.getFullYear();
343
359
  let minDate = new Date(9999, 0, 1);
344
360
  let maxDate = new Date(1e3, 0, 1);
361
+ const { queryStartDate, queryEndDate } = this.config;
345
362
  if (this.data.length === 0) {
346
- minDate = /* @__PURE__ */ new Date();
347
- maxDate = DateUtils.addDays(/* @__PURE__ */ new Date(), 60);
363
+ minDate = queryStartDate ? queryStartDate : /* @__PURE__ */ new Date();
364
+ maxDate = DateUtils.addDays(queryEndDate ? queryEndDate : /* @__PURE__ */ new Date(), 60);
348
365
  } else {
349
366
  this.taskMap.forEach(({ task }) => {
350
367
  if (task.hide) {
@@ -364,6 +381,12 @@ class GanttChart {
364
381
  }
365
382
  this.minDate = minDate;
366
383
  this.maxDate = maxDate;
384
+ if (queryStartDate && queryStartDate < minDate) {
385
+ minDate = queryStartDate;
386
+ }
387
+ if (queryEndDate && queryEndDate > maxDate) {
388
+ maxDate = queryEndDate;
389
+ }
367
390
  const minYear = minDate.getFullYear();
368
391
  const maxYear = maxDate.getFullYear();
369
392
  minDate = DateUtils.addDays(minYear === 9999 ? new Date(currentYear, 0, 1) : minDate, -7);
@@ -581,9 +604,9 @@ class GanttChart {
581
604
  return ctx;
582
605
  }
583
606
  /**
584
- * 辅助函数:像素对齐(确保MAC/Windows等不同设备上1px线条清晰)
585
- * 用于 1px 线条,使其落在 x.5 位置,填满一个物理像素,避免模糊
586
- */
607
+ * Helper function: Pixel alignment (ensures 1px lines are crisp on different devices like MAC/Windows)
608
+ * Used for 1px lines to position them at x.5 coordinates, filling a physical pixel to avoid blurriness
609
+ */
587
610
  snap(val) {
588
611
  return Math.floor(val) + 0.5;
589
612
  }
@@ -752,7 +775,7 @@ class GanttChart {
752
775
  }
753
776
  const groupedBlocks = this.groupConsecutiveBlocks(visibleBlocks);
754
777
  ctx.fillStyle = "#333";
755
- ctx.font = "bold 14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
778
+ ctx.font = "bold 12px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
756
779
  ctx.textAlign = "left";
757
780
  groupedBlocks.forEach((group) => {
758
781
  const visibleStart = Math.max(group.startX, this.scrollLeft);
@@ -761,7 +784,7 @@ class GanttChart {
761
784
  ctx.fillStyle = this.config.headerBgColor;
762
785
  ctx.fillRect(Math.round(visibleStart), 0, Math.round(visibleEnd - visibleStart), Math.round(h * 0.5));
763
786
  ctx.fillStyle = "#333";
764
- ctx.fillText(group.text, Math.round(visibleStart + 5), Math.round(group.yPos));
787
+ ctx.fillText(group.text, Math.round(visibleStart + 5), Math.round(group.yPos - 3));
765
788
  }
766
789
  });
767
790
  while (currentDateForLower <= this.visibleDateRange.end) {
@@ -800,7 +823,7 @@ class GanttChart {
800
823
  }
801
824
  const unitWidth = this.dateToX(nextDate) - x;
802
825
  ctx.fillStyle = "#000412";
803
- ctx.font = "14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
826
+ ctx.font = "12px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
804
827
  ctx.textAlign = "center";
805
828
  ctx.fillText(lowerText, Math.round(x + unitWidth / 2), Math.round(h * 0.7));
806
829
  ctx.beginPath();
@@ -1188,9 +1211,9 @@ class GanttChart {
1188
1211
  this.showTooltip = false;
1189
1212
  }
1190
1213
  /**
1191
- * 计算任务宽度占的百分比(方便绘制精确到具体时间的每日任务)
1192
- * @param diffMilliseconds 距离目标日期时间的差异毫秒数
1193
- * @param pixelsPerDay 每日的像素数
1214
+ * Calculate the percentage width of a task (convenient for drawing daily tasks accurate to specific times)
1215
+ * @param diffMilliseconds The difference in milliseconds from the target date
1216
+ * @param pixelsPerDay Number of pixels per day
1194
1217
  * @returns
1195
1218
  */
1196
1219
  static getTaskWidthPercent(diffMilliseconds, pixelsPerDay) {
package/dist/index.umd.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * gantt-canvas-chart v1.6.0
2
+ * gantt-canvas-chart v1.6.2
3
3
  * (c) 2025-present chandq
4
4
  * Released under the MIT License.
5
5
  */
@@ -11,24 +11,35 @@
11
11
  class DateUtils {
12
12
  static ONE_DAY_MS = 24 * 60 * 60 * 1e3;
13
13
  static format(date, format = "yyyy-MM-dd") {
14
- const o = {
15
- "M+": date.getMonth() + 1,
16
- "d+": date.getDate(),
17
- "h+": date.getHours(),
18
- "m+": date.getMinutes(),
19
- "s+": date.getSeconds(),
14
+ let fmt = format;
15
+ let ret;
16
+ const opt = {
17
+ "Y+": `${date.getFullYear()}`,
18
+ //
19
+ "y+": `${date.getFullYear()}`,
20
+ // 年
21
+ "M+": `${date.getMonth() + 1}`,
22
+ // 月
23
+ "D+": `${date.getDate()}`,
24
+ // 日
25
+ "d+": `${date.getDate()}`,
26
+ // 日
27
+ "h+": `${date.getHours()}`,
28
+ // 时
29
+ "m+": `${date.getMinutes()}`,
30
+ // 分
31
+ "s+": `${date.getSeconds()}`,
32
+ // 秒
20
33
  "q+": Math.floor((date.getMonth() + 3) / 3),
21
34
  W: ["日", "一", "二", "三", "四", "五", "六"][date.getDay()]
22
35
  };
23
- if (/(y+)/.test(format))
24
- format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
25
- for (let k in o)
26
- if (new RegExp("(" + k + ")").test(format))
27
- format = format.replace(
28
- RegExp.$1,
29
- RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
30
- );
31
- return format;
36
+ for (const k in opt) {
37
+ ret = new RegExp("(" + k + ")").exec(fmt);
38
+ if (ret) {
39
+ fmt = fmt.replace(ret[1], ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, "0"));
40
+ }
41
+ }
42
+ return fmt;
32
43
  }
33
44
  static addDays(date, days) {
34
45
  const r = new Date(date);
@@ -194,6 +205,8 @@
194
205
  showRightRemark: false,
195
206
  showCenterRemark: false,
196
207
  showTooltip: true,
208
+ queryStartDate: null,
209
+ queryEndDate: null,
197
210
  tooltipFormat: null,
198
211
  tooltipColor: "black",
199
212
  todayColor: "#ff4d4f",
@@ -333,6 +346,7 @@
333
346
  destroy() {
334
347
  if (this.resizeObserver) {
335
348
  this.resizeObserver.disconnect();
349
+ this.resizeObserver = null;
336
350
  }
337
351
  this.container.removeEventListener("scroll", this.handleScroll);
338
352
  this.mainCanvas.removeEventListener("mousemove", this.handleMouseMove);
@@ -340,15 +354,18 @@
340
354
  this.data = [];
341
355
  this.taskMap.clear();
342
356
  this.taskPositions.clear();
357
+ this.holidaysMap.clear();
358
+ this.config.holidays = [];
343
359
  this.container.remove();
344
360
  }
345
361
  calculateFullTimeline() {
346
362
  const currentYear = this.today.getFullYear();
347
363
  let minDate = new Date(9999, 0, 1);
348
364
  let maxDate = new Date(1e3, 0, 1);
365
+ const { queryStartDate, queryEndDate } = this.config;
349
366
  if (this.data.length === 0) {
350
- minDate = /* @__PURE__ */ new Date();
351
- maxDate = DateUtils.addDays(/* @__PURE__ */ new Date(), 60);
367
+ minDate = queryStartDate ? queryStartDate : /* @__PURE__ */ new Date();
368
+ maxDate = DateUtils.addDays(queryEndDate ? queryEndDate : /* @__PURE__ */ new Date(), 60);
352
369
  } else {
353
370
  this.taskMap.forEach(({ task }) => {
354
371
  if (task.hide) {
@@ -368,6 +385,12 @@
368
385
  }
369
386
  this.minDate = minDate;
370
387
  this.maxDate = maxDate;
388
+ if (queryStartDate && queryStartDate < minDate) {
389
+ minDate = queryStartDate;
390
+ }
391
+ if (queryEndDate && queryEndDate > maxDate) {
392
+ maxDate = queryEndDate;
393
+ }
371
394
  const minYear = minDate.getFullYear();
372
395
  const maxYear = maxDate.getFullYear();
373
396
  minDate = DateUtils.addDays(minYear === 9999 ? new Date(currentYear, 0, 1) : minDate, -7);
@@ -585,9 +608,9 @@
585
608
  return ctx;
586
609
  }
587
610
  /**
588
- * 辅助函数:像素对齐(确保MAC/Windows等不同设备上1px线条清晰)
589
- * 用于 1px 线条,使其落在 x.5 位置,填满一个物理像素,避免模糊
590
- */
611
+ * Helper function: Pixel alignment (ensures 1px lines are crisp on different devices like MAC/Windows)
612
+ * Used for 1px lines to position them at x.5 coordinates, filling a physical pixel to avoid blurriness
613
+ */
591
614
  snap(val) {
592
615
  return Math.floor(val) + 0.5;
593
616
  }
@@ -756,7 +779,7 @@
756
779
  }
757
780
  const groupedBlocks = this.groupConsecutiveBlocks(visibleBlocks);
758
781
  ctx.fillStyle = "#333";
759
- ctx.font = "bold 14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
782
+ ctx.font = "bold 12px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
760
783
  ctx.textAlign = "left";
761
784
  groupedBlocks.forEach((group) => {
762
785
  const visibleStart = Math.max(group.startX, this.scrollLeft);
@@ -765,7 +788,7 @@
765
788
  ctx.fillStyle = this.config.headerBgColor;
766
789
  ctx.fillRect(Math.round(visibleStart), 0, Math.round(visibleEnd - visibleStart), Math.round(h * 0.5));
767
790
  ctx.fillStyle = "#333";
768
- ctx.fillText(group.text, Math.round(visibleStart + 5), Math.round(group.yPos));
791
+ ctx.fillText(group.text, Math.round(visibleStart + 5), Math.round(group.yPos - 3));
769
792
  }
770
793
  });
771
794
  while (currentDateForLower <= this.visibleDateRange.end) {
@@ -804,7 +827,7 @@
804
827
  }
805
828
  const unitWidth = this.dateToX(nextDate) - x;
806
829
  ctx.fillStyle = "#000412";
807
- ctx.font = "14px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
830
+ ctx.font = "12px Roboto,PingFang SC,Noto Sans SC,Microsoft YaHei UI,Microsoft YaHei,Segoe UI,Helvetica Neue,Helvetica,Arial,sans-serif";
808
831
  ctx.textAlign = "center";
809
832
  ctx.fillText(lowerText, Math.round(x + unitWidth / 2), Math.round(h * 0.7));
810
833
  ctx.beginPath();
@@ -1192,9 +1215,9 @@
1192
1215
  this.showTooltip = false;
1193
1216
  }
1194
1217
  /**
1195
- * 计算任务宽度占的百分比(方便绘制精确到具体时间的每日任务)
1196
- * @param diffMilliseconds 距离目标日期时间的差异毫秒数
1197
- * @param pixelsPerDay 每日的像素数
1218
+ * Calculate the percentage width of a task (convenient for drawing daily tasks accurate to specific times)
1219
+ * @param diffMilliseconds The difference in milliseconds from the target date
1220
+ * @param pixelsPerDay Number of pixels per day
1198
1221
  * @returns
1199
1222
  */
1200
1223
  static getTaskWidthPercent(diffMilliseconds, pixelsPerDay) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gantt-canvas-chart",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
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",