RadioGroup 单选按钮组
RadioGroup 是一个用于呈现一组单选按钮的组件。用户可以从多个选项中选择一个选项。
代码演示
基本使用
基础使用 key: value:
import { Space, RadioGroup, RadioOption } from '@dance-ui/ui'
import { useState } from 'react'
import React from 'react'
const radioOpts: Array<RadioOption | RadioOption[] | null> = [
{ key: 'id', label: '我是id,value值为test1,有前后缀', prefix: 'prefix_', suffix: '_suffix', value: 'test1' },
{
key: 'input1',
isInput: true,
suffix: '_suffix',
inputClass: 'w-[10.625rem]',
placeholder: '请输入---自定义value值',
},
{
key: 'onClick',
label: '点击事件',
value: 'onClick1',
onClick: (e: any) => {
alert('Hello')
},
},
{
key: 'onClick',
label: '点击事件2',
value: 'onClick2',
onClick: (e: any) => {
alert('Hello World')
},
},
[
{
key: 'subGroup-1',
label: "I'm subGroup-1",
value: 'subGroup-1',
},
{
key: 'subGroup-2',
label: "I'm subGroup-2",
value: 'subGroup-2',
},
{
key: 'customValue',
isInput: true,
value: 'initValue',
placeholder: '千万不要输入"-"',
beforeOnChange: (value: string) => {
if (value.includes('-')) {
alert('不允许输入"-"')
return false
}
return true
},
},
],
]
export default () => {
const [value, setValue] = useState('')
const [key, setSelectedRadioKey] = useState('')
return (
<Space direction="vertical">
<Space direction="vertical">
<p>
基础使用 key: {key} value: {value}
</p>
<RadioGroup
defaultValue="test1"
options={radioOpts}
labelClass="min-w-[10.125rem]"
value={value}
onChange={(value: string, key?: string) => {
setSelectedRadioKey(key ?? '')
setValue(value)
}}
/>
</Space>
</Space>
)
}
API
RadioGroup
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
options | 单选按钮组的选项 | Array<RadioOption \| RadioOption[] \| null> | - |
name | 单选按钮的 name 属性 | string | - |
value | 当前选中的值 | OptionValueType | - |
defaultValue | 默认选中的值 | string | - |
onChange | 选项变化时的回调函数 | (value: string, key?: string) => void | - |
className | 组件的类名 | string | - |
dotClass | 单选按钮点的类名 | string | - |
dotContainerClass | 单选按钮点容器的类名 | string | - |
labelClass | 标签的类名 | string | - |
RadioOption
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
label | 选项的标签 | string \| ReactNode | - |
key | 选项的键 | string | - |
value | 选项的值 | OptionValueType | - |
suffix | 选项的后缀 | string | - |
prefix | 选项的前缀 | string | - |
onClick | 选项点击时的回调函数 | MouseEventHandler<HTMLSpanElement> | - |
beforeOnChange | 在改变之前的回调函数 | (value: string) => boolean | - |
isInput | 是否为输入值 | boolean | - |
defaultValue | 输入的默认值 | string | - |
inputClass | 输入的类名 | string | - |
placeholder | 输入的占位符 | string | - |
className | 选项的类名 | string | - |
labelClass | 选项标签的类名 | string | - |
labelStyle | 选项标签的样式 | CSSProperties | - |
dotClass | 选项点的类名 | string | - |
dotContainerClass | 选项点容器的类名 | string | - |
组件源码
组件源码
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
// TODO: Fix ts error
import { CSSProperties, Fragment, MouseEventHandler, ReactNode, Ref, forwardRef, useCallback, useState } from 'react'
import { twMerge } from 'tailwind-merge'
import { RadioItem } from './RadioItem'
export type OptionValueType = string
export type RadioOption = {
label?: string | ReactNode
key?: string
value?: OptionValueType
suffix?: string
prefix?: string
onClick?: MouseEventHandler<HTMLSpanElement>
beforeOnChange?: (value: string) => boolean
// input value
isInput?: boolean
defaultValue?: string
inputClass?: string
placeholder?: string
// classes
className?: string
labelClass?: string
labelStyle?: CSSProperties
dotClass?: string
dotContainerClass?: string
}
export type RadioGroupProps = {
options: Array<RadioOption | RadioOption[] | null>
name: string
onChange: (value: string, key?: string) => void
value?: OptionValueType
defaultValue?: string
className?: string
dotClass?: string
dotContainerClass?: string
labelClass?: string
}
const RadioGroup = forwardRef(
(
{
options,
name,
value: externalValue,
defaultValue,
onChange,
className,
dotClass,
dotContainerClass,
labelClass,
}: RadioGroupProps,
ref: Ref<HTMLDivElement>,
) => {
const [selectedValue, setSelectedValue] = useState<string | null>(defaultValue ?? null)
const [inputValues, setInputValues] = useState<Record<string, string>>({})
const isControlled = externalValue !== undefined
const currentValue = isControlled ? externalValue : selectedValue ?? ''
const handleRadioChange = useCallback(
(value?: string, key?: string) => {
if (!value) return
if (!isControlled) {
setSelectedValue(value)
}
onChange(value, key)
},
[isControlled, onChange],
)
const handleInputChange = (key: string, value: string) => {
setInputValues((prev) => ({
...prev,
[key]: value,
}))
}
const renderOptions = useCallback(
() =>
options.map((option, idx) => {
if (!option) return null
const isArray = Array.isArray(option)
if (isArray && (option as RadioOption[]).length === 0) return null
if (isArray) {
const opts = option as RadioOption[]
const realValues = opts.map((opt) => {
const { value = '', isInput, key, prefix = '', suffix = '' } = opt
const realValue = `${prefix}${isInput ? inputValues[key ?? 'default'] : value}${suffix}`
return realValue
})
return (
<Fragment key={idx}>
{opts.map((opt, idx) => {
const isSelected = currentValue ? realValues.includes(currentValue) : false
// const isSelected = realValues.includes(currentValue)
return (
<RadioItem
showDot={idx === 0}
key={opt?.key ?? opt?.value}
option={opt}
name={name}
currentValue={currentValue ?? ''}
onValueChange={handleRadioChange}
inputValues={inputValues}
onInputChange={handleInputChange}
dotContainerClass={dotContainerClass}
dotClass={dotClass}
labelClass={labelClass}
dotIsSelected={isSelected}
/>
)
})}
</Fragment>
)
}
const opt = option as RadioOption
return (
<RadioItem
key={opt?.key ?? opt?.value}
option={opt}
name={name}
currentValue={currentValue ?? ''}
onValueChange={handleRadioChange}
inputValues={inputValues}
onInputChange={handleInputChange}
dotContainerClass={dotContainerClass}
dotClass={dotClass}
labelClass={labelClass}
/>
)
}),
[currentValue, dotClass, dotContainerClass, handleRadioChange, inputValues, labelClass, name, options],
)
return (
<div className={twMerge('flex flex-wrap items-center gap-3', className)} ref={ref}>
{renderOptions()}
</div>
)
},
)
RadioGroup.defaultProps = {}
export default RadioGroup