@fatcore/gantt-lite 1.0.1 → 1.0.2
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 +9 -12
- package/fatcore-gantt-lite-1.0.0.tgz +0 -0
- package/fatcore-gantt-lite-1.0.2.tgz +0 -0
- package/fesm2022/fatcore-gantt-lite.mjs +774 -0
- package/fesm2022/fatcore-gantt-lite.mjs.map +1 -0
- package/fesm2022/gantt-lite.mjs +774 -0
- package/fesm2022/gantt-lite.mjs.map +1 -0
- package/gantt-lite-1.0.2.tgz +0 -0
- package/index.d.ts +180 -0
- package/package.json +17 -41
- package/.editorconfig +0 -16
- package/.vscode/extensions.json +0 -4
- package/.vscode/launch.json +0 -20
- package/.vscode/tasks.json +0 -42
- package/angular.json +0 -165
- package/my-workspace-0.0.0.tgz +0 -0
- package/projects/gantt-lite/README.md +0 -24
- package/projects/gantt-lite/ng-package.json +0 -7
- package/projects/gantt-lite/package.json +0 -10
- package/projects/gantt-lite/src/gantt-lite.module.ts +0 -10
- package/projects/gantt-lite/src/lib/gantt-lite-base.ts +0 -300
- package/projects/gantt-lite/src/lib/gantt-lite.component.html +0 -128
- package/projects/gantt-lite/src/lib/gantt-lite.component.scss +0 -323
- package/projects/gantt-lite/src/lib/gantt-lite.component.ts +0 -391
- package/projects/gantt-lite/src/lib/gantt-lite.helper.ts +0 -124
- package/projects/gantt-lite/src/lib/gantt-lite.model.ts +0 -56
- package/projects/gantt-lite/src/public-api.ts +0 -5
- package/projects/gantt-lite/tsconfig.lib.json +0 -14
- package/projects/gantt-lite/tsconfig.lib.prod.json +0 -10
- package/projects/gantt-lite/tsconfig.spec.json +0 -14
- package/tsconfig.json +0 -37
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# GanttLite
|
|
2
|
-
|
|
3
|
-
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0.
|
|
4
|
-
|
|
5
|
-
## Code scaffolding
|
|
6
|
-
|
|
7
|
-
Run `ng generate component component-name --project gantt-lite` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project gantt-lite`.
|
|
8
|
-
> Note: Don't forget to add `--project gantt-lite` or else it will be added to the default project in your `angular.json` file.
|
|
9
|
-
|
|
10
|
-
## Build
|
|
11
|
-
|
|
12
|
-
Run `ng build gantt-lite` to build the project. The build artifacts will be stored in the `dist/` directory.
|
|
13
|
-
|
|
14
|
-
## Publishing
|
|
15
|
-
|
|
16
|
-
After building your library with `ng build gantt-lite`, go to the dist folder `cd dist/gantt-lite` and run `npm publish`.
|
|
17
|
-
|
|
18
|
-
## Running unit tests
|
|
19
|
-
|
|
20
|
-
Run `ng test gantt-lite` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
|
21
|
-
|
|
22
|
-
## Further help
|
|
23
|
-
|
|
24
|
-
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { NgModule } from "@angular/core";
|
|
2
|
-
import { GanttLiteComponent } from "./lib/gantt-lite.component";
|
|
3
|
-
import { CommonModule } from "@angular/common";
|
|
4
|
-
|
|
5
|
-
@NgModule({
|
|
6
|
-
declarations: [GanttLiteComponent],
|
|
7
|
-
imports: [CommonModule],
|
|
8
|
-
exports: [GanttLiteComponent]
|
|
9
|
-
})
|
|
10
|
-
export class GanttLiteModule {}
|
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
import { Directive, Input } from '@angular/core';
|
|
2
|
-
import { DataType, DateMode, DependencyPath, GanttColumn, GanttScale, GanttTask, ScaleConfig } from './gantt-lite.model';
|
|
3
|
-
import { TimelineSlot } from './gantt-lite.model';
|
|
4
|
-
|
|
5
|
-
@Directive()
|
|
6
|
-
export abstract class GanttLiteBase {
|
|
7
|
-
@Input() public rowHeight: number = 40;
|
|
8
|
-
@Input() public defaultScale: GanttScale = 'day';
|
|
9
|
-
@Input() public dataType: DataType = 'duration';
|
|
10
|
-
@Input() public dateMode: DateMode = 'local';
|
|
11
|
-
@Input() public extraColumns: GanttColumn[] = [];
|
|
12
|
-
protected slots: TimelineSlot[] = [];
|
|
13
|
-
protected tasks: GanttTask[] = [];
|
|
14
|
-
public dependencyPaths: DependencyPath[] = [];
|
|
15
|
-
protected scaleSelected: ScaleConfig = this.getScaleConfig(this.defaultScale);
|
|
16
|
-
protected headerHeight: number = 32;
|
|
17
|
-
protected ganttHeight: number = 0;
|
|
18
|
-
protected ganttWidth: number = 0;
|
|
19
|
-
protected leftPanelWidth = 320;
|
|
20
|
-
protected visibleGridColumns: GanttColumn[] = [];
|
|
21
|
-
protected visibleTooltipColumns: GanttColumn[] = [];
|
|
22
|
-
protected layout = { rootMaxHeight: 0, bodyHeight: 0, scrollHeight: 0, resizerHeight: 0 };
|
|
23
|
-
protected readonly scales: GanttScale[] = ['1m', '10m', '30m', 'hour', '6 hours', '12 hours', 'day', 'week', 'month', 'year'];
|
|
24
|
-
protected readonly scalesDate: GanttScale[] = ['1m', '10m', '30m', 'hour', 'day', 'week', 'month', 'year'];
|
|
25
|
-
protected readonly negativeSlotWidth = 50;
|
|
26
|
-
protected hasNegativeDependencySpace = false;
|
|
27
|
-
|
|
28
|
-
constructor() { }
|
|
29
|
-
|
|
30
|
-
// recompute all sizes
|
|
31
|
-
protected recomputeLayout(): void {
|
|
32
|
-
this.layout.rootMaxHeight = this.ganttHeight - this.headerHeight + this.rowHeight - 3;
|
|
33
|
-
this.layout.bodyHeight = this.ganttHeight - this.rowHeight / 3;
|
|
34
|
-
this.layout.scrollHeight = this.ganttHeight - this.rowHeight;
|
|
35
|
-
this.layout.resizerHeight = this.ganttHeight + this.headerHeight - this.rowHeight;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
protected get columns(): GanttColumn[] {
|
|
39
|
-
return [
|
|
40
|
-
...this.coreColumns,
|
|
41
|
-
...this.extraColumns
|
|
42
|
-
];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// minimum columns to show gantt
|
|
46
|
-
protected readonly coreColumns: GanttColumn[] = [
|
|
47
|
-
{
|
|
48
|
-
id: 'label',
|
|
49
|
-
title: 'Name',
|
|
50
|
-
valueGetter: t => t.label,
|
|
51
|
-
showInGrid: true,
|
|
52
|
-
showInTooltip: true
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
id: 'start',
|
|
56
|
-
title: 'Start',
|
|
57
|
-
valueGetter: t => this.getDateString(t.start),
|
|
58
|
-
showInGrid: true,
|
|
59
|
-
showInTooltip: true,
|
|
60
|
-
hidden: () => this.dataType !== 'calendar'
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
id: 'end',
|
|
64
|
-
title: 'End',
|
|
65
|
-
valueGetter: t => this.getDateString(t.end),
|
|
66
|
-
showInGrid: true,
|
|
67
|
-
showInTooltip: true,
|
|
68
|
-
hidden: () => this.dataType !== 'calendar'
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
id: 'duration',
|
|
72
|
-
title: 'Duration',
|
|
73
|
-
valueGetter: t => this.getDuration(t),
|
|
74
|
-
showInGrid: true,
|
|
75
|
-
showInTooltip: true,
|
|
76
|
-
hidden: () => this.dataType !== 'duration'
|
|
77
|
-
}
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
protected ganttWidthCalcul(): void {
|
|
81
|
-
this.ganttWidth = this.slots.length * this.scaleSelected.px;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
protected ganttHeightCalcul(): void {
|
|
85
|
-
const max = Math.max(...this.tasks.map((t) => t.row));
|
|
86
|
-
this.ganttHeight = (max + 2) * this.rowHeight;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
protected refreshVisibleColumns(): void {
|
|
90
|
-
this.visibleGridColumns = this.columns.filter(c =>
|
|
91
|
-
c.showInGrid &&
|
|
92
|
-
(!c.hidden || !c.hidden())
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
this.visibleTooltipColumns = this.columns.filter(c =>
|
|
96
|
-
c.showInTooltip &&
|
|
97
|
-
(!c.hidden || !c.hidden())
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
protected getRawTaskX(task: GanttTask): number {
|
|
102
|
-
return (task.startSlot ?? 0) * this.scaleSelected.px;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
protected getDateString(date: Date | number | undefined | null): string {
|
|
106
|
-
if (!date) return '';
|
|
107
|
-
if (this.dateMode === 'utc') {
|
|
108
|
-
return (date as Date)
|
|
109
|
-
.toLocaleString([], {
|
|
110
|
-
timeZone: 'UTC',
|
|
111
|
-
day: '2-digit',
|
|
112
|
-
month: '2-digit',
|
|
113
|
-
year: 'numeric',
|
|
114
|
-
hour: '2-digit',
|
|
115
|
-
minute: '2-digit',
|
|
116
|
-
hour12: false,
|
|
117
|
-
})
|
|
118
|
-
.replace(',', '');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return (date as Date)
|
|
122
|
-
.toLocaleString([], {
|
|
123
|
-
day: '2-digit',
|
|
124
|
-
month: '2-digit',
|
|
125
|
-
year: 'numeric',
|
|
126
|
-
hour: '2-digit',
|
|
127
|
-
minute: '2-digit',
|
|
128
|
-
hour12: false,
|
|
129
|
-
})
|
|
130
|
-
.replace(',', '');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
protected getDuration(task: GanttTask | undefined | null): string {
|
|
134
|
-
if (!task) return '';
|
|
135
|
-
|
|
136
|
-
if (typeof task.start !== 'number' || typeof task.end !== 'number') {
|
|
137
|
-
throw new Error(`Task ${task.id} is not in duration mode`);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
let total = task.end - task.start;
|
|
141
|
-
const days = Math.floor(total / 86400);
|
|
142
|
-
total %= 86400;
|
|
143
|
-
const hours = Math.floor(total / 3600);
|
|
144
|
-
total %= 3600;
|
|
145
|
-
const minutes = Math.floor(total / 60);
|
|
146
|
-
const seconds = total % 60;
|
|
147
|
-
let result = '';
|
|
148
|
-
|
|
149
|
-
if (days > 0) result += `${days}d `;
|
|
150
|
-
if (hours > 0 || days > 0) result += `${hours}h `;
|
|
151
|
-
if (minutes > 0 || hours > 0 || days > 0) result += `${minutes}m `;
|
|
152
|
-
|
|
153
|
-
result += `${seconds}s`;
|
|
154
|
-
return result.trim();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
protected getTaskWidth(task: GanttTask): number {
|
|
158
|
-
return ((task.endSlot ?? 0) - (task.startSlot ?? 0)) * this.scaleSelected.px;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
protected getGridTop(i: number): number {
|
|
162
|
-
return i * this.rowHeight;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
protected getTaskX(task: GanttTask): number {
|
|
166
|
-
const base = (task.startSlot ?? 0) * this.scaleSelected.px;
|
|
167
|
-
return this.hasNegativeDependencySpace ? this.negativeSlotWidth + base : base;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
protected getTaskY(task: GanttTask): number {
|
|
171
|
-
return task.row * this.rowHeight + this.rowHeight / 10;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
public trackByIndex(index: number): number {
|
|
175
|
-
return index;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// get per arrow the path
|
|
179
|
-
protected getDependencyPath(fromId: string, toId: string): string {
|
|
180
|
-
const from = this.tasks.find((t) => t.id === fromId);
|
|
181
|
-
const to = this.tasks.find((t) => t.id === toId);
|
|
182
|
-
if (!from || !to) return '';
|
|
183
|
-
const fromRight = this.getTaskX(from) + this.getTaskWidth(from);
|
|
184
|
-
const toLeft = this.getTaskX(to);
|
|
185
|
-
const fromY = this.getTaskY(from) + this.rowHeight / 2 - this.rowHeight / 12;
|
|
186
|
-
const toY = this.getTaskY(to) + this.rowHeight / 2 - this.rowHeight / 12;
|
|
187
|
-
|
|
188
|
-
if (from.row === to.row) {
|
|
189
|
-
return `
|
|
190
|
-
M ${fromRight} ${fromY}
|
|
191
|
-
H ${fromRight + 20}
|
|
192
|
-
V ${toY}
|
|
193
|
-
H ${toLeft}`;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// consistent U-turn style (even if not strictly needed)
|
|
197
|
-
const escapeY = toY - this.rowHeight * 0.54;
|
|
198
|
-
const midX1 = fromRight + 25;
|
|
199
|
-
const midX2 = toLeft - 25;
|
|
200
|
-
return `
|
|
201
|
-
M ${fromRight} ${fromY}
|
|
202
|
-
H ${midX1}
|
|
203
|
-
V ${escapeY}
|
|
204
|
-
H ${midX2}
|
|
205
|
-
V ${toY}
|
|
206
|
-
H ${toLeft}`;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// size of columns per minutes
|
|
210
|
-
protected getScaleConfig(scale: any): ScaleConfig {
|
|
211
|
-
switch (scale) {
|
|
212
|
-
case '1m':
|
|
213
|
-
return { secondes: 60, px: 120 };
|
|
214
|
-
case '10m':
|
|
215
|
-
return { secondes: 600, px: 120 };
|
|
216
|
-
case '30m':
|
|
217
|
-
return { secondes: 1800, px: 120 };
|
|
218
|
-
case 'hour':
|
|
219
|
-
return { secondes: 3600, px: 120 };
|
|
220
|
-
case '6 hours':
|
|
221
|
-
return { secondes: 21600, px: 120 };
|
|
222
|
-
case '12 hours':
|
|
223
|
-
return { secondes: 43200, px: 120 };
|
|
224
|
-
case 'day':
|
|
225
|
-
return { secondes: 86400, px: 150 };
|
|
226
|
-
case 'week':
|
|
227
|
-
return { secondes: 604800, px: 200 };
|
|
228
|
-
case 'month':
|
|
229
|
-
return { secondes: 2592000, px: 350 };
|
|
230
|
-
case 'year':
|
|
231
|
-
return { secondes: 31536000, px: 500 };
|
|
232
|
-
default:
|
|
233
|
-
return { secondes: 1800, px: 120 };
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
protected alignOriginByScale(date: Date, defaultScale: GanttScale): Date {
|
|
238
|
-
const d = new Date(date);
|
|
239
|
-
switch (defaultScale) {
|
|
240
|
-
case 'week': {
|
|
241
|
-
// align monday
|
|
242
|
-
const d = new Date(date);
|
|
243
|
-
const day = d.getDay(); // 0 = Sunday, 1 = Monday, ...
|
|
244
|
-
const diff = day === 0 ? -6 : 1 - day; // move back to Monday
|
|
245
|
-
d.setDate(d.getDate() + diff);
|
|
246
|
-
d.setHours(0, 0, 0, 0);
|
|
247
|
-
return d
|
|
248
|
-
}
|
|
249
|
-
case 'month': {
|
|
250
|
-
d.setDate(1);
|
|
251
|
-
d.setHours(0, 0, 0, 0);
|
|
252
|
-
return d;
|
|
253
|
-
}
|
|
254
|
-
case 'year': {
|
|
255
|
-
d.setMonth(0, 1); // Jan 1
|
|
256
|
-
d.setHours(0, 0, 0, 0);
|
|
257
|
-
return d;
|
|
258
|
-
}
|
|
259
|
-
case 'day': {
|
|
260
|
-
// shift back by 1 day (buffer slot)
|
|
261
|
-
d.setDate(d.getDate() - 1);
|
|
262
|
-
d.setHours(0, 0, 0, 0);
|
|
263
|
-
return d;
|
|
264
|
-
}
|
|
265
|
-
case 'hour': {
|
|
266
|
-
// shift back by 1 hour
|
|
267
|
-
d.setHours(d.getHours() - 1);
|
|
268
|
-
d.setMinutes(0, 0, 0);
|
|
269
|
-
return d;
|
|
270
|
-
}
|
|
271
|
-
case '6 hours': {
|
|
272
|
-
d.setHours(d.getHours() - 6);
|
|
273
|
-
d.setMinutes(0, 0, 0);
|
|
274
|
-
return d;
|
|
275
|
-
}
|
|
276
|
-
case '12 hours': {
|
|
277
|
-
d.setHours(d.getHours() - 12);
|
|
278
|
-
d.setMinutes(0, 0, 0);
|
|
279
|
-
return d;
|
|
280
|
-
}
|
|
281
|
-
case '30m': {
|
|
282
|
-
d.setMinutes(d.getMinutes() - 30);
|
|
283
|
-
d.setSeconds(0, 0);
|
|
284
|
-
return d;
|
|
285
|
-
}
|
|
286
|
-
case '10m': {
|
|
287
|
-
d.setMinutes(d.getMinutes() - 10);
|
|
288
|
-
d.setSeconds(0, 0);
|
|
289
|
-
return d;
|
|
290
|
-
}
|
|
291
|
-
case '1m': {
|
|
292
|
-
d.setMinutes(d.getMinutes() - 1);
|
|
293
|
-
d.setSeconds(0, 0);
|
|
294
|
-
return d;
|
|
295
|
-
}
|
|
296
|
-
default:
|
|
297
|
-
return d;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
<div class="gantt-root" [style.max-height.px]="layout.rootMaxHeight" [style.min-height.px]="100">
|
|
2
|
-
<!-- TOOLBAR -->
|
|
3
|
-
<div class="toolbar" [style.height.px]="buttonsHeight">
|
|
4
|
-
<div *ngIf="dateMode === 'utc' || dataType === 'duration'">
|
|
5
|
-
<button class="gantt-button" *ngFor="let s of scales" (click)="setScale(s)" [class.active]="defaultScale === s">
|
|
6
|
-
{{ s }}
|
|
7
|
-
</button>
|
|
8
|
-
</div>
|
|
9
|
-
<div *ngIf="dateMode !== 'utc' && dataType === 'calendar'">
|
|
10
|
-
<button class="gantt-button" *ngFor="let s of scalesDate" (click)="setScale(s)"
|
|
11
|
-
[class.active]="defaultScale === s">
|
|
12
|
-
{{ s }}
|
|
13
|
-
</button>
|
|
14
|
-
</div>
|
|
15
|
-
<label *ngIf="maxSlotsAlert" style="color: red;">
|
|
16
|
-
Data is too massive to show Gantt smoothly, try changing the scale
|
|
17
|
-
</label>
|
|
18
|
-
</div>
|
|
19
|
-
<!-- TOOLTIP -->
|
|
20
|
-
<div class="global-tooltip" [style.display]="tooltipTask ? 'block' : 'none'">
|
|
21
|
-
<ng-container *ngIf="tooltipTask as task">
|
|
22
|
-
<div *ngFor="let column of visibleTooltipColumns">
|
|
23
|
-
<strong>{{ column?.title }}:</strong>
|
|
24
|
-
{{ column?.valueGetter(task) }}
|
|
25
|
-
</div>
|
|
26
|
-
</ng-container>
|
|
27
|
-
</div>
|
|
28
|
-
<!-- BUTTONS FOR SCROLL GANTT -->
|
|
29
|
-
<div *ngIf="!maxSlotsAlert && loading === false" class="floating-nav">
|
|
30
|
-
<button class="nav-btn left" (mousedown)="startScroll('left')"
|
|
31
|
-
(mouseup)="stopScroll()" (mouseleave)="stopScroll()" #leftBtn >
|
|
32
|
-
‹
|
|
33
|
-
</button>
|
|
34
|
-
<button class="nav-btn right" (mousedown)="startScroll('right')"
|
|
35
|
-
(mouseup)="stopScroll()" (mouseleave)="stopScroll()" #rightBtn>
|
|
36
|
-
›
|
|
37
|
-
</button>
|
|
38
|
-
</div>
|
|
39
|
-
<!-- LOADING -->
|
|
40
|
-
<div class="loading-overlay" *ngIf="loading" [style.top]="'50%'">
|
|
41
|
-
<div class="spinner"></div>
|
|
42
|
-
</div>
|
|
43
|
-
<!-- SINGLE VERTICAL SCROLL -->
|
|
44
|
-
<div class="gantt-body" #ganttBodyScroll>
|
|
45
|
-
<!-- LEFT PANEL -->
|
|
46
|
-
<div class="left-panel" #leftPanel>
|
|
47
|
-
<div class="task-row header" [style.height.px]="headerHeight">
|
|
48
|
-
<div class="cell" *ngFor="let column of visibleGridColumns" [style.width.px]="column.width || 120">
|
|
49
|
-
{{ column?.title }}
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
<div class="task-row" *ngFor="let task of tasks" [style.height.px]="rowHeight"
|
|
53
|
-
(click)="selectTask(task, true); $event.stopPropagation()"
|
|
54
|
-
[class.highlighted-row]="selectedTaskId === task.id">
|
|
55
|
-
<div class="cell" *ngFor="let column of visibleGridColumns" [style.width.px]="column.width || 120">
|
|
56
|
-
{{ column?.valueGetter(task) }}
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
<!-- RESIZER -->
|
|
61
|
-
<div class="resizer" [style.height.px]="layout.resizerHeight" (mousedown)="startResize($event)">
|
|
62
|
-
</div>
|
|
63
|
-
<!-- RIGHT PANEL -->
|
|
64
|
-
<div class="right-panel" *ngIf="!maxSlotsAlert">
|
|
65
|
-
<!-- TIMELINE -->
|
|
66
|
-
<div class="timeline-header" #timelineHeader>
|
|
67
|
-
<div class="timeline-slot" *ngFor="let slot of slots" [style.width.px]="slot.width">
|
|
68
|
-
{{ slot.label }}
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
<!-- HORIZONTAL SCROLL ONLY -->
|
|
72
|
-
<div class="horizontal-scroll" (scroll)="onHorizontalScroll($event)" #ganttScroll>
|
|
73
|
-
<div class="gantt-content" #ganttContent>
|
|
74
|
-
<!-- GRID -->
|
|
75
|
-
<div class="grid-layer">
|
|
76
|
-
<div *ngFor="let slot of slots; let i = index; trackBy: trackByIndex" class="grid-line"
|
|
77
|
-
[style.left.px]="slot.x - 1">
|
|
78
|
-
</div>
|
|
79
|
-
<!-- final vertical line -->
|
|
80
|
-
<div class="grid-line" [style.left.px]="ganttWidth - 1">
|
|
81
|
-
</div>
|
|
82
|
-
<!-- horizontal lines -->
|
|
83
|
-
<div class="horizontal-grid-layer">
|
|
84
|
-
<div *ngFor="let task of tasks; let i = index; trackBy: trackByIndex"
|
|
85
|
-
class="horizontal-line" [style.top.px]="getGridTop(i)">
|
|
86
|
-
</div>
|
|
87
|
-
<!-- final horizontal line -->
|
|
88
|
-
<div class="horizontal-line" [style.top.px]="ganttHeight">
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
<!-- TASKS -->
|
|
93
|
-
<div class="tasks-layer">
|
|
94
|
-
<div *ngFor="let task of viewTasks; let i = index; trackBy: trackByIndex" class="task"
|
|
95
|
-
(mouseenter)="showTooltip($event, task)" (mouseleave)="hideTooltip()"
|
|
96
|
-
(click)="selectTask(task, false); $event.stopPropagation()"
|
|
97
|
-
[class.highlighted-task]="selectedTaskId === task.id" [style.left.px]="task.x"
|
|
98
|
-
[style.top.px]="task.y" [style.width.px]="task.width" [style.height.px]="task.height">
|
|
99
|
-
<div class="task-label">
|
|
100
|
-
{{ task.label }}
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
<!-- DEPENDENCIES -->
|
|
105
|
-
<svg class="dependencies-svg">
|
|
106
|
-
<defs>
|
|
107
|
-
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6"
|
|
108
|
-
orient="auto">
|
|
109
|
-
<path d="M 0 0 L 10 5 L 0 10 z" fill="#868d97" />
|
|
110
|
-
</marker>
|
|
111
|
-
</defs>
|
|
112
|
-
<g class="dependencies-normal">
|
|
113
|
-
<path *ngFor="let dep of dependencyPaths" [attr.d]="dep.path" class="dependency"
|
|
114
|
-
[class.hidden]="hoveredDependencyIds.has(dep.id)" marker-end="url(#arrow)">
|
|
115
|
-
</path>
|
|
116
|
-
</g>
|
|
117
|
-
<g class="dependencies-highlighted">
|
|
118
|
-
<path *ngFor="let dep of dependencyPaths" [attr.d]="dep.path" class="highlighted-dependency"
|
|
119
|
-
[class.hidden]="!hoveredDependencyIds.has(dep.id)" marker-end="url(#arrow)">
|
|
120
|
-
</path>
|
|
121
|
-
</g>
|
|
122
|
-
</svg>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
|
-
<div class="right-panel-dead" *ngIf="maxSlotsAlert"></div>
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|