Skip to content

Commit 2f33f57

Browse files
committed
feat: add drag and dom service
1 parent a25d7b8 commit 2f33f57

25 files changed

+682
-107
lines changed

.prettierrc

+22-26
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,24 @@
11
{
2-
"eslintIntegration": true,
3-
"stylelintIntegration": true,
4-
"tabWidth": 4,
5-
"singleQuote": true,
6-
"semi": true,
7-
"printWidth": 120,
8-
"overrides": [
9-
{
10-
"files": "*.json",
11-
"options": {
12-
"tabWidth": 2
13-
}
14-
},
15-
{
16-
"files": "*.html",
17-
"options": {
18-
"tabWidth": 2
19-
}
20-
},
21-
{
22-
"files": ".prettierrc",
23-
"options": {
24-
"parser": "json"
25-
}
26-
}
27-
]
2+
"eslintIntegration": true,
3+
"stylelintIntegration": true,
4+
"tabWidth": 2,
5+
"semi": true,
6+
"printWidth": 140,
7+
"proseWrap": "preserve",
8+
"trailingComma": "none",
9+
"singleQuote": true,
10+
"overrides": [
11+
{
12+
"files": "*.js",
13+
"options": {
14+
"tabWidth": 4
15+
}
16+
},
17+
{
18+
"files": "*.ts",
19+
"options": {
20+
"tabWidth": 4
21+
}
22+
}
23+
]
2824
}
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<ngx-gantt [start]="1514736000" end="1609430400" [items]="items" [groups]="groups"></ngx-gantt>
1+
<ngx-gantt [start]="1514736000" end="1609430400" [items]="items" [groups]="groups" [draggable]="true" ></ngx-gantt>

packages/gantt/src/bar/bar-drag.ts

