adore-gantt 2.0.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/README.md +179 -0
- package/license.txt +9 -0
- package/package.json +58 -0
- package/src/arrow.js +103 -0
- package/src/bar.js +737 -0
- package/src/date_utils.js +292 -0
- package/src/defaults.js +161 -0
- package/src/index.js +1596 -0
- package/src/popup.js +61 -0
- package/src/styles/dark.css +87 -0
- package/src/styles/gantt.css +343 -0
- package/src/styles/light.css +22 -0
- package/src/svg_utils.js +135 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
const YEAR = 'year';
|
|
2
|
+
const MONTH = 'month';
|
|
3
|
+
const DAY = 'day';
|
|
4
|
+
const HOUR = 'hour';
|
|
5
|
+
const MINUTE = 'minute';
|
|
6
|
+
const SECOND = 'second';
|
|
7
|
+
const MILLISECOND = 'millisecond';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
parse_duration(duration) {
|
|
11
|
+
const regex = /([0-9]+)(y|m|d|h|min|s|ms)/gm;
|
|
12
|
+
const matches = regex.exec(duration);
|
|
13
|
+
if (matches !== null) {
|
|
14
|
+
if (matches[2] === 'y') {
|
|
15
|
+
return { duration: parseInt(matches[1]), scale: `year` };
|
|
16
|
+
} else if (matches[2] === 'm') {
|
|
17
|
+
return { duration: parseInt(matches[1]), scale: `month` };
|
|
18
|
+
} else if (matches[2] === 'd') {
|
|
19
|
+
return { duration: parseInt(matches[1]), scale: `day` };
|
|
20
|
+
} else if (matches[2] === 'h') {
|
|
21
|
+
return { duration: parseInt(matches[1]), scale: `hour` };
|
|
22
|
+
} else if (matches[2] === 'min') {
|
|
23
|
+
return { duration: parseInt(matches[1]), scale: `minute` };
|
|
24
|
+
} else if (matches[2] === 's') {
|
|
25
|
+
return { duration: parseInt(matches[1]), scale: `second` };
|
|
26
|
+
} else if (matches[2] === 'ms') {
|
|
27
|
+
return { duration: parseInt(matches[1]), scale: `millisecond` };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
parse(date, date_separator = '-', time_separator = /[.:]/) {
|
|
32
|
+
if (date instanceof Date) {
|
|
33
|
+
return date;
|
|
34
|
+
}
|
|
35
|
+
if (typeof date === 'string') {
|
|
36
|
+
let date_parts, time_parts;
|
|
37
|
+
const parts = date.split(' ');
|
|
38
|
+
date_parts = parts[0]
|
|
39
|
+
.split(date_separator)
|
|
40
|
+
.map((val) => parseInt(val, 10));
|
|
41
|
+
time_parts = parts[1] && parts[1].split(time_separator);
|
|
42
|
+
|
|
43
|
+
// month is 0 indexed
|
|
44
|
+
date_parts[1] = date_parts[1] ? date_parts[1] - 1 : 0;
|
|
45
|
+
|
|
46
|
+
let vals = date_parts;
|
|
47
|
+
|
|
48
|
+
if (time_parts && time_parts.length) {
|
|
49
|
+
if (time_parts.length === 4) {
|
|
50
|
+
time_parts[3] = '0.' + time_parts[3];
|
|
51
|
+
time_parts[3] = parseFloat(time_parts[3]) * 1000;
|
|
52
|
+
}
|
|
53
|
+
vals = vals.concat(time_parts);
|
|
54
|
+
}
|
|
55
|
+
return new Date(...vals);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
to_string(date, with_time = false) {
|
|
60
|
+
if (!(date instanceof Date)) {
|
|
61
|
+
throw new TypeError('Invalid argument type');
|
|
62
|
+
}
|
|
63
|
+
const vals = this.get_date_values(date).map((val, i) => {
|
|
64
|
+
if (i === 1) {
|
|
65
|
+
// add 1 for month
|
|
66
|
+
val = val + 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (i === 6) {
|
|
70
|
+
return padStart(val + '', 3, '0');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return padStart(val + '', 2, '0');
|
|
74
|
+
});
|
|
75
|
+
const date_string = `${vals[0]}-${vals[1]}-${vals[2]}`;
|
|
76
|
+
const time_string = `${vals[3]}:${vals[4]}:${vals[5]}.${vals[6]}`;
|
|
77
|
+
|
|
78
|
+
return date_string + (with_time ? ' ' + time_string : '');
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
format(date, date_format = 'YYYY-MM-DD HH:mm:ss.SSS', lang = 'en') {
|
|
82
|
+
const dateTimeFormat = new Intl.DateTimeFormat(lang, {
|
|
83
|
+
month: 'long',
|
|
84
|
+
});
|
|
85
|
+
const dateTimeFormatShort = new Intl.DateTimeFormat(lang, {
|
|
86
|
+
month: 'short',
|
|
87
|
+
});
|
|
88
|
+
const month_name = dateTimeFormat.format(date);
|
|
89
|
+
const month_name_capitalized =
|
|
90
|
+
month_name.charAt(0).toUpperCase() + month_name.slice(1);
|
|
91
|
+
|
|
92
|
+
const values = this.get_date_values(date).map((d) => padStart(d, 2, 0));
|
|
93
|
+
const format_map = {
|
|
94
|
+
YYYY: values[0],
|
|
95
|
+
MM: padStart(+values[1] + 1, 2, 0),
|
|
96
|
+
DD: values[2],
|
|
97
|
+
HH: values[3],
|
|
98
|
+
mm: values[4],
|
|
99
|
+
ss: values[5],
|
|
100
|
+
SSS: values[6],
|
|
101
|
+
D: values[2],
|
|
102
|
+
MMMM: month_name_capitalized,
|
|
103
|
+
MMM: dateTimeFormatShort.format(date),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
let str = date_format;
|
|
107
|
+
const formatted_values = [];
|
|
108
|
+
|
|
109
|
+
Object.keys(format_map)
|
|
110
|
+
.sort((a, b) => b.length - a.length) // big string first
|
|
111
|
+
.forEach((key) => {
|
|
112
|
+
if (str.includes(key)) {
|
|
113
|
+
str = str.replaceAll(key, `$${formatted_values.length}`);
|
|
114
|
+
formatted_values.push(format_map[key]);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
formatted_values.forEach((value, i) => {
|
|
119
|
+
str = str.replaceAll(`$${i}`, value);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return str;
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
diff(date_a, date_b, scale = 'day') {
|
|
126
|
+
let milliseconds, seconds, hours, minutes, days, months, years;
|
|
127
|
+
|
|
128
|
+
milliseconds =
|
|
129
|
+
date_a -
|
|
130
|
+
date_b +
|
|
131
|
+
(date_b.getTimezoneOffset() - date_a.getTimezoneOffset()) * 60000;
|
|
132
|
+
seconds = milliseconds / 1000;
|
|
133
|
+
minutes = seconds / 60;
|
|
134
|
+
hours = minutes / 60;
|
|
135
|
+
days = hours / 24;
|
|
136
|
+
// Calculate months across years
|
|
137
|
+
let yearDiff = date_a.getFullYear() - date_b.getFullYear();
|
|
138
|
+
let monthDiff = date_a.getMonth() - date_b.getMonth();
|
|
139
|
+
// calculate extra
|
|
140
|
+
monthDiff += (days % 30) / 30;
|
|
141
|
+
|
|
142
|
+
/* If monthDiff is negative, date_b is in an earlier month than
|
|
143
|
+
date_a and thus subtracted from the year difference in months */
|
|
144
|
+
months = yearDiff * 12 + monthDiff;
|
|
145
|
+
/* If date_a's (e.g. march 1st) day of the month is smaller than date_b (e.g. february 28th),
|
|
146
|
+
adjust the month difference */
|
|
147
|
+
if (date_a.getDate() < date_b.getDate()) {
|
|
148
|
+
months--;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Calculate years based on actual months
|
|
152
|
+
years = months / 12;
|
|
153
|
+
|
|
154
|
+
if (!scale.endsWith('s')) {
|
|
155
|
+
scale += 's';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
Math.round(
|
|
160
|
+
{
|
|
161
|
+
milliseconds,
|
|
162
|
+
seconds,
|
|
163
|
+
minutes,
|
|
164
|
+
hours,
|
|
165
|
+
days,
|
|
166
|
+
months,
|
|
167
|
+
years,
|
|
168
|
+
}[scale] * 100,
|
|
169
|
+
) / 100
|
|
170
|
+
);
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
today() {
|
|
174
|
+
const vals = this.get_date_values(new Date()).slice(0, 3);
|
|
175
|
+
return new Date(...vals);
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
now() {
|
|
179
|
+
return new Date();
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
add(date, qty, scale) {
|
|
183
|
+
qty = parseInt(qty, 10);
|
|
184
|
+
const vals = [
|
|
185
|
+
date.getFullYear() + (scale === YEAR ? qty : 0),
|
|
186
|
+
date.getMonth() + (scale === MONTH ? qty : 0),
|
|
187
|
+
date.getDate() + (scale === DAY ? qty : 0),
|
|
188
|
+
date.getHours() + (scale === HOUR ? qty : 0),
|
|
189
|
+
date.getMinutes() + (scale === MINUTE ? qty : 0),
|
|
190
|
+
date.getSeconds() + (scale === SECOND ? qty : 0),
|
|
191
|
+
date.getMilliseconds() + (scale === MILLISECOND ? qty : 0),
|
|
192
|
+
];
|
|
193
|
+
return new Date(...vals);
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
start_of(date, scale) {
|
|
197
|
+
const scores = {
|
|
198
|
+
[YEAR]: 6,
|
|
199
|
+
[MONTH]: 5,
|
|
200
|
+
[DAY]: 4,
|
|
201
|
+
[HOUR]: 3,
|
|
202
|
+
[MINUTE]: 2,
|
|
203
|
+
[SECOND]: 1,
|
|
204
|
+
[MILLISECOND]: 0,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
function should_reset(_scale) {
|
|
208
|
+
const max_score = scores[scale];
|
|
209
|
+
return scores[_scale] <= max_score;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const vals = [
|
|
213
|
+
date.getFullYear(),
|
|
214
|
+
should_reset(YEAR) ? 0 : date.getMonth(),
|
|
215
|
+
should_reset(MONTH) ? 1 : date.getDate(),
|
|
216
|
+
should_reset(DAY) ? 0 : date.getHours(),
|
|
217
|
+
should_reset(HOUR) ? 0 : date.getMinutes(),
|
|
218
|
+
should_reset(MINUTE) ? 0 : date.getSeconds(),
|
|
219
|
+
should_reset(SECOND) ? 0 : date.getMilliseconds(),
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
return new Date(...vals);
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
clone(date) {
|
|
226
|
+
return new Date(...this.get_date_values(date));
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
get_date_values(date) {
|
|
230
|
+
return [
|
|
231
|
+
date.getFullYear(),
|
|
232
|
+
date.getMonth(),
|
|
233
|
+
date.getDate(),
|
|
234
|
+
date.getHours(),
|
|
235
|
+
date.getMinutes(),
|
|
236
|
+
date.getSeconds(),
|
|
237
|
+
date.getMilliseconds(),
|
|
238
|
+
];
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
convert_scales(period, to_scale) {
|
|
242
|
+
const TO_DAYS = {
|
|
243
|
+
millisecond: 1 / 60 / 60 / 24 / 1000,
|
|
244
|
+
second: 1 / 60 / 60 / 24,
|
|
245
|
+
minute: 1 / 60 / 24,
|
|
246
|
+
hour: 1 / 24,
|
|
247
|
+
day: 1,
|
|
248
|
+
month: 30,
|
|
249
|
+
year: 365,
|
|
250
|
+
};
|
|
251
|
+
const { duration, scale } = this.parse_duration(period);
|
|
252
|
+
let in_days = duration * TO_DAYS[scale];
|
|
253
|
+
return in_days / TO_DAYS[to_scale];
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
get_days_in_month(date) {
|
|
257
|
+
const no_of_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
258
|
+
|
|
259
|
+
const month = date.getMonth();
|
|
260
|
+
|
|
261
|
+
if (month !== 1) {
|
|
262
|
+
return no_of_days[month];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Feb
|
|
266
|
+
const year = date.getFullYear();
|
|
267
|
+
if ((year % 4 === 0 && year % 100 != 0) || year % 400 === 0) {
|
|
268
|
+
return 29;
|
|
269
|
+
}
|
|
270
|
+
return 28;
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
get_days_in_year(date) {
|
|
274
|
+
return date.getFullYear() % 4 ? 365 : 366;
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
|
|
279
|
+
function padStart(str, targetLength, padString) {
|
|
280
|
+
str = str + '';
|
|
281
|
+
targetLength = targetLength >> 0;
|
|
282
|
+
padString = String(typeof padString !== 'undefined' ? padString : ' ');
|
|
283
|
+
if (str.length > targetLength) {
|
|
284
|
+
return String(str);
|
|
285
|
+
} else {
|
|
286
|
+
targetLength = targetLength - str.length;
|
|
287
|
+
if (targetLength > padString.length) {
|
|
288
|
+
padString += padString.repeat(targetLength / padString.length);
|
|
289
|
+
}
|
|
290
|
+
return padString.slice(0, targetLength) + String(str);
|
|
291
|
+
}
|
|
292
|
+
}
|
package/src/defaults.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import date_utils from './date_utils';
|
|
2
|
+
|
|
3
|
+
function getDecade(d) {
|
|
4
|
+
const year = d.getFullYear();
|
|
5
|
+
return year - (year % 10) + '';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function formatWeek(d, ld, lang) {
|
|
9
|
+
let endOfWeek = date_utils.add(d, 6, 'day');
|
|
10
|
+
let endFormat = endOfWeek.getMonth() !== d.getMonth() ? 'D MMM' : 'D';
|
|
11
|
+
let beginFormat = !ld || d.getMonth() !== ld.getMonth() ? 'D MMM' : 'D';
|
|
12
|
+
return `${date_utils.format(d, beginFormat, lang)} - ${date_utils.format(endOfWeek, endFormat, lang)}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEFAULT_VIEW_MODES = [
|
|
16
|
+
{
|
|
17
|
+
name: 'Hour',
|
|
18
|
+
padding: '7d',
|
|
19
|
+
step: '1h',
|
|
20
|
+
date_format: 'YYYY-MM-DD HH:',
|
|
21
|
+
lower_text: 'HH',
|
|
22
|
+
upper_text: (d, ld, lang) =>
|
|
23
|
+
!ld || d.getDate() !== ld.getDate()
|
|
24
|
+
? date_utils.format(d, 'D MMMM', lang)
|
|
25
|
+
: '',
|
|
26
|
+
upper_text_frequency: 24,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'Quarter Day',
|
|
30
|
+
padding: '7d',
|
|
31
|
+
step: '6h',
|
|
32
|
+
date_format: 'YYYY-MM-DD HH:',
|
|
33
|
+
lower_text: 'HH',
|
|
34
|
+
upper_text: (d, ld, lang) =>
|
|
35
|
+
!ld || d.getDate() !== ld.getDate()
|
|
36
|
+
? date_utils.format(d, 'D MMM', lang)
|
|
37
|
+
: '',
|
|
38
|
+
upper_text_frequency: 4,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Half Day',
|
|
42
|
+
padding: '14d',
|
|
43
|
+
step: '12h',
|
|
44
|
+
date_format: 'YYYY-MM-DD HH:',
|
|
45
|
+
lower_text: 'HH',
|
|
46
|
+
upper_text: (d, ld, lang) =>
|
|
47
|
+
!ld || d.getDate() !== ld.getDate()
|
|
48
|
+
? d.getMonth() !== d.getMonth()
|
|
49
|
+
? date_utils.format(d, 'D MMM', lang)
|
|
50
|
+
: date_utils.format(d, 'D', lang)
|
|
51
|
+
: '',
|
|
52
|
+
upper_text_frequency: 2,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Day',
|
|
56
|
+
padding: '7d',
|
|
57
|
+
date_format: 'YYYY-MM-DD',
|
|
58
|
+
step: '1d',
|
|
59
|
+
lower_text: (d, ld, lang) =>
|
|
60
|
+
!ld || d.getDate() !== ld.getDate()
|
|
61
|
+
? date_utils.format(d, 'D', lang)
|
|
62
|
+
: '',
|
|
63
|
+
upper_text: (d, ld, lang) =>
|
|
64
|
+
!ld || d.getMonth() !== ld.getMonth()
|
|
65
|
+
? date_utils.format(d, 'MMMM', lang)
|
|
66
|
+
: '',
|
|
67
|
+
thick_line: (d) => d.getDay() === 1,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'Week',
|
|
71
|
+
padding: '1m',
|
|
72
|
+
step: '7d',
|
|
73
|
+
date_format: 'YYYY-MM-DD',
|
|
74
|
+
column_width: 140,
|
|
75
|
+
lower_text: formatWeek,
|
|
76
|
+
upper_text: (d, ld, lang) =>
|
|
77
|
+
!ld || d.getMonth() !== ld.getMonth()
|
|
78
|
+
? date_utils.format(d, 'MMMM', lang)
|
|
79
|
+
: '',
|
|
80
|
+
thick_line: (d) => d.getDate() >= 1 && d.getDate() <= 7,
|
|
81
|
+
upper_text_frequency: 4,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'Month',
|
|
85
|
+
padding: '2m',
|
|
86
|
+
step: '1m',
|
|
87
|
+
column_width: 120,
|
|
88
|
+
date_format: 'YYYY-MM',
|
|
89
|
+
lower_text: 'MMMM',
|
|
90
|
+
upper_text: (d, ld, lang) =>
|
|
91
|
+
!ld || d.getFullYear() !== ld.getFullYear()
|
|
92
|
+
? date_utils.format(d, 'YYYY', lang)
|
|
93
|
+
: '',
|
|
94
|
+
thick_line: (d) => d.getMonth() % 3 === 0,
|
|
95
|
+
snap_at: '7d',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'Year',
|
|
99
|
+
padding: '2y',
|
|
100
|
+
step: '1y',
|
|
101
|
+
column_width: 120,
|
|
102
|
+
date_format: 'YYYY',
|
|
103
|
+
upper_text: (d, ld, lang) =>
|
|
104
|
+
!ld || getDecade(d) !== getDecade(ld) ? getDecade(d) : '',
|
|
105
|
+
lower_text: 'YYYY',
|
|
106
|
+
snap_at: '30d',
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
const DEFAULT_OPTIONS = {
|
|
111
|
+
arrow_curve: 5,
|
|
112
|
+
auto_move_label: false,
|
|
113
|
+
bar_corner_radius: 3,
|
|
114
|
+
bar_height: 30,
|
|
115
|
+
container_height: 'auto',
|
|
116
|
+
column_width: null,
|
|
117
|
+
date_format: 'YYYY-MM-DD HH:mm',
|
|
118
|
+
upper_header_height: 45,
|
|
119
|
+
lower_header_height: 30,
|
|
120
|
+
snap_at: null,
|
|
121
|
+
infinite_padding: true,
|
|
122
|
+
holidays: { 'var(--g-weekend-highlight-color)': 'weekend' },
|
|
123
|
+
ignore: [],
|
|
124
|
+
language: 'en',
|
|
125
|
+
lines: 'both',
|
|
126
|
+
move_dependencies: true,
|
|
127
|
+
padding: 18,
|
|
128
|
+
popup: (ctx) => {
|
|
129
|
+
ctx.set_title(ctx.task.name);
|
|
130
|
+
if (ctx.task.description) ctx.set_subtitle(ctx.task.description);
|
|
131
|
+
else ctx.set_subtitle('');
|
|
132
|
+
|
|
133
|
+
const start_date = date_utils.format(
|
|
134
|
+
ctx.task._start,
|
|
135
|
+
'MMM D',
|
|
136
|
+
ctx.chart.options.language,
|
|
137
|
+
);
|
|
138
|
+
const end_date = date_utils.format(
|
|
139
|
+
date_utils.add(ctx.task._end, -1, 'second'),
|
|
140
|
+
'MMM D',
|
|
141
|
+
ctx.chart.options.language,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
ctx.set_details(
|
|
145
|
+
`${start_date} - ${end_date} (${ctx.task.actual_duration} days${ctx.task.ignored_duration ? ' + ' + ctx.task.ignored_duration + ' excluded' : ''})<br/>Progress: ${Math.floor(ctx.task.progress * 100) / 100}%`,
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
popup_on: 'click',
|
|
149
|
+
readonly_progress: false,
|
|
150
|
+
readonly_dates: false,
|
|
151
|
+
readonly: false,
|
|
152
|
+
scroll_to: 'today',
|
|
153
|
+
show_expected_progress: false,
|
|
154
|
+
today_button: true,
|
|
155
|
+
view_mode: 'Day',
|
|
156
|
+
view_mode_select: false,
|
|
157
|
+
view_modes: DEFAULT_VIEW_MODES,
|
|
158
|
+
is_weekend: (d) => d.getDay() === 0 || d.getDay() === 6,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export { DEFAULT_OPTIONS, DEFAULT_VIEW_MODES };
|