aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2023-11-08 08:53:16 +0100
committerGitHub <noreply@github.com>2023-11-08 08:53:16 +0100
commit069eb070a95e65e0468a2679e1a35f5408b10023 (patch)
tree26b047d37a3c3276e9336936f0d5c8bbde949869 /apps/files/src
parent1d568a89a45456d9405f347d57b2caf9a80ad179 (diff)
parent32c1aebaae07b9b4d7e2dcdfca6b2cbd36a6cfc3 (diff)
downloadnextcloud-server-069eb070a95e65e0468a2679e1a35f5408b10023.tar.gz
nextcloud-server-069eb070a95e65e0468a2679e1a35f5408b10023.zip
Merge pull request #41010 from nextcloud/feat/nested-actins
Diffstat (limited to 'apps/files/src')
-rw-r--r--apps/files/src/components/FileEntry/FileEntryActions.vue94
-rw-r--r--apps/files/src/components/FileEntry/FileEntryPreview.vue1
-rw-r--r--apps/files/src/components/NavigationQuota.vue4
-rw-r--r--apps/files/src/init.ts3
-rw-r--r--apps/files/src/sidebar.js2
5 files changed, 92 insertions, 12 deletions
diff --git a/apps/files/src/components/FileEntry/FileEntryActions.vue b/apps/files/src/components/FileEntry/FileEntryActions.vue
index 688524962f5..5a1b3235d90 100644
--- a/apps/files/src/components/FileEntry/FileEntryActions.vue
+++ b/apps/files/src/components/FileEntry/FileEntryActions.vue
@@ -39,12 +39,18 @@
:force-name="true"
:force-menu="enabledInlineActions.length === 0 /* forceMenu only if no inline actions */"
:inline="enabledInlineActions.length"
- :open.sync="openedMenu">
+ :open.sync="openedMenu"
+ @close="openedSubmenu = null">
+ <!-- Default actions list-->
<NcActionButton v-for="action in enabledMenuActions"
:key="action.id"
- :class="'files-list__row-action-' + action.id"
- :close-after-click="true"
+ :class="{
+ [`files-list__row-action-${action.id}`]: true,
+ [`files-list__row-action--menu`]: isMenu(action.id)
+ }"
+ :close-after-click="!isMenu(action.id)"
:data-cy-files-list-row-action="action.id"
+ :is-menu="isMenu(action.id)"
:title="action.title?.([source], currentView)"
@click="onActionClick(action)">
<template #icon>
@@ -53,6 +59,34 @@
</template>
{{ actionDisplayName(action) }}
</NcActionButton>
+
+ <!-- Submenu actions list-->
+ <template v-if="openedSubmenu && enabledSubmenuActions[openedSubmenu?.id]">
+ <!-- Back to top-level button -->
+ <NcActionButton class="files-list__row-action-back" @click="openedSubmenu = null">
+ <template #icon>
+ <ArrowLeftIcon />
+ </template>
+ {{ actionDisplayName(openedSubmenu) }}
+ </NcActionButton>
+ <NcActionSeparator />
+
+ <!-- Submenu actions -->
+ <NcActionButton v-for="action in enabledSubmenuActions[openedSubmenu?.id]"
+ :key="action.id"
+ :class="`files-list__row-action-${action.id}`"
+ class="files-list__row-action--submenu"
+ :close-after-click="false /* never close submenu, just go back */"
+ :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>
+ </template>
</NcActions>
</td>
</template>
@@ -63,8 +97,11 @@ import { showError, showSuccess } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n';
import Vue, { PropType } from 'vue'
+import ArrowLeftIcon from 'vue-material-design-icons/ArrowLeft.vue'
+import ChevronRightIcon from 'vue-material-design-icons/ChevronRight.vue'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
+import NcActionSeparator from '@nextcloud/vue/dist/Components/NcActionSeparator.js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
@@ -78,11 +115,14 @@ export default Vue.extend({
name: 'FileEntryActions',
components: {
+ ArrowLeftIcon,
+ ChevronRightIcon,
+ CustomElementRender,
NcActionButton,
NcActions,
+ NcActionSeparator,
NcIconSvgWrapper,
NcLoadingIcon,
- CustomElementRender,
},
props: {
@@ -108,8 +148,9 @@ export default Vue.extend({
},
},
- setup() {
+ data() {
return {
+ openedSubmenu: null as FileAction | null,
}
},
@@ -159,7 +200,13 @@ export default Vue.extend({
// Actions shown in the menu
enabledMenuActions() {
- return [
+ // If we're in a submenu, only render the inline
+ // actions before the filtered submenu
+ if (this.openedSubmenu) {
+ return this.enabledInlineActions
+ }
+
+ const actions = [
// Showing inline first for the NcActions inline prop
...this.enabledInlineActions,
// Then the rest
@@ -168,6 +215,24 @@ export default Vue.extend({
// Then we filter duplicates to prevent inline actions to be shown twice
return index === self.findIndex(action => action.id === value.id)
})
+
+ // Generate list of all top-level actions ids
+ const topActionsIds = actions.filter(action => !action.parent).map(action => action.id) as string[]
+
+ // Filter actions that are not top-level AND have a valid parent
+ return actions.filter(action => !(action.parent && topActionsIds.includes(action.parent)))
+ },
+
+ enabledSubmenuActions() {
+ return this.enabledActions
+ .filter(action => action.parent)
+ .reduce((arr, action) => {
+ if (!arr[action.parent]) {
+ arr[action.parent] = []
+ }
+ arr[action.parent].push(action)
+ return arr
+ }, {} as Record<string, FileAction>)
},
openedMenu: {
@@ -200,7 +265,13 @@ export default Vue.extend({
return action.displayName([this.source], this.currentView)
},
- async onActionClick(action) {
+ async onActionClick(action, isSubmenu = false) {
+ // If the action is a submenu, we open it
+ if (this.enabledSubmenuActions[action.id]) {
+ this.openedSubmenu = action
+ return
+ }
+
const displayName = action.displayName([this.source], this.currentView)
try {
// Set the loading marker
@@ -226,6 +297,11 @@ export default Vue.extend({
// Reset the loading marker
this.$emit('update:loading', '')
Vue.set(this.source, 'status', undefined)
+
+ // If that was a submenu, we just go back after the action
+ if (isSubmenu) {
+ this.openedSubmenu = null
+ }
}
},
execDefaultAction(event) {
@@ -237,6 +313,10 @@ export default Vue.extend({
}
},
+ isMenu(id: string) {
+ return this.enabledSubmenuActions[id]?.length > 0
+ },
+
t,
},
})
diff --git a/apps/files/src/components/FileEntry/FileEntryPreview.vue b/apps/files/src/components/FileEntry/FileEntryPreview.vue
index 8a7af255ec2..cb7cee7054b 100644
--- a/apps/files/src/components/FileEntry/FileEntryPreview.vue
+++ b/apps/files/src/components/FileEntry/FileEntryPreview.vue
@@ -37,6 +37,7 @@
alt=""
class="files-list__row-icon-preview"
:class="{'files-list__row-icon-preview--loaded': backgroundFailed === false}"
+ loading="lazy"
:src="previewUrl"
@error="backgroundFailed = true"
@load="backgroundFailed = false">
diff --git a/apps/files/src/components/NavigationQuota.vue b/apps/files/src/components/NavigationQuota.vue
index 25bdcde1b45..18cd99f248b 100644
--- a/apps/files/src/components/NavigationQuota.vue
+++ b/apps/files/src/components/NavigationQuota.vue
@@ -51,8 +51,8 @@ export default {
computed: {
storageStatsTitle() {
- const usedQuotaByte = formatFileSize(this.storageStats?.used, false, false)
- const quotaByte = formatFileSize(this.storageStats?.quota, false, false)
+ const usedQuotaByte = formatFileSize(this.storageStats?.used, false, false, true)
+ const quotaByte = formatFileSize(this.storageStats?.quota, false, false, true)
// If no quota set
if (this.storageStats?.quota < 0) {
diff --git a/apps/files/src/init.ts b/apps/files/src/init.ts
index 9cbf3dc2e69..430b17ae7ae 100644
--- a/apps/files/src/init.ts
+++ b/apps/files/src/init.ts
@@ -19,7 +19,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-import { addNewFileMenuEntry, registerFileAction } from '@nextcloud/files'
+import MenuIcon from '@mdi/svg/svg/sun-compass.svg?raw'
+import { FileAction, addNewFileMenuEntry, registerFileAction } from '@nextcloud/files'
import { action as deleteAction } from './actions/deleteAction'
import { action as downloadAction } from './actions/downloadAction'
diff --git a/apps/files/src/sidebar.js b/apps/files/src/sidebar.js
index 3cdb8c4fb0b..c8bfc2ca4db 100644
--- a/apps/files/src/sidebar.js
+++ b/apps/files/src/sidebar.js
@@ -36,8 +36,6 @@ if (!window.OCA.Files) {
Object.assign(window.OCA.Files, { Sidebar: new Sidebar() })
Object.assign(window.OCA.Files.Sidebar, { Tab })
-console.debug('OCA.Files.Sidebar initialized')
-
window.addEventListener('DOMContentLoaded', function() {
const contentElement = document.querySelector('body > .content')
|| document.querySelector('body > #content')