From ecef1041c20c7823721da1d92055ea1c3b63aa90 Mon Sep 17 00:00:00 2001 From: Henriette Dinger Date: Fri, 16 Aug 2019 13:19:35 +0200 Subject: [PATCH 1/5] Add context menu for "toggle-view" button to switch between the work package views --- config/locales/js-en.yml | 5 + .../wp-view-dropdown-menu.directive.ts | 107 ++++++++++++++++++ .../wp-details-view-button.component.ts | 13 +-- .../wp-timeline-toggle-button.component.ts | 16 +-- ...rk-package-view-toggle-button.component.ts | 79 ++----------- .../openproject-work-packages.module.ts | 2 + 6 files changed, 127 insertions(+), 95 deletions(-) create mode 100644 frontend/src/app/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index ccea0324fc..1771fff6b5 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -917,3 +917,8 @@ en: all_projects: "all projects" project_and_subprojects: "and all subprojects" search_for: "Search for" + + views: + card: 'Cards' + list: 'Table' + timeline: 'Gantt' diff --git a/frontend/src/app/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts b/frontend/src/app/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts new file mode 100644 index 0000000000..e513711cb4 --- /dev/null +++ b/frontend/src/app/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts @@ -0,0 +1,107 @@ +//-- 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 {OPContextMenuService} from "core-components/op-context-menu/op-context-menu.service"; +import {Directive, ElementRef} from "@angular/core"; +import {OpContextMenuTrigger} from "core-components/op-context-menu/handlers/op-context-menu-trigger.directive"; +import { + WorkPackageDisplayRepresentationService, + wpDisplayCardRepresentation, + wpDisplayListRepresentation +} from "core-components/wp-fast-table/state/work-package-display-representation.service"; +import {I18nService} from "core-app/modules/common/i18n/i18n.service"; +import {WorkPackageTableTimelineService} from "core-components/wp-fast-table/state/wp-table-timeline.service"; + +@Directive({ + selector: '[wpViewDropdown]' +}) +export class WorkPackageViewDropdownMenuDirective extends OpContextMenuTrigger { + constructor(readonly elementRef:ElementRef, + readonly opContextMenu:OPContextMenuService, + readonly I18n:I18nService, + readonly wpDisplayRepresentationService:WorkPackageDisplayRepresentationService, + readonly wpTableTimeline:WorkPackageTableTimelineService) { + + super(elementRef, opContextMenu); + } + + protected open(evt:JQueryEventObject) { + this.buildItems(); + this.opContextMenu.show(this, evt); + } + + public get locals() { + return { + items: this.items, + contextMenuId: 'wp-view-context-menu' + }; + } + + private buildItems() { + this.items = [ + { + // Card View + linkText: this.I18n.t('js.views.card'), + icon: 'icon-view-card', + onClick: (evt:any) => { + this.wpDisplayRepresentationService.setDisplayRepresentation(wpDisplayCardRepresentation); + if (this.wpTableTimeline.isVisible) { + // Necessary for the timeline buttons to disappear + this.wpTableTimeline.toggle(); + } + return true; + } + }, + { + // List View + linkText: this.I18n.t('js.views.list'), + icon: 'icon-view-list', + onClick: (evt:any) => { + this.wpDisplayRepresentationService.setDisplayRepresentation(wpDisplayListRepresentation); + if (this.wpTableTimeline.isVisible) { + this.wpTableTimeline.toggle(); + } + return true; + } + }, + { + // List View with enabled Gantt + linkText: this.I18n.t('js.views.timeline'), + icon: 'icon-view-timeline', + onClick: (evt:any) => { + this.wpDisplayRepresentationService.setDisplayRepresentation(wpDisplayListRepresentation); + if (!this.wpTableTimeline.isVisible) { + this.wpTableTimeline.toggle(); + } + return true; + } + } + ]; + } +} + diff --git a/frontend/src/app/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts b/frontend/src/app/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts index 7580989f3d..a8de44d092 100644 --- a/frontend/src/app/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts +++ b/frontend/src/app/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts @@ -33,8 +33,6 @@ import {StateService, TransitionService} from '@uirouter/core'; import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core'; import {AbstractWorkPackageButtonComponent} from 'core-components/wp-buttons/wp-buttons.module'; import {I18nService} from "core-app/modules/common/i18n/i18n.service"; -import {untilComponentDestroyed} from "ng2-rx-componentdestroyed"; -import {WorkPackageViewDisplayRepresentationService} from "core-app/modules/work_packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; @Component({ templateUrl: '../wp-button.template.html', @@ -62,9 +60,7 @@ export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageBu readonly cdRef:ChangeDetectorRef, public states:States, public wpTableFocus:WorkPackageViewFocusService, - public keepTab:KeepTabService, - public wpDisplayRepresentationService:WorkPackageViewDisplayRepresentationService) { - + public keepTab:KeepTabService) { super(I18n); this.activateLabel = I18n.t('js.button_open_details'); @@ -77,13 +73,6 @@ export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageBu } public ngOnInit() { - this.wpDisplayRepresentationService.live$() - .pipe( - untilComponentDestroyed(this) - ) - .subscribe(() => { - this.cdRef.detectChanges(); - }); } public ngOnDestroy() { diff --git a/frontend/src/app/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts b/frontend/src/app/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts index 5ed82bbda9..ea01998808 100644 --- a/frontend/src/app/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts +++ b/frontend/src/app/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts @@ -31,10 +31,6 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; import {TimelineZoomLevel} from 'core-app/modules/hal/resources/query-resource'; import {untilComponentDestroyed} from "ng2-rx-componentdestroyed"; -import { - WorkPackageViewDisplayRepresentationService, - wpDisplayCardRepresentation -} from "core-app/modules/work_packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; import {WorkPackageViewTimelineService} from "core-app/modules/work_packages/routing/wp-view-base/view-services/wp-view-timeline.service"; export interface TimelineButtonText extends ButtonControllerText { @@ -67,8 +63,7 @@ export class WorkPackageTimelineButtonComponent extends AbstractWorkPackageButto constructor(readonly I18n:I18nService, readonly cdRef:ChangeDetectorRef, - public wpTableTimeline:WorkPackageViewTimelineService, - public wpDisplayRepresentationService:WorkPackageViewDisplayRepresentationService) { + public wpTableTimeline:WorkPackageViewTimelineService) { super(I18n); this.activateLabel = I18n.t('js.timelines.button_activate'); @@ -102,15 +97,6 @@ export class WorkPackageTimelineButtonComponent extends AbstractWorkPackageButto this.isMinLevel = current === this.minZoomLevel; this.cdRef.detectChanges(); }); - - this.wpDisplayRepresentationService.live$() - .pipe( - untilComponentDestroyed(this) - ) - .subscribe(() => { - this.disabled = this.wpDisplayRepresentationService.current === wpDisplayCardRepresentation; - this.cdRef.detectChanges(); - }); } ngOnDestroy():void { diff --git a/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts b/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts index bb8d9830e5..0d758527e7 100644 --- a/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts +++ b/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts @@ -39,52 +39,22 @@ import {untilComponentDestroyed} from "ng2-rx-componentdestroyed"; @Component({ - template: ` - + template: ` + `, changeDetection: ChangeDetectionStrategy.OnPush, selector: 'wp-view-toggle-button', }) -export class WorkPackageViewToggleButton extends AbstractWorkPackageButtonComponent implements OnInit, OnDestroy { - public iconListView:string = 'icon-view-list'; - public iconCardView:string = 'icon-view-card'; - - public inListView:boolean = true; - - public cardLabel:string; - public listLabel:string; - - constructor(readonly $state:StateService, - readonly I18n:I18nService, +export class WorkPackageViewToggleButton implements OnInit, OnDestroy { + constructor(readonly I18n:I18nService, readonly cdRef:ChangeDetectorRef, readonly wpDisplayRepresentationService:WorkPackageViewDisplayRepresentationService) { - super(I18n); - - this.cardLabel = I18n.t('js.button_card_list'); - this.listLabel = I18n.t('js.button_show_list'); } ngOnInit() { @@ -93,40 +63,13 @@ export class WorkPackageViewToggleButton extends AbstractWorkPackageButtonCompon untilComponentDestroyed(this) ) .subscribe(() => { - this.inListView = this.wpDisplayRepresentationService.current !== wpDisplayCardRepresentation; this.cdRef.detectChanges(); }); } ngOnDestroy() { - // - } - - public performAction(evt:Event):false { - if (this.inListView) { - this.activateCardView(); - } else { - this.activateListView(); - } - - evt.preventDefault(); - return false; + // Nothing to do } - - private activateCardView() { - this.inListView = false; - this.wpDisplayRepresentationService.setDisplayRepresentation(wpDisplayCardRepresentation); - - this.cdRef.detectChanges(); - } - - private activateListView() { - this.inListView = true; - this.wpDisplayRepresentationService.setDisplayRepresentation(wpDisplayListRepresentation); - - this.cdRef.detectChanges(); - } - } DynamicBootstrapper.register({ selector: 'wp-view-toggle-button', cls: WorkPackageViewToggleButton }); diff --git a/frontend/src/app/modules/work_packages/openproject-work-packages.module.ts b/frontend/src/app/modules/work_packages/openproject-work-packages.module.ts index 0c32977eb2..354e80c263 100644 --- a/frontend/src/app/modules/work_packages/openproject-work-packages.module.ts +++ b/frontend/src/app/modules/work_packages/openproject-work-packages.module.ts @@ -158,6 +158,7 @@ import {WorkPackagesTableConfigMenu} from "core-components/wp-table/config-menu/ import {WorkPackageIsolatedGraphQuerySpaceDirective} from "core-app/modules/work_packages/query-space/wp-isolated-graph-query-space.directive"; import {WorkPackageViewToggleButton} from "core-components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component"; import {WorkPackagesGridComponent} from "core-components/wp-grid/wp-grid.component"; +import {WorkPackageViewDropdownMenuDirective} from "core-components/op-context-menu/handlers/wp-view-dropdown-menu.directive"; @NgModule({ @@ -275,6 +276,7 @@ import {WorkPackagesGridComponent} from "core-components/wp-grid/wp-grid.compone WorkPackageCreateSettingsMenuDirective, WorkPackageSingleContextMenuDirective, WorkPackageQuerySelectDropdownComponent, + WorkPackageViewDropdownMenuDirective, // Timeline WorkPackageTimelineButtonComponent, From 9b7775450cde4a7b9082b20bc7f8c6a9ae0c15af Mon Sep 17 00:00:00 2001 From: Henriette Dinger Date: Fri, 16 Aug 2019 14:47:33 +0200 Subject: [PATCH 2/5] Show current view in toolbar button --- .../wp-details-view-button.component.ts | 7 +--- .../wp-timeline-toggle-button.html | 12 ------ ...rk-package-view-toggle-button.component.ts | 38 +++++++++++++++---- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/frontend/src/app/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts b/frontend/src/app/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts index a8de44d092..edc273c828 100644 --- a/frontend/src/app/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts +++ b/frontend/src/app/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts @@ -30,7 +30,7 @@ import {KeepTabService} from '../../wp-single-view-tabs/keep-tab/keep-tab.servic import {States} from '../../states.service'; import {WorkPackageViewFocusService} from 'core-app/modules/work_packages/routing/wp-view-base/view-services/wp-view-focus.service'; import {StateService, TransitionService} from '@uirouter/core'; -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy} from '@angular/core'; import {AbstractWorkPackageButtonComponent} from 'core-components/wp-buttons/wp-buttons.module'; import {I18nService} from "core-app/modules/common/i18n/i18n.service"; @@ -39,7 +39,7 @@ import {I18nService} from "core-app/modules/common/i18n/i18n.service"; changeDetection: ChangeDetectionStrategy.OnPush, selector: 'wp-details-view-button', }) -export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageButtonComponent implements OnInit, OnDestroy { +export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageButtonComponent implements OnDestroy { public projectIdentifier:string; public accessKey:number = 8; public activeState:string = 'work-packages.list.details'; @@ -72,9 +72,6 @@ export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageBu }); } - public ngOnInit() { - } - public ngOnDestroy() { this.transitionListener(); } diff --git a/frontend/src/app/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.html b/frontend/src/app/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.html index 21b751cc61..6a4ad8e322 100644 --- a/frontend/src/app/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.html +++ b/frontend/src/app/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.html @@ -30,16 +30,4 @@ - -
  • - -
  • - diff --git a/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts b/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts index 0d758527e7..7ffdf63fdf 100644 --- a/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts +++ b/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts @@ -26,25 +26,27 @@ // See doc/COPYRIGHT.rdoc for more details. // ++ -import {AbstractWorkPackageButtonComponent} from '../wp-buttons.module'; import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core'; import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; import {DynamicBootstrapper} from "core-app/globals/dynamic-bootstrapper"; -import {StateService} from "@uirouter/core"; import { - WorkPackageViewDisplayRepresentationService, wpDisplayCardRepresentation, + WorkPackageViewDisplayRepresentationService, + wpDisplayCardRepresentation, wpDisplayListRepresentation } from "core-app/modules/work_packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; import {untilComponentDestroyed} from "ng2-rx-componentdestroyed"; +import {WorkPackageViewTimelineService} from "core-app/modules/work_packages/routing/wp-view-base/view-services/wp-view-timeline.service"; @Component({ - template: ` + template: ` `, @@ -52,9 +54,18 @@ import {untilComponentDestroyed} from "ng2-rx-componentdestroyed"; selector: 'wp-view-toggle-button', }) export class WorkPackageViewToggleButton implements OnInit, OnDestroy { + public view:string; + + public text:any = { + card: this.I18n.t('js.views.card'), + list: this.I18n.t('js.views.list'), + timeline: this.I18n.t('js.views.timeline'), + }; + constructor(readonly I18n:I18nService, readonly cdRef:ChangeDetectorRef, - readonly wpDisplayRepresentationService:WorkPackageViewDisplayRepresentationService) { + readonly wpDisplayRepresentationService:WorkPackageViewDisplayRepresentationService, + readonly wpTableTimeline:WorkPackageViewTimelineService) { } ngOnInit() { @@ -63,6 +74,7 @@ export class WorkPackageViewToggleButton implements OnInit, OnDestroy { untilComponentDestroyed(this) ) .subscribe(() => { + this.detectView(); this.cdRef.detectChanges(); }); } @@ -70,6 +82,18 @@ export class WorkPackageViewToggleButton implements OnInit, OnDestroy { ngOnDestroy() { // Nothing to do } + + public detectView() { + if (this.wpDisplayRepresentationService.current !== wpDisplayCardRepresentation) { + if (this.wpTableTimeline.isVisible) { + this.view = 'timeline'; + } else { + this.view = wpDisplayListRepresentation; + } + } else { + this.view = wpDisplayCardRepresentation; + } + } } DynamicBootstrapper.register({ selector: 'wp-view-toggle-button', cls: WorkPackageViewToggleButton }); From b42813d3b19831dc8dbf839ceac47ccf2c15fc23 Mon Sep 17 00:00:00 2001 From: Henriette Dinger Date: Fri, 16 Aug 2019 15:33:14 +0200 Subject: [PATCH 3/5] Adapt WP view drop down to new service structure (after refactoring) --- .../handlers/wp-view-dropdown-menu.directive.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts b/frontend/src/app/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts index e513711cb4..df353a84b6 100644 --- a/frontend/src/app/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts +++ b/frontend/src/app/components/op-context-menu/handlers/wp-view-dropdown-menu.directive.ts @@ -29,13 +29,13 @@ import {OPContextMenuService} from "core-components/op-context-menu/op-context-menu.service"; import {Directive, ElementRef} from "@angular/core"; import {OpContextMenuTrigger} from "core-components/op-context-menu/handlers/op-context-menu-trigger.directive"; +import {I18nService} from "core-app/modules/common/i18n/i18n.service"; import { - WorkPackageDisplayRepresentationService, + WorkPackageViewDisplayRepresentationService, wpDisplayCardRepresentation, wpDisplayListRepresentation -} from "core-components/wp-fast-table/state/work-package-display-representation.service"; -import {I18nService} from "core-app/modules/common/i18n/i18n.service"; -import {WorkPackageTableTimelineService} from "core-components/wp-fast-table/state/wp-table-timeline.service"; +} from "core-app/modules/work_packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; +import {WorkPackageViewTimelineService} from "core-app/modules/work_packages/routing/wp-view-base/view-services/wp-view-timeline.service"; @Directive({ selector: '[wpViewDropdown]' @@ -44,8 +44,8 @@ export class WorkPackageViewDropdownMenuDirective extends OpContextMenuTrigger { constructor(readonly elementRef:ElementRef, readonly opContextMenu:OPContextMenuService, readonly I18n:I18nService, - readonly wpDisplayRepresentationService:WorkPackageDisplayRepresentationService, - readonly wpTableTimeline:WorkPackageTableTimelineService) { + readonly wpDisplayRepresentationService:WorkPackageViewDisplayRepresentationService, + readonly wpTableTimeline:WorkPackageViewTimelineService) { super(elementRef, opContextMenu); } From 2551e5c2063f50a6f817dc1e8d707ab8b77d4ada Mon Sep 17 00:00:00 2001 From: Henriette Dinger Date: Mon, 19 Aug 2019 10:06:09 +0200 Subject: [PATCH 4/5] Adapt tests to new view selection button --- ...rk-package-view-toggle-button.component.ts | 1 + .../work_packages/display_representation.rb | 36 +++++++++++++++---- .../work_packages/work_packages_timeline.rb | 10 +++--- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts b/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts index 7ffdf63fdf..f9d03b148c 100644 --- a/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts +++ b/frontend/src/app/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts @@ -41,6 +41,7 @@ import {WorkPackageViewTimelineService} from "core-app/modules/work_packages/rou @Component({ template: `