Skip to content

Commit

Permalink
feat: Input.OTP support mask prop (#48257)
Browse files Browse the repository at this point in the history
* feat: Input.OTP support mask prop

* fix: fix

* fix: fix

* test: add test case

* test: add test case

* chore: fix

* chore: update

* chore: remove

* chore: rename useOTPSingleValue

* fix: fix

* fix: fix

* chore: rm 3 lib

* chore: add 3 lib

* fix: fix

* fix: fix

* test: fix test case

* test: fix test case

* fix: fix

* fix: fix

---------

Signed-off-by: lijianan <574980606@qq.com>
  • Loading branch information
li-jia-nan committed Apr 9, 2024
1 parent 4621bd9 commit 53cbceb
Show file tree
Hide file tree
Showing 9 changed files with 539 additions and 443 deletions.
9 changes: 7 additions & 2 deletions components/input/OTP/OTPInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ export interface OTPInputProps extends Omit<InputProps, 'onChange'> {
onChange: (index: number, value: string) => void;
/** Tell parent to do active offset */
onActiveChange: (nextIndex: number) => void;

mask?: boolean | string;
}

const OTPInput = React.forwardRef<InputRef, OTPInputProps>((props, ref) => {
const { value, onChange, onActiveChange, index, ...restProps } = props;
const { value, onChange, onActiveChange, index, mask, ...restProps } = props;

const internalValue = value && typeof mask === 'string' ? mask : value;

const onInternalChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
onChange(index, e.target.value);
Expand Down Expand Up @@ -56,13 +60,14 @@ const OTPInput = React.forwardRef<InputRef, OTPInputProps>((props, ref) => {
<Input
{...restProps}
ref={inputRef}
value={value}
value={internalValue}
onInput={onInternalChange}
onFocus={syncSelection}
onKeyDown={onInternalKeyDown}
onKeyUp={onInternalKeyUp}
onMouseDown={syncSelection}
onMouseUp={syncSelection}
type={mask === true ? 'password' : 'text'}
/>
);
});
Expand Down
22 changes: 18 additions & 4 deletions components/input/OTP/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import pickAttrs from 'rc-util/lib/pickAttrs';

import { getMergedStatus } from '../../_util/statusUtils';
import type { InputStatus } from '../../_util/statusUtils';
import { devUseWarning } from '../../_util/warning';
import { ConfigContext } from '../../config-provider';
import useCSSVarCls from '../../config-provider/hooks/useCSSVarCls';
import useSize from '../../config-provider/hooks/useSize';
import type { SizeType } from '../../config-provider/SizeContext';
import { FormItemInputContext } from '../../form/context';
import type { FormItemStatusContextProps } from '../../form/context';
import type { Variant } from '../../form/hooks/useVariants';
import type { InputRef } from '../Input';
import useStyle from '../style/otp';
Expand Down Expand Up @@ -42,6 +44,8 @@ export interface OTPProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'on
// Status
disabled?: boolean;
status?: InputStatus;

mask?: boolean | string;
}

function strToArr(str: string) {
Expand All @@ -61,9 +65,19 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
disabled,
status: customStatus,
autoFocus,
mask,
...restProps
} = props;

if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Input.OTP');
warning(
!(typeof mask === 'string' && mask.length > 1),
'usage',
'`mask` prop should be a single character.',
);
}

const { getPrefixCls, direction } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('otp', customizePrefixCls);

Expand All @@ -85,7 +99,7 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
const formContext = React.useContext(FormItemInputContext);
const mergedStatus = getMergedStatus(formContext.status, customStatus);

const proxyFormContext = React.useMemo(
const proxyFormContext = React.useMemo<FormItemStatusContextProps>(
() => ({
...formContext,
status: mergedStatus,
Expand Down Expand Up @@ -194,10 +208,11 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
};

// ======================== Render ========================
const inputSharedProps = {
const inputSharedProps: Partial<OTPInputProps> = {
variant,
disabled,
status: mergedStatus as InputStatus,
mask,
};

return wrapCSSVar(
Expand All @@ -216,10 +231,9 @@ const OTP = React.forwardRef<OTPRef, OTPProps>((props, ref) => {
)}
>
<FormItemInputContext.Provider value={proxyFormContext}>
{new Array(length).fill(0).map((_, index) => {
{Array.from({ length }).map((_, index) => {
const key = `otp-${index}`;
const singleValue = valueCells[index] || '';

return (
<OTPInput
ref={(inputEle) => {
Expand Down
10 changes: 7 additions & 3 deletions components/input/Password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@ const actionMap: Record<PropertyKey, keyof React.DOMAttributes<HTMLSpanElement>>
type IconPropsType = React.HTMLAttributes<HTMLSpanElement> & React.Attributes;

const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
const { visibilityToggle = true } = props;
const {
disabled,
action = 'click',
visibilityToggle = true,
iconRender = defaultIconRender,
} = props;

const visibilityControlled =
typeof visibilityToggle === 'object' && visibilityToggle.visible !== undefined;
const [visible, setVisible] = useState(() =>
Expand All @@ -53,7 +59,6 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
const removePasswordTimeout = useRemovePasswordTimeout(inputRef);

const onVisibleChange = () => {
const { disabled } = props;
if (disabled) {
return;
}
Expand All @@ -70,7 +75,6 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
};

const getIcon = (prefixCls: string) => {
const { action = 'click', iconRender = defaultIconRender } = props;
const iconTrigger = actionMap[action] || '';
const icon = iconRender(visible);
const iconProps: IconPropsType = {
Expand Down

0 comments on commit 53cbceb

Please sign in to comment.