unified selection state (#433)

* selection state

* use unified selection state

* cleanup tests

* remove "console.log"

* remove old selection property

* remove coma
This commit is contained in:
Denys Vuika
2018-06-19 08:16:53 +01:00
committed by GitHub
parent abd63ba0a4
commit 1a53f8d2aa
13 changed files with 171 additions and 169 deletions

View File

@@ -3,14 +3,14 @@
<adf-breadcrumb root="APP.BROWSE.FAVORITES.TITLE"> <adf-breadcrumb root="APP.BROWSE.FAVORITES.TITLE">
</adf-breadcrumb> </adf-breadcrumb>
<adf-toolbar class="inline" *ngIf="hasSelection"> <adf-toolbar class="inline" *ngIf="!selection.isEmpty">
<button <button
mat-icon-button mat-icon-button
color="primary" color="primary"
*ngIf="selectedFile" *ngIf="selection.file"
title="{{ 'APP.ACTIONS.VIEW' | translate }}" title="{{ 'APP.ACTIONS.VIEW' | translate }}"
(click)="showPreview(selectedFile)"> (click)="showPreview(selection.file)">
<mat-icon>open_in_browser</mat-icon> <mat-icon>open_in_browser</mat-icon>
</button> </button>
@@ -18,16 +18,16 @@
mat-icon-button mat-icon-button
color="primary" color="primary"
title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}" title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}"
[adfNodeDownload]="selectedNodes"> [adfNodeDownload]="selection.nodes">
<mat-icon>get_app</mat-icon> <mat-icon>get_app</mat-icon>
</button> </button>
<button <button
mat-icon-button mat-icon-button
color="primary" color="primary"
*ngIf="selectedFolder" *ngIf="selection.folder"
title="{{ 'APP.ACTIONS.EDIT' | translate }}" title="{{ 'APP.ACTIONS.EDIT' | translate }}"
[acaEditFolder]="selectedFolder"> [acaEditFolder]="selection.folder">
<mat-icon>create</mat-icon> <mat-icon>create</mat-icon>
</button> </button>
@@ -49,39 +49,39 @@
[overlapTrigger]="false"> [overlapTrigger]="false">
<button <button
mat-menu-item mat-menu-item
#selection="adfFavorite" #favorites="adfFavorite"
(toggle)="reload()" (toggle)="reload()"
[adf-node-favorite]="selectedNodes"> [adf-node-favorite]="selection.nodes">
<mat-icon color="primary" *ngIf="selection.hasFavorites()">star</mat-icon> <mat-icon color="primary" *ngIf="favorites.hasFavorites()">star</mat-icon>
<mat-icon *ngIf="!selection.hasFavorites()">star_border</mat-icon> <mat-icon *ngIf="!favorites.hasFavorites()">star_border</mat-icon>
<span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span> <span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
[acaCopyNode]="selectedNodes"> [acaCopyNode]="selection.nodes">
<mat-icon>content_copy</mat-icon> <mat-icon>content_copy</mat-icon>
<span>{{ 'APP.ACTIONS.COPY' | translate }}</span> <span>{{ 'APP.ACTIONS.COPY' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
[acaMoveNode]="selectedNodes"> [acaMoveNode]="selection.nodes">
<mat-icon>library_books</mat-icon> <mat-icon>library_books</mat-icon>
<span>{{ 'APP.ACTIONS.MOVE' | translate }}</span> <span>{{ 'APP.ACTIONS.MOVE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
[acaDeleteNode]="selectedNodes"> [acaDeleteNode]="selection.nodes">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'APP.ACTIONS.DELETE' | translate }}</span> <span>{{ 'APP.ACTIONS.DELETE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="selectedFile" *ngIf="selection.file"
[acaNodeVersions]="selectedFile"> [acaNodeVersions]="selection.file">
<mat-icon>history</mat-icon> <mat-icon>history</mat-icon>
<span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span> <span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span>
</button> </button>
@@ -169,7 +169,7 @@
</div> </div>
<div class="inner-layout__side-panel" *ngIf="infoDrawerOpened"> <div class="inner-layout__side-panel" *ngIf="infoDrawerOpened">
<aca-info-drawer [node]="lastSelectedNode"></aca-info-drawer> <aca-info-drawer [node]="selection.last"></aca-info-drawer>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -6,13 +6,13 @@
(navigate)="onBreadcrumbNavigate($event)"> (navigate)="onBreadcrumbNavigate($event)">
</adf-breadcrumb> </adf-breadcrumb>
<adf-toolbar class="inline" *ngIf="hasSelection"> <adf-toolbar class="inline" *ngIf="!selection.isEmpty">
<button <button
color="primary" color="primary"
mat-icon-button mat-icon-button
*ngIf="selectedFile" *ngIf="selection.file"
title="{{ 'APP.ACTIONS.VIEW' | translate }}" title="{{ 'APP.ACTIONS.VIEW' | translate }}"
(click)="showPreview(selectedFile)"> (click)="showPreview(selection.file)">
<mat-icon>open_in_browser</mat-icon> <mat-icon>open_in_browser</mat-icon>
</button> </button>
@@ -20,16 +20,16 @@
color="primary" color="primary"
mat-icon-button mat-icon-button
title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}" title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}"
[adfNodeDownload]="selectedNodes"> [adfNodeDownload]="selection.nodes">
<mat-icon>get_app</mat-icon> <mat-icon>get_app</mat-icon>
</button> </button>
<button <button
color="primary" color="primary"
mat-icon-button mat-icon-button
*ngIf="selectedFolder && permission.check(selectedFolder, ['update'])" *ngIf="selection.folder && permission.check(selection.folder, ['update'])"
title="{{ 'APP.ACTIONS.EDIT' | translate }}" title="{{ 'APP.ACTIONS.EDIT' | translate }}"
[acaEditFolder]="selectedFolder"> [acaEditFolder]="selection.folder">
<mat-icon>create</mat-icon> <mat-icon>create</mat-icon>
</button> </button>
@@ -52,40 +52,40 @@
[overlapTrigger]="false"> [overlapTrigger]="false">
<button <button
mat-menu-item mat-menu-item
#selection="adfFavorite" #favorites="adfFavorite"
[adf-node-favorite]="selectedNodes"> [adf-node-favorite]="selection.nodes">
<mat-icon color="primary" *ngIf="selection.hasFavorites()">star</mat-icon> <mat-icon color="primary" *ngIf="favorites.hasFavorites()">star</mat-icon>
<mat-icon *ngIf="!selection.hasFavorites()">star_border</mat-icon> <mat-icon *ngIf="!favorites.hasFavorites()">star_border</mat-icon>
<span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span> <span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
[acaCopyNode]="selectedNodes"> [acaCopyNode]="selection.nodes">
<mat-icon>content_copy</mat-icon> <mat-icon>content_copy</mat-icon>
<span>{{ 'APP.ACTIONS.COPY' | translate }}</span> <span>{{ 'APP.ACTIONS.COPY' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selectedNodes, ['delete'])" *ngIf="permission.check(selection.nodes, ['delete'])"
[acaMoveNode]="selectedNodes"> [acaMoveNode]="selection.nodes">
<mat-icon>library_books</mat-icon> <mat-icon>library_books</mat-icon>
<span>{{ 'APP.ACTIONS.MOVE' | translate }}</span> <span>{{ 'APP.ACTIONS.MOVE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selectedNodes, ['delete'])" *ngIf="permission.check(selection.nodes, ['delete'])"
[acaDeleteNode]="selectedNodes"> [acaDeleteNode]="selection.nodes">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'APP.ACTIONS.DELETE' | translate }}</span> <span>{{ 'APP.ACTIONS.DELETE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="selectedFile" *ngIf="selection.file"
[acaNodeVersions]="selectedFile"> [acaNodeVersions]="selection.file">
<mat-icon>history</mat-icon> <mat-icon>history</mat-icon>
<span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span> <span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span>
</button> </button>
@@ -167,7 +167,7 @@
</div> </div>
<div class="inner-layout__side-panel" *ngIf="infoDrawerOpened"> <div class="inner-layout__side-panel" *ngIf="infoDrawerOpened">
<aca-info-drawer [node]="lastSelectedNode"></aca-info-drawer> <aca-info-drawer [node]="selection.last"></aca-info-drawer>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -24,15 +24,10 @@
*/ */
import { PageComponent } from './page.component'; import { PageComponent } from './page.component';
import { MinimalNodeEntity } from 'alfresco-js-api';
class TestClass extends PageComponent { class TestClass extends PageComponent {
node: any; node: any;
setSelection(selection: MinimalNodeEntity[] = []) {
this.onSelectionChanged(selection);
}
constructor() { constructor() {
super(null, null, null); super(null, null, null);
} }
@@ -58,47 +53,4 @@ describe('PageComponent', () => {
expect(component.getParentNodeId()).toBe(null); expect(component.getParentNodeId()).toBe(null);
}); });
}); });
describe('hasSelection()', () => {
it('returns true when it has nodes selected', () => {
component.setSelection([
{ entry: { isFile: true } },
{ entry: { isFile: true } }
]);
expect(component.hasSelection).toBe(true);
});
it('returns false when it has no selections', () => {
component.setSelection([]);
expect(component.hasSelection).toBe(false);
});
});
describe('selectedFile', () => {
it('returns true if selected node is file', () => {
const selection = [ { entry: { isFile: true } } ];
component.setSelection(selection);
expect(component.selectedFile).toBe(selection[0]);
});
it('returns false if selected node is folder', () => {
const selection = [ { entry: { isFile: false, isFolder: true } } ];
component.setSelection(selection);
expect(component.selectedFile).toBeFalsy();
});
});
describe('selectedFolder', () => {
it('returns true if selected node is folder', () => {
const selection = [ { entry: { isFile: false, isFolder: true } } ];
component.setSelection(selection);
expect(component.selectedFolder).toBe(selection[0]);
});
it('returns false if selected node is file', () => {
const selection = [ { entry: { isFile: true, isFolder: false } } ];
component.setSelection(selection);
expect(component.selectedFolder).toBeFalsy();
});
});
}); });

