97075: Feedback 2022-12-09 - splitting into multiple components

This commit is contained in:
Kristof De Langhe
2022-12-12 16:28:01 +01:00
parent 3c9623a2b0
commit 1ca217e748
14 changed files with 304 additions and 146 deletions

View File

@@ -0,0 +1,12 @@
<div class="flex-grow-1 ds-drop-list">
<ds-dso-edit-metadata-value *ngFor="let mdValue of form.fields[mdField]; let idx = index"
[mdValue]="mdValue"
[dsoType]="dsoType"
[saving$]="saving$"
[isOnlyValue]="form.fields[mdField].length === 1"
(edit)="mdValue.editing = true"
(confirm)="mdValue.confirmChanges($event); form.resetReinstatable(); valueSaved.emit()"
(remove)="mdValue.change = DsoEditMetadataChangeTypeEnum.REMOVE; form.resetReinstatable(); valueSaved.emit()"
(undo)="mdValue.change === DsoEditMetadataChangeTypeEnum.ADD ? form.remove(mdField, idx) : mdValue.discard(); valueSaved.emit()">
</ds-dso-edit-metadata-value>
</div>

View File

@@ -0,0 +1,3 @@
.ds-drop-list {
background-color: var(--bs-gray-500);
}

View File

@@ -0,0 +1,45 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { DsoEditMetadataChangeType, DsoEditMetadataForm } from '../dso-edit-metadata-form';
import { Observable } from 'rxjs/internal/Observable';
@Component({
selector: 'ds-dso-edit-metadata-field-values',
styleUrls: ['./dso-edit-metadata-field-values.component.scss'],
templateUrl: './dso-edit-metadata-field-values.component.html',
})
/**
* Component displaying table rows for each value for a certain metadata field within a form
*/
export class DsoEditMetadataFieldValuesComponent {
/**
* A dynamic form object containing all information about the metadata and the changes made to them, see {@link DsoEditMetadataForm}
*/
@Input() form: DsoEditMetadataForm;
/**
* Metadata field to display values for
*/
@Input() mdField: string;
/**
* Type of DSO we're displaying values for
* Determines i18n messages
*/
@Input() dsoType: string;
/**
* Observable to check if the form is being saved or not
*/
@Input() saving$: Observable<boolean>;
/**
* Emit when the value has been saved within the form
*/
@Output() valueSaved: EventEmitter<any> = new EventEmitter<any>();
/**
* The DsoEditMetadataChangeType enumeration for access in the component's template
* @type {DsoEditMetadataChangeType}
*/
public DsoEditMetadataChangeTypeEnum = DsoEditMetadataChangeType;
}

View File

@@ -0,0 +1,10 @@
<div class="d-flex flex-row ds-field-row ds-header-row">
<div class="lbl-cell">{{ dsoType + '.edit.metadata.headers.field' | translate }}</div>
<div class="flex-grow-1">
<div class="d-flex flex-row">
<div class="flex-grow-1 ds-flex-cell ds-value-cell"><b class="dont-break-out preserve-line-breaks">{{ dsoType + '.edit.metadata.headers.value' | translate }}</b></div>
<div class="ds-flex-cell ds-lang-cell"><b>{{ dsoType + '.edit.metadata.headers.language' | translate }}</b></div>
<div class="text-center ds-flex-cell ds-edit-cell"><b>{{ dsoType + '.edit.metadata.headers.edit' | translate }}</b></div>
</div>
</div>
</div>

View File

@@ -0,0 +1,12 @@
.lbl-cell {
min-width: var(--ds-dso-edit-field-width);
max-width: var(--ds-dso-edit-field-width);
background-color: var(--bs-gray-100);
font-weight: bold;
padding: 1rem;
border: 1px solid var(--bs-gray-200);
}
.ds-header-row {
background-color: var(--bs-gray-100);
}

View File

@@ -0,0 +1,17 @@
import { Component, Input } from '@angular/core';
@Component({
selector: 'ds-dso-edit-metadata-headers',
styleUrls: ['./dso-edit-metadata-headers.component.scss', '../dso-edit-metadata-shared/dso-edit-metadata-cells.scss'],
templateUrl: './dso-edit-metadata-headers.component.html',
})
/**
* Component displaying the header table row for DSO edit metadata page
*/
export class DsoEditMetadataHeadersComponent {
/**
* Type of DSO we're displaying values for
* Determines i18n messages
*/
@Input() dsoType: string;
}

