Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(viewer): return label and id in multiple resource selection (DSP-1842 / DSP-1820) #327

Merged
merged 6 commits into from Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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