feat: Input.OTP support mask prop (#48257)

* 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>
This commit is contained in:
lijianan 2024-04-09 14:47:58 +08:00 committed by GitHub
parent 4621bd9f5e
commit 53cbceb7db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 551 additions and 455 deletions

View File

@ -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);
@ -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'}
/>
);
});

View File

@ -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';
@ -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) {
@ -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);
@ -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,
@ -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(
@ -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) => {

View File

@ -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(() =>
@ -53,7 +59,6 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
const removePasswordTimeout = useRemovePasswordTimeout(inputRef);
const onVisibleChange = () => {
const { disabled } = props;
if (disabled) {
return;
}
@ -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 = {

View File

@ -10116,242 +10116,259 @@ exports[`renders components/input/demo/group.tsx extend context correctly 2`] =
exports[`renders components/input/demo/otp.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
class="ant-flex ant-flex-align-flex-start ant-flex-gap-middle ant-flex-vertical"
>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<h5
class="ant-typography"
>
With formatter (Upcase)
</h5>
With formatter (Upcase)
</h5>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
With Disabled
</h5>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<h5
class="ant-typography"
>
With Disabled
</h5>
With Length (8)
</h5>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
</div>
With variant
</h5>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<h5
class="ant-typography"
>
With Length (8)
</h5>
</div>
With custom display character
</h5>
<div
class="ant-space-item"
class="ant-otp"
>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
</div>
<div
class="ant-space-item"
>
<h5
class="ant-typography"
>
With variant
</h5>
</div>
<div
class="ant-space-item"
>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
</div>
`;
exports[`renders components/input/demo/otp.tsx extend context correctly 2`] = `[]`;
exports[`renders components/input/demo/otp.tsx extend context correctly 2`] = `
[
"Warning: [antd: Input.OTP] \`mask\` prop should be a single character.",
]
`;
exports[`renders components/input/demo/password-input.tsx extend context correctly 1`] = `
<div

View File

@ -3513,237 +3513,250 @@ exports[`renders components/input/demo/group.tsx correctly 1`] = `
exports[`renders components/input/demo/otp.tsx correctly 1`] = `
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
class="ant-flex ant-flex-align-flex-start ant-flex-gap-middle ant-flex-vertical"
>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<h5
class="ant-typography"
>
With formatter (Upcase)
</h5>
With formatter (Upcase)
</h5>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
With Disabled
</h5>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<h5
class="ant-typography"
>
With Disabled
</h5>
With Length (8)
</h5>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-disabled ant-input-outlined ant-otp-input"
disabled=""
size="1"
type="text"
value=""
/>
</div>
With variant
</h5>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
<div
class="ant-space-item"
<h5
class="ant-typography"
>
<h5
class="ant-typography"
>
With Length (8)
</h5>
</div>
With custom display character
</h5>
<div
class="ant-space-item"
class="ant-otp"
>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
</div>
<div
class="ant-space-item"
>
<h5
class="ant-typography"
>
With variant
</h5>
</div>
<div
class="ant-space-item"
>
<div
class="ant-otp"
>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-filled ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
<input
class="ant-input ant-input-outlined ant-otp-input"
size="1"
type="text"
value=""
/>
</div>
</div>
`;

View File

@ -13,13 +13,13 @@ describe('Input.OTP', () => {
mountTest(Input.OTP);
rtlTest(Input.OTP);
function getText(container: HTMLElement) {
const inputList = container.querySelectorAll('input');
const getText = (container: HTMLElement) => {
const inputList = container.querySelectorAll<HTMLInputElement>('input');
return Array.from(inputList)
.map((input) => input.value || ' ')
.join('')
.replace(/\s*$/, '');
}
};
beforeEach(() => {
jest.useFakeTimers();
@ -128,4 +128,34 @@ describe('Input.OTP', () => {
fireEvent.input(container.querySelector('input')!, { target: { value: 'little' } });
expect(getText(container)).toBe('LITTLE');
});
it('support mask prop', () => {
// default
const { container, rerender } = render(<OTP defaultValue="bamboo" />);
expect(getText(container)).toBe('bamboo');
// support string
rerender(<OTP defaultValue="bamboo" mask="*" />);
expect(getText(container)).toBe('******');
// support emoji
rerender(<OTP defaultValue="bamboo" mask="🔒" />);
expect(getText(container)).toBe('🔒🔒🔒🔒🔒🔒');
});
it('should throw Error when mask.length > 1', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<OTP mask="abc" />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Input.OTP] `mask` prop should be a single character.',
);
errSpy.mockRestore();
});
it('should not throw Error when mask.length <= 1', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<OTP mask="x" />);
expect(errSpy).not.toHaveBeenCalled();
errSpy.mockRestore();
});
});

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Flex, Input, Typography } from 'antd';
import type { GetProp } from 'antd';
import { Input, Space, Typography } from 'antd';
import type { OTPProps } from 'antd/es/input/OTP';
const { Title } = Typography;
@ -9,12 +10,12 @@ const App: React.FC = () => {
console.log('onChange:', text);
};
const sharedProps = {
const sharedProps: OTPProps = {
onChange,
};
return (
<Space direction="vertical">
<Flex gap="middle" align="flex-start" vertical>
<Title level={5}>With formatter (Upcase)</Title>
<Input.OTP formatter={(str) => str.toUpperCase()} {...sharedProps} />
<Title level={5}>With Disabled</Title>
@ -23,7 +24,9 @@ const App: React.FC = () => {
<Input.OTP length={8} {...sharedProps} />
<Title level={5}>With variant</Title>
<Input.OTP variant="filled" {...sharedProps} />
</Space>
<Title level={5}>With custom display character</Title>
<Input.OTP mask="🔒" {...sharedProps} />
</Flex>
);
};

View File

@ -124,11 +124,16 @@ Supports all props of `Input`.
Added in `5.16.0`.
> Notes for developers
>
> When the `mask` prop is string, we recommend receiving a single character or a single emoji. If multiple characters or multiple emoji are passed, a warning will be thrown.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| defaultValue | Default value | string | - | |
| disabled | Whether the input is disabled | boolean | false | |
| formatter | Format display, blank fields will be filled with ` ` | (value: string) => string | - | |
| mask | Custom display, the original value will not be modified | boolean \| string | `false` | `5.17.0` |
| length | The number of input elements | number | 6 | |
| status | Set validation status | 'error' \| 'warning' | - | |
| size | The size of the input box | `small` \| `middle` \| `large` | `middle` | |

View File

@ -125,11 +125,16 @@ interface CountConfig {
`5.16.0` 新增。
> 开发者注意事项:
>
> 当 `mask` 属性的类型为 string 时,我们强烈推荐接收单个字符或单个 emoji如果传入多个字符或多个 emoji则会在控制台抛出警告。
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| defaultValue | 默认值 | string | - | |
| disabled | 是否禁用 | boolean | false | |
| formatter | 格式化展示,留空字段会被 ` ` 填充 | (value: string) => string | - | |
| mask | 自定义展示,和 `formatter` 的区别是不会修改原始值 | boolean \| string | `false` | `5.17.0` |
| length | 输入元素数量 | number | 6 | |
| status | 设置校验状态 | 'error' \| 'warning' | - | |
| size | 输入框大小 | `small` \| `middle` \| `large` | `middle` | |