Merge pull request #5765 from tsiegleauq/poll-create-layout-enhance

Enhance poll dialog layout
This commit is contained in:
Emanuel Schütze 2020-12-13 21:37:08 +01:00 committed by GitHub
commit acd33b8207
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 124 additions and 107 deletions

View File

@ -3,6 +3,7 @@
[matTooltip]="iconTooltip" [matTooltip]="iconTooltip"
[class]="iconAction ? 'pointer' : ''" [class]="iconAction ? 'pointer' : ''"
(click)="iconClick()" (click)="iconClick()"
[color]="color"
>{{ icon }}</mat-icon >{{ icon }}</mat-icon
> >
<span class="content-node" [ngClass]="noWrap ? 'single-line' : 'break-lines'"> <span class="content-node" [ngClass]="noWrap ? 'single-line' : 'break-lines'">
@ -13,5 +14,6 @@
[matTooltip]="iconTooltip" [matTooltip]="iconTooltip"
[class]="iconAction ? 'pointer' : ''" [class]="iconAction ? 'pointer' : ''"
(click)="iconClick()" (click)="iconClick()"
[color]="color"
>{{ icon }}</mat-icon >{{ icon }}</mat-icon
> >

View File

@ -51,6 +51,9 @@ export class IconContainerComponent {
@Input() @Input()
public noWrap = false; public noWrap = false;
@Input()
public color: string;
/** /**
* Optional action for clicking on the icon. * Optional action for clicking on the icon.
*/ */

View File

@ -1,24 +1,3 @@
.submit-buttons {
display: flex;
justify-content: flex-end;
}
.meta-text {
font-style: italic;
margin-left: 10px;
margin-right: 10px;
mat-chip {
margin-left: 5px;
margin-right: 2px;
}
}
.candidate-name {
word-wrap: break-word;
width: 100%;
border-bottom: 1px solid grey;
}
.votes-grid { .votes-grid {
display: grid; display: grid;
grid-gap: 5px; grid-gap: 5px;
@ -29,12 +8,3 @@
width: 100%; width: 100%;
} }
} }
.sum-value {
display: flex;
justify-content: flex-end;
}
.width-600 {
max-width: 600px;
}

View File

@ -9,13 +9,22 @@
</mat-card-title> </mat-card-title>
<!-- Type and State --> <!-- Type and State -->
<div class="italic spacer-bottom-20"> <div class="type-and-state italic spacer-bottom-20">
<span *osPerms="'assignments.can_manage'; and: poll.type === 'pseudoanonymous'"> <span *osPerms="'assignments.can_manage'; and: poll.isEVoting">
<button mat-icon-button color="warn" (click)="openVotingWarning()"> <os-icon-container
<mat-icon> warning </mat-icon> class="poll-type"
</button> icon="info"
size="large"
color="primary"
[swap]="true"
[showIcon]="poll.isAnon"
(iconAction)="openVotingWarning()"
>
{{ poll.typeVerbose | translate }}
</os-icon-container>
<span *ngIf="poll.isAnon">&nbsp;</span>
&middot;&nbsp;
</span> </span>
<span *ngIf="poll.type !== 'analog'"> {{ poll.typeVerbose | translate }} &middot; </span>
<span> <span>
{{ poll.stateVerbose | translate }} {{ poll.stateVerbose | translate }}
</span> </span>

View File

@ -19,4 +19,14 @@
display: flex; display: flex;
margin: auto 0; margin: auto 0;
} }
.type-and-state {
display: flex;
span {
display: flex;
}
.poll-type {
width: fit-content;
}
}
} }

View File

