Account: <brave-account-password-strength-meter> (#34055)
This commit is contained in:
@@ -32,6 +32,8 @@ build_webui("build") {
|
||||
"brave_account_forgot_password_dialog.ts",
|
||||
"brave_account_password_icons.html.ts",
|
||||
"brave_account_password_icons.ts",
|
||||
"brave_account_password_strength_meter.html.ts",
|
||||
"brave_account_password_strength_meter.ts",
|
||||
"brave_account_sign_in_dialog.html.ts",
|
||||
"brave_account_sign_in_dialog.ts",
|
||||
]
|
||||
@@ -44,6 +46,7 @@ build_webui("build") {
|
||||
"brave_account_entry_dialog.css",
|
||||
"brave_account_forgot_password_dialog.css",
|
||||
"brave_account_password_icons.css",
|
||||
"brave_account_password_strength_meter.css",
|
||||
"brave_account_sign_in_dialog.css",
|
||||
]
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@ import { html } from '//resources/lit/v3_0/lit.rollup.js'
|
||||
|
||||
import './brave_account_email_input.js'
|
||||
import './brave_account_password_icons.js'
|
||||
import './brave_account_password_strength_meter.js'
|
||||
import { BraveAccountCreateDialogElement } from './brave_account_create_dialog.js'
|
||||
import type { EmailInputEventDetail } from './brave_account_email_input.js'
|
||||
import { onToggleVisibility } from './brave_account_common.js'
|
||||
import type { PasswordStrengthChangedEventDetail } from './brave_account_password_strength_meter.js'
|
||||
|
||||
export function getHtml(this: BraveAccountCreateDialogElement) {
|
||||
return html`<!--_html_template_start_-->
|
||||
@@ -47,11 +49,18 @@ export function getHtml(this: BraveAccountCreateDialogElement) {
|
||||
</brave-account-password-icons>
|
||||
<div
|
||||
slot="errors"
|
||||
class="dropdown ${this.passwordStrength !== 0 ? 'visible' : ''}"
|
||||
class="dropdown ${this.password.length !== 0 ? 'visible' : ''}"
|
||||
>
|
||||
<div class="dropdown-content">
|
||||
<password-strength-meter strength=${this.passwordStrength}>
|
||||
</password-strength-meter>
|
||||
<brave-account-password-strength-meter
|
||||
password=${this.password}
|
||||
@password-strength-changed=${(
|
||||
e: CustomEvent<PasswordStrengthChangedEventDetail>,
|
||||
) => {
|
||||
this.isPasswordStrongEnough = e.detail.isStrongEnough
|
||||
}}
|
||||
>
|
||||
</brave-account-password-strength-meter>
|
||||
</div>
|
||||
</div>
|
||||
</leo-input>
|
||||
@@ -101,7 +110,7 @@ export function getHtml(this: BraveAccountCreateDialogElement) {
|
||||
<leo-button
|
||||
slot="buttons"
|
||||
?isDisabled=${!this.isEmailValid
|
||||
|| this.passwordStrength !== 100
|
||||
|| !this.isPasswordStrongEnough
|
||||
|| this.passwordConfirmation !== this.password}
|
||||
@click=${this.onCreateAccountButtonClicked}
|
||||
>
|
||||
|
||||
@@ -3,14 +3,12 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { CrLitElement, css, html } from '//resources/lit/v3_0/lit.rollup.js'
|
||||
import { loadTimeData } from '//resources/js/load_time_data.js'
|
||||
import { CrLitElement } from '//resources/lit/v3_0/lit.rollup.js'
|
||||
|
||||
import {
|
||||
BraveAccountBrowserProxy,
|
||||
BraveAccountBrowserProxyImpl,
|
||||
} from './brave_account_browser_proxy.js'
|
||||
import { BraveAccountStrings } from './brave_components_webui_strings.js'
|
||||
import { getCss } from './brave_account_create_dialog.css.js'
|
||||
import { getHtml } from './brave_account_create_dialog.html.js'
|
||||
import { Error, makeFocusHandler } from './brave_account_common.js'
|
||||
@@ -22,106 +20,6 @@ import {
|
||||
// @ts-expect-error
|
||||
import { Registration } from 'chrome://resources/brave/opaque_ke.bundle.js'
|
||||
|
||||
class PasswordStrengthMeter extends CrLitElement {
|
||||
static get is() {
|
||||
return 'password-strength-meter'
|
||||
}
|
||||
|
||||
static override get styles() {
|
||||
return css`
|
||||
:host {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: var(--leo-spacing-m);
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:host([category='Weak']) {
|
||||
--primary-color: var(--leo-color-systemfeedback-error-icon);
|
||||
--secondary-color: var(--leo-color-systemfeedback-error-background);
|
||||
}
|
||||
|
||||
:host([category='Medium']) {
|
||||
--primary-color: var(--leo-color-systemfeedback-warning-icon);
|
||||
--secondary-color: var(--leo-color-systemfeedback-warning-background);
|
||||
}
|
||||
|
||||
:host([category='Strong']) {
|
||||
--primary-color: var(--leo-color-systemfeedback-success-icon);
|
||||
--secondary-color: var(--leo-color-systemfeedback-success-background);
|
||||
}
|
||||
|
||||
.bar {
|
||||
background-color: var(--secondary-color);
|
||||
border-radius: var(--leo-radius-m);
|
||||
flex-grow: 1;
|
||||
height: 4px;
|
||||
transition: 750ms;
|
||||
}
|
||||
|
||||
.strength {
|
||||
background-color: var(--primary-color);
|
||||
border-radius: var(--leo-radius-m);
|
||||
height: 100%;
|
||||
transition: 750ms;
|
||||
width: calc(1% * var(--strength));
|
||||
}
|
||||
|
||||
.text {
|
||||
color: var(--primary-color);
|
||||
font: var(--leo-font-small-regular);
|
||||
transition: 750ms;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="bar">
|
||||
<div
|
||||
class="strength"
|
||||
style="--strength: ${this.strength}"
|
||||
></div>
|
||||
</div>
|
||||
<div class="text">
|
||||
${loadTimeData.getString(
|
||||
this.category === 'Weak'
|
||||
? BraveAccountStrings.BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_WEAK
|
||||
: this.category === 'Medium'
|
||||
? BraveAccountStrings.BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_MEDIUM
|
||||
: BraveAccountStrings.BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_STRONG,
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static override get properties() {
|
||||
return {
|
||||
category: { type: String, reflect: true },
|
||||
strength: { type: Number },
|
||||
}
|
||||
}
|
||||
|
||||
override updated(changedProperties: Map<PropertyKey, unknown>) {
|
||||
if (changedProperties.has('strength')) {
|
||||
this.category =
|
||||
this.strength < 60 ? 'Weak' : this.strength < 100 ? 'Medium' : 'Strong'
|
||||
}
|
||||
}
|
||||
|
||||
protected accessor category: 'Weak' | 'Medium' | 'Strong' = 'Weak'
|
||||
protected accessor strength: number = 0
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'password-strength-meter': PasswordStrengthMeter
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(PasswordStrengthMeter.is, PasswordStrengthMeter)
|
||||
|
||||
export class BraveAccountCreateDialogElement extends CrLitElement {
|
||||
static get is() {
|
||||
return 'brave-account-create-dialog'
|
||||
@@ -142,20 +40,14 @@ export class BraveAccountCreateDialogElement extends CrLitElement {
|
||||
isEmailValid: { type: Boolean },
|
||||
isPasswordInputFocused: { type: Boolean },
|
||||
isPasswordConfirmationInputFocused: { type: Boolean },
|
||||
isPasswordStrongEnough: { type: Boolean },
|
||||
password: { type: String },
|
||||
passwordConfirmation: { type: String },
|
||||
passwordStrength: { type: Number },
|
||||
}
|
||||
}
|
||||
|
||||
protected onPasswordInput(detail: { value: string }) {
|
||||
this.password = detail.value
|
||||
this.browserProxy.password_strength_meter
|
||||
.getPasswordStrength(this.password)
|
||||
.then(
|
||||
(value: { strength: number }) =>
|
||||
(this.passwordStrength = value.strength),
|
||||
)
|
||||
}
|
||||
|
||||
protected onPasswordConfirmationInput(detail: { value: string }) {
|
||||
@@ -232,9 +124,9 @@ export class BraveAccountCreateDialogElement extends CrLitElement {
|
||||
protected accessor isEmailValid: boolean = false
|
||||
protected accessor isPasswordInputFocused: boolean = false
|
||||
protected accessor isPasswordConfirmationInputFocused: boolean = false
|
||||
protected accessor isPasswordStrongEnough: boolean = false
|
||||
protected accessor password: string = ''
|
||||
protected accessor passwordConfirmation: string = ''
|
||||
protected accessor passwordStrength: number = 0
|
||||
protected registration = new Registration()
|
||||
|
||||
protected readonly passwordFocusHandler = makeFocusHandler(
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* #css_wrapper_metadata_start
|
||||
* #type=style-lit
|
||||
* #css_wrapper_metadata_end */
|
||||
|
||||
:host {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: var(--leo-spacing-m);
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:host([category='Weak']) {
|
||||
--primary-color: var(--leo-color-systemfeedback-error-icon);
|
||||
--secondary-color: var(--leo-color-systemfeedback-error-background);
|
||||
}
|
||||
|
||||
:host([category='Medium']) {
|
||||
--primary-color: var(--leo-color-systemfeedback-warning-icon);
|
||||
--secondary-color: var(--leo-color-systemfeedback-warning-background);
|
||||
}
|
||||
|
||||
:host([category='Strong']) {
|
||||
--primary-color: var(--leo-color-systemfeedback-success-icon);
|
||||
--secondary-color: var(--leo-color-systemfeedback-success-background);
|
||||
}
|
||||
|
||||
.bar {
|
||||
background-color: var(--secondary-color);
|
||||
border-radius: var(--leo-radius-m);
|
||||
flex-grow: 1;
|
||||
height: 4px;
|
||||
transition: 750ms;
|
||||
}
|
||||
|
||||
.strength {
|
||||
background-color: var(--primary-color);
|
||||
border-radius: var(--leo-radius-m);
|
||||
height: 100%;
|
||||
transition: 750ms;
|
||||
width: calc(1% * var(--strength));
|
||||
}
|
||||
|
||||
.text {
|
||||
color: var(--primary-color);
|
||||
font: var(--leo-font-small-regular);
|
||||
transition: 750ms;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { html } from '//resources/lit/v3_0/lit.rollup.js'
|
||||
|
||||
import { BraveAccountPasswordStrengthMeter } from './brave_account_password_strength_meter.js'
|
||||
|
||||
export function getHtml(this: BraveAccountPasswordStrengthMeter) {
|
||||
return html`<!--_html_template_start_-->
|
||||
<div class="bar">
|
||||
<div
|
||||
class="strength"
|
||||
style="--strength: ${this.strength}"
|
||||
></div>
|
||||
</div>
|
||||
<div class="text">
|
||||
${this.category === 'Weak'
|
||||
? '$i18n{BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_WEAK}'
|
||||
: this.category === 'Medium'
|
||||
? '$i18n{BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_MEDIUM}'
|
||||
: '$i18n{BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_STRONG}'}
|
||||
</div>
|
||||
<!--_html_template_end_-->`
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import {
|
||||
CrLitElement,
|
||||
PropertyValues,
|
||||
} from '//resources/lit/v3_0/lit.rollup.js'
|
||||
|
||||
import {
|
||||
BraveAccountBrowserProxy,
|
||||
BraveAccountBrowserProxyImpl,
|
||||
} from './brave_account_browser_proxy.js'
|
||||
import { getCss } from './brave_account_password_strength_meter.css.js'
|
||||
import { getHtml } from './brave_account_password_strength_meter.html.js'
|
||||
|
||||
export type PasswordStrengthChangedEventDetail = { isStrongEnough: boolean }
|
||||
|
||||
export class BraveAccountPasswordStrengthMeter extends CrLitElement {
|
||||
static get is() {
|
||||
return 'brave-account-password-strength-meter'
|
||||
}
|
||||
|
||||
static override get styles() {
|
||||
return getCss()
|
||||
}
|
||||
|
||||
override render() {
|
||||
return getHtml.bind(this)()
|
||||
}
|
||||
|
||||
static override get properties() {
|
||||
return {
|
||||
category: { type: String, reflect: true },
|
||||
strength: { type: Number, state: true },
|
||||
password: { type: String },
|
||||
}
|
||||
}
|
||||
|
||||
override async updated(changedProperties: PropertyValues<this>) {
|
||||
super.updated(changedProperties)
|
||||
|
||||
if ((changedProperties as Map<PropertyKey, unknown>).has('password')) {
|
||||
const password = this.password
|
||||
try {
|
||||
const { strength } =
|
||||
await this.browserProxy.password_strength_meter.getPasswordStrength(
|
||||
password,
|
||||
)
|
||||
|
||||
// Ignore stale response if password changed while we were waiting.
|
||||
if (this.password !== password) {
|
||||
return
|
||||
}
|
||||
|
||||
this.strength = strength
|
||||
} catch (error) {
|
||||
console.error('Failed to calculate password strength:', error)
|
||||
this.strength = 0
|
||||
}
|
||||
|
||||
this.category =
|
||||
this.strength < 60 ? 'Weak' : this.strength < 100 ? 'Medium' : 'Strong'
|
||||
|
||||
this.fire('password-strength-changed', {
|
||||
isStrongEnough: this.strength === 100,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private browserProxy: BraveAccountBrowserProxy =
|
||||
BraveAccountBrowserProxyImpl.getInstance()
|
||||
|
||||
protected accessor category: 'Weak' | 'Medium' | 'Strong' = 'Weak'
|
||||
protected accessor strength = 0
|
||||
private accessor password = ''
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'brave-account-password-strength-meter': BraveAccountPasswordStrengthMeter
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(
|
||||
BraveAccountPasswordStrengthMeter.is,
|
||||
BraveAccountPasswordStrengthMeter,
|
||||
)
|
||||
@@ -215,7 +215,7 @@
|
||||
"includes": [53980],
|
||||
},
|
||||
"<(SHARED_INTERMEDIATE_DIR)/brave/components/brave_account/resources/resources.grd": {
|
||||
"META": {"sizes": {"includes": [35]}},
|
||||
"META": {"sizes": {"includes": [40]}},
|
||||
"includes": [54000],
|
||||
},
|
||||
"brave/ios/web/test/test_resources.grd": {
|
||||
|
||||
Reference in New Issue
Block a user