Skip to content

Commit

Permalink
fix(files): Allow to drag and drop new files also on empty directories
Browse files Browse the repository at this point in the history
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
  • Loading branch information
susnux committed Dec 5, 2023
1 parent 7ae780b commit 89e01ea
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 124 deletions.
72 changes: 42 additions & 30 deletions apps/files/src/components/DragAndDropNotice.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
-
-->
<template>
<div class="files-list__drag-drop-notice"
:class="{ 'files-list__drag-drop-notice--dragover': dragover }"
<div v-show="dragover"
class="files-list__drag-drop-notice"
@drop="onDrop">
<div class="files-list__drag-drop-notice-wrapper">
<TrayArrowDownIcon :size="48" />
Expand All @@ -34,17 +34,16 @@

<script lang="ts">
import type { Upload } from '@nextcloud/upload'
import { join } from 'path'
import { showSuccess } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import { getUploader } from '@nextcloud/upload'
import Vue from 'vue'
import { defineComponent } from 'vue'
import TrayArrowDownIcon from 'vue-material-design-icons/TrayArrowDown.vue'
import logger from '../logger.js'
export default Vue.extend({
export default defineComponent({
name: 'DragAndDropNotice',
components: {
Expand All @@ -56,16 +55,43 @@ export default Vue.extend({
type: Object,
required: true,
},
dragover: {
type: Boolean,
default: false,
},
},
data() {
return {
dragover: false,
}
},
mounted() {
// Add events on parent to cover both the table and DragAndDrop notice
const mainContent = window.document.querySelector('main.app-content') as HTMLElement
mainContent.addEventListener('dragover', this.onDragOver)
mainContent.addEventListener('dragleave', this.onDragLeave)
},
beforeDestroy() {
const mainContent = window.document.querySelector('main.app-content') as HTMLElement
mainContent.removeEventListener('dragover', this.onDragOver)
mainContent.removeEventListener('dragleave', this.onDragLeave)
},
methods: {
onDrop(event: DragEvent) {
this.$emit('update:dragover', false)
onDragOver(event: DragEvent) {
const isForeignFile = event.dataTransfer?.types.includes('Files')
if (isForeignFile) {
// Only handle uploading
this.dragover = true
}
},
onDragLeave(/* event: DragEvent */) {
if (this.dragover) {
this.dragover = false
}
},
onDrop(event: DragEvent) {
if (this.$el.querySelector('tbody')?.contains(event.target as Node)) {
return
}
Expand All @@ -91,12 +117,13 @@ export default Vue.extend({
// Scroll to last upload if terminated
const lastUpload = uploads[uploads.length - 1]
if (lastUpload?.response?.headers?.['oc-fileid']) {
this.$router.push(Object.assign({}, this.$route, {
this.$router.push({
...this.$route,
params: {
// Remove instanceid from header response
fileid: parseInt(lastUpload.response?.headers?.['oc-fileid']),
},
}))
})
}
})
}
Expand All @@ -108,12 +135,7 @@ export default Vue.extend({

<style lang="scss" scoped>
.files-list__drag-drop-notice {
position: absolute;
z-index: 9999;
top: 0;
right: 0;
left: 0;
display: none;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
Expand All @@ -123,11 +145,7 @@ export default Vue.extend({
user-select: none;
color: var(--color-text-maxcontrast);
background-color: var(--color-main-background);
&--dragover {
display: flex;
border-color: black;
}
border-color: black;
h3 {
margin-left: 16px;
Expand All @@ -144,12 +162,6 @@ export default Vue.extend({
border: 2px var(--color-border-dark) dashed;
border-radius: var(--border-radius-large);
}
&__close {
position: absolute !important;
top: 10px;
right: 10px;
}
}
</style>
134 changes: 51 additions & 83 deletions apps/files/src/components/FilesListVirtual.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,91 +20,78 @@
-
-->
<template>
<Fragment>
<!-- Drag and drop notice -->
<DragAndDropNotice v-if="canUpload && filesListWidth >= 512"
:current-folder="currentFolder"
:dragover.sync="dragover"
:style="{ height: dndNoticeHeight }" />

<VirtualList ref="table"
:data-component="userConfig.grid_view ? FileEntryGrid : FileEntry"
:data-key="'source'"
:data-sources="nodes"
:grid-mode="userConfig.grid_view"
:extra-props="{
isMtimeAvailable,
isSizeAvailable,
nodes,
filesListWidth,
}"
:scroll-to-index="scrollToIndex"
:caption="caption"
@scroll="onScroll">
<template #before>
<!-- Headers -->
<FilesListHeader v-for="header in sortedHeaders"
:key="header.id"
:current-folder="currentFolder"
:current-view="currentView"
:header="header" />
</template>

<!-- Thead-->
<template #header>
<!-- Table header and sort buttons -->
<FilesListTableHeader ref="thead"
:files-list-width="filesListWidth"
:is-mtime-available="isMtimeAvailable"
:is-size-available="isSizeAvailable"
:nodes="nodes" />
</template>

<!-- Tfoot-->
<template #footer>
<FilesListTableFooter :files-list-width="filesListWidth"
:is-mtime-available="isMtimeAvailable"
:is-size-available="isSizeAvailable"
:nodes="nodes"
:summary="summary" />
</template>
</VirtualList>
</Fragment>
<VirtualList ref="table"
:data-component="userConfig.grid_view ? FileEntryGrid : FileEntry"
:data-key="'source'"
:data-sources="nodes"
:grid-mode="userConfig.grid_view"
:extra-props="{
isMtimeAvailable,
isSizeAvailable,
nodes,
filesListWidth,
}"
:scroll-to-index="scrollToIndex"
:caption="caption">
<template #before>
<!-- Headers -->
<FilesListHeader v-for="header in sortedHeaders"
:key="header.id"
:current-folder="currentFolder"
:current-view="currentView"
:header="header" />
</template>

<!-- Thead-->
<template #header>
<!-- Table header and sort buttons -->
<FilesListTableHeader ref="thead"
:files-list-width="filesListWidth"
:is-mtime-available="isMtimeAvailable"
:is-size-available="isSizeAvailable"
:nodes="nodes" />
</template>

<!-- Tfoot-->
<template #footer>
<FilesListTableFooter :files-list-width="filesListWidth"
:is-mtime-available="isMtimeAvailable"
:is-size-available="isSizeAvailable"
:nodes="nodes"
:summary="summary" />
</template>
</VirtualList>
</template>

<script lang="ts">
import type { Node as NcNode } from '@nextcloud/files'
import type { PropType } from 'vue'
import type { UserConfig } from '../types.ts'
import type { UserConfig } from '../types'
import { Fragment } from 'vue-frag'
import { getFileListHeaders, Folder, View, Permission, getFileActions } from '@nextcloud/files'
import { getFileListHeaders, Folder, View, getFileActions } from '@nextcloud/files'
import { showError } from '@nextcloud/dialogs'
import { loadState } from '@nextcloud/initial-state'
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import Vue from 'vue'
import { action as sidebarAction } from '../actions/sidebarAction.ts'
import { useUserConfigStore } from '../store/userconfig.ts'
import DragAndDropNotice from './DragAndDropNotice.vue'
import { action as sidebarAction } from '../actions/sidebarAction.js'
import { useUserConfigStore } from '../store/userconfig.js'
import FileEntry from './FileEntry.vue'
import FileEntryGrid from './FileEntryGrid.vue'
import FilesListHeader from './FilesListHeader.vue'
import FilesListTableFooter from './FilesListTableFooter.vue'
import FilesListTableHeader from './FilesListTableHeader.vue'
import filesListWidthMixin from '../mixins/filesListWidth.ts'
import filesListWidthMixin from '../mixins/filesListWidth.js'
import logger from '../logger.js'
import VirtualList from './VirtualList.vue'
export default Vue.extend({
name: 'FilesListVirtual',
components: {
DragAndDropNotice,
FilesListHeader,
FilesListTableFooter,
FilesListTableHeader,
Fragment,
VirtualList,
},
Expand Down Expand Up @@ -140,7 +127,6 @@ export default Vue.extend({
FileEntryGrid,
headers: getFileListHeaders(),
scrollToIndex: 0,
dragover: false,
dndNoticeHeight: 0,
}
},
Expand Down Expand Up @@ -192,10 +178,6 @@ export default Vue.extend({
return [...this.headers].sort((a, b) => a.order - b.order)
},
canUpload() {
return this.currentFolder && (this.currentFolder.permissions & Permission.CREATE) !== 0
},
caption() {
const defaultCaption = t('files', 'List of files and folders.')
const viewCaption = this.currentView.caption || defaultCaption
Expand All @@ -215,12 +197,15 @@ export default Vue.extend({
// Add events on parent to cover both the table and DragAndDrop notice
const mainContent = window.document.querySelector('main.app-content') as HTMLElement
mainContent.addEventListener('dragover', this.onDragOver)
mainContent.addEventListener('dragleave', this.onDragLeave)
this.scrollToFile(this.fileId)
this.openSidebarForFile(this.fileId)
this.handleOpenFile()
},
beforeDestroy() {
const mainContent = window.document.querySelector('main.app-content') as HTMLElement
mainContent.removeEventListener('dragover', this.onDragOver)
},
methods: {
Expand Down Expand Up @@ -274,9 +259,7 @@ export default Vue.extend({
// Detect if we're only dragging existing files or not
const isForeignFile = event.dataTransfer?.types.includes('Files')
if (isForeignFile) {
this.dragover = true
} else {
this.dragover = false
return
}
event.preventDefault()
Expand All @@ -296,21 +279,6 @@ export default Vue.extend({
this.$refs.table.$el.scrollTop = this.$refs.table.$el.scrollTop + 25
}
},
onDragLeave(event: DragEvent) {
// Counter bubbling, make sure we're ending the drag
// only when we're leaving the current element
const currentTarget = event.currentTarget as HTMLElement
if (currentTarget?.contains(event.relatedTarget as HTMLElement)) {
return
}
this.dragover = false
},
onScroll() {
// Update the sticky position of the thead to adapt to the scroll
this.dndNoticeHeight = (this.$refs.thead.$el?.getBoundingClientRect?.()?.top ?? 0) + 'px'
},
t,
},
Expand Down
4 changes: 3 additions & 1 deletion apps/files/src/components/VirtualList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@

<script lang="ts">
import type { File, Folder, Node } from '@nextcloud/files'
import type { PropType } from 'vue'
import { debounce } from 'debounce'
import Vue, { PropType } from 'vue'
import Vue from 'vue'
import filesListWidthMixin from '../mixins/filesListWidth.ts'
import logger from '../logger.js'
Expand Down
2 changes: 2 additions & 0 deletions apps/files/src/mixins/filesListWidth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export default Vue.extend({
},
mounted() {
const fileListEl = document.querySelector('#app-content-vue')
this.filesListWidth = fileListEl?.clientWidth ?? null

this.$resizeObserver = new ResizeObserver((entries) => {
if (entries.length > 0 && entries[0].target === fileListEl) {
this.filesListWidth = entries[0].contentRect.width
Expand Down

0 comments on commit 89e01ea

Please sign in to comment.