refactor: 바깥클릭/ESC 닫기 공용 훅 useClickOutside 도입 (Phase 0)
11곳에 복붙되던 document 리스너 등록/해제·바깥클릭·ESC·스크롤 닫기 로직을 useClickOutside 훅으로 통합. 재사용 드롭다운 5종에 우선 적용. - composables/useClickOutside.ts 신설 (event/esc/closeOnScroll 옵션) - 적용: DropdownSelect, StatusSelect, ScheduleTypeFilter, ScheduleDetailPanel(참석자 스택), ScheduleEventModal(참석자 멀티셀렉트) - 모달 ESC·페이지 레벨 오버레이 조정 로직은 후속 단계(BaseModal)에서 처리 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { onBeforeUnmount, onMounted, type Ref } from 'vue'
|
||||
|
||||
// 바깥 클릭 / ESC / 스크롤 시 닫기 공용 훅.
|
||||
// 드롭다운·팝오버·메뉴 등에서 반복되던 document 리스너 등록/해제 로직을 한 곳으로 모은다.
|
||||
//
|
||||
// - isOpen: 열림 상태 ref (닫혀 있으면 아무 동작 안 함)
|
||||
// - close: 닫기 콜백
|
||||
// - containers: "내부"로 간주할 엘리먼트 ref 목록. 클릭이 이 안이면 무시한다
|
||||
// (Teleport 로 body 에 띄운 메뉴는 트리거와 메뉴 ref 를 모두 넣는다)
|
||||
// - opts.event: 바깥 클릭 감지 이벤트 (기본 'mousedown')
|
||||
// - opts.esc: Escape 로 닫을지 (기본 true)
|
||||
// - opts.closeOnScroll: 스크롤/리사이즈 시 닫을지 — fixed 로 띄운 메뉴 위치 어긋남 방지 (기본 false)
|
||||
export function useClickOutside(
|
||||
isOpen: Ref<boolean>,
|
||||
close: () => void,
|
||||
containers: Ref<HTMLElement | null>[],
|
||||
opts: {
|
||||
event?: 'mousedown' | 'click'
|
||||
esc?: boolean
|
||||
closeOnScroll?: boolean
|
||||
} = {},
|
||||
): void {
|
||||
const { event = 'mousedown', esc = true, closeOnScroll = false } = opts
|
||||
|
||||
function onPointer(e: Event): void {
|
||||
if (!isOpen.value) return
|
||||
const t = e.target as Node
|
||||
if (containers.some((r) => r.value?.contains(t))) return
|
||||
close()
|
||||
}
|
||||
function onKey(e: KeyboardEvent): void {
|
||||
if (isOpen.value && e.key === 'Escape') close()
|
||||
}
|
||||
function onScroll(): void {
|
||||
if (isOpen.value) close()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener(event, onPointer, true)
|
||||
if (esc) document.addEventListener('keydown', onKey)
|
||||
if (closeOnScroll) {
|
||||
window.addEventListener('scroll', onScroll, true)
|
||||
window.addEventListener('resize', onScroll)
|
||||
}
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener(event, onPointer, true)
|
||||
if (esc) document.removeEventListener('keydown', onKey)
|
||||
if (closeOnScroll) {
|
||||
window.removeEventListener('scroll', onScroll, true)
|
||||
window.removeEventListener('resize', onScroll)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user