demo D271
A project plan (Gantt)
A task list as a schedule: editable bars, the four dependency links, the critical path, milestones and progress, recomputing as you drag
createGantt
The configuration
'gantt-chart': () => ({
rows: [],
config: {},
foot: [
'a task list on the left and a timeline on the right, bound both ways',
'drag a bar to move a task or its right edge to resize it, and edit a start or a duration in the grid: the other pane keeps up',
'the four dependency links join the bars, the critical path is drawn in red, milestones are diamonds, and each bar fills to its progress',
'a task dragged earlier than its predecessors allow is flagged rather than silently moved',
],
mount: (el: HTMLElement, LG: any) => {
const { tasks, dependencies } = plan();
let gantt: any;
let grid: any;
let disposed = false;
const split = document.createElement('div');
split.style.cssText = 'display:grid;grid-template-columns:320px 1fr;gap:12px;align-items:start';
const tasksCol = document.createElement('div');
tasksCol.style.minWidth = '0';
const tasksEl = document.createElement('div');
tasksEl.style.cssText = 'height:360px;border:1px solid var(--rule);border-radius:9px;background:var(--paper);min-width:0';
tasksCol.append(gridTitle('Tasks'), tasksEl);
const planCol = document.createElement('div');
planCol.style.minWidth = '0';
const planEl = document.createElement('div');
planEl.style.cssText = 'height:360px;border:1px solid var(--rule);border-radius:9px;background:var(--paper);min-width:0;overflow:auto';
planCol.append(gridTitle('Schedule'), planEl);
split.append(tasksCol, planCol);
el.append(split);
grid = LG.createGrid(tasksEl, {
rowKey: 'id',
theme: 'light',
columns: [
{ field: 'name', title: 'Task', layout: { flex: 1, min: 120 } },
{ field: 'start', title: 'Start', type: 'number', layout: { width: 74 }, edit: true },
{ field: 'duration', title: 'Days', type: 'number', layout: { width: 68 }, edit: true },
],
rows: tasks,
});
loadGantt()
.then((G: any) => {
if (disposed) return;
gantt = G.createGantt({
tasks,
dependencies,
grid,
columns: { start: 'start', duration: 'duration' },
autoSchedule: true,
});
gantt.mount(planEl, {
editable: true,
today: 12,
label: 'name',
dateAxis: true,
rowHeight: 34,
width: 860,
});
gantt.view?.linkVerticalScroll?.(grid.element);
})
.catch((err) => console.error('[gantt]', err));
return () => {
disposed = true;
gantt?.destroy?.();
grid?.destroy?.();
};
},
})
A task list that draws its own schedule
Give the plan a list of tasks and how they depend on one another, and it works out the schedule for you: when each task can start and finish, how much slack it has, and which tasks have none. That last run, the tasks that cannot slip without moving the end date, is the critical path, and it is drawn in red so the risk in the plan is the first thing you see. Change a duration or drag a task and the whole schedule recomputes, so the picture is always the real one rather than a drawing someone forgot to update.
The timeline is not a screenshot. Drag a bar to move a task, or drag its right edge to make it shorter or longer, and the new dates are written back to the row underneath; edit a start or a duration in the task list beside it and the bar keeps up. Tasks can depend on one another in each of the four standard ways, finish-to-start through to start-to-finish, each with an optional lead or lag, and the arrows between the bars show how the plan holds together. Milestones sit as diamonds, each bar fills to show how far along it is, the timeline runs on a calendar axis, and a line marks today.
The plan is honest about trouble. A task dragged earlier than its dependencies allow is flagged rather than quietly moved, so you see the conflict instead of shipping it. Anything that has slipped behind is marked, and because the schedule is built from the tasks a grid already holds, the same live feed that fills your grids can fill the plan too, keeping it current as the work moves.
How do I turn a task list into a Gantt chart?
Load the gantt module and call createGantt({ tasks, dependencies }), then mount(element, options) to draw it. Each task is a row with a duration (and, for a fixed placement, a start); a milestone is a task marked as one, and a task named as another’s parent becomes a summary rolled up from its children. Dependencies are { from, to, type } where the type is one of the four link kinds, with an optional lag. Pass a grid and a column map to bind the timeline to an editable task grid, so a drag writes the dates back and an edit in the grid redraws the bar. Turn on editable for drag-editing, set today for the today line, and turn on dateAxis to read the timeline as calendar dates.