iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0

元件介紹

Badge 可以讓我們在其 children element 的右上角(預設位置)顯示一個小徽章,通常用來表示需要處理的訊息數量,透過醒目的視覺形式來吸引用戶處理。

參考設計 & 屬性分析

Badge 位置
一般的 Badge 位置都是預設出現在右上角,但可能有些情境會需要調整位置,MUI 這邊提供了 anchorOrigin 的 props,這個 anchorOrigin 比較特別,是一個物件,裡面有兩個欄位,分別是 vertical 和 horizontal,顧名思義,就是提供我們可以決定要放在右上、右下、左上、左下這四個位置。

<Badge
  anchorOrigin={{
    vertical: 'bottom', // top, bottom
    horizontal: 'right', // right, left
  }}
>
  {children}
</Badge>

當然 MUI 除了這四個位置可以透過 props 調整之外,也能夠藉由 overrides documentation 提供我們的方法,透過 class 去覆寫 css 屬性(ex: top, right)來微調偏移,所以可以客製化的幅度還蠻彈性的。

Antd 提供的 props 叫做 offset ,傳入值為一陣列,格式为 [left, top],用來表示其水平及垂直的偏移量。

<Badge count={5} offset={[10, 10]}>
  {children}
</Badge>

展示的內容

比較常見的展示內容一般來說都是數字,所以 Antd 的 props 叫做 count,一般我們看到 count 的直覺會覺得應該要填入的是數字,但很特別的是,count 支援的類型為 ReactNode ,因此,如下範例,他也可以是一個 icon。

// Antd Badge
<Badge count={0} showZero>
  {children}
</Badge>

<Badge count={<ClockCircleOutlined style={{ color: '#f5222d' }} />}>
  {children}
</Badge>

MUI 所提供的 props 為 badgeContent ,其型別為 node,因此也支援我們傳入數字以外的資料。

最大展示值
Antd: overflowCount
MUI: max

variant

有時候我們希望 Badge 只是提醒我們有東西更新就好,不需要呈現數字,因此除了我們常見的 default 樣式之外,也提供了 dot 樣式。

在 MUI 上一樣是提供我們 variant 這個 props ,可以填入的值是 dot, standard,預設是 standard。

Antd 上則是提供我們一個布林值 dot,若為 true ,則只呈現一個小紅點,沒有內容;若為 false,則為我們常見的 Badge 含數字內容的樣式。

顏色

這個元件一樣在 MUI 及 Antd 都提供我們顏色的客製化彈性,因為雖然大家印象中 Badge 都是紅色的,但是難免在其他情境我們需要別的顏色。 MUI 的 color 可傳入一樣為預設的保留字 default, error, primary, secondary,而 Antd 除了預設的保留字之外,也支援我們色票的傳入。

是否呈現 0

當 Badge 的數字為 0 時,是否要展示 Badge 呢? MUI 及 Antd 都提供給我們這樣的選擇,很有共識的,兩邊的 props 命名很一致,都是 showZero 的 boolean。

介面設計

屬性 說明 類型 默認值
placement 徽章位置 top-left, top-right, bottom-left, bottom-right top-right
badgeContent 展示內容 ReactNode
max 最大顯示值 number 99
variant 變化模式 standard, dot standard
themeColor 顏色 primary, secondary, 色票 #FE6B8B
showZero 是否呈現 0 boolean false
children 內容 ReactNode

元件實作

最基本的 Badge 使用方式如下:

<Badge badgeContent={7}>
  <MailIcon />
</Badge>

因此根據上述的使用介面,把 Badge 的結構設計如下:

<BadgeWrapper>
  {children}
  <StandardBadge
    className={className}
    $color={color}
    $placement={placement}
  >
    {badgeContent}
  </StandardBadge>
</BadgeWrapper>

由於 Badge 會跟 children 有重疊的區塊,因此 Badge 勢必要設為 position: absolute;,並把 <BadgeWrapper /> 設為 position: relative; 如此 Badge 才能相對於他做定位。

徽章位置 Placement

在 Badge 當中我們需要一個 props 來決定 Badge 的位置是在 右上左上右下左下

我想要用跟在 FormControl 一樣的手法,透過 Object 的 key-value 對應,來取得相對應的樣式:

const placementStyleMap = {
  'top-left': topLeftStyle,
  'top-right': topRightStyle,
  'bottom-left': bottomLeftStyle,
  'bottom-right': bottomRightStyle,
};

因為前面我們已經將 Badge 設為 absolute 了,因此在定位方面,我們就能夠使用 top, left, right, transform 來調配出 右上左上右下左下 各種位置:

const topLeftStyle = css`
  top: 0px;
  left: 0px;
  transform: translate(-50%, -50%);
`;

const topRightStyle = css`
  top: 0px;
  right: 0px;
  transform: translate(50%, -50%);
`;

const bottomLeftStyle = css`
  bottom: 0px;
  left: 0px;
  transform: translate(-50%, 50%);
`;

const bottomRightStyle = css`
  bottom: 0px;
  right: 0px;
  transform: translate(50%, 50%);
`;

變化模式 variant

Badge 有兩種變化模式,一個是 standard,另一個是 dot

其中 dot 的樣式就比較單純,因為不需要考慮到內容的變化,他就只有一個而已。

const DotBadge = styled.div`
  position: absolute;
  width: 6px;
  height: 6px;
  border-radius: 100%;
  background-color: ${(props) => props.$color};
  ${(props) => placementStyleMap[props.$placement] || topRightStyle}
`;
<BadgeWrapper>
  {children}
  {variant === 'dot' && (
    <DotBadge
      className={className}
      $color={color}
      $placement={placement}
    />
  )}
</BadgeWrapper>

standard 當中,特別需要留意的是會使用到 box-sizing: border-box; 這個屬性,在 金魚都能懂的CSS必學屬性 當中有很詳細的講解,border-box 主要是讓我們能將寬高設定作用在邊框外緣的範圍內,所以當我們在設定 width 以及 height 的時候,指的是 border-box 範圍內的寬跟高。

最大值 max

Badge 內容當然不能讓人家隨便輸入,甚至也需要限制他的最大數字,否則就會像下圖這樣悲劇:

因此至少要有一個簡單的判斷,來限制我們最大值的輸入

const content = badgeContent > max ? `${max}+` : badgeContent;

是否呈現 0

最後一個小問題就是,到底 0 要不要呈現?可能有些情境要,有些情境不用,因此我們需要一個 boolean 來做到這件事,所以跟上述 max 的邏輯合併,我們來做一個 function 來產生我們 Badge 的內容:

const makeBadgeContent = ({ showZero, max, badgeContent }) => {
  if (showZero && badgeContent === 0) {
    return '0';
  }
  if (!showZero && badgeContent === 0) {
    return null;
  }
  return badgeContent > max ? `${max}+` : badgeContent;
};


Badge 元件原始碼:
Source code

Storybook:
Badge


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

尚未有邦友留言

立即登入留言