From 2cd0d39ee90d27481b61d26d0699845f03fbbef6 Mon Sep 17 00:00:00 2001 From: "Stephen J. Maher" Date: Thu, 24 Apr 2025 20:39:59 +0100 Subject: [PATCH 1/3] adds functionality to move tasks between rows --- src/arrow.js | 12 ++-------- src/bar.js | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/index.js | 33 ++++++++++++++++++++++++---- 3 files changed, 91 insertions(+), 16 deletions(-) diff --git a/src/arrow.js b/src/arrow.js index da6925c60..d089eb35b 100644 --- a/src/arrow.js +++ b/src/arrow.js @@ -24,19 +24,11 @@ export default class Arrow { start_x -= 10; let start_y = - this.gantt.config.header_height + - this.gantt.options.bar_height + - (this.gantt.options.padding + this.gantt.options.bar_height) * - this.from_task.task._index + - this.gantt.options.padding / 2; + this.from_task.$bar.getY() + this.gantt.options.bar_height let end_x = this.to_task.$bar.getX() - 13; let end_y = - this.gantt.config.header_height + - this.gantt.options.bar_height / 2 + - (this.gantt.options.padding + this.gantt.options.bar_height) * - this.to_task.task._index + - this.gantt.options.padding / 2; + this.to_task.$bar.getY() + this.gantt.options.bar_height / 2; const from_is_below_to = this.from_task.task._index > this.to_task.task._index; diff --git a/src/bar.js b/src/bar.js index 1315200ca..462ef0fc3 100644 --- a/src/bar.js +++ b/src/bar.js @@ -418,7 +418,7 @@ export default class Bar { }); } - update_bar_position({ x = null, width = null }) { + update_bar_position({ x = null, y = null, width = null }) { const bar = this.$bar; if (x) { @@ -433,6 +433,10 @@ export default class Bar { this.x = x; this.$date_highlight.style.left = x + 'px'; } + if (y) { + this.update_attr(bar, 'y', y); + this.y = y; + } if (width > 0) { this.update_attr(bar, 'width', width); this.$date_highlight.style.width = width + 'px'; @@ -441,6 +445,7 @@ export default class Bar { this.update_label_position(); this.update_handle_position(); this.date_changed(); + this.task_index_changed(); this.compute_duration(); if (this.gantt.options.show_expected_progress) { @@ -508,6 +513,23 @@ export default class Bar { ]); } + task_index_changed() { + let changed = false; + const new_index = this.compute_task_index(); + const old_index = this.task._index; + if (this.task._index != new_index) { + changed = true; + this.task._index = new_index; + } + + if (!changed) return; + + this.gantt.trigger_event('task_index_change', [ + this.task, + new_index, + ]); + } + progress_changed() { this.task.progress = this.compute_progress(); this.gantt.trigger_event('progress_change', [ @@ -540,6 +562,19 @@ export default class Bar { return { new_start_date, new_end_date }; } + compute_task_index() { + const bar = this.$bar; + let new_index = + ( + bar.getY() + - this.gantt.config.header_height + - this.gantt.options.padding / 2 + ) / + (bar.getHeight() + this.gantt.options.padding); + + return new_index; + } + compute_progress() { this.progress_width = this.$bar_progress.getWidth(); this.x = this.$bar_progress.getBBox().x; @@ -659,6 +694,7 @@ export default class Bar { update_expected_progressbar_position() { if (this.invalid) return; this.$expected_bar_progress.setAttribute('x', this.$bar.getX()); + this.$expected_bar_progress.setAttribute('y', this.$bar.getY()); this.compute_expected_progress(); this.$expected_bar_progress.setAttribute( 'width', @@ -671,6 +707,7 @@ export default class Bar { update_progressbar_position() { if (this.invalid || this.gantt.options.readonly) return; this.$bar_progress.setAttribute('x', this.$bar.getX()); + this.$bar_progress.setAttribute('y', this.$bar.getY()); this.$bar_progress.setAttribute( 'width', @@ -694,8 +731,13 @@ export default class Bar { img.setAttribute('x', bar.getEndX() + padding); img_mask.setAttribute('x', bar.getEndX() + padding); label.setAttribute('x', bar.getEndX() + x_offset_label_img); + + img.setAttribute('y', bar.getY() + bar.getHeight()/ 2); + img_mask.setAttribute('y', bar.getY() + bar.getHeight()/ 2); + label.setAttribute('y', bar.getY() + bar.getHeight()/ 2); } else { label.setAttribute('x', bar.getEndX() + padding); + label.setAttribute('y', bar.getY() + bar.getHeight()/ 2); } } else { label.classList.remove('big'); @@ -706,11 +748,16 @@ export default class Bar { 'x', bar.getX() + barWidth / 2 + x_offset_label_img, ); + + img.setAttribute('y', bar.getY() + bar.getHeight()/ 2); + img_mask.setAttribute('y', bar.getY() + bar.getHeight()/ 2); + label.setAttribute('y', bar.getY() + bar.getHeight()/ 2); } else { label.setAttribute( 'x', bar.getX() + barWidth / 2 - labelWidth / 2, ); + label.setAttribute('y', bar.getY() + bar.getHeight()/ 2); } } } @@ -721,11 +768,22 @@ export default class Bar { this.handle_group .querySelector('.handle.left') .setAttribute('x', bar.getX()); + this.handle_group + .querySelector('.handle.left') + .setAttribute('y', bar.getY() + bar.getHeight() / 4); this.handle_group .querySelector('.handle.right') .setAttribute('x', bar.getEndX()); + this.handle_group + .querySelector('.handle.right') + .setAttribute('y', bar.getY() + bar.getHeight() / 4); const handle = this.group.querySelector('.handle.progress'); - handle && handle.setAttribute('cx', this.$bar_progress.getEndX()); + handle + && handle.setAttribute('cx', this.$bar_progress.getEndX()) + && handle.setAttribute( + 'cy', + this.$bar_progress.getY() + this.$bar_progress.getHeight() / 2 + ); } update_arrow_position() { diff --git a/src/index.js b/src/index.js index 054d3e2ce..dce92ac8b 100644 --- a/src/index.js +++ b/src/index.js @@ -1088,11 +1088,13 @@ export default class Gantt { if (e.target.classList.contains('grid-row')) this.unselect_all(); }; - let pos = 0; + let posx = 0; + let posy = 0; $.on(this.$svg, 'mousemove', '.bar-wrapper, .handle', (e) => { if ( this.bar_being_dragged === false && - Math.abs((e.offsetX || e.layerX) - pos) > 10 + (Math.abs((e.offsetX || e.layerX) - posx) > 10 || + Math.abs((e.offsetY || e.layerY) - posy) > 10) ) this.bar_being_dragged = true; }); @@ -1127,7 +1129,8 @@ export default class Gantt { bars = ids.map((id) => this.get_bar(id)); this.bar_being_dragged = false; - pos = x_on_start; + posx = x_on_start; + posy = y_on_start; bars.forEach((bar) => { const $bar = bar.$bar; @@ -1135,6 +1138,7 @@ export default class Gantt { $bar.oy = $bar.getY(); $bar.owidth = $bar.getWidth(); $bar.finaldx = 0; + $bar.finaldy = 0; }); }); @@ -1278,6 +1282,7 @@ export default class Gantt { $.on(this.$svg, 'mousemove', (e) => { if (!action_in_progress()) return; const dx = (e.offsetX || e.layerX) - x_on_start; + const dy = (e.offsetY || e.layerY) - y_on_start; bars.forEach((bar) => { const $bar = bar.$bar; @@ -1305,7 +1310,11 @@ export default class Gantt { !this.options.readonly && !this.options.readonly_dates ) { - bar.update_bar_position({ x: $bar.ox + $bar.finaldx }); + $bar.finaldy = this.get_snap_row_position(dy, $bar.oy); + bar.update_bar_position({ + x: $bar.ox + $bar.finaldx, + y: $bar.oy + $bar.finaldy + }); } }); }); @@ -1335,6 +1344,7 @@ export default class Gantt { bind_bar_progress() { let x_on_start = 0; + let y_on_start = 0; let is_resizing = null; let bar = null; let $bar_progress = null; @@ -1466,6 +1476,21 @@ export default class Gantt { return final_pos - ox; } + get_snap_row_position(dy, oy) { + let snap_height = this.options.bar_height + this.options.padding; + const rem = dy % snap_height; + + let final_dy = + dy - + rem + + (rem < snap_height * 2 + ? 0 + : snap_height); + let final_pos = oy + final_dy; + + return final_pos - oy; + } + get_ignored_region(pos, drn = 1) { if (drn === 1) { return this.config.ignored_positions.filter((val) => { From 8360abefde7f9834fa04f8245b132f1294b9126e Mon Sep 17 00:00:00 2001 From: "Stephen J. Maher" Date: Fri, 25 Apr 2025 14:55:35 +0100 Subject: [PATCH 2/3] only move parent bar in the y direction --- src/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index dce92ac8b..2bce8d47c 100644 --- a/src/index.js +++ b/src/index.js @@ -1310,11 +1310,16 @@ export default class Gantt { !this.options.readonly && !this.options.readonly_dates ) { - $bar.finaldy = this.get_snap_row_position(dy, $bar.oy); bar.update_bar_position({ x: $bar.ox + $bar.finaldx, - y: $bar.oy + $bar.finaldy }); + if (parent_bar_id === bar.task.id) { + $bar.finaldy = this.get_snap_row_position(dy, $bar.oy); + bar.update_bar_position({ + x: $bar.ox + $bar.finaldx, + y: $bar.oy + $bar.finaldy + }); + } } }); }); From c62a0a7f4c25e1ded06512b1d7a3f2b69068ab35 Mon Sep 17 00:00:00 2001 From: "Stephen J. Maher" Date: Fri, 25 Apr 2025 14:55:53 +0100 Subject: [PATCH 3/3] fixes arrow drawing when task from below --- src/arrow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arrow.js b/src/arrow.js index d089eb35b..b1c437f0f 100644 --- a/src/arrow.js +++ b/src/arrow.js @@ -72,7 +72,7 @@ export default class Arrow { this.path = ` M ${start_x} ${start_y} V ${offset} - a ${curve} ${curve} 0 0 ${clockwise} ${curve} ${curve} + a ${curve} ${curve} 0 0 ${clockwise} ${curve} ${curve_y} L ${end_x} ${end_y} m -5 -5 l 5 5