View File

@@ -0,0 +1,49 @@
.ds-field-row {
border: 1px solid var(--bs-gray-400);
}
.ds-flex-cell {
padding: 1rem;
border: 1px solid var(--bs-gray-200);
}
.ds-lang-cell {
min-width: var(--ds-dso-edit-lang-width);
max-width: var(--ds-dso-edit-lang-width);
}
.ds-edit-cell {
min-width: var(--ds-dso-edit-actions-width);
}
.ds-value-row {
background-color: white;
&:active {
cursor: grabbing;
}
&.ds-warning {
background-color: var(--bs-warning-bg);
.ds-flex-cell {
border: 1px solid var(--bs-warning);
}
}
&.ds-danger {
background-color: var(--bs-danger-bg);
.ds-flex-cell {
border: 1px solid var(--bs-danger);
}
}
&.ds-success {
background-color: var(--bs-success-bg);
.ds-flex-cell {
border: 1px solid var(--bs-success);
}
}
}

View File

@@ -0,0 +1,37 @@
<div class="d-flex flex-row ds-value-row"
[ngClass]="{ 'ds-warning': mdValue.change === DsoEditMetadataChangeTypeEnum.UPDATE, 'ds-danger': mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE, 'ds-success': mdValue.change === DsoEditMetadataChangeTypeEnum.ADD, 'h-100': isOnlyValue }">
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center">
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.value }}</div>
<textarea class="form-control" rows="2" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.value"
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
</div>
<div class="ds-flex-cell ds-lang-cell">
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.language }}</div>
<input class="form-control" type="text" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.language" />
</div>
<div class="text-center ds-flex-cell ds-edit-cell">
<div class="btn-group edit-field">
<button class="btn btn-outline-primary btn-sm ng-star-inserted" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.edit' | translate }}" *ngIf="!mdValue.editing"
[disabled]="mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE || (saving$ | async)" (click)="edit.emit()">
<i class="fas fa-edit fa-fw"></i>
</button>
<button class="btn btn-outline-success btn-sm ng-star-inserted" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.confirm' | translate }}" *ngIf="mdValue.editing"
[disabled]="(saving$ | async)" (click)="confirm.emit(true)">
<i class="fas fa-check fa-fw"></i>
</button>
<button class="btn btn-outline-danger btn-sm" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.remove' | translate }}"
[disabled]="mdValue.change || mdValue.editing || (saving$ | async)" (click)="remove.emit()">
<i class="fas fa-trash-alt fa-fw"></i>
</button>
<button class="btn btn-outline-warning btn-sm" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.undo' | translate }}"
[disabled]="(!mdValue.change && !mdValue.editing) || (saving$ | async)" (click)="undo.emit()">
<i class="fas fa-undo-alt fa-fw"></i>
</button>
<!-- TODO: Enable drag -->
<button class="btn btn-outline-secondary ds-drag-handle btn-sm"
[ngClass]="{'disabled': isOnlyValue || (saving$ | async)}" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.drag' | translate }}" [disabled]="isOnlyValue || (saving$ | async)">
<i class="fas fa-grip-vertical fa-fw"></i>
</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,8 @@
.ds-success {
background-color: var(--bs-success-bg);
border: 1px solid var(--bs-success);
}
.ds-drag-handle:not(.disabled) {
cursor: grab;
}

View File

