Task list editing
The task list is a full treegrid: cells edit in place, headers sort, resize and move (each with a keyboard twin), a filter row and a search box narrow the tree without losing ancestors, and a multi-selection drives bulk delete, indent and outdent — every bulk edit one undo step.
Inline editing
Double-click a cell or press F2 on a row. Text, date, number, duration (days) and predecessor editors (2FS+1d, 3); summaries keep their rolled-up dates read-only and editor: false locks a column. Enter commits, Escape cancels, Tab / Shift+Tab move along the row.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { OgeGantt } from '@oge-ui/gantt';
import type { OgeGanttColumn } from '@oge-ui/gantt';
@Component({
selector: 'demo-root',
imports: [OgeGantt],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- inlineEditing: double-click a cell or press F2 on a row; Enter
commits, Escape cancels, Tab / Shift+Tab move along the row. Every
commit runs the cancelable update pipeline and is one undo step. -->
<oge-gantt
[tasks]="tasks"
[dependencies]="links"
[columns]="columns"
[inlineEditing]="true"
style="height: 360px"
/>
`,
})
export class Demo {
protected readonly columns: OgeGanttColumn[] = [
{ field: 'wbs', editor: false },
{ field: 'title' },
{ field: 'start' },
{ field: 'duration' },
{ field: 'progress' },
{ field: 'predecessors' },
{ field: 'owner' }, // a data field: a text editor by default
];
protected readonly tasks = [
{ id: 1, title: 'Discovery', start: new Date(2026, 7, 3), end: new Date(2026, 7, 3) },
{ id: 2, parentId: 1, title: 'Interviews', start: new Date(2026, 7, 3), end: new Date(2026, 7, 6), owner: 'Ana' },
{ id: 3, parentId: 1, title: 'Survey', start: new Date(2026, 7, 4), end: new Date(2026, 7, 8), owner: 'Bora' },
{ id: 4, title: 'Delivery', start: new Date(2026, 7, 10), end: new Date(2026, 7, 10) },
{ id: 5, parentId: 4, title: 'Build', start: new Date(2026, 7, 10), end: new Date(2026, 7, 14), owner: 'Cem' },
{ id: 6, parentId: 4, title: 'Review', start: new Date(2026, 7, 14), end: new Date(2026, 7, 15), owner: 'Ana' },
];
protected readonly links = [{ id: 'a', predecessorId: 5, successorId: 6 }];
}Sort, filter & columns
Click a header (or focus it and press Enter) to sort ascending, descending, off — siblings stay inside their parent and the WBS keeps the store order. Type into the filter row or the toolbar search: matches keep their ancestors. Drag a header edge or press Alt+Left/Right to resize; drag a header or press Ctrl+Shift+Left/Right to move it. WBS and Task are frozen.
Sorted by: store order
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeGantt } from '@oge-ui/gantt';
import type { OgeGanttColumn, OgeGanttSortChangedEvent } from '@oge-ui/gantt';
@Component({
selector: 'demo-root',
imports: [OgeGantt],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- header click / Enter sorts siblings; the filter row and the search
box keep the ancestors of matches. Drag a header edge (Alt+Arrow) to
resize, drag a header (Ctrl+Shift+Arrow) to move it; frozen columns
stay pinned while the pane scrolls sideways. -->
<oge-gantt
[tasks]="tasks"
[dependencies]="links"
[columns]="columns"
[allowSorting]="true"
[allowColumnResizing]="true"
[allowColumnReordering]="true"
[filterRow]="true"
[searchPanel]="true"
style="height: 400px"
(sortChanged)="lastSort.set($event)"
/>
<p>Sorted by: {{ lastSort()?.field ?? 'store order' }}</p>
`,
})
export class Demo {
protected readonly lastSort = signal<OgeGanttSortChangedEvent | null>(null);
protected readonly columns: OgeGanttColumn[] = [
{ field: 'wbs', frozen: true, allowSorting: false },
{ field: 'title', frozen: true },
{ field: 'owner' },
{ field: 'start' },
{ field: 'end' },
{ field: 'duration' },
];
protected readonly tasks = [
{ id: 1, title: 'Discovery', start: new Date(2026, 7, 3), end: new Date(2026, 7, 3) },
{ id: 2, parentId: 1, title: 'Interviews', start: new Date(2026, 7, 3), end: new Date(2026, 7, 6), owner: 'Ana' },
{ id: 3, parentId: 1, title: 'Survey', start: new Date(2026, 7, 4), end: new Date(2026, 7, 8), owner: 'Bora' },
{ id: 4, title: 'Delivery', start: new Date(2026, 7, 10), end: new Date(2026, 7, 10) },
{ id: 5, parentId: 4, title: 'Build', start: new Date(2026, 7, 10), end: new Date(2026, 7, 14), owner: 'Cem' },
{ id: 6, parentId: 4, title: 'Review', start: new Date(2026, 7, 14), end: new Date(2026, 7, 15), owner: 'Ana' },
];
protected readonly links = [{ id: 'a', predecessorId: 5, successorId: 6 }];
}Multi-select & bulk edits
Ctrl/Cmd-click toggles, Shift-click or Shift+Up/Down extends, Ctrl+A selects every row. Delete, Alt+Shift+Left/Right and the context menu then act on the whole selection — as one undo step, with one plural announcement.
0 selected
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeGantt } from '@oge-ui/gantt';
import type { RowKey } from '@oge-ui/core';
import type { OgeGanttTask } from '@oge-ui/gantt';
@Component({
selector: 'demo-root',
imports: [OgeGantt],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- selectionMode 'multiple': Ctrl/Cmd-click toggles, Shift-click and
Shift+Up/Down extend, Ctrl+A selects all. Delete, Alt+Shift+Left/Right
and the context menu act on the whole selection — one undo step. -->
<button type="button" (click)="gantt.indentTasks(gantt.getSelectedTasks())">Indent selected</button>
<button type="button" (click)="gantt.deleteTasks(sourcesOf(gantt.getSelectedTasks()))">
Delete selected
</button>
<oge-gantt
#gantt
[tasks]="tasks"
[dependencies]="links"
selectionMode="multiple"
[(selectedTaskKeys)]="selected"
style="height: 360px"
/>
<p>{{ selected().length }} selected</p>
`,
})
export class Demo {
protected readonly selected = signal<readonly RowKey[]>([]);
protected sourcesOf<T>(tasks: readonly OgeGanttTask<T>[]): T[] {
return tasks.map((task) => task.source);
}
protected readonly tasks = [
{ id: 1, title: 'Discovery', start: new Date(2026, 7, 3), end: new Date(2026, 7, 3) },
{ id: 2, parentId: 1, title: 'Interviews', start: new Date(2026, 7, 3), end: new Date(2026, 7, 6), owner: 'Ana' },
{ id: 3, parentId: 1, title: 'Survey', start: new Date(2026, 7, 4), end: new Date(2026, 7, 8), owner: 'Bora' },
{ id: 4, title: 'Delivery', start: new Date(2026, 7, 10), end: new Date(2026, 7, 10) },
{ id: 5, parentId: 4, title: 'Build', start: new Date(2026, 7, 10), end: new Date(2026, 7, 14), owner: 'Cem' },
{ id: 6, parentId: 4, title: 'Review', start: new Date(2026, 7, 14), end: new Date(2026, 7, 15), owner: 'Ana' },
];
protected readonly links = [{ id: 'a', predecessorId: 5, successorId: 6 }];
}