65528: Renaming/Moving according to feedback; style and links added; chevrons fixed

This commit is contained in:
Marie Verdonck
2019-10-11 17:37:43 +02:00
parent 79424ac108
commit 3daf35e4a4
8 changed files with 243 additions and 228 deletions

View File

@@ -1,131 +0,0 @@
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
import { PaginatedList } from '../core/data/paginated-list';
import { RemoteData } from '../core/data/remote-data';
import { hasValue, isNotEmpty } from '../shared/empty.util';
import { CommunityListService } from './CommunityListService';
import { CollectionViewer, DataSource } from '@angular/cdk/typings/collections';
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
import {
catchError,
filter,
finalize,
map,
switchMap,
take,
} from 'rxjs/operators';
import { Community } from '../core/shared/community.model';
import { Collection } from '../core/shared/collection.model';
export interface FlatNode {
isExpandable: boolean;
name: string;
id: string;
level: number;
isExpanded?: boolean;
parent?: FlatNode;
payload: Community | Collection;
}
const combineAndFlatten = (obsList: Array<Observable<FlatNode[]>>) =>
observableCombineLatest(...obsList).pipe(
map((matrix: FlatNode[][]) =>
matrix.reduce((combinedList, currentList: FlatNode[]) => [...combinedList, ...currentList]))
);
const toFlatNode = (
c: Community | Collection,
level: number,
isExpanded: boolean,
parent?: FlatNode
): FlatNode => ({
isExpandable: c instanceof Community,
name: c.name,
id: c.id,
level: level,
isExpanded,
parent,
payload: c,
});
export class CommunityListDataSource implements DataSource<FlatNode> {
private communityList$ = new BehaviorSubject<FlatNode[]>([]);
private loading$ = new BehaviorSubject<boolean>(false);
constructor(private communityListService: CommunityListService) {
}
connect(collectionViewer: CollectionViewer): Observable<FlatNode[]> {
return this.communityList$.asObservable();
}
disconnect(collectionViewer: CollectionViewer): void {
this.communityList$.complete();
this.loading$.complete();
}
loadCommunities(expandedNodes: FlatNode[]): void {
this.loading$.next(true);
this.communityListService.communities$
.pipe(
take(1),
switchMap((result: Community[]) => {
return this.transformListOfCommunities(result, 0, null, expandedNodes);
}),
catchError(() => observableOf([])),
finalize(() => this.loading$.next(false)),
).subscribe((flatNodes: FlatNode[]) => this.communityList$.next(flatNodes));
};
private transformListOfCommunities(listOfCommunities: Community[],
level: number,
parent: FlatNode,
expandedNodes: FlatNode[]): Observable<FlatNode[]> {
if (isNotEmpty(listOfCommunities)) {
const obsList = listOfCommunities
.map((community: Community) =>
this.transformCommunity(community, level, parent, expandedNodes));
return combineAndFlatten(obsList);
} else {
return observableOf([]);
}
}
private transformCommunity(community: Community, level: number, parent: FlatNode, expandedNodes: FlatNode[]): Observable<FlatNode[]> {
let isExpanded = false;
if (isNotEmpty(expandedNodes)) {
isExpanded = hasValue(expandedNodes.find((node) => (node.id === community.id)));
}
const communityFlatNode = toFlatNode(community, level, isExpanded, parent);
let obsList = [observableOf([communityFlatNode])];
if (isExpanded) {
const subCommunityNodes$ = community.subcommunities.pipe(
filter((rd: RemoteData<PaginatedList<Community>>) => rd.hasSucceeded),
take(1),
switchMap((rd: RemoteData<PaginatedList<Community>>) =>
this.transformListOfCommunities(rd.payload.page, level + 1, communityFlatNode, expandedNodes))
);
obsList = [...obsList, subCommunityNodes$];
const collectionNodes$ = community.collections.pipe(
filter((rd: RemoteData<PaginatedList<Collection>>) => rd.hasSucceeded),
take(1),
map((rd: RemoteData<PaginatedList<Collection>>) =>
rd.payload.page
.map((collection: Collection) => toFlatNode(collection, level + 1, false, parent))
)
);
obsList = [...obsList, collectionNodes$];
}
return combineAndFlatten(obsList);
}
}

View File

@@ -1,37 +0,0 @@
import {Injectable} from '@angular/core';
import {Observable, of} from 'rxjs';
import {CommunityDataService} from '../core/data/community-data.service';
import {PaginationComponentOptions} from '../shared/pagination/pagination-component-options.model';
import {SortDirection, SortOptions} from '../core/cache/models/sort-options.model';
import { map, take, tap } from 'rxjs/operators';
import {Community} from '../core/shared/community.model';
@Injectable()
export class CommunityListService {
communities$: Observable<Community[]>;
config: PaginationComponentOptions;
sortConfig: SortOptions;
constructor(private cds: CommunityDataService) {
this.config = new PaginationComponentOptions();
this.config.id = 'top-level-pagination';
this.config.pageSize = 10;
this.config.currentPage = 1;
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
this.initTopCommunityList()
}
private initTopCommunityList(): void {
this.communities$ = this.cds.findTop({
currentPage: this.config.currentPage,
elementsPerPage: this.config.pageSize,
sort: { field: this.sortConfig.field, direction: this.sortConfig.direction }
}).pipe(
take(1),
map((results) => results.payload.page),
);
}
}

