Merge pull request #4648 from FinnStutzenstein/motionBlockSlideTwoColumns

Dynamic a two column layout in motion block slides
This commit is contained in:
Emanuel Schütze 2019-04-30 16:38:58 +02:00 committed by GitHub
commit 9f5226c1d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 113 additions and 23 deletions

View File

@ -4,14 +4,18 @@
<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 *ngFor="let motion of data.data.motions" class="blockitem"> <table>
<tr *ngFor="let i of rowsArray">
<td *ngFor="let j of columnsArray">
<div class="ellipsis-overflow"> <div class="ellipsis-overflow">
{{ getMotionTitle(motion) }} {{ getMotionTitle(i, j) }}
</div> </div>
<div class="white ellipsis-overflow"> <div class="white"><!-- ellipsis-overflow?? -->
<mat-basic-chip *ngIf="motion.recommendation" disableRipple [ngClass]="getStateCssColor(motion)" class="block-recommendation"> <mat-basic-chip *ngIf="hasRecommendation(i, j)" disableRipple [ngClass]="getStateCssColor(i, j)" class="block-recommendation">
{{ getRecommendationLabel(motion) }} {{ getRecommendationLabel(i, j) }}
</mat-basic-chip> </mat-basic-chip>
</div> </div>
</div> </td>
</tr>
</table>
</div> </div>

View File

@ -1,4 +1,9 @@
.blockitem { table {
line-height: 1; line-height: 1;
margin-bottom: 10px; width: 100%;
table-layout: fixed;
}
td {
min-width: 50%;
} }

View File

@ -5,9 +5,14 @@ import { TranslateService } from '@ngx-translate/core';
import { MotionBlockSlideData, MotionBlockSlideMotionRepresentation } from './motion-block-slide-data'; import { MotionBlockSlideData, MotionBlockSlideMotionRepresentation } from './motion-block-slide-data';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { StateCssClassMapping } from 'app/site/motions/models/view-workflow'; import { StateCssClassMapping } from 'app/site/motions/models/view-workflow';
import { BaseMotionSlideComponent, MotionTitleInformation } 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';
/**
* The row threshold to switch from one to a two column layout
*/
const TWO_COLUMNS_THRESHOLD = 8;
@Component({ @Component({
selector: 'os-motion-block-slide', selector: 'os-motion-block-slide',
templateUrl: './motion-block-slide.component.html', templateUrl: './motion-block-slide.component.html',
@ -28,7 +33,10 @@ export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBl
public set data(data: SlideData<MotionBlockSlideData>) { public set data(data: SlideData<MotionBlockSlideData>) {
if (data && data.data.motions) { if (data && data.data.motions) {
data.data.motions = data.data.motions.sort((a, b) => data.data.motions = data.data.motions.sort((a, b) =>
this.languageCollator.compare(this.getMotionIdentifier(a), this.getMotionIdentifier(b)) this.languageCollator.compare(
this.motionRepo.getIdentifierOrTitle(a),
this.motionRepo.getIdentifierOrTitle(b)
)
); );
} }
this._data = data; this._data = data;
@ -38,30 +46,100 @@ export class MotionBlockSlideComponent extends BaseMotionSlideComponent<MotionBl
return this._data; return this._data;
} }
/**
* @returns the amount of motions in this block
*/
public get motionsAmount(): number {
if (this.data && this.data.data.motions) {
return this.data.data.motions.length;
} else {
return 0;
}
}
/**
* @returns the amount of rows to display.
*/
public get rows(): number {
let rows = this.motionsAmount;
if (this.motionsAmount > TWO_COLUMNS_THRESHOLD) {
rows = Math.ceil(rows / 2);
}
return rows;
}
/**
* @returns an aray with [1, ..., this.rows]
*/
public get rowsArray(): number[] {
const indices = [];
const rows = this.rows;
for (let i = 0; i < rows; i++) {
indices.push(i);
}
return indices;
}
/**
* @returns [0] or [0, 1] if one or two columns are used
*/
public get columnsArray(): number[] {
if (this.motionsAmount > TWO_COLUMNS_THRESHOLD) {
return [0, 1];
} else {
return [0];
}
}
public constructor(translate: TranslateService, motionRepo: MotionRepositoryService) { public constructor(translate: TranslateService, motionRepo: MotionRepositoryService) {
super(translate, motionRepo); super(translate, motionRepo);
this.languageCollator = new Intl.Collator(this.translate.currentLang); this.languageCollator = new Intl.Collator(this.translate.currentLang);
} }
/**
* Get the motion for the cell given by i and j
*
* @param i the row
* @param j the column
*/
private getMotion(i: number, j: number): MotionBlockSlideMotionRepresentation {
const index = i + this.rows * j;
return this.data.data.motions[index];
}
/** /**
* @returns the title of the given motion. * @returns the title of the given motion.
*/ */
public getMotionTitle(motion: MotionTitleInformation): string { public getMotionTitle(i: number, j: number): string {
return this.motionRepo.getTitle(motion); 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.
*/ */
public getMotionIdentifier(motion: MotionTitleInformation): string { public getMotionIdentifier(i: number, j: number): string {
return this.motionRepo.getIdentifierOrTitle(motion); return this.motionRepo.getIdentifierOrTitle(this.getMotion(i, j));
} }
public getStateCssColor(motion: MotionBlockSlideMotionRepresentation): string { /**
return StateCssClassMapping[motion.recommendation.css_class] || ''; * @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;
} }
public getRecommendationLabel(motion: MotionBlockSlideMotionRepresentation): string { /**
* @returns the css color for the state of the motion in cell i and j
*/
public getStateCssColor(i: number, j: number): string {
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); let recommendation = this.translate.instant(motion.recommendation.name);
if (motion.recommendation_extension) { if (motion.recommendation_extension) {
recommendation += recommendation +=

View File

@ -1,5 +1,5 @@
import re import re
from typing import Any, Dict from typing import Any, Dict, Optional
from ..users.projector import get_user_name from ..users.projector import get_user_name
from ..utils.projector import ( from ..utils.projector import (
@ -121,13 +121,16 @@ async def get_amendment_base_statute(amendment, all_data):
async def extend_reference_motion_dict( async def extend_reference_motion_dict(
all_data: AllData, all_data: AllData,
recommendation: str, recommendation: Optional[str],
referenced_motions: Dict[int, Dict[str, str]], referenced_motions: Dict[int, Dict[str, str]],
) -> None: ) -> None:
""" """
Extends a dict of motion ids mapped to their title information. Extends a dict of motion ids mapped to their title information.
The client can replace the placeholders in the recommendation correctly. The client can replace the placeholders in the recommendation correctly.
""" """
if recommendation is None:
return
# Collect all meantioned motions via [motion:<id>] # Collect all meantioned motions via [motion:<id>]
referenced_ids = [ referenced_ids = [
int(id) for id in motion_placeholder_regex.findall(recommendation) int(id) for id in motion_placeholder_regex.findall(recommendation)