diff options
author | John Molakvoæ <skjnldsv@protonmail.com> | 2023-04-18 09:43:29 +0200 |
---|---|---|
committer | John Molakvoæ <skjnldsv@protonmail.com> | 2023-04-20 09:06:57 +0200 |
commit | bb4d7969b93c806e4f578ecc5a6d04bb6bebee73 (patch) | |
tree | e6247c554d7e136042e0913f370f37ff1467ac37 /apps/files/src/components/FileEntry.vue | |
parent | c85c04e4a8495eb04419a27a8e162c03acad6282 (diff) | |
download | nextcloud-server-bb4d7969b93c806e4f578ecc5a6d04bb6bebee73.tar.gz nextcloud-server-bb4d7969b93c806e4f578ecc5a6d04bb6bebee73.zip |
feat(files): add default action support
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
Diffstat (limited to 'apps/files/src/components/FileEntry.vue')
-rw-r--r-- | apps/files/src/components/FileEntry.vue | 93 |
1 files changed, 67 insertions, 26 deletions
diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue index 7db22482220..00ff8a3d533 100644 --- a/apps/files/src/components/FileEntry.vue +++ b/apps/files/src/components/FileEntry.vue @@ -33,7 +33,7 @@ <!-- Link to file --> <td class="files-list__row-name"> - <a ref="name" v-bind="linkTo"> + <a ref="name" v-bind="linkTo" @click="execDefaultAction"> <!-- Icon or preview --> <span class="files-list__row-icon"> <FolderIcon v-if="source.type === 'folder'" /> @@ -49,6 +49,13 @@ :style="{ backgroundImage: mimeIconUrl }" /> <FileIcon v-else /> + + <!-- Favorite icon --> + <span v-if="isFavorite" + class="files-list__row-icon-favorite" + :aria-label="t('files', 'Favorite')"> + <StarIcon aria-hidden="true" :size="20" /> + </span> </span> <!-- File name --> @@ -64,6 +71,8 @@ <!-- Menu actions --> <NcActions v-if="active" ref="actionsMenu" + :boundaries-element="boundariesElement" + :container="boundariesElement" :disabled="source._loading" :force-title="true" :inline="enabledInlineActions.length" @@ -84,7 +93,8 @@ <!-- Size --> <td v-if="isSizeAvailable" :style="{ opacity: sizeOpacity }" - class="files-list__row-size"> + class="files-list__row-size" + @click="execDefaultAction"> <span>{{ size }}</span> </td> @@ -92,7 +102,8 @@ <td v-for="column in columns" :key="column.id" :class="`files-list__row-${currentView?.id}-${column.id}`" - class="files-list__row-column-custom"> + class="files-list__row-column-custom" + @click="execDefaultAction"> <CustomElementRender v-if="active" :current-view="currentView" :render="column.render" @@ -115,9 +126,11 @@ import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js' import NcActions from '@nextcloud/vue/dist/Components/NcActions.js' import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js' +import StarIcon from 'vue-material-design-icons/Star.vue' import Vue from 'vue' import { getFileActions } from '../services/FileAction.ts' +import { hashCode } from '../utils/hashUtils.ts' import { isCachedPreview } from '../services/PreviewService.ts' import { useActionsMenuStore } from '../store/actionsmenu.ts' import { useFilesStore } from '../store/files.ts' @@ -144,6 +157,7 @@ export default Vue.extend({ NcActions, NcCheckboxRadioSwitch, NcLoadingIcon, + StarIcon, }, props: { @@ -192,6 +206,7 @@ export default Vue.extend({ return { backgroundFailed: false, backgroundImage: '', + boundariesElement: document.querySelector('.app-content > .files-list'), loading: '', } }, @@ -204,7 +219,6 @@ export default Vue.extend({ currentView() { return this.$navigation.active }, - columns() { // Hide columns if the list is too small if (this.filesListWidth < 512) { @@ -217,7 +231,6 @@ export default Vue.extend({ // Remove any trailing slash but leave root slash return (this.$route?.query?.dir || '/').replace(/^(.+)\/$/, '$1') }, - fileid() { return this.source?.fileid?.toString?.() }, @@ -225,6 +238,7 @@ export default Vue.extend({ return this.source.attributes.displayName || this.source.basename }, + size() { const size = parseInt(this.source.size, 10) || 0 if (typeof size !== 'number' || size < 0) { @@ -232,7 +246,6 @@ export default Vue.extend({ } return formatFileSize(size, true) }, - sizeOpacity() { const size = parseInt(this.source.size, 10) || 0 if (!size || size < 0) { @@ -247,6 +260,15 @@ export default Vue.extend({ }, linkTo() { + if (this.enabledDefaultActions.length > 0) { + const action = this.enabledDefaultActions[0] + const displayName = action.displayName([this.source], this.currentView) + return { + title: displayName, + role: 'button', + } + } + if (this.source.type === 'folder') { const to = { ...this.$route, query: { dir: join(this.dir, this.source.basename) } } return { @@ -272,7 +294,6 @@ export default Vue.extend({ cropPreviews() { return this.userConfig.crop_image_previews }, - previewUrl() { try { const url = new URL(window.location.origin + this.source.attributes.previewUrl) @@ -280,13 +301,12 @@ export default Vue.extend({ url.searchParams.set('x', '32') url.searchParams.set('y', '32') // Handle cropping - url.searchParams.set('a', this.cropPreviews === true ? '1' : '0') + url.searchParams.set('a', this.cropPreviews === true ? '0' : '1') return url.href } catch (e) { return null } }, - mimeIconUrl() { const mimeType = this.source.mime || 'application/octet-stream' const mimeIconUrl = window.OC?.MimeType?.getIconUrl?.(mimeType) @@ -301,29 +321,38 @@ export default Vue.extend({ .filter(action => !action.enabled || action.enabled([this.source], this.currentView)) .sort((a, b) => (a.order || 0) - (b.order || 0)) }, - enabledInlineActions() { if (this.filesListWidth < 768) { return [] } return this.enabledActions.filter(action => action?.inline?.(this.source, this.currentView)) }, - enabledMenuActions() { if (this.filesListWidth < 768) { + // If we have a default action, do not render the first one + if (this.enabledDefaultActions.length > 0) { + return this.enabledActions.slice(1) + } return this.enabledActions } - return [ + const actions = [ ...this.enabledInlineActions, ...this.enabledActions.filter(action => !action.inline), ] - }, - uniqueId() { - return this.hashCode(this.source.source) - }, + // If we have a default action, do not render the first one + if (this.enabledDefaultActions.length > 0) { + return actions.slice(1) + } + return actions + }, + enabledDefaultActions() { + return [ + ...this.enabledActions.filter(action => action.default), + ] + }, openedMenu: { get() { return this.actionsMenuStore.opened === this.uniqueId @@ -332,6 +361,14 @@ export default Vue.extend({ this.actionsMenuStore.opened = opened ? this.uniqueId : null }, }, + + uniqueId() { + return hashCode(this.source.source) + }, + + isFavorite() { + return this.source.attributes.favorite === 1 + }, }, watch: { @@ -457,16 +494,6 @@ export default Vue.extend({ } }, - hashCode(str) { - let hash = 0 - for (let i = 0, len = str.length; i < len; i++) { - const chr = str.charCodeAt(i) - hash = (hash << 5) - hash + chr - hash |= 0 // Convert to 32bit integer - } - return hash - }, - async onActionClick(action) { const displayName = action.displayName([this.source], this.currentView) try { @@ -475,6 +502,12 @@ export default Vue.extend({ Vue.set(this.source, '_loading', true) const success = await action.exec(this.source, this.currentView) + + // If the action returns null, we stay silent + if (success === null) { + return + } + if (success) { showSuccess(this.t('files', '"{displayName}" action executed successfully', { displayName })) return @@ -489,6 +522,14 @@ export default Vue.extend({ Vue.set(this.source, '_loading', false) } }, + 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) + } + }, onSelectionChange(selection) { const newSelectedIndex = this.index |