Nghệ thuật dựng component trong thực tế-Phần 3
Tags: html, css, component, react
Published: 2022-05-13 09:03:02

Bài viết được dịch từ:
https://ishadeed.com/article/building-real-life-components/

### Nhiều hình đại diện trong trò chuyện nhóm

Trong trường hợp trò chuyện với nhiều người, khu vực hình đại diện sẽ có hai hình đại diện với mỗi hình đại diện được đặt lần lượt ở trên cùng bên phải và dưới cùng bên trái.

Để giữ cho một hình đại diện và nhiều hình đại diện được căn chỉnh, chúng ta cần đặt một kích thước cố định cho thẻ cha chứa nhiều hình đại diện.

```
.card__avatar {
  width: 56px;
  height: 56px;
}
```

![Image multiple-avatar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b5qzsg10ti1hydqnxd1n.png)

Biến thể này yêu cầu thay đổi HTML, xem đoạn code HTML dưới.

```
<div class="card__avatar card__avatar--multiple">
  <svg
    class="avatar avatar-1"
    role="none"
    style="height: 36px; width: 36px"
  ></svg>
  <svg
    class="avatar avatar-2"
    role="none"
    style="height: 36px; width: 36px"
  ></svg>
  <div class="badge"></div>
</div>
```

```
.card__avatar--multiple {
  position: relative;
  width: 56px;
  height: 56px;
}

.card__avatar--multiple .avatar {
  position: absolute;
}

.card__avatar--multiple .avatar-1 {
  right: 0;
  top: 0;
}

.card__avatar--multiple .avatar-2 {
  left: 0;
  bottom: 0;
}

.card__avatar--multiple .badge {
  right: 6px;
  bottom: 6px;
}
```

![Image multiple-avatar-2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00vk0sqdpj8jes0fn7on.png)

### Nội dung

Khu vực này là nơi người dùng có thể nhìn thấy tên của người mà họ đang trò chuyện và nội dung của tin nhắn hoặc hành động (không rõ lắm chắc là status).

![Image content-area](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cyln2i1qjsse7vzb3tsk.png)

Tôi có thể tưởng tượng phần code HTML này được chia thành hai phần, một phần cho nội dung văn bản (tên, tin nhắn hoặc hành động) và phần thứ hai cho chỉ báo ở phía bên phải (tin nhắn mới, đã xem, đã tắt tiếng, đã gửi).

![Image content-area-2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cvrqoznjasxl6q6nju66.png)

#### Phần đầu tiên

Hãy cùng khám phá code HTML cho khu vực nội dung.

```
<div class="card__content">
  <div class="card__content__start">
    <h3>Ahmad Shadeed</h3>
    <div class="row">
      <p>You: Thanks, sounds good. What about doing a webinar, too?</p>
      <span class="separator">.</span>
      <time>8hr</time>
    </div>
  </div>
  <div class="card__content__end">
    <!-- The indicator (new message, seen, muted, sent) -->
  </div>
</div>
```

```
.card__content {
  display: flex;
  flex: 1;
}

.card__content__start {
  display: flex;
  flex: 1;
}

.card__content__start .row {
  display: flex;
  align-items: center;
}

.card__content__end {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-left: 12px;
}

.separator {
  margin-left: 4px;
  margin-right: 4px;
}
```

Với phần trên, khu vực nội dung sẽ giống như bên dưới (Đây là ảnh chụp màn hình được chụp từ Firefox).

![Image content-area-3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kewuculhqpug7ef4h2sc.png)

Độ dài của tên hoặc tin nhắn có thể rất dài. Quan trọng là phải tính đến điều đó ngay từ đầu. Trước tiên, chúng ta hãy khám phá phương pháp "flow as you like".

![Image content-area-4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d9x3c0nqueeyyn9d732y.png)

Trong hình trên, nội dung thẻ thứ hai bao gồm nhiều dòng. Điều này có vẻ không tốt cho một component. Để tránh điều đó, đây là những thứ cần làm:

- Đặt `min-width: 0` trên các item con (dùng flex cho parent component). Vì sao? Tôi sẽ cho bạn biết sau đây.
- Cắt bớt văn bản bằng cách sử dụng các thuộc tính `overflow`, `white-space` và `text-overflow`.

