diff options
author | Carl Schwan <carl@carlschwan.eu> | 2022-07-18 17:58:05 +0200 |
---|---|---|
committer | Carl Schwan <carl@carlschwan.eu> | 2022-07-27 10:43:21 +0200 |
commit | 253118298dbac78d13c5333279def8bbd3ad555c (patch) | |
tree | d684b43694cf5cdfa70fa528fbeaba89e65f5b66 /core/src | |
parent | 5a236762b8e6b5f09a0c65f5840416bbad2d159a (diff) | |
download | nextcloud-server-253118298dbac78d13c5333279def8bbd3ad555c.tar.gz nextcloud-server-253118298dbac78d13c5333279def8bbd3ad555c.zip |
Redesign guest pages for better accessibility
- Use white box and put content on it
- Improve focus indicator
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/components/login/LoginButton.vue | 54 | ||||
-rw-r--r-- | core/src/components/login/ResetPassword.vue | 9 | ||||
-rw-r--r-- | core/src/components/setup/InstallButton.vue | 43 | ||||
-rw-r--r-- | core/src/components/setup/RecommendedApps.vue | 40 | ||||
-rw-r--r-- | core/src/install.js | 13 | ||||
-rw-r--r-- | core/src/views/Login.vue | 191 |
6 files changed, 166 insertions, 184 deletions
diff --git a/core/src/components/login/LoginButton.vue b/core/src/components/login/LoginButton.vue index e99398ba09a..0b9afd58685 100644 --- a/core/src/components/login/LoginButton.vue +++ b/core/src/components/login/LoginButton.vue @@ -20,23 +20,27 @@ --> <template> - <div class="submit-wrapper" @click="$emit('click')"> - <input type="submit" - class="submit-wrapper__input primary" - title="" - :value="!loading ? value : valueLoading"> - <div v-if="loading" class="submit-wrapper__icon icon-loading-small-dark" /> - <ArrowRight v-else class="submit-wrapper__icon" /> - </div> + <Button type="primary" + native-type="submit" + :wide="true" + @click="$emit('click')"> + {{ !loading ? value : valueLoading }} + <template #icon> + <div v-if="loading" class="submit-wrapper__icon icon-loading-small-dark" /> + <ArrowRight v-else class="submit-wrapper__icon" /> + </template> + </Button> </template> <script> +import Button from '@nextcloud/vue/dist/Components/Button' import ArrowRight from 'vue-material-design-icons/ArrowRight.vue' export default { name: 'LoginButton', components: { ArrowRight, + Button, }, props: { value: { @@ -58,37 +62,3 @@ export default { }, } </script> - -<style scoped lang="scss"> -.submit-wrapper { - display: flex; - align-items: center; - justify-content: center; - padding: 10px 5px; - position: relative; - margin: 0 auto; - - &__input { - width: 260px; - height: 50px; - } - - &__icon { - display: flex; - position: absolute; - right: 24px; - transition: right 100ms ease-in-out; - /* The submit icon is positioned on the submit button. - From the user point of view the icon is part of the - button, so the clicks on the icon have to be - applied to the button instead. */ - pointer-events: none; - } - - &__input:hover + &__icon:not(.icon-loading-small-dark), - &__input:focus + &__icon:not(.icon-loading-small-dark), - &__input:active + &__icon:not(.icon-loading-small-dark) { - right: 20px; - } -} -</style> diff --git a/core/src/components/login/ResetPassword.vue b/core/src/components/login/ResetPassword.vue index 7a499baa2f0..aadf7863dfe 100644 --- a/core/src/components/login/ResetPassword.vue +++ b/core/src/components/login/ResetPassword.vue @@ -40,22 +40,19 @@ <LoginButton :value="t('core', 'Reset password')" /> </div> <p v-if="message === 'send-success'" - class="update"> + class="notecard 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="update warning"> + class="notecard error"> {{ t('core', 'Couldn\'t send reset email. Please contact your administrator.') }} </p> <p v-else-if="message === 'reset-error'" - class="update warning"> + class="notecard error"> {{ t('core', 'Password cannot be changed. Please contact your administrator.') }} </p> - <p v-else-if="message" - class="update" - :class="{warning: error}" /> <a href="#" @click.prevent="$emit('abort')"> diff --git a/core/src/components/setup/InstallButton.vue b/core/src/components/setup/InstallButton.vue deleted file mode 100644 index 52d2d622a76..00000000000 --- a/core/src/components/setup/InstallButton.vue +++ /dev/null @@ -1,43 +0,0 @@ -<!-- - - @copyright 2022 Christopher Ng <chrng8@gmail.com> - - - - @author Christopher Ng <chrng8@gmail.com> - - - - @license GNU AGPL version 3 or any later version - - - - 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> - <button class="primary" - :autofocus="true" - v-on="$listeners"> - {{ t('core', 'Install recommended apps') }} - </button> -</template> - -<script> -export default { - name: 'InstallButton', -} -</script> - -<style lang="scss" scoped> - button { - margin: 24px auto 10px auto; - padding: 10px 20px; - font-size: 20px; - } -</style> diff --git a/core/src/components/setup/RecommendedApps.vue b/core/src/components/setup/RecommendedApps.vue index cd39929f14b..5bd5bb0bbae 100644 --- a/core/src/components/setup/RecommendedApps.vue +++ b/core/src/components/setup/RecommendedApps.vue @@ -20,7 +20,7 @@ --> <template> - <div class="body-login-container"> + <div class="guest-box"> <h2>{{ t('core', 'Recommended apps') }}</h2> <p v-if="loadingApps" class="loading text-center"> {{ t('core', 'Loading apps …') }} @@ -53,12 +53,21 @@ </div> </div> - <InstallButton v-if="showInstallButton" - @click.stop.prevent="installApps" /> - - <p class="text-center"> - <a :href="defaultPageUrl">{{ t('core', 'Cancel') }}</a> - </p> + <div class="dialog-row"> + <Button v-if="showInstallButton" + type="tertiary" + role="link" + href="defaultPageUrl" + @click="goTo(defaultPageUrl)"> + {{ t('core', 'Cancel') }} + </Button> + + <Button v-if="showInstallButton" + type="primary" + @click.stop.prevent="installApps"> + {{ t('core', 'Install recommended apps') }} + </Button> + </div> </div> </template> @@ -69,8 +78,7 @@ import { loadState } from '@nextcloud/initial-state' import pLimit from 'p-limit' import { translate as t } from '@nextcloud/l10n' -// TODO replace with Button component when @nextcloud/vue is upgraded to v5 -import InstallButton from './InstallButton' +import Button from '@nextcloud/vue/dist/Components/Button' import logger from '../../logger' @@ -106,7 +114,7 @@ const defaultPageUrl = loadState('core', 'defaultPageUrl') export default { name: 'RecommendedApps', components: { - InstallButton, + Button, }, data() { return { @@ -184,13 +192,18 @@ export default { } return recommended[appId].description }, + goTo(href) { + window.location.href = href + }, }, } </script> <style lang="scss" scoped> -.body-login-container { - +.dialog-row { + display: flex; + justify-content: end; + margin-top: 8px; } p { @@ -215,7 +228,7 @@ p { img { height: 50px; width: 50px; - filter: invert(1); + filter: var(--background-invert-if-dark); } img, .info { @@ -228,7 +241,6 @@ p { } h3 { - color: #fff; margin-top: 0; } diff --git a/core/src/install.js b/core/src/install.js index 51358e6f70d..6dbae452d31 100644 --- a/core/src/install.js +++ b/core/src/install.js @@ -95,14 +95,13 @@ window.addEventListener('DOMContentLoaded', function() { $('.float-spinner').show(250) // Disable inputs - $(':submit', this).attr('disabled', 'disabled').val($(':submit', this).data('finishing')) + $('input[type="submit"]').attr('disabled', 'disabled').val($('input[type="submit"]').data('finishing')) $('input', this).addClass('ui-state-disabled').attr('disabled', 'disabled') // only disable buttons if they are present if ($('#selectDbType').find('.ui-button').length > 0) { $('#selectDbType').buttonset('disable') } $('.strengthify-wrapper, .tipsy') - .css('-ms-filter', '"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"') .css('filter', 'alpha(opacity=30)') .css('opacity', 0.3) @@ -165,5 +164,13 @@ window.addEventListener('DOMContentLoaded', function() { }) $('#dbpass').showPassword().keyup() - $('#adminpass').showPassword().keyup() + $('.toggle-password').click(function(event) { + event.preventDefault() + const currentValue = $(this).parent().children('input').attr('type') + if (currentValue === 'password') { + $(this).parent().children('input').attr('type', 'text') + } else { + $(this).parent().children('input').attr('type', 'password') + } + }) }) diff --git a/core/src/views/Login.vue b/core/src/views/Login.vue index e2ca484126c..262c9e2973f 100644 --- a/core/src/views/Login.vue +++ b/core/src/views/Login.vue @@ -20,87 +20,102 @@ --> <template> - <div v-if="!hideLoginForm || directLogin"> - <transition name="fade" mode="out-in"> - <div v-if="!passwordlessLogin && !resetPassword && resetPasswordTarget === ''" - key="login"> - <LoginForm :username.sync="user" - :redirect-url="redirectUrl" - :direct-login="directLogin" - :messages="messages" - :errors="errors" - :throttle-delay="throttleDelay" - :auto-complete-allowed="autoCompleteAllowed" - @submit="loading = true" /> - <a v-if="canResetPassword && resetPasswordLink !== ''" - id="lost-password" - :href="resetPasswordLink"> - {{ t('core', 'Forgot password?') }} - </a> - <a v-else-if="canResetPassword && !resetPassword" - id="lost-password" - :href="resetPasswordLink" - @click.prevent="resetPassword = true"> - {{ t('core', 'Forgot password?') }} - </a> - <br> - <template v-if="hasPasswordless"> - <div v-if="countAlternativeLogins" - class="alternative-logins"> - <a v-if="hasPasswordless" - class="button" - :class="{ 'single-alt-login-option': countAlternativeLogins }" + <div id="login" class="guest-box"> + <div v-if="!hideLoginForm || directLogin"> + <transition name="fade" mode="out-in"> + <div v-if="!passwordlessLogin && !resetPassword && resetPasswordTarget === ''"> + <LoginForm :username.sync="user" + :redirect-url="redirectUrl" + :direct-login="directLogin" + :messages="messages" + :errors="errors" + :throttle-delay="throttleDelay" + :auto-complete-allowed="autoCompleteAllowed" + @submit="loading = true" /> + <a v-if="canResetPassword && resetPasswordLink !== ''" + id="lost-password" + :href="resetPasswordLink"> + {{ t('core', 'Forgot password?') }} + </a> + <a v-else-if="canResetPassword && !resetPassword" + id="lost-password" + :href="resetPasswordLink" + @click.prevent="resetPassword = true"> + {{ t('core', 'Forgot password?') }} + </a> + <br> + <template v-if="hasPasswordless"> + <div v-if="countAlternativeLogins" + class="alternative-logins"> + <a v-if="hasPasswordless" + class="button" + :class="{ 'single-alt-login-option': countAlternativeLogins }" + href="#" + @click.prevent="passwordlessLogin = true"> + {{ t('core', 'Log in with a device') }} + </a> + </div> + <a v-else href="#" @click.prevent="passwordlessLogin = true"> {{ t('core', 'Log in with a device') }} </a> - </div> - <a v-else - href="#" - @click.prevent="passwordlessLogin = true"> - {{ t('core', 'Log in with a device') }} + </template> + </div> + <div v-else-if="!loading && passwordlessLogin" + key="reset" + class="login-additional"> + <PasswordLessLoginForm :username.sync="user" + :redirect-url="redirectUrl" + :auto-complete-allowed="autoCompleteAllowed" + :is-https="isHttps" + :is-localhost="isLocalhost" + :has-public-key-credential="hasPublicKeyCredential" + @submit="loading = true" /> + <a href="#" @click.prevent="passwordlessLogin = false"> + {{ t('core', 'Back') }} </a> - </template> - </div> - <div v-else-if="!loading && passwordlessLogin" - key="reset" - class="login-additional"> - <PasswordLessLoginForm :username.sync="user" - :redirect-url="redirectUrl" - :auto-complete-allowed="autoCompleteAllowed" - :is-https="isHttps" - :is-localhost="isLocalhost" - :has-public-key-credential="hasPublicKeyCredential" - @submit="loading = true" /> - <a href="#" @click.prevent="passwordlessLogin = false"> - {{ t('core', 'Back') }} - </a> - </div> - <div v-else-if="!loading && canResetPassword" - key="reset" - class="login-additional"> - <div class="lost-password-container"> - <ResetPassword v-if="resetPassword" - :username.sync="user" - :reset-password-link="resetPasswordLink" - @abort="resetPassword = false" /> </div> - </div> - <div v-else-if="resetPasswordTarget !== ''"> - <UpdatePassword :username.sync="user" - :reset-password-target="resetPasswordTarget" - @done="passwordResetFinished" /> - </div> - </transition> - </div> - <div v-else> - <transition name="fade" mode="out-in"> - <div class="warning"> - {{ t('core', 'Login form is disabled.') }}<br> - <small>{{ t('core', 'Please contact your administrator.') }} - </small> - </div> - </transition> + <div v-else-if="!loading && canResetPassword" + key="reset" + class="login-additional"> + <div class="lost-password-container"> + <ResetPassword v-if="resetPassword" + :username.sync="user" + :reset-password-link="resetPasswordLink" + @abort="resetPassword = false" /> + </div> + </div> + <div v-else-if="resetPasswordTarget !== ''"> + <UpdatePassword :username.sync="user" + :reset-password-target="resetPasswordTarget" + @done="passwordResetFinished" /> + </div> + </transition> + </div> + <div v-else> + <transition name="fade" mode="out-in"> + <div class="warning"> + {{ t('core', 'Login form is disabled.') }}<br> + <small> + {{ t('core', 'Please contact your administrator.') }} + </small> + </div> + </transition> + </div> + + <div id="alternative-logins" class="alternative-logins"> + <Button v-for="(alternativeLogin, index) in alternativeLogins" + :key="index" + type="primary" + :wide="true" + :class="[alternativeLogin.class]" + role="link" + :href="alternativeLogin.href" + @click="goTo(alternativeLogin.href)"> + {{ alternativeLogin.name }} + </Button> + </div> </div> </template> @@ -112,6 +127,7 @@ import LoginForm from '../components/login/LoginForm.vue' import PasswordLessLoginForm from '../components/login/PasswordLessLoginForm.vue' import ResetPassword from '../components/login/ResetPassword.vue' import UpdatePassword from '../components/login/UpdatePassword.vue' +import Button from '@nextcloud/vue/dist/Components/Button' const query = queryString.parse(location.search) if (query.clear === '1') { @@ -132,6 +148,7 @@ export default { PasswordLessLoginForm, ResetPassword, UpdatePassword, + Button, }, data() { @@ -154,6 +171,7 @@ export default { directLogin: query.direct === '1', hasPasswordless: loadState('core', 'webauthn-available', false), countAlternativeLogins: loadState('core', 'countAlternativeLogins', false), + alternativeLogins: loadState('core', 'alternativeLogins', []), isHttps: window.location.protocol === 'https:', isLocalhost: window.location.hostname === 'localhost', hasPublicKeyCredential: typeof (window.PublicKeyCredential) !== 'undefined', @@ -166,15 +184,36 @@ export default { this.resetPasswordTarget = '' this.directLogin = true }, + goTo(href) { + window.location.href = href + }, }, } </script> -<style> +<style lang="scss"> .fade-enter-active, .fade-leave-active { transition: opacity .3s; } .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { opacity: 0; } + + #lost-password { + padding: 4px; + margin: 8px; + border-radius: var(--border-radius); + } + + .alternative-logins button { + margin-top: 12px; + margin-bottom: 12px; + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } </style> |