【Day04】数据输入元件 - Checkbox

元件介绍

Checkbox 是一个多选框元件。通常使用情境是在一个群组的选项当中进行多项选择时使用。

参考设计 & 属性分析

checked 属性

checked 是一个 boolean 值,透过这个属性来决定该选项是否被选取。

状态属性

disabled 属性表示这个 checkbox 是否被禁用,当前状态如果是 checked 就不能被改变成 unchecked,反之亦然,也就是禁止改变他的当前状态。当然这边的改变指的是透过 onChange 来改变,如果直接改变 checked props 的话,还是可以改变当前的状态。

外观属性

MUI 以及 Antd 的一样是用 size 来控制不同情境需要的大小,其中 MUI 的传入值可以是 medium, samll,而 Antd 的传入值可以是 large, middle, small

颜色的部分也跟 Radio Button 雷同,MUI 提供 color 这个 props 让我们传入,传入值可以是 primary, secondary, default,而 Antd 从文件来看没有特别提供明显的介面让我们改变 Checkbox 的颜色。

label 属性
Antd 的 label 属性是放在 Checkbox 的 children component 里面,而 MUI 的 label 及 labelPlacement 一样是由另外的元件 FormControlLabel 来独立控制。

我们在点击 Checkbox 或是 Radio Button 的时候,都希望 click 的监听元件不只是那个 box而已,因为这样能够被点击的作用区域太小了,很容易让使用者点不到,造成不好的使用体验,因此如果我们点击到他的 label 也希望能够勾选,甚至如果我们 checkbox 是 List item 的一部份的话,通常也是会希望整个 List item 被点击的时候可以改变 Checkbox 状态。

小结
到目前为止我们看过几个很类似的元件,例如 Toggle Switch, Radio Button, Checkbox,甚至是我们一开始讲的 Button ,有很多属性其实都蛮雷同的而且也是很多元件都有的,例如 checked, disabled, loading, label, size, color......等等。

我自己在设计元件的时候,会希望也会建议让这些 props 的介面命名、型别、传入值尽量保持一致,例如下面举例一些不是很建议的案例:

  • Checkbox 的 checked 叫做 checked,而 Switch 的 checked 却叫做 open,然後可能 Radio Button 又叫做 selected。
  • label 也是一样,不建议有些元件叫做 label ,有些元件叫做 text ,可能又有些元件叫做 title。
  • 可能有些人在某些元件让 size 可以传入 medium, small,然後另外的元件可传入值叫做 big, middle, little.....。另外甚至有些 size 是这些固定的 string 传入,但有些元件却是让 size 传入一个 number。

在同一套系统当中我个人是会希望尽量统一介面,让使用者好使用,不用特别再去看一下文件或是深入追一下 code 才知道使用方法,避免可能今天写一写,下个月又使用到同样的元件又忘记了。

这样的状况可能会发生在同一个人设计不同元件上面(可能上次设计类似的元件是一段时间以前了),也有可能发生在不同人设计类似的元件上面,这些东西一点一滴累积起来,整个专案可能会变得越来越混乱、越来越难维护、越来越难以令人理解,因此建议设计新元件的时候,也需要去参考其他元件的设计方式,比较能够设计出统一一致的介面。

当然这样的建议可能也只是理想和原则,实务上当然也可能遇到需要取舍的时候,但也只少需要是团队有共识的设计,并且把这些不直观的设计记录下来,避免之後的人踩雷,或是不知道为什麽当初这样设计而不小心改坏掉。

介面设计

属性 说明 类型 默认值
isChecked 开启或关闭 boolean false
isDisabled 禁用状态 boolean false
themeColor 设置颜色 primary, secondary, 色票 pirmary
children 内容, label element, string
onClick 点击事件 function(event: object) => void
onChange 状态改变的 callback function function(event: object) => void

元件实作

其实从介面上以及外观来看,Radio 跟 Checkbox 相似度几乎是 87%,唯一差别就是 Icon 不一样,若因为 Icon 不一样就把程序码一模一样复制一次,那这样在程序码里面就需要维护两套一模一样的逻辑。因此我希望把前一篇提到的 Radio 重构一下,把 Radio 以及 Checkbox 会用到的部分抽出来成另一个元件,这边暂时想不到更好的名字,姑且用 Option 这个名字。

所以 Radio 和 Checkbox 我们希望把它变成下面这样,透过 props 把 checkedIcon & unCheckedIcon 传进去,其他的逻辑以及样式的部分就写在共同的 <Option /> 元件里面:

Radio:

import RadioButtonUncheckedIcon from '@material-ui/icons/RadioButtonUnchecked';
import RadioButtonCheckedIcon from '@material-ui/icons/RadioButtonChecked';

const Radio = (props) => (
  <Option
    checkedIcon={<RadioButtonCheckedIcon />}
    unCheckedIcon={<RadioButtonUncheckedIcon />}
    {...props}
  />
);

Checkbox:

import CheckBoxIcon from '@material-ui/icons/CheckBox';
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';

const Checkbox = (props) => (
  <Option
    checkedIcon={<CheckBoxIcon />}
    unCheckedIcon={<CheckBoxOutlineBlankIcon />}
    {...props}
  />
);

这样 Option 其实就跟我们上一篇的 Radio 差不多,唯一的差别就是这里我希望传进来的 checkedIcon & unCheckedIcon 跟之前一样拥有一个 className 方便我客制化他的样式,因此用 React.cloneElement 这个方法把 props 塞入其中(实际上是复制一个一模一样的 element 并给予他我们传入的 props)。

<StyledOption
  onClick={isDisabled ? null : onClick}
  $isDisabled={isDisabled}
  $btnColor={btnColor}
  {...props}
>
  {
    isChecked
    ? (
      React.cloneElement(checkedIcon, {
      	className: clsx(checkedIcon.props.className, 'option__checked-icon'),
      })
    )
    : (
      React.cloneElement(unCheckedIcon, {
        className: clsx(unCheckedIcon.props.className, 'option__unchecked-icon'),
      })
    )
}
{!!children && <span>{children}</span>}
</StyledOption>

Checkbox 元件原始码:
Source code

Option 元件原始码:
Source code

Storybook:
Checkbox


<<:  DAY5: Node 的内部机制(一)

>>:  Python - Python3 虚拟环境参考笔记

系统开发生命周期(SDLC)

SDLC定义了工程系统时的阶段和过程。由於系统的多样性,它通常不提供特定的设计原则。 系统开发生命周...

[30天 Vue学好学满 DAY30] 总结 & 完赛感言

最後一篇文,挤出了一些觉得在开发上容易踩的雷以及要注意的事情 要使用、渲染的变数除了传递进入元件的,...

[Day30] 总结

终於到最後一天啦! 很感谢 iT 铁人赛,在这一个月内,不只把我本来知道的东西透过文字或影像记录下...

Day 26:旅行推销员问题(TSP)

之前在贪婪演算法的文章中有提到,现实生活中并不是所有问题都能用演算法快狠准地解决,有些困难的问题只有...

Day 28:IRQ (Part 2) - 中断突进!简单的 IRQ 程序

接下来的实验中,会写一个把 GPIO 当作是中断的来源的程序。这个 GPIO 由 Arduino 发...