View File

@@ -32,12 +32,13 @@ import { MinimalNodeEntity, MinimalNodeEntryEntity, Pagination } from 'alfresco-
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs/Rx'; import { Subject, Subscription } from 'rxjs/Rx';
import { SnackbarErrorAction, ViewNodeAction, SetSelectedNodesAction } from '../store/actions'; import { SnackbarErrorAction, ViewNodeAction, SetSelectedNodesAction } from '../store/actions';
import { selectedNodes } from '../store/selectors/app.selectors'; import { appSelection } from '../store/selectors/app.selectors';
import { AppStore } from '../store/states/app.state'; import { AppStore } from '../store/states/app.state';
import { SelectionState } from '../store/states/selection.state';
export abstract class PageComponent implements OnInit, OnDestroy { export abstract class PageComponent implements OnInit, OnDestroy {
onDestroy$: Subject<void> = new Subject<void>(); onDestroy$: Subject<boolean> = new Subject<boolean>();
@ViewChild(DocumentListComponent) @ViewChild(DocumentListComponent)
documentList: DocumentListComponent; documentList: DocumentListComponent;
@@ -45,13 +46,7 @@ export abstract class PageComponent implements OnInit, OnDestroy {
title = 'Page'; title = 'Page';
infoDrawerOpened = false; infoDrawerOpened = false;
node: MinimalNodeEntryEntity; node: MinimalNodeEntryEntity;
selection: SelectionState;
selectedFolder: MinimalNodeEntity;
selectedFile: MinimalNodeEntity;
hasSelection = false;
lastSelectedNode: MinimalNodeEntity;
selectedNodes: MinimalNodeEntity[];
protected subscriptions: Subscription[] = []; protected subscriptions: Subscription[] = [];
@@ -70,35 +65,24 @@ export abstract class PageComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.store this.store
.select(selectedNodes) .select(appSelection)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntil(this.onDestroy$))
.subscribe(selection => this.onSelectionChanged(selection)); .subscribe(selection => {
this.selection = selection;
if (selection.isEmpty) {
this.infoDrawerOpened = false;
}
});
} }
ngOnDestroy() { ngOnDestroy() {
this.subscriptions.forEach(subscription => subscription.unsubscribe()); this.subscriptions.forEach(subscription => subscription.unsubscribe());
this.subscriptions = []; this.subscriptions = [];
this.onDestroy$.next(true);
this.onDestroy$.complete(); this.onDestroy$.complete();
} }
// Precalculates all the "static state" flags so that UI does not re-evaluate that on every tick
protected onSelectionChanged(selection: MinimalNodeEntity[] = []) {
this.selectedNodes = selection;
this.hasSelection = selection.length > 0;
this.selectedFolder = null;
this.selectedFile = null;
if (selection.length > 0) {
if (selection.length === 1) {
this.selectedFile = selection.find(entity => entity.entry.isFile);
this.selectedFolder = selection.find(entity => entity.entry.isFolder);
}
} else {
this.lastSelectedNode = null;
this.infoDrawerOpened = false;
}
}
showPreview(node: MinimalNodeEntity) { showPreview(node: MinimalNodeEntity) {
if (node && node.entry) { if (node && node.entry) {
const { id, nodeId, name, isFile, isFolder } = node.entry; const { id, nodeId, name, isFile, isFolder } = node.entry;
@@ -130,7 +114,6 @@ export abstract class PageComponent implements OnInit, OnDestroy {
this.unSelectLockedNodes(documentList); this.unSelectLockedNodes(documentList);
} }
this.lastSelectedNode = event.detail.node;
this.store.dispatch(new SetSelectedNodesAction(documentList.selection)); this.store.dispatch(new SetSelectedNodesAction(documentList.selection));
} }
} }

