iT邦幫忙

2021 iThome 鐵人賽

DAY 10
0

元件介紹

Chip 元件用於標記事物的屬性、標籤或用於分類、篩選。

在 MUI 當中,這樣的元件叫做 Chip,而在 Antd 中,這樣的元件叫做 Tag,但其實是指一樣的元件。

我自己以前會喜歡把這樣的元件叫做 Tag,因為最符合直覺,像我們常見的 Hashtag 大概也是長這樣。但是開發經驗累積一段時間之後,會發現怎麼到處都有東西叫做 Tag?真的很容易撞名,而且這樣的元件他也不一定會用在 Tag 上面,可能有些資料叫做 categories 或是 filters ,但他也需要用同樣的元件來展示,所以如果叫做 Tag 我自己是覺得容易撞名,有時候也容易混淆,跟別人溝通的時候可能也會不小心產生誤會,特別是你的專案裡面同時有資料叫做 tags 和 categories 要在同個地方展示,而且你同時又有一個元件叫做 <Tag />,在跟別人溝通的時候真的非常怕他會聽錯或是自己講錯,因此我覺得 MUI 把它叫做 Chip 真的是很不錯,我自己也蠻喜歡這個名字,因此這邊文章統一起見,我先暫時叫這個元件為 Chip。

參考設計 & 屬性分析

Chip 元件也是在 MUI 及 Antd 還蠻一致的元件,可以變化的樣式和功能都很相似。

外觀樣式
variant 跟 button 很相似,有 default 跟 outlined 兩種選項,default 是整個元件填滿的樣式。

顏色
顏色的部分 MUI 只提供我們使用 default, primary, secondary,而 Antd 的 color 支援填入色票之外,也支援一些保留字,例如 success, proccessing, error, warning, default。

icon
icon 可以讓我們在 Chip 開頭的地方放上一些圖像,例如頭像或是其他 icon 方便我們識別。

closeIcon
在結尾的地方放上的圖像,MUI 叫做 deleteIcon ,Andtd 叫做 closeIcon,功能上看起來是大同小異。並且這個 icon 是支援點擊事件的,透果 onDelete/onClose 可以 handle 對 icon 的點擊動作。

我覺得 MUI 及 Antd 這邊有個小細節還不錯,就是 Antd 的 closeIcon 對應的事件是 onClose,而 MUI deleteIcon 對應到的事件是 onDelete,不會說 close 和 delete 混著用,如果我們沒有注意到的話,我們平常專案內的元件很有可能就會設計出命名不一致的元件。

元件內容
在 MUI 裏面叫做 label ,是一個可傳入 ReactNode 的 props;Antd 則是讓內容可以透過 children element 傳進去。

// MUI
 <Chip
  ...otherprops
  label="標籤內容"
/>

// Antd
<Tag
  ...otherprops
>
  標籤內容
</Tag>

之前我們有遇到類似的狀況是在 button 的地方,由於 button 是 html 原生的元件,有大家既定認知的使用方式,所以我會比較希望是用 children element 的方式來實作;但 Chip 這邊好像兩種方式都有人喜歡,畢竟 MUI Chip 也可以讓我們在 label 傳入 element。

狀態屬性
由於 Chip 是可點擊的元件,有 onDelete/onClose 事件,因此若若有需要它不可被點擊的狀態,這邊也提供 disabled 的 boolean 屬性讓我們使用。

介面設計

屬性 說明 類型 默認值
variant 變化模式 contained, outlined contained
themeColor 主題顏色 primary, secondary, 色票 primary
isDisabled 是否能進行交互 boolean false
label 內容 ReactNode, String
icon 圖示 ReactNode
deleteIcon 刪除圖示 ReactNode
onDelete 刪除事件 func

元件實作

一個 Chip 的 children 有可能會出現 icon, label, deleteIcon,如下圖:

因此我們在規劃 html 結構的時候也以這樣為主,其實跟我們在設計 Button 的時候蠻像的,其中 ChipWrapper 來決定其 children 的佈局,而 icon & deleteIcon 會再根據各別的條件來決定是否顯示:

<ChipWrapper>
  <Icon />
  <Label>
  <DeleteIcon>
</ChipWrapper>

變化模式 variant

我們會根據 variant 來決定他是 contained 或是 outlined 的樣式,跟先前的 Button 一樣,我們用一個 variantMap 的 object 來取得對應的樣式,若沒有對應到,則預設為 contained:

const containedStyle = css`
  background: ${(props) => props.$color};
  color: #FFF;
`;

const outlinedStyle = css`
  background: #FFF;
  color: ${(props) => props.$color};
`;

const variantMap = {
  contained: containedStyle,
  outlined: outlinedStyle,
};

客製化顏色

顏色的部分我們一樣有 primary, secondray 以及隨意傳入的色票號碼,這邊的作法跟 Button 是一樣的,附上連結,就不再詳細說明。

我們已經有一個 makeColor({ themeColor }) 的 function,把 themeColor 傳入,就能夠將 primary, secondary 轉換成對應的色票號碼

https://github.com/TimingJL/13th-ithelp_custom-react-ui-components/blob/main/src/hooks/useColor.jsx

Icon & DeleteIcon

Label 左側的 icon 是隨著有沒有傳入 icon 這個 props 來決定是否顯示,其中我們透過 React.cloneElement 加上一個 className 為 chip__start-icon 來調整他的樣式:

<StyledChip
  className={className}
  $variant={variant}
  $color={color}
>
  {icon && React.cloneElement(icon, {
    className: clsx(icon.props.className, 'chip__start-icon'),
  })}
  <Label>{label}</Label>
</StyledChip>

Label 右側的 icon 多數為用來觸發 onDelete function,因命名上也使用 deleteIcon 這個名稱。

deleteIcon 這個 props 是當我們想要客製化 endIcon 時能夠使用,否則,若只有給定 onDelete function 而沒有給定 deleteIcon 時,則顯示預設的 deleteIcon:

const endIcon = deleteIcon || <CancelIcon />;

<StyledChip
  className={className}
  $variant={variant}
  $color={color}
>
  {icon && React.cloneElement(icon, {
    className: clsx(icon.props.className, 'chip__start-icon'),
  })}
  <Label>{label}</Label>
  {(deleteIcon || onDelete) && React.cloneElement(endIcon, {
    className: clsx(endIcon.props.className, 'chip__end-icon'),
    onClick: onDelete,
  })}
</StyledChip>

到目前為止,我們就已經完成一個相當接近 MUI 的 Chip 了,實作邏輯上其實並沒有特別複雜,跟前面提到幾個 數據輸入元件 做法都蠻類似的,但主要是樣式上會隨著不同專案的需要有些調整,若把客製化樣式的部分做得好用,我覺得就會是很不錯的元件。


Chip 元件原始碼:
Source code

Storybook:
Chip


上一篇
【Day09】數據輸入元件 - Upload
下一篇
【Day11】數據展示元件 - Badge
系列文
30 天擁有一套自己手刻的 React UI 元件庫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言