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 +69 -1
- package/dist/index.cjs.js +50 -27
- package/dist/index.css +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.es.js +50 -27
- package/dist/index.umd.js +50 -27
- package/package.json +1 -1
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/
|
|
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
|
+

|
|
55
|
+

|
|
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.
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
587
|
-
|
|
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
|
|
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 = "
|
|
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
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
|
-
*
|
|
125
|
-
*
|
|
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.
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
"
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
585
|
-
|
|
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
|
|
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 = "
|
|
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.
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
589
|
-
|
|
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
|
|
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 = "
|
|
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) {
|