Enhance motion extension field

Enhances the motion extension field to make it even more easily
accessible.

Also allows to quickly jump between motions using:
Meta + Alt(left) + ArrowKeyLeft/Right
This commit is contained in:
Sean Engelhardt 2019-07-09 22:06:12 +02:00
parent 3f6fe28f35
commit 719f1f8031
4 changed files with 129 additions and 40 deletions

View File

@ -2,17 +2,22 @@
<div> <div>
<h4 *ngIf="title">{{ title }}</h4> <h4 *ngIf="title">{{ title }}</h4>
<mat-menu #triggerMenu="matMenu"> <mat-menu #triggerMenu="matMenu">
<ng-container <ng-container [ngTemplateOutlet]="triggerTemplate"> </ng-container>
[ngTemplateOutlet]="triggerTemplate">
</ng-container>
</mat-menu> </mat-menu>
<os-icon-container <os-icon-container
iconTooltip="{{ 'Edit' | translate }}" iconTooltip="{{ 'Edit' | translate }}"
icon="create" icon="create"
[swap]="true" [swap]="true"
[showIcon]="!editMode && canBeEdited && hasExtension" [showIcon]="!editMode && canBeEdited && hasExtension"
(iconAction)="changeEditMode()"> (iconAction)="changeEditMode()"
<mat-basic-chip *ngIf="canBeEdited" [matMenuTriggerFor]="triggerMenu" [ngClass]="classes" class="pointer" disableRipple> >
<mat-basic-chip
*ngIf="canBeEdited"
[matMenuTriggerFor]="triggerMenu"
[ngClass]="classes"
class="pointer"
disableRipple
>
{{ chipValue || '' }} {{ chipValue || '' }}
</mat-basic-chip> </mat-basic-chip>
<mat-basic-chip *ngIf="!canBeEdited" [ngClass]="classes" disableRipple> <mat-basic-chip *ngIf="!canBeEdited" [ngClass]="classes" disableRipple>
@ -22,15 +27,14 @@
</div> </div>
<!-- Extension field --> <!-- Extension field -->
<div <div *ngIf="hasExtension && editMode" class="spacer-top-10 extension-container">
*ngIf="hasExtension && editMode"
class="spacer-top-10 extension-container"
>
<mat-form-field> <mat-form-field>
<input <input
matInput matInput
osAutofocus
[(ngModel)]="inputControl" [(ngModel)]="inputControl"
placeholder="{{ extensionLabel }}" placeholder="{{ extensionLabel }}"
(keydown)="keyDownFunction($event)"
/> />
</mat-form-field> </mat-form-field>
<os-search-value-selector <os-search-value-selector

View File

