diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2023-11-08 08:53:16 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-08 08:53:16 +0100 |
commit | 069eb070a95e65e0468a2679e1a35f5408b10023 (patch) | |
tree | 26b047d37a3c3276e9336936f0d5c8bbde949869 /apps/files/src | |
parent | 1d568a89a45456d9405f347d57b2caf9a80ad179 (diff) | |
parent | 32c1aebaae07b9b4d7e2dcdfca6b2cbd36a6cfc3 (diff) | |
download | nextcloud-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.vue | 94 | ||||
-rw-r--r-- | apps/files/src/components/FileEntry/FileEntryPreview.vue | 1 | ||||
-rw-r--r-- | apps/files/src/components/NavigationQuota.vue | 4 | ||||
-rw-r--r-- | apps/files/src/init.ts | 3 | ||||
-rw-r--r-- | apps/files/src/sidebar.js | 2 |
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') |