Timeline: WIP relations

pull/4927/head
Roman Roelofsen 8 years ago
parent 067625ebc2
commit 4b1dbcfe7e
  1. 16
      frontend/app/components/wp-table/timeline/cell-renderer/timeline-cell-renderer.ts
  2. 14
      frontend/app/components/wp-table/timeline/wp-timeline-cell.ts
  3. 12
      frontend/app/components/wp-table/timeline/wp-timeline-container.directive.ts
  4. 110
      frontend/app/components/wp-table/timeline/wp-timeline-global.directive.ts
  5. 32
      frontend/app/components/wp-table/timeline/wp-timeline.ts
  6. 18
      frontend/app/components/wp-table/wp-table.directive.html

@ -1,5 +1,8 @@
import {WorkPackageResourceInterface} from "../../../api/api-v3/hal-resources/work-package-resource.service";
import {RenderInfo, calculatePositionValueForDayCount, timelineElementCssClass} from "../wp-timeline";
import {
RenderInfo, calculatePositionValueForDayCount, timelineElementCssClass,
calculatePositionValueForDayCountinPx
} from "../wp-timeline";
import {classNameLeftHandle, classNameRightHandle} from "../wp-timeline-cell-mouse-handler";
import * as moment from 'moment';
import Moment = moment.Moment;
@ -223,6 +226,17 @@ export class TimelineCellRenderer {
return true;
}
getRightmostPosition(renderInfo: RenderInfo): number {
const wp = renderInfo.workPackage;
let start = moment(wp.startDate as any);
let due = moment(wp.dueDate as any);
const offsetStart = start.diff(renderInfo.viewParams.dateDisplayStart, "days");
const duration = due.diff(start, "days") + 1;
return calculatePositionValueForDayCountinPx(renderInfo.viewParams, offsetStart + duration);
}
/**
* Render the generic cell element, a bar spanning from
* start to due date.

@ -47,7 +47,7 @@ export class WorkPackageTimelineCell {
private subscription: Subscription;
private latestRenderInfo: RenderInfo;
public latestRenderInfo: RenderInfo;
private wpElement: HTMLDivElement|null = null;
@ -57,21 +57,28 @@ export class WorkPackageTimelineCell {
private wpCacheService: WorkPackageCacheService,
private states: States,
private workPackageId: string,
private timelineCell: HTMLElement) {
public timelineCell: HTMLElement) {
}
activate() {
this.subscription = this.workPackageTimeline.addWorkPackage(this.workPackageId)
.subscribe(renderInfo => {
this.updateView(renderInfo);
this.workPackageTimeline.globalService.updateWorkPackageInfo(this);
});
}
deactivate() {
this.clear();
this.workPackageTimeline.globalService.removeWorkPackageInfo(this.workPackageId);
this.subscription && this.subscription.unsubscribe();
}
getRightmostPosition(): number {
const renderer = this.cellRenderer(this.latestRenderInfo.workPackage);
return renderer.getRightmostPosition(this.latestRenderInfo);
}
private clear() {
this.timelineCell.innerHTML = "";
this.wpElement = null;
@ -136,6 +143,8 @@ export class WorkPackageTimelineCell {
}
private updateView(renderInfo: RenderInfo) {
// console.log("updateView()", "wpID=" + renderInfo.workPackage.id);
this.latestRenderInfo = renderInfo;
const renderer = this.cellRenderer(renderInfo.workPackage);
@ -148,4 +157,5 @@ export class WorkPackageTimelineCell {
this.clear();
}
}
}

@ -32,11 +32,11 @@ import {InteractiveTableController} from "./../../common/interactive-table/inter
import {WpTimelineHeader} from "./wp-timeline.header";
import {States} from "./../../states.service";
import {BehaviorSubject, Observable} from "rxjs";
import * as moment from 'moment';
import * as moment from "moment";
import Moment = moment.Moment;
import IDirective = angular.IDirective;
import IScope = angular.IScope;
import {WpTimelineGlobalService} from "./wp-timeline-global.directive";
export class WorkPackageTimelineTableController {
@ -46,6 +46,8 @@ export class WorkPackageTimelineTableController {
public wpTimelineHeader: WpTimelineHeader;
public readonly globalService = new WpTimelineGlobalService();
private updateAllWorkPackagesSubject = new BehaviorSubject<boolean>(true);
private refreshViewRequested = false;
@ -120,6 +122,7 @@ export class WorkPackageTimelineTableController {
const viewParamsChanged = this.calculateViewParams(this._viewParameters);
if (viewParamsChanged) {
// view params have changed, notify all cells
// console.log("addWorkPackage()", wp.id, "viewParamsChanged==true");
this.refreshView();
}
@ -150,6 +153,7 @@ export class WorkPackageTimelineTableController {
// Calculate view parameters
for (const wpId in this.workPackagesInView) {
// console.log(" check", wpId);
const workPackage = this.workPackagesInView[wpId];
const startDate = workPackage.startDate ? moment(workPackage.startDate) : currentParams.now;
@ -185,12 +189,14 @@ export class WorkPackageTimelineTableController {
// start date
if (!newParams.dateDisplayStart.isSame(this._viewParameters.dateDisplayStart)) {
changed = true;
// console.log(" start changed");
this._viewParameters.dateDisplayStart = newParams.dateDisplayStart;
}
// end date
if (!newParams.dateDisplayEnd.isSame(this._viewParameters.dateDisplayEnd)) {
changed = true;
// console.log(" end changed");
this._viewParameters.dateDisplayEnd = newParams.dateDisplayEnd;
}
@ -209,6 +215,6 @@ function wpTimelineContainer() {
controller: WorkPackageTimelineTableController,
bindToController: true
};
};
}
openprojectModule.directive('wpTimelineContainer', wpTimelineContainer);

@ -25,38 +25,104 @@
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {wpDirectivesModule} from "../../../angular-modules";
import {WorkPackageTimelineTableController} from "./wp-timeline-container.directive";
import IDirective = angular.IDirective;
import IComponentOptions = angular.IComponentOptions;
import {WorkPackageResourceInterface} from "../../api/api-v3/hal-resources/work-package-resource.service";
import {RenderInfo, TimelineViewParameters, timelineElementCssClass} from "./wp-timeline";
import {WorkPackageTimelineCell} from "./wp-timeline-cell";
class WpTimelineGlobalController {
export const timelineGlobalElementCssClassname = "timeline-global-element";
wpTimelineContainer: WorkPackageTimelineTableController;
init() {
console.log("init");
console.log(this.wpTimelineContainer);
export class TimelineGlobalElement {
from: string;
to: string;
}
export class WpTimelineGlobalService {
private workPackageIdOrder: string[] = ["56", "55", "54"];
private cells: {[id: string]: WorkPackageTimelineCell} = {};
private elements: TimelineGlobalElement[] = [];
constructor() {
setTimeout(() => {
console.log("displayRelation");
this.displayRelation("55", "54");
}, 3000);
}
updateWorkPackageInfo(cell: WorkPackageTimelineCell) {
this.cells[cell.latestRenderInfo.workPackage.id] = cell;
// TODO called to often
}
this.update();
}
removeWorkPackageInfo(id: string) {
delete this.cells[id];
this.update()
}
displayRelation(from: string, to: string) {
const elem = new TimelineGlobalElement();
elem.from = from;
elem.to = to;
this.elements.push(elem);
this.update();
}
private update() {
this.removeAllElements();
this.renderElements();
}
private removeAllElements() {
// console.log("removeAllElements()");
jQuery("." + timelineGlobalElementCssClassname).children().remove();
}
private renderElements() {
console.debug("renderElements()");
for (let e of this.elements) {
const idxFrom = this.workPackageIdOrder.indexOf(e.from);
const idxTo = this.workPackageIdOrder.indexOf(e.to);
const start = Math.min(idxFrom, idxTo);
const end = Math.max(idxFrom, idxTo);
// start
const startCell = this.cells[e.from];
let lastX = startCell.getRightmostPosition();
const line = document.createElement("div");
line.className = timelineElementCssClass;
line.style.position = "absolute";
line.style.zIndex = "100";
line.style.cssFloat = "left";
line.style.backgroundColor = "green";
line.style.top = "19px";
line.style.left = lastX + "px";
line.style.width = "20px";
lastX += 20;
line.style.height = "2px";
startCell.timelineCell.appendChild(line);
// vert line
for (let index = start; index <= end; index++) {
const id = this.workPackageIdOrder[index];
const cell = this.cells[id];
// console.log("i", index, id, cell);
}
}
}
function directive(): IDirective {
return {
require: ["^^wpTimelineContainer", "^wpTimelineGlobal"],
link: (scope,
element,
attrs,
[a, b]: [WorkPackageTimelineTableController, WpTimelineGlobalController]) => {
b.wpTimelineContainer = a;
b.init();
},
controller: WpTimelineGlobalController
};
}
wpDirectivesModule
.directive('wpTimelineGlobal', directive);

@ -25,12 +25,11 @@
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
import * as moment from 'moment';
import * as moment from "moment";
import {WorkPackageResourceInterface} from "../../api/api-v3/hal-resources/work-package-resource.service";
import {WpTimelineHeader} from "./wp-timeline.header";
import Moment = moment.Moment;
import {WorkPackageResourceInterface} from '../../api/api-v3/hal-resources/work-package-resource.service';
import {WpTimelineHeader} from "./wp-timeline.header";
export const timelineElementCssClass = "timeline-element";
/**
@ -38,7 +37,7 @@ export const timelineElementCssClass = "timeline-element";
*/
export class TimelineViewParametersSettings {
showDurationInPx = true;
// showDurationInPx = true;
scrollOffsetInDays = 0;
@ -105,17 +104,22 @@ export interface RenderInfo {
/**
*
* @param viewParams
* @param days
* @returns {string}
*/
export function calculatePositionValueForDayCount(viewParams: TimelineViewParameters, days: number): string {
export function calculatePositionValueForDayCountinPx(viewParams: TimelineViewParameters, days: number): number {
const daysInPx = days * viewParams.pixelPerDay;
if (viewParams.settings.showDurationInPx) {
return daysInPx + "px";
} else {
return (daysInPx / viewParams.maxWidthInPx * 100) + "%";
}
return daysInPx;
}
/**
*
*/
export function calculatePositionValueForDayCount(viewParams: TimelineViewParameters, days: number): string {
const value = calculatePositionValueForDayCountinPx(viewParams, days);
// if (viewParams.settings.showDurationInPx) {
return value + "px";
// } else {
// return (value / viewParams.maxWidthInPx * 100) + "%";
// }
}

@ -53,6 +53,24 @@
</th>
</tr>
</thead>
<!--<tbody>-->
<!--<tr style=";">-->
<!--<td ng-repeat="c in columns" style="padding: 0; line-height: 0;">a</td>-->
<!--<td style="padding: 0; line-height: 0;">b</td>-->
<!---->
<!--<td style="padding: 0; height: 50px; line-height: 50px; overflow: hidden;">-->
<!---->
<!--<div class="timeline-global-element" style="background-color: lightpink; ">-->
<!---->
<!--<div class="timeline-element" style="position: relative; top: 45px; left: 10px; width: 40px; height: 40px; background-color: red;"></div>-->
<!---->
<!--</div>-->
<!---->
<!--</td>-->
<!--</tr>-->
<!--</tbody>-->
<tbody class="work-package--empty-tbody" ng-if="query.hasError || rowcount === 0">
<tr id="empty-row-notification">
<td colspan="{{ columns.length + 1 }}">

Loading…
Cancel
Save