React造轮子(reacthook实现一套自己的组件库)轮子公开课——第六课【Checkbox、CheckboxGroup】组件

一、组件库介绍

有兴趣的同学 可以先着手看源码,之前写的一系列开源组件库项目(有帮助的同学也帮忙点个star👍)

名称官网github
tinkerbell-ui(vue2.0)http://tinkerbell.tophttps://github.com/hanbingxu82/tinkerbell-ui
tinkerbell-ui(vue3.0)https://github.com/hanbingxu82/tinkerbell-ui-next
tinkerbell-ui(react-hook)https://github.com/hanbingxu82/tinkerbell-ui-react

二、Checkbox、CheckboxGroup组件流程介绍

关于 CheckboxGroup 也就是用来包裹 Checkbox 的一个通用组件,用于处理 Checkbox 组件变化监听回调的这样一个组件,其实主要的也是如何使用父组件去监听子组件的变化这样一个点,所以我们要在 CheckboxGroup 组件当中去捕获 Checkbox 的一个value变化,所以我们给 Checkbox 一个 onChange,然后 CheckboxGroup 去抓取,因为 Checkbox、CheckboxGroup 是自定义组件所以我们使用React.Children.map、React.cloneElement 去对子组件内容进行捕获。

CheckBoxGroup
name?: string // 原生 name 属性
disabled: boolean // 是否统一禁用
options: any // js编程式组件列表
value: any // 绑定 value 值

CheckBox
type: string // 样式类型
checked: boolean // 是否选中
disabled: boolean // 是否禁用
checkGroupValue: any // 父 group 的 value 值
value: string | number // 单独 value 值
name?: string // 原生 name 属性
label: string // label 名称
indeterminate?: any // 是否为统一全选控制按钮组件

三、代码详解

3.1、CheckBoxGroup.tsx


import React from 'react'
import CheckBox from '../CheckBox/index'
interface Iprops {
  name?: string // 原生 name 属性
  disabled: boolean // 是否统一禁用
  options: any // js编程式组件列表
  value: any // 绑定 value 值
}
function CheckBoxGroup(props: any) {
  const { name, value = [], options = [] }: Iprops = props
  function handleChange(evt: any) {
    //  有就删除 没有就新增
    value.indexOf(evt.target.value) > -1
      ? value.splice(value.indexOf(evt.target.value), 1)
      : value.push(evt.target.value)
    //  判断check选中状态
    console.log(value)
    props.onChange && props.onChange(value, evt)
  }
  //  options 循环遍历
  const checkboxDom = options.map((item: any) => {
    return (
      <CheckBox
        key={item.value}
        value={item.value}
        checkGroupValue={value}
        name={name}
        disabled={item.disabled ? item.disabled : null}
        onChange={handleChange}
      >
        {item.label}
      </CheckBox>
    )
  })
  //  遍历dom子节点方式
  const checkboxItems = React.Children.map(props.children, (item) => {
    return React.cloneElement(item, {
      item,
      componentName: 'checkboxGroup',
      checkGroupValue: value,
      name,
      onChange: handleChange
    })
  })

  return (
    <div className='tb-checkbox-group'>
      {checkboxItems ? checkboxItems : checkboxDom}
    </div>
  )
}
export default CheckBoxGroup

3.2、Checkbox.tsx


import React, { useEffect, useState } from 'react'
import './index.scss'

