diff options
author | Ferdinand Thiessen <opensource@fthiessen.de> | 2024-01-21 01:29:24 +0100 |
---|---|---|
committer | Ferdinand Thiessen <opensource@fthiessen.de> | 2024-02-09 01:06:42 +0100 |
commit | 8be4704e1125b6eb6fe17a58c8b54c651fa40c0c (patch) | |
tree | efad38860b0004efb2b3831ad2c4d2d75756dd2c /apps/files/src/components | |
parent | e62c5d719de2a50f944246dee8f0990ccd84beec (diff) | |
download | nextcloud-server-8be4704e1125b6eb6fe17a58c8b54c651fa40c0c.tar.gz nextcloud-server-8be4704e1125b6eb6fe17a58c8b54c651fa40c0c.zip |
enh(files): Add modal to set filename before creating new files in the fileslist
* Reactive `openfile` query
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Diffstat (limited to 'apps/files/src/components')
-rw-r--r-- | apps/files/src/components/FileEntry.vue | 15 | ||||
-rw-r--r-- | apps/files/src/components/FilesListVirtual.vue | 35 | ||||
-rw-r--r-- | apps/files/src/components/NewNodeDialog.vue | 149 |
3 files changed, 190 insertions, 9 deletions
diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue index 0ccd5622a5e..973e1de667f 100644 --- a/apps/files/src/components/FileEntry.vue +++ b/apps/files/src/components/FileEntry.vue @@ -21,7 +21,11 @@ --> <template> - <tr :class="{'files-list__row--dragover': dragover, 'files-list__row--loading': isLoading}" + <tr :class="{ + 'files-list__row--dragover': dragover, + 'files-list__row--loading': isLoading, + 'files-list__row--active': isActive, + }" data-cy-files-list-row :data-cy-files-list-row-fileid="fileid" :data-cy-files-list-row-name="source.basename" @@ -97,7 +101,7 @@ <script lang="ts"> import { defineComponent } from 'vue' -import { formatFileSize } from '@nextcloud/files' +import { Permission, formatFileSize } from '@nextcloud/files' import moment from '@nextcloud/moment' import { useActionsMenuStore } from '../store/actionsmenu.ts' @@ -232,6 +236,13 @@ export default defineComponent({ } return '' }, + + /** + * This entry is the current active node + */ + isActive() { + return this.fileid === this.currentFileId?.toString?.() + }, }, methods: { diff --git a/apps/files/src/components/FilesListVirtual.vue b/apps/files/src/components/FilesListVirtual.vue index afb2dbd888a..b6a11391dc1 100644 --- a/apps/files/src/components/FilesListVirtual.vue +++ b/apps/files/src/components/FilesListVirtual.vue @@ -139,6 +139,7 @@ export default defineComponent({ FileEntryGrid, headers: getFileListHeaders(), scrollToIndex: 0, + openFileId: null as number|null, } }, @@ -151,6 +152,14 @@ export default defineComponent({ return parseInt(this.$route.params.fileid) || null }, + /** + * If the current `fileId` should be opened + * The state of the `openfile` query param + */ + openFile() { + return !!this.$route.query.openfile + }, + summary() { return getSummaryFor(this.nodes) }, @@ -199,6 +208,12 @@ export default defineComponent({ fileId(fileId) { this.scrollToFile(fileId, false) }, + + openFile(open: boolean) { + if (open) { + this.$nextTick(() => this.handleOpenFile(this.fileId)) + } + }, }, mounted() { @@ -206,9 +221,11 @@ export default defineComponent({ const mainContent = window.document.querySelector('main.app-content') as HTMLElement mainContent.addEventListener('dragover', this.onDragOver) - this.scrollToFile(this.fileId) - this.openSidebarForFile(this.fileId) - this.handleOpenFile() + // handle initially opening a given file + const { id } = loadState<{ id?: number }>('files', 'openFileInfo', {}) + this.scrollToFile(id ?? this.fileId) + this.openSidebarForFile(id ?? this.fileId) + this.handleOpenFile(id ?? null) }, beforeDestroy() { @@ -241,18 +258,22 @@ export default defineComponent({ } }, - handleOpenFile() { - const openFileInfo = loadState('files', 'openFileInfo', {}) as ({ id?: number }) - if (openFileInfo === undefined) { + /** + * Handle opening a file (e.g. by ?openfile=true) + * @param fileId File to open + */ + handleOpenFile(fileId: number|null) { + if (fileId === null || this.openFileId === fileId) { return } - const node = this.nodes.find(n => n.fileid === openFileInfo.id) as NcNode + const node = this.nodes.find(n => n.fileid === fileId) as NcNode if (node === undefined || node.type === FileType.Folder) { return } logger.debug('Opening file ' + node.path, { node }) + this.openFileId = fileId getFileActions() .filter(action => !action.enabled || action.enabled([node], this.currentView)) .sort((a, b) => (a.order || 0) - (b.order || 0)) diff --git a/apps/files/src/components/NewNodeDialog.vue b/apps/files/src/components/NewNodeDialog.vue new file mode 100644 index 00000000000..38337ddf4b8 --- /dev/null +++ b/apps/files/src/components/NewNodeDialog.vue @@ -0,0 +1,149 @@ +<!-- + - @copyright Copyright (c) 2024 Ferdinand Thiessen <opensource@fthiessen.de> + - + - @author Ferdinand Thiessen <opensource@fthiessen.de> + - + - @license AGPL-3.0-or-later + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> +<template> + <NcDialog :name="name" + :open="open" + close-on-click-outside + out-transition + @update:open="onClose"> + <template #actions> + <NcButton type="primary" + :disabled="!isUniqueName" + @click="onCreate"> + {{ t('files', 'Create') }} + </NcButton> + </template> + <form @submit.prevent="onCreate"> + <NcTextField ref="input" + :error="!isUniqueName" + :helper-text="errorMessage" + :label="label" + :value.sync="localDefaultName" /> + </form> + </NcDialog> +</template> + +<script lang="ts"> +import type { PropType } from 'vue' + +import { defineComponent } from 'vue' +import { translate as t } from '@nextcloud/l10n' +import { getUniqueName } from '../utils/fileUtils' + +import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' +import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js' +import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' + +interface ICanFocus { + focus: () => void +} + +export default defineComponent({ + name: 'NewNodeDialog', + components: { + NcButton, + NcDialog, + NcTextField, + }, + props: { + /** + * The name to be used by default + */ + defaultName: { + type: String, + default: t('files', 'New folder'), + }, + /** + * Other files that are in the current directory + */ + otherNames: { + type: Array as PropType<string[]>, + default: () => [], + }, + /** + * Open state of the dialog + */ + open: { + type: Boolean, + default: true, + }, + /** + * Dialog name + */ + name: { + type: String, + default: t('files', 'Create new folder'), + }, + /** + * Input label + */ + label: { + type: String, + default: t('files', 'Folder name'), + }, + }, + emits: { + close: (name: string|null) => name === null || name, + }, + data() { + return { + localDefaultName: this.defaultName || t('files', 'New folder'), + } + }, + computed: { + errorMessage() { + if (this.isUniqueName) { + return '' + } else { + return t('files', 'A file or folder with that name already exists.') + } + }, + uniqueName() { + return getUniqueName(this.localDefaultName, this.otherNames) + }, + isUniqueName() { + return this.localDefaultName === this.uniqueName + }, + }, + watch: { + defaultName() { + this.localDefaultName = this.defaultName || t('files', 'New folder') + }, + }, + mounted() { + // on mounted lets use the unique name + this.localDefaultName = this.uniqueName + this.$nextTick(() => (this.$refs.input as unknown as ICanFocus)?.focus?.()) + }, + methods: { + t, + onCreate() { + this.$emit('close', this.localDefaultName) + }, + onClose(state: boolean) { + if (!state) { + this.$emit('close', null) + } + }, + }, +}) +</script> |