Wheel Picker 항목을 세로로 스크롤하여 하나 이상의 값을 선택하는 컴포넌트입니다.
"use client" ;
import { Box, Text, VStack } from "@seed-design/react" ;
import * as React from "react" ;
import { WheelPicker } from "seed-design/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" ,
"aria-label" : "동" ,
options: buildingOptions,
value: building,
onValueChange : ( nextBuilding : string ) =>
setAddress (( current ) => ({ ... current, building: nextBuilding })),
},
{
id: "unit" ,
"aria-label" : "호수" ,
options: unitOptions,
value: unit,
onValueChange : ( nextUnit : string ) =>
setAddress (( current ) => ({ ... current, unit: nextUnit })),
},
];
return (
< VStack width = "360px" maxWidth = "100%" gap = "x3" align = "center" >
< Box width = "full" >
< WheelPicker aria-label = "동호수 선택" columns = {columns} />
</ Box >
< Text aria-live = "polite" >
{building}동 {unit}호
</ Text >
</ VStack >
);
}
npx @seed-design/cli@latest add ui:wheel-picker pnpm dlx @seed-design/cli@latest add ui:wheel-picker yarn dlx @seed-design/cli@latest add ui:wheel-picker bun x @seed-design/cli@latest add ui:wheel-picker
의존성 설치
npm install @seed-design/react yarn add @seed-design/react pnpm add @seed-design/react bun add @seed-design/react 아래 코드를 복사 후 붙여넣고 사용하세요 /**
* @file ui:wheel-picker
* @requires @seed-design/react@^2.4.0
* @requires @seed-design/css@^2.6.0
**/
"use client" ;
import { WheelPicker as SeedWheelPicker } from "@seed-design/react" ;
import * as React from "react" ;
export interface WheelPickerOption {
/** 항목을 식별하고 선택 상태로 주고받는 고유 값입니다. */
value : string ;
/** 항목에 표시할 내용입니다. */
label : React . ReactNode ;
/** React 요소 label을 스크린 리더에서 읽을 문자열입니다. */
ariaLabel ?: string ;
}
export interface WheelPickerColumn
extends Omit < SeedWheelPicker . ColumnProps , "options" > {
/** 컬럼을 식별하는 고유 값입니다. */
id : string ;
/** 컬럼에 표시할 선택 항목입니다. 하나 이상의 항목을 제공해야 합니다. */
options : readonly WheelPickerOption [];
}
export interface WheelPickerProps extends Omit < SeedWheelPicker . RootProps , "children" > {
/** Wheel Picker를 구성하는 컬럼 목록입니다. */
columns : readonly WheelPickerColumn [];
}
function validateOptionLabels ( columns : readonly WheelPickerColumn []) {
if (process.env. NODE_ENV === "production" ) return ;
for ( const column of columns) {
for ( const option of column.options) {
if (React. isValidElement (option.label) && ! option.ariaLabel) {
console. warn (
`WheelPicker: "${ column . id }" 컬럼에서 React 요소 label을 사용하는 option에는 ariaLabel이 필요합니다.` ,
);
}
}
}
}
/**
* 여러 Wheel Picker 컬럼을 데이터 배열로 구성합니다.
*
* @see https://seed-design.io/react/components/wheel-picker
*/
export const WheelPicker = React. forwardRef < HTMLDivElement , WheelPickerProps >(
({ columns , ... rootProps }, ref ) => {
validateOptionLabels (columns);
return (
< SeedWheelPicker.Root ref = {ref} { ... rootProps}>
{columns. map (({ id , options , ... columnProps }) => (
< SeedWheelPicker.Column key = {id} options = {options} { ... columnProps} />
))}
</ SeedWheelPicker.Root >
);
},
);
WheelPicker.displayName = "WheelPicker" ;
/**
* This file is a snippet from SEED Design, helping you get started quickly with @seed-design/* packages.
* You can extend this snippet however you want.
*/
getAriaValueText?(( value : string ) => string) | undefined
"use client" ;
import { Box, Text, VStack, WheelPicker } from "@seed-design/react" ;
import * as React from "react" ;
const options = [ "작게" , "보통" , "크게" ]. map (( label ) => ({ value: label, label }));
export default function WheelPickerPackageApi () {
const [ value , setValue ] = React. useState ( "보통" );
return (
< VStack width = "320px" maxWidth = "100%" gap = "x3" align = "center" >
< Box width = "full" >
< WheelPicker.Root aria-label = "글자 크기 선택" size = "small" >
< WheelPicker.Column
aria-label = "글자 크기"
options = {options}
value = {value}
onValueChange = {setValue}
renderLabel = {( option ) => (
< WheelPicker.ItemLabel >
< Text fontSize = "t5" fontWeight = "medium" >
{option.label}
</ Text >
</ WheelPicker.ItemLabel >
)}
/>
</ WheelPicker.Root >
</ Box >
< Text aria-live = "polite" >선택값: {value}</ Text >
</ VStack >
);
}
@seed-design/react의 compound API는 컬럼을 직접 조합할 때 사용합니다. Root와 각 Column의 className으로 전체 또는 컬럼 단위 스타일을 조정할 수 있습니다. renderLabel은 기본 ItemLabel을 반환한 요소로 대체합니다. 기본 여백과 정렬이 필요하면 예시처럼 WheelPicker.ItemLabel을 다시 조합할 수 있습니다. 내부 항목과 선택 표시의 slot 스타일 API는 제공하지 않습니다.
onIndexChange는 사용자가 스크롤하거나 항목을 탭해 중앙 항목이 바뀔 때마다 호출됩니다. 정착 전에도 통과한 각 항목의 인덱스와 값을 순서대로 전달하므로 항목별 햅틱에 사용할 수 있습니다. 최종 선택값은 스크롤이 정착한 뒤 onValueChange로 처리하세요.
"use client" ;
import { Box, HStack, Text, VStack } from "@seed-design/react" ;
import * as React from "react" ;
import { WheelPicker } from "seed-design/ui/wheel-picker" ;
function ColorDot ({ color } : { color : string }) {
return (
< Box
as = "span"
aria-hidden = "true"
width = "x3"
height = "x3"
borderRadius = "full"
style = {{ backgroundColor: color }}
/>
);
}
const colorOptions = [
{ value: "carrot" , name: "당근색" , color: "#ff6f0f" },
{ value: "blue" , name: "파란색" , color: "#4285f4" },
{ value: "green" , name: "초록색" , color: "#22a06b" },
]. map (({ value , name , color }) => ({
value,
ariaLabel: name,
label : (
< HStack as = "span" gap = "x2" align = "center" >
< ColorDot color = {color} />
< span >{name}</ span >
</ HStack >
),
}));
export default function WheelPickerReactElementLabel () {
const [ value , setValue ] = React. useState ( "carrot" );
return (
< VStack width = "320px" maxWidth = "100%" gap = "x3" align = "center" >
< Box width = "full" >
< WheelPicker
aria-label = "색상 선택"
columns = {[
{
id: "color" ,
"aria-label" : "색상" ,
options: colorOptions,
value,
onValueChange: setValue,
},
]}
/>
</ Box >
< Text aria-live = "polite" >선택값: {value}</ Text >
</ VStack >
);
}
문자열과 숫자 label은 그대로 접근성 이름으로 사용됩니다. React 요소를 label로 사용한다면 각 option에 ariaLabel을 제공하세요. 접근성 텍스트를 별도로 계산해야 할 때는 getAriaValueText를 사용할 수 있습니다. 내부 요소는 currentColor를 상속해야 하며 버튼, 링크, 입력 요소와 여러 줄 콘텐츠는 넣지 않습니다.
import { Box, Text, VStack, WheelPicker } from "@seed-design/react" ;
const options = [ "낮음" , "보통" , "높음" ]. map (( label ) => ({ value: label, label }));
export default function WheelPickerStates () {
return (
< VStack width = "160px" gap = "x2" align = "center" >
< Text >Disabled</ Text >
< Box width = "full" >
< WheelPicker.Root aria-label = "비활성 중요도" disabled >
< WheelPicker.Column aria-label = "중요도" options = {options} defaultValue = "보통" />
</ WheelPicker.Root >
</ Box >
</ VStack >
);
}
disabled는 컬럼의 포커스와 조작을 모두 막습니다.
각 컬럼의 options에는 하나 이상의 항목을 제공하세요.
option의 value는 컬럼 안에서 고유해야 합니다.
제어 중인 value는 현재 options에 있어야 합니다.
의존 컬럼이 바뀌면 새 options와 유효한 value를 같은 렌더에서 전달하세요.
사용자가 조작 중이거나 정착을 기다리는 동안 외부 value가 바뀌면 해당 스크롤 위치 동기화는 건너뜁니다.
로딩, 오류, 빈 상태는 Wheel Picker 바깥에서 처리하세요.
Wheel Picker는 유효하지 않은 값을 첫 항목으로 자동 보정하거나 보정 callback을 호출하지 않습니다. 날짜 계산, 컬럼 간 의존성, hidden input과 확인 UI도 사용하는 쪽에서 구성합니다.
전체 Wheel Picker는 group, 각 컬럼은 spinbutton으로 제공됩니다. Root와 각 Column에 aria-label 또는 aria-labelledby를 지정하세요.
키보드에서는 ArrowUp과 ArrowDown으로 이전·다음 항목을 선택합니다. Home과 End는 처음·마지막 항목으로 이동합니다. Tab으로 컬럼 사이를 이동할 수 있습니다.