mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
116 lines
2.6 KiB
TypeScript
116 lines
2.6 KiB
TypeScript
import {Fragment} from 'react';
|
|
import {render} from 'react-dom';
|
|
import {css} from '@emotion/react';
|
|
import styled from '@emotion/styled';
|
|
import throttle from 'lodash/throttle';
|
|
import zxcvbn from 'zxcvbn';
|
|
|
|
import {tct} from 'sentry/locale';
|
|
import {space} from 'sentry/styles/space';
|
|
import theme from 'sentry/utils/theme';
|
|
|
|
/**
|
|
* NOTE: Do not import this component synchronously. The zxcvbn library is
|
|
* relatively large. This component should be loaded async as a split chunk.
|
|
*/
|
|
|
|
/**
|
|
* The maximum score that zxcvbn reports
|
|
*/
|
|
const MAX_SCORE = 5;
|
|
|
|
type Props = {
|
|
/**
|
|
* The password value.
|
|
*/
|
|
value: string;
|
|
/**
|
|
* The color to make the progress bar for each strength level. 5 levels.
|
|
*/
|
|
colors?: [string, string, string, string, string];
|
|
/**
|
|
* A set of labels to display for each password strength level. 5 levels.
|
|
*/
|
|
labels?: [string, string, string, string, string];
|
|
};
|
|
|
|
function PasswordStrength({
|
|
value,
|
|
labels = ['Very Weak', 'Very Weak', 'Weak', 'Strong', 'Very Strong'],
|
|
colors = [theme.red300, theme.red300, theme.yellow300, theme.green300, theme.green300],
|
|
}: Props) {
|
|
if (value === '') {
|
|
return null;
|
|
}
|
|
|
|
const result = zxcvbn(value);
|
|
|
|
if (!result) {
|
|
return null;
|
|
}
|
|
|
|
const {score} = result;
|
|
const percent = Math.round(((score + 1) / MAX_SCORE) * 100);
|
|
|
|
const styles = css`
|
|
background: ${colors[score]};
|
|
width: ${percent}%;
|
|
`;
|
|
|
|
return (
|
|
<Fragment>
|
|
<StrengthProgress
|
|
role="progressbar"
|
|
aria-valuenow={score}
|
|
aria-valuemin={0}
|
|
aria-valuemax={100}
|
|
>
|
|
<StrengthProgressBar css={styles} />
|
|
</StrengthProgress>
|
|
<StrengthLabel>
|
|
{tct('Strength: [textScore]', {
|
|
textScore: <ScoreText>{labels[score]}</ScoreText>,
|
|
})}
|
|
</StrengthLabel>
|
|
</Fragment>
|
|
);
|
|
}
|
|
|
|
const StrengthProgress = styled('div')`
|
|
background: ${theme.gray200};
|
|
height: 8px;
|
|
border-radius: 2px;
|
|
overflow: hidden;
|
|
`;
|
|
|
|
const StrengthProgressBar = styled('div')`
|
|
height: 100%;
|
|
`;
|
|
|
|
const StrengthLabel = styled('div')`
|
|
font-size: 0.8em;
|
|
margin-top: ${space(0.25)};
|
|
color: ${theme.gray400};
|
|
`;
|
|
|
|
const ScoreText = styled('strong')`
|
|
color: ${p => p.theme.black};
|
|
`;
|
|
|
|
export default PasswordStrength;
|
|
|
|
/**
|
|
* This is a shim that allows the password strength component to be used
|
|
* outside of our main react application. Mostly useful since all of our
|
|
* registration pages aren't in the react app.
|
|
*/
|
|
export const attachTo = ({input, element}) =>
|
|
element &&
|
|
input &&
|
|
input.addEventListener(
|
|
'input',
|
|
throttle(e => {
|
|
render(<PasswordStrength value={e.target.value} />, element);
|
|
})
|
|
);
|