ant-design/components/input/OTP/OTPInput.tsx
lijianan 53cbceb7db
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>
2024-04-09 14:47:58 +08:00

76 lines
2.1 KiB
TypeScript

import * as React from 'react';
import raf from 'rc-util/lib/raf';
import Input from '../Input';
import type { InputProps, InputRef } from '../Input';
export interface OTPInputProps extends Omit<InputProps, 'onChange'> {
index: number;
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, mask, ...restProps } = props;
const internalValue = value && typeof mask === 'string' ? mask : value;
const onInternalChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
onChange(index, e.target.value);
};
// ========================== Ref ===========================
const inputRef = React.useRef<InputRef>(null);
React.useImperativeHandle(ref, () => inputRef.current!);
// ========================= Focus ==========================
const syncSelection = () => {
raf(() => {
const inputEle = inputRef.current?.input;
if (document.activeElement === inputEle && inputEle) {
inputEle.select();
}
});
};
// ======================== Keyboard ========================
const onInternalKeyDown: React.KeyboardEventHandler<HTMLInputElement> = ({ key }) => {
if (key === 'ArrowLeft') {
onActiveChange(index - 1);
} else if (key === 'ArrowRight') {
onActiveChange(index + 1);
}
syncSelection();
};
const onInternalKeyUp: React.KeyboardEventHandler<HTMLInputElement> = (e) => {
if (e.key === 'Backspace' && !value) {
onActiveChange(index - 1);
}
syncSelection();
};
// ========================= Render =========================
return (
<Input
{...restProps}
ref={inputRef}
value={internalValue}
onInput={onInternalChange}
onFocus={syncSelection}
onKeyDown={onInternalKeyDown}
onKeyUp={onInternalKeyUp}
onMouseDown={syncSelection}
onMouseUp={syncSelection}
type={mask === true ? 'password' : 'text'}
/>
);
});
export default OTPInput;