Adds preview of dataset template, adds delete with spacebar on multiple autocomplete

This commit is contained in:
apapachristou 2020-10-20 13:00:02 +03:00
parent b8f3c5af70
commit 32caed9f6f
12 changed files with 211 additions and 18 deletions

View File

@ -31,7 +31,9 @@ export interface MultipleAutoCompleteConfiguration {
optionTemplate?: TemplateRef<any>;
// Selected value formating template
selectedValueTemplate?: TemplateRef<any>;
// Display icon that opens popup
popupItemActionIcon?: string;
autoSelectFirstOptionOnBlur?: boolean;
}

View File

@ -10,9 +10,9 @@
</mat-chip>
</ng-container>
</mat-chip-list>
<input matInput #autocompleteInput class="col" [name]="id" autocomplete="off" #autocompleteTrigger="matAutocompleteTrigger" [placeholder]="placeholder" [matAutocomplete]="autocomplete" [value]="inputValue" (keyup)="onKeyUp($event)" [disabled]="disabled" (focus)="_onInputFocus()" (blur)="onBlur($event)" [matChipInputFor]="chipList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="autoSelectFirstOptionOnBlur" (matChipInputTokenEnd)="_addItem($event)">
<input matInput #autocompleteInput class="col" [name]="id" autocomplete="off" #autocompleteTrigger="matAutocompleteTrigger" [placeholder]="placeholder" [matAutocomplete]="autocomplete" [value]="inputValue" (keyup)="onKeyUp($event)" (keydown)="onKeyDown($event)" [disabled]="disabled" (focus)="_onInputFocus()" (blur)="onBlur($event)" [matChipInputFor]="chipList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="autoSelectFirstOptionOnBlur" (matChipInputTokenEnd)="_addItem($event)">
<!-- The attribute autocomplete="nope", set by downshift, is ignored in Chrome 67 and Opera 54 (latest at the time of writing)
<input matInput #autocompleteInput class="col" [name]="id" autocomplete="nope" #autocompleteTrigger="matAutocompleteTrigger" [placeholder]="placeholder" [matAutocomplete]="autocomplete" [value]="inputValue" (keyup)="onKeyUp($event)" [disabled]="disabled" (focus)="_onInputFocus()" (blur)="onBlur($event)" [matChipInputFor]="chipList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="autoSelectFirstOptionOnBlur" (matChipInputTokenEnd)="_addItem($event)"> -->
<input matInput #autocompleteInput class="col" [name]="id" autocomplete="nope" #autocompleteTrigger="matAutocompleteTrigger" [placeholder]="placeholder" [matAutocomplete]="autocomplete" [value]="inputValue" (keyup)="onKeyUp($event)" (keydown)="onKeyDown($event)" [disabled]="disabled" (focus)="_onInputFocus()" (blur)="onBlur($event)" [matChipInputFor]="chipList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="autoSelectFirstOptionOnBlur" (matChipInputTokenEnd)="_addItem($event)"> -->
<mat-icon *ngIf="!disabled" class="align-arrow-right" matSuffix>arrow_drop_down</mat-icon>
<mat-autocomplete #autocomplete="matAutocomplete" [displayWith]="_displayFn.bind(this)" (optionSelected)="_optionSelected($event)">
<span *ngIf="_groupedItems">
@ -22,10 +22,13 @@
<ng-template #cellTemplate *ngIf="_optionTemplate(item)" [ngTemplateOutlet]="_optionTemplate(item)" [ngTemplateOutletContext]="{
item: item
}"></ng-template>
<div *ngIf="!_optionTemplate(item)">
<span>{{_titleFn(item)}}</span>
<br *ngIf="_subtitleFn(item)">
<small *ngIf="_subtitleFn(item)">{{_subtitleFn(item)}}</small>
<div *ngIf="!_optionTemplate(item)" class="d-flex">
<div class="title-fn">
<span>{{_titleFn(item)}}</span>
<br *ngIf="_subtitleFn(item)">
<small *ngIf="_subtitleFn(item)">{{_subtitleFn(item)}}</small>
</div>
<span *ngIf="popupItemActionIcon" class="option-icon" (click)="_optionActionClick(item, $event)"><mat-icon>{{popupItemActionIcon}}</mat-icon></span>
</div>
</mat-option>
</mat-optgroup>
@ -38,10 +41,13 @@
<ng-template #cellTemplate *ngIf="_optionTemplate(item)" [ngTemplateOutlet]="_optionTemplate(item)" [ngTemplateOutletContext]="{
item: item
}"></ng-template>
<div *ngIf="!_optionTemplate(item)">
<span *ngIf="!_optionTemplate(item)">{{_titleFn(item)}}</span>
<br *ngIf="_subtitleFn(item)">
<small *ngIf="_subtitleFn(item)">{{_subtitleFn(item)}}</small>
<div *ngIf="!_optionTemplate(item)" class="d-flex">
<div class="title-fn">
<span *ngIf="!_optionTemplate(item)">{{_titleFn(item)}}</span>
<br *ngIf="_subtitleFn(item)">
<small *ngIf="_subtitleFn(item)">{{_subtitleFn(item)}}</small>
</div>
<span *ngIf="popupItemActionIcon" class="option-icon" (click)="_optionActionClick(item, $event)"><mat-icon>{{popupItemActionIcon}}</mat-icon></span>
</div>
</mat-option>
</ng-container>

