diff options
| author | Jan-Hendrik Willms <tleilax+studip@gmail.com> | 2025-02-14 11:15:04 +0000 |
|---|---|---|
| committer | Jan-Hendrik Willms <tleilax+studip@gmail.com> | 2025-02-14 11:15:04 +0000 |
| commit | 3592861804c484d3386c1be5eb050c1761892c59 (patch) | |
| tree | 901ade7b1397ff8675e6955b182d3bc8f085486b /resources | |
| parent | 461e5c491da6fe3b2bb3b7400fb09303b54d1aa0 (diff) | |
implement generic methods to allow the user to display a password in a password input, fixes #5186
Closes #5186
Merge request studip/studip!3876
Diffstat (limited to 'resources')
| -rw-r--r-- | resources/assets/javascripts/bootstrap/application.js | 21 | ||||
| -rw-r--r-- | resources/assets/stylesheets/studip.scss | 20 | ||||
| -rw-r--r-- | resources/vue/base-directives.js | 2 | ||||
| -rw-r--r-- | resources/vue/components/admission/PasswordAdmission.vue | 18 | ||||
| -rw-r--r-- | resources/vue/directives/allow-plaintext-toggle.ts | 52 |
5 files changed, 90 insertions, 23 deletions
diff --git a/resources/assets/javascripts/bootstrap/application.js b/resources/assets/javascripts/bootstrap/application.js index 4e5eccc..7fdb316 100644 --- a/resources/assets/javascripts/bootstrap/application.js +++ b/resources/assets/javascripts/bootstrap/application.js @@ -1,5 +1,7 @@ import { $gettext } from '../lib/gettext'; import eventBus from "../lib/event-bus.ts"; +import allowPlaintextToggle from "../../../vue/directives/allow-plaintext-toggle"; + /* ------------------------------------------------------------------------ * application.js @@ -365,8 +367,6 @@ STUDIP.domReady(function () { const passwordInput = document.getElementById('password'); const usernameInput = document.getElementById('loginname'); const passwordCapsText = document.getElementById('password-caps'); - const iconPasswordVisible = document.getElementById('visible-password'); - const iconPasswordInVisible = document.getElementById('invisible-password'); [usernameInput, passwordInput].forEach((input) => { input.addEventListener('keydown', (event) => { @@ -391,16 +391,11 @@ STUDIP.domReady(function () { event.preventDefault(); }); } +}); - document.getElementById('password-toggle').addEventListener('click', () => { - if (passwordInput.type === 'password') { - passwordInput.type = 'text'; - iconPasswordVisible.style.display = 'none'; - iconPasswordInVisible.style.display = ''; - } else { - passwordInput.type = 'password'; - iconPasswordVisible.style.display = ''; - iconPasswordInVisible.style.display = 'none'; - } - }); +/* Allow password inputs to display the password as plain text */ +STUDIP.ready(() => { + document.querySelectorAll('input[type="password"].allow-plaintext-toggle').forEach((node) => { + allowPlaintextToggle(node); + }) }); diff --git a/resources/assets/stylesheets/studip.scss b/resources/assets/stylesheets/studip.scss index 9183081..42ab072 100644 --- a/resources/assets/stylesheets/studip.scss +++ b/resources/assets/stylesheets/studip.scss @@ -627,3 +627,23 @@ mark { width: var(--icon-size-inline); height: var(--icon-size-inline); } + +/** Password plain toggle **/ +input.allow-plaintext-toggle { + padding-right: 28px !important; + + +.password-display-toggle { + @include hide-text; + @extend .studip-icon; + + cursor: pointer; + position: absolute; + transform: translateY(-50%); + + @include background-icon(visibility-invisible); + + &.password-is-hidden { + @include background-icon(visibility-checked); + } + } +} diff --git a/resources/vue/base-directives.js b/resources/vue/base-directives.js index 8f46b03..c7b8509 100644 --- a/resources/vue/base-directives.js +++ b/resources/vue/base-directives.js @@ -1,7 +1,9 @@ import autofocus from './directives/autofocus'; +import allowPlaintextToggle from "./directives/allow-plaintext-toggle"; const BaseDirectives = { autofocus, + allowPlaintextToggle, }; export default BaseDirectives; diff --git a/resources/vue/components/admission/PasswordAdmission.vue b/resources/vue/components/admission/PasswordAdmission.vue index 3f0ecf4..5d87e4d 100644 --- a/resources/vue/components/admission/PasswordAdmission.vue +++ b/resources/vue/components/admission/PasswordAdmission.vue @@ -12,15 +12,17 @@ </studip-message-box> <label> {{ $gettext('Zugangspasswort') }} - <input :type="passwordVisible ? 'text' : 'password'" v-model="password1" ref="password1"> - <studip-icon class="password-visibility" @click="togglePasswordVisible" - :shape="passwordVisible ? 'visibility-invisible' : 'visibility-visible'"></studip-icon> + <input type="password" + v-model="password1" + v-allow-plaintext-toggle + > </label> <label> {{ $gettext('Passwort wiederholen') }} - <input :type="passwordVisible ? 'text' : 'password'" v-model="password2" ref="password2"> - <studip-icon class="password-visibility" @click="togglePasswordVisible" - :shape="passwordVisible ? 'visibility-invisible' : 'visibility-visible'"></studip-icon> + <input type="password" + v-model="password2" + v-allow-plaintext-toggle + > </label> </section> </form> @@ -43,14 +45,10 @@ export default { messageText: this.message || this.$gettext('Für die Anmeldung ist ein Passwort erforderlich.'), password1: '', password2: '', - passwordVisible: false, passwordSet: this.hasPassword } }, methods: { - togglePasswordVisible() { - this.passwordVisible = !this.passwordVisible; - }, setRuleData(data) { if (data.attributes.payload.password !== '') { this.passwordSet = true; diff --git a/resources/vue/directives/allow-plaintext-toggle.ts b/resources/vue/directives/allow-plaintext-toggle.ts new file mode 100644 index 0000000..7969c49 --- /dev/null +++ b/resources/vue/directives/allow-plaintext-toggle.ts @@ -0,0 +1,52 @@ +import {$gettext} from "../../assets/javascripts/lib/gettext"; +import {DirectiveBinding, nextTick} from "vue"; + +const messages = { + 0: $gettext('Passwort verstecken'), + 1: $gettext('Passwort anzeigen'), +}; + +function initialize(el: HTMLElement): void { + if (!el.parentElement) { + return; + } + + el.classList.add('allow-plaintext-toggle'); + + el.parentElement.style.position = 'relative'; + + const bbox = el.getBoundingClientRect(); + const parentBbox = el.parentElement.getBoundingClientRect(); + + const x = (bbox.x - parentBbox.x) + bbox.width - 26; + const y = (bbox.y - parentBbox.y) + bbox.height / 2; + + const toggle = document.createElement('button'); + toggle.classList.add('as-link', 'password-display-toggle', 'password-is-hidden'); + toggle.setAttribute('title', messages[1]); + toggle.innerText = messages[1]; + toggle.style.top = `${y}px`; + toggle.style.left = `${x}px`; + + toggle.addEventListener('click', (event) => { + const isHidden = el.getAttribute('type') === 'text'; + toggle.classList.toggle('password-is-hidden', isHidden); + toggle.setAttribute('title', messages[isHidden ? 1 : 0]); + toggle.innerText = messages[isHidden ? 1 : 0]; + + el.setAttribute('type', isHidden ? 'password' : 'text'); + + event.preventDefault(); + }); + + el.after(toggle); +} + +export default (el: HTMLElement, binding?: DirectiveBinding | undefined): void => { + if (el.nextElementSibling?.matches('button.password-display-toggle')) { + return; + } + + const promise = binding ? nextTick() : Promise.resolve(); + promise.then(() => initialize(el)) +} |
