Home / Docs / gn-vgantt

@gerald-neumann/gn-vgantt

Complete Gantt product for Angular. A host ships a GnVGanttDataProvider and one (commit) callback. The host does not implement D3, split panes, virtualization, schedule-conflict math, propose-to-fix, row-reorder chrome, or a second schedule table.

Peer dependencies: Angular 21 or 22, RxJS 7, d3 7, @gerald-neumann/gn-vtable ^1.4.0. Optional host chip search: @gerald-neumann/gn-vquery ^1.3.0.

Quick start (hardcoded)

Absolute minimum — in-memory rows, no REST, no kind catalog:

import { Component } from '@angular/core';
import {
  GnVGanttComponent,
  MemoryVGanttProvider,
  type GnVGanttRow,
} from '@gerald-neumann/gn-vgantt';

const rows: GnVGanttRow[] = [
  {
    id: '1',
    name: 'Design',
    parentId: null,
    startDate: new Date('2026-01-06'),
    endDate: new Date('2026-01-17'),
  },
  {
    id: '2',
    name: 'Build',
    parentId: null,
    startDate: new Date('2026-01-13'),
    endDate: new Date('2026-01-31'),
    dependency: { predecessorId: '1', type: 'FS' },
  },
];

@Component({
  imports: [GnVGanttComponent],
  template: `<gn-vgantt [dataProvider]="provider" />`,
})
export class MinimalGanttPage {
  readonly provider = new MemoryVGanttProvider({ rows });
}

MemoryVGanttProvider implements commit, so bar/link edits stay in memory under default persistMode: 'auto'. Add [kindCatalog] when you need type badges, pin rules, or create menus. Production hosts usually switch to HttpVGanttProvider (below).

Runnable copy in this repo: projects/minimum-sample — .\start-minimum-sample.ps1 (or npm run start:minimum) → http://localhost:4201. Use Show code in the sample chrome to view this snippet beside the chart.

Host surface

<gn-vgantt
  [dataProvider]="provider"
  [kindCatalog]="catalog"
  [assigneeOptions]="users()"
  [highlightedRowIds]="highlightIds()"
  [pinnedId]="focusReleaseId()"
  (commit)="onCommit($event)"
/>
this.provider = new HttpVGanttProvider({
  baseUrl: apiUrl('planning/gantt'),
  http: this.http,
});

onCommit(event: GnVGanttCommitOutput): void {
  if (event.persistHandledBy === 'provider') {
    return; // library already POSTs via provider.commit — do not double-save
  }
  // persistMode output / auto without provider.commit:
  this.gantt()?.setPersistStatus('saving');
  this.save(event).subscribe({
    next: () => this.gantt()?.setPersistStatus('idle'),
    error: () => this.gantt()?.setPersistStatus('error'),
  });
}

GnVGanttCommit is one payload for bar drag, link draw/remove, inline edit, reorder, reparent, and propose-to-fix. (commit) emits GnVGanttCommitOutput (= commit fields + persistHandledBy: 'provider' | 'host').

Persist (persistMode)

Mode Behavior
auto (default) Call provider.commit when present (Saving… chrome); otherwise host handles (commit)
provider Only provider.commit
output Only (commit) — never call provider.commit

Always check event.persistHandledBy before POSTing. For host-only persist, call setPersistStatus('saving' | 'idle' | 'error').

Deprecated: (barChange) / (linkChange) — analytics only; do not persist from them.

Schedule snapshot

There is no host [(schedule)] binding. Use scheduleRows() for a readonly viewport/scoped snapshot. Do not treat it as the full dataset in HTTP mode.

Holidays

Built-in tables are AT/DE only. For other regions pass [holidayResolver]="(date) => …" (return { name } or undefined to fall through). Unknown [holidayRegions] codes without a resolver warn in dev mode.

Supported host API (@public)

Full consumer contract: docs/SUPPORTED-HOST-API.md.

Exported from @gerald-neumann/gn-vgantt: GnVGanttComponent, provider types, HttpVGanttProvider / MemoryVGanttProvider, commit/row/kind/issue models, display helpers, i18n, holiday types + holidayResolver.

Supported component methods: reloadTable(), scrollToId(), flashRow(), scheduleRows(), setPersistStatus().

Expert helpers (detectIssues / proposeFix, schedule geometry, drop planners, calendar math, planning) live on @gerald-neumann/gn-vgantt/advanced — not semver-stable for hosts. Timeline stays package-private. Import outline helpers from @gerald-neumann/gn-vtable when needed.

