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:
parent
3f6fe28f35
commit
719f1f8031
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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"> ...</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"> ...</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>
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user