mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
227 lines
6.0 KiB
TypeScript
227 lines
6.0 KiB
TypeScript
import {
|
|
AsyncPipe,
|
|
NgFor,
|
|
NgIf,
|
|
} from '@angular/common';
|
|
import {
|
|
ChangeDetectorRef,
|
|
Component,
|
|
ElementRef,
|
|
EventEmitter,
|
|
HostListener,
|
|
Input,
|
|
OnDestroy,
|
|
OnInit,
|
|
Output,
|
|
} from '@angular/core';
|
|
import { TranslateModule } from '@ngx-translate/core';
|
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
|
import {
|
|
BehaviorSubject,
|
|
Observable,
|
|
Subscription,
|
|
} from 'rxjs';
|
|
import {
|
|
reduce,
|
|
startWith,
|
|
switchMap,
|
|
} from 'rxjs/operators';
|
|
|
|
import { EntityTypeDataService } from '../../core/data/entity-type-data.service';
|
|
import { FindListOptions } from '../../core/data/find-list-options.model';
|
|
import { PaginatedList } from '../../core/data/paginated-list.model';
|
|
import { RemoteData } from '../../core/data/remote-data';
|
|
import { ItemType } from '../../core/shared/item-relationships/item-type.model';
|
|
import { getFirstSucceededRemoteWithNotEmptyData } from '../../core/shared/operators';
|
|
import { hasValue } from '../empty.util';
|
|
import { ThemedLoadingComponent } from '../loading/themed-loading.component';
|
|
|
|
@Component({
|
|
selector: 'ds-entity-dropdown',
|
|
templateUrl: './entity-dropdown.component.html',
|
|
styleUrls: ['./entity-dropdown.component.scss'],
|
|
standalone: true,
|
|
imports: [InfiniteScrollModule, NgIf, NgFor, ThemedLoadingComponent, AsyncPipe, TranslateModule],
|
|
})
|
|
export class EntityDropdownComponent implements OnInit, OnDestroy {
|
|
/**
|
|
* The entity list obtained from a search
|
|
* @type {Observable<ItemType[]>}
|
|
*/
|
|
public searchListEntity$: Observable<ItemType[]>;
|
|
|
|
/**
|
|
* A boolean representing if dropdown list is scrollable to the bottom
|
|
* @type {boolean}
|
|
*/
|
|
private scrollableBottom = false;
|
|
|
|
/**
|
|
* A boolean representing if dropdown list is scrollable to the top
|
|
* @type {boolean}
|
|
*/
|
|
private scrollableTop = false;
|
|
|
|
/**
|
|
* The list of entity to render
|
|
*/
|
|
public searchListEntity: ItemType[] = [];
|
|
|
|
/**
|
|
* TRUE if the parent operation is a 'new submission' operation, FALSE otherwise (eg.: is an 'Import metadata from an external source' operation).
|
|
*/
|
|
@Input() isSubmission: boolean;
|
|
|
|
/**
|
|
* The entity to output to the parent component
|
|
*/
|
|
@Output() selectionChange = new EventEmitter<ItemType>();
|
|
|
|
/**
|
|
* A boolean representing if the loader is visible or not
|
|
*/
|
|
public isLoadingList: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
|
|
|
/**
|
|
* A numeric representig current page
|
|
*/
|
|
public currentPage: number;
|
|
|
|
/**
|
|
* A boolean representing if exist another page to render
|
|
*/
|
|
public hasNextPage: boolean;
|
|
|
|
/**
|
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
|
* @type {Array}
|
|
*/
|
|
public subs: Subscription[] = [];
|
|
|
|
/**
|
|
* Initialize instance variables
|
|
*
|
|
* @param {ChangeDetectorRef} changeDetectorRef
|
|
* @param {EntityTypeDataService} entityTypeService
|
|
* @param el
|
|
*/
|
|
constructor(
|
|
private changeDetectorRef: ChangeDetectorRef,
|
|
private entityTypeService: EntityTypeDataService,
|
|
private el: ElementRef,
|
|
) { }
|
|
|
|
/**
|
|
* Method called on mousewheel event, it prevent the page scroll
|
|
* when arriving at the top/bottom of dropdown menu
|
|
*
|
|
* @param event
|
|
* mousewheel event
|
|
*/
|
|
@HostListener('mousewheel', ['$event']) onMousewheel(event) {
|
|
if (event.wheelDelta > 0 && this.scrollableTop) {
|
|
event.preventDefault();
|
|
}
|
|
if (event.wheelDelta < 0 && this.scrollableBottom) {
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize entity list
|
|
*/
|
|
ngOnInit() {
|
|
this.resetPagination();
|
|
this.populateEntityList(this.currentPage);
|
|
}
|
|
|
|
/**
|
|
* Check if dropdown scrollbar is at the top or bottom of the dropdown list
|
|
*
|
|
* @param event
|
|
*/
|
|
public onScroll(event) {
|
|
this.scrollableBottom = (event.target.scrollTop + event.target.clientHeight === event.target.scrollHeight);
|
|
this.scrollableTop = (event.target.scrollTop === 0);
|
|
}
|
|
|
|
/**
|
|
* Method used from infitity scroll for retrieve more data on scroll down
|
|
*/
|
|
public onScrollDown() {
|
|
if ( this.hasNextPage ) {
|
|
this.populateEntityList(++this.currentPage);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Emit a [selectionChange] event when a new entity is selected from list
|
|
*
|
|
* @param event
|
|
* the selected [ItemType]
|
|
*/
|
|
public onSelect(event: ItemType) {
|
|
this.selectionChange.emit(event);
|
|
}
|
|
|
|
/**
|
|
* Method called for populate the entity list
|
|
* @param page page number
|
|
*/
|
|
public populateEntityList(page: number) {
|
|
this.isLoadingList.next(true);
|
|
// Set the pagination info
|
|
const findOptions: FindListOptions = {
|
|
elementsPerPage: 10,
|
|
currentPage: page,
|
|
};
|
|
let searchListEntity$;
|
|
if (this.isSubmission) {
|
|
searchListEntity$ = this.entityTypeService.getAllAuthorizedRelationshipType(findOptions);
|
|
} else {
|
|
searchListEntity$ = this.entityTypeService.getAllAuthorizedRelationshipTypeImport(findOptions);
|
|
}
|
|
this.searchListEntity$ = searchListEntity$.pipe(
|
|
getFirstSucceededRemoteWithNotEmptyData(),
|
|
switchMap((entityType: RemoteData<PaginatedList<ItemType>>) => {
|
|
if ( (this.searchListEntity.length + findOptions.elementsPerPage) >= entityType.payload.totalElements ) {
|
|
this.hasNextPage = false;
|
|
}
|
|
return entityType.payload.page;
|
|
}),
|
|
reduce((acc: any, value: any) => [...acc, value], []),
|
|
startWith([]),
|
|
);
|
|
this.subs.push(
|
|
this.searchListEntity$.subscribe(
|
|
(next) => { this.searchListEntity.push(...next); }, undefined,
|
|
() => { this.hideShowLoader(false); this.changeDetectorRef.detectChanges(); },
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Reset pagination values
|
|
*/
|
|
public resetPagination() {
|
|
this.currentPage = 1;
|
|
this.hasNextPage = true;
|
|
this.searchListEntity = [];
|
|
}
|
|
|
|
/**
|
|
* Hide/Show the entity list loader
|
|
* @param hideShow true for show, false otherwise
|
|
*/
|
|
public hideShowLoader(hideShow: boolean) {
|
|
this.isLoadingList.next(hideShow);
|
|
}
|
|
|
|
/**
|
|
* Unsubscribe from all subscriptions
|
|
*/
|
|
ngOnDestroy(): void {
|
|
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
|
}
|
|
}
|