diff --git a/frontend/src/app/components/wp-card-view/styles/wp-card-view-vertical.sass b/frontend/src/app/components/wp-card-view/styles/wp-card-view-vertical.sass
index 31b4ace9ba..40415060b1 100644
--- a/frontend/src/app/components/wp-card-view/styles/wp-card-view-vertical.sass
+++ b/frontend/src/app/components/wp-card-view/styles/wp-card-view-vertical.sass
@@ -10,9 +10,3 @@
// independently of whether we scroll or not.
overflow-y: scroll
@include styled-scroll-bar
-
- .wp-card
- margin-top: 10px
- // Take care that the shadow of the last element is still visible
- &:last-of-type
- margin-bottom: 3px
diff --git a/frontend/src/app/components/wp-card-view/styles/wp-card-view.component.sass b/frontend/src/app/components/wp-card-view/styles/wp-card-view.component.sass
index 9804b1b8dd..5628ea04dc 100644
--- a/frontend/src/app/components/wp-card-view/styles/wp-card-view.component.sass
+++ b/frontend/src/app/components/wp-card-view/styles/wp-card-view.component.sass
@@ -1,86 +1,7 @@
-@import 'helpers'
-
-.wp-card
- display: flex
- flex-direction: column
- user-select: none
- width: 100%
- border: 1px solid var(--widget-box-block-border-color)
- border-radius: 2px
- padding: 10px
- position: relative
- box-shadow: 1px 1px 3px 0px lightgrey
- background: var(--body-background)
- font-size: var(--card-font-size)
- max-width: 400px
-
- &:hover
- box-shadow: 0px 0px 10px lightgrey
-
- &.-new
- padding-right: 25px
-
- &.-checked
- background-color: var(--table-row-highlighting-color)
-
-.wp-card--content:not(.-new)
- display: grid
- grid-template-columns: auto 1fr auto
- grid-template-rows: auto 1fr auto
- grid-row-gap: 10px
- grid-template-areas: "type type type" "subject subject subject" "attributeTag avatar idlink"
- overflow: hidden
- flex-grow: 1
-
- .wp-card--type
- grid-area: type
- .wp-card--subject
- grid-area: subject
- max-width: 230px
- @include text-shortener
- white-space: normal
- .wp-card--assignee
- grid-area: avatar
- place-self: center left
- .wp-card--id
- grid-area: idlink
- place-self: center right
- .wp-card--status
- grid-area: attributeTag
- max-width: 120px
- margin-right: 5px
- overflow: hidden
-
-.wp-card--highlighting
- width: 5px
- height: 100%
- position: absolute
- top: 0
- left: 0
- border-radius: 2px 0 0 2px
-
.wp-inline-create-button
font-size: 0.9rem
padding-top: 1rem
text-align: center
-.wp-card--inline-buttons
- position: absolute
- right: 0
- top: 5px
- opacity: 0
-
- &.-show, .wp-card:hover &
- opacity: 1
-
.wp-inline-create--reference-container
margin-bottom: 3rem
-
-.wp-card--cover-image
- display: block
- margin: -10px -10px 10px
- width: calc(100% + 10px + 10px)
- max-width: calc(100% + 10px + 10px)
-
- flex-basis: 200px
- object-fit: cover
diff --git a/frontend/src/app/components/wp-card-view/wp-card-view.component.html b/frontend/src/app/components/wp-card-view/wp-card-view.component.html
index cdf5385b9b..a9221caaaa 100644
--- a/frontend/src/app/components/wp-card-view/wp-card-view.component.html
+++ b/frontend/src/app/components/wp-card-view/wp-card-view.component.html
@@ -8,76 +8,16 @@
-
diff --git a/frontend/src/app/components/wp-card-view/wp-card-view.component.ts b/frontend/src/app/components/wp-card-view/wp-card-view.component.ts
index 38f2c7a166..a49233eaa3 100644
--- a/frontend/src/app/components/wp-card-view/wp-card-view.component.ts
+++ b/frontend/src/app/components/wp-card-view/wp-card-view.component.ts
@@ -5,7 +5,6 @@ import {
Component,
ElementRef,
EventEmitter,
- Inject,
Injector,
Input,
OnInit,
@@ -21,8 +20,6 @@ import {I18nService} from "core-app/modules/common/i18n/i18n.service";
import {WorkPackageInlineCreateService} from "core-components/wp-inline-create/wp-inline-create.service";
import {WorkPackageCreateService} from "core-components/wp-new/wp-create.service";
import {AngularTrackingHelpers} from "core-components/angular/tracking-functions";
-import {HalResourceNotificationService} from "core-app/modules/hal/services/hal-resource-notification.service";
-import {Highlighting} from "core-components/wp-fast-table/builders/highlighting/highlighting.functions";
import {CardHighlightingMode} from "core-components/wp-fast-table/builders/highlighting/highlighting-mode.const";
import {AuthorisationService} from "core-app/modules/common/model-auth/model-auth.service";
import {StateService} from "@uirouter/core";
@@ -35,7 +32,6 @@ import {WorkPackageViewSelectionService} from "core-app/modules/work_packages/ro
import {CardViewHandlerRegistry} from "core-components/wp-card-view/event-handler/card-view-handler-registry";
import {WorkPackageCardViewService} from "core-components/wp-card-view/services/wp-card-view.service";
import {WorkPackageCardDragAndDropService} from "core-components/wp-card-view/services/wp-card-drag-and-drop.service";
-import {checkedClassName, uiStateLinkClass} from "core-components/wp-fast-table/builders/ui-state-link-builder";
import {WorkPackageNotificationService} from "core-app/modules/work_packages/notifications/work-package-notification.service";
export type CardViewOrientation = 'horizontal'|'vertical';
@@ -75,12 +71,8 @@ export class WorkPackageCardViewComponent implements OnInit, AfterViewInit {
title: this.I18n.t('js.work_packages.no_results.title'),
description: this.I18n.t('js.work_packages.no_results.description')
},
- detailsView: this.I18n.t('js.button_open_details')
};
- public uiStateLinkClass:string = uiStateLinkClass;
- public checkedClassName:string = checkedClassName;
-
/** Inline create / reference properties */
public canAdd = false;
public canReference = false;
@@ -162,68 +154,6 @@ export class WorkPackageCardViewComponent implements OnInit, AfterViewInit {
this.cardDragDrop.destroy();
}
- public openSplitScreen(wp:WorkPackageResource) {
- let classIdentifier = this.classIdentifier(wp);
- this.wpTableSelection.setSelection(wp.id!, this.cardView.findRenderedCard(classIdentifier));
- this.$state.go(
- 'work-packages.list.details',
- {workPackageId: wp.id!}
- );
- }
-
- public wpTypeAttribute(wp:WorkPackageResource) {
- return wp.type.name;
- }
-
- public wpSubject(wp:WorkPackageResource) {
- return wp.subject;
- }
-
- public isSelected(wp:WorkPackageResource):boolean {
- return this.wpTableSelection.isSelected(wp.id!);
- }
-
- public classIdentifier(wp:WorkPackageResource) {
- return this.cardView.classIdentifier(wp);
- }
-
- public bcfSnapshotPath(wp:WorkPackageResource) {
- let vp = _.get(wp, 'bcf.viewpoints[0]');
- if (vp) {
- return this.pathHelper.attachmentDownloadPath(vp.id, vp.file_name);
- } else {
- return null;
- }
- }
-
-
- public cardClasses(wp:WorkPackageResource) {
- let classes = this.isSelected(wp) ? checkedClassName : '';
- classes += this.canDragOutOf(wp) ? ' -draggable' : '';
- classes += wp.isNew ? ' -new' : '';
- classes += ' wp-card-' + wp.id;
- return classes;
- }
-
- public cardHighlightingClass(wp:WorkPackageResource) {
- return this.cardHighlighting(wp);
- }
-
- public typeHighlightingClass(wp:WorkPackageResource) {
- return this.attributeHighlighting('type', wp);
- }
-
- private cardHighlighting(wp:WorkPackageResource) {
- if (['status', 'priority', 'type'].includes(this.highlightingMode)) {
- return Highlighting.backgroundClass(this.highlightingMode, wp[this.highlightingMode].id);
- }
- return '';
- }
-
- private attributeHighlighting(type:string, wp:WorkPackageResource) {
- return Highlighting.inlineClass(type, wp.type.id!);
- }
-
public get workPackages():WorkPackageResource[] {
return this.cardDragDrop.workPackages;
}
diff --git a/frontend/src/app/components/wp-card-view/wp-single-card/wp-single-card.component.html b/frontend/src/app/components/wp-card-view/wp-single-card/wp-single-card.component.html
new file mode 100644
index 0000000000..ea2f998b79
--- /dev/null
+++ b/frontend/src/app/components/wp-card-view/wp-single-card/wp-single-card.component.html
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/app/components/wp-card-view/wp-single-card/wp-single-card.component.sass b/frontend/src/app/components/wp-card-view/wp-single-card/wp-single-card.component.sass
new file mode 100644
index 0000000000..df1687d8eb
--- /dev/null
+++ b/frontend/src/app/components/wp-card-view/wp-single-card/wp-single-card.component.sass
@@ -0,0 +1,87 @@
+@import 'helpers'
+
+.wp-card
+ display: flex
+ flex-direction: column
+ user-select: none
+ width: 100%
+ border: 1px solid var(--widget-box-block-border-color)
+ border-radius: 2px
+ padding: 10px
+ position: relative
+ box-shadow: 1px 1px 3px 0px lightgrey
+ background: var(--body-background)
+ font-size: var(--card-font-size)
+ max-width: 400px
+
+ &:hover
+ box-shadow: 0px 0px 10px lightgrey
+
+ &.-new
+ padding-right: 25px
+
+ &.-checked
+ background-color: var(--table-row-highlighting-color)
+
+ &.-horizontal
+ height: 100%
+
+ &.-vertical
+ margin-top: 10px
+ // Take care that the shadow of the last element is still visible
+ &:last-of-type
+ margin-bottom: 3px
+
+.wp-card--content:not(.-new)
+ display: grid
+ grid-template-columns: auto 1fr auto
+ grid-template-rows: auto 1fr auto
+ grid-row-gap: 10px
+ grid-template-areas: "type type type" "subject subject subject" "attributeTag avatar idlink"
+ overflow: hidden
+ flex-grow: 1
+
+ .wp-card--type
+ grid-area: type
+ .wp-card--subject
+ grid-area: subject
+ max-width: 230px
+ @include text-shortener
+ white-space: normal
+ .wp-card--assignee
+ grid-area: avatar
+ place-self: center left
+ .wp-card--id
+ grid-area: idlink
+ place-self: center right
+ .wp-card--status
+ grid-area: attributeTag
+ max-width: 120px
+ margin-right: 5px
+ overflow: hidden
+
+.wp-card--highlighting
+ width: 5px
+ height: 100%
+ position: absolute
+ top: 0
+ left: 0
+ border-radius: 2px 0 0 2px
+
+.wp-card--inline-buttons
+ position: absolute
+ right: 0
+ top: 5px
+ opacity: 0
+
+ &.-show, .wp-card:hover &
+ opacity: 1
+
+.wp-card--cover-image
+ display: block
+ margin: -10px -10px 10px
+ width: calc(100% + 10px + 10px)
+ max-width: calc(100% + 10px + 10px)
+
+ flex-basis: 200px
+ object-fit: cover
diff --git a/frontend/src/app/components/wp-card-view/wp-single-card/wp-single-card.component.ts b/frontend/src/app/components/wp-card-view/wp-single-card/wp-single-card.component.ts
new file mode 100644
index 0000000000..598d3acf49
--- /dev/null
+++ b/frontend/src/app/components/wp-card-view/wp-single-card/wp-single-card.component.ts
@@ -0,0 +1,110 @@
+import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from "@angular/core";
+import {WorkPackageResource} from "core-app/modules/hal/resources/work-package-resource";
+import {checkedClassName, uiStateLinkClass} from "core-components/wp-fast-table/builders/ui-state-link-builder";
+import {PathHelperService} from "core-app/modules/common/path-helper/path-helper.service";
+import {Highlighting} from "core-components/wp-fast-table/builders/highlighting/highlighting.functions";
+import {StateService} from "@uirouter/core";
+import {WorkPackageViewSelectionService} from "core-app/modules/work_packages/routing/wp-view-base/view-services/wp-view-selection.service";
+import {WorkPackageCardViewService} from "core-components/wp-card-view/services/wp-card-view.service";
+import {I18nService} from "core-app/modules/common/i18n/i18n.service";
+import {CardHighlightingMode} from "core-components/wp-fast-table/builders/highlighting/highlighting-mode.const";
+import {CardViewOrientation} from "core-components/wp-card-view/wp-card-view.component";
+
+
+@Component({
+ selector: 'wp-single-card',
+ styleUrls: ['./wp-single-card.component.sass'],
+ templateUrl: './wp-single-card.component.html',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class WorkPackageSingleCardComponent {
+ @Input() public workPackage:WorkPackageResource;
+ @Input() public showInfoButton:boolean = false;
+ @Input() public showStatusButton:boolean = true;
+ @Input() public showRemoveButton:boolean = false;
+ @Input() public highlightingMode:CardHighlightingMode = 'inline';
+ @Input() public draggable:boolean = false;
+ @Input() public orientation:CardViewOrientation = 'vertical';
+
+ @Output() public onRemove = new EventEmitter
();
+
+ public uiStateLinkClass:string = uiStateLinkClass;
+
+ public text = {
+ removeCard: this.I18n.t('js.card.remove_from_list'),
+ detailsView: this.I18n.t('js.button_open_details')
+ };
+
+ constructor(readonly pathHelper:PathHelperService,
+ readonly I18n:I18nService,
+ readonly $state:StateService,
+ readonly wpTableSelection:WorkPackageViewSelectionService,
+ readonly cardView:WorkPackageCardViewService,) {
+ }
+
+ public classIdentifier(wp:WorkPackageResource) {
+ return this.cardView.classIdentifier(wp);
+ }
+
+ public openSplitScreen(wp:WorkPackageResource) {
+ let classIdentifier = this.classIdentifier(wp);
+ this.wpTableSelection.setSelection(wp.id!, this.cardView.findRenderedCard(classIdentifier));
+ this.$state.go(
+ 'work-packages.list.details',
+ {workPackageId: wp.id!}
+ );
+ }
+
+ public cardClasses(wp:WorkPackageResource) {
+ let classes = this.isSelected(wp) ? checkedClassName : '';
+ classes += this.draggable ? ' -draggable' : '';
+ classes += wp.isNew ? ' -new' : '';
+ classes += ' wp-card-' + wp.id;
+ classes += ' -' + this.orientation;
+ return classes;
+ }
+
+ public isSelected(wp:WorkPackageResource):boolean {
+ return this.wpTableSelection.isSelected(wp.id!);
+ }
+
+ public wpTypeAttribute(wp:WorkPackageResource) {
+ return wp.type.name;
+ }
+
+ public wpSubject(wp:WorkPackageResource) {
+ return wp.subject;
+ }
+
+ public cardHighlightingClass(wp:WorkPackageResource) {
+ return this.cardHighlighting(wp);
+ }
+
+ public typeHighlightingClass(wp:WorkPackageResource) {
+ return this.attributeHighlighting('type', wp);
+ }
+
+ public onRemoved(wp:WorkPackageResource) {
+ this.onRemove.emit(wp);
+ }
+
+ public bcfSnapshotPath(wp:WorkPackageResource) {
+ let vp = _.get(wp, 'bcf.viewpoints[0]');
+ if (vp) {
+ return this.pathHelper.attachmentDownloadPath(vp.id, vp.file_name);
+ } else {
+ return null;
+ }
+ }
+
+ private cardHighlighting(wp:WorkPackageResource) {
+ if (['status', 'priority', 'type'].includes(this.highlightingMode)) {
+ return Highlighting.backgroundClass(this.highlightingMode, wp[this.highlightingMode].id);
+ }
+ return '';
+ }
+
+ private attributeHighlighting(type:string, wp:WorkPackageResource) {
+ return Highlighting.inlineClass(type, wp.type.id!);
+ }
+}
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 03c7422cc1..96eb9caf8a 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
@@ -162,6 +162,7 @@ import {WorkPackageNotificationService} from "core-app/modules/work_packages/not
import {WorkPackageEditActionsBarComponent} from "core-app/modules/common/edit-actions-bar/wp-edit-actions-bar.component";
import {HalResource} from "core-app/modules/hal/resources/hal-resource";
import {WorkPackageChangeset} from "core-components/wp-edit/work-package-changeset";
+import {WorkPackageSingleCardComponent} from "core-components/wp-card-view/wp-single-card/wp-single-card.component";
@NgModule({
@@ -375,6 +376,7 @@ import {WorkPackageChangeset} from "core-components/wp-edit/work-package-changes
// Card view
WorkPackageCardViewComponent,
+ WorkPackageSingleCardComponent,
WorkPackageViewToggleButton,
],
entryComponents: [
@@ -451,6 +453,7 @@ import {WorkPackageChangeset} from "core-components/wp-edit/work-package-changes
// Card view
WorkPackageCardViewComponent,
+ WorkPackageSingleCardComponent,
CustomDateActionAdminComponent,
],
@@ -460,6 +463,7 @@ import {WorkPackageChangeset} from "core-components/wp-edit/work-package-changes
WorkPackageEmbeddedTableComponent,
WorkPackageEmbeddedTableEntryComponent,
WorkPackageCardViewComponent,
+ WorkPackageSingleCardComponent,
WorkPackageFilterButtonComponent,
WorkPackageFilterContainerComponent,
WorkPackageIsolatedQuerySpaceDirective,