View File

@@ -0,0 +1,134 @@
import {Injectable} from '@angular/core';
import {combineLatest as observableCombineLatest} from 'rxjs/internal/observable/combineLatest';
import {Observable, of as observableOf} from 'rxjs';
import {CommunityDataService} from '../core/data/community-data.service';
import {PaginationComponentOptions} from '../shared/pagination/pagination-component-options.model';
import {SortDirection, SortOptions} from '../core/cache/models/sort-options.model';
import {catchError, filter, map, switchMap, take} from 'rxjs/operators';
import {Community} from '../core/shared/community.model';
import {Collection} from '../core/shared/collection.model';
import {hasValue, isNotEmpty} from '../shared/empty.util';
import {RemoteData} from '../core/data/remote-data';
import {PaginatedList} from '../core/data/paginated-list';
export interface FlatNode {
isExpandable: boolean;
name: string;
id: string;
level: number;
isExpanded?: boolean;
parent?: FlatNode;
payload: Community | Collection;
}
export const combineAndFlatten = (obsList: Array<Observable<FlatNode[]>>): Observable<FlatNode[]> =>
observableCombineLatest(...obsList).pipe(
map((matrix: FlatNode[][]) =>
matrix.reduce((combinedList, currentList: FlatNode[]) => [...combinedList, ...currentList]))
);
export const toFlatNode = (
c: Community | Collection,
level: number,
isExpanded: boolean,
parent?: FlatNode
): FlatNode => ({
isExpandable: c instanceof Community,
name: c.name,
id: c.id,
level: level,
isExpanded,
parent,
payload: c,
});
@Injectable()
export class CommunityListAdapter {
communities$: Observable<Community[]>;
config: PaginationComponentOptions;
sortConfig: SortOptions;
constructor(private cds: CommunityDataService) {
this.config = new PaginationComponentOptions();
this.config.id = 'top-level-pagination';
this.config.pageSize = 50;
this.config.currentPage = 1;
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC);
this.initTopCommunityList()
}
private initTopCommunityList(): void {
this.communities$ = this.cds.findTop({
currentPage: this.config.currentPage,
elementsPerPage: this.config.pageSize,
sort: {field: this.sortConfig.field, direction: this.sortConfig.direction}
}).pipe(
take(1),
map((results) => results.payload.page),
);
}
loadCommunities(expandedNodes: FlatNode[]): Observable<FlatNode[]> {
return this.communities$
.pipe(
take(1),
switchMap((result: Community[]) => {
return this.transformListOfCommunities(result, 0, null, expandedNodes);
}),
catchError(() => observableOf([]))
);
};
private transformListOfCommunities(listOfCommunities: Community[],
level: number,
parent: FlatNode,
expandedNodes: FlatNode[]): Observable<FlatNode[]> {
if (isNotEmpty(listOfCommunities)) {
const obsList = listOfCommunities
.map((community: Community) =>
this.transformCommunity(community, level, parent, expandedNodes));
return combineAndFlatten(obsList);
} else {
return observableOf([]);
}
}
private transformCommunity(community: Community, level: number, parent: FlatNode, expandedNodes: FlatNode[]): Observable<FlatNode[]> {
let isExpanded = false;
if (isNotEmpty(expandedNodes)) {
isExpanded = hasValue(expandedNodes.find((node) => (node.id === community.id)));
}
const communityFlatNode = toFlatNode(community, level, isExpanded, parent);
let obsList = [observableOf([communityFlatNode])];
if (isExpanded) {
const subCommunityNodes$ = community.subcommunities.pipe(
filter((rd: RemoteData<PaginatedList<Community>>) => rd.hasSucceeded),
take(1),
switchMap((rd: RemoteData<PaginatedList<Community>>) =>
this.transformListOfCommunities(rd.payload.page, level + 1, communityFlatNode, expandedNodes))
);
obsList = [...obsList, subCommunityNodes$];
const collectionNodes$ = community.collections.pipe(
filter((rd: RemoteData<PaginatedList<Collection>>) => rd.hasSucceeded),
take(1),
map((rd: RemoteData<PaginatedList<Collection>>) =>
rd.payload.page
.map((collection: Collection) => toFlatNode(collection, level + 1, false, parent))
)
);
obsList = [...obsList, collectionNodes$];
}
return combineAndFlatten(obsList);
}
}

View File

