mirror of
https://github.com/nextcloud/server.git
synced 2026-06-11 09:42:09 -04:00
feat(files): add drag and drop support
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
This commit is contained in:
parent
4621198744
commit
16094c7db5
3 changed files with 125 additions and 4 deletions
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
46
apps/files/src/store/dragging.ts
Normal file
46
apps/files/src/store/dragging.ts
Normal 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', [])
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -106,3 +106,9 @@ export interface RenamingStore {
|
|||
export interface UploaderStore {
|
||||
queue: Upload[]
|
||||
}
|
||||
|
||||
// Drag and drop store
|
||||
export interface DragAndDropStore {
|
||||
dragging: FileId[]
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue