Text Fields
Text Field
이 문서는 정리 중이에요. 문의 내용은 #_design_core 채널을 찾아주세요.
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldPreview() {
return (
<TextField>
<TextFieldInput autoFocus />
</TextField>
);
}
Installation
npx @seed-design/cli@latest add text-field
pnpm dlx @seed-design/cli@latest add text-field
yarn dlx @seed-design/cli@latest add text-field
bun x @seed-design/cli@latest add text-field
Props
TextField
Prop | Type | Default |
---|---|---|
asChild? | boolean | false |
maxGraphemeCount? | number | - |
onValueChange? | ((values: { value: string; graphemes: string[]; slicedValue: string; slicedGraphemes: string[]; }) => void) | - |
name? | string | - |
invalid? | boolean | false |
readOnly? | boolean | false |
disabled? | boolean | false |
required? | boolean | false |
defaultValue? | string | - |
value? | string | - |
size? | "xlarge" | "large" | "medium" | medium |
hideCharacterCount? | boolean | - |
errorMessage? | ReactNode | - |
description? | ReactNode | - |
suffix? | ReactNode | - |
suffixIcon? | ReactNode | - |
prefix? | ReactNode | - |
prefixIcon? | ReactNode | - |
indicator? | ReactNode | - |
label? | ReactNode | - |
TextFieldInput
Prop | Type | Default |
---|---|---|
asChild? | boolean | false |
Examples
State
Enabled
import { HStack } from "@seed-design/react";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldEnabled() {
return (
<HStack width="full" gap="x3">
<TextField label="라벨" description="설명을 써주세요">
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
<TextField
label="라벨"
description="설명을 써주세요"
invalid
errorMessage="오류가 발생한 이유를 써주세요"
>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
</HStack>
);
}
Disabled
import { HStack } from "@seed-design/react";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldDisabled() {
return (
<HStack width="full" gap="x3">
<TextField label="라벨" description="설명을 써주세요" disabled>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
<TextField
label="라벨"
description="설명을 써주세요"
disabled
invalid
errorMessage="오류가 발생한 이유를 써주세요"
>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
</HStack>
);
}
Read Only
import { HStack } from "@seed-design/react";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldReadOnly() {
return (
<HStack width="full" gap="x3">
<TextField label="라벨" description="설명을 써주세요" readOnly>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
<TextField
label="라벨"
description="설명을 써주세요"
readOnly
invalid
errorMessage="오류가 발생한 이유를 써주세요"
>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
</HStack>
);
}
Customizable Parts
Prefix
https://
import { IconMagnifyingglassLine } from "@karrotmarket/react-monochrome-icon";
import { HStack } from "@seed-design/react";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldPrefix() {
return (
<HStack width="full" gap="x3">
<TextField label="라벨" description="설명을 써주세요" prefix="https://">
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
<TextField
label="라벨"
description="설명을 써주세요"
prefixIcon={<IconMagnifyingglassLine />}
>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
</HStack>
);
}
Suffix
cm
import { IconWonLine } from "@karrotmarket/react-monochrome-icon";
import { HStack } from "@seed-design/react";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldSuffix() {
return (
<HStack width="full" gap="x3">
<TextField label="라벨" description="설명을 써주세요" suffix="cm">
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
<TextField label="라벨" description="설명을 써주세요" suffixIcon={<IconWonLine />}>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
</HStack>
);
}
Both Affixes
만세
import { IconPlusCircleLine, IconWonLine } from "@karrotmarket/react-monochrome-icon";
import { HStack } from "@seed-design/react";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldSuffix() {
return (
<HStack width="full" gap="x3">
<TextField label="라벨" description="설명을 써주세요" prefix="만" suffix="세">
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
<TextField
label="라벨"
description="설명을 써주세요"
prefixIcon={<IconPlusCircleLine />}
suffixIcon={<IconWonLine />}
>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
</HStack>
);
}
Indicator
(선택)
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldIndicator() {
return (
<TextField label="라벨" description="설명을 써주세요" indicator="(선택)">
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
);
}
Grapheme Count
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldGraphemeCount() {
return (
<TextField label="라벨" description="설명을 써주세요" maxGraphemeCount={8}>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
);
}
Size
XLarge
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldXlarge() {
return (
<TextField label="라벨" description="설명을 써주세요" size="xlarge">
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
);
}
Large
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldLarge() {
return (
<TextField label="라벨" description="설명을 써주세요" size="large">
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
);
}
Medium
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldMedium() {
return (
<TextField label="라벨" description="설명을 써주세요" size="medium">
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
);
}
Use Cases
Form
import { HStack, VStack } from "@seed-design/react";
import { useCallback, useState, type FormEvent } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
interface FormValues {
name: string;
address: string;
}
type FieldErrors = Record<keyof FormValues, string | null>;
export default function TextFieldForm() {
const [formValues, setFormValues] = useState<FormValues>({
name: "",
address: "",
});
const [fieldErrors, setFieldStates] = useState<FieldErrors>({
name: null,
address: null,
});
const validateForm = useCallback((): boolean => {
let isValid = true;
const newFieldErrors: FieldErrors = {
name: null,
address: null,
};
// Name validation
if (!formValues.name) {
newFieldErrors.name = "필수 입력 항목입니다";
isValid = false;
}
if (!formValues.address.startsWith("대한민국")) {
newFieldErrors.address = "대한민국으로 시작해주세요";
isValid = false;
}
if (!formValues.address) {
newFieldErrors.address = "필수 입력 항목입니다";
isValid = false;
}
setFieldStates(newFieldErrors);
return isValid;
}, [formValues]);
const handleSubmit = useCallback(
(event: FormEvent) => {
event.preventDefault();
if (validateForm()) {
window.alert(JSON.stringify(formValues, null, 2));
}
},
[formValues, validateForm],
);
const handleReset = useCallback((event: FormEvent) => {
event.preventDefault();
setFormValues({ name: "", address: "" });
setFieldStates({ name: null, address: null });
}, []);
const handleNameChange = (value: string) => {
setFormValues((prev) => ({ ...prev, name: value }));
setFieldStates((prev) => ({ ...prev, name: null }));
};
const handleAddressChange = (value: string) => {
setFormValues((prev) => ({ ...prev, address: value }));
setFieldStates((prev) => ({ ...prev, address: null }));
};
return (
<VStack gap="x3" width="full" as="form" onSubmit={handleSubmit} onReset={handleReset}>
<HStack gap="x2">
<TextField
label="이름"
indicator="(필수)"
description="이름을 써주세요"
required
value={formValues.name}
onValueChange={({ value }) => handleNameChange(value)}
{...(fieldErrors.name && { invalid: true, errorMessage: fieldErrors.name })}
>
<TextFieldInput placeholder="홍길동" />
</TextField>
<TextField
label="주소"
indicator="(필수)"
description="주소를 써주세요"
maxGraphemeCount={30}
required
value={formValues.address}
onValueChange={({ slicedValue }) => handleAddressChange(slicedValue)}
{...(fieldErrors.address && { invalid: true, errorMessage: fieldErrors.address })}
>
<TextFieldInput placeholder="대한민국" />
</TextField>
</HStack>
<HStack gap="x2">
<ActionButton type="reset" variant="neutralWeak">
초기화
</ActionButton>
<ActionButton type="submit" flexGrow={1}>
제출
</ActionButton>
</HStack>
</VStack>
);
}
React Hook Form
import { HStack, VStack } from "@seed-design/react";
import { useCallback, type FormEvent } from "react";
import { useController, useForm } from "react-hook-form";
import { ActionButton } from "seed-design/ui/action-button";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
interface FormValues {
name: string;
address: string;
}
export default function TextFieldReactHookForm() {
const { handleSubmit, reset, control } = useForm<FormValues>({
defaultValues: {
name: "",
address: "",
},
});
const { field: nameField, fieldState: nameFieldState } = useController({
name: "name",
control,
rules: {
required: "필수 입력 항목입니다",
},
});
const {
field: { onChange: addressOnChange, ...addressField },
fieldState: addressFieldState,
} = useController({
name: "address",
control,
rules: {
required: "필수 입력 항목입니다",
pattern: { value: /^대한민국/, message: "대한민국으로 시작해주세요" },
},
});
const onValid = useCallback((data: FormValues) => {
window.alert(JSON.stringify(data, null, 2));
}, []);
const onReset = useCallback(
(event: FormEvent) => {
event.preventDefault();
reset();
},
[reset],
);
return (
<VStack gap="x3" width="full" as="form" onSubmit={handleSubmit(onValid)} onReset={onReset}>
<HStack gap="x2">
<TextField
label="이름"
indicator="(필수)"
description="이름을 써주세요"
invalid={nameFieldState.invalid}
errorMessage={nameFieldState.error?.message}
required
{...nameField}
>
<TextFieldInput placeholder="홍길동" />
</TextField>
<TextField
label="주소"
indicator="(필수)"
description="주소를 써주세요"
invalid={addressFieldState.invalid}
errorMessage={addressFieldState.error?.message}
maxGraphemeCount={30}
onValueChange={({ slicedValue }) => addressOnChange(slicedValue)}
required
{...addressField}
>
<TextFieldInput placeholder="대한민국" />
</TextField>
</HStack>
<HStack gap="x2">
<ActionButton type="reset" variant="neutralWeak">
초기화
</ActionButton>
<ActionButton type="submit" flexGrow={1}>
제출
</ActionButton>
</HStack>
</VStack>
);
}
Number Formatting
import { useMemo, useState } from "react";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldNumberFormatting() {
const [value, setValue] = useState("1000");
const formattedValue = useMemo(() => {
if (value === "") return value;
const number = Number(value.replace(/,/g, ""));
if (Number.isNaN(number)) return "";
return number.toLocaleString();
}, [value]);
return (
<TextField
label="금액"
description="금액을 써주세요"
value={formattedValue}
onValueChange={({ value }) => setValue(value)}
>
<TextFieldInput placeholder="9,999,999" />
</TextField>
);
}
Slicing
import { useState } from "react";
import { TextField, TextFieldInput } from "seed-design/ui/text-field";
export default function TextFieldSlicing() {
const [value, setValue] = useState("");
return (
<TextField
label="라벨"
description="6글자까지 입력 가능합니다"
maxGraphemeCount={6}
value={value}
onValueChange={({ slicedValue }) => setValue(slicedValue)}
>
<TextFieldInput placeholder="플레이스홀더" />
</TextField>
);
}
Last updated on