Merge pull request #5321 from tsiegleauq/projector-indicator-on-agenda

Projector indicator list view tables
This commit is contained in:
Emanuel Schütze 2020-05-05 17:50:27 +02:00 committed by GitHub
commit 77cf3e2785
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 95 additions and 48 deletions

View File

@ -22,6 +22,8 @@
[columns]="columnSet" [columns]="columnSet"
[hideColumns]="hiddenColumns" [hideColumns]="hiddenColumns"
(rowClick)="onSelectRow($event)" (rowClick)="onSelectRow($event)"
[rowClassUpdate]="isElementProjected"
rowClassUpdateFreq="ngDoCheck"
> >
<!-- "row" has the view model --> <!-- "row" has the view model -->
<!-- "value" has the property, that was defined in the columnDefinition --> <!-- "value" has the property, that was defined in the columnDefinition -->
@ -30,10 +32,21 @@
<!-- Projector column --> <!-- Projector column -->
<div *pblNgridCellDef="'projector'; row as viewModel" class="fill ngrid-lg"> <div *pblNgridCellDef="'projector'; row as viewModel" class="fill ngrid-lg">
<os-projector-button <os-projector-button
*osPerms="'core.can_manage_projector'"
class="projector-button" class="projector-button"
[object]="getProjectable(viewModel)" [object]="getProjectable(viewModel)"
(changeEvent)="viewUpdateEvent()" (changeEvent)="viewUpdateEvent()"
></os-projector-button> ></os-projector-button>
<!-- Projector indicator -->
<div class="projector-button" *osPerms="'core.can_manage_projector'; complement: true">
<mat-icon
color="accent"
*ngIf="projectorService.isProjected(getProjectable(viewModel))"
matTooltip="{{ 'Currently projected' | translate }}"
>
videocam
</mat-icon>
</div>
</div> </div>
<!-- No Results --> <!-- No Results -->

View File