Tôi thêm code CSS cho phần Tên và ở thẻ `p`:

```
.card__content__start h3,
.card__content__start p {
  overflow-x: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
```

Tuy nhiên, điều này không thể giải quyết vấn đề khi chúng ta đang sử dụng flexbox. Lưu ý kết quả sau khi áp dụng CSS ở trên:

![Image content-area-5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iini1lz771yj6z82lcax.png)

Lý do là các `flex items` sẽ không thu nhỏ dưới **kích thước nội dung tối thiểu của chúng**. Để giải quyết vấn đề này, chúng ta cần đặt `min-width: 0` trên các phần tử `.card__content` và `card__content__start`.

![Image content-area-6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n2ot76sytf2to6bgfcbl.png)

#### Phần thứ hai

Mỗi tin nhắn có một indicator (status) và chúng ta nên tính đến tất cả chúng. Đây là tất cả các indicator mà tôi biết. Có thể còn nhiều điều mà tôi không biết (Nếu có, vui lòng cho tôi biết).

![Image message-status](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/708fkjyldjy5usdl21f3.png)

Đối với phần này, chúng ta sẽ tập trung vào phần tử `.card__content__end` và nội dung bên trong của nó.

```
<div class="card__content">
  <div class="card__content__start">
    <!-- The name and message -->
  </div>
  <div class="card__content__end">
    <!-- The indicator (new message, seen, muted, sent) -->
  </div>
</div>
```

Bằng cách có một phần tử chung, bất kỳ component nào cũng có thể đặt trong nó. Phần tử `.card__content__end` không được có bất kỳ styles nào như color hoặc font, nó sẽ chỉ đóng vai trò là ngôi nhà cho một component cụ thể.

#### Tin nhắn mới
Trong khi kiểm tra cách Facebook thực hiện chỉ báo (indicator) tin nhắn mới, hóa ra đó là một button có nhãn “Mark as read”.

```
<div role="button" aria-label="Mark as read" tabindex="0"></div>
```

![Image new-message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p9t1a9p0eyxxjawac4f5.png)

Tôi không hiểu bối cảnh tại sao nhóm tại Facebook lại chọn một phần tử `div` thay vì một phần tử `button` thực tế. Với `button`, chúng ta không cần sử dụng `role`, `aria-label` và `tabindex`. Thay vào đó, tất cả những thứ đó đều có sẵn miễn phí.

#### Hình đại diện Seen

Tương tự như hình đại diện của người dùng, hình đại diện Seen không có gì khác biệt. Nó sử dụng phần tử `svg` cho hình đại diện có nhãn `aria-label` cho biết tên của người dùng.

![Image signle-seen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/46w4dl61nwqahnwcs8va.png)

```
<svg aria-label="Ahmad Shadeed" role="img">
  <!-- Mask and image -->
</svg>
```

#### Nhiều hình đại diện Seen

Thành thật mà nói, đây là một trong những yêu thích của tôi. Tôi thực sự thích cách team tại Facebook thực hiện nó.

![Image multiple-seen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00k7w6su0nh8nj4rhph4.png)

Bạn có nhận thấy đường viền giữa hai hình đại diện không? Ngay từ cái nhìn đầu tiên, bạn có thể nghĩ rằng đây là đường viền bởi CSS cho hình đại diện đầu tiên. Nếu bạn nghĩ như vậy, rất tiếc phải thông báo cho bạn biết rằng bạn đã sai (đúng như suy nghĩ ban đầu của tôi).

Điều này có được do áp dụng SVG mask.

```
<svg role="none">
  <mask id="circle">
    <circle cx="8" cy="8" fill="white" r="8"></circle>
    <circle cx="-4" cy="8" fill="black" r="10"></circle>
  </mask>

  <g mask="url(#circle)">
    <image></image>
    <circle class="border" cx="28" cy="28" r="28"></circle>
  </g>
</svg>
```
Đây là hình ảnh về cách hoạt động của nó.

![Image multiple-seen-2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lyzya73kdy8zfqsrrz71.png)

Thật đáng kinh ngạc. Phải không? Tôi thực sự thích sử dụng SVG cho trường hợp cụ thể như này.

### Hết phần 3