@ -1,4 +1,4 @@
import { Component, OnInit, Input, Output, EventEmitter, OnDestroy } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter, OnDestroy, AfterViewInit } from '@angular/core';
import { BehaviorSubject, Subscription } from 'rxjs'; import { BehaviorSubject, Subscription } from 'rxjs';
import { FormGroup, FormBuilder } from '@angular/forms'; import { FormGroup, FormBuilder } from '@angular/forms';
import { Router, NavigationEnd } from '@angular/router'; import { Router, NavigationEnd } from '@angular/router';
@ -8,7 +8,7 @@ import { Router, NavigationEnd } from '@angular/router';
templateUrl: './extension-field.component.html', templateUrl: './extension-field.component.html',
styleUrls: ['./extension-field.component.scss'] styleUrls: ['./extension-field.component.scss']
}) })
export class ExtensionFieldComponent implements OnInit, OnDestroy { export class ExtensionFieldComponent implements OnInit, OnDestroy, AfterViewInit {
/** /**
* Optional additional classes for the `mat-chip`. * Optional additional classes for the `mat-chip`.
*/ */
@ -27,11 +27,33 @@ export class ExtensionFieldComponent implements OnInit, OnDestroy {
@Input() @Input()
public chipValue: string; public chipValue: string;
/**
* Allow automatically jump into autoEdit
*/
private allowAutoEdit = false;
/** /**
* Boolean, whether the extension should be shown. * Boolean, whether the extension should be shown.
*/ */
@Input() private _hasExtension = false;
public hasExtension = false;
/**
* Setter for the extension condition
*/
@Input() public set hasExtension(extension: boolean) {
this._hasExtension = extension;
if (this.hasExtension && this.allowAutoEdit) {
this.editMode = true;
}
}
/**
* Getter for the extension condition
*/
public get hasExtension(): boolean {
return this._hasExtension;
}
/** /**
* Optional label for the input. * Optional label for the input.
@ -119,6 +141,11 @@ export class ExtensionFieldComponent implements OnInit, OnDestroy {
*/ */
private navigationSubscription: Subscription; private navigationSubscription: Subscription;
/**
* Subscription for the search value selector
*/
private searchValueSubscription: Subscription;
/** /**
* Constructor * Constructor
* *
@ -133,33 +160,63 @@ export class ExtensionFieldComponent implements OnInit, OnDestroy {
this.navigationSubscription = this.router.events.subscribe(navEvent => { this.navigationSubscription = this.router.events.subscribe(navEvent => {
if (navEvent instanceof NavigationEnd) { if (navEvent instanceof NavigationEnd) {
this.editMode = false; this.editMode = false;
if (this.extensionFieldForm) {
this.extensionFieldForm.reset(); this.extensionFieldForm.reset();
} }
}
}); });
this.initInput(); this.initInput();
if (this.searchList) {
this.extensionFieldForm = this.fb.group({ this.extensionFieldForm = this.fb.group({
list: this.searchList ? [[]] : undefined list: [[]]
}); });
this.extensionFieldForm.get('list').valueChanges.subscribe((value: number) => { this.searchValueSubscription = this.extensionFieldForm
.get('list')
.valueChanges.subscribe((value: number) => {
if (!!value) {
if (this.listSubmitOnChange) { if (this.listSubmitOnChange) {
this.listChange.emit(value); this.listChange.emit(value);
} }
if (this.appendValueToInput && this.inputControl.length) { if (this.appendValueToInput) {
this.inputControl = this.inputControl.concat( if (!this.inputControl) {
`[${this.listValuePrefix}${value}${this.listValueSuffix}]` this.inputControl = '';
); }
this.inputControl += `[${this.listValuePrefix}${value}${this.listValueSuffix}]`;
}
this.extensionFieldForm.reset();
} }
}); });
} }
}
/** /**
* On destroy unsubscribe from the nav subscription * After view inits, allow to automatically open the edit view
*/
public ngAfterViewInit(): void {
this.allowAutoEdit = true;
}
/**
* On destroy unsubscribe from the subscriptions
*/ */
public ngOnDestroy(): void { public ngOnDestroy(): void {
this.navigationSubscription.unsubscribe(); this.navigationSubscription.unsubscribe();
if (this.searchValueSubscription) {
this.searchValueSubscription.unsubscribe();
}
}
/**
* Hitting enter on the input field should save the content
*/
public keyDownFunction(event: any): void {
if (event.key === 'Enter') {
this.changeEditMode(true);
}
} }
/** /**

View File

@ -1,3 +1,4 @@
<div (window:keydown)="onKeyNavigation($event)"></div>
<os-head-bar <os-head-bar
[mainButton]="perms.isAllowed('update', motion)" [mainButton]="perms.isAllowed('update', motion)"
mainButtonIcon="edit" mainButtonIcon="edit"
@ -101,7 +102,12 @@
<span translate>Show entire motion text</span> <span translate>Show entire motion text</span>
</button> </button>
<button mat-menu-item *osPerms="'core.can_see_history'" [routerLink]="['/history']" [queryParams]="{element: motion.elementId}"> <button
mat-menu-item
*osPerms="'core.can_see_history'"
[routerLink]="['/history']"
[queryParams]="{ element: motion.elementId }"
>
<mat-icon>history</mat-icon> <mat-icon>history</mat-icon>
<span translate> <span translate>
History History
@ -126,10 +132,16 @@
<div class="title-line"> <div class="title-line">
<h1 class="motion-title"> <h1 class="motion-title">
<span *ngIf="titleCanBeChanged()"> <span *ngIf="titleCanBeChanged()">
<span class="title-change-indicator" *ngIf="getTitleChangingObject()" <span
(click)="gotoChangeRecommendation(getTitleChangingObject())"></span> class="title-change-indicator"
<span class="change-title" *osPerms="'motions.can_manage'; and: !getTitleChangingObject()" *ngIf="getTitleChangingObject()"
(click)="createTitleChangeRecommendation()"></span> (click)="gotoChangeRecommendation(getTitleChangingObject())"
></span>
<span
class="change-title"
*osPerms="'motions.can_manage'; and: !getTitleChangingObject()"
(click)="createTitleChangeRecommendation()"
></span>
</span> </span>
{{ getTitleWithChanges() }} {{ getTitleWithChanges() }}
@ -250,7 +262,8 @@
[inputValue]="newStateExtension" [inputValue]="newStateExtension"
[classes]="motion.stateCssColor" [classes]="motion.stateCssColor"
extensionLabel="{{ 'Extension' | translate }}" extensionLabel="{{ 'Extension' | translate }}"
(success)="setStateExtension($event)"> (success)="setStateExtension($event)"
>
<span class="trigger-menu"> <span class="trigger-menu">
<button *ngFor="let state of motion.nextStates" mat-menu-item (click)="setState(state.id)"> <button *ngFor="let state of motion.nextStates" mat-menu-item (click)="setState(state.id)">
{{ state.name | translate }} <span *ngIf="state.show_state_extension_field">&nbsp;...</span> {{ state.name | translate }} <span *ngIf="state.show_state_extension_field">&nbsp;...</span>
@ -281,7 +294,8 @@
[searchList]="motionObserver" [searchList]="motionObserver"
searchListLabel="{{ 'Motions' | translate }}" searchListLabel="{{ 'Motions' | translate }}"
listValuePrefix="motion:" listValuePrefix="motion:"
(success)="setRecommendationExtension($event)"> (success)="setRecommendationExtension($event)"
>
<span class="trigger-menu"> <span class="trigger-menu">
<button <button
*ngFor="let recommendation of motion.possibleRecommendations" *ngFor="let recommendation of motion.possibleRecommendations"
@ -292,10 +306,7 @@
<span *ngIf="recommendation.show_recommendation_extension_field">&nbsp;...</span> <span *ngIf="recommendation.show_recommendation_extension_field">&nbsp;...</span>
</button> </button>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<button <button mat-menu-item (click)="setRecommendation(null)">
mat-menu-item
(click)="setRecommendation(null)"
>
<mat-icon>replay</mat-icon> {{ 'Reset recommendation' | translate }} <mat-icon>replay</mat-icon> {{ 'Reset recommendation' | translate }}
</button> </button>
</span> </span>
@ -781,7 +792,10 @@
</mat-list> </mat-list>
</div> </div>
<div *osPerms="'motions.can_manage'; and: editMotion"> <div *osPerms="'motions.can_manage'; and: editMotion">
<os-attachment-control (errorHandler)="showUploadError($event)" [controlName]="contentForm.get('attachments_id')"></os-attachment-control> <os-attachment-control
(errorHandler)="showUploadError($event)"
[controlName]="contentForm.get('attachments_id')"
></os-attachment-control>
</div> </div>
</div> </div>

View File

@ -745,6 +745,20 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
} }
} }
/**
* Using Meta, Alt + the arrow keys will navigate between the motions
*
* @param event has the key code
*/
public onKeyNavigation(event: KeyboardEvent): void {
if (event.key === 'ArrowLeft' && event.altKey && event.metaKey) {
this.navigateToMotion(this.previousMotion);
}
if (event.key === 'ArrowRight' && event.altKey && event.metaKey) {
this.navigateToMotion(this.nextMotion);
}
}
/** /**
* Before updating or creating, the motions needs to be prepared for paragraph based amendments. * Before updating or creating, the motions needs to be prepared for paragraph based amendments.
* A motion of type T is created, prepared and deserialized from the given motionValues * A motion of type T is created, prepared and deserialized from the given motionValues