diff --git a/apps/files/src/components/FileEntry/FileEntryActions.vue b/apps/files/src/components/FileEntry/FileEntryActions.vue
index bd4649cdee568..3997cfc58b2ae 100644
--- a/apps/files/src/components/FileEntry/FileEntryActions.vue
+++ b/apps/files/src/components/FileEntry/FileEntryActions.vue
@@ -39,20 +39,56 @@
:force-name="true"
:force-menu="enabledInlineActions.length === 0 /* forceMenu only if no inline actions */"
:inline="enabledInlineActions.length"
- :open.sync="openedMenu">
-
-
-
-
-
- {{ actionDisplayName(action) }}
-
+ :open.sync="openedMenu"
+ @close="openedSubmenu = null">
+
+
+
+
+
+
+
+
+ {{ actionDisplayName(action) }}
+
+
+
+
+
+
+
+
+
+
+ {{ t('files', 'Back') }}
+
+
+
+
+
+
@@ -63,8 +99,11 @@ import { showError, showSuccess } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n';
import Vue, { PropType } from 'vue'
+import ChevronLeftIcon from 'vue-material-design-icons/ChevronLeft.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 +117,14 @@ export default Vue.extend({
name: 'FileEntryActions',
components: {
+ ChevronLeftIcon,
+ ChevronRightIcon,
+ CustomElementRender,
NcActionButton,
NcActions,
+ NcActionSeparator,
NcIconSvgWrapper,
NcLoadingIcon,
- CustomElementRender,
},
props: {
@@ -108,8 +150,9 @@ export default Vue.extend({
},
},
- setup() {
+ data() {
return {
+ openedSubmenu: '',
}
},
@@ -159,7 +202,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 +217,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)
},
openedMenu: {
@@ -201,6 +268,12 @@ export default Vue.extend({
},
async onActionClick(action) {
+ // If the action is a submenu, we open it
+ if (this.enabledSubmenuActions[action.id]) {
+ this.openedSubmenu = action.id
+ return
+ }
+
const displayName = action.displayName([this.source], this.currentView)
try {
// Set the loading marker
@@ -237,6 +310,10 @@ export default Vue.extend({
}
},
+ isMenu(id: string) {
+ return this.enabledSubmenuActions[id]?.length > 0
+ },
+
t,
},
})
diff --git a/apps/files/src/init.ts b/apps/files/src/init.ts
index 9cbf3dc2e69de..db6803101ba5b 100644
--- a/apps/files/src/init.ts
+++ b/apps/files/src/init.ts
@@ -19,7 +19,8 @@
* along with this program. If not, see .
*
*/
-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'
@@ -62,3 +63,39 @@ registerRecentView()
// Register preview service worker
registerPreviewServiceWorker()
+
+registerFileAction(new FileAction({
+ id: 'menu',
+ displayName: () => 'Menu',
+ iconSvgInline: () => MenuIcon,
+ exec: async () => true,
+}))
+
+registerFileAction(new FileAction({
+ id: 'submenu1',
+ displayName: () => 'Submenu 1',
+ iconSvgInline: () => MenuIcon,
+ exec: async () => alert('Hello 1'),
+ parent: 'menu',
+}))
+registerFileAction(new FileAction({
+ id: 'submenu2',
+ displayName: () => 'Submenu 2',
+ iconSvgInline: () => MenuIcon,
+ exec: async () => alert('Hello 2'),
+ parent: 'menu',
+}))
+registerFileAction(new FileAction({
+ id: 'submenu3',
+ displayName: () => 'Submenu 3',
+ iconSvgInline: () => MenuIcon,
+ exec: async () => alert('Hello 3'),
+ parent: 'menu',
+}))
+registerFileAction(new FileAction({
+ id: 'submenu4',
+ displayName: () => 'Submenu 4',
+ iconSvgInline: () => MenuIcon,
+ exec: async () => alert('Hello 4'),
+ parent: 'menu',
+}))