Merge pull request #4730 from FinnStutzenstein/motionBlockSlideLayout

Improved motion block slide layout
This commit is contained in:
Emanuel Schütze 2019-05-27 14:39:10 +02:00 committed by GitHub
commit 6f36094cde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 49 deletions

View File

@ -7,6 +7,9 @@ export interface MotionBlockSlideMotionRepresentation extends MotionTitleInforma
css_class: string; css_class: string;
}; };
recommendation_extension?: string; recommendation_extension?: string;
// This property will be calculated and saved here.
recommendationLabel?: string;
} }
export interface MotionBlockSlideData { export interface MotionBlockSlideData {

View File

@ -4,17 +4,24 @@
<h2><span translate>Motion block</span> {{ data.data.motions.length }} <span translate>motions</span></h2> <h2><span translate>Motion block</span> {{ data.data.motions.length }} <span translate>motions</span></h2>
</div> </div>
<div *ngIf="commonRecommendation" class="spacer-bottom-20">
<mat-basic-chip class="large" [ngClass]="getStateCssColor(0, 0)">
{{ commonRecommendation }}
</mat-basic-chip>
</div>
<table> <table>
<tr *ngFor="let i of rowsArray"> <tr *ngFor="let i of rowsArray">
<td *ngFor="let j of columnsArray"> <td *ngFor="let j of columnsArray">
<ng-container *ngIf="getMotion(i, j)"> <ng-container *ngIf="getMotion(i, j)">
<div class="ellipsis-overflow"> <div class="ellipsis-overflow spacer-top-5">
{{ getMotionTitle(i, j) }} {{ getMotionTitle(i, j) }}
</div> <br *ngIf="!shortDisplayStyle"/>
<div class="white"> <span class="white" *ngIf="!commonRecommendation && getMotion(i, j).recommendationLabel">
<mat-basic-chip *ngIf="hasRecommendation(i, j)" disableRipple [ngClass]="getStateCssColor(i, j)" class="block-recommendation"> <mat-basic-chip disableRipple [ngClass]="getStateCssColor(i, j)" class="block-recommendation">
{{ getRecommendationLabel(i, j) }} {{ getMotion(i, j).recommendationLabel }}
</mat-basic-chip> </mat-basic-chip>
</span>
</div> </div>
</ng-container> </ng-container>
</td> </td>

View File

@ -6,4 +6,9 @@ table {
td { td {
min-width: 50%; min-width: 50%;
vertical-align: top;
}
.mat-basic-chip.large {
font-size: 100%;
} }

View File

@ -8,10 +8,20 @@ import { StateCssClassMapping } from 'app/site/motions/models/view-workflow';
import { BaseMotionSlideComponent } from '../base/base-motion-slide'; import { BaseMotionSlideComponent } from '../base/base-motion-slide';
import { SlideData } from 'app/core/core-services/projector-data.service'; import { SlideData } from 'app/core/core-services/projector-data.service';
/** // Layout:
* The row threshold to switch from one to a two column layout // 1) Long layout: Motion title is shown and the motions are
*/ // displayed in two lines: title and recommendation. This
const TWO_COLUMNS_THRESHOLD = 8; // mode is used until #motions<=SHORT_LAYOUT_THRESHOLD. There
// are ROWS_PER_COLUMN_SHORT rows per column, is MAX_COLUMNS
// is reached. If so, thhe rows per columns will be ignored.
// 2) Short Layout: Just motion identifier and the recommendation
// in one line. This mode is used if #motions>SHORT_LAYOUT_THRESHOLD.
// The same as in the log layout holds, just with ROWS_PER_COLUMN_SHORT.
const ROWS_PER_COLUMN_SHORT = 8;
const ROWS_PER_COLUMN_LONG = 16;
const SHORT_LAYOUT_THRESHOLD = 8;
const MAX_COLUMNS = 3;
@Component({ @Component({
selector: 'os-motion-block-slide', selector: 'os-motion-block-slide',
@ -38,6 +48,36 @@ export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBl
this.motionRepo.getIdentifierOrTitle(b) this.motionRepo.getIdentifierOrTitle(b)
) )
); );
// Populate the motion with the recommendation_label
data.data.motions.forEach(motion => {
if (motion.recommendation) {
let recommendation = this.translate.instant(motion.recommendation.name);
if (motion.recommendation_extension) {
recommendation +=
' ' +
this.replaceReferencedMotions(
motion.recommendation_extension,
data.data.referenced_motions
);
}
motion.recommendationLabel = recommendation;
} else {
motion.recommendationLabel = null;
}
});
// Check, if all motions have the same recommendation label
if (data.data.motions.length > 0) {
const recommendationLabel = data.data.motions[0].recommendationLabel;
if (data.data.motions.every(motion => motion.recommendationLabel === recommendationLabel)) {
this.commonRecommendation = recommendationLabel;
}
} else {
this.commonRecommendation = null;
}
} else {
this.commonRecommendation = null;
} }
this._data = data; this._data = data;
} }
@ -46,6 +86,11 @@ export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBl
return this._data; return this._data;
} }
/**
* If this is set, all motions have the same recommendation, saved in this variable.
*/
public commonRecommendation: string | null = null;
/** /**
* @returns the amount of motions in this block * @returns the amount of motions in this block
*/ */
@ -57,38 +102,39 @@ export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBl
} }
} }
public get shortDisplayStyle(): boolean {
return this.motionsAmount > SHORT_LAYOUT_THRESHOLD;
}
/** /**
* @returns the amount of rows to display. * @returns the amount of rows to display.
*/ */
public get rows(): number { public get rows(): number {
let rows = this.motionsAmount; return Math.ceil(this.motionsAmount / this.columns);
if (this.motionsAmount > TWO_COLUMNS_THRESHOLD) {
rows = Math.ceil(rows / 2);
}
return rows;
} }
/** /**
* @returns an aray with [1, ..., this.rows] * @returns an aray with [0, ..., this.rows-1]
*/ */
public get rowsArray(): number[] { public get rowsArray(): number[] {
const indices = []; return this.makeIndicesArray(this.rows);
const rows = this.rows; }
for (let i = 0; i < rows; i++) {
indices.push(i); public get columns(): number {
const rowsPerColumn = this.shortDisplayStyle ? ROWS_PER_COLUMN_SHORT : ROWS_PER_COLUMN_LONG;
const columns = Math.ceil(this.motionsAmount / rowsPerColumn);
if (columns > MAX_COLUMNS) {
return MAX_COLUMNS;
} else {
return columns;
} }
return indices;
} }
/** /**
* @returns [0] or [0, 1] if one or two columns are used * @returns an aray with [0, ..., this.columns-1]
*/ */
public get columnsArray(): number[] { public get columnsArray(): number[] {
if (this.motionsAmount > TWO_COLUMNS_THRESHOLD) { return this.makeIndicesArray(this.columns);
return [0, 1];
} else {
return [0];
}
} }
public constructor(translate: TranslateService, motionRepo: MotionRepositoryService) { public constructor(translate: TranslateService, motionRepo: MotionRepositoryService) {
@ -96,6 +142,17 @@ export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBl
this.languageCollator = new Intl.Collator(this.translate.currentLang); this.languageCollator = new Intl.Collator(this.translate.currentLang);
} }
/**
* @returns an array [0, ..., n-1]
*/
public makeIndicesArray(n: number): number[] {
const indices = [];
for (let i = 0; i < n; i++) {
indices.push(i);
}
return indices;
}
/** /**
* Get the motion for the cell given by i and j * Get the motion for the cell given by i and j
* *
@ -108,10 +165,15 @@ export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBl
} }
/** /**
* @returns the title of the given motion. * @returns the title of the given motion. If no title should be shown, just the
* identifier is returned.
*/ */
public getMotionTitle(i: number, j: number): string { public getMotionTitle(i: number, j: number): string {
return this.motionRepo.getTitle(this.getMotion(i, j)); if (this.shortDisplayStyle) {
return this.motionRepo.getIdentifierOrTitle(this.getMotion(i, j));
} else {
return this.motionRepo.getTitle(this.getMotion(i, j));
}
} }
/** /**
@ -121,30 +183,10 @@ export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBl
return this.motionRepo.getIdentifierOrTitle(this.getMotion(i, j)); return this.motionRepo.getIdentifierOrTitle(this.getMotion(i, j));
} }
/**
* @returns true if the motion in cell i and j has a recommendation
*/
public hasRecommendation(i: number, j: number): boolean {
return !!this.getMotion(i, j).recommendation;
}
/** /**
* @returns the css color for the state of the motion in cell i and j * @returns the css color for the state of the motion in cell i and j
*/ */
public getStateCssColor(i: number, j: number): string { public getStateCssColor(i: number, j: number): string {
return StateCssClassMapping[this.getMotion(i, j).recommendation.css_class] || ''; return StateCssClassMapping[this.getMotion(i, j).recommendation.css_class] || '';
} }
/**
* @returns the recommendation label for motion in cell i and j
*/
public getRecommendationLabel(i: number, j: number): string {
const motion = this.getMotion(i, j);
let recommendation = this.translate.instant(motion.recommendation.name);
if (motion.recommendation_extension) {
recommendation +=
' ' + this.replaceReferencedMotions(motion.recommendation_extension, this.data.data.referenced_motions);
}
return recommendation;
}
} }

View File

@ -505,6 +505,9 @@ button.mat-menu-item.selected {
.spacer-top-3 { .spacer-top-3 {
margin-top: 3px !important; margin-top: 3px !important;
} }
.spacer-top-5 {
margin-top: 5px !important;
}
.spacer-top-10 { .spacer-top-10 {
margin-top: 10px !important; margin-top: 10px !important;
} }