DocSpace-buildtools/packages/asc-web-components/password-input/index.js
Artem Tarasov d960ae6220 Merge branch 'develop' into bugfix/fixed-styles
# Conflicts:
#	web/ASC.Web.Client/src/components/NavMenu/sub-components/header.js
2021-06-02 15:09:03 +03:00

591 lines
16 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from "react";
import PropTypes from "prop-types";
import equal from "fast-deep-equal/react";
import InputBlock from "../input-block";
import { RefreshIcon } from "./svg";
import Link from "../link";
import Text from "../text";
import Tooltip from "../tooltip";
import Base from "../themes/base";
import {
Progress,
CopyLink,
NewPasswordButton,
PasswordProgress,
StyledInput,
TooltipStyle,
StyledTooltipContainer,
StyledTooltipItem,
} from "./styled-password-input";
class PasswordInput extends React.Component {
constructor(props) {
super(props);
const { inputValue, inputType, clipActionResource, emailInputName } = props;
this.ref = React.createRef();
this.refTooltip = React.createRef();
this.state = {
type: inputType,
progressColor: "transparent",
progressWidth: 0,
inputValue: inputValue,
copyLabel: clipActionResource,
disableCopyAction: emailInputName ? false : true,
displayTooltip: false,
validLength: false,
validDigits: false,
validCapital: false,
validSpecial: false,
};
}
hideTooltip = () => {
this.hideTooltip && this.refTooltip.current.hideTooltip();
};
onBlur = (e) => {
e.persist();
this.hideTooltip();
if (this.props.onBlur) this.props.onBlur(e);
};
onKeyDown = (e) => {
e.persist();
if (this.props.onKeyDown) this.props.onKeyDown(e);
};
changeInputType = () => {
this.hideTooltip();
const newType = this.state.type === "text" ? "password" : "text";
this.setState({
type: newType,
});
};
testStrength = (value) => {
const { generatorSpecial, passwordSettings } = this.props;
const specSymbols = new RegExp("[" + generatorSpecial + "]");
let capital;
let digits;
let special;
passwordSettings.upperCase
? (capital = /[A-Z]/.test(value))
: (capital = true);
passwordSettings.digits ? (digits = /\d/.test(value)) : (digits = true);
passwordSettings.specSymbols
? (special = specSymbols.test(value))
: (special = true);
return {
digits: digits,
capital: capital,
special: special,
length: value.trim().length >= passwordSettings.minLength,
};
};
checkPassword = (value) => {
const greenColor = "#44bb00";
const redColor = "#B40404";
const passwordValidation = this.testStrength(value);
const progressScore =
passwordValidation.digits &&
passwordValidation.capital &&
passwordValidation.special &&
passwordValidation.length;
const progressWidth =
(value.trim().length * 100) / this.props.passwordSettings.minLength;
const progressColor = progressScore
? greenColor
: value.length === 0
? "transparent"
: redColor;
this.props.onValidateInput &&
this.props.onValidateInput(progressScore, passwordValidation);
this.setState({
progressColor: progressColor,
progressWidth: progressWidth > 100 ? 100 : progressWidth,
inputValue: value,
validLength: passwordValidation.length,
validDigits: passwordValidation.digits,
validCapital: passwordValidation.capital,
validSpecial: passwordValidation.special,
});
};
onChangeAction = (e) => {
this.props.onChange && this.props.onChange(e);
if (this.props.simpleView) {
this.setState({
inputValue: e.target.value,
});
return;
}
this.checkPassword(e.target.value);
};
onGeneratePassword = (e) => {
if (this.props.isDisabled) return e.preventDefault();
const newPassword = this.getNewPassword();
if (this.state.type !== "text") {
this.setState({
type: "text",
});
}
this.checkPassword(newPassword);
this.props.onChange &&
this.props.onChange({ target: { value: newPassword } });
};
getNewPassword = () => {
const { passwordSettings, generatorSpecial } = this.props;
const length = passwordSettings.minLength;
const string = "abcdefghijklmnopqrstuvwxyz";
const numeric = "0123456789";
const special = generatorSpecial;
let password = "";
let character = "";
while (password.length < length) {
const a = Math.ceil(string.length * Math.random() * Math.random());
const b = Math.ceil(numeric.length * Math.random() * Math.random());
const c = Math.ceil(special.length * Math.random() * Math.random());
let hold = string.charAt(a);
if (passwordSettings.upperCase) {
hold = password.length % 2 == 0 ? hold.toUpperCase() : hold;
}
character += hold;
if (passwordSettings.digits) {
character += numeric.charAt(b);
}
if (passwordSettings.specSymbols) {
character += special.charAt(c);
}
password = character;
}
password = password
.split("")
.sort(() => 0.5 - Math.random())
.join("");
return password.substr(0, length);
};
copyToClipboard = (emailInputName) => {
const {
clipEmailResource,
clipPasswordResource,
clipActionResource,
clipCopiedResource,
isDisabled,
onCopyToClipboard,
} = this.props;
const { disableCopyAction, inputValue } = this.state;
if (isDisabled || disableCopyAction) return event.preventDefault();
this.setState({
disableCopyAction: true,
copyLabel: clipCopiedResource,
});
const textField = document.createElement("textarea");
const emailValue = document.getElementsByName(emailInputName)[0].value;
const formattedText =
clipEmailResource +
emailValue +
" | " +
clipPasswordResource +
inputValue;
textField.innerText = formattedText;
document.body.appendChild(textField);
textField.select();
document.execCommand("copy");
textField.remove();
onCopyToClipboard && onCopyToClipboard(formattedText);
setTimeout(() => {
this.setState({
disableCopyAction: false,
copyLabel: clipActionResource,
});
}, 2000);
};
shouldComponentUpdate(nextProps, nextState) {
return !equal(this.props, nextProps) || !equal(this.state, nextState);
}
componentDidUpdate(prevProps, prevState) {
if (
prevProps.clipActionResource !== this.props.clipActionResource &&
this.state.copyLabel !== this.props.clipCopiedResource
) {
this.setState({ copyLabel: this.props.clipActionResource });
}
}
renderTextTooltip = (settings, length, digits, capital, special) => {
return (
<>
<div className="break"></div>
<Text
className="text-tooltip"
fontSize="10px"
color="#A3A9AE"
as="span"
>
{settings.minLength ? length : null}{" "}
{settings.digits ? `, ${digits}` : null}{" "}
{settings.upperCase ? `, ${capital}` : null}{" "}
{settings.specSymbols ? `, ${special}` : null}
</Text>
<div className="break"></div>
</>
);
};
renderTextTooltip = () => {
const {
tooltipPasswordLength,
tooltipPasswordDigits,
tooltipPasswordCapital,
tooltipPasswordSpecial,
passwordSettings,
isTextTooltipVisible,
} = this.props;
return isTextTooltipVisible ? (
<>
<div className="break"></div>
<Text
className="text-tooltip"
fontSize="10px"
color="#A3A9AE"
as="span"
>
{passwordSettings.minLength ? tooltipPasswordLength : null}{" "}
{passwordSettings.digits ? `, ${tooltipPasswordDigits}` : null}{" "}
{passwordSettings.upperCase ? `, ${tooltipPasswordCapital}` : null}{" "}
{passwordSettings.specSymbols ? `, ${tooltipPasswordSpecial}` : null}
</Text>
<div className="break"></div>
</>
) : null;
};
renderTooltipContent = () =>
!this.props.isDisableTooltip && !this.props.isDisabled ? (
<StyledTooltipContainer
forwardedAs="div"
title={this.props.tooltipPasswordTitle}
>
{this.props.tooltipPasswordTitle}
<StyledTooltipItem
forwardedAs="div"
title={this.props.tooltipPasswordLength}
valid={this.validLength}
>
{this.props.tooltipPasswordLength}
</StyledTooltipItem>
{this.props.passwordSettings.digits && (
<StyledTooltipItem
forwardedAs="div"
title={this.props.tooltipPasswordDigits}
valid={this.validDigits}
>
{this.props.tooltipPasswordDigits}
</StyledTooltipItem>
)}
{this.props.passwordSettings.upperCase && (
<StyledTooltipItem
forwardedAs="div"
title={this.props.tooltipPasswordCapital}
valid={this.validCapital}
>
{this.props.tooltipPasswordCapital}
</StyledTooltipItem>
)}
{this.props.passwordSettings.specSymbols && (
<StyledTooltipItem
forwardedAs="div"
title={this.props.tooltipPasswordSpecial}
valid={this.validSpecial}
>
{this.props.tooltipPasswordSpecial}
</StyledTooltipItem>
)}
</StyledTooltipContainer>
) : null;
renderInputGroup = () => {
const {
inputName,
isDisabled,
scale,
size,
hasError,
hasWarning,
placeholder,
tabIndex,
maxLength,
theme,
id,
autoComplete,
} = this.props;
const { type, progressColor, progressWidth, inputValue } = this.state;
const iconName =
type === "password"
? "/static/images/eye.off.react.svg"
: "/static/images/eye.react.svg";
return (
<>
<InputBlock
className="input-relative"
id={id}
name={inputName}
hasError={hasError}
isDisabled={isDisabled}
iconName={iconName}
value={inputValue}
onIconClick={this.changeInputType}
onChange={this.onChangeAction}
scale={scale}
size={size}
type={type}
iconSize={16}
hoverColor={"#A3A9AE"}
isIconFill={true}
onBlur={this.onBlur}
onKeyDown={this.onKeyDown}
hasWarning={hasWarning}
placeholder={placeholder}
tabIndex={tabIndex}
maxLength={maxLength}
autoComplete={autoComplete}
theme={theme}
></InputBlock>
<TooltipStyle>
<Tooltip
id="tooltipContent"
effect="solid"
place="top"
offsetLeft={this.tooltipOffsetLeft}
reference={this.refTooltip}
>
{this.renderTooltipContent()}
</Tooltip>
</TooltipStyle>
<Progress
progressColor={progressColor}
progressWidth={progressWidth}
isDisabled={isDisabled}
/>
</>
);
};
render() {
//console.log('PasswordInput render()');
const {
emailInputName,
inputWidth,
onValidateInput,
className,
style,
simpleView,
hideNewPasswordButton,
isDisabled,
showCopyLink,
} = this.props;
const { copyLabel, disableCopyAction, type } = this.state;
return (
<StyledInput
onValidateInput={onValidateInput}
className={className}
style={style}
>
{simpleView ? (
<>
{this.renderInputGroup()}
{this.renderTextTooltip()}
</>
) : (
<>
<div className="password-field-wrapper">
<PasswordProgress
inputWidth={inputWidth}
data-for="tooltipContent"
data-tip=""
data-event="click"
ref={this.ref}
isDisabled={isDisabled}
>
{this.renderInputGroup()}
</PasswordProgress>
{!hideNewPasswordButton ? (
<NewPasswordButton isDisabled={isDisabled}>
<RefreshIcon
size="medium"
onClick={this.onGeneratePassword}
/>
</NewPasswordButton>
) : null}
</div>
{this.renderTextTooltip()}
{showCopyLink && (
<CopyLink>
<Link
type="action"
isHovered={true}
fontSize="13px"
className="password-input_link"
isSemitransparent={disableCopyAction}
onClick={this.copyToClipboard.bind(this, emailInputName)}
>
{copyLabel}
</Link>
</CopyLink>
)}
</>
)}
</StyledInput>
);
}
}
PasswordInput.propTypes = {
/** Allows you to set the component id */
id: PropTypes.string,
/** Allows you to set the component auto-complete */
autoComplete: PropTypes.string,
/** It is necessary for correct display of values inside input */
inputType: PropTypes.oneOf(["text", "password"]),
/** Input name */
inputName: PropTypes.string,
/** Required to associate password field with email field */
emailInputName: PropTypes.string,
/** Input value */
inputValue: PropTypes.string,
/** Will be triggered whenever an PasswordInput typing */
onChange: PropTypes.func,
onKeyDown: PropTypes.func,
onBlur: PropTypes.func,
/** If you need to set input width manually */
inputWidth: PropTypes.string,
hasError: PropTypes.bool,
hasWarning: PropTypes.bool,
placeholder: PropTypes.string,
tabIndex: PropTypes.number,
maxLength: PropTypes.number,
/** Accepts class */
className: PropTypes.string,
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Set input disabled */
isDisabled: PropTypes.bool,
size: PropTypes.oneOf(["base", "middle", "big", "huge", "large"]),
scale: PropTypes.bool,
/** Allows to hide NewPasswordButton */
hideNewPasswordButton: PropTypes.bool,
/** Allows to hide Tooltip */
isDisableTooltip: PropTypes.bool,
/** Allows to show text Tooltip */
isTextTooltipVisible: PropTypes.bool,
/** Translation of text for copying email data and password */
clipActionResource: PropTypes.string,
/** Text translation email to copy */
clipEmailResource: PropTypes.string,
/** Text translation password to copy */
clipPasswordResource: PropTypes.string,
/** Text translation copy action to copy */
clipCopiedResource: PropTypes.string,
/** Text translation tooltip */
tooltipPasswordTitle: PropTypes.string,
/** Password text translation is long tooltip */
tooltipPasswordLength: PropTypes.string,
/** Digit text translation tooltip */
tooltipPasswordDigits: PropTypes.string,
/** Capital text translation tooltip */
tooltipPasswordCapital: PropTypes.string,
/** Special text translation tooltip */
tooltipPasswordSpecial: PropTypes.string,
/** Set of special characters for password generator and validator */
generatorSpecial: PropTypes.string,
NewPasswordButtonVisible: PropTypes.bool,
/** Set of settings for password generator and validator */
passwordSettings: PropTypes.object,
/** Will be triggered whenever an PasswordInput typing, return bool value */
onValidateInput: PropTypes.func,
/** Will be triggered if you press copy button, return formatted value */
onCopyToClipboard: PropTypes.func,
tooltipOffsetLeft: PropTypes.number,
/** Set simple view of password input (without tooltips, password progress bar and several additional buttons (copy and generate password) */
simpleView: PropTypes.bool,
/** Sets the link to copy the password visible */
showCopyLink: PropTypes.bool,
};
PasswordInput.defaultProps = {
inputType: "password",
inputName: "passwordInput",
autoComplete: "new-password",
theme: Base,
isDisabled: false,
size: "base",
scale: true,
hideNewPasswordButton: false,
isDisableTooltip: false,
isTextTooltipVisible: false,
clipEmailResource: "E-mail ",
clipPasswordResource: "Password ",
clipCopiedResource: "Copied",
generatorSpecial: "!@#$%^&*",
className: "",
tooltipOffsetLeft: 110,
simpleView: false,
passwordSettings: {
minLength: 8,
upperCase: false,
digits: false,
specSymbols: false,
},
showCopyLink: true,
};
export default PasswordInput;