Components
FieldButton
폼과 연동되는 필드형 버튼(headless) 패턴
기본 export FieldButton은 레이블·설명·오류 메시지와 메인 버튼을 한 덩어리로 제공합니다. buttonProps로 클릭·aria-label 등 버튼 속성을 넘깁니다. 값 배열·hidden input이 필요하면 values, onValuesChange, name을 함께 쓰면 됩니다.
커스텀 레이아웃이 필요할 때는 FieldButtonRoot, FieldButtonButton 등 headless 부품을 직접 조합할 수 있습니다.
기본 사용법
'use client';import FieldButton, { FieldButtonPlaceholder, FieldButtonValue,} from '@aift/ui/FieldButton';import { useState } from 'react';export default function FieldButtonPreviewExample() { const [selected, setSelected] = useState(''); return ( <div className="max-w-md"> <FieldButton values={selected ? [selected] : []} onValuesChange={(next) => setSelected(next[0] ?? '')} buttonProps={{ onClick: () => setSelected('서울'), 'aria-label': '알림 표시', }} showClearButton > {selected ? ( <FieldButtonValue>{selected}</FieldButtonValue> ) : ( <FieldButtonPlaceholder> 값을 입력하려면 클릭하세요 </FieldButtonPlaceholder> )} </FieldButton> </div> );}Label
레이블을 추가할 수 있습니다.
'use client';import FieldButton, { FieldButtonPlaceholder } from '@aift/ui/FieldButton';export default function FieldButtonLabelExample() { return ( <div className="max-w-md space-y-6"> <FieldButton label="레이블" buttonProps={{ type: 'button', onClick: () => alert('클릭') }} > <FieldButtonPlaceholder> 값을 입력하려면 클릭하세요. </FieldButtonPlaceholder> </FieldButton> </div> );}Message
invalid와 errorMessage는 오류 상태를 표시합니다.
이 필드는 유효하지 않습니다.
'use client';import FieldButton, { FieldButtonPlaceholder } from '@aift/ui/FieldButton';export default function FieldButtonMessageExample() { return ( <div className="max-w-md space-y-6"> <FieldButton invalid errorMessage="이 필드는 유효하지 않습니다." buttonProps={{ type: 'button', onClick: () => alert('클릭') }} > <FieldButtonPlaceholder> 값을 입력하려면 클릭하세요. </FieldButtonPlaceholder> </FieldButton> </div> );}Required (Dot)
required가 true이면 레이블 옆에 필수 표시 도트가 나타납니다.
'use client';import FieldButton, { FieldButtonPlaceholder } from '@aift/ui/FieldButton';export default function FieldButtonRequiredExample() { return ( <div className="max-w-md space-y-6"> <FieldButton required label="필수 입력" buttonProps={{ type: 'button', onClick: () => alert('클릭') }} > <FieldButtonPlaceholder> 값을 입력하려면 클릭하세요. </FieldButtonPlaceholder> </FieldButton> </div> );}Disabled
disabled는 버튼을 막고 클리어 버튼을 숨기며, hidden input도 제출되지 않습니다. readOnly는 조작만 막고 hidden 값은 유지할 수 있습니다.
'use client';import { useState } from 'react';import FieldButton, { FieldButtonPlaceholder } from '@aift/ui/FieldButton';export default function FieldButtonDisabledExample() { return ( <div className="max-w-md space-y-6"> <FieldButton disabled buttonProps={{ type: 'button', onClick: () => alert('클릭') }} > <FieldButtonPlaceholder> 입력 불가능한 상태입니다. </FieldButtonPlaceholder> </FieldButton> <FieldButton readOnly buttonProps={{ type: 'button', onClick: () => alert('클릭') }} > <FieldButtonPlaceholder> 입력 불가능한 상태입니다. </FieldButtonPlaceholder> </FieldButton> </div> );}Color
TextField와 동일하게 color( white | gray )로 메인 버튼 배경을 바꿀 수 있습니다. 세부 스타일은 buttonProps.className으로 덮어쓸 수 있습니다.
'use client';import FieldButton, { FieldButtonPlaceholder } from '@aift/ui/FieldButton';export default function FieldButtonColorExample() { return ( <div className="max-w-md space-y-6"> <FieldButton color="white" buttonProps={{ type: 'button', onClick: () => alert('클릭') }} > <FieldButtonPlaceholder>흰색 배경</FieldButtonPlaceholder> </FieldButton> <FieldButton color="gray" buttonProps={{ type: 'button', onClick: () => alert('클릭') }} > <FieldButtonPlaceholder>회색 배경</FieldButtonPlaceholder> </FieldButton> </div> );}React Hook Form
useController의 field와 values / onValuesChange / name을 맞추면 제출·검증과 연동할 수 있습니다.
'use client';import { useController, useForm } from 'react-hook-form';import FieldButton, { FieldButtonPlaceholder, FieldButtonValue,} from '@aift/ui/FieldButton';import { Button } from '@aift/ui/Button';import { useState } from 'react';interface FormData { region: string;}export default function FieldButtonReactHookFormExample() { const { reset, handleSubmit, formState: { errors, isSubmitting }, control, } = useForm<FormData>({ mode: 'onSubmit', reValidateMode: 'onChange', defaultValues: { region: '' }, }); const { field } = useController({ name: 'region', control, rules: { required: '지역을 선택해 주세요.' }, }); const [submitted, setSubmitted] = useState<FormData | null>(null); const selected = field.value; return ( <form onSubmit={handleSubmit((data) => setSubmitted(data))} className="max-w-md space-y-4" > <FieldButton required label="지역" name={field.name} values={selected ? [selected] : []} onValuesChange={(next) => field.onChange(next[0] ?? '')} invalid={!!errors.region} errorMessage={errors.region?.message as string | undefined} showClearButton={!!selected} buttonProps={{ type: 'button', onBlur: field.onBlur, onClick: () => field.onChange('서울'), 'aria-label': '지역 선택', }} > {selected ? ( <FieldButtonValue>{selected}</FieldButtonValue> ) : ( <FieldButtonPlaceholder>클릭하여 지역 선택</FieldButtonPlaceholder> )} </FieldButton> <div className="flex gap-2"> <Button type="button" variant="outlined" onClick={() => reset()}> 초기화 </Button> <Button type="submit" disabled={isSubmitting} variant="surface" color="primary" > 제출 </Button> </div> {submitted && ( <p className="text-sm font-semibold text-neutral-80"> 선택한 지역: {submitted.region} </p> )} </form> );}Props
| 컴포넌트 | 역할 |
|---|---|
FieldButtonRoot | values, onValuesChange, name, disabled, readOnly, invalid 및 루트 포인터 상태 |
FieldButtonButton | 메인 트리거 버튼(포커스·active 등) |
FieldButtonClearButton | onValuesChange([]) 호출 |
FieldButtonHiddenInput | valueIndex에 해당하는 hidden input |
FieldButtonDescription | 보조 설명 |
FieldButtonErrorMessage | 오류 영역(aria-live) |
useFieldButtonContext로 동일 컨텍스트의 props를 커스텀 레이아웃에 붙일 수 있습니다.