Restore relations display

[ci skip]
pull/5456/head
Oliver Günther 8 years ago
parent 1ee7653c11
commit fd4b5b03c7
No known key found for this signature in database
GPG Key ID: 88872239EB414F99
  1. 39
      frontend/app/components/wp-table/timeline/container/wp-timeline-container.directive.ts
  2. 1
      frontend/app/components/wp-table/timeline/container/wp-timeline-container.html
  3. 145
      frontend/app/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts
  4. 2
      frontend/app/components/wp-table/timeline/global-elements/wp-timeline.today-line.ts
  5. 1
      frontend/app/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts
  6. 1
      frontend/app/components/wp-table/timeline/header/wp-timeline-header.directive.ts
  7. 4
      frontend/app/components/wp-table/timeline/wp-timeline-cell.ts
  8. 5
      frontend/app/components/wp-table/wp-table.directive.html

@ -34,7 +34,6 @@ import {
TimelineViewParameters
} from "../wp-timeline";
import {WorkPackageResourceInterface} from "../../../api/api-v3/hal-resources/work-package-resource.service";
import {WpTimelineGlobalService} from "../wp-timeline-global.directive";
import {States} from "../../../states.service";
import {WorkPackageTableTimelineService} from "../../../wp-fast-table/state/wp-table-timeline.service";
import {WorkPackageNotificationService} from "../../../wp-edit/wp-notification.service";
@ -45,6 +44,7 @@ import {debugLog} from "../../../../helpers/debug_output";
import {openprojectModule} from "../../../../angular-modules";
import {WorkPackageTimelineHeaderController} from "../header/wp-timeline-header.directive";
import {TypeResource} from "../../../api/api-v3/hal-resources/type-resource.service";
import {WorkPackageTimelineCell} from "../wp-timeline-cell";
export class WorkPackageTimelineTableController {
@ -54,8 +54,6 @@ export class WorkPackageTimelineTableController {
private workPackagesInView: {[id: string]: WorkPackageResourceInterface} = {};
public readonly globalService = new WpTimelineGlobalService(this.$scope);
private updateAllWorkPackagesSubject = new BehaviorSubject<boolean>(true);
private refreshViewRequested = false;
@ -64,7 +62,9 @@ export class WorkPackageTimelineTableController {
public header:WorkPackageTimelineHeaderController;
private members:{ [name:string]: (vp:TimelineViewParameters) => void } = {};
public cells:{[id: string]:WorkPackageTimelineCell} = {};
private renderers:{ [name:string]: (vp:TimelineViewParameters) => void } = {};
constructor(private $scope:IScope,
private $element:ng.IAugmentedJQuery,
@ -108,15 +108,25 @@ export class WorkPackageTimelineTableController {
}
onRefreshRequested(name:string, callback:(vp:TimelineViewParameters) => void) {
this.members[name] = callback;
this.renderers[name] = callback;
}
refreshScrollOnly() {
jQuery("." + timelineElementCssClass).css("margin-left", this._viewParameters.scrollOffsetInPx + "px");
}
/**
* Returns a defensive copy of the currently used view parameters.
*/
getViewParametersCopy(): TimelineViewParameters {
return _.cloneDeep(this._viewParameters);
public updateWorkPackageInfo(cell: WorkPackageTimelineCell) {
this.cells[cell.latestRenderInfo.workPackage.id] = cell;
this.refreshView();
}
public removeWorkPackageInfo(id: string) {
delete this.cells[id];
this.refreshView();
}
get viewParameters(): TimelineViewParameters {
return this._viewParameters;
}
get viewParameterSettings() {
@ -140,22 +150,17 @@ export class WorkPackageTimelineTableController {
this.updateAllWorkPackagesSubject.next(true);
this.header.refreshView(this._viewParameters);
_.each(this.members, (cb, key) => {
_.each(this.renderers, (cb, key) => {
debugLog(`Refreshing timeline member ${key}`);
cb(this._viewParameters);
});
this.refreshScrollOnly();
this.refreshViewRequested = false;
}, 30);
}
this.refreshViewRequested = true;
}
refreshScrollOnly() {
jQuery("." + timelineElementCssClass).css("margin-left", this._viewParameters.scrollOffsetInPx + "px");
}
addWorkPackage(wpId: string): Observable<RenderInfo> {
const wpObs = this.states.workPackages.get(wpId).values$()
.takeUntil(scopeDestroyed$(this.$scope))
@ -163,8 +168,6 @@ export class WorkPackageTimelineTableController {
this.workPackagesInView[wp.id] = wp;
const viewParamsChanged = this.calculateViewParams(this._viewParameters);
if (viewParamsChanged) {
// view params have changed, notify all cells
this.globalService.updateViewParameter(this._viewParameters);
this.refreshView();
}

@ -3,6 +3,7 @@
<div class="wp-table-timeline--container">
<wp-timeline-header></wp-timeline-header>
<wp-timeline-grid></wp-timeline-grid>
<wp-timeline-relations></wp-timeline-relations>
<div class="wp-table-timeline--body"></div>
</div>
</div>

@ -1,7 +1,6 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
@ -26,23 +25,25 @@
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
import IDirective = angular.IDirective;
import IComponentOptions = angular.IComponentOptions;
import {Observable} from "rxjs/Rx";
import {scopeDestroyed$} from "../../../helpers/angular-rx-utils";
import {debugLog} from "../../../helpers/debug_output";
import {injectorBridge} from "../../angular/angular-injector-bridge.functions";
import {RelationResource} from "../../api/api-v3/hal-resources/relation-resource.service";
import {WorkPackageResource} from "../../api/api-v3/hal-resources/work-package-resource.service";
import {States} from "../../states.service";
import {WorkPackageStates} from "../../work-package-states.service";
import {RelationsStateValue, WorkPackageRelationsService} from "../../wp-relations/wp-relations.service";
import {TimelineRelationElement} from "./global-elements/timeline-relation-element";
import {timelineElementCssClass, TimelineViewParameters} from "./wp-timeline";
import {WorkPackageTimelineCell} from "./wp-timeline-cell";
import IScope = angular.IScope;
import {
timelineElementCssClass,
TimelineViewParameters,
} from "../wp-timeline";
import {WorkPackageTimelineTableController} from "../container/wp-timeline-container.directive";
import {Observable} from 'rxjs';
import * as moment from 'moment';
import Moment = moment.Moment;
import {openprojectModule} from "../../../../angular-modules";
import {States} from "../../../states.service";
import {WorkPackageStates} from "../../../work-package-states.service";
import {
RelationsStateValue,
WorkPackageRelationsService
} from "../../../wp-relations/wp-relations.service";
import {scopeDestroyed$} from "../../../../helpers/angular-rx-utils";
import {TimelineRelationElement} from "./timeline-relation-element";
import {RelationResource} from "../../../api/api-v3/hal-resources/relation-resource.service";
export const timelineGlobalElementCssClassname = "relation-line";
@ -70,40 +71,33 @@ function newSegment(vp: TimelineViewParameters,
return segment;
}
export class WpTimelineGlobalService {
export class WorkPackageTableTimelineRelations {
// Injected arguments
public states:States;
public wpStates:WorkPackageStates;
public wpRelations:WorkPackageRelationsService;
public wpTimeline:WorkPackageTimelineTableController;
private workPackageIdOrder:string[] = [];
private container:ng.IAugmentedJQuery;
private viewParameters:TimelineViewParameters;
private cells:{[id: string]:WorkPackageTimelineCell} = {};
private workPackageIdOrder:string[] = [];
private elements:TimelineRelationElement[] = [];
constructor(private scope:IScope) {
injectorBridge(this);
this.requireVisibleRelations();
this.setupRelationSubscription();
constructor(public $element:ng.IAugmentedJQuery,
public $scope:ng.IScope,
public states:States,
public wpStates:WorkPackageStates,
public wpRelations:WorkPackageRelationsService) {
}
updateViewParameter(viewParams: TimelineViewParameters) {
this.viewParameters = viewParams;
this.update();
}
$onInit() {
this.container = this.$element.find('.wp-table-timeline--relations');
this.wpTimeline.onRefreshRequested('relations', (vp:TimelineViewParameters) => this.refreshView(vp));
updateWorkPackageInfo(cell: WorkPackageTimelineCell) {
this.cells[cell.latestRenderInfo.workPackage.id] = cell;
this.update();
this.requireVisibleRelations();
this.setupRelationSubscription();
}
removeWorkPackageInfo(id: string) {
delete this.cells[id];
this.update();
private refreshView(vp:TimelineViewParameters) {
this.update(vp);
}
/**
@ -114,31 +108,40 @@ export class WpTimelineGlobalService {
// Observe the rows and request relations if changed
// AND timeline is visible.
Observable.combineLatest(
this.states.table.timelineVisible.values$().takeUntil(scopeDestroyed$(this.scope)),
this.states.table.rows.values$().takeUntil(scopeDestroyed$(this.scope))
this.states.table.timelineVisible.values$().takeUntil(scopeDestroyed$(this.$scope)),
this.states.table.rendered.values$().takeUntil(scopeDestroyed$(this.$scope))
)
.filter(([timelineState, rows]) => timelineState.isVisible)
.map(([_timelineState, rows]) => rows)
.subscribe((rows:WorkPackageResource[]) => {
this.workPackageIdOrder = rows.map(wp => wp.id.toString());
.filter(([timelineState, rendered]) => timelineState.isVisible)
.subscribe(() => {
this.workPackageIdOrder = this.getVisibleWorkPackageOrder();
this.wpRelations.requireInvolved(this.workPackageIdOrder);
});
}
private getVisibleWorkPackageOrder():string[] {
const ids:string[] = [];
jQuery('.wp-table--row').each((i, el) => {
ids.push(el.getAttribute('data-work-package-id')!);
});
return ids;
}
/**
* Refresh relations of visible rows.
*/
private setupRelationSubscription() {
this.wpStates.relations.observeChange()
.takeUntil(scopeDestroyed$(this.scope))
.takeUntil(scopeDestroyed$(this.$scope))
.withLatestFrom(
this.states.table.timelineVisible.values$().takeUntil(scopeDestroyed$(this.scope)))
this.states.table.timelineVisible.values$().takeUntil(scopeDestroyed$(this.$scope)))
.filter(([relations, timelineVisible]) => relations && timelineVisible.isVisible)
.map(([relations]) => relations)
.subscribe((nextVal) => {
const [workPackageId, relations] = nextVal;
if (workPackageId && this.cells[workPackageId]) {
if (workPackageId && this.wpTimeline.cells[workPackageId]) {
this.refreshRelations(workPackageId, relations!);
}
});
@ -154,45 +157,32 @@ export class WpTimelineGlobalService {
const elem = new TimelineRelationElement(workPackageId, relation);
this.elements.push(elem);
if (this.viewParameters !== undefined) {
this.renderElement(elem);
}
this.renderElement(this.wpTimeline.viewParameters, elem);
});
}
private update() {
this.removeAllVisibleElements();
this.renderElements();
}
private removeAllElements() {
private update(vp:TimelineViewParameters) {
this.removeAllVisibleElements();
this.elements = [];
this.renderElements(vp);
}
private removeAllVisibleElements() {
jQuery('.' + timelineGlobalElementCssClassname).remove();
}
private renderElements() {
if (this.viewParameters === undefined) {
debugLog('renderElements() aborted - no viewParameters');
return;
}
private renderElements(vp:TimelineViewParameters) {
for (const e of this.elements) {
this.renderElement(e);
this.renderElement(vp, e);
}
}
private renderElement(e:TimelineRelationElement) {
const vp = this.viewParameters;
private renderElement(vp:TimelineViewParameters, e:TimelineRelationElement) {
const involved = e.relation.ids;
const idxFrom = this.workPackageIdOrder.indexOf(involved.from);
const idxTo = this.workPackageIdOrder.indexOf(involved.to);
const startCell = this.cells[involved.from];
const endCell = this.cells[involved.to];
const startCell = this.wpTimeline.cells[involved.from];
const endCell = this.wpTimeline.cells[involved.to];
if (idxFrom === -1 || idxTo === -1 || _.isNil(startCell) || _.isNil(endCell)) {
return;
@ -225,7 +215,7 @@ export class WpTimelineGlobalService {
// vert segment
for (let index = idxFrom + directionY; index !== idxTo; index += directionY) {
const id = this.workPackageIdOrder[index];
const cell = this.cells[id];
const cell = this.wpTimeline.cells[id];
if (_.isNil(cell)) {
continue;
}
@ -255,6 +245,13 @@ export class WpTimelineGlobalService {
}
}
}
}
WpTimelineGlobalService.$inject = ['states', 'wpStates', 'wpRelations'];
openprojectModule.component("wpTimelineRelations", {
template: '<div class="wp-table-timeline--relations"></div>',
controller: WorkPackageTableTimelineRelations,
require: {
wpTimeline: '^wpTimelineContainer'
}
});

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {calculatePositionValueForDayCount, TimelineViewParameters} from "./wp-timeline";
import {calculatePositionValueForDayCount, TimelineViewParameters} from "../wp-timeline";
import * as moment from 'moment';

@ -31,7 +31,6 @@ import {
ZoomLevel,
calculatePositionValueForDayCount, timelineGridElementCssClass
} from "../wp-timeline";
import {todayLine} from "../wp-timeline.today-line";
import {WorkPackageTimelineTableController} from "../container/wp-timeline-container.directive";
import * as moment from 'moment';
import Moment = moment.Moment;

@ -31,7 +31,6 @@ import {
ZoomLevel,
calculatePositionValueForDayCount
} from "../wp-timeline";
import {todayLine} from "../wp-timeline.today-line";
import {WorkPackageTimelineTableController} from "../container/wp-timeline-container.directive";
import * as moment from 'moment';
import Moment = moment.Moment;

@ -74,13 +74,13 @@ export class WorkPackageTimelineCell {
.map(([renderInfo, _visible]) => renderInfo)
.subscribe(renderInfo => {
this.updateView(renderInfo);
this.workPackageTimeline.globalService.updateWorkPackageInfo(this);
this.workPackageTimeline.cells[this.workPackageId] = this;
});
}
deactivate() {
this.clear();
this.workPackageTimeline.globalService.removeWorkPackageInfo(this.workPackageId);
delete this.workPackageTimeline.cells[this.workPackageId];
this.subscription && this.subscription.unsubscribe();
}

@ -1,6 +1,5 @@
<div class="work-packages-split-view--left">
<div class="work-packages-split-view--left-table loading-indicator--location"
data-indicator-name="table">
<div class="work-packages-split-view--left loading-indicator--location" data-indicator-name="table">
<div class="work-packages-split-view--left-table">
<table class="keyboard-accessible-list generic-table work-package-table">
<colgroup>
<col highlight-col/>

Loading…
Cancel
Save