fix(a11y): #301+#302 모달 접근성 + race condition + 필터 상태 동기화
CRITICAL — 모달 접근성: - frontend/src/lib/hooks/useModalA11y.ts 신규 (useEscapeKey, useFocusTrap, useBodyScrollLock) - BottomSheet: role='dialog' / aria-modal / aria-label / ESC 닫기 / focus trap - FilterSheet: role='dialog' / aria-modal / aria-labelledby / ESC 닫기 / focus trap, 닫기 버튼 aria-label MAJOR — race condition (#301): - RestaurantDetail useEffect에 cancelled 플래그 추가 → restaurant.id 변경 시 이전 fetch 결과 폐기 MAJOR — 필터 상태 동기화 (#302): - page.tsx에 exitSearchMode 헬퍼 추가 - 검색 모드(isSearchResult=true)에서 cuisine/price/country/city/district 변경 시 자동으로 검색 모드 해제 + 원본 restaurants 재로드 후속 분리: #319(BottomSheet 매직넘버/UX), #320(필터 정밀도/접근성/테스트) Refs: #301 #302
This commit is contained in:
@@ -1,23 +1,28 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useEscapeKey, useFocusTrap } from "@/lib/hooks/useModalA11y";
|
||||
|
||||
interface BottomSheetProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
children: React.ReactNode;
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
const SNAP_POINTS = { PEEK: 0.4, HALF: 0.55, FULL: 0.92 };
|
||||
const VELOCITY_THRESHOLD = 0.5;
|
||||
|
||||
export default function BottomSheet({ open, onClose, children }: BottomSheetProps) {
|
||||
export default function BottomSheet({ open, onClose, children, ariaLabel = "상세 정보" }: BottomSheetProps) {
|
||||
const sheetRef = useRef<HTMLDivElement>(null);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const [height, setHeight] = useState(SNAP_POINTS.PEEK);
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const dragState = useRef({ startY: 0, startH: 0, lastY: 0, lastTime: 0 });
|
||||
|
||||
useEscapeKey(open, onClose);
|
||||
useFocusTrap(open, sheetRef);
|
||||
|
||||
// Reset to peek when opened
|
||||
useEffect(() => {
|
||||
if (open) setHeight(SNAP_POINTS.PEEK);
|
||||
@@ -89,6 +94,10 @@ export default function BottomSheet({ open, onClose, children }: BottomSheetProp
|
||||
{/* Sheet */}
|
||||
<div
|
||||
ref={sheetRef}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label={ariaLabel}
|
||||
tabIndex={-1}
|
||||
className="fixed bottom-0 left-0 right-0 z-50 md:hidden flex flex-col bg-surface/85 backdrop-blur-xl rounded-t-2xl shadow-2xl"
|
||||
style={{
|
||||
height: `${height * 100}vh`,
|
||||
|
||||
Reference in New Issue
Block a user