@@ -0,0 +1,62 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { DsoEditMetadataChangeType, DsoEditMetadataValue } from '../dso-edit-metadata-form';
import { Observable } from 'rxjs/internal/Observable';
@Component({
selector: 'ds-dso-edit-metadata-value',
styleUrls: ['./dso-edit-metadata-value.component.scss', '../dso-edit-metadata-shared/dso-edit-metadata-cells.scss'],
templateUrl: './dso-edit-metadata-value.component.html',
})
/**
* Component displaying a single editable row for a metadata value
*/
export class DsoEditMetadataValueComponent {
/**
* Editable metadata value to show
*/
@Input() mdValue: DsoEditMetadataValue;
/**
* Type of DSO we're displaying values for
* Determines i18n messages
*/
@Input() dsoType: string;
/**
* Observable to check if the form is being saved or not
* Will disable certain functionality while saving
*/
@Input() saving$: Observable<boolean>;
/**
* Is this value the only one within its list?
* Will disable certain functionality like dragging (because dragging within a list of 1 is pointless)
*/
@Input() isOnlyValue = false;
/**
* Emits when the user clicked edit
*/
@Output() edit: EventEmitter<any> = new EventEmitter<any>();
/**
* Emits when the user clicked confirm
*/
@Output() confirm: EventEmitter<boolean> = new EventEmitter<boolean>();
/**
* Emits when the user clicked remove
*/
@Output() remove: EventEmitter<any> = new EventEmitter<any>();
/**
* Emits when the user clicked undo
*/
@Output() undo: EventEmitter<any> = new EventEmitter<any>();
/**
* The DsoEditMetadataChangeType enumeration for access in the component's template
* @type {DsoEditMetadataChangeType}
*/
public DsoEditMetadataChangeTypeEnum = DsoEditMetadataChangeType;
}

View File

