Skip to content

Commit

Permalink
enh(files): Add modal to set filename before creating new files in th…
Browse files Browse the repository at this point in the history
…e fileslist

* Reactive `openfile` query

Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Feb 8, 2024
1 parent 3b77df9 commit 0d412b9
Show file tree
Hide file tree
Showing 10 changed files with 469 additions and 180 deletions.
15 changes: 13 additions & 2 deletions apps/files/src/components/FileEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -232,6 +236,13 @@ export default defineComponent({
}
return ''
},

/**
* This entry is the current active node
*/
isActive() {
return this.fileid === this.currentFileId?.toString?.()
},
},

methods: {
Expand Down
35 changes: 28 additions & 7 deletions apps/files/src/components/FilesListVirtual.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export default defineComponent({
FileEntryGrid,
headers: getFileListHeaders(),
scrollToIndex: 0,
openFileId: null as number|null,
}
},

Expand All @@ -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)
},
Expand Down Expand Up @@ -199,16 +208,24 @@ export default defineComponent({
fileId(fileId) {
this.scrollToFile(fileId, false)
},

openFile(open: boolean) {
if (open) {
this.$nextTick(() => this.handleOpenFile(this.fileId))
}
},
},

mounted() {
// Add events on parent to cover both the table and DragAndDrop notice
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() {
Expand Down Expand Up @@ -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))
Expand Down
149 changes: 149 additions & 0 deletions apps/files/src/components/NewNodeDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<!--
- @copyright Copyright (c) 2024 Ferdinand Thiessen <opensource@fthiessen.de>
-
- @author Ferdinand Thiessen <[email protected]>
-
- @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>
Loading

0 comments on commit 0d412b9

Please sign in to comment.