@ganttloom/gantt-react 0.2.1 → 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/README.md CHANGED
@@ -8,6 +8,8 @@ wrapper: it owns no scheduling logic of its own, just React lifecycle,
8
8
  props-as-events, and an imperative handle for the escape hatches (undo/redo,
9
9
  zoom, export) that don't fit the declarative-props model.
10
10
 
11
+ ![ganttloom overview — day-view Gantt chart with dependencies, critical path, and baselines](https://raw.githubusercontent.com/santhoshkumarhari/ganttLoom/master/docs/screenshots/overview.png)
12
+
11
13
  ## Install
12
14
 
13
15
  ```bash
@@ -107,7 +109,28 @@ shift-click selects a range. Then drive it from the handle:
107
109
  ```
108
110
 
109
111
  Both off by default (same construction-time-only wiring caveat as
110
- `onTaskCreate` — pass the prop from the first render).
112
+ `onTaskCreate` — pass the prop from the first render). Double-clicking a
113
+ task bar itself fires the separate `onTaskDblClick={(task) => ...}`, same
114
+ caveat.
115
+
116
+ ## Filling the viewport at any zoom level
117
+
118
+ A short project at month/year zoom, or just a handful of tasks, naturally
119
+ renders narrow. `autoFitToViewport` stretches (never shrinks) the timeline
120
+ to fill the container instead of leaving empty space:
121
+
122
+ ```tsx
123
+ <GanttChart tasks={tasks} autoFitToViewport />
124
+ ```
125
+
126
+ Off by default — upgrading doesn't silently change an existing chart's
127
+ pixel layout for anyone who hasn't opted in.
128
+
129
+ Always on, separately: the visible range is padded to a minimum tick count
130
+ per zoom level, so a short project at year/quarter zoom never collapses
131
+ into one or two lonely columns, and the date header stays pinned to the
132
+ top while scrolling a tall task list vertically (still scrolling
133
+ horizontally in sync with the bars).
111
134
 
112
135
  ## Drag-to-create
113
136
 
@@ -140,6 +163,21 @@ See `@ganttloom/gantt-core`'s README for the full behavior of each — the
140
163
  props here are 1:1 mirrors of its `GanttOptions` fields, and `filterTasks`
141
164
  is a pure re-export.
142
165
 
166
+ ## Column sorting
167
+
168
+ Mark a column `sortable`; clicking its header cycles asc → desc → none:
169
+
170
+ ```tsx
171
+ <GanttChart
172
+ tasks={tasks}
173
+ columns={[{ id: "name", title: "Task", sortable: true }]}
174
+ onSortChange={(sort) => console.log(sort)} // { columnId, direction } | null
175
+ />
176
+ ```
177
+
178
+ `chartRef.current?.setSort(columnId, direction)` / `.getSort()` are also
179
+ available on the handle.
180
+
143
181
  ## Resource leveling and multi-page PNG export
144
182
 
145
183
  ```tsx
@@ -159,6 +197,21 @@ const svg = renderResourceHistogramSVG(buckets);
159
197
  // e.g. <div dangerouslySetInnerHTML={{ __html: svg }} />
160
198
  ```
161
199
 
