Temporeum Timelinev1.0.2

Migrating from frappe-gantt

A frappe-gantt chart moves over by turning each task into an item in a lane of its own, its dependencies string into a list of arrows, and its progress from a percentage into a fraction. This page maps every frappe-gantt option, task field, method and event that has an equivalent, and lists, honestly, the ones that don't have one yet. Each mapping in the tables below is a running example in the test suite, and each "Not yet" row is checked against the configuration's types. A row that maps to something that doesn't exist fails the build.

Live example: frappe-gantt tasks, converted

Loading…

Run “frappe-gantt tasks, converted” on your own machine
  1. Make an empty folder and put this example’s two files in it:

    index.html — the page the code runs in, its markup and styles:

    <div class="gantt" style="display: grid; grid-template-columns: 240px 1fr; height: 300px">
      <div id="grid"></div>
      <div id="timeline" style="height: 300px"></div>
    </div>
    <p class="log" id="log" aria-live="polite">Drag a bar, or press Alt and an arrow on the focused one.</p>
    

    main.js — the code, as it is in the editor now, your edits included:

  2. In a terminal in that folder, install the library and Vite (it lets the browser follow the imports), then start it:

    npm init -y
    npm install @temporeum/timeline-element
    npm install --save-dev vite
    npx vite
  3. Open the address it prints, http://localhost:5173/.

The plugins

import { createTimeline, registerPlugin } from "@temporeum/timeline-element";
import { ganttPlugin, mountTaskGrid } from "@temporeum/timeline-element/gantt";
import { toolsPlugin } from "@temporeum/timeline-element/tools";

// The Gantt chart, and the zoom presets for frappe-gantt's view modes.
registerPlugin(ganttPlugin, toolsPlugin);

From the running example example/migration-frappe-gantt

frappe-gantt is all chart; here the chart is a plugin, and so are some of the tools a frappe-gantt setup maps to. The Gantt plugin draws the arrows, the progress, the baselines and milestones. The time tools plugin, toolsPlugin from @temporeum/timeline-element/tools, is what the zoom presets for the view modes (zoomPresets), the popup (tooltip), ignored weekends (hiddenDates) and a click on a date (rangeSelect) need. Register both once, before the first timeline; a configuration that needs a plugin that is not registered throws a TypeError where the timeline is created, naming the import that is missing.

