diff options
author | Carl Schwan <carl@carlschwan.eu> | 2022-09-01 02:11:25 +0300 |
---|---|---|
committer | Carl Schwan <carl@carlschwan.eu> | 2022-09-05 12:34:00 +0300 |
commit | df40fc91729ebd66941705637b440033036c8b8e (patch) | |
tree | a5090342568897e5a02c2794af9e215cdae5ea60 | |
parent | f496e471e06d546179e3f12af1e6cc9319db9bb3 (diff) |
Use new vue components in login form
- Improve accessibility
- Simply code
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
-rw-r--r-- | core/css/guest.css | 42 | ||||
-rw-r--r-- | core/src/components/login/LoginForm.vue | 134 | ||||
-rw-r--r-- | core/src/components/login/ResetPassword.vue | 58 | ||||
-rw-r--r-- | core/src/views/Login.vue | 17 |
4 files changed, 102 insertions, 149 deletions
diff --git a/core/css/guest.css b/core/css/guest.css index 813e04db213..5ef3d865694 100644 --- a/core/css/guest.css +++ b/core/css/guest.css @@ -108,14 +108,6 @@ form { margin: auto; padding: 0; } -form fieldset { - width: 260px; - margin-top: 8px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} form #sqliteInformation { margin-top: 0px; margin-bottom: 20px; @@ -160,9 +152,6 @@ form #datadirField legend { .wrapper { margin-top: 0; } - .alternative-logins { - margin: auto; - } } @@ -262,9 +251,6 @@ input[type='email'] { color: var(--color-text-lighter); cursor: text; font-family: inherit; - -webkit-appearance: textfield; - -moz-appearance: textfield; - box-sizing: content-box; font-weight: normal; margin-left: 0; margin-right: 0; @@ -491,34 +477,6 @@ form .warning input[type='checkbox']+label { color: var(--color-primary-text); } -/* Alternative Logins */ -.alternative-logins legend { - margin-bottom: 10px; -} -.alternative-logins li { - height: 40px; - white-space: nowrap; - padding: 05px; -} -.alternative-logins a.button, -.alternative-logins li a { - width: 100%; - display: inline-block; - text-align: center; - box-sizing: border-box; - border: 2px solid var(--color-primary-text); - background-color: var(--color-primary); - color: var(--color-primary-text); - border-radius: 100px; /* --border-radius-pill */ -} - -.alternative-logins a.button:focus, -.alternative-logins li a:focus { - border: 2px solid var(--color-primary-hover); - background-image: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-light) 100%); - background-position: initial; -} - /* fixes for update page TODO should be fixed some time in a proper way */ /* this is just for an error while updating the ownCloud instance */ .updateProgress .error { diff --git a/core/src/components/login/LoginForm.vue b/core/src/components/login/LoginForm.vue index 88a465d1aa5..6d33d37d5c8 100644 --- a/core/src/components/login/LoginForm.vue +++ b/core/src/components/login/LoginForm.vue @@ -21,11 +21,12 @@ <template> <form ref="loginForm" + class="login-form" method="post" name="login" :action="loginActionUrl" @submit="submit"> - <fieldset> + <fieldset class="login-form__fieldset"> <div v-if="apacheAuthFailed" class="warning"> {{ t('core', 'Server side authentication failed!') }}<br> @@ -52,65 +53,36 @@ <!-- the following div ensures that the spinner is always inside the #message div --> <div style="clear: both;" /> </div> - <p class="grouptop" - :class="{shake: invalidPassword}"> - <input id="user" - ref="user" - v-model="user" - type="text" - name="user" - autocapitalize="none" - autocorrect="off" - :autocomplete="autoCompleteAllowed ? 'on' : 'off'" - :placeholder="t('core', 'Username or email')" - :aria-label="t('core', 'Username or email')" - required - @change="updateUsername"> - <label for="user" class="infield">{{ t('core', 'Username or email') }}</label> - </p> - - <p class="groupbottom" - :class="{shake: invalidPassword}"> - <input id="password" - ref="password" - :type="passwordInputType" - class="password-with-toggle" - name="password" - autocorrect="off" - autocapitalize="none" - :autocomplete="autoCompleteAllowed ? 'current-password' : 'off'" - :placeholder="t('core', 'Password')" - :aria-label="t('core', 'Password')" - required> - <label for="password" - class="infield">{{ t('core', 'Password') }}</label> - <NcButton class="toggle-password" - type="tertiary-no-background" - :aria-label="isPasswordHidden ? t('core', 'Show password') : t('core', 'Hide password')" - @click.stop.prevent="togglePassword"> - <template #icon> - <Eye v-if="isPasswordHidden" :size="20" /> - <EyeOff v-else :size="20" /> - </template> - </NcButton> - </p> + <NcTextField id="user" + :label="t('core', 'Username or email')" + :labelVisible="true" + ref="user" + name="user" + :class="{shake: invalidPassword}" + :value.sync="user" + autocapitalize="none" + :spellchecking="false" + :autocomplete="autoCompleteAllowed ? 'username' : 'off'" + :aria-label="t('core', 'Username or email')" + required + @change="updateUsername" /> + + <NcPasswordField id="password" + ref="password" + name="password" + :labelVisible="true" + :class="{shake: invalidPassword}" + :value.sync="password" + :spellchecking="false" + autocapitalize="none" + :autocomplete="autoCompleteAllowed ? 'current-password' : 'off'" + :label="t('core', 'Password')" + :helperText="errorLabel" + :error="isError" + required /> <LoginButton :loading="loading" /> - <p v-if="invalidPassword" - class="warning wrongPasswordMsg"> - {{ t('core', 'Wrong username or password.') }} - </p> - <p v-else-if="userDisabled" - class="warning userDisabledMsg"> - {{ t('core', 'User disabled') }} - </p> - - <p v-if="throttleDelay && throttleDelay > 5000" - class="warning throttledMsg"> - {{ t('core', 'We have detected multiple invalid login attempts from your IP. Therefore your next login is throttled up to 30 seconds.') }} - </p> - <input v-if="redirectUrl" type="hidden" name="redirect_url" @@ -136,7 +108,9 @@ import jstz from 'jstimezonedetect' import { generateUrl, imagePath } from '@nextcloud/router' -import NcButton from '@nextcloud/vue/dist/Components/NcButton' +import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' +import NcPasswordField from '@nextcloud/vue/dist/Components/NcPasswordField.js' +import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' import Eye from 'vue-material-design-icons/Eye' import EyeOff from 'vue-material-design-icons/EyeOff' @@ -150,6 +124,8 @@ export default { Eye, EyeOff, LoginButton, + NcPasswordField, + NcTextField, }, props: { @@ -190,11 +166,26 @@ export default { timezoneOffset: (-new Date().getTimezoneOffset() / 60), user: this.username, password: '', - passwordInputType: 'password', } }, computed: { + isError() { + return this.invalidPassword || this.userDisabled + || (this.throttleDelay && this.throttleDelay > 5000) + }, + errorLabel() { + if (this.invalidPassword) { + return t('core', 'Wrong username or password.') + } + if (this.userDisabled) { + return t('core', 'User disabled') + } + if (this.throttleDelay && this.throttleDelay > 5000) { + return t('core', 'We have detected multiple invalid login attempts from your IP. Therefore your next login is throttled up to 30 seconds.') + } + return undefined; + }, apacheAuthFailed() { return this.errors.indexOf('apacheAuthFailed') !== -1 }, @@ -213,9 +204,6 @@ export default { loginActionUrl() { return generateUrl('login') }, - isPasswordHidden() { - return this.passwordInputType === 'password' - }, }, mounted() { @@ -227,13 +215,6 @@ export default { }, methods: { - togglePassword() { - if (this.passwordInputType === 'password') { - this.passwordInputType = 'text' - } else { - this.passwordInputType = 'password' - } - }, updateUsername() { this.$emit('update:username', this.user) }, @@ -246,10 +227,15 @@ export default { </script> <style lang="scss" scoped> -.toggle-password { - position: absolute; - top: 2px; - right: 10px; - color: var(--color-text-lighter); +.login-form { + text-align: left; + font-size: 1rem; + + &__fieldset { + width: 100%; + display: flex; + flex-direction: column; + gap: 1rem; + } } </style> diff --git a/core/src/components/login/ResetPassword.vue b/core/src/components/login/ResetPassword.vue index aadf7863dfe..d3670f1f163 100644 --- a/core/src/components/login/ResetPassword.vue +++ b/core/src/components/login/ResetPassword.vue @@ -20,39 +20,35 @@ --> <template> - <form @submit.prevent="submit"> - <fieldset> - <p> - <input id="user" - v-model="user" - type="text" - name="user" - autocapitalize="off" - :placeholder="t('core', 'Username or email')" - :aria-label="t('core', 'Username or email')" - required - @change="updateUsername"> + <form @submit.prevent="submit" class="login-form"> + <fieldset class="login-form__fieldset"> + <NcTextField id="user" + :value.sync="user" + name="user" + autocapitalize="off" + :label="t('core', 'Username or email')" + :labelVisible="true" + required + @change="updateUsername" /> <!--<?php p($_['user_autofocus'] ? 'autofocus' : ''); ?> autocomplete="<?php p($_['login_form_autocomplete']); ?>" autocapitalize="none" autocorrect="off"--> - <label for="user" class="infield">{{ t('core', 'Username or email') }}</label> - </p> <div id="reset-password-wrapper"> <LoginButton :value="t('core', 'Reset password')" /> </div> - <p v-if="message === 'send-success'" - class="notecard success"> + <NcNoteCard v-if="message === 'send-success'" + type="success"> {{ t('core', 'A password reset message has been sent to the email address of this account. If you do not receive it, check your spam/junk folders or ask your local administrator for help.') }} <br> {{ t('core', 'If it is not there ask your local administrator.') }} - </p> - <p v-else-if="message === 'send-error'" - class="notecard error"> + </NcNoteCard> + <NcNoteCard v-else-if="message === 'send-error'" + type="error"> {{ t('core', 'Couldn\'t send reset email. Please contact your administrator.') }} - </p> - <p v-else-if="message === 'reset-error'" - class="notecard error"> + </NcNoteCard> + <NcNoteCard v-else-if="message === 'reset-error'" + type="error"> {{ t('core', 'Password cannot be changed. Please contact your administrator.') }} - </p> + </NcNoteCard> <a href="#" @click.prevent="$emit('abort')"> @@ -66,11 +62,15 @@ import axios from '@nextcloud/axios' import { generateUrl } from '@nextcloud/router' import LoginButton from './LoginButton.vue' +import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' +import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js' export default { name: 'ResetPassword', components: { LoginButton, + NcNoteCard, + NcTextField, }, props: { username: { @@ -131,7 +131,15 @@ export default { </script> <style scoped> - .update { - width: auto; +.login-form { + text-align: left; + font-size: 1rem; + + &__fieldset { + width: 100%; + display: flex; + flex-direction: column; + gap: 1rem; } +} </style> diff --git a/core/src/views/Login.vue b/core/src/views/Login.vue index 0cc8dc39cca..c42d5acced1 100644 --- a/core/src/views/Login.vue +++ b/core/src/views/Login.vue @@ -192,6 +192,9 @@ export default { </script> <style lang="scss"> + #login { + width: 300px; + } .fade-enter-active, .fade-leave-active { transition: opacity .3s; } @@ -205,15 +208,13 @@ export default { border-radius: var(--border-radius); } - .alternative-logins button { - margin-top: 12px; - margin-bottom: 12px; - &:first-child { - margin-top: 0; - } + .alternative-logins { + display: flex; + flex-direction: column; + gap: 0.75rem; - &:last-child { - margin-bottom: 0; + .button-vue { + box-sizing: border-box; } } </style> |