0
0
Lập trình
Admin Team
Admin Teamtechmely

Thiết kế Thanh Hành Động Tập Trung trong React

Đăng vào 1 tuần trước

• 5 phút đọc

Giới thiệu

Trong phần trước, chúng ta đã khám phá cách mà các micro-interaction nâng cao trải nghiệm người dùng của Thanh Hành Động Tập Trung (Bulk Actions Bar). Trong bài viết này, chúng ta sẽ đi sâu vào những thách thức kỹ thuật trong việc tạo ra những tương tác này, đồng thời đảm bảo tính linh hoạt, hiệu suất và khả năng tiếp cận.

Phát hiện Tràn: Tính Toán Số Lượng Mục Hiển Thị

Một trong những trở ngại đầu tiên là xác định số lượng nút có thể hiển thị theo chiều ngang dựa trên chiều rộng của container. CSS một mình không đủ để giải quyết vấn đề này.

Tôi đã xây dựng một custom hook useVisibleChildrenCount sử dụng ResizeObserver để tính toán động số lượng mục hiển thị:

javascript Copy
export function useVisibleChildrenCount({ containerEl, isEnabled, gap = 0 }) {
  const [visibleCount, setVisibleCount] = useState(0);

  const measureVisibleItems = useCallback(() => {
    if (!containerEl) return;

    const childrenEls = Array.from(containerEl.children).filter(
      (el) => el instanceof HTMLDivElement
    );

    let totalWidth = 0;
    let fitCount = 0;

    for (let i = 0; i < childrenEls.length; i++) {
      const childWidth = childrenEls[i].offsetWidth;
      const spaceRequired = totalWidth === 0 ? childWidth : childWidth + gap;

      if (totalWidth + spaceRequired <= containerEl.offsetWidth) {
        totalWidth += spaceRequired;
        fitCount += 1;
      } else {
        break;
      }
    }

    setVisibleCount(fitCount);
  }, [containerEl, gap]);

  useEffect(() => {
    if (!isEnabled || !containerEl) return;

    const observer = new ResizeObserver(() => {
      window.requestAnimationFrame(measureVisibleItems);
    });

    observer.observe(containerEl);
    measureVisibleItems();

    return () => observer.disconnect();
  }, [containerEl, isEnabled, measureVisibleItems]);

  return isEnabled ? visibleCount : containerEl?.children.length || 0;
}

Custom hook này cho phép component thích ứng theo thời gian thực khi kích thước của container thay đổi.

Calendly sử dụng react-aria-components cho các menu có khả năng tiếp cận. Việc xử lý các submenu lồng nhau có hành vi khác nhau trên desktop và mobile đã tạo ra những thách thức:

  • Cấu trúc MenuTrigger/MenuContent là cứng nhắc.
  • Các submenu trên mobile cần chồng lên nhau.
  • Độ sâu của submenu là động và đệ quy.

Cố Gắng #1: Portal trong Mục Dropdown

Trong nỗ lực đầu tiên, chúng tôi đã thử render các submenu vào một portal nằm bên trong mục menu dropdown. Ý tưởng là tận dụng một portal để xếp chồng các submenu trong khi giữ menu cha mở. Tuy nhiên, cấu trúc DOM nghiêm ngặt và kỳ vọng quản lý tiêu điểm của React Aria đã làm điều này trở nên không khả thi, vì nó đã phá vỡ giả định về nơi nội dung menu nên tồn tại trong cấu trúc.

Cố Gắng #2: Thay Thế Nội Dung Gốc với Trạng Thái Chồng Chất

Giải pháp cuối cùng là quản lý một trạng thái chồng chất submenu trong MenuContent, nơi submenu đang hoạt động sẽ thay thế hoàn toàn nội dung gốc. Trên mobile, khi một submenu được kích hoạt, chúng tôi đã thay thế nút nội dung gốc bằng nút submenu, giả lập một trải nghiệm chồng trang. Điều này cho phép chúng tôi duy trì các tính năng khả năng tiếp cận của React Aria trong khi vẫn kiểm soát hoàn toàn các chuyển tiếp của submenu.

Căn Giữa với Các Rào Cản Điều Hướng Bên

Sản phẩm chính của Calendly có một sidebar bên trái cố định, điều này đã làm lệch khu vực nội dung. Triển khai ban đầu của tôi cho Thanh Hành Động Tập Trung được căn giữa theo viewport, điều này khiến nó trông bị lệch.

Để khắc phục điều này, chúng tôi đã áp dụng một “Mô Hình Outlet”:

  1. Render một component Outlet bọc được căn chỉnh với khu vực nội dung chính.
  2. Truyền một ref cho Outlet này.
  3. Sử dụng một chiến lược portal để render Thanh Hành Động Tập Trung vào outlet này.

Cách tiếp cận này đảm bảo sự căn chỉnh thị giác bất kể sự hiện diện của sidebar.

Xử Lý Các Submenu Đệ Quy

Quản lý các submenu đệ quy yêu cầu xử lý tiêu điểm, độ sâu chồng chất và điều hướng bằng bàn phím mà không có giả định về cấp độ submenu. Tôi đã thiết kế một chiến lược render đệ quy thích ứng dựa trên ngữ cảnh:

  • Trên desktop: flyouts inline.
  • Trên mobile: overlays chồng lên nhau.

Quản lý tiêu điểm bàn phím được thực hiện một cách động, đảm bảo một trải nghiệm điều hướng liền mạch qua các độ sâu khác nhau.

Những Bài Học Rút Ra

  • Các component UI nhỏ có thể tạo ra những cân nhắc kiến trúc đáng kể.
  • Các ràng buộc của hệ thống thiết kế yêu cầu các giải pháp kỹ thuật linh hoạt nhưng mạnh mẽ.
  • Khả năng tiếp cận không thể là một suy nghĩ sau—nó là nền tảng cho kiến trúc của component.

Kết Luận

Việc thiết kế một Thanh Hành Động Tập Trung linh hoạt đòi hỏi sự kết hợp giữa giải quyết vấn đề sáng tạo và sự chú ý kỹ lưỡng đến chi tiết UX và khả năng tiếp cận. Nó cho thấy cách mà thiết kế component chu đáo vượt ra ngoài việc tạo kiểu thị giác, giao thoa giữa kiến trúc, quy trình người dùng và các hạn chế sản phẩm trong thế giới thực.


Tài Liệu Tham Khảo:

  • Mẫu Nâng Cao của React Aria - Slots & Stacking
  • Mô Hình Outlet (React Portals)

Cảm ơn đội ngũ Hệ thống Thiết kế của Calendly vì những buổi thảo luận và tư duy hợp tác đã giúp làm nên component này.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào