mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
improved uploading of files (#1730)
* improved uploading of files - new core/UploadDirective to allow dropping files to any html element - enhanced file dropping for DataTable rows (disabled by default) - enhanced file dropping for DocumentList rows (disabled by default) - upload drop area now handles file uploads for child elements (i.e. rows in the document list) * fix unit tests * unit tests and code cleanup * #1732, fix upload of folders
This commit is contained in:
committed by
Mario Romano
parent
f3de023ab3
commit
3fee3b5002
@@ -43,26 +43,134 @@ necessary configuration, see this [page](https://github.com/Alfresco/alfresco-ng
|
||||
npm install --save ng2-alfresco-core
|
||||
```
|
||||
|
||||
## Main components and services
|
||||
## Library content
|
||||
|
||||
### Components
|
||||
- Components
|
||||
- Context Menu directive
|
||||
- Material Design directives
|
||||
- [mdl]
|
||||
- [alfresco-mdl-button]
|
||||
- [alfresco-mdl-menu]
|
||||
- [alfresco-mdl-tabs]
|
||||
- Directives
|
||||
- UploadDirective
|
||||
- Services
|
||||
- **LogService**, log service implementation
|
||||
- **NotificationService**, Notification service implementation
|
||||
- **AlfrescoApiService**, provides access to Alfresco JS API instance
|
||||
- **AlfrescoAuthenticationService**, main authentication APIs
|
||||
- **AlfrescoTranslationService**, various i18n-related APIs
|
||||
- **ContextMenuService**, global context menu APIs
|
||||
|
||||
- Context Menu directive
|
||||
- Material Design directives
|
||||
- [mdl]
|
||||
- [alfresco-mdl-button]
|
||||
- [alfresco-mdl-menu]
|
||||
- [alfresco-mdl-tabs]
|
||||
|
||||
### Services
|
||||
## UploadDirective
|
||||
|
||||
- **LogService**, log service implementation
|
||||
- **NotificationService**, Notification service implementation
|
||||
- **AlfrescoApiService**, provides access to Alfresco JS API instance
|
||||
- **AlfrescoAuthenticationService**, main authentication APIs
|
||||
- **AlfrescoTranslationService**, various i18n-related APIs
|
||||
- **ContextMenuService**, global context menu APIs
|
||||
Allows your components or common HTML elements reacting on File drag and drop in order to upload content.
|
||||
Used by attaching to an element or component.
|
||||
|
||||
### Basic usage
|
||||
|
||||
The directive itself does not do any file management process,
|
||||
but collects information on dropped files and raises corresponding events instead.
|
||||
|
||||
```html
|
||||
<div style="width:100px; height:100px"
|
||||
[adf-upload]="true"
|
||||
[adf-upload-data]="{ some: 'data' }">
|
||||
Drop files here...
|
||||
</div>
|
||||
```
|
||||
|
||||
It is possible controlling when upload behaviour is enabled/disabled by binding directive to a `boolean` value or expression:
|
||||
|
||||
```html
|
||||
<div [adf-upload]="true">...</div>
|
||||
<div [adf-upload]="allowUpload">...</div>
|
||||
<div [adf-upload]="isUploadEnabled()">...</div>
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
Once a single or multiple files are dropped on the decorated element the `upload-files` [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) is raised.
|
||||
The DOM event is configured to have `bubbling` enabled, so any component up the component tree can handle, process or prevent it:
|
||||
|
||||
```html
|
||||
<div (upload-files)="onUploadFiles($event)">
|
||||
<div [adf-upload]="true"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```ts
|
||||
onUploadFiles(e: CustomEvent) {
|
||||
console.log(e.detail.files);
|
||||
|
||||
// your code
|
||||
}
|
||||
```
|
||||
|
||||
Please note that event will be raised only if valid [Files](https://developer.mozilla.org/en-US/docs/Web/API/File) were dropped onto the decorated element.
|
||||
|
||||
The `upload-files` event is cancellable, so you can stop propagation of the drop event to uppper levels in case it has been already handled by your code:
|
||||
|
||||
```ts
|
||||
onUploadFiles(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// your code
|
||||
}
|
||||
```
|
||||
|
||||
It is also possible attaching arbitrary data to each event in order to access it from within external event handlers.
|
||||
A typical scenario is data tables where you may want to handle also the data row and/or underlying data to be accessible upon files drop.
|
||||
|
||||
You may be using `adf-upload-data` to bind custom values or objects for every event raised:
|
||||
|
||||
```html
|
||||
<div [adf-upload]="true" [adf-upload-data]="dataRow"></div>
|
||||
<div [adf-upload]="true" [adf-upload-data]="'string value'"></div>
|
||||
<div [adf-upload]="true" [adf-upload-data]="{ name: 'custom object' }"></div>
|
||||
<div [adf-upload]="true" [adf-upload-data]="getUploadData()"></div>
|
||||
```
|
||||
|
||||
As part of the `details` property of the [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) you can get access to the following:
|
||||
|
||||
```ts
|
||||
detail: {
|
||||
sender: UploadDirective, // directive that raised given event
|
||||
data: any, // arbitrary data associated (bound)
|
||||
files: File[] // dropped files
|
||||
}
|
||||
```
|
||||
|
||||
### Styling
|
||||
|
||||
The decorated element gets `adf-upload__dragging` CSS class name in the class list every time files are dragged over it.
|
||||
This allows changing look and feel of your components in case additional visual indication is required,
|
||||
for example you may want drawing a dashed border around the table row on drag:
|
||||
|
||||
```html
|
||||
<table>
|
||||
<tr [adf-upload]="true">
|
||||
...
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
```css
|
||||
.adf-upload__dragging > td:first-child {
|
||||
border-left: 1px dashed rgb(68,138,255);
|
||||
}
|
||||
|
||||
.adf-upload__dragging > td {
|
||||
border-top: 1px dashed rgb(68,138,255);
|
||||
border-bottom: 1px dashed rgb(68,138,255);
|
||||
}
|
||||
|
||||
.adf-upload__dragging > td:last-child {
|
||||
border-right: 1px dashed rgb(68,138,255);
|
||||
}
|
||||
```
|
||||
|
||||
## Alfresco Api Service
|
||||
|
||||
@@ -78,7 +186,7 @@ export class MyComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
let nodeId = 'some-node-id';
|
||||
let params = {};
|
||||
this.getAlfrescoApi().nodes
|
||||
this.apiService.getInstance().nodes
|
||||
.getNodeChildren(nodeId, params)
|
||||
.then(result => console.log(result));
|
||||
}
|
||||
@@ -92,8 +200,8 @@ In case of any TypeScript type check errors you can still call any supported
|
||||
Alfresco JS api by casting the instance to `any` type like the following:_
|
||||
|
||||
```ts
|
||||
let apiService: any = this.authService.getAlfrescoApi();
|
||||
apiService.nodes.addNode('-root-', body, {});
|
||||
let api: any = this.apiService.getInstance();
|
||||
api.nodes.addNode('-root-', body, {});
|
||||
```
|
||||
|
||||
## Notification Service
|
||||
|
@@ -39,6 +39,7 @@ import {
|
||||
NotificationService
|
||||
} from './src/services/index';
|
||||
|
||||
import { UploadDirective } from './src/directives/upload.directive';
|
||||
import { DataColumnComponent } from './src/components/data-column/data-column.component';
|
||||
import { DataColumnListComponent } from './src/components/data-column/data-column-list.component';
|
||||
import { MATERIAL_DESIGN_DIRECTIVES } from './src/components/material/index';
|
||||
@@ -48,6 +49,7 @@ export * from './src/services/index';
|
||||
export * from './src/components/index';
|
||||
export * from './src/components/data-column/data-column.component';
|
||||
export * from './src/components/data-column/data-column-list.component';
|
||||
export * from './src/directives/upload.directive';
|
||||
export * from './src/utils/index';
|
||||
export * from './src/events/base.event';
|
||||
export * from './src/events/base-ui.event';
|
||||
@@ -90,6 +92,7 @@ export function createTranslateLoader(http: Http, logService: LogService) {
|
||||
declarations: [
|
||||
...MATERIAL_DESIGN_DIRECTIVES,
|
||||
...CONTEXT_MENU_DIRECTIVES,
|
||||
UploadDirective,
|
||||
DataColumnComponent,
|
||||
DataColumnListComponent
|
||||
],
|
||||
@@ -105,6 +108,7 @@ export function createTranslateLoader(http: Http, logService: LogService) {
|
||||
TranslateModule,
|
||||
...MATERIAL_DESIGN_DIRECTIVES,
|
||||
...CONTEXT_MENU_DIRECTIVES,
|
||||
UploadDirective,
|
||||
DataColumnComponent,
|
||||
DataColumnListComponent
|
||||
]
|
||||
|
@@ -0,0 +1,136 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ElementRef } from '@angular/core';
|
||||
import { UploadDirective } from './upload.directive';
|
||||
|
||||
describe('UploadDirective', () => {
|
||||
|
||||
let directive: UploadDirective;
|
||||
let nativeElement: any;
|
||||
|
||||
beforeEach(() => {
|
||||
nativeElement = {
|
||||
dispatchEvent: () => {}
|
||||
};
|
||||
directive = new UploadDirective(new ElementRef(nativeElement));
|
||||
});
|
||||
|
||||
it('should be enabled by default', () => {
|
||||
expect(directive.enabled).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have debug mode switched off by default', () => {
|
||||
expect(directive.debug).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should update drag status on dragenter', () => {
|
||||
expect(directive.isDragging).toBeFalsy();
|
||||
directive.enabled = true;
|
||||
directive.onDragEnter();
|
||||
expect(directive.isDragging).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not update drag status on dragenter when disabled', () => {
|
||||
expect(directive.isDragging).toBeFalsy();
|
||||
directive.enabled = false;
|
||||
directive.onDragEnter();
|
||||
expect(directive.isDragging).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should update drag status on dragover', () => {
|
||||
expect(directive.isDragging).toBeFalsy();
|
||||
directive.enabled = true;
|
||||
directive.onDragOver(null);
|
||||
expect(directive.isDragging).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should prevent default event on dragover', () => {
|
||||
let event = new Event('dom-event');
|
||||
spyOn(event, 'preventDefault').and.stub();
|
||||
directive.enabled = true;
|
||||
directive.onDragOver(event);
|
||||
expect(event.preventDefault).toHaveBeenCalled();
|
||||
expect(directive.isDragging).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not update drag status on dragover when disabled', () => {
|
||||
expect(directive.isDragging).toBeFalsy();
|
||||
directive.enabled = false;
|
||||
directive.onDragOver(null);
|
||||
});
|
||||
|
||||
it('should update drag status on dragleave', () => {
|
||||
directive.enabled = true;
|
||||
directive.isDragging = true;
|
||||
directive.onDragLeave();
|
||||
expect(directive.isDragging).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not update drag status on dragleave when disabled', () => {
|
||||
directive.enabled = false;
|
||||
directive.isDragging = true;
|
||||
directive.onDragLeave();
|
||||
expect(directive.isDragging).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should prevent default event on drop', () => {
|
||||
directive.enabled = true;
|
||||
let event = jasmine.createSpyObj('event', ['preventDefault', 'stopPropagation']);
|
||||
directive.onDrop(<DragEvent>event);
|
||||
expect(event.preventDefault).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should stop default event propagation on drop', () => {
|
||||
directive.enabled = true;
|
||||
let event = jasmine.createSpyObj('event', ['preventDefault', 'stopPropagation']);
|
||||
directive.onDrop(<DragEvent>event);
|
||||
expect(event.stopPropagation).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not prevent default event on drop when disabled', () => {
|
||||
directive.enabled = false;
|
||||
let event = jasmine.createSpyObj('event', ['preventDefault', 'stopPropagation']);
|
||||
directive.onDrop(<DragEvent>event);
|
||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should raise upload-files event on files drop', () => {
|
||||
directive.enabled = true;
|
||||
let files = [<File> {}];
|
||||
let event = jasmine.createSpyObj('event', ['preventDefault', 'stopPropagation']);
|
||||
spyOn(directive, 'getFilesDropped').and.returnValue(files);
|
||||
spyOn(nativeElement, 'dispatchEvent').and.stub();
|
||||
directive.onDrop(event);
|
||||
expect(nativeElement.dispatchEvent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should provide dropped files in upload-files event', () => {
|
||||
directive.enabled = true;
|
||||
let files = [<File> {}];
|
||||
let event = jasmine.createSpyObj('event', ['preventDefault', 'stopPropagation']);
|
||||
spyOn(directive, 'getFilesDropped').and.returnValue(files);
|
||||
|
||||
spyOn(nativeElement, 'dispatchEvent').and.callFake(e => {
|
||||
expect(e.detail.files.length).toBe(1);
|
||||
expect(e.detail.files[0]).toBe(files[0]);
|
||||
});
|
||||
directive.onDrop(event);
|
||||
expect(nativeElement.dispatchEvent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,113 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Directive, Input, HostBinding, HostListener, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[adf-upload]'
|
||||
})
|
||||
export class UploadDirective {
|
||||
|
||||
@Input('adf-upload')
|
||||
enabled: boolean = true;
|
||||
|
||||
@Input('adf-upload-data')
|
||||
data: any;
|
||||
|
||||
@Input()
|
||||
debug: boolean = false;
|
||||
|
||||
@HostBinding('class.adf-upload__dragging')
|
||||
isDragging: boolean;
|
||||
|
||||
constructor(private el: ElementRef) {
|
||||
}
|
||||
|
||||
@HostListener('dragenter')
|
||||
onDragEnter() {
|
||||
if (this.enabled) {
|
||||
this.isDragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('dragover', ['$event'])
|
||||
onDragOver(event: Event) {
|
||||
if (this.enabled) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.isDragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('dragleave')
|
||||
onDragLeave() {
|
||||
if (this.enabled) {
|
||||
this.isDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('drop', ['$event'])
|
||||
onDrop(event: DragEvent) {
|
||||
if (this.enabled) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.isDragging = false;
|
||||
|
||||
let files = this.getFilesDropped(event.dataTransfer);
|
||||
if (files.length > 0) {
|
||||
let e = new CustomEvent('upload-files', {
|
||||
detail: {
|
||||
sender: this,
|
||||
data: this.data,
|
||||
files: files
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
|
||||
this.el.nativeElement.dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract files from the DataTransfer object used to hold the data that is being dragged during a drag and drop operation.
|
||||
* @param dataTransfer DataTransfer object
|
||||
*/
|
||||
protected getFilesDropped(dataTransfer: DataTransfer): File[] {
|
||||
let result: File[] = [];
|
||||
|
||||
if (dataTransfer) {
|
||||
let items: DataTransferItemList = dataTransfer.items;
|
||||
|
||||
if (items && items.length > 0) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item: DataTransferItem = items[i];
|
||||
if (item.type) {
|
||||
let file = item.getAsFile();
|
||||
if (file) {
|
||||
result.push(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -184,6 +184,7 @@ You can also use HTML-based schema declaration like shown below:
|
||||
| `actionsPosition` | string (left\|right) | right | Position of the actions dropdown menu. |
|
||||
| `fallbackThumbnail` | string | | Fallback image for row ehre thubnail is missing|
|
||||
| `contextMenu` | boolean | false | Toggles custom context menu for the component |
|
||||
| `allowDropFiles` | boolean | false | Toggle file drop support for rows (see **ng2-alfresco-core/UploadDirective** for more details) |
|
||||
|
||||
### DataColumn Properties
|
||||
|
||||
|
@@ -122,3 +122,16 @@
|
||||
.alfresco-datatable__row--selected {
|
||||
color: rgb(68,138,255);
|
||||
}
|
||||
|
||||
.adf-upload__dragging > td {
|
||||
border-top: 1px dashed rgb(68,138,255);
|
||||
border-bottom: 1px dashed rgb(68,138,255);
|
||||
}
|
||||
|
||||
.adf-upload__dragging > td:first-child {
|
||||
border-left: 1px dashed rgb(68,138,255);
|
||||
}
|
||||
|
||||
.adf-upload__dragging > td:last-child {
|
||||
border-right: 1px dashed rgb(68,138,255);
|
||||
}
|
||||
|
@@ -37,7 +37,8 @@
|
||||
|
||||
<tr *ngFor="let row of data.getRows(); let idx = index" tabindex="0"
|
||||
class="alfresco-datatable__row"
|
||||
[class.alfresco-datatable__row--selected]="selectedRow === row">
|
||||
[class.alfresco-datatable__row--selected]="selectedRow === row"
|
||||
[adf-upload]="allowDropFiles" [adf-upload-data]="row">
|
||||
|
||||
<!-- Actions (right) -->
|
||||
<td *ngIf="actions && actionsPosition === 'left'" class="alfresco-datatable__actions-cell">
|
||||
|
@@ -51,6 +51,9 @@ export class DataTableComponent implements AfterContentInit {
|
||||
@Input()
|
||||
contextMenu: boolean = false;
|
||||
|
||||
@Input()
|
||||
allowDropFiles: boolean = false;
|
||||
|
||||
@Output()
|
||||
rowClick: EventEmitter<DataRowEvent> = new EventEmitter<DataRowEvent>();
|
||||
|
||||
|
@@ -184,9 +184,10 @@ The properties currentFolderId, folderNode and node are the entry initialization
|
||||
| `contentActionsPosition` | string (left\|right) | right | Position of the content actions dropdown menu. |
|
||||
| `contextMenuActions` | boolean | false | Toggles context menus for each row |
|
||||
| `enablePagination` | boolean | true | Shows pagination |
|
||||
| `creationMenuActions` | boolean | true | Toggles the creation menu actions|
|
||||
| `rowFilter` | `RowFilter` | | Custom row filter, [see more](#custom-row-filter).
|
||||
| `imageResolver` | `ImageResolver` | | Custom image resolver, [see more](#custom-image-resolver).
|
||||
| `creationMenuActions` | boolean | true | Toggles the creation menu actions |
|
||||
| `rowFilter` | `RowFilter` | | Custom row filter, [see more](#custom-row-filter). |
|
||||
| `imageResolver` | `ImageResolver` | | Custom image resolver, [see more](#custom-image-resolver). |
|
||||
| `allowDropFiles` | boolean | false | Toggle file drop support for rows (see **ng2-alfresco-core/UploadDirective** for more details) |
|
||||
|
||||
### Events
|
||||
|
||||
|
@@ -10,6 +10,7 @@
|
||||
[actionsPosition]="contentActionsPosition"
|
||||
[multiselect]="multiselect"
|
||||
[fallbackThumbnail]="fallbackThumbnail"
|
||||
[allowDropFiles]="allowDropFiles"
|
||||
[contextMenu]="contextMenuActions"
|
||||
(showRowContextMenu)="onShowRowContextMenu($event)"
|
||||
(showRowActionsMenu)="onShowRowActionsMenu($event)"
|
||||
|
@@ -82,6 +82,9 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
|
||||
@Input()
|
||||
emptyFolderImageUrl: string = this.baseComponentPath + 'assets/images/empty_doc_lib.svg';
|
||||
|
||||
@Input()
|
||||
allowDropFiles: boolean = false;
|
||||
|
||||
skipCount: number = 0;
|
||||
|
||||
pagination: Pagination;
|
||||
|
@@ -128,7 +128,7 @@ export class UploadButtonComponent {
|
||||
let directoryName = this.getDirectoryName(directoryPath);
|
||||
let absolutePath = this.currentFolderPath + this.getDirectoryPath(directoryPath);
|
||||
|
||||
this.uploadService.createFolder(absolutePath, directoryName)
|
||||
this.uploadService.createFolder(absolutePath, directoryName, this.rootFolderId)
|
||||
.subscribe(
|
||||
res => {
|
||||
let relativeDir = this.currentFolderPath + '/' + directoryPath;
|
||||
|
@@ -4,8 +4,7 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.input-focus {
|
||||
.file-draggable__input-focus {
|
||||
color: #2196F3;
|
||||
margin-left: 3px;
|
||||
border: 3px dashed #2196F3;
|
||||
border: 1px dashed #2196F3;
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@
|
||||
(onFilesDropped)="onFilesDropped($event)"
|
||||
(onFilesEntityDropped)="onFilesEntityDropped($event)"
|
||||
(onFolderEntityDropped)="onFolderEntityDropped($event)"
|
||||
(upload-files)="onUploadFiles($event)"
|
||||
dropzone="" webkitdropzone="*" #droparea>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
@@ -72,16 +72,35 @@ export class UploadDragAreaComponent {
|
||||
this.uploadService.setOptions(formFields, this.versioning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles 'upload-files' events raised by child components.
|
||||
* @param e DOM event
|
||||
*/
|
||||
onUploadFiles(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
let files = e.detail.files;
|
||||
if (files && files.length > 0) {
|
||||
if (e.detail.data.obj.entry.isFolder) {
|
||||
let id = e.detail.data.obj.entry.id;
|
||||
this.onFilesDropped(files, id, '/');
|
||||
} else {
|
||||
this.onFilesDropped(files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when files are dropped in the drag area.
|
||||
*
|
||||
* @param {File[]} files - files dropped in the drag area.
|
||||
*/
|
||||
onFilesDropped(files: File[]): void {
|
||||
onFilesDropped(files: File[], rootId?: string, directory?: string): void {
|
||||
if (files.length) {
|
||||
if (this.checkValidity(files)) {
|
||||
this.uploadService.addToQueue(files);
|
||||
this.uploadService.uploadFilesInTheQueue(this.rootFolderId, this.currentFolderPath, this.onSuccess);
|
||||
this.uploadService.uploadFilesInTheQueue(rootId || this.rootFolderId, directory || this.currentFolderPath, this.onSuccess);
|
||||
let latestFilesAdded = this.uploadService.getQueue();
|
||||
if (this.showNotificationBar) {
|
||||
this.showUndoNotificationBar(latestFilesAdded);
|
||||
@@ -132,7 +151,7 @@ export class UploadDragAreaComponent {
|
||||
let relativePath = folder.fullPath.replace(folder.name, '');
|
||||
relativePath = this.currentFolderPath + relativePath;
|
||||
|
||||
this.uploadService.createFolder(relativePath, folder.name)
|
||||
this.uploadService.createFolder(relativePath, folder.name, this.rootFolderId)
|
||||
.subscribe(
|
||||
message => {
|
||||
this.onSuccess.emit({
|
||||
|
@@ -19,7 +19,7 @@ import { FileDraggableDirective } from '../directives/file-draggable.directive';
|
||||
|
||||
describe('FileDraggableDirective', () => {
|
||||
|
||||
let component;
|
||||
let component: FileDraggableDirective;
|
||||
|
||||
beforeEach( () => {
|
||||
component = new FileDraggableDirective();
|
||||
@@ -51,7 +51,7 @@ describe('FileDraggableDirective', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
component._onDropFiles(fakeEvent);
|
||||
component.onDropFiles(fakeEvent);
|
||||
});
|
||||
|
||||
it('should emit onFilesDropped event when a file is dragged not with Chrome' , (done) => {
|
||||
@@ -70,7 +70,7 @@ describe('FileDraggableDirective', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
component._onDropFiles(fakeEvent);
|
||||
component.onDropFiles(fakeEvent);
|
||||
});
|
||||
|
||||
it('should emit onFilesDropped event when a file is dragged with Chrome', (done) => {
|
||||
@@ -90,7 +90,7 @@ describe('FileDraggableDirective', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
component._onDropFiles(fakeEvent);
|
||||
component.onDropFiles(fakeEvent);
|
||||
});
|
||||
|
||||
it('should take the focus when the drag enter is called', () => {
|
||||
@@ -98,7 +98,7 @@ describe('FileDraggableDirective', () => {
|
||||
spyOn(mockEvent, 'preventDefault');
|
||||
|
||||
expect(component.getInputFocus()).toBe(false);
|
||||
component._onDragEnter(mockEvent);
|
||||
component.onDragEnter(mockEvent);
|
||||
expect(component.getInputFocus()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Directive, EventEmitter, Output } from '@angular/core';
|
||||
import { Directive, HostListener, HostBinding, EventEmitter, Output } from '@angular/core';
|
||||
|
||||
/**
|
||||
* [file-draggable]
|
||||
@@ -29,17 +29,12 @@ import { Directive, EventEmitter, Output } from '@angular/core';
|
||||
* @returns {FileDraggableDirective} .
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[file-draggable]',
|
||||
host: {
|
||||
'(drop)': '_onDropFiles($event)',
|
||||
'(dragenter)': '_onDragEnter($event)',
|
||||
'(dragleave)': '_onDragLeave($event)',
|
||||
'(dragover)': '_onDragOver($event)',
|
||||
'[class.input-focus]': '_inputFocusClass'
|
||||
}
|
||||
selector: '[file-draggable]'
|
||||
})
|
||||
export class FileDraggableDirective {
|
||||
|
||||
files: File [];
|
||||
|
||||
@Output()
|
||||
onFilesDropped: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
@@ -49,41 +44,39 @@ export class FileDraggableDirective {
|
||||
@Output()
|
||||
onFolderEntityDropped: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
files: File [];
|
||||
|
||||
private _inputFocusClass: boolean = false;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
@HostBinding('class.file-draggable__input-focus')
|
||||
inputFocusClass: boolean = false;
|
||||
|
||||
/**
|
||||
* Method called when files is dropped in the drag and drop area.
|
||||
*
|
||||
* @param {$event} $event - DOM $event.
|
||||
* @param event DOM event.
|
||||
*/
|
||||
_onDropFiles($event: any): void {
|
||||
this._preventDefault($event);
|
||||
@HostListener('drop', ['$event'])
|
||||
onDropFiles(event: any): void {
|
||||
if (!event.defaultPrevented) {
|
||||
this.preventDefault(event);
|
||||
|
||||
let items = $event.dataTransfer.items;
|
||||
if (items) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (typeof items[i].webkitGetAsEntry !== 'undefined') {
|
||||
let item = items[i].webkitGetAsEntry();
|
||||
if (item) {
|
||||
this._traverseFileTree(item);
|
||||
let items = event.dataTransfer.items;
|
||||
if (items) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (typeof items[i].webkitGetAsEntry !== 'undefined') {
|
||||
let item = items[i].webkitGetAsEntry();
|
||||
if (item) {
|
||||
this.traverseFileTree(item);
|
||||
}
|
||||
} else {
|
||||
let files = event.dataTransfer.files;
|
||||
this.onFilesDropped.emit(files);
|
||||
}
|
||||
} else {
|
||||
let files = $event.dataTransfer.files;
|
||||
this.onFilesDropped.emit(files);
|
||||
}
|
||||
} else {
|
||||
// safari or FF
|
||||
let files = event.dataTransfer.files;
|
||||
this.onFilesDropped.emit(files);
|
||||
}
|
||||
} else {
|
||||
// safari or FF
|
||||
let files = $event.dataTransfer.files;
|
||||
this.onFilesDropped.emit(files);
|
||||
}
|
||||
|
||||
this._inputFocusClass = false;
|
||||
this.inputFocusClass = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,7 +84,7 @@ export class FileDraggableDirective {
|
||||
*
|
||||
* @param {Object} item - can contains files or folders.
|
||||
*/
|
||||
private _traverseFileTree(item: any): void {
|
||||
private traverseFileTree(item: any): void {
|
||||
if (item.isFile) {
|
||||
let self = this;
|
||||
self.onFilesEntityDropped.emit(item);
|
||||
@@ -105,44 +98,50 @@ export class FileDraggableDirective {
|
||||
/**
|
||||
* Change the style of the drag area when a file drag in.
|
||||
*
|
||||
* @param {$event} $event - DOM $event.
|
||||
* @param {event} event - DOM event.
|
||||
*/
|
||||
_onDragEnter($event: Event): void {
|
||||
this._preventDefault($event);
|
||||
|
||||
this._inputFocusClass = true;
|
||||
@HostListener('dragenter', ['$event'])
|
||||
onDragEnter(event: Event): void {
|
||||
if (!event.defaultPrevented) {
|
||||
this.preventDefault(event);
|
||||
this.inputFocusClass = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the style of the drag area when a file drag out.
|
||||
*
|
||||
* @param {$event} $event - DOM $event.
|
||||
* @param {event} event - DOM event.
|
||||
*/
|
||||
_onDragLeave($event: Event): void {
|
||||
this._preventDefault($event);
|
||||
|
||||
this._inputFocusClass = false;
|
||||
@HostListener('dragleave', ['$event'])
|
||||
onDragLeave(event: Event): void {
|
||||
if (!event.defaultPrevented) {
|
||||
this.preventDefault(event);
|
||||
this.inputFocusClass = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the style of the drag area when a file is over the drag area.
|
||||
*
|
||||
* @param $event
|
||||
* @private
|
||||
* @param event
|
||||
*/
|
||||
_onDragOver($event: Event): void {
|
||||
this._preventDefault($event);
|
||||
this._inputFocusClass = true;
|
||||
@HostListener('dragover', ['$event'])
|
||||
onDragOver(event: Event): void {
|
||||
if (!event.defaultPrevented) {
|
||||
this.preventDefault(event);
|
||||
this.inputFocusClass = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent default and stop propagation of the DOM event.
|
||||
*
|
||||
* @param {$event} $event - DOM $event.
|
||||
* @param {event} $event - DOM event.
|
||||
*/
|
||||
_preventDefault($event: Event): void {
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault();
|
||||
preventDefault(event: Event): void {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,6 +149,6 @@ export class FileDraggableDirective {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
getInputFocus () {
|
||||
return this._inputFocusClass;
|
||||
return this.inputFocusClass;
|
||||
}
|
||||
}
|
||||
|
@@ -162,17 +162,14 @@ export class UploadService {
|
||||
* Create a folder
|
||||
* @param name - the folder name
|
||||
*/
|
||||
createFolder(relativePath: string, name: string) {
|
||||
return Observable.fromPromise(this.callApiCreateFolder(relativePath, name))
|
||||
.map(res => {
|
||||
return res;
|
||||
})
|
||||
createFolder(relativePath: string, name: string, parentId?: string) {
|
||||
return Observable.fromPromise(this.callApiCreateFolder(relativePath, name, parentId))
|
||||
.do(data => this.logService.info('Node data', data)) // eyeball results in the console
|
||||
.catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
callApiCreateFolder(relativePath: string, name: string): Promise<MinimalNodeEntity> {
|
||||
return this.apiService.getInstance().nodes.createFolder(name, relativePath);
|
||||
callApiCreateFolder(relativePath: string, name: string, parentId?: string): Promise<MinimalNodeEntity> {
|
||||
return this.apiService.getInstance().nodes.createFolder(name, relativePath, parentId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user