OtpField
다른 곳에서 받아 옮겨 적는 짧은 코드를 위한, 한 글자씩 들어가는 칸의 행입니다. PIN, 문자로 온 인증 코드, 초대 키에 씁니다.
tsx
import { OtpField } from 'neba';
<OtpField label="Verification code" length={6} groupSize={3} onComplete={(code) => verify(code)} />;Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| variant공통 | 'solid' | 'outline' | 'text' | 'outline' | 표면의 무게. 채움 / 하이라인 / 없음 |
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 한 칸의 크기와 그 안의 타입 스케일. 컨트롤 사다리와 별개인 이유는, 칸은 줄 안의 컨트롤이 아니라 혼자 서 있는 글자 하나이기 때문입니다 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| density공통 | 'default' | 'compact' | 'default' | 칸 사이의 간격만 바꿉니다 |
| elevation공통 | 0 | 1 | 2 | 3 | 0 | 그림자 깊이. 0은 그림자 없음 |
| length | number | 6 | 코드의 자리수. 2–12로 잘립니다. 한 칸짜리는 TextField이고, 열두 칸을 넘기면 휴대폰 화면에 들어가지 않습니다 |
| charset | 'numeric' | 'alpha' | 'alphanumeric' | 'any' | 'numeric' | 입력할 수 있는 문자. 거부된 문자는 표시되지 않고 버려지며 onValueInvalid로 알려집니다. 기본값 numeric은 휴대폰에서 숫자 키패드를 띄웁니다 |
| mask | boolean | false | 입력한 문자를 가립니다 |
| groupSize | number | — | 몇 칸마다 구분자를 넣을지. 여섯 자리에 3이면 익숙한 3+3이 됩니다 |
| separator | ReactNode | '–' | 두 그룹 사이에 그려지는 것 |
| value | string | — | 코드. onValueChange와 함께 쓰면 controlled가 됩니다 |
| defaultValue | string | — | 처음 값 |
| onValueChange | (value: string) => void | — | 값이 바뀔 때 |
| onComplete | (value: string) => void | — | 모든 칸이 찼을 때. 코드를 검증할 시점입니다 |
| onValueInvalid | (value: string) => void | — | 입력되거나 붙여넣어진 글자에 charset이 거부하는 문자가 있었을 때 |
| autoSubmit | boolean | false | 코드가 완성되면 폼을 제출합니다 |
| label | ReactNode | — | 컨트롤과 연결되는 라벨. Base UI Field가 묶어 줍니다 |
| description | ReactNode | — | 보조 설명 |
| error | ReactNode | — | 오류 메시지. 값이 있으면 invalid 상태도 함께 켜지고 색 계열이 danger로 넘어갑니다 |
| invalid | boolean | !!error | 메시지 없이 invalid만 켭니다. 외부 폼 라이브러리가 유효성을 가질 때 |
| name | string | — | 폼이 제출될 때 이 필드를 가리키는 이름. 값 전체를 담은 숨겨진 input에 붙습니다 |
| required | boolean | false | 제출 전에 코드가 완성되어 있어야 합니다 |
| readOnly | boolean | false | 값은 보이지만 바꿀 수 없음. 색과 가장자리는 유지한 채 채도만 빠집니다 |
| disabled | boolean | false | 사용 불가. 색 계열을 버리고 중립 회색이 됩니다 |
| autoFocus | boolean | false | 마운트되면 첫 칸에 커서를 둡니다 |
나머지 <div> 속성은 그대로 칸들의 행에 전달됩니다. color·size·onChange는 제외됩니다. 앞의 둘은 Neba prop이고, 값은 onValueChange가 알려줍니다. 공용 축은 prop 규약에 있습니다.
예시
charset
charset은 입력할 수 있는 문자를 정합니다. 벗어나는 문자는 표시되지 않고 버려지며, 그 문자가 들어온 텍스트는 onValueInvalid가 알려줍니다. numeric은 휴대폰에 숫자 키패드를 띄우기도 합니다. any는 키보드가 만들어 내는 무엇이든 받습니다.
length와 groupSize
length는 코드의 자리수이며 2–12로 잘립니다. groupSize는 N칸마다 separator를 넣어 행을 나눕니다. separator는 따로 주지 않으면 en dash입니다.
mask · error · readOnly · disabled
mask는 입력한 문자를 가립니다. error는 메시지를 띄우면서 색 계열을 danger로 옮기고, invalid는 메시지 없이 같은 일을 합니다. readOnly는 코드를 선택해 복사할 수 있게 두고, disabled는 모든 칸이 반응하지 않게 합니다.
size
폼 안에서
name은 값 전체를 그 이름으로 폼에 올립니다. autoSubmit은 코드가 완성되는 순간 폼을 제출하며, 필드가 하나뿐인 인증 화면이 원하는 모양입니다.
tsx
<form action={verify}>
<OtpField name="code" length={6} required autoSubmit />
</form>접근성
- 입력하면 다음 칸으로 넘어가고, Backspace는 앞 글자를 지우며 뒤로 물러나며, 방향키로 행을 오갑니다.
- 붙여넣은 코드는 어떤 방식으로 붙여넣었든 caret이 있던 자리부터 칸에 나눠 들어갑니다.
- 클릭은 포인터 밑의 칸이 아니라 첫 빈 칸에 떨어지므로, 쓰다 만 코드를 중간부터 고쳐 넣을 일이 없습니다.
- 값 전체를 담은 클리핑된 input이 폼 제출과 휴대폰 autofill을 맡습니다.
autocomplete="one-time-code"는 이미 붙어 있습니다. label·description·error가 칸들과 연결되어 있어 셋 다 필드와 함께 읽힙니다.