@canvas-components/gantt-table
v0.1.5
Published
Framework-agnostic Canvas Gantt chart core engine.
Downloads
102
Readme
@canvas-components/gantt-table
纯原生 Canvas 甘特图核心,目录组织参考 @canvas-components/list-table:配置位于 schema,布局与日期计算位于 domain,绘制位于 rendering,命中测试位于 interaction。
首版支持时间轴、任务条、进度、依赖线、横纵滚动、固定时间表头与任务列、任务选中、拖拽平移日期和任务条宽度调整。开启 editable 后,可在未排期任务行中拖拽添加任务条,移动或缩放已有任务条,并用 Delete/Backspace 删除选中任务条(清除 start / end,保留任务行)。选中任务后还可以从左右连接锚点拖到另一任务条,新建或修改依赖;依赖线可独立选中并用 Delete/Backspace 删除。通过 snapToColumn 可让移动与缩放吸附到最小日维度,拖拽结果始终限制在时间轴范围内;开启 rowDraggable 后可在固定任务列中拖拽调整行顺序,相邻行会以动画让出目标位置,并通过 onTaskReorder 返回新顺序。weekendBackground 可控制是否显示周末底色,颜色可通过 theme.weekendBackgroundColor 调整。滚动位置会根据内容和视口尺寸自动约束,也可以通过实例的 scrollTo(left, top) 方法定位。React 接入位于独立的 @canvas-components/react-gantt-table 适配包中。
React 配置示例
import { useState } from "react";
import { ReactGanttTable } from "@canvas-components/react-gantt-table";
import type { GanttTask } from "@canvas-components/gantt-table";
const taskContent = {
render: (task: GanttTask) => `${task.title} · ${task.progress ?? 0}%`,
style: { color: "#ffffff", fontWeight: 600, textAlign: "center" },
} satisfies Pick<GanttTask, "render" | "style">;
export function ProjectPlan() {
const [tasks, setTasks] = useState<GanttTask[]>([
{
id: "research",
title: "需求调研",
start: "2026-09-01",
end: "2026-09-04",
progress: 100,
...taskContent,
},
{
id: "design",
title: "交互设计",
start: "2026-09-04",
end: "2026-09-09",
progress: 60,
...taskContent,
},
]);
const [dependencies, setDependencies] = useState([
{ from: "research", to: "design", type: "finish-to-start" as const },
]);
return (
<ReactGanttTable
tasks={tasks}
dependencies={dependencies}
startDate="2026-08-31"
endDate="2026-10-01"
scale="day"
taskColumnWidth={180}
editable
taskColumnResizable
taskResizable
snapToColumn
rowDraggable
weekendBackground
theme={{ weekendBackgroundColor: "#fafafa" }}
onTaskChange={(changedTask) => {
setTasks((current) =>
current.map((task) =>
task.id === changedTask.id ? { ...changedTask } : task,
),
);
}}
onTaskReorder={(reorderedTasks) => {
setTasks(reorderedTasks.map((task) => ({ ...task })));
}}
onDependenciesChange={(nextDependencies) => {
setDependencies(nextDependencies.map((item) => ({ ...item })));
}}
style={{ height: 360 }}
/>
);
}原生配置示例
import { Gantt } from "@canvas-components/gantt-table";
const tasksWithContent = tasks.map((task) => ({
...task,
render: (currentTask) =>
`${currentTask.title} · ${currentTask.progress ?? 0}%`,
style: { color: "#ffffff", fontWeight: 600, textAlign: "center" },
}));
const gantt = new Gantt(container, {
tasks: tasksWithContent,
dependencies,
startDate: "2026-08-31",
endDate: "2026-10-01",
scale: "week",
editable: true,
taskColumnResizable: true,
taskResizable: true,
snapToColumn: true,
rowDraggable: true,
weekendBackground: true,
onTaskChange: (task) => console.log(task),
onTaskReorder: (nextTasks) => console.log(nextTasks),
onDependenciesChange: (nextDependencies) => console.log(nextDependencies),
});
gantt.mount();
gantt.scrollTo(320, 0);
// gantt.destroy();常用配置
| 配置 | 说明 | 默认值 |
| ------------------------------------------- | ------------------------------------ | ------------- |
| scale | 时间轴维度:day、week、month | day |
| headerLevels | 时间轴多层表头配置 | 当前 scale |
| headerHeight | 每层表头的默认高度 | 56 |
| editable | 开启任务条新增、移动、缩放与键盘删除 | false |
| taskResizable | 编辑模式下允许移动及拖拽两端调整日期 | true |
| snapToColumn | 鼠标释放后吸附到最小日维度 | false |
| rowDraggable | 在固定任务列中拖拽改变任务顺序 | false |
| multiTask | 开启一行多个任务条模式 | false |
| debug | Canvas 调试叠层、日志和性能埋点配置 | undefined |
| taskColumnResizable | 允许拖拽改变固定任务列宽度 | true |
| taskColumnMinWidth / taskColumnMaxWidth | 固定任务列宽度限制 | 120 / 480 |
| weekendBackground | 是否显示周末底色 | true |
| theme.weekendBackgroundColor | 周末背景颜色 | #fafafa |
| GanttTask.render | 自定义当前任务条内部内容 | - |
| GanttTask.style | 当前任务条内部文字样式 | - |
| onTaskChange | 任务条新增、移动、缩放或删除完成回调 | - |
| onTaskReorder | 行拖拽排序完成回调 | - |
| onDependenciesChange | 依赖连线新增、修改或删除回调 | - |
调试模式
通过 debug.enabled 开启与 list-table 一致的 Canvas 调试叠层。默认显示多层表头、表体行、任务条、固定列分隔线、视口和鼠标命中单元格;绘制节点详情统一使用公共调试 popup。各部分可通过 showHeaderBounds、showBodyBounds、showTaskBounds、showFixedSplitLines、showViewportBounds、showHitTarget 和 showNodeInspector 独立关闭。
const ganttRef = useRef<ReactGanttTableRef>(null);
<ReactGanttTable
ref={ganttRef}
debug={{
enabled: true,
logDrawCalls: true,
onPerformance: (metric) => console.log(metric),
}}
/>;
const snapshot = ganttRef.current?.getDebugSnapshot();getDebugSnapshot() 返回最近一帧的布局、视口、滚动位置、可见行范围、任务与依赖数量、选中态、交互态和布局/渲染耗时。logStateChanges 可输出配置与滚动变化,logDrawCalls 可输出每帧快照;两项默认关闭。
任务可通过 color 单独设置未完成区域颜色,通过 progressColor 设置已完成区域颜色,progress 接受 0 到 100 的进度百分比。未配置任务级颜色时,分别回退到 theme.taskColor 与 theme.taskProgressColor。
每个任务可通过同级的 render(task, rowIndex) 与 style 分别配置内部内容和文字样式。render 返回字符串、数字或空值;style 支持 color、fontSize、fontWeight、fontFamily 和 textAlign。内容会裁剪在任务条的圆角范围内,空间不足时自动显示省略号。
默认每个顶层 GanttTask 对应一行和一个任务条。开启 multiTask 后,顶层任务作为行定义,固定列显示其 title,并通过 tasks 配置同一行内的多个任务条:
<ReactGanttTable
multiTask
tasks={[
{
id: "development-row",
title: "核心开发",
tasks: [
{
id: "api",
title: "接口开发",
start: "2026-09-09",
end: "2026-09-13",
},
{
id: "ui",
title: "界面开发",
start: "2026-09-13",
end: "2026-09-17",
},
],
},
]}
/>多任务模式下,选择、移动、缩放、删除、onTaskChange 和依赖连线均以行内任务为单位;固定列拖拽仍以顶层行为单位。若需要在空白区域拖拽创建任务条,可在行内预先提供未设置 start / end 的任务。
通过 headerLevels 可由上至下配置多层时间表头。支持 year、quarter、month、week 和 day,每层可单独设置 height 或通过 formatter(start, end) 自定义文案。未配置时保持当前 scale 对应的单层表头:
<ReactGanttTable
headerHeight={36}
headerLevels={[
{ unit: "month", height: 42 },
{ unit: "week" },
{
unit: "day",
formatter: (start) => String(start.getDate()),
},
]}
/>左侧任务列表头会自动纵向合并并覆盖全部时间表头层级;横向滚动时,各层表头与时间网格保持同步固定。
任务的 start 和 end 可省略,用于展示尚未排期的空白行。编辑模式下在该行时间轴区域按住并水平拖拽即可创建任务条;选中后按 Delete 或 Backspace 会再次清除这两个字段。请在 onTaskChange 中同步受控数据。
依赖关系的 type 支持 finish-to-start、start-to-start、finish-to-finish,默认使用 finish-to-start。每条依赖可以通过 color 单独设置连线颜色;未配置时回退到 theme.milestoneColor。
编辑模式下先选中任务,任务条两侧会显示连接锚点。拖动左锚点到另一任务的开始端会创建 start-to-start;拖动右锚点到目标开始端会创建 finish-to-start,拖到目标完成端会创建 finish-to-finish。同一对任务重复连接时会更新原连线类型并保留其自定义颜色。
