aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/components/FileEntry.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src/components/FileEntry.vue')
-rw-r--r--apps/files/src/components/FileEntry.vue243
1 files changed, 54 insertions, 189 deletions
diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue
index 87c36356210..7ff6186a6e3 100644
--- a/apps/files/src/components/FileEntry.vue
+++ b/apps/files/src/components/FileEntry.vue
@@ -86,44 +86,14 @@
</td>
<!-- Actions -->
- <td v-show="!isRenamingSmallScreen"
+ <FileEntryActions v-show="!isRenamingSmallScreen"
+ ref="actions"
:class="`files-list__row-actions-${uniqueId}`"
- class="files-list__row-actions"
- data-cy-files-list-row-actions>
- <!-- Render actions -->
- <CustomElementRender v-for="action in enabledRenderActions"
- :key="action.id"
- :class="'files-list__row-action-' + action.id"
- :current-view="currentView"
- :render="action.renderInline"
- :source="source"
- class="files-list__row-action--inline" />
-
- <!-- Menu actions -->
- <NcActions v-if="visible"
- ref="actionsMenu"
- :boundaries-element="getBoundariesElement()"
- :container="getBoundariesElement()"
- :disabled="isLoading"
- :force-name="true"
- :force-menu="enabledInlineActions.length === 0 /* forceMenu only if no inline actions */"
- :inline="enabledInlineActions.length"
- :open.sync="openedMenu">
- <NcActionButton v-for="action in enabledMenuActions"
- :key="action.id"
- :class="'files-list__row-action-' + action.id"
- :close-after-click="true"
- :data-cy-files-list-row-action="action.id"
- :title="action.title?.([source], currentView)"
- @click="onActionClick(action)">
- <template #icon>
- <NcLoadingIcon v-if="loading === action.id" :size="18" />
- <NcIconSvgWrapper v-else :svg="action.iconSvgInline([source], currentView)" />
- </template>
- {{ actionDisplayName(action) }}
- </NcActionButton>
- </NcActions>
- </td>
+ :files-list-width="filesListWidth"
+ :loading.sync="loading"
+ :opened.sync="openedMenu"
+ :source="source"
+ :visible="visible" />
<!-- Size -->
<td v-if="isSizeAvailable"
@@ -163,7 +133,7 @@ import type { PropType } from 'vue'
import { emit } from '@nextcloud/event-bus'
import { extname, join } from 'path'
-import { getFileActions, DefaultType, FileType, formatFileSize, Permission, Folder, File as NcFile, FileAction, NodeStatus, Node } from '@nextcloud/files'
+import { FileType, formatFileSize, Permission, Folder, File as NcFile, NodeStatus, Node, View } from '@nextcloud/files'
import { getUploader } from '@nextcloud/upload'
import { loadState } from '@nextcloud/initial-state'
import { showError, showSuccess } from '@nextcloud/dialogs'
@@ -173,30 +143,24 @@ import axios from '@nextcloud/axios'
import moment from '@nextcloud/moment'
import Vue from 'vue'
-import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
-import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
-import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
-import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
import { action as sidebarAction } from '../actions/sidebarAction.ts'
import { getDragAndDropPreview } from '../utils/dragUtils.ts'
import { handleCopyMoveNodeTo } from '../actions/moveOrCopyAction.ts'
-import { MoveCopyAction } from '../actions/moveOrCopyActionUtils.ts'
import { hashCode } from '../utils/hashUtils.ts'
+import { MoveCopyAction } from '../actions/moveOrCopyActionUtils.ts'
import { useActionsMenuStore } from '../store/actionsmenu.ts'
import { useDragAndDropStore } from '../store/dragging.ts'
import { useFilesStore } from '../store/files.ts'
import { useRenamingStore } from '../store/renaming.ts'
import { useSelectionStore } from '../store/selection.ts'
import CustomElementRender from './CustomElementRender.vue'
+import FileEntryActions from './FileEntry/FileEntryActions.vue'
import FileEntryCheckbox from './FileEntry/FileEntryCheckbox.vue'
import FileEntryPreview from './FileEntry/FileEntryPreview.vue'
import logger from '../logger.js'
-// The registered actions list
-const actions = getFileActions()
-
Vue.directive('onClickOutside', vOnClickOutside)
const forbiddenCharacters = loadState('files', 'forbiddenCharacters', '') as string
@@ -206,11 +170,8 @@ export default Vue.extend({
components: {
CustomElementRender,
+ FileEntryActions,
FileEntryPreview,
- NcActionButton,
- NcActions,
- NcIconSvgWrapper,
- NcLoadingIcon,
NcTextField,
FileEntryCheckbox,
},
@@ -267,8 +228,8 @@ export default Vue.extend({
},
computed: {
- currentView() {
- return this.$navigation.active
+ currentView(): View {
+ return this.$navigation.active as View
},
columns() {
// Hide columns if the list is too small
@@ -288,6 +249,12 @@ export default Vue.extend({
fileid() {
return this.source?.fileid?.toString?.()
},
+ uniqueId() {
+ return hashCode(this.source.source)
+ },
+ isLoading() {
+ return this.source.status === NodeStatus.LOADING
+ },
extension() {
if (this.source.attributes?.displayName) {
@@ -363,8 +330,9 @@ export default Vue.extend({
}
}
- if (this.enabledDefaultActions.length > 0) {
- const action = this.enabledDefaultActions[0]
+ const enabledDefaultActions = this.$refs?.actions?.enabledDefaultActions
+ if (enabledDefaultActions?.length > 0) {
+ const action = enabledDefaultActions[0]
const displayName = action.displayName([this.source], this.currentView)
return {
title: displayName,
@@ -395,66 +363,6 @@ export default Vue.extend({
return this.selectedFiles.includes(this.fileid)
},
- // Sorted actions that are enabled for this node
- enabledActions() {
- if (this.source.attributes.failed) {
- return []
- }
-
- return actions
- .filter(action => !action.enabled || action.enabled([this.source], this.currentView))
- .sort((a, b) => (a.order || 0) - (b.order || 0))
- },
-
- // Enabled action that are displayed inline
- enabledInlineActions() {
- if (this.filesListWidth < 768) {
- return []
- }
- return this.enabledActions.filter(action => action?.inline?.(this.source, this.currentView))
- },
-
- // Enabled action that are displayed inline with a custom render function
- enabledRenderActions() {
- if (!this.visible) {
- return []
- }
- return this.enabledActions.filter(action => typeof action.renderInline === 'function')
- },
-
- // Default actions
- enabledDefaultActions() {
- return this.enabledActions.filter(action => !!action?.default)
- },
-
- // Actions shown in the menu
- enabledMenuActions() {
- return [
- // Showing inline first for the NcActions inline prop
- ...this.enabledInlineActions,
- // Then the rest
- ...this.enabledActions.filter(action => action.default !== DefaultType.HIDDEN && typeof action.renderInline !== 'function'),
- ].filter((value, index, self) => {
- // Then we filter duplicates to prevent inline actions to be shown twice
- return index === self.findIndex(action => action.id === value.id)
- })
- },
- openedMenu: {
- get() {
- return this.actionsMenuStore.opened === this.uniqueId
- },
- set(opened) {
- this.actionsMenuStore.opened = opened ? this.uniqueId : null
- },
- },
-
- uniqueId() {
- return hashCode(this.source.source)
- },
- isLoading() {
- return this.source.status === NodeStatus.LOADING
- },
-
renameLabel() {
const matchLabel: Record<FileType, string> = {
[FileType.File]: t('files', 'File name'),
@@ -507,6 +415,15 @@ export default Vue.extend({
return (this.source.permissions & Permission.CREATE) !== 0
},
+
+ openedMenu: {
+ get() {
+ return this.actionsMenuStore.opened === this.uniqueId
+ },
+ set(opened) {
+ this.actionsMenuStore.opened = opened ? this.uniqueId : null
+ },
+ },
},
watch: {
@@ -545,67 +462,6 @@ export default Vue.extend({
this.openedMenu = false
},
- async onActionClick(action) {
- const displayName = action.displayName([this.source], this.currentView)
- try {
- // Set the loading marker
- this.loading = action.id
- Vue.set(this.source, 'status', NodeStatus.LOADING)
-
- const success = await action.exec(this.source, this.currentView, this.currentDir)
-
- // If the action returns null, we stay silent
- if (success === null) {
- return
- }
-
- if (success) {
- showSuccess(t('files', '"{displayName}" action executed successfully', { displayName }))
- return
- }
- showError(t('files', '"{displayName}" action failed', { displayName }))
- } catch (e) {
- logger.error('Error while executing action', { action, e })
- showError(t('files', '"{displayName}" action failed', { displayName }))
- } finally {
- // Reset the loading marker
- this.loading = ''
- Vue.set(this.source, 'status', undefined)
- }
- },
- execDefaultAction(event) {
- if (this.enabledDefaultActions.length > 0) {
- event.preventDefault()
- event.stopPropagation()
- // Execute the first default action if any
- this.enabledDefaultActions[0].exec(this.source, this.currentView, this.currentDir)
- }
- },
-
- openDetailsIfAvailable(event) {
- event.preventDefault()
- event.stopPropagation()
- if (sidebarAction?.enabled?.([this.source], this.currentView)) {
- sidebarAction.exec(this.source, this.currentView, this.currentDir)
- }
- },
-
- // 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.uniqueId
-
- // Prevent any browser defaults
- event.preventDefault()
- event.stopPropagation()
- },
-
/**
* Check if the file name is valid and update the
* input validity using browser's native validation.
@@ -749,23 +605,32 @@ export default Vue.extend({
}
},
- /**
- * Making this a function in case the files-list
- * reference changes in the future. That way we're
- * sure there is one at the time we call it.
- */
- getBoundariesElement() {
- return document.querySelector('.app-content > .files-list')
+ // 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.uniqueId
+
+ // Prevent any browser defaults
+ event.preventDefault()
+ event.stopPropagation()
},
- actionDisplayName(action: FileAction) {
- if (this.filesListWidth < 768 && action.inline && typeof action.title === 'function') {
- // if an inline action is rendered in the menu for
- // lack of space we use the title first if defined
- const title = action.title([this.source], this.currentView)
- if (title) return title
+ execDefaultAction() {
+ this.$refs.actions.execDefaultAction()
+ },
+
+ openDetailsIfAvailable(event) {
+ event.preventDefault()
+ event.stopPropagation()
+ if (sidebarAction?.enabled?.([this.source], this.currentView)) {
+ sidebarAction.exec(this.source, this.currentView, this.currentDir)
}
- return action.displayName([this.source], this.currentView)
},
onDragOver(event: DragEvent) {