55946: Multi-list object select support

This commit is contained in:
Kristof De Langhe
2018-11-16 13:55:00 +01:00
parent 08063154e7
commit 8d396f3832
6 changed files with 41 additions and 31 deletions

View File

@@ -15,11 +15,12 @@
</div> </div>
</div> </div>
<ngb-tabset (tabChange)="tabChange($event)"> <ngb-tabset (tabChange)="tabChange($event)" [destroyOnHide]="false">
<ngb-tab title="{{'collection.item-mapper.tabs.browse' | translate}}"> <ngb-tab title="{{'collection.item-mapper.tabs.browse' | translate}}">
<ng-template ngbTabContent> <ng-template ngbTabContent>
<div class="mt-2"> <div class="mt-2">
<ds-item-select class="mt-2" <ds-item-select class="mt-2"
[key]="'browse'"
[dsoRD$]="collectionItemsRD$" [dsoRD$]="collectionItemsRD$"
[paginationOptions]="(searchOptions$ | async)?.pagination" [paginationOptions]="(searchOptions$ | async)?.pagination"
[confirmButton]="'collection.item-mapper.remove'" [confirmButton]="'collection.item-mapper.remove'"
@@ -32,6 +33,7 @@
<ng-template ngbTabContent> <ng-template ngbTabContent>
<div class="mt-2"> <div class="mt-2">
<ds-item-select class="mt-2" <ds-item-select class="mt-2"
[key]="'map'"
[dsoRD$]="mappingItemsRD$" [dsoRD$]="mappingItemsRD$"
[paginationOptions]="(searchOptions$ | async)?.pagination" [paginationOptions]="(searchOptions$ | async)?.pagination"
[confirmButton]="'collection.item-mapper.confirm'" [confirmButton]="'collection.item-mapper.confirm'"

View File

@@ -19,6 +19,7 @@
<ng-template ngbTabContent> <ng-template ngbTabContent>
<div class="mt-2"> <div class="mt-2">
<ds-collection-select class="mt-2" <ds-collection-select class="mt-2"
[key]="'browse'"
[dsoRD$]="itemCollectionsRD$" [dsoRD$]="itemCollectionsRD$"
[paginationOptions]="(searchOptions$ | async)?.pagination" [paginationOptions]="(searchOptions$ | async)?.pagination"
[confirmButton]="'item.edit.item-mapper.buttons.remove'" [confirmButton]="'item.edit.item-mapper.buttons.remove'"
@@ -30,6 +31,7 @@
<ng-template ngbTabContent> <ng-template ngbTabContent>
<div class="mt-2"> <div class="mt-2">
<ds-collection-select class="mt-2" <ds-collection-select class="mt-2"
[key]="'map'"
[dsoRD$]="mappingCollectionsRD$" [dsoRD$]="mappingCollectionsRD$"
[paginationOptions]="(searchOptions$ | async)?.pagination" [paginationOptions]="(searchOptions$ | async)?.pagination"
[confirmButton]="'item.edit.item-mapper.buttons.add'" [confirmButton]="'item.edit.item-mapper.buttons.add'"

View File

