DEV Community

mikebui
mikebui

Posted on

Nghệ thuật dựng component trong thực tế-Phần 3

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;
}
Enter fullscreen mode Exit fullscreen mode

Image multiple-avatar

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>
Enter fullscreen mode Exit fullscreen mode
.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;
}
Enter fullscreen mode Exit fullscreen mode

Image multiple-avatar-2

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

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

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>
Enter fullscreen mode Exit fullscreen mode
.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;
}
Enter fullscreen mode Exit fullscreen mode

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

Độ 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

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-spacetext-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;
}
Enter fullscreen mode Exit fullscreen mode

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

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__contentcard__content__start.

Image content-area-6

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

Đố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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Image new-message

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-labeltabindex. 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

<svg aria-label="Ahmad Shadeed" role="img">
  <!-- Mask and image -->
</svg>
Enter fullscreen mode Exit fullscreen mode

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

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>
Enter fullscreen mode Exit fullscreen mode

Đây là hình ảnh về cách hoạt động của nó.

Image multiple-seen-2

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

Top comments (0)