diff --git a/docs/form/input.md b/docs/form/input.md index 116b0d8d7..f7395d281 100644 --- a/docs/form/input.md +++ b/docs/form/input.md @@ -140,9 +140,9 @@ Il testo di aiuto deve essere esplicitamente associato agli elementi del modulo ### Input password -Per rendere più semplice l'inserimento della password, l'elemento è stato dotato di un visualizzatore dei caratteri digitati. Inoltre è possibile abbinare un controllo per segnalare quanto la password che si sta inserendo sia sicura con l'aggiunta dell'HTML necessario. +Per rendere più semplice l'inserimento della password, l'elemento è dotato di un pulsante che permette di mostrare i caratteri inseriti. -È possibile personalizzare la componente `strength meter` usando gli attributi data. +Inoltre, nel caso di un campo Input password utilizzato per la scelta di una password, è possibile abbinare un controllo per segnalare quanto la password che si sta inserendo sia sicura, con l'aggiunta dell'HTML necessario. È possibile personalizzare alcuni messaggi di questa variante con `strength meter` usando specifici attributi `data`. @@ -156,7 +156,7 @@ Per rendere più semplice l'inserimento della password, l'elemento è stato dota - +
data-bs-minimum-length Lunghezza minima per il calcolo della forza della password (soglia password molto debole)48
@@ -175,22 +175,22 @@ Per rendere più semplice l'inserimento della password, l'elemento è stato dota data-bs-short-pass Testo per il punteggio di forza della password minimo - Password molto debole + Password molto debole. data-bs-bad-pass Testo per punteggio di forza della password basso - Password debole + Password debole. data-bs-good-pass Testo per punteggio di forza della password buono - Password sicura + Password sicura. data-bs-strong-pass Testo per il punteggio di forza della password massimo - Password molto sicura + Password molto sicura. @@ -200,23 +200,24 @@ Per rendere più semplice l'inserimento della password, l'elemento è stato dota
- - - Inserisci almeno 8 caratteri e una lettera maiuscola + + + Inserisci almeno 8 caratteri, una lettera maiuscola e un carattere speciale.
- +
- Inserisci almeno 8 caratteri e una lettera maiuscola + Inserisci almeno 8 caratteri, una lettera maiuscola e un carattere speciale.
@@ -227,11 +228,13 @@ Per rendere più semplice l'inserimento della password, l'elemento è stato dota
- - CAPS LOCK inserito + + +
{% endcapture %}{% include example.html content=example %} @@ -243,7 +246,7 @@ Abilitarlo manualmente con: ```js var inputElement = document.querySelector('#exampleInputPassword')) var passwordComponent = new bootstrap.InputPassword(inputElement, { - minimumLength: 4, + minimumLength: 8, }) ``` @@ -261,7 +264,7 @@ var passwordComponent = new bootstrap.InputPassword(inputElement, { minimumLength Lunghezza minima per il calcolo della forza della password (soglia password molto debole) - 4 + 8 diff --git a/src/js/plugins/input-password.js b/src/js/plugins/input-password.js index 8f5cca2f1..bc12e2f92 100644 --- a/src/js/plugins/input-password.js +++ b/src/js/plugins/input-password.js @@ -12,20 +12,19 @@ const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' const Default = { - shortPass: 'Password molto debole', - badPass: 'Password debole', - goodPass: 'Password sicura', - strongPass: 'Password molto sicura', - enterPass: 'Inserisci almeno 8 caratteri e una lettera maiuscola', - alertCaps: 'CAPS LOCK inserito', + shortPass: 'Password molto debole. ', + badPass: 'Password debole. ', + goodPass: 'Password sicura. ', + strongPass: 'Password molto sicura. ', + enterPass: 'Inserisci almeno 8 caratteri, una lettera maiuscola e un carattere speciale. ', + alertCaps: 'Attenzione: CAPS LOCK inserito. ', showText: true, - minimumLength: 4, + minimumLength: 8, } const EVENT_CLICK = `click${EVENT_KEY}` const EVENT_KEYUP = `keyup${EVENT_KEY}` const EVENT_KEYDOWN = `keydown${EVENT_KEY}` -const EVENT_KEYPRESS = `keypress${EVENT_KEY}` const EVENT_SCORE = `score${EVENT_KEY}` const EVENT_TEXT = `text${EVENT_KEY}` @@ -35,7 +34,6 @@ const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}` const CLASS_NAME_PASSWORD = 'input-password' //const CLASS_NAME_METER = 'input-password-strength-meter' -const CLASS_NAME_SHOW = 'show' const SELECTOR_PASSWORD = 'input[data-bs-input][type="password"]' const SELECTOR_BTN_SHOW_PWD = '.password-icon' @@ -99,45 +97,26 @@ class InputPassword extends BaseComponent { } if (this._isCustom) { this._capsElement = this._element.parentNode.querySelector(SELECTOR_CAPS) + if (this._capsElement) { + // Ensure the element is hidden and empty initially + this._capsElement.style.display = 'none' + this._capsElement.textContent = '' + } } this._showPwdElement = SelectorEngine.findOne(SELECTOR_BTN_SHOW_PWD, this._element.parentNode) } _bindEvents() { + EventHandler.on(this._element, 'keypress', (evt) => this._preventSpace(evt)) + if (this._meter) { EventHandler.on(this._element, EVENT_KEYUP, () => this._checkPassword()) } if (this._isCustom) { - EventHandler.on(this._element, EVENT_KEYDOWN, (evt) => { - if (evt.key === 'Shift') { - this._isShiftPressed = true - } - }) - EventHandler.on(this._element, EVENT_KEYUP, (evt) => { - if (evt.key === 'Shift') { - this._isShiftPressed = false - } - if (evt.key === 'CapsLock') { - this._isCapsOn = !this._isCapsOn - if (this._isCapsOn) { - this._showCapsMsg() - } else { - this._hideCapsMsg() - } - } - }) - EventHandler.on(this._element, EVENT_KEYPRESS, (evt) => { - const matches = evt.key.match(/[A-Z]$/) || [] - if (matches.length > 0 && !this._isShiftPressed) { - this._isCapsOn = true - this._showCapsMsg() - } else if (this._isCapsOn) { - this._isCapsOn = false - this._hideCapsMsg() - } - }) + EventHandler.on(this._element, EVENT_KEYDOWN, (evt) => this._handleKeyDown(evt)) + EventHandler.on(this._element, EVENT_KEYUP, (evt) => this._handleKeyUp(evt)) } if (this._showPwdElement) { @@ -145,25 +124,69 @@ class InputPassword extends BaseComponent { } } - _showCapsMsg() { - if (this._capsElement) { - this._capsElement.classList.add(CLASS_NAME_SHOW) + _preventSpace(evt) { + if (evt.key === ' ' || evt.keyCode === 32) { + evt.preventDefault() + } + } + + _handleKeyDown(evt) { + if (evt.key === 'Shift') { + this._isShiftPressed = true + } + this._checkCapsLock(evt) + } + + _handleKeyUp(evt) { + if (evt.key === 'Shift') { + this._isShiftPressed = false + } + this._checkCapsLock(evt) + } + + _checkCapsLock(evt) { + if (!this._capsElement) return + + const capsOn = this._isCapsLockOn(evt) + if (capsOn !== this._isCapsOn) { + this._isCapsOn = capsOn + this._toggleCapsLockWarning(this._isCapsOn) + } + } + + _isCapsLockOn(evt) { + if (evt.getModifierState) { + return evt.getModifierState('CapsLock') } + const charCode = evt.which || evt.keyCode + const isUpperCase = charCode >= 65 && charCode <= 90 + const isLowerCase = charCode >= 97 && charCode <= 122 + return (isUpperCase && !evt.shiftKey) || (isLowerCase && evt.shiftKey) } - _hideCapsMsg() { + + _toggleCapsLockWarning(show) { if (this._capsElement) { - this._capsElement.classList.remove(CLASS_NAME_SHOW) + if (show) { + this._capsElement.textContent = this._config.alertCaps || Default.alertCaps + this._capsElement.style.display = 'block' + } else { + this._capsElement.style.display = 'none' + setTimeout(() => { + if (this._capsElement.style.display === 'none') { + this._capsElement.textContent = '' + } + }, 100) + } } } _toggleShowPassword() { const toShow = this._element.getAttribute('type') === 'password' + SelectorEngine.find('[class^="password-icon"]', this._showPwdElement).forEach((icon) => icon.classList.toggle('d-none')) - if (toShow) { - this._element.setAttribute('type', 'text') - } else { - this._element.setAttribute('type', 'password') - } + + this._element.setAttribute('type', toShow ? 'text' : 'password') + this._showPwdElement.setAttribute('aria-checked', toShow.toString()) } _checkPassword() { @@ -198,6 +221,43 @@ class InputPassword extends BaseComponent { EventHandler.trigger(this._element, EVENT_TEXT) } } + + const strengthMeter = this._element.parentNode.querySelector('#strengthMeter') + if (strengthMeter) { + const requirements = this._getCompletedRequirements(this._element.value) + const strengthText = this._scoreText(score) + let detailedMessage = `${strengthText} ${requirements.completed} su ${requirements.total} requisiti soddisfatti. ` + + if (requirements.completedDescriptions.length > 0) { + detailedMessage += `Soddisfatti: ${requirements.completedDescriptions.join(' ')} ` + } + + if (requirements.missingDescriptions.length > 0) { + detailedMessage += `Mancanti: ${requirements.missingDescriptions.join(' ')} ` + } + + strengthMeter.textContent = detailedMessage + } + } + + _getCompletedRequirements(password) { + const requirements = [ + { test: password.length >= 8, description: 'Almeno 8 caratteri.' }, + { test: /[A-Z]/.test(password), description: 'Almeno una lettera maiuscola.' }, + { test: /[a-z]/.test(password), description: 'Almeno una lettera minuscola.' }, + { test: /[0-9]/.test(password), description: 'Almeno un numero.' }, + { test: /[^A-Z-a-z0-9]/.test(password), description: 'Almeno un carattere speciale.' }, + ] + + const completedRequirements = requirements.filter((req) => req.test) + const missingRequirements = requirements.filter((req) => !req.test) + + return { + completed: completedRequirements.length, + total: requirements.length, + completedDescriptions: completedRequirements.map((req) => req.description), + missingDescriptions: missingRequirements.map((req) => req.description), + } } /**