import React, { useState, useRef } from "react"; import classNames from "classnames"; import { Button } from "../button"; import { mergeRefs } from "../_util/merge-refs"; import { useConfig } from "../_util/config-context"; import { useDefaultValue, ChangeContext } from "../form/controlled"; import { forwardRefWithStatics } from "../_util/forward-ref-with-statics"; import { List } from "../list"; import { Icon } from "../icon"; import { Input, InputProps } from "./Input"; import { Bubble, BubbleProps } from "../bubble"; export interface InputPasswordRule { /** * 规则描述 */ text: string; /** * 规则校验器 */ validator: (inputValue: string, subRuleResults: boolean[]) => boolean; /** * 子规则 */ subRules?: { /** * 规则描述 */ text: InputPasswordRule["text"]; /** * 规则校验器 */ validator: (inputValue: string) => boolean; }[]; } export interface InputPasswordChangeContext extends ChangeContext { /** * 是否符合校验规则 */ valid: boolean; } export interface InputPasswordProps extends Omit< InputProps, "multiline" | "type" | "onChange" | "rows" | "showCount" | "baseClassName" > { /** * 密码校验规则 * * **建议统一使用组件默认规则** * * @default 见示例 */ rules?: InputPasswordRule[] | false; /** * 规则气泡放置的位置 * @default "right-start" */ bubblePlacement?: BubbleProps["placement"]; /** * 值发生变更时进行回调 */ onChange?: (value: string, context: InputPasswordChangeContext) => void; } const defaultRules: InputPasswordRule[] = [ { text: "在8 ~ 30 位字符数以内(推荐12位以上)", validator: value => value.length >= 8 && value.length <= 30, }, { text: '不能以" / "开头', validator: value => !value.startsWith("/"), }, { text: "至少包含其中三项", validator: (_, results) => results.reduce((pre, cur) => (cur ? pre + 1 : pre), 0) >= 3, subRules: [ { text: "小写字母 a ~ z", validator: value => /[a-z]/.test(value), }, { text: "大写字母 A ~ Z", validator: value => /[A-Z]/.test(value), }, { text: "数字 0 ~ 9", validator: value => /[0-9]/.test(value), }, { text: "()!@#$%^&*|?><", validator: value => /[()!@#$%^&*|?><]/.test(value), }, ], }, ]; function isValid(value: string, rules: InputPasswordProps["rules"]) { return (rules || []).reduce((pre, cur) => { const subResults = Array.isArray(cur.subRules) ? cur.subRules.map(({ validator }) => validator(value)) : []; return pre && cur.validator(value, subResults); }, true); } export const InputPassword = forwardRefWithStatics( (props: InputPasswordProps, ref: React.Ref) => { const { classPrefix } = useConfig(); const { value, onChange, className, style, rules = defaultRules, bubblePlacement = "right-start", size, readonly, disabled, ...inputProps } = useDefaultValue(props, ""); const inputRef = useRef(null); const [show, setShow] = useState(false); return (
{rules.map(({ text, validator, subRules }, index) => { return ( validator(value) ) : [] ) ? "success" : "error--ring" } /> {text} {Array.isArray(subRules) && ( {subRules.map(({ text, validator }, index) => { return ( {text} ); })} )} ); })} ) } > onChange(value, { event, valid: isValid(value, rules) }) } ref={mergeRefs(ref, inputRef)} type={show ? "text" : "password"} readonly={readonly} disabled={disabled} />
{!readonly && !disabled && value && (
); }, { defaultLabelAlign: "middle", } ); InputPassword.displayName = "InputPassword";