kanbanworkflowstimelinescrumrubyroadmapproject-planningproject-managementopenprojectangularissue-trackerifcgantt-chartganttbug-trackerboardsbcf
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
5.7 KiB
185 lines
5.7 KiB
// -- copyright
|
|
// OpenProject is a project management system.
|
|
// 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.
|
|
//
|
|
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
|
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
// Copyright (C) 2010-2013 the ChiliProject Team
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
//
|
|
// See doc/COPYRIGHT.rdoc for more details.
|
|
// ++
|
|
|
|
import {wpDirectivesModule} from "../../angular-modules";
|
|
import {scopedObservable, runInScopeDigest} from "../../helpers/angular-rx-utils";
|
|
import IScope = angular.IScope;
|
|
import IRootElementService = angular.IRootElementService;
|
|
import IAnimateProvider = angular.animate.IAnimateProvider;
|
|
import ITranscludeFunction = angular.ITranscludeFunction;
|
|
import {ReplaySubject, Subject} from "rxjs";
|
|
|
|
function wpVirtualScrollRow(workPackageTableVirtualScrollService: WorkPackageTableVirtualScrollService) {
|
|
return {
|
|
restrict: 'A',
|
|
|
|
link: ($scope: IScope,
|
|
$element: IRootElementService,
|
|
$attr: any) => {
|
|
|
|
new RowDisplay($scope, $element, $attr, workPackageTableVirtualScrollService);
|
|
}
|
|
};
|
|
}
|
|
|
|
wpDirectivesModule.directive('wpVirtualScrollRow', wpVirtualScrollRow);
|
|
|
|
class RowDisplay {
|
|
|
|
private index: number;
|
|
private viewport: [number, number] = [0, 0];
|
|
private watchersEnabled = true;
|
|
|
|
constructor(private $scope: angular.IScope,
|
|
private $element: angular.IRootElementService,
|
|
private $attr: any,
|
|
private workPackageTableVirtualScrollService: WorkPackageTableVirtualScrollService) {
|
|
|
|
this.index = $scope.$eval($attr.wpVirtualScrollRow);
|
|
|
|
scopedObservable($scope, workPackageTableVirtualScrollService.viewportChanges)
|
|
.subscribe(vp => {
|
|
this.viewport = vp;
|
|
this.viewportChanged();
|
|
});
|
|
|
|
workPackageTableVirtualScrollService.requestUpdate();
|
|
}
|
|
|
|
private isRowInViewport() {
|
|
const offsetTop = this.$element.offset().top;
|
|
const top = this.viewport[0];
|
|
const bottom = this.viewport[1];
|
|
return (offsetTop > top && offsetTop < bottom);
|
|
}
|
|
|
|
private viewportChanged() {
|
|
const enableWatchers = this.isRowInViewport();
|
|
if (this.watchersEnabled !== enableWatchers) {
|
|
this.adjustWatchers(this.$element, enableWatchers);
|
|
}
|
|
}
|
|
|
|
private adjustWatchers(element: JQuery, enableWatchers: boolean) {
|
|
this.watchersEnabled = enableWatchers;
|
|
|
|
const scope: any = angular.element(element).scope();
|
|
if (scope === undefined) {
|
|
return;
|
|
}
|
|
|
|
// Do not toggle watch state on table rows themselves
|
|
// since watchers are needed for, e.g., group collapsing
|
|
if (!(element.length > 0 && element[0].tagName === 'TR')) {
|
|
this.setWatchState(scope, enableWatchers);
|
|
}
|
|
|
|
angular.forEach(angular.element(element).children(), (child: JQuery) => {
|
|
this.adjustWatchers(child, enableWatchers);
|
|
});
|
|
|
|
}
|
|
|
|
private setWatchState(scope, enableWatchers: boolean) {
|
|
if (!enableWatchers) {
|
|
if (scope.$$watchers && scope.$$watchers.length > 0) {
|
|
scope.__backup_watchers = scope.$$watchers;
|
|
scope.$$watchers = [];
|
|
}
|
|
} else {
|
|
if (scope.__backup_watchers && scope.__backup_watchers.length > 0) {
|
|
scope.$$watchers = scope.__backup_watchers;
|
|
scope.__backup_watchers = [];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class WorkPackageTableVirtualScrollService {
|
|
|
|
public viewportChanges: Subject<[number, number]> = new ReplaySubject<[number, number]>(0);
|
|
|
|
private requestedUpdateTimeout: any;
|
|
|
|
/*@ngInject*/
|
|
constructor(private $rootScope: angular.IRootScopeService) {
|
|
}
|
|
|
|
updateScrollInfo() {
|
|
runInScopeDigest(this.$rootScope, () => {
|
|
this.viewportChanges.next([-50, window.innerHeight + 50]);
|
|
});
|
|
}
|
|
|
|
requestUpdate() {
|
|
this.requestedUpdateTimeout && clearTimeout(this.requestedUpdateTimeout);
|
|
this.requestedUpdateTimeout = setTimeout(() => {
|
|
this.updateScrollInfo();
|
|
}, 1000);
|
|
}
|
|
}
|
|
|
|
wpDirectivesModule.service("workPackageTableVirtualScrollService", WorkPackageTableVirtualScrollService);
|
|
|
|
|
|
function wpVirtualScrollTable(workPackageTableVirtualScrollService: WorkPackageTableVirtualScrollService) {
|
|
return {
|
|
restrict: 'A',
|
|
link: ($scope: IScope, $element: IRootElementService, attr: any) => {
|
|
// flag to avoid endless loops
|
|
let updateActive = false;
|
|
|
|
const updateScrollInfo = () => {
|
|
if (updateActive) {
|
|
return;
|
|
}
|
|
updateActive = true;
|
|
try {
|
|
workPackageTableVirtualScrollService.updateScrollInfo();
|
|
} finally {
|
|
updateActive = false;
|
|
}
|
|
};
|
|
|
|
let scrollTimeout: any;
|
|
$element.on("scroll", () => {
|
|
scrollTimeout && clearTimeout(scrollTimeout);
|
|
scrollTimeout = setTimeout(() => {
|
|
updateScrollInfo();
|
|
}, 2000);
|
|
});
|
|
|
|
window.addEventListener('resize', () => {
|
|
updateScrollInfo();
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
wpDirectivesModule.directive('wpVirtualScrollTable', wpVirtualScrollTable);
|
|
|