From f0e45477fa85a1d82750597cdaf122e41e9c7764 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Fri, 26 Mar 2021 16:05:02 +0100 Subject: [PATCH] feat(frontend): password visibilty toggle --- frontend/src/utils/inputs/inputs.js | 2 + frontend/src/utils/inputs/inputs.sass | 27 ++++++++++ frontend/src/utils/inputs/password.js | 77 +++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 frontend/src/utils/inputs/password.js diff --git a/frontend/src/utils/inputs/inputs.js b/frontend/src/utils/inputs/inputs.js index 9b438bf6e..a072c2196 100644 --- a/frontend/src/utils/inputs/inputs.js +++ b/frontend/src/utils/inputs/inputs.js @@ -1,6 +1,7 @@ import { Checkbox } from './checkbox'; import { FileInput } from './file-input'; import { FileMaxSize } from './file-max-size'; +import { Password } from './password'; import './inputs.sass'; import './radio-group.sass'; @@ -9,4 +10,5 @@ export const InputUtils = [ Checkbox, FileInput, FileMaxSize, + Password, ]; diff --git a/frontend/src/utils/inputs/inputs.sass b/frontend/src/utils/inputs/inputs.sass index a1633f934..a8c49a716 100644 --- a/frontend/src/utils/inputs/inputs.sass +++ b/frontend/src/utils/inputs/inputs.sass @@ -273,3 +273,30 @@ option .form--vertical__cell vertical-align: top + +// PASSWORD INPUT + +.password-input__wrapper + display: grid + grid-template-areas: 'input toggle' + width: 100% + max-width: 600px + grid-template-rows: auto + grid-template-columns: 1fr auto + + .password-input__input + grid-area: input + + .password-input__toggle + grid-area: toggle + + display: flex + justify-content: center + align-content: center + flex-direction: column + padding: 7px + + cursor: pointer + color: var(--color-fontsec) + &:hover + color: var(--color-font) diff --git a/frontend/src/utils/inputs/password.js b/frontend/src/utils/inputs/password.js new file mode 100644 index 000000000..2bb750802 --- /dev/null +++ b/frontend/src/utils/inputs/password.js @@ -0,0 +1,77 @@ +import { Utility } from '../../core/utility'; + +const PASSWORD_INITIALIZED_CLASS = 'password-input--initialized'; + +@Utility({ + selector: 'input[type="password"]:not([uw-no-password])', +}) +export class Password { + _element; + _iconEl; + _toggleContainerEl; + + constructor(element) { + if (!element) + throw new Error('Password utility cannot be setup without an element!'); + + if (element.classList.contains(PASSWORD_INITIALIZED_CLASS)) + return false; + + this._element = element; + + this._element.classList.add('password-input__input'); + + const siblingEl = this._element.nextSibling; + const parentEl = this._element.parentElement; + + const wrapperEl = document.createElement('div'); + wrapperEl.classList.add('password-input__wrapper'); + wrapperEl.appendChild(this._element); + + this._toggleContainerEl = document.createElement('div'); + this._toggleContainerEl.classList.add('password-input__toggle'); + wrapperEl.appendChild(this._toggleContainerEl); + + this._iconEl = document.createElement('i'); + this._iconEl.classList.add('fas', 'fa-fw'); + this._toggleContainerEl.appendChild(this._iconEl); + + parentEl.insertBefore(wrapperEl, siblingEl); + + this._element.classList.add(PASSWORD_INITIALIZED_CLASS); + } + + start() { + this.updateVisibleIcon(this.isVisible()); + + this._toggleContainerEl.addEventListener('mouseover', () => { + this.updateVisibleIcon(!this.isVisible()); + }); + this._toggleContainerEl.addEventListener('mouseout', () => { + this.updateVisibleIcon(this.isVisible()); + }); + this._toggleContainerEl.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + this.setVisible(!this.isVisible()); + }); + } + + isVisible() { + return this._element.type !== 'password'; + } + + setVisible(visible) { + this._element.type = visible ? 'text' : 'password'; + this.updateVisibleIcon(visible); + } + + updateVisibleIcon(visible) { + function visibleClass(visible) { + return 'fa-' + (visible ? 'eye' : 'eye-slash'); + } + + this._iconEl.classList.remove(visibleClass(!visible)); + this._iconEl.classList.add(visibleClass(!!visible)); + } +}