summaryrefslogtreecommitdiffstats
path: root/apps/files/src
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@protonmail.com>2023-04-11 09:31:30 +0200
committerJohn Molakvoæ <skjnldsv@protonmail.com>2023-04-11 14:38:53 +0200
commitb7df4196e2883da9028f8c5d646fbbf5ae10cf49 (patch)
treebf25c4448b898ca83e0671a3d4888573b5550e47 /apps/files/src
parenta05176a1f592d06b24725cd158baaf85e4456c6d (diff)
downloadnextcloud-server-b7df4196e2883da9028f8c5d646fbbf5ae10cf49.tar.gz
nextcloud-server-b7df4196e2883da9028f8c5d646fbbf5ae10cf49.zip
feat(files): right click
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
Diffstat (limited to 'apps/files/src')
-rw-r--r--apps/files/src/components/FileEntry.vue47
-rw-r--r--apps/files/src/components/FilesListHeaderActions.vue15
-rw-r--r--apps/files/src/store/actionsmenu.ts31
-rw-r--r--apps/files/src/types.ts7
4 files changed, 93 insertions, 7 deletions
diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue
index eb6ad9f7e18..337e4c7b6ac 100644
--- a/apps/files/src/components/FileEntry.vue
+++ b/apps/files/src/components/FileEntry.vue
@@ -66,7 +66,8 @@
ref="actionsMenu"
:disabled="source._loading"
:force-title="true"
- :inline="enabledInlineActions.length">
+ :inline="enabledInlineActions.length"
+ :open.sync="openedMenu">
<NcActionButton v-for="action in enabledMenuActions"
:key="action.id"
:class="'files-list__row-action-' + action.id"
@@ -116,12 +117,13 @@ import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadi
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import Vue from 'vue'
-import { isCachedPreview } from '../services/PreviewService.ts'
import { getFileActions } from '../services/FileAction.ts'
+import { isCachedPreview } from '../services/PreviewService.ts'
+import { useActionsMenuStore } from '../store/actionsmenu.ts'
import { useFilesStore } from '../store/files.ts'
+import { useKeyboardStore } from '../store/keyboard.ts'
import { useSelectionStore } from '../store/selection.ts'
import { useUserConfigStore } from '../store/userconfig.ts'
-import { useKeyboardStore } from '../store/keyboard.ts'
import CustomElementRender from './CustomElementRender.vue'
import CustomSvgIconRender from './CustomSvgIconRender.vue'
import logger from '../logger.js'
@@ -168,15 +170,17 @@ export default Vue.extend({
},
setup() {
+ const actionsMenuStore = useActionsMenuStore()
const filesStore = useFilesStore()
+ const keyboardStore = useKeyboardStore()
const selectionStore = useSelectionStore()
const userConfigStore = useUserConfigStore()
- const keyboardStore = useKeyboardStore()
return {
+ actionsMenuStore,
filesStore,
+ keyboardStore,
selectionStore,
userConfigStore,
- keyboardStore,
}
},
@@ -253,6 +257,9 @@ export default Vue.extend({
selectedFiles() {
return this.selectionStore.selected
},
+ isSelected() {
+ return this.selectedFiles.includes(this.source?.fileid?.toString?.())
+ },
cropPreviews() {
return this.userConfig.crop_image_previews
@@ -301,6 +308,15 @@ export default Vue.extend({
uniqueId() {
return this.hashCode(this.source.source)
},
+
+ openedMenu: {
+ get() {
+ return this.actionsMenuStore.opened === this
+ },
+ set(opened) {
+ this.actionsMenuStore.opened = opened ? this : null
+ },
+ },
},
watch: {
@@ -342,6 +358,9 @@ export default Vue.extend({
// Fetch the preview on init
this.debounceIfNotCached()
+
+ // Right click watcher on tr
+ this.$el.parentNode?.addEventListener?.('contextmenu', this.onRightClick)
},
beforeDestroy() {
@@ -410,7 +429,7 @@ export default Vue.extend({
this.clearImg()
// Close menu
- this.$refs?.actionsMenu?.closeMenu?.()
+ this.openedMenu = false
},
clearImg() {
@@ -487,6 +506,22 @@ export default Vue.extend({
this.selectionStore.setLastIndex(newSelectedIndex)
},
+ // Open the actions menu on right click
+ onRightClick(event) {
+ // If already opened, fallback to default browser
+ if (this.openedMenu) {
+ return
+ }
+
+ // If the clicked row is in the selection, open global menu
+ const isMoreThanOneSelected = this.selectedFiles.length > 1
+ this.actionsMenuStore.opened = this.isSelected && isMoreThanOneSelected ? 'global' : this
+
+ // Prevent any browser defaults
+ event.preventDefault()
+ event.stopPropagation()
+ },
+
t: translate,
formatFileSize,
},
diff --git a/apps/files/src/components/FilesListHeaderActions.vue b/apps/files/src/components/FilesListHeaderActions.vue
index d60fd81ad00..f136e281f09 100644
--- a/apps/files/src/components/FilesListHeaderActions.vue
+++ b/apps/files/src/components/FilesListHeaderActions.vue
@@ -24,7 +24,8 @@
<NcActions ref="actionsMenu"
:disabled="!!loading || areSomeNodesLoading"
:force-title="true"
- :inline="3">
+ :inline="3"
+ :open.sync="openedMenu">
<NcActionButton v-for="action in enabledActions"
:key="action.id"
:class="'files-list__row-actions-batch-' + action.id"
@@ -48,6 +49,7 @@ import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import Vue from 'vue'
import { getFileActions } from '../services/FileAction.ts'
+import { useActionsMenuStore } from '../store/actionsmenu.ts'
import { useFilesStore } from '../store/files.ts'
import { useSelectionStore } from '../store/selection.ts'
import CustomSvgIconRender from './CustomSvgIconRender.vue'
@@ -78,9 +80,11 @@ export default Vue.extend({
},
setup() {
+ const actionsMenuStore = useActionsMenuStore()
const filesStore = useFilesStore()
const selectionStore = useSelectionStore()
return {
+ actionsMenuStore,
filesStore,
selectionStore,
}
@@ -109,6 +113,15 @@ export default Vue.extend({
areSomeNodesLoading() {
return this.nodes.some(node => node._loading)
},
+
+ openedMenu: {
+ get() {
+ return this.actionsMenuStore.opened === 'global'
+ },
+ set(opened) {
+ this.actionsMenuStore.opened = opened ? 'global' : null
+ },
+ },
},
methods: {
diff --git a/apps/files/src/store/actionsmenu.ts b/apps/files/src/store/actionsmenu.ts
new file mode 100644
index 00000000000..b68f4998470
--- /dev/null
+++ b/apps/files/src/store/actionsmenu.ts
@@ -0,0 +1,31 @@
+/**
+ * @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/>.
+ *
+ */
+/* eslint-disable */
+import { defineStore } from 'pinia'
+import Vue from 'vue'
+import type { ActionsMenuStore } from '../types'
+
+export const useActionsMenuStore = defineStore('actionsmenu', {
+ state: () => ({
+ opened: null,
+ } as ActionsMenuStore),
+})
diff --git a/apps/files/src/types.ts b/apps/files/src/types.ts
index 9bbb572faaf..4c3d57d3e62 100644
--- a/apps/files/src/types.ts
+++ b/apps/files/src/types.ts
@@ -22,6 +22,7 @@
/* eslint-disable */
import type { Folder } from '@nextcloud/files'
import type { Node } from '@nextcloud/files'
+import type { ComponentInstance } from 'vue'
// Global definitions
export type Service = string
@@ -86,3 +87,9 @@ export interface SelectionStore {
lastSelection: FileId[]
lastSelectedIndex: number | null
}
+
+// Actions menu store
+export type GlobalActions = 'global'
+export interface ActionsMenuStore {
+ opened: ComponentInstance|GlobalActions|null
+}