@@ -14,7 +14,11 @@ import {
} from './+search-page/search-filters/search-filter/search-filter.reducer'; } from './+search-page/search-filters/search-filter/search-filter.reducer';
import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers'; import { notificationsReducer, NotificationsState } from './shared/notifications/notifications.reducers';
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer'; import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
import { objectSelectionReducer, ObjectSelectionsState } from './shared/object-select/object-select.reducer'; import {
ObjectSelectionListState,
objectSelectionReducer,
ObjectSelectionsState
} from './shared/object-select/object-select.reducer';
export interface AppState { export interface AppState {
router: fromRouter.RouterReducerState; router: fromRouter.RouterReducerState;
@@ -25,7 +29,7 @@ export interface AppState {
searchSidebar: SearchSidebarState; searchSidebar: SearchSidebarState;
searchFilter: SearchFiltersState; searchFilter: SearchFiltersState;
truncatable: TruncatablesState, truncatable: TruncatablesState,
objectSelection: ObjectSelectionsState objectSelection: ObjectSelectionListState
} }
export const appReducers: ActionReducerMap<AppState> = { export const appReducers: ActionReducerMap<AppState> = {

View File

@@ -37,11 +37,11 @@ export function objectSelectionReducer(state = initialState, action: ObjectSelec
case ObjectSelectionActionTypes.INITIAL_SELECT: { case ObjectSelectionActionTypes.INITIAL_SELECT: {
if (isEmpty(state) || isEmpty(state[action.key]) || isEmpty(state[action.key][action.id])) { if (isEmpty(state) || isEmpty(state[action.key]) || isEmpty(state[action.key][action.id])) {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.key]: { [action.key]: Object.assign({}, state[action.key], {
[action.id]: { [action.id]: {
checked: true checked: true
} }
} })
}); });
} }
return state; return state;
@@ -50,11 +50,11 @@ export function objectSelectionReducer(state = initialState, action: ObjectSelec
case ObjectSelectionActionTypes.INITIAL_DESELECT: { case ObjectSelectionActionTypes.INITIAL_DESELECT: {
if (isEmpty(state) || isEmpty(state[action.key]) || isEmpty(state[action.key][action.id])) { if (isEmpty(state) || isEmpty(state[action.key]) || isEmpty(state[action.key][action.id])) {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.key]: { [action.key]: Object.assign({}, state[action.key], {
[action.id]: { [action.id]: {
checked: false checked: false
} }
} })
}); });
} }
return state; return state;
@@ -62,31 +62,31 @@ export function objectSelectionReducer(state = initialState, action: ObjectSelec
case ObjectSelectionActionTypes.SELECT: { case ObjectSelectionActionTypes.SELECT: {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.key]: { [action.key]: Object.assign({}, state[action.key], {
[action.id]: { [action.id]: {
checked: true checked: true
} }
} })
}); });
} }
case ObjectSelectionActionTypes.DESELECT: { case ObjectSelectionActionTypes.DESELECT: {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.key]: { [action.key]: Object.assign({}, state[action.key], {
[action.id]: { [action.id]: {
checked: false checked: false
} }
} })
}); });
} }
case ObjectSelectionActionTypes.SWITCH: { case ObjectSelectionActionTypes.SWITCH: {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.key]: { [action.key]: Object.assign({}, state[action.key], {
[action.id]: { [action.id]: {
checked: (isEmpty(state) || isEmpty(state[action.key]) || isEmpty(state[action.key][action.id])) ? true : !state[action.key][action.id].checked checked: (isEmpty(state) || isEmpty(state[action.key]) || isEmpty(state[action.key][action.id])) ? true : !state[action.key][action.id].checked
} }
} })
}); });
} }

View File

