@jaeungkim/gantt-chart 0.3.0 β 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +60 -101
- package/dist/components/GanttBar.d.ts +23 -0
- package/dist/components/GanttChartHeader.d.ts +17 -0
- package/dist/components/GanttDependencyArrows.d.ts +15 -0
- package/dist/components/GanttDragGuides.d.ts +9 -0
- package/dist/components/GanttGridSplitter.d.ts +14 -0
- package/dist/components/GanttMarkers.d.ts +18 -0
- package/dist/components/GanttTaskGrid.d.ts +42 -0
- package/dist/components/ScaleSelector.d.ts +11 -0
- package/dist/constants/gantt.d.ts +47 -0
- package/dist/core/calendar.d.ts +45 -0
- package/dist/core/criticalPath.d.ts +67 -0
- package/dist/core/dates.d.ts +32 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/scheduling.d.ts +104 -0
- package/dist/core/tree.d.ts +42 -0
- package/dist/core/types.d.ts +76 -0
- package/dist/gantt-chart.css +1 -1
- package/dist/hooks/useGanttBarDrag.d.ts +35 -0
- package/dist/hooks/useGanttDrawCreate.d.ts +31 -0
- package/dist/hooks/useGanttExportApi.d.ts +30 -0
- package/dist/hooks/useGanttHistoryApi.d.ts +28 -0
- package/dist/hooks/useGanttLinkDrag.d.ts +31 -0
- package/dist/hooks/useGanttProgressDrag.d.ts +16 -0
- package/dist/hooks/useGanttRowDrag.d.ts +33 -0
- package/dist/hooks/useGanttScrollApi.d.ts +63 -0
- package/dist/hooks/useGanttSelectors.d.ts +23 -0
- package/dist/hooks/useGanttVirtualization.d.ts +32 -0
- package/dist/hooks/useResolvedTheme.d.ts +15 -0
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -51
- package/dist/index.js +5819 -0
- package/dist/pages/Gantt.d.ts +306 -0
- package/dist/stores/context.d.ts +10 -0
- package/dist/stores/store.d.ts +118 -0
- package/dist/types/gantt.d.ts +283 -0
- package/dist/types/task.d.ts +111 -0
- package/dist/utils/a11y.d.ts +141 -0
- package/dist/utils/arrowPath.d.ts +66 -0
- package/dist/utils/dependency.d.ts +42 -0
- package/dist/utils/grouping.d.ts +81 -0
- package/dist/utils/headerUtils.d.ts +6 -0
- package/dist/utils/history.d.ts +56 -0
- package/dist/utils/i18n.d.ts +16 -0
- package/dist/utils/mutation.d.ts +47 -0
- package/dist/utils/pngExport.d.ts +67 -0
- package/dist/utils/pointerGesture.d.ts +31 -0
- package/dist/utils/rowDrag.d.ts +69 -0
- package/dist/utils/timeline.d.ts +161 -0
- package/dist/utils/transformData.d.ts +15 -0
- package/dist/utils/viewport.d.ts +95 -0
- package/package.json +47 -32
- package/dist/index.cjs.js +0 -4
- package/dist/index.es.js +0 -2357
- package/dist/readmeImg.png +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 jaeungkim
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,62 +1,49 @@
|
|
|
1
1
|
# @jaeungkim/gantt-chart
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@jaeungkim/gantt-chart)
|
|
4
|
+
[](https://github.com/jaeungkim/gantt-chart/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
A Gantt chart for React: virtualized rows, editable bars, dependency arrows, and a scheduling
|
|
8
|
+
engine that runs without a DOM.
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
**[Live demo](https://jaeungkim.com/gantt-chart)** Β· **[Documentation](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/README.md)** Β· **[νκ΅μ΄ λ¬Έμ](https://github.com/jaeungkim/gantt-chart/blob/main/docs/ko/README.md)**
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## Motivation
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
I wanted Microsoft Project's Gantt chart for my own project planning, and it wanted a
|
|
15
|
+
subscription. So I built one, borrowing ideas from MS Project, DHTMLX and Frappe Gantt.
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
There are not many open-source Gantt charts, so I hope this is useful to someone else too.
|
|
18
|
+
Feedback, feature requests and pull requests are all welcome β this is my first open-source
|
|
19
|
+
project and I would like to keep improving it with whoever shows up.
|
|
14
20
|
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
- π Multiple timeline scales: Day, Week, Month, Year
|
|
18
|
-
- π Drag-and-drop support:
|
|
19
|
-
- Move entire task bars
|
|
20
|
-
- Resize from left/right edges
|
|
21
|
-
- Snap to configured intervals
|
|
22
|
-
- π§² Smart dependency arrows (FS, SS, FF, SF)
|
|
23
|
-
- β‘ Virtualized rendering for performance
|
|
24
|
-
- π Light/Dark/System theme support
|
|
25
|
-
- π Today marker indicator
|
|
26
|
-
- π¬ Drag tooltip showing date changes
|
|
27
|
-
- π¦ Lightweight with minimal dependencies
|
|
28
|
-
|
|
29
|
-
## πΊ [Demo](https://jaeungkim.com/gantt-chart)
|
|
30
|
-
|
|
31
|
-
## π Getting Started
|
|
32
|
-
|
|
33
|
-
### Installation
|
|
21
|
+
## Install
|
|
34
22
|
|
|
35
23
|
```bash
|
|
36
|
-
|
|
37
|
-
# or
|
|
38
|
-
yarn add @jaeungkim/gantt-chart
|
|
24
|
+
pnpm add @jaeungkim/gantt-chart
|
|
39
25
|
```
|
|
40
26
|
|
|
41
|
-
|
|
27
|
+
`react` and `react-dom` (`^18` or `^19`) are peer dependencies.
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
42
30
|
|
|
43
31
|
```tsx
|
|
44
|
-
import { ReactGanttChart } from '@jaeungkim/gantt-chart';
|
|
45
|
-
import
|
|
32
|
+
import { ReactGanttChart, type Task } from '@jaeungkim/gantt-chart';
|
|
33
|
+
import '@jaeungkim/gantt-chart/style.css';
|
|
46
34
|
|
|
47
35
|
const tasks: Task[] = [
|
|
48
36
|
{
|
|
49
37
|
id: '1',
|
|
50
|
-
name: 'Project
|
|
38
|
+
name: 'Project kickoff',
|
|
51
39
|
startDate: '2024-06-01T09:00:00Z',
|
|
52
40
|
endDate: '2024-06-03T17:00:00Z',
|
|
53
41
|
parentId: null,
|
|
54
42
|
sequence: '1',
|
|
55
|
-
dependencies: [],
|
|
56
43
|
},
|
|
57
44
|
{
|
|
58
45
|
id: '2',
|
|
59
|
-
name: 'Requirements
|
|
46
|
+
name: 'Requirements',
|
|
60
47
|
startDate: '2024-06-04T09:00:00Z',
|
|
61
48
|
endDate: '2024-06-10T17:00:00Z',
|
|
62
49
|
parentId: null,
|
|
@@ -69,92 +56,64 @@ export default function App() {
|
|
|
69
56
|
return (
|
|
70
57
|
<ReactGanttChart
|
|
71
58
|
tasks={tasks}
|
|
72
|
-
height=
|
|
73
|
-
width="100%"
|
|
59
|
+
height={600}
|
|
74
60
|
theme="system"
|
|
75
61
|
defaultScale="month"
|
|
76
|
-
onTasksChange={(updated) => console.log(
|
|
62
|
+
onTasksChange={(updated) => console.log(updated)}
|
|
77
63
|
/>
|
|
78
64
|
);
|
|
79
65
|
}
|
|
80
66
|
```
|
|
81
67
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| Prop | Type | Default | Description |
|
|
85
|
-
|------|------|---------|-------------|
|
|
86
|
-
| `tasks` | `Task[]` | `[]` | Array of task objects to render |
|
|
87
|
-
| `onTasksChange` | `(tasks: Task[]) => void` | - | Callback when tasks are moved or resized |
|
|
88
|
-
| `height` | `number \| string` | `600` | Chart height (px or CSS value) |
|
|
89
|
-
| `width` | `number \| string` | `"100%"` | Chart width (px or CSS value) |
|
|
90
|
-
| `theme` | `"light" \| "dark" \| "system"` | - | Theme mode |
|
|
91
|
-
| `defaultScale` | `"day" \| "week" \| "month" \| "year"` | `"month"` | Initial timeline scale |
|
|
92
|
-
| `className` | `string` | - | Additional CSS class for the container |
|
|
93
|
-
|
|
94
|
-
## Task Format
|
|
95
|
-
|
|
96
|
-
All dates must be in **UTC ISO string format**: `"2024-06-01T09:00:00Z"`
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
interface Task {
|
|
100
|
-
id: string;
|
|
101
|
-
name: string;
|
|
102
|
-
startDate: string; // UTC ISO string
|
|
103
|
-
endDate: string; // UTC ISO string
|
|
104
|
-
parentId: string | null;
|
|
105
|
-
sequence: string;
|
|
106
|
-
dependencies?: TaskDependency[];
|
|
107
|
-
}
|
|
68
|
+
The chart never stores your data. Every committed gesture ends in one `onTasksChange` call with a
|
|
69
|
+
new array, and it is up to you to keep it.
|
|
108
70
|
|
|
109
|
-
|
|
110
|
-
targetId: string;
|
|
111
|
-
type: DependencyType;
|
|
112
|
-
}
|
|
71
|
+
Full walkthrough: **[Quick start](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/quick-start.md)**.
|
|
113
72
|
|
|
114
|
-
|
|
115
|
-
// FS = Finish-to-Start
|
|
116
|
-
// SS = Start-to-Start
|
|
117
|
-
// FF = Finish-to-Finish
|
|
118
|
-
// SF = Start-to-Finish
|
|
119
|
-
```
|
|
73
|
+
## What it does
|
|
120
74
|
|
|
121
|
-
|
|
75
|
+
**Rendering** β virtualized rows, a configurable task list pane with a draggable splitter, six
|
|
76
|
+
timeline scales from hour to year, elbow-routed dependency arrows, date markers, range bands,
|
|
77
|
+
non-working-day shading and baseline bars.
|
|
122
78
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
| `day` | Day | Hour | 1 hour |
|
|
126
|
-
| `week` | Week | Day | 6 hours |
|
|
127
|
-
| `month` | Month | Day | 1 day |
|
|
128
|
-
| `year` | Year | Month | 7 days |
|
|
79
|
+
**Editing** β move, resize and set progress by dragging; draw a new task on empty row space; draw
|
|
80
|
+
and delete dependency arrows; reorder, indent and outdent rows; undo and redo.
|
|
129
81
|
|
|
130
|
-
|
|
82
|
+
**Scheduling** β successors reschedule on a drag under a policy you choose, a working-day calendar
|
|
83
|
+
makes weekends and holidays stop counting, and the critical path comes with total and free slack.
|
|
84
|
+
The same functions are exported as plain functions you can call on a server or in a worker.
|
|
131
85
|
|
|
132
|
-
|
|
86
|
+
**Accessibility** β one ARIA treegrid with a roving tab stop; bars move, resize and step their
|
|
87
|
+
progress from the keyboard.
|
|
133
88
|
|
|
134
|
-
|
|
89
|
+
**Theming** β `--gantt-*` custom properties scoped to the chart container, a light / dark / system
|
|
90
|
+
theme prop, per-task colors, and four render props for replacing elements outright.
|
|
135
91
|
|
|
136
|
-
|
|
137
|
-
-
|
|
138
|
-
- **`system`** - Follows system preference (uses `prefers-color-scheme`)
|
|
92
|
+
Each of these, and the honest list of what the library does **not** do, is in
|
|
93
|
+
**[Introduction](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/introduction.md)**.
|
|
139
94
|
|
|
140
|
-
|
|
141
|
-
<ReactGanttChart theme="dark" ... />
|
|
142
|
-
```
|
|
95
|
+
## Documentation
|
|
143
96
|
|
|
144
|
-
|
|
97
|
+
| Page | |
|
|
98
|
+
|---|---|
|
|
99
|
+
| [Quick start](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/quick-start.md) | install to a working, editable chart |
|
|
100
|
+
| [Task data](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/task-data.md) | the `Task` shape and how the `tasks` prop is compared |
|
|
101
|
+
| [Editing tasks](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/editing.md) | gestures, permissions, touch |
|
|
102
|
+
| [Dependencies](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/dependencies.md) | the four link types and lag |
|
|
103
|
+
| [Scheduling](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/scheduling.md) | policies, working calendar, critical path, baselines |
|
|
104
|
+
| [Keyboard and screen readers](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/accessibility.md) | the key map, the ARIA tree, and the gaps |
|
|
105
|
+
| [Theming](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/theming.md) | the CSS custom properties |
|
|
106
|
+
| [Props](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/ref/props.md) | every prop, in one table |
|
|
145
107
|
|
|
146
|
-
|
|
147
|
-
- [ ] Right sidebar for task details
|
|
148
|
-
- [ ] Collapsible parent-child rows
|
|
149
|
-
- [ ] Inline editing for task names
|
|
150
|
-
- [ ] Export to PNG/SVG
|
|
151
|
-
- [ ] Custom bar colors
|
|
108
|
+
All 35 pages: **[English](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/README.md)** Β· **[νκ΅μ΄](https://github.com/jaeungkim/gantt-chart/blob/main/docs/ko/README.md)**
|
|
152
109
|
|
|
153
|
-
##
|
|
110
|
+
## Contributing
|
|
154
111
|
|
|
155
|
-
|
|
156
|
-
|
|
112
|
+
Setup, the checks CI runs, and the branch and PR conventions are in
|
|
113
|
+
[CONTRIBUTING.md](CONTRIBUTING.md). Questions and ideas go in
|
|
114
|
+
[Discussions](https://github.com/jaeungkim/gantt-chart/discussions); issues are for bugs and
|
|
115
|
+
concrete feature requests.
|
|
157
116
|
|
|
158
|
-
##
|
|
117
|
+
## License
|
|
159
118
|
|
|
160
|
-
MIT
|
|
119
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { GanttDependencyChange } from '../hooks/useGanttLinkDrag';
|
|
2
|
+
import { GanttBarOptions, GanttScheduling } from '../types/gantt';
|
|
3
|
+
import { GanttInteractionConfig, TaskTransformed } from '../types/task';
|
|
4
|
+
interface GanttBarProps {
|
|
5
|
+
currentTask: TaskTransformed;
|
|
6
|
+
options?: GanttBarOptions;
|
|
7
|
+
interaction?: GanttInteractionConfig;
|
|
8
|
+
/**
|
|
9
|
+
* Roving tabindex of the treegrid (default -1)
|
|
10
|
+
*
|
|
11
|
+
* Exactly one cell in the chart carries 0, so Tab enters and leaves the whole
|
|
12
|
+
* grid once and the arrow keys move within it.
|
|
13
|
+
*/
|
|
14
|
+
tabIndex?: number;
|
|
15
|
+
/** `row:column` coordinate the chart's focus manager looks the cell up by */
|
|
16
|
+
cellCoord?: string;
|
|
17
|
+
scheduling?: GanttScheduling;
|
|
18
|
+
/** Scroll the timeline when the drag reaches a viewport edge (default true) */
|
|
19
|
+
autoScrollOnDrag?: boolean;
|
|
20
|
+
onDependencyCreate?: (change: GanttDependencyChange) => boolean | void;
|
|
21
|
+
}
|
|
22
|
+
export default function GanttBar({ currentTask, options, interaction, tabIndex, cellCoord, scheduling, autoScrollOnDrag, onDependencyCreate, }: GanttBarProps): import("react").JSX.Element;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { GanttBottomRowCell, GanttHeaderCellRenderer, GanttScaleKey } from '../types/gantt';
|
|
3
|
+
interface GanttChartHeaderProps {
|
|
4
|
+
bottomRowCells: GanttBottomRowCell[];
|
|
5
|
+
selectedScale: GanttScaleKey;
|
|
6
|
+
width: number;
|
|
7
|
+
/** Scroll container used to virtualize the bottom time cells (pinning the top group labels is done with CSS sticky) */
|
|
8
|
+
scrollRef: React.RefObject<HTMLDivElement | null>;
|
|
9
|
+
/** Replaces a header cell wholesale - both rows go through it */
|
|
10
|
+
renderHeaderCell?: GanttHeaderCellRenderer;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Gantt chart header component
|
|
14
|
+
* Renders the top group labels and the bottom time cells
|
|
15
|
+
*/
|
|
16
|
+
declare function GanttChartHeader({ bottomRowCells, selectedScale, width, scrollRef, renderHeaderCell, }: GanttChartHeaderProps): React.JSX.Element;
|
|
17
|
+
export default GanttChartHeader;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { GanttDependencyChange } from '../hooks/useGanttLinkDrag';
|
|
2
|
+
import { GanttInteractionConfig, Task, TaskTransformed } from '../types/task';
|
|
3
|
+
interface Props {
|
|
4
|
+
transformedTasks: TaskTransformed[];
|
|
5
|
+
/** Row count of the chart - a lane row can carry several tasks, so it is not the task count */
|
|
6
|
+
rowCount: number;
|
|
7
|
+
/** Link keys on the critical path (from computeCriticalPath) - undefined when it is off */
|
|
8
|
+
criticalLinkIds?: Set<string>;
|
|
9
|
+
interaction?: GanttInteractionConfig;
|
|
10
|
+
onTasksChange?: (updatedTasks: Task[]) => void;
|
|
11
|
+
/** Returning false keeps the dependency */
|
|
12
|
+
onDependencyDelete?: (change: GanttDependencyChange) => boolean | void;
|
|
13
|
+
}
|
|
14
|
+
export default function GanttDependencyArrows({ transformedTasks, rowCount, criticalLinkIds, interaction, onTasksChange, onDependencyDelete, }: Props): import("react").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface GanttDragGuidesProps {
|
|
2
|
+
width: number;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Shows the start/end points during a drag as vertical guides running up through the header
|
|
6
|
+
* Each guide carries the current date label at its top, so the moment can be read straight off the header
|
|
7
|
+
*/
|
|
8
|
+
export default function GanttDragGuides({ width }: GanttDragGuidesProps): import("react").JSX.Element | null;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface GanttGridSplitterProps {
|
|
2
|
+
width: number;
|
|
3
|
+
onWidthChange: (width: number) => void;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Drag handle between the task list and the timeline
|
|
7
|
+
*
|
|
8
|
+
* Rendered outside the scroll container rather than inside the grid pane: the
|
|
9
|
+
* pane sits inside the treegrid, whose only children may be rows, and a
|
|
10
|
+
* separator there would also be a second tab stop in a widget that is meant to
|
|
11
|
+
* have exactly one.
|
|
12
|
+
*/
|
|
13
|
+
export default function GanttGridSplitter({ width, onWidthChange, }: GanttGridSplitterProps): import("react").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PositionedBand, PositionedMarker } from '../utils/timeline';
|
|
2
|
+
/**
|
|
3
|
+
* Shaded date-range bands (sprints, phases)
|
|
4
|
+
*
|
|
5
|
+
* Drawn on the background layer, below the rows, arrows and bars.
|
|
6
|
+
*/
|
|
7
|
+
export declare function GanttRangeBands({ bands }: {
|
|
8
|
+
bands: PositionedBand[];
|
|
9
|
+
}): import("react").JSX.Element | null;
|
|
10
|
+
/**
|
|
11
|
+
* Vertical date markers (deadlines, releases, and the chart's own today line)
|
|
12
|
+
*
|
|
13
|
+
* `data-warning` is set when a task the marker watches ends past its date, so the warning
|
|
14
|
+
* look is a plain CSS attribute selector the host can restyle.
|
|
15
|
+
*/
|
|
16
|
+
export declare function GanttMarkers({ markers }: {
|
|
17
|
+
markers: PositionedMarker[];
|
|
18
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { VirtualItem } from '@tanstack/react-virtual';
|
|
2
|
+
import { GanttColumn, GanttReorderChange } from '../types/gantt';
|
|
3
|
+
import { GanttInteractionConfig, Task, TaskTransformed } from '../types/task';
|
|
4
|
+
import { GanttFocus } from '../utils/a11y';
|
|
5
|
+
import { GanttRow } from '../utils/grouping';
|
|
6
|
+
interface GanttTaskGridProps {
|
|
7
|
+
/** The rows on screen (collapsed subtrees and groups are already filtered out) */
|
|
8
|
+
rows: GanttRow[];
|
|
9
|
+
columns: GanttColumn[];
|
|
10
|
+
/** The timeline's own virtualization result - leaves no room for the rows to drift apart */
|
|
11
|
+
virtualItems: VirtualItem[];
|
|
12
|
+
totalHeight: number;
|
|
13
|
+
width: number;
|
|
14
|
+
hierarchy: boolean;
|
|
15
|
+
collapsedIds: Set<string>;
|
|
16
|
+
onToggleCollapse: (rowId: string) => void;
|
|
17
|
+
/** Which cell currently holds the chart's single tab stop */
|
|
18
|
+
focus: GanttFocus;
|
|
19
|
+
/** Whether rows can be dragged to reorder and re-parent */
|
|
20
|
+
allowRowReorder: boolean;
|
|
21
|
+
/** The same guards the bars use - a row is draggable only where the task can move */
|
|
22
|
+
interaction: GanttInteractionConfig;
|
|
23
|
+
onReorder?: (change: GanttReorderChange) => void | boolean;
|
|
24
|
+
onTasksChange?: (updatedTasks: Task[]) => void;
|
|
25
|
+
/** The selected row, highlighted in step with its bar */
|
|
26
|
+
selectedTaskId?: string | null;
|
|
27
|
+
onRowClick?: (task: TaskTransformed, event: React.MouseEvent) => void;
|
|
28
|
+
onRowDoubleClick?: (task: TaskTransformed, event: React.MouseEvent) => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The task grid on the left
|
|
32
|
+
*
|
|
33
|
+
* A sticky column inside the timeline's own scroll container, so vertical scrolling is
|
|
34
|
+
* locked to the rows by construction.
|
|
35
|
+
* (Syncing two panes through scroll events drifts the moment virtualization kicks in)
|
|
36
|
+
*
|
|
37
|
+
* These rows are the treegrid's `row` elements: each one owns its bars in the
|
|
38
|
+
* timeline through `aria-owns`, which is what makes the two panes read as a
|
|
39
|
+
* single widget rather than two unrelated lists.
|
|
40
|
+
*/
|
|
41
|
+
export default function GanttTaskGrid({ rows, columns, virtualItems, totalHeight, width, hierarchy, collapsedIds, onToggleCollapse, focus, allowRowReorder, interaction, onReorder, onTasksChange, selectedTaskId, onRowClick, onRowDoubleClick, }: GanttTaskGridProps): import("react").JSX.Element;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GanttScaleKey } from '../types/gantt';
|
|
2
|
+
interface ScaleSelectorProps {
|
|
3
|
+
selectedScale: GanttScaleKey;
|
|
4
|
+
onScaleChange: (scale: GanttScaleKey) => void;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Segmented control for choosing the Gantt chart scale
|
|
8
|
+
* Uses a pill-style button group instead of a dropdown
|
|
9
|
+
*/
|
|
10
|
+
export default function ScaleSelector({ selectedScale, onScaleChange, }: ScaleSelectorProps): import("react").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { GanttColumn, GanttScaleConfig, GanttScaleKey } from '../types/gantt';
|
|
2
|
+
export declare const NODE_HEIGHT = 38;
|
|
3
|
+
/** Full timeline header height (44 top group + 28 bottom cell + 1 border) - the grid header matches it */
|
|
4
|
+
export declare const HEADER_HEIGHT = 73;
|
|
5
|
+
/** Default grid column width (px) */
|
|
6
|
+
export declare const DEFAULT_COLUMN_WIDTH = 120;
|
|
7
|
+
/** Grid pane width limits (px) */
|
|
8
|
+
export declare const MIN_GRID_WIDTH = 120;
|
|
9
|
+
export declare const MAX_GRID_WIDTH = 800;
|
|
10
|
+
/** Indentation per tree level (px) */
|
|
11
|
+
export declare const TREE_INDENT = 16;
|
|
12
|
+
/**
|
|
13
|
+
* Default task grid columns
|
|
14
|
+
* Replaceable wholesale through the columns prop, so no header label is baked into the library.
|
|
15
|
+
*/
|
|
16
|
+
export declare const DEFAULT_COLUMNS: GanttColumn[];
|
|
17
|
+
export declare const TIMELINE_SHIFT_BUFFER = 5;
|
|
18
|
+
/** Bars render at least this wide, however narrow they are (px) - keeps short tasks grabbable */
|
|
19
|
+
export declare const MIN_BAR_WIDTH = 14;
|
|
20
|
+
/** Below this width the task name is shown outside the bar (px) */
|
|
21
|
+
export declare const MIN_LABEL_INSIDE_WIDTH = 56;
|
|
22
|
+
/** Size of the resize edge hit area (px) */
|
|
23
|
+
export declare const EDGE_THRESHOLD = 8;
|
|
24
|
+
/**
|
|
25
|
+
* Size of the resize edge hit area for touch and pen (px)
|
|
26
|
+
*
|
|
27
|
+
* The 44px accessibility guideline, applied to the axis the bar can grow along. A bar
|
|
28
|
+
* is half a row tall, so the target cannot also be 44px high without covering the row
|
|
29
|
+
* next to it - what keeps a mistimed tap harmless instead is the long press: a touch
|
|
30
|
+
* has to rest on the edge before anything is lifted.
|
|
31
|
+
*/
|
|
32
|
+
export declare const TOUCH_EDGE_THRESHOLD = 44;
|
|
33
|
+
/** Below this bar width there is no edge resizing and the whole bar is the move handle (px) */
|
|
34
|
+
export declare const MIN_RESIZABLE_WIDTH: number;
|
|
35
|
+
/** Same rule for touch - below this a bar is move-only, so the edges cannot swallow it (px) */
|
|
36
|
+
export declare const MIN_TOUCH_RESIZABLE_WIDTH: number;
|
|
37
|
+
/** Side length of the milestone diamond (px, before the 45-degree rotation) */
|
|
38
|
+
export declare const MILESTONE_SIZE = 16;
|
|
39
|
+
/** Horizontal distance from the diamond's center to its vertex (px) */
|
|
40
|
+
export declare const MILESTONE_HALF_DIAGONAL: number;
|
|
41
|
+
/**
|
|
42
|
+
* Date display format per scale (shared by the tooltip and the drag guides) - year included, 24-hour clock
|
|
43
|
+
* The chart is drawn in UTC, so the zone is spelled out only on the day scale, where the time is visible
|
|
44
|
+
*/
|
|
45
|
+
export declare const DATE_FORMATS: Record<GanttScaleKey, string>;
|
|
46
|
+
/** Declaration order is the order the scale selector lists the scales in - finest first */
|
|
47
|
+
export declare const GANTT_SCALE_CONFIG: Record<GanttScaleKey, GanttScaleConfig>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Dayjs } from 'dayjs';
|
|
2
|
+
/**
|
|
3
|
+
* The calendar every piece of date arithmetic in the core routes through.
|
|
4
|
+
*
|
|
5
|
+
* There is exactly one notion of "a day" in the engine: whatever this object says.
|
|
6
|
+
* The default calendar counts every day, so the plain calendar-date behaviour is not a
|
|
7
|
+
* separate code path - it is the same code with a calendar that never skips anything.
|
|
8
|
+
* Turning the working-day calendar on swaps the object; nothing else changes.
|
|
9
|
+
*/
|
|
10
|
+
export interface WorkingCalendar {
|
|
11
|
+
/** False for the default calendar (every day counts) - lets callers take the cheap path */
|
|
12
|
+
readonly skipsNonWorkingDays: boolean;
|
|
13
|
+
isWorkingDay(date: Dayjs): boolean;
|
|
14
|
+
/** Moves `days` days forward (or backward), skipping non-working days */
|
|
15
|
+
addDays(date: Dayjs, days: number): Dayjs;
|
|
16
|
+
/** Days from `from` to `to`, counted the same way `addDays` moves. Signed. */
|
|
17
|
+
daysBetween(from: Dayjs, to: Dayjs): number;
|
|
18
|
+
/** Smallest d where `addDays(from, d) >= target` - how far a task must move to clear a date */
|
|
19
|
+
daysUntil(from: Dayjs, target: Dayjs): number;
|
|
20
|
+
/** Largest d where `addDays(from, d) <= target` - how far a task may slip before it breaks one */
|
|
21
|
+
daysUpTo(from: Dayjs, target: Dayjs): number;
|
|
22
|
+
/** The date itself when it is a working day, otherwise the next one (time of day kept) */
|
|
23
|
+
snapForward(date: Dayjs): Dayjs;
|
|
24
|
+
}
|
|
25
|
+
export interface WorkingCalendarOptions {
|
|
26
|
+
/** Working weekdays as UTC day numbers, 0 = Sunday (default Mon-Fri) */
|
|
27
|
+
workingWeekdays?: number[];
|
|
28
|
+
/** Non-working dates as UTC `YYYY-MM-DD` strings */
|
|
29
|
+
holidays?: string[];
|
|
30
|
+
/** Replaces the weekday + holiday check entirely */
|
|
31
|
+
isNonWorkingDay?: (date: Dayjs) => boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The default calendar: every day counts.
|
|
35
|
+
* Date arithmetic through it is plain calendar arithmetic, so it is what keeps the
|
|
36
|
+
* scheduling features behaviour-neutral until a host opts into working days.
|
|
37
|
+
*/
|
|
38
|
+
export declare const CALENDAR_DAYS: WorkingCalendar;
|
|
39
|
+
/**
|
|
40
|
+
* A calendar that skips weekends and holidays.
|
|
41
|
+
*
|
|
42
|
+
* Takes the same configuration the chart already uses to shade non-working days
|
|
43
|
+
* (`holidays` / `isNonWorkingDay`), so what is shaded and what is skipped cannot drift.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createWorkingCalendar(options?: WorkingCalendarOptions): WorkingCalendar;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Dayjs } from 'dayjs';
|
|
2
|
+
import { WorkingCalendar } from './calendar';
|
|
3
|
+
import { TaskGraph } from './scheduling';
|
|
4
|
+
import { Task } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Critical path method over the dependency graph.
|
|
7
|
+
*
|
|
8
|
+
* Every task carries real dates, so its own start acts as a "no earlier than" constraint:
|
|
9
|
+
* the forward pass moves a task later when a predecessor demands it and never earlier, and
|
|
10
|
+
* the backward pass works out how much later each task could still finish without pushing
|
|
11
|
+
* the project's end out. Zero difference between the two is zero slack - the critical path.
|
|
12
|
+
*
|
|
13
|
+
* Everything is measured as a whole-day shift of the task's own dates, so times of day
|
|
14
|
+
* survive untouched and the numbers are working days whenever the calendar says so.
|
|
15
|
+
*/
|
|
16
|
+
export interface EarlyDates {
|
|
17
|
+
start: Dayjs;
|
|
18
|
+
finish: Dayjs;
|
|
19
|
+
/** Days later than the task's own dates */
|
|
20
|
+
shift: number;
|
|
21
|
+
}
|
|
22
|
+
export interface LateDates {
|
|
23
|
+
start: Dayjs;
|
|
24
|
+
finish: Dayjs;
|
|
25
|
+
/** Days later than the task's own dates the task could still run */
|
|
26
|
+
shift: number;
|
|
27
|
+
}
|
|
28
|
+
export interface TaskScheduleMetrics {
|
|
29
|
+
earlyStart: string;
|
|
30
|
+
earlyFinish: string;
|
|
31
|
+
lateStart: string;
|
|
32
|
+
lateFinish: string;
|
|
33
|
+
/** Days a task can slip before the project's finish moves */
|
|
34
|
+
totalSlack: number;
|
|
35
|
+
/** Days a task can slip before any successor's early start moves */
|
|
36
|
+
freeSlack: number;
|
|
37
|
+
critical: boolean;
|
|
38
|
+
/** Calendar days, or working days when the working-day calendar is on */
|
|
39
|
+
duration: number;
|
|
40
|
+
}
|
|
41
|
+
export interface CriticalPathResult {
|
|
42
|
+
metrics: Map<string, TaskScheduleMetrics>;
|
|
43
|
+
criticalTaskIds: Set<string>;
|
|
44
|
+
/** Keys from `linkKey` for the links that lie along the critical path */
|
|
45
|
+
criticalLinkIds: Set<string>;
|
|
46
|
+
/** Ids caught in a dependency cycle - they get no metrics */
|
|
47
|
+
cycle: string[] | null;
|
|
48
|
+
/** The project's earliest finish (UTC ISO string), or null with no tasks */
|
|
49
|
+
projectFinish: string | null;
|
|
50
|
+
}
|
|
51
|
+
export interface CriticalPathOptions {
|
|
52
|
+
calendar?: WorkingCalendar;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Earliest each task can run given its predecessors, walked predecessors-first.
|
|
56
|
+
* A task with no predecessor stays on its own dates.
|
|
57
|
+
*/
|
|
58
|
+
export declare function forwardPass(tasks: Task[], calendar?: WorkingCalendar, graph?: TaskGraph): Map<string, EarlyDates>;
|
|
59
|
+
/**
|
|
60
|
+
* Latest each task can run without moving the project's finish, walked successors-first.
|
|
61
|
+
*
|
|
62
|
+
* Takes the forward pass's output rather than recomputing it, so it can be exercised on
|
|
63
|
+
* its own with hand-written early dates.
|
|
64
|
+
*/
|
|
65
|
+
export declare function backwardPass(tasks: Task[], early: Map<string, EarlyDates>, calendar?: WorkingCalendar, graph?: TaskGraph, projectFinish?: Dayjs): Map<string, LateDates>;
|
|
66
|
+
/** Forward pass, backward pass, and the slack numbers that fall out of the two */
|
|
67
|
+
export declare function computeCriticalPath(tasks: Task[], options?: CriticalPathOptions): CriticalPathResult;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { default as dayjs, Dayjs } from 'dayjs';
|
|
2
|
+
/**
|
|
3
|
+
* The chart's own dayjs - always parses and displays in UTC mode.
|
|
4
|
+
*
|
|
5
|
+
* The contract for task dates is "UTC ISO string" (README > Task Format), so
|
|
6
|
+
* positioning and labels follow UTC too. Parsing in local mode would draw the same
|
|
7
|
+
* data in different date cells depending on where the viewer is (#84), and local
|
|
8
|
+
* calendar DST days (23/25 hours) would make cell widths wobble as well (#28).
|
|
9
|
+
*
|
|
10
|
+
* - A string carrying a zone ('...Z', '+09:00') displays that instant as a UTC time
|
|
11
|
+
* - A string without a zone ('2025-06-01', '2025-06-01T09:00') is read as a UTC wall
|
|
12
|
+
* clock, so it displays exactly as written, whatever the viewer's time zone
|
|
13
|
+
*/
|
|
14
|
+
declare const ganttDayjs: typeof dayjs.utc;
|
|
15
|
+
export default ganttDayjs;
|
|
16
|
+
/**
|
|
17
|
+
* Calendar quarter of the date, 1-4
|
|
18
|
+
*
|
|
19
|
+
* Written out instead of pulling in dayjs' quarterOfYear plugin - the quarter scale
|
|
20
|
+
* needs exactly these two lines of it, and the plugin would grow the bundle for every
|
|
21
|
+
* consumer, quarter scale in use or not.
|
|
22
|
+
*/
|
|
23
|
+
export declare function quarterOfYear(date: Dayjs): number;
|
|
24
|
+
/** First moment of the calendar quarter the date falls in */
|
|
25
|
+
export declare function startOfQuarter(date: Dayjs): Dayjs;
|
|
26
|
+
/**
|
|
27
|
+
* First moment of the week the date falls in
|
|
28
|
+
*
|
|
29
|
+
* `firstDayOfWeek` is 0 = Sunday .. 6 = Saturday. dayjs' own `startOf('week')` is fixed
|
|
30
|
+
* to the locale dayjs was loaded with (Sunday), so the week boundary is computed here.
|
|
31
|
+
*/
|
|
32
|
+
export declare function startOfWeek(date: Dayjs, firstDayOfWeek?: number): Dayjs;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The headless scheduling core.
|
|
3
|
+
*
|
|
4
|
+
* Everything under `src/core/` is plain data and pure functions - no React, no DOM, no
|
|
5
|
+
* pixels - so it runs in Node, is unit-testable on its own, and could be published as a
|
|
6
|
+
* separate entry point without dragging the renderer along. The boundary is enforced by an
|
|
7
|
+
* eslint rule in `eslint.config.js`; keep anything that touches refs, elements or bar
|
|
8
|
+
* geometry in `src/utils/` instead.
|
|
9
|
+
*/
|
|
10
|
+
export { default as dayjs } from './dates';
|
|
11
|
+
export type { DependencyType, Task, TaskDependency, TaskType, } from './types';
|
|
12
|
+
export { isMilestoneTask, normalizeProgress } from './types';
|
|
13
|
+
export { CALENDAR_DAYS, createWorkingCalendar, type WorkingCalendar, type WorkingCalendarOptions, } from './calendar';
|
|
14
|
+
export { buildTaskGraph, canLink, findPath, linkDelta, linkKey, scheduleTasks, shiftTask, taskEnd, taskStart, type ScheduleOptions, type ScheduleResult, type SchedulingLink, type SchedulingPolicy, type TaskGraph, } from './scheduling';
|
|
15
|
+
export { backwardPass, computeCriticalPath, forwardPass, type CriticalPathOptions, type CriticalPathResult, type EarlyDates, type LateDates, type TaskScheduleMetrics, } from './criticalPath';
|
|
16
|
+
export { buildTaskTree, collectSubtreeIds, getVisibleTasks, rollUpTasks, type TaskTree, } from './tree';
|