Skip to content

Commit 3fd2e22

Browse files
committed
feat: better scrolling behaviour; data parsing added; state manager added;
1 parent 0a56316 commit 3fd2e22

19 files changed

+337
-35
lines changed

README.md

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ The layout can be configured by either setting the properties in the table below
7575
| enableTaskResize | `true` | Enable/disable gantt export options |
7676
| headerBackground | `#f3f3f3` | Background color for header |
7777
| inputDateFormat | `MM-DD-YYYY` | Input date format |
78-
| tasksContainerWidth | `440` | Task sidebar container width |
78+
| tasksContainerWidth | `425` | Task sidebar container width |
7979
| tooltipId | `apexgantt-tooltip-container` | The tooltip HTML element id |
8080
| tooltipTemplate | `tooltipTemplate` | The HTML template for tooltip |
8181
| tooltipBorderColor | `#BCBCBC` | The border color of tooltip |
@@ -185,6 +185,65 @@ Each tasks should be in below format
185185
];
186186
```
187187

188+
## Data Parsing
189+
190+
Map your existing data structure to ApexGantt format without manual transformation.
191+
192+
```javascript
193+
const apiData = [
194+
{
195+
task_id: 'T1',
196+
task_name: 'Design Phase',
197+
start_date: '01-01-2024',
198+
end_date: '01-15-2024',
199+
completion: 75,
200+
},
201+
];
202+
203+
const gantt = new ApexGantt(document.getElementById('gantt'), {
204+
series: apiData,
205+
parsing: {
206+
id: 'task_id',
207+
name: 'task_name',
208+
startTime: 'start_date',
209+
endTime: 'end_date',
210+
progress: 'completion',
211+
},
212+
});
213+
```
214+
215+
### Nested Objects & Transforms
216+
217+
Use dot notation for nested properties and inline transforms for data conversion:
218+
219+
```javascript
220+
const nestedData = [
221+
{
222+
project: {
223+
task: {id: 'T1', title: 'Design'},
224+
dates: {start: '01-01-2024', end: '01-15-2024'},
225+
status: {completion: 0.75},
226+
},
227+
},
228+
];
229+
230+
const gantt = new ApexGantt(document.getElementById('gantt'), {
231+
series: nestedData,
232+
parsing: {
233+
id: 'project.task.id',
234+
name: 'project.task.title',
235+
startTime: 'project.dates.start',
236+
endTime: 'project.dates.end',
237+
progress: {
238+
key: 'project.status.completion',
239+
transform: (value) => value * 100, // convert to percentage
240+
},
241+
},
242+
});
243+
```
244+
245+
**Supported fields:** `id`, `name`, `startTime`, `endTime`, `progress`, `type`, `parentId`, `dependency`, `barBackgroundColor`, `rowBackgroundColor`, `collapsed`
246+
188247
## 📘 Public API
189248

190249
### 1. `update(options)`

demo/data-parsing.html

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>ApexGantt</title>
7+
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;600&display=swap" rel="stylesheet" />
8+
<script src="../apexgantt.min.js"></script>
9+
</head>
10+
<body>
11+
<div id="svg-gantt" style="margin: 0 auto; width: 90%; height: 600px"></div>
12+
<script>
13+
const nestedData = [
14+
{
15+
project: {
16+
task: {
17+
id: 'T1',
18+
details: {
19+
title: 'Design Phase',
20+
dates: {
21+
start: '01-01-2024',
22+
end: '01-15-2024',
23+
},
24+
},
25+
},
26+
metadata: {
27+
completion_percentage: 0.75,
28+
category: 'design',
29+
},
30+
styling: {
31+
colors: {
32+
bar: '#4A90E2',
33+
row: '#F0F4F8',
34+
},
35+
},
36+
},
37+
},
38+
{
39+
project: {
40+
task: {
41+
id: 'T2',
42+
details: {
43+
title: 'Development Phase',
44+
dates: {
45+
start: '01-16-2024',
46+
end: '02-28-2024',
47+
},
48+
},
49+
},
50+
metadata: {
51+
completion_percentage: 0.45,
52+
category: 'task',
53+
},
54+
styling: {
55+
colors: {
56+
bar: '#50C878',
57+
row: '#F0F8F0',
58+
},
59+
},
60+
},
61+
},
62+
];
63+
64+
const ganttOptions = {
65+
enableTaskDrag: false,
66+
enableTaskResize: false,
67+
series: nestedData,
68+
parsing: {
69+
id: 'project.task.id',
70+
name: 'project.task.details.title',
71+
startTime: 'project.task.details.dates.start',
72+
endTime: 'project.task.details.dates.end',
73+
progress: {
74+
key: 'project.metadata.completion_percentage',
75+
transform: (value) => value * 100,
76+
},
77+
barBackgroundColor: 'project.styling.colors.bar',
78+
rowBackgroundColor: 'project.styling.colors.row',
79+
},
80+
};
81+
const chart = new ApexGantt(document.getElementById('svg-gantt'), ganttOptions);
82+
chart.render();
83+
</script>
84+
</body>
85+
</html>

index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ export { ApexGantt as default };
44
export { ApexGantt };
55
export type { Annotation } from './lib/models/Annotation';
66
export type { GanttUserOptions } from './lib/models/Options';
7-
export type { Task } from './lib/models/Tasks';
7+
export type { TaskInput, TaskType } from './lib/models/Tasks';
88
export { ViewMode } from './lib/util/gantt.util';
99
export type { ThemeMode, GanttTheme } from './lib/models/Theme';
1010
export { LightTheme, DarkTheme, getTheme } from './lib/models/Theme';
1111
export { GanttEvents } from './lib/types/events';
1212
export type { TaskUpdateEventDetail, TaskValidationErrorEventDetail, TaskUpdateSuccessEventDetail, TaskUpdateErrorEventDetail, } from './lib/types/events';
13+
export type { ParsingConfig, ParsingValue } from './lib/models/DataParser';
14+
export { DataParser } from './lib/models/DataParser';

lib/gantt.d.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export declare class ApexGantt extends BaseChart {
1010
private dataManager;
1111
private zoomHandler;
1212
private timelineScrollHandlers;
13+
private isSyncingScroll;
14+
private scrollbarResizeObserver;
15+
private splitBarResizeHandler;
16+
private stateManager;
1317
constructor(element: HTMLElement, options?: GanttUserOptions);
1418
static setLicense(key: string): void;
1519
private setupShadowDOMEnvironment;
@@ -30,16 +34,20 @@ export declare class ApexGantt extends BaseChart {
3034
* Setup proper positioning for chart container to support dialogs
3135
*/
3236
private setupChartContainerPositioning;
37+
private disableHeaderMousewheelScroll;
3338
private createLayout;
3439
private syncTasksColumnWidths;
40+
private setupScrollbarResizeObserver;
41+
private setupSplitBarResizeListener;
42+
private positionHorizontalScrollbar;
3543
private compensateForScrollbar;
44+
private setupTimelineHorizontalScroll;
45+
private applyScrollbarStylesToElement;
3646
private setupZoomEventListener;
3747
private setupRowBackgroundColors;
3848
private renderDependencyArrows;
3949
private setupDependencyArrowEvents;
40-
private createExportDropdown;
4150
private rerenderTimeline;
42-
private setupTimelineHeaderScroll;
4351
private updateToolbarAfterZoom;
4452
private cleanupEventListeners;
4553
private cleanupTooltips;
@@ -49,9 +57,18 @@ export declare class ApexGantt extends BaseChart {
4957
private createViewModeDisplay;
5058
renderToolbar(container: HTMLElement): void;
5159
update(options: GanttUserOptions): void;
60+
private detectCurrentTheme;
61+
private fillEmptyRowsAfterRender;
62+
private createEmptyTimelineRow;
63+
private createEmptyTaskRow;
64+
private cleanupScrollbarStyles;
5265
updateTask(taskId: string, updatedTask: Partial<Task>): void;
5366
zoomIn(): void;
5467
zoomOut(): void;
68+
/**
69+
* update the horizontal scrollbar's content width to match timeline width
70+
*/
71+
private updateHorizontalScrollbarContent;
5572
/**
5673
* Check if element already has explicit dimensions from CSS
5774
*/

lib/models/ArrowLink.d.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ChartContext } from '../../../../graph-utils/src/index.ts';
2+
3+
export interface Edge {
4+
readonly id: string;
5+
readonly source: HTMLElement;
6+
readonly target: HTMLElement;
7+
}
8+
export interface ArrowLinkOptions {
9+
readonly arrowColor: string;
10+
readonly height: number;
11+
readonly paddingX: number;
12+
readonly paddingY: number;
13+
readonly width: number;
14+
}
15+
export declare class ArrowLink {
16+
private elements;
17+
private svg;
18+
options: Partial<ArrowLinkOptions>;
19+
private instanceId;
20+
private chartContext;
21+
constructor(instanceId?: string);
22+
getInstanceId(): string;
23+
static calculateArrowPath(fromElement: HTMLElement, toElement: HTMLElement, svg: SVGSVGElement, options: Partial<ArrowLinkOptions>): string;
24+
static drawArrow(fromElement: HTMLElement, toElement: HTMLElement, svg: SVGSVGElement, options: Partial<ArrowLinkOptions>, id: string, instanceId?: string, chartContext?: ChartContext): SVGPathElement;
25+
static updateArrow(fromElement: HTMLElement, toElement: HTMLElement, svg: SVGSVGElement, options: Partial<ArrowLinkOptions>, id: string): void;
26+
private createMarker;
27+
render(container: Element, elements: readonly Edge[], options: Partial<ArrowLinkOptions>, chartContext?: ChartContext): SVGSVGElement;
28+
}

lib/models/DataManager.d.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Task } from './Tasks';
1+
import { Task, TaskInput } from './Tasks';
22
import { ChartContext } from '../../../../graph-utils/src/index.ts';
33
import { Dayjs } from 'dayjs';
44

@@ -12,13 +12,13 @@ export declare class DataManager {
1212
private taskMap;
1313
private taskTree;
1414
private arrowLinkInstanceId;
15-
constructor(tasks?: Task[]);
15+
constructor(tasks?: TaskInput[]);
1616
private buildTaskTree;
1717
private processLevel;
1818
private sortTasksByDate;
1919
private validateTask;
2020
addDependency(fromId: string, toId: string, type?: Dependency['type']): void;
21-
addTask(task: Partial<Task>): void;
21+
addTask(taskInput: Partial<TaskInput>): void;
2222
calculateProgress(): number;
2323
getDateRange(add: number, viewMode: any): [Dayjs, Dayjs];
2424
getFlatSortedTasks(tasks: Task[], getAll?: boolean): Task[];
@@ -35,10 +35,10 @@ export declare class DataManager {
3535
hasChildren(id: string): boolean;
3636
removeDependency(fromId: string, toId: string): void;
3737
removeTask(id: string): void;
38-
setTasks(tasks: Task[]): void;
38+
setTasks(taskInputs: TaskInput[]): void;
3939
toggleTask(id: string): void;
4040
setArrowLinkInstanceId(instanceId: string): void;
4141
updateDependencyArrows(taskId: string, chartContext?: ChartContext): void;
42-
updateTask(id: string, updates: Partial<Task>): Task;
42+
updateTask(id: string, updates: Partial<TaskInput>): Task;
4343
}
4444
export {};

lib/models/DataParser.d.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { TaskInput } from './Tasks';
2+
3+
/**
4+
* can be a simple path string or an object with key and transform
5+
*/
6+
export type ParsingValue = string | {
7+
key: string;
8+
transform?: (value: any) => any;
9+
};
10+
/**
11+
* Configuration for parsing raw data into TaskInput format
12+
*/
13+
export interface ParsingConfig {
14+
id: ParsingValue;
15+
name: ParsingValue;
16+
startTime: ParsingValue;
17+
endTime?: ParsingValue;
18+
progress?: ParsingValue;
19+
type?: ParsingValue;
20+
parentId?: ParsingValue;
21+
dependency?: ParsingValue;
22+
barBackgroundColor?: ParsingValue;
23+
rowBackgroundColor?: ParsingValue;
24+
collapsed?: ParsingValue;
25+
}
26+
/**
27+
* DataParser - for parsing raw data objects into TaskInput format
28+
*/
29+
export declare class DataParser {
30+
/**
31+
* Extract value from nested object using dot notation path
32+
* @param obj - source object
33+
* @param path - dot notation path (e.g., 'project.task.title')
34+
* @returns Extracted value
35+
*/
36+
private static getNestedValue;
37+
/**
38+
* single parsing value configuration
39+
* @param obj - Source object
40+
* @param parsingValue - Either a string path or object with key and transform
41+
* @returns Processed value
42+
*/
43+
private static processParsingValue;
44+
/**
45+
* Parse an array of raw data objects into TaskInput array
46+
* @param data - Array of raw data objects
47+
* @param config - Parsing configuration mapping
48+
* @returns Array of TaskInput objects
49+
*/
50+
static parse(data: any[], config: ParsingConfig): TaskInput[];
51+
/**
52+
* validate parsing configuration
53+
* @param config - parsing configuration to validate
54+
* @returns true if valid, false otherwise
55+
*/
56+
static validateConfig(config: ParsingConfig): boolean;
57+
}

lib/models/Options.d.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { ParsingConfig } from '../models/DataParser';
12
import { ThemeMode } from './Theme';
2-
import { Task } from './Tasks';
3+
import { Task, TaskInput } from './Tasks';
34
import { Annotation, Orientation } from './Annotation';
45
import { ViewMode } from '../util/gantt.util';
56

@@ -43,7 +44,7 @@ export interface GanttBarOptions {
4344
}
4445
export interface GanttData {
4546
readonly annotations: Annotation[];
46-
readonly series: Task[];
47+
readonly series: TaskInput[];
4748
}
4849
export interface GanttRowOptions {
4950
readonly rowBackgroundColors: readonly string[];
@@ -94,14 +95,15 @@ export interface GanttUserOptions {
9495
readonly inputDateFormat?: string;
9596
readonly rowBackgroundColors?: readonly string[];
9697
readonly rowHeight?: number;
97-
readonly series: Task[];
98+
readonly series: TaskInput[] | Record<string, unknown>[];
9899
readonly tasksContainerWidth?: number;
99100
readonly tooltipBGColor?: string;
100101
readonly tooltipBorderColor?: string;
101102
readonly tooltipId?: string;
102103
readonly tooltipTemplate?: (task: Task, dateFormat: string) => string;
103104
readonly viewMode?: ViewMode;
104105
readonly width?: number | string;
106+
readonly parsing?: ParsingConfig;
105107
}
106108
export type GanttOptions = GanttOptionsInternal;
107109
export declare const ColumnWidthByMode: Record<ViewMode, number>;

0 commit comments

Comments
 (0)