Files
sgw-owner-app/Task.md
2025-12-03 16:22:25 +07:00

5.9 KiB
Raw Blame History

Prompt: Tạo bottom sheet kéo/thả trên Map ở app/(tabs)/index.tsx

Mục tiêu

  • Thêm một component dạng bottom sheet nổi trên MapView, có thể nhấn (click) để mở nhanh và kéo (swipe) để thay đổi chiều cao mượt mà.

Ngữ cảnh

  • Dự án Expo React Native (TypeScript) với màn hình map tại: app/(tabs)/index.tsx.
  • Cần chèn một panel nổi (overlay) bám đáy màn hình, hiển thị trên MapView.

Yêu cầu chức năng

  • Chiều cao ban đầu: 10% chiều cao màn hình.
  • Chiều cao tối đa: 50% chiều cao màn hình.
  • Có một nút ở góc trên bên phải của panel để “mở/thu” (move up/down).
  • Khi người dùng click vào panel hoặc click nút: panel mở ngay lập tức lên 50%.
  • Khi người dùng vuốt (kéo) panel lên/xuống: chiều cao thay đổi dần, bị chặn trong khoảng [10%, 50%].
  • Hiệu ứng chuyển đổi chiều cao phải mượt mà, không gây khó chịu (ease-in-out, 180300ms).
  • Panel chỉ nhận touch trong vùng của nó, không chặn thao tác trên map ở các vùng khác.

Yêu cầu UI/UX

  • Panel bám đáy màn hình, bo tròn góc trên (ví dụ: borderTopLeftRadius/borderTopRightRadius = 1620).
  • Có bóng đổ nhẹ để nổi trên map.
  • Có “khu vực kéo” (drag handle) ở header panel (có thể là dải ngang mảnh) + nút toggle ở góc trên bên phải.
  • Nội dung bên trong panel có thể là placeholder đơn giản (ví dụ một vài dòng text) để kiểm chứng hành vi.

Ràng buộc kỹ thuật

  • Ưu tiên dùng react-native-gesture-handler + react-native-reanimated để có tương tác kéo mượt (UI thread). Nếu không khả dụng, fallback sang Animated + PanResponder vẫn được.
  • Không thêm UI library bên thứ ba cho bottom sheet (ví dụ @gorhom/bottom-sheet) — tự triển khai component nhẹ.
  • TypeScript, functional components, tuân thủ style hiện có (không thay đổi kiến trúc không cần thiết).

Hướng triển khai (gợi ý)

  1. Tạo component components/DraggablePanel.tsx với các props:
    • minHeightPct?: number (mặc định 0.1)
    • maxHeightPct?: number (mặc định 0.5)
    • initialState?: 'min' | 'max' (mặc định 'min')
    • onExpandedChange?: (expanded: boolean) => void
  2. Trong DraggablePanel, tính chiều cao theo phần trăm màn hình bằng useWindowDimensions hoặc Dimensions.get('window').height.
  3. Nếu dùng Reanimated: dùng useSharedValue cho height hoặc translateY, withTiming khi click, và withSpring/withTiming khi kéo. Clamp giá trị vào [min, max].
  4. Nếu dùng Animated: dùng Animated.Value + Animated.timingPanResponder để cập nhật height theo cử chỉ, clamp trong [min, max].
  5. Thêm header có drag handle và nút toggle ở góc trên phải:
    • Click header hoặc click nút: toggle giữa min (10%) và max (50%).
    • Vuốt: cập nhật theo delta cử chỉ; khi thả tay nếu vượt ngưỡng giữa thì snap về 50%, ngược lại về 10%.
  6. Đặt panel overlay lên Map ở app/(tabs)/index.tsx bằng position: 'absolute', left: 0, right: 0, bottom: 0, height do animation điều khiển. Đảm bảo pointerEvents phù hợp để không chặn thao tác trên map bên ngoài panel.

Tiêu chí chấp nhận (Acceptance Criteria)

  • Panel xuất hiện trên Map, bám đáy, bo góc trên, có bóng đổ nhẹ.
  • Chiều cao ban đầu là ~10% màn hình; click mở hoặc click nút toggle lập tức chuyển lên ~50% (animation ~200ms, mượt).
  • Vuốt kéo panel thay đổi chiều cao theo ngón tay, bị chặn trong khoảng [10%, 50%]. Thả tay, panel snap về 10% hoặc 50% theo vị trí/độ nhanh.
  • Nút ở góc trên phải hoạt động đúng: chuyển qua lại giữa 10% và 50%.
  • Tương tác map vẫn bình thường ở vùng ngoài panel; trong panel, vuốt không làm map pan.
  • Không crash, không warning/ lỗi TypeScript mới do thay đổi này.

Bàn giao

  • File mới: components/DraggablePanel.tsx.
  • Sửa file: app/(tabs)/index.tsx để chèn panel overlay.
  • Không thay đổi các phần không liên quan.
  • Nếu cần cài đặt react-native-reanimated/react-native-gesture-handler, cung cấp hướng dẫn và chỉnh cấu hình cần thiết cho Expo (nhưng chỉ thực hiện nếu bắt buộc).

Gợi ý kiểm thử thủ công

  • iOS và Android (nếu có):
    • Mở app, xác nhận panel đang ở 10%.
    • Click vào panel hoặc nút: panel mở lên 50% với animation mượt.
    • Kéo lên/xuống nhiều lần: độ cao thay đổi mượt, không vượt quá 50% hoặc thấp hơn 10%.
    • Pan/zoom Map ở vùng ngoài panel: vẫn hoạt động bình thường.
    • Xoay màn hình (nếu hỗ trợ): panel tính lại theo chiều cao mới.

Lưu ý

  • Giữ code gọn, tách logic cử chỉ/animation khỏi phần trình bày nếu hợp lý.

  • Đặt tên biến/props rõ ràng, không dùng one-letter variable.

  • Không thêm license header hoặc thay đổi cấu trúc dự án nếu không cần thiết.

  • Mục tiêu: Tạo một component có thể di chuyển lên xuống

  • Bài toán: Ở giao diện index.tsx, đã có component MapView

    • Giờ mình muốn thêm một component nổi lên trên map, ban đầu sẽ có chiều cao 10% chiều cao màn hình, giống như modal của Expo
    • Trên giao diện cho một nút di chuyển lên/ xuống ở góc trên bên phải
    • Khi người dùng click vào hoặc vuốt component lên thì chiều cao của thẻ cũng thay đổi, max là 50% chiều cao màn hình
    • Khi click thì chiều cao sẽ đổi thành 50% luôn, còn vuốt thì chiều cao sẽ tăng dần
    • Hiệu ứng thay đổi chiều cao mượt mà, không gây khó chịu cho người dùng