Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix(files): Allow to drag and drop new files also on empty directories
Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Dec 5, 2023
commit 89e01eafee1a14798ad9f87ddd32751c9eaa0490
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
Loading