From 3688be1acd99339e05ea14f86af846eb55cb55df Mon Sep 17 00:00:00 2001 From: hiawui Date: Tue, 28 Jun 2022 23:00:11 +0800 Subject: [PATCH] enable dragging to change the task order --- src/arrow.js | 14 ++--- src/bar.js | 15 +++++- src/index.js | 149 +++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 130 insertions(+), 48 deletions(-) diff --git a/src/arrow.js b/src/arrow.js index f1c6ad64..25cc306d 100644 --- a/src/arrow.js +++ b/src/arrow.js @@ -23,22 +23,14 @@ export default class Arrow { } const start_y = - this.gantt.options.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; + this.from_task.$bar.getY() + this.gantt.options.bar_height; const end_x = this.to_task.$bar.getX() - this.gantt.options.padding / 2; const end_y = - this.gantt.options.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; + 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; + this.from_task.$bar.getY() > this.to_task.$bar.getY(); const curve = this.gantt.options.arrow_curve; const clockwise = from_is_below_to ? 1 : 0; const curve_y = from_is_below_to ? -curve : curve; diff --git a/src/bar.js b/src/bar.js index 2a47f2c9..67a1b1ea 100644 --- a/src/bar.js +++ b/src/bar.js @@ -219,7 +219,7 @@ export default class Bar { }); } - update_bar_position({ x = null, width = null }) { + update_bar_position({ x = null, width = null, y = null }) { const bar = this.$bar; if (x) { // get all x values of parent task @@ -239,9 +239,12 @@ export default class Bar { if (width && width >= this.gantt.options.column_width) { this.update_attr(bar, 'width', width); } + if (y) { + this.update_attr(bar, 'y', y); + } this.update_label_position(); - this.update_handle_position(); this.update_progressbar_position(); + this.update_handle_position(); this.update_arrow_position(); } @@ -370,6 +373,7 @@ export default class Bar { update_progressbar_position() { if (this.invalid) return; this.$bar_progress.setAttribute('x', this.$bar.getX()); + this.$bar_progress.setAttribute('y', this.$bar.getY()); this.$bar_progress.setAttribute( 'width', this.$bar.getWidth() * (this.task.progress / 100) @@ -387,6 +391,7 @@ export default class Bar { label.classList.remove('big'); label.setAttribute('x', bar.getX() + bar.getWidth() / 2); } + label.setAttribute('y', bar.getY() + bar.getHeight() / 2); } update_handle_position() { @@ -395,9 +400,15 @@ export default class Bar { this.handle_group .querySelector('.handle.left') .setAttribute('x', bar.getX() + 1); + this.handle_group + .querySelector('.handle.left') + .setAttribute('y', bar.getY() + 1); this.handle_group .querySelector('.handle.right') .setAttribute('x', bar.getEndX() - 9); + this.handle_group + .querySelector('.handle.right') + .setAttribute('y', bar.getY() + 1); const handle = this.group.querySelector('.handle.progress'); handle && handle.setAttribute('points', this.get_progress_polygon_points()); diff --git a/src/index.js b/src/index.js index 6d005afa..f21f944c 100644 --- a/src/index.js +++ b/src/index.js @@ -87,6 +87,7 @@ export default class Gantt { popup_trigger: 'click', custom_popup_html: null, language: 'en', + sortable: false, }; this.options = Object.assign({}, default_options, options); } @@ -649,6 +650,26 @@ export default class Gantt { ); } + sort_bars() { + const changed_bars = []; + if (!this.bars) { + return changed_bars; + } + this.bars = this.bars.sort((b0, b1) => { + return b0.$bar.getY() - b1.$bar.getY(); + }); + + this.tasks = this.bars.map((b, i) => { + const task = b.task; + if (task._index !== i) { + changed_bars.push(b); + } + task._index = i; + return task; + }); + return changed_bars; + } + bind_bar_events() { let is_dragging = false; let x_on_start = 0; @@ -656,8 +677,13 @@ export default class Gantt { let is_resizing_left = false; let is_resizing_right = false; let parent_bar_id = null; - let bars = []; // instanceof Bar - this.bar_being_dragged = null; + let bars = []; // instanceof Bars, the dragged bar and its children + const min_y = this.options.header_height; + const max_y = + this.options.header_height + + this.tasks.length * + (this.options.bar_height + this.options.padding); + this.bar_being_dragged = null; // instanceof dragged bar function action_in_progress() { return is_dragging || is_resizing_left || is_resizing_right; @@ -684,16 +710,18 @@ export default class Gantt { parent_bar_id, ...this.get_all_dependent_tasks(parent_bar_id), ]; - bars = ids.map((id) => this.get_bar(id)); - - this.bar_being_dragged = parent_bar_id; - - bars.forEach((bar) => { + bars = ids.map((id) => { + const bar = this.get_bar(id); + if (parent_bar_id === id) { + this.bar_being_dragged = bar; + } const $bar = bar.$bar; $bar.ox = $bar.getX(); $bar.oy = $bar.getY(); $bar.owidth = $bar.getWidth(); $bar.finaldx = 0; + $bar.finaldy = 0; + return bar; }); }); @@ -702,53 +730,104 @@ export default class Gantt { const dx = e.offsetX - x_on_start; const dy = e.offsetY - y_on_start; + this.hide_popup(); + + // update the dragged bar + const bar_being_dragged = this.bar_being_dragged; + bar_being_dragged.$bar.finaldx = this.get_snap_position(dx); + if (is_resizing_left) { + bar_being_dragged.update_bar_position({ + x: + bar_being_dragged.$bar.ox + + bar_being_dragged.$bar.finaldx, + width: + bar_being_dragged.$bar.owidth - + bar_being_dragged.$bar.finaldx, + }); + } else if (is_resizing_right) { + bar_being_dragged.update_bar_position({ + width: + bar_being_dragged.$bar.owidth + + bar_being_dragged.$bar.finaldx, + }); + } else if (is_dragging) { + let y = bar_being_dragged.$bar.oy + dy; + if (y < min_y) { + y = min_y; + } else if (y > max_y) { + y = max_y; + } + bar_being_dragged.update_bar_position({ + x: + bar_being_dragged.$bar.ox + + bar_being_dragged.$bar.finaldx, + y: this.options.sortable ? y : null, + }); + } + + // update children bars.forEach((bar) => { + if (bar.task.id === parent_bar_id) { + return; + } const $bar = bar.$bar; $bar.finaldx = this.get_snap_position(dx); this.hide_popup(); if (is_resizing_left) { - if (parent_bar_id === bar.task.id) { - bar.update_bar_position({ - x: $bar.ox + $bar.finaldx, - width: $bar.owidth - $bar.finaldx, - }); - } else { - bar.update_bar_position({ - x: $bar.ox + $bar.finaldx, - }); - } - } else if (is_resizing_right) { - if (parent_bar_id === bar.task.id) { - bar.update_bar_position({ - width: $bar.owidth + $bar.finaldx, - }); - } + bar.update_bar_position({ + x: $bar.ox + $bar.finaldx, + }); } else if (is_dragging) { - bar.update_bar_position({ x: $bar.ox + $bar.finaldx }); + bar.update_bar_position({ + x: $bar.ox + $bar.finaldx, + }); } }); + + // update y pos + if ( + this.options.sortable && + is_dragging && + Math.abs(dy - bar_being_dragged.$bar.finaldy) > + bar_being_dragged.height + ) { + this.sort_bars().map((bar) => { + const y = bar.compute_y(); + if (bar.task.id === parent_bar_id) { + bar.$bar.finaldy = y - bar.$bar.oy; + return; + } + bar.update_bar_position({ y: y }); + }); + } }); document.addEventListener('mouseup', (e) => { + const dy = e.offsetY - y_on_start; if (is_dragging || is_resizing_left || is_resizing_right) { - bars.forEach((bar) => bar.group.classList.remove('active')); + bars.forEach((bar) => { + bar.group.classList.remove('active'); + + const $bar = bar.$bar; + if ($bar.finaldx) { + bar.date_changed(); + bar.set_action_completed(); + } + }); + const $bar = this.bar_being_dragged.$bar; + if (this.options.sortable && dy !== $bar.finaldy) { + this.bar_being_dragged.update_bar_position({ + y: $bar.oy + $bar.finaldy, + }); + } } + this.bar_being_dragged = null; is_dragging = false; is_resizing_left = false; is_resizing_right = false; }); - $.on(this.$svg, 'mouseup', (e) => { - this.bar_being_dragged = null; - bars.forEach((bar) => { - const $bar = bar.$bar; - if (!$bar.finaldx) return; - bar.date_changed(); - bar.set_action_completed(); - }); - }); - this.bind_bar_progress(); }