@@ -0,0 +1,33 @@
import {CommunityListAdapter, FlatNode} from './community-list-adapter';
import {CollectionViewer, DataSource} from '@angular/cdk/typings/collections';
import {BehaviorSubject, Observable,} from 'rxjs';
import {finalize, take,} from 'rxjs/operators';
export class CommunityListDatasource implements DataSource<FlatNode> {
private communityList$ = new BehaviorSubject<FlatNode[]>([]);
private loading$ = new BehaviorSubject<boolean>(false);
constructor(private communityListService: CommunityListAdapter) {
}
connect(collectionViewer: CollectionViewer): Observable<FlatNode[]> {
this.loadCommunities(null);
return this.communityList$.asObservable();
}
loadCommunities(expandedNodes: FlatNode[]) {
this.loading$.next(true);
this.communityListService.loadCommunities(expandedNodes).pipe(
take(1),
finalize(() => this.loading$.next(false)),
).subscribe((flatNodes: FlatNode[]) => this.communityList$.next(flatNodes));
}
disconnect(collectionViewer: CollectionViewer): void {
this.communityList$.complete();
this.loading$.complete();
}
}

View File

@@ -1,2 +1,4 @@
<h2>{{ 'communityList.title' | translate }}</h2> <div class="container">
<ds-community-list></ds-community-list> <h2>{{ 'communityList.title' | translate }}</h2>
<ds-community-list></ds-community-list>
</div>

View File

@@ -3,7 +3,7 @@ import { RouterModule } from '@angular/router';
import {CdkTreeModule} from '@angular/cdk/tree'; import {CdkTreeModule} from '@angular/cdk/tree';
import {CommunityListPageComponent} from './community-list-page.component'; import {CommunityListPageComponent} from './community-list-page.component';
import {CommunityListService} from './CommunityListService'; import {CommunityListAdapter} from './community-list-adapter';
@NgModule({ @NgModule({
imports: [ imports: [
@@ -12,6 +12,6 @@ import {CommunityListService} from './CommunityListService';
]), ]),
CdkTreeModule, CdkTreeModule,
], ],
providers: [CommunityListService] providers: [CommunityListAdapter]
}) })
export class CommunityListPageRoutingModule { } export class CommunityListPageRoutingModule { }

View File

@@ -5,21 +5,34 @@
class="example-tree-node"> class="example-tree-node">
<!-- use a disabled button to provide padding for tree leaf --> <!-- use a disabled button to provide padding for tree leaf -->
<button type="button" class="btn btn-default" [style.visibility]="'hidden'"></button> <button type="button" class="btn btn-default" [style.visibility]="'hidden'"></button>
<h5 class="align-middle">{{node.name}}</h5> <h6 class="align-middle">
<a [routerLink]="['/collections/' + node.payload.id]" class="lead">
{{node.name}}
</a>
</h6>
<div class="text-muted">
{{node.payload.shortDescription}}
</div>
</cdk-tree-node> </cdk-tree-node>
<!-- This is the tree node template for expandable nodes --> <!-- This is the tree node template for expandable nodes -->
<cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding <cdk-tree-node *cdkTreeNodeDef="let node; when: hasChild" cdkTreeNodePadding
[style.display]="shouldRender(node) ? 'flex' : 'none'"
class="example-tree-node"> class="example-tree-node">
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-default" cdkTreeNodeToggle <button type="button" class="btn btn-default" cdkTreeNodeToggle
[attr.aria-label]="'toggle ' + node.name" [attr.aria-label]="'toggle ' + node.name"
(click)="toggleExpanded(node)" (click)="toggleExpanded(node)"
[style.visibility]="node.isExpandable ? 'visible' : 'hidden'"> [style.visibility]="node.isExpandable ? 'visible' : 'hidden'">
<span class="{{treeControl.isExpanded(node) ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}" aria-hidden="true"> <span class="{{node.isExpanded ? 'fa fa-chevron-down' : 'fa fa-chevron-right'}}"
</span> aria-hidden="true"></span>
</button> </button>
<h5 class="align-middle pt-2">{{node.name}}</h5> <h5 class="align-middle pt-2">
<a [routerLink]="['/communities/' + node.payload.id]" class="lead">
{{node.name}}
</a>
</h5>
</div>
<div class="text-muted pl-4">
{{node.payload.shortDescription}}
</div> </div>
</cdk-tree-node> </cdk-tree-node>
</cdk-tree> </cdk-tree>

View File

@@ -1,6 +1,6 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {CommunityListService} from '../CommunityListService'; import {CommunityListAdapter, FlatNode} from '../community-list-adapter';
import {FlatNode, CommunityListDataSource} from '../CommunityListDataSource'; import {CommunityListDatasource} from '../community-list-datasource';
import {FlatTreeControl} from '@angular/cdk/tree'; import {FlatTreeControl} from '@angular/cdk/tree';
@Component({ @Component({
@@ -16,12 +16,13 @@ export class CommunityListComponent implements OnInit {
(node) => node.level, (node) => node.isExpandable (node) => node.level, (node) => node.isExpandable
); );
dataSource: CommunityListDataSource; dataSource: CommunityListDatasource;
constructor(private communityListService: CommunityListService) { } constructor(private communityListService: CommunityListAdapter) {
}
ngOnInit() { ngOnInit() {
this.dataSource = new CommunityListDataSource(this.communityListService); this.dataSource = new CommunityListDatasource(this.communityListService);
this.dataSource.loadCommunities(null); this.dataSource.loadCommunities(null);
} }