Merge pull request #6490 from opf/feature/28141/drag-an-drop-attachments

[28141] drag-and-drop images and attachments into open and closed editors

[ci skip]
pull/6494/head
Oliver Günther 6 years ago committed by GitHub
commit 742e04b1cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      app/assets/stylesheets/content/work_packages/single_view/_attachments.sass
  2. 6
      config/locales/js-en.yml
  3. 45
      frontend/src/app/components/attachments/attachment-list/attachment-list-item.component.ts
  4. 7
      frontend/src/app/components/attachments/attachment-list/attachment-list-item.html
  5. 1
      frontend/src/app/components/work-packages/work-package-comment/work-package-comment.component.html
  6. 13
      frontend/src/app/components/work-packages/work-package-comment/work-package-comment.component.ts
  7. 1
      frontend/src/app/components/work-packages/wp-single-view/wp-single-view.html
  8. 2
      frontend/src/app/components/wp-edit/wp-edit-field/wp-edit-field-group.directive.ts
  9. 20
      frontend/src/app/components/wp-edit/wp-edit-field/wp-edit-field.component.ts
  10. 3
      frontend/src/app/components/wp-edit/wp-edit-field/wp-edit-field.html
  11. 1
      frontend/src/app/components/wp-form-group/wp-attribute-group.template.html

@ -83,3 +83,6 @@
.add-another-file
.button
margin-bottom: 0
.work-package--attachments--draggable-item
cursor: pointer

@ -31,6 +31,12 @@ en:
ajax:
hide: "Hide"
loading: "Loading ..."
attachments:
draggable_hint: |
Drag on editor field to inline image or reference attachment. Closed editor fields will be opened while you keep dragging.
autocomplete_select:
placeholder:
multi: "Add \"%{name}\""

@ -43,7 +43,10 @@ export class AttachmentListItemComponent {
@Input() public index:any;
@Input() public selfDestroy?:boolean;
static imageFileExtensions:string[] = ['jpeg', 'jpg', 'gif', 'bmp', 'png'];
public text = {
dragHint: this.I18n.t('js.attachments.draggable_hint'),
destroyConfirmation: this.I18n.t('js.text_attachment_destroy_confirmation'),
removeFile: (arg:any) => this.I18n.t('js.label_remove_file', arg)
};
@ -54,8 +57,48 @@ export class AttachmentListItemComponent {
readonly pathHelper:PathHelperService) {
}
/**
* Set the appropriate data for drag & drop of an attachment item.
* @param evt DragEvent
*/
public setDragData(evt:DragEvent) {
const url = this.downloadPath;
const previewElement = this.draggableHTML(url);
evt.dataTransfer.setData("text/plain", url);
evt.dataTransfer.setData("text/html", previewElement.outerHTML);
evt.dataTransfer.setData("text/uri-list", url);
evt.dataTransfer.setDragImage(previewElement, 0, 0);
}
public draggableHTML(url:string) {
let el;
if (this.isImage) {
el = document.createElement('img')
el.src = url;
el.textContent = this.fileName;
} else {
el = document.createElement('a');
el.href = url;
el.textContent = this.fileName;
}
return el;
}
public get downloadPath() {
return this.pathHelper.attachmentDownloadPath(this.attachment.id, this.attachment.name);
return this.pathHelper.attachmentDownloadPath(this.attachment.id, this.fileName);
}
public get isImage() {
const ext = this.fileName.split('.').pop() || '';
return AttachmentListItemComponent.imageFileExtensions.indexOf(ext.toLowerCase()) > -1;
}
public get fileName() {
const a = this.attachment;
return a.fileName || a.customName || a.name;
}
public confirmRemoveAttachment($event:JQueryEventObject) {

@ -1,4 +1,9 @@
<li class="form--selected-value--container" focus-within focusWithinSelector=".inplace-edit--icon-wrapper">
<li class="form--selected-value--container work-package--attachments--draggable-item"
focus-within
draggable="true"
(dragstart)="setDragData($event)"
[title]="text.dragHint"
focusWithinSelector=".inplace-edit--icon-wrapper">
<span class="form--selected-value">
<op-icon icon-classes="icon-context icon-attachment"></op-icon>
<a

@ -12,6 +12,7 @@
[editFieldHandler]="handler">
</edit-form-portal>
<div *ngIf="!active"
(dragover)="startDragOverActivation($event)"
class="inplace-edit--read">
<accessible-by-keyboard
class="inplace-editing--trigger-container"

@ -103,6 +103,19 @@ export class WorkPackageCommentComponent implements IEditFieldHandler, OnInit, O
});
}
// Open the field when its closed and relay drag & drop events to it.
public startDragOverActivation(event:JQueryEventObject) {
if (this.active) {
return true;
}
this.activate();
event.preventDefault();
return false;
}
public ngOnDestroy() {
// Nothing to do.
}

