# Wheel Picker
URL: /lynx/components/wheel-picker
Source: https://github.com/daangn/seed-design/blob/dev/docs/content/lynx/components/wheel-picker.mdx
항목을 세로로 스크롤하여 하나 이상의 값을 선택하는 컴포넌트입니다.
사용 가능 버전: @seed-design/lynx-react@1.0.0, @seed-design/lynx-css@1.0.0
## Preview
```tsx
import "./styles";
import * as React from "@lynx-js/react";
import { WheelPicker } from "@/components/ui/wheel-picker";
const buildingOptions = Array.from({ length: 10 }, (_, index) => {
const value = String(101 + index);
return { value, label: `${value}동` };
});
const unitOptions = Array.from({ length: 15 }, (_, floorIndex) =>
Array.from({ length: 4 }, (_, lineIndex) => {
const value = String((floorIndex + 1) * 100 + lineIndex + 1);
return { value, label: `${value}호` };
}),
).flat();
export default function WheelPickerPreview() {
const [{ building, unit }, setAddress] = React.useState({
building: "103",
unit: "1202",
});
const columns = [
{
id: "building",
"accessibility-label": "동",
options: buildingOptions,
value: building,
onValueChange: (nextBuilding: string) => {
"background only";
setAddress((current) => ({ ...current, building: nextBuilding }));
},
},
{
id: "unit",
"accessibility-label": "호수",
options: unitOptions,
value: unit,
onValueChange: (nextUnit: string) => {
"background only";
setAddress((current) => ({ ...current, unit: nextUnit }));
},
},
];
return (
{building}동 {unit}호
);
}
```
## Installation
- npm: npx @seed-design/cli@latest add ui:wheel-picker
- pnpm: pnpm dlx @seed-design/cli@latest add ui:wheel-picker
- yarn: yarn dlx @seed-design/cli@latest add ui:wheel-picker
- bun: bun x @seed-design/cli@latest add ui:wheel-picker
## Props
### `WheelPicker`
### `WheelPickerColumn`
## Examples
### Customizing Columns
```tsx
import "./styles";
import * as React from "@lynx-js/react";
import { WheelPickerItemLabel } from "@seed-design/lynx-react";
import { WheelPicker } from "@/components/ui/wheel-picker";
const options = ["작게", "보통", "크게"].map((label) => ({ value: label, label }));
export default function WheelPickerPackageApi() {
const [value, setValue] = React.useState("보통");
return (
{
"background only";
setValue(nextValue);
},
renderLabel: (option) => (
{option.label}
),
},
]}
/>
선택값: {value}
);
}
```
Registry snippet의 `WheelPicker`에 `columns` 배열을 전달해 컬럼을 구성합니다. `WheelPicker`의 `className`과 각 컬럼의 `className`으로 스타일을 조정하고, 컬럼의 `renderLabel`로 항목 label을 바꿀 수 있습니다. 이 예제의 `WheelPickerItemLabel`은 snippet에서 제공하지 않는 label 부품이므로 `@seed-design/lynx-react`에서 가져옵니다.
`size="small"`의 기본 항목 높이는 36px, `size="medium"`은 44px입니다. 기본 `visibleItemCount`는 5이므로 `WheelPicker` 높이는 각각 180px과 220px입니다. `itemSize`와 `visibleItemCount`로 이를 조정할 수 있습니다. 컬럼의 `loop`는 기본 `false`이며 양 끝을 넘겨 끌면 저항을 주다가 놓으면 돌아옵니다.
### Value Change
`onIndexChange`는 사용자가 스크롤하거나 항목을 탭해 중앙 항목이 바뀔 때마다 호출됩니다. 정착 전에도 통과한 각 항목의 인덱스와 값을 순서대로 전달하므로 항목별 햅틱에 사용할 수 있습니다. 최종 선택값은 스크롤이 정착한 뒤 `onValueChange`로 처리하세요. 두 번째 인자인 `{ stepDelta }`는 직전에 정착한 항목에서 이동한 칸 수입니다.
외부 `value` 변경 시 `valueChangeBehavior="smooth"`로 부드럽게 이동할 수 있습니다. 기본값인 `"auto"`와 `"instant"`는 즉시 이동합니다. 외부 값 변경에 따른 이동은 사용자 조작 callback을 호출하지 않습니다. 상태를 변경하는 callback에는 `"background only"` 지시문을 사용하세요.
### React Element Label
```tsx
import "./styles";
import * as React from "@lynx-js/react";
import { WheelPicker } from "@/components/ui/wheel-picker";
function ColorDot({ color }: { color: string }) {
return (
);
}
const colorOptions = [
{ value: "carrot", name: "당근색", color: "var(--seed-color-palette-carrot-600)" },
{ value: "blue", name: "파란색", color: "var(--seed-color-palette-blue-700)" },
{ value: "green", name: "초록색", color: "var(--seed-color-palette-green-700)" },
].map(({ value, name, color }) => ({
value,
ariaLabel: name,
label: (
{name}
),
}));
export default function WheelPickerReactElementLabel() {
const [value, setValue] = React.useState("carrot");
return (
{
"background only";
setValue(nextValue);
},
},
]}
/>
선택값: {value}
);
}
```
문자열 label은 접근성 값으로 사용됩니다. React 요소를 label로 사용한다면 각 option에 `ariaLabel`을 제공하세요. 접근성 텍스트를 별도로 계산해야 할 때는 `getAriaValueText`를 사용할 수 있습니다. 접근성 값은 `getAriaValueText`, `ariaLabel`, 문자열 label, `value` 순으로 선택합니다. 버튼, 입력 요소와 여러 줄 콘텐츠는 넣지 않습니다.
커스텀 label의 문자열은 `` 안에 넣으세요. `renderLabel` 안의 ``는 항목의 선택·비활성 색을 따릅니다. 이 색을 유지하려면 커스텀 label에 색을 직접 지정하지 마세요. 타이포그래피 상속에는 [Feature Flags](/lynx/getting-started/feature-flags)의 `enableCSSInheritance: true` 설정을 사용합니다. Lynx에서는 CSS `inherit` 값을 지원하지 않습니다.
### Disabled
```tsx
import "./styles";
import { WheelPicker } from "@/components/ui/wheel-picker";
const options = ["낮음", "보통", "높음"].map((label) => ({ value: label, label }));
export default function WheelPickerStates() {
return (
Disabled
);
}
```
`disabled`는 조작과 값 변경을 막고 항목을 비활성 색으로 표시합니다. `readOnly`는 사용자 조작으로 값을 변경하지 못하게 하되 활성 색을 유지합니다. Lynx에는 웹의 포커스 유지 동작이 없습니다.
### Data Contract
- 각 컬럼의 `options`에는 하나 이상의 항목을 제공하세요.
- option의 `value`는 컬럼 안에서 고유해야 합니다.
- 제어 중인 `value`는 현재 `options`에 있어야 합니다.
- 의존 컬럼이 바뀌면 새 `options`와 유효한 `value`를 같은 렌더에서 전달하세요.
- 사용자가 조작 중이거나 정착을 기다리는 동안 외부 `value`가 바뀌면 스크롤 위치 동기화를 미루고 조작이 끝난 뒤 반영합니다.
- 로딩, 오류, 빈 상태는 Wheel Picker 바깥에서 처리하세요.
유효하지 않은 값이면 첫 항목을 표시하지만 전달받은 값을 보정하거나 보정 callback을 호출하지 않습니다. 날짜 계산, 컬럼 간 의존성과 확인 UI도 사용하는 쪽에서 구성합니다. 웹의 hidden input과 form 제출 기능은 제공하지 않습니다.
### Accessibility
`columns`의 각 항목에 `accessibility-label`을 지정하세요. 컬럼은 이름과 현재 선택값을 알리며, `disabled` 상태도 전달합니다. raw 접근성 속성과 `flatten`을 직접 지정하면 기본값보다 우선합니다.
## 웹 버전과의 차이
- `WheelPicker`를 접근성 요소로 만들면 iOS에서 자식 컬럼이 가려질 수 있습니다. 이름과 값은 `columns`의 각 항목에 지정하세요.
- 위치 계산과 애니메이션은 Main Thread에서 실행하고 값 callback은 Background Thread에서 실행합니다.
- 활성 컬럼의 세로 drag는 바깥 scroll view 대신 Wheel Picker가 처리합니다. `disabled` 또는 `readOnly`일 때는 바깥 scroll view가 처리합니다.
- Bottom Sheet 안에서 drag로 시트를 움직이려면 Wheel Picker 바깥에서 시작하세요.
## Lynx 미지원 기능
웹의 키보드 `ArrowUp`, `ArrowDown`, `Home`, `End` 조작과 `Tab` 컬럼 이동, 포커스 API는 지원하지 않습니다. 터치 드래그나 보이는 항목 탭으로 선택하세요. `readOnly`는 사용자 값 변경만 막으며 웹처럼 포커스를 유지하는 기능은 없습니다.
스크린 리더는 각 컬럼의 이름과 현재 값을 읽지만, 스와이프로 값을 올리고 내리는 조작(`accessibility-actions` 등)은 제공하지 않습니다.