+287
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
import { Injectable, ElementRef, OnDestroy, NgZone } from '@angular/core';
2+
import { DragRef, DragDrop } from '@angular/cdk/drag-drop';
3+
import { GanttDomService } from '../gantt-dom.service';
4+
import { GanttDragContainer } from '../gantt-drag-container';
5+
import { GanttItemInternal } from '../class/item';
6+
import { GanttDate, differenceInCalendarDays } from '../utils/date';
7+
import { GanttRef } from '../gantt-ref';
8+
import { fromEvent, Subject } from 'rxjs';
9+
import { takeUntil } from 'rxjs/operators';
10+
11+
const dragMinWidth = 10;
12+
const activeClass = 'gantt-bar-active';
13+
const linkDropClass = 'gantt-bar-dependency-drop';
14+
15+
function createSvgElement(qualifiedName: string, className: string) {
16+
const element = document.createElementNS('http://www.w3.org/2000/svg', qualifiedName);
17+
element.classList.add(className);
18+
return element;
19+
}
20+
21+
@Injectable()
22+
export class GanttBarDrag implements OnDestroy {
23+
private ganttRef: GanttRef;
24+
25+
private barElement: HTMLElement;
26+
27+
private item: GanttItemInternal;
28+
29+
private get dragDisabled() {
30+
return !this.ganttRef.draggable;
31+
}
32+
33+
private get dependencyDragDisabled() {
34+
return !this.ganttRef.linkable;
35+
}
36+
37+
private dependencyDraggingLine: SVGElement;
38+
39+
private barDragRef: DragRef;
40+
41+
private dragRefs: DragRef[] = [];
42+
43+
private destroy$ = new Subject();
44+
45+
constructor(private dragDrop: DragDrop, private dom: GanttDomService, private dragContainer: GanttDragContainer) {}
46+
47+
private createMouseEvents() {
48+
fromEvent(this.barElement, 'mouseenter')
49+
.pipe(takeUntil(this.destroy$))
50+
.subscribe(() => {
51+
if (this.dragContainer.linkDraggingId && this.dragContainer.linkDraggingId !== this.item.id) {
52+
this.barElement.classList.add(linkDropClass);
53+
this.dragContainer.emitLinkDragEntered(this.item);
54+
} else {
55+
this.barElement.classList.add(activeClass);
56+
}
57+
});
58+
59+
fromEvent(this.barElement, 'mouseleave')
60+
.pipe(takeUntil(this.destroy$))
61+
.subscribe(() => {
62+
if (!this.dragContainer.linkDraggingId) {
63+
this.barElement.classList.remove(activeClass);
64+
} else {
65+
this.dragContainer.emitLinkDragLeaved();
66+
}
67+
this.barElement.classList.remove(linkDropClass);
68+
});
69+
}
70+
71+
private createBarDrag() {
72+
const dragRef = this.dragDrop.createDrag(this.barElement);
73+
dragRef.lockAxis = 'x';
74+
dragRef.started.subscribe(() => {
75+
this.setDraggingStyles();
76+
this.dragContainer.dragStarted.emit({ item: this.item.origin });
77+
});
78+
dragRef.moved.subscribe((event) => {
79+
const x = this.item.refs.x + event.distance.x;
80+
const days = differenceInCalendarDays(this.item.end.value, this.item.start.value);
81+
const start = this.ganttRef.view.getDateByXPoint(x);
82+
const end = start.addDays(days);
83+
this.openDragBackdrop(this.barElement, this.ganttRef.view.getDateByXPoint(x), end);
84+
});
85+
dragRef.ended.subscribe((event) => {
86+
const days = differenceInCalendarDays(this.item.end.value, this.item.start.value);
87+
const start = this.ganttRef.view.getDateByXPoint(this.item.refs.x + event.distance.x);
88+
const end = start.addDays(days);
89+
this.item.updateDate(start, end);
90+
this.clearDraggingStyles();
91+
this.closeDragBackdrop();
92+
this.dragContainer.dragEnded.emit({ item: this.item.origin });
93+
event.source.reset();
94+
});
95+
this.barDragRef = dragRef;
96+
return dragRef;
97+
}
98+
99+
private createBarHandleDrags() {
100+
const dragRefs = [];
101+
const handles = this.barElement.querySelectorAll<HTMLElement>('.drag-handles .handle');
102+
handles.forEach((handle, index) => {
103+
const isBefore = index === 0;
104+
const dragRef = this.dragDrop.createDrag(handle);
105+
dragRef.lockAxis = 'x';
106+
dragRef.withBoundaryElement(this.dom.root as HTMLElement);
107+
108+
dragRef.started.subscribe(() => {
109+
this.setDraggingStyles();
110+
this.dragContainer.dragStarted.emit({ item: this.item.origin });
111+
});
112+
113+
dragRef.moved.subscribe((event) => {
114+
if (isBefore) {
115+
const x = this.item.refs.x + event.distance.x;
116+
const width = Math.max(this.item.refs.width + event.distance.x * -1, dragMinWidth);
117+
this.barElement.style.width = width + 'px';
118+
this.barElement.style.left = x + 'px';
119+
this.openDragBackdrop(
120+
this.barElement,
121+
this.ganttRef.view.getDateByXPoint(x),
122+
this.ganttRef.view.getDateByXPoint(x + width)
123+
);
124+
} else {
125+
const width = this.item.refs.width + event.distance.x;
126+
this.barElement.style.width = width + 'px';
127+
this.openDragBackdrop(
128+
this.barElement,
129+
this.ganttRef.view.getDateByXPoint(this.item.refs.x),
130+
this.ganttRef.view.getDateByXPoint(this.item.refs.x + width)
131+
);
132+
}
133+
event.source.reset();
134+
});
135+
136+
dragRef.ended.subscribe((event) => {
137+
if (isBefore) {
138+
const start = this.ganttRef.view.getDateByXPoint(this.item.refs.x + event.distance.x);
139+
this.item.updateDate(start, this.item.end);
140+
} else {
141+
const end = this.ganttRef.view.getDateByXPoint(
142+
this.item.refs.x + this.item.refs.width + event.distance.x
143+
);
144+
this.item.updateDate(this.item.start, end);
145+
}
146+
this.clearDraggingStyles();
147+
this.closeDragBackdrop();
148+
this.dragContainer.dragEnded.emit({ item: this.item.origin });
149+
});
150+
dragRefs.push(dragRef);
151+
});
152+
return dragRefs;
153+
}
154+
155+
private createDependencyHandleDrags() {
156+
const dragRefs = [];
157+
const handles = this.barElement.querySelectorAll<HTMLElement>('.dependency-handles .handle');
158+
handles.forEach((handle, index) => {
159+
const isBefore = index === 0;
160+
const dragRef = this.dragDrop.createDrag(handle);
161+
dragRef.withBoundaryElement(this.dom.root as HTMLElement);
162+
dragRef.beforeStarted.subscribe(() => {
163+
handle.style.pointerEvents = 'none';
164+
if (this.barDragRef) {
165+
this.barDragRef.disabled = true;
166+
}
167+
this.createDependencyDraggingLine();
168+
this.dragContainer.emitLinkDragStarted(isBefore ? 'dependent' : 'source', this.item);
169+
});
170+
171+
dragRef.moved.subscribe(() => {
172+
const positions = this.calcDependencyLinePositions(handle, isBefore);
173+
this.dependencyDraggingLine.setAttribute('x1', positions.x1.toString());
174+
this.dependencyDraggingLine.setAttribute('y1', positions.y1.toString());
175+
this.dependencyDraggingLine.setAttribute('x2', positions.x2.toString());
176+
this.dependencyDraggingLine.setAttribute('y2', positions.y2.toString());
177+
});
178+
179+
dragRef.ended.subscribe((event) => {
180+
event.source.reset();
181+
handle.style.pointerEvents = '';
182+
if (this.barDragRef) {
183+
this.barDragRef.disabled = false;
184+
}
185+
this.barElement.classList.remove(activeClass);
186+
this.destroyDependencyDraggingLine();
187+
this.dragContainer.emitLinkDragEnded();
188+
});
189+
190+
dragRefs.push(dragRef);
191+
});
192+
return dragRefs;
193+
}
194+
195+
private openDragBackdrop(dragElement: HTMLElement, start: GanttDate, end: GanttDate) {
196+
// const dragMaskElement = this.dom.root.querySelector('.gantt-drag-mask') as HTMLElement;
197+
// const dragBackdropElement = this.dom.root.querySelector('.gantt-drag-backdrop') as HTMLElement;
198+
// const rootRect = this.dom.root.getBoundingClientRect();
199+
// const dragRect = dragElement.getBoundingClientRect();
200+
// const left = Math.max(400, dragRect.left - rootRect.left);
201+
// const width = dragRect.right - rootRect.left - left;
202+
// dragMaskElement.style.left = left + 'px';
203+
// dragMaskElement.style.width = width + 'px';
204+
// dragMaskElement.querySelector('.start').innerHTML = start.format('MM-dd');
205+
// dragMaskElement.querySelector('.end').innerHTML = end.format('MM-dd');
206+
// dragMaskElement.style.display = 'block';
207+
// dragBackdropElement.style.display = 'block';
208+
}
209+
210+
private closeDragBackdrop() {
211+
const dragMaskElement = this.dom.root.querySelector('.gantt-drag-mask') as HTMLElement;
212+
const dragBackdropElement = this.dom.root.querySelector('.gantt-drag-backdrop') as HTMLElement;
213+
dragMaskElement.style.display = 'none';
214+
dragBackdropElement.style.display = 'none';
215+
}
216+
217+
private setDraggingStyles() {
218+
this.barElement.style.pointerEvents = 'none';
219+
this.barElement.classList.add('gantt-bar-draggable-drag');
220+
}
221+
222+
private clearDraggingStyles() {
223+
this.barElement.style.pointerEvents = '';
224+
this.barElement.classList.remove('gantt-bar-draggable-drag');
225+
}
226+
227+
private calcDependencyLinePositions(target: HTMLElement, isBefore: boolean) {
228+
const dragHandleWidth = 16;
229+
const container = this.dom.viewer;
230+
const targetRect = target.getBoundingClientRect();
231+
const containerRect = container.getBoundingClientRect();
232+
const refs = this.item.refs;
233+
const appendX = isBefore ? -dragHandleWidth : refs.width + dragHandleWidth;
234+
const appendY = this.ganttRef.styles.barHeight / 2;
235+
return {
236+
x1: refs.x + appendX - container.scrollLeft,
237+
y1: refs.y + appendY - container.scrollTop,
238+
x2: targetRect.left + targetRect.width / 2 - containerRect.left,
239+
y2: targetRect.top + targetRect.height / 2 - containerRect.top,
240+
};
241+
}
242+
243+
private createDependencyDraggingLine() {
244+
if (!this.dependencyDraggingLine) {
245+
const svgElement = createSvgElement('svg', 'dependency-dragging-container');
246+
const linElement = createSvgElement('line', 'dependency-dragging-line');
247+
svgElement.appendChild(linElement);
248+
this.dom.root.appendChild(svgElement);
249+
this.dependencyDraggingLine = linElement;
250+
}
251+
}
252+
253+
private destroyDependencyDraggingLine() {
254+
if (this.dependencyDraggingLine) {
255+
this.dependencyDraggingLine.parentElement.remove();
256+
this.dependencyDraggingLine = null;
257+
}
258+
}
259+
260+
createDrags(elementRef: ElementRef, item: GanttItemInternal, ganttRef: GanttRef) {
261+
this.item = item;
262+
this.barElement = elementRef.nativeElement;
263+
this.ganttRef = ganttRef;
264+
265+
if (this.dragDisabled && this.dependencyDragDisabled) {
266+
return;
267+
} else {
268+
this.createMouseEvents();
269+
if (!this.dragDisabled) {
270+
const dragRef = this.createBarDrag();
271+
const dragHandlesRefs = this.createBarHandleDrags();
272+
this.dragRefs.push(dragRef, ...dragHandlesRefs);
273+
}
274+
if (!this.dependencyDragDisabled) {
275+
const dependencyDragRefs = this.createDependencyHandleDrags();
276+
this.dragRefs.push(...dependencyDragRefs);
277+
}
278+
}
279+
}
280+
281+
ngOnDestroy() {
282+
this.closeDragBackdrop();
283+
this.dragRefs.forEach((dragRef) => dragRef.dispose());
284+
this.destroy$.next();
285+
this.destroy$.complete();
286+
}
287+
}

packages/gantt/src/bar/bar.component.scss

+2-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ $gantt-bar-dependency-drop-border: 5px;
109109
background-color: $gantt-item-bg-color;
110110
border-radius: 4px;
111111
z-index: 1;
112-
background: #348fe4;
112+
113113

114114
.gantt-bar-layer {
115115
width: calc(100% + #{$gantt-bar-layer-append-width});
@@ -142,6 +142,7 @@ $gantt-bar-dependency-drop-border: 5px;
142142
cursor: pointer;
143143
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
144144
border-radius: 4px;
145+
background: #348fe4;
145146
}
146147

147148
&-active {

0 commit comments

Comments
 (0)