Skip to content

Commit

Permalink
refactor(viewer): return label and id in multiple resource selection (D…
Browse files Browse the repository at this point in the history
…SP-1842 / DSP-1820) (#327)

* refactor(lib): clean up and update package.json

* chore: git ignore yalc.lock

* refactor(viewer): refactor code and improve functionality

* chore(playground): update playground with new setup

* fix(viewer): bug fix in tests

* refactor(viewer): remove console.logs
  • Loading branch information
kilchenmann committed Aug 10, 2021
1 parent 128a7d6 commit 016e22a
Show file tree
Hide file tree
Showing 14 changed files with 209 additions and 115 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -11,6 +11,7 @@
# dependencies
/node_modules
/.yalc
yalc.lock

# profiling files
chrome-profiler-events*.json
Expand Down
9 changes: 5 additions & 4 deletions projects/dsp-ui/ng-package.json
Expand Up @@ -7,13 +7,14 @@
"lib": {
"entryFile": "src/public-api.ts",
"umdModuleIds": {
"@ckeditor/ckeditor5-angular": "ckeditor5Angular",
"@dasch-swiss/dsp-js": "@dasch-swiss/dsp-js",
"ngx-color-picker": "ngxColorPicker",
"angular-split": "angular-split",
"ckeditor5-custom-build": "Editor",
"jdnconvertiblecalendar": "jdnconvertiblecalendar",
"jdnconvertiblecalendardateadapter": "jdnconvertiblecalendardateadapter",
"ts-md5": "tsMd5",
"@ckeditor/ckeditor5-angular": "ckeditor5Angular",
"ckeditor5-custom-build": "Editor"
"ngx-color-picker": "ngxColorPicker",
"ts-md5": "tsMd5"
}
}
}
9 changes: 5 additions & 4 deletions projects/dsp-ui/package.json
Expand Up @@ -20,17 +20,18 @@
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/cdk": "^11.2.5",
"@angular/common": "~11.2.6",
"@angular/core": "~11.2.6",
"@angular/material": "^11.2.5",
"@angular/cdk": "^11.2.5",
"@ckeditor/ckeditor5-angular": "^1.2.3",
"@dasch-swiss/dsp-js": "^3.0.0",
"angular-split": "^4.0.0",
"ckeditor5-custom-build": "github:dasch-swiss/ckeditor_custom_build",
"jdnconvertiblecalendar": "^0.0.7",
"jdnconvertiblecalendardateadapter": "^0.0.17",
"ngx-color-picker": "^11.0.0",
"openseadragon": "^2.4.2",
"svg-overlay": "github:openseadragon/svg-overlay",
"@ckeditor/ckeditor5-angular": "^1.2.3",
"ckeditor5-custom-build": "github:dasch-swiss/ckeditor_custom_build"
"svg-overlay": "github:openseadragon/svg-overlay"
}
}
106 changes: 70 additions & 36 deletions projects/dsp-ui/src/lib/viewer/views/list-view/list-view.component.ts
@@ -1,6 +1,6 @@
import { Component, EventEmitter, Inject, Input, OnChanges, Output } from '@angular/core';
import { PageEvent } from '@angular/material/paginator';
import { ApiResponseError, CountQueryResponse, IFulltextSearchParams, KnoraApiConnection, ReadResource, ReadResourceSequence } from '@dasch-swiss/dsp-js';
import { ApiResponseError, CountQueryResponse, IFulltextSearchParams, KnoraApiConnection, ReadResourceSequence } from '@dasch-swiss/dsp-js';
import { NotificationService } from '../../../action/services/notification.service';
import { DspApiConnectionToken } from '../../../core/core.module';
import { AdvancedSearchParamsService } from '../../../search/services/advanced-search-params.service';
Expand All @@ -22,16 +22,21 @@ export interface SearchParams {
filter?: IFulltextSearchParams;
}

export interface ShortResInfo {
id: string;
label: string;
}