@@ -12,7 +12,6 @@ import { hasValue } from '../empty.util';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { AppState } from '../../app.reducer'; import { AppState } from '../../app.reducer';
const selectionStateSelector = (state: ObjectSelectionsState) => state.objectSelection;
const objectSelectionsStateSelector = (state: ObjectSelectionListState) => state.objectSelection; const objectSelectionsStateSelector = (state: ObjectSelectionListState) => state.objectSelection;
const objectSelectionListStateSelector = (state: AppState) => state.objectSelection; const objectSelectionListStateSelector = (state: AppState) => state.objectSelection;
@@ -23,7 +22,7 @@ const objectSelectionListStateSelector = (state: AppState) => state.objectSelect
export class ObjectSelectService { export class ObjectSelectService {
constructor( constructor(
private store: Store<ObjectSelectionsState>, private store: Store<ObjectSelectionListState>,
private appStore: Store<AppState> private appStore: Store<AppState>
) { ) {
} }
@@ -35,7 +34,7 @@ export class ObjectSelectService {
* @returns {Observable<boolean>} Emits the current selection state of the given object, if it's unavailable, return false * @returns {Observable<boolean>} Emits the current selection state of the given object, if it's unavailable, return false
*/ */
getSelected(key: string, id: string): Observable<boolean> { getSelected(key: string, id: string): Observable<boolean> {
return this.store.select(selectionByIdSelector(id)).pipe( return this.store.select(selectionByKeyAndIdSelector(key, id)).pipe(
map((object: ObjectSelectionState) => { map((object: ObjectSelectionState) => {
if (object) { if (object) {
return object.checked; return object.checked;
@@ -47,12 +46,12 @@ export class ObjectSelectService {
} }
/** /**
* Request the current selection of all objects * Request the current selection of all objects within a specific list
* @returns {Observable<boolean>} Emits the current selection state of all objects * @returns {Observable<boolean>} Emits the current selection state of all objects
*/ */
getAllSelected(): Observable<string[]> { getAllSelected(key: string): Observable<string[]> {
return this.appStore.select(objectSelectionsStateSelector).pipe( return this.appStore.select(objectSelectionListStateSelector).pipe(
map((state: ObjectSelectionsState) => Object.keys(state).filter((key) => state[key].checked)) map((state: ObjectSelectionListState) => Object.keys(state[key]).filter((id) => state[key][id].checked))
); );
} }
@@ -111,14 +110,14 @@ export class ObjectSelectService {
} }
function selectionByIdSelector(id: string): MemoizedSelector<ObjectSelectionsState, ObjectSelectionState> { function selectionByKeyAndIdSelector(key: string, id: string): MemoizedSelector<ObjectSelectionListState, ObjectSelectionState> {
return keySelector<ObjectSelectionState>(id); return keyAndIdSelector<ObjectSelectionState>(key, id);
} }
export function keySelector<T>(key: string): MemoizedSelector<ObjectSelectionsState, T> { export function keyAndIdSelector<T>(key: string, id: string): MemoizedSelector<ObjectSelectionListState, T> {
return createSelector(selectionStateSelector, (state: ObjectSelectionState) => { return createSelector(objectSelectionsStateSelector, (state: ObjectSelectionsState) => {
if (hasValue(state)) { if (hasValue(state) && hasValue(state[key])) {
return state[key]; return state[key][id];
} else { } else {
return undefined; return undefined;
} }

View File

@@ -11,6 +11,9 @@ import { ObjectSelectService } from '../object-select.service';
*/ */
export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestroy { export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestroy {
@Input()
key: string;
/** /**
* The list of DSpaceObjects to display * The list of DSpaceObjects to display
*/ */
@@ -49,11 +52,11 @@ export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestro
} }
ngOnInit(): void { ngOnInit(): void {
this.selectedIds$ = this.objectSelectService.getAllSelected(); this.selectedIds$ = this.objectSelectService.getAllSelected(this.key);
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.objectSelectService.reset(); this.objectSelectService.reset(this.key);
} }
/** /**
@@ -61,7 +64,7 @@ export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestro
* @param {string} id * @param {string} id
*/ */
switch(id: string) { switch(id: string) {
this.objectSelectService.switch(id); this.objectSelectService.switch(this.key, id);
} }
/** /**
@@ -70,7 +73,7 @@ export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestro
* @returns {Observable<boolean>} * @returns {Observable<boolean>}
*/ */
getSelected(id: string): Observable<boolean> { getSelected(id: string): Observable<boolean> {
return this.objectSelectService.getSelected(id); return this.objectSelectService.getSelected(this.key, id);
} }
/** /**
@@ -82,7 +85,7 @@ export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestro
take(1) take(1)
).subscribe((ids: string[]) => { ).subscribe((ids: string[]) => {
this.confirm.emit(ids); this.confirm.emit(ids);
this.objectSelectService.reset(); this.objectSelectService.reset(this.key);
}); });
} }