What changes

  • A task is an item in a lane. frappe-gantt draws one task per row; give each task a lane with the same id, and the lanes are the rows. With gantt and the Gantt plugin (registerPlugin(ganttPlugin), from @temporeum/timeline-element/gantt), the timeline draws the arrows, the progress fill, the baseline and milestones (Gantt); mountTaskGrid, from the same entry point, draws the table beside it.
  • Times are Date objects or epoch milliseconds. frappe-gantt reads "2026-03-02" as local midnight, and a date-only end as the end of that day. new Date("2026-03-02") is UTC midnight, so convert the way the example does: local midnight, and a day added to a date-only end.
  • Progress is 0 to 1, not 0 to 100.
  • Dependencies are one list for the chart, { from, to, type, lagMs }, not a string on each task. The type is finish-to-start unless it says otherwise.
  • Read-only until told. frappe-gantt is editable until readonly; a Temporeum timeline is read-only until editing.enabled. A move moves one task: the scheduler of @temporeum/timeline-core/schedule says where the ones after it go (scheduling).
  • Views are zooms. Day, week, month and year are the default zoomPresets (with toolsPlugin registered); the axis follows the zoom instead of being redrawn per mode.
  • The keyboard works by default. Every gesture has a key, the arrows follow the dependencies with ] and [, and screen readers see the chart as a listbox (accessibility).

The mapping

The chart in each row's example has five tasks with progress, each in its own lane, joined by a chain of dependencies; the row's code is the only difference.

Setup
frappe-ganttTemporeumStatusNotes
new Gantt("#gantt", tasks, options)createTimeline(container, { items, lanes, gantt: { dependencies } })RenamedA task is an item in a lane of its own: the lanes are the chart's rows. gantt turns on the arrows, the progress fill and milestones; mountTaskGrid draws the table beside it.
gantt.refresh(tasks)timeline.setItems(items)RenamedHand the timeline the next array; it redraws once.
gantt.update_task(id, details)timeline.updateItems([{ id, ...changes }])RenamedSeveral at once in one call; undefined takes a field away.
gantt.update_options(options)timeline.update(config)RenamedA key given as undefined goes back to its default; a key left out keeps its value.
container_heightheightRenamedIn CSS pixels. Without one, the container's own height.
language: "fr"locale: "fr"RenamedMonth and day names come from Intl for any locale; the timeline's own words from strings (@temporeum/timeline-core/intl).
View
frappe-ganttTemporeumStatusNotes
view_mode: "Week", gantt.change_view_mode("Week")zoomPresets: true, timeline.zoomToPreset("week")DifferentDay, week, month and year are the default presets. A preset zooms the view to that span; the columns follow the zoom instead of being redrawn per mode.
view_mode_select: truezoomPresets: trueDifferentThe presets are buttons over the timeline.
scroll_to: "start" | "end" | datedomain: [start, end]DifferentThe first view. Without a domain the view fits the tasks, with a margin.
gantt.scroll_current()timeline.scrollTo(Date.now())RenamedThe library never reads the clock; the host passes the time.
today_button: truea button that calls timeline.scrollTo(Date.now())DifferentNot built in; one line of the host's.
infinite_padding: truelimits: { min, max } (unbounded by default)DifferentThe view pans without end unless limits bounds it.
ignore: ["weekend"], is_weekendhiddenDates: [{ start, end, repeat: "weekly" }]DifferentA hidden stretch is cut out of the axis; with repeat it is a pattern — here every weekend.
holidaysdecorations: [{ kind: "era", id, start, end, label }]DifferentA named stretch drawn behind the tasks, with its label. A one-day holiday is a day long.
date_format, view_modes[].upper_text / lower_textformatTick(tick, timeZone)DifferentOne function for every axis label; tick.granularity says what the label stands for.
bar_height, paddingdensityDifferentRow height follows the density; auto picks one at which every row fits.
auto_move_label: trueon by defaultSameA label stays on the visible part of a bar the edge cuts off; one that does not fit inside its bar is shortened with an ellipsis, or given up with labelOverflow: "drop".
popup, popup_on: "hover"tooltip: (item) => textRenamedOn hover and on keyboard focus. The text is the item's; the library adds no HTML of its own.
show_expected_progress—Not yetbaseline draws the planned span under the bar, which shows a slip; an expected-progress mark is not drawn.
arrow_curve—Not yetArrows are routed around the bars with right angles.
lines: "vertical" | "horizontal" | "none"—Not yetThe grid follows the axis ticks and the lanes; it is styled, not switched off by an option.
upper_header_height, lower_header_height—Not yetThe axis is one band of its own height.
Editing
frappe-ganttTemporeumStatusNotes
readonly: truethe default (editing: { enabled: false })DifferentA Temporeum timeline is read-only until editing.enabled; frappe-gantt is editable until readonly.
readonly_dates: trueitem.movable: false, item.resizable: falseDifferentPer item, with editing on.
snap_at: "1d"editing: { snapMs: DAY }RenamedIn milliseconds, from the epoch or snapOrigin. A grid of whole days lies on the timeline's timeZone midnights, all year.
move_dependencies: truescheduleTasks(items, dependencies) from /scheduleDifferentA move moves one task. The scheduler of @temporeum/timeline-core/schedule says where the ones after it go — with lags, constraints and working calendars — and setItems puts them there.
Tasks
frappe-ganttTemporeumStatusNotes
task.iditem.id, and the lane's idSameOne lane per task, with the same id, keeps one task per row.
task.nameitem.labelRenamedAlso the item's accessible name.
task.start, task.end: "YYYY-MM-DD"item.start, item.end: Date or epoch msDifferentfrappe-gantt reads a date string as local midnight, and a date-only end as the end of that day. new Date(2026, 2, 6) is local midnight; new Date("2026-03-06") is UTC midnight. For an inclusive end, add a day.
task.progress: 0 to 100item.progress: 0 to 1DifferentDivide by 100. Drawn as the filled part of the bar.
task.dependencies: "a, b"gantt: { dependencies: [{ from: "a", to: id }] }DifferentOne list for the whole chart, each with its type (FS by default) and lag.
task.custom_classitem.classNameRenamed
task.color, task.color_progressitem.className and a stylesheetDifferentColours come from the theme's custom properties and the item's class.
task.duration: "3d"item.endDifferentGive the end: start plus the duration.
task.descriptiontooltipDifferentThe tooltip reads it from the item, or from data.
a zero-length taskitem.kind: "milestone"DifferentA diamond at its start, with the gantt option.
task.thumbnail—Not yetNo picture on the bar itself; a template draws sanitised HTML in its place.
Events
frappe-ganttTemporeumStatusNotes
on_click(task)on("itemClick")RenamedAlso fires for Enter on the focused item.
on_double_click(task)on("itemDblClick")Renamed
on_hover(task)on("itemHover")Renameditem is null when the pointer leaves every item.
on_date_change(task, start, end)on("itemMoved"), on("itemResized")DifferentEach carries the item, where it was, where it is now, and the reversible command. The library never saves.
on_view_change(mode)on("viewportChanged")RenamedFires for every change of the view, with its reason.
on_date_click(date)rangeSelect: true, on("rangeSelected")DifferentA drag on empty time selects a range; a click on it selects nothing.
on_progress_change(task, progress)—Not yetProgress is drawn, not dragged: change it with updateItems.

What is not here yet

The "Not yet" rows above, in short: dragging progress (and its event), expected progress, curved arrows, switching the grid lines off, header heights, and a picture on the bar. If your chart depends on one of them, say so in an issue: the order is decided by what users need.