@@ -19,95 +19,36 @@
</button>
</div>
<div class="d-flex flex-row ds-field-row ds-header-row">
<div class="lbl-cell">{{ dsoType + '.edit.metadata.headers.field' | translate }}</div>
<div class="flex-grow-1">
<div class="d-flex flex-row">
<div class="flex-grow-1 ds-flex-cell ds-value-cell"><b class="dont-break-out preserve-line-breaks">{{ dsoType + '.edit.metadata.headers.value' | translate }}</b></div>
<div class="ds-flex-cell ds-lang-cell"><b>{{ dsoType + '.edit.metadata.headers.language' | translate }}</b></div>
<div class="text-center ds-flex-cell ds-edit-cell"><b>{{ dsoType + '.edit.metadata.headers.edit' | translate }}</b></div>
</div>
</div>
</div>
<ds-dso-edit-metadata-headers [dsoType]="dsoType"></ds-dso-edit-metadata-headers>
<div class="d-flex flex-row ds-field-row" *ngIf="form.newValue">
<div class="lbl-cell ds-success">
<ds-metadata-field-selector [dsoType]="dsoType" [(mdField)]="newMdField" [autofocus]="true"></ds-metadata-field-selector>
<ds-metadata-field-selector [dsoType]="dsoType"
[(mdField)]="newMdField"
[autofocus]="true">
</ds-metadata-field-selector>
</div>
<div class="flex-grow-1 ds-drop-list">
<div class="d-flex flex-row ds-value-row ds-success h-100">
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center">
<textarea class="form-control" rows="2" [(ngModel)]="form.newValue.newValue.value"></textarea>
</div>
<div class="ds-flex-cell ds-lang-cell">
<input class="form-control" type="text" [(ngModel)]="form.newValue.newValue.language" />
</div>
<div class="text-center ds-flex-cell ds-edit-cell">
<div class="btn-group edit-field">
<button class="btn btn-outline-success btn-sm ng-star-inserted" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.confirm' | translate }}"
[disabled]="!newMdField || (saving$ | async) || (loadingFieldValidation$ | async)" (click)="setMetadataField()">
<i class="fas fa-check fa-fw"></i>
</button>
<button class="btn btn-outline-danger btn-sm" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.remove' | translate }}"
[disabled]="true">
<i class="fas fa-trash-alt fa-fw"></i>
</button>
<button class="btn btn-outline-warning btn-sm" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.undo' | translate }}"
[disabled]="(saving$ | async) || (loadingFieldValidation$ | async)" (click)="form.newValue = undefined">
<i class="fas fa-undo-alt fa-fw"></i>
</button>
<button class="btn btn-outline-secondary ds-drag-handle btn-sm disabled" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.drag' | translate }}"
[disabled]="true">
<i class="fas fa-grip-vertical fa-fw"></i>
</button>
</div>
</div>
</div>
<ds-dso-edit-metadata-value [mdValue]="form.newValue"
[dsoType]="dsoType"
[saving$]="savingOrLoadingFieldValidation$"
[isOnlyValue]="true"
(confirm)="confirmNewValue($event)"
(remove)="form.newValue = undefined"
(undo)="form.newValue = undefined">
</ds-dso-edit-metadata-value>
</div>
</div>
<div class="d-flex flex-row ds-field-row" *ngFor="let mdField of form.fieldKeys">
<div class="lbl-cell">
<span class="dont-break-out preserve-line-breaks">{{ mdField }}</span>
</div>
<div class="flex-grow-1 ds-drop-list">
<div class="d-flex flex-row ds-value-row"
[ngClass]="{ 'ds-warning': mdValue.change === DsoEditMetadataChangeTypeEnum.UPDATE, 'ds-danger': mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE, 'ds-success': mdValue.change === DsoEditMetadataChangeTypeEnum.ADD, 'h-100': form.fields[mdField].length === 1 }"
*ngFor="let mdValue of form.fields[mdField]; let idx = index">
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center">
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.value }}</div>
<textarea class="form-control" rows="2" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.value"
[dsDebounce]="300" (onDebounce)="mdValue.confirmChanges(); form.resetReinstatable(); onValueSaved()"></textarea>
</div>
<div class="ds-flex-cell ds-lang-cell">
<div class="dont-break-out preserve-line-breaks" *ngIf="!mdValue.editing">{{ mdValue.newValue.language }}</div>
<input class="form-control" type="text" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.language" />
</div>
<div class="text-center ds-flex-cell ds-edit-cell">
<div class="btn-group edit-field">
<button class="btn btn-outline-primary btn-sm ng-star-inserted" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.edit' | translate }}" *ngIf="!mdValue.editing"
[disabled]="mdValue.change === DsoEditMetadataChangeTypeEnum.REMOVE || (saving$ | async)" (click)="mdValue.editing = true">
<i class="fas fa-edit fa-fw"></i>
</button>
<button class="btn btn-outline-success btn-sm ng-star-inserted" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.confirm' | translate }}" *ngIf="mdValue.editing"
[disabled]="(saving$ | async)" (click)="mdValue.confirmChanges(true); form.resetReinstatable(); onValueSaved()">
<i class="fas fa-check fa-fw"></i>
</button>
<button class="btn btn-outline-danger btn-sm" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.remove' | translate }}"
[disabled]="mdValue.change || mdValue.editing || (saving$ | async)" (click)="mdValue.change = DsoEditMetadataChangeTypeEnum.REMOVE; form.resetReinstatable(); onValueSaved()">
<i class="fas fa-trash-alt fa-fw"></i>
</button>
<button class="btn btn-outline-warning btn-sm" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.undo' | translate }}"
[disabled]="(!mdValue.change && !mdValue.editing) || (saving$ | async)" (click)="mdValue.change === DsoEditMetadataChangeTypeEnum.ADD ? form.remove(mdField, idx) : mdValue.discard(); onValueSaved()">
<i class="fas fa-undo-alt fa-fw"></i>
</button>
<!-- TODO: Enable drag -->
<button class="btn btn-outline-secondary ds-drag-handle btn-sm"
[ngClass]="{'disabled': form.fields[mdField].length === 1 || (saving$ | async)}" ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.drag' | translate }}" [disabled]="form.fields[mdField].length === 1 || (saving$ | async)">
<i class="fas fa-grip-vertical fa-fw"></i>
</button>
</div>
</div>
</div>
</div>
<ds-dso-edit-metadata-field-values class="flex-grow-1"
[form]="form"
[dsoType]="dsoType"
[saving$]="saving$"
[mdField]="mdField"
(valueSaved)="onValueSaved()">
</ds-dso-edit-metadata-field-values>
</div>
<div *ngIf="isEmpty">

View File