View File

@ -19,6 +19,23 @@
}
.title-fn {
flex-grow: 1;
white-space: nowrap;
width: calc(100% - 16px);
overflow: hidden;
text-overflow: ellipsis;
}
.option-icon {
mat-icon {
margin: 0px 5px 0px 10px;
}
mat-icon:hover {
color: #129d99;
}
}
.two-line-mat-option {
line-height: 1.2em;
}

View File

@ -1,5 +1,5 @@
import { FocusMonitor } from '@angular/cdk/a11y';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { BACKSPACE, COMMA, ENTER } from '@angular/cdk/keycodes';
import { Component, DoCheck, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Optional, Output, Self, SimpleChanges, TemplateRef, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormGroupDirective, NgControl, NgForm } from '@angular/forms';
import { ErrorStateMatcher, MatChipInputEvent, mixinErrorState } from '@angular/material';
@ -45,6 +45,9 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp
@Output() optionSelected: EventEmitter<any> = new EventEmitter();
@Output() optionRemoved: EventEmitter<any> = new EventEmitter();
@Output() optionActionClicked: EventEmitter<any> = new EventEmitter();
id = `multiple-autocomplete-${MultipleAutoCompleteComponent.nextId++}`;
stateChanges = new Subject<void>();
focused = false;
@ -200,6 +203,12 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp
}
}
public onKeyDown(event) {
if (event.keyCode === BACKSPACE && event.target.value.length === 0 && this._selectedItems.size > 0) {
this._removeSelectedItem(Array.from(this._selectedItems.values()).pop(), event);
}
}
private _setValue(value: any) {
this.value = value;
this.pushChanges(this.value);
@ -301,6 +310,10 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp
return this.configuration.autoSelectFirstOptionOnBlur != null ? this.configuration.autoSelectFirstOptionOnBlur : false;
}
get popupItemActionIcon(): string {
return this.configuration.popupItemActionIcon != null ? this.configuration.popupItemActionIcon : '';
}
//Chip Functions
_addItem(event: MatChipInputEvent): void {
@ -329,4 +342,9 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp
this.autocompleteInput.nativeElement.focus();
this.pushChanges(this.value);
}
_optionActionClick(item: any, event: MouseEvent): void {
if (event != null) { event.stopPropagation(); }
this.optionActionClicked.emit(item);
}
}

View File

