feat(files): add drag and drop support

Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
This commit is contained in:
John Molakvoæ 2023-08-22 19:32:05 +02:00
parent 4621198744
commit 16094c7db5
No known key found for this signature in database
GPG key ID: 60C25B8C072916CF
3 changed files with 125 additions and 4 deletions

View file

@ -45,10 +45,13 @@
<!-- Icon or preview -->
<span class="files-list__row-icon" @click="execDefaultAction">
<template v-if="source.type === 'folder'">
<FolderIcon />
<OverlayIcon :is="folderOverlay"
v-if="folderOverlay"
class="files-list__row-icon-overlay" />
<FolderOpenIcon v-if="dragover" />
<template v-else>
<FolderIcon />
<OverlayIcon :is="folderOverlay"
v-if="folderOverlay"
class="files-list__row-icon-overlay" />
</template>
</template>
<!-- Decorative image, should not be aria documented -->
@ -194,6 +197,7 @@ import Vue from 'vue'
import AccountGroupIcon from 'vue-material-design-icons/AccountGroup.vue'
import FileIcon from 'vue-material-design-icons/File.vue'
import FolderIcon from 'vue-material-design-icons/Folder.vue'
import FolderOpenIcon from 'vue-material-design-icons/FolderOpen.vue'
import KeyIcon from 'vue-material-design-icons/Key.vue'
import TagIcon from 'vue-material-design-icons/Tag.vue'
import LinkIcon from 'vue-material-design-icons/Link.vue'
@ -209,6 +213,7 @@ import { action as sidebarAction } from '../actions/sidebarAction.ts'
import { hashCode } from '../utils/hashUtils.ts'
import { isCachedPreview } from '../services/PreviewService.ts'
import { useActionsMenuStore } from '../store/actionsmenu.ts'
import { useDragAndDropStore } from '../store/dragging.ts'
import { useFilesStore } from '../store/files.ts'
import { useKeyboardStore } from '../store/keyboard.ts'
import { useRenamingStore } from '../store/renaming.ts'
@ -235,6 +240,7 @@ export default Vue.extend({
FavoriteIcon,
FileIcon,
FolderIcon,
FolderOpenIcon,
KeyIcon,
LinkIcon,
NcActionButton,
@ -279,6 +285,7 @@ export default Vue.extend({
setup() {
const actionsMenuStore = useActionsMenuStore()
const draggingStore = useDragAndDropStore()
const filesStore = useFilesStore()
const keyboardStore = useKeyboardStore()
const renamingStore = useRenamingStore()
@ -286,6 +293,7 @@ export default Vue.extend({
const userConfigStore = useUserConfigStore()
return {
actionsMenuStore,
draggingStore,
filesStore,
keyboardStore,
renamingStore,
@ -299,6 +307,7 @@ export default Vue.extend({
backgroundFailed: false,
backgroundImage: '',
loading: '',
dragover: false,
}
},
@ -445,6 +454,9 @@ export default Vue.extend({
}
},
draggingFiles() {
return this.draggingStore.dragging
},
selectedFiles() {
return this.selectionStore.selected
},
@ -567,6 +579,23 @@ export default Vue.extend({
isActive() {
return this.fileid === this.currentFileId?.toString?.()
},
canDrag() {
return (this.source.permissions & Permission.UPDATE) !== 0
},
canDrop() {
if (this.source.type !== FileType.Folder) {
return false
}
// If the current folder is also being dragged, we can't drop it on itself
if (this.draggingFiles.find(fileId => fileId === this.fileid)) {
return false
}
return (this.source.permissions & Permission.CREATE) !== 0
},
},
watch: {
@ -930,6 +959,46 @@ export default Vue.extend({
return action.displayName([this.source], this.currentView)
},
onDragEnter() {
this.dragover = this.canDrop
},
onDragLeave() {
this.dragover = false
},
onDragStart(event) {
if (!this.canDrag) {
event.preventDefault()
event.stopPropagation()
return
}
logger.debug('Drag started')
// Dragging set of files
if (this.selectedFiles.length > 0) {
this.draggingStore.set(this.selectedFiles)
return
}
this.draggingStore.set([this.fileid])
},
onDragEnd() {
this.draggingStore.reset()
this.dragover = false
logger.debug('Drag ended')
},
onDrop(event) {
// If another button is pressed, cancel it
// This allows cancelling the drag with the right click
if (!this.canDrop || event.button !== 0) {
return
}
logger.debug('Dropped', { event, selection: this.draggingFiles })
},
t: translate,
formatFileSize,
},

View file

@ -0,0 +1,46 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import { defineStore } from 'pinia'
import Vue from 'vue'
import type { FileId, DragAndDropStore } from '../types'
export const useDragAndDropStore = defineStore('dragging', {
state: () => ({
dragging: [],
} as DragAndDropStore),
actions: {
/**
* Set the selection of fileIds
*/
set(selection = [] as FileId[]) {
Vue.set(this, 'dragging', selection)
},
/**
* Reset the selection
*/
reset() {
Vue.set(this, 'dragging', [])
},
},
})

View file

@ -106,3 +106,9 @@ export interface RenamingStore {
export interface UploaderStore {
queue: Upload[]
}
// Drag and drop store
export interface DragAndDropStore {
dragging: FileId[]
}