@@ -3,6 +3,8 @@
max-width: var(--ds-dso-edit-field-width);
background-color: var(--bs-gray-100);
font-weight: bold;
padding: 1rem;
border: 1px solid var(--bs-gray-200);
&.ds-success {
background-color: var(--bs-success-bg);
@@ -10,66 +12,6 @@
}
}
.lbl-cell,
.ds-flex-cell {
padding: 1rem;
border: 1px solid var(--bs-gray-200);
}
.ds-lang-cell {
min-width: var(--ds-dso-edit-lang-width);
max-width: var(--ds-dso-edit-lang-width);
}
.ds-edit-cell {
min-width: var(--ds-dso-edit-actions-width);
}
.ds-value-row {
background-color: white;
&:active {
cursor: grabbing;
}
&.ds-warning {
background-color: var(--bs-warning-bg);
.ds-flex-cell {
border: 1px solid var(--bs-warning);
}
}
&.ds-danger {
background-color: var(--bs-danger-bg);
.ds-flex-cell {
border: 1px solid var(--bs-danger);
}
}
&.ds-success {
background-color: var(--bs-success-bg);
.ds-flex-cell {
border: 1px solid var(--bs-success);
}
}
}
.ds-drop-list {
background-color: var(--bs-gray-500);
}
.ds-field-row {
border: 1px solid var(--bs-gray-400);
}
.ds-header-row {
background-color: var(--bs-gray-100);
}
.ds-drag-handle:not(.disabled) {
cursor: grab;
}

View File

@@ -1,7 +1,7 @@
import { Component, Injector, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { AlertType } from '../../shared/alert/aletr-type';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { DsoEditMetadataChangeType, DsoEditMetadataForm } from './dso-edit-metadata-form';
import { DsoEditMetadataForm } from './dso-edit-metadata-form';
import { map } from 'rxjs/operators';
import { ActivatedRoute, Data } from '@angular/router';
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
@@ -18,6 +18,7 @@ import { ResourceType } from '../../core/shared/resource-type';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { MetadataFieldSelectorComponent } from './metadata-field-selector/metadata-field-selector.component';
import { Observable } from 'rxjs/internal/Observable';
@Component({
selector: 'ds-dso-edit-metadata',
@@ -76,18 +77,18 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
*/
loadingFieldValidation$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
/**
* Combination of saving$ and loadingFieldValidation$
* Emits true when any of the two emit true
*/
savingOrLoadingFieldValidation$: Observable<boolean>;
/**
* The AlertType enumeration for access in the component's template
* @type {AlertType}
*/
public AlertTypeEnum = AlertType;
/**
* The DsoEditMetadataChangeType enumeration for access in the component's template
* @type {DsoEditMetadataChangeType}
*/
public DsoEditMetadataChangeTypeEnum = DsoEditMetadataChangeType;
/**
* Subscription for updating the current DSpaceObject
* Unsubscribed from in ngOnDestroy()
@@ -118,6 +119,9 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
this.initDataService();
this.initForm();
}
this.savingOrLoadingFieldValidation$ = observableCombineLatest([this.saving$, this.loadingFieldValidation$]).pipe(
map(([saving, loading]) => saving || loading),
);
}
/**
@@ -180,6 +184,16 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
});
}
/**
* Confirm the newly added value
* @param saved Whether or not the value was manually saved (only then, add the value to its metadata field)
*/
confirmNewValue(saved: boolean) {
if (saved) {
this.setMetadataField();
}
}
/**
* Set the metadata field of the temporary added new metadata value
* This will move the new value to its respective parent metadata field

View File

@@ -2,6 +2,9 @@ import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { DsoEditMetadataComponent } from './dso-edit-metadata/dso-edit-metadata.component';
import { MetadataFieldSelectorComponent } from './dso-edit-metadata/metadata-field-selector/metadata-field-selector.component';
import { DsoEditMetadataFieldValuesComponent } from './dso-edit-metadata/dso-edit-metadata-field-values/dso-edit-metadata-field-values.component';
import { DsoEditMetadataValueComponent } from './dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component';
import { DsoEditMetadataHeadersComponent } from './dso-edit-metadata/dso-edit-metadata-headers/dso-edit-metadata-headers.component';
@NgModule({
imports: [
@@ -10,6 +13,9 @@ import { MetadataFieldSelectorComponent } from './dso-edit-metadata/metadata-fie
declarations: [
DsoEditMetadataComponent,
MetadataFieldSelectorComponent,
DsoEditMetadataFieldValuesComponent,
DsoEditMetadataValueComponent,
DsoEditMetadataHeadersComponent,
],
exports: [
DsoEditMetadataComponent,