Skip to content

Commit

Permalink
fix(links): add line click
Browse files Browse the repository at this point in the history
  • Loading branch information
HandsomeButterball committed Jun 11, 2020
1 parent 359d688 commit 3fb5a15
Show file tree
Hide file tree
Showing 15 changed files with 110 additions and 64 deletions.
1 change: 1 addition & 0 deletions example/src/app/examples/examples.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
[draggable]="options.draggable"
[linkable]="options.linkable"
(barClick)="barClick($event)"
(lineClick)="lineClick($event)"
(dragEnded)="dragEnded($event)"
(loadOnScroll)="loadOnScroll($event)"
>
Expand Down
6 changes: 5 additions & 1 deletion example/src/app/examples/examples.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, OnInit, HostBinding } from '@angular/core';
import { mockItems, mockGroups } from './mocks';
import { GanttBarClickEvent, GanttViewType, GanttDragEvent, GanttLoadOnScrollEvent } from 'ngx-gantt';
import { GanttBarClickEvent, GanttViewType, GanttDragEvent, GanttLoadOnScrollEvent, GanttLineClickEvent } from 'ngx-gantt';

@Component({
selector: 'app-examples-gantt',
Expand Down Expand Up @@ -28,6 +28,10 @@ export class AppExamplesComponent implements OnInit {
console.log(event);
}

lineClick(event: GanttLineClickEvent) {
console.log(event);
}

dragEnded(event: GanttDragEvent) {
this.groups = [...this.groups];
this.items = [...this.items];
Expand Down
3 changes: 2 additions & 1 deletion example/src/app/examples/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ export const mockItems = [
title: 'VERSION 0204',
start: 1591035675,
end: 1592418400,
group_id: '00002'
group_id: '00002',
links: ['item-0301', 'item-0402']
},

{
Expand Down
2 changes: 1 addition & 1 deletion packages/gantt/src/class/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class GanttLoadOnScrollEvent {
end: number;
}

export class GanttLinkEvent<T = unknown> {
export class GanttLineClickEvent<T = unknown> {
event: MouseEvent;
source: GanttItem;
target: GanttItem;
Expand Down
4 changes: 2 additions & 2 deletions packages/gantt/src/components/links/links.component.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<svg [attr.width]="gantt.view.width" [attr.height]="'100%'">
<svg [attr.width]="gantt.view.width" height="1">
<ng-container *ngFor="let link of links; let i = index; trackBy: trackBy">
<path [attr.d]="link.path" fill="transparent" stroke-width="2" [attr.stroke]="link.color" pointer-events="none"></path>
<g>
<path
class="link-line"
(click)="onLinkClick($event, link)"
(click)="onLineClick($event, link)"
(mouseenter)="mouseEnterPath(link)"
(mouseleave)="mouseLeavePath(link)"
[attr.d]="link.path"
Expand Down
6 changes: 5 additions & 1 deletion packages/gantt/src/components/links/links.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
left: 0px;
width: 100%;
height: 100%;
overflow: hidden;
svg {
overflow: visible;
z-index: 1;
position: absolute;
}
}

.link-dragging-container {
Expand Down
94 changes: 51 additions & 43 deletions packages/gantt/src/components/links/links.component.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { Component, OnInit, Input, Output, EventEmitter, HostBinding, Inject, ChangeDetectorRef, ElementRef } from '@angular/core';
import { GanttGroupInternal } from '../../class/group';
import { GanttItemInternal, GanttItem } from './../../class/item';
import { GanttLinkEvent } from '../../class/event';
import { GanttLineClickEvent } from '../../class/event';
import { GANTT_REF_TOKEN, GanttRef } from '../../gantt-ref';
import { GanttDragContainer } from '../../gantt-drag-container';
import { merge } from 'rxjs';
import { headerHeight, defaultStyles } from '../../gantt.styles';

enum LinkColors {
default = '#cacaca',
blocked = '#FF7575',
active = '#348FE4'
}

interface GanttLinkItem {
id: string;
before: { x: number; y: number };
after: { x: number; y: number };
origin: GanttItem;
links: string[];
}

interface LinkInternal {
path: string;
source: GanttItem;
Expand All @@ -29,10 +36,12 @@ export class GanttLinksComponent implements OnInit {

@Input() items: GanttItemInternal[] = [];

@Output() linkClick = new EventEmitter<GanttLinkEvent>();
@Output() lineClick = new EventEmitter<GanttLineClickEvent>();

public links: LinkInternal[] = [];

private linkItems: GanttLinkItem[] = [];

private bezierWeight = -0.5;

private firstChange = true;
Expand Down Expand Up @@ -69,43 +78,49 @@ export class GanttLinksComponent implements OnInit {
private computeItemPosition() {
const lineHeight = this.gantt.styles.lineHeight;
const barHeight = this.gantt.styles.barHeight;
this.links = [];
this.linkItems = [];
if (this.groups.length > 0) {
let itemNum = 0;
let groupNum = 0;
this.groups.forEach((group) => {
groupNum++;
if (group.expand) {
group.items.forEach((item, itemIndex) => {
const y = headerHeight + (groupNum + itemNum + itemIndex) * lineHeight + item.refs.y + barHeight / 2;
item.before = {
x: item.refs.x,
y
};
item.after = {
x: item.refs.x + item.refs.width,
y
};
const y = (groupNum + itemNum + itemIndex) * lineHeight + item.refs.y + barHeight / 2;
this.linkItems.push({
...item,
before: {
x: item.refs.x,
y
},
after: {
x: item.refs.x + item.refs.width,
y
}
});
});
itemNum += group.items.length;
}
});
} else {
this.items.forEach((item, itemIndex) => {
const y = headerHeight + itemIndex * lineHeight + item.refs.y + barHeight / 2;
item.before = {
x: item.refs.x,
y
};
item.after = {
x: item.refs.x + item.refs.width,
y
};
const y = itemIndex * lineHeight + item.refs.y + barHeight / 2;
this.linkItems.push({
...item,
before: {
x: item.refs.x,
y
},
after: {
x: item.refs.x + item.refs.width,
y
}
});
});
}
}

private generatePath(source: GanttItemInternal, target: GanttItemInternal) {
private generatePath(source: GanttLinkItem, target: GanttLinkItem) {
if (source.before && source.after && target.before && target.after) {
const x1 = source.after.x;
const y1 = source.after.y;
Expand All @@ -117,31 +132,24 @@ export class GanttLinksComponent implements OnInit {
const x2 = x1 - dx;
const x3 = x4 + dx;

if (y1 === y4) {
if (source.origin.end < target.origin.start) {
return `M ${x1} ${y1} C ${x2} ${y1} ${x3} ${y4} ${x4} ${y4}`;
} else {
const dx2 = Math.abs(x4 - x1) * 0.1;
return `M ${x1} ${y1} C ${x1} ${y1 + dx2} ${x4} ${y1 + dx2} ${x4} ${y4}`;
}
} else {
return `M ${x1} ${y1} C ${x2} ${y1} ${x3} ${y4} ${x4} ${y4}`;
}
return `M ${x1} ${y1} C ${x2} ${y1} ${x3} ${y4} ${x4} ${y4}`;
}
}

buildLinks() {
this.computeItemPosition();
this.links = [];
this.items.forEach((source) => {
this.linkItems.forEach((source) => {
source.links.forEach((linkId) => {
const target = this.items.find((item) => item.id === linkId);
this.links.push({
path: this.generatePath(source, target),
source: source.origin,
target: target.origin,
color: source.origin.end > target.origin.start ? LinkColors.blocked : LinkColors.default
});
const target = this.linkItems.find((item) => item.id === linkId);
if (target) {
this.links.push({
path: this.generatePath(source, target),
source: source.origin,
target: target.origin,
color: source.origin.end > target.origin.start ? LinkColors.blocked : LinkColors.default
});
}
});
});
}
Expand All @@ -150,8 +158,8 @@ export class GanttLinksComponent implements OnInit {
return index;
}

onLinkClick(event: MouseEvent, link: LinkInternal) {
this.linkClick.emit({
onLineClick(event: MouseEvent, link: LinkInternal) {
this.lineClick.emit({
event,
source: link.source,
target: link.target
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<gantt-links-overlay [groups]="groups" [items]="items" *ngIf="ganttRef.linkable" (lineClick)="lineClick.emit($event)"></gantt-links-overlay>
<!-- groups -->
<div class="gantt-main-groups" *ngIf="groups && groups.length > 0; else itemsTemplate" [style.width.px]="ganttRef.view.width">
<ng-container *ngFor="let group of groups; trackBy: trackBy">
Expand Down
9 changes: 7 additions & 2 deletions packages/gantt/src/components/main/gantt-main.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, OnInit, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter } from '@angular/core';
import { GanttGroupInternal, GanttItemInternal, GanttBarClickEvent } from '../../class';
import { Component, OnInit, HostBinding, Inject, Input, TemplateRef, Output, EventEmitter, ViewChild } from '@angular/core';
import { GanttGroupInternal, GanttItemInternal, GanttBarClickEvent, GanttLineClickEvent } from '../../class';
import { GANTT_REF_TOKEN, GanttRef } from '../../gantt-ref';
import { GanttLinksComponent } from '../links/links.component';

@Component({
selector: 'gantt-main',
Expand All @@ -15,6 +16,10 @@ export class GanttMainComponent implements OnInit {

@Output() barClick = new EventEmitter<GanttBarClickEvent>();

@Output() lineClick = new EventEmitter<GanttLineClickEvent>();

@ViewChild(GanttLinksComponent, { static: false }) links: GanttLinksComponent;

@HostBinding('class.gantt-main-container') ganttMainClass = true;

constructor(@Inject(GANTT_REF_TOKEN) public ganttRef: GanttRef) {}
Expand Down
5 changes: 4 additions & 1 deletion packages/gantt/src/components/table/gantt-table.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, HostBinding, TemplateRef, QueryList, Input, OnInit, Inject, ElementRef } from '@angular/core';
import { Component, HostBinding, TemplateRef, QueryList, Input, OnInit, Inject, ElementRef, Output, EventEmitter } from '@angular/core';
import { GanttItemInternal, GanttGroupInternal } from '../../class';
import { GANTT_REF_TOKEN, GanttRef } from '../../gantt-ref';
import { NgxGanttTableColumnComponent } from '../../table/gantt-column.component';
Expand All @@ -21,6 +21,8 @@ export class GanttTableComponent implements OnInit {

@Input() groupTemplate: TemplateRef<any>;

@Output() expandChange = new EventEmitter<null>();

@HostBinding('class.gantt-table') ganttTableClass = true;

constructor(@Inject(GANTT_REF_TOKEN) public ganttRef: GanttRef, private elementRef: ElementRef) {}
Expand All @@ -29,6 +31,7 @@ export class GanttTableComponent implements OnInit {

expandGroup(group: GanttGroupInternal) {
group.expand = !group.expand;
this.expandChange.emit();
this.ganttRef.detectChanges();
}
}
4 changes: 1 addition & 3 deletions packages/gantt/src/gantt-dom.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ export class GanttDomService implements OnDestroy {
private syncScroll(event: Event) {
const target = event.currentTarget as HTMLElement;
this.calendarOverlay.scrollLeft = this.mainContainer.scrollLeft;
if (this.linksOverlay) {
this.linksOverlay.scrollLeft = this.mainContainer.scrollLeft;
}

this.sideContainer.scrollTop = target.scrollTop;
this.mainContainer.scrollTop = target.scrollTop;
}
Expand Down
1 change: 1 addition & 0 deletions packages/gantt/src/gantt-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface GanttRef {
styles: GanttStyles;
draggable: boolean;
linkable?: boolean;
expandChange(): void;
detectChanges(): void;
}

Expand Down
4 changes: 1 addition & 3 deletions packages/gantt/src/gantt-upper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,9 @@ export abstract class GanttUpper {
this.originItems = uniqBy(this.originItems, 'id');
if (this.groups.length > 0) {
this.originItems.forEach((origin) => {
const itemInternal = new GanttItemInternal(origin);
const group = this.groupsMap[origin.group_id];
this.items.push(itemInternal);
if (group) {
group.items.push(itemInternal);
group.items.push(new GanttItemInternal(origin));
}
});
} else {
Expand Down
18 changes: 15 additions & 3 deletions packages/gantt/src/gantt.component.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
<div class="gantt-side gantt-side-has-shadow" [style.width.px]="sideTableWidth">
<gantt-table [groups]="groups" [items]="items" [columns]="columns" [groupTemplate]="groupTemplate"></gantt-table>
<gantt-table
[groups]="groups"
[items]="items"
[columns]="columns"
[groupTemplate]="groupTemplate"
(expandChange)="expandChange()"
></gantt-table>
</div>
<div class="gantt-container">
<gantt-calendar-overlay></gantt-calendar-overlay>
<gantt-links-overlay [groups]="groups" [items]="items" *ngIf="linkable"></gantt-links-overlay>
<gantt-drag-backdrop></gantt-drag-backdrop>
<gantt-main [groups]="groups" [items]="items" [barTemplate]="barTemplate" (barClick)="barClick.emit($event)"></gantt-main>
<gantt-main
[groups]="groups"
[items]="items"
[barTemplate]="barTemplate"
(barClick)="barClick.emit($event)"
(lineClick)="lineClick.emit($event)"
>
</gantt-main>
</div>
16 changes: 13 additions & 3 deletions packages/gantt/src/gantt.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ import {
TemplateRef,
ContentChildren,
QueryList,
AfterViewInit
AfterViewInit,
ViewChild
} from '@angular/core';
import { startWith, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { GanttUpper } from './gantt-upper';
import { GanttRef, GANTT_REF_TOKEN } from './gantt-ref';
import { GanttLinkDragEvent, GanttLinkEvent, GanttItemInternal, GanttBarClickEvent } from './class';
import { GanttLinkDragEvent, GanttLineClickEvent, GanttItemInternal, GanttBarClickEvent } from './class';
import { GanttDomService } from './gantt-dom.service';
import { GanttDragContainer } from './gantt-drag-container';
import { NgxGanttTableColumnComponent } from './table/gantt-column.component';
import { sideWidth } from './gantt.styles';
import { getColumnWidthConfig } from './utils/column-compute';
import { GanttMainComponent } from './components/main/gantt-main.component';

@Component({
selector: 'ngx-gantt',
Expand All @@ -52,7 +54,9 @@ export class NgxGanttComponent extends GanttUpper implements GanttRef, OnInit, A

@Output() linkDragEnded = new EventEmitter<GanttLinkDragEvent>();

@Output() linkClick = new EventEmitter<GanttLinkEvent>();
@Output() lineClick = new EventEmitter<GanttLineClickEvent>();

@ViewChild(GanttMainComponent, { static: false }) ganttMain: GanttMainComponent;

@ContentChild('group', { static: true }) groupTemplate: TemplateRef<any>;

Expand Down Expand Up @@ -122,6 +126,12 @@ export class NgxGanttComponent extends GanttUpper implements GanttRef, OnInit, A
});
}

expandChange() {
if (this.linkable) {
this.ganttMain.links.buildLinks();
}
}

detectChanges() {
this.cdr.detectChanges();
}
Expand Down

0 comments on commit 3fb5a15

Please sign in to comment.