@ -82,6 +82,7 @@
<div class="single-attribute work-packages--details--description">
<wp-edit-field [fieldName]="'description'"
[workPackageId]="workPackage.id"
[isDropTarget]="true"
[wrapperClasses]="'-no-label'"
[displayPlaceholder]="text.description.placeholder">
</wp-edit-field>

@ -148,7 +148,7 @@ export class WorkPackageEditFieldGroupComponent implements OnInit, OnDestroy {
(this.editMode && !this.skipField(field) || this.form.activeFields[field.fieldName])
if (shouldActivate) {
field.activateOnForm(this.form, true);
field.activateOnForm(true);
} else {
this.states.workPackages
.get(this.workPackage.id)

@ -30,13 +30,11 @@ import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-r
import {WorkPackageCacheService} from '../../work-packages/work-package-cache.service';
import {WorkPackageNotificationService} from '../wp-notification.service';
import {States} from '../../states.service';
import {WorkPackageEditForm} from '../../wp-edit-form/work-package-edit-form';
import {
displayClassName,
DisplayFieldRenderer,
editFieldContainerClass
} from '../../wp-edit-form/display-field-renderer';
import {WorkPackageEditFieldHandler} from '../../wp-edit-form/work-package-edit-field-handler';
import {WorkPackageEditingService} from '../../wp-edit-form/work-package-editing-service';
import {SelectionHelpers} from '../../../helpers/selection-helpers';
import {debugLog} from '../../../helpers/debug_output';
@ -59,6 +57,7 @@ export class WorkPackageEditFieldComponent implements OnInit {
@Input('workPackageId') public workPackageId:string;
@Input('wrapperClasses') public wrapperClasses?:string;
@Input('displayPlaceholder') public displayPlaceholder?:string;
@Input('isDropTarget') public isDropTarget?:boolean = false;
@ViewChild('displayContainer') readonly displayContainer:ElementRef;
@ViewChild('editContainer') readonly editContainer:ElementRef;
@ -89,6 +88,17 @@ export class WorkPackageEditFieldComponent implements OnInit {
this.wpEditFieldGroup.register(this);
}
// Open the field when its closed and relay drag & drop events to it.
public startDragOverActivation(event:JQueryEventObject) {
if (!this.isDropTarget || this.active) {
return true;
}
this.handleUserActivate(null);
event.preventDefault();
return false;
}
public render() {
const el = this.fieldRenderer.render(this.resource, this.fieldName, null, this.displayPlaceholder);
this.displayContainer.nativeElement.innerHTML = '';
@ -140,10 +150,10 @@ export class WorkPackageEditFieldComponent implements OnInit {
return false;
}
public activateOnForm(form:WorkPackageEditForm, noWarnings:boolean = false) {
public activateOnForm(noWarnings:boolean = false) {
// Activate the field
this.active = true;
return form
return this.wpEditFieldGroup.form
.activate(this.fieldName, noWarnings)
.catch(() => this.deactivate(true));
}
@ -156,7 +166,7 @@ export class WorkPackageEditFieldComponent implements OnInit {
positionOffset = ClickPositionMapper.getPosition(evt);
}
this.activateOnForm(this.wpEditFieldGroup.form)
this.activateOnForm()
.then((handler) => {
handler.focus(positionOffset);
});

@ -3,7 +3,8 @@
fieldName,
active && '-active' || '',
wrapperClasses || '-small'
]">
]"
(dragover)="startDragOverActivation($event)">
<div #editContainer
[hidden]="!active">

@ -24,6 +24,7 @@
<wp-edit-field *ngIf="!descriptor.field.isFormattable"
[workPackageId]="workPackage.id"
[fieldName]="descriptor.name"
[isDropTarget]="true"
[ngClass]="descriptor.field!.schema.type === 'String' ? 'wp-edit-field--text' : ''">
</wp-edit-field>
</div>

Loading…
Cancel
Save