@ -0,0 +1,17 @@
<div class="template-container">
<div mat-dialog-title class="row d-flex m-0 header" (click)="closeDialog()">
<span class="template-title align-self-center">{{'DMP-LISTING.COLUMNS.PROFILE' | translate}} - {{data.template.label}}</span>
<span class="ml-auto align-self-center"><mat-icon class="close-icon">close</mat-icon></span>
</div>
<div *ngIf="progressIndication" class="progress-bar">
<mat-progress-bar color="primary" mode="indeterminate"></mat-progress-bar>
</div>
<div mat-dialog-content class="definition-content">
<app-dataset-description *ngIf="datasetProfileDefinitionFormGroup && datasetProfileDefinitionModel" [form]="datasetProfileDefinitionFormGroup" [visibilityRules]="datasetProfileDefinitionModel.rules" [datasetProfileId]="data.template.id"></app-dataset-description>
</div>
<div mat-mat-dialog-actions *ngIf="datasetProfileDefinitionFormGroup">
<div class="col-auto d-flex pb-4 pt-2">
<button mat-raised-button type="button" class="start-btn ml-auto" (click)="select()">{{'TYPES.EXTERNAL-DATASET-TYPE.SELECT' | translate}}</button>
</div>
</div>
</div>

View File

@ -0,0 +1,42 @@
.template-container {
.header {
display: flex;
width: 100%;
height: 89px;
background-color: #129D99;
color: #FFFFFF;
font-size: 1.25rem;
}
.template-title {
margin-left: 37px;
}
.close-icon {
cursor: pointer;
margin-right: 20px;
}
.close-icon:hover {
background-color: #fefefe6e !important;
border-radius: 50%;
}
.definition-content {
display: block;
margin: 0px;
padding: 0 24px;
}
.start-btn {
background: #129d99 0% 0% no-repeat padding-box;
border: 1px solid #129d99;
border-radius: 30px;
opacity: 1;
width: 101px;
height: 43px;
color: #ffffff;
font-weight: 500;
}
}

View File

@ -0,0 +1,54 @@
import { Component, Inject, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service';
import { ProgressIndicationService } from '@app/core/services/progress-indication/progress-indication-service';
import { DatasetDescriptionFormEditorModel } from '@app/ui/misc/dataset-description-form/dataset-description-form.model';
import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-dataset-preview-dialog-component',
templateUrl: 'dataset-preview-dialog.component.html',
styleUrls: ['./dataset-preview-dialog.component.scss'],
})
export class DatasetPreviewDialogComponent extends BaseComponent implements OnInit {
datasetProfileDefinitionModel: DatasetDescriptionFormEditorModel;
datasetProfileDefinitionFormGroup: FormGroup;
progressIndication = false;
constructor(
public dialogRef: MatDialogRef<DatasetPreviewDialogComponent>,
private datasetWizardService: DatasetWizardService,
private progressIndicationService: ProgressIndicationService,
@Inject(MAT_DIALOG_DATA) public data: any
) {
super();
}
ngOnInit(): void {
this.progressIndicationService.getProgressIndicationObservable().pipe(takeUntil(this._destroyed)).subscribe(x => {
setTimeout(() => { this.progressIndication = x; });
});
if (this.data && this.data.template) {
this.datasetWizardService.getDefinition(this.data.template.id)
.pipe(takeUntil(this._destroyed))
.subscribe(item => {
this.datasetProfileDefinitionModel = new DatasetDescriptionFormEditorModel().fromModel(item);
this.datasetProfileDefinitionFormGroup = this.datasetProfileDefinitionModel.buildForm();
this.datasetProfileDefinitionFormGroup.disable();
});
}
}
select(): void {
this.dialogRef.close(true);
}
closeDialog(): void {
this.dialogRef.close();
}
}

View File

@ -48,6 +48,7 @@ import { NgxDropzoneModule } from 'ngx-dropzone';
import { DmpToDatasetDialogComponent } from './dmp-to-dataset/dmp-to-dataset-dialog.component';
import { FormProgressIndicationComponent } from '../misc/dataset-description-form/components/form-progress-indication/form-progress-indication.component';
import { FormProgressIndicationModule } from '../misc/dataset-description-form/components/form-progress-indication/form-progress-indication.module';
import { DatasetPreviewDialogComponent } from './dataset-preview/dataset-preview-dialog.component';
@NgModule({
imports: [
@ -99,7 +100,8 @@ import { FormProgressIndicationModule } from '../misc/dataset-description-form/c
MainInfoComponent,
FundingInfoComponent,
DatasetInfoComponent,
LicenseInfoComponent
LicenseInfoComponent,
DatasetPreviewDialogComponent
],
entryComponents: [
DmpInvitationDialogComponent,
@ -114,7 +116,8 @@ import { FormProgressIndicationModule } from '../misc/dataset-description-form/c
AddCostComponent,
StartNewDmpDialogComponent,
StartNewDatasetDialogComponent,
DatasetEditorDetailsComponent
DatasetEditorDetailsComponent,
DatasetPreviewDialogComponent
]
})
export class DmpModule { }

