Skip to content

Commit c995590

Browse files
authored
Merge pull request #1174 from nextcloud-libraries/refactor/vue
refactor: use css modules and script-setup
2 parents 1c4ba6b + cd380c4 commit c995590

2 files changed

Lines changed: 117 additions & 166 deletions

File tree

src/components/PasswordDialog.vue

Lines changed: 117 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -3,175 +3,137 @@
33
- SPDX-License-Identifier: MIT
44
-->
55

6-
<template>
7-
<NcDialog
8-
:name="t('Authentication required')"
9-
content-classes="vue-password-confirmation"
10-
@update:open="close">
11-
<!-- Dialog content -->
12-
<p>{{ t('This action needs authentication, please confirm it by entering your password.') }}</p>
13-
<form class="vue-password-confirmation__form" @submit.prevent="confirm">
14-
<NcPasswordField
15-
ref="field"
16-
v-model="password"
17-
:label="t('Password')"
18-
:helper-text="helperText"
19-
:error="error !== false"
20-
required />
21-
<NcButton
22-
class="vue-password-confirmation__submit"
23-
variant="primary"
24-
type="submit"
25-
:disabled="!password || loading">
26-
<template v-if="loading" #icon>
27-
<NcLoadingIcon :size="20" />
28-
</template>
29-
{{ t('Confirm') }}
30-
</NcButton>
31-
</form>
32-
</NcDialog>
33-
</template>
34-
35-
<script lang="ts">
6+
<script setup lang="ts">
367
import { isAxiosError } from '@nextcloud/axios'
37-
import { defineComponent } from 'vue'
38-
import NcButton from '@nextcloud/vue/components/NcButton'
8+
import { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'
399
import NcDialog from '@nextcloud/vue/components/NcDialog'
40-
import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
4110
import NcPasswordField from '@nextcloud/vue/components/NcPasswordField'
4211
import { t } from '../utils/l10n.js'
4312
import { logger } from '../utils/logger.js'
4413
45-
type ICanFocus = {
46-
focus: () => void
47-
select: () => void
48-
}
14+
type DialogButtons = InstanceType<typeof NcDialog>['$props']['buttons']
15+
16+
const props = defineProps<{
17+
/**
18+
* Function to call to validate password
19+
*/
20+
validate: (password: string) => Promise<void> | void
21+
}>()
22+
23+
const emit = defineEmits<{
24+
close: [confirmed: boolean]
25+
}>()
26+
27+
onMounted(focusPasswordField)
28+
29+
const passwordInput = useTemplateRef('field')
4930
50-
export default defineComponent({
51-
name: 'PasswordDialog',
52-
53-
components: {
54-
NcButton,
55-
NcDialog,
56-
NcLoadingIcon,
57-
NcPasswordField,
58-
},
59-
60-
props: {
61-
/**
62-
* Function to call to validate password
63-
*/
64-
validate: {
65-
type: Function,
66-
required: true,
67-
},
68-
},
69-
70-
emits: ['close'],
71-
72-
data() {
73-
return {
74-
password: '',
75-
loading: false,
76-
error: false as boolean | 403,
31+
const password = ref('')
32+
const loading = ref(false)
33+
const hasError = ref<boolean | 403>(false)
34+
35+
const buttons: DialogButtons = [{
36+
label: t('Confirm'),
37+
type: 'submit',
38+
variant: 'primary',
39+
callback,
40+
}]
41+
42+
const helperText = computed(() => {
43+
if (hasError.value !== false) {
44+
if (password.value === '') {
45+
return t('Please enter your password')
46+
}
47+
48+
switch (hasError.value) {
49+
case true:
50+
return t('Unknown error while checking password')
51+
case 403:
52+
return t('Wrong password')
7753
}
78-
},
79-
80-
computed: {
81-
helperText() {
82-
if (this.error !== false) {
83-
if (this.password === '') {
84-
return t('Please enter your password')
85-
}
86-
87-
switch (this.error) {
88-
case true:
89-
return t('Unknown error while checking password')
90-
case 403:
91-
return t('Wrong password')
92-
}
93-
}
94-
95-
if (this.loading) {
96-
return t('Checking password …') // TRANSLATORS: This is a status message, shown when the system is checking the users password
97-
}
98-
99-
return ''
100-
},
101-
},
102-
103-
mounted() {
104-
this.focusPasswordField()
105-
},
106-
107-
methods: {
108-
t,
109-
110-
async confirm(): Promise<void> {
111-
this.error = false
112-
this.loading = true
113-
114-
if (this.password === '') {
115-
this.error = true
116-
return
117-
}
118-
119-
try {
120-
await this.validate(this.password)
121-
this.$emit('close', true)
122-
} catch (error) {
123-
if (isAxiosError(error) && error.response?.status === 403) {
124-
this.error = 403
125-
} else {
126-
this.error = true
127-
}
128-
129-
logger.error('Exception during password confirmation', { error })
130-
this.selectPasswordField()
131-
} finally {
132-
this.loading = false
133-
}
134-
},
135-
136-
close(open: boolean): void {
137-
if (!open) {
138-
this.$emit('close', false)
139-
}
140-
},
141-
142-
focusPasswordField() {
143-
this.$nextTick(() => {
144-
(this.$refs.field as ICanFocus).focus()
145-
})
146-
},
147-
148-
selectPasswordField() {
149-
this.$nextTick(() => {
150-
(this.$refs.field as ICanFocus).select()
151-
})
152-
},
153-
},
54+
}
55+
56+
if (loading.value) {
57+
return t('Checking password …') // TRANSLATORS: This is a status message, shown when the system is checking the users password
58+
}
59+
60+
return ''
15461
})
62+
63+
/**
64+
* Handle confirm button click
65+
*/
66+
async function callback(): Promise<boolean> {
67+
hasError.value = false
68+
loading.value = true
69+
70+
if (password.value === '') {
71+
hasError.value = true
72+
return false
73+
}
74+
75+
try {
76+
await props.validate(password.value)
77+
emit('close', true)
78+
return true
79+
} catch (error) {
80+
if (isAxiosError(error) && error.response?.status === 403) {
81+
hasError.value = 403
82+
} else {
83+
hasError.value = true
84+
}
85+
86+
logger.error('Exception during password confirmation', { error })
87+
selectPasswordField()
88+
return false
89+
} finally {
90+
loading.value = false
91+
}
92+
}
93+
94+
/**
95+
* Focus the password field
96+
*/
97+
function focusPasswordField() {
98+
nextTick(() => {
99+
passwordInput.value!.focus()
100+
})
101+
}
102+
103+
/**
104+
* Select the password field
105+
*/
106+
function selectPasswordField() {
107+
nextTick(() => {
108+
passwordInput.value!.select()
109+
})
110+
}
155111
</script>
156112

157-
<style lang="scss">
158-
.vue-password-confirmation {
113+
<template>
114+
<NcDialog
115+
is-form
116+
:buttons
117+
:name="t('Authentication required')"
118+
:content-classes="$style.passwordDialog"
119+
@update:open="emit('close', false)">
120+
<p>{{ t('This action needs authentication, please confirm it by entering your password.') }}</p>
121+
<NcPasswordField
122+
ref="field"
123+
v-model="password"
124+
:label="t('Password')"
125+
:helper-text="helperText"
126+
:error="hasError !== false"
127+
required />
128+
</NcDialog>
129+
</template>
130+
131+
<style module>
132+
.passwordDialog {
159133
display: flex;
160134
flex-direction: column;
135+
gap: 10px 0;
161136
margin-inline: 6px;
162137
margin-block-end: 6px;
163-
gap: 10px 0;
164-
165-
&__form {
166-
display: flex;
167-
flex-direction: column;
168-
gap: 8px 0;
169-
// allow focus visible outlines
170-
padding: 2px;
171-
}
172-
173-
&__submit {
174-
align-self: end;
175-
}
176138
}
177139
</style>

src/vue-shims.d.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)