一、组件库介绍
有兴趣的同学 可以先着手看源码,之前写的一系列开源组件库项目(有帮助的同学也帮忙点个star👍)
二、Radio、RadioButton、RadioGroup组件流程介绍
在一个ui组件库当中其实,表单组件可以称之为重中之重,因为现在基本上pc端大部分开发还是面向的b端,所以基本上表单组件,在每个小模块当中是基本上都可以使用到的,那么关于 radio 组件我相信大家也都很熟悉,因为不免得每个表单中基本都带有着单选框组件,像如下,其实本身html标签的input的type给个radio属性也就是最初始的单选组件 那么我们现在肯定也要捋清楚一个逻辑就是我们怎么去实现这个radio单选框组件呢?其实有两种思路:
1、我们采用一个div去做底层,然后给这个div去设置样式,实现对应的点击事件,触发回显值,对应点击设置高亮等。但是这样也有弊端就是什么,我们外层绑定一个原始的form是无法提交表单值的、再有无法抓取value值、无法使用fieldset进行统一禁用这些,表单的原始功能就无法模拟了。
2、我们采用 input type=“radio” 去做一个底层,当然因为本身也是携带着radio样式的,这样我们只需要改样式即可,然后同样的本身也带有着点击事件默认行为等,我们直接使用默认的一系列方法即可
所以综上所述,我们的 radio 组件底层既采用 input type=“radio” 去实现功能,再有像RadioButton的话,我们也可以做一个简单的组件封装,底层我们还是一样的,就是设置边框以及选中高亮这些实现对应的功能,再有至于RadioGroup的话,总的来说它是用来包裹 Radio 、RadioButton组件的,我们可以直接在这个组件上面设置默认的选中值,以及回传值,以此来实现功能,所以接下来我们总结功能:
Radio: type: string // type类型 判断颜色 checked: boolean // 是否选中 disabled: boolean // 禁用项 groupValue: any // groupValue 值是否与value值相等 逻辑上来说可传可不传 当时只是为了做判断用的 因为 value 也同样可以给与初始值 value: any // value初始值 label: string // 初始 lable 文本 name?: string // 原生 name 属性
RadioButton: type: string // type类型 判断颜色 checked: boolean // 选中状态 disabled: boolean // 禁用状态 groupValue: any // groupValue 值是否与value值相等 逻辑上来说可传可不传 当时只是为了做判断用的 因为 value 也同样可以给与初始值 label:string, // 初始 lable 文本 value: any // value初始值 name?: string // 原生 name 属性 size: string // 按钮大小 buttonStyle: string // style样式
RadioGroup: name?: string // 统一原生name值 buttonStyle: string // 如果是button类型 统一的buttonStyle disabled: boolean // 统一禁用 options: any // js编程式组件 optionType: string // js编程式类型 size: string // 大小 value: any // value值
在这里也是讲解一下 RadioGroup 的options,js编程式的组件,在这里其实也是参考了antd传入options,然后实现组件功能,随机我们也在我们的组件库当中去实现此功能。
三、代码详解
3.1、Radio.tsx
import React from 'react'
import './index.scss'
const classnames = require('classnames')
interface Iprops {
type: string
checked: boolean
disabled: boolean
groupValue: any
value: any
label: string
name?: string
}
function Radio(props: any) {
const {
type = 'default',
disabled = false,
groupValue,
checked = false,
value,
label,
name
}: Iprops = props
function handleChange(evt: any) {
props.onChange && props.onChange(evt.target.value)
}
return (
<div
className={[
'tb-radio',
classnames({
'is-disabled': disabled
})
].join(' ')}
>
<label>
<input
type='radio'
checked={groupValue ? (groupValue == value ? true : false) : checked}
name={name}
disabled={disabled}
onChange={handleChange}
value={value}
className={[
`radio-type_${type}`,
classnames({
'is-disabled': disabled
})
].join(' ')}
/>
<span>{props.children ? props.children : label ? label : value}</span>
</label>
</div>
)
}
export default Radio
3.2、Radio / index.scss
@import '../../style/variables.scss';
$type:('success', 'primary', 'danger', 'warning', 'info', 'default');
@function typeFunction($t) {
@if $t==success {
@return $color-success
}
@else if $t==primary {
@return $color-primary
}
@else if $t==info {
@return $color-info
}
@else if $t==danger {
@return $color-danger
}
@else if $t==warning {
@return $color-warning
}
@return $color-primary
}
@function typeDisabledFunction($t) {
@if $t==success {
@return $color-success-light2
}
@else if $t==primary {
@return $color-primary-light2
}
@else if $t==info {
@return $color-info-light2
}
@else if $t==danger {
@return $color-danger-light2
}
@else if $t==warning {
@return $color-warning-light2
}
@return $color-primary-light2
}
.tb-radio {
display: inline-block;
label {
display: flex;
align-items: center;
}
}
@each $t in $type {
.radio-type_#{$t} {
width: 20px;
height: 20px;
appearance: none;
position: relative;
outline: none;
vertical-align: bottom;
cursor: pointer;
}
.radio-type_#{$t}:before {
content: "";
width: 75%;
height: 75%;
border: 1px solid typeFunction($t);
display: inline-block;
border-radius: 50%;
vertical-align: middle;
cursor: pointer;
}
.radio-type_#{$t}.is-disabled:before {
border-color: typeDisabledFunction($t);
cursor: not-allowed;
}
.radio-type_#{$t}:checked:before {
content: "";
width: 75%;
height: 75%;
border: 1px solid typeFunction($t);
display: inline-block;
border-radius: 50%;
vertical-align: middle;
cursor: pointer;
}
.radio-type_#{$t}.is-disabled:checked:before {
border-color: typeDisabledFunction($t);
cursor: not-allowed;
}
.radio-type_#{$t}:checked:after {
content: "";
width: 45%;
height: 45%;
text-align: center;
background: typeFunction($t);
border-radius: 50%;
display: block;
position: absolute;
top: 20%;
left: 20%;
cursor: pointer;
}
.radio-type_#{$t}.is-disabled:checked:after {
background: typeDisabledFunction($t);
cursor: not-allowed;
}
.radio-type_#{$t}+span {
cursor: pointer;
}
.radio-type_#{$t}:checked+span {
color: typeFunction($t);
cursor: pointer;
}
.radio-type_#{$t}.is-disabled+span {
color: $color-text-disabled;
cursor: not-allowed;
}
.radio-type_#{$t}.is-disabled:checked+span {
color: typeDisabledFunction($t);
cursor: not-allowed;
}
}
3.3、RadioButton.tsx
import React from 'react'
import './index.scss'
const classnames = require('classnames')
interface Iprops {
type: string
checked: boolean
disabled: boolean
groupValue: any
label:string,
value: any
name?: string
size: string
buttonStyle: string
}
function RadioButton(props: any) {
const {
type = 'default',
disabled = false,
groupValue,
checked,
value,
name,
label,
size = 'default',
buttonStyle = 'outline'
}: Iprops = props
function handleChange(evt: any) {
props.onChange && props.onChange(evt)
}
return (
<div
className={[
'tb-radio-button',
classnames({
'is-disabled': disabled
})
].join(' ')}
>
<label>
<input
type='radio'
checked={groupValue ? (groupValue == value ? true : false) : checked}
name={name}
disabled={disabled}
onChange={handleChange}
value={value}
className={[
`radio-button-type_${type}`,
classnames({
'is-disabled': disabled
})
].join(' ')}
/>
<span
className={[
`tb-radio-button--${size}`,
`tb-radio-button--${type}_${buttonStyle}`
].join(' ')}
>
{props.children ? props.children : label ? label : value}
</span>
</label>
</div>
)
}
export default RadioButton
3.4、RadioButton / index.scss
@import '../../style/variables.scss';
$type:('success', 'primary', 'danger', 'warning', 'info', 'default');
@function typeFunction($t) {
@if $t==success {
@return $color-success
}
@else if $t==primary {
@return $color-primary
}
@else if $t==info {
@return $color-info
}
@else if $t==danger {
@return $color-danger
}
@else if $t==warning {
@return $color-warning
}
@return $color-primary
}
@function typeDisabledFunction($t) {
@if $t==success {
@return $color-success-light2
}
@else if $t==primary {
@return $color-primary-light2
}
@else if $t==info {
@return $color-info-light2
}
@else if $t==danger {
@return $color-danger-light2
}
@else if $t==warning {
@return $color-warning-light2
}
@return $color-primary-light2
}
.tb-radio-button {
display: inline-block;
}
@each $t in $type {
.radio-button-type_#{$t} {
display: none;
}
.radio-button-type_#{$t}+span {
position: relative;
display: inline-block;
vertical-align: middle;
height: $default-height;
line-height: $default-height - 2;
white-space: nowrap;
cursor: pointer;
background: $color-white;
border: $border-base;
color: $color-text-default;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
transition: $animation-duration-base;
user-select: none;
padding: 0 15px;
font-size: $base-font-size;
border-radius: $border-base-radius;
}
.radio-button-type_#{$t}+.tb-radio-button--mini {
height: $mini-height;
line-height: $mini-height - 2;
padding: 0 7px;
}
.radio-button-type_#{$t}+.tb-radio-button--small {
height: $small-height;
line-height: $small-height - 2;
padding: 0 7px;
}
.radio-button-type_#{$t}+.tb-radio-button--large {
height: $large-height;
line-height: $large-height - 2;
padding: 0 14px;
font-size: $header-font-size;
}
.radio-button-type_#{$t}:checked+span {
border-color: typeFunction($t);
color: typeFunction($t);
cursor: pointer;
}
.radio-button-type_#{$t}.is-disabled+span {
color: $color-text-disabled;
cursor: not-allowed;
}
.radio-button-type_#{$t}.is-disabled:checked+span {
color: typeDisabledFunction($t);
cursor: not-allowed;
}
.radio-button-type_#{$t}:checked+.tb-radio-button--#{$t}_solid {
background-color: typeFunction($t);
color: $color-white;
cursor: pointer;
}
.radio-button-type_#{$t}.is-disabled+.tb-radio-button--#{$t}_solid {
color: $color-text-disabled;
background-color: $btn-disable-color;
cursor: not-allowed;
}
.radio-button-type_#{$t}.is-disabled:checked+.tb-radio-button--#{$t}_solid {
color: $color-white;
background-color: typeDisabledFunction($t);
border-color: typeDisabledFunction($t);
cursor: not-allowed;
}
}
3.5、RadioGroup.tsx
import React from 'react'
import Radio from '../Radio/index'
import RadioButton from '../RadioButton/index'
interface Iprops {
name?: string
buttonStyle: string
disabled: boolean
options: any
optionType: string
size: string
value: any
}
function RadioGroup(props: any) {
const {
name,
value,
options = [],
buttonStyle,
size,
optionType = 'default'
}: Iprops = props
function handleChange(val: any) {
props.onChange && props.onChange(val)
}
const radioDom = options.map((item: any) => {
if (optionType === 'button') {
return (
<RadioButton
key={item.value}
value={item.value||''}
groupValue={value}
name={name}
size={size}
buttonStyle={buttonStyle}
disabled={item.disabled ? item.disabled : null}
onChange={handleChange}
>
{item.label}
</RadioButton>
)
} else {
return (
<Radio
key={item.value}
value={item.value}
groupValue={value}
name={name}
disabled={item.disabled ? item.disabled : null}
onChange={handleChange}
>
{item.label}
</Radio>
)
}
})
const radioItems = React.Children.map(props.children, (item) => {
return React.cloneElement(item, {
item,
componentName: 'radioGroup',
groupValue: value,
name,
onChange: props.onChange,
buttonStyle,
size
})
})
return (
<div className='tb-radio-group'>{radioItems ? radioItems : radioDom}</div>
)
}
export default RadioGroup
3.6、variables.scss
// $font-path : './fonts'
$color-white : #FFFFFF;
$color-white-light : rgba(255, 255, 255, 0.65);
$color-white-light2 : rgba(255, 255, 255, 0.35);
$color-primary : #1089ff;
$color-primary-light1 : mix(#FFFFFF, $color-primary, 20%);
$color-primary-light2 : mix(#FFFFFF, $color-primary, 40%);
$color-primary-light3 : mix(#FFFFFF, $color-primary, 60%);
$color-primary-light4 : mix(#FFFFFF, $color-primary, 80%);
$color-primary-light5 : mix(#FFFFFF, $color-primary, 90%);
$color-primary-light6 : mix(#FFFFFF, $color-primary, 95%);
$color-primary-active : mix(#000000, $color-primary, 10%);
$color-success :#52c41a;
$color-success-light1 : mix(#FFFFFF, $color-success, 20%);
$color-success-light2 : mix(#FFFFFF, $color-success, 40%);
$color-success-light3 : mix(#FFFFFF, $color-success, 60%);
$color-success-light4 : mix(#FFFFFF, $color-success, 80%);
$color-success-light5 : mix(#FFFFFF, $color-success, 90%);
$color-success-light6 : mix(#FFFFFF, $color-success, 95%);
$color-success-active : mix(#000000, $color-success, 10%);
$color-info : #35495E;
$color-info-light1 : mix(#FFFFFF, $color-info, 20%);
$color-info-light2 : mix(#FFFFFF, $color-info, 40%);
$color-info-light3 : mix(#FFFFFF, $color-info, 60%);
$color-info-light4 : mix(#FFFFFF, $color-info, 80%);
$color-info-light5 : mix(#FFFFFF, $color-info, 90%);
$color-info-light6 : mix(#FFFFFF, $color-info, 95%);
$color-info-active : mix(#000000, $color-info, 10%);
$color-warning : #fea638;
$color-warning-light1 : mix(#FFFFFF, $color-warning, 20%);
$color-warning-light2 : mix(#FFFFFF, $color-warning, 40%);
$color-warning-light3 : mix(#FFFFFF, $color-warning, 60%);
$color-warning-light4 : mix(#FFFFFF, $color-warning, 80%);
$color-warning-light5 : mix(#FFFFFF, $color-warning, 90%);
$color-warning-light6 : mix(#FFFFFF, $color-warning, 95%);
$color-warning-active : mix(#000000, $color-warning, 10%);
$color-danger : #ff4d4f;
$color-danger-light1 : mix(#FFFFFF, $color-danger, 20%);
$color-danger-light2 : mix(#FFFFFF, $color-danger, 40%);
$color-danger-light3 : mix(#FFFFFF, $color-danger, 60%);
$color-danger-light4 : mix(#FFFFFF, $color-danger, 80%);
$color-danger-light5 : mix(#FFFFFF, $color-danger, 90%);
$color-danger-light6 : mix(#FFFFFF, $color-danger, 95%);
$color-danger-active : mix(#000000, $color-danger, 10%);
$color-text-primary : rgba(0, 0, 0, .85);
$color-text-default : rgba(0, 0, 0, .65);
$color-text-secondary : rgba(0, 0, 0, .45);
$color-text-disabled : rgba(0, 0, 0, .25);
$color-text-placeholder : #C0C4CC;
$btn-disable-color : #c5c8ce;
$color-disabled-bg:rgb(245, 245, 245);
$color-disabled-border:rgb(217, 217, 217);
$color-bg-fa : #fafafa;
$color-select-hover : #f5f5f5;
$color-effect-shadow : alpha($color-primary);
$color-input-shadow : alpha($color-primary);
$color-input-error-shadow : alpha($color-danger);
$border-color-base : #d9d9d9;
$border-color-light : #f0f0f0;
$border-base : 1px solid $border-color-base;
$border-width:1px;
$border-base-light : 1px solid $border-color-light;
$border-table : 1px solid #e8eaec;
$border-table-color : #e8eaec;
$border-base-radius : 2px;
$base-font-size : 14px;
$header-font-size : 16px;
$large-height : 36px;
$default-height : 32px;
$small-height : 28px;
$mini-height : 24px;
$base-line-height : 1.5715;
$animation-duration-slow : 0.3s;
$animation-duration-base : 0.2s;
$animation-duration-fast : 0.1s;
// placeholder
$placeholder-color : #c0c4cc;
四、效果呈现
至此效果实现
react 官网持续开发中,也请大家持续关注博主
结语
? 每天创作一点点 ? 开心快乐一整天 ? 点赞关注加收藏 ? 美好一天又一天
铁铁们 感谢支持 我需要你们的三连 👍👍👍
|