View File

@ -13,7 +13,7 @@
</div>
<div class="profile-form">
<mat-form-field>
<app-multiple-auto-complete required='true' [formControl]="formGroup.get('profiles')" placeholder="{{'DMP-EDITOR.FIELDS.SELECT-TEMPLATE' | translate}}" [configuration]="profilesAutoCompleteConfiguration" (optionRemoved)="onRemoveTemplate($event)">
<app-multiple-auto-complete required='true' [formControl]="formGroup.get('profiles')" placeholder="{{'DMP-EDITOR.FIELDS.SELECT-TEMPLATE' | translate}}" [configuration]="profilesAutoCompleteConfiguration" (optionRemoved)="onRemoveTemplate($event)" (optionActionClicked)="onPreviewTemplate($event)">
</app-multiple-auto-complete>
<mat-error *ngIf="formGroup.get('profiles').hasError('backendError')">
{{formGroup.get('profiles').getError('backendError').message}}</mat-error>

View File

@ -24,6 +24,7 @@ import { DatasetDescriptionFormEditorModel } from '@app/ui/misc/dataset-descript
import { DatasetWizardEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model';
import { DmpListingModel } from '@app/core/model/dmp/dmp-listing';
import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service';
import { DatasetPreviewDialogComponent } from '../../dataset-preview/dataset-preview-dialog.component';
@Component({
selector: 'dataset-info',
@ -49,6 +50,7 @@ export class DatasetInfoComponent extends BaseComponent implements OnInit {
profilesAutoCompleteConfiguration: MultipleAutoCompleteConfiguration;
datasetProfileDefinitionModel: DatasetDescriptionFormEditorModel;
datasetProfileDefinitionFormGroup: FormGroup;
constructor(
private language: TranslateService,
@ -71,7 +73,8 @@ export class DatasetInfoComponent extends BaseComponent implements OnInit {
initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['label'],
titleFn: (item) => item['label'],
subtitleFn: (item) => item['description']
subtitleFn: (item) => item['description'],
popupItemActionIcon: 'visibility'
};
if (this.formGroup.get('definition')) { this.selectedDmpProfileDefinition = this.formGroup.get('definition').value; }
@ -84,7 +87,8 @@ export class DatasetInfoComponent extends BaseComponent implements OnInit {
initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['label'],
titleFn: (item) => item['label'],
subtitleFn: (item) => item['description']
subtitleFn: (item) => item['description'],
popupItemActionIcon: 'visibility'
};
}
}
@ -205,4 +209,30 @@ export class DatasetInfoComponent extends BaseComponent implements OnInit {
}
}
onPreviewTemplate(event) {
const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, {
width: '590px',
minHeight: '200px',
restoreFocus: false,
data: {
template: event
},
panelClass: 'custom-modalbox'
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
let profiles = this.formGroup.get('profiles').value;
profiles.push(event);
this.formGroup.get('profiles').setValue(profiles);
this.profilesAutoCompleteConfiguration = {
filterFn: this.filterProfiles.bind(this),
initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['label'],
titleFn: (item) => item['label'],
subtitleFn: (item) => item['description']
};
}
});
}
}

View File

@ -2,7 +2,7 @@
.expansion-panel {
// background-color: #eeeeee54;
background-color: white;
margin-bottom: 1em;
// margin-bottom: 1em;
}
.addOneFieldButton {
margin-top: -15px;

View File

@ -667,6 +667,10 @@ hr {
cursor: pointer;
}
.custom-modalbox > mat-dialog-container {
padding: 0px;
}
@media (min-width: 576px) {
.container {
max-width: 540px;