При использовании стандартной конфигурации макета, переключение грида или диаграммы выполняется изменением параметров show_grid или show_chart, после чего необходимо вызвать метод render() для обновления отображения.
function toggleGrid(){
gantt.config.show_grid = !gantt.config.show_grid;
gantt.render();
}
Related sample: Gantt. Переключение грида (стандартный макет)
function toggleChart(){
gantt.config.show_chart = !gantt.config.show_chart;
gantt.render();
}
Related sample: Gantt. Переключение временной шкалы (стандартный макет)
Для пользовательских макетов необходимо создать отдельные макеты с гридом и без него или с временной шкалой и без неё. Переключение между ними осуществляется обновлением параметра gantt.config.layout и инициализацией через метод init():
let showGrid = true;
function toggleGrid() {
showGrid = !showGrid;
if (showGrid) {
gantt.config.layout = gridAndChart; // макет с гридом и временной шкалой
}
else {
gantt.config.layout = onlyChart; // макет только с временной шкалой
}
gantt.init("gantt_here");
}
Related sample: Gantt. Переключение грида (пользовательский макет)
let showChart = true;
function toggleChart() {
showChart = !showChart;
if (showChart) {
gantt.config.layout = gridAndChart; // макет с гридом и временной шкалой
}
else {
gantt.config.layout = onlyGrid; // макет только с гридом
}
gantt.init("gantt_here");
}
Related sample: Gantt. Переключение временной шкалы (пользовательский макет)
Аналогично переключению грида или временной шкалы, необходимо подготовить несколько макетов с ресурсами и без них. Для переключения между ними обновите параметр gantt.config.layout и вызовите метод init() для применения изменений:
let resourceChart = true;
function layoutChange() {
resourceChart = !resourceChart;
if (resourceChart) {
gantt.config.layout = resourceLayout;
}
else {
gantt.config.layout = noresourceLayout;
}
gantt.init("gantt_here");
};
Related sample: Gantt. Переключение диаграммы загрузки ресурсов
let histogramView = true;
function layoutChange() {
histogramView = !histogramView;
if (histogramView) {
gantt.config.layout = histogramLayout;
}
else {
gantt.config.layout = simpleLayout;
}
gantt.init("gantt_here");
};
Related sample: Gantt. Переключение гистограммы ресурсов
Другой подход — динамическое формирование макета с использованием layout views и повторная инициализация Gantt для обновления отображения:
Related sample: Gantt. Генерация макета
Бесконечная прокрутка может быть реализована разными способами, но обычно это связано с изменением отображаемого диапазона дат через параметры gantt.config.start_date и gantt.config.end_date:
Отслеживая позицию прокрутки, можно расширять диапазон дат при приближении пользователя к краям шкалы. Чтобы избежать проблем с производительностью, перерисовку Gantt рекомендуется выполнять с задержкой через timeout:
gantt.init("gantt_here");
gantt.parse(tasks);
gantt.attachEvent("onGanttScroll", function (left, top) {
const left_date = gantt.dateFromPos(left)
const right_date = gantt.dateFromPos(left + gantt.$task.offsetWidth)
gantt.config.start_date = gantt.config.start_date || gantt.getState().min_date;
gantt.config.end_date = gantt.config.end_date || gantt.getState().max_date;
const min_allowed_date = gantt.date.add(gantt.config.start_date, 1, "day");
const max_allowed_date = gantt.date.add(gantt.config.end_date, -2, "day");
let repaint = false;
if (+left_date <= +min_allowed_date) {
gantt.config.start_date = gantt.date.add(gantt.config.start_date, -2, "day");
repaint = true;
}
if (+right_date >= +max_allowed_date) {
gantt.config.end_date = gantt.date.add(gantt.config.end_date, 2, "day");
repaint = true;
}
if (repaint) {
setTimeout(function () {
gantt.render()
gantt.showDate(left_date)
}, 20)
}
});
Related sample: Gantt. Бесконечная прокрутка с полосой прокрутки
Определяя текущую позицию прокрутки при перетаскивании временной шкалы, можно расширять диапазон дат, если прокрутка приближается к началу или концу шкалы:
gantt.attachEvent("onMouseMove", function (id, e) {
if (!gantt.getState().drag_id && e.buttons == 1) {
const left_date = gantt.dateFromPos(gantt.getScrollState().x);
const right_date = gantt.dateFromPos(
gantt.getScrollState().x + gantt.$task.offsetWidth - 1
);
if (left_date && +left_date <= +gantt.config.start_date) {
gantt.config.start_date = gantt.date.add(gantt.config.start_date, -1, 'day');
gantt.render();
}
if (right_date && +gantt.config.end_date < +gantt.date.add(right_date, 1, 'day')) {
gantt.config.end_date = gantt.date.add(gantt.config.end_date, 1, 'day');
gantt.render();
}
}
});
Related sample: Gantt. Бесконечная прокрутка при перетаскивании временной шкалы
Если диапазон дат не задан явно, вызов метода render() при перетаскивании задачи к краям временной шкалы позволяет сохранять видимость диапазона:
gantt.init("gantt_here");
gantt.parse(tasks);
gantt.attachEvent("onTaskDrag", function (id, mode, task, original) {
if (task.start_date <= gantt.getState().min_date ||
task.end_date >= gantt.getState().max_date) {
gantt.render()
}
});
Если диапазон дат задан явно, его необходимо обновлять при перетаскивании задач к краям:
gantt.init("gantt_here");
gantt.parse(tasks);
gantt.config.start_date = new Date(2025, 02, 28)
gantt.config.end_date = new Date(2025, 03, 10)
gantt.render();
gantt.attachEvent("onTaskDrag", function (id, mode, task, original) {
if (+task.start_date <= +gantt.config.start_date) {
gantt.config.start_date = gantt.date.add(
gantt.config.start_date, -1, gantt.config.duration_unit
);
gantt.render()
}
if (+task.end_date >= +gantt.config.end_date) {
gantt.config.end_date = gantt.date.add(
gantt.config.end_date, 1, gantt.config.duration_unit
);
gantt.render()
}
});
Related sample: Gantt. Бесконечная прокрутка при перетаскивании задачи (явные настройки диапазона)
Определяя момент, когда прокрутка достигает последней видимой задачи с помощью события onGanttScroll, можно динамически загружать дополнительные задачи методом parse():
gantt.attachEvent("onGanttScroll", function (left, top) {
const visibleTasks = gantt.getVisibleTaskCount();
const lastVisibleTask = gantt.getTaskByIndex(visibleTasks - 1)
if (gantt.getTaskRowNode(lastVisibleTask.id)) {
const tasks = load_tasks()
gantt.parse(tasks);
}
});
Related sample: Gantt. Динамическая загрузка данных
Методы open() и close() позволяют разворачивать или сворачивать отдельные задачи. Для применения ко всем задачам можно использовать их совместно с функцией eachTask(). Обертывание операции в batchUpdate() гарантирует, что диаграмма будет перерисована только один раз:
function collapseAll() {
gantt.batchUpdate(function () {
gantt.eachTask(function (task) {
gantt.close(task.id)
})
})
}
function expandAll() {
gantt.batchUpdate(function () {
gantt.eachTask(function (task) {
gantt.open(task.id)
})
})
}
Related sample: Gantt. Кнопки свернуть/развернуть в заголовке Gantt
Related sample: Gantt. Свернуть/развернуть все задачи
Для отображения многострочного текста в заголовках или ячейках грида необходимо применить специальные CSS-стили.
Для заголовка грида:
.gantt_grid_head_text{
line-height: 12px;
white-space:normal;
}
Related sample: Gantt. Многострочный текст в заголовке грида
Для ячеек грида:
.gantt_tree_content, .gantt_task_content{
line-height: 12px;
white-space:normal;
overflow-wrap: break-word;
}
Related sample: Gantt. Многострочный текст в ячейках грида и временной шкале
Related sample: Gantt. Многострочный текст в ячейках столбца грида
Чтобы добавить пользовательский столбец, измените параметр gantt.config.columns. Если указать свойство name, Gantt отобразит соответствующее значение свойства задачи. Также можно использовать функцию template(), чтобы возвращать кастомные данные или HTML-элементы.
gantt.config.columns = [
/*
другие столбцы
*/
{
name: "progress", label: "Progress", width: 50, resize: true, align: "center",
template: function (task) {
return Math.round(task.progress * 100) + "%"
}
},
/*
другие столбцы
*/
];
Related sample: Gantt. Пользовательский столбец с шаблоном для прогресса задачи
Related sample: Gantt. Пользовательский столбец с шаблоном для кнопок действий
Создать пользовательскую кнопку добавления можно через определение пользовательского столбца в параметре gantt.config.columns. Имя столбца не должно быть add, чтобы не активировать стандартный столбец добавления. С помощью функции template можно вернуть любой HTML-контент, например, кнопку, и назначить обработчик события для добавления задачи.
gantt.config.columns = [
/*
другие столбцы
*/
{
name: "add_tasks", label: "+", width: 50, resize: true, align: "center",
template: function (task) {
return `<button onclick='addTask(${task.id})';>`
}
},
];
Related sample: Gantt. Пользовательские столбцы с шаблонами для кнопок добавления (+)
Чтобы добавить пользовательскую шкалу, начните с создания пользовательской единицы шкалы времени и реализуйте логику вычисления дат.
Пример пользовательской шкалы, отображающей рабочие смены (06:30, 18:30):
gantt.date.custom_scale_start = function (date) {
return date;
};
gantt.date.add_custom_scale = function (date, inc) {
let next = new Date(date)
if (!next.getMinutes()) {
gantt.date.day_start(next)
next = gantt.date.add(next, 6, "hour");
next = gantt.date.add(next, 30, "minute");
}
else {
next = gantt.date.add(next, 12 * inc, "hour");
}
return next
};
gantt.config.scales = [
{ unit: "day", step: 1, date: "%d" },
{ unit: "custom_scale", step: 1, date: "%H:%i" },
];
Related sample: Gantt. Custom work shift hours on the scale
Еще один пример — пользовательская шкала, где вместо дней используются числа:
gantt.config.scales = [
{
unit: "day", step: 1, format: function (date) {
return gantt.getScale().trace_indexes[+date] + 1
}
}
]
Related sample: Gantt. Numbers of days on the scale
Пример пользовательской шкалы для 5-дневных рабочих недель:
const weekScaleTemplate = function (date) {
const dateToStr = gantt.date.date_to_str("%d");
const endDate = gantt.date.add(gantt.date.add(date, 5, "day"), -1, "day");
return dateToStr(date) + " - " + dateToStr(endDate);
};
gantt.date.five_days_start = function (date) {
return date;
};
gantt.date.add_five_days = function (date, inc) {
if (date.getDay() == 0 || date.getDay() == 6) {
return gantt.date.add(date, 1 * inc, "day");
}
gantt.date.week_start(date);
return gantt.date.add(date, 5 * inc, "day");
};
gantt.config.scales = [
{ unit: "month", step: 1, format: "%F, %Y" },
{ unit: "five_days", step: 1, format: weekScaleTemplate },
];
gantt.ignore_time = function (date) {
return date.getDay() == 0 || date.getDay() == 6;
};
Related sample: 5-day work weeks on the scale
Пример пользовательской шкалы, отображающей недели года, где номер недели начинается с первого дня года:
gantt.date.custom_week_start = function (date) {
return date;
};
gantt.date.add_custom_week = function (date, inc) {
const year_start = new Date(date);
gantt.date.year_start(year_start);
const week_number = Math.round(gantt.calculateDuration(year_start, date) / 7);
const next_week = gantt.date.add(year_start, week_number + 1, "week");
if (next_week.getYear() != date.getYear()) {
gantt.date.year_start(next_week)
}
return next_week;
};
const custom_week_template = function (date) {
const year_start = gantt.date.year_start(new Date(date));
const week_number = Math.round(gantt.calculateDuration(year_start, date) / 7) + 1;
return "Week:" + week_number
}
gantt.config.scales = [
{ unit: 'custom_week', step: 1, template: custom_week_template },
{ unit: 'day', step: 1, format: "%d, %M" },
];
Related sample: Gantt. Weeks of the year on the scale
Метод copy() можно использовать для создания глубокой копии объекта задачи. После копирования вы можете назначить склонированной задаче новый ID и добавить ее с помощью методов addTask() или createTask().
Ниже приведен пример добавления кнопки для клонирования задачи:
function clone_task(id) {
const task = gantt.getTask(id);
const clone = gantt.copy(task);
clone.id = +(new Date());
gantt.addTask(clone, clone.parent, clone.$index)
}
gantt.config.columns = [
/*
other columns
*/
{
name: "clone", label: "clone", width: 44, template: function (task) {
return "<input type=button value='V' onclick=clone_task(" + task.id + ")>"
}
}
];
Related sample: Gantt. Clone a task
Следующий пример иллюстрирует клонирование задачи вместе со всеми ее подзадачами и связями:
let child_links;
let clone_original_ids_table;
function obtain_link_ids(id) {
const task = gantt.getTask(id);
const source_links = task.$source;
for (let i = 0; i < source_links.length; i++) {
child_links.push(source_links[i]);
}
}
function create_clone_original_ids_table(original_id, clone_id) {
clone_original_ids_table[original_id] = clone_id;
}
function clone_child_links() {
for (let i = 0; i < child_links.length; i++) {
const link = gantt.getLink(child_links[i]);
if (clone_original_ids_table[link.source] && clone_original_ids_table[link.target]){
const clone_link = {};
clone_link.id = gantt.uid();
clone_link.target = clone_original_ids_table[link.target];
clone_link.source = clone_original_ids_table[link.source];
clone_link.type = link.type;
gantt.addLink(clone_link)
}
}
}
function clone_children(id, new_parent) {
const children = gantt.getChildren(id)
for (let i = 0; i < children.length; i++) {
const child_original = gantt.getTask(children[i]);
const child_clone = gantt.copy(child_original);
child_clone.id = gantt.uid();
child_clone.parent = new_parent;
gantt.addTask(child_clone, child_clone.parent, child_clone.$index);
obtain_link_ids(child_original.id);
create_clone_original_ids_table(child_original.id, child_clone.id);
if (gantt.hasChild(child_original.id)) clone_children(
child_original.id, child_clone.id
);
}
}
function clone_task(id) {
const task = gantt.getTask(id);
const clone = gantt.copy(task);
clone.id = gantt.uid();
gantt.addTask(clone, clone.parent, clone.$index);
child_links = [];
obtain_link_ids(id);
clone_original_ids_table = {};
create_clone_original_ids_table(task.id, clone.id);
if (gantt.hasChild(id)) {
clone_children(id, clone.id)
}
clone_child_links()
}
gantt.config.order_branch = true;
gantt.config.order_branch_free = true;
gantt.config.columns = [
/*
other columns
*/
{
name: "clone", label: "clone", width: 44, template: function (task) {
return "<input type=button value='V' onclick=clone_task(" + task.id + ")>"
}
}
];
Related sample: Gantt. Clone a task with all its subtasks and links
Еще один пример показывает, как реализовать копирование через горячие клавиши: выберите задачи, нажмите Ctrl + C для копирования и Ctrl + V для вставки в качестве подзадач выбранной задачи:
gantt.plugins({
keyboard_navigation: true,
multiselect: true,
})
let tasks_to_copy = [];
gantt.ext.keyboardNavigation.addShortcut("ctrl+c", function (e) {
tasks_to_copy = [];
gantt.eachSelectedTask(function (task_id) {
tasks_to_copy.push(task_id);
});
}, "taskRow");
gantt.ext.keyboardNavigation.addShortcut("ctrl+v", function (e) {
const new_parent = gantt.getSelectedId();
for (let i = 0; i < tasks_to_copy.length; i++) {
const task = gantt.copy(gantt.getTask(tasks_to_copy[i]));
task.id = +new Date() + '+' + Math.floor(Math.random() * 10);
gantt.addTask(task, new_parent)
}
gantt.getTask(new_parent).$open = true;
gantt.render()
}, "taskRow");
Related sample: Gantt. Copy and paste tasks via Ctrl+C, Ctrl+V
Чтобы добавить пользовательские стили или диаграммы ресурсов в экспортируемый PDF, экспортируйте данные в raw режиме и добавьте стили через параметры header или footer функции экспорта.
Например, вы можете сохранить стили в переменной и включить эту переменную в параметр header:
const header = `
.gantt_bar_task {
background: orange;
}
.gantt_task_progress {
background-color: rgba(33, 33, 33, 0.17);
}
`
gantt.exportToPDF({
header: "<style>" + header + "</style>"
});
Related sample: Gantt. Export Gantt to PDF (styles from a variable)
Или получить содержимое элемента <style> на странице и добавить его так:
gantt.exportToPDF({
raw: true,
header: "<style>" + document.getElementById("styles").innerHTML + "</style>"
});
<style id='styles'>
.gantt_bar_task {
background: orange;
}
.gantt_task_progress {
background-color: rgba(33, 33, 33, 0.17);
}
</style>
Related sample: Gantt. Export Gantt to PDF (styles from <style> element)
Related sample: Gantt. Export Gantt with custom icons to PDF
Пример экспорта диаграммы Gantt с легендой:
Related sample: Gantt. Export Gantt with legend to PDF
Примеры экспорта диаграммы загрузки ресурсов и гистограммы:
Related sample: Gantt. Export Gantt with resource load diagram to PDF
Related sample: Gantt. Export Gantt with resource histogram to PDF
Один из простых способов — обновлять прогресс родительской задачи сразу после изменения дочерней задачи. Для обхода родительских задач удобно использовать метод eachParent().
В примере ниже прогресс родительских задач рассчитывается только на основе прогресса их дочерних задач:
gantt.config.auto_types = true;
gantt.templates.progress_text = function (start, end, task) {
return "<span style='text-align:left;'>" + Math.round(task.progress * 100)
+ "% </span>";
};
gantt.init("gantt_here");
gantt.parse({
"data": [
...
]
});
gantt.attachEvent("onAfterTaskUpdate", function (id, task) {
parentProgress(id)
});
gantt.attachEvent("onTaskDrag", function (id, mode, task, original) {
if (mode == "progress") {
parentProgress(id)
}
});
gantt.attachEvent("onAfterTaskAdd", function (id) {
parentProgress(id)
});
gantt.attachEvent("onAfterTaskDelete", function (id, task) {
if (task.parent) {
const siblings = gantt.getChildren(task.parent);
if (siblings.length) {
parentProgress(siblings[0])
}
}
});
function parentProgress(id) {
gantt.eachParent(function (task) {
const children = gantt.getChildren(task.id);
let childProgress = 0;
for (let i = 0; i < children.length; i++) {
const child = gantt.getTask(children[i])
childProgress += (child.progress * 100);
}
task.progress = childProgress / children.length / 100;
}, id)
gantt.render();
}
Related sample: Gantt. Calculate progress of a parent task dynamically
В следующем примере прогресс родительских задач рассчитывается с учетом как прогресса, так и длительности дочерних задач:
function calculateSummaryProgress(task) {
if (task.type != gantt.config.types.project)
return task.progress;
var totalToDo = 0;
var totalDone = 0;
gantt.eachTask(function (child) {
if (child.type != gantt.config.types.project) {
totalToDo += child.duration;
totalDone += (child.progress || 0) * child.duration;
}
}, task.id);
if (!totalToDo) return 0;
else return totalDone / totalToDo;
}
function refreshSummaryProgress(id, submit) {
if (!gantt.isTaskExists(id))
return;
var task = gantt.getTask(id);
var newProgress = calculateSummaryProgress(task);
if (newProgress !== task.progress) {
task.progress = newProgress;
if (!submit) {
gantt.refreshTask(id);
} else {
gantt.updateTask(id);
}
}
if (!submit && gantt.getParent(id) !== gantt.config.root_id) {
refreshSummaryProgress(gantt.getParent(id), submit);
}
}
gantt.attachEvent("onParse", function () {
gantt.eachTask(function (task) {
task.progress = calculateSummaryProgress(task);
});
});
gantt.attachEvent("onAfterTaskUpdate", function (id) {
refreshSummaryProgress(gantt.getParent(id), true);
});
gantt.attachEvent("onTaskDrag", function (id) {
refreshSummaryProgress(gantt.getParent(id), false);
});
gantt.attachEvent("onAfterTaskAdd", function (id) {
refreshSummaryProgress(gantt.getParent(id), true);
});
(function () {
var idParentBeforeDeleteTask = 0;
gantt.attachEvent("onBeforeTaskDelete", function (id) {
idParentBeforeDeleteTask = gantt.getParent(id);
});
gantt.attachEvent("onAfterTaskDelete", function () {
refreshSummaryProgress(idParentBeforeDeleteTask, true);
});
})();
...
gantt.config.auto_types = true;
gantt.templates.progress_text = function (start, end, task) {
return "<span style='text-align:left;'>" + Math.round(task.progress * 100)
+ "% </span>";
};
gantt.templates.task_class = function (start, end, task) {
if (task.type == gantt.config.types.project)
return "hide_project_progress_drag";
};
Related sample: Calculate Progress of Summary Tasks
Метод addTaskLayer() позволяет добавлять пользовательские HTML-элементы на временную шкалу и поддерживает их вертикальное и горизонтальное перемещение.
В примере ниже показано, как можно менять порядок задач по вертикали на временной шкале, аналогично тому, как задачи можно менять местами в гриде:
Related sample: Gantt. Reorder tasks vertically in timeline
В другом примере показано, как менять порядок разделённых задач и размещать задачи на одной строке:
Related sample: Gantt. Reorder split tasks vertically in timeline
Такого эффекта можно добиться с помощью CSS. Столбец, который нужно зафиксировать, должен иметь позицию 'relative'. Свойство 'left' должно соответствовать текущей позиции скроллбара. Для актуализации значения можно добавить обработчик события на скроллбар и изменять CSS-переменную:
gantt.attachEvent("onGanttReady", function () {
const el = document.querySelector(".gantt_hor_scroll");
if (el) {
el.addEventListener('scroll', function () {
document.documentElement.style.setProperty(
'--gantt-frozen-column-scroll-left', el.scrollLeft + "px"
);
});
}
});
const textEditor = { type: "text", map_to: "text" };
const start_dateEditor = { type: "date", map_to: "start_date" };
const end_dateEditor = { type: "date", map_to: "end_date" };
const durationEditor = { type: "number", map_to: "duration", min: 0, max: 100 };
gantt.config.columns = [
{ name: "text", tree: true, width: 150, resize: true, editor: textEditor },
{ name: "start_date", align: "center", width: 120, resize: true,
editor: start_dateEditor },
{ name: "end_date", label: "End Time", align: "center", width: 120,
resize: true, editor: end_dateEditor },
{ name: "duration", align: "center", width: 80, resize: true,
editor: durationEditor },
{ name: "progress", label: "Progress", width: 80, align: "center",
resize: true },
{
name: "custom", label: "Custom", width: 180, align: "center",
resize: true, template: function (task) {
return Math.round(Math.random() * 100)
}
},
{ name: "add", width: 44 }
];
gantt.config.layout = {
css: "gantt_container",
cols: [
{
rows: [
{
view: "grid", scrollable: true,
scrollX: "scrollHor1", scrollY: "scrollVer"
},
{
view: "scrollbar", id: "scrollHor1",
scroll: 'x', group: 'hor'
},
]
},
{ resizer: true, width: 1 },
{
rows: [
{
view: "timeline", scrollX: "scrollHor", scrollY: "scrollVer"
},
{
view: "scrollbar", id: "scrollHor",
scroll: 'x', group: 'hor'
},
]
},
{ view: "scrollbar", id: "scrollVer" }
]
}
Дополнительно добавьте следующие CSS-стили:
:root {
--gantt-frozen-column-scroll-left: 0px;
}
.gantt_cell:nth-child(1),
.gantt_grid_head_cell:nth-child(1) {
background: Azure;
position: relative;
left: var(--gantt-frozen-column-scroll-left);
}
.gantt_grid_editor_placeholder[data-column-name="text"] {
left: var(--gantt-frozen-column-scroll-left) !important;
}
.gantt_grid_head_cell:nth-child(1) {
z-index: 1;
}
Related sample: Gantt. Frozen column in Grid (via CSS)
В качестве альтернативы можно настроить несколько представлений грида, однако такой подход не очень хорошо работает с встроенными редакторами:
gantt.config.columns = [
{ name: "start_date", align: "center", width: 80, resize: true },
{ name: "end_date", label: "End Date", align: "center", width: 80, resize: true },
{ name: "duration", width: 60, align: "center", resize: true },
{ name: "progress", label: "Progress", width: 60, align: "center", resize: true },
{ name: "add", width: 44 }
];
const fixedColumn = {
columns: [
{ name: "text", tree: true, width: 200, resize: true },
]
};
gantt.config.layout = {
css: "gantt_container",
cols: [
{
width: 400,
//min_width: 100,
rows: [
{
group: "gantt",
cols: [
{
rows: [
{ view: 'grid', config: fixedColumn, bind: "task",
scrollY: 'gridScrollY' }
]
},
{
rows: [
{ view: 'grid', bind: "task", scrollX: 'gridScrollX',
scrollable: true, scrollY: 'gridScrollY' },
{ view: 'scrollbar', id: 'gridScrollX' }
]
},
{ view: 'scrollbar', id: 'gridScrollY' }
]
}
]
},
{ resizer: true, width: 1 },
{
rows: [
{
group: "gantt",
cols: [
{
rows: [
{ view: "timeline", scrollX: "scrollHor", scrollY: "scrollVer" },
{ view: "scrollbar", id: "scrollHor" }
]
},
{ view: 'scrollbar', id: 'scrollVer' }
]
}
]
}
]
}
Related sample: Gantt. Fixed column in Grid (several grid views)
В Gantt нет встроенной функции для добавления легенды. Ближайший вариант — Overlay extension, но это не совсем то же самое и возможности настройки ограничены.
Тем не менее, добавить легенду достаточно просто. Можно создать элемент легенды в HTML и вставить его в контейнер Gantt следующим образом:
gantt.$root.appendChild(legend);
Ниже приведён живой пример, где легенда появляется после нажатия кнопки "Toggle legend" над Gantt:
Related sample: Gantt. Add information legend
Чтобы добавить интерактивность, можно навешивать обработчики событий прямо на элемент легенды или обрабатывать события на корневом элементе Gantt с помощью делегирования:
gantt.event(gantt.$root, "click", function(e){
var closest = gantt.utils.dom.closest;
if(closest(e.target, ".gantt-legend")) {
gantt.message("Mouse click inside the legend element");
}
});
К началу