200
+ ## Hover tooltips
201
+
202
+ On by default (name/dates/progress/assignees/notes), styled via plain CSS
203
+ (`.gantt-tooltip`, see `gantt-core`'s README) — not the browser's native
204
+ tooltip. `renderTooltip` fully replaces the content:
205
+
206
+ ```tsx
207
+ <GanttChart
208
+ tasks={tasks}
209
+ renderTooltip={(task) => (task.isMilestone ? null : `${task.name} — ${task.progress ?? 0}%`)}
210
+ />
211
+ ```
212
+
213
+ Set `showTooltip={false}` to disable it entirely.
214
+
162
215
  ## `useGanttChart` (lower-level hook)
163
216
 
164
217
  If you want to own the container element/DOM tree yourself instead of using
package/dist/index.cjs CHANGED
@@ -134,7 +134,8 @@ var EMPTY_RENDER_MODEL = {
134
134
  columns: [],
135
135
  theme: {},
136
136
  markers: [],
137
- rangeStart: /* @__PURE__ */ new Date(0)
137
+ rangeStart: /* @__PURE__ */ new Date(0),
138
+ sort: null
138
139
  };
139
140
  function GanttChartInner(props, ref) {
140
141
  const {
@@ -148,6 +149,7 @@ function GanttChartInner(props, ref) {
148
149
  onDependencyRemove,
149
150
  onDependencyDblClick,
150
151
  onTaskClick,
152
+ onTaskDblClick,
151
153
  onGroupToggle,
152
154
  onContextMenu,
153
155
  onTaskCreate,
@@ -155,6 +157,7 @@ function GanttChartInner(props, ref) {
155
157
  onColumnReorder,
156
158
  onTaskReorder,
157
159
  onSelectionChange,
160
+ onSortChange,
158
161
  ...rest
159
162
  } = props;
160
163
  const containerRef = (0, import_react2.useRef)(null);
@@ -172,6 +175,8 @@ function GanttChartInner(props, ref) {
172
175
  );
173
176
  const enableDependencyDblClickWiring = (0, import_react2.useRef)((_dep) => {
174
177
  });
178
+ const enableTaskDblClickWiring = (0, import_react2.useRef)((_task) => {
179
+ });
175
180
  const options = (0, import_react2.useMemo)(
176
181
  () => ({
177
182
  ...rest,
@@ -180,11 +185,11 @@ function GanttChartInner(props, ref) {
180
185
  onColumnResize: onColumnResize ? enableColumnResizeWiring.current : void 0,
181
186
  onColumnReorder: onColumnReorder ? enableColumnReorderWiring.current : void 0,
182
187
  onTaskReorder: onTaskReorder ? enableTaskReorderWiring.current : void 0,
183
- onDependencyDblClick: onDependencyDblClick ? enableDependencyDblClickWiring.current : void 0
184
- // onSelectionChange is deliberately NOT set here: gantt-core invokes it directly from
185
- // this.options AND emits a "selection-change" event at the same call sites, and the
186
- // effect below already subscribes to that event including it here would double-fire
187
- // every selection change.
188
+ onDependencyDblClick: onDependencyDblClick ? enableDependencyDblClickWiring.current : void 0,
189
+ onTaskDblClick: onTaskDblClick ? enableTaskDblClickWiring.current : void 0
190
+ // onSelectionChange/onSortChange are deliberately NOT set here: gantt-core invokes them
191
+ // directly from this.options AND emits a matching event at the same call sites, and the
192
+ // effect below already subscribes to those events — including them here would double-fire.
188
193
  }),
189
194
  [
190
195
  rest.viewMode,
@@ -199,6 +204,9 @@ function GanttChartInner(props, ref) {
199
204
  rest.showCriticalPath,
200
205
  rest.showBaseline,
201
206
  rest.showDeadlines,
207
+ rest.showTooltip,
208
+ rest.renderTooltip,
209
+ rest.autoFitToViewport,
202
210
  rest.showAssigneeAvatars,
203
211
  rest.enableHistory,
204
212
  rest.keyboardAccessible,
@@ -214,7 +222,8 @@ function GanttChartInner(props, ref) {
214
222
  Boolean(onColumnResize),
215
223
  Boolean(onColumnReorder),
216
224
  Boolean(onTaskReorder),
217
- Boolean(onDependencyDblClick)
225
+ Boolean(onDependencyDblClick),
226
+ Boolean(onTaskDblClick)
218
227
  ]
219
228
  );
220
229
  const { chart } = useGanttChart(containerRef, tasks, dependencies, options);
@@ -225,13 +234,15 @@ function GanttChartInner(props, ref) {
225
234
  onDependencyRemove,
226
235
  onDependencyDblClick,
227
236
  onTaskClick,
237
+ onTaskDblClick,
228
238
  onGroupToggle,
229
239
  onContextMenu,
230
240
  onTaskCreate,
231
241
  onColumnResize,
232
242
  onColumnReorder,
233
243
  onTaskReorder,
234
- onSelectionChange
244
+ onSelectionChange,
245
+ onSortChange
235
246
  });
236
247
  callbacksRef.current = {
237
248
  onDateChange,
@@ -240,13 +251,15 @@ function GanttChartInner(props, ref) {
240
251
  onDependencyRemove,
241
252
  onDependencyDblClick,
242
253
  onTaskClick,
254
+ onTaskDblClick,
243
255
  onGroupToggle,
244
256
  onContextMenu,
245
257
  onTaskCreate,
246
258
  onColumnResize,
247
259
  onColumnReorder,
248
260
  onTaskReorder,
249
- onSelectionChange
261
+ onSelectionChange,
262
+ onSortChange
250
263
  };
251
264
  (0, import_react2.useEffect)(() => {
252
265
  if (!chart) return void 0;
@@ -255,6 +268,7 @@ function GanttChartInner(props, ref) {
255
268
  const handleDependencyCreate = (dep) => callbacksRef.current.onDependencyCreate?.(dep);
256
269
  const handleDependencyRemove = (dep) => callbacksRef.current.onDependencyRemove?.(dep);
257
270
  const handleTaskClick = (p) => callbacksRef.current.onTaskClick?.(p.task);
271
+ const handleTaskDblClick = (p) => callbacksRef.current.onTaskDblClick?.(p.task);
258
272
  const handleGroupToggle = (p) => callbacksRef.current.onGroupToggle?.(p.task, p.collapsed);
259
273
  const handleContextMenu = (p) => callbacksRef.current.onContextMenu?.(p.task, p.evt);
260
274
  const handleTaskCreate = (p) => callbacksRef.current.onTaskCreate?.(p.task);
@@ -263,12 +277,14 @@ function GanttChartInner(props, ref) {
263
277
  const handleDependencyDblClick = (dep) => callbacksRef.current.onDependencyDblClick?.(dep);
264
278
  const handleTaskReorder = (p) => callbacksRef.current.onTaskReorder?.(p.draggedTaskId, p.targetTaskId, p.position);
265
279
  const handleSelectionChange = (p) => callbacksRef.current.onSelectionChange?.(p.taskIds);
280
+ const handleSortChange = (p) => callbacksRef.current.onSortChange?.(p.sort);
266
281
  chart.on("date-change", handleDateChange);
267
282
  chart.on("progress-change", handleProgressChange);
268
283
  chart.on("dependency-create", handleDependencyCreate);
269
284
  chart.on("dependency-remove", handleDependencyRemove);
270
285
  chart.on("dependency-dblclick", handleDependencyDblClick);
271
286
  chart.on("task-click", handleTaskClick);
287
+ chart.on("task-dblclick", handleTaskDblClick);
272
288
  chart.on("group-toggle", handleGroupToggle);
273
289
  chart.on("context-menu", handleContextMenu);
274
290
  chart.on("task-create", handleTaskCreate);
@@ -276,6 +292,7 @@ function GanttChartInner(props, ref) {
276
292
  chart.on("column-reorder", handleColumnReorder);
277
293
  chart.on("task-reorder", handleTaskReorder);
278
294
  chart.on("selection-change", handleSelectionChange);
295
+ chart.on("sort-change", handleSortChange);
279
296
  return () => {
280
297
  chart.off("date-change", handleDateChange);
281
298
  chart.off("progress-change", handleProgressChange);
@@ -283,6 +300,7 @@ function GanttChartInner(props, ref) {
283
300
  chart.off("dependency-remove", handleDependencyRemove);
284
301
  chart.off("dependency-dblclick", handleDependencyDblClick);
285
302
  chart.off("task-click", handleTaskClick);
303
+ chart.off("task-dblclick", handleTaskDblClick);
286
304
  chart.off("group-toggle", handleGroupToggle);
287
305
  chart.off("context-menu", handleContextMenu);
288
306
  chart.off("task-create", handleTaskCreate);
@@ -290,6 +308,7 @@ function GanttChartInner(props, ref) {
290
308
  chart.off("column-reorder", handleColumnReorder);
291
309
  chart.off("task-reorder", handleTaskReorder);
292
310
  chart.off("selection-change", handleSelectionChange);
311
+ chart.off("sort-change", handleSortChange);
293
312
  };
294
313
  }, [chart]);
295
314
  (0, import_react2.useImperativeHandle)(
@@ -321,6 +340,8 @@ function GanttChartInner(props, ref) {
321
340
  selectAllVisible: () => chart?.selectAllVisible(),
322
341
  clearSelection: () => chart?.clearSelection(),
323
342
  getSelectedTaskIds: () => chart?.getSelectedTaskIds() ?? [],
343
+ setSort: (columnId, direction) => chart?.setSort(columnId, direction),
344
+ getSort: () => chart?.getSort() ?? null,
324
345
  bulkShiftDates: (deltaMs) => chart?.bulkShiftDates(deltaMs),
325
346
  bulkDelete: () => chart?.bulkDelete(),
326
347
  toSVGString: () => chart?.toSVGString() ?? "",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/GanttChart.tsx","../src/useGanttChart.ts"],"sourcesContent":["export { GanttChart, default } from \"./GanttChart\";\nexport type { GanttChartProps, GanttChartHandle } from \"./GanttChart\";\n\nexport { useGanttChart } from \"./useGanttChart\";\nexport type { UseGanttChartResult } from \"./useGanttChart\";\n\n// Re-exported so consumers never need to add @ganttloom/gantt-core as a direct\n// dependency just to reference its types.\nexport type {\n ViewMode,\n DependencyType,\n GanttDependency,\n GanttAssignee,\n GanttTask,\n GanttColumn,\n GanttTheme,\n GanttRenderRow,\n GanttRenderBar,\n GanttRenderLink,\n GanttRenderTick,\n GanttRenderModel,\n GanttOptions,\n GanttEventMap,\n WorkingCalendar,\n ConstraintType,\n ResourceHistogramOptions,\n ResourceLoadBucket,\n GanttMarker,\n GanttRenderMarker,\n FilterTasksOptions,\n FilterTasksResult,\n Density,\n ResourceLevelingOptions,\n ResourceLevelingResult,\n} from \"@ganttloom/gantt-core\";\n\nexport {\n computeWBSCodes,\n applyConstraint,\n isWorkingDay,\n isHoliday,\n isWithinWorkingHours,\n isWorkingTime,\n nextWorkingDay,\n previousWorkingDay,\n shiftToWorkingDay,\n shiftToWorkingTime,\n computeResourceHistogram,\n renderResourceHistogramSVG,\n filterTasks,\n colorByField,\n applyColorByField,\n DEFAULT_PALETTE,\n DENSITY_PRESETS,\n levelResources,\n} from \"@ganttloom/gantt-core\";\n","// Pulls in the core's default styles so consumers get a working chart out of the box.\n// This resolves through gantt-core's package.json \"exports\" map (\"./styles.css\" -> \"./dist/styles.css\").\n// If your bundler can't resolve CSS imports from a dependency's export map, import it yourself instead:\n// import \"@ganttloom/gantt-core/styles.css\";\nimport \"@ganttloom/gantt-core/styles.css\";\n\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n type CSSProperties,\n type Ref,\n} from \"react\";\nimport type {\n GanttChart as GanttCore,\n GanttColumn,\n GanttDependency,\n GanttMarker,\n GanttOptions,\n GanttRenderModel,\n GanttTask,\n GanttTheme,\n ResourceHistogramOptions,\n ResourceLoadBucket,\n ResourceLevelingOptions,\n ResourceLevelingResult,\n ViewMode,\n WorkingCalendar,\n} from \"@ganttloom/gantt-core\";\nimport { useGanttChart } from \"./useGanttChart\";\n\nexport interface GanttChartProps {\n tasks: GanttTask[];\n dependencies?: GanttDependency[];\n\n // Mirrors of GanttOptions' non-callback fields.\n viewMode?: ViewMode;\n columns?: GanttColumn[];\n theme?: Partial<GanttTheme>;\n colorScheme?: \"light\" | \"dark\" | \"auto\";\n columnWidth?: number;\n readonly?: boolean;\n showProgress?: boolean;\n showDependencies?: boolean;\n gridPanelWidth?: number;\n showCriticalPath?: boolean;\n showBaseline?: boolean;\n showDeadlines?: boolean;\n showAssigneeAvatars?: boolean;\n enableHistory?: boolean;\n keyboardAccessible?: boolean;\n virtualScroll?: boolean;\n autoSchedule?: boolean;\n pagination?: { pageSize: number; page: number };\n snapToUnit?: \"hour\" | \"day\" | \"week\" | false;\n /** working-day/holiday definition used to shade non-working time and (with autoSchedule) skip it when cascading */\n calendar?: WorkingCalendar;\n /** arbitrary labeled vertical lines on the timeline, independent of any task */\n markers?: GanttMarker[];\n /** a group task with no explicit `progress` gets a duration-weighted average of its children's progress instead of 0 */\n autoRollupProgress?: boolean;\n /** enables click/ctrl-click/shift-click multi-selection of task bars, and the handle's bulkShiftDates()/bulkDelete() */\n selectable?: boolean;\n\n // Mirrors of GanttOptions' callback fields, as normal React event props.\n onDateChange?: (task: GanttTask, start: Date, end: Date) => void;\n onProgressChange?: (task: GanttTask, progress: number) => void;\n onDependencyCreate?: (dep: GanttDependency) => void;\n onDependencyRemove?: (dep: GanttDependency) => void;\n /** called on double-click of a dependency link, for building your own \"edit this link\" UI (see the handle's updateDependency) */\n onDependencyDblClick?: (dep: GanttDependency) => void;\n onTaskClick?: (task: GanttTask) => void;\n onGroupToggle?: (task: GanttTask, collapsed: boolean) => void;\n onContextMenu?: (task: GanttTask, evt: PointerEvent) => void;\n /** called when a task is created via drag-to-create on an empty timeline row */\n onTaskCreate?: (task: GanttTask) => void;\n /** called when a grid column is resized by dragging its header's edge */\n onColumnResize?: (columnId: string, width: number) => void;\n /** called when grid columns are reordered by dragging a header; `order` is the new full list of column ids */\n onColumnReorder?: (order: string[]) => void;\n /** called when a grid row is dragged onto another; \"inside\" reparents the dragged task under the target */\n onTaskReorder?: (draggedTaskId: string, targetTaskId: string, position: \"before\" | \"after\" | \"inside\") => void;\n onSelectionChange?: (taskIds: string[]) => void;\n\n /** Applied to the wrapping container div. */\n className?: string;\n style?: CSSProperties;\n}\n\n/** Imperative escape hatches that don't need to be reactive props. */\nexport interface GanttChartHandle {\n undo(): void;\n redo(): void;\n canUndo(): boolean;\n canRedo(): boolean;\n expandAll(): void;\n collapseAll(): void;\n toggleGroup(taskId: string): void;\n removeDependency(fromId: string, toId: string): void;\n updateDependency(fromId: string, toId: string, partial: Partial<GanttDependency>): void;\n updateTask(id: string, partial: Partial<GanttTask>): void;\n setViewMode(mode: ViewMode): void;\n fitToViewport(containerWidthPx: number): void;\n scrollToToday(): void;\n scrollToRangeStart(): void;\n scrollToRangeEnd(): void;\n getPageCount(): number;\n setPage(page: number): void;\n zoomIn(factor?: number): void;\n zoomOut(factor?: number): void;\n getZoomLevel(): number;\n getResourceHistogram(options?: ResourceHistogramOptions): ResourceLoadBucket[];\n getLeveledTasks(options?: ResourceLevelingOptions): ResourceLevelingResult;\n selectTask(id: string, options?: { additive?: boolean }): void;\n selectAllVisible(): void;\n clearSelection(): void;\n getSelectedTaskIds(): string[];\n bulkShiftDates(deltaMs: number): void;\n bulkDelete(): void;\n toSVGString(): string;\n toPNGDataURL(): Promise<string>;\n toPNGDataURLs(options?: { pageWidthPx?: number; pageHeightPx?: number }): Promise<string[]>;\n getRenderModel(): GanttRenderModel | null;\n /** Direct access to the underlying framework-agnostic core instance (null before mount). */\n getInstance(): GanttCore | null;\n}\n\nconst EMPTY_DEPENDENCIES: GanttDependency[] = [];\n\nconst EMPTY_RENDER_MODEL: GanttRenderModel = {\n width: 0,\n height: 0,\n rowHeight: 0,\n headerHeight: 0,\n rows: [],\n bars: [],\n links: [],\n ticks: [],\n columns: [],\n theme: {} as GanttTheme,\n markers: [],\n rangeStart: new Date(0),\n};\n\nfunction GanttChartInner(props: GanttChartProps, ref: Ref<GanttChartHandle>) {\n const {\n tasks,\n dependencies = EMPTY_DEPENDENCIES,\n className,\n style,\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n ...rest\n } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // The core only wires up its internal contextmenu DOM listener (and therefore only\n // ever emits the \"context-menu\" event) when options.onContextMenu is truthy AT\n // CONSTRUCTION TIME (see GanttChart#setupDom in gantt-core). We pass a stable no-op\n // here purely to force that wiring on regardless of whether the consumer supplied\n // onContextMenu, and dispatch the actual prop via the \"context-menu\" event\n // subscription below (so it stays reactive to prop changes and is never double-fired).\n const enableContextMenuWiring = useRef(() => {});\n\n // Same construction-time-only caveat as onContextMenu above: drag-to-create is only\n // wired into the core's InteractionController when options.onTaskCreate is truthy at\n // construction. A stable no-op forces it on whenever this component was given an\n // onTaskCreate prop at all; the real dispatch goes through the \"task-create\" event.\n const enableCreateWiring = useRef(() => {});\n\n // Same construction-time-only caveat: column resize/reorder DOM listeners are only\n // built into the grid header when these options are truthy at construction.\n const enableColumnResizeWiring = useRef((_columnId: string, _width: number) => {});\n const enableColumnReorderWiring = useRef((_order: string[]) => {});\n const enableTaskReorderWiring = useRef(\n (_draggedTaskId: string, _targetTaskId: string, _position: \"before\" | \"after\" | \"inside\") => {}\n );\n const enableDependencyDblClickWiring = useRef((_dep: GanttDependency) => {});\n\n // Deliberately depend on Boolean(handler) rather than the handler itself below: the\n // ref-backed enableXWiring callbacks already pick up the latest handler without\n // needing the memo to re-run, so depending on the raw (often inline) callback would\n // just re-create `options` — and remount the chart — every render.\n /* eslint-disable react-hooks/exhaustive-deps */\n const options = useMemo<GanttOptions>(\n () => ({\n ...rest,\n onContextMenu: enableContextMenuWiring.current,\n onTaskCreate: onTaskCreate ? enableCreateWiring.current : undefined,\n onColumnResize: onColumnResize ? enableColumnResizeWiring.current : undefined,\n onColumnReorder: onColumnReorder ? enableColumnReorderWiring.current : undefined,\n onTaskReorder: onTaskReorder ? enableTaskReorderWiring.current : undefined,\n onDependencyDblClick: onDependencyDblClick ? enableDependencyDblClickWiring.current : undefined,\n // onSelectionChange is deliberately NOT set here: gantt-core invokes it directly from\n // this.options AND emits a \"selection-change\" event at the same call sites, and the\n // effect below already subscribes to that event — including it here would double-fire\n // every selection change.\n }),\n [\n rest.viewMode,\n rest.columns,\n rest.theme,\n rest.colorScheme,\n rest.columnWidth,\n rest.readonly,\n rest.showProgress,\n rest.showDependencies,\n rest.gridPanelWidth,\n rest.showCriticalPath,\n rest.showBaseline,\n rest.showDeadlines,\n rest.showAssigneeAvatars,\n rest.enableHistory,\n rest.keyboardAccessible,\n rest.virtualScroll,\n rest.autoSchedule,\n rest.pagination,\n rest.snapToUnit,\n rest.calendar,\n rest.markers,\n rest.autoRollupProgress,\n rest.selectable,\n Boolean(onTaskCreate),\n Boolean(onColumnResize),\n Boolean(onColumnReorder),\n Boolean(onTaskReorder),\n Boolean(onDependencyDblClick),\n ]\n );\n /* eslint-enable react-hooks/exhaustive-deps */\n\n const { chart } = useGanttChart(containerRef, tasks, dependencies, options);\n\n // Keep the latest callback props in a ref so the event-subscription effect below\n // doesn't need to re-subscribe (tearing down/rebuilding listeners) on every render.\n const callbacksRef = useRef({\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n });\n callbacksRef.current = {\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n };\n\n useEffect(() => {\n if (!chart) return undefined;\n\n const handleDateChange = (p: { task: GanttTask; start: Date; end: Date }) =>\n callbacksRef.current.onDateChange?.(p.task, p.start, p.end);\n const handleProgressChange = (p: { task: GanttTask; progress: number }) =>\n callbacksRef.current.onProgressChange?.(p.task, p.progress);\n const handleDependencyCreate = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyCreate?.(dep);\n const handleDependencyRemove = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyRemove?.(dep);\n const handleTaskClick = (p: { task: GanttTask }) =>\n callbacksRef.current.onTaskClick?.(p.task);\n const handleGroupToggle = (p: { task: GanttTask; collapsed: boolean }) =>\n callbacksRef.current.onGroupToggle?.(p.task, p.collapsed);\n const handleContextMenu = (p: { task: GanttTask; evt: PointerEvent }) =>\n callbacksRef.current.onContextMenu?.(p.task, p.evt);\n const handleTaskCreate = (p: { task: GanttTask }) => callbacksRef.current.onTaskCreate?.(p.task);\n const handleColumnResize = (p: { columnId: string; width: number }) =>\n callbacksRef.current.onColumnResize?.(p.columnId, p.width);\n const handleColumnReorder = (p: { order: string[] }) =>\n callbacksRef.current.onColumnReorder?.(p.order);\n const handleDependencyDblClick = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyDblClick?.(dep);\n const handleTaskReorder = (p: {\n draggedTaskId: string;\n targetTaskId: string;\n position: \"before\" | \"after\" | \"inside\";\n }) => callbacksRef.current.onTaskReorder?.(p.draggedTaskId, p.targetTaskId, p.position);\n const handleSelectionChange = (p: { taskIds: string[] }) =>\n callbacksRef.current.onSelectionChange?.(p.taskIds);\n\n chart.on(\"date-change\", handleDateChange);\n chart.on(\"progress-change\", handleProgressChange);\n chart.on(\"dependency-create\", handleDependencyCreate);\n chart.on(\"dependency-remove\", handleDependencyRemove);\n chart.on(\"dependency-dblclick\", handleDependencyDblClick);\n chart.on(\"task-click\", handleTaskClick);\n chart.on(\"group-toggle\", handleGroupToggle);\n chart.on(\"context-menu\", handleContextMenu);\n chart.on(\"task-create\", handleTaskCreate);\n chart.on(\"column-resize\", handleColumnResize);\n chart.on(\"column-reorder\", handleColumnReorder);\n chart.on(\"task-reorder\", handleTaskReorder);\n chart.on(\"selection-change\", handleSelectionChange);\n\n return () => {\n chart.off(\"date-change\", handleDateChange);\n chart.off(\"progress-change\", handleProgressChange);\n chart.off(\"dependency-create\", handleDependencyCreate);\n chart.off(\"dependency-remove\", handleDependencyRemove);\n chart.off(\"dependency-dblclick\", handleDependencyDblClick);\n chart.off(\"task-click\", handleTaskClick);\n chart.off(\"group-toggle\", handleGroupToggle);\n chart.off(\"context-menu\", handleContextMenu);\n chart.off(\"task-create\", handleTaskCreate);\n chart.off(\"column-resize\", handleColumnResize);\n chart.off(\"column-reorder\", handleColumnReorder);\n chart.off(\"task-reorder\", handleTaskReorder);\n chart.off(\"selection-change\", handleSelectionChange);\n };\n }, [chart]);\n\n useImperativeHandle(\n ref,\n (): GanttChartHandle => ({\n undo: () => chart?.undo(),\n redo: () => chart?.redo(),\n canUndo: () => chart?.canUndo ?? false,\n canRedo: () => chart?.canRedo ?? false,\n expandAll: () => chart?.expandAll(),\n collapseAll: () => chart?.collapseAll(),\n toggleGroup: (taskId: string) => chart?.toggleGroup(taskId),\n removeDependency: (fromId: string, toId: string) => chart?.removeDependency(fromId, toId),\n updateDependency: (fromId: string, toId: string, partial: Partial<GanttDependency>) =>\n chart?.updateDependency(fromId, toId, partial),\n updateTask: (id: string, partial: Partial<GanttTask>) => chart?.updateTask(id, partial),\n setViewMode: (mode: ViewMode) => chart?.setViewMode(mode),\n fitToViewport: (containerWidthPx: number) => chart?.fitToViewport(containerWidthPx),\n scrollToToday: () => chart?.scrollToToday(),\n scrollToRangeStart: () => chart?.scrollToRangeStart(),\n scrollToRangeEnd: () => chart?.scrollToRangeEnd(),\n getPageCount: () => chart?.getPageCount() ?? 1,\n setPage: (page: number) => chart?.setPage(page),\n zoomIn: (factor?: number) => chart?.zoomIn(factor),\n zoomOut: (factor?: number) => chart?.zoomOut(factor),\n getZoomLevel: () => chart?.getZoomLevel() ?? 0,\n getResourceHistogram: (options?: ResourceHistogramOptions) =>\n chart?.getResourceHistogram(options) ?? [],\n getLeveledTasks: (options?: ResourceLevelingOptions) =>\n chart?.getLeveledTasks(options) ?? { tasks: [], shifted: [] },\n selectTask: (id: string, options?: { additive?: boolean }) => chart?.selectTask(id, options),\n selectAllVisible: () => chart?.selectAllVisible(),\n clearSelection: () => chart?.clearSelection(),\n getSelectedTaskIds: () => chart?.getSelectedTaskIds() ?? [],\n bulkShiftDates: (deltaMs: number) => chart?.bulkShiftDates(deltaMs),\n bulkDelete: () => chart?.bulkDelete(),\n toSVGString: () => chart?.toSVGString() ?? \"\",\n toPNGDataURL: () => chart?.toPNGDataURL() ?? Promise.resolve(\"\"),\n toPNGDataURLs: (options?: { pageWidthPx?: number; pageHeightPx?: number }) =>\n chart?.toPNGDataURLs(options) ?? Promise.resolve([]),\n getRenderModel: () => chart?.getRenderModel() ?? EMPTY_RENDER_MODEL,\n getInstance: () => chart,\n }),\n [chart]\n );\n\n return <div ref={containerRef} className={className} style={style} />;\n}\n\nexport const GanttChart = forwardRef(GanttChartInner);\nGanttChart.displayName = \"GanttChart\";\n\nexport default GanttChart;\n","import { useEffect, useRef, useState, type RefObject } from \"react\";\nimport { GanttChart as GanttCore } from \"@ganttloom/gantt-core\";\nimport type { GanttDependency, GanttOptions, GanttTask } from \"@ganttloom/gantt-core\";\n\nexport interface UseGanttChartResult {\n /** The underlying framework-agnostic GanttChart instance, or null before the DOM mount effect runs (or on the server). */\n chart: GanttCore | null;\n /** Reactive, subscribed to the core's \"history-change\" event. Only meaningful when `enableHistory` is set in options. */\n canUndo: boolean;\n /** Reactive, subscribed to the core's \"history-change\" event. Only meaningful when `enableHistory` is set in options. */\n canRedo: boolean;\n}\n\nconst EMPTY_DEPENDENCIES: GanttDependency[] = [];\nconst EMPTY_OPTIONS: GanttOptions = {};\n\n/**\n * Lower-level escape hatch for consumers who want to own their own container element\n * and DOM tree instead of using the <GanttChart /> component. Constructs a\n * @ganttloom/gantt-core GanttChart instance against `containerRef.current` on mount,\n * keeps it in sync with `tasks` / `dependencies` / `options` via setTasks / setDependencies /\n * setOptions (reference-equality checks only — pass new arrays/objects to trigger a sync),\n * and destroys it on unmount.\n */\nexport function useGanttChart(\n containerRef: RefObject<HTMLElement | null>,\n tasks: GanttTask[],\n dependencies: GanttDependency[] = EMPTY_DEPENDENCIES,\n options: GanttOptions = EMPTY_OPTIONS\n): UseGanttChartResult {\n const [chart, setChart] = useState<GanttCore | null>(null);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n\n const chartRef = useRef<GanttCore | null>(null);\n\n // Latest props, read from the mount effect so it doesn't need to depend on them\n // (which would otherwise tear down and recreate the whole chart on every change).\n const initialTasksRef = useRef(tasks);\n initialTasksRef.current = tasks;\n const initialDepsRef = useRef(dependencies);\n initialDepsRef.current = dependencies;\n const initialOptionsRef = useRef(options);\n initialOptionsRef.current = options;\n\n useEffect(() => {\n if (typeof window === \"undefined\") return undefined;\n const container = containerRef.current;\n if (!container) return undefined;\n\n const instance = new GanttCore(\n container,\n initialTasksRef.current,\n initialDepsRef.current,\n initialOptionsRef.current\n );\n chartRef.current = instance;\n setChart(instance);\n setCanUndo(instance.canUndo);\n setCanRedo(instance.canRedo);\n\n const onHistoryChange = (state: { canUndo: boolean; canRedo: boolean }) => {\n setCanUndo(state.canUndo);\n setCanRedo(state.canRedo);\n };\n instance.on(\"history-change\", onHistoryChange);\n\n return () => {\n instance.off(\"history-change\", onHistoryChange);\n instance.destroy();\n chartRef.current = null;\n setChart(null);\n setCanUndo(false);\n setCanRedo(false);\n };\n // Intentionally mount/unmount only — the container element identity is what matters.\n }, [containerRef]);\n\n const isFirstTasksSync = useRef(true);\n useEffect(() => {\n if (isFirstTasksSync.current) {\n isFirstTasksSync.current = false;\n return;\n }\n chartRef.current?.setTasks(tasks);\n }, [tasks]);\n\n const isFirstDepsSync = useRef(true);\n useEffect(() => {\n if (isFirstDepsSync.current) {\n isFirstDepsSync.current = false;\n return;\n }\n chartRef.current?.setDependencies(dependencies);\n }, [dependencies]);\n\n const isFirstOptionsSync = useRef(true);\n useEffect(() => {\n if (isFirstOptionsSync.current) {\n isFirstOptionsSync.current = false;\n return;\n }\n chartRef.current?.setOptions(options);\n }, [options]);\n\n return { chart, canUndo, canRedo };\n}\n"],"mappings":"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","names":["import_react","GanttCore","EMPTY_DEPENDENCIES","options","import_gantt_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/GanttChart.tsx","../src/useGanttChart.ts"],"sourcesContent":["export { GanttChart, default } from \"./GanttChart\";\nexport type { GanttChartProps, GanttChartHandle } from \"./GanttChart\";\n\nexport { useGanttChart } from \"./useGanttChart\";\nexport type { UseGanttChartResult } from \"./useGanttChart\";\n\n// Re-exported so consumers never need to add @ganttloom/gantt-core as a direct\n// dependency just to reference its types.\nexport type {\n ViewMode,\n DependencyType,\n GanttDependency,\n GanttAssignee,\n GanttTask,\n GanttColumn,\n GanttTheme,\n GanttRenderRow,\n GanttRenderBar,\n GanttRenderLink,\n GanttRenderTick,\n GanttRenderModel,\n GanttOptions,\n GanttEventMap,\n WorkingCalendar,\n ConstraintType,\n ResourceHistogramOptions,\n ResourceLoadBucket,\n GanttMarker,\n GanttRenderMarker,\n FilterTasksOptions,\n FilterTasksResult,\n Density,\n ResourceLevelingOptions,\n ResourceLevelingResult,\n} from \"@ganttloom/gantt-core\";\n\nexport {\n computeWBSCodes,\n applyConstraint,\n isWorkingDay,\n isHoliday,\n isWithinWorkingHours,\n isWorkingTime,\n nextWorkingDay,\n previousWorkingDay,\n shiftToWorkingDay,\n shiftToWorkingTime,\n computeResourceHistogram,\n renderResourceHistogramSVG,\n filterTasks,\n colorByField,\n applyColorByField,\n DEFAULT_PALETTE,\n DENSITY_PRESETS,\n levelResources,\n} from \"@ganttloom/gantt-core\";\n","// Pulls in the core's default styles so consumers get a working chart out of the box.\n// This resolves through gantt-core's package.json \"exports\" map (\"./styles.css\" -> \"./dist/styles.css\").\n// If your bundler can't resolve CSS imports from a dependency's export map, import it yourself instead:\n// import \"@ganttloom/gantt-core/styles.css\";\nimport \"@ganttloom/gantt-core/styles.css\";\n\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n type CSSProperties,\n type Ref,\n} from \"react\";\nimport type {\n GanttChart as GanttCore,\n GanttColumn,\n GanttDependency,\n GanttMarker,\n GanttOptions,\n GanttRenderModel,\n GanttTask,\n GanttTheme,\n ResourceHistogramOptions,\n ResourceLoadBucket,\n ResourceLevelingOptions,\n ResourceLevelingResult,\n ViewMode,\n WorkingCalendar,\n} from \"@ganttloom/gantt-core\";\nimport { useGanttChart } from \"./useGanttChart\";\n\nexport interface GanttChartProps {\n tasks: GanttTask[];\n dependencies?: GanttDependency[];\n\n // Mirrors of GanttOptions' non-callback fields.\n viewMode?: ViewMode;\n columns?: GanttColumn[];\n theme?: Partial<GanttTheme>;\n colorScheme?: \"light\" | \"dark\" | \"auto\";\n columnWidth?: number;\n readonly?: boolean;\n showProgress?: boolean;\n showDependencies?: boolean;\n gridPanelWidth?: number;\n showCriticalPath?: boolean;\n showBaseline?: boolean;\n showDeadlines?: boolean;\n /** hover tooltip on task bars, defaults to true. A floating, CSS-customizable element (`.gantt-tooltip`) - not the browser's native title tooltip. */\n showTooltip?: boolean;\n /** fully control tooltip content/markup; return null/undefined to suppress it for a specific task. Defaults to name/dates/progress/assignees/notes. */\n renderTooltip?: (task: GanttTask) => HTMLElement | string | null | undefined;\n showAssigneeAvatars?: boolean;\n enableHistory?: boolean;\n keyboardAccessible?: boolean;\n virtualScroll?: boolean;\n autoSchedule?: boolean;\n pagination?: { pageSize: number; page: number };\n snapToUnit?: \"hour\" | \"day\" | \"week\" | false;\n /** working-day/holiday definition used to shade non-working time and (with autoSchedule) skip it when cascading */\n calendar?: WorkingCalendar;\n /** arbitrary labeled vertical lines on the timeline, independent of any task */\n markers?: GanttMarker[];\n /** a group task with no explicit `progress` gets a duration-weighted average of its children's progress instead of 0 */\n autoRollupProgress?: boolean;\n /** enables click/ctrl-click/shift-click multi-selection of task bars, and the handle's bulkShiftDates()/bulkDelete() */\n selectable?: boolean;\n /**\n * Stretch (never shrink) the timeline to fill the container's width instead of leaving empty\n * space - the common \"short project at month/year zoom\" or \"just a few tasks\" case. Off by\n * default (a new chart's pixel layout shouldn't change for existing consumers who don't opt in).\n */\n autoFitToViewport?: boolean;\n\n // Mirrors of GanttOptions' callback fields, as normal React event props.\n onDateChange?: (task: GanttTask, start: Date, end: Date) => void;\n onProgressChange?: (task: GanttTask, progress: number) => void;\n onDependencyCreate?: (dep: GanttDependency) => void;\n onDependencyRemove?: (dep: GanttDependency) => void;\n /** called on double-click of a dependency link, for building your own \"edit this link\" UI (see the handle's updateDependency) */\n onDependencyDblClick?: (dep: GanttDependency) => void;\n onTaskClick?: (task: GanttTask) => void;\n /** called on double-click of a task bar (distinct from onDependencyDblClick, which is for links) */\n onTaskDblClick?: (task: GanttTask) => void;\n onGroupToggle?: (task: GanttTask, collapsed: boolean) => void;\n onContextMenu?: (task: GanttTask, evt: PointerEvent) => void;\n /** called when a task is created via drag-to-create on an empty timeline row */\n onTaskCreate?: (task: GanttTask) => void;\n /** called when a grid column is resized by dragging its header's edge */\n onColumnResize?: (columnId: string, width: number) => void;\n /** called when grid columns are reordered by dragging a header; `order` is the new full list of column ids */\n onColumnReorder?: (order: string[]) => void;\n /** called when a grid row is dragged onto another; \"inside\" reparents the dragged task under the target */\n onTaskReorder?: (draggedTaskId: string, targetTaskId: string, position: \"before\" | \"after\" | \"inside\") => void;\n onSelectionChange?: (taskIds: string[]) => void;\n /** fired whenever the sort state changes, via a sortable column header click or the handle's setSort() */\n onSortChange?: (sort: { columnId: string; direction: \"asc\" | \"desc\" } | null) => void;\n\n /** Applied to the wrapping container div. */\n className?: string;\n style?: CSSProperties;\n}\n\n/** Imperative escape hatches that don't need to be reactive props. */\nexport interface GanttChartHandle {\n undo(): void;\n redo(): void;\n canUndo(): boolean;\n canRedo(): boolean;\n expandAll(): void;\n collapseAll(): void;\n toggleGroup(taskId: string): void;\n removeDependency(fromId: string, toId: string): void;\n updateDependency(fromId: string, toId: string, partial: Partial<GanttDependency>): void;\n updateTask(id: string, partial: Partial<GanttTask>): void;\n setViewMode(mode: ViewMode): void;\n fitToViewport(containerWidthPx: number): void;\n scrollToToday(): void;\n scrollToRangeStart(): void;\n scrollToRangeEnd(): void;\n getPageCount(): number;\n setPage(page: number): void;\n zoomIn(factor?: number): void;\n zoomOut(factor?: number): void;\n getZoomLevel(): number;\n getResourceHistogram(options?: ResourceHistogramOptions): ResourceLoadBucket[];\n getLeveledTasks(options?: ResourceLevelingOptions): ResourceLevelingResult;\n selectTask(id: string, options?: { additive?: boolean }): void;\n selectAllVisible(): void;\n clearSelection(): void;\n getSelectedTaskIds(): string[];\n setSort(columnId: string | null, direction?: \"asc\" | \"desc\" | null): void;\n getSort(): { columnId: string; direction: \"asc\" | \"desc\" } | null;\n bulkShiftDates(deltaMs: number): void;\n bulkDelete(): void;\n toSVGString(): string;\n toPNGDataURL(): Promise<string>;\n toPNGDataURLs(options?: { pageWidthPx?: number; pageHeightPx?: number }): Promise<string[]>;\n getRenderModel(): GanttRenderModel | null;\n /** Direct access to the underlying framework-agnostic core instance (null before mount). */\n getInstance(): GanttCore | null;\n}\n\nconst EMPTY_DEPENDENCIES: GanttDependency[] = [];\n\nconst EMPTY_RENDER_MODEL: GanttRenderModel = {\n width: 0,\n height: 0,\n rowHeight: 0,\n headerHeight: 0,\n rows: [],\n bars: [],\n links: [],\n ticks: [],\n columns: [],\n theme: {} as GanttTheme,\n markers: [],\n rangeStart: new Date(0),\n sort: null,\n};\n\nfunction GanttChartInner(props: GanttChartProps, ref: Ref<GanttChartHandle>) {\n const {\n tasks,\n dependencies = EMPTY_DEPENDENCIES,\n className,\n style,\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onTaskDblClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n onSortChange,\n ...rest\n } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // The core only wires up its internal contextmenu DOM listener (and therefore only\n // ever emits the \"context-menu\" event) when options.onContextMenu is truthy AT\n // CONSTRUCTION TIME (see GanttChart#setupDom in gantt-core). We pass a stable no-op\n // here purely to force that wiring on regardless of whether the consumer supplied\n // onContextMenu, and dispatch the actual prop via the \"context-menu\" event\n // subscription below (so it stays reactive to prop changes and is never double-fired).\n const enableContextMenuWiring = useRef(() => {});\n\n // Same construction-time-only caveat as onContextMenu above: drag-to-create is only\n // wired into the core's InteractionController when options.onTaskCreate is truthy at\n // construction. A stable no-op forces it on whenever this component was given an\n // onTaskCreate prop at all; the real dispatch goes through the \"task-create\" event.\n const enableCreateWiring = useRef(() => {});\n\n // Same construction-time-only caveat: column resize/reorder DOM listeners are only\n // built into the grid header when these options are truthy at construction.\n const enableColumnResizeWiring = useRef((_columnId: string, _width: number) => {});\n const enableColumnReorderWiring = useRef((_order: string[]) => {});\n const enableTaskReorderWiring = useRef(\n (_draggedTaskId: string, _targetTaskId: string, _position: \"before\" | \"after\" | \"inside\") => {}\n );\n const enableDependencyDblClickWiring = useRef((_dep: GanttDependency) => {});\n const enableTaskDblClickWiring = useRef((_task: GanttTask) => {});\n\n // Deliberately depend on Boolean(handler) rather than the handler itself below: the\n // ref-backed enableXWiring callbacks already pick up the latest handler without\n // needing the memo to re-run, so depending on the raw (often inline) callback would\n // just re-create `options` — and remount the chart — every render.\n /* eslint-disable react-hooks/exhaustive-deps */\n const options = useMemo<GanttOptions>(\n () => ({\n ...rest,\n onContextMenu: enableContextMenuWiring.current,\n onTaskCreate: onTaskCreate ? enableCreateWiring.current : undefined,\n onColumnResize: onColumnResize ? enableColumnResizeWiring.current : undefined,\n onColumnReorder: onColumnReorder ? enableColumnReorderWiring.current : undefined,\n onTaskReorder: onTaskReorder ? enableTaskReorderWiring.current : undefined,\n onDependencyDblClick: onDependencyDblClick ? enableDependencyDblClickWiring.current : undefined,\n onTaskDblClick: onTaskDblClick ? enableTaskDblClickWiring.current : undefined,\n // onSelectionChange/onSortChange are deliberately NOT set here: gantt-core invokes them\n // directly from this.options AND emits a matching event at the same call sites, and the\n // effect below already subscribes to those events — including them here would double-fire.\n }),\n [\n rest.viewMode,\n rest.columns,\n rest.theme,\n rest.colorScheme,\n rest.columnWidth,\n rest.readonly,\n rest.showProgress,\n rest.showDependencies,\n rest.gridPanelWidth,\n rest.showCriticalPath,\n rest.showBaseline,\n rest.showDeadlines,\n rest.showTooltip,\n rest.renderTooltip,\n rest.autoFitToViewport,\n rest.showAssigneeAvatars,\n rest.enableHistory,\n rest.keyboardAccessible,\n rest.virtualScroll,\n rest.autoSchedule,\n rest.pagination,\n rest.snapToUnit,\n rest.calendar,\n rest.markers,\n rest.autoRollupProgress,\n rest.selectable,\n Boolean(onTaskCreate),\n Boolean(onColumnResize),\n Boolean(onColumnReorder),\n Boolean(onTaskReorder),\n Boolean(onDependencyDblClick),\n Boolean(onTaskDblClick),\n ]\n );\n /* eslint-enable react-hooks/exhaustive-deps */\n\n const { chart } = useGanttChart(containerRef, tasks, dependencies, options);\n\n // Keep the latest callback props in a ref so the event-subscription effect below\n // doesn't need to re-subscribe (tearing down/rebuilding listeners) on every render.\n const callbacksRef = useRef({\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onTaskDblClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n onSortChange,\n });\n callbacksRef.current = {\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onTaskDblClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n onSortChange,\n };\n\n useEffect(() => {\n if (!chart) return undefined;\n\n const handleDateChange = (p: { task: GanttTask; start: Date; end: Date }) =>\n callbacksRef.current.onDateChange?.(p.task, p.start, p.end);\n const handleProgressChange = (p: { task: GanttTask; progress: number }) =>\n callbacksRef.current.onProgressChange?.(p.task, p.progress);\n const handleDependencyCreate = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyCreate?.(dep);\n const handleDependencyRemove = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyRemove?.(dep);\n const handleTaskClick = (p: { task: GanttTask }) =>\n callbacksRef.current.onTaskClick?.(p.task);\n const handleTaskDblClick = (p: { task: GanttTask }) =>\n callbacksRef.current.onTaskDblClick?.(p.task);\n const handleGroupToggle = (p: { task: GanttTask; collapsed: boolean }) =>\n callbacksRef.current.onGroupToggle?.(p.task, p.collapsed);\n const handleContextMenu = (p: { task: GanttTask; evt: PointerEvent }) =>\n callbacksRef.current.onContextMenu?.(p.task, p.evt);\n const handleTaskCreate = (p: { task: GanttTask }) => callbacksRef.current.onTaskCreate?.(p.task);\n const handleColumnResize = (p: { columnId: string; width: number }) =>\n callbacksRef.current.onColumnResize?.(p.columnId, p.width);\n const handleColumnReorder = (p: { order: string[] }) =>\n callbacksRef.current.onColumnReorder?.(p.order);\n const handleDependencyDblClick = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyDblClick?.(dep);\n const handleTaskReorder = (p: {\n draggedTaskId: string;\n targetTaskId: string;\n position: \"before\" | \"after\" | \"inside\";\n }) => callbacksRef.current.onTaskReorder?.(p.draggedTaskId, p.targetTaskId, p.position);\n const handleSelectionChange = (p: { taskIds: string[] }) =>\n callbacksRef.current.onSelectionChange?.(p.taskIds);\n const handleSortChange = (p: { sort: { columnId: string; direction: \"asc\" | \"desc\" } | null }) =>\n callbacksRef.current.onSortChange?.(p.sort);\n\n chart.on(\"date-change\", handleDateChange);\n chart.on(\"progress-change\", handleProgressChange);\n chart.on(\"dependency-create\", handleDependencyCreate);\n chart.on(\"dependency-remove\", handleDependencyRemove);\n chart.on(\"dependency-dblclick\", handleDependencyDblClick);\n chart.on(\"task-click\", handleTaskClick);\n chart.on(\"task-dblclick\", handleTaskDblClick);\n chart.on(\"group-toggle\", handleGroupToggle);\n chart.on(\"context-menu\", handleContextMenu);\n chart.on(\"task-create\", handleTaskCreate);\n chart.on(\"column-resize\", handleColumnResize);\n chart.on(\"column-reorder\", handleColumnReorder);\n chart.on(\"task-reorder\", handleTaskReorder);\n chart.on(\"selection-change\", handleSelectionChange);\n chart.on(\"sort-change\", handleSortChange);\n\n return () => {\n chart.off(\"date-change\", handleDateChange);\n chart.off(\"progress-change\", handleProgressChange);\n chart.off(\"dependency-create\", handleDependencyCreate);\n chart.off(\"dependency-remove\", handleDependencyRemove);\n chart.off(\"dependency-dblclick\", handleDependencyDblClick);\n chart.off(\"task-click\", handleTaskClick);\n chart.off(\"task-dblclick\", handleTaskDblClick);\n chart.off(\"group-toggle\", handleGroupToggle);\n chart.off(\"context-menu\", handleContextMenu);\n chart.off(\"task-create\", handleTaskCreate);\n chart.off(\"column-resize\", handleColumnResize);\n chart.off(\"column-reorder\", handleColumnReorder);\n chart.off(\"task-reorder\", handleTaskReorder);\n chart.off(\"selection-change\", handleSelectionChange);\n chart.off(\"sort-change\", handleSortChange);\n };\n }, [chart]);\n\n useImperativeHandle(\n ref,\n (): GanttChartHandle => ({\n undo: () => chart?.undo(),\n redo: () => chart?.redo(),\n canUndo: () => chart?.canUndo ?? false,\n canRedo: () => chart?.canRedo ?? false,\n expandAll: () => chart?.expandAll(),\n collapseAll: () => chart?.collapseAll(),\n toggleGroup: (taskId: string) => chart?.toggleGroup(taskId),\n removeDependency: (fromId: string, toId: string) => chart?.removeDependency(fromId, toId),\n updateDependency: (fromId: string, toId: string, partial: Partial<GanttDependency>) =>\n chart?.updateDependency(fromId, toId, partial),\n updateTask: (id: string, partial: Partial<GanttTask>) => chart?.updateTask(id, partial),\n setViewMode: (mode: ViewMode) => chart?.setViewMode(mode),\n fitToViewport: (containerWidthPx: number) => chart?.fitToViewport(containerWidthPx),\n scrollToToday: () => chart?.scrollToToday(),\n scrollToRangeStart: () => chart?.scrollToRangeStart(),\n scrollToRangeEnd: () => chart?.scrollToRangeEnd(),\n getPageCount: () => chart?.getPageCount() ?? 1,\n setPage: (page: number) => chart?.setPage(page),\n zoomIn: (factor?: number) => chart?.zoomIn(factor),\n zoomOut: (factor?: number) => chart?.zoomOut(factor),\n getZoomLevel: () => chart?.getZoomLevel() ?? 0,\n getResourceHistogram: (options?: ResourceHistogramOptions) =>\n chart?.getResourceHistogram(options) ?? [],\n getLeveledTasks: (options?: ResourceLevelingOptions) =>\n chart?.getLeveledTasks(options) ?? { tasks: [], shifted: [] },\n selectTask: (id: string, options?: { additive?: boolean }) => chart?.selectTask(id, options),\n selectAllVisible: () => chart?.selectAllVisible(),\n clearSelection: () => chart?.clearSelection(),\n getSelectedTaskIds: () => chart?.getSelectedTaskIds() ?? [],\n setSort: (columnId: string | null, direction?: \"asc\" | \"desc\" | null) => chart?.setSort(columnId, direction),\n getSort: () => chart?.getSort() ?? null,\n bulkShiftDates: (deltaMs: number) => chart?.bulkShiftDates(deltaMs),\n bulkDelete: () => chart?.bulkDelete(),\n toSVGString: () => chart?.toSVGString() ?? \"\",\n toPNGDataURL: () => chart?.toPNGDataURL() ?? Promise.resolve(\"\"),\n toPNGDataURLs: (options?: { pageWidthPx?: number; pageHeightPx?: number }) =>\n chart?.toPNGDataURLs(options) ?? Promise.resolve([]),\n getRenderModel: () => chart?.getRenderModel() ?? EMPTY_RENDER_MODEL,\n getInstance: () => chart,\n }),\n [chart]\n );\n\n return <div ref={containerRef} className={className} style={style} />;\n}\n\nexport const GanttChart = forwardRef(GanttChartInner);\nGanttChart.displayName = \"GanttChart\";\n\nexport default GanttChart;\n","import { useEffect, useRef, useState, type RefObject } from \"react\";\nimport { GanttChart as GanttCore } from \"@ganttloom/gantt-core\";\nimport type { GanttDependency, GanttOptions, GanttTask } from \"@ganttloom/gantt-core\";\n\nexport interface UseGanttChartResult {\n /** The underlying framework-agnostic GanttChart instance, or null before the DOM mount effect runs (or on the server). */\n chart: GanttCore | null;\n /** Reactive, subscribed to the core's \"history-change\" event. Only meaningful when `enableHistory` is set in options. */\n canUndo: boolean;\n /** Reactive, subscribed to the core's \"history-change\" event. Only meaningful when `enableHistory` is set in options. */\n canRedo: boolean;\n}\n\nconst EMPTY_DEPENDENCIES: GanttDependency[] = [];\nconst EMPTY_OPTIONS: GanttOptions = {};\n\n/**\n * Lower-level escape hatch for consumers who want to own their own container element\n * and DOM tree instead of using the <GanttChart /> component. Constructs a\n * @ganttloom/gantt-core GanttChart instance against `containerRef.current` on mount,\n * keeps it in sync with `tasks` / `dependencies` / `options` via setTasks / setDependencies /\n * setOptions (reference-equality checks only — pass new arrays/objects to trigger a sync),\n * and destroys it on unmount.\n */\nexport function useGanttChart(\n containerRef: RefObject<HTMLElement | null>,\n tasks: GanttTask[],\n dependencies: GanttDependency[] = EMPTY_DEPENDENCIES,\n options: GanttOptions = EMPTY_OPTIONS\n): UseGanttChartResult {\n const [chart, setChart] = useState<GanttCore | null>(null);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n\n const chartRef = useRef<GanttCore | null>(null);\n\n // Latest props, read from the mount effect so it doesn't need to depend on them\n // (which would otherwise tear down and recreate the whole chart on every change).\n const initialTasksRef = useRef(tasks);\n initialTasksRef.current = tasks;\n const initialDepsRef = useRef(dependencies);\n initialDepsRef.current = dependencies;\n const initialOptionsRef = useRef(options);\n initialOptionsRef.current = options;\n\n useEffect(() => {\n if (typeof window === \"undefined\") return undefined;\n const container = containerRef.current;\n if (!container) return undefined;\n\n const instance = new GanttCore(\n container,\n initialTasksRef.current,\n initialDepsRef.current,\n initialOptionsRef.current\n );\n chartRef.current = instance;\n setChart(instance);\n setCanUndo(instance.canUndo);\n setCanRedo(instance.canRedo);\n\n const onHistoryChange = (state: { canUndo: boolean; canRedo: boolean }) => {\n setCanUndo(state.canUndo);\n setCanRedo(state.canRedo);\n };\n instance.on(\"history-change\", onHistoryChange);\n\n return () => {\n instance.off(\"history-change\", onHistoryChange);\n instance.destroy();\n chartRef.current = null;\n setChart(null);\n setCanUndo(false);\n setCanRedo(false);\n };\n // Intentionally mount/unmount only — the container element identity is what matters.\n }, [containerRef]);\n\n const isFirstTasksSync = useRef(true);\n useEffect(() => {\n if (isFirstTasksSync.current) {\n isFirstTasksSync.current = false;\n return;\n }\n chartRef.current?.setTasks(tasks);\n }, [tasks]);\n\n const isFirstDepsSync = useRef(true);\n useEffect(() => {\n if (isFirstDepsSync.current) {\n isFirstDepsSync.current = false;\n return;\n }\n chartRef.current?.setDependencies(dependencies);\n }, [dependencies]);\n\n const isFirstOptionsSync = useRef(true);\n useEffect(() => {\n if (isFirstOptionsSync.current) {\n isFirstOptionsSync.current = false;\n return;\n }\n chartRef.current?.setOptions(options);\n }, [options]);\n\n return { chart, canUndo, canRedo };\n}\n"],"mappings":"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","names":["import_react","GanttCore","EMPTY_DEPENDENCIES","options","import_gantt_core"]}
package/dist/index.d.cts CHANGED
@@ -18,6 +18,10 @@ interface GanttChartProps {
18
18
  showCriticalPath?: boolean;
19
19
  showBaseline?: boolean;
20
20
  showDeadlines?: boolean;
21
+ /** hover tooltip on task bars, defaults to true. A floating, CSS-customizable element (`.gantt-tooltip`) - not the browser's native title tooltip. */
22
+ showTooltip?: boolean;
23
+ /** fully control tooltip content/markup; return null/undefined to suppress it for a specific task. Defaults to name/dates/progress/assignees/notes. */
24
+ renderTooltip?: (task: GanttTask) => HTMLElement | string | null | undefined;
21
25
  showAssigneeAvatars?: boolean;
22
26
  enableHistory?: boolean;
23
27
  keyboardAccessible?: boolean;
@@ -36,6 +40,12 @@ interface GanttChartProps {
36
40
  autoRollupProgress?: boolean;
37
41
  /** enables click/ctrl-click/shift-click multi-selection of task bars, and the handle's bulkShiftDates()/bulkDelete() */
38
42
  selectable?: boolean;
43
+ /**
44
+ * Stretch (never shrink) the timeline to fill the container's width instead of leaving empty
45
+ * space - the common "short project at month/year zoom" or "just a few tasks" case. Off by
46
+ * default (a new chart's pixel layout shouldn't change for existing consumers who don't opt in).
47
+ */
48
+ autoFitToViewport?: boolean;
39
49
  onDateChange?: (task: GanttTask, start: Date, end: Date) => void;
40
50
  onProgressChange?: (task: GanttTask, progress: number) => void;
41
51
  onDependencyCreate?: (dep: GanttDependency) => void;
@@ -43,6 +53,8 @@ interface GanttChartProps {
43
53
  /** called on double-click of a dependency link, for building your own "edit this link" UI (see the handle's updateDependency) */
44
54
  onDependencyDblClick?: (dep: GanttDependency) => void;
45
55
  onTaskClick?: (task: GanttTask) => void;
56
+ /** called on double-click of a task bar (distinct from onDependencyDblClick, which is for links) */
57
+ onTaskDblClick?: (task: GanttTask) => void;
46
58
  onGroupToggle?: (task: GanttTask, collapsed: boolean) => void;
47
59
  onContextMenu?: (task: GanttTask, evt: PointerEvent) => void;
48
60
  /** called when a task is created via drag-to-create on an empty timeline row */
@@ -54,6 +66,11 @@ interface GanttChartProps {
54
66
  /** called when a grid row is dragged onto another; "inside" reparents the dragged task under the target */
55
67
  onTaskReorder?: (draggedTaskId: string, targetTaskId: string, position: "before" | "after" | "inside") => void;
56
68
  onSelectionChange?: (taskIds: string[]) => void;
69
+ /** fired whenever the sort state changes, via a sortable column header click or the handle's setSort() */
70
+ onSortChange?: (sort: {
71
+ columnId: string;
72
+ direction: "asc" | "desc";
73
+ } | null) => void;
57
74
  /** Applied to the wrapping container div. */
58
75
  className?: string;
59
76
  style?: CSSProperties;
@@ -88,6 +105,11 @@ interface GanttChartHandle {
88
105
  selectAllVisible(): void;
89
106
  clearSelection(): void;
90
107
  getSelectedTaskIds(): string[];
108
+ setSort(columnId: string | null, direction?: "asc" | "desc" | null): void;
109
+ getSort(): {
110
+ columnId: string;
111
+ direction: "asc" | "desc";
112
+ } | null;
91
113
  bulkShiftDates(deltaMs: number): void;
92
114
  bulkDelete(): void;
93
115
  toSVGString(): string;
package/dist/index.d.ts CHANGED
@@ -18,6 +18,10 @@ interface GanttChartProps {
18
18
  showCriticalPath?: boolean;
19
19
  showBaseline?: boolean;
20
20
  showDeadlines?: boolean;
21
+ /** hover tooltip on task bars, defaults to true. A floating, CSS-customizable element (`.gantt-tooltip`) - not the browser's native title tooltip. */
22
+ showTooltip?: boolean;
23
+ /** fully control tooltip content/markup; return null/undefined to suppress it for a specific task. Defaults to name/dates/progress/assignees/notes. */
24
+ renderTooltip?: (task: GanttTask) => HTMLElement | string | null | undefined;
21
25
  showAssigneeAvatars?: boolean;
22
26
  enableHistory?: boolean;
23
27
  keyboardAccessible?: boolean;
@@ -36,6 +40,12 @@ interface GanttChartProps {
36
40
  autoRollupProgress?: boolean;
37
41
  /** enables click/ctrl-click/shift-click multi-selection of task bars, and the handle's bulkShiftDates()/bulkDelete() */
38
42
  selectable?: boolean;
43
+ /**
44
+ * Stretch (never shrink) the timeline to fill the container's width instead of leaving empty
45
+ * space - the common "short project at month/year zoom" or "just a few tasks" case. Off by
46
+ * default (a new chart's pixel layout shouldn't change for existing consumers who don't opt in).
47
+ */
48
+ autoFitToViewport?: boolean;
39
49
  onDateChange?: (task: GanttTask, start: Date, end: Date) => void;
40
50
  onProgressChange?: (task: GanttTask, progress: number) => void;
41
51
  onDependencyCreate?: (dep: GanttDependency) => void;
@@ -43,6 +53,8 @@ interface GanttChartProps {
43
53
  /** called on double-click of a dependency link, for building your own "edit this link" UI (see the handle's updateDependency) */
44
54
  onDependencyDblClick?: (dep: GanttDependency) => void;
45
55
  onTaskClick?: (task: GanttTask) => void;
56
+ /** called on double-click of a task bar (distinct from onDependencyDblClick, which is for links) */
57
+ onTaskDblClick?: (task: GanttTask) => void;
46
58
  onGroupToggle?: (task: GanttTask, collapsed: boolean) => void;
47
59
  onContextMenu?: (task: GanttTask, evt: PointerEvent) => void;
48
60
  /** called when a task is created via drag-to-create on an empty timeline row */
@@ -54,6 +66,11 @@ interface GanttChartProps {
54
66
  /** called when a grid row is dragged onto another; "inside" reparents the dragged task under the target */
55
67
  onTaskReorder?: (draggedTaskId: string, targetTaskId: string, position: "before" | "after" | "inside") => void;
56
68
  onSelectionChange?: (taskIds: string[]) => void;
69
+ /** fired whenever the sort state changes, via a sortable column header click or the handle's setSort() */
70
+ onSortChange?: (sort: {
71
+ columnId: string;
72
+ direction: "asc" | "desc";
73
+ } | null) => void;
57
74
  /** Applied to the wrapping container div. */
58
75
  className?: string;
59
76
  style?: CSSProperties;
@@ -88,6 +105,11 @@ interface GanttChartHandle {
88
105
  selectAllVisible(): void;
89
106
  clearSelection(): void;
90
107
  getSelectedTaskIds(): string[];
108
+ setSort(columnId: string | null, direction?: "asc" | "desc" | null): void;
109
+ getSort(): {
110
+ columnId: string;
111
+ direction: "asc" | "desc";
112
+ } | null;
91
113
  bulkShiftDates(deltaMs: number): void;
92
114
  bulkDelete(): void;
93
115
  toSVGString(): string;
package/dist/index.js CHANGED
@@ -94,7 +94,8 @@ var EMPTY_RENDER_MODEL = {
94
94
  columns: [],
95
95
  theme: {},
96
96
  markers: [],
97
- rangeStart: /* @__PURE__ */ new Date(0)
97
+ rangeStart: /* @__PURE__ */ new Date(0),
98
+ sort: null
98
99
  };
99
100
  function GanttChartInner(props, ref) {
100
101
  const {
@@ -108,6 +109,7 @@ function GanttChartInner(props, ref) {
108
109
  onDependencyRemove,
109
110
  onDependencyDblClick,
110
111
  onTaskClick,
112
+ onTaskDblClick,
111
113
  onGroupToggle,
112
114
  onContextMenu,
113
115
  onTaskCreate,
@@ -115,6 +117,7 @@ function GanttChartInner(props, ref) {
115
117
  onColumnReorder,
116
118
  onTaskReorder,
117
119
  onSelectionChange,
120
+ onSortChange,
118
121
  ...rest
119
122
  } = props;
120
123
  const containerRef = useRef2(null);
@@ -132,6 +135,8 @@ function GanttChartInner(props, ref) {
132
135
  );
133
136
  const enableDependencyDblClickWiring = useRef2((_dep) => {
134
137
  });
138
+ const enableTaskDblClickWiring = useRef2((_task) => {
139
+ });
135
140
  const options = useMemo(
136
141
  () => ({
137
142
  ...rest,
@@ -140,11 +145,11 @@ function GanttChartInner(props, ref) {
140
145
  onColumnResize: onColumnResize ? enableColumnResizeWiring.current : void 0,
141
146
  onColumnReorder: onColumnReorder ? enableColumnReorderWiring.current : void 0,
142
147
  onTaskReorder: onTaskReorder ? enableTaskReorderWiring.current : void 0,
143
- onDependencyDblClick: onDependencyDblClick ? enableDependencyDblClickWiring.current : void 0
144
- // onSelectionChange is deliberately NOT set here: gantt-core invokes it directly from
145
- // this.options AND emits a "selection-change" event at the same call sites, and the
146
- // effect below already subscribes to that event including it here would double-fire
147
- // every selection change.
148
+ onDependencyDblClick: onDependencyDblClick ? enableDependencyDblClickWiring.current : void 0,
149
+ onTaskDblClick: onTaskDblClick ? enableTaskDblClickWiring.current : void 0
150
+ // onSelectionChange/onSortChange are deliberately NOT set here: gantt-core invokes them
151
+ // directly from this.options AND emits a matching event at the same call sites, and the
152
+ // effect below already subscribes to those events — including them here would double-fire.
148
153
  }),
149
154
  [
150
155
  rest.viewMode,
@@ -159,6 +164,9 @@ function GanttChartInner(props, ref) {
159
164
  rest.showCriticalPath,
160
165
  rest.showBaseline,
161
166
  rest.showDeadlines,
167
+ rest.showTooltip,
168
+ rest.renderTooltip,
169
+ rest.autoFitToViewport,
162
170
  rest.showAssigneeAvatars,
163
171
  rest.enableHistory,
164
172
  rest.keyboardAccessible,
@@ -174,7 +182,8 @@ function GanttChartInner(props, ref) {
174
182
  Boolean(onColumnResize),
175
183
  Boolean(onColumnReorder),
176
184
  Boolean(onTaskReorder),
177
- Boolean(onDependencyDblClick)
185
+ Boolean(onDependencyDblClick),
186
+ Boolean(onTaskDblClick)
178
187
  ]
179
188
  );
180
189
  const { chart } = useGanttChart(containerRef, tasks, dependencies, options);
@@ -185,13 +194,15 @@ function GanttChartInner(props, ref) {
185
194
  onDependencyRemove,
186
195
  onDependencyDblClick,
187
196
  onTaskClick,
197
+ onTaskDblClick,
188
198
  onGroupToggle,
189
199
  onContextMenu,
190
200
  onTaskCreate,
191
201
  onColumnResize,
192
202
  onColumnReorder,
193
203
  onTaskReorder,
194
- onSelectionChange
204
+ onSelectionChange,
205
+ onSortChange
195
206
  });
196
207
  callbacksRef.current = {
197
208
  onDateChange,
@@ -200,13 +211,15 @@ function GanttChartInner(props, ref) {
200
211
  onDependencyRemove,
201
212
  onDependencyDblClick,
202
213
  onTaskClick,
214
+ onTaskDblClick,
203
215
  onGroupToggle,
204
216
  onContextMenu,
205
217
  onTaskCreate,
206
218
  onColumnResize,
207
219
  onColumnReorder,
208
220
  onTaskReorder,
209
- onSelectionChange
221
+ onSelectionChange,
222
+ onSortChange
210
223
  };
211
224
  useEffect2(() => {
212
225
  if (!chart) return void 0;
@@ -215,6 +228,7 @@ function GanttChartInner(props, ref) {
215
228
  const handleDependencyCreate = (dep) => callbacksRef.current.onDependencyCreate?.(dep);
216
229
  const handleDependencyRemove = (dep) => callbacksRef.current.onDependencyRemove?.(dep);
217
230
  const handleTaskClick = (p) => callbacksRef.current.onTaskClick?.(p.task);
231
+ const handleTaskDblClick = (p) => callbacksRef.current.onTaskDblClick?.(p.task);
218
232
  const handleGroupToggle = (p) => callbacksRef.current.onGroupToggle?.(p.task, p.collapsed);
219
233
  const handleContextMenu = (p) => callbacksRef.current.onContextMenu?.(p.task, p.evt);
220
234
  const handleTaskCreate = (p) => callbacksRef.current.onTaskCreate?.(p.task);
@@ -223,12 +237,14 @@ function GanttChartInner(props, ref) {
223
237
  const handleDependencyDblClick = (dep) => callbacksRef.current.onDependencyDblClick?.(dep);
224
238
  const handleTaskReorder = (p) => callbacksRef.current.onTaskReorder?.(p.draggedTaskId, p.targetTaskId, p.position);
225
239
  const handleSelectionChange = (p) => callbacksRef.current.onSelectionChange?.(p.taskIds);
240
+ const handleSortChange = (p) => callbacksRef.current.onSortChange?.(p.sort);
226
241
  chart.on("date-change", handleDateChange);
227
242
  chart.on("progress-change", handleProgressChange);
228
243
  chart.on("dependency-create", handleDependencyCreate);
229
244
  chart.on("dependency-remove", handleDependencyRemove);
230
245
  chart.on("dependency-dblclick", handleDependencyDblClick);
231
246
  chart.on("task-click", handleTaskClick);
247
+ chart.on("task-dblclick", handleTaskDblClick);
232
248
  chart.on("group-toggle", handleGroupToggle);
233
249
  chart.on("context-menu", handleContextMenu);
234
250
  chart.on("task-create", handleTaskCreate);
@@ -236,6 +252,7 @@ function GanttChartInner(props, ref) {
236
252
  chart.on("column-reorder", handleColumnReorder);
237
253
  chart.on("task-reorder", handleTaskReorder);
238
254
  chart.on("selection-change", handleSelectionChange);
255
+ chart.on("sort-change", handleSortChange);
239
256
  return () => {
240
257
  chart.off("date-change", handleDateChange);
241
258
  chart.off("progress-change", handleProgressChange);
@@ -243,6 +260,7 @@ function GanttChartInner(props, ref) {
243
260
  chart.off("dependency-remove", handleDependencyRemove);
244
261
  chart.off("dependency-dblclick", handleDependencyDblClick);
245
262
  chart.off("task-click", handleTaskClick);
263
+ chart.off("task-dblclick", handleTaskDblClick);
246
264
  chart.off("group-toggle", handleGroupToggle);
247
265
  chart.off("context-menu", handleContextMenu);
248
266
  chart.off("task-create", handleTaskCreate);
@@ -250,6 +268,7 @@ function GanttChartInner(props, ref) {
250
268
  chart.off("column-reorder", handleColumnReorder);
251
269
  chart.off("task-reorder", handleTaskReorder);
252
270
  chart.off("selection-change", handleSelectionChange);
271
+ chart.off("sort-change", handleSortChange);
253
272
  };
254
273
  }, [chart]);
255
274
  useImperativeHandle(
@@ -281,6 +300,8 @@ function GanttChartInner(props, ref) {
281
300
  selectAllVisible: () => chart?.selectAllVisible(),
282
301
  clearSelection: () => chart?.clearSelection(),
283
302
  getSelectedTaskIds: () => chart?.getSelectedTaskIds() ?? [],
303
+ setSort: (columnId, direction) => chart?.setSort(columnId, direction),
304
+ getSort: () => chart?.getSort() ?? null,
284
305
  bulkShiftDates: (deltaMs) => chart?.bulkShiftDates(deltaMs),
285
306
  bulkDelete: () => chart?.bulkDelete(),
286
307
  toSVGString: () => chart?.toSVGString() ?? "",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/GanttChart.tsx","../src/useGanttChart.ts","../src/index.ts"],"sourcesContent":["// Pulls in the core's default styles so consumers get a working chart out of the box.\n// This resolves through gantt-core's package.json \"exports\" map (\"./styles.css\" -> \"./dist/styles.css\").\n// If your bundler can't resolve CSS imports from a dependency's export map, import it yourself instead:\n// import \"@ganttloom/gantt-core/styles.css\";\nimport \"@ganttloom/gantt-core/styles.css\";\n\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n type CSSProperties,\n type Ref,\n} from \"react\";\nimport type {\n GanttChart as GanttCore,\n GanttColumn,\n GanttDependency,\n GanttMarker,\n GanttOptions,\n GanttRenderModel,\n GanttTask,\n GanttTheme,\n ResourceHistogramOptions,\n ResourceLoadBucket,\n ResourceLevelingOptions,\n ResourceLevelingResult,\n ViewMode,\n WorkingCalendar,\n} from \"@ganttloom/gantt-core\";\nimport { useGanttChart } from \"./useGanttChart\";\n\nexport interface GanttChartProps {\n tasks: GanttTask[];\n dependencies?: GanttDependency[];\n\n // Mirrors of GanttOptions' non-callback fields.\n viewMode?: ViewMode;\n columns?: GanttColumn[];\n theme?: Partial<GanttTheme>;\n colorScheme?: \"light\" | \"dark\" | \"auto\";\n columnWidth?: number;\n readonly?: boolean;\n showProgress?: boolean;\n showDependencies?: boolean;\n gridPanelWidth?: number;\n showCriticalPath?: boolean;\n showBaseline?: boolean;\n showDeadlines?: boolean;\n showAssigneeAvatars?: boolean;\n enableHistory?: boolean;\n keyboardAccessible?: boolean;\n virtualScroll?: boolean;\n autoSchedule?: boolean;\n pagination?: { pageSize: number; page: number };\n snapToUnit?: \"hour\" | \"day\" | \"week\" | false;\n /** working-day/holiday definition used to shade non-working time and (with autoSchedule) skip it when cascading */\n calendar?: WorkingCalendar;\n /** arbitrary labeled vertical lines on the timeline, independent of any task */\n markers?: GanttMarker[];\n /** a group task with no explicit `progress` gets a duration-weighted average of its children's progress instead of 0 */\n autoRollupProgress?: boolean;\n /** enables click/ctrl-click/shift-click multi-selection of task bars, and the handle's bulkShiftDates()/bulkDelete() */\n selectable?: boolean;\n\n // Mirrors of GanttOptions' callback fields, as normal React event props.\n onDateChange?: (task: GanttTask, start: Date, end: Date) => void;\n onProgressChange?: (task: GanttTask, progress: number) => void;\n onDependencyCreate?: (dep: GanttDependency) => void;\n onDependencyRemove?: (dep: GanttDependency) => void;\n /** called on double-click of a dependency link, for building your own \"edit this link\" UI (see the handle's updateDependency) */\n onDependencyDblClick?: (dep: GanttDependency) => void;\n onTaskClick?: (task: GanttTask) => void;\n onGroupToggle?: (task: GanttTask, collapsed: boolean) => void;\n onContextMenu?: (task: GanttTask, evt: PointerEvent) => void;\n /** called when a task is created via drag-to-create on an empty timeline row */\n onTaskCreate?: (task: GanttTask) => void;\n /** called when a grid column is resized by dragging its header's edge */\n onColumnResize?: (columnId: string, width: number) => void;\n /** called when grid columns are reordered by dragging a header; `order` is the new full list of column ids */\n onColumnReorder?: (order: string[]) => void;\n /** called when a grid row is dragged onto another; \"inside\" reparents the dragged task under the target */\n onTaskReorder?: (draggedTaskId: string, targetTaskId: string, position: \"before\" | \"after\" | \"inside\") => void;\n onSelectionChange?: (taskIds: string[]) => void;\n\n /** Applied to the wrapping container div. */\n className?: string;\n style?: CSSProperties;\n}\n\n/** Imperative escape hatches that don't need to be reactive props. */\nexport interface GanttChartHandle {\n undo(): void;\n redo(): void;\n canUndo(): boolean;\n canRedo(): boolean;\n expandAll(): void;\n collapseAll(): void;\n toggleGroup(taskId: string): void;\n removeDependency(fromId: string, toId: string): void;\n updateDependency(fromId: string, toId: string, partial: Partial<GanttDependency>): void;\n updateTask(id: string, partial: Partial<GanttTask>): void;\n setViewMode(mode: ViewMode): void;\n fitToViewport(containerWidthPx: number): void;\n scrollToToday(): void;\n scrollToRangeStart(): void;\n scrollToRangeEnd(): void;\n getPageCount(): number;\n setPage(page: number): void;\n zoomIn(factor?: number): void;\n zoomOut(factor?: number): void;\n getZoomLevel(): number;\n getResourceHistogram(options?: ResourceHistogramOptions): ResourceLoadBucket[];\n getLeveledTasks(options?: ResourceLevelingOptions): ResourceLevelingResult;\n selectTask(id: string, options?: { additive?: boolean }): void;\n selectAllVisible(): void;\n clearSelection(): void;\n getSelectedTaskIds(): string[];\n bulkShiftDates(deltaMs: number): void;\n bulkDelete(): void;\n toSVGString(): string;\n toPNGDataURL(): Promise<string>;\n toPNGDataURLs(options?: { pageWidthPx?: number; pageHeightPx?: number }): Promise<string[]>;\n getRenderModel(): GanttRenderModel | null;\n /** Direct access to the underlying framework-agnostic core instance (null before mount). */\n getInstance(): GanttCore | null;\n}\n\nconst EMPTY_DEPENDENCIES: GanttDependency[] = [];\n\nconst EMPTY_RENDER_MODEL: GanttRenderModel = {\n width: 0,\n height: 0,\n rowHeight: 0,\n headerHeight: 0,\n rows: [],\n bars: [],\n links: [],\n ticks: [],\n columns: [],\n theme: {} as GanttTheme,\n markers: [],\n rangeStart: new Date(0),\n};\n\nfunction GanttChartInner(props: GanttChartProps, ref: Ref<GanttChartHandle>) {\n const {\n tasks,\n dependencies = EMPTY_DEPENDENCIES,\n className,\n style,\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n ...rest\n } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // The core only wires up its internal contextmenu DOM listener (and therefore only\n // ever emits the \"context-menu\" event) when options.onContextMenu is truthy AT\n // CONSTRUCTION TIME (see GanttChart#setupDom in gantt-core). We pass a stable no-op\n // here purely to force that wiring on regardless of whether the consumer supplied\n // onContextMenu, and dispatch the actual prop via the \"context-menu\" event\n // subscription below (so it stays reactive to prop changes and is never double-fired).\n const enableContextMenuWiring = useRef(() => {});\n\n // Same construction-time-only caveat as onContextMenu above: drag-to-create is only\n // wired into the core's InteractionController when options.onTaskCreate is truthy at\n // construction. A stable no-op forces it on whenever this component was given an\n // onTaskCreate prop at all; the real dispatch goes through the \"task-create\" event.\n const enableCreateWiring = useRef(() => {});\n\n // Same construction-time-only caveat: column resize/reorder DOM listeners are only\n // built into the grid header when these options are truthy at construction.\n const enableColumnResizeWiring = useRef((_columnId: string, _width: number) => {});\n const enableColumnReorderWiring = useRef((_order: string[]) => {});\n const enableTaskReorderWiring = useRef(\n (_draggedTaskId: string, _targetTaskId: string, _position: \"before\" | \"after\" | \"inside\") => {}\n );\n const enableDependencyDblClickWiring = useRef((_dep: GanttDependency) => {});\n\n // Deliberately depend on Boolean(handler) rather than the handler itself below: the\n // ref-backed enableXWiring callbacks already pick up the latest handler without\n // needing the memo to re-run, so depending on the raw (often inline) callback would\n // just re-create `options` — and remount the chart — every render.\n /* eslint-disable react-hooks/exhaustive-deps */\n const options = useMemo<GanttOptions>(\n () => ({\n ...rest,\n onContextMenu: enableContextMenuWiring.current,\n onTaskCreate: onTaskCreate ? enableCreateWiring.current : undefined,\n onColumnResize: onColumnResize ? enableColumnResizeWiring.current : undefined,\n onColumnReorder: onColumnReorder ? enableColumnReorderWiring.current : undefined,\n onTaskReorder: onTaskReorder ? enableTaskReorderWiring.current : undefined,\n onDependencyDblClick: onDependencyDblClick ? enableDependencyDblClickWiring.current : undefined,\n // onSelectionChange is deliberately NOT set here: gantt-core invokes it directly from\n // this.options AND emits a \"selection-change\" event at the same call sites, and the\n // effect below already subscribes to that event — including it here would double-fire\n // every selection change.\n }),\n [\n rest.viewMode,\n rest.columns,\n rest.theme,\n rest.colorScheme,\n rest.columnWidth,\n rest.readonly,\n rest.showProgress,\n rest.showDependencies,\n rest.gridPanelWidth,\n rest.showCriticalPath,\n rest.showBaseline,\n rest.showDeadlines,\n rest.showAssigneeAvatars,\n rest.enableHistory,\n rest.keyboardAccessible,\n rest.virtualScroll,\n rest.autoSchedule,\n rest.pagination,\n rest.snapToUnit,\n rest.calendar,\n rest.markers,\n rest.autoRollupProgress,\n rest.selectable,\n Boolean(onTaskCreate),\n Boolean(onColumnResize),\n Boolean(onColumnReorder),\n Boolean(onTaskReorder),\n Boolean(onDependencyDblClick),\n ]\n );\n /* eslint-enable react-hooks/exhaustive-deps */\n\n const { chart } = useGanttChart(containerRef, tasks, dependencies, options);\n\n // Keep the latest callback props in a ref so the event-subscription effect below\n // doesn't need to re-subscribe (tearing down/rebuilding listeners) on every render.\n const callbacksRef = useRef({\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n });\n callbacksRef.current = {\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n };\n\n useEffect(() => {\n if (!chart) return undefined;\n\n const handleDateChange = (p: { task: GanttTask; start: Date; end: Date }) =>\n callbacksRef.current.onDateChange?.(p.task, p.start, p.end);\n const handleProgressChange = (p: { task: GanttTask; progress: number }) =>\n callbacksRef.current.onProgressChange?.(p.task, p.progress);\n const handleDependencyCreate = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyCreate?.(dep);\n const handleDependencyRemove = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyRemove?.(dep);\n const handleTaskClick = (p: { task: GanttTask }) =>\n callbacksRef.current.onTaskClick?.(p.task);\n const handleGroupToggle = (p: { task: GanttTask; collapsed: boolean }) =>\n callbacksRef.current.onGroupToggle?.(p.task, p.collapsed);\n const handleContextMenu = (p: { task: GanttTask; evt: PointerEvent }) =>\n callbacksRef.current.onContextMenu?.(p.task, p.evt);\n const handleTaskCreate = (p: { task: GanttTask }) => callbacksRef.current.onTaskCreate?.(p.task);\n const handleColumnResize = (p: { columnId: string; width: number }) =>\n callbacksRef.current.onColumnResize?.(p.columnId, p.width);\n const handleColumnReorder = (p: { order: string[] }) =>\n callbacksRef.current.onColumnReorder?.(p.order);\n const handleDependencyDblClick = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyDblClick?.(dep);\n const handleTaskReorder = (p: {\n draggedTaskId: string;\n targetTaskId: string;\n position: \"before\" | \"after\" | \"inside\";\n }) => callbacksRef.current.onTaskReorder?.(p.draggedTaskId, p.targetTaskId, p.position);\n const handleSelectionChange = (p: { taskIds: string[] }) =>\n callbacksRef.current.onSelectionChange?.(p.taskIds);\n\n chart.on(\"date-change\", handleDateChange);\n chart.on(\"progress-change\", handleProgressChange);\n chart.on(\"dependency-create\", handleDependencyCreate);\n chart.on(\"dependency-remove\", handleDependencyRemove);\n chart.on(\"dependency-dblclick\", handleDependencyDblClick);\n chart.on(\"task-click\", handleTaskClick);\n chart.on(\"group-toggle\", handleGroupToggle);\n chart.on(\"context-menu\", handleContextMenu);\n chart.on(\"task-create\", handleTaskCreate);\n chart.on(\"column-resize\", handleColumnResize);\n chart.on(\"column-reorder\", handleColumnReorder);\n chart.on(\"task-reorder\", handleTaskReorder);\n chart.on(\"selection-change\", handleSelectionChange);\n\n return () => {\n chart.off(\"date-change\", handleDateChange);\n chart.off(\"progress-change\", handleProgressChange);\n chart.off(\"dependency-create\", handleDependencyCreate);\n chart.off(\"dependency-remove\", handleDependencyRemove);\n chart.off(\"dependency-dblclick\", handleDependencyDblClick);\n chart.off(\"task-click\", handleTaskClick);\n chart.off(\"group-toggle\", handleGroupToggle);\n chart.off(\"context-menu\", handleContextMenu);\n chart.off(\"task-create\", handleTaskCreate);\n chart.off(\"column-resize\", handleColumnResize);\n chart.off(\"column-reorder\", handleColumnReorder);\n chart.off(\"task-reorder\", handleTaskReorder);\n chart.off(\"selection-change\", handleSelectionChange);\n };\n }, [chart]);\n\n useImperativeHandle(\n ref,\n (): GanttChartHandle => ({\n undo: () => chart?.undo(),\n redo: () => chart?.redo(),\n canUndo: () => chart?.canUndo ?? false,\n canRedo: () => chart?.canRedo ?? false,\n expandAll: () => chart?.expandAll(),\n collapseAll: () => chart?.collapseAll(),\n toggleGroup: (taskId: string) => chart?.toggleGroup(taskId),\n removeDependency: (fromId: string, toId: string) => chart?.removeDependency(fromId, toId),\n updateDependency: (fromId: string, toId: string, partial: Partial<GanttDependency>) =>\n chart?.updateDependency(fromId, toId, partial),\n updateTask: (id: string, partial: Partial<GanttTask>) => chart?.updateTask(id, partial),\n setViewMode: (mode: ViewMode) => chart?.setViewMode(mode),\n fitToViewport: (containerWidthPx: number) => chart?.fitToViewport(containerWidthPx),\n scrollToToday: () => chart?.scrollToToday(),\n scrollToRangeStart: () => chart?.scrollToRangeStart(),\n scrollToRangeEnd: () => chart?.scrollToRangeEnd(),\n getPageCount: () => chart?.getPageCount() ?? 1,\n setPage: (page: number) => chart?.setPage(page),\n zoomIn: (factor?: number) => chart?.zoomIn(factor),\n zoomOut: (factor?: number) => chart?.zoomOut(factor),\n getZoomLevel: () => chart?.getZoomLevel() ?? 0,\n getResourceHistogram: (options?: ResourceHistogramOptions) =>\n chart?.getResourceHistogram(options) ?? [],\n getLeveledTasks: (options?: ResourceLevelingOptions) =>\n chart?.getLeveledTasks(options) ?? { tasks: [], shifted: [] },\n selectTask: (id: string, options?: { additive?: boolean }) => chart?.selectTask(id, options),\n selectAllVisible: () => chart?.selectAllVisible(),\n clearSelection: () => chart?.clearSelection(),\n getSelectedTaskIds: () => chart?.getSelectedTaskIds() ?? [],\n bulkShiftDates: (deltaMs: number) => chart?.bulkShiftDates(deltaMs),\n bulkDelete: () => chart?.bulkDelete(),\n toSVGString: () => chart?.toSVGString() ?? \"\",\n toPNGDataURL: () => chart?.toPNGDataURL() ?? Promise.resolve(\"\"),\n toPNGDataURLs: (options?: { pageWidthPx?: number; pageHeightPx?: number }) =>\n chart?.toPNGDataURLs(options) ?? Promise.resolve([]),\n getRenderModel: () => chart?.getRenderModel() ?? EMPTY_RENDER_MODEL,\n getInstance: () => chart,\n }),\n [chart]\n );\n\n return <div ref={containerRef} className={className} style={style} />;\n}\n\nexport const GanttChart = forwardRef(GanttChartInner);\nGanttChart.displayName = \"GanttChart\";\n\nexport default GanttChart;\n","import { useEffect, useRef, useState, type RefObject } from \"react\";\nimport { GanttChart as GanttCore } from \"@ganttloom/gantt-core\";\nimport type { GanttDependency, GanttOptions, GanttTask } from \"@ganttloom/gantt-core\";\n\nexport interface UseGanttChartResult {\n /** The underlying framework-agnostic GanttChart instance, or null before the DOM mount effect runs (or on the server). */\n chart: GanttCore | null;\n /** Reactive, subscribed to the core's \"history-change\" event. Only meaningful when `enableHistory` is set in options. */\n canUndo: boolean;\n /** Reactive, subscribed to the core's \"history-change\" event. Only meaningful when `enableHistory` is set in options. */\n canRedo: boolean;\n}\n\nconst EMPTY_DEPENDENCIES: GanttDependency[] = [];\nconst EMPTY_OPTIONS: GanttOptions = {};\n\n/**\n * Lower-level escape hatch for consumers who want to own their own container element\n * and DOM tree instead of using the <GanttChart /> component. Constructs a\n * @ganttloom/gantt-core GanttChart instance against `containerRef.current` on mount,\n * keeps it in sync with `tasks` / `dependencies` / `options` via setTasks / setDependencies /\n * setOptions (reference-equality checks only — pass new arrays/objects to trigger a sync),\n * and destroys it on unmount.\n */\nexport function useGanttChart(\n containerRef: RefObject<HTMLElement | null>,\n tasks: GanttTask[],\n dependencies: GanttDependency[] = EMPTY_DEPENDENCIES,\n options: GanttOptions = EMPTY_OPTIONS\n): UseGanttChartResult {\n const [chart, setChart] = useState<GanttCore | null>(null);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n\n const chartRef = useRef<GanttCore | null>(null);\n\n // Latest props, read from the mount effect so it doesn't need to depend on them\n // (which would otherwise tear down and recreate the whole chart on every change).\n const initialTasksRef = useRef(tasks);\n initialTasksRef.current = tasks;\n const initialDepsRef = useRef(dependencies);\n initialDepsRef.current = dependencies;\n const initialOptionsRef = useRef(options);\n initialOptionsRef.current = options;\n\n useEffect(() => {\n if (typeof window === \"undefined\") return undefined;\n const container = containerRef.current;\n if (!container) return undefined;\n\n const instance = new GanttCore(\n container,\n initialTasksRef.current,\n initialDepsRef.current,\n initialOptionsRef.current\n );\n chartRef.current = instance;\n setChart(instance);\n setCanUndo(instance.canUndo);\n setCanRedo(instance.canRedo);\n\n const onHistoryChange = (state: { canUndo: boolean; canRedo: boolean }) => {\n setCanUndo(state.canUndo);\n setCanRedo(state.canRedo);\n };\n instance.on(\"history-change\", onHistoryChange);\n\n return () => {\n instance.off(\"history-change\", onHistoryChange);\n instance.destroy();\n chartRef.current = null;\n setChart(null);\n setCanUndo(false);\n setCanRedo(false);\n };\n // Intentionally mount/unmount only — the container element identity is what matters.\n }, [containerRef]);\n\n const isFirstTasksSync = useRef(true);\n useEffect(() => {\n if (isFirstTasksSync.current) {\n isFirstTasksSync.current = false;\n return;\n }\n chartRef.current?.setTasks(tasks);\n }, [tasks]);\n\n const isFirstDepsSync = useRef(true);\n useEffect(() => {\n if (isFirstDepsSync.current) {\n isFirstDepsSync.current = false;\n return;\n }\n chartRef.current?.setDependencies(dependencies);\n }, [dependencies]);\n\n const isFirstOptionsSync = useRef(true);\n useEffect(() => {\n if (isFirstOptionsSync.current) {\n isFirstOptionsSync.current = false;\n return;\n }\n chartRef.current?.setOptions(options);\n }, [options]);\n\n return { chart, canUndo, canRedo };\n}\n","export { GanttChart, default } from \"./GanttChart\";\nexport type { GanttChartProps, GanttChartHandle } from \"./GanttChart\";\n\nexport { useGanttChart } from \"./useGanttChart\";\nexport type { UseGanttChartResult } from \"./useGanttChart\";\n\n// Re-exported so consumers never need to add @ganttloom/gantt-core as a direct\n// dependency just to reference its types.\nexport type {\n ViewMode,\n DependencyType,\n GanttDependency,\n GanttAssignee,\n GanttTask,\n GanttColumn,\n GanttTheme,\n GanttRenderRow,\n GanttRenderBar,\n GanttRenderLink,\n GanttRenderTick,\n GanttRenderModel,\n GanttOptions,\n GanttEventMap,\n WorkingCalendar,\n ConstraintType,\n ResourceHistogramOptions,\n ResourceLoadBucket,\n GanttMarker,\n GanttRenderMarker,\n FilterTasksOptions,\n FilterTasksResult,\n Density,\n ResourceLevelingOptions,\n ResourceLevelingResult,\n} from \"@ganttloom/gantt-core\";\n\nexport {\n computeWBSCodes,\n applyConstraint,\n isWorkingDay,\n isHoliday,\n isWithinWorkingHours,\n isWorkingTime,\n nextWorkingDay,\n previousWorkingDay,\n shiftToWorkingDay,\n shiftToWorkingTime,\n computeResourceHistogram,\n renderResourceHistogramSVG,\n filterTasks,\n colorByField,\n applyColorByField,\n DEFAULT_PALETTE,\n DENSITY_PRESETS,\n levelResources,\n} from \"@ganttloom/gantt-core\";\n"],"mappings":"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","names":["useEffect","useRef","EMPTY_DEPENDENCIES","useRef","useEffect","options"]}
1
+ {"version":3,"sources":["../src/GanttChart.tsx","../src/useGanttChart.ts","../src/index.ts"],"sourcesContent":["// Pulls in the core's default styles so consumers get a working chart out of the box.\n// This resolves through gantt-core's package.json \"exports\" map (\"./styles.css\" -> \"./dist/styles.css\").\n// If your bundler can't resolve CSS imports from a dependency's export map, import it yourself instead:\n// import \"@ganttloom/gantt-core/styles.css\";\nimport \"@ganttloom/gantt-core/styles.css\";\n\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n type CSSProperties,\n type Ref,\n} from \"react\";\nimport type {\n GanttChart as GanttCore,\n GanttColumn,\n GanttDependency,\n GanttMarker,\n GanttOptions,\n GanttRenderModel,\n GanttTask,\n GanttTheme,\n ResourceHistogramOptions,\n ResourceLoadBucket,\n ResourceLevelingOptions,\n ResourceLevelingResult,\n ViewMode,\n WorkingCalendar,\n} from \"@ganttloom/gantt-core\";\nimport { useGanttChart } from \"./useGanttChart\";\n\nexport interface GanttChartProps {\n tasks: GanttTask[];\n dependencies?: GanttDependency[];\n\n // Mirrors of GanttOptions' non-callback fields.\n viewMode?: ViewMode;\n columns?: GanttColumn[];\n theme?: Partial<GanttTheme>;\n colorScheme?: \"light\" | \"dark\" | \"auto\";\n columnWidth?: number;\n readonly?: boolean;\n showProgress?: boolean;\n showDependencies?: boolean;\n gridPanelWidth?: number;\n showCriticalPath?: boolean;\n showBaseline?: boolean;\n showDeadlines?: boolean;\n /** hover tooltip on task bars, defaults to true. A floating, CSS-customizable element (`.gantt-tooltip`) - not the browser's native title tooltip. */\n showTooltip?: boolean;\n /** fully control tooltip content/markup; return null/undefined to suppress it for a specific task. Defaults to name/dates/progress/assignees/notes. */\n renderTooltip?: (task: GanttTask) => HTMLElement | string | null | undefined;\n showAssigneeAvatars?: boolean;\n enableHistory?: boolean;\n keyboardAccessible?: boolean;\n virtualScroll?: boolean;\n autoSchedule?: boolean;\n pagination?: { pageSize: number; page: number };\n snapToUnit?: \"hour\" | \"day\" | \"week\" | false;\n /** working-day/holiday definition used to shade non-working time and (with autoSchedule) skip it when cascading */\n calendar?: WorkingCalendar;\n /** arbitrary labeled vertical lines on the timeline, independent of any task */\n markers?: GanttMarker[];\n /** a group task with no explicit `progress` gets a duration-weighted average of its children's progress instead of 0 */\n autoRollupProgress?: boolean;\n /** enables click/ctrl-click/shift-click multi-selection of task bars, and the handle's bulkShiftDates()/bulkDelete() */\n selectable?: boolean;\n /**\n * Stretch (never shrink) the timeline to fill the container's width instead of leaving empty\n * space - the common \"short project at month/year zoom\" or \"just a few tasks\" case. Off by\n * default (a new chart's pixel layout shouldn't change for existing consumers who don't opt in).\n */\n autoFitToViewport?: boolean;\n\n // Mirrors of GanttOptions' callback fields, as normal React event props.\n onDateChange?: (task: GanttTask, start: Date, end: Date) => void;\n onProgressChange?: (task: GanttTask, progress: number) => void;\n onDependencyCreate?: (dep: GanttDependency) => void;\n onDependencyRemove?: (dep: GanttDependency) => void;\n /** called on double-click of a dependency link, for building your own \"edit this link\" UI (see the handle's updateDependency) */\n onDependencyDblClick?: (dep: GanttDependency) => void;\n onTaskClick?: (task: GanttTask) => void;\n /** called on double-click of a task bar (distinct from onDependencyDblClick, which is for links) */\n onTaskDblClick?: (task: GanttTask) => void;\n onGroupToggle?: (task: GanttTask, collapsed: boolean) => void;\n onContextMenu?: (task: GanttTask, evt: PointerEvent) => void;\n /** called when a task is created via drag-to-create on an empty timeline row */\n onTaskCreate?: (task: GanttTask) => void;\n /** called when a grid column is resized by dragging its header's edge */\n onColumnResize?: (columnId: string, width: number) => void;\n /** called when grid columns are reordered by dragging a header; `order` is the new full list of column ids */\n onColumnReorder?: (order: string[]) => void;\n /** called when a grid row is dragged onto another; \"inside\" reparents the dragged task under the target */\n onTaskReorder?: (draggedTaskId: string, targetTaskId: string, position: \"before\" | \"after\" | \"inside\") => void;\n onSelectionChange?: (taskIds: string[]) => void;\n /** fired whenever the sort state changes, via a sortable column header click or the handle's setSort() */\n onSortChange?: (sort: { columnId: string; direction: \"asc\" | \"desc\" } | null) => void;\n\n /** Applied to the wrapping container div. */\n className?: string;\n style?: CSSProperties;\n}\n\n/** Imperative escape hatches that don't need to be reactive props. */\nexport interface GanttChartHandle {\n undo(): void;\n redo(): void;\n canUndo(): boolean;\n canRedo(): boolean;\n expandAll(): void;\n collapseAll(): void;\n toggleGroup(taskId: string): void;\n removeDependency(fromId: string, toId: string): void;\n updateDependency(fromId: string, toId: string, partial: Partial<GanttDependency>): void;\n updateTask(id: string, partial: Partial<GanttTask>): void;\n setViewMode(mode: ViewMode): void;\n fitToViewport(containerWidthPx: number): void;\n scrollToToday(): void;\n scrollToRangeStart(): void;\n scrollToRangeEnd(): void;\n getPageCount(): number;\n setPage(page: number): void;\n zoomIn(factor?: number): void;\n zoomOut(factor?: number): void;\n getZoomLevel(): number;\n getResourceHistogram(options?: ResourceHistogramOptions): ResourceLoadBucket[];\n getLeveledTasks(options?: ResourceLevelingOptions): ResourceLevelingResult;\n selectTask(id: string, options?: { additive?: boolean }): void;\n selectAllVisible(): void;\n clearSelection(): void;\n getSelectedTaskIds(): string[];\n setSort(columnId: string | null, direction?: \"asc\" | \"desc\" | null): void;\n getSort(): { columnId: string; direction: \"asc\" | \"desc\" } | null;\n bulkShiftDates(deltaMs: number): void;\n bulkDelete(): void;\n toSVGString(): string;\n toPNGDataURL(): Promise<string>;\n toPNGDataURLs(options?: { pageWidthPx?: number; pageHeightPx?: number }): Promise<string[]>;\n getRenderModel(): GanttRenderModel | null;\n /** Direct access to the underlying framework-agnostic core instance (null before mount). */\n getInstance(): GanttCore | null;\n}\n\nconst EMPTY_DEPENDENCIES: GanttDependency[] = [];\n\nconst EMPTY_RENDER_MODEL: GanttRenderModel = {\n width: 0,\n height: 0,\n rowHeight: 0,\n headerHeight: 0,\n rows: [],\n bars: [],\n links: [],\n ticks: [],\n columns: [],\n theme: {} as GanttTheme,\n markers: [],\n rangeStart: new Date(0),\n sort: null,\n};\n\nfunction GanttChartInner(props: GanttChartProps, ref: Ref<GanttChartHandle>) {\n const {\n tasks,\n dependencies = EMPTY_DEPENDENCIES,\n className,\n style,\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onTaskDblClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n onSortChange,\n ...rest\n } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // The core only wires up its internal contextmenu DOM listener (and therefore only\n // ever emits the \"context-menu\" event) when options.onContextMenu is truthy AT\n // CONSTRUCTION TIME (see GanttChart#setupDom in gantt-core). We pass a stable no-op\n // here purely to force that wiring on regardless of whether the consumer supplied\n // onContextMenu, and dispatch the actual prop via the \"context-menu\" event\n // subscription below (so it stays reactive to prop changes and is never double-fired).\n const enableContextMenuWiring = useRef(() => {});\n\n // Same construction-time-only caveat as onContextMenu above: drag-to-create is only\n // wired into the core's InteractionController when options.onTaskCreate is truthy at\n // construction. A stable no-op forces it on whenever this component was given an\n // onTaskCreate prop at all; the real dispatch goes through the \"task-create\" event.\n const enableCreateWiring = useRef(() => {});\n\n // Same construction-time-only caveat: column resize/reorder DOM listeners are only\n // built into the grid header when these options are truthy at construction.\n const enableColumnResizeWiring = useRef((_columnId: string, _width: number) => {});\n const enableColumnReorderWiring = useRef((_order: string[]) => {});\n const enableTaskReorderWiring = useRef(\n (_draggedTaskId: string, _targetTaskId: string, _position: \"before\" | \"after\" | \"inside\") => {}\n );\n const enableDependencyDblClickWiring = useRef((_dep: GanttDependency) => {});\n const enableTaskDblClickWiring = useRef((_task: GanttTask) => {});\n\n // Deliberately depend on Boolean(handler) rather than the handler itself below: the\n // ref-backed enableXWiring callbacks already pick up the latest handler without\n // needing the memo to re-run, so depending on the raw (often inline) callback would\n // just re-create `options` — and remount the chart — every render.\n /* eslint-disable react-hooks/exhaustive-deps */\n const options = useMemo<GanttOptions>(\n () => ({\n ...rest,\n onContextMenu: enableContextMenuWiring.current,\n onTaskCreate: onTaskCreate ? enableCreateWiring.current : undefined,\n onColumnResize: onColumnResize ? enableColumnResizeWiring.current : undefined,\n onColumnReorder: onColumnReorder ? enableColumnReorderWiring.current : undefined,\n onTaskReorder: onTaskReorder ? enableTaskReorderWiring.current : undefined,\n onDependencyDblClick: onDependencyDblClick ? enableDependencyDblClickWiring.current : undefined,\n onTaskDblClick: onTaskDblClick ? enableTaskDblClickWiring.current : undefined,\n // onSelectionChange/onSortChange are deliberately NOT set here: gantt-core invokes them\n // directly from this.options AND emits a matching event at the same call sites, and the\n // effect below already subscribes to those events — including them here would double-fire.\n }),\n [\n rest.viewMode,\n rest.columns,\n rest.theme,\n rest.colorScheme,\n rest.columnWidth,\n rest.readonly,\n rest.showProgress,\n rest.showDependencies,\n rest.gridPanelWidth,\n rest.showCriticalPath,\n rest.showBaseline,\n rest.showDeadlines,\n rest.showTooltip,\n rest.renderTooltip,\n rest.autoFitToViewport,\n rest.showAssigneeAvatars,\n rest.enableHistory,\n rest.keyboardAccessible,\n rest.virtualScroll,\n rest.autoSchedule,\n rest.pagination,\n rest.snapToUnit,\n rest.calendar,\n rest.markers,\n rest.autoRollupProgress,\n rest.selectable,\n Boolean(onTaskCreate),\n Boolean(onColumnResize),\n Boolean(onColumnReorder),\n Boolean(onTaskReorder),\n Boolean(onDependencyDblClick),\n Boolean(onTaskDblClick),\n ]\n );\n /* eslint-enable react-hooks/exhaustive-deps */\n\n const { chart } = useGanttChart(containerRef, tasks, dependencies, options);\n\n // Keep the latest callback props in a ref so the event-subscription effect below\n // doesn't need to re-subscribe (tearing down/rebuilding listeners) on every render.\n const callbacksRef = useRef({\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onTaskDblClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n onSortChange,\n });\n callbacksRef.current = {\n onDateChange,\n onProgressChange,\n onDependencyCreate,\n onDependencyRemove,\n onDependencyDblClick,\n onTaskClick,\n onTaskDblClick,\n onGroupToggle,\n onContextMenu,\n onTaskCreate,\n onColumnResize,\n onColumnReorder,\n onTaskReorder,\n onSelectionChange,\n onSortChange,\n };\n\n useEffect(() => {\n if (!chart) return undefined;\n\n const handleDateChange = (p: { task: GanttTask; start: Date; end: Date }) =>\n callbacksRef.current.onDateChange?.(p.task, p.start, p.end);\n const handleProgressChange = (p: { task: GanttTask; progress: number }) =>\n callbacksRef.current.onProgressChange?.(p.task, p.progress);\n const handleDependencyCreate = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyCreate?.(dep);\n const handleDependencyRemove = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyRemove?.(dep);\n const handleTaskClick = (p: { task: GanttTask }) =>\n callbacksRef.current.onTaskClick?.(p.task);\n const handleTaskDblClick = (p: { task: GanttTask }) =>\n callbacksRef.current.onTaskDblClick?.(p.task);\n const handleGroupToggle = (p: { task: GanttTask; collapsed: boolean }) =>\n callbacksRef.current.onGroupToggle?.(p.task, p.collapsed);\n const handleContextMenu = (p: { task: GanttTask; evt: PointerEvent }) =>\n callbacksRef.current.onContextMenu?.(p.task, p.evt);\n const handleTaskCreate = (p: { task: GanttTask }) => callbacksRef.current.onTaskCreate?.(p.task);\n const handleColumnResize = (p: { columnId: string; width: number }) =>\n callbacksRef.current.onColumnResize?.(p.columnId, p.width);\n const handleColumnReorder = (p: { order: string[] }) =>\n callbacksRef.current.onColumnReorder?.(p.order);\n const handleDependencyDblClick = (dep: GanttDependency) =>\n callbacksRef.current.onDependencyDblClick?.(dep);\n const handleTaskReorder = (p: {\n draggedTaskId: string;\n targetTaskId: string;\n position: \"before\" | \"after\" | \"inside\";\n }) => callbacksRef.current.onTaskReorder?.(p.draggedTaskId, p.targetTaskId, p.position);\n const handleSelectionChange = (p: { taskIds: string[] }) =>\n callbacksRef.current.onSelectionChange?.(p.taskIds);\n const handleSortChange = (p: { sort: { columnId: string; direction: \"asc\" | \"desc\" } | null }) =>\n callbacksRef.current.onSortChange?.(p.sort);\n\n chart.on(\"date-change\", handleDateChange);\n chart.on(\"progress-change\", handleProgressChange);\n chart.on(\"dependency-create\", handleDependencyCreate);\n chart.on(\"dependency-remove\", handleDependencyRemove);\n chart.on(\"dependency-dblclick\", handleDependencyDblClick);\n chart.on(\"task-click\", handleTaskClick);\n chart.on(\"task-dblclick\", handleTaskDblClick);\n chart.on(\"group-toggle\", handleGroupToggle);\n chart.on(\"context-menu\", handleContextMenu);\n chart.on(\"task-create\", handleTaskCreate);\n chart.on(\"column-resize\", handleColumnResize);\n chart.on(\"column-reorder\", handleColumnReorder);\n chart.on(\"task-reorder\", handleTaskReorder);\n chart.on(\"selection-change\", handleSelectionChange);\n chart.on(\"sort-change\", handleSortChange);\n\n return () => {\n chart.off(\"date-change\", handleDateChange);\n chart.off(\"progress-change\", handleProgressChange);\n chart.off(\"dependency-create\", handleDependencyCreate);\n chart.off(\"dependency-remove\", handleDependencyRemove);\n chart.off(\"dependency-dblclick\", handleDependencyDblClick);\n chart.off(\"task-click\", handleTaskClick);\n chart.off(\"task-dblclick\", handleTaskDblClick);\n chart.off(\"group-toggle\", handleGroupToggle);\n chart.off(\"context-menu\", handleContextMenu);\n chart.off(\"task-create\", handleTaskCreate);\n chart.off(\"column-resize\", handleColumnResize);\n chart.off(\"column-reorder\", handleColumnReorder);\n chart.off(\"task-reorder\", handleTaskReorder);\n chart.off(\"selection-change\", handleSelectionChange);\n chart.off(\"sort-change\", handleSortChange);\n };\n }, [chart]);\n\n useImperativeHandle(\n ref,\n (): GanttChartHandle => ({\n undo: () => chart?.undo(),\n redo: () => chart?.redo(),\n canUndo: () => chart?.canUndo ?? false,\n canRedo: () => chart?.canRedo ?? false,\n expandAll: () => chart?.expandAll(),\n collapseAll: () => chart?.collapseAll(),\n toggleGroup: (taskId: string) => chart?.toggleGroup(taskId),\n removeDependency: (fromId: string, toId: string) => chart?.removeDependency(fromId, toId),\n updateDependency: (fromId: string, toId: string, partial: Partial<GanttDependency>) =>\n chart?.updateDependency(fromId, toId, partial),\n updateTask: (id: string, partial: Partial<GanttTask>) => chart?.updateTask(id, partial),\n setViewMode: (mode: ViewMode) => chart?.setViewMode(mode),\n fitToViewport: (containerWidthPx: number) => chart?.fitToViewport(containerWidthPx),\n scrollToToday: () => chart?.scrollToToday(),\n scrollToRangeStart: () => chart?.scrollToRangeStart(),\n scrollToRangeEnd: () => chart?.scrollToRangeEnd(),\n getPageCount: () => chart?.getPageCount() ?? 1,\n setPage: (page: number) => chart?.setPage(page),\n zoomIn: (factor?: number) => chart?.zoomIn(factor),\n zoomOut: (factor?: number) => chart?.zoomOut(factor),\n getZoomLevel: () => chart?.getZoomLevel() ?? 0,\n getResourceHistogram: (options?: ResourceHistogramOptions) =>\n chart?.getResourceHistogram(options) ?? [],\n getLeveledTasks: (options?: ResourceLevelingOptions) =>\n chart?.getLeveledTasks(options) ?? { tasks: [], shifted: [] },\n selectTask: (id: string, options?: { additive?: boolean }) => chart?.selectTask(id, options),\n selectAllVisible: () => chart?.selectAllVisible(),\n clearSelection: () => chart?.clearSelection(),\n getSelectedTaskIds: () => chart?.getSelectedTaskIds() ?? [],\n setSort: (columnId: string | null, direction?: \"asc\" | \"desc\" | null) => chart?.setSort(columnId, direction),\n getSort: () => chart?.getSort() ?? null,\n bulkShiftDates: (deltaMs: number) => chart?.bulkShiftDates(deltaMs),\n bulkDelete: () => chart?.bulkDelete(),\n toSVGString: () => chart?.toSVGString() ?? \"\",\n toPNGDataURL: () => chart?.toPNGDataURL() ?? Promise.resolve(\"\"),\n toPNGDataURLs: (options?: { pageWidthPx?: number; pageHeightPx?: number }) =>\n chart?.toPNGDataURLs(options) ?? Promise.resolve([]),\n getRenderModel: () => chart?.getRenderModel() ?? EMPTY_RENDER_MODEL,\n getInstance: () => chart,\n }),\n [chart]\n );\n\n return <div ref={containerRef} className={className} style={style} />;\n}\n\nexport const GanttChart = forwardRef(GanttChartInner);\nGanttChart.displayName = \"GanttChart\";\n\nexport default GanttChart;\n","import { useEffect, useRef, useState, type RefObject } from \"react\";\nimport { GanttChart as GanttCore } from \"@ganttloom/gantt-core\";\nimport type { GanttDependency, GanttOptions, GanttTask } from \"@ganttloom/gantt-core\";\n\nexport interface UseGanttChartResult {\n /** The underlying framework-agnostic GanttChart instance, or null before the DOM mount effect runs (or on the server). */\n chart: GanttCore | null;\n /** Reactive, subscribed to the core's \"history-change\" event. Only meaningful when `enableHistory` is set in options. */\n canUndo: boolean;\n /** Reactive, subscribed to the core's \"history-change\" event. Only meaningful when `enableHistory` is set in options. */\n canRedo: boolean;\n}\n\nconst EMPTY_DEPENDENCIES: GanttDependency[] = [];\nconst EMPTY_OPTIONS: GanttOptions = {};\n\n/**\n * Lower-level escape hatch for consumers who want to own their own container element\n * and DOM tree instead of using the <GanttChart /> component. Constructs a\n * @ganttloom/gantt-core GanttChart instance against `containerRef.current` on mount,\n * keeps it in sync with `tasks` / `dependencies` / `options` via setTasks / setDependencies /\n * setOptions (reference-equality checks only — pass new arrays/objects to trigger a sync),\n * and destroys it on unmount.\n */\nexport function useGanttChart(\n containerRef: RefObject<HTMLElement | null>,\n tasks: GanttTask[],\n dependencies: GanttDependency[] = EMPTY_DEPENDENCIES,\n options: GanttOptions = EMPTY_OPTIONS\n): UseGanttChartResult {\n const [chart, setChart] = useState<GanttCore | null>(null);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n\n const chartRef = useRef<GanttCore | null>(null);\n\n // Latest props, read from the mount effect so it doesn't need to depend on them\n // (which would otherwise tear down and recreate the whole chart on every change).\n const initialTasksRef = useRef(tasks);\n initialTasksRef.current = tasks;\n const initialDepsRef = useRef(dependencies);\n initialDepsRef.current = dependencies;\n const initialOptionsRef = useRef(options);\n initialOptionsRef.current = options;\n\n useEffect(() => {\n if (typeof window === \"undefined\") return undefined;\n const container = containerRef.current;\n if (!container) return undefined;\n\n const instance = new GanttCore(\n container,\n initialTasksRef.current,\n initialDepsRef.current,\n initialOptionsRef.current\n );\n chartRef.current = instance;\n setChart(instance);\n setCanUndo(instance.canUndo);\n setCanRedo(instance.canRedo);\n\n const onHistoryChange = (state: { canUndo: boolean; canRedo: boolean }) => {\n setCanUndo(state.canUndo);\n setCanRedo(state.canRedo);\n };\n instance.on(\"history-change\", onHistoryChange);\n\n return () => {\n instance.off(\"history-change\", onHistoryChange);\n instance.destroy();\n chartRef.current = null;\n setChart(null);\n setCanUndo(false);\n setCanRedo(false);\n };\n // Intentionally mount/unmount only — the container element identity is what matters.\n }, [containerRef]);\n\n const isFirstTasksSync = useRef(true);\n useEffect(() => {\n if (isFirstTasksSync.current) {\n isFirstTasksSync.current = false;\n return;\n }\n chartRef.current?.setTasks(tasks);\n }, [tasks]);\n\n const isFirstDepsSync = useRef(true);\n useEffect(() => {\n if (isFirstDepsSync.current) {\n isFirstDepsSync.current = false;\n return;\n }\n chartRef.current?.setDependencies(dependencies);\n }, [dependencies]);\n\n const isFirstOptionsSync = useRef(true);\n useEffect(() => {\n if (isFirstOptionsSync.current) {\n isFirstOptionsSync.current = false;\n return;\n }\n chartRef.current?.setOptions(options);\n }, [options]);\n\n return { chart, canUndo, canRedo };\n}\n","export { GanttChart, default } from \"./GanttChart\";\nexport type { GanttChartProps, GanttChartHandle } from \"./GanttChart\";\n\nexport { useGanttChart } from \"./useGanttChart\";\nexport type { UseGanttChartResult } from \"./useGanttChart\";\n\n// Re-exported so consumers never need to add @ganttloom/gantt-core as a direct\n// dependency just to reference its types.\nexport type {\n ViewMode,\n DependencyType,\n GanttDependency,\n GanttAssignee,\n GanttTask,\n GanttColumn,\n GanttTheme,\n GanttRenderRow,\n GanttRenderBar,\n GanttRenderLink,\n GanttRenderTick,\n GanttRenderModel,\n GanttOptions,\n GanttEventMap,\n WorkingCalendar,\n ConstraintType,\n ResourceHistogramOptions,\n ResourceLoadBucket,\n GanttMarker,\n GanttRenderMarker,\n FilterTasksOptions,\n FilterTasksResult,\n Density,\n ResourceLevelingOptions,\n ResourceLevelingResult,\n} from \"@ganttloom/gantt-core\";\n\nexport {\n computeWBSCodes,\n applyConstraint,\n isWorkingDay,\n isHoliday,\n isWithinWorkingHours,\n isWorkingTime,\n nextWorkingDay,\n previousWorkingDay,\n shiftToWorkingDay,\n shiftToWorkingTime,\n computeResourceHistogram,\n renderResourceHistogramSVG,\n filterTasks,\n colorByField,\n applyColorByField,\n DEFAULT_PALETTE,\n DENSITY_PRESETS,\n levelResources,\n} from \"@ganttloom/gantt-core\";\n"],"mappings":"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","names":["useEffect","useRef","EMPTY_DEPENDENCIES","useRef","useEffect","options"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ganttloom/gantt-react",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "React wrapper for @ganttloom/gantt-core — fully customizable Gantt chart component.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -40,7 +40,7 @@
40
40
  "react-dom": ">=18"
41
41
  },
42
42
  "dependencies": {
43
- "@ganttloom/gantt-core": "0.2.1"
43
+ "@ganttloom/gantt-core": "0.4.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "react": "^18.3.1",