Skip to content

Visual Configuration

Source code
import { controlAttributes, createControl } from '@revolist/ui';
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();
import {
defineGanttToolbar,
GanttPlugin,
GanttCalendarEditorPlugin,
type ProjectSnapshot,
} from '@revolist/gantt';
import { ExportExcelPlugin, RowSelectPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';
import {
SHOWCASE_ASSIGNMENTS,
SHOWCASE_BASELINES,
SHOWCASE_COLUMNS_WITH_ROW_SELECT,
SHOWCASE_DEPENDENCIES,
SHOWCASE_RESOURCES,
SHOWCASE_TOOLBAR_COLUMNS,
STANDARD_CALENDAR,
} from './shared/gantt-project-data';
import {
createProductionGanttConfig,
createProductionTasks,
createProductionPersistence,
productionError,
productionSourceRows,
} from './GanttProductionPatternsShared';
import './gantt-production-patterns.css';
export function load(parentSelector: string) {
const parent = document.querySelector(parentSelector);
if (!parent) return () => {};
const root = document.createElement('section');
const controls = document.createElement('div');
const status = document.createElement('p');
const toolbar = document.createElement('div');
const grid = document.createElement('revo-grid');
const persistence = createProductionPersistence();
let readOnly = false;
let localized = false;
let large = false;
let project: ProjectSnapshot | null = null;
const applyConfig = () => {
grid.gantt = createProductionGanttConfig(readOnly, localized, project);
};
const applyProject = () => {
applyConfig();
grid.ganttDependencies =
project?.dependencies ?? (large ? [] : SHOWCASE_DEPENDENCIES);
grid.ganttCalendars = project?.calendars ?? [STANDARD_CALENDAR];
grid.ganttResources = project?.resources ?? SHOWCASE_RESOURCES;
grid.ganttAssignments =
project?.assignments ?? (large ? [] : SHOWCASE_ASSIGNMENTS);
grid.ganttBaselines =
project?.baselines ?? (large ? [] : SHOWCASE_BASELINES);
grid.source = project
? productionSourceRows(project)
: createProductionTasks(large);
};
const toggle = (label: string, update: (value: boolean) => void) => {
const element = document.createElement('label');
const input = createControl(document, 'input', { type: 'checkbox' });
input.type = 'checkbox';
input.addEventListener('change', () => update(input.checked));
element.append(input, document.createTextNode(label));
return { element, input };
};
const run = async (action: () => unknown | Promise<unknown>) => {
try {
await action();
} catch (error) {
status.textContent = productionError(error);
}
};
const button = (label: string, action: () => unknown | Promise<unknown>) => {
const element = createControl(document, 'button', { type: 'button' });
element.type = 'button';
element.textContent = label;
element.className = controlAttributes('button', { class: 'rv-btn' }).class;
element.addEventListener('click', () => {
void run(action);
});
return element;
};
root.className = 'gantt-production-demo';
controls.className = 'gantt-production-controls';
status.className = 'gantt-production-status';
status.setAttribute('role', 'status');
toolbar.className = 'gantt-production-toolbar';
grid.className = 'gantt-production-grid';
grid.theme = currentTheme().isDark() ? 'darkCompact' : 'compact';
grid.hideAttribution = true;
grid.plugins = [
GanttPlugin,
GanttCalendarEditorPlugin,
ExportExcelPlugin,
RowSelectPlugin,
];
grid.rowSelect = { rowOrder: true };
grid.columns = [...SHOWCASE_COLUMNS_WITH_ROW_SELECT];
const largeToggle = toggle('750 tasks', (value) => {
large = value;
project = null;
applyProject();
});
const restore = button('Restore', async () => {
project = await persistence.load();
large = false;
largeToggle.input.checked = false;
applyProject();
status.textContent =
'Restored the saved project, including tasks and project collections.';
});
restore.disabled = true;
const fileInput = createControl(document, 'input', { type: 'file' });
fileInput.type = 'file';
fileInput.hidden = true;
fileInput.accept = '.json,application/json';
fileInput.setAttribute('aria-label', 'Import JSON file');
fileInput.addEventListener('change', async () => {
const file = fileInput.files?.[0];
if (file)
await run(async () => {
project = await persistence.importFile(file);
large = false;
largeToggle.input.checked = false;
applyProject();
status.textContent = `Imported ${project.tasks.length} tasks from ${file.name}.`;
});
fileInput.value = '';
});
const options = document.createElement('div');
options.className = 'gantt-production-options';
const actions = document.createElement('div');
actions.className = 'gantt-production-actions';
options.append(
toggle('Read only', (value) => {
readOnly = value;
applyConfig();
}).element,
toggle('Español dates', (value) => {
localized = value;
applyConfig();
}).element,
largeToggle.element,
);
actions.append(
fileInput,
button('Import JSON', () => fileInput.click()),
button('JSON', () => persistence.exportFile(grid)),
button('Save', async () => {
await persistence.save(grid);
restore.disabled = false;
status.textContent =
'Saved the current edited project in browser memory.';
}),
restore,
button('Print', () => persistence.print(grid)),
);
controls.append(options, actions);
root.append(controls, status, toolbar, grid);
parent.appendChild(root);
applyProject();
defineGanttToolbar(toolbar, {
grid,
columns: SHOWCASE_TOOLBAR_COLUMNS,
controls: { baseline: true, calendars: true },
});
return () => {
toolbar.textContent = '';
grid.remove();
root.remove();
};
}

gantt.visuals controls overlays and rendering hooks:

  • percentDoneMode selects the project-wide number, bar (default), or circle rendering for the managed % Done task-table column using the shared Pro progress renderers. An authored { prop: 'percentDone', mode: '...' } column overrides it.

  • showBaseline, baselineId

  • showCriticalPath

  • showTaskLabels

  • projectLineDate, showTodayLine, milestoneLines

  • timeRanges, shadeNonWorkingTime

  • taskMarkerHook, taskBarColorHook, taskBarContentHook

  • taskTooltipFields, taskTooltipHook

Use taskTooltipFields when you only need to choose which built-in fields appear in task-bar tooltips:

grid.gantt = {
...project,
visuals: {
taskTooltipFields: [
'name',
'startDate',
'endDate',
{ field: 'percentDone', label: 'Complete' },
'assignees',
],
},
};

taskTooltipFields is an ordered array. Each entry can be a field id string or an object with a field and optional label:

type GanttTaskTooltipField =
| GanttTaskTooltipFieldId
| { field: GanttTaskTooltipFieldId; label?: string };

Object entries let you rename a built-in line. Set label: '' to render only the value without a Label: prefix.

visuals: {
taskTooltipFields: [
{ field: 'name', label: '' },
{ field: 'percentDone', label: 'Done' },
],
}

Supported built-in fields:

FieldTooltip output
summaryTask name and completion percent, for example Design - 50%.
nameTask name.
percentDoneCompletion percent.
taskTypeHuman-readable task type.
statusHuman-readable task status.
startDateScheduled start date, formatted with gantt.dateFormats.tooltip.
endDateScheduled end date, formatted with gantt.dateFormats.tooltip.
durationFormatted task duration in its retained or project-selected unit.
taskModeAuto Scheduled or Manually Scheduled.
schedulingWarningsScheduling warnings joined into one line; skipped when empty.
costFormatted task cost; skipped when empty.
assigneesComma-separated assignee names; skipped when empty.

When taskTooltipFields is omitted, Gantt keeps the default tooltip: summary, taskType, status, startDate, endDate, duration, taskMode, schedulingWarnings, cost, and assignees. An empty array intentionally renders an empty tooltip.

taskTooltipHook has priority over taskTooltipFields and can replace the whole tooltip string:

visuals: {
taskTooltipFields: ['name'],
taskTooltipHook: ({ row }) => `${row.name}\n${row.status}`,
}

Return null or undefined from taskTooltipHook to fall back to taskTooltipFields or the default tooltip. Use the hook for custom row metadata that is not part of the built-in field list.

Type source: packages/gantt/src/gantt/grid/gantt-plugin.types.ts. Rendering sources: features/timeline-background/*, grid/gantt-column-type.ts.