Merge pull request #4730 from FinnStutzenstein/motionBlockSlideLayout
Improved motion block slide layout
This commit is contained in:
commit
6f36094cde
@ -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 {
|
||||||
|
@ -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>
|
||||||
|
@ -6,4 +6,9 @@ table {
|
|||||||
|
|
||||||
td {
|
td {
|
||||||
min-width: 50%;
|
min-width: 50%;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-basic-chip.large {
|
||||||
|
font-size: 100%;
|
||||||
}
|
}
|
||||||
|
@ -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,11 +165,16 @@ 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 {
|
||||||
|
if (this.shortDisplayStyle) {
|
||||||
|
return this.motionRepo.getIdentifierOrTitle(this.getMotion(i, j));
|
||||||
|
} else {
|
||||||
return this.motionRepo.getTitle(this.getMotion(i, j));
|
return this.motionRepo.getTitle(this.getMotion(i, j));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the identifier (of title if identifier not availabe) of the given motion.
|
* @returns the identifier (of title if identifier not availabe) of the given motion.
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user