Field
Field는 폼 요소들을 감싸는 컨테이너 컴포넌트로, 라벨, 설명, 에러 메시지, 성공 메시지 등을 제공합니다.'use client';
import { Box, Field, Text, TextInput } from '@vapor-ui/core';
export default function DefaultField() {
return (
<Box width="300px">
<Field.Root name="username">
<Box render={<Field.Label />} flexDirection="column">
<Text typography="subtitle2" foreground="normal-200">
Field.Label
</Text>
<TextInput placeholder="사용자명을 입력하세요" />
</Box>
<Field.Description>Field.Description</Field.Description>
<Field.Error match={true}>Field.Error</Field.Error>
<Field.Success>Field.Success</Field.Success>
</Field.Root>
</Box>
);
}Property
Invalid State
invalid prop을 사용하여 필드의 유효하지 않은 상태를 표시할 수 있습니다.
'use client';
import { Box, Field, TextInput } from '@vapor-ui/core';
export default function FieldInvalid() {
// 공식 문서에 따라 custom error 타입 객체 반환
const validate = (value: unknown) => {
const email = String(value);
const EMAIL_REGEX =
/^[a-zA-Z0-9]{1}[a-zA-Z0-9-_.]*@[a-zA-Z0-9-]+.[a-zA-Z0-9-_]+(.[a-zA-Z0-9-_]+)?$/;
if (!EMAIL_REGEX.test(email)) return '올바른 이메일 형식을 입력해주세요.';
return null;
};
return (
<Box width="300px">
<Field.Root name="email" validationMode="onChange" validate={validate}>
<Box render={<Field.Label />} flexDirection="column">
이메일
<TextInput type="email" placeholder="이메일을 입력하세요" />
</Box>
<Field.Error />
</Field.Root>
</Box>
);
}Validation Mode
validationMode prop을 사용하여 언제 검증을 수행할지 제어할 수 있습니다. onChange는 입력할 때마다, onBlur는 포커스를 잃을 때 검증을 수행합니다.
'use client';
import { Box, Field, TextInput, VStack } from '@vapor-ui/core';
export default function FieldValidationMode() {
const validateEmail = (value: unknown) => {
const stringValue = String(value || '');
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!EMAIL_REGEX.test(stringValue)) return '올바른 이메일 형식을 입력해주세요.';
return null;
};
return (
<VStack gap="$200">
<Field.Root name="name" validationMode="onChange">
<Box render={<Field.Label />} flexDirection="column">
이름 (onChange 검증)
<TextInput required placeholder="이름을 입력하세요" />
</Box>
<Field.Description>입력할 때마다 실시간으로 검증됩니다.</Field.Description>
<Field.Error />
</Field.Root>
<Field.Root name="email" validationMode="onBlur" validate={validateEmail}>
<Box render={<Field.Label />} flexDirection="column">
이메일 (onBlur 검증)
<TextInput type="email" placeholder="이메일을 입력하세요" />
</Box>
<Field.Description>입력 필드를 벗어날 때 검증됩니다.</Field.Description>
<Field.Error />
</Field.Root>
</VStack>
);
}Match
Field.Error의 match prop을 사용하여 특정 조건에 따라 에러 메시지 표시를 제어할 수 있습니다.
'use client';
import { useState } from 'react';
import { Box, Field, TextInput, VStack } from '@vapor-ui/core';
export default function FieldMatch() {
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
return (
<VStack gap="$200" width="300px">
<Field.Root name="password" validationMode="onBlur">
<Box render={<Field.Label />} flexDirection="column">
비밀번호
<TextInput
type="password"
required
minLength={8}
value={password}
onValueChange={(value) => setPassword(value)}
placeholder="비밀번호를 입력하세요"
/>
</Box>
<Field.Description>최소 8자 이상 입력해주세요.</Field.Description>
<Field.Error match="valueMissing">비밀번호를 입력해주세요.</Field.Error>
<Field.Error match="tooShort">비밀번호는 최소 8자 이상이어야 합니다.</Field.Error>
</Field.Root>
<Field.Root name="confirmPassword">
<Box render={<Field.Label />} flexDirection="column">
비밀번호 확인
<TextInput
type="password"
value={confirmPassword}
onValueChange={(value) => setConfirmPassword(value)}
placeholder="비밀번호를 다시 입력하세요"
pattern={password}
/>
</Box>
<Field.Error match="patternMismatch">비밀번호가 일치하지 않습니다.</Field.Error>
</Field.Root>
</VStack>
);
}Examples
With Description
Field.Description을 사용하여 필드에 대한 추가 설명을 제공할 수 있습니다.
'use client';
import { Box, Field, TextInput } from '@vapor-ui/core';
export default function FieldDescription() {
return (
<Box width="300px">
<Field.Root name="email">
<Box render={<Field.Label />} flexDirection="column">
이메일 주소
<TextInput type="email" placeholder="example@email.com" />
</Box>
<Field.Description>
회원가입 시 사용할 이메일 주소를 입력해주세요.
</Field.Description>
</Field.Root>
</Box>
);
}With Validation
Field.Error와 Field.Success를 사용하여 유효성 검사 결과를 표시할 수 있습니다.
'use client';
import { Box, Field, TextInput } from '@vapor-ui/core';
export default function FieldValidation() {
return (
<Box width="300px">
<Field.Root name="password" validationMode="onChange">
<Box render={<Field.Label />} flexDirection="column">
비밀번호
<TextInput type="password" required placeholder="비밀번호를 입력하세요" />
</Box>
<Field.Error match="valueMissing">비밀번호를 입력해주세요.</Field.Error>
<Field.Success>✓ 사용 가능한 비밀번호입니다.</Field.Success>
</Field.Root>
</Box>
);
}With Checkbox
Checkbox 컴포넌트와 Field를 함께 사용하는 예제입니다.
'use client';
import { Box, Checkbox, Field } from '@vapor-ui/core';
export default function FieldCheckbox() {
return (
<Field.Root name="agreement">
<Box render={<Field.Label />} alignItems="center">
<Checkbox.Root />
이용약관에 동의합니다
</Box>
<Field.Description>서비스 이용을 위해 이용약관에 동의해주세요.</Field.Description>
</Field.Root>
);
}With Switch
Switch 컴포넌트와 Field를 함께 사용하는 예제입니다.
'use client';
import { Box, Field, Switch } from '@vapor-ui/core';
export default function FieldSwitch() {
return (
<Field.Root name="notifications">
<Box render={<Field.Label />} alignItems="center">
<Switch.Root />
알림 수신 동의
</Box>
<Field.Description>
새로운 소식과 업데이트를 이메일로 받아보실 수 있습니다.
</Field.Description>
</Field.Root>
);
}With RadioGroup
RadioGroup과 Field를 함께 사용하여 선택지가 있는 필드를 만드는 예제입니다.
'use client';
import { Field, Radio, RadioGroup } from '@vapor-ui/core';
export default function FieldRadioGroup() {
return (
<Field.Root name="gender" className="v-space-y-3">
<Field.Label>성별</Field.Label>
<RadioGroup.Root className="v-space-y-2">
<div className="v-flex v-items-center v-gap-2">
<Radio.Root value="male">
<Radio.Indicator />
</Radio.Root>
<Field.Label>남성</Field.Label>
</div>
<div className="v-flex v-items-center v-gap-2">
<Radio.Root value="female">
<Radio.Indicator />
</Radio.Root>
<Field.Label>여성</Field.Label>
</div>
<div className="v-flex v-items-center v-gap-2">
<Radio.Root value="other">
<Radio.Indicator />
</Radio.Root>
<Field.Label>기타</Field.Label>
</div>
</RadioGroup.Root>
<Field.Description>개인정보 보호를 위해 선택사항입니다.</Field.Description>
</Field.Root>
);
}With Different Input Types
다양한 폼 컨트롤(TextInput, Checkbox, Switch, Select 등)과 Field를 함께 사용하는 예제입니다.
'use client';
import { Box, Checkbox, Field, Select, Switch, TextInput, VStack } from '@vapor-ui/core';
export default function FieldWithInputs() {
return (
<VStack gap="$200" width="300px">
<Field.Root name="email">
<Box render={<Field.Label />} flexDirection="column">
이메일
<TextInput type="email" placeholder="example@domain.com" />
</Box>
<Field.Description>알림을 받을 이메일 주소를 입력하세요.</Field.Description>
</Field.Root>
{/* Checkbox with Field */}
<Field.Root name="newsletter">
<Box render={<Field.Label />} alignItems="center">
<Checkbox.Root />
뉴스레터 구독
</Box>
<Field.Description>최신 소식과 업데이트를 이메일로 받아보세요.</Field.Description>
</Field.Root>
{/* Switch with Field */}
<Field.Root name="notifications">
<Box render={<Field.Label />} alignItems="center">
<Switch.Root />
푸시 알림
</Box>
<Field.Description>중요한 알림을 즉시 받아보세요.</Field.Description>
</Field.Root>
{/* Select with Field */}
<Field.Root name="country">
<Select.Root placeholder="국가를 선택하세요">
<Box render={<Field.Label htmlFor="country-select" />} flexDirection="column">
국가
<Select.Trigger id="country-select" />
</Box>
<Select.Popup>
<Select.Item value="kr">대한민국</Select.Item>
<Select.Item value="us">미국</Select.Item>
<Select.Item value="jp">일본</Select.Item>
<Select.Item value="cn">중국</Select.Item>
</Select.Popup>
</Select.Root>
<Field.Description>거주 중인 국가를 선택하세요.</Field.Description>
</Field.Root>
</VStack>
);
}Required Fields
필수 필드와 선택 필드를 구분하여 표시하는 예제입니다.
'use client';
import { Box, Field, Text, TextInput, VStack } from '@vapor-ui/core';
export default function FieldRequired() {
return (
<VStack gap="$200" width="300px">
<Field.Root name="required-field">
<Box render={<Field.Label />} flexDirection="column">
<span>
필수 입력 필드 <Text foreground="danger-100">*</Text>
</span>
<TextInput required placeholder="필수 입력 항목입니다" />
</Box>
<Field.Description>
이 필드는 반드시 입력해야 하는 필수 항목입니다.
</Field.Description>
<Field.Error match="valueMissing">이 필드는 필수 입력 항목입니다.</Field.Error>
<Field.Success>입력이 완료되었습니다.</Field.Success>
</Field.Root>
{/* Optional Field */}
<Field.Root name="optional-field">
<Box render={<Field.Label />} flexDirection="column">
<span>
선택 입력 필드{' '}
<Text foreground="hint-100" typography="subtitle2">
(선택사항)
</Text>
</span>
<TextInput placeholder="선택적으로 입력하세요" />
</Box>
<Field.Description>이 필드는 선택적으로 입력할 수 있습니다.</Field.Description>
</Field.Root>
</VStack>
);
}Field States
다양한 필드 상태(일반, 오류, 성공, 비활성화)를 보여주는 예제입니다.
'use client';
import { Box, Field, TextInput, VStack } from '@vapor-ui/core';
export default function FieldStates() {
return (
<VStack gap="$200" width="300px">
<Field.Root name="normal">
<Box render={<Field.Label />} flexDirection="column">
일반 상태
<TextInput placeholder="일반 입력 필드" />
</Box>
<Field.Description>일반적인 필드 상태입니다.</Field.Description>
</Field.Root>
<Field.Root name="error">
<Box render={<Field.Label />} flexDirection="column">
오류 상태
<TextInput placeholder="오류가 있는 필드" invalid />
</Box>
<Field.Error match={true}>필수 입력 항목입니다. 값을 입력해주세요.</Field.Error>
</Field.Root>
<Field.Root name="success">
<Box render={<Field.Label />} flexDirection="column">
성공 상태
<TextInput placeholder="유효한 필드" defaultValue="valid@example.com" />
</Box>
<Field.Success>올바른 이메일 형식입니다.</Field.Success>
</Field.Root>
{/* Disabled State */}
<Field.Root name="disabled" disabled>
<Box render={<Field.Label />} flexDirection="column">
비활성화 상태
<TextInput placeholder="비활성화된 필드" />
</Box>
<Field.Description>이 필드는 현재 비활성화되어 있습니다.</Field.Description>
</Field.Root>
</VStack>
);
}Controlled Fields
제어 컴포넌트로 필드를 관리하고 실시간으로 상태를 표시하는 예제입니다.
'use client';
import { useState } from 'react';
import { Box, Button, Field, Form, Text, TextInput, VStack } from '@vapor-ui/core';
export default function FieldControlled() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
alert(`[제출된 값]\n- 이름: ${firstName}\n- 성: ${lastName}\n- 이메일: ${email}`);
};
return (
<Form onSubmit={handleSubmit}>
<VStack gap="$200" width="300px">
<Field.Root name="firstName">
<Box render={<Field.Label />} flexDirection="column">
이름
<TextInput
value={firstName}
onValueChange={(value) => setFirstName(value)}
placeholder="이름을 입력하세요"
/>
</Box>
<Text typography="body3" foreground="hint-200">
현재 값: {firstName || '(비어있음)'}
</Text>
</Field.Root>
<Field.Root name="lastName">
<Box render={<Field.Label />} flexDirection="column">
성
<TextInput
value={lastName}
onValueChange={(value) => setLastName(value)}
placeholder="성을 입력하세요"
/>
</Box>
<Text typography="body3" foreground="hint-200">
현재 값: {lastName || '(비어있음)'}
</Text>
</Field.Root>
<Field.Root name="email" validationMode="onChange">
<Box render={<Field.Label />} flexDirection="column">
이메일
<TextInput
type="email"
required
value={email}
onValueChange={(value) => setEmail(value)}
placeholder="이메일을 입력하세요"
/>
</Box>
<Text typography="body3" foreground="hint-200">
현재 값: {email || '(비어있음)'}
</Text>
<Field.Error match="typeMismatch">올바른 이메일 형식이 아닙니다.</Field.Error>
<Field.Error match="valueMissing">이메일을 입력해주세요.</Field.Error>
<Field.Success>유효한 이메일 형식입니다.</Field.Success>
</Field.Root>
<Button>제출</Button>
</VStack>
</Form>
);
}Disabled
disabled 속성을 사용하여 비활성화된 필드를 만들 수 있습니다.
'use client';
import { Box, Field, TextInput } from '@vapor-ui/core';
export default function FieldDisabled() {
return (
<Field.Root name="disabled" disabled>
<Box render={<Field.Label />} flexDirection="column">
비활성 필드
<TextInput value="이 필드는 수정할 수 없습니다" />
</Box>
<Field.Description>이 필드는 현재 비활성화되어 수정할 수 없습니다.</Field.Description>
</Field.Root>
);
}Props Table
Field.Root
Field의 메인 컨테이너 컴포넌트입니다. 폼 필드의 기본 구조를 제공하며, 라벨, 입력 요소, 설명, 에러 메시지 등을 포함할 수 있습니다.
| Prop | Default | Type |
|---|---|---|
name | - | string |
disabled? | false | boolean |
invalid | - | boolean |
validate | - | function |
validationMode? | 'onBlur' | 'onBlur''onChange' |
validationDebounceTime? | 0 | number |
className | - | stringfunction |
render | - | React.ReactElementfunction |
children | - | React.ReactNode |
Field.Label
필드에 라벨을 제공하는 컴포넌트입니다. 자동으로 폼 컨트롤과 연결됩니다.
| Prop | Default | Type |
|---|---|---|
children | - | React.ReactNode |
className | - | stringfunction |
render | - | React.ReactElementfunction |
Field.Description
필드에 대한 추가 설명을 제공하는 컴포넌트입니다.
| Prop | Default | Type |
|---|---|---|
children | - | React.ReactNode |
className | - | stringfunction |
render | - | React.ReactElementfunction |
Field.Error
필드 유효성 검사 실패 시 표시되는 오류 메시지 컴포넌트입니다.
| Prop | Default | Type |
|---|---|---|
children | - | React.ReactNode |
match | - | booleanstringfunction |
className | - | stringfunction |
render | - | React.ReactElementfunction |
Field.Success
필드 유효성 검사 성공 시 표시되는 성공 메시지 컴포넌트입니다.
| Prop | Default | Type |
|---|---|---|
children | - | React.ReactNode |
className | - | stringfunction |
render | - | React.ReactElementfunction |
Field.Validity
필드의 유효성 상태에 따라 커스텀 내용을 표시할 수 있는 컴포넌트입니다.
| Prop | Default | Type |
|---|---|---|
children | - | function |