Use when building, configuring, styling, or modifying SVAR Svelte Gantt / @wx/svelte-gantt timelines, grids, task editors, toolbars, context menus, tooltips, scales, links, and Gantt data actions.
Package
import {
Gantt,
ContextMenu,
HeaderMenu,
Toolbar,
Tooltip,
Editor,
Willow,
WillowDark,
version,
defaultEditorItems,
defaultToolbarButtons,
defaultMenuOptions,
defaultColumns,
defaultTaskTypes,
getEditorItems,
getToolbarButtons,
getMenuOptions,
registerScaleUnit,
registerEditorItem,
} from "@wx/svelte-gantt";
Components
Gantt - main grid plus timeline chart; bind:this exposes the Gantt API object from source.
Toolbar - Gantt-aware wrapper around @wx/svelte-toolbar; accepts api and optional items.
ContextMenu - Gantt-aware wrapper around @wx/svelte-menu context menu; resolves tasks from Gantt data-id.
Editor - Gantt-aware wrapper around @wx/svelte-editor; opens for activeTask / show-editor.
Tooltip - wraps a Gantt and displays task text or a custom content component from data-tooltip-id.
HeaderMenu - wraps @wx/svelte-grid header menu and passes api.getTable() to it.
Willow, WillowDark - theme wrappers; each accepts fonts?: boolean and optional children.
Supported functionality
Gantt Data
tasks is an array of task objects; common fields are id, text, start, end, duration, progress, parent, type, open, details, data, unscheduled, segments, base_start, base_end, base_duration, rollup.
links is an array of dependency links with id, source, target, type, optional lag.
- link
type values are s2s, s2e, e2s, e2e.
- task
type defaults are task, summary, milestone; custom types are supported through taskTypes.
- milestone tasks are normalized with
duration = 0 and no end.
- summary task dates are calculated from children when
start / end are missing.
parseTaskDates(tasks, { durationUnit, splitTasks, calendar }) runs inside Gantt and mutates task date fields unless _export is set.
Common data objects:
const tasks = [
{
id: 1,
text: "Project",
type: "summary",
parent: 0,
open: true,
},
{
id: 10,
text: "Task",
type: "task",
parent: 1,
start: new Date(2026, 3, 2),
duration: 3,
progress: 40,
},
{
id: 11,
text: "Milestone",
type: "milestone",
parent: 1,
start: new Date(2026, 3, 8),
},
];
const links = [{ id: 1, source: 10, target: 11, type: "e2s" }];
Gantt Config
- default scales:
[{ unit: "month", step: 1, format: "%F %Y" }, { unit: "day", step: 1, format: "%j" }].
scales rows accept unit, step, format?: string | (date, next) => string, css?: (date) => string.
start, end, and autoScale control timeline bounds; autoScale includes tasks, markers, baselines, projectStart, and projectEnd.
zoom accepts true or IZoomConfig; chart zoom is handled by Ctrl/Command + wheel and emits zoom-scale.
columns defaults to defaultColumns; columns={false} hides the grid and resizer.
- the
text column is rendered with the built-in TextCell; add-task uses ActionCell, is removed in readonly, and moves to the first column in compact chart mode.
cellWidth, cellHeight, scaleHeight control timeline width, row height, and scale row height.
lengthUnit controls chart math; durationUnit controls task duration calculation and is "day" or "hour".
cellBorders is "column" or "full".
readonly disables task drag, progress editing, link editing, grid add column behavior, and editor opening from double click.
selected and activeTask are config inputs, not $bindable() props; read or change live selection through api.getReactiveState() and api.exec("select-task", ...).
highlightTime(date, unit) can return a CSS class for timescale cells and chart background cells when min unit is day or hour.
- compact mode is driven by a
ResizeObserver on document.body; width <= 650 switches the layout to grid/chart modes.
Common config objects:
const columns = [
{ id: "text", header: "Task name", flexgrow: 1, editor: "text" },
{
id: "start",
header: "Start date",
align: "center",
editor: "datepicker",
},
{ id: "duration", header: "Duration", width: 100, align: "center" },
{ id: "add-task", header: "Add task", width: 50, align: "center" },
];
const scales = [
{ unit: "month", step: 1, format: "%F %Y" },
{ unit: "week", step: 1, format: "Week %w" },
{
unit: "day",
step: 1,
format: "%j",
css: date => (date.getDay() === 0 ? "wx-weekend" : ""),
},
];
const zoom = {
level: 2,
minCellWidth: 40,
maxCellWidth: 180,
};
Pro And Advanced Config
baselines shows base_start / base_end / base_duration.
rollups accepts true or { type: "all" | "closest" }; tasks with rollup: true are drawn on summary rows.
markers is an array of { start: Date, text?: string, css?: string }.
criticalPath={{ type: "strict" | "flexible" }} marks critical tasks and links.
schedule={{ type?: "forward", auto?: boolean }} enables scheduling behavior; projectStart and projectEnd provide bounds.
calendar accepts a Calendar from @wx/gantt-store; Gantt then creates default weekend highlighting when calendar is set.
undo enables history state plus undo / redo actions and toolbar buttons.
splitTasks enables task segments editing, split toolbar/menu actions, and segment-aware editor/context-menu targets.
summary={{ autoProgress?: boolean, autoConvert?: boolean }} controls summary progress and summary type conversion behavior.
slack shows task slack visuals.
unscheduledTasks enables task unscheduled support and editor scheduling controls.
api.exec("export-data", config) supports format: "pdf" | "png" | "xlsx" | "mspx" through IExportConfig.
api.exec("import-data", { data, format: "mspx" }) imports MS Project XML.
API And Events
Gantt exposes this API from source through bind:this or init(api):
- state:
getState(), getReactiveState(), getStores()
- events/actions:
exec(action, data), on(action, callback, config?), intercept(action, callback, config?), detach(tag), setNext(eventBusOrProvider)
- task/table helpers:
getTask(id), getTable(waitRender?), serialize(), getHistory()
Component callback props are generated from action names by removing hyphens and prefixing on:
add-task -> onaddtask
update-task -> onupdatetask
select-task -> onselecttask
- custom
taskTemplate action "custom-click" -> oncustomclick
Source routes actions through DataStore first, then calls the matching component callback prop if present.
Common actions:
api.exec("add-task", {
task: { text: "New task", type: "task" },
target: 10,
mode: "after",
show: true,
});
api.exec("update-task", {
id: 10,
task: { text: "Updated", progress: 75 },
});
api.exec("select-task", {
id: 10,
toggle: false,
range: false,
show: "xy",
focus: "grid",
});
api.exec("move-task", { id: 10, target: 1, mode: "child" });
api.exec("filter-tasks", {
filter: task => task.text?.includes("API"),
open: true,
});
api.exec("scroll-chart", { date: new Date(2026, 4, 1) });
Use api.intercept(action, cb) to block or replace built-in behavior; returning false blocks the action. Use api.setNext(provider) to forward data changes to a provider such as RestDataProvider.
Saving
RestDataProvider from @wx/gantt-data-provider persists data changes to a REST backend.
- Wire it once with
api.setNext(provider) in init; the provider then forwards every data action (add-task, update-task, delete-task, move-task, add-link, update-link, delete-link, etc.) emitted on the event bus as the matching REST call. No per-action save handlers needed.
- Initial load uses
provider.getData(); lazy branches use provider.getData(id) inside request-data and dispatch back through provide-data.
- Optional
{ batchURL } constructor option batches concurrent writes into a single endpoint.
import { RestDataProvider } from "@wx/gantt-data-provider";
const provider = new RestDataProvider("/api/gantt");
function init(api) {
api.setNext(provider); // forwards all task/link mutations to REST
}
Toolbar
Toolbar source props are api = null and items = [].
- default items come from
getToolbarButtons({ undo: $undo, splitTasks: $splitTasks }).
- default handled ids are wired through
handleAction(api, item.id, null, _).
- custom item
handler functions are preserved only when the id is not one of the handled default action ids.
- if no task is selected, default toolbar keeps only targetless items such as
add-task and history controls.
undo adds undo and redo; splitTasks adds split-task.
Default toolbar ids:
[
"add-task",
"edit-task",
"delete-task",
"move-task:up",
"move-task:down",
"copy-task",
"cut-task",
"paste-task",
"indent-task:add",
"indent-task:remove",
];
Context Menu
ContextMenu source props are options, api, resolver, filter, at = "point", children, onclick, css.
- the wrapper passes
dataKey="id" to @wx/svelte-menu; Gantt grid rows and bars expose data-id.
- built-in menu options are from
getMenuOptions({ splitTasks, taskTypes, summary }).
- right-clicking a task selects it when it is not already selected.
resolver(id, event) can return true to use the default task, return a replacement context object, or return a falsy value to prevent task resolution.
filter(option, task) is applied across selected tasks; built-in isHidden and isDisabled are also applied.
- wrapper
onclick receives the menu event used in demos as { context, action }.
- built-in handled
action.id values are executed through handleAction(api, action.id, activeId, _) before user onclick.
- with
splitTasks, segment targets use { id, segmentIndex } internally for edit/delete/split actions.
Default menu ids:
[
"add-task:child",
"add-task:before",
"add-task:after",
"convert-task:<taskType>",
"edit-task",
"cut-task",
"copy-task",
"paste-task",
"move-task:up",
"move-task:down",
"indent-task:add",
"indent-task:remove",
"delete-task",
];
Editor
Editor source props are api, items = [], css = "", layout = "default", readonly = false, placement = "sidebar", bottomBar = true, topBar = true, autoSave = true, focus = false, hotkeys = {}.
- editor renders only when
api.getReactiveState()._activeTask exists.
- default items come from
getEditorItems({ unscheduledTasks, rollups, summary, taskTypes }).
- built-in editor comps registered by the wrapper:
select, date, twostate, slider, counter, links, checkbox.
registerEditorItem(name, Component) registers custom comps for items.
- item
key is the task field; editor values are task objects keyed by those key values.
autoSave={true} saves valid changes on field change; autoSave={false} stores local values and saves from a save top/bottom bar action.
- saved task payload removes
links and data; duration may be removed when no duration editor is present.
links editor changes are batched in the wrapper and saved through link actions.
- default
topBar={true} in editable mode creates close/spacer/delete buttons, plus save when autoSave={false}.
Default editor keys:
["text", "details", "type", "start", "end", "duration", "progress", "links"];
Tooltip
Tooltip source props are api, content, children.
- default tooltip text is the task
text, or segment text when data-segment is present.
- custom
content component receives { data }; source also passes segmentIndex for split-task segments.
- direct tooltip override can use a DOM
data-tooltip attribute; placement hint uses data-tooltip-at="left".
- tooltip lookup is debounced by 300ms.
Header Menu
HeaderMenu source props are api, columns, children.
- wrapper calls
api?.getTable() and passes that table API to @wx/svelte-grid HeaderMenu.
- use it when the grid table API is needed for column visibility/menu behavior.
Public Types
import type { Component, ComponentProps } from "svelte";
import { ContextMenu as BaseContextMenu } from "@wx/svelte-menu";
import { Toolbar as BaseToolbar } from "@wx/svelte-toolbar";
import { Editor as BaseEditor } from "@wx/svelte-editor";
import {
HeaderMenu as BaseHeaderMenu,
IColumnConfig as ITableColumn,
} from "@wx/svelte-grid";
import type {
TMethodsConfig,
IApi,
IConfig,
ITask,
IGanttColumn,
} from "@wx/gantt-store";
export * from "@wx/gantt-store";
export { registerEditorItem } from "@wx/svelte-editor";
export interface IColumnConfig extends Omit<IGanttColumn, "header"> {
cell?: ITableColumn["cell"];
header?: ITableColumn["header"];
editor?: ITableColumn["editor"];
}
export declare const Gantt: Component<
{
columns?: false | IColumnConfig[];
taskTemplate?: Component<{
data: ITask;
api: IApi;
onaction: (ev: {
action: string;
data: { [key: string]: any };
}) => void;
}>;
readonly?: boolean;
cellBorders?: "column" | "full";
highlightTime?: (date: Date, unit: "day" | "hour") => string;
init?: (api: IApi) => void;
} & IConfig &
GanttActions<TMethodsConfig>
>;
export declare const HeaderMenu: Component<
ComponentProps<typeof BaseHeaderMenu> & {
api?: IApi;
}
>;
export declare const ContextMenu: Component<
ComponentProps<typeof BaseContextMenu> & {
api?: IApi;
}
>;
export declare const Toolbar: Component<
ComponentProps<typeof BaseToolbar> & {
api?: IApi;
}
>;
export declare const Editor: Component<
ComponentProps<typeof BaseEditor> & {
api?: IApi;
}
>;
export declare const Tooltip: Component<{
content?: Component<{
data: ITask;
}>;
api?: IApi;
children?: () => any;
}>;
export declare const Willow: Component<{
fonts?: boolean;
children?: () => any;
}>;
export declare const WillowDark: Component<{
fonts?: boolean;
children?: () => any;
}>;
/* get component events from store actions*/
type RemoveHyphen<S extends string> = S extends `${infer Head}-${infer Tail}`
? `${Head}${RemoveHyphen<Tail>}`
: S;
type EventName<K extends string> = `on${RemoveHyphen<K>}`;
export type GanttActions<TMethodsConfig extends Record<string, any>> = {
[K in keyof TMethodsConfig as EventName<K & string>]?: (
ev: TMethodsConfig[K]
) => void;
} & {
[key: `on${string}`]: (ev?: any) => void;
};
Styling
Main hooks:
- root/layout:
.wx-gantt, .wx-pseudo-rows, .wx-stuck, .wx-layout, .wx-content
- grid:
.wx-table-container, .wx-table, .wx-grid, .wx-header, .wx-body, .wx-row, .wx-cell, .wx-action, .wx-toggle-icon, .wx-action-icon
- chart:
.wx-chart, .wx-scale, .wx-area, .wx-bars, .wx-bar, .wx-task, .wx-summary, .wx-milestone, .wx-content, .wx-text-out
- custom task types: built-ins render as
.wx-task, .wx-summary, .wx-milestone; custom task types render as .wx-task.<customType>
- task state:
.wx-selected, .wx-critical, .wx-reorder-task, .wx-split, .wx-touch
- progress/links:
.wx-progress-wrapper, .wx-progress-percent, .wx-progress-marker, .wx-link, .wx-line, .wx-line-selected, .wx-delete-link
- split/rollup/baseline/slack:
.wx-segments, .wx-segment, .wx-rollup, .wx-task-rollup, .wx-summary-rollup, .wx-milestone-rollup, .wx-baseline, .wx-slack, .wx-slack-task
- scales/markers/holidays:
.wx-scale, .wx-row, .wx-cell, .wx-weekend, .wx-markers, .wx-marker
- companion components:
.wx-gantt-editor, .wx-gantt-tooltip, .wx-gantt-tooltip-text, .wx-tooltip-area, .wx-menu, .wx-option
- resizer/display controls:
.wx-resizer, .wx-resizer-display-all, .wx-resizer-display-grid, .wx-resizer-display-chart, .wx-button-expand-left, .wx-button-expand-right
Layout defaults:
- the host container must provide height;
.wx-gantt uses height: 100%; width: 100%; overflow-y: auto.
.wx-layout is a flex row with hidden overflow.
.wx-table-container uses flex: 0 0 <grid width> and height: 100%.
.wx-chart uses flex: 1 1 auto with horizontal scrolling.
.wx-scale is sticky at the top of the chart.
columns={false} removes grid and resizer; otherwise grid and chart are split by the resizer.
Important CSS variables:
- bars:
--wx-gantt-bar-font, --wx-gantt-bar-border-radius, --wx-gantt-bar-shadow
- tasks:
--wx-gantt-task-color, --wx-gantt-task-fill-color, --wx-gantt-task-font-color, --wx-gantt-task-border, --wx-gantt-task-border-color
- summaries/milestones:
--wx-gantt-summary-color, --wx-gantt-summary-fill-color, --wx-gantt-summary-font-color, --wx-gantt-milestone-color, --wx-gantt-milestone-border-radius
- critical/slack:
--wx-gantt-critical-color, --wx-gantt-task-critical-color, --wx-gantt-task-critical-fill-color, --wx-gantt-task-slack-color, --wx-gantt-task-slack-border-color
- links:
--wx-gantt-link-color, --wx-gantt-link-color-hovered, --wx-gantt-link-critical-color, --wx-gantt-link-critical-color-hovered, --wx-gantt-link-marker-background, --wx-gantt-link-marker-color
- grid/scale:
--wx-gantt-border, --wx-gantt-select-color, --wx-grid-body-font, --wx-grid-header-font, --wx-timescale-font, --wx-timescale-border, --wx-timescale-shadow
- markers/tooltips:
--wx-gantt-marker-color, --wx-gantt-marker-font, --wx-gantt-marker-font-color, --wx-tooltip-background, --wx-tooltip-font, --wx-tooltip-font-color
<script>
import { Gantt, Willow } from "@wx/svelte-gantt";
</script>
<div class="gantt-shell">
<Willow>
<Gantt {tasks} {links} />
</Willow>
</div>
<style>
.gantt-shell {
height: 100%;
}
.gantt-shell .wx-gantt .wx-bar.wx-task.urgent {
background-color: #f49a82;
border: 1px solid #f45e36;
}
.gantt-shell .wx-gantt .wx-bar.wx-task.urgent .wx-progress-percent {
background-color: #f45e36;
}
.gantt-shell .wx-gantt .my-marker {
background-color: rgba(255, 84, 84, 0.77);
}
</style>
Recipes
Basic Gantt With Theme
<script>
import { Gantt, Willow } from "@wx/svelte-gantt";
const tasks = [
{ id: 1, text: "Planning", type: "summary", parent: 0, open: true },
{
id: 10,
text: "Research",
type: "task",
parent: 1,
start: new Date(2026, 3, 2),
duration: 3,
progress: 50,
},
];
const links = [];
</script>
<div class="gtcell">
<Willow>
<Gantt {tasks} {links} />
</Willow>
</div>
<style>
.gtcell {
height: 100%;
}
</style>
Toolbar, Context Menu, And Editor
<script>
import { Gantt, Toolbar, ContextMenu, Editor } from "@wx/svelte-gantt";
let api = $state();
</script>
<Toolbar {api} />
<div class="gtcell">
<ContextMenu {api}>
<Gantt bind:this={api} {tasks} {links} {scales} undo />
</ContextMenu>
<Editor {api} />
</div>
<style>
.gtcell {
height: calc(100% - 50px);
border-top: var(--wx-gantt-border);
}
</style>
Initialize API And Handle Actions
<script>
import { Gantt, Editor } from "@wx/svelte-gantt";
let api = $state();
function init(ganttApi) {
api = ganttApi;
api.on("add-task", ({ id }) => {
api.exec("show-editor", { id });
});
api.intercept("sort-tasks", ev => {
return ev.key === "text";
});
}
</script>
<Gantt
{init}
{tasks}
{links}
=> console.log(ev.id, ev.task)}
/>
<Editor {api} />
Custom Columns And Inline Editors
<script>
import { Gantt } from "@wx/svelte-gantt";
import AvatarCell from "./AvatarCell.svelte";
const columns = [
{ id: "text", header: "Task name", width: 220, editor: "text" },
{ id: "assigned", header: "Assigned", width: 160, cell: AvatarCell },
{
id: "start",
header: ["Start date", { filter: { type: "datepicker" } }],
align: "center",
width: 130,
editor: "datepicker",
},
{ id: "add-task", header: "Add task", width: 50, align: "center" },
];
</script>
<Gantt {tasks} {links} {columns} cellHeight={40} />
Custom Task Content And Custom Event
<!-- TaskContent.svelte -->
<script>
let { data, onaction } = $props();
function toggle(ev) {
ev.stopPropagation();
onaction({
action: "custom-click",
data: { id: data.id, clicked: !data.clicked },
});
}
</script>
<button ? "Clicked" : "Click"}</button>
<script>
import { Gantt } from "@wx/svelte-gantt";
import TaskContent from "./TaskContent.svelte";
let api = $state();
</script>
<Gantt
bind:this={api}
{tasks}
{links}
taskTemplate={TaskContent}
=>
api.exec("update-task", {
id: ev.id,
task: { clicked: ev.clicked },
})}
/>
Custom Editor Items
<script>
import {
Gantt,
Editor,
getEditorItems,
registerEditorItem,
defaultTaskTypes,
} from "@wx/svelte-gantt";
import { RadioButtonGroup } from "@wx/svelte-core";
registerEditorItem("radio", RadioButtonGroup);
const base = getEditorItems();
const items = base.map(item =>
item.key === "type"
? {
key: "type",
comp: "radio",
label: "Type",
options: defaultTaskTypes.map(type => ({
...type,
value: type.id,
})),
config: { type: "inline" },
}
: item
);
let api = $state();
</script>
<Gantt bind:this={api} {tasks} {links} />
<Editor {api} {items} placement="modal" autoSave={false} />
Custom Menu Options
<script>
import {
Gantt,
ContextMenu,
Editor,
getMenuOptions,
} from "@wx/svelte-gantt";
let api = $state();
const ids = ["cut-task", "copy-task", "paste-task", "delete-task"];
const options = [
{ id: "add-task:after", text: "Add below", icon: "wxi-plus" },
...getMenuOptions().filter(op => ids.includes(op.id)),
{
id: "custom-action",
text: "Custom action",
icon: "wxi-empty",
handler: () => console.log("custom action"),
},
];
</script>
<ContextMenu
{api}
{options}
context, action }) => console.log(context?.id, action?.id)}
>
<Gantt bind:this={api} {tasks} {links} />
</ContextMenu>
<Editor {api} />
Scales, Zoom, Markers, And Holidays
<script>
import { Gantt } from "@wx/svelte-gantt";
const scales = [
{ unit: "year", step: 1, format: "%Y" },
{ unit: "month", step: 1, format: "%F" },
{ unit: "day", step: 1, format: "%j" },
];
const markers = [
{ start: new Date(2026, 3, 2), text: "Start" },
{ start: new Date(2026, 3, 8), text: "Review", css: "my-marker" },
];
function highlightTime(date, unit) {
const weekend = date.getDay() === 0 || date.getDay() === 6;
return unit === "day" && weekend ? "wx-weekend" : "";
}
</script>
<Gantt
{tasks}
{links}
{scales}
{markers}
{highlightTime}
start={new Date(2026, 3, 1)}
end={new Date(2026, 4, 1)}
cellWidth={60}
zoom
/>
Pro Feature Bundle
<script>
import {
Gantt,
ContextMenu,
Editor,
Toolbar,
Tooltip,
} from "@wx/svelte-gantt";
import { Calendar } from "@wx/gantt-store";
import TooltipContent from "./TooltipContent.svelte";
const calendar = new Calendar({
weekHours: {
monday: 8,
tuesday: 8,
wednesday: 8,
thursday: 8,
friday: 8,
saturday: 0,
sunday: 0,
},
});
let api = $state();
</script>
<Toolbar {api} />
<div class="gtcell">
<ContextMenu {api}>
<Tooltip {api} content={TooltipContent}>
<Gantt
bind:this={api}
{tasks}
{links}
{calendar}
baselines
splitTasks
rollups={{ type: "closest" }}
criticalPath={{ type: "flexible" }}
summary={{ autoProgress: true }}
undo
/>
</Tooltip>
</ContextMenu>
<Editor {api} />
</div>
Server Provider And Lazy Data
<script>
import { Gantt, ContextMenu, Editor } from "@wx/svelte-gantt";
import { RestDataProvider } from "@wx/gantt-data-provider";
const provider = new RestDataProvider("/api/gantt");
let api = $state();
let tasks = $state([]);
let links = $state([]);
provider.getData().then(data => {
tasks = data.tasks;
links = data.links;
});
function init(ganttApi) {
api = ganttApi;
api.setNext(provider);
api.on("request-data", ev => {
provider.getData(ev.id).then(data => {
api.exec("provide-data", { id: ev.id, data });
});
});
}
</script>
<ContextMenu {api}>
<Gantt {init} bind:this={api} {tasks} {links} />
</ContextMenu>
<Editor {api} />
Implementation Notes
Tooltip.content is typed as receiving only { data }, but source also passes segmentIndex for split-task segment tooltips.
show-editor public action type is { id: TID }, but split-task source also passes segmentIndex.
Gantt mutates task objects during date normalization; clone tasks before passing them if caller-owned data must remain unchanged.
columns can be false for no grid; it is normalized to an empty column set by the store.
- date column templates are added automatically for
start, end, and duration unless a column has a custom template.
- default
add-task actions create { type: "task", text: _("New Task") } and usually select or show the new task depending on caller.
Source: svar-widgets/gantt — distributed by TomeVault.
1---2name: gantt3description: Use when building, configuring, styling, or modifying SVAR Svelte Gantt / @wx/svelte-gantt timelines, grids, task editors, toolbars, context menus, tooltips, scales, links, and Gantt data actions.4---5Use when building, configuring, styling, or modifying SVAR Svelte Gantt / @wx/svelte-gantt timelines, grids, task editors, toolbars, context menus, tooltips, scales, links, and Gantt data actions.67## Package89```js10import {11 Gantt,12 ContextMenu,13 HeaderMenu,14 Toolbar,15 Tooltip,16 Editor,17 Willow,18 WillowDark,19 version,20 defaultEditorItems,21 defaultToolbarButtons,22 defaultMenuOptions,23 defaultColumns,24 defaultTaskTypes,25 getEditorItems,26 getToolbarButtons,27 getMenuOptions,28 registerScaleUnit,29 registerEditorItem,30} from "@wx/svelte-gantt";31```3233## Components3435- `Gantt` - main grid plus timeline chart; `bind:this` exposes the Gantt API object from source.36- `Toolbar` - Gantt-aware wrapper around `@wx/svelte-toolbar`; accepts `api` and optional `items`.37- `ContextMenu` - Gantt-aware wrapper around `@wx/svelte-menu` context menu; resolves tasks from Gantt `data-id`.38- `Editor` - Gantt-aware wrapper around `@wx/svelte-editor`; opens for `activeTask` / `show-editor`.39- `Tooltip` - wraps a Gantt and displays task text or a custom content component from `data-tooltip-id`.40- `HeaderMenu` - wraps `@wx/svelte-grid` header menu and passes `api.getTable()` to it.41- `Willow`, `WillowDark` - theme wrappers; each accepts `fonts?: boolean` and optional children.4243## Supported functionality4445### Gantt Data4647- `tasks` is an array of task objects; common fields are `id`, `text`, `start`, `end`, `duration`, `progress`, `parent`, `type`, `open`, `details`, `data`, `unscheduled`, `segments`, `base_start`, `base_end`, `base_duration`, `rollup`.48- `links` is an array of dependency links with `id`, `source`, `target`, `type`, optional `lag`.49- link `type` values are `s2s`, `s2e`, `e2s`, `e2e`.50- task `type` defaults are `task`, `summary`, `milestone`; custom types are supported through `taskTypes`.51- milestone tasks are normalized with `duration = 0` and no `end`.52- summary task dates are calculated from children when `start` / `end` are missing.53- `parseTaskDates(tasks, { durationUnit, splitTasks, calendar })` runs inside `Gantt` and mutates task date fields unless `_export` is set.5455Common data objects:5657```js58const tasks = [59 {60 id: 1,61 text: "Project",62 type: "summary",63 parent: 0,64 open: true,65 },66 {67 id: 10,68 text: "Task",69 type: "task",70 parent: 1,71 start: new Date(2026, 3, 2),72 duration: 3,73 progress: 40,74 },75 {76 id: 11,77 text: "Milestone",78 type: "milestone",79 parent: 1,80 start: new Date(2026, 3, 8),81 },82];8384const links = [{ id: 1, source: 10, target: 11, type: "e2s" }];85```8687### Gantt Config8889- default scales: `[{ unit: "month", step: 1, format: "%F %Y" }, { unit: "day", step: 1, format: "%j" }]`.90- `scales` rows accept `unit`, `step`, `format?: string | (date, next) => string`, `css?: (date) => string`.91- `start`, `end`, and `autoScale` control timeline bounds; `autoScale` includes tasks, markers, baselines, `projectStart`, and `projectEnd`.92- `zoom` accepts `true` or `IZoomConfig`; chart zoom is handled by Ctrl/Command + wheel and emits `zoom-scale`.93- `columns` defaults to `defaultColumns`; `columns={false}` hides the grid and resizer.94- the `text` column is rendered with the built-in `TextCell`; `add-task` uses `ActionCell`, is removed in `readonly`, and moves to the first column in compact chart mode.95- `cellWidth`, `cellHeight`, `scaleHeight` control timeline width, row height, and scale row height.96- `lengthUnit` controls chart math; `durationUnit` controls task duration calculation and is `"day"` or `"hour"`.97- `cellBorders` is `"column"` or `"full"`.98- `readonly` disables task drag, progress editing, link editing, grid add column behavior, and editor opening from double click.99- `selected` and `activeTask` are config inputs, not `$bindable()` props; read or change live selection through `api.getReactiveState()` and `api.exec("select-task", ...)`.100- `highlightTime(date, unit)` can return a CSS class for timescale cells and chart background cells when min unit is `day` or `hour`.101- compact mode is driven by a `ResizeObserver` on `document.body`; width `<= 650` switches the layout to grid/chart modes.102103Common config objects:104105```js106const columns = [107 { id: "text", header: "Task name", flexgrow: 1, editor: "text" },108 {109 id: "start",110 header: "Start date",111 align: "center",112 editor: "datepicker",113 },114 { id: "duration", header: "Duration", width: 100, align: "center" },115 { id: "add-task", header: "Add task", width: 50, align: "center" },116];117118const scales = [119 { unit: "month", step: 1, format: "%F %Y" },120 { unit: "week", step: 1, format: "Week %w" },121 {122 unit: "day",123 step: 1,124 format: "%j",125 css: date => (date.getDay() === 0 ? "wx-weekend" : ""),126 },127];128129const zoom = {130 level: 2,131 minCellWidth: 40,132 maxCellWidth: 180,133};134```135136### Pro And Advanced Config137138- `baselines` shows `base_start` / `base_end` / `base_duration`.139- `rollups` accepts `true` or `{ type: "all" | "closest" }`; tasks with `rollup: true` are drawn on summary rows.140- `markers` is an array of `{ start: Date, text?: string, css?: string }`.141- `criticalPath={{ type: "strict" | "flexible" }}` marks critical tasks and links.142- `schedule={{ type?: "forward", auto?: boolean }}` enables scheduling behavior; `projectStart` and `projectEnd` provide bounds.143- `calendar` accepts a `Calendar` from `@wx/gantt-store`; Gantt then creates default weekend highlighting when `calendar` is set.144- `undo` enables history state plus `undo` / `redo` actions and toolbar buttons.145- `splitTasks` enables task `segments` editing, split toolbar/menu actions, and segment-aware editor/context-menu targets.146- `summary={{ autoProgress?: boolean, autoConvert?: boolean }}` controls summary progress and summary type conversion behavior.147- `slack` shows task slack visuals.148- `unscheduledTasks` enables task `unscheduled` support and editor scheduling controls.149- `api.exec("export-data", config)` supports `format: "pdf" | "png" | "xlsx" | "mspx"` through `IExportConfig`.150- `api.exec("import-data", { data, format: "mspx" })` imports MS Project XML.151152### API And Events153154`Gantt` exposes this API from source through `bind:this` or `init(api)`:155156- state: `getState()`, `getReactiveState()`, `getStores()`157- events/actions: `exec(action, data)`, `on(action, callback, config?)`, `intercept(action, callback, config?)`, `detach(tag)`, `setNext(eventBusOrProvider)`158- task/table helpers: `getTask(id)`, `getTable(waitRender?)`, `serialize()`, `getHistory()`159160Component callback props are generated from action names by removing hyphens and prefixing `on`:161162- `add-task` -> `onaddtask`163- `update-task` -> `onupdatetask`164- `select-task` -> `onselecttask`165- custom `taskTemplate` action `"custom-click"` -> `oncustomclick`166167Source routes actions through `DataStore` first, then calls the matching component callback prop if present.168169Common actions:170171```js172api.exec("add-task", {173 task: { text: "New task", type: "task" },174 target: 10,175 mode: "after",176 show: true,177});178179api.exec("update-task", {180 id: 10,181 task: { text: "Updated", progress: 75 },182});183184api.exec("select-task", {185 id: 10,186 toggle: false,187 range: false,188 show: "xy",189 focus: "grid",190});191192api.exec("move-task", { id: 10, target: 1, mode: "child" });193api.exec("filter-tasks", {194 filter: task => task.text?.includes("API"),195 open: true,196});197api.exec("scroll-chart", { date: new Date(2026, 4, 1) });198```199200Use `api.intercept(action, cb)` to block or replace built-in behavior; returning `false` blocks the action. Use `api.setNext(provider)` to forward data changes to a provider such as `RestDataProvider`.201202### Saving203204- `RestDataProvider` from `@wx/gantt-data-provider` persists data changes to a REST backend.205- Wire it once with `api.setNext(provider)` in `init`; the provider then forwards every data action (`add-task`, `update-task`, `delete-task`, `move-task`, `add-link`, `update-link`, `delete-link`, etc.) emitted on the event bus as the matching REST call. No per-action save handlers needed.206- Initial load uses `provider.getData()`; lazy branches use `provider.getData(id)` inside `request-data` and dispatch back through `provide-data`.207- Optional `{ batchURL }` constructor option batches concurrent writes into a single endpoint.208209```js210import { RestDataProvider } from "@wx/gantt-data-provider";211212const provider = new RestDataProvider("/api/gantt");213214function init(api) {215 api.setNext(provider); // forwards all task/link mutations to REST216}217```218219### Toolbar220221- `Toolbar` source props are `api = null` and `items = []`.222- default items come from `getToolbarButtons({ undo: $undo, splitTasks: $splitTasks })`.223- default handled ids are wired through `handleAction(api, item.id, null, _)`.224- custom item `handler` functions are preserved only when the id is not one of the handled default action ids.225- if no task is selected, default toolbar keeps only targetless items such as `add-task` and history controls.226- `undo` adds `undo` and `redo`; `splitTasks` adds `split-task`.227228Default toolbar ids:229230```js231[232 "add-task",233 "edit-task",234 "delete-task",235 "move-task:up",236 "move-task:down",237 "copy-task",238 "cut-task",239 "paste-task",240 "indent-task:add",241 "indent-task:remove",242];243```244245### Context Menu246247- `ContextMenu` source props are `options`, `api`, `resolver`, `filter`, `at = "point"`, `children`, `onclick`, `css`.248- the wrapper passes `dataKey="id"` to `@wx/svelte-menu`; Gantt grid rows and bars expose `data-id`.249- built-in menu options are from `getMenuOptions({ splitTasks, taskTypes, summary })`.250- right-clicking a task selects it when it is not already selected.251- `resolver(id, event)` can return `true` to use the default task, return a replacement context object, or return a falsy value to prevent task resolution.252- `filter(option, task)` is applied across selected tasks; built-in `isHidden` and `isDisabled` are also applied.253- wrapper `onclick` receives the menu event used in demos as `{ context, action }`.254- built-in handled `action.id` values are executed through `handleAction(api, action.id, activeId, _)` before user `onclick`.255- with `splitTasks`, segment targets use `{ id, segmentIndex }` internally for edit/delete/split actions.256257Default menu ids:258259```js260[261 "add-task:child",262 "add-task:before",263 "add-task:after",264 "convert-task:<taskType>",265 "edit-task",266 "cut-task",267 "copy-task",268 "paste-task",269 "move-task:up",270 "move-task:down",271 "indent-task:add",272 "indent-task:remove",273 "delete-task",274];275```276277### Editor278279- `Editor` source props are `api`, `items = []`, `css = ""`, `layout = "default"`, `readonly = false`, `placement = "sidebar"`, `bottomBar = true`, `topBar = true`, `autoSave = true`, `focus = false`, `hotkeys = {}`.280- editor renders only when `api.getReactiveState()._activeTask` exists.281- default items come from `getEditorItems({ unscheduledTasks, rollups, summary, taskTypes })`.282- built-in editor comps registered by the wrapper: `select`, `date`, `twostate`, `slider`, `counter`, `links`, `checkbox`.283- `registerEditorItem(name, Component)` registers custom comps for `items`.284- item `key` is the task field; editor values are task objects keyed by those `key` values.285- `autoSave={true}` saves valid changes on field change; `autoSave={false}` stores local values and saves from a `save` top/bottom bar action.286- saved task payload removes `links` and `data`; `duration` may be removed when no duration editor is present.287- `links` editor changes are batched in the wrapper and saved through link actions.288- default `topBar={true}` in editable mode creates close/spacer/delete buttons, plus save when `autoSave={false}`.289290Default editor keys:291292```js293["text", "details", "type", "start", "end", "duration", "progress", "links"];294```295296### Tooltip297298- `Tooltip` source props are `api`, `content`, `children`.299- default tooltip text is the task `text`, or segment `text` when `data-segment` is present.300- custom `content` component receives `{ data }`; source also passes `segmentIndex` for split-task segments.301- direct tooltip override can use a DOM `data-tooltip` attribute; placement hint uses `data-tooltip-at="left"`.302- tooltip lookup is debounced by 300ms.303304### Header Menu305306- `HeaderMenu` source props are `api`, `columns`, `children`.307- wrapper calls `api?.getTable()` and passes that table API to `@wx/svelte-grid` `HeaderMenu`.308- use it when the grid table API is needed for column visibility/menu behavior.309310## Public Types311312```ts313import type { Component, ComponentProps } from "svelte";314import { ContextMenu as BaseContextMenu } from "@wx/svelte-menu";315import { Toolbar as BaseToolbar } from "@wx/svelte-toolbar";316import { Editor as BaseEditor } from "@wx/svelte-editor";317import {318 HeaderMenu as BaseHeaderMenu,319 IColumnConfig as ITableColumn,320} from "@wx/svelte-grid";321322import type {323 TMethodsConfig,324 IApi,325 IConfig,326 ITask,327 IGanttColumn,328} from "@wx/gantt-store";329330export * from "@wx/gantt-store";331export { registerEditorItem } from "@wx/svelte-editor";332333export interface IColumnConfig extends Omit<IGanttColumn, "header"> {334 cell?: ITableColumn["cell"];335 header?: ITableColumn["header"];336 editor?: ITableColumn["editor"];337}338339export declare const Gantt: Component<340 {341 columns?: false | IColumnConfig[];342 taskTemplate?: Component<{343 data: ITask;344 api: IApi;345 onaction: (ev: {346 action: string;347 data: { [key: string]: any };348 }) => void;349 }>;350 readonly?: boolean;351 cellBorders?: "column" | "full";352 highlightTime?: (date: Date, unit: "day" | "hour") => string;353 init?: (api: IApi) => void;354 } & IConfig &355 GanttActions<TMethodsConfig>356>;357358export declare const HeaderMenu: Component<359 ComponentProps<typeof BaseHeaderMenu> & {360 api?: IApi;361 }362>;363364export declare const ContextMenu: Component<365 ComponentProps<typeof BaseContextMenu> & {366 api?: IApi;367 }368>;369370export declare const Toolbar: Component<371 ComponentProps<typeof BaseToolbar> & {372 api?: IApi;373 }374>;375376export declare const Editor: Component<377 ComponentProps<typeof BaseEditor> & {378 api?: IApi;379 }380>;381382export declare const Tooltip: Component<{383 content?: Component<{384 data: ITask;385 }>;386 api?: IApi;387 children?: () => any;388}>;389390export declare const Willow: Component<{391 fonts?: boolean;392 children?: () => any;393}>;394395export declare const WillowDark: Component<{396 fonts?: boolean;397 children?: () => any;398}>;399400/* get component events from store actions*/401type RemoveHyphen<S extends string> = S extends `${infer Head}-${infer Tail}`402 ? `${Head}${RemoveHyphen<Tail>}`403 : S;404405type EventName<K extends string> = `on${RemoveHyphen<K>}`;406407export type GanttActions<TMethodsConfig extends Record<string, any>> = {408 [K in keyof TMethodsConfig as EventName<K & string>]?: (409 ev: TMethodsConfig[K]410 ) => void;411} & {412 [key: `on${string}`]: (ev?: any) => void;413};414```415416## Styling417418Main hooks:419420- root/layout: `.wx-gantt`, `.wx-pseudo-rows`, `.wx-stuck`, `.wx-layout`, `.wx-content`421- grid: `.wx-table-container`, `.wx-table`, `.wx-grid`, `.wx-header`, `.wx-body`, `.wx-row`, `.wx-cell`, `.wx-action`, `.wx-toggle-icon`, `.wx-action-icon`422- chart: `.wx-chart`, `.wx-scale`, `.wx-area`, `.wx-bars`, `.wx-bar`, `.wx-task`, `.wx-summary`, `.wx-milestone`, `.wx-content`, `.wx-text-out`423- custom task types: built-ins render as `.wx-task`, `.wx-summary`, `.wx-milestone`; custom task types render as `.wx-task.<customType>`424- task state: `.wx-selected`, `.wx-critical`, `.wx-reorder-task`, `.wx-split`, `.wx-touch`425- progress/links: `.wx-progress-wrapper`, `.wx-progress-percent`, `.wx-progress-marker`, `.wx-link`, `.wx-line`, `.wx-line-selected`, `.wx-delete-link`426- split/rollup/baseline/slack: `.wx-segments`, `.wx-segment`, `.wx-rollup`, `.wx-task-rollup`, `.wx-summary-rollup`, `.wx-milestone-rollup`, `.wx-baseline`, `.wx-slack`, `.wx-slack-task`427- scales/markers/holidays: `.wx-scale`, `.wx-row`, `.wx-cell`, `.wx-weekend`, `.wx-markers`, `.wx-marker`428- companion components: `.wx-gantt-editor`, `.wx-gantt-tooltip`, `.wx-gantt-tooltip-text`, `.wx-tooltip-area`, `.wx-menu`, `.wx-option`429- resizer/display controls: `.wx-resizer`, `.wx-resizer-display-all`, `.wx-resizer-display-grid`, `.wx-resizer-display-chart`, `.wx-button-expand-left`, `.wx-button-expand-right`430431Layout defaults:432433- the host container must provide height; `.wx-gantt` uses `height: 100%; width: 100%; overflow-y: auto`.434- `.wx-layout` is a flex row with hidden overflow.435- `.wx-table-container` uses `flex: 0 0 <grid width>` and `height: 100%`.436- `.wx-chart` uses `flex: 1 1 auto` with horizontal scrolling.437- `.wx-scale` is sticky at the top of the chart.438- `columns={false}` removes grid and resizer; otherwise grid and chart are split by the resizer.439440Important CSS variables:441442- bars: `--wx-gantt-bar-font`, `--wx-gantt-bar-border-radius`, `--wx-gantt-bar-shadow`443- tasks: `--wx-gantt-task-color`, `--wx-gantt-task-fill-color`, `--wx-gantt-task-font-color`, `--wx-gantt-task-border`, `--wx-gantt-task-border-color`444- summaries/milestones: `--wx-gantt-summary-color`, `--wx-gantt-summary-fill-color`, `--wx-gantt-summary-font-color`, `--wx-gantt-milestone-color`, `--wx-gantt-milestone-border-radius`445- critical/slack: `--wx-gantt-critical-color`, `--wx-gantt-task-critical-color`, `--wx-gantt-task-critical-fill-color`, `--wx-gantt-task-slack-color`, `--wx-gantt-task-slack-border-color`446- links: `--wx-gantt-link-color`, `--wx-gantt-link-color-hovered`, `--wx-gantt-link-critical-color`, `--wx-gantt-link-critical-color-hovered`, `--wx-gantt-link-marker-background`, `--wx-gantt-link-marker-color`447- grid/scale: `--wx-gantt-border`, `--wx-gantt-select-color`, `--wx-grid-body-font`, `--wx-grid-header-font`, `--wx-timescale-font`, `--wx-timescale-border`, `--wx-timescale-shadow`448- markers/tooltips: `--wx-gantt-marker-color`, `--wx-gantt-marker-font`, `--wx-gantt-marker-font-color`, `--wx-tooltip-background`, `--wx-tooltip-font`, `--wx-tooltip-font-color`449450```svelte451<script>452 import { Gantt, Willow } from "@wx/svelte-gantt";453</script>454455<div class="gantt-shell">456 <Willow>457 <Gantt {tasks} {links} />458 </Willow>459</div>460461<style>462 .gantt-shell {463 height: 100%;464 }465466 .gantt-shell .wx-gantt .wx-bar.wx-task.urgent {467 background-color: #f49a82;468 border: 1px solid #f45e36;469 }470471 .gantt-shell .wx-gantt .wx-bar.wx-task.urgent .wx-progress-percent {472 background-color: #f45e36;473 }474475 .gantt-shell .wx-gantt .my-marker {476 background-color: rgba(255, 84, 84, 0.77);477 }478</style>479```480481## Recipes482483### Basic Gantt With Theme484485```svelte486<script>487 import { Gantt, Willow } from "@wx/svelte-gantt";488489 const tasks = [490 { id: 1, text: "Planning", type: "summary", parent: 0, open: true },491 {492 id: 10,493 text: "Research",494 type: "task",495 parent: 1,496 start: new Date(2026, 3, 2),497 duration: 3,498 progress: 50,499 },500 ];501 const links = [];502</script>503504<div class="gtcell">505 <Willow>506 <Gantt {tasks} {links} />507 </Willow>508</div>509510<style>511 .gtcell {512 height: 100%;513 }514</style>515```516517### Toolbar, Context Menu, And Editor518519```svelte520<script>521 import { Gantt, Toolbar, ContextMenu, Editor } from "@wx/svelte-gantt";522523 let api = $state();524</script>525526<Toolbar {api} />527528<div class="gtcell">529 <ContextMenu {api}>530 <Gantt bind:this={api} {tasks} {links} {scales} undo />531 </ContextMenu>532 <Editor {api} />533</div>534535<style>536 .gtcell {537 height: calc(100% - 50px);538 border-top: var(--wx-gantt-border);539 }540</style>541```542543### Initialize API And Handle Actions544545```svelte546<script>547 import { Gantt, Editor } from "@wx/svelte-gantt";548549 let api = $state();550551 function init(ganttApi) {552 api = ganttApi;553554 api.on("add-task", ({ id }) => {555 api.exec("show-editor", { id });556 });557558 api.intercept("sort-tasks", ev => {559 return ev.key === "text";560 });561 }562</script>563564<Gantt565 {init}566 {tasks}567 {links}568 onupdatetask={ev => console.log(ev.id, ev.task)}569/>570<Editor {api} />571```572573### Custom Columns And Inline Editors574575```svelte576<script>577 import { Gantt } from "@wx/svelte-gantt";578 import AvatarCell from "./AvatarCell.svelte";579580 const columns = [581 { id: "text", header: "Task name", width: 220, editor: "text" },582 { id: "assigned", header: "Assigned", width: 160, cell: AvatarCell },583 {584 id: "start",585 header: ["Start date", { filter: { type: "datepicker" } }],586 align: "center",587 width: 130,588 editor: "datepicker",589 },590 { id: "add-task", header: "Add task", width: 50, align: "center" },591 ];592</script>593594<Gantt {tasks} {links} {columns} cellHeight={40} />595```596597### Custom Task Content And Custom Event598599```svelte600<!-- TaskContent.svelte -->601<script>602 let { data, onaction } = $props();603604 function toggle(ev) {605 ev.stopPropagation();606 onaction({607 action: "custom-click",608 data: { id: data.id, clicked: !data.clicked },609 });610 }611</script>612613<button onclick={toggle}>{data.clicked ? "Clicked" : "Click"}</button>614```615616```svelte617<script>618 import { Gantt } from "@wx/svelte-gantt";619 import TaskContent from "./TaskContent.svelte";620621 let api = $state();622</script>623624<Gantt625 bind:this={api}626 {tasks}627 {links}628 taskTemplate={TaskContent}629 oncustomclick={ev =>630 api.exec("update-task", {631 id: ev.id,632 task: { clicked: ev.clicked },633 })}634/>635```636637### Custom Editor Items638639```svelte640<script>641 import {642 Gantt,643 Editor,644 getEditorItems,645 registerEditorItem,646 defaultTaskTypes,647 } from "@wx/svelte-gantt";648 import { RadioButtonGroup } from "@wx/svelte-core";649650 registerEditorItem("radio", RadioButtonGroup);651652 const base = getEditorItems();653 const items = base.map(item =>654 item.key === "type"655 ? {656 key: "type",657 comp: "radio",658 label: "Type",659 options: defaultTaskTypes.map(type => ({660 ...type,661 value: type.id,662 })),663 config: { type: "inline" },664 }665 : item666 );667668 let api = $state();669</script>670671<Gantt bind:this={api} {tasks} {links} />672<Editor {api} {items} placement="modal" autoSave={false} />673```674675### Custom Menu Options676677```svelte678<script>679 import {680 Gantt,681 ContextMenu,682 Editor,683 getMenuOptions,684 } from "@wx/svelte-gantt";685686 let api = $state();687 const ids = ["cut-task", "copy-task", "paste-task", "delete-task"];688 const options = [689 { id: "add-task:after", text: "Add below", icon: "wxi-plus" },690 ...getMenuOptions().filter(op => ids.includes(op.id)),691 {692 id: "custom-action",693 text: "Custom action",694 icon: "wxi-empty",695 handler: () => console.log("custom action"),696 },697 ];698</script>699700<ContextMenu701 {api}702 {options}703 onclick={({ context, action }) => console.log(context?.id, action?.id)}704>705 <Gantt bind:this={api} {tasks} {links} />706</ContextMenu>707<Editor {api} />708```709710### Scales, Zoom, Markers, And Holidays711712```svelte713<script>714 import { Gantt } from "@wx/svelte-gantt";715716 const scales = [717 { unit: "year", step: 1, format: "%Y" },718 { unit: "month", step: 1, format: "%F" },719 { unit: "day", step: 1, format: "%j" },720 ];721722 const markers = [723 { start: new Date(2026, 3, 2), text: "Start" },724 { start: new Date(2026, 3, 8), text: "Review", css: "my-marker" },725 ];726727 function highlightTime(date, unit) {728 const weekend = date.getDay() === 0 || date.getDay() === 6;729 return unit === "day" && weekend ? "wx-weekend" : "";730 }731</script>732733<Gantt734 {tasks}735 {links}736 {scales}737 {markers}738 {highlightTime}739 start={new Date(2026, 3, 1)}740 end={new Date(2026, 4, 1)}741 cellWidth={60}742 zoom743/>744```745746### Pro Feature Bundle747748```svelte749<script>750 import {751 Gantt,752 ContextMenu,753 Editor,754 Toolbar,755 Tooltip,756 } from "@wx/svelte-gantt";757 import { Calendar } from "@wx/gantt-store";758 import TooltipContent from "./TooltipContent.svelte";759760 const calendar = new Calendar({761 weekHours: {762 monday: 8,763 tuesday: 8,764 wednesday: 8,765 thursday: 8,766 friday: 8,767 saturday: 0,768 sunday: 0,769 },770 });771772 let api = $state();773</script>774775<Toolbar {api} />776<div class="gtcell">777 <ContextMenu {api}>778 <Tooltip {api} content={TooltipContent}>779 <Gantt780 bind:this={api}781 {tasks}782 {links}783 {calendar}784 baselines785 splitTasks786 rollups={{ type: "closest" }}787 criticalPath={{ type: "flexible" }}788 summary={{ autoProgress: true }}789 undo790 />791 </Tooltip>792 </ContextMenu>793 <Editor {api} />794</div>795```796797### Server Provider And Lazy Data798799```svelte800<script>801 import { Gantt, ContextMenu, Editor } from "@wx/svelte-gantt";802 import { RestDataProvider } from "@wx/gantt-data-provider";803804 const provider = new RestDataProvider("/api/gantt");805 let api = $state();806 let tasks = $state([]);807 let links = $state([]);808809 provider.getData().then(data => {810 tasks = data.tasks;811 links = data.links;812 });813814 function init(ganttApi) {815 api = ganttApi;816 api.setNext(provider);817 api.on("request-data", ev => {818 provider.getData(ev.id).then(data => {819 api.exec("provide-data", { id: ev.id, data });820 });821 });822 }823</script>824825<ContextMenu {api}>826 <Gantt {init} bind:this={api} {tasks} {links} />827</ContextMenu>828<Editor {api} />829```830831## Implementation Notes832833- `Tooltip.content` is typed as receiving only `{ data }`, but source also passes `segmentIndex` for split-task segment tooltips.834- `show-editor` public action type is `{ id: TID }`, but split-task source also passes `segmentIndex`.835- `Gantt` mutates task objects during date normalization; clone `tasks` before passing them if caller-owned data must remain unchanged.836- `columns` can be `false` for no grid; it is normalized to an empty column set by the store.837- date column templates are added automatically for `start`, `end`, and `duration` unless a column has a custom `template`.838- default `add-task` actions create `{ type: "task", text: _("New Task") }` and usually select or show the new task depending on caller.839840---841> Source: [svar-widgets/gantt](https://github.com/svar-widgets/gantt) — distributed by [TomeVault](https://tomevault.io).842<!-- tomevault:4.0:skill_md:2026-06-17 -->