/* return the selected resources in below format
*
* count: total number of resources selected
* selectedIds: list of selected resource's ids
*/
export interface FilteredResouces {
count: number,
resListIndex: number[],
resIds: string[],
selectionType: 'multiple' | 'single'
export interface FilteredResources {
count: number;
resListIndex: number[];
resInfo: ShortResInfo[];
selectionType: 'multiple' | 'single';
}

/* return the checkbox value
Expand All @@ -40,10 +45,11 @@ export interface FilteredResouces {
* resIndex: resource index from the list
*/
export interface CheckboxUpdate {
checked: boolean,
resListIndex: number,
resId: string,
isCheckbox: boolean
checked: boolean;
resIndex: number;
resId: string;
resLabel: string;
isCheckbox: boolean;
}

@Component({
Expand All @@ -65,21 +71,30 @@ export class ListViewComponent implements OnChanges {
@Input() withMultipleSelection?: boolean = false;

/**
* Emits the selected resources 1-n
*/
@Output() selectedResources: EventEmitter<FilteredResources> = new EventEmitter<FilteredResources>();

/**
* @deprecated Use selectedResources instead
*
* Click on checkbox will emit the resource info
*
* @param {EventEmitter<FilteredResouces>} resourcesSelected
* @param {EventEmitter<FilteredResources>} resourcesSelected
*/
@Output() multipleResourcesSelected?: EventEmitter<FilteredResouces> = new EventEmitter<FilteredResouces>();
@Output() multipleResourcesSelected?: EventEmitter<FilteredResources> = new EventEmitter<FilteredResources>();

/**
* @deprecated Use selectedResources instead
*
* Click on an item will emit the resource iri
*
* @param {EventEmitter<string>} singleResourceSelected
*/
@Output() singleResourceSelected?: EventEmitter<string> = new EventEmitter<string>();

/**
* @deprecated Use singleResourceSelected instead.
* @deprecated Use selectedResources instead.
* Click on an item will emit the resource iri
*/
@Output() resourceSelected: EventEmitter<string> = new EventEmitter<string>();
Expand Down Expand Up @@ -122,24 +137,33 @@ export class ListViewComponent implements OnChanges {
this.view = view;
}

// If 'withMultipleSelection' is true, multiple resources are selected for comparision
// If 'withMultipleSelection' is false, single resource is selected for viewing
emitSelectedResources(resInfo: FilteredResouces) {
this.selectedResourceIdx = resInfo.resListIndex;

if (resInfo.selectionType === 'multiple') {
this.multipleResourcesSelected.emit(resInfo);
} else {
this.singleResourceSelected.emit(resInfo.resIds[0]);
this.resourceSelected.emit(resInfo.resIds[0]);
// the child component send the selected resources to the parent of this component directly;
// but when this component is intialized, it should select the first item in the list and
// emit this selected resource to the parent.
emitSelectedResources(res?: FilteredResources) {

if (!res || res.count === 0) {
// no resource is selected: In case of an error or no search results
this.selectedResources.emit({ count: 0, resListIndex: [], resInfo: [], selectionType: 'single' });
} else if (res.count > 0) {
this.selectedResourceIdx = res.resListIndex;
this.selectedResources.emit(res);
this.resourceSelected.emit(res.resInfo[0].id);
}


}

goToPage(page: PageEvent) {
this.pageEvent = page;
this._doSearch();
}


/**
* do the search and send the resources to the child components
* like resource-list, resource-grid or resource-table
*/
private _doSearch() {

this.loading = true;
Expand All @@ -153,24 +177,27 @@ export class ListViewComponent implements OnChanges {
this.numberOfAllResults = count.numberOfResults;

if (this.numberOfAllResults === 0) {
this.emitSelectedResources();
this.resources = undefined;
this._emitSelectedResource(undefined);
this.loading = false;
}
},
(countError: ApiResponseError) => {
this._notification.openSnackBar(countError);
this._emitSelectedResource(undefined);
}
);
}

// perform full text search
this._dspApiConnection.v2.search.doFulltextSearch(this.search.query, this.pageEvent.pageIndex, this.search.filter).subscribe(
(response: ReadResourceSequence) => {
// if the response does not contain any resources even the search count is greater than 0,
// it means that the user does not have the permissions to see anything: emit an empty result
if (response.resources.length === 0) {
this.emitSelectedResources();
}
this.resources = response;
this.loading = false;
this._emitSelectedResource(this.resources.resources);
},
(error: ApiResponseError) => {
this._notification.openSnackBar(error);
Expand All @@ -189,14 +216,13 @@ export class ListViewComponent implements OnChanges {
this.numberOfAllResults = count.numberOfResults;

if (this.numberOfAllResults === 0) {
this.emitSelectedResources();
this.resources = undefined;
this._emitSelectedResource(undefined);
this.loading = false;
}
},
(countError: ApiResponseError) => {
this._notification.openSnackBar(countError);
this._emitSelectedResource(undefined);
}
);
}
Expand All @@ -207,9 +233,13 @@ export class ListViewComponent implements OnChanges {
if (typeof gravsearch === 'string') {
this._dspApiConnection.v2.search.doExtendedSearch(gravsearch).subscribe(
(response: ReadResourceSequence) => {
// if the response does not contain any resources even the search count is greater than 0,
// it means that the user does not have the permissions to see anything: emit an empty result
if (response.resources.length === 0) {
this.emitSelectedResources();
}
this.resources = response;
this.loading = false;
this._emitSelectedResource(this.resources.resources);
},
(error: ApiResponseError) => {
this._notification.openSnackBar(error);
Expand All @@ -227,12 +257,16 @@ export class ListViewComponent implements OnChanges {

}

private _emitSelectedResource(resources: ReadResource[]) {
if (resources && resources.length > 0) {
this.emitSelectedResources({ count: 1, resListIndex: [0], resIds: [resources[0].id], selectionType: 'single' });
} else {
this.emitSelectedResources({ count: 0, resListIndex: [], resIds: [], selectionType: 'single'});
}
}
// private _prepareResourceToEmit(resources: ReadResource[]) {
// if (resources && resources.length > 0) {
// const resInfo: ShortResInfo = {
// id: resources[0].id,
// label: resources[0].label
// };
// this.emitSelectedResources({ count: 1, resListIndex: [0], resInfo: [resInfo], selectionType: 'single' });
// } else {
// this.emitSelectedResources({ count: 0, resListIndex: [], resInfo: [], selectionType: 'single'});
// }
// }

}
46 changes: 31 additions & 15 deletions projects/dsp-ui/src/lib/viewer/views/list-view/list-view.service.ts
@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { MatCheckbox } from '@angular/material/checkbox';
import { CheckboxUpdate, FilteredResouces } from './list-view.component';
import { CheckboxUpdate, FilteredResources, ShortResInfo } from './list-view.component';

@Injectable({
providedIn: 'root'
Expand All @@ -9,52 +9,68 @@ export class ListViewService {

// for keeping track of multiple selection
selectedResourcesCount = 0;
selectedResourcesList = [];
selectedResourcesList: ShortResInfo[] = [];
selectedResourceIdxMultiple = [];

constructor() { }

viewResource(status: CheckboxUpdate, withMultipleSelection: boolean, selectedResourceIdx: number [], resChecks: MatCheckbox[]): FilteredResouces {
viewResource(status: CheckboxUpdate, withMultipleSelection: boolean, selectedResourceIdx: number [], resChecks: MatCheckbox[]): FilteredResources {

if (selectedResourceIdx.length === 1 && this.selectedResourcesCount === 0) {
// reset the selected resources count and list
// this.selectedResourcesCount = 0;
this.selectedResourcesList = [];
this.selectedResourceIdxMultiple = [];
}


const resInfo: ShortResInfo = {
id: status.resId,
label: status.resLabel
};

// when multiple selection and checkbox is used to select more
// than one resources
if (withMultipleSelection && status.isCheckbox) {

if (status.checked) {
if (selectedResourceIdx.indexOf(status.resListIndex) <= 0) {
if (selectedResourceIdx.indexOf(status.resIndex) <= 0) {
// add resource in to the selected resources list
this.selectedResourcesList.push(status.resId);
this.selectedResourcesList.push(resInfo);

// increase the count of selected resources
this.selectedResourcesCount += 1;

// add resource list index to apply selected class style
this.selectedResourceIdxMultiple.push(status.resListIndex);
this.selectedResourceIdxMultiple.push(status.resIndex);
}
} else {
// remove resource from the selected resources list
let index = this.selectedResourcesList.findIndex(d => d === status.resId);
let index = this.selectedResourcesList.findIndex(d => d.id === status.resId);
this.selectedResourcesList.splice(index, 1);

// decrease the count of selected resources
this.selectedResourcesCount -= 1;

// remove resource list index from the selected index list
index = this.selectedResourceIdxMultiple.findIndex(d => d === status.resListIndex);
index = this.selectedResourceIdxMultiple.findIndex(d => d === status.resIndex);
this.selectedResourceIdxMultiple.splice(index, 1);
}
// selectedResourceIdx = selectedResourceIdxMultiple;
return { count: this.selectedResourcesCount, resListIndex: this.selectedResourceIdxMultiple, resIds: this.selectedResourcesList, selectionType: "multiple" };
return { count: this.selectedResourcesCount, resListIndex: this.selectedResourceIdxMultiple, resInfo: this.selectedResourcesList, selectionType: "multiple" };

} else {
// else condition when single resource is clicked for viewing

// unselect checkboxes if any
resChecks.forEach(function (ckb) {
if (ckb.checked) {
ckb.checked = false;
}
});
if (resChecks) {

resChecks.forEach(function (ckb) {
if (ckb.checked) {
ckb.checked = false;
}
});
}

// reset all the variables for multiple selection
this.selectedResourceIdxMultiple = [];
Expand All @@ -63,7 +79,7 @@ export class ListViewService {

// add resource list index to apply selected class style
// selectedResourceIdx = [status.resListIndex];
return { count: 1, resListIndex: [status.resListIndex], resIds: [status.resId], selectionType: "single" };
return { count: 1, resListIndex: [status.resIndex], resInfo: [resInfo], selectionType: "single" };
}
}
}
Expand Up @@ -5,7 +5,7 @@
<mat-card [class.selected-resource]="selectedResourceIdx.indexOf(i) > -1">
<!-- TODO: add the representation preview here, mat-card-image can be used for images -->
<mat-card-header class="grid-card-header">
<mat-card-title-group class="res-class-header-text" (click)="selectResource({ checked: true, resListIndex: i, resId: resource.id, isCheckbox: false})">
<mat-card-title-group class="res-class-header-text" (click)="selectResource({ checked: true, resIndex: i, resId: resource.id, resLabel: resource.label, isCheckbox: false})">
<mat-card-subtitle class="res-class-label">{{ resource.entityInfo.classes[resource.type].label }}</mat-card-subtitle>
<mat-card-title class="res-class-value">{{ resource.label }}</mat-card-title>
</mat-card-title-group>
Expand All @@ -14,13 +14,13 @@
<mat-card-title-group *ngIf="withMultipleSelection" class="res-class-header-actions">
<mat-checkbox #gridCkbox
id="{{ i }}"
(change)="selectResource({ checked: $event.checked, resListIndex: i, resId: resource.id, isCheckbox: true})"
(change)="selectResource({ checked: $event.checked, resIndex: i, resId: resource.id, resLabel: resource.label, isCheckbox: true})"
class="res-checkbox">
</mat-checkbox>
</mat-card-title-group>

</mat-card-header>
<div (click)="selectResource({ checked: true, resListIndex: i, resId: resource.id, isCheckbox: false})">
<div (click)="selectResource({ checked: true, resIndex: i, resId: resource.id, resLabel: resource.label, isCheckbox: false})">
<mat-card-content class="grid-card-content" *ngFor="let prop of resource.properties | keyvalue">
<div *ngFor="let val of prop.value">
<span class="res-prop-label">
Expand Down

0 comments on commit 016e22a

Please sign in to comment.