@ -21,21 +21,28 @@
<!-- Subtitle --> <!-- Subtitle -->
<div class="italic spacer-bottom-20"> <div class="italic spacer-bottom-20">
<span *osPerms="'motions.can_manage_polls'; and: poll.type === 'pseudoanonymous'"> <!-- Type and State -->
<button mat-icon-button color="warn" (click)="openVotingWarning()"> <div class="type-and-state italic spacer-bottom-20">
<mat-icon>warning</mat-icon> <span *osPerms="permission.motionsCanManagePolls; and: poll.isEVoting">
</button> <os-icon-container
</span> class="poll-type"
icon="info"
color="primary"
[swap]="true"
[showIcon]="poll.isAnon"
(iconAction)="openVotingWarning()"
>
{{ poll.typeVerbose | translate }}
</os-icon-container>
<span *ngIf="poll.isAnon">&nbsp;</span>
&middot;&nbsp;
</span>
<!-- Subtitle --> <!-- State -->
<span *ngIf="pollService.isElectronicVotingEnabled && poll.type !== 'analog'"> <span>
{{ poll.typeVerbose | translate }} &middot; {{ poll.stateVerbose | translate }}
</span> </span>
</div>
<!-- State chip -->
<span>
{{ poll.stateVerbose | translate }}
</span>
</div> </div>
<!-- Change state button --> <!-- Change state button -->

View File

