# Text Field Input
URL: /lynx/components/text-field-input
Source: https://github.com/daangn/seed-design/blob/dev/docs/content/lynx/components/text-field-input.mdx
한 줄 텍스트를 입력받고 Field의 레이블·설명·오류 상태와 조합하는 컴포넌트입니다.
Lynx Engine 최소 버전: 3.6
사용 XElement:
사용 가능 버전: @seed-design/lynx-react@0.4.0, @seed-design/lynx-css@0.7.0
## Preview
```tsx
import "./styles";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
);
}
```
Lynx의 `TextFieldInput`은 React의 `autoFocus` prop을 지원하지 않습니다. 처음부터 포커스해야 한다면 입력에 `id`를 지정하고, 화면이 준비된 뒤 Lynx native input의 `focus` UI method를 호출하세요.
## Installation
- npm: npx @seed-design/cli add ui:text-field
- pnpm: pnpm dlx @seed-design/cli add ui:text-field
- yarn: yarn dlx @seed-design/cli add ui:text-field
- bun: bun x @seed-design/cli add ui:text-field
## Props
### `TextField`
### `TextFieldInput`
## Examples
### State
#### Enabled
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
);
}
```
#### Disabled
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
);
}
```
#### Read Only
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
);
}
```
### Size
`size`로 TextField의 크기를 정합니다. (default: `large`)
Lynx에서는 `large`와 `medium`을 지원합니다. CSS viewport breakpoint가 없어 `responsive`는 지원하지 않습니다.
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
);
}
```
### Customizable Parts
아이콘만으로 맥락을 전달할 때는 `description` 또는 `label`에 아이콘의 의미를 설명하는 텍스트를 포함하세요. 아이콘 자체를 읽어야 한다면 `accessibility-label`을 지정하세요.
#### Prefix
```tsx
import "./styles";
import IconMagnifyingglassLine from "@karrotmarket/lynx-monochrome-icon/IconMagnifyingglassLine";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
}
>
}
>
);
}
```
#### Suffix
```tsx
import "./styles";
import IconWonLine from "@karrotmarket/lynx-monochrome-icon/IconWonLine";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
}>
}>
);
}
```
#### Both Affixes
```tsx
import "./styles";
import IconPlusCircleLine from "@karrotmarket/lynx-monochrome-icon/IconPlusCircleLine";
import IconWonLine from "@karrotmarket/lynx-monochrome-icon/IconWonLine";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
}
suffixIcon={}
>
}
suffixIcon={}
>
);
}
```
#### Indicator
`indicator` 또는 `showRequiredIndicator` prop을 사용할 수 있습니다.
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
);
}
```
#### Grapheme Count
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
return (
);
}
```
`value`를 사용자 인식 문자(grapheme cluster) 단위로 나눈 결과를 `onValueChange` 콜백의 `graphemes`와 `slicedGraphemes`로 제공합니다.
```tsx
import "./styles";
import { useState } from "@lynx-js/react";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
const [value, setValue] = useState("");
const [graphemes, setGraphemes] = useState([]);
return (
{
setValue(slicedValue);
setGraphemes(slicedGraphemes);
}}
>
graphemes.length: {JSON.stringify(graphemes.length)}
value.length: {JSON.stringify(value.length)}
graphemes: {JSON.stringify(graphemes)}
value: {value}
);
}
```
### Use Cases
#### Controlled State
Lynx는 HTML Form을 지원하지 않습니다. `value`와 `onValueChange`를 사용해 입력값을 React state로 관리할 수 있습니다.
```tsx
import "./styles";
import { useState } from "@lynx-js/react";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
const [value, setValue] = useState("안녕하세요");
return (
setValue(nextValue)}
>
입력값: {value}
);
}
```
#### Number Formatting
```tsx
import "./styles";
import { useState } from "@lynx-js/react";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
// Lynx JS 런타임에는 `Intl`이 없어 `toLocaleString()`이 구분 기호를 넣지 않으므로 직접 넣습니다.
function formatNumber(value: string) {
if (value === "") return value;
const number = Number(value.replace(/,/g, ""));
if (Number.isNaN(number)) return "";
const [integer = "", fraction] = String(number).split(".");
const grouped = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return fraction === undefined ? grouped : `${grouped}.${fraction}`;
}
export default function Example() {
const [value, setValue] = useState("1000");
const formattedValue = formatNumber(value);
return (
setValue(nextValue)}
>
);
}
```
#### Slicing
```tsx
import "./styles";
import { useState } from "@lynx-js/react";
import { VStack } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export default function Example() {
const [value, setValue] = useState("");
return (
setValue(slicedValue)}
>
);
}
```
### Lynx Usage
#### Keyboard Avoidance
`KeyboardAvoidingScrollView.Content` 안에 배치하면 `TextFieldInput`이 focus될 때 자동으로 등록되어 키보드에 가려지지 않는 위치로 스크롤됩니다. `KeyboardAvoidingScrollView.Footer` 안에 배치하면 Footer와 함께 키보드 위로 올라가므로 스크롤하지 않습니다.
```tsx
import { KeyboardAvoidingScrollView } from "@seed-design/lynx-react";
import { TextField, TextFieldInput } from "@/components/ui/text-field";
export function KeyboardAwareFields() {
return (
);
}
```
## Web Version Differences
- 편집 가능한 상태에서는 HTML `` 대신 Lynx native ``을 렌더링합니다.
- `readOnly` 상태에서는 focus, selection, 잘라내기 메뉴가 없는 ``를 렌더링합니다. 이때 ref는 ``를 가리키므로 input 전용 UI method와 이벤트를 사용할 수 없습니다.
- `onChange` 대신 `TextField`의 `onValueChange`를 사용합니다. native `bindinput`은 `TextFieldInput`에 직접 전달할 수 있습니다.
- DOM id로 label을 연결하지 않으므로 입력에 `accessibility-label`을 직접 지정하세요.
- `show-soft-input-on-focus`의 기본값은 `true`입니다. 커스텀 키보드를 사용한다면 `false`로 지정하세요.
- `android-set-soft-input-mode`는 기본값이 `"unspecified"`이며 host window 전체에 영향을 줍니다. 같은 window의 입력에는 같은 값을 사용하고, `KeyboardAvoidingScrollView`가 키보드 회피를 맡는 화면에서는 `"nothing"`을 사용하세요.
## Unsupported Lynx Features
- HTML form submit, browser validation, React Hook Form, `aria-describedby` id 연결은 지원하지 않습니다.