const classnames = require('classnames')
interface Iprops {
  type: string // 样式类型
  checked: boolean // 是否选中
  disabled: boolean // 是否禁用
  checkGroupValue: any // 父 group 的 value 值
  value: string | number // 单独 value 值
  name?: string // 原生 name 属性
  label: string // label 名称
  indeterminate?: any // 是否为统一全选控制按钮组件
}
function CheckBox(props: any) {
  const {
    type = 'default',
    disabled = false,
    checked = false,
    value,
    name,
    label,
    checkGroupValue = [],
    indeterminate
  }: Iprops = props
  const [checkBoxChecked, setCheckBoxChecked] = useState(checked || false)
  const [isIndeterminate, setIsIndeterminate] = useState(false)
  useEffect(() => {
    //  监听 初始化判断是否为 多选组选项行为
    if (!!checkGroupValue.length && checkGroupValue.includes(value)) {
      setCheckBoxChecked(true)
    } else if (
      (indeterminate === 'undefined' && !checkGroupValue.length) ||
      (indeterminate === undefined && !checkGroupValue.length) ||
      (indeterminate === null && !checkGroupValue.length)
    ) {
      setCheckBoxChecked(false)
    }
  }, [props.checkGroupValue]) // eslint-disable-line
  useEffect(() => {
    setCheckBoxChecked(checked)
  }, [props.checked]) // eslint-disable-line

  useEffect(() => {
    console.log(checkBoxChecked, 3333)
  }, [checkBoxChecked]) // eslint-disable-line
  useEffect(() => {
    setIsIndeterminate(!!indeterminate ? indeterminate : false)
  }, [indeterminate]) // eslint-disable-line
  function handleChange(evt: any) {
    setCheckBoxChecked(evt.target.checked)
    props.onChange && props.onChange(evt)
  }

  return (
    <div
      className={[
        'tb-checkbox',
        classnames({
          'is-disabled': disabled
        })
      ].join(' ')}
    >
      <label>
        <input
          type='checkbox'
          checked={checkBoxChecked}
          name={name}
          disabled={disabled}
          onChange={handleChange}
          value={value || label}
          className={[
            `checkbox-type_${type}`,
            classnames({
              'is-disabled': disabled,
              // 判断是否需要 全选,如果全选加标记
              'is-indeterminate': isIndeterminate
            })
          ].join(' ')}
        />
        <span>{props.children ? props.children : label ? label : value}</span>
      </label>
    </div>
  )
}
export default CheckBox

3.3、Checkbox / 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-checkbox {
  display: inline-block;

  label {
    display: flex;
    align-items: center;
    cursor: pointer;
  }
}

@each $t in $type {
  .checkbox-type_#{$t} {
    width: 20px;
    height: 20px;
    display: inline-block;
    text-align: center;
    vertical-align: baseline;
    // line-height: 1.54rem;
    position: relative;
    outline: none;
    -webkit-appearance: none;
    border: 1px solid #fff;
    -webkit-tab-highlight-color: rgba(0, 0, 0, 0);
    color: #fff;
    background: #fff;
    margin-right: 3px;
  }

  .checkbox-type_#{$t}:checked+span {
    color: typeFunction($t);
    cursor: pointer;
  }

  .checkbox-type_#{$t}::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    background: #fff;
    width: 75%;
    height: 75%;
    border: 1px solid $color-text-placeholder;
    color: #fff;
    cursor: pointer;
  }

  .checkbox-type_#{$t}:checked::before {
    content: "\2713";
    background-color: typeFunction($t);
    border: 1px solid typeFunction($t);
    position: absolute;
    top: 0;
    left: 0;
    width: 75%;
    color: #fff;
    cursor: pointer;
  }

  //   .checkbox-type_#{$t}.is-indeterminate::before {
  //     background-color: typeFunction($t);
  //     // border: 1px solid typeFunction($t);
  //     width: 75%;
  //     padding: 10px;
  //     box-sizing: border-box;
  //     position: absolute;
  //     top: 0;
  //     left: 0;
  //     color: #fff;
  //     cursor: pointer;
  //   }

  .checkbox-type_#{$t}.is-indeterminate:after {
    content: "";
    width: 45%;
    height: 45%;
    text-align: center;
    background-color: typeFunction($t);
    display: block;
    position: absolute;
    top: 20%;
    left: 20%;
    cursor: pointer;
  }



  .checkbox-type_#{$t}.is-disabled:checked::before {
    content: "\2713";
    background-color: $color-disabled-bg;
    border: 1px solid $color-disabled-border;
    position: absolute;
    top: 0;
    left: 0;
    width: 75%;
    color: $color-text-primary;
    cursor: pointer;
  }

  .checkbox-type_#{$t}.is-disabled:before {
    background-color: $color-disabled-bg;
    border: 1px solid $color-disabled-border;
    cursor: not-allowed;
  }

  .checkbox-type_#{$t}.is-disabled+span {
    color: $color-text-disabled;
    cursor: not-allowed;
  }

}

3.4、variables.scss

// $font-path : './fonts'

/* Color
-------------------------- */
$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%);

/* text-color
-------------------------- */
$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);

/* bg-color
-------------------------- */
$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
-------------------------- */
$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;

/* radius font-size
-------------------------- */
$border-base-radius : 2px;
$base-font-size : 14px;
$header-font-size : 16px;

/* height
-------------------------- */
$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 官网持续开发中,也请大家持续关注博主

结语

✨ 每天创作一点点
✨ 开心快乐一整天
✨ 点赞关注加收藏
✨ 美好一天又一天

铁铁们 感谢支持 我需要你们的三连 👍👍👍
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

归来巨星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值