Security+UX: Add DOMPurify XSS protection, security headers, admin auth gate, import file size limit, due date color coding, swipe/scroll conflict prevention
This commit is contained in:
@@ -46,6 +46,8 @@ export function TaskDetail({
|
||||
|
||||
// Mobile Bottom-Sheet: swipe down to close
|
||||
const touchStartY = useRef<number | null>(null);
|
||||
const touchStartX = useRef<number | null>(null);
|
||||
const gestureDirection = useRef<"vertical" | "horizontal" | null>(null);
|
||||
const [panelTranslateY, setPanelTranslateY] = useState(0);
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
|
||||
@@ -306,15 +308,30 @@ export function TaskDetail({
|
||||
[isDemo, onDemoUpdateTask, onUpdate, subtasks, task]
|
||||
);
|
||||
|
||||
// Touch handlers: 모바일에서 아래로 스와이프 → 패널 닫기
|
||||
// Touch handlers: 모바일에서 아래로 스와이프 → 패널 닫기 (스크롤과 충돌 방지)
|
||||
const handleTouchStart = (e: React.TouchEvent) => {
|
||||
// 스와이프 핸들 또는 detail-header 영역에서만 드래그 닫기 허용
|
||||
const target = e.target as HTMLElement;
|
||||
const isHandle = target.closest(".mobile-swipe-handle") || target.closest(".detail-header");
|
||||
if (!isHandle || !isMobile) return;
|
||||
touchStartY.current = e.touches[0].clientY;
|
||||
touchStartX.current = e.touches[0].clientX;
|
||||
gestureDirection.current = null;
|
||||
};
|
||||
|
||||
const handleTouchMove = (e: React.TouchEvent) => {
|
||||
if (touchStartY.current === null) return;
|
||||
if (touchStartY.current === null || touchStartX.current === null) return;
|
||||
const deltaY = e.touches[0].clientY - touchStartY.current;
|
||||
if (deltaY > 0) {
|
||||
const deltaX = e.touches[0].clientX - touchStartX.current;
|
||||
|
||||
// 초기 제스처 방향 결정 (첫 10px 이동 기준)
|
||||
if (gestureDirection.current === null && (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 10)) {
|
||||
gestureDirection.current = Math.abs(deltaY) > Math.abs(deltaX) ? "vertical" : "horizontal";
|
||||
}
|
||||
|
||||
// 수직 드래그일 때만 패널 이동
|
||||
if (gestureDirection.current === "vertical" && deltaY > 0) {
|
||||
e.preventDefault();
|
||||
setPanelTranslateY(deltaY);
|
||||
}
|
||||
};
|
||||
@@ -325,6 +342,8 @@ export function TaskDetail({
|
||||
}
|
||||
setPanelTranslateY(0);
|
||||
touchStartY.current = null;
|
||||
touchStartX.current = null;
|
||||
gestureDirection.current = null;
|
||||
};
|
||||
|
||||
const completedCount = subtasks.filter((s) => s.completed).length;
|
||||
|
||||
Reference in New Issue
Block a user