View File

@@ -35,10 +35,10 @@
<button <button
mat-menu-item mat-menu-item
#selection="adfFavorite" #favorites="adfFavorite"
[adf-node-favorite]="selectedEntities"> [adf-node-favorite]="selectedEntities">
<mat-icon color="primary" *ngIf="selection.hasFavorites()">star</mat-icon> <mat-icon color="primary" *ngIf="favorites.hasFavorites()">star</mat-icon>
<mat-icon *ngIf="!selection.hasFavorites()">star_border</mat-icon> <mat-icon *ngIf="!favorites.hasFavorites()">star_border</mat-icon>
<span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span> <span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span>
</button> </button>

View File

@@ -3,14 +3,14 @@
<adf-breadcrumb root="APP.BROWSE.RECENT.TITLE"> <adf-breadcrumb root="APP.BROWSE.RECENT.TITLE">
</adf-breadcrumb> </adf-breadcrumb>
<adf-toolbar class="inline" *ngIf="hasSelection"> <adf-toolbar class="inline" *ngIf="!selection.isEmpty">
<button <button
mat-icon-button mat-icon-button
color="primary" color="primary"
*ngIf="selectedFile" *ngIf="selection.file"
title="{{ 'APP.ACTIONS.VIEW' | translate }}" title="{{ 'APP.ACTIONS.VIEW' | translate }}"
(click)="showPreview(selectedFile)"> (click)="showPreview(selection.file)">
<mat-icon>open_in_browser</mat-icon> <mat-icon>open_in_browser</mat-icon>
</button> </button>
@@ -18,7 +18,7 @@
mat-icon-button mat-icon-button
color="primary" color="primary"
title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}" title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}"
[adfNodeDownload]="selectedNodes"> [adfNodeDownload]="selection.nodes">
<mat-icon>get_app</mat-icon> <mat-icon>get_app</mat-icon>
</button> </button>
@@ -41,40 +41,40 @@
[overlapTrigger]="false"> [overlapTrigger]="false">
<button <button
mat-menu-item mat-menu-item
#selection="adfFavorite" #favorites="adfFavorite"
[adf-node-favorite]="selectedNodes"> [adf-node-favorite]="selection.nodes">
<mat-icon color="primary" *ngIf="selection.hasFavorites()">star</mat-icon> <mat-icon color="primary" *ngIf="favorites.hasFavorites()">star</mat-icon>
<mat-icon *ngIf="!selection.hasFavorites()">star_border</mat-icon> <mat-icon *ngIf="!favorites.hasFavorites()">star_border</mat-icon>
<span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span> <span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
[acaCopyNode]="selectedNodes"> [acaCopyNode]="selection.nodes">
<mat-icon>content_copy</mat-icon> <mat-icon>content_copy</mat-icon>
<span>{{ 'APP.ACTIONS.COPY' | translate }}</span> <span>{{ 'APP.ACTIONS.COPY' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selectedNodes, ['delete'])" *ngIf="permission.check(selection.nodes, ['delete'])"
[acaMoveNode]="selectedNodes"> [acaMoveNode]="selection.nodes">
<mat-icon>library_books</mat-icon> <mat-icon>library_books</mat-icon>
<span>{{ 'APP.ACTIONS.MOVE' | translate }}</span> <span>{{ 'APP.ACTIONS.MOVE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selectedNodes, ['delete'])" *ngIf="permission.check(selection.nodes, ['delete'])"
[acaDeleteNode]="selectedNodes"> [acaDeleteNode]="selection.nodes">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'APP.ACTIONS.DELETE' | translate }}</span> <span>{{ 'APP.ACTIONS.DELETE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="selectedFile" *ngIf="selection.file"
[acaNodeVersions]="selectedFile"> [acaNodeVersions]="selection.file">
<mat-icon>history</mat-icon> <mat-icon>history</mat-icon>
<span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span> <span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span>
</button> </button>
@@ -157,7 +157,7 @@
</div> </div>
<div class="inner-layout__side-panel" *ngIf="infoDrawerOpened"> <div class="inner-layout__side-panel" *ngIf="infoDrawerOpened">
<aca-info-drawer [node]="lastSelectedNode"></aca-info-drawer> <aca-info-drawer [node]="selection.last"></aca-info-drawer>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -2,13 +2,13 @@
<div class="inner-layout__header"> <div class="inner-layout__header">
<adf-breadcrumb root="APP.BROWSE.SEARCH.TITLE"> <adf-breadcrumb root="APP.BROWSE.SEARCH.TITLE">
</adf-breadcrumb> </adf-breadcrumb>
<adf-toolbar class="inline" *ngIf="hasSelection"> <adf-toolbar class="inline" *ngIf="!selection.isEmpty">
<button <button
color="primary" color="primary"
mat-icon-button mat-icon-button
*ngIf="selectedFile" *ngIf="selection.file"
title="{{ 'APP.ACTIONS.VIEW' | translate }}" title="{{ 'APP.ACTIONS.VIEW' | translate }}"
(click)="showPreview(selectedFile)"> (click)="showPreview(selection.file)">
<mat-icon>open_in_browser</mat-icon> <mat-icon>open_in_browser</mat-icon>
</button> </button>
@@ -16,7 +16,7 @@
color="primary" color="primary"
mat-icon-button mat-icon-button
title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}" title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}"
[acaDownloadNodes]="selectedNodes"> [acaDownloadNodes]="selection.nodes">
<mat-icon>get_app</mat-icon> <mat-icon>get_app</mat-icon>
</button> </button>
@@ -38,24 +38,24 @@
<mat-menu #actionsMenu="matMenu" [overlapTrigger]="false"> <mat-menu #actionsMenu="matMenu" [overlapTrigger]="false">
<button <button
mat-menu-item mat-menu-item
#selection="adfFavorite" #favorites="adfFavorite"
[adf-node-favorite]="selectedNodes"> [adf-node-favorite]="selection.nodes">
<mat-icon color="primary" *ngIf="selection.hasFavorites()">star</mat-icon> <mat-icon color="primary" *ngIf="favorites.hasFavorites()">star</mat-icon>
<mat-icon *ngIf="!selection.hasFavorites()">star_border</mat-icon> <mat-icon *ngIf="!favorites.hasFavorites()">star_border</mat-icon>
<span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span> <span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
[acaCopyNode]="selectedNodes"> [acaCopyNode]="selection.nodes">
<mat-icon>content_copy</mat-icon> <mat-icon>content_copy</mat-icon>
<span>{{ 'APP.ACTIONS.COPY' | translate }}</span> <span>{{ 'APP.ACTIONS.COPY' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="selectedFile" *ngIf="selection.file"
[acaNodeVersions]="selectedFile"> [acaNodeVersions]="selection.file">
<mat-icon>history</mat-icon> <mat-icon>history</mat-icon>
<span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span> <span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span>
</button> </button>
@@ -128,7 +128,7 @@
</div> </div>
</div> </div>
<div class="inner-layout__side-panel" *ngIf="infoDrawerOpened"> <div class="inner-layout__side-panel" *ngIf="infoDrawerOpened">
<aca-info-drawer [node]="lastSelectedNode"></aca-info-drawer> <aca-info-drawer [node]="selection.last"></aca-info-drawer>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -3,13 +3,13 @@
<adf-breadcrumb root="APP.BROWSE.SHARED.TITLE"> <adf-breadcrumb root="APP.BROWSE.SHARED.TITLE">
</adf-breadcrumb> </adf-breadcrumb>
<adf-toolbar class="inline" *ngIf="hasSelection"> <adf-toolbar class="inline" *ngIf="!selection.isEmpty">
<button <button
*ngIf="selectedNodes.length === 1" *ngIf="selection.count === 1"
color="primary" color="primary"
mat-icon-button mat-icon-button
title="{{ 'APP.ACTIONS.VIEW' | translate }}" title="{{ 'APP.ACTIONS.VIEW' | translate }}"
(click)="showPreview(selectedNodes[0])"> (click)="showPreview(selection.nodes[0])">
<mat-icon>open_in_browser</mat-icon> <mat-icon>open_in_browser</mat-icon>
</button> </button>
@@ -17,7 +17,7 @@
color="primary" color="primary"
mat-icon-button mat-icon-button
title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}" title="{{ 'APP.ACTIONS.DOWNLOAD' | translate }}"
[adfNodeDownload]="selectedNodes"> [adfNodeDownload]="selection.nodes">
<mat-icon>get_app</mat-icon> <mat-icon>get_app</mat-icon>
</button> </button>
@@ -40,32 +40,32 @@
[overlapTrigger]="false"> [overlapTrigger]="false">
<button <button
mat-menu-item mat-menu-item
#selection="adfFavorite" #favorites="adfFavorite"
[adf-node-favorite]="selectedNodes"> [adf-node-favorite]="selection.nodes">
<mat-icon color="primary" *ngIf="selection.hasFavorites()">star</mat-icon> <mat-icon color="primary" *ngIf="favorites.hasFavorites()">star</mat-icon>
<mat-icon *ngIf="!selection.hasFavorites()">star_border</mat-icon> <mat-icon *ngIf="!favorites.hasFavorites()">star_border</mat-icon>
<span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span> <span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
[acaCopyNode]="selectedNodes"> [acaCopyNode]="selection.nodes">
<mat-icon>content_copy</mat-icon> <mat-icon>content_copy</mat-icon>
<span>{{ 'APP.ACTIONS.COPY' | translate }}</span> <span>{{ 'APP.ACTIONS.COPY' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selectedNodes, ['delete'], { target: 'allowableOperationsOnTarget' })" *ngIf="permission.check(selection.nodes, ['delete'], { target: 'allowableOperationsOnTarget' })"
[acaMoveNode]="selectedNodes"> [acaMoveNode]="selection.nodes">
<mat-icon>library_books</mat-icon> <mat-icon>library_books</mat-icon>
<span>{{ 'APP.ACTIONS.MOVE' | translate }}</span> <span>{{ 'APP.ACTIONS.MOVE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selectedNodes, ['delete'])" *ngIf="permission.check(selection.nodes, ['delete'])"
[acaUnshareNode]="selectedNodes" [acaUnshareNode]="selection.nodes"
(links-unshared)="reload()"> (links-unshared)="reload()">
<mat-icon>stop_screen_share</mat-icon> <mat-icon>stop_screen_share</mat-icon>
<span>{{ 'APP.ACTIONS.UNSHARE' | translate }}</span> <span>{{ 'APP.ACTIONS.UNSHARE' | translate }}</span>
@@ -73,16 +73,16 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selectedNodes, ['delete'], { target: 'allowableOperationsOnTarget' })" *ngIf="permission.check(selection.nodes, ['delete'], { target: 'allowableOperationsOnTarget' })"
[acaDeleteNode]="selectedNodes"> [acaDeleteNode]="selection.nodes">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'APP.ACTIONS.DELETE' | translate }}</span> <span>{{ 'APP.ACTIONS.DELETE' | translate }}</span>
</button> </button>
<button <button
mat-menu-item mat-menu-item
*ngIf="selectedNodes.length === 1 && permission.check(selectedNodes, ['update'], { target: 'allowableOperationsOnTarget' })" *ngIf="selection.count === 1 && permission.check(selection.nodes, ['update'], { target: 'allowableOperationsOnTarget' })"
[acaNodeVersions]="selectedNodes"> [acaNodeVersions]="selection.nodes">
<mat-icon>history</mat-icon> <mat-icon>history</mat-icon>
<span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span> <span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span>
</button> </button>
@@ -175,7 +175,7 @@
</div> </div>
<div class="inner-layout__side-panel" *ngIf="infoDrawerOpened"> <div class="inner-layout__side-panel" *ngIf="infoDrawerOpened">
<aca-info-drawer [node]="lastSelectedNode"></aca-info-drawer> <aca-info-drawer [node]="selection.last"></aca-info-drawer>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -3,11 +3,11 @@
<adf-breadcrumb root="APP.BROWSE.TRASHCAN.TITLE"> <adf-breadcrumb root="APP.BROWSE.TRASHCAN.TITLE">
</adf-breadcrumb> </adf-breadcrumb>
<adf-toolbar class="inline" *ngIf="hasSelection"> <adf-toolbar class="inline" *ngIf="!selection.isEmpty">
<button <button
color="primary" color="primary"
mat-icon-button mat-icon-button
[acaPermanentDelete]="selectedNodes" [acaPermanentDelete]="selection.nodes"
title="{{ 'APP.ACTIONS.DELETE_PERMANENT' | translate }}"> title="{{ 'APP.ACTIONS.DELETE_PERMANENT' | translate }}">
<mat-icon>delete_forever</mat-icon> <mat-icon>delete_forever</mat-icon>
</button> </button>
@@ -15,7 +15,7 @@
<button <button
color="primary" color="primary"
mat-icon-button mat-icon-button
[acaRestoreNode]="selectedNodes" [acaRestoreNode]="selection.nodes"
title="{{ 'APP.ACTIONS.RESTORE' | translate }}"> title="{{ 'APP.ACTIONS.RESTORE' | translate }}">
<mat-icon>restore</mat-icon> <mat-icon>restore</mat-icon>
</button> </button>

View File

@@ -90,6 +90,33 @@ function updateSelectedNodes(
action: SetSelectedNodesAction action: SetSelectedNodesAction
): AppState { ): AppState {
const newState = Object.assign({}, state); const newState = Object.assign({}, state);
newState.selectedNodes = [...action.payload]; const nodes = [...action.payload];
const count = nodes.length;
const isEmpty = nodes.length === 0;
let first = null;
let last = null;
let file = null;
let folder = null;
if (nodes.length > 0) {
first = nodes[0];
last = nodes[nodes.length - 1];
if (nodes.length === 1) {
file = nodes.find(entity => entity.entry.isFile);
folder = nodes.find(entity => entity.entry.isFolder);
}
}
newState.selection = {
count,
nodes,
isEmpty,
first,
last,
file,
folder
};
return newState; return newState;
} }

View File

@@ -30,4 +30,4 @@ export const selectApp = (state: AppStore) => state.app;
export const selectHeaderColor = createSelector(selectApp, (state: AppState) => state.headerColor); export const selectHeaderColor = createSelector(selectApp, (state: AppState) => state.headerColor);
export const selectAppName = createSelector(selectApp, (state: AppState) => state.appName); export const selectAppName = createSelector(selectApp, (state: AppState) => state.appName);
export const selectLogoPath = createSelector(selectApp, (state: AppState) => state.logoPath); export const selectLogoPath = createSelector(selectApp, (state: AppState) => state.logoPath);
export const selectedNodes = createSelector(selectApp, (state: AppState) => state.selectedNodes); export const appSelection = createSelector(selectApp, (state: AppState) => state.selection);

View File

@@ -23,20 +23,24 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { MinimalNodeEntity } from 'alfresco-js-api'; import { SelectionState } from './selection.state';
export interface AppState { export interface AppState {
appName: string; appName: string;
headerColor: string; headerColor: string;
logoPath: string; logoPath: string;
selectedNodes: MinimalNodeEntity[]; selection: SelectionState;
} }
export const INITIAL_APP_STATE: AppState = { export const INITIAL_APP_STATE: AppState = {
appName: 'Alfresco Example Content Application', appName: 'Alfresco Example Content Application',
headerColor: '#2196F3', headerColor: '#2196F3',
logoPath: 'assets/images/alfresco-logo-white.svg', logoPath: 'assets/images/alfresco-logo-white.svg',
selectedNodes: [] selection: {
nodes: [],
isEmpty: true,
count: 0
}
}; };
export interface AppStore { export interface AppStore {

View File

@@ -0,0 +1,36 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { MinimalNodeEntity } from 'alfresco-js-api';
export interface SelectionState {
count: number;
nodes: MinimalNodeEntity[];
isEmpty: boolean;
first?: MinimalNodeEntity;
last?: MinimalNodeEntity;
folder?: MinimalNodeEntity;
file?: MinimalNodeEntity;
}