mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-07-24 17:31:52 +00:00
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:
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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>
|
||||||
|
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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 {
|
||||||
|
36
src/app/store/states/selection.state.ts
Normal file
36
src/app/store/states/selection.state.ts
Normal 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;
|
||||||
|
}
|
Reference in New Issue
Block a user