@ -4,6 +4,7 @@ $pbl-height: var(--pbl-height);
.projector-button { .projector-button {
margin: auto; margin: auto;
display: flex;
} }
.pbl-ngrid-row { .pbl-ngrid-row {

View File

@ -0,0 +1,13 @@
@import '~@angular/material/theming';
@mixin os-list-view-table-theme($theme) {
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, accent);
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
.projected {
background-color: mat-color($background, hover) !important;
}
}

View File

@ -5,8 +5,8 @@ import { E2EImportsModule } from 'e2e-imports.module';
import { ListViewTableComponent } from './list-view-table.component'; import { ListViewTableComponent } from './list-view-table.component';
describe('ListViewTableComponent', () => { describe('ListViewTableComponent', () => {
let component: ListViewTableComponent<any, any>; let component: ListViewTableComponent<any>;
let fixture: ComponentFixture<ListViewTableComponent<any, any>>; let fixture: ComponentFixture<ListViewTableComponent<any>>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({

View File

@ -13,18 +13,18 @@ import {
import { NavigationStart, Router } from '@angular/router'; import { NavigationStart, Router } from '@angular/router';
import { columnFactory, createDS, DataSourcePredicate, PblDataSource, PblNgridComponent } from '@pebula/ngrid'; import { columnFactory, createDS, DataSourcePredicate, PblDataSource, PblNgridComponent } from '@pebula/ngrid';
import { PblColumnDefinition, PblColumnFactory, PblNgridColumnSet } from '@pebula/ngrid/lib/grid'; import { PblColumnDefinition, PblColumnFactory, PblNgridColumnSet, PblNgridRowContext } from '@pebula/ngrid/lib/grid';
import { PblNgridDataMatrixRow } from '@pebula/ngrid/target-events'; import { PblNgridDataMatrixRow } from '@pebula/ngrid/target-events';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { distinctUntilChanged, filter } from 'rxjs/operators'; import { distinctUntilChanged, filter } from 'rxjs/operators';
import { OperatorService, Permission } from 'app/core/core-services/operator.service'; import { OperatorService, Permission } from 'app/core/core-services/operator.service';
import { ProjectorService } from 'app/core/core-services/projector.service';
import { StorageService } from 'app/core/core-services/storage.service'; import { StorageService } from 'app/core/core-services/storage.service';
import { HasViewModelListObservable } from 'app/core/definitions/has-view-model-list-observable'; import { HasViewModelListObservable } from 'app/core/definitions/has-view-model-list-observable';
import { BaseFilterListService } from 'app/core/ui-services/base-filter-list.service'; import { BaseFilterListService } from 'app/core/ui-services/base-filter-list.service';
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service'; import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
import { ViewportService } from 'app/core/ui-services/viewport.service'; import { ViewportService } from 'app/core/ui-services/viewport.service';
import { BaseModel } from 'app/shared/models/base/base-model';
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model'; import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
import { BaseViewModel } from 'app/site/base/base-view-model'; import { BaseViewModel } from 'app/site/base/base-view-model';
import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object'; import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object';
@ -89,7 +89,8 @@ export interface ColumnRestriction {
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel> implements OnInit, OnDestroy { export class ListViewTableComponent<V extends BaseViewModel | BaseViewModelWithContentObject>
implements OnInit, OnDestroy {
/** /**
* Declare the table * Declare the table
*/ */
@ -254,11 +255,6 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
*/ */
private dataListObservable: Observable<V[]>; private dataListObservable: Observable<V[]>;
/**
* Minimal column width
*/
private columnMinWidth = '60px';
/** /**
* The column set to display in the table * The column set to display in the table
*/ */
@ -289,6 +285,14 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
*/ */
private subs: Subscription[] = []; private subs: Subscription[] = [];
private get projectorColumnWidth(): number {
if (this.operator.hasPerms('core.can_manage_projector')) {
return 60;
} else {
return 24;
}
}
/** /**
* Most, of not all list views require these * Most, of not all list views require these
*/ */
@ -302,7 +306,7 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
{ {
prop: 'projector', prop: 'projector',
label: '', label: '',
width: this.columnMinWidth width: `${this.projectorColumnWidth}px`
} }
]; ];
return columns; return columns;
@ -358,12 +362,7 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
} }
// hide the projector columns // hide the projector columns
if ( if (this.multiSelect || this.isMobile || !this.allowProjector) {
this.multiSelect ||
this.isMobile ||
!this.allowProjector ||
!this.operator.hasPerms('core.can_manage_projector')
) {
hidden.push('projector'); hidden.push('projector');
} }
@ -398,7 +397,8 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
vp: ViewportService, vp: ViewportService,
router: Router, router: Router,
private store: StorageService, private store: StorageService,
private cd: ChangeDetectorRef private cd: ChangeDetectorRef,
public projectorService: ProjectorService
) { ) {
vp.isMobileSubject.subscribe(mobile => { vp.isMobileSubject.subscribe(mobile => {
if (mobile !== this.isMobile) { if (mobile !== this.isMobile) {
@ -458,7 +458,7 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
// Define the columns. Has to be in the OnInit cause "columns" is slower than // Define the columns. Has to be in the OnInit cause "columns" is slower than
// the constructor of this class // the constructor of this class
this.columnSet = columnFactory() this.columnSet = columnFactory()
.default({ width: this.columnMinWidth }) .default({ width: '60px' })
.table(...this.defaultStartColumns, ...this.columns, ...this.defaultEndColumns) .table(...this.defaultStartColumns, ...this.columns, ...this.defaultEndColumns)
.build(); .build();
@ -483,6 +483,12 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
} }
} }
public isElementProjected = (context: PblNgridRowContext<V>) => {
if (this.projectorService.isProjected(this.getProjectable(context.$implicit as V))) {
return 'projected';
}
};
/** /**
* Determines and sets the raw data as observable lists according * Determines and sets the raw data as observable lists according
* to the used search and filter services * to the used search and filter services
@ -597,11 +603,8 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
* @param viewModel The model of the table * @param viewModel The model of the table
* @returns a view model that can be projected * @returns a view model that can be projected
*/ */
public getProjectable( public getProjectable(viewModel: V): BaseProjectableViewModel {
viewModel: BaseViewModelWithContentObject | BaseProjectableViewModel return (viewModel as BaseViewModelWithContentObject)?.contentObject ?? viewModel;
): BaseProjectableViewModel {
const withContent = viewModel as BaseViewModelWithContentObject;
return !!withContent.contentObject ? withContent.contentObject : viewModel;
} }
/** /**

View File

@ -33,19 +33,31 @@
<div *pblNgridCellDef="'title'; row as item; rowContext as rowContext" class="cell-slot fill"> <div *pblNgridCellDef="'title'; row as item; rowContext as rowContext" class="cell-slot fill">
<a class="detail-link" [routerLink]="getDetailUrl(item)" *ngIf="!isMultiSelect"></a> <a class="detail-link" [routerLink]="getDetailUrl(item)" *ngIf="!isMultiSelect"></a>
<div [ngStyle]="{ 'margin-left': item.level * 25 + 'px' }" class="innerTable"> <div [ngStyle]="{ 'margin-left': item.level * 25 + 'px' }" class="innerTable">
<os-icon-container [noWrap]="true" [icon]="item.closed ? 'check' : null" size="large">
<div class="ellipsis-overflow"> <!-- Title line -->
<div class="title-line ellipsis-overflow">
<!-- Is Closed -->
<span class="icon-prefix" *ngIf="item.closed">
<mat-icon>check</mat-icon>
</span>
<!-- Title -->
<span>
{{ item.getListTitle() }} {{ item.getListTitle() }}
</span>
</div> </div>
<div *ngIf="showSubtitle" class="subtitle ellipsis-overflow">
<!-- Subtitle line -->
<div class="subtitle ellipsis-overflow">
{{ item.getSubtitle() }} {{ item.getSubtitle() }}
</div> </div>
<div *ngIf="item.comment" class="subtitle ellipsis-overflow">
<!-- Comment line -->
<div class="subtitle ellipsis-overflow" *ngIf="item.comment">
<os-icon-container size="small" icon="comment" [noWrap]="true"> <os-icon-container size="small" icon="comment" [noWrap]="true">
{{ item.comment }} {{ item.comment }}
</os-icon-container> </os-icon-container>
</div> </div>
</os-icon-container>
</div> </div>
</div> </div>
@ -121,7 +133,12 @@
<span>{{ 'Import' | translate }}</span> <span>{{ 'Import' | translate }}</span>
</button> </button>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<button mat-menu-item *osPerms="'agenda.can_manage'" class="red-warning-text" (click)="deleteAllSpeakersOfAllListsOfSpeakers()"> <button
mat-menu-item
*osPerms="'agenda.can_manage'"
class="red-warning-text"
(click)="deleteAllSpeakersOfAllListsOfSpeakers()"
>
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'Clear all list of speakers' | translate }}</span> <span>{{ 'Clear all list of speakers' | translate }}</span>
</button> </button>
@ -229,7 +246,12 @@
<span>{{ 'Remove from agenda' | translate }}</span> <span>{{ 'Remove from agenda' | translate }}</span>
</button> </button>
<button mat-menu-item class="red-warning-text" (click)="deleteTopic(item)" *ngIf="isTopic(item.contentObject)"> <button
mat-menu-item
class="red-warning-text"
(click)="deleteTopic(item)"
*ngIf="isTopic(item.contentObject)"
>
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'Delete' | translate }}</span> <span>{{ 'Delete' | translate }}</span>
</button> </button>

View File

@ -16,10 +16,3 @@
.align-right { .align-right {
margin-left: auto; margin-left: auto;
} }
/*
* Where is this used?
*/
.done-check {
margin-right: 10px;
}

View File

@ -68,7 +68,7 @@
</span> </span>
<!-- Has File --> <!-- Has File -->
<span class="attached-files" *ngIf="motion.hasAttachments()"> <span class="icon-prefix" *ngIf="motion.hasAttachments()">
<mat-icon>attach_file</mat-icon> <mat-icon>attach_file</mat-icon>
</span> </span>

View File

@ -74,7 +74,7 @@
</span> </span>
<!-- Has File --> <!-- Has File -->
<span class="attached-files" *ngIf="motion.hasAttachments()"> <span class="icon-prefix" *ngIf="motion.hasAttachments()">
<mat-icon>attach_file</mat-icon> <mat-icon>attach_file</mat-icon>
</span> </span>

View File

@ -4,7 +4,7 @@
font-weight: 500; font-weight: 500;
font-size: 16px; font-size: 16px;
.attached-files { .icon-prefix {
.mat-icon { .mat-icon {
display: inline-flex; display: inline-flex;
vertical-align: middle; vertical-align: middle;

View File

@ -33,6 +33,7 @@
@import './app/site/assignments/components/assignment-poll-detail/assignment-poll-detail-component.scss-theme.scss'; @import './app/site/assignments/components/assignment-poll-detail/assignment-poll-detail-component.scss-theme.scss';
@import './app/shared/components/progress-snack-bar/progress-snack-bar.component.scss-theme.scss'; @import './app/shared/components/progress-snack-bar/progress-snack-bar.component.scss-theme.scss';
@import './app/shared/components/jitsi/jitsi.component.scss-theme.scss'; @import './app/shared/components/jitsi/jitsi.component.scss-theme.scss';
@import './app/shared/components/list-view-table/list-view-table.component.scss-theme.scss';
/** fonts */ /** fonts */
@import './assets/styles/fonts.scss'; @import './assets/styles/fonts.scss';
@ -66,6 +67,7 @@ $narrow-spacing: (
@include os-assignment-poll-detail-style($theme); @include os-assignment-poll-detail-style($theme);
@include os-progress-snack-bar-style($theme); @include os-progress-snack-bar-style($theme);
@include os-jitsi-theme($theme); @include os-jitsi-theme($theme);
@include os-list-view-table-theme($theme);
} }
/** Load projector specific SCSS values */ /** Load projector specific SCSS values */