-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
NewNodeDialog.vue
157 lines (144 loc) · 3.74 KB
/
NewNodeDialog.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<!--
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<NcDialog data-cy-files-new-node-dialog
:name="name"
:open="open"
close-on-click-outside
out-transition
@update:open="emit('close', null)">
<template #actions>
<NcButton data-cy-files-new-node-dialog-submit
type="primary"
:disabled="validity !== ''"
@click="submit">
{{ t('files', 'Create') }}
</NcButton>
</template>
<form ref="formElement"
class="new-node-dialog__form"
@submit.prevent="emit('close', localDefaultName)">
<NcTextField ref="nameInput"
data-cy-files-new-node-dialog-input
:error="validity !== ''"
:helper-text="validity"
:label="label"
:value.sync="localDefaultName" />
</form>
</NcDialog>
</template>
<script setup lang="ts">
import type { ComponentPublicInstance, PropType } from 'vue'
import { getUniqueName } from '@nextcloud/files'
import { t } from '@nextcloud/l10n'
import { extname } from 'path'
import { nextTick, onMounted, ref, watch, watchEffect } from 'vue'
import { getFilenameValidity } from '../utils/filenameValidity.ts'
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'
const props = defineProps({
/**
* 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'),
},
})
const emit = defineEmits<{
(event: 'close', name: string | null): void
}>()
const localDefaultName = ref<string>(props.defaultName)
const nameInput = ref<ComponentPublicInstance>()
const formElement = ref<HTMLFormElement>()
const validity = ref('')
/**
* Focus the filename input field
*/
function focusInput() {
nextTick(() => {
// get the input element
const input = nameInput.value?.$el.querySelector('input')
if (!props.open || !input) {
return
}
// length of the basename
const length = localDefaultName.value.length - extname(localDefaultName.value).length
// focus the input
input.focus()
// and set the selection to the basename (name without extension)
input.setSelectionRange(0, length)
})
}
/**
* Trigger submit on the form
*/
function submit() {
formElement.value?.requestSubmit()
}
// Reset local name on props change
watch(() => props.defaultName, () => {
localDefaultName.value = getUniqueName(props.defaultName, props.otherNames)
})
// Validate the local name
watchEffect(() => {
if (props.otherNames.includes(localDefaultName.value)) {
validity.value = t('files', 'This name is already in use.')
} else {
validity.value = getFilenameValidity(localDefaultName.value)
}
const input = nameInput.value?.$el.querySelector('input')
if (input) {
input.setCustomValidity(validity.value)
input.reportValidity()
}
})
// Ensure the input is focussed even if the dialog is already mounted but not open
watch(() => props.open, () => {
nextTick(() => {
focusInput()
})
})
onMounted(() => {
// on mounted lets use the unique name
localDefaultName.value = getUniqueName(localDefaultName.value, props.otherNames)
nextTick(() => focusInput())
})
</script>
<style scoped>
.new-node-dialog__form {
/* Ensure the dialog does not jump when there is a validity error */
min-height: calc(3 * var(--default-clickable-area));
}
</style>