Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 86 additions & 87 deletions docs/guides/dnd.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,46 @@ sidebar_label: "Dragging Tasks within the Timeline"

# Dragging Tasks within the Timeline

Dragging allows users to quickly change the start (end) dates of the tasks, their duration.


Dragging allows users to quickly change the start (end) dates of the tasks, their duration.
By default, the drag-and-drop is enabled and the user can drag a task along its row in the timeline.

To customize the drag-and-drop behavior, use the following events:

- [onBeforeTaskDrag](api/event/onbeforetaskdrag.md) - to deny dragging of specific tasks
- [onTaskDrag](api/event/ontaskdrag.md) - to limit the area for dragging or to provide some other logic when the user drags a task
- [onTaskDrag](api/event/ontaskdrag.md) - to limit the area for dragging or to provide some other logic when the user drags a task
- [onAfterTaskDrag](api/event/onaftertaskdrag.md) - to postprocess tasks after they have been dragged to a new place

Let's consider typical cases when the default drag behavior needs customization:


1. [Denying dragging specific tasks](#denying-dragging-of-specific-tasks).
2. [Denying dragging tasks out of specific dates](#denying-dragging-tasks-out-of-specific-dates).
3. [Dragging children together with the parent](#dragging-children-together-with-the-parent).
4. [Dragging projects with subtasks](#draggingprojectswithsubtasks).
5. [Setting minimal task duration](#setting-minimal-task-duration).
6. [Autoscroll during tasks' dragging](#autoscrollduringtasksdragging).


## Denying dragging of specific tasks

To deny dragging of specific tasks, use the [onBeforeTaskDrag](api/event/onbeforetaskdrag.md) event:

~~~js
gantt.attachEvent("onBeforeTaskDrag", function(id, mode, e){
if(gantt.getGlobalTaskIndex(id)%2==0){
return false; //denies dragging if the global task index is odd
gantt.attachEvent("onBeforeTaskDrag", (taskId, dragMode, event) => {
if (gantt.getGlobalTaskIndex(taskId) % 2 === 0) {
return false; // denies dragging if the global task index is even
}
return true; //allows dragging if the global task index is even
return true; // allows dragging if the global task index is odd
});
~~~


## Denying dragging tasks out of specific dates

To deny dragging tasks out of specific dates, use the [onTaskDrag](api/event/ontaskdrag.md) event.
To deny dragging tasks out of specific dates, use the [onTaskDrag](api/event/ontaskdrag.md) event.

<p style="margin-top: 20px; font-weight: bold;"> The onTaskDrag event: </p>

<ul style="margin-top:5px;">
<li>Fires each time the user makes a drag movement with the mouse in the timeline area: moves, resizes a task or changes the task's progress.</li>
<li>The type of a drag movement is passed as the 2nd argument - <b>mode</b>.</li>
<li>The type of a drag movement is passed as the 2nd argument - <b>mode</b>.</li>
<li>All available values of the drag movement's type are stored in the [drag_mode](api/config/drag_mode.md) property.</li>
</ul>

Expand All @@ -59,79 +54,87 @@ To deny dragging tasks out of specific dates, use the [onTaskDrag](api/event/ont
<li>The user makes a move.</li>
<li>dhtmlxGantt recalculates the task's date according to the new position.</li>
<li>dhtmlxGantt fires the [onTaskDrag](api/event/ontaskdrag.md) event.</li>
<li>dhtmlxGantt re-renders the task in the Gantt chart.<br><i>As the [](api/event/ontaskdrag.md) event fires after dhtmlxGantt makes recalculation,
<li>dhtmlxGantt re-renders the task in the Gantt chart.<br><i>As the [onTaskDrag](api/event/ontaskdrag.md) event fires after dhtmlxGantt makes recalculation,
you can specify any custom values for the dragged task in the event's handler, without being afraid that these values will be overwritten. As a result, the task will be rendered in the desired position.</i></li>
</ol>

Let's assume that you want to forbid users to drag tasks out of the **"31 March, 2020 - 11 April, 2020"** interval.
Let's assume that you want to forbid users to drag tasks out of the **"31 March, 2028 - 11 April, 2028"** interval.

![custom_dnd](/img/custom_dnd.png)

Then, you can use the code as in:

[Denying dragging tasks out of interval - [31.03.2020, 11.04.2020]](Denying dragging tasks out of interval - [31.03.2020, 11.04.2020])
~~~js
var leftLimit = new Date(2020, 2 ,31), rightLimit = new Date(2020, 3 ,12);

gantt.attachEvent("onTaskDrag", function(id, mode, task, original){
var modes = gantt.config.drag_mode;
if(mode == modes.move || mode == modes.resize){

var diff = original.duration*(1000*60*60*24);

if(+task.end_date > +rightLimit){
const leftLimit = new Date(2028, 2, 31);
const rightLimit = new Date(2028, 3, 12);
const millisecondsInDay = 24 * 60 * 60 * 1000;

gantt.attachEvent("onTaskDrag", (taskId, dragMode, task, originalTask) => {
const dragModes = gantt.config.drag_mode;

if (dragMode === dragModes.move || dragMode === dragModes.resize) {
const taskDuration = originalTask.duration * millisecondsInDay;

if (+task.end_date > +rightLimit) {
task.end_date = new Date(rightLimit);
if(mode == modes.move)
task.start_date = new Date(task.end_date - diff);
if (dragMode === dragModes.move) {
task.start_date = new Date(task.end_date - taskDuration);
}
if(+task.start_date < +leftLimit){
}

if (+task.start_date < +leftLimit) {
task.start_date = new Date(leftLimit);
if(mode == modes.move)
task.end_date = new Date(+task.start_date + diff);
if (dragMode === dragModes.move) {
task.end_date = new Date(+task.start_date + taskDuration);
}
}
}
});
~~~


[Drag parent task with its children](https://docs.dhtmlx.com/gantt/samples/08_api/05_limit_drag_dates.html)


## Dragging children together with the parent

To allow dragging children when the user is dragging their parent's task, use the [onTaskDrag](api/event/ontaskdrag.md) event (see more on the event [above](guides/dnd.md#denying-dragging-tasks-out-of-specific-dates)):

~~~js
gantt.attachEvent("onTaskDrag", function(id, mode, task, original){
var modes = gantt.config.drag_mode;
if(mode == modes.move){
var diff = task.start_date - original.start_date;
gantt.eachTask(function(child){
child.start_date = new Date(+child.start_date + diff);
child.end_date = new Date(+child.end_date + diff);
gantt.attachEvent("onTaskDrag", (taskId, dragMode, task, originalTask) => {
const dragModes = gantt.config.drag_mode;

if (dragMode === dragModes.move) {
const dateShift = task.start_date - originalTask.start_date;
gantt.eachTask((child) => {
child.start_date = new Date(+child.start_date + dateShift);
child.end_date = new Date(+child.end_date + dateShift);
gantt.refreshTask(child.id, true);
},id );
}, taskId);
}
});
//rounds positions of the child items to scale
gantt.attachEvent("onAfterTaskDrag", function(id, mode, e){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
var state = gantt.getState();
gantt.eachTask(function(child){

// rounds positions of the child items to scale
gantt.attachEvent("onAfterTaskDrag", (taskId, dragMode, event) => {
const dragModes = gantt.config.drag_mode;

if (dragMode === dragModes.move) {
const ganttState = gantt.getState();
gantt.eachTask((child) => {
child.start_date = gantt.roundDate({
date:child.start_date,
unit:state.scale_unit,
step:state.scale_step
});
child.end_date = gantt.calculateEndDate(child.start_date,
child.duration, gantt.config.duration_unit);
gantt.updateTask(child.id);
},id );
date: child.start_date,
unit: ganttState.scale_unit,
step: ganttState.scale_step
});
child.end_date = gantt.calculateEndDate(
child.start_date,
child.duration,
gantt.config.duration_unit
);
gantt.updateTask(child.id);
}, taskId);
}
});
~~~

**Related sample**: [Drag parent task with its children](https://docs.dhtmlx.com/gantt/samples/08_api/05_limit_drag_dates.html)

## Dragging projects with subtasks {#draggingprojectswithsubtasks}

:::info
Expand All @@ -145,16 +148,13 @@ You can enable drag and drop of projects using the [drag_project](api/config/dra
gantt.config.drag_project = true;
~~~


[Draggable projects](https://docs.dhtmlx.com/gantt/samples/08_api/19_draggable_projects.html)

**Related sample**: [Draggable projects](https://docs.dhtmlx.com/gantt/samples/08_api/19_draggable_projects.html)

## Dragging dependent tasks together with independent tasks

There are several ways of implementing tasks moving with their dependent tasks.
You can read about all of them in a separate article [Dragging Tasks Together with Their Dependent Tasks](guides/dragging-dependent-tasks.md).


## Setting minimal task duration

Minimal task duration can be specified via the [min_duration](api/config/min_duration.md) setting.
Expand All @@ -164,19 +164,19 @@ The option defines the minimum size of the task that can be set during resizing
The value is set in milliseconds:
~~~js
// 1 day
gantt.config.min_duration = 24*60*60*1000;
gantt.config.min_duration = 24 * 60 * 60 * 1000;

//OR
// OR

// 1 hour
gantt.config.min_duration = 60*60*1000;
gantt.config.min_duration = 60 * 60 * 1000;
~~~

## Autoscroll during tasks' dragging {#autoscrollduringtasksdragging}

If you have a large dataset in the Gantt chart, you often need to drag a task to a new distant position or set links between tasks located at a significant distance.

In this case the **autoscroll** functionality is of great help. It is enabled by default, but you can manage this behavior via
In this case the **autoscroll** functionality is of great help. It is enabled by default, but you can manage this behavior via
the [autoscroll](api/config/autoscroll.md) configuration option.

~~~js
Expand All @@ -189,7 +189,7 @@ Besides, you can adjust the speed of autoscrolling in milliseconds with the help
~~~js
gantt.config.autoscroll = true;
gantt.config.autoscroll_speed = 50;

gantt.init("gantt_here");
~~~

Expand All @@ -201,27 +201,28 @@ If you want to prevent certain tasks from being resized, there are two things yo
In order to do this, you need to use the **task_class** template to add an extra CSS class to the required items so that you could locate them via the selector:

~~~js
gantt.templates.task_class = function(start, end, task){
if(task.no_resize) { // no_resize is a custom property used for the demonstration
gantt.templates.task_class = (startDate, endDate, task) => {
if (task.no_resize) { // no_resize is a custom property used for the demonstration
return "no_resize";
}
return "";
};
~~~

Then, you can hide the resize handles using the following CSS:

~~~css
.no_resize .gantt_task_drag{
display: none !important;
.no_resize .gantt_task_drag {
display: none !important;
}
~~~

2. Prevent drag and drop from code using the [onBeforeTaskDrag](api/event/onbeforetaskdrag.md) event.
Returning *false* from the handler will prevent resizing:

~~~js
gantt.attachEvent("onBeforeTaskDrag", function(id, mode, e){
if(mode === "resize" && gantt.getTask(id).no_resize){
gantt.attachEvent("onBeforeTaskDrag", (taskId, dragMode, event) => {
if (dragMode === "resize" && gantt.getTask(taskId).no_resize) {
return false;
}
return true;
Expand All @@ -235,9 +236,9 @@ The ["resize"](api/event/onbeforetaskdrag.md) mode of drag and drop means that t
If you need to find out which date the user is modifying by the resize, you can use the **gantt.getState().drag_from_start** flag:

~~~js
gantt.attachEvent("onBeforeTaskDrag", function(id, mode, e){
if(mode === "resize"){
if(gantt.getState().drag_from_start === true) {
gantt.attachEvent("onBeforeTaskDrag", (taskId, dragMode, event) => {
if (dragMode === "resize") {
if (gantt.getState().drag_from_start === true) {
// changing the start date of a task
} else {
// changing the end date of a task
Expand All @@ -251,39 +252,37 @@ gantt.attachEvent("onBeforeTaskDrag", function(id, mode, e){

You can locate resize handles using the following selectors:

- .gantt_task_drag[data-bind-property="start_date"]
- .gantt_task_drag[data-bind-property="end_date"]
- `.gantt_task_drag[data-bind-property="start_date"]`
- `.gantt_task_drag[data-bind-property="end_date"]`

The following CSS can be used for disabling resizing of start dates of tasks:

~~~css
.gantt_task_drag[data-bind-property="start_date"]{
display: none !important;
.gantt_task_drag[data-bind-property="start_date"] {
display: none !important;
}
~~~

Similarly, preventing resizing of the end dates looks like this:

~~~css
.gantt_task_drag[data-bind-property="end_date"]{
display: none !important;
.gantt_task_drag[data-bind-property="end_date"] {
display: none !important;
}
~~~

Another way to do this is use the [onBeforeTaskDrag](api/event/onbeforetaskdrag.md) event.
Returning *false* from the handler will prevent resizing:

~~~js
gantt.attachEvent("onBeforeTaskDrag", function(id, mode, e){
if(mode === "resize"){
if(gantt.getState().drag_from_start === true) {
return false;
gantt.attachEvent("onBeforeTaskDrag", (taskId, dragMode, event) => {
if (dragMode === "resize") {
if (gantt.getState().drag_from_start === true) {
return false;
} else {
// changing the end date of a task
// changing the end date of a task
}
}
return true;
});
~~~


Binary file modified static/img/custom_dnd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.