Editing, Validation, Diagnostics
These APIs are presentation-free. They make decisions and shape data, while your application owns forms, popovers, panels, toasts, and persistence.
import { GANTT_BEFORE_ASSIGNMENT_CHANGE_EVENT, GANTT_BEFORE_DEPENDENCY_CHANGE_EVENT, GANTT_BEFORE_TASK_CHANGE_EVENT, TASK_EDITOR_FIELD_SCHEMA, buildDependencyValidationSummary, buildResourceOverallocationSummary, createApprovalGateValidator, createForbiddenDateRangeValidator, createGanttBeforeTaskChangeValidationHandler, createGanttRoleEditabilityHelpers, createLockedPhaseValidator, createRoleGuardValidator, createTaskDetailPopoverModel, createTaskEditorFormValues, normalizeTaskEditorSubmit,} from '@revolist/revogrid-enterprise';Role-Based Editability
Section titled “Role-Based Editability”Before-change events are cancelable. Use role helpers to decide whether a task, dependency, or assignment mutation should proceed. Call event.preventDefault() to stop the packaged mutation before the Gantt store changes; timeline previews are cleared and the next render keeps the previous task, dependency, or assignment data.
const policy = { roles: { viewer: [], planner: ['task:*', 'dependency:create', 'dependency:delete'], staffing: ['assignment:edit'], admin: ['*'], }, fallback: 'deny',} as const;
const editability = createGanttRoleEditabilityHelpers(policy);const getRoleContext = () => ({ roles: currentUser.roles });
grid.addEventListener(GANTT_BEFORE_TASK_CHANGE_EVENT, (event) => { if (!editability.canEditTask(event.detail, getRoleContext())) { event.preventDefault(); }});
grid.addEventListener(GANTT_BEFORE_DEPENDENCY_CHANGE_EVENT, (event) => { if (!editability.canEditDependency(event.detail, getRoleContext())) { event.preventDefault(); }});
grid.addEventListener(GANTT_BEFORE_ASSIGNMENT_CHANGE_EVENT, (event) => { if (!editability.canEditAssignment(event.detail, getRoleContext())) { event.preventDefault(); }});Task before-change actions cover move, resize, create, edit, progress, split, indent, outdent, and delete. Dependency before-change actions cover create, delete, edit, and replace; replace is emitted by predecessor/successor inline field edits and includes previousDependencies plus proposed dependencies because one cell edit can add, update, and remove multiple links. Assignment edits emit proposed assignments and previousAssignments.
Custom Task Editor Forms
Section titled “Custom Task Editor Forms”Use the field schema and initial value builder to render a framework form, then normalize submitted values into a TaskUpdate patch.
For a complete standalone UI example, see Task Editing.
const fields = TASK_EDITOR_FIELD_SCHEMA.filter((field) => ['name', 'startDate', 'endDate', 'progressPercent', 'resourceLabels'].includes(field.id),);
const values = createTaskEditorFormValues(task, resources, assignments);
renderTaskEditor({ fields, values, onSubmit(nextValues) { const result = normalizeTaskEditorSubmit(task, nextValues);
if (!result.ok) { renderErrors(result.errors); return; }
updateTask(task.id, result.patch); },});Task Detail Popovers
Section titled “Task Detail Popovers”createTaskDetailPopoverModel() returns sections, fields, warnings, and resource labels. Your app handles hover/focus, positioning, and rendering.
const model = createTaskDetailPopoverModel({ row, task, resources, assignments,});
renderPopover({ title: model.title, subtitle: model.subtitle, sections: model.sections, resources: model.resourceAssignments.map((assignment) => assignment.displayValue),});Dependency Diagnostics
Section titled “Dependency Diagnostics”Use dependency summaries to power badges, side panels, or task focus commands.
const summary = buildDependencyValidationSummary({ conflicts: schedulerResult.conflicts, issues: schedulerResult.issues, rows: projectedRows,});
renderDiagnosticsPanel({ count: summary.totalCount, groups: summary.groups,});Resource Over-Allocation Diagnostics
Section titled “Resource Over-Allocation Diagnostics”Use resource summaries when staffing dashboards need to show who is overloaded and when.
const overloads = buildResourceOverallocationSummary({ rows: resourcePlanningRows, loadSummaries: resourceLoadSummaries, conflicts: schedulerResult.conflicts,});
renderResourceHealth({ count: overloads.totalCount, peakOverloadUnits: overloads.peakOverloadUnits, groups: overloads.groups,});Custom Validation Recipes
Section titled “Custom Validation Recipes”Compose validators and attach the generated handler to gantt-before-task-change.
const validators = [ createRoleGuardValidator({ policy }), createForbiddenDateRangeValidator({ ranges: [{ startDate: '2026-05-20', endDate: '2026-05-27', label: 'release freeze' }], }), createLockedPhaseValidator({ lockedPhaseIds: ['closed'], getPhaseId: (detail) => detail.taskId ? phaseByTaskId[detail.taskId] : null, }), createApprovalGateValidator({ actions: ['move', 'resize', 'progress'], requiresApproval: (detail) => detail.changes.progressPercent === 100, }),];
grid.addEventListener( GANTT_BEFORE_TASK_CHANGE_EVENT, createGanttBeforeTaskChangeValidationHandler({ context: () => validationContext, validators, onReject: ({ decision }) => showValidationToast(decision.message ?? 'Change rejected'), }),);