refactor: Teleport 드롭다운 위치/토글 로직 useDropdownMenu 로 통합 (Phase 2)
DropdownSelect·StatusSelect 에 중복되던 fixed 위치 계산·open 토글· 바깥클릭/ESC/스크롤 닫기 로직을 useDropdownMenu 훅으로 추출. - composables/useDropdownMenu.ts 신설 (align/matchWidth/gap/esc 옵션) - DropdownSelect, StatusSelect 가 훅을 사용하도록 정리 - 절대배치 메뉴(일정 멀티셀렉트/유형필터/참석자 스택)는 Phase 0 useClickOutside 로 이미 공유 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { nextTick, ref } from 'vue'
|
||||
import { useClickOutside } from './useClickOutside'
|
||||
|
||||
// Teleport(body) + fixed 위치로 띄우는 드롭다운 메뉴 공용 훅.
|
||||
// 트리거 기준 좌표 계산 · open 토글 · 바깥클릭/ESC/스크롤 닫기를 한 곳으로 모은다.
|
||||
// (DropdownSelect, StatusSelect 등 스크롤/overflow 컨테이너 안에서도 안 잘리는 메뉴에 사용)
|
||||
//
|
||||
// - align: 트리거 좌/우 끝 기준 정렬 (기본 'left')
|
||||
// - matchWidth: 메뉴 최소 너비를 트리거 너비에 맞출지 (기본 false)
|
||||
// - gap: 트리거 하단과 메뉴 사이 간격(px) (기본 6)
|
||||
// - esc: ESC 로 닫을지 (기본 true)
|
||||
export function useDropdownMenu(
|
||||
opts: {
|
||||
align?: 'left' | 'right'
|
||||
matchWidth?: boolean
|
||||
gap?: number
|
||||
esc?: boolean
|
||||
} = {},
|
||||
) {
|
||||
const { align = 'left', matchWidth = false, gap = 6, esc = true } = opts
|
||||
|
||||
const open = ref(false)
|
||||
const triggerRef = ref<HTMLElement | null>(null)
|
||||
const menuRef = ref<HTMLElement | null>(null)
|
||||
// 메뉴 fixed 위치(트리거 기준 계산)
|
||||
const menuStyle = ref<Record<string, string>>({})
|
||||
|
||||
function position(): void {
|
||||
const el = triggerRef.value
|
||||
if (!el) return
|
||||
const r = el.getBoundingClientRect()
|
||||
const style: Record<string, string> = { top: `${r.bottom + gap}px` }
|
||||
if (matchWidth) style.minWidth = `${r.width}px`
|
||||
if (align === 'right') {
|
||||
style.right = `${Math.max(0, window.innerWidth - r.right)}px`
|
||||
} else {
|
||||
style.left = `${r.left}px`
|
||||
}
|
||||
menuStyle.value = style
|
||||
}
|
||||
|
||||
function toggle(): void {
|
||||
open.value = !open.value
|
||||
if (open.value) void nextTick(position)
|
||||
}
|
||||
function close(): void {
|
||||
open.value = false
|
||||
}
|
||||
|
||||
// 바깥클릭(트리거/메뉴 외부)·ESC·스크롤(위치 어긋남) 닫기
|
||||
useClickOutside(open, close, [triggerRef, menuRef], {
|
||||
event: 'click',
|
||||
esc,
|
||||
closeOnScroll: true,
|
||||
})
|
||||
|
||||
return { open, triggerRef, menuRef, menuStyle, toggle, close, position }
|
||||
}
|
||||
Reference in New Issue
Block a user