Skip to content

Commit

Permalink
feat(files): support nested actions
Browse files Browse the repository at this point in the history
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
  • Loading branch information
skjnldsv committed Oct 20, 2023
1 parent 694352e commit 9dbac0d
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 18 deletions.
111 changes: 94 additions & 17 deletions apps/files/src/components/FileEntry/FileEntryActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,56 @@
: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>
:open.sync="openedMenu"
@close="openedSubmenu = null">
<!-- Default actions list-->
<template>
<NcActionButton v-for="action in enabledMenuActions"
:key="action.id"
: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"
:title="action.title?.([source], currentView)"
@click="onActionClick(action)">
<template #icon>
<ChevronRightIcon v-if="isMenu(action.id)" />
<NcLoadingIcon v-else-if="loading === action.id" :size="18" />
<NcIconSvgWrapper v-else :svg="action.iconSvgInline([source], currentView)" />
</template>
{{ actionDisplayName(action) }}
</NcActionButton>
</template>

<!-- Submenu actions list-->
<template v-if="openedSubmenu && enabledSubmenuActions[openedSubmenu]">
<!-- Back to top-level button -->
<NcActionButton class="files-list__row-action-back" @click="openedSubmenu = ''">
<template #icon>
<ChevronLeftIcon />
</template>
{{ t('files', 'Back') }}
</NcActionButton>
<NcActionSeparator />

<!-- Submenu actions -->
<NcActionButton v-for="action in enabledSubmenuActions[openedSubmenu]"
:key="action.id"
:class="`files-list__row-action-${action.id}`"
class="files-list__row-action--submenu"
: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>
</template>
</NcActions>
</td>
</template>
Expand All @@ -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'

Expand All @@ -78,11 +117,14 @@ export default Vue.extend({
name: 'FileEntryActions',

components: {
ChevronLeftIcon,
ChevronRightIcon,
CustomElementRender,
NcActionButton,
NcActions,
NcActionSeparator,
NcIconSvgWrapper,
NcLoadingIcon,
CustomElementRender,
},

props: {
Expand All @@ -108,8 +150,9 @@ export default Vue.extend({
},
},

setup() {
data() {
return {
openedSubmenu: '',
}
},

Expand Down Expand Up @@ -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
Expand All @@ -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<string, FileAction>)
},

openedMenu: {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -237,6 +310,10 @@ export default Vue.extend({
}
},

isMenu(id: string) {
return this.enabledSubmenuActions[id]?.length > 0
},

t,
},
})
Expand Down
39 changes: 38 additions & 1 deletion apps/files/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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',
}))

0 comments on commit 9dbac0d

Please sign in to comment.