RadioGroup 사용자가 여러 옵션 중 하나만 선택할 수 있게 하는 라디오 그룹 컴포넌트입니다.
@seed-design/lynx-react@0.1.0, @seed-design/lynx-css@0.1.0
import "./styles" ;
import { root } from "@lynx-js/react" ;
import { RadioGroup, VStack, useSeedClassName } from "@seed-design/lynx-react" ;
function Root () {
const seedClassName = useSeedClassName ({ colorMode: "system" });
return (
< page className = {seedClassName}>
< VStack className = "radio-group-preview" >
< RadioGroup.Root defaultValue = "apple" size = "large" tone = "neutral" >
< RadioGroup.Item value = "apple" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >사과</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "banana" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >바나나</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "orange" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >오렌지</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
</ VStack >
</ page >
);
}
root. render (< Root />);
npx @seed-design/cli add ui:radio-group pnpm dlx @seed-design/cli add ui:radio-group yarn dlx @seed-design/cli add ui:radio-group bun x @seed-design/cli add ui:radio-group
의존성 설치
npm install @seed-design/lynx-react yarn add @seed-design/lynx-react pnpm add @seed-design/lynx-react bun add @seed-design/lynx-react 아래 코드를 복사 후 붙여넣고 사용하세요 /**
* @file ui:radio-group
* @requires @seed-design/lynx-react@>=0.1.0 <1.0.0
* @requires @seed-design/lynx-css@>=0.1.0 <1.0.0
**/
import * as React from "@lynx-js/react" ;
import { RadioGroup as SeedRadioGroup } from "@seed-design/lynx-react" ;
export interface RadioGroupProps extends SeedRadioGroup . RootProps {}
/**
* @see https://seed-design.io/lynx/components/radio-group
*/
export const RadioGroup = React. forwardRef < unknown , RadioGroupProps >(
({ children , ... otherProps }, ref ) => {
return (
< SeedRadioGroup.Root ref = {ref} { ... otherProps}>
{children}
</ SeedRadioGroup.Root >
);
},
);
RadioGroup.displayName = "RadioGroup" ;
export interface RadioGroupItemProps extends SeedRadioGroup . ItemProps {
label ?: React . ReactNode ;
}
/**
* @see https://seed-design.io/lynx/components/radio-group
*/
export const RadioGroupItem = React. forwardRef < unknown , RadioGroupItemProps >(
({ label , children , ... otherProps }, ref ) => {
return (
< SeedRadioGroup.Item ref = {ref} { ... otherProps}>
< SeedRadioGroup.ItemControl >
< SeedRadioGroup.ItemIndicator />
</ SeedRadioGroup.ItemControl >
{label != null ? < SeedRadioGroup.ItemLabel >{label}</ SeedRadioGroup.ItemLabel > : null }
{children}
</ SeedRadioGroup.Item >
);
},
);
RadioGroupItem.displayName = "RadioGroupItem" ;
export interface RadiomarkProps extends Omit < SeedRadioGroup . ItemControlProps , "children" > {}
/**
* @see https://seed-design.io/lynx/components/radio-group
*/
export const Radiomark = React. forwardRef < unknown , RadiomarkProps >(( props , ref ) => {
return (
< SeedRadioGroup.ItemControl ref = {ref} { ... props}>
< SeedRadioGroup.ItemIndicator />
</ SeedRadioGroup.ItemControl >
);
});
Radiomark.displayName = "Radiomark" ;
/**
* 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.
*/
value?string | undefined
defaultValue?string | undefined
onValueChange?(( value : string ) => void ) | undefined
style?CSSProperties | undefined
children?React.ReactNode
className?string | undefined
label?React.ReactNode
valuestring
disabled?boolean | undefined
style?CSSProperties | undefined
children?React.ReactNode
className?string | undefined
className?string | undefined
style?CSSProperties | undefined
import "./styles" ;
import { root } from "@lynx-js/react" ;
import { RadioGroup, VStack, useSeedClassName } from "@seed-design/lynx-react" ;
function Root () {
const seedClassName = useSeedClassName ({ colorMode: "system" });
return (
< page className = {seedClassName}>
< VStack className = "radio-group-preview" gap = "x5" >
< RadioGroup.Root defaultValue = "apple" size = "medium" tone = "neutral" >
< RadioGroup.Item value = "apple" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >사과</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "banana" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >바나나</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
< RadioGroup.Root defaultValue = "red" size = "large" tone = "neutral" >
< RadioGroup.Item value = "red" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >빨간색</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "blue" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >파란색</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
</ VStack >
</ page >
);
}
root. render (< Root />);
import "./styles" ;
import { root } from "@lynx-js/react" ;
import { RadioGroup, VStack, useSeedClassName } from "@seed-design/lynx-react" ;
function Root () {
const seedClassName = useSeedClassName ({ colorMode: "system" });
return (
< page className = {seedClassName}>
< VStack className = "radio-group-preview" >
< RadioGroup.Root defaultValue = "apple" size = "large" tone = "brand" >
< RadioGroup.Item value = "apple" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >사과</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "banana" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >바나나</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "orange" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >오렌지</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
</ VStack >
</ page >
);
}
root. render (< Root />);
import "./styles" ;
import { root } from "@lynx-js/react" ;
import { RadioGroup, VStack, useSeedClassName } from "@seed-design/lynx-react" ;
function Root () {
const seedClassName = useSeedClassName ({ colorMode: "system" });
return (
< page className = {seedClassName}>
< VStack className = "radio-group-preview" >
< RadioGroup.Root defaultValue = "apple" size = "large" tone = "neutral" >
< RadioGroup.Item value = "apple" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >사과</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "banana" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >바나나</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "orange" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >오렌지</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
</ VStack >
</ page >
);
}
root. render (< Root />);
import "./styles" ;
import { root } from "@lynx-js/react" ;
import { RadioGroup, VStack, useSeedClassName } from "@seed-design/lynx-react" ;
function Root () {
const seedClassName = useSeedClassName ({ colorMode: "system" });
return (
< page className = {seedClassName}>
< VStack className = "radio-group-preview" gap = "x5" >
< RadioGroup.Root defaultValue = "regular-1" size = "large" tone = "neutral" weight = "regular" >
< RadioGroup.Item value = "regular-1" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >Regular</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "regular-2" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >Regular</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
< RadioGroup.Root defaultValue = "bold-1" size = "large" tone = "neutral" weight = "bold" >
< RadioGroup.Item value = "bold-1" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >Bold</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "bold-2" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >Bold</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
</ VStack >
</ page >
);
}
root. render (< Root />);
import "./styles" ;
import { root } from "@lynx-js/react" ;
import { RadioGroup, VStack, useSeedClassName } from "@seed-design/lynx-react" ;
function Root () {
const seedClassName = useSeedClassName ({ colorMode: "system" });
return (
< page className = {seedClassName}>
< VStack className = "radio-group-preview" >
< RadioGroup.Root defaultValue = "medium" size = "medium" tone = "neutral" >
< RadioGroup.Item value = "medium" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >
긴 라벨도 여러 줄로 자연스럽게 표시되어 선택 항목의 내용을 모두 확인할 수 있습니다.
</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "large" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >
화면 너비가 좁아져도 라디오 마크와 라벨의 정렬을 유지합니다.
</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
</ VStack >
</ page >
);
}
root. render (< Root />);
RadioGroup.Root의 disabled는 그룹 전체를 비활성화합니다. 개별 RadioGroup.Item도 따로 비활성화할 수 있습니다.
import "./styles" ;
import { root } from "@lynx-js/react" ;
import { RadioGroup, VStack, useSeedClassName } from "@seed-design/lynx-react" ;
function Root () {
const seedClassName = useSeedClassName ({ colorMode: "system" });
return (
< page className = {seedClassName}>
< VStack className = "radio-group-preview" gap = "x8" >
< RadioGroup.Root defaultValue = "option1" size = "large" tone = "neutral" >
< RadioGroup.Item value = "option1" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >활성 옵션</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "option2" disabled >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >비활성 옵션</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
< RadioGroup.Root defaultValue = "all-disabled" size = "large" tone = "neutral" disabled >
< RadioGroup.Item value = "all-disabled" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >그룹 전체 비활성</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "unavailable" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >선택할 수 없음</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
</ VStack >
</ page >
);
}
root. render (< Root />);
RadioGroup.Root의 onValueChange로 선택 값 변경을 감지할 수 있습니다. Controlled 모드에서는 value와 onValueChange를 함께 사용합니다.
import "./styles" ;
import { root, useState } from "@lynx-js/react" ;
import { RadioGroup, VStack, useSeedClassName } from "@seed-design/lynx-react" ;
function Root () {
const seedClassName = useSeedClassName ({ colorMode: "system" });
const [ count , setCount ] = useState ( 0 );
const [ lastValue , setLastValue ] = useState < string | null >( null );
return (
< page className = {seedClassName}>
< VStack className = "radio-group-preview" gap = "x4" >
< RadioGroup.Root
defaultValue = "apple"
size = "large"
tone = "neutral"
onValueChange = {( value ) => {
setCount (( previous ) => previous + 1 );
setLastValue (value);
}}
>
< RadioGroup.Item value = "apple" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >사과</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "banana" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >바나나</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
< RadioGroup.Item value = "orange" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >오렌지</ RadioGroup.ItemLabel >
</ RadioGroup.Item >
</ RadioGroup.Root >
< text className = "radio-group-preview__status" >
onValueChange 호출: {count}회, 마지막 값: { JSON . stringify (lastValue)}
</ text >
</ VStack >
</ page >
);
}
root. render (< Root />);
RadioGroup.ItemControl과 RadioGroup.ItemIndicator를 직접 조합하면 선택 상태를 공유하면서 항목의 배치를 바꿀 수 있습니다.
import "./styles" ;
import { root } from "@lynx-js/react" ;
import { HStack, RadioGroup, VStack, useSeedClassName } from "@seed-design/lynx-react" ;
function Root () {
const seedClassName = useSeedClassName ({ colorMode: "system" });
return (
< page className = {seedClassName}>
< VStack className = "radio-group-preview" >
< RadioGroup.Root defaultValue = "medium" size = "large" tone = "neutral" >
< HStack gap = "x6" >
< RadioGroup.Item value = "regular" >
< VStack gap = "x2" align = "center" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >Regular</ RadioGroup.ItemLabel >
</ VStack >
</ RadioGroup.Item >
< RadioGroup.Item value = "medium" >
< VStack gap = "x2" align = "center" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >Medium</ RadioGroup.ItemLabel >
</ VStack >
</ RadioGroup.Item >
< RadioGroup.Item value = "bold" >
< VStack gap = "x2" align = "center" >
< RadioGroup.ItemControl >
< RadioGroup.ItemIndicator />
</ RadioGroup.ItemControl >
< RadioGroup.ItemLabel >Bold</ RadioGroup.ItemLabel >
</ VStack >
</ RadioGroup.Item >
</ HStack >
</ RadioGroup.Root >
</ VStack >
</ page >
);
}
root. render (< Root />);
Lynx RadioGroup은 React RadioGroup과 다음과 같은 차이가 있습니다.
Field wrapper 없음 : React snippet의 label, description, errorMessage, indicator, showRequiredIndicator 같은 field 관련 prop은 Lynx snippet에서 제공하지 않습니다.
아이콘 렌더링 방식 : 기본 RadioGroup.ItemIndicator는 <view> dot으로 렌더됩니다. custom icon을 전달할 때는 @karrotmarket/lynx-monochrome-icon의 monochrome icon 컴포넌트를 사용해야 하며 raw <svg> 주입은 Lynx 범위 밖입니다.
이벤트 핸들링 : onChange 대신 onValueChange(value: string)만 노출합니다. tap 핸들러는 item 내부에서 소유합니다.
Pressed 상태 : 웹의 data-active 대신 내부 press state를 radiomark recipe의 pressed boolean variant로 전달합니다.
렌더링 요소 : HTML <label> / <input type="radio"> 대신 네이티브 <view> / <text> 요소를 렌더링합니다.
Strict containment : RadioGroupItem, Radiomark, RadioGroup.ItemControl, RadioGroup.ItemIndicator, RadioGroup.ItemLabel은 반드시 RadioGroup.Root와 RadioGroup.Item의 적절한 context 안에서 사용해야 합니다. Radiomark는 standalone primitive가 아니라 item context 안에서 ItemControl과 ItemIndicator를 묶는 snippet helper입니다.
현재 Lynx 플랫폼 제약으로 다음 기능이 지원되지 않습니다.
기능 웹 대응 설명 RadioGroupItemHiddenInput<input type="radio">Lynx에 HTML form 제출 모델 없음 name / required / invalidform field props Lynx에 native form 제출 모델 없음 inputProps내부 input prop 전달 Lynx에는 전달할 HTML input이 없음 label / description / errorMessage on RadioGroupfield wrapper props Lynx에 대응 field wrapper snippet을 제공하지 않음 focus / focusVisible키보드 포커스 Lynx에 키보드 포커스 개념 없음 onChange (raw DOM event)React.ChangeEvent의미 없음. onValueChange로 대체