UNPKG

passbolt-styleguide

Version:

Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.

187 lines (168 loc) 5.54 kB
/** * Passbolt ~ Open source PasswordComplexity manager for teams * Copyright (c) 2022 Passbolt SA (https://www.passbolt.com) * * Licensed under GNU Affero General Public License version 3 of the or any later version. * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) 2022 Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) * @since 3.6.0 */ import React, { Component } from "react"; import PropTypes from "prop-types"; import { Trans, withTranslation } from "react-i18next"; import { SecretGeneratorComplexity } from "../../lib/SecretGenerator/SecretGeneratorComplexity"; import Tooltip from "../../../react-extension/components/Common/Tooltip/Tooltip"; import InfoSVG from "../../../img/svg/info.svg"; const COLOR_GRADIENT = { COLOR_1: hexToRgb("#BA2809"), COLOR_2: hexToRgb("#FFA724"), COLOR_3: hexToRgb("#0EAA00"), }; /** * Hex color to rgb color object * @param hex * @returns {null|{red: number, green: number, blue: number}} */ function hexToRgb(hex) { const hexRegex = new RegExp("^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$", "i"); const result = hexRegex.exec(hex.trim()); if (result) { const red = parseInt(result[1], 16); const green = parseInt(result[2], 16); const blue = parseInt(result[3], 16); return { red, green, blue }; } return null; } /** * This component represents a password complexity with the strength, an entropy and a bar */ class PasswordComplexity extends Component { /** * Get the entropy value formatted for display. * @returns {number} */ get entropy() { const entropy = this.props.entropy || 0.0; return entropy.toFixed(1); } /** * Get the translated tooltip message. * @returns {JSX} */ get tooltipMessage() { return ( <> <Trans>Entropy:</Trans> {this.entropy} bits </> ); } /** * Get the password strength label to display based on the actual entropy or error state. * @returns {JSX}; */ get passwordStrengthLabel() { const shouldDisplayEntropyLabel = this.hasEntropy() || this.hasError(); if (!shouldDisplayEntropyLabel) { return <Trans>Quality</Trans>; } /* * The parser can't find the translation for passwordStrength.label * To fix that we can use it in comment * this.translate("n/a") * this.translate("Very weak") * this.translate("Weak") * this.translate("Fair") * this.translate("Strong") * this.translate("Very strong") */ const strength = SecretGeneratorComplexity.strength(this.props.entropy); return <>{strength.label}</>; } /** * Has entropy * @returns {boolean} */ hasEntropy() { return this.props.entropy !== null && typeof this.props.entropy !== "undefined"; } /** * Has error * @returns {boolean} */ hasError() { return this.props.error; } /** * Get the dynamic part style of the entropy progression bar. * @returns {object} */ getProgresseBarStyle() { const relativePositionForEntropy = this.getRelativeEntropyPosition(); return { width: `${relativePositionForEntropy}%`, backgroundColor: this.colorGradient(relativePositionForEntropy) }; } /** * Get the rgb color at a specific position in percentage * @param {number} fadeFraction The fade fraction * @returns {string} the color in rgb(0,0,0) */ colorGradient(fadeFraction) { let rgbColor1, rgbColor2; let fade = (fadeFraction / 100) * 2; // Find which interval to use and adjust the fade percentage if (fade >= 1) { fade -= 1; rgbColor1 = COLOR_GRADIENT.COLOR_2; rgbColor2 = COLOR_GRADIENT.COLOR_3; } else { rgbColor1 = COLOR_GRADIENT.COLOR_1; rgbColor2 = COLOR_GRADIENT.COLOR_2; } const red = Math.floor(rgbColor1.red + (rgbColor2.red - rgbColor1.red) * fade); const green = Math.floor(rgbColor1.green + (rgbColor2.green - rgbColor1.green) * fade); const blue = Math.floor(rgbColor1.blue + (rgbColor2.blue - rgbColor1.blue) * fade); return `rgb(${red},${green},${blue})`; } /** * Return a percentage value matching the position of the given entropy compared to the full value possible. * @returns {number} */ getRelativeEntropyPosition() { // Power curve with an asymptote at 100%. It will never reach 100% but will get infinitely closer. return 100 - 99 / (1 + Math.pow(this.props.entropy / 90, 10)); } /** * Render the component * @returns {JSX} */ render() { return ( <div className="password-complexity"> <span className="complexity-text"> <Tooltip message={this.tooltipMessage}> {this.passwordStrengthLabel} <InfoSVG /> </Tooltip> </span> <span className="progress"> <span className="progress-bar background" /> <span className={`progress-bar foreground ${this.hasError() ? "error" : ""}`} style={this.hasEntropy() ? this.getProgresseBarStyle(this.props.entropy) : null} /> </span> </div> ); } } PasswordComplexity.defaultProps = { entropy: null, }; PasswordComplexity.propTypes = { entropy: PropTypes.number, // The entropy error: PropTypes.bool, // The error }; export default withTranslation("common")(PasswordComplexity);