Popular features/Grid controls
Loading live demo…from dhtmlxcode.com
Popular features
Grid controls
Render edit, add-child, duplicate, delete, and star-flag buttons in every grid row, each wired straight to the Gantt API.
gantt.config.date_format = "%Y-%m-%d";
gantt.config.row_height = 38;
gantt.config.bar_height = 24;
gantt.config.scale_height = 52;
gantt.config.scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "week", step: 1, format: "Week %W" }
];
let ICON = {
above: '<svg viewBox="0 0 16 16"><polyline points="4 10 8 6 12 10"/><polyline points="4 6 8 2 12 6"/></svg>',
below: '<svg viewBox="0 0 16 16"><polyline points="4 6 8 10 12 6"/><polyline points="4 10 8 14 12 10"/></svg>',
edit: '<svg viewBox="0 0 16 16"><path d="M2 11.5 10 3.5l2.5 2.5-8 8H2z"/><path d="M9 4.5 11.5 7"/></svg>',
add: '<svg viewBox="0 0 16 16"><line x1="8" y1="3" x2="8" y2="13"/><line x1="3" y1="8" x2="13" y2="8"/></svg>',
dup: '<svg viewBox="0 0 16 16"><rect x="5" y="5" width="8" height="8" rx="1.5"/><path d="M3 11V4a1 1 0 0 1 1-1h7"/></svg>',
del: '<svg viewBox="0 0 16 16"><polyline points="3 5 13 5"/><path d="M6 5V3h4v2"/><path d="M5 5l1 8h4l1-8"/></svg>',
star: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"><path d="M8 2l1.8 3.9 4.2.5-3.1 2.9.8 4.2L8 11.8 4.3 13.4l.8-4.2L2 6.3l4.2-.5z"/></svg>',
starF:'<svg viewBox="0 0 16 16" fill="currentColor" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"><path d="M8 2l1.8 3.9 4.2.5-3.1 2.9.8 4.2L8 11.8 4.3 13.4l.8-4.2L2 6.3l4.2-.5z"/></svg>'
};
function flagCell(task) {
return `<span class="flag ${task.important ? "on" : ""}" data-grid-action="toggle-flag">${task.important ? ICON.starF : ICON.star}</span>`;
}
function actionsCell() {
return `<span class="grid_actions">
<span class="act edit" title="Edit" data-grid-action="edit">${ICON.edit}</span>
<span class="act add" title="Add child" data-grid-action="add">${ICON.add}</span>
<span class="act above" title="Add task above" data-grid-action="add-sibling" data-position="above">${ICON.above}</span>
<span class="act below" title="Add task below" data-grid-action="add-sibling" data-position="below">${ICON.below}</span>
<span class="act dup" title="Duplicate" data-grid-action="duplicate">${ICON.dup}</span>
<span class="act del" title="Delete" data-grid-action="delete">${ICON.del}</span>
</span>`;
}
gantt.config.columns = [
{ name: "text", label: "Step", tree: true, width: 250, resize: true },
{ name: "flag", label: "", align: "center", width: 40, resize: true, template: flagCell },
{ name: "duration", label: "Days", align: "center", width: 56, resize: true },
{ name: "actions", label: `<div class="gantt_add"></div>`, align: "center", width: 164, resize: true, template: actionsCell }
];
gantt.templates.task_class = function (start, end, task) {
return task.important ? "is_important" : "";
};
/* control handlers */
function insertSibling(taskId, position) {
const referenceTask = gantt.getTask(taskId);
const taskIndex = gantt.getTaskIndex(taskId) + (position === "below" ? 1 : 0);
gantt.createTask({
text: "New task",
start_date: referenceTask.start_date,
duration: Math.max(referenceTask.duration, 1),
progress: 0
}, referenceTask.parent, taskIndex);
}
function editTask(taskId) {
gantt.showLightbox(taskId);
}
function addChildTask(taskId) {
gantt.createTask(null, taskId);
}
function duplicateTask(taskId) {
const task = gantt.getTask(taskId);
gantt.addTask({
text: task.text + " (copy)",
start_date: task.start_date,
duration: task.duration,
progress: 0
}, task.parent, gantt.getTaskIndex(taskId) + 1);
}
function deleteTask(taskId) {
gantt.confirm({
title: gantt.locale.labels.confirm_deleting_title,
text: gantt.locale.labels.confirm_deleting,
callback: function (isConfirmed) {
if (isConfirmed) {
gantt.deleteTask(taskId);
}
}
});
}
function toggleFlag(taskId) {
const task = gantt.getTask(taskId);
task.important = !task.important;
gantt.updateTask(taskId);
}
gantt.attachEvent("onTaskClick", function (taskId, event) {
const eventTarget = event.target instanceof Element ? event.target : null;
const controlElement = eventTarget ? eventTarget.closest("[data-grid-action]") : null;
if (!controlElement) {
return true;
}
event.stopPropagation();
const gridAction = controlElement.dataset.gridAction;
if (gridAction === "toggle-flag") {
toggleFlag(taskId);
} else if (gridAction === "edit") {
editTask(taskId);
} else if (gridAction === "add") {
addChildTask(taskId);
} else if (gridAction === "add-sibling") {
insertSibling(taskId, controlElement.dataset.position);
} else if (gridAction === "duplicate") {
duplicateTask(taskId);
} else if (gridAction === "delete") {
deleteTask(taskId);
}
return false;
});
/* data */
/* Anchored to today (not a fixed year) so the sample never looks stale. */
function addDays(base, n) {
let d = gantt.date.day_start(new Date(base));
return gantt.date.add(d, n, "day");
}
let TODAY = gantt.date.day_start(new Date());
let PLAN_START = addDays(TODAY, -14);
let data = {
tasks: [
{ id: 1, text: "New hire onboarding", type: "project", open: true, progress: 0.5 },
{ id: 10, text: "Before day 1", type: "project", parent: 1, open: true, progress: 1 },
{ id: 11, text: "Provision laptop", parent: 10, start_date: addDays(PLAN_START, 0), duration: 2, progress: 1, important: true },
{ id: 12, text: "Create accounts", parent: 10, start_date: addDays(PLAN_START, 2), duration: 1, progress: 1 },
{ id: 13, text: "Prepare workspace", parent: 10, start_date: addDays(PLAN_START, 2), duration: 1, progress: 1 },
{ id: 20, text: "Week 1", type: "project", parent: 1, open: true, progress: 0.5 },
{ id: 21, text: "Team intros", parent: 20, start_date: addDays(PLAN_START, 3), duration: 2, progress: 1 },
{ id: 22, text: "Codebase tour", parent: 20, start_date: addDays(PLAN_START, 7), duration: 3, progress: 0.5, important: true },
{ id: 23, text: "First PR", parent: 20, start_date: addDays(PLAN_START, 10), duration: 2, progress: 0 },
{ id: 24, text: "Product walkthrough", parent: 20, start_date: addDays(PLAN_START, 11), duration: 2, progress: 0 },
{ id: 30, text: "Week 2-4", type: "project", parent: 1, open: true, progress: 0.1 },
{ id: 31, text: "Own small feature", parent: 30, start_date: addDays(PLAN_START, 15), duration: 5, progress: 0.2, important: true },
{ id: 32, text: "Support rotation shadow", parent: 30, start_date: addDays(PLAN_START, 22), duration: 4, progress: 0 },
{ id: 33, text: "Architecture review", parent: 30, start_date: addDays(PLAN_START, 25), duration: 3, progress: 0 },
{ id: 40, text: "Day 30 review", parent: 1, type: "milestone", start_date: addDays(PLAN_START, 32) },
{ id: 50, text: "Weeks 6-8", type: "project", parent: 1, open: true, progress: 0.3 },
{ id: 51, text: "Own a feature end-to-end", parent: 50, start_date: addDays(PLAN_START, 35), duration: 5, progress: 0.6, important: true },
{ id: 52, text: "Mentor a new joiner", parent: 50, start_date: addDays(PLAN_START, 42), duration: 3, progress: 0 },
{ id: 53, text: "Cross-team collaboration", parent: 50, start_date: addDays(PLAN_START, 45), duration: 4, progress: 0 },
{ id: 54, text: "Performance check-in", parent: 50, start_date: addDays(PLAN_START, 49), duration: 1, progress: 0 },
{ id: 60, text: "Weeks 9-10", type: "project", parent: 1, open: true, progress: 0 },
{ id: 61, text: "Lead a small project", parent: 60, start_date: addDays(PLAN_START, 56), duration: 6, progress: 0, important: true },
{ id: 62, text: "Present at team demo", parent: 60, start_date: addDays(PLAN_START, 63), duration: 1, progress: 0 },
{ id: 63, text: "Write technical documentation", parent: 60, start_date: addDays(PLAN_START, 64), duration: 3, progress: 0 },
{ id: 64, text: "Shadow on-call rotation", parent: 60, start_date: addDays(PLAN_START, 67), duration: 2, progress: 0 },
{ id: 70, text: "Weeks 11-12", type: "project", parent: 1, open: true, progress: 0 },
{ id: 71, text: "Own a mid-size initiative", parent: 70, start_date: addDays(PLAN_START, 70), duration: 8, progress: 0, important: true },
{ id: 72, text: "Contribute to design review", parent: 70, start_date: addDays(PLAN_START, 78), duration: 2, progress: 0 },
{ id: 73, text: "Onboard the next new hire", parent: 70, start_date: addDays(PLAN_START, 80), duration: 3, progress: 0 },
{ id: 80, text: "90-day review", parent: 1, type: "milestone", start_date: addDays(PLAN_START, 84) }
],
links: [
{ id: 1, source: 11, target: 12, type: "0" },
{ id: 2, source: 21, target: 22, type: "0" },
{ id: 3, source: 22, target: 23, type: "0" },
{ id: 4, source: 23, target: 31, type: "0" },
{ id: 5, source: 31, target: 40, type: "0" },
{ id: 6, source: 33, target: 40, type: "0" },
{ id: 7, source: 40, target: 51, type: "0" },
{ id: 8, source: 54, target: 61, type: "0" },
{ id: 9, source: 64, target: 71, type: "0" },
{ id: 10, source: 73, target: 80, type: "0" }
]
};
gantt.init("gantt_here");
gantt.parse(data);<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Controls inside the grid: dhtmlxGantt</title>
<script src="../../codebase/dhtmlxgantt.js?v=10.0.0"></script>
<link rel="stylesheet" href="../../codebase/dhtmlxgantt.css?v=10.0.0">
<link rel="stylesheet" href="../common/demo-controls/dhx_controls.css">
<script src="../common/demo-controls/dhx_controls.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
<style>
body { display: flex; flex-direction: column; }
#gantt_here { flex: 1 1 auto; min-height: 0; width: 100%; }
.gantt_task_line { border: none; border-radius: 7px; box-shadow: 0 1px 2px rgba(16,24,40,.18); }
.gantt_task_line .gantt_task_progress { border-radius: 7px 0 0 7px; }
.gantt_task_line.gantt_project { border-radius: 5px; }
.gantt_task_line.is_important { box-shadow: 0 0 0 2px #f59e0b, 0 1px 2px rgba(16,24,40,.18); }
.grid_actions { display: inline-flex; gap: 2px; }
.act {
display: inline-flex; align-items: center; justify-content: center;
width: 24px; height: 24px; border-radius: 6px;
color: var(--dhx-muted); cursor: pointer;
opacity: .75; transition: opacity .12s, background .12s, color .12s;
}
.act:hover { opacity: 1; background: var(--dhx-surface-2); }
.act svg { width: 15px; height: 15px; stroke: currentColor; stroke-width: 1.7; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.act.above:hover,
.act.below:hover,
.act.edit:hover,
.act.add:hover,
.act.dup:hover { color: var(--dhx-info-ink); }
.act.del:hover { color: var(--dhx-danger-solid); }
.flag { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; cursor: pointer; color: var(--dhx-muted); }
.flag:hover { color: #f59e0b; }
.flag.on { color: #f59e0b; }
.flag svg { width: 15px; height: 15px; }
.head_add {
display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
color: var(--dhx-gantt-container-color); font: 600 12px var(--dhx-font);
}
</style>
</head>
<body>
<div id="gantt_here"></div>
</body>
</html>