Skip to content

Commit

Permalink
feat(document-grid): add drag'n'drop capability to document grid
Browse files Browse the repository at this point in the history
This duplicates quite a few lines of code. But I didn't see a way
to reuse the code from the upload button component. Maybe we should
move those actions to a service that can be used by all components
where we could also put the upload and delete actions.
But this is - IMO - out of scope for this PR. I will gladly make
another PR with the service.
  • Loading branch information
fkm-adfinis committed Dec 1, 2020
1 parent b5abb58 commit dd8bc22
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 36 deletions.
102 changes: 66 additions & 36 deletions addon/components/document-grid.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -16,46 +16,76 @@
/>
</div>

<div class="uk-padding-small uk-overflow-auto" ...attributes>
<div
class="
uk-position-relative
uk-height-1-1
{{if this.isDragOver "document-grid--dragover"}}
"
{{on "dragenter" this.onDragEnter}}
{{on "dragleave" this.onDragLeave}}
{{on "dragover" this.onDragOver}}
{{on "drop" this.onDrop}}
>
<div
class="document-grid"
{{did-insert (perform this.fetchDocuments)}}
{{did-insert this.setupGridAnimations}}
{{did-update (perform this.fetchDocuments) @filters}}
class="
uk-position-cover
uk-padding-small
uk-height-1-1 uk-overflow-auto
"
...attributes
>
{{#if this.fetchDocuments.isRunning}}
{{#each (range 0 10) as |index|}}
<div>
<DocumentCard::Skeleton @animationDelay={{concat index "00ms"}} />
</div>
{{/each}}
{{else}}
{{#each this.documents as |document|}}
{{! This wrapper div is needed for the uk-grid spacing between elements }}
<div data-test-document-container>
<LinkTo
@query={{hash document=document.id}}
class="uk-link-reset"
data-test-document-link
{{did-insert
this.scrollIntoView
(eq @selectedDocumentId document.id)
}}
>
<DocumentCard
data-test-document
@document={{document}}
class={{if (eq @selectedDocumentId document.id) "selected"}}
/>
</LinkTo>
</div>
<div
class="document-grid"
{{did-insert (perform this.fetchDocuments)}}
{{did-insert this.setupGridAnimations}}
{{did-update (perform this.fetchDocuments) @filters}}
>
{{#if this.fetchDocuments.isRunning}}
{{#each (range 0 10) as |index|}}
<div>
<DocumentCard::Skeleton @animationDelay={{concat index "00ms"}} />
</div>
{{/each}}
{{else}}
<div class="empty uk-margin-large-top" data-test-empty>
<EmptyIcon />
</div>
{{/each}}
{{/if}}
{{#each this.documents as |document|}}
{{! This wrapper div is needed for the uk-grid spacing between elements }}
<div data-test-document-container>
<LinkTo
@query={{hash document=document.id}}
class="uk-link-reset"
data-test-document-link
{{did-insert
this.scrollIntoView
(eq @selectedDocumentId document.id)
}}
>
<DocumentCard
data-test-document
@document={{document}}
class={{if (eq @selectedDocumentId document.id) "selected"}}
/>
</LinkTo>
</div>
{{else}}
<div class="empty uk-margin-large-top" data-test-empty>
<EmptyIcon />
</div>
{{/each}}
{{/if}}
</div>
</div>
{{#if this.isDragOver}}
<button
class="uk-width-1
uk-padding-remove
uk-position-bottom
uk-button uk-button-primary"
type="button"
>
{{t "alexandria.document-grid.drop-to-upload"}}
</button>
{{/if}}
</div>
</div>
<DocumentDetails @document={{this.selectedDocument}} data-test-details />
Expand Down
85 changes: 85 additions & 0 deletions addon/components/document-grid.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { wrapGrid } from "animate-css-grid";
import { lastValue, task } from "ember-concurrency-decorators";

export default class DocumentGridComponent extends Component {
@service notification;
@service config;
@service store;
@service intl;

@tracked isDragOver = false;

get selectedDocument() {
if (this.args.selectedDocumentId) {
Expand Down Expand Up @@ -43,4 +49,83 @@ export default class DocumentGridComponent extends Component {
@action setupGridAnimations(element) {
wrapGrid(element);
}

// Drag'n'Drop document upload

@action onDragEnter() {
if (!this.args.filters.category) {
return;
}

this.isDragOver = true;
}

@action onDragLeave() {
this.isDragOver = false;
}

@action onDragOver(event) {
event.preventDefault();
event.stopPropagation();
}

@action async onDrop(event) {
if (!this.args.filters.category) {
return;
}

event.preventDefault();
event.stopPropagation();

const { files } = event.dataTransfer;

try {
const category =
(await this.store.peekRecord("category", this.args.filters.category)) ||
this.store.findRecord("category", this.args.filters.category);

await Promise.all(
Array.from(files).map(async (file) => {
const documentModel = this.store.createRecord("document", {
category,
meta: this.config.defaultModelMeta.document,
});
documentModel.title = file.name;
await documentModel.save();

const fileModel = this.store.createRecord("file", {
name: file.name,
type: "original",
document: documentModel,
});
await fileModel.save();

const response = await fetch(fileModel.uploadUrl, {
method: "PUT",
body: file,
});

if (!response.ok) {
throw new Error("The request returned an error status code");
}
})
);

this.notification.success(
this.intl.t("alexandria.success.upload-document", {
count: files.length,
})
);

await this.fetchDocuments.perform();
} catch (error) {
this.notification.danger(
this.intl.t("alexandria.errors.upload-document", {
count: files.length,
})
);
}

this.isDragOver = false;
}
}
12 changes: 12 additions & 0 deletions app/styles/components/_document-grid.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@use "ember-uikit/variables-theme" as uikit;

.document-grid {
display: grid;
grid-template-columns: repeat(auto-fill, 200px);
Expand All @@ -9,4 +11,14 @@
grid-column-start: 1;
grid-column-end: end;
}

&--dragover {
border: 1px solid uikit.$alert-primary-color;

// Disable pointer-events while draging something over the grid.
// Otherwise, the drag&drop will stop working when hovering over children.
.document-grid {
pointer-events: none;
}
}
}
3 changes: 3 additions & 0 deletions translations/de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ alexandria:
cancel: "Abbrechen"
confirm: "Löschen"

document-grid:
drop-to-upload: "Datei fallen lassen, um sie hochzuladen"

document-details:
document-title: "Dokumententitel"
created-at: "Erstellt am {date}"
Expand Down
3 changes: 3 additions & 0 deletions translations/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ alexandria:
cancel: "Cancel"
confirm: "Delete"

document-grid:
drop-to-upload: "Drop file to upload"

document-details:
document-title: "Document title"
created-at: "Created on {date}"
Expand Down

0 comments on commit dd8bc22

Please sign in to comment.