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>
<h4 *ngIf="title">{{ title }}</h4>
<mat-menu #triggerMenu="matMenu">
<ng-container
[ngTemplateOutlet]="triggerTemplate">
</ng-container>
<ng-container [ngTemplateOutlet]="triggerTemplate"> </ng-container>
</mat-menu>
<os-icon-container
iconTooltip="{{ 'Edit' | translate }}"
icon="create"
[swap]="true"
[showIcon]="!editMode && canBeEdited && hasExtension"
(iconAction)="changeEditMode()">
<mat-basic-chip *ngIf="canBeEdited" [matMenuTriggerFor]="triggerMenu" [ngClass]="classes" class="pointer" disableRipple>
(iconAction)="changeEditMode()"
>
<mat-basic-chip
*ngIf="canBeEdited"
[matMenuTriggerFor]="triggerMenu"
[ngClass]="classes"
class="pointer"
disableRipple
>
{{ chipValue || '' }}
</mat-basic-chip>
<mat-basic-chip *ngIf="!canBeEdited" [ngClass]="classes" disableRipple>
@ -22,15 +27,14 @@
</div>
<!-- Extension field -->
<div
*ngIf="hasExtension && editMode"
class="spacer-top-10 extension-container"
>
<div *ngIf="hasExtension && editMode" class="spacer-top-10 extension-container">
<mat-form-field>
<input
matInput
osAutofocus
[(ngModel)]="inputControl"
placeholder="{{ extensionLabel }}"
(keydown)="keyDownFunction($event)"
/>
</mat-form-field>
<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 { FormGroup, FormBuilder } from '@angular/forms';
import { Router, NavigationEnd } from '@angular/router';
@ -8,7 +8,7 @@ import { Router, NavigationEnd } from '@angular/router';
templateUrl: './extension-field.component.html',
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`.
*/
@ -27,11 +27,33 @@ export class ExtensionFieldComponent implements OnInit, OnDestroy {
@Input()
public chipValue: string;
/**
* Allow automatically jump into autoEdit
*/
private allowAutoEdit = false;
/**
* Boolean, whether the extension should be shown.
*/
@Input()
public hasExtension = false;
private _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.
@ -119,6 +141,11 @@ export class ExtensionFieldComponent implements OnInit, OnDestroy {
*/
private navigationSubscription: Subscription;
/**
* Subscription for the search value selector
*/
private searchValueSubscription: Subscription;
/**
* Constructor
*
@ -133,33 +160,63 @@ export class ExtensionFieldComponent implements OnInit, OnDestroy {
this.navigationSubscription = this.router.events.subscribe(navEvent => {
if (navEvent instanceof NavigationEnd) {
this.editMode = false;
this.extensionFieldForm.reset();
if (this.extensionFieldForm) {
this.extensionFieldForm.reset();
}
}
});
this.initInput();
this.extensionFieldForm = this.fb.group({
list: this.searchList ? [[]] : undefined
});
if (this.searchList) {
this.extensionFieldForm = this.fb.group({
list: [[]]
});
this.extensionFieldForm.get('list').valueChanges.subscribe((value: number) => {
if (this.listSubmitOnChange) {
this.listChange.emit(value);
}
if (this.appendValueToInput && this.inputControl.length) {
this.inputControl = this.inputControl.concat(
`[${this.listValuePrefix}${value}${this.listValueSuffix}]`
);
}
});
this.searchValueSubscription = this.extensionFieldForm
.get('list')
.valueChanges.subscribe((value: number) => {
if (!!value) {
if (this.listSubmitOnChange) {
this.listChange.emit(value);
}
if (this.appendValueToInput) {
if (!this.inputControl) {
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 {
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
[mainButton]="perms.isAllowed('update', motion)"
mainButtonIcon="edit"
@ -101,7 +102,12 @@
<span translate>Show entire motion text</span>
</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>
<span translate>
History
@ -126,10 +132,16 @@
<div class="title-line">
<h1 class="motion-title">
<span *ngIf="titleCanBeChanged()">
<span class="title-change-indicator" *ngIf="getTitleChangingObject()"
(click)="gotoChangeRecommendation(getTitleChangingObject())"></span>
<span class="change-title" *osPerms="'motions.can_manage'; and: !getTitleChangingObject()"
(click)="createTitleChangeRecommendation()"></span>
<span
class="title-change-indicator"
*ngIf="getTitleChangingObject()"
(click)="gotoChangeRecommendation(getTitleChangingObject())"
></span>
<span
class="change-title"
*osPerms="'motions.can_manage'; and: !getTitleChangingObject()"
(click)="createTitleChangeRecommendation()"
></span>
</span>
{{ getTitleWithChanges() }}
@ -250,7 +262,8 @@
[inputValue]="newStateExtension"
[classes]="motion.stateCssColor"
extensionLabel="{{ 'Extension' | translate }}"
(success)="setStateExtension($event)">
(success)="setStateExtension($event)"
>
<span class="trigger-menu">
<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>
@ -281,7 +294,8 @@
[searchList]="motionObserver"
searchListLabel="{{ 'Motions' | translate }}"
listValuePrefix="motion:"
(success)="setRecommendationExtension($event)">
(success)="setRecommendationExtension($event)"
>
<span class="trigger-menu">
<button
*ngFor="let recommendation of motion.possibleRecommendations"
@ -292,10 +306,7 @@
<span *ngIf="recommendation.show_recommendation_extension_field">&nbsp;...</span>
</button>
<mat-divider></mat-divider>
<button
mat-menu-item
(click)="setRecommendation(null)"
>
<button mat-menu-item (click)="setRecommendation(null)">
<mat-icon>replay</mat-icon> {{ 'Reset recommendation' | translate }}
</button>
</span>
@ -781,7 +792,10 @@
</mat-list>
</div>
<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>

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.
* A motion of type T is created, prepared and deserialized from the given motionValues