import { applyCommit, detectIssues, proposeFix } from '@gerald-neumann/gn-vgantt/advanced';

Published REST (normative)

Method Path Role
POST {base}/rows VTableDataParams → VTableResponse<GnVGanttRow> (ISO dates)
POST {base}/count VTableCountParams → number
GET or POST {base}/domain GnVGanttTimeDomain for the current filter/pin
POST {base}/links { ids: string[] } → GnVGanttLink[] (visible ids + overscan)
POST {base}/commit GnVGanttCommit → { ok, rows? } (optional)

Optional chip filters (host @gerald-neumann/gn-vquery ^1.3.0): HttpVGanttProvider.setChipFilterQuery adds chipFilterQuery / chipFilterDialect to /rows /count /domain. Use applyCompiledQueryToVGantt then reloadTable({ countFirst: true }).

demo-api + demo-api/openapi.yaml are the reference implementation. HTTP mode stays O(viewport): fetchTimeDomain and fetchLinks are required; the component does not accumulate every loaded chunk.

Kind catalog (prefer) / kind graph (advanced)

Pass [kindCatalog] with sameKindChildren / childGroups / pinnable — the library derives the creatable-child graph. Optional [kindGraph] is an advanced override when catalog metadata is not enough. The library has no Magna or ReSpecT kind defaults — the demo app supplies those catalogs.

WBS parentId and visual scheduleOrder are independent. Table DnD uses gn-vtable drop zones: middle of the row nests onto a valid parent kind (commit.parents); top/bottom edge reorders as a sibling (commit.ranks, 10, 20, …). Illegal drops are rejected. [allowReparent]="false" keeps sibling reorder only; dropping into another row of the same kind then offers Set as predecessor / Set as successor (commit.links). Draw a connector from the end of a bar to the start of another same-kind bar (or Shift+drag). The preview is always that orthogonal end→start path (or start←end from the start handle), not a free diagonal. Double-click an arrow to remove it. Cross-type links are rejected.

Navigate / highlight / pin (precedence)

Clicking a row or bar highlights the critical path through that item (CPM, zero total float). Path bars and arrows use --gantt-critical. Click again to clear.

Use one concern per API — do not wire all four for the same jump:

API Role Side effects
[highlightedRowIds] Heatmap ?highlight= — timed flash of first id Also scrolls to that id
[focusId] One-shot scroll + select when the id changes Sets selectedId, then scrollToId
[(pinnedId)] Persist pin scope (subtree filter via provider) Synced to provider.setPinnedId
[(selectedId)] Current selection / critical-path root Cleared when user toggles the same bar
scrollToId(id) / flashRow(id) Imperative after first chunk Hosts must not query [data-task-id]

Recipe: heatmap deep-link → [highlightedRowIds]; ALM “open this item” → [focusId]; “work under this release” → [(pinnedId)]; toolbar selection chrome → [(selectedId)].

[maxDepth] drills the pinned/scoped tree independently of navigation.

Font Awesome license

gn-vgantt does not ship Font Awesome files. Icons use gn-vtable’s iconLibrary / iconLibraries contract (sanitizeFaGlyphName: bare glyph names such as chart-gantt).

If you use font-awesome-solid, font-awesome-duotone, or a custom Sharp Duotone library, you must have your own Font Awesome license and load the CSS in the host app. The demo app uses Font Awesome Pro via FONTAWESOME_PRO_TOKEN (same pattern as MyERP / ReSpecT).

Theming (tokens only)

@use '@gerald-neumann/gn-vgantt/themes/bootstrap';
// or material, or tailwind

No host D3 CSS is required. All colors — including SVG chrome (buffer hatch, milestone knobs, resize/link handles, rails, header bands) — read --gantt-* tokens with light fallbacks; the component never assigns tokens on :host, so a plain host element selector (gn-vgantt { --gantt-bg: …; }) is enough for dark skins. The full token contract is documented in change-reqests/CR-2026-08-24-prototype-visual-parity.md and mirrored by the three theme files.

Outline / L-rails

Name-column L-rails come from gn-vtable (vtable-outline-prefix). Timeline date rails stay in this library. Providers must supply __outline (memory provider and demo-api do).

outlineMode Meaning
l-rails (default) Colored through/join stems + chevron
tree Indent + chevron only
none Flat name column

Chevron collapse: gn-vgantt handles (outlineToggle) and calls provider.toggleOutlineExpanded?.(id) when present. MemoryVGanttProvider implements it (hides descendants). HTTP providers typically stamp __expanded from the server instead.