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
Field의 유효하지 않은 상태를 설정합니다.
'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
유효성 검사 타이밍을 설정합니다.
'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>
);
}Examples
With Description
Field에 대한 추가 설명을 표시합니다.
'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>
);
}Validation
입력된 값에 대한 유효성을 검사합니다.
'use client';
import { useState } from 'react';
import { Field, Text, TextInput, VStack } from '@vapor-ui/core';
export default function FieldValidation() {
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
return (
<VStack gap="$200" width="300px">
<Field.Root name="email" validationMode="onBlur">
<Field.Label flexDirection="column">
<Text typography="subtitle2">이메일</Text>
<TextInput type="email" required placeholder="이메일을 입력하세요" />
</Field.Label>
<Field.Description>최소 8자 이상 입력해주세요.</Field.Description>
<Field.Error match="valueMissing">이메일을 입력해주세요.</Field.Error>
<Field.Error match="typeMismatch">유효한 이메일 주소를 입력해주세요.</Field.Error>
<Field.Success match="valid">올바른 이메일 형식입니다.</Field.Success>
</Field.Root>
<Field.Root name="password" validationMode="onBlur">
<Field.Label flexDirection="column">
<Text typography="subtitle2">비밀번호</Text>
<TextInput
type="password"
required
minLength={8}
value={password}
onValueChange={(value) => setPassword(value)}
placeholder="비밀번호를 입력하세요"
/>
</Field.Label>
<Field.Description>최소 8자 이상 입력해주세요.</Field.Description>
<Field.Error match="valueMissing">비밀번호를 입력해주세요.</Field.Error>
<Field.Error match="tooShort">비밀번호는 최소 8자 이상이어야 합니다.</Field.Error>
<Field.Success match="valid">유효한 비밀번호입니다.</Field.Success>
</Field.Root>
<Field.Root name="confirmPassword">
<Field.Label flexDirection="column">
<Text typography="subtitle2">비밀번호 확인</Text>
<TextInput
type="password"
value={confirmPassword}
onValueChange={(value) => setConfirmPassword(value)}
placeholder="비밀번호를 다시 입력하세요"
pattern={password}
/>
</Field.Label>
<Field.Error match="patternMismatch">비밀번호가 일치하지 않습니다.</Field.Error>
<Field.Success match="valid">비밀번호가 일치합니다.</Field.Success>
</Field.Root>
</VStack>
);
}Required
필수 Field와 선택 Field를 구분하여 표시합니다.
'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" validationMode="onChange">
<Field.Label flexDirection="column">
<Text typography="subtitle2" foreground="secondary-100">
필수 입력 필드 <Text foreground="danger-100">*</Text>
</Text>
<TextInput required placeholder="필수 입력 항목입니다" />
</Field.Label>
<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">
<Text typography="subtitle2" foreground="secondary-100">
선택 입력 필드{' '}
<Text foreground="hint-100" typography="subtitle2">
(선택사항)
</Text>
</Text>
<TextInput placeholder="선택적으로 입력하세요" />
</Box>
<Field.Description>이 필드는 선택적으로 입력할 수 있습니다.</Field.Description>
</Field.Root>
</VStack>
);
}Disabled
disabled 속성을 사용하여 비활성화된 Field를 만들 수 있습니다.
'use client';
import { Field, Text, TextInput } from '@vapor-ui/core';
export default function FieldDisabled() {
return (
<Field.Root name="disabled" disabled>
<Field.Label flexDirection="column">
<Text typography="subtitle2" foreground="secondary-100">
비활성 필드
</Text>
<TextInput value="이 필드는 수정할 수 없습니다" />
</Field.Label>
<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 Form Elements
다양한 폼 요소(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.Error match="typeMismatch">유효한 이메일 주소를 입력해주세요.</Field.Error>
</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>
);
}Controlled
제어 컴포넌트로 Field를 관리하고 실시간으로 상태를 표시합니다.
'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>
);
}Props Table
Field.Root
Loading component documentation...
Field.Label
Loading component documentation...
Field.Description
Loading component documentation...
Field.Error
Loading component documentation...
Field.Success
Loading component documentation...