mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 10:34:15 +00:00
97075: Edit metadata redesign
This commit is contained in:
133
src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts
Normal file
133
src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-form.ts
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import { MetadataMap, MetadataValue } from '../../core/shared/metadata.models';
|
||||||
|
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
|
|
||||||
|
export enum DsoEditMetadataChangeType {
|
||||||
|
UPDATE = 1,
|
||||||
|
ADD = 2,
|
||||||
|
REMOVE = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DsoEditMetadataValue {
|
||||||
|
originalValue: MetadataValue;
|
||||||
|
newValue: MetadataValue;
|
||||||
|
reinstatableValue: MetadataValue;
|
||||||
|
|
||||||
|
editing = false;
|
||||||
|
change: DsoEditMetadataChangeType;
|
||||||
|
reinstatableChange: DsoEditMetadataChangeType;
|
||||||
|
|
||||||
|
constructor(value: MetadataValue, added = false) {
|
||||||
|
this.originalValue = value;
|
||||||
|
this.newValue = Object.assign(new MetadataValue(), value);
|
||||||
|
if (added) {
|
||||||
|
this.change = DsoEditMetadataChangeType.ADD;
|
||||||
|
this.editing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmChanges() {
|
||||||
|
if (hasNoValue(this.change) || this.change === DsoEditMetadataChangeType.UPDATE) {
|
||||||
|
if ((this.originalValue.value !== this.newValue.value || this.originalValue.language !== this.newValue.language)) {
|
||||||
|
this.change = DsoEditMetadataChangeType.UPDATE;
|
||||||
|
} else {
|
||||||
|
this.change = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.editing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChanges(): boolean {
|
||||||
|
return hasValue(this.change);
|
||||||
|
}
|
||||||
|
|
||||||
|
discardAndMarkReinstatable(): void {
|
||||||
|
if (this.change === DsoEditMetadataChangeType.UPDATE) {
|
||||||
|
this.reinstatableValue = this.newValue;
|
||||||
|
}
|
||||||
|
this.reinstatableChange = this.change;
|
||||||
|
this.discard();
|
||||||
|
}
|
||||||
|
|
||||||
|
discard(): void {
|
||||||
|
this.change = undefined;
|
||||||
|
this.newValue = Object.assign(new MetadataValue(), this.originalValue);
|
||||||
|
this.editing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reinstate(): void {
|
||||||
|
this.newValue = this.reinstatableValue;
|
||||||
|
this.reinstatableValue = undefined;
|
||||||
|
this.change = this.reinstatableChange;
|
||||||
|
this.reinstatableChange = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
isReinstatable(): boolean {
|
||||||
|
return hasValue(this.reinstatableValue) || hasValue(this.reinstatableChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DsoEditMetadataForm {
|
||||||
|
fieldKeys: string[];
|
||||||
|
fields: {
|
||||||
|
[mdField: string]: DsoEditMetadataValue[],
|
||||||
|
};
|
||||||
|
newValue: DsoEditMetadataValue;
|
||||||
|
|
||||||
|
constructor(metadata: MetadataMap) {
|
||||||
|
this.fieldKeys = [];
|
||||||
|
this.fields = {};
|
||||||
|
Object.entries(metadata).forEach(([mdField, values]: [string, MetadataValue[]]) => {
|
||||||
|
this.fieldKeys.push(mdField);
|
||||||
|
this.fields[mdField] = values.map((value) => new DsoEditMetadataValue(value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
add(): void {
|
||||||
|
if (hasNoValue(this.newValue)) {
|
||||||
|
this.newValue = new DsoEditMetadataValue(new MetadataValue(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMetadataField(mdField: string) {
|
||||||
|
if (isEmpty(this.fields[mdField])) {
|
||||||
|
this.fieldKeys.push(mdField);
|
||||||
|
this.fields[mdField] = [];
|
||||||
|
}
|
||||||
|
this.fields[mdField].push(this.newValue);
|
||||||
|
this.newValue = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(mdField: string, index: number) {
|
||||||
|
if (isNotEmpty(this.fields[mdField])) {
|
||||||
|
this.fields[mdField].splice(index, 1);
|
||||||
|
if (this.fields[mdField].length === 0) {
|
||||||
|
this.fieldKeys.splice(this.fieldKeys.indexOf(mdField), 1);
|
||||||
|
delete this.fields[mdField];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChanges(): boolean {
|
||||||
|
return Object.values(this.fields).some((values) => values.some((value) => value.hasChanges()));
|
||||||
|
}
|
||||||
|
|
||||||
|
discard(): void {
|
||||||
|
Object.values(this.fields).forEach((values) => {
|
||||||
|
values.forEach((value) => {
|
||||||
|
value.discard();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reinstate(): void {
|
||||||
|
Object.values(this.fields).forEach((values) => {
|
||||||
|
values.forEach((value) => {
|
||||||
|
value.reinstate();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isReinstatable(): boolean {
|
||||||
|
return Object.values(this.fields).some((values) => values.some((value) => value.isReinstatable()));
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,126 @@
|
|||||||
|
<div class="item-metadata" *ngIf="form">
|
||||||
|
<div class="button-row top d-flex my-2 space-children-mr">
|
||||||
|
<button class="mr-auto btn btn-success" [disabled]="form.newValue"
|
||||||
|
(click)="add()"><i class="fas fa-plus"></i>
|
||||||
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.add-button' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-warning" *ngIf="isReinstatable"
|
||||||
|
(click)="reinstate()"><i class="fas fa-undo-alt"></i>
|
||||||
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary" [disabled]="!hasChanges"
|
||||||
|
(click)="submit()"><i class="fas fa-save"></i>
|
||||||
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.save-button' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-danger" *ngIf="!isReinstatable"
|
||||||
|
[disabled]="!hasChanges"
|
||||||
|
(click)="discard()"><i class="fas fa-times"></i>
|
||||||
|
<span class="d-none d-sm-inline"> {{ dsoType + '.edit.metadata.discard-button' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-row ds-field-row ds-header-row">
|
||||||
|
<div class="lbl-cell">Field</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">Value</b></div>
|
||||||
|
<div class="ds-flex-cell ds-lang-cell"><b>Lang</b></div>
|
||||||
|
<div class="text-center ds-flex-cell ds-edit-cell"><b>Edit</b></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-row ds-field-row" *ngIf="form.newValue">
|
||||||
|
<div class="lbl-cell ds-success">
|
||||||
|
<ds-metadata-field-selector [(mdField)]="newMdField"></ds-metadata-field-selector>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1 ds-drop-list">
|
||||||
|
<div class="d-flex flex-row ds-value-row ds-success">
|
||||||
|
<div class="flex-grow-1 ds-flex-cell ds-value-cell d-flex align-items-center">
|
||||||
|
<input class="form-control" type="text" [(ngModel)]="form.newValue.newValue.value" />
|
||||||
|
</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="Confirm changes"
|
||||||
|
[disabled]="!newMdField" (click)="form.setMetadataField(newMdField)">
|
||||||
|
<i class="fas fa-check fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-danger btn-sm" ngbTooltip="Remove" [disabled]="true">
|
||||||
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-warning btn-sm" ngbTooltip="Undo changes" (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" ngbTooltip="Drag to reorder" [disabled]="true">
|
||||||
|
<i class="fas fa-grip-vertical fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-row ds-field-row" *ngFor="let mdField of form.fieldKeys">
|
||||||
|
<div class="lbl-cell">
|
||||||
|
<span>{{ 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 }"
|
||||||
|
*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>
|
||||||
|
<input class="form-control" type="text" *ngIf="mdValue.editing" [(ngModel)]="mdValue.newValue.value" />
|
||||||
|
</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="Edit" *ngIf="!mdValue.editing"
|
||||||
|
[disabled]="mdValue.change && mdValue.change !== DsoEditMetadataChangeTypeEnum.UPDATE" (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="Confirm changes" *ngIf="mdValue.editing"
|
||||||
|
(click)="mdValue.confirmChanges()">
|
||||||
|
<i class="fas fa-check fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-danger btn-sm" ngbTooltip="Remove"
|
||||||
|
[disabled]="mdValue.change || mdValue.editing" (click)="mdValue.change = DsoEditMetadataChangeTypeEnum.REMOVE">
|
||||||
|
<i class="fas fa-trash-alt fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-warning btn-sm" ngbTooltip="Undo changes"
|
||||||
|
[disabled]="!mdValue.change && !mdValue.editing" (click)="mdValue.change === DsoEditMetadataChangeTypeEnum.ADD ? form.remove(mdField, idx) : mdValue.discard()">
|
||||||
|
<i class="fas fa-undo-alt fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<!-- TODO: Enable drag -->
|
||||||
|
<button class="btn btn-outline-secondary ds-drag-handle btn-sm" ngbTooltip="Drag to reorder" [disabled]="true">
|
||||||
|
<i class="fas fa-grip-vertical fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="isEmpty">
|
||||||
|
<ds-alert [content]="dsoType + '.edit.metadata.empty'" [type]="AlertTypeEnum.Info"></ds-alert>
|
||||||
|
</div>
|
||||||
|
<div class="button-row bottom">
|
||||||
|
<div class="mt-2 float-right space-children-mr ml-gap">
|
||||||
|
<button class="btn btn-warning" *ngIf="isReinstatable"
|
||||||
|
(click)="reinstate()"><i class="fas fa-undo-alt"></i> {{ dsoType + '.edit.metadata.reinstate-button' | translate }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary" [disabled]="!hasChanges"
|
||||||
|
(click)="submit()"><i class="fas fa-save"></i> {{ dsoType + '.edit.metadata.save-button' | translate }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-danger" *ngIf="!isReinstatable"
|
||||||
|
[disabled]="!hasChanges"
|
||||||
|
(click)="discard()"><i class="fas fa-times"></i> {{ dsoType + '.edit.metadata.discard-button' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ds-loading *ngIf="!form"></ds-loading>
|
@@ -0,0 +1,73 @@
|
|||||||
|
.lbl-cell {
|
||||||
|
min-width: 210px;
|
||||||
|
background-color: var(--bs-gray-100);
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&.ds-success {
|
||||||
|
background-color: var(--bs-success-bg);
|
||||||
|
border: 1px solid var(--bs-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lbl-cell,
|
||||||
|
.ds-flex-cell {
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid var(--bs-gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ds-lang-cell {
|
||||||
|
min-width: 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ds-edit-cell {
|
||||||
|
min-width: 173px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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 {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
@@ -0,0 +1,99 @@
|
|||||||
|
import { Component, Input, OnDestroy, OnInit } 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 { map, switchMap } from 'rxjs/operators';
|
||||||
|
import { ActivatedRoute, Data } from '@angular/router';
|
||||||
|
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
|
import { hasNoValue, hasValue } from '../../shared/empty.util';
|
||||||
|
import { RegistryService } from '../../core/registry/registry.service';
|
||||||
|
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
|
import { getFirstSucceededRemoteData, metadataFieldsToString } from '../../core/shared/operators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-dso-edit-metadata',
|
||||||
|
styleUrls: ['./dso-edit-metadata.component.scss'],
|
||||||
|
templateUrl: './dso-edit-metadata.component.html',
|
||||||
|
})
|
||||||
|
export class DsoEditMetadataComponent implements OnInit, OnDestroy {
|
||||||
|
@Input() dso: DSpaceObject;
|
||||||
|
dsoType: string;
|
||||||
|
|
||||||
|
form: DsoEditMetadataForm;
|
||||||
|
newMdField: string;
|
||||||
|
|
||||||
|
isReinstatable: boolean;
|
||||||
|
hasChanges: boolean;
|
||||||
|
isEmpty: 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;
|
||||||
|
|
||||||
|
dsoUpdateSubscription: Subscription;
|
||||||
|
|
||||||
|
constructor(protected route: ActivatedRoute) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (hasNoValue(this.dso)) {
|
||||||
|
this.dsoUpdateSubscription = observableCombineLatest([this.route.data, this.route.parent.data]).pipe(
|
||||||
|
map(([data, parentData]: [Data, Data]) => Object.assign({}, data, parentData)),
|
||||||
|
map((data: any) => data.dso)
|
||||||
|
).subscribe((rd: RemoteData<DSpaceObject>) => {
|
||||||
|
this.dso = rd.payload;
|
||||||
|
this.initForm();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.initForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initForm(): void {
|
||||||
|
this.dsoType = typeof this.dso.type === 'string' ? this.dso.type as any : this.dso.type.value;
|
||||||
|
this.form = new DsoEditMetadataForm(this.dso.metadata);
|
||||||
|
this.onDebounce();
|
||||||
|
}
|
||||||
|
|
||||||
|
onDebounce(): void {
|
||||||
|
this.hasChanges = this.form.hasChanges();
|
||||||
|
this.isReinstatable = this.form.isReinstatable();
|
||||||
|
this.isEmpty = Object.keys(this.form.fields).length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
submit(): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
add(): void {
|
||||||
|
this.newMdField = undefined;
|
||||||
|
this.form.add();
|
||||||
|
}
|
||||||
|
|
||||||
|
discard(): void {
|
||||||
|
this.form.discard();
|
||||||
|
}
|
||||||
|
|
||||||
|
reinstate(): void {
|
||||||
|
this.form.reinstate();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (hasValue(this.dsoUpdateSubscription)) {
|
||||||
|
this.dsoUpdateSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
<div class="w-100 position-relative">
|
||||||
|
<input type="text"
|
||||||
|
class="form-control"
|
||||||
|
[value]="mdField"
|
||||||
|
[formControl]="input"
|
||||||
|
(focusin)="query$.next(mdField)"
|
||||||
|
(dsClickOutside)="query$.next(null)"
|
||||||
|
(click)="$event.stopPropagation();" />
|
||||||
|
<div class="autocomplete dropdown-menu" [ngClass]="{'show': (mdFieldOptions$ | async)?.length > 0}">
|
||||||
|
<div class="dropdown-list">
|
||||||
|
<div *ngFor="let mdFieldOption of (mdFieldOptions$ | async)">
|
||||||
|
<a href="javascript:void(0);" class="d-block dropdown-item" (click)="select(mdFieldOption)">
|
||||||
|
<span [innerHTML]="mdFieldOption"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -0,0 +1,68 @@
|
|||||||
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
|
import { startWith, switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||||
|
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||||
|
import {
|
||||||
|
getAllSucceededRemoteData,
|
||||||
|
getFirstSucceededRemoteData,
|
||||||
|
metadataFieldsToString
|
||||||
|
} from '../../../core/shared/operators';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { RegistryService } from '../../../core/registry/registry.service';
|
||||||
|
import { FormControl } from '@angular/forms';
|
||||||
|
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||||
|
import { hasValue } from '../../../shared/empty.util';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-metadata-field-selector',
|
||||||
|
styleUrls: ['./metadata-field-selector.component.scss'],
|
||||||
|
templateUrl: './metadata-field-selector.component.html'
|
||||||
|
})
|
||||||
|
export class MetadataFieldSelectorComponent implements OnInit, OnDestroy {
|
||||||
|
@Input() mdField: string;
|
||||||
|
@Output() mdFieldChange = new EventEmitter<string>();
|
||||||
|
mdFieldOptions$: Observable<string[]>;
|
||||||
|
|
||||||
|
public input: FormControl = new FormControl();
|
||||||
|
|
||||||
|
query$: BehaviorSubject<string> = new BehaviorSubject<string>(null);
|
||||||
|
debounceTime = 500;
|
||||||
|
|
||||||
|
subs: Subscription[] = [];
|
||||||
|
|
||||||
|
constructor(protected registryService: RegistryService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.subs.push(
|
||||||
|
this.input.valueChanges.pipe(
|
||||||
|
debounceTime(this.debounceTime),
|
||||||
|
).subscribe((valueChange) => {
|
||||||
|
this.query$.next(valueChange);
|
||||||
|
this.mdField = valueChange;
|
||||||
|
this.mdFieldChange.emit(this.mdField);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.mdFieldOptions$ = this.query$.pipe(
|
||||||
|
distinctUntilChanged(),
|
||||||
|
switchMap((query) => {
|
||||||
|
if (query !== null) {
|
||||||
|
return this.registryService.queryMetadataFields(query, null, true, false, followLink('schema')).pipe(
|
||||||
|
getAllSucceededRemoteData(),
|
||||||
|
metadataFieldsToString(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return [[]];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
select(mdFieldOption: string) {
|
||||||
|
this.mdField = mdFieldOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
}
|
20
src/app/dso-shared/dso-shared.module.ts
Normal file
20
src/app/dso-shared/dso-shared.module.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
SharedModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
DsoEditMetadataComponent,
|
||||||
|
MetadataFieldSelectorComponent,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
DsoEditMetadataComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class DsoSharedModule {
|
||||||
|
|
||||||
|
}
|
@@ -35,6 +35,7 @@ import { ItemVersionHistoryComponent } from './item-version-history/item-version
|
|||||||
import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component';
|
import { ItemAuthorizationsComponent } from './item-authorizations/item-authorizations.component';
|
||||||
import { ObjectValuesPipe } from '../../shared/utils/object-values-pipe';
|
import { ObjectValuesPipe } from '../../shared/utils/object-values-pipe';
|
||||||
import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-policies.module';
|
import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-policies.module';
|
||||||
|
import { DsoSharedModule } from '../../dso-shared/dso-shared.module';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +49,8 @@ import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-
|
|||||||
EditItemPageRoutingModule,
|
EditItemPageRoutingModule,
|
||||||
SearchPageModule,
|
SearchPageModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
ResourcePoliciesModule
|
ResourcePoliciesModule,
|
||||||
|
DsoSharedModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
EditItemPageComponent,
|
EditItemPageComponent,
|
||||||
|
@@ -38,6 +38,7 @@ import { ItemPageBitstreamsGuard } from './item-page-bitstreams.guard';
|
|||||||
import { ItemPageRelationshipsGuard } from './item-page-relationships.guard';
|
import { ItemPageRelationshipsGuard } from './item-page-relationships.guard';
|
||||||
import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard';
|
import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard';
|
||||||
import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard';
|
import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard';
|
||||||
|
import { DsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/dso-edit-metadata.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Routing module that handles the routing for the Edit Item page administrator functionality
|
* Routing module that handles the routing for the Edit Item page administrator functionality
|
||||||
@@ -75,7 +76,7 @@ import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.gua
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'metadata',
|
path: 'metadata',
|
||||||
component: ItemMetadataComponent,
|
component: DsoEditMetadataComponent,
|
||||||
data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true },
|
data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true },
|
||||||
canActivate: [ItemPageMetadataGuard]
|
canActivate: [ItemPageMetadataGuard]
|
||||||
},
|
},
|
||||||
|
@@ -29,11 +29,17 @@
|
|||||||
--bs-teal: #{$teal};
|
--bs-teal: #{$teal};
|
||||||
--bs-cyan: #{$cyan};
|
--bs-cyan: #{$cyan};
|
||||||
--bs-primary: #{$primary};
|
--bs-primary: #{$primary};
|
||||||
|
--bs-primary-bg: #{lighten($primary, 30%)};
|
||||||
--bs-secondary: #{$secondary};
|
--bs-secondary: #{$secondary};
|
||||||
|
--bs-secondary-bg: #{lighten($secondary, 30%)};
|
||||||
--bs-success: #{$success};
|
--bs-success: #{$success};
|
||||||
|
--bs-success-bg: #{lighten($success, 30%)};
|
||||||
--bs-info: #{$info};
|
--bs-info: #{$info};
|
||||||
|
--bs-info-bg: #{lighten($info, 30%)};
|
||||||
--bs-warning: #{$warning};
|
--bs-warning: #{$warning};
|
||||||
|
--bs-warning-bg: #{lighten($warning, 30%)};
|
||||||
--bs-danger: #{$danger};
|
--bs-danger: #{$danger};
|
||||||
|
--bs-danger-bg: #{lighten($danger, 30%)};
|
||||||
--bs-light: #{$light};
|
--bs-light: #{$light};
|
||||||
--bs-dark: #{$dark};
|
--bs-dark: #{$dark};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user