@ -28,6 +28,16 @@
} }
} }
.type-and-state {
display: flex;
span {
display: flex;
}
.poll-type {
width: fit-content;
}
}
.poll-chart-wrapper { .poll-chart-wrapper {
cursor: pointer; cursor: pointer;
display: grid; display: grid;

View File

@ -10,21 +10,26 @@
<form [formGroup]="contentForm" class="poll-preview-meta-info-form"> <form [formGroup]="contentForm" class="poll-preview-meta-info-form">
<ng-container *ngIf="!data || !data.state || data.isCreated"> <ng-container *ngIf="!data || !data.state || data.isCreated">
<!-- Poll Type --> <div class="info-grid">
<mat-form-field class="pollType" *ngIf="isEVotingEnabled"> <!-- Poll Type -->
<mat-select [placeholder]="PollPropertyVerbose.type | translate" formControlName="type" required> <mat-form-field *ngIf="isEVotingEnabled">
<mat-option *ngFor="let option of pollTypes | keyvalue" [value]="option.key"> <mat-select [placeholder]="PollPropertyVerbose.type | translate" formControlName="type" required>
{{ option.value | translate }} <mat-option *ngFor="let option of pollTypes | keyvalue" [value]="option.key">
</mat-option> {{ option.value | translate }}
</mat-select> </mat-option>
<mat-error>{{ 'This field is required.' | translate }}</mat-error> </mat-select>
<mat-hint (click)="openVotingWarning()" *ngIf="showNonNominalWarning"> <mat-icon
{{ 'Not suitable for formal secret voting!' | translate }}</mat-hint color="primary"
> matSuffix
</mat-form-field> *ngIf="showNonNominalWarning"
(click)="openVotingWarning($event)"
>
info
</mat-icon>
<mat-error>{{ 'This field is required.' | translate }}</mat-error>
</mat-form-field>
<!-- Groups entitled to Vote --> <!-- Groups entitled to Vote -->
<div class="suboption">
<mat-form-field *ngIf="contentForm.get('type').value && contentForm.get('type').value !== 'analog'"> <mat-form-field *ngIf="contentForm.get('type').value && contentForm.get('type').value !== 'analog'">
<os-search-value-selector <os-search-value-selector
formControlName="groups_id" formControlName="groups_id"
@ -37,34 +42,36 @@
</mat-form-field> </mat-form-field>
</div> </div>
<!-- Poll Methods --> <div class="info-grid">
<mat-form-field *ngIf="pollMethods"> <!-- Poll Methods -->
<mat-select <mat-form-field *ngIf="pollMethods">
[placeholder]="PollPropertyVerbose.pollmethod | translate" <mat-select
formControlName="pollmethod" [placeholder]="PollPropertyVerbose.pollmethod | translate"
required formControlName="pollmethod"
> required
<mat-option *ngFor="let option of pollMethods | keyvalue: keepEntryOrder" [value]="option.key"> >
{{ option.value | translate }} <mat-option *ngFor="let option of pollMethods | keyvalue: keepEntryOrder" [value]="option.key">
</mat-option> {{ option.value | translate }}
</mat-select> </mat-option>
<mat-error>{{ 'This field is required.' | translate }}</mat-error> </mat-select>
</mat-form-field> <mat-error>{{ 'This field is required.' | translate }}</mat-error>
</ng-container> </mat-form-field>
<!-- Amount of Votes and global options --> <!-- Amount of Votes -->
<div class="suboption" *ngIf="showAmountAndGlobal(data)"> <mat-form-field *ngIf="showAmountAndGlobal(data)">
<mat-form-field> <input
<input type="number"
type="number" matInput
matInput placeholder="{{ PollPropertyVerbose.votes_amount | translate }}"
placeholder="{{ PollPropertyVerbose.votes_amount | translate }}" formControlName="votes_amount"
formControlName="votes_amount" min="1"
min="1" required
required />
/> </mat-form-field>
</mat-form-field> </div>
<div class="global-options">
<!-- Amount of Votes and global options -->
<div class="global-options" *ngIf="showAmountAndGlobal(data)">
<mat-checkbox formControlName="global_yes"> <mat-checkbox formControlName="global_yes">
{{ PollPropertyVerbose.global_yes | translate }} {{ PollPropertyVerbose.global_yes | translate }}
</mat-checkbox> </mat-checkbox>
@ -73,7 +80,7 @@
{{ PollPropertyVerbose.global_abstain | translate }} {{ PollPropertyVerbose.global_abstain | translate }}
</mat-checkbox> </mat-checkbox>
</div> </div>
</div> </ng-container>
<!-- 100 Percent Base --> <!-- 100 Percent Base -->
<mat-form-field> <mat-form-field>

View File

@ -2,16 +2,11 @@
margin: 0; margin: 0;
} }
.pollType {
.mat-hint {
color: red;
cursor: pointer;
}
}
.poll-preview-meta-info-form { .poll-preview-meta-info-form {
.suboption { .info-grid {
margin-left: 1.5em; display: grid;
column-gap: 1em;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
} }
.mat-checkbox { .mat-checkbox {

View File

@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
@ -29,7 +29,8 @@ import { PollService } from '../../services/poll.service';
@Component({ @Component({
selector: 'os-poll-form', selector: 'os-poll-form',
templateUrl: './poll-form.component.html', templateUrl: './poll-form.component.html',
styleUrls: ['./poll-form.component.scss'] styleUrls: ['./poll-form.component.scss'],
encapsulation: ViewEncapsulation.None
}) })
export class PollFormComponent<T extends ViewBasePoll, S extends PollService> export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
extends BaseViewComponentDirective extends BaseViewComponentDirective
@ -160,14 +161,16 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
this.contentForm.get('pollmethod').valueChanges.subscribe(method => { this.contentForm.get('pollmethod').valueChanges.subscribe(method => {
if (method) { if (method) {
this.updatePercentBases(method); this.updatePercentBases(method);
this.setVotesAmountCtrl(); this.setWarning();
} }
}), }),
// poll type changes // poll type changes
this.contentForm.get('type').valueChanges.subscribe(() => { this.contentForm.get('type').valueChanges.subscribe(() => {
this.setVotesAmountCtrl(); this.setWarning();
}) })
); );
this.setWarning();
} }
private disablePollType(): void { private disablePollType(): void {
@ -232,7 +235,7 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
* Disable votes_amount form control if the poll type is anonymous * Disable votes_amount form control if the poll type is anonymous
* and the poll method is votes. * and the poll method is votes.
*/ */
private setVotesAmountCtrl(): void { private setWarning(): void {
if (this.contentForm.get('type').value === PollType.Pseudoanonymous) { if (this.contentForm.get('type').value === PollType.Pseudoanonymous) {
this.showNonNominalWarning = true; this.showNonNominalWarning = true;
} else { } else {
@ -297,7 +300,8 @@ export class PollFormComponent<T extends ViewBasePoll, S extends PollService>
}); });
} }
public openVotingWarning(): void { public openVotingWarning(event: MouseEvent): void {
event.stopPropagation();
this.dialog.open(VotingPrivacyWarningComponent, infoDialogSettings); this.dialog.open(VotingPrivacyWarningComponent, infoDialogSettings);
} }