@jaeungkim/gantt-chart 0.4.0 → 1.1.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/README.md +60 -64
- package/dist/Gantt.d.ts +5 -0
- package/dist/bars/components/GanttBar.d.ts +20 -0
- package/dist/bars/components/GanttBarsLayer.d.ts +24 -0
- package/dist/bars/components/GanttDragGuides.d.ts +1 -0
- package/dist/bars/hooks/useGanttBarDrag.d.ts +16 -0
- package/dist/bars/hooks/useGanttDrawCreate.d.ts +25 -0
- package/dist/{hooks → bars/hooks}/useGanttProgressDrag.d.ts +3 -8
- package/dist/core/calendar.d.ts +4 -22
- package/dist/core/dates.d.ts +5 -21
- package/dist/core/index.d.ts +1 -14
- package/dist/core/reorder.d.ts +55 -0
- package/dist/core/tree.d.ts +4 -12
- package/dist/core/types.d.ts +4 -37
- package/dist/{components → dependencies/components}/GanttDependencyArrows.d.ts +4 -6
- package/dist/{hooks → dependencies/hooks}/useGanttLinkDrag.d.ts +2 -13
- package/dist/dependencies/utils/arrowPath.d.ts +20 -0
- package/dist/dependencies/utils/link.d.ts +17 -0
- package/dist/detail/components/GanttDetailPanel.d.ts +21 -0
- package/dist/detail/hooks/useGanttDetail.d.ts +42 -0
- package/dist/detail/utils/edit.d.ts +21 -0
- package/dist/gantt-chart.css +1 -1
- package/dist/index.cjs +1 -4
- package/dist/index.d.cts +11 -13
- package/dist/index.d.ts +11 -13
- package/dist/index.js +4071 -5141
- package/dist/interaction/hooks/useGanttInteraction.d.ts +2 -0
- package/dist/interaction/hooks/useGanttKeyboardNav.d.ts +33 -0
- package/dist/interaction/hooks/useGanttSelection.d.ts +12 -0
- package/dist/interaction/utils/a11y.d.ts +84 -0
- package/dist/props.d.ts +121 -0
- package/dist/rows/components/GanttRowsLayer.d.ts +13 -0
- package/dist/rows/hooks/useGanttCollapse.d.ts +18 -0
- package/dist/rows/hooks/useGanttRowModel.d.ts +19 -0
- package/dist/rows/utils/rows.d.ts +21 -0
- package/dist/shared/constants.d.ts +24 -0
- package/dist/{stores → shared}/context.d.ts +1 -7
- package/dist/shared/hooks/useLatestRef.d.ts +2 -0
- package/dist/shared/hooks/useResolvedTheme.d.ts +8 -0
- package/dist/shared/store.d.ts +81 -0
- package/dist/shared/task.d.ts +55 -0
- package/dist/shared/types.d.ts +115 -0
- package/dist/shared/utils/holidays.d.ts +12 -0
- package/dist/shared/utils/i18n.d.ts +5 -0
- package/dist/shared/utils/pointerGesture.d.ts +11 -0
- package/dist/shared/virtual/axis.d.ts +13 -0
- package/dist/shared/virtual/useVirtualWindow.d.ts +16 -0
- package/dist/shared/virtual/window.d.ts +30 -0
- package/dist/task-list/components/GanttGridSplitter.d.ts +6 -0
- package/dist/task-list/components/GanttTaskAddRow.d.ts +7 -0
- package/dist/task-list/components/GanttTaskGrid.d.ts +24 -0
- package/dist/task-list/hooks/useGanttRowDrag.d.ts +17 -0
- package/dist/task-list/hooks/useGanttTaskListPane.d.ts +11 -0
- package/dist/task-list/hooks/useGanttTaskMove.d.ts +16 -0
- package/dist/timeline/components/GanttChartHeader.d.ts +10 -0
- package/dist/timeline/components/GanttHolidayLabels.d.ts +6 -0
- package/dist/timeline/components/GanttNonWorkingLayer.d.ts +6 -0
- package/dist/timeline/components/GanttTodayLine.d.ts +6 -0
- package/dist/{hooks → timeline/hooks}/useGanttScrollApi.d.ts +22 -23
- package/dist/timeline/hooks/useGanttViewport.d.ts +47 -0
- package/dist/timeline/hooks/useGanttVirtualization.d.ts +28 -0
- package/dist/timeline/utils/geometry.d.ts +52 -0
- package/dist/timeline/utils/header.d.ts +8 -0
- package/dist/timeline/utils/transform.d.ts +4 -0
- package/dist/timeline/utils/viewport.d.ts +31 -0
- package/dist/useGanttSelectors.d.ts +11 -0
- package/package.json +12 -12
- package/dist/components/GanttBar.d.ts +0 -23
- package/dist/components/GanttChartHeader.d.ts +0 -17
- package/dist/components/GanttDragGuides.d.ts +0 -9
- package/dist/components/GanttGridSplitter.d.ts +0 -14
- package/dist/components/GanttMarkers.d.ts +0 -18
- package/dist/components/GanttTaskGrid.d.ts +0 -42
- package/dist/components/ScaleSelector.d.ts +0 -11
- package/dist/constants/gantt.d.ts +0 -47
- package/dist/core/criticalPath.d.ts +0 -67
- package/dist/core/scheduling.d.ts +0 -104
- package/dist/hooks/useGanttBarDrag.d.ts +0 -35
- package/dist/hooks/useGanttDrawCreate.d.ts +0 -31
- package/dist/hooks/useGanttExportApi.d.ts +0 -30
- package/dist/hooks/useGanttHistoryApi.d.ts +0 -28
- package/dist/hooks/useGanttRowDrag.d.ts +0 -33
- package/dist/hooks/useGanttSelectors.d.ts +0 -23
- package/dist/hooks/useGanttVirtualization.d.ts +0 -32
- package/dist/hooks/useResolvedTheme.d.ts +0 -15
- package/dist/pages/Gantt.d.ts +0 -306
- package/dist/stores/store.d.ts +0 -118
- package/dist/types/gantt.d.ts +0 -283
- package/dist/types/task.d.ts +0 -111
- package/dist/utils/a11y.d.ts +0 -141
- package/dist/utils/arrowPath.d.ts +0 -66
- package/dist/utils/dependency.d.ts +0 -42
- package/dist/utils/grouping.d.ts +0 -81
- package/dist/utils/headerUtils.d.ts +0 -6
- package/dist/utils/history.d.ts +0 -56
- package/dist/utils/i18n.d.ts +0 -16
- package/dist/utils/mutation.d.ts +0 -47
- package/dist/utils/pngExport.d.ts +0 -67
- package/dist/utils/pointerGesture.d.ts +0 -31
- package/dist/utils/rowDrag.d.ts +0 -69
- package/dist/utils/timeline.d.ts +0 -161
- package/dist/utils/transformData.d.ts +0 -15
- package/dist/utils/viewport.d.ts +0 -95
package/README.md
CHANGED
|
@@ -1,117 +1,113 @@
|
|
|
1
|
+
[](https://gantt.jaeungkim.com)
|
|
2
|
+
|
|
1
3
|
# @jaeungkim/gantt-chart
|
|
2
4
|
|
|
3
5
|
[](https://www.npmjs.com/package/@jaeungkim/gantt-chart)
|
|
4
6
|
[](https://github.com/jaeungkim/gantt-chart/actions/workflows/ci.yml)
|
|
5
7
|
[](LICENSE)
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
engine that runs without a DOM.
|
|
9
|
-
|
|
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)**
|
|
9
|
+
An editable Gantt chart for React.
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
[Quick start](https://gantt.jaeungkim.com/docs/quick-start) |
|
|
12
|
+
[Playground](https://gantt.jaeungkim.com/playground) |
|
|
13
|
+
[Documentation](https://gantt.jaeungkim.com/docs) |
|
|
14
|
+
[한국어 문서](https://gantt.jaeungkim.com/ko/docs)
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
subscription. So I built one, borrowing ideas from MS Project, DHTMLX and Frappe Gantt.
|
|
16
|
+
## Features
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
- Bar drags that move a task, resize it from either edge and set its progress.
|
|
19
|
+
- Dependency arrows for `FS`, `SS`, `FF` and `SF`, drawn between bars and removed with Delete or Backspace.
|
|
20
|
+
- A task list pane with the `parentId` tree, summary roll-up and row reordering.
|
|
21
|
+
- A docked detail panel that narrows the timeline and edits the task in place.
|
|
22
|
+
- Five scales from `day` to `year`, rows and time cells virtualized together, and tasks packed onto shared rows by `lane`.
|
|
23
|
+
- A working calendar from `workingWeekdays` and `holidays`, which also shades the timeline.
|
|
24
|
+
- Keyboard editing and screen reader announcements on one ARIA treegrid.
|
|
25
|
+
- `locale` date labels through `Intl.DateTimeFormat`, and a light, dark or system theme.
|
|
20
26
|
|
|
21
27
|
## Install
|
|
22
28
|
|
|
23
29
|
```bash
|
|
24
30
|
pnpm add @jaeungkim/gantt-chart
|
|
31
|
+
# npm install @jaeungkim/gantt-chart
|
|
32
|
+
# yarn add @jaeungkim/gantt-chart
|
|
25
33
|
```
|
|
26
34
|
|
|
27
|
-
`react` and `react-dom` (`^18` or `^19`) are peer dependencies.
|
|
35
|
+
`react` and `react-dom` (`^18` or `^19`) are peer dependencies. The runtime dependencies are
|
|
36
|
+
`dayjs` and `zustand`.
|
|
28
37
|
|
|
29
|
-
##
|
|
38
|
+
## Quick example
|
|
30
39
|
|
|
31
40
|
```tsx
|
|
41
|
+
import { useState } from 'react';
|
|
32
42
|
import { ReactGanttChart, type Task } from '@jaeungkim/gantt-chart';
|
|
33
43
|
import '@jaeungkim/gantt-chart/style.css';
|
|
34
44
|
|
|
35
|
-
const
|
|
45
|
+
const initialTasks: Task[] = [
|
|
36
46
|
{
|
|
37
|
-
id: '
|
|
38
|
-
name: '
|
|
39
|
-
startDate: '
|
|
40
|
-
endDate: '
|
|
47
|
+
id: 'design',
|
|
48
|
+
name: 'Design',
|
|
49
|
+
startDate: '2026-03-02',
|
|
50
|
+
endDate: '2026-03-06',
|
|
41
51
|
parentId: null,
|
|
42
52
|
sequence: '1',
|
|
53
|
+
progress: 100,
|
|
43
54
|
},
|
|
44
55
|
{
|
|
45
|
-
id: '
|
|
46
|
-
name: '
|
|
47
|
-
startDate: '
|
|
48
|
-
endDate: '
|
|
56
|
+
id: 'build',
|
|
57
|
+
name: 'Build',
|
|
58
|
+
startDate: '2026-03-09',
|
|
59
|
+
endDate: '2026-03-20',
|
|
49
60
|
parentId: null,
|
|
50
61
|
sequence: '2',
|
|
51
|
-
|
|
62
|
+
progress: 40,
|
|
63
|
+
dependencies: [{ targetId: 'design', type: 'FS' }],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'ship',
|
|
67
|
+
name: 'Ship',
|
|
68
|
+
startDate: '2026-03-23',
|
|
69
|
+
endDate: '2026-03-23',
|
|
70
|
+
parentId: null,
|
|
71
|
+
sequence: '3',
|
|
72
|
+
dependencies: [{ targetId: 'build', type: 'FS' }],
|
|
52
73
|
},
|
|
53
74
|
];
|
|
54
75
|
|
|
55
|
-
export default function
|
|
76
|
+
export default function ProjectChart() {
|
|
77
|
+
const [tasks, setTasks] = useState(initialTasks);
|
|
78
|
+
|
|
56
79
|
return (
|
|
57
80
|
<ReactGanttChart
|
|
58
81
|
tasks={tasks}
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
onTasksChange={setTasks}
|
|
83
|
+
height={420}
|
|
84
|
+
showTaskList
|
|
61
85
|
defaultScale="month"
|
|
62
|
-
onTasksChange={(updated) => console.log(updated)}
|
|
63
86
|
/>
|
|
64
87
|
);
|
|
65
88
|
}
|
|
66
89
|
```
|
|
67
90
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
Full walkthrough: **[Quick start](https://github.com/jaeungkim/gantt-chart/blob/main/docs/en/quick-start.md)**.
|
|
72
|
-
|
|
73
|
-
## What it does
|
|
74
|
-
|
|
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.
|
|
78
|
-
|
|
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.
|
|
81
|
-
|
|
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.
|
|
85
|
-
|
|
86
|
-
**Accessibility** — one ARIA treegrid with a roving tab stop; bars move, resize and step their
|
|
87
|
-
progress from the keyboard.
|
|
88
|
-
|
|
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.
|
|
91
|
-
|
|
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)**.
|
|
91
|
+
That chart is editable. Every committed gesture ends in one `onTasksChange` call with the complete
|
|
92
|
+
next array. The chart persists nothing, including the scale.
|
|
94
93
|
|
|
95
94
|
## Documentation
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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 |
|
|
96
|
+
[Quick start](https://gantt.jaeungkim.com/docs/quick-start) goes from install to a chart your app
|
|
97
|
+
can edit. [GanttProps](https://gantt.jaeungkim.com/docs/ref/props) lists every prop, callback and
|
|
98
|
+
default. The chart renders no toolbar, so your app renders the scale control and drives it
|
|
99
|
+
through the `ref` in [Imperative API](https://gantt.jaeungkim.com/docs/imperative-api).
|
|
100
|
+
|
|
101
|
+
The tree and calendar helpers are exported as plain functions that run without React or a DOM. See
|
|
102
|
+
[Headless core](https://gantt.jaeungkim.com/docs/headless-core).
|
|
107
103
|
|
|
108
|
-
|
|
104
|
+
Release notes are on [GitHub Releases](https://github.com/jaeungkim/gantt-chart/releases).
|
|
109
105
|
|
|
110
106
|
## Contributing
|
|
111
107
|
|
|
112
108
|
Setup, the checks CI runs, and the branch and PR conventions are in
|
|
113
109
|
[CONTRIBUTING.md](CONTRIBUTING.md). Questions and ideas go in
|
|
114
|
-
[Discussions](https://github.com/jaeungkim/gantt-chart/discussions)
|
|
110
|
+
[Discussions](https://github.com/jaeungkim/gantt-chart/discussions). Issues are for bugs and
|
|
115
111
|
concrete feature requests.
|
|
116
112
|
|
|
117
113
|
## License
|
package/dist/Gantt.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { GanttHandle } from './timeline/hooks/useGanttScrollApi';
|
|
2
|
+
import { GanttProps } from './props';
|
|
3
|
+
/** Gantt chart component. Creates a store per instance, so two charts on one page cannot share state. */
|
|
4
|
+
declare const Gantt: import('react').ForwardRefExoticComponent<GanttProps & import('react').RefAttributes<GanttHandle>>;
|
|
5
|
+
export default Gantt;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { GanttDependencyChange } from '../../dependencies/hooks/useGanttLinkDrag';
|
|
2
|
+
import { GanttBarOptions } from '../../shared/types';
|
|
3
|
+
import { GanttInteractionConfig, TaskTransformed } from '../../shared/task';
|
|
4
|
+
import { WorkingCalendar } from '../../core';
|
|
5
|
+
interface GanttBarProps {
|
|
6
|
+
currentTask: TaskTransformed;
|
|
7
|
+
options: GanttBarOptions;
|
|
8
|
+
interaction?: GanttInteractionConfig;
|
|
9
|
+
/** Roving tabindex of the treegrid - exactly one cell carries 0, so Tab enters the grid once */
|
|
10
|
+
tabIndex: number;
|
|
11
|
+
/** `row:column` coordinate the chart's focus manager looks the cell up by */
|
|
12
|
+
cellCoord?: string;
|
|
13
|
+
/** Working-day calendar - drag results snap forward off non-working days */
|
|
14
|
+
calendar?: WorkingCalendar;
|
|
15
|
+
/** Scroll the timeline when the drag reaches a viewport edge; Gantt.tsx owns the default */
|
|
16
|
+
autoScrollOnDrag: boolean;
|
|
17
|
+
onDependencyCreate?: (change: GanttDependencyChange) => boolean | void;
|
|
18
|
+
}
|
|
19
|
+
export default function GanttBar({ currentTask, options, interaction, tabIndex, cellCoord, calendar, autoScrollOnDrag, onDependencyCreate, }: GanttBarProps): import("react").JSX.Element;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { VirtualItem } from '../../shared/virtual/window';
|
|
2
|
+
import { GanttDependencyChange } from '../../dependencies/hooks/useGanttLinkDrag';
|
|
3
|
+
import { GanttBarOptions } from '../../shared/types';
|
|
4
|
+
import { WorkingCalendar } from '../../core';
|
|
5
|
+
import { GanttInteractionConfig } from '../../shared/task';
|
|
6
|
+
import { GanttFocus } from '../../interaction/utils/a11y';
|
|
7
|
+
import { GanttRow } from '../../rows/utils/rows';
|
|
8
|
+
interface GanttBarsLayerProps {
|
|
9
|
+
rows: GanttRow[];
|
|
10
|
+
virtualItems: VirtualItem[];
|
|
11
|
+
/** Cells before the bars on a row - the bar's column is this plus its lane */
|
|
12
|
+
gridColumnCount: number;
|
|
13
|
+
focus: GanttFocus;
|
|
14
|
+
/** Horizontal culling, shared with the header so the two cannot disagree */
|
|
15
|
+
isBarVisible: (barLeft: number, barWidth: number) => boolean;
|
|
16
|
+
options: GanttBarOptions;
|
|
17
|
+
interaction: GanttInteractionConfig;
|
|
18
|
+
/** Working-day calendar - drag results snap forward off non-working days */
|
|
19
|
+
calendar?: WorkingCalendar;
|
|
20
|
+
autoScrollOnDrag: boolean;
|
|
21
|
+
onDependencyCreate?: (change: GanttDependencyChange) => boolean | void;
|
|
22
|
+
}
|
|
23
|
+
export default function GanttBarsLayer({ rows, virtualItems, gridColumnCount, focus, isBarVisible, options, interaction, calendar, autoScrollOnDrag, onDependencyCreate, }: GanttBarsLayerProps): (import("react").JSX.Element | null)[];
|
|
24
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function GanttDragGuides(): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { GanttDragMode } from '../../shared/types';
|
|
2
|
+
import { GanttInteractionConfig, Task, TaskTransformed } from '../../shared/task';
|
|
3
|
+
import { WorkingCalendar } from '../../core';
|
|
4
|
+
export type DragMode = GanttDragMode;
|
|
5
|
+
interface GanttBarDragOptions {
|
|
6
|
+
onTasksChange?: (updatedTasks: Task[]) => void;
|
|
7
|
+
/** Scroll the timeline when the drag reaches a viewport edge (default true) */
|
|
8
|
+
autoScroll?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Gantt bar drag behavior; `autoScroll` (default true) scrolls the timeline at a viewport edge. */
|
|
11
|
+
export declare function useGanttBarDrag(task: TaskTransformed, options?: GanttBarDragOptions, interaction?: GanttInteractionConfig, calendar?: WorkingCalendar): {
|
|
12
|
+
onPointerDown: import('react').PointerEventHandler<HTMLDivElement>;
|
|
13
|
+
dragMode: GanttDragMode | null;
|
|
14
|
+
consumeDragClick: () => boolean;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Dayjs } from 'dayjs';
|
|
2
|
+
import { GanttBottomRowCell, GanttScaleKey } from '../../shared/types';
|
|
3
|
+
/** The task the user drew, handed to `onTaskCreate` - nothing is committed by the chart */
|
|
4
|
+
export interface GanttTaskDraft {
|
|
5
|
+
/** UTC ISO string, snapped to the current scale */
|
|
6
|
+
startDate: string;
|
|
7
|
+
endDate: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function defaultTaskDraft(now: Dayjs, timelineTicks: GanttBottomRowCell[], scaleKey: GanttScaleKey): GanttTaskDraft | null;
|
|
10
|
+
interface DrawGhost {
|
|
11
|
+
leftPx: number;
|
|
12
|
+
widthPx: number;
|
|
13
|
+
topPx: number;
|
|
14
|
+
}
|
|
15
|
+
interface UseGanttDrawCreateParams {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
rowCount: number;
|
|
18
|
+
onTaskCreate?: (draft: GanttTaskDraft) => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function drawSlotTopPx(offsetY: number, rowCount: number): number | null;
|
|
21
|
+
export declare function useGanttDrawCreate({ enabled, rowCount, onTaskCreate, }: UseGanttDrawCreateParams): {
|
|
22
|
+
onDrawPointerDown: (e: React.PointerEvent<HTMLDivElement>) => void;
|
|
23
|
+
ghost: DrawGhost | null;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export interface GanttProgressDragOptions {
|
|
2
|
+
import { Task, TaskTransformed } from '../../shared/task';
|
|
3
|
+
interface GanttProgressDragOptions {
|
|
5
4
|
onTasksChange?: (updatedTasks: Task[]) => void;
|
|
6
|
-
onBeforeTaskChange?: GanttBeforeChangeHandler;
|
|
7
5
|
}
|
|
8
|
-
/**
|
|
9
|
-
* Progress handle drag hook
|
|
10
|
-
* Previews with a local value while dragging, commits to rawTasks on pointerup
|
|
11
|
-
*/
|
|
12
6
|
export declare function useGanttProgressDrag(task: TaskTransformed, barRef: React.RefObject<HTMLDivElement | null>, options?: GanttProgressDragOptions): {
|
|
13
7
|
onProgressPointerDown: React.PointerEventHandler<HTMLDivElement>;
|
|
14
8
|
progress: number | null;
|
|
15
9
|
isDraggingProgress: boolean;
|
|
16
10
|
};
|
|
11
|
+
export {};
|
package/dist/core/calendar.d.ts
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
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
|
-
*/
|
|
2
|
+
/** The calendar every piece of date arithmetic in the core routes through. */
|
|
10
3
|
export interface WorkingCalendar {
|
|
11
|
-
/** False for the default calendar (every day counts)
|
|
4
|
+
/** False for the default calendar (every day counts) */
|
|
12
5
|
readonly skipsNonWorkingDays: boolean;
|
|
13
6
|
isWorkingDay(date: Dayjs): boolean;
|
|
14
7
|
/** Moves `days` days forward (or backward), skipping non-working days */
|
|
@@ -27,19 +20,8 @@ export interface WorkingCalendarOptions {
|
|
|
27
20
|
workingWeekdays?: number[];
|
|
28
21
|
/** Non-working dates as UTC `YYYY-MM-DD` strings */
|
|
29
22
|
holidays?: string[];
|
|
30
|
-
/** Replaces the weekday + holiday check entirely */
|
|
31
|
-
isNonWorkingDay?: (date: Dayjs) => boolean;
|
|
32
23
|
}
|
|
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
|
-
*/
|
|
24
|
+
/** The default calendar: every day counts, so arithmetic through it is plain calendar arithmetic. */
|
|
38
25
|
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
|
-
*/
|
|
26
|
+
/** A calendar that skips weekends and holidays, configured like the chart's non-working-day shading. */
|
|
45
27
|
export declare function createWorkingCalendar(options?: WorkingCalendarOptions): WorkingCalendar;
|
package/dist/core/dates.d.ts
CHANGED
|
@@ -1,32 +1,16 @@
|
|
|
1
1
|
import { default as dayjs, Dayjs } from 'dayjs';
|
|
2
2
|
/**
|
|
3
|
-
* The chart's own dayjs - always parses and displays in UTC
|
|
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
|
|
3
|
+
* The chart's own dayjs - always parses and displays in UTC, so the same data lands in the
|
|
4
|
+
* same cells for every viewer and DST never changes a cell's width (#84, #28).
|
|
13
5
|
*/
|
|
14
6
|
declare const ganttDayjs: typeof dayjs.utc;
|
|
15
7
|
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
|
-
*/
|
|
8
|
+
/** Calendar quarter of the date, 1-4 - inlined so dayjs' quarterOfYear plugin stays out of the bundle */
|
|
23
9
|
export declare function quarterOfYear(date: Dayjs): number;
|
|
24
10
|
/** First moment of the calendar quarter the date falls in */
|
|
25
11
|
export declare function startOfQuarter(date: Dayjs): Dayjs;
|
|
26
12
|
/**
|
|
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.
|
|
13
|
+
* First moment of the week the date falls in; `firstDayOfWeek` is 0 = Sunday .. 6 = Saturday.
|
|
14
|
+
* Computed here because dayjs' `startOf('week')` is fixed to the loaded locale.
|
|
31
15
|
*/
|
|
32
16
|
export declare function startOfWeek(date: Dayjs, firstDayOfWeek?: number): Dayjs;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
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
1
|
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
2
|
export { buildTaskTree, collectSubtreeIds, getVisibleTasks, rollUpTasks, type TaskTree, } from './tree';
|
|
3
|
+
export { moveTask, sortTasksBySequence, validateMove, type GanttMoveOptions, } from './reorder';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Task } from './types';
|
|
2
|
+
/** Where a task is going: a parent, and a slot among that parent's children */
|
|
3
|
+
export interface GanttTaskMove {
|
|
4
|
+
taskId: string;
|
|
5
|
+
/** New parent, null for the root level */
|
|
6
|
+
toParentId: string | null;
|
|
7
|
+
/** Slot among the new parent's children, counted in the list as it stands after the move */
|
|
8
|
+
toIndex: number;
|
|
9
|
+
}
|
|
10
|
+
/** The move that was applied - `toIndex` for an index-based API, `afterId`/`beforeId` for a position-based one */
|
|
11
|
+
export interface GanttTaskMoveChange extends GanttTaskMove {
|
|
12
|
+
fromParentId: string | null;
|
|
13
|
+
fromIndex: number;
|
|
14
|
+
/** Sibling the task now follows - null when it is the first child */
|
|
15
|
+
afterId: string | null;
|
|
16
|
+
/** Sibling the task now precedes - null when it is the last child */
|
|
17
|
+
beforeId: string | null;
|
|
18
|
+
}
|
|
19
|
+
/** Where a drop lands relative to the row under the pointer */
|
|
20
|
+
export type GanttDropMode = "before" | "after" | "child";
|
|
21
|
+
/** Why a move was refused - null from `validateMove` means it is allowed */
|
|
22
|
+
export type GanttMoveRejection = "unknown-task" | "unknown-parent" | "read-only" | "cycle" | "reparent-disabled" | "no-op";
|
|
23
|
+
export interface GanttMoveOptions {
|
|
24
|
+
/** Whether the parentId hierarchy is on - off, depth comes from `sequence` and a reparent is refused */
|
|
25
|
+
hierarchy: boolean;
|
|
26
|
+
/** Whether this task may be moved at all - left out, every task may */
|
|
27
|
+
canReorder?: (task: Task) => boolean;
|
|
28
|
+
}
|
|
29
|
+
/** The sibling lists a move is measured against - `toIndex` is counted against exactly these */
|
|
30
|
+
interface TaskOrder {
|
|
31
|
+
/** Normalized parent of each task - null for a root, an orphan or a cycle */
|
|
32
|
+
parentOf: ReadonlyMap<string, string | null>;
|
|
33
|
+
/** One parent's children in row order; pass null for the root list */
|
|
34
|
+
childrenOf: (parentId: string | null) => readonly string[];
|
|
35
|
+
}
|
|
36
|
+
/** Sort tasks by their sequence hierarchy - row order comes from this and nothing else */
|
|
37
|
+
export declare function sortTasksBySequence<T extends Pick<Task, "sequence">>(tasks: T[]): T[];
|
|
38
|
+
/** Reads the ordered forest the moves below are counted against */
|
|
39
|
+
export declare function buildTaskOrder(tasks: Task[], hierarchy: boolean): TaskOrder;
|
|
40
|
+
/**
|
|
41
|
+
* The move a drop on `targetId` would make: `before`/`after` make it a sibling, `child`
|
|
42
|
+
* appends it. Says nothing about whether the move is allowed - that is `validateMove`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function moveForDrop(order: TaskOrder, taskId: string, targetId: string, mode: GanttDropMode): GanttTaskMove;
|
|
45
|
+
/** Whether a move can be applied, and why not when it cannot - runs on every frame of a row drag */
|
|
46
|
+
export declare function validateMove(tasks: Task[], move: GanttTaskMove, options: GanttMoveOptions): GanttMoveRejection | null;
|
|
47
|
+
/**
|
|
48
|
+
* Applies a move, renumbering every sequence from the resulting row order; `parentId` is rewritten
|
|
49
|
+
* on the moved task only. Null when refused (`validateMove` says why); unchanged tasks keep identity.
|
|
50
|
+
*/
|
|
51
|
+
export declare function moveTask(tasks: Task[], move: GanttTaskMove, options: GanttMoveOptions): {
|
|
52
|
+
tasks: Task[];
|
|
53
|
+
change: GanttTaskMoveChange;
|
|
54
|
+
} | null;
|
|
55
|
+
export {};
|
package/dist/core/tree.d.ts
CHANGED
|
@@ -2,12 +2,8 @@ import { Task } from './types';
|
|
|
2
2
|
/** The minimum a task needs for the tree math - TaskTransformed fits as-is */
|
|
3
3
|
type TaskNode = Pick<Task, "id" | "parentId">;
|
|
4
4
|
/**
|
|
5
|
-
* A normalized tree built from parentId
|
|
6
|
-
*
|
|
7
|
-
* Orphans (a parent id that is not in the data), self-references and cyclic chains all get
|
|
8
|
-
* their parent link cut and become roots. The parentOf/childIds that come out are therefore
|
|
9
|
-
* always acyclic, so the functions below - and the render - can walk up or down without
|
|
10
|
-
* risking an infinite loop.
|
|
5
|
+
* A normalized tree built from parentId. Orphans, self-references and cycles lose their
|
|
6
|
+
* parent link and become roots, so what comes out is always acyclic.
|
|
11
7
|
*/
|
|
12
8
|
export interface TaskTree {
|
|
13
9
|
/** parent id -> child ids (in input order) */
|
|
@@ -31,12 +27,8 @@ export declare function collectSubtreeIds(tasks: TaskNode[], rootId: string, tre
|
|
|
31
27
|
*/
|
|
32
28
|
export declare function getVisibleTasks<T extends TaskNode>(tasks: T[], collapsedIds: Iterable<string>, tree?: TaskTree): T[];
|
|
33
29
|
/**
|
|
34
|
-
* The tasks with every parent recomputed as a summary row
|
|
35
|
-
*
|
|
36
|
-
* Start and end always come from the children, never from what the data says
|
|
37
|
-
* (min(child start)..max(child end); a milestone child counts at its startDate alone).
|
|
38
|
-
* Deepest first, so a grandchild's move travels up through the parent and the grandparent.
|
|
39
|
-
* An explicit progress is left alone; only a missing one is rolled up from the children.
|
|
30
|
+
* The tasks with every parent recomputed as a summary row: start/end always from the children
|
|
31
|
+
* (deepest first, so a move travels all the way up); an explicit progress is left alone.
|
|
40
32
|
*/
|
|
41
33
|
export declare function rollUpTasks(tasks: Task[], tree?: TaskTree): Task[];
|
|
42
34
|
export {};
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,23 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The task data model - the part of it the headless core owns.
|
|
3
|
-
*
|
|
4
|
-
* Everything here is plain data and pure functions: no React, no DOM, no pixels.
|
|
5
|
-
* `src/types/task.ts` re-exports these and adds the render-side types (bar geometry,
|
|
6
|
-
* arrow coordinates) on top.
|
|
7
|
-
*/
|
|
8
|
-
export type TaskType = 'task' | 'milestone';
|
|
9
1
|
export type DependencyType = 'FS' | 'SS' | 'FF' | 'SF';
|
|
10
2
|
export interface TaskDependency {
|
|
11
3
|
/** The predecessor's id - a task's `dependencies` list the tasks it waits on */
|
|
12
4
|
targetId: string;
|
|
13
5
|
type: DependencyType;
|
|
14
|
-
/**
|
|
15
|
-
* Signed delay between the two ends of the link, in days
|
|
16
|
-
*
|
|
17
|
-
* Positive is lag (wait this long after the predecessor), negative is lead (overlap).
|
|
18
|
-
* Counted in working days when the working-day calendar is on, calendar days otherwise.
|
|
19
|
-
*/
|
|
20
|
-
lag?: number;
|
|
21
6
|
}
|
|
22
7
|
export interface Task {
|
|
23
8
|
id: string;
|
|
@@ -26,26 +11,13 @@ export interface Task {
|
|
|
26
11
|
endDate: string;
|
|
27
12
|
parentId: string | null;
|
|
28
13
|
sequence: string;
|
|
29
|
-
/** Task kind - 'milestone' renders as a diamond at startDate (default 'task') */
|
|
30
|
-
type?: TaskType;
|
|
31
14
|
/** Progress 0-100 (%) - omitted means no progress display */
|
|
32
15
|
progress?: number;
|
|
33
|
-
/**
|
|
34
|
-
* Bar color - any CSS color value
|
|
35
|
-
*
|
|
36
|
-
* The progress fill and the hover shade are derived from it, so one value colors the
|
|
37
|
-
* whole bar. Omitted, the `--gantt-*` theme tokens decide as before.
|
|
38
|
-
*/
|
|
16
|
+
/** Bar color (any CSS color) - progress fill and hover shade derive from it; omitted, the `--gantt-*` tokens decide */
|
|
39
17
|
color?: string;
|
|
40
18
|
/** Extra class name put on this task's bar and its task-list row */
|
|
41
19
|
className?: string;
|
|
42
|
-
/**
|
|
43
|
-
* Swimlane this task shares a row with
|
|
44
|
-
*
|
|
45
|
-
* Tasks with the same lane (inside the same group) are drawn side by side on one
|
|
46
|
-
* row; overlapping ones stack onto extra rows automatically. Omitted, the task
|
|
47
|
-
* gets a row of its own as before.
|
|
48
|
-
*/
|
|
20
|
+
/** Lane this task shares a row with - lane-mates draw side by side, overlaps stack onto extra rows */
|
|
49
21
|
lane?: string;
|
|
50
22
|
dependencies?: TaskDependency[];
|
|
51
23
|
/** Blocks every gesture on this task - overrides the chart's `readOnly` prop */
|
|
@@ -60,17 +32,12 @@ export interface Task {
|
|
|
60
32
|
allowLinkCreate?: boolean;
|
|
61
33
|
/** Allows/blocks deleting a dependency this task owns - overrides both `readOnly` settings */
|
|
62
34
|
allowLinkDelete?: boolean;
|
|
35
|
+
/** Allows/blocks dragging this row to a new position or parent - opt-in, off unless this or the chart's `allowReorder` turns it on */
|
|
36
|
+
allowReorder?: boolean;
|
|
63
37
|
/** Earliest date this task may be dragged to (ISO string) - overrides the chart's `minDate` */
|
|
64
38
|
minDate?: string;
|
|
65
39
|
/** Latest date this task may be dragged to (ISO string) - overrides the chart's `maxDate` */
|
|
66
40
|
maxDate?: string;
|
|
67
|
-
/** The scheduling engine never moves this task; it still constrains its successors */
|
|
68
|
-
manuallyScheduled?: boolean;
|
|
69
|
-
/** Planned start snapshot - drawn as a thin bar under the live one (UTC ISO string) */
|
|
70
|
-
baselineStart?: string;
|
|
71
|
-
/** Planned end snapshot (UTC ISO string) */
|
|
72
|
-
baselineEnd?: string;
|
|
73
41
|
}
|
|
74
|
-
export declare function isMilestoneTask(task: Pick<Task, 'type'>): boolean;
|
|
75
42
|
/** Normalizes progress into the 0-100 range; null when the value is missing or invalid */
|
|
76
43
|
export declare function normalizeProgress(progress: number | undefined): number | null;
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { GanttDependencyChange } from '../hooks/useGanttLinkDrag';
|
|
2
|
-
import {
|
|
2
|
+
import { GanttVirtualization } from '../../timeline/hooks/useGanttVirtualization';
|
|
3
|
+
import { GanttInteractionConfig, Task, TaskTransformed } from '../../shared/task';
|
|
3
4
|
interface Props {
|
|
4
5
|
transformedTasks: TaskTransformed[];
|
|
5
|
-
/** Row count of the chart - a lane row can carry several tasks, so it is not the task count */
|
|
6
6
|
rowCount: number;
|
|
7
|
-
|
|
8
|
-
criticalLinkIds?: Set<string>;
|
|
7
|
+
virtual: GanttVirtualization;
|
|
9
8
|
interaction?: GanttInteractionConfig;
|
|
10
9
|
onTasksChange?: (updatedTasks: Task[]) => void;
|
|
11
|
-
/** Returning false keeps the dependency */
|
|
12
10
|
onDependencyDelete?: (change: GanttDependencyChange) => boolean | void;
|
|
13
11
|
}
|
|
14
|
-
export default function GanttDependencyArrows({ transformedTasks, rowCount,
|
|
12
|
+
export default function GanttDependencyArrows({ transformedTasks, rowCount, virtual, interaction, onTasksChange, onDependencyDelete, }: Props): import("react").JSX.Element;
|
|
15
13
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DependencyType, GanttInteractionConfig, Task, TaskTransformed } from '
|
|
2
|
-
import { LinkAnchor } from '../utils/
|
|
1
|
+
import { DependencyType, GanttInteractionConfig, Task, TaskTransformed } from '../../shared/task';
|
|
2
|
+
import { LinkAnchor } from '../utils/link';
|
|
3
3
|
/** The link the user drew, handed to `onDependencyCreate` before anything is committed */
|
|
4
4
|
export interface GanttDependencyChange {
|
|
5
5
|
/** Task the drag started on */
|
|
@@ -12,19 +12,8 @@ interface UseGanttLinkDragParams {
|
|
|
12
12
|
task: TaskTransformed;
|
|
13
13
|
interaction?: GanttInteractionConfig;
|
|
14
14
|
onTasksChange?: (updatedTasks: Task[]) => void;
|
|
15
|
-
/** Returning false rejects the link */
|
|
16
15
|
onDependencyCreate?: (change: GanttDependencyChange) => boolean | void;
|
|
17
16
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Dragging a dependency from one of a bar's connector dots to another bar
|
|
20
|
-
*
|
|
21
|
-
* The gesture's direction is the dependency's direction: the bar the drag starts on is
|
|
22
|
-
* the predecessor, the bar it lands on the successor. Which ends the two dots sit on
|
|
23
|
-
* decides the type (see `linkTypeFromAnchors`).
|
|
24
|
-
*
|
|
25
|
-
* The drop target is found with `elementFromPoint` rather than from the bar geometry, so
|
|
26
|
-
* virtualization, scrolling and the sticky task list pane all take care of themselves.
|
|
27
|
-
*/
|
|
28
17
|
export declare function useGanttLinkDrag({ task, interaction, onTasksChange, onDependencyCreate, }: UseGanttLinkDragParams): {
|
|
29
18
|
startLink: (anchor: LinkAnchor) => (e: React.PointerEvent<HTMLElement>) => void;
|
|
30
19
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { RenderedDependency, TaskTransformed } from '../../shared/task';
|
|
2
|
+
import { LinkAnchor } from './link';
|
|
3
|
+
interface DragOffset {
|
|
4
|
+
offsetX: number;
|
|
5
|
+
offsetWidth: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function getSmartGanttPath(dependencyType: string, startX: number, startY: number, endX: number, endY: number): string;
|
|
8
|
+
export declare function anchorPoint(task: TaskTransformed, anchor: LinkAnchor, offset?: DragOffset): {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function buildTaskIndex(transformedTasks: TaskTransformed[]): Map<string, TaskTransformed>;
|
|
13
|
+
export interface ArrowViewport {
|
|
14
|
+
topPx: number;
|
|
15
|
+
bottomPx: number;
|
|
16
|
+
isBarVisible: (left: number, width: number) => boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function isArrowVisible(dep: Pick<RenderedDependency, "fromX" | "fromY" | "toX" | "toY">, viewport: ArrowViewport): boolean;
|
|
19
|
+
export declare function buildDependencies(taskById: Map<string, TaskTransformed>, liveOffsets: Record<string, DragOffset>): RenderedDependency[];
|
|
20
|
+
export {};
|