@workiom/frappe-gantt 1.0.21 → 1.0.23
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 +65 -0
- package/dist/frappe-gantt.css +1 -1
- package/dist/frappe-gantt.es.js +753 -512
- package/dist/frappe-gantt.umd.js +92 -35
- package/package.json +1 -1
- package/src/arrow.js +205 -14
- package/src/bar.js +44 -0
- package/src/defaults.js +1 -0
- package/src/index.js +262 -2
- package/src/styles/dark.css +27 -0
- package/src/styles/gantt.css +69 -0
- package/src/styles/light.css +6 -0
package/src/arrow.js
CHANGED
|
@@ -9,6 +9,7 @@ export default class Arrow {
|
|
|
9
9
|
this.is_critical = this.check_critical_path();
|
|
10
10
|
this.is_invalid = this.check_invalid_dependency();
|
|
11
11
|
this.is_hovered = false;
|
|
12
|
+
this.is_active = false;
|
|
12
13
|
|
|
13
14
|
this.calculate_path();
|
|
14
15
|
this.draw();
|
|
@@ -76,47 +77,64 @@ export default class Arrow {
|
|
|
76
77
|
this.path = this._path_finish_to_start(
|
|
77
78
|
right_A, left_A, right_B, left_B, y_A, y_B, y_mid, padding, curve
|
|
78
79
|
);
|
|
80
|
+
this.label_pos = { x: left_B, y: y_B, side: 'left' };
|
|
79
81
|
break;
|
|
80
82
|
case 'start-to-start':
|
|
81
83
|
this.path = this._path_start_to_start(
|
|
82
84
|
left_A, left_B, y_A, y_B, padding, curve
|
|
83
85
|
);
|
|
86
|
+
this.label_pos = { x: left_B, y: y_B, side: 'left' };
|
|
84
87
|
break;
|
|
85
88
|
case 'finish-to-finish':
|
|
86
89
|
this.path = this._path_finish_to_finish(
|
|
87
90
|
right_A, right_B, y_A, y_B, padding, curve
|
|
88
91
|
);
|
|
92
|
+
this.label_pos = { x: right_B, y: y_B, side: 'right' };
|
|
89
93
|
break;
|
|
90
94
|
case 'start-to-finish':
|
|
91
95
|
this.path = this._path_start_to_finish(
|
|
92
96
|
left_A, right_B, y_A, y_B, y_mid, padding, curve
|
|
93
97
|
);
|
|
98
|
+
this.label_pos = { x: right_B, y: y_B, side: 'right' };
|
|
94
99
|
break;
|
|
95
100
|
default:
|
|
96
101
|
this.path = this._path_finish_to_start(
|
|
97
102
|
right_A, left_A, right_B, left_B, y_A, y_B, y_mid, padding, curve
|
|
98
103
|
);
|
|
104
|
+
this.label_pos = { x: left_B, y: y_B, side: 'left' };
|
|
99
105
|
}
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
_path_finish_to_start(right_A, left_A, right_B, left_B, y_A, y_B, y_mid, padding, curve) {
|
|
103
109
|
const x_right = right_A + padding;
|
|
110
|
+
const going_up = y_B < y_A;
|
|
104
111
|
|
|
105
112
|
if (x_right < left_B) {
|
|
106
|
-
// Case 1:
|
|
113
|
+
// Case 1: gap between tasks — 3 segments: right, vertical, right
|
|
114
|
+
if (!going_up) {
|
|
115
|
+
return `
|
|
116
|
+
M ${right_A} ${y_A}
|
|
117
|
+
H ${x_right - curve}
|
|
118
|
+
a ${curve} ${curve} 0 0 1 ${curve} ${curve}
|
|
119
|
+
V ${y_B - curve}
|
|
120
|
+
a ${curve} ${curve} 0 0 0 ${curve} ${curve}
|
|
121
|
+
H ${left_B}
|
|
122
|
+
m -5 -5 l 5 5 l -5 5`;
|
|
123
|
+
}
|
|
107
124
|
return `
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
125
|
+
M ${right_A} ${y_A}
|
|
126
|
+
H ${x_right - curve}
|
|
127
|
+
a ${curve} ${curve} 0 0 0 ${curve} ${-curve}
|
|
128
|
+
V ${y_B + curve}
|
|
129
|
+
a ${curve} ${curve} 0 0 1 ${curve} ${-curve}
|
|
130
|
+
H ${left_B}
|
|
131
|
+
m -5 -5 l 5 5 l -5 5`;
|
|
115
132
|
}
|
|
116
133
|
|
|
117
|
-
// Case 2: overlap — 5 segments: right,
|
|
134
|
+
// Case 2: overlap — 5 segments: right, vertical, left, vertical, right
|
|
118
135
|
const x_left = left_B - padding;
|
|
119
|
-
|
|
136
|
+
if (!going_up) {
|
|
137
|
+
return `
|
|
120
138
|
M ${right_A} ${y_A}
|
|
121
139
|
H ${x_right - curve}
|
|
122
140
|
a ${curve} ${curve} 0 0 1 ${curve} ${curve}
|
|
@@ -128,12 +146,27 @@ export default class Arrow {
|
|
|
128
146
|
a ${curve} ${curve} 0 0 0 ${curve} ${curve}
|
|
129
147
|
H ${left_B}
|
|
130
148
|
m -5 -5 l 5 5 l -5 5`;
|
|
149
|
+
}
|
|
150
|
+
return `
|
|
151
|
+
M ${right_A} ${y_A}
|
|
152
|
+
H ${x_right - curve}
|
|
153
|
+
a ${curve} ${curve} 0 0 0 ${curve} ${-curve}
|
|
154
|
+
V ${y_mid + curve}
|
|
155
|
+
a ${curve} ${curve} 0 0 0 ${-curve} ${-curve}
|
|
156
|
+
H ${x_left + curve}
|
|
157
|
+
a ${curve} ${curve} 0 0 1 ${-curve} ${-curve}
|
|
158
|
+
V ${y_B + curve}
|
|
159
|
+
a ${curve} ${curve} 0 0 1 ${curve} ${-curve}
|
|
160
|
+
H ${left_B}
|
|
161
|
+
m -5 -5 l 5 5 l -5 5`;
|
|
131
162
|
}
|
|
132
163
|
|
|
133
164
|
_path_start_to_start(left_A, left_B, y_A, y_B, padding, curve) {
|
|
134
165
|
const x_left = Math.min(left_A, left_B) - padding;
|
|
166
|
+
const going_up = y_B < y_A;
|
|
135
167
|
|
|
136
|
-
|
|
168
|
+
if (!going_up) {
|
|
169
|
+
return `
|
|
137
170
|
M ${left_A} ${y_A}
|
|
138
171
|
H ${x_left + curve}
|
|
139
172
|
a ${curve} ${curve} 0 0 0 ${-curve} ${curve}
|
|
@@ -141,12 +174,23 @@ export default class Arrow {
|
|
|
141
174
|
a ${curve} ${curve} 0 0 0 ${curve} ${curve}
|
|
142
175
|
H ${left_B}
|
|
143
176
|
m -5 -5 l 5 5 l -5 5`;
|
|
177
|
+
}
|
|
178
|
+
return `
|
|
179
|
+
M ${left_A} ${y_A}
|
|
180
|
+
H ${x_left + curve}
|
|
181
|
+
a ${curve} ${curve} 0 0 1 ${-curve} ${-curve}
|
|
182
|
+
V ${y_B + curve}
|
|
183
|
+
a ${curve} ${curve} 0 0 1 ${curve} ${-curve}
|
|
184
|
+
H ${left_B}
|
|
185
|
+
m -5 -5 l 5 5 l -5 5`;
|
|
144
186
|
}
|
|
145
187
|
|
|
146
188
|
_path_finish_to_finish(right_A, right_B, y_A, y_B, padding, curve) {
|
|
147
189
|
const x_right = Math.max(right_A, right_B) + padding;
|
|
190
|
+
const going_up = y_B < y_A;
|
|
148
191
|
|
|
149
|
-
|
|
192
|
+
if (!going_up) {
|
|
193
|
+
return `
|
|
150
194
|
M ${right_A} ${y_A}
|
|
151
195
|
H ${x_right - curve}
|
|
152
196
|
a ${curve} ${curve} 0 0 1 ${curve} ${curve}
|
|
@@ -154,13 +198,27 @@ export default class Arrow {
|
|
|
154
198
|
a ${curve} ${curve} 0 0 1 ${-curve} ${curve}
|
|
155
199
|
H ${right_B}
|
|
156
200
|
m 5 -5 l -5 5 l 5 5`;
|
|
201
|
+
}
|
|
202
|
+
return `
|
|
203
|
+
M ${right_A} ${y_A}
|
|
204
|
+
H ${x_right - curve}
|
|
205
|
+
a ${curve} ${curve} 0 0 0 ${curve} ${-curve}
|
|
206
|
+
V ${y_B + curve}
|
|
207
|
+
a ${curve} ${curve} 0 0 0 ${-curve} ${-curve}
|
|
208
|
+
H ${right_B}
|
|
209
|
+
m 5 -5 l -5 5 l 5 5`;
|
|
157
210
|
}
|
|
158
211
|
|
|
159
212
|
_path_start_to_finish(left_A, right_B, y_A, y_B, y_mid, padding, curve) {
|
|
160
213
|
const x_left = left_A - padding;
|
|
161
214
|
const x_right = right_B + padding;
|
|
215
|
+
const going_up = y_B < y_A;
|
|
216
|
+
// crossed: from-task is to the right of to-task — not enough room for mid-column detour
|
|
217
|
+
const crossed = x_right < x_left + 2 * curve;
|
|
162
218
|
|
|
163
|
-
|
|
219
|
+
if (!crossed) {
|
|
220
|
+
if (!going_up) {
|
|
221
|
+
return `
|
|
164
222
|
M ${left_A} ${y_A}
|
|
165
223
|
H ${x_left + curve}
|
|
166
224
|
a ${curve} ${curve} 0 0 0 ${-curve} ${curve}
|
|
@@ -172,6 +230,39 @@ export default class Arrow {
|
|
|
172
230
|
a ${curve} ${curve} 0 0 1 ${-curve} ${curve}
|
|
173
231
|
H ${right_B}
|
|
174
232
|
m 5 -5 l -5 5 l 5 5`;
|
|
233
|
+
}
|
|
234
|
+
return `
|
|
235
|
+
M ${left_A} ${y_A}
|
|
236
|
+
H ${x_left + curve}
|
|
237
|
+
a ${curve} ${curve} 0 0 1 ${-curve} ${-curve}
|
|
238
|
+
V ${y_mid + curve}
|
|
239
|
+
a ${curve} ${curve} 0 0 1 ${curve} ${-curve}
|
|
240
|
+
H ${x_right - curve}
|
|
241
|
+
a ${curve} ${curve} 0 0 0 ${curve} ${-curve}
|
|
242
|
+
V ${y_B + curve}
|
|
243
|
+
a ${curve} ${curve} 0 0 0 ${-curve} ${-curve}
|
|
244
|
+
H ${right_B}
|
|
245
|
+
m 5 -5 l -5 5 l 5 5`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!going_up) {
|
|
249
|
+
return `
|
|
250
|
+
M ${left_A} ${y_A}
|
|
251
|
+
H ${x_left + curve}
|
|
252
|
+
a ${curve} ${curve} 0 0 0 ${-curve} ${curve}
|
|
253
|
+
V ${y_B - curve}
|
|
254
|
+
a ${curve} ${curve} 0 0 1 ${-curve} ${curve}
|
|
255
|
+
H ${right_B}
|
|
256
|
+
m 5 -5 l -5 5 l 5 5`;
|
|
257
|
+
}
|
|
258
|
+
return `
|
|
259
|
+
M ${left_A} ${y_A}
|
|
260
|
+
H ${x_left + curve}
|
|
261
|
+
a ${curve} ${curve} 0 0 1 ${-curve} ${-curve}
|
|
262
|
+
V ${y_B + curve}
|
|
263
|
+
a ${curve} ${curve} 0 0 0 ${-curve} ${-curve}
|
|
264
|
+
H ${right_B}
|
|
265
|
+
m 5 -5 l -5 5 l 5 5`;
|
|
175
266
|
}
|
|
176
267
|
|
|
177
268
|
_get_connected_bars() {
|
|
@@ -220,6 +311,7 @@ export default class Arrow {
|
|
|
220
311
|
const bar = el.querySelector('.bar');
|
|
221
312
|
if (bar) bar.classList.add(bar_class);
|
|
222
313
|
});
|
|
314
|
+
this._show_label();
|
|
223
315
|
});
|
|
224
316
|
|
|
225
317
|
this.hit_element.addEventListener('mouseleave', () => {
|
|
@@ -227,8 +319,106 @@ export default class Arrow {
|
|
|
227
319
|
this.element.classList.remove('arrow-hover');
|
|
228
320
|
this._get_connected_bars().forEach(el => {
|
|
229
321
|
const bar = el.querySelector('.bar');
|
|
230
|
-
if (bar)
|
|
322
|
+
if (bar) {
|
|
323
|
+
bar.classList.remove('bar-arrow-hover');
|
|
324
|
+
if (!this.is_active) {
|
|
325
|
+
bar.classList.remove('bar-arrow-critical', 'bar-arrow-invalid');
|
|
326
|
+
}
|
|
327
|
+
}
|
|
231
328
|
});
|
|
329
|
+
if (!this.is_active && !this.is_hovered) this._hide_label();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
this.hit_element.addEventListener('click', (e) => {
|
|
333
|
+
e.stopPropagation();
|
|
334
|
+
if (this.is_active) {
|
|
335
|
+
this.gantt.set_active_arrow(null);
|
|
336
|
+
} else {
|
|
337
|
+
this.gantt.set_active_arrow(this);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
_get_type_abbr() {
|
|
343
|
+
const map = {
|
|
344
|
+
'finish-to-start': 'FS',
|
|
345
|
+
'start-to-start': 'SS',
|
|
346
|
+
'finish-to-finish': 'FF',
|
|
347
|
+
'start-to-finish': 'SF',
|
|
348
|
+
};
|
|
349
|
+
return map[this.dependency_type] || 'FS';
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
_show_label() {
|
|
353
|
+
if (this.label_element) return;
|
|
354
|
+
const abbr = this._get_type_abbr();
|
|
355
|
+
const w = 21;
|
|
356
|
+
const h = 20;
|
|
357
|
+
const { x: tip_x, y: tip_y, side } = this.label_pos;
|
|
358
|
+
const cx = side === 'left'
|
|
359
|
+
? tip_x - 10 - w / 2
|
|
360
|
+
: tip_x + 10 + w / 2;
|
|
361
|
+
const cy = tip_y;
|
|
362
|
+
|
|
363
|
+
this.label_element = createSVG('g', { class: 'arrow-type-label' });
|
|
364
|
+
const bg = createSVG('rect', {
|
|
365
|
+
x: cx - w / 2,
|
|
366
|
+
y: cy - h / 2,
|
|
367
|
+
width: w,
|
|
368
|
+
height: h,
|
|
369
|
+
rx: 3,
|
|
370
|
+
});
|
|
371
|
+
const text = createSVG('text', {
|
|
372
|
+
x: cx,
|
|
373
|
+
y: cy,
|
|
374
|
+
'dominant-baseline': 'middle',
|
|
375
|
+
'text-anchor': 'middle',
|
|
376
|
+
});
|
|
377
|
+
text.textContent = abbr;
|
|
378
|
+
this.label_element.appendChild(bg);
|
|
379
|
+
this.label_element.appendChild(text);
|
|
380
|
+
this.label_element.addEventListener('mouseenter', () => {
|
|
381
|
+
this.hit_element.dispatchEvent(new MouseEvent('mouseenter', { bubbles: false }));
|
|
382
|
+
});
|
|
383
|
+
this.label_element.addEventListener('mouseleave', () => {
|
|
384
|
+
this.hit_element.dispatchEvent(new MouseEvent('mouseleave', { bubbles: false }));
|
|
385
|
+
});
|
|
386
|
+
this.label_element.addEventListener('click', (e) => {
|
|
387
|
+
e.stopPropagation();
|
|
388
|
+
this.hit_element.dispatchEvent(new MouseEvent('click', { bubbles: false }));
|
|
389
|
+
});
|
|
390
|
+
this.gantt.layers.arrow.appendChild(this.label_element);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
_hide_label() {
|
|
394
|
+
if (this.label_element) {
|
|
395
|
+
this.label_element.remove();
|
|
396
|
+
this.label_element = null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
activate() {
|
|
401
|
+
this.is_active = true;
|
|
402
|
+
this.element.classList.add('arrow-active');
|
|
403
|
+
this._show_label();
|
|
404
|
+
const bar_class = this.is_invalid
|
|
405
|
+
? 'bar-arrow-invalid'
|
|
406
|
+
: this.is_critical
|
|
407
|
+
? 'bar-arrow-critical'
|
|
408
|
+
: 'bar-arrow-active';
|
|
409
|
+
this._get_connected_bars().forEach(el => {
|
|
410
|
+
const bar = el.querySelector('.bar');
|
|
411
|
+
if (bar) bar.classList.add(bar_class);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
deactivate() {
|
|
416
|
+
this.is_active = false;
|
|
417
|
+
this.element.classList.remove('arrow-active');
|
|
418
|
+
this._hide_label();
|
|
419
|
+
this._get_connected_bars().forEach(el => {
|
|
420
|
+
const bar = el.querySelector('.bar');
|
|
421
|
+
if (bar) bar.classList.remove('bar-arrow-active', 'bar-arrow-critical', 'bar-arrow-invalid');
|
|
232
422
|
});
|
|
233
423
|
}
|
|
234
424
|
|
|
@@ -248,6 +438,7 @@ export default class Arrow {
|
|
|
248
438
|
arrowClass = 'arrow-critical';
|
|
249
439
|
}
|
|
250
440
|
if (this.is_hovered) arrowClass += ' arrow-hover';
|
|
441
|
+
if (this.is_active) arrowClass += ' arrow-active';
|
|
251
442
|
this.element.setAttribute('class', arrowClass.trim());
|
|
252
443
|
}
|
|
253
444
|
}
|
package/src/bar.js
CHANGED
|
@@ -115,6 +115,7 @@ export default class Bar {
|
|
|
115
115
|
if (this.gantt.options.task_add_icon_position) {
|
|
116
116
|
this.draw_add_task_icon();
|
|
117
117
|
}
|
|
118
|
+
this.draw_connector_circles();
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
draw_bar() {
|
|
@@ -439,6 +440,48 @@ export default class Bar {
|
|
|
439
440
|
}
|
|
440
441
|
}
|
|
441
442
|
|
|
443
|
+
draw_connector_circles() {
|
|
444
|
+
this.$connector_start = null;
|
|
445
|
+
this.$connector_end = null;
|
|
446
|
+
if (!this.gantt.options.allow_dependency_creation) return;
|
|
447
|
+
if (this.gantt.options.readonly) return;
|
|
448
|
+
const isRTL = this.gantt.options.isRTL;
|
|
449
|
+
const cy = this.y + this.height / 2;
|
|
450
|
+
const start_cx = isRTL ? this.x + this.width : this.x;
|
|
451
|
+
const end_cx = isRTL ? this.x : this.x + this.width;
|
|
452
|
+
|
|
453
|
+
this.$connector_start = createSVG('circle', {
|
|
454
|
+
class: 'connector-circle connector-start',
|
|
455
|
+
'data-endpoint': 'start',
|
|
456
|
+
cx: start_cx,
|
|
457
|
+
cy,
|
|
458
|
+
r: 4,
|
|
459
|
+
append_to: this.handle_group,
|
|
460
|
+
});
|
|
461
|
+
this.$connector_end = createSVG('circle', {
|
|
462
|
+
class: 'connector-circle connector-end',
|
|
463
|
+
'data-endpoint': 'end',
|
|
464
|
+
cx: end_cx,
|
|
465
|
+
cy,
|
|
466
|
+
r: 4,
|
|
467
|
+
append_to: this.handle_group,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
update_connector_circles() {
|
|
472
|
+
if (!this.$connector_start) return;
|
|
473
|
+
const isRTL = this.gantt.options.isRTL;
|
|
474
|
+
const bx = this.$bar.getX();
|
|
475
|
+
const bw = this.$bar.getWidth();
|
|
476
|
+
const cy = this.y + this.height / 2;
|
|
477
|
+
const start_cx = isRTL ? bx + bw : bx;
|
|
478
|
+
const end_cx = isRTL ? bx : bx + bw;
|
|
479
|
+
this.$connector_start.setAttribute('cx', start_cx);
|
|
480
|
+
this.$connector_start.setAttribute('cy', cy);
|
|
481
|
+
this.$connector_end.setAttribute('cx', end_cx);
|
|
482
|
+
this.$connector_end.setAttribute('cy', cy);
|
|
483
|
+
}
|
|
484
|
+
|
|
442
485
|
bind() {
|
|
443
486
|
if (this.invalid) return;
|
|
444
487
|
this.setup_click_event();
|
|
@@ -653,6 +696,7 @@ export default class Bar {
|
|
|
653
696
|
|
|
654
697
|
this.update_progressbar_position();
|
|
655
698
|
this.update_arrow_position();
|
|
699
|
+
this.update_connector_circles();
|
|
656
700
|
}
|
|
657
701
|
|
|
658
702
|
update_label_position_on_horizontal_scroll({ x, sx }) {
|